aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--helix-core/src/transaction.rs77
-rw-r--r--helix-term/Cargo.toml3
-rw-r--r--helix-term/src/main.rs4
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")