about summary refs log tree commit diff stats
path: root/sandbox/003-shortcuts.mu
diff options
context:
space:
mode:
authorKartik K. Agaram <vc@akkartik.com>2016-05-11 18:00:11 -0700
committerKartik K. Agaram <vc@akkartik.com>2016-05-11 18:00:11 -0700
commit1156971774b307bec29fab34a523eb39a7904174 (patch)
tree1b7cc1829fa5f491ac9bbfc58d757bc69aed68fc /sandbox/003-shortcuts.mu
parentbf3c3d30246c4e955f418f7b34cd29b1c2e8bec4 (diff)
downloadmu-1156971774b307bec29fab34a523eb39a7904174.tar.gz
2953 - use pgup/pgdn to scroll through sandboxes
In the process I've also simplified the sandbox/ app. Since it's
impossible for sandbox editors to span multiple pages, we can drop all
scroll support altogether.
Diffstat (limited to 'sandbox/003-shortcuts.mu')
-rw-r--r--sandbox/003-shortcuts.mu1344
1 files changed, 35 insertions, 1309 deletions
diff --git a/sandbox/003-shortcuts.mu b/sandbox/003-shortcuts.mu
index 40956cb3..46ab636e 100644
--- a/sandbox/003-shortcuts.mu
+++ b/sandbox/003-shortcuts.mu
@@ -89,21 +89,19 @@ def delete-before-cursor editor:address:editor-data, screen:address:screen -> ed
   prev:address:duplex-list:character <- prev before-cursor
   go-render?, backspaced-cell <- copy 0/no-more-render, 0/nothing-deleted
   return-unless prev
+  go-render? <- copy 1/true
   trace 10, [app], [delete-before-cursor]
   original-row:number <- get *editor, cursor-row:offset
-  editor, scroll?:boolean <- move-cursor-coordinates-left editor
+  editor <- move-cursor-coordinates-left editor
   backspaced-cell:address:duplex-list:character <- 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
-  go-render? <- copy 1/true
-  return-if scroll?
   screen-width:number <- screen-width screen
   cursor-row:number <- get *editor, cursor-row:offset
   cursor-column:number <- get *editor, cursor-column:offset
   # did we just backspace over a newline?
   same-row?:boolean <- equal cursor-row, original-row
-  go-render? <- copy 1/true
   return-unless same-row?
   left:number <- get *editor, left:offset
   right:number <- get *editor, right:offset
@@ -113,7 +111,6 @@ def delete-before-cursor editor:address:editor-data, screen:address:screen -> ed
   {
     # hit right margin? give up and let caller render
     at-right?:boolean <- greater-or-equal curr-column, right
-    go-render? <- copy 1/true
     return-if at-right?
     break-unless curr
     # newline? done.
@@ -131,7 +128,7 @@ def delete-before-cursor editor:address:editor-data, screen:address:screen -> ed
   go-render? <- copy 0/false
 ]
 
-def move-cursor-coordinates-left editor:address:editor-data -> editor:address:editor-data, go-render?:boolean [
+def move-cursor-coordinates-left editor:address:editor-data -> editor:address:editor-data [
   local-scope
   load-ingredients
   before-cursor:address:duplex-list:character <- get *editor, before-cursor:offset
@@ -145,12 +142,10 @@ def move-cursor-coordinates-left editor:address:editor-data -> editor:address:ed
     trace 10, [app], [decrementing cursor column]
     cursor-column <- subtract cursor-column, 1
     *editor <- put *editor, cursor-column:offset, cursor-column
-    go-render? <- copy 0/false
     return
   }
   # if at left margin, we must move to previous row:
   top-of-screen?:boolean <- equal cursor-row, 1  # exclude menu bar
-  go-render?:boolean <- copy 0/false
   {
     break-if top-of-screen?
     cursor-row <- subtract cursor-row, 1
@@ -158,8 +153,7 @@ def move-cursor-coordinates-left editor:address:editor-data -> editor:address:ed
   }
   {
     break-unless top-of-screen?
-    <scroll-up>
-    go-render? <- copy 1/true
+    # no scroll, so do nothing
   }
   {
     # case 1: if previous character was newline, figure out how long the previous line is
@@ -452,7 +446,6 @@ def move-cursor-coordinates-right editor:address:editor-data, screen-height:numb
     below-screen?:boolean <- greater-or-equal cursor-row, screen-height  # must be equal
     go-render? <- copy 0/false
     return-unless below-screen?
-    <scroll-down>
     cursor-row <- subtract cursor-row, 1  # bring back into screen range
     *editor <- put *editor, cursor-row:offset, cursor-row
     go-render? <- copy 1/true
@@ -476,7 +469,6 @@ def move-cursor-coordinates-right editor:address:editor-data, screen-height:numb
     *editor <- put *editor, cursor-column:offset, cursor-column
     below-screen?:boolean <- greater-or-equal cursor-row, screen-height  # must be equal
     return-unless below-screen?, editor/same-as-ingredient:0, 0/no-more-render
-    <scroll-down>
     cursor-row <- subtract cursor-row, 1  # bring back into screen range
     *editor <- put *editor, cursor-row:offset, cursor-row
     go-render? <- copy 1/true
@@ -707,7 +699,7 @@ after <handle-special-key> [
     go-render? <- copy 0/false
     return-unless prev
     <move-cursor-begin>
-    editor, go-render? <- move-cursor-coordinates-left editor
+    editor <- move-cursor-coordinates-left editor
     before-cursor <- copy prev
     *editor <- put *editor, before-cursor:offset, before-cursor
     undo-coalesce-tag:number <- copy 1/left-arrow
@@ -1033,10 +1025,9 @@ def move-to-previous-line editor:address:editor-data -> editor:address:editor-da
     return
   }
   {
-    # if cursor already at top, scroll up
+    # if cursor already at top, do nothing
     break-unless already-at-top?
-    <scroll-up>
-    go-render? <- copy 1/true
+    go-render? <- copy 0/true
     return
   }
 ]
@@ -1196,14 +1187,14 @@ after <handle-special-key> [
     move-to-next-line?:boolean <- equal k, 65516/down-arrow
     break-unless move-to-next-line?
     <move-cursor-begin>
-    editor, go-render? <- move-to-next-line editor, screen-height
+    editor <- move-to-next-line editor, screen-height
     undo-coalesce-tag:number <- copy 4/down-arrow
     <move-cursor-end>
     return
   }
 ]
 
