diff options
Diffstat (limited to 'src/nvim/ex_docmd.c')
-rw-r--r-- | src/nvim/ex_docmd.c | 285 |
1 files changed, 167 insertions, 118 deletions
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 9f0f8d93a3..a4b43e598d 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -67,8 +67,8 @@ #include "nvim/sign.h" #include "nvim/spell.h" #include "nvim/spellfile.h" -#include "nvim/strings.h" #include "nvim/state.h" +#include "nvim/strings.h" #include "nvim/syntax.h" #include "nvim/tag.h" #include "nvim/terminal.h" @@ -81,23 +81,7 @@ static int quitmore = 0; static bool ex_pressedreturn = false; -typedef struct ucmd { - char_u *uc_name; // The command name - uint32_t uc_argt; // The argument type - char_u *uc_rep; // The command's replacement string - long uc_def; // The default value for a range/count - int uc_compl; // completion type - cmd_addr_T uc_addr_type; // The command's address type - sctx_T uc_script_ctx; // SCTX where the command was defined - char_u *uc_compl_arg; // completion argument if any -} ucmd_T; - -#define UC_BUFFER 1 // -buffer: local to current buffer - -static garray_T ucmds = { 0, 0, sizeof(ucmd_T), 4, NULL }; - -#define USER_CMD(i) (&((ucmd_T *)(ucmds.ga_data))[i]) -#define USER_CMD_GA(gap, i) (&((ucmd_T *)((gap)->ga_data))[i]) +garray_T ucmds = { 0, 0, sizeof(ucmd_T), 4, NULL }; // Whether a command index indicates a user command. #define IS_USER_CMDIDX(idx) ((int)(idx) < 0) @@ -2761,6 +2745,7 @@ static char_u *find_ucmd(exarg_T *eap, char_u *p, int *full, expand_T *xp, int * *complp = uc->uc_compl; } if (xp != NULL) { + xp->xp_luaref = uc->uc_compl_luaref; xp->xp_arg = uc->uc_compl_arg; xp->xp_script_ctx = uc->uc_script_ctx; xp->xp_script_ctx.sc_lnum += sourcing_lnum; @@ -2900,6 +2885,31 @@ int cmd_exists(const char *const name) return ea.cmdidx == CMD_SIZE ? 0 : (full ? 2 : 1); } +// "fullcommand" function +void f_fullcommand(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + exarg_T ea; + char_u *name = argvars[0].vval.v_string; + + while (name[0] != NUL && name[0] == ':') { + name++; + } + name = skip_range(name, NULL); + + rettv->v_type = VAR_STRING; + + ea.cmd = (*name == '2' || *name == '3') ? name + 1 : name; + ea.cmdidx = (cmdidx_T)0; + char_u *p = find_command(&ea, NULL); + if (p == NULL || ea.cmdidx == CMD_SIZE) { + return; + } + + rettv->vval.v_string = vim_strsave(IS_USER_CMDIDX(ea.cmdidx) + ? get_user_commands(NULL, ea.useridx) + : cmdnames[ea.cmdidx].cmd_name); +} + /// This is all pretty much copied from do_one_cmd(), with all the extra stuff /// we don't need/want deleted. Maybe this could be done better if we didn't /// repeat all this stuff. The only problem is that they may not stay @@ -4360,7 +4370,7 @@ static char_u *replace_makeprg(exarg_T *eap, char_u *p, char_u **cmdlinep) ++i; } len = (int)STRLEN(p); - new_cmdline = xmalloc(STRLEN(program) + i * (len - 2) + 1); + new_cmdline = xmalloc(STRLEN(program) + (size_t)i * (len - 2) + 1); ptr = new_cmdline; while ((pos = (char_u *)strstr((char *)program, "$*")) != NULL) { i = (int)(pos - program); @@ -5146,8 +5156,9 @@ char_u *get_command_name(expand_T *xp, int idx) return cmdnames[idx].cmd_name; } -static int uc_add_command(char_u *name, size_t name_len, char_u *rep, uint32_t argt, long def, - int flags, int compl, char_u *compl_arg, cmd_addr_T addr_type, bool force) +int uc_add_command(char_u *name, size_t name_len, char_u *rep, uint32_t argt, long def, int flags, + int compl, char_u *compl_arg, LuaRef compl_luaref, cmd_addr_T addr_type, + LuaRef luaref, bool force) FUNC_ATTR_NONNULL_ARG(1, 3) { ucmd_T *cmd = NULL; @@ -5201,6 +5212,8 @@ static int uc_add_command(char_u *name, size_t name_len, char_u *rep, uint32_t a XFREE_CLEAR(cmd->uc_rep); XFREE_CLEAR(cmd->uc_compl_arg); + NLUA_CLEAR_REF(cmd->uc_luaref); + NLUA_CLEAR_REF(cmd->uc_compl_luaref); break; } @@ -5231,13 +5244,17 @@ static int uc_add_command(char_u *name, size_t name_len, char_u *rep, uint32_t a cmd->uc_script_ctx = current_sctx; cmd->uc_script_ctx.sc_lnum += sourcing_lnum; cmd->uc_compl_arg = compl_arg; + cmd->uc_compl_luaref = compl_luaref; cmd->uc_addr_type = addr_type; + cmd->uc_luaref = luaref; return OK; fail: xfree(rep_buf); xfree(compl_arg); + NLUA_CLEAR_REF(luaref); + NLUA_CLEAR_REF(compl_luaref); return FAIL; } @@ -5276,6 +5293,7 @@ static const char *command_complete[] = [EXPAND_CSCOPE] = "cscope", [EXPAND_USER_DEFINED] = "custom", [EXPAND_USER_LIST] = "customlist", + [EXPAND_USER_LUA] = "<Lua function>", [EXPAND_DIFF_BUFFERS] = "diff_buffer", [EXPAND_DIRECTORIES] = "dir", [EXPAND_ENV_VARS] = "environment", @@ -5499,6 +5517,8 @@ static int uc_scan_attr(char_u *attr, size_t len, uint32_t *argt, long *def, int *flags |= UC_BUFFER; } else if (STRNICMP(attr, "register", len) == 0) { *argt |= EX_REGSTR; + } else if (STRNICMP(attr, "keepscript", len) == 0) { + *argt |= EX_KEEPSCRIPT; } else if (STRNICMP(attr, "bar", len) == 0) { *argt |= EX_TRLBAR; } else { @@ -5677,8 +5697,8 @@ static void ex_command(exarg_T *eap) } else if (compl > 0 && (argt & EX_EXTRA) == 0) { emsg(_(e_complete_used_without_nargs)); } else { - uc_add_command(name, end - name, p, argt, def, flags, compl, compl_arg, - addr_type_arg, eap->forceit); + uc_add_command(name, end - name, p, argt, def, flags, compl, compl_arg, LUA_NOREF, + addr_type_arg, LUA_NOREF, eap->forceit); } } @@ -5692,11 +5712,13 @@ void ex_comclear(exarg_T *eap) uc_clear(&curbuf->b_ucmds); } -static void free_ucmd(ucmd_T *cmd) +void free_ucmd(ucmd_T *cmd) { xfree(cmd->uc_name); xfree(cmd->uc_rep); xfree(cmd->uc_compl_arg); + NLUA_CLEAR_REF(cmd->uc_compl_luaref); + NLUA_CLEAR_REF(cmd->uc_luaref); } /* @@ -5734,9 +5756,7 @@ static void ex_delcommand(exarg_T *eap) return; } - xfree(cmd->uc_name); - xfree(cmd->uc_rep); - xfree(cmd->uc_compl_arg); + free_ucmd(cmd); --gap->ga_len; @@ -5818,7 +5838,7 @@ static char_u *uc_split_args(char_u *arg, size_t *lenp) return buf; } -static size_t add_cmd_modifier(char_u *buf, char *mod_str, bool *multi_mods) +static size_t add_cmd_modifier(char *buf, char *mod_str, bool *multi_mods) { size_t result = STRLEN(mod_str); if (*multi_mods) { @@ -6010,7 +6030,7 @@ static size_t uc_check_code(char_u *code, size_t len, char_u *buf, ucmd_T *cmd, break; } - case ct_MODS: { + case ct_MODS: result = quote ? 2 : 0; if (buf != NULL) { if (quote) { @@ -6019,76 +6039,13 @@ static size_t uc_check_code(char_u *code, size_t len, char_u *buf, ucmd_T *cmd, *buf = '\0'; } - bool multi_mods = false; + result += uc_mods((char *)buf); - // :aboveleft and :leftabove - if (cmdmod.split & WSP_ABOVE) { - result += add_cmd_modifier(buf, "aboveleft", &multi_mods); - } - // :belowright and :rightbelow - if (cmdmod.split & WSP_BELOW) { - result += add_cmd_modifier(buf, "belowright", &multi_mods); - } - // :botright - if (cmdmod.split & WSP_BOT) { - result += add_cmd_modifier(buf, "botright", &multi_mods); - } - - typedef struct { - bool *set; - char *name; - } mod_entry_T; - static mod_entry_T mod_entries[] = { - { &cmdmod.browse, "browse" }, - { &cmdmod.confirm, "confirm" }, - { &cmdmod.hide, "hide" }, - { &cmdmod.keepalt, "keepalt" }, - { &cmdmod.keepjumps, "keepjumps" }, - { &cmdmod.keepmarks, "keepmarks" }, - { &cmdmod.keeppatterns, "keeppatterns" }, - { &cmdmod.lockmarks, "lockmarks" }, - { &cmdmod.noswapfile, "noswapfile" } - }; - // the modifiers that are simple flags - for (size_t i = 0; i < ARRAY_SIZE(mod_entries); i++) { - if (*mod_entries[i].set) { - result += add_cmd_modifier(buf, mod_entries[i].name, &multi_mods); - } - } - - // TODO(vim): How to support :noautocmd? - // TODO(vim): How to support :sandbox? - - // :silent - if (msg_silent > 0) { - result += add_cmd_modifier(buf, emsg_silent > 0 ? "silent!" : "silent", - &multi_mods); - } - // :tab - if (cmdmod.tab > 0) { - result += add_cmd_modifier(buf, "tab", &multi_mods); - } - // :topleft - if (cmdmod.split & WSP_TOP) { - result += add_cmd_modifier(buf, "topleft", &multi_mods); - } - - // TODO(vim): How to support :unsilent? - - // :verbose - if (p_verbose > 0) { - result += add_cmd_modifier(buf, "verbose", &multi_mods); - } - // :vertical - if (cmdmod.split & WSP_VERT) { - result += add_cmd_modifier(buf, "vertical", &multi_mods); - } if (quote && buf != NULL) { buf += result - 2; *buf = '"'; } break; - } case ct_REGISTER: result = eap->regname ? 1 : 0; @@ -6127,6 +6084,76 @@ static size_t uc_check_code(char_u *code, size_t len, char_u *buf, ucmd_T *cmd, return result; } +size_t uc_mods(char *buf) +{ + size_t result = 0; + bool multi_mods = false; + + // :aboveleft and :leftabove + if (cmdmod.split & WSP_ABOVE) { + result += add_cmd_modifier(buf, "aboveleft", &multi_mods); + } + // :belowright and :rightbelow + if (cmdmod.split & WSP_BELOW) { + result += add_cmd_modifier(buf, "belowright", &multi_mods); + } + // :botright + if (cmdmod.split & WSP_BOT) { + result += add_cmd_modifier(buf, "botright", &multi_mods); + } + + typedef struct { + bool *set; + char *name; + } mod_entry_T; + static mod_entry_T mod_entries[] = { + { &cmdmod.browse, "browse" }, + { &cmdmod.confirm, "confirm" }, + { &cmdmod.hide, "hide" }, + { &cmdmod.keepalt, "keepalt" }, + { &cmdmod.keepjumps, "keepjumps" }, + { &cmdmod.keepmarks, "keepmarks" }, + { &cmdmod.keeppatterns, "keeppatterns" }, + { &cmdmod.lockmarks, "lockmarks" }, + { &cmdmod.noswapfile, "noswapfile" } + }; + // the modifiers that are simple flags + for (size_t i = 0; i < ARRAY_SIZE(mod_entries); i++) { + if (*mod_entries[i].set) { + result += add_cmd_modifier(buf, mod_entries[i].name, &multi_mods); + } + } + + // TODO(vim): How to support :noautocmd? + // TODO(vim): How to support :sandbox? + + // :silent + if (msg_silent > 0) { + result += add_cmd_modifier(buf, emsg_silent > 0 ? "silent!" : "silent", &multi_mods); + } + // :tab + if (cmdmod.tab > 0) { + result += add_cmd_modifier(buf, "tab", &multi_mods); + } + // :topleft + if (cmdmod.split & WSP_TOP) { + result += add_cmd_modifier(buf, "topleft", &multi_mods); + } + + // TODO(vim): How to support :unsilent? + + // :verbose + if (p_verbose > 0) { + result += add_cmd_modifier(buf, "verbose", &multi_mods); + } + // :vertical + if (cmdmod.split & WSP_VERT) { + result += add_cmd_modifier(buf, "vertical", &multi_mods); + } + + return result; +} + static void do_ucmd(exarg_T *eap) { char_u *buf; @@ -6149,6 +6176,11 @@ static void do_ucmd(exarg_T *eap) cmd = USER_CMD_GA(&curbuf->b_ucmds, eap->useridx); } + if (cmd->uc_luaref > 0) { + nlua_do_ucmd(cmd, eap); + return; + } + /* * Replace <> in the command by the arguments. * First round: "buf" is NULL, compute length, allocate "buf". @@ -6174,7 +6206,6 @@ static void do_ucmd(exarg_T *eap) // K_SPECIAL has been put in the buffer as K_SPECIAL // KS_SPECIAL KE_FILLER, like for mappings, but // do_cmdline() doesn't handle that, so convert it back. - // Also change K_SPECIAL KS_EXTRA KE_CSI into CSI. len = ksp - p; if (len > 0) { memmove(q, p, len); @@ -6227,10 +6258,14 @@ static void do_ucmd(exarg_T *eap) buf = xmalloc(totlen + 1); } - current_sctx.sc_sid = cmd->uc_script_ctx.sc_sid; + if ((cmd->uc_argt & EX_KEEPSCRIPT) == 0) { + current_sctx.sc_sid = cmd->uc_script_ctx.sc_sid; + } (void)do_cmdline(buf, eap->getline, eap->cookie, DOCMD_VERBOSE|DOCMD_NOWAIT|DOCMD_KEYTYPED); - current_sctx = save_current_sctx; + if ((cmd->uc_argt & EX_KEEPSCRIPT) == 0) { + current_sctx = save_current_sctx; + } xfree(buf); xfree(split_buf); } @@ -6276,7 +6311,7 @@ char_u *get_user_cmd_flags(expand_T *xp, int idx) { static char *user_cmd_flags[] = { "addr", "bang", "bar", "buffer", "complete", "count", - "nargs", "range", "register" }; + "nargs", "range", "register", "keepscript" }; if (idx >= (int)ARRAY_SIZE(user_cmd_flags)) { return NULL; @@ -7464,9 +7499,8 @@ void do_exedit(exarg_T *eap, win_T *old_curwin) if ((eap->cmdidx == CMD_new || eap->cmdidx == CMD_tabnew || eap->cmdidx == CMD_tabedit - || eap->cmdidx == CMD_vnew - ) && *eap->arg == NUL) { - // ":new" or ":tabnew" without argument: edit an new empty buffer + || eap->cmdidx == CMD_vnew) && *eap->arg == NUL) { + // ":new" or ":tabnew" without argument: edit a new empty buffer setpcmark(); (void)do_ecmd(0, NULL, NULL, eap, ECMD_ONE, ECMD_HIDE + (eap->forceit ? ECMD_FORCEIT : 0), @@ -7807,17 +7841,21 @@ bool changedir_func(char_u *new_dir, CdScope scope) prev_dir = pdir; } + // For UNIX ":cd" means: go to home directory. + // On other systems too if 'cdhome' is set. #if defined(UNIX) - // On Unix ":cd" means: go to home directory. if (*new_dir == NUL) { +#else + if (*new_dir == NUL && p_cdh) { +#endif // Use NameBuff for home directory name. expand_env((char_u *)"$HOME", NameBuff, MAXPATHL); new_dir = NameBuff; } -#endif - if (vim_chdir(new_dir) == 0) { - bool dir_differs = pdir == NULL || pathcmp((char *)pdir, (char *)new_dir, -1) != 0; + bool dir_differs = new_dir == NULL || pdir == NULL + || pathcmp((char *)pdir, (char *)new_dir, -1) != 0; + if (new_dir != NULL && (!dir_differs || vim_chdir(new_dir) == 0)) { post_chdir(scope, dir_differs); retval = true; } else { @@ -7833,9 +7871,9 @@ void ex_cd(exarg_T *eap) { char_u *new_dir; new_dir = eap->arg; -#if !defined(UNIX) && !defined(VMS) - // for non-UNIX ":cd" means: print current directory - if (*new_dir == NUL) { +#if !defined(UNIX) + // for non-UNIX ":cd" means: print current directory unless 'cdhome' is set + if (*new_dir == NUL && !p_cdh) { ex_pwd(NULL); } else #endif @@ -8594,7 +8632,7 @@ static void ex_normal(exarg_T *eap) return; } - // vgetc() expects a CSI and K_SPECIAL to have been escaped. Don't do + // vgetc() expects K_SPECIAL to have been escaped. Don't do // this for the K_SPECIAL leading byte, otherwise special keys will not // work. { @@ -8603,8 +8641,7 @@ static void ex_normal(exarg_T *eap) // Count the number of characters to be escaped. for (p = eap->arg; *p != NUL; p++) { for (l = utfc_ptr2len(p) - 1; l > 0; l--) { - if (*++p == K_SPECIAL // trailbyte K_SPECIAL or CSI - ) { + if (*++p == K_SPECIAL) { // trailbyte K_SPECIAL len += 2; } } @@ -9513,23 +9550,34 @@ static void ex_filetype(exarg_T *eap) } } -/// Set all :filetype options ON if user did not explicitly set any to OFF. -void filetype_maybe_enable(void) +/// Source ftplugin.vim and indent.vim to create the necessary FileType +/// autocommands. We do this separately from filetype.vim so that these +/// autocommands will always fire first (and thus can be overriden) while still +/// allowing general filetype detection to be disabled in the user's init file. +void filetype_plugin_enable(void) { - if (filetype_detect == kNone) { - source_runtime(FILETYPE_FILE, true); - filetype_detect = kTrue; - } if (filetype_plugin == kNone) { - source_runtime(FTPLUGIN_FILE, true); + source_runtime(FTPLUGIN_FILE, DIP_ALL); filetype_plugin = kTrue; } if (filetype_indent == kNone) { - source_runtime(INDENT_FILE, true); + source_runtime(INDENT_FILE, DIP_ALL); filetype_indent = kTrue; } } +/// Enable filetype detection if the user did not explicitly disable it. +void filetype_maybe_enable(void) +{ + if (filetype_detect == kNone) { + // Normally .vim files are sourced before .lua files when both are + // supported, but we reverse the order here because we want the Lua + // autocommand to be defined first so that it runs first + source_runtime(FILETYPE_FILE, DIP_ALL); + filetype_detect = kTrue; + } +} + /// ":setfiletype [FALLBACK] {name}" static void ex_setfiletype(exarg_T *eap) { @@ -9802,6 +9850,7 @@ Dictionary commands_array(buf_T *buf) PUT(d, "bang", BOOLEAN_OBJ(!!(cmd->uc_argt & EX_BANG))); PUT(d, "bar", BOOLEAN_OBJ(!!(cmd->uc_argt & EX_TRLBAR))); PUT(d, "register", BOOLEAN_OBJ(!!(cmd->uc_argt & EX_REGSTR))); + PUT(d, "keepscript", BOOLEAN_OBJ(!!(cmd->uc_argt & EX_KEEPSCRIPT))); switch (cmd->uc_argt & (EX_EXTRA | EX_NOSPC | EX_NEEDARG)) { case 0: |