about summary refs log tree commit diff stats
path: root/html/042name.cc.html
Commit message (Expand)AuthorAgeFilesLines
* 3764 - better colors for cross-linksKartik K. Agaram2017-03-081-4/+5
* 3761Kartik K. Agaram2017-03-071-80/+81
* 3750Kartik K. Agaram2017-03-021-13/+13
* 3749Kartik K. Agaram2017-03-021-13/+13
* 3746Kartik K. Agaram2017-02-071-8/+8
* 3725Kartik K. Agaram2016-12-271-74/+93
* 3716Kartik K. Agaram2016-12-261-0/+2
* 3713 - cross-link calls with definitions in htmlKartik K. Agaram2016-12-261-66/+66
* 3710Kartik K. Agaram2016-12-261-306/+306
* 3709 - line numbers in htmlKartik K. Agaram2016-12-261-311/+335
* 3667Kartik K. Agaram2016-11-111-3/+9
* 3604Kartik K. Agaram2016-10-271-2/+2
* 3578Kartik K. Agaram2016-10-231-0/+2
* 3561Kartik K. Agaram2016-10-221-1/+1
* 3544Kartik K. Agaram2016-10-221-1/+1
* 3543Kartik K. Agaram2016-10-221-1/+1
* 3524Kartik K. Agaram2016-10-201-6/+4
* 3431Kartik K. Agaram2016-09-301-18/+19
* 3395Kartik K. Agaram2016-09-171-21/+21
* 3315Kartik K. Agaram2016-09-101-6/+4
* 3266Kartik K. Agaram2016-08-271-23/+26
* 3158Kartik K. Agaram2016-07-271-3/+3
* 3102Kartik K. Agaram2016-07-051-12/+4
* 3021Kartik K. Agaram2016-05-271-0/+12
* 2996Kartik K. Agaram2016-05-211-8/+8
* 2866Kartik K. Agaram2016-04-251-12/+33
* 2812Kartik K. Agaram2016-03-271-46/+63
* 2745Kartik K. Agaram2016-03-091-1/+1
* 2744Kartik K. Agaram2016-03-091-3/+3
* 2743Kartik K. Agaram2016-03-091-97/+103
* 2710Kartik K. Agaram2016-02-251-2/+0
* 2706 - update htmlKartik K. Agaram2016-02-251-10/+5
* 2625Kartik K. Agaram2016-02-021-5/+5
* 2605Kartik K. Agaram2016-01-261-8/+12
* 2545Kartik K. Agaram2015-12-151-3/+3
* 2611Kartik K. Agaram2015-11-291-15/+20
* 2423 - describe shape-shifting in html docsKartik K. Agaram2015-11-101-69/+66
* 2175Kartik K. Agaram2015-09-061-3/+0
* 2062Kartik K. Agaram2015-08-231-74/+73
* 1949Kartik K. Agaram2015-08-061-69/+70
* 1925Kartik K. Agaram2015-08-031-3/+8
* 1885Kartik K. Agaram2015-07-291-110/+86
* 1853Kartik K. Agaram2015-07-251-93/+99
* 1778Kartik K. Agaram2015-07-131-0/+308
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799





















                                                                                                                   




















































































































































































































































































































































































                                                                                                                                                                                          







                                                                    







                                                                             





















                                                                             
                   




                                                                                 






                                                                  

















                                                                                      



















                                                                      
                                                                                                                             
                                                             
                                                   







                                              













































                                                                       
                                            
                                            




                         











                                                                                                                    















































                                                                       
                                            
                                            




                         


                                                             




















































































































































































                                                                                                                                            
## putting the environment together out of editors
#
# Consists of one editor on the left for recipes and one on the right for the
# sandbox.

recipe main [
  local-scope
  open-console
  initial-recipe:address:array:character <- restore [recipes.mu]
  initial-sandbox:address:array:character <- new []
  hide-screen 0/screen
  env:address:programming-environment-data <- new-programming-environment 0/screen, initial-recipe, initial-sandbox
  env <- restore-sandboxes env
  render-all 0/screen, env
  event-loop 0/screen, 0/console, env
  # never gets here
]

container programming-environment-data [
  recipes:address:editor-data
  recipe-warnings:address:array:character
  current-sandbox:address:editor-data
  sandbox-in-focus?:boolean  # false => cursor in recipes; true => cursor in current-sandbox
]

