about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorKartik K. Agaram <vc@akkartik.com>2015-08-04 19:28:45 -0700
committerKartik K. Agaram <vc@akkartik.com>2015-08-04 19:28:45 -0700
commit1ccbf38565f3297210f54819725ec896c4109831 (patch)
tree7e4bf51bfa0d2985ce79687285ce7af9aab3d6d1
parente779af8508baa81443b3ad3c4d91e3f66e0b85c3 (diff)
downloadmu-1ccbf38565f3297210f54819725ec896c4109831.tar.gz
1931 - basic support for scrolling
Still need to fix all the todo's in edit.mu dealing with scrolling.
-rw-r--r--030container.cc20
-rw-r--r--edit.mu284
2 files changed, 294 insertions, 10 deletions
diff --git a/030container.cc b/030container.cc
index 6159f3a0..54a0df1f 100644
--- a/030container.cc
+++ b/030container.cc
@@ -321,6 +321,25 @@ void insert_container(const string& command, kind_of_type kind, istream& in) {
   t.size = SIZE(t.elements);
 }
 
+:(scenarios run)
+:(scenario container_define_twice)
+container foo [
+  x:number
+]
+
+container foo [
+  y:number
+]
+
+recipe main [
+  1:number <- copy 34
+  2:number <- copy 35
+  3:number <- get 1:foo, x:offset
+  4:number <- get 1:foo, y:offset
+]
++mem: storing 34 in location 3
++mem: storing 35 in location 4
+
 //: ensure types created in one scenario don't leak outside it.
 :(before "End Globals")
 vector<type_ordinal> recently_added_types;
@@ -359,7 +378,6 @@ Next_type_ordinal = 1000;
 //:: Allow container definitions anywhere in the codebase, but warn if you
 //:: can't find a definition.
 
-:(scenarios run)
 :(scenario run_warns_on_unknown_types)
 % Hide_warnings = true;
 #? % Trace_stream->dump_layer = "run";
