1use std::time::Duration;
6
7use serde::{Deserialize, Serialize};
8
9use crate::changeset::ChangeSet;
10use crate::selection::SelectionSet;
11use crate::text::Document;
12
13#[derive(Clone, Debug, Serialize, Deserialize)]
16pub struct Transaction {
17 pub changes: ChangeSet,
18 pub inverse: ChangeSet,
19 pub before: SelectionSet,
20 pub after: SelectionSet,
21}
22
23#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
25pub enum EditKind {
26 Insert,
28 DeleteBackward,
29 DeleteForward,
30 Other,
32}
33
34pub const COALESCE_TIMEOUT: Duration = Duration::from_secs(1);
36
37pub type Revision = u64;
40
41#[derive(Clone, Debug, Serialize, Deserialize)]
42pub struct UndoStep {
43 pub txns: Vec<Transaction>,
44 pub kind: EditKind,
45 pub rev_before: Revision,
46 pub rev_after: Revision,
47 #[serde(skip)]
49 last_edit_at: Duration,
50}
51
52impl UndoStep {
53 pub fn approx_size(&self) -> usize {
56 self.txns
57 .iter()
58 .map(|t| {
59 let edits = t.changes.edits().iter().chain(t.inverse.edits());
60 let sels = t.before.len() + t.after.len();
61 edits.map(|e| e.text.len() + 48).sum::<usize>() + sels * 40 + 64
62 })
63 .sum()
64 }
65}
66
67#[derive(Clone, Debug, Default, Serialize, Deserialize)]
72pub struct SavedHistory {
73 pub undo: Vec<UndoStep>,
75 pub redo: Vec<UndoStep>,
77 pub revision: Revision,
79 pub next_revision: Revision,
81}
82
83impl SavedHistory {
84 pub fn is_empty(&self) -> bool {
85 self.undo.is_empty() && self.redo.is_empty()
86 }
87
88 pub fn check(&self, doc: &Document) -> Result<(), String> {
94 if self.next_revision > Revision::MAX / 2 || self.revision >= self.next_revision {
96 return Err("a revision is out of range".into());
97 }
98 let all = self.undo.iter().chain(&self.redo);
99 if all
100 .clone()
101 .any(|s| s.rev_before >= self.next_revision || s.rev_after >= self.next_revision)
102 {
103 return Err("a revision is out of range".into());
104 }
105 if self
106 .undo
107 .last()
108 .is_some_and(|s| s.rev_after != self.revision)
109 || self
110 .redo
111 .last()
112 .is_some_and(|s| s.rev_before != self.revision)
113 || self
114 .undo
115 .windows(2)
116 .any(|w| w[0].rev_after != w[1].rev_before)
117 || self
118 .redo
119 .windows(2)
120 .any(|w| w[1].rev_after != w[0].rev_before)
121 {
122 return Err("the steps do not connect".into());
123 }
124 if all.clone().any(|s| s.txns.is_empty()) {
125 return Err("a step is empty".into());
126 }
127 let bad = |what: &str| Err(format!("{what} does not fit the text"));
128 let mut d = doc.clone();
129 for t in self.undo.iter().rev().flat_map(|s| s.txns.iter().rev()) {
130 if !t.after.fits(&d) || !t.inverse.fits(&d) {
131 return bad("an undo step");
132 }
133 t.inverse.apply(&mut d);
134 if !t.changes.fits(&d) || !t.before.fits(&d) {
135 return bad("an undo step");
136 }
137 }
138 let mut d = doc.clone();
139 for t in self.redo.iter().rev().flat_map(|s| s.txns.iter()) {
140 if !t.before.fits(&d) || !t.changes.fits(&d) {
141 return bad("a redo step");
142 }
143 t.changes.apply(&mut d);
144 if !t.inverse.fits(&d) || !t.after.fits(&d) {
145 return bad("a redo step");
146 }
147 }
148 Ok(())
149 }
150}
151
152pub fn sha256_hex(doc: &Document) -> String {
155 use sha2::{Digest, Sha256};
156 let mut hasher = Sha256::new();
157 for chunk in doc.chunks() {
158 hasher.update(chunk.as_bytes());
159 }
160 hasher
161 .finalize()
162 .iter()
163 .map(|b| format!("{b:02x}"))
164 .collect()
165}
166
167#[derive(Clone, Debug, Default)]
168pub struct History {
169 undo: Vec<UndoStep>,
170 redo: Vec<UndoStep>,
171 coalesce_open: bool,
174 group_depth: usize,
176 group_started: bool,
178}
179
180impl History {
181 pub fn push(
182 &mut self,
183 txn: Transaction,
184 kind: EditKind,
185 rev_before: Revision,
186 rev_after: Revision,
187 now: Duration,
188 ) {
189 self.redo.clear();
190 let extend_top = if self.group_depth > 0 {
191 self.group_started
192 } else {
193 self.coalesce_open
194 && kind != EditKind::Other
195 && self.undo.last().is_some_and(|top| {
196 top.kind == kind
197 && top.rev_after == rev_before
198 && now.saturating_sub(top.last_edit_at) <= COALESCE_TIMEOUT
199 })
200 };
201 match self.undo.last_mut() {
202 Some(top) if extend_top => {
203 top.txns.push(txn);
204 top.rev_after = rev_after;
205 top.last_edit_at = now;
206 }
207 _ => self.undo.push(UndoStep {
208 txns: vec![txn],
209 kind,
210 rev_before,
211 rev_after,
212 last_edit_at: now,
213 }),
214 }
215 self.group_started = self.group_depth > 0;
216 self.coalesce_open = true;
217 }
218
219 pub fn break_coalescing(&mut self) {
220 self.coalesce_open = false;
221 }
222
223 pub fn begin_group(&mut self) {
225 if self.group_depth == 0 {
226 self.group_started = false;
227 }
228 self.group_depth += 1;
229 }
230
231 pub fn end_group(&mut self) {
232 self.group_depth = self.group_depth.saturating_sub(1);
233 if self.group_depth == 0 {
234 self.group_started = false;
235 self.coalesce_open = false;
236 }
237 }
238
239 pub fn can_undo(&self) -> bool {
240 !self.undo.is_empty()
241 }
242
243 pub fn can_redo(&self) -> bool {
244 !self.redo.is_empty()
245 }
246
247 pub(crate) fn pop_undo(&mut self) -> Option<UndoStep> {
248 self.coalesce_open = false;
249 self.group_started = false;
250 self.undo.pop()
251 }
252
253 pub(crate) fn push_redo(&mut self, step: UndoStep) {
254 self.redo.push(step);
255 }
256
257 pub(crate) fn pop_redo(&mut self) -> Option<UndoStep> {
258 self.coalesce_open = false;
259 self.group_started = false;
260 self.redo.pop()
261 }
262
263 pub(crate) fn push_undo(&mut self, step: UndoStep) {
264 self.undo.push(step);
265 }
266
267 pub(crate) fn newest(&self, max_bytes: usize) -> (Vec<UndoStep>, Vec<UndoStep>) {
272 let mut budget = max_bytes;
273 let mut take = |steps: &[UndoStep]| {
274 let mut kept: Vec<UndoStep> = Vec::new();
275 for s in steps.iter().rev() {
276 let size = s.approx_size();
277 if size > budget {
278 break;
279 }
280 budget -= size;
281 kept.push(s.clone());
282 }
283 kept.reverse();
284 kept
285 };
286 let redo = take(&self.redo);
287 let undo = take(&self.undo);
288 (undo, redo)
289 }
290
291 pub(crate) fn replace(&mut self, undo: Vec<UndoStep>, redo: Vec<UndoStep>) {
294 *self = History {
295 undo,
296 redo,
297 ..History::default()
298 };
299 }
300
301 pub fn clear(&mut self) {
303 *self = History::default();
304 }
305
306 pub(crate) fn extend_base(&mut self, by: usize, old: Revision, new: Revision) {
312 for step in self.undo.iter_mut().chain(self.redo.iter_mut()) {
313 for t in &mut step.txns {
314 t.changes.extend_base(by);
315 t.inverse.extend_base(by);
316 }
317 }
318 if let Some(top) = self.undo.last_mut().filter(|s| s.rev_after == old) {
319 top.rev_after = new;
320 }
321 if let Some(next) = self.redo.last_mut().filter(|s| s.rev_before == old) {
322 next.rev_before = new;
323 }
324 self.coalesce_open = false;
325 }
326}