aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/ex_docmd.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/ex_docmd.c')
-rw-r--r--src/nvim/ex_docmd.c1308
1 files changed, 615 insertions, 693 deletions
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index 34c3b3889e..cbfe6e3789 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -38,17 +38,19 @@
#include "nvim/getchar.h"
#include "nvim/globals.h"
#include "nvim/hardcopy.h"
+#include "nvim/highlight_group.h"
#include "nvim/if_cscope.h"
+#include "nvim/input.h"
#include "nvim/keymap.h"
#include "nvim/lua/executor.h"
#include "nvim/main.h"
+#include "nvim/match.h"
#include "nvim/mark.h"
#include "nvim/mbyte.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/menu.h"
#include "nvim/message.h"
-#include "nvim/misc1.h"
#include "nvim/mouse.h"
#include "nvim/move.h"
#include "nvim/normal.h"
@@ -67,6 +69,7 @@
#include "nvim/sign.h"
#include "nvim/spell.h"
#include "nvim/spellfile.h"
+#include "nvim/state.h"
#include "nvim/strings.h"
#include "nvim/syntax.h"
#include "nvim/tag.h"
@@ -77,26 +80,14 @@
#include "nvim/vim.h"
#include "nvim/window.h"
+static char *e_no_such_user_defined_command_str = N_("E184: No such user-defined command: %s");
+static char *e_no_such_user_defined_command_in_current_buffer_str
+ = N_("E1237: No such user-defined command in current buffer: %s");
+
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)
@@ -196,6 +187,7 @@ void do_exmode(void)
exmode_active = true;
State = NORMAL;
+ may_trigger_modechanged();
// When using ":global /pat/ visual" and then "Q" we return to continue
// the :global command.
@@ -259,8 +251,9 @@ void do_exmode(void)
msg_scroll = save_msg_scroll;
}
-// Print the executed command for when 'verbose' is set.
-// When "lnum" is 0 only print the command.
+/// Print the executed command for when 'verbose' is set.
+///
+/// @param lnum if 0, only print the command.
static void msg_verbose_cmd(linenr_T lnum, char_u *cmd)
FUNC_ATTR_NONNULL_ALL
{
@@ -280,9 +273,7 @@ static void msg_verbose_cmd(linenr_T lnum, char_u *cmd)
no_wait_return--;
}
-/*
- * Execute a simple command line. Used for translated commands like "*".
- */
+/// Execute a simple command line. Used for translated commands like "*".
int do_cmdline_cmd(const char *cmd)
{
return do_cmdline((char_u *)cmd, NULL, NULL,
@@ -434,7 +425,7 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, void *cookie, int flags)
&& cstack.cs_idx < 0
&& !(getline_is_func
&& func_has_abort(real_cookie))) {
- did_emsg = FALSE;
+ did_emsg = false;
}
/*
@@ -818,7 +809,7 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, void *cookie, int flags)
// of interrupts or errors to exceptions, and ensure that no more
// commands are executed.
if (current_exception) {
- void *p = NULL;
+ char *p = NULL;
char_u *saved_sourcing_name;
int saved_sourcing_lnum;
struct msglist *messages = NULL;
@@ -835,7 +826,7 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, void *cookie, int flags)
vim_snprintf((char *)IObuff, IOSIZE,
_("E605: Exception not caught: %s"),
current_exception->value);
- p = vim_strsave(IObuff);
+ p = (char *)vim_strsave(IObuff);
break;
case ET_ERROR:
messages = current_exception->messages;
@@ -961,9 +952,7 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, void *cookie, int flags)
return retval;
}
-/*
- * Obtain a line when inside a ":while" or ":for" loop.
- */
+/// Obtain a line when inside a ":while" or ":for" loop.
static char_u *get_loop_line(int c, void *cookie, int indent, bool do_concat)
{
struct loop_cookie *cp = (struct loop_cookie *)cookie;
@@ -995,9 +984,7 @@ static char_u *get_loop_line(int c, void *cookie, int indent, bool do_concat)
return vim_strsave(wp->line);
}
-/*
- * Store a line in "gap" so that a ":while" loop can execute it again.
- */
+/// Store a line in "gap" so that a ":while" loop can execute it again.
static void store_loop_line(garray_T *gap, char_u *line)
{
wcmd_T *p = GA_APPEND_VIA_PTR(wcmd_T, gap);
@@ -1047,11 +1034,10 @@ void *getline_cookie(LineGetter fgetline, void *cookie)
return cp;
}
-/*
- * Helper function to apply an offset for buffer commands, i.e. ":bdelete",
- * ":bwipeout", etc.
- * Returns the buffer number.
- */
+/// Helper function to apply an offset for buffer commands, i.e. ":bdelete",
+/// ":bwipeout", etc.
+///
+/// @return the buffer number.
static int compute_buffer_local_count(int addr_type, int lnum, int offset)
{
buf_T *buf;
@@ -1093,8 +1079,8 @@ static int compute_buffer_local_count(int addr_type, int lnum, int offset)
return buf->b_fnum;
}
-// Return the window number of "win".
-// When "win" is NULL return the number of windows.
+/// @return the window number of "win" or,
+/// the number of windows if "win" is NULL
static int current_win_nr(const win_T *win)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
@@ -1262,6 +1248,7 @@ static char_u *do_one_cmd(char_u **cmdlinep, int flags, cstack_T *cstack, LineGe
const int save_msg_scroll = msg_scroll;
cmdmod_T save_cmdmod;
const int save_reg_executing = reg_executing;
+ const bool save_pending_end_reg_executing = pending_end_reg_executing;
char_u *cmd;
memset(&ea, 0, sizeof(ea));
@@ -1772,7 +1759,9 @@ static char_u *do_one_cmd(char_u **cmdlinep, int flags, cstack_T *cstack, LineGe
ea.regname = *ea.arg++;
// for '=' register: accept the rest of the line as an expression
if (ea.arg[-1] == '=' && ea.arg[0] != NUL) {
- set_expr_line(vim_strsave(ea.arg));
+ if (!ea.skip) {
+ set_expr_line(vim_strsave(ea.arg));
+ }
ea.arg += STRLEN(ea.arg);
}
ea.arg = skipwhite(ea.arg);
@@ -2030,6 +2019,7 @@ doend:
undo_cmdmod(&ea, save_msg_scroll);
cmdmod = save_cmdmod;
reg_executing = save_reg_executing;
+ pending_end_reg_executing = save_pending_end_reg_executing;
if (ea.did_sandbox) {
sandbox--;
@@ -2044,18 +2034,32 @@ doend:
return ea.nextcmd;
}
-// Parse and skip over command modifiers:
-// - update eap->cmd
-// - store flags in "cmdmod".
-// - Set ex_pressedreturn for an empty command line.
-// - set msg_silent for ":silent"
-// - set 'eventignore' to "all" for ":noautocmd"
-// - set p_verbose for ":verbose"
-// - Increment "sandbox" for ":sandbox"
-// When "skip_only" is true the global variables are not changed, except for
-// "cmdmod".
-// Return FAIL when the command is not to be executed.
-// May set "errormsg" to an error message.
+static char ex_error_buf[MSG_BUF_LEN];
+
+/// @return an error message with argument included.
+/// Uses a static buffer, only the last error will be kept.
+/// "msg" will be translated, caller should use N_().
+char *ex_errmsg(const char *const msg, const char_u *const arg)
+ FUNC_ATTR_NONNULL_ALL
+{
+ vim_snprintf(ex_error_buf, MSG_BUF_LEN, _(msg), arg);
+ return ex_error_buf;
+}
+
+/// Parse and skip over command modifiers:
+/// - update eap->cmd
+/// - store flags in "cmdmod".
+/// - Set ex_pressedreturn for an empty command line.
+/// - set msg_silent for ":silent"
+/// - set 'eventignore' to "all" for ":noautocmd"
+/// - set p_verbose for ":verbose"
+/// - Increment "sandbox" for ":sandbox"
+///
+/// @param skip_only if true, the global variables are not changed, except for
+/// "cmdmod".
+/// @param[out] errormsg potential error message.
+///
+/// @return FAIL when the command is not to be executed.
int parse_command_modifiers(exarg_T *eap, char **errormsg, bool skip_only)
{
char_u *p;
@@ -2314,7 +2318,7 @@ int parse_command_modifiers(exarg_T *eap, char **errormsg, bool skip_only)
return OK;
}
-// Undo and free contents of "cmdmod".
+/// Undo and free contents of "cmdmod".
static void undo_cmdmod(const exarg_T *eap, int save_msg_scroll)
FUNC_ATTR_NONNULL_ALL
{
@@ -2353,9 +2357,10 @@ static void undo_cmdmod(const exarg_T *eap, int save_msg_scroll)
}
-// Parse the address range, if any, in "eap".
-// May set the last search pattern, unless "silent" is true.
-// Return FAIL and set "errormsg" or return OK.
+/// Parse the address range, if any, in "eap".
+/// May set the last search pattern, unless "silent" is true.
+///
+/// @return FAIL and set "errormsg" or return OK.
int parse_cmd_address(exarg_T *eap, char **errormsg, bool silent)
FUNC_ATTR_NONNULL_ALL
{
@@ -2547,11 +2552,9 @@ int checkforcmd(char_u **pp, char *cmd, int len)
return FALSE;
}
-/*
- * Append "cmd" to the error message in IObuff.
- * Takes care of limiting the length and handling 0xa0, which would be
- * invisible otherwise.
- */
+/// Append "cmd" to the error message in IObuff.
+/// Takes care of limiting the length and handling 0xa0, which would be
+/// invisible otherwise.
static void append_command(char_u *cmd)
{
char_u *s = cmd;
@@ -2571,11 +2574,12 @@ static void append_command(char_u *cmd)
*d = NUL;
}
-// Find an Ex command by its name, either built-in or user.
-// Start of the name can be found at eap->cmd.
-// Sets eap->cmdidx and returns a pointer to char after the command name.
-// "full" is set to TRUE if the whole command name matched.
-// Returns NULL for an ambiguous user command.
+/// Find an Ex command by its name, either built-in or user.
+/// Start of the name can be found at eap->cmd.
+/// Sets eap->cmdidx and returns a pointer to char after the command name.
+/// "full" is set to TRUE if the whole command name matched.
+///
+/// @return NULL for an ambiguous user command.
static char_u *find_command(exarg_T *eap, int *full)
FUNC_ATTR_NONNULL_ARG(1)
{
@@ -2711,12 +2715,10 @@ static char_u *find_ucmd(exarg_T *eap, char_u *p, int *full, expand_T *xp, int *
bool amb_local = false; // Found ambiguous buffer-local command,
// only full match global is accepted.
- /*
- * Look for buffer-local user commands first, then global ones.
- */
- gap = &curbuf->b_ucmds;
+ // Look for buffer-local user commands first, then global ones.
+ gap = &prevwin_curwin()->w_buffer->b_ucmds;
for (;;) {
- for (j = 0; j < gap->ga_len; ++j) {
+ for (j = 0; j < gap->ga_len; j++) {
uc = USER_CMD_GA(gap, j);
cp = eap->cmd;
np = uc->uc_name;
@@ -2759,6 +2761,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;
@@ -2830,10 +2833,8 @@ static struct cmdmod {
{ "vertical", 4, false },
};
-/*
- * Return length of a command modifier (including optional count).
- * Return zero when it's not a modifier.
- */
+/// @return length of a command modifier (including optional count) or,
+/// zero when it's not a modifier.
int modifier_len(char_u *cmd)
{
char_u *p = cmd;
@@ -2844,7 +2845,7 @@ int modifier_len(char_u *cmd)
for (int i = 0; i < (int)ARRAY_SIZE(cmdmods); i++) {
int j;
for (j = 0; p[j] != NUL; j++) {
- if (p[j] != cmdmods[i].name[j]) {
+ if (p[j] != (char_u)cmdmods[i].name[j]) {
break;
}
}
@@ -2857,11 +2858,9 @@ int modifier_len(char_u *cmd)
return 0;
}
-/*
- * Return > 0 if an Ex command "name" exists.
- * Return 2 if there is an exact match.
- * Return 3 if there is an ambiguous match.
- */
+/// @return > 0 if an Ex command "name" exists or,
+/// 2 if there is an exact match or,
+/// 3 if there is an ambiguous match.
int cmd_exists(const char *const name)
{
exarg_T ea;
@@ -2898,6 +2897,35 @@ 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;
+
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = NULL;
+ if (name == NULL) {
+ return;
+ }
+
+ while (*name == ':') {
+ name++;
+ }
+ name = skip_range(name, NULL);
+
+ 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_command_name(ea.useridx, ea.cmdidx)
+ : 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
@@ -4001,8 +4029,9 @@ static linenr_T get_address(exarg_T *eap, char_u **ptr, cmd_addr_T addr_type, in
// When '/' or '?' follows another address, start from
// there.
- if (lnum != MAXLNUM) {
- curwin->w_cursor.lnum = lnum;
+ if (lnum > 0 && lnum != MAXLNUM) {
+ curwin->w_cursor.lnum
+ = lnum > curbuf->b_ml.ml_line_count ? curbuf->b_ml.ml_line_count : lnum;
}
// Start a forward search at the end of the line (unless
@@ -4121,7 +4150,11 @@ static linenr_T get_address(exarg_T *eap, char_u **ptr, cmd_addr_T addr_type, in
if (!ascii_isdigit(*cmd)) { // '+' is '+1', but '+0' is not '+1'
n = 1;
} else {
- n = getdigits(&cmd, true, 0);
+ n = getdigits(&cmd, false, MAXLNUM);
+ if (n == MAXLNUM) {
+ emsg(_(e_line_number_out_of_range));
+ goto error;
+ }
}
if (addr_type == ADDR_TABS_RELATIVE) {
@@ -4140,6 +4173,10 @@ static linenr_T get_address(exarg_T *eap, char_u **ptr, cmd_addr_T addr_type, in
if (i == '-') {
lnum -= n;
} else {
+ if (n >= LONG_MAX - lnum) {
+ emsg(_(e_line_number_out_of_range));
+ goto error;
+ }
lnum += n;
}
}
@@ -4151,9 +4188,7 @@ error:
return lnum;
}
-/*
- * Get flags from an Ex command argument.
- */
+/// Get flags from an Ex command argument.
static void get_flags(exarg_T *eap)
{
while (vim_strchr((char_u *)"lp#", *eap->arg) != NULL) {
@@ -4188,10 +4223,9 @@ static void ex_script_ni(exarg_T *eap)
}
}
-/*
- * Check range in Ex command for validity.
- * Return NULL when valid, error message when invalid.
- */
+/// Check range in Ex command for validity.
+///
+/// @return NULL when valid, error message when invalid.
static char *invalid_range(exarg_T *eap)
{
buf_T *buf;
@@ -4277,9 +4311,7 @@ static char *invalid_range(exarg_T *eap)
return NULL;
}
-/*
- * Correct the range for zero line number, if required.
- */
+/// Correct the range for zero line number, if required.
static void correct_range(exarg_T *eap)
{
if (!(eap->argt & EX_ZEROR)) { // zero in range not allowed
@@ -4293,10 +4325,8 @@ static void correct_range(exarg_T *eap)
}
-/*
- * For a ":vimgrep" or ":vimgrepadd" command return a pointer past the
- * pattern. Otherwise return eap->arg.
- */
+/// For a ":vimgrep" or ":vimgrepadd" command return a pointer past the
+/// pattern. Otherwise return eap->arg.
static char_u *skip_grep_pat(exarg_T *eap)
{
char_u *p = eap->arg;
@@ -4313,10 +4343,8 @@ static char_u *skip_grep_pat(exarg_T *eap)
return p;
}
-/*
- * For the ":make" and ":grep" commands insert the 'makeprg'/'grepprg' option
- * in the command line, so that things like % get expanded.
- */
+/// For the ":make" and ":grep" commands insert the 'makeprg'/'grepprg' option
+/// in the command line, so that things like % get expanded.
static char_u *replace_makeprg(exarg_T *eap, char_u *p, char_u **cmdlinep)
{
char_u *new_cmdline;
@@ -4358,7 +4386,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);
@@ -4384,9 +4412,10 @@ static char_u *replace_makeprg(exarg_T *eap, char_u *p, char_u **cmdlinep)
return p;
}
-// Expand file name in Ex command argument.
-// When an error is detected, "errormsgp" is set to a non-NULL pointer.
-// Return FAIL for failure, OK otherwise.
+/// Expand file name in Ex command argument.
+/// When an error is detected, "errormsgp" is set to a non-NULL pointer.
+///
+/// @return FAIL for failure, OK otherwise.
int expand_filename(exarg_T *eap, char_u **cmdlinep, char **errormsgp)
{
int has_wildcards; // need to expand wildcards
@@ -4554,13 +4583,12 @@ int expand_filename(exarg_T *eap, char_u **cmdlinep, char **errormsgp)
return OK;
}
-/*
- * Replace part of the command line, keeping eap->cmd, eap->arg and
- * eap->nextcmd correct.
- * "src" points to the part that is to be replaced, of length "srclen".
- * "repl" is the replacement string.
- * Returns a pointer to the character after the replaced string.
- */
+/// Replace part of the command line, keeping eap->cmd, eap->arg and
+/// eap->nextcmd correct.
+/// "src" points to the part that is to be replaced, of length "srclen".
+/// "repl" is the replacement string.
+///
+/// @return a pointer to the character after the replaced string.
static char_u *repl_cmdline(exarg_T *eap, char_u *src, size_t srclen, char_u *repl,
char_u **cmdlinep)
{
@@ -4606,9 +4634,7 @@ static char_u *repl_cmdline(exarg_T *eap, char_u *src, size_t srclen, char_u *re
return src;
}
-/*
- * Check for '|' to separate commands and '"' to start comments.
- */
+/// Check for '|' to separate commands and '"' to start comments.
void separate_nextcmd(exarg_T *eap)
{
char_u *p;
@@ -4663,9 +4689,7 @@ void separate_nextcmd(exarg_T *eap)
}
}
-/*
- * get + command from ex argument
- */
+/// get + command from ex argument
static char_u *getargcmd(char_u **argp)
{
char_u *arg = *argp;
@@ -4722,10 +4746,9 @@ int get_bad_opt(const char_u *p, exarg_T *eap)
return OK;
}
-/*
- * Get "++opt=arg" argument.
- * Return FAIL or OK.
- */
+/// Get "++opt=arg" argument.
+///
+/// @return FAIL or OK.
static int getargopt(exarg_T *eap)
{
char_u *arg = eap->arg + 2;
@@ -4805,8 +4828,9 @@ static int getargopt(exarg_T *eap)
}
/// Handle the argument for a tabpage related ex command.
-/// Returns a tabpage number.
/// When an error is encountered then eap->errmsg is set.
+///
+/// @return a tabpage number.
static int get_tabpage_arg(exarg_T *eap)
{
int tab_number = 0;
@@ -4833,7 +4857,13 @@ static int get_tabpage_arg(exarg_T *eap)
if (STRCMP(p, "$") == 0) {
tab_number = LAST_TAB_NR;
} else if (STRCMP(p, "#") == 0) {
- tab_number = tabpage_index(lastused_tabpage);
+ if (valid_tabpage(lastused_tabpage)) {
+ tab_number = tabpage_index(lastused_tabpage);
+ } else {
+ eap->errmsg = ex_errmsg(e_invargval, eap->arg);
+ tab_number = 0;
+ goto theend;
+ }
} else if (p == p_save || *p_save == '-' || *p != NUL
|| tab_number > LAST_TAB_NR) {
// No numbers as argument.
@@ -4889,17 +4919,13 @@ theend:
return tab_number;
}
-/*
- * ":abbreviate" and friends.
- */
+/// ":abbreviate" and friends.
static void ex_abbreviate(exarg_T *eap)
{
do_exmap(eap, TRUE); // almost the same as mapping
}
-/*
- * ":map" and friends.
- */
+/// ":map" and friends.
static void ex_map(exarg_T *eap)
{
/*
@@ -4914,25 +4940,19 @@ static void ex_map(exarg_T *eap)
do_exmap(eap, FALSE);
}
-/*
- * ":unmap" and friends.
- */
+/// ":unmap" and friends.
static void ex_unmap(exarg_T *eap)
{
do_exmap(eap, FALSE);
}
-/*
- * ":mapclear" and friends.
- */
+/// ":mapclear" and friends.
static void ex_mapclear(exarg_T *eap)
{
map_clear_mode(eap->cmd, eap->arg, eap->forceit, false);
}
-/*
- * ":abclear" and friends.
- */
+/// ":abclear" and friends.
static void ex_abclear(exarg_T *eap)
{
map_clear_mode(eap->cmd, eap->arg, true, true);
@@ -4952,9 +4972,7 @@ static void ex_autocmd(exarg_T *eap)
}
}
-/*
- * ":doautocmd": Apply the automatic commands to the current buffer.
- */
+/// ":doautocmd": Apply the automatic commands to the current buffer.
static void ex_doautocmd(exarg_T *eap)
{
char_u *arg = eap->arg;
@@ -4968,11 +4986,9 @@ static void ex_doautocmd(exarg_T *eap)
}
}
-/*
- * :[N]bunload[!] [N] [bufname] unload buffer
- * :[N]bdelete[!] [N] [bufname] delete buffer from buffer list
- * :[N]bwipeout[!] [N] [bufname] delete buffer really
- */
+/// :[N]bunload[!] [N] [bufname] unload buffer
+/// :[N]bdelete[!] [N] [bufname] delete buffer from buffer list
+/// :[N]bwipeout[!] [N] [bufname] delete buffer really
static void ex_bunload(exarg_T *eap)
{
eap->errmsg = do_bufdel(eap->cmdidx == CMD_bdelete ? DOBUF_DEL
@@ -4982,10 +4998,8 @@ static void ex_bunload(exarg_T *eap)
eap->addr_count, (int)eap->line1, (int)eap->line2, eap->forceit);
}
-/*
- * :[N]buffer [N] to buffer N
- * :[N]sbuffer [N] to buffer N
- */
+/// :[N]buffer [N] to buffer N
+/// :[N]sbuffer [N] to buffer N
static void ex_buffer(exarg_T *eap)
{
if (*eap->arg) {
@@ -5002,10 +5016,8 @@ static void ex_buffer(exarg_T *eap)
}
}
-/*
- * :[N]bmodified [N] to next mod. buffer
- * :[N]sbmodified [N] to next mod. buffer
- */
+/// :[N]bmodified [N] to next mod. buffer
+/// :[N]sbmodified [N] to next mod. buffer
static void ex_bmodified(exarg_T *eap)
{
goto_buffer(eap, DOBUF_MOD, FORWARD, (int)eap->line2);
@@ -5014,10 +5026,8 @@ static void ex_bmodified(exarg_T *eap)
}
}
-/*
- * :[N]bnext [N] to next buffer
- * :[N]sbnext [N] split and to next buffer
- */
+/// :[N]bnext [N] to next buffer
+/// :[N]sbnext [N] split and to next buffer
static void ex_bnext(exarg_T *eap)
{
goto_buffer(eap, DOBUF_CURRENT, FORWARD, (int)eap->line2);
@@ -5026,12 +5036,10 @@ static void ex_bnext(exarg_T *eap)
}
}
-/*
- * :[N]bNext [N] to previous buffer
- * :[N]bprevious [N] to previous buffer
- * :[N]sbNext [N] split and to previous buffer
- * :[N]sbprevious [N] split and to previous buffer
- */
+/// :[N]bNext [N] to previous buffer
+/// :[N]bprevious [N] to previous buffer
+/// :[N]sbNext [N] split and to previous buffer
+/// :[N]sbprevious [N] split and to previous buffer
static void ex_bprevious(exarg_T *eap)
{
goto_buffer(eap, DOBUF_CURRENT, BACKWARD, (int)eap->line2);
@@ -5040,12 +5048,10 @@ static void ex_bprevious(exarg_T *eap)
}
}
-/*
- * :brewind to first buffer
- * :bfirst to first buffer
- * :sbrewind split and to first buffer
- * :sbfirst split and to first buffer
- */
+/// :brewind to first buffer
+/// :bfirst to first buffer
+/// :sbrewind split and to first buffer
+/// :sbfirst split and to first buffer
static void ex_brewind(exarg_T *eap)
{
goto_buffer(eap, DOBUF_FIRST, FORWARD, 0);
@@ -5054,10 +5060,8 @@ static void ex_brewind(exarg_T *eap)
}
}
-/*
- * :blast to last buffer
- * :sblast split and to last buffer
- */
+/// :blast to last buffer
+/// :sblast split and to last buffer
static void ex_blast(exarg_T *eap)
{
goto_buffer(eap, DOBUF_LAST, BACKWARD, 0);
@@ -5071,10 +5075,8 @@ int ends_excmd(int c) FUNC_ATTR_CONST
return c == NUL || c == '|' || c == '"' || c == '\n';
}
-/*
- * Return the next command, after the first '|' or '\n'.
- * Return NULL if not found.
- */
+/// @return the next command, after the first '|' or '\n' or,
+/// NULL if not found.
char_u *find_nextcmd(const char_u *p)
{
while (*p != '|' && *p != '\n') {
@@ -5087,7 +5089,8 @@ char_u *find_nextcmd(const char_u *p)
}
/// Check if *p is a separator between Ex commands, skipping over white space.
-/// Return NULL if it isn't, the following character if it is.
+///
+/// @return NULL if it isn't, the following character if it is.
char_u *check_nextcmd(char_u *p)
{
char_u *s = skipwhite(p);
@@ -5103,9 +5106,10 @@ char_u *check_nextcmd(char_u *p)
/// - and this is the last window
/// - and forceit not used
/// - and not repeated twice on a row
-/// @return FAIL and give error message if 'message' TRUE, return OK otherwise
///
/// @param message when FALSE check only, no messages
+///
+/// @return FAIL and give error message if 'message' TRUE, return OK otherwise
static int check_more(int message, bool forceit)
{
int n = ARGCOUNT - curwin->w_arg_idx - 1;
@@ -5133,19 +5137,36 @@ static int check_more(int message, bool forceit)
return OK;
}
-/*
- * Function given to ExpandGeneric() to obtain the list of command names.
- */
+/// Function given to ExpandGeneric() to obtain the list of command names.
char_u *get_command_name(expand_T *xp, int idx)
{
if (idx >= CMD_SIZE) {
- return get_user_command_name(idx);
+ return expand_user_command_name(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)
+/// Check for a valid user command name
+///
+/// If the given {name} is valid, then a pointer to the end of the valid name is returned.
+/// Otherwise, returns NULL.
+char *uc_validate_name(char *name)
+{
+ if (ASCII_ISALPHA(*name)) {
+ while (ASCII_ISALNUM(*name)) {
+ name++;
+ }
+ }
+ if (!ends_excmd(*name) && !ascii_iswhite(*name)) {
+ return NULL;
+ }
+
+ return name;
+}
+
+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;
@@ -5199,6 +5220,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;
}
@@ -5228,14 +5251,19 @@ static int uc_add_command(char_u *name, size_t name_len, char_u *rep, uint32_t a
cmd->uc_compl = compl;
cmd->uc_script_ctx = current_sctx;
cmd->uc_script_ctx.sc_lnum += sourcing_lnum;
+ nlua_set_sctx(&cmd->uc_script_ctx);
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;
}
@@ -5274,6 +5302,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",
@@ -5323,11 +5352,9 @@ static void uc_list(char_u *name, size_t name_len)
uint32_t a;
// In cmdwin, the alternative buffer should be used.
- garray_T *gap = (cmdwin_type != 0 && get_cmdline_type() == NUL)
- ? &prevwin->w_buffer->b_ucmds
- : &curbuf->b_ucmds;
+ const garray_T *gap = &prevwin_curwin()->w_buffer->b_ucmds;
for (;;) {
- for (i = 0; i < gap->ga_len; ++i) {
+ for (i = 0; i < gap->ga_len; i++) {
cmd = USER_CMD_GA(gap, i);
a = cmd->uc_argt;
@@ -5497,6 +5524,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 {
@@ -5619,9 +5648,7 @@ invalid_count:
static char e_complete_used_without_nargs[] = N_("E1208: -complete used without -nargs");
-/*
- * ":command ..."
- */
+/// ":command ..."
static void ex_command(exarg_T *eap)
{
char_u *name;
@@ -5651,23 +5678,18 @@ static void ex_command(exarg_T *eap)
// Get the name (if any) and skip to the following argument.
name = p;
- if (ASCII_ISALPHA(*p)) {
- while (ASCII_ISALNUM(*p)) {
- p++;
- }
- }
- if (!ends_excmd(*p) && !ascii_iswhite(*p)) {
+ end = (char_u *)uc_validate_name((char *)name);
+ if (!end) {
emsg(_("E182: Invalid command name"));
return;
}
- end = p;
- name_len = (int)(end - name);
+ name_len = (size_t)(end - name);
// If there is nothing after the name, and no attributes were specified,
// we are listing commands
p = skipwhite(end);
if (!has_attr && ends_excmd(*p)) {
- uc_list(name, end - name);
+ uc_list(name, name_len);
} else if (!ASCII_ISUPPER(*name)) {
emsg(_("E183: User defined commands must start with an uppercase letter"));
} else if (name_len <= 4 && STRNCMP(name, "Next", name_len) == 0) {
@@ -5675,31 +5697,29 @@ 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, name_len, p, argt, def, flags, compl, compl_arg, LUA_NOREF,
+ addr_type_arg, LUA_NOREF, eap->forceit);
}
}
-/*
- * ":comclear"
- * Clear all user commands, global and for current buffer.
- */
+/// ":comclear"
+/// Clear all user commands, global and for current buffer.
void ex_comclear(exarg_T *eap)
{
uc_clear(&ucmds);
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);
}
-/*
- * Clear all user commands for "gap".
- */
+/// Clear all user commands for "gap".
void uc_clear(garray_T *gap)
{
GA_DEEP_CLEAR(gap, ucmd_T, free_ucmd);
@@ -5709,32 +5729,40 @@ static void ex_delcommand(exarg_T *eap)
{
int i = 0;
ucmd_T *cmd = NULL;
- int cmp = -1;
+ int res = -1;
garray_T *gap;
+ const char_u *arg = eap->arg;
+ bool buffer_only = false;
+
+ if (STRNCMP(arg, "-buffer", 7) == 0 && ascii_iswhite(arg[7])) {
+ buffer_only = true;
+ arg = skipwhite(arg + 7);
+ }
gap = &curbuf->b_ucmds;
for (;;) {
- for (i = 0; i < gap->ga_len; ++i) {
+ for (i = 0; i < gap->ga_len; i++) {
cmd = USER_CMD_GA(gap, i);
- cmp = STRCMP(eap->arg, cmd->uc_name);
- if (cmp <= 0) {
+ res = STRCMP(arg, cmd->uc_name);
+ if (res <= 0) {
break;
}
}
- if (gap == &ucmds || cmp == 0) {
+ if (gap == &ucmds || res == 0 || buffer_only) {
break;
}
gap = &ucmds;
}
- if (cmp != 0) {
- semsg(_("E184: No such user-defined command: %s"), eap->arg);
+ if (res != 0) {
+ semsg(_(buffer_only
+ ? e_no_such_user_defined_command_in_current_buffer_str
+ : e_no_such_user_defined_command_str),
+ arg);
return;
}
- xfree(cmd->uc_name);
- xfree(cmd->uc_rep);
- xfree(cmd->uc_compl_arg);
+ free_ucmd(cmd);
--gap->ga_len;
@@ -5743,9 +5771,50 @@ static void ex_delcommand(exarg_T *eap)
}
}
-/*
- * split and quote args for <f-args>
- */
+/// Split a string by unescaped whitespace (space & tab), used for f-args on Lua commands callback.
+/// Similar to uc_split_args(), but does not allocate, add quotes, add commas and is an iterator.
+///
+/// @param[in] arg String to split
+/// @param[in] arglen Length of {arg}
+/// @param[inout] end Index of last character of previous iteration
+/// @param[out] buf Buffer to copy string into
+/// @param[out] len Length of string in {buf}
+///
+/// @return true if iteration is complete, else false
+bool uc_split_args_iter(const char_u *arg, size_t arglen, size_t *end, char *buf, size_t *len)
+{
+ if (!arglen) {
+ return true;
+ }
+
+ size_t pos = *end;
+ while (pos < arglen && ascii_iswhite(arg[pos])) {
+ pos++;
+ }
+
+ size_t l = 0;
+ for (; pos < arglen - 1; pos++) {
+ if (arg[pos] == '\\' && (arg[pos + 1] == '\\' || ascii_iswhite(arg[pos + 1]))) {
+ buf[l++] = arg[++pos];
+ } else {
+ buf[l++] = arg[pos];
+ if (ascii_iswhite(arg[pos + 1])) {
+ *end = pos + 1;
+ *len = l;
+ return false;
+ }
+ }
+ }
+
+ if (pos < arglen && !ascii_iswhite(arg[pos])) {
+ buf[l++] = arg[pos];
+ }
+
+ *len = l;
+ return true;
+}
+
+/// split and quote args for <f-args>
static char_u *uc_split_args(char_u *arg, size_t *lenp)
{
char_u *buf;
@@ -5816,7 +5885,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) {
@@ -6008,7 +6077,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) {
@@ -6017,76 +6086,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;
-
- // :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?
+ result += uc_mods((char *)buf);
- // :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;
@@ -6125,6 +6131,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;
@@ -6139,7 +6215,6 @@ static void do_ucmd(exarg_T *eap)
size_t split_len = 0;
char_u *split_buf = NULL;
ucmd_T *cmd;
- const sctx_T save_current_sctx = current_sctx;
if (eap->cmdidx == CMD_USER) {
cmd = USER_CMD(eap->useridx);
@@ -6147,6 +6222,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".
@@ -6172,7 +6252,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);
@@ -6225,36 +6304,42 @@ static void do_ucmd(exarg_T *eap)
buf = xmalloc(totlen + 1);
}
- current_sctx.sc_sid = cmd->uc_script_ctx.sc_sid;
+ sctx_T save_current_sctx;
+ bool restore_current_sctx = false;
+ if ((cmd->uc_argt & EX_KEEPSCRIPT) == 0) {
+ restore_current_sctx = true;
+ save_current_sctx = current_sctx;
+ 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;
+
+ // Careful: Do not use "cmd" here, it may have become invalid if a user
+ // command was added.
+ if (restore_current_sctx) {
+ current_sctx = save_current_sctx;
+ }
xfree(buf);
xfree(split_buf);
}
-static char_u *get_user_command_name(int idx)
+static char_u *expand_user_command_name(int idx)
{
return get_user_commands(NULL, idx - CMD_SIZE);
}
-/*
- * Function given to ExpandGeneric() to obtain the list of user address type names.
- */
+
+/// Function given to ExpandGeneric() to obtain the list of user address type names.
char_u *get_user_cmd_addr_type(expand_T *xp, int idx)
{
return (char_u *)addr_type_complete[idx].name;
}
-/*
- * Function given to ExpandGeneric() to obtain the list of user command names.
- */
+/// Function given to ExpandGeneric() to obtain the list of user command names.
char_u *get_user_commands(expand_T *xp FUNC_ATTR_UNUSED, int idx)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
// In cmdwin, the alternative buffer should be used.
- const buf_T *const buf = (cmdwin_type != 0 && get_cmdline_type() == NUL)
- ? prevwin->w_buffer
- : curbuf;
+ const buf_T *const buf = prevwin_curwin()->w_buffer;
if (idx < buf->b_ucmds.ga_len) {
return USER_CMD_GA(&buf->b_ucmds, idx)->uc_name;
@@ -6266,15 +6351,33 @@ char_u *get_user_commands(expand_T *xp FUNC_ATTR_UNUSED, int idx)
return NULL;
}
-/*
- * Function given to ExpandGeneric() to obtain the list of user command
- * attributes.
- */
+/// Get the name of user command "idx". "cmdidx" can be CMD_USER or
+/// CMD_USER_BUF.
+///
+/// @return NULL if the command is not found.
+static char_u *get_user_command_name(int idx, int cmdidx)
+{
+ if (cmdidx == CMD_USER && idx < ucmds.ga_len) {
+ return USER_CMD(idx)->uc_name;
+ }
+ if (cmdidx == CMD_USER_BUF) {
+ // In cmdwin, the alternative buffer should be used.
+ const buf_T *const buf = prevwin_curwin()->w_buffer;
+
+ if (idx < buf->b_ucmds.ga_len) {
+ return USER_CMD_GA(&buf->b_ucmds, idx)->uc_name;
+ }
+ }
+ return NULL;
+}
+
+/// Function given to ExpandGeneric() to obtain the list of user command
+/// attributes.
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;
@@ -6282,9 +6385,7 @@ char_u *get_user_cmd_flags(expand_T *xp, int idx)
return (char_u *)user_cmd_flags[idx];
}
-/*
- * Function given to ExpandGeneric() to obtain the list of values for -nargs.
- */
+/// Function given to ExpandGeneric() to obtain the list of values for -nargs.
char_u *get_user_cmd_nargs(expand_T *xp, int idx)
{
static char *user_cmd_nargs[] = { "0", "1", "*", "?", "+" };
@@ -6295,9 +6396,7 @@ char_u *get_user_cmd_nargs(expand_T *xp, int idx)
return (char_u *)user_cmd_nargs[idx];
}
-/*
- * Function given to ExpandGeneric() to obtain the list of values for -complete.
- */
+/// Function given to ExpandGeneric() to obtain the list of values for -complete.
char_u *get_user_cmd_complete(expand_T *xp, int idx)
{
if (idx >= (int)ARRAY_SIZE(command_complete)) {
@@ -6311,9 +6410,7 @@ char_u *get_user_cmd_complete(expand_T *xp, int idx)
}
}
-/*
- * Parse address type argument
- */
+/// Parse address type argument
int parse_addr_type_arg(char_u *value, int vallen, cmd_addr_T *addr_type_arg)
FUNC_ATTR_NONNULL_ALL
{
@@ -6340,13 +6437,12 @@ int parse_addr_type_arg(char_u *value, int vallen, cmd_addr_T *addr_type_arg)
return OK;
}
-/*
- * Parse a completion argument "value[vallen]".
- * The detected completion goes in "*complp", argument type in "*argt".
- * When there is an argument, for function and user defined completion, it's
- * copied to allocated memory and stored in "*compl_arg".
- * Returns FAIL if something is wrong.
- */
+/// Parse a completion argument "value[vallen]".
+/// The detected completion goes in "*complp", argument type in "*argt".
+/// When there is an argument, for function and user defined completion, it's
+/// copied to allocated memory and stored in "*compl_arg".
+///
+/// @return FAIL if something is wrong.
int parse_compl_arg(const char_u *value, int vallen, int *complp, uint32_t *argt,
char_u **compl_arg)
FUNC_ATTR_NONNULL_ALL
@@ -6451,10 +6547,8 @@ static void ex_highlight(exarg_T *eap)
}
-/*
- * Call this function if we thought we were going to exit, but we won't
- * (because of an error). May need to restore the terminal mode.
- */
+/// Call this function if we thought we were going to exit, but we won't
+/// (because of an error). May need to restore the terminal mode.
void not_exiting(void)
{
exiting = false;
@@ -6489,8 +6583,8 @@ bool before_quit_autocmds(win_T *wp, bool quit_all, bool forceit)
return false;
}
-// ":quit": quit current window, quit Vim if the last window is closed.
-// ":{nr}quit": quit window {nr}
+/// ":quit": quit current window, quit Vim if the last window is closed.
+/// ":{nr}quit": quit window {nr}
static void ex_quit(exarg_T *eap)
{
if (cmdwin_type != 0) {
@@ -6550,12 +6644,13 @@ static void ex_quit(exarg_T *eap)
}
not_exiting();
// close window; may free buffer
- win_close(wp, !buf_hide(wp->w_buffer) || eap->forceit);
+ win_close(wp, !buf_hide(wp->w_buffer) || eap->forceit, eap->forceit);
}
}
/// ":cquit".
static void ex_cquit(exarg_T *eap)
+ FUNC_ATTR_NORETURN
{
// this does not always pass on the exit code to the Manx compiler. why?
getout(eap->addr_count > 0 ? (int)eap->line2 : EXIT_FAILURE);
@@ -6590,9 +6685,7 @@ static void ex_quit_all(exarg_T *eap)
not_exiting();
}
-/*
- * ":close": close current window, unless it is the last one
- */
+/// ":close": close current window, unless it is the last one
static void ex_close(exarg_T *eap)
{
win_T *win = NULL;
@@ -6618,9 +6711,7 @@ static void ex_close(exarg_T *eap)
}
}
-/*
- * ":pclose": Close any preview window.
- */
+/// ":pclose": Close any preview window.
static void ex_pclose(exarg_T *eap)
{
FOR_ALL_WINDOWS_IN_TAB(win, curtab) {
@@ -6665,16 +6756,14 @@ void ex_win_close(int forceit, win_T *win, tabpage_T *tp)
// free buffer when not hiding it or when it's a scratch buffer
if (tp == NULL) {
- win_close(win, !need_hide && !buf_hide(buf));
+ win_close(win, !need_hide && !buf_hide(buf), forceit);
} else {
win_close_othertab(win, !need_hide && !buf_hide(buf), tp);
}
}
-/*
- * ":tabclose": close current tab page, unless it is the last one.
- * ":tabclose N": close tab page N.
- */
+/// ":tabclose": close current tab page, unless it is the last one.
+/// ":tabclose N": close tab page N.
static void ex_tabclose(exarg_T *eap)
{
tabpage_T *tp;
@@ -6735,9 +6824,7 @@ static void ex_tabonly(exarg_T *eap)
}
}
-/*
- * Close the current tab page.
- */
+/// Close the current tab page.
void tabpage_close(int forceit)
{
// First close all the windows but the current one. If that worked then
@@ -6753,12 +6840,10 @@ void tabpage_close(int forceit)
}
}
-/*
- * Close tab page "tp", which is not the current tab page.
- * Note that autocommands may make "tp" invalid.
- * Also takes care of the tab pages line disappearing when closing the
- * last-but-one tab page.
- */
+/// Close tab page "tp", which is not the current tab page.
+/// Note that autocommands may make "tp" invalid.
+/// Also takes care of the tab pages line disappearing when closing the
+/// last-but-one tab page.
void tabpage_close_other(tabpage_T *tp, int forceit)
{
int done = 0;
@@ -6775,7 +6860,7 @@ void tabpage_close_other(tabpage_T *tp, int forceit)
// Autocommands may delete the tab page under our fingers and we may
// fail to close a window with a modified buffer.
- if (!valid_tabpage(tp) || tp->tp_firstwin == wp) {
+ if (!valid_tabpage(tp) || tp->tp_lastwin == wp) {
break;
}
}
@@ -6786,9 +6871,7 @@ void tabpage_close_other(tabpage_T *tp, int forceit)
}
}
-/*
- * ":only".
- */
+/// ":only".
static void ex_only(exarg_T *eap)
{
win_T *wp;
@@ -6812,10 +6895,8 @@ static void ex_only(exarg_T *eap)
close_others(TRUE, eap->forceit);
}
-/*
- * ":all" and ":sall".
- * Also used for ":tab drop file ..." after setting the argument list.
- */
+/// ":all" and ":sall".
+/// Also used for ":tab drop file ..." after setting the argument list.
void ex_all(exarg_T *eap)
{
if (eap->addr_count == 0) {
@@ -6829,7 +6910,7 @@ static void ex_hide(exarg_T *eap)
// ":hide" or ":hide | cmd": hide current window
if (!eap->skip) {
if (eap->addr_count == 0) {
- win_close(curwin, false); // don't free buffer
+ win_close(curwin, false, eap->forceit); // don't free buffer
} else {
int winnr = 0;
win_T *win = NULL;
@@ -6844,7 +6925,7 @@ static void ex_hide(exarg_T *eap)
if (win == NULL) {
win = lastwin;
}
- win_close(win, false);
+ win_close(win, false, eap->forceit);
}
}
}
@@ -6870,7 +6951,7 @@ static void ex_stop(exarg_T *eap)
apply_autocmds(EVENT_VIMRESUME, NULL, NULL, false, NULL);
}
-// ":exit", ":xit" and ":wq": Write file and quit the current window.
+/// ":exit", ":xit" and ":wq": Write file and quit the current window.
static void ex_exit(exarg_T *eap)
{
if (cmdwin_type != 0) {
@@ -6902,13 +6983,11 @@ static void ex_exit(exarg_T *eap)
}
not_exiting();
// Quit current window, may free the buffer.
- win_close(curwin, !buf_hide(curwin->w_buffer));
+ win_close(curwin, !buf_hide(curwin->w_buffer), eap->forceit);
}
}
-/*
- * ":print", ":list", ":number".
- */
+/// ":print", ":list", ":number".
static void ex_print(exarg_T *eap)
{
if (curbuf->b_ml.ml_flags & ML_EMPTY) {
@@ -6938,29 +7017,23 @@ static void ex_goto(exarg_T *eap)
goto_byte(eap->line2);
}
-/*
- * Clear an argument list: free all file names and reset it to zero entries.
- */
+/// Clear an argument list: free all file names and reset it to zero entries.
void alist_clear(alist_T *al)
{
#define FREE_AENTRY_FNAME(arg) xfree(arg->ae_fname)
GA_DEEP_CLEAR(&al->al_ga, aentry_T, FREE_AENTRY_FNAME);
}
-/*
- * Init an argument list.
- */
+/// Init an argument list.
void alist_init(alist_T *al)
{
ga_init(&al->al_ga, (int)sizeof(aentry_T), 5);
}
-/*
- * Remove a reference from an argument list.
- * Ignored when the argument list is the global one.
- * If the argument list is no longer used by any window, free it.
- */
+/// Remove a reference from an argument list.
+/// Ignored when the argument list is the global one.
+/// If the argument list is no longer used by any window, free it.
void alist_unlink(alist_T *al)
{
if (al != &global_alist && --al->al_refcount <= 0) {
@@ -6969,9 +7042,7 @@ void alist_unlink(alist_T *al)
}
}
-/*
- * Create a new argument list and use it for the current window.
- */
+/// Create a new argument list and use it for the current window.
void alist_new(void)
{
curwin->w_alist = xmalloc(sizeof(*curwin->w_alist));
@@ -6981,11 +7052,10 @@ void alist_new(void)
}
#if !defined(UNIX)
-/*
- * Expand the file names in the global argument list.
- * If "fnum_list" is not NULL, use "fnum_list[fnum_len]" as a list of buffer
- * numbers to be re-used.
- */
+
+/// Expand the file names in the global argument list.
+/// If "fnum_list" is not NULL, use "fnum_list[fnum_len]" as a list of buffer
+/// numbers to be re-used.
void alist_expand(int *fnum_list, int fnum_len)
{
char_u **old_arg_files;
@@ -7016,10 +7086,8 @@ void alist_expand(int *fnum_list, int fnum_len)
}
#endif
-/*
- * Set the argument list for the current window.
- * Takes over the allocated files[] and the allocated fnames in it.
- */
+/// Set the argument list for the current window.
+/// Takes over the allocated files[] and the allocated fnames in it.
void alist_set(alist_T *al, int count, char_u **files, int use_curbuf, int *fnum_list, int fnum_len)
{
int i;
@@ -7083,9 +7151,8 @@ void alist_add(alist_T *al, char_u *fname, int set_fnum)
}
#if defined(BACKSLASH_IN_FILENAME)
-/*
- * Adjust slashes in file names. Called after 'shellslash' was set.
- */
+
+/// Adjust slashes in file names. Called after 'shellslash' was set.
void alist_slash_adjust(void)
{
for (int i = 0; i < GARGCOUNT; ++i) {
@@ -7110,7 +7177,6 @@ void alist_slash_adjust(void)
/// ":preserve".
static void ex_preserve(exarg_T *eap)
{
- curbuf->b_flags |= BF_PRESERVED;
ml_preserve(curbuf, true, true);
}
@@ -7131,27 +7197,23 @@ static void ex_recover(exarg_T *eap)
recoverymode = false;
}
-/*
- * Command modifier used in a wrong way.
- */
+/// Command modifier used in a wrong way.
static void ex_wrongmodifier(exarg_T *eap)
{
eap->errmsg = e_invcmd;
}
-/*
- * :sview [+command] file split window with new file, read-only
- * :split [[+command] file] split window with current or new file
- * :vsplit [[+command] file] split window vertically with current or new file
- * :new [[+command] file] split window with no or new file
- * :vnew [[+command] file] split vertically window with no or new file
- * :sfind [+command] file split window with file in 'path'
- *
- * :tabedit open new Tab page with empty window
- * :tabedit [+command] file open new Tab page and edit "file"
- * :tabnew [[+command] file] just like :tabedit
- * :tabfind [+command] file open new Tab page and find "file"
- */
+/// :sview [+command] file split window with new file, read-only
+/// :split [[+command] file] split window with current or new file
+/// :vsplit [[+command] file] split window vertically with current or new file
+/// :new [[+command] file] split window with no or new file
+/// :vnew [[+command] file] split vertically window with no or new file
+/// :sfind [+command] file split window with file in 'path'
+///
+/// :tabedit open new Tab page with empty window
+/// :tabedit [+command] file open new Tab page and edit "file"
+/// :tabnew [[+command] file] just like :tabedit
+/// :tabfind [+command] file open new Tab page and find "file"
void ex_splitview(exarg_T *eap)
{
win_T *old_curwin = curwin;
@@ -7214,9 +7276,7 @@ theend:
xfree(fname);
}
-/*
- * Open a new tab page.
- */
+/// Open a new tab page.
void tabpage_new(void)
{
exarg_T ea;
@@ -7228,9 +7288,7 @@ void tabpage_new(void)
ex_splitview(&ea);
}
-/*
- * :tabnext command
- */
+/// :tabnext command
static void ex_tabnext(exarg_T *eap)
{
int tab_number;
@@ -7277,9 +7335,7 @@ static void ex_tabnext(exarg_T *eap)
}
}
-/*
- * :tabmove command
- */
+/// :tabmove command
static void ex_tabmove(exarg_T *eap)
{
int tab_number = get_tabpage_arg(eap);
@@ -7288,9 +7344,7 @@ static void ex_tabmove(exarg_T *eap)
}
}
-/*
- * :tabs command: List tabs and their contents.
- */
+/// :tabs command: List tabs and their contents.
static void ex_tabs(exarg_T *eap)
{
int tabcount = 1;
@@ -7336,10 +7390,8 @@ static void ex_tabs(exarg_T *eap)
}
-/*
- * ":mode":
- * If no argument given, get the screen size and redraw.
- */
+/// ":mode":
+/// If no argument given, get the screen size and redraw.
static void ex_mode(exarg_T *eap)
{
if (*eap->arg == NUL) {
@@ -7350,10 +7402,8 @@ static void ex_mode(exarg_T *eap)
}
}
-/*
- * ":resize".
- * set, increment or decrement current window height
- */
+/// ":resize".
+/// set, increment or decrement current window height
static void ex_resize(exarg_T *eap)
{
int n;
@@ -7383,9 +7433,7 @@ static void ex_resize(exarg_T *eap)
}
}
-/*
- * ":find [+command] <file>" command.
- */
+/// ":find [+command] <file>" command.
static void ex_find(exarg_T *eap)
{
char_u *fname;
@@ -7416,7 +7464,7 @@ static void ex_edit(exarg_T *eap)
do_exedit(eap, NULL);
}
-/// ":edit <file>" command and alikes.
+/// ":edit <file>" command and alike.
///
/// @param old_curwin curwin before doing a split or NULL
void do_exedit(exarg_T *eap, win_T *old_curwin)
@@ -7462,9 +7510,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),
@@ -7504,7 +7551,7 @@ void do_exedit(exarg_T *eap, win_T *old_curwin)
// Reset the error/interrupt/exception state here so that
// aborting() returns FALSE when closing a window.
enter_cleanup(&cs);
- win_close(curwin, !need_hide && !buf_hide(curbuf));
+ win_close(curwin, !need_hide && !buf_hide(curbuf), false);
// Restore the error/interrupt/exception state if not
// discarded by a new aborting error, interrupt, or
@@ -7563,11 +7610,9 @@ static void ex_swapname(exarg_T *eap)
}
}
-/*
- * ":syncbind" forces all 'scrollbind' windows to have the same relative
- * offset.
- * (1998-11-02 16:21:01 R. Edward Ralston <eralston@computer.org>)
- */
+/// ":syncbind" forces all 'scrollbind' windows to have the same relative
+/// offset.
+/// (1998-11-02 16:21:01 R. Edward Ralston <eralston@computer.org>)
static void ex_syncbind(exarg_T *eap)
{
win_T *save_curwin = curwin;
@@ -7698,7 +7743,7 @@ void free_cd_dir(void)
#endif
-// Get the previous directory for the given chdir scope.
+/// Get the previous directory for the given chdir scope.
static char_u *get_prevdir(CdScope scope)
{
switch (scope) {
@@ -7716,7 +7761,7 @@ static char_u *get_prevdir(CdScope scope)
/// Deal with the side effects of changing the current directory.
///
/// @param scope Scope of the function call (global, tab or window).
-void post_chdir(CdScope scope, bool trigger_dirchanged)
+static void post_chdir(CdScope scope, bool trigger_dirchanged)
{
// Always overwrite the window-local CWD.
XFREE_CLEAR(curwin->w_localdir);
@@ -7753,10 +7798,11 @@ void post_chdir(CdScope scope, bool trigger_dirchanged)
abort();
}
+ last_chdir_reason = NULL;
shorten_fnames(true);
if (trigger_dirchanged) {
- do_autocmd_dirchanged(cwd, scope, kCdCauseManual);
+ do_autocmd_dirchanged(cwd, scope, kCdCauseManual, false);
}
}
@@ -7766,14 +7812,11 @@ void post_chdir(CdScope scope, bool trigger_dirchanged)
/// @return true if the directory is successfully changed.
bool changedir_func(char_u *new_dir, CdScope scope)
{
- char_u *tofree;
- char_u *pdir = NULL;
- bool retval = false;
-
if (new_dir == NULL || allbuf_locked()) {
return false;
}
+ char_u *pdir = NULL;
// ":cd -": Change to previous directory
if (STRCMP(new_dir, "-") == 0) {
pdir = get_prevdir(scope);
@@ -7784,45 +7827,51 @@ bool changedir_func(char_u *new_dir, CdScope scope)
new_dir = pdir;
}
- // Free the previous directory
- tofree = get_prevdir(scope);
-
if (os_dirname(NameBuff, MAXPATHL) == OK) {
pdir = vim_strsave(NameBuff);
} else {
pdir = NULL;
}
- switch (scope) {
- case kCdScopeTabpage:
- curtab->tp_prevdir = pdir;
- break;
- case kCdScopeWindow:
- curwin->w_prevdir = pdir;
- break;
- default:
- 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;
- post_chdir(scope, dir_differs);
- retval = true;
- } else {
- emsg(_(e_failed));
+ bool dir_differs = pdir == NULL || pathcmp((char *)pdir, (char *)new_dir, -1) != 0;
+ if (dir_differs) {
+ do_autocmd_dirchanged((char *)new_dir, scope, kCdCauseManual, true);
+ if (vim_chdir(new_dir) != 0) {
+ emsg(_(e_failed));
+ xfree(pdir);
+ return false;
+ }
}
- xfree(tofree);
- return retval;
+ char_u **pp;
+ switch (scope) {
+ case kCdScopeTabpage:
+ pp = &curtab->tp_prevdir;
+ break;
+ case kCdScopeWindow:
+ pp = &curwin->w_prevdir;
+ break;
+ default:
+ pp = &prev_dir;
+ }
+ xfree(*pp);
+ *pp = pdir;
+
+ post_chdir(scope, dir_differs);
+
+ return true;
}
/// ":cd", ":tcd", ":lcd", ":chdir", "tchdir" and ":lchdir".
@@ -7830,9 +7879,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
@@ -7859,9 +7908,7 @@ void ex_cd(exarg_T *eap)
}
}
-/*
- * ":pwd".
- */
+/// ":pwd".
static void ex_pwd(exarg_T *eap)
{
if (os_dirname(NameBuff, MAXPATHL) == OK) {
@@ -7870,7 +7917,9 @@ static void ex_pwd(exarg_T *eap)
#endif
if (p_verbose > 0) {
char *context = "global";
- if (curwin->w_localdir != NULL) {
+ if (last_chdir_reason != NULL) {
+ context = last_chdir_reason;
+ } else if (curwin->w_localdir != NULL) {
context = "window";
} else if (curtab->tp_localdir != NULL) {
context = "tabpage";
@@ -7884,9 +7933,7 @@ static void ex_pwd(exarg_T *eap)
}
}
-/*
- * ":=".
- */
+/// ":=".
static void ex_equal(exarg_T *eap)
{
smsg("%" PRId64, (int64_t)eap->line2);
@@ -7917,9 +7964,7 @@ static void ex_sleep(exarg_T *eap)
do_sleep(len);
}
-/*
- * Sleep for "msec" milliseconds, but keep checking for a CTRL-C every second.
- */
+/// Sleep for "msec" milliseconds, but keep checking for a CTRL-C every second.
void do_sleep(long msec)
{
ui_flush(); // flush before waiting
@@ -7955,9 +8000,7 @@ static void do_exmap(exarg_T *eap, int isabbrev)
}
}
-/*
- * ":winsize" command (obsolete).
- */
+/// ":winsize" command (obsolete).
static void ex_winsize(exarg_T *eap)
{
char_u *arg = eap->arg;
@@ -8008,9 +8051,7 @@ static void ex_wincmd(exarg_T *eap)
}
}
-/*
- * Handle command that work like operators: ":delete", ":yank", ":>" and ":<".
- */
+/// Handle command that work like operators: ":delete", ":yank", ":>" and ":<".
static void ex_operators(exarg_T *eap)
{
oparg_T oa;
@@ -8040,7 +8081,7 @@ static void ex_operators(exarg_T *eap)
case CMD_yank:
oa.op_type = OP_YANK;
- (void)op_yank(&oa, true, false);
+ (void)op_yank(&oa, true);
break;
default: // CMD_rshift or CMD_lshift
@@ -8057,9 +8098,7 @@ static void ex_operators(exarg_T *eap)
ex_may_print(eap);
}
-/*
- * ":put".
- */
+/// ":put".
static void ex_put(exarg_T *eap)
{
// ":0put" works like ":1put!".
@@ -8068,13 +8107,12 @@ static void ex_put(exarg_T *eap)
eap->forceit = TRUE;
}
curwin->w_cursor.lnum = eap->line2;
+ check_cursor_col();
do_put(eap->regname, NULL, eap->forceit ? BACKWARD : FORWARD, 1,
PUT_LINE|PUT_CURSLINE);
}
-/*
- * Handle ":copy" and ":move".
- */
+/// Handle ":copy" and ":move".
static void ex_copymove(exarg_T *eap)
{
long n = get_address(eap, &eap->arg, eap->addr_type, false, false, false, 1);
@@ -8104,9 +8142,7 @@ static void ex_copymove(exarg_T *eap)
ex_may_print(eap);
}
-/*
- * Print the current line if flags were given to the Ex command.
- */
+/// Print the current line if flags were given to the Ex command.
void ex_may_print(exarg_T *eap)
{
if (eap->flags != 0) {
@@ -8126,9 +8162,7 @@ static void ex_submagic(exarg_T *eap)
p_magic = magic_save;
}
-/*
- * ":join".
- */
+/// ":join".
static void ex_join(exarg_T *eap)
{
curwin->w_cursor.lnum = eap->line1;
@@ -8147,9 +8181,7 @@ static void ex_join(exarg_T *eap)
ex_may_print(eap);
}
-/*
- * ":[addr]@r": execute register
- */
+/// ":[addr]@r": execute register
static void ex_at(exarg_T *eap)
{
int prev_len = typebuf.tb_len;
@@ -8185,17 +8217,13 @@ static void ex_at(exarg_T *eap)
}
}
-/*
- * ":!".
- */
+/// ":!".
static void ex_bang(exarg_T *eap)
{
do_bang(eap->addr_count, eap, eap->forceit, true, true);
}
-/*
- * ":undo".
- */
+/// ":undo".
static void ex_undo(exarg_T *eap)
{
if (eap->addr_count == 1) { // :undo 123
@@ -8261,9 +8289,7 @@ static void ex_later(exarg_T *eap)
}
}
-/*
- * ":redir": start/stop redirection.
- */
+/// ":redir": start/stop redirection.
static void ex_redir(exarg_T *eap)
{
char *mode;
@@ -8334,9 +8360,7 @@ static void ex_redir(exarg_T *eap)
if (var_redir_start(skipwhite(arg), append) == OK) {
redir_vname = 1;
}
- }
- // TODO: redirect to a buffer
- else {
+ } else { // TODO(vim): redirect to a buffer
semsg(_(e_invarg2), eap->arg);
}
}
@@ -8406,7 +8430,7 @@ static void ex_redrawstatus(exarg_T *eap)
ui_flush();
}
-// ":redrawtabline": force redraw of the tabline
+/// ":redrawtabline": force redraw of the tabline
static void ex_redrawtabline(exarg_T *eap FUNC_ATTR_UNUSED)
{
const int r = RedrawingDisabled;
@@ -8480,9 +8504,7 @@ FILE *open_exfile(char_u *fname, int forceit, char *mode)
return fd;
}
-/*
- * ":mark" and ":k".
- */
+/// ":mark" and ":k".
static void ex_mark(exarg_T *eap)
{
pos_T pos;
@@ -8502,9 +8524,7 @@ static void ex_mark(exarg_T *eap)
}
}
-/*
- * Update w_topline, w_leftcol and the cursor position.
- */
+/// Update w_topline, w_leftcol and the cursor position.
void update_topline_cursor(void)
{
check_cursor(); // put cursor on valid line
@@ -8515,8 +8535,9 @@ void update_topline_cursor(void)
update_curswant();
}
-// Save the current State and go to Normal mode.
-// Return true if the typeahead could be saved.
+/// Save the current State and go to Normal mode.
+///
+/// @return true if the typeahead could be saved.
bool save_current_state(save_state_T *sst)
FUNC_ATTR_NONNULL_ALL
{
@@ -8528,6 +8549,7 @@ bool save_current_state(save_state_T *sst)
sst->save_finish_op = finish_op;
sst->save_opcount = opcount;
sst->save_reg_executing = reg_executing;
+ sst->save_pending_end_reg_executing = pending_end_reg_executing;
msg_scroll = false; // no msg scrolling in Normal mode
restart_edit = 0; // don't go to Insert mode
@@ -8558,6 +8580,7 @@ void restore_current_state(save_state_T *sst)
finish_op = sst->save_finish_op;
opcount = sst->save_opcount;
reg_executing = sst->save_reg_executing;
+ pending_end_reg_executing = sst->save_pending_end_reg_executing;
// don't reset msg_didout now
msg_didout |= sst->save_msg_didout;
@@ -8568,9 +8591,7 @@ void restore_current_state(save_state_T *sst)
ui_cursor_shape(); // may show different cursor shape
}
-/*
- * ":normal[!] {commands}": Execute normal mode commands.
- */
+/// ":normal[!] {commands}": Execute normal mode commands.
static void ex_normal(exarg_T *eap)
{
if (curbuf->terminal && State & TERM_FOCUS) {
@@ -8591,7 +8612,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.
{
@@ -8600,8 +8621,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;
}
}
@@ -8652,9 +8672,7 @@ static void ex_normal(exarg_T *eap)
xfree(arg);
}
-/*
- * ":startinsert", ":startreplace" and ":startgreplace"
- */
+/// ":startinsert", ":startreplace" and ":startgreplace"
static void ex_startinsert(exarg_T *eap)
{
if (eap->forceit) {
@@ -8691,9 +8709,7 @@ static void ex_startinsert(exarg_T *eap)
}
}
-/*
- * ":stopinsert"
- */
+/// ":stopinsert"
static void ex_stopinsert(exarg_T *eap)
{
restart_edit = 0;
@@ -8701,10 +8717,8 @@ static void ex_stopinsert(exarg_T *eap)
clearmode();
}
-/*
- * Execute normal mode command "cmd".
- * "remap" can be REMAP_NONE or REMAP_YES.
- */
+/// Execute normal mode command "cmd".
+/// "remap" can be REMAP_NONE or REMAP_YES.
void exec_normal_cmd(char_u *cmd, int remap, bool silent)
{
// Stuff the argument into the typeahead buffer.
@@ -8737,9 +8751,7 @@ static void ex_checkpath(exarg_T *eap)
(linenr_T)1, (linenr_T)MAXLNUM);
}
-/*
- * ":psearch"
- */
+/// ":psearch"
static void ex_psearch(exarg_T *eap)
{
g_do_tagpreview = p_pvh;
@@ -8802,18 +8814,14 @@ static void ex_findpat(exarg_T *eap)
}
-/*
- * ":ptag", ":ptselect", ":ptjump", ":ptnext", etc.
- */
+/// ":ptag", ":ptselect", ":ptjump", ":ptnext", etc.
static void ex_ptag(exarg_T *eap)
{
g_do_tagpreview = p_pvh; // will be reset to 0 in ex_tag_cmd()
ex_tag_cmd(eap, cmdnames[eap->cmdidx].cmd_name + 1);
}
-/*
- * ":pedit"
- */
+/// ":pedit"
static void ex_pedit(exarg_T *eap)
{
win_T *curwin_save = curwin;
@@ -8834,9 +8842,7 @@ static void ex_pedit(exarg_T *eap)
g_do_tagpreview = 0;
}
-/*
- * ":stag", ":stselect" and ":stjump".
- */
+/// ":stag", ":stselect" and ":stjump".
static void ex_stag(exarg_T *eap)
{
postponed_split = -1;
@@ -8847,9 +8853,7 @@ static void ex_stag(exarg_T *eap)
postponed_split_tab = 0;
}
-/*
- * ":tag", ":tselect", ":tjump", ":tnext", etc.
- */
+/// ":tag", ":tselect", ":tjump", ":tnext", etc.
static void ex_tag(exarg_T *eap)
{
ex_tag_cmd(eap, cmdnames[eap->cmdidx].cmd_name);
@@ -8918,11 +8922,9 @@ enum {
// SPEC_CLIENT,
};
-/*
- * Check "str" for starting with a special cmdline variable.
- * If found return one of the SPEC_ values and set "*usedlen" to the length of
- * the variable. Otherwise return -1 and "*usedlen" is unchanged.
- */
+/// Check "str" for starting with a special cmdline variable.
+/// If found return one of the SPEC_ values and set "*usedlen" to the length of
+/// the variable. Otherwise return -1 and "*usedlen" is unchanged.
ssize_t find_cmdline_var(const char_u *src, size_t *usedlen)
FUNC_ATTR_NONNULL_ALL
{
@@ -9235,11 +9237,9 @@ char_u *eval_vars(char_u *src, char_u *srcstart, size_t *usedlen, linenr_T *lnum
return result;
}
-/*
- * Concatenate all files in the argument list, separated by spaces, and return
- * it in one allocated string.
- * Spaces and backslashes in the file names are escaped with a backslash.
- */
+/// Concatenate all files in the argument list, separated by spaces, and return
+/// it in one allocated string.
+/// Spaces and backslashes in the file names are escaped with a backslash.
static char_u *arg_all(void)
{
int len;
@@ -9298,11 +9298,9 @@ static char_u *arg_all(void)
return retval;
}
-/*
- * Expand the <sfile> string in "arg".
- *
- * Returns an allocated string, or NULL for any error.
- */
+/// Expand the <sfile> string in "arg".
+///
+/// @return an allocated string, or NULL for any error.
char_u *expand_sfile(char_u *arg)
{
char *errormsg;
@@ -9348,9 +9346,7 @@ char_u *expand_sfile(char_u *arg)
return result;
}
-/*
- * ":rshada" and ":wshada".
- */
+/// ":rshada" and ":wshada".
static void ex_shada(exarg_T *eap)
{
char_u *save_shada;
@@ -9367,10 +9363,8 @@ static void ex_shada(exarg_T *eap)
p_shada = save_shada;
}
-/*
- * Make a dialog message in "buff[DIALOG_MSG_SIZE]".
- * "format" must contain "%s".
- */
+/// Make a dialog message in "buff[DIALOG_MSG_SIZE]".
+/// "format" must contain "%s".
void dialog_msg(char_u *buff, char *format, char_u *fname)
{
if (fname == NULL) {
@@ -9379,9 +9373,7 @@ void dialog_msg(char_u *buff, char *format, char_u *fname)
vim_snprintf((char *)buff, DIALOG_MSG_SIZE, format, fname);
}
-/*
- * ":behave {mswin,xterm}"
- */
+/// ":behave {mswin,xterm}"
static void ex_behave(exarg_T *eap)
{
if (STRCMP(eap->arg, "mswin") == 0) {
@@ -9399,10 +9391,8 @@ static void ex_behave(exarg_T *eap)
}
}
-/*
- * Function given to ExpandGeneric() to obtain the possible arguments of the
- * ":behave {mswin,xterm}" command.
- */
+/// Function given to ExpandGeneric() to obtain the possible arguments of the
+/// ":behave {mswin,xterm}" command.
char_u *get_behave_arg(expand_T *xp, int idx)
{
if (idx == 0) {
@@ -9414,8 +9404,8 @@ char_u *get_behave_arg(expand_T *xp, int idx)
return NULL;
}
-// Function given to ExpandGeneric() to obtain the possible arguments of the
-// ":messages {clear}" command.
+/// Function given to ExpandGeneric() to obtain the possible arguments of the
+/// ":messages {clear}" command.
char_u *get_messages_arg(expand_T *xp FUNC_ATTR_UNUSED, int idx)
{
if (idx == 0) {
@@ -9436,15 +9426,13 @@ static TriState filetype_detect = kNone;
static TriState filetype_plugin = kNone;
static TriState filetype_indent = kNone;
-/*
- * ":filetype [plugin] [indent] {on,off,detect}"
- * on: Load the filetype.vim file to install autocommands for file types.
- * off: Load the ftoff.vim file to remove all autocommands for file types.
- * plugin on: load filetype.vim and ftplugin.vim
- * plugin off: load ftplugof.vim
- * indent on: load filetype.vim and indent.vim
- * indent off: load indoff.vim
- */
+/// ":filetype [plugin] [indent] {on,off,detect}"
+/// on: Load the filetype.vim file to install autocommands for file types.
+/// off: Load the ftoff.vim file to remove all autocommands for file types.
+/// plugin on: load filetype.vim and ftplugin.vim
+/// plugin off: load ftplugof.vim
+/// indent on: load filetype.vim and indent.vim
+/// indent off: load indoff.vim
static void ex_filetype(exarg_T *eap)
{
char_u *arg = eap->arg;
@@ -9510,23 +9498,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 overridden) 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)
{
@@ -9553,97 +9552,19 @@ static void ex_digraphs(exarg_T *eap)
}
}
-static void ex_set(exarg_T *eap)
-{
- int flags = 0;
-
- if (eap->cmdidx == CMD_setlocal) {
- flags = OPT_LOCAL;
- } else if (eap->cmdidx == CMD_setglobal) {
- flags = OPT_GLOBAL;
- }
- (void)do_set(eap->arg, flags);
-}
-
void set_no_hlsearch(bool flag)
{
no_hlsearch = flag;
set_vim_var_nr(VV_HLSEARCH, !no_hlsearch && p_hls);
}
-/*
- * ":nohlsearch"
- */
+/// ":nohlsearch"
static void ex_nohlsearch(exarg_T *eap)
{
set_no_hlsearch(true);
redraw_all_later(SOME_VALID);
}
-// ":[N]match {group} {pattern}"
-// Sets nextcmd to the start of the next command, if any. Also called when
-// skipping commands to find the next command.
-static void ex_match(exarg_T *eap)
-{
- char_u *p;
- char_u *g = NULL;
- char_u *end;
- int c;
- int id;
-
- if (eap->line2 <= 3) {
- id = eap->line2;
- } else {
- emsg(e_invcmd);
- return;
- }
-
- // First clear any old pattern.
- if (!eap->skip) {
- match_delete(curwin, id, false);
- }
-
- if (ends_excmd(*eap->arg)) {
- end = eap->arg;
- } else if ((STRNICMP(eap->arg, "none", 4) == 0
- && (ascii_iswhite(eap->arg[4]) || ends_excmd(eap->arg[4])))) {
- end = eap->arg + 4;
- } else {
- p = skiptowhite(eap->arg);
- if (!eap->skip) {
- g = vim_strnsave(eap->arg, p - eap->arg);
- }
- p = skipwhite(p);
- if (*p == NUL) {
- // There must be two arguments.
- xfree(g);
- semsg(_(e_invarg2), eap->arg);
- return;
- }
- end = skip_regexp(p + 1, *p, true, NULL);
- if (!eap->skip) {
- if (*end != NUL && !ends_excmd(*skipwhite(end + 1))) {
- xfree(g);
- eap->errmsg = e_trailing;
- return;
- }
- if (*end != *p) {
- xfree(g);
- semsg(_(e_invarg2), p);
- return;
- }
-
- c = *end;
- *end = NUL;
- match_add(curwin, (const char *)g, (const char *)p + 1, 10, id,
- NULL, NULL);
- xfree(g);
- *end = c;
- }
- }
- eap->nextcmd = find_nextcmd(end);
-}
-
static void ex_fold(exarg_T *eap)
{
if (foldManualAllowed(true)) {
@@ -9673,8 +9594,8 @@ static void ex_folddo(exarg_T *eap)
ml_clearmarked(); // clear rest of the marks
}
-// Returns true if the supplied Ex cmdidx is for a location list command
-// instead of a quickfix command.
+/// @return true if the supplied Ex cmdidx is for a location list command
+/// instead of a quickfix command.
bool is_loclist_cmd(int cmdidx)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
@@ -9799,6 +9720,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: