diff --git a/src/nvim/keycodes.c b/src/nvim/keycodes.c index ef067f68e2..9827604d67 100644 --- a/src/nvim/keycodes.c +++ b/src/nvim/keycodes.c @@ -850,7 +850,8 @@ char *vim_strsave_escape_ks(char *p) /// Remove escaping from K_SPECIAL characters. Reverse of /// vim_strsave_escape_ks(). Works in-place. -void vim_unescape_ks(char *p) +/// Returns the number of bytes in the unescaped string. +size_t vim_unescape_ks(char *p) { uint8_t *s = (uint8_t *)p; uint8_t *d = (uint8_t *)p; @@ -864,4 +865,5 @@ void vim_unescape_ks(char *p) } } *d = NUL; + return (size_t)((char *)d - p); } diff --git a/src/nvim/mapping.c b/src/nvim/mapping.c index c9b8763354..3bebbb62eb 100644 --- a/src/nvim/mapping.c +++ b/src/nvim/mapping.c @@ -628,22 +628,28 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev, // vi-compatible way. int same = -1; - const int first = vim_iswordp(lhs); + char keys_unescaped[MAXMAPLEN + 1]; + xmemcpyz(keys_unescaped, lhs, (size_t)len); + size_t keys_unescaped_len = vim_unescape_ks(keys_unescaped); + const char *p = keys_unescaped; + + const int first = vim_iswordp(p); int last = first; - const char *p = lhs + utfc_ptr2len(lhs); + MB_PTR_ADV(p); int n = 1; - while (p < lhs + len) { + while (p < keys_unescaped + keys_unescaped_len) { n++; // nr of (multi-byte) chars last = vim_iswordp(p); // type of last char if (same == -1 && last != first) { same = n - 1; // count of same char type } - p += utfc_ptr2len(p); + MB_PTR_ADV(p); } if (last && n > 2 && same >= 0 && same < n - 1) { retval = 1; goto theend; } + // An abbreviation cannot contain white space. for (n = 0; n < len; n++) { if (ascii_iswhite(lhs[n])) { @@ -1530,8 +1536,7 @@ bool check_abbr(int c, char *ptr, int col, int mincol) if (strchr(mp->m_keys, K_SPECIAL) != NULL) { // Might have K_SPECIAL escaped mp->m_keys. q = xstrdup(mp->m_keys); - vim_unescape_ks(q); - qlen = (int)strlen(q); + qlen = (int)vim_unescape_ks(q); } // find entries with right mode and keys int match = (mp->m_mode & State) diff --git a/test/old/testdir/test_mapping.vim b/test/old/testdir/test_mapping.vim index 2c6730bfed..5505d0f4d4 100644 --- a/test/old/testdir/test_mapping.vim +++ b/test/old/testdir/test_mapping.vim @@ -7,11 +7,41 @@ source term_util.vim func Test_abbreviation() new - " abbreviation with 0x80 should work + + " abbreviation with 0x80 (full-id) inoreab чкпр vim call feedkeys("Goчкпр \", "xt") call assert_equal('vim ', getline('$')) iunab чкпр + + " abbreviation with 0x80 (non-id) + inoreab abc⁀ abc^ + inoreab ⁀ ^ + call feedkeys("Goabc⁀ def⁀ ⁀ \", "xt") + call assert_equal('abc^ def⁀ ^ ', getline('$')) + iunab abc⁀ + iunab ⁀ + + " abbreviation with 0x9b (non-id) + inoreab abc; abc; + inoreab ; ; + call feedkeys("Goabc; def; ; \", "xt") + call assert_equal('abc; def; ; ', getline('$')) + iunab abc; + iunab ; + + " abbreviation with composing chars (end-id) + inoreab ..ã a^~ + inoreab ..β̃ β^~ + inoreab ..π̃ π^~ + inoreab ..Λ̃ Λ^~ + call feedkeys("Go..ã ..β̃ ..π̃ ..Λ̃ \", "xt") + call assert_equal('a^~ β^~ π^~ Λ^~ ', getline('$')) + iunab ..ã + iunab ..β̃ + iunab ..π̃ + iunab ..Λ̃ + bwipe! endfunc