## the basic editor data structure, and how it displays text to the screen # temporary main for this layer: just render the given text at the given # screen dimensions, then stop def! 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-text-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 [ # top line of screen reserved for menu . . .abc . . . ] ] container editor-data [ # editable text: doubly linked list of characters (head contains a special sentinel) data:address:duplex-list:character top-of-screen:address:duplex-list:character bottom-of-screen:address:duplex-list:character # location before cursor inside data before-cursor:address:duplex-list:character # raw bounds of display area on screen # always displays from row 1 (leaving row 0 for a menu) and at most until bottom of screen left:number right:number bottom:number # raw screen coordinates of cursor cursor-row:number cursor-column:number ] # creates a new editor widget and renders its initial appearance to screen # top/left/right constrain the screen area available to the new editor # right is exclusive def new-editor s:address:array:character, screen:address:screen, left:number, right:number -> result:address:editor-data, screen:address:screen [ local-scope load-ingredients # no clipping of bounds right <- subtract right, 1 result <- new editor-data:type # initialize screen-related fields *result <- put *result, left:offset, left *result <- put *result, right:offset, right # initialize cursor coordinates *result <- put *result, cursor-row:offset, 1/top *result <- put *result, cursor-column:offset, left # initialize empty contents init:address:duplex-list:character <- push 167/§, 0/tail *result <- put *result, data:offset, init *result <- put *result, top-of-screen:offset, init *result <- put *result, before-cursor:offset, init result <- insert-text result, s # initial render to screen, just for some old tests _, _, screen, result <- render screen, result ] def insert-text editor:address:editor-data, text:address:array:character -> editor:address:editor-data [ local-scope load-ingredients # early exit if text is empty return-unless text, editor/same-as-ingredient:0 len:number <- length *text return-unless len, editor/same-as-ingredient:0 idx:number <- copy 0 # now we can start appending the rest, character by character 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 c, curr # next iter curr <- next curr idx <- add idx, 1 loop } return 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 [ # 2 (data) <- just the § sentinel # 3 (top of screen) <- the § sentinel 4 <- 0 # bottom-of-screen; null since text fits on screen # 5 (before cursor) <- the § sentinel 6 <- 2 # left 7 <- 4 # right (inclusive) 8 <- 1 # bottom 9 <- 1 # cursor row 10 <- 2 # cursor column ] screen-should-contain [ . . . . . . ] ] # Assumes cursor should be at coordinates (cursor-row, cursor-column) and # updates before-cursor to match. Might also move coordinates if they're # outside text. def 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 return-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
# read a character from stdin, save it to a local on the stack, write it to stdout

fn main [
  var x : char
  call read 0/stdin, x, 1/size
  result/EBX <- call write 1/stdout, x, 1/size
  call exit-EBX
]

fn read fd : int, x : (address array byte), size : int [
  EBX <- copy fd
  ECX <- copy x
  EDX <- copy size
  EAX <- copy 3/read
  syscall
]

fn write fd : int, x : (address array byte), size : int [
  EBX <- copy fd
  ECX <- copy x
  EDX <- copy size
  EAX <- copy 4/write
  syscall
]

# like exit, but assumes the code is already in EBX
fn exit-EBX [
  code/EAX <- copy 1/exit
  syscall
]
o 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 # cursor row 4 <- 0 # cursor column ] ] # just a little color for mu code 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 [ color <- get-color color, c ] # so far the previous color is all the information we need; that may change def get-color color:number, c:character -> color:number [ local-scope load-ingredients color-is-white?:boolean <- equal color, 7/white # if color is white and next character is '#', switch color to blue { 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 } # if color is blue and next character is newline, switch color to white { 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 } # if color is white (no comments) and next character is '<', switch color to red { break-unless color-is-white? starting-assignment?:boolean <- equal c, 60/< break-unless starting-assignment? color <- copy 1/red jump +exit:label } # if color is red and next character is space, switch color to white { 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 } # otherwise no change +exit return 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, [ . . . . . <- . . . . . ] ]