-def move-to-next-line editor:address:editor-data, screen-height:number -> editor:address:editor-data, go-render?:boolean [
+def move-to-next-line editor:address:editor-data, screen-height:number -> editor:address:editor-data [
   local-scope
   load-ingredients
   cursor-row:number <- get *editor, cursor-row:offset
@@ -1213,50 +1204,36 @@ def move-to-next-line editor:address:editor-data, screen-height:number -> editor
   right:number <- get *editor, right:offset
   last-line:number <- subtract screen-height, 1
   already-at-bottom?:boolean <- greater-or-equal cursor-row, last-line
+  # if cursor not at bottom
+  return-if already-at-bottom?
+  # scan to start of next line, then to right column or until end of line
+  max:number <- subtract right, left
+  next-line:address:duplex-list:character <- before-start-of-next-line before-cursor, max
+  # already at end of buffer? do nothing
+  no-motion?:boolean <- equal next-line, before-cursor
+  return-if no-motion?
+  cursor-row <- add cursor-row, 1
+  *editor <- put *editor, cursor-row:offset, cursor-row
+  before-cursor <- copy next-line
+  *editor <- put *editor, before-cursor:offset, before-cursor
+  target-column:number <- copy cursor-column
+  cursor-column <- copy left
+  *editor <- put *editor, cursor-column:offset, cursor-column
   {
-    # if cursor not at bottom, move it
-    break-if already-at-bottom?
-    # scan to start of next line, then to right column or until end of line
-    max:number <- subtract right, left
-    next-line:address:duplex-list:character <- before-start-of-next-line before-cursor, max
-    {
-      # already at end of buffer? try to scroll up (so we can see more
-      # warnings or sandboxes below)
-      no-motion?:boolean <- equal next-line, before-cursor
-      break-unless no-motion?
-      scroll?:boolean <- greater-than cursor-row, 1
-      break-if scroll?, +try-to-scroll:label
-      go-render? <- copy 0/false
-      return
-    }
-    cursor-row <- add cursor-row, 1
-    *editor <- put *editor, cursor-row:offset, cursor-row
-    before-cursor <- copy next-line
+    done?:boolean <- greater-or-equal cursor-column, target-column
+    break-if done?
+    curr:address:duplex-list:character <- next before-cursor
+    break-unless curr
+    currc:character <- get *curr, value:offset
+    at-newline?:boolean <- equal currc, 10/newline
+    break-if at-newline?
+    #
+    before-cursor <- copy curr
     *editor <- put *editor, before-cursor:offset, before-cursor
-    target-column:number <- copy cursor-column
-    cursor-column <- copy left
+    cursor-column <- add cursor-column, 1
     *editor <- put *editor, cursor-column:offset, cursor-column
-    {
-      done?:boolean <- greater-or-equal cursor-column, target-column
-      break-if done?
-      curr:address:duplex-list:character <- next before-cursor
-      break-unless curr
-      currc:character <- get *curr, value:offset
-      at-newline?:boolean <- 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
-    }
-    go-render? <- copy 0/false
-    return
+    loop
   }
-  +try-to-scroll
-  <scroll-down>
-  go-render? <- copy 1/true
 ]
 
 scenario editor-adjusts-column-at-next-line [
@@ -1921,54 +1898,6 @@ scenario editor-deletes-to-end-of-line-with-ctrl-k-6 [
   ]
 ]
 
-# cursor-down can scroll if necessary
-
-scenario editor-can-scroll-down-using-arrow-keys [
-  # screen has 1 line for menu + 3 lines
-  assume-screen 10/width, 4/height
-  # initialize editor with >3 lines
-  1:address:array:character <- new [a
-b
-c
-d]
-  2:address:editor-data <- new-editor 1:address:array:character, screen:address:screen, 0/left, 10/right
-  screen-should-contain [
-    .          .
-    .a         .
-    .b         .
-    .c         .
-  ]
-  # position cursor at last line, then try to move further down
-  assume-console [
-    left-click 3, 0
-    press down-arrow
-  ]
-  run [
-    editor-event-loop screen:address:screen, console:address:console, 2:address:editor-data
-  ]
-  # screen slides by one line
-  screen-should-contain [
-    .          .
-    .b         .
-    .c         .
-    .d         .
-  ]
-]
-
-after <scroll-down> [
-  trace 10, [app], [scroll down]
-  top-of-screen:address:duplex-list:character <- get *editor, top-of-screen:offset
-  left:number <- get *editor, left:offset
-  right:number <- get *editor, right:offset
-  max:number <- subtract right, left
-  old-top:address:duplex-list:character <- copy top-of-screen
-  top-of-screen <- before-start-of-next-line top-of-screen, max
-  *editor <- put *editor, top-of-screen:offset, top-of-screen
-  no-movement?:boolean <- equal old-top, top-of-screen
-  go-render? <- copy 0/false
-  return-if no-movement?
-]
-
 # takes a pointer into the doubly-linked list, scans ahead at most 'max'
 # positions until the next newline
 # beware: never return null pointer.
@@ -2000,343 +1929,6 @@ def before-start-of-next-line original:address:duplex-list:character, max:number
   return curr
 ]
 
