neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

mapping.c (91612B)


      1 // mapping.c: Code for mappings and abbreviations.
      2 
      3 #include <assert.h>
      4 #include <lauxlib.h>
      5 #include <limits.h>
      6 #include <stdbool.h>
      7 #include <stdint.h>
      8 #include <stdio.h>
      9 #include <stdlib.h>
     10 #include <string.h>
     11 
     12 #include "klib/kvec.h"
     13 #include "nvim/api/keysets_defs.h"
     14 #include "nvim/api/private/converter.h"
     15 #include "nvim/api/private/defs.h"
     16 #include "nvim/api/private/dispatch.h"
     17 #include "nvim/api/private/helpers.h"
     18 #include "nvim/ascii_defs.h"
     19 #include "nvim/buffer_defs.h"
     20 #include "nvim/charset.h"
     21 #include "nvim/cmdexpand.h"
     22 #include "nvim/cmdexpand_defs.h"
     23 #include "nvim/errors.h"
     24 #include "nvim/eval.h"
     25 #include "nvim/eval/typval.h"
     26 #include "nvim/eval/typval_defs.h"
     27 #include "nvim/eval/userfunc.h"
     28 #include "nvim/eval/vars.h"
     29 #include "nvim/ex_cmds_defs.h"
     30 #include "nvim/ex_session.h"
     31 #include "nvim/fuzzy.h"
     32 #include "nvim/garray.h"
     33 #include "nvim/garray_defs.h"
     34 #include "nvim/getchar.h"
     35 #include "nvim/getchar_defs.h"
     36 #include "nvim/gettext_defs.h"
     37 #include "nvim/globals.h"
     38 #include "nvim/highlight_defs.h"
     39 #include "nvim/keycodes.h"
     40 #include "nvim/lua/executor.h"
     41 #include "nvim/macros_defs.h"
     42 #include "nvim/mapping.h"
     43 #include "nvim/mapping_defs.h"
     44 #include "nvim/mbyte.h"
     45 #include "nvim/mbyte_defs.h"
     46 #include "nvim/memory.h"
     47 #include "nvim/memory_defs.h"
     48 #include "nvim/message.h"
     49 #include "nvim/option_defs.h"
     50 #include "nvim/option_vars.h"
     51 #include "nvim/pos_defs.h"
     52 #include "nvim/regexp.h"
     53 #include "nvim/regexp_defs.h"
     54 #include "nvim/runtime.h"
     55 #include "nvim/state_defs.h"
     56 #include "nvim/strings.h"
     57 #include "nvim/types_defs.h"
     58 #include "nvim/ui.h"
     59 #include "nvim/ui_defs.h"
     60 #include "nvim/vim_defs.h"
     61 
     62 /// List used for abbreviations.
     63 static mapblock_T *first_abbr = NULL;  // first entry in abbrlist
     64 
     65 // Each mapping is put in one of the MAX_MAPHASH hash lists,
     66 // to speed up finding it.
     67 static mapblock_T *(maphash[MAX_MAPHASH]) = { 0 };
     68 
     69 // Make a hash value for a mapping.
     70 // "mode" is the lower 4 bits of the State for the mapping.
     71 // "c1" is the first character of the "lhs".
     72 // Returns a value between 0 and 255, index in maphash.
     73 // Put Normal/Visual mode mappings mostly separately from Insert/Cmdline mode.
     74 #define MAP_HASH(mode, \
     75                 c1) (((mode) & \
     76                       (MODE_NORMAL | MODE_VISUAL | MODE_SELECT | \
     77                        MODE_OP_PENDING | MODE_TERMINAL)) ? (c1) : ((c1) ^ 0x80))
     78 
     79 /// All possible |:map-arguments| usable in a |:map| command.
     80 ///
     81 /// The <special> argument has no effect on mappings and is excluded from this
     82 /// struct declaration. |:noremap| is included, since it behaves like a map
     83 /// argument when used in a mapping.
     84 ///
     85 /// @see mapblock_T
     86 struct map_arguments {
     87  bool buffer;
     88  bool expr;
     89  bool noremap;
     90  bool nowait;
     91  bool script;
     92  bool silent;
     93  bool unique;
     94  bool replace_keycodes;
     95 
     96  /// The {lhs} of the mapping.
     97  ///
     98  /// vim limits this to MAXMAPLEN characters, allowing us to use a static
     99  /// buffer. Setting lhs_len to a value larger than MAXMAPLEN can signal
    100  /// that {lhs} was too long and truncated.
    101  char lhs[MAXMAPLEN + 1];
    102  size_t lhs_len;
    103 
    104  /// Unsimplifed {lhs} of the mapping. If no simplification has been done then alt_lhs_len is 0.
    105  char alt_lhs[MAXMAPLEN + 1];
    106  size_t alt_lhs_len;
    107 
    108  char *rhs;  ///< The {rhs} of the mapping.
    109  size_t rhs_len;
    110  LuaRef rhs_lua;  ///< lua function as {rhs}
    111  bool rhs_is_noop;  ///< True when the {rhs} should be <Nop>.
    112 
    113  char *orig_rhs;  ///< The original text of the {rhs}.
    114  size_t orig_rhs_len;
    115  char *desc;  ///< map description
    116 };
    117 typedef struct map_arguments MapArguments;
    118 #define MAP_ARGUMENTS_INIT { false, false, false, false, false, false, false, false, \
    119                             { 0 }, 0, { 0 }, 0, NULL, 0, LUA_NOREF, false, NULL, 0, NULL }
    120 
    121 #include "mapping.c.generated.h"
    122 
    123 static const char e_global_abbreviation_already_exists_for_str[]
    124  = N_("E224: Global abbreviation already exists for %s");
    125 static const char e_global_mapping_already_exists_for_str[]
    126  = N_("E225: Global mapping already exists for %s");
    127 static const char e_abbreviation_already_exists_for_str[]
    128  = N_("E226: Abbreviation already exists for %s");
    129 static const char e_mapping_already_exists_for_str[]
    130  = N_("E227: Mapping already exists for %s");
    131 static const char e_entries_missing_in_mapset_dict_argument[]
    132  = N_("E460: Entries missing in mapset() dict argument");
    133 static const char e_illegal_map_mode_string_str[]
    134  = N_("E1276: Illegal map mode string: '%s'");
    135 
    136 /// Get the start of the hashed map list for "state" and first character "c".
    137 mapblock_T *get_maphash_list(int state, int c)
    138 {
    139  return maphash[MAP_HASH(state, c)];
    140 }
    141 
    142 /// Get the buffer-local hashed map list for "state" and first character "c".
    143 mapblock_T *get_buf_maphash_list(int state, int c)
    144 {
    145  return curbuf->b_maphash[MAP_HASH(state, c)];
    146 }
    147 
    148 /// Delete one entry from the abbrlist or maphash[].
    149 /// "mpp" is a pointer to the m_next field of the PREVIOUS entry!
    150 static void mapblock_free(mapblock_T **mpp)
    151 {
    152  mapblock_T *mp = *mpp;
    153  xfree(mp->m_keys);
    154  if (mp->m_alt != NULL) {
    155    mp->m_alt->m_alt = NULL;
    156  } else {
    157    NLUA_CLEAR_REF(mp->m_luaref);
    158    xfree(mp->m_str);
    159    xfree(mp->m_orig_str);
    160    xfree(mp->m_desc);
    161  }
    162  *mpp = mp->m_next;
    163  xfree(mp);
    164 }
    165 
    166 /// put characters to represent the map mode in a string buffer
    167 ///
    168 /// @param[out] buf must be at least 7 bytes (including NUL)
    169 void map_mode_to_chars(int mode, char *buf)
    170  FUNC_ATTR_NONNULL_ALL
    171 {
    172  char *p = buf;
    173  if ((mode & (MODE_INSERT | MODE_CMDLINE)) == (MODE_INSERT | MODE_CMDLINE)) {
    174    *p++ = '!';                           // :map!
    175  } else if (mode & MODE_INSERT) {
    176    *p++ = 'i';                           // :imap
    177  } else if (mode & MODE_LANGMAP) {
    178    *p++ = 'l';                           // :lmap
    179  } else if (mode & MODE_CMDLINE) {
    180    *p++ = 'c';                           // :cmap
    181  } else if ((mode & (MODE_NORMAL | MODE_VISUAL | MODE_SELECT | MODE_OP_PENDING))
    182             == (MODE_NORMAL | MODE_VISUAL | MODE_SELECT | MODE_OP_PENDING)) {
    183    *p++ = ' ';                           // :map
    184  } else {
    185    if (mode & MODE_NORMAL) {
    186      *p++ = 'n';                         // :nmap
    187    }
    188    if (mode & MODE_OP_PENDING) {
    189      *p++ = 'o';                         // :omap
    190    }
    191    if (mode & MODE_TERMINAL) {
    192      *p++ = 't';                         // :tmap
    193    }
    194    if ((mode & (MODE_VISUAL | MODE_SELECT)) == (MODE_VISUAL | MODE_SELECT)) {
    195      *p++ = 'v';                         // :vmap
    196    } else {
    197      if (mode & MODE_VISUAL) {
    198        *p++ = 'x';                       // :xmap
    199      }
    200      if (mode & MODE_SELECT) {
    201        *p++ = 's';                       // :smap
    202      }
    203    }
    204  }
    205 
    206  *p = NUL;
    207 }
    208 
    209 /// @param local  true for buffer-local map
    210 static void showmap(mapblock_T *mp, bool local)
    211 {
    212  if (message_filtered(mp->m_keys) && message_filtered(mp->m_str)
    213      && (mp->m_desc == NULL || message_filtered(mp->m_desc))) {
    214    return;
    215  }
    216 
    217  if (msg_col > 0 || msg_silent != 0) {
    218    msg_putchar('\n');
    219    if (got_int) {          // 'q' typed at MORE prompt
    220      return;
    221    }
    222  }
    223 
    224  char mapchars[7];
    225  map_mode_to_chars(mp->m_mode, mapchars);
    226  msg_puts(mapchars);
    227  size_t len = strlen(mapchars);
    228 
    229  while (++len <= 3) {
    230    msg_putchar(' ');
    231  }
    232 
    233  // Display the LHS.  Get length of what we write.
    234  len = (size_t)msg_outtrans_special(mp->m_keys, true, 0);
    235  do {
    236    msg_putchar(' ');                   // pad with blanks
    237    len++;
    238  } while (len < 12);
    239 
    240  if (mp->m_noremap == REMAP_NONE) {
    241    msg_puts_hl("*", HLF_8, false);
    242  } else if (mp->m_noremap == REMAP_SCRIPT) {
    243    msg_puts_hl("&", HLF_8, false);
    244  } else {
    245    msg_putchar(' ');
    246  }
    247 
    248  if (local) {
    249    msg_putchar('@');
    250  } else {
    251    msg_putchar(' ');
    252  }
    253 
    254  // Use false below if we only want things like <Up> to show up as such on
    255  // the rhs, and not M-x etc, true gets both -- webb
    256  if (mp->m_luaref != LUA_NOREF) {
    257    char *str = nlua_funcref_str(mp->m_luaref, NULL);
    258    msg_puts_hl(str, HLF_8, false);
    259    xfree(str);
    260  } else if (mp->m_str[0] == NUL) {
    261    msg_puts_hl("<Nop>", HLF_8, false);
    262  } else {
    263    msg_outtrans_special(mp->m_str, false, 0);
    264  }
    265 
    266  if (mp->m_desc != NULL) {
    267    msg_puts("\n                 ");  // Shift line to same level as rhs.
    268    msg_puts(mp->m_desc);
    269  }
    270  if (p_verbose > 0) {
    271    last_set_msg(mp->m_script_ctx);
    272  }
    273  msg_clr_eos();
    274 }
    275 
    276 /// Replace termcodes in the given LHS and RHS and store the results into the
    277 /// `lhs` and `rhs` of the given @ref MapArguments struct.
    278 ///
    279 /// `rhs` and `orig_rhs` will both point to new allocated buffers. `orig_rhs`
    280 /// will hold a copy of the given `orig_rhs`.
    281 ///
    282 /// The `*_len` variables will be set appropriately. If the length of
    283 /// the final `lhs` exceeds `MAXMAPLEN`, `lhs_len` will be set equal to the
    284 /// original larger length and `lhs` will be truncated.
    285 ///
    286 /// If RHS should be <Nop>, `rhs` will be an empty string, `rhs_len` will be
    287 /// zero, and `rhs_is_noop` will be set to true.
    288 ///
    289 /// Any memory allocated by @ref replace_termcodes is freed before this function
    290 /// returns.
    291 ///
    292 /// @param[in] orig_lhs   Original mapping LHS, with characters to replace.
    293 /// @param[in] orig_lhs_len   `strlen` of orig_lhs.
    294 /// @param[in] orig_rhs   Original mapping RHS, with characters to replace.
    295 /// @param[in] rhs_lua   Lua reference for Lua mappings.
    296 /// @param[in] orig_rhs_len   `strlen` of orig_rhs.
    297 /// @param[in] cpo_val  See param docs for @ref replace_termcodes.
    298 /// @param[out] mapargs   MapArguments struct holding the replaced strings.
    299 static bool set_maparg_lhs_rhs(const char *const orig_lhs, const size_t orig_lhs_len,
    300                               const char *const orig_rhs, const size_t orig_rhs_len,
    301                               const LuaRef rhs_lua, const char *const cpo_val,
    302                               MapArguments *const mapargs)
    303 {
    304  char lhs_buf[128];
    305 
    306  // If mapping has been given as ^V<C_UP> say, then replace the term codes
    307  // with the appropriate two bytes. If it is a shifted special key, unshift
    308  // it too, giving another two bytes.
    309  //
    310  // replace_termcodes() may move the result to allocated memory, which
    311  // needs to be freed later (*lhs_buf and *rhs_buf).
    312  // replace_termcodes() also removes CTRL-Vs and sometimes backslashes.
    313  // If something like <C-H> is simplified to 0x08 then mark it as simplified
    314  // and also add en entry with a modifier.
    315  bool did_simplify = false;
    316  const int flags = REPTERM_FROM_PART | REPTERM_DO_LT;
    317  char *bufarg = lhs_buf;
    318  char *replaced = replace_termcodes(orig_lhs, orig_lhs_len, &bufarg, 0,
    319                                     flags, &did_simplify, cpo_val);
    320  if (replaced == NULL) {
    321    return false;
    322  }
    323  mapargs->lhs_len = strlen(replaced);
    324  xstrlcpy(mapargs->lhs, replaced, sizeof(mapargs->lhs));
    325  if (did_simplify) {
    326    replaced = replace_termcodes(orig_lhs, orig_lhs_len, &bufarg, 0,
    327                                 flags | REPTERM_NO_SIMPLIFY, NULL, cpo_val);
    328    if (replaced == NULL) {
    329      return false;
    330    }
    331    mapargs->alt_lhs_len = strlen(replaced);
    332    xstrlcpy(mapargs->alt_lhs, replaced, sizeof(mapargs->alt_lhs));
    333  } else {
    334    mapargs->alt_lhs_len = 0;
    335  }
    336 
    337  set_maparg_rhs(orig_rhs, orig_rhs_len, rhs_lua, 0, cpo_val, mapargs);
    338 
    339  return true;
    340 }
    341 
    342 /// @see set_maparg_lhs_rhs
    343 static void set_maparg_rhs(const char *const orig_rhs, const size_t orig_rhs_len,
    344                           const LuaRef rhs_lua, const scid_T sid, const char *const cpo_val,
    345                           MapArguments *const mapargs)
    346 {
    347  mapargs->rhs_lua = rhs_lua;
    348 
    349  if (rhs_lua == LUA_NOREF) {
    350    mapargs->orig_rhs_len = orig_rhs_len;
    351    mapargs->orig_rhs = xcalloc(mapargs->orig_rhs_len + 1, sizeof(char));
    352    xmemcpyz(mapargs->orig_rhs, orig_rhs, mapargs->orig_rhs_len);
    353    if (STRICMP(orig_rhs, "<nop>") == 0) {  // "<Nop>" means nothing
    354      mapargs->rhs = xcalloc(1, sizeof(char));  // single NUL-char
    355      mapargs->rhs_len = 0;
    356      mapargs->rhs_is_noop = true;
    357    } else {
    358      char *rhs_buf = NULL;
    359      char *replaced = replace_termcodes(orig_rhs, orig_rhs_len, &rhs_buf, sid,
    360                                         REPTERM_DO_LT, NULL, cpo_val);
    361      mapargs->rhs_len = strlen(replaced);
    362      // NB: replace_termcodes may produce an empty string even if orig_rhs is non-empty
    363      // (e.g. a single ^V, see :h map-empty-rhs)
    364      mapargs->rhs_is_noop = orig_rhs_len != 0 && mapargs->rhs_len == 0;
    365      mapargs->rhs = replaced;
    366    }
    367  } else {
    368    char tmp_buf[64];
    369    // orig_rhs is not used for Lua mappings, but still needs to be a string.
    370    mapargs->orig_rhs = xcalloc(1, sizeof(char));
    371    mapargs->orig_rhs_len = 0;
    372    // stores <lua>ref_no<cr> in map_str
    373    mapargs->rhs_len = (size_t)vim_snprintf(S_LEN(tmp_buf), "%c%c%c%d\r", K_SPECIAL,
    374                                            KS_EXTRA, KE_LUA, rhs_lua);
    375    mapargs->rhs = xstrdup(tmp_buf);
    376  }
    377 }
    378 
    379 /// Parse a string of |:map-arguments| into a @ref MapArguments struct.
    380 ///
    381 /// Termcodes, backslashes, CTRL-V's, etc. inside the extracted {lhs} and
    382 /// {rhs} are replaced by @ref set_maparg_lhs_rhs.
    383 ///
    384 /// rhs and orig_rhs in the returned mapargs will be set to null or a pointer
    385 /// to allocated memory and should be freed even on error.
    386 ///
    387 /// @param[in]  strargs   String of map args, e.g. "<buffer> <expr><silent>".
    388 ///                       May contain leading or trailing whitespace.
    389 /// @param[in]  is_unmap  True, if strargs should be parsed like an |:unmap|
    390 ///                       command. |:unmap| commands interpret *all* text to the
    391 ///                       right of the last map argument as the {lhs} of the
    392 ///                       mapping, i.e. a literal ' ' character is treated like
    393 ///                       a "<space>", rather than separating the {lhs} from the
    394 ///                       {rhs}.
    395 /// @param[out] mapargs   MapArguments struct holding all extracted argument
    396 ///                       values.
    397 /// @return 0 on success, 1 if invalid arguments are detected.
    398 static int str_to_mapargs(const char *strargs, bool is_unmap, MapArguments *mapargs)
    399 {
    400  const char *to_parse = strargs;
    401  to_parse = skipwhite(to_parse);
    402  CLEAR_POINTER(mapargs);
    403 
    404  // Accept <buffer>, <nowait>, <silent>, <expr>, <script>, and <unique> in
    405  // any order.
    406  while (true) {
    407    if (strncmp(to_parse, "<buffer>", 8) == 0) {
    408      to_parse = skipwhite(to_parse + 8);
    409      mapargs->buffer = true;
    410      continue;
    411    }
    412 
    413    if (strncmp(to_parse, "<nowait>", 8) == 0) {
    414      to_parse = skipwhite(to_parse + 8);
    415      mapargs->nowait = true;
    416      continue;
    417    }
    418 
    419    if (strncmp(to_parse, "<silent>", 8) == 0) {
    420      to_parse = skipwhite(to_parse + 8);
    421      mapargs->silent = true;
    422      continue;
    423    }
    424 
    425    // Ignore obsolete "<special>" modifier.
    426    if (strncmp(to_parse, "<special>", 9) == 0) {
    427      to_parse = skipwhite(to_parse + 9);
    428      continue;
    429    }
    430 
    431    if (strncmp(to_parse, "<script>", 8) == 0) {
    432      to_parse = skipwhite(to_parse + 8);
    433      mapargs->script = true;
    434      continue;
    435    }
    436 
    437    if (strncmp(to_parse, "<expr>", 6) == 0) {
    438      to_parse = skipwhite(to_parse + 6);
    439      mapargs->expr = true;
    440      continue;
    441    }
    442 
    443    if (strncmp(to_parse, "<unique>", 8) == 0) {
    444      to_parse = skipwhite(to_parse + 8);
    445      mapargs->unique = true;
    446      continue;
    447    }
    448    break;
    449  }
    450 
    451  // Find the next whitespace character, call that the end of {lhs}.
    452  //
    453  // If a character (e.g. whitespace) is immediately preceded by a CTRL-V,
    454  // "scan past" that character, i.e. don't "terminate" LHS with that character
    455  // if it's whitespace.
    456  //
    457  // Treat backslash like CTRL-V when 'cpoptions' does not contain 'B'.
    458  //
    459  // With :unmap, literal white space is included in the {lhs}; there is no
    460  // separate {rhs}.
    461  const char *lhs_end = to_parse;
    462  bool do_backslash = (vim_strchr(p_cpo, CPO_BSLASH) == NULL);
    463  while (*lhs_end && (is_unmap || !ascii_iswhite(*lhs_end))) {
    464    if ((lhs_end[0] == Ctrl_V || (do_backslash && lhs_end[0] == '\\'))
    465        && lhs_end[1] != NUL) {
    466      lhs_end++;  // skip CTRL-V or backslash
    467    }
    468    lhs_end++;
    469  }
    470 
    471  // {lhs_end} is a pointer to the "terminating whitespace" after {lhs}.
    472  // Use that to initialize {rhs_start}.
    473  const char *rhs_start = skipwhite(lhs_end);
    474 
    475  // Given {lhs} might be larger than MAXMAPLEN before replace_termcodes
    476  // (e.g. "<Space>" is longer than ' '), so first copy into a buffer.
    477  size_t orig_lhs_len = (size_t)(lhs_end - to_parse);
    478  if (orig_lhs_len >= 256) {
    479    return 1;
    480  }
    481  char lhs_to_replace[256];
    482  xmemcpyz(lhs_to_replace, to_parse, orig_lhs_len);
    483 
    484  size_t orig_rhs_len = strlen(rhs_start);
    485  if (!set_maparg_lhs_rhs(lhs_to_replace, orig_lhs_len,
    486                          rhs_start, orig_rhs_len, LUA_NOREF,
    487                          p_cpo, mapargs)) {
    488    return 1;
    489  }
    490 
    491  if (mapargs->lhs_len > MAXMAPLEN) {
    492    return 1;
    493  }
    494  return 0;
    495 }
    496 
    497 /// @param args  "rhs", "rhs_lua", "orig_rhs", "expr", "silent", "nowait",
    498 ///              "replace_keycodes" and "desc" fields are used.
    499 /// @param sid  0 to use current_sctx
    500 static mapblock_T *map_add(buf_T *buf, mapblock_T **map_table, mapblock_T **abbr_table,
    501                           const char *keys, MapArguments *args, int noremap, int mode,
    502                           bool is_abbr, scid_T sid, linenr_T lnum, bool simplified)
    503  FUNC_ATTR_NONNULL_RET
    504 {
    505  mapblock_T *mp = xcalloc(1, sizeof(mapblock_T));
    506 
    507  // If CTRL-C has been mapped, don't always use it for Interrupting.
    508  if (*keys == Ctrl_C) {
    509    if (map_table == buf->b_maphash) {
    510      buf->b_mapped_ctrl_c |= mode;
    511    } else {
    512      mapped_ctrl_c |= mode;
    513    }
    514  }
    515 
    516  mp->m_keys = xstrdup(keys);
    517  mp->m_str = args->rhs;
    518  mp->m_orig_str = args->orig_rhs;
    519  mp->m_luaref = args->rhs_lua;
    520  mp->m_keylen = (int)strlen(mp->m_keys);
    521  mp->m_noremap = noremap;
    522  mp->m_nowait = args->nowait;
    523  mp->m_silent = args->silent;
    524  mp->m_mode = mode;
    525  mp->m_simplified = simplified;
    526  mp->m_expr = args->expr;
    527  mp->m_replace_keycodes = args->replace_keycodes;
    528  if (sid != 0) {
    529    mp->m_script_ctx.sc_sid = sid;
    530    mp->m_script_ctx.sc_lnum = lnum;
    531  } else {
    532    mp->m_script_ctx = current_sctx;
    533    mp->m_script_ctx.sc_lnum += SOURCING_LNUM;
    534    nlua_set_sctx(&mp->m_script_ctx);
    535  }
    536  mp->m_desc = args->desc;
    537 
    538  // add the new entry in front of the abbrlist or maphash[] list
    539  if (is_abbr) {
    540    mp->m_next = *abbr_table;
    541    *abbr_table = mp;
    542  } else {
    543    const int n = MAP_HASH(mp->m_mode, (uint8_t)mp->m_keys[0]);
    544    mp->m_next = map_table[n];
    545    map_table[n] = mp;
    546  }
    547  return mp;
    548 }
    549 
    550 /// Sets or removes a mapping or abbreviation in buffer `buf`.
    551 ///
    552 /// @param maptype    @see do_map
    553 /// @param args  Fully parsed and "preprocessed" arguments for the
    554 ///              (un)map/abbrev command. Termcodes should have already been
    555 ///              replaced; whitespace, `<` and `>` signs, etc. in {lhs} and
    556 ///              {rhs} are assumed to be literal components of the mapping.
    557 /// @param mode       @see do_map
    558 /// @param is_abbrev  @see do_map
    559 /// @param buf        Target Buffer
    560 static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev, buf_T *buf)
    561 {
    562  int retval = 0;
    563 
    564  // If <buffer> was given, we'll be searching through the buffer's
    565  // mappings/abbreviations, not the globals.
    566  mapblock_T **map_table = args->buffer ? buf->b_maphash : maphash;
    567  mapblock_T **abbr_table = args->buffer ? &buf->b_first_abbr : &first_abbr;
    568  mapblock_T *mp_result[2] = { NULL, NULL };
    569 
    570  bool unmap_lhs_only = false;
    571  if (maptype == MAPTYPE_UNMAP_LHS) {
    572    unmap_lhs_only = true;
    573    maptype = MAPTYPE_UNMAP;
    574  }
    575 
    576  // For ":noremap" don't remap, otherwise do remap.
    577  int noremap = args->script ? REMAP_SCRIPT
    578                             : maptype == MAPTYPE_NOREMAP ? REMAP_NONE : REMAP_YES;
    579 
    580  const bool has_lhs = (args->lhs[0] != NUL);
    581  const bool has_rhs = args->rhs_lua != LUA_NOREF || (args->rhs[0] != NUL) || args->rhs_is_noop;
    582  const bool do_print = !has_lhs || (maptype != MAPTYPE_UNMAP && !has_rhs);
    583  if (do_print) {
    584    msg_ext_set_kind("list_cmd");
    585  }
    586 
    587  // check for :unmap without argument
    588  if (maptype == MAPTYPE_UNMAP && !has_lhs) {
    589    retval = 1;
    590    goto theend;
    591  }
    592 
    593  const char *lhs = (char *)&args->lhs;
    594  const bool did_simplify = args->alt_lhs_len != 0;
    595 
    596  // The following is done twice if we have two versions of keys
    597  for (int keyround = 1; keyround <= 2; keyround++) {
    598    bool did_it = false;
    599    bool did_local = false;
    600    bool keyround1_simplified = keyround == 1 && did_simplify;
    601    int len = (int)args->lhs_len;
    602 
    603    if (keyround == 2) {
    604      if (!did_simplify) {
    605        break;
    606      }
    607      lhs = (char *)&args->alt_lhs;
    608      len = (int)args->alt_lhs_len;
    609    } else if (did_simplify && do_print) {
    610      // when printing always use the not-simplified map
    611      lhs = (char *)&args->alt_lhs;
    612      len = (int)args->alt_lhs_len;
    613    }
    614 
    615    // check arguments and translate function keys
    616    if (has_lhs) {
    617      if (len > MAXMAPLEN) {
    618        retval = 1;
    619        goto theend;
    620      }
    621 
    622      if (is_abbrev && maptype != MAPTYPE_UNMAP) {
    623        // If an abbreviation ends in a keyword character, the
    624        // rest must be all keyword-char or all non-keyword-char.
    625        // Otherwise we won't be able to find the start of it in a
    626        // vi-compatible way.
    627        int same = -1;
    628 
    629        const int first = vim_iswordp(lhs);
    630        int last = first;
    631        const char *p = lhs + utfc_ptr2len(lhs);
    632        int n = 1;
    633        while (p < lhs + len) {
    634          n++;                                  // nr of (multi-byte) chars
    635          last = vim_iswordp(p);                // type of last char
    636          if (same == -1 && last != first) {
    637            same = n - 1;                       // count of same char type
    638          }
    639          p += utfc_ptr2len(p);
    640        }
    641        if (last && n > 2 && same >= 0 && same < n - 1) {
    642          retval = 1;
    643          goto theend;
    644        }
    645        // An abbreviation cannot contain white space.
    646        for (n = 0; n < len; n++) {
    647          if (ascii_iswhite(lhs[n])) {
    648            retval = 1;
    649            goto theend;
    650          }
    651        }  // for
    652      }
    653    }
    654 
    655    if (has_lhs && has_rhs && is_abbrev) {  // if we will add an abbreviation,
    656      no_abbr = false;  // reset flag that indicates there are no abbreviations
    657    }
    658 
    659    if (do_print) {
    660      msg_start();
    661    }
    662 
    663    // Check if a new local mapping wasn't already defined globally.
    664    if (args->unique && map_table == buf->b_maphash && has_lhs && has_rhs
    665        && maptype != MAPTYPE_UNMAP) {
    666      // need to loop over all global hash lists
    667      for (int hash = 0; hash < 256 && !got_int; hash++) {
    668        mapblock_T *mp;
    669        if (is_abbrev) {
    670          if (hash != 0) {  // there is only one abbreviation list
    671            break;
    672          }
    673          mp = first_abbr;
    674        } else {
    675          mp = maphash[hash];
    676        }
    677        for (; mp != NULL && !got_int; mp = mp->m_next) {
    678          // check entries with the same mode
    679          if ((mp->m_mode & mode) != 0
    680              && mp->m_keylen == len
    681              && strncmp(mp->m_keys, lhs, (size_t)len) == 0) {
    682            retval = 6;
    683            goto theend;
    684          }
    685        }
    686      }
    687    }
    688 
    689    // When listing global mappings, also list buffer-local ones here.
    690    if (map_table != buf->b_maphash && !has_rhs && maptype != MAPTYPE_UNMAP) {
    691      // need to loop over all global hash lists
    692      for (int hash = 0; hash < 256 && !got_int; hash++) {
    693        mapblock_T *mp;
    694        if (is_abbrev) {
    695          if (hash != 0) {  // there is only one abbreviation list
    696            break;
    697          }
    698          mp = buf->b_first_abbr;
    699        } else {
    700          mp = buf->b_maphash[hash];
    701        }
    702        for (; mp != NULL && !got_int; mp = mp->m_next) {
    703          // check entries with the same mode
    704          if (!mp->m_simplified && (mp->m_mode & mode) != 0) {
    705            if (!has_lhs) {  // show all entries
    706              showmap(mp, true);
    707              did_local = true;
    708            } else {
    709              int n = mp->m_keylen;
    710              if (strncmp(mp->m_keys, lhs, (size_t)(n < len ? n : len)) == 0) {
    711                showmap(mp, true);
    712                did_local = true;
    713              }
    714            }
    715          }
    716        }
    717      }
    718    }
    719 
    720    // Find an entry in the maphash[] list that matches.
    721    // For :unmap we may loop two times: once to try to unmap an entry with a
    722    // matching 'from' part, a second time, if the first fails, to unmap an
    723    // entry with a matching 'to' part. This was done to allow ":ab foo bar"
    724    // to be unmapped by typing ":unab foo", where "foo" will be replaced by
    725    // "bar" because of the abbreviation.
    726    const int num_rounds = maptype == MAPTYPE_UNMAP && !unmap_lhs_only ? 2 : 1;
    727    for (int round = 0; round < num_rounds && !did_it && !got_int; round++) {
    728      int hash_start, hash_end;
    729      if ((round == 0 && has_lhs) || is_abbrev) {
    730        // just use one hash
    731        hash_start = is_abbrev ? 0 : MAP_HASH(mode, (uint8_t)lhs[0]);
    732        hash_end = hash_start + 1;
    733      } else {
    734        // need to loop over all hash lists
    735        hash_start = 0;
    736        hash_end = 256;
    737      }
    738      for (int hash = hash_start; hash < hash_end && !got_int; hash++) {
    739        mapblock_T **mpp = is_abbrev ? abbr_table : &(map_table[hash]);
    740        for (mapblock_T *mp = *mpp; mp != NULL && !got_int; mp = *mpp) {
    741          if ((mp->m_mode & mode) == 0) {
    742            // skip entries with wrong mode
    743            mpp = &(mp->m_next);
    744            continue;
    745          }
    746          if (!has_lhs) {                      // show all entries
    747            if (!mp->m_simplified) {
    748              showmap(mp, map_table != maphash);
    749              did_it = true;
    750            }
    751          } else {                          // do we have a match?
    752            int n;
    753            const char *p;
    754            if (round) {              // second round: Try unmap "rhs" string
    755              n = (int)strlen(mp->m_str);
    756              p = mp->m_str;
    757            } else {
    758              n = mp->m_keylen;
    759              p = mp->m_keys;
    760            }
    761            if (strncmp(p, lhs, (size_t)(n < len ? n : len)) == 0) {
    762              if (maptype == MAPTYPE_UNMAP) {
    763                // Delete entry.
    764                // Only accept a full match.  For abbreviations
    765                // we ignore trailing space when matching with
    766                // the "lhs", since an abbreviation can't have
    767                // trailing space.
    768                if (n != len && (!is_abbrev || round || n > len || *skipwhite(lhs + n) != NUL)) {
    769                  mpp = &(mp->m_next);
    770                  continue;
    771                }
    772                // In keyround for simplified keys, don't unmap
    773                // a mapping without m_simplified flag.
    774                if (keyround1_simplified && !mp->m_simplified) {
    775                  break;
    776                }
    777                // We reset the indicated mode bits. If nothing
    778                // is left the entry is deleted below.
    779                mp->m_mode &= ~mode;
    780                did_it = true;  // remember we did something
    781              } else if (!has_rhs) {  // show matching entry
    782                if (!mp->m_simplified) {
    783                  showmap(mp, map_table != maphash);
    784                  did_it = true;
    785                }
    786              } else if (n != len) {  // new entry is ambiguous
    787                mpp = &(mp->m_next);
    788                continue;
    789              } else if (keyround1_simplified && !mp->m_simplified) {
    790                // In keyround for simplified keys, don't replace
    791                // a mapping without m_simplified flag.
    792                did_it = true;
    793                break;
    794              } else if (args->unique) {
    795                retval = 5;
    796                goto theend;
    797              } else {
    798                // new rhs for existing entry
    799                mp->m_mode &= ~mode;  // remove mode bits
    800                if (mp->m_mode == 0 && !did_it) {  // reuse entry
    801                  if (mp->m_alt != NULL) {
    802                    mp->m_alt = mp->m_alt->m_alt = NULL;
    803                  } else {
    804                    NLUA_CLEAR_REF(mp->m_luaref);
    805                    xfree(mp->m_str);
    806                    xfree(mp->m_orig_str);
    807                    xfree(mp->m_desc);
    808                  }
    809                  mp->m_str = args->rhs;
    810                  mp->m_orig_str = args->orig_rhs;
    811                  mp->m_luaref = args->rhs_lua;
    812                  mp->m_noremap = noremap;
    813                  mp->m_nowait = args->nowait;
    814                  mp->m_silent = args->silent;
    815                  mp->m_mode = mode;
    816                  mp->m_simplified = keyround1_simplified;
    817                  mp->m_expr = args->expr;
    818                  mp->m_replace_keycodes = args->replace_keycodes;
    819                  mp->m_script_ctx = current_sctx;
    820                  mp->m_script_ctx.sc_lnum += SOURCING_LNUM;
    821                  nlua_set_sctx(&mp->m_script_ctx);
    822                  mp->m_desc = args->desc;
    823                  mp_result[keyround - 1] = mp;
    824                  did_it = true;
    825                }
    826              }
    827              if (mp->m_mode == 0) {  // entry can be deleted
    828                mapblock_free(mpp);
    829                continue;  // continue with *mpp
    830              }
    831 
    832              // May need to put this entry into another hash list.
    833              int new_hash = MAP_HASH(mp->m_mode, (uint8_t)mp->m_keys[0]);
    834              if (!is_abbrev && new_hash != hash) {
    835                *mpp = mp->m_next;
    836                mp->m_next = map_table[new_hash];
    837                map_table[new_hash] = mp;
    838 
    839                continue;  // continue with *mpp
    840              }
    841            }
    842          }
    843          mpp = &(mp->m_next);
    844        }
    845      }
    846    }
    847 
    848    if (maptype == MAPTYPE_UNMAP) {
    849      // delete entry
    850      if (!did_it) {
    851        if (!keyround1_simplified) {
    852          retval = 2;  // no match
    853        }
    854      } else if (*lhs == Ctrl_C) {
    855        // If CTRL-C has been unmapped, reuse it for Interrupting.
    856        if (map_table == buf->b_maphash) {
    857          buf->b_mapped_ctrl_c &= ~mode;
    858        } else {
    859          mapped_ctrl_c &= ~mode;
    860        }
    861      }
    862      continue;
    863    }
    864 
    865    if (!has_lhs || !has_rhs) {
    866      // print entries
    867      if (!did_it && !did_local) {
    868        if (is_abbrev) {
    869          msg(_("No abbreviation found"), 0);
    870        } else {
    871          msg(_("No mapping found"), 0);
    872        }
    873      }
    874      goto theend;  // listing finished
    875    }
    876 
    877    if (did_it) {
    878      continue;  // have added the new entry already
    879    }
    880 
    881    // Get here when adding a new entry to the maphash[] list or abbrlist.
    882    mp_result[keyround - 1] = map_add(buf, map_table, abbr_table, lhs,
    883                                      args, noremap, mode, is_abbrev,
    884                                      0,  // sid
    885                                      0,  // lnum
    886                                      keyround1_simplified);
    887  }
    888 
    889  if (mp_result[0] != NULL && mp_result[1] != NULL) {
    890    mp_result[0]->m_alt = mp_result[1];
    891    mp_result[1]->m_alt = mp_result[0];
    892  }
    893 
    894 theend:
    895  if (mp_result[0] != NULL || mp_result[1] != NULL) {
    896    args->rhs = NULL;
    897    args->orig_rhs = NULL;
    898    args->rhs_lua = LUA_NOREF;
    899    args->desc = NULL;
    900  }
    901  return retval;
    902 }
    903 
    904 /// Set or remove a mapping or an abbreviation in the current buffer, OR
    905 /// display (matching) mappings/abbreviations.
    906 ///
    907 /// ```vim
    908 /// map[!]                          " show all key mappings
    909 /// map[!] {lhs}                    " show key mapping for {lhs}
    910 /// map[!] {lhs} {rhs}              " set key mapping for {lhs} to {rhs}
    911 /// noremap[!] {lhs} {rhs}          " same, but no remapping for {rhs}
    912 /// unmap[!] {lhs}                  " remove key mapping for {lhs}
    913 /// abbr                            " show all abbreviations
    914 /// abbr {lhs}                      " show abbreviations for {lhs}
    915 /// abbr {lhs} {rhs}                " set abbreviation for {lhs} to {rhs}
    916 /// noreabbr {lhs} {rhs}            " same, but no remapping for {rhs}
    917 /// unabbr {lhs}                    " remove abbreviation for {lhs}
    918 ///
    919 /// for :map   mode is MODE_NORMAL | MODE_VISUAL | MODE_SELECT | MODE_OP_PENDING
    920 /// for :map!  mode is MODE_INSERT | MODE_CMDLINE
    921 /// for :cmap  mode is MODE_CMDLINE
    922 /// for :imap  mode is MODE_INSERT
    923 /// for :lmap  mode is MODE_LANGMAP
    924 /// for :nmap  mode is MODE_NORMAL
    925 /// for :vmap  mode is MODE_VISUAL | MODE_SELECT
    926 /// for :xmap  mode is MODE_VISUAL
    927 /// for :smap  mode is MODE_SELECT
    928 /// for :omap  mode is MODE_OP_PENDING
    929 /// for :tmap  mode is MODE_TERMINAL
    930 ///
    931 /// for :abbr  mode is MODE_INSERT | MODE_CMDLINE
    932 /// for :iabbr mode is MODE_INSERT
    933 /// for :cabbr mode is MODE_CMDLINE
    934 /// ```
    935 ///
    936 /// @param maptype  MAPTYPE_MAP for |:map| or |:abbr|
    937 ///                 MAPTYPE_UNMAP for |:unmap| or |:unabbr|
    938 ///                 MAPTYPE_NOREMAP for |:noremap| or |:noreabbr|
    939 ///                 MAPTYPE_UNMAP_LHS is like MAPTYPE_UNMAP, but doesn't try to match
    940 ///                 with {rhs} if there is no match with {lhs}.
    941 /// @param arg      C-string containing the arguments of the map/abbrev
    942 ///                 command, i.e. everything except the initial `:[X][nore]map`.
    943 ///                 - Cannot be a read-only string; it will be modified.
    944 /// @param mode   Bitflags representing the mode in which to set the mapping.
    945 ///               See @ref get_map_mode.
    946 /// @param is_abbrev  True if setting an abbreviation, false otherwise.
    947 ///
    948 /// @return 0 on success. On failure, will return one of the following:
    949 ///         - 1 for invalid arguments
    950 ///         - 2 for no match
    951 ///         - 4 for out of mem (deprecated, WON'T HAPPEN)
    952 ///         - 5 for entry not unique
    953 ///         - 6 for buflocal unique entry conflicts with global entry
    954 ///
    955 int do_map(int maptype, char *arg, int mode, bool is_abbrev)
    956 {
    957  MapArguments parsed_args;
    958  int result = str_to_mapargs(arg, maptype == MAPTYPE_UNMAP, &parsed_args);
    959  switch (result) {
    960  case 0:
    961    break;
    962  case 1:
    963    // invalid arguments
    964    goto free_and_return;
    965  default:
    966    assert(false && "Unknown return code from str_to_mapargs!");
    967    result = -1;
    968    goto free_and_return;
    969  }  // switch
    970 
    971  result = buf_do_map(maptype, &parsed_args, mode, is_abbrev, curbuf);
    972 
    973 free_and_return:
    974  xfree(parsed_args.rhs);
    975  xfree(parsed_args.orig_rhs);
    976  return result;
    977 }
    978 
    979 /// Get the mapping mode from the command name.
    980 static int get_map_mode(char **cmdp, bool forceit)
    981 {
    982  int mode;
    983 
    984  char *p = *cmdp;
    985  int modec = (uint8_t)(*p++);
    986  if (modec == 'i') {
    987    mode = MODE_INSERT;                                                  // :imap
    988  } else if (modec == 'l') {
    989    mode = MODE_LANGMAP;                                                 // :lmap
    990  } else if (modec == 'c') {
    991    mode = MODE_CMDLINE;                                                 // :cmap
    992  } else if (modec == 'n' && *p != 'o') {  // avoid :noremap
    993    mode = MODE_NORMAL;                                                  // :nmap
    994  } else if (modec == 'v') {
    995    mode = MODE_VISUAL | MODE_SELECT;                                    // :vmap
    996  } else if (modec == 'x') {
    997    mode = MODE_VISUAL;                                                  // :xmap
    998  } else if (modec == 's') {
    999    mode = MODE_SELECT;                                                  // :smap
   1000  } else if (modec == 'o') {
   1001    mode = MODE_OP_PENDING;                                              // :omap
   1002  } else if (modec == 't') {
   1003    mode = MODE_TERMINAL;                                                // :tmap
   1004  } else {
   1005    p--;
   1006    if (forceit) {
   1007      mode = MODE_INSERT | MODE_CMDLINE;                                 // :map !
   1008    } else {
   1009      mode = MODE_VISUAL | MODE_SELECT | MODE_NORMAL | MODE_OP_PENDING;  // :map
   1010    }
   1011  }
   1012 
   1013  *cmdp = p;
   1014  return mode;
   1015 }
   1016 
   1017 /// Clear all mappings (":mapclear") or abbreviations (":abclear").
   1018 /// "abbr" should be false for mappings, true for abbreviations.
   1019 /// This function used to be called map_clear().
   1020 static void do_mapclear(char *cmdp, char *arg, int forceit, int abbr)
   1021 {
   1022  bool local = strcmp(arg, "<buffer>") == 0;
   1023  if (!local && *arg != NUL) {
   1024    emsg(_(e_invarg));
   1025    return;
   1026  }
   1027 
   1028  int mode = get_map_mode(&cmdp, forceit);
   1029  map_clear_mode(curbuf, mode, local, abbr);
   1030 }
   1031 
   1032 /// Clear all mappings in "mode".
   1033 ///
   1034 /// @param buf,  buffer for local mappings
   1035 /// @param mode  mode in which to delete
   1036 /// @param local  true for buffer-local mappings
   1037 /// @param abbr  true for abbreviations
   1038 void map_clear_mode(buf_T *buf, int mode, bool local, bool abbr)
   1039 {
   1040  for (int hash = 0; hash < 256; hash++) {
   1041    mapblock_T **mpp;
   1042    if (abbr) {
   1043      if (hash > 0) {           // there is only one abbrlist
   1044        break;
   1045      }
   1046      if (local) {
   1047        mpp = &buf->b_first_abbr;
   1048      } else {
   1049        mpp = &first_abbr;
   1050      }
   1051    } else {
   1052      if (local) {
   1053        mpp = &buf->b_maphash[hash];
   1054      } else {
   1055        mpp = &maphash[hash];
   1056      }
   1057    }
   1058    while (*mpp != NULL) {
   1059      mapblock_T *mp = *mpp;
   1060      if (mp->m_mode & mode) {
   1061        mp->m_mode &= ~mode;
   1062        if (mp->m_mode == 0) {       // entry can be deleted
   1063          mapblock_free(mpp);
   1064          continue;
   1065        }
   1066        // May need to put this entry into another hash list.
   1067        int new_hash = MAP_HASH(mp->m_mode, (uint8_t)mp->m_keys[0]);
   1068        if (!abbr && new_hash != hash) {
   1069          *mpp = mp->m_next;
   1070          if (local) {
   1071            mp->m_next = buf->b_maphash[new_hash];
   1072            buf->b_maphash[new_hash] = mp;
   1073          } else {
   1074            mp->m_next = maphash[new_hash];
   1075            maphash[new_hash] = mp;
   1076          }
   1077          continue;                     // continue with *mpp
   1078        }
   1079      }
   1080      mpp = &(mp->m_next);
   1081    }
   1082  }
   1083 }
   1084 
   1085 /// Check if a map exists that has given string in the rhs
   1086 ///
   1087 /// Also checks mappings local to the current buffer.
   1088 ///
   1089 /// @param[in]  str  String which mapping must have in the rhs. Termcap codes
   1090 ///                  are recognized in this argument.
   1091 /// @param[in]  modechars  Mode(s) in which mappings are checked.
   1092 /// @param[in]  abbr  true if checking abbreviations in place of mappings.
   1093 ///
   1094 /// @return true if there is at least one mapping with given parameters.
   1095 bool map_to_exists(const char *const str, const char *const modechars, const bool abbr)
   1096  FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE
   1097 {
   1098  int mode = 0;
   1099 
   1100  char *buf = NULL;
   1101  const char *const rhs = replace_termcodes(str, strlen(str), &buf, 0,
   1102                                            REPTERM_DO_LT, NULL, p_cpo);
   1103 
   1104 #define MAPMODE(mode, modechars, chr, modeflags) \
   1105  do { \
   1106    if (strchr(modechars, chr) != NULL) { \
   1107      (mode) |= (modeflags); \
   1108    } \
   1109  } while (0)
   1110  MAPMODE(mode, modechars, 'n', MODE_NORMAL);
   1111  MAPMODE(mode, modechars, 'v', MODE_VISUAL | MODE_SELECT);
   1112  MAPMODE(mode, modechars, 'x', MODE_VISUAL);
   1113  MAPMODE(mode, modechars, 's', MODE_SELECT);
   1114  MAPMODE(mode, modechars, 'o', MODE_OP_PENDING);
   1115  MAPMODE(mode, modechars, 'i', MODE_INSERT);
   1116  MAPMODE(mode, modechars, 'l', MODE_LANGMAP);
   1117  MAPMODE(mode, modechars, 'c', MODE_CMDLINE);
   1118 #undef MAPMODE
   1119 
   1120  bool retval = map_to_exists_mode(rhs, mode, abbr);
   1121  xfree(buf);
   1122 
   1123  return retval;
   1124 }
   1125 
   1126 /// Check if a map exists that has given string in the rhs
   1127 ///
   1128 /// Also checks mappings local to the current buffer.
   1129 ///
   1130 /// @param[in]  rhs  String which mapping must have in the rhs. Termcap codes
   1131 ///                  are recognized in this argument.
   1132 /// @param[in]  mode  Mode(s) in which mappings are checked.
   1133 /// @param[in]  abbr  true if checking abbreviations in place of mappings.
   1134 ///
   1135 /// @return true if there is at least one mapping with given parameters.
   1136 bool map_to_exists_mode(const char *const rhs, const int mode, const bool abbr)
   1137 {
   1138  bool exp_buffer = false;
   1139 
   1140  // Do it twice: once for global maps and once for local maps.
   1141  while (true) {
   1142    for (int hash = 0; hash < 256; hash++) {
   1143      mapblock_T *mp;
   1144      if (abbr) {
   1145        if (hash > 0) {  // There is only one abbr list.
   1146          break;
   1147        }
   1148        if (exp_buffer) {
   1149          mp = curbuf->b_first_abbr;
   1150        } else {
   1151          mp = first_abbr;
   1152        }
   1153      } else if (exp_buffer) {
   1154        mp = curbuf->b_maphash[hash];
   1155      } else {
   1156        mp = maphash[hash];
   1157      }
   1158      for (; mp; mp = mp->m_next) {
   1159        if ((mp->m_mode & mode) && strstr(mp->m_str, rhs) != NULL) {
   1160          return true;
   1161        }
   1162      }
   1163    }
   1164    if (exp_buffer) {
   1165      break;
   1166    }
   1167    exp_buffer = true;
   1168  }
   1169 
   1170  return false;
   1171 }
   1172 
   1173 /// Used below when expanding mapping/abbreviation names.
   1174 static int expand_mapmodes = 0;
   1175 static bool expand_isabbrev = false;
   1176 static bool expand_buffer = false;
   1177 
   1178 /// Translate an internal mapping/abbreviation representation into the
   1179 /// corresponding external one recognized by :map/:abbrev commands.
   1180 ///
   1181 /// This function is called when expanding mappings/abbreviations on the
   1182 /// command-line.
   1183 ///
   1184 /// It uses a growarray to build the translation string since the latter can be
   1185 /// wider than the original description. The caller has to free the string
   1186 /// afterwards.
   1187 ///
   1188 /// @param[in] cpo_val  See param docs for @ref replace_termcodes.
   1189 ///
   1190 /// @return  NULL when there is a problem.
   1191 static char *translate_mapping(const char *const str_in, const char *const cpo_val)
   1192 {
   1193  const uint8_t *str = (const uint8_t *)str_in;
   1194  garray_T ga;
   1195  ga_init(&ga, 1, 40);
   1196 
   1197  const bool cpo_bslash = (vim_strchr(cpo_val, CPO_BSLASH) != NULL);
   1198 
   1199  for (; *str; str++) {
   1200    int c = *str;
   1201    if (c == K_SPECIAL && str[1] != NUL && str[2] != NUL) {
   1202      int modifiers = 0;
   1203      if (str[1] == KS_MODIFIER) {
   1204        str++;
   1205        modifiers = *++str;
   1206        c = *++str;
   1207      }
   1208 
   1209      if (c == K_SPECIAL && str[1] != NUL && str[2] != NUL) {
   1210        c = TO_SPECIAL(str[1], str[2]);
   1211        if (c == K_ZERO) {
   1212          // display <Nul> as ^@
   1213          c = NUL;
   1214        }
   1215        str += 2;
   1216      }
   1217      if (IS_SPECIAL(c) || modifiers) {         // special key
   1218        ga_concat(&ga, get_special_key_name(c, modifiers));
   1219        continue;         // for (str)
   1220      }
   1221    }
   1222 
   1223    if (c == ' ' || c == '\t' || c == Ctrl_J || c == Ctrl_V
   1224        || c == '<' || (c == '\\' && !cpo_bslash)) {
   1225      ga_append(&ga, cpo_bslash ? Ctrl_V : '\\');
   1226    }
   1227 
   1228    if (c) {
   1229      ga_append(&ga, (uint8_t)c);
   1230    }
   1231  }
   1232  ga_append(&ga, NUL);
   1233  return (char *)ga.ga_data;
   1234 }
   1235 
   1236 /// Work out what to complete when doing command line completion of mapping
   1237 /// or abbreviation names.
   1238 ///
   1239 /// @param forceit  true if '!' given
   1240 /// @param isabbrev  true if abbreviation
   1241 /// @param isunmap  true if unmap/unabbrev command
   1242 char *set_context_in_map_cmd(expand_T *xp, char *cmd, char *arg, bool forceit, bool isabbrev,
   1243                             bool isunmap, cmdidx_T cmdidx)
   1244 {
   1245  if (forceit && cmdidx != CMD_map && cmdidx != CMD_unmap) {
   1246    xp->xp_context = EXPAND_NOTHING;
   1247  } else {
   1248    if (isunmap) {
   1249      expand_mapmodes = get_map_mode(&cmd, forceit || isabbrev);
   1250    } else {
   1251      expand_mapmodes = MODE_INSERT | MODE_CMDLINE;
   1252      if (!isabbrev) {
   1253        expand_mapmodes |= MODE_VISUAL | MODE_SELECT | MODE_NORMAL | MODE_OP_PENDING;
   1254      }
   1255    }
   1256    expand_isabbrev = isabbrev;
   1257    xp->xp_context = EXPAND_MAPPINGS;
   1258    expand_buffer = false;
   1259    while (true) {
   1260      if (strncmp(arg, "<buffer>", 8) == 0) {
   1261        expand_buffer = true;
   1262        arg = skipwhite(arg + 8);
   1263        continue;
   1264      }
   1265      if (strncmp(arg, "<unique>", 8) == 0) {
   1266        arg = skipwhite(arg + 8);
   1267        continue;
   1268      }
   1269      if (strncmp(arg, "<nowait>", 8) == 0) {
   1270        arg = skipwhite(arg + 8);
   1271        continue;
   1272      }
   1273      if (strncmp(arg, "<silent>", 8) == 0) {
   1274        arg = skipwhite(arg + 8);
   1275        continue;
   1276      }
   1277      if (strncmp(arg, "<special>", 9) == 0) {
   1278        arg = skipwhite(arg + 9);
   1279        continue;
   1280      }
   1281      if (strncmp(arg, "<script>", 8) == 0) {
   1282        arg = skipwhite(arg + 8);
   1283        continue;
   1284      }
   1285      if (strncmp(arg, "<expr>", 6) == 0) {
   1286        arg = skipwhite(arg + 6);
   1287        continue;
   1288      }
   1289      break;
   1290    }
   1291    xp->xp_pattern = arg;
   1292  }
   1293 
   1294  return NULL;
   1295 }
   1296 
   1297 /// Find all mapping/abbreviation names that match regexp "regmatch".
   1298 /// For command line expansion of ":[un]map" and ":[un]abbrev" in all modes.
   1299 /// @return OK if matches found, FAIL otherwise.
   1300 int ExpandMappings(char *pat, regmatch_T *regmatch, int *numMatches, char ***matches)
   1301 {
   1302  const bool fuzzy = cmdline_fuzzy_complete(pat);
   1303 
   1304  *numMatches = 0;                    // return values in case of FAIL
   1305  *matches = NULL;
   1306 
   1307  garray_T ga;
   1308  if (!fuzzy) {
   1309    ga_init(&ga, sizeof(char *), 3);
   1310  } else {
   1311    ga_init(&ga, sizeof(fuzmatch_str_T), 3);
   1312  }
   1313 
   1314  // First search in map modifier arguments
   1315  for (int i = 0; i < 7; i++) {
   1316    char *p;
   1317    if (i == 0) {
   1318      p = "<silent>";
   1319    } else if (i == 1) {
   1320      p = "<unique>";
   1321    } else if (i == 2) {
   1322      p = "<script>";
   1323    } else if (i == 3) {
   1324      p = "<expr>";
   1325    } else if (i == 4 && !expand_buffer) {
   1326      p = "<buffer>";
   1327    } else if (i == 5) {
   1328      p = "<nowait>";
   1329    } else if (i == 6) {
   1330      p = "<special>";
   1331    } else {
   1332      continue;
   1333    }
   1334 
   1335    bool match;
   1336    int score = 0;
   1337    if (!fuzzy) {
   1338      match = vim_regexec(regmatch, p, 0);
   1339    } else {
   1340      score = fuzzy_match_str(p, pat);
   1341      match = (score != FUZZY_SCORE_NONE);
   1342    }
   1343 
   1344    if (!match) {
   1345      continue;
   1346    }
   1347 
   1348    if (fuzzy) {
   1349      GA_APPEND(fuzmatch_str_T, &ga, ((fuzmatch_str_T){
   1350        .idx = ga.ga_len,
   1351        .str = xstrdup(p),
   1352        .score = score,
   1353      }));
   1354    } else {
   1355      GA_APPEND(char *, &ga, xstrdup(p));
   1356    }
   1357  }
   1358 
   1359  for (int hash = 0; hash < 256; hash++) {
   1360    mapblock_T *mp;
   1361    if (expand_isabbrev) {
   1362      if (hash > 0) {    // only one abbrev list
   1363        break;  // for (hash)
   1364      }
   1365      mp = first_abbr;
   1366    } else if (expand_buffer) {
   1367      mp = curbuf->b_maphash[hash];
   1368    } else {
   1369      mp = maphash[hash];
   1370    }
   1371    for (; mp; mp = mp->m_next) {
   1372      if (mp->m_simplified || !(mp->m_mode & expand_mapmodes)) {
   1373        continue;
   1374      }
   1375 
   1376      char *p = translate_mapping(mp->m_keys, p_cpo);
   1377      if (p == NULL) {
   1378        continue;
   1379      }
   1380 
   1381      bool match;
   1382      int score = 0;
   1383      if (!fuzzy) {
   1384        match = vim_regexec(regmatch, p, 0);
   1385      } else {
   1386        score = fuzzy_match_str(p, pat);
   1387        match = (score != FUZZY_SCORE_NONE);
   1388      }
   1389 
   1390      if (!match) {
   1391        xfree(p);
   1392        continue;
   1393      }
   1394 
   1395      if (fuzzy) {
   1396        GA_APPEND(fuzmatch_str_T, &ga, ((fuzmatch_str_T){
   1397          .idx = ga.ga_len,
   1398          .str = p,
   1399          .score = score,
   1400        }));
   1401      } else {
   1402        GA_APPEND(char *, &ga, p);
   1403      }
   1404    }  // for (mp)
   1405  }  // for (hash)
   1406 
   1407  if (ga.ga_len == 0) {
   1408    return FAIL;
   1409  }
   1410 
   1411  if (!fuzzy) {
   1412    *matches = ga.ga_data;
   1413    *numMatches = ga.ga_len;
   1414  } else {
   1415    fuzzymatches_to_strmatches(ga.ga_data, matches, ga.ga_len, false);
   1416    *numMatches = ga.ga_len;
   1417  }
   1418 
   1419  int count = *numMatches;
   1420  if (count > 1) {
   1421    // Sort the matches
   1422    // Fuzzy matching already sorts the matches
   1423    if (!fuzzy) {
   1424      sort_strings(*matches, count);
   1425    }
   1426 
   1427    // Remove multiple entries
   1428    char **ptr1 = *matches;
   1429    char **ptr2 = ptr1 + 1;
   1430    char **ptr3 = ptr1 + count;
   1431 
   1432    while (ptr2 < ptr3) {
   1433      if (strcmp(*ptr1, *ptr2) != 0) {
   1434        *++ptr1 = *ptr2++;
   1435      } else {
   1436        xfree(*ptr2++);
   1437        count--;
   1438      }
   1439    }
   1440  }
   1441 
   1442  *numMatches = count;
   1443  return count == 0 ? FAIL : OK;
   1444 }
   1445 
   1446 // Check for an abbreviation.
   1447 // Cursor is at ptr[col].
   1448 // When inserting, mincol is where insert started.
   1449 // For the command line, mincol is what is to be skipped over.
   1450 // "c" is the character typed before check_abbr was called.  It may have
   1451 // ABBR_OFF added to avoid prepending a CTRL-V to it.
   1452 //
   1453 // Historic vi practice: The last character of an abbreviation must be an id
   1454 // character ([a-zA-Z0-9_]). The characters in front of it must be all id
   1455 // characters or all non-id characters. This allows for abbr. "#i" to
   1456 // "#include".
   1457 //
   1458 // Vim addition: Allow for abbreviations that end in a non-keyword character.
   1459 // Then there must be white space before the abbr.
   1460 //
   1461 // Return true if there is an abbreviation, false if not.
   1462 bool check_abbr(int c, char *ptr, int col, int mincol)
   1463 {
   1464  uint8_t tb[MB_MAXBYTES + 4];
   1465  int clen = 0;                 // length in characters
   1466 
   1467  if (typebuf.tb_no_abbr_cnt) {  // abbrev. are not recursive
   1468    return false;
   1469  }
   1470 
   1471  // no remapping implies no abbreviation, except for CTRL-]
   1472  if (noremap_keys() && c != Ctrl_RSB) {
   1473    return false;
   1474  }
   1475 
   1476  // Check for word before the cursor: If it ends in a keyword char all
   1477  // chars before it must be keyword chars or non-keyword chars, but not
   1478  // white space. If it ends in a non-keyword char we accept any characters
   1479  // before it except white space.
   1480  if (col == 0) {  // cannot be an abbr.
   1481    return false;
   1482  }
   1483 
   1484  int scol;  // starting column of the abbr.
   1485 
   1486  {
   1487    bool is_id = true;
   1488    bool vim_abbr;
   1489    char *p = mb_prevptr(ptr, ptr + col);
   1490    if (!vim_iswordp(p)) {
   1491      vim_abbr = true;    // Vim added abbr.
   1492    } else {
   1493      vim_abbr = false;   // vi compatible abbr.
   1494      if (p > ptr) {
   1495        is_id = vim_iswordp(mb_prevptr(ptr, p));
   1496      }
   1497    }
   1498    clen = 1;
   1499    while (p > ptr + mincol) {
   1500      p = mb_prevptr(ptr, p);
   1501      if (ascii_isspace(*p) || (!vim_abbr && is_id != vim_iswordp(p))) {
   1502        p += utfc_ptr2len(p);
   1503        break;
   1504      }
   1505      clen++;
   1506    }
   1507    scol = (int)(p - ptr);
   1508  }
   1509 
   1510  if (scol < mincol) {
   1511    scol = mincol;
   1512  }
   1513  if (scol < col) {             // there is a word in front of the cursor
   1514    ptr += scol;
   1515    int len = col - scol;
   1516    mapblock_T *mp = curbuf->b_first_abbr;
   1517    mapblock_T *mp2 = first_abbr;
   1518    if (mp == NULL) {
   1519      mp = mp2;
   1520      mp2 = NULL;
   1521    }
   1522    for (; mp;
   1523         mp->m_next == NULL ? (mp = mp2, mp2 = NULL)
   1524                            : (mp = mp->m_next)) {
   1525      int qlen = mp->m_keylen;
   1526      char *q = mp->m_keys;
   1527 
   1528      if (strchr(mp->m_keys, K_SPECIAL) != NULL) {
   1529        // Might have K_SPECIAL escaped mp->m_keys.
   1530        q = xstrdup(mp->m_keys);
   1531        vim_unescape_ks(q);
   1532        qlen = (int)strlen(q);
   1533      }
   1534      // find entries with right mode and keys
   1535      int match = (mp->m_mode & State)
   1536                  && qlen == len
   1537                  && !strncmp(q, ptr, (size_t)len);
   1538      if (q != mp->m_keys) {
   1539        xfree(q);
   1540      }
   1541      if (match) {
   1542        break;
   1543      }
   1544    }
   1545    if (mp != NULL) {
   1546      // Found a match:
   1547      // Insert the rest of the abbreviation in typebuf.tb_buf[].
   1548      // This goes from end to start.
   1549      //
   1550      // Characters 0x000 - 0x100: normal chars, may need CTRL-V,
   1551      // except K_SPECIAL: Becomes K_SPECIAL KS_SPECIAL KE_FILLER
   1552      // Characters where IS_SPECIAL() == true: key codes, need
   1553      // K_SPECIAL. Other characters (with ABBR_OFF): don't use CTRL-V.
   1554      //
   1555      // Character CTRL-] is treated specially - it completes the
   1556      // abbreviation, but is not inserted into the input stream.
   1557      int j = 0;
   1558      if (c != Ctrl_RSB) {
   1559        // special key code, split up
   1560        if (IS_SPECIAL(c) || c == K_SPECIAL) {
   1561          tb[j++] = K_SPECIAL;
   1562          tb[j++] = (uint8_t)K_SECOND(c);
   1563          tb[j++] = (uint8_t)K_THIRD(c);
   1564        } else {
   1565          if (c < ABBR_OFF && (c < ' ' || c > '~')) {
   1566            tb[j++] = Ctrl_V;                   // special char needs CTRL-V
   1567          }
   1568          // if ABBR_OFF has been added, remove it here.
   1569          if (c >= ABBR_OFF) {
   1570            c -= ABBR_OFF;
   1571          }
   1572          int newlen = utf_char2bytes(c, (char *)tb + j);
   1573          tb[j + newlen] = NUL;
   1574          // Need to escape K_SPECIAL.
   1575          char *escaped = vim_strsave_escape_ks((char *)tb + j);
   1576          if (escaped != NULL) {
   1577            newlen = (int)strlen(escaped);
   1578            memmove(tb + j, escaped, (size_t)newlen);
   1579            j += newlen;
   1580            xfree(escaped);
   1581          }
   1582        }
   1583        tb[j] = NUL;
   1584        // insert the last typed char
   1585        ins_typebuf((char *)tb, 1, 0, true, mp->m_silent);
   1586      }
   1587 
   1588      // copy values here, calling eval_map_expr() may make "mp" invalid!
   1589      const int noremap = mp->m_noremap;
   1590      const bool silent = mp->m_silent;
   1591      const bool expr = mp->m_expr;
   1592 
   1593      char *s;
   1594      if (expr) {
   1595        s = eval_map_expr(mp, c);
   1596      } else {
   1597        s = mp->m_str;
   1598      }
   1599      if (s != NULL) {
   1600        // insert the to string
   1601        ins_typebuf(s, noremap, 0, true, silent);
   1602        // no abbrev. for these chars
   1603        typebuf.tb_no_abbr_cnt += (int)strlen(s) + j + 1;
   1604        if (expr) {
   1605          xfree(s);
   1606        }
   1607      }
   1608 
   1609      tb[0] = Ctrl_H;
   1610      tb[1] = NUL;
   1611      len = clen;  // Delete characters instead of bytes
   1612      while (len-- > 0) {  // delete the from string
   1613        ins_typebuf((char *)tb, 1, 0, true, silent);
   1614      }
   1615      return true;
   1616    }
   1617  }
   1618  return false;
   1619 }
   1620 
   1621 /// Evaluate the RHS of a mapping or abbreviations and take care of escaping
   1622 /// special characters.
   1623 /// Careful: after this "mp" will be invalid if the mapping was deleted.
   1624 ///
   1625 /// @param c  NUL or typed character for abbreviation
   1626 char *eval_map_expr(mapblock_T *mp, int c)
   1627 {
   1628  char *p = NULL;
   1629  char *expr = NULL;
   1630 
   1631  // Remove escaping of K_SPECIAL, because "str" is in a format to be used as
   1632  // typeahead.
   1633  if (mp->m_luaref == LUA_NOREF) {
   1634    expr = xstrdup(mp->m_str);
   1635    vim_unescape_ks(expr);
   1636  }
   1637 
   1638  const bool replace_keycodes = mp->m_replace_keycodes;
   1639 
   1640  // Forbid changing text or using ":normal" to avoid most of the bad side
   1641  // effects.  Also restore the cursor position.
   1642  expr_map_lock++;
   1643  set_vim_var_char(c);    // set v:char to the typed character
   1644  const pos_T save_cursor = curwin->w_cursor;
   1645  const int save_msg_col = msg_col;
   1646  const int save_msg_row = msg_row;
   1647  if (mp->m_luaref != LUA_NOREF) {
   1648    Error err = ERROR_INIT;
   1649    Array args = ARRAY_DICT_INIT;
   1650    Object ret = nlua_call_ref(mp->m_luaref, NULL, args, kRetObject, NULL, &err);
   1651    if (ret.type == kObjectTypeString) {
   1652      p = string_to_cstr(ret.data.string);
   1653    }
   1654    api_free_object(ret);
   1655    if (ERROR_SET(&err)) {
   1656      semsg_multiline("emsg", "E5108: %s", err.msg);
   1657      api_clear_error(&err);
   1658    }
   1659  } else {
   1660    p = eval_to_string(expr, false, false);
   1661    xfree(expr);
   1662  }
   1663  expr_map_lock--;
   1664  curwin->w_cursor = save_cursor;
   1665  msg_col = save_msg_col;
   1666  msg_row = save_msg_row;
   1667 
   1668  if (p == NULL) {
   1669    return NULL;
   1670  }
   1671 
   1672  char *res = NULL;
   1673 
   1674  if (replace_keycodes) {
   1675    replace_termcodes(p, strlen(p), &res, 0, REPTERM_DO_LT, NULL, p_cpo);
   1676  } else {
   1677    // Escape K_SPECIAL in the result to be able to use the string as typeahead.
   1678    res = vim_strsave_escape_ks(p);
   1679  }
   1680  xfree(p);
   1681 
   1682  return res;
   1683 }
   1684 
   1685 /// Write map commands for the current mappings to an .exrc file.
   1686 /// Return FAIL on error, OK otherwise.
   1687 ///
   1688 /// @param buf  buffer for local mappings or NULL
   1689 int makemap(FILE *fd, buf_T *buf)
   1690 {
   1691  bool did_cpo = false;
   1692 
   1693  // Do the loop twice: Once for mappings, once for abbreviations.
   1694  // Then loop over all map hash lists.
   1695  for (int abbr = 0; abbr < 2; abbr++) {
   1696    for (int hash = 0; hash < 256; hash++) {
   1697      mapblock_T *mp;
   1698      if (abbr) {
   1699        if (hash > 0) {                 // there is only one abbr list
   1700          break;
   1701        }
   1702        if (buf != NULL) {
   1703          mp = buf->b_first_abbr;
   1704        } else {
   1705          mp = first_abbr;
   1706        }
   1707      } else {
   1708        if (buf != NULL) {
   1709          mp = buf->b_maphash[hash];
   1710        } else {
   1711          mp = maphash[hash];
   1712        }
   1713      }
   1714 
   1715      for (; mp; mp = mp->m_next) {
   1716        // skip script-local mappings
   1717        if (mp->m_noremap == REMAP_SCRIPT) {
   1718          continue;
   1719        }
   1720 
   1721        // skip Lua mappings and mappings that contain a <SNR> (script-local thing),
   1722        // they probably don't work when loaded again
   1723        if (mp->m_luaref != LUA_NOREF) {
   1724          continue;
   1725        }
   1726        char *p;
   1727        for (p = mp->m_str; *p != NUL; p++) {
   1728          if ((uint8_t)p[0] == K_SPECIAL && (uint8_t)p[1] == KS_EXTRA
   1729              && p[2] == KE_SNR) {
   1730            break;
   1731          }
   1732        }
   1733        if (*p != NUL) {
   1734          continue;
   1735        }
   1736 
   1737        // It's possible to create a mapping and then ":unmap" certain
   1738        // modes.  We recreate this here by mapping the individual
   1739        // modes, which requires up to three of them.
   1740        char c1 = NUL;
   1741        char c2 = NUL;
   1742        char c3 = NUL;
   1743        char *cmd = abbr ? "abbr" : "map";
   1744        switch (mp->m_mode) {
   1745        case MODE_NORMAL | MODE_VISUAL | MODE_SELECT | MODE_OP_PENDING:
   1746          break;
   1747        case MODE_NORMAL:
   1748          c1 = 'n';
   1749          break;
   1750        case MODE_VISUAL:
   1751          c1 = 'x';
   1752          break;
   1753        case MODE_SELECT:
   1754          c1 = 's';
   1755          break;
   1756        case MODE_OP_PENDING:
   1757          c1 = 'o';
   1758          break;
   1759        case MODE_NORMAL | MODE_VISUAL:
   1760          c1 = 'n';
   1761          c2 = 'x';
   1762          break;
   1763        case MODE_NORMAL | MODE_SELECT:
   1764          c1 = 'n';
   1765          c2 = 's';
   1766          break;
   1767        case MODE_NORMAL | MODE_OP_PENDING:
   1768          c1 = 'n';
   1769          c2 = 'o';
   1770          break;
   1771        case MODE_VISUAL | MODE_SELECT:
   1772          c1 = 'v';
   1773          break;
   1774        case MODE_VISUAL | MODE_OP_PENDING:
   1775          c1 = 'x';
   1776          c2 = 'o';
   1777          break;
   1778        case MODE_SELECT | MODE_OP_PENDING:
   1779          c1 = 's';
   1780          c2 = 'o';
   1781          break;
   1782        case MODE_NORMAL | MODE_VISUAL | MODE_SELECT:
   1783          c1 = 'n';
   1784          c2 = 'v';
   1785          break;
   1786        case MODE_NORMAL | MODE_VISUAL | MODE_OP_PENDING:
   1787          c1 = 'n';
   1788          c2 = 'x';
   1789          c3 = 'o';
   1790          break;
   1791        case MODE_NORMAL | MODE_SELECT | MODE_OP_PENDING:
   1792          c1 = 'n';
   1793          c2 = 's';
   1794          c3 = 'o';
   1795          break;
   1796        case MODE_VISUAL | MODE_SELECT | MODE_OP_PENDING:
   1797          c1 = 'v';
   1798          c2 = 'o';
   1799          break;
   1800        case MODE_CMDLINE | MODE_INSERT:
   1801          if (!abbr) {
   1802            cmd = "map!";
   1803          }
   1804          break;
   1805        case MODE_CMDLINE:
   1806          c1 = 'c';
   1807          break;
   1808        case MODE_INSERT:
   1809          c1 = 'i';
   1810          break;
   1811        case MODE_LANGMAP:
   1812          c1 = 'l';
   1813          break;
   1814        case MODE_TERMINAL:
   1815          c1 = 't';
   1816          break;
   1817        default:
   1818          iemsg(_("E228: makemap: Illegal mode"));
   1819          return FAIL;
   1820        }
   1821        do {  // do this twice if c2 is set, 3 times with c3
   1822          // When outputting <> form, need to make sure that 'cpo'
   1823          // is set to the Vim default.
   1824          if (!did_cpo) {
   1825            if (*mp->m_str == NUL) {  // Will use <Nop>.
   1826              did_cpo = true;
   1827            } else {
   1828              const char specials[] = { (char)(uint8_t)K_SPECIAL, NL, NUL };
   1829              if (strpbrk(mp->m_str, specials) != NULL || strpbrk(mp->m_keys, specials) != NULL) {
   1830                did_cpo = true;
   1831              }
   1832            }
   1833            if (did_cpo) {
   1834              if (fprintf(fd, "let s:cpo_save=&cpo") < 0
   1835                  || put_eol(fd) < 0
   1836                  || fprintf(fd, "set cpo&vim") < 0
   1837                  || put_eol(fd) < 0) {
   1838                return FAIL;
   1839              }
   1840            }
   1841          }
   1842          if (c1 && putc(c1, fd) < 0) {
   1843            return FAIL;
   1844          }
   1845          if (mp->m_noremap != REMAP_YES && fprintf(fd, "nore") < 0) {
   1846            return FAIL;
   1847          }
   1848          if (fputs(cmd, fd) < 0) {
   1849            return FAIL;
   1850          }
   1851          if (buf != NULL && fputs(" <buffer>", fd) < 0) {
   1852            return FAIL;
   1853          }
   1854          if (mp->m_nowait && fputs(" <nowait>", fd) < 0) {
   1855            return FAIL;
   1856          }
   1857          if (mp->m_silent && fputs(" <silent>", fd) < 0) {
   1858            return FAIL;
   1859          }
   1860          if (mp->m_expr && fputs(" <expr>", fd) < 0) {
   1861            return FAIL;
   1862          }
   1863 
   1864          if (putc(' ', fd) < 0
   1865              || put_escstr(fd, mp->m_keys, 0) == FAIL
   1866              || putc(' ', fd) < 0
   1867              || put_escstr(fd, mp->m_str, 1) == FAIL
   1868              || put_eol(fd) < 0) {
   1869            return FAIL;
   1870          }
   1871          c1 = c2;
   1872          c2 = c3;
   1873          c3 = NUL;
   1874        } while (c1 != NUL);
   1875      }
   1876    }
   1877  }
   1878  if (did_cpo) {
   1879    if (fprintf(fd, "let &cpo=s:cpo_save") < 0
   1880        || put_eol(fd) < 0
   1881        || fprintf(fd, "unlet s:cpo_save") < 0
   1882        || put_eol(fd) < 0) {
   1883      return FAIL;
   1884    }
   1885  }
   1886  return OK;
   1887 }
   1888 
   1889 // write escape string to file
   1890 // "what": 0 for :map lhs, 1 for :map rhs, 2 for :set
   1891 //
   1892 // return FAIL for failure, OK otherwise
   1893 int put_escstr(FILE *fd, const char *strstart, int what)
   1894 {
   1895  uint8_t *str = (uint8_t *)strstart;
   1896 
   1897  // :map xx <Nop>
   1898  if (*str == NUL && what == 1) {
   1899    if (fprintf(fd, "<Nop>") < 0) {
   1900      return FAIL;
   1901    }
   1902    return OK;
   1903  }
   1904 
   1905  for (; *str != NUL; str++) {
   1906    // Check for a multi-byte character, which may contain escaped
   1907    // K_SPECIAL bytes.
   1908    const char *p = mb_unescape((const char **)&str);
   1909    if (p != NULL) {
   1910      while (*p != NUL) {
   1911        if (fputc(*p++, fd) < 0) {
   1912          return FAIL;
   1913        }
   1914      }
   1915      str--;
   1916      continue;
   1917    }
   1918 
   1919    int c = *str;
   1920    // Special key codes have to be translated to be able to make sense
   1921    // when they are read back.
   1922    if (c == K_SPECIAL && what != 2) {
   1923      int modifiers = 0;
   1924      if (str[1] == KS_MODIFIER) {
   1925        modifiers = str[2];
   1926        str += 3;
   1927 
   1928        // Modifiers can be applied too to multi-byte characters.
   1929        p = mb_unescape((const char **)&str);
   1930 
   1931        if (p == NULL) {
   1932          c = *str;
   1933        } else {
   1934          // retrieve codepoint (character number) from unescaped string
   1935          c = utf_ptr2char(p);
   1936          str--;
   1937        }
   1938      }
   1939      if (c == K_SPECIAL) {
   1940        c = TO_SPECIAL(str[1], str[2]);
   1941        str += 2;
   1942      }
   1943      if (IS_SPECIAL(c) || modifiers) {         // special key
   1944        if (fputs(get_special_key_name(c, modifiers), fd) < 0) {
   1945          return FAIL;
   1946        }
   1947        continue;
   1948      }
   1949    }
   1950 
   1951    // A '\n' in a map command should be written as <NL>.
   1952    // A '\n' in a set command should be written as \^V^J.
   1953    if (c == NL) {
   1954      if (what == 2) {
   1955        if (fprintf(fd, "\\\026\n") < 0) {
   1956          return FAIL;
   1957        }
   1958      } else {
   1959        if (fprintf(fd, "<NL>") < 0) {
   1960          return FAIL;
   1961        }
   1962      }
   1963      continue;
   1964    }
   1965 
   1966    // Some characters have to be escaped with CTRL-V to
   1967    // prevent them from misinterpreted in DoOneCmd().
   1968    // A space, Tab and '"' has to be escaped with a backslash to
   1969    // prevent it to be misinterpreted in do_set().
   1970    // A space has to be escaped with a CTRL-V when it's at the start of a
   1971    // ":map" rhs.
   1972    // A '<' has to be escaped with a CTRL-V to prevent it being
   1973    // interpreted as the start of a special key name.
   1974    // A space in the lhs of a :map needs a CTRL-V.
   1975    if (what == 2 && (ascii_iswhite(c) || c == '"' || c == '\\')) {
   1976      if (putc('\\', fd) < 0) {
   1977        return FAIL;
   1978      }
   1979    } else if (c < ' ' || c > '~' || c == '|'
   1980               || (what == 0 && c == ' ')
   1981               || (what == 1 && str == (uint8_t *)strstart && c == ' ')
   1982               || (what != 2 && c == '<')) {
   1983      if (putc(Ctrl_V, fd) < 0) {
   1984        return FAIL;
   1985      }
   1986    }
   1987    if (putc(c, fd) < 0) {
   1988      return FAIL;
   1989    }
   1990  }
   1991  return OK;
   1992 }
   1993 
   1994 /// Check the string "keys" against the lhs of all mappings.
   1995 /// Return pointer to rhs of mapping (mapblock->m_str).
   1996 /// NULL when no mapping found.
   1997 ///
   1998 /// @param exact  require exact match
   1999 /// @param ign_mod  ignore preceding modifier
   2000 /// @param abbr  do abbreviations
   2001 /// @param mp_ptr  return: pointer to mapblock or NULL
   2002 /// @param local_ptr  return: buffer-local mapping or NULL
   2003 char *check_map(char *keys, int mode, int exact, int ign_mod, int abbr, mapblock_T **mp_ptr,
   2004                int *local_ptr, int *rhs_lua)
   2005 {
   2006  *rhs_lua = LUA_NOREF;
   2007 
   2008  int len = (int)strlen(keys);
   2009  for (int local = 1; local >= 0; local--) {
   2010    // loop over all hash lists
   2011    for (int hash = 0; hash < 256; hash++) {
   2012      mapblock_T *mp;
   2013      if (abbr) {
   2014        if (hash > 0) {                 // there is only one list.
   2015          break;
   2016        }
   2017        if (local) {
   2018          mp = curbuf->b_first_abbr;
   2019        } else {
   2020          mp = first_abbr;
   2021        }
   2022      } else if (local) {
   2023        mp = curbuf->b_maphash[hash];
   2024      } else {
   2025        mp = maphash[hash];
   2026      }
   2027      for (; mp != NULL; mp = mp->m_next) {
   2028        // skip entries with wrong mode, wrong length and not matching ones
   2029        if ((mp->m_mode & mode) && (!exact || mp->m_keylen == len)) {
   2030          char *s = mp->m_keys;
   2031          int keylen = mp->m_keylen;
   2032          if (ign_mod && keylen >= 3
   2033              && (uint8_t)s[0] == K_SPECIAL && (uint8_t)s[1] == KS_MODIFIER) {
   2034            s += 3;
   2035            keylen -= 3;
   2036          }
   2037          int minlen = keylen < len ? keylen : len;
   2038          if (strncmp(s, keys, (size_t)minlen) == 0) {
   2039            if (mp_ptr != NULL) {
   2040              *mp_ptr = mp;
   2041            }
   2042            if (local_ptr != NULL) {
   2043              *local_ptr = local;
   2044            }
   2045            *rhs_lua = mp->m_luaref;
   2046            return mp->m_luaref == LUA_NOREF ? mp->m_str : NULL;
   2047          }
   2048        }
   2049      }
   2050    }
   2051  }
   2052 
   2053  return NULL;
   2054 }
   2055 
   2056 /// "hasmapto()" function
   2057 void f_hasmapto(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
   2058 {
   2059  const char *mode;
   2060  const char *const name = tv_get_string(&argvars[0]);
   2061  bool abbr = false;
   2062  char buf[NUMBUFLEN];
   2063  if (argvars[1].v_type == VAR_UNKNOWN) {
   2064    mode = "nvo";
   2065  } else {
   2066    mode = tv_get_string_buf(&argvars[1], buf);
   2067    if (argvars[2].v_type != VAR_UNKNOWN) {
   2068      abbr = tv_get_number(&argvars[2]);
   2069    }
   2070  }
   2071 
   2072  rettv->vval.v_number = map_to_exists(name, mode, abbr);
   2073 }
   2074 
   2075 /// Fill a Dict with all applicable maparg() like dictionaries
   2076 ///
   2077 /// @param mp            The maphash that contains the mapping information
   2078 /// @param buffer_value  The "buffer" value
   2079 /// @param abbr          True if abbreviation
   2080 /// @param compatible    True for compatible with old maparg() dict
   2081 ///
   2082 /// @return  Dict.
   2083 static Dict mapblock_fill_dict(const mapblock_T *const mp, const char *lhsrawalt,
   2084                               const int buffer_value, const bool abbr, const bool compatible,
   2085                               Arena *arena)
   2086  FUNC_ATTR_NONNULL_ARG(1)
   2087 {
   2088  Dict dict = arena_dict(arena, 19);
   2089  char *const lhs = str2special_arena(mp->m_keys, compatible, !compatible, arena);
   2090  char *mapmode = arena_alloc(arena, 7, false);
   2091  map_mode_to_chars(mp->m_mode, mapmode);
   2092  int noremap_value;
   2093 
   2094  if (compatible) {
   2095    // Keep old compatible behavior
   2096    // This is unable to determine whether a mapping is a <script> mapping
   2097    noremap_value = !!mp->m_noremap;
   2098  } else {
   2099    // Distinguish between <script> mapping
   2100    // If it's not a <script> mapping, check if it's a noremap
   2101    noremap_value = mp->m_noremap == REMAP_SCRIPT ? 2 : !!mp->m_noremap;
   2102  }
   2103 
   2104  if (mp->m_luaref != LUA_NOREF) {
   2105    PUT_C(dict, "callback", LUAREF_OBJ(api_new_luaref(mp->m_luaref)));
   2106  } else {
   2107    String rhs = cstr_as_string(compatible
   2108                                ? mp->m_orig_str
   2109                                : str2special_arena(mp->m_str, false, true, arena));
   2110    PUT_C(dict, "rhs", STRING_OBJ(rhs));
   2111  }
   2112  if (mp->m_desc != NULL) {
   2113    PUT_C(dict, "desc", CSTR_AS_OBJ(mp->m_desc));
   2114  }
   2115  PUT_C(dict, "lhs", CSTR_AS_OBJ(lhs));
   2116  PUT_C(dict, "lhsraw", CSTR_AS_OBJ(mp->m_keys));
   2117  if (lhsrawalt != NULL) {
   2118    // Also add the value for the simplified entry.
   2119    PUT_C(dict, "lhsrawalt", CSTR_AS_OBJ(lhsrawalt));
   2120  }
   2121  PUT_C(dict, "noremap", INTEGER_OBJ(noremap_value));
   2122  PUT_C(dict, "script", INTEGER_OBJ(mp->m_noremap == REMAP_SCRIPT ? 1 : 0));
   2123  PUT_C(dict, "expr", INTEGER_OBJ(mp->m_expr ? 1 : 0));
   2124  PUT_C(dict, "silent", INTEGER_OBJ(mp->m_silent ? 1 : 0));
   2125  PUT_C(dict, "sid", INTEGER_OBJ(mp->m_script_ctx.sc_sid));
   2126  PUT_C(dict, "scriptversion", INTEGER_OBJ(1));
   2127  PUT_C(dict, "lnum", INTEGER_OBJ(mp->m_script_ctx.sc_lnum));
   2128  PUT_C(dict, "buffer", INTEGER_OBJ(buffer_value));
   2129  PUT_C(dict, "nowait", INTEGER_OBJ(mp->m_nowait ? 1 : 0));
   2130  PUT_C(dict, "replace_keycodes", INTEGER_OBJ(mp->m_replace_keycodes ? 1 : 0));
   2131  PUT_C(dict, "mode", CSTR_AS_OBJ(mapmode));
   2132  PUT_C(dict, "abbr", INTEGER_OBJ(abbr ? 1 : 0));
   2133  PUT_C(dict, "mode_bits", INTEGER_OBJ(mp->m_mode));
   2134 
   2135  return dict;
   2136 }
   2137 
   2138 static void get_maparg(typval_T *argvars, typval_T *rettv, int exact)
   2139 {
   2140  // Return empty string for failure.
   2141  rettv->v_type = VAR_STRING;
   2142  rettv->vval.v_string = NULL;
   2143 
   2144  char *keys = (char *)tv_get_string(&argvars[0]);
   2145  if (*keys == NUL) {
   2146    return;
   2147  }
   2148 
   2149  const char *which;
   2150  char buf[NUMBUFLEN];
   2151  bool abbr = false;
   2152  bool get_dict = false;
   2153 
   2154  if (argvars[1].v_type != VAR_UNKNOWN) {
   2155    which = tv_get_string_buf_chk(&argvars[1], buf);
   2156    if (argvars[2].v_type != VAR_UNKNOWN) {
   2157      abbr = (bool)tv_get_number(&argvars[2]);
   2158      if (argvars[3].v_type != VAR_UNKNOWN) {
   2159        get_dict = (bool)tv_get_number(&argvars[3]);
   2160      }
   2161    }
   2162  } else {
   2163    which = "";
   2164  }
   2165  if (which == NULL) {
   2166    return;
   2167  }
   2168 
   2169  char *keys_buf = NULL;
   2170  char *alt_keys_buf = NULL;
   2171  bool did_simplify = false;
   2172  const int flags = REPTERM_FROM_PART | REPTERM_DO_LT;
   2173  const int mode = get_map_mode((char **)&which, 0);
   2174 
   2175  char *keys_simplified = replace_termcodes(keys, strlen(keys), &keys_buf, 0,
   2176                                            flags, &did_simplify, p_cpo);
   2177  mapblock_T *mp = NULL;
   2178  int buffer_local;
   2179  LuaRef rhs_lua;
   2180  char *rhs = check_map(keys_simplified, mode, exact, false, abbr, &mp, &buffer_local,
   2181                        &rhs_lua);
   2182  if (did_simplify) {
   2183    // When the lhs is being simplified the not-simplified keys are
   2184    // preferred for printing, like in do_map().
   2185    replace_termcodes(keys, strlen(keys), &alt_keys_buf, 0,
   2186                      flags | REPTERM_NO_SIMPLIFY, NULL, p_cpo);
   2187    rhs = check_map(alt_keys_buf, mode, exact, false, abbr, &mp, &buffer_local, &rhs_lua);
   2188  }
   2189 
   2190  if (!get_dict) {
   2191    // Return a string.
   2192    if (rhs != NULL) {
   2193      if (*rhs == NUL) {
   2194        rettv->vval.v_string = xstrdup("<Nop>");
   2195      } else {
   2196        rettv->vval.v_string = str2special_save(rhs, false, false);
   2197      }
   2198    } else if (rhs_lua != LUA_NOREF) {
   2199      rettv->vval.v_string = nlua_funcref_str(mp->m_luaref, NULL);
   2200    }
   2201  } else {
   2202    // Return a dictionary.
   2203    if (mp != NULL && (rhs != NULL || rhs_lua != LUA_NOREF)) {
   2204      Arena arena = ARENA_EMPTY;
   2205      Dict dict = mapblock_fill_dict(mp, did_simplify ? keys_simplified : NULL,
   2206                                     buffer_local, abbr, true, &arena);
   2207      object_to_vim_take_luaref(&DICT_OBJ(dict), rettv, true, NULL);
   2208      arena_mem_free(arena_finish(&arena));
   2209    } else {
   2210      // Return an empty dictionary.
   2211      tv_dict_alloc_ret(rettv);
   2212    }
   2213  }
   2214 
   2215  xfree(keys_buf);
   2216  xfree(alt_keys_buf);
   2217 }
   2218 
   2219 /// Get the mapping mode from the mode string.
   2220 /// It may contain multiple characters, eg "nox", or "!", or ' '
   2221 /// Return 0 if there is an error.
   2222 static int get_map_mode_string(const char *const mode_string, const bool abbr)
   2223 {
   2224  const char *p = mode_string;
   2225  const int MASK_V = MODE_VISUAL | MODE_SELECT;
   2226  const int MASK_MAP = MODE_VISUAL | MODE_SELECT | MODE_NORMAL | MODE_OP_PENDING;
   2227  const int MASK_BANG = MODE_INSERT | MODE_CMDLINE;
   2228 
   2229  if (*p == NUL) {
   2230    p = " ";  // compatibility
   2231  }
   2232  int mode = 0;
   2233  int modec;
   2234  while ((modec = (uint8_t)(*p++))) {
   2235    int tmode;
   2236    switch (modec) {
   2237    case 'i':
   2238      tmode = MODE_INSERT; break;
   2239    case 'l':
   2240      tmode = MODE_LANGMAP; break;
   2241    case 'c':
   2242      tmode = MODE_CMDLINE; break;
   2243    case 'n':
   2244      tmode = MODE_NORMAL; break;
   2245    case 'x':
   2246      tmode = MODE_VISUAL; break;
   2247    case 's':
   2248      tmode = MODE_SELECT; break;
   2249    case 'o':
   2250      tmode = MODE_OP_PENDING; break;
   2251    case 't':
   2252      tmode = MODE_TERMINAL; break;
   2253    case 'v':
   2254      tmode = MASK_V; break;
   2255    case '!':
   2256      tmode = MASK_BANG; break;
   2257    case ' ':
   2258      tmode = MASK_MAP; break;
   2259    default:
   2260      return 0;  // error, unknown mode character
   2261    }
   2262    mode |= tmode;
   2263  }
   2264  if ((abbr && (mode & ~MASK_BANG) != 0)
   2265      || (!abbr && (mode & (mode - 1)) != 0  // more than one bit set
   2266          && (
   2267              // false if multiple bits set in mode and mode is fully
   2268              // contained in one mask
   2269              !(((mode & MASK_BANG) != 0 && (mode & ~MASK_BANG) == 0)
   2270                || ((mode & MASK_MAP) != 0 && (mode & ~MASK_MAP) == 0))))) {
   2271    return 0;
   2272  }
   2273 
   2274  return mode;
   2275 }
   2276 
   2277 /// "mapset()" function
   2278 void f_mapset(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
   2279 {
   2280  const char *which;
   2281  char buf[NUMBUFLEN];
   2282  int is_abbr;
   2283  dict_T *d;
   2284 
   2285  // If first arg is a dict, then that's the only arg permitted.
   2286  const bool dict_only = argvars[0].v_type == VAR_DICT;
   2287 
   2288  if (dict_only) {
   2289    d = argvars[0].vval.v_dict;
   2290    which = tv_dict_get_string(d, "mode", false);
   2291    is_abbr = (int)tv_dict_get_bool(d, "abbr", -1);
   2292    if (which == NULL || is_abbr < 0) {
   2293      emsg(_(e_entries_missing_in_mapset_dict_argument));
   2294      return;
   2295    }
   2296  } else {
   2297    which = tv_get_string_buf_chk(&argvars[0], buf);
   2298    if (which == NULL) {
   2299      return;
   2300    }
   2301    is_abbr = (int)tv_get_bool(&argvars[1]);
   2302    if (tv_check_for_dict_arg(argvars, 2) == FAIL) {
   2303      return;
   2304    }
   2305    d = argvars[2].vval.v_dict;
   2306  }
   2307  const int mode = get_map_mode_string(which, is_abbr);
   2308  if (mode == 0) {
   2309    semsg(_(e_illegal_map_mode_string_str), which);
   2310    return;
   2311  }
   2312 
   2313  // Get the values in the same order as above in get_maparg().
   2314  char *lhs = tv_dict_get_string(d, "lhs", false);
   2315  char *lhsraw = tv_dict_get_string(d, "lhsraw", false);
   2316  char *lhsrawalt = tv_dict_get_string(d, "lhsrawalt", false);
   2317  char *orig_rhs = tv_dict_get_string(d, "rhs", false);
   2318  LuaRef rhs_lua = LUA_NOREF;
   2319  dictitem_T *callback_di = tv_dict_find(d, S_LEN("callback"));
   2320  if (callback_di != NULL) {
   2321    if (callback_di->di_tv.v_type == VAR_FUNC) {
   2322      ufunc_T *fp = find_func(callback_di->di_tv.vval.v_string);
   2323      if (fp != NULL && (fp->uf_flags & FC_LUAREF)) {
   2324        rhs_lua = api_new_luaref(fp->uf_luaref);
   2325        orig_rhs = "";
   2326      }
   2327    }
   2328  }
   2329  if (lhs == NULL || lhsraw == NULL || orig_rhs == NULL) {
   2330    emsg(_(e_entries_missing_in_mapset_dict_argument));
   2331    api_free_luaref(rhs_lua);
   2332    return;
   2333  }
   2334 
   2335  int noremap = tv_dict_get_number(d, "noremap") != 0 ? REMAP_NONE : 0;
   2336  if (tv_dict_get_number(d, "script") != 0) {
   2337    noremap = REMAP_SCRIPT;
   2338  }
   2339  MapArguments args = {
   2340    .expr = tv_dict_get_number(d, "expr") != 0,
   2341    .silent = tv_dict_get_number(d, "silent") != 0,
   2342    .nowait = tv_dict_get_number(d, "nowait") != 0,
   2343    .replace_keycodes = tv_dict_get_number(d, "replace_keycodes") != 0,
   2344    .desc = tv_dict_get_string(d, "desc", true),
   2345  };
   2346  scid_T sid = (scid_T)tv_dict_get_number(d, "sid");
   2347  linenr_T lnum = (linenr_T)tv_dict_get_number(d, "lnum");
   2348  bool buffer = tv_dict_get_number(d, "buffer") != 0;
   2349  // mode from the dict is not used
   2350 
   2351  set_maparg_rhs(orig_rhs, strlen(orig_rhs), rhs_lua, sid, p_cpo, &args);
   2352 
   2353  mapblock_T **map_table = buffer ? curbuf->b_maphash : maphash;
   2354  mapblock_T **abbr_table = buffer ? &curbuf->b_first_abbr : &first_abbr;
   2355 
   2356  // Delete any existing mapping for this lhs and mode.
   2357  MapArguments unmap_args = MAP_ARGUMENTS_INIT;
   2358  set_maparg_lhs_rhs(lhs, strlen(lhs), "", 0, LUA_NOREF, p_cpo, &unmap_args);
   2359  unmap_args.buffer = buffer;
   2360  buf_do_map(MAPTYPE_UNMAP_LHS, &unmap_args, mode, is_abbr, curbuf);
   2361  xfree(unmap_args.rhs);
   2362  xfree(unmap_args.orig_rhs);
   2363 
   2364  mapblock_T *mp_result[2] = { NULL, NULL };
   2365 
   2366  mp_result[0] = map_add(curbuf, map_table, abbr_table, lhsraw, &args,
   2367                         noremap, mode, is_abbr, sid, lnum, false);
   2368  if (lhsrawalt != NULL) {
   2369    mp_result[1] = map_add(curbuf, map_table, abbr_table, lhsrawalt, &args,
   2370                           noremap, mode, is_abbr, sid, lnum, true);
   2371  }
   2372 
   2373  if (mp_result[0] != NULL && mp_result[1] != NULL) {
   2374    mp_result[0]->m_alt = mp_result[1];
   2375    mp_result[1]->m_alt = mp_result[0];
   2376  }
   2377 }
   2378 
   2379 /// "maplist()" function
   2380 void f_maplist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
   2381 {
   2382  const int flags = REPTERM_FROM_PART | REPTERM_DO_LT;
   2383  const bool abbr = argvars[0].v_type != VAR_UNKNOWN && tv_get_bool(&argvars[0]);
   2384 
   2385  tv_list_alloc_ret(rettv, kListLenUnknown);
   2386 
   2387  // Do it twice: once for global maps and once for local maps.
   2388  for (int buffer_local = 0; buffer_local <= 1; buffer_local++) {
   2389    for (int hash = 0; hash < 256; hash++) {
   2390      mapblock_T *mp;
   2391      if (abbr) {
   2392        if (hash > 0) {  // there is only one abbr list
   2393          break;
   2394        }
   2395        if (buffer_local) {
   2396          mp = curbuf->b_first_abbr;
   2397        } else {
   2398          mp = first_abbr;
   2399        }
   2400      } else if (buffer_local) {
   2401        mp = curbuf->b_maphash[hash];
   2402      } else {
   2403        mp = maphash[hash];
   2404      }
   2405      for (; mp; mp = mp->m_next) {
   2406        if (mp->m_simplified) {
   2407          continue;
   2408        }
   2409 
   2410        char *keys_buf = NULL;
   2411        bool did_simplify = false;
   2412 
   2413        Arena arena = ARENA_EMPTY;
   2414        char *lhs = str2special_arena(mp->m_keys, true, false, &arena);
   2415        replace_termcodes(lhs, strlen(lhs), &keys_buf, 0, flags, &did_simplify,
   2416                          p_cpo);
   2417 
   2418        Dict dict = mapblock_fill_dict(mp, did_simplify ? keys_buf : NULL, buffer_local, abbr, true,
   2419                                       &arena);
   2420        typval_T d = TV_INITIAL_VALUE;
   2421        object_to_vim_take_luaref(&DICT_OBJ(dict), &d, true, NULL);
   2422        assert(d.v_type == VAR_DICT);
   2423        tv_list_append_dict(rettv->vval.v_list, d.vval.v_dict);
   2424        arena_mem_free(arena_finish(&arena));
   2425        xfree(keys_buf);
   2426      }
   2427    }
   2428  }
   2429 }
   2430 
   2431 /// "maparg()" function
   2432 void f_maparg(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
   2433 {
   2434  get_maparg(argvars, rettv, true);
   2435 }
   2436 
   2437 /// "mapcheck()" function
   2438 void f_mapcheck(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
   2439 {
   2440  get_maparg(argvars, rettv, false);
   2441 }
   2442 
   2443 /// Add a mapping. Unlike @ref do_map this copies the string arguments, so
   2444 /// static or read-only strings can be used.
   2445 ///
   2446 /// @param lhs  C-string containing the lhs of the mapping
   2447 /// @param rhs  C-string containing the rhs of the mapping
   2448 /// @param mode  Bitflags representing the mode in which to set the mapping.
   2449 ///              See @ref get_map_mode.
   2450 /// @param buffer  If true, make a buffer-local mapping for curbuf
   2451 void add_map(char *lhs, char *rhs, int mode, bool buffer)
   2452 {
   2453  MapArguments args = MAP_ARGUMENTS_INIT;
   2454  set_maparg_lhs_rhs(lhs, strlen(lhs), rhs, strlen(rhs), LUA_NOREF, p_cpo, &args);
   2455  args.buffer = buffer;
   2456 
   2457  buf_do_map(MAPTYPE_NOREMAP, &args, mode, false, curbuf);
   2458  xfree(args.rhs);
   2459  xfree(args.orig_rhs);
   2460 }
   2461 
   2462 /// Any character has an equivalent 'langmap' character.  This is used for
   2463 /// keyboards that have a special language mode that sends characters above
   2464 /// 128 (although other characters can be translated too).  The "to" field is a
   2465 /// Vim command character.  This avoids having to switch the keyboard back to
   2466 /// ASCII mode when leaving Insert mode.
   2467 ///
   2468 /// langmap_mapchar[] maps any of 256 chars to an ASCII char used for Vim
   2469 /// commands.
   2470 /// langmap_mapga.ga_data is a sorted table of langmap_entry_T.
   2471 /// This does the same as langmap_mapchar[] for characters >= 256.
   2472 ///
   2473 /// With multi-byte support use growarray for 'langmap' chars >= 256
   2474 typedef struct {
   2475  int from;
   2476  int to;
   2477 } langmap_entry_T;
   2478 
   2479 static garray_T langmap_mapga = GA_EMPTY_INIT_VALUE;
   2480 
   2481 /// Search for an entry in "langmap_mapga" for "from".  If found set the "to"
   2482 /// field.  If not found insert a new entry at the appropriate location.
   2483 static void langmap_set_entry(int from, int to)
   2484 {
   2485  langmap_entry_T *entries = (langmap_entry_T *)(langmap_mapga.ga_data);
   2486  unsigned a = 0;
   2487  assert(langmap_mapga.ga_len >= 0);
   2488  unsigned b = (unsigned)langmap_mapga.ga_len;
   2489 
   2490  // Do a binary search for an existing entry.
   2491  while (a != b) {
   2492    unsigned i = (a + b) / 2;
   2493    int d = entries[i].from - from;
   2494 
   2495    if (d == 0) {
   2496      entries[i].to = to;
   2497      return;
   2498    }
   2499    if (d < 0) {
   2500      a = i + 1;
   2501    } else {
   2502      b = i;
   2503    }
   2504  }
   2505 
   2506  ga_grow(&langmap_mapga, 1);
   2507 
   2508  // insert new entry at position "a"
   2509  entries = (langmap_entry_T *)(langmap_mapga.ga_data) + a;
   2510  memmove(entries + 1, entries,
   2511          ((unsigned)langmap_mapga.ga_len - a) * sizeof(langmap_entry_T));
   2512  langmap_mapga.ga_len++;
   2513  entries[0].from = from;
   2514  entries[0].to = to;
   2515 }
   2516 
   2517 /// Apply 'langmap' to multi-byte character "c" and return the result.
   2518 int langmap_adjust_mb(int c)
   2519 {
   2520  langmap_entry_T *entries = (langmap_entry_T *)(langmap_mapga.ga_data);
   2521  int a = 0;
   2522  int b = langmap_mapga.ga_len;
   2523 
   2524  while (a != b) {
   2525    int i = (a + b) / 2;
   2526    int d = entries[i].from - c;
   2527 
   2528    if (d == 0) {
   2529      return entries[i].to;        // found matching entry
   2530    }
   2531    if (d < 0) {
   2532      a = i + 1;
   2533    } else {
   2534      b = i;
   2535    }
   2536  }
   2537  return c;    // no entry found, return "c" unmodified
   2538 }
   2539 
   2540 void langmap_init(void)
   2541 {
   2542  for (int i = 0; i < 256; i++) {
   2543    langmap_mapchar[i] = (uint8_t)i;      // we init with a one-to-one map
   2544  }
   2545  ga_init(&langmap_mapga, sizeof(langmap_entry_T), 8);
   2546 }
   2547 
   2548 /// Called when langmap option is set; the language map can be
   2549 /// changed at any time!
   2550 const char *did_set_langmap(optset_T *args)
   2551 {
   2552  ga_clear(&langmap_mapga);  // clear the previous map first
   2553  langmap_init();            // back to one-to-one map
   2554 
   2555  for (char *p = p_langmap; p[0] != NUL;) {
   2556    char *p2;
   2557    for (p2 = p; p2[0] != NUL && p2[0] != ',' && p2[0] != ';';
   2558         MB_PTR_ADV(p2)) {
   2559      if (p2[0] == '\\' && p2[1] != NUL) {
   2560        p2++;
   2561      }
   2562    }
   2563    if (p2[0] == ';') {
   2564      p2++;                 // abcd;ABCD form, p2 points to A
   2565    } else {
   2566      p2 = NULL;            // aAbBcCdD form, p2 is NULL
   2567    }
   2568    while (p[0]) {
   2569      if (p[0] == ',') {
   2570        p++;
   2571        break;
   2572      }
   2573      if (p[0] == '\\' && p[1] != NUL) {
   2574        p++;
   2575      }
   2576      int from = utf_ptr2char(p);
   2577      const char *const from_ptr = p;
   2578      int to = NUL;
   2579      const char *to_ptr = "";
   2580      if (p2 == NULL) {
   2581        MB_PTR_ADV(p);
   2582        if (p[0] != ',') {
   2583          if (p[0] == '\\') {
   2584            p++;
   2585          }
   2586          to = utf_ptr2char(to_ptr = p);
   2587        }
   2588      } else {
   2589        if (p2[0] != ',') {
   2590          if (p2[0] == '\\') {
   2591            p2++;
   2592          }
   2593          to = utf_ptr2char(to_ptr = p2);
   2594        }
   2595      }
   2596      if (to == NUL) {
   2597        snprintf(args->os_errbuf, args->os_errbuflen,
   2598                 _("E357: 'langmap': Matching character missing for %s"),
   2599                 transchar(from));
   2600        return args->os_errbuf;
   2601      }
   2602 
   2603      if (from >= 256) {
   2604        langmap_set_entry(from, to);
   2605      } else {
   2606        if (to > UCHAR_MAX) {
   2607          swmsg(true, "'langmap': Mapping from %.*s to %.*s will not work properly",
   2608                utf_ptr2len(from_ptr), from_ptr, utf_ptr2len(to_ptr), to_ptr);
   2609        }
   2610        langmap_mapchar[from & 255] = (uint8_t)to;
   2611      }
   2612 
   2613      // Advance to next pair
   2614      MB_PTR_ADV(p);
   2615      if (p2 != NULL) {
   2616        MB_PTR_ADV(p2);
   2617        if (*p == ';') {
   2618          p = p2;
   2619          if (p[0] != NUL) {
   2620            if (p[0] != ',') {
   2621              snprintf(args->os_errbuf, args->os_errbuflen,
   2622                       _("E358: 'langmap': Extra characters after semicolon: %s"),
   2623                       p);
   2624              return args->os_errbuf;
   2625            }
   2626            p++;
   2627          }
   2628          break;
   2629        }
   2630      }
   2631    }
   2632  }
   2633 
   2634  return NULL;
   2635 }
   2636 
   2637 static void do_exmap(exarg_T *eap, int isabbrev)
   2638 {
   2639  char *cmdp = eap->cmd;
   2640  int mode = get_map_mode(&cmdp, eap->forceit || isabbrev);
   2641 
   2642  int maptype;
   2643  if (*cmdp == 'n') {
   2644    maptype = MAPTYPE_NOREMAP;
   2645  } else if (*cmdp == 'u') {
   2646    maptype = MAPTYPE_UNMAP;
   2647  } else {
   2648    maptype = MAPTYPE_MAP;
   2649  }
   2650  MapArguments parsed_args;
   2651  int result = str_to_mapargs(eap->arg, maptype == MAPTYPE_UNMAP, &parsed_args);
   2652  switch (result) {
   2653  case 0:
   2654    break;
   2655  case 1:
   2656    emsg(_(e_invarg));
   2657    goto free_rhs;
   2658    break;
   2659  default:
   2660    assert(false && "Unknown return code from str_to_mapargs!");
   2661    goto free_rhs;
   2662  }
   2663  switch (buf_do_map(maptype, &parsed_args, mode, isabbrev, curbuf)) {
   2664  case 1:
   2665    emsg(_(e_invarg));
   2666    break;
   2667  case 2:
   2668    emsg(isabbrev ? _(e_noabbr) : _(e_nomap));
   2669    break;
   2670  case 5:
   2671    semsg(isabbrev ? _(e_abbreviation_already_exists_for_str)
   2672                   : _(e_mapping_already_exists_for_str),
   2673          parsed_args.lhs);
   2674    break;
   2675  case 6:
   2676    semsg(isabbrev ? _(e_global_abbreviation_already_exists_for_str)
   2677                   : _(e_global_mapping_already_exists_for_str),
   2678          parsed_args.lhs);
   2679  }
   2680 free_rhs:
   2681  xfree(parsed_args.rhs);
   2682  xfree(parsed_args.orig_rhs);
   2683 }
   2684 
   2685 /// ":abbreviate" and friends.
   2686 void ex_abbreviate(exarg_T *eap)
   2687 {
   2688  do_exmap(eap, true);          // almost the same as mapping
   2689 }
   2690 
   2691 /// ":map" and friends.
   2692 void ex_map(exarg_T *eap)
   2693 {
   2694  // If we are in a secure mode we print the mappings for security reasons.
   2695  if (secure) {
   2696    secure = 2;
   2697    msg_outtrans(eap->cmd, 0, false);
   2698    msg_putchar('\n');
   2699  }
   2700  do_exmap(eap, false);
   2701 }
   2702 
   2703 /// ":unmap" and friends.
   2704 void ex_unmap(exarg_T *eap)
   2705 {
   2706  do_exmap(eap, false);
   2707 }
   2708 
   2709 /// ":mapclear" and friends.
   2710 void ex_mapclear(exarg_T *eap)
   2711 {
   2712  do_mapclear(eap->cmd, eap->arg, eap->forceit, false);
   2713 }
   2714 
   2715 /// ":abclear" and friends.
   2716 void ex_abclear(exarg_T *eap)
   2717 {
   2718  do_mapclear(eap->cmd, eap->arg, true, true);
   2719 }
   2720 
   2721 /// Set, tweak, or remove a mapping in a mode. Acts as the implementation for
   2722 /// functions like @ref nvim_buf_set_keymap.
   2723 ///
   2724 /// Arguments are handled like @ref nvim_set_keymap unless noted.
   2725 /// @param  buffer    Buffer handle for a specific buffer, or 0 for the current
   2726 ///                   buffer, or -1 to signify global behavior ("all buffers")
   2727 /// @param  is_unmap  When true, removes the mapping that matches {lhs}.
   2728 void modify_keymap(uint64_t channel_id, Buffer buffer, bool is_unmap, String mode, String lhs,
   2729                   String rhs, Dict(keymap) *opts, Error *err)
   2730 {
   2731  LuaRef lua_funcref = LUA_NOREF;
   2732  bool global = (buffer == -1);
   2733  if (global) {
   2734    buffer = 0;
   2735  }
   2736  buf_T *target_buf = find_buffer_by_handle(buffer, err);
   2737 
   2738  if (!target_buf) {
   2739    return;
   2740  }
   2741 
   2742  const sctx_T save_current_sctx = api_set_sctx(channel_id);
   2743 
   2744  MapArguments parsed_args = MAP_ARGUMENTS_INIT;
   2745  if (opts) {
   2746    parsed_args.nowait = opts->nowait;
   2747    parsed_args.noremap = opts->noremap;
   2748    parsed_args.silent = opts->silent;
   2749    parsed_args.script = opts->script;
   2750    parsed_args.expr = opts->expr;
   2751    parsed_args.unique = opts->unique;
   2752    parsed_args.replace_keycodes = opts->replace_keycodes;
   2753    if (HAS_KEY(opts, keymap, callback)) {
   2754      lua_funcref = opts->callback;
   2755      opts->callback = LUA_NOREF;
   2756    }
   2757    if (HAS_KEY(opts, keymap, desc)) {
   2758      parsed_args.desc = string_to_cstr(opts->desc);
   2759    }
   2760  }
   2761  parsed_args.buffer = !global;
   2762 
   2763  if (parsed_args.replace_keycodes && !parsed_args.expr) {
   2764    api_set_error(err, kErrorTypeValidation,  "\"replace_keycodes\" requires \"expr\"");
   2765    goto fail_and_free;
   2766  }
   2767 
   2768  if (!set_maparg_lhs_rhs(lhs.data, lhs.size,
   2769                          rhs.data, rhs.size, lua_funcref,
   2770                          p_cpo, &parsed_args)) {
   2771    api_set_error(err, kErrorTypeValidation,  "LHS exceeds maximum map length: %s", lhs.data);
   2772    goto fail_and_free;
   2773  }
   2774 
   2775  if (parsed_args.lhs_len > MAXMAPLEN || parsed_args.alt_lhs_len > MAXMAPLEN) {
   2776    api_set_error(err, kErrorTypeValidation,  "LHS exceeds maximum map length: %s", lhs.data);
   2777    goto fail_and_free;
   2778  }
   2779 
   2780  char *p = mode.size > 0 ? mode.data : "m";
   2781  bool forceit = *p == '!';
   2782  // integer value of the mapping mode, to be passed to do_map()
   2783  int mode_val = get_map_mode(&p, forceit);
   2784  if (forceit) {
   2785    assert(p == mode.data);
   2786    p++;
   2787  }
   2788  bool is_abbrev = (mode_val & (MODE_INSERT | MODE_CMDLINE)) != 0 && *p == 'a';
   2789  if (is_abbrev) {
   2790    p++;
   2791  }
   2792  if (mode.size > 0 && (size_t)(p - mode.data) != mode.size) {
   2793    api_set_error(err, kErrorTypeValidation, "Invalid mode shortname: \"%s\"", mode.data);
   2794    goto fail_and_free;
   2795  }
   2796 
   2797  if (parsed_args.lhs_len == 0) {
   2798    api_set_error(err, kErrorTypeValidation, "Invalid (empty) LHS");
   2799    goto fail_and_free;
   2800  }
   2801 
   2802  bool is_noremap = parsed_args.noremap;
   2803  assert(!(is_unmap && is_noremap));
   2804 
   2805  if (!is_unmap && lua_funcref == LUA_NOREF
   2806      && (parsed_args.rhs_len == 0 && !parsed_args.rhs_is_noop)) {
   2807    if (rhs.size == 0) {  // assume that the user wants RHS to be a <Nop>
   2808      parsed_args.rhs_is_noop = true;
   2809    } else {
   2810      abort();  // should never happen
   2811    }
   2812  } else if (is_unmap && (parsed_args.rhs_len || parsed_args.rhs_lua != LUA_NOREF)) {
   2813    if (parsed_args.rhs_len) {
   2814      api_set_error(err, kErrorTypeValidation,
   2815                    "Gave nonempty RHS in unmap command: %s", parsed_args.rhs);
   2816    } else {
   2817      api_set_error(err, kErrorTypeValidation, "Gave nonempty RHS for unmap");
   2818    }
   2819    goto fail_and_free;
   2820  }
   2821 
   2822  // buf_do_map() reads noremap/unmap as its own argument.
   2823  int maptype_val = MAPTYPE_MAP;
   2824  if (is_unmap) {
   2825    maptype_val = MAPTYPE_UNMAP;
   2826  } else if (is_noremap) {
   2827    maptype_val = MAPTYPE_NOREMAP;
   2828  }
   2829 
   2830  switch (buf_do_map(maptype_val, &parsed_args, mode_val, is_abbrev, target_buf)) {
   2831  case 0:
   2832    break;
   2833  case 1:
   2834    api_set_error(err, kErrorTypeException, e_invarg, 0);
   2835    goto fail_and_free;
   2836  case 2:
   2837    api_set_error(err, kErrorTypeException, e_nomap, 0);
   2838    goto fail_and_free;
   2839  case 5:
   2840    api_set_error(err, kErrorTypeException,
   2841                  is_abbrev ? e_abbreviation_already_exists_for_str
   2842                            : e_mapping_already_exists_for_str, lhs.data);
   2843    goto fail_and_free;
   2844    break;
   2845  case 6:
   2846    api_set_error(err, kErrorTypeException,
   2847                  is_abbrev ? e_global_abbreviation_already_exists_for_str
   2848                            : e_global_mapping_already_exists_for_str, lhs.data);
   2849    goto fail_and_free;
   2850  default:
   2851    assert(false && "Unrecognized return code!");
   2852    goto fail_and_free;
   2853  }  // switch
   2854 
   2855 fail_and_free:
   2856  current_sctx = save_current_sctx;
   2857  NLUA_CLEAR_REF(parsed_args.rhs_lua);
   2858  xfree(parsed_args.rhs);
   2859  xfree(parsed_args.orig_rhs);
   2860  xfree(parsed_args.desc);
   2861 }
   2862 
   2863 /// Get an array containing dictionaries describing mappings
   2864 /// based on mode and buffer id
   2865 ///
   2866 /// @param  mode  The abbreviation for the mode
   2867 /// @param  buf  The buffer to get the mapping array. NULL for global
   2868 /// @returns Array of maparg()-like dictionaries describing mappings
   2869 ArrayOf(Dict) keymap_array(String mode, buf_T *buf, Arena *arena)
   2870 {
   2871  ArrayBuilder mappings = KV_INITIAL_VALUE;
   2872  kvi_init(mappings);
   2873 
   2874  char *p = mode.size > 0 ? mode.data : "m";
   2875  bool forceit = *p == '!';
   2876  // Convert the string mode to the integer mode stored within each mapblock.
   2877  int int_mode = get_map_mode(&p, forceit);
   2878  if (forceit) {
   2879    assert(p == mode.data);
   2880    p++;
   2881  }
   2882  bool is_abbrev = (int_mode & (MODE_INSERT | MODE_CMDLINE)) != 0 && *p == 'a';
   2883 
   2884  // Determine the desired buffer value
   2885  int buffer_value = (buf == NULL) ? 0 : buf->handle;
   2886 
   2887  for (int i = 0; i < (is_abbrev ? 1 : MAX_MAPHASH); i++) {
   2888    for (const mapblock_T *current_maphash = is_abbrev
   2889                                             ? (buf ? buf->b_first_abbr : first_abbr)
   2890                                             : (buf ? buf->b_maphash[i] : maphash[i]);
   2891         current_maphash;
   2892         current_maphash = current_maphash->m_next) {
   2893      if (current_maphash->m_simplified) {
   2894        continue;
   2895      }
   2896      // Check for correct mode
   2897      if (int_mode & current_maphash->m_mode) {
   2898        kvi_push(mappings, DICT_OBJ(mapblock_fill_dict(current_maphash,
   2899                                                       current_maphash->m_alt
   2900                                                       ? current_maphash->m_alt->m_keys : NULL,
   2901                                                       buffer_value,
   2902                                                       is_abbrev, false, arena)));
   2903      }
   2904    }
   2905  }
   2906 
   2907  return arena_take_arraybuilder(arena, &mappings);
   2908 }