diff options
Diffstat (limited to 'archive/2.vm/edit/012-editor-undo.mu')
-rw-r--r-- | archive/2.vm/edit/012-editor-undo.mu | 2111 |
1 files changed, 0 insertions, 2111 deletions
diff --git a/archive/2.vm/edit/012-editor-undo.mu b/archive/2.vm/edit/012-editor-undo.mu deleted file mode 100644 index 871f6c74..00000000 --- a/archive/2.vm/edit/012-editor-undo.mu +++ /dev/null @@ -1,2111 +0,0 @@ -## undo/redo - -# for every undoable event, create a type of *operation* that contains all the -# information needed to reverse it -exclusive-container operation [ - typing:insert-operation - move:move-operation - delete:delete-operation -] - -container insert-operation [ - before-row:num - before-column:num - before-top-of-screen:&:duplex-list:char - after-row:num - after-column:num - after-top-of-screen:&:duplex-list:char - # inserted text is from 'insert-from' until 'insert-until'; list doesn't have to terminate - insert-from:&:duplex-list:char - insert-until:&:duplex-list:char - tag:num # event causing this operation; might be used to coalesce runs of similar events - # 0: no coalesce (enter+indent) - # 1: regular alphanumeric characters -] - -container move-operation [ - before-row:num - before-column:num - before-top-of-screen:&:duplex-list:char - after-row:num - after-column:num - after-top-of-screen:&:duplex-list:char - tag:num # event causing this operation; might be used to coalesce runs of similar events - # 0: no coalesce (touch events, etc) - # 1: left arrow - # 2: right arrow - # 3: up arrow - # 4: down arrow - # 5: line up - # 6: line down -] - -container delete-operation [ - before-row:num - before-column:num - before-top-of-screen:&:duplex-list:char - after-row:num - after-column:num - after-top-of-screen:&:duplex-list:char - deleted-text:&:duplex-list:char - delete-from:&:duplex-list:char - delete-until:&:duplex-list:char - tag:num # event causing this operation; might be used to coalesce runs of similar events - # 0: no coalesce (ctrl-k, ctrl-u) - # 1: backspace - # 2: delete -] - -# every editor accumulates a list of operations to undo/redo -container editor [ - undo:&:list:&:operation - redo:&:list:&:operation -] - -# ctrl-z - undo operation -after <handle-special-character> [ - { - undo?:bool <- equal c, 26/ctrl-z - break-unless undo? - undo:&:list:&:operation <- get *editor, undo:offset - break-unless undo - op:&:operation <- first undo - undo <- rest undo - *editor <- put *editor, undo:offset, undo - redo:&:list:&:operation <- get *editor, redo:offset - redo <- push op, redo - *editor <- put *editor, redo:offset, redo - <handle-undo> - return true/go-render - } -] - -# ctrl-y - redo operation -after <handle-special-character> [ - { - redo?:bool <- equal c, 25/ctrl-y - break-unless redo? - redo:&:list:&:operation <- get *editor, redo:offset - break-unless redo - op:&:operation <- first redo - redo <- rest redo - *editor <- put *editor, redo:offset, redo - undo:&:list:&:operation <- get *editor, undo:offset - undo <- push op, undo - *editor <- put *editor, undo:offset, undo - <handle-redo> - return true/go-render - } -] - -# undo typing - -scenario editor-can-undo-typing [ - local-scope - # create an editor and type a character - assume-screen 10/width, 5/height - e:&:editor <- new-editor [], 0/left, 10/right - editor-render screen, e - assume-console [ - type [0] - ] - editor-event-loop screen, console, e - # undo - assume-console [ - press ctrl-z - ] - run [ - editor-event-loop screen, console, e - ] - # character should be gone - screen-should-contain [ - . . - . . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - # cursor should be in the right place - assume-console [ - type [1] - ] - run [ - editor-event-loop screen, console, e - ] - screen-should-contain [ - . . - .1 . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] -] - -# save operation to undo -after <begin-insert-character> [ - top-before:&:duplex-list:char <- get *editor, top-of-screen:offset - cursor-before:&:duplex-list:char <- get *editor, before-cursor:offset -] -before <end-insert-character> [ - top-after:&:duplex-list:char <- get *editor, top-of-screen:offset - cursor-row:num <- get *editor, cursor-row:offset - cursor-column:num <- get *editor, cursor-column:offset - undo:&:list:&:operation <- get *editor, undo:offset - { - # if previous operation was an insert, coalesce this operation with it - break-unless undo - op:&:operation <- first undo - typing:insert-operation, is-insert?:bool <- maybe-convert *op, typing:variant - break-unless is-insert? - previous-coalesce-tag:num <- get typing, tag:offset - break-unless previous-coalesce-tag - before-cursor:&:duplex-list:char <- get *editor, before-cursor:offset - insert-until:&:duplex-list:char <- next before-cursor - typing <- put typing, insert-until:offset, insert-until - typing <- put typing, after-row:offset, cursor-row - typing <- put typing, after-column:offset, cursor-column - typing <- put typing, after-top-of-screen:offset, top-after - *op <- merge 0/insert-operation, typing - break +done-adding-insert-operation - } - # if not, create a new operation - insert-from:&:duplex-list:char <- next cursor-before - insert-to:&:duplex-list:char <- next insert-from - op:&:operation <- new operation:type - *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 - editor <- add-operation editor, op - +done-adding-insert-operation -] - -# enter operations never coalesce with typing before or after -after <begin-insert-enter> [ - cursor-row-before:num <- copy cursor-row - cursor-column-before:num <- copy cursor-column - top-before:&:duplex-list:char <- get *editor, top-of-screen:offset - cursor-before:&:duplex-list:char <- get *editor, before-cursor:offset -] -before <end-insert-enter> [ - top-after:&:duplex-list:char <- get *editor, top-of-screen:offset - cursor-row:num <- get *editor, cursor-row:offset - cursor-column:num <- get *editor, cursor-row:offset - # never coalesce - insert-from:&:duplex-list:char <- next cursor-before - before-cursor:&:duplex-list:char <- get *editor, before-cursor:offset - insert-to:&:duplex-list:char <- next before-cursor - op:&:operation <- new operation:type - *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 - editor <- add-operation editor, op -] - -# Everytime you add a new operation to the undo stack, be sure to clear the -# redo stack, because it's now obsolete. -# Beware: since we're counting cursor moves as operations, this means just -# moving the cursor can lose work on the undo stack. -def add-operation editor:&:editor, op:&:operation -> editor:&:editor [ - local-scope - load-inputs - undo:&:list:&:operation <- get *editor, undo:offset - undo <- push op undo - *editor <- put *editor, undo:offset, undo - redo:&:list:&:operation <- get *editor, redo:offset - redo <- copy null - *editor <- put *editor, redo:offset, redo -] - -after <handle-undo> [ - { - typing:insert-operation, is-insert?:bool <- maybe-convert *op, typing:variant - break-unless is-insert? - start:&:duplex-list:char <- get typing, insert-from:offset - end:&:duplex-list:char <- get typing, insert-until:offset - # assert cursor-row/cursor-column/top-of-screen match after-row/after-column/after-top-of-screen - before-cursor:&:duplex-list:char <- prev start - *editor <- put *editor, before-cursor:offset, before-cursor - remove-between before-cursor, end - cursor-row <- get typing, before-row:offset - *editor <- put *editor, cursor-row:offset, cursor-row - cursor-column <- get typing, before-column:offset - *editor <- put *editor, cursor-column:offset, cursor-column - top:&:duplex-list:char <- get typing, before-top-of-screen:offset - *editor <- put *editor, top-of-screen:offset, top - } -] - -scenario editor-can-undo-typing-multiple [ - local-scope - # create an editor and type multiple characters - assume-screen 10/width, 5/height - e:&:editor <- new-editor [], 0/left, 10/right - editor-render screen, e - assume-console [ - type [012] - ] - editor-event-loop screen, console, e - # undo - assume-console [ - press ctrl-z - ] - run [ - editor-event-loop screen, console, e - ] - # all characters must be gone - screen-should-contain [ - . . - . . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] -] - -scenario editor-can-undo-typing-multiple-2 [ - local-scope - # create an editor with some text - assume-screen 10/width, 5/height - e:&:editor <- new-editor [a], 0/left, 10/right - editor-render screen, e - # type some characters - assume-console [ - type [012] - ] - editor-event-loop screen, console, e - screen-should-contain [ - . . - .012a . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - # undo - assume-console [ - press ctrl-z - ] - run [ - editor-event-loop screen, console, e - ] - # back to original text - screen-should-contain [ - . . - .a . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - # cursor should be in the right place - assume-console [ - type [3] - ] - run [ - editor-event-loop screen, console, e - ] - screen-should-contain [ - . . - .3a . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] -] - -scenario editor-can-undo-typing-enter [ - local-scope - # create an editor with some text - assume-screen 10/width, 5/height - e:&:editor <- new-editor [ abc], 0/left, 10/right - editor-render screen, e - # new line - assume-console [ - left-click 1, 8 - press enter - ] - editor-event-loop screen, console, e - screen-should-contain [ - . . - . abc . - . . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - # line is indented - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - memory-should-contain [ - 3 <- 2 - 4 <- 2 - ] - # undo - assume-console [ - press ctrl-z - ] - run [ - editor-event-loop screen, console, e - ] - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - memory-should-contain [ - 3 <- 1 - 4 <- 5 - ] - # back to original text - screen-should-contain [ - . . - . abc . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - # cursor should be at end of line - assume-console [ - type [1] - ] - run [ - editor-event-loop screen, console, e - ] - screen-should-contain [ - . . - . abc1 . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] -] - -# redo typing - -scenario editor-redo-typing [ - local-scope - # create an editor, type something, undo - assume-screen 10/width, 5/height - e:&:editor <- new-editor [a], 0/left, 10/right - editor-render screen, e - assume-console [ - type [012] - press ctrl-z - ] - editor-event-loop screen, console, e - screen-should-contain [ - . . - .a . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - # redo - assume-console [ - press ctrl-y - ] - run [ - editor-event-loop screen, console, e - ] - # all characters must be back - screen-should-contain [ - . . - .012a . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - # cursor should be in the right place - assume-console [ - type [3] - ] - run [ - editor-event-loop screen, console, e - ] - screen-should-contain [ - . . - .0123a . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] -] - -after <handle-redo> [ - { - typing:insert-operation, is-insert?:bool <- maybe-convert *op, typing:variant - break-unless is-insert? - before-cursor <- get *editor, before-cursor:offset - insert-from:&:duplex-list:char <- get typing, insert-from:offset # ignore insert-to because it's already been spliced away - # assert insert-to matches next(before-cursor) - splice before-cursor, insert-from - # assert cursor-row/cursor-column/top-of-screen match after-row/after-column/after-top-of-screen - cursor-row <- get typing, after-row:offset - *editor <- put *editor, cursor-row:offset, cursor-row - cursor-column <- get typing, after-column:offset - *editor <- put *editor, cursor-column:offset, cursor-column - top:&:duplex-list:char <- get typing, after-top-of-screen:offset - *editor <- put *editor, top-of-screen:offset, top - } -] - -scenario editor-redo-typing-empty [ - local-scope - # create an editor, type something, undo - assume-screen 10/width, 5/height - e:&:editor <- new-editor [], 0/left, 10/right - editor-render screen, e - assume-console [ - type [012] - press ctrl-z - ] - editor-event-loop screen, console, e - screen-should-contain [ - . . - . . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - # redo - assume-console [ - press ctrl-y - ] - run [ - editor-event-loop screen, console, e - ] - # all characters must be back - screen-should-contain [ - . . - .012 . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - # cursor should be in the right place - assume-console [ - type [3] - ] - run [ - editor-event-loop screen, console, e - ] - screen-should-contain [ - . . - .0123 . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] -] - -scenario editor-work-clears-redo-stack [ - local-scope - # create an editor with some text, do some work, undo - assume-screen 10/width, 5/height - contents:text <- new [abc -def -ghi] - e:&:editor <- new-editor contents, 0/left, 10/right - editor-render screen, e - assume-console [ - type [1] - press ctrl-z - ] - editor-event-loop screen, console, e - # do some more work - assume-console [ - type [0] - ] - editor-event-loop screen, console, e - screen-should-contain [ - . . - .0abc . - .def . - .ghi . - .┈┈┈┈┈┈┈┈┈┈. - ] - # redo - assume-console [ - press ctrl-y - ] - run [ - editor-event-loop screen, console, e - ] - # nothing should happen - screen-should-contain [ - . . - .0abc . - .def . - .ghi . - .┈┈┈┈┈┈┈┈┈┈. - ] -] - -scenario editor-can-redo-typing-and-enter-and-tab [ - local-scope - # create an editor - assume-screen 10/width, 5/height - e:&:editor <- new-editor [], 0/left, 10/right - editor-render screen, e - # insert some text and tabs, hit enter, some more text and tabs - assume-console [ - press tab - type [ab] - press tab - type [cd] - press enter - press tab - type [efg] - ] - editor-event-loop screen, console, e - screen-should-contain [ - . . - . ab cd . - . efg . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - memory-should-contain [ - 3 <- 2 - 4 <- 7 - ] - # undo - assume-console [ - press ctrl-z - ] - run [ - editor-event-loop screen, console, e - ] - # typing in second line deleted, but not indent - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - memory-should-contain [ - 3 <- 2 - 4 <- 2 - ] - screen-should-contain [ - . . - . ab cd . - . . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - # undo again - assume-console [ - press ctrl-z - ] - run [ - editor-event-loop screen, console, e - ] - # indent and newline deleted - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - memory-should-contain [ - 3 <- 1 - 4 <- 8 - ] - screen-should-contain [ - . . - . ab cd . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - # undo again - assume-console [ - press ctrl-z - ] - run [ - editor-event-loop screen, console, e - ] - # empty screen - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - memory-should-contain [ - 3 <- 1 - 4 <- 0 - ] - screen-should-contain [ - . . - . . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - # redo - assume-console [ - press ctrl-y - ] - run [ - editor-event-loop screen, console, e - ] - # first line inserted - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - memory-should-contain [ - 3 <- 1 - 4 <- 8 - ] - screen-should-contain [ - . . - . ab cd . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - # redo again - assume-console [ - press ctrl-y - ] - run [ - editor-event-loop screen, console, e - ] - # newline and indent inserted - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - memory-should-contain [ - 3 <- 2 - 4 <- 2 - ] - screen-should-contain [ - . . - . ab cd . - . . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - # redo again - assume-console [ - press ctrl-y - ] - run [ - editor-event-loop screen, console, e - ] - # indent and newline deleted - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - memory-should-contain [ - 3 <- 2 - 4 <- 7 - ] - screen-should-contain [ - . . - . ab cd . - . efg . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] -] - -# undo cursor movement and scroll - -scenario editor-can-undo-touch [ - local-scope - # create an editor with some text - assume-screen 10/width, 5/height - contents:text <- new [abc -def -ghi] - e:&:editor <- new-editor contents, 0/left, 10/right - editor-render screen, e - # move the cursor - assume-console [ - left-click 3, 1 - ] - editor-event-loop screen, console, e - # undo - assume-console [ - press ctrl-z - ] - run [ - editor-event-loop screen, console, e - ] - # click undone - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - memory-should-contain [ - 3 <- 1 - 4 <- 0 - ] - # cursor should be in the right place - assume-console [ - type [1] - ] - run [ - editor-event-loop screen, console, e - ] - screen-should-contain [ - . . - .1abc . - .def . - .ghi . - .┈┈┈┈┈┈┈┈┈┈. - ] -] - -after <begin-move-cursor> [ - cursor-row-before:num <- get *editor, cursor-row:offset - cursor-column-before:num <- get *editor, cursor-column:offset - top-before:&:duplex-list:char <- get *editor, top-of-screen:offset -] -before <end-move-cursor> [ - top-after:&:duplex-list:char <- get *editor, top-of-screen:offset - cursor-row:num <- get *editor, cursor-row:offset - cursor-column:num <- get *editor, cursor-column:offset - { - break-unless undo-coalesce-tag - # if previous operation was also a move, and also had the same coalesce - # tag, coalesce with it - undo:&:list:&:operation <- get *editor, undo:offset - break-unless undo - op:&:operation <- first undo - move:move-operation, is-move?:bool <- maybe-convert *op, move:variant - break-unless is-move? - previous-coalesce-tag:num <- get move, tag:offset - coalesce?:bool <- equal undo-coalesce-tag, previous-coalesce-tag - break-unless coalesce? - move <- put move, after-row:offset, cursor-row - move <- put move, after-column:offset, cursor-column - move <- put move, after-top-of-screen:offset, top-after - *op <- merge 1/move-operation, move - break +done-adding-move-operation - } - op:&:operation <- new operation:type - *op <- merge 1/move-operation, cursor-row-before, cursor-column-before, top-before, cursor-row/after, cursor-column/after, top-after, undo-coalesce-tag - editor <- add-operation editor, op - +done-adding-move-operation -] - -after <handle-undo> [ - { - move:move-operation, is-move?:bool <- maybe-convert *op, move:variant - break-unless is-move? - # assert cursor-row/cursor-column/top-of-screen match after-row/after-column/after-top-of-screen - cursor-row <- get move, before-row:offset - *editor <- put *editor, cursor-row:offset, cursor-row - cursor-column <- get move, before-column:offset - *editor <- put *editor, cursor-column:offset, cursor-column - top:&:duplex-list:char <- get move, before-top-of-screen:offset - *editor <- put *editor, top-of-screen:offset, top - } -] - -scenario editor-can-undo-scroll [ - local-scope - # screen has 1 line for menu + 3 lines - assume-screen 5/width, 4/height - # editor contains a wrapped line - contents:text <- new [a -b -cdefgh] - e:&:editor <- new-editor contents, 0/left, 5/right - # position cursor at end of screen and try to move right - assume-console [ - left-click 3, 3 - press right-arrow - ] - editor-event-loop screen, console, e - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - # screen scrolls - screen-should-contain [ - . . - .b . - .cdef↩. - .gh . - ] - memory-should-contain [ - 3 <- 3 - 4 <- 0 - ] - # undo - assume-console [ - press ctrl-z - ] - run [ - editor-event-loop screen, console, e - ] - # cursor moved back - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - memory-should-contain [ - 3 <- 3 - 4 <- 3 - ] - # scroll undone - screen-should-contain [ - . . - .a . - .b . - .cdef↩. - ] - # cursor should be in the right place - assume-console [ - type [1] - ] - run [ - editor-event-loop screen, console, e - ] - screen-should-contain [ - . . - .b . - .cde1↩. - .fgh . - ] -] - -scenario editor-can-undo-left-arrow [ - local-scope - # create an editor with some text - assume-screen 10/width, 5/height - contents:text <- new [abc -def -ghi] - e:&:editor <- new-editor contents, 0/left, 10/right - editor-render screen, e - # move the cursor - assume-console [ - left-click 3, 1 - press left-arrow - ] - editor-event-loop screen, console, e - # undo - assume-console [ - press ctrl-z - ] - run [ - editor-event-loop screen, console, e - ] - # cursor moves back - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - memory-should-contain [ - 3 <- 3 - 4 <- 1 - ] - # cursor should be in the right place - assume-console [ - type [1] - ] - run [ - editor-event-loop screen, console, e - ] - screen-should-contain [ - . . - .abc . - .def . - .g1hi . - .┈┈┈┈┈┈┈┈┈┈. - ] -] - -scenario editor-can-undo-up-arrow [ - local-scope - # create an editor with some text - assume-screen 10/width, 5/height - contents:text <- new [abc -def -ghi] - e:&:editor <- new-editor contents, 0/left, 10/right - editor-render screen, e - # move the cursor - assume-console [ - left-click 3, 1 - press up-arrow - ] - editor-event-loop screen, console, e - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - memory-should-contain [ - 3 <- 2 - 4 <- 1 - ] - # undo - assume-console [ - press ctrl-z - ] - run [ - editor-event-loop screen, console, e - ] - # cursor moves back - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - memory-should-contain [ - 3 <- 3 - 4 <- 1 - ] - # cursor should be in the right place - assume-console [ - type [1] - ] - run [ - editor-event-loop screen, console, e - ] - screen-should-contain [ - . . - .abc . - .def . - .g1hi . - .┈┈┈┈┈┈┈┈┈┈. - ] -] - -scenario editor-can-undo-down-arrow [ - local-scope - # create an editor with some text - assume-screen 10/width, 5/height - contents:text <- new [abc -def -ghi] - e:&:editor <- new-editor contents, 0/left, 10/right - editor-render screen, e - # move the cursor - assume-console [ - left-click 2, 1 - press down-arrow - ] - editor-event-loop screen, console, e - # undo - assume-console [ - press ctrl-z - ] - run [ - editor-event-loop screen, console, e - ] - # cursor moves back - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - memory-should-contain [ - 3 <- 2 - 4 <- 1 - ] - # cursor should be in the right place - assume-console [ - type [1] - ] - run [ - editor-event-loop screen, console, e - ] - screen-should-contain [ - . . - .abc . - .d1ef . - .ghi . - .┈┈┈┈┈┈┈┈┈┈. - ] -] - -scenario editor-can-undo-ctrl-f [ - local-scope - # create an editor with multiple pages of text - assume-screen 10/width, 5/height - contents:text <- new [a -b -c -d -e -f] - e:&:editor <- new-editor contents, 0/left, 10/right - editor-render screen, e - # scroll the page - assume-console [ - press ctrl-f - ] - editor-event-loop screen, console, e - # undo - assume-console [ - press ctrl-z - ] - run [ - editor-event-loop screen, console, e - ] - # screen should again show page 1 - screen-should-contain [ - . . - .a . - .b . - .c . - .d . - ] -] - -scenario editor-can-undo-page-down [ - local-scope - # create an editor with multiple pages of text - assume-screen 10/width, 5/height - contents:text <- new [a -b -c -d -e -f] - e:&:editor <- new-editor contents, 0/left, 10/right - editor-render screen, e - # scroll the page - assume-console [ - press page-down - ] - editor-event-loop screen, console, e - # undo - assume-console [ - press ctrl-z - ] - run [ - editor-event-loop screen, console, e - ] - # screen should again show page 1 - screen-should-contain [ - . . - .a . - .b . - .c . - .d . - ] -] - -scenario editor-can-undo-ctrl-b [ - local-scope - # create an editor with multiple pages of text - assume-screen 10/width, 5/height - contents:text <- new [a -b -c -d -e -f] - e:&:editor <- new-editor contents, 0/left, 10/right - editor-render screen, e - # scroll the page down and up - assume-console [ - press page-down - press ctrl-b - ] - editor-event-loop screen, console, e - # undo - assume-console [ - press ctrl-z - ] - run [ - editor-event-loop screen, console, e - ] - # screen should again show page 2 - screen-should-contain [ - . . - .d . - .e . - .f . - .┈┈┈┈┈┈┈┈┈┈. - ] -] - -scenario editor-can-undo-page-up [ - local-scope - # create an editor with multiple pages of text - assume-screen 10/width, 5/height - contents:text <- new [a -b -c -d -e -f] - e:&:editor <- new-editor contents, 0/left, 10/right - editor-render screen, e - # scroll the page down and up - assume-console [ - press page-down - press page-up - ] - editor-event-loop screen, console, e - # undo - assume-console [ - press ctrl-z - ] - run [ - editor-event-loop screen, console, e - ] - # screen should again show page 2 - screen-should-contain [ - . . - .d . - .e . - .f . - .┈┈┈┈┈┈┈┈┈┈. - ] -] - -scenario editor-can-undo-ctrl-a [ - local-scope - # create an editor with some text - assume-screen 10/width, 5/height - contents:text <- new [abc -def -ghi] - e:&:editor <- new-editor contents, 0/left, 10/right - editor-render screen, e - # move the cursor, then to start of line - assume-console [ - left-click 2, 1 - press ctrl-a - ] - editor-event-loop screen, console, e - # undo - assume-console [ - press ctrl-z - ] - run [ - editor-event-loop screen, console, e - ] - # cursor moves back - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - memory-should-contain [ - 3 <- 2 - 4 <- 1 - ] - # cursor should be in the right place - assume-console [ - type [1] - ] - run [ - editor-event-loop screen, console, e - ] - screen-should-contain [ - . . - .abc . - .d1ef . - .ghi . - .┈┈┈┈┈┈┈┈┈┈. - ] -] - -scenario editor-can-undo-home [ - local-scope - # create an editor with some text - assume-screen 10/width, 5/height - contents:text <- new [abc -def -ghi] - e:&:editor <- new-editor contents, 0/left, 10/right - editor-render screen, e - # move the cursor, then to start of line - assume-console [ - left-click 2, 1 - press home - ] - editor-event-loop screen, console, e - # undo - assume-console [ - press ctrl-z - ] - run [ - editor-event-loop screen, console, e - ] - # cursor moves back - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - memory-should-contain [ - 3 <- 2 - 4 <- 1 - ] - # cursor should be in the right place - assume-console [ - type [1] - ] - run [ - editor-event-loop screen, console, e - ] - screen-should-contain [ - . . - .abc . - .d1ef . - .ghi . - .┈┈┈┈┈┈┈┈┈┈. - ] -] - -scenario editor-can-undo-ctrl-e [ - local-scope - # create an editor with some text - assume-screen 10/width, 5/height - contents:text <- new [abc -def -ghi] - e:&:editor <- new-editor contents, 0/left, 10/right - editor-render screen, e - # move the cursor, then to start of line - assume-console [ - left-click 2, 1 - press ctrl-e - ] - editor-event-loop screen, console, e - # undo - assume-console [ - press ctrl-z - ] - run [ - editor-event-loop screen, console, e - ] - # cursor moves back - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - memory-should-contain [ - 3 <- 2 - 4 <- 1 - ] - # cursor should be in the right place - assume-console [ - type [1] - ] - run [ - editor-event-loop screen, console, e - ] - screen-should-contain [ - . . - .abc . - .d1ef . - .ghi . - .┈┈┈┈┈┈┈┈┈┈. - ] -] - -scenario editor-can-undo-end [ - local-scope - # create an editor with some text - assume-screen 10/width, 5/height - contents:text <- new [abc -def -ghi] - e:&:editor <- new-editor contents, 0/left, 10/right - editor-render screen, e - # move the cursor, then to start of line - assume-console [ - left-click 2, 1 - press end - ] - editor-event-loop screen, console, e - # undo - assume-console [ - press ctrl-z - ] - run [ - editor-event-loop screen, console, e - ] - # cursor moves back - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - memory-should-contain [ - 3 <- 2 - 4 <- 1 - ] - # cursor should be in the right place - assume-console [ - type [1] - ] - run [ - editor-event-loop screen, console, e - ] - screen-should-contain [ - . . - .abc . - .d1ef . - .ghi . - .┈┈┈┈┈┈┈┈┈┈. - ] -] - -scenario editor-can-undo-multiple-arrows-in-the-same-direction [ - local-scope - # create an editor with some text - assume-screen 10/width, 5/height - contents:text <- new [abc -def -ghi] - e:&:editor <- new-editor contents, 0/left, 10/right - editor-render screen, e - # move the cursor - assume-console [ - left-click 2, 1 - press right-arrow - press right-arrow - press up-arrow - ] - editor-event-loop screen, console, e - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - memory-should-contain [ - 3 <- 1 - 4 <- 3 - ] - # undo - assume-console [ - press ctrl-z - ] - run [ - editor-event-loop screen, console, e - ] - # up-arrow is undone - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - memory-should-contain [ - 3 <- 2 - 4 <- 3 - ] - # undo again - assume-console [ - press ctrl-z - ] - run [ - editor-event-loop screen, console, e - ] - # both right-arrows are undone - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - memory-should-contain [ - 3 <- 2 - 4 <- 1 - ] -] - -# redo cursor movement and scroll - -scenario editor-redo-touch [ - local-scope - # create an editor with some text, click on a character, undo - assume-screen 10/width, 5/height - contents:text <- new [abc -def -ghi] - e:&:editor <- new-editor contents, 0/left, 10/right - editor-render screen, e - assume-console [ - left-click 3, 1 - press ctrl-z - ] - editor-event-loop screen, console, e - # redo - assume-console [ - press ctrl-y - ] - run [ - editor-event-loop screen, console, e - ] - # cursor moves to left-click - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - memory-should-contain [ - 3 <- 3 - 4 <- 1 - ] - # cursor should be in the right place - assume-console [ - type [1] - ] - run [ - editor-event-loop screen, console, e - ] - screen-should-contain [ - . . - .abc . - .def . - .g1hi . - .┈┈┈┈┈┈┈┈┈┈. - ] -] - -after <handle-redo> [ - { - move:move-operation, is-move?:bool <- maybe-convert *op, move:variant - break-unless is-move? - # assert cursor-row/cursor-column/top-of-screen match after-row/after-column/after-top-of-screen - cursor-row <- get move, after-row:offset - *editor <- put *editor, cursor-row:offset, cursor-row - cursor-column <- get move, after-column:offset - *editor <- put *editor, cursor-column:offset, cursor-column - top:&:duplex-list:char <- get move, after-top-of-screen:offset - *editor <- put *editor, top-of-screen:offset, top - } -] - -scenario editor-separates-undo-insert-from-undo-cursor-move [ - local-scope - # create an editor, type some text, move the cursor, type some more text - assume-screen 10/width, 5/height - e:&:editor <- new-editor [], 0/left, 10/right - editor-render screen, e - assume-console [ - type [abc] - left-click 1, 1 - type [d] - ] - editor-event-loop screen, console, e - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - screen-should-contain [ - . . - .adbc . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - memory-should-contain [ - 3 <- 1 - 4 <- 2 - ] - # undo - assume-console [ - press ctrl-z - ] - run [ - editor-event-loop screen, console, e - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - ] - # last letter typed is deleted - screen-should-contain [ - . . - .abc . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - memory-should-contain [ - 3 <- 1 - 4 <- 1 - ] - # undo again - assume-console [ - press ctrl-z - ] - run [ - editor-event-loop screen, console, e - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - ] - # no change to screen; cursor moves - screen-should-contain [ - . . - .abc . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - memory-should-contain [ - 3 <- 1 - 4 <- 3 - ] - # undo again - assume-console [ - press ctrl-z - ] - run [ - editor-event-loop screen, console, e - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - ] - # screen empty - screen-should-contain [ - . . - . . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - memory-should-contain [ - 3 <- 1 - 4 <- 0 - ] - # redo - assume-console [ - press ctrl-y - ] - run [ - editor-event-loop screen, console, e - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - ] - # first insert - screen-should-contain [ - . . - .abc . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - memory-should-contain [ - 3 <- 1 - 4 <- 3 - ] - # redo again - assume-console [ - press ctrl-y - ] - run [ - editor-event-loop screen, console, e - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - ] - # cursor moves - screen-should-contain [ - . . - .abc . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - # cursor moves - memory-should-contain [ - 3 <- 1 - 4 <- 1 - ] - # redo again - assume-console [ - press ctrl-y - ] - run [ - editor-event-loop screen, console, e - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - ] - # second insert - screen-should-contain [ - . . - .adbc . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - memory-should-contain [ - 3 <- 1 - 4 <- 2 - ] -] - -# undo backspace - -scenario editor-can-undo-and-redo-backspace [ - local-scope - # create an editor - assume-screen 10/width, 5/height - e:&:editor <- new-editor [], 0/left, 10/right - editor-render screen, e - # insert some text and hit backspace - assume-console [ - type [abc] - press backspace - press backspace - ] - editor-event-loop screen, console, e - screen-should-contain [ - . . - .a . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - memory-should-contain [ - 3 <- 1 - 4 <- 1 - ] - # undo - assume-console [ - press ctrl-z - ] - run [ - editor-event-loop screen, console, e - ] - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - memory-should-contain [ - 3 <- 1 - 4 <- 3 - ] - screen-should-contain [ - . . - .abc . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - # redo - assume-console [ - press ctrl-y - ] - run [ - editor-event-loop screen, console, e - ] - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - memory-should-contain [ - 3 <- 1 - 4 <- 1 - ] - screen-should-contain [ - . . - .a . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] -] - -# save operation to undo -after <begin-backspace-character> [ - top-before:&:duplex-list:char <- get *editor, top-of-screen:offset -] -before <end-backspace-character> [ - { - break-unless backspaced-cell # backspace failed; don't add an undo operation - top-after:&:duplex-list:char <- get *editor, top-of-screen:offset - cursor-row:num <- get *editor, cursor-row:offset - cursor-column:num <- get *editor, cursor-row:offset - before-cursor:&:duplex-list:char <- get *editor, before-cursor:offset - undo:&:list:&:operation <- get *editor, undo:offset - { - # if previous operation was an insert, coalesce this operation with it - break-unless undo - op:&:operation <- first undo - deletion:delete-operation, is-delete?:bool <- maybe-convert *op, delete:variant - break-unless is-delete? - previous-coalesce-tag:num <- get deletion, tag:offset - coalesce?:bool <- equal previous-coalesce-tag, 1/coalesce-backspace - break-unless coalesce? - deletion <- put deletion, delete-from:offset, before-cursor - backspaced-so-far:&:duplex-list:char <- get deletion, deleted-text:offset - splice backspaced-cell, backspaced-so-far - deletion <- put deletion, deleted-text:offset, backspaced-cell - deletion <- put deletion, after-row:offset, cursor-row - deletion <- put deletion, after-column:offset, cursor-column - deletion <- put deletion, after-top-of-screen:offset, top-after - *op <- merge 2/delete-operation, deletion - break +done-adding-backspace-operation - } - # if not, create a new operation - op:&:operation <- new operation:type - deleted-until:&:duplex-list:char <- next before-cursor - *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 - editor <- add-operation editor, op - +done-adding-backspace-operation - } -] - -after <handle-undo> [ - { - deletion:delete-operation, is-delete?:bool <- maybe-convert *op, delete:variant - break-unless is-delete? - anchor:&:duplex-list:char <- get deletion, delete-from:offset - break-unless anchor - deleted:&:duplex-list:char <- get deletion, deleted-text:offset - old-cursor:&:duplex-list:char <- last deleted - splice anchor, deleted - # assert cursor-row/cursor-column/top-of-screen match after-row/after-column/after-top-of-screen - before-cursor <- copy old-cursor - cursor-row <- get deletion, before-row:offset - *editor <- put *editor, cursor-row:offset, cursor-row - cursor-column <- get deletion, before-column:offset - *editor <- put *editor, cursor-column:offset, cursor-column - top:&:duplex-list:char <- get deletion, before-top-of-screen:offset - *editor <- put *editor, top-of-screen:offset, top - } -] - -after <handle-redo> [ - { - deletion:delete-operation, is-delete?:bool <- maybe-convert *op, delete:variant - break-unless is-delete? - start:&:duplex-list:char <- get deletion, delete-from:offset - end:&:duplex-list:char <- get deletion, delete-until:offset - data:&:duplex-list:char <- get *editor, data:offset - remove-between start, end - # assert cursor-row/cursor-column/top-of-screen match after-row/after-column/after-top-of-screen - cursor-row <- get deletion, after-row:offset - *editor <- put *editor, cursor-row:offset, cursor-row - cursor-column <- get deletion, after-column:offset - *editor <- put *editor, cursor-column:offset, cursor-column - top:&:duplex-list:char <- get deletion, before-top-of-screen:offset - *editor <- put *editor, top-of-screen:offset, top - } -] - -# undo delete - -scenario editor-can-undo-and-redo-delete [ - local-scope - # create an editor - assume-screen 10/width, 5/height - e:&:editor <- new-editor [], 0/left, 10/right - editor-render screen, e - # insert some text and hit delete and backspace a few times - assume-console [ - type [abcdef] - left-click 1, 2 - press delete - press backspace - press delete - press delete - ] - editor-event-loop screen, console, e - screen-should-contain [ - . . - .af . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - memory-should-contain [ - 3 <- 1 - 4 <- 1 - ] - # undo deletes - assume-console [ - press ctrl-z - ] - run [ - editor-event-loop screen, console, e - ] - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - memory-should-contain [ - 3 <- 1 - 4 <- 1 - ] - screen-should-contain [ - . . - .adef . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - # undo backspace - assume-console [ - press ctrl-z - ] - run [ - editor-event-loop screen, console, e - ] - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - memory-should-contain [ - 3 <- 1 - 4 <- 2 - ] - screen-should-contain [ - . . - .abdef . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - # undo first delete - assume-console [ - press ctrl-z - ] - run [ - editor-event-loop screen, console, e - ] - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - memory-should-contain [ - 3 <- 1 - 4 <- 2 - ] - screen-should-contain [ - . . - .abcdef . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - # redo first delete - assume-console [ - press ctrl-y - ] - run [ - editor-event-loop screen, console, e - ] - # first line inserted - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - memory-should-contain [ - 3 <- 1 - 4 <- 2 - ] - screen-should-contain [ - . . - .abdef . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - # redo backspace - assume-console [ - press ctrl-y - ] - run [ - editor-event-loop screen, console, e - ] - # first line inserted - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - memory-should-contain [ - 3 <- 1 - 4 <- 1 - ] - screen-should-contain [ - . . - .adef . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - # redo deletes - assume-console [ - press ctrl-y - ] - run [ - editor-event-loop screen, console, e - ] - # first line inserted - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - memory-should-contain [ - 3 <- 1 - 4 <- 1 - ] - screen-should-contain [ - . . - .af . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] -] - -after <begin-delete-character> [ - top-before:&:duplex-list:char <- get *editor, top-of-screen:offset -] -before <end-delete-character> [ - { - break-unless deleted-cell # delete failed; don't add an undo operation - top-after:&:duplex-list:char <- get *editor, top-of-screen:offset - cursor-row:num <- get *editor, cursor-row:offset - cursor-column:num <- get *editor, cursor-column:offset - before-cursor:&:duplex-list:char <- get *editor, before-cursor:offset - undo:&:list:&:operation <- get *editor, undo:offset - { - # if previous operation was an insert, coalesce this operation with it - break-unless undo - op:&:operation <- first undo - deletion:delete-operation, is-delete?:bool <- maybe-convert *op, delete:variant - break-unless is-delete? - previous-coalesce-tag:num <- get deletion, tag:offset - coalesce?:bool <- equal previous-coalesce-tag, 2/coalesce-delete - break-unless coalesce? - delete-until:&:duplex-list:char <- next before-cursor - deletion <- put deletion, delete-until:offset, delete-until - deleted-so-far:&:duplex-list:char <- get deletion, deleted-text:offset - deleted-so-far <- append deleted-so-far, deleted-cell - deletion <- put deletion, deleted-text:offset, deleted-so-far - deletion <- put deletion, after-row:offset, cursor-row - deletion <- put deletion, after-column:offset, cursor-column - deletion <- put deletion, after-top-of-screen:offset, top-after - *op <- merge 2/delete-operation, deletion - break +done-adding-delete-operation - } - # if not, create a new operation - op:&:operation <- new operation:type - deleted-until:&:duplex-list:char <- next before-cursor - *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 - editor <- add-operation editor, op - +done-adding-delete-operation - } -] - -# undo ctrl-k - -scenario editor-can-undo-and-redo-ctrl-k [ - local-scope - # create an editor - assume-screen 10/width, 5/height - contents:text <- new [abc -def] - e:&:editor <- new-editor contents, 0/left, 10/right - editor-render screen, e - # insert some text and hit delete and backspace a few times - assume-console [ - left-click 1, 1 - press ctrl-k - ] - editor-event-loop screen, console, e - screen-should-contain [ - . . - .a . - .def . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - memory-should-contain [ - 3 <- 1 - 4 <- 1 - ] - # undo - assume-console [ - press ctrl-z - ] - run [ - editor-event-loop screen, console, e - ] - screen-should-contain [ - . . - .abc . - .def . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - memory-should-contain [ - 3 <- 1 - 4 <- 1 - ] - # redo - assume-console [ - press ctrl-y - ] - run [ - editor-event-loop screen, console, e - ] - # first line inserted - screen-should-contain [ - . . - .a . - .def . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - memory-should-contain [ - 3 <- 1 - 4 <- 1 - ] - # cursor should be in the right place - assume-console [ - type [1] - ] - run [ - editor-event-loop screen, console, e - ] - screen-should-contain [ - . . - .a1 . - .def . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] -] - -after <begin-delete-to-end-of-line> [ - top-before:&:duplex-list:char <- get *editor, top-of-screen:offset -] -before <end-delete-to-end-of-line> [ - { - break-unless deleted-cells # delete failed; don't add an undo operation - top-after:&:duplex-list:char <- get *editor, top-of-screen:offset - cursor-row:num <- get *editor, cursor-row:offset - cursor-column:num <- get *editor, cursor-column:offset - deleted-until:&:duplex-list:char <- next before-cursor - op:&:operation <- new operation:type - *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 - editor <- add-operation editor, op - +done-adding-delete-operation - } -] - -# undo ctrl-u - -scenario editor-can-undo-and-redo-ctrl-u [ - local-scope - # create an editor - assume-screen 10/width, 5/height - contents:text <- new [abc -def] - e:&:editor <- new-editor contents, 0/left, 10/right - editor-render screen, e - # insert some text and hit delete and backspace a few times - assume-console [ - left-click 1, 2 - press ctrl-u - ] - editor-event-loop screen, console, e - screen-should-contain [ - . . - .c . - .def . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - memory-should-contain [ - 3 <- 1 - 4 <- 0 - ] - # undo - assume-console [ - press ctrl-z - ] - run [ - editor-event-loop screen, console, e - ] - screen-should-contain [ - . . - .abc . - .def . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - memory-should-contain [ - 3 <- 1 - 4 <- 2 - ] - # redo - assume-console [ - press ctrl-y - ] - run [ - editor-event-loop screen, console, e - ] - # first line inserted - screen-should-contain [ - . . - .c . - .def . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - memory-should-contain [ - 3 <- 1 - 4 <- 0 - ] - # cursor should be in the right place - assume-console [ - type [1] - ] - run [ - editor-event-loop screen, console, e - ] - screen-should-contain [ - . . - .1c . - .def . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] -] - -after <begin-delete-to-start-of-line> [ - top-before:&:duplex-list:char <- get *editor, top-of-screen:offset -] -before <end-delete-to-start-of-line> [ - { - break-unless deleted-cells # delete failed; don't add an undo operation - top-after:&:duplex-list:char <- get *editor, top-of-screen:offset - op:&:operation <- new operation:type - before-cursor:&:duplex-list:char <- get *editor, before-cursor:offset - deleted-until:&:duplex-list:char <- next before-cursor - cursor-row:num <- get *editor, cursor-row:offset - cursor-column:num <- get *editor, cursor-column:offset - *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 - editor <- add-operation editor, op - +done-adding-delete-operation - } -] - -scenario editor-can-undo-and-redo-ctrl-u-2 [ - local-scope - # create an editor - assume-screen 10/width, 5/height - e:&:editor <- new-editor [], 0/left, 10/right - editor-render screen, e - # insert some text and hit delete and backspace a few times - assume-console [ - type [abc] - press ctrl-u - press ctrl-z - ] - editor-event-loop screen, console, e - screen-should-contain [ - . . - .abc . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] -] |