-scenario editor-scrolls-down-past-wrapped-line-using-arrow-keys [
-  # screen has 1 line for menu + 3 lines
-  assume-screen 10/width, 4/height
-  # initialize editor with a long, wrapped line and more than a screen of
-  # other lines
-  1:address:array:character <- new [abcdef
-g
-h
-i]
-  2:address:editor-data <- new-editor 1:address:array:character, screen:address:screen, 0/left, 5/right
-  screen-should-contain [
-    .          .
-    .abcd↩     .
-    .ef        .
-    .g         .
-  ]
-  # position cursor at last line, then try to move further down
-  assume-console [
-    left-click 3, 0
-    press down-arrow
-  ]
-  run [
-    editor-event-loop screen:address:screen, console:address:console, 2:address:editor-data
-  ]
-  # screen shows partial wrapped line
-  screen-should-contain [
-    .          .
-    .ef        .
-    .g         .
-    .h         .
-  ]
-]
-
-scenario editor-scrolls-down-past-wrapped-line-using-arrow-keys-2 [
-  # screen has 1 line for menu + 3 lines
-  assume-screen 10/width, 4/height
-  # editor starts with a long line wrapping twice
-  1:address:array:character <- new [abcdefghij
-k
-l
-m]
-  2:address:editor-data <- new-editor 1:address:array:character, screen:address:screen, 0/left, 5/right
-  # position cursor at last line, then try to move further down
-  assume-console [
-    left-click 3, 0
-    press down-arrow
-  ]
-  run [
-    editor-event-loop screen:address:screen, console:address:console, 2:address:editor-data
-  ]
-  # screen shows partial wrapped line containing a wrap icon
-  screen-should-contain [
-    .          .
-    .efgh↩     .
-    .ij        .
-    .k         .
-  ]
-  # scroll down again
-  assume-console [
-    press down-arrow
-  ]
-  run [
-    editor-event-loop screen:address:screen, console:address:console, 2:address:editor-data
-  ]
-  # screen shows partial wrapped line
-  screen-should-contain [
-    .          .
-    .ij        .
-    .k         .
-    .l         .
-  ]
-]
-
-scenario editor-scrolls-down-when-line-wraps [
-  # screen has 1 line for menu + 3 lines
-  assume-screen 5/width, 4/height
-  # editor contains a long line in the third line
-  1:address:array:character <- new [a
-b
-cdef]
-  2:address:editor-data <- new-editor 1:address:array:character, screen:address:screen, 0/left, 5/right
-  # position cursor at end, type a character
-  assume-console [
-    left-click 3, 4
-    type [g]
-  ]
-  run [
-    editor-event-loop screen:address:screen, console:address:console, 2:address:editor-data
-    3:number <- get *2:address:editor-data, cursor-row:offset
-    4:number <- get *2:address:editor-data, cursor-column:offset
-  ]
-  # screen scrolls
-  screen-should-contain [
-    .     .
-    .b    .
-    .cdef↩.
-    .g    .
-  ]
-  memory-should-contain [
-    3 <- 3
-    4 <- 1
-  ]
-]
-
-scenario editor-scrolls-down-on-newline [
-  assume-screen 5/width, 4/height
-  # position cursor after last line and type newline
-  1:address:array:character <- new [a
-b
-c]
-  2:address:editor-data <- new-editor 1:address:array:character, screen:address:screen, 0/left, 5/right
-  assume-console [
-    left-click 3, 4
-    type [
-]
-  ]
-  run [
-    editor-event-loop screen:address:screen, console:address:console, 2:address:editor-data
-    3:number <- get *2:address:editor-data, cursor-row:offset
-    4:number <- get *2:address:editor-data, cursor-column:offset
-  ]
-  # screen scrolls
-  screen-should-contain [
-    .     .
-    .b    .
-    .c    .
-    .     .
-  ]
-  memory-should-contain [
-    3 <- 3
-    4 <- 0
-  ]
-]
-
-scenario editor-scrolls-down-on-right-arrow [
-  # screen has 1 line for menu + 3 lines
-  assume-screen 5/width, 4/height
-  # editor contains a wrapped line
-  1:address:array:character <- new [a
-b
-cdefgh]
-  2:address:editor-data <- new-editor 1:address:array:character, screen:address:screen, 0/left, 5/right
-  # position cursor at end of screen and try to move right
-  assume-console [
-    left-click 3, 3
-    press right-arrow
-  ]
-  run [
-    editor-event-loop screen:address:screen, console:address:console, 2:address:editor-data
-    3:number <- get *2:address:editor-data, cursor-row:offset
-    4:number <- get *2:address:editor-data, cursor-column:offset
-  ]
-  # screen scrolls
-  screen-should-contain [
-    .     .
-    .b    .
-    .cdef↩.
-    .gh   .
-  ]
-  memory-should-contain [
-    3 <- 3
-    4 <- 0
-  ]
-]
-
-scenario editor-scrolls-down-on-right-arrow-2 [
-  # screen has 1 line for menu + 3 lines
-  assume-screen 5/width, 4/height
-  # editor contains more lines than can fit on screen
-  1:address:array:character <- new [a
-b
-c
-d]
-  2:address:editor-data <- new-editor 1:address:array:character, screen:address:screen, 0/left, 5/right
-  # position cursor at end of screen and try to move right
-  assume-console [
-    left-click 3, 3
-    press right-arrow
-  ]
-  run [
-    editor-event-loop screen:address:screen, console:address:console, 2:address:editor-data
-    3:number <- get *2:address:editor-data, cursor-row:offset
-    4:number <- get *2:address:editor-data, cursor-column:offset
-  ]
-  # screen scrolls
-  screen-should-contain [
-    .     .
-    .b    .
-    .c    .
-    .d    .
-  ]
-  memory-should-contain [
-    3 <- 3
-    4 <- 0
-  ]
-]
-
-scenario editor-scrolls-at-end-on-down-arrow [
-  assume-screen 10/width, 5/height
-  1:address:array:character <- new [abc
-de]
-  2:address:editor-data <- new-editor 1:address:array:character, screen:address:screen, 0/left, 10/right
-  editor-render screen, 2:address:editor-data
-  $clear-trace
-  # try to move down past end of text
-  assume-console [
-    left-click 2, 0
-    press down-arrow
-  ]
-  run [
-    editor-event-loop screen:address:screen, console:address:console, 2:address:editor-data
-    3:number <- get *2:address:editor-data, cursor-row:offset
-    4:number <- get *2:address:editor-data, cursor-column:offset
-  ]
-  # screen should scroll, moving cursor to end of text
-  memory-should-contain [
-    3 <- 1
-    4 <- 2
-  ]
-  assume-console [
-    type [0]
-  ]
-  run [
-    editor-event-loop screen:address:screen, console:address:console, 2:address:editor-data
-  ]
-  screen-should-contain [
-    .          .
-    .de0       .
-    .┈┈┈┈┈┈┈┈┈┈.
-    .          .
-  ]
-  # try to move down again
-  $clear-trace
-  assume-console [
-    left-click 2, 0
-    press down-arrow
-  ]
-  run [
-    editor-event-loop screen:address:screen, console:address:console, 2:address:editor-data
-    3:number <- get *2:address:editor-data, cursor-row:offset
-    4:number <- get *2:address:editor-data, cursor-column:offset
-  ]
-  # screen stops scrolling because cursor is already at top
-  memory-should-contain [
-    3 <- 1
-    4 <- 3
-  ]
-  check-trace-count-for-label 0, [print-character]
-  assume-console [
-    type [1]
-  ]
-  run [
-    editor-event-loop screen:address:screen, console:address:console, 2:address:editor-data
-  ]
-  screen-should-contain [
-    .          .
-    .de01      .
-    .┈┈┈┈┈┈┈┈┈┈.
-    .          .
-  ]
-]
-
-scenario editor-combines-page-and-line-scroll [
-  # screen has 1 line for menu + 3 lines
-  assume-screen 10/width, 4/height
-  # initialize editor with a few pages of lines
-  1:address:array:character <- new [a
-b
-c
-d
-e
-f
-g]
-  2:address:editor-data <- new-editor 1:address:array:character, screen:address:screen, 0/left, 5/right
-  # scroll down one page and one line
-  assume-console [
-    press page-down
-    left-click 3, 0
-    press down-arrow
-  ]
-  run [
-    editor-event-loop screen:address:screen, console:address:console, 2:address:editor-data
-  ]
-  # screen scrolls down 3 lines
-  screen-should-contain [
-    .          .
-    .d         .
-    .e         .
-    .f         .
-  ]
-]
-
-# cursor-up can scroll if necessary
-
-scenario editor-can-scroll-up-using-arrow-keys [
-  # screen has 1 line for menu + 3 lines
-  assume-screen 10/width, 4/height
-  # initialize editor with >3 lines
-  1:address:array:character <- new [a
-b
-c
-d]
-  2:address:editor-data <- new-editor 1:address:array:character, screen:address:screen, 0/left, 10/right
-  screen-should-contain [
-    .          .
-    .a         .
-    .b         .
-    .c         .
-  ]
-  # position cursor at top of second page, then try to move up
-  assume-console [
-    press page-down
-    press up-arrow
-  ]
-  run [
-    editor-event-loop screen:address:screen, console:address:console, 2:address:editor-data
-  ]
-  # screen slides by one line
-  screen-should-contain [
-    .          .
-    .b         .
-    .c         .
-    .d         .
-  ]
-]
-
-after <scroll-up> [
-  trace 10, [app], [scroll up]
-  top-of-screen:address:duplex-list:character <- get *editor, top-of-screen:offset
-  old-top:address:duplex-list:character <- copy top-of-screen
-  top-of-screen <- before-previous-line top-of-screen, editor
-  *editor <- put *editor, top-of-screen:offset, top-of-screen
-  no-movement?:boolean <- equal old-top, top-of-screen
-  go-render? <- copy 0/false
-  return-if no-movement?
-]
-
 # takes a pointer into the doubly-linked list, scans back to before start of
 # previous *wrapped* line
 # beware: never return null pointer
