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