diff options
Diffstat (limited to 'helix-view/src')
-rw-r--r-- | helix-view/src/editor.rs | 70 |
1 files changed, 69 insertions, 1 deletions
diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index 973cf82e..c13a6673 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -1005,6 +1005,8 @@ impl Editor { return; } + self.enter_normal_mode(); + match action { Action::Replace => { let (view, doc) = current_ref!(self); @@ -1025,6 +1027,9 @@ impl Editor { let (view, doc) = current!(self); let view_id = view.id; + // Append any outstanding changes to history in the old document. + doc.append_changes_to_history(view); + if remove_empty_scratch { // Copy `doc.id` into a variable before calling `self.documents.remove`, which requires a mutable // borrow, invalidating direct access to `doc.id`. @@ -1262,7 +1267,7 @@ impl Editor { // if leaving the view: mode should reset and the cursor should be // within view if prev_id != view_id { - self.mode = Mode::Normal; + self.enter_normal_mode(); self.ensure_cursor_in_view(view_id); // Update jumplist selections with new document changes. @@ -1427,4 +1432,67 @@ impl Editor { Ok(()) } + + /// Switches the editor into normal mode. + pub fn enter_normal_mode(&mut self) { + use helix_core::{graphemes, Range}; + + if self.mode == Mode::Normal { + return; + } + + self.mode = Mode::Normal; + let (view, doc) = current!(self); + + try_restore_indent(doc, view); + + // if leaving append mode, move cursor back by 1 + if doc.restore_cursor { + let text = doc.text().slice(..); + let selection = doc.selection(view.id).clone().transform(|range| { + Range::new( + range.from(), + graphemes::prev_grapheme_boundary(text, range.to()), + ) + }); + + doc.set_selection(view.id, selection); + doc.restore_cursor = false; + } + } +} + +fn try_restore_indent(doc: &mut Document, view: &mut View) { + use helix_core::{ + chars::char_is_whitespace, line_ending::line_end_char_index, Operation, Transaction, + }; + + fn inserted_a_new_blank_line(changes: &[Operation], pos: usize, line_end_pos: usize) -> bool { + if let [Operation::Retain(move_pos), Operation::Insert(ref inserted_str), Operation::Retain(_)] = + changes + { + move_pos + inserted_str.len() == pos + && inserted_str.starts_with('\n') + && inserted_str.chars().skip(1).all(char_is_whitespace) + && pos == line_end_pos // ensure no characters exists after current position + } else { + false + } + } + + let doc_changes = doc.changes().changes(); + let text = doc.text().slice(..); + let range = doc.selection(view.id).primary(); + let pos = range.cursor(text); + let line_end_pos = line_end_char_index(&text, range.cursor_line(text)); + + if inserted_a_new_blank_line(doc_changes, pos, line_end_pos) { + // Removes tailing whitespaces. + let transaction = + Transaction::change_by_selection(doc.text(), doc.selection(view.id), |range| { + let line_start_pos = text.line_to_char(range.cursor_line(text)); + (line_start_pos, pos, None) + }); + crate::apply_transaction(&transaction, doc, view); + } } |