@@ -2380,869 +1972,3 @@ def before-previous-line in:address:duplex-list:character, editor:address:editor
   }
   return curr
 ]
-
-scenario editor-scrolls-up-past-wrapped-line-using-arrow-keys [
-  # screen has 1 line for menu + 3 lines
-  assume-screen 10/width, 4/height
-  # initialize editor with a long, wrapped line and more than a screen of
-  # other lines
-  1:address:array:character <- new [abcdef
-g
-h
-i]
-  2:address:editor-data <- new-editor 1:address:array:character, screen:address:screen, 0/left, 5/right
-  screen-should-contain [
-    .          .
-    .abcd↩     .
-    .ef        .
-    .g         .
-  ]
-  # position cursor at top of second page, just below wrapped line
-  assume-console [
-    press page-down
-  ]
-  run [
-    editor-event-loop screen:address:screen, console:address:console, 2:address:editor-data
-  ]
-  screen-should-contain [
-    .          .
-    .g         .
-    .h         .
-    .i         .
-  ]
-  # now move up one line
-  assume-console [
-    press up-arrow
-  ]
-  run [
-    editor-event-loop screen:address:screen, console:address:console, 2:address:editor-data
-  ]
-  # screen shows partial wrapped line
-  screen-should-contain [
-    .          .
-    .ef        .
-    .g         .
-    .h         .
-  ]
-]
-
-scenario editor-scrolls-up-past-wrapped-line-using-arrow-keys-2 [
-  # screen has 1 line for menu + 4 lines
-  assume-screen 10/width, 5/height
-  # editor starts with a long line wrapping twice, occupying 3 of the 4 lines
-  1:address:array:character <- new [abcdefghij
-k
-l
-m]
-  2:address:editor-data <- new-editor 1:address:array:character, screen:address:screen, 0/left, 5/right
-  # position cursor at top of second page
-  assume-console [
-    press page-down
-  ]
-  run [
-    editor-event-loop screen:address:screen, console:address:console, 2:address:editor-data
-  ]
-  screen-should-contain [
-    .          .
-    .k         .
-    .l         .
-    .m         .
-    .┈┈┈┈┈     .
-  ]
-  # move up one line
-  assume-console [
-    press up-arrow
-  ]
-  run [
-    editor-event-loop screen:address:screen, console:address:console, 2:address:editor-data
-  ]
-  # screen shows partial wrapped line
-  screen-should-contain [
-    .          .
-    .ij        .
-    .k         .
-    .l         .
-    .m         .
-  ]
-  # move up a second line
-  assume-console [
-    press up-arrow
-  ]
-  run [
-    editor-event-loop screen:address:screen, console:address:console, 2:address:editor-data
-  ]
-  # screen shows partial wrapped line
-  screen-should-contain [
-    .          .
-    .efgh↩     .
-    .ij        .
-    .k         .
-    .l         .
-  ]
-  # move up a third line
-  assume-console [
-    press up-arrow
-  ]
-  run [
-    editor-event-loop screen:address:screen, console:address:console, 2:address:editor-data
-  ]
-  # screen shows partial wrapped line
-  screen-should-contain [
-    .          .
-    .abcd↩     .
-    .efgh↩     .
-    .ij        .
-    .k         .
-  ]
-]
-
-# same as editor-scrolls-up-past-wrapped-line-using-arrow-keys but length
-# slightly off, just to prevent over-training
-scenario editor-scrolls-up-past-wrapped-line-using-arrow-keys-3 [
-  # screen has 1 line for menu + 3 lines
-  assume-screen 10/width, 4/height
-  # initialize editor with a long, wrapped line and more than a screen of
-  # other lines
-  1:address:array:character <- new [abcdef
-g
-h
-i]
-  2:address:editor-data <- new-editor 1:address:array:character, screen:address:screen, 0/left, 6/right
-  screen-should-contain [
-    .          .
-    .abcde↩    .
-    .f         .
-    .g         .
-  ]
-  # position cursor at top of second page, just below wrapped line
-  assume-console [
-    press page-down
-  ]
-  run [
-    editor-event-loop screen:address:screen, console:address:console, 2:address:editor-data
-  ]
-  screen-should-contain [
-    .          .
-    .g         .
-    .h         .
-    .i         .
-  ]
-  # now move up one line
-  assume-console [
-    press up-arrow
-  ]
-  run [
-    editor-event-loop screen:address:screen, console:address:console, 2:address:editor-data
-  ]
-  # screen shows partial wrapped line
-  screen-should-contain [
-    .          .
-    .f         .
-    .g         .
-    .h         .
-  ]
-]
-
-# check empty lines
-scenario editor-scrolls-up-past-wrapped-line-using-arrow-keys-4 [
-  assume-screen 10/width, 4/height
-  # initialize editor with some lines around an empty line
-  1:address:array:character <- new [a
-b
-
-c
-d
-e]
-  2:address:editor-data <- new-editor 1:address:array:character, screen:address:screen, 0/left, 6/right
-  assume-console [
-    press page-down
-  ]
-  run [
-    editor-event-loop screen:address:screen, console:address:console, 2:address:editor-data
-  ]
-  screen-should-contain [
-    .          .
-    .          .
-    .c         .
-    .d         .
-  ]
-  assume-console [
-    press page-down
-  ]
-  run [
-    editor-event-loop screen:address:screen, console:address:console, 2:address:editor-data
-  ]
-  screen-should-contain [
-    .          .
-    .d         .
-    .e         .
-    .┈┈┈┈┈┈    .
-  ]
-  assume-console [
-    press page-up
-  ]
-  run [
-    editor-event-loop screen:address:screen, console:address:console, 2:address:editor-data
-  ]
-  screen-should-contain [
-    .          .
-    .          .
-    .c         .
-    .d         .
-  ]
-]
-
-scenario editor-scrolls-up-on-left-arrow [
-  # screen has 1 line for menu + 3 lines
-  assume-screen 5/width, 4/height
-  # editor contains >3 lines
-  1:address:array:character <- new [a
-b
-c
-d
-e]
-  2:address:editor-data <- new-editor 1:address:array:character, screen:address:screen, 0/left, 5/right
-  # position cursor at top of second page
-  assume-console [
-    press page-down
-  ]
-  run [
-    editor-event-loop screen:address:screen, console:address:console, 2:address:editor-data
-  ]
-  screen-should-contain [
-    .     .
-    .c    .
-    .d    .
-    .e    .
-  ]
-  # now try to move left
-  assume-console [
-    press left-arrow
-  ]
-  run [
-    editor-event-loop screen:address:screen, console:address:console, 2:address:editor-data
-    3:number <- get *2:address:editor-data, cursor-row:offset
-    4:number <- get *2:address:editor-data, cursor-column:offset
-  ]
-  # screen scrolls
-  screen-should-contain [
-    .     .
-    .b    .
-    .c    .
-    .d    .
-  ]
-  memory-should-contain [
-    3 <- 1
-    4 <- 1
-  ]
-]
-
-scenario editor-can-scroll-up-to-start-of-file [
-  # screen has 1 line for menu + 3 lines
-  assume-screen 10/width, 4/height
-  # initialize editor with >3 lines
-  1:address:array:character <- new [a
-b
-c
-d]
-  2:address:editor-data <- new-editor 1:address:array:character, screen:address:screen, 0/left, 10/right
-  screen-should-contain [
-    .          .
-    .a         .
-    .b         .
-    .c         .
-  ]
-  # position cursor at top of second page, then try to move up to start of
-  # text
-  assume-console [
-    press page-down
-    press up-arrow
-    press up-arrow
-  ]
-  run [
-    editor-event-loop screen:address:screen, console:address:console, 2:address:editor-data
-  ]
-  # screen slides by one line
-  screen-should-contain [
-    .          .
-    .a         .
-    .b         .
-    .c         .
-  ]
-  # try to move up again
-  assume-console [
-    press up-arrow
-  ]
-  run [
-    editor-event-loop screen:address:screen, console:address:console, 2:address:editor-data
-  ]
-  # screen remains unchanged
-  screen-should-contain [
-    .          .
-    .a         .
-    .b         .
-    .c         .
-  ]
-]
-
-# ctrl-f/page-down - render next page if it exists
-
-scenario editor-can-scroll [
-  assume-screen 10/width, 4/height
-  1:address:array:character <- new [a
-b
-c
-d]
-  2:address:editor-data <- new-editor 1:address:array:character, screen:address:screen, 0/left, 10/right
-  screen-should-contain [
-    .          .
-    .a         .
-    .b         .
-    .c         .
-  ]
-  # scroll down
-  assume-console [
-    press page-down
-  ]
-  run [
-    editor-event-loop screen:address:screen, console:address:console, 2:address:editor-data
-  ]
-  # screen shows next page
-  screen-should-contain [
-    .          .
-    .c         .
-    .d         .
-    .┈┈┈┈┈┈┈┈┈┈.
-  ]
-]
-
-after <handle-special-character> [
-  {
-    page-down?:boolean <- equal c, 6/ctrl-f
-    break-unless page-down?
-    old-top:address:duplex-list:character <- get *editor, top-of-screen:offset
-    <move-cursor-begin>
-    page-down editor
-    undo-coalesce-tag:number <- copy 0/never
-    <move-cursor-end>
-    top-of-screen:address:duplex-list:character <- get *editor, top-of-screen:offset
-    no-movement?:boolean <- equal top-of-screen, old-top
-    go-render? <- not no-movement?
-    return
-  }
-]
-
-after <handle-special-key> [
-  {
-    page-down?:boolean <- equal k, 65518/page-down
-    break-unless page-down?
-    old-top:address:duplex-list:character <- get *editor, top-of-screen:offset
-    <move-cursor-begin>
-    page-down editor
-    undo-coalesce-tag:number <- copy 0/never
-    <move-cursor-end>
-    top-of-screen:address:duplex-list:character <- get *editor, top-of-screen:offset
-    no-movement?:boolean <- equal top-of-screen, old-top
-    go-render? <- not no-movement?
-    return
-  }
-]
-
-# page-down skips entire wrapped lines, so it can't scroll past lines
-# taking up the entire screen
-def page-down editor:address:editor-data -> editor:address:editor-data [
-  local-scope
-  load-ingredients
-  # if editor contents don't overflow screen, do nothing
-  bottom-of-screen:address:duplex-list:character <- get *editor, bottom-of-screen:offset
-  return-unless bottom-of-screen
-  # if not, position cursor at final character
-  before-cursor:address:duplex-list:character <- get *editor, before-cursor:offset
-  before-cursor:address:duplex-list:character <- prev bottom-of-screen
-  *editor <- put *editor, before-cursor:offset, before-cursor
-  # keep one line in common with previous page
-  {
-    last:character <- get *before-cursor, value:offset
-    newline?:boolean <- equal last, 10/newline
-    break-unless newline?:boolean
-    before-cursor <- prev before-cursor
-    *editor <- put *editor, before-cursor:offset, before-cursor
-  }
-  # move cursor and top-of-screen to start of that line
-  move-to-start-of-line editor
-  before-cursor <- get *editor, before-cursor:offset
-  *editor <- put *editor, top-of-screen:offset, before-cursor
-]
-
-scenario editor-does-not-scroll-past-end [
-  assume-screen 10/width, 4/height
-  1:address:array:character <- new [a
-b]
-  2:address:editor-data <- new-editor 1:address:array:character, screen:address:screen, 0/left, 10/right
-  editor-render screen, 2:address:editor-data
-  screen-should-contain [
-    .          .
-    .a         .
-    .b         .
-    .┈┈┈┈┈┈┈┈┈┈.
-  ]
-  # scroll down
-  assume-console [
-    press page-down
-  ]
-  run [
-    editor-event-loop screen:address:screen, console:address:console, 2:address:editor-data
-  ]
-  # screen remains unmodified
-  screen-should-contain [
-    .          .
-    .a         .
-    .b         .
-    .┈┈┈┈┈┈┈┈┈┈.
-  ]
-]
-
-scenario editor-starts-next-page-at-start-of-wrapped-line [
-  # screen has 1 line for menu + 3 lines for text
-  assume-screen 10/width, 4/height
-  # editor contains a long last line
-  1:address:array:character <- new [a
-b
-cdefgh]
-  # editor screen triggers wrap of last line
-  2:address:editor-data <- new-editor 1:address:array:character, screen:address:screen, 0/left, 4/right
-  # some part of last line is not displayed
-  screen-should-contain [
-    .          .
-    .a         .
-    .b         .
-    .cde↩      .
-  ]
-  # scroll down
-  assume-console [
-    press page-down
-  ]
-  run [
-    editor-event-loop screen:address:screen, console:address:console, 2:address:editor-data
-  ]
-  # screen shows entire wrapped line
-  screen-should-contain [
-    .          .
-    .cde↩      .
-    .fgh       .
-    .┈┈┈┈      .
-  ]
-]
-
-scenario editor-starts-next-page-at-start-of-wrapped-line-2 [
-  # screen has 1 line for menu + 3 lines for text
-  assume-screen 10/width, 4/height
-  # editor contains a very long line that occupies last two lines of screen
-  # and still has something left over
-  1:address:array:character <- new [a
-bcdefgh]
-  2:address:editor-data <- new-editor 1:address:array:character, screen:address:screen, 0/left, 4/right
-  # some part of last line is not displayed
-  screen-should-contain [
-    .          .
-    .a         .
-    .bcd↩      .
-    .efg↩      .
-  ]
-  # scroll down
-  assume-console [
-    press page-down
-  ]
-  run [
-    editor-event-loop screen:address:screen, console:address:console, 2:address:editor-data
-  ]
-  # screen shows entire wrapped line
-  screen-should-contain [
-    .          .
-    .bcd↩      .
-    .efg↩      .
-    .h         .
-  ]
-]
-
-# ctrl-b/page-up - render previous page if it exists
-
-scenario editor-can-scroll-up [
-  assume-screen 10/width, 4/height
-  1:address:array:character <- new [a
-b
-c
-d]
-  2:address:editor-data <- new-editor 1:address:array:character, screen:address:screen, 0/left, 10/right
-  screen-should-contain [
-    .          .
-    .a         .
-    .b         .
-    .c         .
-  ]
-  # scroll down
-  assume-console [
-    press page-down
-  ]
-  run [
-    editor-event-loop screen:address:screen, console:address:console, 2:address:editor-data
-  ]
-  # screen shows next page
-  screen-should-contain [
-    .          .
-    .c         .
-    .d         .
-    .┈┈┈┈┈┈┈┈┈┈.
-  ]
-  # scroll back up
-  assume-console [
-    press page-up
-  ]
-  run [
-    editor-event-loop screen:address:screen, console:address:console, 2:address:editor-data
-  ]
-  # screen shows original page again
-  screen-should-contain [
-    .          .
-    .a         .
-    .b         .
-    .c         .
-  ]
-]
-
-after <handle-special-character> [
-  {
-    page-up?:boolean <- equal c, 2/ctrl-b
-    break-unless page-up?
-    old-top:address:duplex-list:character <- get *editor, top-of-screen:offset
-    <move-cursor-begin>
-    editor <- page-up editor, screen-height
-    undo-coalesce-tag:number <- copy 0/never
-    <move-cursor-end>
-    top-of-screen:address:duplex-list:character <- get *editor, top-of-screen:offset
-    no-movement?:boolean <- equal top-of-screen, old-top
-    go-render? <- not no-movement?
-    return
-  }
-]
-
-after <handle-special-key> [
-  {
-    page-up?:boolean <- equal k, 65519/page-up
-    break-unless page-up?
-    old-top:address:duplex-list:character <- get *editor, top-of-screen:offset
-    <move-cursor-begin>
-    editor <- page-up editor, screen-height
-    undo-coalesce-tag:number <- copy 0/never
-    <move-cursor-end>
-    top-of-screen:address:duplex-list:character <- get *editor, top-of-screen:offset
-    no-movement?:boolean <- equal top-of-screen, old-top
-    # don't bother re-rendering if nothing changed. todo: test this
-    go-render? <- not no-movement?
-    return
-  }
-]
-
-def page-up editor:address:editor-data, screen-height:number -> editor:address:editor-data [
-  local-scope
-  load-ingredients
-  max:number <- subtract screen-height, 1/menu-bar, 1/overlapping-line
-  count:number <- copy 0
-  top-of-screen:address:duplex-list:character <- get *editor, top-of-screen:offset
-  {
-    done?:boolean <- greater-or-equal count, max
-    break-if done?
-    prev:address:duplex-list:character <- before-previous-line top-of-screen, editor
-    break-unless prev
-    top-of-screen <- copy prev
-    *editor <- put *editor, top-of-screen:offset, top-of-screen
-    count <- add count, 1
-    loop
-  }
-]
-
-scenario editor-can-scroll-up-multiple-pages [
-  # screen has 1 line for menu + 3 lines
-  assume-screen 10/width, 4/height
-  # initialize editor with 8 lines
-  1:address:array:character <- new [a
-b
-c
-d
-e
-f
-g
-h]
-  2:address:editor-data <- new-editor 1:address:array:character, screen:address:screen, 0/left, 10/right
-  screen-should-contain [
-    .          .
-    .a         .
-    .b         .
-    .c         .
-  ]
-  # scroll down two pages
-  assume-console [
-    press page-down
-    press page-down
-  ]
-  run [
-    editor-event-loop screen:address:screen, console:address:console, 2:address:editor-data
-  ]
-  # screen shows third page
-  screen-should-contain [
-    .          .
-    .e         .
-    .f         .
-    .g         .
-  ]
-  # scroll up
-  assume-console [
-    press page-up
-  ]
-  run [
-    editor-event-loop screen:address:screen, console:address:console, 2:address:editor-data
-  ]
-  # screen shows second page
-  screen-should-contain [
-    .          .
-    .c         .
-    .d         .
-    .e         .
-  ]
-  # scroll up again
-  assume-console [
-    press page-up
-  ]
-  run [
-    editor-event-loop screen:address:screen, console:address:console, 2:address:editor-data
-  ]
-  # screen shows original page again
-  screen-should-contain [
-    .          .
-    .a         .
-    .b         .
-    .c         .
-  ]
-]
-
-scenario editor-can-scroll-up-wrapped-lines [
-  # screen has 1 line for menu + 5 lines for text
-  assume-screen 10/width, 6/height
-  # editor contains a long line in the first page
-  1:address:array:character <- new [a
-b
-cdefgh
-i
-j
-k
-l
-m
-n
-o]
-  # editor screen triggers wrap of last line
-  2:address:editor-data <- new-editor 1:address:array:character, screen:address:screen, 0/left, 4/right
-  # some part of last line is not displayed
-  screen-should-contain [
-    .          .
-    .a         .
-    .b         .
-    .cde↩      .
-    .fgh       .
-    .i         .
-  ]
-  # scroll down a page and a line
-  assume-console [
-    press page-down
-    left-click 5, 0
-    press down-arrow
-  ]
-  run [
-    editor-event-loop screen:address:screen, console:address:console, 2:address:editor-data
-  ]
-  # screen shows entire wrapped line
-  screen-should-contain [
-    .          .
-    .j         .
-    .k         .
-    .l         .
-    .m         .
-    .n         .
-  ]
-  # now scroll up one page
-  assume-console [
-    press page-up
-  ]
-  run [
-    editor-event-loop screen:address:screen, console:address:console, 2:address:editor-data
-  ]
-  # screen resets
-  screen-should-contain [
-    .          .
-    .b         .
-    .cde↩      .
-    .fgh       .
-    .i         .
-    .j         .
-  ]
-]
-
-scenario editor-can-scroll-up-wrapped-lines-2 [
-  # screen has 1 line for menu + 3 lines for text
-  assume-screen 10/width, 4/height
-  # editor contains a very long line that occupies last two lines of screen
-  # and still has something left over
-  1:address:array:character <- new [a
-bcdefgh]
-  2:address:editor-data <- new-editor 1:address:array:character, screen:address:screen, 0/left, 4/right
-  # some part of last line is not displayed
-  screen-should-contain [
-    .          .
-    .a         .
-    .bcd↩      .
-    .efg↩      .
-  ]
-  # scroll down
-  assume-console [
-    press page-down
-  ]
-  run [
-    editor-event-loop screen:address:screen, console:address:console, 2:address:editor-data
-  ]
-  # screen shows entire wrapped line
-  screen-should-contain [
-    .          .
-    .bcd↩      .
-    .efg↩      .
-    .h         .
-  ]
-  # scroll back up
-  assume-console [
-    press page-up
-  ]
-  run [
-    editor-event-loop screen:address:screen, console:address:console, 2:address:editor-data
-  ]
-  # screen resets
-  screen-should-contain [
-    .          .
-    .a         .
-    .bcd↩      .
-    .efg↩      .
-  ]
-]
-
-scenario editor-can-scroll-up-past-nonempty-lines [
-  assume-screen 10/width, 4/height
-  # text with empty line in second screen
-  1:address:array:character <- new [axx
-bxx
-cxx
-dxx
-exx
-fxx
-gxx
-hxx
-]
-  2:address:editor-data <- new-editor 1:address:array:character, screen:address:screen, 0/left, 4/right
-  screen-should-contain [
-    .          .
-    .axx       .
-    .bxx       .
-    .cxx       .
-  ]
-  assume-console [
-    press page-down
-  ]
-  run [
-    editor-event-loop screen:address:screen, console:address:console, 2:address:editor-data
-  ]
-  screen-should-contain [
-    .          .
-    .cxx       .
-    .dxx       .
-    .exx       .
-  ]
-  assume-console [
-    press page-down
-  ]
-  run [
-    editor-event-loop screen:address:screen, console:address:console, 2:address:editor-data
-  ]
-  screen-should-contain [
-    .          .
-    .exx       .
-    .fxx       .
-    .gxx       .
-  ]
-  # scroll back up past empty line
-  assume-console [
-    press page-up
-  ]
-  run [
-    editor-event-loop screen:address:screen, console:address:console, 2:address:editor-data
-  ]
-  screen-should-contain [
-    .          .
-    .cxx       .
-    .dxx       .
-    .exx       .
-  ]
-]
-
-scenario editor-can-scroll-up-past-empty-lines [
-  assume-screen 10/width, 4/height
-  # text with empty line in second screen
-  1:address:array:character <- new [axy
-bxy
-cxy
-
-dxy
-exy
-fxy
-gxy
-]
-  2:address:editor-data <- new-editor 1:address:array:character, screen:address:screen, 0/left, 4/right
-  screen-should-contain [
-    .          .
-    .axy       .
-    .bxy       .
-    .cxy       .
-  ]
-  assume-console [
-    press page-down
-  ]
-  run [
-    editor-event-loop screen:address:screen, console:address:console, 2:address:editor-data
-  ]
-  screen-should-contain [
-    .          .
-    .cxy       .
-    .          .
-    .dxy       .
-  ]
-  assume-console [
-    press page-down
-  ]
-  run [
-    editor-event-loop screen:address:screen, console:address:console, 2:address:editor-data
-  ]
-  screen-should-contain [
-    .          .
-    .dxy       .
-    .exy       .
-    .fxy       .
-  ]
-  # scroll back up past empty line
-  assume-console [
-    press page-up
-  ]
-  run [
-    editor-event-loop screen:address:screen, console:address:console, 2:address:editor-data
-  ]
-  screen-should-contain [
-    .          .
-    .cxy       .
-    .          .
-    .dxy       .
-  ]
-]