Implement Transaction::invert.

pull/3/head
Blaž Hrastnik 4 years ago
parent 956ccc7b5c
commit aa077a07f3

@ -1,4 +1,5 @@
use crate::{Range, Rope, Selection, State, Tendril}; use crate::{Range, Rope, Selection, State, Tendril};
use std::borrow::Cow;
use std::convert::TryFrom; use std::convert::TryFrom;
/// (from, to, replacement) /// (from, to, replacement)
@ -21,7 +22,7 @@ pub enum Assoc {
} }
// ChangeSpec = Change | ChangeSet | Vec<Change> // ChangeSpec = Change | ChangeSet | Vec<Change>
#[derive(Debug)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct ChangeSet { pub struct ChangeSet {
pub(crate) changes: Vec<Operation>, pub(crate) changes: Vec<Operation>,
/// The required document length. Will refuse to apply changes unless it matches. /// The required document length. Will refuse to apply changes unless it matches.
@ -185,8 +186,38 @@ impl ChangeSet {
} }
/// Returns a new changeset that reverts this one. Useful for `undo` implementation. /// Returns a new changeset that reverts this one. Useful for `undo` implementation.
pub fn invert(self) -> Self { /// The document parameter expects the original document before this change was applied.
unimplemented!() pub fn invert(&self, original_doc: &Rope) -> Self {
if original_doc.len_chars() != self.len {
panic!("Document length mismatch");
// return false;
}
let mut changes = Vec::with_capacity(self.changes.len());
let mut pos = 0;
let mut len = 0;
for change in &self.changes {
use Operation::*;
match change {
Retain(n) => {
changes.push(Retain(*n));
pos += n;
len += n;
}
Delete(n) => {
let text = Cow::from(original_doc.slice(pos..pos + *n));
changes.push(Insert(Tendril::from_slice(&text)));
pos += n;
}
Insert(s) => {
changes.push(Delete(s.len()));
len += s.len();
}
}
}
Self { changes, len }
} }
/// Returns true if applied successfully. /// Returns true if applied successfully.
@ -306,6 +337,7 @@ impl ChangeSet {
/// Transaction represents a single undoable unit of changes. Several changes can be grouped into /// Transaction represents a single undoable unit of changes. Several changes can be grouped into
/// a single transaction. /// a single transaction.
#[derive(Clone)]
pub struct Transaction { pub struct Transaction {
/// Changes made to the buffer. /// Changes made to the buffer.
pub(crate) changes: ChangeSet, pub(crate) changes: ChangeSet,
@ -352,6 +384,18 @@ impl Transaction {
true true
} }
/// Generate a transaction that reverts this one.
pub fn invert(&self, original: &State) -> Self {
let changes = self.changes.invert(original.doc());
// Store the current cursor position
let selection = original.selection.clone();
Self {
changes,
selection: Some(selection),
}
}
pub fn with_selection(mut self, selection: Selection) -> Self { pub fn with_selection(mut self, selection: Selection) -> Self {
self.selection = Some(selection); self.selection = Some(selection);
self self
@ -444,6 +488,33 @@ mod test {
// TODO: assert // TODO: assert
} }
#[test]
fn invert() {
use Operation::*;
let changes = ChangeSet {
changes: vec![Retain(4), Delete(5), Insert("test".into()), Retain(3)],
len: 12,
};
let doc = Rope::from("123 hello xz");
let revert = changes.invert(&doc);
let mut doc2 = doc.clone();
changes.apply(&mut doc2);
// a revert is different
assert_ne!(changes, revert);
assert_ne!(doc, doc2);
// but inverting a revert will give us the original
assert_eq!(changes, revert.invert(&doc2));
// applying a revert gives us back the original
revert.apply(&mut doc2);
assert_eq!(doc, doc2);
}
#[test] #[test]
fn map_pos() { fn map_pos() {
use Operation::*; use Operation::*;

Loading…
Cancel
Save