summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorShougo Matsushita <Shougo.Matsu@gmail.com>2024-07-11 22:05:12 +0200
committerChristian Brabandt <cb@256bit.org>2024-07-11 22:05:12 +0200
commit83678849095abfa914f5405e8a5e5d23346b5917 (patch)
tree421ff1ac0c9fbdf8a82f7fab5ea4ebca7a52fabe
parente7b98ab96e1f1bd12032c620615a2c69adbf018d (diff)
patch 9.1.0563: Cannot process any Key eventv9.1.0563
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>
-rw-r--r--runtime/doc/autocmd.txt19
-rw-r--r--runtime/doc/eval.txt3
-rw-r--r--runtime/doc/tags1
-rw-r--r--runtime/doc/version9.txt1
-rw-r--r--src/autocmd.c11
-rw-r--r--src/getchar.c75
-rw-r--r--src/proto/autocmd.pro1
-rw-r--r--src/testdir/test_autocmd.vim70
-rw-r--r--src/version.c2
-rw-r--r--src/vim.h1
10 files changed, 182 insertions, 2 deletions
diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt
index 23f1cbf92d..1e9c896545 100644
--- a/runtime/doc/autocmd.txt
+++ b/runtime/doc/autocmd.txt
@@ -1,4 +1,4 @@
-*autocmd.txt* For Vim version 9.1. Last change: 2024 Jul 09
+*autocmd.txt* For Vim version 9.1. Last change: 2024 Jul 11
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -439,6 +439,8 @@ Name triggered by ~
|CompleteDone| after Insert mode completion is done, after clearing
info
+|KeyInputPre| just before a key is processed
+
|User| to be used in combination with ":doautocmd"
|SigUSR1| after the SIGUSR1 signal has been detected
@@ -977,6 +979,21 @@ InsertLeavePre Just before leaving Insert mode. Also when
*InsertLeave*
InsertLeave Just after leaving Insert mode. Also when
using CTRL-O |i_CTRL-O|. But not for |i_CTRL-C|.
+ *KeyInputPre*
+KeyInputPre Just before a key is processed. The pattern is
+ matched against a string that indicates the
+ current mode, which is the same as what is
+ returned by `mode(1)`.
+ The |v:char| variable indicates the key typed
+ and can be changed during the event to process
+ a different key. When |v:char| is not a
+ single character or a special key, the first
+ character is used.
+ The following values of |v:event| are set:
+ typed The key is typed or not.
+ It is not allowed to change the text
+ |textlock| or the current mode.
+ {only with the +eval feature}
*MenuPopup*
MenuPopup Just before showing the popup menu (under the
right mouse button). Useful for adjusting the
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt
index 18cd2c1237..eddafd078e 100644
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -1995,7 +1995,8 @@ v:beval_winid The |window-ID| of the window, over which the mouse pointer
*v:char* *char-variable*
v:char Argument for evaluating 'formatexpr' and used for the typed
character when using <expr> in an abbreviation |:map-<expr>|.
- It is also used by the |InsertCharPre| and |InsertEnter| events.
+ It is also used by the |InsertCharPre|, |InsertEnter| and
+ |KeyInputPre| events.
*v:charconvert_from* *charconvert_from-variable*
v:charconvert_from
diff --git a/runtime/doc/tags b/runtime/doc/tags
index 1238a9c8af..b3761e88e2 100644
--- a/runtime/doc/tags
+++ b/runtime/doc/tags
@@ -5554,6 +5554,7 @@ Jobs eval.txt /*Jobs*
K various.txt /*K*
KDE gui_x11.txt /*KDE*
KVim gui_x11.txt /*KVim*
+KeyInputPre autocmd.txt /*KeyInputPre*
Kibaale uganda.txt /*Kibaale*
Korean mbyte.txt /*Korean*
L motion.txt /*L*
diff --git a/runtime/doc/version9.txt b/runtime/doc/version9.txt
index ed1cd43081..cfeb28c7b9 100644
--- a/runtime/doc/version9.txt
+++ b/runtime/doc/version9.txt
@@ -41607,6 +41607,7 @@ Functions: ~
Autocommands: ~
|CursorMovedC| after the cursor was moved in the comamnd-line
+|KeyInputPre| process any Key event in any mode
|SessionWritePost| after writing the session file |:mksession|
|TermResponseAll| after the terminal response to |t_RV| and others is
received
diff --git a/src/autocmd.c b/src/autocmd.c
index e60eac794d..00f41bddd9 100644
--- a/src/autocmd.c
+++ b/src/autocmd.c
@@ -155,6 +155,7 @@ static keyvalue_T event_tab[] = {
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 @@ has_insertcharpre(void)
}
/*
+ * 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 @@ apply_autocmds_group(
|| 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 1c544da634..0a37b7b111 100644
--- a/src/getchar.c
+++ b/src/getchar.c
@@ -96,6 +96,9 @@ static void closescript(void);
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 @@ vgetc(void)
}
#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 @@ vgetc(void)
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 4a502da625..920987a8c7 100644
--- a/src/proto/autocmd.pro
+++ b/src/proto/autocmd.pro
@@ -26,6 +26,7 @@ int has_textchanged(void);
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 bcbddf9789..5ad1730c40 100644
--- a/src/testdir/test_autocmd.vim
+++ b/src/testdir/test_autocmd.vim
@@ -4752,4 +4752,74 @@ func Test_BufEnter_botline()
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 39c29b0649..ff654844fc 100644
--- a/src/version.c
+++ b/src/version.c
@@ -705,6 +705,8 @@ static char *(features[]) =
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 ae7458ad7e..a35916246e 100644
--- a/src/vim.h
+++ b/src/vim.h
@@ -1397,6 +1397,7 @@ enum auto_event
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