diff --git a/helix-core/src/comment.rs b/helix-core/src/comment.rs index c3b91b4fe..fd9d9058b 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 13a2a020b..df4b9fc4d 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 b29d6165a..6d6fe648b 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 c0f27abe8..6c60c9c55 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, // 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 946843626..cf160ccac 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, /// 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());