Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle suffixes of cancelled keymaps in insert mode #8486

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 26 additions & 17 deletions helix-term/src/ui/editor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -907,28 +907,37 @@ impl EditorView {
}

fn insert_mode(&mut self, cx: &mut commands::Context, event: KeyEvent) {
if let Some(keyresult) = self.handle_keymap_event(Mode::Insert, cx, event) {
match keyresult {
KeymapResult::NotFound => {
if let Some(ch) = event.char() {
commands::insert::insert_char(cx, ch)
let mut stack = vec![event];

while let Some(event) = stack.pop() {
if let Some(keyresult) = self.handle_keymap_event(Mode::Insert, cx, event) {
match keyresult {
KeymapResult::NotFound => {
if let Some(ch) = event.char() {
commands::insert::insert_char(cx, ch)
}
}
}
KeymapResult::Cancelled(pending) => {
for ev in pending {
match ev.char() {
Some(ch) => commands::insert::insert_char(cx, ch),
None => {
if let KeymapResult::Matched(command) =
self.keymaps.get(Mode::Insert, ev)
{
command.execute(cx);
}
KeymapResult::Cancelled(pending) => {
let mut pending = pending.into_iter();
if let Some(first) = pending.next() {
// Note that since this is the first pending key, we know
// it can't map to a command by itself.
match first.char() {
// The first key is both the start of a menu and a regular
// insert key. The user may have intended to type it as an
// insert, and then execute the remaining suffix of keys.
Some(ch) => commands::insert::insert_char(cx, ch),
// If the first key is not a character to insert, then we
// assume the user intended to enter a command menu, so we
// should just discard pending keys if they don't match.
None => continue,
}
}

stack.extend(pending.rev());
}
_ => unreachable!(),
}
_ => unreachable!(),
}
}
}
Expand Down
1 change: 1 addition & 0 deletions helix-term/tests/integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ mod test {
mod auto_indent;
mod auto_pairs;
mod commands;
mod insert_keymap_suffix;
mod languages;
mod movement;
mod prompt;
Expand Down
2 changes: 0 additions & 2 deletions helix-term/tests/test/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -349,8 +349,6 @@ impl AppBuilder {
self
}

// Remove this attribute once `with_config` is used in a test:
#[allow(dead_code)]
pub fn with_config(mut self, mut config: Config) -> Self {
let keys = replace(&mut config.keys, helix_term::keymap::default());
merge_keys(&mut config.keys, keys);
Expand Down
36 changes: 36 additions & 0 deletions helix-term/tests/test/insert_keymap_suffix.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
use super::*;

#[tokio::test(flavor = "multi_thread")]
async fn insert_keymap_suffix() -> anyhow::Result<()> {
test_with_config(
AppBuilder::new().with_config(config()),
("#[|]#", "iselffd", "self#[|]#"),
)
.await?;

Ok(())
}

#[tokio::test(flavor = "multi_thread")]
async fn insert_keymap_suffix_non_char() -> anyhow::Result<()> {
test_with_config(
AppBuilder::new().with_config(config()),
("#[|]#", "i<F1>ua", "a#[|]#"),
)
.await?;

Ok(())
}

fn config() -> Config {
let config = r#"
[keys.insert]
f.d = "normal_mode"
F1.j = "insert_newline"
"#;
Config::load(
Ok(config.to_owned()),
Err(helix_term::config::ConfigLoadError::default()),
)
.unwrap()
}
Loading