From 2a4088119cf41175457414dfa59bd4064b8f0562 Mon Sep 17 00:00:00 2001 From: Kartik Agaram Date: Wed, 1 Jan 2020 17:04:37 -0800 Subject: 5852 --- archive/2.vm/sandbox/001-editor.mu | 464 ---- archive/2.vm/sandbox/002-typing.mu | 1144 -------- archive/2.vm/sandbox/003-shortcuts.mu | 2800 -------------------- .../2.vm/sandbox/004-programming-environment.mu | 268 -- archive/2.vm/sandbox/005-sandbox.mu | 1081 -------- archive/2.vm/sandbox/006-sandbox-copy.mu | 286 -- archive/2.vm/sandbox/007-sandbox-delete.mu | 345 --- archive/2.vm/sandbox/008-sandbox-edit.mu | 319 --- archive/2.vm/sandbox/009-sandbox-test.mu | 233 -- archive/2.vm/sandbox/010-sandbox-trace.mu | 243 -- archive/2.vm/sandbox/011-errors.mu | 687 ----- archive/2.vm/sandbox/012-editor-undo.mu | 1907 ------------- archive/2.vm/sandbox/Readme.md | 33 - archive/2.vm/sandbox/mu_run | 16 - archive/2.vm/sandbox/tmux.conf | 3 - 15 files changed, 9829 deletions(-) delete mode 100644 archive/2.vm/sandbox/001-editor.mu delete mode 100644 archive/2.vm/sandbox/002-typing.mu delete mode 100644 archive/2.vm/sandbox/003-shortcuts.mu delete mode 100644 archive/2.vm/sandbox/004-programming-environment.mu delete mode 100644 archive/2.vm/sandbox/005-sandbox.mu delete mode 100644 archive/2.vm/sandbox/006-sandbox-copy.mu delete mode 100644 archive/2.vm/sandbox/007-sandbox-delete.mu delete mode 100644 archive/2.vm/sandbox/008-sandbox-edit.mu delete mode 100644 archive/2.vm/sandbox/009-sandbox-test.mu delete mode 100644 archive/2.vm/sandbox/010-sandbox-trace.mu delete mode 100644 archive/2.vm/sandbox/011-errors.mu delete mode 100644 archive/2.vm/sandbox/012-editor-undo.mu delete mode 100644 archive/2.vm/sandbox/Readme.md delete mode 100755 archive/2.vm/sandbox/mu_run delete mode 100644 archive/2.vm/sandbox/tmux.conf (limited to 'archive/2.vm/sandbox') diff --git a/archive/2.vm/sandbox/001-editor.mu b/archive/2.vm/sandbox/001-editor.mu deleted file mode 100644 index b3399dbb..00000000 --- a/archive/2.vm/sandbox/001-editor.mu +++ /dev/null @@ -1,464 +0,0 @@ -## the basic editor data structure, and how it displays text to the screen - -# temporary main for this layer: just render the given text at the given -# screen dimensions, then stop -def main text:text [ - local-scope - load-inputs - open-console - clear-screen null/screen # non-scrolling app - e:&:editor <- new-editor text, 0/left, 5/right - render null/screen, e - wait-for-event null/console - close-console -] - -scenario editor-renders-text-to-screen [ - local-scope - assume-screen 10/width, 5/height - e:&:editor <- new-editor [abc], 0/left, 10/right - run [ - render screen, e - ] - screen-should-contain [ - # top line of screen reserved for menu - . . - .abc . - . . - ] -] - -container editor [ - # editable text: doubly linked list of characters (head contains a special sentinel) - data:&:duplex-list:char - top-of-screen:&:duplex-list:char - bottom-of-screen:&:duplex-list:char - # location before cursor inside data - before-cursor:&:duplex-list:char - - # raw bounds of display area on screen - # always displays from row 1 (leaving row 0 for a menu) and at most until bottom of screen - left:num - right:num - bottom:num - # raw screen coordinates of cursor - cursor-row:num - cursor-column:num -] - -# creates a new editor widget -# right is exclusive -def new-editor s:text, left:num, right:num -> result:&:editor [ - local-scope - load-inputs - # no clipping of bounds - right <- subtract right, 1 - result <- new editor:type - # initialize screen-related fields - *result <- put *result, left:offset, left - *result <- put *result, right:offset, right - # initialize cursor coordinates - *result <- put *result, cursor-row:offset, 1/top - *result <- put *result, cursor-column:offset, left - # initialize empty contents - init:&:duplex-list:char <- push 167/§, null - *result <- put *result, data:offset, init - *result <- put *result, top-of-screen:offset, init - *result <- put *result, before-cursor:offset, init - result <- insert-text result, s - -] - -def insert-text editor:&:editor, text:text -> editor:&:editor [ - local-scope - load-inputs - curr:&:duplex-list:char <- get *editor, data:offset - insert curr, text -] - -scenario editor-initializes-without-data [ - local-scope - assume-screen 5/width, 3/height - run [ - e:&:editor <- new-editor null/data, 2/left, 5/right - 1:editor/raw <- copy *e - ] - memory-should-contain [ - # 1,2 (data) <- just the § sentinel - # 3,4 (top of screen) <- the § sentinel - # 5 (bottom of screen) <- null since text fits on screen - 5 <- 0 - 6 <- 0 - # 7,8 (before cursor) <- the § sentinel - 9 <- 2 # left - 10 <- 4 # right (inclusive) - 11 <- 0 # bottom (not set until render) - 12 <- 1 # cursor row - 13 <- 2 # cursor column - ] - screen-should-contain [ - . . - . . - . . - ] -] - -# Assumes cursor should be at coordinates (cursor-row, cursor-column) and -# updates before-cursor to match. Might also move coordinates if they're -# outside text. -def render screen:&:screen, editor:&:editor -> last-row:num, last-column:num, screen:&:screen, editor:&:editor [ - local-scope - load-inputs - return-unless editor, 1/top, 0/left - left:num <- get *editor, left:offset - screen-height:num <- screen-height screen - right:num <- get *editor, right:offset - # traversing editor - curr:&:duplex-list:char <- get *editor, top-of-screen:offset - prev:&:duplex-list:char <- copy curr # just in case curr becomes null and we can't compute prev - curr <- next curr - # traversing screen - color:num <- copy 7/white - row:num <- copy 1/top - column:num <- copy left - cursor-row:num <- get *editor, cursor-row:offset - cursor-column:num <- get *editor, cursor-column:offset - before-cursor:&:duplex-list:char <- get *editor, before-cursor:offset - screen <- move-cursor screen, row, column - { - +next-character - break-unless curr - off-screen?:bool <- greater-or-equal row, screen-height - break-if off-screen? - # update editor.before-cursor - # Doing so at the start of each iteration ensures it stays one step behind - # the current character. - { - at-cursor-row?:bool <- equal row, cursor-row - break-unless at-cursor-row? - at-cursor?:bool <- equal column, cursor-column - break-unless at-cursor? - before-cursor <- copy prev - } - c:char <- get *curr, value:offset - - { - # newline? move to left rather than 0 - newline?:bool <- equal c, 10/newline - break-unless newline? - # adjust cursor if necessary - { - at-cursor-row?:bool <- equal row, cursor-row - break-unless at-cursor-row? - left-of-cursor?:bool <- lesser-than column, cursor-column - break-unless left-of-cursor? - cursor-column <- copy column - before-cursor <- prev curr - } - # clear rest of line in this window - clear-line-until screen, right - # skip to next line - row <- add row, 1 - column <- copy left - screen <- move-cursor screen, row, column - curr <- next curr - prev <- next prev - loop +next-character - } - { - # at right? wrap. even if there's only one more letter left; we need - # room for clicking on the cursor after it. - at-right?:bool <- equal column, right - break-unless at-right? - # print wrap icon - wrap-icon:char <- copy 8617/loop-back-to-left - print screen, wrap-icon, 245/grey - column <- copy left - row <- add row, 1 - screen <- move-cursor screen, row, column - # don't increment curr - loop +next-character - } - print screen, c, color - curr <- next curr - prev <- next prev - column <- add column, 1 - loop - } - # save first character off-screen - *editor <- put *editor, bottom-of-screen:offset, curr - # is cursor to the right of the last line? move to end - { - at-cursor-row?:bool <- equal row, cursor-row - cursor-outside-line?:bool <- lesser-or-equal column, cursor-column - before-cursor-on-same-line?:bool <- and at-cursor-row?, cursor-outside-line? - above-cursor-row?:bool <- lesser-than row, cursor-row - before-cursor?:bool <- or before-cursor-on-same-line?, above-cursor-row? - break-unless before-cursor? - cursor-row <- copy row - cursor-column <- copy column - before-cursor <- copy prev - } - *editor <- put *editor, bottom:offset, row - *editor <- put *editor, cursor-row:offset, cursor-row - *editor <- put *editor, cursor-column:offset, cursor-column - *editor <- put *editor, before-cursor:offset, before-cursor - clear-line-until screen, right - row <- add row, 1 - return row, left/column -] - -def clear-screen-from screen:&:screen, row:num, column:num, left:num, right:num -> screen:&:screen [ - local-scope - load-inputs - # if it's the real screen, use the optimized primitive - { - break-if screen - clear-display-from row, column, left, right - return - } - # if not, go the slower route - screen <- move-cursor screen, row, column - clear-line-until screen, right - clear-rest-of-screen screen, row, left, right -] - -def clear-rest-of-screen screen:&:screen, row:num, left:num, right:num -> screen:&:screen [ - local-scope - load-inputs - row <- add row, 1 - # if it's the real screen, use the optimized primitive - { - break-if screen - clear-display-from row, left, left, right - return - } - screen <- move-cursor screen, row, left - screen-height:num <- screen-height screen - { - at-bottom-of-screen?:bool <- greater-or-equal row, screen-height - break-if at-bottom-of-screen? - screen <- move-cursor screen, row, left - clear-line-until screen, right - row <- add row, 1 - loop - } -] - -scenario editor-prints-multiple-lines [ - local-scope - assume-screen 5/width, 5/height - s:text <- new [abc -def] - e:&:editor <- new-editor s, 0/left, 5/right - run [ - render screen, e - ] - screen-should-contain [ - . . - .abc . - .def . - . . - ] -] - -scenario editor-handles-offsets [ - local-scope - assume-screen 5/width, 5/height - e:&:editor <- new-editor [abc], 1/left, 5/right - run [ - render screen, e - ] - screen-should-contain [ - . . - . abc . - . . - ] -] - -scenario editor-prints-multiple-lines-at-offset [ - local-scope - assume-screen 5/width, 5/height - s:text <- new [abc -def] - e:&:editor <- new-editor s, 1/left, 5/right - run [ - render screen, e - ] - screen-should-contain [ - . . - . abc . - . def . - . . - ] -] - -scenario editor-wraps-long-lines [ - local-scope - assume-screen 5/width, 5/height - e:&:editor <- new-editor [abc def], 0/left, 5/right - run [ - render screen, e - ] - screen-should-contain [ - . . - .abc ↩. - .def . - . . - ] - screen-should-contain-in-color 245/grey [ - . . - . ↩. - . . - . . - ] -] - -scenario editor-wraps-barely-long-lines [ - local-scope - assume-screen 5/width, 5/height - e:&:editor <- new-editor [abcde], 0/left, 5/right - run [ - render screen, e - ] - # still wrap, even though the line would fit. We need room to click on the - # end of the line - screen-should-contain [ - . . - .abcd↩. - .e . - . . - ] - screen-should-contain-in-color 245/grey [ - . . - . ↩. - . . - . . - ] -] - -scenario editor-with-empty-text [ - local-scope - assume-screen 5/width, 5/height - e:&:editor <- new-editor [], 0/left, 5/right - run [ - render screen, e - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - ] - screen-should-contain [ - . . - . . - . . - ] - memory-should-contain [ - 3 <- 1 # cursor row - 4 <- 0 # cursor column - ] -] - -# just a little color for Mu code - -scenario render-colors-comments [ - local-scope - assume-screen 5/width, 5/height - s:text <- new [abc -# de -f] - e:&:editor <- new-editor s, 0/left, 5/right - run [ - render screen, e - ] - screen-should-contain [ - . . - .abc . - .# de . - .f . - . . - ] - screen-should-contain-in-color 12/lightblue, [ - . . - . . - .# de . - . . - . . - ] - screen-should-contain-in-color 7/white, [ - . . - .abc . - . . - .f . - . . - ] -] - -after [ - color <- get-color color, c -] - -# so far the previous color is all the information we need; that may change -def get-color color:num, c:char -> color:num [ - local-scope - load-inputs - color-is-white?:bool <- equal color, 7/white - # if color is white and next character is '#', switch color to blue - { - break-unless color-is-white? - starting-comment?:bool <- equal c, 35/# - break-unless starting-comment? - trace 90, [app], [switch color back to blue] - return 12/lightblue - } - # if color is blue and next character is newline, switch color to white - { - color-is-blue?:bool <- equal color, 12/lightblue - break-unless color-is-blue? - ending-comment?:bool <- equal c, 10/newline - break-unless ending-comment? - trace 90, [app], [switch color back to white] - return 7/white - } - # if color is white (no comments) and next character is '<', switch color to red - { - break-unless color-is-white? - starting-assignment?:bool <- equal c, 60/< - break-unless starting-assignment? - return 1/red - } - # if color is red and next character is space, switch color to white - { - color-is-red?:bool <- equal color, 1/red - break-unless color-is-red? - ending-assignment?:bool <- equal c, 32/space - break-unless ending-assignment? - return 7/white - } - # otherwise no change - return color -] - -scenario render-colors-assignment [ - local-scope - assume-screen 8/width, 5/height - s:text <- new [abc -d <- e -f] - e:&:editor <- new-editor s, 0/left, 8/right - run [ - render screen, e - ] - screen-should-contain [ - . . - .abc . - .d <- e . - .f . - . . - ] - screen-should-contain-in-color 1/red, [ - . . - . . - . <- . - . . - . . - ] -] diff --git a/archive/2.vm/sandbox/002-typing.mu b/archive/2.vm/sandbox/002-typing.mu deleted file mode 100644 index ef3f25d2..00000000 --- a/archive/2.vm/sandbox/002-typing.mu +++ /dev/null @@ -1,1144 +0,0 @@ -## handling events from the keyboard, mouse, touch screen, ... - -# temporary main: interactive editor -# hit ctrl-c to exit -def! main text:text [ - local-scope - load-inputs - open-console - clear-screen null/screen # non-scrolling app - editor:&:editor <- new-editor text, 5/left, 45/right - editor-render null/screen, editor - editor-event-loop null/screen, null/console, editor - close-console -] - -def editor-event-loop screen:&:screen, console:&:console, editor:&:editor -> screen:&:screen, console:&:console, editor:&:editor [ - local-scope - load-inputs - { - # looping over each (keyboard or touch) event as it occurs - +next-event - cursor-row:num <- get *editor, cursor-row:offset - cursor-column:num <- get *editor, cursor-column:offset - screen <- move-cursor screen, cursor-row, cursor-column - e:event, found?:bool, quit?:bool, console <- read-event console - loop-unless found? - break-if quit? # only in tests - trace 10, [app], [next-event] - # 'touch' event - t:touch-event, is-touch?:bool <- maybe-convert e, touch:variant - { - break-unless is-touch? - move-cursor editor, screen, t - loop +next-event - } - # keyboard events - { - break-if is-touch? - go-render?:bool <- handle-keyboard-event screen, editor, e - { - break-unless go-render? - screen <- editor-render screen, editor - } - } - loop - } -] - -# process click, return if it was on current editor -def move-cursor editor:&:editor, screen:&:screen, t:touch-event -> in-focus?:bool, editor:&:editor [ - local-scope - load-inputs - return-unless editor, false - click-row:num <- get t, row:offset - return-unless click-row, false # ignore clicks on 'menu' - click-column:num <- get t, column:offset - left:num <- get *editor, left:offset - too-far-left?:bool <- lesser-than click-column, left - return-if too-far-left?, false - right:num <- get *editor, right:offset - too-far-right?:bool <- greater-than click-column, right - return-if too-far-right?, false - # position cursor - - editor <- snap-cursor editor, screen, click-row, click-column - undo-coalesce-tag:num <- copy 0/never - - # gain focus - return true -] - -# 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. -def snap-cursor editor:&:editor, screen:&:screen, target-row:num, target-column:num -> editor:&:editor [ - local-scope - load-inputs - return-unless editor - left:num <- get *editor, left:offset - right:num <- get *editor, right:offset - screen-height:num <- screen-height screen - # count newlines until screen row - curr:&:duplex-list:char <- get *editor, top-of-screen:offset - prev:&:duplex-list:char <- copy curr # just in case curr becomes null and we can't compute prev - curr <- next curr - row:num <- copy 1/top - column:num <- copy left - *editor <- put *editor, cursor-row:offset, target-row - cursor-row:num <- copy target-row - *editor <- put *editor, cursor-column:offset, target-column - cursor-column:num <- copy target-column - before-cursor:&:duplex-list:char <- get *editor, before-cursor:offset - { - +next-character - break-unless curr - off-screen?:bool <- greater-or-equal row, screen-height - break-if off-screen? - # update editor.before-cursor - # Doing so at the start of each iteration ensures it stays one step behind - # the current character. - { - at-cursor-row?:bool <- equal row, cursor-row - break-unless at-cursor-row? - at-cursor?:bool <- equal column, cursor-column - break-unless at-cursor? - before-cursor <- copy prev - *editor <- put *editor, before-cursor:offset, before-cursor - } - c:char <- get *curr, value:offset - { - # newline? move to left rather than 0 - newline?:bool <- equal c, 10/newline - break-unless newline? - # adjust cursor if necessary - { - at-cursor-row?:bool <- equal row, cursor-row - break-unless at-cursor-row? - left-of-cursor?:bool <- lesser-than column, cursor-column - break-unless left-of-cursor? - cursor-column <- copy column - *editor <- put *editor, cursor-column:offset, cursor-column - before-cursor <- copy prev - *editor <- put *editor, before-cursor:offset, before-cursor - } - # skip to next line - row <- add row, 1 - column <- copy left - curr <- next curr - prev <- next prev - loop +next-character - } - { - # at right? wrap. even if there's only one more letter left; we need - # room for clicking on the cursor after it. - at-right?:bool <- equal column, right - break-unless at-right? - column <- copy left - row <- add row, 1 - # don't increment curr/prev - loop +next-character - } - curr <- next curr - prev <- next prev - column <- add column, 1 - loop - } - # is cursor to the right of the last line? move to end - { - at-cursor-row?:bool <- equal row, cursor-row - cursor-outside-line?:bool <- lesser-or-equal column, cursor-column - before-cursor-on-same-line?:bool <- and at-cursor-row?, cursor-outside-line? - above-cursor-row?:bool <- lesser-than row, cursor-row - before-cursor?:bool <- or before-cursor-on-same-line?, above-cursor-row? - break-unless before-cursor? - cursor-row <- copy row - *editor <- put *editor, cursor-row:offset, cursor-row - cursor-column <- copy column - *editor <- put *editor, cursor-column:offset, cursor-column - before-cursor <- copy prev - *editor <- put *editor, before-cursor:offset, before-cursor - } -] - -# 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. -def handle-keyboard-event screen:&:screen, editor:&:editor, e:event -> go-render?:bool, screen:&:screen, editor:&:editor [ - local-scope - load-inputs - return-unless editor, false/don't-render - screen-width:num <- screen-width screen - screen-height:num <- screen-height screen - left:num <- get *editor, left:offset - right:num <- get *editor, right:offset - before-cursor:&:duplex-list:char <- get *editor, before-cursor:offset - cursor-row:num <- get *editor, cursor-row:offset - cursor-column:num <- get *editor, cursor-column:offset - save-row:num <- copy cursor-row - save-column:num <- copy cursor-column - # character - { - c:char, is-unicode?:bool <- maybe-convert e, text:variant - break-unless is-unicode? - trace 10, [app], [handle-keyboard-event: special character] - # exceptions for special characters go here - - # ignore any other special characters - regular-character?:bool <- greater-or-equal c, 32/space - return-unless regular-character?, false/don't-render - # otherwise type it in - - go-render? <- insert-at-cursor editor, c, screen - - return - } - # special key to modify the text or move the cursor - k:num, is-keycode?:bool <- maybe-convert e:event, keycode:variant - assert is-keycode?, [event was of unknown type; neither keyboard nor mouse] - # handlers for each special key will go here - - return true/go-render -] - -def insert-at-cursor editor:&:editor, c:char, screen:&:screen -> go-render?:bool, editor:&:editor, screen:&:screen [ - local-scope - load-inputs - before-cursor:&:duplex-list:char <- get *editor, before-cursor:offset - insert c, before-cursor - before-cursor <- next before-cursor - *editor <- put *editor, before-cursor:offset, before-cursor - cursor-row:num <- get *editor, cursor-row:offset - cursor-column:num <- get *editor, cursor-column:offset - left:num <- get *editor, left:offset - right:num <- get *editor, right:offset - save-row:num <- copy cursor-row - save-column:num <- copy cursor-column - screen-width:num <- screen-width screen - screen-height:num <- screen-height screen - # occasionally we'll need to mess with the cursor - - # but mostly we'll just move the cursor right - cursor-column <- add cursor-column, 1 - *editor <- put *editor, cursor-column:offset, cursor-column - next:&:duplex-list:char <- next before-cursor - { - # at end of all text? no need to scroll? just print the character and leave - at-end?:bool <- equal next, null - break-unless at-end? - bottom:num <- subtract screen-height, 1 - at-bottom?:bool <- equal save-row, bottom - at-right?:bool <- equal save-column, right - overflow?:bool <- and at-bottom?, at-right? - break-if overflow? - move-cursor screen, save-row, save-column - print screen, c - return false/don't-render - } - { - # not at right margin? print the character and rest of line - break-unless next - at-right?:bool <- greater-or-equal cursor-column, screen-width - break-if at-right? - curr:&:duplex-list:char <- copy before-cursor - move-cursor screen, save-row, save-column - curr-column:num <- copy save-column - { - # hit right margin? give up and let caller render - at-right?:bool <- greater-than curr-column, right - return-if at-right?, true/go-render - break-unless curr - # newline? done. - currc:char <- get *curr, value:offset - at-newline?:bool <- equal currc, 10/newline - break-if at-newline? - print screen, currc - curr-column <- add curr-column, 1 - curr <- next curr - loop - } - return false/don't-render - } - return true/go-render -] - -# helper for tests -def editor-render screen:&:screen, editor:&:editor -> screen:&:screen, editor:&:editor [ - local-scope - load-inputs - old-top-idx:num <- save-top-idx screen - left:num <- get *editor, left:offset - right:num <- get *editor, right:offset - row:num, column:num <- render screen, editor - draw-horizontal screen, row, left, right, 9480/horizontal-dotted - row <- add row, 1 - clear-screen-from screen, row, left, left, right - assert-no-scroll screen, old-top-idx -] - -scenario editor-handles-empty-event-queue [ - local-scope - assume-screen 10/width, 5/height - e:&:editor <- new-editor [abc], 0/left, 10/right - editor-render screen, e - assume-console [] - run [ - editor-event-loop screen, console, e - ] - screen-should-contain [ - . . - .abc . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] -] - -scenario editor-handles-mouse-clicks [ - local-scope - assume-screen 10/width, 5/height - e:&:editor <- new-editor [abc], 0/left, 10/right - editor-render screen, e - $clear-trace - assume-console [ - left-click 1, 1 # on the 'b' - ] - run [ - editor-event-loop screen, console, e - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - ] - screen-should-contain [ - . . - .abc . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - memory-should-contain [ - 3 <- 1 # cursor is at row 0.. - 4 <- 1 # ..and column 1 - ] - check-trace-count-for-label 0, [print-character] -] - -scenario editor-handles-mouse-clicks-outside-text [ - local-scope - assume-screen 10/width, 5/height - e:&:editor <- new-editor [abc], 0/left, 10/right - $clear-trace - assume-console [ - left-click 1, 7 # last line, to the right of text - ] - run [ - editor-event-loop screen, console, e - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - ] - memory-should-contain [ - 3 <- 1 # cursor row - 4 <- 3 # cursor column - ] - check-trace-count-for-label 0, [print-character] -] - -scenario editor-handles-mouse-clicks-outside-text-2 [ - local-scope - assume-screen 10/width, 5/height - s:text <- new [abc -def] - e:&:editor <- new-editor s, 0/left, 10/right - $clear-trace - assume-console [ - left-click 1, 7 # interior line, to the right of text - ] - run [ - editor-event-loop screen, console, e - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - ] - memory-should-contain [ - 3 <- 1 # cursor row - 4 <- 3 # cursor column - ] - check-trace-count-for-label 0, [print-character] -] - -scenario editor-handles-mouse-clicks-outside-text-3 [ - local-scope - assume-screen 10/width, 5/height - s:text <- new [abc -def] - e:&:editor <- new-editor s, 0/left, 10/right - $clear-trace - assume-console [ - left-click 3, 7 # below text - ] - run [ - editor-event-loop screen, console, e - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - ] - memory-should-contain [ - 3 <- 2 # cursor row - 4 <- 3 # cursor column - ] - check-trace-count-for-label 0, [print-character] -] - -scenario editor-handles-mouse-clicks-outside-column [ - local-scope - assume-screen 10/width, 5/height - # editor occupies only left half of screen - e:&:editor <- new-editor [abc], 0/left, 5/right - editor-render screen, e - $clear-trace - assume-console [ - # click on right half of screen - left-click 3, 8 - ] - run [ - editor-event-loop screen, console, e - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - ] - screen-should-contain [ - . . - .abc . - .┈┈┈┈┈ . - . . - ] - memory-should-contain [ - 3 <- 1 # no change to cursor row - 4 <- 0 # ..or column - ] - check-trace-count-for-label 0, [print-character] -] - -scenario editor-handles-mouse-clicks-in-menu-area [ - local-scope - assume-screen 10/width, 5/height - e:&:editor <- new-editor [abc], 0/left, 5/right - editor-render screen, e - $clear-trace - assume-console [ - # click on first, 'menu' row - left-click 0, 3 - ] - run [ - editor-event-loop screen, console, e - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - ] - # no change to cursor - memory-should-contain [ - 3 <- 1 - 4 <- 0 - ] -] - -scenario editor-inserts-characters-into-empty-editor [ - local-scope - assume-screen 10/width, 5/height - e:&:editor <- new-editor [], 0/left, 5/right - editor-render screen, e - $clear-trace - assume-console [ - type [abc] - ] - run [ - editor-event-loop screen, console, e - ] - screen-should-contain [ - . . - .abc . - .┈┈┈┈┈ . - . . - ] - check-trace-count-for-label 3, [print-character] -] - -scenario editor-inserts-characters-at-cursor [ - local-scope - assume-screen 10/width, 5/height - e:&:editor <- new-editor [abc], 0/left, 10/right - editor-render screen, e - $clear-trace - # type two letters at different places - assume-console [ - type [0] - left-click 1, 2 - type [d] - ] - run [ - editor-event-loop screen, console, e - ] - screen-should-contain [ - . . - .0adbc . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - check-trace-count-for-label 7, [print-character] # 4 for first letter, 3 for second -] - -scenario editor-inserts-characters-at-cursor-2 [ - local-scope - assume-screen 10/width, 5/height - e:&:editor <- new-editor [abc], 0/left, 10/right - editor-render screen, e - $clear-trace - assume-console [ - left-click 1, 5 # right of last line - type [d] - ] - run [ - editor-event-loop screen, console, e - ] - screen-should-contain [ - . . - .abcd . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - check-trace-count-for-label 1, [print-character] -] - -scenario editor-inserts-characters-at-cursor-5 [ - local-scope - assume-screen 10/width, 5/height - s:text <- new [abc -d] - e:&:editor <- new-editor s, 0/left, 10/right - editor-render screen, e - $clear-trace - assume-console [ - left-click 1, 5 # right of non-last line - type [e] - ] - run [ - editor-event-loop screen, console, e - ] - screen-should-contain [ - . . - .abce . - .d . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - check-trace-count-for-label 1, [print-character] -] - -scenario editor-inserts-characters-at-cursor-3 [ - local-scope - assume-screen 10/width, 5/height - e:&:editor <- new-editor [abc], 0/left, 10/right - editor-render screen, e - $clear-trace - assume-console [ - left-click 3, 5 # below all text - type [d] - ] - run [ - editor-event-loop screen, console, e - ] - screen-should-contain [ - . . - .abcd . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - check-trace-count-for-label 1, [print-character] -] - -scenario editor-inserts-characters-at-cursor-4 [ - local-scope - assume-screen 10/width, 5/height - s:text <- new [abc -d] - e:&:editor <- new-editor s, 0/left, 10/right - editor-render screen, e - $clear-trace - assume-console [ - left-click 3, 5 # below all text - type [e] - ] - run [ - editor-event-loop screen, console, e - ] - screen-should-contain [ - . . - .abc . - .de . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - check-trace-count-for-label 1, [print-character] -] - -scenario editor-inserts-characters-at-cursor-6 [ - local-scope - assume-screen 10/width, 5/height - s:text <- new [abc -d] - e:&:editor <- new-editor s, 0/left, 10/right - editor-render screen, e - $clear-trace - assume-console [ - left-click 3, 5 # below all text - type [ef] - ] - run [ - editor-event-loop screen, console, e - ] - screen-should-contain [ - . . - .abc . - .def . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - check-trace-count-for-label 2, [print-character] -] - -scenario editor-moves-cursor-after-inserting-characters [ - local-scope - assume-screen 10/width, 5/height - e:&:editor <- new-editor [ab], 0/left, 5/right - editor-render screen, e - assume-console [ - type [01] - ] - run [ - editor-event-loop screen, console, e - ] - screen-should-contain [ - . . - .01ab . - .┈┈┈┈┈ . - . . - ] -] - -# if the cursor reaches the right margin, wrap the line - -scenario editor-wraps-line-on-insert [ - local-scope - assume-screen 5/width, 5/height - e:&:editor <- new-editor [abc], 0/left, 5/right - editor-render screen, e - # type a letter - assume-console [ - type [e] - ] - run [ - editor-event-loop screen, console, e - ] - # no wrap yet - screen-should-contain [ - . . - .eabc . - .┈┈┈┈┈. - . . - . . - ] - # type a second letter - assume-console [ - type [f] - ] - run [ - editor-event-loop screen, console, e - ] - # now wrap - screen-should-contain [ - . . - .efab↩. - .c . - .┈┈┈┈┈. - . . - ] -] - -scenario editor-wraps-line-on-insert-2 [ - local-scope - # create an editor with some text - assume-screen 10/width, 5/height - s:text <- new [abcdefg -defg] - e:&:editor <- new-editor s, 0/left, 5/right - editor-render screen, e - # type more text at the start - assume-console [ - left-click 3, 0 - type [abc] - ] - run [ - editor-event-loop screen, console, e - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - ] - # cursor is not wrapped - memory-should-contain [ - 3 <- 3 - 4 <- 3 - ] - # but line is wrapped - screen-should-contain [ - . . - .abcd↩ . - .efg . - .abcd↩ . - .efg . - ] -] - -after [ - # if the line wraps at the cursor, move cursor to start of next row - { - # if either: - # a) we're at the end of the line and at the column of the wrap indicator, or - # b) we're not at end of line and just before the column of the wrap indicator - wrap-column:num <- copy right - before-wrap-column:num <- subtract wrap-column, 1 - at-wrap?:bool <- greater-or-equal cursor-column, wrap-column - just-before-wrap?:bool <- greater-or-equal cursor-column, before-wrap-column - next:&:duplex-list:char <- next before-cursor - # at end of line? next == 0 || next.value == 10/newline - at-end-of-line?:bool <- equal next, null - { - break-if at-end-of-line? - next-character:char <- get *next, value:offset - at-end-of-line? <- equal next-character, 10/newline - } - # break unless ((eol? and at-wrap?) or (~eol? and just-before-wrap?)) - move-cursor-to-next-line?:bool <- copy false - { - break-if at-end-of-line? - move-cursor-to-next-line? <- copy just-before-wrap? - # if we're moving the cursor because it's in the middle of a wrapping - # line, adjust it to left-most column - potential-new-cursor-column:num <- copy left - } - { - break-unless at-end-of-line? - move-cursor-to-next-line? <- copy at-wrap? - # if we're moving the cursor because it's at the end of a wrapping line, - # adjust it to one past the left-most column to make room for the - # newly-inserted wrap-indicator - potential-new-cursor-column:num <- add left, 1/make-room-for-wrap-indicator - } - break-unless move-cursor-to-next-line? - cursor-column <- copy potential-new-cursor-column - *editor <- put *editor, cursor-column:offset, cursor-column - cursor-row <- add cursor-row, 1 - *editor <- put *editor, cursor-row:offset, cursor-row - # if we're out of the screen, scroll down - { - below-screen?:bool <- greater-or-equal cursor-row, screen-height - break-unless below-screen? - - } - return true/go-render - } -] - -scenario editor-wraps-cursor-after-inserting-characters-in-middle-of-line [ - local-scope - assume-screen 10/width, 5/height - e:&:editor <- new-editor [abcde], 0/left, 5/right - assume-console [ - left-click 1, 3 # right before the wrap icon - type [f] - ] - run [ - editor-event-loop screen, console, e - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - ] - screen-should-contain [ - . . - .abcf↩ . - .de . - .┈┈┈┈┈ . - . . - ] - memory-should-contain [ - 3 <- 2 # cursor row - 4 <- 0 # cursor column - ] -] - -scenario editor-wraps-cursor-after-inserting-characters-at-end-of-line [ - local-scope - assume-screen 10/width, 5/height - # create an editor containing two lines - s:text <- new [abc -xyz] - e:&:editor <- new-editor s, 0/left, 5/right - editor-render screen, e - screen-should-contain [ - . . - .abc . - .xyz . - .┈┈┈┈┈ . - . . - ] - assume-console [ - left-click 1, 4 # at end of first line - type [de] # trigger wrap - ] - run [ - editor-event-loop screen, console, e - ] - screen-should-contain [ - . . - .abcd↩ . - .e . - .xyz . - .┈┈┈┈┈ . - ] -] - -scenario editor-wraps-cursor-to-left-margin [ - local-scope - assume-screen 10/width, 5/height - e:&:editor <- new-editor [abcde], 2/left, 7/right - assume-console [ - left-click 1, 5 # line is full; no wrap icon yet - type [01] - ] - run [ - editor-event-loop screen, console, e - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - ] - screen-should-contain [ - . . - . abc0↩ . - . 1de . - . ┈┈┈┈┈ . - . . - ] - memory-should-contain [ - 3 <- 2 # cursor row - 4 <- 3 # cursor column - ] -] - -# if newline, move cursor to start of next line, and maybe align indent with previous line - -container editor [ - indent?:bool -] - -after [ - *result <- put *result, indent?:offset, true -] - -scenario editor-moves-cursor-down-after-inserting-newline [ - local-scope - assume-screen 10/width, 5/height - e:&:editor <- new-editor [abc], 0/left, 10/right - assume-console [ - type [0 -1] - ] - run [ - editor-event-loop screen, console, e - ] - screen-should-contain [ - . . - .0 . - .1abc . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] -] - -after [ - { - newline?:bool <- equal c, 10/newline - break-unless newline? - - insert-new-line-and-indent editor, screen - - return true/go-render - } -] - -def insert-new-line-and-indent editor:&:editor, screen:&:screen -> editor:&:editor, screen:&:screen [ - local-scope - load-inputs - cursor-row:num <- get *editor, cursor-row:offset - cursor-column:num <- get *editor, cursor-column:offset - before-cursor:&:duplex-list:char <- get *editor, before-cursor:offset - left:num <- get *editor, left:offset - right:num <- get *editor, right:offset - screen-height:num <- screen-height screen - # update cursor coordinates - at-start-of-wrapped-line?:bool <- at-start-of-wrapped-line? editor - { - break-if at-start-of-wrapped-line? - cursor-row <- add cursor-row, 1 - *editor <- put *editor, cursor-row:offset, cursor-row - } - cursor-column <- copy left - *editor <- put *editor, cursor-column:offset, cursor-column - # maybe scroll - { - below-screen?:bool <- greater-or-equal cursor-row, screen-height # must be equal, never greater - break-unless below-screen? - - cursor-row <- subtract cursor-row, 1 # bring back into screen range - *editor <- put *editor, cursor-row:offset, cursor-row - } - # insert newline - insert 10/newline, before-cursor - before-cursor <- next before-cursor - *editor <- put *editor, before-cursor:offset, before-cursor - # indent if necessary - indent?:bool <- get *editor, indent?:offset - return-unless indent? - d:&:duplex-list:char <- get *editor, data:offset - end-of-previous-line:&:duplex-list:char <- prev before-cursor - indent:num <- line-indent end-of-previous-line, d - i:num <- copy 0 - { - indent-done?:bool <- greater-or-equal i, indent - break-if indent-done? - insert-at-cursor editor, 32/space, screen - i <- add i, 1 - loop - } -] - -def at-start-of-wrapped-line? editor:&:editor -> result:bool [ - local-scope - load-inputs - left:num <- get *editor, left:offset - cursor-column:num <- get *editor, cursor-column:offset - cursor-at-left?:bool <- equal cursor-column, left - return-unless cursor-at-left?, false - before-cursor:&:duplex-list:char <- get *editor, before-cursor:offset - before-before-cursor:&:duplex-list:char <- prev before-cursor - return-unless before-before-cursor, false # cursor is at start of editor - char-before-cursor:char <- get *before-cursor, value:offset - cursor-after-newline?:bool <- equal char-before-cursor, 10/newline - return-if cursor-after-newline?, false - # if cursor is at left margin and not at start, but previous character is not a newline, - # then we're at start of a wrapped line - return true -] - -# 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'. -def line-indent curr:&:duplex-list:char, start:&:duplex-list:char -> result:num [ - local-scope - load-inputs - result:num <- copy 0 - return-unless curr - at-start?:bool <- equal curr, start - return-if at-start? - { - curr <- prev curr - break-unless curr - at-start?:bool <- equal curr, start - break-if at-start? - c:char <- get *curr, value:offset - at-newline?:bool <- equal c, 10/newline - break-if at-newline? - # if c is a space, increment result - is-space?:bool <- equal c, 32/space - { - break-unless is-space? - result <- add result, 1 - } - # if c is not a space, reset result - { - break-if is-space? - result <- copy 0 - } - loop - } -] - -scenario editor-moves-cursor-down-after-inserting-newline-2 [ - local-scope - assume-screen 10/width, 5/height - e:&:editor <- new-editor [abc], 1/left, 10/right - assume-console [ - type [0 -1] - ] - run [ - editor-event-loop screen, console, e - ] - screen-should-contain [ - . . - . 0 . - . 1abc . - . ┈┈┈┈┈┈┈┈┈. - . . - ] -] - -scenario editor-clears-previous-line-completely-after-inserting-newline [ - local-scope - assume-screen 10/width, 5/height - e:&:editor <- new-editor [abcde], 0/left, 5/right - editor-render screen, e - screen-should-contain [ - . . - .abcd↩ . - .e . - .┈┈┈┈┈ . - . . - ] - assume-console [ - press enter - ] - run [ - editor-event-loop screen, console, e - ] - # line should be fully cleared - screen-should-contain [ - . . - . . - .abcd↩ . - .e . - .┈┈┈┈┈ . - ] -] - -scenario editor-splits-wrapped-line-after-inserting-newline [ - local-scope - assume-screen 10/width, 5/height - e:&:editor <- new-editor [abcdef], 0/left, 5/right - editor-render screen, e - screen-should-contain [ - . . - .abcd↩ . - .ef . - .┈┈┈┈┈ . - . . - ] - assume-console [ - left-click 2, 0 - press enter - ] - run [ - editor-event-loop screen, console, e - 10:num/raw <- get *e, cursor-row:offset - 11:num/raw <- get *e, cursor-column:offset - ] - screen-should-contain [ - . . - .abcd . - .ef . - .┈┈┈┈┈ . - ] - memory-should-contain [ - 10 <- 2 # cursor-row - 11 <- 0 # cursor-column - ] -] - -scenario editor-inserts-indent-after-newline [ - local-scope - assume-screen 10/width, 10/height - s:text <- new [ab - cd -ef] - e:&:editor <- new-editor s, 0/left, 10/right - # position cursor after 'cd' and hit 'newline' - assume-console [ - left-click 2, 8 - type [ -] - ] - run [ - editor-event-loop screen, console, e - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - ] - # cursor should be below start of previous line - memory-should-contain [ - 3 <- 3 # cursor row - 4 <- 2 # cursor column (indented) - ] -] - -scenario editor-skips-indent-around-paste [ - local-scope - assume-screen 10/width, 10/height - s:text <- new [ab - cd -ef] - e:&:editor <- new-editor s, 0/left, 10/right - # position cursor after 'cd' and hit 'newline' surrounded by paste markers - assume-console [ - left-click 2, 8 - press 65507 # start paste - press enter - press 65506 # end paste - ] - run [ - editor-event-loop screen, console, e - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - ] - # cursor should be below start of previous line - memory-should-contain [ - 3 <- 3 # cursor row - 4 <- 0 # cursor column (not indented) - ] -] - -after [ - { - paste-start?:bool <- equal k, 65507/paste-start - break-unless paste-start? - *editor <- put *editor, indent?:offset, false - return true/go-render - } -] - -after [ - { - paste-end?:bool <- equal k, 65506/paste-end - break-unless paste-end? - *editor <- put *editor, indent?:offset, true - return true/go-render - } -] - -## helpers - -def draw-horizontal screen:&:screen, row:num, x:num, right:num -> screen:&:screen [ - local-scope - load-inputs - height:num <- screen-height screen - past-bottom?:bool <- greater-or-equal row, height - return-if past-bottom? - style:char, style-found?:bool <- next-input - { - break-if style-found? - style <- copy 9472/horizontal - } - color:num, color-found?:bool <- next-input - { - # default color to white - break-if color-found? - color <- copy 245/grey - } - bg-color:num, bg-color-found?:bool <- next-input - { - break-if bg-color-found? - bg-color <- copy 0/black - } - screen <- move-cursor screen, row, x - { - continue?:bool <- lesser-or-equal x, right # right is inclusive, to match editor semantics - break-unless continue? - print screen, style, color, bg-color - x <- add x, 1 - loop - } -] diff --git a/archive/2.vm/sandbox/003-shortcuts.mu b/archive/2.vm/sandbox/003-shortcuts.mu deleted file mode 100644 index c9e66d5b..00000000 --- a/archive/2.vm/sandbox/003-shortcuts.mu +++ /dev/null @@ -1,2800 +0,0 @@ -## special shortcuts for manipulating the editor -# Some keys on the keyboard generate unicode characters, others generate -# terminfo key codes. We need to modify different places in the two cases. - -# tab - insert two spaces - -scenario editor-inserts-two-spaces-on-tab [ - local-scope - assume-screen 10/width, 5/height - s:text <- new [ab -cd] - e:&:editor <- new-editor s, 0/left, 5/right - editor-render screen, e - $clear-trace - assume-console [ - press tab - ] - run [ - editor-event-loop screen, console, e - ] - screen-should-contain [ - . . - . ab . - .cd . - ] - # we render at most two editor rows worth (one row for each space) - check-trace-count-for-label-lesser-than 10, [print-character] -] - -scenario editor-inserts-two-spaces-and-wraps-line-on-tab [ - local-scope - assume-screen 10/width, 5/height - e:&:editor <- new-editor [abcd], 0/left, 5/right - editor-render screen, e - $clear-trace - assume-console [ - press tab - ] - run [ - editor-event-loop screen, console, e - ] - screen-should-contain [ - . . - . ab↩ . - .cd . - ] - # we re-render the whole editor - check-trace-count-for-label-greater-than 10, [print-character] -] - -after [ - { - tab?:bool <- equal c, 9/tab - break-unless tab? - - # todo: decompose insert-at-cursor into editor update and screen update, - # so that 'tab' doesn't render the current line multiple times - insert-at-cursor editor, 32/space, screen - go-render? <- insert-at-cursor editor, 32/space, screen - - return - } -] - -# backspace - delete character before cursor - -scenario editor-handles-backspace-key [ - local-scope - assume-screen 10/width, 5/height - e:&:editor <- new-editor [abc], 0/left, 10/right - editor-render screen, e - $clear-trace - assume-console [ - left-click 1, 1 - press backspace - ] - run [ - editor-event-loop screen, console, e - 4:num/raw <- get *e, cursor-row:offset - 5:num/raw <- get *e, cursor-column:offset - ] - screen-should-contain [ - . . - .bc . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - memory-should-contain [ - 4 <- 1 - 5 <- 0 - ] - check-trace-count-for-label 3, [print-character] # length of original line to overwrite -] - -after [ - { - delete-previous-character?:bool <- equal c, 8/backspace - break-unless delete-previous-character? - - go-render?:bool, backspaced-cell:&:duplex-list:char <- delete-before-cursor editor, screen - - return - } -] - -# 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. -def delete-before-cursor editor:&:editor, screen:&:screen -> go-render?:bool, backspaced-cell:&:duplex-list:char, editor:&:editor, screen:&:screen [ - local-scope - load-inputs - before-cursor:&:duplex-list:char <- get *editor, before-cursor:offset - data:&:duplex-list:char <- get *editor, data:offset - # if at start of text (before-cursor at § sentinel), return - prev:&:duplex-list:char <- prev before-cursor - return-unless prev, false/no-more-render, null/nothing-deleted - trace 10, [app], [delete-before-cursor] - original-row:num <- get *editor, cursor-row:offset - move-cursor-coordinates-left editor - backspaced-cell:&:duplex-list:char <- copy before-cursor - data <- remove before-cursor, data # will also neatly trim next/prev pointers in backspaced-cell/before-cursor - before-cursor <- copy prev - *editor <- put *editor, before-cursor:offset, before-cursor - screen-width:num <- screen-width screen - cursor-row:num <- get *editor, cursor-row:offset - cursor-column:num <- get *editor, cursor-column:offset - # did we just backspace over a newline? - same-row?:bool <- equal cursor-row, original-row - return-unless same-row?, true/go-render - left:num <- get *editor, left:offset - right:num <- get *editor, right:offset - curr:&:duplex-list:char <- next before-cursor - screen <- move-cursor screen, cursor-row, cursor-column - curr-column:num <- copy cursor-column - { - # hit right margin? give up and let caller render - at-right?:bool <- greater-or-equal curr-column, right - return-if at-right?, true/go-render - break-unless curr - # newline? done. - currc:char <- get *curr, value:offset - at-newline?:bool <- equal currc, 10/newline - break-if at-newline? - screen <- print screen, currc - curr-column <- add curr-column, 1 - curr <- next curr - loop - } - # we're guaranteed not to be at the right margin - space:char <- copy 32/space - screen <- print screen, space - go-render? <- copy false -] - -def move-cursor-coordinates-left editor:&:editor -> editor:&:editor [ - local-scope - load-inputs - before-cursor:&:duplex-list:char <- get *editor, before-cursor:offset - cursor-row:num <- get *editor, cursor-row:offset - cursor-column:num <- get *editor, cursor-column:offset - left:num <- get *editor, left:offset - # if not at left margin, move one character left - { - at-left-margin?:bool <- equal cursor-column, left - break-if at-left-margin? - trace 10, [app], [decrementing cursor column] - cursor-column <- subtract cursor-column, 1 - *editor <- put *editor, cursor-column:offset, cursor-column - return - } - # if at left margin, we must move to previous row: - top-of-screen?:bool <- equal cursor-row, 1 # exclude menu bar - { - break-if top-of-screen? - cursor-row <- subtract cursor-row, 1 - *editor <- put *editor, cursor-row:offset, cursor-row - } - { - break-unless top-of-screen? - # no scroll, so do nothing - } - { - # case 1: if previous character was newline, figure out how long the previous line is - previous-character:char <- get *before-cursor, value:offset - previous-character-is-newline?:bool <- equal previous-character, 10/newline - break-unless previous-character-is-newline? - # compute length of previous line - trace 10, [app], [switching to previous line] - d:&:duplex-list:char <- get *editor, data:offset - end-of-line:num <- previous-line-length before-cursor, d - right:num <- get *editor, right:offset - width:num <- subtract right, left - wrap?:bool <- greater-than end-of-line, width - { - break-unless wrap? - _, column-offset:num <- divide-with-remainder end-of-line, width - cursor-column <- add left, column-offset - *editor <- put *editor, cursor-column:offset, cursor-column - } - { - break-if wrap? - cursor-column <- add left, end-of-line - *editor <- put *editor, cursor-column:offset, cursor-column - } - return - } - # case 2: if previous-character was not newline, we're just at a wrapped line - trace 10, [app], [wrapping to previous line] - right:num <- get *editor, right:offset - cursor-column <- subtract right, 1 # leave room for wrap icon - *editor <- put *editor, cursor-column:offset, cursor-column -] - -# takes a pointer 'curr' into the doubly-linked list and its sentinel, counts -# the length of the previous line before the 'curr' pointer. -def previous-line-length curr:&:duplex-list:char, start:&:duplex-list:char -> result:num [ - local-scope - load-inputs - result:num <- copy 0 - return-unless curr - at-start?:bool <- equal curr, start - return-if at-start? - { - curr <- prev curr - break-unless curr - at-start?:bool <- equal curr, start - break-if at-start? - c:char <- get *curr, value:offset - at-newline?:bool <- equal c, 10/newline - break-if at-newline? - result <- add result, 1 - loop - } -] - -scenario editor-clears-last-line-on-backspace [ - local-scope - assume-screen 10/width, 5/height - s:text <- new [ab -cd] - e:&:editor <- new-editor s, 0/left, 10/right - assume-console [ - left-click 2, 0 - press backspace - ] - run [ - editor-event-loop screen, console, e - 4:num/raw <- get *e, cursor-row:offset - 5:num/raw <- get *e, cursor-column:offset - ] - screen-should-contain [ - . . - .abcd . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - memory-should-contain [ - 4 <- 1 - 5 <- 2 - ] -] - -scenario editor-joins-and-wraps-lines-on-backspace [ - local-scope - assume-screen 10/width, 5/height - # initialize editor with two long-ish but non-wrapping lines - s:text <- new [abc def -ghi jkl] - e:&:editor <- new-editor s, 0/left, 10/right - editor-render screen, e - $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, console, e - ] - # resulting single line should wrap correctly - screen-should-contain [ - . . - .abc defgh↩. - .i jkl . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] -] - -scenario editor-wraps-long-lines-on-backspace [ - local-scope - assume-screen 10/width, 5/height - # initialize editor in part of the screen with a long line - e:&:editor <- new-editor [abc def ghij], 0/left, 8/right - editor-render screen, e - # 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, console, e - ] - # 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 [ - local-scope - assume-screen 10/width, 5/height - e:&:editor <- new-editor [abc], 0/left, 10/right - editor-render screen, e - $clear-trace - assume-console [ - press delete - ] - run [ - editor-event-loop screen, console, e - ] - screen-should-contain [ - . . - .bc . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - check-trace-count-for-label 3, [print-character] # length of original line to overwrite - $clear-trace - assume-console [ - press delete - ] - run [ - editor-event-loop screen, console, e - ] - screen-should-contain [ - . . - .c . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - check-trace-count-for-label 2, [print-character] # new length to overwrite -] - -after [ - { - delete-next-character?:bool <- equal k, 65522/delete - break-unless delete-next-character? - - go-render?:bool, deleted-cell:&:duplex-list:char <- delete-at-cursor editor, screen - - return - } -] - -def delete-at-cursor editor:&:editor, screen:&:screen -> go-render?:bool, deleted-cell:&:duplex-list:char, editor:&:editor, screen:&:screen [ - local-scope - load-inputs - before-cursor:&:duplex-list:char <- get *editor, before-cursor:offset - data:&:duplex-list:char <- get *editor, data:offset - deleted-cell:&:duplex-list:char <- next before-cursor - return-unless deleted-cell, false/don't-render - currc:char <- get *deleted-cell, value:offset - data <- remove deleted-cell, data - deleted-newline?:bool <- equal currc, 10/newline - return-if deleted-newline?, true/go-render - # wasn't a newline? render rest of line - curr:&:duplex-list:char <- next before-cursor # refresh after remove above - cursor-row:num <- get *editor, cursor-row:offset - cursor-column:num <- get *editor, cursor-column:offset - screen <- move-cursor screen, cursor-row, cursor-column - curr-column:num <- copy cursor-column - screen-width:num <- screen-width screen - { - # hit right margin? give up and let caller render - at-right?:bool <- greater-or-equal curr-column, screen-width - return-if at-right?, true/go-render - break-unless curr - currc:char <- get *curr, value:offset - at-newline?:bool <- equal currc, 10/newline - break-if at-newline? - screen <- print screen, currc - curr-column <- add curr-column, 1 - curr <- next curr - loop - } - # we're guaranteed not to be at the right margin - space:char <- copy 32/space - screen <- print screen, space - go-render? <- copy false -] - -# right arrow - -scenario editor-moves-cursor-right-with-key [ - local-scope - assume-screen 10/width, 5/height - e:&:editor <- new-editor [abc], 0/left, 10/right - editor-render screen, e - $clear-trace - assume-console [ - press right-arrow - type [0] - ] - run [ - editor-event-loop screen, console, e - ] - screen-should-contain [ - . . - .a0bc . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - check-trace-count-for-label 3, [print-character] # 0 and following characters -] - -after [ - { - move-to-next-character?:bool <- equal k, 65514/right-arrow - break-unless move-to-next-character? - # if not at end of text - next-cursor:&:duplex-list:char <- next before-cursor - break-unless next-cursor - # scan to next character - - before-cursor <- copy next-cursor - *editor <- put *editor, before-cursor:offset, before-cursor - go-render?:bool <- move-cursor-coordinates-right editor, screen-height - screen <- move-cursor screen, cursor-row, cursor-column - undo-coalesce-tag:num <- copy 2/right-arrow - - return - } -] - -def move-cursor-coordinates-right editor:&:editor, screen-height:num -> go-render?:bool, editor:&:editor [ - local-scope - load-inputs - before-cursor:&:duplex-list:char <- get *editor before-cursor:offset - cursor-row:num <- get *editor, cursor-row:offset - cursor-column:num <- get *editor, cursor-column:offset - left:num <- get *editor, left:offset - right:num <- get *editor, right:offset - # if crossed a newline, move cursor to start of next row - { - old-cursor-character:char <- get *before-cursor, value:offset - was-at-newline?:bool <- equal old-cursor-character, 10/newline - break-unless was-at-newline? - cursor-row <- add cursor-row, 1 - *editor <- put *editor, cursor-row:offset, cursor-row - cursor-column <- copy left - *editor <- put *editor, cursor-column:offset, cursor-column - below-screen?:bool <- greater-or-equal cursor-row, screen-height # must be equal - return-unless below-screen?, false/don't-render - cursor-row <- subtract cursor-row, 1 # bring back into screen range - *editor <- put *editor, cursor-row:offset, cursor-row - return true/go-render - } - # if the line wraps, move cursor to start of next row - { - # if we're at the column just before the wrap indicator - wrap-column:num <- subtract right, 1 - at-wrap?:bool <- equal cursor-column, wrap-column - break-unless at-wrap? - # and if next character isn't newline - next:&:duplex-list:char <- next before-cursor - break-unless next - next-character:char <- get *next, value:offset - newline?:bool <- equal next-character, 10/newline - break-if newline? - cursor-row <- add cursor-row, 1 - *editor <- put *editor, cursor-row:offset, cursor-row - cursor-column <- copy left - *editor <- put *editor, cursor-column:offset, cursor-column - below-screen?:bool <- greater-or-equal cursor-row, screen-height # must be equal - return-unless below-screen?, false/no-more-render - cursor-row <- subtract cursor-row, 1 # bring back into screen range - *editor <- put *editor, cursor-row:offset, cursor-row - return true/go-render - } - # otherwise move cursor one character right - cursor-column <- add cursor-column, 1 - *editor <- put *editor, cursor-column:offset, cursor-column - go-render? <- copy false -] - -scenario editor-moves-cursor-to-next-line-with-right-arrow [ - local-scope - assume-screen 10/width, 5/height - s:text <- new [abc -d] - e:&:editor <- new-editor s, 0/left, 10/right - editor-render screen, e - $clear-trace - # type right-arrow a few times to get to start of second line - assume-console [ - press right-arrow - press right-arrow - press right-arrow - press right-arrow # next line - ] - run [ - editor-event-loop screen, console, e - ] - check-trace-count-for-label 0, [print-character] - # type something and ensure it goes where it should - assume-console [ - type [0] - ] - run [ - editor-event-loop screen, console, e - ] - screen-should-contain [ - . . - .abc . - .0d . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - check-trace-count-for-label 2, [print-character] # new length of second line -] - -scenario editor-moves-cursor-to-next-line-with-right-arrow-2 [ - local-scope - assume-screen 10/width, 5/height - s:text <- new [abc -d] - e:&:editor <- new-editor s, 1/left, 10/right - editor-render screen, e - assume-console [ - press right-arrow - press right-arrow - press right-arrow - press right-arrow # next line - type [0] - ] - run [ - editor-event-loop screen, console, e - ] - screen-should-contain [ - . . - . abc . - . 0d . - . ┈┈┈┈┈┈┈┈┈. - . . - ] -] - -scenario editor-moves-cursor-to-next-wrapped-line-with-right-arrow [ - local-scope - assume-screen 10/width, 5/height - e:&:editor <- new-editor [abcdef], 0/left, 5/right - editor-render screen, e - $clear-trace - assume-console [ - left-click 1, 3 - press right-arrow - ] - run [ - editor-event-loop screen, console, e - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - ] - screen-should-contain [ - . . - .abcd↩ . - .ef . - .┈┈┈┈┈ . - . . - ] - memory-should-contain [ - 3 <- 2 - 4 <- 0 - ] - check-trace-count-for-label 0, [print-character] -] - -scenario editor-moves-cursor-to-next-wrapped-line-with-right-arrow-2 [ - local-scope - assume-screen 10/width, 5/height - # line just barely wrapping - e:&:editor <- new-editor [abcde], 0/left, 5/right - editor-render screen, e - $clear-trace - # position cursor at last character before wrap and hit right-arrow - assume-console [ - left-click 1, 3 - press right-arrow - ] - run [ - editor-event-loop screen, console, e - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - ] - memory-should-contain [ - 3 <- 2 - 4 <- 0 - ] - # now hit right arrow again - assume-console [ - press right-arrow - ] - run [ - editor-event-loop screen, console, e - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - ] - memory-should-contain [ - 3 <- 2 - 4 <- 1 - ] - check-trace-count-for-label 0, [print-character] -] - -scenario editor-moves-cursor-to-next-wrapped-line-with-right-arrow-3 [ - local-scope - assume-screen 10/width, 5/height - e:&:editor <- new-editor [abcdef], 1/left, 6/right - editor-render screen, e - $clear-trace - assume-console [ - left-click 1, 4 - press right-arrow - ] - run [ - editor-event-loop screen, console, e - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - ] - screen-should-contain [ - . . - . abcd↩ . - . ef . - . ┈┈┈┈┈ . - . . - ] - memory-should-contain [ - 3 <- 2 - 4 <- 1 - ] - check-trace-count-for-label 0, [print-character] -] - -scenario editor-moves-cursor-to-next-line-with-right-arrow-at-end-of-line [ - local-scope - assume-screen 10/width, 5/height - s:text <- new [abc -d] - e:&:editor <- new-editor s, 0/left, 10/right - editor-render screen, e - $clear-trace - # move to end of line, press right-arrow, type a character - assume-console [ - left-click 1, 3 - press right-arrow - type [0] - ] - run [ - editor-event-loop screen, console, e - ] - # new character should be in next line - screen-should-contain [ - . . - .abc . - .0d . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - check-trace-count-for-label 2, [print-character] -] - -# todo: ctrl-right: next word-end - -# left arrow - -scenario editor-moves-cursor-left-with-key [ - local-scope - assume-screen 10/width, 5/height - e:&:editor <- new-editor [abc], 0/left, 10/right - editor-render screen, e - $clear-trace - assume-console [ - left-click 1, 2 - press left-arrow - type [0] - ] - run [ - editor-event-loop screen, console, e - ] - screen-should-contain [ - . . - .a0bc . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - check-trace-count-for-label 3, [print-character] -] - -after [ - { - move-to-previous-character?:bool <- equal k, 65515/left-arrow - break-unless move-to-previous-character? - trace 10, [app], [left arrow] - # if not at start of text (before-cursor at § sentinel) - prev:&:duplex-list:char <- prev before-cursor - return-unless prev, false/don't-render - - move-cursor-coordinates-left editor - before-cursor <- copy prev - *editor <- put *editor, before-cursor:offset, before-cursor - undo-coalesce-tag:num <- copy 1/left-arrow - - return - } -] - -scenario editor-moves-cursor-to-previous-line-with-left-arrow-at-start-of-line [ - local-scope - assume-screen 10/width, 5/height - # initialize editor with two lines - s:text <- new [abc -d] - e:&:editor <- new-editor s, 0/left, 10/right - editor-render screen, e - $clear-trace - # position cursor at start of second line (so there's no previous newline) - assume-console [ - left-click 2, 0 - press left-arrow - ] - run [ - editor-event-loop screen, console, e - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - ] - memory-should-contain [ - 3 <- 1 - 4 <- 3 - ] - check-trace-count-for-label 0, [print-character] -] - -scenario editor-moves-cursor-to-previous-line-with-left-arrow-at-start-of-line-2 [ - local-scope - assume-screen 10/width, 5/height - # initialize editor with three lines - s:text <- new [abc -def -g] - e:&:editor <- new-editor s:text, 0/left, 10/right - editor-render screen, e - $clear-trace - # position cursor further down (so there's a newline before the character at - # the cursor) - assume-console [ - left-click 3, 0 - press left-arrow - type [0] - ] - run [ - editor-event-loop screen, console, e - ] - screen-should-contain [ - . . - .abc . - .def0 . - .g . - .┈┈┈┈┈┈┈┈┈┈. - ] - check-trace-count-for-label 1, [print-character] # just the '0' -] - -scenario editor-moves-cursor-to-previous-line-with-left-arrow-at-start-of-line-3 [ - local-scope - assume-screen 10/width, 5/height - s:text <- new [abc -def -g] - e:&:editor <- new-editor s, 0/left, 10/right - editor-render screen, e - $clear-trace - # position cursor at start of text, press left-arrow, then type a character - assume-console [ - left-click 1, 0 - press left-arrow - type [0] - ] - run [ - editor-event-loop screen, console, e - ] - # left-arrow should have had no effect - screen-should-contain [ - . . - .0abc . - .def . - .g . - .┈┈┈┈┈┈┈┈┈┈. - ] - check-trace-count-for-label 4, [print-character] # length of first line -] - -scenario editor-moves-cursor-to-previous-line-with-left-arrow-at-start-of-line-4 [ - local-scope - assume-screen 10/width, 5/height - # initialize editor with text containing an empty line - s:text <- new [abc - -d] - e:&:editor <- new-editor s, 0/left, 10/right - editor-render screen, e:&:editor - $clear-trace - # position cursor right after empty line - assume-console [ - left-click 3, 0 - press left-arrow - type [0] - ] - run [ - editor-event-loop screen, console, e - ] - screen-should-contain [ - . . - .abc . - .0 . - .d . - .┈┈┈┈┈┈┈┈┈┈. - ] - check-trace-count-for-label 1, [print-character] # just the '0' -] - -scenario editor-moves-across-screen-lines-across-wrap-with-left-arrow [ - local-scope - assume-screen 10/width, 5/height - # initialize editor with a wrapping line - e:&:editor <- new-editor [abcdef], 0/left, 5/right - editor-render screen, e - $clear-trace - screen-should-contain [ - . . - .abcd↩ . - .ef . - .┈┈┈┈┈ . - . . - ] - # position cursor right after empty line - assume-console [ - left-click 2, 0 - press left-arrow - ] - run [ - editor-event-loop screen, console, e - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - ] - memory-should-contain [ - 3 <- 1 # previous row - 4 <- 3 # right margin except wrap icon - ] - check-trace-count-for-label 0, [print-character] -] - -scenario editor-moves-across-screen-lines-to-wrapping-line-with-left-arrow [ - local-scope - assume-screen 10/width, 5/height - # initialize editor with a wrapping line followed by a second line - s:text <- new [abcdef -g] - e:&:editor <- new-editor s, 0/left, 5/right - editor-render screen, e - $clear-trace - screen-should-contain [ - . . - .abcd↩ . - .ef . - .g . - .┈┈┈┈┈ . - ] - # position cursor right after empty line - assume-console [ - left-click 3, 0 - press left-arrow - ] - run [ - editor-event-loop screen, console, e - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - ] - memory-should-contain [ - 3 <- 2 # previous row - 4 <- 2 # end of wrapped line - ] - check-trace-count-for-label 0, [print-character] -] - -scenario editor-moves-across-screen-lines-to-non-wrapping-line-with-left-arrow [ - local-scope - assume-screen 10/width, 5/height - # initialize editor with a line on the verge of wrapping, followed by a second line - s:text <- new [abcd -e] - e:&:editor <- new-editor s, 0/left, 5/right - editor-render screen, e - $clear-trace - screen-should-contain [ - . . - .abcd . - .e . - .┈┈┈┈┈ . - . . - ] - # position cursor right after empty line - assume-console [ - left-click 2, 0 - press left-arrow - ] - run [ - editor-event-loop screen, console, e - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - ] - memory-should-contain [ - 3 <- 1 # previous row - 4 <- 4 # end of wrapped line - ] - check-trace-count-for-label 0, [print-character] -] - -# todo: ctrl-left: previous word-start - -# up arrow - -scenario editor-moves-to-previous-line-with-up-arrow [ - local-scope - assume-screen 10/width, 5/height - s:text <- new [abc -def] - e:&:editor <- new-editor s, 0/left, 10/right - editor-render screen, e - $clear-trace - assume-console [ - left-click 2, 1 - press up-arrow - ] - run [ - editor-event-loop screen, console, e - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - ] - memory-should-contain [ - 3 <- 1 - 4 <- 1 - ] - check-trace-count-for-label 0, [print-character] - assume-console [ - type [0] - ] - run [ - editor-event-loop screen, console, e - ] - screen-should-contain [ - . . - .a0bc . - .def . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] -] - -after [ - { - move-to-previous-line?:bool <- equal k, 65517/up-arrow - break-unless move-to-previous-line? - - move-to-previous-line editor - undo-coalesce-tag:num <- copy 3/up-arrow - - return - } -] - -def move-to-previous-line editor:&:editor -> editor:&:editor [ - local-scope - load-inputs - cursor-row:num <- get *editor, cursor-row:offset - cursor-column:num <- get *editor, cursor-column:offset - before-cursor:&:duplex-list:char <- get *editor, before-cursor:offset - left:num <- get *editor, left:offset - right:num <- get *editor, right:offset - already-at-top?:bool <- lesser-or-equal cursor-row, 1/top - { - # if cursor not at top, move it - break-if already-at-top? - # 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:&:duplex-list:char <- copy before-cursor - old:&:duplex-list:char <- copy curr - { - at-left?:bool <- equal cursor-column, left - break-if at-left? - curr <- before-previous-screen-line curr, editor - no-motion?:bool <- equal curr, old - return-if no-motion? - } - { - curr <- before-previous-screen-line curr, editor - no-motion?:bool <- equal curr, old - return-if no-motion? - } - before-cursor <- copy curr - *editor <- put *editor, before-cursor:offset, before-cursor - cursor-row <- subtract cursor-row, 1 - *editor <- put *editor, cursor-row:offset, cursor-row - # scan ahead to right column or until end of line - target-column:num <- copy cursor-column - cursor-column <- copy left - *editor <- put *editor, cursor-column:offset, cursor-column - { - done?:bool <- greater-or-equal cursor-column, target-column - break-if done? - curr:&:duplex-list:char <- next before-cursor - break-unless curr - currc:char <- get *curr, value:offset - at-newline?:bool <- equal currc, 10/newline - break-if at-newline? - # - before-cursor <- copy curr - *editor <- put *editor, before-cursor:offset, before-cursor - cursor-column <- add cursor-column, 1 - *editor <- put *editor, cursor-column:offset, cursor-column - loop - } - } -] - -# Takes a pointer into the doubly-linked list, scans back to before start of -# previous *wrapped* line. -# Returns original if no next newline. -# Beware: never return null pointer. -def before-previous-screen-line in:&:duplex-list:char, editor:&:editor -> out:&:duplex-list:char [ - local-scope - load-inputs - curr:&:duplex-list:char <- copy in - c:char <- 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 - left:num <- get *editor, left:offset - right:num <- get *editor, right:offset - max-line-length:num <- subtract right, left, -1/exclusive-right, 1/wrap-icon - sentinel:&:duplex-list:char <- get *editor, data:offset - len:num <- previous-line-length curr, sentinel - { - break-if len - # empty line; just skip this newline - prev:&:duplex-list:char <- prev curr - return-unless prev, curr - return prev - } - _, max:num <- divide-with-remainder len, max-line-length - # remainder 0 => scan one width-worth - { - break-if max - max <- copy max-line-length - } - max <- add max, 1 - count:num <- copy 0 - # skip 'max' characters - { - done?:bool <- greater-or-equal count, max - break-if done? - prev:&:duplex-list:char <- prev curr - break-unless prev - curr <- copy prev - count <- add count, 1 - loop - } - return curr -] - -scenario editor-adjusts-column-at-previous-line [ - local-scope - assume-screen 10/width, 5/height - s:text <- new [ab -def] - e:&:editor <- new-editor s, 0/left, 10/right - editor-render screen, e - $clear-trace - assume-console [ - left-click 2, 3 - press up-arrow - ] - run [ - editor-event-loop screen, console, e - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - ] - memory-should-contain [ - 3 <- 1 - 4 <- 2 - ] - check-trace-count-for-label 0, [print-character] - assume-console [ - type [0] - ] - run [ - editor-event-loop screen, console, e - ] - screen-should-contain [ - . . - .ab0 . - .def . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] -] - -scenario editor-adjusts-column-at-empty-line [ - local-scope - assume-screen 10/width, 5/height - s:text <- new [ -def] - e:&:editor <- new-editor s, 0/left, 10/right - editor-render screen, e - $clear-trace - assume-console [ - left-click 2, 3 - press up-arrow - ] - run [ - editor-event-loop screen, console, e - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - ] - memory-should-contain [ - 3 <- 1 - 4 <- 0 - ] - check-trace-count-for-label 0, [print-character] - assume-console [ - type [0] - ] - run [ - editor-event-loop screen, console, e - ] - screen-should-contain [ - . . - .0 . - .def . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] -] - -scenario editor-moves-to-previous-line-from-zero-margin [ - local-scope - assume-screen 10/width, 5/height - # start out with three lines - s:text <- new [abc -def -ghi] - e:&:editor <- new-editor s, 0/left, 10/right - editor-render screen, e - $clear-trace - # click on the third line and hit up-arrow, so you end up just after a newline - assume-console [ - left-click 3, 0 - press up-arrow - ] - run [ - editor-event-loop screen, console, e - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - ] - memory-should-contain [ - 3 <- 2 - 4 <- 0 - ] - check-trace-count-for-label 0, [print-character] - assume-console [ - type [0] - ] - run [ - editor-event-loop screen, console, e - ] - screen-should-contain [ - . . - .abc . - .0def . - .ghi . - .┈┈┈┈┈┈┈┈┈┈. - ] -] - -scenario editor-moves-to-previous-line-from-left-margin [ - local-scope - assume-screen 10/width, 5/height - # start out with three lines - s:text <- new [abc -def -ghi] - e:&:editor <- new-editor s, 1/left, 10/right - editor-render screen, e - $clear-trace - # click on the third line and hit up-arrow, so you end up just after a newline - assume-console [ - left-click 3, 1 - press up-arrow - ] - run [ - editor-event-loop screen, console, e - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - ] - memory-should-contain [ - 3 <- 2 - 4 <- 1 - ] - check-trace-count-for-label 0, [print-character] - assume-console [ - type [0] - ] - run [ - editor-event-loop screen, console, e - ] - screen-should-contain [ - . . - . abc . - . 0def . - . ghi . - . ┈┈┈┈┈┈┈┈┈. - ] -] - -scenario editor-moves-to-top-line-in-presence-of-wrapped-line [ - local-scope - assume-screen 10/width, 5/height - e:&:editor <- new-editor [abcde], 0/left, 5/right - editor-render screen, e - screen-should-contain [ - . . - .abcd↩ . - .e . - .┈┈┈┈┈ . - ] - $clear-trace - assume-console [ - left-click 2, 0 - press up-arrow - ] - run [ - editor-event-loop screen, console, e - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - ] - memory-should-contain [ - 3 <- 1 - 4 <- 0 - ] - check-trace-count-for-label 0, [print-character] - assume-console [ - type [0] - ] - run [ - editor-event-loop screen, console, e - ] - screen-should-contain [ - . . - .0abc↩ . - .de . - .┈┈┈┈┈ . - ] -] - -scenario editor-moves-to-top-line-in-presence-of-wrapped-line-2 [ - local-scope - assume-screen 10/width, 5/height - s:text <- new [abc -defgh] - e:&:editor <- new-editor s, 0/left, 5/right - editor-render screen, e - screen-should-contain [ - . . - .abc . - .defg↩ . - .h . - .┈┈┈┈┈ . - ] - $clear-trace - assume-console [ - left-click 3, 0 - press up-arrow - press up-arrow - ] - run [ - editor-event-loop screen, console, e - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - ] - memory-should-contain [ - 3 <- 1 - 4 <- 0 - ] - check-trace-count-for-label 0, [print-character] - assume-console [ - type [0] - ] - run [ - editor-event-loop screen, console, e - ] - screen-should-contain [ - . . - .0abc . - .defg↩ . - .h . - .┈┈┈┈┈ . - ] -] - -# down arrow - -scenario editor-moves-to-next-line-with-down-arrow [ - local-scope - assume-screen 10/width, 5/height - s:text <- new [abc -def] - e:&:editor <- new-editor s, 0/left, 10/right - editor-render screen, e - $clear-trace - # cursor starts out at (1, 0) - assume-console [ - press down-arrow - ] - run [ - editor-event-loop screen, console, e - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - ] - # ..and ends at (2, 0) - memory-should-contain [ - 3 <- 2 - 4 <- 0 - ] - check-trace-count-for-label 0, [print-character] - assume-console [ - type [0] - ] - run [ - editor-event-loop screen, console, e - ] - screen-should-contain [ - . . - .abc . - .0def . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] -] - -after [ - { - move-to-next-line?:bool <- equal k, 65516/down-arrow - break-unless move-to-next-line? - - move-to-next-line editor, screen-height - undo-coalesce-tag:num <- copy 4/down-arrow - - return - } -] - -def move-to-next-line editor:&:editor, screen-height:num -> editor:&:editor [ - local-scope - load-inputs - cursor-row:num <- get *editor, cursor-row:offset - cursor-column:num <- get *editor, cursor-column:offset - before-cursor:&:duplex-list:char <- get *editor, before-cursor:offset - left:num <- get *editor, left:offset - right:num <- get *editor, right:offset - last-line:num <- subtract screen-height, 1 - bottom:num <- get *editor, bottom:offset - at-bottom-of-screen?:bool <- greater-or-equal bottom, last-line - return-unless before-cursor - next:&:duplex-list:char <- next before-cursor - return-unless next - already-at-bottom?:bool <- greater-or-equal cursor-row, last-line - # if cursor not at bottom, move it - return-if already-at-bottom? - target-column:num <- copy cursor-column - # scan to start of next line - { - next:&:duplex-list:char <- next before-cursor - break-unless next - done?:bool <- greater-or-equal cursor-column, right - break-if done? - cursor-column <- add cursor-column, 1 - before-cursor <- copy next - c:char <- get *next, value:offset - at-newline?:bool <- equal c, 10/newline - break-if at-newline? - loop - } - return-unless next - cursor-row <- add cursor-row, 1 - cursor-column <- copy left - { - next:&:duplex-list:char <- next before-cursor - break-unless next - c:char <- get *next, value:offset - at-newline?:bool <- equal c, 10/newline - break-if at-newline? - done?:bool <- greater-or-equal cursor-column, target-column - break-if done? - cursor-column <- add cursor-column, 1 - before-cursor <- copy next - loop - } - *editor <- put *editor, before-cursor:offset, before-cursor - *editor <- put *editor, cursor-column:offset, cursor-column - *editor <- put *editor, cursor-row:offset, cursor-row -] - -scenario editor-adjusts-column-at-next-line [ - local-scope - assume-screen 10/width, 5/height - # second line is shorter than first - s:text <- new [abcde -fg -hi] - e:&:editor <- new-editor s, 0/left, 10/right - editor-render screen, e - $clear-trace - # move to end of first line, then press down - assume-console [ - left-click 1, 8 - press down-arrow - ] - run [ - editor-event-loop screen, console, e - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - ] - # cursor doesn't go vertically down, it goes to end of shorter line - memory-should-contain [ - 3 <- 2 - 4 <- 2 - ] - check-trace-count-for-label 0, [print-character] - assume-console [ - type [0] - ] - run [ - editor-event-loop screen, console, e - ] - screen-should-contain [ - . . - .abcde . - .fg0 . - .hi . - .┈┈┈┈┈┈┈┈┈┈. - ] -] - -# ctrl-a/home - move cursor to start of line - -scenario editor-moves-to-start-of-line-with-ctrl-a [ - local-scope - assume-screen 10/width, 5/height - s:text <- new [123 -456] - e:&:editor <- new-editor s, 0/left, 10/right - editor-render screen, e - $clear-trace - # start on second line, press ctrl-a - assume-console [ - left-click 2, 3 - press ctrl-a - ] - run [ - editor-event-loop screen, console, e - 4:num/raw <- get *e, cursor-row:offset - 5:num/raw <- get *e, cursor-column:offset - ] - # cursor moves to start of line - memory-should-contain [ - 4 <- 2 - 5 <- 0 - ] - check-trace-count-for-label 0, [print-character] -] - -after [ - { - move-to-start-of-line?:bool <- equal c, 1/ctrl-a - break-unless move-to-start-of-line? - - move-to-start-of-screen-line editor - undo-coalesce-tag:num <- copy 0/never - - return false/don't-render - } -] - -after [ - { - move-to-start-of-line?:bool <- equal k, 65521/home - break-unless move-to-start-of-line? - - move-to-start-of-screen-line editor - undo-coalesce-tag:num <- copy 0/never - - return false/don't-render - } -] - -# handles wrapped lines -# precondition: cursor-column should be in a consistent state -def move-to-start-of-screen-line editor:&:editor -> editor:&:editor [ - local-scope - load-inputs - # update cursor column - left:num <- get *editor, left:offset - col:num <- get *editor, cursor-column:offset - # update before-cursor - curr:&:duplex-list:char <- get *editor, before-cursor:offset - # while not at start of line, move - { - done?:bool <- equal col, left - break-if done? - assert curr, [move-to-start-of-line tried to move before start of text] - curr <- prev curr - col <- subtract col, 1 - loop - } - *editor <- put *editor, cursor-column:offset, col - *editor <- put *editor, before-cursor:offset, curr -] - -scenario editor-moves-to-start-of-line-with-ctrl-a-2 [ - local-scope - assume-screen 10/width, 5/height - s:text <- new [123 -456] - e:&:editor <- new-editor s, 0/left, 10/right - editor-render screen, e - $clear-trace - # start on first line (no newline before), press ctrl-a - assume-console [ - left-click 1, 3 - press ctrl-a - ] - run [ - editor-event-loop screen, console, e - 4:num/raw <- get *e, cursor-row:offset - 5:num/raw <- get *e, cursor-column:offset - ] - # cursor moves to start of line - memory-should-contain [ - 4 <- 1 - 5 <- 0 - ] - check-trace-count-for-label 0, [print-character] -] - -scenario editor-moves-to-start-of-line-with-home [ - local-scope - assume-screen 10/width, 5/height - s:text <- new [123 -456] - e:&:editor <- new-editor s, 0/left, 10/right - $clear-trace - # start on second line, press 'home' - assume-console [ - left-click 2, 3 - press home - ] - run [ - editor-event-loop screen, console, e - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - ] - # cursor moves to start of line - memory-should-contain [ - 3 <- 2 - 4 <- 0 - ] - check-trace-count-for-label 0, [print-character] -] - -scenario editor-moves-to-start-of-line-with-home-2 [ - local-scope - assume-screen 10/width, 5/height - s:text <- new [123 -456] - e:&:editor <- new-editor s, 0/left, 10/right - editor-render screen, e - $clear-trace - # start on first line (no newline before), press 'home' - assume-console [ - left-click 1, 3 - press home - ] - run [ - editor-event-loop screen, console, e - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - ] - # cursor moves to start of line - memory-should-contain [ - 3 <- 1 - 4 <- 0 - ] - check-trace-count-for-label 0, [print-character] -] - -scenario editor-moves-to-start-of-screen-line-with-ctrl-a [ - local-scope - assume-screen 10/width, 5/height - e:&:editor <- new-editor [123456], 0/left, 5/right - editor-render screen, e - screen-should-contain [ - . . - .1234↩ . - .56 . - .┈┈┈┈┈ . - . . - ] - $clear-trace - # start on second line, press ctrl-a then up - assume-console [ - left-click 2, 1 - press ctrl-a - press up-arrow - ] - run [ - editor-event-loop screen, console, e - 4:num/raw <- get *e, cursor-row:offset - 5:num/raw <- get *e, cursor-column:offset - ] - # cursor moves to start of first line - memory-should-contain [ - 4 <- 1 # cursor-row - 5 <- 0 # cursor-column - ] - check-trace-count-for-label 0, [print-character] - # make sure before-cursor is in sync - assume-console [ - type [a] - ] - run [ - editor-event-loop screen, console, e - 4:num/raw <- get *e, cursor-row:offset - 5:num/raw <- get *e, cursor-column:offset - ] - screen-should-contain [ - . . - .a123↩ . - .456 . - .┈┈┈┈┈ . - . . - ] - memory-should-contain [ - 4 <- 1 # cursor-row - 5 <- 1 # cursor-column - ] -] - -# ctrl-e/end - move cursor to end of line - -scenario editor-moves-to-end-of-line-with-ctrl-e [ - local-scope - assume-screen 10/width, 5/height - s:text <- new [123 -456] - e:&:editor <- new-editor s, 0/left, 10/right - editor-render screen, e - $clear-trace - # start on first line, press ctrl-e - assume-console [ - left-click 1, 1 - press ctrl-e - ] - run [ - editor-event-loop screen, console, e - 4:num/raw <- get *e, cursor-row:offset - 5:num/raw <- get *e, cursor-column:offset - ] - # cursor moves to end of line - memory-should-contain [ - 4 <- 1 - 5 <- 3 - ] - check-trace-count-for-label 0, [print-character] - # editor inserts future characters at cursor - assume-console [ - type [z] - ] - run [ - editor-event-loop screen, console, e - 4:num/raw <- get *e, cursor-row:offset - 5:num/raw <- get *e, cursor-column:offset - ] - memory-should-contain [ - 4 <- 1 - 5 <- 4 - ] - screen-should-contain [ - . . - .123z . - .456 . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - check-trace-count-for-label 1, [print-character] -] - -after [ - { - move-to-end-of-line?:bool <- equal c, 5/ctrl-e - break-unless move-to-end-of-line? - - move-to-end-of-line editor - undo-coalesce-tag:num <- copy 0/never - - return false/don't-render - } -] - -after [ - { - move-to-end-of-line?:bool <- equal k, 65520/end - break-unless move-to-end-of-line? - - move-to-end-of-line editor - undo-coalesce-tag:num <- copy 0/never - - return false/don't-render - } -] - -def move-to-end-of-line editor:&:editor -> editor:&:editor [ - local-scope - load-inputs - before-cursor:&:duplex-list:char <- get *editor, before-cursor:offset - cursor-column:num <- get *editor, cursor-column:offset - right:num <- get *editor, right:offset - # while not at end of line, move - { - next:&:duplex-list:char <- next before-cursor - break-unless next # end of text - nextc:char <- get *next, value:offset - at-end-of-line?:bool <- equal nextc, 10/newline - break-if at-end-of-line? - cursor-column <- add cursor-column, 1 - at-right?:bool <- equal cursor-column, right - break-if at-right? - *editor <- put *editor, cursor-column:offset, cursor-column - before-cursor <- copy next - *editor <- put *editor, before-cursor:offset, before-cursor - loop - } -] - -scenario editor-moves-to-end-of-line-with-ctrl-e-2 [ - local-scope - assume-screen 10/width, 5/height - s:text <- new [123 -456] - e:&:editor <- new-editor s, 0/left, 10/right - editor-render screen, e - $clear-trace - # start on second line (no newline after), press ctrl-e - assume-console [ - left-click 2, 1 - press ctrl-e - ] - run [ - editor-event-loop screen, console, e - 4:num/raw <- get *e, cursor-row:offset - 5:num/raw <- get *e, cursor-column:offset - ] - # cursor moves to end of line - memory-should-contain [ - 4 <- 2 - 5 <- 3 - ] - check-trace-count-for-label 0, [print-character] -] - -scenario editor-moves-to-end-of-line-with-end [ - local-scope - assume-screen 10/width, 5/height - s:text <- new [123 -456] - e:&:editor <- new-editor s, 0/left, 10/right - editor-render screen, e - $clear-trace - # start on first line, press 'end' - assume-console [ - left-click 1, 1 - press end - ] - run [ - editor-event-loop screen, console, e - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - ] - # cursor moves to end of line - memory-should-contain [ - 3 <- 1 - 4 <- 3 - ] - check-trace-count-for-label 0, [print-character] -] - -scenario editor-moves-to-end-of-line-with-end-2 [ - local-scope - assume-screen 10/width, 5/height - s:text <- new [123 -456] - e:&:editor <- new-editor s, 0/left, 10/right - editor-render screen, e - $clear-trace - # start on second line (no newline after), press 'end' - assume-console [ - left-click 2, 1 - press end - ] - run [ - editor-event-loop screen, console, e - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - ] - # cursor moves to end of line - memory-should-contain [ - 3 <- 2 - 4 <- 3 - ] - check-trace-count-for-label 0, [print-character] -] - -scenario editor-moves-to-end-of-wrapped-line [ - local-scope - assume-screen 10/width, 5/height - s:text <- new [123456 -789] - e:&:editor <- new-editor s, 0/left, 5/right - editor-render screen, e - $clear-trace - # start on first line, press 'end' - assume-console [ - left-click 1, 1 - press end - ] - run [ - editor-event-loop screen, console, e - 10:num/raw <- get *e, cursor-row:offset - 11:num/raw <- get *e, cursor-column:offset - ] - # cursor moves to end of line - memory-should-contain [ - 10 <- 1 - 11 <- 3 - ] - # no prints - check-trace-count-for-label 0, [print-character] - # before-cursor is also consistent - assume-console [ - type [a] - ] - run [ - editor-event-loop screen, console, e - ] - screen-should-contain [ - . . - .123a↩ . - .456 . - .789 . - .┈┈┈┈┈ . - ] -] - -# ctrl-u - delete text from start of line until (but not at) cursor - -scenario editor-deletes-to-start-of-line-with-ctrl-u [ - local-scope - assume-screen 10/width, 5/height - s:text <- new [123 -456] - e:&:editor <- new-editor s, 0/left, 10/right - editor-render screen, e - $clear-trace - # start on second line, press ctrl-u - assume-console [ - left-click 2, 2 - press ctrl-u - ] - run [ - editor-event-loop screen, console, e - ] - # cursor deletes to start of line - screen-should-contain [ - . . - .123 . - .6 . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - check-trace-count-for-label 10, [print-character] -] - -after [ - { - delete-to-start-of-line?:bool <- equal c, 21/ctrl-u - break-unless delete-to-start-of-line? - - deleted-cells:&:duplex-list:char <- delete-to-start-of-line editor - - go-render?:bool <- minimal-render-for-ctrl-u screen, editor, deleted-cells - return - } -] - -def minimal-render-for-ctrl-u screen:&:screen, editor:&:editor, deleted-cells:&:duplex-list:char -> go-render?:bool, screen:&:screen [ - local-scope - load-inputs - curr-column:num <- get *editor, cursor-column:offset - # accumulate the current line as text and render it - buf:&:buffer:char <- new-buffer 30 # accumulator for the text we need to render - curr:&:duplex-list:char <- get *editor, before-cursor:offset - i:num <- copy curr-column - right:num <- get *editor, right:offset - { - # if we have a wrapped line, give up and render the whole screen - wrap?:bool <- greater-or-equal i, right - return-if wrap?, true/go-render - curr <- next curr - break-unless curr - c:char <- get *curr, value:offset - b:bool <- equal c, 10 - break-if b - buf <- append buf, c - i <- add i, 1 - loop - } - # if the line used to be wrapped, give up and render the whole screen - num-deleted-cells:num <- length deleted-cells - old-row-len:num <- add i, num-deleted-cells - left:num <- get *editor, left:offset - end:num <- subtract right, left - wrap?:bool <- greater-or-equal old-row-len, end - return-if wrap?, true/go-render - curr-line:text <- buffer-to-array buf - curr-row:num <- get *editor, cursor-row:offset - render-code screen, curr-line, curr-column, right, curr-row - return false/dont-render -] - -def delete-to-start-of-line editor:&:editor -> result:&:duplex-list:char, editor:&:editor [ - local-scope - load-inputs - # compute range to delete - init:&:duplex-list:char <- get *editor, data:offset - before-cursor:&:duplex-list:char <- get *editor, before-cursor:offset - start:&:duplex-list:char <- copy before-cursor - end:&:duplex-list:char <- next before-cursor - { - at-start-of-text?:bool <- equal start, init - break-if at-start-of-text? - curr:char <- get *start, value:offset - at-start-of-line?:bool <- equal curr, 10/newline - break-if at-start-of-line? - start <- prev start - assert start, [delete-to-start-of-line tried to move before start of text] - loop - } - # snip it out - result:&:duplex-list:char <- next start - remove-between start, end - # adjust cursor - before-cursor <- copy start - *editor <- put *editor, before-cursor:offset, before-cursor - left:num <- get *editor, left:offset - *editor <- put *editor, cursor-column:offset, left - # if the line wrapped before, we may need to adjust cursor-row as well - right:num <- get *editor, right:offset - width:num <- subtract right, left - num-deleted:num <- length result - cursor-row-adjustment:num <- divide-with-remainder num-deleted, width - return-unless cursor-row-adjustment - cursor-row:num <- get *editor, cursor-row:offset - cursor-row <- subtract cursor-row, cursor-row-adjustment - put *editor, cursor-row:offset, cursor-row -] - -def render-code screen:&:screen, s:text, left:num, right:num, row:num -> row:num, screen:&:screen [ - local-scope - load-inputs - return-unless s - color:num <- copy 7/white - column:num <- copy left - screen <- move-cursor screen, row, column - screen-height:num <- screen-height screen - i:num <- copy 0 - len:num <- length *s - { - +next-character - done?:bool <- greater-or-equal i, len - break-if done? - done? <- greater-or-equal row, screen-height - break-if done? - c:char <- index *s, i - - { - # newline? move to left rather than 0 - newline?:bool <- equal c, 10/newline - break-unless newline? - # clear rest of line in this window - { - done?:bool <- greater-than column, right - break-if done? - space:char <- copy 32/space - print screen, space - column <- add column, 1 - loop - } - row <- add row, 1 - column <- copy left - screen <- move-cursor screen, row, column - i <- add i, 1 - loop +next-character - } - { - # at right? wrap. - at-right?:bool <- equal column, right - break-unless at-right? - # print wrap icon - wrap-icon:char <- copy 8617/loop-back-to-left - print screen, wrap-icon, 245/grey - column <- copy left - row <- add row, 1 - screen <- move-cursor screen, row, column - # don't increment i - loop +next-character - } - i <- add i, 1 - print screen, c, color - column <- add column, 1 - loop - } - was-at-left?:bool <- equal column, left - clear-line-until screen, right - { - break-if was-at-left? - row <- add row, 1 - } - move-cursor screen, row, left -] - -scenario editor-deletes-to-start-of-line-with-ctrl-u-2 [ - local-scope - assume-screen 10/width, 5/height - s:text <- new [123 -456] - e:&:editor <- new-editor s, 0/left, 10/right - editor-render screen, e - $clear-trace - # start on first line (no newline before), press ctrl-u - assume-console [ - left-click 1, 2 - press ctrl-u - ] - run [ - editor-event-loop screen, console, e - ] - # cursor deletes to start of line - screen-should-contain [ - . . - .3 . - .456 . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - check-trace-count-for-label 10, [print-character] -] - -scenario editor-deletes-to-start-of-line-with-ctrl-u-3 [ - local-scope - assume-screen 10/width, 5/height - s:text <- new [123 -456] - e:&:editor <- new-editor s, 0/left, 10/right - editor-render screen, e - $clear-trace - # start past end of line, press ctrl-u - assume-console [ - left-click 1, 3 - press ctrl-u - ] - run [ - editor-event-loop screen, console, e - ] - # cursor deletes to start of line - screen-should-contain [ - . . - . . - .456 . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - check-trace-count-for-label 10, [print-character] -] - -scenario editor-deletes-to-start-of-final-line-with-ctrl-u [ - local-scope - assume-screen 10/width, 5/height - s:text <- new [123 -456] - e:&:editor <- new-editor s, 0/left, 10/right - editor-render screen, e - $clear-trace - # start past end of final line, press ctrl-u - assume-console [ - left-click 2, 3 - press ctrl-u - ] - run [ - editor-event-loop screen, console, e - ] - # cursor deletes to start of line - screen-should-contain [ - . . - .123 . - . . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - check-trace-count-for-label 10, [print-character] -] - -scenario editor-deletes-to-start-of-wrapped-line-with-ctrl-u [ - local-scope - assume-screen 10/width, 10/height - # first line starts out wrapping - s:text <- new [123456 -789] - e:&:editor <- new-editor s, 0/left, 5/right - editor-render screen, e - screen-should-contain [ - . . - .1234↩ . - .56 . - .789 . - .┈┈┈┈┈ . - . . - ] - $clear-trace - # ctrl-u enough of the first line that it's no longer wrapping - assume-console [ - left-click 1, 3 - press ctrl-u - ] - run [ - editor-event-loop screen, console, e - ] - # entire screen needs to be refreshed - screen-should-contain [ - . . - .456 . - .789 . - .┈┈┈┈┈ . - . . - ] - check-trace-count-for-label 45, [print-character] -] - -# sometimes hitting ctrl-u needs to adjust the cursor row -scenario editor-deletes-to-start-of-wrapped-line-with-ctrl-u-2 [ - local-scope - assume-screen 10/width, 10/height - # third line starts out wrapping - s:text <- new [1 -2 -345678 -9] - e:&:editor <- new-editor s, 0/left, 5/right - editor-render screen, e - screen-should-contain [ - . . - .1 . - .2 . - .3456↩ . - .78 . - .9 . - .┈┈┈┈┈ . - . . - ] - # position cursor on screen line after the wrap and hit ctrl-u - assume-console [ - left-click 4, 1 # on '8' - press ctrl-u - ] - run [ - editor-event-loop screen, console, e - 10:num/raw <- get *e, cursor-row:offset - 11:num/raw <- get *e, cursor-column:offset - ] - screen-should-contain [ - . . - .1 . - .2 . - .8 . - .9 . - .┈┈┈┈┈ . - . . - ] - # cursor moves up one screen line - memory-should-contain [ - 10 <- 3 # cursor-row - 11 <- 0 # cursor-column - ] -] - -# line wrapping twice (taking up 3 screen lines) -scenario editor-deletes-to-start-of-wrapped-line-with-ctrl-u-3 [ - local-scope - assume-screen 10/width, 10/height - # third line starts out wrapping - s:text <- new [1 -2 -3456789abcd -e] - e:&:editor <- new-editor s, 0/left, 5/right - editor-render screen, e - assume-console [ - left-click 4, 1 # on '8' - ] - editor-event-loop screen, console, e - screen-should-contain [ - . . - .1 . - .2 . - .3456↩ . - .789a↩ . - .bcd . - .e . - .┈┈┈┈┈ . - . . - ] - assume-console [ - left-click 5, 1 - press ctrl-u - ] - run [ - editor-event-loop screen, console, e - 10:num/raw <- get *e, cursor-row:offset - 11:num/raw <- get *e, cursor-column:offset - ] - screen-should-contain [ - . . - .1 . - .2 . - .cd . - .e . - .┈┈┈┈┈ . - . . - ] - # make sure we adjusted cursor-row - memory-should-contain [ - 10 <- 3 # cursor-row - 11 <- 0 # cursor-column - ] -] - -# ctrl-k - delete text from cursor to end of line (but not the newline) - -scenario editor-deletes-to-end-of-line-with-ctrl-k [ - local-scope - assume-screen 10/width, 5/height - s:text <- new [123 -456] - e:&:editor <- new-editor s, 0/left, 10/right - editor-render screen, e - $clear-trace - # start on first line, press ctrl-k - assume-console [ - left-click 1, 1 - press ctrl-k - ] - run [ - editor-event-loop screen, console, e - ] - # cursor deletes to end of line - screen-should-contain [ - . . - .1 . - .456 . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - check-trace-count-for-label 9, [print-character] -] - -after [ - { - delete-to-end-of-line?:bool <- equal c, 11/ctrl-k - break-unless delete-to-end-of-line? - - deleted-cells:&:duplex-list:char <- delete-to-end-of-line editor - - # checks if we can do a minimal render and if we can it will do a minimal render - go-render?:bool <- minimal-render-for-ctrl-k screen, editor, deleted-cells - return - } -] - -def minimal-render-for-ctrl-k screen:&:screen, editor:&:editor, deleted-cells:&:duplex-list:char -> go-render?:bool, screen:&:screen [ - local-scope - load-inputs - # if we deleted nothing, there's nothing to render - return-unless deleted-cells, false/dont-render - # if the line used to wrap before, give up and render the whole screen - curr-column:num <- get *editor, cursor-column:offset - num-deleted-cells:num <- length deleted-cells - old-row-len:num <- add curr-column, num-deleted-cells - left:num <- get *editor, left:offset - right:num <- get *editor, right:offset - end:num <- subtract right, left - wrap?:bool <- greater-or-equal old-row-len, end - return-if wrap?, true/go-render - clear-line-until screen, right - return false/dont-render -] - -def delete-to-end-of-line editor:&:editor -> result:&:duplex-list:char, editor:&:editor [ - local-scope - load-inputs - # compute range to delete - start:&:duplex-list:char <- get *editor, before-cursor:offset - end:&:duplex-list:char <- next start - { - at-end-of-text?:bool <- equal end, null - break-if at-end-of-text? - curr:char <- get *end, value:offset - at-end-of-line?:bool <- equal curr, 10/newline - break-if at-end-of-line? - end <- next end - loop - } - # snip it out - result <- next start - remove-between start, end -] - -scenario editor-deletes-to-end-of-line-with-ctrl-k-2 [ - local-scope - assume-screen 10/width, 5/height - s:text <- new [123 -456] - e:&:editor <- new-editor s, 0/left, 10/right - editor-render screen, e - $clear-trace - # start on second line (no newline after), press ctrl-k - assume-console [ - left-click 2, 1 - press ctrl-k - ] - run [ - editor-event-loop screen, console, e - ] - # cursor deletes to end of line - screen-should-contain [ - . . - .123 . - .4 . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - check-trace-count-for-label 9, [print-character] -] - -scenario editor-deletes-to-end-of-line-with-ctrl-k-3 [ - local-scope - assume-screen 10/width, 5/height - s:text <- new [123 -456] - e:&:editor <- new-editor s, 0/left, 10/right - editor-render screen, e - $clear-trace - # start at end of line - assume-console [ - left-click 1, 2 - press ctrl-k - ] - run [ - editor-event-loop screen, console, e - ] - # cursor deletes just last character - screen-should-contain [ - . . - .12 . - .456 . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - check-trace-count-for-label 8, [print-character] -] - -scenario editor-deletes-to-end-of-line-with-ctrl-k-4 [ - local-scope - assume-screen 10/width, 5/height - s:text <- new [123 -456] - e:&:editor <- new-editor s, 0/left, 10/right - editor-render screen, e - $clear-trace - # start past end of line - assume-console [ - left-click 1, 3 - press ctrl-k - ] - run [ - editor-event-loop screen, console, e - ] - # cursor deletes nothing - screen-should-contain [ - . . - .123 . - .456 . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - check-trace-count-for-label 7, [print-character] -] - -scenario editor-deletes-to-end-of-line-with-ctrl-k-5 [ - local-scope - assume-screen 10/width, 5/height - s:text <- new [123 -456] - e:&:editor <- new-editor s, 0/left, 10/right - editor-render screen, e - $clear-trace - # start at end of text - assume-console [ - left-click 2, 2 - press ctrl-k - ] - run [ - editor-event-loop screen, console, e - ] - # cursor deletes just the final character - screen-should-contain [ - . . - .123 . - .45 . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - check-trace-count-for-label 8, [print-character] -] - -scenario editor-deletes-to-end-of-line-with-ctrl-k-6 [ - local-scope - assume-screen 10/width, 5/height - s:text <- new [123 -456] - e:&:editor <- new-editor s, 0/left, 10/right - editor-render screen, e - $clear-trace - # start past end of text - assume-console [ - left-click 2, 3 - press ctrl-k - ] - run [ - editor-event-loop screen, console, e - ] - # cursor deletes nothing - screen-should-contain [ - . . - .123 . - .456 . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - # no prints necessary - check-trace-count-for-label 0, [print-character] -] - -scenario editor-deletes-to-end-of-wrapped-line-with-ctrl-k [ - local-scope - assume-screen 10/width, 5/height - # create an editor with the first line wrapping to a second screen row - s:text <- new [1234 -567] - e:&:editor <- new-editor s, 0/left, 4/right - editor-render screen, e - $clear-trace - # delete all of the first wrapped line - assume-console [ - press ctrl-k - ] - run [ - editor-event-loop screen, console, e - ] - # screen shows an empty unwrapped first line - screen-should-contain [ - . . - . . - .567 . - .┈┈┈┈ . - . . - ] - # entire screen is refreshed - check-trace-count-for-label 16, [print-character] -] - -# takes a pointer into the doubly-linked list, scans ahead at most 'max' -# positions until the next newline -# returns original if no next newline -# beware: never return null pointer. -def before-start-of-next-line original:&:duplex-list:char, max:num -> curr:&:duplex-list:char [ - local-scope - load-inputs - count:num <- copy 0 - curr:&:duplex-list:char <- copy original - # skip the initial newline if it exists - { - c:char <- get *curr, value:offset - at-newline?:bool <- equal c, 10/newline - break-unless at-newline? - curr <- next curr - count <- add count, 1 - } - { - return-unless curr, original - done?:bool <- greater-or-equal count, max - break-if done? - c:char <- get *curr, value:offset - at-newline?:bool <- equal c, 10/newline - break-if at-newline? - curr <- next curr - count <- add count, 1 - loop - } - return-unless curr, original - return curr -] - -# ctrl-/ - comment/uncomment current line - -after [ - { - comment-toggle?:bool <- equal c, 31/ctrl-slash - break-unless comment-toggle? - cursor-column:num <- get *editor, cursor-column:offset - data:&:duplex-list:char <- get *editor, data:offset - - before-line-start:&:duplex-list:char <- before-start-of-screen-line editor - line-start:&:duplex-list:char <- next before-line-start - commented-out?:bool <- match line-start, [#? ] # comment prefix - { - break-unless commented-out? - # uncomment - data <- remove line-start, 3/length-comment-prefix, data - cursor-column <- subtract cursor-column, 3/length-comment-prefix - *editor <- put *editor, cursor-column:offset, cursor-column - go-render? <- render-line-from-start screen, editor, 3/size-of-comment-leader - } - { - break-if commented-out? - # comment - insert before-line-start, [#? ] - cursor-column <- add cursor-column, 3/length-comment-prefix - *editor <- put *editor, cursor-column:offset, cursor-column - go-render? <- render-line-from-start screen, editor, 0 - } - - return - } -] - -# Render just from the start of the current line, and only if it wasn't -# wrapping before (include margin) and isn't wrapping now. Otherwise just tell -# the caller to go-render? the entire screen. -def render-line-from-start screen:&:screen, editor:&:editor, right-margin:num -> go-render?:bool, screen:&:screen [ - local-scope - load-inputs - before-line-start:&:duplex-list:char <- before-start-of-screen-line editor - line-start:&:duplex-list:char <- next before-line-start - color:num <- copy 7/white - left:num <- get *editor, left:offset - cursor-row:num <- get *editor, cursor-row:offset - screen <- move-cursor screen, cursor-row, left - right:num <- get *editor, right:offset - end:num <- subtract right, right-margin - i:num <- copy 0 - curr:&:duplex-list:char <- copy line-start - { - render-all?:bool <- greater-or-equal i, end - return-if render-all?, true/go-render - break-unless curr - c:char <- get *curr, value:offset - newline?:bool <- equal c, 10/newline - break-if newline? - color <- get-color color, c - print screen, c, color - curr <- next curr - i <- add i, 1 - loop - } - clear-line-until screen, right - return false/dont-render -] - -def before-start-of-screen-line editor:&:editor -> result:&:duplex-list:char [ - local-scope - load-inputs - cursor:&:duplex-list:char <- get *editor, before-cursor:offset - { - next:&:duplex-list:char <- next cursor - break-unless next - cursor <- copy next - } - result <- before-previous-screen-line cursor, editor -] - -scenario editor-comments-empty-line [ - local-scope - assume-screen 10/width, 5/height - e:&:editor <- new-editor [], 0/left, 5/right - editor-render screen, e - $clear-trace - assume-console [ - press ctrl-slash - ] - run [ - editor-event-loop screen, console, e - 4:num/raw <- get *e, cursor-row:offset - 5:num/raw <- get *e, cursor-column:offset - ] - screen-should-contain [ - . . - .#? . - .┈┈┈┈┈ . - . . - ] - memory-should-contain [ - 4 <- 1 - 5 <- 3 - ] - check-trace-count-for-label 5, [print-character] -] - -scenario editor-comments-at-start-of-contents [ - local-scope - assume-screen 10/width, 5/height - e:&:editor <- new-editor [ab], 0/left, 10/right - editor-render screen, e - $clear-trace - assume-console [ - press ctrl-slash - ] - run [ - editor-event-loop screen, console, e - 4:num/raw <- get *e, cursor-row:offset - 5:num/raw <- get *e, cursor-column:offset - ] - screen-should-contain [ - . . - .#? ab . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - memory-should-contain [ - 4 <- 1 - 5 <- 3 - ] - check-trace-count-for-label 10, [print-character] -] - -scenario editor-comments-at-end-of-contents [ - local-scope - assume-screen 10/width, 5/height - e:&:editor <- new-editor [ab], 0/left, 10/right - editor-render screen, e - $clear-trace - assume-console [ - left-click 1, 7 - press ctrl-slash - ] - run [ - editor-event-loop screen, console, e - 4:num/raw <- get *e, cursor-row:offset - 5:num/raw <- get *e, cursor-column:offset - ] - screen-should-contain [ - . . - .#? ab . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - memory-should-contain [ - 4 <- 1 - 5 <- 5 - ] - check-trace-count-for-label 10, [print-character] - # toggle to uncomment - $clear-trace - assume-console [ - press ctrl-slash - ] - run [ - editor-event-loop screen, console, e - 4:num/raw <- get *e, cursor-row:offset - 5:num/raw <- get *e, cursor-column:offset - ] - screen-should-contain [ - . . - .ab . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - check-trace-count-for-label 10, [print-character] -] - -scenario editor-comments-almost-wrapping-line [ - local-scope - assume-screen 10/width, 5/height - # editor starts out with a non-wrapping line - e:&:editor <- new-editor [abcd], 0/left, 5/right - editor-render screen, e - screen-should-contain [ - . . - .abcd . - .┈┈┈┈┈ . - . . - ] - $clear-trace - # on commenting the line is now wrapped - assume-console [ - left-click 1, 7 - press ctrl-slash - ] - run [ - editor-event-loop screen, console, e - ] - screen-should-contain [ - . . - .#? a↩ . - .bcd . - .┈┈┈┈┈ . - . . - ] -] - -scenario editor-uncomments-just-wrapping-line [ - local-scope - assume-screen 10/width, 5/height - # editor starts out with a comment that wraps the line - e:&:editor <- new-editor [#? ab], 0/left, 5/right - editor-render screen, e - screen-should-contain [ - . . - .#? a↩ . - .b . - .┈┈┈┈┈ . - . . - ] - $clear-trace - # on uncommenting the line is no longer wrapped - assume-console [ - left-click 1, 7 - press ctrl-slash - ] - run [ - editor-event-loop screen, console, e - ] - screen-should-contain [ - . . - .ab . - .┈┈┈┈┈ . - . . - ] -] diff --git a/archive/2.vm/sandbox/004-programming-environment.mu b/archive/2.vm/sandbox/004-programming-environment.mu deleted file mode 100644 index 1454144b..00000000 --- a/archive/2.vm/sandbox/004-programming-environment.mu +++ /dev/null @@ -1,268 +0,0 @@ -## putting the environment together out of editors - -def! main [ - local-scope - open-console - clear-screen null/screen # non-scrolling app - env:&:environment <- new-programming-environment null/filesystem, null/screen - render-all null/screen, env, render - event-loop null/screen, null/console, env, null/filesystem -] - -container environment [ - current-sandbox:&:editor -] - -def new-programming-environment resources:&:resources, screen:&:screen, test-sandbox-editor-contents:text -> result:&:environment [ - local-scope - load-inputs - width:num <- screen-width screen - result <- new environment:type - # sandbox editor - current-sandbox:&:editor <- new-editor test-sandbox-editor-contents, 0/left, width/right - *result <- put *result, current-sandbox:offset, current-sandbox - -] - -def event-loop screen:&:screen, console:&:console, env:&:environment, resources:&:resources -> screen:&:screen, console:&:console, env:&:environment, resources:&:resources [ - local-scope - load-inputs - current-sandbox:&:editor <- 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. - # todo: test this - render-all-on-no-more-events?:bool <- copy false - { - # looping over each (keyboard or touch) event as it occurs - +next-event - e:event, found?:bool, quit?:bool, console <- read-event console - loop-unless found? - break-if quit? # only in tests - trace 10, [app], [next-event] - - # check for global events that will trigger regardless of which editor has focus - { - k:num, is-keycode?:bool <- maybe-convert e:event, keycode:variant - break-unless is-keycode? - - } - { - c:char, is-unicode?:bool <- maybe-convert e:event, text:variant - break-unless is-unicode? - - } - # 'touch' event - { - t:touch-event, is-touch?:bool <- maybe-convert e:event, touch:variant - break-unless is-touch? - # ignore all but 'left-click' events for now - # todo: test this - touch-type:num <- get t, type:offset - is-left-click?:bool <- equal touch-type, 65513/mouse-left - loop-unless is-left-click?, +next-event - click-row:num <- get t, row:offset - click-column:num <- get t, column:offset - # later exceptions for non-editor touches will go here - - move-cursor current-sandbox, screen, t - screen <- update-cursor screen, current-sandbox, env - loop +next-event - } - # 'resize' event - redraw editor - # todo: test this after supporting resize in assume-console - { - r:resize-event, is-resize?:bool <- maybe-convert e:event, resize:variant - break-unless is-resize? - env, screen <- resize screen, env - screen <- render-all screen, env, render-without-moving-cursor - loop +next-event - } - # not global and not a touch event - { - render?:bool <- handle-keyboard-event screen, current-sandbox, e:event - # try to batch up rendering if there are more events queued up - render-all-on-no-more-events? <- or render-all-on-no-more-events?, render? - more-events?:bool <- has-more-events? console - { - break-if more-events? - break-unless render-all-on-no-more-events? - render-all-on-no-more-events? <- copy false - screen <- render-all screen, env, render - } - screen <- update-cursor screen, current-sandbox, env - } - loop - } -] - -def resize screen:&:screen, env:&:environment -> env:&:environment, screen:&:screen [ - local-scope - load-inputs - clear-screen screen # update screen dimensions - width:num <- screen-width screen - # update sandbox editor - current-sandbox:&:editor <- get *env, current-sandbox:offset - right:num <- subtract width, 1 - *current-sandbox <- put *current-sandbox right:offset, right - # reset cursor - *current-sandbox <- put *current-sandbox, cursor-row:offset, 1 - *current-sandbox <- put *current-sandbox, cursor-column:offset, 0 -] - -# Variant of 'render' that updates cursor-row and cursor-column based on -# before-cursor (rather than the other way around). If before-cursor moves -# off-screen, it resets cursor-row and cursor-column. -def render-without-moving-cursor screen:&:screen, editor:&:editor -> last-row:num, last-column:num, screen:&:screen, editor:&:editor [ - local-scope - load-inputs - return-unless editor, 1/top, 0/left - left:num <- get *editor, left:offset - screen-height:num <- screen-height screen - right:num <- get *editor, right:offset - curr:&:duplex-list:char <- get *editor, top-of-screen:offset - prev:&:duplex-list:char <- copy curr # just in case curr becomes null and we can't compute prev - curr <- next curr - color:num <- copy 7/white - row:num <- copy 1/top - column:num <- copy left - # save before-cursor - old-before-cursor:&:duplex-list:char <- get *editor, before-cursor:offset - # initialze cursor-row/cursor-column/before-cursor to the top of the screen - # by default - *editor <- put *editor, cursor-row:offset, row - *editor <- put *editor, cursor-column:offset, column - top-of-screen:&:duplex-list:char <- get *editor, top-of-screen:offset - *editor <- put *editor, before-cursor:offset, top-of-screen - screen <- move-cursor screen, row, column - { - +next-character - break-unless curr - off-screen?:bool <- greater-or-equal row, screen-height - break-if off-screen? - # if we find old-before-cursor still on the new resized screen, update - # editor.cursor-row and editor.cursor-column based on - # old-before-cursor - { - at-cursor?:bool <- equal old-before-cursor, prev - break-unless at-cursor? - *editor <- put *editor, cursor-row:offset, row - *editor <- put *editor, cursor-column:offset, column - *editor <- put *editor, before-cursor:offset, old-before-cursor - } - c:char <- get *curr, value:offset - - { - # newline? move to left rather than 0 - newline?:bool <- equal c, 10/newline - break-unless newline? - # clear rest of line in this window - clear-line-until screen, right - # skip to next line - row <- add row, 1 - column <- copy left - screen <- move-cursor screen, row, column - curr <- next curr - prev <- next prev - loop +next-character - } - { - # at right? wrap. even if there's only one more letter left; we need - # room for clicking on the cursor after it. - at-right?:bool <- equal column, right - break-unless at-right? - # print wrap icon - wrap-icon:char <- copy 8617/loop-back-to-left - print screen, wrap-icon, 245/grey - column <- copy left - row <- add row, 1 - screen <- move-cursor screen, row, column - # don't increment curr - loop +next-character - } - print screen, c, color - curr <- next curr - prev <- next prev - column <- add column, 1 - loop - } - # save first character off-screen - *editor <- put *editor, bottom-of-screen:offset, curr - *editor <- put *editor, bottom:offset, row - return row, column -] - -type render-recipe = (recipe (address screen) (address editor) -> number number (address screen) (address editor)) - -def render-all screen:&:screen, env:&:environment, render-editor:render-recipe -> screen:&:screen, env:&:environment [ - local-scope - load-inputs - trace 10, [app], [render all] - # top menu - trace 11, [app], [render top menu] - width:num <- screen-width screen - draw-horizontal screen, 0, 0/left, width, 32/space, 0/black, 238/grey - button-start:num <- subtract width, 20 - button-on-screen?:bool <- greater-or-equal button-start, 0 - assert button-on-screen?, [screen too narrow for menu] - screen <- move-cursor screen, 0/row, button-start - print screen, [ run (F4) ], 255/white, 161/reddish - # - screen <- render-sandbox-side screen, env, render-editor - # no early returns permitted - # - current-sandbox:&:editor <- get *env, current-sandbox:offset - screen <- update-cursor screen, current-sandbox, env -] - -# replaced in a later layer -def render-sandbox-side screen:&:screen, env:&:environment, render-editor:render-recipe -> screen:&:screen, env:&:environment [ - local-scope - load-inputs - trace 11, [app], [render sandboxes] - old-top-idx:num <- save-top-idx screen - current-sandbox:&:editor <- get *env, current-sandbox:offset - left:num <- get *current-sandbox, left:offset - right:num <- get *current-sandbox, right:offset - row:num, column:num, screen, current-sandbox <- call render-editor, screen, current-sandbox - # draw solid line after code (you'll see why in later layers) - draw-horizontal screen, row, left, right - row <- add row, 1 - clear-screen-from screen, row, left, left, right - # - assert-no-scroll screen, old-top-idx -] - -def update-cursor screen:&:screen, current-sandbox:&:editor, env:&:environment -> screen:&:screen [ - local-scope - load-inputs - - cursor-row:num <- get *current-sandbox, cursor-row:offset - cursor-column:num <- get *current-sandbox, cursor-column:offset - screen <- move-cursor screen, cursor-row, cursor-column -] - -scenario backspace-over-text [ - local-scope - trace-until 100/app # trace too long - assume-screen 50/width, 15/height - # recipes.mu is empty - assume-resources [ - ] - # sandbox editor contains an instruction without storing outputs - env:&:environment <- new-programming-environment resources, screen, [] - render-all screen, env, render - # run the code in the editors - assume-console [ - type [a] - press backspace - ] - run [ - event-loop screen, console, env, resources - 10:num/raw <- get *screen, cursor-row:offset - 11:num/raw <- get *screen, cursor-column:offset - ] - memory-should-contain [ - 10 <- 1 - 11 <- 0 - ] -] diff --git a/archive/2.vm/sandbox/005-sandbox.mu b/archive/2.vm/sandbox/005-sandbox.mu deleted file mode 100644 index 632a5df1..00000000 --- a/archive/2.vm/sandbox/005-sandbox.mu +++ /dev/null @@ -1,1081 +0,0 @@ -## running code from the editor and creating sandboxes -# -# Running code in the sandbox editor prepends its contents to a list of -# (non-editable) sandboxes below the editor, showing the result and maybe a -# few other things (later layers). -# -# This layer draws the menubar buttons in non-editable sandboxes but they -# don't do anything yet. Later layers implement each button. - -def! main [ - local-scope - open-console - clear-screen null/screen # non-scrolling app - env:&:environment <- new-programming-environment null/filesystem, null/screen - env <- restore-sandboxes env, null/filesystem - render-all null/screen, env, render - event-loop null/screen, null/console, env, null/filesystem -] - -container environment [ - sandbox:&:sandbox # list of sandboxes, from top to bottom. TODO: switch to &:list:sandbox - render-from:num - number-of-sandboxes:num -] - -after [ - *result <- put *result, render-from:offset, -1 -] - -container sandbox [ - data:text - response:text - # coordinates to track clicks - # constraint: will be 0 for sandboxes at positions before env.render-from - starting-row-on-screen:num - code-ending-row-on-screen:num # past end of code - screen:&:screen # prints in the sandbox go here - next-sandbox:&:sandbox -] - -scenario run-and-show-results [ - local-scope - trace-until 100/app # trace too long - assume-screen 50/width, 15/height - # recipes.mu is empty - assume-resources [ - ] - # sandbox editor contains an instruction without storing outputs - env:&:environment <- new-programming-environment resources, screen, [divide-with-remainder 11, 3] - render-all screen, env, render - # run the code in the editors - assume-console [ - press F4 - ] - run [ - event-loop screen, console, env, resources - ] - # check that screen prints the results - screen-should-contain [ - . run (F4) . - . . - .──────────────────────────────────────────────────. - .0 edit copy delete . - .divide-with-remainder 11, 3 . - .3 . - .2 . - .──────────────────────────────────────────────────. - . . - ] - screen-should-contain-in-color 7/white, [ - . . - . . - . . - . . - .divide-with-remainder 11, 3 . - . . - . . - . . - . . - ] - screen-should-contain-in-color 245/grey, [ - . . - . . - .──────────────────────────────────────────────────. - . . - . . - .3 . - .2 . - .──────────────────────────────────────────────────. - . . - ] - # run another command - assume-console [ - left-click 1, 80 - type [add 2, 2] - press F4 - ] - run [ - event-loop screen, console, env, resources - ] - # check that screen prints both sandboxes - screen-should-contain [ - . run (F4) . - . . - .──────────────────────────────────────────────────. - .0 edit copy delete . - .add 2, 2 . - .4 . - .──────────────────────────────────────────────────. - .1 edit copy delete . - .divide-with-remainder 11, 3 . - .3 . - .2 . - .──────────────────────────────────────────────────. - . . - ] -] - -after [ - # F4? load all code and run all sandboxes. - { - do-run?:bool <- equal k, 65532/F4 - break-unless do-run? - screen <- update-status screen, [running... ], 245/grey - error?:bool <- run-sandboxes env, resources, screen - # F4 might update warnings and results on both sides - screen <- render-all screen, env, render - { - break-if error? - screen <- update-status screen, [ ], 245/grey - } - screen <- update-cursor screen, current-sandbox, env - loop +next-event - } -] - -def run-sandboxes env:&:environment, resources:&:resources, screen:&:screen -> errors-found?:bool, env:&:environment, resources:&:resources, screen:&:screen [ - local-scope - load-inputs - errors-found?:bool <- update-recipes env, resources, screen - # check contents of editor - - current-sandbox:&:editor <- get *env, current-sandbox:offset - { - sandbox-contents:text <- editor-contents current-sandbox - break-unless sandbox-contents - # if contents exist, first save them - # run them and turn them into a new sandbox - new-sandbox:&:sandbox <- new sandbox:type - *new-sandbox <- put *new-sandbox, data:offset, sandbox-contents - # push to head of sandbox list - dest:&:sandbox <- get *env, sandbox:offset - *new-sandbox <- put *new-sandbox, next-sandbox:offset, dest - *env <- put *env, sandbox:offset, new-sandbox - # update sandbox count - sandbox-count:num <- get *env, number-of-sandboxes:offset - sandbox-count <- add sandbox-count, 1 - *env <- put *env, number-of-sandboxes:offset, sandbox-count - # save all sandboxes - # needs to be before running them, in case we die when running - save-sandboxes env, resources - # clear sandbox editor - init:&:duplex-list:char <- push 167/§, null - *current-sandbox <- put *current-sandbox, data:offset, init - *current-sandbox <- put *current-sandbox, top-of-screen:offset, init - } - # run all sandboxes - curr:&:sandbox <- get *env, sandbox:offset - idx:num <- copy 0 - { - break-unless curr - curr <- update-sandbox curr, env, idx - curr <- get *curr, next-sandbox:offset - idx <- add idx, 1 - loop - } - - { - break-if resources # ignore this in tests - $system [./snapshot_lesson] - } -] - -# load code from disk -# replaced in a later layer (whereupon errors-found? will actually be set) -def update-recipes env:&:environment, resources:&:resources, screen:&:screen -> errors-found?:bool, env:&:environment, screen:&:screen [ - local-scope - load-inputs - in:text <- slurp resources, [lesson/recipes.mu] - reload in - errors-found? <- copy false -] - -# replaced in a later layer -def update-sandbox sandbox:&:sandbox, env:&:environment, idx:num -> sandbox:&:sandbox, env:&:environment [ - local-scope - load-inputs - data:text <- get *sandbox, data:offset - response:text, _, fake-screen:&:screen <- run-sandboxed data - *sandbox <- put *sandbox, response:offset, response - *sandbox <- put *sandbox, screen:offset, fake-screen -] - -def update-status screen:&:screen, msg:text, color:num -> screen:&:screen [ - local-scope - load-inputs - screen <- move-cursor screen, 0, 2 - screen <- print screen, msg, color, 238/grey/background -] - -def save-sandboxes env:&:environment, resources:&:resources -> resources:&:resources [ - local-scope - load-inputs - trace 11, [app], [save sandboxes] - current-sandbox:&:editor <- 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 '>&' - curr:&:sandbox <- get *env, sandbox:offset - idx:num <- copy 0 - { - break-unless curr - resources <- save-sandbox resources, curr, idx - idx <- add idx, 1 - curr <- get *curr, next-sandbox:offset - loop - } -] - -def save-sandbox resources:&:resources, sandbox:&:sandbox, sandbox-index:num -> resources:&:resources [ - local-scope - load-inputs - data:text <- get *sandbox, data:offset - filename:text <- append [lesson/], sandbox-index - resources <- dump resources, filename, data - -] - -def! render-sandbox-side screen:&:screen, env:&:environment, render-editor:render-recipe -> screen:&:screen, env:&:environment [ - local-scope - load-inputs - trace 11, [app], [render sandbox side] - old-top-idx:num <- save-top-idx screen - current-sandbox:&:editor <- get *env, current-sandbox:offset - row:num, column:num <- copy 1, 0 - left:num <- get *current-sandbox, left:offset - right:num <- get *current-sandbox, right:offset - # render sandbox editor - render-from:num <- get *env, render-from:offset - { - render-current-sandbox?:bool <- equal render-from, -1 - break-unless render-current-sandbox? - row, column, screen, current-sandbox <- call render-editor, screen, current-sandbox - } - # render sandboxes - draw-horizontal screen, row, left, right - sandbox:&:sandbox <- get *env, sandbox:offset - row, screen <- render-sandboxes screen, sandbox, left, right, row, render-from, 0, env - clear-rest-of-screen screen, row, left, right - # - assert-no-scroll screen, old-top-idx -] - -def render-sandboxes screen:&:screen, sandbox:&:sandbox, left:num, right:num, row:num, render-from:num, idx:num -> row:num, screen:&:screen, sandbox:&:sandbox [ - local-scope - load-inputs - env:&:environment, _/optional <- next-input - return-unless sandbox - screen-height:num <- screen-height screen - hidden?:bool <- lesser-than idx, render-from - { - break-if hidden? - # render sandbox menu - row <- add row, 1 - at-bottom?:bool <- greater-or-equal row, screen-height - return-if at-bottom? - screen <- move-cursor screen, row, left - screen <- render-sandbox-menu screen, idx, left, right - # save menu row so we can detect clicks to it later - *sandbox <- put *sandbox, starting-row-on-screen:offset, row - # render sandbox contents - row <- add row, 1 - screen <- move-cursor screen, row, left - sandbox-data:text <- get *sandbox, data:offset - row, screen <- render-code screen, sandbox-data, left, right, row - *sandbox <- put *sandbox, code-ending-row-on-screen:offset, row - # render sandbox warnings, screen or response, in that order - sandbox-response:text <- get *sandbox, response:offset - - { - sandbox-screen:&:screen <- get *sandbox, screen:offset - empty-screen?:bool <- fake-screen-is-empty? sandbox-screen - break-if empty-screen? - row, screen <- render-screen screen, sandbox-screen, left, right, row - } - { - break-unless empty-screen? - - row, screen <- render-text screen, sandbox-response, left, right, 245/grey, row - } - +render-sandbox-end - at-bottom?:bool <- greater-or-equal row, screen-height - return-if at-bottom? - # draw solid line after sandbox - draw-horizontal screen, row, left, right - } - # if hidden, reset row attributes - { - break-unless hidden? - *sandbox <- put *sandbox, starting-row-on-screen:offset, 0 - *sandbox <- put *sandbox, code-ending-row-on-screen:offset, 0 - - } - # draw next sandbox - next-sandbox:&:sandbox <- get *sandbox, next-sandbox:offset - next-idx:num <- add idx, 1 - row, screen <- render-sandboxes screen, next-sandbox, left, right, row, render-from, next-idx, env -] - -def render-sandbox-menu screen:&:screen, sandbox-index:num, left:num, right:num -> screen:&:screen [ - local-scope - load-inputs - move-cursor-to-column screen, left - edit-button-left:num, edit-button-right:num, copy-button-left:num, copy-button-right:num, delete-button-left:num <- sandbox-menu-columns left, right - print screen, sandbox-index, 232/dark-grey, 245/grey - start-buttons:num <- subtract edit-button-left, 1 - clear-line-until screen, start-buttons, 245/grey - print screen, [edit], 232/black, 25/background-blue - clear-line-until screen, edit-button-right, 25/background-blue - print screen, [copy], 232/black, 58/background-green - clear-line-until screen, copy-button-right, 58/background-green - print screen, [delete], 232/black, 52/background-red - clear-line-until screen, right, 52/background-red -] - -scenario skip-rendering-sandbox-menu-past-bottom-row [ - trace-until 100/app # trace too long - assume-screen 50/width, 6/height - # recipes.mu is empty - assume-resources [ - [lesson/0] <- [|add 2, 2|] - [lesson/1] <- [|add 1, 1|] - ] - # create two sandboxes such that the top one just barely fills the screen - env:&:environment <- new-programming-environment resources, screen, [] - env <- restore-sandboxes env, resources - run [ - render-all screen, env, render - ] - screen-should-contain [ - . run (F4) . - . . - .──────────────────────────────────────────────────. - .0 edit copy delete . - .add 2, 2 . - .──────────────────────────────────────────────────. - ] -] - -# divide up the menu bar for a sandbox into 3 segments, for edit/copy/delete buttons -# delete-button-right == right -# all left/right pairs are inclusive -def sandbox-menu-columns left:num, right:num -> edit-button-left:num, edit-button-right:num, copy-button-left:num, copy-button-right:num, delete-button-left:num [ - local-scope - load-inputs - start-buttons:num <- add left, 4/space-for-sandbox-index - buttons-space:num <- subtract right, start-buttons - button-width:num <- divide-with-remainder buttons-space, 3 # integer division - buttons-wide-enough?:bool <- greater-or-equal button-width, 8 - assert buttons-wide-enough?, [sandbox must be at least 30 or so characters wide] - edit-button-left:num <- copy start-buttons - copy-button-left:num <- add start-buttons, button-width - edit-button-right:num <- subtract copy-button-left, 1 - delete-button-left:num <- subtract right, button-width - copy-button-right:num <- subtract delete-button-left, 1 -] - -# print a text 's' to 'editor' in 'color' starting at 'row' -# clear rest of last line, move cursor to next line -def render-text screen:&:screen, s:text, left:num, right:num, color:num, row:num -> row:num, screen:&:screen [ - local-scope - load-inputs - return-unless s - column:num <- copy left - screen <- move-cursor screen, row, column - screen-height:num <- screen-height screen - i:num <- copy 0 - len:num <- length *s - { - +next-character - done?:bool <- greater-or-equal i, len - break-if done? - done? <- greater-or-equal row, screen-height - break-if done? - c:char <- index *s, i - { - # newline? move to left rather than 0 - newline?:bool <- equal c, 10/newline - break-unless newline? - # clear rest of line in this window - { - done?:bool <- greater-than column, right - break-if done? - space:char <- copy 32/space - print screen, space - column <- add column, 1 - loop - } - row <- add row, 1 - column <- copy left - screen <- move-cursor screen, row, column - i <- add i, 1 - loop +next-character - } - { - # at right? wrap. - at-right?:bool <- equal column, right - break-unless at-right? - # print wrap icon - wrap-icon:char <- copy 8617/loop-back-to-left - print screen, wrap-icon, 245/grey - column <- copy left - row <- add row, 1 - screen <- move-cursor screen, row, column - # don't increment i - loop +next-character - } - i <- add i, 1 - print screen, c, color - column <- add column, 1 - loop - } - was-at-left?:bool <- equal column, left - clear-line-until screen, right - { - break-if was-at-left? - row <- add row, 1 - } - move-cursor screen, row, left -] - -scenario render-text-wraps-barely-long-lines [ - local-scope - assume-screen 5/width, 5/height - run [ - render-text screen, [abcde], 0/left, 4/right, 7/white, 1/row - ] - screen-should-contain [ - . . - .abcd↩. - .e . - . . - ] -] - -# assumes programming environment has no sandboxes; restores them from previous session -def restore-sandboxes env:&:environment, resources:&:resources -> env:&:environment [ - local-scope - load-inputs - # read all scenarios, pushing them to end of a list of scenarios - idx:num <- copy 0 - curr:&:sandbox <- copy null - prev:&:sandbox <- copy null - { - filename:text <- append [lesson/], idx - contents:text <- slurp resources, filename - break-unless contents # stop at first error; assuming file didn't exist - # todo: handle empty sandbox - # create new sandbox for file - curr <- new sandbox:type - *curr <- put *curr, data:offset, contents - - { - break-if idx - *env <- put *env, sandbox:offset, curr - } - { - break-unless idx - *prev <- put *prev, next-sandbox:offset, curr - } - idx <- add idx, 1 - prev <- copy curr - loop - } - # update sandbox count - *env <- put *env, number-of-sandboxes:offset, idx -] - -# print the fake sandbox screen to 'screen' with appropriate delimiters -# leave cursor at start of next line -def render-screen screen:&:screen, sandbox-screen:&:screen, left:num, right:num, row:num -> row:num, screen:&:screen [ - local-scope - load-inputs - return-unless sandbox-screen - # print 'screen:' - row <- render-text screen, [screen:], left, right, 245/grey, row - screen <- move-cursor screen, row, left - # start printing sandbox-screen - column:num <- copy left - s-width:num <- screen-width sandbox-screen - s-height:num <- screen-height sandbox-screen - buf:&:@:screen-cell <- get *sandbox-screen, data:offset - stop-printing:num <- add left, s-width, 3 - max-column:num <- min stop-printing, right - i:num <- copy 0 - len:num <- length *buf - screen-height:num <- screen-height screen - { - done?:bool <- greater-or-equal i, len - break-if done? - done? <- greater-or-equal row, screen-height - break-if done? - column <- copy left - screen <- move-cursor screen, row, column - # initial leader for each row: two spaces and a '.' - space:char <- copy 32/space - print screen, space, 245/grey - print screen, space, 245/grey - full-stop:char <- copy 46/period - print screen, full-stop, 245/grey - column <- add left, 3 - { - # print row - row-done?:bool <- greater-or-equal column, max-column - break-if row-done? - curr:screen-cell <- index *buf, i - c:char <- get curr, contents:offset - color:num <- get curr, color:offset - { - # damp whites down to grey - white?:bool <- equal color, 7/white - break-unless white? - color <- copy 245/grey - } - print screen, c, color - column <- add column, 1 - i <- add i, 1 - loop - } - # print final '.' - print screen, full-stop, 245/grey - column <- add column, 1 - { - # clear rest of current line - line-done?:bool <- greater-than column, right - break-if line-done? - print screen, space - column <- add column, 1 - loop - } - row <- add row, 1 - loop - } -] - -scenario run-updates-results [ - local-scope - trace-until 100/app # trace too long - assume-screen 50/width, 12/height - # define a recipe (no indent for the 'add' line below so column numbers are more obvious) - assume-resources [ - [lesson/recipes.mu] <- [ - || - |recipe foo [| - | local-scope| - | z:num <- add 2, 2| - | reply z| - |]| - ] - ] - # sandbox editor contains an instruction without storing outputs - env:&:environment <- new-programming-environment resources, screen, [foo] # contents of sandbox editor - render-all screen, env, render - $clear-trace - # run the code in the editors - assume-console [ - press F4 - ] - event-loop screen, console, env, resources - screen-should-contain [ - . run (F4) . - . . - .──────────────────────────────────────────────────. - .0 edit copy delete . - .foo . - .4 . - .──────────────────────────────────────────────────. - . . - ] - # the new sandbox should be saved to disk - trace-should-contain [ - app: save sandboxes - ] - # make a change (incrementing one of the args to 'add'), then rerun - assume-resources [ - [lesson/recipes.mu] <- [ - || - |recipe foo [| - | local-scope| - | z:num <- add 2, 3| - | reply z| - |]| - ] - ] - $clear-trace - assume-console [ - press F4 - ] - run [ - event-loop screen, console, env, resources - ] - # check that screen updates the result on the right - screen-should-contain [ - . run (F4) . - . . - .──────────────────────────────────────────────────. - .0 edit copy delete . - .foo . - .5 . - .──────────────────────────────────────────────────. - . . - ] - # no need to save sandboxes all over again - trace-should-not-contain [ - app: save sandboxes - ] -] - -scenario run-instruction-manages-screen-per-sandbox [ - local-scope - trace-until 100/app # trace too long - assume-screen 50/width, 20/height - # empty recipes - assume-resources [ - ] - # sandbox editor contains an instruction - env:&:environment <- new-programming-environment resources, screen, [print screen, 4] # contents of sandbox editor - render-all screen, env, render - # run the code in the editor - assume-console [ - press F4 - ] - run [ - event-loop screen, console, env, resources - ] - # check that it prints a little toy screen - screen-should-contain [ - . run (F4) . - . . - .──────────────────────────────────────────────────. - .0 edit copy delete . - .print screen, 4 . - .screen: . - . .4 . . - . . . . - . . . . - . . . . - . . . . - .──────────────────────────────────────────────────. - . . - ] -] - -def editor-contents editor:&:editor -> result:text [ - local-scope - load-inputs - buf:&:buffer:char <- new-buffer 80 - curr:&:duplex-list:char <- get *editor, data:offset - # skip § sentinel - assert curr, [editor without data is illegal; must have at least a sentinel] - curr <- next curr - return-unless curr, null - { - break-unless curr - c:char <- get *curr, value:offset - buf <- append buf, c - curr <- next curr - loop - } - result <- buffer-to-array buf -] - -scenario editor-provides-edited-contents [ - local-scope - assume-screen 10/width, 5/height - e:&:editor <- new-editor [abc], 0/left, 10/right - assume-console [ - left-click 1, 2 - type [def] - ] - run [ - editor-event-loop screen, console, e - s:text <- editor-contents e - 1:@:char/raw <- copy *s - ] - memory-should-contain [ - 1:array:character <- [abdefc] - ] -] - -# scrolling through sandboxes - -scenario scrolling-down-past-bottom-of-sandbox-editor [ - local-scope - trace-until 100/app # trace too long - assume-screen 50/width, 20/height - # initialize - assume-resources [ - ] - env:&:environment <- new-programming-environment resources, screen, [add 2, 2] - render-all screen, env, render - assume-console [ - # create a sandbox - press F4 - ] - event-loop screen, console, env, resources - screen-should-contain [ - . run (F4) . - . . - .──────────────────────────────────────────────────. - .0 edit copy delete . - .add 2, 2 . - .4 . - .──────────────────────────────────────────────────. - . . - ] - # hit 'page-down' - assume-console [ - press page-down - ] - run [ - event-loop screen, console, env, resources - cursor:char <- copy 9251/␣ - print screen, cursor - ] - # sandbox editor hidden; first sandbox displayed - # cursor moves to first sandbox - screen-should-contain [ - . run (F4) . - .──────────────────────────────────────────────────. - .␣ edit copy delete . - .add 2, 2 . - .4 . - .──────────────────────────────────────────────────. - . . - ] - # hit 'page-up' - assume-console [ - press page-up - ] - run [ - event-loop screen, console, env, resources - cursor:char <- copy 9251/␣ - print screen, cursor - ] - # sandbox editor displays again - screen-should-contain [ - . run (F4) . - .␣ . - .──────────────────────────────────────────────────. - .0 edit copy delete . - .add 2, 2 . - .4 . - .──────────────────────────────────────────────────. - . . - ] -] - -# page-down updates render-from to scroll sandboxes -after [ - { - page-down?:bool <- equal k, 65518/page-down - break-unless page-down? - sandbox:&:sandbox <- get *env, sandbox:offset - break-unless sandbox - # slide down if possible - { - render-from:num <- get *env, render-from:offset - number-of-sandboxes:num <- get *env, number-of-sandboxes:offset - max:num <- subtract number-of-sandboxes, 1 - at-end?:bool <- greater-or-equal render-from, max - break-if at-end? - render-from <- add render-from, 1 - *env <- put *env, render-from:offset, render-from - } - screen <- render-sandbox-side screen, env, render - screen <- update-cursor screen, current-sandbox, env - loop +next-event - } -] - -# update-cursor takes render-from into account -after [ - { - render-from:num <- get *env, render-from:offset - scrolling?:bool <- greater-or-equal render-from, 0 - break-unless scrolling? - cursor-column:num <- get *current-sandbox, left:offset - screen <- move-cursor screen, 2/row, cursor-column # highlighted sandbox will always start at row 2 - return - } -] - -# 'page-up' is like 'page-down': updates first-sandbox-to-render when necessary -after [ - { - page-up?:bool <- equal k, 65519/page-up - break-unless page-up? - render-from:num <- get *env, render-from:offset - at-beginning?:bool <- equal render-from, -1 - break-if at-beginning? - render-from <- subtract render-from, 1 - *env <- put *env, render-from:offset, render-from - screen <- render-sandbox-side screen, env, render - screen <- update-cursor screen, current-sandbox, env - loop +next-event - } -] - -# sandbox belonging to 'env' whose next-sandbox is 'in' -# return null if there's no such sandbox, either because 'in' doesn't exist in 'env', or because it's the first sandbox -def previous-sandbox env:&:environment, in:&:sandbox -> out:&:sandbox [ - local-scope - load-inputs - curr:&:sandbox <- get *env, sandbox:offset - return-unless curr, null - next:&:sandbox <- get *curr, next-sandbox:offset - { - return-unless next, null - found?:bool <- equal next, in - break-if found? - curr <- copy next - next <- get *curr, next-sandbox:offset - loop - } - return curr -] - -scenario scrolling-through-multiple-sandboxes [ - local-scope - trace-until 100/app # trace too long - assume-screen 50/width, 20/height - # initialize environment - assume-resources [ - ] - env:&:environment <- new-programming-environment resources, screen, [] - render-all screen, env, render - # create 2 sandboxes - assume-console [ - press ctrl-n - type [add 2, 2] - press F4 - type [add 1, 1] - press F4 - ] - event-loop screen, console, env, resources - cursor:char <- copy 9251/␣ - print screen, cursor - screen-should-contain [ - . run (F4) . - .␣ . - .──────────────────────────────────────────────────. - .0 edit copy delete . - .add 1, 1 . - .2 . - .──────────────────────────────────────────────────. - .1 edit copy delete . - .add 2, 2 . - .4 . - .──────────────────────────────────────────────────. - . . - ] - # hit 'page-down' - assume-console [ - press page-down - ] - run [ - event-loop screen, console, env, resources - cursor:char <- copy 9251/␣ - print screen, cursor - ] - # sandbox editor hidden; first sandbox displayed - # cursor moves to first sandbox - screen-should-contain [ - . run (F4) . - .──────────────────────────────────────────────────. - .␣ edit copy delete . - .add 1, 1 . - .2 . - .──────────────────────────────────────────────────. - .1 edit copy delete . - .add 2, 2 . - .4 . - .──────────────────────────────────────────────────. - . . - ] - # hit 'page-down' again - assume-console [ - press page-down - ] - run [ - event-loop screen, console, env, resources - ] - # just second sandbox displayed - screen-should-contain [ - . run (F4) . - .──────────────────────────────────────────────────. - .1 edit copy delete . - .add 2, 2 . - .4 . - .──────────────────────────────────────────────────. - . . - ] - # hit 'page-down' again - assume-console [ - press page-down - ] - run [ - event-loop screen, console, env, resources - ] - # no change - screen-should-contain [ - . run (F4) . - .──────────────────────────────────────────────────. - .1 edit copy delete . - .add 2, 2 . - .4 . - .──────────────────────────────────────────────────. - . . - ] - # hit 'page-up' - assume-console [ - press page-up - ] - run [ - event-loop screen, console, env, resources - ] - # back to displaying both sandboxes without editor - screen-should-contain [ - . run (F4) . - .──────────────────────────────────────────────────. - .0 edit copy delete . - .add 1, 1 . - .2 . - .──────────────────────────────────────────────────. - .1 edit copy delete . - .add 2, 2 . - .4 . - .──────────────────────────────────────────────────. - . . - ] - # hit 'page-up' again - assume-console [ - press page-up - ] - run [ - event-loop screen, console, env, resources - cursor:char <- copy 9251/␣ - print screen, cursor - ] - # back to displaying both sandboxes as well as editor - screen-should-contain [ - . run (F4) . - .␣ . - .──────────────────────────────────────────────────. - .0 edit copy delete . - .add 1, 1 . - .2 . - .──────────────────────────────────────────────────. - .1 edit copy delete . - .add 2, 2 . - .4 . - .──────────────────────────────────────────────────. - . . - ] - # hit 'page-up' again - assume-console [ - press page-up - ] - run [ - event-loop screen, console, env, resources - cursor:char <- copy 9251/␣ - print screen, cursor - ] - # no change - screen-should-contain [ - . run (F4) . - .␣ . - .──────────────────────────────────────────────────. - .0 edit copy delete . - .add 1, 1 . - .2 . - .──────────────────────────────────────────────────. - .1 edit copy delete . - .add 2, 2 . - .4 . - .──────────────────────────────────────────────────. - . . - ] -] - -scenario scrolling-manages-sandbox-index-correctly [ - local-scope - trace-until 100/app # trace too long - assume-screen 50/width, 20/height - # initialize environment - assume-resources [ - ] - env:&:environment <- new-programming-environment resources, screen, [] - render-all screen, env, render - # create a sandbox - assume-console [ - press ctrl-n - type [add 1, 1] - press F4 - ] - event-loop screen, console, env, resources - screen-should-contain [ - . run (F4) . - . . - .──────────────────────────────────────────────────. - .0 edit copy delete . - .add 1, 1 . - .2 . - .──────────────────────────────────────────────────. - . . - ] - # hit 'page-down' and 'page-up' a couple of times. sandbox index should be stable - assume-console [ - press page-down - ] - run [ - event-loop screen, console, env, resources - ] - # sandbox editor hidden; first sandbox displayed - # cursor moves to first sandbox - screen-should-contain [ - . run (F4) . - .──────────────────────────────────────────────────. - .0 edit copy delete . - .add 1, 1 . - .2 . - .──────────────────────────────────────────────────. - . . - ] - # hit 'page-up' again - assume-console [ - press page-up - ] - run [ - event-loop screen, console, env, resources - ] - # back to displaying both sandboxes as well as editor - screen-should-contain [ - . run (F4) . - . . - .──────────────────────────────────────────────────. - .0 edit copy delete . - .add 1, 1 . - .2 . - .──────────────────────────────────────────────────. - . . - ] - # hit 'page-down' - assume-console [ - press page-down - ] - run [ - event-loop screen, console, env, resources - ] - # sandbox editor hidden; first sandbox displayed - # cursor moves to first sandbox - screen-should-contain [ - . run (F4) . - .──────────────────────────────────────────────────. - .0 edit copy delete . # no change - .add 1, 1 . - .2 . - .──────────────────────────────────────────────────. - . . - ] -] diff --git a/archive/2.vm/sandbox/006-sandbox-copy.mu b/archive/2.vm/sandbox/006-sandbox-copy.mu deleted file mode 100644 index 0eae6cf7..00000000 --- a/archive/2.vm/sandbox/006-sandbox-copy.mu +++ /dev/null @@ -1,286 +0,0 @@ -## the 'copy' button makes it easy to duplicate a sandbox, and thence to -## see code operate in multiple situations - -scenario copy-a-sandbox-to-editor [ - local-scope - trace-until 50/app # trace too long - assume-screen 50/width, 10/height - # empty recipes - assume-resources [ - ] - env:&:environment <- new-programming-environment resources, screen, [add 1, 1] # contents of sandbox editor - render-all screen, env, render - # run it - assume-console [ - press F4 - ] - event-loop screen, console, env, resources - screen-should-contain [ - . run (F4) . - . . - .──────────────────────────────────────────────────. - .0 edit copy delete . - .add 1, 1 . - .2 . - .──────────────────────────────────────────────────. - . . - . . - . . - ] - # click at left edge of 'copy' button - assume-console [ - left-click 3, 19 - ] - run [ - event-loop screen, console, env - ] - # it copies into editor - screen-should-contain [ - . run (F4) . - .add 1, 1 . - .──────────────────────────────────────────────────. - .0 edit copy delete . - .add 1, 1 . - .2 . - .──────────────────────────────────────────────────. - . . - . . - . . - ] - # cursor should be in the right place - assume-console [ - type [0] - ] - run [ - event-loop screen, console, env, resources - ] - screen-should-contain [ - . run (F4) . - .0add 1, 1 . - .──────────────────────────────────────────────────. - .0 edit copy delete . - .add 1, 1 . - .2 . - .──────────────────────────────────────────────────. - . . - . . - . . - ] -] - -scenario copy-a-sandbox-to-editor-2 [ - local-scope - trace-until 50/app # trace too long - assume-screen 50/width, 10/height - # empty recipes - assume-resources [ - ] - env:&:environment <- new-programming-environment resources, screen, [add 1, 1] # contents of sandbox editor - render-all screen, env, render - # run it - assume-console [ - press F4 - ] - event-loop screen, console, env, resources - screen-should-contain [ - . run (F4) . - . . - .──────────────────────────────────────────────────. - .0 edit copy delete . - .add 1, 1 . - .2 . - .──────────────────────────────────────────────────. - . . - . . - . . - ] - # click at right edge of 'copy' button (just before 'delete') - assume-console [ - left-click 3, 33 - ] - run [ - event-loop screen, console, env, resources - ] - # it copies into editor - screen-should-contain [ - . run (F4) . - .add 1, 1 . - .──────────────────────────────────────────────────. - .0 edit copy delete . - .add 1, 1 . - .2 . - .──────────────────────────────────────────────────. - . . - . . - . . - ] - # cursor should be in the right place - assume-console [ - type [0] - ] - run [ - event-loop screen, console, env, resources - ] - screen-should-contain [ - . run (F4) . - .0add 1, 1 . - .──────────────────────────────────────────────────. - .0 edit copy delete . - .add 1, 1 . - .2 . - .──────────────────────────────────────────────────. - . . - . . - . . - ] -] - -after [ - # support 'copy' button - { - copy?:bool <- should-attempt-copy? click-row, click-column, env - break-unless copy? - copy?, env <- try-copy-sandbox click-row, env - break-unless copy? - screen <- render-sandbox-side screen, env, render - screen <- update-cursor screen, current-sandbox, env - loop +next-event - } -] - -# some preconditions for attempting to copy a sandbox -def should-attempt-copy? click-row:num, click-column:num, env:&:environment -> result:bool [ - local-scope - load-inputs - # are we below the sandbox editor? - click-sandbox-area?:bool <- click-on-sandbox-area? click-row, env - return-unless click-sandbox-area?, false - # narrower, is the click in the columns spanning the 'copy' button? - first-sandbox:&:editor <- get *env, current-sandbox:offset - assert first-sandbox, [!!] - sandbox-left-margin:num <- get *first-sandbox, left:offset - sandbox-right-margin:num <- get *first-sandbox, right:offset - _, _, copy-button-left:num, copy-button-right:num, _ <- sandbox-menu-columns sandbox-left-margin, sandbox-right-margin - copy-button-vertical-area?:bool <- within-range? click-column, copy-button-left, copy-button-right - return-unless copy-button-vertical-area?, false - # finally, is sandbox editor empty? - current-sandbox:&:editor <- get *env, current-sandbox:offset - result <- empty-editor? current-sandbox -] - -def try-copy-sandbox click-row:num, env:&:environment -> clicked-on-copy-button?:bool, env:&:environment [ - local-scope - load-inputs - # identify the sandbox to copy, if the click was actually on the 'copy' button - sandbox:&:sandbox <- find-sandbox env, click-row - return-unless sandbox, false - clicked-on-copy-button? <- copy true - text:text <- get *sandbox, data:offset - current-sandbox:&:editor <- get *env, current-sandbox:offset - current-sandbox <- insert-text current-sandbox, text - # reset scroll - *env <- put *env, render-from:offset, -1 -] - -def find-sandbox env:&:environment, click-row:num -> result:&:sandbox [ - local-scope - load-inputs - curr-sandbox:&:sandbox <- get *env, sandbox:offset - { - break-unless curr-sandbox - start:num <- get *curr-sandbox, starting-row-on-screen:offset - found?:bool <- equal click-row, start - return-if found?, curr-sandbox - curr-sandbox <- get *curr-sandbox, next-sandbox:offset - loop - } - return null/not-found -] - -def click-on-sandbox-area? click-row:num, env:&:environment -> result:bool [ - local-scope - load-inputs - first-sandbox:&:sandbox <- get *env, sandbox:offset - return-unless first-sandbox, false - first-sandbox-begins:num <- get *first-sandbox, starting-row-on-screen:offset - result <- greater-or-equal click-row, first-sandbox-begins -] - -def empty-editor? editor:&:editor -> result:bool [ - local-scope - load-inputs - head:&:duplex-list:char <- get *editor, data:offset - first:&:duplex-list:char <- next head - result <- not first -] - -def within-range? x:num, low:num, high:num -> result:bool [ - local-scope - load-inputs - not-too-far-left?:bool <- greater-or-equal x, low - not-too-far-right?:bool <- lesser-or-equal x, high - result <- and not-too-far-left? not-too-far-right? -] - -scenario copy-fails-if-sandbox-editor-not-empty [ - local-scope - trace-until 50/app # trace too long - assume-screen 50/width, 10/height - # empty recipes - assume-resources [ - ] - env:&:environment <- new-programming-environment resources, screen, [add 1, 1] # contents of sandbox editor - render-all screen, env, render - # run it - assume-console [ - press F4 - ] - event-loop screen, console, env, resources - screen-should-contain [ - . run (F4) . - . . - .──────────────────────────────────────────────────. - .0 edit copy delete . - .add 1, 1 . - .2 . - .──────────────────────────────────────────────────. - . . - ] - # type something into the sandbox editor, then click on the 'copy' button - assume-console [ - left-click 2, 20 # put cursor in sandbox editor - type [0] # type something - left-click 3, 20 # click 'copy' button - ] - run [ - event-loop screen, console, env - ] - # copy doesn't happen - screen-should-contain [ - . run (F4) . - .0 . - .──────────────────────────────────────────────────. - .0 edit copy delete . - .add 1, 1 . - .2 . - .──────────────────────────────────────────────────. - . . - ] - # cursor should be in the right place - assume-console [ - type [1] - ] - run [ - event-loop screen, console, env, resources - ] - screen-should-contain [ - . run (F4) . - .01 . - .──────────────────────────────────────────────────. - .0 edit copy delete . - .add 1, 1 . - .2 . - .──────────────────────────────────────────────────. - . . - ] -] diff --git a/archive/2.vm/sandbox/007-sandbox-delete.mu b/archive/2.vm/sandbox/007-sandbox-delete.mu deleted file mode 100644 index 01f01f42..00000000 --- a/archive/2.vm/sandbox/007-sandbox-delete.mu +++ /dev/null @@ -1,345 +0,0 @@ -## deleting sandboxes - -scenario deleting-sandboxes [ - local-scope - trace-until 100/app # trace too long - assume-screen 50/width, 15/height - assume-resources [ - ] - env:&:environment <- new-programming-environment resources, screen, [] - render-all screen, env, render - # run a few commands - assume-console [ - type [divide-with-remainder 11, 3] - press F4 - type [add 2, 2] - press F4 - ] - event-loop screen, console, env, resources - screen-should-contain [ - . run (F4) . - . . - .──────────────────────────────────────────────────. - .0 edit copy delete . - .add 2, 2 . - .4 . - .──────────────────────────────────────────────────. - .1 edit copy delete . - .divide-with-remainder 11, 3 . - .3 . - .2 . - .──────────────────────────────────────────────────. - . . - ] - # delete second sandbox by clicking on left edge of 'delete' button - assume-console [ - left-click 7, 34 - ] - run [ - event-loop screen, console, env, resources - ] - screen-should-contain [ - . run (F4) . - . . - .──────────────────────────────────────────────────. - .0 edit copy delete . - .add 2, 2 . - .4 . - .──────────────────────────────────────────────────. - . . - ] - # delete first sandbox by clicking at right edge of 'delete' button - assume-console [ - left-click 3, 49 - ] - run [ - event-loop screen, console, env, resources - ] - screen-should-contain [ - . run (F4) . - . . - .──────────────────────────────────────────────────. - . . - ] -] - -after [ - # support 'delete' button - { - delete?:bool <- should-attempt-delete? click-row, click-column, env - break-unless delete? - delete?, env <- try-delete-sandbox click-row, env - break-unless delete? - screen <- render-sandbox-side screen, env, render - screen <- update-cursor screen, current-sandbox, env - loop +next-event - } -] - -# some preconditions for attempting to delete a sandbox -def should-attempt-delete? click-row:num, click-column:num, env:&:environment -> result:bool [ - local-scope - load-inputs - # are we below the sandbox editor? - click-sandbox-area?:bool <- click-on-sandbox-area? click-row, env - return-unless click-sandbox-area?, false - # narrower, is the click in the columns spanning the 'copy' button? - first-sandbox:&:editor <- get *env, current-sandbox:offset - assert first-sandbox, [!!] - sandbox-left-margin:num <- get *first-sandbox, left:offset - sandbox-right-margin:num <- get *first-sandbox, right:offset - _, _, _, _, delete-button-left:num <- sandbox-menu-columns sandbox-left-margin, sandbox-right-margin - result <- within-range? click-column, delete-button-left, sandbox-right-margin -] - -def try-delete-sandbox click-row:num, env:&:environment -> clicked-on-delete-button?:bool, env:&:environment [ - local-scope - load-inputs - # identify the sandbox to delete, if the click was actually on the 'delete' button - sandbox:&:sandbox <- find-sandbox env, click-row - return-unless sandbox, false - clicked-on-delete-button? <- copy true - env <- delete-sandbox env, sandbox -] - -def delete-sandbox env:&:environment, sandbox:&:sandbox -> env:&:environment [ - local-scope - load-inputs - curr-sandbox:&:sandbox <- get *env, sandbox:offset - first-sandbox?:bool <- equal curr-sandbox, sandbox - { - # first sandbox? pop - break-unless first-sandbox? - next-sandbox:&:sandbox <- get *curr-sandbox, next-sandbox:offset - *env <- put *env, sandbox:offset, next-sandbox - } - { - # not first sandbox? - break-if first-sandbox? - prev-sandbox:&:sandbox <- copy curr-sandbox - curr-sandbox <- get *curr-sandbox, next-sandbox:offset - { - assert curr-sandbox, [sandbox not found! something is wrong.] - found?:bool <- equal curr-sandbox, sandbox - break-if found? - prev-sandbox <- copy curr-sandbox - curr-sandbox <- get *curr-sandbox, next-sandbox:offset - loop - } - # snip sandbox out of its list - next-sandbox:&:sandbox <- get *curr-sandbox, next-sandbox:offset - *prev-sandbox <- put *prev-sandbox, next-sandbox:offset, next-sandbox - } - # update sandbox count - sandbox-count:num <- get *env, number-of-sandboxes:offset - sandbox-count <- subtract sandbox-count, 1 - *env <- put *env, number-of-sandboxes:offset, sandbox-count - # reset scroll if deleted sandbox was last - { - break-if next-sandbox - render-from:num <- get *env, render-from:offset - reset-scroll?:bool <- equal render-from, sandbox-count - break-unless reset-scroll? - *env <- put *env, render-from:offset, -1 - } -] - -scenario deleting-sandbox-after-scroll [ - local-scope - trace-until 100/app # trace too long - assume-screen 50/width, 10/height - # initialize environment - assume-resources [ - ] - env:&:environment <- new-programming-environment resources, screen, [] - render-all screen, env, render - # create 2 sandboxes and scroll to second - assume-console [ - press ctrl-n - type [add 2, 2] - press F4 - type [add 1, 1] - press F4 - press page-down - ] - event-loop screen, console, env, resources - screen-should-contain [ - . run (F4) . - .──────────────────────────────────────────────────. - .0 edit copy delete . - .add 1, 1 . - .2 . - .──────────────────────────────────────────────────. - .1 edit copy delete . - .add 2, 2 . - .4 . - .──────────────────────────────────────────────────. - ] - # delete the second sandbox - assume-console [ - left-click 6, 34 - ] - run [ - event-loop screen, console, env, resources - ] - # second sandbox shows in editor; scroll resets to display first sandbox - screen-should-contain [ - . run (F4) . - .──────────────────────────────────────────────────. - .0 edit copy delete . - .add 1, 1 . - .2 . - .──────────────────────────────────────────────────. - . . - ] -] - -scenario deleting-top-sandbox-after-scroll [ - local-scope - trace-until 100/app # trace too long - assume-screen 50/width, 10/height - # initialize environment - assume-resources [ - ] - env:&:environment <- new-programming-environment resources, screen, [] - render-all screen, env, render - # create 2 sandboxes and scroll to second - assume-console [ - press ctrl-n - type [add 2, 2] - press F4 - type [add 1, 1] - press F4 - press page-down - ] - event-loop screen, console, env, resources - screen-should-contain [ - . run (F4) . - .──────────────────────────────────────────────────. - .0 edit copy delete . - .add 1, 1 . - .2 . - .──────────────────────────────────────────────────. - .1 edit copy delete . - .add 2, 2 . - .4 . - .──────────────────────────────────────────────────. - ] - # delete the second sandbox - assume-console [ - left-click 2, 34 - ] - run [ - event-loop screen, console, env, resources - ] - # second sandbox shows in editor; scroll resets to display first sandbox - screen-should-contain [ - . run (F4) . - .──────────────────────────────────────────────────. - .0 edit copy delete . - .add 2, 2 . - .4 . - .──────────────────────────────────────────────────. - . . - ] -] - -scenario deleting-final-sandbox-after-scroll [ - local-scope - trace-until 100/app # trace too long - assume-screen 50/width, 10/height - # initialize environment - assume-resources [ - ] - env:&:environment <- new-programming-environment resources, screen, [] - render-all screen, env, render - # create 2 sandboxes and scroll to second - assume-console [ - press ctrl-n - type [add 2, 2] - press F4 - type [add 1, 1] - press F4 - press page-down - press page-down - ] - event-loop screen, console, env, resources - screen-should-contain [ - . run (F4) . - .──────────────────────────────────────────────────. - .1 edit copy delete . - .add 2, 2 . - .4 . - .──────────────────────────────────────────────────. - . . - ] - # delete the second sandbox - assume-console [ - left-click 2, 34 - ] - run [ - event-loop screen, console, env, resources - ] - # implicitly scroll up to first sandbox - screen-should-contain [ - . run (F4) . - . . - .──────────────────────────────────────────────────. - .0 edit copy delete . - .add 1, 1 . - .2 . - .──────────────────────────────────────────────────. - . . - ] -] - -scenario deleting-updates-sandbox-count [ - local-scope - trace-until 100/app # trace too long - assume-screen 50/width, 10/height - # initialize environment - assume-resources [ - ] - env:&:environment <- new-programming-environment resources, screen, [] - render-all screen, env, render - # create 2 sandboxes - assume-console [ - press ctrl-n - type [add 2, 2] - press F4 - type [add 1, 1] - press F4 - ] - event-loop screen, console, env, resources - screen-should-contain [ - . run (F4) . - . . - .──────────────────────────────────────────────────. - .0 edit copy delete . - .add 1, 1 . - .2 . - .──────────────────────────────────────────────────. - .1 edit copy delete . - .add 2, 2 . - .4 . - ] - # delete the second sandbox, then try to scroll down twice - assume-console [ - left-click 3, 34 - press page-down - press page-down - ] - run [ - event-loop screen, console, env, resources - ] - # shouldn't go past last sandbox - screen-should-contain [ - . run (F4) . - .──────────────────────────────────────────────────. - .0 edit copy delete . - .add 2, 2 . - .4 . - .──────────────────────────────────────────────────. - . . - ] -] diff --git a/archive/2.vm/sandbox/008-sandbox-edit.mu b/archive/2.vm/sandbox/008-sandbox-edit.mu deleted file mode 100644 index fb3981bf..00000000 --- a/archive/2.vm/sandbox/008-sandbox-edit.mu +++ /dev/null @@ -1,319 +0,0 @@ -## editing sandboxes after they've been created - -scenario clicking-on-sandbox-edit-button-moves-it-to-editor [ - local-scope - trace-until 100/app # trace too long - assume-screen 50/width, 10/height - # empty recipes - assume-resources [ - ] - env:&:environment <- new-programming-environment resources, screen, [add 2, 2] - render-all screen, env, render - # run it - assume-console [ - press F4 - ] - event-loop screen, console, env, resources - screen-should-contain [ - . run (F4) . - . . - .──────────────────────────────────────────────────. - .0 edit copy delete . - .add 2, 2 . - .4 . - .──────────────────────────────────────────────────. - . . - ] - # click at left edge of 'edit' button - assume-console [ - left-click 3, 4 - ] - run [ - event-loop screen, console, env, resources - ] - # it pops back into editor - screen-should-contain [ - . run (F4) . - .add 2, 2 . - .──────────────────────────────────────────────────. - . . - ] - # cursor should be in the right place - assume-console [ - type [0] - ] - run [ - event-loop screen, console, env, resources - ] - screen-should-contain [ - . run (F4) . - .0add 2, 2 . - .──────────────────────────────────────────────────. - . . - ] -] - -scenario clicking-on-sandbox-edit-button-moves-it-to-editor-2 [ - local-scope - trace-until 100/app # trace too long - assume-screen 50/width, 10/height - # empty recipes - assume-resources [ - ] - env:&:environment <- new-programming-environment resources, screen, [add 2, 2] - render-all screen, env, render - # run it - assume-console [ - press F4 - ] - event-loop screen, console, env, resources - screen-should-contain [ - . run (F4) . - . . - .──────────────────────────────────────────────────. - .0 edit copy delete . - .add 2, 2 . - .4 . - .──────────────────────────────────────────────────. - . . - ] - # click at right edge of 'edit' button (just before 'copy') - assume-console [ - left-click 3, 18 - ] - run [ - event-loop screen, console, env, resources - ] - # it pops back into editor - screen-should-contain [ - . run (F4) . - .add 2, 2 . - .──────────────────────────────────────────────────. - . . - ] - # cursor should be in the right place - assume-console [ - type [0] - ] - run [ - event-loop screen, console, env, resources - ] - screen-should-contain [ - . run (F4) . - .0add 2, 2 . - .──────────────────────────────────────────────────. - . . - ] -] - -after [ - # support 'edit' button - { - edit?:bool <- should-attempt-edit? click-row, click-column, env - break-unless edit? - edit?, env <- try-edit-sandbox click-row, env - break-unless edit? - screen <- render-sandbox-side screen, env, render - screen <- update-cursor screen, current-sandbox, env - loop +next-event - } -] - -# some preconditions for attempting to edit a sandbox -def should-attempt-edit? click-row:num, click-column:num, env:&:environment -> result:bool [ - local-scope - load-inputs - # are we below the sandbox editor? - click-sandbox-area?:bool <- click-on-sandbox-area? click-row, env - return-unless click-sandbox-area?, false - # narrower, is the click in the columns spanning the 'edit' button? - first-sandbox:&:editor <- get *env, current-sandbox:offset - assert first-sandbox, [!!] - sandbox-left-margin:num <- get *first-sandbox, left:offset - sandbox-right-margin:num <- get *first-sandbox, right:offset - edit-button-left:num, edit-button-right:num, _ <- sandbox-menu-columns sandbox-left-margin, sandbox-right-margin - edit-button-vertical-area?:bool <- within-range? click-column, edit-button-left, edit-button-right - return-unless edit-button-vertical-area?, false - # finally, is sandbox editor empty? - current-sandbox:&:editor <- get *env, current-sandbox:offset - result <- empty-editor? current-sandbox -] - -def try-edit-sandbox click-row:num, env:&:environment -> clicked-on-edit-button?:bool, env:&:environment [ - local-scope - load-inputs - # identify the sandbox to edit, if the click was actually on the 'edit' button - sandbox:&:sandbox <- find-sandbox env, click-row - return-unless sandbox, false - clicked-on-edit-button? <- copy true - # 'edit' button = 'copy' button + 'delete' button - text:text <- get *sandbox, data:offset - current-sandbox:&:editor <- get *env, current-sandbox:offset - current-sandbox <- insert-text current-sandbox, text - env <- delete-sandbox env, sandbox - # reset scroll - *env <- put *env, render-from:offset, -1 -] - -scenario sandbox-with-print-can-be-edited [ - local-scope - trace-until 100/app # trace too long - assume-screen 50/width, 20/height - # empty recipes - assume-resources [ - ] - # right editor contains a print instruction - env:&:environment <- new-programming-environment resources, screen, [print screen, 4] - render-all screen, env, render - # run the sandbox - assume-console [ - press F4 - ] - event-loop screen, console, env, resources - screen-should-contain [ - . run (F4) . - . . - .──────────────────────────────────────────────────. - .0 edit copy delete . - .print screen, 4 . - .screen: . - . .4 . . - . . . . - . . . . - . . . . - . . . . - .──────────────────────────────────────────────────. - . . - ] - # edit the sandbox - assume-console [ - left-click 3, 18 - ] - run [ - event-loop screen, console, env, resources - ] - screen-should-contain [ - . run (F4) . - .print screen, 4 . - .──────────────────────────────────────────────────. - . . - . . - ] -] - -scenario editing-sandbox-after-scrolling-resets-scroll [ - local-scope - trace-until 100/app # trace too long - assume-screen 50/width, 20/height - # initialize environment - assume-resources [ - ] - env:&:environment <- new-programming-environment resources, screen, [] - render-all screen, env, render - # create 2 sandboxes and scroll to second - assume-console [ - press ctrl-n - type [add 2, 2] - press F4 - type [add 1, 1] - press F4 - press page-down - press page-down - ] - event-loop screen, console, env, resources - screen-should-contain [ - . run (F4) . - .──────────────────────────────────────────────────. - .1 edit copy delete . - .add 2, 2 . - .4 . - .──────────────────────────────────────────────────. - . . - ] - # edit the second sandbox - assume-console [ - left-click 2, 10 - ] - run [ - event-loop screen, console, env, resources - ] - # second sandbox shows in editor; scroll resets to display first sandbox - screen-should-contain [ - . run (F4) . - .add 2, 2 . - .──────────────────────────────────────────────────. - .0 edit copy delete . - .add 1, 1 . - .2 . - .──────────────────────────────────────────────────. - . . - ] -] - -scenario editing-sandbox-updates-sandbox-count [ - local-scope - trace-until 100/app # trace too long - assume-screen 50/width, 20/height - # initialize environment - assume-resources [ - ] - env:&:environment <- new-programming-environment resources, screen, [] - render-all screen, env, render - # create 2 sandboxes - assume-console [ - press ctrl-n - type [add 2, 2] - press F4 - type [add 1, 1] - press F4 - ] - event-loop screen, console, env, resources - screen-should-contain [ - . run (F4) . - . . - .──────────────────────────────────────────────────. - .0 edit copy delete . - .add 1, 1 . - .2 . - .──────────────────────────────────────────────────. - .1 edit copy delete . - ] - # edit the second sandbox, then resave - assume-console [ - left-click 3, 10 - press F4 - ] - run [ - event-loop screen, console, env, resources - ] - # no change in contents - screen-should-contain [ - . run (F4) . - . . - .──────────────────────────────────────────────────. - .0 edit copy delete . - .add 1, 1 . - .2 . - .──────────────────────────────────────────────────. - .1 edit copy delete . - ] - # now try to scroll past end - assume-console [ - press page-down - press page-down - press page-down - ] - run [ - event-loop screen, console, env, resources - ] - # screen should show just final sandbox with the right index (1) - screen-should-contain [ - . run (F4) . - .──────────────────────────────────────────────────. - .1 edit copy delete . - .add 2, 2 . - .4 . - .──────────────────────────────────────────────────. - . . - ] -] diff --git a/archive/2.vm/sandbox/009-sandbox-test.mu b/archive/2.vm/sandbox/009-sandbox-test.mu deleted file mode 100644 index c22916a7..00000000 --- a/archive/2.vm/sandbox/009-sandbox-test.mu +++ /dev/null @@ -1,233 +0,0 @@ -## clicking on sandbox results to 'fix' them and turn sandboxes into tests - -scenario sandbox-click-on-result-toggles-color-to-green [ - local-scope - trace-until 100/app # trace too long - assume-screen 50/width, 20/height - # basic recipe - assume-resources [ - [lesson/recipes.mu] <- [ - |recipe foo [| - | reply 4| - |]| - ] - ] - env:&:environment <- new-programming-environment resources, screen, [foo] - render-all screen, env, render - # run it - assume-console [ - press F4 - ] - event-loop screen, console, env, resources - screen-should-contain [ - . run (F4) . - . . - .──────────────────────────────────────────────────. - .0 edit copy delete . - .foo . - .4 . - .──────────────────────────────────────────────────. - . . - ] - # click on the '4' in the result - $clear-trace - assume-console [ - left-click 5, 21 - ] - run [ - event-loop screen, console, env, resources - ] - # color toggles to green - screen-should-contain-in-color 2/green, [ - . . - . . - . . - . . - . . - .4 . - . . - ] - # don't render entire sandbox side - check-trace-count-for-label-lesser-than 250, [print-character] # say 5 sandbox lines - # cursor should remain unmoved - run [ - cursor:char <- copy 9251/␣ - print screen, cursor - ] - screen-should-contain [ - . run (F4) . - .␣ . - .──────────────────────────────────────────────────. - .0 edit copy delete . - .foo . - .4 . - .──────────────────────────────────────────────────. - . . - ] - # now change the result - assume-resources [ - [lesson/recipes.mu] <- [ - |recipe foo [| - | reply 3| - |]| - ] - ] - # then rerun - assume-console [ - press F4 - ] - run [ - event-loop screen, console, env, resources - ] - # result turns red - screen-should-contain-in-color 1/red, [ - . . - . . - . . - . . - . . - .3 . - . . - ] -] - -# this requires tracking a couple more things -container sandbox [ - response-starting-row-on-screen:num - expected-response:text -] - -# include expected response when saving or restoring a sandbox -before [ - { - expected-response:text <- get *sandbox, expected-response:offset - break-unless expected-response - filename <- append filename, [.out] - resources <- dump resources, filename, expected-response - } -] - -before [ - { - filename <- append filename, [.out] - contents <- slurp resources, filename - break-unless contents - *curr <- put *curr, expected-response:offset, contents - } -] - -# clicks on sandbox responses save it as 'expected' -after [ - # check if it's inside the output of any sandbox - { - sandbox-left-margin:num <- get *current-sandbox, left:offset - click-column:num <- get t, column:offset - on-sandbox-side?:bool <- greater-or-equal click-column, sandbox-left-margin - break-unless on-sandbox-side? - first-sandbox:&:sandbox <- get *env, sandbox:offset - break-unless first-sandbox - first-sandbox-begins:num <- get *first-sandbox, starting-row-on-screen:offset - click-row:num <- get t, row:offset - below-sandbox-editor?:bool <- greater-or-equal click-row, first-sandbox-begins - break-unless below-sandbox-editor? - # identify the sandbox whose output is being clicked on - sandbox:&:sandbox, sandbox-index:num <- find-click-in-sandbox-output env, click-row - break-unless sandbox - # update it - sandbox <- toggle-expected-response sandbox - # minimal update to disk - save-sandbox resources, sandbox, sandbox-index - # minimal update to screen - sandbox-right-margin:num <- get *current-sandbox, right:offset - row:num <- render-sandbox-response screen, sandbox, sandbox-left-margin, sandbox-right-margin - { - height:num <- screen-height screen - at-bottom?:bool <- greater-or-equal row, height - break-if at-bottom? - draw-horizontal screen, row, sandbox-left-margin, sandbox-right-margin - } - screen <- update-cursor screen, current-sandbox, env - loop +next-event - } -] - -def find-click-in-sandbox-output env:&:environment, click-row:num -> sandbox:&:sandbox, sandbox-index:num [ - local-scope - load-inputs - # assert click-row >= sandbox.starting-row-on-screen - sandbox:&:sandbox <- get *env, sandbox:offset - start:num <- get *sandbox, starting-row-on-screen:offset - clicked-on-sandboxes?:bool <- greater-or-equal click-row, start - assert clicked-on-sandboxes?, [extract-sandbox called on click to sandbox editor] - # while click-row < sandbox.next-sandbox.starting-row-on-screen - sandbox-index <- copy 0 - { - next-sandbox:&:sandbox <- get *sandbox, next-sandbox:offset - break-unless next-sandbox - next-start:num <- get *next-sandbox, starting-row-on-screen:offset - found?:bool <- lesser-than click-row, next-start - break-if found? - sandbox <- copy next-sandbox - sandbox-index <- add sandbox-index, 1 - loop - } - # return sandbox if click is in its output region - response-starting-row:num <- get *sandbox, response-starting-row-on-screen:offset - return-unless response-starting-row, null/no-click-in-sandbox-output, 0/sandbox-index - click-in-response?:bool <- greater-or-equal click-row, response-starting-row - return-unless click-in-response?, null/no-click-in-sandbox-output, 0/sandbox-index - return sandbox, sandbox-index -] - -def toggle-expected-response sandbox:&:sandbox -> sandbox:&:sandbox [ - local-scope - load-inputs - expected-response:text <- get *sandbox, expected-response:offset - { - # if expected-response is set, reset - break-unless expected-response - *sandbox <- put *sandbox, expected-response:offset, null - } - { - # if not, set expected response to the current response - break-if expected-response - response:text <- get *sandbox, response:offset - *sandbox <- put *sandbox, expected-response:offset, response - } -] - -# when rendering a sandbox, color it in red/green if expected response exists -after [ - { - break-unless sandbox-response - *sandbox <- put *sandbox, response-starting-row-on-screen:offset, row - row <- render-sandbox-response screen, sandbox, left, right - jump +render-sandbox-end - } -] - -def render-sandbox-response screen:&:screen, sandbox:&:sandbox, left:num, right:num -> row:num, screen:&:screen [ - local-scope - load-inputs - sandbox-response:text <- get *sandbox, response:offset - expected-response:text <- get *sandbox, expected-response:offset - row:num <- get *sandbox response-starting-row-on-screen:offset - { - break-if expected-response - row <- render-text screen, sandbox-response, left, right, 245/grey, row - return - } - response-is-expected?:bool <- equal expected-response, sandbox-response - { - break-if response-is-expected? - row <- render-text screen, sandbox-response, left, right, 1/red, row - } - { - break-unless response-is-expected?:bool - row <- render-text screen, sandbox-response, left, right, 2/green, row - } -] - -before [ - *sandbox <- put *sandbox, response-starting-row-on-screen:offset, 0 -] diff --git a/archive/2.vm/sandbox/010-sandbox-trace.mu b/archive/2.vm/sandbox/010-sandbox-trace.mu deleted file mode 100644 index d544dd8c..00000000 --- a/archive/2.vm/sandbox/010-sandbox-trace.mu +++ /dev/null @@ -1,243 +0,0 @@ -## clicking on the code typed into a sandbox toggles its trace - -scenario sandbox-click-on-code-toggles-app-trace [ - local-scope - trace-until 100/app # trace too long - assume-screen 50/width, 10/height - # run a stash instruction - assume-resources [ - ] - env:&:environment <- new-programming-environment resources, screen, [stash [abc]] - render-all screen, env, render - assume-console [ - press F4 - ] - event-loop screen, console, env, resources - screen-should-contain [ - . run (F4) . - . . - .──────────────────────────────────────────────────. - .0 edit copy delete . - .stash [abc] . - .──────────────────────────────────────────────────. - . . - ] - # click on the code in the sandbox - assume-console [ - left-click 4, 21 - ] - run [ - event-loop screen, console, env, resources - cursor:char <- copy 9251/␣ - print screen, cursor - ] - # trace now printed and cursor shouldn't have budged - screen-should-contain [ - . run (F4) . - .␣ . - .──────────────────────────────────────────────────. - .0 edit copy delete . - .stash [abc] . - .abc . - ] - screen-should-contain-in-color 245/grey, [ - . . - . . - .──────────────────────────────────────────────────. - . . - . . - .abc . - ] - # click again on the same region - assume-console [ - left-click 4, 25 - ] - run [ - event-loop screen, console, env, resources - print screen, cursor - ] - # trace hidden again - screen-should-contain [ - . run (F4) . - .␣ . - .──────────────────────────────────────────────────. - .0 edit copy delete . - .stash [abc] . - .──────────────────────────────────────────────────. - . . - ] -] - -scenario sandbox-shows-app-trace-and-result [ - local-scope - trace-until 100/app # trace too long - assume-screen 50/width, 10/height - # run a stash instruction and some code - assume-resources [ - ] - test-sandbox:text <- new [stash [abc] -add 2, 2] - env:&:environment <- new-programming-environment resources, screen, test-sandbox - render-all screen, env, render - assume-console [ - press F4 - ] - event-loop screen, console, env, resources - screen-should-contain [ - . run (F4) . - . . - .──────────────────────────────────────────────────. - .0 edit copy delete . - .stash [abc] . - .add 2, 2 . - .4 . - .──────────────────────────────────────────────────. - . . - ] - # click on the code in the sandbox - assume-console [ - left-click 4, 21 - ] - run [ - event-loop screen, console, env, resources - ] - # trace now printed above result - screen-should-contain [ - . run (F4) . - . . - .──────────────────────────────────────────────────. - .0 edit copy delete . - .stash [abc] . - .add 2, 2 . - .abc . - .7 instructions run . - .4 . - .──────────────────────────────────────────────────. - ] -] - -scenario clicking-on-app-trace-does-nothing [ - local-scope - trace-until 100/app # trace too long - assume-screen 50/width, 10/height - assume-resources [ - ] - env:&:environment <- new-programming-environment resources, screen, [stash 123456789] - render-all screen, env, render - # create and expand the trace - assume-console [ - press F4 - left-click 4, 1 - ] - event-loop screen, console, env, resources - screen-should-contain [ - . run (F4) . - . . - .──────────────────────────────────────────────────. - .0 edit copy delete . - .stash 123456789 . - .123456789 . - ] - # click on the stash under the edit-button region (or any of the other buttons, really) - assume-console [ - left-click 5, 7 - ] - run [ - event-loop screen, console, env, resources - ] - # no change; doesn't die - screen-should-contain [ - . run (F4) . - . . - .──────────────────────────────────────────────────. - .0 edit copy delete . - .stash 123456789 . - .123456789 . - ] -] - -container sandbox [ - trace:text - display-trace?:bool -] - -# replaced in a later layer -def! update-sandbox sandbox:&:sandbox, env:&:environment, idx:num -> sandbox:&:sandbox, env:&:environment [ - local-scope - load-inputs - data:text <- get *sandbox, data:offset - response:text, _, fake-screen:&:screen, trace:text <- run-sandboxed data - *sandbox <- put *sandbox, response:offset, response - *sandbox <- put *sandbox, screen:offset, fake-screen - *sandbox <- put *sandbox, trace:offset, trace -] - -# clicks on sandbox code toggle its display-trace? flag -after [ - # check if it's inside the code of any sandbox - { - sandbox-left-margin:num <- get *current-sandbox, left:offset - click-column:num <- get t, column:offset - on-sandbox-side?:bool <- greater-or-equal click-column, sandbox-left-margin - break-unless on-sandbox-side? - first-sandbox:&:sandbox <- get *env, sandbox:offset - break-unless first-sandbox - first-sandbox-begins:num <- get *first-sandbox, starting-row-on-screen:offset - click-row:num <- get t, row:offset - below-sandbox-editor?:bool <- greater-or-equal click-row, first-sandbox-begins - break-unless below-sandbox-editor? - # identify the sandbox whose code is being clicked on - sandbox:&:sandbox <- find-click-in-sandbox-code env, click-row - break-unless sandbox - # toggle its display-trace? property - x:bool <- get *sandbox, display-trace?:offset - x <- not x - *sandbox <- put *sandbox, display-trace?:offset, x - screen <- render-sandbox-side screen, env, render - screen <- update-cursor screen, current-sandbox, env - loop +next-event - } -] - -def find-click-in-sandbox-code env:&:environment, click-row:num -> sandbox:&:sandbox [ - local-scope - load-inputs - # assert click-row >= sandbox.starting-row-on-screen - sandbox <- get *env, sandbox:offset - start:num <- get *sandbox, starting-row-on-screen:offset - clicked-on-sandboxes?:bool <- greater-or-equal click-row, start - assert clicked-on-sandboxes?, [extract-sandbox called on click to sandbox editor] - # while click-row < sandbox.next-sandbox.starting-row-on-screen - { - next-sandbox:&:sandbox <- get *sandbox, next-sandbox:offset - break-unless next-sandbox - next-start:num <- get *next-sandbox, starting-row-on-screen:offset - found?:bool <- lesser-than click-row, next-start - break-if found? - sandbox <- copy next-sandbox - loop - } - # return sandbox if click is in its code region - code-ending-row:num <- get *sandbox, code-ending-row-on-screen:offset - click-above-response?:bool <- lesser-than click-row, code-ending-row - start:num <- get *sandbox, starting-row-on-screen:offset - click-below-menu?:bool <- greater-than click-row, start - click-on-sandbox-code?:bool <- and click-above-response?, click-below-menu? - { - break-if click-on-sandbox-code? - return null/no-click-in-sandbox-output - } - return sandbox -] - -# when rendering a sandbox, dump its trace before response/warning if display-trace? property is set -after [ - { - display-trace?:bool <- get *sandbox, display-trace?:offset - break-unless display-trace? - sandbox-trace:text <- get *sandbox, trace:offset - break-unless sandbox-trace # nothing to print; move on - row, screen <- render-text screen, sandbox-trace, left, right, 245/grey, row - } - -] diff --git a/archive/2.vm/sandbox/011-errors.mu b/archive/2.vm/sandbox/011-errors.mu deleted file mode 100644 index 2f59d1fe..00000000 --- a/archive/2.vm/sandbox/011-errors.mu +++ /dev/null @@ -1,687 +0,0 @@ -## handling malformed programs - -container environment [ - recipe-errors:text -] - -# load code from disk, save any errors -def! update-recipes env:&:environment, resources:&:resources, screen:&:screen -> errors-found?:bool, env:&:environment, screen:&:screen [ - local-scope - load-inputs - in:text <- slurp resources, [lesson/recipes.mu] - recipe-errors:text <- reload in - *env <- put *env, recipe-errors:offset, recipe-errors - # if recipe editor has errors, stop - { - break-unless recipe-errors - update-status screen, [errors found ], 1/red - errors-found? <- copy true - return - } - errors-found? <- copy false -] - -before [ - trace 11, [app], [render status] - recipe-errors:text <- get *env, recipe-errors:offset - { - break-unless recipe-errors - update-status screen, [errors found ], 1/red - } -] - -container environment [ - error-index:num # index of first sandbox with an error (or -1 if none) -] - -after [ - *result <- put *result, error-index:offset, -1 -] - -after [ - *env <- put *env, error-index:offset, -1 -] - -before [ - { - error-index:num <- get *env, error-index:offset - sandboxes-completed-successfully?:bool <- equal error-index, -1 - break-if sandboxes-completed-successfully? - errors-found? <- copy true - } -] - -before [ - { - break-if recipe-errors - error-index:num <- get *env, error-index:offset - sandboxes-completed-successfully?:bool <- equal error-index, -1 - break-if sandboxes-completed-successfully? - error-index-text:text <- to-text error-index - status:text <- interpolate [errors found (_) ], error-index-text - update-status screen, status, 1/red - } -] - -container sandbox [ - errors:text -] - -def! update-sandbox sandbox:&:sandbox, env:&:environment, idx:num -> sandbox:&:sandbox, env:&:environment [ - local-scope - load-inputs - { - recipe-errors:text <- get *env, recipe-errors:offset - break-unless recipe-errors - *sandbox <- put *sandbox, errors:offset, recipe-errors - return - } - data:text <- get *sandbox, data:offset - response:text, errors:text, fake-screen:&:screen, trace:text, completed?:bool <- run-sandboxed data - *sandbox <- put *sandbox, response:offset, response - *sandbox <- put *sandbox, errors:offset, errors - *sandbox <- put *sandbox, screen:offset, fake-screen - *sandbox <- put *sandbox, trace:offset, trace - { - break-if errors - break-if completed? - errors <- new [took too long! -] - *sandbox <- put *sandbox, errors:offset, errors - } - { - break-unless errors - error-index:num <- get *env, error-index:offset - error-not-set?:bool <- equal error-index, -1 - break-unless error-not-set? - *env <- put *env, error-index:offset, idx - } -] - -# make sure we render any trace -after [ - { - sandbox-errors:text <- get *sandbox, errors:offset - break-unless sandbox-errors - *sandbox <- put *sandbox, response-starting-row-on-screen:offset, 0 # no response - { - break-unless env - recipe-errors:text <- get *env, recipe-errors:offset - row, screen <- render-text screen, recipe-errors, left, right, 1/red, row - } - row, screen <- render-text screen, sandbox-errors, left, right, 1/red, row - # don't try to print anything more for this sandbox - jump +render-sandbox-end - } -] - -scenario run-shows-errors-in-get [ - local-scope - trace-until 100/app # trace too long - assume-screen 50/width, 20/height - assume-resources [ - [lesson/recipes.mu] <- [ - |recipe foo [| - | get 123:num, foo:offset| - |]| - ] - ] - env:&:environment <- new-programming-environment resources, screen, [foo] - render-all screen, env, render - screen-should-contain [ - . run (F4) . - .foo . - .──────────────────────────────────────────────────. - . . - ] - assume-console [ - press F4 - ] - run [ - event-loop screen, console, env, resources - ] - screen-should-contain [ - . errors found run (F4) . - . . - .──────────────────────────────────────────────────. - .0 edit copy delete . - .foo . - .foo: unknown element 'foo' in container 'number' . - .foo: first ingredient of 'get' should be a contai↩. - .ner, but got '123:num' . - ] - screen-should-contain-in-color 1/red, [ - . errors found . - . . - ] -] - -scenario run-updates-status-with-first-erroneous-sandbox [ - local-scope - trace-until 100/app # trace too long - assume-screen 50/width, 20/height - assume-resources [ - ] - env:&:environment <- new-programming-environment resources, screen, [] - render-all screen, env, render - assume-console [ - # create invalid sandbox 1 - type [get foo, x:offset] - press F4 - # create invalid sandbox 0 - type [get foo, x:offset] - press F4 - ] - run [ - event-loop screen, console, env, resources - ] - # status line shows that error is in first sandbox - screen-should-contain [ - . errors found (0) run (F4) . - ] -] - -scenario run-updates-status-with-first-erroneous-sandbox-2 [ - local-scope - trace-until 100/app # trace too long - assume-screen 50/width, 20/height - assume-resources [ - ] - env:&:environment <- new-programming-environment resources, screen, [] - render-all screen, env, render - assume-console [ - # create invalid sandbox 2 - type [get foo, x:offset] - press F4 - # create invalid sandbox 1 - type [get foo, x:offset] - press F4 - # create valid sandbox 0 - type [add 2, 2] - press F4 - ] - run [ - event-loop screen, console, env, resources - ] - # status line shows that error is in second sandbox - screen-should-contain [ - . errors found (1) run (F4) . - ] -] - -scenario run-hides-errors-from-past-sandboxes [ - local-scope - trace-until 100/app # trace too long - assume-screen 50/width, 20/height - assume-resources [ - ] - env:&:environment <- new-programming-environment resources, screen, [get foo, x:offset] # invalid - render-all screen, env, render - assume-console [ - press F4 # generate error - ] - event-loop screen, console, env, resources - assume-console [ - left-click 3, 10 - press ctrl-k - type [add 2, 2] # valid code - press F4 # update sandbox - ] - run [ - event-loop screen, console, env, resources - ] - # error should disappear - screen-should-contain [ - . run (F4) . - . . - .──────────────────────────────────────────────────. - .0 edit copy delete . - .add 2, 2 . - .4 . - .──────────────────────────────────────────────────. - . . - ] -] - -scenario run-updates-errors-for-shape-shifting-recipes [ - local-scope - trace-until 100/app # trace too long - assume-screen 50/width, 20/height - # define a shape-shifting recipe with an error - assume-resources [ - [lesson/recipes.mu] <- [ - |recipe foo x:_elem -> z:_elem [| - | local-scope| - | load-ingredients| - | y:&:num <- copy null| - | z <- add x, y| - |]| - ] - ] - env:&:environment <- new-programming-environment resources, screen, [foo 2] - render-all screen, env, render - assume-console [ - press F4 - ] - event-loop screen, console, env, resources - screen-should-contain [ - . errors found (0) run (F4) . - . . - .──────────────────────────────────────────────────. - .0 edit copy delete . - .foo 2 . - .foo_2: 'add' requires number ingredients, but got↩. - . 'y' . - .──────────────────────────────────────────────────. - . . - ] - # now rerun everything - assume-console [ - press F4 - ] - run [ - event-loop screen, console, env, resources - ] - # error should remain unchanged - screen-should-contain [ - . errors found (0) run (F4) . - . . - .──────────────────────────────────────────────────. - .0 edit copy delete . - .foo 2 . - .foo_3: 'add' requires number ingredients, but got↩. - . 'y' . - .──────────────────────────────────────────────────. - . . - ] -] - -scenario run-avoids-spurious-errors-on-reloading-shape-shifting-recipes [ - local-scope - trace-until 100/app # trace too long - assume-screen 50/width, 20/height - # overload a well-known shape-shifting recipe - assume-resources [ - [lesson/recipes.mu] <- [ - |recipe length l:&:list:_elem -> n:num [| - |]| - ] - ] - # call code that uses other variants of it, but not it itself - test-sandbox:text <- new [x:&:list:num <- copy null -to-text x] - env:&:environment <- new-programming-environment resources, screen, test-sandbox - render-all screen, env, render - # run it once - assume-console [ - press F4 - ] - event-loop screen, console, env, resources - # no errors anywhere on screen (can't check anything else, since to-text will return an address) - screen-should-contain-in-color 1/red, [ - . . - . . - . . - . . - . <- . - . . - . . - . . - . . - ] - # rerun everything - assume-console [ - press F4 - ] - run [ - event-loop screen, console, env, resources - ] - # still no errors - screen-should-contain-in-color 1/red, [ - . . - . . - . . - . . - . <- . - . . - . . - . . - . . - ] -] - -scenario run-shows-missing-type-errors [ - local-scope - trace-until 100/app # trace too long - assume-screen 50/width, 20/height - assume-resources [ - [lesson/recipes.mu] <- [ - |recipe foo [| - | x <- copy 0| - |]| - ] - ] - env:&:environment <- new-programming-environment resources, screen, [foo] - render-all screen, env, render - assume-console [ - press F4 - ] - run [ - event-loop screen, console, env, resources - ] - screen-should-contain [ - . errors found run (F4) . - . . - .──────────────────────────────────────────────────. - .0 edit copy delete . - .foo . - .foo: missing type for 'x' in 'x <- copy 0' . - ] -] - -scenario run-shows-unbalanced-bracket-errors [ - local-scope - trace-until 100/app # trace too long - assume-screen 50/width, 20/height - # recipe is incomplete (unbalanced '[') - assume-resources [ - [lesson/recipes.mu] <- [ - |recipe foo \\\[| - | x <- copy 0| - ] - ] - env:&:environment <- new-programming-environment resources, screen, [foo] - render-all screen, env, render - assume-console [ - press F4 - ] - run [ - event-loop screen, console, env, resources - ] - screen-should-contain [ - . errors found run (F4) . - . . - .──────────────────────────────────────────────────. - .0 edit copy delete . - .foo . - .9: unbalanced '\\[' for recipe . - .9: unbalanced '\\[' for recipe . - .──────────────────────────────────────────────────. - . . - ] -] - -scenario run-shows-get-on-non-container-errors [ - local-scope - trace-until 100/app # trace too long - assume-screen 50/width, 20/height - assume-resources [ - [lesson/recipes.mu] <- [ - |recipe foo [| - | local-scope| - | x:&:point <- new point:type| - | get x:&:point, 1:offset| - |]| - ] - ] - env:&:environment <- new-programming-environment resources, screen, [foo] - render-all screen, env, render - assume-console [ - press F4 - ] - run [ - event-loop screen, console, env, resources - ] - screen-should-contain [ - . errors found run (F4) . - . . - .──────────────────────────────────────────────────. - .0 edit copy delete . - .foo . - .foo: first ingredient of 'get' should be a contai↩. - .ner, but got 'x:&:point' . - ] -] - -scenario run-shows-non-literal-get-argument-errors [ - local-scope - trace-until 100/app # trace too long - assume-screen 50/width, 20/height - assume-resources [ - [lesson/recipes.mu] <- [ - |recipe foo [| - | local-scope| - | x:num <- copy 0| - | y:&:point <- new point:type| - | get *y:&:point, x:num| - |]| - ] - ] - env:&:environment <- new-programming-environment resources, screen, [foo] - render-all screen, env, render - assume-console [ - press F4 - ] - run [ - event-loop screen, console, env, resources - ] - screen-should-contain [ - . errors found run (F4) . - . . - .──────────────────────────────────────────────────. - .0 edit copy delete . - .foo . - .foo: second ingredient of 'get' should have type ↩. - .'offset', but got 'x:num' . - ] -] - -scenario run-shows-errors-every-time [ - local-scope - trace-until 100/app # trace too long - assume-screen 50/width, 20/height - # try to run a file with an error - assume-resources [ - [lesson/recipes.mu] <- [ - |recipe foo [| - | local-scope| - | x:num <- copy y:num| - |]| - ] - ] - env:&:environment <- new-programming-environment resources, screen, [foo] - render-all screen, env, render - assume-console [ - press F4 - ] - event-loop screen, console, env, resources - screen-should-contain [ - . errors found run (F4) . - . . - .──────────────────────────────────────────────────. - .0 edit copy delete . - .foo . - .foo: tried to read ingredient 'y' in 'x:num <- co↩. - .py y:num' but it hasn't been written to yet . - ] - # rerun the file, check for the same error - assume-console [ - press F4 - ] - run [ - event-loop screen, console, env, resources - ] - screen-should-contain [ - . errors found run (F4) . - . . - .──────────────────────────────────────────────────. - .0 edit copy delete . - .foo . - .foo: tried to read ingredient 'y' in 'x:num <- co↩. - .py y:num' but it hasn't been written to yet . - ] -] - -scenario run-instruction-and-print-errors [ - local-scope - trace-until 100/app # trace too long - assume-screen 50/width, 15/height - assume-resources [ - ] - # editor contains an illegal instruction - env:&:environment <- new-programming-environment resources, screen, [get 1:&:point, 1:offset] - render-all screen, env, render - assume-console [ - press F4 - ] - run [ - event-loop screen, console, env, resources - ] - # check that screen prints error message in red - screen-should-contain [ - . errors found (0) run (F4) . - . . - .──────────────────────────────────────────────────. - .0 edit copy delete . - .get 1:&:point, 1:offset . - .first ingredient of 'get' should be a container, ↩. - .but got '1:&:point' . - .──────────────────────────────────────────────────. - . . - ] - screen-should-contain-in-color 1/red, [ - . errors found (0) . - . . - . . - . . - . . - .first ingredient of 'get' should be a container, . - .but got '1:&:point' . - . . - . . - ] -] - -scenario run-instruction-and-print-errors-only-once [ - local-scope - trace-until 100/app # trace too long - assume-screen 50/width, 10/height - assume-resources [ - ] - # editor contains an illegal instruction - env:&:environment <- new-programming-environment resources, screen, [get 1234:num, foo:offset] - render-all screen, env, render - # run the code in the editors multiple times - assume-console [ - press F4 - press F4 - ] - run [ - event-loop screen, console, env, resources - ] - # check that screen prints error message just once - screen-should-contain [ - . errors found (0) run (F4) . - . . - .──────────────────────────────────────────────────. - .0 edit copy delete . - .get 1234:num, foo:offset . - .unknown element 'foo' in container 'number' . - .first ingredient of 'get' should be a container, ↩. - .but got '1234:num' . - .──────────────────────────────────────────────────. - . . - ] -] - -scenario sandbox-can-handle-infinite-loop [ - local-scope - trace-until 100/app # trace too long - assume-screen 50/width, 20/height - assume-resources [ - ] - # editor contains an infinite loop - test-sandbox:text <- new [{ -loop -}] - env:&:environment <- new-programming-environment resources, screen, test-sandbox - render-all screen, env, render - # run the sandbox - assume-console [ - press F4 - ] - run [ - event-loop screen, console, env, resources - ] - screen-should-contain [ - . errors found (0) run (F4) . - . . - .──────────────────────────────────────────────────. - .0 edit copy delete . - .{ . - .loop . - .} . - .took too long! . - .──────────────────────────────────────────────────. - . . - ] -] - -scenario sandbox-with-errors-shows-trace [ - local-scope - trace-until 100/app # trace too long - assume-screen 50/width, 20/height - # generate a stash and a error - assume-resources [ - [lesson/recipes.mu] <- [ - |recipe foo [| - | local-scope| - | a:num <- next-ingredient| - | b:num <- next-ingredient| - | stash [dividing by], b| - | _, c:num <- divide-with-remainder a, b| - | reply b| - |]| - ] - ] - env:&:environment <- new-programming-environment resources, screen, [foo 4, 0] - render-all screen, env, render - # run - assume-console [ - press F4 - ] - event-loop screen, console, env, resources - # screen prints error message - screen-should-contain [ - . errors found (0) run (F4) . - . . - .──────────────────────────────────────────────────. - .0 edit copy delete . - .foo 4, 0 . - .foo: divide by zero in '_, c:num <- divide-with-r↩. - .emainder a, b' . - .──────────────────────────────────────────────────. - . . - ] - # click on the call in the sandbox - assume-console [ - left-click 4, 15 - ] - run [ - event-loop screen, console, env, resources - ] - # screen should expand trace - screen-should-contain [ - . errors found (0) run (F4) . - . . - .──────────────────────────────────────────────────. - .0 edit copy delete . - .foo 4, 0 . - .dividing by 0 . - .14 instructions run . - .foo: divide by zero in '_, c:num <- divide-with-r↩. - .emainder a, b' . - .──────────────────────────────────────────────────. - . . - ] -] diff --git a/archive/2.vm/sandbox/012-editor-undo.mu b/archive/2.vm/sandbox/012-editor-undo.mu deleted file mode 100644 index 69afd207..00000000 --- a/archive/2.vm/sandbox/012-editor-undo.mu +++ /dev/null @@ -1,1907 +0,0 @@ -## undo/redo - -# for every undoable event, create a type of *operation* that contains all the -# information needed to reverse it -exclusive-container operation [ - typing:insert-operation - move:move-operation - delete:delete-operation -] - -container insert-operation [ - before-row:num - before-column:num - before-top-of-screen:&:duplex-list:char - after-row:num - after-column:num - after-top-of-screen:&:duplex-list:char - # inserted text is from 'insert-from' until 'insert-until'; list doesn't have to terminate - insert-from:&:duplex-list:char - insert-until:&:duplex-list:char - tag:num # event causing this operation; might be used to coalesce runs of similar events - # 0: no coalesce (enter+indent) - # 1: regular alphanumeric characters -] - -container move-operation [ - before-row:num - before-column:num - before-top-of-screen:&:duplex-list:char - after-row:num - after-column:num - after-top-of-screen:&:duplex-list:char - tag:num # event causing this operation; might be used to coalesce runs of similar events - # 0: no coalesce (touch events, etc) - # 1: left arrow - # 2: right arrow - # 3: up arrow - # 4: down arrow -] - -container delete-operation [ - before-row:num - before-column:num - before-top-of-screen:&:duplex-list:char - after-row:num - after-column:num - after-top-of-screen:&:duplex-list:char - deleted-text:&:duplex-list:char - delete-from:&:duplex-list:char - delete-until:&:duplex-list:char - tag:num # event causing this operation; might be used to coalesce runs of similar events - # 0: no coalesce (ctrl-k, ctrl-u) - # 1: backspace - # 2: delete -] - -# every editor accumulates a list of operations to undo/redo -container editor [ - undo:&:list:&:operation - redo:&:list:&:operation -] - -# ctrl-z - undo operation -after [ - { - undo?:bool <- equal c, 26/ctrl-z - break-unless undo? - undo:&:list:&:operation <- get *editor, undo:offset - break-unless undo - op:&:operation <- first undo - undo <- rest undo - *editor <- put *editor, undo:offset, undo - redo:&:list:&:operation <- get *editor, redo:offset - redo <- push op, redo - *editor <- put *editor, redo:offset, redo - - return true/go-render - } -] - -# ctrl-y - redo operation -after [ - { - redo?:bool <- equal c, 25/ctrl-y - break-unless redo? - redo:&:list:&:operation <- get *editor, redo:offset - break-unless redo - op:&:operation <- first redo - redo <- rest redo - *editor <- put *editor, redo:offset, redo - undo:&:list:&:operation <- get *editor, undo:offset - undo <- push op, undo - *editor <- put *editor, undo:offset, undo - - return true/go-render - } -] - -# undo typing - -scenario editor-can-undo-typing [ - local-scope - # create an editor and type a character - assume-screen 10/width, 5/height - e:&:editor <- new-editor [], 0/left, 10/right - editor-render screen, e - assume-console [ - type [0] - ] - editor-event-loop screen, console, e - # undo - assume-console [ - press ctrl-z - ] - run [ - editor-event-loop screen, console, e - ] - # character should be gone - screen-should-contain [ - . . - . . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - # cursor should be in the right place - assume-console [ - type [1] - ] - run [ - editor-event-loop screen, console, e - ] - screen-should-contain [ - . . - .1 . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] -] - -# save operation to undo -after [ - top-before:&:duplex-list:char <- get *editor, top-of-screen:offset - cursor-before:&:duplex-list:char <- get *editor, before-cursor:offset -] -before [ - top-after:&:duplex-list:char <- get *editor, top-of-screen:offset - cursor-row:num <- get *editor, cursor-row:offset - cursor-column:num <- get *editor, cursor-column:offset - undo:&:list:&:operation <- get *editor, undo:offset - { - # if previous operation was an insert, coalesce this operation with it - break-unless undo - op:&:operation <- first undo - typing:insert-operation, is-insert?:bool <- maybe-convert *op, typing:variant - break-unless is-insert? - previous-coalesce-tag:num <- get typing, tag:offset - break-unless previous-coalesce-tag - before-cursor:&:duplex-list:char <- get *editor, before-cursor:offset - insert-until:&:duplex-list:char <- next before-cursor - typing <- put typing, insert-until:offset, insert-until - typing <- put typing, after-row:offset, cursor-row - typing <- put typing, after-column:offset, cursor-column - typing <- put typing, after-top-of-screen:offset, top-after - *op <- merge 0/insert-operation, typing - break +done-adding-insert-operation - } - # if not, create a new operation - insert-from:&:duplex-list:char <- next cursor-before - insert-to:&:duplex-list:char <- next insert-from - op:&:operation <- new operation:type - *op <- merge 0/insert-operation, save-row/before, save-column/before, top-before, cursor-row/after, cursor-column/after, top-after, insert-from, insert-to, 1/coalesce - editor <- add-operation editor, op - +done-adding-insert-operation -] - -# enter operations never coalesce with typing before or after -after [ - cursor-row-before:num <- copy cursor-row - cursor-column-before:num <- copy cursor-column - top-before:&:duplex-list:char <- get *editor, top-of-screen:offset - cursor-before:&:duplex-list:char <- get *editor, before-cursor:offset -] -before [ - top-after:&:duplex-list:char <- get *editor, top-of-screen:offset - cursor-row:num <- get *editor, cursor-row:offset - cursor-column:num <- get *editor, cursor-row:offset - # never coalesce - insert-from:&:duplex-list:char <- next cursor-before - before-cursor:&:duplex-list:char <- get *editor, before-cursor:offset - insert-to:&:duplex-list:char <- next before-cursor - op:&: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 -] - -# Everytime you add a new operation to the undo stack, be sure to clear the -# 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. -def add-operation editor:&:editor, op:&:operation -> editor:&:editor [ - local-scope - load-inputs - undo:&:list:&:operation <- get *editor, undo:offset - undo <- push op undo - *editor <- put *editor, undo:offset, undo - redo:&:list:&:operation <- get *editor, redo:offset - redo <- copy null - *editor <- put *editor, redo:offset, redo -] - -after [ - { - typing:insert-operation, is-insert?:bool <- maybe-convert *op, typing:variant - break-unless is-insert? - start:&:duplex-list:char <- get typing, insert-from:offset - end:&:duplex-list:char <- get typing, insert-until:offset - # assert cursor-row/cursor-column/top-of-screen match after-row/after-column/after-top-of-screen - before-cursor:&:duplex-list:char <- prev start - *editor <- put *editor, before-cursor:offset, before-cursor - remove-between before-cursor, end - cursor-row <- get typing, before-row:offset - *editor <- put *editor, cursor-row:offset, cursor-row - cursor-column <- get typing, before-column:offset - *editor <- put *editor, cursor-column:offset, cursor-column - top:&:duplex-list:char <- get typing, before-top-of-screen:offset - *editor <- put *editor, top-of-screen:offset, top - } -] - -scenario editor-can-undo-typing-multiple [ - local-scope - # create an editor and type multiple characters - assume-screen 10/width, 5/height - e:&:editor <- new-editor [], 0/left, 10/right - editor-render screen, e - assume-console [ - type [012] - ] - editor-event-loop screen, console, e - # undo - assume-console [ - press ctrl-z - ] - run [ - editor-event-loop screen, console, e - ] - # all characters must be gone - screen-should-contain [ - . . - . . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] -] - -scenario editor-can-undo-typing-multiple-2 [ - local-scope - # create an editor with some text - assume-screen 10/width, 5/height - e:&:editor <- new-editor [a], 0/left, 10/right - editor-render screen, e - # type some characters - assume-console [ - type [012] - ] - editor-event-loop screen, console, e - screen-should-contain [ - . . - .012a . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - # undo - assume-console [ - press ctrl-z - ] - run [ - editor-event-loop screen, console, e - ] - # back to original text - screen-should-contain [ - . . - .a . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - # cursor should be in the right place - assume-console [ - type [3] - ] - run [ - editor-event-loop screen, console, e - ] - screen-should-contain [ - . . - .3a . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] -] - -scenario editor-can-undo-typing-enter [ - local-scope - # create an editor with some text - assume-screen 10/width, 5/height - e:&:editor <- new-editor [ abc], 0/left, 10/right - editor-render screen, e - # new line - assume-console [ - left-click 1, 8 - press enter - ] - editor-event-loop screen, console, e - screen-should-contain [ - . . - . abc . - . . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - # line is indented - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - memory-should-contain [ - 3 <- 2 - 4 <- 2 - ] - # undo - assume-console [ - press ctrl-z - ] - run [ - editor-event-loop screen, console, e - ] - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - memory-should-contain [ - 3 <- 1 - 4 <- 5 - ] - # back to original text - screen-should-contain [ - . . - . abc . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - # cursor should be at end of line - assume-console [ - type [1] - ] - run [ - editor-event-loop screen, console, e - ] - screen-should-contain [ - . . - . abc1 . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] -] - -# redo typing - -scenario editor-redo-typing [ - local-scope - # create an editor, type something, undo - assume-screen 10/width, 5/height - e:&:editor <- new-editor [a], 0/left, 10/right - editor-render screen, e - assume-console [ - type [012] - press ctrl-z - ] - editor-event-loop screen, console, e - screen-should-contain [ - . . - .a . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - # redo - assume-console [ - press ctrl-y - ] - run [ - editor-event-loop screen, console, e - ] - # all characters must be back - screen-should-contain [ - . . - .012a . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - # cursor should be in the right place - assume-console [ - type [3] - ] - run [ - editor-event-loop screen, console, e - ] - screen-should-contain [ - . . - .0123a . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] -] - -after [ - { - typing:insert-operation, is-insert?:bool <- maybe-convert *op, typing:variant - break-unless is-insert? - before-cursor <- get *editor, before-cursor:offset - insert-from:&:duplex-list:char <- get typing, insert-from:offset # ignore insert-to because it's already been spliced away - # assert insert-to matches next(before-cursor) - splice 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 - *editor <- put *editor, cursor-row:offset, cursor-row - cursor-column <- get typing, after-column:offset - *editor <- put *editor, cursor-column:offset, cursor-column - top:&:duplex-list:char <- get typing, after-top-of-screen:offset - *editor <- put *editor, top-of-screen:offset, top - } -] - -scenario editor-redo-typing-empty [ - local-scope - # create an editor, type something, undo - assume-screen 10/width, 5/height - e:&:editor <- new-editor [], 0/left, 10/right - editor-render screen, e - assume-console [ - type [012] - press ctrl-z - ] - editor-event-loop screen, console, e - screen-should-contain [ - . . - . . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - # redo - assume-console [ - press ctrl-y - ] - run [ - editor-event-loop screen, console, e - ] - # all characters must be back - screen-should-contain [ - . . - .012 . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - # cursor should be in the right place - assume-console [ - type [3] - ] - run [ - editor-event-loop screen, console, e - ] - screen-should-contain [ - . . - .0123 . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] -] - -scenario editor-work-clears-redo-stack [ - local-scope - # create an editor with some text, do some work, undo - assume-screen 10/width, 5/height - contents:text <- new [abc -def -ghi] - e:&:editor <- new-editor contents, 0/left, 10/right - editor-render screen, e - assume-console [ - type [1] - press ctrl-z - ] - editor-event-loop screen, console, e - # do some more work - assume-console [ - type [0] - ] - editor-event-loop screen, console, e - screen-should-contain [ - . . - .0abc . - .def . - .ghi . - .┈┈┈┈┈┈┈┈┈┈. - ] - # redo - assume-console [ - press ctrl-y - ] - run [ - editor-event-loop screen, console, e - ] - # nothing should happen - screen-should-contain [ - . . - .0abc . - .def . - .ghi . - .┈┈┈┈┈┈┈┈┈┈. - ] -] - -scenario editor-can-redo-typing-and-enter-and-tab [ - local-scope - # create an editor - assume-screen 10/width, 5/height - e:&:editor <- new-editor [], 0/left, 10/right - editor-render screen, e - # insert some text and tabs, hit enter, some more text and tabs - assume-console [ - press tab - type [ab] - press tab - type [cd] - press enter - press tab - type [efg] - ] - editor-event-loop screen, console, e - screen-should-contain [ - . . - . ab cd . - . efg . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - memory-should-contain [ - 3 <- 2 - 4 <- 7 - ] - # undo - assume-console [ - press ctrl-z - ] - run [ - editor-event-loop screen, console, e - ] - # typing in second line deleted, but not indent - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - memory-should-contain [ - 3 <- 2 - 4 <- 2 - ] - screen-should-contain [ - . . - . ab cd . - . . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - # undo again - assume-console [ - press ctrl-z - ] - run [ - editor-event-loop screen, console, e - ] - # indent and newline deleted - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - memory-should-contain [ - 3 <- 1 - 4 <- 8 - ] - screen-should-contain [ - . . - . ab cd . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - # undo again - assume-console [ - press ctrl-z - ] - run [ - editor-event-loop screen, console, e - ] - # empty screen - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - memory-should-contain [ - 3 <- 1 - 4 <- 0 - ] - screen-should-contain [ - . . - . . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - # redo - assume-console [ - press ctrl-y - ] - run [ - editor-event-loop screen, console, e - ] - # first line inserted - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - memory-should-contain [ - 3 <- 1 - 4 <- 8 - ] - screen-should-contain [ - . . - . ab cd . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - # redo again - assume-console [ - press ctrl-y - ] - run [ - editor-event-loop screen, console, e - ] - # newline and indent inserted - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - memory-should-contain [ - 3 <- 2 - 4 <- 2 - ] - screen-should-contain [ - . . - . ab cd . - . . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - # redo again - assume-console [ - press ctrl-y - ] - run [ - editor-event-loop screen, console, e - ] - # indent and newline deleted - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - memory-should-contain [ - 3 <- 2 - 4 <- 7 - ] - screen-should-contain [ - . . - . ab cd . - . efg . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] -] - -# undo cursor movement - -scenario editor-can-undo-touch [ - local-scope - # create an editor with some text - assume-screen 10/width, 5/height - contents:text <- new [abc -def -ghi] - e:&:editor <- new-editor contents, 0/left, 10/right - editor-render screen, e - # move the cursor - assume-console [ - left-click 3, 1 - ] - editor-event-loop screen, console, e - # undo - assume-console [ - press ctrl-z - ] - run [ - editor-event-loop screen, console, e - ] - # click undone - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - memory-should-contain [ - 3 <- 1 - 4 <- 0 - ] - # cursor should be in the right place - assume-console [ - type [1] - ] - run [ - editor-event-loop screen, console, e - ] - screen-should-contain [ - . . - .1abc . - .def . - .ghi . - .┈┈┈┈┈┈┈┈┈┈. - ] -] - -after [ - cursor-row-before:num <- get *editor, cursor-row:offset - cursor-column-before:num <- get *editor, cursor-column:offset - top-before:&:duplex-list:char <- get *editor, top-of-screen:offset -] -before [ - top-after:&:duplex-list:char <- get *editor, top-of-screen:offset - cursor-row:num <- get *editor, cursor-row:offset - cursor-column:num <- get *editor, cursor-column:offset - { - break-unless undo-coalesce-tag - # if previous operation was also a move, and also had the same coalesce - # tag, coalesce with it - undo:&:list:&:operation <- get *editor, undo:offset - break-unless undo - op:&:operation <- first undo - move:move-operation, is-move?:bool <- maybe-convert *op, move:variant - break-unless is-move? - previous-coalesce-tag:num <- get move, tag:offset - coalesce?:bool <- equal undo-coalesce-tag, previous-coalesce-tag - break-unless coalesce? - move <- put move, after-row:offset, cursor-row - move <- put move, after-column:offset, cursor-column - move <- put move, after-top-of-screen:offset, top-after - *op <- merge 1/move-operation, move - break +done-adding-move-operation - } - op:&:operation <- new operation:type - *op <- merge 1/move-operation, cursor-row-before, cursor-column-before, top-before, cursor-row/after, cursor-column/after, top-after, undo-coalesce-tag - editor <- add-operation editor, op - +done-adding-move-operation -] - -after [ - { - move:move-operation, is-move?:bool <- maybe-convert *op, move:variant - break-unless is-move? - # assert cursor-row/cursor-column/top-of-screen match after-row/after-column/after-top-of-screen - cursor-row <- get move, before-row:offset - *editor <- put *editor, cursor-row:offset, cursor-row - cursor-column <- get move, before-column:offset - *editor <- put *editor, cursor-column:offset, cursor-column - top:&:duplex-list:char <- get move, before-top-of-screen:offset - *editor <- put *editor, top-of-screen:offset, top - } -] - -scenario editor-can-undo-left-arrow [ - local-scope - # create an editor with some text - assume-screen 10/width, 5/height - contents:text <- new [abc -def -ghi] - e:&:editor <- new-editor contents, 0/left, 10/right - editor-render screen, e - # move the cursor - assume-console [ - left-click 3, 1 - press left-arrow - ] - editor-event-loop screen, console, e - # undo - assume-console [ - press ctrl-z - ] - run [ - editor-event-loop screen, console, e - ] - # cursor moves back - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - memory-should-contain [ - 3 <- 3 - 4 <- 1 - ] - # cursor should be in the right place - assume-console [ - type [1] - ] - run [ - editor-event-loop screen, console, e - ] - screen-should-contain [ - . . - .abc . - .def . - .g1hi . - .┈┈┈┈┈┈┈┈┈┈. - ] -] - -scenario editor-can-undo-up-arrow [ - local-scope - # create an editor with some text - assume-screen 10/width, 5/height - contents:text <- new [abc -def -ghi] - e:&:editor <- new-editor contents, 0/left, 10/right - editor-render screen, e - # move the cursor - assume-console [ - left-click 3, 1 - press up-arrow - ] - editor-event-loop screen, console, e - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - memory-should-contain [ - 3 <- 2 - 4 <- 1 - ] - # undo - assume-console [ - press ctrl-z - ] - run [ - editor-event-loop screen, console, e - ] - # cursor moves back - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - memory-should-contain [ - 3 <- 3 - 4 <- 1 - ] - # cursor should be in the right place - assume-console [ - type [1] - ] - run [ - editor-event-loop screen, console, e - ] - screen-should-contain [ - . . - .abc . - .def . - .g1hi . - .┈┈┈┈┈┈┈┈┈┈. - ] -] - -scenario editor-can-undo-down-arrow [ - local-scope - # create an editor with some text - assume-screen 10/width, 5/height - contents:text <- new [abc -def -ghi] - e:&:editor <- new-editor contents, 0/left, 10/right - editor-render screen, e - # move the cursor - assume-console [ - left-click 2, 1 - press down-arrow - ] - editor-event-loop screen, console, e - # undo - assume-console [ - press ctrl-z - ] - run [ - editor-event-loop screen, console, e - ] - # cursor moves back - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - memory-should-contain [ - 3 <- 2 - 4 <- 1 - ] - # cursor should be in the right place - assume-console [ - type [1] - ] - run [ - editor-event-loop screen, console, e - ] - screen-should-contain [ - . . - .abc . - .d1ef . - .ghi . - .┈┈┈┈┈┈┈┈┈┈. - ] -] - -scenario editor-can-undo-ctrl-a [ - local-scope - # create an editor with some text - assume-screen 10/width, 5/height - contents:text <- new [abc -def -ghi] - e:&:editor <- new-editor contents, 0/left, 10/right - editor-render screen, e - # move the cursor, then to start of line - assume-console [ - left-click 2, 1 - press ctrl-a - ] - editor-event-loop screen, console, e - # undo - assume-console [ - press ctrl-z - ] - run [ - editor-event-loop screen, console, e - ] - # cursor moves back - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - memory-should-contain [ - 3 <- 2 - 4 <- 1 - ] - # cursor should be in the right place - assume-console [ - type [1] - ] - run [ - editor-event-loop screen, console, e - ] - screen-should-contain [ - . . - .abc . - .d1ef . - .ghi . - .┈┈┈┈┈┈┈┈┈┈. - ] -] - -scenario editor-can-undo-home [ - local-scope - # create an editor with some text - assume-screen 10/width, 5/height - contents:text <- new [abc -def -ghi] - e:&:editor <- new-editor contents, 0/left, 10/right - editor-render screen, e - # move the cursor, then to start of line - assume-console [ - left-click 2, 1 - press home - ] - editor-event-loop screen, console, e - # undo - assume-console [ - press ctrl-z - ] - run [ - editor-event-loop screen, console, e - ] - # cursor moves back - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - memory-should-contain [ - 3 <- 2 - 4 <- 1 - ] - # cursor should be in the right place - assume-console [ - type [1] - ] - run [ - editor-event-loop screen, console, e - ] - screen-should-contain [ - . . - .abc . - .d1ef . - .ghi . - .┈┈┈┈┈┈┈┈┈┈. - ] -] - -scenario editor-can-undo-ctrl-e [ - local-scope - # create an editor with some text - assume-screen 10/width, 5/height - contents:text <- new [abc -def -ghi] - e:&:editor <- new-editor contents, 0/left, 10/right - editor-render screen, e - # move the cursor, then to start of line - assume-console [ - left-click 2, 1 - press ctrl-e - ] - editor-event-loop screen, console, e - # undo - assume-console [ - press ctrl-z - ] - run [ - editor-event-loop screen, console, e - ] - # cursor moves back - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - memory-should-contain [ - 3 <- 2 - 4 <- 1 - ] - # cursor should be in the right place - assume-console [ - type [1] - ] - run [ - editor-event-loop screen, console, e - ] - screen-should-contain [ - . . - .abc . - .d1ef . - .ghi . - .┈┈┈┈┈┈┈┈┈┈. - ] -] - -scenario editor-can-undo-end [ - local-scope - # create an editor with some text - assume-screen 10/width, 5/height - contents:text <- new [abc -def -ghi] - e:&:editor <- new-editor contents, 0/left, 10/right - editor-render screen, e - # move the cursor, then to start of line - assume-console [ - left-click 2, 1 - press end - ] - editor-event-loop screen, console, e - # undo - assume-console [ - press ctrl-z - ] - run [ - editor-event-loop screen, console, e - ] - # cursor moves back - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - memory-should-contain [ - 3 <- 2 - 4 <- 1 - ] - # cursor should be in the right place - assume-console [ - type [1] - ] - run [ - editor-event-loop screen, console, e - ] - screen-should-contain [ - . . - .abc . - .d1ef . - .ghi . - .┈┈┈┈┈┈┈┈┈┈. - ] -] - -scenario editor-can-undo-multiple-arrows-in-the-same-direction [ - local-scope - # create an editor with some text - assume-screen 10/width, 5/height - contents:text <- new [abc -def -ghi] - e:&:editor <- new-editor contents, 0/left, 10/right - editor-render screen, e - # move the cursor - assume-console [ - left-click 2, 1 - press right-arrow - press right-arrow - press up-arrow - ] - editor-event-loop screen, console, e - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - memory-should-contain [ - 3 <- 1 - 4 <- 3 - ] - # undo - assume-console [ - press ctrl-z - ] - run [ - editor-event-loop screen, console, e - ] - # up-arrow is undone - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - memory-should-contain [ - 3 <- 2 - 4 <- 3 - ] - # undo again - assume-console [ - press ctrl-z - ] - run [ - editor-event-loop screen, console, e - ] - # both right-arrows are undone - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - memory-should-contain [ - 3 <- 2 - 4 <- 1 - ] -] - -# redo cursor movement - -scenario editor-redo-touch [ - local-scope - # create an editor with some text, click on a character, undo - assume-screen 10/width, 5/height - contents:text <- new [abc -def -ghi] - e:&:editor <- new-editor contents, 0/left, 10/right - editor-render screen, e - assume-console [ - left-click 3, 1 - press ctrl-z - ] - editor-event-loop screen, console, e - # redo - assume-console [ - press ctrl-y - ] - run [ - editor-event-loop screen, console, e - ] - # cursor moves to left-click - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - memory-should-contain [ - 3 <- 3 - 4 <- 1 - ] - # cursor should be in the right place - assume-console [ - type [1] - ] - run [ - editor-event-loop screen, console, e - ] - screen-should-contain [ - . . - .abc . - .def . - .g1hi . - .┈┈┈┈┈┈┈┈┈┈. - ] -] - -after [ - { - move:move-operation, is-move?:bool <- maybe-convert *op, move:variant - break-unless is-move? - # assert cursor-row/cursor-column/top-of-screen match after-row/after-column/after-top-of-screen - cursor-row <- get move, after-row:offset - *editor <- put *editor, cursor-row:offset, cursor-row - cursor-column <- get move, after-column:offset - *editor <- put *editor, cursor-column:offset, cursor-column - top:&:duplex-list:char <- get move, after-top-of-screen:offset - *editor <- put *editor, top-of-screen:offset, top - } -] - -scenario editor-separates-undo-insert-from-undo-cursor-move [ - local-scope - # create an editor, type some text, move the cursor, type some more text - assume-screen 10/width, 5/height - e:&:editor <- new-editor [], 0/left, 10/right - editor-render screen, e - assume-console [ - type [abc] - left-click 1, 1 - type [d] - ] - editor-event-loop screen, console, e - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - screen-should-contain [ - . . - .adbc . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - memory-should-contain [ - 3 <- 1 - 4 <- 2 - ] - # undo - assume-console [ - press ctrl-z - ] - run [ - editor-event-loop screen, console, e - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - ] - # last letter typed is deleted - screen-should-contain [ - . . - .abc . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - memory-should-contain [ - 3 <- 1 - 4 <- 1 - ] - # undo again - assume-console [ - press ctrl-z - ] - run [ - editor-event-loop screen, console, e - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - ] - # no change to screen; cursor moves - screen-should-contain [ - . . - .abc . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - memory-should-contain [ - 3 <- 1 - 4 <- 3 - ] - # undo again - assume-console [ - press ctrl-z - ] - run [ - editor-event-loop screen, console, e - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - ] - # screen empty - screen-should-contain [ - . . - . . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - memory-should-contain [ - 3 <- 1 - 4 <- 0 - ] - # redo - assume-console [ - press ctrl-y - ] - run [ - editor-event-loop screen, console, e - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - ] - # first insert - screen-should-contain [ - . . - .abc . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - memory-should-contain [ - 3 <- 1 - 4 <- 3 - ] - # redo again - assume-console [ - press ctrl-y - ] - run [ - editor-event-loop screen, console, e - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - ] - # cursor moves - screen-should-contain [ - . . - .abc . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - # cursor moves - memory-should-contain [ - 3 <- 1 - 4 <- 1 - ] - # redo again - assume-console [ - press ctrl-y - ] - run [ - editor-event-loop screen, console, e - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - ] - # second insert - screen-should-contain [ - . . - .adbc . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - memory-should-contain [ - 3 <- 1 - 4 <- 2 - ] -] - -# undo backspace - -scenario editor-can-undo-and-redo-backspace [ - local-scope - # create an editor - assume-screen 10/width, 5/height - e:&:editor <- new-editor [], 0/left, 10/right - editor-render screen, e - # insert some text and hit backspace - assume-console [ - type [abc] - press backspace - press backspace - ] - editor-event-loop screen, console, e - screen-should-contain [ - . . - .a . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - memory-should-contain [ - 3 <- 1 - 4 <- 1 - ] - # undo - assume-console [ - press ctrl-z - ] - run [ - editor-event-loop screen, console, e - ] - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - memory-should-contain [ - 3 <- 1 - 4 <- 3 - ] - screen-should-contain [ - . . - .abc . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - # redo - assume-console [ - press ctrl-y - ] - run [ - editor-event-loop screen, console, e - ] - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - memory-should-contain [ - 3 <- 1 - 4 <- 1 - ] - screen-should-contain [ - . . - .a . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] -] - -# save operation to undo -after [ - top-before:&:duplex-list:char <- get *editor, top-of-screen:offset -] -before [ - { - break-unless backspaced-cell # backspace failed; don't add an undo operation - top-after:&:duplex-list:char <- get *editor, top-of-screen:offset - cursor-row:num <- get *editor, cursor-row:offset - cursor-column:num <- get *editor, cursor-row:offset - before-cursor:&:duplex-list:char <- get *editor, before-cursor:offset - undo:&:list:&:operation <- get *editor, undo:offset - { - # if previous operation was an insert, coalesce this operation with it - break-unless undo - op:&:operation <- first undo - deletion:delete-operation, is-delete?:bool <- maybe-convert *op, delete:variant - break-unless is-delete? - previous-coalesce-tag:num <- get deletion, tag:offset - coalesce?:bool <- equal previous-coalesce-tag, 1/coalesce-backspace - break-unless coalesce? - deletion <- put deletion, delete-from:offset, before-cursor - backspaced-so-far:&:duplex-list:char <- get deletion, deleted-text:offset - splice backspaced-cell, backspaced-so-far - deletion <- put deletion, deleted-text:offset, backspaced-cell - deletion <- put deletion, after-row:offset, cursor-row - deletion <- put deletion, after-column:offset, cursor-column - deletion <- put deletion, after-top-of-screen:offset, top-after - *op <- merge 2/delete-operation, deletion - break +done-adding-backspace-operation - } - # if not, create a new operation - op:&:operation <- new operation:type - deleted-until:&:duplex-list:char <- next before-cursor - *op <- merge 2/delete-operation, save-row/before, save-column/before, top-before, cursor-row/after, cursor-column/after, top-after, backspaced-cell/deleted, before-cursor/delete-from, deleted-until, 1/coalesce-backspace - editor <- add-operation editor, op - +done-adding-backspace-operation - } -] - -after [ - { - deletion:delete-operation, is-delete?:bool <- maybe-convert *op, delete:variant - break-unless is-delete? - anchor:&:duplex-list:char <- get deletion, delete-from:offset - break-unless anchor - deleted:&:duplex-list:char <- get deletion, deleted-text:offset - old-cursor:&:duplex-list:char <- last deleted - splice 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 - *editor <- put *editor, cursor-row:offset, cursor-row - cursor-column <- get deletion, before-column:offset - *editor <- put *editor, cursor-column:offset, cursor-column - top:&:duplex-list:char <- get deletion, before-top-of-screen:offset - *editor <- put *editor, top-of-screen:offset, top - } -] - -after [ - { - deletion:delete-operation, is-delete?:bool <- maybe-convert *op, delete:variant - break-unless is-delete? - start:&:duplex-list:char <- get deletion, delete-from:offset - end:&:duplex-list:char <- get deletion, delete-until:offset - data:&:duplex-list:char <- get *editor, data:offset - remove-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 - *editor <- put *editor, cursor-row:offset, cursor-row - cursor-column <- get deletion, after-column:offset - *editor <- put *editor, cursor-column:offset, cursor-column - top:&:duplex-list:char <- get deletion, before-top-of-screen:offset - *editor <- put *editor, top-of-screen:offset, top - } -] - -# undo delete - -scenario editor-can-undo-and-redo-delete [ - local-scope - # create an editor - assume-screen 10/width, 5/height - e:&:editor <- new-editor [], 0/left, 10/right - editor-render screen, e - # insert some text and hit delete and backspace a few times - assume-console [ - type [abcdef] - left-click 1, 2 - press delete - press backspace - press delete - press delete - ] - editor-event-loop screen, console, e - screen-should-contain [ - . . - .af . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - memory-should-contain [ - 3 <- 1 - 4 <- 1 - ] - # undo deletes - assume-console [ - press ctrl-z - ] - run [ - editor-event-loop screen, console, e - ] - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - memory-should-contain [ - 3 <- 1 - 4 <- 1 - ] - screen-should-contain [ - . . - .adef . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - # undo backspace - assume-console [ - press ctrl-z - ] - run [ - editor-event-loop screen, console, e - ] - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - memory-should-contain [ - 3 <- 1 - 4 <- 2 - ] - screen-should-contain [ - . . - .abdef . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - # undo first delete - assume-console [ - press ctrl-z - ] - run [ - editor-event-loop screen, console, e - ] - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - memory-should-contain [ - 3 <- 1 - 4 <- 2 - ] - screen-should-contain [ - . . - .abcdef . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - # redo first delete - assume-console [ - press ctrl-y - ] - run [ - editor-event-loop screen, console, e - ] - # first line inserted - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - memory-should-contain [ - 3 <- 1 - 4 <- 2 - ] - screen-should-contain [ - . . - .abdef . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - # redo backspace - assume-console [ - press ctrl-y - ] - run [ - editor-event-loop screen, console, e - ] - # first line inserted - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - memory-should-contain [ - 3 <- 1 - 4 <- 1 - ] - screen-should-contain [ - . . - .adef . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - # redo deletes - assume-console [ - press ctrl-y - ] - run [ - editor-event-loop screen, console, e - ] - # first line inserted - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - memory-should-contain [ - 3 <- 1 - 4 <- 1 - ] - screen-should-contain [ - . . - .af . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] -] - -after [ - top-before:&:duplex-list:char <- get *editor, top-of-screen:offset -] -before [ - { - break-unless deleted-cell # delete failed; don't add an undo operation - top-after:&:duplex-list:char <- get *editor, top-of-screen:offset - cursor-row:num <- get *editor, cursor-row:offset - cursor-column:num <- get *editor, cursor-column:offset - before-cursor:&:duplex-list:char <- get *editor, before-cursor:offset - undo:&:list:&:operation <- get *editor, undo:offset - { - # if previous operation was an insert, coalesce this operation with it - break-unless undo - op:&:operation <- first undo - deletion:delete-operation, is-delete?:bool <- maybe-convert *op, delete:variant - break-unless is-delete? - previous-coalesce-tag:num <- get deletion, tag:offset - coalesce?:bool <- equal previous-coalesce-tag, 2/coalesce-delete - break-unless coalesce? - delete-until:&:duplex-list:char <- next before-cursor - deletion <- put deletion, delete-until:offset, delete-until - deleted-so-far:&:duplex-list:char <- get deletion, deleted-text:offset - deleted-so-far <- append deleted-so-far, deleted-cell - deletion <- put deletion, deleted-text:offset, deleted-so-far - deletion <- put deletion, after-row:offset, cursor-row - deletion <- put deletion, after-column:offset, cursor-column - deletion <- put deletion, after-top-of-screen:offset, top-after - *op <- merge 2/delete-operation, deletion - break +done-adding-delete-operation - } - # if not, create a new operation - op:&:operation <- new operation:type - deleted-until:&:duplex-list:char <- next 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 - } -] - -# undo ctrl-k - -scenario editor-can-undo-and-redo-ctrl-k [ - local-scope - # create an editor - assume-screen 10/width, 5/height - contents:text <- new [abc -def] - e:&:editor <- new-editor contents, 0/left, 10/right - editor-render screen, e - # insert some text and hit delete and backspace a few times - assume-console [ - left-click 1, 1 - press ctrl-k - ] - editor-event-loop screen, console, e - screen-should-contain [ - . . - .a . - .def . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - memory-should-contain [ - 3 <- 1 - 4 <- 1 - ] - # undo - assume-console [ - press ctrl-z - ] - run [ - editor-event-loop screen, console, e - ] - screen-should-contain [ - . . - .abc . - .def . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - memory-should-contain [ - 3 <- 1 - 4 <- 1 - ] - # redo - assume-console [ - press ctrl-y - ] - run [ - editor-event-loop screen, console, e - ] - # first line inserted - screen-should-contain [ - . . - .a . - .def . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - memory-should-contain [ - 3 <- 1 - 4 <- 1 - ] - # cursor should be in the right place - assume-console [ - type [1] - ] - run [ - editor-event-loop screen, console, e - ] - screen-should-contain [ - . . - .a1 . - .def . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] -] - -after [ - top-before:&:duplex-list:char <- get *editor, top-of-screen:offset -] -before [ - { - break-unless deleted-cells # delete failed; don't add an undo operation - top-after:&:duplex-list:char <- get *editor, top-of-screen:offset - cursor-row:num <- get *editor, cursor-row:offset - cursor-column:num <- get *editor, cursor-column:offset - deleted-until:&:duplex-list:char <- next before-cursor - op:&:operation <- new operation:type - *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 - } -] - -# undo ctrl-u - -scenario editor-can-undo-and-redo-ctrl-u [ - local-scope - # create an editor - assume-screen 10/width, 5/height - contents:text <- new [abc -def] - e:&:editor <- new-editor contents, 0/left, 10/right - editor-render screen, e - # insert some text and hit delete and backspace a few times - assume-console [ - left-click 1, 2 - press ctrl-u - ] - editor-event-loop screen, console, e - screen-should-contain [ - . . - .c . - .def . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - memory-should-contain [ - 3 <- 1 - 4 <- 0 - ] - # undo - assume-console [ - press ctrl-z - ] - run [ - editor-event-loop screen, console, e - ] - screen-should-contain [ - . . - .abc . - .def . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - memory-should-contain [ - 3 <- 1 - 4 <- 2 - ] - # redo - assume-console [ - press ctrl-y - ] - run [ - editor-event-loop screen, console, e - ] - # first line inserted - screen-should-contain [ - . . - .c . - .def . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] - 3:num/raw <- get *e, cursor-row:offset - 4:num/raw <- get *e, cursor-column:offset - memory-should-contain [ - 3 <- 1 - 4 <- 0 - ] - # cursor should be in the right place - assume-console [ - type [1] - ] - run [ - editor-event-loop screen, console, e - ] - screen-should-contain [ - . . - .1c . - .def . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] -] - -after [ - top-before:&:duplex-list:char <- get *editor, top-of-screen:offset -] -before [ - { - break-unless deleted-cells # delete failed; don't add an undo operation - top-after:&:duplex-list:char <- get *editor, top-of-screen:offset - op:&:operation <- new operation:type - before-cursor:&:duplex-list:char <- get *editor, before-cursor:offset - deleted-until:&:duplex-list:char <- next before-cursor - cursor-row:num <- get *editor, cursor-row:offset - cursor-column:num <- get *editor, cursor-column:offset - *op <- merge 2/delete-operation, save-row/before, save-column/before, top-before, cursor-row/after, cursor-column/after, top-after, deleted-cells/deleted, before-cursor/delete-from, deleted-until, 0/never-coalesce - editor <- add-operation editor, op - +done-adding-delete-operation - } -] - -scenario editor-can-undo-and-redo-ctrl-u-2 [ - local-scope - # create an editor - assume-screen 10/width, 5/height - e:&:editor <- new-editor [], 0/left, 10/right - editor-render screen, e - # insert some text and hit delete and backspace a few times - assume-console [ - type [abc] - press ctrl-u - press ctrl-z - ] - editor-event-loop screen, console, e - screen-should-contain [ - . . - .abc . - .┈┈┈┈┈┈┈┈┈┈. - . . - ] -] diff --git a/archive/2.vm/sandbox/Readme.md b/archive/2.vm/sandbox/Readme.md deleted file mode 100644 index e8acd78b..00000000 --- a/archive/2.vm/sandbox/Readme.md +++ /dev/null @@ -1,33 +0,0 @@ -Variant of [the Mu programming environment](../edit) that runs just the sandbox. - -Suitable for people who want to run their favorite terminal-based editor with -Mu. Just run editor and sandbox inside split panes atop tmux. For example, -here's Mu running alongside vim: - -tmux+vim example - -To set this up: - - a) copy the lines in tmux.conf into `$HOME/.tmux.conf`. - - b) copy the file `mu_run` somewhere in your `$PATH`. - -Now when you start tmux, split it into two vertical panes, run `./mu sandbox` -on the right pane and your editor on the left. You should be able to hit F4 in -either side to run the sandbox. - -Known issues: you have to explicitly save inside your editor before hitting -F4, unlike with `./mu edit`. - ---- - -Appendix: keyboard shortcuts - - _moving_ - - `ctrl-a` or `home`: move cursor to start of line - - `ctrl-e` or `end`: move cursor to end of line - - _modifying text_ - - `ctrl-k`: delete text from cursor to end of line - - `ctrl-u`: delete text from start of line until just before cursor - - `ctrl-/`: comment/uncomment current line (using a special leader to ignore real comments https://www.reddit.com/r/vim/comments/4ootmz/_/d4ehmql) diff --git a/archive/2.vm/sandbox/mu_run b/archive/2.vm/sandbox/mu_run deleted file mode 100755 index b96cfd1c..00000000 --- a/archive/2.vm/sandbox/mu_run +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/zsh -# Little bit of glue to support running Mu from Vim over tmux. - -export ALREADY_FOCUSED=0 -tmux list-panes |grep "^1.*active" -q && export ALREADY_FOCUSED=1 -if [[ $ALREADY_FOCUSED -eq 0 ]] -then - tmux select-pane -t 1 -fi - -tmux send-keys 'F4' - -if [[ $ALREADY_FOCUSED -eq 0 ]] -then - tmux last-pane -fi diff --git a/archive/2.vm/sandbox/tmux.conf b/archive/2.vm/sandbox/tmux.conf deleted file mode 100644 index 7816b1eb..00000000 --- a/archive/2.vm/sandbox/tmux.conf +++ /dev/null @@ -1,3 +0,0 @@ -# Hotkey for running Mu over tmux -# Assumes exactly two panes, with vim running on the left side and `./mu sandbox` running on the right side. -bind-key -n F4 run mu_run -- cgit 1.4.1-2-gfad0