exclusive-container operation [
typing:insert-operation
move:move-operation
delete:delete-operation
]
container insert-operation [
before-row:number
before-column:number
before-top-of-screen:address:duplex-list:character
after-row:number
after-column:number
after-top-of-screen:address:duplex-list:character
insert-from:address:duplex-list:character
insert-until:address:duplex-list:character
tag:number
]
container move-operation [
before-row:number
before-column:number
before-top-of-screen:address:duplex-list:character
after-row:number
after-column:number
after-top-of-screen:address:duplex-list:character
tag:number
]
container delete-operation [
before-row:number
before-column:number
before-top-of-screen:address:duplex-list:character
after-row:number
after-column:number
after-top-of-screen:address:duplex-list:character
deleted-text:address:duplex-list:character
delete-from:address:duplex-list:character
delete-until:address:duplex-list:character
tag:number
]
container editor-data [
undo:address:list:address:operation
redo:address:list:address:operation
]
after <handle-special-character> [
{
undo?:boolean <- equal *c, 26/ctrl-z
break-unless undo?
undo:address:address:list <- get-address *editor, undo:offset
break-unless *undo
op:address:operation <- first *undo
*undo <- rest *undo
redo:address:address:list <- get-address *editor, redo:offset
*redo <- push op, *redo
<handle-undo>
reply screen/same-as-ingredient:0, editor/same-as-ingredient:1, 1/go-render
}
]
after <handle-special-character> [
{
redo?:boolean <- equal *c, 25/ctrl-y
break-unless redo?
redo:address:address:list <- get-address *editor, redo:offset
break-unless *redo
op:address:operation <- first *redo
*redo <- rest *redo
undo:address:address:list <- get-address *editor, undo:offset
*undo <- push op, *undo
<handle-redo>
reply screen/same-as-ingredient:0, editor/same-as-ingredient:1, 1/go-render
}
]
scenario editor-can-undo-typing [
assume-screen 10/width, 5/height
1:address:array:character <- new []
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 10/right
editor-render screen, 2:address:editor-data
assume-console [
type [0]
]
editor-event-loop screen:address, console:address, 2:address:editor-data
assume-console [
press ctrl-z
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
. .
.┈┈┈┈┈┈┈┈┈┈.
. .
]
assume-console [
type [1]
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
.1 .
.┈┈┈┈┈┈┈┈┈┈.
. .
]
]
after <insert-character-begin> [
top-before:address:duplex-list <- get *editor, top-of-screen:offset
cursor-before:address:duplex-list <- copy *before-cursor
]
before <insert-character-end> [
top-after:address:duplex-list <- get *editor, top-of-screen:offset
undo:address:address:list <- get-address *editor, undo:offset
{
break-unless *undo
op:address:operation <- first *undo
typing:address:insert-operation <- maybe-convert *op, typing:variant
break-unless typing
previous-coalesce-tag:number <- get *typing, tag:offset
break-unless previous-coalesce-tag
insert-until:address:address:duplex-list <- get-address *typing, insert-until:offset
*insert-until <- next-duplex *before-cursor
after-row:address:number <- get-address *typing, after-row:offset
*after-row <- copy *cursor-row
after-column:address:number <- get-address *typing, after-column:offset
*after-column <- copy *cursor-column
after-top:address:number <- get-address *typing, after-top-of-screen:offset
*after-top <- get *editor, top-of-screen:offset
break +done-adding-insert-operation:label
}
insert-from:address:duplex-list <- next-duplex cursor-before
insert-to:address:duplex-list <- next-duplex insert-from
op:address: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:number <- copy *cursor-row
cursor-column-before:number <- copy *cursor-column
top-before:address:duplex-list <- get *editor, top-of-screen:offset
cursor-before:address:duplex-list <- copy *before-cursor
]
before <insert-enter-end> [
top-after:address:duplex-list <- get *editor, top-of-screen:offset
insert-from:address:duplex-list <- next-duplex cursor-before
insert-to:address:duplex-list <- next-duplex *before-cursor
op:address: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
]
recipe add-operation [
local-scope
editor:address:editor-data <- next-ingredient
op:address:operation <- next-ingredient
undo:address:address:list:address:operation <- get-address *editor, undo:offset
*undo <- push op *undo
redo:address:address:list:address:operation <- get-address *editor, redo:offset
*redo <- copy 0
reply editor/same-as-ingredient:0
]
after <handle-undo> [
{
typing:address:insert-operation <- maybe-convert *op, typing:variant
break-unless typing
start:address:duplex-list <- get *typing, insert-from:offset
end:address:duplex-list <- get *typing, insert-until:offset
*before-cursor <- prev-duplex start
remove-duplex-between *before-cursor, end
*cursor-row <- get *typing, before-row:offset
*cursor-column <- get *typing, before-column:offset
top:address:address:duplex-list <- get *editor, top-of-screen:offset
*top <- get *typing, before-top-of-screen:offset
}
]
scenario editor-can-undo-typing-multiple [
assume-screen 10/width, 5/height
1:address:array:character <- new []
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 10/right
editor-render screen, 2:address:editor-data
assume-console [
type [012]
]
editor-event-loop screen:address, console:address, 2:address:editor-data
assume-console [
press ctrl-z
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
. .
.┈┈┈┈┈┈┈┈┈┈.
. .
]
]
scenario editor-can-undo-typing-multiple-2 [
assume-screen 10/width, 5/height
1:address:array:character <- new [a]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 10/right
editor-render screen, 2:address:editor-data
assume-console [
type [012]
]
editor-event-loop screen:address, console:address, 2:address:editor-data
screen-should-contain [
. .
.012a .
.┈┈┈┈┈┈┈┈┈┈.
. .
]
assume-console [
press ctrl-z
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
.a .
.┈┈┈┈┈┈┈┈┈┈.
. .
]
assume-console [
type [3]
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
.3a .
.┈┈┈┈┈┈┈┈┈┈.
. .
]
]
scenario editor-can-undo-typing-enter [
assume-screen 10/width, 5/height
1:address:array:character <- new [ abc]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 10/right
editor-render screen, 2:address:editor-data
assume-console [
left-click 1, 8
press enter
]
editor-event-loop screen:address, console:address, 2:address:editor-data
screen-should-contain [
. .
. abc .
. .
.┈┈┈┈┈┈┈┈┈┈.
. .
]
3:number <- get *2:address:editor-data, cursor-row:offset
4:number <- get *2:address:editor-data, cursor-column:offset
memory-should-contain [
3 <- 2
4 <- 2
]
assume-console [
press ctrl-z
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
3:number <- get *2:address:editor-data, cursor-row:offset
4:number <- get *2:address:editor-data, cursor-column:offset
memory-should-contain [
3 <- 1
4 <- 5
]
screen-should-contain [
. .
. abc .
.┈┈┈┈┈┈┈┈┈┈.
. .
]
assume-console [
type [1]
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
. abc1 .
.┈┈┈┈┈┈┈┈┈┈.
. .
]
]
scenario editor-redo-typing [
assume-screen 10/width, 5/height
1:address:array:character <- new [a]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 10/right
editor-render screen, 2:address:editor-data
assume-console [
type [012]
press ctrl-z
]
editor-event-loop screen:address, console:address, 2:address:editor-data
screen-should-contain [
. .
.a .
.┈┈┈┈┈┈┈┈┈┈.
. .
]
assume-console [
press ctrl-y
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
.012a .
.┈┈┈┈┈┈┈┈┈┈.
. .
]
assume-console [
type [3]
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
.0123a .
.┈┈┈┈┈┈┈┈┈┈.
. .
]
]
after <handle-redo> [
{
typing:address:insert-operation <- maybe-convert *op, typing:variant
break-unless typing
insert-from:address:duplex-list <- get *typing, insert-from:offset
insert-duplex-range *before-cursor, insert-from
*cursor-row <- get *typing, after-row:offset
*cursor-column <- get *typing, after-column:offset
top:address:address:duplex-list <- get *editor, top-of-screen:offset
*top <- get *typing, after-top-of-screen:offset
}
]
scenario editor-redo-typing-empty [
assume-screen 10/width, 5/height
1:address:array:character <- new []
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 10/right
editor-render screen, 2:address:editor-data
assume-console [
type [012]
press ctrl-z
]
editor-event-loop screen:address, console:address, 2:address:editor-data
screen-should-contain [
. .
. .
.┈┈┈┈┈┈┈┈┈┈.
. .
]
assume-console [
press ctrl-y
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
.012 .
.┈┈┈┈┈┈┈┈┈┈.
. .
]
assume-console [
type [3]
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
.0123 .
.┈┈┈┈┈┈┈┈┈┈.
. .
]
]
scenario editor-work-clears-redo-stack [
assume-screen 10/width, 5/height
1:address:array:character <- new [abc
def
ghi]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 10/right
editor-render screen, 2:address:editor-data
assume-console [
type [1]
press ctrl-z
]
editor-event-loop screen:address, console:address, 2:address:editor-data
assume-console [
type [0]
]
editor-event-loop screen:address, console:address, 2:address:editor-data
screen-should-contain [
. .
.0abc .
.def .
.ghi .
.┈┈┈┈┈┈┈┈┈┈.
]
assume-console [
press ctrl-y
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
.0abc .
.def .
.ghi .
.┈┈┈┈┈┈┈┈┈┈.
]
]
scenario editor-can-redo-typing-and-enter-and-tab [
assume-screen 10/width, 5/height
1:address:array:character <- new []
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 10/right
editor-render screen, 2:address:editor-data
assume-console [
press tab
type [ab]
press tab
type [cd]
press enter
press tab
type [efg]
]
editor-event-loop screen:address, console:address, 2:address:editor-data
screen-should-contain [
. .
. ab cd .
. efg .
.┈┈┈┈┈┈┈┈┈┈.
. .
]
3:number <- get *2:address:editor-data, cursor-row:offset
4:number <- get *2:address:editor-data, cursor-column:offset
memory-should-contain [
3 <- 2
4 <- 7
]
assume-console [
press ctrl-z
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
3:number <- get *2:address:editor-data, cursor-row:offset
4:number <- get *2:address:editor-data, 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:address, console:address, 2:address:editor-data
]
3:number <- get *2:address:editor-data, cursor-row:offset
4:number <- get *2:address:editor-data, 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:address, console:address, 2:address:editor-data
]
3:number <- get *2:address:editor-data, cursor-row:offset
4:number <- get *2:address:editor-data, cursor-column:offset
memory-should-contain [
3 <- 1
4 <- 0
]
screen-should-contain [
. .
. .
.┈┈┈┈┈┈┈┈┈┈.
. .
]
assume-console [
press ctrl-y
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
3:number <- get *2:address:editor-data, cursor-row:offset
4:number <- get *2:address:editor-data, 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:address, console:address, 2:address:editor-data
]
3:number <- get *2:address:editor-data, cursor-row:offset
4:number <- get *2:address:editor-data, 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:address, console:address, 2:address:editor-data
]
3:number <- get *2:address:editor-data, cursor-row:offset
4:number <- get *2:address:editor-data, cursor-column:offset
memory-should-contain [
3 <- 2
4 <- 7
]
screen-should-contain [
. .
. ab cd .
. efg .
.┈┈┈┈┈┈┈┈┈┈.
. .
]
]
scenario editor-can-undo-touch [
assume-screen 10/width, 5/height
1:address:array:character <- new [abc
def
ghi]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 10/right
editor-render screen, 2:address:editor-data
assume-console [
left-click 3, 1
]
editor-event-loop screen:address, console:address, 2:address:editor-data
assume-console [
press ctrl-z
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
3:number <- get *2:address:editor-data, cursor-row:offset
4:number <- get *2:address:editor-data, cursor-column:offset
]
memory-should-contain [
3 <- 1
4 <- 0
]
assume-console [
type [1]
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
.1abc .
.def .
.ghi .
.┈┈┈┈┈┈┈┈┈┈.
]
]
after <move-cursor-begin> [
before-cursor-row:number <- get *editor, cursor-row:offset
before-cursor-column:number <- get *editor, cursor-column:offset
before-top-of-screen:address:duplex-list <- get *editor, top-of-screen:offset
]
before <move-cursor-end> [
after-cursor-row:number <- get *editor, cursor-row:offset
after-cursor-column:number <- get *editor, cursor-column:offset
after-top-of-screen:address:duplex-list <- get *editor, top-of-screen:offset
{
break-unless undo-coalesce-tag
undo:address:address:list <- get-address *editor, undo:offset
break-unless *undo
op:address:operation <- first *undo
move:address:move-operation <- maybe-convert *op, move:variant
break-unless move
previous-coalesce-tag:number <- get *move, tag:offset
coalesce?:boolean <- equal undo-coalesce-tag, previous-coalesce-tag
break-unless coalesce?
after-row:address:number <- get-address *move, after-row:offset
*after-row <- copy after-cursor-row
after-column:address:number <- get-address *move, after-column:offset
*after-column <- copy after-cursor-column
after-top:address:number <- get-address *move, after-top-of-screen:offset
*after-top <- get *editor, top-of-screen:offset
break +done-adding-move-operation:label
}
op:address:operation <- new operation:type
*op <- merge 1/move-operation, before-cursor-row, before-cursor-column, before-top-of-screen, after-cursor-row, after-cursor-column, after-top-of-screen, undo-coalesce-tag
editor <- add-operation editor, op
+done-adding-move-operation
]
after <handle-undo> [
{
move:address:move-operation <- maybe-convert *op, move:variant
break-unless move
top:address:address:duplex-list <- get-address *editor, top-of-screen:offset
*cursor-row <- get *move, before-row:offset
*cursor-column <- get *move, before-column:offset
*top <- get *move, before-top-of-screen:offset
}
]
scenario editor-can-undo-scroll [
assume-screen 5/width, 4/height
1:address:array:character <- new [a
b
cdefgh]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 5/right
assume-console [
left-click 3, 3
press right-arrow
]
editor-event-loop screen:address, console:address, 2:address:editor-data
3:number <- get *2:address:editor-data, cursor-row:offset
4:number <- get *2:address:editor-data, 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:address, console:address, 2:address:editor-data
3:number <- get *2:address:editor-data, cursor-row:offset
4:number <- get *2:address:editor-data, 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:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
.b .
.cde1↩.
.fgh .
]
]
scenario editor-can-undo-left-arrow [
assume-screen 10/width, 5/height
1:address:array:character <- new [abc
def
ghi]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 10/right
editor-render screen, 2:address:editor-data
assume-console [
left-click 3, 1
press left-arrow
]
editor-event-loop screen:address, console:address, 2:address:editor-data
assume-console [
press ctrl-z
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
3:number <- get *2:address:editor-data, cursor-row:offset
4:number <- get *2:address:editor-data, cursor-column:offset
]
memory-should-contain [
3 <- 3
4 <- 1
]
assume-console [
type [1]
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
.abc .
.def .
.g1hi .
.┈┈┈┈┈┈┈┈┈┈.
]
]
scenario editor-can-undo-up-arrow [
assume-screen 10/width, 5/height
1:address:array:character <- new [abc
def
ghi]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 10/right
editor-render screen, 2:address:editor-data
assume-console [
left-click 3, 1
press up-arrow
]
editor-event-loop screen:address, console:address, 2:address:editor-data
3:number <- get *2:address:editor-data, cursor-row:offset
4:number <- get *2:address:editor-data, cursor-column:offset
memory-should-contain [
3 <- 2
4 <- 1
]
assume-console [
press ctrl-z
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
3:number <- get *2:address:editor-data, cursor-row:offset
4:number <- get *2:address:editor-data, cursor-column:offset
]
memory-should-contain [
3 <- 3
4 <- 1
]
assume-console [
type [1]
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
.abc .
.def .
.g1hi .
.┈┈┈┈┈┈┈┈┈┈.
]
]
scenario editor-can-undo-down-arrow [
assume-screen 10/width, 5/height
1:address:array:character <- new [abc
def
ghi]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 10/right
editor-render screen, 2:address:editor-data
assume-console [
left-click 2, 1
press down-arrow
]
editor-event-loop screen:address, console:address, 2:address:editor-data
assume-console [
press ctrl-z
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
3:number <- get *2:address:editor-data, cursor-row:offset
4:number <- get *2:address:editor-data, cursor-column:offset
]
memory-should-contain [
3 <- 2
4 <- 1
]
assume-console [
type [1]
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
.abc .
.d1ef .
.ghi .
.┈┈┈┈┈┈┈┈┈┈.
]
]
scenario editor-can-undo-ctrl-f [
assume-screen 10/width, 5/height
1:address:array:character <- new [a
b
c
d
e
f]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 10/right
editor-render screen, 2:address:editor-data
assume-console [
press ctrl-f
]
editor-event-loop screen:address, console:address, 2:address:editor-data
assume-console [
press ctrl-z
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
3:number <- get *2:address:editor-data, cursor-row:offset
4:number <- get *2:address:editor-data, cursor-column:offset
]
screen-should-contain [
. .
.a .
.b .
.c .
.d .
]
]
scenario editor-can-undo-page-down [
assume-screen 10/width, 5/height
1:address:array:character <- new [a
b
c
d
e
f]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 10/right
editor-render screen, 2:address:editor-data
assume-console [
press page-down
]
editor-event-loop screen:address, console:address, 2:address:editor-data
assume-console [
press ctrl-z
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
3:number <- get *2:address:editor-data, cursor-row:offset
4:number <- get *2:address:editor-data, cursor-column:offset
]
screen-should-contain [
. .
.a .
.b .
.c .
.d .
]
]
scenario editor-can-undo-ctrl-b [
assume-screen 10/width, 5/height
1:address:array:character <- new [a
b
c
d
e
f]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 10/right
editor-render screen, 2:address:editor-data
assume-console [
press page-down
press ctrl-b
]
editor-event-loop screen:address, console:address, 2:address:editor-data
assume-console [
press ctrl-z
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
3:number <- get *2:address:editor-data, cursor-row:offset
4:number <- get *2:address:editor-data, cursor-column:offset
]
screen-should-contain [
. .
.d .
.e .
.f .
.┈┈┈┈┈┈┈┈┈┈.
]
]
scenario editor-can-undo-page-up [
assume-screen 10/width, 5/height
1:address:array:character <- new [a
b
c
d
e
f]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 10/right
editor-render screen, 2:address:editor-data
assume-console [
press page-down
press page-up
]
editor-event-loop screen:address, console:address, 2:address:editor-data
assume-console [
press ctrl-z
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
3:number <- get *2:address:editor-data, cursor-row:offset
4:number <- get *2:address:editor-data, cursor-column:offset
]
screen-should-contain [
. .
.d .
.e .
.f .
.┈┈┈┈┈┈┈┈┈┈.
]
]
scenario editor-can-undo-ctrl-a [
assume-screen 10/width, 5/height
1:address:array:character <- new [abc
def
ghi]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 10/right
editor-render screen, 2:address:editor-data
assume-console [
left-click 2, 1
press ctrl-a
]
editor-event-loop screen:address, console:address, 2:address:editor-data
assume-console [
press ctrl-z
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
3:number <- get *2:address:editor-data, cursor-row:offset
4:number <- get *2:address:editor-data, cursor-column:offset
]
memory-should-contain [
3 <- 2
4 <- 1
]
assume-console [
type [1]
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
.abc .
.d1ef .
.ghi .
.┈┈┈┈┈┈┈┈┈┈.
]
]
scenario editor-can-undo-home [
assume-screen 10/width, 5/height
1:address:array:character <- new [abc
def
ghi]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 10/right
editor-render screen, 2:address:editor-data
assume-console [
left-click 2, 1
press home
]
editor-event-loop screen:address, console:address, 2:address:editor-data
assume-console [
press ctrl-z
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
3:number <- get *2:address:editor-data, cursor-row:offset
4:number <- get *2:address:editor-data, cursor-column:offset
]
memory-should-contain [
3 <- 2
4 <- 1
]
assume-console [
type [1]
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
.abc .
.d1ef .
.ghi .
.┈┈┈┈┈┈┈┈┈┈.
]
]
scenario editor-can-undo-ctrl-e [
assume-screen 10/width, 5/height
1:address:array:character <- new [abc
def
ghi]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 10/right
editor-render screen, 2:address:editor-data
assume-console [
left-click 2, 1
press ctrl-e
]
editor-event-loop screen:address, console:address, 2:address:editor-data
assume-console [
press ctrl-z
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
3:number <- get *2:address:editor-data, cursor-row:offset
4:number <- get *2:address:editor-data, cursor-column:offset
]
memory-should-contain [
3 <- 2
4 <- 1
]
assume-console [
type [1]
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
.abc .
.d1ef .
.ghi .
.┈┈┈┈┈┈┈┈┈┈.
]
]
scenario editor-can-undo-end [
assume-screen 10/width, 5/height
1:address:array:character <- new [abc
def
ghi]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 10/right
editor-render screen, 2:address:editor-data
assume-console [
left-click 2, 1
press end
]
editor-event-loop screen:address, console:address, 2:address:editor-data
assume-console [
press ctrl-z
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
3:number <- get *2:address:editor-data, cursor-row:offset
4:number <- get *2:address:editor-data, cursor-column:offset
]
memory-should-contain [
3 <- 2
4 <- 1
]
assume-console [
type [1]
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
.abc .
.d1ef .
.ghi .
.┈┈┈┈┈┈┈┈┈┈.
]
]
scenario editor-separates-undo-insert-from-undo-cursor-move [
assume-screen 10/width, 5/height
1:address:array:character <- new []
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 10/right
editor-render screen, 2:address:editor-data
assume-console [
type [abc]
left-click 1, 1
type [d]
]
editor-event-loop screen:address, console:address, 2:address:editor-data
3:number <- get *2:address:editor-data, cursor-row:offset
4:number <- get *2:address:editor-data, cursor-column:offset
screen-should-contain [
. .
.adbc .
.┈┈┈┈┈┈┈┈┈┈.
. .
]
memory-should-contain [
3 <- 1
4 <- 2
]
assume-console [
press ctrl-z
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
3:number <- get *2:address:editor-data, cursor-row:offset
4:number <- get *2:address:editor-data, cursor-column:offset
]
screen-should-contain [
. .
.abc .
.┈┈┈┈┈┈┈┈┈┈.
. .
]
memory-should-contain [
3 <- 1
4 <- 1
]
assume-console [
press ctrl-z
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
3:number <- get *2:address:editor-data, cursor-row:offset
4:number <- get *2:address:editor-data, cursor-column:offset
]
screen-should-contain [
. .
.abc .
.┈┈┈┈┈┈┈┈┈┈.
. .
]
memory-should-contain [
3 <- 1
4 <- 3
]
assume-console [
press ctrl-z
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
3:number <- get *2:address:editor-data, cursor-row:offset
4:number <- get *2:address:editor-data, cursor-column:offset
]
screen-should-contain [
. .
. .
.┈┈┈┈┈┈┈┈┈┈.
. .
]
memory-should-contain [
3 <- 1
4 <- 0
]
assume-console [
press ctrl-y
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
3:number <- get *2:address:editor-data, cursor-row:offset
4:number <- get *2:address:editor-data, cursor-column:offset
]
screen-should-contain [
. .
.abc .
.┈┈┈┈┈┈┈┈┈┈.
. .
]
memory-should-contain [
3 <- 1
4 <- 3
]
assume-console [
press ctrl-y
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
3:number <- get *2:address:editor-data, cursor-row:offset
4:number <- get *2:address:editor-data, cursor-column:offset
]
screen-should-contain [
. .
.abc .
.┈┈┈┈┈┈┈┈┈┈.
. .
]
memory-should-contain [
3 <- 1
4 <- 1
]
assume-console [
press ctrl-y
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
3:number <- get *2:address:editor-data, cursor-row:offset
4:number <- get *2:address:editor-data, cursor-column:offset
]
screen-should-contain [
. .
.adbc .
.┈┈┈┈┈┈┈┈┈┈.
. .
]
memory-should-contain [
3 <- 1
4 <- 2
]
]
scenario editor-can-undo-multiple-arrows-in-the-same-direction [
assume-screen 10/width, 5/height
1:address:array:character <- new [abc
def
ghi]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 10/right
editor-render screen, 2:address:editor-data
assume-console [
left-click 2, 1
press right-arrow
press right-arrow
press up-arrow
]
editor-event-loop screen:address, console:address, 2:address:editor-data
3:number <- get *2:address:editor-data, cursor-row:offset
4:number <- get *2:address:editor-data, cursor-column:offset
memory-should-contain [
3 <- 1
4 <- 3
]
assume-console [
press ctrl-z
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
3:number <- get *2:address:editor-data, cursor-row:offset
4:number <- get *2:address:editor-data, cursor-column:offset
]
memory-should-contain [
3 <- 2
4 <- 3
]
assume-console [
press ctrl-z
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
3:number <- get *2:address:editor-data, cursor-row:offset
4:number <- get *2:address:editor-data, cursor-column:offset
]
memory-should-contain [
3 <- 2
4 <- 1
]
]
scenario editor-redo-touch [
assume-screen 10/width, 5/height
1:address:array:character <- new [abc
def
ghi]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 10/right
editor-render screen, 2:address:editor-data
assume-console [
left-click 3, 1
press ctrl-z
]
editor-event-loop screen:address, console:address, 2:address:editor-data
assume-console [
press ctrl-y
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
3:number <- get *2:address:editor-data, cursor-row:offset
4:number <- get *2:address:editor-data, cursor-column:offset
]
memory-should-contain [
3 <- 3
4 <- 1
]
assume-console [
type [1]
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
.abc .
.def .
.g1hi .
.┈┈┈┈┈┈┈┈┈┈.
]
]
after <handle-redo> [
{
move:address:move-operation <- maybe-convert *op, move:variant
break-unless move
*cursor-row <- get *move, after-row:offset
*cursor-column <- get *move, after-column:offset
top:address:address:duplex-list <- get *editor, top-of-screen:offset
*top <- get *move, after-top-of-screen:offset
}
]
scenario editor-can-undo-and-redo-backspace [
assume-screen 10/width, 5/height
1:address:array:character <- new []
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 10/right
editor-render screen, 2:address:editor-data
assume-console [
type [abc]
press backspace
press backspace
]
editor-event-loop screen:address, console:address, 2:address:editor-data
screen-should-contain [
. .
.a .
.┈┈┈┈┈┈┈┈┈┈.
. .
]
3:number <- get *2:address:editor-data, cursor-row:offset
4:number <- get *2:address:editor-data, cursor-column:offset
memory-should-contain [
3 <- 1
4 <- 1
]
assume-console [
press ctrl-z
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
3:number <- get *2:address:editor-data, cursor-row:offset
4:number <- get *2:address:editor-data, cursor-column:offset
memory-should-contain [
3 <- 1
4 <- 3
]
screen-should-contain [
. .
.abc .
.┈┈┈┈┈┈┈┈┈┈.
. .
]
assume-console [
press ctrl-y
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
3:number <- get *2:address:editor-data, cursor-row:offset
4:number <- get *2:address:editor-data, cursor-column:offset
memory-should-contain [
3 <- 1
4 <- 1
]
screen-should-contain [
. .
.a .
.┈┈┈┈┈┈┈┈┈┈.
. .
]
]
after <backspace-character-begin> [
top-before:address:duplex-list <- get *editor, top-of-screen:offset
]
before <backspace-character-end> [
{
break-unless backspaced-cell
top-after:address:duplex-list <- get *editor, top-of-screen:offset
undo:address:address:list <- get-address *editor, undo:offset
{
break-unless *undo
op:address:operation <- first *undo
deletion:address:delete-operation <- maybe-convert *op, delete:variant
break-unless deletion
previous-coalesce-tag:number <- get *deletion, tag:offset
coalesce?:boolean <- equal previous-coalesce-tag, 1/coalesce-backspace
break-unless coalesce?
delete-from:address:address:duplex-list <- get-address *deletion, delete-from:offset
*delete-from <- copy *before-cursor
backspaced-so-far:address:address:duplex-list <- get-address *deletion, deleted-text:offset
insert-duplex-range backspaced-cell, *backspaced-so-far
*backspaced-so-far <- copy backspaced-cell
after-row:address:number <- get-address *deletion, after-row:offset
*after-row <- copy *cursor-row
after-column:address:number <- get-address *deletion, after-column:offset
*after-column <- copy *cursor-column
after-top:address:number <- get-address *deletion, after-top-of-screen:offset
*after-top <- get *editor, top-of-screen:offset
break +done-adding-backspace-operation:label
}
op:address:operation <- new operation:type
deleted-until:address:duplex-list <- next-duplex *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:address:delete-operation <- maybe-convert *op, delete:variant
break-unless deletion
start2:address:address:duplex-list <- get-address *editor, data:offset
anchor:address:duplex-list <- get *deletion, delete-from:offset
break-unless anchor
deleted:address:duplex-list <- get *deletion, deleted-text:offset
old-cursor:address:duplex-list <- last-duplex deleted
insert-duplex-range anchor, deleted
*before-cursor <- copy old-cursor
*cursor-row <- get *deletion, before-row:offset
*cursor-column <- get *deletion, before-column:offset
top:address:address:duplex-list <- get *editor, top-of-screen:offset
*top <- get *deletion, before-top-of-screen:offset
}
]
after <handle-redo> [
{
deletion:address:delete-operation <- maybe-convert *op, delete:variant
break-unless deletion
start:address:duplex-list <- get *deletion, delete-from:offset
end:address:duplex-list <- get *deletion, delete-until:offset
remove-duplex-between start, end
*cursor-row <- get *deletion, after-row:offset
*cursor-column <- get *deletion, after-column:offset
top:address:address:duplex-list <- get *editor, top-of-screen:offset
*top <- get *deletion, after-top-of-screen:offset
}
]
scenario editor-can-undo-and-redo-delete [
assume-screen 10/width, 5/height
1:address:array:character <- new []
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 10/right
editor-render screen, 2:address:editor-data
assume-console [
type [abcdef]
left-click 1, 2
press delete
press backspace
press delete
press delete
]
editor-event-loop screen:address, console:address, 2:address:editor-data
screen-should-contain [
. .
.af .
.┈┈┈┈┈┈┈┈┈┈.
. .
]
3:number <- get *2:address:editor-data, cursor-row:offset
4:number <- get *2:address:editor-data, cursor-column:offset
memory-should-contain [
3 <- 1
4 <- 1
]
assume-console [
press ctrl-z
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
3:number <- get *2:address:editor-data, cursor-row:offset
4:number <- get *2:address:editor-data, cursor-column:offset
memory-should-contain [
3 <- 1
4 <- 1
]
screen-should-contain [
. .
.adef .
.┈┈┈┈┈┈┈┈┈┈.
. .
]
assume-console [
press ctrl-z
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
3:number <- get *2:address:editor-data, cursor-row:offset
4:number <- get *2:address:editor-data, cursor-column:offset
memory-should-contain [
3 <- 1
4 <- 2
]
screen-should-contain [
. .
.abdef .
.┈┈┈┈┈┈┈┈┈┈.
. .
]
assume-console [
press ctrl-z
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
3:number <- get *2:address:editor-data, cursor-row:offset
4:number <- get *2:address:editor-data, cursor-column:offset
memory-should-contain [
3 <- 1
4 <- 2
]
screen-should-contain [
. .
.abcdef .
.┈┈┈┈┈┈┈┈┈┈.
. .
]
assume-console [
press ctrl-y
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
3:number <- get *2:address:editor-data, cursor-row:offset
4:number <- get *2:address:editor-data, cursor-column:offset
memory-should-contain [
3 <- 1
4 <- 2
]
screen-should-contain [
. .
.abdef .
.┈┈┈┈┈┈┈┈┈┈.
. .
]
assume-console [
press ctrl-y
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
3:number <- get *2:address:editor-data, cursor-row:offset
4:number <- get *2:address:editor-data, cursor-column:offset
memory-should-contain [
3 <- 1
4 <- 1
]
screen-should-contain [
. .
.adef .
.┈┈┈┈┈┈┈┈┈┈.
. .
]
assume-console [
press ctrl-y
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
3:number <- get *2:address:editor-data, cursor-row:offset
4:number <- get *2:address:editor-data, cursor-column:offset
memory-should-contain [
3 <- 1
4 <- 1
]
screen-should-contain [
. .
.af .
.┈┈┈┈┈┈┈┈┈┈.
. .
]
]
after <delete-character-begin> [
top-before:address:duplex-list <- get *editor, top-of-screen:offset
]
before <delete-character-end> [
{
break-unless deleted-cell
top-after:address:duplex-list <- get *editor, top-of-screen:offset
undo:address:address:list <- get-address *editor, undo:offset
{
break-unless *undo
op:address:operation <- first *undo
deletion:address:delete-operation <- maybe-convert *op, delete:variant
break-unless deletion
previous-coalesce-tag:number <- get *deletion, tag:offset
coalesce?:boolean <- equal previous-coalesce-tag, 2/coalesce-delete
break-unless coalesce?
delete-until:address:address:duplex-list <- get-address *deletion, delete-until:offset
*delete-until <- next-duplex *before-cursor
deleted-so-far:address:address:duplex-list <- get-address *deletion, deleted-text:offset
*deleted-so-far <- append-duplex *deleted-so-far, deleted-cell
after-row:address:number <- get-address *deletion, after-row:offset
*after-row <- copy *cursor-row
after-column:address:number <- get-address *deletion, after-column:offset
*after-column <- copy *cursor-column
after-top:address:number <- get-address *deletion, after-top-of-screen:offset
*after-top <- get *editor, top-of-screen:offset
break +done-adding-delete-operation:label
}
op:address:operation <- new operation:type
deleted-until:address:duplex-list <- next-duplex *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 [
assume-screen 10/width, 5/height
1:address:array:character <- new [abc
def]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 10/right
editor-render screen, 2:address:editor-data
assume-console [
left-click 1, 1
press ctrl-k
]
editor-event-loop screen:address, console:address, 2:address:editor-data
screen-should-contain [
. .
.a .
.def .
.┈┈┈┈┈┈┈┈┈┈.
. .
]
3:number <- get *2:address:editor-data, cursor-row:offset
4:number <- get *2:address:editor-data, cursor-column:offset
memory-should-contain [
3 <- 1
4 <- 1
]
assume-console [
press ctrl-z
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
.abc .
.def .
.┈┈┈┈┈┈┈┈┈┈.
. .
]
3:number <- get *2:address:editor-data, cursor-row:offset
4:number <- get *2:address:editor-data, cursor-column:offset
memory-should-contain [
3 <- 1
4 <- 1
]
assume-console [
press ctrl-y
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
.a .
.def .
.┈┈┈┈┈┈┈┈┈┈.
. .
]
3:number <- get *2:address:editor-data, cursor-row:offset
4:number <- get *2:address:editor-data, cursor-column:offset
memory-should-contain [
3 <- 1
4 <- 1
]
assume-console [
type [1]
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
.a1 .
.def .
.┈┈┈┈┈┈┈┈┈┈.
. .
]
]
after <delete-to-end-of-line-begin> [
top-before:address:duplex-list <- get *editor, top-of-screen:offset
]
before <delete-to-end-of-line-end> [
{
break-unless deleted-cells
top-after:address:duplex-list <- get *editor, top-of-screen:offset
undo:address:address:list <- get-address *editor, undo:offset
op:address:operation <- new operation:type
deleted-until:address:duplex-list <- next-duplex *before-cursor
*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 [
assume-screen 10/width, 5/height
1:address:array:character <- new [abc
def]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 10/right
editor-render screen, 2:address:editor-data
assume-console [
left-click 1, 2
press ctrl-u
]
editor-event-loop screen:address, console:address, 2:address:editor-data
screen-should-contain [
. .
.c .
.def .
.┈┈┈┈┈┈┈┈┈┈.
. .
]
3:number <- get *2:address:editor-data, cursor-row:offset
4:number <- get *2:address:editor-data, cursor-column:offset
memory-should-contain [
3 <- 1
4 <- 0
]
assume-console [
press ctrl-z
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
.abc .
.def .
.┈┈┈┈┈┈┈┈┈┈.
. .
]
3:number <- get *2:address:editor-data, cursor-row:offset
4:number <- get *2:address:editor-data, cursor-column:offset
memory-should-contain [
3 <- 1
4 <- 2
]
assume-console [
press ctrl-y
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
.c .
.def .
.┈┈┈┈┈┈┈┈┈┈.
. .
]
3:number <- get *2:address:editor-data, cursor-row:offset
4:number <- get *2:address:editor-data, cursor-column:offset
memory-should-contain [
3 <- 1
4 <- 0
]
assume-console [
type [1]
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
.1c .
.def .
.┈┈┈┈┈┈┈┈┈┈.
. .
]
]
after <delete-to-start-of-line-begin> [
top-before:address:duplex-list <- get *editor, top-of-screen:offset
]
before <delete-to-start-of-line-end> [
{
break-unless deleted-cells
top-after:address:duplex-list <- get *editor, top-of-screen:offset
undo:address:address:list <- get-address *editor, undo:offset
op:address:operation <- new operation:type
deleted-until:address:duplex-list <- next-duplex *before-cursor
*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
}
]