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