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
insert-from:&:duplex-list:char
insert-until:&:duplex-list:char
tag:num
]
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
]
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
]
container editor [
undo:&:list:&:operation
redo:&:list:&: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 1/go-render
}
]
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 1/go-render
}
]
scenario editor-can-undo-typing [
local-scope
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
assume-console [
press ctrl-z
]
run [
editor-event-loop screen, console, e
]
screen-should-contain [
. .
. .
.╌╌╌╌╌╌╌╌╌╌.
. .
]
assume-console [
type [1]
]
run [
editor-event-loop screen, console, e
]
screen-should-contain [
. .
.1 .
.╌╌╌╌╌╌╌╌╌╌.
. .
]
]
after <insert-character-begin> [
top-before:&:duplex-list:char <- get *editor, top-of-screen:offset
cursor-before:&:duplex-list:char <- get *editor, before-cursor:offset
]
before <insert-character-end> [
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
{
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
}
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
]
after <insert-enter-begin> [
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 <insert-enter-end> [
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
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
]
def add-operation editor:&:editor, op:&:operation -> editor:&:editor [
local-scope
load-ingredients
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 0
*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
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
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
assume-console [
press ctrl-z
]
run [
editor-event-loop screen, console, e
]
screen-should-contain [
. .
. .
.╌╌╌╌╌╌╌╌╌╌.
. .
]
]
scenario editor-can-undo-typing-multiple-2 [
local-scope
assume-screen 10/width, 5/height
e:&:editor <- new-editor [a], 0/left, 10/right
editor-render screen, e
assume-console [
type [012]
]
editor-event-loop screen, console, e
screen-should-contain [
. .
.012a .
.╌╌╌╌╌╌╌╌╌╌.
. .
]
assume-console [
press ctrl-z
]
run [
editor-event-loop screen, console, e
]
screen-should-contain [
. .
.a .
.╌╌╌╌╌╌╌╌╌╌.
. .
]
assume-console [
type [3]
]
run [
editor-event-loop screen, console, e
]
screen-should-contain [
. .
.3a .
.╌╌╌╌╌╌╌╌╌╌.
. .
]
]
scenario editor-can-undo-typing-enter [
local-scope
assume-screen 10/width, 5/height
e:&:editor <- new-editor [ abc], 0/left, 10/right
editor-render screen, e
assume-console [
left-click 1, 8
press enter
]
editor-event-loop screen, console, e
screen-should-contain [
. .
. abc .
. .
.╌╌╌╌╌╌╌╌╌╌.
. .
]
3:num/raw <- get *e, cursor-row:offset
4:num/raw <- get *e, cursor-column:offset
memory-should-contain [
3 <- 2
4 <- 2
]
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
]
screen-should-contain [
. .
. abc .
.╌╌╌╌╌╌╌╌╌╌.
. .
]
assume-console [
type [1]
]
run [
editor-event-loop screen, console, e
]
screen-should-contain [
. .
. abc1 .
.╌╌╌╌╌╌╌╌╌╌.
. .
]
]
scenario editor-redo-typing [
local-scope
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 .
.╌╌╌╌╌╌╌╌╌╌.
. .
]
assume-console [
press ctrl-y
]
run [
editor-event-loop screen, console, e
]
screen-should-contain [
. .
.012a .
.╌╌╌╌╌╌╌╌╌╌.
. .
]
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
insert-range before-cursor, insert-from
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
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 [
. .
. .
.╌╌╌╌╌╌╌╌╌╌.
. .
]
assume-console [
press ctrl-y
]
run [
editor-event-loop screen, console, e
]
screen-should-contain [
. .
.012 .
.╌╌╌╌╌╌╌╌╌╌.
. .
]
assume-console [
type [3]
]
run [
editor-event-loop screen, console, e
]
screen-should-contain [
. .
.0123 .
.╌╌╌╌╌╌╌╌╌╌.
. .
]
]
scenario editor-work-clears-redo-stack [
local-scope
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
assume-console [
type [0]
]
editor-event-loop screen, console, e
screen-should-contain [
. .
.0abc .
.def .
.ghi .
.╌╌╌╌╌╌╌╌╌╌.
]
assume-console [
press ctrl-y
]
run [
editor-event-loop screen, console, e
]
screen-should-contain [
. .
.0abc .
.def .
.ghi .
.╌╌╌╌╌╌╌╌╌╌.
]
]
scenario editor-can-redo-typing-and-enter-and-tab [
local-scope
assume-screen 10/width, 5/height
e:&:editor <- new-editor [], 0/left, 10/right
editor-render screen, e
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
]
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 <- 2
4 <- 2
]
screen-should-contain [
. .
. ab cd .
. .
.╌╌╌╌╌╌╌╌╌╌.
. .
]
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 <- 8
]
screen-should-contain [
. .
. ab cd .
.╌╌╌╌╌╌╌╌╌╌.
. .
]
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 <- 0
]
screen-should-contain [
. .
. .
.╌╌╌╌╌╌╌╌╌╌.
. .
]
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 <- 8
]
screen-should-contain [
. .
. ab cd .
.╌╌╌╌╌╌╌╌╌╌.
. .
]
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 <- 2
4 <- 2
]
screen-should-contain [
. .
. ab cd .
. .
.╌╌╌╌╌╌╌╌╌╌.
. .
]
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 <- 2
4 <- 7
]
screen-should-contain [
. .
. ab cd .
. efg .
.╌╌╌╌╌╌╌╌╌╌.
. .
]
]
scenario editor-can-undo-touch [
local-scope
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
]
editor-event-loop screen, console, e
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 <- 0
]
assume-console [
type [1]
]
run [
editor-event-loop screen, console, e
]
screen-should-contain [
. .
.1abc .
.def .
.ghi .
.╌╌╌╌╌╌╌╌╌╌.
]
]
after <move-cursor-begin> [
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 <move-cursor-end> [
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
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?
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
assume-screen 5/width, 4/height
contents:text <- new [a
b
cdefgh]
e:&:editor <- new-editor contents, 0/left, 5/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-should-contain [
. .
.b .
.cdef↩.
.gh .
]
memory-should-contain [
3 <- 3
4 <- 0
]
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 <- 3
4 <- 3
]
screen-should-contain [
. .
.a .
.b .
.cdef↩.
]
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
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 left-arrow
]
editor-event-loop screen, console, e
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 <- 3
4 <- 1
]
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
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 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
]
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 <- 3
4 <- 1
]
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
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 2, 1
press down-arrow
]
editor-event-loop screen, console, e
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 <- 2
4 <- 1
]
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
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
assume-console [
press ctrl-f
]
editor-event-loop screen, console, e
assume-console [
press ctrl-z
]
run [
editor-event-loop screen, console, e
]
screen-should-contain [
. .
.a .
.b .
.c .
.d .
]
]
scenario editor-can-undo-page-down [
local-scope
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
assume-console [
press page-down
]
editor-event-loop screen, console, e
assume-console [
press ctrl-z
]
run [
editor-event-loop screen, console, e
]
screen-should-contain [
. .
.a .
.b .
.c .
.d .
]
]
scenario editor-can-undo-ctrl-b [
local-scope
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
assume-console [
press page-down
press ctrl-b
]
editor-event-loop screen, console, e
assume-console [
press ctrl-z
]
run [
editor-event-loop screen, console, e
]
screen-should-contain [
. .
.d .
.e .
.f .
.╌╌╌╌╌╌╌╌╌╌.
]
]
scenario editor-can-undo-page-up [
local-scope
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
assume-console [
press page-down
press page-up
]
editor-event-loop screen, console, e
assume-console [
press ctrl-z
]
run [
editor-event-loop screen, console, e
]
screen-should-contain [
. .
.d .
.e .
.f .
.╌╌╌╌╌╌╌╌╌╌.
]
]
scenario editor-can-undo-ctrl-a [
local-scope
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 2, 1
press ctrl-a
]
editor-event-loop screen, console, e
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 <- 2
4 <- 1
]
assume-console [
type [1]
]
run [
editor-event-loop screen, console, e
]
screen-should-contain [
. .
.abc .
.d1ef .
.ghi .
.╌╌╌╌╌╌╌╌╌╌.
]
]
scenario editor-can-undo-home [
local-scope
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 2, 1
press home
]
editor-event-loop screen, console, e
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 <- 2
4 <- 1
]
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
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 2, 1
press ctrl-e
]
editor-event-loop screen, console, e
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 <- 2
4 <- 1
]
assume-console [
type [1]
]
run [
editor-event-loop screen, console, e
]
screen-should-contain [
. .
.abc .
.d1ef .
.ghi .
.╌╌╌╌╌╌╌╌╌╌.
]
]
scenario editor-can-undo-end [
local-scope
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 2, 1
press end
]
editor-event-loop screen, console, e
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 <- 2
4 <- 1
]
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
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 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
]
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 <- 2
4 <- 3
]
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 <- 2
4 <- 1
]
]
scenario editor-redo-touch [
local-scope
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
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 <- 3
4 <- 1
]
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?
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
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
]
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-should-contain [
. .
.abc .
.╌╌╌╌╌╌╌╌╌╌.
. .
]
memory-should-contain [
3 <- 1
4 <- 1
]
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-should-contain [
. .
.abc .
.╌╌╌╌╌╌╌╌╌╌.
. .
]
memory-should-contain [
3 <- 1
4 <- 3
]
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-should-contain [
. .
. .
.╌╌╌╌╌╌╌╌╌╌.
. .
]
memory-should-contain [
3 <- 1
4 <- 0
]
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
]
screen-should-contain [
. .
.abc .
.╌╌╌╌╌╌╌╌╌╌.
. .
]
memory-should-contain [
3 <- 1
4 <- 3
]
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
]
screen-should-contain [
. .
.abc .
.╌╌╌╌╌╌╌╌╌╌.
. .
]
memory-should-contain [
3 <- 1
4 <- 1
]
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
]
screen-should-contain [
. .
.adbc .
.╌╌╌╌╌╌╌╌╌╌.
. .
]
memory-should-contain [
3 <- 1
4 <- 2
]
]
scenario editor-can-undo-and-redo-backspace [
local-scope
assume-screen 10/width, 5/height
e:&:editor <- new-editor [], 0/left, 10/right
editor-render screen, e
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
]
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 .
.╌╌╌╌╌╌╌╌╌╌.
. .
]
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 .
.╌╌╌╌╌╌╌╌╌╌.
. .
]
]
after <backspace-character-begin> [
top-before:&:duplex-list:char <- get *editor, top-of-screen:offset
]
before <backspace-character-end> [
{
break-unless backspaced-cell
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
{
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
insert-range 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
}
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
insert-range anchor, deleted
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
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
}
]
scenario editor-can-undo-and-redo-delete [
local-scope
assume-screen 10/width, 5/height
e:&:editor <- new-editor [], 0/left, 10/right
editor-render screen, e
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
]
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 .
.╌╌╌╌╌╌╌╌╌╌.
. .
]
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 .
.╌╌╌╌╌╌╌╌╌╌.
. .
]
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 .
.╌╌╌╌╌╌╌╌╌╌.
. .
]
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 <- 2
]
screen-should-contain [
. .
.abdef .
.╌╌╌╌╌╌╌╌╌╌.
. .
]
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 [
. .
.adef .
.╌╌╌╌╌╌╌╌╌╌.
. .
]
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 [
. .
.af .
.╌╌╌╌╌╌╌╌╌╌.
. .
]
]
after <delete-character-begin> [
top-before:&:duplex-list:char <- get *editor, top-of-screen:offset
]
before <delete-character-end> [
{
break-unless deleted-cell
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
{
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
}
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
}
]
scenario editor-can-undo-and-redo-ctrl-k [
local-scope
assume-screen 10/width, 5/height
contents:text <- new [abc
def]
e:&:editor <- new-editor contents, 0/left, 10/right
editor-render screen, e
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
]
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
]
assume-console [
press ctrl-y
]
run [
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
]
assume-console [
type [1]
]
run [
editor-event-loop screen, console, e
]
screen-should-contain [
. .
.a1 .
.def .
.╌╌╌╌╌╌╌╌╌╌.
. .
]
]
after <delete-to-end-of-line-begin> [
top-before:&:duplex-list:char <- get *editor, top-of-screen:offset
]
before <delete-to-end-of-line-end> [
{
break-unless deleted-cells
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
}
]
scenario editor-can-undo-and-redo-ctrl-u [
local-scope
assume-screen 10/width, 5/height
contents:text <- new [abc
def]
e:&:editor <- new-editor contents, 0/left, 10/right
editor-render screen, e
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
]
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
]
assume-console [
press ctrl-y
]
run [
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
]
assume-console [
type [1]
]
run [
editor-event-loop screen, console, e
]
screen-should-contain [
. .
.1c .
.def .
.╌╌╌╌╌╌╌╌╌╌.
. .
]
]
after <delete-to-start-of-line-begin> [
top-before:&:duplex-list:char <- get *editor, top-of-screen:offset
]
before <delete-to-start-of-line-end> [
{
break-unless deleted-cells
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
assume-screen 10/width, 5/height
e:&:editor <- new-editor [], 0/left, 10/right
editor-render screen, e
assume-console [
type [abc]
press ctrl-u
press ctrl-z
]
editor-event-loop screen, console, e
screen-should-contain [
. .
.abc .
.╌╌╌╌╌╌╌╌╌╌.
. .
]
]