diff --git a/edit.mu b/edit.mu
index 385e8480..e5d43001 100644
--- a/edit.mu
+++ b/edit.mu
@@ -34,6 +34,8 @@ scenario editor-initially-prints-string-to-screen [
 container editor-data [
   # editable text: doubly linked list of characters (head contains a special sentinel)
   data:address:duplex-list
+  top-of-screen:address:duplex-list
+  bottom-of-screen:address:duplex-list
   # location before cursor inside data
   before-cursor:address:duplex-list
 
@@ -71,6 +73,8 @@ recipe new-editor [
   *x <- copy left
   init:address:address:duplex-list <- get-address *result, data:offset
   *init <- push-duplex 167/§, 0/tail
+  top-of-screen:address:address:duplex-list <- get-address *result, top-of-screen:offset
+  *top-of-screen <- copy *init
   y:address:address:duplex-list <- get-address *result, before-cursor:offset
   *y <- copy *init
   result <- insert-text result, s
@@ -114,11 +118,13 @@ scenario editor-initializes-without-data [
   ]
   memory-should-contain [
     # 2 (data) <- just the § sentinel
-    # 3 (before cursor) <- the § sentinel
-    4 <- 2  # left
-    5 <- 4  # right  (inclusive)
-    6 <- 1  # cursor row
-    7 <- 2  # cursor column
+    # 3 (top of screen) <- the § sentinel
+    4 <- 0  # bottom-of-screen; null since text fits on screen
+    # 5 (before cursor) <- the § sentinel
+    6 <- 2  # left
+    7 <- 4  # right  (inclusive)
+    8 <- 1  # cursor row
+    9 <- 2  # cursor column
   ]
   screen-should-contain [
     .     .
@@ -138,7 +144,7 @@ recipe render [
   right:number <- get *editor, right:offset
   hide-screen screen
   # traversing editor
-  curr:address:duplex-list <- get *editor, data:offset
+  curr:address:duplex-list <- get *editor, top-of-screen:offset
   prev:address:duplex-list <- copy curr
   curr <- next-duplex curr
   # traversing screen
@@ -209,6 +215,9 @@ recipe render [
     column <- add column, 1
     loop
   }
+  # save first character off-screen
+  bottom-of-screen:address:address:duplex-list <- get-address *editor, bottom-of-screen:offset
+  *bottom-of-screen <- copy curr
   # is cursor to the right of the last line? move to end
   {
     at-cursor-row?:boolean <- equal row, *cursor-row
@@ -230,8 +239,9 @@ recipe render [
     }
     *before-cursor <- copy prev
   }
-  # clear rest of current line
+  # clear rest of screen
   clear-line-delimited screen, column, right
+  clear-rest-of-screen screen, row, left, right
   reply row, screen/same-as-ingredient:0
 ]
 
@@ -2448,6 +2458,252 @@ scenario editor-deletes-to-end-of-line-with-ctrl-k-6 [
   ]
 ]
 
+# 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, 0/left, 10/right
+  screen-should-contain [
+    .          .
+    .a         .
+    .b         .
+    .c         .
+  ]
+  # scroll down
+  assume-console [
+    press 65518  # page-down
+  ]
+  run [
+    editor-event-loop screen:address, console:address, 2:address:editor-data
+  ]
+  # screen shows next page
+  screen-should-contain [
+    .          .
+    .c         .
+    .d         .
+    .          .
+  ]
+]
+
+after +handle-special-character [
+  {
+    ctrl-f?:boolean <- equal *c, 6/ctrl-f
+    break-unless ctrl-f?
+    page-down editor
+    reply
+  }
+]
+
+after +handle-special-key [
+  {
+    page-down?:boolean <- equal *k, 65518/page-down
+    break-unless page-down?
+    page-down editor
+    reply
+  }
+]
+
+# cache old pages in a linked-list so page-up later doesn't have to recompute
+# them
+container editor-data [
+  previous-page:address:list:address:duplex-list:character
+]
+
+recipe page-down [
+  local-scope
+  editor:address:editor-data <- next-ingredient
+  # if editor contents don't overflow screen, do nothing
+  bottom-of-screen:address:duplex-list <- get *editor, bottom-of-screen:offset
+  reply-unless bottom-of-screen, editor/same-as-ingredient:0
+  # if not, position cursor at final character
+  before-cursor:address:address:duplex-list <- get-address *editor, before-cursor:offset
+  *before-cursor <- prev-duplex bottom-of-screen
+  # 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-duplex *before-cursor
+  }
+  # save top-of-screen to previous-page list
+  top-of-screen:address:address:duplex-list <- get-address *editor, top-of-screen:offset
+  previous-page:address:address:list:address:duplex-list:character <- get-address *editor, previous-page:offset
+  *previous-page <- push *top-of-screen, *previous-page
+  # move cursor and top-of-screen to start of that line
+  move-to-start-of-line editor
+  *top-of-screen <- copy *before-cursor
+  reply editor/same-as-ingredient:0
+]
+
+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, 0/left, 10/right
+  screen-should-contain [
+    .          .
+    .a         .
+    .b         .
+    .          .
+  ]
+  # scroll down
+  assume-console [
+    press 65518  # page-down
+  ]
+  run [
+    editor-event-loop screen:address, console:address, 2:address:editor-data
+  ]
+  # screen remains unmodified
+  screen-should-contain [
+    .          .
+    .a         .
+    .b         .
+    .          .
+  ]
+]
+
+# 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, 0/left, 10/right
+  screen-should-contain [
+    .          .
+    .a         .
+    .b         .
+    .c         .
+  ]
+  # scroll down
+  assume-console [
+    press 65518  # page-down
+  ]
+  run [
+    editor-event-loop screen:address, console:address, 2:address:editor-data
+  ]
+  # screen shows next page
+  screen-should-contain [
+    .          .
+    .c         .
+    .d         .
+    .          .
+  ]
+  # scroll back up
+  assume-console [
+    press 65519  # page-up
+  ]
+  run [
+    editor-event-loop screen:address, console:address, 2:address:editor-data
+  ]
+  # screen shows original page again
+  screen-should-contain [
+    .          .
+    .a         .
+    .b         .
+    .c         .
+  ]
+]
+
+after +handle-special-character [
+  {
+    ctrl-b?:boolean <- equal *c, 2/ctrl-f
+    break-unless ctrl-b?
+    page-up editor
+    reply
+  }
+]
+
+after +handle-special-key [
+  {
+    page-up?:boolean <- equal *k, 65519/page-up
+    break-unless page-up?
+    page-up editor
+    reply
+  }
+]
+
+recipe page-up [
+  local-scope
+  editor:address:editor-data <- next-ingredient
+  previous-page:address:address:list:address:duplex-list:character <- get-address *editor, previous-page:offset
+  reply-unless *previous-page, editor/same-as-ingredient:0
+  top-of-screen:address:address:duplex-list <- get-address *editor, top-of-screen:offset
+  *top-of-screen <- first *previous-page
+  *previous-page <- rest *previous-page
+  reply editor/same-as-ingredient:0
+]
+
+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, 0/left, 10/right
+  screen-should-contain [
+    .          .
+    .a         .
+    .b         .
+    .c         .
+  ]
+  # scroll down two pages
+  assume-console [
+    press 65518  # page-down
+    press 65518  # page-down
+  ]
+  run [
+    editor-event-loop screen:address, console:address, 2:address:editor-data
+  ]
+  # screen shows third page
+  screen-should-contain [
+    .          .
+    .e         .
+    .f         .
+    .g         .
+  ]
+  # scroll up
+  assume-console [
+    press 65519  # page-up
+  ]
+  run [
+    editor-event-loop screen:address, console:address, 2:address:editor-data
+  ]
+  # screen shows second page
+  screen-should-contain [
+    .          .
+    .c         .
+    .d         .
+    .e         .
+  ]
+  # scroll up again
+  assume-console [
+    press 65519  # page-up
+  ]
+  run [
+    editor-event-loop screen:address, console:address, 2:address:editor-data
+  ]
+  # screen shows original page again
+  screen-should-contain [
+    .          .
+    .a         .
+    .b         .
+    .c         .
+  ]
+]
+
 ## putting the environment together out of editors
 
 container programming-environment-data [
@@ -2745,7 +3001,16 @@ recipe render-recipes [
   }
   # draw dotted line after recipes
   draw-horizontal screen, row, left, right, 9480/horizontal-dotted
-  # clear rest of screen
+  clear-rest-of-screen screen, row, left, right
+  reply screen/same-as-ingredient:0
+]
+
+recipe clear-rest-of-screen [
+  local-scope
+  screen:address <- next-ingredient
+  row:number <- next-ingredient
+  left:number <- next-ingredient
+  right:number <- next-ingredient
   row <- add row, 1
   move-cursor screen, row, left
   screen-height:number <- screen-height screen
@@ -2757,7 +3022,6 @@ recipe render-recipes [
     row <- add row, 1
     loop
   }
-  reply screen/same-as-ingredient:0
 ]
 
 # helper for testing a single editor
@@ -2922,6 +3186,8 @@ recipe run-sandboxes [
     # clear sandbox editor
     init:address:address:duplex-list <- get-address *current-sandbox, data:offset
     *init <- push-duplex 167/§, 0/tail
+    top-of-screen:address:address:duplex-list <- get-address *current-sandbox, top-of-screen:offset
+    *top-of-screen <- copy *init
   }
   # save all sandboxes before running, just in case we die when running
   save-sandboxes env