recipe new-programming-environment [
  local-scope
  screen:address <- next-ingredient
  initial-recipe-contents:address:array:character <- next-ingredient
  initial-sandbox-contents:address:array:character <- next-ingredient
  width:number <- screen-width screen
  height:number <- screen-height screen
  # top menu
  result:address:programming-environment-data <- new programming-environment-data:type
  draw-horizontal screen, 0, 0/left, width, 32/space, 0/black, 238/grey
  button-start:number <- subtract width, 20
  button-on-screen?:boolean <- greater-or-equal button-start, 0
  assert button-on-screen?, [screen too narrow for menu]
  screen <- move-cursor screen, 0/row, button-start
  run-button:address:array:character <- new [ run (F4) ]
  print-string screen, run-button, 255/white, 161/reddish
  # dotted line down the middle
  divider:number, _ <- divide-with-remainder width, 2
  draw-vertical screen, divider, 1/top, height, 9482/vertical-dotted
  # recipe editor on the left
  recipes:address:address:editor-data <- get-address *result, recipes:offset
  *recipes <- new-editor initial-recipe-contents, screen, 0/left, divider/right
  # sandbox editor on the right
  new-left:number <- add divider, 1
  current-sandbox:address:address:editor-data <- get-address *result, current-sandbox:offset
  *current-sandbox <- new-editor initial-sandbox-contents, screen, new-left, width/right
  +programming-environment-initialization
  reply result
]

recipe event-loop [
  local-scope
  screen:address <- next-ingredient
  console:address <- next-ingredient
  env:address:programming-environment-data <- next-ingredient
  recipes:address:editor-data <- get *env, recipes:offset
  current-sandbox:address:editor-data <- get *env, current-sandbox:offset
  sandbox-in-focus?:address:boolean <- get-address *env, sandbox-in-focus?:offset
  # if we fall behind we'll stop updating the screen, but then we have to
  # render the entire screen when we catch up.
  # todo: test this
  render-all-on-no-more-events?:boolean <- copy 0/false
  {
    # looping over each (keyboard or touch) event as it occurs
    +next-event
    e:event, console, found?:boolean, quit?:boolean <- read-event console
    loop-unless found?
    break-if quit?  # only in tests
    trace 10, [app], [next-event]
    <handle-event>
    # check for global events that will trigger regardless of which editor has focus
    {
      k:address:number <- maybe-convert e:event, keycode:variant
      break-unless k
      <global-keypress>
    }
    {
      c:address:character <- maybe-convert e:event, text:variant
      break-unless c
      <global-type>
    }
    # 'touch' event - send to both sides, see what picks it up
    {
      t:address:touch-event <- maybe-convert e:event, touch:variant
      break-unless t
      # ignore all but 'left-click' events for now
      # todo: test this
      touch-type:number <- get *t, type:offset
      is-left-click?:boolean <- equal touch-type, 65513/mouse-left
      loop-unless is-left-click?, +next-event:label
      # later exceptions for non-editor touches will go here
      <global-touch>
      # send to both editors
      _ <- move-cursor-in-editor screen, recipes, *t
      *sandbox-in-focus? <- move-cursor-in-editor screen, current-sandbox, *t
      screen <- update-cursor screen, recipes, current-sandbox, *sandbox-in-focus?
      loop +next-event:label
    }
    # 'resize' event - redraw editor
    # todo: test this after supporting resize in assume-console
    {
      r:address:resize-event <- maybe-convert e:event, resize:variant
      break-unless r
      # if more events, we're still resizing; wait until we stop
      more-events?:boolean <- has-more-events? console
      {
        break-unless more-events?
        render-all-on-no-more-events? <- copy 1/true  # no rendering now, full rendering on some future event
      }
      {
        break-if more-events?
        env <- resize screen, env
        screen <- render-all screen, env
        render-all-on-no-more-events? <- copy 0/false  # full render done
      }
      loop +next-event:label
    }
    # if it's not global and not a touch event, send to appropriate editor
    {
      hide-screen screen
      {
        break-if *sandbox-in-focus?
        screen, recipes, render?:boolean <- handle-keyboard-event screen, recipes, e:event
        # refresh screen only if no more events
        # if there are more events to process, wait for them to clear up, then make sure you render-all afterward.
        more-events?:boolean <- has-more-events? console
        {
          break-unless more-events?
          render-all-on-no-more-events? <- copy 1/true  # no rendering now, full rendering on some future event
          jump +finish-event:label
        }
        {
          break-if more-events?
          {
            break-unless render-all-on-no-more-events?
            # no more events, and we have to force render
            screen <- render-all screen, env
            render-all-on-no-more-events? <- copy 0/false
            jump +finish-event:label
          }
          # no more events, no force render
          {
            break-unless render?
            screen <- render-recipes screen, env
            jump +finish-event:label
          }
        }
      }
      {
        break-unless *sandbox-in-focus?
        screen, current-sandbox, render?:boolean <- handle-keyboard-event screen, current-sandbox, e:event
        # refresh screen only if no more events
        # if there are more events to process, wait for them to clear up, then make sure you render-all afterward.
        more-events?:boolean <- has-more-events? console
        {
          break-unless more-events?
          render-all-on-no-more-events? <- copy 1/true  # no rendering now, full rendering on some future event
          jump +finish-event:label
        }
        {
          break-if more-events?
          {
            break-unless render-all-on-no-more-events?
            # no more events, and we have to force render
            screen <- render-all screen, env
            render-all-on-no-more-events? <- copy 0/false
            jump +finish-event:label
          }
          # no more events, no force render
          {
            break-unless render?
            screen <- render-sandbox-side screen, env
            jump +finish-event:label
          }
        }
      }
      +finish-event
      screen <- update-cursor screen, recipes, current-sandbox, *sandbox-in-focus?
      show-screen screen
    }
    loop
  }
]

