about summary refs log tree commit diff stats
path: root/sandbox
diff options
context:
space:
mode:
authorKartik K. Agaram <vc@akkartik.com>2016-04-22 22:53:39 -0700
committerKartik K. Agaram <vc@akkartik.com>2016-04-22 22:53:39 -0700
commitd31037ffdcdb8097b91af121a27ef18c15f7e802 (patch)
treec697eab36eeb21eb15d27937c68396f0d0786f14 /sandbox
parentea5e7fd4cb5757589cf3cb52439a3d432517bc7a (diff)
downloadmu-d31037ffdcdb8097b91af121a27ef18c15f7e802.tar.gz
2854 - purge get-address from sandbox/ app
Diffstat (limited to 'sandbox')
-rw-r--r--sandbox/001-editor.mu69
-rw-r--r--sandbox/002-typing.mu121
-rw-r--r--sandbox/003-shortcuts.mu261
-rw-r--r--sandbox/004-programming-environment.mu14
-rw-r--r--sandbox/005-sandbox.mu75
-rw-r--r--sandbox/006-sandbox-edit.mu48
-rw-r--r--sandbox/007-sandbox-delete.mu51
-rw-r--r--sandbox/008-sandbox-test.mu25
-rw-r--r--sandbox/009-sandbox-trace.mu13
-rw-r--r--sandbox/010-errors.mu90
-rw-r--r--sandbox/011-editor-undo.mu466
11 files changed, 644 insertions, 589 deletions
diff --git a/sandbox/001-editor.mu b/sandbox/001-editor.mu
index 818f3520..614608c2 100644
--- a/sandbox/001-editor.mu
+++ b/sandbox/001-editor.mu
@@ -55,25 +55,17 @@ def new-editor s:address:shared:array:character, screen:address:shared:screen, l
   right <- subtract right, 1
   result <- new editor-data:type
   # initialize screen-related fields
-  x:address:number <- get-address *result, left:offset
-  *x <- copy left
-  x <- get-address *result, right:offset
-  *x <- copy right
-  # initialize cursor
-  x <- get-address *result, cursor-row:offset
-  *x <- copy 1/top
-  x <- get-address *result, cursor-column:offset
-  *x <- copy left
-  init:address:address:shared:duplex-list:character <- get-address *result, data:offset
-  *init <- push 167/§, 0/tail
-  top-of-screen:address:address:shared:duplex-list:character <- get-address *result, top-of-screen:offset
-  *top-of-screen <- copy *init
-  y:address:address:shared:duplex-list:character <- get-address *result, before-cursor:offset
-  *y <- copy *init
+  *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:address:shared:duplex-list:character <- push 167/§, 0/tail
+  *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
-  # initialize cursor to top of screen
-  y <- get-address *result, before-cursor:offset
-  *y <- copy *init
   # initial render to screen, just for some old tests
   _, _, screen, result <- render screen, result
   <editor-initialization>
@@ -145,9 +137,9 @@ def render screen:address:shared:screen, editor:address:shared:editor-data -> la
   color:number <- copy 7/white
   row:number <- copy 1/top
   column:number <- copy left
-  cursor-row:address:number <- get-address *editor, cursor-row:offset
-  cursor-column:address:number <- get-address *editor, cursor-column:offset
-  before-cursor:address:address:shared:duplex-list:character <- get-address *editor, before-cursor:offset
+  cursor-row:number <- get *editor, cursor-row:offset
+  cursor-column:number <- get *editor, cursor-column:offset
+  before-cursor:address:shared:duplex-list:character <- get *editor, before-cursor:offset
   screen <- move-cursor screen, row, column
   {
     +next-character
@@ -158,11 +150,11 @@ def render screen:address:shared:screen, editor:address:shared:editor-data -> la
     # Doing so at the start of each iteration ensures it stays one step behind
     # the current character.
     {
-      at-cursor-row?:boolean <- equal row, *cursor-row
+      at-cursor-row?:boolean <- equal row, cursor-row
       break-unless at-cursor-row?
-      at-cursor?:boolean <- equal column, *cursor-column
+      at-cursor?:boolean <- equal column, cursor-column
       break-unless at-cursor?
-      *before-cursor <- copy prev
+      before-cursor <- copy prev
     }
     c:character <- get *curr, value:offset
     <character-c-received>
@@ -172,12 +164,12 @@ def render screen:address:shared:screen, editor:address:shared:editor-data -> la
       break-unless newline?
       # adjust cursor if necessary
       {
-        at-cursor-row?:boolean <- equal row, *cursor-row
+        at-cursor-row?:boolean <- equal row, cursor-row
         break-unless at-cursor-row?
-        left-of-cursor?:boolean <- lesser-than column, *cursor-column
+        left-of-cursor?:boolean <- lesser-than column, cursor-column
         break-unless left-of-cursor?
-        *cursor-column <- copy column
-        *before-cursor <- prev curr
+        cursor-column <- copy column
+        before-cursor <- prev curr
       }
       # clear rest of line in this window
       clear-line-delimited screen, column, right
@@ -210,22 +202,23 @@ def render screen:address:shared:screen, editor:address:shared:editor-data -> la
     loop
   }
   # save first character off-screen
-  bottom-of-screen:address:address:shared:duplex-list:character <- get-address *editor, bottom-of-screen:offset
-  *bottom-of-screen <- copy curr
+  *editor <- put *editor, bottom-of-screen:offset, curr
   # is cursor to the right of the last line? move to end
   {
-    at-cursor-row?:boolean <- equal row, *cursor-row
-    cursor-outside-line?:boolean <- lesser-or-equal column, *cursor-column
+    at-cursor-row?:boolean <- equal row, cursor-row
+    cursor-outside-line?:boolean <- lesser-or-equal column, cursor-column
     before-cursor-on-same-line?:boolean <- and at-cursor-row?, cursor-outside-line?
-    above-cursor-row?:boolean <- lesser-than row, *cursor-row
+    above-cursor-row?:boolean <- lesser-than row, cursor-row
     before-cursor?:boolean <- 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
+    cursor-row <- copy row
+    cursor-column <- copy column
+    before-cursor <- copy prev
   }
-  bottom:address:number <- get-address *editor, bottom:offset
-  *bottom <- copy row
+  *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
   return row, column, screen/same-as-ingredient:0, editor/same-as-ingredient:1
 ]
 
diff --git a/sandbox/002-typing.mu b/sandbox/002-typing.mu
index 7a68bb64..1c27ce98 100644
--- a/sandbox/002-typing.mu
+++ b/sandbox/002-typing.mu
@@ -83,11 +83,11 @@ def snap-cursor screen:address:shared:screen, editor:address:shared:editor-data,
   curr <- next curr
   row:number <- copy 1/top
   column:number <- copy left
