aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorzeertzjq <zeertzjq@outlook.com>2022-05-03 06:11:22 +0800
committerzeertzjq <zeertzjq@outlook.com>2022-05-03 06:21:50 +0800
commit27149e0071c3fa38c81526f63a997bedfd6e2be8 (patch)
tree8d819a934bd5c11c7947ff06bea23a609723d078
parentddf7bb24f98b468d2bc6c16c6f300570fc6530f5 (diff)
downloadrneovim-27149e0071c3fa38c81526f63a997bedfd6e2be8.tar.gz
rneovim-27149e0071c3fa38c81526f63a997bedfd6e2be8.tar.bz2
rneovim-27149e0071c3fa38c81526f63a997bedfd6e2be8.zip
vim-patch:8.2.4858: K_SPECIAL may be escaped twice
Problem: K_SPECIAL may be escaped twice. Solution: Avoid double escaping. (closes vim/vim#10340) https://github.com/vim/vim/commit/db08887f24d20be11d184ce321bc0890613e42bd
-rw-r--r--src/nvim/eval.c9
-rw-r--r--src/nvim/keymap.c17
-rw-r--r--src/nvim/os/input.c2
-rw-r--r--src/nvim/testdir/test_eval_stuff.vim22
-rw-r--r--src/nvim/testdir/test_feedkeys.vim11
-rw-r--r--src/nvim/testdir/test_functions.vim4
-rw-r--r--src/nvim/testdir/test_mapping.vim15
-rw-r--r--src/nvim/viml/parser/expressions.c9
8 files changed, 69 insertions, 20 deletions
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index da869df5cc..3023b1e774 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -4869,11 +4869,10 @@ static int get_string_tv(char_u **arg, typval_T *rettv, int evaluate)
if (*p == '\\' && p[1] != NUL) {
p++;
// A "\<x>" form occupies at least 4 characters, and produces up
- // to 21 characters (3 * 6 for the char and 3 for a modifier):
- // reserve space for 18 extra.
- // Each byte in the char could be encoded as K_SPECIAL K_EXTRA x.
+ // to 9 characters (6 for the char and 3 for a modifier):
+ // reserve space for 5 extra.
if (*p == '<') {
- extra += 18;
+ extra += 5;
}
}
}
@@ -4971,7 +4970,7 @@ static int get_string_tv(char_u **arg, typval_T *rettv, int evaluate)
if (p[1] != '*') {
flags |= FSK_SIMPLIFY;
}
- extra = trans_special((const char_u **)&p, STRLEN(p), name, flags, NULL);
+ extra = trans_special((const char_u **)&p, STRLEN(p), name, flags, false, NULL);
if (extra != 0) {
name += extra;
if (name >= rettv->vval.v_string + len) {
diff --git a/src/nvim/keymap.c b/src/nvim/keymap.c
index db51237771..7541837782 100644
--- a/src/nvim/keymap.c
+++ b/src/nvim/keymap.c
@@ -569,11 +569,12 @@ char_u *get_special_key_name(int c, int modifiers)
/// @param[out] dst Location where translation result will be kept. It must
// be at least 19 bytes per "<x>" form.
/// @param[in] flags FSK_ values
+/// @param[in] escape_ks escape K_SPECIAL bytes in the character
/// @param[out] did_simplify found <C-H>, etc.
///
/// @return Number of characters added to dst, zero for no match.
unsigned int trans_special(const char_u **const srcp, const size_t src_len, char_u *const dst,
- const int flags, bool *const did_simplify)
+ const int flags, const bool escape_ks, bool *const did_simplify)
FUNC_ATTR_NONNULL_ARG(1, 3) FUNC_ATTR_WARN_UNUSED_RESULT
{
int modifiers = 0;
@@ -582,15 +583,15 @@ unsigned int trans_special(const char_u **const srcp, const size_t src_len, char
return 0;
}
- return special_to_buf(key, modifiers, flags & FSK_KEYCODE, dst);
+ return special_to_buf(key, modifiers, escape_ks, dst);
}
/// Put the character sequence for "key" with "modifiers" into "dst" and return
/// the resulting length.
-/// When "keycode" is true prefer key code, e.g. K_DEL instead of DEL.
+/// When "escape_ks" is true escape K_SPECIAL bytes in the character.
/// The sequence is not NUL terminated.
/// This is how characters in a string are encoded.
-unsigned int special_to_buf(int key, int modifiers, bool keycode, char_u *dst)
+unsigned int special_to_buf(int key, int modifiers, bool escape_ks, char_u *dst)
{
unsigned int dlen = 0;
@@ -605,12 +606,12 @@ unsigned int special_to_buf(int key, int modifiers, bool keycode, char_u *dst)
dst[dlen++] = K_SPECIAL;
dst[dlen++] = (char_u)KEY2TERMCAP0(key);
dst[dlen++] = KEY2TERMCAP1(key);
- } else if (!keycode) {
- dlen += (unsigned int)utf_char2bytes(key, dst + dlen);
- } else {
+ } else if (escape_ks) {
char_u *after = add_char2buf(key, dst + dlen);
assert(after >= dst && (uintmax_t)(after - dst) <= UINT_MAX);
dlen = (unsigned int)(after - dst);
+ } else {
+ dlen += (unsigned int)utf_char2bytes(key, dst + dlen);
}
return dlen;
@@ -943,7 +944,7 @@ char_u *replace_termcodes(const char_u *const from, const size_t from_len, char_
slen = trans_special(&src, (size_t)(end - src) + 1, result + dlen,
FSK_KEYCODE | ((flags & REPTERM_NO_SIMPLIFY) ? 0 : FSK_SIMPLIFY),
- did_simplify);
+ true, did_simplify);
if (slen) {
dlen += slen;
continue;
diff --git a/src/nvim/os/input.c b/src/nvim/os/input.c
index 9ee2f57b3d..437baf0e08 100644
--- a/src/nvim/os/input.c
+++ b/src/nvim/os/input.c
@@ -247,7 +247,7 @@ size_t input_enqueue(String keys)
uint8_t buf[19] = { 0 };
// Do not simplify the keys here. Simplification will be done later.
unsigned int new_size
- = trans_special((const uint8_t **)&ptr, (size_t)(end - ptr), buf, FSK_KEYCODE, NULL);
+ = trans_special((const uint8_t **)&ptr, (size_t)(end - ptr), buf, FSK_KEYCODE, true, NULL);
if (new_size) {
new_size = handle_mouse_event(&ptr, buf, new_size);
diff --git a/src/nvim/testdir/test_eval_stuff.vim b/src/nvim/testdir/test_eval_stuff.vim
index 95eccde35c..811c6c946d 100644
--- a/src/nvim/testdir/test_eval_stuff.vim
+++ b/src/nvim/testdir/test_eval_stuff.vim
@@ -320,4 +320,26 @@ func Test_curly_assignment()
unlet g:gvar
endfunc
+" K_SPECIAL in the modified character used be escaped, which causes
+" double-escaping with feedkeys() or as the return value of an <expr> mapping,
+" and doesn't match what getchar() returns,
+func Test_modified_char_no_escape_special()
+ nnoremap <M-…> <Cmd>let g:got_m_ellipsis += 1<CR>
+ call feedkeys("\<M-…>", 't')
+ call assert_equal("\<M-…>", getchar())
+ let g:got_m_ellipsis = 0
+ call feedkeys("\<M-…>", 'xt')
+ call assert_equal(1, g:got_m_ellipsis)
+ func Func()
+ return "\<M-…>"
+ endfunc
+ nmap <expr> <F2> Func()
+ call feedkeys("\<F2>", 'xt')
+ call assert_equal(2, g:got_m_ellipsis)
+ delfunc Func
+ nunmap <F2>
+ unlet g:got_m_ellipsis
+ nunmap <M-…>
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_feedkeys.vim b/src/nvim/testdir/test_feedkeys.vim
index f343b0174c..fb64711863 100644
--- a/src/nvim/testdir/test_feedkeys.vim
+++ b/src/nvim/testdir/test_feedkeys.vim
@@ -23,4 +23,15 @@ func Test_feedkeys_with_abbreviation()
iunabbrev trigger
endfunc
+func Test_feedkeys_escape_special()
+ nnoremap … <Cmd>let g:got_ellipsis += 1<CR>
+ call feedkeys('…', 't')
+ call assert_equal('…', getcharstr())
+ let g:got_ellipsis = 0
+ call feedkeys('…', 'xt')
+ call assert_equal(1, g:got_ellipsis)
+ unlet g:got_ellipsis
+ nunmap …
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim
index f8be250f73..87606f17b8 100644
--- a/src/nvim/testdir/test_functions.vim
+++ b/src/nvim/testdir/test_functions.vim
@@ -1751,8 +1751,8 @@ func Test_nr2char()
call assert_equal('a', nr2char(97, 1))
call assert_equal('a', nr2char(97, 0))
- call assert_equal("\x80\xfc\b\xf4\x80\xfeX\x80\xfeX\x80\xfeX", eval('"\<M-' .. nr2char(0x100000) .. '>"'))
- call assert_equal("\x80\xfc\b\xfd\x80\xfeX\x80\xfeX\x80\xfeX\x80\xfeX\x80\xfeX", eval('"\<M-' .. nr2char(0x40000000) .. '>"'))
+ call assert_equal("\x80\xfc\b" .. nr2char(0x100000), eval('"\<M-' .. nr2char(0x100000) .. '>"'))
+ call assert_equal("\x80\xfc\b" .. nr2char(0x40000000), eval('"\<M-' .. nr2char(0x40000000) .. '>"'))
endfunc
" Test for getcurpos() and setpos()
diff --git a/src/nvim/testdir/test_mapping.vim b/src/nvim/testdir/test_mapping.vim
index 1da3b71a32..752b1700d0 100644
--- a/src/nvim/testdir/test_mapping.vim
+++ b/src/nvim/testdir/test_mapping.vim
@@ -1025,4 +1025,19 @@ func Test_unmap_simplifiable()
unmap <C-I>
endfunc
+func Test_expr_map_escape_special()
+ nnoremap … <Cmd>let g:got_ellipsis += 1<CR>
+ func Func()
+ return '…'
+ endfunc
+ nmap <expr> <F2> Func()
+ let g:got_ellipsis = 0
+ call feedkeys("\<F2>", 'xt')
+ call assert_equal(1, g:got_ellipsis)
+ delfunc Func
+ nunmap <F2>
+ unlet g:got_ellipsis
+ nunmap …
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/viml/parser/expressions.c b/src/nvim/viml/parser/expressions.c
index 29d686193d..01fc282891 100644
--- a/src/nvim/viml/parser/expressions.c
+++ b/src/nvim/viml/parser/expressions.c
@@ -1651,10 +1651,11 @@ static void parse_quoted_string(ParserState *const pstate, ExprASTNode *const no
}
switch (*p) {
// A "\<x>" form occupies at least 4 characters, and produces up to
- // 6 characters: reserve space for 2 extra, but do not compute actual
- // length just now, it would be costy.
+ // to 9 characters (6 for the char and 3 for a modifier):
+ // reserve space for 5 extra, but do not compute actual length
+ // just now, it would be costly.
case '<':
- size += 2;
+ size += 5;
break;
// Hexadecimal, always single byte, but at least three bytes each.
case 'x':
@@ -1822,7 +1823,7 @@ static void parse_quoted_string(ParserState *const pstate, ExprASTNode *const no
flags |= FSK_SIMPLIFY;
}
const size_t special_len = trans_special((const char_u **)&p, (size_t)(e - p),
- (char_u *)v_p, flags, NULL);
+ (char_u *)v_p, flags, false, NULL);
if (special_len != 0) {
v_p += special_len;
} else {