recipe resize [
  local-scope
  screen:address <- next-ingredient
  env:address:programming-environment-data <- next-ingredient
  clear-screen screen  # update screen dimensions
  width:number <- screen-width screen
  divider:number, _ <- divide-with-remainder width, 2
  # update recipe editor
  recipes:address:editor-data <- get *env, recipes:offset
  right:address:number <- get-address *recipes, right:offset
  *right <- subtract divider, 1
  # reset cursor (later we'll try to preserve its position)
  cursor-row:address:number <- get-address *recipes, cursor-row:offset
  *cursor-row <- copy 1
  cursor-column:address:number <- get-address *recipes, cursor-column:offset
  *cursor-column <- copy 0
  # update sandbox editor
  current-sandbox:address:editor-data <- get *env, current-sandbox:offset
  left:address:number <- get-address *current-sandbox, left:offset
  right:address:number <- get-address *current-sandbox, right:offset
  *left <- add divider, 1
  *right <- subtract width, 1
  # reset cursor (later we'll try to preserve its position)
  cursor-row:address:number <- get-address *current-sandbox, cursor-row:offset
  *cursor-row <- copy 1
  cursor-column:address:number <- get-address *current-sandbox, cursor-column:offset
  *cursor-column <- copy *left
  reply env/same-as-ingredient:1
]

scenario point-at-multiple-editors [
  $close-trace  # trace too long
  assume-screen 30/width, 5/height
  # initialize both halves of screen
  1:address:array:character <- new [abc]
  2:address:array:character <- new [def]
  3:address:programming-environment-data <- new-programming-environment screen:address, 1:address:array:character, 2:address:array:character
  # focus on both sides
  assume-console [
    left-click 1, 1
    left-click 1, 17
  ]
  # check cursor column in each
  run [
    event-loop screen:address, console:address, 3:address:programming-environment-data
    4:address:editor-data <- get *3:address:programming-environment-data, recipes:offset
    5:number <- get *4:address:editor-data, cursor-column:offset
    6:address:editor-data <- get *3:address:programming-environment-data, current-sandbox:offset
    7:number <- get *6:address:editor-data, cursor-column:offset
  ]
  memory-should-contain [
    5 <- 1
    7 <- 17
  ]
]

