on master help sign in
Implement Transaction::invert.
commitaa077a07f38174c9be7e9ec9ca0fd95b7686937e authorBlaž Hrastnik <blaz@mxxn.io> dateFri 2 Oct 2020, 18:16 (6 years ago) parent956ccc7
1 file changed+74−3
helix-core/src/transaction.rs+74−3
helix-core/src/transaction.rs+74−3
@@ -1,4 +1,5 @@
11use crate::{Range, Rope, Selection, State, Tendril};
2+use std::borrow::Cow;
23use std::convert::TryFrom;
34
45/// (from, to, replacement)
@@ -21,7 +22,7 @@ pub enum Assoc {
2122}
2223
2324// ChangeSpec = Change | ChangeSet | Vec<Change>
24−#[derive(Debug)]
25+#[derive(Debug, Clone, PartialEq, Eq)]
2526pub struct ChangeSet {
2627 pub(crate) changes: Vec<Operation>,
2728 /// The required document length. Will refuse to apply changes unless it matches.
@@ -185,8 +186,38 @@ impl ChangeSet {
185186 }
186187
187188 /// Returns a new changeset that reverts this one. Useful for `undo` implementation.
188− pub fn invert(self) -> Self {
189− unimplemented!()
189+ /// The document parameter expects the original document before this change was applied.
190+ pub fn invert(&self, original_doc: &Rope) -> Self {
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 }
190221 }
191222
192223 /// Returns true if applied successfully.
@@ -306,6 +337,7 @@ impl ChangeSet {
306337
307338/// Transaction represents a single undoable unit of changes. Several changes can be grouped into
308339/// a single transaction.
340+#[derive(Clone)]
309341pub struct Transaction {
310342 /// Changes made to the buffer.
311343 pub(crate) changes: ChangeSet,
@@ -352,6 +384,18 @@ impl Transaction {
352384 true
353385 }
354386
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+
355399 pub fn with_selection(mut self, selection: Selection) -> Self {
356400 self.selection = Some(selection);
357401 self
@@ -444,6 +488,33 @@ mod test {
444488 // TODO: assert
445489 }
446490
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+
447518 #[test]
448519 fn map_pos() {
449520 use Operation::*;