vim-patch:9.1.0642: Check that mapping rhs starts with lhs fails if not simplified (#29909)

Problem:  Check that mapping rhs starts with lhs doesn't work if lhs is
          not simplified.
Solution: Keep track of the mapblock containing the alternative lhs and
          also compare with it (zeertzjq).

fixes: vim/vim#15376
closes: vim/vim#15384

9d997addc7

Cherry-pick removal of save_m_str from patch 8.2.4059.
This commit is contained in:
zeertzjq
2024-07-30 07:35:25 +08:00
committed by GitHub
parent d131c48c82
commit 4b852bc555
4 changed files with 103 additions and 38 deletions

View File

@@ -2341,8 +2341,7 @@ static int handle_mapping(int *keylenp, const bool *timedout, int *mapdepth)
const int save_m_noremap = mp->m_noremap;
const bool save_m_silent = mp->m_silent;
char *save_m_keys = NULL; // only saved when needed
char *save_m_str = NULL; // only saved when needed
const LuaRef save_m_luaref = mp->m_luaref;
char *save_alt_m_keys = NULL; // only saved when needed
// Handle ":map <expr>": evaluate the {rhs} as an
// expression. Also save and restore the command line
@@ -2356,9 +2355,7 @@ static int handle_mapping(int *keylenp, const bool *timedout, int *mapdepth)
may_garbage_collect = false;
save_m_keys = xstrdup(mp->m_keys);
if (save_m_luaref == LUA_NOREF) {
save_m_str = xstrdup(mp->m_str);
}
save_alt_m_keys = mp->m_alt != NULL ? xstrdup(mp->m_alt->m_keys) : NULL;
map_str = eval_map_expr(mp, NUL);
if ((map_str == NULL || *map_str == NUL)) {
@@ -2409,11 +2406,18 @@ static int handle_mapping(int *keylenp, const bool *timedout, int *mapdepth)
if (save_m_noremap != REMAP_YES) {
noremap = save_m_noremap;
} else if (strncmp(map_str, save_m_keys != NULL ? save_m_keys : mp->m_keys,
(size_t)keylen) != 0) {
noremap = REMAP_YES;
} else {
} else if (save_m_expr
? strncmp(map_str, save_m_keys, (size_t)keylen) == 0
|| (save_alt_m_keys != NULL
&& strncmp(map_str, save_alt_m_keys,
strlen(save_alt_m_keys)) == 0)
: strncmp(map_str, mp->m_keys, (size_t)keylen) == 0
|| (mp->m_alt != NULL
&& strncmp(map_str, mp->m_alt->m_keys,
strlen(mp->m_alt->m_keys)) == 0)) {
noremap = REMAP_SKIP;
} else {
noremap = REMAP_YES;
}
i = ins_typebuf(map_str, noremap, 0, true, cmd_silent || save_m_silent);
if (save_m_expr) {
@@ -2421,7 +2425,7 @@ static int handle_mapping(int *keylenp, const bool *timedout, int *mapdepth)
}
}
xfree(save_m_keys);
xfree(save_m_str);
xfree(save_alt_m_keys);
*keylenp = keylen;
if (i == FAIL) {
return map_result_fail;

View File

@@ -151,7 +151,9 @@ static void mapblock_free(mapblock_T **mpp)
{
mapblock_T *mp = *mpp;
xfree(mp->m_keys);
if (!mp->m_simplified) {
if (mp->m_alt != NULL) {
mp->m_alt->m_alt = NULL;
} else {
NLUA_CLEAR_REF(mp->m_luaref);
xfree(mp->m_str);
xfree(mp->m_orig_str);
@@ -493,13 +495,13 @@ static int str_to_mapargs(const char *strargs, bool is_unmap, MapArguments *mapa
return 0;
}
/// @param args "rhs", "rhs_lua", "orig_rhs", "expr", "silent", "nowait", "replace_keycodes" and
/// and "desc" fields are used.
/// "rhs", "rhs_lua", "orig_rhs" fields are cleared if "simplified" is false.
/// @param args "rhs", "rhs_lua", "orig_rhs", "expr", "silent", "nowait",
/// "replace_keycodes" and "desc" fields are used.
/// @param sid 0 to use current_sctx
static void map_add(buf_T *buf, mapblock_T **map_table, mapblock_T **abbr_table, const char *keys,
MapArguments *args, int noremap, int mode, bool is_abbr, scid_T sid,
linenr_T lnum, bool simplified)
static mapblock_T *map_add(buf_T *buf, mapblock_T **map_table, mapblock_T **abbr_table,
const char *keys, MapArguments *args, int noremap, int mode,
bool is_abbr, scid_T sid, linenr_T lnum, bool simplified)
FUNC_ATTR_NONNULL_RET
{
mapblock_T *mp = xcalloc(1, sizeof(mapblock_T));
@@ -516,11 +518,6 @@ static void map_add(buf_T *buf, mapblock_T **map_table, mapblock_T **abbr_table,
mp->m_str = args->rhs;
mp->m_orig_str = args->orig_rhs;
mp->m_luaref = args->rhs_lua;
if (!simplified) {
args->rhs = NULL;
args->orig_rhs = NULL;
args->rhs_lua = LUA_NOREF;
}
mp->m_keylen = (int)strlen(mp->m_keys);
mp->m_noremap = noremap;
mp->m_nowait = args->nowait;
@@ -551,6 +548,7 @@ static void map_add(buf_T *buf, mapblock_T **map_table, mapblock_T **abbr_table,
mp->m_next = map_table[n];
map_table[n] = mp;
}
return mp;
}
/// Sets or removes a mapping or abbreviation in buffer `buf`.
@@ -571,6 +569,7 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev,
// mappings/abbreviations, not the globals.
mapblock_T **map_table = args->buffer ? buf->b_maphash : maphash;
mapblock_T **abbr_table = args->buffer ? &buf->b_first_abbr : &first_abbr;
mapblock_T *mp_result[2] = { NULL, NULL };
// For ":noremap" don't remap, otherwise do remap.
int noremap = args->script ? REMAP_SCRIPT
@@ -805,19 +804,16 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev,
mp->m_mode &= ~mode; // remove mode bits
if (mp->m_mode == 0 && !did_it) { // reuse entry
XFREE_CLEAR(mp->m_desc);
if (!mp->m_simplified) {
if (mp->m_alt != NULL) {
mp->m_alt = mp->m_alt->m_alt = NULL;
} else {
NLUA_CLEAR_REF(mp->m_luaref);
XFREE_CLEAR(mp->m_str);
XFREE_CLEAR(mp->m_orig_str);
xfree(mp->m_str);
xfree(mp->m_orig_str);
}
mp->m_str = args->rhs;
mp->m_orig_str = args->orig_rhs;
mp->m_luaref = args->rhs_lua;
if (!keyround1_simplified) {
args->rhs = NULL;
args->orig_rhs = NULL;
args->rhs_lua = LUA_NOREF;
}
mp->m_noremap = noremap;
mp->m_nowait = args->nowait;
mp->m_silent = args->silent;
@@ -831,6 +827,7 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev,
if (args->desc != NULL) {
mp->m_desc = xstrdup(args->desc);
}
mp_result[keyround - 1] = mp;
did_it = true;
}
}
@@ -889,13 +886,24 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev,
}
// Get here when adding a new entry to the maphash[] list or abbrlist.
map_add(buf, map_table, abbr_table, lhs, args, noremap, mode, is_abbrev,
0, // sid
0, // lnum
keyround1_simplified);
mp_result[keyround - 1] = map_add(buf, map_table, abbr_table, lhs,
args, noremap, mode, is_abbrev,
0, // sid
0, // lnum
keyround1_simplified);
}
if (mp_result[0] != NULL && mp_result[1] != NULL) {
mp_result[0]->m_alt = mp_result[1];
mp_result[1]->m_alt = mp_result[0];
}
theend:
if (mp_result[0] != NULL || mp_result[1] != NULL) {
args->rhs = NULL;
args->orig_rhs = NULL;
args->rhs_lua = LUA_NOREF;
}
return retval;
}
@@ -2348,12 +2356,19 @@ void f_mapset(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
xfree(unmap_args.rhs);
xfree(unmap_args.orig_rhs);
mapblock_T *mp_result[2] = { NULL, NULL };
mp_result[0] = map_add(curbuf, map_table, abbr_table, lhsraw, &args,
noremap, mode, is_abbr, sid, lnum, false);
if (lhsrawalt != NULL) {
map_add(curbuf, map_table, abbr_table, lhsrawalt, &args, noremap, mode, is_abbr,
sid, lnum, true);
mp_result[1] = map_add(curbuf, map_table, abbr_table, lhsrawalt, &args,
noremap, mode, is_abbr, sid, lnum, true);
}
if (mp_result[0] != NULL && mp_result[1] != NULL) {
mp_result[0]->m_alt = mp_result[1];
mp_result[1]->m_alt = mp_result[0];
}
map_add(curbuf, map_table, abbr_table, lhsraw, &args, noremap, mode, is_abbr,
sid, lnum, false);
}
/// "maplist()" function

View File

@@ -10,6 +10,9 @@ enum { MAXMAPLEN = 50, }; ///< Maximum length of key sequence to be mapped.
typedef struct mapblock mapblock_T;
struct mapblock {
mapblock_T *m_next; ///< next mapblock in list
mapblock_T *m_alt; ///< pointer to mapblock of the same mapping
///< with an alternative form of m_keys, or NULL
///< if there is no such mapblock
char *m_keys; ///< mapped from, lhs
char *m_str; ///< mapped to, rhs
char *m_orig_str; ///< rhs as entered by the user

View File

@@ -1672,6 +1672,49 @@ func Test_unmap_simplifiable()
unmap <C-I>
endfunc
" Test that the first byte of rhs is not remapped if rhs starts with lhs.
func Test_map_rhs_starts_with_lhs()
new
func MapExpr()
return "\<C-R>\<C-P>"
endfunc
for expr in [v:false, v:true]
if expr
imap <buffer><expr> <C-R> MapExpr()
else
imap <buffer> <C-R> <C-R><C-P>
endif
for restore in [v:false, v:true]
if restore
let saved = maparg('<C-R>', 'i', v:false, v:true)
iunmap <buffer> <C-R>
call mapset(saved)
endif
let @a = 'foo'
call feedkeys("S\<C-R>a", 'tx')
call assert_equal('foo', getline('.'))
let @a = 'bar'
call feedkeys("S\<*C-R>a", 'tx')
call assert_equal('bar', getline('.'))
endfor
endfor
" When two mappings are used for <C-I> and <Tab>, remapping should work.
imap <buffer> <C-I> <Tab>bar
imap <buffer> <Tab> foo
call feedkeys("S\<Tab>", 'xt')
call assert_equal('foo', getline('.'))
call feedkeys("S\<*C-I>", 'xt')
call assert_equal('foobar', getline('.'))
delfunc MapExpr
bwipe!
endfunc
func Test_expr_map_escape_special()
nnoremap<Cmd>let g:got_ellipsis += 1<CR>
func Func()