diff options
author | Kartik K. Agaram <vc@akkartik.com> | 2015-11-12 22:31:49 -0800 |
---|---|---|
committer | Kartik K. Agaram <vc@akkartik.com> | 2015-11-12 22:31:49 -0800 |
commit | be422222cd20a0d68cc7600cbe373acd22ecfdc4 (patch) | |
tree | 7766fa1925c55aa5f50aa1783cf439de21c5af3f | |
parent | b61dfa4fd3596d3095c8ff00913cc443dd751ad0 (diff) | |
download | mu-be422222cd20a0d68cc7600cbe373acd22ecfdc4.tar.gz |
2428 - sandbox/ working again
-rw-r--r-- | edit/006-sandbox-edit.mu | 1 | ||||
-rw-r--r-- | sandbox/001-editor.mu | 76 | ||||
-rw-r--r-- | sandbox/002-typing.mu | 127 | ||||
-rw-r--r-- | sandbox/003-shortcuts.mu | 514 | ||||
-rw-r--r-- | sandbox/004-programming-environment.mu | 66 | ||||
-rw-r--r-- | sandbox/005-sandbox.mu | 92 | ||||
-rw-r--r-- | sandbox/006-sandbox-edit.mu | 19 | ||||
-rw-r--r-- | sandbox/007-sandbox-delete.mu | 6 | ||||
-rw-r--r-- | sandbox/008-sandbox-test.mu | 10 | ||||
-rw-r--r-- | sandbox/009-sandbox-trace.mu | 11 | ||||
-rw-r--r-- | sandbox/010-warnings.mu | 12 | ||||
-rw-r--r-- | sandbox/011-editor-undo.mu | 131 |
12 files changed, 521 insertions, 544 deletions
diff --git a/edit/006-sandbox-edit.mu b/edit/006-sandbox-edit.mu index 6f1af1d6..bec144f4 100644 --- a/edit/006-sandbox-edit.mu +++ b/edit/006-sandbox-edit.mu @@ -121,7 +121,6 @@ recipe extract-sandbox env:address:programming-environment-data, click-row:numbe # position cursor in sandbox editor sandbox-in-focus?:address:boolean <- get-address *env, sandbox-in-focus?:offset *sandbox-in-focus? <- copy 1/true - reply result ] scenario sandbox-with-print-can-be-edited [ diff --git a/sandbox/001-editor.mu b/sandbox/001-editor.mu index f89a6ab9..4b8da7a8 100644 --- a/sandbox/001-editor.mu +++ b/sandbox/001-editor.mu @@ -2,9 +2,9 @@ # temporary main for this layer: just render the given string at the given # screen dimensions, then stop -recipe! main [ +recipe! main text:address:array:character [ local-scope - text:address:array:character <- next-ingredient + load-ingredients open-console hide-screen 0/screen new-editor text, 0/screen, 0/left, 5/right @@ -44,19 +44,15 @@ container editor-data [ cursor-column:number ] -# editor:address:editor-data, screen <- new-editor s:address:array:character, screen:address:screen, left:number, right: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. -recipe new-editor [ +# 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 +recipe new-editor s:address:array:character, screen:address:screen, left:number, right:number -> result:address:editor-data [ local-scope - s:address:array:character <- next-ingredient - screen:address:screen <- next-ingredient + load-ingredients # no clipping of bounds - left:number <- next-ingredient - right:number <- next-ingredient right <- subtract right, 1 - result:address:editor-data <- new editor-data:type + result <- new editor-data:type # initialize screen-related fields x:address:number <- get-address *result, left:offset *x <- copy left @@ -67,11 +63,11 @@ recipe new-editor [ *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:address:address:duplex-list:character <- 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:address:address:duplex-list:character <- get-address *result, top-of-screen:offset *top-of-screen <- copy *init - y:address:address:duplex-list <- get-address *result, before-cursor:offset + y:address:address:duplex-list:character <- get-address *result, before-cursor:offset *y <- copy *init result <- insert-text result, s # initialize cursor to top of screen @@ -80,20 +76,18 @@ recipe new-editor [ # initial render to screen, just for some old tests _, _, screen, result <- render screen, result <editor-initialization> - reply result ] -recipe insert-text [ +recipe insert-text editor:address:editor-data, text:address:array:character -> editor:address:editor-data [ local-scope - editor:address:editor-data <- next-ingredient - text:address:array:character <- next-ingredient + load-ingredients # early exit if text is empty reply-unless text, editor/same-as-ingredient:0 len:number <- length *text reply-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 <- get *editor, data:offset + curr:address:duplex-list:character <- get *editor, data:offset { done?:boolean <- greater-or-equal idx, len break-if done? @@ -130,22 +124,19 @@ scenario editor-initializes-without-data [ ] ] -# last-row:number, last-column:number, screen, editor <- render screen:address:screen, editor:address:editor-data -# # 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. -recipe render [ +recipe render screen:address:screen, editor:address:editor-data -> last-row:number, last-column:number, screen:address:screen, editor:address:editor-data [ local-scope - screen:address:screen <- next-ingredient - editor:address:editor-data <- next-ingredient + 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 # traversing editor - curr:address:duplex-list <- get *editor, top-of-screen:offset - prev:address:duplex-list <- copy curr # just in case curr becomes null and we can't compute prev-duplex + curr:address:duplex-list:character <- get *editor, top-of-screen:offset + prev:address:duplex-list:character <- copy curr # just in case curr becomes null and we can't compute prev-duplex curr <- next-duplex curr # traversing screen +render-loop-initialization @@ -154,7 +145,7 @@ recipe render [ 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 + before-cursor:address:address:duplex-list:character <- get-address *editor, before-cursor:offset screen <- move-cursor screen, row, column { +next-character @@ -216,7 +207,7 @@ recipe render [ loop } # save first character off-screen - bottom-of-screen:address:address:duplex-list <- get-address *editor, bottom-of-screen:offset + bottom-of-screen:address:address:duplex-list:character <- get-address *editor, bottom-of-screen:offset *bottom-of-screen <- copy curr # is cursor to the right of the last line? move to end { @@ -233,11 +224,9 @@ recipe render [ reply row, column, screen/same-as-ingredient:0, editor/same-as-ingredient:1 ] -recipe clear-line-delimited [ +recipe clear-line-delimited screen:address:screen, column:number, right:number [ local-scope - screen:address:screen <- next-ingredient - column:number <- next-ingredient - right:number <- next-ingredient + load-ingredients { done?:boolean <- greater-than column, right break-if done? @@ -247,13 +236,9 @@ recipe clear-line-delimited [ } ] -recipe clear-screen-from [ +recipe clear-screen-from screen:address:screen, row:number, column:number, left:number, right:number -> screen:address:screen [ local-scope - screen:address:screen <- next-ingredient - row:number <- next-ingredient - column:number <- next-ingredient - left:number <- next-ingredient - right:number <- next-ingredient + load-ingredients # if it's the real screen, use the optimized primitive { break-if screen @@ -267,12 +252,9 @@ recipe clear-screen-from [ reply screen/same-as-ingredient:0 ] -recipe clear-rest-of-screen [ +recipe clear-rest-of-screen screen:address:screen, row:number, left:number, right:number [ local-scope - screen:address:screen <- next-ingredient - row:number <- next-ingredient - left:number <- next-ingredient - right:number <- next-ingredient + load-ingredients row <- add row, 1 screen <- move-cursor screen, row, left screen-height:number <- screen-height screen @@ -427,12 +409,10 @@ after <character-c-received> [ color <- get-color color, c ] -# color <- get-color color:number, c:character # so far the previous color is all the information we need; that may change -recipe get-color [ +recipe get-color color:number, c:character -> color:number [ local-scope - color:number <- next-ingredient - c:character <- next-ingredient + load-ingredients color-is-white?:boolean <- equal color, 7/white # if color is white and next character is '#', switch color to blue { diff --git a/sandbox/002-typing.mu b/sandbox/002-typing.mu index 45eaaa89..e766926a 100644 --- a/sandbox/002-typing.mu +++ b/sandbox/002-typing.mu @@ -2,20 +2,18 @@ # temporary main: interactive editor # hit ctrl-c to exit -recipe! main [ +recipe! main text:address:array:character [ local-scope - text:address:array:character <- next-ingredient + load-ingredients open-console editor:address:editor-data <- new-editor text, 0/screen, 5/left, 45/right editor-event-loop 0/screen, 0/console, editor close-console ] -recipe editor-event-loop [ +recipe editor-event-loop screen:address:screen, console:address:console, editor:address:editor-data [ local-scope - screen:address:screen <- next-ingredient - console:address:console <- next-ingredient - editor:address:editor-data <- next-ingredient + load-ingredients { # looping over each (keyboard or touch) event as it occurs +next-event @@ -47,11 +45,9 @@ recipe editor-event-loop [ ] # process click, return if it was on current editor -recipe move-cursor-in-editor [ +recipe move-cursor-in-editor screen:address:screen, editor:address:editor-data, t:touch-event -> in-focus?:boolean [ local-scope - screen:address:screen <- next-ingredient - editor:address:editor-data <- next-ingredient - t:touch-event <- next-ingredient + load-ingredients reply-unless editor, 0/false click-row:number <- get t, row:offset reply-unless click-row, 0/false # ignore clicks on 'menu' @@ -71,24 +67,19 @@ recipe move-cursor-in-editor [ reply 1/true ] -# editor <- snap-cursor screen:address:screen, editor:address:editor-data, target-row:number, target-column:number -# # Variant of 'render' that only moves the cursor (coordinates and # before-cursor). If it's past the end of a line, it 'slides' it left. If it's # past the last line it positions at end of last line. -recipe snap-cursor [ +recipe snap-cursor screen:address:screen, editor:address:editor-data, target-row:number, target-column:number -> editor:address:editor-data [ local-scope - screen:address:screen <- next-ingredient - editor:address:editor-data <- next-ingredient - target-row:number <- next-ingredient - target-column:number <- next-ingredient - reply-unless editor, 1/top, editor/same-as-ingredient:1 + load-ingredients + reply-unless editor left:number <- get *editor, left:offset right:number <- get *editor, right:offset screen-height:number <- screen-height screen # count newlines until screen row - curr:address:duplex-list <- get *editor, top-of-screen:offset - prev:address:duplex-list <- copy curr # just in case curr becomes null and we can't compute prev-duplex + curr:address:duplex-list:character <- get *editor, top-of-screen:offset + prev:address:duplex-list:character <- copy curr # just in case curr becomes null and we can't compute prev-duplex curr <- next-duplex curr row:number <- copy 1/top column:number <- copy left @@ -96,7 +87,7 @@ recipe snap-cursor [ *cursor-row <- copy target-row cursor-column:address:number <- get-address *editor, cursor-column:offset *cursor-column <- copy target-column - before-cursor:address:address:duplex-list <- get-address *editor, before-cursor:offset + before-cursor:address:address:duplex-list:character <- get-address *editor, before-cursor:offset { +next-character break-unless curr @@ -160,23 +151,20 @@ recipe snap-cursor [ *cursor-column <- copy column *before-cursor <- copy prev } - reply editor/same-as-ingredient:1 ] -# screen, editor, go-render?:boolean <- handle-keyboard-event screen:address:screen, editor:address:editor-data, e:event # Process an event 'e' and try to minimally update the screen. # Set 'go-render?' to true to indicate the caller must perform a non-minimal update. -recipe handle-keyboard-event [ +recipe handle-keyboard-event screen:address:screen, editor:address:editor-data, e:event -> screen:address:screen, editor:address:editor-data, go-render?:boolean [ local-scope - screen:address:screen <- next-ingredient - editor:address:editor-data <- next-ingredient - e:event <- next-ingredient - reply-unless editor, screen/same-as-ingredient:0, editor/same-as-ingredient:1, 0/no-more-render + load-ingredients + go-render? <- copy 0/false + reply-unless editor screen-width:number <- screen-width screen screen-height:number <- screen-height screen left:number <- get *editor, left:offset right:number <- get *editor, right:offset - before-cursor:address:address:duplex-list <- get-address *editor, before-cursor:offset + before-cursor:address:address:duplex-list:character <- 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 save-row:number <- copy *cursor-row @@ -190,27 +178,27 @@ recipe handle-keyboard-event [ <handle-special-character> # ignore any other special characters regular-character?:boolean <- greater-or-equal *c, 32/space - reply-unless regular-character?, screen/same-as-ingredient:0, editor/same-as-ingredient:1, 0/no-more-render + go-render? <- copy 0/false + reply-unless regular-character? # otherwise type it in <insert-character-begin> editor, screen, go-render?:boolean <- insert-at-cursor editor, *c, screen <insert-character-end> - reply screen/same-as-ingredient:0, editor/same-as-ingredient:1, go-render? + reply } # special key to modify the text or move the cursor k:address:number <- maybe-convert e:event, keycode:variant assert k, [event was of unknown type; neither keyboard nor mouse] # handlers for each special key will go here <handle-special-key> - reply screen/same-as-ingredient:0, editor/same-as-ingredient:1, 1/go-render + go-render? <- copy 1/true + reply ] -recipe insert-at-cursor [ +recipe insert-at-cursor editor:address:editor-data, c:character, screen:address:screen -> editor:address:editor-data, screen:address:screen, go-render?:boolean [ local-scope - editor:address:editor-data <- next-ingredient - c:character <- next-ingredient - screen:address:screen <- next-ingredient - before-cursor:address:address:duplex-list <- get-address *editor, before-cursor:offset + load-ingredients + before-cursor:address:address:duplex-list:character <- 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 @@ -225,7 +213,7 @@ recipe insert-at-cursor [ <insert-character-special-case> # but mostly we'll just move the cursor right *cursor-column <- add *cursor-column, 1 - next:address:duplex-list <- next-duplex *before-cursor + next:address:duplex-list:character <- next-duplex *before-cursor { # at end of all text? no need to scroll? just print the character and leave at-end?:boolean <- equal next, 0/null @@ -237,20 +225,22 @@ recipe insert-at-cursor [ break-if overflow? move-cursor screen, save-row, save-column print-character screen, c - reply editor/same-as-ingredient:0, screen/same-as-ingredient:2, 0/no-more-render + go-render? <- copy 0/false + reply } { # not at right margin? print the character and rest of line break-unless next at-right?:boolean <- greater-or-equal *cursor-column, screen-width break-if at-right? - curr:address:duplex-list <- copy *before-cursor + curr:address:duplex-list:character <- copy *before-cursor move-cursor screen, save-row, save-column curr-column:number <- copy save-column { # hit right margin? give up and let caller render + go-render? <- copy 1/true at-right?:boolean <- greater-than curr-column, right - reply-if at-right?, editor/same-as-ingredient:0, screen/same-as-ingredient:2, 1/go-render + reply-if at-right? break-unless curr # newline? done. currc:character <- get *curr, value:offset @@ -261,16 +251,17 @@ recipe insert-at-cursor [ curr <- next-duplex curr loop } - reply editor/same-as-ingredient:0, screen/same-as-ingredient:2, 0/no-more-render + go-render? <- copy 0/false + reply } - reply editor/same-as-ingredient:0, screen/same-as-ingredient:2, 1/go-render + go-render? <- copy 1/true + reply ] # helper for tests -recipe editor-render [ +recipe editor-render screen:address:screen, editor:address:editor-data -> screen:address:screen [ local-scope - screen:address:screen <- next-ingredient - editor:address:editor-data <- next-ingredient + load-ingredients left:number <- get *editor, left:offset right:number <- get *editor, right:offset row:number, column:number <- render screen, editor @@ -705,7 +696,8 @@ after <insert-character-special-case> [ break-unless below-screen? <scroll-down> } - reply editor/same-as-ingredient:0, screen/same-as-ingredient:2, 1/go-render + go-render? <- copy 1/true + reply } ] @@ -825,17 +817,17 @@ after <handle-special-character> [ <insert-enter-begin> editor <- insert-new-line-and-indent editor, screen <insert-enter-end> - reply screen/same-as-ingredient:0, editor/same-as-ingredient:1, 1/go-render + go-render? <- copy 1/true + reply } ] -recipe insert-new-line-and-indent [ +recipe insert-new-line-and-indent editor:address:editor-data, screen:address:screen -> editor:address:editor-data, screen:address:screen, go-render?:boolean [ local-scope - editor:address:editor-data <- next-ingredient - screen:address:screen <- next-ingredient + load-ingredients 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 + before-cursor:address:address:duplex-list:character <- get-address *editor, before-cursor:offset left:number <- get *editor, left:offset right:number <- get *editor, right:offset screen-height:number <- screen-height screen @@ -849,13 +841,14 @@ recipe insert-new-line-and-indent [ below-screen?:boolean <- greater-or-equal *cursor-row, screen-height # must be equal, never greater break-unless below-screen? <scroll-down> + go-render? <- copy 1/true *cursor-row <- subtract *cursor-row, 1 # bring back into screen range } # indent if necessary indent?:boolean <- get *editor, indent?:offset - reply-unless indent?, editor/same-as-ingredient:0, screen/same-as-ingredient:1 - d:address:duplex-list <- get *editor, data:offset - end-of-previous-line:address:duplex-list <- prev-duplex *before-cursor + reply-unless indent? + d:address:duplex-list:character <- get *editor, data:offset + end-of-previous-line:address:duplex-list:character <- prev-duplex *before-cursor indent:number <- line-indent end-of-previous-line, d i:number <- copy 0 { @@ -865,19 +858,17 @@ recipe insert-new-line-and-indent [ i <- add i, 1 loop } - reply editor/same-as-ingredient:0, screen/same-as-ingredient:1 ] # takes a pointer 'curr' into the doubly-linked list and its sentinel, counts # the number of spaces at the start of the line containing 'curr'. -recipe line-indent [ +recipe line-indent curr:address:duplex-list:character, start:address:duplex-list:character -> result:number [ local-scope - curr:address:duplex-list <- next-ingredient - start:address:duplex-list <- next-ingredient + load-ingredients result:number <- copy 0 - reply-unless curr, result + reply-unless curr at-start?:boolean <- equal curr, start - reply-if at-start?, result + reply-if at-start? { curr <- prev-duplex curr break-unless curr @@ -899,7 +890,6 @@ recipe line-indent [ } loop } - reply result ] scenario editor-moves-cursor-down-after-inserting-newline-2 [ @@ -1004,7 +994,8 @@ after <handle-special-key> [ break-unless paste-start? indent?:address:boolean <- get-address *editor, indent?:offset *indent? <- copy 0/false - reply screen/same-as-ingredient:0, editor/same-as-ingredient:1, 1/go-render + go-render? <- copy 1/true + reply } ] @@ -1014,18 +1005,16 @@ after <handle-special-key> [ break-unless paste-end? indent?:address:boolean <- get-address *editor, indent?:offset *indent? <- copy 1/true - reply screen/same-as-ingredient:0, editor/same-as-ingredient:1, 1/go-render + go-render? <- copy 1/true + reply } ] ## helpers -recipe draw-horizontal [ +recipe draw-horizontal screen:address:screen, row:number, x:number, right:number [ local-scope - screen:address:screen <- next-ingredient - row:number <- next-ingredient - x:number <- next-ingredient - right:number <- next-ingredient + load-ingredients style:character, style-found?:boolean <- next-ingredient { break-if style-found? diff --git a/sandbox/003-shortcuts.mu b/sandbox/003-shortcuts.mu index ac5b468e..f4c480d3 100644 --- a/sandbox/003-shortcuts.mu +++ b/sandbox/003-shortcuts.mu @@ -31,7 +31,8 @@ after <handle-special-character> [ editor, screen, go-render?:boolean <- insert-at-cursor editor, 32/space, screen editor, screen, go-render?:boolean <- insert-at-cursor editor, 32/space, screen <insert-character-end> - reply screen/same-as-ingredient:0, editor/same-as-ingredient:1, 1/go-render + go-render? <- copy 1/true + reply } ] @@ -70,46 +71,48 @@ after <handle-special-character> [ delete-previous-character?:boolean <- equal *c, 8/backspace break-unless delete-previous-character? <backspace-character-begin> - editor, screen, go-render?:boolean, backspaced-cell:address:duplex-list <- delete-before-cursor editor, screen + editor, screen, go-render?:boolean, backspaced-cell:address:duplex-list:character <- delete-before-cursor editor, screen <backspace-character-end> - reply screen/same-as-ingredient:0, editor/same-as-ingredient:1, go-render? + reply } ] -# editor, screen, go-render?:boolean, backspaced-cell:address:duplex-list <- delete-before-cursor editor:address:editor-data, screen # return values: # go-render? - whether caller needs to update the screen # backspaced-cell - value deleted (or 0 if nothing was deleted) so we can save it for undo, etc. -recipe delete-before-cursor [ +recipe delete-before-cursor editor:address:editor-data, screen:address:screen -> editor:address:editor-data, screen:address:screen, go-render?:boolean, backspaced-cell:address:duplex-list:character [ local-scope - editor:address:editor-data <- next-ingredient - screen:address:screen <- next-ingredient - before-cursor:address:address:duplex-list <- get-address *editor, before-cursor:offset + load-ingredients + before-cursor:address:address:duplex-list:character <- get-address *editor, before-cursor:offset # if at start of text (before-cursor at § sentinel), return - prev:address:duplex-list <- prev-duplex *before-cursor - reply-unless prev, editor/same-as-ingredient:0, screen/same-as-ingredient:1, 0/no-more-render, 0/nothing-deleted + prev:address:duplex-list:character <- prev-duplex *before-cursor + go-render?, backspaced-cell <- copy 0/no-more-render, 0/nothing-deleted + reply-unless prev trace 10, [app], [delete-before-cursor] original-row:number <- get *editor, cursor-row:offset editor, scroll?:boolean <- move-cursor-coordinates-left editor - backspaced-cell:address:duplex-list <- copy *before-cursor + backspaced-cell:address:duplex-list:character <- copy *before-cursor remove-duplex *before-cursor # will also neatly trim next/prev pointers in backspaced-cell/*before-cursor *before-cursor <- copy prev - reply-if scroll?, editor/same-as-ingredient:0, 1/go-render, backspaced-cell + go-render? <- copy 1/true + reply-if scroll? screen-width:number <- screen-width screen cursor-row:number <- get *editor, cursor-row:offset cursor-column:number <- get *editor, cursor-column:offset # did we just backspace over a newline? same-row?:boolean <- equal cursor-row, original-row - reply-unless same-row?, editor/same-as-ingredient:0, screen/same-as-ingredient:1, 1/go-render, backspaced-cell + go-render? <- copy 1/true + reply-unless same-row? left:number <- get *editor, left:offset right:number <- get *editor, right:offset - curr:address:duplex-list <- next-duplex *before-cursor + curr:address:duplex-list:character <- next-duplex *before-cursor screen <- move-cursor screen, cursor-row, cursor-column curr-column:number <- copy cursor-column { # hit right margin? give up and let caller render - at-right?:boolean <- greater-or-equal curr-column, screen-width - reply-if at-right?, editor/same-as-ingredient:0, screen/same-as-ingredient:1, 1/go-render, backspaced-cell + at-right?:boolean <- greater-or-equal curr-column, right + go-render? <- copy 1/true + reply-if at-right? break-unless curr # newline? done. currc:character <- get *curr, value:offset @@ -122,13 +125,13 @@ recipe delete-before-cursor [ } # we're guaranteed not to be at the right margin screen <- print-character screen, 32/space - reply editor/same-as-ingredient:0, screen/same-as-ingredient:1, 0/no-more-render, backspaced-cell + go-render? <- copy 0/false ] -recipe move-cursor-coordinates-left [ +recipe move-cursor-coordinates-left editor:address:editor-data -> editor:address:editor-data, go-render?:boolean [ local-scope - editor:address:editor-data <- next-ingredient - before-cursor:address:duplex-list <- get *editor, before-cursor:offset + load-ingredients + before-cursor:address:duplex-list:character <- 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 @@ -138,7 +141,8 @@ recipe move-cursor-coordinates-left [ break-if at-left-margin? trace 10, [app], [decrementing cursor column] *cursor-column <- subtract *cursor-column, 1 - reply editor/same-as-ingredient:0, 0/no-more-render + go-render? <- copy 0/false + reply } # if at left margin, we must move to previous row: top-of-screen?:boolean <- equal *cursor-row, 1 # exclude menu bar @@ -159,28 +163,26 @@ recipe move-cursor-coordinates-left [ break-unless previous-character-is-newline? # compute length of previous line trace 10, [app], [switching to previous line] - d:address:duplex-list <- get *editor, data:offset + d:address:duplex-list:character <- 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, go-render? + reply } # case 2: if previous-character was not newline, we're just at a wrapped line trace 10, [app], [wrapping to previous line] right:number <- get *editor, right:offset *cursor-column <- subtract right, 1 # leave room for wrap icon - reply editor/same-as-ingredient:0, go-render? ] # takes a pointer 'curr' into the doubly-linked list and its sentinel, counts # the length of the previous line before the 'curr' pointer. -recipe previous-line-length [ +recipe previous-line-length curr:address:duplex-list:character, start:address:duplex-list:character -> result:number [ local-scope - curr:address:duplex-list <- next-ingredient - start:address:duplex-list <- next-ingredient + load-ingredients result:number <- copy 0 - reply-unless curr, result + reply-unless curr at-start?:boolean <- equal curr, start - reply-if at-start?, result + reply-if at-start? { curr <- prev-duplex curr break-unless curr @@ -192,7 +194,6 @@ recipe previous-line-length [ result <- add result, 1 loop } - reply result ] scenario editor-clears-last-line-on-backspace [ @@ -222,6 +223,64 @@ cd] ] ] +scenario editor-joins-and-wraps-lines-on-backspace [ + assume-screen 10/width, 5/height + # initialize editor with two long-ish but non-wrapping lines + 1:address:array:character <- new [abc def +ghi jkl] + 2:address:editor-data <- new-editor 1:address:array:character, screen:address:screen, 0/left, 10/right + editor-render screen, 2:address:editor-data + $clear-trace + # position the cursor at the start of the second and hit backspace + assume-console [ + left-click 2, 0 + press backspace + ] + run [ + editor-event-loop screen:address:screen, console:address:console, 2:address:editor-data + ] + # resulting single line should wrap correctly + screen-should-contain [ + . . + .abc defgh↩. + .i jkl . + .┈┈┈┈┈┈┈┈┈┈. + . . + ] +] + +scenario editor-wraps-long-lines-on-backspace [ + assume-screen 10/width, 5/height + # initialize editor in part of the screen with a long line + 1:address:array:character <- new [abc def ghij] + 2:address:editor-data <- new-editor 1:address:array:character, screen:address:screen, 0/left, 8/right + editor-render screen, 2:address:editor-data + # confirm that it wraps + screen-should-contain [ + . . + .abc def↩ . + . ghij . + .┈┈┈┈┈┈┈┈ . + ] + $clear-trace + # position the cursor somewhere in the middle of the top screen line and hit backspace + assume-console [ + left-click 1, 4 + press backspace + ] + run [ + editor-event-loop screen:address:screen, console:address:console, 2:address:editor-data + ] + # resulting single line should wrap correctly and not overflow its bounds + screen-should-contain [ + . . + .abcdef ↩ . + .ghij . + .┈┈┈┈┈┈┈┈ . + . . + ] +] + # delete - delete character at cursor scenario editor-handles-delete-key [ @@ -264,25 +323,26 @@ after <handle-special-key> [ delete-next-character?:boolean <- equal *k, 65522/delete break-unless delete-next-character? <delete-character-begin> - editor, screen, go-render?:boolean, deleted-cell:address:duplex-list <- delete-at-cursor editor, screen + editor, screen, go-render?:boolean, deleted-cell:address:duplex-list:character <- delete-at-cursor editor, screen <delete-character-end> - reply screen/same-as-ingredient:0, editor/same-as-ingredient:1, go-render? + reply } ] -recipe delete-at-cursor [ +recipe delete-at-cursor editor:address:editor-data, screen:address:screen -> editor:address:editor-data, screen:address:screen, go-render?:boolean, deleted-cell:address:duplex-list:character [ local-scope - editor:address:editor-data <- next-ingredient - screen:address:screen <- next-ingredient - before-cursor:address:address:duplex-list <- get-address *editor, before-cursor:offset - candidate:address:duplex-list <- next-duplex *before-cursor - reply-unless candidate, editor/same-as-ingredient:0, screen/same-as-ingredient:1, 0/no-more-render, 0/nothing-deleted - currc:character <- get *candidate, value:offset - remove-duplex candidate + load-ingredients + before-cursor:address:address:duplex-list:character <- get-address *editor, before-cursor:offset + deleted-cell:address:duplex-list:character <- next-duplex *before-cursor + go-render? <- copy 0/false + reply-unless deleted-cell + currc:character <- get *deleted-cell, value:offset + remove-duplex deleted-cell deleted-newline?:boolean <- equal currc, 10/newline - reply-if deleted-newline?, editor/same-as-ingredient:0, screen/same-as-ingredient:1, 1/go-render, candidate/deleted-cell + go-render? <- copy 1/true + reply-if deleted-newline? # wasn't a newline? render rest of line - curr:address:duplex-list <- next-duplex *before-cursor # refresh after remove-duplex above + curr:address:duplex-list:character <- next-duplex *before-cursor # refresh after remove-duplex above cursor-row:address:number <- get-address *editor, cursor-row:offset cursor-column:address:number <- get-address *editor, cursor-column:offset screen <- move-cursor screen, *cursor-row, *cursor-column @@ -291,7 +351,8 @@ recipe delete-at-cursor [ { # hit right margin? give up and let caller render at-right?:boolean <- greater-or-equal curr-column, screen-width - reply-if at-right?, editor/same-as-ingredient:0, screen/same-as-ingredient:1, 1/go-render, candidate/deleted-cell + go-render? <- copy 1/true + reply-if at-right? break-unless curr # newline? done. currc:character <- get *curr, value:offset @@ -304,7 +365,7 @@ recipe delete-at-cursor [ } # we're guaranteed not to be at the right margin screen <- print-character screen, 32/space - reply editor/same-as-ingredient:0, screen/same-as-ingredient:1, 0/no-more-render, candidate/deleted-cell + go-render? <- copy 0/false ] # right arrow @@ -336,7 +397,7 @@ after <handle-special-key> [ move-to-next-character?:boolean <- equal *k, 65514/right-arrow break-unless move-to-next-character? # if not at end of text - next-cursor:address:duplex-list <- next-duplex *before-cursor + next-cursor:address:duplex-list:character <- next-duplex *before-cursor break-unless next-cursor # scan to next character <move-cursor-begin> @@ -345,15 +406,14 @@ after <handle-special-key> [ screen <- move-cursor screen, *cursor-row, *cursor-column undo-coalesce-tag:number <- copy 2/right-arrow <move-cursor-end> - reply screen/same-as-ingredient:0, editor/same-as-ingredient:1, go-render? + reply } ] -recipe move-cursor-coordinates-right [ +recipe move-cursor-coordinates-right editor:address:editor-data, screen-height:number -> editor:address:editor-data, go-render?:boolean [ local-scope - editor:address:editor-data <- next-ingredient - screen-height:number <- next-ingredient - before-cursor:address:duplex-list <- get *editor before-cursor:offset + load-ingredients + before-cursor:address:duplex-list:character <- 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 @@ -366,10 +426,12 @@ recipe move-cursor-coordinates-right [ *cursor-row <- add *cursor-row, 1 *cursor-column <- copy left below-screen?:boolean <- greater-or-equal *cursor-row, screen-height # must be equal - reply-unless below-screen?, editor/same-as-ingredient:0, 0/no-more-render + go-render? <- copy 0/false + reply-unless below-screen? <scroll-down> *cursor-row <- subtract *cursor-row, 1 # bring back into screen range - reply editor/same-as-ingredient:0, 1/go-render + go-render? <- copy 1/true + reply } # if the line wraps, move cursor to start of next row { @@ -378,7 +440,7 @@ recipe move-cursor-coordinates-right [ at-wrap?:boolean <- equal *cursor-column, wrap-column break-unless at-wrap? # and if next character isn't newline - next:address:duplex-list <- next-duplex before-cursor + next:address:duplex-list:character <- next-duplex before-cursor break-unless next next-character:character <- get *next, value:offset newline?:boolean <- equal next-character, 10/newline @@ -389,11 +451,12 @@ recipe move-cursor-coordinates-right [ reply-unless below-screen?, editor/same-as-ingredient:0, 0/no-more-render <scroll-down> *cursor-row <- subtract *cursor-row, 1 # bring back into screen range - reply editor/same-as-ingredient:0, 1/go-render + go-render? <- copy 1/true + reply } # otherwise move cursor one character right *cursor-column <- add *cursor-column, 1 - reply editor/same-as-ingredient:0, 0/no-more-render + go-render? <- copy 0/false ] scenario editor-moves-cursor-to-next-line-with-right-arrow [ @@ -611,14 +674,15 @@ after <handle-special-key> [ break-unless move-to-previous-character? trace 10, [app], [left arrow] # if not at start of text (before-cursor at § sentinel) - prev:address:duplex-list <- prev-duplex *before-cursor - reply-unless prev, screen/same-as-ingredient:0, editor/same-as-ingredient:1, 0/no-more-render + prev:address:duplex-list:character <- prev-duplex *before-cursor + go-render? <- copy 0/false + reply-unless prev <move-cursor-begin> editor, go-render? <- move-cursor-coordinates-left editor *before-cursor <- copy prev undo-coalesce-tag:number <- copy 1/left-arrow <move-cursor-end> - reply screen/same-as-ingredient:0, editor/same-as-ingredient:1, go-render? + reply } ] @@ -811,16 +875,16 @@ after <handle-special-key> [ editor, go-render? <- move-to-previous-line editor undo-coalesce-tag:number <- copy 3/up-arrow <move-cursor-end> - reply screen/same-as-ingredient:0, editor/same-as-ingredient:1, go-render? + reply } ] -recipe move-to-previous-line [ +recipe move-to-previous-line editor:address:editor-data -> editor:address:editor-data, go-render?:boolean [ local-scope - editor:address:editor-data <- next-ingredient + load-ingredients 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 + before-cursor:address:address:duplex-list:character <- get-address *editor, before-cursor:offset left:number <- get *editor, left:offset right:number <- get *editor, right:offset already-at-top?:boolean <- lesser-or-equal *cursor-row, 1/top @@ -830,21 +894,23 @@ recipe move-to-previous-line [ # if not at newline, move to start of line (previous newline) # then scan back another line # if either step fails, give up without modifying cursor or coordinates - curr:address:duplex-list <- copy *before-cursor + curr:address:duplex-list:character <- copy *before-cursor { - old:address:duplex-list <- copy curr + old:address:duplex-list:character <- copy curr c2:character <- get *curr, value:offset at-newline?:boolean <- equal c2, 10/newline break-if at-newline? - curr:address:duplex-list <- before-previous-line curr, editor + curr:address:duplex-list:character <- before-previous-line curr, editor no-motion?:boolean <- equal curr, old - reply-if no-motion?, editor/same-as-ingredient:0, 0/no-more-render + go-render? <- copy 0/false + reply-if no-motion? } { old <- copy curr curr <- before-previous-line curr, editor no-motion?:boolean <- equal curr, old - reply-if no-motion?, editor/same-as-ingredient:0, 0/no-more-render + go-render? <- copy 0/false + reply-if no-motion? } *before-cursor <- copy curr *cursor-row <- subtract *cursor-row, 1 @@ -854,7 +920,7 @@ recipe move-to-previous-line [ { done?:boolean <- greater-or-equal *cursor-column, target-column break-if done? - curr:address:duplex-list <- next-duplex *before-cursor + curr:address:duplex-list:character <- next-duplex *before-cursor break-unless curr currc:character <- get *curr, value:offset at-newline?:boolean <- equal currc, 10/newline @@ -864,13 +930,15 @@ recipe move-to-previous-line [ *cursor-column <- add *cursor-column, 1 loop } - reply editor/same-as-ingredient:0, 0/no-more-render + go-render? <- copy 0/false + reply } { # if cursor already at top, scroll up break-unless already-at-top? <scroll-up> - reply editor/same-as-ingredient:0, 1/go-render + go-render? <- copy 1/true + reply } ] @@ -1032,17 +1100,16 @@ after <handle-special-key> [ editor, go-render? <- move-to-next-line editor, screen-height undo-coalesce-tag:number <- copy 4/down-arrow <move-cursor-end> - reply screen/same-as-ingredient:0, editor/same-as-ingredient:1, go-render? + reply } ] -recipe move-to-next-line [ +recipe move-to-next-line editor:address:editor-data, screen-height:number -> editor:address:editor-data, go-render?:boolean [ local-scope - editor:address:editor-data <- next-ingredient - screen-height:number <- next-ingredient + load-ingredients 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 + before-cursor:address:address:duplex-list:character <- get-address *editor, before-cursor:offset left:number <- get *editor, left:offset right:number <- get *editor, right:offset last-line:number <- subtract screen-height, 1 @@ -1052,7 +1119,7 @@ recipe move-to-next-line [ break-if already-at-bottom? # scan to start of next line, then to right column or until end of line max:number <- subtract right, left - next-line:address:duplex-list <- before-start-of-next-line *before-cursor, max + next-line:address:duplex-list:character <- before-start-of-next-line *before-cursor, max { # already at end of buffer? try to scroll up (so we can see more # warnings or sandboxes below) @@ -1060,7 +1127,8 @@ recipe move-to-next-line [ break-unless no-motion? scroll?:boolean <- greater-than *cursor-row, 1 break-if scroll?, +try-to-scroll:label - reply editor/same-as-ingredient:0, 0/no-more-render + go-render? <- copy 0/false + reply } *cursor-row <- add *cursor-row, 1 *before-cursor <- copy next-line @@ -1069,7 +1137,7 @@ recipe move-to-next-line [ { done?:boolean <- greater-or-equal *cursor-column, target-column break-if done? - curr:address:duplex-list <- next-duplex *before-cursor + curr:address:duplex-list:character <- next-duplex *before-cursor break-unless curr currc:character <- get *curr, value:offset at-newline?:boolean <- equal currc, 10/newline @@ -1079,11 +1147,12 @@ recipe move-to-next-line [ *cursor-column <- add *cursor-column, 1 loop } - reply editor/same-as-ingredient:0, 0/no-more-render + go-render? <- copy 0/false + reply } +try-to-scroll <scroll-down> - reply editor/same-as-ingredient:0, 1/go-render + go-render? <- copy 1/true ] scenario editor-adjusts-column-at-next-line [ @@ -1122,71 +1191,6 @@ de] ] ] -scenario editor-scrolls-at-end-on-down-arrow [ - 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:screen, 0/left, 10/right - editor-render screen, 2:address:editor-data - $clear-trace - # try to move down past end of text - assume-console [ - left-click 2, 0 - press down-arrow - ] - run [ - editor-event-loop screen:address:screen, console:address:console, 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 scroll, moving cursor to end of text - memory-should-contain [ - 3 <- 1 - 4 <- 2 - ] - assume-console [ - type [0] - ] - run [ - editor-event-loop screen:address:screen, console:address:console, 2:address:editor-data - ] - screen-should-contain [ - . . - .de0 . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - # try to move down again - $clear-trace - assume-console [ - left-click 2, 0 - press down-arrow - ] - run [ - editor-event-loop screen:address:screen, console:address:console, 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 stops scrolling because cursor is already at top - memory-should-contain [ - 3 <- 1 - 4 <- 3 - ] - check-trace-count-for-label 0, [print-character] - assume-console [ - type [1] - ] - run [ - editor-event-loop screen:address:screen, console:address:console, 2:address:editor-data - ] - screen-should-contain [ - . . - .de01 . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] -] - # ctrl-a/home - move cursor to start of line scenario editor-moves-to-start-of-line-with-ctrl-a [ @@ -1222,7 +1226,8 @@ after <handle-special-character> [ move-to-start-of-line editor undo-coalesce-tag:number <- copy 0/never <move-cursor-end> - reply screen/same-as-ingredient:0, editor/same-as-ingredient:1, 0/no-more-render + go-render? <- copy 0/false + reply } ] @@ -1234,20 +1239,21 @@ after <handle-special-key> [ move-to-start-of-line editor undo-coalesce-tag:number <- copy 0/never <move-cursor-end> - reply screen/same-as-ingredient:0, editor/same-as-ingredient:1, 0/no-more-render + go-render? <- copy 0/false + reply } ] -recipe move-to-start-of-line [ +recipe move-to-start-of-line editor:address:editor-data [ local-scope - editor:address:editor-data <- next-ingredient + load-ingredients # update cursor column left:number <- get *editor, left:offset cursor-column:address:number <- get-address *editor, cursor-column:offset *cursor-column <- copy left # update before-cursor - before-cursor:address:address:duplex-list <- get-address *editor, before-cursor:offset - init:address:duplex-list <- get *editor, data:offset + before-cursor:address:address:duplex-list:character <- get-address *editor, before-cursor:offset + init:address:duplex-list:character <- get *editor, data:offset # while not at start of line, move { at-start-of-text?:boolean <- equal *before-cursor, init @@ -1391,7 +1397,8 @@ after <handle-special-character> [ move-to-end-of-line editor undo-coalesce-tag:number <- copy 0/never <move-cursor-end> - reply screen/same-as-ingredient:0, editor/same-as-ingredient:1, 0/no-more-render + go-render? <- copy 0/false + reply } ] @@ -1403,18 +1410,19 @@ after <handle-special-key> [ move-to-end-of-line editor undo-coalesce-tag:number <- copy 0/never <move-cursor-end> - reply screen/same-as-ingredient:0, editor/same-as-ingredient:1, 0/no-more-render + go-render? <- copy 0/false + reply } ] -recipe move-to-end-of-line [ +recipe move-to-end-of-line editor:address:editor-data [ local-scope - editor:address:editor-data <- next-ingredient - before-cursor:address:address:duplex-list <- get-address *editor, before-cursor:offset + load-ingredients + before-cursor:address:address:duplex-list:character <- get-address *editor, before-cursor:offset cursor-column:address:number <- get-address *editor, cursor-column:offset # while not at start of line, move { - next:address:duplex-list <- next-duplex *before-cursor + next:address:duplex-list:character <- next-duplex *before-cursor break-unless next # end of text nextc:character <- get *next, value:offset at-end-of-line?:boolean <- equal nextc, 10/newline @@ -1530,20 +1538,21 @@ after <handle-special-character> [ delete-to-start-of-line?:boolean <- equal *c, 21/ctrl-u break-unless delete-to-start-of-line? <delete-to-start-of-line-begin> - deleted-cells:address:duplex-list <- delete-to-start-of-line editor + deleted-cells:address:duplex-list:character <- delete-to-start-of-line editor <delete-to-start-of-line-end> - reply screen/same-as-ingredient:0, editor/same-as-ingredient:1, 1/go-render + go-render? <- copy 1/true + reply } ] -recipe delete-to-start-of-line [ +recipe delete-to-start-of-line editor:address:editor-data -> result:address:duplex-list:character [ local-scope - editor:address:editor-data <- next-ingredient + load-ingredients # compute range to delete - 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 + init:address:duplex-list:character <- get *editor, data:offset + before-cursor:address:address:duplex-list:character <- get-address *editor, before-cursor:offset + start:address:duplex-list:character <- copy *before-cursor + end:address:duplex-list:character <- next-duplex *before-cursor { at-start-of-text?:boolean <- equal start, init break-if at-start-of-text? @@ -1555,14 +1564,13 @@ recipe delete-to-start-of-line [ loop } # snip it out - result:address:duplex-list <- next-duplex start + result:address:duplex-list:character <- next-duplex start remove-duplex-between start, end # adjust cursor *before-cursor <- copy start left:number <- get *editor, left:offset cursor-column:address:number <- get-address *editor, cursor-column:offset *cursor-column <- copy left - reply result ] scenario editor-deletes-to-start-of-line-with-ctrl-u-2 [ @@ -1664,18 +1672,19 @@ after <handle-special-character> [ delete-to-end-of-line?:boolean <- equal *c, 11/ctrl-k break-unless delete-to-end-of-line? <delete-to-end-of-line-begin> - deleted-cells:address:duplex-list <- delete-to-end-of-line editor + deleted-cells:address:duplex-list:character <- delete-to-end-of-line editor <delete-to-end-of-line-end> - reply screen/same-as-ingredient:0, editor/same-as-ingredient:1, 1/go-render + go-render? <- copy 1/true + reply } ] -recipe delete-to-end-of-line [ +recipe delete-to-end-of-line editor:address:editor-data -> result:address:duplex-list:character [ local-scope - editor:address:editor-data <- next-ingredient + load-ingredients # compute range to delete - start:address:duplex-list <- get *editor, before-cursor:offset - end:address:duplex-list <- next-duplex start + start:address:duplex-list:character <- get *editor, before-cursor:offset + end:address:duplex-list:character <- next-duplex start { at-end-of-text?:boolean <- equal end, 0/null break-if at-end-of-text? @@ -1686,9 +1695,8 @@ recipe delete-to-end-of-line [ loop } # snip it out - result:address:duplex-list <- next-duplex start + result <- next-duplex start remove-duplex-between start, end - reply result ] scenario editor-deletes-to-end-of-line-with-ctrl-k-2 [ @@ -1842,29 +1850,25 @@ d] after <scroll-down> [ trace 10, [app], [scroll down] - top-of-screen:address:address:duplex-list <- get-address *editor, top-of-screen:offset + top-of-screen:address:address:duplex-list:character <- get-address *editor, top-of-screen:offset left:number <- get *editor, left:offset right:number <- get *editor, right:offset max:number <- subtract right, left - old-top:address:duplex-list <- copy *top-of-screen + old-top:address:duplex-list:character <- copy *top-of-screen *top-of-screen <- before-start-of-next-line *top-of-screen, max no-movement?:boolean <- equal old-top, *top-of-screen - # Hack: this reply doesn't match one of the locations of <scroll-down>, - # directly within insert-at-cursor. However, I'm unable to trigger the - # error.. If necessary create a duplicate copy of <scroll-down> with the - # right 'reply-if'. - reply-if no-movement?, editor/same-as-ingredient:0, 0/no-more-render + go-render? <- copy 0/false + reply-if no-movement? ] # takes a pointer into the doubly-linked list, scans ahead at most 'max' # positions until the next newline # beware: never return null pointer. -recipe before-start-of-next-line [ +recipe before-start-of-next-line original:address:duplex-list:character, max:number -> curr:address:duplex-list:character [ local-scope - original:address:duplex-list <- next-ingredient - max:number <- next-ingredient + load-ingredients count:number <- copy 0 - curr:address:duplex-list <- copy original + curr:address:duplex-list:character <- copy original # skip the initial newline if it exists { c:character <- get *curr, value:offset @@ -2085,6 +2089,71 @@ d] ] ] +scenario editor-scrolls-at-end-on-down-arrow [ + 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:screen, 0/left, 10/right + editor-render screen, 2:address:editor-data + $clear-trace + # try to move down past end of text + assume-console [ + left-click 2, 0 + press down-arrow + ] + run [ + editor-event-loop screen:address:screen, console:address:console, 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 scroll, moving cursor to end of text + memory-should-contain [ + 3 <- 1 + 4 <- 2 + ] + assume-console [ + type [0] + ] + run [ + editor-event-loop screen:address:screen, console:address:console, 2:address:editor-data + ] + screen-should-contain [ + . . + .de0 . + .┈┈┈┈┈┈┈┈┈┈. + . . + ] + # try to move down again + $clear-trace + assume-console [ + left-click 2, 0 + press down-arrow + ] + run [ + editor-event-loop screen:address:screen, console:address:console, 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 stops scrolling because cursor is already at top + memory-should-contain [ + 3 <- 1 + 4 <- 3 + ] + check-trace-count-for-label 0, [print-character] + assume-console [ + type [1] + ] + run [ + editor-event-loop screen:address:screen, console:address:console, 2:address:editor-data + ] + screen-should-contain [ + . . + .de01 . + .┈┈┈┈┈┈┈┈┈┈. + . . + ] +] + scenario editor-combines-page-and-line-scroll [ # screen has 1 line for menu + 3 lines assume-screen 10/width, 4/height @@ -2151,33 +2220,33 @@ d] after <scroll-up> [ trace 10, [app], [scroll up] - top-of-screen:address:address:duplex-list <- get-address *editor, top-of-screen:offset - old-top:address:duplex-list <- copy *top-of-screen + top-of-screen:address:address:duplex-list:character <- get-address *editor, top-of-screen:offset + old-top:address:duplex-list:character <- copy *top-of-screen *top-of-screen <- before-previous-line *top-of-screen, editor no-movement?:boolean <- equal old-top, *top-of-screen - reply-if no-movement?, editor/same-as-ingredient:0, 0/no-more-render + go-render? <- copy 0/false + reply-if no-movement? ] # takes a pointer into the doubly-linked list, scans back to before start of # previous *wrapped* line # beware: never return null pointer -recipe before-previous-line [ +recipe before-previous-line curr:address:duplex-list:character, editor:address:editor-data -> curr:address:duplex-list:character [ local-scope - curr:address:duplex-list <- next-ingredient + load-ingredients c:character <- get *curr, value:offset # compute max, number of characters to skip # 1 + len%(width-1) # except rotate second term to vary from 1 to width-1 rather than 0 to width-2 - editor:address:editor-data <- next-ingredient left:number <- get *editor, left:offset right:number <- get *editor, right:offset max-line-length:number <- subtract right, left, -1/exclusive-right, 1/wrap-icon - sentinel:address:duplex-list <- get *editor, data:offset + sentinel:address:duplex-list:character <- get *editor, data:offset len:number <- previous-line-length curr, sentinel { break-if len # empty line; just skip this newline - prev:address:duplex-list <- prev-duplex curr + prev:address:duplex-list:character <- prev-duplex curr reply-unless prev, curr reply prev } @@ -2193,7 +2262,7 @@ recipe before-previous-line [ { done?:boolean <- greater-or-equal count, max break-if done? - prev:address:duplex-list <- prev-duplex curr + prev:address:duplex-list:character <- prev-duplex curr break-unless prev curr <- copy prev count <- add count, 1 @@ -2541,15 +2610,15 @@ after <handle-special-character> [ { page-down?:boolean <- equal *c, 6/ctrl-f break-unless page-down? - top-of-screen:address:address:duplex-list <- get-address *editor, top-of-screen:offset - old-top:address:duplex-list <- copy *top-of-screen + top-of-screen:address:address:duplex-list:character <- get-address *editor, top-of-screen:offset + old-top:address:duplex-list:character <- copy *top-of-screen <move-cursor-begin> page-down editor undo-coalesce-tag:number <- copy 0/never <move-cursor-end> no-movement?:boolean <- equal *top-of-screen, old-top - reply-if no-movement?, screen/same-as-ingredient:0, editor/same-as-ingredient:1, 0/no-more-render - reply screen/same-as-ingredient:0, editor/same-as-ingredient:1, 1/go-render + go-render? <- not no-movement? + reply } ] @@ -2557,28 +2626,28 @@ after <handle-special-key> [ { page-down?:boolean <- equal *k, 65518/page-down break-unless page-down? - top-of-screen:address:address:duplex-list <- get-address *editor, top-of-screen:offset - old-top:address:duplex-list <- copy *top-of-screen + top-of-screen:address:address:duplex-list:character <- get-address *editor, top-of-screen:offset + old-top:address:duplex-list:character <- copy *top-of-screen <move-cursor-begin> page-down editor undo-coalesce-tag:number <- copy 0/never <move-cursor-end> no-movement?:boolean <- equal *top-of-screen, old-top - reply-if no-movement?, screen/same-as-ingredient:0, editor/same-as-ingredient:1, 0/no-more-render - reply screen/same-as-ingredient:0, editor/same-as-ingredient:1, 1/go-render + go-render? <- not no-movement? + reply } ] # page-down skips entire wrapped lines, so it can't scroll past lines # taking up the entire screen -recipe page-down [ +recipe page-down editor:address:editor-data -> editor:address:editor-data [ local-scope - editor:address:editor-data <- next-ingredient + load-ingredients # if editor contents don't overflow screen, do nothing - bottom-of-screen:address:duplex-list <- get *editor, bottom-of-screen:offset - reply-unless bottom-of-screen, editor/same-as-ingredient:0 + bottom-of-screen:address:duplex-list:character <- get *editor, bottom-of-screen:offset + reply-unless bottom-of-screen # if not, position cursor at final character - before-cursor:address:address:duplex-list <- get-address *editor, before-cursor:offset + before-cursor:address:address:duplex-list:character <- get-address *editor, before-cursor:offset *before-cursor <- prev-duplex bottom-of-screen # keep one line in common with previous page { @@ -2589,9 +2658,8 @@ recipe page-down [ } # move cursor and top-of-screen to start of that line move-to-start-of-line editor - top-of-screen:address:address:duplex-list <- get-address *editor, top-of-screen:offset + top-of-screen:address:address:duplex-list:character <- get-address *editor, top-of-screen:offset *top-of-screen <- copy *before-cursor - reply editor/same-as-ingredient:0 ] scenario editor-does-not-scroll-past-end [ @@ -2734,15 +2802,15 @@ after <handle-special-character> [ { page-up?:boolean <- equal *c, 2/ctrl-b break-unless page-up? - top-of-screen:address:address:duplex-list <- get-address *editor, top-of-screen:offset - old-top:address:duplex-list <- copy *top-of-screen + top-of-screen:address:address:duplex-list:character <- get-address *editor, top-of-screen:offset + old-top:address:duplex-list:character <- copy *top-of-screen <move-cursor-begin> editor <- page-up editor, screen-height undo-coalesce-tag:number <- copy 0/never <move-cursor-end> no-movement?:boolean <- equal *top-of-screen, old-top - reply-if no-movement?, screen/same-as-ingredient:0, editor/same-as-ingredient:1, 0/no-more-render - reply screen/same-as-ingredient:0, editor/same-as-ingredient:1, 1/go-render + go-render? <- not no-movement? + reply } ] @@ -2750,36 +2818,34 @@ after <handle-special-key> [ { page-up?:boolean <- equal *k, 65519/page-up break-unless page-up? - top-of-screen:address:address:duplex-list <- get-address *editor, top-of-screen:offset - old-top:address:duplex-list <- copy *top-of-screen + top-of-screen:address:address:duplex-list:character <- get-address *editor, top-of-screen:offset + old-top:address:duplex-list:character <- copy *top-of-screen <move-cursor-begin> editor <- page-up editor, screen-height undo-coalesce-tag:number <- copy 0/never <move-cursor-end> no-movement?:boolean <- equal *top-of-screen, old-top # don't bother re-rendering if nothing changed. todo: test this - reply-if no-movement?, screen/same-as-ingredient:0, editor/same-as-ingredient:1, 0/no-more-render - reply screen/same-as-ingredient:0, editor/same-as-ingredient:1, 1/go-render + go-render? <- not no-movement? + reply } ] -recipe page-up [ +recipe page-up editor:address:editor-data, screen-height:number -> editor:address:editor-data [ local-scope - editor:address:editor-data <- next-ingredient - screen-height:number <- next-ingredient + load-ingredients max:number <- subtract screen-height, 1/menu-bar, 1/overlapping-line count:number <- copy 0 - top-of-screen:address:address:duplex-list <- get-address *editor, top-of-screen:offset + top-of-screen:address:address:duplex-list:character <- get-address *editor, top-of-screen:offset { done?:boolean <- greater-or-equal count, max break-if done? - prev:address:duplex-list <- before-previous-line *top-of-screen, editor + prev:address:duplex-list:character <- before-previous-line *top-of-screen, editor break-unless prev *top-of-screen <- copy prev count <- add count, 1 loop } - reply editor/same-as-ingredient:0 ] scenario editor-can-scroll-up-multiple-pages [ diff --git a/sandbox/004-programming-environment.mu b/sandbox/004-programming-environment.mu index a405c173..353502fe 100644 --- a/sandbox/004-programming-environment.mu +++ b/sandbox/004-programming-environment.mu @@ -19,14 +19,13 @@ container programming-environment-data [ current-sandbox:address:editor-data ] -recipe new-programming-environment [ +recipe new-programming-environment screen:address:screen, initial-sandbox-contents:address:array:character -> result:address:programming-environment-data [ local-scope - screen:address:screen <- next-ingredient - initial-sandbox-contents:address:array:character <- next-ingredient + load-ingredients width:number <- screen-width screen height:number <- screen-height screen # top menu - result:address:programming-environment-data <- new programming-environment-data:type + result <- 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 @@ -37,15 +36,11 @@ recipe new-programming-environment [ # sandbox editor current-sandbox:address:address:editor-data <- get-address *result, current-sandbox:offset *current-sandbox <- new-editor initial-sandbox-contents, screen, 0, width/right - +programming-environment-initialization - reply result ] -recipe event-loop [ +recipe event-loop screen:address:screen, console:address:console, env:address:programming-environment-data [ local-scope - screen:address:screen <- next-ingredient - console:address:console <- next-ingredient - env:address:programming-environment-data <- next-ingredient + load-ingredients current-sandbox:address:editor-data <- get *env, current-sandbox:offset # if we fall behind we'll stop updating the screen, but then we have to # render the entire screen when we catch up. @@ -140,10 +135,9 @@ recipe event-loop [ } ] -recipe resize [ +recipe resize screen:address:screen, env:address:programming-environment-data -> env:address:programming-environment-data [ local-scope - screen:address:screen <- next-ingredient - env:address:programming-environment-data <- next-ingredient + load-ingredients clear-screen screen # update screen dimensions width:number <- screen-width screen # update sandbox editor @@ -155,13 +149,11 @@ recipe resize [ *cursor-row <- copy 1 cursor-column:address:number <- get-address *current-sandbox, cursor-column:offset *cursor-column <- copy 0 - reply env/same-as-ingredient:1 ] -recipe render-all [ +recipe render-all screen:address:screen, env:address:programming-environment-data -> screen:address:screen [ local-scope - screen:address:screen <- next-ingredient - env:address:programming-environment-data <- next-ingredient + load-ingredients trace 10, [app], [render all] hide-screen screen # top menu @@ -182,49 +174,38 @@ recipe render-all [ screen <- update-cursor screen, current-sandbox # show-screen screen - reply screen/same-as-ingredient:0 ] # replaced in a later layer -recipe render-sandbox-side [ +recipe render-sandbox-side screen:address:screen, env:address:programming-environment-data -> screen:address:screen [ local-scope - screen:address:screen <- next-ingredient - env:address:programming-environment-data <- next-ingredient + load-ingredients 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, column:number, screen, current-sandbox <- render screen, current-sandbox clear-line-delimited screen, column, right row <- add row, 1 - # draw solid line after code + # draw solid line after code (you'll see why in later layers) draw-horizontal screen, row, left, right, 9473/horizontal row <- add row, 1 clear-screen-from screen, row, left, left, right - reply screen/same-as-ingredient:0 ] -recipe update-cursor [ +recipe update-cursor screen:address:screen, current-sandbox:address:editor-data -> screen:address:screen [ local-scope - screen:address:screen <- next-ingredient - current-sandbox:address:editor-data <- next-ingredient + load-ingredients cursor-row:number <- get *current-sandbox, cursor-row:offset cursor-column:number <- get *current-sandbox, cursor-column:offset screen <- move-cursor screen, cursor-row, cursor-column - reply screen/same-as-ingredient:0 ] -# row, screen <- render-string screen:address:screen, s:address:array:character, left:number, right:number, color:number, row:number # print a string 's' to 'editor' in 'color' starting at 'row' # clear rest of last line, move cursor to next line -recipe render-string [ +recipe render-string screen:address:screen, s:address:array:character, left:number, right:number, color:number, row:number -> row:number, screen:address:screen [ local-scope - screen:address:screen <- next-ingredient - s:address:array:character <- next-ingredient - left:number <- next-ingredient - right:number <- next-ingredient - color:number <- next-ingredient - row:number <- next-ingredient - reply-unless s, row/same-as-ingredient:5, screen/same-as-ingredient:0 + load-ingredients + reply-unless s column:number <- copy left screen <- move-cursor screen, row, column screen-height:number <- screen-height screen @@ -277,19 +258,13 @@ recipe render-string [ row <- add row, 1 } move-cursor screen, row, left - reply row/same-as-ingredient:5, screen/same-as-ingredient:0 ] -# row, screen <- render-code-string screen:address:screen, s:address:array:character, left:number, right:number, row:number # like 'render-string' but with colorization for comments like in the editor -recipe render-code-string [ +recipe render-code-string screen:address:screen, s:address:array:character, left:number, right:number, row:number -> row:number, screen:address:screen [ local-scope - screen:address:screen <- next-ingredient - s:address:array:character <- next-ingredient - left:number <- next-ingredient - right:number <- next-ingredient - row:number <- next-ingredient - reply-unless s, row/same-as-ingredient:4, screen/same-as-ingredient:0 + load-ingredients + reply-unless s color:number <- copy 7/white column:number <- copy left screen <- move-cursor screen, row, column @@ -344,7 +319,6 @@ recipe render-code-string [ row <- add row, 1 } move-cursor screen, row, left - reply row/same-as-ingredient:4, screen/same-as-ingredient:0 ] # ctrl-l - redraw screen (just in case it printed junk somehow) diff --git a/sandbox/005-sandbox.mu b/sandbox/005-sandbox.mu index 91bbc1f0..bbd3f8f8 100644 --- a/sandbox/005-sandbox.mu +++ b/sandbox/005-sandbox.mu @@ -114,12 +114,11 @@ after <global-keypress> [ } ] -recipe run-sandboxes [ +recipe run-sandboxes env:address:programming-environment-data, screen:address:screen -> errors-found?:boolean, env:address:programming-environment-data, screen:address:screen [ local-scope - env:address:programming-environment-data <- next-ingredient - screen:address:screen <- next-ingredient - stop?:boolean, env, screen <- update-recipes env, screen - reply-if stop?, 1/errors-found, env/same-as-ingredient:0, screen/same-as-ingredient:1 + load-ingredients + errors-found?:boolean, env, screen <- update-recipes env, screen + reply-if errors-found? # check contents of editor current-sandbox:address:editor-data <- get *env, current-sandbox:offset { @@ -136,9 +135,9 @@ recipe run-sandboxes [ *next <- copy *dest *dest <- copy new-sandbox # clear sandbox editor - init:address:address:duplex-list <- get-address *current-sandbox, data:offset + init:address:address:duplex-list:character <- 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:address:address:duplex-list:character <- get-address *current-sandbox, top-of-screen:offset *top-of-screen <- copy *init } # save all sandboxes before running, just in case we die when running @@ -147,7 +146,7 @@ recipe run-sandboxes [ curr:address:sandbox-data <- get *env, sandbox:offset { break-unless curr - update-sandbox curr, env + curr <- update-sandbox curr, env curr <- get *curr, next-sandbox:offset loop } @@ -155,39 +154,35 @@ recipe run-sandboxes [ ] # load code from recipes.mu -# replaced in a later layer -recipe update-recipes [ +# replaced in a later layer (whereupon errors-found? will actually be set) +recipe update-recipes env:address:programming-environment-data, screen:address:screen -> errors-found?:boolean, env:address:programming-environment-data, screen:address:screen [ local-scope - env:address:programming-environment-data <- next-ingredient - screen:address:screen <- next-ingredient + load-ingredients in:address:array:character <- restore [recipes.mu] # newlayer: persistence reload in - reply 0/no-errors-found, env/same-as-ingredient:0, screen/same-as-ingredient:1 + errors-found? <- copy 0/false ] # replaced in a later layer -recipe update-sandbox [ +recipe update-sandbox sandbox:address:sandbox-data -> sandbox:address:sandbox-data [ local-scope - sandbox:address:sandbox-data <- next-ingredient + load-ingredients data:address:array:character <- get *sandbox, data:offset response:address:address:array:character <- get-address *sandbox, response:offset fake-screen:address:address:screen <- get-address *sandbox, screen:offset *response, _, *fake-screen <- run-interactive data ] -recipe update-status [ +recipe update-status screen:address:screen, msg:address:array:character, color:number -> screen:address:screen [ local-scope - screen:address:screen <- next-ingredient - msg:address:array:character <- next-ingredient - color:number <- next-ingredient + load-ingredients screen <- move-cursor screen, 0, 2 screen <- print-string screen, msg, color, 238/grey/background - reply screen/same-as-ingredient:0 ] -recipe save-sandboxes [ +recipe save-sandboxes env:address:programming-environment-data [ local-scope - env:address:programming-environment-data <- next-ingredient + load-ingredients current-sandbox:address:editor-data <- get *env, current-sandbox:offset # first clear previous versions, in case we deleted some sandbox $system [rm lesson/[0-9]* >/dev/null 2>/dev/null] # some shells can't handle '>&' @@ -211,10 +206,9 @@ recipe save-sandboxes [ } ] -recipe! render-sandbox-side [ +recipe! render-sandbox-side screen:address:screen, env:address:programming-environment-data -> screen:address:screen [ local-scope - screen:address:screen <- next-ingredient - env:address:programming-environment-data <- next-ingredient + load-ingredients trace 11, [app], [render sandbox side] current-sandbox:address:editor-data <- get *env, current-sandbox:offset left:number <- get *current-sandbox, left:offset @@ -226,21 +220,16 @@ recipe! render-sandbox-side [ sandbox:address:sandbox-data <- get *env, sandbox:offset row, screen <- render-sandboxes screen, sandbox, left, right, row, env clear-rest-of-screen screen, row, left, left, right - reply screen/same-as-ingredient:0 ] -recipe render-sandboxes [ +recipe render-sandboxes screen:address:screen, sandbox:address:sandbox-data, left:number, right:number, row:number -> row:number, screen:address:screen [ local-scope - screen:address:screen <- next-ingredient - sandbox:address:sandbox-data <- next-ingredient - left:number <- next-ingredient - right:number <- next-ingredient - row:number <- next-ingredient + load-ingredients env:address:programming-environment-data, _/optional <- next-ingredient - reply-unless sandbox, row/same-as-ingredient:4, screen/same-as-ingredient:0 + reply-unless sandbox 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 + reply-if at-bottom?:boolean # render sandbox menu row <- add row, 1 screen <- move-cursor screen, row, left @@ -280,13 +269,12 @@ recipe render-sandboxes [ # draw next sandbox 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 ] # assumes programming environment has no sandboxes; restores them from previous session -recipe restore-sandboxes [ +recipe restore-sandboxes env:address:programming-environment-data -> env:address:programming-environment-data [ local-scope - env:address:programming-environment-data <- next-ingredient + load-ingredients # read all scenarios, pushing them to end of a list of scenarios suffix:address:array:character <- new [.out] idx:number <- copy 0 @@ -312,29 +300,23 @@ recipe restore-sandboxes [ curr <- get-address **curr, next-sandbox:offset loop } - reply env/same-as-ingredient:0 ] -# row, screen <- render-screen screen:address:screen, sandbox-screen:address:screen, left:number, right:number, row:number # print the fake sandbox screen to 'screen' with appropriate delimiters # leave cursor at start of next line -recipe render-screen [ +recipe render-screen screen:address:screen, sandbox-screen:address:screen, left:number, right:number, row:number -> row:number, screen:address:screen [ local-scope - screen:address:screen <- next-ingredient - s:address:screen <- next-ingredient - left:number <- next-ingredient - right:number <- next-ingredient - row:number <- next-ingredient - reply-unless s, row/same-as-ingredient:4, screen/same-as-ingredient:0 + load-ingredients + reply-unless sandbox-screen # print 'screen:' header:address:array:character <- new [screen:] row <- render-string screen, header, left, right, 245/grey, row screen <- move-cursor screen, row, left - # start printing s + # start printing sandbox-screen 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 + s-width:number <- screen-width sandbox-screen + s-height:number <- screen-height sandbox-screen + buf:address:array:screen-cell <- get *sandbox-screen, data:offset stop-printing:number <- add left, s-width, 3 max-column:number <- min stop-printing, right i:number <- copy 0 @@ -384,7 +366,6 @@ recipe render-screen [ row <- add row, 1 loop } - reply row/same-as-ingredient:4, screen/same-as-ingredient:0 ] scenario run-instruction-manages-screen-per-sandbox [ @@ -418,11 +399,11 @@ scenario run-instruction-manages-screen-per-sandbox [ ] ] -recipe editor-contents [ +recipe editor-contents editor:address:editor-data -> result:address:array:character [ local-scope - editor:address:editor-data <- next-ingredient + load-ingredients buf:address:buffer <- new-buffer 80 - curr:address:duplex-list <- get *editor, data:offset + curr:address:duplex-list:character <- get *editor, data:offset # skip § sentinel assert curr, [editor without data is illegal; must have at least a sentinel] curr <- next-duplex curr @@ -434,8 +415,7 @@ recipe editor-contents [ curr <- next-duplex curr loop } - result:address:array:character <- buffer-to-array buf - reply result + result <- buffer-to-array buf ] scenario editor-provides-edited-contents [ diff --git a/sandbox/006-sandbox-edit.mu b/sandbox/006-sandbox-edit.mu index 4f76b124..778d30c0 100644 --- a/sandbox/006-sandbox-edit.mu +++ b/sandbox/006-sandbox-edit.mu @@ -90,19 +90,17 @@ after <global-touch> [ } ] -recipe empty-editor? [ +recipe empty-editor? editor:address:editor-data -> result:boolean [ 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 + load-ingredients + head:address:duplex-list:character <- get *editor, data:offset + first:address:duplex-list:character <- next-duplex head + result <- not first ] -recipe extract-sandbox [ +recipe extract-sandbox env:address:programming-environment-data, click-row:number -> result:address:sandbox-data [ local-scope - env:address:programming-environment-data <- next-ingredient - click-row:number <- next-ingredient + load-ingredients # assert click-row >= sandbox.starting-row-on-screen sandbox:address:address:sandbox-data <- get-address *env, sandbox:offset start:number <- get **sandbox, starting-row-on-screen:offset @@ -119,9 +117,8 @@ recipe extract-sandbox [ loop } # snip sandbox out of its list - result:address:sandbox-data <- copy *sandbox + result <- copy *sandbox *sandbox <- copy next-sandbox - reply result ] scenario sandbox-with-print-can-be-edited [ diff --git a/sandbox/007-sandbox-delete.mu b/sandbox/007-sandbox-delete.mu index a3ef543e..85d7af28 100644 --- a/sandbox/007-sandbox-delete.mu +++ b/sandbox/007-sandbox-delete.mu @@ -76,11 +76,9 @@ after <global-touch> [ } ] -# was-deleted?:boolean <- delete-sandbox t:touch-event, env:address:programming-environment-data -recipe delete-sandbox [ +recipe delete-sandbox t:touch-event, env:address:programming-environment-data -> was-delete?:boolean [ local-scope - t:touch-event <- next-ingredient - env:address:programming-environment-data <- next-ingredient + load-ingredients click-column:number <- get t, column:offset current-sandbox:address:editor-data <- get *env, current-sandbox:offset right:number <- get *current-sandbox, right:offset diff --git a/sandbox/008-sandbox-test.mu b/sandbox/008-sandbox-test.mu index bc6ca80a..a73064df 100644 --- a/sandbox/008-sandbox-test.mu +++ b/sandbox/008-sandbox-test.mu @@ -31,10 +31,9 @@ after <global-touch> [ } ] -recipe find-click-in-sandbox-output [ +recipe find-click-in-sandbox-output env:address:programming-environment-data, click-row:number -> sandbox:address:sandbox-data [ local-scope - env:address:programming-environment-data <- next-ingredient - click-row:number <- next-ingredient + load-ingredients # assert click-row >= sandbox.starting-row-on-screen sandbox:address:sandbox-data <- get *env, sandbox:offset start:number <- get *sandbox, starting-row-on-screen:offset @@ -58,9 +57,9 @@ recipe find-click-in-sandbox-output [ reply sandbox ] -recipe toggle-expected-response [ +recipe toggle-expected-response sandbox:address:sandbox-data -> sandbox:address:sandbox-data [ local-scope - sandbox:address:sandbox-data <- next-ingredient + load-ingredients expected-response:address:address:array:character <- get-address *sandbox, expected-response:offset { # if expected-response is set, reset @@ -71,7 +70,6 @@ recipe toggle-expected-response [ # if not, current response is the expected response response:address:array:character <- get *sandbox, response:offset *expected-response <- copy response - reply sandbox/same-as-ingredient:0 ] # when rendering a sandbox, color it in red/green if expected response exists diff --git a/sandbox/009-sandbox-trace.mu b/sandbox/009-sandbox-trace.mu index c05c00d8..1829e3a4 100644 --- a/sandbox/009-sandbox-trace.mu +++ b/sandbox/009-sandbox-trace.mu @@ -118,9 +118,9 @@ container sandbox-data [ ] # replaced in a later layer -recipe! update-sandbox [ +recipe! update-sandbox sandbox:address:sandbox-data -> sandbox:address:sandbox-data [ local-scope - sandbox:address:sandbox-data <- next-ingredient + load-ingredients data:address:array:character <- get *sandbox, data:offset response:address:address:array:character <- get-address *sandbox, response:offset trace:address:address:array:character <- get-address *sandbox, trace:offset @@ -157,12 +157,11 @@ after <global-touch> [ } ] -recipe find-click-in-sandbox-code [ +recipe find-click-in-sandbox-code env:address:programming-environment-data, click-row:number -> sandbox:address:sandbox-data [ local-scope - env:address:programming-environment-data <- next-ingredient - click-row:number <- next-ingredient + load-ingredients # assert click-row >= sandbox.starting-row-on-screen - sandbox:address:sandbox-data <- get *env, sandbox:offset + sandbox <- 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] diff --git a/sandbox/010-warnings.mu b/sandbox/010-warnings.mu index 31e85418..c04e4690 100644 --- a/sandbox/010-warnings.mu +++ b/sandbox/010-warnings.mu @@ -5,10 +5,9 @@ container programming-environment-data [ ] # copy code from recipe editor, persist, load into mu, save any warnings -recipe! update-recipes [ +recipe! update-recipes env:address:programming-environment-data, screen:address:screen -> errors-found?:boolean, env:address:programming-environment-data, screen:address:screen [ local-scope - env:address:programming-environment-data <- next-ingredient - screen:address:screen <- next-ingredient + load-ingredients in:address:array:character <- restore [recipes.mu] recipe-warnings:address:address:array:character <- get-address *env, recipe-warnings:offset *recipe-warnings <- reload in @@ -18,7 +17,7 @@ recipe! update-recipes [ status:address:array:character <- new [errors found] update-status screen, status, 1/red } - reply 0/show-recipe-warnings-in-sandboxes, env/same-as-ingredient:0, screen/same-as-ingredient:1 + errors-found? <- copy 0/false ] before <render-components-end> [ @@ -35,10 +34,9 @@ container sandbox-data [ warnings:address:array:character ] -recipe! update-sandbox [ +recipe! update-sandbox sandbox:address:sandbox-data, env:address:programming-environment-data -> sandbox:address:sandbox-data [ local-scope - sandbox:address:sandbox-data <- next-ingredient - env:address:programming-environment-data <- next-ingredient + load-ingredients data:address:array:character <- get *sandbox, data:offset response:address:address:array:character <- get-address *sandbox, response:offset warnings:address:address:array:character <- get-address *sandbox, warnings:offset diff --git a/sandbox/011-editor-undo.mu b/sandbox/011-editor-undo.mu index 17d97213..655d30fb 100644 --- a/sandbox/011-editor-undo.mu +++ b/sandbox/011-editor-undo.mu @@ -65,12 +65,12 @@ after <handle-special-character> [ { undo?:boolean <- equal *c, 26/ctrl-z break-unless undo? - undo:address:address:list <- get-address *editor, undo:offset + undo:address:address:list:address:operation <- get-address *editor, undo:offset break-unless *undo - op:address:operation/skiptypecheck <- first *undo + op:address:operation <- first *undo *undo <- rest *undo - redo:address:address:list <- get-address *editor, redo:offset - *redo/skiptypecheck <- push op, *redo + redo:address:address:list:address:operation <- get-address *editor, redo:offset + *redo <- push op, *redo <handle-undo> reply screen/same-as-ingredient:0, editor/same-as-ingredient:1, 1/go-render } @@ -81,12 +81,12 @@ after <handle-special-character> [ { redo?:boolean <- equal *c, 25/ctrl-y break-unless redo? - redo:address:address:list <- get-address *editor, redo:offset + redo:address:address:list:address:operation <- get-address *editor, redo:offset break-unless *redo - op:address:operation/skiptypecheck <- first *redo + op:address:operation <- first *redo *redo <- rest *redo - undo:address:address:list <- get-address *editor, undo:offset - *undo/skiptypecheck <- push op, *undo + undo:address:address:list:address:operation <- get-address *editor, undo:offset + *undo <- push op, *undo <handle-redo> reply screen/same-as-ingredient:0, editor/same-as-ingredient:1, 1/go-render } @@ -135,21 +135,21 @@ scenario editor-can-undo-typing [ # save operation to undo after <insert-character-begin> [ - top-before:address:duplex-list <- get *editor, top-of-screen:offset - cursor-before:address:duplex-list <- copy *before-cursor + top-before:address:duplex-list:character <- get *editor, top-of-screen:offset + cursor-before:address:duplex-list:character <- copy *before-cursor ] before <insert-character-end> [ - top-after:address:duplex-list <- get *editor, top-of-screen:offset - undo:address:address:list <- get-address *editor, undo:offset + top-after:address:duplex-list:character <- get *editor, top-of-screen:offset + undo:address:address:list:address:operation <- get-address *editor, undo:offset { # if previous operation was an insert, coalesce this operation with it break-unless *undo - op:address:operation/skiptypecheck <- first *undo + op:address:operation <- first *undo typing:address:insert-operation <- maybe-convert *op, typing:variant break-unless typing previous-coalesce-tag:number <- get *typing, tag:offset break-unless previous-coalesce-tag - insert-until:address:address:duplex-list <- get-address *typing, insert-until:offset + insert-until:address:address:duplex-list:character <- get-address *typing, insert-until:offset *insert-until <- next-duplex *before-cursor after-row:address:number <- get-address *typing, after-row:offset *after-row <- copy *cursor-row @@ -160,8 +160,8 @@ before <insert-character-end> [ break +done-adding-insert-operation:label } # if not, create a new operation - insert-from:address:duplex-list <- next-duplex cursor-before - insert-to:address:duplex-list <- next-duplex insert-from + insert-from:address:duplex-list:character <- next-duplex cursor-before + insert-to:address:duplex-list:character <- next-duplex insert-from op:address:operation <- new operation:type *op <- merge 0/insert-operation, save-row/before, save-column/before, top-before, *cursor-row/after, *cursor-column/after, top-after, insert-from, insert-to, 1/coalesce editor <- add-operation editor, op @@ -172,14 +172,14 @@ before <insert-character-end> [ after <insert-enter-begin> [ cursor-row-before:number <- copy *cursor-row cursor-column-before:number <- copy *cursor-column - top-before:address:duplex-list <- get *editor, top-of-screen:offset - cursor-before:address:duplex-list <- copy *before-cursor + top-before:address:duplex-list:character <- get *editor, top-of-screen:offset + cursor-before:address:duplex-list:character <- copy *before-cursor ] before <insert-enter-end> [ - top-after:address:duplex-list <- get *editor, top-of-screen:offset + top-after:address:duplex-list:character <- get *editor, top-of-screen:offset # never coalesce - insert-from:address:duplex-list <- next-duplex cursor-before - insert-to:address:duplex-list <- next-duplex *before-cursor + insert-from:address:duplex-list:character <- next-duplex cursor-before + insert-to:address:duplex-list:character <- next-duplex *before-cursor op:address:operation <- new operation:type *op <- merge 0/insert-operation, cursor-row-before, cursor-column-before, top-before, *cursor-row/after, *cursor-column/after, top-after, insert-from, insert-to, 0/never-coalesce editor <- add-operation editor, op @@ -189,12 +189,11 @@ before <insert-enter-end> [ # redo stack, because it's now obsolete. # Beware: since we're counting cursor moves as operations, this means just # moving the cursor can lose work on the undo stack. -recipe add-operation [ +recipe add-operation editor:address:editor-data, op:address:operation -> editor:address:editor-data [ local-scope - editor:address:editor-data <- next-ingredient - op:address:operation <- next-ingredient + load-ingredients undo:address:address:list:address:operation <- get-address *editor, undo:offset - *undo/skiptypecheck <- push op *undo + *undo <- push op *undo redo:address:address:list:address:operation <- get-address *editor, redo:offset *redo <- copy 0 reply editor/same-as-ingredient:0 @@ -204,14 +203,14 @@ after <handle-undo> [ { typing:address:insert-operation <- maybe-convert *op, typing:variant break-unless typing - start:address:duplex-list <- get *typing, insert-from:offset - end:address:duplex-list <- get *typing, insert-until:offset + start:address:duplex-list:character <- get *typing, insert-from:offset + end:address:duplex-list:character <- get *typing, insert-until:offset # assert cursor-row/cursor-column/top-of-screen match after-row/after-column/after-top-of-screen *before-cursor <- prev-duplex start remove-duplex-between *before-cursor, end *cursor-row <- get *typing, before-row:offset *cursor-column <- get *typing, before-column:offset - top:address:address:duplex-list <- get-address *editor, top-of-screen:offset + top:address:address:duplex-list:character <- get-address *editor, top-of-screen:offset *top <- get *typing, before-top-of-screen:offset } ] @@ -401,13 +400,13 @@ after <handle-redo> [ { typing:address:insert-operation <- maybe-convert *op, typing:variant break-unless typing - insert-from:address:duplex-list <- get *typing, insert-from:offset # ignore insert-to because it's already been spliced away + insert-from:address:duplex-list:character <- get *typing, insert-from:offset # ignore insert-to because it's already been spliced away # assert insert-to matches next-duplex(*before-cursor) insert-duplex-range *before-cursor, insert-from # assert cursor-row/cursor-column/top-of-screen match after-row/after-column/after-top-of-screen *cursor-row <- get *typing, after-row:offset *cursor-column <- get *typing, after-column:offset - top:address:address:duplex-list <- get-address *editor, top-of-screen:offset + top:address:address:duplex-list:character <- get-address *editor, top-of-screen:offset *top <- get *typing, after-top-of-screen:offset } ] @@ -703,19 +702,19 @@ ghi] after <move-cursor-begin> [ before-cursor-row:number <- get *editor, cursor-row:offset before-cursor-column:number <- get *editor, cursor-column:offset - before-top-of-screen:address:duplex-list <- get *editor, top-of-screen:offset + before-top-of-screen:address:duplex-list:character <- get *editor, top-of-screen:offset ] before <move-cursor-end> [ after-cursor-row:number <- get *editor, cursor-row:offset after-cursor-column:number <- get *editor, cursor-column:offset - after-top-of-screen:address:duplex-list <- get *editor, top-of-screen:offset + after-top-of-screen:address:duplex-list:character <- get *editor, top-of-screen:offset { break-unless undo-coalesce-tag # if previous operation was also a move, and also had the same coalesce # tag, coalesce with it - undo:address:address:list <- get-address *editor, undo:offset + undo:address:address:list:address:operation <- get-address *editor, undo:offset break-unless *undo - op:address:operation/skiptypecheck <- first *undo + op:address:operation <- first *undo move:address:move-operation <- maybe-convert *op, move:variant break-unless move previous-coalesce-tag:number <- get *move, tag:offset @@ -740,7 +739,7 @@ after <handle-undo> [ move:address:move-operation <- maybe-convert *op, move:variant break-unless move # assert cursor-row/cursor-column/top-of-screen match after-row/after-column/after-top-of-screen - top:address:address:duplex-list <- get-address *editor, top-of-screen:offset + top:address:address:duplex-list:character <- get-address *editor, top-of-screen:offset *cursor-row <- get *move, before-row:offset *cursor-column <- get *move, before-column:offset *top <- get *move, before-top-of-screen:offset @@ -1516,7 +1515,7 @@ after <handle-redo> [ # assert cursor-row/cursor-column/top-of-screen match after-row/after-column/after-top-of-screen *cursor-row <- get *move, after-row:offset *cursor-column <- get *move, after-column:offset - top:address:address:duplex-list <- get-address *editor, top-of-screen:offset + top:address:address:duplex-list:character <- get-address *editor, top-of-screen:offset *top <- get *move, after-top-of-screen:offset } ] @@ -1590,25 +1589,25 @@ scenario editor-can-undo-and-redo-backspace [ # save operation to undo after <backspace-character-begin> [ - top-before:address:duplex-list <- get *editor, top-of-screen:offset + top-before:address:duplex-list:character <- get *editor, top-of-screen:offset ] before <backspace-character-end> [ { break-unless backspaced-cell # backspace failed; don't add an undo operation - top-after:address:duplex-list <- get *editor, top-of-screen:offset - undo:address:address:list <- get-address *editor, undo:offset + top-after:address:duplex-list:character <- get *editor, top-of-screen:offset + undo:address:address:list:address:operation <- get-address *editor, undo:offset { # if previous operation was an insert, coalesce this operation with it break-unless *undo - op:address:operation/skiptypecheck <- first *undo + op:address:operation <- first *undo deletion:address:delete-operation <- maybe-convert *op, delete:variant break-unless deletion previous-coalesce-tag:number <- get *deletion, tag:offset coalesce?:boolean <- equal previous-coalesce-tag, 1/coalesce-backspace break-unless coalesce? - delete-from:address:address:duplex-list <- get-address *deletion, delete-from:offset + delete-from:address:address:duplex-list:character <- get-address *deletion, delete-from:offset *delete-from <- copy *before-cursor - backspaced-so-far:address:address:duplex-list <- get-address *deletion, deleted-text:offset + backspaced-so-far:address:address:duplex-list:character <- get-address *deletion, deleted-text:offset insert-duplex-range backspaced-cell, *backspaced-so-far *backspaced-so-far <- copy backspaced-cell after-row:address:number <- get-address *deletion, after-row:offset @@ -1621,7 +1620,7 @@ before <backspace-character-end> [ } # if not, create a new operation op:address:operation <- new operation:type - deleted-until:address:duplex-list <- next-duplex *before-cursor + deleted-until:address:duplex-list:character <- next-duplex *before-cursor *op <- merge 2/delete-operation, save-row/before, save-column/before, top-before, *cursor-row/after, *cursor-column/after, top-after, backspaced-cell/deleted, *before-cursor/delete-from, deleted-until, 1/coalesce-backspace editor <- add-operation editor, op +done-adding-backspace-operation @@ -1632,17 +1631,17 @@ after <handle-undo> [ { deletion:address:delete-operation <- maybe-convert *op, delete:variant break-unless deletion - start2:address:address:duplex-list <- get-address *editor, data:offset - anchor:address:duplex-list <- get *deletion, delete-from:offset + start2:address:address:duplex-list:character <- get-address *editor, data:offset + anchor:address:duplex-list:character <- get *deletion, delete-from:offset break-unless anchor - deleted:address:duplex-list <- get *deletion, deleted-text:offset - old-cursor:address:duplex-list <- last-duplex deleted + deleted:address:duplex-list:character <- get *deletion, deleted-text:offset + old-cursor:address:duplex-list:character <- last-duplex deleted insert-duplex-range anchor, deleted # assert cursor-row/cursor-column/top-of-screen match after-row/after-column/after-top-of-screen *before-cursor <- copy old-cursor *cursor-row <- get *deletion, before-row:offset *cursor-column <- get *deletion, before-column:offset - top:address:address:duplex-list <- get-address *editor, top-of-screen:offset + top:address:address:duplex-list:character <- get-address *editor, top-of-screen:offset *top <- get *deletion, before-top-of-screen:offset } ] @@ -1651,13 +1650,13 @@ after <handle-redo> [ { deletion:address:delete-operation <- maybe-convert *op, delete:variant break-unless deletion - start:address:duplex-list <- get *deletion, delete-from:offset - end:address:duplex-list <- get *deletion, delete-until:offset + start:address:duplex-list:character <- get *deletion, delete-from:offset + end:address:duplex-list:character <- get *deletion, delete-until:offset remove-duplex-between start, end # assert cursor-row/cursor-column/top-of-screen match after-row/after-column/after-top-of-screen *cursor-row <- get *deletion, after-row:offset *cursor-column <- get *deletion, after-column:offset - top:address:address:duplex-list <- get-address *editor, top-of-screen:offset + top:address:address:duplex-list:character <- get-address *editor, top-of-screen:offset *top <- get *deletion, after-top-of-screen:offset } ] @@ -1812,25 +1811,25 @@ scenario editor-can-undo-and-redo-delete [ ] after <delete-character-begin> [ - top-before:address:duplex-list <- get *editor, top-of-screen:offset + top-before:address:duplex-list:character <- get *editor, top-of-screen:offset ] before <delete-character-end> [ { break-unless deleted-cell # delete failed; don't add an undo operation - top-after:address:duplex-list <- get *editor, top-of-screen:offset - undo:address:address:list <- get-address *editor, undo:offset + top-after:address:duplex-list:character <- get *editor, top-of-screen:offset + undo:address:address:list:address:operation <- get-address *editor, undo:offset { # if previous operation was an insert, coalesce this operation with it break-unless *undo - op:address:operation/skiptypecheck <- first *undo + op:address:operation <- first *undo deletion:address:delete-operation <- maybe-convert *op, delete:variant break-unless deletion previous-coalesce-tag:number <- get *deletion, tag:offset coalesce?:boolean <- equal previous-coalesce-tag, 2/coalesce-delete break-unless coalesce? - delete-until:address:address:duplex-list <- get-address *deletion, delete-until:offset + delete-until:address:address:duplex-list:character <- get-address *deletion, delete-until:offset *delete-until <- next-duplex *before-cursor - deleted-so-far:address:address:duplex-list <- get-address *deletion, deleted-text:offset + deleted-so-far:address:address:duplex-list:character <- get-address *deletion, deleted-text:offset *deleted-so-far <- append-duplex *deleted-so-far, deleted-cell after-row:address:number <- get-address *deletion, after-row:offset *after-row <- copy *cursor-row @@ -1842,7 +1841,7 @@ before <delete-character-end> [ } # if not, create a new operation op:address:operation <- new operation:type - deleted-until:address:duplex-list <- next-duplex *before-cursor + deleted-until:address:duplex-list:character <- next-duplex *before-cursor *op <- merge 2/delete-operation, save-row/before, save-column/before, top-before, *cursor-row/after, *cursor-column/after, top-after, deleted-cell/deleted, *before-cursor/delete-from, deleted-until, 2/coalesce-delete editor <- add-operation editor, op +done-adding-delete-operation @@ -1935,15 +1934,15 @@ def] ] after <delete-to-end-of-line-begin> [ - top-before:address:duplex-list <- get *editor, top-of-screen:offset + top-before:address:duplex-list:character <- get *editor, top-of-screen:offset ] before <delete-to-end-of-line-end> [ { break-unless deleted-cells # delete failed; don't add an undo operation - top-after:address:duplex-list <- get *editor, top-of-screen:offset - undo:address:address:list <- get-address *editor, undo:offset + top-after:address:duplex-list:character <- get *editor, top-of-screen:offset + undo:address:address:list:address:operation <- get-address *editor, undo:offset op:address:operation <- new operation:type - deleted-until:address:duplex-list <- next-duplex *before-cursor + deleted-until:address:duplex-list:character <- next-duplex *before-cursor *op <- merge 2/delete-operation, save-row/before, save-column/before, top-before, *cursor-row/after, *cursor-column/after, top-after, deleted-cells/deleted, *before-cursor/delete-from, deleted-until, 0/never-coalesce editor <- add-operation editor, op +done-adding-delete-operation @@ -2036,15 +2035,15 @@ def] ] after <delete-to-start-of-line-begin> [ - top-before:address:duplex-list <- get *editor, top-of-screen:offset + top-before:address:duplex-list:character <- get *editor, top-of-screen:offset ] before <delete-to-start-of-line-end> [ { break-unless deleted-cells # delete failed; don't add an undo operation - top-after:address:duplex-list <- get *editor, top-of-screen:offset - undo:address:address:list <- get-address *editor, undo:offset + top-after:address:duplex-list:character <- get *editor, top-of-screen:offset + undo:address:address:list:address:operation <- get-address *editor, undo:offset op:address:operation <- new operation:type - deleted-until:address:duplex-list <- next-duplex *before-cursor + deleted-until:address:duplex-list:character <- next-duplex *before-cursor *op <- merge 2/delete-operation, save-row/before, save-column/before, top-before, *cursor-row/after, *cursor-column/after, top-after, deleted-cells/deleted, *before-cursor/delete-from, deleted-until, 0/never-coalesce editor <- add-operation editor, op +done-adding-delete-operation |