diff options
-rw-r--r-- | src/nvim/getchar.c | 8 | ||||
-rw-r--r-- | src/nvim/mapping.c | 19 | ||||
-rw-r--r-- | src/nvim/testdir/test_mapping.vim | 14 |
3 files changed, 31 insertions, 10 deletions
diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index db4d640f46..b0dac7aea6 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -2191,12 +2191,12 @@ static int handle_mapping(int *keylenp, const bool *timedout, int *mapdepth) // Copy the values from *mp that are used, because evaluating the // expression may invoke a function that redefines the mapping, thereby // making *mp invalid. - char save_m_expr = mp->m_expr; - int save_m_noremap = mp->m_noremap; - char save_m_silent = mp->m_silent; + const bool save_m_expr = mp->m_expr; + 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 - LuaRef save_m_luaref = mp->m_luaref; + const LuaRef save_m_luaref = mp->m_luaref; // Handle ":map <expr>": evaluate the {rhs} as an // expression. Also save and restore the command line diff --git a/src/nvim/mapping.c b/src/nvim/mapping.c index 3522e0de36..a412fd05b2 100644 --- a/src/nvim/mapping.c +++ b/src/nvim/mapping.c @@ -1517,17 +1517,23 @@ bool check_abbr(int c, char *ptr, int col, int mincol) // insert the last typed char (void)ins_typebuf((char *)tb, 1, 0, true, mp->m_silent); } - if (mp->m_expr) { + + // copy values here, calling eval_map_expr() may make "mp" invalid! + const int noremap = mp->m_noremap; + const bool silent = mp->m_silent; + const bool expr = mp->m_expr; + + if (expr) { s = eval_map_expr(mp, c); } else { s = mp->m_str; } if (s != NULL) { // insert the to string - (void)ins_typebuf(s, mp->m_noremap, 0, true, mp->m_silent); + (void)ins_typebuf(s, noremap, 0, true, silent); // no abbrev. for these chars typebuf.tb_no_abbr_cnt += (int)strlen(s) + j + 1; - if (mp->m_expr) { + if (expr) { xfree(s); } } @@ -1536,7 +1542,7 @@ bool check_abbr(int c, char *ptr, int col, int mincol) tb[1] = NUL; len = clen; // Delete characters instead of bytes while (len-- > 0) { // delete the from string - (void)ins_typebuf((char *)tb, 1, 0, true, mp->m_silent); + (void)ins_typebuf((char *)tb, 1, 0, true, silent); } return true; } @@ -1546,6 +1552,7 @@ bool check_abbr(int c, char *ptr, int col, int mincol) /// Evaluate the RHS of a mapping or abbreviations and take care of escaping /// special characters. +/// Careful: after this "mp" will be invalid if the mapping was deleted. /// /// @param c NUL or typed character for abbreviation char *eval_map_expr(mapblock_T *mp, int c) @@ -1560,6 +1567,8 @@ char *eval_map_expr(mapblock_T *mp, int c) vim_unescape_ks((char_u *)expr); } + const bool replace_keycodes = mp->m_replace_keycodes; + // Forbid changing text or using ":normal" to avoid most of the bad side // effects. Also restore the cursor position. textlock++; @@ -1596,7 +1605,7 @@ char *eval_map_expr(mapblock_T *mp, int c) char *res = NULL; - if (mp->m_replace_keycodes) { + if (replace_keycodes) { replace_termcodes(p, strlen(p), &res, REPTERM_DO_LT, NULL, CPO_TO_CPO_FLAGS); } else { // Escape K_SPECIAL in the result to be able to use the string as typeahead. diff --git a/src/nvim/testdir/test_mapping.vim b/src/nvim/testdir/test_mapping.vim index 522a51589f..5c5a65d4ca 100644 --- a/src/nvim/testdir/test_mapping.vim +++ b/src/nvim/testdir/test_mapping.vim @@ -767,6 +767,11 @@ func Test_mapcomplete() mapclear endfunc +func GetAbbrText() + unabbr hola + return 'hello' +endfunc + " Test for <expr> in abbreviation func Test_expr_abbr() new @@ -782,7 +787,14 @@ func Test_expr_abbr() call assert_equal('', getline(1)) unabbr <expr> hte - close! + " evaluating the expression deletes the abbreviation + abbr <expr> hola GetAbbrText() + call assert_equal('GetAbbrText()', maparg('hola', 'i', '1')) + call feedkeys("ahola \<Esc>", 'xt') + call assert_equal('hello ', getline('.')) + call assert_equal('', maparg('hola', 'i', '1')) + + bwipe! endfunc " Test for storing mappings in different modes in a vimrc file |