1use std::time::Duration;
4
5use crate::changeset::ChangeSet;
6use crate::selection::SelectionSet;
7
8#[derive(Clone, Debug)]
11pub struct Transaction {
12 pub changes: ChangeSet,
13 pub inverse: ChangeSet,
14 pub before: SelectionSet,
15 pub after: SelectionSet,
16}
17
18#[derive(Clone, Copy, Debug, PartialEq, Eq)]
20pub enum EditKind {
21 Insert,
23 DeleteBackward,
24 DeleteForward,
25 Other,
27}
28
29pub const COALESCE_TIMEOUT: Duration = Duration::from_secs(1);
31
32pub type Revision = u64;
35
36#[derive(Clone, Debug)]
37pub struct UndoStep {
38 pub txns: Vec<Transaction>,
39 pub kind: EditKind,
40 pub rev_before: Revision,
41 pub rev_after: Revision,
42 last_edit_at: Duration,
43}
44
45#[derive(Clone, Debug, Default)]
46pub struct History {
47 undo: Vec<UndoStep>,
48 redo: Vec<UndoStep>,
49 coalesce_open: bool,
52 group_depth: usize,
54 group_started: bool,
56}
57
58impl History {
59 pub fn push(
60 &mut self,
61 txn: Transaction,
62 kind: EditKind,
63 rev_before: Revision,
64 rev_after: Revision,
65 now: Duration,
66 ) {
67 self.redo.clear();
68 let extend_top = if self.group_depth > 0 {
69 self.group_started
70 } else {
71 self.coalesce_open
72 && kind != EditKind::Other
73 && self.undo.last().is_some_and(|top| {
74 top.kind == kind
75 && top.rev_after == rev_before
76 && now.saturating_sub(top.last_edit_at) <= COALESCE_TIMEOUT
77 })
78 };
79 match self.undo.last_mut() {
80 Some(top) if extend_top => {
81 top.txns.push(txn);
82 top.rev_after = rev_after;
83 top.last_edit_at = now;
84 }
85 _ => self.undo.push(UndoStep {
86 txns: vec![txn],
87 kind,
88 rev_before,
89 rev_after,
90 last_edit_at: now,
91 }),
92 }
93 self.group_started = self.group_depth > 0;
94 self.coalesce_open = true;
95 }
96
97 pub fn break_coalescing(&mut self) {
98 self.coalesce_open = false;
99 }
100
101 pub fn begin_group(&mut self) {
103 if self.group_depth == 0 {
104 self.group_started = false;
105 }
106 self.group_depth += 1;
107 }
108
109 pub fn end_group(&mut self) {
110 self.group_depth = self.group_depth.saturating_sub(1);
111 if self.group_depth == 0 {
112 self.group_started = false;
113 self.coalesce_open = false;
114 }
115 }
116
117 pub fn can_undo(&self) -> bool {
118 !self.undo.is_empty()
119 }
120
121 pub fn can_redo(&self) -> bool {
122 !self.redo.is_empty()
123 }
124
125 pub(crate) fn pop_undo(&mut self) -> Option<UndoStep> {
126 self.coalesce_open = false;
127 self.group_started = false;
128 self.undo.pop()
129 }
130
131 pub(crate) fn push_redo(&mut self, step: UndoStep) {
132 self.redo.push(step);
133 }
134
135 pub(crate) fn pop_redo(&mut self) -> Option<UndoStep> {
136 self.coalesce_open = false;
137 self.group_started = false;
138 self.redo.pop()
139 }
140
141 pub(crate) fn push_undo(&mut self, step: UndoStep) {
142 self.undo.push(step);
143 }
144}