diff options
-rw-r--r-- | helix-core/src/transaction.rs | 77 | ||||
-rw-r--r-- | helix-term/Cargo.toml | 3 | ||||
-rw-r--r-- | helix-term/src/main.rs | 4 |
3 files changed, 77 insertions, 7 deletions
diff --git a/helix-core/src/transaction.rs b/helix-core/src/transaction.rs index a8059497..7a00a4f0 100644 --- a/helix-core/src/transaction.rs +++ b/helix-core/src/transaction.rs @@ -1,4 +1,5 @@ use crate::{Range, Rope, Selection, State, Tendril}; +use std::borrow::Cow; use std::convert::TryFrom; /// (from, to, replacement) @@ -21,7 +22,7 @@ pub enum Assoc { } // ChangeSpec = Change | ChangeSet | Vec<Change> -#[derive(Debug)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct ChangeSet { pub(crate) changes: Vec<Operation>, /// 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. - pub fn invert(self) -> Self { - unimplemented!() + /// The document parameter expects the original document before this change was applied. + 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. @@ -306,6 +337,7 @@ impl ChangeSet { /// Transaction represents a single undoable unit of changes. Several changes can be grouped into /// a single transaction. +#[derive(Clone)] pub struct Transaction { /// Changes made to the buffer. pub(crate) changes: ChangeSet, @@ -352,6 +384,18 @@ impl Transaction { 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 { self.selection = Some(selection); self @@ -445,6 +489,33 @@ mod test { } #[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] fn map_pos() { use Operation::*; diff --git a/helix-term/Cargo.toml b/helix-term/Cargo.toml index 82eeb345..ed546090 100644 --- a/helix-term/Cargo.toml +++ b/helix-term/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "helix-term" version = "0.1.0" +description = "A post-modern text editor." authors = ["Blaž Hrastnik <blaz@mxxn.io>"] edition = "2018" @@ -21,4 +22,4 @@ num_cpus = "1.13" # tui = { version = "0.12", default-features = false, features = ["crossterm"] } tui = { git = "https://github.com/fdehau/tui-rs", default-features = false, features = ["crossterm"] } crossterm = { version = "0.18", features = ["event-stream"] } -clap = { version = "3.0.0-beta.2 ", default-features = false, features = ["std"] } +clap = { version = "3.0.0-beta.2 ", default-features = false, features = ["std", "cargo"] } diff --git a/helix-term/src/main.rs b/helix-term/src/main.rs index eeb498bc..f5af9175 100644 --- a/helix-term/src/main.rs +++ b/helix-term/src/main.rs @@ -12,9 +12,7 @@ use anyhow::Error; static EX: smol::Executor = smol::Executor::new(); fn main() -> Result<(), Error> { - let args = App::new("helix") - .version("0.1") - .about("A post-modern text editor.") + let args = clap::app_from_crate!() .arg( Arg::new("files") .about("Sets the input file to use") |