From f7b2f770730cf9a8e0364affe9e78782b830080e Mon Sep 17 00:00:00 2001 From: Michael Davis Date: Sun, 18 Feb 2024 15:30:30 -0500 Subject: [PATCH] Append changes to history before & after each command TODO ensure cursor is in view in insert mode. Maybe have a helper for executing any command that ensures this. Related to --- helix-term/src/commands.rs | 61 +++++++++++++++++++++++++++---------- helix-term/src/ui/editor.rs | 61 ++++++++++--------------------------- 2 files changed, 61 insertions(+), 61 deletions(-) diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 16fa40389418c..fbf2fd76a687b 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -51,7 +51,7 @@ use movement::Movement; use crate::{ args, compositor::{self, Component, Compositor}, - filter_picker_entry, + events, filter_picker_entry, job::Callback, ui::{self, overlay::overlaid, Picker, PickerColumn, Popup, Prompt, PromptEvent}, }; @@ -133,6 +133,45 @@ impl<'a> Context<'a> { pub fn count(&self) -> usize { self.count.map_or(1, |v| v.get()) } + + pub fn execute(&mut self, execute_fn: F) { + self.execute_impl(false, execute_fn) + } + + pub fn execute_command(&mut self, command: &MappableCommand) { + self.execute_impl(command.is_typable(), |cx| { + command.execute(cx); + helix_event::dispatch(events::PostCommand { command, cx }); + }) + } + + fn execute_impl(&mut self, append_changes: bool, execute_fn: F) { + let pre_command_mode = self.editor.mode(); + if pre_command_mode != Mode::Insert || append_changes { + let (view, doc) = current!(self.editor); + doc.append_changes_to_history(view); + } + + execute_fn(self); + + let post_command_mode = self.editor.mode(); + if post_command_mode != pre_command_mode { + helix_event::dispatch(events::OnModeSwitch { + old_mode: pre_command_mode, + new_mode: post_command_mode, + cx: self, + }); + } + + if !self.editor.tree.is_empty() { + let scrolloff = self.editor.config().scrolloff; + let (view, doc) = current!(self.editor); + if post_command_mode != Mode::Insert || append_changes { + doc.append_changes_to_history(view); + } + view.ensure_cursor_in_view(doc, scrolloff); + } + } } #[inline] @@ -256,6 +295,10 @@ impl MappableCommand { } } + pub fn is_typable(&self) -> bool { + matches!(self, Self::Typable { .. }) + } + #[rustfmt::skip] static_commands!( no_op, "Do nothing", @@ -3094,22 +3137,8 @@ pub fn command_palette(cx: &mut Context) { on_next_key_callback: None, jobs: cx.jobs, }; - let focus = view!(ctx.editor).id; - command.execute(&mut ctx); - - if ctx.editor.tree.contains(focus) { - let config = ctx.editor.config(); - let mode = ctx.editor.mode(); - let view = view_mut!(ctx.editor, focus); - let doc = doc_mut!(ctx.editor, &view.doc); - - view.ensure_cursor_in_view(doc, config.scrolloff); - - if mode != Mode::Insert { - doc.append_changes_to_history(view); - } - } + ctx.execute_command(command); }); compositor.push(Box::new(overlaid(picker))); }, diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs index 3ab139d6a5f90..5b5b3048086b5 100644 --- a/helix-term/src/ui/editor.rs +++ b/helix-term/src/ui/editor.rs @@ -1,7 +1,6 @@ use crate::{ commands::{self, OnKeyCallback}, compositor::{Component, Context, Event, EventResult}, - events::{OnModeSwitch, PostCommand}, key, keymap::{KeymapResult, Keymaps}, ui::{ @@ -902,17 +901,10 @@ impl EditorView { cxt.editor.autoinfo = self.keymaps.sticky().map(|node| node.infobox()); let mut execute_command = |command: &commands::MappableCommand| { - command.execute(cxt); - helix_event::dispatch(PostCommand { command, cx: cxt }); + cxt.execute_command(command); let current_mode = cxt.editor.mode(); if current_mode != last_mode { - helix_event::dispatch(OnModeSwitch { - old_mode: last_mode, - new_mode: current_mode, - cx: cxt, - }); - // HAXX: if we just entered insert mode from normal, clear key buf // and record the command that got us into this mode. if current_mode == Mode::Insert { @@ -922,7 +914,6 @@ impl EditorView { self.last_insert.1.clear(); } } - last_mode = current_mode; }; @@ -946,18 +937,18 @@ impl EditorView { match keyresult { KeymapResult::NotFound => { if let Some(ch) = event.char() { - commands::insert::insert_char(cx, ch) + cx.execute(|cx| commands::insert::insert_char(cx, ch)) } } KeymapResult::Cancelled(pending) => { for ev in pending { match ev.char() { - Some(ch) => commands::insert::insert_char(cx, ch), + Some(ch) => cx.execute(|cx| commands::insert::insert_char(cx, ch)), None => { if let KeymapResult::Matched(command) = self.keymaps.get(Mode::Insert, ev) { - command.execute(cx); + cx.execute_command(&command); } } } @@ -984,7 +975,7 @@ impl EditorView { (key!('.'), _) if self.keymaps.pending().is_empty() => { for _ in 0..cxt.editor.count.map_or(1, NonZeroUsize::into) { // first execute whatever put us into insert mode - self.last_insert.0.execute(cxt); + cxt.execute_command(&self.last_insert.0); let mut last_savepoint = None; let mut last_request_savepoint = None; // then replay the inputs @@ -1276,8 +1267,9 @@ impl EditorView { }; if should_yank { - commands::MappableCommand::yank_main_selection_to_primary_clipboard - .execute(cxt); + cxt.execute_command( + &commands::MappableCommand::yank_main_selection_to_primary_clipboard, + ); EventResult::Consumed(None) } else { EventResult::Ignored(None) @@ -1294,9 +1286,9 @@ impl EditorView { { doc.set_selection(view_id, Selection::point(pos)); if modifiers == KeyModifiers::ALT { - commands::MappableCommand::dap_edit_log.execute(cxt); + cxt.execute_command(&commands::MappableCommand::dap_edit_log); } else { - commands::MappableCommand::dap_edit_condition.execute(cxt); + cxt.execute_command(&commands::MappableCommand::dap_edit_condition); } return EventResult::Consumed(None); @@ -1313,8 +1305,9 @@ impl EditorView { } if modifiers == KeyModifiers::ALT { - commands::MappableCommand::replace_selections_with_primary_clipboard - .execute(cxt); + cxt.execute_command( + &commands::MappableCommand::replace_selections_with_primary_clipboard, + ); return EventResult::Consumed(None); } @@ -1323,7 +1316,7 @@ impl EditorView { let doc = doc_mut!(editor, &view!(editor, view_id).doc); doc.set_selection(view_id, Selection::point(pos)); cxt.editor.focus(view_id); - commands::MappableCommand::paste_primary_clipboard_before.execute(cxt); + cxt.execute_command(&commands::MappableCommand::paste_primary_clipboard_before); return EventResult::Consumed(None); } @@ -1355,20 +1348,9 @@ impl Component for EditorView { Event::Paste(contents) => { self.handle_non_key_input(&mut cx); cx.count = cx.editor.count; - commands::paste_bracketed_value(&mut cx, contents.clone()); + cx.execute(|cx| commands::paste_bracketed_value(cx, contents.clone())); cx.editor.count = None; - let config = cx.editor.config(); - let mode = cx.editor.mode(); - let (view, doc) = current!(cx.editor); - view.ensure_cursor_in_view(doc, config.scrolloff); - - // Store a history state if not in insert mode. Otherwise wait till we exit insert - // to include any edits to the paste in the history state. - if mode != Mode::Insert { - doc.append_changes_to_history(view); - } - EventResult::Consumed(None) } Event::Resize(_width, _height) => { @@ -1387,7 +1369,7 @@ impl Component for EditorView { if let Some(on_next_key) = self.on_next_key.take() { // if there's a command waiting input, do that first - on_next_key(&mut cx, key); + cx.execute(|cx| on_next_key(cx, key)); } else { match mode { Mode::Insert => { @@ -1451,17 +1433,6 @@ impl Component for EditorView { return EventResult::Ignored(None); } - let config = cx.editor.config(); - let mode = cx.editor.mode(); - let (view, doc) = current!(cx.editor); - - view.ensure_cursor_in_view(doc, config.scrolloff); - - // Store a history state if not in insert mode. This also takes care of - // committing changes when leaving insert mode. - if mode != Mode::Insert { - doc.append_changes_to_history(view); - } let callback = if callbacks.is_empty() { None } else {