Skip to content

Commit

Permalink
Append changes to history before & after each command
Browse files Browse the repository at this point in the history
TODO ensure cursor is in view in insert mode. Maybe have a helper for
executing any command that ensures this.

Related to <https://redirect.github.com/helix-editor/helix/pull/7226>
  • Loading branch information
the-mikedavis committed Mar 27, 2024
1 parent dd28158 commit f7b2f77
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 61 deletions.
61 changes: 45 additions & 16 deletions helix-term/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
};
Expand Down Expand Up @@ -133,6 +133,45 @@ impl<'a> Context<'a> {
pub fn count(&self) -> usize {
self.count.map_or(1, |v| v.get())
}

pub fn execute<F: FnOnce(&mut Self)>(&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<F: FnOnce(&mut Self)>(&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]
Expand Down Expand Up @@ -256,6 +295,10 @@ impl MappableCommand {
}
}

pub fn is_typable(&self) -> bool {
matches!(self, Self::Typable { .. })
}

#[rustfmt::skip]
static_commands!(
no_op, "Do nothing",
Expand Down Expand Up @@ -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)));
},
Expand Down
61 changes: 16 additions & 45 deletions helix-term/src/ui/editor.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use crate::{
commands::{self, OnKeyCallback},
compositor::{Component, Context, Event, EventResult},
events::{OnModeSwitch, PostCommand},
key,
keymap::{KeymapResult, Keymaps},
ui::{
Expand Down Expand Up @@ -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 {
Expand All @@ -922,7 +914,6 @@ impl EditorView {
self.last_insert.1.clear();
}
}

last_mode = current_mode;
};

Expand All @@ -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);
}
}
}
Expand All @@ -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
Expand Down Expand Up @@ -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)
Expand All @@ -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);
Expand All @@ -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);
}
Expand All @@ -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);
}
Expand Down Expand Up @@ -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) => {
Expand All @@ -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 => {
Expand Down Expand Up @@ -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 {
Expand Down

0 comments on commit f7b2f77

Please sign in to comment.