patch 9.1.1070: Cannot control cursor positioning of getchar()

Problem:  Cannot control cursor positioning of getchar().
Solution: Add "cursor" flag to {opts}, with possible values "hide",
          "keep" and "msg".

related: #10603
closes: #16569

Signed-off-by: zeertzjq <zeertzjq@outlook.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
zeertzjq
2025-02-02 19:01:01 +01:00
committed by Christian Brabandt
parent 001c26cd61
commit edf0f7db28
6 changed files with 98 additions and 11 deletions

View File

@ -3953,6 +3953,13 @@ getchar([{expr} [, {opts}]]) *getchar()*
The optional argument {opts} is a Dict and supports the
following items:
cursor A String specifying cursor behavior
when waiting for a character.
"hide": hide the cursor.
"keep": keep current cursor unchanged.
"msg": move cursor to message area.
(default: "msg")
number If |TRUE|, return a Number when getting
a single character.
If |FALSE|, the return value is always

View File

@ -1,4 +1,4 @@
*todo.txt* For Vim version 9.1. Last change: 2025 Jan 16
*todo.txt* For Vim version 9.1. Last change: 2025 Feb 02
VIM REFERENCE MANUAL by Bram Moolenaar
@ -467,9 +467,6 @@ IDEA: when drawing the text, store the text byte index in ScreenLinesIdx[].
When converting screen column to text position use this.
The line number can be obtained from win->w_lines[].
Version of getchar() that does not move the cursor - #10603 Use a separate
argument for the new flag.
test_arglist func Test_all_not_allowed_from_cmdwin() hangs on MS-Windows.
Can we add highlighting to ":echowindow"?

View File

@ -41636,6 +41636,8 @@ Changed~
- add |dist#vim9#Launch()| and |dist#vim9#Open()| to the |vim-script-library|
and decouple it from |netrw|
- new digraph "APPROACHES THE LIMIT" using ".="
- Add the optional {opts} |Dict| argument to |getchar()| to control: cursor
behaviour, return type and whether or not to simplify the returned key
*added-9.2*
Added ~

View File

