mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
Merge pull request #18366 from famiu/feat/api/nvim_cmd
feat(api): add `nvim_cmd`
This commit is contained in:
commit
96a125b207
@ -1711,17 +1711,49 @@ nvim_call_function({fn}, {args}) *nvim_call_function()*
|
|||||||
Return: ~
|
Return: ~
|
||||||
Result of the function call
|
Result of the function call
|
||||||
|
|
||||||
|
nvim_cmd({*cmd}, {*opts}) *nvim_cmd()*
|
||||||
|
Executes an Ex command.
|
||||||
|
|
||||||
|
Unlike |nvim_command()| this command takes a structured
|
||||||
|
Dictionary instead of a String. This allows for easier
|
||||||
|
construction and manipulation of an Ex command. This also
|
||||||
|
allows for things such as having spaces inside a command
|
||||||
|
argument, expanding filenames in a command that otherwise
|
||||||
|
doesn't expand filenames, etc.
|
||||||
|
|
||||||
|
Parameters: ~
|
||||||
|
{cmd} Command to execute. Must be a Dictionary that can
|
||||||
|
contain the same values as the return value of
|
||||||
|
|nvim_parse_cmd()| except "addr", "nargs" and
|
||||||
|
"nextcmd" which are ignored if provided. All
|
||||||
|
values except for "cmd" are optional.
|
||||||
|
{opts} Optional parameters.
|
||||||
|
• output: (boolean, default false) Whether to
|
||||||
|
return command output.
|
||||||
|
|
||||||
|
Return: ~
|
||||||
|
Command output (non-error, non-shell |:!|) if `output` is
|
||||||
|
true, else empty string.
|
||||||
|
|
||||||
|
See also: ~
|
||||||
|
|nvim_exec()|
|
||||||
|
|nvim_command()|
|
||||||
|
|
||||||
nvim_command({command}) *nvim_command()*
|
nvim_command({command}) *nvim_command()*
|
||||||
Executes an ex-command.
|
Executes an Ex command.
|
||||||
|
|
||||||
On execution error: fails with VimL error, does not update
|
On execution error: fails with VimL error, does not update
|
||||||
v:errmsg.
|
v:errmsg.
|
||||||
|
|
||||||
Parameters: ~
|
Prefer using |nvim_cmd()| or |nvim_exec()| over this. To
|
||||||
{command} Ex-command string
|
evaluate multiple lines of Vim script or an Ex command
|
||||||
|
directly, use |nvim_exec()|. To construct an Ex command using
|
||||||
|
a structured format and then execute it, use |nvim_cmd()|. To
|
||||||
|
modify an Ex command before evaluating it, use
|
||||||
|
|nvim_parse_cmd()| in conjunction with |nvim_cmd()|.
|
||||||
|
|
||||||
See also: ~
|
Parameters: ~
|
||||||
|nvim_exec()|
|
{command} Ex command string
|
||||||
|
|
||||||
nvim_eval({expr}) *nvim_eval()*
|
nvim_eval({expr}) *nvim_eval()*
|
||||||
Evaluates a VimL |expression|. Dictionaries and Lists are
|
Evaluates a VimL |expression|. Dictionaries and Lists are
|
||||||
@ -1737,7 +1769,7 @@ nvim_eval({expr}) *nvim_eval()*
|
|||||||
Evaluation result or expanded object
|
Evaluation result or expanded object
|
||||||
|
|
||||||
nvim_exec({src}, {output}) *nvim_exec()*
|
nvim_exec({src}, {output}) *nvim_exec()*
|
||||||
Executes Vimscript (multiline block of Ex-commands), like
|
Executes Vimscript (multiline block of Ex commands), like
|
||||||
anonymous |:source|.
|
anonymous |:source|.
|
||||||
|
|
||||||
Unlike |nvim_command()| this function supports heredocs,
|
Unlike |nvim_command()| this function supports heredocs,
|
||||||
@ -1758,6 +1790,7 @@ nvim_exec({src}, {output}) *nvim_exec()*
|
|||||||
See also: ~
|
See also: ~
|
||||||
|execute()|
|
|execute()|
|
||||||
|nvim_command()|
|
|nvim_command()|
|
||||||
|
|nvim_cmd()|
|
||||||
|
|
||||||
nvim_parse_cmd({str}, {opts}) *nvim_parse_cmd()*
|
nvim_parse_cmd({str}, {opts}) *nvim_parse_cmd()*
|
||||||
Parse command line.
|
Parse command line.
|
||||||
|
@ -155,5 +155,44 @@ return {
|
|||||||
create_augroup = {
|
create_augroup = {
|
||||||
"clear";
|
"clear";
|
||||||
};
|
};
|
||||||
|
cmd = {
|
||||||
|
"cmd";
|
||||||
|
"range";
|
||||||
|
"count";
|
||||||
|
"reg";
|
||||||
|
"bang";
|
||||||
|
"args";
|
||||||
|
"magic";
|
||||||
|
"mods";
|
||||||
|
"nargs";
|
||||||
|
"addr";
|
||||||
|
"nextcmd";
|
||||||
|
};
|
||||||
|
cmd_magic = {
|
||||||
|
"file";
|
||||||
|
"bar";
|
||||||
|
};
|
||||||
|
cmd_mods = {
|
||||||
|
"silent";
|
||||||
|
"emsg_silent";
|
||||||
|
"sandbox";
|
||||||
|
"noautocmd";
|
||||||
|
"browse";
|
||||||
|
"confirm";
|
||||||
|
"hide";
|
||||||
|
"keepalt";
|
||||||
|
"keepjumps";
|
||||||
|
"keepmarks";
|
||||||
|
"keeppatterns";
|
||||||
|
"lockmarks";
|
||||||
|
"noswapfile";
|
||||||
|
"tab";
|
||||||
|
"verbose";
|
||||||
|
"vertical";
|
||||||
|
"split";
|
||||||
|
};
|
||||||
|
cmd_opts = {
|
||||||
|
"output";
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
#include "nvim/decoration.h"
|
#include "nvim/decoration.h"
|
||||||
#include "nvim/eval.h"
|
#include "nvim/eval.h"
|
||||||
#include "nvim/eval/typval.h"
|
#include "nvim/eval/typval.h"
|
||||||
|
#include "nvim/ex_cmds_defs.h"
|
||||||
#include "nvim/extmark.h"
|
#include "nvim/extmark.h"
|
||||||
#include "nvim/fileio.h"
|
#include "nvim/fileio.h"
|
||||||
#include "nvim/getchar.h"
|
#include "nvim/getchar.h"
|
||||||
@ -1690,3 +1691,174 @@ int init_sign_text(char **sign_text, char *text)
|
|||||||
|
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Check if a string contains only whitespace characters.
|
||||||
|
bool string_iswhite(String str)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < str.size; i++) {
|
||||||
|
if (!ascii_iswhite(str.data[i])) {
|
||||||
|
// Found a non-whitespace character
|
||||||
|
return false;
|
||||||
|
} else if (str.data[i] == NUL) {
|
||||||
|
// Terminate at first occurence of a NUL character
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add modifier string for command into the command line. Includes trailing whitespace if non-empty.
|
||||||
|
// @return OK or FAIL.
|
||||||
|
static int add_cmd_modifier_str(char *cmdline, size_t *pos, const size_t bufsize,
|
||||||
|
CmdParseInfo *cmdinfo)
|
||||||
|
{
|
||||||
|
#define APPEND_MODIFIER(...) \
|
||||||
|
do { \
|
||||||
|
if (*pos < bufsize) { \
|
||||||
|
*pos += (size_t)snprintf(cmdline + *pos, bufsize - *pos, __VA_ARGS__); \
|
||||||
|
} \
|
||||||
|
if (*pos < bufsize) { \
|
||||||
|
cmdline[*pos] = ' '; \
|
||||||
|
*pos += 1; \
|
||||||
|
} else { \
|
||||||
|
goto err; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define APPEND_MODIFIER_IF(cond, mod) \
|
||||||
|
do { \
|
||||||
|
if (cond) { \
|
||||||
|
APPEND_MODIFIER(mod); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
if (cmdinfo->cmdmod.tab != 0) {
|
||||||
|
APPEND_MODIFIER("%dtab", cmdinfo->cmdmod.tab - 1);
|
||||||
|
}
|
||||||
|
if (cmdinfo->verbose != -1) {
|
||||||
|
APPEND_MODIFIER("%ldverbose", cmdinfo->verbose);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (cmdinfo->cmdmod.split & (WSP_ABOVE | WSP_BELOW | WSP_TOP | WSP_BOT)) {
|
||||||
|
case WSP_ABOVE:
|
||||||
|
APPEND_MODIFIER("aboveleft");
|
||||||
|
break;
|
||||||
|
case WSP_BELOW:
|
||||||
|
APPEND_MODIFIER("belowright");
|
||||||
|
break;
|
||||||
|
case WSP_TOP:
|
||||||
|
APPEND_MODIFIER("topleft");
|
||||||
|
break;
|
||||||
|
case WSP_BOT:
|
||||||
|
APPEND_MODIFIER("botright");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
APPEND_MODIFIER_IF(cmdinfo->cmdmod.split & WSP_VERT, "vertical");
|
||||||
|
|
||||||
|
if (cmdinfo->emsg_silent) {
|
||||||
|
APPEND_MODIFIER("silent!");
|
||||||
|
} else if (cmdinfo->silent) {
|
||||||
|
APPEND_MODIFIER("silent");
|
||||||
|
}
|
||||||
|
|
||||||
|
APPEND_MODIFIER_IF(cmdinfo->sandbox, "sandbox");
|
||||||
|
APPEND_MODIFIER_IF(cmdinfo->noautocmd, "noautocmd");
|
||||||
|
APPEND_MODIFIER_IF(cmdinfo->cmdmod.browse, "browse");
|
||||||
|
APPEND_MODIFIER_IF(cmdinfo->cmdmod.confirm, "confirm");
|
||||||
|
APPEND_MODIFIER_IF(cmdinfo->cmdmod.hide, "hide");
|
||||||
|
APPEND_MODIFIER_IF(cmdinfo->cmdmod.keepalt, "keepalt");
|
||||||
|
APPEND_MODIFIER_IF(cmdinfo->cmdmod.keepjumps, "keepjumps");
|
||||||
|
APPEND_MODIFIER_IF(cmdinfo->cmdmod.keepmarks, "keepmarks");
|
||||||
|
APPEND_MODIFIER_IF(cmdinfo->cmdmod.keeppatterns, "keeppatterns");
|
||||||
|
APPEND_MODIFIER_IF(cmdinfo->cmdmod.lockmarks, "lockmarks");
|
||||||
|
APPEND_MODIFIER_IF(cmdinfo->cmdmod.noswapfile, "noswapfile");
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
err:
|
||||||
|
return FAIL;
|
||||||
|
|
||||||
|
#undef APPEND_MODIFIER
|
||||||
|
#undef APPEND_MODIFIER_IF
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Build cmdline string for command, used by `nvim_cmd()`.
|
||||||
|
///
|
||||||
|
/// @return OK or FAIL.
|
||||||
|
int build_cmdline_str(char **cmdlinep, exarg_T *eap, CmdParseInfo *cmdinfo, char **args,
|
||||||
|
size_t argc)
|
||||||
|
{
|
||||||
|
const size_t bufsize = IOSIZE;
|
||||||
|
size_t pos = 0;
|
||||||
|
char *cmdline = xcalloc(bufsize, sizeof(char));
|
||||||
|
|
||||||
|
#define CMDLINE_APPEND(...) \
|
||||||
|
do { \
|
||||||
|
if (pos < bufsize) { \
|
||||||
|
pos += (size_t)snprintf(cmdline + pos, bufsize - pos, __VA_ARGS__); \
|
||||||
|
} else { \
|
||||||
|
goto err; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
// Command modifiers.
|
||||||
|
if (add_cmd_modifier_str(cmdline, &pos, bufsize, cmdinfo) == FAIL) {
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command range / count.
|
||||||
|
if (eap->argt & EX_RANGE) {
|
||||||
|
if (eap->addr_count == 1) {
|
||||||
|
CMDLINE_APPEND("%ld", eap->line2);
|
||||||
|
} else if (eap->addr_count > 1) {
|
||||||
|
CMDLINE_APPEND("%ld,%ld", eap->line1, eap->line2);
|
||||||
|
eap->addr_count = 2; // Make sure address count is not greater than 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep the index of the position where command name starts, so eap->cmd can point to it.
|
||||||
|
size_t cmdname_idx = pos;
|
||||||
|
CMDLINE_APPEND("%s", eap->cmd);
|
||||||
|
eap->cmd = cmdline + cmdname_idx;
|
||||||
|
|
||||||
|
// Command bang.
|
||||||
|
if (eap->argt & EX_BANG && eap->forceit) {
|
||||||
|
CMDLINE_APPEND("!");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command register.
|
||||||
|
if (eap->argt & EX_REGSTR && eap->regname) {
|
||||||
|
CMDLINE_APPEND(" %c", eap->regname);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate through each argument and store the starting position and length of each argument in
|
||||||
|
// the cmdline string in `eap->args` and `eap->arglens`, respectively.
|
||||||
|
eap->args = xcalloc(argc, sizeof(char *));
|
||||||
|
eap->arglens = xcalloc(argc, sizeof(size_t));
|
||||||
|
for (size_t i = 0; i < argc; i++) {
|
||||||
|
eap->args[i] = cmdline + pos + 1; // add 1 to skip the leading space.
|
||||||
|
eap->arglens[i] = STRLEN(args[i]);
|
||||||
|
CMDLINE_APPEND(" %s", args[i]);
|
||||||
|
}
|
||||||
|
eap->argc = argc;
|
||||||
|
eap->arg = argc > 0 ? eap->args[0] : NULL;
|
||||||
|
|
||||||
|
// Replace, :make and :grep with 'makeprg' and 'grepprg'.
|
||||||
|
char *p = replace_makeprg(eap, eap->arg, cmdlinep);
|
||||||
|
if (p != eap->arg) {
|
||||||
|
// If replace_makeprg modified the cmdline string, correct the argument pointers.
|
||||||
|
assert(argc == 1);
|
||||||
|
eap->arg = p;
|
||||||
|
eap->args[0] = p;
|
||||||
|
}
|
||||||
|
|
||||||
|
*cmdlinep = cmdline;
|
||||||
|
return OK;
|
||||||
|
err:
|
||||||
|
xfree(cmdline);
|
||||||
|
return FAIL;
|
||||||
|
|
||||||
|
#undef CMDLINE_APPEND
|
||||||
|
}
|
||||||
|
@ -10,10 +10,14 @@
|
|||||||
#include "nvim/api/private/helpers.h"
|
#include "nvim/api/private/helpers.h"
|
||||||
#include "nvim/api/vimscript.h"
|
#include "nvim/api/vimscript.h"
|
||||||
#include "nvim/ascii.h"
|
#include "nvim/ascii.h"
|
||||||
|
#include "nvim/autocmd.h"
|
||||||
#include "nvim/eval.h"
|
#include "nvim/eval.h"
|
||||||
#include "nvim/eval/typval.h"
|
#include "nvim/eval/typval.h"
|
||||||
#include "nvim/eval/userfunc.h"
|
#include "nvim/eval/userfunc.h"
|
||||||
#include "nvim/ex_cmds2.h"
|
#include "nvim/ex_cmds2.h"
|
||||||
|
#include "nvim/ops.h"
|
||||||
|
#include "nvim/strings.h"
|
||||||
|
#include "nvim/vim.h"
|
||||||
#include "nvim/viml/parser/expressions.h"
|
#include "nvim/viml/parser/expressions.h"
|
||||||
#include "nvim/viml/parser/parser.h"
|
#include "nvim/viml/parser/parser.h"
|
||||||
#include "nvim/window.h"
|
#include "nvim/window.h"
|
||||||
@ -22,7 +26,9 @@
|
|||||||
# include "api/vimscript.c.generated.h"
|
# include "api/vimscript.c.generated.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/// Executes Vimscript (multiline block of Ex-commands), like anonymous
|
#define IS_USER_CMDIDX(idx) ((int)(idx) < 0)
|
||||||
|
|
||||||
|
/// Executes Vimscript (multiline block of Ex commands), like anonymous
|
||||||
/// |:source|.
|
/// |:source|.
|
||||||
///
|
///
|
||||||
/// Unlike |nvim_command()| this function supports heredocs, script-scope (s:),
|
/// Unlike |nvim_command()| this function supports heredocs, script-scope (s:),
|
||||||
@ -32,6 +38,7 @@
|
|||||||
///
|
///
|
||||||
/// @see |execute()|
|
/// @see |execute()|
|
||||||
/// @see |nvim_command()|
|
/// @see |nvim_command()|
|
||||||
|
/// @see |nvim_cmd()|
|
||||||
///
|
///
|
||||||
/// @param src Vimscript code
|
/// @param src Vimscript code
|
||||||
/// @param output Capture and return all (non-error, non-shell |:!|) output
|
/// @param output Capture and return all (non-error, non-shell |:!|) output
|
||||||
@ -89,13 +96,16 @@ theend:
|
|||||||
return (String)STRING_INIT;
|
return (String)STRING_INIT;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Executes an ex-command.
|
/// Executes an Ex command.
|
||||||
///
|
///
|
||||||
/// On execution error: fails with VimL error, does not update v:errmsg.
|
/// On execution error: fails with VimL error, does not update v:errmsg.
|
||||||
///
|
///
|
||||||
/// @see |nvim_exec()|
|
/// Prefer using |nvim_cmd()| or |nvim_exec()| over this. To evaluate multiple lines of Vim script
|
||||||
|
/// or an Ex command directly, use |nvim_exec()|. To construct an Ex command using a structured
|
||||||
|
/// format and then execute it, use |nvim_cmd()|. To modify an Ex command before evaluating it, use
|
||||||
|
/// |nvim_parse_cmd()| in conjunction with |nvim_cmd()|.
|
||||||
///
|
///
|
||||||
/// @param command Ex-command string
|
/// @param command Ex command string
|
||||||
/// @param[out] err Error details (Vim error), if any
|
/// @param[out] err Error details (Vim error), if any
|
||||||
void nvim_command(String command, Error *err)
|
void nvim_command(String command, Error *err)
|
||||||
FUNC_API_SINCE(1)
|
FUNC_API_SINCE(1)
|
||||||
@ -978,3 +988,370 @@ end:
|
|||||||
xfree(cmdline);
|
xfree(cmdline);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Executes an Ex command.
|
||||||
|
///
|
||||||
|
/// Unlike |nvim_command()| this command takes a structured Dictionary instead of a String. This
|
||||||
|
/// allows for easier construction and manipulation of an Ex command. This also allows for things
|
||||||
|
/// such as having spaces inside a command argument, expanding filenames in a command that otherwise
|
||||||
|
/// doesn't expand filenames, etc.
|
||||||
|
///
|
||||||
|
/// @see |nvim_exec()|
|
||||||
|
/// @see |nvim_command()|
|
||||||
|
///
|
||||||
|
/// @param cmd Command to execute. Must be a Dictionary that can contain the same values as
|
||||||
|
/// the return value of |nvim_parse_cmd()| except "addr", "nargs" and "nextcmd"
|
||||||
|
/// which are ignored if provided. All values except for "cmd" are optional.
|
||||||
|
/// @param opts Optional parameters.
|
||||||
|
/// - output: (boolean, default false) Whether to return command output.
|
||||||
|
/// @param[out] err Error details, if any.
|
||||||
|
/// @return Command output (non-error, non-shell |:!|) if `output` is true, else empty string.
|
||||||
|
String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error *err)
|
||||||
|
FUNC_API_SINCE(10)
|
||||||
|
{
|
||||||
|
exarg_T ea;
|
||||||
|
memset(&ea, 0, sizeof(ea));
|
||||||
|
ea.verbose_save = -1;
|
||||||
|
ea.save_msg_silent = -1;
|
||||||
|
|
||||||
|
CmdParseInfo cmdinfo;
|
||||||
|
memset(&cmdinfo, 0, sizeof(cmdinfo));
|
||||||
|
cmdinfo.verbose = -1;
|
||||||
|
|
||||||
|
char *cmdline = NULL;
|
||||||
|
char *cmdname = NULL;
|
||||||
|
char **args = NULL;
|
||||||
|
size_t argc = 0;
|
||||||
|
|
||||||
|
String retv = (String)STRING_INIT;
|
||||||
|
|
||||||
|
#define OBJ_TO_BOOL(var, value, default, varname) \
|
||||||
|
do { \
|
||||||
|
var = api_object_to_bool(value, varname, default, err); \
|
||||||
|
if (ERROR_SET(err)) { \
|
||||||
|
goto end; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define VALIDATION_ERROR(...) \
|
||||||
|
do { \
|
||||||
|
api_set_error(err, kErrorTypeValidation, __VA_ARGS__); \
|
||||||
|
goto end; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
bool output;
|
||||||
|
OBJ_TO_BOOL(output, opts->output, false, "'output'");
|
||||||
|
|
||||||
|
// First, parse the command name and check if it exists and is valid.
|
||||||
|
if (!HAS_KEY(cmd->cmd) || cmd->cmd.type != kObjectTypeString
|
||||||
|
|| cmd->cmd.data.string.data[0] == NUL) {
|
||||||
|
VALIDATION_ERROR("'cmd' must be a non-empty String");
|
||||||
|
}
|
||||||
|
|
||||||
|
cmdname = string_to_cstr(cmd->cmd.data.string);
|
||||||
|
ea.cmd = cmdname;
|
||||||
|
|
||||||
|
char *p = find_ex_command(&ea, NULL);
|
||||||
|
|
||||||
|
// If this looks like an undefined user command and there are CmdUndefined
|
||||||
|
// autocommands defined, trigger the matching autocommands.
|
||||||
|
if (p != NULL && ea.cmdidx == CMD_SIZE && ASCII_ISUPPER(*ea.cmd)
|
||||||
|
&& has_event(EVENT_CMDUNDEFINED)) {
|
||||||
|
p = xstrdup(cmdname);
|
||||||
|
int ret = apply_autocmds(EVENT_CMDUNDEFINED, (char_u *)p, (char_u *)p, true, NULL);
|
||||||
|
xfree(p);
|
||||||
|
// If the autocommands did something and didn't cause an error, try
|
||||||
|
// finding the command again.
|
||||||
|
p = (ret && !aborting()) ? find_ex_command(&ea, NULL) : ea.cmd;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p == NULL || ea.cmdidx == CMD_SIZE) {
|
||||||
|
VALIDATION_ERROR("Command not found: %s", cmdname);
|
||||||
|
}
|
||||||
|
if (is_cmd_ni(ea.cmdidx)) {
|
||||||
|
VALIDATION_ERROR("Command not implemented: %s", cmdname);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the command flags so that we can know what type of arguments the command uses.
|
||||||
|
// Not required for a user command since `find_ex_command` already deals with it in that case.
|
||||||
|
if (!IS_USER_CMDIDX(ea.cmdidx)) {
|
||||||
|
ea.argt = get_cmd_argt(ea.cmdidx);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse command arguments since it's needed to get the command address type.
|
||||||
|
if (HAS_KEY(cmd->args)) {
|
||||||
|
if (cmd->args.type != kObjectTypeArray) {
|
||||||
|
VALIDATION_ERROR("'args' must be an Array");
|
||||||
|
}
|
||||||
|
// Check if every argument is valid
|
||||||
|
for (size_t i = 0; i < cmd->args.data.array.size; i++) {
|
||||||
|
Object elem = cmd->args.data.array.items[i];
|
||||||
|
if (elem.type != kObjectTypeString) {
|
||||||
|
VALIDATION_ERROR("Command argument must be a String");
|
||||||
|
} else if (string_iswhite(elem.data.string)) {
|
||||||
|
VALIDATION_ERROR("Command argument must have non-whitespace characters");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
argc = cmd->args.data.array.size;
|
||||||
|
bool argc_valid;
|
||||||
|
|
||||||
|
// Check if correct number of arguments is used.
|
||||||
|
switch (ea.argt & (EX_EXTRA | EX_NOSPC | EX_NEEDARG)) {
|
||||||
|
case EX_EXTRA | EX_NOSPC | EX_NEEDARG:
|
||||||
|
argc_valid = argc == 1;
|
||||||
|
break;
|
||||||
|
case EX_EXTRA | EX_NOSPC:
|
||||||
|
argc_valid = argc <= 1;
|
||||||
|
break;
|
||||||
|
case EX_EXTRA | EX_NEEDARG:
|
||||||
|
argc_valid = argc >= 1;
|
||||||
|
break;
|
||||||
|
case EX_EXTRA:
|
||||||
|
argc_valid = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
argc_valid = argc == 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!argc_valid) {
|
||||||
|
argc = 0; // Ensure that args array isn't erroneously freed at the end.
|
||||||
|
VALIDATION_ERROR("Incorrect number of arguments supplied");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argc != 0) {
|
||||||
|
args = xcalloc(argc, sizeof(char *));
|
||||||
|
|
||||||
|
for (size_t i = 0; i < argc; i++) {
|
||||||
|
args[i] = string_to_cstr(cmd->args.data.array.items[i].data.string);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simply pass the first argument (if it exists) as the arg pointer to `set_cmd_addr_type()`
|
||||||
|
// since it only ever checks the first argument.
|
||||||
|
set_cmd_addr_type(&ea, argc > 0 ? (char_u *)args[0] : NULL);
|
||||||
|
|
||||||
|
if (HAS_KEY(cmd->range)) {
|
||||||
|
if (!(ea.argt & EX_RANGE)) {
|
||||||
|
VALIDATION_ERROR("Command cannot accept a range");
|
||||||
|
} else if (cmd->range.type != kObjectTypeArray) {
|
||||||
|
VALIDATION_ERROR("'range' must be an Array");
|
||||||
|
} else if (cmd->range.data.array.size > 2) {
|
||||||
|
VALIDATION_ERROR("'range' cannot contain more than two elements");
|
||||||
|
}
|
||||||
|
|
||||||
|
Array range = cmd->range.data.array;
|
||||||
|
ea.addr_count = (int)range.size;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < range.size; i++) {
|
||||||
|
Object elem = range.items[i];
|
||||||
|
if (elem.type != kObjectTypeInteger || elem.data.integer < 0) {
|
||||||
|
VALIDATION_ERROR("'range' element must be a non-negative Integer");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (range.size > 0) {
|
||||||
|
ea.line1 = range.items[0].data.integer;
|
||||||
|
ea.line2 = range.items[range.size - 1].data.integer;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (invalid_range(&ea) != NULL) {
|
||||||
|
VALIDATION_ERROR("Invalid range provided");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ea.addr_count == 0) {
|
||||||
|
if (ea.argt & EX_DFLALL) {
|
||||||
|
set_cmd_dflall_range(&ea); // Default range for range=%
|
||||||
|
} else {
|
||||||
|
ea.line1 = ea.line2 = get_cmd_default_range(&ea); // Default range.
|
||||||
|
|
||||||
|
if (ea.addr_type == ADDR_OTHER) {
|
||||||
|
// Default is 1, not cursor.
|
||||||
|
ea.line2 = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (HAS_KEY(cmd->count)) {
|
||||||
|
if (!(ea.argt & EX_COUNT)) {
|
||||||
|
VALIDATION_ERROR("Command cannot accept a count");
|
||||||
|
} else if (cmd->count.type != kObjectTypeInteger || cmd->count.data.integer < 0) {
|
||||||
|
VALIDATION_ERROR("'count' must be a non-negative Integer");
|
||||||
|
}
|
||||||
|
set_cmd_count(&ea, cmd->count.data.integer, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (HAS_KEY(cmd->reg)) {
|
||||||
|
if (!(ea.argt & EX_REGSTR)) {
|
||||||
|
VALIDATION_ERROR("Command cannot accept a register");
|
||||||
|
} else if (cmd->reg.type != kObjectTypeString || cmd->reg.data.string.size != 1) {
|
||||||
|
VALIDATION_ERROR("'reg' must be a single character");
|
||||||
|
}
|
||||||
|
char regname = cmd->reg.data.string.data[0];
|
||||||
|
if (regname == '=') {
|
||||||
|
VALIDATION_ERROR("Cannot use register \"=");
|
||||||
|
} else if (!valid_yank_reg(regname, ea.cmdidx != CMD_put && !IS_USER_CMDIDX(ea.cmdidx))) {
|
||||||
|
VALIDATION_ERROR("Invalid register: \"%c", regname);
|
||||||
|
}
|
||||||
|
ea.regname = (uint8_t)regname;
|
||||||
|
}
|
||||||
|
|
||||||
|
OBJ_TO_BOOL(ea.forceit, cmd->bang, false, "'bang'");
|
||||||
|
if (ea.forceit && !(ea.argt & EX_BANG)) {
|
||||||
|
VALIDATION_ERROR("Command cannot accept a bang");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (HAS_KEY(cmd->magic)) {
|
||||||
|
if (cmd->magic.type != kObjectTypeDictionary) {
|
||||||
|
VALIDATION_ERROR("'magic' must be a Dictionary");
|
||||||
|
}
|
||||||
|
|
||||||
|
Dict(cmd_magic) magic = { 0 };
|
||||||
|
if (!api_dict_to_keydict(&magic, KeyDict_cmd_magic_get_field,
|
||||||
|
cmd->magic.data.dictionary, err)) {
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
OBJ_TO_BOOL(cmdinfo.magic.file, magic.file, ea.argt & EX_XFILE, "'magic.file'");
|
||||||
|
OBJ_TO_BOOL(cmdinfo.magic.bar, magic.bar, ea.argt & EX_TRLBAR, "'magic.bar'");
|
||||||
|
} else {
|
||||||
|
cmdinfo.magic.file = ea.argt & EX_XFILE;
|
||||||
|
cmdinfo.magic.bar = ea.argt & EX_TRLBAR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (HAS_KEY(cmd->mods)) {
|
||||||
|
if (cmd->mods.type != kObjectTypeDictionary) {
|
||||||
|
VALIDATION_ERROR("'mods' must be a Dictionary");
|
||||||
|
}
|
||||||
|
|
||||||
|
Dict(cmd_mods) mods = { 0 };
|
||||||
|
if (!api_dict_to_keydict(&mods, KeyDict_cmd_mods_get_field, cmd->mods.data.dictionary, err)) {
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (HAS_KEY(mods.tab)) {
|
||||||
|
if (mods.tab.type != kObjectTypeInteger || mods.tab.data.integer < 0) {
|
||||||
|
VALIDATION_ERROR("'mods.tab' must be a non-negative Integer");
|
||||||
|
}
|
||||||
|
cmdinfo.cmdmod.tab = (int)mods.tab.data.integer + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (HAS_KEY(mods.verbose)) {
|
||||||
|
if (mods.verbose.type != kObjectTypeInteger || mods.verbose.data.integer <= 0) {
|
||||||
|
VALIDATION_ERROR("'mods.verbose' must be a non-negative Integer");
|
||||||
|
}
|
||||||
|
cmdinfo.verbose = mods.verbose.data.integer;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool vertical;
|
||||||
|
OBJ_TO_BOOL(vertical, mods.vertical, false, "'mods.vertical'");
|
||||||
|
cmdinfo.cmdmod.split |= (vertical ? WSP_VERT : 0);
|
||||||
|
|
||||||
|
if (HAS_KEY(mods.split)) {
|
||||||
|
if (mods.split.type != kObjectTypeString) {
|
||||||
|
VALIDATION_ERROR("'mods.split' must be a String");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (STRCMP(mods.split.data.string.data, "aboveleft") == 0
|
||||||
|
|| STRCMP(mods.split.data.string.data, "leftabove") == 0) {
|
||||||
|
cmdinfo.cmdmod.split |= WSP_ABOVE;
|
||||||
|
} else if (STRCMP(mods.split.data.string.data, "belowright") == 0
|
||||||
|
|| STRCMP(mods.split.data.string.data, "rightbelow") == 0) {
|
||||||
|
cmdinfo.cmdmod.split |= WSP_BELOW;
|
||||||
|
} else if (STRCMP(mods.split.data.string.data, "topleft") == 0) {
|
||||||
|
cmdinfo.cmdmod.split |= WSP_TOP;
|
||||||
|
} else if (STRCMP(mods.split.data.string.data, "botright") == 0) {
|
||||||
|
cmdinfo.cmdmod.split |= WSP_BOT;
|
||||||
|
} else {
|
||||||
|
VALIDATION_ERROR("Invalid value for 'mods.split'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
OBJ_TO_BOOL(cmdinfo.silent, mods.silent, false, "'mods.silent'");
|
||||||
|
OBJ_TO_BOOL(cmdinfo.emsg_silent, mods.emsg_silent, false, "'mods.emsg_silent'");
|
||||||
|
OBJ_TO_BOOL(cmdinfo.sandbox, mods.sandbox, false, "'mods.sandbox'");
|
||||||
|
OBJ_TO_BOOL(cmdinfo.noautocmd, mods.noautocmd, false, "'mods.noautocmd'");
|
||||||
|
OBJ_TO_BOOL(cmdinfo.cmdmod.browse, mods.browse, false, "'mods.browse'");
|
||||||
|
OBJ_TO_BOOL(cmdinfo.cmdmod.confirm, mods.confirm, false, "'mods.confirm'");
|
||||||
|
OBJ_TO_BOOL(cmdinfo.cmdmod.hide, mods.hide, false, "'mods.hide'");
|
||||||
|
OBJ_TO_BOOL(cmdinfo.cmdmod.keepalt, mods.keepalt, false, "'mods.keepalt'");
|
||||||
|
OBJ_TO_BOOL(cmdinfo.cmdmod.keepjumps, mods.keepjumps, false, "'mods.keepjumps'");
|
||||||
|
OBJ_TO_BOOL(cmdinfo.cmdmod.keepmarks, mods.keepmarks, false, "'mods.keepmarks'");
|
||||||
|
OBJ_TO_BOOL(cmdinfo.cmdmod.keeppatterns, mods.keeppatterns, false, "'mods.keeppatterns'");
|
||||||
|
OBJ_TO_BOOL(cmdinfo.cmdmod.lockmarks, mods.lockmarks, false, "'mods.lockmarks'");
|
||||||
|
OBJ_TO_BOOL(cmdinfo.cmdmod.noswapfile, mods.noswapfile, false, "'mods.noswapfile'");
|
||||||
|
|
||||||
|
if (cmdinfo.sandbox && !(ea.argt & EX_SBOXOK)) {
|
||||||
|
VALIDATION_ERROR("Command cannot be run in sandbox");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally, build the command line string that will be stored inside ea.cmdlinep.
|
||||||
|
// This also sets the values of ea.cmd, ea.arg, ea.args and ea.arglens.
|
||||||
|
if (build_cmdline_str(&cmdline, &ea, &cmdinfo, args, argc) == FAIL) {
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
ea.cmdlinep = &cmdline;
|
||||||
|
|
||||||
|
garray_T capture_local;
|
||||||
|
const int save_msg_silent = msg_silent;
|
||||||
|
garray_T * const save_capture_ga = capture_ga;
|
||||||
|
|
||||||
|
if (output) {
|
||||||
|
ga_init(&capture_local, 1, 80);
|
||||||
|
capture_ga = &capture_local;
|
||||||
|
}
|
||||||
|
|
||||||
|
try_start();
|
||||||
|
if (output) {
|
||||||
|
msg_silent++;
|
||||||
|
}
|
||||||
|
|
||||||
|
WITH_SCRIPT_CONTEXT(channel_id, {
|
||||||
|
execute_cmd(&ea, &cmdinfo);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (output) {
|
||||||
|
capture_ga = save_capture_ga;
|
||||||
|
msg_silent = save_msg_silent;
|
||||||
|
}
|
||||||
|
try_end(err);
|
||||||
|
|
||||||
|
if (ERROR_SET(err)) {
|
||||||
|
goto clear_ga;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (output && capture_local.ga_len > 1) {
|
||||||
|
retv = (String){
|
||||||
|
.data = capture_local.ga_data,
|
||||||
|
.size = (size_t)capture_local.ga_len,
|
||||||
|
};
|
||||||
|
// redir usually (except :echon) prepends a newline.
|
||||||
|
if (retv.data[0] == '\n') {
|
||||||
|
memmove(retv.data, retv.data + 1, retv.size - 1);
|
||||||
|
retv.data[retv.size - 1] = '\0';
|
||||||
|
retv.size = retv.size - 1;
|
||||||
|
}
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
clear_ga:
|
||||||
|
if (output) {
|
||||||
|
ga_clear(&capture_local);
|
||||||
|
}
|
||||||
|
end:
|
||||||
|
xfree(cmdline);
|
||||||
|
xfree(cmdname);
|
||||||
|
xfree(ea.args);
|
||||||
|
xfree(ea.arglens);
|
||||||
|
for (size_t i = 0; i < argc; i++) {
|
||||||
|
xfree(args[i]);
|
||||||
|
}
|
||||||
|
xfree(args);
|
||||||
|
|
||||||
|
return retv;
|
||||||
|
|
||||||
|
#undef OBJ_TO_BOOL
|
||||||
|
#undef VALIDATION_ERROR
|
||||||
|
}
|
||||||
|
@ -175,6 +175,9 @@ enum {
|
|||||||
/// Arguments used for Ex commands.
|
/// Arguments used for Ex commands.
|
||||||
struct exarg {
|
struct exarg {
|
||||||
char *arg; ///< argument of the command
|
char *arg; ///< argument of the command
|
||||||
|
char **args; ///< starting position of command arguments
|
||||||
|
size_t *arglens; ///< length of command arguments
|
||||||
|
size_t argc; ///< number of command arguments
|
||||||
char *nextcmd; ///< next command (NULL if none)
|
char *nextcmd; ///< next command (NULL if none)
|
||||||
char *cmd; ///< the name of the command (except for :make)
|
char *cmd; ///< the name of the command (except for :make)
|
||||||
char **cmdlinep; ///< pointer to pointer of allocated cmdline
|
char **cmdlinep; ///< pointer to pointer of allocated cmdline
|
||||||
|
@ -1218,7 +1218,7 @@ static char *skip_colon_white(const char *p, bool skipleadingwhite)
|
|||||||
/// Set the addr type for command
|
/// Set the addr type for command
|
||||||
///
|
///
|
||||||
/// @param p pointer to character after command name in cmdline
|
/// @param p pointer to character after command name in cmdline
|
||||||
static void set_cmd_addr_type(exarg_T *eap, char_u *p)
|
void set_cmd_addr_type(exarg_T *eap, char_u *p)
|
||||||
{
|
{
|
||||||
// ea.addr_type for user commands is set by find_ucmd
|
// ea.addr_type for user commands is set by find_ucmd
|
||||||
if (IS_USER_CMDIDX(eap->cmdidx)) {
|
if (IS_USER_CMDIDX(eap->cmdidx)) {
|
||||||
@ -1239,8 +1239,48 @@ static void set_cmd_addr_type(exarg_T *eap, char_u *p)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get default range number for command based on its address type
|
||||||
|
linenr_T get_cmd_default_range(exarg_T *eap)
|
||||||
|
{
|
||||||
|
switch (eap->addr_type) {
|
||||||
|
case ADDR_LINES:
|
||||||
|
case ADDR_OTHER:
|
||||||
|
// Default is the cursor line number. Avoid using an invalid
|
||||||
|
// line number though.
|
||||||
|
return MIN(curwin->w_cursor.lnum, curbuf->b_ml.ml_line_count);
|
||||||
|
break;
|
||||||
|
case ADDR_WINDOWS:
|
||||||
|
return CURRENT_WIN_NR;
|
||||||
|
break;
|
||||||
|
case ADDR_ARGUMENTS:
|
||||||
|
return MIN(curwin->w_arg_idx + 1, ARGCOUNT);
|
||||||
|
break;
|
||||||
|
case ADDR_LOADED_BUFFERS:
|
||||||
|
case ADDR_BUFFERS:
|
||||||
|
return curbuf->b_fnum;
|
||||||
|
break;
|
||||||
|
case ADDR_TABS:
|
||||||
|
return CURRENT_TAB_NR;
|
||||||
|
break;
|
||||||
|
case ADDR_TABS_RELATIVE:
|
||||||
|
case ADDR_UNSIGNED:
|
||||||
|
return 1;
|
||||||
|
break;
|
||||||
|
case ADDR_QUICKFIX:
|
||||||
|
return (linenr_T)qf_get_cur_idx(eap);
|
||||||
|
break;
|
||||||
|
case ADDR_QUICKFIX_VALID:
|
||||||
|
return qf_get_cur_valid_idx(eap);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
// Will give an error later if a range is found.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Set default command range for -range=% based on the addr type of the command
|
/// Set default command range for -range=% based on the addr type of the command
|
||||||
static void set_cmd_default_range(exarg_T *eap)
|
void set_cmd_dflall_range(exarg_T *eap)
|
||||||
{
|
{
|
||||||
buf_T *buf;
|
buf_T *buf;
|
||||||
|
|
||||||
@ -1320,6 +1360,25 @@ static void parse_register(exarg_T *eap)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Change line1 and line2 of Ex command to use count
|
||||||
|
void set_cmd_count(exarg_T *eap, long count, bool validate)
|
||||||
|
{
|
||||||
|
if (eap->addr_type != ADDR_LINES) { // e.g. :buffer 2, :sleep 3
|
||||||
|
eap->line2 = count;
|
||||||
|
if (eap->addr_count == 0) {
|
||||||
|
eap->addr_count = 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
eap->line1 = eap->line2;
|
||||||
|
eap->line2 += count - 1;
|
||||||
|
eap->addr_count++;
|
||||||
|
// Be vi compatible: no error message for out of range.
|
||||||
|
if (validate && eap->line2 > curbuf->b_ml.ml_line_count) {
|
||||||
|
eap->line2 = curbuf->b_ml.ml_line_count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static int parse_count(exarg_T *eap, char **errormsg, bool validate)
|
static int parse_count(exarg_T *eap, char **errormsg, bool validate)
|
||||||
{
|
{
|
||||||
// Check for a count. When accepting a EX_BUFNAME, don't use "123foo" as a
|
// Check for a count. When accepting a EX_BUFNAME, don't use "123foo" as a
|
||||||
@ -1338,25 +1397,19 @@ static int parse_count(exarg_T *eap, char **errormsg, bool validate)
|
|||||||
}
|
}
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
if (eap->addr_type != ADDR_LINES) { // e.g. :buffer 2, :sleep 3
|
set_cmd_count(eap, n, validate);
|
||||||
eap->line2 = n;
|
|
||||||
if (eap->addr_count == 0) {
|
|
||||||
eap->addr_count = 1;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
eap->line1 = eap->line2;
|
|
||||||
eap->line2 += n - 1;
|
|
||||||
eap->addr_count++;
|
|
||||||
// Be vi compatible: no error message for out of range.
|
|
||||||
if (validate && eap->line2 > curbuf->b_ml.ml_line_count) {
|
|
||||||
eap->line2 = curbuf->b_ml.ml_line_count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Check if command is not implemented
|
||||||
|
bool is_cmd_ni(cmdidx_T cmdidx)
|
||||||
|
{
|
||||||
|
return !IS_USER_CMDIDX(cmdidx) && (cmdnames[cmdidx].cmd_func == ex_ni
|
||||||
|
|| cmdnames[cmdidx].cmd_func == ex_script_ni);
|
||||||
|
}
|
||||||
|
|
||||||
/// Parse command line and return information about the first command.
|
/// Parse command line and return information about the first command.
|
||||||
///
|
///
|
||||||
/// @param cmdline Command line string
|
/// @param cmdline Command line string
|
||||||
@ -1394,14 +1447,17 @@ bool parse_cmdline(char *cmdline, exarg_T *eap, CmdParseInfo *cmdinfo, char **er
|
|||||||
if (eap->save_msg_silent != -1) {
|
if (eap->save_msg_silent != -1) {
|
||||||
cmdinfo->silent = !!msg_silent;
|
cmdinfo->silent = !!msg_silent;
|
||||||
msg_silent = eap->save_msg_silent;
|
msg_silent = eap->save_msg_silent;
|
||||||
|
eap->save_msg_silent = -1;
|
||||||
}
|
}
|
||||||
if (eap->did_esilent) {
|
if (eap->did_esilent) {
|
||||||
cmdinfo->emsg_silent = true;
|
cmdinfo->emsg_silent = true;
|
||||||
emsg_silent--;
|
emsg_silent--;
|
||||||
|
eap->did_esilent = false;
|
||||||
}
|
}
|
||||||
if (eap->did_sandbox) {
|
if (eap->did_sandbox) {
|
||||||
cmdinfo->sandbox = true;
|
cmdinfo->sandbox = true;
|
||||||
sandbox--;
|
sandbox--;
|
||||||
|
eap->did_sandbox = false;
|
||||||
}
|
}
|
||||||
if (cmdmod.save_ei != NULL) {
|
if (cmdmod.save_ei != NULL) {
|
||||||
cmdinfo->noautocmd = true;
|
cmdinfo->noautocmd = true;
|
||||||
@ -1411,6 +1467,7 @@ bool parse_cmdline(char *cmdline, exarg_T *eap, CmdParseInfo *cmdinfo, char **er
|
|||||||
if (eap->verbose_save != -1) {
|
if (eap->verbose_save != -1) {
|
||||||
cmdinfo->verbose = p_verbose;
|
cmdinfo->verbose = p_verbose;
|
||||||
p_verbose = eap->verbose_save;
|
p_verbose = eap->verbose_save;
|
||||||
|
eap->verbose_save = -1;
|
||||||
} else {
|
} else {
|
||||||
cmdinfo->verbose = -1;
|
cmdinfo->verbose = -1;
|
||||||
}
|
}
|
||||||
@ -1424,7 +1481,7 @@ bool parse_cmdline(char *cmdline, exarg_T *eap, CmdParseInfo *cmdinfo, char **er
|
|||||||
if (*eap->cmd == '*') {
|
if (*eap->cmd == '*') {
|
||||||
eap->cmd = skipwhite(eap->cmd + 1);
|
eap->cmd = skipwhite(eap->cmd + 1);
|
||||||
}
|
}
|
||||||
p = find_command(eap, NULL);
|
p = find_ex_command(eap, NULL);
|
||||||
|
|
||||||
// Set command address type and parse command range
|
// Set command address type and parse command range
|
||||||
set_cmd_addr_type(eap, (char_u *)p);
|
set_cmd_addr_type(eap, (char_u *)p);
|
||||||
@ -1494,7 +1551,7 @@ bool parse_cmdline(char *cmdline, exarg_T *eap, CmdParseInfo *cmdinfo, char **er
|
|||||||
}
|
}
|
||||||
// Set default range for command if required
|
// Set default range for command if required
|
||||||
if ((eap->argt & EX_DFLALL) && eap->addr_count == 0) {
|
if ((eap->argt & EX_DFLALL) && eap->addr_count == 0) {
|
||||||
set_cmd_default_range(eap);
|
set_cmd_dflall_range(eap);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse register and count
|
// Parse register and count
|
||||||
@ -1519,6 +1576,148 @@ bool parse_cmdline(char *cmdline, exarg_T *eap, CmdParseInfo *cmdinfo, char **er
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Execute an Ex command using parsed command line information.
|
||||||
|
/// Does not do any validation of the Ex command arguments.
|
||||||
|
///
|
||||||
|
/// @param eap Ex-command arguments
|
||||||
|
/// @param cmdinfo Command parse information
|
||||||
|
void execute_cmd(exarg_T *eap, CmdParseInfo *cmdinfo)
|
||||||
|
{
|
||||||
|
#define ERROR(msg) \
|
||||||
|
do { \
|
||||||
|
emsg(msg); \
|
||||||
|
goto end; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
char *errormsg = NULL;
|
||||||
|
cmdmod_T save_cmdmod = cmdmod;
|
||||||
|
cmdmod = cmdinfo->cmdmod;
|
||||||
|
|
||||||
|
// Apply command modifiers
|
||||||
|
if (cmdinfo->silent) {
|
||||||
|
eap->save_msg_silent = msg_silent;
|
||||||
|
msg_silent++;
|
||||||
|
}
|
||||||
|
if (cmdinfo->emsg_silent) {
|
||||||
|
eap->did_esilent = true;
|
||||||
|
emsg_silent++;
|
||||||
|
}
|
||||||
|
if (cmdinfo->sandbox) {
|
||||||
|
eap->did_sandbox = true;
|
||||||
|
sandbox++;
|
||||||
|
}
|
||||||
|
if (cmdinfo->noautocmd) {
|
||||||
|
cmdmod.save_ei = (char *)vim_strsave(p_ei);
|
||||||
|
set_string_option_direct("ei", -1, (char_u *)"all", OPT_FREE, SID_NONE);
|
||||||
|
}
|
||||||
|
if (cmdinfo->verbose != -1) {
|
||||||
|
eap->verbose_save = p_verbose;
|
||||||
|
p_verbose = cmdinfo->verbose;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!MODIFIABLE(curbuf) && (eap->argt & EX_MODIFY)
|
||||||
|
// allow :put in terminals
|
||||||
|
&& !(curbuf->terminal && eap->cmdidx == CMD_put)) {
|
||||||
|
ERROR(_(e_modifiable));
|
||||||
|
}
|
||||||
|
if (text_locked() && !(eap->argt & EX_CMDWIN)
|
||||||
|
&& !IS_USER_CMDIDX(eap->cmdidx)) {
|
||||||
|
ERROR(_(get_text_locked_msg()));
|
||||||
|
}
|
||||||
|
// Disallow editing another buffer when "curbuf->b_ro_locked" is set.
|
||||||
|
// Do allow ":checktime" (it is postponed).
|
||||||
|
// Do allow ":edit" (check for an argument later).
|
||||||
|
// Do allow ":file" with no arguments
|
||||||
|
if (!(eap->argt & EX_CMDWIN)
|
||||||
|
&& eap->cmdidx != CMD_checktime
|
||||||
|
&& eap->cmdidx != CMD_edit
|
||||||
|
&& !(eap->cmdidx == CMD_file && *eap->arg == NUL)
|
||||||
|
&& !IS_USER_CMDIDX(eap->cmdidx)
|
||||||
|
&& curbuf_locked()) {
|
||||||
|
ERROR(_(e_cannot_edit_other_buf));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (((eap->argt & EX_WHOLEFOLD) || eap->addr_count >= 2) && !global_busy
|
||||||
|
&& eap->addr_type == ADDR_LINES) {
|
||||||
|
// Put the first line at the start of a closed fold, put the last line
|
||||||
|
// at the end of a closed fold.
|
||||||
|
(void)hasFolding(eap->line1, &eap->line1, NULL);
|
||||||
|
(void)hasFolding(eap->line2, NULL, &eap->line2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If filename expansion is enabled, expand filenames
|
||||||
|
if (cmdinfo->magic.file) {
|
||||||
|
if (expand_filename(eap, (char_u **)eap->cmdlinep, &errormsg) == FAIL) {
|
||||||
|
ERROR(errormsg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Accept buffer name. Cannot be used at the same time with a buffer
|
||||||
|
// number. Don't do this for a user command.
|
||||||
|
if ((eap->argt & EX_BUFNAME) && *eap->arg != NUL && eap->addr_count == 0
|
||||||
|
&& !IS_USER_CMDIDX(eap->cmdidx)) {
|
||||||
|
if (eap->args == NULL) {
|
||||||
|
// If argument positions are not specified, search the argument for the buffer name.
|
||||||
|
// :bdelete, :bwipeout and :bunload take several arguments, separated by spaces:
|
||||||
|
// find next space (skipping over escaped characters).
|
||||||
|
// The others take one argument: ignore trailing spaces.
|
||||||
|
char *p;
|
||||||
|
|
||||||
|
if (eap->cmdidx == CMD_bdelete || eap->cmdidx == CMD_bwipeout
|
||||||
|
|| eap->cmdidx == CMD_bunload) {
|
||||||
|
p = (char *)skiptowhite_esc((char_u *)eap->arg);
|
||||||
|
} else {
|
||||||
|
p = eap->arg + STRLEN(eap->arg);
|
||||||
|
while (p > eap->arg && ascii_iswhite(p[-1])) {
|
||||||
|
p--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
eap->line2 = buflist_findpat((char_u *)eap->arg, (char_u *)p, (eap->argt & EX_BUFUNL) != 0,
|
||||||
|
false, false);
|
||||||
|
eap->addr_count = 1;
|
||||||
|
eap->arg = skipwhite(p);
|
||||||
|
} else {
|
||||||
|
// If argument positions are specified, just use the first argument
|
||||||
|
eap->line2 = buflist_findpat((char_u *)eap->args[0],
|
||||||
|
(char_u *)(eap->args[0] + eap->arglens[0]),
|
||||||
|
(eap->argt & EX_BUFUNL) != 0, false, false);
|
||||||
|
eap->addr_count = 1;
|
||||||
|
// Shift each argument by 1
|
||||||
|
if (eap->args != NULL) {
|
||||||
|
for (size_t i = 0; i < eap->argc - 1; i++) {
|
||||||
|
eap->args[i] = eap->args[i + 1];
|
||||||
|
}
|
||||||
|
// Make the last argument point to the NUL terminator at the end of string
|
||||||
|
eap->args[eap->argc - 1] = eap->args[eap->argc - 1] + eap->arglens[eap->argc - 1];
|
||||||
|
eap->argc -= 1;
|
||||||
|
}
|
||||||
|
eap->arg = eap->args[0];
|
||||||
|
}
|
||||||
|
if (eap->line2 < 0) { // failed
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute the command
|
||||||
|
if (IS_USER_CMDIDX(eap->cmdidx)) {
|
||||||
|
// Execute a user-defined command.
|
||||||
|
do_ucmd(eap);
|
||||||
|
} else {
|
||||||
|
// Call the function to execute the command.
|
||||||
|
eap->errmsg = NULL;
|
||||||
|
(cmdnames[eap->cmdidx].cmd_func)(eap);
|
||||||
|
if (eap->errmsg != NULL) {
|
||||||
|
ERROR(_(eap->errmsg));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end:
|
||||||
|
// Undo command modifiers
|
||||||
|
undo_cmdmod(eap, msg_scroll);
|
||||||
|
cmdmod = save_cmdmod;
|
||||||
|
|
||||||
|
#undef ERROR
|
||||||
|
}
|
||||||
|
|
||||||
/// Execute one Ex command.
|
/// Execute one Ex command.
|
||||||
///
|
///
|
||||||
/// If 'sourcing' is TRUE, the command will be included in the error message.
|
/// If 'sourcing' is TRUE, the command will be included in the error message.
|
||||||
@ -1606,7 +1805,7 @@ static char *do_one_cmd(char **cmdlinep, int flags, cstack_T *cstack, LineGetter
|
|||||||
if (*ea.cmd == '*') {
|
if (*ea.cmd == '*') {
|
||||||
ea.cmd = skipwhite(ea.cmd + 1);
|
ea.cmd = skipwhite(ea.cmd + 1);
|
||||||
}
|
}
|
||||||
p = find_command(&ea, NULL);
|
p = find_ex_command(&ea, NULL);
|
||||||
|
|
||||||
// Count this line for profiling if skip is TRUE.
|
// Count this line for profiling if skip is TRUE.
|
||||||
if (do_profiling == PROF_YES
|
if (do_profiling == PROF_YES
|
||||||
@ -1732,7 +1931,7 @@ static char *do_one_cmd(char **cmdlinep, int flags, cstack_T *cstack, LineGetter
|
|||||||
xfree(p);
|
xfree(p);
|
||||||
// If the autocommands did something and didn't cause an error, try
|
// If the autocommands did something and didn't cause an error, try
|
||||||
// finding the command again.
|
// finding the command again.
|
||||||
p = (ret && !aborting()) ? find_command(&ea, NULL) : ea.cmd;
|
p = (ret && !aborting()) ? find_ex_command(&ea, NULL) : ea.cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (p == NULL) {
|
if (p == NULL) {
|
||||||
@ -1759,10 +1958,7 @@ static char *do_one_cmd(char **cmdlinep, int flags, cstack_T *cstack, LineGetter
|
|||||||
}
|
}
|
||||||
|
|
||||||
// set when Not Implemented
|
// set when Not Implemented
|
||||||
const int ni = !IS_USER_CMDIDX(ea.cmdidx)
|
const int ni = is_cmd_ni(ea.cmdidx);
|
||||||
&& (cmdnames[ea.cmdidx].cmd_func == ex_ni
|
|
||||||
|| cmdnames[ea.cmdidx].cmd_func == ex_script_ni);
|
|
||||||
|
|
||||||
|
|
||||||
// Forced commands.
|
// Forced commands.
|
||||||
if (*p == '!' && ea.cmdidx != CMD_substitute
|
if (*p == '!' && ea.cmdidx != CMD_substitute
|
||||||
@ -1976,7 +2172,7 @@ static char *do_one_cmd(char **cmdlinep, int flags, cstack_T *cstack, LineGetter
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ((ea.argt & EX_DFLALL) && ea.addr_count == 0) {
|
if ((ea.argt & EX_DFLALL) && ea.addr_count == 0) {
|
||||||
set_cmd_default_range(&ea);
|
set_cmd_dflall_range(&ea);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse register and count
|
// Parse register and count
|
||||||
@ -2544,6 +2740,7 @@ static void undo_cmdmod(const exarg_T *eap, int save_msg_scroll)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// Parse the address range, if any, in "eap".
|
/// Parse the address range, if any, in "eap".
|
||||||
/// May set the last search pattern, unless "silent" is true.
|
/// May set the last search pattern, unless "silent" is true.
|
||||||
///
|
///
|
||||||
@ -2557,47 +2754,7 @@ int parse_cmd_address(exarg_T *eap, char **errormsg, bool silent)
|
|||||||
// Repeat for all ',' or ';' separated addresses.
|
// Repeat for all ',' or ';' separated addresses.
|
||||||
for (;;) {
|
for (;;) {
|
||||||
eap->line1 = eap->line2;
|
eap->line1 = eap->line2;
|
||||||
switch (eap->addr_type) {
|
eap->line2 = get_cmd_default_range(eap);
|
||||||
case ADDR_LINES:
|
|
||||||
case ADDR_OTHER:
|
|
||||||
// Default is the cursor line number. Avoid using an invalid
|
|
||||||
// line number though.
|
|
||||||
if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count) {
|
|
||||||
eap->line2 = curbuf->b_ml.ml_line_count;
|
|
||||||
} else {
|
|
||||||
eap->line2 = curwin->w_cursor.lnum;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ADDR_WINDOWS:
|
|
||||||
eap->line2 = CURRENT_WIN_NR;
|
|
||||||
break;
|
|
||||||
case ADDR_ARGUMENTS:
|
|
||||||
eap->line2 = curwin->w_arg_idx + 1;
|
|
||||||
if (eap->line2 > ARGCOUNT) {
|
|
||||||
eap->line2 = ARGCOUNT;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ADDR_LOADED_BUFFERS:
|
|
||||||
case ADDR_BUFFERS:
|
|
||||||
eap->line2 = curbuf->b_fnum;
|
|
||||||
break;
|
|
||||||
case ADDR_TABS:
|
|
||||||
eap->line2 = CURRENT_TAB_NR;
|
|
||||||
break;
|
|
||||||
case ADDR_TABS_RELATIVE:
|
|
||||||
case ADDR_UNSIGNED:
|
|
||||||
eap->line2 = 1;
|
|
||||||
break;
|
|
||||||
case ADDR_QUICKFIX:
|
|
||||||
eap->line2 = (linenr_T)qf_get_cur_idx(eap);
|
|
||||||
break;
|
|
||||||
case ADDR_QUICKFIX_VALID:
|
|
||||||
eap->line2 = qf_get_cur_valid_idx(eap);
|
|
||||||
break;
|
|
||||||
case ADDR_NONE:
|
|
||||||
// Will give an error later if a range is found.
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
eap->cmd = skipwhite(eap->cmd);
|
eap->cmd = skipwhite(eap->cmd);
|
||||||
lnum = get_address(eap, &eap->cmd, eap->addr_type, eap->skip, silent,
|
lnum = get_address(eap, &eap->cmd, eap->addr_type, eap->skip, silent,
|
||||||
eap->addr_count == 0, address_count++);
|
eap->addr_count == 0, address_count++);
|
||||||
@ -2772,7 +2929,7 @@ static void append_command(char *cmd)
|
|||||||
/// "full" is set to TRUE if the whole command name matched.
|
/// "full" is set to TRUE if the whole command name matched.
|
||||||
///
|
///
|
||||||
/// @return NULL for an ambiguous user command.
|
/// @return NULL for an ambiguous user command.
|
||||||
static char *find_command(exarg_T *eap, int *full)
|
char *find_ex_command(exarg_T *eap, int *full)
|
||||||
FUNC_ATTR_NONNULL_ARG(1)
|
FUNC_ATTR_NONNULL_ARG(1)
|
||||||
{
|
{
|
||||||
int len;
|
int len;
|
||||||
@ -2859,7 +3016,7 @@ static char *find_command(exarg_T *eap, int *full)
|
|||||||
|
|
||||||
for (; (int)eap->cmdidx < CMD_SIZE;
|
for (; (int)eap->cmdidx < CMD_SIZE;
|
||||||
eap->cmdidx = (cmdidx_T)((int)eap->cmdidx + 1)) {
|
eap->cmdidx = (cmdidx_T)((int)eap->cmdidx + 1)) {
|
||||||
if (STRNCMP(cmdnames[(int)eap->cmdidx].cmd_name, (char *)eap->cmd,
|
if (STRNCMP(cmdnames[(int)eap->cmdidx].cmd_name, eap->cmd,
|
||||||
(size_t)len) == 0) {
|
(size_t)len) == 0) {
|
||||||
if (full != NULL
|
if (full != NULL
|
||||||
&& cmdnames[(int)eap->cmdidx].cmd_name[len] == NUL) {
|
&& cmdnames[(int)eap->cmdidx].cmd_name[len] == NUL) {
|
||||||
@ -3076,7 +3233,7 @@ int cmd_exists(const char *const name)
|
|||||||
ea.cmd = (char *)((*name == '2' || *name == '3') ? name + 1 : name);
|
ea.cmd = (char *)((*name == '2' || *name == '3') ? name + 1 : name);
|
||||||
ea.cmdidx = (cmdidx_T)0;
|
ea.cmdidx = (cmdidx_T)0;
|
||||||
int full = false;
|
int full = false;
|
||||||
p = find_command(&ea, &full);
|
p = find_ex_command(&ea, &full);
|
||||||
if (p == NULL) {
|
if (p == NULL) {
|
||||||
return 3;
|
return 3;
|
||||||
}
|
}
|
||||||
@ -3108,7 +3265,7 @@ void f_fullcommand(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
|
|
||||||
ea.cmd = (*name == '2' || *name == '3') ? name + 1 : name;
|
ea.cmd = (*name == '2' || *name == '3') ? name + 1 : name;
|
||||||
ea.cmdidx = (cmdidx_T)0;
|
ea.cmdidx = (cmdidx_T)0;
|
||||||
char *p = find_command(&ea, NULL);
|
char *p = find_ex_command(&ea, NULL);
|
||||||
if (p == NULL || ea.cmdidx == CMD_SIZE) {
|
if (p == NULL || ea.cmdidx == CMD_SIZE) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -3431,7 +3588,7 @@ const char *set_one_cmd_context(expand_T *xp, const char *buff)
|
|||||||
xp->xp_shell = TRUE;
|
xp->xp_shell = TRUE;
|
||||||
#endif
|
#endif
|
||||||
// When still after the command name expand executables.
|
// When still after the command name expand executables.
|
||||||
if ((char_u *)xp->xp_pattern == (char_u *)skipwhite(arg)) {
|
if (xp->xp_pattern == skipwhite(arg)) {
|
||||||
xp->xp_context = EXPAND_SHELLCMD;
|
xp->xp_context = EXPAND_SHELLCMD;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -4416,7 +4573,7 @@ static void ex_script_ni(exarg_T *eap)
|
|||||||
/// Check range in Ex command for validity.
|
/// Check range in Ex command for validity.
|
||||||
///
|
///
|
||||||
/// @return NULL when valid, error message when invalid.
|
/// @return NULL when valid, error message when invalid.
|
||||||
static char *invalid_range(exarg_T *eap)
|
char *invalid_range(exarg_T *eap)
|
||||||
{
|
{
|
||||||
buf_T *buf;
|
buf_T *buf;
|
||||||
if (eap->line1 < 0 || eap->line2 < 0 || eap->line1 > eap->line2) {
|
if (eap->line1 < 0 || eap->line2 < 0 || eap->line1 > eap->line2) {
|
||||||
@ -4535,7 +4692,7 @@ static char *skip_grep_pat(exarg_T *eap)
|
|||||||
|
|
||||||
/// For the ":make" and ":grep" commands insert the 'makeprg'/'grepprg' option
|
/// For the ":make" and ":grep" commands insert the 'makeprg'/'grepprg' option
|
||||||
/// in the command line, so that things like % get expanded.
|
/// in the command line, so that things like % get expanded.
|
||||||
static char *replace_makeprg(exarg_T *eap, char *p, char **cmdlinep)
|
char *replace_makeprg(exarg_T *eap, char *p, char **cmdlinep)
|
||||||
{
|
{
|
||||||
char *new_cmdline;
|
char *new_cmdline;
|
||||||
char *program;
|
char *program;
|
||||||
@ -4773,7 +4930,7 @@ int expand_filename(exarg_T *eap, char_u **cmdlinep, char **errormsgp)
|
|||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Replace part of the command line, keeping eap->cmd, eap->arg and
|
/// Replace part of the command line, keeping eap->cmd, eap->arg, eap->args and
|
||||||
/// eap->nextcmd correct.
|
/// eap->nextcmd correct.
|
||||||
/// "src" points to the part that is to be replaced, of length "srclen".
|
/// "src" points to the part that is to be replaced, of length "srclen".
|
||||||
/// "repl" is the replacement string.
|
/// "repl" is the replacement string.
|
||||||
@ -4792,6 +4949,7 @@ static char *repl_cmdline(exarg_T *eap, char *src, size_t srclen, char *repl, ch
|
|||||||
i += STRLEN(eap->nextcmd); // add space for next command
|
i += STRLEN(eap->nextcmd); // add space for next command
|
||||||
}
|
}
|
||||||
char *new_cmdline = xmalloc(i);
|
char *new_cmdline = xmalloc(i);
|
||||||
|
size_t offset = (size_t)(src - *cmdlinep);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copy the stuff before the expanded part.
|
* Copy the stuff before the expanded part.
|
||||||
@ -4799,7 +4957,7 @@ static char *repl_cmdline(exarg_T *eap, char *src, size_t srclen, char *repl, ch
|
|||||||
* Copy what came after the expanded part.
|
* Copy what came after the expanded part.
|
||||||
* Copy the next commands, if there are any.
|
* Copy the next commands, if there are any.
|
||||||
*/
|
*/
|
||||||
i = (size_t)(src - *cmdlinep); // length of part before match
|
i = offset; // length of part before match
|
||||||
memmove(new_cmdline, *cmdlinep, i);
|
memmove(new_cmdline, *cmdlinep, i);
|
||||||
|
|
||||||
memmove(new_cmdline + i, repl, len);
|
memmove(new_cmdline + i, repl, len);
|
||||||
@ -4814,6 +4972,19 @@ static char *repl_cmdline(exarg_T *eap, char *src, size_t srclen, char *repl, ch
|
|||||||
}
|
}
|
||||||
eap->cmd = new_cmdline + (eap->cmd - *cmdlinep);
|
eap->cmd = new_cmdline + (eap->cmd - *cmdlinep);
|
||||||
eap->arg = new_cmdline + (eap->arg - *cmdlinep);
|
eap->arg = new_cmdline + (eap->arg - *cmdlinep);
|
||||||
|
|
||||||
|
for (size_t j = 0; j < eap->argc; j++) {
|
||||||
|
if (offset >= (size_t)(eap->args[j] - *cmdlinep)) {
|
||||||
|
// If replaced text is after or in the same position as the argument,
|
||||||
|
// the argument's position relative to the beginning of the cmdline stays the same.
|
||||||
|
eap->args[j] = new_cmdline + (eap->args[j] - *cmdlinep);
|
||||||
|
} else {
|
||||||
|
// Otherwise, argument gets shifted alongside the replaced text.
|
||||||
|
// The amount of the shift is equal to the difference of the old and new string length.
|
||||||
|
eap->args[j] = new_cmdline + (eap->args[j] - *cmdlinep) + (len - srclen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (eap->do_ecmd_cmd != NULL && eap->do_ecmd_cmd != dollar_command) {
|
if (eap->do_ecmd_cmd != NULL && eap->do_ecmd_cmd != dollar_command) {
|
||||||
eap->do_ecmd_cmd = new_cmdline + (eap->do_ecmd_cmd - *cmdlinep);
|
eap->do_ecmd_cmd = new_cmdline + (eap->do_ecmd_cmd - *cmdlinep);
|
||||||
}
|
}
|
||||||
@ -6002,7 +6173,7 @@ bool uc_split_args_iter(const char *arg, size_t arglen, size_t *end, char *buf,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// split and quote args for <f-args>
|
/// split and quote args for <f-args>
|
||||||
static char *uc_split_args(char *arg, size_t *lenp)
|
static char *uc_split_args(char *arg, char **args, size_t *arglens, size_t argc, size_t *lenp)
|
||||||
{
|
{
|
||||||
char *buf;
|
char *buf;
|
||||||
char *p;
|
char *p;
|
||||||
@ -6010,61 +6181,107 @@ static char *uc_split_args(char *arg, size_t *lenp)
|
|||||||
int len;
|
int len;
|
||||||
|
|
||||||
// Precalculate length
|
// Precalculate length
|
||||||
p = arg;
|
|
||||||
len = 2; // Initial and final quotes
|
len = 2; // Initial and final quotes
|
||||||
|
if (args == NULL) {
|
||||||
|
p = arg;
|
||||||
|
|
||||||
while (*p) {
|
while (*p) {
|
||||||
if (p[0] == '\\' && p[1] == '\\') {
|
if (p[0] == '\\' && p[1] == '\\') {
|
||||||
len += 2;
|
len += 2;
|
||||||
p += 2;
|
p += 2;
|
||||||
} else if (p[0] == '\\' && ascii_iswhite(p[1])) {
|
} else if (p[0] == '\\' && ascii_iswhite(p[1])) {
|
||||||
len += 1;
|
len += 1;
|
||||||
p += 2;
|
p += 2;
|
||||||
} else if (*p == '\\' || *p == '"') {
|
} else if (*p == '\\' || *p == '"') {
|
||||||
len += 2;
|
len += 2;
|
||||||
p += 1;
|
p += 1;
|
||||||
} else if (ascii_iswhite(*p)) {
|
} else if (ascii_iswhite(*p)) {
|
||||||
p = skipwhite(p);
|
p = skipwhite(p);
|
||||||
if (*p == NUL) {
|
if (*p == NUL) {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
len += 3; // ","
|
||||||
|
} else {
|
||||||
|
const int charlen = utfc_ptr2len(p);
|
||||||
|
|
||||||
|
len += charlen;
|
||||||
|
p += charlen;
|
||||||
}
|
}
|
||||||
len += 3; // ","
|
}
|
||||||
} else {
|
} else {
|
||||||
const int charlen = utfc_ptr2len(p);
|
for (size_t i = 0; i < argc; i++) {
|
||||||
|
p = args[i];
|
||||||
|
const char *arg_end = args[i] + arglens[i];
|
||||||
|
|
||||||
len += charlen;
|
while (p < arg_end) {
|
||||||
p += charlen;
|
if (*p == '\\' || *p == '"') {
|
||||||
|
len += 2;
|
||||||
|
p += 1;
|
||||||
|
} else {
|
||||||
|
const int charlen = utfc_ptr2len(p);
|
||||||
|
|
||||||
|
len += charlen;
|
||||||
|
p += charlen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i != argc - 1) {
|
||||||
|
len += 3; // ","
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
buf = xmalloc((size_t)len + 1);
|
buf = xmalloc((size_t)len + 1);
|
||||||
|
|
||||||
p = arg;
|
|
||||||
q = buf;
|
q = buf;
|
||||||
*q++ = '"';
|
*q++ = '"';
|
||||||
while (*p) {
|
|
||||||
if (p[0] == '\\' && p[1] == '\\') {
|
if (args == NULL) {
|
||||||
*q++ = '\\';
|
p = arg;
|
||||||
*q++ = '\\';
|
while (*p) {
|
||||||
p += 2;
|
if (p[0] == '\\' && p[1] == '\\') {
|
||||||
} else if (p[0] == '\\' && ascii_iswhite(p[1])) {
|
*q++ = '\\';
|
||||||
*q++ = p[1];
|
*q++ = '\\';
|
||||||
p += 2;
|
p += 2;
|
||||||
} else if (*p == '\\' || *p == '"') {
|
} else if (p[0] == '\\' && ascii_iswhite(p[1])) {
|
||||||
*q++ = '\\';
|
*q++ = p[1];
|
||||||
*q++ = *p++;
|
p += 2;
|
||||||
} else if (ascii_iswhite(*p)) {
|
} else if (*p == '\\' || *p == '"') {
|
||||||
p = skipwhite(p);
|
*q++ = '\\';
|
||||||
if (*p == NUL) {
|
*q++ = *p++;
|
||||||
break;
|
} else if (ascii_iswhite(*p)) {
|
||||||
|
p = skipwhite(p);
|
||||||
|
if (*p == NUL) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
*q++ = '"';
|
||||||
|
*q++ = ',';
|
||||||
|
*q++ = '"';
|
||||||
|
} else {
|
||||||
|
mb_copy_char((const char_u **)&p, (char_u **)&q);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (size_t i = 0; i < argc; i++) {
|
||||||
|
p = args[i];
|
||||||
|
const char *arg_end = args[i] + arglens[i];
|
||||||
|
|
||||||
|
while (p < arg_end) {
|
||||||
|
if (*p == '\\' || *p == '"') {
|
||||||
|
*q++ = '\\';
|
||||||
|
*q++ = *p++;
|
||||||
|
} else {
|
||||||
|
mb_copy_char((const char_u **)&p, (char_u **)&q);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (i != argc - 1) {
|
||||||
|
*q++ = '"';
|
||||||
|
*q++ = ',';
|
||||||
|
*q++ = '"';
|
||||||
}
|
}
|
||||||
*q++ = '"';
|
|
||||||
*q++ = ',';
|
|
||||||
*q++ = '"';
|
|
||||||
} else {
|
|
||||||
mb_copy_char((const char_u **)&p, (char_u **)&q);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
*q++ = '"';
|
*q++ = '"';
|
||||||
*q = 0;
|
*q = 0;
|
||||||
|
|
||||||
@ -6201,7 +6418,7 @@ static size_t uc_check_code(char *code, size_t len, char *buf, ucmd_T *cmd, exar
|
|||||||
case 2: // Quote and split (<f-args>)
|
case 2: // Quote and split (<f-args>)
|
||||||
// This is hard, so only do it once, and cache the result
|
// This is hard, so only do it once, and cache the result
|
||||||
if (*split_buf == NULL) {
|
if (*split_buf == NULL) {
|
||||||
*split_buf = uc_split_args(eap->arg, split_len);
|
*split_buf = uc_split_args(eap->arg, eap->args, eap->arglens, eap->argc, split_len);
|
||||||
}
|
}
|
||||||
|
|
||||||
result = *split_len;
|
result = *split_len;
|
||||||
@ -9887,7 +10104,7 @@ bool cmd_can_preview(char *cmd)
|
|||||||
if (*ea.cmd == '*') {
|
if (*ea.cmd == '*') {
|
||||||
ea.cmd = skipwhite(ea.cmd + 1);
|
ea.cmd = skipwhite(ea.cmd + 1);
|
||||||
}
|
}
|
||||||
char *end = find_command(&ea, NULL);
|
char *end = find_ex_command(&ea, NULL);
|
||||||
|
|
||||||
switch (ea.cmdidx) {
|
switch (ea.cmdidx) {
|
||||||
case CMD_substitute:
|
case CMD_substitute:
|
||||||
@ -10278,3 +10495,9 @@ void verify_command(char *cmd)
|
|||||||
msg("` `.:.`.,:iii;;;;;;;;iii;;;:` `.`` "
|
msg("` `.:.`.,:iii;;;;;;;;iii;;;:` `.`` "
|
||||||
" `nW");
|
" `nW");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get argt of command with id
|
||||||
|
uint32_t get_cmd_argt(cmdidx_T cmdidx)
|
||||||
|
{
|
||||||
|
return cmdnames[(int)cmdidx].cmd_argt;
|
||||||
|
}
|
||||||
|
@ -33,6 +33,7 @@
|
|||||||
#include "nvim/func_attr.h"
|
#include "nvim/func_attr.h"
|
||||||
#include "nvim/garray.h"
|
#include "nvim/garray.h"
|
||||||
#include "nvim/getchar.h"
|
#include "nvim/getchar.h"
|
||||||
|
#include "nvim/globals.h"
|
||||||
#include "nvim/highlight.h"
|
#include "nvim/highlight.h"
|
||||||
#include "nvim/highlight_defs.h"
|
#include "nvim/highlight_defs.h"
|
||||||
#include "nvim/highlight_group.h"
|
#include "nvim/highlight_group.h"
|
||||||
@ -2522,7 +2523,7 @@ char *get_text_locked_msg(void)
|
|||||||
bool curbuf_locked(void)
|
bool curbuf_locked(void)
|
||||||
{
|
{
|
||||||
if (curbuf->b_ro_locked > 0) {
|
if (curbuf->b_ro_locked > 0) {
|
||||||
emsg(_("E788: Not allowed to edit another buffer now"));
|
emsg(_(e_cannot_edit_other_buf));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return allbuf_locked();
|
return allbuf_locked();
|
||||||
|
@ -77,7 +77,7 @@ function M.switcher(put, tab, maxlen, worst_buck_size)
|
|||||||
put "break;\n"
|
put "break;\n"
|
||||||
end
|
end
|
||||||
put " default: break;\n"
|
put " default: break;\n"
|
||||||
put " }\n "
|
put " }\n "
|
||||||
else
|
else
|
||||||
local startidx = #neworder
|
local startidx = #neworder
|
||||||
table.insert(neworder, posbuck[keys[1]][1])
|
table.insert(neworder, posbuck[keys[1]][1])
|
||||||
@ -85,7 +85,7 @@ function M.switcher(put, tab, maxlen, worst_buck_size)
|
|||||||
put("low = "..startidx.."; ")
|
put("low = "..startidx.."; ")
|
||||||
if bucky then put("high = "..endidx.."; ") end
|
if bucky then put("high = "..endidx.."; ") end
|
||||||
end
|
end
|
||||||
put " break;\n"
|
put "break;\n"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
put " default: break;\n"
|
put " default: break;\n"
|
||||||
@ -105,17 +105,23 @@ function M.hashy_hash(name, strings, access)
|
|||||||
end
|
end
|
||||||
local neworder = M.switcher(put, len_pos_buckets, maxlen, worst_buck_size)
|
local neworder = M.switcher(put, len_pos_buckets, maxlen, worst_buck_size)
|
||||||
if worst_buck_size > 1 then
|
if worst_buck_size > 1 then
|
||||||
error [[ not implemented yet ]] -- TODO(bfredl)
|
put ([[
|
||||||
|
for (int i = low; i < high; i++) {
|
||||||
|
if (!memcmp(str, ]]..access("i")..[[, len)) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
]])
|
||||||
else
|
else
|
||||||
put [[
|
put ([[
|
||||||
if (low < 0) {
|
if (low < 0 || memcmp(str, ]]..access("low")..[[, len)) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
]]
|
return low;
|
||||||
put("if(memcmp(str, "..access("low")..", len)) {\n return -1;\n }\n")
|
]])
|
||||||
put " return low;\n"
|
|
||||||
put "}\n\n"
|
|
||||||
end
|
end
|
||||||
|
put "}\n\n"
|
||||||
return neworder, table.concat(stats)
|
return neworder, table.concat(stats)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -997,6 +997,7 @@ EXTERN char e_listarg[] INIT(= N_("E686: Argument of %s must be a List"));
|
|||||||
EXTERN char e_unsupportedoption[] INIT(= N_("E519: Option not supported"));
|
EXTERN char e_unsupportedoption[] INIT(= N_("E519: Option not supported"));
|
||||||
EXTERN char e_fnametoolong[] INIT(= N_("E856: Filename too long"));
|
EXTERN char e_fnametoolong[] INIT(= N_("E856: Filename too long"));
|
||||||
EXTERN char e_float_as_string[] INIT(= N_("E806: using Float as a String"));
|
EXTERN char e_float_as_string[] INIT(= N_("E806: using Float as a String"));
|
||||||
|
EXTERN char e_cannot_edit_other_buf[] INIT(= N_("E788: Not allowed to edit another buffer now"));
|
||||||
|
|
||||||
EXTERN char e_autocmd_err[] INIT(= N_("E5500: autocmd has thrown an exception: %s"));
|
EXTERN char e_autocmd_err[] INIT(= N_("E5500: autocmd has thrown an exception: %s"));
|
||||||
EXTERN char e_cmdmap_err[] INIT(= N_("E5520: <Cmd> mapping must end with <CR>"));
|
EXTERN char e_cmdmap_err[] INIT(= N_("E5520: <Cmd> mapping must end with <CR>"));
|
||||||
|
@ -1863,8 +1863,8 @@ void nlua_do_ucmd(ucmd_T *cmd, exarg_T *eap)
|
|||||||
if (cmd->uc_argt & EX_NOSPC) {
|
if (cmd->uc_argt & EX_NOSPC) {
|
||||||
// Commands where nargs = 1 or "?" fargs is the same as args
|
// Commands where nargs = 1 or "?" fargs is the same as args
|
||||||
lua_rawseti(lstate, -2, 1);
|
lua_rawseti(lstate, -2, 1);
|
||||||
} else {
|
} else if (eap->args == NULL) {
|
||||||
// Commands with more than one possible argument we split
|
// For commands with more than one possible argument, split if argument list isn't available.
|
||||||
lua_pop(lstate, 1); // Pop the reference of opts.args
|
lua_pop(lstate, 1); // Pop the reference of opts.args
|
||||||
size_t length = STRLEN(eap->arg);
|
size_t length = STRLEN(eap->arg);
|
||||||
size_t end = 0;
|
size_t end = 0;
|
||||||
@ -1881,6 +1881,13 @@ void nlua_do_ucmd(ucmd_T *cmd, exarg_T *eap)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
xfree(buf);
|
xfree(buf);
|
||||||
|
} else {
|
||||||
|
// If argument list is available, just use it.
|
||||||
|
lua_pop(lstate, 1);
|
||||||
|
for (size_t i = 0; i < eap->argc; i++) {
|
||||||
|
lua_pushlstring(lstate, eap->args[i], eap->arglens[i]);
|
||||||
|
lua_rawseti(lstate, -2, (int)i + 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
lua_setfield(lstate, -2, "fargs");
|
lua_setfield(lstate, -2, "fargs");
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
local helpers = require('test.functional.helpers')(after_each)
|
local helpers = require('test.functional.helpers')(after_each)
|
||||||
local Screen = require('test.functional.ui.screen')
|
local Screen = require('test.functional.ui.screen')
|
||||||
|
local lfs = require('lfs')
|
||||||
|
|
||||||
local fmt = string.format
|
local fmt = string.format
|
||||||
local assert_alive = helpers.assert_alive
|
local assert_alive = helpers.assert_alive
|
||||||
@ -25,6 +26,7 @@ local tmpname = helpers.tmpname
|
|||||||
local write_file = helpers.write_file
|
local write_file = helpers.write_file
|
||||||
local exec_lua = helpers.exec_lua
|
local exec_lua = helpers.exec_lua
|
||||||
local exc_exec = helpers.exc_exec
|
local exc_exec = helpers.exc_exec
|
||||||
|
local insert = helpers.insert
|
||||||
|
|
||||||
local pcall_err = helpers.pcall_err
|
local pcall_err = helpers.pcall_err
|
||||||
local format_string = helpers.format_string
|
local format_string = helpers.format_string
|
||||||
@ -3473,4 +3475,145 @@ describe('API', function()
|
|||||||
pcall_err(meths.parse_cmd, '4,6Fubar', {}))
|
pcall_err(meths.parse_cmd, '4,6Fubar', {}))
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
describe('nvim_cmd', function()
|
||||||
|
it('works', function ()
|
||||||
|
meths.cmd({ cmd = "set", args = { "cursorline" } }, {})
|
||||||
|
eq(true, meths.get_option_value("cursorline", {}))
|
||||||
|
end)
|
||||||
|
it('captures output', function()
|
||||||
|
eq("foo", meths.cmd({ cmd = "echo", args = { '"foo"' } }, { output = true }))
|
||||||
|
end)
|
||||||
|
it('sets correct script context', function()
|
||||||
|
meths.cmd({ cmd = "set", args = { "cursorline" } }, {})
|
||||||
|
local str = meths.exec([[verbose set cursorline?]], true)
|
||||||
|
neq(nil, str:find("cursorline\n\tLast set from API client %(channel id %d+%)"))
|
||||||
|
end)
|
||||||
|
it('works with range', function()
|
||||||
|
insert [[
|
||||||
|
line1
|
||||||
|
line2
|
||||||
|
line3
|
||||||
|
line4
|
||||||
|
you didn't expect this
|
||||||
|
line5
|
||||||
|
line6
|
||||||
|
]]
|
||||||
|
meths.cmd({ cmd = "del", range = {2, 4} }, {})
|
||||||
|
expect [[
|
||||||
|
line1
|
||||||
|
you didn't expect this
|
||||||
|
line5
|
||||||
|
line6
|
||||||
|
]]
|
||||||
|
end)
|
||||||
|
it('works with count', function()
|
||||||
|
insert [[
|
||||||
|
line1
|
||||||
|
line2
|
||||||
|
line3
|
||||||
|
line4
|
||||||
|
you didn't expect this
|
||||||
|
line5
|
||||||
|
line6
|
||||||
|
]]
|
||||||
|
meths.cmd({ cmd = "del", range = { 2 }, count = 4 }, {})
|
||||||
|
expect [[
|
||||||
|
line1
|
||||||
|
line5
|
||||||
|
line6
|
||||||
|
]]
|
||||||
|
end)
|
||||||
|
it('works with register', function()
|
||||||
|
insert [[
|
||||||
|
line1
|
||||||
|
line2
|
||||||
|
line3
|
||||||
|
line4
|
||||||
|
you didn't expect this
|
||||||
|
line5
|
||||||
|
line6
|
||||||
|
]]
|
||||||
|
meths.cmd({ cmd = "del", range = { 2, 4 }, reg = 'a' }, {})
|
||||||
|
meths.exec("1put a", false)
|
||||||
|
expect [[
|
||||||
|
line1
|
||||||
|
line2
|
||||||
|
line3
|
||||||
|
line4
|
||||||
|
you didn't expect this
|
||||||
|
line5
|
||||||
|
line6
|
||||||
|
]]
|
||||||
|
end)
|
||||||
|
it('works with bang', function ()
|
||||||
|
meths.create_user_command("Foo", 'echo "<bang>"', { bang = true })
|
||||||
|
eq("!", meths.cmd({ cmd = "Foo", bang = true }, { output = true }))
|
||||||
|
eq("", meths.cmd({ cmd = "Foo", bang = false }, { output = true }))
|
||||||
|
end)
|
||||||
|
it('works with modifiers', function()
|
||||||
|
meths.create_user_command("Foo", 'set verbose', {})
|
||||||
|
eq(" verbose=1", meths.cmd({ cmd = "Foo", mods = { verbose = 1 } }, { output = true }))
|
||||||
|
eq(0, meths.get_option_value("verbose", {}))
|
||||||
|
end)
|
||||||
|
it('works with magic.file', function()
|
||||||
|
exec_lua([[
|
||||||
|
vim.api.nvim_create_user_command("Foo", function(opts)
|
||||||
|
vim.api.nvim_echo({{ opts.fargs[1] }}, false, {})
|
||||||
|
end, { nargs = 1 })
|
||||||
|
]])
|
||||||
|
eq(lfs.currentdir(),
|
||||||
|
meths.cmd({ cmd = "Foo", args = { '%:p:h' }, magic = { file = true } },
|
||||||
|
{ output = true }))
|
||||||
|
end)
|
||||||
|
it('splits arguments correctly', function()
|
||||||
|
meths.exec([[
|
||||||
|
function! FooFunc(...)
|
||||||
|
echo a:000
|
||||||
|
endfunction
|
||||||
|
]], false)
|
||||||
|
meths.create_user_command("Foo", "call FooFunc(<f-args>)", { nargs = '+' })
|
||||||
|
eq([=[['a quick', 'brown fox', 'jumps over the', 'lazy dog']]=],
|
||||||
|
meths.cmd({ cmd = "Foo", args = { "a quick", "brown fox", "jumps over the", "lazy dog"}},
|
||||||
|
{ output = true }))
|
||||||
|
eq([=[['test \ \\ \"""\', 'more\ tests\" ']]=],
|
||||||
|
meths.cmd({ cmd = "Foo", args = { [[test \ \\ \"""\]], [[more\ tests\" ]] } },
|
||||||
|
{ output = true }))
|
||||||
|
end)
|
||||||
|
it('splits arguments correctly for Lua callback', function()
|
||||||
|
meths.exec_lua([[
|
||||||
|
local function FooFunc(opts)
|
||||||
|
vim.pretty_print(opts.fargs)
|
||||||
|
end
|
||||||
|
|
||||||
|
vim.api.nvim_create_user_command("Foo", FooFunc, { nargs = '+' })
|
||||||
|
]], {})
|
||||||
|
eq([[{ "a quick", "brown fox", "jumps over the", "lazy dog" }]],
|
||||||
|
meths.cmd({ cmd = "Foo", args = { "a quick", "brown fox", "jumps over the", "lazy dog"}},
|
||||||
|
{ output = true }))
|
||||||
|
eq([[{ 'test \\ \\\\ \\"""\\', 'more\\ tests\\" ' }]],
|
||||||
|
meths.cmd({ cmd = "Foo", args = { [[test \ \\ \"""\]], [[more\ tests\" ]] } },
|
||||||
|
{ output = true }))
|
||||||
|
end)
|
||||||
|
it('works with buffer names', function()
|
||||||
|
command("edit foo.txt | edit bar.txt")
|
||||||
|
meths.cmd({ cmd = "buffer", args = { "foo.txt" } }, {})
|
||||||
|
eq("foo.txt", funcs.fnamemodify(meths.buf_get_name(0), ":t"))
|
||||||
|
meths.cmd({ cmd = "buffer", args = { "bar.txt" } }, {})
|
||||||
|
eq("bar.txt", funcs.fnamemodify(meths.buf_get_name(0), ":t"))
|
||||||
|
end)
|
||||||
|
it('triggers CmdUndefined event if command is not found', function()
|
||||||
|
meths.exec_lua([[
|
||||||
|
vim.api.nvim_create_autocmd("CmdUndefined",
|
||||||
|
{ pattern = "Foo",
|
||||||
|
callback = function()
|
||||||
|
vim.api.nvim_create_user_command("Foo", "echo 'foo'", {})
|
||||||
|
end
|
||||||
|
})
|
||||||
|
]], {})
|
||||||
|
eq("foo", meths.cmd({ cmd = "Foo" }, { output = true }))
|
||||||
|
end)
|
||||||
|
it('errors if command is not implemented', function()
|
||||||
|
eq("Command not implemented: popup", pcall_err(meths.cmd, { cmd = "popup" }, {}))
|
||||||
|
end)
|
||||||
|
end)
|
||||||
end)
|
end)
|
||||||
|
Loading…
Reference in New Issue
Block a user