fix(api): nvim_set_keymap() throws error even in pcall() #33228

Problem: When `nvim_set_keymap` tries to overwrite a `<unique>` mapping,
it throws an error even when called in `pcall`.

Solution: src/nvim/mapping.c:buf_do_map no longer calls `semsg`. Its
callers now decide whether to ignore the error, or use
`semsg` (not caught)/`api_set_error` (caught by `pcall`).
This commit is contained in:
tstsrt
2025-04-01 12:30:00 +00:00
committed by GitHub
parent 0e7479bb76
commit ec18ebcb41
2 changed files with 62 additions and 14 deletions

View File

@@ -681,12 +681,7 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev,
if ((mp->m_mode & mode) != 0
&& mp->m_keylen == len
&& strncmp(mp->m_keys, lhs, (size_t)len) == 0) {
if (is_abbrev) {
semsg(_(e_global_abbreviation_already_exists_for_str), mp->m_keys);
} else {
semsg(_(e_global_mapping_already_exists_for_str), mp->m_keys);
}
retval = 5;
retval = 6;
goto theend;
}
}
@@ -799,11 +794,6 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev,
did_it = true;
break;
} else if (args->unique) {
if (is_abbrev) {
semsg(_(e_abbreviation_already_exists_for_str), p);
} else {
semsg(_(e_mapping_already_exists_for_str), p);
}
retval = 5;
goto theend;
} else {
@@ -962,6 +952,7 @@ theend:
/// - 2 for no match
/// - 4 for out of mem (deprecated, WON'T HAPPEN)
/// - 5 for entry not unique
/// - 6 for buflocal unique entry conflicts with global entry
///
int do_map(int maptype, char *arg, int mode, bool is_abbrev)
{
@@ -2637,16 +2628,47 @@ static void do_exmap(exarg_T *eap, int isabbrev)
char *cmdp = eap->cmd;
int mode = get_map_mode(&cmdp, eap->forceit || isabbrev);
switch (do_map((*cmdp == 'n') ? MAPTYPE_NOREMAP
: (*cmdp == 'u') ? MAPTYPE_UNMAP : MAPTYPE_MAP,
eap->arg, mode, isabbrev)) {
int maptype;
if (*cmdp == 'n') {
maptype = MAPTYPE_NOREMAP;
} else if (*cmdp == 'u') {
maptype = MAPTYPE_UNMAP;
} else {
maptype = MAPTYPE_MAP;
}
MapArguments parsed_args;
int result = str_to_mapargs(eap->arg, maptype == MAPTYPE_UNMAP, &parsed_args);
switch (result) {
case 0:
break;
case 1:
emsg(_(e_invarg));
goto free_rhs;
break;
default:
assert(false && "Unknown return code from str_to_mapargs!");
goto free_rhs;
}
switch (buf_do_map(maptype, &parsed_args, mode, isabbrev, curbuf)) {
case 1:
emsg(_(e_invarg));
break;
case 2:
emsg(isabbrev ? _(e_noabbr) : _(e_nomap));
break;
case 5:
semsg(isabbrev ? _(e_abbreviation_already_exists_for_str)
: _(e_mapping_already_exists_for_str),
parsed_args.lhs);
break;
case 6:
semsg(isabbrev ? _(e_global_abbreviation_already_exists_for_str)
: _(e_global_mapping_already_exists_for_str),
parsed_args.lhs);
}
free_rhs:
xfree(parsed_args.rhs);
xfree(parsed_args.orig_rhs);
}
/// ":abbreviate" and friends.
@@ -2808,6 +2830,12 @@ void modify_keymap(uint64_t channel_id, Buffer buffer, bool is_unmap, String mod
is_abbrev ? e_abbreviation_already_exists_for_str
: e_mapping_already_exists_for_str, lhs.data);
goto fail_and_free;
break;
case 6:
api_set_error(err, kErrorTypeException,
is_abbrev ? e_global_abbreviation_already_exists_for_str
: e_global_mapping_already_exists_for_str, lhs.data);
goto fail_and_free;
default:
assert(false && "Unrecognized return code!");
goto fail_and_free;

View File

@@ -1465,4 +1465,24 @@ describe('nvim_buf_set_keymap, nvim_buf_del_keymap', function()
eq(1, exec_lua [[return GlobalCount]])
eq('\nNo mapping found', n.exec_capture('nmap <C-I>'))
end)
it('does not overwrite in <unique> mappings', function()
api.nvim_buf_set_keymap(0, 'i', 'lhs', 'rhs', {})
eq(
'E227: Mapping already exists for lhs',
pcall_err(api.nvim_buf_set_keymap, 0, 'i', 'lhs', 'rhs', { unique = true })
)
api.nvim_buf_set_keymap(0, 'ia', 'lhs2', 'rhs2', {})
eq(
'E226: Abbreviation already exists for lhs2',
pcall_err(api.nvim_buf_set_keymap, 0, 'ia', 'lhs2', 'rhs2', { unique = true })
)
api.nvim_set_keymap('n', 'lhs', 'rhs', {})
eq(
'E225: Global mapping already exists for lhs',
pcall_err(api.nvim_buf_set_keymap, 0, 'n', 'lhs', 'rhs', { unique = true })
)
end)
end)