# HG changeset patch # User Bram Moolenaar # Date 1599598804 -7200 # Node ID 6941d3205be9bd7cda5b62fa5253f97dc0fad853 # Parent 0c8d7e6e20a6a7be13d4a336c188e7ab44aa9abb patch 8.2.1637: Vim9: :put ={expr} does not work inside :def function Commit: https://github.com/vim/vim/commit/c3516f7e4507c77424b94cb89071f6d0841f4e6a Author: Bram Moolenaar 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) diff --git a/src/edit.c b/src/edit.c --- 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 diff --git a/src/ex_docmd.c b/src/ex_docmd.c --- 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); } diff --git a/src/mouse.c b/src/mouse.c --- 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) diff --git a/src/normal.c b/src/normal.c --- 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 diff --git a/src/proto/register.pro b/src/proto/register.pro --- 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); diff --git a/src/register.c b/src/register.c --- 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. diff --git a/src/testdir/test_vim9_cmd.vim b/src/testdir/test_vim9_cmd.vim --- 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 diff --git a/src/testdir/test_vim9_disassemble.vim b/src/testdir/test_vim9_disassemble.vim --- 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('\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 diff --git a/src/version.c b/src/version.c --- 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, diff --git a/src/vim9.h b/src/vim9.h --- 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; }; diff --git a/src/vim9compile.c b/src/vim9compile.c --- 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: diff --git a/src/vim9execute.c b/src/vim9execute.c --- 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,