This commit is contained in:
Shougo 2024-09-12 21:26:46 +09:00 committed by GitHub
commit f7705dfbb5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 182 additions and 0 deletions

View File

@ -736,6 +736,26 @@ 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 after mappings
matched against a string that indicates the
have been applied. The pattern is matched
current mode, which is the same as what is
against a string that indicates the current
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.
typedchar The (actual) typed key since
the last |KeyInputPre| call.
Note: "typedchar" may be empty if successive
|KeyInputPre| autocmds are processed.
It is not allowed to change the text
|textlock| or the current mode.
*MenuPopup*
MenuPopup Just before showing the popup menu (under the
right mouse button). Useful for adjusting the

View File

@ -71,6 +71,7 @@ return {
'InsertEnter', -- when entering Insert mode
'InsertLeave', -- just after leaving Insert mode
'InsertLeavePre', -- just before leaving Insert mode
'KeyInputPre', -- just before a key is processed
'LspAttach', -- after an LSP client attaches to a buffer
'LspDetach', -- after an LSP client detaches from a buffer
'LspRequest', -- after an LSP request is started, canceled, or completed

View File

@ -1713,6 +1713,7 @@ bool apply_autocmds_group(event_T event, char *fname, char *fname_io, bool force
|| event == EVENT_DIRCHANGEDPRE
|| event == EVENT_FILETYPE
|| event == EVENT_FUNCUNDEFINED
|| event == EVENT_KEYINPUTPRE
|| event == EVENT_MENUPOPUP
|| event == EVENT_MODECHANGED
|| event == EVENT_OPTIONSET

View File

@ -14,6 +14,7 @@
#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
#include "nvim/ascii_defs.h"
#include "nvim/autocmd.h"
#include "nvim/buffer_defs.h"
#include "nvim/charset.h"
#include "nvim/cursor.h"
@ -112,6 +113,9 @@ static size_t on_key_ignore_len = 0;
static int typeahead_char = 0; ///< typeahead char that's not flushed
static uint8_t typedchars[MAXMAPLEN + 1] = { NUL }; // typed chars before map
static int typedchars_pos = 0;
/// When block_redo is true the redo buffer will not be changed.
/// Used by edit() to repeat insertions.
static bool block_redo = false;
@ -1482,6 +1486,10 @@ static void updatescript(int c)
(!!p_fs || idle)); // Always fsync at idle (CursorHold).
count = 0;
}
if (typedchars_pos < MAXMAPLEN) {
typedchars[typedchars_pos] = (uint8_t)c;
typedchars_pos++;
}
}
/// Merge "modifiers" into "c_arg".
@ -1561,6 +1569,74 @@ static void add_byte_to_showcmd(uint8_t byte)
}
}
// Handle the InsertCharPre autocommand.
// "c" is the character that was typed.
// Return new input character.
static int do_key_input_pre(int c)
{
// Return quickly when there is nothing to do.
if (!has_event(EVENT_KEYINPUTPRE)) {
return c;
}
uint8_t buf[MB_MAXBYTES + 1];
char curr_mode[MODE_MAX_LENGTH];
int save_State = State;
save_v_event_T save_v_event;
if (IS_SPECIAL(c)) {
buf[0] = K_SPECIAL;
buf[1] = KEY2TERMCAP0(c);
buf[2] = KEY2TERMCAP1(c);
buf[3] = NUL;
} else {
buf[utf_char2bytes(c, (char *)buf)] = NUL;
}
typedchars[typedchars_pos] = NUL;
vim_unescape_ks((char *)typedchars);
get_mode(curr_mode);
// Lock the text to avoid weird things from happening.
textlock++;
set_vim_var_string(VV_CHAR, (char *)buf, -1); // set v:char
dict_T *v_event;
v_event = get_v_event(&save_v_event);
(void)tv_dict_add_bool(v_event, S_LEN("typed"), KeyTyped);
(void)tv_dict_add_str(v_event, S_LEN("typedchar"), (char *)typedchars);
int res = c;
if (apply_autocmds(EVENT_KEYINPUTPRE, curr_mode, curr_mode, false, curbuf)
&& strcmp((char *)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 *v_char;
v_char = get_vim_var_str(VV_CHAR);
// Convert special bytes when it is special string.
if (strlen(v_char) >= 3 && IS_SPECIAL(v_char[0])) {
res = TERMCAP2KEY(v_char[1], v_char[2]);
} else if (strlen(v_char) > 0) {
res = utf_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;
}
/// Get the next input character.
/// Can return a special key or a multi-byte character.
/// Can return NUL when called recursively, use safe_vgetc() if that's not
@ -1769,6 +1845,11 @@ int vgetc(void)
kvi_destroy(on_key_buf);
kvi_init(on_key_buf);
// Execute KeyInputPre callbacks.
c = do_key_input_pre(c);
// Clear the next typedchars_pos
typedchars_pos = 0;
// Need to process the character before we know it's safe to do something
// else.
if (c != K_IGNORE) {

View File

@ -4152,4 +4152,83 @@ 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
" Test for v:event.typedchar
nnoremap j k
au KeyInputPre n
\ call assert_equal(v:event.typedchar, 'j')
\ | call assert_equal(v:char, 'k')
call feedkeys('j', 'tx')
au! KeyInputPre
endfunc
" vim: shiftwidth=2 sts=2 expandtab