mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
Merge pull request #19133 from famiu/feat/api/cmd_support_filter
feat(api): make `nvim_parse_cmd` and `nvim_cmd` support :filter
This commit is contained in:
commit
21a1f1f552
@ -1944,6 +1944,11 @@ nvim_parse_cmd({str}, {opts}) *nvim_parse_cmd()*
|
||||
is treated as the start of a comment.
|
||||
|
||||
• mods: (dictionary) |:command-modifiers|.
|
||||
• filter: (dictionary) |:filter|.
|
||||
• pattern: (string) Filter pattern. Empty string if
|
||||
there is no filter.
|
||||
• force: (boolean) Whether filter is inverted or not.
|
||||
|
||||
• silent: (boolean) |:silent|.
|
||||
• emsg_silent: (boolean) |:silent!|.
|
||||
• sandbox: (boolean) |:sandbox|.
|
||||
|
@ -49,6 +49,9 @@
|
||||
/// - 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|.
|
||||
/// - filter: (dictionary) |:filter|.
|
||||
/// - pattern: (string) Filter pattern. Empty string if there is no filter.
|
||||
/// - force: (boolean) Whether filter is inverted or not.
|
||||
/// - silent: (boolean) |:silent|.
|
||||
/// - emsg_silent: (boolean) |:silent!|.
|
||||
/// - sandbox: (boolean) |:sandbox|.
|
||||
@ -95,7 +98,6 @@ Dictionary nvim_parse_cmd(String str, Dictionary opts, Error *err)
|
||||
}
|
||||
goto end;
|
||||
}
|
||||
vim_regfree(cmdinfo.cmdmod.cmod_filter_regmatch.regprog);
|
||||
|
||||
// Parse arguments
|
||||
Array args = ARRAY_DICT_INIT;
|
||||
@ -220,6 +222,14 @@ Dictionary nvim_parse_cmd(String str, Dictionary opts, Error *err)
|
||||
PUT(result, "nextcmd", CSTR_TO_OBJ((char *)ea.nextcmd));
|
||||
|
||||
Dictionary mods = ARRAY_DICT_INIT;
|
||||
|
||||
Dictionary filter = ARRAY_DICT_INIT;
|
||||
PUT(filter, "pattern", cmdinfo.cmdmod.cmod_filter_pat
|
||||
? CSTR_TO_OBJ(cmdinfo.cmdmod.cmod_filter_pat)
|
||||
: STRING_OBJ(STATIC_CSTR_TO_STRING("")));
|
||||
PUT(filter, "force", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_filter_force));
|
||||
PUT(mods, "filter", DICTIONARY_OBJ(filter));
|
||||
|
||||
PUT(mods, "silent", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_SILENT));
|
||||
PUT(mods, "emsg_silent", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_ERRSILENT));
|
||||
PUT(mods, "sandbox", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_SANDBOX));
|
||||
@ -257,6 +267,8 @@ Dictionary nvim_parse_cmd(String str, Dictionary opts, Error *err)
|
||||
PUT(magic, "file", BOOLEAN_OBJ(cmdinfo.magic.file));
|
||||
PUT(magic, "bar", BOOLEAN_OBJ(cmdinfo.magic.bar));
|
||||
PUT(result, "magic", DICTIONARY_OBJ(magic));
|
||||
|
||||
undo_cmdmod(&cmdinfo.cmdmod);
|
||||
end:
|
||||
xfree(cmdline);
|
||||
return result;
|
||||
@ -513,6 +525,35 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (HAS_KEY(mods.filter)) {
|
||||
if (mods.filter.type != kObjectTypeDictionary) {
|
||||
VALIDATION_ERROR("'mods.filter' must be a Dictionary");
|
||||
}
|
||||
|
||||
Dict(cmd_mods_filter) filter = { 0 };
|
||||
|
||||
if (!api_dict_to_keydict(&filter, KeyDict_cmd_mods_filter_get_field,
|
||||
mods.filter.data.dictionary, err)) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (HAS_KEY(filter.pattern)) {
|
||||
if (filter.pattern.type != kObjectTypeString) {
|
||||
VALIDATION_ERROR("'mods.filter.pattern' must be a String");
|
||||
}
|
||||
|
||||
OBJ_TO_BOOL(cmdinfo.cmdmod.cmod_filter_force, filter.force, false, "'mods.filter.force'");
|
||||
|
||||
// "filter! // is not no-op, so add a filter if either the pattern is non-empty or if filter
|
||||
// is inverted.
|
||||
if (*filter.pattern.data.string.data != NUL || cmdinfo.cmdmod.cmod_filter_force) {
|
||||
cmdinfo.cmdmod.cmod_filter_pat = string_to_cstr(filter.pattern.data.string);
|
||||
cmdinfo.cmdmod.cmod_filter_regmatch.regprog = vim_regcomp(cmdinfo.cmdmod.cmod_filter_pat,
|
||||
RE_MAGIC);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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");
|
||||
|
@ -181,6 +181,7 @@ return {
|
||||
cmd_mods = {
|
||||
"silent";
|
||||
"emsg_silent";
|
||||
"filter";
|
||||
"sandbox";
|
||||
"noautocmd";
|
||||
"browse";
|
||||
@ -197,6 +198,10 @@ return {
|
||||
"vertical";
|
||||
"split";
|
||||
};
|
||||
cmd_mods_filter = {
|
||||
"pattern";
|
||||
"force";
|
||||
};
|
||||
cmd_opts = {
|
||||
"output";
|
||||
};
|
||||
|
@ -268,6 +268,7 @@ typedef struct {
|
||||
|
||||
int cmod_split; ///< flags for win_split()
|
||||
int cmod_tab; ///< > 0 when ":tab" was used
|
||||
char *cmod_filter_pat;
|
||||
regmatch_T cmod_filter_regmatch; ///< set by :filter /pat/
|
||||
bool cmod_filter_force; ///< set for :filter!
|
||||
|
||||
|
@ -1405,8 +1405,8 @@ bool is_cmd_ni(cmdidx_T cmdidx)
|
||||
}
|
||||
|
||||
/// Parse command line and return information about the first command.
|
||||
/// If parsing is done successfully, need to free cmod_filter_regmatch.regprog after calling,
|
||||
/// usually done using undo_cmdmod() or execute_cmd().
|
||||
/// If parsing is done successfully, need to free cmod_filter_pat and cmod_filter_regmatch.regprog
|
||||
/// after calling, usually done using undo_cmdmod() or execute_cmd().
|
||||
///
|
||||
/// @param cmdline Command line string
|
||||
/// @param[out] eap Ex command arguments
|
||||
@ -1434,8 +1434,7 @@ bool parse_cmdline(char *cmdline, exarg_T *eap, CmdParseInfo *cmdinfo, char **er
|
||||
|
||||
// Parse command modifiers
|
||||
if (parse_command_modifiers(eap, errormsg, &cmdinfo->cmdmod, false) == FAIL) {
|
||||
vim_regfree(cmdinfo->cmdmod.cmod_filter_regmatch.regprog);
|
||||
return false;
|
||||
goto err;
|
||||
}
|
||||
after_modifier = eap->cmd;
|
||||
|
||||
@ -1449,21 +1448,21 @@ bool parse_cmdline(char *cmdline, exarg_T *eap, CmdParseInfo *cmdinfo, char **er
|
||||
p = find_ex_command(eap, NULL);
|
||||
if (p == NULL) {
|
||||
*errormsg = _(e_ambiguous_use_of_user_defined_command);
|
||||
return false;
|
||||
goto err;
|
||||
}
|
||||
|
||||
// Set command address type and parse command range
|
||||
set_cmd_addr_type(eap, p);
|
||||
eap->cmd = cmd;
|
||||
if (parse_cmd_address(eap, errormsg, false) == FAIL) {
|
||||
return false;
|
||||
goto err;
|
||||
}
|
||||
|
||||
// Skip colon and whitespace
|
||||
eap->cmd = skip_colon_white(eap->cmd, true);
|
||||
// Fail if command is a comment or if command doesn't exist
|
||||
if (*eap->cmd == NUL || *eap->cmd == '"') {
|
||||
return false;
|
||||
goto err;
|
||||
}
|
||||
// Fail if command is invalid
|
||||
if (eap->cmdidx == CMD_SIZE) {
|
||||
@ -1472,7 +1471,7 @@ bool parse_cmdline(char *cmdline, exarg_T *eap, CmdParseInfo *cmdinfo, char **er
|
||||
char *cmdname = after_modifier ? after_modifier : cmdline;
|
||||
append_command(cmdname);
|
||||
*errormsg = (char *)IObuff;
|
||||
return false;
|
||||
goto err;
|
||||
}
|
||||
|
||||
// Correctly set 'forceit' for commands
|
||||
@ -1511,12 +1510,12 @@ bool parse_cmdline(char *cmdline, exarg_T *eap, CmdParseInfo *cmdinfo, char **er
|
||||
// Fail if command doesn't support bang but is used with a bang
|
||||
if (!(eap->argt & EX_BANG) && eap->forceit) {
|
||||
*errormsg = _(e_nobang);
|
||||
return false;
|
||||
goto err;
|
||||
}
|
||||
// Fail if command doesn't support a range but it is given a range
|
||||
if (!(eap->argt & EX_RANGE) && eap->addr_count > 0) {
|
||||
*errormsg = _(e_norange);
|
||||
return false;
|
||||
goto err;
|
||||
}
|
||||
// Set default range for command if required
|
||||
if ((eap->argt & EX_DFLALL) && eap->addr_count == 0) {
|
||||
@ -1526,7 +1525,7 @@ bool parse_cmdline(char *cmdline, exarg_T *eap, CmdParseInfo *cmdinfo, char **er
|
||||
// Parse register and count
|
||||
parse_register(eap);
|
||||
if (parse_count(eap, errormsg, false) == FAIL) {
|
||||
return false;
|
||||
goto err;
|
||||
}
|
||||
|
||||
// Remove leading whitespace and colon from next command
|
||||
@ -1543,6 +1542,9 @@ bool parse_cmdline(char *cmdline, exarg_T *eap, CmdParseInfo *cmdinfo, char **er
|
||||
}
|
||||
|
||||
return true;
|
||||
err:
|
||||
undo_cmdmod(&cmdinfo->cmdmod);
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Execute an Ex command using parsed command line information.
|
||||
@ -2393,7 +2395,7 @@ char *ex_errmsg(const char *const msg, const char *const arg)
|
||||
/// - Set ex_pressedreturn for an empty command line.
|
||||
///
|
||||
/// @param skip_only if false, undo_cmdmod() must be called later to free
|
||||
/// any cmod_filter_regmatch.regprog.
|
||||
/// any cmod_filter_pat and cmod_filter_regmatch.regprog.
|
||||
/// @param[out] errormsg potential error message.
|
||||
///
|
||||
/// Call apply_cmdmod() to get the side effects of the modifiers:
|
||||
@ -2512,6 +2514,7 @@ int parse_command_modifiers(exarg_T *eap, char **errormsg, cmdmod_T *cmod, bool
|
||||
break;
|
||||
}
|
||||
if (!skip_only) {
|
||||
cmod->cmod_filter_pat = xstrdup(reg_pat);
|
||||
cmod->cmod_filter_regmatch.regprog = vim_regcomp(reg_pat, RE_MAGIC);
|
||||
if (cmod->cmod_filter_regmatch.regprog == NULL) {
|
||||
break;
|
||||
@ -2673,7 +2676,7 @@ static void apply_cmdmod(cmdmod_T *cmod)
|
||||
}
|
||||
|
||||
/// Undo and free contents of "cmod".
|
||||
static void undo_cmdmod(cmdmod_T *cmod)
|
||||
void undo_cmdmod(cmdmod_T *cmod)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
if (cmod->cmod_verbose_save > 0) {
|
||||
@ -2693,6 +2696,7 @@ static void undo_cmdmod(cmdmod_T *cmod)
|
||||
cmod->cmod_save_ei = NULL;
|
||||
}
|
||||
|
||||
xfree(cmod->cmod_filter_pat);
|
||||
vim_regfree(cmod->cmod_filter_regmatch.regprog);
|
||||
|
||||
if (cmod->cmod_save_msg_silent > 0) {
|
||||
|
@ -2404,7 +2404,7 @@ static bool cmdpreview_may_show(CommandLineState *s)
|
||||
|
||||
// Check if command is previewable, if not, don't attempt to show preview
|
||||
if (!(ea.argt & EX_PREVIEW)) {
|
||||
vim_regfree(cmdinfo.cmdmod.cmod_filter_regmatch.regprog);
|
||||
undo_cmdmod(&cmdinfo.cmdmod);
|
||||
goto end;
|
||||
}
|
||||
|
||||
|
@ -3167,6 +3167,10 @@ describe('API', function()
|
||||
browse = false,
|
||||
confirm = false,
|
||||
emsg_silent = false,
|
||||
filter = {
|
||||
pattern = "",
|
||||
force = false
|
||||
},
|
||||
hide = false,
|
||||
keepalt = false,
|
||||
keepjumps = false,
|
||||
@ -3203,6 +3207,10 @@ describe('API', function()
|
||||
browse = false,
|
||||
confirm = false,
|
||||
emsg_silent = false,
|
||||
filter = {
|
||||
pattern = "",
|
||||
force = false
|
||||
},
|
||||
hide = false,
|
||||
keepalt = false,
|
||||
keepjumps = false,
|
||||
@ -3239,6 +3247,10 @@ describe('API', function()
|
||||
browse = false,
|
||||
confirm = false,
|
||||
emsg_silent = false,
|
||||
filter = {
|
||||
pattern = "",
|
||||
force = false
|
||||
},
|
||||
hide = false,
|
||||
keepalt = false,
|
||||
keepjumps = false,
|
||||
@ -3275,6 +3287,10 @@ describe('API', function()
|
||||
browse = false,
|
||||
confirm = false,
|
||||
emsg_silent = false,
|
||||
filter = {
|
||||
pattern = "",
|
||||
force = false
|
||||
},
|
||||
hide = false,
|
||||
keepalt = false,
|
||||
keepjumps = false,
|
||||
@ -3311,6 +3327,10 @@ describe('API', function()
|
||||
browse = false,
|
||||
confirm = false,
|
||||
emsg_silent = false,
|
||||
filter = {
|
||||
pattern = "",
|
||||
force = false
|
||||
},
|
||||
hide = false,
|
||||
keepalt = false,
|
||||
keepjumps = false,
|
||||
@ -3347,6 +3367,10 @@ describe('API', function()
|
||||
browse = false,
|
||||
confirm = false,
|
||||
emsg_silent = false,
|
||||
filter = {
|
||||
pattern = "",
|
||||
force = false
|
||||
},
|
||||
hide = false,
|
||||
keepalt = false,
|
||||
keepjumps = false,
|
||||
@ -3383,6 +3407,10 @@ describe('API', function()
|
||||
browse = false,
|
||||
confirm = false,
|
||||
emsg_silent = true,
|
||||
filter = {
|
||||
pattern = "foo",
|
||||
force = false
|
||||
},
|
||||
hide = false,
|
||||
keepalt = false,
|
||||
keepjumps = false,
|
||||
@ -3398,7 +3426,45 @@ describe('API', function()
|
||||
tab = 2,
|
||||
verbose = 15
|
||||
},
|
||||
}, meths.parse_cmd('15verbose silent! aboveleft topleft tab split foo.txt', {}))
|
||||
}, meths.parse_cmd('15verbose silent! aboveleft topleft tab filter /foo/ split foo.txt', {}))
|
||||
eq({
|
||||
cmd = 'split',
|
||||
args = { 'foo.txt' },
|
||||
bang = false,
|
||||
range = {},
|
||||
count = -1,
|
||||
reg = '',
|
||||
addr = '?',
|
||||
magic = {
|
||||
file = true,
|
||||
bar = true
|
||||
},
|
||||
nargs = '?',
|
||||
nextcmd = '',
|
||||
mods = {
|
||||
browse = false,
|
||||
confirm = false,
|
||||
emsg_silent = false,
|
||||
filter = {
|
||||
pattern = "foo",
|
||||
force = true
|
||||
},
|
||||
hide = false,
|
||||
keepalt = false,
|
||||
keepjumps = false,
|
||||
keepmarks = false,
|
||||
keeppatterns = false,
|
||||
lockmarks = false,
|
||||
noautocmd = false,
|
||||
noswapfile = false,
|
||||
sandbox = false,
|
||||
silent = false,
|
||||
vertical = false,
|
||||
split = "",
|
||||
tab = 0,
|
||||
verbose = -1
|
||||
},
|
||||
}, meths.parse_cmd('filter! /foo/ split foo.txt', {}))
|
||||
end)
|
||||
it('works with user commands', function()
|
||||
command('command -bang -nargs=+ -range -addr=lines MyCommand echo foo')
|
||||
@ -3420,6 +3486,10 @@ describe('API', function()
|
||||
browse = false,
|
||||
confirm = false,
|
||||
emsg_silent = false,
|
||||
filter = {
|
||||
pattern = "",
|
||||
force = false
|
||||
},
|
||||
hide = false,
|
||||
keepalt = false,
|
||||
keepjumps = false,
|
||||
@ -3456,6 +3526,10 @@ describe('API', function()
|
||||
browse = false,
|
||||
confirm = false,
|
||||
emsg_silent = false,
|
||||
filter = {
|
||||
pattern = "",
|
||||
force = false
|
||||
},
|
||||
hide = false,
|
||||
keepalt = false,
|
||||
keepjumps = false,
|
||||
@ -3493,6 +3567,10 @@ describe('API', function()
|
||||
browse = false,
|
||||
confirm = false,
|
||||
emsg_silent = false,
|
||||
filter = {
|
||||
pattern = "",
|
||||
force = false
|
||||
},
|
||||
hide = false,
|
||||
keepalt = false,
|
||||
keepjumps = false,
|
||||
@ -3604,6 +3682,13 @@ describe('API', 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", {}))
|
||||
command('edit foo.txt | edit bar.txt')
|
||||
eq(' 1 #h "foo.txt" line 1',
|
||||
meths.cmd({ cmd = "buffers", mods = { filter = { pattern = "foo", force = false } } },
|
||||
{ output = true }))
|
||||
eq(' 2 %a "bar.txt" line 1',
|
||||
meths.cmd({ cmd = "buffers", mods = { filter = { pattern = "foo", force = true } } },
|
||||
{ output = true }))
|
||||
end)
|
||||
it('works with magic.file', function()
|
||||
exec_lua([[
|
||||
|
Loading…
Reference in New Issue
Block a user