mirror of
https://github.com/neovim/neovim.git
synced 2025-09-07 03:48:18 +00:00
feat(api): add nvim_parse_cmdline
Adds an API function to parse a command line string and get command information from it.
This commit is contained in:
@@ -28,6 +28,7 @@
|
||||
#include "nvim/eval/typval.h"
|
||||
#include "nvim/eval/userfunc.h"
|
||||
#include "nvim/ex_cmds2.h"
|
||||
#include "nvim/ex_cmds_defs.h"
|
||||
#include "nvim/ex_docmd.h"
|
||||
#include "nvim/file_search.h"
|
||||
#include "nvim/fileio.h"
|
||||
@@ -2460,3 +2461,203 @@ void nvim_del_user_command(String name, Error *err)
|
||||
{
|
||||
nvim_buf_del_user_command(-1, name, err);
|
||||
}
|
||||
|
||||
/// Parse command line.
|
||||
///
|
||||
/// Doesn't check the validity of command arguments.
|
||||
///
|
||||
/// @param str Command line string to parse. Cannot contain "\n".
|
||||
/// @param opts Optional parameters. Reserved for future use.
|
||||
/// @param[out] err Error details, if any.
|
||||
/// @return Dictionary containing command information, with these keys:
|
||||
/// - cmd: (string) Command name.
|
||||
/// - line1: (number) Starting line of command range. Only applicable if command can take a
|
||||
/// range.
|
||||
/// - line2: (number) Final line of command range. Only applicable if command can take a
|
||||
/// range.
|
||||
/// - bang: (boolean) Whether command contains a bang (!) modifier.
|
||||
/// - args: (array) Command arguments.
|
||||
/// - addr: (string) Value of |:command-addr|. Uses short name.
|
||||
/// - nargs: (string) Value of |:command-nargs|.
|
||||
/// - nextcmd: (string) Next command if there are multiple commands separated by a |:bar|.
|
||||
/// Empty if there isn't a next command.
|
||||
/// - magic: (dictionary) Which characters have special meaning in the command arguments.
|
||||
/// - file: (boolean) The command expands filenames. Which means characters such as "%",
|
||||
/// "#" and wildcards are expanded.
|
||||
/// - bar: (boolean) The "|" character is treated as a command separator and the double
|
||||
/// quote character (\") is treated as the start of a comment.
|
||||
/// - mods: (dictionary) |:command-modifiers|.
|
||||
/// - silent: (boolean) |:silent|.
|
||||
/// - emsg_silent: (boolean) |:silent!|.
|
||||
/// - sandbox: (boolean) |:sandbox|.
|
||||
/// - noautocmd: (boolean) |:noautocmd|.
|
||||
/// - browse: (boolean) |:browse|.
|
||||
/// - confirm: (boolean) |:confirm|.
|
||||
/// - hide: (boolean) |:hide|.
|
||||
/// - keepalt: (boolean) |:keepalt|.
|
||||
/// - keepjumps: (boolean) |:keepjumps|.
|
||||
/// - keepmarks: (boolean) |:keepmarks|.
|
||||
/// - keeppatterns: (boolean) |:keeppatterns|.
|
||||
/// - lockmarks: (boolean) |:lockmarks|.
|
||||
/// - noswapfile: (boolean) |:noswapfile|.
|
||||
/// - tab: (integer) |:tab|.
|
||||
/// - verbose: (integer) |:verbose|.
|
||||
/// - vertical: (boolean) |:vertical|.
|
||||
/// - split: (string) Split modifier string, is an empty string when there's no split
|
||||
/// modifier. If there is a split modifier it can be one of:
|
||||
/// - "aboveleft": |:aboveleft|.
|
||||
/// - "belowright": |:belowright|.
|
||||
/// - "topleft": |:topleft|.
|
||||
/// - "botright": |:botright|.
|
||||
Dictionary nvim_parse_cmd(String str, Dictionary opts, Error *err)
|
||||
FUNC_API_SINCE(10) FUNC_API_FAST
|
||||
{
|
||||
Dictionary result = ARRAY_DICT_INIT;
|
||||
|
||||
if (opts.size > 0) {
|
||||
api_set_error(err, kErrorTypeValidation, "opts dict isn't empty");
|
||||
return result;
|
||||
}
|
||||
|
||||
// Parse command line
|
||||
exarg_T ea;
|
||||
CmdParseInfo cmdinfo;
|
||||
char_u *cmdline = vim_strsave((char_u *)str.data);
|
||||
|
||||
if (!parse_cmdline(cmdline, &ea, &cmdinfo)) {
|
||||
api_set_error(err, kErrorTypeException, "Error while parsing command line");
|
||||
goto end;
|
||||
}
|
||||
|
||||
// Parse arguments
|
||||
Array args = ARRAY_DICT_INIT;
|
||||
size_t length = STRLEN(ea.arg);
|
||||
|
||||
// For nargs = 1 or '?', pass the entire argument list as a single argument,
|
||||
// otherwise split arguments by whitespace.
|
||||
if (ea.argt & EX_NOSPC) {
|
||||
if (*ea.arg != NUL) {
|
||||
ADD(args, STRING_OBJ(cstrn_to_string((char *)ea.arg, length)));
|
||||
}
|
||||
} else {
|
||||
size_t end = 0;
|
||||
size_t len = 0;
|
||||
char *buf = xcalloc(length, sizeof(char));
|
||||
bool done = false;
|
||||
|
||||
while (!done) {
|
||||
done = uc_split_args_iter(ea.arg, length, &end, buf, &len);
|
||||
if (len > 0) {
|
||||
ADD(args, STRING_OBJ(cstrn_to_string(buf, len)));
|
||||
}
|
||||
}
|
||||
|
||||
xfree(buf);
|
||||
}
|
||||
|
||||
if (ea.cmdidx == CMD_USER) {
|
||||
PUT(result, "cmd", CSTR_TO_OBJ((char *)USER_CMD(ea.useridx)->uc_name));
|
||||
} else if (ea.cmdidx == CMD_USER_BUF) {
|
||||
PUT(result, "cmd", CSTR_TO_OBJ((char *)USER_CMD_GA(&curbuf->b_ucmds, ea.useridx)->uc_name));
|
||||
} else {
|
||||
PUT(result, "cmd", CSTR_TO_OBJ((char *)get_command_name(NULL, ea.cmdidx)));
|
||||
}
|
||||
PUT(result, "line1", INTEGER_OBJ(ea.line1));
|
||||
PUT(result, "line2", INTEGER_OBJ(ea.line2));
|
||||
PUT(result, "bang", BOOLEAN_OBJ(ea.forceit));
|
||||
PUT(result, "args", ARRAY_OBJ(args));
|
||||
|
||||
char nargs[2];
|
||||
if (ea.argt & EX_EXTRA) {
|
||||
if (ea.argt & EX_NOSPC) {
|
||||
if (ea.argt & EX_NEEDARG) {
|
||||
nargs[0] = '1';
|
||||
} else {
|
||||
nargs[0] = '?';
|
||||
}
|
||||
} else if (ea.argt & EX_NEEDARG) {
|
||||
nargs[0] = '+';
|
||||
} else {
|
||||
nargs[0] = '*';
|
||||
}
|
||||
} else {
|
||||
nargs[0] = '0';
|
||||
}
|
||||
nargs[1] = '\0';
|
||||
PUT(result, "nargs", CSTR_TO_OBJ(nargs));
|
||||
|
||||
const char *addr;
|
||||
switch (ea.addr_type) {
|
||||
case ADDR_LINES:
|
||||
addr = "line";
|
||||
break;
|
||||
case ADDR_ARGUMENTS:
|
||||
addr = "arg";
|
||||
break;
|
||||
case ADDR_BUFFERS:
|
||||
addr = "buf";
|
||||
break;
|
||||
case ADDR_LOADED_BUFFERS:
|
||||
addr = "load";
|
||||
break;
|
||||
case ADDR_WINDOWS:
|
||||
addr = "win";
|
||||
break;
|
||||
case ADDR_TABS:
|
||||
addr = "tab";
|
||||
break;
|
||||
case ADDR_QUICKFIX:
|
||||
addr = "qf";
|
||||
break;
|
||||
case ADDR_NONE:
|
||||
addr = "none";
|
||||
break;
|
||||
default:
|
||||
addr = "?";
|
||||
break;
|
||||
}
|
||||
PUT(result, "addr", CSTR_TO_OBJ(addr));
|
||||
PUT(result, "nextcmd", CSTR_TO_OBJ((char *)ea.nextcmd));
|
||||
|
||||
Dictionary mods = ARRAY_DICT_INIT;
|
||||
PUT(mods, "silent", BOOLEAN_OBJ(cmdinfo.silent));
|
||||
PUT(mods, "emsg_silent", BOOLEAN_OBJ(cmdinfo.emsg_silent));
|
||||
PUT(mods, "sandbox", BOOLEAN_OBJ(cmdinfo.sandbox));
|
||||
PUT(mods, "noautocmd", BOOLEAN_OBJ(cmdinfo.noautocmd));
|
||||
PUT(mods, "tab", INTEGER_OBJ(cmdmod.tab));
|
||||
PUT(mods, "verbose", INTEGER_OBJ(cmdinfo.verbose));
|
||||
PUT(mods, "browse", BOOLEAN_OBJ(cmdmod.browse));
|
||||
PUT(mods, "confirm", BOOLEAN_OBJ(cmdmod.confirm));
|
||||
PUT(mods, "hide", BOOLEAN_OBJ(cmdmod.hide));
|
||||
PUT(mods, "keepalt", BOOLEAN_OBJ(cmdmod.keepalt));
|
||||
PUT(mods, "keepjumps", BOOLEAN_OBJ(cmdmod.keepjumps));
|
||||
PUT(mods, "keepmarks", BOOLEAN_OBJ(cmdmod.keepmarks));
|
||||
PUT(mods, "keeppatterns", BOOLEAN_OBJ(cmdmod.keeppatterns));
|
||||
PUT(mods, "lockmarks", BOOLEAN_OBJ(cmdmod.lockmarks));
|
||||
PUT(mods, "noswapfile", BOOLEAN_OBJ(cmdmod.noswapfile));
|
||||
PUT(mods, "vertical", BOOLEAN_OBJ(cmdmod.split & WSP_VERT));
|
||||
|
||||
const char *split;
|
||||
if (cmdmod.split & WSP_BOT) {
|
||||
split = "botright";
|
||||
} else if (cmdmod.split & WSP_TOP) {
|
||||
split = "topleft";
|
||||
} else if (cmdmod.split & WSP_BELOW) {
|
||||
split = "belowright";
|
||||
} else if (cmdmod.split & WSP_ABOVE) {
|
||||
split = "aboveleft";
|
||||
} else {
|
||||
split = "";
|
||||
}
|
||||
PUT(mods, "split", CSTR_TO_OBJ(split));
|
||||
|
||||
PUT(result, "mods", DICTIONARY_OBJ(mods));
|
||||
|
||||
Dictionary magic = ARRAY_DICT_INIT;
|
||||
PUT(magic, "file", BOOLEAN_OBJ(cmdinfo.magic.file));
|
||||
PUT(magic, "bar", BOOLEAN_OBJ(cmdinfo.magic.bar));
|
||||
PUT(result, "magic", DICTIONARY_OBJ(magic));
|
||||
end:
|
||||
xfree(cmdline);
|
||||
return result;
|
||||
}
|
||||
|
Reference in New Issue
Block a user