@@ -1,20 +1,61 @@
11use crate::{ChangeSet, Rope, State, Transaction};
2+use once_cell::sync::Lazy;
3+use regex::Regex;
24use smallvec::{smallvec, SmallVec};
5+use std::num::NonZeroUsize;
6+use std::time::{Duration, Instant};
37
4−/// Undo-tree style history store.
8+// Stores the history of changes to a buffer.
9+//
10+// Currently the history is represented as a vector of revisions. The vector
11+// always has at least one element: the empty root revision. Each revision
12+// with the exception of the root has a parent revision, a [Transaction]
13+// that can be applied to its parent to transition from the parent to itself,
14+// and an inversion of that transaction to transition from the parent to its
15+// latest child.
16+//
17+// When using `u` to undo a change, an inverse of the stored transaction will
18+// be applied which will transition the buffer to the parent state.
19+//
20+// Each revision with the exception of the last in the vector also has a
21+// last child revision. When using `U` to redo a change, the last child transaction
22+// will be applied to the current state of the buffer.
23+//
24+// The current revision is the one currently displayed in the buffer.
25+//
26+// Commiting a new revision to the history will update the last child of the
27+// current revision, and push a new revision to the end of the vector.
28+//
29+// Revisions are commited with a timestamp. :earlier and :later can be used
30+// to jump to the closest revision to a moment in time relative to the timestamp
31+// of the current revision plus (:later) or minus (:earlier) the duration
32+// given to the command. If a single integer is given, the editor will instead
33+// jump the given number of revisions in the vector.
34+//
35+// Limitations:
36+// * Changes in selections currently don't commit history changes. The selection
37+// will only be updated to the state after a commited buffer change.
38+// * The vector of history revisions is currently unbounded. This might
39+// cause the memory consumption to grow significantly large during long
40+// editing sessions.
41+// * Because delete transactions currently don't store the text that they
42+// delete, we also store an inversion of the transaction.
543#[derive(Debug)]
644pub struct History {
745 revisions: Vec<Revision>,
8− cursor: usize,
46+ current: usize,
947}
1048
49+// A single point in history. See [History] for more information.
1150#[derive(Debug)]
1251struct Revision {
1352 parent: usize,
14− children: SmallVec<[(usize, Transaction); 1]>,
15− /// The transaction to revert to previous state.
16− revert: Transaction,
17− // selection before, selection after?
53+ last_child: Option<NonZeroUsize>,
54+ transaction: Transaction,
55+ // We need an inversion for undos because delete transactions don't store
56+ // the deleted text.
57+ inversion: Transaction,
58+ timestamp: Instant,
1859}
1960
2061impl Default for History {
@@ -23,72 +64,253 @@ impl Default for History {
2364 Self {
2465 revisions: vec![Revision {
2566 parent: 0,
26− children: SmallVec::new(),
27− revert: Transaction::from(ChangeSet::new(&Rope::new())),
67+ last_child: None,
68+ transaction: Transaction::from(ChangeSet::new(&Rope::new())),
69+ inversion: Transaction::from(ChangeSet::new(&Rope::new())),
70+ timestamp: Instant::now(),
2871 }],
29− cursor: 0,
72+ current: 0,
3073 }
3174 }
3275}
3376
3477impl History {
3578 pub fn commit_revision(&mut self, transaction: &Transaction, original: &State) {
36− // TODO: could store a single transaction, if deletes also stored the text they delete
37− let revert = transaction
79+ self.commit_revision_at_timestamp(transaction, original, Instant::now());
80+ }
81+
82+ pub fn commit_revision_at_timestamp(
83+ &mut self,
84+ transaction: &Transaction,
85+ original: &State,
86+ timestamp: Instant,
87+ ) {
88+ let inversion = transaction
3889 .invert(&original.doc)
3990 // Store the current cursor position
4091 .with_selection(original.selection.clone());
4192
42− let new_cursor = self.revisions.len();
93+ let new_current = self.revisions.len();
94+ self.revisions[self.current].last_child = NonZeroUsize::new(new_current);
4395 self.revisions.push(Revision {
44− parent: self.cursor,
45− children: SmallVec::new(),
46− revert,
96+ parent: self.current,
97+ last_child: None,
98+ transaction: transaction.clone(),
99+ inversion,
100+ timestamp,
47101 });
48−
49− // add a reference to the parent
50− self.revisions
51− .get_mut(self.cursor)
52− .unwrap() // TODO: get_unchecked_mut
53− .children
54− .push((new_cursor, transaction.clone()));
55−
56− self.cursor = new_cursor;
102+ self.current = new_current;
57103 }
58104
59105 #[inline]
60106 pub fn current_revision(&self) -> usize {
61− self.cursor
107+ self.current
62108 }
63109
64110 #[inline]
65111 pub const fn at_root(&self) -> bool {
66− self.cursor == 0
112+ self.current == 0
67113 }
68114
69115 pub fn undo(&mut self) -> Option<&Transaction> {
70116 if self.at_root() {
71− // We're at the root of undo, nothing to do.
72117 return None;
73118 }
74119
75− let current_revision = &self.revisions[self.cursor];
120+ let current_revision = &self.revisions[self.current];
121+ self.current = current_revision.parent;
122+ Some(¤t_revision.inversion)
123+ }
76124
77− self.cursor = current_revision.parent;
125+ pub fn redo(&mut self) -> Option<&Transaction> {
126+ let current_revision = &self.revisions[self.current];
127+ let last_child = current_revision.last_child?;
128+ self.current = last_child.get();
78129
79− Some(¤t_revision.revert)
130+ let last_child_revision = &self.revisions[last_child.get()];
131+ Some(&self.revisions[last_child.get()].transaction)
80132 }
81133
82− pub fn redo(&mut self) -> Option<&Transaction> {
83− let current_revision = &self.revisions[self.cursor];
134+ fn lowest_common_ancestor(&self, mut a: usize, mut b: usize) -> usize {
135+ use std::collections::HashSet;
136+ let mut a_path_set = HashSet::new();
137+ let mut b_path_set = HashSet::new();
138+ loop {
139+ a_path_set.insert(a);
140+ b_path_set.insert(b);
141+ if a_path_set.contains(&b) {
142+ return b;
143+ }
144+ if b_path_set.contains(&a) {
145+ return a;
146+ }
147+ a = self.revisions[a].parent; // Relies on the parent of 0 being 0.
148+ b = self.revisions[b].parent; // Same as above.
149+ }
150+ }
151+
152+ // List of nodes on the way from `n` to 'a`. Doesn`t include `a`.
153+ // Includes `n` unless `a == n`. `a` must be an ancestor of `n`.
154+ fn path_up(&self, mut n: usize, a: usize) -> Vec<usize> {
155+ let mut path = Vec::new();
156+ while n != a {
157+ path.push(n);
158+ n = self.revisions[n].parent;
159+ }
160+ path
161+ }
84162
85− // for now, simply pick the latest child (linear undo / redo)
86− if let Some((index, transaction)) = current_revision.children.last() {
87− self.cursor = *index;
163+ fn jump_to(&mut self, to: usize) -> Vec<Transaction> {
164+ let lca = self.lowest_common_ancestor(self.current, to);
165+ let up = self.path_up(self.current, lca);
166+ let down = self.path_up(to, lca);
167+ self.current = to;
168+ let up_txns = up.iter().map(|&n| self.revisions[n].inversion.clone());
169+ let down_txns = down
170+ .iter()
171+ .rev()
172+ .map(|&n| self.revisions[n].transaction.clone());
173+ up_txns.chain(down_txns).collect()
174+ }
175+
176+ fn jump_backward(&mut self, delta: usize) -> Vec<Transaction> {
177+ self.jump_to(self.current.saturating_sub(delta))
178+ }
179+
180+ fn jump_forward(&mut self, delta: usize) -> Vec<Transaction> {
181+ self.jump_to(
182+ self.current
183+ .saturating_add(delta)
184+ .min(self.revisions.len() - 1),
185+ )
186+ }
187+
188+ // Helper for a binary search case below.
189+ fn revision_closer_to_instant(&self, i: usize, instant: Instant) -> usize {
190+ let dur_im1 = instant.duration_since(self.revisions[i - 1].timestamp);
191+ let dur_i = self.revisions[i].timestamp.duration_since(instant);
192+ use std::cmp::Ordering::*;
193+ match dur_im1.cmp(&dur_i) {
194+ Less => i - 1,
195+ Equal | Greater => i,
196+ }
197+ }
198+
199+ fn jump_instant(&mut self, instant: Instant) -> Vec<Transaction> {
200+ let search_result = self
201+ .revisions
202+ .binary_search_by(|rev| rev.timestamp.cmp(&instant));
203+ let revision = match search_result {
204+ Ok(revision) => revision,
205+ Err(insert_point) => match insert_point {
206+ 0 => 0,
207+ n if n == self.revisions.len() => n - 1,
208+ i => self.revision_closer_to_instant(i, instant),
209+ },
210+ };
211+ self.jump_to(revision)
212+ }
213+
214+ fn jump_duration_backward(&mut self, duration: Duration) -> Vec<Transaction> {
215+ match self.revisions[self.current].timestamp.checked_sub(duration) {
216+ Some(instant) => self.jump_instant(instant),
217+ None => self.jump_to(0),
218+ }
219+ }
88220
89− return Some(&transaction);
221+ fn jump_duration_forward(&mut self, duration: Duration) -> Vec<Transaction> {
222+ match self.revisions[self.current].timestamp.checked_add(duration) {
223+ Some(instant) => self.jump_instant(instant),
224+ None => self.jump_to(self.revisions.len() - 1),
225+ }
226+ }
227+
228+ pub fn earlier(&mut self, uk: UndoKind) -> Vec<Transaction> {
229+ use UndoKind::*;
230+ match uk {
231+ Steps(n) => self.jump_backward(n),
232+ TimePeriod(d) => self.jump_duration_backward(d),
233+ }
234+ }
235+
236+ pub fn later(&mut self, uk: UndoKind) -> Vec<Transaction> {
237+ use UndoKind::*;
238+ match uk {
239+ Steps(n) => self.jump_forward(n),
240+ TimePeriod(d) => self.jump_duration_forward(d),
241+ }
242+ }
243+}
244+
245+#[derive(Debug, PartialEq)]
246+pub enum UndoKind {
247+ Steps(usize),
248+ TimePeriod(std::time::Duration),
249+}
250+
251+// A subset of sytemd.time time span syntax units.
252+const TIME_UNITS: &[(&[&str], &str, u64)] = &[
253+ (&["seconds", "second", "sec", "s"], "seconds", 1),
254+ (&["minutes", "minute", "min", "m"], "minutes", 60),
255+ (&["hours", "hour", "hr", "h"], "hours", 60 * 60),
256+ (&["days", "day", "d"], "days", 24 * 60 * 60),
257+];
258+
259+static DURATION_VALIDATION_REGEX: Lazy<Regex> =
260+ Lazy::new(|| Regex::new(r"^(?:\d+\s*[a-z]+\s*)+$").unwrap());
261+
262+static NUMBER_UNIT_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"(\d+)\s*([a-z]+)").unwrap());
263+
264+fn parse_human_duration(s: &str) -> Result<Duration, String> {
265+ if !DURATION_VALIDATION_REGEX.is_match(s) {
266+ return Err("duration should be composed \
267+ of positive integers followed by time units"
268+ .to_string());
269+ }
270+
271+ let mut specified = [false; TIME_UNITS.len()];
272+ let mut seconds = 0u64;
273+ for cap in NUMBER_UNIT_REGEX.captures_iter(s) {
274+ let (n, unit_str) = (&cap[1], &cap[2]);
275+
276+ let n: u64 = n.parse().map_err(|_| format!("integer too large: {}", n))?;
277+
278+ let time_unit = TIME_UNITS
279+ .iter()
280+ .enumerate()
281+ .find(|(_, (forms, _, _))| forms.iter().any(|f| f == &unit_str));
282+
283+ if let Some((i, (_, unit, mul))) = time_unit {
284+ if specified[i] {
285+ return Err(format!("{} specified more than once", unit));
286+ }
287+ specified[i] = true;
288+
289+ let new_seconds = n.checked_mul(*mul).and_then(|s| seconds.checked_add(s));
290+ match new_seconds {
291+ Some(ns) => seconds = ns,
292+ None => return Err("duration too large".to_string()),
293+ }
294+ } else {
295+ return Err(format!("incorrect time unit: {}", unit_str));
296+ }
297+ }
298+
299+ Ok(Duration::from_secs(seconds))
300+}
301+
302+impl std::str::FromStr for UndoKind {
303+ type Err = String;
304+
305+ fn from_str(s: &str) -> Result<Self, Self::Err> {
306+ let s = s.trim();
307+ if s.is_empty() {
308+ Ok(Self::Steps(1usize))
309+ } else if let Ok(n) = s.parse::<usize>() {
310+ Ok(UndoKind::Steps(n))
311+ } else {
312+ Ok(Self::TimePeriod(parse_human_duration(s)?))
90313 }
91− None
92314 }
93315}
94316
@@ -144,4 +366,191 @@ mod test {
144366 undo(&mut history, &mut state);
145367 assert_eq!("hello", state.doc);
146368 }
369+
370+ #[test]
371+ fn test_earlier_later() {
372+ let mut history = History::default();
373+ let doc = Rope::from("a\n");
374+ let mut state = State::new(doc);
375+
376+ fn undo(history: &mut History, state: &mut State) {
377+ if let Some(transaction) = history.undo() {
378+ transaction.apply(&mut state.doc);
379+ }
380+ };
381+
382+ fn earlier(history: &mut History, state: &mut State, uk: UndoKind) {
383+ let txns = history.earlier(uk);
384+ for txn in txns {
385+ txn.apply(&mut state.doc);
386+ }
387+ };
388+
389+ fn later(history: &mut History, state: &mut State, uk: UndoKind) {
390+ let txns = history.later(uk);
391+ for txn in txns {
392+ txn.apply(&mut state.doc);
393+ }
394+ };
395+
396+ fn commit_change(
397+ history: &mut History,
398+ state: &mut State,
399+ change: crate::transaction::Change,
400+ instant: Instant,
401+ ) {
402+ let txn = Transaction::change(&state.doc, vec![change.clone()].into_iter());
403+ history.commit_revision_at_timestamp(&txn, &state, instant);
404+ txn.apply(&mut state.doc);
405+ };
406+
407+ let t0 = Instant::now();
408+ let t = |n| t0.checked_add(Duration::from_secs(n)).unwrap();
409+
410+ commit_change(&mut history, &mut state, (1, 1, Some(" b".into())), t(0));
411+ assert_eq!("a b\n", state.doc);
412+
413+ commit_change(&mut history, &mut state, (3, 3, Some(" c".into())), t(10));
414+ assert_eq!("a b c\n", state.doc);
415+
416+ commit_change(&mut history, &mut state, (5, 5, Some(" d".into())), t(20));
417+ assert_eq!("a b c d\n", state.doc);
418+
419+ undo(&mut history, &mut state);
420+ assert_eq!("a b c\n", state.doc);
421+
422+ commit_change(&mut history, &mut state, (5, 5, Some(" e".into())), t(30));
423+ assert_eq!("a b c e\n", state.doc);
424+
425+ undo(&mut history, &mut state);
426+ undo(&mut history, &mut state);
427+ assert_eq!("a b\n", state.doc);
428+
429+ commit_change(&mut history, &mut state, (1, 3, None), t(40));
430+ assert_eq!("a\n", state.doc);
431+
432+ commit_change(&mut history, &mut state, (1, 1, Some(" f".into())), t(50));
433+ assert_eq!("a f\n", state.doc);
434+
435+ use UndoKind::*;
436+
437+ earlier(&mut history, &mut state, Steps(3));
438+ assert_eq!("a b c d\n", state.doc);
439+
440+ later(&mut history, &mut state, TimePeriod(Duration::new(20, 0)));
441+ assert_eq!("a\n", state.doc);
442+
443+ earlier(&mut history, &mut state, TimePeriod(Duration::new(19, 0)));
444+ assert_eq!("a b c d\n", state.doc);
445+
446+ earlier(
447+ &mut history,
448+ &mut state,
449+ TimePeriod(Duration::new(10000, 0)),
450+ );
451+ assert_eq!("a\n", state.doc);
452+
453+ later(&mut history, &mut state, Steps(50));
454+ assert_eq!("a f\n", state.doc);
455+
456+ earlier(&mut history, &mut state, Steps(4));
457+ assert_eq!("a b c\n", state.doc);
458+
459+ later(&mut history, &mut state, TimePeriod(Duration::new(1, 0)));
460+ assert_eq!("a b c\n", state.doc);
461+
462+ later(&mut history, &mut state, TimePeriod(Duration::new(5, 0)));
463+ assert_eq!("a b c d\n", state.doc);
464+
465+ later(&mut history, &mut state, TimePeriod(Duration::new(6, 0)));
466+ assert_eq!("a b c e\n", state.doc);
467+
468+ later(&mut history, &mut state, Steps(1));
469+ assert_eq!("a\n", state.doc);
470+ }
471+
472+ #[test]
473+ fn test_parse_undo_kind() {
474+ use UndoKind::*;
475+
476+ // Default is one step.
477+ assert_eq!("".parse(), Ok(Steps(1)));
478+
479+ // An integer means the number of steps.
480+ assert_eq!("1".parse(), Ok(Steps(1)));
481+ assert_eq!(" 16 ".parse(), Ok(Steps(16)));
482+
483+ // Duration has a strict format.
484+ let validation_err = Err("duration should be composed \
485+ of positive integers followed by time units"
486+ .to_string());
487+ assert_eq!(" 16 33".parse::<UndoKind>(), validation_err);
488+ assert_eq!(" seconds 22 ".parse::<UndoKind>(), validation_err);
489+ assert_eq!(" -4 m".parse::<UndoKind>(), validation_err);
490+ assert_eq!("5s 3".parse::<UndoKind>(), validation_err);
491+
492+ // Units are u64.
493+ assert_eq!(
494+ "18446744073709551616minutes".parse::<UndoKind>(),
495+ Err("integer too large: 18446744073709551616".to_string())
496+ );
497+
498+ // Units are validated.
499+ assert_eq!(
500+ "1 millenium".parse::<UndoKind>(),
501+ Err("incorrect time unit: millenium".to_string())
502+ );
503+
504+ // Units can't be specified twice.
505+ assert_eq!(
506+ "2 seconds 6s".parse::<UndoKind>(),
507+ Err("seconds specified more than once".to_string())
508+ );
509+
510+ // Various formats are correctly handled.
511+ assert_eq!(
512+ "4s".parse::<UndoKind>(),
513+ Ok(TimePeriod(Duration::from_secs(4)))
514+ );
515+ assert_eq!(
516+ "2m".parse::<UndoKind>(),
517+ Ok(TimePeriod(Duration::from_secs(120)))
518+ );
519+ assert_eq!(
520+ "5h".parse::<UndoKind>(),
521+ Ok(TimePeriod(Duration::from_secs(5 * 60 * 60)))
522+ );
523+ assert_eq!(
524+ "3d".parse::<UndoKind>(),
525+ Ok(TimePeriod(Duration::from_secs(3 * 24 * 60 * 60)))
526+ );
527+ assert_eq!(
528+ "1m30s".parse::<UndoKind>(),
529+ Ok(TimePeriod(Duration::from_secs(90)))
530+ );
531+ assert_eq!(
532+ "1m 20 seconds".parse::<UndoKind>(),
533+ Ok(TimePeriod(Duration::from_secs(80)))
534+ );
535+ assert_eq!(
536+ " 2 minute 1day".parse::<UndoKind>(),
537+ Ok(TimePeriod(Duration::from_secs(24 * 60 * 60 + 2 * 60)))
538+ );
539+ assert_eq!(
540+ "3 d 2hour 5 minutes 30sec".parse::<UndoKind>(),
541+ Ok(TimePeriod(Duration::from_secs(
542+ 3 * 24 * 60 * 60 + 2 * 60 * 60 + 5 * 60 + 30
543+ )))
544+ );
545+
546+ // Sum overflow is handled.
547+ assert_eq!(
548+ "18446744073709551615minutes".parse::<UndoKind>(),
549+ Err("duration too large".to_string())
550+ );
551+ assert_eq!(
552+ "1 minute 18446744073709551615 seconds".parse::<UndoKind>(),
553+ Err("duration too large".to_string())
554+ );
555+ }
147556}