patch 9.1.0563: Cannot process any Key event
Problem: Cannot process any Key event
Solution: Add the KeyInputPre autocmd
(Shougo Matsushita)
closes: #15182
Co-authored-by: zeertzjq <zeertzjq@outlook.com>
Co-authored-by: K.Takata <kentkt@csc.jp>
Signed-off-by: Shougo Matsushita <Shougo.Matsu@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
diff --git a/src/autocmd.c b/src/autocmd.c
index e60eac7..00f41bd 100644
--- a/src/autocmd.c
+++ b/src/autocmd.c
@@ -155,6 +155,7 @@
KEYVALUE_ENTRY(EVENT_INSERTENTER, "InsertEnter"),
KEYVALUE_ENTRY(EVENT_INSERTLEAVE, "InsertLeave"),
KEYVALUE_ENTRY(EVENT_INSERTLEAVEPRE, "InsertLeavePre"),
+ KEYVALUE_ENTRY(EVENT_KEYINPUTPRE, "KeyInputPre"),
KEYVALUE_ENTRY(EVENT_MENUPOPUP, "MenuPopup"),
KEYVALUE_ENTRY(EVENT_MODECHANGED, "ModeChanged"),
KEYVALUE_ENTRY(EVENT_OPTIONSET, "OptionSet"),
@@ -2022,6 +2023,15 @@
}
/*
+ * Return TRUE when there is an KeyInputPre autocommand defined.
+ */
+ int
+has_keyinputpre(void)
+{
+ return (first_autopat[(int)EVENT_KEYINPUTPRE] != NULL);
+}
+
+/*
* Return TRUE when there is an CmdUndefined autocommand defined.
*/
int
@@ -2256,6 +2266,7 @@
|| event == EVENT_CMDWINLEAVE
|| event == EVENT_CMDUNDEFINED
|| event == EVENT_FUNCUNDEFINED
+ || event == EVENT_KEYINPUTPRE
|| event == EVENT_REMOTEREPLY
|| event == EVENT_SPELLFILEMISSING
|| event == EVENT_QUICKFIXCMDPRE
diff --git a/src/getchar.c b/src/getchar.c
index 1c544da..0a37b7b 100644
--- a/src/getchar.c
+++ b/src/getchar.c
@@ -96,6 +96,9 @@
static void updatescript(int c);
static int vgetorpeek(int);
static int inchar(char_u *buf, int maxlen, long wait_time);
+#ifdef FEAT_EVAL
+static int do_key_input_pre(int c);
+#endif
/*
* Free and clear a buffer.
@@ -2130,6 +2133,10 @@
}
#endif
+#ifdef FEAT_EVAL
+ c = do_key_input_pre(c);
+#endif
+
// Need to process the character before we know it's safe to do something
// else.
if (c != K_IGNORE)
@@ -2138,6 +2145,74 @@
return c;
}
+#ifdef FEAT_EVAL
+/*
+ * Handle the InsertCharPre autocommand.
+ * "c" is the character that was typed.
+ * Return new input character.
+ */
+ static int
+do_key_input_pre(int c)
+{
+ int res = c;
+ char_u buf[MB_MAXBYTES + 1];
+ char_u curr_mode[MODE_MAX_LENGTH];
+ int save_State = State;
+ save_v_event_T save_v_event;
+ dict_T *v_event;
+
+ // Return quickly when there is nothing to do.
+ if (!has_keyinputpre())
+ return res;
+
+ if (IS_SPECIAL(c))
+ {
+ buf[0] = K_SPECIAL;
+ buf[1] = KEY2TERMCAP0(c);
+ buf[2] = KEY2TERMCAP1(c);
+ buf[3] = NUL;
+ }
+ else
+ buf[(*mb_char2bytes)(c, buf)] = NUL;
+
+ get_mode(curr_mode);
+
+ // Lock the text to avoid weird things from happening.
+ ++textlock;
+ set_vim_var_string(VV_CHAR, buf, -1); // set v:char
+
+ v_event = get_v_event(&save_v_event);
+ (void)dict_add_bool(v_event, "typed", KeyTyped);
+
+ if (apply_autocmds(EVENT_KEYINPUTPRE, curr_mode, curr_mode, FALSE, curbuf)
+ && STRCMP(buf, get_vim_var_str(VV_CHAR)) != 0)
+ {
+ // Get the value of v:char. It may be empty or more than one
+ // character. Only use it when changed, otherwise continue with the
+ // original character.
+ char_u *v_char;
+
+ v_char = get_vim_var_str(VV_CHAR);
+
+ // Convert special bytes when it is special string.
+ if (STRLEN(v_char) >= 3 && v_char[0] == K_SPECIAL)
+ res = TERMCAP2KEY(v_char[1], v_char[2]);
+ else if (STRLEN(v_char) > 0)
+ res = PTR2CHAR(v_char);
+ }
+
+ restore_v_event(v_event, &save_v_event);
+
+ set_vim_var_string(VV_CHAR, NULL, -1); // clear v:char
+ --textlock;
+
+ // Restore the State, it may have been changed.
+ State = save_State;
+
+ return res;
+}
+#endif
+
/*
* Like vgetc(), but never return a NUL when called recursively, get a key
* directly from the user (ignoring typeahead).
diff --git a/src/proto/autocmd.pro b/src/proto/autocmd.pro
index 4a502da..920987a 100644
--- a/src/proto/autocmd.pro
+++ b/src/proto/autocmd.pro
@@ -26,6 +26,7 @@
int has_textchangedI(void);
int has_textchangedP(void);
int has_insertcharpre(void);
+int has_keyinputpre(void);
int has_cmdundefined(void);
int has_textyankpost(void);
int has_completechanged(void);
diff --git a/src/testdir/test_autocmd.vim b/src/testdir/test_autocmd.vim
index bcbddf9..5ad1730 100644
--- a/src/testdir/test_autocmd.vim
+++ b/src/testdir/test_autocmd.vim
@@ -4752,4 +4752,74 @@
set hidden&vim
endfunc
+func Test_KeyInputPre()
+ " Consume previous keys
+ call feedkeys('', 'ntx')
+
+ " KeyInputPre can record input keys.
+ let s:keys = []
+ au KeyInputPre n call add(s:keys, v:char)
+
+ call feedkeys('jkjkjjj', 'ntx')
+ call assert_equal(
+ \ ['j', 'k', 'j', 'k', 'j', 'j', 'j'],
+ \ s:keys)
+
+ unlet s:keys
+ au! KeyInputPre
+
+ " KeyInputPre can handle multibyte.
+ let s:keys = []
+ au KeyInputPre * call add(s:keys, v:char)
+ edit Xxx1
+
+ call feedkeys("iあ\<ESC>", 'ntx')
+ call assert_equal(['i', "あ", "\<ESC>"], s:keys)
+
+ bwipe! Xxx1
+ unlet s:keys
+ au! KeyInputPre
+
+ " KeyInputPre can change input keys.
+ au KeyInputPre i if v:char ==# 'a' | let v:char = 'b' | endif
+ edit Xxx1
+
+ call feedkeys("iaabb\<ESC>", 'ntx')
+ call assert_equal(getline('.'), 'bbbb')
+
+ bwipe! Xxx1
+ au! KeyInputPre
+
+ " KeyInputPre returns multiple characters.
+ au KeyInputPre i if v:char ==# 'a' | let v:char = 'cccc' | endif
+ edit Xxx1
+
+ call feedkeys("iaabb\<ESC>", 'ntx')
+ call assert_equal(getline('.'), 'ccbb')
+
+ bwipe! Xxx1
+ au! KeyInputPre
+
+ " KeyInputPre can use special keys.
+ au KeyInputPre i if v:char ==# 'a' | let v:char = "\<Ignore>" | endif
+ edit Xxx1
+
+ call feedkeys("iaabb\<ESC>", 'ntx')
+ call assert_equal(getline('.'), 'bb')
+
+ bwipe! Xxx1
+ au! KeyInputPre
+
+ " Test for v:event.typed
+ au KeyInputPre n call assert_true(v:event.typed)
+ call feedkeys('j', 'ntx')
+
+ au! KeyInputPre
+
+ au KeyInputPre n call assert_false(v:event.typed)
+ call feedkeys('j', 'nx')
+
+ au! KeyInputPre
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/version.c b/src/version.c
index 39c29b0..ff65484 100644
--- a/src/version.c
+++ b/src/version.c
@@ -705,6 +705,8 @@
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 563,
+/**/
562,
/**/
561,
diff --git a/src/vim.h b/src/vim.h
index ae7458a..a359162 100644
--- a/src/vim.h
+++ b/src/vim.h
@@ -1397,6 +1397,7 @@
EVENT_INSERTENTER, // when entering Insert mode
EVENT_INSERTLEAVEPRE, // just before leaving Insert mode
EVENT_INSERTLEAVE, // just after leaving Insert mode
+ EVENT_KEYINPUTPRE, // before key input
EVENT_MENUPOPUP, // just before popup menu is displayed
EVENT_MODECHANGED, // after changing the mode
EVENT_OPTIONSET, // option was set