scenario edit-multiple-editors [
  $close-trace  # trace too long
  assume-screen 30/width, 5/height
  # initialize both halves of screen
  1:address:array:character <- new [abc]
  2:address:array:character <- new [def]
  3:address:programming-environment-data <- new-programming-environment screen:address, 1:address:array:character, 2:address:array:character
  render-all screen, 3:address:programming-environment-data
  # type one letter in each of them
  assume-console [
    left-click 1, 1
    type [0]
    left-click 1, 17
    type [1]
  ]
  run [
    event-loop screen:address, console:address, 3:address:programming-environment-data
    4:address:editor-data <- get *3:address:programming-environment-data, recipes:offset
    5:number <- get *4:address:editor-data, cursor-column:offset
    6:address:editor-data <- get *3:address:programming-environment-data, current-sandbox:offset
    7:number <- get *6:address:editor-data, cursor-column:offset
  ]
  screen-should-contain [
    .           run (F4)           .  # this line has a different background, but we don't test that yet
    .a0bc           d1ef          .
    .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊━━━━━━━━━━━━━━.
    .                             .
  ]
  memory-should-contain [
    5 <- 2  # cursor column of recipe editor
    7 <- 18  # cursor column of sandbox editor
  ]
  # show the cursor at the right window
  run [
    print-character screen:address, 9251//cursor
  ]
  screen-should-contain [
    .           run (F4)           .
    .a0bc           d1f          .
    .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊━━━━━━━━━━━━━━.
    .                             .
  ]
]

scenario multiple-editors-cover-only-their-own-areas [
  $close-trace  # trace too long
  assume-screen 60/width, 10/height
  run [
    1:address:array:character <- new [abc]
    2:address:array:character <- new [def]
    3:address:programming-environment-data <- new-programming-environment screen:address, 1:address:array:character, 2:address:array:character
    render-all screen, 3:address:programming-environment-data
  ]
  # divider isn't messed up
  screen-should-contain [
    .                                         run (F4)           .
    .abc                           def                          .
    .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.
    .                                                           .
    .                                                           .
  ]
]

scenario editor-in-focus-keeps-cursor [
  $close-trace  # trace too long
  assume-screen 30/width, 5/height
  1:address:array:character <- new [abc]
  2:address:array:character <- new [def]
  3:address:programming-environment-data <- new-programming-environment screen:address, 1:address:array:character, 2:address:array:character
  render-all screen, 3:address:programming-environment-data
  # initialize programming environment and highlight cursor
  assume-console []
  run [
    event-loop screen:address, console:address, 3:address:programming-environment-data
    print-character screen:address, 9251//cursor
  ]
  # is cursor at the right place?
  screen-should-contain [
    .           run (F4)           .
    .bc            def           .
    .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊━━━━━━━━━━━━━━.
    .                             .
  ]
  # now try typing a letter
  assume-console [
    type [z]
  ]
  run [
    event-loop screen:address, console:address, 3:address:programming-environment-data
    print-character screen:address, 9251//cursor
  ]
  # cursor should still be right
  screen-should-contain [
    .           run (F4)           .
    .zbc           def           .
    .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊━━━━━━━━━━━━━━.
    .                             .
  ]
]

scenario backspace-in-sandbox-editor-joins-lines [
  $close-trace  # trace too long
  assume-screen 30/width, 5/height
  # initialize sandbox side with two lines
  1:address:array:character <- new []
  2:address:array:character <- new [abc
def]
  3:address:programming-environment-data <- new-programming-environment screen:address, 1:address:array:character, 2:address:array:character
  render-all screen, 3:address:programming-environment-data
  screen-should-contain [
    .           run (F4)           .
    .               abc           .
    .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊def           .
    .               ┊━━━━━━━━━━━━━━.
    .                             .
  ]
  # position cursor at start of second line and hit backspace
  assume-console [
    left-click 2, 16
    press backspace
  ]
  run [
    event-loop screen:address, console:address, 3:address:programming-environment-data
    print-character screen:address, 9251//cursor
  ]
  # cursor moves to end of old line
  screen-should-contain [
    .           run (F4)           .
    .               abcef        .
    .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊━━━━━━━━━━━━━━.
    .                             .
  ]
]

