1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
use Rope;
use crateSelections;
/// What an edit did, for the purpose of deciding whether it continues the
/// previous one.
///
/// Coarse on purpose: the question is only "is this the same kind of thing the
/// user was already doing", and a finer taxonomy would split runs the user
/// experiences as one.
/// The buffer as it stood before an edit, and where the cursors were.
///
/// Storing the selections is what makes undo put the caret back where the edit
/// happened rather than wherever clamping left it. Every editor in the field
/// does this; an undo that leaves the cursor somewhere unrelated is disorienting
/// enough that users stop trusting it.
/// Whole-content undo history, stored as rope snapshots.
///
/// A snapshot is a `Rope` rather than a `String` because ropey clones are O(1)
/// and copy-on-write: two snapshots share every node they have in common, so a
/// deep undo stack over a large file costs the edits, not one full copy of the
/// text per step. `to_string()` would allocate and copy the whole buffer on
/// every keystroke.
///
/// Whole-content snapshots stay the right call at this size — they are correct
/// for any edit shape, and with structural sharing they are no longer expensive
/// enough to justify per-edit deltas.
///
/// **Runs have no clock.** VS Code and Zed break undo groups on an idle timer.
/// A timer means the buffer needs a clock, which means tests need to inject one,
/// which means the rule is only ever exercised through a fake. The rule here is
/// structural instead: consecutive edits of the same `EditKind` coalesce, and
/// anything that is not an edit — a motion, a click, a save — calls `boundary`.
/// That is deterministic and it matches what a user means by "undo what I just
/// typed": the run ends when they moved.
///
/// ponytail: pausing mid-word for ten minutes without moving still coalesces.
/// If that ever bites, a timer goes beside this rule, not instead of it.
/// How many undo steps are kept.
///
/// vim's `undolevels` default, and there is no reason to be cleverer until
/// someone measures a session where it bites. Structural sharing makes each
/// snapshot cheap but not free — every one pins the rope nodes it replaced, so
/// an uncapped stack is an uncapped retention of every version of the file for
/// as long as the editor is open.
pub const MAX_UNDO_STEPS: usize = 1000;