fix(input): put modifiers back into typeahead buffer when needed

This commit is contained in:
zeertzjq 2022-01-23 05:58:32 +08:00
parent 7717f38d3f
commit 818456470c
6 changed files with 54 additions and 25 deletions

View File

@ -973,27 +973,35 @@ int ins_typebuf(char_u *str, int noremap, int offset, bool nottyped, bool silent
* Uses cmd_silent, KeyTyped and KeyNoremap to restore the flags belonging to * Uses cmd_silent, KeyTyped and KeyNoremap to restore the flags belonging to
* the char. * the char.
*/ */
void ins_char_typebuf(int c) void ins_char_typebuf(int c, int modifier)
{ {
char_u buf[MB_MAXBYTES + 1]; char_u buf[MB_MAXBYTES + 4];
if (IS_SPECIAL(c)) { int idx = 0;
if (modifier != 0) {
buf[0] = K_SPECIAL; buf[0] = K_SPECIAL;
buf[1] = (char_u)K_SECOND(c); buf[1] = KS_MODIFIER;
buf[2] = (char_u)K_THIRD(c); buf[2] = (char_u)modifier;
buf[3] = NUL; buf[3] = NUL;
idx = 3;
}
if (IS_SPECIAL(c)) {
buf[idx] = K_SPECIAL;
buf[idx + 1] = (char_u)K_SECOND(c);
buf[idx + 2] = (char_u)K_THIRD(c);
buf[idx + 3] = NUL;
} else { } else {
buf[utf_char2bytes(c, buf)] = NUL; char_u *p = buf + idx;
char_u *p = buf; int char_len = utf_char2bytes(c, p);
while (*p) { // If the character contains K_SPECIAL bytes they need escaping.
for (int i = char_len; --i >= 0; p++) {
if ((uint8_t)(*p) == K_SPECIAL) { if ((uint8_t)(*p) == K_SPECIAL) {
memmove(p + 3, p + 1, STRLEN(p + 1) + 1); memmove(p + 3, p + 1, (size_t)i);
*p++ = K_SPECIAL; *p++ = K_SPECIAL;
*p++ = KS_SPECIAL; *p++ = KS_SPECIAL;
*p++ = KE_FILLER; *p = KE_FILLER;
} else {
p++;
} }
} }
*p = NUL;
} }
(void)ins_typebuf(buf, KeyNoremap, 0, !KeyTyped, cmd_silent); (void)ins_typebuf(buf, KeyNoremap, 0, !KeyTyped, cmd_silent);
} }
@ -1433,8 +1441,9 @@ int vgetc(void)
mouse_row = old_mouse_row; mouse_row = old_mouse_row;
mouse_col = old_mouse_col; mouse_col = old_mouse_col;
} else { } else {
mod_mask = 0x0; mod_mask = 0;
last_recorded_len = 0; last_recorded_len = 0;
for (;;) { // this is done twice if there are modifiers for (;;) { // this is done twice if there are modifiers
bool did_inc = false; bool did_inc = false;
if (mod_mask) { // no mapping after modifier has been read if (mod_mask) { // no mapping after modifier has been read
@ -1560,8 +1569,8 @@ int vgetc(void)
if (!no_mapping && KeyTyped && !(State & TERM_FOCUS) if (!no_mapping && KeyTyped && !(State & TERM_FOCUS)
&& (mod_mask == MOD_MASK_ALT || mod_mask == MOD_MASK_META)) { && (mod_mask == MOD_MASK_ALT || mod_mask == MOD_MASK_META)) {
mod_mask = 0; mod_mask = 0;
ins_char_typebuf(c); ins_char_typebuf(c, 0);
ins_char_typebuf(ESC); ins_char_typebuf(ESC, 0);
continue; continue;
} }

View File

@ -127,7 +127,7 @@ typedef off_t off_T;
// When vgetc() is called, it sets mod_mask to the set of modifiers that are // When vgetc() is called, it sets mod_mask to the set of modifiers that are
// held down based on the MOD_MASK_* symbols that are read first. // held down based on the MOD_MASK_* symbols that are read first.
EXTERN int mod_mask INIT(= 0x0); // current key modifiers EXTERN int mod_mask INIT(= 0); // current key modifiers
// Cmdline_row is the row where the command line starts, just below the // Cmdline_row is the row where the command line starts, just below the

View File

@ -1215,7 +1215,7 @@ void wait_return(int redraw)
} else if (vim_strchr((char_u *)"\r\n ", c) == NULL && c != Ctrl_C) { } else if (vim_strchr((char_u *)"\r\n ", c) == NULL && c != Ctrl_C) {
// Put the character back in the typeahead buffer. Don't use the // Put the character back in the typeahead buffer. Don't use the
// stuff buffer, because lmaps wouldn't work. // stuff buffer, because lmaps wouldn't work.
ins_char_typebuf(c); ins_char_typebuf(c, mod_mask);
do_redraw = true; // need a redraw even though there is do_redraw = true; // need a redraw even though there is
// typeahead // typeahead
} }
@ -3497,7 +3497,7 @@ int do_dialog(int type, char_u *title, char_u *message, char_u *buttons, int dfl
} }
if (c == ':' && ex_cmd) { if (c == ':' && ex_cmd) {
retval = dfltbutton; retval = dfltbutton;
ins_char_typebuf(':'); ins_char_typebuf(':', 0);
break; break;
} }

