on master help sign in
Implement Transaction::invert.
aa077a0Blaž Hrastnik
helix-core/src/transaction.rs+74−3
@@ -1,4 +1,5 @@
1use crate::{Range, Rope, Selection, State, Tendril};
1use crate::{Range, Rope, Selection, State, Tendril};
2+use std::borrow::Cow;
2use std::convert::TryFrom;
3use std::convert::TryFrom;
3
4
4/// (from, to, replacement)
5/// (from, to, replacement)
@@ -21,7 +22,7 @@ pub enum Assoc {
21}
22}
22
23
23// ChangeSpec = Change | ChangeSet | Vec<Change>
24// ChangeSpec = Change | ChangeSet | Vec<Change>
24−#[derive(Debug)]
25+#[derive(Debug, Clone, PartialEq, Eq)]
25pub struct ChangeSet {
26pub struct ChangeSet {
26 pub(crate) changes: Vec<Operation>,
27 pub(crate) changes: Vec<Operation>,
27 /// The required document length. Will refuse to apply changes unless it matches.
28 /// The required document length. Will refuse to apply changes unless it matches.
@@ -185,8 +186,38 @@ impl ChangeSet {
185 }
186 }
186
187
187 /// Returns a new changeset that reverts this one. Useful for `undo` implementation.
188 /// Returns a new changeset that reverts this one. Useful for `undo` implementation.
189+ /// The document parameter expects the original document before this change was applied.
188− pub fn invert(self) -> Self {
190+ pub fn invert(&self, original_doc: &Rope) -> Self {
189− unimplemented!()
191+ if original_doc.len_chars() != self.len {
192+ panic!("Document length mismatch");
193+ // return false;
194+ }
195+
196+ let mut changes = Vec::with_capacity(self.changes.len());
197+ let mut pos = 0;
198+ let mut len = 0;
199+
200+ for change in &self.changes {
201+ use Operation::*;
202+ match change {
203+ Retain(n) => {
204+ changes.push(Retain(*n));
205+ pos += n;
206+ len += n;
207+ }
208+ Delete(n) => {
209+ let text = Cow::from(original_doc.slice(pos..pos + *n));
210+ changes.push(Insert(Tendril::from_slice(&text)));
211+ pos += n;
212+ }
213+ Insert(s) => {
214+ changes.push(Delete(s.len()));
215+ len += s.len();
216+ }
217+ }
218+ }
219+
220+ Self { changes, len }
190 }
221 }
191
222
192 /// Returns true if applied successfully.
223 /// Returns true if applied successfully.
@@ -306,6 +337,7 @@ impl ChangeSet {
306
337
307/// Transaction represents a single undoable unit of changes. Several changes can be grouped into
338/// Transaction represents a single undoable unit of changes. Several changes can be grouped into
308/// a single transaction.
339/// a single transaction.
340+#[derive(Clone)]
309pub struct Transaction {
341pub struct Transaction {
310 /// Changes made to the buffer.
342 /// Changes made to the buffer.
311 pub(crate) changes: ChangeSet,
343 pub(crate) changes: ChangeSet,
@@ -352,6 +384,18 @@ impl Transaction {
352 true
384 true
353 }
385 }
354
386
387+ /// Generate a transaction that reverts this one.
388+ pub fn invert(&self, original: &State) -> Self {
389+ let changes = self.changes.invert(original.doc());
390+ // Store the current cursor position
391+ let selection = original.selection.clone();
392+
393+ Self {
394+ changes,
395+ selection: Some(selection),
396+ }
397+ }
398+
355 pub fn with_selection(mut self, selection: Selection) -> Self {
399 pub fn with_selection(mut self, selection: Selection) -> Self {
356 self.selection = Some(selection);
400 self.selection = Some(selection);
357 self
401 self
@@ -444,6 +488,33 @@ mod test {
444 // TODO: assert
488 // TODO: assert
445 }
489 }
446
490
491+ #[test]
492+ fn invert() {
493+ use Operation::*;
494+
495+ let changes = ChangeSet {
496+ changes: vec![Retain(4), Delete(5), Insert("test".into()), Retain(3)],
497+ len: 12,
498+ };
499+
500+ let doc = Rope::from("123 hello xz");
501+ let revert = changes.invert(&doc);
502+
503+ let mut doc2 = doc.clone();
504+ changes.apply(&mut doc2);
505+
506+ // a revert is different
507+ assert_ne!(changes, revert);
508+ assert_ne!(doc, doc2);
509+
510+ // but inverting a revert will give us the original
511+ assert_eq!(changes, revert.invert(&doc2));
512+
513+ // applying a revert gives us back the original
514+ revert.apply(&mut doc2);
515+ assert_eq!(doc, doc2);
516+ }
517+
447 #[test]
518 #[test]
448 fn map_pos() {
519 fn map_pos() {
449 use Operation::*;
520 use Operation::*;