changeset 24838:3f9053c21765 v8.2.2957

patch 8.2.2957: using getchar() in Vim9 script is problematic Commit: https://github.com/vim/vim/commit/3a7503c34c65ed15cc08deb5b54aaf2ea51525b4 Author: Bram Moolenaar <Bram@vim.org> 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)
author Bram Moolenaar <Bram@vim.org>
date Mon, 07 Jun 2021 18:30:04 +0200
parents a9c9ff4fb89b
children 964d7c506776
files runtime/doc/eval.txt src/evalfunc.c src/getchar.c src/proto/getchar.pro src/testdir/test_functions.vim src/version.c
diffstat 6 files changed, 70 insertions(+), 5 deletions(-) [+]
line wrap: on
line diff
--- 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 <expr> , 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
--- 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,
--- 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
--- 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);
--- 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)
--- 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,