# HG changeset patch # User Bram Moolenaar # Date 1623083404 -7200 # Node ID 3f9053c217652002466c8d3b06aeb808cda970cf # Parent a9c9ff4fb89b84b039f68dcccc075b359b36a366 patch 8.2.2957: using getchar() in Vim9 script is problematic Commit: https://github.com/vim/vim/commit/3a7503c34c65ed15cc08deb5b54aaf2ea51525b4 Author: Bram Moolenaar Date: Mon Jun 7 18:29:17 2021 +0200 patch 8.2.2957: using getchar() in Vim9 script is problematic Problem: Using getchar() in Vim9 script is problematic. Solution: Add getcharstr(). (closes https://github.com/vim/vim/issues/8343) diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -2594,10 +2594,12 @@ getbufline({expr}, {lnum} [, {end}]) getbufvar({expr}, {varname} [, {def}]) any variable {varname} in buffer {expr} getchangelist([{expr}]) List list of change list items -getchar([expr]) Number get one character from the user +getchar([expr]) Number or String + get one character from the user getcharmod() Number modifiers for the last typed character getcharpos({expr}) List position of cursor, mark, etc. getcharsearch() Dict last character search +getcharstr([expr]) String get one character from the user getcmdline() String return the current command-line getcmdpos() Number return cursor position in command-line getcmdtype() String return current command-line type @@ -5232,6 +5234,7 @@ getchar([expr]) *getchar()* 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()|. Without [expr] and when [expr] is 0 a whole character or special key is returned. If it is a single character, the @@ -5357,6 +5360,20 @@ getcharsearch() *getcharsearch()* :nnoremap , getcharsearch().forward ? ',' : ';' < Also see |setcharsearch()|. + +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. + + getcmdline() *getcmdline()* Return the current command-line. Only works when the command line is being edited, thus requires use of |c_CTRL-\_e| or diff --git a/src/evalfunc.c b/src/evalfunc.c --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -945,13 +945,15 @@ static funcentry_T global_functions[] = {"getchangelist", 0, 1, FEARG_1, NULL, ret_list_any, f_getchangelist}, {"getchar", 0, 1, 0, NULL, - ret_number, f_getchar}, + ret_any, f_getchar}, {"getcharmod", 0, 0, 0, NULL, ret_number, f_getcharmod}, {"getcharpos", 1, 1, FEARG_1, NULL, ret_list_number, f_getcharpos}, {"getcharsearch", 0, 0, 0, NULL, ret_dict_any, f_getcharsearch}, + {"getcharstr", 0, 1, 0, NULL, + ret_string, f_getcharstr}, {"getcmdline", 0, 0, 0, NULL, ret_string, f_getcmdline}, {"getcmdpos", 0, 0, 0, NULL, diff --git a/src/getchar.c b/src/getchar.c --- a/src/getchar.c +++ b/src/getchar.c @@ -2016,10 +2016,10 @@ char_avail(void) #if defined(FEAT_EVAL) || defined(PROTO) /* - * "getchar()" function + * "getchar()" and "getcharstr()" functions */ - void -f_getchar(typval_T *argvars, typval_T *rettv) + static void +getchar_common(typval_T *argvars, typval_T *rettv) { varnumber_T n; int error = FALSE; @@ -2127,6 +2127,42 @@ f_getchar(typval_T *argvars, typval_T *r } /* + * "getchar()" function + */ + void +f_getchar(typval_T *argvars, typval_T *rettv) +{ + getchar_common(argvars, rettv); +} + +/* + * "getcharstr()" function + */ + void +f_getcharstr(typval_T *argvars, typval_T *rettv) +{ + getchar_common(argvars, rettv); + + if (rettv->v_type == VAR_NUMBER) + { + char_u temp[7]; // mbyte-char: 6, NUL: 1 + varnumber_T n = rettv->vval.v_number; + int i = 0; + + if (n != 0) + { + if (has_mbyte) + i += (*mb_char2bytes)(n, temp + i); + else + temp[i++] = n; + } + temp[i++] = NUL; + rettv->v_type = VAR_STRING; + rettv->vval.v_string = vim_strsave(temp); + } +} + +/* * "getcharmod()" function */ void diff --git a/src/proto/getchar.pro b/src/proto/getchar.pro --- a/src/proto/getchar.pro +++ b/src/proto/getchar.pro @@ -46,6 +46,7 @@ int vpeekc_nomap(void); int vpeekc_any(void); int char_avail(void); void f_getchar(typval_T *argvars, typval_T *rettv); +void f_getcharstr(typval_T *argvars, typval_T *rettv); void f_getcharmod(typval_T *argvars, typval_T *rettv); void parse_queued_messages(void); void vungetc(int c); diff --git a/src/testdir/test_functions.vim b/src/testdir/test_functions.vim --- a/src/testdir/test_functions.vim +++ b/src/testdir/test_functions.vim @@ -1729,6 +1729,13 @@ endfunc func Test_getchar() call feedkeys('a', '') call assert_equal(char2nr('a'), getchar()) + call assert_equal(0, getchar(0)) + call assert_equal(0, getchar(1)) + + call feedkeys('a', '') + call assert_equal('a', getcharstr()) + call assert_equal('', getcharstr(0)) + call assert_equal('', getcharstr(1)) call setline(1, 'xxxx') call test_setmouse(1, 3) diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -751,6 +751,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 2957, +/**/ 2956, /**/ 2955,