diff options
Diffstat (limited to 'src/nvim/mapping.c')
-rw-r--r-- | src/nvim/mapping.c | 478 |
1 files changed, 269 insertions, 209 deletions
diff --git a/src/nvim/mapping.c b/src/nvim/mapping.c index 001bbf7e48..831d1299a8 100644 --- a/src/nvim/mapping.c +++ b/src/nvim/mapping.c @@ -5,32 +5,43 @@ #include <assert.h> #include <inttypes.h> +#include <lauxlib.h> +#include <limits.h> #include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> #include <string.h> #include "nvim/api/private/converter.h" +#include "nvim/api/private/defs.h" #include "nvim/api/private/helpers.h" #include "nvim/ascii.h" -#include "nvim/assert.h" #include "nvim/buffer_defs.h" #include "nvim/charset.h" +#include "nvim/cmdexpand.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" -#include "nvim/ex_docmd.h" +#include "nvim/eval/typval_defs.h" +#include "nvim/ex_cmds_defs.h" #include "nvim/ex_session.h" -#include "nvim/func_attr.h" #include "nvim/garray.h" #include "nvim/getchar.h" +#include "nvim/gettext.h" +#include "nvim/globals.h" +#include "nvim/highlight_defs.h" #include "nvim/keycodes.h" #include "nvim/lua/executor.h" +#include "nvim/macros.h" #include "nvim/mapping.h" #include "nvim/mbyte.h" #include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/option.h" +#include "nvim/option_defs.h" +#include "nvim/pos.h" #include "nvim/regexp.h" #include "nvim/runtime.h" -#include "nvim/ui.h" +#include "nvim/search.h" +#include "nvim/strings.h" #include "nvim/vim.h" /// List used for abbreviations. @@ -150,7 +161,7 @@ static void showmap(mapblock_T *mp, bool local) { size_t len = 1; - if (message_filtered((char *)mp->m_keys) && message_filtered(mp->m_str) + if (message_filtered(mp->m_keys) && message_filtered(mp->m_str) && (mp->m_desc == NULL || message_filtered(mp->m_desc))) { return; } @@ -174,9 +185,9 @@ static void showmap(mapblock_T *mp, bool local) } // Display the LHS. Get length of what we write. - len = (size_t)msg_outtrans_special((char *)mp->m_keys, true, 0); + len = (size_t)msg_outtrans_special(mp->m_keys, true, 0); do { - msg_putchar(' '); // padd with blanks + msg_putchar(' '); // pad with blanks len++; } while (len < 12); @@ -263,7 +274,7 @@ static bool set_maparg_lhs_rhs(const char *const orig_lhs, const size_t orig_lhs return false; } mapargs->lhs_len = strlen(replaced); - STRLCPY(mapargs->lhs, replaced, sizeof(mapargs->lhs)); + xstrlcpy(mapargs->lhs, replaced, sizeof(mapargs->lhs)); if (did_simplify) { replaced = replace_termcodes(orig_lhs, orig_lhs_len, &bufarg, flags | REPTERM_NO_SIMPLIFY, NULL, cpo_flags); @@ -271,7 +282,7 @@ static bool set_maparg_lhs_rhs(const char *const orig_lhs, const size_t orig_lhs return false; } mapargs->alt_lhs_len = strlen(replaced); - STRLCPY(mapargs->alt_lhs, replaced, sizeof(mapargs->alt_lhs)); + xstrlcpy(mapargs->alt_lhs, replaced, sizeof(mapargs->alt_lhs)); } else { mapargs->alt_lhs_len = 0; } @@ -289,10 +300,10 @@ static void set_maparg_rhs(const char *const orig_rhs, const size_t orig_rhs_len if (rhs_lua == LUA_NOREF) { mapargs->orig_rhs_len = orig_rhs_len; - mapargs->orig_rhs = xcalloc(mapargs->orig_rhs_len + 1, sizeof(char_u)); - STRLCPY(mapargs->orig_rhs, orig_rhs, mapargs->orig_rhs_len + 1); + mapargs->orig_rhs = xcalloc(mapargs->orig_rhs_len + 1, sizeof(char)); + xstrlcpy(mapargs->orig_rhs, orig_rhs, mapargs->orig_rhs_len + 1); if (STRICMP(orig_rhs, "<nop>") == 0) { // "<Nop>" means nothing - mapargs->rhs = xcalloc(1, sizeof(char_u)); // single NUL-char + mapargs->rhs = xcalloc(1, sizeof(char)); // single NUL-char mapargs->rhs_len = 0; mapargs->rhs_is_noop = true; } else { @@ -308,7 +319,7 @@ static void set_maparg_rhs(const char *const orig_rhs, const size_t orig_rhs_len } else { char tmp_buf[64]; // orig_rhs is not used for Lua mappings, but still needs to be a string. - mapargs->orig_rhs = xcalloc(1, sizeof(char_u)); + mapargs->orig_rhs = xcalloc(1, sizeof(char)); mapargs->orig_rhs_len = 0; // stores <lua>ref_no<cr> in map_str mapargs->rhs_len = (size_t)vim_snprintf(S_LEN(tmp_buf), "%c%c%c%d\r", K_SPECIAL, @@ -336,53 +347,53 @@ static void set_maparg_rhs(const char *const orig_rhs, const size_t orig_rhs_len /// @param[out] mapargs MapArguments struct holding all extracted argument /// values. /// @return 0 on success, 1 if invalid arguments are detected. -static int str_to_mapargs(const char_u *strargs, bool is_unmap, MapArguments *mapargs) +static int str_to_mapargs(const char *strargs, bool is_unmap, MapArguments *mapargs) { - const char_u *to_parse = strargs; - to_parse = (char_u *)skipwhite((char *)to_parse); + const char *to_parse = strargs; + to_parse = skipwhite(to_parse); CLEAR_POINTER(mapargs); // Accept <buffer>, <nowait>, <silent>, <expr>, <script>, and <unique> in // any order. while (true) { - if (STRNCMP(to_parse, "<buffer>", 8) == 0) { - to_parse = (char_u *)skipwhite((char *)to_parse + 8); + if (strncmp(to_parse, "<buffer>", 8) == 0) { + to_parse = skipwhite(to_parse + 8); mapargs->buffer = true; continue; } - if (STRNCMP(to_parse, "<nowait>", 8) == 0) { - to_parse = (char_u *)skipwhite((char *)to_parse + 8); + if (strncmp(to_parse, "<nowait>", 8) == 0) { + to_parse = skipwhite(to_parse + 8); mapargs->nowait = true; continue; } - if (STRNCMP(to_parse, "<silent>", 8) == 0) { - to_parse = (char_u *)skipwhite((char *)to_parse + 8); + if (strncmp(to_parse, "<silent>", 8) == 0) { + to_parse = skipwhite(to_parse + 8); mapargs->silent = true; continue; } // Ignore obsolete "<special>" modifier. - if (STRNCMP(to_parse, "<special>", 9) == 0) { - to_parse = (char_u *)skipwhite((char *)to_parse + 9); + if (strncmp(to_parse, "<special>", 9) == 0) { + to_parse = skipwhite(to_parse + 9); continue; } - if (STRNCMP(to_parse, "<script>", 8) == 0) { - to_parse = (char_u *)skipwhite((char *)to_parse + 8); + if (strncmp(to_parse, "<script>", 8) == 0) { + to_parse = skipwhite(to_parse + 8); mapargs->script = true; continue; } - if (STRNCMP(to_parse, "<expr>", 6) == 0) { - to_parse = (char_u *)skipwhite((char *)to_parse + 6); + if (strncmp(to_parse, "<expr>", 6) == 0) { + to_parse = skipwhite(to_parse + 6); mapargs->expr = true; continue; } - if (STRNCMP(to_parse, "<unique>", 8) == 0) { - to_parse = (char_u *)skipwhite((char *)to_parse + 8); + if (strncmp(to_parse, "<unique>", 8) == 0) { + to_parse = skipwhite(to_parse + 8); mapargs->unique = true; continue; } @@ -399,7 +410,7 @@ static int str_to_mapargs(const char_u *strargs, bool is_unmap, MapArguments *ma // // With :unmap, literal white space is included in the {lhs}; there is no // separate {rhs}. - const char *lhs_end = (char *)to_parse; + const char *lhs_end = to_parse; bool do_backslash = (vim_strchr(p_cpo, CPO_BSLASH) == NULL); while (*lhs_end && (is_unmap || !ascii_iswhite(*lhs_end))) { if ((lhs_end[0] == Ctrl_V || (do_backslash && lhs_end[0] == '\\')) @@ -411,20 +422,20 @@ static int str_to_mapargs(const char_u *strargs, bool is_unmap, MapArguments *ma // {lhs_end} is a pointer to the "terminating whitespace" after {lhs}. // Use that to initialize {rhs_start}. - const char_u *rhs_start = (char_u *)skipwhite((char *)lhs_end); + const char *rhs_start = skipwhite((char *)lhs_end); // Given {lhs} might be larger than MAXMAPLEN before replace_termcodes // (e.g. "<Space>" is longer than ' '), so first copy into a buffer. - size_t orig_lhs_len = (size_t)((char_u *)lhs_end - to_parse); + size_t orig_lhs_len = (size_t)(lhs_end - to_parse); if (orig_lhs_len >= 256) { return 1; } - char_u lhs_to_replace[256]; - STRLCPY(lhs_to_replace, to_parse, orig_lhs_len + 1); + char lhs_to_replace[256]; + xstrlcpy(lhs_to_replace, to_parse, orig_lhs_len + 1); - size_t orig_rhs_len = STRLEN(rhs_start); - if (!set_maparg_lhs_rhs((char *)lhs_to_replace, orig_lhs_len, - (char *)rhs_start, orig_rhs_len, LUA_NOREF, + size_t orig_rhs_len = strlen(rhs_start); + if (!set_maparg_lhs_rhs(lhs_to_replace, orig_lhs_len, + rhs_start, orig_rhs_len, LUA_NOREF, CPO_TO_CPO_FLAGS, mapargs)) { return 1; } @@ -454,16 +465,16 @@ static void map_add(buf_T *buf, mapblock_T **map_table, mapblock_T **abbr_table, } } - mp->m_keys = (uint8_t *)xstrdup(keys); + mp->m_keys = xstrdup(keys); mp->m_str = args->rhs; - mp->m_orig_str = (char *)args->orig_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_keylen = (int)strlen(mp->m_keys); mp->m_noremap = noremap; mp->m_nowait = args->nowait; mp->m_silent = args->silent; @@ -489,7 +500,7 @@ static void map_add(buf_T *buf, mapblock_T **map_table, mapblock_T **abbr_table, mp->m_next = *abbr_table; *abbr_table = mp; } else { - const int n = MAP_HASH(mp->m_mode, mp->m_keys[0]); + const int n = MAP_HASH(mp->m_mode, (uint8_t)mp->m_keys[0]); mp->m_next = map_table[n]; map_table[n] = mp; } @@ -508,7 +519,7 @@ static void map_add(buf_T *buf, mapblock_T **map_table, mapblock_T **abbr_table, static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev, buf_T *buf) { mapblock_T *mp, **mpp; - const char_u *p; + const char *p; int n; int retval = 0; mapblock_T **abbr_table; @@ -545,7 +556,7 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev, goto theend; } - const char_u *lhs = (char_u *)&args->lhs; + const char *lhs = (char *)&args->lhs; const bool did_simplify = args->alt_lhs_len != 0; // The following is done twice if we have two versions of keys @@ -559,11 +570,11 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev, if (!did_simplify) { break; } - lhs = (char_u *)&args->alt_lhs; + lhs = (char *)&args->alt_lhs; len = (int)args->alt_lhs_len; } else if (did_simplify && do_print) { // when printing always use the not-simplified map - lhs = (char_u *)&args->alt_lhs; + lhs = (char *)&args->alt_lhs; len = (int)args->alt_lhs_len; } @@ -583,9 +594,9 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev, const int first = vim_iswordp(lhs); int last = first; - p = lhs + utfc_ptr2len((char *)lhs); + p = (char *)lhs + utfc_ptr2len((char *)lhs); n = 1; - while (p < lhs + len) { + while (p < (char *)lhs + len) { n++; // nr of (multi-byte) chars last = vim_iswordp(p); // type of last char if (same == -1 && last != first) { @@ -632,7 +643,7 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev, // check entries with the same mode if ((mp->m_mode & mode) != 0 && mp->m_keylen == len - && STRNCMP(mp->m_keys, lhs, (size_t)len) == 0) { + && strncmp(mp->m_keys, lhs, (size_t)len) == 0) { if (is_abbrev) { semsg(_("E224: global abbreviation already exists for %s"), mp->m_keys); @@ -666,7 +677,7 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev, did_local = true; } else { n = mp->m_keylen; - if (STRNCMP(mp->m_keys, lhs, (size_t)(n < len ? n : len)) == 0) { + if (strncmp(mp->m_keys, lhs, (size_t)(n < len ? n : len)) == 0) { showmap(mp, true); did_local = true; } @@ -685,9 +696,9 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev, for (int round = 0; (round == 0 || maptype == MAPTYPE_UNMAP) && round <= 1 && !did_it && !got_int; round++) { int hash_start, hash_end; - if (has_lhs || is_abbrev) { + if ((round == 0 && has_lhs) || is_abbrev) { // just use one hash - hash_start = is_abbrev ? 0 : MAP_HASH(mode, lhs[0]); + hash_start = is_abbrev ? 0 : MAP_HASH(mode, (uint8_t)lhs[0]); hash_end = hash_start + 1; } else { // need to loop over all hash lists @@ -710,12 +721,12 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev, } else { // do we have a match? if (round) { // second round: Try unmap "rhs" string n = (int)strlen(mp->m_str); - p = (char_u *)mp->m_str; + p = mp->m_str; } else { n = mp->m_keylen; p = mp->m_keys; } - if (STRNCMP(p, lhs, (size_t)(n < len ? n : len)) == 0) { + if (strncmp(p, lhs, (size_t)(n < len ? n : len)) == 0) { if (maptype == MAPTYPE_UNMAP) { // Delete entry. // Only accept a full match. For abbreviations @@ -768,7 +779,7 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev, XFREE_CLEAR(mp->m_orig_str); } mp->m_str = args->rhs; - mp->m_orig_str = (char *)args->orig_rhs; + mp->m_orig_str = args->orig_rhs; mp->m_luaref = args->rhs_lua; if (!keyround1_simplified) { args->rhs = NULL; @@ -797,7 +808,7 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev, } // May need to put this entry into another hash list. - int new_hash = MAP_HASH(mp->m_mode, mp->m_keys[0]); + int new_hash = MAP_HASH(mp->m_mode, (uint8_t)mp->m_keys[0]); if (!is_abbrev && new_hash != hash) { *mpp = mp->m_next; mp->m_next = map_table[new_hash]; @@ -904,7 +915,7 @@ theend: /// - 4 for out of mem (deprecated, WON'T HAPPEN) /// - 5 for entry not unique /// -int do_map(int maptype, char_u *arg, int mode, bool is_abbrev) +int do_map(int maptype, char *arg, int mode, bool is_abbrev) { MapArguments parsed_args; int result = str_to_mapargs(arg, maptype == MAPTYPE_UNMAP, &parsed_args); @@ -1024,7 +1035,7 @@ void map_clear_mode(buf_T *buf, int mode, bool local, bool abbr) continue; } // May need to put this entry into another hash list. - new_hash = MAP_HASH(mp->m_mode, mp->m_keys[0]); + new_hash = MAP_HASH(mp->m_mode, (uint8_t)mp->m_keys[0]); if (!abbr && new_hash != hash) { *mpp = mp->m_next; if (local) { @@ -1059,9 +1070,9 @@ bool map_to_exists(const char *const str, const char *const modechars, const boo int retval; char *buf = NULL; - const char_u *const rhs = (char_u *)replace_termcodes(str, strlen(str), - &buf, REPTERM_DO_LT, - NULL, CPO_TO_CPO_FLAGS); + const char *const rhs = replace_termcodes(str, strlen(str), + &buf, REPTERM_DO_LT, + NULL, CPO_TO_CPO_FLAGS); #define MAPMODE(mode, modechars, chr, modeflags) \ do { \ @@ -1079,7 +1090,7 @@ bool map_to_exists(const char *const str, const char *const modechars, const boo MAPMODE(mode, modechars, 'c', MODE_CMDLINE); #undef MAPMODE - retval = map_to_exists_mode((char *)rhs, mode, abbr); + retval = map_to_exists_mode(rhs, mode, abbr); xfree(buf); return retval; @@ -1188,7 +1199,7 @@ static char_u *translate_mapping(char_u *str, int cpo_flags) } if (c) { - ga_append(&ga, (char)c); + ga_append(&ga, (uint8_t)c); } } ga_append(&ga, NUL); @@ -1201,7 +1212,7 @@ static char_u *translate_mapping(char_u *str, int cpo_flags) /// @param forceit true if '!' given /// @param isabbrev true if abbreviation /// @param isunmap true if unmap/unabbrev command -char_u *set_context_in_map_cmd(expand_T *xp, char *cmd, char_u *arg, bool forceit, bool isabbrev, +char_u *set_context_in_map_cmd(expand_T *xp, char *cmd, char *arg, bool forceit, bool isabbrev, bool isunmap, cmdidx_T cmdidx) { if (forceit && cmdidx != CMD_map && cmdidx != CMD_unmap) { @@ -1219,38 +1230,38 @@ char_u *set_context_in_map_cmd(expand_T *xp, char *cmd, char_u *arg, bool forcei xp->xp_context = EXPAND_MAPPINGS; expand_buffer = false; for (;;) { - if (STRNCMP(arg, "<buffer>", 8) == 0) { + if (strncmp(arg, "<buffer>", 8) == 0) { expand_buffer = true; - arg = (char_u *)skipwhite((char *)arg + 8); + arg = skipwhite(arg + 8); continue; } - if (STRNCMP(arg, "<unique>", 8) == 0) { - arg = (char_u *)skipwhite((char *)arg + 8); + if (strncmp(arg, "<unique>", 8) == 0) { + arg = skipwhite(arg + 8); continue; } - if (STRNCMP(arg, "<nowait>", 8) == 0) { - arg = (char_u *)skipwhite((char *)arg + 8); + if (strncmp(arg, "<nowait>", 8) == 0) { + arg = skipwhite(arg + 8); continue; } - if (STRNCMP(arg, "<silent>", 8) == 0) { - arg = (char_u *)skipwhite((char *)arg + 8); + if (strncmp(arg, "<silent>", 8) == 0) { + arg = skipwhite(arg + 8); continue; } - if (STRNCMP(arg, "<special>", 9) == 0) { - arg = (char_u *)skipwhite((char *)arg + 9); + if (strncmp(arg, "<special>", 9) == 0) { + arg = skipwhite(arg + 9); continue; } - if (STRNCMP(arg, "<script>", 8) == 0) { - arg = (char_u *)skipwhite((char *)arg + 8); + if (strncmp(arg, "<script>", 8) == 0) { + arg = skipwhite(arg + 8); continue; } - if (STRNCMP(arg, "<expr>", 6) == 0) { - arg = (char_u *)skipwhite((char *)arg + 6); + if (strncmp(arg, "<expr>", 6) == 0) { + arg = skipwhite(arg + 6); continue; } break; } - xp->xp_pattern = (char *)arg; + xp->xp_pattern = arg; } return NULL; @@ -1259,98 +1270,140 @@ char_u *set_context_in_map_cmd(expand_T *xp, char *cmd, char_u *arg, bool forcei /// Find all mapping/abbreviation names that match regexp "regmatch". /// For command line expansion of ":[un]map" and ":[un]abbrev" in all modes. /// @return OK if matches found, FAIL otherwise. -int ExpandMappings(regmatch_T *regmatch, int *num_file, char ***file) +int ExpandMappings(char *pat, regmatch_T *regmatch, int *numMatches, char ***matches) { - mapblock_T *mp; - int hash; - int count; - int round; - char *p; - int i; - - *num_file = 0; // return values in case of FAIL - *file = NULL; - - // round == 1: Count the matches. - // round == 2: Build the array to keep the matches. - for (round = 1; round <= 2; round++) { - count = 0; - - for (i = 0; i < 7; i++) { - if (i == 0) { - p = "<silent>"; - } else if (i == 1) { - p = "<unique>"; - } else if (i == 2) { - p = "<script>"; - } else if (i == 3) { - p = "<expr>"; - } else if (i == 4 && !expand_buffer) { - p = "<buffer>"; - } else if (i == 5) { - p = "<nowait>"; - } else if (i == 6) { - p = "<special>"; - } else { + const bool fuzzy = cmdline_fuzzy_complete(pat); + + *numMatches = 0; // return values in case of FAIL + *matches = NULL; + + garray_T ga; + if (!fuzzy) { + ga_init(&ga, sizeof(char *), 3); + } else { + ga_init(&ga, sizeof(fuzmatch_str_T), 3); + } + + // First search in map modifier arguments + for (int i = 0; i < 7; i++) { + char *p; + if (i == 0) { + p = "<silent>"; + } else if (i == 1) { + p = "<unique>"; + } else if (i == 2) { + p = "<script>"; + } else if (i == 3) { + p = "<expr>"; + } else if (i == 4 && !expand_buffer) { + p = "<buffer>"; + } else if (i == 5) { + p = "<nowait>"; + } else if (i == 6) { + p = "<special>"; + } else { + continue; + } + + bool match; + int score = 0; + if (!fuzzy) { + match = vim_regexec(regmatch, p, (colnr_T)0); + } else { + score = fuzzy_match_str(p, pat); + match = (score != 0); + } + + if (!match) { + continue; + } + + if (fuzzy) { + GA_APPEND(fuzmatch_str_T, &ga, ((fuzmatch_str_T){ + .idx = ga.ga_len, + .str = xstrdup(p), + .score = score, + })); + } else { + GA_APPEND(char *, &ga, xstrdup(p)); + } + } + + for (int hash = 0; hash < 256; hash++) { + mapblock_T *mp; + if (expand_isabbrev) { + if (hash > 0) { // only one abbrev list + break; // for (hash) + } + mp = first_abbr; + } else if (expand_buffer) { + mp = curbuf->b_maphash[hash]; + } else { + mp = maphash[hash]; + } + for (; mp; mp = mp->m_next) { + if (!(mp->m_mode & expand_mapmodes)) { continue; } - if (vim_regexec(regmatch, p, (colnr_T)0)) { - if (round == 1) { - count++; - } else { - (*file)[count++] = xstrdup(p); - } + char *p = (char *)translate_mapping((char_u *)mp->m_keys, CPO_TO_CPO_FLAGS); + if (p == NULL) { + continue; } - } - for (hash = 0; hash < 256; hash++) { - if (expand_isabbrev) { - if (hash > 0) { // only one abbrev list - break; // for (hash) - } - mp = first_abbr; - } else if (expand_buffer) { - mp = curbuf->b_maphash[hash]; + bool match; + int score = 0; + if (!fuzzy) { + match = vim_regexec(regmatch, p, (colnr_T)0); } else { - mp = maphash[hash]; + score = fuzzy_match_str(p, pat); + match = (score != 0); } - for (; mp; mp = mp->m_next) { - if (mp->m_mode & expand_mapmodes) { - p = (char *)translate_mapping(mp->m_keys, CPO_TO_CPO_FLAGS); - if (p != NULL && vim_regexec(regmatch, p, (colnr_T)0)) { - if (round == 1) { - count++; - } else { - (*file)[count++] = p; - p = NULL; - } - } - xfree(p); - } - } // for (mp) - } // for (hash) - if (count == 0) { // no match found - break; // for (round) - } + if (!match) { + xfree(p); + continue; + } - if (round == 1) { - *file = xmalloc((size_t)count * sizeof(char_u *)); - } - } // for (round) + if (fuzzy) { + GA_APPEND(fuzmatch_str_T, &ga, ((fuzmatch_str_T){ + .idx = ga.ga_len, + .str = p, + .score = score, + })); + } else { + GA_APPEND(char *, &ga, p); + } + } // for (mp) + } // for (hash) + + if (ga.ga_len == 0) { + return FAIL; + } + if (!fuzzy) { + *matches = ga.ga_data; + *numMatches = ga.ga_len; + } else { + fuzzymatches_to_strmatches(ga.ga_data, matches, ga.ga_len, false); + *numMatches = ga.ga_len; + } + + int count = *numMatches; if (count > 1) { // Sort the matches - sort_strings(*file, count); + // Fuzzy matching already sorts the matches + if (!fuzzy) { + sort_strings(*matches, count); + } // Remove multiple entries - char **ptr1 = *file; + char **ptr1 = *matches; char **ptr2 = ptr1 + 1; char **ptr3 = ptr1 + count; while (ptr2 < ptr3) { - if (strcmp(*ptr1, *ptr2)) { + if (strcmp(*ptr1, *ptr2) != 0) { *++ptr1 = *ptr2++; } else { xfree(*ptr2++); @@ -1359,7 +1412,7 @@ int ExpandMappings(regmatch_T *regmatch, int *num_file, char ***file) } } - *num_file = count; + *numMatches = count; return count == 0 ? FAIL : OK; } @@ -1379,12 +1432,12 @@ int ExpandMappings(regmatch_T *regmatch, int *num_file, char ***file) // Then there must be white space before the abbr. // // Return true if there is an abbreviation, false if not. -bool check_abbr(int c, char_u *ptr, int col, int mincol) +bool check_abbr(int c, char *ptr, int col, int mincol) { int len; int scol; // starting column of the abbr. int j; - char_u *s; + char *s; char_u tb[MB_MAXBYTES + 4]; mapblock_T *mp; mapblock_T *mp2; @@ -1410,7 +1463,7 @@ bool check_abbr(int c, char_u *ptr, int col, int mincol) { bool vim_abbr; - char_u *p = mb_prevptr(ptr, ptr + col); + char *p = mb_prevptr(ptr, ptr + col); if (!vim_iswordp(p)) { vim_abbr = true; // Vim added abbr. } else { @@ -1423,7 +1476,7 @@ bool check_abbr(int c, char_u *ptr, int col, int mincol) while (p > ptr + mincol) { p = mb_prevptr(ptr, p); if (ascii_isspace(*p) || (!vim_abbr && is_id != vim_iswordp(p))) { - p += utfc_ptr2len((char *)p); + p += utfc_ptr2len(p); break; } clen++; @@ -1447,20 +1500,20 @@ bool check_abbr(int c, char_u *ptr, int col, int mincol) mp->m_next == NULL ? (mp = mp2, mp2 = NULL) : (mp = mp->m_next)) { int qlen = mp->m_keylen; - char *q = (char *)mp->m_keys; + char *q = mp->m_keys; int match; if (strchr((const char *)mp->m_keys, K_SPECIAL) != NULL) { // Might have K_SPECIAL escaped mp->m_keys. - q = xstrdup((char *)mp->m_keys); + q = xstrdup(mp->m_keys); vim_unescape_ks((char_u *)q); - qlen = (int)STRLEN(q); + qlen = (int)strlen(q); } // find entries with right mode and keys match = (mp->m_mode & State) && qlen == len - && !STRNCMP(q, ptr, (size_t)len); - if (q != (char *)mp->m_keys) { + && !strncmp(q, ptr, (size_t)len); + if (q != mp->m_keys) { xfree(q); } if (match) { @@ -1497,9 +1550,9 @@ bool check_abbr(int c, char_u *ptr, int col, int mincol) int newlen = utf_char2bytes(c, (char *)tb + j); tb[j + newlen] = NUL; // Need to escape K_SPECIAL. - char_u *escaped = (char_u *)vim_strsave_escape_ks((char *)tb + j); + char *escaped = vim_strsave_escape_ks((char *)tb + j); if (escaped != NULL) { - newlen = (int)STRLEN(escaped); + newlen = (int)strlen(escaped); memmove(tb + j, escaped, (size_t)newlen); j += newlen; xfree(escaped); @@ -1509,17 +1562,23 @@ bool check_abbr(int c, char_u *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) { - s = (char_u *)eval_map_expr(mp, c); + + // 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 = (char_u *)mp->m_str; + s = mp->m_str; } if (s != NULL) { // insert the to string - (void)ins_typebuf((char *)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) { + typebuf.tb_no_abbr_cnt += (int)strlen(s) + j + 1; + if (expr) { xfree(s); } } @@ -1528,7 +1587,7 @@ bool check_abbr(int c, char_u *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; } @@ -1538,6 +1597,7 @@ bool check_abbr(int c, char_u *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) @@ -1552,6 +1612,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++; @@ -1588,8 +1650,8 @@ char *eval_map_expr(mapblock_T *mp, int c) char *res = NULL; - if (mp->m_replace_keycodes) { - replace_termcodes(p, STRLEN(p), &res, REPTERM_DO_LT, NULL, CPO_TO_CPO_FLAGS); + 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. res = vim_strsave_escape_ks(p); @@ -1606,8 +1668,8 @@ char *eval_map_expr(mapblock_T *mp, int c) int makemap(FILE *fd, buf_T *buf) { mapblock_T *mp; - char_u c1, c2, c3; - char_u *p; + char c1, c2, c3; + char *p; char *cmd; int abbr; int hash; @@ -1645,8 +1707,8 @@ int makemap(FILE *fd, buf_T *buf) if (mp->m_luaref != LUA_NOREF) { continue; } - for (p = (char_u *)mp->m_str; *p != NUL; p++) { - if (p[0] == K_SPECIAL && p[1] == KS_EXTRA + for (p = mp->m_str; *p != NUL; p++) { + if ((uint8_t)p[0] == K_SPECIAL && (uint8_t)p[1] == KS_EXTRA && p[2] == KE_SNR) { break; } @@ -1791,7 +1853,7 @@ int makemap(FILE *fd, buf_T *buf) if (putc(' ', fd) < 0 || put_escstr(fd, mp->m_keys, 0) == FAIL || putc(' ', fd) < 0 - || put_escstr(fd, (char_u *)mp->m_str, 1) == FAIL + || put_escstr(fd, mp->m_str, 1) == FAIL || put_eol(fd) < 0) { return FAIL; } @@ -1817,9 +1879,9 @@ int makemap(FILE *fd, buf_T *buf) // "what": 0 for :map lhs, 1 for :map rhs, 2 for :set // // return FAIL for failure, OK otherwise -int put_escstr(FILE *fd, char_u *strstart, int what) +int put_escstr(FILE *fd, char *strstart, int what) { - char_u *str = strstart; + char_u *str = (char_u *)strstart; int c; // :map xx <Nop> @@ -1896,7 +1958,7 @@ int put_escstr(FILE *fd, char_u *strstart, int what) } } else if (c < ' ' || c > '~' || c == '|' || (what == 0 && c == ' ') - || (what == 1 && str == strstart && c == ' ') + || (what == 1 && str == (char_u *)strstart && c == ' ') || (what != 2 && c == '<')) { if (putc(Ctrl_V, fd) < 0) { return FAIL; @@ -1918,14 +1980,14 @@ int put_escstr(FILE *fd, char_u *strstart, int what) /// @param abbr do abbreviations /// @param mp_ptr return: pointer to mapblock or NULL /// @param local_ptr return: buffer-local mapping or NULL -char_u *check_map(char_u *keys, int mode, int exact, int ign_mod, int abbr, mapblock_T **mp_ptr, - int *local_ptr, int *rhs_lua) +char *check_map(char *keys, int mode, int exact, int ign_mod, int abbr, mapblock_T **mp_ptr, + int *local_ptr, int *rhs_lua) { int len, minlen; mapblock_T *mp; *rhs_lua = LUA_NOREF; - len = (int)STRLEN(keys); + len = (int)strlen(keys); for (int local = 1; local >= 0; local--) { // loop over all hash lists for (int hash = 0; hash < 256; hash++) { @@ -1946,15 +2008,15 @@ char_u *check_map(char_u *keys, int mode, int exact, int ign_mod, int abbr, mapb for (; mp != NULL; mp = mp->m_next) { // skip entries with wrong mode, wrong length and not matching ones if ((mp->m_mode & mode) && (!exact || mp->m_keylen == len)) { - char_u *s = mp->m_keys; + char *s = mp->m_keys; int keylen = mp->m_keylen; if (ign_mod && keylen >= 3 - && s[0] == K_SPECIAL && s[1] == KS_MODIFIER) { + && (uint8_t)s[0] == K_SPECIAL && (uint8_t)s[1] == KS_MODIFIER) { s += 3; keylen -= 3; } minlen = keylen < len ? keylen : len; - if (STRNCMP(s, keys, minlen) == 0) { + if (strncmp(s, keys, (size_t)minlen) == 0) { if (mp_ptr != NULL) { *mp_ptr = mp; } @@ -1962,7 +2024,7 @@ char_u *check_map(char_u *keys, int mode, int exact, int ign_mod, int abbr, mapb *local_ptr = local; } *rhs_lua = mp->m_luaref; - return mp->m_luaref == LUA_NOREF ? (char_u *)mp->m_str : NULL; + return mp->m_luaref == LUA_NOREF ? mp->m_str : NULL; } } } @@ -2007,7 +2069,7 @@ static Dictionary mapblock_fill_dict(const mapblock_T *const mp, const char *lhs FUNC_ATTR_NONNULL_ARG(1) { Dictionary dict = ARRAY_DICT_INIT; - char *const lhs = str2special_save((const char *)mp->m_keys, compatible, !compatible); + char *const lhs = str2special_save(mp->m_keys, compatible, !compatible); char *const mapmode = map_mode_to_chars(mp->m_mode); varnumber_T noremap_value; @@ -2090,13 +2152,13 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact) const int flags = REPTERM_FROM_PART | REPTERM_DO_LT; const int mode = get_map_mode((char **)&which, 0); - char_u *keys_simplified - = (char_u *)replace_termcodes(keys, strlen(keys), &keys_buf, flags, &did_simplify, - CPO_TO_CPO_FLAGS); + char *keys_simplified = replace_termcodes(keys, strlen(keys), &keys_buf, flags, &did_simplify, + CPO_TO_CPO_FLAGS); mapblock_T *mp = NULL; int buffer_local; LuaRef rhs_lua; - char_u *rhs = check_map(keys_simplified, mode, exact, false, abbr, &mp, &buffer_local, &rhs_lua); + char *rhs = check_map(keys_simplified, mode, exact, false, abbr, &mp, &buffer_local, + &rhs_lua); if (did_simplify) { // When the lhs is being simplified the not-simplified keys are // preferred for printing, like in do_map(). @@ -2104,7 +2166,7 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact) strlen(keys), &alt_keys_buf, flags | REPTERM_NO_SIMPLIFY, NULL, CPO_TO_CPO_FLAGS); - rhs = check_map((char_u *)alt_keys_buf, mode, exact, false, abbr, &mp, &buffer_local, &rhs_lua); + rhs = check_map(alt_keys_buf, mode, exact, false, abbr, &mp, &buffer_local, &rhs_lua); } if (!get_dict) { @@ -2113,7 +2175,7 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact) if (*rhs == NUL) { rettv->vval.v_string = xstrdup("<Nop>"); } else { - rettv->vval.v_string = str2special_save((char *)rhs, false, false); + rettv->vval.v_string = str2special_save(rhs, false, false); } } else if (rhs_lua != LUA_NOREF) { rettv->vval.v_string = nlua_funcref_str(mp->m_luaref); @@ -2122,7 +2184,7 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact) // Return a dictionary. if (mp != NULL && (rhs != NULL || rhs_lua != LUA_NOREF)) { Dictionary dict = mapblock_fill_dict(mp, - did_simplify ? (char *)keys_simplified : NULL, + did_simplify ? keys_simplified : NULL, buffer_local, true); (void)object_to_vim(DICTIONARY_OBJ(dict), rettv, NULL); api_free_dictionary(dict); @@ -2332,14 +2394,14 @@ void langmap_init(void) /// changed at any time! void langmap_set(void) { - char_u *p; - char_u *p2; + char *p; + char *p2; int from, to; ga_clear(&langmap_mapga); // clear the previous map first langmap_init(); // back to one-to-one map - for (p = (char_u *)p_langmap; p[0] != NUL;) { + for (p = p_langmap; p[0] != NUL;) { for (p2 = p; p2[0] != NUL && p2[0] != ',' && p2[0] != ';'; MB_PTR_ADV(p2)) { if (p2[0] == '\\' && p2[1] != NUL) { @@ -2359,7 +2421,7 @@ void langmap_set(void) if (p[0] == '\\' && p[1] != NUL) { p++; } - from = utf_ptr2char((char *)p); + from = utf_ptr2char(p); to = NUL; if (p2 == NULL) { MB_PTR_ADV(p); @@ -2367,14 +2429,14 @@ void langmap_set(void) if (p[0] == '\\') { p++; } - to = utf_ptr2char((char *)p); + to = utf_ptr2char(p); } } else { if (p2[0] != ',') { if (p2[0] == '\\') { p2++; } - to = utf_ptr2char((char *)p2); + to = utf_ptr2char(p2); } } if (to == NUL) { @@ -2398,8 +2460,7 @@ void langmap_set(void) p = p2; if (p[0] != NUL) { if (p[0] != ',') { - semsg(_("E358: 'langmap': Extra characters after semicolon: %s"), - p); + semsg(_("E358: 'langmap': Extra characters after semicolon: %s"), p); return; } p++; @@ -2419,7 +2480,7 @@ static void do_exmap(exarg_T *eap, int isabbrev) switch (do_map((*cmdp == 'n') ? MAPTYPE_NOREMAP : (*cmdp == 'u') ? MAPTYPE_UNMAP : MAPTYPE_MAP, - (char_u *)eap->arg, mode, isabbrev)) { + eap->arg, mode, isabbrev)) { case 1: emsg(_(e_invarg)); break; @@ -2438,8 +2499,7 @@ void ex_abbreviate(exarg_T *eap) /// ":map" and friends. void ex_map(exarg_T *eap) { - // If we are sourcing .exrc or .vimrc in current directory we - // print the mappings for security reasons. + // If we are in a secure mode we print the mappings for security reasons. if (secure) { secure = 2; msg_outtrans(eap->cmd); @@ -2540,7 +2600,7 @@ void modify_keymap(uint64_t channel_id, Buffer buffer, bool is_unmap, String mod } int mode_val; // integer value of the mapping mode, to be passed to do_map() char *p = (mode.size) ? mode.data : "m"; - if (STRNCMP(p, "!", 2) == 0) { + if (strncmp(p, "!", 2) == 0) { mode_val = get_map_mode(&p, true); // mapmode-ic } else { mode_val = get_map_mode(&p, false); |