View File

@ -1010,7 +1010,7 @@ static int normal_execute(VimState *state, int key)
// restart automatically. // restart automatically.
// Insert the typed character in the typeahead buffer, so that it can // Insert the typed character in the typeahead buffer, so that it can
// be mapped in Insert mode. Required for ":lmap" to work. // be mapped in Insert mode. Required for ":lmap" to work.
ins_char_typebuf(s->c); ins_char_typebuf(s->c, mod_mask);
if (restart_edit != 0) { if (restart_edit != 0) {
s->c = 'd'; s->c = 'd';
} else { } else {

View File

@ -1299,7 +1299,7 @@ static bool send_mouse_event(Terminal *term, int c)
} }
end: end:
ins_char_typebuf(c); ins_char_typebuf(c, mod_mask);
return true; return true;
} }

View File

@ -1,7 +1,7 @@
local helpers = require('test.functional.helpers')(after_each) local helpers = require('test.functional.helpers')(after_each)
local thelpers = require('test.functional.terminal.helpers') local thelpers = require('test.functional.terminal.helpers')
local clear, eq, eval = helpers.clear, helpers.eq, helpers.eval local clear, eq, eval = helpers.clear, helpers.eq, helpers.eval
local feed, nvim = helpers.feed, helpers.nvim local feed, nvim, command = helpers.feed, helpers.nvim, helpers.command
local feed_data = thelpers.feed_data local feed_data = thelpers.feed_data
describe(':terminal mouse', function() describe(':terminal mouse', function()
@ -10,9 +10,9 @@ describe(':terminal mouse', function()
before_each(function() before_each(function()
clear() clear()
nvim('set_option', 'statusline', '==========') nvim('set_option', 'statusline', '==========')
nvim('command', 'highlight StatusLine cterm=NONE') command('highlight StatusLine cterm=NONE')
nvim('command', 'highlight StatusLineNC cterm=NONE') command('highlight StatusLineNC cterm=NONE')
nvim('command', 'highlight VertSplit cterm=NONE') command('highlight VertSplit cterm=NONE')
screen = thelpers.screen_setup() screen = thelpers.screen_setup()
local lines = {} local lines = {}
for i = 1, 30 do for i = 1, 30 do
@ -38,6 +38,26 @@ describe(':terminal mouse', function()
eq('nt', eval('mode(1)')) eq('nt', eval('mode(1)'))
end) end)
it('will exit focus and trigger Normal mode mapping on mouse click', function()
command('let g:got_leftmouse = 0')
command('nnoremap <LeftMouse> <Cmd>let g:got_leftmouse = 1<CR>')
eq('t', eval('mode(1)'))
eq(0, eval('g:got_leftmouse'))
feed('<LeftMouse>')
eq('nt', eval('mode(1)'))
eq(1, eval('g:got_leftmouse'))
end)
it('will exit focus and trigger Normal mode mapping on mouse click with modifier', function()
command('let g:got_ctrl_leftmouse = 0')
command('nnoremap <C-LeftMouse> <Cmd>let g:got_ctrl_leftmouse = 1<CR>')
eq('t', eval('mode(1)'))
eq(0, eval('g:got_ctrl_leftmouse'))
feed('<C-LeftMouse>')
eq('nt', eval('mode(1)'))
eq(1, eval('g:got_ctrl_leftmouse'))
end)
it('will exit focus on <C-\\> + mouse-scroll', function() it('will exit focus on <C-\\> + mouse-scroll', function()
eq('t', eval('mode(1)')) eq('t', eval('mode(1)'))
feed('<C-\\>') feed('<C-\\>')
@ -180,7 +200,7 @@ describe(':terminal mouse', function()
it('will forward mouse clicks to the program with the correct even if set nu', function() it('will forward mouse clicks to the program with the correct even if set nu', function()
if helpers.pending_win32(pending) then return end if helpers.pending_win32(pending) then return end
nvim('command', 'set number') command('set number')
-- When the display area such as a number is clicked, it returns to the -- When the display area such as a number is clicked, it returns to the
-- normal mode. -- normal mode.
feed('<LeftMouse><3,0>') feed('<LeftMouse><3,0>')