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