From 6e1eeeebfb453fa7c871869c19375ce60fbd7413 Mon Sep 17 00:00:00 2001 From: Kartik Agaram Date: Sat, 27 Jul 2019 16:01:55 -0700 Subject: 5485 - promote SubX to top-level --- html/edit/012-editor-undo.mu.html | 2176 ------------------------------------- 1 file changed, 2176 deletions(-) delete mode 100644 html/edit/012-editor-undo.mu.html (limited to 'html/edit/012-editor-undo.mu.html') diff --git a/html/edit/012-editor-undo.mu.html b/html/edit/012-editor-undo.mu.html deleted file mode 100644 index 3b663e19..00000000 --- a/html/edit/012-editor-undo.mu.html +++ /dev/null @@ -1,2176 +0,0 @@ - - - - -Mu - edit/012-editor-undo.mu - - - - - - - - - - -https://github.com/akkartik/mu/blob/master/edit/012-editor-undo.mu -
-   1 ## undo/redo
-   2 
-   3 # for every undoable event, create a type of *operation* that contains all the
-   4 # information needed to reverse it
-   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   # inserted text is from 'insert-from' until 'insert-until'; list doesn't have to terminate
-  19   insert-from:&:duplex-list:char
-  20   insert-until:&:duplex-list:char
-  21   tag:num  # event causing this operation; might be used to coalesce runs of similar events
-  22     # 0: no coalesce (enter+indent)
-  23     # 1: regular alphanumeric characters
-  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  # event causing this operation; might be used to coalesce runs of similar events
-  34     # 0: no coalesce (touch events, etc)
-  35     # 1: left arrow
-  36     # 2: right arrow
-  37     # 3: up arrow
-  38     # 4: down arrow
-  39     # 5: line up
-  40     # 6: line down
-  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  # event causing this operation; might be used to coalesce runs of similar events
-  54     # 0: no coalesce (ctrl-k, ctrl-u)
-  55     # 1: backspace
-  56     # 2: delete
-  57 ]
-  58 
-  59 # every editor accumulates a list of operations to undo/redo
-  60 container editor [
-  61   undo:&:list:&:operation
-  62   redo:&:list:&:operation
-  63 ]
-  64 
-  65 # ctrl-z - undo operation
-  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 # ctrl-y - redo operation
-  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 # undo typing
- 102 
- 103 scenario editor-can-undo-typing [
- 104   local-scope
- 105   # create an editor and type a character
- 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   # undo
- 114   assume-console [
- 115     press ctrl-z
- 116   ]
- 117   run [
- 118     editor-event-loop screen, console, e
- 119   ]
- 120   # character should be gone
- 121   screen-should-contain [
- 122     .          .
- 123     .          .
- 124     .╌╌╌╌╌╌╌╌╌╌.
- 125     .          .
- 126   ]
- 127   # cursor should be in the right place
- 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 # save operation to undo
- 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     # if previous operation was an insert, coalesce this operation with it
- 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   # if not, create a new operation
- 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 # enter operations never coalesce with typing before or after
- 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   # never coalesce
- 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 # Everytime you add a new operation to the undo stack, be sure to clear the
- 199 # redo stack, because it's now obsolete.
- 200 # Beware: since we're counting cursor moves as operations, this means just
- 201 # moving the cursor can lose work on the undo stack.
- 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     # assert cursor-row/cursor-column/top-of-screen match after-row/after-column/after-top-of-screen
- 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   # create an editor and type multiple characters
- 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   # undo
- 243   assume-console [
- 244     press ctrl-z
- 245   ]
- 246   run [
- 247     editor-event-loop screen, console, e
- 248   ]
- 249   # all characters must be gone
- 250   screen-should-contain [
- 251     .          .
- 252     .          .
- 253     .╌╌╌╌╌╌╌╌╌╌.
- 254     .          .
- 255   ]
- 256 ]
- 257 
- 258 scenario editor-can-undo-typing-multiple-2 [
- 259   local-scope
- 260   # create an editor with some text
- 261   assume-screen 10/width, 5/height
- 262   e:&:editor <- new-editor [a], 0/left, 10/right
- 263   editor-render screen, e
- 264   # type some characters
- 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   # undo
- 276   assume-console [
- 277     press ctrl-z
- 278   ]
- 279   run [
- 280     editor-event-loop screen, console, e
- 281   ]
- 282   # back to original text
- 283   screen-should-contain [
- 284     .          .
- 285     .a         .
- 286     .╌╌╌╌╌╌╌╌╌╌.
- 287     .          .
- 288   ]
- 289   # cursor should be in the right place
- 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   # create an editor with some text
- 307   assume-screen 10/width, 5/height
- 308   e:&:editor <- new-editor [  abc], 0/left, 10/right
- 309   editor-render screen, e
- 310   # new line
- 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   # line is indented
- 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   # undo
- 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   # back to original text
- 344   screen-should-contain [
- 345     .          .
- 346     .  abc     .
- 347     .╌╌╌╌╌╌╌╌╌╌.
- 348     .          .
- 349   ]
- 350   # cursor should be at end of line
- 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 # redo typing
- 366 
- 367 scenario editor-redo-typing [
- 368   local-scope
- 369   # create an editor, type something, undo
- 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   # redo
- 385   assume-console [
- 386     press ctrl-y
- 387   ]
- 388   run [
- 389     editor-event-loop screen, console, e
- 390   ]
- 391   # all characters must be back
- 392   screen-should-contain [
- 393     .          .
- 394     .012a      .
- 395     .╌╌╌╌╌╌╌╌╌╌.
- 396     .          .
- 397   ]
- 398   # cursor should be in the right place
- 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  # ignore insert-to because it's already been spliced away
- 419     # assert insert-to matches next(before-cursor)
- 420     splice before-cursor, insert-from
- 421     # assert cursor-row/cursor-column/top-of-screen match after-row/after-column/after-top-of-screen
- 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   # create an editor, type something, undo
- 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   # redo
- 449   assume-console [
- 450     press ctrl-y
- 451   ]
- 452   run [
- 453     editor-event-loop screen, console, e
- 454   ]
- 455   # all characters must be back
- 456   screen-should-contain [
- 457     .          .
- 458     .012       .
- 459     .╌╌╌╌╌╌╌╌╌╌.
- 460     .          .
- 461   ]
- 462   # cursor should be in the right place
- 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   # create an editor with some text, do some work, undo
- 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   # do some more work
- 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   # redo
- 504   assume-console [
- 505     press ctrl-y
- 506   ]
- 507   run [
- 508     editor-event-loop screen, console, e
- 509   ]
- 510   # nothing should happen
- 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   # create an editor
- 523   assume-screen 10/width, 5/height
- 524   e:&:editor <- new-editor [], 0/left, 10/right
- 525   editor-render screen, e
- 526   # insert some text and tabs, hit enter, some more text and tabs
- 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   # undo
- 551   assume-console [
- 552     press ctrl-z
- 553   ]
- 554   run [
- 555     editor-event-loop screen, console, e
- 556   ]
- 557   # typing in second line deleted, but not indent
- 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   # undo again
- 572   assume-console [
- 573     press ctrl-z
- 574   ]
- 575   run [
- 576     editor-event-loop screen, console, e
- 577   ]
- 578   # indent and newline deleted
- 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   # undo again
- 592   assume-console [
- 593     press ctrl-z
- 594   ]
- 595   run [
- 596     editor-event-loop screen, console, e
- 597   ]
- 598   # empty screen
- 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   # redo
- 612   assume-console [
- 613     press ctrl-y
- 614   ]
- 615   run [
- 616     editor-event-loop screen, console, e
- 617   ]
- 618   # first line inserted
- 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   # redo again
- 632   assume-console [
- 633     press ctrl-y
- 634   ]
- 635   run [
- 636     editor-event-loop screen, console, e
- 637   ]
- 638   # newline and indent inserted
- 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   # redo again
- 653   assume-console [
- 654     press ctrl-y
- 655   ]
- 656   run [
- 657     editor-event-loop screen, console, e
- 658   ]
- 659   # indent and newline deleted
- 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 # undo cursor movement and scroll
- 676 
- 677 scenario editor-can-undo-touch [
- 678   local-scope
- 679   # create an editor with some text
- 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   # move the cursor
- 687   assume-console [
- 688     left-click 3, 1
- 689   ]
- 690   editor-event-loop screen, console, e
- 691   # undo
- 692   assume-console [
- 693     press ctrl-z
- 694   ]
- 695   run [
- 696     editor-event-loop screen, console, e
- 697   ]
- 698   # click undone
- 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   # cursor should be in the right place
- 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     # if previous operation was also a move, and also had the same coalesce
- 733     # tag, coalesce with it
- 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     # assert cursor-row/cursor-column/top-of-screen match after-row/after-column/after-top-of-screen
- 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   # screen has 1 line for menu + 3 lines
- 771   assume-screen 5/width, 4/height
- 772   # editor contains a wrapped line
- 773   contents:text <- new [a
- 774 b
- 775 cdefgh]
- 776   e:&:editor <- new-editor contents, 0/left, 5/right
- 777   # position cursor at end of screen and try to move right
- 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   # screen scrolls
- 786   screen-should-contain [
- 787     .     .
- 788     .b    .
- 789     .cdef↩.
- 790     .gh   .
- 791   ]
- 792   memory-should-contain [
- 793     3 <- 3
- 794     4 <- 0
- 795   ]
- 796   # undo
- 797   assume-console [
- 798     press ctrl-z
- 799   ]
- 800   run [
- 801     editor-event-loop screen, console, e
- 802   ]
- 803   # cursor moved back
- 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   # scroll undone
- 811   screen-should-contain [
- 812     .     .
- 813     .a    .
- 814     .b    .
- 815     .cdef↩.
- 816   ]
- 817   # cursor should be in the right place
- 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   # create an editor with some text
- 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   # move the cursor
- 842   assume-console [
- 843     left-click 3, 1
- 844     press left-arrow
- 845   ]
- 846   editor-event-loop screen, console, e
- 847   # undo
- 848   assume-console [
- 849     press ctrl-z
- 850   ]
- 851   run [
- 852     editor-event-loop screen, console, e
- 853   ]
- 854   # cursor moves back
- 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   # cursor should be in the right place
- 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   # create an editor with some text
- 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   # move the cursor
- 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   # undo
- 899   assume-console [
- 900     press ctrl-z
- 901   ]
- 902   run [
- 903     editor-event-loop screen, console, e
- 904   ]
- 905   # cursor moves back
- 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   # cursor should be in the right place
- 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   # create an editor with some text
- 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   # move the cursor
- 938   assume-console [
- 939     left-click 2, 1
- 940     press down-arrow
- 941   ]
- 942   editor-event-loop screen, console, e
- 943   # undo
- 944   assume-console [
- 945     press ctrl-z
- 946   ]
- 947   run [
- 948     editor-event-loop screen, console, e
- 949   ]
- 950   # cursor moves back
- 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   # cursor should be in the right place
- 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   # create an editor with multiple pages of text
- 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   # scroll the page
- 986   assume-console [
- 987     press ctrl-f
- 988   ]
- 989   editor-event-loop screen, console, e
- 990   # undo
- 991   assume-console [
- 992     press ctrl-z
- 993   ]
- 994   run [
- 995     editor-event-loop screen, console, e
- 996   ]
- 997   # screen should again show page 1
- 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   # create an editor with multiple pages of text
-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   # scroll the page
-1020   assume-console [
-1021     press page-down
-1022   ]
-1023   editor-event-loop screen, console, e
-1024   # undo
-1025   assume-console [
-1026     press ctrl-z
-1027   ]
-1028   run [
-1029     editor-event-loop screen, console, e
-1030   ]
-1031   # screen should again show page 1
-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   # create an editor with multiple pages of text
-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   # scroll the page down and up
-1054   assume-console [
-1055     press page-down
-1056     press ctrl-b
-1057   ]
-1058   editor-event-loop screen, console, e
-1059   # undo
-1060   assume-console [
-1061     press ctrl-z
-1062   ]
-1063   run [
-1064     editor-event-loop screen, console, e
-1065   ]
-1066   # screen should again show page 2
-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   # create an editor with multiple pages of text
-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   # scroll the page down and up
-1089   assume-console [
-1090     press page-down
-1091     press page-up
-1092   ]
-1093   editor-event-loop screen, console, e
-1094   # undo
-1095   assume-console [
-1096     press ctrl-z
-1097   ]
-1098   run [
-1099     editor-event-loop screen, console, e
-1100   ]
-1101   # screen should again show page 2
-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   # create an editor with some text
-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   # move the cursor, then to start of line
-1121   assume-console [
-1122     left-click 2, 1
-1123     press ctrl-a
-1124   ]
-1125   editor-event-loop screen, console, e
-1126   # undo
-1127   assume-console [
-1128     press ctrl-z
-1129   ]
-1130   run [
-1131     editor-event-loop screen, console, e
-1132   ]
-1133   # cursor moves back
-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   # cursor should be in the right place
-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   # create an editor with some text
-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   # move the cursor, then to start of line
-1166   assume-console [
-1167     left-click 2, 1
-1168     press home
-1169   ]
-1170   editor-event-loop screen, console, e
-1171   # undo
-1172   assume-console [
-1173     press ctrl-z
-1174   ]
-1175   run [
-1176     editor-event-loop screen, console, e
-1177   ]
-1178   # cursor moves back
-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   # cursor should be in the right place
-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   # create an editor with some text
-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   # move the cursor, then to start of line
-1211   assume-console [
-1212     left-click 2, 1
-1213     press ctrl-e
-1214   ]
-1215   editor-event-loop screen, console, e
-1216   # undo
-1217   assume-console [
-1218     press ctrl-z
-1219   ]
-1220   run [
-1221     editor-event-loop screen, console, e
-1222   ]
-1223   # cursor moves back
-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   # cursor should be in the right place
-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   # create an editor with some text
-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   # move the cursor, then to start of line
-1256   assume-console [
-1257     left-click 2, 1
-1258     press end
-1259   ]
-1260   editor-event-loop screen, console, e
-1261   # undo
-1262   assume-console [
-1263     press ctrl-z
-1264   ]
-1265   run [
-1266     editor-event-loop screen, console, e
-1267   ]
-1268   # cursor moves back
-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   # cursor should be in the right place
-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   # create an editor with some text
-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   # move the cursor
-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   # undo
-1315   assume-console [
-1316     press ctrl-z
-1317   ]
-1318   run [
-1319     editor-event-loop screen, console, e
-1320   ]
-1321   # up-arrow is undone
-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   # undo again
-1329   assume-console [
-1330     press ctrl-z
-1331   ]
-1332   run [
-1333     editor-event-loop screen, console, e
-1334   ]
-1335   # both right-arrows are undone
-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 # redo cursor movement and scroll
-1345 
-1346 scenario editor-redo-touch [
-1347   local-scope
-1348   # create an editor with some text, click on a character, undo
-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   # redo
-1361   assume-console [
-1362     press ctrl-y
-1363   ]
-1364   run [
-1365     editor-event-loop screen, console, e
-1366   ]
-1367   # cursor moves to left-click
-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   # cursor should be in the right place
-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     # assert cursor-row/cursor-column/top-of-screen match after-row/after-column/after-top-of-screen
-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   # create an editor, type some text, move the cursor, type some more text
-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   # undo
-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   # last letter typed is deleted
-1438   screen-should-contain [
-1439     .          .
-1440     .abc       .
-1441     .╌╌╌╌╌╌╌╌╌╌.
-1442     .          .
-1443   ]
-1444   memory-should-contain [
-1445     3 <- 1
-1446     4 <- 1
-1447   ]
-1448   # undo again
-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   # no change to screen; cursor moves
-1458   screen-should-contain [
-1459     .          .
-1460     .abc       .
-1461     .╌╌╌╌╌╌╌╌╌╌.
-1462     .          .
-1463   ]
-1464   memory-should-contain [
-1465     3 <- 1
-1466     4 <- 3
-1467   ]
-1468   # undo again
-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   # screen empty
-1478   screen-should-contain [
-1479     .          .
-1480     .          .
-1481     .╌╌╌╌╌╌╌╌╌╌.
-1482     .          .
-1483   ]
-1484   memory-should-contain [
-1485     3 <- 1
-1486     4 <- 0
-1487   ]
-1488   # redo
-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   # first insert
-1498   screen-should-contain [
-1499     .          .
-1500     .abc       .
-1501     .╌╌╌╌╌╌╌╌╌╌.
-1502     .          .
-1503   ]
-1504   memory-should-contain [
-1505     3 <- 1
-1506     4 <- 3
-1507   ]
-1508   # redo again
-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   # cursor moves
-1518   screen-should-contain [
-1519     .          .
-1520     .abc       .
-1521     .╌╌╌╌╌╌╌╌╌╌.
-1522     .          .
-1523   ]
-1524   # cursor moves
-1525   memory-should-contain [
-1526     3 <- 1
-1527     4 <- 1
-1528   ]
-1529   # redo again
-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   # second insert
-1539   screen-should-contain [
-1540     .          .
-1541     .adbc      .
-1542     .╌╌╌╌╌╌╌╌╌╌.
-1543     .          .
-1544   ]
-1545   memory-should-contain [
-1546     3 <- 1
-1547     4 <- 2
-1548   ]
-1549 ]
-1550 
-1551 # undo backspace
-1552 
-1553 scenario editor-can-undo-and-redo-backspace [
-1554   local-scope
-1555   # create an editor
-1556   assume-screen 10/width, 5/height
-1557   e:&:editor <- new-editor [], 0/left, 10/right
-1558   editor-render screen, e
-1559   # insert some text and hit backspace
-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   # undo
-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   # redo
-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 # save operation to undo
-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  # backspace failed; don't add an undo operation
-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       # if previous operation was an insert, coalesce this operation with it
-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     # if not, create a new operation
-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     # assert cursor-row/cursor-column/top-of-screen match after-row/after-column/after-top-of-screen
-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     # assert cursor-row/cursor-column/top-of-screen match after-row/after-column/after-top-of-screen
-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 # undo delete
-1697 
-1698 scenario editor-can-undo-and-redo-delete [
-1699   local-scope
-1700   # create an editor
-1701   assume-screen 10/width, 5/height
-1702   e:&:editor <- new-editor [], 0/left, 10/right
-1703   editor-render screen, e
-1704   # insert some text and hit delete and backspace a few times
-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   # undo deletes
-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   # undo backspace
-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   # undo first delete
-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   # redo first delete
-1784   assume-console [
-1785     press ctrl-y
-1786   ]
-1787   run [
-1788     editor-event-loop screen, console, e
-1789   ]
-1790   # first line inserted
-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   # redo backspace
-1804   assume-console [
-1805     press ctrl-y
-1806   ]
-1807   run [
-1808     editor-event-loop screen, console, e
-1809   ]
-1810   # first line inserted
-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   # redo deletes
-1824   assume-console [
-1825     press ctrl-y
-1826   ]
-1827   run [
-1828     editor-event-loop screen, console, e
-1829   ]
-1830   # first line inserted
-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  # delete failed; don't add an undo operation
-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       # if previous operation was an insert, coalesce this operation with it
-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     # if not, create a new operation
-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 # undo ctrl-k
-1886 
-1887 scenario editor-can-undo-and-redo-ctrl-k [
-1888   local-scope
-1889   # create an editor
-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   # insert some text and hit delete and backspace a few times
-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   # undo
-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   # redo
-1935   assume-console [
-1936     press ctrl-y
-1937   ]
-1938   run [
-1939     editor-event-loop screen, console, e
-1940   ]
-1941   # first line inserted
-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   # cursor should be in the right place
-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  # delete failed; don't add an undo operation
-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 # undo ctrl-u
-1989 
-1990 scenario editor-can-undo-and-redo-ctrl-u [
-1991   local-scope
-1992   # create an editor
-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   # insert some text and hit delete and backspace a few times
-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   # undo
-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   # redo
-2038   assume-console [
-2039     press ctrl-y
-2040   ]
-2041   run [
-2042     editor-event-loop screen, console, e
-2043   ]
-2044   # first line inserted
-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   # cursor should be in the right place
-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  # delete failed; don't add an undo operation
-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   # create an editor
-2095   assume-screen 10/width, 5/height
-2096   e:&:editor <- new-editor [], 0/left, 10/right
-2097   editor-render screen, e
-2098   # insert some text and hit delete and backspace a few times
-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 ]
-
- - - -- cgit 1.4.1-2-gfad0