recipe render-all [
  local-scope
  screen:address <- next-ingredient
  env:address:programming-environment-data <- next-ingredient
  trace 10, [app], [render all]
  hide-screen screen
  # top menu
  trace 11, [app], [render top menu]
  width:number <- screen-width screen
  draw-horizontal screen, 0, 0/left, width, 32/space, 0/black, 238/grey
  button-start:number <- subtract width, 20
  button-on-screen?:boolean <- greater-or-equal button-start, 0
  assert button-on-screen?, [screen too narrow for menu]
  screen <- move-cursor screen, 0/row, button-start
  run-button:address:array:character <- new [ run (F4) ]
  print-string screen, run-button, 255/white, 161/reddish
  # dotted line down the middle
  trace 11, [app], [render divider]
  divider:number, _ <- divide-with-remainder width, 2
  height:number <- screen-height screen
  draw-vertical screen, divider, 1/top, height, 9482/vertical-dotted
  #
  screen <- render-recipes screen, env
  screen <- render-sandbox-side screen, env
  # error message
  trace 11, [app], [render status]
  recipe-warnings:address:array:character <- get *env, recipe-warnings:offset
  {
    break-unless recipe-warnings
    status:address:array:character <- new [errors found]
    update-status screen, status, 1/red
  }
  #
  recipes:address:editor-data <- get *env, recipes:offset
  current-sandbox:address:editor-data <- get *env, current-sandbox:offset
  sandbox-in-focus?:boolean <- get *env, sandbox-in-focus?:offset
  screen <- update-cursor screen, recipes, current-sandbox, sandbox-in-focus?
  #
  show-screen screen
  reply screen/same-as-ingredient:0
]

recipe render-recipes [
  local-scope
  screen:address <- next-ingredient
  env:address:programming-environment-data <- next-ingredient
  trace 11, [app], [render recipes]
  recipes:address:editor-data <- get *env, recipes:offset
  # render recipes
  left:number <- get *recipes, left:offset
  right:number <- get *recipes, right:offset
  row:number, column:number, screen <- render screen, recipes
  clear-line-delimited screen, column, right
  recipe-warnings:address:array:character <- get *env, recipe-warnings:offset
  row <- add row, 1
  {
    # print any warnings
    break-unless recipe-warnings
    row, screen <- render-string screen, recipe-warnings, left, right, 1/red, row
  }
  # draw dotted line after recipes
  draw-horizontal screen, row, left, right, 9480/horizontal-dotted
  row <- add row, 1
  clear-screen-from screen, row, left, left, right
  reply screen/same-as-ingredient:0
]

# replaced in a later layer
recipe render-sandbox-side [
  local-scope
  screen:address <- next-ingredient
  env:address:programming-environment-data <- next-ingredient
  current-sandbox:address:editor-data <- get *env, current-sandbox:offset
  left:number <- get *current-sandbox, left:offset
  right:number <- get *current-sandbox, right:offset
  row:number, column:number, screen, current-sandbox <- render screen, current-sandbox
  clear-line-delimited screen, column, right
  row <- add row, 1
  # draw solid line after recipes (you'll see why in later layers)
  draw-horizontal screen, row, left, right, 9473/horizontal
  row <- add row, 1
  clear-screen-from screen, row, left, left, right
  reply screen/same-as-ingredient:0
]

recipe update-cursor [
  local-scope
  screen:address <- next-ingredient
  recipes:address:editor-data <- next-ingredient
  current-sandbox:address:editor-data <- next-ingredient
  sandbox-in-focus?:boolean <- next-ingredient
  {
    break-if sandbox-in-focus?
    cursor-row:number <- get *recipes, cursor-row:offset
    cursor-column:number <- get *recipes, cursor-column:offset
  }
  {
    break-unless sandbox-in-focus?
    cursor-row:number <- get *current-sandbox, cursor-row:offset
    cursor-column:number <- get *current-sandbox, cursor-column:offset
  }
  screen <- move-cursor screen, cursor-row, cursor-column
  reply screen/same-as-ingredient:0
]

