recipe! main text:address:array:character [
local-scope
load-ingredients
open-console
hide-screen 0/screen
new-editor text, 0/screen, 0/left, 5/right
show-screen 0/screen
wait-for-event 0/console
close-console
]
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:screen, 0/left, 10/right
]
screen-should-contain [
. .
.abc .
. .
]
]
container editor-data [
data:address:duplex-list:character
top-of-screen:address:duplex-list:character
bottom-of-screen:address:duplex-list:character
before-cursor:address:duplex-list:character
left:number
right:number
cursor-row:number
cursor-column:number
]
recipe new-editor s:address:array:character, screen:address:screen, left:number, right:number -> result:address:editor-data [
local-scope
load-ingredients
right <- subtract right, 1
result <- 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:character <- get-address *result, data:offset
*init <- push-duplex 167/§, 0/tail
top-of-screen:address:address:duplex-list:character <- get-address *result, top-of-screen:offset
*top-of-screen <- copy *init
y:address:address:duplex-list:character <- 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, result <- render screen, result
<editor-initialization>
]
recipe insert-text editor:address:editor-data, text:address:array:character -> editor:address:editor-data [
local-scope
load-ingredients
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:character <- 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:screen, 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 screen:address:screen, editor:address:editor-data -> last-row:number, last-column:number, screen:address:screen, editor:address:editor-data [
local-scope
load-ingredients
reply-unless editor, 1/top, 0/left, screen/same-as-ingredient:0, editor/same-as-ingredient:1
left:number <- get *editor, left:offset
screen-height:number <- screen-height screen
right:number <- get *editor, right:offset
curr:address:duplex-list:character <- get *editor, top-of-screen:offset
prev:address:duplex-list:character <- 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:character <- get-address *editor, before-cursor:offset
screen <- 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 <- copy prev
}
c:character <- get *curr, value:offset
<character-c-received>
{
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
screen <- 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
screen <- 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:character <- 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
*before-cursor <- copy prev
}
reply row, column, screen/same-as-ingredient:0, editor/same-as-ingredient:1
]
recipe clear-line-delimited screen:address:screen, column:number, right:number [
local-scope
load-ingredients
{
done?:boolean <- greater-than column, right
break-if done?
print-character screen, 32/space
column <- add column, 1
loop
}
]
recipe clear-screen-from screen:address:screen, row:number, column:number, left:number, right:number -> screen:address:screen [
local-scope
load-ingredients
{
break-if screen
clear-display-from row, column, left, right
reply screen/same-as-ingredient:0
}
screen <- move-cursor screen, row, column
clear-line-delimited screen, column, right
clear-rest-of-screen screen, row, left, right
reply screen/same-as-ingredient:0
]
recipe clear-rest-of-screen screen:address:screen, row:number, left:number, right:number [
local-scope
load-ingredients
row <- add row, 1
screen <- 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?
screen <- move-cursor screen, row, left
clear-line-delimited screen, left, right
row <- add row, 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:screen, 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:screen, 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:screen, 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:screen, 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:screen, 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:screen, 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:screen, 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-received> [
color <- get-color color, c
]
recipe get-color color:number, c:character -> color:number [
local-scope
load-ingredients
color-is-white?:boolean <- equal color, 7/white
{
break-unless color-is-white?
starting-comment?:boolean <- equal c, 35/#
break-unless starting-comment?
trace 90, [app], [switch color back to blue]
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?
trace 90, [app], [switch color back to white]
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:screen, 0/left, 8/right
]
screen-should-contain [
. .
.abc .
.d <- e .
.f .
. .
]
screen-should-contain-in-color 1/red, [
. .
. .
. <- .
. .
. .
]
]