@ -2386,9 +2386,11 @@ char_avail(void)
static void
getchar_common(typval_T *argvars, typval_T *rettv, int allow_number)
{
varnumber_T n;
varnumber_T n = 0;
int called_emsg_start = called_emsg;
int error = FALSE;
int simplify = TRUE;
char_u cursor_flag = 'm';
if ((in_vim9script()
&& check_for_opt_bool_or_number_arg(argvars, 0) == FAIL)
@ -2399,18 +2401,31 @@ getchar_common(typval_T *argvars, typval_T *rettv, int allow_number)
if (argvars[0].v_type != VAR_UNKNOWN && argvars[1].v_type == VAR_DICT)
{
dict_T *d = argvars[1].vval.v_dict;
char_u *cursor_str;
if (allow_number)
allow_number = dict_get_bool(d, "number", TRUE);
else if (dict_has_key(d, "number"))
{
semsg(_(e_invalid_argument_str), "number");
error = TRUE;
}
simplify = dict_get_bool(d, "simplify", TRUE);
cursor_str = dict_get_string(d, "cursor", FALSE);
if (cursor_str != NULL)
{
if (STRCMP(cursor_str, "hide") != 0
&& STRCMP(cursor_str, "keep") != 0
&& STRCMP(cursor_str, "msg") != 0)
semsg(_(e_invalid_value_for_argument_str_str), "cursor",
cursor_str);
else
cursor_flag = cursor_str[0];
}
}
if (called_emsg != called_emsg_start)
return;
#ifdef MESSAGE_QUEUE
// vpeekc() used to check for messages, but that caused problems, invoking
// a callback where it was not expected. Some plugins use getchar(1) in a
@ -2418,14 +2433,16 @@ getchar_common(typval_T *argvars, typval_T *rettv, int allow_number)
parse_queued_messages();
#endif
// Position the cursor. Needed after a message that ends in a space.
windgoto(msg_row, msg_col);
if (cursor_flag == 'h')
cursor_sleep();
else if (cursor_flag == 'm')
windgoto(msg_row, msg_col);
++no_mapping;
++allow_keys;
if (!simplify)
++no_reduce_keys;
while (!error)
for (;;)
{
if (argvars[0].v_type == VAR_UNKNOWN
|| (argvars[0].v_type == VAR_NUMBER
@ -2453,6 +2470,9 @@ getchar_common(typval_T *argvars, typval_T *rettv, int allow_number)
if (!simplify)
--no_reduce_keys;
if (cursor_flag == 'h')
cursor_unsleep();
set_vim_var_nr(VV_MOUSE_WIN, 0);
set_vim_var_nr(VV_MOUSE_WINID, 0);
set_vim_var_nr(VV_MOUSE_LNUM, 0);

View File

@ -2628,6 +2628,14 @@ func Test_getchar()
call assert_fails('call getchar(1, 1)', 'E1206:')
call assert_fails('call getcharstr(1, 1)', 'E1206:')
call assert_fails('call getchar(1, #{cursor: "foo"})', 'E475:')
call assert_fails('call getcharstr(1, #{cursor: "foo"})', 'E475:')
call assert_fails('call getchar(1, #{cursor: 0z})', 'E976:')
call assert_fails('call getcharstr(1, #{cursor: 0z})', 'E976:')
call assert_fails('call getchar(1, #{simplify: 0z})', 'E974:')
call assert_fails('call getcharstr(1, #{simplify: 0z})', 'E974:')
call assert_fails('call getchar(1, #{number: []})', 'E745:')
call assert_fails('call getchar(1, #{number: {}})', 'E728:')
call assert_fails('call getcharstr(1, #{number: v:true})', 'E475:')
call assert_fails('call getcharstr(1, #{number: v:false})', 'E475:')
@ -2646,6 +2654,57 @@ func Test_getchar()
enew!
endfunc
func Test_getchar_cursor_position()
CheckRunVimInTerminal
let lines =<< trim END
call setline(1, ['foobar', 'foobar', 'foobar'])
call cursor(3, 6)
nnoremap <F1> <Cmd>echo 1234<Bar>call getchar()<CR>
nnoremap <F2> <Cmd>call getchar()<CR>
nnoremap <F3> <Cmd>call getchar(-1, {})<CR>
nnoremap <F4> <Cmd>call getchar(-1, #{cursor: 'msg'})<CR>
nnoremap <F5> <Cmd>call getchar(-1, #{cursor: 'keep'})<CR>
nnoremap <F6> <Cmd>call getchar(-1, #{cursor: 'hide'})<CR>
END
call writefile(lines, 'XgetcharCursorPos', 'D')
let buf = RunVimInTerminal('-S XgetcharCursorPos', {'rows': 6})
call WaitForAssert({-> assert_equal([3, 6], term_getcursor(buf)[0:1])})
call term_sendkeys(buf, "\<F1>")
call WaitForAssert({-> assert_equal([6, 5], term_getcursor(buf)[0:1])})
call assert_true(term_getcursor(buf)[2].visible)
call term_sendkeys(buf, 'a')
call WaitForAssert({-> assert_equal([3, 6], term_getcursor(buf)[0:1])})
call assert_true(term_getcursor(buf)[2].visible)
for key in ["\<F2>", "\<F3>", "\<F4>"]
call term_sendkeys(buf, key)
call WaitForAssert({-> assert_equal([6, 1], term_getcursor(buf)[0:1])})
call assert_true(term_getcursor(buf)[2].visible)
call term_sendkeys(buf, 'a')
call WaitForAssert({-> assert_equal([3, 6], term_getcursor(buf)[0:1])})
call assert_true(term_getcursor(buf)[2].visible)
endfor
call term_sendkeys(buf, "\<F5>")
call TermWait(buf, 50)
call assert_equal([3, 6], term_getcursor(buf)[0:1])
call assert_true(term_getcursor(buf)[2].visible)
call term_sendkeys(buf, 'a')
call TermWait(buf, 50)
call assert_equal([3, 6], term_getcursor(buf)[0:1])
call assert_true(term_getcursor(buf)[2].visible)
call term_sendkeys(buf, "\<F6>")
call WaitForAssert({-> assert_false(term_getcursor(buf)[2].visible)})
call term_sendkeys(buf, 'a')
call WaitForAssert({-> assert_true(term_getcursor(buf)[2].visible)})
call assert_equal([3, 6], term_getcursor(buf)[0:1])
call StopVimInTerminal(buf)
endfunc
func Test_libcall_libcallnr()
CheckFeature libcall

View File

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