diff options
Diffstat (limited to 'edit/005-sandbox.mu')
-rw-r--r-- | edit/005-sandbox.mu | 683 |
1 files changed, 683 insertions, 0 deletions
diff --git a/edit/005-sandbox.mu b/edit/005-sandbox.mu new file mode 100644 index 00000000..239112ce --- /dev/null +++ b/edit/005-sandbox.mu @@ -0,0 +1,683 @@ +## running code from the editor and creating sandboxes + +container sandbox-data [ + data:address:array:character + response:address:array:character + warnings:address:array:character + trace:address:array:character + expected-response:address:array:character + # coordinates to track clicks + starting-row-on-screen:number + code-ending-row-on-screen:number + response-starting-row-on-screen:number + display-trace?:boolean + screen:address:screen # prints in the sandbox go here + next-sandbox:address:sandbox-data +] + +scenario run-and-show-results [ + $close-trace # trace too long + assume-screen 100/width, 15/height + # recipe editor is empty + 1:address:array:character <- new [] + # sandbox editor contains an instruction without storing outputs + 2:address:array:character <- new [divide-with-remainder 11, 3] + 3:address:programming-environment-data <- new-programming-environment screen:address, 1:address:array:character, 2:address:array:character + # run the code in the editors + assume-console [ + press F4 + ] + run [ + event-loop screen:address, console:address, 3:address:programming-environment-data + ] + # check that screen prints the results + screen-should-contain [ + . run (F4) . + . ┊ . + .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━. + . ┊ x. + . ┊divide-with-remainder 11, 3 . + . ┊3 . + . ┊2 . + . ┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━. + . ┊ . + ] + screen-should-contain-in-color 7/white, [ + . . + . . + . . + . . + . divide-with-remainder 11, 3 . + . . + . . + . . + . . + ] + screen-should-contain-in-color 245/grey, [ + . . + . ┊ . + .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━. + . ┊ x. + . ┊ . + . ┊3 . + . ┊2 . + . ┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━. + . ┊ . + ] + # run another command + assume-console [ + left-click 1, 80 + type [add 2, 2] + press F4 + ] + run [ + event-loop screen:address, console:address, 3:address:programming-environment-data + ] + # check that screen prints the results + screen-should-contain [ + . run (F4) . + . ┊ . + .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━. + . ┊ x. + . ┊add 2, 2 . + . ┊4 . + . ┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━. + . ┊ x. + . ┊divide-with-remainder 11, 3 . + . ┊3 . + . ┊2 . + . ┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━. + . ┊ . + ] +] + +# hook into event-loop recipe: read non-unicode keypress from k, process it if +# necessary, then go to next level +after <global-keypress> [ + # F4? load all code and run all sandboxes. + { + do-run?:boolean <- equal *k, 65532/F4 + break-unless do-run? + status:address:array:character <- new [running... ] + screen <- update-status screen, status, 245/grey + screen, error?:boolean <- run-sandboxes env, screen + # F4 might update warnings and results on both sides + screen <- render-all screen, env + { + break-if error? + status:address:array:character <- new [ ] + screen <- update-status screen, status, 245/grey + } + screen <- update-cursor screen, recipes, current-sandbox, *sandbox-in-focus? + loop +next-event:label + } +] + +recipe run-sandboxes [ + local-scope + env:address:programming-environment-data <- next-ingredient + screen:address <- next-ingredient + recipes:address:editor-data <- get *env, recipes:offset + # copy code from recipe editor, persist, load into mu, save any warnings + in:address:array:character <- editor-contents recipes + save [recipes.mu], in + recipe-warnings:address:address:array:character <- get-address *env, recipe-warnings:offset + *recipe-warnings <- reload in + # if recipe editor has errors, stop + { + break-unless *recipe-warnings + status:address:array:character <- new [errors found] + update-status screen, status, 1/red + reply screen/same-as-ingredient:1, 1/errors-found + } + # check contents of right editor (sandbox) + current-sandbox:address:editor-data <- get *env, current-sandbox:offset + { + sandbox-contents:address:array:character <- editor-contents current-sandbox + break-unless sandbox-contents + # if contents exist, first save them + # run them and turn them into a new sandbox-data + new-sandbox:address:sandbox-data <- new sandbox-data:type + data:address:address:array:character <- get-address *new-sandbox, data:offset + *data <- copy sandbox-contents + # push to head of sandbox list + dest:address:address:sandbox-data <- get-address *env, sandbox:offset + next:address:address:sandbox-data <- get-address *new-sandbox, next-sandbox:offset + *next <- copy *dest + *dest <- copy new-sandbox + # clear sandbox editor + init:address:address:duplex-list <- get-address *current-sandbox, data:offset + *init <- push-duplex 167/§, 0/tail + top-of-screen:address:address:duplex-list <- get-address *current-sandbox, top-of-screen:offset + *top-of-screen <- copy *init + } + # save all sandboxes before running, just in case we die when running + save-sandboxes env + # run all sandboxes + curr:address:sandbox-data <- get *env, sandbox:offset + { + break-unless curr + data <- get-address *curr, data:offset + response:address:address:array:character <- get-address *curr, response:offset + warnings:address:address:array:character <- get-address *curr, warnings:offset + trace:address:address:array:character <- get-address *curr, trace:offset + fake-screen:address:address:screen <- get-address *curr, screen:offset + *response, *warnings, *fake-screen, *trace, completed?:boolean <- run-interactive *data + { + break-if *warnings + break-if completed?:boolean + *warnings <- new [took too long! +] + } + curr <- get *curr, next-sandbox:offset + loop + } + reply screen/same-as-ingredient:1, 0/no-errors-found +] + +recipe update-status [ + local-scope + screen:address <- next-ingredient + msg:address:array:character <- next-ingredient + color:number <- next-ingredient + screen <- move-cursor screen, 0, 2 + screen <- print-string screen, msg, color, 238/grey/background + reply screen/same-as-ingredient:0 +] + +recipe save-sandboxes [ + local-scope + env:address:programming-environment-data <- next-ingredient + current-sandbox:address:editor-data <- get *env, current-sandbox:offset + # first clear previous versions, in case we deleted some sandbox + $system [rm lesson/[0-9]* >/dev/null 2>/dev/null] # some shells can't handle '>&' + curr:address:sandbox-data <- get *env, sandbox:offset + suffix:address:array:character <- new [.out] + idx:number <- copy 0 + { + break-unless curr + data:address:array:character <- get *curr, data:offset + filename:address:array:character <- integer-to-decimal-string idx + save filename, data + { + expected-response:address:array:character <- get *curr, expected-response:offset + break-unless expected-response + filename <- string-append filename, suffix + save filename, expected-response + } + idx <- add idx, 1 + curr <- get *curr, next-sandbox:offset + loop + } +] + +recipe render-sandbox-side [ + local-scope + screen:address <- next-ingredient + env:address:programming-environment-data <- next-ingredient + trace 11, [app], [render sandbox side] + current-sandbox:address:editor-data <- get *env, current-sandbox:offset + left:number <- get *current-sandbox, left:offset + right:number <- get *current-sandbox, right:offset + row:number, column:number, screen, current-sandbox <- render screen, current-sandbox + clear-screen-from screen, row, column, left, right + row <- add row, 1 + draw-horizontal screen, row, left, right, 9473/horizontal-double + sandbox:address:sandbox-data <- get *env, sandbox:offset + row, screen <- render-sandboxes screen, sandbox, left, right, row + clear-rest-of-screen screen, row, left, left, right + reply screen/same-as-ingredient:0 +] + +recipe render-sandboxes [ + local-scope + screen:address <- next-ingredient + sandbox:address:sandbox-data <- next-ingredient + left:number <- next-ingredient + right:number <- next-ingredient + row:number <- next-ingredient + reply-unless sandbox, row/same-as-ingredient:4, screen/same-as-ingredient:0 + screen-height:number <- screen-height screen + at-bottom?:boolean <- greater-or-equal row, screen-height + reply-if at-bottom?:boolean, row/same-as-ingredient:4, screen/same-as-ingredient:0 + # render sandbox menu + row <- add row, 1 + screen <- move-cursor screen, row, left + clear-line-delimited screen, left, right + print-character screen, 120/x, 245/grey + # save menu row so we can detect clicks to it later + starting-row:address:number <- get-address *sandbox, starting-row-on-screen:offset + *starting-row <- copy row + # render sandbox contents + sandbox-data:address:array:character <- get *sandbox, data:offset + row, screen <- render-code-string screen, sandbox-data, left, right, row + code-ending-row:address:number <- get-address *sandbox, code-ending-row-on-screen:offset + *code-ending-row <- copy row + # render sandbox warnings, screen or response, in that order + response-starting-row:address:number <- get-address *sandbox, response-starting-row-on-screen:offset + sandbox-response:address:array:character <- get *sandbox, response:offset + sandbox-warnings:address:array:character <- get *sandbox, warnings:offset + sandbox-screen:address <- get *sandbox, screen:offset + <render-sandbox-results> + { + break-unless sandbox-warnings + *response-starting-row <- copy 0 # no response + row, screen <- render-string screen, sandbox-warnings, left, right, 1/red, row + } + { + break-if sandbox-warnings + empty-screen?:boolean <- fake-screen-is-empty? sandbox-screen + break-if empty-screen? + row, screen <- render-screen screen, sandbox-screen, left, right, row + } + { + break-if sandbox-warnings + break-unless empty-screen? + *response-starting-row <- add row, 1 + <render-sandbox-response> + row, screen <- render-string screen, sandbox-response, left, right, 245/grey, row + } + +render-sandbox-end + at-bottom?:boolean <- greater-or-equal row, screen-height + reply-if at-bottom?, row/same-as-ingredient:4, screen/same-as-ingredient:0 + # draw solid line after sandbox + draw-horizontal screen, row, left, right, 9473/horizontal-double + # draw next sandbox + next-sandbox:address:sandbox-data <- get *sandbox, next-sandbox:offset + row, screen <- render-sandboxes screen, next-sandbox, left, right, row + reply row/same-as-ingredient:4, screen/same-as-ingredient:0 +] + +# assumes programming environment has no sandboxes; restores them from previous session +recipe restore-sandboxes [ + local-scope + env:address:programming-environment-data <- next-ingredient + # read all scenarios, pushing them to end of a list of scenarios + suffix:address:array:character <- new [.out] + idx:number <- copy 0 + curr:address:address:sandbox-data <- get-address *env, sandbox:offset + { + filename:address:array:character <- integer-to-decimal-string idx + contents:address:array:character <- restore filename + break-unless contents # stop at first error; assuming file didn't exist + # create new sandbox for file + *curr <- new sandbox-data:type + data:address:address:array:character <- get-address **curr, data:offset + *data <- copy contents + # restore expected output for sandbox if it exists + { + filename <- string-append filename, suffix + contents <- restore filename + break-unless contents + expected-response:address:address:array:character <- get-address **curr, expected-response:offset + *expected-response <- copy contents + } + +continue + idx <- add idx, 1 + curr <- get-address **curr, next-sandbox:offset + loop + } + reply env/same-as-ingredient:0 +] + +# row, screen <- render-screen screen:address, sandbox-screen:address, left:number, right:number, row:number +# print the fake sandbox screen to 'screen' with appropriate delimiters +# leave cursor at start of next line +recipe render-screen [ + local-scope + screen:address <- next-ingredient + s:address:screen <- next-ingredient + left:number <- next-ingredient + right:number <- next-ingredient + row:number <- next-ingredient + row <- add row, 1 + reply-unless s, row/same-as-ingredient:4, screen/same-as-ingredient:0 + # print 'screen:' + header:address:array:character <- new [screen:] + row <- subtract row, 1 # compensate for render-string below + row <- render-string screen, header, left, right, 245/grey, row + # newline + row <- add row, 1 + screen <- move-cursor screen, row, left + # start printing s + column:number <- copy left + s-width:number <- screen-width s + s-height:number <- screen-height s + buf:address:array:screen-cell <- get *s, data:offset + stop-printing:number <- add left, s-width, 3 + max-column:number <- min stop-printing, right + i:number <- copy 0 + len:number <- length *buf + screen-height:number <- screen-height screen + { + done?:boolean <- greater-or-equal i, len + break-if done? + done? <- greater-or-equal row, screen-height + break-if done? + column <- copy left + screen <- move-cursor screen, row, column + # initial leader for each row: two spaces and a '.' + print-character screen, 32/space, 245/grey + print-character screen, 32/space, 245/grey + print-character screen, 46/full-stop, 245/grey + column <- add left, 3 + { + # print row + row-done?:boolean <- greater-or-equal column, max-column + break-if row-done? + curr:screen-cell <- index *buf, i + c:character <- get curr, contents:offset + color:number <- get curr, color:offset + { + # damp whites down to grey + white?:boolean <- equal color, 7/white + break-unless white? + color <- copy 245/grey + } + print-character screen, c, color + column <- add column, 1 + i <- add i, 1 + loop + } + # print final '.' + print-character screen, 46/full-stop, 245/grey + column <- add column, 1 + { + # clear rest of current line + line-done?:boolean <- greater-than column, right + break-if line-done? + print-character screen, 32/space + column <- add column, 1 + loop + } + row <- add row, 1 + loop + } + reply row/same-as-ingredient:4, screen/same-as-ingredient:0 +] + +scenario run-updates-results [ + $close-trace # trace too long + assume-screen 100/width, 12/height + # define a recipe (no indent for the 'add' line below so column numbers are more obvious) + 1:address:array:character <- new [ +recipe foo [ +z:number <- add 2, 2 +]] + # sandbox editor contains an instruction without storing outputs + 2:address:array:character <- new [foo] + 3:address:programming-environment-data <- new-programming-environment screen:address, 1:address:array:character, 2:address:array:character + # run the code in the editors + assume-console [ + press F4 + ] + run [ + event-loop screen:address, console:address, 3:address:programming-environment-data + ] + # check that screen prints the results + screen-should-contain [ + . run (F4) . + . ┊ . + .recipe foo [ ┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━. + .z:number <- add 2, 2 ┊ x. + .] ┊foo . + .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊4 . + . ┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━. + . ┊ . + ] + # make a change (incrementing one of the args to 'add'), then rerun + assume-console [ + left-click 3, 28 # one past the value of the second arg + press backspace + type [3] + press F4 + ] + run [ + event-loop screen:address, console:address, 3:address:programming-environment-data + ] + # check that screen updates the result on the right + screen-should-contain [ + . run (F4) . + . ┊ . + .recipe foo [ ┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━. + .z:number <- add 2, 3 ┊ x. + .] ┊foo . + .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊5 . + . ┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━. + . ┊ . + ] +] + +scenario run-instruction-and-print-warnings [ + $close-trace # trace too long + assume-screen 100/width, 10/height + # left editor is empty + 1:address:array:character <- new [] + # right editor contains an illegal instruction + 2:address:array:character <- new [get 1234:number, foo:offset] + 3:address:programming-environment-data <- new-programming-environment screen:address, 1:address:array:character, 2:address:array:character + # run the code in the editors + assume-console [ + press F4 + ] + run [ + event-loop screen:address, console:address, 3:address:programming-environment-data + ] + # check that screen prints error message in red + screen-should-contain [ + . run (F4) . + . ┊ . + .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━. + . ┊ x. + . ┊get 1234:number, foo:offset . + . ┊unknown element foo in container number . + . ┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━. + . ┊ . + ] + screen-should-contain-in-color 7/white, [ + . . + . . + . . + . . + . get 1234:number, foo:offset . + . . + . . + . . + ] + screen-should-contain-in-color 1/red, [ + . . + . . + . . + . . + . . + . unknown element foo in container number . + . . + ] + screen-should-contain-in-color 245/grey, [ + . . + . ┊ . + .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━. + . ┊ x. + . ┊ . + . ┊ . + . ┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━. + . ┊ . + ] +] + +scenario run-instruction-and-print-warnings-only-once [ + $close-trace # trace too long + assume-screen 100/width, 10/height + # left editor is empty + 1:address:array:character <- new [] + # right editor contains an illegal instruction + 2:address:array:character <- new [get 1234:number, foo:offset] + 3:address:programming-environment-data <- new-programming-environment screen:address, 1:address:array:character, 2:address:array:character + # run the code in the editors multiple times + assume-console [ + press F4 + press F4 + ] + run [ + event-loop screen:address, console:address, 3:address:programming-environment-data + ] + # check that screen prints error message just once + screen-should-contain [ + . run (F4) . + . ┊ . + .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━. + . ┊ x. + . ┊get 1234:number, foo:offset . + . ┊unknown element foo in container number . + . ┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━. + . ┊ . + ] +] + +scenario run-instruction-manages-screen-per-sandbox [ + $close-trace # trace too long + assume-screen 100/width, 20/height + # left editor is empty + 1:address:array:character <- new [] + # right editor contains an instruction + 2:address:array:character <- new [print-integer screen:address, 4] + 3:address:programming-environment-data <- new-programming-environment screen:address, 1:address:array:character, 2:address:array:character + # run the code in the editor + assume-console [ + press F4 + ] + run [ + event-loop screen:address, console:address, 3:address:programming-environment-data + ] + # check that it prints a little toy screen + screen-should-contain [ + . run (F4) . + . ┊ . + .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━. + . ┊ x. + . ┊print-integer screen:address, 4 . + . ┊screen: . + . ┊ .4 . . + . ┊ . . . + . ┊ . . . + . ┊ . . . + . ┊ . . . + . ┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━. + . ┊ . + ] +] + +scenario sandbox-with-print-can-be-edited [ + $close-trace # trace too long + assume-screen 100/width, 20/height + # left editor is empty + 1:address:array:character <- new [] + # right editor contains an instruction + 2:address:array:character <- new [print-integer screen:address, 4] + 3:address:programming-environment-data <- new-programming-environment screen:address, 1:address:array:character, 2:address:array:character + # run the sandbox + assume-console [ + press F4 + ] + run [ + event-loop screen:address, console:address, 3:address:programming-environment-data + ] + screen-should-contain [ + . run (F4) . + . ┊ . + .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━. + . ┊ x. + . ┊print-integer screen:address, 4 . + . ┊screen: . + . ┊ .4 . . + . ┊ . . . + . ┊ . . . + . ┊ . . . + . ┊ . . . + . ┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━. + . ┊ . + ] + # edit the sandbox + assume-console [ + left-click 3, 70 + ] + run [ + event-loop screen:address, console:address, 3:address:programming-environment-data + ] + screen-should-contain [ + . run (F4) . + . ┊print-integer screen:address, 4 . + .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━. + . ┊ . + . ┊ . + ] +] + +scenario sandbox-can-handle-infinite-loop [ + $close-trace # trace too long + assume-screen 100/width, 20/height + # left editor is empty + 1:address:array:character <- new [recipe foo [ + { + loop + } +]] + # right editor contains an instruction + 2:address:array:character <- new [foo] + 3:address:programming-environment-data <- new-programming-environment screen:address, 1:address:array:character, 2:address:array:character + # run the sandbox + assume-console [ + press F4 + ] + run [ + event-loop screen:address, console:address, 3:address:programming-environment-data + ] + screen-should-contain [ + . run (F4) . + .recipe foo [ ┊ . + . { ┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━. + . loop ┊ x. + . } ┊foo . + .] ┊took too long! . + .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━. + . ┊ . + ] +] + +recipe editor-contents [ + local-scope + editor:address:editor-data <- next-ingredient + buf:address:buffer <- new-buffer 80 + curr:address:duplex-list <- get *editor, data:offset + # skip § sentinel + assert curr, [editor without data is illegal; must have at least a sentinel] + curr <- next-duplex curr + reply-unless curr, 0 + { + break-unless curr + c:character <- get *curr, value:offset + buffer-append buf, c + curr <- next-duplex curr + loop + } + result:address:array:character <- buffer-to-array buf + reply result +] + +scenario editor-provides-edited-contents [ + assume-screen 10/width, 5/height + 1:address:array:character <- new [abc] + 2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 10/right + assume-console [ + left-click 1, 2 + type [def] + ] + run [ + editor-event-loop screen:address, console:address, 2:address:editor-data + 3:address:array:character <- editor-contents 2:address:editor-data + 4:array:character <- copy *3:address:array:character + ] + memory-should-contain [ + 4:string <- [abdefc] + ] +] |