about summary refs log tree commit diff stats
path: root/undo.lua
diff options
context:
space:
mode:
Diffstat (limited to 'undo.lua')
-rw-r--r--undo.lua78
1 files changed, 78 insertions, 0 deletions
diff --git a/undo.lua b/undo.lua
new file mode 100644
index 0000000..abf5f33
--- /dev/null
+++ b/undo.lua
@@ -0,0 +1,78 @@
+-- undo/redo by managing the sequence of events in the current session
+-- based on https://github.com/akkartik/mu1/blob/master/edit/012-editor-undo.mu
+
+-- Incredibly inefficient; we make a copy of lines on every single keystroke.
+-- The hope here is that we're either editing small files or just reading large files.
+-- TODO: highlight stuff inserted by any undo/redo operation
+-- TODO: coalesce multiple similar operations
+
+function record_undo_event(data)
+  History[Next_history] = data
+  Next_history = Next_history+1
+  for i=Next_history,#History do
+    History[i] = nil
+  end
+end
+
+function undo_event()
+  if Next_history > 1 then
+--?     print('moving to history', Next_history-1)
+    Next_history = Next_history-1
+    local result = History[Next_history]
+    return result
+  end
+end
+
+function redo_event()
+  if Next_history <= #History then
+--?     print('restoring history', Next_history+1)
+    local result = History[Next_history]
+    Next_history = Next_history+1
+    return result
+  end
+end
+
+-- Make copies of objects; the rest of the app may mutate them in place, but undo requires immutable histories.
+function snapshot_everything()
+  -- compare with App.initialize_globals
+  local event = {
+    screen_top=deepcopy(Screen_top1),
+    selection=deepcopy(Selection1),
+    cursor=deepcopy(Cursor1),
+    current_drawing_mode=Drawing_mode,
+    previous_drawing_mode=Previous_drawing_mode,
+    zoom=Zoom,
+    lines={},
+    -- no filename; undo history is cleared when filename changes
+  }
+  -- deep copy lines without cached stuff like text fragments
+  for _,line in ipairs(Lines) do
+    if line.mode == 'text' then
+      table.insert(event.lines, {mode='text', data=line.data})
+    elseif line.mode == 'drawing' then
+      local points=deepcopy(line.points)
+--?       print('copying', line.points, 'with', #line.points, 'points into', points)
+      local shapes=deepcopy(line.shapes)
+--?       print('copying', line.shapes, 'with', #line.shapes, 'shapes into', shapes)
+      table.insert(event.lines, {mode='drawing', y=line.y, h=line.h, points=points, shapes=shapes, pending={}})
+--?       table.insert(event.lines, {mode='drawing', y=line.y, h=line.h, points=deepcopy(line.points), shapes=deepcopy(line.shapes), pending={}})
+    else
+      print(line.mode)
+      assert(false)
+    end
+  end
+  return event
+end
+
+-- https://stackoverflow.com/questions/640642/how-do-you-copy-a-lua-table-by-value/26367080#26367080
+function deepcopy(obj, seen)
+  if type(obj) ~= 'table' then return obj end
+  if seen and seen[obj] then return seen[obj] end
+  local s = seen or {}
+  local result = setmetatable({}, getmetatable(obj))
+  s[obj] = result
+  for k,v in pairs(obj) do
+    result[deepcopy(k, s)] = deepcopy(v, s)
+  end
+  return result
+end