From 9eaef6e33376407931162780cd402297c44f26c4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bla=C5=BE=20Hrastnik?= <blaz@mxxn.io>
Date: Wed, 31 Mar 2021 15:45:18 +0900
Subject: [PATCH] Fully drop State references.

---
 helix-core/src/comment.rs     |  6 +++--
 helix-core/src/history.rs     | 13 ++++++----
 helix-core/src/syntax.rs      |  6 ++---
 helix-core/src/transaction.rs | 48 +++++++++++++++++------------------
 helix-view/src/document.rs    | 12 +++++++--
 5 files changed, 48 insertions(+), 37 deletions(-)

diff --git a/helix-core/src/comment.rs b/helix-core/src/comment.rs
index c3b91b4f..fd9d9058 100644
--- a/helix-core/src/comment.rs
+++ b/helix-core/src/comment.rs
@@ -96,13 +96,15 @@ mod test {
 
         // comment
         let transaction = toggle_line_comments(&state.doc, &state.selection);
-        transaction.apply(&mut state);
+        transaction.apply(&mut state.doc);
+        state.selection = state.selection.clone().map(transaction.changes());
 
         assert_eq!(state.doc, "  // 1\n\n  // 2\n  // 3");
 
         // uncomment
         let transaction = toggle_line_comments(&state.doc, &state.selection);
-        transaction.apply(&mut state);
+        transaction.apply(&mut state.doc);
+        state.selection = state.selection.clone().map(transaction.changes());
         assert_eq!(state.doc, "  1\n\n  2\n  3");
 
         // TODO: account for no margin after comment
diff --git a/helix-core/src/history.rs b/helix-core/src/history.rs
index 13a2a020..df4b9fc4 100644
--- a/helix-core/src/history.rs
+++ b/helix-core/src/history.rs
@@ -33,7 +33,10 @@ impl Default for History {
 impl History {
     pub fn commit_revision(&mut self, transaction: &Transaction, original: &State) {
         // TODO: could store a single transaction, if deletes also stored the text they delete
-        let revert = transaction.invert(original);
+        let revert = transaction
+            .invert(&original.doc)
+            // Store the current cursor position
+            .with_selection(original.selection.clone());
 
         let new_cursor = self.revisions.len();
         self.revisions.push(Revision {
@@ -100,7 +103,7 @@ mod test {
 
         // Need to commit before applying!
         history.commit_revision(&transaction1, &state);
-        transaction1.apply(&mut state);
+        transaction1.apply(&mut state.doc);
         assert_eq!("hello world!", state.doc);
 
         // ---
@@ -110,18 +113,18 @@ mod test {
 
         // Need to commit before applying!
         history.commit_revision(&transaction2, &state);
-        transaction2.apply(&mut state);
+        transaction2.apply(&mut state.doc);
         assert_eq!("hello 世界!", state.doc);
 
         // ---
         fn undo(history: &mut History, state: &mut State) {
             if let Some(transaction) = history.undo() {
-                transaction.apply(state);
+                transaction.apply(&mut state.doc);
             }
         }
         fn redo(history: &mut History, state: &mut State) {
             if let Some(transaction) = history.redo() {
-                transaction.apply(state);
+                transaction.apply(&mut state.doc);
             }
         }
 
diff --git a/helix-core/src/syntax.rs b/helix-core/src/syntax.rs
index b29d6165..6d6fe648 100644
--- a/helix-core/src/syntax.rs
+++ b/helix-core/src/syntax.rs
@@ -1593,7 +1593,7 @@ fn test_input_edits() {
         &state.doc,
         vec![(6, 11, Some("test".into())), (12, 17, None)].into_iter(),
     );
-    let edits = LanguageLayer::generate_edits(state.doc.slice(..), &transaction.changes);
+    let edits = LanguageLayer::generate_edits(state.doc.slice(..), transaction.changes());
     // transaction.apply(&mut state);
 
     assert_eq!(
@@ -1622,8 +1622,8 @@ fn test_input_edits() {
     let mut state = State::new("fn test() {}".into());
     let transaction =
         Transaction::change(&state.doc, vec![(8, 8, Some("a: u32".into()))].into_iter());
-    let edits = LanguageLayer::generate_edits(state.doc.slice(..), &transaction.changes);
-    transaction.apply(&mut state);
+    let edits = LanguageLayer::generate_edits(state.doc.slice(..), transaction.changes());
+    transaction.apply(&mut state.doc);
 
     assert_eq!(state.doc, "fn test(a: u32) {}");
     assert_eq!(
diff --git a/helix-core/src/transaction.rs b/helix-core/src/transaction.rs
index c0f27abe..6c60c9c5 100644
--- a/helix-core/src/transaction.rs
+++ b/helix-core/src/transaction.rs
@@ -400,9 +400,7 @@ impl ChangeSet {
 /// a single transaction.
 #[derive(Debug, Clone)]
 pub struct Transaction {
-    /// Changes made to the buffer.
-    pub(crate) changes: ChangeSet,
-    /// When set, explicitly updates the selection.
+    changes: ChangeSet,
     selection: Option<Selection>,
     // effects, annotations
     // scroll_into_view
@@ -417,40 +415,35 @@ impl Transaction {
         }
     }
 
+    /// Changes made to the buffer.
     pub fn changes(&self) -> &ChangeSet {
         &self.changes
     }
 
+    /// When set, explicitly updates the selection.
+    pub fn selection(&self) -> Option<&Selection> {
+        self.selection.as_ref()
+    }
+
     /// Returns true if applied successfully.
-    pub fn apply(&self, state: &mut State) -> bool {
+    pub fn apply(&self, doc: &mut Rope) -> bool {
         if !self.changes.is_empty() {
             // apply changes to the document
-            if !self.changes.apply(&mut state.doc) {
+            if !self.changes.apply(doc) {
                 return false;
             }
         }
 
-        // TODO: also avoid mapping the selection if not necessary
-
-        // update the selection: either take the selection specified in the transaction, or map the
-        // current selection through changes.
-        state.selection = self
-            .selection
-            .clone()
-            .unwrap_or_else(|| state.selection.clone().map(&self.changes));
-
         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();
+    pub fn invert(&self, original: &Rope) -> Self {
+        let changes = self.changes.invert(original);
 
         Self {
             changes,
-            selection: Some(selection),
+            selection: None,
         }
     }
 
@@ -675,7 +668,7 @@ mod test {
             // (1, 1, None) is a useless 0-width delete
             vec![(1, 1, None), (6, 11, Some("void".into())), (12, 17, None)].into_iter(),
         );
-        transaction.apply(&mut state);
+        transaction.apply(&mut state.doc);
         assert_eq!(state.doc, Rope::from_str("hello void! 123"));
     }
 
@@ -691,15 +684,20 @@ mod test {
     fn optimized_composition() {
         let mut state = State::new("".into());
         let t1 = Transaction::insert(&state.doc, &state.selection, Tendril::from_char('h'));
-        t1.apply(&mut state);
+        t1.apply(&mut state.doc);
+        state.selection = state.selection.clone().map(t1.changes());
         let t2 = Transaction::insert(&state.doc, &state.selection, Tendril::from_char('e'));
-        t2.apply(&mut state);
+        t2.apply(&mut state.doc);
+        state.selection = state.selection.clone().map(t2.changes());
         let t3 = Transaction::insert(&state.doc, &state.selection, Tendril::from_char('l'));
-        t3.apply(&mut state);
+        t3.apply(&mut state.doc);
+        state.selection = state.selection.clone().map(t3.changes());
         let t4 = Transaction::insert(&state.doc, &state.selection, Tendril::from_char('l'));
-        t4.apply(&mut state);
+        t4.apply(&mut state.doc);
+        state.selection = state.selection.clone().map(t4.changes());
         let t5 = Transaction::insert(&state.doc, &state.selection, Tendril::from_char('o'));
-        t5.apply(&mut state);
+        t5.apply(&mut state.doc);
+        state.selection = state.selection.clone().map(t5.changes());
 
         assert_eq!(state.doc, Rope::from_str("hello"));
 
diff --git a/helix-view/src/document.rs b/helix-view/src/document.rs
index 94684362..cf160cca 100644
--- a/helix-view/src/document.rs
+++ b/helix-view/src/document.rs
@@ -21,6 +21,7 @@ pub struct Document {
     // rope + selection
     pub(crate) id: DocumentId,
     state: State,
+
     path: Option<PathBuf>,
 
     /// Current editing mode.
@@ -185,9 +186,16 @@ impl Document {
     fn _apply(&mut self, transaction: &Transaction) -> bool {
         let old_doc = self.text().clone();
 
-        let success = transaction.apply(&mut self.state);
+        let success = transaction.changes().apply(&mut self.state.doc);
 
         if !transaction.changes().is_empty() {
+            // update the selection: either take the selection specified in the transaction, or map the
+            // current selection through changes.
+            self.state.selection = transaction
+                .selection()
+                .cloned()
+                .unwrap_or_else(|| self.selection().clone().map(transaction.changes()));
+
             self.version += 1;
 
             // update tree-sitter syntax tree
@@ -415,7 +423,7 @@ mod test {
 
         // delete
 
-        let transaction = transaction.invert(&old_doc);
+        let transaction = transaction.invert(&old_doc.doc);
         let old_doc = doc.state.clone();
         doc.apply(&transaction);
         let changes = Client::changeset_to_changes(&old_doc.doc, doc.text(), transaction.changes());