ruby 49 lines · 9 steps

How undo/redo history works in Ruby

An editor's undo stack is just an array of snapshots and a cursor that slides back and forth over them.

Explained by highlit
1class EditorHistory
2 MAX_DEPTH = 100
3 
4 def initialize(initial = "")
5 @snapshots = [initial.freeze]
6 @cursor = 0
7 end
8 
9 def current
10 @snapshots[@cursor]
11 end
12 
13 def commit(text)
14 text = text.dup.freeze
15 return current if text == current
16 
17 @snapshots = @snapshots[0..@cursor]
18 @snapshots << text
19 
20 if @snapshots.size > MAX_DEPTH
21 @snapshots.shift(@snapshots.size - MAX_DEPTH)
22 end
23 
24 @cursor = @snapshots.size - 1
25 current
26 end
27 
28 def undo
29 return current if @cursor.zero?
30 
31 @cursor -= 1
32 current
33 end
34 
35 def redo
36 return current if @cursor >= @snapshots.size - 1
37 
38 @cursor += 1
39 current
40 end
41 
42 def can_undo?
43 @cursor.positive?
44 end
45 
46 def can_redo?
47 @cursor < @snapshots.size - 1
48 end
49end
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1A cursor index over an append-only snapshot array turns undo/redo into simple pointer arithmetic.
  2. 2Committing after an undo must truncate the forward history, because that redo branch is now invalid.
  3. 3Freezing stored snapshots and capping the array bounds memory and prevents mutation from corrupting past states.

Related explainers

Share this explainer

Here's the card — post it anywhere.

How undo/redo history works in Ruby — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code