add `insert_character_interactive` command

this command inserts a character of the user's choice underneath the current cursor, without exiting normal mode. multiple cursors are handled correctly.

i chose `C-y` for the default mapping because it doesn't conflict with anything else. i would have preferred `C-i` (i for insert), but that conflicts with `jump_forward`; and additionally most terminals do not distinguish `C-i` from `tab`, so it's not a good choice for the default.

this command does not have precedence in vim or kakoune to my knowledge, but at least vim allows replicating it with remap command (https://superuser.com/questions/581572/insert-single-character-in-vim), while helix currently does not, since there's no way to say "a character entered by the user".
pull/11411/head
jyn 4 months ago
parent cfe80acb6f
commit 08ec9d5ada

@ -397,6 +397,7 @@ impl MappableCommand {
smart_tab, "Insert tab if all cursors have all whitespace to their left; otherwise, run a separate command.",
insert_tab, "Insert tab char",
insert_newline, "Insert newline char",
insert_char_interactive, "Insert an interactively-chosen char",
delete_char_backward, "Delete previous char",
delete_char_forward, "Delete next char",
delete_word_backward, "Delete previous word",
@ -3727,7 +3728,7 @@ fn hunk_range(hunk: Hunk, text: RopeSlice) -> Range {
}
pub mod insert {
use crate::events::PostInsertChar;
use crate::{events::PostInsertChar, key};
use super::*;
pub type Hook = fn(&Rope, &Selection, char) -> Option<Transaction>;
@ -3802,11 +3803,15 @@ pub mod insert {
}
pub fn insert_tab(cx: &mut Context) {
insert_tab_impl(cx, 1)
}
fn insert_tab_impl(cx: &mut Context, count: usize) {
let (view, doc) = current!(cx.editor);
// TODO: round out to nearest indentation level (for example a line with 3 spaces should
// indent by one to reach 4 spaces).
let indent = Tendril::from(doc.indent_style.as_str());
let indent = Tendril::from(doc.indent_style.as_str().repeat(count));
let transaction = Transaction::insert(
doc.text(),
&doc.selection(view.id).clone().cursors(doc.text().slice(..)),
@ -3815,6 +3820,34 @@ pub mod insert {
doc.apply(&transaction, view.id);
}
pub fn insert_char_interactive(cx: &mut Context) {
let count = cx.count();
// need to wait for next key
cx.on_next_key(move |cx, event| {
match event {
KeyEvent {
code: KeyCode::Char(ch),
..
} => {
for _ in 0..count {
insert::insert_char(cx, ch)
}
}
key!(Enter) => {
if count != 1 {
cx.editor
.set_error("inserting multiple newlines not yet supported");
return;
}
insert_newline(cx)
}
key!(Tab) => insert_tab_impl(cx, count),
_ => (),
};
});
}
pub fn insert_newline(cx: &mut Context) {
let (view, doc) = current_ref!(cx.editor);
let text = doc.text().slice(..);

Loading…
Cancel
Save