mirror of
https://github.com/neovim/neovim.git
synced 2026-05-23 21:30:11 +00:00
refactor(excmd): pass fargs to Lua for builtin cmds #39528
Problem: The fallback that tokenizes `eap->arg` by unescaped whitespace (when the parser doesn't pre-split via `EX_EXPAND` etc.) lives in `nlua_do_ucmd`, so only user-command callbacks got `eap.fargs`. Builtin commands routed through `nlua_call_excmd` have to re-parse the args themselves (e.g. `M.ex_lsp`). Solution: - Move the tokenization into `nlua_push_eap` so every Lua handler sees `eap.fargs`. Keep only the `EX_NOSPC` override in `nlua_do_ucmd` (the `nargs=1`/`?` case which is genuinely user-command-specific). - Drop the re-parse in `M.ex_lsp`.
This commit is contained in:
@@ -174,19 +174,13 @@ local available_subcmds = vim.tbl_keys(actions)
|
||||
--- Implements command: `:lsp {subcmd} {name}?`.
|
||||
--- @param eap vim._core.ExCmdArgs
|
||||
function M.ex_lsp(eap)
|
||||
local fargs = api.nvim_parse_cmd('lsp ' .. eap.args, {}).args
|
||||
if not fargs then
|
||||
return
|
||||
end
|
||||
local subcmd = fargs[1]
|
||||
local subcmd = eap.fargs[1]
|
||||
if not vim.list_contains(available_subcmds, subcmd) then
|
||||
echo_err(N_('E5800: Invalid :lsp subcommand: %s'):format(subcmd))
|
||||
return
|
||||
end
|
||||
|
||||
local clients = { unpack(fargs, 2) }
|
||||
|
||||
actions[subcmd](clients)
|
||||
actions[subcmd]({ unpack(eap.fargs, 2) })
|
||||
end
|
||||
|
||||
--- Completion logic for `:lsp` command
|
||||
|
||||
@@ -52,7 +52,7 @@ end
|
||||
--- @param items string[] List of swapfile paths.
|
||||
function M.select_swap(items)
|
||||
vim.ui.select(items, {
|
||||
prompt = N_('Enter number of swap file to use (q or empty cancels):'),
|
||||
prompt = N_('Select a swapfile:'),
|
||||
kind = 'swap',
|
||||
format_item = format_swap,
|
||||
}, function(_, idx)
|
||||
|
||||
@@ -30,7 +30,7 @@ function M.select_tag(eap, extra)
|
||||
end
|
||||
|
||||
vim.ui.select(items, {
|
||||
prompt = N_('Type number and <Enter> (q or empty cancels):'),
|
||||
prompt = N_('Select a tag:'),
|
||||
kind = 'tag',
|
||||
format_item = function(m)
|
||||
local marker = m.cur and '>' or ' '
|
||||
|
||||
@@ -223,13 +223,35 @@ static void nlua_push_eap(lua_State *lstate, exarg_T *eap, const cmdmod_T *cmod)
|
||||
lua_setfield(lstate, -2, "reg");
|
||||
|
||||
// Push pre-split args as "fargs" list, if available (set by the command-line parser).
|
||||
if (eap->args != NULL && eap->argc > 0) {
|
||||
// - Or fall back to splitting `eap->arg` on unescaped whitespace.
|
||||
// - Usercmds with nargs=1/? need different splitting, handled by `nlua_do_ucmd`.
|
||||
if (eap->args != NULL) {
|
||||
lua_createtable(lstate, (int)eap->argc, 0);
|
||||
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");
|
||||
} else {
|
||||
lua_newtable(lstate);
|
||||
size_t length = strlen(eap->arg);
|
||||
if (length > 0) {
|
||||
size_t end = 0;
|
||||
size_t len = 0;
|
||||
int i = 1;
|
||||
char *buf = xcalloc(length, sizeof(char));
|
||||
bool done = false;
|
||||
while (!done) {
|
||||
done = uc_split_args_iter(eap->arg, length, &end, buf, &len);
|
||||
if (len > 0) {
|
||||
lua_pushlstring(lstate, buf, len);
|
||||
lua_rawseti(lstate, -2, i);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
xfree(buf);
|
||||
}
|
||||
lua_setfield(lstate, -2, "fargs");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2336,37 +2358,16 @@ int nlua_do_ucmd(ucmd_T *cmd, exarg_T *eap, bool preview)
|
||||
lua_setfield(lstate, -2, "count");
|
||||
}
|
||||
|
||||
// Override fargs for user command-specific splitting (nlua_push_eap already set it
|
||||
// for the eap->args!=NULL case, but user commands need special handling for nargs).
|
||||
// Override fargs for nargs=1/? (EX_NOSPC): the whole arg as one element (or empty).
|
||||
// Other cases are handled by `nlua_push_eap`.
|
||||
if (cmd->uc_argt & EX_NOSPC) {
|
||||
// nargs=1 or "?": fargs is the whole arg as a single element, or empty.
|
||||
lua_createtable(lstate, 1, 0);
|
||||
if ((cmd->uc_argt & EX_NEEDARG) || *eap->arg != NUL) {
|
||||
lua_pushstring(lstate, eap->arg);
|
||||
lua_rawseti(lstate, -2, 1);
|
||||
}
|
||||
lua_setfield(lstate, -2, "fargs");
|
||||
} else if (eap->args == NULL) {
|
||||
// Pre-split args not available: tokenize eap->arg by unescaped whitespace.
|
||||
lua_newtable(lstate);
|
||||
size_t length = strlen(eap->arg);
|
||||
size_t end = 0;
|
||||
size_t len = 0;
|
||||
int i = 1;
|
||||
char *buf = xcalloc(length, sizeof(char));
|
||||
bool done = false;
|
||||
while (!done) {
|
||||
done = uc_split_args_iter(eap->arg, length, &end, buf, &len);
|
||||
if (len > 0) {
|
||||
lua_pushlstring(lstate, buf, len);
|
||||
lua_rawseti(lstate, -2, i);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
xfree(buf);
|
||||
lua_setfield(lstate, -2, "fargs");
|
||||
}
|
||||
// else: eap->args was available, nlua_push_eap already set fargs.
|
||||
|
||||
char nargs[2];
|
||||
if (cmd->uc_argt & EX_EXTRA) {
|
||||
|
||||
@@ -4,7 +4,6 @@ local t_lsp = require('test.functional.plugin.lsp.testutil')
|
||||
|
||||
local clear = n.clear
|
||||
local eq = t.eq
|
||||
local pcall_err = t.pcall_err
|
||||
local exec_lua = n.exec_lua
|
||||
|
||||
local create_server_definition = t_lsp.create_server_definition
|
||||
|
||||
@@ -172,7 +172,7 @@ describe("preserve and (R)ecover with custom 'directory'", function()
|
||||
text = text .. (#chunk >= 2 and chunk[2] or chunk[1])
|
||||
end
|
||||
-- New ui.select-driven prompt; rich info from format_item.
|
||||
eq(true, text:match('Enter number of swap file to use') ~= nil)
|
||||
eq(true, text:match('Select a swapfile:') ~= nil)
|
||||
eq(true, text:match('%.swo') ~= nil)
|
||||
eq(true, text:match('%.swp') ~= nil)
|
||||
eq(true, text:match('host name:') ~= nil)
|
||||
@@ -183,7 +183,7 @@ describe("preserve and (R)ecover with custom 'directory'", function()
|
||||
else
|
||||
screen:expect({
|
||||
any = {
|
||||
vim.pesc('Enter number of swap file to use (q or empty cancels):'),
|
||||
vim.pesc('Select a swapfile:'),
|
||||
'\n1:.*%.swo',
|
||||
'\n2:.*%.swp',
|
||||
'host name:',
|
||||
|
||||
@@ -413,7 +413,7 @@ describe('ui/ext_messages', function()
|
||||
for _, chunk in ipairs(msg.content) do
|
||||
text = text .. (#chunk >= 2 and chunk[2] or chunk[1])
|
||||
end
|
||||
t.matches('^Type number and <Enter> %(q or empty cancels%):\n', text)
|
||||
t.matches('^Select a tag:\n', text)
|
||||
t.matches('1: > F%s+help%.txt%s+', text)
|
||||
end
|
||||
screen.messages = {}
|
||||
|
||||
@@ -1241,7 +1241,7 @@ func Test_tselect_listing()
|
||||
let l = split(execute("tselect first"), "\n")
|
||||
" Nvim: :tselect goes through vim.ui.select().
|
||||
let expected =<< [DATA]
|
||||
Type number and <Enter> (q or empty cancels):
|
||||
Select a tag:
|
||||
1: FS v first Xfoo
|
||||
2: FS v first Xfoo
|
||||
[DATA]
|
||||
|
||||
Reference in New Issue
Block a user