<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>Mu - edit.mu</title>
<meta name="Generator" content="Vim/7.4">
<meta name="plugin-version" content="vim7.4_v1">
<meta name="syntax" content="none">
<meta name="settings" content="use_css,pre_wrap,no_foldcolumn,expand_tabs,prevent_copy=">
<meta name="colorscheme" content="minimal">
<style type="text/css">
<!--
pre { white-space: pre-wrap; font-family: monospace; color: #eeeeee; background-color: #080808; }
body { font-family: monospace; color: #eeeeee; background-color: #080808; }
* { font-size: 1.05em; }
.muControl { color: #c0a020; }
.muRecipe { color: #ff8700; }
.SalientComment { color: #00ffff; }
.CommentedCode { color: #6c6c6c; }
.Comment { color: #9090ff; }
.Constant { color: #00a0a0; }
.Special { color: #ff6060; }
.muScenario { color: #00af00; }
.Delimiter { color: #a04060; }
-->
</style>
<script type='text/javascript'>
<!--
-->
</script>
</head>
<body>
<pre id='vimCodeElement'>
<span class="Comment"># Environment for learning programming using mu: <a href="http://akkartik.name/post/mu">http://akkartik.name/post/mu</a></span>
<span class="Comment">#</span>
<span class="Comment"># Consists of one editor on the left for recipes and one on the right for the</span>
<span class="Comment"># sandbox.</span>
<span class="muRecipe">recipe</span> main [
<span class="Constant">local-scope</span>
open-console
initial-recipe:address:array:character<span class="Special"> <- </span>restore <span class="Constant">[recipes.mu]</span>
initial-sandbox:address:array:character<span class="Special"> <- </span>new <span class="Constant">[]</span>
env:address:programming-environment-data<span class="Special"> <- </span>new-programming-environment <span class="Constant">0/screen</span>, initial-recipe, initial-sandbox
env<span class="Special"> <- </span>restore-sandboxes env
render-all <span class="Constant">0/screen</span>, env
show-screen <span class="Constant">0/screen</span>
event-loop <span class="Constant">0/screen</span>, <span class="Constant">0/console</span>, env
<span class="Comment"># never gets here</span>
]
<span class="SalientComment">## the basic editor data structure, and how it displays text to the screen</span>
<span class="muScenario">scenario</span> editor-initially-prints-string-to-screen [
assume-screen <span class="Constant">10/width</span>, <span class="Constant">5/height</span>
run [
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[abc]</span>
new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">10/right</span>
]
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .abc .</span>
<span class="Constant"> . .</span>
]
]
container editor-data [
<span class="Comment"># editable text: doubly linked list of characters (head contains a special sentinel)</span>
data:address:duplex-list
top-of-screen:address:duplex-list
bottom-of-screen:address:duplex-list
<span class="Comment"># location before cursor inside data</span>
before-cursor:address:duplex-list
<span class="Comment"># raw bounds of display area on screen</span>
<span class="Comment"># always displays from row 1 (leaving row 0 for a menu) and at most until bottom of screen</span>
left:number
right:number
<span class="Comment"># raw screen coordinates of cursor</span>
cursor-row:number
cursor-column:number
]
<span class="Comment"># editor:address, screen:address <- new-editor s:address:array:character, screen:address, left:number, right:number</span>
<span class="Comment"># creates a new editor widget and renders its initial appearance to screen.</span>
<span class="Comment"># top/left/right constrain the screen area available to the new editor.</span>
<span class="Comment"># right is exclusive.</span>
<span class="muRecipe">recipe</span> new-editor [
<span class="Constant">local-scope</span>
s:address:array:character<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
screen:address<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
<span class="Comment"># no clipping of bounds</span>
left:number<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
right:number<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
right<span class="Special"> <- </span>subtract right, <span class="Constant">1</span>
result:address:editor-data<span class="Special"> <- </span>new <span class="Constant">editor-data:type</span>
<span class="Comment"># initialize screen-related fields</span>
x:address:number<span class="Special"> <- </span>get-address *result, <span class="Constant">left:offset</span>
*x<span class="Special"> <- </span>copy left
x<span class="Special"> <- </span>get-address *result, <span class="Constant">right:offset</span>
*x<span class="Special"> <- </span>copy right
<span class="Comment"># initialize cursor</span>
x<span class="Special"> <- </span>get-address *result, <span class="Constant">cursor-row:offset</span>
*x<span class="Special"> <- </span>copy <span class="Constant">1/top</span>
x<span class="Special"> <- </span>get-address *result, <span class="Constant">cursor-column:offset</span>
*x<span class="Special"> <- </span>copy left
init:address:address:duplex-list<span class="Special"> <- </span>get-address *result, <span class="Constant">data:offset</span>
*init<span class="Special"> <- </span>push-duplex <span class="Constant">167/§</span>, <span class="Constant">0/tail</span>
top-of-screen:address:address:duplex-list<span class="Special"> <- </span>get-address *result, <span class="Constant">top-of-screen:offset</span>
*top-of-screen<span class="Special"> <- </span>copy *init
y:address:address:duplex-list<span class="Special"> <- </span>get-address *result, <span class="Constant">before-cursor:offset</span>
*y<span class="Special"> <- </span>copy *init
result<span class="Special"> <- </span>insert-text result, s
<span class="Comment"># initialize cursor to top of screen</span>
y<span class="Special"> <- </span>get-address *result, <span class="Constant">before-cursor:offset</span>
*y<span class="Special"> <- </span>copy *init
<span class="Comment"># initial render to screen, just for some old tests</span>
_, screen<span class="Special"> <- </span>render screen, result
<span class="muControl">reply</span> result
]
<span class="muRecipe">recipe</span> insert-text [
<span class="Constant">local-scope</span>
editor:address:editor-data<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
text:address:array:character<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
<span class="Comment"># early exit if text is empty</span>
<span class="muControl">reply-unless</span> text, editor/same-as-ingredient:<span class="Constant">0</span>
len:number<span class="Special"> <- </span>length *text
<span class="muControl">reply-unless</span> len, editor/same-as-ingredient:<span class="Constant">0</span>
idx:number<span class="Special"> <- </span>copy <span class="Constant">0</span>
<span class="Comment"># now we can start appending the rest, character by character</span>
curr:address:duplex-list<span class="Special"> <- </span>get *editor, <span class="Constant">data:offset</span>
<span class="Delimiter">{</span>
done?:boolean<span class="Special"> <- </span>greater-or-equal idx, len
<span class="muControl">break-if</span> done?
c:character<span class="Special"> <- </span>index *text, idx
insert-duplex c, curr
<span class="Comment"># next iter</span>
curr<span class="Special"> <- </span>next-duplex curr
idx<span class="Special"> <- </span>add idx, <span class="Constant">1</span>
<span class="muControl">loop</span>
<span class="Delimiter">}</span>
<span class="muControl">reply</span> editor/same-as-ingredient:<span class="Constant">0</span>
]
<span class="muScenario">scenario</span> editor-initializes-without-data [
assume-screen <span class="Constant">5/width</span>, <span class="Constant">3/height</span>
run [
<span class="Constant">1</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">0/data</span>, screen:address, <span class="Constant">2/left</span>, <span class="Constant">5/right</span>
<span class="Constant">2</span>:editor-data<span class="Special"> <- </span>copy *<span class="Constant">1</span>:address:editor-data
]
memory-should-contain [
<span class="Comment"># 2 (data) <- just the § sentinel</span>
<span class="Comment"># 3 (top of screen) <- the § sentinel</span>
<span class="Constant">4</span><span class="Special"> <- </span><span class="Constant">0</span> <span class="Comment"># bottom-of-screen; null since text fits on screen</span>
<span class="Comment"># 5 (before cursor) <- the § sentinel</span>
<span class="Constant">6</span><span class="Special"> <- </span><span class="Constant">2</span> <span class="Comment"># left</span>
<span class="Constant">7</span><span class="Special"> <- </span><span class="Constant">4</span> <span class="Comment"># right (inclusive)</span>
<span class="Constant">8</span><span class="Special"> <- </span><span class="Constant">1</span> <span class="Comment"># cursor row</span>
<span class="Constant">9</span><span class="Special"> <- </span><span class="Constant">2</span> <span class="Comment"># cursor column</span>
]
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> . .</span>
<span class="Constant"> . .</span>
]
]
<span class="Comment"># bottom:number, screen:address <- render screen:address, editor:address:editor-data</span>
<span class="Comment">#</span>
<span class="Comment"># Assumes cursor should be at coordinates (cursor-row, cursor-column) and</span>
<span class="Comment"># updates before-cursor to match. Might also move coordinates if they're</span>
<span class="Comment"># outside text.</span>
<span class="muRecipe">recipe</span> render [
<span class="Constant">local-scope</span>
screen:address<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
editor:address:editor-data<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
<span class="muControl">reply-unless</span> editor, <span class="Constant">1/top</span>, screen/same-as-ingredient:<span class="Constant">0</span>
left:number<span class="Special"> <- </span>get *editor, <span class="Constant">left:offset</span>
screen-height:number<span class="Special"> <- </span>screen-height screen
right:number<span class="Special"> <- </span>get *editor, <span class="Constant">right:offset</span>
hide-screen screen
<span class="Comment"># traversing editor</span>
curr:address:duplex-list<span class="Special"> <- </span>get *editor, <span class="Constant">top-of-screen:offset</span>
prev:address:duplex-list<span class="Special"> <- </span>copy curr
curr<span class="Special"> <- </span>next-duplex curr
<span class="Comment"># traversing screen</span>
<span class="Constant"> +render-loop-initialization</span>
color:number<span class="Special"> <- </span>copy <span class="Constant">7/white</span>
row:number<span class="Special"> <- </span>copy <span class="Constant">1/top</span>
column:number<span class="Special"> <- </span>copy left
cursor-row:address:number<span class="Special"> <- </span>get-address *editor, <span class="Constant">cursor-row:offset</span>
cursor-column:address:number<span class="Special"> <- </span>get-address *editor, <span class="Constant">cursor-column:offset</span>
before-cursor:address:address:duplex-list<span class="Special"> <- </span>get-address *editor, <span class="Constant">before-cursor:offset</span>
move-cursor screen, row, column
<span class="Delimiter">{</span>
<span class="Constant"> +next-character</span>
<span class="muControl">break-unless</span> curr
off-screen?:boolean<span class="Special"> <- </span>greater-or-equal row, screen-height
<span class="muControl">break-if</span> off-screen?
<span class="Comment"># update editor-data.before-cursor</span>
<span class="Comment"># Doing so at the start of each iteration ensures it stays one step behind</span>
<span class="Comment"># the current character.</span>
<span class="Delimiter">{</span>
at-cursor-row?:boolean<span class="Special"> <- </span>equal row, *cursor-row
<span class="muControl">break-unless</span> at-cursor-row?
at-cursor?:boolean<span class="Special"> <- </span>equal column, *cursor-column
<span class="muControl">break-unless</span> at-cursor?
*before-cursor<span class="Special"> <- </span>prev-duplex curr
<span class="Delimiter">}</span>
c:character<span class="Special"> <- </span>get *curr, <span class="Constant">value:offset</span>
<span class="Constant"> +character-c-recived</span>
<span class="Delimiter">{</span>
<span class="Comment"># newline? move to left rather than 0</span>
newline?:boolean<span class="Special"> <- </span>equal c, <span class="Constant">10/newline</span>
<span class="muControl">break-unless</span> newline?
<span class="Comment"># adjust cursor if necessary</span>
<span class="Delimiter">{</span>
at-cursor-row?:boolean<span class="Special"> <- </span>equal row, *cursor-row
<span class="muControl">break-unless</span> at-cursor-row?
left-of-cursor?:boolean<span class="Special"> <- </span>lesser-than column, *cursor-column
<span class="muControl">break-unless</span> left-of-cursor?
*cursor-column<span class="Special"> <- </span>copy column
*before-cursor<span class="Special"> <- </span>prev-duplex curr
<span class="Delimiter">}</span>
<span class="Comment"># clear rest of line in this window</span>
clear-line-delimited screen, column, right
<span class="Comment"># skip to next line</span>
row<span class="Special"> <- </span>add row, <span class="Constant">1</span>
column<span class="Special"> <- </span>copy left
move-cursor screen, row, column
curr<span class="Special"> <- </span>next-duplex curr
prev<span class="Special"> <- </span>next-duplex prev
<span class="muControl">loop</span> <span class="Constant">+next-character:label</span>
<span class="Delimiter">}</span>
<span class="Delimiter">{</span>
<span class="Comment"># at right? wrap. even if there's only one more letter left; we need</span>
<span class="Comment"># room for clicking on the cursor after it.</span>
at-right?:boolean<span class="Special"> <- </span>equal column, right
<span class="muControl">break-unless</span> at-right?
<span class="Comment"># print wrap icon</span>
print-character screen, <span class="Constant">8617/loop-back-to-left</span>, <span class="Constant">245/grey</span>
column<span class="Special"> <- </span>copy left
row<span class="Special"> <- </span>add row, <span class="Constant">1</span>
move-cursor screen, row, column
<span class="Comment"># don't increment curr</span>
<span class="muControl">loop</span> <span class="Constant">+next-character:label</span>
<span class="Delimiter">}</span>
print-character screen, c, color
curr<span class="Special"> <- </span>next-duplex curr
prev<span class="Special"> <- </span>next-duplex prev
column<span class="Special"> <- </span>add column, <span class="Constant">1</span>
<span class="muControl">loop</span>
<span class="Delimiter">}</span>
<span class="Comment"># save first character off-screen</span>
bottom-of-screen:address:address:duplex-list<span class="Special"> <- </span>get-address *editor, <span class="Constant">bottom-of-screen:offset</span>
*bottom-of-screen<span class="Special"> <- </span>copy curr
<span class="Comment"># is cursor to the right of the last line? move to end</span>
<span class="Delimiter">{</span>
at-cursor-row?:boolean<span class="Special"> <- </span>equal row, *cursor-row
cursor-outside-line?:boolean<span class="Special"> <- </span>lesser-or-equal column, *cursor-column
before-cursor-on-same-line?:boolean<span class="Special"> <- </span>and at-cursor-row?, cursor-outside-line?
above-cursor-row?:boolean<span class="Special"> <- </span>lesser-than row, *cursor-row
before-cursor?:boolean<span class="Special"> <- </span>or before-cursor-on-same-line?, above-cursor-row?
<span class="muControl">break-unless</span> before-cursor?
*cursor-row<span class="Special"> <- </span>copy row
*cursor-column<span class="Special"> <- </span>copy column
<span class="Comment"># line not wrapped but cursor outside bounds? wrap cursor</span>
<span class="Delimiter">{</span>
too-far-right?:boolean<span class="Special"> <- </span>greater-than *cursor-column, right
<span class="muControl">break-unless</span> too-far-right?
*cursor-column<span class="Special"> <- </span>copy left
*cursor-row<span class="Special"> <- </span>add *cursor-row, <span class="Constant">1</span>
above-screen-bottom?:boolean<span class="Special"> <- </span>lesser-than *cursor-row, screen-height
assert above-screen-bottom?, <span class="Constant">[unimplemented: wrapping cursor past bottom of screen]</span>
<span class="Delimiter">}</span>
*before-cursor<span class="Special"> <- </span>copy prev
<span class="Delimiter">}</span>
<span class="Comment"># clear rest of screen</span>
clear-line-delimited screen, column, right
clear-rest-of-screen screen, row, left, right
<span class="muControl">reply</span> row, screen/same-as-ingredient:<span class="Constant">0</span>
]
<span class="Comment"># row:number, screen:address <- render-string screen:address, s:address:array:character, left:number, right:number, color:number, row:number</span>
<span class="Comment"># move cursor at start of next line</span>
<span class="Comment"># print a string 's' to 'editor' in 'color' starting at 'row'</span>
<span class="Comment"># clear rest of last line, but don't move cursor to next line</span>
<span class="muRecipe">recipe</span> render-string [
<span class="Constant">local-scope</span>
screen:address<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
s:address:array:character<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
left:number<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
right:number<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
color:number<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
row:number<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
row<span class="Special"> <- </span>add row, <span class="Constant">1</span>
<span class="muControl">reply-unless</span> s, row/same-as-ingredient:<span class="Constant">5</span>, screen/same-as-ingredient:<span class="Constant">0</span>
column:number<span class="Special"> <- </span>copy left
move-cursor screen, row, column
screen-height:number<span class="Special"> <- </span>screen-height screen
i:number<span class="Special"> <- </span>copy <span class="Constant">0</span>
len:number<span class="Special"> <- </span>length *s
<span class="Delimiter">{</span>
<span class="Constant"> +next-character</span>
done?:boolean<span class="Special"> <- </span>greater-or-equal i, len
<span class="muControl">break-if</span> done?
done?<span class="Special"> <- </span>greater-or-equal row, screen-height
<span class="muControl">break-if</span> done?
c:character<span class="Special"> <- </span>index *s, i
<span class="Delimiter">{</span>
<span class="Comment"># at right? wrap.</span>
at-right?:boolean<span class="Special"> <- </span>equal column, right
<span class="muControl">break-unless</span> at-right?
<span class="Comment"># print wrap icon</span>
print-character screen, <span class="Constant">8617/loop-back-to-left</span>, <span class="Constant">245/grey</span>
column<span class="Special"> <- </span>copy left
row<span class="Special"> <- </span>add row, <span class="Constant">1</span>
move-cursor screen, row, column
<span class="muControl">loop</span> <span class="Constant">+next-character:label</span> <span class="Comment"># retry i</span>
<span class="Delimiter">}</span>
i<span class="Special"> <- </span>add i, <span class="Constant">1</span>
<span class="Delimiter">{</span>
<span class="Comment"># newline? move to left rather than 0</span>
newline?:boolean<span class="Special"> <- </span>equal c, <span class="Constant">10/newline</span>
<span class="muControl">break-unless</span> newline?
<span class="Comment"># clear rest of line in this window</span>
<span class="Delimiter">{</span>
done?:boolean<span class="Special"> <- </span>greater-than column, right
<span class="muControl">break-if</span> done?
print-character screen, <span class="Constant">32/space</span>
column<span class="Special"> <- </span>add column, <span class="Constant">1</span>
<span class="muControl">loop</span>
<span class="Delimiter">}</span>
row<span class="Special"> <- </span>add row, <span class="Constant">1</span>
column<span class="Special"> <- </span>copy left
move-cursor screen, row, column
<span class="muControl">loop</span> <span class="Constant">+next-character:label</span>
<span class="Delimiter">}</span>
print-character screen, c, color
column<span class="Special"> <- </span>add column, <span class="Constant">1</span>
<span class="muControl">loop</span>
<span class="Delimiter">}</span>
<span class="Delimiter">{</span>
<span class="Comment"># clear rest of current line</span>
line-done?:boolean<span class="Special"> <- </span>greater-than column, right
<span class="muControl">break-if</span> line-done?
print-character screen, <span class="Constant">32/space</span>
column<span class="Special"> <- </span>add column, <span class="Constant">1</span>
<span class="muControl">loop</span>
<span class="Delimiter">}</span>
<span class="muControl">reply</span> row/same-as-ingredient:<span class="Constant">5</span>, screen/same-as-ingredient:<span class="Constant">0</span>
]
<span class="muRecipe">recipe</span> clear-line-delimited [
<span class="Constant">local-scope</span>
screen:address<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
left:number<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
right:number<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
column:number<span class="Special"> <- </span>copy left
<span class="Delimiter">{</span>
done?:boolean<span class="Special"> <- </span>greater-than column, right
<span class="muControl">break-if</span> done?
print-character screen, <span class="Constant">32/space</span>
column<span class="Special"> <- </span>add column, <span class="Constant">1</span>
<span class="muControl">loop</span>
<span class="Delimiter">}</span>
]
<span class="muScenario">scenario</span> editor-initially-prints-multiple-lines [
assume-screen <span class="Constant">5/width</span>, <span class="Constant">5/height</span>
run [
s:address:array:character<span class="Special"> <- </span>new <span class="Constant">[abc</span>
<span class="Constant">def]</span>
new-editor s:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">5/right</span>
]
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .abc .</span>
<span class="Constant"> .def .</span>
<span class="Constant"> . .</span>
]
]
<span class="muScenario">scenario</span> editor-initially-handles-offsets [
assume-screen <span class="Constant">5/width</span>, <span class="Constant">5/height</span>
run [
s:address:array:character<span class="Special"> <- </span>new <span class="Constant">[abc]</span>
new-editor s:address:array:character, screen:address, <span class="Constant">1/left</span>, <span class="Constant">5/right</span>
]
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> . abc .</span>
<span class="Constant"> . .</span>
]
]
<span class="muScenario">scenario</span> editor-initially-prints-multiple-lines-at-offset [
assume-screen <span class="Constant">5/width</span>, <span class="Constant">5/height</span>
run [
s:address:array:character<span class="Special"> <- </span>new <span class="Constant">[abc</span>
<span class="Constant">def]</span>
new-editor s:address:array:character, screen:address, <span class="Constant">1/left</span>, <span class="Constant">5/right</span>
]
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> . abc .</span>
<span class="Constant"> . def .</span>
<span class="Constant"> . .</span>
]
]
<span class="muScenario">scenario</span> editor-initially-wraps-long-lines [
assume-screen <span class="Constant">5/width</span>, <span class="Constant">5/height</span>
run [
s:address:array:character<span class="Special"> <- </span>new <span class="Constant">[abc def]</span>
new-editor s:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">5/right</span>
]
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .abc ↩.</span>
<span class="Constant"> .def .</span>
<span class="Constant"> . .</span>
]
screen-should-contain-in-color <span class="Constant">245/grey</span> [
<span class="Constant"> . .</span>
<span class="Constant"> . ↩.</span>
<span class="Constant"> . .</span>
<span class="Constant"> . .</span>
]
]
<span class="muScenario">scenario</span> editor-initially-wraps-barely-long-lines [
assume-screen <span class="Constant">5/width</span>, <span class="Constant">5/height</span>
run [
s:address:array:character<span class="Special"> <- </span>new <span class="Constant">[abcde]</span>
new-editor s:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">5/right</span>
]
<span class="Comment"># still wrap, even though the line would fit. We need room to click on the</span>
<span class="Comment"># end of the line</span>
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .abcd↩.</span>
<span class="Constant"> .e .</span>
<span class="Constant"> . .</span>
]
screen-should-contain-in-color <span class="Constant">245/grey</span> [
<span class="Constant"> . .</span>
<span class="Constant"> . ↩.</span>
<span class="Constant"> . .</span>
<span class="Constant"> . .</span>
]
]
<span class="muScenario">scenario</span> editor-initializes-empty-text [
assume-screen <span class="Constant">5/width</span>, <span class="Constant">5/height</span>
run [
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[]</span>
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">5/right</span>
<span class="Constant">3</span>:number<span class="Special"> <- </span>get *<span class="Constant">2</span>:address:editor-data, <span class="Constant">cursor-row:offset</span>
<span class="Constant">4</span>:number<span class="Special"> <- </span>get *<span class="Constant">2</span>:address:editor-data, <span class="Constant">cursor-column:offset</span>
]
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> . .</span>
<span class="Constant"> . .</span>
]
memory-should-contain [
<span class="Constant">3</span><span class="Special"> <- </span><span class="Constant">1</span> <span class="Comment"># cursor row</span>
<span class="Constant">4</span><span class="Special"> <- </span><span class="Constant">0</span> <span class="Comment"># cursor column</span>
]
]
<span class="Comment"># just a little color for mu code</span>
<span class="muScenario">scenario</span> render-colors-comments [
assume-screen <span class="Constant">5/width</span>, <span class="Constant">5/height</span>
run [
s:address:array:character<span class="Special"> <- </span>new <span class="Constant">[abc</span>
<span class="Constant"># de</span>
<span class="Constant">f]</span>
new-editor s:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">5/right</span>
]
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .abc .</span>
<span class="Constant"> .# de .</span>
<span class="Constant"> .f .</span>
<span class="Constant"> . .</span>
]
screen-should-contain-in-color <span class="Constant">12/lightblue</span>, [
<span class="Constant"> . .</span>
<span class="Constant"> . .</span>
<span class="Constant"> .# de .</span>
<span class="Constant"> . .</span>
<span class="Constant"> . .</span>
]
screen-should-contain-in-color <span class="Constant">7/white</span>, [
<span class="Constant"> . .</span>
<span class="Constant"> .abc .</span>
<span class="Constant"> . .</span>
<span class="Constant"> .f .</span>
<span class="Constant"> . .</span>
]
]
<span class="muRecipe">after</span> +character-c-recived [
color<span class="Special"> <- </span>get-color color, c
]
<span class="Comment"># color:number <- get-color color:number, c:character</span>
<span class="Comment"># so far the previous color is all the information we need; that may change</span>
<span class="muRecipe">recipe</span> get-color [
<span class="Constant">local-scope</span>
color:number<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
c:character<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
color-is-white?:boolean<span class="Special"> <- </span>equal color, <span class="Constant">7/white</span>
<span class="CommentedCode">#? $print [character: ], c, 10/newline #? 1</span>
<span class="Comment"># if color is white and next character is '#', switch color to blue</span>
<span class="Delimiter">{</span>
<span class="muControl">break-unless</span> color-is-white?
starting-comment?:boolean<span class="Special"> <- </span>equal c, <span class="Constant">35/#</span>
<span class="muControl">break-unless</span> starting-comment?
<span class="CommentedCode">#? $print [switch color back to blue], 10/newline #? 1</span>
color<span class="Special"> <- </span>copy <span class="Constant">12/lightblue</span>
<span class="muControl">jump</span> <span class="Constant">+exit:label</span>
<span class="Delimiter">}</span>
<span class="Comment"># if color is blue and next character is newline, switch color to white</span>
<span class="Delimiter">{</span>
color-is-blue?:boolean<span class="Special"> <- </span>equal color, <span class="Constant">12/lightblue</span>
<span class="muControl">break-unless</span> color-is-blue?
ending-comment?:boolean<span class="Special"> <- </span>equal c, <span class="Constant">10/newline</span>
<span class="muControl">break-unless</span> ending-comment?
<span class="CommentedCode">#? $print [switch color back to white], 10/newline #? 1</span>
color<span class="Special"> <- </span>copy <span class="Constant">7/white</span>
<span class="muControl">jump</span> <span class="Constant">+exit:label</span>
<span class="Delimiter">}</span>
<span class="Comment"># if color is white (no comments) and next character is '<', switch color to red</span>
<span class="Delimiter">{</span>
<span class="muControl">break-unless</span> color-is-white?
starting-assignment?:boolean<span class="Special"> <- </span>equal c, <span class="Constant">60/<</span>
<span class="muControl">break-unless</span> starting-assignment?
color<span class="Special"> <- </span>copy <span class="Constant">1/red</span>
<span class="muControl">jump</span> <span class="Constant">+exit:label</span>
<span class="Delimiter">}</span>
<span class="Comment"># if color is red and next character is space, switch color to white</span>
<span class="Delimiter">{</span>
color-is-red?:boolean<span class="Special"> <- </span>equal color, <span class="Constant">1/red</span>
<span class="muControl">break-unless</span> color-is-red?
ending-assignment?:boolean<span class="Special"> <- </span>equal c, <span class="Constant">32/space</span>
<span class="muControl">break-unless</span> ending-assignment?
color<span class="Special"> <- </span>copy <span class="Constant">7/white</span>
<span class="muControl">jump</span> <span class="Constant">+exit:label</span>
<span class="Delimiter">}</span>
<span class="Comment"># otherwise no change</span>
<span class="Constant"> +exit</span>
<span class="muControl">reply</span> color
]
<span class="muScenario">scenario</span> render-colors-assignment [
assume-screen <span class="Constant">8/width</span>, <span class="Constant">5/height</span>
run [
s:address:array:character<span class="Special"> <- </span>new <span class="Constant">[abc</span>
<span class="Constant">d <- e</span>
<span class="Constant">f]</span>
new-editor s:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">8/right</span>
]
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .abc .</span>
<span class="Constant"> .d <- e .</span>
<span class="Constant"> .f .</span>
<span class="Constant"> . .</span>
]
screen-should-contain-in-color <span class="Constant">1/red</span>, [
<span class="Constant"> . .</span>
<span class="Constant"> . .</span>
<span class="Constant"> . <- .</span>
<span class="Constant"> . .</span>
<span class="Constant"> . .</span>
]
]
<span class="SalientComment">## handling events from the keyboard, mouse, touch screen, ...</span>
<span class="muRecipe">recipe</span> editor-event-loop [
<span class="Constant">local-scope</span>
screen:address<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
console:address<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
editor:address:editor-data<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
<span class="Delimiter">{</span>
<span class="Comment"># looping over each (keyboard or touch) event as it occurs</span>
<span class="Constant"> +next-event</span>
e:event, console:address, found?:boolean, quit?:boolean<span class="Special"> <- </span>read-event console
<span class="muControl">loop-unless</span> found?
<span class="muControl">break-if</span> quit? <span class="Comment"># only in tests</span>
trace <span class="Constant">[app]</span>, <span class="Constant">[next-event]</span>
<span class="Comment"># 'touch' event - send to both editors</span>
t:address:touch-event<span class="Special"> <- </span>maybe-convert e, <span class="Constant">touch:variant</span>
<span class="Delimiter">{</span>
<span class="muControl">break-unless</span> t
move-cursor-in-editor screen, editor, *t
<span class="Delimiter">}</span>
<span class="Comment"># keyboard events</span>
<span class="Delimiter">{</span>
<span class="muControl">break-if</span> t
handle-keyboard-event screen, console, editor, e
<span class="Delimiter">}</span>
<span class="Comment"># send any changes to screen</span>
row:number, screen<span class="Special"> <- </span>render screen, editor
<span class="Comment"># clear final line, in case we just processed a backspace</span>
left:number<span class="Special"> <- </span>get *editor, <span class="Constant">left:offset</span>
right:number<span class="Special"> <- </span>get *editor, <span class="Constant">right:offset</span>
row<span class="Special"> <- </span>add row, <span class="Constant">1</span>
move-cursor screen, row, left
clear-line-delimited screen, left, right
<span class="muControl">loop</span>
<span class="Delimiter">}</span>
]
<span class="muRecipe">recipe</span> handle-keyboard-event [
<span class="Constant">local-scope</span>
screen:address<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
console:address<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
editor:address:editor-data<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
e:event<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
<span class="muControl">reply-unless</span> editor
<span class="Comment"># character</span>
<span class="Delimiter">{</span>
c:address:character<span class="Special"> <- </span>maybe-convert e, <span class="Constant">text:variant</span>
<span class="muControl">break-unless</span> c
<span class="CommentedCode">#? trace [app], [handle-keyboard-event: special character] #? 1</span>
<span class="Comment"># exceptions for special characters go here</span>
<span class="Constant"> +handle-special-character</span>
<span class="Comment"># otherwise type it in</span>
insert-at-cursor editor, *c, screen
<span class="muControl">reply</span>
<span class="Delimiter">}</span>
<span class="Comment"># special key to modify the text or move the cursor</span>
k:address:number<span class="Special"> <- </span>maybe-convert e:event, <span class="Constant">keycode:variant</span>
assert k, <span class="Constant">[event was of unknown type; neither keyboard nor mouse]</span>
before-cursor:address:address:duplex-list<span class="Special"> <- </span>get-address *editor, <span class="Constant">before-cursor:offset</span>
cursor-row:address:number<span class="Special"> <- </span>get-address *editor, <span class="Constant">cursor-row:offset</span>
cursor-column:address:number<span class="Special"> <- </span>get-address *editor, <span class="Constant">cursor-column:offset</span>
screen-height:number<span class="Special"> <- </span>screen-height screen
left:number<span class="Special"> <- </span>get *editor, <span class="Constant">left:offset</span>
right:number<span class="Special"> <- </span>get *editor, <span class="Constant">right:offset</span>
<span class="Comment"># handlers for each special key will go here</span>
<span class="Constant"> +handle-special-key</span>
]
<span class="Comment"># process click, return if it was on current editor</span>
<span class="Comment"># todo: ignores menu bar (for now just displays shortcuts)</span>
<span class="muRecipe">recipe</span> move-cursor-in-editor [
<span class="Constant">local-scope</span>
screen:address<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
editor:address:editor-data<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
t:touch-event<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
<span class="muControl">reply-unless</span> editor, <span class="Constant">0/false</span>
click-column:number<span class="Special"> <- </span>get t, <span class="Constant">column:offset</span>
left:number<span class="Special"> <- </span>get *editor, <span class="Constant">left:offset</span>
too-far-left?:boolean<span class="Special"> <- </span>lesser-than click-column, left
<span class="muControl">reply-if</span> too-far-left?, <span class="Constant">0/false</span>
right:number<span class="Special"> <- </span>get *editor, <span class="Constant">right:offset</span>
too-far-right?:boolean<span class="Special"> <- </span>greater-than click-column, right
<span class="muControl">reply-if</span> too-far-right?, <span class="Constant">0/false</span>
<span class="Comment"># update cursor</span>
cursor-row:address:number<span class="Special"> <- </span>get-address *editor, <span class="Constant">cursor-row:offset</span>
*cursor-row<span class="Special"> <- </span>get t, <span class="Constant">row:offset</span>
cursor-column:address:number<span class="Special"> <- </span>get-address *editor, <span class="Constant">cursor-column:offset</span>
*cursor-column<span class="Special"> <- </span>get t, <span class="Constant">column:offset</span>
<span class="Comment"># gain focus</span>
<span class="muControl">reply</span> <span class="Constant">1/true</span>
]
<span class="muRecipe">recipe</span> insert-at-cursor [
<span class="Constant">local-scope</span>
editor:address:editor-data<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
c:character<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
screen:address<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
before-cursor:address:address:duplex-list<span class="Special"> <- </span>get-address *editor, <span class="Constant">before-cursor:offset</span>
insert-duplex c, *before-cursor
*before-cursor<span class="Special"> <- </span>next-duplex *before-cursor
cursor-row:address:number<span class="Special"> <- </span>get-address *editor, <span class="Constant">cursor-row:offset</span>
cursor-column:address:number<span class="Special"> <- </span>get-address *editor, <span class="Constant">cursor-column:offset</span>
left:number<span class="Special"> <- </span>get *editor, <span class="Constant">left:offset</span>
right:number<span class="Special"> <- </span>get *editor, <span class="Constant">right:offset</span>
<span class="Comment"># occasionally we'll need to mess with the cursor</span>
<span class="Constant"> +insert-character-special-case</span>
<span class="Comment"># but mostly we'll just move the cursor right</span>
*cursor-column<span class="Special"> <- </span>add *cursor-column, <span class="Constant">1</span>
]
<span class="muScenario">scenario</span> editor-handles-empty-event-queue [
assume-screen <span class="Constant">10/width</span>, <span class="Constant">5/height</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[abc]</span>
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">10/right</span>
assume-console <span class="Constant">[]</span>
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
]
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .abc .</span>
<span class="Constant"> . .</span>
]
]
<span class="muScenario">scenario</span> editor-handles-mouse-clicks [
assume-screen <span class="Constant">10/width</span>, <span class="Constant">5/height</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[abc]</span>
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">10/right</span>
assume-console [
left-click <span class="Constant">1</span>, <span class="Constant">1</span> <span class="Comment"># on the 'b'</span>
]
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
<span class="Constant">3</span>:number<span class="Special"> <- </span>get *<span class="Constant">2</span>:address:editor-data, <span class="Constant">cursor-row:offset</span>
<span class="Constant">4</span>:number<span class="Special"> <- </span>get *<span class="Constant">2</span>:address:editor-data, <span class="Constant">cursor-column:offset</span>
]
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .abc .</span>
<span class="Constant"> . .</span>
]
memory-should-contain [
<span class="Constant">3</span><span class="Special"> <- </span><span class="Constant">1</span> <span class="Comment"># cursor is at row 0..</span>
<span class="Constant">4</span><span class="Special"> <- </span><span class="Constant">1</span> <span class="Comment"># ..and column 1</span>
]
]
<span class="muScenario">scenario</span> editor-handles-mouse-clicks-outside-text [
assume-screen <span class="Constant">10/width</span>, <span class="Constant">5/height</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[abc]</span>
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">10/right</span>
assume-console [
left-click <span class="Constant">1</span>, <span class="Constant">7</span> <span class="Comment"># last line, to the right of text</span>
]
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
<span class="Constant">3</span>:number<span class="Special"> <- </span>get *<span class="Constant">2</span>:address:editor-data, <span class="Constant">cursor-row:offset</span>
<span class="Constant">4</span>:number<span class="Special"> <- </span>get *<span class="Constant">2</span>:address:editor-data, <span class="Constant">cursor-column:offset</span>
]
memory-should-contain [
<span class="Constant">3</span><span class="Special"> <- </span><span class="Constant">1</span> <span class="Comment"># cursor row</span>
<span class="Constant">4</span><span class="Special"> <- </span><span class="Constant">3</span> <span class="Comment"># cursor column</span>
]
]
<span class="muScenario">scenario</span> editor-handles-mouse-clicks-outside-text-2 [
assume-screen <span class="Constant">10/width</span>, <span class="Constant">5/height</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[abc</span>
<span class="Constant">def]</span>
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">10/right</span>
assume-console [
left-click <span class="Constant">1</span>, <span class="Constant">7</span> <span class="Comment"># interior line, to the right of text</span>
]
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
<span class="Constant">3</span>:number<span class="Special"> <- </span>get *<span class="Constant">2</span>:address:editor-data, <span class="Constant">cursor-row:offset</span>
<span class="Constant">4</span>:number<span class="Special"> <- </span>get *<span class="Constant">2</span>:address:editor-data, <span class="Constant">cursor-column:offset</span>
]
memory-should-contain [
<span class="Constant">3</span><span class="Special"> <- </span><span class="Constant">1</span> <span class="Comment"># cursor row</span>
<span class="Constant">4</span><span class="Special"> <- </span><span class="Constant">3</span> <span class="Comment"># cursor column</span>
]
]
<span class="muScenario">scenario</span> editor-handles-mouse-clicks-outside-text-3 [
assume-screen <span class="Constant">10/width</span>, <span class="Constant">5/height</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[abc</span>
<span class="Constant">def]</span>
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">10/right</span>
assume-console [
left-click <span class="Constant">3</span>, <span class="Constant">7</span> <span class="Comment"># below text</span>
]
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
<span class="Constant">3</span>:number<span class="Special"> <- </span>get *<span class="Constant">2</span>:address:editor-data, <span class="Constant">cursor-row:offset</span>
<span class="Constant">4</span>:number<span class="Special"> <- </span>get *<span class="Constant">2</span>:address:editor-data, <span class="Constant">cursor-column:offset</span>
]
memory-should-contain [
<span class="Constant">3</span><span class="Special"> <- </span><span class="Constant">2</span> <span class="Comment"># cursor row</span>
<span class="Constant">4</span><span class="Special"> <- </span><span class="Constant">3</span> <span class="Comment"># cursor column</span>
]
]
<span class="muScenario">scenario</span> editor-handles-mouse-clicks-outside-column [
assume-screen <span class="Constant">10/width</span>, <span class="Constant">5/height</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[abc]</span>
<span class="Comment"># editor occupies only left half of screen</span>
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">5/right</span>
assume-console [
<span class="Comment"># click on right half of screen</span>
left-click <span class="Constant">3</span>, <span class="Constant">8</span>
]
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
<span class="Constant">3</span>:number<span class="Special"> <- </span>get *<span class="Constant">2</span>:address:editor-data, <span class="Constant">cursor-row:offset</span>
<span class="Constant">4</span>:number<span class="Special"> <- </span>get *<span class="Constant">2</span>:address:editor-data, <span class="Constant">cursor-column:offset</span>
]
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .abc .</span>
<span class="Constant"> . .</span>
]
memory-should-contain [
<span class="Constant">3</span><span class="Special"> <- </span><span class="Constant">1</span> <span class="Comment"># no change to cursor row</span>
<span class="Constant">4</span><span class="Special"> <- </span><span class="Constant">0</span> <span class="Comment"># ..or column</span>
]
]
<span class="muScenario">scenario</span> editor-inserts-characters-into-empty-editor [
assume-screen <span class="Constant">10/width</span>, <span class="Constant">5/height</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[]</span>
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">5/right</span>
assume-console [
type <span class="Constant">[abc]</span>
]
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
]
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .abc .</span>
<span class="Constant"> . .</span>
]
]
<span class="muScenario">scenario</span> editor-inserts-characters-at-cursor [
assume-screen <span class="Constant">10/width</span>, <span class="Constant">5/height</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[abc]</span>
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">10/right</span>
assume-console [
type <span class="Constant">[0]</span>
left-click <span class="Constant">1</span>, <span class="Constant">2</span>
type <span class="Constant">[d]</span>
]
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
]
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .0adbc .</span>
<span class="Constant"> . .</span>
]
]
<span class="muScenario">scenario</span> editor-inserts-characters-at-cursor-2 [
assume-screen <span class="Constant">10/width</span>, <span class="Constant">5/height</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[abc]</span>
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">10/right</span>
assume-console [
left-click <span class="Constant">1</span>, <span class="Constant">5</span> <span class="Comment"># right of last line</span>
type <span class="Constant">[d]</span> <span class="Comment"># should append</span>
]
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
]
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .abcd .</span>
<span class="Constant"> . .</span>
]
]
<span class="muScenario">scenario</span> editor-inserts-characters-at-cursor-3 [
assume-screen <span class="Constant">10/width</span>, <span class="Constant">5/height</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[abc]</span>
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">10/right</span>
assume-console [
left-click <span class="Constant">3</span>, <span class="Constant">5</span> <span class="Comment"># below all text</span>
type <span class="Constant">[d]</span> <span class="Comment"># should append</span>
]
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
]
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .abcd .</span>
<span class="Constant"> . .</span>
]
]
<span class="muScenario">scenario</span> editor-inserts-characters-at-cursor-4 [
assume-screen <span class="Constant">10/width</span>, <span class="Constant">5/height</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[abc</span>
<span class="Constant">d]</span>
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">10/right</span>
assume-console [
left-click <span class="Constant">3</span>, <span class="Constant">5</span> <span class="Comment"># below all text</span>
type <span class="Constant">[e]</span> <span class="Comment"># should append</span>
]
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
]
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .abc .</span>
<span class="Constant"> .de .</span>
<span class="Constant"> . .</span>
]
]
<span class="muScenario">scenario</span> editor-inserts-characters-at-cursor-5 [
assume-screen <span class="Constant">10/width</span>, <span class="Constant">5/height</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[abc</span>
<span class="Constant">d]</span>
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">10/right</span>
assume-console [
left-click <span class="Constant">3</span>, <span class="Constant">5</span> <span class="Comment"># below all text</span>
type <span class="Constant">[ef]</span> <span class="Comment"># should append multiple characters in order</span>
]
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
]
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .abc .</span>
<span class="Constant"> .def .</span>
<span class="Constant"> . .</span>
]
]
<span class="muScenario">scenario</span> editor-moves-cursor-after-inserting-characters [
assume-screen <span class="Constant">10/width</span>, <span class="Constant">5/height</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[ab]</span>
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">5/right</span>
assume-console [
type <span class="Constant">[01]</span>
]
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
]
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .01ab .</span>
<span class="Constant"> . .</span>
]
]
<span class="Comment"># if the cursor reaches the right margin, wrap the line</span>
<span class="muScenario">scenario</span> editor-wraps-line-on-insert [
assume-screen <span class="Constant">5/width</span>, <span class="Constant">5/height</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[abc]</span>
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">5/right</span>
<span class="Comment"># type a letter</span>
assume-console [
type <span class="Constant">[e]</span>
]
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
]
<span class="Comment"># no wrap yet</span>
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .eabc .</span>
<span class="Constant"> . .</span>
<span class="Constant"> . .</span>
]
<span class="Comment"># type a second letter</span>
assume-console [
type <span class="Constant">[f]</span>
]
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
]
<span class="Comment"># now wrap</span>
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .efab↩.</span>
<span class="Constant"> .c .</span>
<span class="Constant"> . .</span>
]
]
<span class="muRecipe">after</span> +insert-character-special-case [
<span class="Comment"># if the line wraps at the cursor, move cursor to start of next row</span>
<span class="Delimiter">{</span>
<span class="Comment"># if we're at the column just before the wrap indicator</span>
wrap-column:number<span class="Special"> <- </span>subtract right, <span class="Constant">1</span>
at-wrap?:boolean<span class="Special"> <- </span>greater-or-equal *cursor-column, wrap-column
<span class="muControl">break-unless</span> at-wrap?
*cursor-column<span class="Special"> <- </span>subtract *cursor-column, wrap-column
*cursor-row<span class="Special"> <- </span>add *cursor-row, <span class="Constant">1</span>
<span class="Comment"># if we're out of the screen, scroll down</span>
<span class="Delimiter">{</span>
screen-height:number<span class="Special"> <- </span>screen-height screen
below-screen?:boolean<span class="Special"> <- </span>greater-or-equal *cursor-row, screen-height
<span class="muControl">break-unless</span> below-screen?
<span class="Constant"> +scroll-down</span>
<span class="Delimiter">}</span>
<span class="muControl">reply</span>
<span class="Delimiter">}</span>
]
<span class="muScenario">scenario</span> editor-wraps-cursor-after-inserting-characters [
assume-screen <span class="Constant">10/width</span>, <span class="Constant">5/height</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[abcde]</span>
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">5/right</span>
assume-console [
left-click <span class="Constant">1</span>, <span class="Constant">4</span> <span class="Comment"># line is full; no wrap icon yet</span>
type <span class="Constant">[f]</span>
]
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
<span class="Constant">3</span>:number<span class="Special"> <- </span>get *<span class="Constant">2</span>:address:editor-data, <span class="Constant">cursor-row:offset</span>
<span class="Constant">4</span>:number<span class="Special"> <- </span>get *<span class="Constant">2</span>:address:editor-data, <span class="Constant">cursor-column:offset</span>
]
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .abcd↩ .</span>
<span class="Constant"> .fe .</span>
<span class="Constant"> . .</span>
]
memory-should-contain [
<span class="Constant">3</span><span class="Special"> <- </span><span class="Constant">2</span> <span class="Comment"># cursor row</span>
<span class="Constant">4</span><span class="Special"> <- </span><span class="Constant">1</span> <span class="Comment"># cursor column</span>
]
]
<span class="muScenario">scenario</span> editor-wraps-cursor-after-inserting-characters-2 [
assume-screen <span class="Constant">10/width</span>, <span class="Constant">5/height</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[abcde]</span>
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">5/right</span>
assume-console [
left-click <span class="Constant">1</span>, <span class="Constant">3</span> <span class="Comment"># right before the wrap icon</span>
type <span class="Constant">[f]</span>
]
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
<span class="Constant">3</span>:number<span class="Special"> <- </span>get *<span class="Constant">2</span>:address:editor-data, <span class="Constant">cursor-row:offset</span>
<span class="Constant">4</span>:number<span class="Special"> <- </span>get *<span class="Constant">2</span>:address:editor-data, <span class="Constant">cursor-column:offset</span>
]
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .abcf↩ .</span>
<span class="Constant"> .de .</span>
<span class="Constant"> . .</span>
]
memory-should-contain [
<span class="Constant">3</span><span class="Special"> <- </span><span class="Constant">2</span> <span class="Comment"># cursor row</span>
<span class="Constant">4</span><span class="Special"> <- </span><span class="Constant">0</span> <span class="Comment"># cursor column</span>
]
]
<span class="Comment"># if newline, move cursor to start of next line</span>
<span class="muScenario">scenario</span> editor-moves-cursor-down-after-inserting-newline [
assume-screen <span class="Constant">10/width</span>, <span class="Constant">5/height</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[abc]</span>
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">10/right</span>
assume-console [
type <span class="Constant">[0</span>
<span class="Constant">1]</span>
]
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
]
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .0 .</span>
<span class="Constant"> .1abc .</span>
<span class="Constant"> . .</span>
]
]
<span class="muRecipe">after</span> +insert-character-special-case [
<span class="Delimiter">{</span>
newline?:boolean<span class="Special"> <- </span>equal c, <span class="Constant">10/newline</span>
<span class="muControl">break-unless</span> newline?
*cursor-row<span class="Special"> <- </span>add *cursor-row, <span class="Constant">1</span>
*cursor-column<span class="Special"> <- </span>copy left
<span class="Delimiter">{</span>
screen-height:number<span class="Special"> <- </span>screen-height screen
below-screen?:boolean<span class="Special"> <- </span>greater-or-equal *cursor-row, screen-height <span class="Comment"># must be equal, never greater</span>
<span class="muControl">break-unless</span> below-screen?
<span class="Constant"> +scroll-down</span>
*cursor-row<span class="Special"> <- </span>subtract *cursor-row, <span class="Constant">1</span> <span class="Comment"># bring back into screen range</span>
<span class="Delimiter">}</span>
<span class="Comment"># indent if necessary</span>
d:address:duplex-list<span class="Special"> <- </span>get *editor, <span class="Constant">data:offset</span>
end-of-previous-line:address:duplex-list<span class="Special"> <- </span>prev-duplex *before-cursor
indent:number<span class="Special"> <- </span>line-indent end-of-previous-line, d
i:number<span class="Special"> <- </span>copy <span class="Constant">0</span>
<span class="Delimiter">{</span>
indent-done?:boolean<span class="Special"> <- </span>greater-or-equal i, indent
<span class="muControl">break-if</span> indent-done?
insert-at-cursor editor, <span class="Constant">32/space</span>, screen
i<span class="Special"> <- </span>add i, <span class="Constant">1</span>
<span class="muControl">loop</span>
<span class="Delimiter">}</span>
<span class="muControl">reply</span>
<span class="Delimiter">}</span>
]
<span class="Comment"># takes a pointer 'curr' into the doubly-linked list and its sentinel, counts</span>
<span class="Comment"># the number of spaces at the start of the line containing 'curr'.</span>
<span class="muRecipe">recipe</span> line-indent [
<span class="Constant">local-scope</span>
curr:address:duplex-list<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
start:address:duplex-list<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
result:number<span class="Special"> <- </span>copy <span class="Constant">0</span>
<span class="muControl">reply-unless</span> curr, result
at-start?:boolean<span class="Special"> <- </span>equal curr, start
<span class="muControl">reply-if</span> at-start?, result
<span class="Delimiter">{</span>
curr<span class="Special"> <- </span>prev-duplex curr
<span class="muControl">break-unless</span> curr
at-start?:boolean<span class="Special"> <- </span>equal curr, start
<span class="muControl">break-if</span> at-start?
c:character<span class="Special"> <- </span>get *curr, <span class="Constant">value:offset</span>
at-newline?:boolean<span class="Special"> <- </span>equal c, <span class="Constant">10/newline</span>
<span class="muControl">break-if</span> at-newline?
<span class="Comment"># if c is a space, increment result</span>
is-space?:boolean<span class="Special"> <- </span>equal c, <span class="Constant">32/space</span>
<span class="Delimiter">{</span>
<span class="muControl">break-unless</span> is-space?
result<span class="Special"> <- </span>add result, <span class="Constant">1</span>
<span class="Delimiter">}</span>
<span class="Comment"># if c is not a space, reset result</span>
<span class="Delimiter">{</span>
<span class="muControl">break-if</span> is-space?
result<span class="Special"> <- </span>copy <span class="Constant">0</span>
<span class="Delimiter">}</span>
<span class="muControl">loop</span>
<span class="Delimiter">}</span>
<span class="muControl">reply</span> result
]
<span class="muScenario">scenario</span> editor-moves-cursor-down-after-inserting-newline-2 [
assume-screen <span class="Constant">10/width</span>, <span class="Constant">5/height</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[abc]</span>
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">1/left</span>, <span class="Constant">10/right</span>
assume-console [
type <span class="Constant">[0</span>
<span class="Constant">1]</span>
]
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
]
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> . 0 .</span>
<span class="Constant"> . 1abc .</span>
<span class="Constant"> . .</span>
]
]
<span class="muScenario">scenario</span> editor-clears-previous-line-completely-after-inserting-newline [
assume-screen <span class="Constant">10/width</span>, <span class="Constant">5/height</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[abcde]</span>
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">5/right</span>
<span class="Comment"># press just a 'newline'</span>
assume-console [
type [
]
]
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .abcd↩ .</span>
<span class="Constant"> .e .</span>
<span class="Constant"> . .</span>
<span class="Constant"> . .</span>
]
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
]
<span class="Comment"># line should be fully cleared</span>
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> . .</span>
<span class="Constant"> .abcd↩ .</span>
<span class="Constant"> .e .</span>
<span class="Constant"> . .</span>
]
]
<span class="muScenario">scenario</span> editor-inserts-indent-after-newline [
assume-screen <span class="Constant">10/width</span>, <span class="Constant">10/height</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[ab</span>
<span class="Constant"> cd</span>
<span class="Constant">ef]</span>
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">10/right</span>
<span class="Comment"># position cursor after 'cd' and hit 'newline'</span>
assume-console [
left-click <span class="Constant">2</span>, <span class="Constant">8</span>
type [
]
]
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
<span class="Constant">3</span>:number<span class="Special"> <- </span>get *<span class="Constant">2</span>:address:editor-data, <span class="Constant">cursor-row:offset</span>
<span class="Constant">4</span>:number<span class="Special"> <- </span>get *<span class="Constant">2</span>:address:editor-data, <span class="Constant">cursor-column:offset</span>
]
<span class="Comment"># cursor should be below start of previous line</span>
memory-should-contain [
<span class="Constant">3</span><span class="Special"> <- </span><span class="Constant">3</span> <span class="Comment"># cursor row</span>
<span class="Constant">4</span><span class="Special"> <- </span><span class="Constant">2</span> <span class="Comment"># cursor column (indented)</span>
]
]
<span class="SalientComment">## special shortcuts for manipulating the editor</span>
<span class="Comment"># Some keys on the keyboard generate unicode characters, others generate</span>
<span class="Comment"># terminfo key codes. We need to modify different places in the two cases.</span>
<span class="Comment"># tab - insert two spaces</span>
<span class="muScenario">scenario</span> editor-inserts-two-spaces-on-tab [
assume-screen <span class="Constant">10/width</span>, <span class="Constant">5/height</span>
<span class="Comment"># just one character in final line</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[ab</span>
<span class="Constant">cd]</span>
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">5/right</span>
assume-console [
type <span class="Constant">[»]</span>
]
<span class="Constant">3</span>:event/tab<span class="Special"> <- </span>merge <span class="Constant">0/text</span>, <span class="Constant">9/tab</span>, <span class="Constant">0/dummy</span>, <span class="Constant">0/dummy</span>
replace-in-console <span class="Constant">187/»</span>, <span class="Constant">3</span>:event/tab
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
]
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> . ab .</span>
<span class="Constant"> .cd .</span>
]
]
<span class="muRecipe">after</span> +handle-special-character [
<span class="Delimiter">{</span>
tab?:boolean<span class="Special"> <- </span>equal *c, <span class="Constant">9/tab</span>
<span class="muControl">break-unless</span> tab?
insert-at-cursor editor, <span class="Constant">32/space</span>, screen
insert-at-cursor editor, <span class="Constant">32/space</span>, screen
<span class="muControl">reply</span>
<span class="Delimiter">}</span>
]
<span class="Comment"># backspace - delete character before cursor</span>
<span class="muScenario">scenario</span> editor-handles-backspace-key [
assume-screen <span class="Constant">10/width</span>, <span class="Constant">5/height</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[abc]</span>
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">10/right</span>
assume-console [
left-click <span class="Constant">1</span>, <span class="Constant">1</span>
type <span class="Constant">[«]</span>
]
<span class="Constant">3</span>:event/backspace<span class="Special"> <- </span>merge <span class="Constant">0/text</span>, <span class="Constant">8/backspace</span>, <span class="Constant">0/dummy</span>, <span class="Constant">0/dummy</span>
replace-in-console <span class="Constant">171/«</span>, <span class="Constant">3</span>:event/backspace
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
<span class="Constant">4</span>:number<span class="Special"> <- </span>get *<span class="Constant">2</span>:address:editor-data, <span class="Constant">cursor-row:offset</span>
<span class="Constant">5</span>:number<span class="Special"> <- </span>get *<span class="Constant">2</span>:address:editor-data, <span class="Constant">cursor-column:offset</span>
]
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .bc .</span>
<span class="Constant"> . .</span>
]
memory-should-contain [
<span class="Constant">4</span><span class="Special"> <- </span><span class="Constant">1</span>
<span class="Constant">5</span><span class="Special"> <- </span><span class="Constant">0</span>
]
]
<span class="muRecipe">after</span> +handle-special-character [
<span class="Delimiter">{</span>
backspace?:boolean<span class="Special"> <- </span>equal *c, <span class="Constant">8/backspace</span>
<span class="muControl">break-unless</span> backspace?
delete-before-cursor editor
<span class="muControl">reply</span>
<span class="Delimiter">}</span>
]
<span class="muRecipe">recipe</span> delete-before-cursor [
<span class="Constant">local-scope</span>
editor:address:editor-data<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
before-cursor:address:address:duplex-list<span class="Special"> <- </span>get-address *editor, <span class="Constant">before-cursor:offset</span>
<span class="Comment"># if at start of text (before-cursor at § sentinel), return</span>
prev:address:duplex-list<span class="Special"> <- </span>prev-duplex *before-cursor
<span class="muControl">reply-unless</span> prev
<span class="CommentedCode">#? trace [app], [delete-before-cursor] #? 1</span>
editor<span class="Special"> <- </span>move-cursor-coordinates-left editor
remove-duplex *before-cursor
*before-cursor<span class="Special"> <- </span>copy prev
]
<span class="muRecipe">recipe</span> move-cursor-coordinates-left [
<span class="Constant">local-scope</span>
editor:address:editor-data<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
before-cursor:address:duplex-list<span class="Special"> <- </span>get *editor, <span class="Constant">before-cursor:offset</span>
cursor-row:address:number<span class="Special"> <- </span>get-address *editor, <span class="Constant">cursor-row:offset</span>
cursor-column:address:number<span class="Special"> <- </span>get-address *editor, <span class="Constant">cursor-column:offset</span>
left:number<span class="Special"> <- </span>get *editor, <span class="Constant">left:offset</span>
<span class="Comment"># if not at left margin, move one character left</span>
<span class="Delimiter">{</span>
at-left-margin?:boolean<span class="Special"> <- </span>equal *cursor-column, left
<span class="muControl">break-if</span> at-left-margin?
<span class="CommentedCode">#? trace [app], [decrementing cursor column] #? 1</span>
*cursor-column<span class="Special"> <- </span>subtract *cursor-column, <span class="Constant">1</span>
<span class="muControl">reply</span> editor/same-as-ingredient:<span class="Constant">0</span>
<span class="Delimiter">}</span>
<span class="Comment"># if at left margin, we must move to previous row:</span>
top-of-screen?:boolean<span class="Special"> <- </span>equal *cursor-row, <span class="Constant">1</span> <span class="Comment"># exclude menu bar</span>
<span class="Delimiter">{</span>
<span class="muControl">break-if</span> top-of-screen?
*cursor-row<span class="Special"> <- </span>subtract *cursor-row, <span class="Constant">1</span>
<span class="Delimiter">}</span>
<span class="Delimiter">{</span>
<span class="muControl">break-unless</span> top-of-screen?
<span class="Constant"> +scroll-up</span>
<span class="Delimiter">}</span>
<span class="Delimiter">{</span>
<span class="Comment"># case 1: if previous character was newline, figure out how long the previous line is</span>
previous-character:character<span class="Special"> <- </span>get *before-cursor, <span class="Constant">value:offset</span>
previous-character-is-newline?:boolean<span class="Special"> <- </span>equal previous-character, <span class="Constant">10/newline</span>
<span class="muControl">break-unless</span> previous-character-is-newline?
<span class="Comment"># compute length of previous line</span>
<span class="CommentedCode">#? trace [app], [switching to previous line] #? 1</span>
d:address:duplex-list<span class="Special"> <- </span>get *editor, <span class="Constant">data:offset</span>
end-of-line:number<span class="Special"> <- </span>previous-line-length before-cursor, d
*cursor-column<span class="Special"> <- </span>add left, end-of-line
<span class="muControl">reply</span> editor/same-as-ingredient:<span class="Constant">0</span>
<span class="Delimiter">}</span>
<span class="Comment"># case 2: if previous-character was not newline, we're just at a wrapped line</span>
<span class="CommentedCode">#? trace [app], [wrapping to previous line] #? 1</span>
right:number<span class="Special"> <- </span>get *editor, <span class="Constant">right:offset</span>
*cursor-column<span class="Special"> <- </span>subtract right, <span class="Constant">1</span> <span class="Comment"># leave room for wrap icon</span>
<span class="muControl">reply</span> editor/same-as-ingredient:<span class="Constant">0</span>
]
<span class="Comment"># takes a pointer 'curr' into the doubly-linked list and its sentinel, counts</span>
<span class="Comment"># the length of the previous line before the 'curr' pointer.</span>
<span class="muRecipe">recipe</span> previous-line-length [
<span class="Constant">local-scope</span>
curr:address:duplex-list<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
start:address:duplex-list<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
result:number<span class="Special"> <- </span>copy <span class="Constant">0</span>
<span class="muControl">reply-unless</span> curr, result
at-start?:boolean<span class="Special"> <- </span>equal curr, start
<span class="muControl">reply-if</span> at-start?, result
<span class="Delimiter">{</span>
curr<span class="Special"> <- </span>prev-duplex curr
<span class="muControl">break-unless</span> curr
at-start?:boolean<span class="Special"> <- </span>equal curr, start
<span class="muControl">break-if</span> at-start?
c:character<span class="Special"> <- </span>get *curr, <span class="Constant">value:offset</span>
at-newline?:boolean<span class="Special"> <- </span>equal c, <span class="Constant">10/newline</span>
<span class="muControl">break-if</span> at-newline?
result<span class="Special"> <- </span>add result, <span class="Constant">1</span>
<span class="muControl">loop</span>
<span class="Delimiter">}</span>
<span class="muControl">reply</span> result
]
<span class="muScenario">scenario</span> editor-clears-last-line-on-backspace [
assume-screen <span class="Constant">10/width</span>, <span class="Constant">5/height</span>
<span class="Comment"># just one character in final line</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[ab</span>
<span class="Constant">cd]</span>
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">10/right</span>
assume-console [
left-click <span class="Constant">2</span>, <span class="Constant">0</span> <span class="Comment"># cursor at only character in final line</span>
type <span class="Constant">[«]</span>
]
<span class="Constant">3</span>:event/backspace<span class="Special"> <- </span>merge <span class="Constant">0/text</span>, <span class="Constant">8/backspace</span>, <span class="Constant">0/dummy</span>, <span class="Constant">0/dummy</span>
replace-in-console <span class="Constant">171/«</span>, <span class="Constant">3</span>:event/backspace
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
<span class="Constant">4</span>:number<span class="Special"> <- </span>get *<span class="Constant">2</span>:address:editor-data, <span class="Constant">cursor-row:offset</span>
<span class="Constant">5</span>:number<span class="Special"> <- </span>get *<span class="Constant">2</span>:address:editor-data, <span class="Constant">cursor-column:offset</span>
]
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .abcd .</span>
<span class="Constant"> . .</span>
]
memory-should-contain [
<span class="Constant">4</span><span class="Special"> <- </span><span class="Constant">1</span>
<span class="Constant">5</span><span class="Special"> <- </span><span class="Constant">2</span>
]
]
<span class="Comment"># delete - delete character at cursor</span>
<span class="muScenario">scenario</span> editor-handles-delete-key [
assume-screen <span class="Constant">10/width</span>, <span class="Constant">5/height</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[abc]</span>
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">10/right</span>
assume-console [
press <span class="Constant">65522</span> <span class="Comment"># delete</span>
]
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
]
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .bc .</span>
<span class="Constant"> . .</span>
]
assume-console [
press <span class="Constant">65522</span> <span class="Comment"># delete</span>
]
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
]
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .c .</span>
<span class="Constant"> . .</span>
]
]
<span class="muRecipe">after</span> +handle-special-key [
<span class="Delimiter">{</span>
delete?:boolean<span class="Special"> <- </span>equal *k, <span class="Constant">65522/delete</span>
<span class="muControl">break-unless</span> delete?
curr:address:duplex-list<span class="Special"> <- </span>get **before-cursor, <span class="Constant">next:offset</span>
_<span class="Special"> <- </span>remove-duplex curr
<span class="muControl">reply</span>
<span class="Delimiter">}</span>
]
<span class="Comment"># right arrow</span>
<span class="muScenario">scenario</span> editor-moves-cursor-right-with-key [
assume-screen <span class="Constant">10/width</span>, <span class="Constant">5/height</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[abc]</span>
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">10/right</span>
assume-console [
press <span class="Constant">65514</span> <span class="Comment"># right arrow</span>
type <span class="Constant">[0]</span>
]
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
]
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .a0bc .</span>
<span class="Constant"> . .</span>
]
]
<span class="muRecipe">after</span> +handle-special-key [
<span class="Delimiter">{</span>
move-to-next-character?:boolean<span class="Special"> <- </span>equal *k, <span class="Constant">65514/right-arrow</span>
<span class="muControl">break-unless</span> move-to-next-character?
<span class="Comment"># if not at end of text</span>
old-cursor:address:duplex-list<span class="Special"> <- </span>next-duplex *before-cursor
<span class="muControl">break-unless</span> old-cursor
<span class="Comment"># scan to next character</span>
*before-cursor<span class="Special"> <- </span>copy old-cursor
<span class="Comment"># if crossed a newline, move cursor to start of next row</span>
<span class="Delimiter">{</span>
old-cursor-character:character<span class="Special"> <- </span>get **before-cursor, <span class="Constant">value:offset</span>
was-at-newline?:boolean<span class="Special"> <- </span>equal old-cursor-character, <span class="Constant">10/newline</span>
<span class="muControl">break-unless</span> was-at-newline?
*cursor-row<span class="Special"> <- </span>add *cursor-row, <span class="Constant">1</span>
*cursor-column<span class="Special"> <- </span>copy left
below-screen?:boolean<span class="Special"> <- </span>greater-or-equal *cursor-row, screen-height <span class="Comment"># must be equal</span>
<span class="muControl">reply-unless</span> below-screen?
<span class="Constant"> +scroll-down</span>
*cursor-row<span class="Special"> <- </span>subtract *cursor-row, <span class="Constant">1</span> <span class="Comment"># bring back into screen range</span>
<span class="muControl">reply</span>
<span class="Delimiter">}</span>
<span class="Comment"># if the line wraps, move cursor to start of next row</span>
<span class="Delimiter">{</span>
<span class="Comment"># if we're at the column just before the wrap indicator</span>
wrap-column:number<span class="Special"> <- </span>subtract right, <span class="Constant">1</span>
at-wrap?:boolean<span class="Special"> <- </span>equal *cursor-column, wrap-column
<span class="muControl">break-unless</span> at-wrap?
<span class="Comment"># and if next character isn't newline</span>
new-cursor:address:duplex-list<span class="Special"> <- </span>next-duplex old-cursor
<span class="muControl">break-unless</span> new-cursor
next-character:character<span class="Special"> <- </span>get *new-cursor, <span class="Constant">value:offset</span>
newline?:boolean<span class="Special"> <- </span>equal next-character, <span class="Constant">10/newline</span>
<span class="muControl">break-if</span> newline?
*cursor-row<span class="Special"> <- </span>add *cursor-row, <span class="Constant">1</span>
*cursor-column<span class="Special"> <- </span>copy left
below-screen?:boolean<span class="Special"> <- </span>greater-or-equal *cursor-row, screen-height <span class="Comment"># must be equal</span>
<span class="muControl">reply-unless</span> below-screen?
<span class="Constant"> +scroll-down</span>
*cursor-row<span class="Special"> <- </span>subtract *cursor-row, <span class="Constant">1</span> <span class="Comment"># bring back into screen range</span>
<span class="muControl">reply</span>
<span class="Delimiter">}</span>
<span class="Comment"># otherwise move cursor one character right</span>
*cursor-column<span class="Special"> <- </span>add *cursor-column, <span class="Constant">1</span>
<span class="Delimiter">}</span>
]
<span class="muScenario">scenario</span> editor-moves-cursor-to-next-line-with-right-arrow [
assume-screen <span class="Constant">10/width</span>, <span class="Constant">5/height</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[abc</span>
<span class="Constant">d]</span>
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">10/right</span>
assume-console [
press <span class="Constant">65514</span> <span class="Comment"># right arrow</span>
press <span class="Constant">65514</span> <span class="Comment"># right arrow</span>
press <span class="Constant">65514</span> <span class="Comment"># right arrow</span>
press <span class="Constant">65514</span> <span class="Comment"># right arrow - next line</span>
type <span class="Constant">[0]</span>
]
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
]
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .abc .</span>
<span class="Constant"> .0d .</span>
<span class="Constant"> . .</span>
]
]
<span class="muScenario">scenario</span> editor-moves-cursor-to-next-line-with-right-arrow-2 [
assume-screen <span class="Constant">10/width</span>, <span class="Constant">5/height</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[abc</span>
<span class="Constant">d]</span>
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">1/left</span>, <span class="Constant">10/right</span>
assume-console [
press <span class="Constant">65514</span> <span class="Comment"># right arrow</span>
press <span class="Constant">65514</span> <span class="Comment"># right arrow</span>
press <span class="Constant">65514</span> <span class="Comment"># right arrow</span>
press <span class="Constant">65514</span> <span class="Comment"># right arrow - next line</span>
type <span class="Constant">[0]</span>
]
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
]
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> . abc .</span>
<span class="Constant"> . 0d .</span>
<span class="Constant"> . .</span>
]
]
<span class="muScenario">scenario</span> editor-moves-cursor-to-next-wrapped-line-with-right-arrow [
assume-screen <span class="Constant">10/width</span>, <span class="Constant">5/height</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[abcdef]</span>
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">5/right</span>
assume-console [
left-click <span class="Constant">1</span>, <span class="Constant">3</span>
press <span class="Constant">65514</span> <span class="Comment"># right arrow</span>
]
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
<span class="Constant">3</span>:number<span class="Special"> <- </span>get *<span class="Constant">2</span>:address:editor-data, <span class="Constant">cursor-row:offset</span>
<span class="Constant">4</span>:number<span class="Special"> <- </span>get *<span class="Constant">2</span>:address:editor-data, <span class="Constant">cursor-column:offset</span>
]
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .abcd↩ .</span>
<span class="Constant"> .ef .</span>
<span class="Constant"> . .</span>
]
memory-should-contain [
<span class="Constant">3</span><span class="Special"> <- </span><span class="Constant">2</span>
<span class="Constant">4</span><span class="Special"> <- </span><span class="Constant">0</span>
]
]
<span class="muScenario">scenario</span> editor-moves-cursor-to-next-wrapped-line-with-right-arrow-2 [
assume-screen <span class="Constant">10/width</span>, <span class="Constant">5/height</span>
<span class="Comment"># line just barely wrapping</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[abcde]</span>
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">5/right</span>
<span class="Comment"># position cursor at last character before wrap and hit right-arrow</span>
assume-console [
left-click <span class="Constant">1</span>, <span class="Constant">3</span>
press <span class="Constant">65514</span> <span class="Comment"># right arrow</span>
]
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
<span class="Constant">3</span>:number<span class="Special"> <- </span>get *<span class="Constant">2</span>:address:editor-data, <span class="Constant">cursor-row:offset</span>
<span class="Constant">4</span>:number<span class="Special"> <- </span>get *<span class="Constant">2</span>:address:editor-data, <span class="Constant">cursor-column:offset</span>
]
memory-should-contain [
<span class="Constant">3</span><span class="Special"> <- </span><span class="Constant">2</span>
<span class="Constant">4</span><span class="Special"> <- </span><span class="Constant">0</span>
]
<span class="Comment"># now hit right arrow again</span>
assume-console [
press <span class="Constant">65514</span>
]
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
<span class="Constant">3</span>:number<span class="Special"> <- </span>get *<span class="Constant">2</span>:address:editor-data, <span class="Constant">cursor-row:offset</span>
<span class="Constant">4</span>:number<span class="Special"> <- </span>get *<span class="Constant">2</span>:address:editor-data, <span class="Constant">cursor-column:offset</span>
]
memory-should-contain [
<span class="Constant">3</span><span class="Special"> <- </span><span class="Constant">2</span>
<span class="Constant">4</span><span class="Special"> <- </span><span class="Constant">1</span>
]
]
<span class="muScenario">scenario</span> editor-moves-cursor-to-next-wrapped-line-with-right-arrow-3 [
assume-screen <span class="Constant">10/width</span>, <span class="Constant">5/height</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[abcdef]</span>
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">1/left</span>, <span class="Constant">6/right</span>
assume-console [
left-click <span class="Constant">1</span>, <span class="Constant">4</span>
press <span class="Constant">65514</span> <span class="Comment"># right arrow</span>
]
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
<span class="Constant">3</span>:number<span class="Special"> <- </span>get *<span class="Constant">2</span>:address:editor-data, <span class="Constant">cursor-row:offset</span>
<span class="Constant">4</span>:number<span class="Special"> <- </span>get *<span class="Constant">2</span>:address:editor-data, <span class="Constant">cursor-column:offset</span>
]
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> . abcd↩ .</span>
<span class="Constant"> . ef .</span>
<span class="Constant"> . .</span>
]
memory-should-contain [
<span class="Constant">3</span><span class="Special"> <- </span><span class="Constant">2</span>
<span class="Constant">4</span><span class="Special"> <- </span><span class="Constant">1</span>
]
]
<span class="muScenario">scenario</span> editor-moves-cursor-to-next-line-with-right-arrow-at-end-of-line [
assume-screen <span class="Constant">10/width</span>, <span class="Constant">5/height</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[abc</span>
<span class="Constant">d]</span>
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">10/right</span>
assume-console [
left-click <span class="Constant">1</span>, <span class="Constant">3</span>
press <span class="Constant">65514</span> <span class="Comment"># right arrow - next line</span>
type <span class="Constant">[0]</span>
]
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
]
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .abc .</span>
<span class="Constant"> .0d .</span>
<span class="Constant"> . .</span>
]
]
<span class="Comment"># left arrow</span>
<span class="muScenario">scenario</span> editor-moves-cursor-left-with-key [
assume-screen <span class="Constant">10/width</span>, <span class="Constant">5/height</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[abc]</span>
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">10/right</span>
assume-console [
left-click <span class="Constant">1</span>, <span class="Constant">2</span>
press <span class="Constant">65515</span> <span class="Comment"># left arrow</span>
type <span class="Constant">[0]</span>
]
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
]
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .a0bc .</span>
<span class="Constant"> . .</span>
]
]
<span class="muRecipe">after</span> +handle-special-key [
<span class="Delimiter">{</span>
move-to-previous-character?:boolean<span class="Special"> <- </span>equal *k, <span class="Constant">65515/left-arrow</span>
<span class="muControl">break-unless</span> move-to-previous-character?
<span class="CommentedCode">#? trace [app], [left arrow] #? 1</span>
<span class="Comment"># if not at start of text (before-cursor at § sentinel)</span>
prev:address:duplex-list<span class="Special"> <- </span>prev-duplex *before-cursor
<span class="muControl">break-unless</span> prev
editor<span class="Special"> <- </span>move-cursor-coordinates-left editor
<span class="Delimiter">}</span>
]
<span class="muScenario">scenario</span> editor-moves-cursor-to-previous-line-with-left-arrow-at-start-of-line [
assume-screen <span class="Constant">10/width</span>, <span class="Constant">5/height</span>
<span class="Comment"># initialize editor with two lines</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[abc</span>
<span class="Constant">d]</span>
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">10/right</span>
<span class="Comment"># position cursor at start of second line (so there's no previous newline)</span>
assume-console [
left-click <span class="Constant">2</span>, <span class="Constant">0</span>
press <span class="Constant">65515</span> <span class="Comment"># left arrow</span>
]
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
<span class="Constant">3</span>:number<span class="Special"> <- </span>get *<span class="Constant">2</span>:address:editor-data, <span class="Constant">cursor-row:offset</span>
<span class="Constant">4</span>:number<span class="Special"> <- </span>get *<span class="Constant">2</span>:address:editor-data, <span class="Constant">cursor-column:offset</span>
]
memory-should-contain [
<span class="Constant">3</span><span class="Special"> <- </span><span class="Constant">1</span>
<span class="Constant">4</span><span class="Special"> <- </span><span class="Constant">3</span>
]
]
<span class="muScenario">scenario</span> editor-moves-cursor-to-previous-line-with-left-arrow-at-start-of-line-2 [
assume-screen <span class="Constant">10/width</span>, <span class="Constant">5/height</span>
<span class="Comment"># initialize editor with three lines</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[abc</span>
<span class="Constant">def</span>
<span class="Constant">g]</span>
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">10/right</span>
<span class="Comment"># position cursor further down (so there's a newline before the character at</span>
<span class="Comment"># the cursor)</span>
assume-console [
left-click <span class="Constant">3</span>, <span class="Constant">0</span>
press <span class="Constant">65515</span> <span class="Comment"># left arrow</span>
type <span class="Constant">[0]</span>
]
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
]
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .abc .</span>
<span class="Constant"> .def0 .</span>
<span class="Constant"> .g .</span>
<span class="Constant"> . .</span>
]
]
<span class="muScenario">scenario</span> editor-moves-cursor-to-previous-line-with-left-arrow-at-start-of-line-3 [
assume-screen <span class="Constant">10/width</span>, <span class="Constant">5/height</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[abc</span>
<span class="Constant">def</span>
<span class="Constant">g]</span>
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">10/right</span>
<span class="Comment"># position cursor at start of text</span>
assume-console [
left-click <span class="Constant">1</span>, <span class="Constant">0</span>
press <span class="Constant">65515</span> <span class="Comment"># left arrow should have no effect</span>
type <span class="Constant">[0]</span>
]
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
]
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .0abc .</span>
<span class="Constant"> .def .</span>
<span class="Constant"> .g .</span>
<span class="Constant"> . .</span>
]
]
<span class="muScenario">scenario</span> editor-moves-cursor-to-previous-line-with-left-arrow-at-start-of-line-4 [
assume-screen <span class="Constant">10/width</span>, <span class="Constant">5/height</span>
<span class="Comment"># initialize editor with text containing an empty line</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[abc</span>
d]
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">10/right</span>
<span class="Comment"># position cursor right after empty line</span>
assume-console [
left-click <span class="Constant">3</span>, <span class="Constant">0</span>
press <span class="Constant">65515</span> <span class="Comment"># left arrow</span>
type <span class="Constant">[0]</span>
]
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
]
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .abc .</span>
<span class="Constant"> .0 .</span>
<span class="Constant"> .d .</span>
<span class="Constant"> . .</span>
]
]
<span class="muScenario">scenario</span> editor-moves-across-screen-lines-across-wrap-with-left-arrow [
assume-screen <span class="Constant">10/width</span>, <span class="Constant">5/height</span>
<span class="Comment"># initialize editor with text containing an empty line</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[abcdef]</span>
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">5/right</span>
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .abcd↩ .</span>
<span class="Constant"> .ef .</span>
<span class="Constant"> . .</span>
]
<span class="Comment"># position cursor right after empty line</span>
assume-console [
left-click <span class="Constant">2</span>, <span class="Constant">0</span>
press <span class="Constant">65515</span> <span class="Comment"># left arrow</span>
]
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
<span class="Constant">3</span>:number<span class="Special"> <- </span>get *<span class="Constant">2</span>:address:editor-data, <span class="Constant">cursor-row:offset</span>
<span class="Constant">4</span>:number<span class="Special"> <- </span>get *<span class="Constant">2</span>:address:editor-data, <span class="Constant">cursor-column:offset</span>
]
memory-should-contain [
<span class="Constant">3</span><span class="Special"> <- </span><span class="Constant">1</span> <span class="Comment"># previous row</span>
<span class="Constant">4</span><span class="Special"> <- </span><span class="Constant">3</span> <span class="Comment"># end of wrapped line</span>
]
]
<span class="Comment"># up arrow</span>
<span class="muScenario">scenario</span> editor-moves-to-previous-line-with-up-arrow [
assume-screen <span class="Constant">10/width</span>, <span class="Constant">5/height</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[abc</span>
<span class="Constant">def]</span>
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">10/right</span>
assume-console [
left-click <span class="Constant">2</span>, <span class="Constant">1</span>
press <span class="Constant">65517</span> <span class="Comment"># up arrow</span>
]
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
<span class="Constant">3</span>:number<span class="Special"> <- </span>get *<span class="Constant">2</span>:address:editor-data, <span class="Constant">cursor-row:offset</span>
<span class="Constant">4</span>:number<span class="Special"> <- </span>get *<span class="Constant">2</span>:address:editor-data, <span class="Constant">cursor-column:offset</span>
]
memory-should-contain [
<span class="Constant">3</span><span class="Special"> <- </span><span class="Constant">1</span>
<span class="Constant">4</span><span class="Special"> <- </span><span class="Constant">1</span>
]
]
<span class="muRecipe">after</span> +handle-special-key [
<span class="Delimiter">{</span>
move-to-previous-line?:boolean<span class="Special"> <- </span>equal *k, <span class="Constant">65517/up-arrow</span>
<span class="muControl">break-unless</span> move-to-previous-line?
already-at-top?:boolean<span class="Special"> <- </span>lesser-or-equal *cursor-row, <span class="Constant">1/top</span>
<span class="Delimiter">{</span>
<span class="Comment"># if cursor not at top, move it</span>
<span class="muControl">break-if</span> already-at-top?
*cursor-row<span class="Special"> <- </span>subtract *cursor-row, <span class="Constant">1</span>
<span class="Delimiter">}</span>
<span class="Delimiter">{</span>
<span class="Comment"># if cursor already at top, scroll up</span>
<span class="muControl">break-unless</span> already-at-top?
<span class="Constant"> +scroll-up</span>
<span class="Delimiter">}</span>
<span class="Comment"># that's it; render will adjust cursor-column as necessary</span>
<span class="Delimiter">}</span>
]
<span class="muScenario">scenario</span> editor-adjusts-column-at-next-line [
assume-screen <span class="Constant">10/width</span>, <span class="Constant">5/height</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[abc</span>
<span class="Constant">de]</span>
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">10/right</span>
assume-console [
left-click <span class="Constant">1</span>, <span class="Constant">3</span>
press <span class="Constant">65516</span> <span class="Comment"># down arrow</span>
]
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
<span class="Constant">3</span>:number<span class="Special"> <- </span>get *<span class="Constant">2</span>:address:editor-data, <span class="Constant">cursor-row:offset</span>
<span class="Constant">4</span>:number<span class="Special"> <- </span>get *<span class="Constant">2</span>:address:editor-data, <span class="Constant">cursor-column:offset</span>
]
memory-should-contain [
<span class="Constant">3</span><span class="Special"> <- </span><span class="Constant">2</span>
<span class="Constant">4</span><span class="Special"> <- </span><span class="Constant">2</span>
]
]
<span class="Comment"># down arrow</span>
<span class="muScenario">scenario</span> editor-moves-to-next-line-with-down-arrow [
assume-screen <span class="Constant">10/width</span>, <span class="Constant">5/height</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[abc</span>
<span class="Constant">def]</span>
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">10/right</span>
<span class="Comment"># cursor starts out at (1, 0)</span>
assume-console [
press <span class="Constant">65516</span> <span class="Comment"># down arrow</span>
]
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
<span class="Constant">3</span>:number<span class="Special"> <- </span>get *<span class="Constant">2</span>:address:editor-data, <span class="Constant">cursor-row:offset</span>
<span class="Constant">4</span>:number<span class="Special"> <- </span>get *<span class="Constant">2</span>:address:editor-data, <span class="Constant">cursor-column:offset</span>
]
<span class="Comment"># ..and ends at (2, 0)</span>
memory-should-contain [
<span class="Constant">3</span><span class="Special"> <- </span><span class="Constant">2</span>
<span class="Constant">4</span><span class="Special"> <- </span><span class="Constant">0</span>
]
]
<span class="muRecipe">after</span> +handle-special-key [
<span class="Delimiter">{</span>
move-to-next-line?:boolean<span class="Special"> <- </span>equal *k, <span class="Constant">65516/down-arrow</span>
<span class="muControl">break-unless</span> move-to-next-line?
last-line:number<span class="Special"> <- </span>subtract screen-height, <span class="Constant">1</span>
already-at-bottom?:boolean<span class="Special"> <- </span>greater-or-equal *cursor-row, last-line
<span class="Delimiter">{</span>
<span class="Comment"># if cursor not at top, move it</span>
<span class="muControl">break-if</span> already-at-bottom?
*cursor-row<span class="Special"> <- </span>add *cursor-row, <span class="Constant">1</span>
<span class="Delimiter">}</span>
<span class="Delimiter">{</span>
<span class="Comment"># if cursor already at top, scroll up</span>
<span class="muControl">break-unless</span> already-at-bottom?
<span class="Constant"> +scroll-down</span>
<span class="Delimiter">}</span>
<span class="Comment"># that's it; render will adjust cursor-column as necessary</span>
<span class="Delimiter">}</span>
]
<span class="muScenario">scenario</span> editor-adjusts-column-at-previous-line [
assume-screen <span class="Constant">10/width</span>, <span class="Constant">5/height</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[ab</span>
<span class="Constant">def]</span>
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">10/right</span>
assume-console [
left-click <span class="Constant">2</span>, <span class="Constant">3</span>
press <span class="Constant">65517</span> <span class="Comment"># up arrow</span>
]
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
<span class="Constant">3</span>:number<span class="Special"> <- </span>get *<span class="Constant">2</span>:address:editor-data, <span class="Constant">cursor-row:offset</span>
<span class="Constant">4</span>:number<span class="Special"> <- </span>get *<span class="Constant">2</span>:address:editor-data, <span class="Constant">cursor-column:offset</span>
]
memory-should-contain [
<span class="Constant">3</span><span class="Special"> <- </span><span class="Constant">1</span>
<span class="Constant">4</span><span class="Special"> <- </span><span class="Constant">2</span>
]
]
<span class="Comment"># ctrl-a/home - move cursor to start of line</span>
<span class="muScenario">scenario</span> editor-moves-to-start-of-line-with-ctrl-a [
assume-screen <span class="Constant">10/width</span>, <span class="Constant">5/height</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[123</span>
<span class="Constant">456]</span>
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">10/right</span>
<span class="Comment"># start on second line, press ctrl-a</span>
assume-console [
left-click <span class="Constant">2</span>, <span class="Constant">3</span>
type <span class="Constant">[a]</span> <span class="Comment"># ctrl-a</span>
]
<span class="Constant">3</span>:event/ctrl-a<span class="Special"> <- </span>merge <span class="Constant">0/text</span>, <span class="Constant">1/ctrl-a</span>, <span class="Constant">0/dummy</span>, <span class="Constant">0/dummy</span>
replace-in-console <span class="Constant">97/a</span>, <span class="Constant">3</span>:event/ctrl-a
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
<span class="Constant">4</span>:number<span class="Special"> <- </span>get *<span class="Constant">2</span>:address:editor-data, <span class="Constant">cursor-row:offset</span>
<span class="Constant">5</span>:number<span class="Special"> <- </span>get *<span class="Constant">2</span>:address:editor-data, <span class="Constant">cursor-column:offset</span>
]
<span class="Comment"># cursor moves to start of line</span>
memory-should-contain [
<span class="Constant">4</span><span class="Special"> <- </span><span class="Constant">2</span>
<span class="Constant">5</span><span class="Special"> <- </span><span class="Constant">0</span>
]
]
<span class="muRecipe">after</span> +handle-special-character [
<span class="Delimiter">{</span>
ctrl-a?:boolean<span class="Special"> <- </span>equal *c, <span class="Constant">1/ctrl-a</span>
<span class="muControl">break-unless</span> ctrl-a?
move-to-start-of-line editor
<span class="muControl">reply</span>
<span class="Delimiter">}</span>
]
<span class="muRecipe">after</span> +handle-special-key [
<span class="Delimiter">{</span>
home?:boolean<span class="Special"> <- </span>equal *k, <span class="Constant">65521/home</span>
<span class="muControl">break-unless</span> home?
move-to-start-of-line editor
<span class="muControl">reply</span>
<span class="Delimiter">}</span>
]
<span class="muRecipe">recipe</span> move-to-start-of-line [
<span class="Constant">local-scope</span>
editor:address:editor-data<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
<span class="Comment"># update cursor column</span>
left:number<span class="Special"> <- </span>get *editor, <span class="Constant">left:offset</span>
cursor-column:address:number<span class="Special"> <- </span>get-address *editor, <span class="Constant">cursor-column:offset</span>
*cursor-column<span class="Special"> <- </span>copy left
<span class="Comment"># update before-cursor</span>
before-cursor:address:address:duplex-list<span class="Special"> <- </span>get-address *editor, <span class="Constant">before-cursor:offset</span>
init:address:duplex-list<span class="Special"> <- </span>get *editor, <span class="Constant">data:offset</span>
<span class="Comment"># while not at start of line, move </span>
<span class="Delimiter">{</span>
at-start-of-text?:boolean<span class="Special"> <- </span>equal *before-cursor, init
<span class="muControl">break-if</span> at-start-of-text?
prev:character<span class="Special"> <- </span>get **before-cursor, <span class="Constant">value:offset</span>
at-start-of-line?:boolean<span class="Special"> <- </span>equal prev, <span class="Constant">10/newline</span>
<span class="muControl">break-if</span> at-start-of-line?
*before-cursor<span class="Special"> <- </span>prev-duplex *before-cursor
assert *before-cursor, <span class="Constant">[move-to-start-of-line tried to move before start of text]</span>
<span class="muControl">loop</span>
<span class="Delimiter">}</span>
]
<span class="muScenario">scenario</span> editor-moves-to-start-of-line-with-ctrl-a-2 [
assume-screen <span class="Constant">10/width</span>, <span class="Constant">5/height</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[123</span>
<span class="Constant">456]</span>
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">10/right</span>
<span class="Comment"># start on first line (no newline before), press ctrl-a</span>
assume-console [
left-click <span class="Constant">1</span>, <span class="Constant">3</span>
type <span class="Constant">[a]</span> <span class="Comment"># ctrl-a</span>
]
<span class="Constant">3</span>:event/ctrl-a<span class="Special"> <- </span>merge <span class="Constant">0/text</span>, <span class="Constant">1/ctrl-a</span>, <span class="Constant">0/dummy</span>, <span class="Constant">0/dummy</span>
replace-in-console <span class="Constant">97/a</span>, <span class="Constant">3</span>:event/ctrl-a
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
<span class="Constant">4</span>:number<span class="Special"> <- </span>get *<span class="Constant">2</span>:address:editor-data, <span class="Constant">cursor-row:offset</span>
<span class="Constant">5</span>:number<span class="Special"> <- </span>get *<span class="Constant">2</span>:address:editor-data, <span class="Constant">cursor-column:offset</span>
]
<span class="Comment"># cursor moves to start of line</span>
memory-should-contain [
<span class="Constant">4</span><span class="Special"> <- </span><span class="Constant">1</span>
<span class="Constant">5</span><span class="Special"> <- </span><span class="Constant">0</span>
]
]
<span class="muScenario">scenario</span> editor-moves-to-start-of-line-with-home [
assume-screen <span class="Constant">10/width</span>, <span class="Constant">5/height</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[123</span>
<span class="Constant">456]</span>
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">10/right</span>
<span class="Comment"># start on second line, press 'home'</span>
assume-console [
left-click <span class="Constant">2</span>, <span class="Constant">3</span>
press <span class="Constant">65521</span> <span class="Comment"># 'home'</span>
]
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
<span class="Constant">3</span>:number<span class="Special"> <- </span>get *<span class="Constant">2</span>:address:editor-data, <span class="Constant">cursor-row:offset</span>
<span class="Constant">4</span>:number<span class="Special"> <- </span>get *<span class="Constant">2</span>:address:editor-data, <span class="Constant">cursor-column:offset</span>
]
<span class="Comment"># cursor moves to start of line</span>
memory-should-contain [
<span class="Constant">3</span><span class="Special"> <- </span><span class="Constant">2</span>
<span class="Constant">4</span><span class="Special"> <- </span><span class="Constant">0</span>
]
]
<span class="muScenario">scenario</span> editor-moves-to-start-of-line-with-home-2 [
assume-screen <span class="Constant">10/width</span>, <span class="Constant">5/height</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[123</span>
<span class="Constant">456]</span>
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">10/right</span>
<span class="Comment"># start on first line (no newline before), press 'home'</span>
assume-console [
left-click <span class="Constant">1</span>, <span class="Constant">3</span>
press <span class="Constant">65521</span> <span class="Comment"># 'home'</span>
]
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
<span class="Constant">3</span>:number<span class="Special"> <- </span>get *<span class="Constant">2</span>:address:editor-data, <span class="Constant">cursor-row:offset</span>
<span class="Constant">4</span>:number<span class="Special"> <- </span>get *<span class="Constant">2</span>:address:editor-data, <span class="Constant">cursor-column:offset</span>
]
<span class="Comment"># cursor moves to start of line</span>
memory-should-contain [
<span class="Constant">3</span><span class="Special"> <- </span><span class="Constant">1</span>
<span class="Constant">4</span><span class="Special"> <- </span><span class="Constant">0</span>
]
]
<span class="Comment"># ctrl-e/end - move cursor to end of line</span>
<span class="muScenario">scenario</span> editor-moves-to-start-of-line-with-ctrl-e [
assume-screen <span class="Constant">10/width</span>, <span class="Constant">5/height</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[123</span>
<span class="Constant">456]</span>
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">10/right</span>
<span class="Comment"># start on first line, press ctrl-e</span>
assume-console [
left-click <span class="Constant">1</span>, <span class="Constant">1</span>
type <span class="Constant">[e]</span> <span class="Comment"># ctrl-e</span>
]
<span class="Constant">3</span>:event/ctrl-e<span class="Special"> <- </span>merge <span class="Constant">0/text</span>, <span class="Constant">5/ctrl-e</span>, <span class="Constant">0/dummy</span>, <span class="Constant">0/dummy</span>
replace-in-console <span class="Constant">101/e</span>, <span class="Constant">3</span>:event/ctrl-e
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
<span class="Constant">4</span>:number<span class="Special"> <- </span>get *<span class="Constant">2</span>:address:editor-data, <span class="Constant">cursor-row:offset</span>
<span class="Constant">5</span>:number<span class="Special"> <- </span>get *<span class="Constant">2</span>:address:editor-data, <span class="Constant">cursor-column:offset</span>
]
<span class="Comment"># cursor moves to end of line</span>
memory-should-contain [
<span class="Constant">4</span><span class="Special"> <- </span><span class="Constant">1</span>
<span class="Constant">5</span><span class="Special"> <- </span><span class="Constant">3</span>
]
<span class="Comment"># editor inserts future characters at cursor</span>
assume-console [
type <span class="Constant">[z]</span>
]
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
<span class="Constant">4</span>:number<span class="Special"> <- </span>get *<span class="Constant">2</span>:address:editor-data, <span class="Constant">cursor-row:offset</span>
<span class="Constant">5</span>:number<span class="Special"> <- </span>get *<span class="Constant">2</span>:address:editor-data, <span class="Constant">cursor-column:offset</span>
]
memory-should-contain [
<span class="Constant">4</span><span class="Special"> <- </span><span class="Constant">1</span>
<span class="Constant">5</span><span class="Special"> <- </span><span class="Constant">4</span>
]
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .123z .</span>
<span class="Constant"> .456 .</span>
<span class="Constant"> . .</span>
]
]
<span class="muRecipe">after</span> +handle-special-character [
<span class="Delimiter">{</span>
ctrl-e?:boolean<span class="Special"> <- </span>equal *c, <span class="Constant">5/ctrl-e</span>
<span class="muControl">break-unless</span> ctrl-e?
move-to-end-of-line editor
<span class="muControl">reply</span>
<span class="Delimiter">}</span>
]
<span class="muRecipe">after</span> +handle-special-key [
<span class="Delimiter">{</span>
end?:boolean<span class="Special"> <- </span>equal *k, <span class="Constant">65520/end</span>
<span class="muControl">break-unless</span> end?
move-to-end-of-line editor
<span class="muControl">reply</span>
<span class="Delimiter">}</span>
]
<span class="muRecipe">recipe</span> move-to-end-of-line [
<span class="Constant">local-scope</span>
editor:address:editor-data<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
before-cursor:address:address:duplex-list<span class="Special"> <- </span>get-address *editor, <span class="Constant">before-cursor:offset</span>
cursor-column:address:number<span class="Special"> <- </span>get-address *editor, <span class="Constant">cursor-column:offset</span>
<span class="Comment"># while not at start of line, move </span>
<span class="Delimiter">{</span>
next:address:duplex-list<span class="Special"> <- </span>next-duplex *before-cursor
<span class="muControl">break-unless</span> next <span class="Comment"># end of text</span>
nextc:character<span class="Special"> <- </span>get *next, <span class="Constant">value:offset</span>
at-end-of-line?:boolean<span class="Special"> <- </span>equal nextc, <span class="Constant">10/newline</span>
<span class="muControl">break-if</span> at-end-of-line?
*before-cursor<span class="Special"> <- </span>copy next
*cursor-column<span class="Special"> <- </span>add *cursor-column, <span class="Constant">1</span>
<span class="muControl">loop</span>
<span class="Delimiter">}</span>
<span class="Comment"># move one past final character</span>
*cursor-column<span class="Special"> <- </span>add *cursor-column, <span class="Constant">1</span>
]
<span class="muScenario">scenario</span> editor-moves-to-end-of-line-with-ctrl-e-2 [
assume-screen <span class="Constant">10/width</span>, <span class="Constant">5/height</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[123</span>
<span class="Constant">456]</span>
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">10/right</span>
<span class="Comment"># start on second line (no newline after), press ctrl-e</span>
assume-console [
left-click <span class="Constant">2</span>, <span class="Constant">1</span>
type <span class="Constant">[e]</span> <span class="Comment"># ctrl-e</span>
]
<span class="Constant">3</span>:event/ctrl-e<span class="Special"> <- </span>merge <span class="Constant">0/text</span>, <span class="Constant">5/ctrl-e</span>, <span class="Constant">0/dummy</span>, <span class="Constant">0/dummy</span>
replace-in-console <span class="Constant">101/e</span>, <span class="Constant">3</span>:event/ctrl-e
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
<span class="Constant">4</span>:number<span class="Special"> <- </span>get *<span class="Constant">2</span>:address:editor-data, <span class="Constant">cursor-row:offset</span>
<span class="Constant">5</span>:number<span class="Special"> <- </span>get *<span class="Constant">2</span>:address:editor-data, <span class="Constant">cursor-column:offset</span>
]
<span class="Comment"># cursor moves to end of line</span>
memory-should-contain [
<span class="Constant">4</span><span class="Special"> <- </span><span class="Constant">2</span>
<span class="Constant">5</span><span class="Special"> <- </span><span class="Constant">3</span>
]
]
<span class="muScenario">scenario</span> editor-moves-to-end-of-line-with-end [
assume-screen <span class="Constant">10/width</span>, <span class="Constant">5/height</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[123</span>
<span class="Constant">456]</span>
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">10/right</span>
<span class="Comment"># start on first line, press 'end'</span>
assume-console [
left-click <span class="Constant">1</span>, <span class="Constant">1</span>
press <span class="Constant">65520</span> <span class="Comment"># 'end'</span>
]
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
<span class="Constant">3</span>:number<span class="Special"> <- </span>get *<span class="Constant">2</span>:address:editor-data, <span class="Constant">cursor-row:offset</span>
<span class="Constant">4</span>:number<span class="Special"> <- </span>get *<span class="Constant">2</span>:address:editor-data, <span class="Constant">cursor-column:offset</span>
]
<span class="Comment"># cursor moves to end of line</span>
memory-should-contain [
<span class="Constant">3</span><span class="Special"> <- </span><span class="Constant">1</span>
<span class="Constant">4</span><span class="Special"> <- </span><span class="Constant">3</span>
]
]
<span class="muScenario">scenario</span> editor-moves-to-end-of-line-with-end-2 [
assume-screen <span class="Constant">10/width</span>, <span class="Constant">5/height</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[123</span>
<span class="Constant">456]</span>
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">10/right</span>
<span class="Comment"># start on second line (no newline after), press 'end'</span>
assume-console [
left-click <span class="Constant">2</span>, <span class="Constant">1</span>
press <span class="Constant">65520</span> <span class="Comment"># 'end'</span>
]
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
<span class="Constant">3</span>:number<span class="Special"> <- </span>get *<span class="Constant">2</span>:address:editor-data, <span class="Constant">cursor-row:offset</span>
<span class="Constant">4</span>:number<span class="Special"> <- </span>get *<span class="Constant">2</span>:address:editor-data, <span class="Constant">cursor-column:offset</span>
]
<span class="Comment"># cursor moves to end of line</span>
memory-should-contain [
<span class="Constant">3</span><span class="Special"> <- </span><span class="Constant">2</span>
<span class="Constant">4</span><span class="Special"> <- </span><span class="Constant">3</span>
]
]
<span class="Comment"># ctrl-u - delete text from start of line until (but not at) cursor</span>
<span class="muScenario">scenario</span> editor-deletes-to-start-of-line-with-ctrl-u [
assume-screen <span class="Constant">10/width</span>, <span class="Constant">5/height</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[123</span>
<span class="Constant">456]</span>
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">10/right</span>
<span class="Comment"># start on second line, press ctrl-u</span>
assume-console [
left-click <span class="Constant">2</span>, <span class="Constant">2</span>
type <span class="Constant">[u]</span> <span class="Comment"># ctrl-u</span>
]
<span class="Constant">3</span>:event/ctrl-a<span class="Special"> <- </span>merge <span class="Constant">0/text</span>, <span class="Constant">21/ctrl-u</span>, <span class="Constant">0/dummy</span>, <span class="Constant">0/dummy</span>
replace-in-console <span class="Constant">117/u</span>, <span class="Constant">3</span>:event/ctrl-u
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
]
<span class="Comment"># cursor deletes to start of line</span>
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .123 .</span>
<span class="Constant"> .6 .</span>
<span class="Constant"> . .</span>
]
]
<span class="muRecipe">after</span> +handle-special-character [
<span class="Delimiter">{</span>
ctrl-u?:boolean<span class="Special"> <- </span>equal *c, <span class="Constant">21/ctrl-u</span>
<span class="muControl">break-unless</span> ctrl-u?
delete-to-start-of-line editor
<span class="muControl">reply</span>
<span class="Delimiter">}</span>
]
<span class="muRecipe">recipe</span> delete-to-start-of-line [
<span class="Constant">local-scope</span>
editor:address:editor-data<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
<span class="Comment"># compute range to delete</span>
init:address:duplex-list<span class="Special"> <- </span>get *editor, <span class="Constant">data:offset</span>
before-cursor:address:address:duplex-list<span class="Special"> <- </span>get-address *editor, <span class="Constant">before-cursor:offset</span>
start:address:duplex-list<span class="Special"> <- </span>copy *before-cursor
end:address:duplex-list<span class="Special"> <- </span>next-duplex *before-cursor
<span class="Delimiter">{</span>
at-start-of-text?:boolean<span class="Special"> <- </span>equal start, init
<span class="muControl">break-if</span> at-start-of-text?
curr:character<span class="Special"> <- </span>get *start, <span class="Constant">value:offset</span>
at-start-of-line?:boolean<span class="Special"> <- </span>equal curr, <span class="Constant">10/newline</span>
<span class="muControl">break-if</span> at-start-of-line?
start<span class="Special"> <- </span>prev-duplex start
assert start, <span class="Constant">[delete-to-start-of-line tried to move before start of text]</span>
<span class="muControl">loop</span>
<span class="Delimiter">}</span>
<span class="Comment"># snip it out</span>
start-next:address:address:duplex-list<span class="Special"> <- </span>get-address *start, <span class="Constant">next:offset</span>
*start-next<span class="Special"> <- </span>copy end
<span class="Delimiter">{</span>
<span class="muControl">break-unless</span> end
end-prev:address:address:duplex-list<span class="Special"> <- </span>get-address *end, <span class="Constant">prev:offset</span>
*end-prev<span class="Special"> <- </span>copy start
<span class="Delimiter">}</span>
<span class="Comment"># adjust cursor</span>
*before-cursor<span class="Special"> <- </span>prev-duplex end
left:number<span class="Special"> <- </span>get *editor, <span class="Constant">left:offset</span>
cursor-column:address:number<span class="Special"> <- </span>get-address *editor, <span class="Constant">cursor-column:offset</span>
*cursor-column<span class="Special"> <- </span>copy left
]
<span class="muScenario">scenario</span> editor-deletes-to-start-of-line-with-ctrl-u-2 [
assume-screen <span class="Constant">10/width</span>, <span class="Constant">5/height</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[123</span>
<span class="Constant">456]</span>
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">10/right</span>
<span class="Comment"># start on first line (no newline before), press ctrl-u</span>
assume-console [
left-click <span class="Constant">1</span>, <span class="Constant">2</span>
type <span class="Constant">[u]</span> <span class="Comment"># ctrl-u</span>
]
<span class="Constant">3</span>:event/ctrl-u<span class="Special"> <- </span>merge <span class="Constant">0/text</span>, <span class="Constant">21/ctrl-u</span>, <span class="Constant">0/dummy</span>, <span class="Constant">0/dummy</span>
replace-in-console <span class="Constant">117/u</span>, <span class="Constant">3</span>:event/ctrl-u
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
]
<span class="Comment"># cursor deletes to start of line</span>
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .3 .</span>
<span class="Constant"> .456 .</span>
<span class="Constant"> . .</span>
]
]
<span class="muScenario">scenario</span> editor-deletes-to-start-of-line-with-ctrl-u-3 [
assume-screen <span class="Constant">10/width</span>, <span class="Constant">5/height</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[123</span>
<span class="Constant">456]</span>
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">10/right</span>
<span class="Comment"># start past end of line, press ctrl-u</span>
assume-console [
left-click <span class="Constant">1</span>, <span class="Constant">3</span>
type <span class="Constant">[u]</span> <span class="Comment"># ctrl-u</span>
]
<span class="Constant">3</span>:event/ctrl-u<span class="Special"> <- </span>merge <span class="Constant">0/text</span>, <span class="Constant">21/ctrl-u</span>, <span class="Constant">0/dummy</span>, <span class="Constant">0/dummy</span>
replace-in-console <span class="Constant">117/u</span>, <span class="Constant">3</span>:event/ctrl-u
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
]
<span class="Comment"># cursor deletes to start of line</span>
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> . .</span>
<span class="Constant"> .456 .</span>
<span class="Constant"> . .</span>
]
]
<span class="muScenario">scenario</span> editor-deletes-to-start-of-final-line-with-ctrl-u [
assume-screen <span class="Constant">10/width</span>, <span class="Constant">5/height</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[123</span>
<span class="Constant">456]</span>
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">10/right</span>
<span class="Comment"># start past end of final line, press ctrl-u</span>
assume-console [
left-click <span class="Constant">2</span>, <span class="Constant">3</span>
type <span class="Constant">[u]</span> <span class="Comment"># ctrl-u</span>
]
<span class="Constant">3</span>:event/ctrl-u<span class="Special"> <- </span>merge <span class="Constant">0/text</span>, <span class="Constant">21/ctrl-u</span>, <span class="Constant">0/dummy</span>, <span class="Constant">0/dummy</span>
replace-in-console <span class="Constant">117/u</span>, <span class="Constant">3</span>:event/ctrl-u
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
]
<span class="Comment"># cursor deletes to start of line</span>
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .123 .</span>
<span class="Constant"> . .</span>
<span class="Constant"> . .</span>
]
]
<span class="Comment"># ctrl-k - delete text from cursor to end of line (but not the newline)</span>
<span class="muScenario">scenario</span> editor-deletes-to-end-of-line-with-ctrl-k [
assume-screen <span class="Constant">10/width</span>, <span class="Constant">5/height</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[123</span>
<span class="Constant">456]</span>
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">10/right</span>
<span class="Comment"># start on first line, press ctrl-k</span>
assume-console [
left-click <span class="Constant">1</span>, <span class="Constant">1</span>
type <span class="Constant">[k]</span> <span class="Comment"># ctrl-k</span>
]
<span class="Constant">3</span>:event/ctrl-k<span class="Special"> <- </span>merge <span class="Constant">0/text</span>, <span class="Constant">11/ctrl-k</span>, <span class="Constant">0/dummy</span>, <span class="Constant">0/dummy</span>
replace-in-console <span class="Constant">107/k</span>, <span class="Constant">3</span>:event/ctrl-k
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
]
<span class="Comment"># cursor deletes to end of line</span>
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .1 .</span>
<span class="Constant"> .456 .</span>
<span class="Constant"> . .</span>
]
]
<span class="muRecipe">after</span> +handle-special-character [
<span class="Delimiter">{</span>
ctrl-k?:boolean<span class="Special"> <- </span>equal *c, <span class="Constant">11/ctrl-k</span>
<span class="muControl">break-unless</span> ctrl-k?
delete-to-end-of-line editor
<span class="muControl">reply</span>
<span class="Delimiter">}</span>
]
<span class="muRecipe">recipe</span> delete-to-end-of-line [
<span class="Constant">local-scope</span>
editor:address:editor-data<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
<span class="Comment"># compute range to delete</span>
start:address:duplex-list<span class="Special"> <- </span>get *editor, <span class="Constant">before-cursor:offset</span>
end:address:duplex-list<span class="Special"> <- </span>next-duplex start
<span class="Delimiter">{</span>
at-end-of-text?:boolean<span class="Special"> <- </span>equal end, <span class="Constant">0/null</span>
<span class="muControl">break-if</span> at-end-of-text?
curr:character<span class="Special"> <- </span>get *end, <span class="Constant">value:offset</span>
at-end-of-line?:boolean<span class="Special"> <- </span>equal curr, <span class="Constant">10/newline</span>
<span class="muControl">break-if</span> at-end-of-line?
end<span class="Special"> <- </span>next-duplex end
<span class="muControl">loop</span>
<span class="Delimiter">}</span>
<span class="Comment"># snip it out</span>
start-next:address:address:duplex-list<span class="Special"> <- </span>get-address *start, <span class="Constant">next:offset</span>
*start-next<span class="Special"> <- </span>copy end
<span class="Delimiter">{</span>
<span class="muControl">break-unless</span> end
end-prev:address:address:duplex-list<span class="Special"> <- </span>get-address *end, <span class="Constant">prev:offset</span>
*end-prev<span class="Special"> <- </span>copy start
<span class="Delimiter">}</span>
]
<span class="muScenario">scenario</span> editor-deletes-to-end-of-line-with-ctrl-k-2 [
assume-screen <span class="Constant">10/width</span>, <span class="Constant">5/height</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[123</span>
<span class="Constant">456]</span>
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">10/right</span>
<span class="Comment"># start on second line (no newline after), press ctrl-k</span>
assume-console [
left-click <span class="Constant">2</span>, <span class="Constant">1</span>
type <span class="Constant">[k]</span> <span class="Comment"># ctrl-k</span>
]
<span class="Constant">3</span>:event/ctrl-k<span class="Special"> <- </span>merge <span class="Constant">0/text</span>, <span class="Constant">11/ctrl-k</span>, <span class="Constant">0/dummy</span>, <span class="Constant">0/dummy</span>
replace-in-console <span class="Constant">107/k</span>, <span class="Constant">3</span>:event/ctrl-k
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
]
<span class="Comment"># cursor deletes to end of line</span>
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .123 .</span>
<span class="Constant"> .4 .</span>
<span class="Constant"> . .</span>
]
]
<span class="muScenario">scenario</span> editor-deletes-to-end-of-line-with-ctrl-k-3 [
assume-screen <span class="Constant">10/width</span>, <span class="Constant">5/height</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[123</span>
<span class="Constant">456]</span>
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">10/right</span>
<span class="Comment"># start at end of line</span>
assume-console [
left-click <span class="Constant">1</span>, <span class="Constant">2</span>
type <span class="Constant">[k]</span> <span class="Comment"># ctrl-k</span>
]
<span class="Constant">3</span>:event/ctrl-k<span class="Special"> <- </span>merge <span class="Constant">0/text</span>, <span class="Constant">11/ctrl-k</span>, <span class="Constant">0/dummy</span>, <span class="Constant">0/dummy</span>
replace-in-console <span class="Constant">107/k</span>, <span class="Constant">3</span>:event/ctrl-k
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
]
<span class="Comment"># cursor deletes to end of line</span>
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .12 .</span>
<span class="Constant"> .456 .</span>
<span class="Constant"> . .</span>
]
]
<span class="muScenario">scenario</span> editor-deletes-to-end-of-line-with-ctrl-k-4 [
assume-screen <span class="Constant">10/width</span>, <span class="Constant">5/height</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[123</span>
<span class="Constant">456]</span>
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">10/right</span>
<span class="Comment"># start past end of line</span>
assume-console [
left-click <span class="Constant">1</span>, <span class="Constant">3</span>
type <span class="Constant">[k]</span> <span class="Comment"># ctrl-k</span>
]
<span class="Constant">3</span>:event/ctrl-k<span class="Special"> <- </span>merge <span class="Constant">0/text</span>, <span class="Constant">11/ctrl-k</span>, <span class="Constant">0/dummy</span>, <span class="Constant">0/dummy</span>
replace-in-console <span class="Constant">107/k</span>, <span class="Constant">3</span>:event/ctrl-k
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
]
<span class="Comment"># cursor deletes to end of line</span>
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .123 .</span>
<span class="Constant"> .456 .</span>
<span class="Constant"> . .</span>
]
]
<span class="muScenario">scenario</span> editor-deletes-to-end-of-line-with-ctrl-k-5 [
assume-screen <span class="Constant">10/width</span>, <span class="Constant">5/height</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[123</span>
<span class="Constant">456]</span>
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">10/right</span>
<span class="Comment"># start at end of text</span>
assume-console [
left-click <span class="Constant">2</span>, <span class="Constant">2</span>
type <span class="Constant">[k]</span> <span class="Comment"># ctrl-k</span>
]
<span class="Constant">3</span>:event/ctrl-k<span class="Special"> <- </span>merge <span class="Constant">0/text</span>, <span class="Constant">11/ctrl-k</span>, <span class="Constant">0/dummy</span>, <span class="Constant">0/dummy</span>
replace-in-console <span class="Constant">107/k</span>, <span class="Constant">3</span>:event/ctrl-k
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
]
<span class="Comment"># cursor deletes to end of line</span>
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .123 .</span>
<span class="Constant"> .45 .</span>
<span class="Constant"> . .</span>
]
]
<span class="muScenario">scenario</span> editor-deletes-to-end-of-line-with-ctrl-k-6 [
assume-screen <span class="Constant">10/width</span>, <span class="Constant">5/height</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[123</span>
<span class="Constant">456]</span>
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">10/right</span>
<span class="Comment"># start past end of text</span>
assume-console [
left-click <span class="Constant">2</span>, <span class="Constant">3</span>
type <span class="Constant">[k]</span> <span class="Comment"># ctrl-k</span>
]
<span class="Constant">3</span>:event/ctrl-k<span class="Special"> <- </span>merge <span class="Constant">0/text</span>, <span class="Constant">11/ctrl-k</span>, <span class="Constant">0/dummy</span>, <span class="Constant">0/dummy</span>
replace-in-console <span class="Constant">107/k</span>, <span class="Constant">3</span>:event/ctrl-k
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
]
<span class="Comment"># cursor deletes to end of line</span>
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .123 .</span>
<span class="Constant"> .456 .</span>
<span class="Constant"> . .</span>
]
]
<span class="Comment"># ctrl-f/page-down - render next page if it exists</span>
<span class="muScenario">scenario</span> editor-can-scroll [
assume-screen <span class="Constant">10/width</span>, <span class="Constant">4/height</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[a</span>
<span class="Constant">b</span>
<span class="Constant">c</span>
<span class="Constant">d]</span>
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">10/right</span>
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .a .</span>
<span class="Constant"> .b .</span>
<span class="Constant"> .c .</span>
]
<span class="Comment"># scroll down</span>
assume-console [
press <span class="Constant">65518</span> <span class="Comment"># page-down</span>
]
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
]
<span class="Comment"># screen shows next page</span>
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .c .</span>
<span class="Constant"> .d .</span>
<span class="Constant"> . .</span>
]
]
<span class="muRecipe">after</span> +handle-special-character [
<span class="Delimiter">{</span>
ctrl-f?:boolean<span class="Special"> <- </span>equal *c, <span class="Constant">6/ctrl-f</span>
<span class="muControl">break-unless</span> ctrl-f?
page-down editor
<span class="muControl">reply</span>
<span class="Delimiter">}</span>
]
<span class="muRecipe">after</span> +handle-special-key [
<span class="Delimiter">{</span>
page-down?:boolean<span class="Special"> <- </span>equal *k, <span class="Constant">65518/page-down</span>
<span class="muControl">break-unless</span> page-down?
page-down editor
<span class="muControl">reply</span>
<span class="Delimiter">}</span>
]
<span class="Comment"># Cache old pointers to top-of-page in a list as you scroll past them, so that</span>
<span class="Comment"># page-up later doesn't have to recompute them.</span>
<span class="Comment"># This only works because we can never ever have edits outside the current</span>
<span class="Comment"># page. Any edits outside the current page might invalidate pointers to old</span>
<span class="Comment"># pages.</span>
container editor-data [
previous-page:address:list:address:duplex-list:character
]
<span class="Comment"># page-down skips entire wrapped lines, so it can't scroll past lines</span>
<span class="Comment"># taking up the entire screen</span>
<span class="muRecipe">recipe</span> page-down [
<span class="Constant">local-scope</span>
editor:address:editor-data<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
<span class="Comment"># if editor contents don't overflow screen, do nothing</span>
bottom-of-screen:address:duplex-list<span class="Special"> <- </span>get *editor, <span class="Constant">bottom-of-screen:offset</span>
<span class="muControl">reply-unless</span> bottom-of-screen, editor/same-as-ingredient:<span class="Constant">0</span>
<span class="Comment"># if not, position cursor at final character</span>
before-cursor:address:address:duplex-list<span class="Special"> <- </span>get-address *editor, <span class="Constant">before-cursor:offset</span>
*before-cursor<span class="Special"> <- </span>prev-duplex bottom-of-screen
<span class="Comment"># keep one line in common with previous page</span>
<span class="Delimiter">{</span>
last:character<span class="Special"> <- </span>get **before-cursor, <span class="Constant">value:offset</span>
newline?:boolean<span class="Special"> <- </span>equal last, <span class="Constant">10/newline</span>
<span class="muControl">break-unless</span> newline?:boolean
*before-cursor<span class="Special"> <- </span>prev-duplex *before-cursor
<span class="Delimiter">}</span>
<span class="Comment"># save top-of-screen to previous-page list</span>
top-of-screen:address:address:duplex-list<span class="Special"> <- </span>get-address *editor, <span class="Constant">top-of-screen:offset</span>
previous-page:address:address:list:address:duplex-list:character<span class="Special"> <- </span>get-address *editor, <span class="Constant">previous-page:offset</span>
*previous-page<span class="Special"> <- </span>push *top-of-screen, *previous-page
<span class="Comment"># move cursor and top-of-screen to start of that line</span>
move-to-start-of-line editor
*top-of-screen<span class="Special"> <- </span>copy *before-cursor
<span class="muControl">reply</span> editor/same-as-ingredient:<span class="Constant">0</span>
]
<span class="muScenario">scenario</span> editor-does-not-scroll-past-end [
assume-screen <span class="Constant">10/width</span>, <span class="Constant">4/height</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[a</span>
<span class="Constant">b]</span>
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">10/right</span>
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .a .</span>
<span class="Constant"> .b .</span>
<span class="Constant"> . .</span>
]
<span class="Comment"># scroll down</span>
assume-console [
press <span class="Constant">65518</span> <span class="Comment"># page-down</span>
]
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
]
<span class="Comment"># screen remains unmodified</span>
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .a .</span>
<span class="Constant"> .b .</span>
<span class="Constant"> . .</span>
]
]
<span class="muScenario">scenario</span> editor-starts-next-page-at-start-of-wrapped-line [
<span class="Comment"># screen has 1 line for menu + 3 lines for text</span>
assume-screen <span class="Constant">10/width</span>, <span class="Constant">4/height</span>
<span class="Comment"># editor contains a long last line</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[a</span>
<span class="Constant">b</span>
<span class="Constant">cdefgh]</span>
<span class="Comment"># editor screen triggers wrap of last line</span>
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">4/right</span>
<span class="Comment"># some part of last line is not displayed</span>
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .a .</span>
<span class="Constant"> .b .</span>
<span class="Constant"> .cde↩ .</span>
]
<span class="Comment"># scroll down</span>
assume-console [
press <span class="Constant">65518</span> <span class="Comment"># page-down</span>
]
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
]
<span class="Comment"># screen shows entire wrapped line</span>
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .cde↩ .</span>
<span class="Constant"> .fgh .</span>
<span class="Constant"> . .</span>
]
]
<span class="muScenario">scenario</span> editor-starts-next-page-at-start-of-wrapped-line-2 [
<span class="Comment"># screen has 1 line for menu + 3 lines for text</span>
assume-screen <span class="Constant">10/width</span>, <span class="Constant">4/height</span>
<span class="Comment"># editor contains a very long line that occupies last two lines of screen</span>
<span class="Comment"># and still has something left over</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[a</span>
<span class="Constant">bcdefgh]</span>
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">4/right</span>
<span class="Comment"># some part of last line is not displayed</span>
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .a .</span>
<span class="Constant"> .bcd↩ .</span>
<span class="Constant"> .efg↩ .</span>
]
<span class="Comment"># scroll down</span>
assume-console [
press <span class="Constant">65518</span> <span class="Comment"># page-down</span>
]
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
]
<span class="Comment"># screen shows entire wrapped line</span>
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .bcd↩ .</span>
<span class="Constant"> .efg↩ .</span>
<span class="Constant"> .h .</span>
]
]
<span class="Comment"># ctrl-b/page-up - render previous page if it exists</span>
<span class="muScenario">scenario</span> editor-can-scroll-up [
assume-screen <span class="Constant">10/width</span>, <span class="Constant">4/height</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[a</span>
<span class="Constant">b</span>
<span class="Constant">c</span>
<span class="Constant">d]</span>
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">10/right</span>
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .a .</span>
<span class="Constant"> .b .</span>
<span class="Constant"> .c .</span>
]
<span class="Comment"># scroll down</span>
assume-console [
press <span class="Constant">65518</span> <span class="Comment"># page-down</span>
]
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
]
<span class="Comment"># screen shows next page</span>
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .c .</span>
<span class="Constant"> .d .</span>
<span class="Constant"> . .</span>
]
<span class="Comment"># scroll back up</span>
assume-console [
press <span class="Constant">65519</span> <span class="Comment"># page-up</span>
]
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
]
<span class="Comment"># screen shows original page again</span>
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .a .</span>
<span class="Constant"> .b .</span>
<span class="Constant"> .c .</span>
]
]
<span class="muRecipe">after</span> +handle-special-character [
<span class="Delimiter">{</span>
ctrl-b?:boolean<span class="Special"> <- </span>equal *c, <span class="Constant">2/ctrl-f</span>
<span class="muControl">break-unless</span> ctrl-b?
page-up editor
<span class="muControl">reply</span>
<span class="Delimiter">}</span>
]
<span class="muRecipe">after</span> +handle-special-key [
<span class="Delimiter">{</span>
page-up?:boolean<span class="Special"> <- </span>equal *k, <span class="Constant">65519/page-up</span>
<span class="muControl">break-unless</span> page-up?
page-up editor
<span class="muControl">reply</span>
<span class="Delimiter">}</span>
]
<span class="muRecipe">recipe</span> page-up [
<span class="Constant">local-scope</span>
editor:address:editor-data<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
previous-page:address:address:list:address:duplex-list:character<span class="Special"> <- </span>get-address *editor, <span class="Constant">previous-page:offset</span>
<span class="muControl">reply-unless</span> *previous-page, editor/same-as-ingredient:<span class="Constant">0</span>
top-of-screen:address:address:duplex-list<span class="Special"> <- </span>get-address *editor, <span class="Constant">top-of-screen:offset</span>
*top-of-screen<span class="Special"> <- </span>first *previous-page
*previous-page<span class="Special"> <- </span>rest *previous-page
<span class="muControl">reply</span> editor/same-as-ingredient:<span class="Constant">0</span>
]
<span class="muScenario">scenario</span> editor-can-scroll-up-multiple-pages [
<span class="Comment"># screen has 1 line for menu + 3 lines</span>
assume-screen <span class="Constant">10/width</span>, <span class="Constant">4/height</span>
<span class="Comment"># initialize editor with 8 lines</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[a</span>
<span class="Constant">b</span>
<span class="Constant">c</span>
<span class="Constant">d</span>
<span class="Constant">e</span>
<span class="Constant">f</span>
<span class="Constant">g</span>
<span class="Constant">h]</span>
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">10/right</span>
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .a .</span>
<span class="Constant"> .b .</span>
<span class="Constant"> .c .</span>
]
<span class="Comment"># scroll down two pages</span>
assume-console [
press <span class="Constant">65518</span> <span class="Comment"># page-down</span>
press <span class="Constant">65518</span> <span class="Comment"># page-down</span>
]
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
]
<span class="Comment"># screen shows third page</span>
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .e .</span>
<span class="Constant"> .f .</span>
<span class="Constant"> .g .</span>
]
<span class="Comment"># scroll up</span>
assume-console [
press <span class="Constant">65519</span> <span class="Comment"># page-up</span>
]
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
]
<span class="Comment"># screen shows second page</span>
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .c .</span>
<span class="Constant"> .d .</span>
<span class="Constant"> .e .</span>
]
<span class="Comment"># scroll up again</span>
assume-console [
press <span class="Constant">65519</span> <span class="Comment"># page-up</span>
]
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
]
<span class="Comment"># screen shows original page again</span>
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .a .</span>
<span class="Constant"> .b .</span>
<span class="Constant"> .c .</span>
]
]
<span class="Comment"># cursor-down can scroll if necessary</span>
<span class="muScenario">scenario</span> editor-can-scroll-down-using-arrow-keys [
<span class="Comment"># screen has 1 line for menu + 3 lines</span>
assume-screen <span class="Constant">10/width</span>, <span class="Constant">4/height</span>
<span class="Comment"># initialize editor with >3 lines</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[a</span>
<span class="Constant">b</span>
<span class="Constant">c</span>
<span class="Constant">d]</span>
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">10/right</span>
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .a .</span>
<span class="Constant"> .b .</span>
<span class="Constant"> .c .</span>
]
<span class="Comment"># position cursor at last line, then try to move further down</span>
assume-console [
left-click <span class="Constant">3</span>, <span class="Constant">0</span>
press <span class="Constant">65516</span> <span class="Comment"># down-arrow</span>
]
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
]
<span class="Comment"># screen slides by one line</span>
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .b .</span>
<span class="Constant"> .c .</span>
<span class="Constant"> .d .</span>
]
]
<span class="muRecipe">after</span> +scroll-down [
<span class="CommentedCode">#? $print [scroll down], 10/newline #? 1</span>
top-of-screen:address:address:duplex-list<span class="Special"> <- </span>get-address *editor, <span class="Constant">top-of-screen:offset</span>
left:number<span class="Special"> <- </span>get *editor, <span class="Constant">left:offset</span>
right:number<span class="Special"> <- </span>get *editor, <span class="Constant">right:offset</span>
max:number<span class="Special"> <- </span>subtract right, left
*top-of-screen<span class="Special"> <- </span>start-of-next-line *top-of-screen, max
]
<span class="Comment"># takes a pointer into the doubly-linked list, scans ahead at most 'max'</span>
<span class="Comment"># positions until just past the next newline</span>
<span class="muRecipe">recipe</span> start-of-next-line [
<span class="Constant">local-scope</span>
original:address:duplex-list<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
max:number<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
count:number<span class="Special"> <- </span>copy <span class="Constant">0</span>
curr:address:duplex-list<span class="Special"> <- </span>copy original
<span class="Delimiter">{</span>
<span class="muControl">reply-unless</span> curr, original
done?:boolean<span class="Special"> <- </span>greater-or-equal count, max
<span class="muControl">break-if</span> done?
c:character<span class="Special"> <- </span>get *curr, <span class="Constant">value:offset</span>
at-newline?:boolean<span class="Special"> <- </span>equal c, <span class="Constant">10/newline</span>
<span class="muControl">break-if</span> at-newline?
curr<span class="Special"> <- </span>next-duplex curr
count<span class="Special"> <- </span>add count, <span class="Constant">1</span>
<span class="muControl">loop</span>
<span class="Delimiter">}</span>
<span class="muControl">reply-unless</span> curr, original
<span class="muControl">reply</span> curr
]
<span class="muScenario">scenario</span> editor-scrolls-down-past-wrapped-line-using-arrow-keys [
<span class="Comment"># screen has 1 line for menu + 3 lines</span>
assume-screen <span class="Constant">10/width</span>, <span class="Constant">4/height</span>
<span class="Comment"># initialize editor with a long, wrapped line and more than a screen of</span>
<span class="Comment"># other lines</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[abcdef</span>
<span class="Constant">g</span>
<span class="Constant">h</span>
<span class="Constant">i]</span>
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">5/right</span>
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .abcd↩ .</span>
<span class="Constant"> .ef .</span>
<span class="Constant"> .g .</span>
]
<span class="Comment"># position cursor at last line, then try to move further down</span>
assume-console [
left-click <span class="Constant">3</span>, <span class="Constant">0</span>
press <span class="Constant">65516</span> <span class="Comment"># down-arrow</span>
]
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
]
<span class="Comment"># screen shows partial wrapped line</span>
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .ef .</span>
<span class="Constant"> .g .</span>
<span class="Constant"> .h .</span>
]
]
<span class="muScenario">scenario</span> editor-scrolls-down-past-wrapped-line-using-arrow-keys-2 [
<span class="Comment"># screen has 1 line for menu + 3 lines</span>
assume-screen <span class="Constant">10/width</span>, <span class="Constant">4/height</span>
<span class="Comment"># editor starts with a long line wrapping twice</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[abcdefghij</span>
<span class="Constant">k</span>
<span class="Constant">l</span>
<span class="Constant">m]</span>
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">5/right</span>
<span class="Comment"># position cursor at last line, then try to move further down</span>
assume-console [
left-click <span class="Constant">3</span>, <span class="Constant">0</span>
press <span class="Constant">65516</span> <span class="Comment"># down-arrow</span>
]
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
]
<span class="Comment"># screen shows partial wrapped line containing a wrap icon</span>
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .efgh↩ .</span>
<span class="Constant"> .ij .</span>
<span class="Constant"> .k .</span>
]
<span class="Comment"># scroll down again</span>
assume-console [
press <span class="Constant">65516</span> <span class="Comment"># down-arrow</span>
]
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
]
<span class="Comment"># screen shows partial wrapped line</span>
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .ij .</span>
<span class="Constant"> .k .</span>
<span class="Constant"> .l .</span>
]
]
<span class="muScenario">scenario</span> editor-scrolls-down-when-line-wraps [
<span class="Comment"># screen has 1 line for menu + 3 lines</span>
assume-screen <span class="Constant">5/width</span>, <span class="Constant">4/height</span>
<span class="Comment"># editor contains a long line in the third line</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[a</span>
<span class="Constant">b</span>
<span class="Constant">cdef]</span>
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">5/right</span>
<span class="Comment"># position cursor at end, type a character</span>
assume-console [
left-click <span class="Constant">3</span>, <span class="Constant">4</span>
type <span class="Constant">[g]</span>
]
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
<span class="Constant">3</span>:number<span class="Special"> <- </span>get *<span class="Constant">2</span>:address:editor-data, <span class="Constant">cursor-row:offset</span>
<span class="Constant">4</span>:number<span class="Special"> <- </span>get *<span class="Constant">2</span>:address:editor-data, <span class="Constant">cursor-column:offset</span>
]
<span class="Comment"># screen scrolls</span>
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .b .</span>
<span class="Constant"> .cdef↩.</span>
<span class="Constant"> .g .</span>
]
memory-should-contain [
<span class="Constant">3</span><span class="Special"> <- </span><span class="Constant">3</span>
<span class="Constant">4</span><span class="Special"> <- </span><span class="Constant">1</span>
]
]
<span class="muScenario">scenario</span> editor-scrolls-down-on-newline [
assume-screen <span class="Constant">5/width</span>, <span class="Constant">4/height</span>
<span class="Comment"># position cursor after last line and type newline</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[a</span>
<span class="Constant">b</span>
<span class="Constant">c]</span>
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">5/right</span>
assume-console [
left-click <span class="Constant">3</span>, <span class="Constant">4</span>
type [
]
]
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
<span class="Constant">3</span>:number<span class="Special"> <- </span>get *<span class="Constant">2</span>:address:editor-data, <span class="Constant">cursor-row:offset</span>
<span class="Constant">4</span>:number<span class="Special"> <- </span>get *<span class="Constant">2</span>:address:editor-data, <span class="Constant">cursor-column:offset</span>
]
<span class="Comment"># screen scrolls</span>
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .b .</span>
<span class="Constant"> .c .</span>
<span class="Constant"> . .</span>
]
memory-should-contain [
<span class="Constant">3</span><span class="Special"> <- </span><span class="Constant">3</span>
<span class="Constant">4</span><span class="Special"> <- </span><span class="Constant">0</span>
]
]
<span class="muScenario">scenario</span> editor-scrolls-down-on-right-arrow [
<span class="Comment"># screen has 1 line for menu + 3 lines</span>
assume-screen <span class="Constant">5/width</span>, <span class="Constant">4/height</span>
<span class="Comment"># editor contains a wrapped line</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[a</span>
<span class="Constant">b</span>
<span class="Constant">cdefgh]</span>
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">5/right</span>
<span class="Comment"># position cursor at end of screen and try to move right</span>
assume-console [
left-click <span class="Constant">3</span>, <span class="Constant">3</span>
press <span class="Constant">65514</span> <span class="Comment"># right arrow</span>
]
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
<span class="Constant">3</span>:number<span class="Special"> <- </span>get *<span class="Constant">2</span>:address:editor-data, <span class="Constant">cursor-row:offset</span>
<span class="Constant">4</span>:number<span class="Special"> <- </span>get *<span class="Constant">2</span>:address:editor-data, <span class="Constant">cursor-column:offset</span>
]
<span class="Comment"># screen scrolls</span>
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .b .</span>
<span class="Constant"> .cdef↩.</span>
<span class="Constant"> .gh .</span>
]
memory-should-contain [
<span class="Constant">3</span><span class="Special"> <- </span><span class="Constant">3</span>
<span class="Constant">4</span><span class="Special"> <- </span><span class="Constant">0</span>
]
]
<span class="muScenario">scenario</span> editor-scrolls-down-on-right-arrow-2 [
<span class="Comment"># screen has 1 line for menu + 3 lines</span>
assume-screen <span class="Constant">5/width</span>, <span class="Constant">4/height</span>
<span class="Comment"># editor contains more lines than can fit on screen</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[a</span>
<span class="Constant">b</span>
<span class="Constant">c</span>
<span class="Constant">d]</span>
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">5/right</span>
<span class="Comment"># position cursor at end of screen and try to move right</span>
assume-console [
left-click <span class="Constant">3</span>, <span class="Constant">3</span>
press <span class="Constant">65514</span> <span class="Comment"># right arrow</span>
]
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
<span class="Constant">3</span>:number<span class="Special"> <- </span>get *<span class="Constant">2</span>:address:editor-data, <span class="Constant">cursor-row:offset</span>
<span class="Constant">4</span>:number<span class="Special"> <- </span>get *<span class="Constant">2</span>:address:editor-data, <span class="Constant">cursor-column:offset</span>
]
<span class="Comment"># screen scrolls</span>
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .b .</span>
<span class="Constant"> .c .</span>
<span class="Constant"> .d .</span>
]
memory-should-contain [
<span class="Constant">3</span><span class="Special"> <- </span><span class="Constant">3</span>
<span class="Constant">4</span><span class="Special"> <- </span><span class="Constant">0</span>
]
]
<span class="Comment"># cursor-up can scroll if necessary</span>
<span class="muScenario">scenario</span> editor-can-scroll-up-using-arrow-keys [
<span class="Comment"># screen has 1 line for menu + 3 lines</span>
assume-screen <span class="Constant">10/width</span>, <span class="Constant">4/height</span>
<span class="Comment"># initialize editor with >3 lines</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[a</span>
<span class="Constant">b</span>
<span class="Constant">c</span>
<span class="Constant">d]</span>
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">10/right</span>
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .a .</span>
<span class="Constant"> .b .</span>
<span class="Constant"> .c .</span>
]
<span class="Comment"># position cursor at top of second page, then try to move up</span>
assume-console [
press <span class="Constant">65518</span> <span class="Comment"># page-down</span>
press <span class="Constant">65517</span> <span class="Comment"># up-arrow</span>
]
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
]
<span class="Comment"># screen slides by one line</span>
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .b .</span>
<span class="Constant"> .c .</span>
<span class="Constant"> .d .</span>
]
]
<span class="muRecipe">after</span> +scroll-up [
<span class="CommentedCode">#? $print [scroll up], 10/newline #? 1</span>
top-of-screen:address:address:duplex-list<span class="Special"> <- </span>get-address *editor, <span class="Constant">top-of-screen:offset</span>
left:number<span class="Special"> <- </span>get *editor, <span class="Constant">left:offset</span>
right:number<span class="Special"> <- </span>get *editor, <span class="Constant">right:offset</span>
max:number<span class="Special"> <- </span>subtract right, left
*top-of-screen<span class="Special"> <- </span>start-of-previous-line *top-of-screen, max
]
<span class="Comment"># takes a pointer into the doubly-linked list, scans back at most 'max'</span>
<span class="Comment"># positions to previous newline. Returns original pointer if falls off edge of</span>
<span class="Comment"># list.</span>
<span class="muRecipe">recipe</span> start-of-previous-line [
<span class="Constant">local-scope</span>
original:address:duplex-list<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
max:number<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
count:number<span class="Special"> <- </span>copy <span class="Constant">0</span>
curr:address:duplex-list<span class="Special"> <- </span>copy original
<span class="muControl">reply-unless</span> curr, original
<span class="Comment"># if top is at start of line, don't count the previous newline</span>
<span class="Delimiter">{</span>
c:character<span class="Special"> <- </span>get *curr, <span class="Constant">value:offset</span>
at-newline?:boolean<span class="Special"> <- </span>equal c, <span class="Constant">10/newline</span>
<span class="muControl">break-unless</span> at-newline?
max<span class="Special"> <- </span>subtract max, <span class="Constant">1</span>
<span class="Delimiter">}</span>
<span class="Comment"># skip newline at original</span>
curr<span class="Special"> <- </span>prev-duplex curr
count<span class="Special"> <- </span>add count, <span class="Constant">1</span>
<span class="Comment"># now skip before until previous newline</span>
<span class="Delimiter">{</span>
<span class="muControl">reply-unless</span> curr, original
done?:boolean<span class="Special"> <- </span>greater-or-equal count, max
<span class="muControl">break-if</span> done?
c:character<span class="Special"> <- </span>get *curr, <span class="Constant">value:offset</span>
at-newline?:boolean<span class="Special"> <- </span>equal c, <span class="Constant">10/newline</span>
<span class="muControl">break-if</span> at-newline?
curr<span class="Special"> <- </span>prev-duplex curr
count<span class="Special"> <- </span>add count, <span class="Constant">1</span>
<span class="muControl">loop</span>
<span class="Delimiter">}</span>
<span class="muControl">reply-unless</span> curr, original
<span class="muControl">reply</span> curr
]
<span class="muScenario">scenario</span> editor-scrolls-up-past-wrapped-line-using-arrow-keys [
<span class="Comment"># screen has 1 line for menu + 3 lines</span>
assume-screen <span class="Constant">10/width</span>, <span class="Constant">4/height</span>
<span class="Comment"># initialize editor with a long, wrapped line and more than a screen of</span>
<span class="Comment"># other lines</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[abcdef</span>
<span class="Constant">g</span>
<span class="Constant">h</span>
<span class="Constant">i]</span>
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">5/right</span>
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .abcd↩ .</span>
<span class="Constant"> .ef .</span>
<span class="Constant"> .g .</span>
]
<span class="Comment"># position cursor at top of second page, just below wrapped line</span>
assume-console [
press <span class="Constant">65518</span> <span class="Comment"># page-down</span>
]
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
]
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .g .</span>
<span class="Constant"> .h .</span>
<span class="Constant"> .i .</span>
]
<span class="Comment"># now move up one line</span>
assume-console [
press <span class="Constant">65517</span> <span class="Comment"># up-arrow</span>
]
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
]
<span class="Comment"># screen shows partial wrapped line</span>
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .ef .</span>
<span class="Constant"> .g .</span>
<span class="Constant"> .h .</span>
]
]
<span class="muScenario">scenario</span> editor-scrolls-up-past-wrapped-line-using-arrow-keys-2 [
<span class="Comment"># screen has 1 line for menu + 4 lines</span>
assume-screen <span class="Constant">10/width</span>, <span class="Constant">5/height</span>
<span class="Comment"># editor starts with a long line wrapping twice, occupying 3 of the 4 lines</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[abcdefghij</span>
<span class="Constant">k</span>
<span class="Constant">l</span>
<span class="Constant">m]</span>
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">5/right</span>
<span class="Comment"># position cursor at top of second page</span>
assume-console [
press <span class="Constant">65518</span> <span class="Comment"># page-down</span>
]
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
]
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .k .</span>
<span class="Constant"> .l .</span>
<span class="Constant"> .m .</span>
<span class="Constant"> . .</span>
]
<span class="Comment"># move up one line</span>
assume-console [
press <span class="Constant">65517</span> <span class="Comment"># up-arrow</span>
]
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
]
<span class="Comment"># screen shows partial wrapped line</span>
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .ij .</span>
<span class="Constant"> .k .</span>
<span class="Constant"> .l .</span>
<span class="Constant"> .m .</span>
]
<span class="Comment"># move up a second line</span>
assume-console [
press <span class="Constant">65517</span> <span class="Comment"># up-arrow</span>
]
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
]
<span class="Comment"># screen shows partial wrapped line</span>
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .efgh↩ .</span>
<span class="Constant"> .ij .</span>
<span class="Constant"> .k .</span>
<span class="Constant"> .l .</span>
]
<span class="Comment"># move up a third line</span>
assume-console [
press <span class="Constant">65517</span> <span class="Comment"># up-arrow</span>
]
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
]
<span class="Comment"># screen shows partial wrapped line</span>
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .abcd↩ .</span>
<span class="Constant"> .efgh↩ .</span>
<span class="Constant"> .ij .</span>
<span class="Constant"> .k .</span>
]
]
<span class="muScenario">scenario</span> editor-scrolls-up-on-left-arrow [
<span class="Comment"># screen has 1 line for menu + 3 lines</span>
assume-screen <span class="Constant">5/width</span>, <span class="Constant">4/height</span>
<span class="Comment"># editor contains >3 lines</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[a</span>
<span class="Constant">b</span>
<span class="Constant">c</span>
<span class="Constant">d</span>
<span class="Constant">e]</span>
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">5/right</span>
<span class="Comment"># position cursor at top of second page</span>
assume-console [
press <span class="Constant">65518</span> <span class="Comment"># page-down</span>
]
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
]
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .c .</span>
<span class="Constant"> .d .</span>
<span class="Constant"> .e .</span>
]
<span class="Comment"># now try to move left</span>
assume-console [
press <span class="Constant">65515</span> <span class="Comment"># left arrow</span>
]
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
<span class="Constant">3</span>:number<span class="Special"> <- </span>get *<span class="Constant">2</span>:address:editor-data, <span class="Constant">cursor-row:offset</span>
<span class="Constant">4</span>:number<span class="Special"> <- </span>get *<span class="Constant">2</span>:address:editor-data, <span class="Constant">cursor-column:offset</span>
]
<span class="Comment"># screen scrolls</span>
screen-should-contain [
<span class="Constant"> . .</span>
<span class="Constant"> .b .</span>
<span class="Constant"> .c .</span>
<span class="Constant"> .d .</span>
]
memory-should-contain [
<span class="Constant">3</span><span class="Special"> <- </span><span class="Constant">1</span>
<span class="Constant">4</span><span class="Special"> <- </span><span class="Constant">1</span>
]
]
<span class="SalientComment">## putting the environment together out of editors</span>
container programming-environment-data [
recipes:address:editor-data
recipe-warnings:address:array:character
current-sandbox:address:editor-data
sandbox:address:sandbox-data <span class="Comment"># list of sandboxes, from top to bottom</span>
sandbox-in-focus?:boolean <span class="Comment"># false => cursor in recipes; true => cursor in current-sandbox</span>
]
<span class="muRecipe">recipe</span> new-programming-environment [
<span class="Constant">local-scope</span>
screen:address<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
initial-recipe-contents:address:array:character<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
initial-sandbox-contents:address:array:character<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
width:number<span class="Special"> <- </span>screen-width screen
height:number<span class="Special"> <- </span>screen-height screen
<span class="Comment"># top menu</span>
result:address:programming-environment-data<span class="Special"> <- </span>new <span class="Constant">programming-environment-data:type</span>
draw-horizontal screen, <span class="Constant">0</span>, <span class="Constant">0/left</span>, width, <span class="Constant">32/space</span>, <span class="Constant">0/black</span>, <span class="Constant">238/grey</span>
button-start:number<span class="Special"> <- </span>subtract width, <span class="Constant">20</span>
button-on-screen?:boolean<span class="Special"> <- </span>greater-or-equal button-start, <span class="Constant">0</span>
assert button-on-screen?, <span class="Constant">[screen too narrow for menu]</span>
move-cursor screen, <span class="Constant">0/row</span>, button-start
run-button:address:array:character<span class="Special"> <- </span>new <span class="Constant">[ run (F4) ]</span>
print-string screen, run-button, <span class="Constant">255/white</span>, <span class="Constant">161/reddish</span>
<span class="Comment"># dotted line down the middle</span>
divider:number, _<span class="Special"> <- </span>divide-with-remainder width, <span class="Constant">2</span>
draw-vertical screen, divider, <span class="Constant">1/top</span>, height, <span class="Constant">9482/vertical-dotted</span>
<span class="Comment"># recipe editor on the left</span>
recipes:address:address:editor-data<span class="Special"> <- </span>get-address *result, <span class="Constant">recipes:offset</span>
*recipes<span class="Special"> <- </span>new-editor initial-recipe-contents, screen, <span class="Constant">0/left</span>, divider/right
<span class="Comment"># sandbox editor on the right</span>
new-left:number<span class="Special"> <- </span>add divider, <span class="Constant">1</span>
new-right:number<span class="Special"> <- </span>add new-left, <span class="Constant">5</span>
current-sandbox:address:address:editor-data<span class="Special"> <- </span>get-address *result, <span class="Constant">current-sandbox:offset</span>
*current-sandbox<span class="Special"> <- </span>new-editor initial-sandbox-contents, screen, new-left, width/right
screen<span class="Special"> <- </span>render-all screen, result
<span class="muControl">reply</span> result
]
<span class="muRecipe">recipe</span> event-loop [
<span class="Constant">local-scope</span>
screen:address<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
console:address<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
env:address:programming-environment-data<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
recipes:address:editor-data<span class="Special"> <- </span>get *env, <span class="Constant">recipes:offset</span>
current-sandbox:address:editor-data<span class="Special"> <- </span>get *env, <span class="Constant">current-sandbox:offset</span>
sandbox-in-focus?:address:boolean<span class="Special"> <- </span>get-address *env, <span class="Constant">sandbox-in-focus?:offset</span>
<span class="Delimiter">{</span>
<span class="Comment"># looping over each (keyboard or touch) event as it occurs</span>
<span class="Constant"> +next-event</span>
e:event, console, found?:boolean, quit?:boolean<span class="Special"> <- </span>read-event console
<span class="muControl">loop-unless</span> found?
<span class="muControl">break-if</span> quit? <span class="Comment"># only in tests</span>
trace <span class="Constant">[app]</span>, <span class="Constant">[next-event]</span>
<span class="Comment"># check for global events that will trigger regardless of which editor has focus</span>
<span class="Delimiter">{</span>
k:address:number<span class="Special"> <- </span>maybe-convert e:event, <span class="Constant">keycode:variant</span>
<span class="muControl">break-unless</span> k
<span class="Constant"> +global-keypress</span>
<span class="Delimiter">}</span>
<span class="Delimiter">{</span>
c:address:character<span class="Special"> <- </span>maybe-convert e:event, <span class="Constant">text:variant</span>
<span class="muControl">break-unless</span> c
<span class="Constant"> +global-type</span>
<span class="Comment"># ctrl-n? - switch focus</span>
<span class="Delimiter">{</span>
ctrl-n?:boolean<span class="Special"> <- </span>equal *c, <span class="Constant">14/ctrl-n</span>
<span class="muControl">break-unless</span> ctrl-n?
*sandbox-in-focus?<span class="Special"> <- </span>not *sandbox-in-focus?
update-cursor screen, recipes, current-sandbox, *sandbox-in-focus?
show-screen screen
<span class="muControl">loop</span> <span class="Constant">+next-event:label</span>
<span class="Delimiter">}</span>
<span class="Delimiter">}</span>
<span class="Comment"># 'touch' event - send to both sides, see what picks it up</span>
<span class="Delimiter">{</span>
t:address:touch-event<span class="Special"> <- </span>maybe-convert e:event, <span class="Constant">touch:variant</span>
<span class="muControl">break-unless</span> t
<span class="Comment"># ignore all but 'left-click' events for now</span>
<span class="Comment"># todo: test this</span>
touch-type:number<span class="Special"> <- </span>get *t, <span class="Constant">type:offset</span>
is-left-click?:boolean<span class="Special"> <- </span>equal touch-type, <span class="Constant">65513/mouse-left</span>
<span class="muControl">loop-unless</span> is-left-click?, <span class="Constant">+next-event:label</span>
<span class="Comment"># later exceptions for non-editor touches will go here</span>
<span class="Constant"> +global-touch</span>
<span class="Comment"># send to both editors</span>
_<span class="Special"> <- </span>move-cursor-in-editor screen, recipes, *t
*sandbox-in-focus?<span class="Special"> <- </span>move-cursor-in-editor screen, current-sandbox, *t
render-minimal screen, env
<span class="muControl">loop</span> <span class="Constant">+next-event:label</span>
<span class="Delimiter">}</span>
<span class="Comment"># if it's not global and not a touch event, send to appropriate editor</span>
<span class="Delimiter">{</span>
<span class="Delimiter">{</span>
<span class="muControl">break-if</span> *sandbox-in-focus?
handle-keyboard-event screen, console, recipes, e:event
<span class="Delimiter">}</span>
<span class="Delimiter">{</span>
<span class="muControl">break-unless</span> *sandbox-in-focus?
handle-keyboard-event screen, console, current-sandbox, e:event
<span class="Delimiter">}</span>
<span class="Comment"># optimization: refresh screen only if no more events</span>
<span class="Comment"># todo: test this</span>
more-events?:boolean<span class="Special"> <- </span>has-more-events? console
<span class="muControl">break-if</span> more-events?
render-minimal screen, env
<span class="Delimiter">}</span>
<span class="muControl">loop</span>
<span class="Delimiter">}</span>
]
<span class="muScenario">scenario</span> point-at-multiple-editors [
<span class="Constant"> $close-trace</span>
assume-screen <span class="Constant">30/width</span>, <span class="Constant">5/height</span>
<span class="Comment"># initialize both halves of screen</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[abc]</span>
<span class="Constant">2</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[def]</span>
<span class="Constant">3</span>:address:programming-environment-data<span class="Special"> <- </span>new-programming-environment screen:address, <span class="Constant">1</span>:address:array:character, <span class="Constant">2</span>:address:array:character
<span class="Comment"># focus on both sides</span>
assume-console [
left-click <span class="Constant">1</span>, <span class="Constant">1</span>
left-click <span class="Constant">1</span>, <span class="Constant">17</span>
]
<span class="Comment"># check cursor column in each</span>
run [
event-loop screen:address, console:address, <span class="Constant">3</span>:address:programming-environment-data
<span class="Constant">4</span>:address:editor-data<span class="Special"> <- </span>get *<span class="Constant">3</span>:address:programming-environment-data, <span class="Constant">recipes:offset</span>
<span class="Constant">5</span>:number<span class="Special"> <- </span>get *<span class="Constant">4</span>:address:editor-data, <span class="Constant">cursor-column:offset</span>
<span class="Constant">6</span>:address:editor-data<span class="Special"> <- </span>get *<span class="Constant">3</span>:address:programming-environment-data, <span class="Constant">current-sandbox:offset</span>
<span class="Constant">7</span>:number<span class="Special"> <- </span>get *<span class="Constant">6</span>:address:editor-data, <span class="Constant">cursor-column:offset</span>
]
memory-should-contain [
<span class="Constant">5</span><span class="Special"> <- </span><span class="Constant">1</span>
<span class="Constant">7</span><span class="Special"> <- </span><span class="Constant">17</span>
]
]
<span class="muScenario">scenario</span> edit-multiple-editors [
<span class="Constant"> $close-trace</span>
assume-screen <span class="Constant">30/width</span>, <span class="Constant">5/height</span>
<span class="Comment"># initialize both halves of screen</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[abc]</span>
<span class="Constant">2</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[def]</span>
<span class="Constant">3</span>:address:programming-environment-data<span class="Special"> <- </span>new-programming-environment screen:address, <span class="Constant">1</span>:address:array:character, <span class="Constant">2</span>:address:array:character
<span class="Comment"># type one letter in each of them</span>
assume-console [
left-click <span class="Constant">1</span>, <span class="Constant">1</span>
type <span class="Constant">[0]</span>
left-click <span class="Constant">1</span>, <span class="Constant">17</span>
type <span class="Constant">[1]</span>
]
run [
event-loop screen:address, console:address, <span class="Constant">3</span>:address:programming-environment-data
<span class="Constant">4</span>:address:editor-data<span class="Special"> <- </span>get *<span class="Constant">3</span>:address:programming-environment-data, <span class="Constant">recipes:offset</span>
<span class="Constant">5</span>:number<span class="Special"> <- </span>get *<span class="Constant">4</span>:address:editor-data, <span class="Constant">cursor-column:offset</span>
<span class="Constant">6</span>:address:editor-data<span class="Special"> <- </span>get *<span class="Constant">3</span>:address:programming-environment-data, <span class="Constant">current-sandbox:offset</span>
<span class="Constant">7</span>:number<span class="Special"> <- </span>get *<span class="Constant">6</span>:address:editor-data, <span class="Constant">cursor-column:offset</span>
]
screen-should-contain [
<span class="Constant"> . run (F4) . # this line has a different background, but we don't test that yet</span>
<span class="Constant"> .a0bc ┊d1ef .</span>
<span class="Constant"> .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊━━━━━━━━━━━━━━.</span>
<span class="Constant"> . ┊ .</span>
]
memory-should-contain [
<span class="Constant">5</span><span class="Special"> <- </span><span class="Constant">2</span> <span class="Comment"># cursor column of recipe editor</span>
<span class="Constant">7</span><span class="Special"> <- </span><span class="Constant">18</span> <span class="Comment"># cursor column of sandbox editor</span>
]
<span class="Comment"># show the cursor at the right window</span>
run [
screen:address<span class="Special"> <- </span>print-character screen:address, <span class="Constant">9251/␣</span>
]
screen-should-contain [
<span class="Constant"> . run (F4) .</span>
<span class="Constant"> .a0bc ┊d1␣f .</span>
<span class="Constant"> .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊━━━━━━━━━━━━━━.</span>
<span class="Constant"> . ┊ .</span>
]
]
<span class="muScenario">scenario</span> multiple-editors-cover-only-their-own-areas [
<span class="Constant"> $close-trace</span>
assume-screen <span class="Constant">60/width</span>, <span class="Constant">10/height</span>
run [
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[abc]</span>
<span class="Constant">2</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[def]</span>
<span class="Constant">3</span>:address:programming-environment-data<span class="Special"> <- </span>new-programming-environment screen:address, <span class="Constant">1</span>:address:array:character, <span class="Constant">2</span>:address:array:character
]
<span class="Comment"># divider isn't messed up</span>
screen-should-contain [
<span class="Constant"> . run (F4) .</span>
<span class="Constant"> .abc ┊def .</span>
<span class="Constant"> .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.</span>
<span class="Constant"> . ┊ .</span>
<span class="Constant"> . ┊ .</span>
]
]
<span class="muScenario">scenario</span> editor-in-focus-keeps-cursor [
<span class="Constant"> $close-trace</span>
assume-screen <span class="Constant">30/width</span>, <span class="Constant">5/height</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[abc]</span>
<span class="Constant">2</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[def]</span>
<span class="Comment"># initialize programming environment and highlight cursor</span>
assume-console <span class="Constant">[]</span>
run [
<span class="Constant">3</span>:address:programming-environment-data<span class="Special"> <- </span>new-programming-environment screen:address, <span class="Constant">1</span>:address:array:character, <span class="Constant">2</span>:address:array:character
event-loop screen:address, console:address, <span class="Constant">3</span>:address:programming-environment-data
screen:address<span class="Special"> <- </span>print-character screen:address, <span class="Constant">9251/␣</span>
]
<span class="Comment"># is cursor at the right place?</span>
screen-should-contain [
<span class="Constant"> . run (F4) .</span>
<span class="Constant"> .␣bc ┊def .</span>
<span class="Constant"> .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊━━━━━━━━━━━━━━.</span>
<span class="Constant"> . ┊ .</span>
]
<span class="Comment"># now try typing a letter</span>
assume-console [
type <span class="Constant">[z]</span>
]
run [
<span class="Constant">3</span>:address:programming-environment-data<span class="Special"> <- </span>new-programming-environment screen:address, <span class="Constant">1</span>:address:array:character, <span class="Constant">2</span>:address:array:character
event-loop screen:address, console:address, <span class="Constant">3</span>:address:programming-environment-data
screen:address<span class="Special"> <- </span>print-character screen:address, <span class="Constant">9251/␣</span>
]
<span class="Comment"># cursor should still be right</span>
screen-should-contain [
<span class="Constant"> . run (F4) .</span>
<span class="Constant"> .z␣bc ┊def .</span>
<span class="Constant"> .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊━━━━━━━━━━━━━━.</span>
<span class="Constant"> . ┊ .</span>
]
]
<span class="muScenario">scenario</span> backspace-in-sandbox-editor-joins-lines [
<span class="Constant"> $close-trace</span>
assume-screen <span class="Constant">30/width</span>, <span class="Constant">5/height</span>
<span class="Comment"># initialize sandbox side with two lines</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[]</span>
<span class="Constant">2</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[abc</span>
<span class="Constant">def]</span>
<span class="Comment"># position cursor at start of second line and hit backspace</span>
assume-console [
left-click <span class="Constant">2</span>, <span class="Constant">16</span>
type <span class="Constant">[«]</span>
]
<span class="Constant">3</span>:event/backspace<span class="Special"> <- </span>merge <span class="Constant">0/text</span>, <span class="Constant">8/backspace</span>, <span class="Constant">0/dummy</span>, <span class="Constant">0/dummy</span>
replace-in-console <span class="Constant">171/«</span>, <span class="Constant">3</span>:event/backspace
run [
<span class="Constant">4</span>:address:programming-environment-data<span class="Special"> <- </span>new-programming-environment screen:address, <span class="Constant">1</span>:address:array:character, <span class="Constant">2</span>:address:array:character
event-loop screen:address, console:address, <span class="Constant">4</span>:address:programming-environment-data
screen:address<span class="Special"> <- </span>print-character screen:address, <span class="Constant">9251/␣</span>
]
<span class="Comment"># cursor moves to end of old line</span>
screen-should-contain [
<span class="Constant"> . run (F4) .</span>
<span class="Constant"> . ┊abc␣ef .</span>
<span class="Constant"> .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊━━━━━━━━━━━━━━.</span>
<span class="Constant"> . ┊ .</span>
]
]
<span class="muRecipe">recipe</span> render-all [
<span class="Constant">local-scope</span>
screen:address<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
env:address:programming-environment-data<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
screen<span class="Special"> <- </span>render-recipes screen, env
screen<span class="Special"> <- </span>render-sandbox-side screen, env
recipes:address:editor-data<span class="Special"> <- </span>get *env, <span class="Constant">recipes:offset</span>
current-sandbox:address:editor-data<span class="Special"> <- </span>get *env, <span class="Constant">current-sandbox:offset</span>
sandbox-in-focus?:boolean<span class="Special"> <- </span>get *env, <span class="Constant">sandbox-in-focus?:offset</span>
update-cursor screen, recipes, current-sandbox, sandbox-in-focus?
show-screen screen
<span class="muControl">reply</span> screen/same-as-ingredient:<span class="Constant">0</span>
]
<span class="muRecipe">recipe</span> render-minimal [
<span class="Constant">local-scope</span>
screen:address<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
env:address:programming-environment-data<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
recipes:address:editor-data<span class="Special"> <- </span>get *env, <span class="Constant">recipes:offset</span>
current-sandbox:address:editor-data<span class="Special"> <- </span>get *env, <span class="Constant">current-sandbox:offset</span>
sandbox-in-focus?:boolean<span class="Special"> <- </span>get *env, <span class="Constant">sandbox-in-focus?:offset</span>
<span class="Delimiter">{</span>
<span class="muControl">break-if</span> sandbox-in-focus?
screen<span class="Special"> <- </span>render-recipes screen, env
cursor-row:number<span class="Special"> <- </span>get *recipes, <span class="Constant">cursor-row:offset</span>
cursor-column:number<span class="Special"> <- </span>get *recipes, <span class="Constant">cursor-column:offset</span>
<span class="Delimiter">}</span>
<span class="Delimiter">{</span>
<span class="muControl">break-unless</span> sandbox-in-focus?
screen<span class="Special"> <- </span>render-sandbox-side screen, env
cursor-row:number<span class="Special"> <- </span>get *current-sandbox, <span class="Constant">cursor-row:offset</span>
cursor-column:number<span class="Special"> <- </span>get *current-sandbox, <span class="Constant">cursor-column:offset</span>
<span class="Delimiter">}</span>
move-cursor screen, cursor-row, cursor-column
show-screen screen
<span class="muControl">reply</span> screen/same-as-ingredient:<span class="Constant">0</span>
]
<span class="muRecipe">recipe</span> render-recipes [
<span class="Constant">local-scope</span>
screen:address<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
env:address:programming-environment-data<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
recipes:address:editor-data<span class="Special"> <- </span>get *env, <span class="Constant">recipes:offset</span>
<span class="Comment"># render recipes</span>
left:number<span class="Special"> <- </span>get *recipes, <span class="Constant">left:offset</span>
right:number<span class="Special"> <- </span>get *recipes, <span class="Constant">right:offset</span>
row:number, screen<span class="Special"> <- </span>render screen, recipes
recipe-warnings:address:array:character<span class="Special"> <- </span>get *env, <span class="Constant">recipe-warnings:offset</span>
<span class="Delimiter">{</span>
<span class="Comment"># print any warnings</span>
<span class="muControl">break-unless</span> recipe-warnings
row, screen<span class="Special"> <- </span>render-string screen, recipe-warnings, left, right, <span class="Constant">1/red</span>, row
<span class="Delimiter">}</span>
<span class="Delimiter">{</span>
<span class="Comment"># no warnings? move to next line</span>
<span class="muControl">break-if</span> recipe-warnings
row<span class="Special"> <- </span>add row, <span class="Constant">1</span>
<span class="Delimiter">}</span>
<span class="Comment"># draw dotted line after recipes</span>
draw-horizontal screen, row, left, right, <span class="Constant">9480/horizontal-dotted</span>
clear-rest-of-screen screen, row, left, right
<span class="muControl">reply</span> screen/same-as-ingredient:<span class="Constant">0</span>
]
<span class="muRecipe">recipe</span> clear-rest-of-screen [
<span class="Constant">local-scope</span>
screen:address<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
row:number<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
left:number<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
right:number<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
row<span class="Special"> <- </span>add row, <span class="Constant">1</span>
move-cursor screen, row, left
screen-height:number<span class="Special"> <- </span>screen-height screen
<span class="Delimiter">{</span>
at-bottom-of-screen?:boolean<span class="Special"> <- </span>greater-or-equal row, screen-height
<span class="muControl">break-if</span> at-bottom-of-screen?
move-cursor screen, row, left
clear-line-delimited screen, left, right
row<span class="Special"> <- </span>add row, <span class="Constant">1</span>
<span class="muControl">loop</span>
<span class="Delimiter">}</span>
]
<span class="Comment"># helper for testing a single editor</span>
<span class="muRecipe">recipe</span> update-cursor [
<span class="Constant">local-scope</span>
screen:address<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
recipes:address:editor-data<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
current-sandbox:address:editor-data<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
sandbox-in-focus?:boolean<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
<span class="Delimiter">{</span>
<span class="muControl">break-if</span> sandbox-in-focus?
<span class="CommentedCode">#? $print [recipes in focus</span>
<span class="CommentedCode">#? ] #? 1</span>
cursor-row:number<span class="Special"> <- </span>get *recipes, <span class="Constant">cursor-row:offset</span>
cursor-column:number<span class="Special"> <- </span>get *recipes, <span class="Constant">cursor-column:offset</span>
<span class="Delimiter">}</span>
<span class="Delimiter">{</span>
<span class="muControl">break-unless</span> sandbox-in-focus?
<span class="CommentedCode">#? $print [sandboxes in focus</span>
<span class="CommentedCode">#? ] #? 1</span>
cursor-row:number<span class="Special"> <- </span>get *current-sandbox, <span class="Constant">cursor-row:offset</span>
cursor-column:number<span class="Special"> <- </span>get *current-sandbox, <span class="Constant">cursor-column:offset</span>
<span class="Delimiter">}</span>
move-cursor screen, cursor-row, cursor-column
]
<span class="SalientComment">## running code from the editor and creating sandboxes</span>
container sandbox-data [
data:address:array:character
response:address:array:character
warnings:address:array:character
trace:address:array:character
expected-response:address:array:character
<span class="Comment"># coordinates to track clicks</span>
starting-row-on-screen:number
response-starting-row-on-screen:number
display-trace?:boolean
screen:address:screen <span class="Comment"># prints in the sandbox go here</span>
next-sandbox:address:sandbox-data
]
<span class="muScenario">scenario</span> run-and-show-results [
$close-trace <span class="Comment"># trace too long for github</span>
assume-screen <span class="Constant">100/width</span>, <span class="Constant">15/height</span>
<span class="Comment"># recipe editor is empty</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[]</span>
<span class="Comment"># sandbox editor contains an instruction without storing outputs</span>
<span class="Constant">2</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[divide-with-remainder 11, 3]</span>
<span class="Constant">3</span>:address:programming-environment-data<span class="Special"> <- </span>new-programming-environment screen:address, <span class="Constant">1</span>:address:array:character, <span class="Constant">2</span>:address:array:character
<span class="Comment"># run the code in the editors</span>
assume-console [
press <span class="Constant">65532</span> <span class="Comment"># F4</span>
]
run [
event-loop screen:address, console:address, <span class="Constant">3</span>:address:programming-environment-data
]
<span class="Comment"># check that screen prints the results</span>
screen-should-contain [
<span class="Constant"> . run (F4) .</span>
<span class="Constant"> . ┊ .</span>
<span class="Constant"> .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.</span>
<span class="Constant"> . ┊ x.</span>
<span class="Constant"> . ┊divide-with-remainder 11, 3 .</span>
<span class="Constant"> . ┊3 .</span>
<span class="Constant"> . ┊2 .</span>
<span class="Constant"> . ┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.</span>
<span class="Constant"> . ┊ .</span>
]
screen-should-contain-in-color <span class="Constant">7/white</span>, [
<span class="Constant"> . .</span>
<span class="Constant"> . .</span>
<span class="Constant"> . .</span>
<span class="Constant"> . .</span>
<span class="Constant"> . divide-with-remainder 11, 3 .</span>
<span class="Constant"> . .</span>
<span class="Constant"> . .</span>
<span class="Constant"> . .</span>
<span class="Constant"> . .</span>
]
screen-should-contain-in-color <span class="Constant">245/grey</span>, [
<span class="Constant"> . .</span>
<span class="Constant"> . ┊ .</span>
<span class="Constant"> .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.</span>
<span class="Constant"> . ┊ x.</span>
<span class="Constant"> . ┊ .</span>
<span class="Constant"> . ┊3 .</span>
<span class="Constant"> . ┊2 .</span>
<span class="Constant"> . ┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.</span>
<span class="Constant"> . ┊ .</span>
]
<span class="Comment"># run another command</span>
assume-console [
left-click <span class="Constant">1</span>, <span class="Constant">80</span>
type <span class="Constant">[add 2, 2]</span>
press <span class="Constant">65532</span> <span class="Comment"># F4</span>
]
run [
event-loop screen:address, console:address, <span class="Constant">3</span>:address:programming-environment-data
]
<span class="Comment"># check that screen prints the results</span>
screen-should-contain [
<span class="Constant"> . run (F4) .</span>
<span class="Constant"> . ┊ .</span>
<span class="Constant"> .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.</span>
<span class="Constant"> . ┊ x.</span>
<span class="Constant"> . ┊add 2, 2 .</span>
<span class="Constant"> . ┊4 .</span>
<span class="Constant"> . ┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.</span>
<span class="Constant"> . ┊ x.</span>
<span class="Constant"> . ┊divide-with-remainder 11, 3 .</span>
<span class="Constant"> . ┊3 .</span>
<span class="Constant"> . ┊2 .</span>
<span class="Constant"> . ┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.</span>
<span class="Constant"> . ┊ .</span>
]
]
<span class="Comment"># hook into event-loop recipe: read non-unicode keypress from k, process it if</span>
<span class="Comment"># necessary, then go to next level</span>
<span class="muRecipe">after</span> +global-keypress [
<span class="Comment"># F4? load all code and run all sandboxes.</span>
<span class="Delimiter">{</span>
do-run?:boolean<span class="Special"> <- </span>equal *k, <span class="Constant">65532/F4</span>
<span class="muControl">break-unless</span> do-run?
run-sandboxes env
<span class="Comment"># F4 might update warnings and results on both sides</span>
screen<span class="Special"> <- </span>render-all screen, env
update-cursor screen, recipes, current-sandbox, *sandbox-in-focus?
show-screen screen
<span class="muControl">loop</span> <span class="Constant">+next-event:label</span>
<span class="Delimiter">}</span>
]
<span class="muRecipe">recipe</span> run-sandboxes [
<span class="Constant">local-scope</span>
env:address:programming-environment-data<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
recipes:address:editor-data<span class="Special"> <- </span>get *env, <span class="Constant">recipes:offset</span>
<span class="Comment"># copy code from recipe editor, persist, load into mu, save any warnings</span>
in:address:array:character<span class="Special"> <- </span>editor-contents recipes
save <span class="Constant">[recipes.mu]</span>, in
recipe-warnings:address:address:array:character<span class="Special"> <- </span>get-address *env, <span class="Constant">recipe-warnings:offset</span>
*recipe-warnings<span class="Special"> <- </span>reload in
<span class="Comment"># if recipe editor has errors, stop</span>
<span class="muControl">reply-if</span> *recipe-warnings
<span class="Comment"># check contents of right editor (sandbox)</span>
current-sandbox:address:editor-data<span class="Special"> <- </span>get *env, <span class="Constant">current-sandbox:offset</span>
<span class="Delimiter">{</span>
sandbox-contents:address:array:character<span class="Special"> <- </span>editor-contents current-sandbox
<span class="muControl">break-unless</span> sandbox-contents
<span class="Comment"># if contents exist, first save them</span>
<span class="Comment"># run them and turn them into a new sandbox-data</span>
new-sandbox:address:sandbox-data<span class="Special"> <- </span>new <span class="Constant">sandbox-data:type</span>
data:address:address:array:character<span class="Special"> <- </span>get-address *new-sandbox, <span class="Constant">data:offset</span>
*data<span class="Special"> <- </span>copy sandbox-contents
<span class="Comment"># push to head of sandbox list</span>
dest:address:address:sandbox-data<span class="Special"> <- </span>get-address *env, <span class="Constant">sandbox:offset</span>
next:address:address:sandbox-data<span class="Special"> <- </span>get-address *new-sandbox, <span class="Constant">next-sandbox:offset</span>
*next<span class="Special"> <- </span>copy *dest
*dest<span class="Special"> <- </span>copy new-sandbox
<span class="Comment"># clear sandbox editor</span>
init:address:address:duplex-list<span class="Special"> <- </span>get-address *current-sandbox, <span class="Constant">data:offset</span>
*init<span class="Special"> <- </span>push-duplex <span class="Constant">167/§</span>, <span class="Constant">0/tail</span>
top-of-screen:address:address:duplex-list<span class="Special"> <- </span>get-address *current-sandbox, <span class="Constant">top-of-screen:offset</span>
*top-of-screen<span class="Special"> <- </span>copy *init
<span class="Delimiter">}</span>
<span class="Comment"># save all sandboxes before running, just in case we die when running</span>
save-sandboxes env
<span class="Comment"># run all sandboxes</span>
curr:address:sandbox-data<span class="Special"> <- </span>get *env, <span class="Constant">sandbox:offset</span>
<span class="Delimiter">{</span>
<span class="muControl">break-unless</span> curr
data<span class="Special"> <- </span>get-address *curr, <span class="Constant">data:offset</span>
response:address:address:array:character<span class="Special"> <- </span>get-address *curr, <span class="Constant">response:offset</span>
warnings:address:address:array:character<span class="Special"> <- </span>get-address *curr, <span class="Constant">warnings:offset</span>
trace:address:address:array:character<span class="Special"> <- </span>get-address *curr, <span class="Constant">trace:offset</span>
fake-screen:address:address:screen<span class="Special"> <- </span>get-address *curr, <span class="Constant">screen:offset</span>
*response, *warnings, *fake-screen, *trace<span class="Special"> <- </span>run-interactive *data
curr<span class="Special"> <- </span>get *curr, <span class="Constant">next-sandbox:offset</span>
<span class="muControl">loop</span>
<span class="Delimiter">}</span>
]
<span class="muRecipe">recipe</span> save-sandboxes [
<span class="Constant">local-scope</span>
env:address:programming-environment-data<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
current-sandbox:address:editor-data<span class="Special"> <- </span>get *env, <span class="Constant">current-sandbox:offset</span>
<span class="Comment"># first clear previous versions, in case we deleted some sandbox</span>
$system <span class="Constant">[rm lesson/[0-9]</span>* >/dev/null <span class="Constant">2</span>>/dev/null] <span class="Comment"># some shells can't handle '>&'</span>
curr:address:sandbox-data<span class="Special"> <- </span>get *env, <span class="Constant">sandbox:offset</span>
suffix:address:array:character<span class="Special"> <- </span>new <span class="Constant">[.out]</span>
idx:number<span class="Special"> <- </span>copy <span class="Constant">0</span>
<span class="Delimiter">{</span>
<span class="muControl">break-unless</span> curr
data:address:array:character<span class="Special"> <- </span>get *curr, <span class="Constant">data:offset</span>
filename:address:array:character<span class="Special"> <- </span>integer-to-decimal-string idx
save filename, data
<span class="Delimiter">{</span>
expected-response:address:array:character<span class="Special"> <- </span>get *curr, <span class="Constant">expected-response:offset</span>
<span class="muControl">break-unless</span> expected-response
filename<span class="Special"> <- </span>string-append filename, suffix
save filename, expected-response
<span class="Delimiter">}</span>
idx<span class="Special"> <- </span>add idx, <span class="Constant">1</span>
curr<span class="Special"> <- </span>get *curr, <span class="Constant">next-sandbox:offset</span>
<span class="muControl">loop</span>
<span class="Delimiter">}</span>
]
<span class="muRecipe">recipe</span> render-sandbox-side [
<span class="Constant">local-scope</span>
screen:address<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
env:address:programming-environment-data<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
<span class="CommentedCode">#? trace [app], [render sandbox side] #? 1</span>
current-sandbox:address:editor-data<span class="Special"> <- </span>get *env, <span class="Constant">current-sandbox:offset</span>
left:number<span class="Special"> <- </span>get *current-sandbox, <span class="Constant">left:offset</span>
right:number<span class="Special"> <- </span>get *current-sandbox, <span class="Constant">right:offset</span>
row:number, screen<span class="Special"> <- </span>render screen, current-sandbox
row<span class="Special"> <- </span>add row, <span class="Constant">1</span>
draw-horizontal screen, row, left, right, <span class="Constant">9473/horizontal-double</span>
sandbox:address:sandbox-data<span class="Special"> <- </span>get *env, <span class="Constant">sandbox:offset</span>
row, screen<span class="Special"> <- </span>render-sandboxes screen, sandbox, left, right, row
<span class="Comment"># clear rest of screen</span>
row<span class="Special"> <- </span>add row, <span class="Constant">1</span>
move-cursor screen, row, left
screen-height:number<span class="Special"> <- </span>screen-height screen
<span class="Delimiter">{</span>
at-bottom-of-screen?:boolean<span class="Special"> <- </span>greater-or-equal row, screen-height
<span class="muControl">break-if</span> at-bottom-of-screen?
move-cursor screen, row, left
clear-line-delimited screen, left, right
row<span class="Special"> <- </span>add row, <span class="Constant">1</span>
<span class="muControl">loop</span>
<span class="Delimiter">}</span>
<span class="muControl">reply</span> screen/same-as-ingredient:<span class="Constant">0</span>
]
<span class="muRecipe">recipe</span> render-sandboxes [
<span class="Constant">local-scope</span>
screen:address<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
sandbox:address:sandbox-data<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
left:number<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
right:number<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
row:number<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
<span class="muControl">reply-unless</span> sandbox, row/same-as-ingredient:<span class="Constant">4</span>, screen/same-as-ingredient:<span class="Constant">0</span>
screen-height:number<span class="Special"> <- </span>screen-height screen
at-bottom?:boolean<span class="Special"> <- </span>greater-or-equal row, screen-height
<span class="muControl">reply-if</span> at-bottom?:boolean, row/same-as-ingredient:<span class="Constant">4</span>, screen/same-as-ingredient:<span class="Constant">0</span>
<span class="Comment"># render sandbox menu</span>
row<span class="Special"> <- </span>add row, <span class="Constant">1</span>
move-cursor screen, row, left
clear-line-delimited screen, left, right
print-character screen, <span class="Constant">120/x</span>, <span class="Constant">245/grey</span>
<span class="Comment"># save menu row so we can detect clicks to it later</span>
starting-row:address:number<span class="Special"> <- </span>get-address *sandbox, <span class="Constant">starting-row-on-screen:offset</span>
*starting-row<span class="Special"> <- </span>copy row
<span class="Comment"># render sandbox contents</span>
sandbox-data:address:array:character<span class="Special"> <- </span>get *sandbox, <span class="Constant">data:offset</span>
row, screen<span class="Special"> <- </span>render-string screen, sandbox-data, left, right, <span class="Constant">7/white</span>, row
<span class="Comment"># render sandbox warnings, screen or response, in that order</span>
response-starting-row:address:number<span class="Special"> <- </span>get-address *sandbox, <span class="Constant">response-starting-row-on-screen:offset</span>
sandbox-response:address:array:character<span class="Special"> <- </span>get *sandbox, <span class="Constant">response:offset</span>
sandbox-warnings:address:array:character<span class="Special"> <- </span>get *sandbox, <span class="Constant">warnings:offset</span>
sandbox-screen:address<span class="Special"> <- </span>get *sandbox, <span class="Constant">screen:offset</span>
<span class="Constant"> +render-sandbox-results</span>
<span class="Delimiter">{</span>
<span class="muControl">break-unless</span> sandbox-warnings
*response-starting-row<span class="Special"> <- </span>copy <span class="Constant">0</span> <span class="Comment"># no response</span>
row, screen<span class="Special"> <- </span>render-string screen, sandbox-warnings, left, right, <span class="Constant">1/red</span>, row
<span class="Delimiter">}</span>
<span class="Delimiter">{</span>
<span class="muControl">break-if</span> sandbox-warnings
empty-screen?:boolean<span class="Special"> <- </span>fake-screen-is-empty? sandbox-screen
<span class="muControl">break-if</span> empty-screen?
row, screen<span class="Special"> <- </span>render-screen screen, sandbox-screen, left, right, row
<span class="Delimiter">}</span>
<span class="Delimiter">{</span>
<span class="muControl">break-if</span> sandbox-warnings
<span class="muControl">break-unless</span> empty-screen?
<span class="CommentedCode">#? $print [display response from ], row, 10/newline #? 1</span>
*response-starting-row<span class="Special"> <- </span>add row, <span class="Constant">1</span>
<span class="Constant"> +render-sandbox-response</span>
row, screen<span class="Special"> <- </span>render-string screen, sandbox-response, left, right, <span class="Constant">245/grey</span>, row
<span class="Delimiter">}</span>
<span class="Constant"> +render-sandbox-end</span>
at-bottom?:boolean<span class="Special"> <- </span>greater-or-equal row, screen-height
<span class="muControl">reply-if</span> at-bottom?, row/same-as-ingredient:<span class="Constant">4</span>, screen/same-as-ingredient:<span class="Constant">0</span>
<span class="Comment"># draw solid line after sandbox</span>
draw-horizontal screen, row, left, right, <span class="Constant">9473/horizontal-double</span>
<span class="Comment"># draw next sandbox</span>
next-sandbox:address:sandbox-data<span class="Special"> <- </span>get *sandbox, <span class="Constant">next-sandbox:offset</span>
row, screen<span class="Special"> <- </span>render-sandboxes screen, next-sandbox, left, right, row
<span class="muControl">reply</span> row/same-as-ingredient:<span class="Constant">4</span>, screen/same-as-ingredient:<span class="Constant">0</span>
]
<span class="Comment"># assumes programming environment has no sandboxes; restores them from previous session</span>
<span class="muRecipe">recipe</span> restore-sandboxes [
<span class="Constant">local-scope</span>
env:address:programming-environment-data<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
<span class="Comment"># read all scenarios, pushing them to end of a list of scenarios</span>
suffix:address:array:character<span class="Special"> <- </span>new <span class="Constant">[.out]</span>
idx:number<span class="Special"> <- </span>copy <span class="Constant">0</span>
curr:address:address:sandbox-data<span class="Special"> <- </span>get-address *env, <span class="Constant">sandbox:offset</span>
<span class="Delimiter">{</span>
filename:address:array:character<span class="Special"> <- </span>integer-to-decimal-string idx
contents:address:array:character<span class="Special"> <- </span>restore filename
<span class="muControl">break-unless</span> contents <span class="Comment"># stop at first error; assuming file didn't exist</span>
<span class="Comment"># create new sandbox for file</span>
*curr<span class="Special"> <- </span>new <span class="Constant">sandbox-data:type</span>
data:address:address:array:character<span class="Special"> <- </span>get-address **curr, <span class="Constant">data:offset</span>
*data<span class="Special"> <- </span>copy contents
<span class="Comment"># restore expected output for sandbox if it exists</span>
<span class="Delimiter">{</span>
filename<span class="Special"> <- </span>string-append filename, suffix
contents<span class="Special"> <- </span>restore filename
<span class="muControl">break-unless</span> contents
expected-response:address:address:array:character<span class="Special"> <- </span>get-address **curr, <span class="Constant">expected-response:offset</span>
*expected-response<span class="Special"> <- </span>copy contents
<span class="Delimiter">}</span>
<span class="Constant"> +continue</span>
idx<span class="Special"> <- </span>add idx, <span class="Constant">1</span>
curr<span class="Special"> <- </span>get-address **curr, <span class="Constant">next-sandbox:offset</span>
<span class="muControl">loop</span>
<span class="Delimiter">}</span>
<span class="muControl">reply</span> env/same-as-ingredient:<span class="Constant">0</span>
]
<span class="Comment"># row:number, screen:address <- render-screen screen:address, sandbox-screen:address, left:number, right:number, row:number</span>
<span class="Comment"># print the fake sandbox screen to 'screen' with appropriate delimiters</span>
<span class="Comment"># leave cursor at start of next line</span>
<span class="muRecipe">recipe</span> render-screen [
<span class="Constant">local-scope</span>
screen:address<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
s:address:screen<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
left:number<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
right:number<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
row:number<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
row<span class="Special"> <- </span>add row, <span class="Constant">1</span>
<span class="muControl">reply-unless</span> s, row/same-as-ingredient:<span class="Constant">4</span>, screen/same-as-ingredient:<span class="Constant">0</span>
<span class="Comment"># print 'screen:'</span>
header:address:array:character<span class="Special"> <- </span>new <span class="Constant">[screen:]</span>
row<span class="Special"> <- </span>subtract row, <span class="Constant">1</span> <span class="Comment"># compensate for render-string below</span>
row<span class="Special"> <- </span>render-string screen, header, left, right, <span class="Constant">245/grey</span>, row
<span class="Comment"># newline</span>
row<span class="Special"> <- </span>add row, <span class="Constant">1</span>
move-cursor screen, row, left
<span class="Comment"># start printing s</span>
column:number<span class="Special"> <- </span>copy left
s-width:number<span class="Special"> <- </span>screen-width s
s-height:number<span class="Special"> <- </span>screen-height s
buf:address:array:screen-cell<span class="Special"> <- </span>get *s, <span class="Constant">data:offset</span>
stop-printing:number<span class="Special"> <- </span>add left, s-width, <span class="Constant">3</span>
max-column:number<span class="Special"> <- </span>min stop-printing, right
i:number<span class="Special"> <- </span>copy <span class="Constant">0</span>
len:number<span class="Special"> <- </span>length *buf
screen-height:number<span class="Special"> <- </span>screen-height screen
<span class="Delimiter">{</span>
done?:boolean<span class="Special"> <- </span>greater-or-equal i, len
<span class="muControl">break-if</span> done?
done?<span class="Special"> <- </span>greater-or-equal row, screen-height
<span class="muControl">break-if</span> done?
column<span class="Special"> <- </span>copy left
move-cursor screen, row, column
<span class="Comment"># initial leader for each row: two spaces and a '.'</span>
print-character screen, <span class="Constant">32/space</span>, <span class="Constant">245/grey</span>
print-character screen, <span class="Constant">32/space</span>, <span class="Constant">245/grey</span>
print-character screen, <span class="Constant">46/full-stop</span>, <span class="Constant">245/grey</span>
column<span class="Special"> <- </span>add left, <span class="Constant">3</span>
<span class="Delimiter">{</span>
<span class="Comment"># print row</span>
row-done?:boolean<span class="Special"> <- </span>greater-or-equal column, max-column
<span class="muControl">break-if</span> row-done?
curr:screen-cell<span class="Special"> <- </span>index *buf, i
c:character<span class="Special"> <- </span>get curr, <span class="Constant">contents:offset</span>
print-character screen, c, <span class="Constant">245/grey</span>
column<span class="Special"> <- </span>add column, <span class="Constant">1</span>
i<span class="Special"> <- </span>add i, <span class="Constant">1</span>
<span class="muControl">loop</span>
<span class="Delimiter">}</span>
<span class="Comment"># print final '.'</span>
print-character screen, <span class="Constant">46/full-stop</span>, <span class="Constant">245/grey</span>
column<span class="Special"> <- </span>add column, <span class="Constant">1</span>
<span class="Delimiter">{</span>
<span class="Comment"># clear rest of current line</span>
line-done?:boolean<span class="Special"> <- </span>greater-than column, right
<span class="muControl">break-if</span> line-done?
print-character screen, <span class="Constant">32/space</span>
column<span class="Special"> <- </span>add column, <span class="Constant">1</span>
<span class="muControl">loop</span>
<span class="Delimiter">}</span>
row<span class="Special"> <- </span>add row, <span class="Constant">1</span>
<span class="muControl">loop</span>
<span class="Delimiter">}</span>
<span class="muControl">reply</span> row/same-as-ingredient:<span class="Constant">4</span>, screen/same-as-ingredient:<span class="Constant">0</span>
]
<span class="muScenario">scenario</span> run-updates-results [
$close-trace <span class="Comment"># trace too long for github</span>
assume-screen <span class="Constant">100/width</span>, <span class="Constant">12/height</span>
<span class="Comment"># define a recipe (no indent for the 'add' line below so column numbers are more obvious)</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[ </span>
<span class="Constant">recipe foo [</span>
<span class="Constant">z:number <- add 2, 2</span>
<span class="Constant">]</span>]
<span class="Comment"># sandbox editor contains an instruction without storing outputs</span>
<span class="Constant">2</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[foo]</span>
<span class="Constant">3</span>:address:programming-environment-data<span class="Special"> <- </span>new-programming-environment screen:address, <span class="Constant">1</span>:address:array:character, <span class="Constant">2</span>:address:array:character
<span class="Comment"># run the code in the editors</span>
assume-console [
press <span class="Constant">65532</span> <span class="Comment"># F4</span>
]
run [
event-loop screen:address, console:address, <span class="Constant">3</span>:address:programming-environment-data
]
<span class="Comment"># check that screen prints the results</span>
screen-should-contain [
<span class="Constant"> . run (F4) .</span>
<span class="Constant"> . ┊ .</span>
<span class="Constant"> .recipe foo [ ┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.</span>
<span class="Constant"> .z:number <- add 2, 2 ┊ x.</span>
<span class="Constant"> .] ┊foo .</span>
<span class="Constant"> .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊4 .</span>
<span class="Constant"> . ┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.</span>
<span class="Constant"> . ┊ .</span>
]
<span class="Comment"># make a change (incrementing one of the args to 'add'), then rerun</span>
assume-console [
left-click <span class="Constant">3</span>, <span class="Constant">28</span> <span class="Comment"># one past the value of the second arg</span>
type <span class="Constant">[«3]</span> <span class="Comment"># replace</span>
press <span class="Constant">65532</span> <span class="Comment"># F4</span>
]
<span class="Constant">4</span>:event/backspace<span class="Special"> <- </span>merge <span class="Constant">0/text</span>, <span class="Constant">8/backspace</span>, <span class="Constant">0/dummy</span>, <span class="Constant">0/dummy</span>
replace-in-console <span class="Constant">171/«</span>, <span class="Constant">4</span>:event/backspace
run [
event-loop screen:address, console:address, <span class="Constant">3</span>:address:programming-environment-data
]
<span class="Comment"># check that screen updates the result on the right</span>
screen-should-contain [
<span class="Constant"> . run (F4) .</span>
<span class="Constant"> . ┊ .</span>
<span class="Constant"> .recipe foo [ ┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.</span>
<span class="Constant"> .z:number <- add 2, 3 ┊ x.</span>
<span class="Constant"> .] ┊foo .</span>
<span class="Constant"> .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊5 .</span>
<span class="Constant"> . ┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.</span>
<span class="Constant"> . ┊ .</span>
]
]
<span class="muScenario">scenario</span> run-instruction-and-print-warnings [
$close-trace <span class="Comment"># trace too long for github</span>
assume-screen <span class="Constant">100/width</span>, <span class="Constant">10/height</span>
<span class="Comment"># left editor is empty</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[]</span>
<span class="Comment"># right editor contains an illegal instruction</span>
<span class="Constant">2</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[get 1234:number, foo:offset]</span>
<span class="Constant">3</span>:address:programming-environment-data<span class="Special"> <- </span>new-programming-environment screen:address, <span class="Constant">1</span>:address:array:character, <span class="Constant">2</span>:address:array:character
<span class="Comment"># run the code in the editors</span>
assume-console [
press <span class="Constant">65532</span> <span class="Comment"># F4</span>
]
run [
event-loop screen:address, console:address, <span class="Constant">3</span>:address:programming-environment-data
]
<span class="Comment"># check that screen prints error message in red</span>
screen-should-contain [
<span class="Constant"> . run (F4) .</span>
<span class="Constant"> . ┊ .</span>
<span class="Constant"> .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.</span>
<span class="Constant"> . ┊ x.</span>
<span class="Constant"> . ┊get 1234:number, foo:offset .</span>
<span class="Constant"> . ┊unknown element foo in container number .</span>
<span class="Constant"> . ┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.</span>
<span class="Constant"> . ┊ .</span>
]
screen-should-contain-in-color <span class="Constant">7/white</span>, [
<span class="Constant"> . .</span>
<span class="Constant"> . .</span>
<span class="Constant"> . .</span>
<span class="Constant"> . .</span>
<span class="Constant"> . get 1234:number, foo:offset .</span>
<span class="Constant"> . .</span>
<span class="Constant"> . .</span>
<span class="Constant"> . .</span>
]
screen-should-contain-in-color <span class="Constant">1/red</span>, [
<span class="Constant"> . .</span>
<span class="Constant"> . .</span>
<span class="Constant"> . .</span>
<span class="Constant"> . .</span>
<span class="Constant"> . .</span>
<span class="Constant"> . unknown element foo in container number .</span>
<span class="Constant"> . .</span>
]
screen-should-contain-in-color <span class="Constant">245/grey</span>, [
<span class="Constant"> . .</span>
<span class="Constant"> . ┊ .</span>
<span class="Constant"> .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.</span>
<span class="Constant"> . ┊ x.</span>
<span class="Constant"> . ┊ .</span>
<span class="Constant"> . ┊ .</span>
<span class="Constant"> . ┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.</span>
<span class="Constant"> . ┊ .</span>
]
]
<span class="muScenario">scenario</span> run-instruction-and-print-warnings-only-once [
$close-trace <span class="Comment"># trace too long for github</span>
assume-screen <span class="Constant">100/width</span>, <span class="Constant">10/height</span>
<span class="Comment"># left editor is empty</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[]</span>
<span class="Comment"># right editor contains an illegal instruction</span>
<span class="Constant">2</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[get 1234:number, foo:offset]</span>
<span class="Constant">3</span>:address:programming-environment-data<span class="Special"> <- </span>new-programming-environment screen:address, <span class="Constant">1</span>:address:array:character, <span class="Constant">2</span>:address:array:character
<span class="Comment"># run the code in the editors multiple times</span>
assume-console [
press <span class="Constant">65532</span> <span class="Comment"># F4</span>
press <span class="Constant">65532</span> <span class="Comment"># F4</span>
]
run [
event-loop screen:address, console:address, <span class="Constant">3</span>:address:programming-environment-data
]
<span class="Comment"># check that screen prints error message just once</span>
screen-should-contain [
<span class="Constant"> . run (F4) .</span>
<span class="Constant"> . ┊ .</span>
<span class="Constant"> .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.</span>
<span class="Constant"> . ┊ x.</span>
<span class="Constant"> . ┊get 1234:number, foo:offset .</span>
<span class="Constant"> . ┊unknown element foo in container number .</span>
<span class="Constant"> . ┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.</span>
<span class="Constant"> . ┊ .</span>
]
]
<span class="muScenario">scenario</span> run-instruction-manages-screen-per-sandbox [
$close-trace <span class="Comment"># trace too long for github #? 1</span>
assume-screen <span class="Constant">100/width</span>, <span class="Constant">20/height</span>
<span class="Comment"># left editor is empty</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[]</span>
<span class="Comment"># right editor contains an illegal instruction</span>
<span class="Constant">2</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[print-integer screen:address, 4]</span>
<span class="Constant">3</span>:address:programming-environment-data<span class="Special"> <- </span>new-programming-environment screen:address, <span class="Constant">1</span>:address:array:character, <span class="Constant">2</span>:address:array:character
<span class="Comment"># run the code in the editor</span>
assume-console [
press <span class="Constant">65532</span> <span class="Comment"># F4</span>
]
run [
event-loop screen:address, console:address, <span class="Constant">3</span>:address:programming-environment-data
]
<span class="Comment"># check that it prints a little 5x5 toy screen</span>
<span class="Comment"># hack: screen address is brittle</span>
screen-should-contain [
<span class="Constant"> . run (F4) .</span>
<span class="Constant"> . ┊ .</span>
<span class="Constant"> .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.</span>
<span class="Constant"> . ┊ x.</span>
<span class="Constant"> . ┊print-integer screen:address, 4 .</span>
<span class="Constant"> . ┊screen: .</span>
<span class="Constant"> . ┊ .4 . .</span>
<span class="Constant"> . ┊ . . .</span>
<span class="Constant"> . ┊ . . .</span>
<span class="Constant"> . ┊ . . .</span>
<span class="Constant"> . ┊ . . .</span>
<span class="Constant"> . ┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.</span>
<span class="Constant"> . ┊ .</span>
]
]
<span class="muRecipe">recipe</span> editor-contents [
<span class="Constant">local-scope</span>
editor:address:editor-data<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
buf:address:buffer<span class="Special"> <- </span>new-buffer <span class="Constant">80</span>
curr:address:duplex-list<span class="Special"> <- </span>get *editor, <span class="Constant">data:offset</span>
<span class="Comment"># skip § sentinel</span>
assert curr, <span class="Constant">[editor without data is illegal; must have at least a sentinel]</span>
curr<span class="Special"> <- </span>next-duplex curr
<span class="muControl">reply-unless</span> curr, <span class="Constant">0</span>
<span class="Delimiter">{</span>
<span class="muControl">break-unless</span> curr
c:character<span class="Special"> <- </span>get *curr, <span class="Constant">value:offset</span>
buffer-append buf, c
curr<span class="Special"> <- </span>next-duplex curr
<span class="muControl">loop</span>
<span class="Delimiter">}</span>
result:address:array:character<span class="Special"> <- </span>buffer-to-array buf
<span class="muControl">reply</span> result
]
<span class="muScenario">scenario</span> editor-provides-edited-contents [
assume-screen <span class="Constant">10/width</span>, <span class="Constant">5/height</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[abc]</span>
<span class="Constant">2</span>:address:editor-data<span class="Special"> <- </span>new-editor <span class="Constant">1</span>:address:array:character, screen:address, <span class="Constant">0/left</span>, <span class="Constant">10/right</span>
assume-console [
left-click <span class="Constant">1</span>, <span class="Constant">2</span>
type <span class="Constant">[def]</span>
]
run [
editor-event-loop screen:address, console:address, <span class="Constant">2</span>:address:editor-data
<span class="Constant">3</span>:address:array:character<span class="Special"> <- </span>editor-contents <span class="Constant">2</span>:address:editor-data
<span class="Constant">4</span>:array:character<span class="Special"> <- </span>copy *<span class="Constant">3</span>:address:array:character
]
memory-should-contain [
<span class="Constant">4</span>:string<span class="Special"> <- </span><span class="Constant">[abdefc]</span>
]
]
<span class="SalientComment">## editing sandboxes after they've been created</span>
<span class="muScenario">scenario</span> clicking-on-a-sandbox-moves-it-to-editor [
<span class="Constant"> $close-trace</span>
assume-screen <span class="Constant">40/width</span>, <span class="Constant">10/height</span>
<span class="Comment"># basic recipe</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[ </span>
<span class="Constant">recipe foo [</span>
<span class="Constant"> add 2, 2</span>
<span class="Constant">]</span>]
<span class="Comment"># run it</span>
<span class="Constant">2</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[foo]</span>
assume-console [
press <span class="Constant">65532</span> <span class="Comment"># F4</span>
]
<span class="Constant">3</span>:address:programming-environment-data<span class="Special"> <- </span>new-programming-environment screen:address, <span class="Constant">1</span>:address:array:character, <span class="Constant">2</span>:address:array:character
event-loop screen:address, console:address, <span class="Constant">3</span>:address:programming-environment-data
screen-should-contain [
<span class="Constant"> . run (F4) .</span>
<span class="Constant"> . ┊ .</span>
<span class="Constant"> .recipe foo [ ┊━━━━━━━━━━━━━━━━━━━.</span>
<span class="Constant"> . add 2, 2 ┊ x.</span>
<span class="Constant"> .] ┊foo .</span>
<span class="Constant"> .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊4 .</span>
<span class="Constant"> . ┊━━━━━━━━━━━━━━━━━━━.</span>
<span class="Constant"> . ┊ .</span>
]
<span class="Comment"># click somewhere on the sandbox</span>
assume-console [
left-click <span class="Constant">3</span>, <span class="Constant">30</span>
]
run [
event-loop screen:address, console:address, <span class="Constant">3</span>:address:programming-environment-data
]
<span class="Comment"># it pops back into editor</span>
screen-should-contain [
<span class="Constant"> . run (F4) .</span>
<span class="Constant"> . ┊foo .</span>
<span class="Constant"> .recipe foo [ ┊━━━━━━━━━━━━━━━━━━━.</span>
<span class="Constant"> . add 2, 2 ┊ .</span>
<span class="Constant"> .] ┊ .</span>
<span class="Constant"> .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊ .</span>
<span class="Constant"> . ┊ .</span>
<span class="Constant"> . ┊ .</span>
]
]
<span class="muRecipe">after</span> +global-touch [
<span class="Comment"># right side of screen and below sandbox editor? pop appropriate sandbox</span>
<span class="Comment"># contents back into sandbox editor provided it's empty</span>
<span class="Delimiter">{</span>
sandbox-left-margin:number<span class="Special"> <- </span>get *current-sandbox, <span class="Constant">left:offset</span>
click-column:number<span class="Special"> <- </span>get *t, <span class="Constant">column:offset</span>
on-sandbox-side?:boolean<span class="Special"> <- </span>greater-or-equal click-column, sandbox-left-margin
<span class="muControl">break-unless</span> on-sandbox-side?
first-sandbox:address:sandbox-data<span class="Special"> <- </span>get *env, <span class="Constant">sandbox:offset</span>
<span class="muControl">break-unless</span> first-sandbox
first-sandbox-begins:number<span class="Special"> <- </span>get *first-sandbox, <span class="Constant">starting-row-on-screen:offset</span>
click-row:number<span class="Special"> <- </span>get *t, <span class="Constant">row:offset</span>
below-sandbox-editor?:boolean<span class="Special"> <- </span>greater-or-equal click-row, first-sandbox-begins
<span class="muControl">break-unless</span> below-sandbox-editor?
empty-sandbox-editor?:boolean<span class="Special"> <- </span>empty-editor? current-sandbox
<span class="muControl">break-unless</span> empty-sandbox-editor? <span class="Comment"># make the user hit F4 before editing a new sandbox</span>
<span class="Comment"># identify the sandbox to edit and remove it from the sandbox list</span>
sandbox:address:sandbox-data<span class="Special"> <- </span>extract-sandbox env, click-row
text:address:array:character<span class="Special"> <- </span>get *sandbox, <span class="Constant">data:offset</span>
current-sandbox<span class="Special"> <- </span>insert-text current-sandbox, text
screen<span class="Special"> <- </span>render-sandbox-side screen, env
update-cursor screen, recipes, current-sandbox, *sandbox-in-focus?
show-screen screen
<span class="muControl">loop</span> <span class="Constant">+next-event:label</span>
<span class="Delimiter">}</span>
]
<span class="muRecipe">recipe</span> empty-editor? [
<span class="Constant">local-scope</span>
editor:address:editor-data<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
head:address:duplex-list<span class="Special"> <- </span>get *editor, <span class="Constant">data:offset</span>
first:address:duplex-list<span class="Special"> <- </span>next-duplex head
result:boolean<span class="Special"> <- </span>not first
<span class="muControl">reply</span> result
]
<span class="muRecipe">recipe</span> extract-sandbox [
<span class="Constant">local-scope</span>
env:address:programming-environment-data<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
click-row:number<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
<span class="Comment"># assert click-row >= sandbox.starting-row-on-screen</span>
sandbox:address:address:sandbox-data<span class="Special"> <- </span>get-address *env, <span class="Constant">sandbox:offset</span>
start:number<span class="Special"> <- </span>get **sandbox, <span class="Constant">starting-row-on-screen:offset</span>
clicked-on-sandboxes?:boolean<span class="Special"> <- </span>greater-or-equal click-row, start
assert clicked-on-sandboxes?, <span class="Constant">[extract-sandbox called on click to sandbox editor]</span>
<span class="Delimiter">{</span>
next-sandbox:address:sandbox-data<span class="Special"> <- </span>get **sandbox, <span class="Constant">next-sandbox:offset</span>
<span class="muControl">break-unless</span> next-sandbox
<span class="Comment"># if click-row < sandbox.next-sandbox.starting-row-on-screen, break</span>
next-start:number<span class="Special"> <- </span>get *next-sandbox, <span class="Constant">starting-row-on-screen:offset</span>
found?:boolean<span class="Special"> <- </span>lesser-than click-row, next-start
<span class="muControl">break-if</span> found?
sandbox<span class="Special"> <- </span>get-address **sandbox, <span class="Constant">next-sandbox:offset</span>
<span class="muControl">loop</span>
<span class="Delimiter">}</span>
<span class="Comment"># snip sandbox out of its list</span>
result:address:sandbox-data<span class="Special"> <- </span>copy *sandbox
*sandbox<span class="Special"> <- </span>copy next-sandbox
<span class="muControl">reply</span> result
]
<span class="SalientComment">## deleting sandboxes</span>
<span class="muScenario">scenario</span> deleting-sandboxes [
$close-trace <span class="Comment"># trace too long for github</span>
assume-screen <span class="Constant">100/width</span>, <span class="Constant">15/height</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[]</span>
<span class="Constant">2</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[]</span>
<span class="Constant">3</span>:address:programming-environment-data<span class="Special"> <- </span>new-programming-environment screen:address, <span class="Constant">1</span>:address:array:character, <span class="Constant">2</span>:address:array:character
<span class="Comment"># run a few commands</span>
assume-console [
left-click <span class="Constant">1</span>, <span class="Constant">80</span>
type <span class="Constant">[divide-with-remainder 11, 3]</span>
press <span class="Constant">65532</span> <span class="Comment"># F4</span>
type <span class="Constant">[add 2, 2]</span>
press <span class="Constant">65532</span> <span class="Comment"># F4</span>
]
run [
event-loop screen:address, console:address, <span class="Constant">3</span>:address:programming-environment-data
]
screen-should-contain [
<span class="Constant"> . run (F4) .</span>
<span class="Constant"> . ┊ .</span>
<span class="Constant"> .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.</span>
<span class="Constant"> . ┊ x.</span>
<span class="Constant"> . ┊add 2, 2 .</span>
<span class="Constant"> . ┊4 .</span>
<span class="Constant"> . ┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.</span>
<span class="Constant"> . ┊ x.</span>
<span class="Constant"> . ┊divide-with-remainder 11, 3 .</span>
<span class="Constant"> . ┊3 .</span>
<span class="Constant"> . ┊2 .</span>
<span class="Constant"> . ┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.</span>
<span class="Constant"> . ┊ .</span>
]
<span class="Comment"># delete second sandbox</span>
assume-console [
left-click <span class="Constant">7</span>, <span class="Constant">99</span>
]
run [
event-loop screen:address, console:address, <span class="Constant">3</span>:address:programming-environment-data
]
screen-should-contain [
<span class="Constant"> . run (F4) .</span>
<span class="Constant"> . ┊ .</span>
<span class="Constant"> .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.</span>
<span class="Constant"> . ┊ x.</span>
<span class="Constant"> . ┊add 2, 2 .</span>
<span class="Constant"> . ┊4 .</span>
<span class="Constant"> . ┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.</span>
<span class="Constant"> . ┊ .</span>
<span class="Constant"> . ┊ .</span>
]
<span class="Comment"># delete first sandbox</span>
assume-console [
left-click <span class="Constant">3</span>, <span class="Constant">99</span>
]
run [
event-loop screen:address, console:address, <span class="Constant">3</span>:address:programming-environment-data
]
screen-should-contain [
<span class="Constant"> . run (F4) .</span>
<span class="Constant"> . ┊ .</span>
<span class="Constant"> .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.</span>
<span class="Constant"> . ┊ .</span>
<span class="Constant"> . ┊ .</span>
]
]
<span class="muRecipe">after</span> +global-touch [
<span class="Comment"># on a sandbox delete icon? process delete</span>
<span class="Delimiter">{</span>
was-delete?:boolean<span class="Special"> <- </span>delete-sandbox *t, env
<span class="muControl">break-unless</span> was-delete?
<span class="CommentedCode">#? trace [app], [delete clicked] #? 1</span>
screen<span class="Special"> <- </span>render-sandbox-side screen, env
update-cursor screen, recipes, current-sandbox, *sandbox-in-focus?
show-screen screen
<span class="muControl">loop</span> <span class="Constant">+next-event:label</span>
<span class="Delimiter">}</span>
]
<span class="Comment"># was-deleted?:boolean <- delete-sandbox t:touch-event, env:address:programming-environment-data</span>
<span class="muRecipe">recipe</span> delete-sandbox [
<span class="Constant">local-scope</span>
t:touch-event<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
env:address:programming-environment-data<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
click-column:number<span class="Special"> <- </span>get t, <span class="Constant">column:offset</span>
current-sandbox:address:editor-data<span class="Special"> <- </span>get *env, <span class="Constant">current-sandbox:offset</span>
right:number<span class="Special"> <- </span>get *current-sandbox, <span class="Constant">right:offset</span>
at-right?:boolean<span class="Special"> <- </span>equal click-column, right
<span class="muControl">reply-unless</span> at-right?, <span class="Constant">0/false</span>
click-row:number<span class="Special"> <- </span>get t, <span class="Constant">row:offset</span>
prev:address:address:sandbox-data<span class="Special"> <- </span>get-address *env, <span class="Constant">sandbox:offset</span>
curr:address:sandbox-data<span class="Special"> <- </span>get *env, <span class="Constant">sandbox:offset</span>
<span class="Delimiter">{</span>
<span class="muControl">break-unless</span> curr
<span class="Comment"># more sandboxes to check</span>
<span class="Delimiter">{</span>
target-row:number<span class="Special"> <- </span>get *curr, <span class="Constant">starting-row-on-screen:offset</span>
delete-curr?:boolean<span class="Special"> <- </span>equal target-row, click-row
<span class="muControl">break-unless</span> delete-curr?
<span class="Comment"># delete this sandbox, rerender and stop</span>
*prev<span class="Special"> <- </span>get *curr, <span class="Constant">next-sandbox:offset</span>
<span class="muControl">reply</span> <span class="Constant">1/true</span>
<span class="Delimiter">}</span>
prev<span class="Special"> <- </span>get-address *curr, <span class="Constant">next-sandbox:offset</span>
curr<span class="Special"> <- </span>get *curr, <span class="Constant">next-sandbox:offset</span>
<span class="muControl">loop</span>
<span class="Delimiter">}</span>
<span class="muControl">reply</span> <span class="Constant">0/false</span>
]
<span class="SalientComment">## clicking on sandbox results to 'fix' them and turn sandboxes into tests</span>
<span class="muScenario">scenario</span> sandbox-click-on-result-toggles-color-to-green [
<span class="Constant"> $close-trace</span>
assume-screen <span class="Constant">40/width</span>, <span class="Constant">10/height</span>
<span class="Comment"># basic recipe</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[ </span>
<span class="Constant">recipe foo [</span>
<span class="Constant"> add 2, 2</span>
<span class="Constant">]</span>]
<span class="Comment"># run it</span>
<span class="Constant">2</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[foo]</span>
assume-console [
press <span class="Constant">65532</span> <span class="Comment"># F4</span>
]
<span class="Constant">3</span>:address:programming-environment-data<span class="Special"> <- </span>new-programming-environment screen:address, <span class="Constant">1</span>:address:array:character, <span class="Constant">2</span>:address:array:character
event-loop screen:address, console:address, <span class="Constant">3</span>:address:programming-environment-data
screen-should-contain [
<span class="Constant"> . run (F4) .</span>
<span class="Constant"> . ┊ .</span>
<span class="Constant"> .recipe foo [ ┊━━━━━━━━━━━━━━━━━━━.</span>
<span class="Constant"> . add 2, 2 ┊ x.</span>
<span class="Constant"> .] ┊foo .</span>
<span class="Constant"> .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊4 .</span>
<span class="Constant"> . ┊━━━━━━━━━━━━━━━━━━━.</span>
<span class="Constant"> . ┊ .</span>
]
<span class="Comment"># click on the '4' in the result</span>
assume-console [
left-click <span class="Constant">5</span>, <span class="Constant">21</span>
]
run [
event-loop screen:address, console:address, <span class="Constant">3</span>:address:programming-environment-data
]
<span class="Comment"># color toggles to green</span>
screen-should-contain-in-color <span class="Constant">2/green</span>, [
<span class="Constant"> . .</span>
<span class="Constant"> . .</span>
<span class="Constant"> . .</span>
<span class="Constant"> . .</span>
<span class="Constant"> . .</span>
<span class="Constant"> . 4 .</span>
<span class="Constant"> . .</span>
<span class="Constant"> . .</span>
]
<span class="Comment"># now change the second arg of the 'add'</span>
<span class="Comment"># then rerun</span>
assume-console [
left-click <span class="Constant">3</span>, <span class="Constant">11</span> <span class="Comment"># cursor to end of line</span>
type <span class="Constant">[«3]</span> <span class="Comment"># turn '2' into '3'</span>
press <span class="Constant">65532</span> <span class="Comment"># F4</span>
]
<span class="Constant">4</span>:event/backspace<span class="Special"> <- </span>merge <span class="Constant">0/text</span>, <span class="Constant">8/backspace</span>, <span class="Constant">0/dummy</span>, <span class="Constant">0/dummy</span>
replace-in-console <span class="Constant">171/«</span>, <span class="Constant">4</span>:event/backspace
run [
event-loop screen:address, console:address, <span class="Constant">3</span>:address:programming-environment-data
]
<span class="Comment"># result turns red</span>
screen-should-contain-in-color <span class="Constant">1/red</span>, [
<span class="Constant"> . .</span>
<span class="Constant"> . .</span>
<span class="Constant"> . .</span>
<span class="Constant"> . .</span>
<span class="Constant"> . .</span>
<span class="Constant"> . 5 .</span>
<span class="Constant"> . .</span>
<span class="Constant"> . .</span>
]
]
<span class="Comment"># clicks on sandbox responses save it as 'expected'</span>
<span class="muRecipe">after</span> +global-touch [
<span class="Comment"># right side of screen? check if it's inside the output of any sandbox</span>
<span class="Delimiter">{</span>
sandbox-left-margin:number<span class="Special"> <- </span>get *current-sandbox, <span class="Constant">left:offset</span>
click-column:number<span class="Special"> <- </span>get *t, <span class="Constant">column:offset</span>
on-sandbox-side?:boolean<span class="Special"> <- </span>greater-or-equal click-column, sandbox-left-margin
<span class="muControl">break-unless</span> on-sandbox-side?
first-sandbox:address:sandbox-data<span class="Special"> <- </span>get *env, <span class="Constant">sandbox:offset</span>
<span class="muControl">break-unless</span> first-sandbox
first-sandbox-begins:number<span class="Special"> <- </span>get *first-sandbox, <span class="Constant">starting-row-on-screen:offset</span>
click-row:number<span class="Special"> <- </span>get *t, <span class="Constant">row:offset</span>
below-sandbox-editor?:boolean<span class="Special"> <- </span>greater-or-equal click-row, first-sandbox-begins
<span class="muControl">break-unless</span> below-sandbox-editor?
<span class="Comment"># identify the sandbox whose output is being clicked on</span>
sandbox:address:sandbox-data<span class="Special"> <- </span>find-click-in-sandbox-output env, click-row
<span class="muControl">break-unless</span> sandbox
<span class="Comment"># toggle its expected-response, and save session</span>
sandbox<span class="Special"> <- </span>toggle-expected-response sandbox
save-sandboxes env
screen<span class="Special"> <- </span>render-sandbox-side screen, env, <span class="Constant">1/clear</span>
<span class="Comment"># no change in cursor</span>
show-screen screen
<span class="muControl">loop</span> <span class="Constant">+next-event:label</span>
<span class="Delimiter">}</span>
]
<span class="muRecipe">recipe</span> find-click-in-sandbox-output [
<span class="Constant">local-scope</span>
env:address:programming-environment-data<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
click-row:number<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
<span class="Comment"># assert click-row >= sandbox.starting-row-on-screen</span>
sandbox:address:sandbox-data<span class="Special"> <- </span>get *env, <span class="Constant">sandbox:offset</span>
start:number<span class="Special"> <- </span>get *sandbox, <span class="Constant">starting-row-on-screen:offset</span>
clicked-on-sandboxes?:boolean<span class="Special"> <- </span>greater-or-equal click-row, start
assert clicked-on-sandboxes?, <span class="Constant">[extract-sandbox called on click to sandbox editor]</span>
<span class="Comment"># while click-row < sandbox.next-sandbox.starting-row-on-screen</span>
<span class="Delimiter">{</span>
next-sandbox:address:sandbox-data<span class="Special"> <- </span>get *sandbox, <span class="Constant">next-sandbox:offset</span>
<span class="muControl">break-unless</span> next-sandbox
next-start:number<span class="Special"> <- </span>get *next-sandbox, <span class="Constant">starting-row-on-screen:offset</span>
found?:boolean<span class="Special"> <- </span>lesser-than click-row, next-start
<span class="muControl">break-if</span> found?
sandbox<span class="Special"> <- </span>copy next-sandbox
<span class="muControl">loop</span>
<span class="Delimiter">}</span>
<span class="Comment"># return sandbox if click is in its output region</span>
response-starting-row:number<span class="Special"> <- </span>get *sandbox, <span class="Constant">response-starting-row-on-screen:offset</span>
click-in-response?:boolean<span class="Special"> <- </span>greater-or-equal click-row, response-starting-row
<span class="Delimiter">{</span>
<span class="muControl">break-if</span> click-in-response?
<span class="muControl">reply</span> <span class="Constant">0/no-click-in-sandbox-output</span>
<span class="Delimiter">}</span>
<span class="muControl">reply</span> sandbox
]
<span class="muRecipe">recipe</span> toggle-expected-response [
<span class="Constant">local-scope</span>
sandbox:address:sandbox-data<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
expected-response:address:address:array:character<span class="Special"> <- </span>get-address *sandbox, <span class="Constant">expected-response:offset</span>
<span class="Delimiter">{</span>
<span class="Comment"># if expected-response is set, reset</span>
<span class="muControl">break-unless</span> *expected-response
*expected-response<span class="Special"> <- </span>copy <span class="Constant">0</span>
<span class="muControl">reply</span> sandbox/same-as-ingredient:<span class="Constant">0</span>
<span class="Delimiter">}</span>
<span class="Comment"># if not, current response is the expected response</span>
response:address:array:character<span class="Special"> <- </span>get *sandbox, <span class="Constant">response:offset</span>
*expected-response<span class="Special"> <- </span>copy response
<span class="muControl">reply</span> sandbox/same-as-ingredient:<span class="Constant">0</span>
]
<span class="Comment"># when rendering a sandbox, color it in red/green if expected response exists</span>
<span class="muRecipe">after</span> +render-sandbox-response [
<span class="Delimiter">{</span>
<span class="muControl">break-unless</span> sandbox-response
expected-response:address:array:character<span class="Special"> <- </span>get *sandbox, <span class="Constant">expected-response:offset</span>
<span class="muControl">break-unless</span> expected-response <span class="Comment"># fall-through to print in grey</span>
response-is-expected?:boolean<span class="Special"> <- </span>string-equal expected-response, sandbox-response
<span class="Delimiter">{</span>
<span class="muControl">break-if</span> response-is-expected?:boolean
row, screen<span class="Special"> <- </span>render-string screen, sandbox-response, left, right, <span class="Constant">1/red</span>, row
<span class="Delimiter">}</span>
<span class="Delimiter">{</span>
<span class="muControl">break-unless</span> response-is-expected?:boolean
row, screen<span class="Special"> <- </span>render-string screen, sandbox-response, left, right, <span class="Constant">2/green</span>, row
<span class="Delimiter">}</span>
<span class="muControl">jump</span> <span class="Constant">+render-sandbox-end:label</span>
<span class="Delimiter">}</span>
]
<span class="SalientComment">## clicking on the code typed into a sandbox toggles its trace</span>
<span class="muScenario">scenario</span> sandbox-click-on-code-toggles-app-trace [
<span class="Constant"> $close-trace</span>
assume-screen <span class="Constant">40/width</span>, <span class="Constant">10/height</span>
<span class="Comment"># basic recipe</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[ </span>
<span class="Constant">recipe foo [</span>
<span class="Constant"> trace [abc]</span>
]]
<span class="Comment"># run it</span>
<span class="Constant">2</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[foo]</span>
assume-console [
press <span class="Constant">65532</span> <span class="Comment"># F4</span>
]
<span class="Constant">3</span>:address:programming-environment-data<span class="Special"> <- </span>new-programming-environment screen:address, <span class="Constant">1</span>:address:array:character, <span class="Constant">2</span>:address:array:character
event-loop screen:address, console:address, <span class="Constant">3</span>:address:programming-environment-data
screen-should-contain [
<span class="Constant"> . run (F4) .</span>
<span class="Constant"> . ┊ .</span>
<span class="Constant"> .recipe foo [ ┊━━━━━━━━━━━━━━━━━━━.</span>
<span class="Constant"> . trace [abc] ┊ x.</span>
<span class="Constant"> .] ┊foo .</span>
<span class="Constant"> .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊━━━━━━━━━━━━━━━━━━━.</span>
<span class="Constant"> . ┊ .</span>
]
<span class="Comment"># click on the 'foo' line in the sandbox</span>
assume-console [
left-click <span class="Constant">4</span>, <span class="Constant">21</span>
]
run [
event-loop screen:address, console:address, <span class="Constant">3</span>:address:programming-environment-data
]
<span class="Comment"># trace now printed</span>
screen-should-contain [
<span class="Constant"> . run (F4) .</span>
<span class="Constant"> . ┊ .</span>
<span class="Constant"> .recipe foo [ ┊━━━━━━━━━━━━━━━━━━━.</span>
<span class="Constant"> . trace [abc] ┊ x.</span>
<span class="Constant"> .] ┊foo .</span>
<span class="Constant"> .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊abc .</span>
<span class="Constant"> . ┊━━━━━━━━━━━━━━━━━━━.</span>
<span class="Constant"> . ┊ .</span>
]
screen-should-contain-in-color <span class="Constant">245/grey</span>, [
<span class="Constant"> . .</span>
<span class="Constant"> . ┊ .</span>
<span class="Constant"> . ┊━━━━━━━━━━━━━━━━━━━.</span>
<span class="Constant"> . ┊ x.</span>
<span class="Constant"> . ┊ .</span>
<span class="Constant"> .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊abc .</span>
<span class="Constant"> . ┊━━━━━━━━━━━━━━━━━━━.</span>
<span class="Constant"> . ┊ .</span>
]
<span class="Comment"># click again on the same region</span>
assume-console [
left-click <span class="Constant">4</span>, <span class="Constant">25</span>
]
run [
event-loop screen:address, console:address, <span class="Constant">3</span>:address:programming-environment-data
]
<span class="Comment"># trace hidden again</span>
screen-should-contain [
<span class="Constant"> . run (F4) .</span>
<span class="Constant"> . ┊ .</span>
<span class="Constant"> .recipe foo [ ┊━━━━━━━━━━━━━━━━━━━.</span>
<span class="Constant"> . trace [abc] ┊ x.</span>
<span class="Constant"> .] ┊foo .</span>
<span class="Constant"> .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊━━━━━━━━━━━━━━━━━━━.</span>
<span class="Constant"> . ┊ .</span>
]
]
<span class="muScenario">scenario</span> sandbox-shows-app-trace-and-result [
<span class="Constant"> $close-trace</span>
assume-screen <span class="Constant">40/width</span>, <span class="Constant">10/height</span>
<span class="Comment"># basic recipe</span>
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[ </span>
<span class="Constant">recipe foo [</span>
<span class="Constant"> trace [abc]</span>
add <span class="Constant">2</span>, <span class="Constant">2</span>
]]
<span class="Comment"># run it</span>
<span class="Constant">2</span>:address:array:character<span class="Special"> <- </span>new <span class="Constant">[foo]</span>
assume-console [
press <span class="Constant">65532</span> <span class="Comment"># F4</span>
]
<span class="Constant">3</span>:address:programming-environment-data<span class="Special"> <- </span>new-programming-environment screen:address, <span class="Constant">1</span>:address:array:character, <span class="Constant">2</span>:address:array:character
event-loop screen:address, console:address, <span class="Constant">3</span>:address:programming-environment-data
screen-should-contain [
<span class="Constant"> . run (F4) .</span>
<span class="Constant"> . ┊ .</span>
<span class="Constant"> .recipe foo [ ┊━━━━━━━━━━━━━━━━━━━.</span>
<span class="Constant"> . trace [abc] ┊ x.</span>
<span class="Constant"> . add 2, 2 ┊foo .</span>
<span class="Constant"> .] ┊4 .</span>
<span class="Constant"> .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊━━━━━━━━━━━━━━━━━━━.</span>
<span class="Constant"> . ┊ .</span>
]
<span class="Comment"># click on the 'foo' line in the sandbox</span>
assume-console [
left-click <span class="Constant">4</span>, <span class="Constant">21</span>
]
run [
event-loop screen:address, console:address, <span class="Constant">3</span>:address:programming-environment-data
]
<span class="Comment"># trace now printed</span>
screen-should-contain [
<span class="Constant"> . run (F4) .</span>
<span class="Constant"> . ┊ .</span>
<span class="Constant"> .recipe foo [ ┊━━━━━━━━━━━━━━━━━━━.</span>
<span class="Constant"> . trace [abc] ┊ x.</span>
<span class="Constant"> . add 2, 2 ┊foo .</span>
<span class="Constant"> .] ┊abc .</span>
<span class="Constant"> .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊4 .</span>
<span class="Constant"> . ┊━━━━━━━━━━━━━━━━━━━.</span>
<span class="Constant"> . ┊ .</span>
]
]
<span class="Comment"># clicks on sandbox code toggle its display-trace? flag</span>
<span class="muRecipe">after</span> +global-touch [
<span class="Comment"># right side of screen? check if it's inside the code of any sandbox</span>
<span class="Delimiter">{</span>
sandbox-left-margin:number<span class="Special"> <- </span>get *current-sandbox, <span class="Constant">left:offset</span>
click-column:number<span class="Special"> <- </span>get *t, <span class="Constant">column:offset</span>
on-sandbox-side?:boolean<span class="Special"> <- </span>greater-or-equal click-column, sandbox-left-margin
<span class="muControl">break-unless</span> on-sandbox-side?
first-sandbox:address:sandbox-data<span class="Special"> <- </span>get *env, <span class="Constant">sandbox:offset</span>
<span class="muControl">break-unless</span> first-sandbox
first-sandbox-begins:number<span class="Special"> <- </span>get *first-sandbox, <span class="Constant">starting-row-on-screen:offset</span>
click-row:number<span class="Special"> <- </span>get *t, <span class="Constant">row:offset</span>
below-sandbox-editor?:boolean<span class="Special"> <- </span>greater-or-equal click-row, first-sandbox-begins
<span class="muControl">break-unless</span> below-sandbox-editor?
<span class="Comment"># identify the sandbox whose code is being clicked on</span>
sandbox:address:sandbox-data<span class="Special"> <- </span>find-click-in-sandbox-code env, click-row
<span class="muControl">break-unless</span> sandbox
<span class="Comment"># toggle its display-trace? property</span>
x:address:boolean<span class="Special"> <- </span>get-address *sandbox, <span class="Constant">display-trace?:offset</span>
*x<span class="Special"> <- </span>not *x
screen<span class="Special"> <- </span>render-sandbox-side screen, env, <span class="Constant">1/clear</span>
<span class="Comment"># no change in cursor</span>
show-screen screen
<span class="muControl">loop</span> <span class="Constant">+next-event:label</span>
<span class="Delimiter">}</span>
]
<span class="muRecipe">recipe</span> find-click-in-sandbox-code [
<span class="Constant">local-scope</span>
env:address:programming-environment-data<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
click-row:number<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
<span class="Comment"># assert click-row >= sandbox.starting-row-on-screen</span>
sandbox:address:sandbox-data<span class="Special"> <- </span>get *env, <span class="Constant">sandbox:offset</span>
start:number<span class="Special"> <- </span>get *sandbox, <span class="Constant">starting-row-on-screen:offset</span>
clicked-on-sandboxes?:boolean<span class="Special"> <- </span>greater-or-equal click-row, start
assert clicked-on-sandboxes?, <span class="Constant">[extract-sandbox called on click to sandbox editor]</span>
<span class="Comment"># while click-row < sandbox.next-sandbox.starting-row-on-screen</span>
<span class="Delimiter">{</span>
next-sandbox:address:sandbox-data<span class="Special"> <- </span>get *sandbox, <span class="Constant">next-sandbox:offset</span>
<span class="muControl">break-unless</span> next-sandbox
next-start:number<span class="Special"> <- </span>get *next-sandbox, <span class="Constant">starting-row-on-screen:offset</span>
found?:boolean<span class="Special"> <- </span>lesser-than click-row, next-start
<span class="muControl">break-if</span> found?
sandbox<span class="Special"> <- </span>copy next-sandbox
<span class="muControl">loop</span>
<span class="Delimiter">}</span>
<span class="Comment"># return sandbox if click is in its code region</span>
response-starting-row:number<span class="Special"> <- </span>get *sandbox, <span class="Constant">response-starting-row-on-screen:offset</span>
click-above-response?:boolean<span class="Special"> <- </span>lesser-than click-row, response-starting-row
start:number<span class="Special"> <- </span>get *sandbox, <span class="Constant">starting-row-on-screen:offset</span>
click-below-menu?:boolean<span class="Special"> <- </span>greater-than click-row, start
click-on-sandbox-code?:boolean<span class="Special"> <- </span>and click-above-response?, click-below-menu?
<span class="Delimiter">{</span>
<span class="muControl">break-if</span> click-on-sandbox-code?
<span class="muControl">reply</span> <span class="Constant">0/no-click-in-sandbox-output</span>
<span class="Delimiter">}</span>
<span class="muControl">reply</span> sandbox
]
<span class="Comment"># when rendering a sandbox, dump its trace before response/warning if display-trace? property is set</span>
<span class="muRecipe">after</span> +render-sandbox-results [
<span class="Delimiter">{</span>
display-trace?:boolean<span class="Special"> <- </span>get *sandbox, <span class="Constant">display-trace?:offset</span>
<span class="muControl">break-unless</span> display-trace?
sandbox-trace:address:array:character<span class="Special"> <- </span>get *sandbox, <span class="Constant">trace:offset</span>
<span class="muControl">break-unless</span> sandbox-trace <span class="Comment"># nothing to print; move on</span>
<span class="CommentedCode">#? $print [display trace from ], row, 10/newline #? 1</span>
row, screen<span class="Special"> <- </span>render-string, screen, sandbox-trace, left, right, <span class="Constant">245/grey</span>, row
row<span class="Special"> <- </span>subtract row, <span class="Constant">1</span> <span class="Comment"># trim the trailing newline that's always present</span>
<span class="Delimiter">}</span>
]
<span class="SalientComment">## handling malformed programs</span>
<span class="muScenario">scenario</span> run-shows-warnings-in-get [
<span class="Constant"> $close-trace</span>
assume-screen <span class="Constant">100/width</span>, <span class="Constant">15/height</span>
assume-console [
press <span class="Constant">65532</span> <span class="Comment"># F4</span>
]
run [
x:address:array:character<span class="Special"> <- </span>new <span class="Constant">[ </span>
<span class="Constant">recipe foo [</span>
<span class="Constant"> get 123:number, foo:offset</span>
<span class="Constant">]</span>]
y:address:array:character<span class="Special"> <- </span>new <span class="Constant">[foo]</span>
env:address:programming-environment-data<span class="Special"> <- </span>new-programming-environment screen:address, x:address:array:character, y:address:array:character
event-loop screen:address, console:address, env:address:programming-environment-data
]
screen-should-contain [
<span class="Constant"> . run (F4) .</span>
<span class="Constant"> . ┊foo .</span>
<span class="Constant"> .recipe foo [ ┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.</span>
<span class="Constant"> . get 123:number, foo:offset ┊ .</span>
<span class="Constant"> .] ┊ .</span>
<span class="Constant"> .unknown element foo in container number ┊ .</span>
<span class="Constant"> .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊ .</span>
<span class="Constant"> . ┊ .</span>
]
screen-should-contain-in-color <span class="Constant">1/red</span>, [
<span class="Constant"> . .</span>
<span class="Constant"> . .</span>
<span class="Constant"> . .</span>
<span class="Constant"> . .</span>
<span class="Constant"> . .</span>
<span class="Constant"> .unknown element foo in container number .</span>
<span class="Constant"> . .</span>
]
]
<span class="muScenario">scenario</span> run-shows-missing-type-warnings [
<span class="Constant"> $close-trace</span>
assume-screen <span class="Constant">100/width</span>, <span class="Constant">15/height</span>
assume-console [
press <span class="Constant">65532</span> <span class="Comment"># F4</span>
]
run [
x:address:array:character<span class="Special"> <- </span>new <span class="Constant">[ </span>
<span class="Constant">recipe foo [</span>
<span class="Constant"> x <- copy 0</span>
<span class="Constant">]</span>]
y:address:array:character<span class="Special"> <- </span>new <span class="Constant">[foo]</span>
env:address:programming-environment-data<span class="Special"> <- </span>new-programming-environment screen:address, x:address:array:character, y:address:array:character
event-loop screen:address, console:address, env:address:programming-environment-data
]
screen-should-contain [
<span class="Constant"> . run (F4) .</span>
<span class="Constant"> . ┊foo .</span>
<span class="Constant"> .recipe foo [ ┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.</span>
<span class="Constant"> . x <- copy 0 ┊ .</span>
<span class="Constant"> .] ┊ .</span>
<span class="Constant"> .missing type in 'x <- copy 0' ┊ .</span>
<span class="Constant"> .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊ .</span>
<span class="Constant"> . ┊ .</span>
]
]
<span class="muScenario">scenario</span> run-shows-get-on-non-container-warnings [
<span class="Constant"> $close-trace</span>
assume-screen <span class="Constant">100/width</span>, <span class="Constant">15/height</span>
assume-console [
press <span class="Constant">65532</span> <span class="Comment"># F4</span>
]
run [
x:address:array:character<span class="Special"> <- </span>new <span class="Constant">[ </span>
<span class="Constant">recipe foo [</span>
<span class="Constant"> x:address:point <- new point:type</span>
<span class="Constant"> get x:address:point, 1:offset</span>
<span class="Constant">]</span>]
y:address:array:character<span class="Special"> <- </span>new <span class="Constant">[foo]</span>
env:address:programming-environment-data<span class="Special"> <- </span>new-programming-environment screen:address, x:address:array:character, y:address:array:character
event-loop screen:address, console:address, env:address:programming-environment-data
]
screen-should-contain [
<span class="Constant"> . run (F4) .</span>
<span class="Constant"> . ┊ .</span>
<span class="Constant"> .recipe foo [ ┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.</span>
<span class="Constant"> . x:address:point <- new point:type ┊ x.</span>
<span class="Constant"> . get x:address:point, 1:offset ┊foo .</span>
<span class="Constant"> .] ┊foo: first ingredient of 'get' should be a conta↩.</span>
<span class="Constant"> .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊iner, but got x:address:point .</span>
<span class="Constant"> . ┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.</span>
<span class="Constant"> . ┊ .</span>
]
]
<span class="muScenario">scenario</span> run-shows-non-literal-get-argument-warnings [
<span class="Constant"> $close-trace</span>
assume-screen <span class="Constant">100/width</span>, <span class="Constant">15/height</span>
assume-console [
press <span class="Constant">65532</span> <span class="Comment"># F4</span>
]
run [
x:address:array:character<span class="Special"> <- </span>new <span class="Constant">[ </span>
<span class="Constant">recipe foo [</span>
<span class="Constant"> x:number <- copy 0</span>
<span class="Constant"> y:address:point <- new point:type</span>
<span class="Constant"> get *y:address:point, x:number</span>
<span class="Constant">]</span>]
y:address:array:character<span class="Special"> <- </span>new <span class="Constant">[foo]</span>
env:address:programming-environment-data<span class="Special"> <- </span>new-programming-environment screen:address, x:address:array:character, y:address:array:character
event-loop screen:address, console:address, env:address:programming-environment-data
]
screen-should-contain [
<span class="Constant"> . run (F4) .</span>
<span class="Constant"> . ┊foo .</span>
<span class="Constant"> .recipe foo [ ┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.</span>
<span class="Constant"> . x:number <- copy 0 ┊ .</span>
<span class="Constant"> . y:address:point <- new point:type ┊ .</span>
<span class="Constant"> . get *y:address:point, x:number ┊ .</span>
<span class="Constant"> .] ┊ .</span>
<span class="Constant"> .foo: expected ingredient 1 of 'get' to have type ↩┊ .</span>
<span class="Constant"> .'offset'; got x:number ┊ .</span>
<span class="Constant"> .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊ .</span>
<span class="Constant"> . ┊ .</span>
]
]
<span class="SalientComment">## helpers for drawing editor borders</span>
<span class="muRecipe">recipe</span> draw-box [
<span class="Constant">local-scope</span>
screen:address<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
top:number<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
left:number<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
bottom:number<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
right:number<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
color:number, color-found?:boolean<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
<span class="Delimiter">{</span>
<span class="Comment"># default color to white</span>
<span class="muControl">break-if</span> color-found?
color<span class="Special"> <- </span>copy <span class="Constant">245/grey</span>
<span class="Delimiter">}</span>
<span class="Comment"># top border</span>
draw-horizontal screen, top, left, right, color
draw-horizontal screen, bottom, left, right, color
draw-vertical screen, left, top, bottom, color
draw-vertical screen, right, top, bottom, color
draw-top-left screen, top, left, color
draw-top-right screen, top, right, color
draw-bottom-left screen, bottom, left, color
draw-bottom-right screen, bottom, right, color
<span class="Comment"># position cursor inside box</span>
move-cursor screen, top, left
cursor-down screen
cursor-right screen
]
<span class="muRecipe">recipe</span> draw-horizontal [
<span class="Constant">local-scope</span>
screen:address<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
row:number<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
x:number<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
right:number<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
style:character, style-found?:boolean<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
<span class="Delimiter">{</span>
<span class="muControl">break-if</span> style-found?
style<span class="Special"> <- </span>copy <span class="Constant">9472/horizontal</span>
<span class="Delimiter">}</span>
color:number, color-found?:boolean<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
<span class="Delimiter">{</span>
<span class="Comment"># default color to white</span>
<span class="muControl">break-if</span> color-found?
color<span class="Special"> <- </span>copy <span class="Constant">245/grey</span>
<span class="Delimiter">}</span>
bg-color:number, bg-color-found?:boolean<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
<span class="Delimiter">{</span>
<span class="muControl">break-if</span> bg-color-found?
bg-color<span class="Special"> <- </span>copy <span class="Constant">0/black</span>
<span class="Delimiter">}</span>
move-cursor screen, row, x
<span class="Delimiter">{</span>
continue?:boolean<span class="Special"> <- </span>lesser-or-equal x, right <span class="Comment"># right is inclusive, to match editor-data semantics</span>
<span class="muControl">break-unless</span> continue?
print-character screen, style, color, bg-color
x<span class="Special"> <- </span>add x, <span class="Constant">1</span>
<span class="muControl">loop</span>
<span class="Delimiter">}</span>
]
<span class="muRecipe">recipe</span> draw-vertical [
<span class="Constant">local-scope</span>
screen:address<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
col:number<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
y:number<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
bottom:number<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
style:character, style-found?:boolean<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
<span class="Delimiter">{</span>
<span class="muControl">break-if</span> style-found?
style<span class="Special"> <- </span>copy <span class="Constant">9474/vertical</span>
<span class="Delimiter">}</span>
color:number, color-found?:boolean<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
<span class="Delimiter">{</span>
<span class="Comment"># default color to white</span>
<span class="muControl">break-if</span> color-found?
color<span class="Special"> <- </span>copy <span class="Constant">245/grey</span>
<span class="Delimiter">}</span>
<span class="Delimiter">{</span>
continue?:boolean<span class="Special"> <- </span>lesser-than y, bottom
<span class="muControl">break-unless</span> continue?
move-cursor screen, y, col
print-character screen, style, color
y<span class="Special"> <- </span>add y, <span class="Constant">1</span>
<span class="muControl">loop</span>
<span class="Delimiter">}</span>
]
<span class="muRecipe">recipe</span> draw-top-left [
<span class="Constant">local-scope</span>
screen:address<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
top:number<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
left:number<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
color:number, color-found?:boolean<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
<span class="Delimiter">{</span>
<span class="Comment"># default color to white</span>
<span class="muControl">break-if</span> color-found?
color<span class="Special"> <- </span>copy <span class="Constant">245/grey</span>
<span class="Delimiter">}</span>
move-cursor screen, top, left
print-character screen, <span class="Constant">9484/down-right</span>, color
]
<span class="muRecipe">recipe</span> draw-top-right [
<span class="Constant">local-scope</span>
screen:address<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
top:number<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
right:number<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
color:number, color-found?:boolean<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
<span class="Delimiter">{</span>
<span class="Comment"># default color to white</span>
<span class="muControl">break-if</span> color-found?
color<span class="Special"> <- </span>copy <span class="Constant">245/grey</span>
<span class="Delimiter">}</span>
move-cursor screen, top, right
print-character screen, <span class="Constant">9488/down-left</span>, color
]
<span class="muRecipe">recipe</span> draw-bottom-left [
<span class="Constant">local-scope</span>
screen:address<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
bottom:number<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
left:number<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
color:number, color-found?:boolean<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
<span class="Delimiter">{</span>
<span class="Comment"># default color to white</span>
<span class="muControl">break-if</span> color-found?
color<span class="Special"> <- </span>copy <span class="Constant">245/grey</span>
<span class="Delimiter">}</span>
move-cursor screen, bottom, left
print-character screen, <span class="Constant">9492/up-right</span>, color
]
<span class="muRecipe">recipe</span> draw-bottom-right [
<span class="Constant">local-scope</span>
screen:address<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
bottom:number<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
right:number<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
color:number, color-found?:boolean<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
<span class="Delimiter">{</span>
<span class="Comment"># default color to white</span>
<span class="muControl">break-if</span> color-found?
color<span class="Special"> <- </span>copy <span class="Constant">245/grey</span>
<span class="Delimiter">}</span>
move-cursor screen, bottom, right
print-character screen, <span class="Constant">9496/up-left</span>, color
]
<span class="muRecipe">recipe</span> print-string-with-gradient-background [
<span class="Constant">local-scope</span>
screen:address<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
s:address:array:character<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
color:number<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
bg-color1:number<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
bg-color2:number<span class="Special"> <- </span><span class="Constant">next-ingredient</span>
len:number<span class="Special"> <- </span>length *s
color-range:number<span class="Special"> <- </span>subtract bg-color2, bg-color1
color-quantum:number<span class="Special"> <- </span>divide color-range, len
bg-color:number<span class="Special"> <- </span>copy bg-color1
i:number<span class="Special"> <- </span>copy <span class="Constant">0</span>
<span class="Delimiter">{</span>
done?:boolean<span class="Special"> <- </span>greater-or-equal i, len
<span class="muControl">break-if</span> done?
c:character<span class="Special"> <- </span>index *s, i
print-character screen, c, color, bg-color
i<span class="Special"> <- </span>add i, <span class="Constant">1</span>
bg-color<span class="Special"> <- </span>add bg-color, color-quantum
<span class="muControl">loop</span>
<span class="Delimiter">}</span>
<span class="muControl">reply</span> screen/same-as-ingredient:<span class="Constant">0</span>
]
</pre>
</body>
</html>
<!-- vim: set foldmethod=manual : -->