# HG changeset patch # User Bram Moolenaar # Date 1603574104 -7200 # Node ID f2bfee4ac356b42aa8ca0dfed1440cc43c6cc0d3 # Parent 81d7d38607861b7350ae2b575ae69f9bacb6c2ae patch 8.2.1900: Vim9: command modifiers do not work Commit: https://github.com/vim/vim/commit/02194d2bd54eacd0b7b9a017a3fe1702ecb80971 Author: Bram Moolenaar Date: Sat Oct 24 23:08:38 2020 +0200 patch 8.2.1900: Vim9: command modifiers do not work Problem: Vim9: command modifiers do not work. Solution: Make most command modifiers work. diff --git a/src/proto/usercmd.pro b/src/proto/usercmd.pro --- a/src/proto/usercmd.pro +++ b/src/proto/usercmd.pro @@ -14,6 +14,7 @@ void ex_command(exarg_T *eap); void ex_comclear(exarg_T *eap); void uc_clear(garray_T *gap); void ex_delcommand(exarg_T *eap); -size_t add_win_cmd_modifers(char_u *buf, int *multi_mods); +size_t add_win_cmd_modifers(char_u *buf, cmdmod_T *cmod, int *multi_mods); +int produce_cmdmods(char_u *buf, cmdmod_T *cmod, int quote); void do_ucmd(exarg_T *eap); /* vim: set ft=c : */ diff --git a/src/scriptfile.c b/src/scriptfile.c --- a/src/scriptfile.c +++ b/src/scriptfile.c @@ -1009,7 +1009,7 @@ ex_options( int multi_mods = 0; buf[0] = NUL; - (void)add_win_cmd_modifers(buf, &multi_mods); + (void)add_win_cmd_modifers(buf, &cmdmod, &multi_mods); vim_setenv((char_u *)"OPTWIN_CMD", buf); cmd_source((char_u *)SYS_OPTWIN_FILE, NULL); 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 @@ -1627,15 +1627,15 @@ def Test_silent() var res = execute('disass s:SilentMessage') assert_match('\d*_SilentMessage\_s*' .. 'silent echomsg "text"\_s*' .. - '\d SILENT\_s*' .. + '\d CMDMOD silent\_s*' .. '\d PUSHS "text"\_s*' .. '\d ECHOMSG 1\_s*' .. - '\d UNSILENT\_s*' .. + '\d CMDMOD_REV\_s*' .. 'silent! echoerr "error"\_s*' .. - '\d SILENT!\_s*' .. + '\d CMDMOD silent!\_s*' .. '\d PUSHS "error"\_s*' .. '\d ECHOERR 1\_s*' .. - '\d UNSILENT!\_s*' .. + '\d CMDMOD_REV\_s*' .. '\d PUSHNR 0\_s*' .. '\d RETURN', res) diff --git a/src/usercmd.c b/src/usercmd.c --- a/src/usercmd.c +++ b/src/usercmd.c @@ -1235,37 +1235,104 @@ add_cmd_modifier(char_u *buf, char *mod_ } /* - * Add modifiers from "cmdmod.cmod_split" to "buf". Set "multi_mods" when one + * Add modifiers from "cmod->cmod_split" to "buf". Set "multi_mods" when one * was added. Return the number of bytes added. */ size_t -add_win_cmd_modifers(char_u *buf, int *multi_mods) +add_win_cmd_modifers(char_u *buf, cmdmod_T *cmod, int *multi_mods) { size_t result = 0; // :aboveleft and :leftabove - if (cmdmod.cmod_split & WSP_ABOVE) + if (cmod->cmod_split & WSP_ABOVE) result += add_cmd_modifier(buf, "aboveleft", multi_mods); // :belowright and :rightbelow - if (cmdmod.cmod_split & WSP_BELOW) + if (cmod->cmod_split & WSP_BELOW) result += add_cmd_modifier(buf, "belowright", multi_mods); // :botright - if (cmdmod.cmod_split & WSP_BOT) + if (cmod->cmod_split & WSP_BOT) result += add_cmd_modifier(buf, "botright", multi_mods); // :tab - if (cmdmod.cmod_tab > 0) + if (cmod->cmod_tab > 0) result += add_cmd_modifier(buf, "tab", multi_mods); // :topleft - if (cmdmod.cmod_split & WSP_TOP) + if (cmod->cmod_split & WSP_TOP) result += add_cmd_modifier(buf, "topleft", multi_mods); // :vertical - if (cmdmod.cmod_split & WSP_VERT) + if (cmod->cmod_split & WSP_VERT) result += add_cmd_modifier(buf, "vertical", multi_mods); return result; } /* + * Generate text for the "cmod" command modifiers. + * If "buf" is NULL just return the length. + */ + int +produce_cmdmods(char_u *buf, cmdmod_T *cmod, int quote) +{ + int result = 0; + int multi_mods = 0; + int i; + typedef struct { + int flag; + char *name; + } mod_entry_T; + static mod_entry_T mod_entries[] = { +#ifdef FEAT_BROWSE_CMD + {CMOD_BROWSE, "browse"}, +#endif +#if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG) + {CMOD_CONFIRM, "confirm"}, +#endif + {CMOD_HIDE, "hide"}, + {CMOD_KEEPALT, "keepalt"}, + {CMOD_KEEPJUMPS, "keepjumps"}, + {CMOD_KEEPMARKS, "keepmarks"}, + {CMOD_KEEPPATTERNS, "keeppatterns"}, + {CMOD_LOCKMARKS, "lockmarks"}, + {CMOD_NOSWAPFILE, "noswapfile"}, + {CMOD_UNSILENT, "unsilent"}, + {CMOD_NOAUTOCMD, "noautocmd"}, +#ifdef HAVE_SANDBOX + {CMOD_SANDBOX, "sandbox"}, +#endif + {0, NULL} + }; + + result = quote ? 2 : 0; + if (buf != NULL) + { + if (quote) + *buf++ = '"'; + *buf = '\0'; + } + + // the modifiers that are simple flags + for (i = 0; mod_entries[i].name != NULL; ++i) + if (cmod->cmod_flags & mod_entries[i].flag) + result += add_cmd_modifier(buf, mod_entries[i].name, &multi_mods); + + // :silent + if (cmod->cmod_flags & CMOD_SILENT) + result += add_cmd_modifier(buf, + (cmod->cmod_flags & CMOD_ERRSILENT) ? "silent!" + : "silent", &multi_mods); + // :verbose + if (p_verbose > 0) + result += add_cmd_modifier(buf, "verbose", &multi_mods); + // flags from cmod->cmod_split + result += add_win_cmd_modifers(buf, cmod, &multi_mods); + if (quote && buf != NULL) + { + buf += result - 2; + *buf = '"'; + } + return result; +} + +/* * Check for a <> code in a user command. * "code" points to the '<'. "len" the length of the <> (inclusive). * "buf" is where the result is to be added. @@ -1452,62 +1519,7 @@ uc_check_code( case ct_MODS: { - int multi_mods = 0; - typedef struct { - int flag; - char *name; - } mod_entry_T; - static mod_entry_T mod_entries[] = { -#ifdef FEAT_BROWSE_CMD - {CMOD_BROWSE, "browse"}, -#endif -#if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG) - {CMOD_CONFIRM, "confirm"}, -#endif - {CMOD_HIDE, "hide"}, - {CMOD_KEEPALT, "keepalt"}, - {CMOD_KEEPJUMPS, "keepjumps"}, - {CMOD_KEEPMARKS, "keepmarks"}, - {CMOD_KEEPPATTERNS, "keeppatterns"}, - {CMOD_LOCKMARKS, "lockmarks"}, - {CMOD_NOSWAPFILE, "noswapfile"}, - {0, NULL} - }; - int i; - - result = quote ? 2 : 0; - if (buf != NULL) - { - if (quote) - *buf++ = '"'; - *buf = '\0'; - } - - // the modifiers that are simple flags - for (i = 0; mod_entries[i].name != NULL; ++i) - if (cmdmod.cmod_flags & mod_entries[i].flag) - result += add_cmd_modifier(buf, mod_entries[i].name, - &multi_mods); - - // TODO: How to support :noautocmd? -#ifdef HAVE_SANDBOX - // TODO: How to support :sandbox? -#endif - // :silent - if (msg_silent > 0) - result += add_cmd_modifier(buf, - emsg_silent > 0 ? "silent!" : "silent", &multi_mods); - // TODO: How to support :unsilent? - // :verbose - if (p_verbose > 0) - result += add_cmd_modifier(buf, "verbose", &multi_mods); - // flags from cmdmod.cmod_split - result += add_win_cmd_modifers(buf, &multi_mods); - if (quote && buf != NULL) - { - buf += result - 2; - *buf = '"'; - } + result = produce_cmdmods(buf, &cmdmod, quote); break; } 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 */ /**/ + 1900, +/**/ 1899, /**/ 1898, diff --git a/src/vim9.h b/src/vim9.h --- a/src/vim9.h +++ b/src/vim9.h @@ -142,8 +142,8 @@ typedef enum { ISN_PUT, // ":put", uses isn_arg.put - ISN_SILENT, // set msg_silent or emsg_silent if arg_number is non-zero - ISN_UNSILENT, // undo ISN_SILENT + ISN_CMDMOD, // set cmdmod + ISN_CMDMOD_REV, // undo ISN_CMDMOD ISN_SHUFFLE, // move item on stack up or down ISN_DROP // pop stack and discard value @@ -278,6 +278,11 @@ typedef struct { linenr_T put_lnum; // line number to put below } put_T; +// arguments to ISN_CMDMOD +typedef struct { + cmdmod_T *cf_cmdmod; // allocated +} cmod_T; + /* * Instruction */ @@ -314,6 +319,7 @@ struct isn_S { checklen_T checklen; shuffle_T shuffle; put_T put; + cmod_T cmdmod; } isn_arg; }; diff --git a/src/vim9compile.c b/src/vim9compile.c --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -142,7 +142,7 @@ struct cctx_S { garray_T ctx_type_stack; // type of each item on the stack garray_T *ctx_type_list; // list of pointers to allocated types - int ctx_silent; // set when ISN_SILENT was generated + int ctx_has_cmdmod; // ISN_CMDMOD was generated }; static void delete_def_function_contents(dfunc_T *dfunc); @@ -1823,36 +1823,45 @@ generate_EXECCONCAT(cctx_T *cctx, int co } /* - * Generate any instructions for side effects of "cmdmod". + * Generate an instruction for any command modifiers. */ static int generate_cmdmods(cctx_T *cctx, cmdmod_T *cmod) { isn_T *isn; - // TODO: use more modifiers in the command - if (cmod->cmod_flags & (CMOD_SILENT | CMOD_ERRSILENT)) - { - if ((isn = generate_instr(cctx, ISN_SILENT)) == NULL) + if (cmod->cmod_flags != 0 + || cmod->cmod_split != 0 + || cmod->cmod_verbose != 0 + || cmod->cmod_tab != 0 + || cmod->cmod_filter_regmatch.regprog != NULL) + { + cctx->ctx_has_cmdmod = TRUE; + + if ((isn = generate_instr(cctx, ISN_CMDMOD)) == NULL) return FAIL; - isn->isn_arg.number = (cmod->cmod_flags & CMOD_ERRSILENT) != 0; - cctx->ctx_silent = (cmod->cmod_flags & CMOD_ERRSILENT) ? 2 : 1; - } + isn->isn_arg.cmdmod.cf_cmdmod = ALLOC_ONE(cmdmod_T); + if (isn->isn_arg.cmdmod.cf_cmdmod == NULL) + return FAIL; + mch_memmove(isn->isn_arg.cmdmod.cf_cmdmod, cmod, sizeof(cmdmod_T)); + // filter progam now belongs to the instruction + cmod->cmod_filter_regmatch.regprog = NULL; + } + return OK; } static int -generate_restore_cmdmods(cctx_T *cctx) +generate_undo_cmdmods(cctx_T *cctx) { isn_T *isn; - if (cctx->ctx_silent > 0) - { - if ((isn = generate_instr(cctx, ISN_UNSILENT)) == NULL) + if (cctx->ctx_has_cmdmod) + { + if ((isn = generate_instr(cctx, ISN_CMDMOD_REV)) == NULL) return FAIL; - isn->isn_arg.number = cctx->ctx_silent == 2; - cctx->ctx_silent = 0; - } + } + return OK; } @@ -7092,9 +7101,9 @@ compile_def_function(ufunc_T *ufunc, int for (;;) { exarg_T ea; - cmdmod_T local_cmdmod; int starts_with_colon = FALSE; char_u *cmd; + cmdmod_T local_cmdmod; // Bail out on the first error to avoid a flood of errors and report // the right line number when inside try/catch. @@ -7175,7 +7184,7 @@ compile_def_function(ufunc_T *ufunc, int /* * COMMAND MODIFIERS */ - CLEAR_FIELD(local_cmdmod); + cctx.ctx_has_cmdmod = FALSE; if (parse_command_modifiers(&ea, &errormsg, &local_cmdmod, FALSE) == FAIL) { @@ -7497,7 +7506,7 @@ nextline: line = skipwhite(line); // Undo any command modifiers. - generate_restore_cmdmods(&cctx); + generate_undo_cmdmods(&cctx); if (cctx.ctx_type_stack.ga_len < 0) { @@ -7742,6 +7751,12 @@ delete_instr(isn_T *isn) free_type(isn->isn_arg.type.ct_type); break; + case ISN_CMDMOD: + vim_regfree(isn->isn_arg.cmdmod.cf_cmdmod + ->cmod_filter_regmatch.regprog); + vim_free(isn->isn_arg.cmdmod.cf_cmdmod); + break; + case ISN_2BOOL: case ISN_2STRING: case ISN_2STRING_ANY: @@ -7754,6 +7769,7 @@ delete_instr(isn_T *isn) case ISN_CATCH: case ISN_CHECKLEN: case ISN_CHECKNR: + case ISN_CMDMOD_REV: case ISN_COMPAREANY: case ISN_COMPAREBLOB: case ISN_COMPAREBOOL: @@ -7805,7 +7821,6 @@ delete_instr(isn_T *isn) case ISN_PUT: case ISN_RETURN: case ISN_SHUFFLE: - case ISN_SILENT: case ISN_SLICE: case ISN_STORE: case ISN_STOREDICT: @@ -7819,7 +7834,6 @@ delete_instr(isn_T *isn) case ISN_STRSLICE: case ISN_THROW: case ISN_TRY: - case ISN_UNSILENT: // nothing allocated break; } diff --git a/src/vim9execute.c b/src/vim9execute.c --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -832,8 +832,8 @@ call_def_function( int save_suppress_errthrow = suppress_errthrow; msglist_T **saved_msg_list = NULL; msglist_T *private_msg_list = NULL; - int save_msg_silent = -1; - int save_emsg_silent = -1; + cmdmod_T save_cmdmod; + int restore_cmdmod = FALSE; // Get pointer to item in the stack. #define STACK_TV(idx) (((typval_T *)ectx.ec_stack.ga_data) + idx) @@ -2816,22 +2816,19 @@ call_def_function( } break; - case ISN_SILENT: - if (save_msg_silent == -1) - save_msg_silent = msg_silent; - ++msg_silent; - if (iptr->isn_arg.number) - { - if (save_emsg_silent == -1) - save_emsg_silent = emsg_silent; - ++emsg_silent; - } + case ISN_CMDMOD: + save_cmdmod = cmdmod; + restore_cmdmod = TRUE; + cmdmod = *iptr->isn_arg.cmdmod.cf_cmdmod; + apply_cmdmod(&cmdmod); break; - case ISN_UNSILENT: - --msg_silent; - if (iptr->isn_arg.number) - --emsg_silent; + case ISN_CMDMOD_REV: + // filter regprog is owned by the instruction, don't free it + cmdmod.cmod_filter_regmatch.regprog = NULL; + undo_cmdmod(&cmdmod); + cmdmod = save_cmdmod; + restore_cmdmod = FALSE; break; case ISN_SHUFFLE: @@ -2905,10 +2902,12 @@ failed: } msg_list = saved_msg_list; - if (save_msg_silent != -1) - msg_silent = save_msg_silent; - if (save_emsg_silent != -1) - emsg_silent = save_emsg_silent; + if (restore_cmdmod) + { + cmdmod.cmod_filter_regmatch.regprog = NULL; + undo_cmdmod(&cmdmod); + cmdmod = save_cmdmod; + } failed_early: // Free all local variables, but not arguments. @@ -3527,10 +3526,24 @@ ex_disassemble(exarg_T *eap) (long)iptr->isn_arg.put.put_lnum); break; - case ISN_SILENT: smsg("%4d SILENT%s", current, - iptr->isn_arg.number ? "!" : ""); break; - case ISN_UNSILENT: smsg("%4d UNSILENT%s", current, - iptr->isn_arg.number ? "!" : ""); break; + // TODO: summarize modifiers + case ISN_CMDMOD: + { + char_u *buf; + int len = produce_cmdmods( + NULL, iptr->isn_arg.cmdmod.cf_cmdmod, FALSE); + + buf = alloc(len + 1); + if (buf != NULL) + { + (void)produce_cmdmods( + buf, iptr->isn_arg.cmdmod.cf_cmdmod, FALSE); + smsg("%4d CMDMOD %s", current, buf); + vim_free(buf); + } + break; + } + case ISN_CMDMOD_REV: smsg("%4d CMDMOD_REV", current); break; case ISN_SHUFFLE: smsg("%4d SHUFFLE %d up %d", current, iptr->isn_arg.shuffle.shfl_item,