about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorKartik K. Agaram <vc@akkartik.com>2015-09-01 12:38:21 -0700
committerKartik K. Agaram <vc@akkartik.com>2015-09-01 12:38:32 -0700
commitcb75d366506687b34213d8fa6b99de926f4d3b36 (patch)
treec2b804ec20d24c92bd7067708c415e0dee087563
parent2e69df9fe5e59c6336e5bc9af07f14d00f36a899 (diff)
downloadmu-cb75d366506687b34213d8fa6b99de926f4d3b36.tar.gz
2121 - undo now supports the 'delete' key
Still to go: ctrl-k and ctrl-u.
-rw-r--r--065duplex_list.mu79
-rw-r--r--edit.mu211
2 files changed, 278 insertions, 12 deletions
diff --git a/065duplex_list.mu b/065duplex_list.mu
index e3eafcba..a8091a22 100644
--- a/065duplex_list.mu
+++ b/065duplex_list.mu
@@ -392,6 +392,72 @@ recipe remove-duplex-between [
   reply start
 ]
 
+scenario remove-range [
+  # construct a duplex list with six elements [13, 14, 15, 16, 17, 18]
+  1:address:duplex-list <- copy 0  # 1 points to singleton list
+  1:address:duplex-list <- push-duplex 18, 1:address:duplex-list
+  1:address:duplex-list <- push-duplex 17, 1:address:duplex-list
+  1:address:duplex-list <- push-duplex 16, 1:address:duplex-list
+  1:address:duplex-list <- push-duplex 15, 1:address:duplex-list
+  1:address:duplex-list <- push-duplex 14, 1:address:duplex-list
+  1:address:duplex-list <- push-duplex 13, 1:address:duplex-list
+  run [
+    # delete 16 onwards
+    # first pointer: to the third element
+    2:address:duplex-list <- next-duplex 1:address:duplex-list
+    2:address:duplex-list <- next-duplex 2:address:duplex-list
+    remove-duplex-between 2:address:duplex-list, 0
+    # now check the list
+    4:number <- get *1:address:duplex-list, value:offset
+    5:address:duplex-list <- next-duplex 1:address:duplex-list
+    6:number <- get *5:address:duplex-list, value:offset
+    7:address:duplex-list <- next-duplex 5:address:duplex-list
+    8:number <- get *7:address:duplex-list, value:offset
+    9:address:duplex-list <- next-duplex 7:address:duplex-list
+  ]
+  memory-should-contain [
+    4 <- 13
+    6 <- 14
+    8 <- 15
+    9 <- 0
+  ]
+]
+
+scenario remove-range-to-end [
+  # construct a duplex list with six elements [13, 14, 15, 16, 17, 18]
+  1:address:duplex-list <- copy 0  # 1 points to singleton list
+  1:address:duplex-list <- push-duplex 18, 1:address:duplex-list
+  1:address:duplex-list <- push-duplex 17, 1:address:duplex-list
+  1:address:duplex-list <- push-duplex 16, 1:address:duplex-list
+  1:address:duplex-list <- push-duplex 15, 1:address:duplex-list
+  1:address:duplex-list <- push-duplex 14, 1:address:duplex-list
+  1:address:duplex-list <- push-duplex 13, 1:address:duplex-list
+  run [
+    # delete 15, 16 and 17
+    # first pointer: to the third element
+    2:address:duplex-list <- next-duplex 1:address:duplex-list
+    # second pointer: to the fifth element
+    3:address:duplex-list <- next-duplex 2:address:duplex-list
+    3:address:duplex-list <- next-duplex 3:address:duplex-list
+    3:address:duplex-list <- next-duplex 3:address:duplex-list
+    3:address:duplex-list <- next-duplex 3:address:duplex-list
+    remove-duplex-between 2:address:duplex-list, 3:address:duplex-list
+    # now check the list
+    4:number <- get *1:address:duplex-list, value:offset
+    5:address:duplex-list <- next-duplex 1:address:duplex-list
+    6:number <- get *5:address:duplex-list, value:offset
+    7:address:duplex-list <- next-duplex 5:address:duplex-list
+    8:number <- get *7:address:duplex-list, value:offset
+    9:address:duplex-list <- next-duplex 7:address:duplex-list
+  ]
+  memory-should-contain [
+    4 <- 13
+    6 <- 14
+    8 <- 18
+    9 <- 0
+  ]
+]
+
 # l:address:duplex-list <- insert-duplex-range in:address:duplex-list, new:address:duplex-list
 # Inserts list beginning at 'new' after 'in'. Returns some pointer into the list.
 recipe insert-duplex-range [
@@ -422,6 +488,19 @@ recipe insert-duplex-range [
   reply in
 ]
 
+recipe append-duplex [
+  local-scope
+  in:address:duplex-list <- next-ingredient
+  new:address:duplex-list <- next-ingredient
+  last:address:duplex-list <- last-duplex in
+  dest:address:address:duplex-list <- get-address *last, next:offset
+  *dest <- copy new
+  reply-unless new, in/same-as-ingredient:0
+  dest <- get-address *new, prev:offset
+  *dest <- copy last
+  reply in/same-as-ingredient:0
+]
+
 recipe last-duplex [
   local-scope
   in:address:duplex-list <- next-ingredient
diff --git a/edit.mu b/edit.mu
index 280aff84..116eac9d 100644
--- a/edit.mu
+++ b/edit.mu
@@ -1765,7 +1765,7 @@ after +handle-special-key [
     delete-next-character?:boolean <- equal *k, 65522/delete
     break-unless delete-next-character?
     +delete-character-begin
-    editor, screen, go-render?:boolean <- delete-at-cursor editor, screen
+    editor, screen, go-render?:boolean, deleted-cell:address:duplex-list <- delete-at-cursor editor, screen
     +delete-character-end
     reply screen/same-as-ingredient:0, editor/same-as-ingredient:1, go-render?
   }
@@ -1776,12 +1776,12 @@ recipe delete-at-cursor [
   editor:address:editor-data <- next-ingredient
   screen:address <- next-ingredient
   before-cursor:address:address:duplex-list <- get-address *editor, before-cursor:offset
-  curr:address:duplex-list <- next-duplex *before-cursor
-  reply-unless curr, editor/same-as-ingredient:0, screen/same-as-ingredient:1, 0/no-more-render
-  currc:character <- get *curr, value:offset
-  remove-duplex curr
+  candidate:address:duplex-list <- next-duplex *before-cursor
+  reply-unless candidate, editor/same-as-ingredient:0, screen/same-as-ingredient:1, 0/no-more-render, 0/nothing-deleted
+  currc:character <- get *candidate, value:offset
+  remove-duplex candidate
   deleted-newline?:boolean <- equal currc, 10/newline
-  reply-if deleted-newline?, screen/same-as-ingredient:0, editor/same-as-ingredient:1, 1/go-render
+  reply-if deleted-newline?, editor/same-as-ingredient:0, screen/same-as-ingredient:1, 1/go-render, candidate/deleted-cell
   # wasn't a newline? render rest of line
   curr:address:duplex-list <- next-duplex *before-cursor  # refresh after remove-duplex above
   cursor-row:address:number <- get-address *editor, cursor-row:offset
@@ -1792,7 +1792,7 @@ recipe delete-at-cursor [
   {
     # hit right margin? give up and let caller render
     at-right?:boolean <- greater-or-equal curr-column, screen-width
-    reply-if at-right?, screen/same-as-ingredient:0, editor/same-as-ingredient:1, 1/go-render
+    reply-if at-right?, editor/same-as-ingredient:0, screen/same-as-ingredient:1, 1/go-render, candidate/deleted-cell
     break-unless curr
     # newline? done.
     currc:character <- get *curr, value:offset
@@ -1805,7 +1805,7 @@ recipe delete-at-cursor [
   }
   # we're guaranteed not to be at the right margin
   screen <- print-character screen, 32/space
-  reply screen/same-as-ingredient:0, editor/same-as-ingredient:1, 0/no-more-render
+  reply editor/same-as-ingredient:0, screen/same-as-ingredient:1, 0/no-more-render, candidate/deleted-cell
 ]
 
 # right arrow
@@ -8147,7 +8147,6 @@ scenario editor-can-undo-and-redo-backspace [
   run [
     editor-event-loop screen:address, console:address, 2:address:editor-data
   ]
-  # typing in second line deleted, but not indent
   3:number <- get *2:address:editor-data, cursor-row:offset
   4:number <- get *2:address:editor-data, cursor-column:offset
   memory-should-contain [
@@ -8167,7 +8166,6 @@ scenario editor-can-undo-and-redo-backspace [
   run [
     editor-event-loop screen:address, console:address, 2:address:editor-data
   ]
-  # first line inserted
   3:number <- get *2:address:editor-data, cursor-row:offset
   4:number <- get *2:address:editor-data, cursor-column:offset
   memory-should-contain [
@@ -8211,14 +8209,14 @@ before +backspace-character-end [
       *after-column <- copy *cursor-column
       after-top:address:number <- get-address *deletion, after-top-of-screen:offset
       *after-top <- get *editor, top-of-screen:offset
-      break +done-adding-insert-operation:label
+      break +done-adding-backspace-operation:label
     }
     # if not, create a new operation
     op:address:operation <- new operation:type
     deleted-until:address:duplex-list <- next-duplex *before-cursor
     *op <- merge 2/delete-operation, save-row/before, save-column/before, top-before, *cursor-row/after, *cursor-column/after, top-after, backspaced-cell/deleted, *before-cursor/delete-from, deleted-until, 1/coalesce-backspace
     editor <- add-operation editor, op
-    +done-adding-insert-operation
+    +done-adding-backspace-operation
   }
 ]
 
@@ -8256,6 +8254,195 @@ after +handle-redo [
   }
 ]
 
+# undo delete
+
+scenario editor-can-undo-and-redo-delete [
+  # create an editor
+  assume-screen 10/width, 5/height
+  1:address:array:character <- new []
+  2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 10/right
+  editor-render screen, 2:address:editor-data
+  # 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:address, console:address, 2:address:editor-data
+  screen-should-contain [
+    .          .
+    .af        .
+    .┈┈┈┈┈┈┈┈┈┈.
+    .          .
+  ]
+  3:number <- get *2:address:editor-data, cursor-row:offset
+  4:number <- get *2:address:editor-data, cursor-column:offset
+  memory-should-contain [
+    3 <- 1
+    4 <- 1
+  ]
+  # undo deletes
+  assume-console [
+    press ctrl-z
+  ]
+  run [
+    editor-event-loop screen:address, console:address, 2:address:editor-data
+  ]
+  3:number <- get *2:address:editor-data, cursor-row:offset
+  4:number <- get *2:address:editor-data, 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:address, console:address, 2:address:editor-data
+  ]
+  3:number <- get *2:address:editor-data, cursor-row:offset
+  4:number <- get *2:address:editor-data, 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:address, console:address, 2:address:editor-data
+  ]
+  3:number <- get *2:address:editor-data, cursor-row:offset
+  4:number <- get *2:address:editor-data, 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:address, console:address, 2:address:editor-data
+  ]
+  # first line inserted
+  3:number <- get *2:address:editor-data, cursor-row:offset
+  4:number <- get *2:address:editor-data, 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:address, console:address, 2:address:editor-data
+  ]
+  # first line inserted
+  3:number <- get *2:address:editor-data, cursor-row:offset
+  4:number <- get *2:address:editor-data, 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:address, console:address, 2:address:editor-data
+  ]
+  # first line inserted
+  3:number <- get *2:address:editor-data, cursor-row:offset
+  4:number <- get *2:address:editor-data, cursor-column:offset
+  memory-should-contain [
+    3 <- 1
+    4 <- 1
+  ]
+  screen-should-contain [
+    .          .
+    .af        .
+    .┈┈┈┈┈┈┈┈┈┈.
+    .          .
+  ]
+]
+
+# save operation to undo
+after +delete-character-begin [
+  top-before:address:duplex-list <- get *editor, top-of-screen:offset
+  undo:address:address:list <- get-address *editor, undo:offset
+]
+before +delete-character-end [
+  {
+    break-unless deleted-cell  # delete failed; don't add an undo operation
+    top-after:address:duplex-list <- get *editor, top-of-screen:offset
+    undo:address:address:list <- get-address *editor, undo:offset
+    {
+      # if previous operation was an insert, coalesce this operation with it
+      break-unless *undo
+      op:address:operation <- first *undo
+      deletion:address:delete-operation <- maybe-convert *op, delete:variant
+      break-unless deletion
+      previous-coalesce-tag:number <- get *deletion, tag:offset
+      coalesce?:boolean <- equal previous-coalesce-tag, 2/coalesce-delete
+      break-unless coalesce?
+      delete-until:address:address:duplex-list <- get-address *deletion, delete-until:offset
+      *delete-until <- next-duplex *before-cursor
+      deleted-so-far:address:address:duplex-list <- get-address *deletion, deleted-text:offset
+      *deleted-so-far <- append-duplex *deleted-so-far, deleted-cell
+      after-row:address:number <- get-address *deletion, after-row:offset
+      *after-row <- copy *cursor-row
+      after-column:address:number <- get-address *deletion, after-column:offset
+      *after-column <- copy *cursor-column
+      after-top:address:number <- get-address *deletion, after-top-of-screen:offset
+      *after-top <- get *editor, top-of-screen:offset
+      break +done-adding-delete-operation:label
+    }
+    # if not, create a new operation
+    op:address:operation <- new operation:type
+    deleted-until:address:duplex-list <- next-duplex *before-cursor
+    *op <- merge 2/delete-operation, save-row/before, save-column/before, top-before, *cursor-row/after, *cursor-column/after, top-after, deleted-cell/deleted, *before-cursor/delete-from, deleted-until, 2/coalesce-delete
+    editor <- add-operation editor, op
+    +done-adding-delete-operation
+  }
+]
+
 # todo:
 # operations for recipe side and each sandbox-data
 # undo delete sandbox as a separate primitive on the status bar