changeset 35643:610d9a6833c3 v9.1.0563

patch 9.1.0563: Cannot process any Key event Commit: https://github.com/vim/vim/commit/83678849095abfa914f5405e8a5e5d23346b5917 Author: Shougo Matsushita <Shougo.Matsu@gmail.com> Date: Thu Jul 11 22:05:12 2024 +0200 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>
author Christian Brabandt <cb@256bit.org>
date Thu, 11 Jul 2024 22:10:17 +0200
parents f0e5c3d311a0
children e478046e7f34
files runtime/doc/autocmd.txt runtime/doc/eval.txt runtime/doc/tags runtime/doc/version9.txt src/autocmd.c src/getchar.c src/proto/autocmd.pro src/testdir/test_autocmd.vim src/version.c src/vim.h
diffstat 10 files changed, 182 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- 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 Ins
 							*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
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -1995,7 +1995,8 @@ v:beval_winid	The |window-ID| of the win
 					*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
--- 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*
--- 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
--- 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
--- 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).
--- 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);
--- 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
--- 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,
--- 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