@@ -1,20 +1,61 @@
1use crate::{ChangeSet, Rope, State, Transaction};
1use crate::{ChangeSet, Rope, State, Transaction};
2+use once_cell::sync::Lazy;
2use smallvec::{smallvec, SmallVec};
4use smallvec::{smallvec, SmallVec};
5+use std::num::NonZeroUsize;
6+use std::time::{Duration, Instant};
4−/// Undo-tree style history store.
8+// Stores the history of changes to a buffer.
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
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.
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.
24+// The current revision is the one currently displayed in the buffer.
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.
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.
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
41+// * Because delete transactions currently don't store the text that they
42+// delete, we also store an inversion of the transaction.
5#[derive(Debug)]
43#[derive(Debug)]
6pub struct History {
44pub struct History {
7 revisions: Vec<Revision>,
45 revisions: Vec<Revision>,
8− cursor: usize,
46+ current: usize,
49+// A single point in history. See [History] for more information.
11#[derive(Debug)]
50#[derive(Debug)]
12struct Revision {
51struct Revision {
13 parent: usize,
52 parent: usize,
14− children: SmallVec<[(usize, Transaction); 1]>,
53+ last_child: Option<NonZeroUsize>,
15− /// The transaction to revert to previous state.
54+ transaction: Transaction,
55+ // We need an inversion for undos because delete transactions don't store
16− revert: Transaction,
57+ inversion: Transaction,
17− // selection before, selection after?
58+ timestamp: Instant,
20impl Default for History {
61impl Default for History {
@@ -23,72 +64,253 @@ impl Default for History {
24 revisions: vec![Revision {
65 revisions: vec![Revision {
25 parent: 0,
66 parent: 0,
26− children: SmallVec::new(),
67+ last_child: None,
68+ transaction: Transaction::from(ChangeSet::new(&Rope::new())),
27− revert: Transaction::from(ChangeSet::new(&Rope::new())),
69+ inversion: Transaction::from(ChangeSet::new(&Rope::new())),
70+ timestamp: Instant::now(),
29− cursor: 0,
72+ current: 0,
34impl History {
77impl History {
35 pub fn commit_revision(&mut self, transaction: &Transaction, original: &State) {
78 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
79+ self.commit_revision_at_timestamp(transaction, original, Instant::now());
82+ pub fn commit_revision_at_timestamp(
84+ transaction: &Transaction,
37− let revert = transaction
88+ let inversion = transaction
38 .invert(&original.doc)
89 .invert(&original.doc)
39 // Store the current cursor position
90 // Store the current cursor position
40 .with_selection(original.selection.clone());
91 .with_selection(original.selection.clone());
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);
43 self.revisions.push(Revision {
95 self.revisions.push(Revision {
44− parent: self.cursor,
96+ parent: self.current,
45− children: SmallVec::new(),
97+ last_child: None,
46− revert,
98+ transaction: transaction.clone(),
48−
102+ self.current = new_current;
49− // add a reference to the parent
51− .get_mut(self.cursor)
52− .unwrap() // TODO: get_unchecked_mut
54− .push((new_cursor, transaction.clone()));
56− self.cursor = new_cursor;
59 #[inline]
105 #[inline]
60 pub fn current_revision(&self) -> usize {
106 pub fn current_revision(&self) -> usize {
61− self.cursor
107+ self.current
64 #[inline]
110 #[inline]
65 pub const fn at_root(&self) -> bool {
111 pub const fn at_root(&self) -> bool {
66− self.cursor == 0
112+ self.current == 0
69 pub fn undo(&mut self) -> Option<&Transaction> {
115 pub fn undo(&mut self) -> Option<&Transaction> {
70 if self.at_root() {
116 if self.at_root() {
71− // We're at the root of undo, nothing to do.
72 return None;
117 return None;
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)
125+ pub fn redo(&mut self) -> Option<&Transaction> {
126+ let current_revision = &self.revisions[self.current];
77− self.cursor = current_revision.parent;
127+ let last_child = current_revision.last_child?;
128+ self.current = last_child.get();
79− Some(¤t_revision.revert)
130+ let last_child_revision = &self.revisions[last_child.get()];
131+ Some(&self.revisions[last_child.get()].transaction)
82− pub fn redo(&mut self) -> Option<&Transaction> {
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();
139+ a_path_set.insert(a);
140+ b_path_set.insert(b);
141+ if a_path_set.contains(&b) {
144+ if b_path_set.contains(&a) {
147+ a = self.revisions[a].parent; // Relies on the parent of 0 being 0.
148+ b = self.revisions[b].parent; // Same as above.
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();
83− let current_revision = &self.revisions[self.cursor];
158+ n = self.revisions[n].parent;
85− // for now, simply pick the latest child (linear undo / redo)
163+ fn jump_to(&mut self, to: usize) -> Vec<Transaction> {
86− if let Some((index, transaction)) = current_revision.children.last() {
164+ let lca = self.lowest_common_ancestor(self.current, to);
87− self.cursor = *index;
165+ let up = self.path_up(self.current, lca);
166+ let down = self.path_up(to, lca);
168+ let up_txns = up.iter().map(|&n| self.revisions[n].inversion.clone());
169+ let down_txns = down
172+ .map(|&n| self.revisions[n].transaction.clone());
173+ up_txns.chain(down_txns).collect()
176+ fn jump_backward(&mut self, delta: usize) -> Vec<Transaction> {
177+ self.jump_to(self.current.saturating_sub(delta))
180+ fn jump_forward(&mut self, delta: usize) -> Vec<Transaction> {
183+ .saturating_add(delta)
184+ .min(self.revisions.len() - 1),
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) {
195+ Equal | Greater => i,
199+ fn jump_instant(&mut self, instant: Instant) -> Vec<Transaction> {
200+ let search_result = self
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 {
207+ n if n == self.revisions.len() => n - 1,
208+ i => self.revision_closer_to_instant(i, instant),
211+ self.jump_to(revision)
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),
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),
228+ pub fn earlier(&mut self, uk: UndoKind) -> Vec<Transaction> {
231+ Steps(n) => self.jump_backward(n),
232+ TimePeriod(d) => self.jump_duration_backward(d),
236+ pub fn later(&mut self, uk: UndoKind) -> Vec<Transaction> {
239+ Steps(n) => self.jump_forward(n),
240+ TimePeriod(d) => self.jump_duration_forward(d),
245+#[derive(Debug, PartialEq)]
248+ TimePeriod(std::time::Duration),
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),
259+static DURATION_VALIDATION_REGEX: Lazy<Regex> =
260+ Lazy::new(|| Regex::new(r"^(?:\d+\s*[a-z]+\s*)+$").unwrap());
262+static NUMBER_UNIT_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"(\d+)\s*([a-z]+)").unwrap());
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"
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]);
276+ let n: u64 = n.parse().map_err(|_| format!("integer too large: {}", n))?;
278+ let time_unit = TIME_UNITS
281+ .find(|(_, (forms, _, _))| forms.iter().any(|f| f == &unit_str));
283+ if let Some((i, (_, unit, mul))) = time_unit {
285+ return Err(format!("{} specified more than once", unit));
287+ specified[i] = true;
289+ let new_seconds = n.checked_mul(*mul).and_then(|s| seconds.checked_add(s));
291+ Some(ns) => seconds = ns,
292+ None => return Err("duration too large".to_string()),
295+ return Err(format!("incorrect time unit: {}", unit_str));
299+ Ok(Duration::from_secs(seconds))
302+impl std::str::FromStr for UndoKind {
305+ fn from_str(s: &str) -> Result<Self, Self::Err> {
308+ Ok(Self::Steps(1usize))
309+ } else if let Ok(n) = s.parse::<usize>() {
310+ Ok(UndoKind::Steps(n))
312+ Ok(Self::TimePeriod(parse_human_duration(s)?))
@@ -144,4 +366,191 @@ mod test {
144 undo(&mut history, &mut state);
366 undo(&mut history, &mut state);
145 assert_eq!("hello", state.doc);
367 assert_eq!("hello", state.doc);
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);
376+ fn undo(history: &mut History, state: &mut State) {
377+ if let Some(transaction) = history.undo() {
378+ transaction.apply(&mut state.doc);
382+ fn earlier(history: &mut History, state: &mut State, uk: UndoKind) {
383+ let txns = history.earlier(uk);
385+ txn.apply(&mut state.doc);
389+ fn later(history: &mut History, state: &mut State, uk: UndoKind) {
390+ let txns = history.later(uk);
392+ txn.apply(&mut state.doc);
397+ history: &mut History,
399+ change: crate::transaction::Change,
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);
407+ let t0 = Instant::now();
408+ let t = |n| t0.checked_add(Duration::from_secs(n)).unwrap();
410+ commit_change(&mut history, &mut state, (1, 1, Some(" b".into())), t(0));
411+ assert_eq!("a b\n", state.doc);
413+ commit_change(&mut history, &mut state, (3, 3, Some(" c".into())), t(10));
414+ assert_eq!("a b c\n", state.doc);
416+ commit_change(&mut history, &mut state, (5, 5, Some(" d".into())), t(20));
417+ assert_eq!("a b c d\n", state.doc);
419+ undo(&mut history, &mut state);
420+ assert_eq!("a b c\n", state.doc);
422+ commit_change(&mut history, &mut state, (5, 5, Some(" e".into())), t(30));
423+ assert_eq!("a b c e\n", state.doc);
425+ undo(&mut history, &mut state);
426+ undo(&mut history, &mut state);
427+ assert_eq!("a b\n", state.doc);
429+ commit_change(&mut history, &mut state, (1, 3, None), t(40));
430+ assert_eq!("a\n", state.doc);
432+ commit_change(&mut history, &mut state, (1, 1, Some(" f".into())), t(50));
433+ assert_eq!("a f\n", state.doc);
437+ earlier(&mut history, &mut state, Steps(3));
438+ assert_eq!("a b c d\n", state.doc);
440+ later(&mut history, &mut state, TimePeriod(Duration::new(20, 0)));
441+ assert_eq!("a\n", state.doc);
443+ earlier(&mut history, &mut state, TimePeriod(Duration::new(19, 0)));
444+ assert_eq!("a b c d\n", state.doc);
449+ TimePeriod(Duration::new(10000, 0)),
451+ assert_eq!("a\n", state.doc);
453+ later(&mut history, &mut state, Steps(50));
454+ assert_eq!("a f\n", state.doc);
456+ earlier(&mut history, &mut state, Steps(4));
457+ assert_eq!("a b c\n", state.doc);
459+ later(&mut history, &mut state, TimePeriod(Duration::new(1, 0)));
460+ assert_eq!("a b c\n", state.doc);
462+ later(&mut history, &mut state, TimePeriod(Duration::new(5, 0)));
463+ assert_eq!("a b c d\n", state.doc);
465+ later(&mut history, &mut state, TimePeriod(Duration::new(6, 0)));
466+ assert_eq!("a b c e\n", state.doc);
468+ later(&mut history, &mut state, Steps(1));
469+ assert_eq!("a\n", state.doc);
473+ fn test_parse_undo_kind() {
476+ // Default is one step.
477+ assert_eq!("".parse(), Ok(Steps(1)));
479+ // An integer means the number of steps.
480+ assert_eq!("1".parse(), Ok(Steps(1)));
481+ assert_eq!(" 16 ".parse(), Ok(Steps(16)));
483+ // Duration has a strict format.
484+ let validation_err = Err("duration should be composed \
485+ of positive integers followed by time units"
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);
494+ "18446744073709551616minutes".parse::<UndoKind>(),
495+ Err("integer too large: 18446744073709551616".to_string())
498+ // Units are validated.
500+ "1 millenium".parse::<UndoKind>(),
501+ Err("incorrect time unit: millenium".to_string())
504+ // Units can't be specified twice.
506+ "2 seconds 6s".parse::<UndoKind>(),
507+ Err("seconds specified more than once".to_string())
510+ // Various formats are correctly handled.
512+ "4s".parse::<UndoKind>(),
513+ Ok(TimePeriod(Duration::from_secs(4)))
516+ "2m".parse::<UndoKind>(),
517+ Ok(TimePeriod(Duration::from_secs(120)))
520+ "5h".parse::<UndoKind>(),
521+ Ok(TimePeriod(Duration::from_secs(5 * 60 * 60)))
524+ "3d".parse::<UndoKind>(),
525+ Ok(TimePeriod(Duration::from_secs(3 * 24 * 60 * 60)))
528+ "1m30s".parse::<UndoKind>(),
529+ Ok(TimePeriod(Duration::from_secs(90)))
532+ "1m 20 seconds".parse::<UndoKind>(),
533+ Ok(TimePeriod(Duration::from_secs(80)))
536+ " 2 minute 1day".parse::<UndoKind>(),
537+ Ok(TimePeriod(Duration::from_secs(24 * 60 * 60 + 2 * 60)))
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
546+ // Sum overflow is handled.
548+ "18446744073709551615minutes".parse::<UndoKind>(),
549+ Err("duration too large".to_string())
552+ "1 minute 18446744073709551615 seconds".parse::<UndoKind>(),
553+ Err("duration too large".to_string())