# row, screen <- render-string screen:address, s:address:array:character, left:number, right:number, color:number, row:number
# print a string 's' to 'editor' in 'color' starting at 'row'
# clear rest of last line, move cursor to next line
recipe render-string [
  local-scope
  screen:address <- next-ingredient
  s:address:array:character <- next-ingredient
  left:number <- next-ingredient
  right:number <- next-ingredient
  color:number <- next-ingredient
  row:number <- next-ingredient
  reply-unless s, row/same-as-ingredient:5, screen/same-as-ingredient:0
  column:number <- copy left
  screen <- move-cursor screen, row, column
  screen-height:number <- screen-height screen
  i:number <- copy 0
  len:number <- length *s
  {
    +next-character
    done?:boolean <- greater-or-equal i, len
    break-if done?
    done? <- greater-or-equal row, screen-height
    break-if done?
    c:character <- index *s, i
    {
      # at right? wrap.
      at-right?:boolean <- equal column, right
      break-unless at-right?
      # print wrap icon
      print-character screen, 8617/loop-back-to-left, 245/grey
      column <- copy left
      row <- add row, 1
      screen <- move-cursor screen, row, column
      loop +next-character:label  # retry i
    }
    i <- add i, 1
    {
      # newline? move to left rather than 0
      newline?:boolean <- equal c, 10/newline
      break-unless newline?
      # clear rest of line in this window
      {
        done?:boolean <- greater-than column, right
        break-if done?
        print-character screen, 32/space
        column <- add column, 1
        loop
      }
      row <- add row, 1
      column <- copy left
      screen <- move-cursor screen, row, column
      loop +next-character:label
    }
    print-character screen, c, color
    column <- add column, 1
    loop
  }
  was-at-left?:boolean <- equal column, left
  clear-line-delimited screen, column, right
  {
    break-if was-at-left?
    row <- add row, 1
  }
  move-cursor row, left
  reply row/same-as-ingredient:5, screen/same-as-ingredient:0
]

# row, screen <- render-code-string screen:address, s:address:array:character, left:number, right:number, row:number
# like 'render-string' but with colorization for comments like in the editor
recipe render-code-string [
  local-scope
  screen:address <- next-ingredient
  s:address:array:character <- next-ingredient
  left:number <- next-ingredient
  right:number <- next-ingredient
  row:number <- next-ingredient
  reply-unless s, row/same-as-ingredient:4, screen/same-as-ingredient:0
  color:number <- copy 7/white
  column:number <- copy left
  screen <- move-cursor screen, row, column
  screen-height:number <- screen-height screen
  i:number <- copy 0
  len:number <- length *s
  {
    +next-character
    done?:boolean <- greater-or-equal i, len
    break-if done?
    done? <- greater-or-equal row, screen-height
    break-if done?
    c:character <- index *s, i
    <character-c-received>  # only line different from render-string
    {
      # at right? wrap.
      at-right?:boolean <- equal column, right
      break-unless at-right?
      # print wrap icon
      print-character screen, 8617/loop-back-to-left, 245/grey
      column <- copy left
      row <- add row, 1
      screen <- move-cursor screen, row, column
      loop +next-character:label  # retry i
    }
    i <- add i, 1
    {
      # newline? move to left rather than 0
      newline?:boolean <- equal c, 10/newline
      break-unless newline?
      # clear rest of line in this window
      {
        done?:boolean <- greater-than column, right
        break-if done?
        print-character screen, 32/space
        column <- add column, 1
        loop
      }
      row <- add row, 1
      column <- copy left
      screen <- move-cursor screen, row, column
      loop +next-character:label
    }
    print-character screen, c, color
    column <- add column, 1
    loop
  }
  was-at-left?:boolean <- equal column, left
  clear-line-delimited screen, column, right
  {
    break-if was-at-left?
    row <- add row, 1
  }
  move-cursor row, left
  reply row/same-as-ingredient:4, screen/same-as-ingredient:0
]

# ctrl-l - redraw screen (just in case it printed junk somehow)

after <global-type> [
  {
    redraw-screen?:boolean <- equal *c, 12/ctrl-l
    break-unless redraw-screen?
    screen <- render-all screen, env:address:programming-environment-data
    sync-screen screen
    loop +next-event:label
  }
]

# ctrl-n - switch focus
# todo: test this

after <global-type> [
  {
    switch-side?:boolean <- equal *c, 14/ctrl-n
    break-unless switch-side?
    *sandbox-in-focus? <- not *sandbox-in-focus?
    screen <- update-cursor screen, recipes, current-sandbox, *sandbox-in-focus?
    loop +next-event:label
  }
]

# ctrl-x - maximize/unmaximize the side with focus

