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>
This commit is contained in:
Shougo Matsushita
2024-07-11 22:05:12 +02:00
committed by Christian Brabandt
parent e7b98ab96e
commit 8367884909
10 changed files with 182 additions and 2 deletions

View File

@ -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

View File

@ -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

View File

@ -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*

View File

@ -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

View File

@ -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"),
@ -2021,6 +2022,15 @@ has_insertcharpre(void)
return (first_autopat[(int)EVENT_INSERTCHARPRE] != NULL);
}
/*
* 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.
*/
@ -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

View File

@ -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).

View File

@ -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);

View File

@ -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

View File

@ -704,6 +704,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
563,
/**/
562,
/**/

View File

@ -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