changeset 22176:6941d3205be9 v8.2.1637

patch 8.2.1637: Vim9: :put ={expr} does not work inside :def function Commit: https://github.com/vim/vim/commit/c3516f7e4507c77424b94cb89071f6d0841f4e6a Author: Bram Moolenaar <Bram@vim.org> Date: Tue Sep 8 22:45:35 2020 +0200 patch 8.2.1637: Vim9: :put ={expr} does not work inside :def function Problem: Vim9: :put ={expr} does not work inside :def function. Solution: Add ISN_PUT. (closes https://github.com/vim/vim/issues/6397)
author Bram Moolenaar <Bram@vim.org>
date Tue, 08 Sep 2020 23:00:04 +0200
parents 0c8d7e6e20a6
children ac6d1eecb47d
files src/edit.c src/ex_docmd.c src/mouse.c src/normal.c src/proto/register.pro src/register.c src/testdir/test_vim9_cmd.vim src/testdir/test_vim9_disassemble.vim src/version.c src/vim9.h src/vim9compile.c src/vim9execute.c
diffstat 12 files changed, 158 insertions(+), 13 deletions(-) [+]
line wrap: on
line diff
--- a/src/edit.c
+++ b/src/edit.c
@@ -3399,7 +3399,7 @@ ins_reg(void)
 	    AppendCharToRedobuff(literally);
 	    AppendCharToRedobuff(regname);
 
-	    do_put(regname, BACKWARD, 1L,
+	    do_put(regname, NULL, BACKWARD, 1L,
 		 (literally == Ctrl_P ? PUT_FIXINDENT : 0) | PUT_CURSEND);
 	}
 	else if (insert_reg(regname, literally) == FAIL)
@@ -4776,7 +4776,7 @@ ins_pagedown(void)
     static void
 ins_drop(void)
 {
-    do_put('~', BACKWARD, 1L, PUT_CURSEND);
+    do_put('~', NULL, BACKWARD, 1L, PUT_CURSEND);
 }
 #endif
 
--- a/src/ex_docmd.c
+++ b/src/ex_docmd.c
@@ -7321,7 +7321,7 @@ ex_put(exarg_T *eap)
 	eap->forceit = TRUE;
     }
     curwin->w_cursor.lnum = eap->line2;
-    do_put(eap->regname, eap->forceit ? BACKWARD : FORWARD, 1L,
+    do_put(eap->regname, NULL, eap->forceit ? BACKWARD : FORWARD, 1L,
 						       PUT_LINE|PUT_CURSLINE);
 }
 
