https://github.com/akkartik/mu1/blob/master/edit/012-editor-undo.mu
   1 
   2 
   3 
   4 
   5 exclusive-container operation [
   6   typing:insert-operation
   7   move:move-operation
   8   delete:delete-operation
   9 ]
  10 
  11 container insert-operation [
  12   before-row:num
  13   before-column:num
  14   before-top-of-screen:&:duplex-list:char
  15   after-row:num
  16   after-column:num
  17   after-top-of-screen:&:duplex-list:char
  18   
  19   insert-from:&:duplex-list:char
  20   insert-until:&:duplex-list:char
  21   tag:num  
  22     
  23     
  24 ]
  25 
  26 container move-operation [
  27   before-row:num
  28   before-column:num
  29   before-top-of-screen:&:duplex-list:char
  30   after-row:num
  31   after-column:num
  32   after-top-of-screen:&:duplex-list:char
  33   tag:num  
  34     
  35     
  36     
  37     
  38     
  39     
  40     
  41 ]
  42 
  43 container delete-operation [
  44   before-row:num
  45   before-column:num
  46   before-top-of-screen:&:duplex-list:char
  47   after-row:num
  48   after-column:num
  49   after-top-of-screen:&:duplex-list:char
  50   deleted-text:&:duplex-list:char
  51   delete-from:&:duplex-list:char
  52   delete-until:&:duplex-list:char
  53   tag:num  
  54     
  55     
  56     
  57 ]
  58 
  59 
  60 container editor [
  61   undo:&:list:&:operation
  62   redo:&:list:&:operation
  63 ]
  64 
  65 
  66 after <handle-special-character> [
  67   {
  68     undo?:bool <- equal c, 26/ctrl-z
  69     break-unless undo?
  70     undo:&:list:&:operation <- get *editor, undo:offset
  71     break-unless undo
  72     op:&:operation <- first undo
  73     undo <- rest undo
  74     *editor <- put *editor, undo:offset, undo
  75     redo:&:list:&:operation <- get *editor, redo:offset
  76     redo <- push op, redo
  77     *editor <- put *editor, redo:offset, redo
  78     <handle-undo>
  79     return true/go-render
  80   }
  81 ]
  82 
  83 
  84 after <handle-special-character> [
  85   {
  86     redo?:bool <- equal c, 25/ctrl-y
  87     break-unless redo?
  88     redo:&:list:&:operation <- get *editor, redo:offset
  89     break-unless redo
  90     op:&:operation <- first redo
  91     redo <- rest redo
  92     *editor <- put *editor, redo:offset, redo
  93     undo:&:list:&:operation <- get *editor, undo:offset
  94     undo <- push op, undo
  95     *editor <- put *editor, undo:offset, undo
  96     <handle-redo>
  97     return true/go-render
  98   }
  99 ]
 100 
 101 
 102 
 103 scenario editor-can-undo-typing [
 104   local-scope
 105   
 106   assume-screen 10/width, 5/height
 107   e:&:editor <- new-editor [], 0/left, 10/right
 108   editor-render screen, e
 109   assume-console [
 110     type [0]
 111   ]
 112   editor-event-loop screen, console, e
 113   
 114   assume-console [
 115     press ctrl-z
 116   ]
 117   run [
 118     editor-event-loop screen, console, e
 119   ]
 120   
 121   screen-should-contain [
 122     .          .
 123     .          .
 124     .╌╌╌╌╌╌╌╌╌╌.
 125     .          .
 126   ]
 127   
 128   assume-console [
 129     type [1]
 130   ]
 131   run [
 132     editor-event-loop screen, console, e
 133   ]
 134   screen-should-contain [
 135     .          .
 136     .1         .
 137     .╌╌╌╌╌╌╌╌╌╌.
 138     .          .
 139   ]
 140 ]
 141 
 142 
 143 after <begin-insert-character> [
 144   top-before:&:duplex-list:char <- get *editor, top-of-screen:offset
 145   cursor-before:&:duplex-list:char <- get *editor, before-cursor:offset
 146 ]
 147 before <end-insert-character> [
 148   top-after:&:duplex-list:char <- get *editor, top-of-screen:offset
 149   cursor-row:num <- get *editor, cursor-row:offset
 150   cursor-column:num <- get *editor, cursor-column:offset
 151   undo:&:list:&:operation <- get *editor, undo:offset
 152   {
 153     
 154     break-unless undo
 155     op:&:operation <- first undo
 156     typing:insert-operation, is-insert?:bool <- maybe-convert *op, typing:variant
 157     break-unless is-insert?
 158     previous-coalesce-tag:num <- get typing, tag:offset
 159     break-unless previous-coalesce-tag
 160     before-cursor:&:duplex-list:char <- get *editor, before-cursor:offset
 161     insert-until:&:duplex-list:char <- next before-cursor
 162     typing <- put typing, insert-until:offset, insert-until
 163     typing <- put typing, after-row:offset, cursor-row
 164     typing <- put typing, after-column:offset, cursor-column
 165     typing <- put typing, after-top-of-screen:offset, top-after
 166     *op <- merge 0/insert-operation, typing
 167     break +done-adding-insert-operation
 168   }
 169   
 170   insert-from:&:duplex-list:char <- next cursor-before
 171   insert-to:&:duplex-list:char <- next insert-from
 172   op:&:operation <- new operation:type
 173   *op <- merge 0/insert-operation, save-row/before, save-column/before, top-before, cursor-row/after, cursor-column/after, top-after, insert-from, insert-to, 1/coalesce
 174   editor <- add-operation editor, op
 175   +done-adding-insert-operation
 176 ]
 177 
 178 
 179 after <begin-insert-enter> [
 180   cursor-row-before:num <- copy cursor-row
 181   cursor-column-before:num <- copy cursor-column
 182   top-before:&:duplex-list:char <- get *editor, top-of-screen:offset
 183   cursor-before:&:duplex-list:char <- get *editor, before-cursor:offset
 184 ]
 185 before <end-insert-enter> [
 186   top-after:&:duplex-list:char <- get *editor, top-of-screen:offset
 187   cursor-row:num <- get *editor, cursor-row:offset
 188   cursor-column:num <- get *editor, cursor-row:offset
 189   
 190   insert-from:&:duplex-list:char <- next cursor-before
 191   before-cursor:&:duplex-list:char <- get *editor, before-cursor:offset
 192   insert-to:&:duplex-list:char <- next before-cursor
 193   op:&:operation <- new operation:type
 194   *op <- merge 0/insert-operation, cursor-row-before, cursor-column-before, top-before, cursor-row/after, cursor-column/after, top-after, insert-from, insert-to, 0/never-coalesce
 195   editor <- add-operation editor, op
 196 ]
 197 
 198 
 199 
 200 
 201 
 202 def add-operation editor:&:editor, op:&:operation -> editor:&:editor [
 203   local-scope
 204   load-inputs
 205   undo:&:list:&:operation <- get *editor, undo:offset
 206   undo <- push op undo
 207   *editor <- put *editor, undo:offset, undo
 208   redo:&:list:&:operation <- get *editor, redo:offset
 209   redo <- copy null
 210   *editor <- put *editor, redo:offset, redo
 211 ]
 212 
 213 after <handle-undo> [
 214   {
 215     typing:insert-operation, is-insert?:bool <- maybe-convert *op, typing:variant
 216     break-unless is-insert?
 217     start:&:duplex-list:char <- get typing, insert-from:offset
 218     end:&:duplex-list:char <- get typing, insert-until:offset
 219     
 220     before-cursor:&:duplex-list:char <- prev start
 221     *editor <- put *editor, before-cursor:offset, before-cursor
 222     remove-between before-cursor, end
 223     cursor-row <- get typing, before-row:offset
 224     *editor <- put *editor, cursor-row:offset, cursor-row
 225     cursor-column <- get typing, before-column:offset
 226     *editor <- put *editor, cursor-column:offset, cursor-column
 227     top:&:duplex-list:char <- get typing, before-top-of-screen:offset
 228     *editor <- put *editor, top-of-screen:offset, top
 229   }
 230 ]
 231 
 232 scenario editor-can-undo-typing-multiple [
 233   local-scope
 234   
 235   assume-screen 10/width, 5/height
 236   e:&:editor <- new-editor [], 0/left, 10/right
 237   editor-render screen, e
 238   assume-console [
 239     type [012]
 240   ]
 241   editor-event-loop screen, console, e
 242   
 243   assume-console [
 244     press ctrl-z
 245   ]
 246   run [
 247     editor-event-loop screen, console, e
 248   ]
 249   
 250   screen-should-contain [
 251     .          .
 252     .          .
 253     .╌╌╌╌╌╌╌╌╌╌.
 254     .          .
 255   ]
 256 ]
 257 
 258 scenario editor-can-undo-typing-multiple-2 [
 259   local-scope
 260   
 261   assume-screen 10/width, 5/height
 262   e:&:editor <- new-editor [a], 0/left, 10/right
 263   editor-render screen, e
 264   
 265   assume-console [
 266     type [012]
 267   ]
 268   editor-event-loop screen, console, e
 269   screen-should-contain [
 270     .          .
 271     .012a      .
 272     .╌╌╌╌╌╌╌╌╌╌.
 273     .          .
 274   ]
 275   
 276   assume-console [
 277     press ctrl-z
 278   ]
 279   run [
 280     editor-event-loop screen, console, e
 281   ]
 282   
 283   screen-should-contain [
 284     .          .
 285     .a         .
 286     .╌╌╌╌╌╌╌╌╌╌.
 287     .          .
 288   ]
 289   
 290   assume-console [
 291     type [3]
 292   ]
 293   run [
 294     editor-event-loop screen, console, e
 295   ]
 296   screen-should-contain [
 297     .          .
 298     .3a        .
 299     .╌╌╌╌╌╌╌╌╌╌.
 300     .          .
 301   ]
 302 ]
 303 
 304 scenario editor-can-undo-typing-enter [
 305   local-scope
 306   
 307   assume-screen 10/width, 5/height
 308   e:&:editor <- new-editor [  abc], 0/left, 10/right
 309   editor-render screen, e
 310   
 311   assume-console [
 312     left-click 1, 8
 313     press enter
 314   ]
 315   editor-event-loop screen, console, e
 316   screen-should-contain [
 317     .          .
 318     .  abc     .
 319     .          .
 320     .╌╌╌╌╌╌╌╌╌╌.
 321     .          .
 322   ]
 323   
 324   3:num/raw <- get *e, cursor-row:offset
 325   4:num/raw <- get *e, cursor-column:offset
 326   memory-should-contain [
 327     3 <- 2
 328     4 <- 2
 329   ]
 330   
 331   assume-console [
 332     press ctrl-z
 333   ]
 334   run [
 335     editor-event-loop screen, console, e
 336   ]
 337   3:num/raw <- get *e, cursor-row:offset
 338   4:num/raw <- get *e, cursor-column:offset
 339   memory-should-contain [
 340     3 <- 1
 341     4 <- 5
 342   ]
 343   
 344   screen-should-contain [
 345     .          .
 346     .  abc     .
 347     .╌╌╌╌╌╌╌╌╌╌.
 348     .          .
 349   ]
 350   
 351   assume-console [
 352     type [1]
 353   ]
 354   run [
 355     editor-event-loop screen, console, e
 356   ]
 357   screen-should-contain [
 358     .          .
 359     .  abc1    .
 360     .╌╌╌╌╌╌╌╌╌╌.
 361     .          .
 362   ]
 363 ]
 364 
 365 
 366 
 367 scenario editor-redo-typing [
 368   local-scope
 369   
 370   assume-screen 10/width, 5/height
 371   e:&:editor <- new-editor [a], 0/left, 10/right
 372   editor-render screen, e
 373   assume-console [
 374     type [012]
 375     press ctrl-z
 376   ]
 377   editor-event-loop screen, console, e
 378   screen-should-contain [
 379     .          .
 380     .a         .
 381     .╌╌╌╌╌╌╌╌╌╌.
 382     .          .
 383   ]
 384   
 385   assume-console [
 386     press ctrl-y
 387   ]
 388   run [
 389     editor-event-loop screen, console, e
 390   ]
 391   
 392   screen-should-contain [
 393     .          .
 394     .012a      .
 395     .╌╌╌╌╌╌╌╌╌╌.
 396     .          .
 397   ]
 398   
 399   assume-console [
 400     type [3]
 401   ]
 402   run [
 403     editor-event-loop screen, console, e
 404   ]
 405   screen-should-contain [
 406     .          .
 407     .0123a     .
 408     .╌╌╌╌╌╌╌╌╌╌.
 409     .          .
 410   ]
 411 ]
 412 
 413 after <handle-redo> [
 414   {
 415     typing:insert-operation, is-insert?:bool <- maybe-convert *op, typing:variant
 416     break-unless is-insert?
 417     before-cursor <- get *editor, before-cursor:offset
 418     insert-from:&:duplex-list:char <- get typing, insert-from:offset  
 419     
 420     splice before-cursor, insert-from
 421     
 422     cursor-row <- get typing, after-row:offset
 423     *editor <- put *editor, cursor-row:offset, cursor-row
 424     cursor-column <- get typing, after-column:offset
 425     *editor <- put *editor, cursor-column:offset, cursor-column
 426     top:&:duplex-list:char <- get typing, after-top-of-screen:offset
 427     *editor <- put *editor, top-of-screen:offset, top
 428   }
 429 ]
 430 
 431 scenario editor-redo-typing-empty [
 432   local-scope
 433   
 434   assume-screen 10/width, 5/height
 435   e:&:editor <- new-editor [], 0/left, 10/right
 436   editor-render screen, e
 437   assume-console [
 438     type [012]
 439     press ctrl-z
 440   ]
 441   editor-event-loop screen, console, e
 442   screen-should-contain [
 443     .          .
 444     .          .
 445     .╌╌╌╌╌╌╌╌╌╌.
 446     .          .
 447   ]
 448   
 449   assume-console [
 450     press ctrl-y
 451   ]
 452   run [
 453     editor-event-loop screen, console, e
 454   ]
 455   
 456   screen-should-contain [
 457     .          .
 458     .012       .
 459     .╌╌╌╌╌╌╌╌╌╌.
 460     .          .
 461   ]
 462   
 463   assume-console [
 464     type [3]
 465   ]
 466   run [
 467     editor-event-loop screen, console, e
 468   ]
 469   screen-should-contain [
 470     .          .
 471     .0123      .
 472     .╌╌╌╌╌╌╌╌╌╌.
 473     .          .
 474   ]
 475 ]
 476 
 477 scenario editor-work-clears-redo-stack [
 478   local-scope
 479   
 480   assume-screen 10/width, 5/height
 481   contents:text <- new [abc
 482 def
 483 ghi]
 484   e:&:editor <- new-editor contents, 0/left, 10/right
 485   editor-render screen, e
 486   assume-console [
 487     type [1]
 488     press ctrl-z
 489   ]
 490   editor-event-loop screen, console, e
 491   
 492   assume-console [
 493     type [0]
 494   ]
 495   editor-event-loop screen, console, e
 496   screen-should-contain [
 497     .          .
 498     .0abc      .
 499     .def       .
 500     .ghi       .
 501     .╌╌╌╌╌╌╌╌╌╌.
 502   ]
 503   
 504   assume-console [
 505     press ctrl-y
 506   ]
 507   run [
 508     editor-event-loop screen, console, e
 509   ]
 510   
 511   screen-should-contain [
 512     .          .
 513     .0abc      .
 514     .def       .
 515     .ghi       .
 516     .╌╌╌╌╌╌╌╌╌╌.
 517   ]
 518 ]
 519 
 520 scenario editor-can-redo-typing-and-enter-and-tab [
 521   local-scope
 522   
 523   assume-screen 10/width, 5/height
 524   e:&:editor <- new-editor [], 0/left, 10/right
 525   editor-render screen, e
 526   
 527   assume-console [
 528     press tab
 529     type [ab]
 530     press tab
 531     type [cd]
 532     press enter
 533     press tab
 534     type [efg]
 535   ]
 536   editor-event-loop screen, console, e
 537   screen-should-contain [
 538     .          .
 539     .  ab  cd  .
 540     .    efg   .
 541     .╌╌╌╌╌╌╌╌╌╌.
 542     .          .
 543   ]
 544   3:num/raw <- get *e, cursor-row:offset
 545   4:num/raw <- get *e, cursor-column:offset
 546   memory-should-contain [
 547     3 <- 2
 548     4 <- 7
 549   ]
 550   
 551   assume-console [
 552     press ctrl-z
 553   ]
 554   run [
 555     editor-event-loop screen, console, e
 556   ]
 557   
 558   3:num/raw <- get *e, cursor-row:offset
 559   4:num/raw <- get *e, cursor-column:offset
 560   memory-should-contain [
 561     3 <- 2
 562     4 <- 2
 563   ]
 564   screen-should-contain [
 565     .          .
 566     .  ab  cd  .
 567     .          .
 568     .╌╌╌╌╌╌╌╌╌╌.
 569     .          .
 570   ]
 571   
 572   assume-console [
 573     press ctrl-z
 574   ]
 575   run [
 576     editor-event-loop screen, console, e
 577   ]
 578   
 579   3:num/raw <- get *e, cursor-row:offset
 580   4:num/raw <- get *e, cursor-column:offset
 581   memory-should-contain [
 582     3 <- 1
 583     4 <- 8
 584   ]
 585   screen-should-contain [
 586     .          .
 587     .  ab  cd  .
 588     .╌╌╌╌╌╌╌╌╌╌.
 589     .          .
 590   ]
 591   
 592   assume-console [
 593     press ctrl-z
 594   ]
 595   run [
 596     editor-event-loop screen, console, e
 597   ]
 598   
 599   3:num/raw <- get *e, cursor-row:offset
 600   4:num/raw <- get *e, cursor-column:offset
 601   memory-should-contain [
 602     3 <- 1
 603     4 <- 0
 604   ]
 605   screen-should-contain [
 606     .          .
 607     .          .
 608     .╌╌╌╌╌╌╌╌╌╌.
 609     .          .
 610   ]
 611   
 612   assume-console [
 613     press ctrl-y
 614   ]
 615   run [
 616     editor-event-loop screen, console, e
 617   ]
 618   
 619   3:num/raw <- get *e, cursor-row:offset
 620   4:num/raw <- get *e, cursor-column:offset
 621   memory-should-contain [
 622     3 <- 1
 623     4 <- 8
 624   ]
 625   screen-should-contain [
 626     .          .
 627     .  ab  cd  .
 628     .╌╌╌╌╌╌╌╌╌╌.
 629     .          .
 630   ]
 631   
 632   assume-console [
 633     press ctrl-y
 634   ]
 635   run [
 636     editor-event-loop screen, console, e
 637   ]
 638   
 639   3:num/raw <- get *e, cursor-row:offset
 640   4:num/raw <- get *e, cursor-column:offset
 641   memory-should-contain [
 642     3 <- 2
 643     4 <- 2
 644   ]
 645   screen-should-contain [
 646     .          .
 647     .  ab  cd  .
 648     .          .
 649     .╌╌╌╌╌╌╌╌╌╌.
 650     .          .
 651   ]
 652   
 653   assume-console [
 654     press ctrl-y
 655   ]
 656   run [
 657     editor-event-loop screen, console, e
 658   ]
 659   
 660   3:num/raw <- get *e, cursor-row:offset
 661   4:num/raw <- get *e, cursor-column:offset
 662   memory-should-contain [
 663     3 <- 2
 664     4 <- 7
 665   ]
 666   screen-should-contain [
 667     .          .
 668     .  ab  cd  .
 669     .    efg   .
 670     .╌╌╌╌╌╌╌╌╌╌.
 671     .          .
 672   ]
 673 ]
 674 
 675 
 676 
 677 scenario editor-can-undo-touch [
 678   local-scope
 679   
 680   assume-screen 10/width, 5/height
 681   contents:text <- new [abc
 682 def
 683 ghi]
 684   e:&:editor <- new-editor contents, 0/left, 10/right
 685   editor-render screen, e
 686   
 687   assume-console [
 688     left-click 3, 1
 689   ]
 690   editor-event-loop screen, console, e
 691   
 692   assume-console [
 693     press ctrl-z
 694   ]
 695   run [
 696     editor-event-loop screen, console, e
 697   ]
 698   
 699   3:num/raw <- get *e, cursor-row:offset
 700   4:num/raw <- get *e, cursor-column:offset
 701   memory-should-contain [
 702     3 <- 1
 703     4 <- 0
 704   ]
 705   
 706   assume-console [
 707     type [1]
 708   ]
 709   run [
 710     editor-event-loop screen, console, e
 711   ]
 712   screen-should-contain [
 713     .          .
 714     .1abc      .
 715     .def       .
 716     .ghi       .
 717     .╌╌╌╌╌╌╌╌╌╌.
 718   ]
 719 ]
 720 
 721 after <begin-move-cursor> [
 722   cursor-row-before:num <- get *editor, cursor-row:offset
 723   cursor-column-before:num <- get *editor, cursor-column:offset
 724   top-before:&:duplex-list:char <- get *editor, top-of-screen:offset
 725 ]
 726 before <end-move-cursor> [
 727   top-after:&:duplex-list:char <- get *editor, top-of-screen:offset
 728   cursor-row:num <- get *editor, cursor-row:offset
 729   cursor-column:num <- get *editor, cursor-column:offset
 730   {
 731     break-unless undo-coalesce-tag
 732     
 733     
 734     undo:&:list:&:operation <- get *editor, undo:offset
 735     break-unless undo
 736     op:&:operation <- first undo
 737     move:move-operation, is-move?:bool <- maybe-convert *op, move:variant
 738     break-unless is-move?
 739     previous-coalesce-tag:num <- get move, tag:offset
 740     coalesce?:bool <- equal undo-coalesce-tag, previous-coalesce-tag
 741     break-unless coalesce?
 742     move <- put move, after-row:offset, cursor-row
 743     move <- put move, after-column:offset, cursor-column
 744     move <- put move, after-top-of-screen:offset, top-after
 745     *op <- merge 1/move-operation, move
 746     break +done-adding-move-operation
 747   }
 748   op:&:operation <- new operation:type
 749   *op <- merge 1/move-operation, cursor-row-before, cursor-column-before, top-before, cursor-row/after, cursor-column/after, top-after, undo-coalesce-tag
 750   editor <- add-operation editor, op
 751   +done-adding-move-operation
 752 ]
 753 
 754 after <handle-undo> [
 755   {
 756     move:move-operation, is-move?:bool <- maybe-convert *op, move:variant
 757     break-unless is-move?
 758     
 759     cursor-row <- get move, before-row:offset
 760     *editor <- put *editor, cursor-row:offset, cursor-row
 761     cursor-column <- get move, before-column:offset
 762     *editor <- put *editor, cursor-column:offset, cursor-column
 763     top:&:duplex-list:char <- get move, before-top-of-screen:offset
 764     *editor <- put *editor, top-of-screen:offset, top
 765   }
 766 ]
 767 
 768 scenario editor-can-undo-scroll [
 769   local-scope
 770   
 771   assume-screen 5/width, 4/height
 772   
 773   contents:text <- new [a
 774 b
 775 cdefgh]
 776   e:&:editor <- new-editor contents, 0/left, 5/right
 777   
 778   assume-console [
 779     left-click 3, 3
 780     press right-arrow
 781   ]
 782   editor-event-loop screen, console, e
 783   3:num/raw <- get *e, cursor-row:offset
 784   4:num/raw <- get *e, cursor-column:offset
 785   
 786   screen-should-contain [
 787     .     .
 788     .b    .
 789     .cdef↩.
 790     .gh   .
 791   ]
 792   memory-should-contain [
 793     3 <- 3
 794     4 <- 0
 795   ]
 796   
 797   assume-console [
 798     press ctrl-z
 799   ]
 800   run [
 801     editor-event-loop screen, console, e
 802   ]
 803   
 804   3:num/raw <- get *e, cursor-row:offset
 805   4:num/raw <- get *e, cursor-column:offset
 806   memory-should-contain [
 807     3 <- 3
 808     4 <- 3
 809   ]
 810   
 811   screen-should-contain [
 812     .     .
 813     .a    .
 814     .b    .
 815     .cdef↩.
 816   ]
 817   
 818   assume-console [
 819     type [1]
 820   ]
 821   run [
 822     editor-event-loop screen, console, e
 823   ]
 824   screen-should-contain [
 825     .     .
 826     .b    .
 827     .cde1↩.
 828     .fgh  .
 829   ]
 830 ]
 831 
 832 scenario editor-can-undo-left-arrow [
 833   local-scope
 834   
 835   assume-screen 10/width, 5/height
 836   contents:text <- new [abc
 837 def
 838 ghi]
 839   e:&:editor <- new-editor contents, 0/left, 10/right
 840   editor-render screen, e
 841   
 842   assume-console [
 843     left-click 3, 1
 844     press left-arrow
 845   ]
 846   editor-event-loop screen, console, e
 847   
 848   assume-console [
 849     press ctrl-z
 850   ]
 851   run [
 852     editor-event-loop screen, console, e
 853   ]
 854   
 855   3:num/raw <- get *e, cursor-row:offset
 856   4:num/raw <- get *e, cursor-column:offset
 857   memory-should-contain [
 858     3 <- 3
 859     4 <- 1
 860   ]
 861   
 862   assume-console [
 863     type [1]
 864   ]
 865   run [
 866     editor-event-loop screen, console, e
 867   ]
 868   screen-should-contain [
 869     .          .
 870     .abc       .
 871     .def       .
 872     .g1hi      .
 873     .╌╌╌╌╌╌╌╌╌╌.
 874   ]
 875 ]
 876 
 877 scenario editor-can-undo-up-arrow [
 878   local-scope
 879   
 880   assume-screen 10/width, 5/height
 881   contents:text <- new [abc
 882 def
 883 ghi]
 884   e:&:editor <- new-editor contents, 0/left, 10/right
 885   editor-render screen, e
 886   
 887   assume-console [
 888     left-click 3, 1
 889     press up-arrow
 890   ]
 891   editor-event-loop screen, console, e
 892   3:num/raw <- get *e, cursor-row:offset
 893   4:num/raw <- get *e, cursor-column:offset
 894   memory-should-contain [
 895     3 <- 2
 896     4 <- 1
 897   ]
 898   
 899   assume-console [
 900     press ctrl-z
 901   ]
 902   run [
 903     editor-event-loop screen, console, e
 904   ]
 905   
 906   3:num/raw <- get *e, cursor-row:offset
 907   4:num/raw <- get *e, cursor-column:offset
 908   memory-should-contain [
 909     3 <- 3
 910     4 <- 1
 911   ]
 912   
 913   assume-console [
 914     type [1]
 915   ]
 916   run [
 917     editor-event-loop screen, console, e
 918   ]
 919   screen-should-contain [
 920     .          .
 921     .abc       .
 922     .def       .
 923     .g1hi      .
 924     .╌╌╌╌╌╌╌╌╌╌.
 925   ]
 926 ]
 927 
 928 scenario editor-can-undo-down-arrow [
 929   local-scope
 930   
 931   assume-screen 10/width, 5/height
 932   contents:text <- new [abc
 933 def
 934 ghi]
 935   e:&:editor <- new-editor contents, 0/left, 10/right
 936   editor-render screen, e
 937   
 938   assume-console [
 939     left-click 2, 1
 940     press down-arrow
 941   ]
 942   editor-event-loop screen, console, e
 943   
 944   assume-console [
 945     press ctrl-z
 946   ]
 947   run [
 948     editor-event-loop screen, console, e
 949   ]
 950   
 951   3:num/raw <- get *e, cursor-row:offset
 952   4:num/raw <- get *e, cursor-column:offset
 953   memory-should-contain [
 954     3 <- 2
 955     4 <- 1
 956   ]
 957   
 958   assume-console [
 959     type [1]
 960   ]
 961   run [
 962     editor-event-loop screen, console, e
 963   ]
 964   screen-should-contain [
 965     .          .
 966     .abc       .
 967     .d1ef      .
 968     .ghi       .
 969     .╌╌╌╌╌╌╌╌╌╌.
 970   ]
 971 ]
 972 
 973 scenario editor-can-undo-ctrl-f [
 974   local-scope
 975   
 976   assume-screen 10/width, 5/height
 977   contents:text <- new [a
 978 b
 979 c
 980 d
 981 e
 982 f]
 983   e:&:editor <- new-editor contents, 0/left, 10/right
 984   editor-render screen, e
 985   
 986   assume-console [
 987     press ctrl-f
 988   ]
 989   editor-event-loop screen, console, e
 990   
 991   assume-console [
 992     press ctrl-z
 993   ]
 994   run [
 995     editor-event-loop screen, console, e
 996   ]
 997   
 998   screen-should-contain [
 999     .          .
1000     .a         .
1001     .b         .
1002     .c         .
1003     .d         .
1004   ]
1005 ]
1006 
1007 scenario editor-can-undo-page-down [
1008   local-scope
1009   
1010   assume-screen 10/width, 5/height
1011   contents:text <- new [a
1012 b
1013 c
1014 d
1015 e
1016 f]
1017   e:&:editor <- new-editor contents, 0/left, 10/right
1018   editor-render screen, e
1019   
1020   assume-console [
1021     press page-down
1022   ]
1023   editor-event-loop screen, console, e
1024   
1025   assume-console [
1026     press ctrl-z
1027   ]
1028   run [
1029     editor-event-loop screen, console, e
1030   ]
1031   
1032   screen-should-contain [
1033     .          .
1034     .a         .
1035     .b         .
1036     .c         .
1037     .d         .
1038   ]
1039 ]
1040 
1041 scenario editor-can-undo-ctrl-b [
1042   local-scope
1043   
1044   assume-screen 10/width, 5/height
1045   contents:text <- new [a
1046 b
1047 c
1048 d
1049 e
1050 f]
1051   e:&:editor <- new-editor contents, 0/left, 10/right
1052   editor-render screen, e
1053   
1054   assume-console [
1055     press page-down
1056     press ctrl-b
1057   ]
1058   editor-event-loop screen, console, e
1059   
1060   assume-console [
1061     press ctrl-z
1062   ]
1063   run [
1064     editor-event-loop screen, console, e
1065   ]
1066   
1067   screen-should-contain [
1068     .          .
1069     .d         .
1070     .e         .
1071     .f         .
1072     .╌╌╌╌╌╌╌╌╌╌.
1073   ]
1074 ]
1075 
1076 scenario editor-can-undo-page-up [
1077   local-scope
1078   
1079   assume-screen 10/width, 5/height
1080   contents:text <- new [a
1081 b
1082 c
1083 d
1084 e
1085 f]
1086   e:&:editor <- new-editor contents, 0/left, 10/right
1087   editor-render screen, e
1088   
1089   assume-console [
1090     press page-down
1091     press page-up
1092   ]
1093   editor-event-loop screen, console, e
1094   
1095   assume-console [
1096     press ctrl-z
1097   ]
1098   run [
1099     editor-event-loop screen, console, e
1100   ]
1101   
1102   screen-should-contain [
1103     .          .
1104     .d         .
1105     .e         .
1106     .f         .
1107     .╌╌╌╌╌╌╌╌╌╌.
1108   ]
1109 ]
1110 
1111 scenario editor-can-undo-ctrl-a [
1112   local-scope
1113   
1114   assume-screen 10/width, 5/height
1115   contents:text <- new [abc
1116 def
1117 ghi]
1118   e:&:editor <- new-editor contents, 0/left, 10/right
1119   editor-render screen, e
1120   
1121   assume-console [
1122     left-click 2, 1
1123     press ctrl-a
1124   ]
1125   editor-event-loop screen, console, e
1126   
1127   assume-console [
1128     press ctrl-z
1129   ]
1130   run [
1131     editor-event-loop screen, console, e
1132   ]
1133   
1134   3:num/raw <- get *e, cursor-row:offset
1135   4:num/raw <- get *e, cursor-column:offset
1136   memory-should-contain [
1137     3 <- 2
1138     4 <- 1
1139   ]
1140   
1141   assume-console [
1142     type [1]
1143   ]
1144   run [
1145     editor-event-loop screen, console, e
1146   ]
1147   screen-should-contain [
1148     .          .
1149     .abc       .
1150     .d1ef      .
1151     .ghi       .
1152     .╌╌╌╌╌╌╌╌╌╌.
1153   ]
1154 ]
1155 
1156 scenario editor-can-undo-home [
1157   local-scope
1158   
1159   assume-screen 10/width, 5/height
1160   contents:text <- new [abc
1161 def
1162 ghi]
1163   e:&:editor <- new-editor contents, 0/left, 10/right
1164   editor-render screen, e
1165   
1166   assume-console [
1167     left-click 2, 1
1168     press home
1169   ]
1170   editor-event-loop screen, console, e
1171   
1172   assume-console [
1173     press ctrl-z
1174   ]
1175   run [
1176     editor-event-loop screen, console, e
1177   ]
1178   
1179   3:num/raw <- get *e, cursor-row:offset
1180   4:num/raw <- get *e, cursor-column:offset
1181   memory-should-contain [
1182     3 <- 2
1183     4 <- 1
1184   ]
1185   
1186   assume-console [
1187     type [1]
1188   ]
1189   run [
1190     editor-event-loop screen, console, e
1191   ]
1192   screen-should-contain [
1193     .          .
1194     .abc       .
1195     .d1ef      .
1196     .ghi       .
1197     .╌╌╌╌╌╌╌╌╌╌.
1198   ]
1199 ]
1200 
1201 scenario editor-can-undo-ctrl-e [
1202   local-scope
1203   
1204   assume-screen 10/width, 5/height
1205   contents:text <- new [abc
1206 def
1207 ghi]
1208   e:&:editor <- new-editor contents, 0/left, 10/right
1209   editor-render screen, e
1210   
1211   assume-console [
1212     left-click 2, 1
1213     press ctrl-e
1214   ]
1215   editor-event-loop screen, console, e
1216   
1217   assume-console [
1218     press ctrl-z
1219   ]
1220   run [
1221     editor-event-loop screen, console, e
1222   ]
1223   
1224   3:num/raw <- get *e, cursor-row:offset
1225   4:num/raw <- get *e, cursor-column:offset
1226   memory-should-contain [
1227     3 <- 2
1228     4 <- 1
1229   ]
1230   
1231   assume-console [
1232     type [1]
1233   ]
1234   run [
1235     editor-event-loop screen, console, e
1236   ]
1237   screen-should-contain [
1238     .          .
1239     .abc       .
1240     .d1ef      .
1241     .ghi       .
1242     .╌╌╌╌╌╌╌╌╌╌.
1243   ]
1244 ]
1245 
1246 scenario editor-can-undo-end [
1247   local-scope
1248   
1249   assume-screen 10/width, 5/height
1250   contents:text <- new [abc
1251 def
1252 ghi]
1253   e:&:editor <- new-editor contents, 0/left, 10/right
1254   editor-render screen, e
1255   
1256   assume-console [
1257     left-click 2, 1
1258     press end
1259   ]
1260   editor-event-loop screen, console, e
1261   
1262   assume-console [
1263     press ctrl-z
1264   ]
1265   run [
1266     editor-event-loop screen, console, e
1267   ]
1268   
1269   3:num/raw <- get *e, cursor-row:offset
1270   4:num/raw <- get *e, cursor-column:offset
1271   memory-should-contain [
1272     3 <- 2
1273     4 <- 1
1274   ]
1275   
1276   assume-console [
1277     type [1]
1278   ]
1279   run [
1280     editor-event-loop screen, console, e
1281   ]
1282   screen-should-contain [
1283     .          .
1284     .abc       .
1285     .d1ef      .
1286     .ghi       .
1287     .╌╌╌╌╌╌╌╌╌╌.
1288   ]
1289 ]
1290 
1291 scenario editor-can-undo-multiple-arrows-in-the-same-direction [
1292   local-scope
1293   
1294   assume-screen 10/width, 5/height
1295   contents:text <- new [abc
1296 def
1297 ghi]
1298   e:&:editor <- new-editor contents, 0/left, 10/right
1299   editor-render screen, e
1300   
1301   assume-console [
1302     left-click 2, 1
1303     press right-arrow
1304     press right-arrow
1305     press up-arrow
1306   ]
1307   editor-event-loop screen, console, e
1308   3:num/raw <- get *e, cursor-row:offset
1309   4:num/raw <- get *e, cursor-column:offset
1310   memory-should-contain [
1311     3 <- 1
1312     4 <- 3
1313   ]
1314   
1315   assume-console [
1316     press ctrl-z
1317   ]
1318   run [
1319     editor-event-loop screen, console, e
1320   ]
1321   
1322   3:num/raw <- get *e, cursor-row:offset
1323   4:num/raw <- get *e, cursor-column:offset
1324   memory-should-contain [
1325     3 <- 2
1326     4 <- 3
1327   ]
1328   
1329   assume-console [
1330     press ctrl-z
1331   ]
1332   run [
1333     editor-event-loop screen, console, e
1334   ]
1335   
1336   3:num/raw <- get *e, cursor-row:offset
1337   4:num/raw <- get *e, cursor-column:offset
1338   memory-should-contain [
1339     3 <- 2
1340     4 <- 1
1341   ]
1342 ]
1343 
1344 
1345 
1346 scenario editor-redo-touch [
1347   local-scope
1348   
1349   assume-screen 10/width, 5/height
1350   contents:text <- new [abc
1351 def
1352 ghi]
1353   e:&:editor <- new-editor contents, 0/left, 10/right
1354   editor-render screen, e
1355   assume-console [
1356     left-click 3, 1
1357     press ctrl-z
1358   ]
1359   editor-event-loop screen, console, e
1360   
1361   assume-console [
1362     press ctrl-y
1363   ]
1364   run [
1365     editor-event-loop screen, console, e
1366   ]
1367   
1368   3:num/raw <- get *e, cursor-row:offset
1369   4:num/raw <- get *e, cursor-column:offset
1370   memory-should-contain [
1371     3 <- 3
1372     4 <- 1
1373   ]
1374   
1375   assume-console [
1376     type [1]
1377   ]
1378   run [
1379     editor-event-loop screen, console, e
1380   ]
1381   screen-should-contain [
1382     .          .
1383     .abc       .
1384     .def       .
1385     .g1hi      .
1386     .╌╌╌╌╌╌╌╌╌╌.
1387   ]
1388 ]
1389 
1390 after <handle-redo> [
1391   {
1392     move:move-operation, is-move?:bool <- maybe-convert *op, move:variant
1393     break-unless is-move?
1394     
1395     cursor-row <- get move, after-row:offset
1396     *editor <- put *editor, cursor-row:offset, cursor-row
1397     cursor-column <- get move, after-column:offset
1398     *editor <- put *editor, cursor-column:offset, cursor-column
1399     top:&:duplex-list:char <- get move, after-top-of-screen:offset
1400     *editor <- put *editor, top-of-screen:offset, top
1401   }
1402 ]
1403 
1404 scenario editor-separates-undo-insert-from-undo-cursor-move [
1405   local-scope
1406   
1407   assume-screen 10/width, 5/height
1408   e:&:editor <- new-editor [], 0/left, 10/right
1409   editor-render screen, e
1410   assume-console [
1411     type [abc]
1412     left-click 1, 1
1413     type [d]
1414   ]
1415   editor-event-loop screen, console, e
1416   3:num/raw <- get *e, cursor-row:offset
1417   4:num/raw <- get *e, cursor-column:offset
1418   screen-should-contain [
1419     .          .
1420     .adbc      .
1421     .╌╌╌╌╌╌╌╌╌╌.
1422     .          .
1423   ]
1424   memory-should-contain [
1425     3 <- 1
1426     4 <- 2
1427   ]
1428   
1429   assume-console [
1430     press ctrl-z
1431   ]
1432   run [
1433     editor-event-loop screen, console, e
1434     3:num/raw <- get *e, cursor-row:offset
1435     4:num/raw <- get *e, cursor-column:offset
1436   ]
1437   
1438   screen-should-contain [
1439     .          .
1440     .abc       .
1441     .╌╌╌╌╌╌╌╌╌╌.
1442     .          .
1443   ]
1444   memory-should-contain [
1445     3 <- 1
1446     4 <- 1
1447   ]
1448   
1449   assume-console [
1450     press ctrl-z
1451   ]
1452   run [
1453     editor-event-loop screen, console, e
1454     3:num/raw <- get *e, cursor-row:offset
1455     4:num/raw <- get *e, cursor-column:offset
1456   ]
1457   
1458   screen-should-contain [
1459     .          .
1460     .abc       .
1461     .╌╌╌╌╌╌╌╌╌╌.
1462     .          .
1463   ]
1464   memory-should-contain [
1465     3 <- 1
1466     4 <- 3
1467   ]
1468   
1469   assume-console [
1470     press ctrl-z
1471   ]
1472   run [
1473     editor-event-loop screen, console, e
1474     3:num/raw <- get *e, cursor-row:offset
1475     4:num/raw <- get *e, cursor-column:offset
1476   ]
1477   
1478   screen-should-contain [
1479     .          .
1480     .          .
1481     .╌╌╌╌╌╌╌╌╌╌.
1482     .          .
1483   ]
1484   memory-should-contain [
1485     3 <- 1
1486     4 <- 0
1487   ]
1488   
1489   assume-console [
1490     press ctrl-y
1491   ]
1492   run [
1493     editor-event-loop screen, console, e
1494     3:num/raw <- get *e, cursor-row:offset
1495     4:num/raw <- get *e, cursor-column:offset
1496   ]
1497   
1498   screen-should-contain [
1499     .          .
1500     .abc       .
1501     .╌╌╌╌╌╌╌╌╌╌.
1502     .          .
1503   ]
1504   memory-should-contain [
1505     3 <- 1
1506     4 <- 3
1507   ]
1508   
1509   assume-console [
1510     press ctrl-y
1511   ]
1512   run [
1513     editor-event-loop screen, console, e
1514     3:num/raw <- get *e, cursor-row:offset
1515     4:num/raw <- get *e, cursor-column:offset
1516   ]
1517   
1518   screen-should-contain [
1519     .          .
1520     .abc       .
1521     .╌╌╌╌╌╌╌╌╌╌.
1522     .          .
1523   ]
1524   
1525   memory-should-contain [
1526     3 <- 1
1527     4 <- 1
1528   ]
1529   
1530   assume-console [
1531     press ctrl-y
1532   ]
1533   run [
1534     editor-event-loop screen, console, e
1535     3:num/raw <- get *e, cursor-row:offset
1536     4:num/raw <- get *e, cursor-column:offset
1537   ]
1538   
1539   screen-should-contain [
1540     .          .
1541     .adbc      .
1542     .╌╌╌╌╌╌╌╌╌╌.
1543     .          .
1544   ]
1545   memory-should-contain [
1546     3 <- 1
1547     4 <- 2
1548   ]
1549 ]
1550 
1551 
1552 
1553 scenario editor-can-undo-and-redo-backspace [
1554   local-scope
1555   
1556   assume-screen 10/width, 5/height
1557   e:&:editor <- new-editor [], 0/left, 10/right
1558   editor-render screen, e
1559   
1560   assume-console [
1561     type [abc]
1562     press backspace
1563     press backspace
1564   ]
1565   editor-event-loop screen, console, e
1566   screen-should-contain [
1567     .          .
1568     .a         .
1569     .╌╌╌╌╌╌╌╌╌╌.
1570     .          .
1571   ]
1572   3:num/raw <- get *e, cursor-row:offset
1573   4:num/raw <- get *e, cursor-column:offset
1574   memory-should-contain [
1575     3 <- 1
1576     4 <- 1
1577   ]
1578   
1579   assume-console [
1580     press ctrl-z
1581   ]
1582   run [
1583     editor-event-loop screen, console, e
1584   ]
1585   3:num/raw <- get *e, cursor-row:offset
1586   4:num/raw <- get *e, cursor-column:offset
1587   memory-should-contain [
1588     3 <- 1
1589     4 <- 3
1590   ]
1591   screen-should-contain [
1592     .          .
1593     .abc       .
1594     .╌╌╌╌╌╌╌╌╌╌.
1595     .          .
1596   ]
1597   
1598   assume-console [
1599     press ctrl-y
1600   ]
1601   run [
1602     editor-event-loop screen, console, e
1603   ]
1604   3:num/raw <- get *e, cursor-row:offset
1605   4:num/raw <- get *e, cursor-column:offset
1606   memory-should-contain [
1607     3 <- 1
1608     4 <- 1
1609   ]
1610   screen-should-contain [
1611     .          .
1612     .a         .
1613     .╌╌╌╌╌╌╌╌╌╌.
1614     .          .
1615   ]
1616 ]
1617 
1618 
1619 after <begin-backspace-character> [
1620   top-before:&:duplex-list:char <- get *editor, top-of-screen:offset
1621 ]
1622 before <end-backspace-character> [
1623   {
1624     break-unless backspaced-cell  
1625     top-after:&:duplex-list:char <- get *editor, top-of-screen:offset
1626     cursor-row:num <- get *editor, cursor-row:offset
1627     cursor-column:num <- get *editor, cursor-row:offset
1628     before-cursor:&:duplex-list:char <- get *editor, before-cursor:offset
1629     undo:&:list:&:operation <- get *editor, undo:offset
1630     {
1631       
1632       break-unless undo
1633       op:&:operation <- first undo
1634       deletion:delete-operation, is-delete?:bool <- maybe-convert *op, delete:variant
1635       break-unless is-delete?
1636       previous-coalesce-tag:num <- get deletion, tag:offset
1637       coalesce?:bool <- equal previous-coalesce-tag, 1/coalesce-backspace
1638       break-unless coalesce?
1639       deletion <- put deletion, delete-from:offset, before-cursor
1640       backspaced-so-far:&:duplex-list:char <- get deletion, deleted-text:offset
1641       splice backspaced-cell, backspaced-so-far
1642       deletion <- put deletion, deleted-text:offset, backspaced-cell
1643       deletion <- put deletion, after-row:offset, cursor-row
1644       deletion <- put deletion, after-column:offset, cursor-column
1645       deletion <- put deletion, after-top-of-screen:offset, top-after
1646       *op <- merge 2/delete-operation, deletion
1647       break +done-adding-backspace-operation
1648     }
1649     
1650     op:&:operation <- new operation:type
1651     deleted-until:&:duplex-list:char <- next before-cursor
1652     *op <- merge 2/delete-operation, save-row/before, save-column/before, top-before, cursor-row/after, cursor-column/after, top-after, backspaced-cell/deleted, before-cursor/delete-from, deleted-until, 1/coalesce-backspace
1653     editor <- add-operation editor, op
1654     +done-adding-backspace-operation
1655   }
1656 ]
1657 
1658 after <handle-undo> [
1659   {
1660     deletion:delete-operation, is-delete?:bool <- maybe-convert *op, delete:variant
1661     break-unless is-delete?
1662     anchor:&:duplex-list:char <- get deletion, delete-from:offset
1663     break-unless anchor
1664     deleted:&:duplex-list:char <- get deletion, deleted-text:offset
1665     old-cursor:&:duplex-list:char <- last deleted
1666     splice anchor, deleted
1667     
1668     before-cursor <- copy old-cursor
1669     cursor-row <- get deletion, before-row:offset
1670     *editor <- put *editor, cursor-row:offset, cursor-row
1671     cursor-column <- get deletion, before-column:offset
1672     *editor <- put *editor, cursor-column:offset, cursor-column
1673     top:&:duplex-list:char <- get deletion, before-top-of-screen:offset
1674     *editor <- put *editor, top-of-screen:offset, top
1675   }
1676 ]
1677 
1678 after <handle-redo> [
1679   {
1680     deletion:delete-operation, is-delete?:bool <- maybe-convert *op, delete:variant
1681     break-unless is-delete?
1682     start:&:duplex-list:char <- get deletion, delete-from:offset
1683     end:&:duplex-list:char <- get deletion, delete-until:offset
1684     data:&:duplex-list:char <- get *editor, data:offset
1685     remove-between start, end
1686     
1687     cursor-row <- get deletion, after-row:offset
1688     *editor <- put *editor, cursor-row:offset, cursor-row
1689     cursor-column <- get deletion, after-column:offset
1690     *editor <- put *editor, cursor-column:offset, cursor-column
1691     top:&:duplex-list:char <- get deletion, before-top-of-screen:offset
1692     *editor <- put *editor, top-of-screen:offset, top
1693   }
1694 ]
1695 
1696 
1697 
1698 scenario editor-can-undo-and-redo-delete [
1699   local-scope
1700   
1701   assume-screen 10/width, 5/height
1702   e:&:editor <- new-editor [], 0/left, 10/right
1703   editor-render screen, e
1704   
1705   assume-console [
1706     type [abcdef]
1707     left-click 1, 2
1708     press delete
1709     press backspace
1710     press delete
1711     press delete
1712   ]
1713   editor-event-loop screen, console, e
1714   screen-should-contain [
1715     .          .
1716     .af        .
1717     .╌╌╌╌╌╌╌╌╌╌.
1718     .          .
1719   ]
1720   3:num/raw <- get *e, cursor-row:offset
1721   4:num/raw <- get *e, cursor-column:offset
1722   memory-should-contain [
1723     3 <- 1
1724     4 <- 1
1725   ]
1726   
1727   assume-console [
1728     press ctrl-z
1729   ]
1730   run [
1731     editor-event-loop screen, console, e
1732   ]
1733   3:num/raw <- get *e, cursor-row:offset
1734   4:num/raw <- get *e, cursor-column:offset
1735   memory-should-contain [
1736     3 <- 1
1737     4 <- 1
1738   ]
1739   screen-should-contain [
1740     .          .
1741     .adef      .
1742     .╌╌╌╌╌╌╌╌╌╌.
1743     .          .
1744   ]
1745   
1746   assume-console [
1747     press ctrl-z
1748   ]
1749   run [
1750     editor-event-loop screen, console, e
1751   ]
1752   3:num/raw <- get *e, cursor-row:offset
1753   4:num/raw <- get *e, cursor-column:offset
1754   memory-should-contain [
1755     3 <- 1
1756     4 <- 2
1757   ]
1758   screen-should-contain [
1759     .          .
1760     .abdef     .
1761     .╌╌╌╌╌╌╌╌╌╌.
1762     .          .
1763   ]
1764   
1765   assume-console [
1766     press ctrl-z
1767   ]
1768   run [
1769     editor-event-loop screen, console, e
1770   ]
1771   3:num/raw <- get *e, cursor-row:offset
1772   4:num/raw <- get *e, cursor-column:offset
1773   memory-should-contain [
1774     3 <- 1
1775     4 <- 2
1776   ]
1777   screen-should-contain [
1778     .          .
1779     .abcdef    .
1780     .╌╌╌╌╌╌╌╌╌╌.
1781     .          .
1782   ]
1783   
1784   assume-console [
1785     press ctrl-y
1786   ]
1787   run [
1788     editor-event-loop screen, console, e
1789   ]
1790   
1791   3:num/raw <- get *e, cursor-row:offset
1792   4:num/raw <- get *e, cursor-column:offset
1793   memory-should-contain [
1794     3 <- 1
1795     4 <- 2
1796   ]
1797   screen-should-contain [
1798     .          .
1799     .abdef     .
1800     .╌╌╌╌╌╌╌╌╌╌.
1801     .          .
1802   ]
1803   
1804   assume-console [
1805     press ctrl-y
1806   ]
1807   run [
1808     editor-event-loop screen, console, e
1809   ]
1810   
1811   3:num/raw <- get *e, cursor-row:offset
1812   4:num/raw <- get *e, cursor-column:offset
1813   memory-should-contain [
1814     3 <- 1
1815     4 <- 1
1816   ]
1817   screen-should-contain [
1818     .          .
1819     .adef      .
1820     .╌╌╌╌╌╌╌╌╌╌.
1821     .          .
1822   ]
1823   
1824   assume-console [
1825     press ctrl-y
1826   ]
1827   run [
1828     editor-event-loop screen, console, e
1829   ]
1830   
1831   3:num/raw <- get *e, cursor-row:offset
1832   4:num/raw <- get *e, cursor-column:offset
1833   memory-should-contain [
1834     3 <- 1
1835     4 <- 1
1836   ]
1837   screen-should-contain [
1838     .          .
1839     .af        .
1840     .╌╌╌╌╌╌╌╌╌╌.
1841     .          .
1842   ]
1843 ]
1844 
1845 after <begin-delete-character> [
1846   top-before:&:duplex-list:char <- get *editor, top-of-screen:offset
1847 ]
1848 before <end-delete-character> [
1849   {
1850     break-unless deleted-cell  
1851     top-after:&:duplex-list:char <- get *editor, top-of-screen:offset
1852     cursor-row:num <- get *editor, cursor-row:offset
1853     cursor-column:num <- get *editor, cursor-column:offset
1854     before-cursor:&:duplex-list:char <- get *editor, before-cursor:offset
1855     undo:&:list:&:operation <- get *editor, undo:offset
1856     {
1857       
1858       break-unless undo
1859       op:&:operation <- first undo
1860       deletion:delete-operation, is-delete?:bool <- maybe-convert *op, delete:variant
1861       break-unless is-delete?
1862       previous-coalesce-tag:num <- get deletion, tag:offset
1863       coalesce?:bool <- equal previous-coalesce-tag, 2/coalesce-delete
1864       break-unless coalesce?
1865       delete-until:&:duplex-list:char <- next before-cursor
1866       deletion <- put deletion, delete-until:offset, delete-until
1867       deleted-so-far:&:duplex-list:char <- get deletion, deleted-text:offset
1868       deleted-so-far <- append deleted-so-far, deleted-cell
1869       deletion <- put deletion, deleted-text:offset, deleted-so-far
1870       deletion <- put deletion, after-row:offset, cursor-row
1871       deletion <- put deletion, after-column:offset, cursor-column
1872       deletion <- put deletion, after-top-of-screen:offset, top-after
1873       *op <- merge 2/delete-operation, deletion
1874       break +done-adding-delete-operation
1875     }
1876     
1877     op:&:operation <- new operation:type
1878     deleted-until:&:duplex-list:char <- next before-cursor
1879     *op <- merge 2/delete-operation, save-row/before, save-column/before, top-before, cursor-row/after, cursor-column/after, top-after, deleted-cell/deleted, before-cursor/delete-from, deleted-until, 2/coalesce-delete
1880     editor <- add-operation editor, op
1881     +done-adding-delete-operation
1882   }
1883 ]
1884 
1885 
1886 
1887 scenario editor-can-undo-and-redo-ctrl-k [
1888   local-scope
1889   
1890   assume-screen 10/width, 5/height
1891   contents:text <- new [abc
1892 def]
1893   e:&:editor <- new-editor contents, 0/left, 10/right
1894   editor-render screen, e
1895   
1896   assume-console [
1897     left-click 1, 1
1898     press ctrl-k
1899   ]
1900   editor-event-loop screen, console, e
1901   screen-should-contain [
1902     .          .
1903     .a         .
1904     .def       .
1905     .╌╌╌╌╌╌╌╌╌╌.
1906     .          .
1907   ]
1908   3:num/raw <- get *e, cursor-row:offset
1909   4:num/raw <- get *e, cursor-column:offset
1910   memory-should-contain [
1911     3 <- 1
1912     4 <- 1
1913   ]
1914   
1915   assume-console [
1916     press ctrl-z
1917   ]
1918   run [
1919     editor-event-loop screen, console, e
1920   ]
1921   screen-should-contain [
1922     .          .
1923     .abc       .
1924     .def       .
1925     .╌╌╌╌╌╌╌╌╌╌.
1926     .          .
1927   ]
1928   3:num/raw <- get *e, cursor-row:offset
1929   4:num/raw <- get *e, cursor-column:offset
1930   memory-should-contain [
1931     3 <- 1
1932     4 <- 1
1933   ]
1934   
1935   assume-console [
1936     press ctrl-y
1937   ]
1938   run [
1939     editor-event-loop screen, console, e
1940   ]
1941   
1942   screen-should-contain [
1943     .          .
1944     .a         .
1945     .def       .
1946     .╌╌╌╌╌╌╌╌╌╌.
1947     .          .
1948   ]
1949   3:num/raw <- get *e, cursor-row:offset
1950   4:num/raw <- get *e, cursor-column:offset
1951   memory-should-contain [
1952     3 <- 1
1953     4 <- 1
1954   ]
1955   
1956   assume-console [
1957     type [1]
1958   ]
1959   run [
1960     editor-event-loop screen, console, e
1961   ]
1962   screen-should-contain [
1963     .          .
1964     .a1        .
1965     .def       .
1966     .╌╌╌╌╌╌╌╌╌╌.
1967     .          .
1968   ]
1969 ]
1970 
1971 after <begin-delete-to-end-of-line> [
1972   top-before:&:duplex-list:char <- get *editor, top-of-screen:offset
1973 ]
1974 before <end-delete-to-end-of-line> [
1975   {
1976     break-unless deleted-cells  
1977     top-after:&:duplex-list:char <- get *editor, top-of-screen:offset
1978     cursor-row:num <- get *editor, cursor-row:offset
1979     cursor-column:num <- get *editor, cursor-column:offset
1980     deleted-until:&:duplex-list:char <- next before-cursor
1981     op:&:operation <- new operation:type
1982     *op <- merge 2/delete-operation, save-row/before, save-column/before, top-before, cursor-row/after, cursor-column/after, top-after, deleted-cells/deleted, before-cursor/delete-from, deleted-until, 0/never-coalesce
1983     editor <- add-operation editor, op
1984     +done-adding-delete-operation
1985   }
1986 ]
1987 
1988 
1989 
1990 scenario editor-can-undo-and-redo-ctrl-u [
1991   local-scope
1992   
1993   assume-screen 10/width, 5/height
1994   contents:text <- new [abc
1995 def]
1996   e:&:editor <- new-editor contents, 0/left, 10/right
1997   editor-render screen, e
1998   
1999   assume-console [
2000     left-click 1, 2
2001     press ctrl-u
2002   ]
2003   editor-event-loop screen, console, e
2004   screen-should-contain [
2005     .          .
2006     .c         .
2007     .def       .
2008     .╌╌╌╌╌╌╌╌╌╌.
2009     .          .
2010   ]
2011   3:num/raw <- get *e, cursor-row:offset
2012   4:num/raw <- get *e, cursor-column:offset
2013   memory-should-contain [
2014     3 <- 1
2015     4 <- 0
2016   ]
2017   
2018   assume-console [
2019     press ctrl-z
2020   ]
2021   run [
2022     editor-event-loop screen, console, e
2023   ]
2024   screen-should-contain [
2025     .          .
2026     .abc       .
2027     .def       .
2028     .╌╌╌╌╌╌╌╌╌╌.
2029     .          .
2030   ]
2031   3:num/raw <- get *e, cursor-row:offset
2032   4:num/raw <- get *e, cursor-column:offset
2033   memory-should-contain [
2034     3 <- 1
2035     4 <- 2
2036   ]
2037   
2038   assume-console [
2039     press ctrl-y
2040   ]
2041   run [
2042     editor-event-loop screen, console, e
2043   ]
2044   
2045   screen-should-contain [
2046     .          .
2047     .c         .
2048     .def       .
2049     .╌╌╌╌╌╌╌╌╌╌.
2050     .          .
2051   ]
2052   3:num/raw <- get *e, cursor-row:offset
2053   4:num/raw <- get *e, cursor-column:offset
2054   memory-should-contain [
2055     3 <- 1
2056     4 <- 0
2057   ]
2058   
2059   assume-console [
2060     type [1]
2061   ]
2062   run [
2063     editor-event-loop screen, console, e
2064   ]
2065   screen-should-contain [
2066     .          .
2067     .1c        .
2068     .def       .
2069     .╌╌╌╌╌╌╌╌╌╌.
2070     .          .
2071   ]
2072 ]
2073 
2074 after <begin-delete-to-start-of-line> [
2075   top-before:&:duplex-list:char <- get *editor, top-of-screen:offset
2076 ]
2077 before <end-delete-to-start-of-line> [
2078   {
2079     break-unless deleted-cells  
2080     top-after:&:duplex-list:char <- get *editor, top-of-screen:offset
2081     op:&:operation <- new operation:type
2082     before-cursor:&:duplex-list:char <- get *editor, before-cursor:offset
2083     deleted-until:&:duplex-list:char <- next before-cursor
2084     cursor-row:num <- get *editor, cursor-row:offset
2085     cursor-column:num <- get *editor, cursor-column:offset
2086     *op <- merge 2/delete-operation, save-row/before, save-column/before, top-before, cursor-row/after, cursor-column/after, top-after, deleted-cells/deleted, before-cursor/delete-from, deleted-until, 0/never-coalesce
2087     editor <- add-operation editor, op
2088     +done-adding-delete-operation
2089   }
2090 ]
2091 
2092 scenario editor-can-undo-and-redo-ctrl-u-2 [
2093   local-scope
2094   
2095   assume-screen 10/width, 5/height
2096   e:&:editor <- new-editor [], 0/left, 10/right
2097   editor-render screen, e
2098   
2099   assume-console [
2100     type [abc]
2101     press ctrl-u
2102     press ctrl-z
2103   ]
2104   editor-event-loop screen, console, e
2105   screen-should-contain [
2106     .          .
2107     .abc       .
2108     .╌╌╌╌╌╌╌╌╌╌.
2109     .          .
2110   ]
2111 ]