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