aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorzeertzjq <zeertzjq@outlook.com>2025-02-02 17:32:51 +0800
committerGitHub <noreply@github.com>2025-02-02 09:32:51 +0000
commit4bdabf9b1ae52134f50a0b75dc2c73a40c0c252f (patch)
tree1d57e428e413301df6a6d4c4cdbf7ff7853c8e7c
parent718e9ce018572f609a7639865af55476a19847aa (diff)
downloadrneovim-4bdabf9b1ae52134f50a0b75dc2c73a40c0c252f.tar.gz
rneovim-4bdabf9b1ae52134f50a0b75dc2c73a40c0c252f.tar.bz2
rneovim-4bdabf9b1ae52134f50a0b75dc2c73a40c0c252f.zip
vim-patch:9.1.1068: getchar() can't distinguish between C-I and Tab (#32295)
Problem: getchar() can't distinguish between C-I and Tab. Solution: Add {opts} to pass extra flags to getchar() and getcharstr(), with "number" and "simplify" keys. related: vim/vim#10603 closes: vim/vim#16554 https://github.com/vim/vim/commit/e0a2ab397fd13a71efec85b017d5d4d62baf7f63 Cherry-pick tv_dict_has_key() from patch 8.2.4683.
-rw-r--r--runtime/doc/builtin.txt49
-rw-r--r--runtime/lua/vim/_meta/vimfn.lua53
-rw-r--r--src/nvim/eval.lua51
-rw-r--r--src/nvim/eval/typval.c12
-rw-r--r--src/nvim/getchar.c63
-rw-r--r--test/functional/terminal/buffer_spec.lua92
-rw-r--r--test/old/testdir/test_functions.vim71
7 files changed, 233 insertions, 158 deletions
diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt
index d4a6bbf9d8..3d9010aa2c 100644
--- a/runtime/doc/builtin.txt
+++ b/runtime/doc/builtin.txt
@@ -3079,14 +3079,16 @@ getchangelist([{buf}]) *getchangelist()*
Return: ~
(`table[]`)
-getchar([{expr}]) *getchar()*
+getchar([{expr} [, {opts}]]) *getchar()*
Get a single character from the user or input stream.
- If {expr} is omitted, wait until a character is available.
+ If {expr} is omitted or is -1, wait until a character is
+ available.
If {expr} is 0, only get a character when one is available.
Return zero otherwise.
If {expr} is 1, only check if a character is available, it is
not consumed. Return zero if no character available.
- If you prefer always getting a string use |getcharstr()|.
+ If you prefer always getting a string use |getcharstr()|, or
+ specify |FALSE| as "number" in {opts}.
Without {expr} and when {expr} is 0 a whole character or
special key is returned. If it is a single character, the
@@ -3096,7 +3098,8 @@ getchar([{expr}]) *getchar()*
starting with 0x80 (decimal: 128). This is the same value as
the String "\<Key>", e.g., "\<Left>". The returned value is
also a String when a modifier (shift, control, alt) was used
- that is not included in the character.
+ that is not included in the character. |keytrans()| can also
+ be used to convert a returned String into a readable form.
When {expr} is 0 and Esc is typed, there will be a short delay
while Vim waits to see if this is the start of an escape
@@ -3108,6 +3111,24 @@ getchar([{expr}]) *getchar()*
Use getcharmod() to obtain any additional modifiers.
+ The optional argument {opts} is a Dict and supports the
+ following items:
+
+ number If |TRUE|, return a Number when getting
+ a single character.
+ If |FALSE|, the return value is always
+ converted to a String, and an empty
+ String (instead of 0) is returned when
+ no character is available.
+ (default: |TRUE|)
+
+ simplify If |TRUE|, include modifiers in the
+ character if possible. E.g., return
+ the same value for CTRL-I and <Tab>.
+ If |FALSE|, don't include modifiers in
+ the character.
+ (default: |TRUE|)
+
When the user clicks a mouse button, the mouse event will be
returned. The position can then be found in |v:mouse_col|,
|v:mouse_lnum|, |v:mouse_winid| and |v:mouse_win|.
@@ -3145,10 +3166,11 @@ getchar([{expr}]) *getchar()*
<
Parameters: ~
- • {expr} (`0|1?`)
+ • {expr} (`-1|0|1?`)
+ • {opts} (`table?`)
Return: ~
- (`integer`)
+ (`integer|string`)
getcharmod() *getcharmod()*
The result is a Number which is the state of the modifiers for
@@ -3213,19 +3235,12 @@ getcharsearch() *getcharsearch()*
(`table`)
getcharstr([{expr}]) *getcharstr()*
- Get a single character from the user or input stream as a
- string.
- If {expr} is omitted, wait until a character is available.
- If {expr} is 0 or false, only get a character when one is
- available. Return an empty string otherwise.
- If {expr} is 1 or true, only check if a character is
- available, it is not consumed. Return an empty string
- if no character is available.
- Otherwise this works like |getchar()|, except that a number
- result is converted to a string.
+ The same as |getchar()|, except that this always returns a
+ String, and "number" isn't allowed in {opts}.
Parameters: ~
- • {expr} (`0|1?`)
+ • {expr} (`-1|0|1?`)
+ • {opts} (`table?`)
Return: ~
(`string`)
diff --git a/runtime/lua/vim/_meta/vimfn.lua b/runtime/lua/vim/_meta/vimfn.lua
index 4b5b276023..c0be6b089c 100644
--- a/runtime/lua/vim/_meta/vimfn.lua
+++ b/runtime/lua/vim/_meta/vimfn.lua
@@ -2748,12 +2748,14 @@ function vim.fn.getcellwidths() end
function vim.fn.getchangelist(buf) end
--- Get a single character from the user or input stream.
---- If {expr} is omitted, wait until a character is available.
+--- If {expr} is omitted or is -1, wait until a character is
+--- available.
--- If {expr} is 0, only get a character when one is available.
--- Return zero otherwise.
--- If {expr} is 1, only check if a character is available, it is
--- not consumed. Return zero if no character available.
---- If you prefer always getting a string use |getcharstr()|.
+--- If you prefer always getting a string use |getcharstr()|, or
+--- specify |FALSE| as "number" in {opts}.
---
--- Without {expr} and when {expr} is 0 a whole character or
--- special key is returned. If it is a single character, the
@@ -2763,7 +2765,8 @@ function vim.fn.getchangelist(buf) end
--- starting with 0x80 (decimal: 128). This is the same value as
--- the String "\<Key>", e.g., "\<Left>". The returned value is
--- also a String when a modifier (shift, control, alt) was used
---- that is not included in the character.
+--- that is not included in the character. |keytrans()| can also
+--- be used to convert a returned String into a readable form.
---
--- When {expr} is 0 and Esc is typed, there will be a short delay
--- while Vim waits to see if this is the start of an escape
@@ -2775,6 +2778,24 @@ function vim.fn.getchangelist(buf) end
---
--- Use getcharmod() to obtain any additional modifiers.
---
+--- The optional argument {opts} is a Dict and supports the
+--- following items:
+---
+--- number If |TRUE|, return a Number when getting
+--- a single character.
+--- If |FALSE|, the return value is always
+--- converted to a String, and an empty
+--- String (instead of 0) is returned when
+--- no character is available.
+--- (default: |TRUE|)
+---
+--- simplify If |TRUE|, include modifiers in the
+--- character if possible. E.g., return
+--- the same value for CTRL-I and <Tab>.
+--- If |FALSE|, don't include modifiers in
+--- the character.
+--- (default: |TRUE|)
+---
--- When the user clicks a mouse button, the mouse event will be
--- returned. The position can then be found in |v:mouse_col|,
--- |v:mouse_lnum|, |v:mouse_winid| and |v:mouse_win|.
@@ -2811,9 +2832,10 @@ function vim.fn.getchangelist(buf) end
--- endfunction
--- <
---
---- @param expr? 0|1
---- @return integer
-function vim.fn.getchar(expr) end
+--- @param expr? -1|0|1
+--- @param opts? table
+--- @return integer|string
+function vim.fn.getchar(expr, opts) end
--- The result is a Number which is the state of the modifiers for
--- the last obtained character with getchar() or in another way.
@@ -2872,20 +2894,13 @@ function vim.fn.getcharpos(expr) end
--- @return table
function vim.fn.getcharsearch() end
---- Get a single character from the user or input stream as a
---- string.
---- If {expr} is omitted, wait until a character is available.
---- If {expr} is 0 or false, only get a character when one is
---- available. Return an empty string otherwise.
---- If {expr} is 1 or true, only check if a character is
---- available, it is not consumed. Return an empty string
---- if no character is available.
---- Otherwise this works like |getchar()|, except that a number
---- result is converted to a string.
----
---- @param expr? 0|1
+--- The same as |getchar()|, except that this always returns a
+--- String, and "number" isn't allowed in {opts}.
+---
+--- @param expr? -1|0|1
+--- @param opts? table
--- @return string
-function vim.fn.getcharstr(expr) end
+function vim.fn.getcharstr(expr, opts) end
--- Return completion pattern of the current command-line.
--- Only works when the command line is being edited, thus
diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua
index ecb213c811..82e3992287 100644
--- a/src/nvim/eval.lua
+++ b/src/nvim/eval.lua
@@ -3471,15 +3471,17 @@ M.funcs = {
signature = 'getchangelist([{buf}])',
},
getchar = {
- args = { 0, 1 },
+ args = { 0, 2 },
desc = [=[
Get a single character from the user or input stream.
- If {expr} is omitted, wait until a character is available.
+ If {expr} is omitted or is -1, wait until a character is
+ available.
If {expr} is 0, only get a character when one is available.
Return zero otherwise.
If {expr} is 1, only check if a character is available, it is
not consumed. Return zero if no character available.
- If you prefer always getting a string use |getcharstr()|.
+ If you prefer always getting a string use |getcharstr()|, or
+ specify |FALSE| as "number" in {opts}.
Without {expr} and when {expr} is 0 a whole character or
special key is returned. If it is a single character, the
@@ -3489,7 +3491,8 @@ M.funcs = {
starting with 0x80 (decimal: 128). This is the same value as
the String "\<Key>", e.g., "\<Left>". The returned value is
also a String when a modifier (shift, control, alt) was used
- that is not included in the character.
+ that is not included in the character. |keytrans()| can also
+ be used to convert a returned String into a readable form.
When {expr} is 0 and Esc is typed, there will be a short delay
while Vim waits to see if this is the start of an escape
@@ -3501,6 +3504,24 @@ M.funcs = {
Use getcharmod() to obtain any additional modifiers.
+ The optional argument {opts} is a Dict and supports the
+ following items:
+
+ number If |TRUE|, return a Number when getting
+ a single character.
+ If |FALSE|, the return value is always
+ converted to a String, and an empty
+ String (instead of 0) is returned when
+ no character is available.
+ (default: |TRUE|)
+
+ simplify If |TRUE|, include modifiers in the
+ character if possible. E.g., return
+ the same value for CTRL-I and <Tab>.
+ If |FALSE|, don't include modifiers in
+ the character.
+ (default: |TRUE|)
+
When the user clicks a mouse button, the mouse event will be
returned. The position can then be found in |v:mouse_col|,
|v:mouse_lnum|, |v:mouse_winid| and |v:mouse_win|.
@@ -3538,9 +3559,9 @@ M.funcs = {
<
]=],
name = 'getchar',
- params = { { 'expr', '0|1' } },
- returns = 'integer',
- signature = 'getchar([{expr}])',
+ params = { { 'expr', '-1|0|1' }, { 'opts', 'table' } },
+ returns = 'integer|string',
+ signature = 'getchar([{expr} [, {opts}]])',
},
getcharmod = {
desc = [=[
@@ -3613,21 +3634,13 @@ M.funcs = {
signature = 'getcharsearch()',
},
getcharstr = {
- args = { 0, 1 },
+ args = { 0, 2 },
desc = [=[
- Get a single character from the user or input stream as a
- string.
- If {expr} is omitted, wait until a character is available.
- If {expr} is 0 or false, only get a character when one is
- available. Return an empty string otherwise.
- If {expr} is 1 or true, only check if a character is
- available, it is not consumed. Return an empty string
- if no character is available.
- Otherwise this works like |getchar()|, except that a number
- result is converted to a string.
+ The same as |getchar()|, except that this always returns a
+ String, and "number" isn't allowed in {opts}.
]=],
name = 'getcharstr',
- params = { { 'expr', '0|1' } },
+ params = { { 'expr', '-1|0|1' }, { 'opts', 'table' } },
returns = 'string',
signature = 'getcharstr([{expr}])',
},
diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c
index ed1031577c..48b2e82c0a 100644
--- a/src/nvim/eval/typval.c
+++ b/src/nvim/eval/typval.c
@@ -2234,6 +2234,18 @@ dictitem_T *tv_dict_find(const dict_T *const d, const char *const key, const ptr
return TV_DICT_HI2DI(hi);
}
+/// Check if a key is present in a dictionary.
+///
+/// @param[in] d Dictionary to check.
+/// @param[in] key Dictionary key.
+///
+/// @return whether the key is present in the dictionary.
+bool tv_dict_has_key(const dict_T *const d, const char *const key)
+ FUNC_ATTR_NONNULL_ARG(2) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ return tv_dict_find(d, key, -1) != NULL;
+}
+
/// Get a typval item from a dictionary and copy it into "rettv".
///
/// @param[in] d Dictionary to check.
diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c
index 6ec84ff543..c9d2165fb0 100644
--- a/src/nvim/getchar.c
+++ b/src/nvim/getchar.c
@@ -1866,22 +1866,47 @@ bool char_avail(void)
return retval != NUL;
}
+static int no_reduce_keys = 0; ///< Do not apply modifiers to the key.
+
/// "getchar()" and "getcharstr()" functions
-static void getchar_common(typval_T *argvars, typval_T *rettv)
+static void getchar_common(typval_T *argvars, typval_T *rettv, bool allow_number)
FUNC_ATTR_NONNULL_ALL
{
varnumber_T n;
bool error = false;
+ bool simplify = true;
+
+ if (argvars[0].v_type != VAR_UNKNOWN
+ && tv_check_for_opt_dict_arg(argvars, 1) == FAIL) {
+ return;
+ }
+
+ if (argvars[0].v_type != VAR_UNKNOWN && argvars[1].v_type == VAR_DICT) {
+ dict_T *d = argvars[1].vval.v_dict;
+
+ if (allow_number) {
+ allow_number = tv_dict_get_bool(d, "number", true);
+ } else if (tv_dict_has_key(d, "number")) {
+ semsg(_(e_invarg2), "number");
+ error = true;
+ }
+
+ simplify = tv_dict_get_bool(d, "simplify", true);
+ }
no_mapping++;
allow_keys++;
- while (true) {
+ if (!simplify) {
+ no_reduce_keys++;
+ }
+ while (!error) {
if (msg_col > 0) {
// Position the cursor. Needed after a message that ends in a space.
ui_cursor_goto(msg_row, msg_col);
}
- if (argvars[0].v_type == VAR_UNKNOWN) {
+ if (argvars[0].v_type == VAR_UNKNOWN
+ || (argvars[0].v_type == VAR_NUMBER && argvars[0].vval.v_number == -1)) {
// getchar(): blocking wait.
// TODO(bfredl): deduplicate shared logic with state_enter ?
if (!char_avail()) {
@@ -1916,14 +1941,16 @@ static void getchar_common(typval_T *argvars, typval_T *rettv)
}
no_mapping--;
allow_keys--;
+ if (!simplify) {
+ no_reduce_keys--;
+ }
set_vim_var_nr(VV_MOUSE_WIN, 0);
set_vim_var_nr(VV_MOUSE_WINID, 0);
set_vim_var_nr(VV_MOUSE_LNUM, 0);
set_vim_var_nr(VV_MOUSE_COL, 0);
- rettv->vval.v_number = n;
- if (n != 0 && (IS_SPECIAL(n) || mod_mask != 0)) {
+ if (n != 0 && (!allow_number || IS_SPECIAL(n) || mod_mask != 0)) {
char temp[10]; // modifier: 3, mbyte-char: 6, NUL: 1
int i = 0;
@@ -1970,35 +1997,23 @@ static void getchar_common(typval_T *argvars, typval_T *rettv)
set_vim_var_nr(VV_MOUSE_COL, col + 1);
}
}
+ } else if (!allow_number) {
+ rettv->v_type = VAR_STRING;
+ } else {
+ rettv->vval.v_number = n;
}
}
/// "getchar()" function
void f_getchar(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- getchar_common(argvars, rettv);
+ getchar_common(argvars, rettv, true);
}
/// "getcharstr()" function
void f_getcharstr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- getchar_common(argvars, rettv);
-
- if (rettv->v_type != VAR_NUMBER) {
- return;
- }
-
- char temp[7]; // mbyte-char: 6, NUL: 1
- const varnumber_T n = rettv->vval.v_number;
- int i = 0;
-
- if (n != 0) {
- i += utf_char2bytes((int)n, temp);
- }
- assert(i < 7);
- temp[i] = NUL;
- rettv->v_type = VAR_STRING;
- rettv->vval.v_string = xmemdupz(temp, (size_t)i);
+ getchar_common(argvars, rettv, false);
}
/// "getcharmod()" function
@@ -2058,7 +2073,7 @@ static int check_simplify_modifier(int max_offset)
{
// We want full modifiers in Terminal mode so that the key can be correctly
// encoded
- if (State & MODE_TERMINAL) {
+ if ((State & MODE_TERMINAL) || no_reduce_keys > 0) {
return 0;
}
diff --git a/test/functional/terminal/buffer_spec.lua b/test/functional/terminal/buffer_spec.lua
index d36dc60a93..4635259e33 100644
--- a/test/functional/terminal/buffer_spec.lua
+++ b/test/functional/terminal/buffer_spec.lua
@@ -557,7 +557,7 @@ describe('terminal input', function()
'--cmd',
'set notermguicolors',
'-c',
- 'while 1 | redraw | echo keytrans(getcharstr()) | endwhile',
+ 'while 1 | redraw | echo keytrans(getcharstr(-1, #{simplify: 0})) | endwhile',
})
screen:expect([[
^ |
@@ -566,7 +566,10 @@ describe('terminal input', function()
|
{3:-- TERMINAL --} |
]])
- for _, key in ipairs({
+ local keys = {
+ '<Tab>',
+ '<CR>',
+ '<Esc>',
'<M-Tab>',
'<M-CR>',
'<M-Esc>',
@@ -632,7 +635,14 @@ describe('terminal input', function()
'<S-ScrollWheelRight>',
'<ScrollWheelLeft>',
'<ScrollWheelRight>',
- }) do
+ }
+ -- FIXME: The escape sequence to enable kitty keyboard mode doesn't work on Windows
+ if not is_os('win') then
+ table.insert(keys, '<C-I>')
+ table.insert(keys, '<C-M>')
+ table.insert(keys, '<C-[>')
+ end
+ for _, key in ipairs(keys) do
feed(key)
screen:expect(([[
|
@@ -643,82 +653,6 @@ describe('terminal input', function()
]]):format(key:gsub('<%d+,%d+>$', '')))
end
end)
-
- -- TODO(bfredl): getcharstr() erases the distinction between <C-I> and <Tab>.
- -- If it was enhanced or replaced this could get folded into the test above.
- it('can send TAB/C-I and ESC/C-[ separately', function()
- if
- skip(
- is_os('win'),
- "The escape sequence to enable kitty keyboard mode doesn't work on Windows"
- )
- then
- return
- end
- clear()
- local screen = tt.setup_child_nvim({
- '-u',
- 'NONE',
- '-i',
- 'NONE',
- '--cmd',
- 'colorscheme vim',
- '--cmd',
- 'set notermguicolors',
- '--cmd',
- 'noremap <Tab> <cmd>echo "Tab!"<cr>',
- '--cmd',
- 'noremap <C-i> <cmd>echo "Ctrl-I!"<cr>',
- '--cmd',
- 'noremap <Esc> <cmd>echo "Esc!"<cr>',
- '--cmd',
- 'noremap <C-[> <cmd>echo "Ctrl-[!"<cr>',
- })
-
- screen:expect([[
- ^ |
- {4:~ }|*3
- {5:[No Name] 0,0-1 All}|
- |
- {3:-- TERMINAL --} |
- ]])
-
- feed('<tab>')
- screen:expect([[
- ^ |
- {4:~ }|*3
- {5:[No Name] 0,0-1 All}|
- Tab! |
- {3:-- TERMINAL --} |
- ]])
-
- feed('<c-i>')
- screen:expect([[
- ^ |
- {4:~ }|*3
- {5:[No Name] 0,0-1 All}|
- Ctrl-I! |
- {3:-- TERMINAL --} |
- ]])
-
- feed('<Esc>')
- screen:expect([[
- ^ |
- {4:~ }|*3
- {5:[No Name] 0,0-1 All}|
- Esc! |
- {3:-- TERMINAL --} |
- ]])
-
- feed('<c-[>')
- screen:expect([[
- ^ |
- {4:~ }|*3
- {5:[No Name] 0,0-1 All}|
- Ctrl-[! |
- {3:-- TERMINAL --} |
- ]])
- end)
end)
if is_os('win') then
diff --git a/test/old/testdir/test_functions.vim b/test/old/testdir/test_functions.vim
index 01e6001dcc..f57743900a 100644
--- a/test/old/testdir/test_functions.vim
+++ b/test/old/testdir/test_functions.vim
@@ -2390,6 +2390,77 @@ func Test_getchar()
call assert_equal("\<M-F2>", getchar(0))
call assert_equal(0, getchar(0))
+ call feedkeys("\<Tab>", '')
+ call assert_equal(char2nr("\<Tab>"), getchar())
+ call feedkeys("\<Tab>", '')
+ call assert_equal(char2nr("\<Tab>"), getchar(-1))
+ call feedkeys("\<Tab>", '')
+ call assert_equal(char2nr("\<Tab>"), getchar(-1, {}))
+ call feedkeys("\<Tab>", '')
+ call assert_equal(char2nr("\<Tab>"), getchar(-1, #{number: v:true}))
+ call assert_equal(0, getchar(0))
+ call assert_equal(0, getchar(1))
+ call assert_equal(0, getchar(0, #{number: v:true}))
+ call assert_equal(0, getchar(1, #{number: v:true}))
+
+ call feedkeys("\<Tab>", '')
+ call assert_equal("\<Tab>", getcharstr())
+ call feedkeys("\<Tab>", '')
+ call assert_equal("\<Tab>", getcharstr(-1))
+ call feedkeys("\<Tab>", '')
+ call assert_equal("\<Tab>", getcharstr(-1, {}))
+ call feedkeys("\<Tab>", '')
+ call assert_equal("\<Tab>", getchar(-1, #{number: v:false}))
+ call assert_equal('', getcharstr(0))
+ call assert_equal('', getcharstr(1))
+ call assert_equal('', getchar(0, #{number: v:false}))
+ call assert_equal('', getchar(1, #{number: v:false}))
+
+ " Nvim: <M-x> is never simplified
+ " for key in ["C-I", "C-X", "M-x"]
+ for key in ["C-I", "C-X"]
+ let lines =<< eval trim END
+ call feedkeys("\<*{key}>", '')
+ call assert_equal(char2nr("\<{key}>"), getchar())
+ call feedkeys("\<*{key}>", '')
+ call assert_equal(char2nr("\<{key}>"), getchar(-1))
+ call feedkeys("\<*{key}>", '')
+ call assert_equal(char2nr("\<{key}>"), getchar(-1, {{}}))
+ call feedkeys("\<*{key}>", '')
+ call assert_equal(char2nr("\<{key}>"), getchar(-1, {{'number': 1}}))
+ call feedkeys("\<*{key}>", '')
+ call assert_equal(char2nr("\<{key}>"), getchar(-1, {{'simplify': 1}}))
+ call feedkeys("\<*{key}>", '')
+ call assert_equal("\<*{key}>", getchar(-1, {{'simplify': v:false}}))
+ call assert_equal(0, getchar(0))
+ call assert_equal(0, getchar(1))
+ END
+ call CheckLegacyAndVim9Success(lines)
+
+ let lines =<< eval trim END
+ call feedkeys("\<*{key}>", '')
+ call assert_equal("\<{key}>", getcharstr())
+ call feedkeys("\<*{key}>", '')
+ call assert_equal("\<{key}>", getcharstr(-1))
+ call feedkeys("\<*{key}>", '')
+ call assert_equal("\<{key}>", getcharstr(-1, {{}}))
+ call feedkeys("\<*{key}>", '')
+ call assert_equal("\<{key}>", getchar(-1, {{'number': 0}}))
+ call feedkeys("\<*{key}>", '')
+ call assert_equal("\<{key}>", getcharstr(-1, {{'simplify': 1}}))
+ call feedkeys("\<*{key}>", '')
+ call assert_equal("\<*{key}>", getcharstr(-1, {{'simplify': v:false}}))
+ call assert_equal('', getcharstr(0))
+ call assert_equal('', getcharstr(1))
+ END
+ call CheckLegacyAndVim9Success(lines)
+ endfor
+
+ call assert_fails('call getchar(1, 1)', 'E1206:')
+ call assert_fails('call getcharstr(1, 1)', 'E1206:')
+ call assert_fails('call getcharstr(1, #{number: v:true})', 'E475:')
+ call assert_fails('call getcharstr(1, #{number: v:false})', 'E475:')
+
call setline(1, 'xxxx')
call Ntest_setmouse(1, 3)
let v:mouse_win = 9