1 ## running code from the editor and creating sandboxes
   2 #
   3 # Running code in the sandbox editor prepends its contents to a list of
   4 # (non-editable) sandboxes below the editor, showing the result and maybe a
   5 # few other things (later layers).
   6 #
   7 # This layer draws the menubar buttons in non-editable sandboxes but they
   8 # don't do anything yet. Later layers implement each button.
   9 
  10 def! main [
  11   local-scope
  12   open-console
  13   env:&:environment <- new-programming-environment 0/filesystem, 0/screen
  14   env <- restore-sandboxes env
  15   render-all 0/screen, env, render
  16   event-loop 0/screen, 0/console, env, 0/filesystem
  17   # never gets here
  18 ]
  19 
  20 container environment [
  21   sandbox:&:sandbox  # list of sandboxes, from top to bottom. TODO: switch to &:list:sandbox
  22   render-from:num
  23   number-of-sandboxes:num
  24 ]
  25 
  26 after <programming-environment-initialization> [
  27   *result <- put *result, render-from:offset, -1
  28 ]
  29 
  30 container sandbox [
  31   data:text
  32   response:text
  33   # coordinates to track clicks
  34   # constraint: will be 0 for sandboxes at positions before env.render-from
  35   starting-row-on-screen:num
  36   code-ending-row-on-screen:num  # past end of code
  37   screen:&:screen  # prints in the sandbox go here
  38   next-sandbox:&:sandbox
  39 ]
  40 
  41 scenario run-and-show-results [
  42   local-scope
  43   trace-until 100/app  # trace too long
  44   assume-screen 100/width, 15/height
  45   # recipe editor is empty
  46   assume-resources [
  47   ]
  48   # sandbox editor contains an instruction without storing outputs
  49   env:&:environment <- new-programming-environment resources, screen, [divide-with-remainder 11, 3]
  50   # run the code in the editors
  51   assume-console [
  52     press F4
  53   ]
  54   run [
  55     event-loop screen, console, env, resources
  56   ]
  57   # check that screen prints the results
  58   screen-should-contain [
  59     .                                                                                 run (F4)           .
  60     .                                                  ╎                                                 .
  61     .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎─────────────────────────────────────────────────.
  62     .                                                  ╎0   edit          copy            delete         .
  63     .                                                  ╎divide-with-remainder 11, 3                      .
  64     .                                                  ╎3                                                .
  65     .                                                  ╎2                                                .
  66     .                                                  ╎─────────────────────────────────────────────────.
  67     .                                                  ╎                                                 .
  68   ]
  69   screen-should-contain-in-color 7/white, [
  70     .                                                                                                    .
  71     .                                                                                                    .
  72     .                                                                                                    .
  73     .                                                                                                    .
  74     .                                                   divide-with-remainder 11, 3                      .
  75     .                                                                                                    .
  76     .                                                                                                    .
  77     .                                                                                                    .
  78     .                                                                                                    .
  79   ]
  80   screen-should-contain-in-color 245/grey, [
  81     .                                                                                                    .
  82     .                                                  ╎                                                 .
  83     .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎─────────────────────────────────────────────────.
  84     .                                                  ╎                                                 .
  85     .                                                  ╎                                                 .
  86     .                                                  ╎3                                                .
  87     .                                                  ╎2                                                .
  88     .                                                  ╎─────────────────────────────────────────────────.
  89     .                                                  ╎                                                 .
  90   ]
  91   # sandbox menu in reverse video
  92   screen-should-contain-in-color 232/black, [
  93     .                                                                                                    .
  94     .                                                                                                    .
  95     .                                                                                                    .
  96     .                                                   0   edit          copy            delete         .
  97   ]
  98   # run another command
  99   assume-console [
 100     left-click 1, 80
 101     type [add 2, 2]
 102     press F4
 103   ]
 104   run [
 105     event-loop screen, console, env, resources
 106   ]
 107   # check that screen prints both sandboxes
 108   screen-should-contain [
 109     .                                                                                 run (F4)           .
 110     .                                                  ╎                                                 .
 111     .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎─────────────────────────────────────────────────.
 112     .                                                  ╎0   edit          copy            delete         .
 113     .                                                  ╎add 2, 2                                         .
 114     .                                                  ╎4                                                .
 115     .                                                  ╎─────────────────────────────────────────────────.
 116     .                                                  ╎1   edit          copy            delete         .
 117     .                                                  ╎divide-with-remainder 11, 3                      .
 118     .                                                  ╎3                                                .
 119     .                                                  ╎2                                                .
 120     .                                                  ╎─────────────────────────────────────────────────.
 121     .                                                  ╎                                                 .
 122   ]
 123 ]
 124 
 125 after <global-keypress> [
 126   # F4? load all code and run all sandboxes.
 127   {
 128     do-run?:bool <- equal k, 65532/F4
 129     break-unless do-run?
 130     screen <- update-status screen, [running...       ], 245/grey
 131     error?:bool <- run-sandboxes env, resources, screen
 132     # F4 might update warnings and results on both sides
 133     screen <- render-all screen, env, render
 134     {
 135       break-if error?
 136       screen <- update-status screen, [                 ], 245/grey
 137     }
 138     screen <- update-cursor screen, recipes, current-sandbox, sandbox-in-focus?, env
 139     loop +next-event
 140   }
 141 ]
 142 
 143 def run-sandboxes env:&:environment, resources:&:resources, screen:&:screen -> errors-found?:bool, env:&:environment, resources:&:resources, screen:&:screen [
 144   local-scope
 145   load-ingredients
 146   errors-found?:bool <- update-recipes env, resources, screen
 147   return-if errors-found?
 148   # check contents of right editor (sandbox)
 149   <run-sandboxes-begin>
 150   current-sandbox:&:editor <- get *env, current-sandbox:offset
 151   {
 152     sandbox-contents:text <- editor-contents current-sandbox
 153     break-unless sandbox-contents
 154     # if contents exist, first save them
 155     # run them and turn them into a new sandbox
 156     new-sandbox:&:sandbox <- new sandbox:type
 157     *new-sandbox <- put *new-sandbox, data:offset, sandbox-contents
 158     # push to head of sandbox list
 159     dest:&:sandbox <- get *env, sandbox:offset
 160     *new-sandbox <- put *new-sandbox, next-sandbox:offset, dest
 161     *env <- put *env, sandbox:offset, new-sandbox
 162     # update sandbox count
 163     sandbox-count:num <- get *env, number-of-sandboxes:offset
 164     sandbox-count <- add sandbox-count, 1
 165     *env <- put *env, number-of-sandboxes:offset, sandbox-count
 166     # clear sandbox editor
 167     init:&:duplex-list:char <- push 167/§, 0/tail
 168     *current-sandbox <- put *current-sandbox, data:offset, init
 169     *current-sandbox <- put *current-sandbox, top-of-screen:offset, init
 170   }
 171   # save all sandboxes before running, just in case we die when running
 172   save-sandboxes env, resources
 173   # run all sandboxes
 174   curr:&:sandbox <- get *env, sandbox:offset
 175   idx:num <- copy 0
 176   {
 177     break-unless curr
 178     curr <- update-sandbox curr, env, idx
 179     curr <- get *curr, next-sandbox:offset
 180     idx <- add idx, 1
 181     loop
 182   }
 183   <run-sandboxes-end>
 184 ]
 185 
 186 # load code from disk
 187 # replaced in a later layer (whereupon errors-found? will actually be set)
 188 def update-recipes env:&:environment, resources:&:resources, screen:&:screen -> errors-found?:bool, env:&:environment, resources:&:resources, screen:&:screen [
 189   local-scope
 190   load-ingredients
 191   recipes:&:editor <- get *env, recipes:offset
 192   in:text <- editor-contents recipes
 193   resources <- dump resources, [lesson/recipes.mu], in
 194   reload in
 195   errors-found? <- copy 0/false
 196 ]
 197 
 198 # replaced in a later layer
 199 def! update-sandbox sandbox:&:sandbox, env:&:environment, idx:num -> sandbox:&:sandbox, env:&:environment [
 200   local-scope
 201   load-ingredients
 202   data:text <- get *sandbox, data:offset
 203   response:text, _, fake-screen:&:screen <- run-sandboxed data
 204   *sandbox <- put *sandbox, response:offset, response
 205   *sandbox <- put *sandbox, screen:offset, fake-screen
 206 ]
 207 
 208 def update-status screen:&:screen, msg:text, color:num -> screen:&:screen [
 209   local-scope
 210   load-ingredients
 211   screen <- move-cursor screen, 0, 2
 212   screen <- print screen, msg, color, 238/grey/background
 213 ]
 214 
 215 def save-sandboxes env:&:environment, resources:&:resources -> resources:&:resources [
 216   local-scope
 217   load-ingredients
 218   current-sandbox:&:editor <- get *env, current-sandbox:offset
 219   # first clear previous versions, in case we deleted some sandbox
 220   $system [rm lesson/[0-9]* >/dev/null 2>/dev/null]  # some shells can't handle '>&'
 221   curr:&:sandbox <- get *env, sandbox:offset
 222   idx:num <- copy 0
 223   {
 224     break-unless curr
 225     data:text <- get *curr, data:offset
 226     filename:text <- append [lesson/], idx
 227     resources <- dump resources, filename, data
 228     <end-save-sandbox>
 229     idx <- add idx, 1
 230     curr <- get *curr, next-sandbox:offset
 231     loop
 232   }
 233 ]
 234 
 235 def! render-sandbox-side screen:&:screen, env:&:environment, {render-editor: (recipe (address screen) (address editor) -> number number (address screen) (address editor))} -> screen:&:screen, env:&:environment [
 236   local-scope
 237   load-ingredients
 238   trace 11, [app], [render sandbox side]
 239   current-sandbox:&:editor <- get *env, current-sandbox:offset
 240   row:num, column:num <- copy 1, 0
 241   left:num <- get *current-sandbox, left:offset
 242   right:num <- get *current-sandbox, right:offset
 243   # render sandbox editor
 244   render-from:num <- get *env, render-from:offset
 245   {
 246     render-current-sandbox?:bool <- equal render-from, -1
 247     break-unless render-current-sandbox?
 248     row, column, screen, current-sandbox <- call render-editor, screen, current-sandbox
 249     clear-screen-from screen, row, column, left, right
 250     row <- add row, 1
 251   }
 252   # render sandboxes
 253   draw-horizontal screen, row, left, right
 254   sandbox:&:sandbox <- get *env, sandbox:offset
 255   row, screen <- render-sandboxes screen, sandbox, left, right, row, render-from
 256   clear-rest-of-screen screen, row, left, right
 257 ]
 258 
 259 def render-sandboxes screen:&:screen, sandbox:&:sandbox, left:num, right:num, row:num, render-from:num, idx:num -> row:num, screen:&:screen, sandbox:&:sandbox [
 260   local-scope
 261   load-ingredients
 262   return-unless sandbox
 263   screen-height:num <- screen-height screen
 264   at-bottom?:bool <- greater-or-equal row, screen-height
 265   return-if at-bottom?:bool
 266   hidden?:bool <- lesser-than idx, render-from
 267   {
 268     break-if hidden?
 269     # render sandbox menu
 270     row <- add row, 1
 271     screen <- move-cursor screen, row, left
 272     screen <- render-sandbox-menu screen, idx, left, right
 273     # save menu row so we can detect clicks to it later
 274     *sandbox <- put *sandbox, starting-row-on-screen:offset, row
 275     # render sandbox contents
 276     row <- add row, 1
 277     screen <- move-cursor screen, row, left
 278     sandbox-data:text <- get *sandbox, data:offset
 279     row, screen <- render-code screen, sandbox-data, left, right, row
 280     *sandbox <- put *sandbox, code-ending-row-on-screen:offset, row
 281     # render sandbox warnings, screen or response, in that order
 282     sandbox-response:text <- get *sandbox, response:offset
 283     <render-sandbox-results>
 284     {
 285       sandbox-screen:&:screen <- get *sandbox, screen:offset
 286       empty-screen?:bool <- fake-screen-is-empty? sandbox-screen
 287       break-if empty-screen?
 288       row, screen <- render-screen screen, sandbox-screen, left, right, row
 289     }
 290     {
 291       break-unless empty-screen?
 292       <render-sandbox-response>
 293       row, screen <- render-text screen, sandbox-response, left, right, 245/grey, row
 294     }
 295     +render-sandbox-end
 296     at-bottom?:bool <- greater-or-equal row, screen-height
 297     return-if at-bottom?
 298     # draw solid line after sandbox
 299     draw-horizontal screen, row, left, right
 300   }
 301   # if hidden, reset row attributes
 302   {
 303     break-unless hidden?
 304     *sandbox <- put *sandbox, starting-row-on-screen:offset, 0
 305     *sandbox <- put *sandbox, code-ending-row-on-screen:offset, 0
 306     <end-render-sandbox-reset-hidden>
 307   }
 308   # draw next sandbox
 309   next-sandbox:&:sandbox <- get *sandbox, next-sandbox:offset
 310   next-idx:num <- add idx, 1
 311   row, screen <- render-sandboxes screen, next-sandbox, left, right, row, render-from, next-idx
 312 ]
 313 
 314 def render-sandbox-menu screen:&:screen, sandbox-index:num, left:num, right:num -> screen:&:screen [
 315   local-scope
 316   load-ingredients
 317   move-cursor-to-column screen, left
 318   edit-button-left:num, edit-button-right:num, copy-button-left:num, copy-button-right:num, delete-button-left:num <- sandbox-menu-columns left, right
 319   print screen, sandbox-index, 232/dark-grey, 245/grey
 320   start-buttons:num <- subtract edit-button-left, 1
 321   clear-line-until screen, start-buttons, 245/grey
 322   print screen, [edit], 232/black, 94/background-orange
 323   clear-line-until screen, edit-button-right, 94/background-orange
 324   _, col:num <- cursor-position screen
 325   at-start-of-copy-button?:bool <- equal col, copy-button-left
 326   assert at-start-of-copy-button?, [aaa]
 327   print screen, [copy], 232/black, 58/background-green
 328   clear-line-until screen, copy-button-right, 58/background-green
 329   _, col:num <- cursor-position screen
 330   at-start-of-delete-button?:bool <- equal col, delete-button-left
 331   assert at-start-of-delete-button?, [bbb]
 332   print screen, [delete], 232/black, 52/background-red
 333   clear-line-until screen, right, 52/background-red
 334 ]
 335 
 336 # divide up the menu bar for a sandbox into 3 segments, for edit/copy/delete buttons
 337 # delete-button-right == right
 338 # all left/right pairs are inclusive
 339 def sandbox-menu-columns left:num, right:num -> edit-button-left:num, edit-button-right:num, copy-button-left:num, copy-button-right:num, delete-button-left:num [
 340   local-scope
 341   load-ingredients
 342   start-buttons:num <- add left, 4/space-for-sandbox-index
 343   buttons-space:num <- subtract right, start-buttons
 344   button-width:num <- divide-with-remainder buttons-space, 3  # integer division
 345   buttons-wide-enough?:bool <- greater-or-equal button-width, 8
 346   assert buttons-wide-enough?, [sandbox must be at least 30 or so characters wide]
 347   edit-button-left:num <- copy start-buttons
 348   copy-button-left:num <- add start-buttons, button-width
 349   edit-button-right:num <- subtract copy-button-left, 1
 350   delete-button-left:num <- subtract right, button-width
 351   copy-button-right:num <- subtract delete-button-left, 1
 352 ]
 353 
 354 # print a text 's' to 'editor' in 'color' starting at 'row'
 355 # clear rest of last line, move cursor to next line
 356 def render-text screen:&:screen, s:text, left:num, right:num, color:num, row:num -> row:num, screen:&:screen [
 357   local-scope
 358   load-ingredients
 359   return-unless s
 360   column:num <- copy left
 361   screen <- move-cursor screen, row, column
 362   screen-height:num <- screen-height screen
 363   i:num <- copy 0
 364   len:num <- length *s
 365   {
 366     +next-character
 367     done?:bool <- greater-or-equal i, len
 368     break-if done?
 369     done? <- greater-or-equal row, screen-height
 370     break-if done?
 371     c:char <- index *s, i
 372     {
 373       # at right? wrap.
 374       at-right?:bool <- equal column, right
 375       break-unless at-right?
 376       # print wrap icon
 377       wrap-icon:char <- copy 8617/loop-back-to-left
 378       print screen, wrap-icon, 245/grey
 379       column <- copy left
 380       row <- add row, 1
 381       screen <- move-cursor screen, row, column
 382       loop +next-character  # retry i
 383     }
 384     i <- add i, 1
 385     {
 386       # newline? move to left rather than 0
 387       newline?:bool <- equal c, 10/newline
 388       break-unless newline?
 389       # clear rest of line in this window
 390       {
 391         done?:bool <- greater-than column, right
 392         break-if done?
 393         space:char <- copy 32/space
 394         print screen, space
 395         column <- add column, 1
 396         loop
 397       }
 398       row <- add row, 1
 399       column <- copy left
 400       screen <- move-cursor screen, row, column
 401       loop +next-character
 402     }
 403     print screen, c, color
 404     column <- add column, 1
 405     loop
 406   }
 407   was-at-left?:bool <- equal column, left
 408   clear-line-until screen, right
 409   {
 410     break-if was-at-left?
 411     row <- add row, 1
 412   }
 413   move-cursor screen, row, left
 414 ]
 415 
 416 # assumes programming environment has no sandboxes; restores them from previous session
 417 def restore-sandboxes env:&:environment, resources:&:resources -> env:&:environment [
 418   local-scope
 419   load-ingredients
 420   # read all scenarios, pushing them to end of a list of scenarios
 421   idx:num <- copy 0
 422   curr:&:sandbox <- copy 0
 423   prev:&:sandbox <- copy 0
 424   {
 425     filename:text <- append [lesson/], idx
 426     contents:text <- slurp resources, filename
 427     break-unless contents  # stop at first error; assuming file didn't exist
 428                            # todo: handle empty sandbox
 429     # create new sandbox for file
 430     curr <- new sandbox:type
 431     *curr <- put *curr, data:offset, contents
 432     <end-restore-sandbox>
 433     {
 434       break-if idx
 435       *env <- put *env, sandbox:offset, curr
 436     }
 437     {
 438       break-unless idx
 439       *prev <- put *prev, next-sandbox:offset, curr
 440     }
 441     idx <- add idx, 1
 442     prev <- copy curr
 443     loop
 444   }
 445   # update sandbox count
 446   *env <- put *env, number-of-sandboxes:offset, idx
 447 ]
 448 
 449 # print the fake sandbox screen to 'screen' with appropriate delimiters
 450 # leave cursor at start of next line
 451 def render-screen screen:&:screen, sandbox-screen:&:screen, left:num, right:num, row:num -> row:num, screen:&:screen [
 452   local-scope
 453   load-ingredients
 454   return-unless sandbox-screen
 455   # print 'screen:'
 456   row <- render-text screen, [screen:], left, right, 245/grey, row
 457   screen <- move-cursor screen, row, left
 458   # start printing sandbox-screen
 459   column:num <- copy left
 460   s-width:num <- screen-width sandbox-screen
 461   s-height:num <- screen-height sandbox-screen
 462   buf:&:@:screen-cell <- get *sandbox-screen, data:offset
 463   stop-printing:num <- add left, s-width, 3
 464   max-column:num <- min stop-printing, right
 465   i:num <- copy 0
 466   len:num <- length *buf
 467   screen-height:num <- screen-height screen
 468   {
 469     done?:bool <- greater-or-equal i, len
 470     break-if done?
 471     done? <- greater-or-equal row, screen-height
 472     break-if done?
 473     column <- copy left
 474     screen <- move-cursor screen, row, column
 475     # initial leader for each row: two spaces and a '.'
 476     space:char <- copy 32/space
 477     print screen, space, 245/grey
 478     print screen, space, 245/grey
 479     full-stop:char <- copy 46/period
 480     print screen, full-stop, 245/grey
 481     column <- add left, 3
 482     {
 483       # print row
 484       row-done?:bool <- greater-or-equal column, max-column
 485       break-if row-done?
 486       curr:screen-cell <- index *buf, i
 487       c:char <- get curr, contents:offset
 488       color:num <- get curr, color:offset
 489       {
 490         # damp whites down to grey
 491         white?:bool <- equal color, 7/white
 492         break-unless white?
 493         color <- copy 245/grey
 494       }
 495       print screen, c, color
 496       column <- add column, 1
 497       i <- add i, 1
 498       loop
 499     }
 500     # print final '.'
 501     print screen, full-stop, 245/grey
 502     column <- add column, 1
 503     {
 504       # clear rest of current line
 505       line-done?:bool <- greater-than column, right
 506       break-if line-done?
 507       print screen, space
 508       column <- add column, 1
 509       loop
 510     }
 511     row <- add row, 1
 512     loop
 513   }
 514 ]
 515 
 516 scenario run-updates-results [
 517   local-scope
 518   trace-until 100/app  # trace too long
 519   assume-screen 100/width, 12/height
 520   # define a recipe (no indent for the 'add' line below so column numbers are more obvious)
 521   assume-resources [
 522     [lesson/recipes.mu] <- [
 523       ||
 524       |recipe foo [|
 525       |  local-scope|
 526       |  z:num <- add 2, 2|
 527       |  reply z|
 528       |]|
 529     ]
 530   ]
 531   # sandbox editor contains an instruction without storing outputs
 532   env:&:environment <- new-programming-environment resources, screen, [foo]  # contents of sandbox editor
 533   # run the code in the editors
 534   assume-console [
 535     press F4
 536   ]
 537   event-loop screen, console, env, resources
 538   screen-should-contain [
 539     .                                                                                 run (F4)           .
 540     .                                                  ╎                                                 .
 541     .recipe foo [                                      ╎─────────────────────────────────────────────────.
 542     .  local-scope                                     ╎0   edit          copy            delete         .
 543     .  z:num <- add 2, 2                               ╎foo                                              .
 544     .  reply z                                         ╎4                                                .
 545     .]                                                 ╎─────────────────────────────────────────────────.
 546     .                                                  ╎                                                 .
 547     .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎                                                 .
 548     .                                                  ╎                                                 .
 549   ]
 550   # make a change (incrementing one of the args to 'add'), then rerun
 551   assume-console [
 552     left-click 4, 28  # one past the value of the second arg
 553     press backspace
 554     type [3]
 555     press F4
 556   ]
 557   run [
 558     event-loop screen, console, env, resources
 559   ]
 560   # check that screen updates the result on the right
 561   screen-should-contain [
 562     .                                                                                 run (F4)           .
 563     .                                                  ╎                                                 .
 564     .recipe foo [                                      ╎─────────────────────────────────────────────────.
 565     .  local-scope                                     ╎0   edit          copy            delete         .
 566     .  z:num <- add 2, 3                               ╎foo                                              .
 567     .  reply z                                         ╎5                                                .
 568     .]                                                 ╎─────────────────────────────────────────────────.
 569     .                                                  ╎                                                 .
 570     .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎                                                 .
 571     .                                                  ╎                                                 .
 572   ]
 573 ]
 574 
 575 scenario run-instruction-manages-screen-per-sandbox [
 576   local-scope
 577   trace-until 100/app  # trace too long
 578   assume-screen 100/width, 20/height
 579   # empty recipes
 580   assume-resources [
 581   ]
 582   # sandbox editor contains an instruction
 583   env:&:environment <- new-programming-environment resources, screen, [print screen, 4]  # contents of sandbox editor
 584   # run the code in the editor
 585   assume-console [
 586     press F4
 587   ]
 588   run [
 589     event-loop screen, console, env, resources
 590   ]
 591   # check that it prints a little toy screen
 592   screen-should-contain [
 593     .                                                                                 run (F4)           .
 594     .                                                  ╎                                                 .
 595     .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎─────────────────────────────────────────────────.
 596     .                                                  ╎0   edit          copy            delete         .
 597     .                                                  ╎print screen, 4                                  .
 598     .                                                  ╎screen:                                          .
 599     .                                                  ╎  .4                             .               .
 600     .                                                  ╎  .                              .               .
 601     .                                                  ╎  .                              .               .
 602     .                                                  ╎  .                              .               .
 603     .                                                  ╎  .                              .               .
 604     .                                                  ╎─────────────────────────────────────────────────.
 605     .                                                  ╎                                                 .
 606   ]
 607 ]
 608 
 609 def editor-contents editor:&:editor -> result:text [
 610   local-scope
 611   load-ingredients
 612   buf:&:buffer <- new-buffer 80
 613   curr:&:duplex-list:char <- get *editor, data:offset
 614   # skip § sentinel
 615   assert curr, [editor without data is illegal; must have at least a sentinel]
 616   curr <- next curr
 617   return-unless curr, 0
 618   {
 619     break-unless curr
 620     c:char <- get *curr, value:offset
 621     buf <- append buf, c
 622     curr <- next curr
 623     loop
 624   }
 625   result <- buffer-to-array buf
 626 ]
 627 
 628 scenario editor-provides-edited-contents [
 629   local-scope
 630   assume-screen 10/width, 5/height
 631   e:&:editor <- new-editor [abc], 0/left, 10/right
 632   assume-console [
 633     left-click 1, 2
 634     type [def]
 635   ]
 636   run [
 637     editor-event-loop screen, console, e
 638     s:text <- editor-contents e
 639     1:@:char/raw <- copy *s
 640   ]
 641   memory-should-contain [
 642     1:array:character <- [abdefc]
 643   ]
 644 ]
 645 
 646 # keep the bottom of recipes from scrolling off the screen
 647 
 648 scenario scrolling-down-past-bottom-of-recipe-editor [
 649   local-scope
 650   trace-until 100/app
 651   assume-screen 100/width, 10/height
 652   assume-resources [
 653   ]
 654   env:&:environment <- new-programming-environment resources, screen, []
 655   render-all screen, env, render
 656   assume-console [
 657     press enter
 658     press down-arrow
 659   ]
 660   event-loop screen, console, env, resources
 661   # no scroll
 662   screen-should-contain [
 663     .                                                                                 run (F4)           .
 664     .                                                  ╎                                                 .
 665     .                                                  ╎─────────────────────────────────────────────────.
 666     .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎                                                 .
 667     .                                                  ╎                                                 .
 668   ]
 669 ]
 670 
 671 scenario cursor-down-in-recipe-editor [
 672   local-scope
 673   trace-until 100/app
 674   assume-screen 100/width, 10/height
 675   assume-resources [
 676   ]
 677   env:&:environment <- new-programming-environment resources, screen, []
 678   render-all screen, env, render
 679   assume-console [
 680     press enter
 681     press up-arrow
 682     press down-arrow  # while cursor isn't at bottom
 683   ]
 684   event-loop screen, console, env, resources
 685   cursor:char <- copy 9251/␣
 686   print screen, cursor
 687   # cursor moves back to bottom
 688   screen-should-contain [
 689     .                                                                                 run (F4)           .
 690     .                                                  ╎                                                 .
 691     .␣                                                 ╎─────────────────────────────────────────────────.
 692     .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎                                                 .
 693     .                                                  ╎                                                 .
 694   ]
 695 ]
 696 
 697 # we'll not use the recipe-editor's 'bottom' element directly, because later
 698 # layers will add other stuff to the left side below the editor (error messages)
 699 
 700 container environment [
 701   recipe-bottom:num
 702 ]
 703 
 704 after <render-recipe-components-end> [
 705   *env <- put *env, recipe-bottom:offset, row
 706 ]
 707 
 708 after <global-keypress> [
 709   {
 710     break-if sandbox-in-focus?
 711     down-arrow?:bool <- equal k, 65516/down-arrow
 712     break-unless down-arrow?
 713     recipe-editor:&:editor <- get *env, recipes:offset
 714     recipe-cursor-row:num <- get *recipe-editor, cursor-row:offset
 715     recipe-editor-bottom:num <- get *recipe-editor, bottom:offset
 716     at-bottom-of-editor?:bool <- greater-or-equal recipe-cursor-row, recipe-editor-bottom
 717     break-unless at-bottom-of-editor?
 718     more-to-scroll?:bool <- more-to-scroll? env, screen
 719     break-if more-to-scroll?
 720     loop +next-event
 721   }
 722   {
 723     break-if sandbox-in-focus?
 724     page-down?:bool <- equal k, 65518/page-down
 725     break-unless page-down?
 726     more-to-scroll?:bool <- more-to-scroll? env, screen
 727     break-if more-to-scroll?
 728     loop +next-event
 729   }
 730 ]
 731 
 732 after <global-type> [
 733   {
 734     break-if sandbox-in-focus?
 735     page-down?:bool <- equal k, 6/ctrl-f
 736     break-unless page-down?
 737     more-to-scroll?:bool <- more-to-scroll? env, screen
 738     break-if more-to-scroll?
 739     loop +next-event
 740   }
 741 ]
 742 
 743 def more-to-scroll? env:&:environment, screen:&:screen -> result:bool [
 744   local-scope
 745   load-ingredients
 746   recipe-bottom:num <- get *env, recipe-bottom:offset
 747   height:num <- screen-height screen
 748   result <- greater-or-equal recipe-bottom, height
 749 ]
 750 
 751 scenario scrolling-down-past-bottom-of-recipe-editor-2 [
 752   local-scope
 753   trace-until 100/app
 754   assume-screen 100/width, 10/height
 755   assume-resources [
 756   ]
 757   env:&:environment <- new-programming-environment resources, screen, []
 758   render-all screen, env, render
 759   assume-console [
 760     # add a line
 761     press enter
 762     # cursor back to top line
 763     press up-arrow
 764     # try to scroll
 765     press page-down  # or ctrl-f
 766   ]
 767   event-loop screen, console, env, resources
 768   # no scroll, and cursor remains at top line
 769   screen-should-contain [
 770     .                                                                                 run (F4)           .
 771     .                                                  ╎                                                 .
 772     .                                                  ╎─────────────────────────────────────────────────.
 773     .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎                                                 .
 774     .                                                  ╎                                                 .
 775   ]
 776 ]
 777 
 778 scenario scrolling-down-past-bottom-of-recipe-editor-3 [
 779   local-scope
 780   trace-until 100/app
 781   assume-screen 100/width, 10/height
 782   assume-resources [
 783   ]
 784   env:&:environment <- new-programming-environment resources, screen, [ab
 785 cd]
 786   render-all screen, env, render
 787   assume-console [
 788     # add a line
 789     press enter
 790     # switch to sandbox
 791     press ctrl-n
 792     # move cursor
 793     press down-arrow
 794   ]
 795   event-loop screen, console, env, resources
 796   cursor:char <- copy 9251/␣
 797   print screen, cursor
 798   # no scroll on recipe side, cursor moves on sandbox side
 799   screen-should-contain [
 800     .                                                                                 run (F4)           .
 801     .                                                  ╎ab                                               .
 802     .                                                  ╎␣d                                               .
 803     .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎─────────────────────────────────────────────────.
 804     .                                                  ╎                                                 .
 805   ]
 806 ]
 807 
 808 # scrolling through sandboxes
 809 
 810 scenario scrolling-down-past-bottom-of-sandbox-editor [
 811   local-scope
 812   trace-until 100/app  # trace too long
 813   assume-screen 100/width, 10/height
 814   # initialize
 815   assume-resources [
 816   ]
 817   env:&:environment <- new-programming-environment resources, screen, [add 2, 2]
 818   render-all screen, env, render
 819   assume-console [
 820     # create a sandbox
 821     press F4
 822   ]
 823   event-loop screen, console, env, resources
 824   screen-should-contain [
 825     .                                                                                 run (F4)           .
 826     .                                                  ╎                                                 .
 827     .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎─────────────────────────────────────────────────.
 828     .                                                  ╎0   edit          copy            delete         .
 829     .                                                  ╎add 2, 2                                         .
 830   ]
 831   # switch to sandbox window and hit 'page-down'
 832   assume-console [
 833     press ctrl-n
 834     press page-down
 835   ]
 836   run [
 837     event-loop screen, console, env, resources
 838     cursor:char <- copy 9251/␣
 839     print screen, cursor
 840   ]
 841   # sandbox editor hidden; first sandbox displayed
 842   # cursor moves to first sandbox
 843   screen-should-contain [
 844     .                                                                                 run (F4)           .
 845     .                                                  ╎─────────────────────────────────────────────────.
 846     .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎␣   edit          copy            delete         .
 847     .                                                  ╎add 2, 2                                         .
 848     .                                                  ╎4                                                .
 849   ]
 850   # hit 'page-up'
 851   assume-console [
 852     press page-up
 853   ]
 854   run [
 855     event-loop screen, console, env, resources
 856     cursor:char <- copy 9251/␣
 857     print screen, cursor
 858   ]
 859   # sandbox editor displays again, cursor is in editor
 860   screen-should-contain [
 861     .                                                                                 run (F4)           .
 862     .                                                  ╎␣                                                .
 863     .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎─────────────────────────────────────────────────.
 864     .                                                  ╎0   edit          copy            delete         .
 865     .                                                  ╎add 2, 2                                         .
 866   ]
 867 ]
 868 
 869 # page-down on sandbox side updates render-from to scroll sandboxes
 870 after <global-keypress> [
 871   {
 872     break-unless sandbox-in-focus?
 873     page-down?:bool <- equal k, 65518/page-down
 874     break-unless page-down?
 875     sandbox:&:sandbox <- get *env, sandbox:offset
 876     break-unless sandbox
 877     # slide down if possible
 878     {
 879       render-from:num <- get *env, render-from:offset
 880       number-of-sandboxes:num <- get *env, number-of-sandboxes:offset
 881       max:num <- subtract number-of-sandboxes, 1
 882       at-end?:bool <- greater-or-equal render-from, max
 883       jump-if at-end?, +finish-event  # render nothing
 884       render-from <- add render-from, 1
 885       *env <- put *env, render-from:offset, render-from
 886     }
 887     hide-screen screen
 888     screen <- render-sandbox-side screen, env, render
 889     show-screen screen
 890     jump +finish-event
 891   }
 892 ]
 893 
 894 # update-cursor takes render-from into account
 895 after <update-cursor-special-cases> [
 896   {
 897     break-unless sandbox-in-focus?
 898     render-from:num <- get *env, render-from:offset
 899     scrolling?:bool <- greater-or-equal render-from, 0
 900     break-unless scrolling?
 901     cursor-column:num <- get *current-sandbox, left:offset
 902     screen <- move-cursor screen, 2/row, cursor-column  # highlighted sandbox will always start at row 2
 903     return
 904   }
 905 ]
 906 
 907 # 'page-up' on sandbox side is like 'page-down': updates render-from when necessary
 908 after <global-keypress> [
 909   {
 910     break-unless sandbox-in-focus?
 911     page-up?:bool <- equal k, 65519/page-up
 912     break-unless page-up?
 913     render-from:num <- get *env, render-from:offset
 914     at-beginning?:bool <- equal render-from, -1
 915     break-if at-beginning?
 916     render-from <- subtract render-from, 1
 917     *env <- put *env, render-from:offset, render-from
 918     hide-screen screen
 919     screen <- render-sandbox-side screen, env, render
 920     show-screen screen
 921     jump +finish-event
 922   }
 923 ]
 924 
 925 # sandbox belonging to 'env' whose next-sandbox is 'in'
 926 # return 0 if there's no such sandbox, either because 'in' doesn't exist in 'env', or because it's the first sandbox
 927 def previous-sandbox env:&:environment, in:&:sandbox -> out:&:sandbox [
 928   local-scope
 929   load-ingredients
 930   curr:&:sandbox <- get *env, sandbox:offset
 931   return-unless curr, 0/nil
 932   next:&:sandbox <- get *curr, next-sandbox:offset
 933   {
 934     return-unless next, 0/nil
 935     found?:bool <- equal next, in
 936     break-if found?
 937     curr <- copy next
 938     next <- get *curr, next-sandbox:offset
 939     loop
 940   }
 941   return curr
 942 ]
 943 
 944 scenario scrolling-down-past-bottom-on-recipe-side [
 945   local-scope
 946   trace-until 100/app  # trace too long
 947   assume-screen 100/width, 10/height
 948   # initialize sandbox side and create a sandbox
 949   assume-resources [
 950     [lesson/recipes.mu] <- [
 951       ||  # file containing just a newline
 952     ]
 953   ]
 954   # create a sandbox
 955   env:&:environment <- new-programming-environment resources, screen, [add 2, 2]
 956   render-all screen, env, render
 957   assume-console [
 958     press F4
 959   ]
 960   event-loop screen, console, env, resources
 961   # hit 'down' in recipe editor
 962   assume-console [
 963     press page-down
 964   ]
 965   run [
 966     event-loop screen, console, env, resources
 967     cursor:char <- copy 9251/␣
 968     print screen, cursor
 969   ]
 970   # cursor doesn't move when the end is already on-screen
 971   screen-should-contain [
 972     .                                                                                 run (F4)           .
 973     .␣                                                 ╎                                                 .
 974     .                                                  ╎─────────────────────────────────────────────────.
 975     .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎0   edit          copy            delete         .
 976     .                                                  ╎add 2, 2                                         .
 977   ]
 978 ]
 979 
 980 scenario scrolling-through-multiple-sandboxes [
 981   local-scope
 982   trace-until 100/app  # trace too long
 983   assume-screen 100/width, 10/height
 984   # initialize environment
 985   assume-resources [
 986   ]
 987   env:&:environment <- new-programming-environment resources, screen, []
 988   render-all screen, env, render
 989   # create 2 sandboxes
 990   assume-console [
 991     press ctrl-n
 992     type [add 2, 2]
 993     press F4
 994     type [add 1, 1]
 995     press F4
 996   ]
 997   event-loop screen, console, env, resources
 998   cursor:char <- copy 9251/␣
 999   print screen, cursor
1000   screen-should-contain [
1001     .                                                                                 run (F4)           .
1002     .                                                  ╎␣                                                .
1003     .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎─────────────────────────────────────────────────.
1004     .                                                  ╎0   edit          copy            delete         .
1005     .                                                  ╎add 1, 1                                         .
1006     .                                                  ╎2                                                .
1007     .                                                  ╎─────────────────────────────────────────────────.
1008     .                                                  ╎1   edit          copy            delete         .
1009     .                                                  ╎add 2, 2                                         .
1010     .                                                  ╎4                                                .
1011   ]
1012   # hit 'page-down'
1013   assume-console [
1014     press page-down
1015   ]
1016   run [
1017     event-loop screen, console, env, resources
1018     cursor:char <- copy 9251/␣
1019     print screen, cursor
1020   ]
1021   # sandbox editor hidden; first sandbox displayed
1022   # cursor moves to first sandbox
1023   screen-should-contain [
1024     .                                                                                 run (F4)           .
1025     .                                                  ╎─────────────────────────────────────────────────.
1026     .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎␣   edit          copy            delete         .
1027     .                                                  ╎add 1, 1                                         .
1028     .                                                  ╎2                                                .
1029     .                                                  ╎─────────────────────────────────────────────────.
1030     .                                                  ╎1   edit          copy            delete         .
1031     .                                                  ╎add 2, 2                                         .
1032     .                                                  ╎4                                                .
1033   ]
1034   # hit 'page-down' again
1035   assume-console [
1036     press page-down
1037   ]
1038   run [
1039     event-loop screen, console, env, resources
1040   ]
1041   # just second sandbox displayed
1042   screen-should-contain [
1043     .                                                                                 run (F4)           .
1044     .                                                  ╎─────────────────────────────────────────────────.
1045     .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎1   edit          copy            delete         .
1046     .                                                  ╎add 2, 2                                         .
1047     .                                                  ╎4                                                .
1048     .                                                  ╎─────────────────────────────────────────────────.
1049     .                                                  ╎                                                 .
1050   ]
1051   # hit 'page-down' again
1052   assume-console [
1053     press page-down
1054   ]
1055   run [
1056     event-loop screen, console, env, resources
1057   ]
1058   # no change
1059   screen-should-contain [
1060     .                                                                                 run (F4)           .
1061     .                                                  ╎─────────────────────────────────────────────────.
1062     .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎1   edit          copy            delete         .
1063     .                                                  ╎add 2, 2                                         .
1064     .                                                  ╎4                                                .
1065     .                                                  ╎─────────────────────────────────────────────────.
1066     .                                                  ╎                                                 .
1067   ]
1068   # hit 'page-up'
1069   assume-console [
1070     press page-up
1071   ]
1072   run [
1073     event-loop screen, console, env, resources
1074   ]
1075   # back to displaying both sandboxes without editor
1076   screen-should-contain [
1077     .                                                                                 run (F4)           .
1078     .                                                  ╎─────────────────────────────────────────────────.
1079     .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎0   edit          copy            delete         .
1080     .                                                  ╎add 1, 1                                         .
1081     .                                                  ╎2                                                .
1082     .                                                  ╎─────────────────────────────────────────────────.
1083     .                                                  ╎1   edit          copy            delete         .
1084     .                                                  ╎add 2, 2                                         .
1085     .                                                  ╎4                                                .
1086   ]
1087   # hit 'page-up' again
1088   assume-console [
1089     press page-up
1090   ]
1091   run [
1092     event-loop screen, console, env, resources
1093     cursor:char <- copy 9251/␣
1094     print screen, cursor
1095   ]
1096   # back to displaying both sandboxes as well as editor
1097   screen-should-contain [
1098     .                                                                                 run (F4)           .
1099     .                                                  ╎␣                                                .
1100     .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎─────────────────────────────────────────────────.
1101     .                                                  ╎0   edit          copy            delete         .
1102     .                                                  ╎add 1, 1                                         .
1103     .                                                  ╎2                                                .
1104     .                                                  ╎─────────────────────────────────────────────────.
1105     .                                                  ╎1   edit          copy            delete         .
1106     .                                                  ╎add 2, 2                                         .
1107     .                                                  ╎4                                                .
1108   ]
1109   # hit 'page-up' again
1110   assume-console [
1111     press page-up
1112   ]
1113   run [
1114     event-loop screen, console, env, resources
1115     cursor:char <- copy 9251/␣
1116     print screen, cursor
1117   ]
1118   # no change
1119   screen-should-contain [
1120     .                                                                                 run (F4)           .
1121     .                                                  ╎␣                                                .
1122     .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎─────────────────────────────────────────────────.
1123     .                                                  ╎0   edit          copy            delete         .
1124     .                                                  ╎add 1, 1                                         .
1125     .                                                  ╎2                                                .
1126     .                                                  ╎─────────────────────────────────────────────────.
1127     .                                                  ╎1   edit          copy            delete         .
1128     .                                                  ╎add 2, 2                                         .
1129     .                                                  ╎4                                                .
1130   ]
1131 ]
1132 
1133 scenario scrolling-manages-sandbox-index-correctly [
1134   local-scope
1135   trace-until 100/app  # trace too long
1136   assume-screen 100/width, 10/height
1137   # initialize environment
1138   assume-resources [
1139   ]
1140   env:&:environment <- new-programming-environment resources, screen, []
1141   render-all screen, env, render
1142   # create a sandbox
1143   assume-console [
1144     press ctrl-n
1145     type [add 1, 1]
1146     press F4
1147   ]
1148   event-loop screen, console, env, resources
1149   screen-should-contain [
1150     .                                                                                 run (F4)           .
1151     .                                                  ╎                                                 .
1152     .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎─────────────────────────────────────────────────.
1153     .                                                  ╎0   edit          copy            delete         .
1154     .                                                  ╎add 1, 1                                         .
1155     .                                                  ╎2                                                .
1156     .                                                  ╎─────────────────────────────────────────────────.
1157     .                                                  ╎                                                 .
1158   ]
1159   # hit 'page-down' and 'page-up' a couple of times. sandbox index should be stable
1160   assume-console [
1161     press page-down
1162   ]
1163   run [
1164     event-loop screen, console, env, resources
1165   ]
1166   # sandbox editor hidden; first sandbox displayed
1167   # cursor moves to first sandbox
1168   screen-should-contain [
1169     .                                                                                 run (F4)           .
1170     .                                                  ╎─────────────────────────────────────────────────.
1171     .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎0   edit          copy            delete         .
1172     .                                                  ╎add 1, 1                                         .
1173     .                                                  ╎2                                                .
1174     .                                                  ╎─────────────────────────────────────────────────.
1175     .                                                  ╎                                                 .
1176   ]
1177   # hit 'page-up' again
1178   assume-console [
1179     press page-up
1180   ]
1181   run [
1182     event-loop screen, console, env, resources
1183   ]
1184   # back to displaying both sandboxes as well as editor
1185   screen-should-contain [
1186     .                                                                                 run (F4)           .
1187     .                                                  ╎                                                 .
1188     .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎─────────────────────────────────────────────────.
1189     .                                                  ╎0   edit          copy            delete         .
1190     .                                                  ╎add 1, 1                                         .
1191     .                                                  ╎2                                                .
1192     .                                                  ╎─────────────────────────────────────────────────.
1193     .                                                  ╎                                                 .
1194   ]
1195   # hit 'page-down'
1196   assume-console [
1197     press page-down
1198   ]
1199   run [
1200     event-loop screen, console, env, resources
1201   ]
1202   # sandbox editor hidden; first sandbox displayed
1203   # cursor moves to first sandbox
1204   screen-should-contain [
1205     .                                                                                 run (F4)           .
1206     .                                                  ╎─────────────────────────────────────────────────.
1207     .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎0   edit          copy            delete         .
1208     .                                                  ╎add 1, 1                                         .
1209     .                                                  ╎2                                                .
1210     .                                                  ╎─────────────────────────────────────────────────.
1211     .                                                  ╎                                                 .
1212   ]
1213 ]