scenario maximize-side [
  $close-trace  # trace too long
  assume-screen 30/width, 5/height
  # initialize both halves of screen
  1:address:array:character <- new [abc]
  2:address:array:character <- new [def]
  3:address:programming-environment-data <- new-programming-environment screen:address, 1:address:array:character, 2:address:array:character
  screen <- render-all screen, 3:address:programming-environment-data
  screen-should-contain [
    .           run (F4)           .
    .abc            def           .
    .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊━━━━━━━━━━━━━━.
    .                             .
  ]
  # hit ctrl-x
  assume-console [
    press ctrl-x
  ]
  run [
    event-loop screen:address, console:address, 3:address:programming-environment-data
  ]
  # only left side visible
  screen-should-contain [
    .           run (F4)           .
    .abc                           .
    .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈.
    .                              .
  ]
  # hit any key to toggle back
  assume-console [
    press ctrl-x
  ]
  run [
    event-loop screen:address, console:address, 3:address:programming-environment-data
  ]
  screen-should-contain [
    .           run (F4)           .
    .abc            def           .
    .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊━━━━━━━━━━━━━━.
    .                             .
  ]
]

#? # ctrl-t - browse trace
#? after <global-type> [
#?   {
#?     browse-trace?:boolean <- equal *c, 20/ctrl-t
#?     break-unless browse-trace?
#?     $browse-trace
#?     screen <- render-all screen, env:address:programming-environment-data
#?     loop +next-event:label
#?   }
#? ]

container programming-environment-data [
  maximized?:boolean
]

after <global-type> [
  {
    maximize?:boolean <- equal *c, 24/ctrl-x
    break-unless maximize?
    screen, console <- maximize screen, console, env:address:programming-environment-data
    loop +next-event:label
  }
]

recipe maximize [
  local-scope
  screen:address <- next-ingredient
  console:address <- next-ingredient
  env:address:programming-environment-data <- next-ingredient
  hide-screen screen
  # maximize one of the sides
  maximized?:address:boolean <- get-address *env, maximized?:offset
  *maximized? <- copy 1/true
  #
  sandbox-in-focus?:boolean <- get *env, sandbox-in-focus?:offset
  {
    break-if sandbox-in-focus?
    editor:address:editor-data <- get *env, recipes:offset
    right:address:number <- get-address *editor, right:offset
    *right <- screen-width screen
    *right <- subtract *right, 1
    screen <- render-recipes screen, env
  }
  {
    break-unless sandbox-in-focus?
    editor:address:editor-data <- get *env, current-sandbox:offset
    left:address:number <- get-address *editor, left:offset
    *left <- copy 0
    screen <- render-sandbox-side screen, env
  }
  show-screen screen
  reply screen/same-as-ingredient:0, console/same-as-ingredient:1
]

# when maximized, wait for any event and simply unmaximize
after <handle-event> [
  {
    maximized?:address:boolean <- get-address *env, maximized?:offset
    break-unless *maximized?
    *maximized? <- copy 0/false
    # undo maximize
    {
      break-if *sandbox-in-focus?
      editor:address:editor-data <- get *env, recipes:offset
      right:address:number <- get-address *editor, right:offset
      *right <- screen-width screen
      *right <- divide *right, 2
      *right <- subtract *right, 1
    }
    {
      break-unless *sandbox-in-focus?
      editor:address:editor-data <- get *env, current-sandbox:offset
      left:address:number <- get-address *editor, left:offset
      *left <- screen-width screen
      *left <- divide *left, 2
      *left <- add *left, 1
    }
    render-all screen, env
    show-screen screen
    loop +next-event:label
  }
]

## helpers

recipe draw-vertical [
  local-scope
  screen:address <- next-ingredient
  col:number <- next-ingredient
  y:number <- next-ingredient
  bottom:number <- next-ingredient
  style:character, style-found?:boolean <- next-ingredient
  {
    break-if style-found?
    style <- copy 9474/vertical
  }
  color:number, color-found?:boolean <- next-ingredient
  {
    # default color to white
    break-if color-found?
    color <- copy 245/grey
  }
  {
    continue?:boolean <- lesser-than y, bottom
    break-unless continue?
    screen <- move-cursor screen, y, col
    print-character screen, style, color
    y <- add y, 1
    loop
  }
]