-  cursor-row:address:number <- get-address *editor, cursor-row:offset
-  *cursor-row <- copy target-row
-  cursor-column:address:number <- get-address *editor, cursor-column:offset
-  *cursor-column <- copy target-column
-  before-cursor:address:address:shared:duplex-list:character <- get-address *editor, before-cursor:offset
+  *editor <- put *editor, cursor-row:offset, target-row
+  cursor-row:number <- copy target-row
+  *editor <- put *editor, cursor-column:offset, target-column
+  cursor-column:number <- copy target-column
+  before-cursor:address:shared:duplex-list:character <- get *editor, before-cursor:offset
   {
     +next-character
     break-unless curr
@@ -97,11 +97,12 @@ def snap-cursor screen:address:shared:screen, editor:address:shared:editor-data,
     # Doing so at the start of each iteration ensures it stays one step behind
     # the current character.
     {
-      at-cursor-row?:boolean <- equal row, *cursor-row
+      at-cursor-row?:boolean <- equal row, cursor-row
       break-unless at-cursor-row?
-      at-cursor?:boolean <- equal column, *cursor-column
+      at-cursor?:boolean <- equal column, cursor-column
       break-unless at-cursor?
-      *before-cursor <- copy prev
+      before-cursor <- copy prev
+      *editor <- put *editor, before-cursor:offset, before-cursor
     }
     c:character <- get *curr, value:offset
     {
@@ -110,12 +111,14 @@ def snap-cursor screen:address:shared:screen, editor:address:shared:editor-data,
       break-unless newline?
       # adjust cursor if necessary
       {
-        at-cursor-row?:boolean <- equal row, *cursor-row
+        at-cursor-row?:boolean <- equal row, cursor-row
         break-unless at-cursor-row?
-        left-of-cursor?:boolean <- lesser-than column, *cursor-column
+        left-of-cursor?:boolean <- lesser-than column, cursor-column
         break-unless left-of-cursor?
-        *cursor-column <- copy column
-        *before-cursor <- copy prev
+        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
@@ -141,15 +144,18 @@ def snap-cursor screen:address:shared:screen, editor:address:shared:editor-data,
   }
   # is cursor to the right of the last line? move to end
   {
-    at-cursor-row?:boolean <- equal row, *cursor-row
-    cursor-outside-line?:boolean <- lesser-or-equal column, *cursor-column
+    at-cursor-row?:boolean <- equal row, cursor-row
+    cursor-outside-line?:boolean <- lesser-or-equal column, cursor-column
     before-cursor-on-same-line?:boolean <- and at-cursor-row?, cursor-outside-line?
-    above-cursor-row?:boolean <- lesser-than row, *cursor-row
+    above-cursor-row?:boolean <- lesser-than row, cursor-row
     before-cursor?:boolean <- 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
+    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
   }
 ]
 
@@ -164,11 +170,11 @@ def handle-keyboard-event screen:address:shared:screen, editor:address:shared:ed
   screen-height:number <- screen-height screen
   left:number <- get *editor, left:offset
   right:number <- get *editor, right:offset
-  before-cursor:address:address:shared:duplex-list:character <- get-address *editor, before-cursor:offset
-  cursor-row:address:number <- get-address *editor, cursor-row:offset
-  cursor-column:address:number <- get-address *editor, cursor-column:offset
-  save-row:number <- copy *cursor-row
-  save-column:number <- copy *cursor-column
+  before-cursor:address:shared:duplex-list:character <- get *editor, before-cursor:offset
+  cursor-row:number <- get *editor, cursor-row:offset
+  cursor-column:number <- get *editor, cursor-column:offset
+  save-row:number <- copy cursor-row
+  save-column:number <- copy cursor-column
   # character
   {
     c:address:character <- maybe-convert e, text:variant
@@ -198,22 +204,24 @@ def handle-keyboard-event screen:address:shared:screen, editor:address:shared:ed
 def insert-at-cursor editor:address:shared:editor-data, c:character, screen:address:shared:screen -> editor:address:shared:editor-data, screen:address:shared:screen, go-render?:boolean [
   local-scope
   load-ingredients
-  before-cursor:address:address:shared:duplex-list:character <- get-address *editor, before-cursor:offset
-  insert c, *before-cursor
-  *before-cursor <- next *before-cursor
-  cursor-row:address:number <- get-address *editor, cursor-row:offset
-  cursor-column:address:number <- get-address *editor, cursor-column:offset
+  before-cursor:address:shared:duplex-list:character <- get *editor, before-cursor:offset
+  insert c, before-cursor
+  before-cursor <- next before-cursor
+  *editor <- put *editor, before-cursor:offset, before-cursor
+  cursor-row:number <- get *editor, cursor-row:offset
+  cursor-column:number <- get *editor, cursor-column:offset
   left:number <- get *editor, left:offset
   right:number <- get *editor, right:offset
-  save-row:number <- copy *cursor-row
-  save-column:number <- copy *cursor-column
+  save-row:number <- copy cursor-row
+  save-column:number <- copy cursor-column
   screen-width:number <- screen-width screen
   screen-height:number <- screen-height screen
   # occasionally we'll need to mess with the cursor
   <insert-character-special-case>
   # but mostly we'll just move the cursor right
-  *cursor-column <- add *cursor-column, 1
-  next:address:shared:duplex-list:character <- next *before-cursor
+  cursor-column <- add cursor-column, 1
+  *editor <- put *editor, cursor-column:offset, cursor-column
+  next:address:shared:duplex-list:character <- next before-cursor
   {
     # at end of all text? no need to scroll? just print the character and leave
     at-end?:boolean <- equal next, 0/null
@@ -231,9 +239,9 @@ def insert-at-cursor editor:address:shared:editor-data, c:character, screen:addr
   {
     # not at right margin? print the character and rest of line
     break-unless next
-    at-right?:boolean <- greater-or-equal *cursor-column, screen-width
+    at-right?:boolean <- greater-or-equal cursor-column, screen-width
     break-if at-right?
-    curr:address:shared:duplex-list:character <- copy *before-cursor
+    curr:address:shared:duplex-list:character <- copy before-cursor
     move-cursor screen, save-row, save-column
     curr-column:number <- copy save-column
     {
@@ -685,14 +693,16 @@ after <insert-character-special-case> [
   {
     # if we're at the column just before the wrap indicator
     wrap-column:number <- subtract right, 1
-    at-wrap?:boolean <- greater-or-equal *cursor-column, wrap-column
+    at-wrap?:boolean <- greater-or-equal cursor-column, wrap-column
     break-unless at-wrap?
-    *cursor-column <- subtract *cursor-column, wrap-column
-    *cursor-column <- add *cursor-column, left
-    *cursor-row <- add *cursor-row, 1
+    cursor-column <- subtract cursor-column, wrap-column
+    cursor-column <- add cursor-column, left
+    *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?:boolean <- greater-or-equal *cursor-row, screen-height
+      below-screen?:boolean <- greater-or-equal cursor-row, screen-height
       break-unless below-screen?
       <scroll-down>
     }
@@ -786,8 +796,7 @@ container editor-data [
 ]
 
 after <editor-initialization> [
-  indent?:address:boolean <- get-address *result, indent?:offset
-  *indent? <- copy 1/true
+  *result <- put *result, indent?:offset, 1/true
 ]
 
 scenario editor-moves-cursor-down-after-inserting-newline [
@@ -825,30 +834,34 @@ after <handle-special-character> [
 def insert-new-line-and-indent editor:address:shared:editor-data, screen:address:shared:screen -> editor:address:shared:editor-data, screen:address:shared:screen, go-render?:boolean [
   local-scope
   load-ingredients
-  cursor-row:address:number <- get-address *editor, cursor-row:offset
-  cursor-column:address:number <- get-address *editor, cursor-column:offset
-  before-cursor:address:address:shared:duplex-list:character <- get-address *editor, before-cursor:offset
+  cursor-row:number <- get *editor, cursor-row:offset
+  cursor-column:number <- get *editor, cursor-column:offset
+  before-cursor:address:shared:duplex-list:character <- get *editor, before-cursor:offset
   left:number <- get *editor, left:offset
   right:number <- get *editor, right:offset
   screen-height:number <- screen-height screen
   # insert newline
-  insert 10/newline, *before-cursor
-  *before-cursor <- next *before-cursor
-  *cursor-row <- add *cursor-row, 1
-  *cursor-column <- copy left
+  insert 10/newline, before-cursor
+  before-cursor <- next before-cursor
+  *editor <- put *editor, before-cursor:offset, before-cursor
+  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?:boolean <- greater-or-equal *cursor-row, screen-height  # must be equal, never greater
+    below-screen?:boolean <- greater-or-equal cursor-row, screen-height  # must be equal, never greater
     break-unless below-screen?
     <scroll-down>
     go-render? <- copy 1/true
-    *cursor-row <- subtract *cursor-row, 1  # bring back into screen range
+    cursor-row <- subtract cursor-row, 1  # bring back into screen range
+    *editor <- put *editor, cursor-row:offset, cursor-row
   }
   # indent if necessary
   indent?:boolean <- get *editor, indent?:offset
   return-unless indent?
   d:address:shared:duplex-list:character <- get *editor, data:offset
-  end-of-previous-line:address:shared:duplex-list:character <- prev *before-cursor
+  end-of-previous-line:address:shared:duplex-list:character <- prev before-cursor
   indent:number <- line-indent end-of-previous-line, d
   i:number <- copy 0
   {
@@ -992,8 +1005,7 @@ after <handle-special-key> [
   {
     paste-start?:boolean <- equal *k, 65507/paste-start
     break-unless paste-start?
-    indent?:address:boolean <- get-address *editor, indent?:offset
-    *indent? <- copy 0/false
+    *editor <- put *editor, indent?:offset, 0/false
     go-render? <- copy 1/true
     return
   }
@@ -1003,8 +1015,7 @@ after <handle-special-key> [
   {
     paste-end?:boolean <- equal *k, 65506/paste-end
     break-unless paste-end?
-    indent?:address:boolean <- get-address *editor, indent?:offset
-    *indent? <- copy 1/true
+    *editor <- put *editor, indent?:offset, 1/true
     go-render? <- copy 1/true
     return
   }
diff --git a/sandbox/003-shortcuts.mu b/sandbox/003-shortcuts.mu
index c09c3441..f29773af 100644
--- a/sandbox/003-shortcuts.mu
+++ b/sandbox/003-shortcuts.mu
@@ -83,18 +83,19 @@ after <handle-special-character> [
 def delete-before-cursor editor:address:shared:editor-data, screen:address:shared:screen -> editor:address:shared:editor-data, screen:address:shared:screen, go-render?:boolean, backspaced-cell:address:shared:duplex-list:character [
   local-scope
   load-ingredients
-  before-cursor:address:address:shared:duplex-list:character <- get-address *editor, before-cursor:offset
+  before-cursor:address:shared:duplex-list:character <- get *editor, before-cursor:offset
   data:address:shared:duplex-list:character <- get *editor, data:offset
   # if at start of text (before-cursor at § sentinel), return
-  prev:address:shared:duplex-list:character <- prev *before-cursor
+  prev:address:shared:duplex-list:character <- prev before-cursor
   go-render?, backspaced-cell <- copy 0/no-more-render, 0/nothing-deleted
   return-unless prev
   trace 10, [app], [delete-before-cursor]
   original-row:number <- get *editor, cursor-row:offset
   editor, scroll?:boolean <- move-cursor-coordinates-left editor
-  backspaced-cell:address:shared: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
+  backspaced-cell:address:shared: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
@@ -106,7 +107,7 @@ def delete-before-cursor editor:address:shared:editor-data, screen:address:share
   return-unless same-row?
   left:number <- get *editor, left:offset
   right:number <- get *editor, right:offset
-  curr:address:shared:duplex-list:character <- next *before-cursor
+  curr:address:shared:duplex-list:character <- next before-cursor
   screen <- move-cursor screen, cursor-row, cursor-column
   curr-column:number <- copy cursor-column
   {
@@ -134,24 +135,26 @@ def move-cursor-coordinates-left editor:address:shared:editor-data -> editor:add
   local-scope
   load-ingredients
   before-cursor:address:shared:duplex-list:character <- get *editor, before-cursor:offset
-  cursor-row:address:number <- get-address *editor, cursor-row:offset
-  cursor-column:address:number <- get-address *editor, cursor-column:offset
+  cursor-row:number <- get *editor, cursor-row:offset
+  cursor-column:number <- get *editor, cursor-column:offset
   left:number <- get *editor, left:offset
   # if not at left margin, move one character left
   {
-    at-left-margin?:boolean <- equal *cursor-column, left
+    at-left-margin?:boolean <- equal cursor-column, left
     break-if at-left-margin?
     trace 10, [app], [decrementing cursor column]
-    *cursor-column <- subtract *cursor-column, 1
+    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
+  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
+    cursor-row <- subtract cursor-row, 1
+    *editor <- put *editor, cursor-row:offset, cursor-row
   }
   {
     break-unless top-of-screen?
@@ -173,18 +176,21 @@ def move-cursor-coordinates-left editor:address:shared:editor-data -> editor:add
     {
       break-unless wrap?
       _, column-offset:number <- divide-with-remainder end-of-line, width
-      *cursor-column <- add left, column-offset
+      cursor-column <- add left, column-offset
+      *editor <- put *editor, cursor-column:offset, cursor-column
     }
     {
       break-if wrap?
-      *cursor-column <- add left, end-of-line
+      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:number <- get *editor, right:offset
-  *cursor-column <- subtract right, 1  # leave room for wrap icon
+  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
@@ -345,9 +351,9 @@ after <handle-special-key> [
 def delete-at-cursor editor:address:shared:editor-data, screen:address:shared:screen -> editor:address:shared:editor-data, screen:address:shared:screen, go-render?:boolean, deleted-cell:address:shared:duplex-list:character [
   local-scope
   load-ingredients
-  before-cursor:address:address:shared:duplex-list:character <- get-address *editor, before-cursor:offset
+  before-cursor:address:shared:duplex-list:character <- get *editor, before-cursor:offset
   data:address:shared:duplex-list:character <- get *editor, data:offset
-  deleted-cell:address:shared:duplex-list:character <- next *before-cursor
+  deleted-cell:address:shared:duplex-list:character <- next before-cursor
   go-render? <- copy 0/false
   return-unless deleted-cell
   currc:character <- get *deleted-cell, value:offset
@@ -356,11 +362,11 @@ def delete-at-cursor editor:address:shared:editor-data, screen:address:shared:sc
   go-render? <- copy 1/true
   return-if deleted-newline?
   # wasn't a newline? render rest of line
-  curr:address:shared:duplex-list:character <- next *before-cursor  # refresh after remove above
-  cursor-row:address:number <- get-address *editor, cursor-row:offset
-  cursor-column:address:number <- get-address *editor, cursor-column:offset
-  screen <- move-cursor screen, *cursor-row, *cursor-column
-  curr-column:number <- copy *cursor-column
+  curr:address:shared:duplex-list:character <- next before-cursor  # refresh after remove above
+  cursor-row:number <- get *editor, cursor-row:offset
+  cursor-column:number <- get *editor, cursor-column:offset
+  screen <- move-cursor screen, cursor-row, cursor-column
+  curr-column:number <- copy cursor-column
   screen-width:number <- screen-width screen
   {
     # hit right margin? give up and let caller render
@@ -412,13 +418,14 @@ after <handle-special-key> [
     move-to-next-character?:boolean <- equal *k, 65514/right-arrow
     break-unless move-to-next-character?
     # if not at end of text
-    next-cursor:address:shared:duplex-list:character <- next *before-cursor
+    next-cursor:address:shared:duplex-list:character <- next before-cursor
     break-unless next-cursor
     # scan to next character
     <move-cursor-begin>
-    *before-cursor <- copy next-cursor
+    before-cursor <- copy next-cursor
+    *editor <- put *editor, before-cursor:offset, before-cursor
     editor, go-render?:boolean <- move-cursor-coordinates-right editor, screen-height
-    screen <- move-cursor screen, *cursor-row, *cursor-column
+    screen <- move-cursor screen, cursor-row, cursor-column
     undo-coalesce-tag:number <- copy 2/right-arrow
     <move-cursor-end>
     return
@@ -429,8 +436,8 @@ def move-cursor-coordinates-right editor:address:shared:editor-data, screen-heig
   local-scope
   load-ingredients
   before-cursor:address:shared:duplex-list:character <- get *editor before-cursor:offset
-  cursor-row:address:number <- get-address *editor, cursor-row:offset
-  cursor-column:address:number <- get-address *editor, cursor-column:offset
+  cursor-row:number <- get *editor, cursor-row:offset
+  cursor-column:number <- get *editor, cursor-column:offset
   left:number <- get *editor, left:offset
   right:number <- get *editor, right:offset
   # if crossed a newline, move cursor to start of next row
@@ -438,13 +445,16 @@ def move-cursor-coordinates-right editor:address:shared:editor-data, screen-heig
     old-cursor-character:character <- get *before-cursor, value:offset
     was-at-newline?:boolean <- equal old-cursor-character, 10/newline
     break-unless was-at-newline?
-    *cursor-row <- add *cursor-row, 1
-    *cursor-column <- copy left
-    below-screen?:boolean <- greater-or-equal *cursor-row, screen-height  # must be equal
+    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?: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
+    cursor-row <- subtract cursor-row, 1  # bring back into screen range
+    *editor <- put *editor, cursor-row:offset, cursor-row
     go-render? <- copy 1/true
     return
   }
@@ -452,7 +462,7 @@ def move-cursor-coordinates-right editor:address:shared:editor-data, screen-heig
   {
     # if we're at the column just before the wrap indicator
     wrap-column:number <- subtract right, 1
-    at-wrap?:boolean <- equal *cursor-column, wrap-column
+    at-wrap?:boolean <- equal cursor-column, wrap-column
     break-unless at-wrap?
     # and if next character isn't newline
     next:address:shared:duplex-list:character <- next before-cursor
@@ -460,17 +470,21 @@ def move-cursor-coordinates-right editor:address:shared:editor-data, screen-heig
     next-character:character <- get *next, value:offset
     newline?:boolean <- equal next-character, 10/newline
     break-if newline?
-    *cursor-row <- add *cursor-row, 1
-    *cursor-column <- copy left
-    below-screen?:boolean <- greater-or-equal *cursor-row, screen-height  # must be equal
+    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?: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
+    cursor-row <- subtract cursor-row, 1  # bring back into screen range
+    *editor <- put *editor, cursor-row:offset, cursor-row
     go-render? <- copy 1/true
     return
   }
   # otherwise move cursor one character right
-  *cursor-column <- add *cursor-column, 1
+  cursor-column <- add cursor-column, 1
+  *editor <- put *editor, cursor-column:offset, cursor-column
   go-render? <- copy 0/false
 ]
 
@@ -689,12 +703,13 @@ after <handle-special-key> [
     break-unless move-to-previous-character?
     trace 10, [app], [left arrow]
     # if not at start of text (before-cursor at § sentinel)
-    prev:address:shared:duplex-list:character <- prev *before-cursor
+    prev:address:shared:duplex-list:character <- prev before-cursor
     go-render? <- copy 0/false
     return-unless prev
     <move-cursor-begin>
     editor, go-render? <- move-cursor-coordinates-left editor
-    *before-cursor <- copy prev
+    before-cursor <- copy prev
+    *editor <- put *editor, before-cursor:offset, before-cursor
     undo-coalesce-tag:number <- copy 1/left-arrow
     <move-cursor-end>
     return
@@ -961,19 +976,19 @@ after <handle-special-key> [
 def move-to-previous-line editor:address:shared:editor-data -> editor:address:shared:editor-data, go-render?:boolean [
   local-scope
   load-ingredients
-  cursor-row:address:number <- get-address *editor, cursor-row:offset
-  cursor-column:address:number <- get-address *editor, cursor-column:offset
-  before-cursor:address:address:shared:duplex-list:character <- get-address *editor, before-cursor:offset
+  cursor-row:number <- get *editor, cursor-row:offset
+  cursor-column:number <- get *editor, cursor-column:offset
+  before-cursor:address:shared:duplex-list:character <- get *editor, before-cursor:offset
   left:number <- get *editor, left:offset
   right:number <- get *editor, right:offset
-  already-at-top?:boolean <- lesser-or-equal *cursor-row, 1/top
+  already-at-top?:boolean <- 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:address:shared:duplex-list:character <- copy *before-cursor
+    curr:address:shared:duplex-list:character <- copy before-cursor
     {
       old:address:shared:duplex-list:character <- copy curr
       c2:character <- get *curr, value:offset
@@ -991,22 +1006,27 @@ def move-to-previous-line editor:address:shared:editor-data -> editor:address:sh
       go-render? <- copy 0/false
       return-if no-motion?
     }
-    *before-cursor <- copy curr
-    *cursor-row <- subtract *cursor-row, 1
+    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:number <- copy *cursor-column
-    *cursor-column <- copy left
+    target-column:number <- copy cursor-column
+    cursor-column <- copy left
+    *editor <- put *editor, cursor-column:offset, cursor-column
     {
-      done?:boolean <- greater-or-equal *cursor-column, target-column
+      done?:boolean <- greater-or-equal cursor-column, target-column
       break-if done?
-      curr:address:shared:duplex-list:character <- next *before-cursor
+      curr:address:shared: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
-      *cursor-column <- add *cursor-column, 1
+      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
@@ -1186,44 +1206,49 @@ after <handle-special-key> [
 def move-to-next-line editor:address:shared:editor-data, screen-height:number -> editor:address:shared:editor-data, go-render?:boolean [
   local-scope
   load-ingredients
-  cursor-row:address:number <- get-address *editor, cursor-row:offset
-  cursor-column:address:number <- get-address *editor, cursor-column:offset
-  before-cursor:address:address:shared:duplex-list:character <- get-address *editor, before-cursor:offset
+  cursor-row:number <- get *editor, cursor-row:offset
+  cursor-column:number <- get *editor, cursor-column:offset
+  before-cursor:address:shared:duplex-list:character <- get *editor, before-cursor:offset
   left:number <- get *editor, left:offset
   right:number <- get *editor, right:offset
   last-line:number <- subtract screen-height, 1
-  already-at-bottom?:boolean <- greater-or-equal *cursor-row, last-line
+  already-at-bottom?:boolean <- greater-or-equal cursor-row, last-line
   {
     # 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:shared:duplex-list:character <- before-start-of-next-line *before-cursor, max
+    next-line:address:shared: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
+      no-motion?:boolean <- equal next-line, before-cursor
       break-unless no-motion?
-      scroll?:boolean <- greater-than *cursor-row, 1
+      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
-    *before-cursor <- copy next-line
-    target-column:number <- copy *cursor-column
-    *cursor-column <- copy left
+    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
     {
-      done?:boolean <- greater-or-equal *cursor-column, target-column
+      done?:boolean <- greater-or-equal cursor-column, target-column
       break-if done?
-      curr:address:shared:duplex-list:character <- next *before-cursor
+      curr:address:shared: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
-      *cursor-column <- add *cursor-column, 1
+      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
@@ -1328,20 +1353,21 @@ def move-to-start-of-line editor:address:shared:editor-data -> editor:address:sh
   load-ingredients
   # update cursor column
   left:number <- get *editor, left:offset
-  cursor-column:address:number <- get-address *editor, cursor-column:offset
-  *cursor-column <- copy left
+  cursor-column:number <- copy left
+  *editor <- put *editor, cursor-column:offset, cursor-column
   # update before-cursor
-  before-cursor:address:address:shared:duplex-list:character <- get-address *editor, before-cursor:offset
+  before-cursor:address:shared:duplex-list:character <- get *editor, before-cursor:offset
   init:address:shared:duplex-list:character <- get *editor, data:offset
   # while not at start of line, move 
   {
-    at-start-of-text?:boolean <- equal *before-cursor, init
+    at-start-of-text?:boolean <- equal before-cursor, init
     break-if at-start-of-text?
-    prev:character <- get **before-cursor, value:offset
+    prev:character <- get *before-cursor, value:offset
     at-start-of-line?:boolean <- equal prev, 10/newline
     break-if at-start-of-line?
-    *before-cursor <- prev *before-cursor
-    assert *before-cursor, [move-to-start-of-line tried to move before start of text]
+    before-cursor <- prev before-cursor
+    *editor <- put *editor, before-cursor:offset, before-cursor
+    assert before-cursor, [move-to-start-of-line tried to move before start of text]
     loop
   }
 ]
@@ -1497,17 +1523,19 @@ after <handle-special-key> [
 def move-to-end-of-line editor:address:shared:editor-data -> editor:address:shared:editor-data [
   local-scope
   load-ingredients
-  before-cursor:address:address:shared:duplex-list:character <- get-address *editor, before-cursor:offset
-  cursor-column:address:number <- get-address *editor, cursor-column:offset
+  before-cursor:address:shared:duplex-list:character <- get *editor, before-cursor:offset
+  cursor-column:number <- get *editor, cursor-column:offset
   # while not at start of line, move 
   {
-    next:address:shared:duplex-list:character <- next *before-cursor
+    next:address:shared:duplex-list:character <- next before-cursor
     break-unless next  # end of text
     nextc:character <- get *next, value:offset
     at-end-of-line?:boolean <- equal nextc, 10/newline
     break-if at-end-of-line?
-    *before-cursor <- copy next
-    *cursor-column <- add *cursor-column, 1
+    before-cursor <- copy next
+    *editor <- put *editor, before-cursor:offset, before-cursor
+    cursor-column <- add cursor-column, 1
+    *editor <- put *editor, cursor-column:offset, cursor-column
     loop
   }
 ]
@@ -1629,9 +1657,9 @@ def delete-to-start-of-line editor:address:shared:editor-data -> result:address:
   load-ingredients
   # compute range to delete
   init:address:shared:duplex-list:character <- get *editor, data:offset
-  before-cursor:address:address:shared:duplex-list:character <- get-address *editor, before-cursor:offset
-  start:address:shared:duplex-list:character <- copy *before-cursor
-  end:address:shared:duplex-list:character <- next *before-cursor
+  before-cursor:address:shared:duplex-list:character <- get *editor, before-cursor:offset
+  start:address:shared:duplex-list:character <- copy before-cursor
+  end:address:shared:duplex-list:character <- next before-cursor
   {
     at-start-of-text?:boolean <- equal start, init
     break-if at-start-of-text?
@@ -1646,10 +1674,10 @@ def delete-to-start-of-line editor:address:shared:editor-data -> result:address:
   result:address:shared:duplex-list:character <- next start
   remove-between start, end
   # adjust cursor
-  *before-cursor <- copy start
+  before-cursor <- copy start
+  *editor <- put *editor, before-cursor:offset, before-cursor
   left:number <- get *editor, left:offset
-  cursor-column:address:number <- get-address *editor, cursor-column:offset
-  *cursor-column <- copy left
+  *editor <- put *editor, cursor-column:offset, left
 ]
 
 scenario editor-deletes-to-start-of-line-with-ctrl-u-2 [
@@ -1929,13 +1957,14 @@ d]
 
 after <scroll-down> [
   trace 10, [app], [scroll down]
-  top-of-screen:address:address:shared:duplex-list:character <- get-address *editor, top-of-screen:offset
+  top-of-screen:address:shared: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:shared:duplex-list:character <- copy *top-of-screen
-  *top-of-screen <- before-start-of-next-line *top-of-screen, max
-  no-movement?:boolean <- equal old-top, *top-of-screen
+  old-top:address:shared: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?
 ]
@@ -2299,10 +2328,11 @@ d]
 
 after <scroll-up> [
   trace 10, [app], [scroll up]
-  top-of-screen:address:address:shared:duplex-list:character <- get-address *editor, top-of-screen:offset
-  old-top:address:shared:duplex-list:character <- copy *top-of-screen
-  *top-of-screen <- before-previous-line *top-of-screen, editor
-  no-movement?:boolean <- equal old-top, *top-of-screen
+  top-of-screen:address:shared:duplex-list:character <- get *editor, top-of-screen:offset
+  old-top:address:shared: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?
 ]
@@ -2690,13 +2720,13 @@ after <handle-special-character> [
   {
     page-down?:boolean <- equal *c, 6/ctrl-f
     break-unless page-down?
-    top-of-screen:address:address:shared:duplex-list:character <- get-address *editor, top-of-screen:offset
-    old-top:address:shared:duplex-list:character <- copy *top-of-screen
+    old-top:address:shared: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>
-    no-movement?:boolean <- equal *top-of-screen, old-top
+    top-of-screen:address:shared:duplex-list:character <- get *editor, top-of-screen:offset
+    no-movement?:boolean <- equal top-of-screen, old-top
     go-render? <- not no-movement?
     return
   }
@@ -2706,13 +2736,13 @@ after <handle-special-key> [
   {
     page-down?:boolean <- equal *k, 65518/page-down
     break-unless page-down?
-    top-of-screen:address:address:shared:duplex-list:character <- get-address *editor, top-of-screen:offset
-    old-top:address:shared:duplex-list:character <- copy *top-of-screen
+    old-top:address:shared: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>
-    no-movement?:boolean <- equal *top-of-screen, old-top
+    top-of-screen:address:shared:duplex-list:character <- get *editor, top-of-screen:offset
+    no-movement?:boolean <- equal top-of-screen, old-top
     go-render? <- not no-movement?
     return
   }
@@ -2727,19 +2757,21 @@ def page-down editor:address:shared:editor-data -> editor:address:shared:editor-
   bottom-of-screen:address:shared:duplex-list:character <- get *editor, bottom-of-screen:offset
   return-unless bottom-of-screen
   # if not, position cursor at final character
-  before-cursor:address:address:shared:duplex-list:character <- get-address *editor, before-cursor:offset
-  *before-cursor <- prev bottom-of-screen
+  before-cursor:address:shared:duplex-list:character <- get *editor, before-cursor:offset
+  before-cursor:address:shared: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
+    last:character <- get *before-cursor, value:offset
     newline?:boolean <- equal last, 10/newline
     break-unless newline?:boolean
-    *before-cursor <- prev *before-cursor
+    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
-  top-of-screen:address:address:shared:duplex-list:character <- get-address *editor, top-of-screen:offset
-  *top-of-screen <- copy *before-cursor
+  before-cursor <- get *editor, before-cursor:offset
+  *editor <- put *editor, top-of-screen:offset, before-cursor
 ]
 
 scenario editor-does-not-scroll-past-end [
@@ -2882,13 +2914,13 @@ after <handle-special-character> [
   {
     page-up?:boolean <- equal *c, 2/ctrl-b
     break-unless page-up?
-    top-of-screen:address:address:shared:duplex-list:character <- get-address *editor, top-of-screen:offset
-    old-top:address:shared:duplex-list:character <- copy *top-of-screen
+    old-top:address:shared: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>
-    no-movement?:boolean <- equal *top-of-screen, old-top
+    top-of-screen:address:shared:duplex-list:character <- get *editor, top-of-screen:offset
+    no-movement?:boolean <- equal top-of-screen, old-top
     go-render? <- not no-movement?
     return
   }
@@ -2898,13 +2930,13 @@ after <handle-special-key> [
   {
     page-up?:boolean <- equal *k, 65519/page-up
     break-unless page-up?
-    top-of-screen:address:address:shared:duplex-list:character <- get-address *editor, top-of-screen:offset
-    old-top:address:shared:duplex-list:character <- copy *top-of-screen
+    old-top:address:shared: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>
-    no-movement?:boolean <- equal *top-of-screen, old-top
+    top-of-screen:address:shared: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
@@ -2916,13 +2948,14 @@ def page-up editor:address:shared:editor-data, screen-height:number -> editor:ad
   load-ingredients
   max:number <- subtract screen-height, 1/menu-bar, 1/overlapping-line
   count:number <- copy 0
-  top-of-screen:address:address:shared:duplex-list:character <- get-address *editor, top-of-screen:offset
+  top-of-screen:address:shared:duplex-list:character <- get *editor, top-of-screen:offset
   {
     done?:boolean <- greater-or-equal count, max
     break-if done?
-    prev:address:shared:duplex-list:character <- before-previous-line *top-of-screen, editor
+    prev:address:shared:duplex-list:character <- before-previous-line top-of-screen, editor
     break-unless prev
-    *top-of-screen <- copy prev
+    top-of-screen <- copy prev
+    *editor <- put *editor, top-of-screen:offset, top-of-screen
     count <- add count, 1
     loop
   }
diff --git a/sandbox/004-programming-environment.mu b/sandbox/004-programming-environment.mu
index bbbb45e2..a329c8c1 100644
--- a/sandbox/004-programming-environment.mu
+++ b/sandbox/004-programming-environment.mu
@@ -33,8 +33,8 @@ def new-programming-environment screen:address:shared:screen, initial-sandbox-co
   screen <- move-cursor screen, 0/row, button-start
   print screen, [ run (F4) ], 255/white, 161/reddish
   # sandbox editor
-  current-sandbox:address:address:shared:editor-data <- get-address *result, current-sandbox:offset
-  *current-sandbox <- new-editor initial-sandbox-contents, screen, 0, width/right
+  current-sandbox:address:shared:editor-data <- new-editor initial-sandbox-contents, screen, 0, width/right
+  *result <- put *result, current-sandbox:offset, current-sandbox
   <programming-environment-initialization>
 ]
 
@@ -142,13 +142,11 @@ def resize screen:address:shared:screen, env:address:shared:programming-environm
   width:number <- screen-width screen
   # update sandbox editor
   current-sandbox:address:shared:editor-data <- get *env, current-sandbox:offset
-  right:address:number <- get-address *current-sandbox, right:offset
-  *right <- subtract width, 1
+  right:number <- subtract width, 1
+  *current-sandbox <- put *current-sandbox right:offset, right
   # reset cursor
-  cursor-row:address:number <- get-address *current-sandbox, cursor-row:offset
-  *cursor-row <- copy 1
-  cursor-column:address:number <- get-address *current-sandbox, cursor-column:offset
-  *cursor-column <- copy 0
+  *current-sandbox <- put *current-sandbox, cursor-row:offset, 1
+  *current-sandbox <- put *current-sandbox, cursor-column:offset, 0
 ]
 
 def render-all screen:address:shared:screen, env:address:shared:programming-environment-data -> screen:address:shared:screen, env:address:shared:programming-environment-data [
diff --git a/sandbox/005-sandbox.mu b/sandbox/005-sandbox.mu
index 57a4f5cf..0b6bd79c 100644
--- a/sandbox/005-sandbox.mu
+++ b/sandbox/005-sandbox.mu
@@ -11,8 +11,7 @@ container programming-environment-data [
 ]
 
 after <programming-environment-initialization> [
-  render-from:address:number <- get-address *result, render-from:offset
-  *render-from <- copy -1
+  *result <- put *result, render-from:offset, -1
 ]
 
 container sandbox-data [
@@ -140,21 +139,19 @@ def run-sandboxes env:address:shared:programming-environment-data, screen:addres
     # if contents exist, first save them
     # run them and turn them into a new sandbox-data
     new-sandbox:address:shared:sandbox-data <- new sandbox-data:type
-    data:address:address:shared:array:character <- get-address *new-sandbox, data:offset
-    *data <- copy sandbox-contents
+    *new-sandbox <- put *new-sandbox, data:offset, sandbox-contents
     # push to head of sandbox list
-    dest:address:address:shared:sandbox-data <- get-address *env, sandbox:offset
-    next:address:address:shared:sandbox-data <- get-address *new-sandbox, next-sandbox:offset
-    *next <- copy *dest
-    *dest <- copy new-sandbox
+    dest:address:shared:sandbox-data <- 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:address:number <- get-address *env, number-of-sandboxes:offset
-    *sandbox-count <- add *sandbox-count, 1
+    sandbox-count:number <- get *env, number-of-sandboxes:offset
+    sandbox-count <- add sandbox-count, 1
+    *env <- put *env, number-of-sandboxes:offset, sandbox-count
     # clear sandbox editor
-    init:address:address:shared:duplex-list:character <- get-address *current-sandbox, data:offset
-    *init <- push 167/§, 0/tail
-    top-of-screen:address:address:shared:duplex-list:character <- get-address *current-sandbox, top-of-screen:offset
-    *top-of-screen <- copy *init
+    init:address:shared:duplex-list:character <- push 167/§, 0/tail
+    *current-sandbox <- put *current-sandbox, data:offset, init
+    *current-sandbox <- put *current-sandbox, top-of-screen:offset, init
   }
   # save all sandboxes before running, just in case we die when running
   save-sandboxes env
@@ -193,9 +190,9 @@ def! update-sandbox sandbox:address:shared:sandbox-data, env:address:shared:prog
   local-scope
   load-ingredients
   data:address:shared:array:character <- get *sandbox, data:offset
-  response:address:address:shared:array:character <- get-address *sandbox, response:offset
-  fake-screen:address:address:shared:screen <- get-address *sandbox, screen:offset
-  *response, _, *fake-screen <- run-interactive data
+  response:address:shared:array:character, _, fake-screen:address:shared:screen <- run-interactive data
+  *sandbox <- put *sandbox, response:offset, response
+  *sandbox <- put *sandbox, screen:offset, fake-screen
 ]
 
 def update-status screen:address:shared:screen, msg:address:shared:array:character, color:number -> screen:address:shared:screen [
@@ -268,15 +265,13 @@ def render-sandboxes screen:address:shared:screen, sandbox:address:shared:sandbo
     delete-icon:character <- copy 120/x
     print screen, delete-icon, 245/grey
     # save menu row so we can detect clicks to it later
-    starting-row:address:number <- get-address *sandbox, starting-row-on-screen:offset
-    *starting-row <- copy row
+    *sandbox <- put *sandbox, starting-row-on-screen:offset, row
     # render sandbox contents
     row <- add row, 1
     screen <- move-cursor screen, row, left
     sandbox-data:address:shared:array:character <- get *sandbox, data:offset
     row, screen <- render-code screen, sandbox-data, left, right, row
-    code-ending-row:address:number <- get-address *sandbox, code-ending-row-on-screen:offset
-    *code-ending-row <- copy row
+    *sandbox <- put *sandbox, code-ending-row-on-screen:offset, row
     # render sandbox warnings, screen or response, in that order
     sandbox-response:address:shared:array:character <- get *sandbox, response:offset
     <render-sandbox-results>
@@ -300,10 +295,8 @@ def render-sandboxes screen:address:shared:screen, sandbox:address:shared:sandbo
   # if hidden, reset row attributes
   {
     break-unless hidden?
-    tmp:address:number <- get-address *sandbox, starting-row-on-screen:offset
-    *tmp <- copy 0
-    tmp:address:number <- get-address *sandbox, code-ending-row-on-screen:offset
-    *tmp <- copy 0
+    *sandbox <- put *sandbox, starting-row-on-screen:offset, 0
+    *sandbox <- put *sandbox, code-ending-row-on-screen:offset, 0
     <end-render-sandbox-reset-hidden>
   }
   # draw next sandbox
@@ -318,15 +311,15 @@ def! restore-sandboxes env:address:shared:programming-environment-data -> env:ad
   load-ingredients
   # read all scenarios, pushing them to end of a list of scenarios
   idx:number <- copy 0
-  curr:address:address:shared:sandbox-data <- get-address *env, sandbox:offset
+  curr:address:shared:sandbox-data <- copy 0
+  prev:address:shared:sandbox-data <- copy 0
   {
     filename:address:shared:array:character <- to-text idx
     contents:address:shared:array:character <- restore filename
     break-unless contents  # stop at first error; assuming file didn't exist
     # create new sandbox for file
-    *curr <- new sandbox-data:type
-    data:address:address:shared:array:character <- get-address **curr, data:offset
-    *data <- copy contents
+    curr <- new sandbox-data:type
+    *curr <- put *curr, data:offset, contents
     # restore expected output for sandbox if it exists
     {
       filename <- append filename, [.out]
@@ -336,12 +329,16 @@ def! restore-sandboxes env:address:shared:programming-environment-data -> env:ad
     }
     +continue
     idx <- add idx, 1
-    curr <- get-address **curr, next-sandbox:offset
+    {
+      break-unless prev
+      *prev <- put *prev, next-sandbox:offset, curr
+    }
+    prev <- copy curr
     loop
   }
+  *env <- put *env, sandbox:offset, curr
   # update sandbox count
-  number-of-sandboxes:address:number <- get-address *env, number-of-sandboxes:offset
-  *number-of-sandboxes <- copy idx
+  *env <- put *env, number-of-sandboxes:offset, idx
 ]
 
 # print the fake sandbox screen to 'screen' with appropriate delimiters
@@ -619,12 +616,13 @@ after <global-keypress> [
     break-unless sandbox
     # slide down if possible
     {
-      render-from:address:number <- get-address *env, render-from:offset
+      render-from:number <- get *env, render-from:offset
       number-of-sandboxes:number <- get *env, number-of-sandboxes:offset
       max:number <- subtract number-of-sandboxes, 1
-      at-end?:boolean <- greater-or-equal *render-from, max
+      at-end?:boolean <- greater-or-equal render-from, max
       break-if at-end?
-      *render-from <- add *render-from, 1
+      render-from <- add render-from, 1
+      *env <- put *env, render-from:offset, render-from
     }
     hide-screen screen
     screen <- render-sandbox-side screen, env
@@ -650,10 +648,11 @@ after <global-keypress> [
   {
     up?:boolean <- equal *k, 65517/up-arrow
     break-unless up?
-    render-from:address:number <- get-address *env, render-from:offset
-    at-beginning?:boolean <- equal *render-from, -1
+    render-from:number <- get *env, render-from:offset
+    at-beginning?:boolean <- equal render-from, -1
     break-if at-beginning?
-    *render-from <- subtract *render-from, 1
+    render-from <- subtract render-from, 1
+    *env <- put *env, render-from:offset, render-from
     hide-screen screen
     screen <- render-sandbox-side screen, env
     show-screen screen
diff --git a/sandbox/006-sandbox-edit.mu b/sandbox/006-sandbox-edit.mu
index d39ec118..c863efe1 100644
--- a/sandbox/006-sandbox-edit.mu
+++ b/sandbox/006-sandbox-edit.mu
@@ -83,8 +83,7 @@ after <global-touch> [
     break-unless sandbox
     text:address:shared:array:character <- get *sandbox, data:offset
     current-sandbox <- insert-text current-sandbox, text
-    render-from:address:number <- get-address *env, render-from:offset
-    *render-from <- copy -1
+    *env <- put *env, render-from:offset, -1
     hide-screen screen
     screen <- render-sandbox-side screen, env
     screen <- update-cursor screen, current-sandbox, env
@@ -104,26 +103,41 @@ def empty-editor? editor:address:shared:editor-data -> result:boolean [
 def extract-sandbox env:address:shared:programming-environment-data, click-row:number -> result:address:shared:sandbox-data, env:address:shared:programming-environment-data [
   local-scope
   load-ingredients
-  sandbox:address:address:shared:sandbox-data <- get-address *env, sandbox:offset
-  start:number <- get **sandbox, starting-row-on-screen:offset
+  curr-sandbox:address:shared:sandbox-data <- get *env, sandbox:offset
+  start:number <- get *curr-sandbox, starting-row-on-screen:offset
   in-editor?:boolean <- lesser-than click-row, start
   return-if in-editor?, 0
+  first-sandbox?:boolean <- equal click-row, start
   {
-    next-sandbox:address:shared:sandbox-data <- get **sandbox, next-sandbox:offset
-    break-unless next-sandbox
-    # if click-row < sandbox.next-sandbox.starting-row-on-screen, break
-    next-start:number <- get *next-sandbox, starting-row-on-screen:offset
-    found?:boolean <- lesser-than click-row, next-start
-    break-if found?
-    sandbox <- get-address **sandbox, next-sandbox:offset
-    loop
+    # first sandbox? pop
+    break-unless first-sandbox?
+    next-sandbox:address:shared:sandbox-data <- get *curr-sandbox, next-sandbox:offset
+    *env <- put *env, sandbox:offset, next-sandbox
   }
-  # snip sandbox out of its list
-  result <- copy *sandbox
-  *sandbox <- copy next-sandbox
+  {
+    # not first sandbox?
+    break-if first-sandbox?
+    prev-sandbox:address:shared:sandbox-data <- copy curr-sandbox
+    curr-sandbox <- get *curr-sandbox, next-sandbox:offset
+    {
+      next-sandbox:address:shared:sandbox-data <- get *curr-sandbox, next-sandbox:offset
+      break-unless next-sandbox
+      # if click-row < sandbox.next-sandbox.starting-row-on-screen, break
+      next-start:number <- get *next-sandbox, starting-row-on-screen:offset
+      found?:boolean <- lesser-than click-row, next-start
+      break-if found?
+      prev-sandbox <- copy curr-sandbox
+      curr-sandbox <- copy next-sandbox
+      loop
+    }
+    # snip sandbox out of its list
+    *prev-sandbox <- put *prev-sandbox, next-sandbox:offset, next-sandbox
+  }
+  result <- copy curr-sandbox
   # update sandbox count
-  sandbox-count:address:number <- get-address *env, number-of-sandboxes:offset
-  *sandbox-count <- subtract *sandbox-count, 1
+  sandbox-count:number <- get *env, number-of-sandboxes:offset
+  sandbox-count <- subtract sandbox-count, 1
+  *env <- put *env, number-of-sandboxes:offset, sandbox-count
 ]
 
 scenario sandbox-with-print-can-be-edited [
diff --git a/sandbox/007-sandbox-delete.mu b/sandbox/007-sandbox-delete.mu
index dbf5d70c..f99012b6 100644
--- a/sandbox/007-sandbox-delete.mu
+++ b/sandbox/007-sandbox-delete.mu
@@ -85,8 +85,20 @@ def delete-sandbox t:touch-event, env:address:shared:programming-environment-dat
   at-right?:boolean <- equal click-column, right
   return-unless at-right?, 0/false
   click-row:number <- get t, row:offset
-  prev:address:address:shared:sandbox-data <- get-address *env, sandbox:offset
-  curr:address:shared:sandbox-data <- get *env, sandbox:offset
+  {
+    first:address:shared:sandbox-data <- get *env, sandbox:offset
+    reply-unless first, 0/false
+    target-row:number <- get *first, starting-row-on-screen:offset
+    delete-first?:boolean <- equal target-row, click-row
+    break-unless delete-first?
+    new-first:address:shared:sandbox-data <- get *first, next-sandbox:offset
+    *env <- put *env, sandbox:offset, new-first
+    env <- fixup-delete env, new-first
+    return 1/true  # force rerender
+  }
+  prev:address:shared:sandbox-data <- get *env, sandbox:offset
+  assert prev, [failed to find any sandboxes!]
+  curr:address:shared:sandbox-data <- get *prev, next-sandbox:offset
   {
     break-unless curr
     # more sandboxes to check
@@ -95,27 +107,36 @@ def delete-sandbox t:touch-event, env:address:shared:programming-environment-dat
       delete-curr?:boolean <- equal target-row, click-row
       break-unless delete-curr?
       # delete this sandbox
-      *prev <- get *curr, next-sandbox:offset
-      # update sandbox count
-      sandbox-count:address:number <- get-address *env, number-of-sandboxes:offset
-      *sandbox-count <- subtract *sandbox-count, 1
-      # if it's the last sandbox and if it was the only sandbox rendered, reset scroll
-      {
-        break-if *prev
-        render-from:address:number <- get-address *env, render-from:offset
-        reset-scroll?:boolean <- equal *render-from, *sandbox-count
-        break-unless reset-scroll?
-        *render-from <- copy -1
-      }
+      next:address:shared:sandbox-data <- get *curr, next-sandbox:offset
+      *prev <- put *prev, next-sandbox:offset, next
+      env <- fixup-delete env, next
       return 1/true  # force rerender
     }
-    prev <- get-address *curr, next-sandbox:offset
+    prev <- copy curr
     curr <- get *curr, next-sandbox:offset
     loop
   }
   return 0/false
 ]
 
+def fixup-delete env:address:shared:programming-environment-data, next:address:shared:sandbox-data -> env:address:shared:programming-environment-data [
+  local-scope
+  load-ingredients
+  # update sandbox count
+  sandbox-count:number <- get *env, number-of-sandboxes:offset
+  sandbox-count <- subtract sandbox-count, 1
+  *env <- put *env, number-of-sandboxes:offset, sandbox-count
+  {
+    break-if next
+    # deleted sandbox was last
+    render-from:number <- get *env, render-from:offset
+    reset-scroll?:boolean <- equal render-from, sandbox-count
+    break-unless reset-scroll?
+    # deleted sandbox was only sandbox rendered, so reset scroll
+    *env <- put *env, render-from:offset, -1
+  }
+]
+
 scenario deleting-sandbox-after-scroll [
   trace-until 100/app  # trace too long
   assume-screen 30/width, 10/height
diff --git a/sandbox/008-sandbox-test.mu b/sandbox/008-sandbox-test.mu
index b64a5980..acd4c04d 100644
--- a/sandbox/008-sandbox-test.mu
+++ b/sandbox/008-sandbox-test.mu
@@ -98,8 +98,7 @@ before <end-save-sandbox> [
 ]
 
 before <end-restore-sandbox> [
-  expected-response:address:address:shared:array:character <- get-address **curr, expected-response:offset
-  *expected-response <- copy contents
+  *curr <- put *curr, expected-response:offset, contents
 ]
 
 # clicks on sandbox responses save it as 'expected'
@@ -160,24 +159,25 @@ def find-click-in-sandbox-output env:address:shared:programming-environment-data
 def toggle-expected-response sandbox:address:shared:sandbox-data -> sandbox:address:shared:sandbox-data [
   local-scope
   load-ingredients
-  expected-response:address:address:shared:array:character <- get-address *sandbox, expected-response:offset
+  expected-response:address:shared:array:character <- get *sandbox, expected-response:offset
   {
     # if expected-response is set, reset
-    break-unless *expected-response
-    *expected-response <- copy 0
-    return sandbox/same-as-ingredient:0
+    break-unless expected-response
+    *sandbox <- put *sandbox, expected-response:offset, 0
+  }
+  {
+    # if not, set expected response to the current response
+    break-if expected-response
+    response:address:shared:array:character <- get *sandbox, response:offset
+    *sandbox <- put *sandbox, expected-response:offset, response
   }
-  # if not, current response is the expected response
-  response:address:shared:array:character <- get *sandbox, response:offset
-  *expected-response <- copy response
 ]
 
 # when rendering a sandbox, color it in red/green if expected response exists
 after <render-sandbox-response> [
   {
     break-unless sandbox-response
-    response-starting-row:address:number <- get-address *sandbox, response-starting-row-on-screen:offset
-    *response-starting-row <- copy row
+    *sandbox <- put *sandbox, response-starting-row-on-screen:offset, row
     expected-response:address:shared:array:character <- get *sandbox, expected-response:offset
     break-unless expected-response  # fall-through to print in grey
     response-is-expected?:boolean <- equal expected-response, sandbox-response
@@ -194,6 +194,5 @@ after <render-sandbox-response> [
 ]
 
 before <end-render-sandbox-reset-hidden> [
-  tmp:address:number <- get-address *sandbox, response-starting-row-on-screen:offset
-  *tmp <- copy 0
+  *sandbox <- put *sandbox, response-starting-row-on-screen:offset, 0
 ]
diff --git a/sandbox/009-sandbox-trace.mu b/sandbox/009-sandbox-trace.mu
index feecf010..a85a1920 100644
--- a/sandbox/009-sandbox-trace.mu
+++ b/sandbox/009-sandbox-trace.mu
@@ -123,10 +123,10 @@ def! update-sandbox sandbox:address:shared:sandbox-data, env:address:shared:prog
   local-scope
   load-ingredients
   data:address:shared:array:character <- get *sandbox, data:offset
-  response:address:address:shared:array:character <- get-address *sandbox, response:offset
-  trace:address:address:shared:array:character <- get-address *sandbox, trace:offset
-  fake-screen:address:address:shared:screen <- get-address *sandbox, screen:offset
-  *response, _, *fake-screen, *trace <- run-interactive data
+  response:address:shared:array:character, _, fake-screen:address:shared:screen, trace:address:shared:array:character <- run-interactive 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
@@ -147,8 +147,9 @@ after <global-touch> [
     sandbox:address:shared:sandbox-data <- find-click-in-sandbox-code env, click-row
     break-unless sandbox
     # toggle its display-trace? property
-    x:address:boolean <- get-address *sandbox, display-trace?:offset
-    *x <- not *x
+    x:boolean <- get *sandbox, display-trace?:offset
+    x <- not x
+    *sandbox <- put *sandbox, display-trace?:offset, x
     hide-screen screen
     screen <- render-sandbox-side screen, env, 1/clear
     screen <- update-cursor screen, current-sandbox, env
diff --git a/sandbox/010-errors.mu b/sandbox/010-errors.mu
index adad4923..a25ade5d 100644
--- a/sandbox/010-errors.mu
+++ b/sandbox/010-errors.mu
@@ -9,19 +9,18 @@ container programming-environment-data [
 def! update-recipes env:address:shared:programming-environment-data, screen:address:shared:screen, test-recipes:address:shared:array:character -> errors-found?:boolean, env:address:shared:programming-environment-data, screen:address:shared:screen [
   local-scope
   load-ingredients
-  recipe-errors:address:address:shared:array:character <- get-address *env, recipe-errors:offset
   {
     break-if test-recipes
-    in:address:shared:array:character <- restore [recipes.mu]
-    *recipe-errors <- reload in
+    recipe-errors:address:shared:array:character <- restore [recipes.mu]
   }
   {
     break-unless test-recipes
-    *recipe-errors <- reload test-recipes
+    recipe-errors <- reload test-recipes
   }
+  *env <- put *env, recipe-errors:offset, recipe-errors
   # if recipe editor has errors, stop
   {
-    break-unless *recipe-errors
+    break-unless recipe-errors
     update-status screen, [errors found     ], 1/red
     errors-found? <- copy 1/true
     return
@@ -43,18 +42,17 @@ container programming-environment-data [
 ]
 
 after <programming-environment-initialization> [
-  error-index:address:number <- get-address *result, error-index:offset
-  *error-index <- copy -1
+  *result <- put *result, error-index:offset, -1
 ]
 
 after <run-sandboxes-begin> [
-  error-index:address:number <- get-address *env, error-index:offset
-  *error-index <- copy -1
+  *env <- put *env, error-index:offset, -1
 ]
 
 before <run-sandboxes-end> [
   {
-    sandboxes-completed-successfully?:boolean <- equal *error-index, -1
+    error-index:number <- get *env, error-index:offset
+    sandboxes-completed-successfully?:boolean <- equal error-index, -1
     break-if sandboxes-completed-successfully?
     errors-found? <- copy 1/true
   }
@@ -79,30 +77,31 @@ container sandbox-data [
 def! update-sandbox sandbox:address:shared:sandbox-data, env:address:shared:programming-environment-data, idx:number -> sandbox:address:shared:sandbox-data, env:address:shared:programming-environment-data [
   local-scope
   load-ingredients
-  data:address:shared:array:character <- get *sandbox, data:offset
-  response:address:address:shared:array:character <- get-address *sandbox, response:offset
-  errors:address:address:shared:array:character <- get-address *sandbox, errors:offset
-  trace:address:address:shared:array:character <- get-address *sandbox, trace:offset
-  fake-screen:address:address:shared:screen <- get-address *sandbox, screen:offset
-  recipe-errors:address:shared:array:character <- get *env, recipe-errors:offset
   {
+    recipe-errors:address:shared:array:character <- get *env, recipe-errors:offset
     break-unless recipe-errors
-    *errors <- copy recipe-errors
+    *sandbox <- put *sandbox, errors:offset, recipe-errors
     return
   }
-  *response, *errors, *fake-screen, *trace, completed?:boolean <- run-interactive data
+  data:address:shared:array:character <- get *sandbox, data:offset
+  response:address:shared:array:character, errors:address:shared:array:character, fake-screen:address:shared:screen, trace:address:shared:array:character, completed?:boolean <- run-interactive 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 errors
     break-if completed?:boolean
-    *errors <- new [took too long!
+    errors <- new [took too long!
 ]
+    *sandbox <- put *sandbox, errors:offset, errors
   }
   {
-    break-unless *errors
-    error-index:address:number <- get-address *env, error-index:offset
-    error-not-set?:boolean <- equal *error-index, -1
+    break-unless errors
+    error-index:number <- get *env, error-index:offset
+    error-not-set?:boolean <- equal error-index, -1
     break-unless error-not-set?
-    *error-index <- copy idx
+    *env <- put *env, error-index:offset, idx
   }
 ]
 
@@ -111,8 +110,7 @@ after <render-sandbox-trace-done> [
   {
     sandbox-errors:address:shared:array:character <- get *sandbox, errors:offset
     break-unless sandbox-errors
-    response-starting-row:address:number <- get-address *sandbox, response-starting-row-on-screen:offset
-    *response-starting-row <- copy 0  # no response
+    *sandbox <- put *sandbox, response-starting-row-on-screen:offset, 0  # no response
     {
       break-unless env
       recipe-errors:address:shared:array:character <- get *env, recipe-errors:offset
@@ -180,23 +178,6 @@ scenario run-updates-status-with-first-erroneous-sandbox [
   # status line shows that error is in first sandbox
   screen-should-contain [
     .  errors found (0)             run (F4)           .
-    .                                                  .
-    .━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.
-    .0                                                x.
-    .get foo, x:offset                                 .
-    .expected a container in 'get foo, x:offset'       .
-    .missing type for foo in 'get foo, x:offset'       .
-    .first ingredient of 'get' should be a container, ↩.
-    .but got foo                                       .
-    .━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.
-    .1                                                x.
-    .get foo, x:offset                                 .
-    .expected a container in 'get foo, x:offset'       .
-    .missing type for foo in 'get foo, x:offset'       .
-    .first ingredient of 'get' should be a container, ↩.
-    .but got foo                                       .
-    .━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.
-    .                                                  .
   ]
 ]
 
@@ -223,25 +204,6 @@ scenario run-updates-status-with-first-erroneous-sandbox-2 [
   # status line shows that error is in second sandbox
   screen-should-contain [
     .  errors found (1)             run (F4)           .
-    .                                                  .
-    .━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.
-    .0                                                x.
-    .add 2, 2                                          .
-    .4                                                 .
-    .━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.
-    .1                                                x.
-    .get foo, x:offset                                 .
-    .expected a container in 'get foo, x:offset'       .
-    .missing type for foo in 'get foo, x:offset'       .
-    .first ingredient of 'get' should be a container, ↩.
-    .but got foo                                       .
-    .━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.
-    .2                                                x.
-    .get foo, x:offset                                 .
-    .expected a container in 'get foo, x:offset'       .
-    .missing type for foo in 'get foo, x:offset'       .
-    .first ingredient of 'get' should be a container, ↩.
-    .but got foo                                       .
   ]
 ]
 
@@ -485,8 +447,8 @@ def foo [
     .━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.
     .0                                                x.
     .foo                                               .
-    .foo: expected ingredient 1 of 'get' to have type ↩.
-    .'offset'; got x:number                            .
+    .foo: second ingredient of 'get' should have type ↩.
+    .'offset', but got x:number                        .
   ]
 ]
 
diff --git a/sandbox/011-editor-undo.mu b/sandbox/011-editor-undo.mu
index 5866fb52..07ecb14f 100644
--- a/sandbox/011-editor-undo.mu
+++ b/sandbox/011-editor-undo.mu
@@ -65,12 +65,14 @@ after <handle-special-character> [
   {
     undo?:boolean <- equal *c, 26/ctrl-z
     break-unless undo?
-    undo:address:address:shared:list:address:shared:operation <- get-address *editor, undo:offset
-    break-unless *undo
-    op:address:shared:operation <- first *undo
-    *undo <- rest *undo
-    redo:address:address:shared:list:address:shared:operation <- get-address *editor, redo:offset
-    *redo <- push op, *redo
+    undo:address:shared:list:address:shared:operation <- get *editor, undo:offset
+    break-unless undo
+    op:address:shared:operation <- first undo
+    undo <- rest undo
+    *editor <- put *editor, undo:offset, undo
+    redo:address:shared:list:address:shared:operation <- get *editor, redo:offset
+    redo <- push op, redo
+    *editor <- put *editor, redo:offset, redo
     <handle-undo>
     return screen/same-as-ingredient:0, editor/same-as-ingredient:1, 1/go-render
   }
@@ -81,12 +83,14 @@ after <handle-special-character> [
   {
     redo?:boolean <- equal *c, 25/ctrl-y
     break-unless redo?
-    redo:address:address:shared:list:address:shared:operation <- get-address *editor, redo:offset
-    break-unless *redo
-    op:address:shared:operation <- first *redo
-    *redo <- rest *redo
-    undo:address:address:shared:list:address:shared:operation <- get-address *editor, undo:offset
-    *undo <- push op, *undo
+    redo:address:shared:list:address:shared:operation <- get *editor, redo:offset
+    break-unless redo
+    op:address:shared:operation <- first redo
+    redo <- rest redo
+    *editor <- put *editor, redo:offset, redo
+    undo:address:shared:list:address:shared:operation <- get *editor, undo:offset
+    undo <- push op, undo
+    *editor <- put *editor, undo:offset, undo
     <handle-redo>
     return screen/same-as-ingredient:0, editor/same-as-ingredient:1, 1/go-render
   }
@@ -136,52 +140,55 @@ scenario editor-can-undo-typing [
 # save operation to undo
 after <insert-character-begin> [
   top-before:address:shared:duplex-list:character <- get *editor, top-of-screen:offset
-  cursor-before:address:shared:duplex-list:character <- copy *before-cursor
+  cursor-before:address:shared:duplex-list:character <- get *editor, before-cursor:offset
 ]
 before <insert-character-end> [
   top-after:address:shared:duplex-list:character <- get *editor, top-of-screen:offset
-  undo:address:address:shared:list:address:shared:operation <- get-address *editor, undo:offset
+  cursor-row:number <- get *editor, cursor-row:offset
+  cursor-column:number <- get *editor, cursor-column:offset
+  undo:address:shared:list:address:shared:operation <- get *editor, undo:offset
   {
     # if previous operation was an insert, coalesce this operation with it
-    break-unless *undo
-    op:address:shared:operation <- first *undo
+    break-unless undo
+    op:address:shared:operation <- first undo
     typing:address:insert-operation <- maybe-convert *op, typing:variant
     break-unless typing
     previous-coalesce-tag:number <- get *typing, tag:offset
     break-unless previous-coalesce-tag
-    insert-until:address:address:shared:duplex-list:character <- get-address *typing, insert-until:offset
-    *insert-until <- next *before-cursor
-    after-row:address:number <- get-address *typing, after-row:offset
-    *after-row <- copy *cursor-row
-    after-column:address:number <- get-address *typing, after-column:offset
-    *after-column <- copy *cursor-column
-    after-top:address:address:shared:duplex-list:character <- get-address *typing, after-top-of-screen:offset
-    *after-top <- get *editor, top-of-screen:offset
+    before-cursor:address:shared:duplex-list:character <- get *editor, before-cursor:offset
+    insert-until:address:shared:duplex-list:character <- 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
     break +done-adding-insert-operation:label
   }
   # if not, create a new operation
   insert-from:address:shared:duplex-list:character <- next cursor-before
   insert-to:address:shared:duplex-list:character <- next insert-from
   op:address:shared: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
+  *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 <insert-enter-begin> [
-  cursor-row-before:number <- copy *cursor-row
-  cursor-column-before:number <- copy *cursor-column
+  cursor-row-before:number <- copy cursor-row
+  cursor-column-before:number <- copy cursor-column
   top-before:address:shared:duplex-list:character <- get *editor, top-of-screen:offset
-  cursor-before:address:shared:duplex-list:character <- copy *before-cursor
+  cursor-before:address:shared:duplex-list:character <- get *editor, before-cursor:offset
 ]
 before <insert-enter-end> [
   top-after:address:shared:duplex-list:character <- get *editor, top-of-screen:offset
+  cursor-row:number <- get *editor, cursor-row:offset
+  cursor-column:number <- get *editor, cursor-row:offset
   # never coalesce
   insert-from:address:shared:duplex-list:character <- next cursor-before
-  insert-to:address:shared:duplex-list:character <- next *before-cursor
+  before-cursor:address:shared:duplex-list:character <- get *editor, before-cursor:offset
+  insert-to:address:shared:duplex-list:character <- next before-cursor
   op:address:shared: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
+  *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
 ]
 
@@ -192,10 +199,12 @@ before <insert-enter-end> [
 def add-operation editor:address:shared:editor-data, op:address:shared:operation -> editor:address:shared:editor-data [
   local-scope
   load-ingredients
-  undo:address:address:shared:list:address:shared:operation <- get-address *editor, undo:offset
-  *undo <- push op *undo
-  redo:address:address:shared:list:address:shared:operation <- get-address *editor, redo:offset
-  *redo <- copy 0
+  undo:address:shared:list:address:shared:operation <- get *editor, undo:offset
+  undo <- push op undo
+  *editor <- put *editor, undo:offset, undo
+  redo:address:shared:list:address:shared:operation <- get *editor, redo:offset
+  redo <- copy 0
+  *editor <- put *editor, redo:offset, redo
   return editor/same-as-ingredient:0
 ]
 
@@ -206,12 +215,15 @@ after <handle-undo> [
     start:address:shared:duplex-list:character <- get *typing, insert-from:offset
     end:address:shared:duplex-list:character <- get *typing, insert-until:offset
     # assert cursor-row/cursor-column/top-of-screen match after-row/after-column/after-top-of-screen
-    *before-cursor <- prev start
-    remove-between *before-cursor, end
-    *cursor-row <- get *typing, before-row:offset
-    *cursor-column <- get *typing, before-column:offset
-    top:address:address:shared:duplex-list:character <- get-address *editor, top-of-screen:offset
-    *top <- get *typing, before-top-of-screen:offset
+    before-cursor:address:shared:duplex-list:character <- 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:address:shared:duplex-list:character <- get *typing, before-top-of-screen:offset
+    *editor <- put *editor, top-of-screen:offset, top
   }
 ]
 
@@ -400,14 +412,17 @@ after <handle-redo> [
   {
     typing:address:insert-operation <- maybe-convert *op, typing:variant
     break-unless typing
+    before-cursor <- get *editor, before-cursor:offset
     insert-from:address:shared:duplex-list:character <- get *typing, insert-from:offset  # ignore insert-to because it's already been spliced away
-    # assert insert-to matches next(*before-cursor)
-    insert-range *before-cursor, insert-from
+    # assert insert-to matches next(before-cursor)
+    insert-range before-cursor, insert-from
     # assert cursor-row/cursor-column/top-of-screen match after-row/after-column/after-top-of-screen
-    *cursor-row <- get *typing, after-row:offset
-    *cursor-column <- get *typing, after-column:offset
-    top:address:address:shared:duplex-list:character <- get-address *editor, top-of-screen:offset
-    *top <- get *typing, after-top-of-screen:offset
+    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:address:shared:duplex-list:character <- get *typing, after-top-of-screen:offset
+    *editor <- put *editor, top-of-screen:offset, top
   }
 ]
 
@@ -700,36 +715,33 @@ ghi]
 ]
 
 after <move-cursor-begin> [
-  before-cursor-row:number <- get *editor, cursor-row:offset
-  before-cursor-column:number <- get *editor, cursor-column:offset
-  before-top-of-screen:address:shared:duplex-list:character <- get *editor, top-of-screen:offset
+  cursor-row-before:number <- get *editor, cursor-row:offset
+  cursor-column-before:number <- get *editor, cursor-column:offset
+  top-before:address:shared:duplex-list:character <- get *editor, top-of-screen:offset
 ]
 before <move-cursor-end> [
-  after-cursor-row:number <- get *editor, cursor-row:offset
-  after-cursor-column:number <- get *editor, cursor-column:offset
-  after-top-of-screen:address:shared:duplex-list:character <- get *editor, top-of-screen:offset
+  top-after:address:shared:duplex-list:character <- get *editor, top-of-screen:offset
+  cursor-row:number <- get *editor, cursor-row:offset
+  cursor-column:number <- 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:address:address:shared:list:address:shared:operation <- get-address *editor, undo:offset
-    break-unless *undo
-    op:address:shared:operation <- first *undo
+    undo:address:shared:list:address:shared:operation <- get *editor, undo:offset
+    break-unless undo
+    op:address:shared:operation <- first undo
     move:address:move-operation <- maybe-convert *op, move:variant
     break-unless move
     previous-coalesce-tag:number <- get *move, tag:offset
     coalesce?:boolean <- equal undo-coalesce-tag, previous-coalesce-tag
     break-unless coalesce?
-    after-row:address:number <- get-address *move, after-row:offset
-    *after-row <- copy after-cursor-row
-    after-column:address:number <- get-address *move, after-column:offset
-    *after-column <- copy after-cursor-column
-    after-top:address:address:shared:duplex-list:character <- get-address *move, after-top-of-screen:offset
-    *after-top <- get *editor, top-of-screen:offset
+    *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
     break +done-adding-move-operation:label
   }
   op:address:shared:operation <- new operation:type
-  *op <- merge 1/move-operation, before-cursor-row, before-cursor-column, before-top-of-screen, after-cursor-row, after-cursor-column, after-top-of-screen, undo-coalesce-tag
+  *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
 ]
@@ -739,10 +751,12 @@ after <handle-undo> [
     move:address:move-operation <- maybe-convert *op, move:variant
     break-unless move
     # assert cursor-row/cursor-column/top-of-screen match after-row/after-column/after-top-of-screen
-    top:address:address:shared:duplex-list:character <- get-address *editor, top-of-screen:offset
-    *cursor-row <- get *move, before-row:offset
-    *cursor-column <- get *move, before-column:offset
-    *top <- get *move, before-top-of-screen:offset
+    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:address:shared:duplex-list:character <- get *move, before-top-of-screen:offset
+    *editor <- put *editor, top-of-screen:offset, top
   }
 ]
 
@@ -1265,6 +1279,117 @@ ghi]
   ]
 ]
 
+scenario editor-can-undo-multiple-arrows-in-the-same-direction [
+  # create an editor with some text
+  assume-screen 10/width, 5/height
+  1:address:shared:array:character <- new [abc
+def
+ghi]
+  2:address:shared:editor-data <- new-editor 1:address:shared:array:character, screen:address:shared:screen, 0/left, 10/right
+  editor-render screen, 2:address:shared:editor-data
+  # move the cursor
+  assume-console [
+    left-click 2, 1
+    press right-arrow
+    press right-arrow
+    press up-arrow
+  ]
+  editor-event-loop screen:address:shared:screen, console:address:shared:console, 2:address:shared:editor-data
+  3:number <- get *2:address:shared:editor-data, cursor-row:offset
+  4:number <- get *2:address:shared:editor-data, cursor-column:offset
+  memory-should-contain [
+    3 <- 1
+    4 <- 3
+  ]
+  # undo
+  assume-console [
+    press ctrl-z
+  ]
+  run [
+    editor-event-loop screen:address:shared:screen, console:address:shared:console, 2:address:shared:editor-data
+    3:number <- get *2:address:shared:editor-data, cursor-row:offset
+    4:number <- get *2:address:shared:editor-data, cursor-column:offset
+  ]
+  # up-arrow is undone
+  memory-should-contain [
+    3 <- 2
+    4 <- 3
+  ]
+  # undo again
+  assume-console [
+    press ctrl-z
+  ]
+  run [
+    editor-event-loop screen:address:shared:screen, console:address:shared:console, 2:address:shared:editor-data
+    3:number <- get *2:address:shared:editor-data, cursor-row:offset
+    4:number <- get *2:address:shared:editor-data, cursor-column:offset
+  ]
+  # both right-arrows are undone
+  memory-should-contain [
+    3 <- 2
+    4 <- 1
+  ]
+]
+
+# redo cursor movement and scroll
+
+scenario editor-redo-touch [
+  # create an editor with some text, click on a character, undo
+  assume-screen 10/width, 5/height
+  1:address:shared:array:character <- new [abc
+def
+ghi]
+  2:address:shared:editor-data <- new-editor 1:address:shared:array:character, screen:address:shared:screen, 0/left, 10/right
+  editor-render screen, 2:address:shared:editor-data
+  assume-console [
+    left-click 3, 1
+    press ctrl-z
+  ]
+  editor-event-loop screen:address:shared:screen, console:address:shared:console, 2:address:shared:editor-data
+  # redo
+  assume-console [
+    press ctrl-y
+  ]
+  run [
+    editor-event-loop screen:address:shared:screen, console:address:shared:console, 2:address:shared:editor-data
+    3:number <- get *2:address:shared:editor-data, cursor-row:offset
+    4:number <- get *2:address:shared:editor-data, cursor-column:offset
+  ]
+  # cursor moves to left-click
+  memory-should-contain [
+    3 <- 3
+    4 <- 1
+  ]
+  # cursor should be in the right place
+  assume-console [
+    type [1]
+  ]
+  run [
+    editor-event-loop screen:address:shared:screen, console:address:shared:console, 2:address:shared:editor-data
+  ]
+  screen-should-contain [
+    .          .
+    .abc       .
+    .def       .
+    .g1hi      .
+    .┈┈┈┈┈┈┈┈┈┈.
+  ]
+]
+
+after <handle-redo> [
+  {
+    move:address:move-operation <- maybe-convert *op, move:variant
+    break-unless move
+    # assert cursor-row/cursor-column/top-of-screen match after-row/after-column/after-top-of-screen
+    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:address:shared:duplex-list:character <- get *move, after-top-of-screen:offset
+    *editor <- put *editor, top-of-screen:offset, top
+  }
+]
+
 scenario editor-separates-undo-insert-from-undo-cursor-move [
   # create an editor, type some text, move the cursor, type some more text
   assume-screen 10/width, 5/height
@@ -1385,6 +1510,7 @@ scenario editor-separates-undo-insert-from-undo-cursor-move [
     .┈┈┈┈┈┈┈┈┈┈.
     .          .
   ]
+  # cursor moves
   memory-should-contain [
     3 <- 1
     4 <- 1
@@ -1411,115 +1537,6 @@ scenario editor-separates-undo-insert-from-undo-cursor-move [
   ]
 ]
 
-scenario editor-can-undo-multiple-arrows-in-the-same-direction [
-  # create an editor with some text
-  assume-screen 10/width, 5/height
-  1:address:shared:array:character <- new [abc
-def
-ghi]
-  2:address:shared:editor-data <- new-editor 1:address:shared:array:character, screen:address:shared:screen, 0/left, 10/right
-  editor-render screen, 2:address:shared:editor-data
-  # move the cursor
-  assume-console [
-    left-click 2, 1
-    press right-arrow
-    press right-arrow
-    press up-arrow
-  ]
-  editor-event-loop screen:address:shared:screen, console:address:shared:console, 2:address:shared:editor-data
-  3:number <- get *2:address:shared:editor-data, cursor-row:offset
-  4:number <- get *2:address:shared:editor-data, cursor-column:offset
-  memory-should-contain [
-    3 <- 1
-    4 <- 3
-  ]
-  # undo
-  assume-console [
-    press ctrl-z
-  ]
-  run [
-    editor-event-loop screen:address:shared:screen, console:address:shared:console, 2:address:shared:editor-data
-    3:number <- get *2:address:shared:editor-data, cursor-row:offset
-    4:number <- get *2:address:shared:editor-data, cursor-column:offset
-  ]
-  # up-arrow is undone
-  memory-should-contain [
-    3 <- 2
-    4 <- 3
-  ]
-  # undo again
-  assume-console [
-    press ctrl-z
-  ]
-  run [
-    editor-event-loop screen:address:shared:screen, console:address:shared:console, 2:address:shared:editor-data
-    3:number <- get *2:address:shared:editor-data, cursor-row:offset
-    4:number <- get *2:address:shared:editor-data, cursor-column:offset
-  ]
-  # both right-arrows are undone
-  memory-should-contain [
-    3 <- 2
-    4 <- 1
-  ]
-]
-
-# redo cursor movement and scroll
-
-scenario editor-redo-touch [
-  # create an editor with some text, click on a character, undo
-  assume-screen 10/width, 5/height
-  1:address:shared:array:character <- new [abc
-def
-ghi]
-  2:address:shared:editor-data <- new-editor 1:address:shared:array:character, screen:address:shared:screen, 0/left, 10/right
-  editor-render screen, 2:address:shared:editor-data
-  assume-console [
-    left-click 3, 1
-    press ctrl-z
-  ]
-  editor-event-loop screen:address:shared:screen, console:address:shared:console, 2:address:shared:editor-data
-  # redo
-  assume-console [
-    press ctrl-y
-  ]
-  run [
-    editor-event-loop screen:address:shared:screen, console:address:shared:console, 2:address:shared:editor-data
-    3:number <- get *2:address:shared:editor-data, cursor-row:offset
-    4:number <- get *2:address:shared:editor-data, cursor-column:offset
-  ]
-  # cursor moves to left-click
-  memory-should-contain [
-    3 <- 3
-    4 <- 1
-  ]
-  # cursor should be in the right place
-  assume-console [
-    type [1]
-  ]
-  run [
-    editor-event-loop screen:address:shared:screen, console:address:shared:console, 2:address:shared:editor-data
-  ]
-  screen-should-contain [
-    .          .
-    .abc       .
-    .def       .
-    .g1hi      .
-    .┈┈┈┈┈┈┈┈┈┈.
-  ]
-]
-
-after <handle-redo> [
-  {
-    move:address:move-operation <- maybe-convert *op, move:variant
-    break-unless move
-    # assert cursor-row/cursor-column/top-of-screen match after-row/after-column/after-top-of-screen
-    *cursor-row <- get *move, after-row:offset
-    *cursor-column <- get *move, after-column:offset
-    top:address:address:shared:duplex-list:character <- get-address *editor, top-of-screen:offset
-    *top <- get *move, after-top-of-screen:offset
-  }
-]
-
 # undo backspace
 
 scenario editor-can-undo-and-redo-backspace [
@@ -1595,33 +1612,32 @@ before <backspace-character-end> [
   {
     break-unless backspaced-cell  # backspace failed; don't add an undo operation
     top-after:address:shared:duplex-list:character <- get *editor, top-of-screen:offset
-    undo:address:address:shared:list:address:shared:operation <- get-address *editor, undo:offset
+    cursor-row:number <- get *editor, cursor-row:offset
+    cursor-column:number <- get *editor, cursor-row:offset
+    before-cursor:address:shared:duplex-list:character <- get *editor, before-cursor:offset
+    undo:address:shared:list:address:shared:operation <- get *editor, undo:offset
     {
       # if previous operation was an insert, coalesce this operation with it
       break-unless *undo
-      op:address:shared:operation <- first *undo
+      op:address:shared:operation <- first undo
       deletion:address:delete-operation <- maybe-convert *op, delete:variant
       break-unless deletion
       previous-coalesce-tag:number <- get *deletion, tag:offset
       coalesce?:boolean <- equal previous-coalesce-tag, 1/coalesce-backspace
       break-unless coalesce?
-      delete-from:address:address:shared:duplex-list:character <- get-address *deletion, delete-from:offset
-      *delete-from <- copy *before-cursor
-      backspaced-so-far:address:address:shared:duplex-list:character <- get-address *deletion, deleted-text:offset
-      insert-range backspaced-cell, *backspaced-so-far
-      *backspaced-so-far <- copy backspaced-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:address:shared:duplex-list:character <- get-address *deletion, after-top-of-screen:offset
-      *after-top <- get *editor, top-of-screen:offset
+      *deletion <- put *deletion, delete-from:offset, before-cursor
+      backspaced-so-far:address:shared:duplex-list:character <- get *deletion, deleted-text:offset
+      insert-range 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
       break +done-adding-backspace-operation:label
     }
     # if not, create a new operation
     op:address:shared:operation <- new operation:type
-    deleted-until:address:shared:duplex-list:character <- 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
+    deleted-until:address:shared:duplex-list:character <- 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
   }
@@ -1631,18 +1647,19 @@ after <handle-undo> [
   {
     deletion:address:delete-operation <- maybe-convert *op, delete:variant
     break-unless deletion
-    start2:address:address:shared:duplex-list:character <- get-address *editor, data:offset
     anchor:address:shared:duplex-list:character <- get *deletion, delete-from:offset
     break-unless anchor
     deleted:address:shared:duplex-list:character <- get *deletion, deleted-text:offset
     old-cursor:address:shared:duplex-list:character <- last deleted
     insert-range anchor, deleted
     # assert cursor-row/cursor-column/top-of-screen match after-row/after-column/after-top-of-screen
-    *before-cursor <- copy old-cursor
-    *cursor-row <- get *deletion, before-row:offset
-    *cursor-column <- get *deletion, before-column:offset
-    top:address:address:shared:duplex-list:character <- get-address *editor, top-of-screen:offset
-    *top <- get *deletion, before-top-of-screen:offset
+    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:address:shared:duplex-list:character <- get *deletion, before-top-of-screen:offset
+    *editor <- put *editor, top-of-screen:offset, top
   }
 ]
 
@@ -1652,12 +1669,15 @@ after <handle-redo> [
     break-unless deletion
     start:address:shared:duplex-list:character <- get *deletion, delete-from:offset
     end:address:shared:duplex-list:character <- get *deletion, delete-until:offset
+    data:address:shared:duplex-list:character <- 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
-    *cursor-column <- get *deletion, after-column:offset
-    top:address:address:shared:duplex-list:character <- get-address *editor, top-of-screen:offset
-    *top <- get *deletion, after-top-of-screen:offset
+    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:address:shared:duplex-list:character <- get *deletion, before-top-of-screen:offset
+    *editor <- put *editor, top-of-screen:offset, top
   }
 ]
 
@@ -1817,32 +1837,33 @@ before <delete-character-end> [
   {
     break-unless deleted-cell  # delete failed; don't add an undo operation
     top-after:address:shared:duplex-list:character <- get *editor, top-of-screen:offset
-    undo:address:address:shared:list:address:shared:operation <- get-address *editor, undo:offset
+    cursor-row:number <- get *editor, cursor-row:offset
+    cursor-column:number <- get *editor, cursor-column:offset
+    before-cursor:address:shared:duplex-list:character <- get *editor, before-cursor:offset
+    undo:address:shared:list:address:shared:operation <- get *editor, undo:offset
     {
       # if previous operation was an insert, coalesce this operation with it
-      break-unless *undo
-      op:address:shared:operation <- first *undo
+      break-unless undo
+      op:address:shared: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:shared:duplex-list:character <- get-address *deletion, delete-until:offset
-      *delete-until <- next *before-cursor
-      deleted-so-far:address:address:shared:duplex-list:character <- get-address *deletion, deleted-text:offset
-      *deleted-so-far <- append *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:address:shared:duplex-list:character <- get-address *deletion, after-top-of-screen:offset
-      *after-top <- get *editor, top-of-screen:offset
+      delete-until:address:shared:duplex-list:character <- next before-cursor
+      *deletion <- put *deletion, delete-until:offset, delete-until
+      deleted-so-far:address:shared:duplex-list:character <- 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
       break +done-adding-delete-operation:label
     }
     # if not, create a new operation
     op:address:shared:operation <- new operation:type
-    deleted-until:address:shared:duplex-list:character <- 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
+    deleted-until:address:shared:duplex-list:character <- 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
   }
@@ -1940,10 +1961,11 @@ before <delete-to-end-of-line-end> [
   {
     break-unless deleted-cells  # delete failed; don't add an undo operation
     top-after:address:shared:duplex-list:character <- get *editor, top-of-screen:offset
-    undo:address:address:shared:list:address:shared:operation <- get-address *editor, undo:offset
+    cursor-row:number <- get *editor, cursor-row:offset
+    cursor-column:number <- get *editor, cursor-column:offset
+    deleted-until:address:shared:duplex-list:character <- next before-cursor
     op:address:shared:operation <- new operation:type
-    deleted-until:address:shared:duplex-list:character <- 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-cells/deleted, *before-cursor/delete-from, deleted-until, 0/never-coalesce
+    *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
   }
@@ -2041,10 +2063,12 @@ before <delete-to-start-of-line-end> [
   {
     break-unless deleted-cells  # delete failed; don't add an undo operation
     top-after:address:shared:duplex-list:character <- get *editor, top-of-screen:offset
-    undo:address:address:shared:list:address:shared:operation <- get-address *editor, undo:offset
     op:address:shared:operation <- new operation:type
-    deleted-until:address:shared:duplex-list:character <- 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-cells/deleted, *before-cursor/delete-from, deleted-until, 0/never-coalesce
+    before-cursor:address:shared:duplex-list:character <- get *editor, before-cursor:offset
+    deleted-until:address:shared:duplex-list:character <- next before-cursor
+    cursor-row:number <- get *editor, cursor-row:offset
+    cursor-column:number <- 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
   }