From 6e1eeeebfb453fa7c871869c19375ce60fbd7413 Mon Sep 17 00:00:00 2001 From: Kartik Agaram Date: Sat, 27 Jul 2019 16:01:55 -0700 Subject: 5485 - promote SubX to top-level --- archive/2.vm/edit/005-sandbox.mu | 1193 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 1193 insertions(+) create mode 100644 archive/2.vm/edit/005-sandbox.mu (limited to 'archive/2.vm/edit/005-sandbox.mu') diff --git a/archive/2.vm/edit/005-sandbox.mu b/archive/2.vm/edit/005-sandbox.mu new file mode 100644 index 00000000..96ec804d --- /dev/null +++ b/archive/2.vm/edit/005-sandbox.mu @@ -0,0 +1,1193 @@ +## running code from the editor and creating sandboxes +# +# Running code in the sandbox editor prepends its contents to a list of +# (non-editable) sandboxes below the editor, showing the result and maybe a +# few other things (later layers). +# +# This layer draws the menubar buttons in non-editable sandboxes but they +# don't do anything yet. Later layers implement each button. + +def! main [ + local-scope + open-console + clear-screen null/screen # non-scrolling app + env:&:environment <- new-programming-environment null/filesystem, null/screen + env <- restore-sandboxes env, null/filesystem + render-all null/screen, env, render + event-loop null/screen, null/console, env, null/filesystem +] + +container environment [ + sandbox:&:sandbox # list of sandboxes, from top to bottom. TODO: switch to &:list:sandbox + render-from:num + number-of-sandboxes:num +] + +after [ + *result <- put *result, render-from:offset, -1 +] + +container sandbox [ + data:text + response:text + # coordinates to track clicks + # constraint: will be 0 for sandboxes at positions before env.render-from + starting-row-on-screen:num + code-ending-row-on-screen:num # past end of code + screen:&:screen # prints in the sandbox go here + next-sandbox:&:sandbox +] + +scenario run-and-show-results [ + local-scope + trace-until 100/app # trace too long + assume-screen 100/width, 15/height + # recipe editor is empty + assume-resources [ + ] + # sandbox editor contains an instruction without storing outputs + env:&:environment <- new-programming-environment resources, screen, [divide-with-remainder 11, 3] + render-all screen, env, render + # run the code in the editors + assume-console [ + press F4 + ] + run [ + event-loop screen, console, env, resources + ] + # check that screen prints the results + screen-should-contain [ + . run (F4) . + . ┊ . + .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊─────────────────────────────────────────────────. + . ┊0 edit copy to recipe delete . + . ┊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, [ + . . + . ┊ . + .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊─────────────────────────────────────────────────. + . ┊ . + . ┊ . + . ┊3 . + . ┊2 . + . ┊─────────────────────────────────────────────────. + . ┊ . + ] + # sandbox menu in reverse video + screen-should-contain-in-color 232/black, [ + . . + . . + . . + . 0 edit copy to recipe delete . + ] + # run another command + assume-console [ + left-click 1, 80 + type [add 2, 2] + press F4 + ] + run [ + event-loop screen, console, env, resources + ] + # check that screen prints both sandboxes + screen-should-contain [ + . run (F4) . + . ┊ . + .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊─────────────────────────────────────────────────. + . ┊0 edit copy to recipe delete . + . ┊add 2, 2 . + . ┊4 . + . ┊─────────────────────────────────────────────────. + . ┊1 edit copy to recipe delete . + . ┊divide-with-remainder 11, 3 . + . ┊3 . + . ┊2 . + . ┊─────────────────────────────────────────────────. + . ┊ . + ] +] + +after [ + # F4? load all code and run all sandboxes. + { + do-run?:bool <- equal k, 65532/F4 + break-unless do-run? + screen <- update-status screen, [running... ], 245/grey + + error?:bool <- run-sandboxes env, resources, screen + # we could just render-all, but we do some work to minimize the number of prints to screen + + screen <- render-sandbox-side screen, env, render + { + break-if error? + screen <- update-status screen, [ ], 245/grey + } + screen <- update-cursor screen, recipes, current-sandbox, sandbox-in-focus?, env + loop +next-event + } +] + +def run-sandboxes env:&:environment, resources:&:resources, screen:&:screen -> errors-found?:bool, env:&:environment, resources:&:resources, screen:&:screen [ + local-scope + load-inputs + errors-found?:bool <- update-recipes env, resources, screen + jump-if errors-found?, +return + # check contents of right editor (sandbox) + + current-sandbox:&:editor <- get *env, current-sandbox:offset + { + sandbox-contents:text <- editor-contents current-sandbox + break-unless sandbox-contents + # if contents exist, first save them + # run them and turn them into a new sandbox + new-sandbox:&:sandbox <- new sandbox:type + *new-sandbox <- put *new-sandbox, data:offset, sandbox-contents + # push to head of sandbox list + dest:&:sandbox <- get *env, sandbox:offset + *new-sandbox <- put *new-sandbox, next-sandbox:offset, dest + *env <- put *env, sandbox:offset, new-sandbox + # update sandbox count + sandbox-count:num <- get *env, number-of-sandboxes:offset + sandbox-count <- add sandbox-count, 1 + *env <- put *env, number-of-sandboxes:offset, sandbox-count + # save all sandboxes + # needs to be before running them, in case we die when running + save-sandboxes env, resources + # clear sandbox editor + init:&:duplex-list:char <- push 167/§, null + *current-sandbox <- put *current-sandbox, data:offset, init + *current-sandbox <- put *current-sandbox, top-of-screen:offset, init + } + # run all sandboxes + curr:&:sandbox <- get *env, sandbox:offset + idx:num <- copy 0 + { + break-unless curr + curr <- update-sandbox curr, env, idx + curr <- get *curr, next-sandbox:offset + idx <- add idx, 1 + loop + } + + +return + { + break-if resources # ignore this in tests + $system [./snapshot_lesson] + } +] + +# load code from disk +# replaced in a later layer (whereupon errors-found? will actually be set) +def update-recipes env:&:environment, resources:&:resources, screen:&:screen -> errors-found?:bool, env:&:environment, resources:&:resources, screen:&:screen [ + local-scope + load-inputs + recipes:&:editor <- get *env, recipes:offset + in:text <- editor-contents recipes + resources <- dump resources, [lesson/recipes.mu], in + reload in + errors-found? <- copy false +] + +# replaced in a later layer +def update-sandbox sandbox:&:sandbox, env:&:environment, idx:num -> sandbox:&:sandbox, env:&:environment [ + local-scope + load-inputs + data:text <- get *sandbox, data:offset + response:text, _, fake-screen:&:screen <- run-sandboxed data + *sandbox <- put *sandbox, response:offset, response + *sandbox <- put *sandbox, screen:offset, fake-screen +] + +def update-status screen:&:screen, msg:text, color:num -> screen:&:screen [ + local-scope + load-inputs + screen <- move-cursor screen, 0, 2 + screen <- print screen, msg, color, 238/grey/background +] + +def save-sandboxes env:&:environment, resources:&:resources -> resources:&:resources [ + local-scope + load-inputs + trace 11, [app], [save sandboxes] + current-sandbox:&:editor <- 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:&:sandbox <- get *env, sandbox:offset + idx:num <- copy 0 + { + break-unless curr + resources <- save-sandbox resources, curr, idx + idx <- add idx, 1 + curr <- get *curr, next-sandbox:offset + loop + } +] + +def save-sandbox resources:&:resources, sandbox:&:sandbox, sandbox-index:num -> resources:&:resources [ + local-scope + load-inputs + data:text <- get *sandbox, data:offset + filename:text <- append [lesson/], sandbox-index + resources <- dump resources, filename, data + +] + +def! render-sandbox-side screen:&:screen, env:&:environment, render-editor:render-recipe -> screen:&:screen, env:&:environment [ + local-scope + load-inputs + trace 11, [app], [render sandbox side] + old-top-idx:num <- save-top-idx screen + current-sandbox:&:editor <- get *env, current-sandbox:offset + row:num, column:num <- copy 1, 0 + left:num <- get *current-sandbox, left:offset + right:num <- get *current-sandbox, right:offset + # render sandbox editor + render-from:num <- get *env, render-from:offset + { + render-current-sandbox?:bool <- equal render-from, -1 + break-unless render-current-sandbox? + row, column, screen, current-sandbox <- call render-editor, screen, current-sandbox + } + # render sandboxes + draw-horizontal screen, row, left, right + sandbox:&:sandbox <- get *env, sandbox:offset + row, screen <- render-sandboxes screen, sandbox, left, right, row, render-from + clear-rest-of-screen screen, row, left, right + # + assert-no-scroll screen, old-top-idx +] + +def render-sandboxes screen:&:screen, sandbox:&:sandbox, left:num, right:num, row:num, render-from:num, idx:num -> row:num, screen:&:screen, sandbox:&:sandbox [ + local-scope + load-inputs + return-unless sandbox + screen-height:num <- screen-height screen + hidden?:bool <- lesser-than idx, render-from + { + break-if hidden? + # render sandbox menu + row <- add row, 1 + at-bottom?:bool <- greater-or-equal row, screen-height + return-if at-bottom? + screen <- move-cursor screen, row, left + screen <- render-sandbox-menu screen, idx, left, right + # save menu row so we can detect clicks to it later + *sandbox <- put *sandbox, starting-row-on-screen:offset, row + # render sandbox contents + row <- add row, 1 + screen <- move-cursor screen, row, left + sandbox-data:text <- get *sandbox, data:offset + row, screen <- render-code screen, sandbox-data, left, right, row + *sandbox <- put *sandbox, code-ending-row-on-screen:offset, row + # render sandbox warnings, screen or response, in that order + sandbox-response:text <- get *sandbox, response:offset + + { + sandbox-screen:&:screen <- get *sandbox, screen:offset + empty-screen?:bool <- fake-screen-is-empty? sandbox-screen + break-if empty-screen? + row, screen <- render-screen screen, sandbox-screen, left, right, row + } + { + break-unless empty-screen? + + row, screen <- render-text screen, sandbox-response, left, right, 245/grey, row + } + +render-sandbox-end + at-bottom?:bool <- greater-or-equal row, screen-height + return-if at-bottom? + # draw solid line after sandbox + draw-horizontal screen, row, left, right + } + # if hidden, reset row attributes + { + break-unless hidden? + *sandbox <- put *sandbox, starting-row-on-screen:offset, 0 + *sandbox <- put *sandbox, code-ending-row-on-screen:offset, 0 + + } + # draw next sandbox + next-sandbox:&:sandbox <- get *sandbox, next-sandbox:offset + next-idx:num <- add idx, 1 + row, screen <- render-sandboxes screen, next-sandbox, left, right, row, render-from, next-idx +] + +def render-sandbox-menu screen:&:screen, sandbox-index:num, left:num, right:num -> screen:&:screen [ + local-scope + load-inputs + move-cursor-to-column screen, left + edit-button-left:num, edit-button-right:num, copy-button-left:num, copy-button-right:num, recipe-button-left:num, recipe-button-right:num, delete-button-left:num <- sandbox-menu-columns left, right + print screen, sandbox-index, 232/dark-grey, 245/grey + start-buttons:num <- subtract edit-button-left, 1 + clear-line-until screen, start-buttons, 245/grey + print screen, [edit], 232/black, 25/background-blue + clear-line-until screen, edit-button-right, 25/background-blue + print screen, [copy], 232/black, 58/background-green + clear-line-until screen, copy-button-right, 58/background-green + print screen, [to recipe], 232/black, 94/background-orange + clear-line-until screen, recipe-button-right, 94/background-orange + print screen, [delete], 232/black, 52/background-red + clear-line-until screen, right, 52/background-red +] + +scenario skip-rendering-sandbox-menu-past-bottom-row [ + trace-until 100/app # trace too long + assume-screen 100/width, 6/height + # recipe editor is empty + assume-resources [ + [lesson/0] <- [|add 2, 2|] + [lesson/1] <- [|add 1, 1|] + ] + # create two sandboxes such that the top one just barely fills the screen + env:&:environment <- new-programming-environment resources, screen, [] + env <- restore-sandboxes env, resources + run [ + render-all screen, env, render + ] + screen-should-contain [ + . run (F4) . + . ┊ . + .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊─────────────────────────────────────────────────. + . ┊0 edit copy to recipe delete . + . ┊add 2, 2 . + . ┊─────────────────────────────────────────────────. + ] +] + +# divide up the menu bar for a sandbox into 3 segments, for edit/copy/delete buttons +# delete-button-right == right +# all left/right pairs are inclusive +def sandbox-menu-columns left:num, right:num -> edit-button-left:num, edit-button-right:num, copy-button-left:num, copy-button-right:num, recipe-button-left:num, recipe-button-right:num, delete-button-left:num [ + local-scope + load-inputs + start-buttons:num <- add left, 4/space-for-sandbox-index + buttons-space:num <- subtract right, start-buttons + button-width:num <- divide-with-remainder buttons-space, 4 # integer division + buttons-wide-enough?:bool <- greater-or-equal button-width, 10 + assert buttons-wide-enough?, [sandbox must be at least 40 or so characters wide] + edit-button-left:num <- copy start-buttons + copy-button-left:num <- add start-buttons, button-width + edit-button-right:num <- subtract copy-button-left, 1 + recipe-button-left:num <- add copy-button-left, button-width + copy-button-right:num <- subtract recipe-button-left, 1 + delete-button-left:num <- subtract right, button-width, -2 # because 'to recipe' is wider than 'delete' + recipe-button-right:num <- subtract delete-button-left, 1 +] + +# print a text 's' to 'editor' in 'color' starting at 'row' +# clear rest of last line, move cursor to next line +# like 'render-code' but without syntax-based colorization +def render-text screen:&:screen, s:text, left:num, right:num, color:num, row:num -> row:num, screen:&:screen [ + local-scope + load-inputs + return-unless s + column:num <- copy left + screen <- move-cursor screen, row, column + screen-height:num <- screen-height screen + i:num <- copy 0 + len:num <- length *s + { + +next-character + done?:bool <- greater-or-equal i, len + break-if done? + done? <- greater-or-equal row, screen-height + break-if done? + c:char <- index *s, i + { + # newline? move to left rather than 0 + newline?:bool <- equal c, 10/newline + break-unless newline? + # clear rest of line in this window + { + done?:bool <- greater-than column, right + break-if done? + space:char <- copy 32/space + print screen, space + column <- add column, 1 + loop + } + row <- add row, 1 + column <- copy left + screen <- move-cursor screen, row, column + i <- add i, 1 + loop +next-character + } + { + # at right? wrap. + at-right?:bool <- equal column, right + break-unless at-right? + # print wrap icon + wrap-icon:char <- copy 8617/loop-back-to-left + print screen, wrap-icon, 245/grey + column <- copy left + row <- add row, 1 + screen <- move-cursor screen, row, column + # don't increment i + loop +next-character + } + i <- add i, 1 + print screen, c, color + column <- add column, 1 + loop + } + was-at-left?:bool <- equal column, left + clear-line-until screen, right + { + break-if was-at-left? + row <- add row, 1 + } + move-cursor screen, row, left +] + +scenario render-text-wraps-barely-long-lines [ + local-scope + assume-screen 5/width, 5/height + run [ + render-text screen, [abcde], 0/left, 4/right, 7/white, 1/row + ] + screen-should-contain [ + . . + .abcd↩. + .e . + . . + ] +] + +# assumes programming environment has no sandboxes; restores them from previous session +def restore-sandboxes env:&:environment, resources:&:resources -> env:&:environment [ + local-scope + load-inputs + # read all scenarios, pushing them to end of a list of scenarios + idx:num <- copy 0 + curr:&:sandbox <- copy null + prev:&:sandbox <- copy null + { + filename:text <- append [lesson/], idx + contents:text <- slurp resources, filename + break-unless contents # stop at first error; assuming file didn't exist + # todo: handle empty sandbox + # create new sandbox for file + curr <- new sandbox:type + *curr <- put *curr, data:offset, contents + + { + break-if idx + *env <- put *env, sandbox:offset, curr + } + { + break-unless idx + *prev <- put *prev, next-sandbox:offset, curr + } + idx <- add idx, 1 + prev <- copy curr + loop + } + # update sandbox count + *env <- put *env, number-of-sandboxes:offset, idx +] + +# print the fake sandbox screen to 'screen' with appropriate delimiters +# leave cursor at start of next line +def render-screen screen:&:screen, sandbox-screen:&:screen, left:num, right:num, row:num -> row:num, screen:&:screen [ + local-scope + load-inputs + return-unless sandbox-screen + # print 'screen:' + row <- render-text screen, [screen:], left, right, 245/grey, row + screen <- move-cursor screen, row, left + # start printing sandbox-screen + column:num <- copy left + s-width:num <- screen-width sandbox-screen + s-height:num <- screen-height sandbox-screen + buf:&:@:screen-cell <- get *sandbox-screen, data:offset + stop-printing:num <- add left, s-width, 3 + max-column:num <- min stop-printing, right + i:num <- copy 0 + len:num <- length *buf + screen-height:num <- screen-height screen + { + done?:bool <- 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 '.' + space:char <- copy 32/space + print screen, space, 245/grey + print screen, space, 245/grey + full-stop:char <- copy 46/period + print screen, full-stop, 245/grey + column <- add left, 3 + { + # print row + row-done?:bool <- greater-or-equal column, max-column + break-if row-done? + curr:screen-cell <- index *buf, i + c:char <- get curr, contents:offset + color:num <- get curr, color:offset + { + # damp whites down to grey + white?:bool <- equal color, 7/white + break-unless white? + color <- copy 245/grey + } + print screen, c, color + column <- add column, 1 + i <- add i, 1 + loop + } + # print final '.' + print screen, full-stop, 245/grey + column <- add column, 1 + { + # clear rest of current line + line-done?:bool <- greater-than column, right + break-if line-done? + print screen, space + column <- add column, 1 + loop + } + row <- add row, 1 + loop + } +] + +scenario run-updates-results [ + local-scope + trace-until 100/app # 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) + assume-resources [ + [lesson/recipes.mu] <- [ + || + |recipe foo [| + | local-scope| + | z:num <- add 2, 2| + | reply z| + |]| + ] + ] + # sandbox editor contains an instruction without storing outputs + env:&:environment <- new-programming-environment resources, screen, [foo] # contents of sandbox editor + render-all screen, env, render + $clear-trace + # run the code in the editors + assume-console [ + press F4 + ] + event-loop screen, console, env, resources + screen-should-contain [ + . run (F4) . + . ┊ . + .recipe foo [ ┊─────────────────────────────────────────────────. + . local-scope ┊0 edit copy to recipe delete . + . z:num <- add 2, 2 ┊foo . + . reply z ┊4 . + .] ┊─────────────────────────────────────────────────. + . ┊ . + .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊ . + . ┊ . + ] + # the new sandbox should be saved to disk + trace-should-contain [ + app: save sandboxes + ] + # no need to update editor + trace-should-not-contain [ + app: render recipes + ] + # make a change (incrementing one of the args to 'add'), then rerun + $clear-trace + assume-console [ + left-click 4, 28 # one past the value of the second arg + press backspace + type [3] + press F4 + ] + run [ + event-loop screen, console, env, resources + ] + # check that screen updates the result on the right + screen-should-contain [ + . run (F4) . + . ┊ . + .recipe foo [ ┊─────────────────────────────────────────────────. + . local-scope ┊0 edit copy to recipe delete . + . z:num <- add 2, 3 ┊foo . + . reply z ┊5 . + .] ┊─────────────────────────────────────────────────. + . ┊ . + .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊ . + . ┊ . + ] + # no need to save sandboxes all over again + trace-should-not-contain [ + app: save sandboxes + ] +] + +scenario run-instruction-manages-screen-per-sandbox [ + local-scope + trace-until 100/app # trace too long + assume-screen 100/width, 20/height + # empty recipes + assume-resources [ + ] + # sandbox editor contains an instruction + env:&:environment <- new-programming-environment resources, screen, [print screen, 4] # contents of sandbox editor + render-all screen, env, render + # run the code in the editor + assume-console [ + press F4 + ] + run [ + event-loop screen, console, env, resources + ] + # check that it prints a little toy screen + screen-should-contain [ + . run (F4) . + . ┊ . + .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊─────────────────────────────────────────────────. + . ┊0 edit copy to recipe delete . + . ┊print screen, 4 . + . ┊screen: . + . ┊ .4 . . + . ┊ . . . + . ┊ . . . + . ┊ . . . + . ┊ . . . + . ┊─────────────────────────────────────────────────. + . ┊ . + ] +] + +def editor-contents editor:&:editor -> result:text [ + local-scope + load-inputs + buf:&:buffer:char <- new-buffer 80 + curr:&:duplex-list:char <- get *editor, data:offset + # skip § sentinel + assert curr, [editor without data is illegal; must have at least a sentinel] + curr <- next curr + return-unless curr, null + { + break-unless curr + c:char <- get *curr, value:offset + buf <- append buf, c + curr <- next curr + loop + } + result <- buffer-to-array buf +] + +scenario editor-provides-edited-contents [ + local-scope + assume-screen 10/width, 5/height + e:&:editor <- new-editor [abc], 0/left, 10/right + assume-console [ + left-click 1, 2 + type [def] + ] + run [ + editor-event-loop screen, console, e + s:text <- editor-contents e + 1:@:char/raw <- copy *s + ] + memory-should-contain [ + 1:array:character <- [abdefc] + ] +] + +# keep the bottom of recipes from scrolling off the screen + +scenario scrolling-down-past-bottom-of-recipe-editor [ + local-scope + trace-until 100/app + assume-screen 100/width, 10/height + assume-resources [ + ] + env:&:environment <- new-programming-environment resources, screen, [] + render-all screen, env, render + assume-console [ + press enter + press down-arrow + ] + event-loop screen, console, env, resources + # no scroll + screen-should-contain [ + . run (F4) . + . ┊ . + . ┊─────────────────────────────────────────────────. + .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊ . + . ┊ . + ] +] + +scenario cursor-down-in-recipe-editor [ + local-scope + trace-until 100/app + assume-screen 100/width, 10/height + assume-resources [ + ] + env:&:environment <- new-programming-environment resources, screen, [] + render-all screen, env, render + assume-console [ + press enter + press up-arrow + press down-arrow # while cursor isn't at bottom + ] + event-loop screen, console, env, resources + cursor:char <- copy 9251/␣ + print screen, cursor + # cursor moves back to bottom + screen-should-contain [ + . run (F4) . + . ┊ . + .␣ ┊─────────────────────────────────────────────────. + .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊ . + . ┊ . + ] +] + +scenario scrolling-down-past-bottom-of-recipe-editor-2 [ + local-scope + trace-until 100/app + assume-screen 100/width, 10/height + assume-resources [ + ] + env:&:environment <- new-programming-environment resources, screen, [] + render-all screen, env, render + assume-console [ + # add a line + press enter + # cursor back to top line + press up-arrow + # try to scroll + press page-down # or ctrl-f + ] + event-loop screen, console, env, resources + # no scroll, and cursor remains at top line + screen-should-contain [ + . run (F4) . + . ┊ . + . ┊─────────────────────────────────────────────────. + .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊ . + . ┊ . + ] +] + +scenario scrolling-down-past-bottom-of-recipe-editor-3 [ + local-scope + trace-until 100/app + assume-screen 100/width, 10/height + assume-resources [ + ] + env:&:environment <- new-programming-environment resources, screen, [ab +cd] + render-all screen, env, render + assume-console [ + # add a line + press enter + # switch to sandbox + press ctrl-n + # move cursor + press down-arrow + ] + event-loop screen, console, env, resources + cursor:char <- copy 9251/␣ + print screen, cursor + # no scroll on recipe side, cursor moves on sandbox side + screen-should-contain [ + . run (F4) . + . ┊ab . + . ┊␣d . + .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊─────────────────────────────────────────────────. + . ┊ . + ] +] + +# scrolling through sandboxes + +scenario scrolling-down-past-bottom-of-sandbox-editor [ + local-scope + trace-until 100/app # trace too long + assume-screen 100/width, 10/height + # initialize + assume-resources [ + ] + env:&:environment <- new-programming-environment resources, screen, [add 2, 2] + render-all screen, env, render + assume-console [ + # create a sandbox + press F4 + ] + event-loop screen, console, env, resources + screen-should-contain [ + . run (F4) . + . ┊ . + .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊─────────────────────────────────────────────────. + . ┊0 edit copy to recipe delete . + . ┊add 2, 2 . + ] + # switch to sandbox window and hit 'page-down' + assume-console [ + press ctrl-n + press page-down + ] + run [ + event-loop screen, console, env, resources + cursor:char <- copy 9251/␣ + print screen, cursor + ] + # sandbox editor hidden; first sandbox displayed + # cursor moves to first sandbox + screen-should-contain [ + . run (F4) . + . ┊─────────────────────────────────────────────────. + .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊␣ edit copy to recipe delete . + . ┊add 2, 2 . + . ┊4 . + ] + # hit 'page-up' + assume-console [ + press page-up + ] + run [ + event-loop screen, console, env, resources + cursor:char <- copy 9251/␣ + print screen, cursor + ] + # sandbox editor displays again, cursor is in editor + screen-should-contain [ + . run (F4) . + . ┊␣ . + .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊─────────────────────────────────────────────────. + . ┊0 edit copy to recipe delete . + . ┊add 2, 2 . + ] +] + +# page-down on sandbox side updates render-from to scroll sandboxes +after [ + { + break-unless sandbox-in-focus? + page-down?:bool <- equal k, 65518/page-down + break-unless page-down? + sandbox:&:sandbox <- get *env, sandbox:offset + break-unless sandbox + # slide down if possible + { + render-from:num <- get *env, render-from:offset + number-of-sandboxes:num <- get *env, number-of-sandboxes:offset + max:num <- subtract number-of-sandboxes, 1 + at-end?:bool <- greater-or-equal render-from, max + loop-if at-end?, +next-event # render nothing + render-from <- add render-from, 1 + *env <- put *env, render-from:offset, render-from + } + screen <- render-sandbox-side screen, env, render + screen <- update-cursor screen, recipes, current-sandbox, sandbox-in-focus?, env + loop +next-event + } +] + +# update-cursor takes render-from into account +after [ + { + break-unless sandbox-in-focus? + render-from:num <- get *env, render-from:offset + scrolling?:bool <- greater-or-equal render-from, 0 + break-unless scrolling? + cursor-column:num <- get *current-sandbox, left:offset + screen <- move-cursor screen, 2/row, cursor-column # highlighted sandbox will always start at row 2 + return + } +] + +# 'page-up' on sandbox side is like 'page-down': updates render-from when necessary +after [ + { + break-unless sandbox-in-focus? + page-up?:bool <- equal k, 65519/page-up + break-unless page-up? + render-from:num <- get *env, render-from:offset + at-beginning?:bool <- equal render-from, -1 + break-if at-beginning? + render-from <- subtract render-from, 1 + *env <- put *env, render-from:offset, render-from + screen <- render-sandbox-side screen, env, render + screen <- update-cursor screen, recipes, current-sandbox, sandbox-in-focus?, env + loop +next-event + } +] + +# sandbox belonging to 'env' whose next-sandbox is 'in' +# return null if there's no such sandbox, either because 'in' doesn't exist in 'env', or because it's the first sandbox +def previous-sandbox env:&:environment, in:&:sandbox -> out:&:sandbox [ + local-scope + load-inputs + curr:&:sandbox <- get *env, sandbox:offset + return-unless curr, null + next:&:sandbox <- get *curr, next-sandbox:offset + { + return-unless next, null + found?:bool <- equal next, in + break-if found? + curr <- copy next + next <- get *curr, next-sandbox:offset + loop + } + return curr +] + +scenario scrolling-through-multiple-sandboxes [ + local-scope + trace-until 100/app # trace too long + assume-screen 100/width, 10/height + # initialize environment + assume-resources [ + ] + env:&:environment <- new-programming-environment resources, screen, [] + render-all screen, env, render + # create 2 sandboxes + assume-console [ + press ctrl-n + type [add 2, 2] + press F4 + type [add 1, 1] + press F4 + ] + event-loop screen, console, env, resources + cursor:char <- copy 9251/␣ + print screen, cursor + screen-should-contain [ + . run (F4) . + . ┊␣ . + .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊─────────────────────────────────────────────────. + . ┊0 edit copy to recipe delete . + . ┊add 1, 1 . + . ┊2 . + . ┊─────────────────────────────────────────────────. + . ┊1 edit copy to recipe delete . + . ┊add 2, 2 . + . ┊4 . + ] + # hit 'page-down' + assume-console [ + press page-down + ] + run [ + event-loop screen, console, env, resources + cursor:char <- copy 9251/␣ + print screen, cursor + ] + # sandbox editor hidden; first sandbox displayed + # cursor moves to first sandbox + screen-should-contain [ + . run (F4) . + . ┊─────────────────────────────────────────────────. + .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊␣ edit copy to recipe delete . + . ┊add 1, 1 . + . ┊2 . + . ┊─────────────────────────────────────────────────. + . ┊1 edit copy to recipe delete . + . ┊add 2, 2 . + . ┊4 . + ] + # hit 'page-down' again + assume-console [ + press page-down + ] + run [ + event-loop screen, console, env, resources + ] + # just second sandbox displayed + screen-should-contain [ + . run (F4) . + . ┊─────────────────────────────────────────────────. + .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊1 edit copy to recipe delete . + . ┊add 2, 2 . + . ┊4 . + . ┊─────────────────────────────────────────────────. + . ┊ . + ] + # hit 'page-down' again + assume-console [ + press page-down + ] + run [ + event-loop screen, console, env, resources + ] + # no change + screen-should-contain [ + . run (F4) . + . ┊─────────────────────────────────────────────────. + .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊1 edit copy to recipe delete . + . ┊add 2, 2 . + . ┊4 . + . ┊─────────────────────────────────────────────────. + . ┊ . + ] + # hit 'page-up' + assume-console [ + press page-up + ] + run [ + event-loop screen, console, env, resources + ] + # back to displaying both sandboxes without editor + screen-should-contain [ + . run (F4) . + . ┊─────────────────────────────────────────────────. + .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊0 edit copy to recipe delete . + . ┊add 1, 1 . + . ┊2 . + . ┊─────────────────────────────────────────────────. + . ┊1 edit copy to recipe delete . + . ┊add 2, 2 . + . ┊4 . + ] + # hit 'page-up' again + assume-console [ + press page-up + ] + run [ + event-loop screen, console, env, resources + cursor:char <- copy 9251/␣ + print screen, cursor + ] + # back to displaying both sandboxes as well as editor + screen-should-contain [ + . run (F4) . + . ┊␣ . + .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊─────────────────────────────────────────────────. + . ┊0 edit copy to recipe delete . + . ┊add 1, 1 . + . ┊2 . + . ┊─────────────────────────────────────────────────. + . ┊1 edit copy to recipe delete . + . ┊add 2, 2 . + . ┊4 . + ] + # hit 'page-up' again + assume-console [ + press page-up + ] + run [ + event-loop screen, console, env, resources + cursor:char <- copy 9251/␣ + print screen, cursor + ] + # no change + screen-should-contain [ + . run (F4) . + . ┊␣ . + .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊─────────────────────────────────────────────────. + . ┊0 edit copy to recipe delete . + . ┊add 1, 1 . + . ┊2 . + . ┊─────────────────────────────────────────────────. + . ┊1 edit copy to recipe delete . + . ┊add 2, 2 . + . ┊4 . + ] +] + +scenario scrolling-manages-sandbox-index-correctly [ + local-scope + trace-until 100/app # trace too long + assume-screen 100/width, 10/height + # initialize environment + assume-resources [ + ] + env:&:environment <- new-programming-environment resources, screen, [] + render-all screen, env, render + # create a sandbox + assume-console [ + press ctrl-n + type [add 1, 1] + press F4 + ] + event-loop screen, console, env, resources + screen-should-contain [ + . run (F4) . + . ┊ . + .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊─────────────────────────────────────────────────. + . ┊0 edit copy to recipe delete . + . ┊add 1, 1 . + . ┊2 . + . ┊─────────────────────────────────────────────────. + . ┊ . + ] + # hit 'page-down' and 'page-up' a couple of times. sandbox index should be stable + assume-console [ + press page-down + ] + run [ + event-loop screen, console, env, resources + ] + # sandbox editor hidden; first sandbox displayed + # cursor moves to first sandbox + screen-should-contain [ + . run (F4) . + . ┊─────────────────────────────────────────────────. + .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊0 edit copy to recipe delete . + . ┊add 1, 1 . + . ┊2 . + . ┊─────────────────────────────────────────────────. + . ┊ . + ] + # hit 'page-up' again + assume-console [ + press page-up + ] + run [ + event-loop screen, console, env, resources + ] + # back to displaying both sandboxes as well as editor + screen-should-contain [ + . run (F4) . + . ┊ . + .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊─────────────────────────────────────────────────. + . ┊0 edit copy to recipe delete . + . ┊add 1, 1 . + . ┊2 . + . ┊─────────────────────────────────────────────────. + . ┊ . + ] + # hit 'page-down' + assume-console [ + press page-down + ] + run [ + event-loop screen, console, env, resources + ] + # sandbox editor hidden; first sandbox displayed + # cursor moves to first sandbox + screen-should-contain [ + . run (F4) . + . ┊─────────────────────────────────────────────────. + .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊0 edit copy to recipe delete . + . ┊add 1, 1 . + . ┊2 . + . ┊─────────────────────────────────────────────────. + . ┊ . + ] +] -- cgit 1.4.1-2-gfad0