1#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
11pub struct Edit {
12 pub at: usize,
13 pub text: String,
15 pub kind: EditKind,
16}
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
19pub enum EditKind {
20 Insert,
21 Delete,
22}
23
24#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
25struct Revision {
26 parent: usize,
27 last_child: Option<usize>,
28 undo: Vec<Edit>,
30 redo: Vec<Edit>,
32}
33
34#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
35pub struct History {
36 revisions: Vec<Revision>,
38 current: usize,
39 pending: Option<(Vec<Edit>, Vec<Edit>)>,
41}
42
43impl Default for History {
44 fn default() -> Self {
47 Self {
48 revisions: vec![Revision {
49 parent: 0,
50 last_child: None,
51 undo: vec![],
52 redo: vec![],
53 }],
54 current: 0,
55 pending: None,
56 }
57 }
58}
59
60#[derive(Debug, Clone)]
62pub struct RevisionRow {
63 pub index: usize,
64 pub parent: usize,
65 pub depth: usize,
67 pub summary: String,
69 pub is_current: bool,
70 pub branches: bool,
73}
74
75impl History {
76 pub fn tree_rows(&self) -> Vec<RevisionRow> {
78 let mut depths = vec![0usize; self.revisions.len()];
79 for i in 1..self.revisions.len() {
80 depths[i] = depths[self.revisions[i].parent] + 1;
81 }
82 let mut out: Vec<RevisionRow> = (1..self.revisions.len())
83 .rev()
84 .map(|i| {
85 let rev = &self.revisions[i];
86 let first = rev.redo.first();
87 let summary = match first {
88 Some(e) => {
89 let sign = match e.kind {
90 EditKind::Insert => "+",
91 EditKind::Delete => "-",
92 };
93 let text: String = e
94 .text
95 .chars()
96 .take(24)
97 .map(|c| if c == '\n' { '↵' } else { c })
98 .collect();
99 let more = if e.text.chars().count() > 24 {
100 "…"
101 } else {
102 ""
103 };
104 format!("{sign} \"{text}{more}\"")
105 }
106 None => "(empty)".into(),
107 };
108 RevisionRow {
109 index: i,
110 parent: rev.parent,
111 depth: depths[i],
112 summary,
113 is_current: i == self.current,
114 branches: self.revisions[..i].iter().any(|r| r.parent == rev.parent),
117 }
118 })
119 .collect();
120 out.sort_by_key(|r| std::cmp::Reverse(r.index));
121 out
122 }
123
124 pub fn ops_to(&mut self, target: usize) -> Option<Vec<Edit>> {
128 if target >= self.revisions.len() {
129 return None;
130 }
131 let mut anc_cur = Vec::new();
133 let mut at = self.current;
134 loop {
135 anc_cur.push(at);
136 if at == 0 {
137 break;
138 }
139 at = self.revisions[at].parent;
140 }
141 let mut up_path = Vec::new(); let mut t = target;
144 while !anc_cur.contains(&t) {
145 up_path.push(t);
146 t = self.revisions[t].parent;
147 }
148 let fork = t;
149 let mut ops = Vec::new();
150 let mut c = self.current;
152 while c != fork {
153 let mut rev_undo = self.revisions[c].undo.clone();
154 rev_undo.reverse();
155 ops.extend(rev_undo);
156 c = self.revisions[c].parent;
157 }
158 for &r in up_path.iter().rev() {
160 ops.extend(self.revisions[r].redo.clone());
161 }
162 let mut c = self.current;
164 while c != fork {
165 let p = self.revisions[c].parent;
166 self.revisions[p].last_child = Some(c);
167 c = p;
168 }
169 let mut p = fork;
170 for &r in up_path.iter().rev() {
171 self.revisions[p].last_child = Some(r);
172 p = r;
173 }
174 self.current = target;
175 Some(ops)
176 }
177}
178
179impl History {
180 pub fn begin(&mut self) {
181 if self.pending.is_none() {
182 self.pending = Some((Vec::new(), Vec::new()));
183 }
184 }
185
186 pub fn commit(&mut self) {
187 let Some((undo, redo)) = self.pending.take() else {
188 return;
189 };
190 if undo.is_empty() {
191 return;
192 }
193 let rev = Revision {
194 parent: self.current,
195 last_child: None,
196 undo,
197 redo,
198 };
199 self.revisions.push(rev);
200 let idx = self.revisions.len() - 1;
201 self.revisions[self.current].last_child = Some(idx);
202 self.current = idx;
203 }
204
205 pub fn record(&mut self, undo: Edit, redo: Edit) {
207 if self.pending.is_none() {
208 self.begin();
210 }
211 if let Some((u, r)) = &mut self.pending {
212 u.push(undo);
213 r.push(redo);
214 }
215 }
216
217 pub fn can_undo(&self) -> bool {
218 self.current > 0
219 }
220
221 pub fn can_redo(&self) -> bool {
222 self.revisions
223 .get(self.current)
224 .and_then(|r| r.last_child)
225 .is_some()
226 }
227
228 pub fn undo_ops(&mut self) -> Option<Vec<Edit>> {
230 if self.current == 0 {
231 return None;
232 }
233 let rev = &self.revisions[self.current];
234 let parent = rev.parent;
235 let mut ops = rev.undo.clone();
236 ops.reverse();
237 self.revisions[parent].last_child = Some(self.current);
238 self.current = parent;
239 Some(ops)
240 }
241
242 pub fn redo_ops(&mut self) -> Option<Vec<Edit>> {
243 let child = self.revisions.get(self.current)?.last_child?;
244 let ops = self.revisions[child].redo.clone();
245 self.current = child;
246 Some(ops)
247 }
248
249 pub fn depth(&self) -> usize {
250 self.revisions.len()
251 }
252
253 pub fn cap(&mut self, cap: usize) {
257 if self.revisions.len() <= cap {
258 return;
259 }
260 let mut chain = Vec::new();
262 let mut at = self.current;
263 loop {
264 chain.push(at);
265 if at == 0 {
266 break;
267 }
268 at = self.revisions[at].parent;
269 }
270 chain.reverse();
271 if chain.len() > cap {
272 chain = chain[chain.len() - cap..].to_vec();
273 }
274 let mut remap = std::collections::HashMap::new();
275 let mut new_revisions = Vec::with_capacity(chain.len());
276 for (new_idx, &old_idx) in chain.iter().enumerate() {
277 remap.insert(old_idx, new_idx);
278 let mut rev = self.revisions[old_idx].clone();
279 rev.parent = if new_idx == 0 { 0 } else { new_idx - 1 };
280 rev.last_child = rev.last_child.and_then(|c| remap.get(&c).copied());
281 new_revisions.push(rev);
282 }
283 self.revisions = new_revisions;
284 self.current = *remap.get(&self.current).unwrap_or(&0);
285 }
286}
287
288#[cfg(test)]
289mod tests {
290 use super::*;
291
292 fn edit(at: usize, text: &str, kind: EditKind) -> Edit {
293 Edit {
294 at,
295 text: text.into(),
296 kind,
297 }
298 }
299
300 #[test]
301 fn linear_undo_redo() {
302 let mut h = History::default();
303 h.begin();
304 h.record(
305 edit(0, "", EditKind::Delete),
306 edit(0, "x", EditKind::Insert),
307 );
308 h.commit();
309 assert!(h.can_undo());
310 let ops = h.undo_ops().unwrap();
311 assert_eq!(ops, vec![edit(0, "", EditKind::Delete)]);
312 assert!(h.can_redo());
313 let ops = h.redo_ops().unwrap();
314 assert_eq!(ops, vec![edit(0, "x", EditKind::Insert)]);
315 assert!(h.can_undo());
317 assert!(!h.can_redo());
318 }
319
320 #[test]
321 fn edit_after_undo_forks_a_branch() {
322 let mut h = History::default();
323 h.begin();
324 h.record(
325 edit(0, "", EditKind::Delete),
326 edit(0, "a", EditKind::Insert),
327 );
328 h.commit();
329 h.undo_ops();
330 h.begin();
331 h.record(
332 edit(0, "", EditKind::Delete),
333 edit(0, "b", EditKind::Insert),
334 );
335 h.commit();
336 assert!(h.depth() >= 2);
339 }
340}