mirror of
https://github.com/neovim/neovim.git
synced 2025-09-11 13:58:18 +00:00
fix(api): nvim_parse_cmd parses :map incorrectly #34068
Problem: nvim_parse_cmd() incorrectly splits mapping commands like into three arguments instead of preserving whitespace in the RHS. Solution: Add special handling for mapping commands to parse them as exactly two arguments - the LHS and the RHS with all whitespace preserved.
This commit is contained in:
@@ -15,6 +15,7 @@
|
||||
#include "nvim/autocmd.h"
|
||||
#include "nvim/autocmd_defs.h"
|
||||
#include "nvim/buffer_defs.h"
|
||||
#include "nvim/charset.h"
|
||||
#include "nvim/cmdexpand_defs.h"
|
||||
#include "nvim/ex_cmds_defs.h"
|
||||
#include "nvim/ex_docmd.h"
|
||||
@@ -40,6 +41,31 @@
|
||||
# include "api/command.c.generated.h"
|
||||
#endif
|
||||
|
||||
/// Parse arguments for :map/:abbrev commands, preserving whitespace in RHS.
|
||||
/// @param arg_str The argument string to parse
|
||||
/// @param arena Arena allocator
|
||||
/// @return Array with at most 2 elements: [lhs, rhs]
|
||||
static Array parse_map_cmd(const char *arg_str, Arena *arena)
|
||||
{
|
||||
Array args = arena_array(arena, 2);
|
||||
|
||||
char *lhs_start = (char *)arg_str;
|
||||
char *lhs_end = skiptowhite(lhs_start);
|
||||
size_t lhs_len = (size_t)(lhs_end - lhs_start);
|
||||
|
||||
// Add the LHS (first argument)
|
||||
ADD_C(args, STRING_OBJ(cstrn_as_string(lhs_start, lhs_len)));
|
||||
|
||||
// Add the RHS (second argument) if it exists, preserving all whitespace
|
||||
char *rhs_start = skipwhite(lhs_end);
|
||||
if (*rhs_start != NUL) {
|
||||
size_t rhs_len = strlen(rhs_start);
|
||||
ADD_C(args, STRING_OBJ(cstrn_as_string(rhs_start, rhs_len)));
|
||||
}
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
/// Parse command line.
|
||||
///
|
||||
/// Doesn't check the validity of command arguments.
|
||||
@@ -121,9 +147,15 @@ Dict(cmd) nvim_parse_cmd(String str, Dict(empty) *opts, Arena *arena, Error *err
|
||||
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) {
|
||||
// Check if this is a mapping command that needs special handling
|
||||
// like mapping commands need special argument parsing to preserve whitespace in RHS:
|
||||
// "map a b c" => { args=["a", "b c"], ... }
|
||||
if (is_map_cmd(ea.cmdidx) && *ea.arg != NUL) {
|
||||
// For mapping commands, split differently to preserve whitespace
|
||||
args = parse_map_cmd(ea.arg, arena);
|
||||
} else if (ea.argt & EX_NOSPC) {
|
||||
// For nargs = 1 or '?', pass the entire argument list as a single argument,
|
||||
// otherwise split arguments by whitespace.
|
||||
if (*ea.arg != NUL) {
|
||||
args = arena_array(arena, 1);
|
||||
ADD_C(args, STRING_OBJ(cstrn_as_string(ea.arg, length)));
|
||||
|
@@ -8187,3 +8187,18 @@ uint32_t get_cmd_argt(cmdidx_T cmdidx)
|
||||
{
|
||||
return cmdnames[(int)cmdidx].cmd_argt;
|
||||
}
|
||||
|
||||
/// Check if a command is a :map/:abbrev command.
|
||||
bool is_map_cmd(cmdidx_T cmdidx)
|
||||
{
|
||||
if (IS_USER_CMDIDX(cmdidx)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ex_func_T func = cmdnames[cmdidx].cmd_func;
|
||||
return func == ex_map // :map, :nmap, :noremap, etc.
|
||||
|| func == ex_unmap // :unmap, :nunmap, etc.
|
||||
|| func == ex_mapclear // :mapclear, :nmapclear, etc.
|
||||
|| func == ex_abbreviate // :abbreviate, :iabbrev, etc.
|
||||
|| func == ex_abclear; // :abclear, :iabclear, etc.
|
||||
}
|
||||
|
@@ -4641,6 +4641,45 @@ describe('API', function()
|
||||
},
|
||||
}, api.nvim_parse_cmd('argadd a.txt | argadd b.txt', {}))
|
||||
end)
|
||||
it('parses :map commands with space in RHS', function()
|
||||
eq({
|
||||
addr = 'none',
|
||||
args = { 'a', 'b c' },
|
||||
bang = false,
|
||||
cmd = 'map',
|
||||
magic = {
|
||||
bar = true,
|
||||
file = false,
|
||||
},
|
||||
mods = {
|
||||
browse = false,
|
||||
confirm = false,
|
||||
emsg_silent = false,
|
||||
filter = {
|
||||
force = false,
|
||||
pattern = '',
|
||||
},
|
||||
hide = false,
|
||||
horizontal = false,
|
||||
keepalt = false,
|
||||
keepjumps = false,
|
||||
keepmarks = false,
|
||||
keeppatterns = false,
|
||||
lockmarks = false,
|
||||
noautocmd = false,
|
||||
noswapfile = false,
|
||||
sandbox = false,
|
||||
silent = false,
|
||||
split = '',
|
||||
tab = -1,
|
||||
unsilent = false,
|
||||
verbose = -1,
|
||||
vertical = false,
|
||||
},
|
||||
nargs = '*',
|
||||
nextcmd = '',
|
||||
}, api.nvim_parse_cmd('map a b c', {}))
|
||||
end)
|
||||
it('works for nargs=1', function()
|
||||
command('command -nargs=1 MyCommand echo <q-args>')
|
||||
eq({
|
||||
|
Reference in New Issue
Block a user