recipe main [
local-scope
open-console
initial-recipe:address:array:character <- restore [recipes.mu]
initial-sandbox:address:array:character <- new []
env:address:programming-environment-data <- new-programming-environment 0/screen, initial-recipe, initial-sandbox
env <- restore-sandboxes env
render-all 0/screen, env
show-screen 0/screen
event-loop 0/screen, 0/console, env
]
scenario editor-initially-prints-string-to-screen [
assume-screen 10/width, 5/height
run [
1:address:array:character <- new [abc]
new-editor 1:address:array:character, screen:address, 0/left, 10/right
]
screen-should-contain [
. .
.abc .
. .
]
]
container editor-data [
data:address:duplex-list
top-of-screen:address:duplex-list
bottom-of-screen:address:duplex-list
before-cursor:address:duplex-list
left:number
right:number
cursor-row:number
cursor-column:number
]
recipe new-editor [
local-scope
s:address:array:character <- next-ingredient
screen:address <- next-ingredient
left:number <- next-ingredient
right:number <- next-ingredient
right <- subtract right, 1
result:address:editor-data <- new editor-data:type
x:address:number <- get-address *result, left:offset
*x <- copy left
x <- get-address *result, right:offset
*x <- copy right
x <- get-address *result, cursor-row:offset
*x <- copy 1/top
x <- get-address *result, cursor-column:offset
*x <- copy left
init:address:address:duplex-list <- get-address *result, data:offset
*init <- push-duplex 167/§, 0/tail
top-of-screen:address:address:duplex-list <- get-address *result, top-of-screen:offset
*top-of-screen <- copy *init
y:address:address:duplex-list <- get-address *result, before-cursor:offset
*y <- copy *init
result <- insert-text result, s
y <- get-address *result, before-cursor:offset
*y <- copy *init
_, screen <- render screen, result
reply result
]
recipe insert-text [
local-scope
editor:address:editor-data <- next-ingredient
text:address:array:character <- next-ingredient
reply-unless text, editor/same-as-ingredient:0
len:number <- length *text
reply-unless len, editor/same-as-ingredient:0
idx:number <- copy 0
curr:address:duplex-list <- get *editor, data:offset
{
done?:boolean <- greater-or-equal idx, len
break-if done?
c:character <- index *text, idx
insert-duplex c, curr
curr <- next-duplex curr
idx <- add idx, 1
loop
}
reply editor/same-as-ingredient:0
]
scenario editor-initializes-without-data [
assume-screen 5/width, 3/height
run [
1:address:editor-data <- new-editor 0/data, screen:address, 2/left, 5/right
2:editor-data <- copy *1:address:editor-data
]
memory-should-contain [
4 <- 0
6 <- 2
7 <- 4
8 <- 1
9 <- 2
]
screen-should-contain [
. .
. .
. .
]
]
recipe render [
local-scope
screen:address <- next-ingredient
editor:address:editor-data <- next-ingredient
reply-unless editor, 1/top, screen/same-as-ingredient:0
left:number <- get *editor, left:offset
screen-height:number <- screen-height screen
right:number <- get *editor, right:offset
hide-screen screen
curr:address:duplex-list <- get *editor, top-of-screen:offset
prev:address:duplex-list <- copy curr
curr <- next-duplex curr
+render-loop-initialization
color:number <- copy 7/white
row:number <- copy 1/top
column:number <- copy left
cursor-row:address:number <- get-address *editor, cursor-row:offset
cursor-column:address:number <- get-address *editor, cursor-column:offset
before-cursor:address:address:duplex-list <- get-address *editor, before-cursor:offset
move-cursor screen, row, column
{
+next-character
break-unless curr
off-screen?:boolean <- greater-or-equal row, screen-height
break-if off-screen?
{
at-cursor-row?:boolean <- equal row, *cursor-row
break-unless at-cursor-row?
at-cursor?:boolean <- equal column, *cursor-column
break-unless at-cursor?
*before-cursor <- prev-duplex curr
}
c:character <- get *curr, value:offset
+character-c-recived
{
newline?:boolean <- equal c, 10/newline
break-unless newline?
{
at-cursor-row?:boolean <- equal row, *cursor-row
break-unless at-cursor-row?
left-of-cursor?:boolean <- lesser-than column, *cursor-column
break-unless left-of-cursor?
*cursor-column <- copy column
*before-cursor <- prev-duplex curr
}
clear-line-delimited screen, column, right
row <- add row, 1
column <- copy left
move-cursor screen, row, column
curr <- next-duplex curr
prev <- next-duplex prev
loop +next-character:label
}
{
at-right?:boolean <- equal column, right
break-unless at-right?
print-character screen, 8617/loop-back-to-left, 245/grey
column <- copy left
row <- add row, 1
move-cursor screen, row, column
loop +next-character:label
}
print-character screen, c, color
curr <- next-duplex curr
prev <- next-duplex prev
column <- add column, 1
loop
}
bottom-of-screen:address:address:duplex-list <- get-address *editor, bottom-of-screen:offset
*bottom-of-screen <- copy curr
{
at-cursor-row?:boolean <- equal row, *cursor-row
cursor-outside-line?:boolean <- lesser-or-equal column, *cursor-column
before-cursor-on-same-line?:boolean <- and at-cursor-row?, cursor-outside-line?
above-cursor-row?:boolean <- lesser-than row, *cursor-row
before-cursor?:boolean <- or before-cursor-on-same-line?, above-cursor-row?
break-unless before-cursor?
*cursor-row <- copy row
*cursor-column <- copy column
{
too-far-right?:boolean <- greater-than *cursor-column, right
break-unless too-far-right?
*cursor-column <- copy left
*cursor-row <- add *cursor-row, 1
above-screen-bottom?:boolean <- lesser-than *cursor-row, screen-height
assert above-screen-bottom?, [unimplemented: wrapping cursor past bottom of screen]
}
*before-cursor <- copy prev
}
clear-line-delimited screen, column, right
clear-rest-of-screen screen, row, left, right
reply row, screen/same-as-ingredient:0
]
recipe render-string [
local-scope
screen:address <- next-ingredient
s:address:array:character <- next-ingredient
left:number <- next-ingredient
right:number <- next-ingredient
color:number <- next-ingredient
row:number <- next-ingredient
row <- add row, 1
reply-unless s, row/same-as-ingredient:5, screen/same-as-ingredient:0
column:number <- copy left
move-cursor screen, row, column
screen-height:number <- screen-height screen
i:number <- copy 0
len:number <- length *s
{
+next-character
done?:boolean <- greater-or-equal i, len
break-if done?
done? <- greater-or-equal row, screen-height
break-if done?
c:character <- index *s, i
{
at-right?:boolean <- equal column, right
break-unless at-right?
print-character screen, 8617/loop-back-to-left, 245/grey
column <- copy left
row <- add row, 1
move-cursor screen, row, column
loop +next-character:label
}
i <- add i, 1
{
newline?:boolean <- equal c, 10/newline
break-unless newline?
{
done?:boolean <- greater-than column, right
break-if done?
print-character screen, 32/space
column <- add column, 1
loop
}
row <- add row, 1
column <- copy left
move-cursor screen, row, column
loop +next-character:label
}
print-character screen, c, color
column <- add column, 1
loop
}
{
line-done?:boolean <- greater-than column, right
break-if line-done?
print-character screen, 32/space
column <- add column, 1
loop
}
reply row/same-as-ingredient:5, screen/same-as-ingredient:0
]
recipe clear-line-delimited [
local-scope
screen:address <- next-ingredient
left:number <- next-ingredient
right:number <- next-ingredient
column:number <- copy left
{
done?:boolean <- greater-than column, right
break-if done?
print-character screen, 32/space
column <- add column, 1
loop
}
]
scenario editor-initially-prints-multiple-lines [
assume-screen 5/width, 5/height
run [
s:address:array:character <- new [abc
def]
new-editor s:address:array:character, screen:address, 0/left, 5/right
]
screen-should-contain [
. .
.abc .
.def .
. .
]
]
scenario editor-initially-handles-offsets [
assume-screen 5/width, 5/height
run [
s:address:array:character <- new [abc]
new-editor s:address:array:character, screen:address, 1/left, 5/right
]
screen-should-contain [
. .
. abc .
. .
]
]
scenario editor-initially-prints-multiple-lines-at-offset [
assume-screen 5/width, 5/height
run [
s:address:array:character <- new [abc
def]
new-editor s:address:array:character, screen:address, 1/left, 5/right
]
screen-should-contain [
. .
. abc .
. def .
. .
]
]
scenario editor-initially-wraps-long-lines [
assume-screen 5/width, 5/height
run [
s:address:array:character <- new [abc def]
new-editor s:address:array:character, screen:address, 0/left, 5/right
]
screen-should-contain [
. .
.abc ↩.
.def .
. .
]
screen-should-contain-in-color 245/grey [
. .
. ↩.
. .
. .
]
]
scenario editor-initially-wraps-barely-long-lines [
assume-screen 5/width, 5/height
run [
s:address:array:character <- new [abcde]
new-editor s:address:array:character, screen:address, 0/left, 5/right
]
screen-should-contain [
. .
.abcd↩.
.e .
. .
]
screen-should-contain-in-color 245/grey [
. .
. ↩.
. .
. .
]
]
scenario editor-initializes-empty-text [
assume-screen 5/width, 5/height
run [
1:address:array:character <- new []
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 5/right
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
]
]
scenario render-colors-comments [
assume-screen 5/width, 5/height
run [
s:address:array:character <- new [abc
# de
f]
new-editor s:address:array:character, screen:address, 0/left, 5/right
]
screen-should-contain [
. .
.abc .
.# de .
.f .
. .
]
screen-should-contain-in-color 12/lightblue, [
. .
. .
.# de .
. .
. .
]
screen-should-contain-in-color 7/white, [
. .
.abc .
. .
.f .
. .
]
]
after +character-c-recived [
color <- get-color color, c
]
recipe get-color [
local-scope
color:number <- next-ingredient
c:character <- next-ingredient
color-is-white?:boolean <- equal color, 7/white
{
break-unless color-is-white?
starting-comment?:boolean <- equal c, 35/#
break-unless starting-comment?
color <- copy 12/lightblue
jump +exit:label
}
{
color-is-blue?:boolean <- equal color, 12/lightblue
break-unless color-is-blue?
ending-comment?:boolean <- equal c, 10/newline
break-unless ending-comment?
color <- copy 7/white
jump +exit:label
}
{
break-unless color-is-white?
starting-assignment?:boolean <- equal c, 60/<
break-unless starting-assignment?
color <- copy 1/red
jump +exit:label
}
{
color-is-red?:boolean <- equal color, 1/red
break-unless color-is-red?
ending-assignment?:boolean <- equal c, 32/space
break-unless ending-assignment?
color <- copy 7/white
jump +exit:label
}
+exit
reply color
]
scenario render-colors-assignment [
assume-screen 8/width, 5/height
run [
s:address:array:character <- new [abc
d <- e
f]
new-editor s:address:array:character, screen:address, 0/left, 8/right
]
screen-should-contain [
. .
.abc .
.d <- e .
.f .
. .
]
screen-should-contain-in-color 1/red, [
. .
. .
. <- .
. .
. .
]
]
recipe editor-event-loop [
local-scope
screen:address <- next-ingredient
console:address <- next-ingredient
editor:address:editor-data <- next-ingredient
{
+next-event
e:event, console:address, found?:boolean, quit?:boolean <- read-event console
loop-unless found?
break-if quit?
trace [app], [next-event]
t:address:touch-event <- maybe-convert e, touch:variant
{
break-unless t
move-cursor-in-editor screen, editor, *t
}
{
break-if t
handle-keyboard-event screen, console, editor, e
}
row:number, screen <- render screen, editor
left:number <- get *editor, left:offset
right:number <- get *editor, right:offset
row <- add row, 1
move-cursor screen, row, left
clear-line-delimited screen, left, right
loop
}
]
recipe handle-keyboard-event [
local-scope
screen:address <- next-ingredient
console:address <- next-ingredient
editor:address:editor-data <- next-ingredient
e:event <- next-ingredient
reply-unless editor
{
c:address:character <- maybe-convert e, text:variant
break-unless c
+handle-special-character
insert-at-cursor editor, *c, screen
reply
}
k:address:number <- maybe-convert e:event, keycode:variant
assert k, [event was of unknown type; neither keyboard nor mouse]
before-cursor:address:address:duplex-list <- get-address *editor, before-cursor:offset
cursor-row:address:number <- get-address *editor, cursor-row:offset
cursor-column:address:number <- get-address *editor, cursor-column:offset
screen-height:number <- screen-height screen
left:number <- get *editor, left:offset
right:number <- get *editor, right:offset
+handle-special-key
]
recipe move-cursor-in-editor [
local-scope
screen:address <- next-ingredient
editor:address:editor-data <- next-ingredient
t:touch-event <- next-ingredient
reply-unless editor, 0/false
click-column:number <- get t, column:offset
left:number <- get *editor, left:offset
too-far-left?:boolean <- lesser-than click-column, left
reply-if too-far-left?, 0/false
right:number <- get *editor, right:offset
too-far-right?:boolean <- greater-than click-column, right
reply-if too-far-right?, 0/false
cursor-row:address:number <- get-address *editor, cursor-row:offset
*cursor-row <- get t, row:offset
cursor-column:address:number <- get-address *editor, cursor-column:offset
*cursor-column <- get t, column:offset
reply 1/true
]
recipe insert-at-cursor [
local-scope
editor:address:editor-data <- next-ingredient
c:character <- next-ingredient
screen:address <- next-ingredient
before-cursor:address:address:duplex-list <- get-address *editor, before-cursor:offset
insert-duplex c, *before-cursor
*before-cursor <- next-duplex *before-cursor
cursor-row:address:number <- get-address *editor, cursor-row:offset
cursor-column:address:number <- get-address *editor, cursor-column:offset
left:number <- get *editor, left:offset
right:number <- get *editor, right:offset
+insert-character-special-case
*cursor-column <- add *cursor-column, 1
]
scenario editor-handles-empty-event-queue [
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
assume-console []
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
.abc .
. .
]
]
scenario editor-handles-mouse-clicks [
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
assume-console [
left-click 1, 1
]
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
]
]
scenario editor-handles-mouse-clicks-outside-text [
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
assume-console [
left-click 1, 7
]
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
]
]
scenario editor-handles-mouse-clicks-outside-text-2 [
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
assume-console [
left-click 1, 7
]
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
]
]
scenario editor-handles-mouse-clicks-outside-text-3 [
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
assume-console [
left-click 3, 7
]
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
]
]
scenario editor-handles-mouse-clicks-outside-column [
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, 5/right
assume-console [
left-click 3, 8
]
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 <- 0
]
]
scenario editor-inserts-characters-into-empty-editor [
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, 5/right
assume-console [
type [abc]
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
.abc .
. .
]
]
scenario editor-inserts-characters-at-cursor [
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
assume-console [
type [0]
left-click 1, 2
type [d]
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
.0adbc .
. .
]
]
scenario editor-inserts-characters-at-cursor-2 [
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
assume-console [
left-click 1, 5
type [d]
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
.abcd .
. .
]
]
scenario editor-inserts-characters-at-cursor-3 [
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
assume-console [
left-click 3, 5
type [d]
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
.abcd .
. .
]
]
scenario editor-inserts-characters-at-cursor-4 [
assume-screen 10/width, 5/height
1:address:array:character <- new [abc
d]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 10/right
assume-console [
left-click 3, 5
type [e]
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
.abc .
.de .
. .
]
]
scenario editor-inserts-characters-at-cursor-5 [
assume-screen 10/width, 5/height
1:address:array:character <- new [abc
d]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 10/right
assume-console [
left-click 3, 5
type [ef]
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
.abc .
.def .
. .
]
]
scenario editor-moves-cursor-after-inserting-characters [
assume-screen 10/width, 5/height
1:address:array:character <- new [ab]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 5/right
assume-console [
type [01]
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
.01ab .
. .
]
]
scenario editor-wraps-line-on-insert [
assume-screen 5/width, 5/height
1:address:array:character <- new [abc]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 5/right
assume-console [
type [e]
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
.eabc .
. .
. .
]
assume-console [
type [f]
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
.efab↩.
.c .
. .
]
]
after +insert-character-special-case [
{
wrap-column:number <- subtract right, 1
at-wrap?:boolean <- greater-or-equal *cursor-column, wrap-column
break-unless at-wrap?
*cursor-column <- subtract *cursor-column, wrap-column
*cursor-row <- add *cursor-row, 1
{
screen-height:number <- screen-height screen
below-screen?:boolean <- greater-or-equal *cursor-row, screen-height
break-unless below-screen?
+scroll-down
}
reply
}
]
scenario editor-wraps-cursor-after-inserting-characters [
assume-screen 10/width, 5/height
1:address:array:character <- new [abcde]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 5/right
assume-console [
left-click 1, 4
type [f]
]
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 [
. .
.abcd↩ .
.fe .
. .
]
memory-should-contain [
3 <- 2
4 <- 1
]
]
scenario editor-wraps-cursor-after-inserting-characters-2 [
assume-screen 10/width, 5/height
1:address:array:character <- new [abcde]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 5/right
assume-console [
left-click 1, 3
type [f]
]
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 [
. .
.abcf↩ .
.de .
. .
]
memory-should-contain [
3 <- 2
4 <- 0
]
]
scenario editor-moves-cursor-down-after-inserting-newline [
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
assume-console [
type [0
1]
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
.0 .
.1abc .
. .
]
]
after +insert-character-special-case [
{
newline?:boolean <- equal c, 10/newline
break-unless newline?
*cursor-row <- add *cursor-row, 1
*cursor-column <- copy left
{
screen-height:number <- screen-height screen
below-screen?:boolean <- greater-or-equal *cursor-row, screen-height
break-unless below-screen?
+scroll-down
*cursor-row <- subtract *cursor-row, 1
}
d:address:duplex-list <- get *editor, data:offset
end-of-previous-line:address:duplex-list <- prev-duplex *before-cursor
indent:number <- line-indent end-of-previous-line, d
i:number <- copy 0
{
indent-done?:boolean <- greater-or-equal i, indent
break-if indent-done?
insert-at-cursor editor, 32/space, screen
i <- add i, 1
loop
}
reply
}
]
recipe line-indent [
local-scope
curr:address:duplex-list <- next-ingredient
start:address:duplex-list <- next-ingredient
result:number <- copy 0
reply-unless curr, result
at-start?:boolean <- equal curr, start
reply-if at-start?, result
{
curr <- prev-duplex curr
break-unless curr
at-start?:boolean <- equal curr, start
break-if at-start?
c:character <- get *curr, value:offset
at-newline?:boolean <- equal c, 10/newline
break-if at-newline?
is-space?:boolean <- equal c, 32/space
{
break-unless is-space?
result <- add result, 1
}
{
break-if is-space?
result <- copy 0
}
loop
}
reply result
]
scenario editor-moves-cursor-down-after-inserting-newline-2 [
assume-screen 10/width, 5/height
1:address:array:character <- new [abc]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 1/left, 10/right
assume-console [
type [0
1]
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
. 0 .
. 1abc .
. .
]
]
scenario editor-clears-previous-line-completely-after-inserting-newline [
assume-screen 10/width, 5/height
1:address:array:character <- new [abcde]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 5/right
assume-console [
type [
]
]
screen-should-contain [
. .
.abcd↩ .
.e .
. .
. .
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
. .
.abcd↩ .
.e .
. .
]
]
scenario editor-inserts-indent-after-newline [
assume-screen 10/width, 10/height
1:address:array:character <- new [ab
cd
ef]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 10/right
assume-console [
left-click 2, 8
type [
]
]
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 <- 2
]
]
scenario editor-inserts-two-spaces-on-tab [
assume-screen 10/width, 5/height
1:address:array:character <- new [ab
cd]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 5/right
assume-console [
type [»]
]
3:event/tab <- merge 0/text, 9/tab, 0/dummy, 0/dummy
replace-in-console 187/», 3:event/tab
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
. ab .
.cd .
]
]
after +handle-special-character [
{
tab?:boolean <- equal *c, 9/tab
break-unless tab?
insert-at-cursor editor, 32/space, screen
insert-at-cursor editor, 32/space, screen
reply
}
]
scenario editor-handles-backspace-key [
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
assume-console [
left-click 1, 1
type [«]
]
3:event/backspace <- merge 0/text, 8/backspace, 0/dummy, 0/dummy
replace-in-console 171/«, 3:event/backspace
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
4:number <- get *2:address:editor-data, cursor-row:offset
5:number <- get *2:address:editor-data, cursor-column:offset
]
screen-should-contain [
. .
.bc .
. .
]
memory-should-contain [
4 <- 1
5 <- 0
]
]
after +handle-special-character [
{
backspace?:boolean <- equal *c, 8/backspace
break-unless backspace?
delete-before-cursor editor
reply
}
]
recipe delete-before-cursor [
local-scope
editor:address:editor-data <- next-ingredient
before-cursor:address:address:duplex-list <- get-address *editor, before-cursor:offset
prev:address:duplex-list <- prev-duplex *before-cursor
reply-unless prev
editor <- move-cursor-coordinates-left editor
remove-duplex *before-cursor
*before-cursor <- copy prev
]
recipe move-cursor-coordinates-left [
local-scope
editor:address:editor-data <- next-ingredient
before-cursor:address:duplex-list <- get *editor, before-cursor:offset
cursor-row:address:number <- get-address *editor, cursor-row:offset
cursor-column:address:number <- get-address *editor, cursor-column:offset
left:number <- get *editor, left:offset
{
at-left-margin?:boolean <- equal *cursor-column, left
break-if at-left-margin?
*cursor-column <- subtract *cursor-column, 1
reply editor/same-as-ingredient:0
}
top-of-screen?:boolean <- equal *cursor-row, 1
{
break-if top-of-screen?
*cursor-row <- subtract *cursor-row, 1
}
{
break-unless top-of-screen?
+scroll-up
}
{
previous-character:character <- get *before-cursor, value:offset
previous-character-is-newline?:boolean <- equal previous-character, 10/newline
break-unless previous-character-is-newline?
d:address:duplex-list <- get *editor, data:offset
end-of-line:number <- previous-line-length before-cursor, d
*cursor-column <- add left, end-of-line
reply editor/same-as-ingredient:0
}
right:number <- get *editor, right:offset
*cursor-column <- subtract right, 1
reply editor/same-as-ingredient:0
]
recipe previous-line-length [
local-scope
curr:address:duplex-list <- next-ingredient
start:address:duplex-list <- next-ingredient
result:number <- copy 0
reply-unless curr, result
at-start?:boolean <- equal curr, start
reply-if at-start?, result
{
curr <- prev-duplex curr
break-unless curr
at-start?:boolean <- equal curr, start
break-if at-start?
c:character <- get *curr, value:offset
at-newline?:boolean <- equal c, 10/newline
break-if at-newline?
result <- add result, 1
loop
}
reply result
]
scenario editor-clears-last-line-on-backspace [
assume-screen 10/width, 5/height
1:address:array:character <- new [ab
cd]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 10/right
assume-console [
left-click 2, 0
type [«]
]
3:event/backspace <- merge 0/text, 8/backspace, 0/dummy, 0/dummy
replace-in-console 171/«, 3:event/backspace
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
4:number <- get *2:address:editor-data, cursor-row:offset
5:number <- get *2:address:editor-data, cursor-column:offset
]
screen-should-contain [
. .
.abcd .
. .
]
memory-should-contain [
4 <- 1
5 <- 2
]
]
scenario editor-handles-delete-key [
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
assume-console [
press 65522
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
.bc .
. .
]
assume-console [
press 65522
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
.c .
. .
]
]
after +handle-special-key [
{
delete?:boolean <- equal *k, 65522/delete
break-unless delete?
curr:address:duplex-list <- get **before-cursor, next:offset
_ <- remove-duplex curr
reply
}
]
scenario editor-moves-cursor-right-with-key [
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
assume-console [
press 65514
type [0]
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
.a0bc .
. .
]
]
after +handle-special-key [
{
move-to-next-character?:boolean <- equal *k, 65514/right-arrow
break-unless move-to-next-character?
old-cursor:address:duplex-list <- next-duplex *before-cursor
break-unless old-cursor
*before-cursor <- copy old-cursor
{
old-cursor-character:character <- get **before-cursor, value:offset
was-at-newline?:boolean <- equal old-cursor-character, 10/newline
break-unless was-at-newline?
*cursor-row <- add *cursor-row, 1
*cursor-column <- copy left
below-screen?:boolean <- greater-or-equal *cursor-row, screen-height
reply-unless below-screen?
+scroll-down
*cursor-row <- subtract *cursor-row, 1
reply
}
{
wrap-column:number <- subtract right, 1
at-wrap?:boolean <- equal *cursor-column, wrap-column
break-unless at-wrap?
new-cursor:address:duplex-list <- next-duplex old-cursor
break-unless new-cursor
next-character:character <- get *new-cursor, value:offset
newline?:boolean <- equal next-character, 10/newline
break-if newline?
*cursor-row <- add *cursor-row, 1
*cursor-column <- copy left
below-screen?:boolean <- greater-or-equal *cursor-row, screen-height
reply-unless below-screen?
+scroll-down
*cursor-row <- subtract *cursor-row, 1
reply
}
*cursor-column <- add *cursor-column, 1
}
]
scenario editor-moves-cursor-to-next-line-with-right-arrow [
assume-screen 10/width, 5/height
1:address:array:character <- new [abc
d]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 10/right
assume-console [
press 65514
press 65514
press 65514
press 65514
type [0]
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
.abc .
.0d .
. .
]
]
scenario editor-moves-cursor-to-next-line-with-right-arrow-2 [
assume-screen 10/width, 5/height
1:address:array:character <- new [abc
d]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 1/left, 10/right
assume-console [
press 65514
press 65514
press 65514
press 65514
type [0]
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
. abc .
. 0d .
. .
]
]
scenario editor-moves-cursor-to-next-wrapped-line-with-right-arrow [
assume-screen 10/width, 5/height
1:address:array:character <- new [abcdef]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 5/right
assume-console [
left-click 1, 3
press 65514
]
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 [
. .
.abcd↩ .
.ef .
. .
]
memory-should-contain [
3 <- 2
4 <- 0
]
]
scenario editor-moves-cursor-to-next-wrapped-line-with-right-arrow-2 [
assume-screen 10/width, 5/height
1:address:array:character <- new [abcde]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 5/right
assume-console [
left-click 1, 3
press 65514
]
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 <- 0
]
assume-console [
press 65514
]
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-moves-cursor-to-next-wrapped-line-with-right-arrow-3 [
assume-screen 10/width, 5/height
1:address:array:character <- new [abcdef]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 1/left, 6/right
assume-console [
left-click 1, 4
press 65514
]
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 [
. .
. abcd↩ .
. ef .
. .
]
memory-should-contain [
3 <- 2
4 <- 1
]
]
scenario editor-moves-cursor-to-next-line-with-right-arrow-at-end-of-line [
assume-screen 10/width, 5/height
1:address:array:character <- new [abc
d]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 10/right
assume-console [
left-click 1, 3
press 65514
type [0]
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
.abc .
.0d .
. .
]
]
scenario editor-moves-cursor-left-with-key [
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
assume-console [
left-click 1, 2
press 65515
type [0]
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
.a0bc .
. .
]
]
after +handle-special-key [
{
move-to-previous-character?:boolean <- equal *k, 65515/left-arrow
break-unless move-to-previous-character?
prev:address:duplex-list <- prev-duplex *before-cursor
break-unless prev
editor <- move-cursor-coordinates-left editor
}
]
scenario editor-moves-cursor-to-previous-line-with-left-arrow-at-start-of-line [
assume-screen 10/width, 5/height
1:address:array:character <- new [abc
d]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 10/right
assume-console [
left-click 2, 0
press 65515
]
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
]
]
scenario editor-moves-cursor-to-previous-line-with-left-arrow-at-start-of-line-2 [
assume-screen 10/width, 5/height
1:address:array:character <- new [abc
def
g]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 10/right
assume-console [
left-click 3, 0
press 65515
type [0]
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
.abc .
.def0 .
.g .
. .
]
]
scenario editor-moves-cursor-to-previous-line-with-left-arrow-at-start-of-line-3 [
assume-screen 10/width, 5/height
1:address:array:character <- new [abc
def
g]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 10/right
assume-console [
left-click 1, 0
press 65515
type [0]
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
.0abc .
.def .
.g .
. .
]
]
scenario editor-moves-cursor-to-previous-line-with-left-arrow-at-start-of-line-4 [
assume-screen 10/width, 5/height
1:address:array:character <- new [abc
d]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 10/right
assume-console [
left-click 3, 0
press 65515
type [0]
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
.abc .
.0 .
.d .
. .
]
]
scenario editor-moves-across-screen-lines-across-wrap-with-left-arrow [
assume-screen 10/width, 5/height
1:address:array:character <- new [abcdef]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 5/right
screen-should-contain [
. .
.abcd↩ .
.ef .
. .
]
assume-console [
left-click 2, 0
press 65515
]
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
]
]
scenario editor-moves-to-previous-line-with-up-arrow [
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
assume-console [
left-click 2, 1
press 65517
]
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
]
]
after +handle-special-key [
{
move-to-previous-line?:boolean <- equal *k, 65517/up-arrow
break-unless move-to-previous-line?
already-at-top?:boolean <- lesser-or-equal *cursor-row, 1/top
{
break-if already-at-top?
*cursor-row <- subtract *cursor-row, 1
}
{
break-unless already-at-top?
+scroll-up
}
}
]
scenario editor-adjusts-column-at-next-line [
assume-screen 10/width, 5/height
1:address:array:character <- new [abc
de]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 10/right
assume-console [
left-click 1, 3
press 65516
]
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
]
]
scenario editor-moves-to-next-line-with-down-arrow [
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
assume-console [
press 65516
]
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 <- 0
]
]
after +handle-special-key [
{
move-to-next-line?:boolean <- equal *k, 65516/down-arrow
break-unless move-to-next-line?
last-line:number <- subtract screen-height, 1
already-at-bottom?:boolean <- greater-or-equal *cursor-row, last-line
{
break-if already-at-bottom?
*cursor-row <- add *cursor-row, 1
}
{
break-unless already-at-bottom?
+scroll-down
}
}
]
scenario editor-adjusts-column-at-previous-line [
assume-screen 10/width, 5/height
1:address:array:character <- new [ab
def]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 10/right
assume-console [
left-click 2, 3
press 65517
]
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
]
]
scenario editor-moves-to-start-of-line-with-ctrl-a [
assume-screen 10/width, 5/height
1:address:array:character <- new [123
456]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 10/right
assume-console [
left-click 2, 3
type [a]
]
3:event/ctrl-a <- merge 0/text, 1/ctrl-a, 0/dummy, 0/dummy
replace-in-console 97/a, 3:event/ctrl-a
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
4:number <- get *2:address:editor-data, cursor-row:offset
5:number <- get *2:address:editor-data, cursor-column:offset
]
memory-should-contain [
4 <- 2
5 <- 0
]
]
after +handle-special-character [
{
ctrl-a?:boolean <- equal *c, 1/ctrl-a
break-unless ctrl-a?
move-to-start-of-line editor
reply
}
]
after +handle-special-key [
{
home?:boolean <- equal *k, 65521/home
break-unless home?
move-to-start-of-line editor
reply
}
]
recipe move-to-start-of-line [
local-scope
editor:address:editor-data <- next-ingredient
left:number <- get *editor, left:offset
cursor-column:address:number <- get-address *editor, cursor-column:offset
*cursor-column <- copy left
before-cursor:address:address:duplex-list <- get-address *editor, before-cursor:offset
init:address:duplex-list <- get *editor, data:offset
{
at-start-of-text?:boolean <- equal *before-cursor, init
break-if at-start-of-text?
prev:character <- get **before-cursor, value:offset
at-start-of-line?:boolean <- equal prev, 10/newline
break-if at-start-of-line?
*before-cursor <- prev-duplex *before-cursor
assert *before-cursor, [move-to-start-of-line tried to move before start of text]
loop
}
]
scenario editor-moves-to-start-of-line-with-ctrl-a-2 [
assume-screen 10/width, 5/height
1:address:array:character <- new [123
456]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 10/right
assume-console [
left-click 1, 3
type [a]
]
3:event/ctrl-a <- merge 0/text, 1/ctrl-a, 0/dummy, 0/dummy
replace-in-console 97/a, 3:event/ctrl-a
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
4:number <- get *2:address:editor-data, cursor-row:offset
5:number <- get *2:address:editor-data, cursor-column:offset
]
memory-should-contain [
4 <- 1
5 <- 0
]
]
scenario editor-moves-to-start-of-line-with-home [
assume-screen 10/width, 5/height
1:address:array:character <- new [123
456]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 10/right
assume-console [
left-click 2, 3
press 65521
]
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 <- 0
]
]
scenario editor-moves-to-start-of-line-with-home-2 [
assume-screen 10/width, 5/height
1:address:array:character <- new [123
456]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 10/right
assume-console [
left-click 1, 3
press 65521
]
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
]
]
scenario editor-moves-to-start-of-line-with-ctrl-e [
assume-screen 10/width, 5/height
1:address:array:character <- new [123
456]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 10/right
assume-console [
left-click 1, 1
type [e]
]
3:event/ctrl-e <- merge 0/text, 5/ctrl-e, 0/dummy, 0/dummy
replace-in-console 101/e, 3:event/ctrl-e
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
4:number <- get *2:address:editor-data, cursor-row:offset
5:number <- get *2:address:editor-data, cursor-column:offset
]
memory-should-contain [
4 <- 1
5 <- 3
]
assume-console [
type [z]
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
4:number <- get *2:address:editor-data, cursor-row:offset
5:number <- get *2:address:editor-data, cursor-column:offset
]
memory-should-contain [
4 <- 1
5 <- 4
]
screen-should-contain [
. .
.123z .
.456 .
. .
]
]
after +handle-special-character [
{
ctrl-e?:boolean <- equal *c, 5/ctrl-e
break-unless ctrl-e?
move-to-end-of-line editor
reply
}
]
after +handle-special-key [
{
end?:boolean <- equal *k, 65520/end
break-unless end?
move-to-end-of-line editor
reply
}
]
recipe move-to-end-of-line [
local-scope
editor:address:editor-data <- next-ingredient
before-cursor:address:address:duplex-list <- get-address *editor, before-cursor:offset
cursor-column:address:number <- get-address *editor, cursor-column:offset
{
next:address:duplex-list <- next-duplex *before-cursor
break-unless next
nextc:character <- get *next, value:offset
at-end-of-line?:boolean <- equal nextc, 10/newline
break-if at-end-of-line?
*before-cursor <- copy next
*cursor-column <- add *cursor-column, 1
loop
}
*cursor-column <- add *cursor-column, 1
]
scenario editor-moves-to-end-of-line-with-ctrl-e-2 [
assume-screen 10/width, 5/height
1:address:array:character <- new [123
456]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 10/right
assume-console [
left-click 2, 1
type [e]
]
3:event/ctrl-e <- merge 0/text, 5/ctrl-e, 0/dummy, 0/dummy
replace-in-console 101/e, 3:event/ctrl-e
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
4:number <- get *2:address:editor-data, cursor-row:offset
5:number <- get *2:address:editor-data, cursor-column:offset
]
memory-should-contain [
4 <- 2
5 <- 3
]
]
scenario editor-moves-to-end-of-line-with-end [
assume-screen 10/width, 5/height
1:address:array:character <- new [123
456]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 10/right
assume-console [
left-click 1, 1
press 65520
]
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
]
]
scenario editor-moves-to-end-of-line-with-end-2 [
assume-screen 10/width, 5/height
1:address:array:character <- new [123
456]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 10/right
assume-console [
left-click 2, 1
press 65520
]
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
]
]
scenario editor-deletes-to-start-of-line-with-ctrl-u [
assume-screen 10/width, 5/height
1:address:array:character <- new [123
456]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 10/right
assume-console [
left-click 2, 2
type [u]
]
3:event/ctrl-a <- merge 0/text, 21/ctrl-u, 0/dummy, 0/dummy
replace-in-console 117/u, 3:event/ctrl-u
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
.123 .
.6 .
. .
]
]
after +handle-special-character [
{
ctrl-u?:boolean <- equal *c, 21/ctrl-u
break-unless ctrl-u?
delete-to-start-of-line editor
reply
}
]
recipe delete-to-start-of-line [
local-scope
editor:address:editor-data <- next-ingredient
init:address:duplex-list <- get *editor, data:offset
before-cursor:address:address:duplex-list <- get-address *editor, before-cursor:offset
start:address:duplex-list <- copy *before-cursor
end:address:duplex-list <- next-duplex *before-cursor
{
at-start-of-text?:boolean <- equal start, init
break-if at-start-of-text?
curr:character <- get *start, value:offset
at-start-of-line?:boolean <- equal curr, 10/newline
break-if at-start-of-line?
start <- prev-duplex start
assert start, [delete-to-start-of-line tried to move before start of text]
loop
}
start-next:address:address:duplex-list <- get-address *start, next:offset
*start-next <- copy end
{
break-unless end
end-prev:address:address:duplex-list <- get-address *end, prev:offset
*end-prev <- copy start
}
*before-cursor <- prev-duplex end
left:number <- get *editor, left:offset
cursor-column:address:number <- get-address *editor, cursor-column:offset
*cursor-column <- copy left
]
scenario editor-deletes-to-start-of-line-with-ctrl-u-2 [
assume-screen 10/width, 5/height
1:address:array:character <- new [123
456]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 10/right
assume-console [
left-click 1, 2
type [u]
]
3:event/ctrl-u <- merge 0/text, 21/ctrl-u, 0/dummy, 0/dummy
replace-in-console 117/u, 3:event/ctrl-u
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
.3 .
.456 .
. .
]
]
scenario editor-deletes-to-start-of-line-with-ctrl-u-3 [
assume-screen 10/width, 5/height
1:address:array:character <- new [123
456]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 10/right
assume-console [
left-click 1, 3
type [u]
]
3:event/ctrl-u <- merge 0/text, 21/ctrl-u, 0/dummy, 0/dummy
replace-in-console 117/u, 3:event/ctrl-u
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
. .
.456 .
. .
]
]
scenario editor-deletes-to-start-of-final-line-with-ctrl-u [
assume-screen 10/width, 5/height
1:address:array:character <- new [123
456]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 10/right
assume-console [
left-click 2, 3
type [u]
]
3:event/ctrl-u <- merge 0/text, 21/ctrl-u, 0/dummy, 0/dummy
replace-in-console 117/u, 3:event/ctrl-u
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
.123 .
. .
. .
]
]
scenario editor-deletes-to-end-of-line-with-ctrl-k [
assume-screen 10/width, 5/height
1:address:array:character <- new [123
456]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 10/right
assume-console [
left-click 1, 1
type [k]
]
3:event/ctrl-k <- merge 0/text, 11/ctrl-k, 0/dummy, 0/dummy
replace-in-console 107/k, 3:event/ctrl-k
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
.1 .
.456 .
. .
]
]
after +handle-special-character [
{
ctrl-k?:boolean <- equal *c, 11/ctrl-k
break-unless ctrl-k?
delete-to-end-of-line editor
reply
}
]
recipe delete-to-end-of-line [
local-scope
editor:address:editor-data <- next-ingredient
start:address:duplex-list <- get *editor, before-cursor:offset
end:address:duplex-list <- next-duplex start
{
at-end-of-text?:boolean <- equal end, 0/null
break-if at-end-of-text?
curr:character <- get *end, value:offset
at-end-of-line?:boolean <- equal curr, 10/newline
break-if at-end-of-line?
end <- next-duplex end
loop
}
start-next:address:address:duplex-list <- get-address *start, next:offset
*start-next <- copy end
{
break-unless end
end-prev:address:address:duplex-list <- get-address *end, prev:offset
*end-prev <- copy start
}
]
scenario editor-deletes-to-end-of-line-with-ctrl-k-2 [
assume-screen 10/width, 5/height
1:address:array:character <- new [123
456]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 10/right
assume-console [
left-click 2, 1
type [k]
]
3:event/ctrl-k <- merge 0/text, 11/ctrl-k, 0/dummy, 0/dummy
replace-in-console 107/k, 3:event/ctrl-k
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
.123 .
.4 .
. .
]
]
scenario editor-deletes-to-end-of-line-with-ctrl-k-3 [
assume-screen 10/width, 5/height
1:address:array:character <- new [123
456]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 10/right
assume-console [
left-click 1, 2
type [k]
]
3:event/ctrl-k <- merge 0/text, 11/ctrl-k, 0/dummy, 0/dummy
replace-in-console 107/k, 3:event/ctrl-k
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
.12 .
.456 .
. .
]
]
scenario editor-deletes-to-end-of-line-with-ctrl-k-4 [
assume-screen 10/width, 5/height
1:address:array:character <- new [123
456]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 10/right
assume-console [
left-click 1, 3
type [k]
]
3:event/ctrl-k <- merge 0/text, 11/ctrl-k, 0/dummy, 0/dummy
replace-in-console 107/k, 3:event/ctrl-k
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
.123 .
.456 .
. .
]
]
scenario editor-deletes-to-end-of-line-with-ctrl-k-5 [
assume-screen 10/width, 5/height
1:address:array:character <- new [123
456]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 10/right
assume-console [
left-click 2, 2
type [k]
]
3:event/ctrl-k <- merge 0/text, 11/ctrl-k, 0/dummy, 0/dummy
replace-in-console 107/k, 3:event/ctrl-k
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
.123 .
.45 .
. .
]
]
scenario editor-deletes-to-end-of-line-with-ctrl-k-6 [
assume-screen 10/width, 5/height
1:address:array:character <- new [123
456]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 10/right
assume-console [
left-click 2, 3
type [k]
]
3:event/ctrl-k <- merge 0/text, 11/ctrl-k, 0/dummy, 0/dummy
replace-in-console 107/k, 3:event/ctrl-k
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
.123 .
.456 .
. .
]
]
scenario editor-can-scroll [
assume-screen 10/width, 4/height
1:address:array:character <- new [a
b
c
d]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 10/right
screen-should-contain [
. .
.a .
.b .
.c .
]
assume-console [
press 65518
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
.c .
.d .
. .
]
]
after +handle-special-character [
{
ctrl-f?:boolean <- equal *c, 6/ctrl-f
break-unless ctrl-f?
page-down editor
reply
}
]
after +handle-special-key [
{
page-down?:boolean <- equal *k, 65518/page-down
break-unless page-down?
page-down editor
reply
}
]
container editor-data [
previous-page:address:list:address:duplex-list:character
]
recipe page-down [
local-scope
editor:address:editor-data <- next-ingredient
bottom-of-screen:address:duplex-list <- get *editor, bottom-of-screen:offset
reply-unless bottom-of-screen, editor/same-as-ingredient:0
before-cursor:address:address:duplex-list <- get-address *editor, before-cursor:offset
*before-cursor <- prev-duplex bottom-of-screen
{
last:character <- get **before-cursor, value:offset
newline?:boolean <- equal last, 10/newline
break-unless newline?:boolean
*before-cursor <- prev-duplex *before-cursor
}
top-of-screen:address:address:duplex-list <- get-address *editor, top-of-screen:offset
previous-page:address:address:list:address:duplex-list:character <- get-address *editor, previous-page:offset
*previous-page <- push *top-of-screen, *previous-page
move-to-start-of-line editor
*top-of-screen <- copy *before-cursor
reply editor/same-as-ingredient:0
]
scenario editor-does-not-scroll-past-end [
assume-screen 10/width, 4/height
1:address:array:character <- new [a
b]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 10/right
screen-should-contain [
. .
.a .
.b .
. .
]
assume-console [
press 65518
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
.a .
.b .
. .
]
]
scenario editor-starts-next-page-at-start-of-wrapped-line [
assume-screen 10/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, 4/right
screen-should-contain [
. .
.a .
.b .
.cde↩ .
]
assume-console [
press 65518
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
.cde↩ .
.fgh .
. .
]
]
scenario editor-starts-next-page-at-start-of-wrapped-line-2 [
assume-screen 10/width, 4/height
1:address:array:character <- new [a
bcdefgh]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 4/right
screen-should-contain [
. .
.a .
.bcd↩ .
.efg↩ .
]
assume-console [
press 65518
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
.bcd↩ .
.efg↩ .
.h .
]
]
scenario editor-can-scroll-up [
assume-screen 10/width, 4/height
1:address:array:character <- new [a
b
c
d]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 10/right
screen-should-contain [
. .
.a .
.b .
.c .
]
assume-console [
press 65518
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
.c .
.d .
. .
]
assume-console [
press 65519
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
.a .
.b .
.c .
]
]
after +handle-special-character [
{
ctrl-b?:boolean <- equal *c, 2/ctrl-f
break-unless ctrl-b?
page-up editor
reply
}
]
after +handle-special-key [
{
page-up?:boolean <- equal *k, 65519/page-up
break-unless page-up?
page-up editor
reply
}
]
recipe page-up [
local-scope
editor:address:editor-data <- next-ingredient
previous-page:address:address:list:address:duplex-list:character <- get-address *editor, previous-page:offset
reply-unless *previous-page, editor/same-as-ingredient:0
top-of-screen:address:address:duplex-list <- get-address *editor, top-of-screen:offset
*top-of-screen <- first *previous-page
*previous-page <- rest *previous-page
reply editor/same-as-ingredient:0
]
scenario editor-can-scroll-up-multiple-pages [
assume-screen 10/width, 4/height
1:address:array:character <- new [a
b
c
d
e
f
g
h]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 10/right
screen-should-contain [
. .
.a .
.b .
.c .
]
assume-console [
press 65518
press 65518
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
.e .
.f .
.g .
]
assume-console [
press 65519
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
.c .
.d .
.e .
]
assume-console [
press 65519
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
.a .
.b .
.c .
]
]
scenario editor-can-scroll-down-using-arrow-keys [
assume-screen 10/width, 4/height
1:address:array:character <- new [a
b
c
d]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 10/right
screen-should-contain [
. .
.a .
.b .
.c .
]
assume-console [
left-click 3, 0
press 65516
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
.b .
.c .
.d .
]
]
after +scroll-down [
top-of-screen:address:address:duplex-list <- get-address *editor, top-of-screen:offset
left:number <- get *editor, left:offset
right:number <- get *editor, right:offset
max:number <- subtract right, left
*top-of-screen <- start-of-next-line *top-of-screen, max
]
recipe start-of-next-line [
local-scope
original:address:duplex-list <- next-ingredient
max:number <- next-ingredient
count:number <- copy 0
curr:address:duplex-list <- copy original
{
reply-unless curr, original
done?:boolean <- greater-or-equal count, max
break-if done?
c:character <- get *curr, value:offset
at-newline?:boolean <- equal c, 10/newline
break-if at-newline?
curr <- next-duplex curr
count <- add count, 1
loop
}
reply-unless curr, original
reply curr
]
scenario editor-scrolls-down-past-wrapped-line-using-arrow-keys [
assume-screen 10/width, 4/height
1:address:array:character <- new [abcdef
g
h
i]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 5/right
screen-should-contain [
. .
.abcd↩ .
.ef .
.g .
]
assume-console [
left-click 3, 0
press 65516
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
.ef .
.g .
.h .
]
]
scenario editor-scrolls-down-past-wrapped-line-using-arrow-keys-2 [
assume-screen 10/width, 4/height
1:address:array:character <- new [abcdefghij
k
l
m]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 5/right
assume-console [
left-click 3, 0
press 65516
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
.efgh↩ .
.ij .
.k .
]
assume-console [
press 65516
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
.ij .
.k .
.l .
]
]
scenario editor-scrolls-down-when-line-wraps [
assume-screen 5/width, 4/height
1:address:array:character <- new [a
b
cdef]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 5/right
assume-console [
left-click 3, 4
type [g]
]
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 [
. .
.b .
.cdef↩.
.g .
]
memory-should-contain [
3 <- 3
4 <- 1
]
]
scenario editor-scrolls-down-on-newline [
assume-screen 5/width, 4/height
1:address:array:character <- new [a
b
c]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 5/right
assume-console [
left-click 3, 4
type [
]
]
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 [
. .
.b .
.c .
. .
]
memory-should-contain [
3 <- 3
4 <- 0
]
]
scenario editor-scrolls-down-on-right-arrow [
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 65514
]
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 [
. .
.b .
.cdef↩.
.gh .
]
memory-should-contain [
3 <- 3
4 <- 0
]
]
scenario editor-scrolls-down-on-right-arrow-2 [
assume-screen 5/width, 4/height
1:address:array:character <- new [a
b
c
d]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 5/right
assume-console [
left-click 3, 3
press 65514
]
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 [
. .
.b .
.c .
.d .
]
memory-should-contain [
3 <- 3
4 <- 0
]
]
scenario editor-can-scroll-up-using-arrow-keys [
assume-screen 10/width, 4/height
1:address:array:character <- new [a
b
c
d]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 10/right
screen-should-contain [
. .
.a .
.b .
.c .
]
assume-console [
press 65518
press 65517
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
.b .
.c .
.d .
]
]
after +scroll-up [
top-of-screen:address:address:duplex-list <- get-address *editor, top-of-screen:offset
left:number <- get *editor, left:offset
right:number <- get *editor, right:offset
max:number <- subtract right, left
*top-of-screen <- start-of-previous-line *top-of-screen, max
]
recipe start-of-previous-line [
local-scope
original:address:duplex-list <- next-ingredient
max:number <- next-ingredient
count:number <- copy 0
curr:address:duplex-list <- copy original
reply-unless curr, original
{
c:character <- get *curr, value:offset
at-newline?:boolean <- equal c, 10/newline
break-unless at-newline?
max <- subtract max, 1
}
curr <- prev-duplex curr
count <- add count, 1
{
reply-unless curr, original
done?:boolean <- greater-or-equal count, max
break-if done?
c:character <- get *curr, value:offset
at-newline?:boolean <- equal c, 10/newline
break-if at-newline?
curr <- prev-duplex curr
count <- add count, 1
loop
}
reply-unless curr, original
reply curr
]
scenario editor-scrolls-up-past-wrapped-line-using-arrow-keys [
assume-screen 10/width, 4/height
1:address:array:character <- new [abcdef
g
h
i]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 5/right
screen-should-contain [
. .
.abcd↩ .
.ef .
.g .
]
assume-console [
press 65518
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
.g .
.h .
.i .
]
assume-console [
press 65517
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
.ef .
.g .
.h .
]
]
scenario editor-scrolls-up-past-wrapped-line-using-arrow-keys-2 [
assume-screen 10/width, 5/height
1:address:array:character <- new [abcdefghij
k
l
m]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 5/right
assume-console [
press 65518
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
.k .
.l .
.m .
. .
]
assume-console [
press 65517
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
.ij .
.k .
.l .
.m .
]
assume-console [
press 65517
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
.efgh↩ .
.ij .
.k .
.l .
]
assume-console [
press 65517
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
.abcd↩ .
.efgh↩ .
.ij .
.k .
]
]
scenario editor-scrolls-up-on-left-arrow [
assume-screen 5/width, 4/height
1:address:array:character <- new [a
b
c
d
e]
2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 5/right
assume-console [
press 65518
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
]
screen-should-contain [
. .
.c .
.d .
.e .
]
assume-console [
press 65515
]
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 [
. .
.b .
.c .
.d .
]
memory-should-contain [
3 <- 1
4 <- 1
]
]
container programming-environment-data [
recipes:address:editor-data
recipe-warnings:address:array:character
current-sandbox:address:editor-data
sandbox:address:sandbox-data
sandbox-in-focus?:boolean
]
recipe new-programming-environment [
local-scope
screen:address <- next-ingredient
initial-recipe-contents:address:array:character <- next-ingredient
initial-sandbox-contents:address:array:character <- next-ingredient
width:number <- screen-width screen
height:number <- screen-height screen
result:address:programming-environment-data <- new programming-environment-data:type
draw-horizontal screen, 0, 0/left, width, 32/space, 0/black, 238/grey
button-start:number <- subtract width, 20
button-on-screen?:boolean <- greater-or-equal button-start, 0
assert button-on-screen?, [screen too narrow for menu]
move-cursor screen, 0/row, button-start
run-button:address:array:character <- new [ run (F4) ]
print-string screen, run-button, 255/white, 161/reddish
divider:number, _ <- divide-with-remainder width, 2
draw-vertical screen, divider, 1/top, height, 9482/vertical-dotted
recipes:address:address:editor-data <- get-address *result, recipes:offset
*recipes <- new-editor initial-recipe-contents, screen, 0/left, divider/right
new-left:number <- add divider, 1
new-right:number <- add new-left, 5
current-sandbox:address:address:editor-data <- get-address *result, current-sandbox:offset
*current-sandbox <- new-editor initial-sandbox-contents, screen, new-left, width/right
screen <- render-all screen, result
reply result
]
recipe event-loop [
local-scope
screen:address <- next-ingredient
console:address <- next-ingredient
env:address:programming-environment-data <- next-ingredient
recipes:address:editor-data <- get *env, recipes:offset
current-sandbox:address:editor-data <- get *env, current-sandbox:offset
sandbox-in-focus?:address:boolean <- get-address *env, sandbox-in-focus?:offset
{
+next-event
e:event, console, found?:boolean, quit?:boolean <- read-event console
loop-unless found?
break-if quit?
trace [app], [next-event]
{
k:address:number <- maybe-convert e:event, keycode:variant
break-unless k
+global-keypress
}
{
c:address:character <- maybe-convert e:event, text:variant
break-unless c
+global-type
{
ctrl-n?:boolean <- equal *c, 14/ctrl-n
break-unless ctrl-n?
*sandbox-in-focus? <- not *sandbox-in-focus?
update-cursor screen, recipes, current-sandbox, *sandbox-in-focus?
show-screen screen
loop +next-event:label
}
}
{
t:address:touch-event <- maybe-convert e:event, touch:variant
break-unless t
touch-type:number <- get *t, type:offset
is-left-click?:boolean <- equal touch-type, 65513/mouse-left
loop-unless is-left-click?, +next-event:label
+global-touch
_ <- move-cursor-in-editor screen, recipes, *t
*sandbox-in-focus? <- move-cursor-in-editor screen, current-sandbox, *t
render-minimal screen, env
loop +next-event:label
}
{
{
break-if *sandbox-in-focus?
handle-keyboard-event screen, console, recipes, e:event
}
{
break-unless *sandbox-in-focus?
handle-keyboard-event screen, console, current-sandbox, e:event
}
more-events?:boolean <- has-more-events? console
break-if more-events?
render-minimal screen, env
}
loop
}
]
scenario point-at-multiple-editors [
$close-trace
assume-screen 30/width, 5/height
1:address:array:character <- new [abc]
2:address:array:character <- new [def]
3:address:programming-environment-data <- new-programming-environment screen:address, 1:address:array:character, 2:address:array:character
assume-console [
left-click 1, 1
left-click 1, 17
]
run [
event-loop screen:address, console:address, 3:address:programming-environment-data
4:address:editor-data <- get *3:address:programming-environment-data, recipes:offset
5:number <- get *4:address:editor-data, cursor-column:offset
6:address:editor-data <- get *3:address:programming-environment-data, current-sandbox:offset
7:number <- get *6:address:editor-data, cursor-column:offset
]
memory-should-contain [
5 <- 1
7 <- 17
]
]
scenario edit-multiple-editors [
$close-trace
assume-screen 30/width, 5/height
1:address:array:character <- new [abc]
2:address:array:character <- new [def]
3:address:programming-environment-data <- new-programming-environment screen:address, 1:address:array:character, 2:address:array:character
assume-console [
left-click 1, 1
type [0]
left-click 1, 17
type [1]
]
run [
event-loop screen:address, console:address, 3:address:programming-environment-data
4:address:editor-data <- get *3:address:programming-environment-data, recipes:offset
5:number <- get *4:address:editor-data, cursor-column:offset
6:address:editor-data <- get *3:address:programming-environment-data, current-sandbox:offset
7:number <- get *6:address:editor-data, cursor-column:offset
]
screen-should-contain [
. run (F4) . # this line has a different background, but we don't test that yet
.a0bc ┊d1ef .
.┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊━━━━━━━━━━━━━━.
. ┊ .
]
memory-should-contain [
5 <- 2
7 <- 18
]
run [
screen:address <- print-character screen:address, 9251/␣
]
screen-should-contain [
. run (F4) .
.a0bc ┊d1␣f .
.┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊━━━━━━━━━━━━━━.
. ┊ .
]
]
scenario multiple-editors-cover-only-their-own-areas [
$close-trace
assume-screen 60/width, 10/height
run [
1:address:array:character <- new [abc]
2:address:array:character <- new [def]
3:address:programming-environment-data <- new-programming-environment screen:address, 1:address:array:character, 2:address:array:character
]
screen-should-contain [
. run (F4) .
.abc ┊def .
.┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.
. ┊ .
. ┊ .
]
]
scenario editor-in-focus-keeps-cursor [
$close-trace
assume-screen 30/width, 5/height
1:address:array:character <- new [abc]
2:address:array:character <- new [def]
assume-console []
run [
3:address:programming-environment-data <- new-programming-environment screen:address, 1:address:array:character, 2:address:array:character
event-loop screen:address, console:address, 3:address:programming-environment-data
screen:address <- print-character screen:address, 9251/␣
]
screen-should-contain [
. run (F4) .
.␣bc ┊def .
.┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊━━━━━━━━━━━━━━.
. ┊ .
]
assume-console [
type [z]
]
run [
3:address:programming-environment-data <- new-programming-environment screen:address, 1:address:array:character, 2:address:array:character
event-loop screen:address, console:address, 3:address:programming-environment-data
screen:address <- print-character screen:address, 9251/␣
]
screen-should-contain [
. run (F4) .
.z␣bc ┊def .
.┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊━━━━━━━━━━━━━━.
. ┊ .
]
]
scenario backspace-in-sandbox-editor-joins-lines [
$close-trace
assume-screen 30/width, 5/height
1:address:array:character <- new []
2:address:array:character <- new [abc
def]
assume-console [
left-click 2, 16
type [«]
]
3:event/backspace <- merge 0/text, 8/backspace, 0/dummy, 0/dummy
replace-in-console 171/«, 3:event/backspace
run [
4:address:programming-environment-data <- new-programming-environment screen:address, 1:address:array:character, 2:address:array:character
event-loop screen:address, console:address, 4:address:programming-environment-data
screen:address <- print-character screen:address, 9251/␣
]
screen-should-contain [
. run (F4) .
. ┊abc␣ef .
.┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊━━━━━━━━━━━━━━.
. ┊ .
]
]
recipe render-all [
local-scope
screen:address <- next-ingredient
env:address:programming-environment-data <- next-ingredient
screen <- render-recipes screen, env
screen <- render-sandbox-side screen, env
recipes:address:editor-data <- get *env, recipes:offset
current-sandbox:address:editor-data <- get *env, current-sandbox:offset
sandbox-in-focus?:boolean <- get *env, sandbox-in-focus?:offset
update-cursor screen, recipes, current-sandbox, sandbox-in-focus?
show-screen screen
reply screen/same-as-ingredient:0
]
recipe render-minimal [
local-scope
screen:address <- next-ingredient
env:address:programming-environment-data <- next-ingredient
recipes:address:editor-data <- get *env, recipes:offset
current-sandbox:address:editor-data <- get *env, current-sandbox:offset
sandbox-in-focus?:boolean <- get *env, sandbox-in-focus?:offset
{
break-if sandbox-in-focus?
screen <- render-recipes screen, env
cursor-row:number <- get *recipes, cursor-row:offset
cursor-column:number <- get *recipes, cursor-column:offset
}
{
break-unless sandbox-in-focus?
screen <- render-sandbox-side screen, env
cursor-row:number <- get *current-sandbox, cursor-row:offset
cursor-column:number <- get *current-sandbox, cursor-column:offset
}
move-cursor screen, cursor-row, cursor-column
show-screen screen
reply screen/same-as-ingredient:0
]
recipe render-recipes [
local-scope
screen:address <- next-ingredient
env:address:programming-environment-data <- next-ingredient
recipes:address:editor-data <- get *env, recipes:offset
left:number <- get *recipes, left:offset
right:number <- get *recipes, right:offset
row:number, screen <- render screen, recipes
recipe-warnings:address:array:character <- get *env, recipe-warnings:offset
{
break-unless recipe-warnings
row, screen <- render-string screen, recipe-warnings, left, right, 1/red, row
}
{
break-if recipe-warnings
row <- add row, 1
}
draw-horizontal screen, row, left, right, 9480/horizontal-dotted
clear-rest-of-screen screen, row, left, right
reply screen/same-as-ingredient:0
]
recipe clear-rest-of-screen [
local-scope
screen:address <- next-ingredient
row:number <- next-ingredient
left:number <- next-ingredient
right:number <- next-ingredient
row <- add row, 1
move-cursor screen, row, left
screen-height:number <- screen-height screen
{
at-bottom-of-screen?:boolean <- greater-or-equal row, screen-height
break-if at-bottom-of-screen?
move-cursor screen, row, left
clear-line-delimited screen, left, right
row <- add row, 1
loop
}
]
recipe update-cursor [
local-scope
screen:address <- next-ingredient
recipes:address:editor-data <- next-ingredient
current-sandbox:address:editor-data <- next-ingredient
sandbox-in-focus?:boolean <- next-ingredient
{
break-if sandbox-in-focus?
cursor-row:number <- get *recipes, cursor-row:offset
cursor-column:number <- get *recipes, cursor-column:offset
}
{
break-unless sandbox-in-focus?
cursor-row:number <- get *current-sandbox, cursor-row:offset
cursor-column:number <- get *current-sandbox, cursor-column:offset
}
move-cursor screen, cursor-row, cursor-column
]
container sandbox-data [
data:address:array:character
response:address:array:character
warnings:address:array:character
trace:address:array:character
expected-response:address:array:character
starting-row-on-screen:number
response-starting-row-on-screen:number
display-trace?:boolean
screen:address:screen
next-sandbox:address:sandbox-data
]
scenario run-and-show-results [
$close-trace
assume-screen 100/width, 15/height
1:address:array:character <- new []
2:address:array:character <- new [divide-with-remainder 11, 3]
3:address:programming-environment-data <- new-programming-environment screen:address, 1:address:array:character, 2:address:array:character
assume-console [
press 65532
]
run [
event-loop screen:address, console:address, 3:address:programming-environment-data
]
screen-should-contain [
. run (F4) .
. ┊ .
.┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.
. ┊ x.
. ┊divide-with-remainder 11, 3 .
. ┊3 .
. ┊2 .
. ┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.
. ┊ .
]
screen-should-contain-in-color 7/white, [
. .
. .
. .
. .
. divide-with-remainder 11, 3 .
. .
. .
. .
. .
]
screen-should-contain-in-color 245/grey, [
. .
. ┊ .
.┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.
. ┊ x.
. ┊ .
. ┊3 .
. ┊2 .
. ┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.
. ┊ .
]
assume-console [
left-click 1, 80
type [add 2, 2]
press 65532
]
run [
event-loop screen:address, console:address, 3:address:programming-environment-data
]
screen-should-contain [
. run (F4) .
. ┊ .
.┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.
. ┊ x.
. ┊add 2, 2 .
. ┊4 .
. ┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.
. ┊ x.
. ┊divide-with-remainder 11, 3 .
. ┊3 .
. ┊2 .
. ┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.
. ┊ .
]
]
after +global-keypress [
{
do-run?:boolean <- equal *k, 65532/F4
break-unless do-run?
run-sandboxes env
screen <- render-all screen, env
update-cursor screen, recipes, current-sandbox, *sandbox-in-focus?
show-screen screen
loop +next-event:label
}
]
recipe run-sandboxes [
local-scope
env:address:programming-environment-data <- next-ingredient
recipes:address:editor-data <- get *env, recipes:offset
in:address:array:character <- editor-contents recipes
save [recipes.mu], in
recipe-warnings:address:address:array:character <- get-address *env, recipe-warnings:offset
*recipe-warnings <- reload in
reply-if *recipe-warnings
current-sandbox:address:editor-data <- get *env, current-sandbox:offset
{
sandbox-contents:address:array:character <- editor-contents current-sandbox
break-unless sandbox-contents
new-sandbox:address:sandbox-data <- new sandbox-data:type
data:address:address:array:character <- get-address *new-sandbox, data:offset
*data <- copy sandbox-contents
dest:address:address:sandbox-data <- get-address *env, sandbox:offset
next:address:address:sandbox-data <- get-address *new-sandbox, next-sandbox:offset
*next <- copy *dest
*dest <- copy new-sandbox
init:address:address:duplex-list <- get-address *current-sandbox, data:offset
*init <- push-duplex 167/§, 0/tail
top-of-screen:address:address:duplex-list <- get-address *current-sandbox, top-of-screen:offset
*top-of-screen <- copy *init
}
save-sandboxes env
curr:address:sandbox-data <- get *env, sandbox:offset
{
break-unless curr
data <- get-address *curr, data:offset
response:address:address:array:character <- get-address *curr, response:offset
warnings:address:address:array:character <- get-address *curr, warnings:offset
trace:address:address:array:character <- get-address *curr, trace:offset
fake-screen:address:address:screen <- get-address *curr, screen:offset
*response, *warnings, *fake-screen, *trace <- run-interactive *data
curr <- get *curr, next-sandbox:offset
loop
}
]
recipe save-sandboxes [
local-scope
env:address:programming-environment-data <- next-ingredient
current-sandbox:address:editor-data <- get *env, current-sandbox:offset
$system [rm lesson/[0-9]* >/dev/null 2>/dev/null]
curr:address:sandbox-data <- get *env, sandbox:offset
suffix:address:array:character <- new [.out]
idx:number <- copy 0
{
break-unless curr
data:address:array:character <- get *curr, data:offset
filename:address:array:character <- integer-to-decimal-string idx
save filename, data
{
expected-response:address:array:character <- get *curr, expected-response:offset
break-unless expected-response
filename <- string-append filename, suffix
save filename, expected-response
}
idx <- add idx, 1
curr <- get *curr, next-sandbox:offset
loop
}
]
recipe render-sandbox-side [
local-scope
screen:address <- next-ingredient
env:address:programming-environment-data <- next-ingredient
current-sandbox:address:editor-data <- get *env, current-sandbox:offset
left:number <- get *current-sandbox, left:offset
right:number <- get *current-sandbox, right:offset
row:number, screen <- render screen, current-sandbox
row <- add row, 1
draw-horizontal screen, row, left, right, 9473/horizontal-double
sandbox:address:sandbox-data <- get *env, sandbox:offset
row, screen <- render-sandboxes screen, sandbox, left, right, row
row <- add row, 1
move-cursor screen, row, left
screen-height:number <- screen-height screen
{
at-bottom-of-screen?:boolean <- greater-or-equal row, screen-height
break-if at-bottom-of-screen?
move-cursor screen, row, left
clear-line-delimited screen, left, right
row <- add row, 1
loop
}
reply screen/same-as-ingredient:0
]
recipe render-sandboxes [
local-scope
screen:address <- next-ingredient
sandbox:address:sandbox-data <- next-ingredient
left:number <- next-ingredient
right:number <- next-ingredient
row:number <- next-ingredient
reply-unless sandbox, row/same-as-ingredient:4, screen/same-as-ingredient:0
screen-height:number <- screen-height screen
at-bottom?:boolean <- greater-or-equal row, screen-height
reply-if at-bottom?:boolean, row/same-as-ingredient:4, screen/same-as-ingredient:0
row <- add row, 1
move-cursor screen, row, left
clear-line-delimited screen, left, right
print-character screen, 120/x, 245/grey
starting-row:address:number <- get-address *sandbox, starting-row-on-screen:offset
*starting-row <- copy row
sandbox-data:address:array:character <- get *sandbox, data:offset
row, screen <- render-string screen, sandbox-data, left, right, 7/white, row
response-starting-row:address:number <- get-address *sandbox, response-starting-row-on-screen:offset
sandbox-response:address:array:character <- get *sandbox, response:offset
sandbox-warnings:address:array:character <- get *sandbox, warnings:offset
sandbox-screen:address <- get *sandbox, screen:offset
+render-sandbox-results
{
break-unless sandbox-warnings
*response-starting-row <- copy 0
row, screen <- render-string screen, sandbox-warnings, left, right, 1/red, row
}
{
break-if sandbox-warnings
empty-screen?:boolean <- fake-screen-is-empty? sandbox-screen
break-if empty-screen?
row, screen <- render-screen screen, sandbox-screen, left, right, row
}
{
break-if sandbox-warnings
break-unless empty-screen?
*response-starting-row <- add row, 1
+render-sandbox-response
row, screen <- render-string screen, sandbox-response, left, right, 245/grey, row
}
+render-sandbox-end
at-bottom?:boolean <- greater-or-equal row, screen-height
reply-if at-bottom?, row/same-as-ingredient:4, screen/same-as-ingredient:0
draw-horizontal screen, row, left, right, 9473/horizontal-double
next-sandbox:address:sandbox-data <- get *sandbox, next-sandbox:offset
row, screen <- render-sandboxes screen, next-sandbox, left, right, row
reply row/same-as-ingredient:4, screen/same-as-ingredient:0
]
recipe restore-sandboxes [
local-scope
env:address:programming-environment-data <- next-ingredient
suffix:address:array:character <- new [.out]
idx:number <- copy 0
curr:address:address:sandbox-data <- get-address *env, sandbox:offset
{
filename:address:array:character <- integer-to-decimal-string idx
contents:address:array:character <- restore filename
break-unless contents
*curr <- new sandbox-data:type
data:address:address:array:character <- get-address **curr, data:offset
*data <- copy contents
{
filename <- string-append filename, suffix
contents <- restore filename
break-unless contents
expected-response:address:address:array:character <- get-address **curr, expected-response:offset
*expected-response <- copy contents
}
+continue
idx <- add idx, 1
curr <- get-address **curr, next-sandbox:offset
loop
}
reply env/same-as-ingredient:0
]
recipe render-screen [
local-scope
screen:address <- next-ingredient
s:address:screen <- next-ingredient
left:number <- next-ingredient
right:number <- next-ingredient
row:number <- next-ingredient
row <- add row, 1
reply-unless s, row/same-as-ingredient:4, screen/same-as-ingredient:0
header:address:array:character <- new [screen:]
row <- subtract row, 1
row <- render-string screen, header, left, right, 245/grey, row
row <- add row, 1
move-cursor screen, row, left
column:number <- copy left
s-width:number <- screen-width s
s-height:number <- screen-height s
buf:address:array:screen-cell <- get *s, data:offset
stop-printing:number <- add left, s-width, 3
max-column:number <- min stop-printing, right
i:number <- copy 0
len:number <- length *buf
screen-height:number <- screen-height screen
{
done?:boolean <- greater-or-equal i, len
break-if done?
done? <- greater-or-equal row, screen-height
break-if done?
column <- copy left
move-cursor screen, row, column
print-character screen, 32/space, 245/grey
print-character screen, 32/space, 245/grey
print-character screen, 46/full-stop, 245/grey
column <- add left, 3
{
row-done?:boolean <- greater-or-equal column, max-column
break-if row-done?
curr:screen-cell <- index *buf, i
c:character <- get curr, contents:offset
print-character screen, c, 245/grey
column <- add column, 1
i <- add i, 1
loop
}
print-character screen, 46/full-stop, 245/grey
column <- add column, 1
{
line-done?:boolean <- greater-than column, right
break-if line-done?
print-character screen, 32/space
column <- add column, 1
loop
}
row <- add row, 1
loop
}
reply row/same-as-ingredient:4, screen/same-as-ingredient:0
]
scenario run-updates-results [
$close-trace
assume-screen 100/width, 12/height
1:address:array:character <- new [
recipe foo [
z:number <- add 2, 2
]]
2:address:array:character <- new [foo]
3:address:programming-environment-data <- new-programming-environment screen:address, 1:address:array:character, 2:address:array:character
assume-console [
press 65532
]
run [
event-loop screen:address, console:address, 3:address:programming-environment-data
]
screen-should-contain [
. run (F4) .
. ┊ .
.recipe foo [ ┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.
.z:number <- add 2, 2 ┊ x.
.] ┊foo .
.┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊4 .
. ┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.
. ┊ .
]
assume-console [
left-click 3, 28
type [«3]
press 65532
]
4:event/backspace <- merge 0/text, 8/backspace, 0/dummy, 0/dummy
replace-in-console 171/«, 4:event/backspace
run [
event-loop screen:address, console:address, 3:address:programming-environment-data
]
screen-should-contain [
. run (F4) .
. ┊ .
.recipe foo [ ┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.
.z:number <- add 2, 3 ┊ x.
.] ┊foo .
.┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊5 .
. ┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.
. ┊ .
]
]
scenario run-instruction-and-print-warnings [
$close-trace
assume-screen 100/width, 10/height
1:address:array:character <- new []
2:address:array:character <- new [get 1234:number, foo:offset]
3:address:programming-environment-data <- new-programming-environment screen:address, 1:address:array:character, 2:address:array:character
assume-console [
press 65532
]
run [
event-loop screen:address, console:address, 3:address:programming-environment-data
]
screen-should-contain [
. run (F4) .
. ┊ .
.┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.
. ┊ x.
. ┊get 1234:number, foo:offset .
. ┊unknown element foo in container number .
. ┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.
. ┊ .
]
screen-should-contain-in-color 7/white, [
. .
. .
. .
. .
. get 1234:number, foo:offset .
. .
. .
. .
]
screen-should-contain-in-color 1/red, [
. .
. .
. .
. .
. .
. unknown element foo in container number .
. .
]
screen-should-contain-in-color 245/grey, [
. .
. ┊ .
.┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.
. ┊ x.
. ┊ .
. ┊ .
. ┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.
. ┊ .
]
]
scenario run-instruction-and-print-warnings-only-once [
$close-trace
assume-screen 100/width, 10/height
1:address:array:character <- new []
2:address:array:character <- new [get 1234:number, foo:offset]
3:address:programming-environment-data <- new-programming-environment screen:address, 1:address:array:character, 2:address:array:character
assume-console [
press 65532
press 65532
]
run [
event-loop screen:address, console:address, 3:address:programming-environment-data
]
screen-should-contain [
. run (F4) .
. ┊ .
.┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.
. ┊ x.
. ┊get 1234:number, foo:offset .
. ┊unknown element foo in container number .
. ┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.
. ┊ .
]
]
scenario run-instruction-manages-screen-per-sandbox [
$close-trace
assume-screen 100/width, 20/height
1:address:array:character <- new []
2:address:array:character <- new [print-integer screen:address, 4]
3:address:programming-environment-data <- new-programming-environment screen:address, 1:address:array:character, 2:address:array:character
assume-console [
press 65532
]
run [
event-loop screen:address, console:address, 3:address:programming-environment-data
]
screen-should-contain [
. run (F4) .
. ┊ .
.┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.
. ┊ x.
. ┊print-integer screen:address, 4 .
. ┊screen: .
. ┊ .4 . .
. ┊ . . .
. ┊ . . .
. ┊ . . .
. ┊ . . .
. ┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.
. ┊ .
]
]
recipe editor-contents [
local-scope
editor:address:editor-data <- next-ingredient
buf:address:buffer <- new-buffer 80
curr:address:duplex-list <- get *editor, data:offset
assert curr, [editor without data is illegal; must have at least a sentinel]
curr <- next-duplex curr
reply-unless curr, 0
{
break-unless curr
c:character <- get *curr, value:offset
buffer-append buf, c
curr <- next-duplex curr
loop
}
result:address:array:character <- buffer-to-array buf
reply result
]
scenario editor-provides-edited-contents [
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
assume-console [
left-click 1, 2
type [def]
]
run [
editor-event-loop screen:address, console:address, 2:address:editor-data
3:address:array:character <- editor-contents 2:address:editor-data
4:array:character <- copy *3:address:array:character
]
memory-should-contain [
4:string <- [abdefc]
]
]
scenario clicking-on-a-sandbox-moves-it-to-editor [
$close-trace
assume-screen 40/width, 10/height
1:address:array:character <- new [
recipe foo [
add 2, 2
]]
2:address:array:character <- new [foo]
assume-console [
press 65532
]
3:address:programming-environment-data <- new-programming-environment screen:address, 1:address:array:character, 2:address:array:character
event-loop screen:address, console:address, 3:address:programming-environment-data
screen-should-contain [
. run (F4) .
. ┊ .
.recipe foo [ ┊━━━━━━━━━━━━━━━━━━━.
. add 2, 2 ┊ x.
.] ┊foo .
.┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊4 .
. ┊━━━━━━━━━━━━━━━━━━━.
. ┊ .
]
assume-console [
left-click 3, 30
]
run [
event-loop screen:address, console:address, 3:address:programming-environment-data
]
screen-should-contain [
. run (F4) .
. ┊foo .
.recipe foo [ ┊━━━━━━━━━━━━━━━━━━━.
. add 2, 2 ┊ .
.] ┊ .
.┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊ .
. ┊ .
. ┊ .
]
]
after +global-touch [
{
sandbox-left-margin:number <- get *current-sandbox, left:offset
click-column:number <- get *t, column:offset
on-sandbox-side?:boolean <- greater-or-equal click-column, sandbox-left-margin
break-unless on-sandbox-side?
first-sandbox:address:sandbox-data <- get *env, sandbox:offset
break-unless first-sandbox
first-sandbox-begins:number <- get *first-sandbox, starting-row-on-screen:offset
click-row:number <- get *t, row:offset
below-sandbox-editor?:boolean <- greater-or-equal click-row, first-sandbox-begins
break-unless below-sandbox-editor?
empty-sandbox-editor?:boolean <- empty-editor? current-sandbox
break-unless empty-sandbox-editor?
sandbox:address:sandbox-data <- extract-sandbox env, click-row
text:address:array:character <- get *sandbox, data:offset
current-sandbox <- insert-text current-sandbox, text
screen <- render-sandbox-side screen, env
update-cursor screen, recipes, current-sandbox, *sandbox-in-focus?
show-screen screen
loop +next-event:label
}
]
recipe empty-editor? [
local-scope
editor:address:editor-data <- next-ingredient
head:address:duplex-list <- get *editor, data:offset
first:address:duplex-list <- next-duplex head
result:boolean <- not first
reply result
]
recipe extract-sandbox [
local-scope
env:address:programming-environment-data <- next-ingredient
click-row:number <- next-ingredient
sandbox:address:address:sandbox-data <- get-address *env, sandbox:offset
start:number <- get **sandbox, starting-row-on-screen:offset
clicked-on-sandboxes?:boolean <- greater-or-equal click-row, start
assert clicked-on-sandboxes?, [extract-sandbox called on click to sandbox editor]
{
next-sandbox:address:sandbox-data <- get **sandbox, next-sandbox:offset
break-unless next-sandbox
next-start:number <- get *next-sandbox, starting-row-on-screen:offset
found?:boolean <- lesser-than click-row, next-start
break-if found?
sandbox <- get-address **sandbox, next-sandbox:offset
loop
}
result:address:sandbox-data <- copy *sandbox
*sandbox <- copy next-sandbox
reply result
]
scenario deleting-sandboxes [
$close-trace
assume-screen 100/width, 15/height
1:address:array:character <- new []
2:address:array:character <- new []
3:address:programming-environment-data <- new-programming-environment screen:address, 1:address:array:character, 2:address:array:character
assume-console [
left-click 1, 80
type [divide-with-remainder 11, 3]
press 65532
type [add 2, 2]
press 65532
]
run [
event-loop screen:address, console:address, 3:address:programming-environment-data
]
screen-should-contain [
. run (F4) .
. ┊ .
.┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.
. ┊ x.
. ┊add 2, 2 .
. ┊4 .
. ┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.
. ┊ x.
. ┊divide-with-remainder 11, 3 .
. ┊3 .
. ┊2 .
. ┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.
. ┊ .
]
assume-console [
left-click 7, 99
]
run [
event-loop screen:address, console:address, 3:address:programming-environment-data
]
screen-should-contain [
. run (F4) .
. ┊ .
.┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.
. ┊ x.
. ┊add 2, 2 .
. ┊4 .
. ┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.
. ┊ .
. ┊ .
]
assume-console [
left-click 3, 99
]
run [
event-loop screen:address, console:address, 3:address:programming-environment-data
]
screen-should-contain [
. run (F4) .
. ┊ .
.┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.
. ┊ .
. ┊ .
]
]
after +global-touch [
{
was-delete?:boolean <- delete-sandbox *t, env
break-unless was-delete?
screen <- render-sandbox-side screen, env
update-cursor screen, recipes, current-sandbox, *sandbox-in-focus?
show-screen screen
loop +next-event:label
}
]
recipe delete-sandbox [
local-scope
t:touch-event <- next-ingredient
env:address:programming-environment-data <- next-ingredient
click-column:number <- get t, column:offset
current-sandbox:address:editor-data <- get *env, current-sandbox:offset
right:number <- get *current-sandbox, right:offset
at-right?:boolean <- equal click-column, right
reply-unless at-right?, 0/false
click-row:number <- get t, row:offset
prev:address:address:sandbox-data <- get-address *env, sandbox:offset
curr:address:sandbox-data <- get *env, sandbox:offset
{
break-unless curr
{
target-row:number <- get *curr, starting-row-on-screen:offset
delete-curr?:boolean <- equal target-row, click-row
break-unless delete-curr?
*prev <- get *curr, next-sandbox:offset
reply 1/true
}
prev <- get-address *curr, next-sandbox:offset
curr <- get *curr, next-sandbox:offset
loop
}
reply 0/false
]
scenario sandbox-click-on-result-toggles-color-to-green [
$close-trace
assume-screen 40/width, 10/height
1:address:array:character <- new [
recipe foo [
add 2, 2
]]
2:address:array:character <- new [foo]
assume-console [
press 65532
]
3:address:programming-environment-data <- new-programming-environment screen:address, 1:address:array:character, 2:address:array:character
event-loop screen:address, console:address, 3:address:programming-environment-data
screen-should-contain [
. run (F4) .
. ┊ .
.recipe foo [ ┊━━━━━━━━━━━━━━━━━━━.
. add 2, 2 ┊ x.
.] ┊foo .
.┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊4 .
. ┊━━━━━━━━━━━━━━━━━━━.
. ┊ .
]
assume-console [
left-click 5, 21
]
run [
event-loop screen:address, console:address, 3:address:programming-environment-data
]
screen-should-contain-in-color 2/green, [
. .
. .
. .
. .
. .
. 4 .
. .
. .
]
assume-console [
left-click 3, 11
type [«3]
press 65532
]
4:event/backspace <- merge 0/text, 8/backspace, 0/dummy, 0/dummy
replace-in-console 171/«, 4:event/backspace
run [
event-loop screen:address, console:address, 3:address:programming-environment-data
]
screen-should-contain-in-color 1/red, [
. .
. .
. .
. .
. .
. 5 .
. .
. .
]
]
after +global-touch [
{
sandbox-left-margin:number <- get *current-sandbox, left:offset
click-column:number <- get *t, column:offset
on-sandbox-side?:boolean <- greater-or-equal click-column, sandbox-left-margin
break-unless on-sandbox-side?
first-sandbox:address:sandbox-data <- get *env, sandbox:offset
break-unless first-sandbox
first-sandbox-begins:number <- get *first-sandbox, starting-row-on-screen:offset
click-row:number <- get *t, row:offset
below-sandbox-editor?:boolean <- greater-or-equal click-row, first-sandbox-begins
break-unless below-sandbox-editor?
sandbox:address:sandbox-data <- find-click-in-sandbox-output env, click-row
break-unless sandbox
sandbox <- toggle-expected-response sandbox
save-sandboxes env
screen <- render-sandbox-side screen, env, 1/clear
show-screen screen
loop +next-event:label
}
]
recipe find-click-in-sandbox-output [
local-scope
env:address:programming-environment-data <- next-ingredient
click-row:number <- next-ingredient
sandbox:address:sandbox-data <- get *env, sandbox:offset
start:number <- get *sandbox, starting-row-on-screen:offset
clicked-on-sandboxes?:boolean <- greater-or-equal click-row, start
assert clicked-on-sandboxes?, [extract-sandbox called on click to sandbox editor]
{
next-sandbox:address:sandbox-data <- get *sandbox, next-sandbox:offset
break-unless next-sandbox
next-start:number <- get *next-sandbox, starting-row-on-screen:offset
found?:boolean <- lesser-than click-row, next-start
break-if found?
sandbox <- copy next-sandbox
loop
}
response-starting-row:number <- get *sandbox, response-starting-row-on-screen:offset
click-in-response?:boolean <- greater-or-equal click-row, response-starting-row
{
break-if click-in-response?
reply 0/no-click-in-sandbox-output
}
reply sandbox
]
recipe toggle-expected-response [
local-scope
sandbox:address:sandbox-data <- next-ingredient
expected-response:address:address:array:character <- get-address *sandbox, expected-response:offset
{
break-unless *expected-response
*expected-response <- copy 0
reply sandbox/same-as-ingredient:0
}
response:address:array:character <- get *sandbox, response:offset
*expected-response <- copy response
reply sandbox/same-as-ingredient:0
]
after +render-sandbox-response [
{
break-unless sandbox-response
expected-response:address:array:character <- get *sandbox, expected-response:offset
break-unless expected-response
response-is-expected?:boolean <- string-equal expected-response, sandbox-response
{
break-if response-is-expected?:boolean
row, screen <- render-string screen, sandbox-response, left, right, 1/red, row
}
{
break-unless response-is-expected?:boolean
row, screen <- render-string screen, sandbox-response, left, right, 2/green, row
}
jump +render-sandbox-end:label
}
]
scenario sandbox-click-on-code-toggles-app-trace [
$close-trace
assume-screen 40/width, 10/height
1:address:array:character <- new [
recipe foo [
trace [abc]
]]
2:address:array:character <- new [foo]
assume-console [
press 65532
]
3:address:programming-environment-data <- new-programming-environment screen:address, 1:address:array:character, 2:address:array:character
event-loop screen:address, console:address, 3:address:programming-environment-data
screen-should-contain [
. run (F4) .
. ┊ .
.recipe foo [ ┊━━━━━━━━━━━━━━━━━━━.
. trace [abc] ┊ x.
.] ┊foo .
.┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊━━━━━━━━━━━━━━━━━━━.
. ┊ .
]
assume-console [
left-click 4, 21
]
run [
event-loop screen:address, console:address, 3:address:programming-environment-data
]
screen-should-contain [
. run (F4) .
. ┊ .
.recipe foo [ ┊━━━━━━━━━━━━━━━━━━━.
. trace [abc] ┊ x.
.] ┊foo .
.┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊abc .
. ┊━━━━━━━━━━━━━━━━━━━.
. ┊ .
]
screen-should-contain-in-color 245/grey, [
. .
. ┊ .
. ┊━━━━━━━━━━━━━━━━━━━.
. ┊ x.
. ┊ .
.┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊abc .
. ┊━━━━━━━━━━━━━━━━━━━.
. ┊ .
]
assume-console [
left-click 4, 25
]
run [
event-loop screen:address, console:address, 3:address:programming-environment-data
]
screen-should-contain [
. run (F4) .
. ┊ .
.recipe foo [ ┊━━━━━━━━━━━━━━━━━━━.
. trace [abc] ┊ x.
.] ┊foo .
.┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊━━━━━━━━━━━━━━━━━━━.
. ┊ .
]
]
scenario sandbox-shows-app-trace-and-result [
$close-trace
assume-screen 40/width, 10/height
1:address:array:character <- new [
recipe foo [
trace [abc]
add 2, 2
]]
2:address:array:character <- new [foo]
assume-console [
press 65532
]
3:address:programming-environment-data <- new-programming-environment screen:address, 1:address:array:character, 2:address:array:character
event-loop screen:address, console:address, 3:address:programming-environment-data
screen-should-contain [
. run (F4) .
. ┊ .
.recipe foo [ ┊━━━━━━━━━━━━━━━━━━━.
. trace [abc] ┊ x.
. add 2, 2 ┊foo .
.] ┊4 .
.┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊━━━━━━━━━━━━━━━━━━━.
. ┊ .
]
assume-console [
left-click 4, 21
]
run [
event-loop screen:address, console:address, 3:address:programming-environment-data
]
screen-should-contain [
. run (F4) .
. ┊ .
.recipe foo [ ┊━━━━━━━━━━━━━━━━━━━.
. trace [abc] ┊ x.
. add 2, 2 ┊foo .
.] ┊abc .
.┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊4 .
. ┊━━━━━━━━━━━━━━━━━━━.
. ┊ .
]
]
after +global-touch [
{
sandbox-left-margin:number <- get *current-sandbox, left:offset
click-column:number <- get *t, column:offset
on-sandbox-side?:boolean <- greater-or-equal click-column, sandbox-left-margin
break-unless on-sandbox-side?
first-sandbox:address:sandbox-data <- get *env, sandbox:offset
break-unless first-sandbox
first-sandbox-begins:number <- get *first-sandbox, starting-row-on-screen:offset
click-row:number <- get *t, row:offset
below-sandbox-editor?:boolean <- greater-or-equal click-row, first-sandbox-begins
break-unless below-sandbox-editor?
sandbox:address:sandbox-data <- find-click-in-sandbox-code env, click-row
break-unless sandbox
x:address:boolean <- get-address *sandbox, display-trace?:offset
*x <- not *x
screen <- render-sandbox-side screen, env, 1/clear
show-screen screen
loop +next-event:label
}
]
recipe find-click-in-sandbox-code [
local-scope
env:address:programming-environment-data <- next-ingredient
click-row:number <- next-ingredient
sandbox:address:sandbox-data <- get *env, sandbox:offset
start:number <- get *sandbox, starting-row-on-screen:offset
clicked-on-sandboxes?:boolean <- greater-or-equal click-row, start
assert clicked-on-sandboxes?, [extract-sandbox called on click to sandbox editor]
{
next-sandbox:address:sandbox-data <- get *sandbox, next-sandbox:offset
break-unless next-sandbox
next-start:number <- get *next-sandbox, starting-row-on-screen:offset
found?:boolean <- lesser-than click-row, next-start
break-if found?
sandbox <- copy next-sandbox
loop
}
response-starting-row:number <- get *sandbox, response-starting-row-on-screen:offset
click-above-response?:boolean <- lesser-than click-row, response-starting-row
start:number <- get *sandbox, starting-row-on-screen:offset
click-below-menu?:boolean <- greater-than click-row, start
click-on-sandbox-code?:boolean <- and click-above-response?, click-below-menu?
{
break-if click-on-sandbox-code?
reply 0/no-click-in-sandbox-output
}
reply sandbox
]
after +render-sandbox-results [
{
display-trace?:boolean <- get *sandbox, display-trace?:offset
break-unless display-trace?
sandbox-trace:address:array:character <- get *sandbox, trace:offset
break-unless sandbox-trace
row, screen <- render-string, screen, sandbox-trace, left, right, 245/grey, row
row <- subtract row, 1
}
]
scenario run-shows-warnings-in-get [
$close-trace
assume-screen 100/width, 15/height
assume-console [
press 65532
]
run [
x:address:array:character <- new [
recipe foo [
get 123:number, foo:offset
]]
y:address:array:character <- new [foo]
env:address:programming-environment-data <- new-programming-environment screen:address, x:address:array:character, y:address:array:character
event-loop screen:address, console:address, env:address:programming-environment-data
]
screen-should-contain [
. run (F4) .
. ┊foo .
.recipe foo [ ┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.
. get 123:number, foo:offset ┊ .
.] ┊ .
.unknown element foo in container number ┊ .
.┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊ .
. ┊ .
]
screen-should-contain-in-color 1/red, [
. .
. .
. .
. .
. .
.unknown element foo in container number .
. .
]
]
scenario run-shows-missing-type-warnings [
$close-trace
assume-screen 100/width, 15/height
assume-console [
press 65532
]
run [
x:address:array:character <- new [
recipe foo [
x <- copy 0
]]
y:address:array:character <- new [foo]
env:address:programming-environment-data <- new-programming-environment screen:address, x:address:array:character, y:address:array:character
event-loop screen:address, console:address, env:address:programming-environment-data
]
screen-should-contain [
. run (F4) .
. ┊foo .
.recipe foo [ ┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.
. x <- copy 0 ┊ .
.] ┊ .
.missing type in 'x <- copy 0' ┊ .
.┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊ .
. ┊ .
]
]
scenario run-shows-get-on-non-container-warnings [
$close-trace
assume-screen 100/width, 15/height
assume-console [
press 65532
]
run [
x:address:array:character <- new [
recipe foo [
x:address:point <- new point:type
get x:address:point, 1:offset
]]
y:address:array:character <- new [foo]
env:address:programming-environment-data <- new-programming-environment screen:address, x:address:array:character, y:address:array:character
event-loop screen:address, console:address, env:address:programming-environment-data
]
screen-should-contain [
. run (F4) .
. ┊ .
.recipe foo [ ┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.
. x:address:point <- new point:type ┊ x.
. get x:address:point, 1:offset ┊foo .
.] ┊foo: first ingredient of 'get' should be a conta↩.
.┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊iner, but got x:address:point .
. ┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.
. ┊ .
]
]
scenario run-shows-non-literal-get-argument-warnings [
$close-trace
assume-screen 100/width, 15/height
assume-console [
press 65532
]
run [
x:address:array:character <- new [
recipe foo [
x:number <- copy 0
y:address:point <- new point:type
get *y:address:point, x:number
]]
y:address:array:character <- new [foo]
env:address:programming-environment-data <- new-programming-environment screen:address, x:address:array:character, y:address:array:character
event-loop screen:address, console:address, env:address:programming-environment-data
]
screen-should-contain [
. run (F4) .
. ┊foo .
.recipe foo [ ┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.
. x:number <- copy 0 ┊ .
. y:address:point <- new point:type ┊ .
. get *y:address:point, x:number ┊ .
.] ┊ .
.foo: expected ingredient 1 of 'get' to have type ↩┊ .
.'offset'; got x:number ┊ .
.┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊ .
. ┊ .
]
]
recipe draw-box [
local-scope
screen:address <- next-ingredient
top:number <- next-ingredient
left:number <- next-ingredient
bottom:number <- next-ingredient
right:number <- next-ingredient
color:number, color-found?:boolean <- next-ingredient
{
break-if color-found?
color <- copy 245/grey
}
draw-horizontal screen, top, left, right, color
draw-horizontal screen, bottom, left, right, color
draw-vertical screen, left, top, bottom, color
draw-vertical screen, right, top, bottom, color
draw-top-left screen, top, left, color
draw-top-right screen, top, right, color
draw-bottom-left screen, bottom, left, color
draw-bottom-right screen, bottom, right, color
move-cursor screen, top, left
cursor-down screen
cursor-right screen
]
recipe draw-horizontal [
local-scope
screen:address <- next-ingredient
row:number <- next-ingredient
x:number <- next-ingredient
right:number <- next-ingredient
style:character, style-found?:boolean <- next-ingredient
{
break-if style-found?
style <- copy 9472/horizontal
}
color:number, color-found?:boolean <- next-ingredient
{
break-if color-found?
color <- copy 245/grey
}
bg-color:number, bg-color-found?:boolean <- next-ingredient
{
break-if bg-color-found?
bg-color <- copy 0/black
}
move-cursor screen, row, x
{
continue?:boolean <- lesser-or-equal x, right
break-unless continue?
print-character screen, style, color, bg-color
x <- add x, 1
loop
}
]
recipe draw-vertical [
local-scope
screen:address <- next-ingredient
col:number <- next-ingredient
y:number <- next-ingredient
bottom:number <- next-ingredient
style:character, style-found?:boolean <- next-ingredient
{
break-if style-found?
style <- copy 9474/vertical
}
color:number, color-found?:boolean <- next-ingredient
{
break-if color-found?
color <- copy 245/grey
}
{
continue?:boolean <- lesser-than y, bottom
break-unless continue?
move-cursor screen, y, col
print-character screen, style, color
y <- add y, 1
loop
}
]
recipe draw-top-left [
local-scope
screen:address <- next-ingredient
top:number <- next-ingredient
left:number <- next-ingredient
color:number, color-found?:boolean <- next-ingredient
{
break-if color-found?
color <- copy 245/grey
}
move-cursor screen, top, left
print-character screen, 9484/down-right, color
]
recipe draw-top-right [
local-scope
screen:address <- next-ingredient
top:number <- next-ingredient
right:number <- next-ingredient
color:number, color-found?:boolean <- next-ingredient
{
break-if color-found?
color <- copy 245/grey
}
move-cursor screen, top, right
print-character screen, 9488/down-left, color
]
recipe draw-bottom-left [
local-scope
screen:address <- next-ingredient
bottom:number <- next-ingredient
left:number <- next-ingredient
color:number, color-found?:boolean <- next-ingredient
{
break-if color-found?
color <- copy 245/grey
}
move-cursor screen, bottom, left
print-character screen, 9492/up-right, color
]
recipe draw-bottom-right [
local-scope
screen:address <- next-ingredient
bottom:number <- next-ingredient
right:number <- next-ingredient
color:number, color-found?:boolean <- next-ingredient
{
break-if color-found?
color <- copy 245/grey
}
move-cursor screen, bottom, right
print-character screen, 9496/up-left, color
]
recipe print-string-with-gradient-background [
local-scope
screen:address <- next-ingredient
s:address:array:character <- next-ingredient
color:number <- next-ingredient
bg-color1:number <- next-ingredient
bg-color2:number <- next-ingredient
len:number <- length *s
color-range:number <- subtract bg-color2, bg-color1
color-quantum:number <- divide color-range, len
bg-color:number <- copy bg-color1
i:number <- copy 0
{
done?:boolean <- greater-or-equal i, len
break-if done?
c:character <- index *s, i
print-character screen, c, color, bg-color
i <- add i, 1
bg-color <- add bg-color, color-quantum
loop
}
reply screen/same-as-ingredient:0
]