--- a/src/mouse.c
+++ b/src/mouse.c
@@ -430,7 +430,8 @@ do_mouse(
 		    insert_reg(regname, TRUE);
 		else
 		{
-		    do_put(regname, BACKWARD, 1L, fixindent | PUT_CURSEND);
+		    do_put(regname, NULL, BACKWARD, 1L,
+						      fixindent | PUT_CURSEND);
 
 		    // Repeat it with CTRL-R CTRL-O r or CTRL-R CTRL-P r
 		    AppendCharToRedobuff(Ctrl_R);
@@ -849,7 +850,7 @@ do_mouse(
 	// to this position
 	if (restart_edit != 0)
 	    where_paste_started = curwin->w_cursor;
-	do_put(regname, dir, count, fixindent | PUT_CURSEND);
+	do_put(regname, NULL, dir, count, fixindent | PUT_CURSEND);
     }
 
 #if defined(FEAT_QUICKFIX)
--- a/src/normal.c
+++ b/src/normal.c
@@ -7427,7 +7427,7 @@ nv_put_opt(cmdarg_T *cap, int fix_indent
 	    // May have been reset in do_put().
 	    VIsual_active = TRUE;
 	}
-	do_put(cap->oap->regname, dir, cap->count1, flags);
+	do_put(cap->oap->regname, NULL, dir, cap->count1, flags);
 
 	// If a register was saved, put it back now.
 	if (reg2 != NULL)
@@ -7500,7 +7500,7 @@ nv_nbcmd(cmdarg_T *cap)
     static void
 nv_drop(cmdarg_T *cap UNUSED)
 {
-    do_put('~', BACKWARD, 1L, PUT_CURSEND);
+    do_put('~', NULL, BACKWARD, 1L, PUT_CURSEND);
 }
 #endif
 
--- a/src/proto/register.pro
+++ b/src/proto/register.pro
@@ -27,7 +27,7 @@ void init_yank(void);
 void clear_registers(void);
 void free_yank_all(void);
 int op_yank(oparg_T *oap, int deleting, int mess);
-void do_put(int regname, int dir, long count, int flags);
+void do_put(int regname, char_u *expr_result, int dir, long count, int flags);
 int get_register_name(int num);
 int get_unname_register(void);
 void ex_display(exarg_T *eap);
--- a/src/register.c
+++ b/src/register.c
@@ -1487,6 +1487,7 @@ copy_yank_reg(yankreg_T *reg)
     void
 do_put(
     int		regname,
+    char_u	*expr_result,	// result for regname "=" when compiled
     int		dir,		// BACKWARD for 'P', FORWARD for 'p'
     long	count,
     int		flags)
@@ -1551,11 +1552,12 @@ do_put(
 
     // For special registers '%' (file name), '#' (alternate file name) and
     // ':' (last command line), etc. we have to create a fake yank register.
-    if (get_spec_reg(regname, &insert_string, &allocated, TRUE))
-    {
-	if (insert_string == NULL)
-	    return;
-    }
+    // For compiled code "expr_result" holds the expression result.
+    if (regname == '=' && expr_result != NULL)
+	insert_string = expr_result;
+    else if (get_spec_reg(regname, &insert_string, &allocated, TRUE)
+		&& insert_string == NULL)
+	return;
 
     // Autocommands may be executed when saving lines for undo.  This might
     // make "y_array" invalid, so we start undo now to avoid that.
--- a/src/testdir/test_vim9_cmd.vim
+++ b/src/testdir/test_vim9_cmd.vim
@@ -315,5 +315,21 @@ def Test_normal_command()
   bwipe!
 enddef
 
+def Test_put_command()
+  new
+  @p = 'ppp'
+  put p
+  assert_equal('ppp', getline(2))
+
+  put ='below'
+  assert_equal('below', getline(3))
+  put! ='above'
+  assert_equal('above', getline(3))
+  assert_equal('below', getline(4))
+
+  bwipe!
+enddef
+
+
 
 " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
--- a/src/testdir/test_vim9_disassemble.vim
+++ b/src/testdir/test_vim9_disassemble.vim
@@ -118,6 +118,21 @@ def Test_disassemble_yank_range()
         res)
 enddef
 
+def s:PutExpr()
+  :3put ="text"
+enddef
+
+def Test_disassemble_put_expr()
+  let res = execute('disass s:PutExpr')
+  assert_match('<SNR>\d*_PutExpr.*' ..
+        ' :3put ="text"\_s*' ..
+        '\d PUSHS "text"\_s*' ..
+        '\d PUT = 3\_s*' ..
+        '\d PUSHNR 0\_s*' ..
+        '\d RETURN',
+        res)
+enddef
+
 def s:ScriptFuncPush()
   let localbool = true
   let localspec = v:none
--- a/src/version.c
+++ b/src/version.c
@@ -755,6 +755,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1637,
+/**/
     1636,
 /**/
     1635,
--- a/src/vim9.h
+++ b/src/vim9.h
@@ -135,6 +135,8 @@ typedef enum {
     ISN_CHECKTYPE,  // check value type is isn_arg.type.tc_type
     ISN_CHECKLEN,   // check list length is isn_arg.checklen.cl_min_len
 
+    ISN_PUT,	    // ":put", uses isn_arg.put
+
     ISN_SHUFFLE,    // move item on stack up or down
     ISN_DROP	    // pop stack and discard value
 } isntype_T;
@@ -261,6 +263,12 @@ typedef struct {
     int		shfl_up;	// places to move upwards
 } shuffle_T;
 
+// arguments to ISN_PUT
+typedef struct {
+    int		put_regname;	// register, can be NUL
+    linenr_T	put_lnum;	// line number to put below
+} put_T;
+
 /*
  * Instruction
  */
@@ -296,6 +304,7 @@ struct isn_S {
 	newfunc_T	    newfunc;
 	checklen_T	    checklen;
 	shuffle_T	    shuffle;
+	put_T		    put;
     } isn_arg;
 };
 
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -1567,6 +1567,22 @@ generate_MULT_EXPR(cctx_T *cctx, isntype
     return OK;
 }
 
+/*
+ * Generate an ISN_PUT instruction.
+ */
+    static int
+generate_PUT(cctx_T *cctx, int regname, linenr_T lnum)
+{
+    isn_T	*isn;
+
+    RETURN_OK_IF_SKIP(cctx);
+    if ((isn = generate_instr(cctx, ISN_PUT)) == NULL)
+	return FAIL;
+    isn->isn_arg.put.put_regname = regname;
+    isn->isn_arg.put.put_lnum = lnum;
+    return OK;
+}
+
     static int
 generate_EXEC(cctx_T *cctx, char_u *line)
 {
@@ -6272,6 +6288,50 @@ compile_mult_expr(char_u *arg, int cmdid
 }
 
 /*
+ * :put r
+ * :put ={expr}
+ */
+    static char_u *
+compile_put(char_u *arg, exarg_T *eap, cctx_T *cctx)
+{
+    char_u	*line = arg;
+    linenr_T	lnum;
+    char	*errormsg;
+    int		above = FALSE;
+
+    if (*arg == '!')
+    {
+	above = TRUE;
+	line = skipwhite(arg + 1);
+    }
+    eap->regname = *line;
+
+    if (eap->regname == '=')
+    {
+	char_u *p = line + 1;
+
+	if (compile_expr0(&p, cctx) == FAIL)
+	    return NULL;
+	line = p;
+    }
+    else if (eap->regname != NUL)
+	++line;
+
+    // TODO: if the range is something like "$" need to evaluate at runtime
+    if (parse_cmd_address(eap, &errormsg, FALSE) == FAIL)
+	return NULL;
+    if (eap->addr_count == 0)
+	lnum = -1;
+    else
+	lnum = eap->line2;
+    if (above)
+	--lnum;
+
+    generate_PUT(cctx, eap->regname, lnum);
+    return line;
+}
+
+/*
  * A command that is not compiled, execute with legacy code.
  */
     static char_u *
@@ -6870,6 +6930,11 @@ compile_def_function(ufunc_T *ufunc, int
 		    line = compile_mult_expr(p, ea.cmdidx, &cctx);
 		    break;
 
+	    case CMD_put:
+		    ea.cmd = cmd;
+		    line = compile_put(p, &ea, &cctx);
+		    break;
+
 	    // TODO: any other commands with an expression argument?
 
 	    case CMD_append:
@@ -7192,6 +7257,7 @@ delete_instr(isn_T *isn)
 	case ISN_PUSHF:
 	case ISN_PUSHNR:
 	case ISN_PUSHSPEC:
+	case ISN_PUT:
 	case ISN_RETURN:
 	case ISN_SHUFFLE:
 	case ISN_SLICE:
--- a/src/vim9execute.c
+++ b/src/vim9execute.c
@@ -2581,6 +2581,36 @@ call_def_function(
 		}
 		break;
 
+	    case ISN_PUT:
+		{
+		    int		regname = iptr->isn_arg.put.put_regname;
+		    linenr_T	lnum = iptr->isn_arg.put.put_lnum;
+		    char_u	*expr = NULL;
+		    int		dir = FORWARD;
+
+		    if (regname == '=')
+		    {
+			tv = STACK_TV_BOT(-1);
+			if (tv->v_type == VAR_STRING)
+			    expr = tv->vval.v_string;
+			else
+			{
+			    expr = typval_tostring(tv);  // allocates value
+			    clear_tv(tv);
+			}
+			--ectx.ec_stack.ga_len;
+		    }
+		    if (lnum == -2)
+			// :put! above cursor
+			dir = BACKWARD;
+		    else if (lnum >= 0)
+			curwin->w_cursor.lnum = iptr->isn_arg.put.put_lnum;
+		    check_cursor();
+		    do_put(regname, expr, dir, 1L, PUT_LINE|PUT_CURSLINE);
+		    vim_free(expr);
+		}
+		break;
+
 	    case ISN_SHUFFLE:
 		{
 		    typval_T	    tmp_tv;
@@ -3227,6 +3257,10 @@ ex_disassemble(exarg_T *eap)
 	    case ISN_2STRING_ANY: smsg("%4d 2STRING_ANY stack[%lld]", current,
 					 (long long)(iptr->isn_arg.number));
 			      break;
+	    case ISN_PUT:
+		smsg("%4d PUT %c %ld", current, iptr->isn_arg.put.put_regname,
+					     (long)iptr->isn_arg.put.put_lnum);
+		break;
 
 	    case ISN_SHUFFLE: smsg("%4d SHUFFLE %d up %d", current,
 					 iptr->isn_arg.shuffle.shfl_item,