blob: 4dec9e76ed88f6f0d0efb75bcb10eeb2b0cd8985 [file] [log] [blame]
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001/* vi:set ts=8 sts=4 sw=4 noet:
2 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
10/*
zeertzjqa3f83fe2021-11-22 12:47:39 +000011 * map.c: Code for mappings and abbreviations.
Bram Moolenaarb66bab32019-08-01 14:28:24 +020012 */
13
14#include "vim.h"
15
16/*
17 * List used for abbreviations.
18 */
19static mapblock_T *first_abbr = NULL; // first entry in abbrlist
20
21/*
22 * Each mapping is put in one of the 256 hash lists, to speed up finding it.
23 */
24static mapblock_T *(maphash[256]);
25static int maphash_valid = FALSE;
26
Bram Moolenaarbf533e42022-11-13 20:43:19 +000027// When non-zero then no mappings can be added or removed. Prevents mappings
28// to change while listing them.
29static int map_locked = 0;
30
Bram Moolenaarb66bab32019-08-01 14:28:24 +020031/*
32 * Make a hash value for a mapping.
33 * "mode" is the lower 4 bits of the State for the mapping.
34 * "c1" is the first character of the "lhs".
35 * Returns a value between 0 and 255, index in maphash.
36 * Put Normal/Visual mode mappings mostly separately from Insert/Cmdline mode.
37 */
Bram Moolenaar24959102022-05-07 20:01:16 +010038#define MAP_HASH(mode, c1) (((mode) & (MODE_NORMAL | MODE_VISUAL | MODE_SELECT | MODE_OP_PENDING | MODE_TERMINAL)) ? (c1) : ((c1) ^ 0x80))
Bram Moolenaarb66bab32019-08-01 14:28:24 +020039
40/*
41 * Get the start of the hashed map list for "state" and first character "c".
42 */
43 mapblock_T *
44get_maphash_list(int state, int c)
45{
46 return maphash[MAP_HASH(state, c)];
47}
48
49/*
50 * Get the buffer-local hashed map list for "state" and first character "c".
51 */
52 mapblock_T *
53get_buf_maphash_list(int state, int c)
54{
55 return curbuf->b_maphash[MAP_HASH(state, c)];
56}
57
58 int
59is_maphash_valid(void)
60{
61 return maphash_valid;
62}
63
64/*
65 * Initialize maphash[] for first use.
66 */
67 static void
68validate_maphash(void)
69{
70 if (!maphash_valid)
71 {
Bram Moolenaara80faa82020-04-12 19:37:17 +020072 CLEAR_FIELD(maphash);
Bram Moolenaarb66bab32019-08-01 14:28:24 +020073 maphash_valid = TRUE;
74 }
75}
76
77/*
78 * Delete one entry from the abbrlist or maphash[].
79 * "mpp" is a pointer to the m_next field of the PREVIOUS entry!
80 */
81 static void
82map_free(mapblock_T **mpp)
83{
84 mapblock_T *mp;
85
86 mp = *mpp;
87 vim_free(mp->m_keys);
88 vim_free(mp->m_str);
89 vim_free(mp->m_orig_str);
90 *mpp = mp->m_next;
Bram Moolenaard648c012022-01-16 14:58:34 +000091#ifdef FEAT_EVAL
Bram Moolenaarf61c89d2022-01-19 22:51:48 +000092 reset_last_used_map(mp);
Bram Moolenaard648c012022-01-16 14:58:34 +000093#endif
Bram Moolenaar8aa0e6c2022-01-20 11:27:58 +000094 vim_free(mp);
Bram Moolenaarb66bab32019-08-01 14:28:24 +020095}
96
97/*
98 * Return characters to represent the map mode in an allocated string.
99 * Returns NULL when out of memory.
100 */
101 static char_u *
102map_mode_to_chars(int mode)
103{
104 garray_T mapmode;
105
106 ga_init2(&mapmode, 1, 7);
107
Bram Moolenaar24959102022-05-07 20:01:16 +0100108 if ((mode & (MODE_INSERT | MODE_CMDLINE)) == (MODE_INSERT | MODE_CMDLINE))
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200109 ga_append(&mapmode, '!'); // :map!
Bram Moolenaar24959102022-05-07 20:01:16 +0100110 else if (mode & MODE_INSERT)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200111 ga_append(&mapmode, 'i'); // :imap
Bram Moolenaar24959102022-05-07 20:01:16 +0100112 else if (mode & MODE_LANGMAP)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200113 ga_append(&mapmode, 'l'); // :lmap
Bram Moolenaar24959102022-05-07 20:01:16 +0100114 else if (mode & MODE_CMDLINE)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200115 ga_append(&mapmode, 'c'); // :cmap
Bram Moolenaar24959102022-05-07 20:01:16 +0100116 else if ((mode
117 & (MODE_NORMAL | MODE_VISUAL | MODE_SELECT | MODE_OP_PENDING))
118 == (MODE_NORMAL | MODE_VISUAL | MODE_SELECT | MODE_OP_PENDING))
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200119 ga_append(&mapmode, ' '); // :map
120 else
121 {
Bram Moolenaar24959102022-05-07 20:01:16 +0100122 if (mode & MODE_NORMAL)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200123 ga_append(&mapmode, 'n'); // :nmap
Bram Moolenaar24959102022-05-07 20:01:16 +0100124 if (mode & MODE_OP_PENDING)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200125 ga_append(&mapmode, 'o'); // :omap
Bram Moolenaar24959102022-05-07 20:01:16 +0100126 if (mode & MODE_TERMINAL)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200127 ga_append(&mapmode, 't'); // :tmap
Bram Moolenaar24959102022-05-07 20:01:16 +0100128 if ((mode & (MODE_VISUAL | MODE_SELECT)) == (MODE_VISUAL | MODE_SELECT))
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200129 ga_append(&mapmode, 'v'); // :vmap
130 else
131 {
Bram Moolenaar24959102022-05-07 20:01:16 +0100132 if (mode & MODE_VISUAL)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200133 ga_append(&mapmode, 'x'); // :xmap
Bram Moolenaar24959102022-05-07 20:01:16 +0100134 if (mode & MODE_SELECT)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200135 ga_append(&mapmode, 's'); // :smap
136 }
137 }
138
139 ga_append(&mapmode, NUL);
140 return (char_u *)mapmode.ga_data;
141}
142
Bram Moolenaar9f62ea02022-10-19 13:07:03 +0100143/*
144 * Output a line for one mapping.
145 */
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200146 static void
147showmap(
148 mapblock_T *mp,
149 int local) // TRUE for buffer-local map
150{
151 int len = 1;
152 char_u *mapchars;
153
154 if (message_filtered(mp->m_keys) && message_filtered(mp->m_str))
155 return;
156
Bram Moolenaarbf533e42022-11-13 20:43:19 +0000157 // Prevent mappings to be cleared while at the more prompt.
158 // Must jump to "theend" instead of returning.
159 ++map_locked;
160
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200161 if (msg_didout || msg_silent != 0)
162 {
163 msg_putchar('\n');
164 if (got_int) // 'q' typed at MORE prompt
Bram Moolenaarbf533e42022-11-13 20:43:19 +0000165 goto theend;
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200166 }
167
168 mapchars = map_mode_to_chars(mp->m_mode);
169 if (mapchars != NULL)
170 {
171 msg_puts((char *)mapchars);
172 len = (int)STRLEN(mapchars);
173 vim_free(mapchars);
174 }
175
176 while (++len <= 3)
177 msg_putchar(' ');
178
179 // Display the LHS. Get length of what we write.
180 len = msg_outtrans_special(mp->m_keys, TRUE, 0);
181 do
182 {
183 msg_putchar(' '); // padd with blanks
184 ++len;
185 } while (len < 12);
186
187 if (mp->m_noremap == REMAP_NONE)
188 msg_puts_attr("*", HL_ATTR(HLF_8));
189 else if (mp->m_noremap == REMAP_SCRIPT)
190 msg_puts_attr("&", HL_ATTR(HLF_8));
191 else
192 msg_putchar(' ');
193
194 if (local)
195 msg_putchar('@');
196 else
197 msg_putchar(' ');
198
199 // Use FALSE below if we only want things like <Up> to show up as such on
200 // the rhs, and not M-x etc, TRUE gets both -- webb
201 if (*mp->m_str == NUL)
202 msg_puts_attr("<Nop>", HL_ATTR(HLF_8));
203 else
zeertzjqac402f42022-05-04 18:51:43 +0100204 msg_outtrans_special(mp->m_str, FALSE, 0);
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200205#ifdef FEAT_EVAL
206 if (p_verbose > 0)
207 last_set_msg(mp->m_script_ctx);
208#endif
Bram Moolenaard288eaa2022-02-16 18:27:55 +0000209 msg_clr_eos();
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200210 out_flush(); // show one line at a time
Bram Moolenaarbf533e42022-11-13 20:43:19 +0000211
212theend:
213 --map_locked;
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200214}
215
Bram Moolenaar4c9243f2020-05-22 13:10:44 +0200216 static int
217map_add(
218 mapblock_T **map_table,
219 mapblock_T **abbr_table,
220 char_u *keys,
221 char_u *rhs,
222 char_u *orig_rhs,
Bram Moolenaar4c9243f2020-05-22 13:10:44 +0200223 int noremap,
224 int nowait,
225 int silent,
226 int mode,
227 int is_abbr,
228#ifdef FEAT_EVAL
Bram Moolenaar5a80f8a2020-05-22 13:38:18 +0200229 int expr,
Bram Moolenaar4c9243f2020-05-22 13:10:44 +0200230 scid_T sid, // -1 to use current_sctx
Bram Moolenaara9528b32022-01-18 20:51:35 +0000231 int scriptversion,
Bram Moolenaar4c9243f2020-05-22 13:10:44 +0200232 linenr_T lnum,
233#endif
234 int simplified)
235{
Bram Moolenaar94075b22022-01-18 20:30:39 +0000236 mapblock_T *mp = ALLOC_CLEAR_ONE(mapblock_T);
Bram Moolenaar4c9243f2020-05-22 13:10:44 +0200237
238 if (mp == NULL)
239 return FAIL;
240
241 // If CTRL-C has been mapped, don't always use it for Interrupting.
242 if (*keys == Ctrl_C)
243 {
244 if (map_table == curbuf->b_maphash)
245 curbuf->b_mapped_ctrl_c |= mode;
246 else
247 mapped_ctrl_c |= mode;
248 }
249
250 mp->m_keys = vim_strsave(keys);
251 mp->m_str = vim_strsave(rhs);
252 mp->m_orig_str = vim_strsave(orig_rhs);
253 if (mp->m_keys == NULL || mp->m_str == NULL)
254 {
255 vim_free(mp->m_keys);
256 vim_free(mp->m_str);
257 vim_free(mp->m_orig_str);
258 vim_free(mp);
259 return FAIL;
260 }
261 mp->m_keylen = (int)STRLEN(mp->m_keys);
262 mp->m_noremap = noremap;
263 mp->m_nowait = nowait;
264 mp->m_silent = silent;
265 mp->m_mode = mode;
266 mp->m_simplified = simplified;
267#ifdef FEAT_EVAL
268 mp->m_expr = expr;
Bram Moolenaara9528b32022-01-18 20:51:35 +0000269 if (sid > 0)
Bram Moolenaar4c9243f2020-05-22 13:10:44 +0200270 {
271 mp->m_script_ctx.sc_sid = sid;
272 mp->m_script_ctx.sc_lnum = lnum;
Bram Moolenaara9528b32022-01-18 20:51:35 +0000273 mp->m_script_ctx.sc_version = scriptversion;
Bram Moolenaar4c9243f2020-05-22 13:10:44 +0200274 }
275 else
276 {
277 mp->m_script_ctx = current_sctx;
278 mp->m_script_ctx.sc_lnum += SOURCING_LNUM;
279 }
280#endif
281
282 // add the new entry in front of the abbrlist or maphash[] list
283 if (is_abbr)
284 {
285 mp->m_next = *abbr_table;
286 *abbr_table = mp;
287 }
288 else
289 {
290 int n = MAP_HASH(mp->m_mode, mp->m_keys[0]);
291
292 mp->m_next = map_table[n];
293 map_table[n] = mp;
294 }
295 return OK;
296}
297
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200298/*
Bram Moolenaar9f62ea02022-10-19 13:07:03 +0100299 * List mappings. When "haskey" is FALSE all mappings, otherwise mappings that
300 * match "keys[keys_len]".
301 */
302 static void
303list_mappings(
304 int keyround,
305 int abbrev,
306 int haskey,
307 char_u *keys,
308 int keys_len,
309 int mode,
310 int *did_local)
311{
Bram Moolenaarbf533e42022-11-13 20:43:19 +0000312 // Prevent mappings to be cleared while at the more prompt.
313 ++map_locked;
314
Bram Moolenaar9f62ea02022-10-19 13:07:03 +0100315 if (p_verbose > 0 && keyround == 1 && seenModifyOtherKeys)
316 msg_puts(_("Seen modifyOtherKeys: true"));
317
318 // need to loop over all global hash lists
319 for (int hash = 0; hash < 256 && !got_int; ++hash)
320 {
321 mapblock_T *mp;
322
323 if (abbrev)
324 {
325 if (hash != 0) // there is only one abbreviation list
326 break;
327 mp = curbuf->b_first_abbr;
328 }
329 else
330 mp = curbuf->b_maphash[hash];
331 for ( ; mp != NULL && !got_int; mp = mp->m_next)
332 {
333 // check entries with the same mode
334 if (!mp->m_simplified && (mp->m_mode & mode) != 0)
335 {
336 if (!haskey) // show all entries
337 {
338 showmap(mp, TRUE);
339 *did_local = TRUE;
340 }
341 else
342 {
343 int n = mp->m_keylen;
344 if (STRNCMP(mp->m_keys, keys,
345 (size_t)(n < keys_len ? n : keys_len)) == 0)
346 {
347 showmap(mp, TRUE);
348 *did_local = TRUE;
349 }
350 }
351 }
352 }
353 }
Bram Moolenaarbf533e42022-11-13 20:43:19 +0000354
355 --map_locked;
Bram Moolenaar9f62ea02022-10-19 13:07:03 +0100356}
357
358/*
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200359 * map[!] : show all key mappings
360 * map[!] {lhs} : show key mapping for {lhs}
361 * map[!] {lhs} {rhs} : set key mapping for {lhs} to {rhs}
362 * noremap[!] {lhs} {rhs} : same, but no remapping for {rhs}
363 * unmap[!] {lhs} : remove key mapping for {lhs}
364 * abbr : show all abbreviations
365 * abbr {lhs} : show abbreviations for {lhs}
366 * abbr {lhs} {rhs} : set abbreviation for {lhs} to {rhs}
367 * noreabbr {lhs} {rhs} : same, but no remapping for {rhs}
368 * unabbr {lhs} : remove abbreviation for {lhs}
369 *
zeertzjq44068e92022-06-16 11:14:55 +0100370 * maptype: MAPTYPE_MAP for :map
371 * MAPTYPE_UNMAP for :unmap
372 * MAPTYPE_NOREMAP for noremap
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200373 *
374 * arg is pointer to any arguments. Note: arg cannot be a read-only string,
375 * it will be modified.
376 *
Bram Moolenaar24959102022-05-07 20:01:16 +0100377 * for :map mode is MODE_NORMAL | MODE_VISUAL | MODE_SELECT | MODE_OP_PENDING
378 * for :map! mode is MODE_INSERT | MODE_CMDLINE
379 * for :cmap mode is MODE_CMDLINE
380 * for :imap mode is MODE_INSERT
381 * for :lmap mode is MODE_LANGMAP
382 * for :nmap mode is MODE_NORMAL
383 * for :vmap mode is MODE_VISUAL | MODE_SELECT
384 * for :xmap mode is MODE_VISUAL
385 * for :smap mode is MODE_SELECT
386 * for :omap mode is MODE_OP_PENDING
387 * for :tmap mode is MODE_TERMINAL
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200388 *
Bram Moolenaar24959102022-05-07 20:01:16 +0100389 * for :abbr mode is MODE_INSERT | MODE_CMDLINE
390 * for :iabbr mode is MODE_INSERT
391 * for :cabbr mode is MODE_CMDLINE
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200392 *
393 * Return 0 for success
394 * 1 for invalid arguments
395 * 2 for no match
396 * 4 for out of mem
397 * 5 for entry not unique
398 */
399 int
400do_map(
401 int maptype,
402 char_u *arg,
403 int mode,
404 int abbrev) // not a mapping but an abbreviation
405{
406 char_u *keys;
407 mapblock_T *mp, **mpp;
408 char_u *rhs;
409 char_u *p;
410 int n;
411 int len = 0; // init for GCC
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200412 int hasarg;
413 int haskey;
Bram Moolenaar459fd782019-10-13 16:43:39 +0200414 int do_print;
415 int keyround;
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200416 char_u *keys_buf = NULL;
Bram Moolenaar459fd782019-10-13 16:43:39 +0200417 char_u *alt_keys_buf = NULL;
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200418 char_u *arg_buf = NULL;
419 int retval = 0;
420 int do_backslash;
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200421 mapblock_T **abbr_table;
422 mapblock_T **map_table;
423 int unique = FALSE;
424 int nowait = FALSE;
425 int silent = FALSE;
426 int special = FALSE;
427#ifdef FEAT_EVAL
428 int expr = FALSE;
429#endif
Bram Moolenaar459fd782019-10-13 16:43:39 +0200430 int did_simplify = FALSE;
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200431 int noremap;
432 char_u *orig_rhs;
433
434 keys = arg;
435 map_table = maphash;
436 abbr_table = &first_abbr;
437
438 // For ":noremap" don't remap, otherwise do remap.
zeertzjq44068e92022-06-16 11:14:55 +0100439 if (maptype == MAPTYPE_NOREMAP)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200440 noremap = REMAP_NONE;
441 else
442 noremap = REMAP_YES;
443
444 // Accept <buffer>, <nowait>, <silent>, <expr> <script> and <unique> in
445 // any order.
446 for (;;)
447 {
448 // Check for "<buffer>": mapping local to buffer.
449 if (STRNCMP(keys, "<buffer>", 8) == 0)
450 {
451 keys = skipwhite(keys + 8);
452 map_table = curbuf->b_maphash;
453 abbr_table = &curbuf->b_first_abbr;
454 continue;
455 }
456
457 // Check for "<nowait>": don't wait for more characters.
458 if (STRNCMP(keys, "<nowait>", 8) == 0)
459 {
460 keys = skipwhite(keys + 8);
461 nowait = TRUE;
462 continue;
463 }
464
465 // Check for "<silent>": don't echo commands.
466 if (STRNCMP(keys, "<silent>", 8) == 0)
467 {
468 keys = skipwhite(keys + 8);
469 silent = TRUE;
470 continue;
471 }
472
473 // Check for "<special>": accept special keys in <>
474 if (STRNCMP(keys, "<special>", 9) == 0)
475 {
476 keys = skipwhite(keys + 9);
477 special = TRUE;
478 continue;
479 }
480
481#ifdef FEAT_EVAL
482 // Check for "<script>": remap script-local mappings only
483 if (STRNCMP(keys, "<script>", 8) == 0)
484 {
485 keys = skipwhite(keys + 8);
486 noremap = REMAP_SCRIPT;
487 continue;
488 }
489
490 // Check for "<expr>": {rhs} is an expression.
491 if (STRNCMP(keys, "<expr>", 6) == 0)
492 {
493 keys = skipwhite(keys + 6);
494 expr = TRUE;
495 continue;
496 }
497#endif
498 // Check for "<unique>": don't overwrite an existing mapping.
499 if (STRNCMP(keys, "<unique>", 8) == 0)
500 {
501 keys = skipwhite(keys + 8);
502 unique = TRUE;
503 continue;
504 }
505 break;
506 }
507
508 validate_maphash();
509
510 // Find end of keys and skip CTRL-Vs (and backslashes) in it.
511 // Accept backslash like CTRL-V when 'cpoptions' does not contain 'B'.
512 // with :unmap white space is included in the keys, no argument possible.
513 p = keys;
514 do_backslash = (vim_strchr(p_cpo, CPO_BSLASH) == NULL);
zeertzjq44068e92022-06-16 11:14:55 +0100515 while (*p && (maptype == MAPTYPE_UNMAP || !VIM_ISWHITE(*p)))
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200516 {
517 if ((p[0] == Ctrl_V || (do_backslash && p[0] == '\\')) &&
518 p[1] != NUL)
519 ++p; // skip CTRL-V or backslash
520 ++p;
521 }
522 if (*p != NUL)
523 *p++ = NUL;
524
525 p = skipwhite(p);
526 rhs = p;
527 hasarg = (*rhs != NUL);
528 haskey = (*keys != NUL);
zeertzjq44068e92022-06-16 11:14:55 +0100529 do_print = !haskey || (maptype != MAPTYPE_UNMAP && !hasarg);
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200530
531 // check for :unmap without argument
zeertzjq44068e92022-06-16 11:14:55 +0100532 if (maptype == MAPTYPE_UNMAP && !haskey)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200533 {
534 retval = 1;
535 goto theend;
536 }
537
538 // If mapping has been given as ^V<C_UP> say, then replace the term codes
539 // with the appropriate two bytes. If it is a shifted special key, unshift
540 // it too, giving another two bytes.
541 // replace_termcodes() may move the result to allocated memory, which
542 // needs to be freed later (*keys_buf and *arg_buf).
543 // replace_termcodes() also removes CTRL-Vs and sometimes backslashes.
Bram Moolenaar459fd782019-10-13 16:43:39 +0200544 // If something like <C-H> is simplified to 0x08 then mark it as simplified
545 // and also add a n entry with a modifier, which will work when
546 // modifyOtherKeys is working.
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200547 if (haskey)
Bram Moolenaar459fd782019-10-13 16:43:39 +0200548 {
549 char_u *new_keys;
550 int flags = REPTERM_FROM_PART | REPTERM_DO_LT;
551
552 if (special)
553 flags |= REPTERM_SPECIAL;
554 new_keys = replace_termcodes(keys, &keys_buf, flags, &did_simplify);
555 if (did_simplify)
556 (void)replace_termcodes(keys, &alt_keys_buf,
557 flags | REPTERM_NO_SIMPLIFY, NULL);
558 keys = new_keys;
559 }
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200560 orig_rhs = rhs;
561 if (hasarg)
562 {
563 if (STRICMP(rhs, "<nop>") == 0) // "<Nop>" means nothing
564 rhs = (char_u *)"";
565 else
Bram Moolenaar459fd782019-10-13 16:43:39 +0200566 rhs = replace_termcodes(rhs, &arg_buf,
567 REPTERM_DO_LT | (special ? REPTERM_SPECIAL : 0), NULL);
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200568 }
569
Bram Moolenaar459fd782019-10-13 16:43:39 +0200570 /*
571 * The following is done twice if we have two versions of keys:
572 * "alt_keys_buf" is not NULL.
573 */
574 for (keyround = 1; keyround <= 2; ++keyround)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200575 {
Bram Moolenaar459fd782019-10-13 16:43:39 +0200576 int did_it = FALSE;
577 int did_local = FALSE;
Bram Moolenaar87f74102022-04-25 18:59:25 +0100578 int keyround1_simplified = keyround == 1 && did_simplify;
Bram Moolenaar459fd782019-10-13 16:43:39 +0200579 int round;
Bram Moolenaar459fd782019-10-13 16:43:39 +0200580
581 if (keyround == 2)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200582 {
Bram Moolenaar459fd782019-10-13 16:43:39 +0200583 if (alt_keys_buf == NULL)
584 break;
585 keys = alt_keys_buf;
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200586 }
Bram Moolenaar459fd782019-10-13 16:43:39 +0200587 else if (alt_keys_buf != NULL && do_print)
588 // when printing always use the not-simplified map
589 keys = alt_keys_buf;
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200590
Bram Moolenaar459fd782019-10-13 16:43:39 +0200591 // check arguments and translate function keys
592 if (haskey)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200593 {
Bram Moolenaar459fd782019-10-13 16:43:39 +0200594 len = (int)STRLEN(keys);
595 if (len > MAXMAPLEN) // maximum length of MAXMAPLEN chars
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200596 {
Bram Moolenaar459fd782019-10-13 16:43:39 +0200597 retval = 1;
598 goto theend;
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200599 }
Bram Moolenaar459fd782019-10-13 16:43:39 +0200600
zeertzjq44068e92022-06-16 11:14:55 +0100601 if (abbrev && maptype != MAPTYPE_UNMAP)
Bram Moolenaar459fd782019-10-13 16:43:39 +0200602 {
603 // If an abbreviation ends in a keyword character, the
604 // rest must be all keyword-char or all non-keyword-char.
605 // Otherwise we won't be able to find the start of it in a
606 // vi-compatible way.
607 if (has_mbyte)
608 {
609 int first, last;
610 int same = -1;
611
612 first = vim_iswordp(keys);
613 last = first;
614 p = keys + (*mb_ptr2len)(keys);
615 n = 1;
616 while (p < keys + len)
617 {
618 ++n; // nr of (multi-byte) chars
619 last = vim_iswordp(p); // type of last char
620 if (same == -1 && last != first)
621 same = n - 1; // count of same char type
622 p += (*mb_ptr2len)(p);
623 }
624 if (last && n > 2 && same >= 0 && same < n - 1)
625 {
626 retval = 1;
627 goto theend;
628 }
629 }
630 else if (vim_iswordc(keys[len - 1]))
631 // ends in keyword char
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200632 for (n = 0; n < len - 2; ++n)
633 if (vim_iswordc(keys[n]) != vim_iswordc(keys[len - 2]))
634 {
635 retval = 1;
636 goto theend;
637 }
Bram Moolenaar459fd782019-10-13 16:43:39 +0200638 // An abbreviation cannot contain white space.
639 for (n = 0; n < len; ++n)
640 if (VIM_ISWHITE(keys[n]))
641 {
642 retval = 1;
643 goto theend;
644 }
645 }
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200646 }
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200647
Bram Moolenaar459fd782019-10-13 16:43:39 +0200648 if (haskey && hasarg && abbrev) // if we will add an abbreviation
649 no_abbr = FALSE; // reset flag that indicates there are
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200650 // no abbreviations
651
Bram Moolenaar459fd782019-10-13 16:43:39 +0200652 if (do_print)
653 msg_start();
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200654
Bram Moolenaar459fd782019-10-13 16:43:39 +0200655 // Check if a new local mapping wasn't already defined globally.
Bram Moolenaar4c9243f2020-05-22 13:10:44 +0200656 if (unique && map_table == curbuf->b_maphash
zeertzjq44068e92022-06-16 11:14:55 +0100657 && haskey && hasarg && maptype != MAPTYPE_UNMAP)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200658 {
Bram Moolenaar459fd782019-10-13 16:43:39 +0200659 // need to loop over all global hash lists
Bram Moolenaar9f62ea02022-10-19 13:07:03 +0100660 for (int hash = 0; hash < 256 && !got_int; ++hash)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200661 {
Bram Moolenaar459fd782019-10-13 16:43:39 +0200662 if (abbrev)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200663 {
Bram Moolenaar459fd782019-10-13 16:43:39 +0200664 if (hash != 0) // there is only one abbreviation list
665 break;
666 mp = first_abbr;
667 }
668 else
669 mp = maphash[hash];
670 for ( ; mp != NULL && !got_int; mp = mp->m_next)
671 {
672 // check entries with the same mode
673 if ((mp->m_mode & mode) != 0
674 && mp->m_keylen == len
Bram Moolenaar459fd782019-10-13 16:43:39 +0200675 && STRNCMP(mp->m_keys, keys, (size_t)len) == 0)
676 {
677 if (abbrev)
Bram Moolenaar6d057012021-12-31 18:49:43 +0000678 semsg(
679 _(e_global_abbreviation_already_exists_for_str),
Bram Moolenaar459fd782019-10-13 16:43:39 +0200680 mp->m_keys);
681 else
Bram Moolenaar6d057012021-12-31 18:49:43 +0000682 semsg(_(e_global_mapping_already_exists_for_str),
Bram Moolenaar459fd782019-10-13 16:43:39 +0200683 mp->m_keys);
684 retval = 5;
685 goto theend;
686 }
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200687 }
688 }
689 }
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200690
Bram Moolenaar459fd782019-10-13 16:43:39 +0200691 // When listing global mappings, also list buffer-local ones here.
zeertzjq44068e92022-06-16 11:14:55 +0100692 if (map_table != curbuf->b_maphash && !hasarg
693 && maptype != MAPTYPE_UNMAP)
Bram Moolenaar9f62ea02022-10-19 13:07:03 +0100694 list_mappings(keyround, abbrev, haskey, keys, len,
695 mode, &did_local);
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200696
Bram Moolenaar459fd782019-10-13 16:43:39 +0200697 // Find an entry in the maphash[] list that matches.
698 // For :unmap we may loop two times: once to try to unmap an entry with
699 // a matching 'from' part, a second time, if the first fails, to unmap
zeertzjqa3f83fe2021-11-22 12:47:39 +0000700 // an entry with a matching 'to' part. This was done to allow
701 // ":ab foo bar" to be unmapped by typing ":unab foo", where "foo" will
702 // be replaced by "bar" because of the abbreviation.
zeertzjq44068e92022-06-16 11:14:55 +0100703 for (round = 0; (round == 0 || maptype == MAPTYPE_UNMAP) && round <= 1
Bram Moolenaar459fd782019-10-13 16:43:39 +0200704 && !did_it && !got_int; ++round)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200705 {
Bram Moolenaar459fd782019-10-13 16:43:39 +0200706 // need to loop over all hash lists
Bram Moolenaar9f62ea02022-10-19 13:07:03 +0100707 for (int hash = 0; hash < 256 && !got_int; ++hash)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200708 {
Bram Moolenaar459fd782019-10-13 16:43:39 +0200709 if (abbrev)
710 {
711 if (hash > 0) // there is only one abbreviation list
712 break;
713 mpp = abbr_table;
714 }
715 else
716 mpp = &(map_table[hash]);
717 for (mp = *mpp; mp != NULL && !got_int; mp = *mpp)
718 {
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200719
Bram Moolenaarfafb4b12019-10-16 18:34:57 +0200720 if ((mp->m_mode & mode) == 0)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200721 {
Bram Moolenaarfafb4b12019-10-16 18:34:57 +0200722 // skip entries with wrong mode
Bram Moolenaar459fd782019-10-13 16:43:39 +0200723 mpp = &(mp->m_next);
724 continue;
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200725 }
Bram Moolenaar459fd782019-10-13 16:43:39 +0200726 if (!haskey) // show all entries
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200727 {
Bram Moolenaarfafb4b12019-10-16 18:34:57 +0200728 if (!mp->m_simplified)
729 {
730 showmap(mp, map_table != maphash);
731 did_it = TRUE;
732 }
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200733 }
Bram Moolenaar459fd782019-10-13 16:43:39 +0200734 else // do we have a match?
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200735 {
Bram Moolenaar459fd782019-10-13 16:43:39 +0200736 if (round) // second round: Try unmap "rhs" string
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200737 {
Bram Moolenaar459fd782019-10-13 16:43:39 +0200738 n = (int)STRLEN(mp->m_str);
739 p = mp->m_str;
740 }
741 else
742 {
743 n = mp->m_keylen;
744 p = mp->m_keys;
745 }
746 if (STRNCMP(p, keys, (size_t)(n < len ? n : len)) == 0)
747 {
zeertzjq44068e92022-06-16 11:14:55 +0100748 if (maptype == MAPTYPE_UNMAP)
Bram Moolenaar459fd782019-10-13 16:43:39 +0200749 {
750 // Delete entry.
751 // Only accept a full match. For abbreviations
752 // we ignore trailing space when matching with
753 // the "lhs", since an abbreviation can't have
754 // trailing space.
755 if (n != len && (!abbrev || round || n > len
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200756 || *skipwhite(keys + n) != NUL))
Bram Moolenaar459fd782019-10-13 16:43:39 +0200757 {
758 mpp = &(mp->m_next);
759 continue;
760 }
zeertzjqabeb09b2022-04-26 12:29:43 +0100761 // In keyround for simplified keys, don't unmap
762 // a mapping without m_simplified flag.
Bram Moolenaar87f74102022-04-25 18:59:25 +0100763 if (keyround1_simplified && !mp->m_simplified)
zeertzjqa4e33322022-04-24 17:07:53 +0100764 break;
Bram Moolenaar459fd782019-10-13 16:43:39 +0200765 // We reset the indicated mode bits. If nothing
766 // is left the entry is deleted below.
767 mp->m_mode &= ~mode;
768 did_it = TRUE; // remember we did something
769 }
770 else if (!hasarg) // show matching entry
771 {
Bram Moolenaarfafb4b12019-10-16 18:34:57 +0200772 if (!mp->m_simplified)
773 {
774 showmap(mp, map_table != maphash);
775 did_it = TRUE;
776 }
Bram Moolenaar459fd782019-10-13 16:43:39 +0200777 }
778 else if (n != len) // new entry is ambiguous
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200779 {
780 mpp = &(mp->m_next);
781 continue;
782 }
Bram Moolenaar459fd782019-10-13 16:43:39 +0200783 else if (unique)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200784 {
Bram Moolenaar459fd782019-10-13 16:43:39 +0200785 if (abbrev)
Bram Moolenaar6d057012021-12-31 18:49:43 +0000786 semsg(
787 _(e_abbreviation_already_exists_for_str),
Bram Moolenaar459fd782019-10-13 16:43:39 +0200788 p);
789 else
Bram Moolenaar6d057012021-12-31 18:49:43 +0000790 semsg(_(e_mapping_already_exists_for_str),
Bram Moolenaar459fd782019-10-13 16:43:39 +0200791 p);
792 retval = 5;
793 goto theend;
794 }
795 else
796 {
797 // new rhs for existing entry
798 mp->m_mode &= ~mode; // remove mode bits
799 if (mp->m_mode == 0 && !did_it) // reuse entry
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200800 {
Bram Moolenaar459fd782019-10-13 16:43:39 +0200801 char_u *newstr = vim_strsave(rhs);
802
803 if (newstr == NULL)
804 {
805 retval = 4; // no mem
806 goto theend;
807 }
808 vim_free(mp->m_str);
809 mp->m_str = newstr;
810 vim_free(mp->m_orig_str);
811 mp->m_orig_str = vim_strsave(orig_rhs);
812 mp->m_noremap = noremap;
813 mp->m_nowait = nowait;
814 mp->m_silent = silent;
815 mp->m_mode = mode;
Bram Moolenaar87f74102022-04-25 18:59:25 +0100816 mp->m_simplified = keyround1_simplified;
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200817#ifdef FEAT_EVAL
Bram Moolenaar459fd782019-10-13 16:43:39 +0200818 mp->m_expr = expr;
819 mp->m_script_ctx = current_sctx;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +0100820 mp->m_script_ctx.sc_lnum += SOURCING_LNUM;
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200821#endif
Bram Moolenaar459fd782019-10-13 16:43:39 +0200822 did_it = TRUE;
823 }
824 }
825 if (mp->m_mode == 0) // entry can be deleted
826 {
827 map_free(mpp);
828 continue; // continue with *mpp
829 }
830
831 // May need to put this entry into another hash
832 // list.
Bram Moolenaar9f62ea02022-10-19 13:07:03 +0100833 int new_hash = MAP_HASH(mp->m_mode, mp->m_keys[0]);
Bram Moolenaar459fd782019-10-13 16:43:39 +0200834 if (!abbrev && new_hash != hash)
835 {
836 *mpp = mp->m_next;
837 mp->m_next = map_table[new_hash];
838 map_table[new_hash] = mp;
839
840 continue; // continue with *mpp
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200841 }
842 }
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200843 }
Bram Moolenaar459fd782019-10-13 16:43:39 +0200844 mpp = &(mp->m_next);
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200845 }
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200846 }
847 }
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200848
zeertzjq44068e92022-06-16 11:14:55 +0100849 if (maptype == MAPTYPE_UNMAP)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200850 {
Bram Moolenaar459fd782019-10-13 16:43:39 +0200851 // delete entry
852 if (!did_it)
zeertzjqa4e33322022-04-24 17:07:53 +0100853 {
Bram Moolenaar87f74102022-04-25 18:59:25 +0100854 if (!keyround1_simplified)
zeertzjqa4e33322022-04-24 17:07:53 +0100855 retval = 2; // no match
856 }
Bram Moolenaar459fd782019-10-13 16:43:39 +0200857 else if (*keys == Ctrl_C)
858 {
859 // If CTRL-C has been unmapped, reuse it for Interrupting.
860 if (map_table == curbuf->b_maphash)
861 curbuf->b_mapped_ctrl_c &= ~mode;
862 else
863 mapped_ctrl_c &= ~mode;
864 }
865 continue;
866 }
867
868 if (!haskey || !hasarg)
869 {
870 // print entries
871 if (!did_it && !did_local)
872 {
873 if (abbrev)
874 msg(_("No abbreviation found"));
875 else
876 msg(_("No mapping found"));
877 }
878 goto theend; // listing finished
879 }
880
881 if (did_it)
882 continue; // have added the new entry already
883
884 // Get here when adding a new entry to the maphash[] list or abbrlist.
Bram Moolenaar5a80f8a2020-05-22 13:38:18 +0200885 if (map_add(map_table, abbr_table, keys, rhs, orig_rhs,
886 noremap, nowait, silent, mode, abbrev,
Bram Moolenaar4c9243f2020-05-22 13:10:44 +0200887#ifdef FEAT_EVAL
Bram Moolenaara9528b32022-01-18 20:51:35 +0000888 expr, /* sid */ -1, /* scriptversion */ 0, /* lnum */ 0,
Bram Moolenaar4c9243f2020-05-22 13:10:44 +0200889#endif
Bram Moolenaar87f74102022-04-25 18:59:25 +0100890 keyround1_simplified) == FAIL)
Bram Moolenaar459fd782019-10-13 16:43:39 +0200891 {
892 retval = 4; // no mem
893 goto theend;
894 }
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200895 }
896
897theend:
898 vim_free(keys_buf);
Bram Moolenaar459fd782019-10-13 16:43:39 +0200899 vim_free(alt_keys_buf);
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200900 vim_free(arg_buf);
901 return retval;
902}
903
904/*
905 * Get the mapping mode from the command name.
906 */
907 static int
908get_map_mode(char_u **cmdp, int forceit)
909{
910 char_u *p;
911 int modec;
912 int mode;
913
914 p = *cmdp;
915 modec = *p++;
916 if (modec == 'i')
Bram Moolenaar24959102022-05-07 20:01:16 +0100917 mode = MODE_INSERT; // :imap
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200918 else if (modec == 'l')
Bram Moolenaar24959102022-05-07 20:01:16 +0100919 mode = MODE_LANGMAP; // :lmap
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200920 else if (modec == 'c')
Bram Moolenaar24959102022-05-07 20:01:16 +0100921 mode = MODE_CMDLINE; // :cmap
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200922 else if (modec == 'n' && *p != 'o') // avoid :noremap
Bram Moolenaar24959102022-05-07 20:01:16 +0100923 mode = MODE_NORMAL; // :nmap
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200924 else if (modec == 'v')
Bram Moolenaar24959102022-05-07 20:01:16 +0100925 mode = MODE_VISUAL | MODE_SELECT; // :vmap
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200926 else if (modec == 'x')
Bram Moolenaar24959102022-05-07 20:01:16 +0100927 mode = MODE_VISUAL; // :xmap
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200928 else if (modec == 's')
Bram Moolenaar24959102022-05-07 20:01:16 +0100929 mode = MODE_SELECT; // :smap
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200930 else if (modec == 'o')
Bram Moolenaar24959102022-05-07 20:01:16 +0100931 mode = MODE_OP_PENDING; // :omap
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200932 else if (modec == 't')
Bram Moolenaar24959102022-05-07 20:01:16 +0100933 mode = MODE_TERMINAL; // :tmap
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200934 else
935 {
936 --p;
937 if (forceit)
Bram Moolenaar24959102022-05-07 20:01:16 +0100938 mode = MODE_INSERT | MODE_CMDLINE; // :map !
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200939 else
Bram Moolenaar24959102022-05-07 20:01:16 +0100940 mode = MODE_VISUAL | MODE_SELECT | MODE_NORMAL | MODE_OP_PENDING;
941 // :map
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200942 }
943
944 *cmdp = p;
945 return mode;
946}
947
948/*
zeertzjqc207fd22022-06-29 10:37:40 +0100949 * Clear all mappings (":mapclear") or abbreviations (":abclear").
950 * "abbr" should be FALSE for mappings, TRUE for abbreviations.
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200951 */
952 static void
953map_clear(
954 char_u *cmdp,
zeertzjqc207fd22022-06-29 10:37:40 +0100955 char_u *arg,
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200956 int forceit,
957 int abbr)
958{
959 int mode;
960 int local;
961
962 local = (STRCMP(arg, "<buffer>") == 0);
963 if (!local && *arg != NUL)
964 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +0000965 emsg(_(e_invalid_argument));
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200966 return;
967 }
968
969 mode = get_map_mode(&cmdp, forceit);
zeertzjqc207fd22022-06-29 10:37:40 +0100970 map_clear_mode(curbuf, mode, local, abbr);
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200971}
972
973/*
Bram Moolenaarbf533e42022-11-13 20:43:19 +0000974 * If "map_locked" is set then give an error and return TRUE.
975 * Otherwise return FALSE.
976 */
977 static int
978is_map_locked(void)
979{
980 if (map_locked > 0)
981 {
982 emsg(_(e_cannot_change_mappings_while_listing));
983 return TRUE;
984 }
985 return FALSE;
986}
987
988/*
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200989 * Clear all mappings in "mode".
990 */
991 void
zeertzjqc207fd22022-06-29 10:37:40 +0100992map_clear_mode(
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200993 buf_T *buf, // buffer for local mappings
994 int mode, // mode in which to delete
995 int local, // TRUE for buffer-local mappings
996 int abbr) // TRUE for abbreviations
997{
998 mapblock_T *mp, **mpp;
999 int hash;
1000 int new_hash;
1001
Bram Moolenaarbf533e42022-11-13 20:43:19 +00001002 if (is_map_locked())
1003 return;
1004
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001005 validate_maphash();
1006
1007 for (hash = 0; hash < 256; ++hash)
1008 {
1009 if (abbr)
1010 {
1011 if (hash > 0) // there is only one abbrlist
1012 break;
1013 if (local)
1014 mpp = &buf->b_first_abbr;
1015 else
1016 mpp = &first_abbr;
1017 }
1018 else
1019 {
1020 if (local)
1021 mpp = &buf->b_maphash[hash];
1022 else
1023 mpp = &maphash[hash];
1024 }
1025 while (*mpp != NULL)
1026 {
1027 mp = *mpp;
1028 if (mp->m_mode & mode)
1029 {
1030 mp->m_mode &= ~mode;
1031 if (mp->m_mode == 0) // entry can be deleted
1032 {
1033 map_free(mpp);
1034 continue;
1035 }
1036 // May need to put this entry into another hash list.
1037 new_hash = MAP_HASH(mp->m_mode, mp->m_keys[0]);
1038 if (!abbr && new_hash != hash)
1039 {
1040 *mpp = mp->m_next;
1041 if (local)
1042 {
1043 mp->m_next = buf->b_maphash[new_hash];
1044 buf->b_maphash[new_hash] = mp;
1045 }
1046 else
1047 {
1048 mp->m_next = maphash[new_hash];
1049 maphash[new_hash] = mp;
1050 }
1051 continue; // continue with *mpp
1052 }
1053 }
1054 mpp = &(mp->m_next);
1055 }
1056 }
1057}
1058
1059#if defined(FEAT_EVAL) || defined(PROTO)
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001060 int
Bram Moolenaar581ba392019-09-03 22:08:33 +02001061mode_str2flags(char_u *modechars)
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001062{
1063 int mode = 0;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001064
1065 if (vim_strchr(modechars, 'n') != NULL)
Bram Moolenaar24959102022-05-07 20:01:16 +01001066 mode |= MODE_NORMAL;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001067 if (vim_strchr(modechars, 'v') != NULL)
Bram Moolenaar24959102022-05-07 20:01:16 +01001068 mode |= MODE_VISUAL | MODE_SELECT;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001069 if (vim_strchr(modechars, 'x') != NULL)
Bram Moolenaar24959102022-05-07 20:01:16 +01001070 mode |= MODE_VISUAL;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001071 if (vim_strchr(modechars, 's') != NULL)
Bram Moolenaar24959102022-05-07 20:01:16 +01001072 mode |= MODE_SELECT;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001073 if (vim_strchr(modechars, 'o') != NULL)
Bram Moolenaar24959102022-05-07 20:01:16 +01001074 mode |= MODE_OP_PENDING;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001075 if (vim_strchr(modechars, 'i') != NULL)
Bram Moolenaar24959102022-05-07 20:01:16 +01001076 mode |= MODE_INSERT;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001077 if (vim_strchr(modechars, 'l') != NULL)
Bram Moolenaar24959102022-05-07 20:01:16 +01001078 mode |= MODE_LANGMAP;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001079 if (vim_strchr(modechars, 'c') != NULL)
Bram Moolenaar24959102022-05-07 20:01:16 +01001080 mode |= MODE_CMDLINE;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001081
Bram Moolenaar581ba392019-09-03 22:08:33 +02001082 return mode;
1083}
1084
1085/*
1086 * Return TRUE if a map exists that has "str" in the rhs for mode "modechars".
1087 * Recognize termcap codes in "str".
1088 * Also checks mappings local to the current buffer.
1089 */
1090 int
1091map_to_exists(char_u *str, char_u *modechars, int abbr)
1092{
1093 char_u *rhs;
1094 char_u *buf;
1095 int retval;
1096
Bram Moolenaar459fd782019-10-13 16:43:39 +02001097 rhs = replace_termcodes(str, &buf, REPTERM_DO_LT, NULL);
Bram Moolenaar581ba392019-09-03 22:08:33 +02001098
1099 retval = map_to_exists_mode(rhs, mode_str2flags(modechars), abbr);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001100 vim_free(buf);
1101
1102 return retval;
1103}
1104#endif
1105
1106/*
1107 * Return TRUE if a map exists that has "str" in the rhs for mode "mode".
1108 * Also checks mappings local to the current buffer.
1109 */
1110 int
1111map_to_exists_mode(char_u *rhs, int mode, int abbr)
1112{
1113 mapblock_T *mp;
1114 int hash;
1115 int exp_buffer = FALSE;
1116
1117 validate_maphash();
1118
1119 // Do it twice: once for global maps and once for local maps.
1120 for (;;)
1121 {
1122 for (hash = 0; hash < 256; ++hash)
1123 {
1124 if (abbr)
1125 {
1126 if (hash > 0) // there is only one abbr list
1127 break;
1128 if (exp_buffer)
1129 mp = curbuf->b_first_abbr;
1130 else
1131 mp = first_abbr;
1132 }
1133 else if (exp_buffer)
1134 mp = curbuf->b_maphash[hash];
1135 else
1136 mp = maphash[hash];
1137 for (; mp; mp = mp->m_next)
1138 {
1139 if ((mp->m_mode & mode)
1140 && strstr((char *)mp->m_str, (char *)rhs) != NULL)
1141 return TRUE;
1142 }
1143 }
1144 if (exp_buffer)
1145 break;
1146 exp_buffer = TRUE;
1147 }
1148
1149 return FALSE;
1150}
1151
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001152/*
1153 * Used below when expanding mapping/abbreviation names.
1154 */
1155static int expand_mapmodes = 0;
1156static int expand_isabbrev = 0;
1157static int expand_buffer = FALSE;
1158
1159/*
Bram Moolenaar7f51bbe2020-01-24 20:21:19 +01001160 * Translate an internal mapping/abbreviation representation into the
1161 * corresponding external one recognized by :map/:abbrev commands.
1162 * Respects the current B/k/< settings of 'cpoption'.
1163 *
1164 * This function is called when expanding mappings/abbreviations on the
1165 * command-line.
1166 *
1167 * It uses a growarray to build the translation string since the latter can be
1168 * wider than the original description. The caller has to free the string
1169 * afterwards.
1170 *
1171 * Returns NULL when there is a problem.
1172 */
1173 static char_u *
1174translate_mapping(char_u *str)
1175{
1176 garray_T ga;
1177 int c;
1178 int modifiers;
1179 int cpo_bslash;
1180 int cpo_special;
1181
1182 ga_init(&ga);
1183 ga.ga_itemsize = 1;
1184 ga.ga_growsize = 40;
1185
1186 cpo_bslash = (vim_strchr(p_cpo, CPO_BSLASH) != NULL);
1187 cpo_special = (vim_strchr(p_cpo, CPO_SPECI) != NULL);
1188
1189 for (; *str; ++str)
1190 {
1191 c = *str;
1192 if (c == K_SPECIAL && str[1] != NUL && str[2] != NUL)
1193 {
1194 modifiers = 0;
1195 if (str[1] == KS_MODIFIER)
1196 {
1197 str++;
1198 modifiers = *++str;
1199 c = *++str;
1200 }
1201 if (c == K_SPECIAL && str[1] != NUL && str[2] != NUL)
1202 {
1203 if (cpo_special)
1204 {
1205 ga_clear(&ga);
1206 return NULL;
1207 }
1208 c = TO_SPECIAL(str[1], str[2]);
1209 if (c == K_ZERO) // display <Nul> as ^@
1210 c = NUL;
1211 str += 2;
1212 }
1213 if (IS_SPECIAL(c) || modifiers) // special key
1214 {
1215 if (cpo_special)
1216 {
1217 ga_clear(&ga);
1218 return NULL;
1219 }
1220 ga_concat(&ga, get_special_key_name(c, modifiers));
1221 continue; // for (str)
1222 }
1223 }
1224 if (c == ' ' || c == '\t' || c == Ctrl_J || c == Ctrl_V
1225 || (c == '<' && !cpo_special) || (c == '\\' && !cpo_bslash))
1226 ga_append(&ga, cpo_bslash ? Ctrl_V : '\\');
1227 if (c)
1228 ga_append(&ga, c);
1229 }
1230 ga_append(&ga, NUL);
1231 return (char_u *)(ga.ga_data);
1232}
1233
1234/*
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001235 * Work out what to complete when doing command line completion of mapping
1236 * or abbreviation names.
1237 */
1238 char_u *
1239set_context_in_map_cmd(
1240 expand_T *xp,
1241 char_u *cmd,
1242 char_u *arg,
1243 int forceit, // TRUE if '!' given
1244 int isabbrev, // TRUE if abbreviation
1245 int isunmap, // TRUE if unmap/unabbrev command
1246 cmdidx_T cmdidx)
1247{
1248 if (forceit && cmdidx != CMD_map && cmdidx != CMD_unmap)
1249 xp->xp_context = EXPAND_NOTHING;
1250 else
1251 {
1252 if (isunmap)
1253 expand_mapmodes = get_map_mode(&cmd, forceit || isabbrev);
1254 else
1255 {
Bram Moolenaar24959102022-05-07 20:01:16 +01001256 expand_mapmodes = MODE_INSERT | MODE_CMDLINE;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001257 if (!isabbrev)
Bram Moolenaar24959102022-05-07 20:01:16 +01001258 expand_mapmodes += MODE_VISUAL | MODE_SELECT | MODE_NORMAL
1259 | MODE_OP_PENDING;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001260 }
1261 expand_isabbrev = isabbrev;
1262 xp->xp_context = EXPAND_MAPPINGS;
1263 expand_buffer = FALSE;
1264 for (;;)
1265 {
1266 if (STRNCMP(arg, "<buffer>", 8) == 0)
1267 {
1268 expand_buffer = TRUE;
1269 arg = skipwhite(arg + 8);
1270 continue;
1271 }
1272 if (STRNCMP(arg, "<unique>", 8) == 0)
1273 {
1274 arg = skipwhite(arg + 8);
1275 continue;
1276 }
1277 if (STRNCMP(arg, "<nowait>", 8) == 0)
1278 {
1279 arg = skipwhite(arg + 8);
1280 continue;
1281 }
1282 if (STRNCMP(arg, "<silent>", 8) == 0)
1283 {
1284 arg = skipwhite(arg + 8);
1285 continue;
1286 }
1287 if (STRNCMP(arg, "<special>", 9) == 0)
1288 {
1289 arg = skipwhite(arg + 9);
1290 continue;
1291 }
1292#ifdef FEAT_EVAL
1293 if (STRNCMP(arg, "<script>", 8) == 0)
1294 {
1295 arg = skipwhite(arg + 8);
1296 continue;
1297 }
1298 if (STRNCMP(arg, "<expr>", 6) == 0)
1299 {
1300 arg = skipwhite(arg + 6);
1301 continue;
1302 }
1303#endif
1304 break;
1305 }
1306 xp->xp_pattern = arg;
1307 }
1308
1309 return NULL;
1310}
1311
1312/*
1313 * Find all mapping/abbreviation names that match regexp "regmatch"'.
1314 * For command line expansion of ":[un]map" and ":[un]abbrev" in all modes.
1315 * Return OK if matches found, FAIL otherwise.
1316 */
1317 int
1318ExpandMappings(
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001319 char_u *pat,
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001320 regmatch_T *regmatch,
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001321 int *numMatches,
1322 char_u ***matches)
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001323{
1324 mapblock_T *mp;
Yegappan Lakshmanan5de4c432022-02-28 13:28:38 +00001325 garray_T ga;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001326 int hash;
1327 int count;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001328 char_u *p;
1329 int i;
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001330 int fuzzy;
1331 int match;
Yasuhiro Matsumoto09f68a52022-06-18 16:48:36 +01001332 int score = 0;
Yegappan Lakshmanan5de4c432022-02-28 13:28:38 +00001333 fuzmatch_str_T *fuzmatch;
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001334
1335 fuzzy = cmdline_fuzzy_complete(pat);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001336
1337 validate_maphash();
1338
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001339 *numMatches = 0; // return values in case of FAIL
1340 *matches = NULL;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001341
Yegappan Lakshmanan5de4c432022-02-28 13:28:38 +00001342 if (!fuzzy)
1343 ga_init2(&ga, sizeof(char *), 3);
1344 else
1345 ga_init2(&ga, sizeof(fuzmatch_str_T), 3);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001346
Yegappan Lakshmanan5de4c432022-02-28 13:28:38 +00001347 // First search in map modifier arguments
1348 for (i = 0; i < 7; ++i)
1349 {
1350 if (i == 0)
1351 p = (char_u *)"<silent>";
1352 else if (i == 1)
1353 p = (char_u *)"<unique>";
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001354#ifdef FEAT_EVAL
Yegappan Lakshmanan5de4c432022-02-28 13:28:38 +00001355 else if (i == 2)
1356 p = (char_u *)"<script>";
1357 else if (i == 3)
1358 p = (char_u *)"<expr>";
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001359#endif
Yegappan Lakshmanan5de4c432022-02-28 13:28:38 +00001360 else if (i == 4 && !expand_buffer)
1361 p = (char_u *)"<buffer>";
1362 else if (i == 5)
1363 p = (char_u *)"<nowait>";
1364 else if (i == 6)
1365 p = (char_u *)"<special>";
1366 else
1367 continue;
1368
1369 if (!fuzzy)
1370 match = vim_regexec(regmatch, p, (colnr_T)0);
1371 else
1372 {
1373 score = fuzzy_match_str(p, pat);
1374 match = (score != 0);
1375 }
1376
1377 if (!match)
1378 continue;
1379
1380 if (ga_grow(&ga, 1) == FAIL)
1381 break;
1382
1383 if (fuzzy)
1384 {
1385 fuzmatch = &((fuzmatch_str_T *)ga.ga_data)[ga.ga_len];
1386 fuzmatch->idx = ga.ga_len;
1387 fuzmatch->str = vim_strsave(p);
1388 fuzmatch->score = score;
1389 }
1390 else
1391 ((char_u **)ga.ga_data)[ga.ga_len] = vim_strsave(p);
1392 ++ga.ga_len;
1393 }
1394
1395 for (hash = 0; hash < 256; ++hash)
1396 {
1397 if (expand_isabbrev)
1398 {
1399 if (hash > 0) // only one abbrev list
1400 break; // for (hash)
1401 mp = first_abbr;
1402 }
1403 else if (expand_buffer)
1404 mp = curbuf->b_maphash[hash];
1405 else
1406 mp = maphash[hash];
1407 for (; mp; mp = mp->m_next)
1408 {
1409 if (!(mp->m_mode & expand_mapmodes))
1410 continue;
1411
1412 p = translate_mapping(mp->m_keys);
1413 if (p == NULL)
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001414 continue;
1415
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001416 if (!fuzzy)
1417 match = vim_regexec(regmatch, p, (colnr_T)0);
1418 else
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001419 {
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001420 score = fuzzy_match_str(p, pat);
1421 match = (score != 0);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001422 }
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001423
1424 if (!match)
Yegappan Lakshmanan5de4c432022-02-28 13:28:38 +00001425 {
1426 vim_free(p);
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001427 continue;
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001428 }
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001429
Yegappan Lakshmanan5de4c432022-02-28 13:28:38 +00001430 if (ga_grow(&ga, 1) == FAIL)
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001431 {
Yegappan Lakshmanan5de4c432022-02-28 13:28:38 +00001432 vim_free(p);
1433 break;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001434 }
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001435
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001436 if (fuzzy)
1437 {
Yegappan Lakshmanan5de4c432022-02-28 13:28:38 +00001438 fuzmatch = &((fuzmatch_str_T *)ga.ga_data)[ga.ga_len];
1439 fuzmatch->idx = ga.ga_len;
1440 fuzmatch->str = p;
1441 fuzmatch->score = score;
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001442 }
1443 else
Yegappan Lakshmanan5de4c432022-02-28 13:28:38 +00001444 ((char_u **)ga.ga_data)[ga.ga_len] = p;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001445
Yegappan Lakshmanan5de4c432022-02-28 13:28:38 +00001446 ++ga.ga_len;
1447 } // for (mp)
1448 } // for (hash)
1449
1450 if (ga.ga_len == 0)
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001451 return FAIL;
1452
Yegappan Lakshmanan5de4c432022-02-28 13:28:38 +00001453 if (!fuzzy)
1454 {
1455 *matches = ga.ga_data;
1456 *numMatches = ga.ga_len;
1457 }
1458 else
1459 {
1460 if (fuzzymatches_to_strmatches(ga.ga_data, matches, ga.ga_len,
1461 FALSE) == FAIL)
1462 return FAIL;
1463 *numMatches = ga.ga_len;
1464 }
1465
1466 count = *numMatches;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001467 if (count > 1)
1468 {
1469 char_u **ptr1;
1470 char_u **ptr2;
1471 char_u **ptr3;
1472
1473 // Sort the matches
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001474 // Fuzzy matching already sorts the matches
1475 if (!fuzzy)
1476 sort_strings(*matches, count);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001477
1478 // Remove multiple entries
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001479 ptr1 = *matches;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001480 ptr2 = ptr1 + 1;
1481 ptr3 = ptr1 + count;
1482
1483 while (ptr2 < ptr3)
1484 {
1485 if (STRCMP(*ptr1, *ptr2))
1486 *++ptr1 = *ptr2++;
1487 else
1488 {
1489 vim_free(*ptr2++);
1490 count--;
1491 }
1492 }
1493 }
1494
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001495 *numMatches = count;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001496 return (count == 0 ? FAIL : OK);
1497}
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001498
1499/*
1500 * Check for an abbreviation.
1501 * Cursor is at ptr[col].
1502 * When inserting, mincol is where insert started.
1503 * For the command line, mincol is what is to be skipped over.
1504 * "c" is the character typed before check_abbr was called. It may have
1505 * ABBR_OFF added to avoid prepending a CTRL-V to it.
1506 *
1507 * Historic vi practice: The last character of an abbreviation must be an id
1508 * character ([a-zA-Z0-9_]). The characters in front of it must be all id
1509 * characters or all non-id characters. This allows for abbr. "#i" to
1510 * "#include".
1511 *
1512 * Vim addition: Allow for abbreviations that end in a non-keyword character.
1513 * Then there must be white space before the abbr.
1514 *
1515 * return TRUE if there is an abbreviation, FALSE if not
1516 */
1517 int
1518check_abbr(
1519 int c,
1520 char_u *ptr,
1521 int col,
1522 int mincol)
1523{
1524 int len;
1525 int scol; // starting column of the abbr.
1526 int j;
1527 char_u *s;
1528 char_u tb[MB_MAXBYTES + 4];
1529 mapblock_T *mp;
1530 mapblock_T *mp2;
1531 int clen = 0; // length in characters
1532 int is_id = TRUE;
1533 int vim_abbr;
1534
1535 if (typebuf.tb_no_abbr_cnt) // abbrev. are not recursive
1536 return FALSE;
1537
1538 // no remapping implies no abbreviation, except for CTRL-]
1539 if (noremap_keys() && c != Ctrl_RSB)
1540 return FALSE;
1541
1542 // Check for word before the cursor: If it ends in a keyword char all
1543 // chars before it must be keyword chars or non-keyword chars, but not
1544 // white space. If it ends in a non-keyword char we accept any characters
1545 // before it except white space.
1546 if (col == 0) // cannot be an abbr.
1547 return FALSE;
1548
1549 if (has_mbyte)
1550 {
1551 char_u *p;
1552
1553 p = mb_prevptr(ptr, ptr + col);
1554 if (!vim_iswordp(p))
1555 vim_abbr = TRUE; // Vim added abbr.
1556 else
1557 {
1558 vim_abbr = FALSE; // vi compatible abbr.
1559 if (p > ptr)
1560 is_id = vim_iswordp(mb_prevptr(ptr, p));
1561 }
1562 clen = 1;
1563 while (p > ptr + mincol)
1564 {
1565 p = mb_prevptr(ptr, p);
1566 if (vim_isspace(*p) || (!vim_abbr && is_id != vim_iswordp(p)))
1567 {
1568 p += (*mb_ptr2len)(p);
1569 break;
1570 }
1571 ++clen;
1572 }
1573 scol = (int)(p - ptr);
1574 }
1575 else
1576 {
1577 if (!vim_iswordc(ptr[col - 1]))
1578 vim_abbr = TRUE; // Vim added abbr.
1579 else
1580 {
1581 vim_abbr = FALSE; // vi compatible abbr.
1582 if (col > 1)
1583 is_id = vim_iswordc(ptr[col - 2]);
1584 }
1585 for (scol = col - 1; scol > 0 && !vim_isspace(ptr[scol - 1])
1586 && (vim_abbr || is_id == vim_iswordc(ptr[scol - 1])); --scol)
1587 ;
1588 }
1589
1590 if (scol < mincol)
1591 scol = mincol;
1592 if (scol < col) // there is a word in front of the cursor
1593 {
1594 ptr += scol;
1595 len = col - scol;
1596 mp = curbuf->b_first_abbr;
1597 mp2 = first_abbr;
1598 if (mp == NULL)
1599 {
1600 mp = mp2;
1601 mp2 = NULL;
1602 }
1603 for ( ; mp; mp->m_next == NULL
1604 ? (mp = mp2, mp2 = NULL) : (mp = mp->m_next))
1605 {
1606 int qlen = mp->m_keylen;
1607 char_u *q = mp->m_keys;
1608 int match;
1609
1610 if (vim_strbyte(mp->m_keys, K_SPECIAL) != NULL)
1611 {
1612 char_u *qe = vim_strsave(mp->m_keys);
1613
1614 // might have CSI escaped mp->m_keys
1615 if (qe != NULL)
1616 {
1617 q = qe;
1618 vim_unescape_csi(q);
1619 qlen = (int)STRLEN(q);
1620 }
1621 }
1622
1623 // find entries with right mode and keys
1624 match = (mp->m_mode & State)
1625 && qlen == len
1626 && !STRNCMP(q, ptr, (size_t)len);
1627 if (q != mp->m_keys)
1628 vim_free(q);
1629 if (match)
1630 break;
1631 }
1632 if (mp != NULL)
1633 {
Bram Moolenaar94075b22022-01-18 20:30:39 +00001634 int noremap;
1635 int silent;
1636#ifdef FEAT_EVAL
1637 int expr;
1638#endif
1639
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001640 // Found a match:
1641 // Insert the rest of the abbreviation in typebuf.tb_buf[].
1642 // This goes from end to start.
1643 //
1644 // Characters 0x000 - 0x100: normal chars, may need CTRL-V,
1645 // except K_SPECIAL: Becomes K_SPECIAL KS_SPECIAL KE_FILLER
1646 // Characters where IS_SPECIAL() == TRUE: key codes, need
1647 // K_SPECIAL. Other characters (with ABBR_OFF): don't use CTRL-V.
1648 //
1649 // Character CTRL-] is treated specially - it completes the
1650 // abbreviation, but is not inserted into the input stream.
1651 j = 0;
1652 if (c != Ctrl_RSB)
1653 {
1654 // special key code, split up
1655 if (IS_SPECIAL(c) || c == K_SPECIAL)
1656 {
1657 tb[j++] = K_SPECIAL;
1658 tb[j++] = K_SECOND(c);
1659 tb[j++] = K_THIRD(c);
1660 }
1661 else
1662 {
1663 if (c < ABBR_OFF && (c < ' ' || c > '~'))
1664 tb[j++] = Ctrl_V; // special char needs CTRL-V
1665 if (has_mbyte)
1666 {
Bram Moolenaar4934ed32021-04-30 19:43:11 +02001667 int newlen;
1668 char_u *escaped;
1669
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001670 // if ABBR_OFF has been added, remove it here
1671 if (c >= ABBR_OFF)
1672 c -= ABBR_OFF;
Bram Moolenaar4934ed32021-04-30 19:43:11 +02001673 newlen = (*mb_char2bytes)(c, tb + j);
1674 tb[j + newlen] = NUL;
1675 // Need to escape K_SPECIAL.
1676 escaped = vim_strsave_escape_csi(tb + j);
1677 if (escaped != NULL)
1678 {
Bram Moolenaar551c1ae2021-05-03 18:57:05 +02001679 newlen = (int)STRLEN(escaped);
Bram Moolenaar4934ed32021-04-30 19:43:11 +02001680 mch_memmove(tb + j, escaped, newlen);
1681 j += newlen;
1682 vim_free(escaped);
1683 }
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001684 }
1685 else
1686 tb[j++] = c;
1687 }
1688 tb[j] = NUL;
1689 // insert the last typed char
1690 (void)ins_typebuf(tb, 1, 0, TRUE, mp->m_silent);
1691 }
Bram Moolenaar94075b22022-01-18 20:30:39 +00001692
1693 // copy values here, calling eval_map_expr() may make "mp" invalid!
1694 noremap = mp->m_noremap;
1695 silent = mp->m_silent;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001696#ifdef FEAT_EVAL
Bram Moolenaar94075b22022-01-18 20:30:39 +00001697 expr = mp->m_expr;
1698
1699 if (expr)
Bram Moolenaar19db9e62022-01-11 11:58:19 +00001700 s = eval_map_expr(mp, c);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001701 else
1702#endif
1703 s = mp->m_str;
1704 if (s != NULL)
1705 {
1706 // insert the to string
Bram Moolenaar94075b22022-01-18 20:30:39 +00001707 (void)ins_typebuf(s, noremap, 0, TRUE, silent);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001708 // no abbrev. for these chars
1709 typebuf.tb_no_abbr_cnt += (int)STRLEN(s) + j + 1;
1710#ifdef FEAT_EVAL
Bram Moolenaar94075b22022-01-18 20:30:39 +00001711 if (expr)
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001712 vim_free(s);
1713#endif
1714 }
1715
1716 tb[0] = Ctrl_H;
1717 tb[1] = NUL;
1718 if (has_mbyte)
1719 len = clen; // Delete characters instead of bytes
1720 while (len-- > 0) // delete the from string
Bram Moolenaar94075b22022-01-18 20:30:39 +00001721 (void)ins_typebuf(tb, 1, 0, TRUE, silent);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001722 return TRUE;
1723 }
1724 }
1725 return FALSE;
1726}
1727
1728#ifdef FEAT_EVAL
1729/*
1730 * Evaluate the RHS of a mapping or abbreviations and take care of escaping
1731 * special characters.
Bram Moolenaar94075b22022-01-18 20:30:39 +00001732 * Careful: after this "mp" will be invalid if the mapping was deleted.
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001733 */
1734 char_u *
1735eval_map_expr(
Bram Moolenaar19db9e62022-01-11 11:58:19 +00001736 mapblock_T *mp,
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001737 int c) // NUL or typed character for abbreviation
1738{
1739 char_u *res;
1740 char_u *p;
1741 char_u *expr;
1742 pos_T save_cursor;
1743 int save_msg_col;
1744 int save_msg_row;
Bram Moolenaar19db9e62022-01-11 11:58:19 +00001745 scid_T save_sctx_sid = current_sctx.sc_sid;
1746 int save_sctx_version = current_sctx.sc_version;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001747
1748 // Remove escaping of CSI, because "str" is in a format to be used as
1749 // typeahead.
Bram Moolenaar19db9e62022-01-11 11:58:19 +00001750 expr = vim_strsave(mp->m_str);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001751 if (expr == NULL)
1752 return NULL;
1753 vim_unescape_csi(expr);
1754
1755 // Forbid changing text or using ":normal" to avoid most of the bad side
1756 // effects. Also restore the cursor position.
zeertzjqcfe45652022-05-27 17:26:55 +01001757 ++textlock;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001758 ++ex_normal_lock;
1759 set_vim_var_char(c); // set v:char to the typed character
1760 save_cursor = curwin->w_cursor;
1761 save_msg_col = msg_col;
1762 save_msg_row = msg_row;
Bram Moolenaar19db9e62022-01-11 11:58:19 +00001763 if (mp->m_script_ctx.sc_version == SCRIPT_VERSION_VIM9)
1764 {
1765 current_sctx.sc_sid = mp->m_script_ctx.sc_sid;
1766 current_sctx.sc_version = SCRIPT_VERSION_VIM9;
1767 }
1768
1769 // Note: the evaluation may make "mp" invalid.
Bram Moolenaara4e0b972022-10-01 19:43:52 +01001770 p = eval_to_string(expr, FALSE, FALSE);
Bram Moolenaar19db9e62022-01-11 11:58:19 +00001771
zeertzjqcfe45652022-05-27 17:26:55 +01001772 --textlock;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001773 --ex_normal_lock;
1774 curwin->w_cursor = save_cursor;
1775 msg_col = save_msg_col;
1776 msg_row = save_msg_row;
Bram Moolenaar19db9e62022-01-11 11:58:19 +00001777 current_sctx.sc_sid = save_sctx_sid;
1778 current_sctx.sc_version = save_sctx_version;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001779
1780 vim_free(expr);
1781
1782 if (p == NULL)
1783 return NULL;
1784 // Escape CSI in the result to be able to use the string as typeahead.
1785 res = vim_strsave_escape_csi(p);
1786 vim_free(p);
1787
1788 return res;
1789}
1790#endif
1791
1792/*
1793 * Copy "p" to allocated memory, escaping K_SPECIAL and CSI so that the result
1794 * can be put in the typeahead buffer.
1795 * Returns NULL when out of memory.
1796 */
1797 char_u *
Bram Moolenaar957cf672020-11-12 14:21:06 +01001798vim_strsave_escape_csi(char_u *p)
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001799{
1800 char_u *res;
1801 char_u *s, *d;
1802
1803 // Need a buffer to hold up to three times as much. Four in case of an
1804 // illegal utf-8 byte:
1805 // 0xc0 -> 0xc3 0x80 -> 0xc3 K_SPECIAL KS_SPECIAL KE_FILLER
1806 res = alloc(STRLEN(p) * 4 + 1);
1807 if (res != NULL)
1808 {
1809 d = res;
1810 for (s = p; *s != NUL; )
1811 {
zeertzjq2cd0f272022-10-04 20:14:28 +01001812 if ((s[0] == K_SPECIAL
1813#ifdef FEAT_GUI
1814 || (gui.in_use && s[0] == CSI)
1815#endif
1816 ) && s[1] != NUL && s[2] != NUL)
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001817 {
1818 // Copy special key unmodified.
1819 *d++ = *s++;
1820 *d++ = *s++;
1821 *d++ = *s++;
1822 }
1823 else
1824 {
1825 // Add character, possibly multi-byte to destination, escaping
1826 // CSI and K_SPECIAL. Be careful, it can be an illegal byte!
1827 d = add_char2buf(PTR2CHAR(s), d);
1828 s += MB_CPTR2LEN(s);
1829 }
1830 }
1831 *d = NUL;
1832 }
1833 return res;
1834}
1835
1836/*
1837 * Remove escaping from CSI and K_SPECIAL characters. Reverse of
1838 * vim_strsave_escape_csi(). Works in-place.
1839 */
1840 void
1841vim_unescape_csi(char_u *p)
1842{
1843 char_u *s = p, *d = p;
1844
1845 while (*s != NUL)
1846 {
1847 if (s[0] == K_SPECIAL && s[1] == KS_SPECIAL && s[2] == KE_FILLER)
1848 {
1849 *d++ = K_SPECIAL;
1850 s += 3;
1851 }
1852 else if ((s[0] == K_SPECIAL || s[0] == CSI)
1853 && s[1] == KS_EXTRA && s[2] == (int)KE_CSI)
1854 {
1855 *d++ = CSI;
1856 s += 3;
1857 }
1858 else
1859 *d++ = *s++;
1860 }
1861 *d = NUL;
1862}
1863
1864/*
1865 * Write map commands for the current mappings to an .exrc file.
1866 * Return FAIL on error, OK otherwise.
1867 */
1868 int
1869makemap(
1870 FILE *fd,
1871 buf_T *buf) // buffer for local mappings or NULL
1872{
1873 mapblock_T *mp;
1874 char_u c1, c2, c3;
1875 char_u *p;
1876 char *cmd;
1877 int abbr;
1878 int hash;
1879 int did_cpo = FALSE;
1880 int i;
1881
1882 validate_maphash();
1883
1884 // Do the loop twice: Once for mappings, once for abbreviations.
1885 // Then loop over all map hash lists.
1886 for (abbr = 0; abbr < 2; ++abbr)
1887 for (hash = 0; hash < 256; ++hash)
1888 {
1889 if (abbr)
1890 {
1891 if (hash > 0) // there is only one abbr list
1892 break;
1893 if (buf != NULL)
1894 mp = buf->b_first_abbr;
1895 else
1896 mp = first_abbr;
1897 }
1898 else
1899 {
1900 if (buf != NULL)
1901 mp = buf->b_maphash[hash];
1902 else
1903 mp = maphash[hash];
1904 }
1905
1906 for ( ; mp; mp = mp->m_next)
1907 {
1908 // skip script-local mappings
1909 if (mp->m_noremap == REMAP_SCRIPT)
1910 continue;
1911
1912 // skip mappings that contain a <SNR> (script-local thing),
1913 // they probably don't work when loaded again
1914 for (p = mp->m_str; *p != NUL; ++p)
1915 if (p[0] == K_SPECIAL && p[1] == KS_EXTRA
1916 && p[2] == (int)KE_SNR)
1917 break;
1918 if (*p != NUL)
1919 continue;
1920
1921 // It's possible to create a mapping and then ":unmap" certain
1922 // modes. We recreate this here by mapping the individual
1923 // modes, which requires up to three of them.
1924 c1 = NUL;
1925 c2 = NUL;
1926 c3 = NUL;
1927 if (abbr)
1928 cmd = "abbr";
1929 else
1930 cmd = "map";
1931 switch (mp->m_mode)
1932 {
Bram Moolenaar24959102022-05-07 20:01:16 +01001933 case MODE_NORMAL | MODE_VISUAL | MODE_SELECT
1934 | MODE_OP_PENDING:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001935 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01001936 case MODE_NORMAL:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001937 c1 = 'n';
1938 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01001939 case MODE_VISUAL:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001940 c1 = 'x';
1941 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01001942 case MODE_SELECT:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001943 c1 = 's';
1944 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01001945 case MODE_OP_PENDING:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001946 c1 = 'o';
1947 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01001948 case MODE_NORMAL | MODE_VISUAL:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001949 c1 = 'n';
1950 c2 = 'x';
1951 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01001952 case MODE_NORMAL | MODE_SELECT:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001953 c1 = 'n';
1954 c2 = 's';
1955 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01001956 case MODE_NORMAL | MODE_OP_PENDING:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001957 c1 = 'n';
1958 c2 = 'o';
1959 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01001960 case MODE_VISUAL | MODE_SELECT:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001961 c1 = 'v';
1962 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01001963 case MODE_VISUAL | MODE_OP_PENDING:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001964 c1 = 'x';
1965 c2 = 'o';
1966 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01001967 case MODE_SELECT | MODE_OP_PENDING:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001968 c1 = 's';
1969 c2 = 'o';
1970 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01001971 case MODE_NORMAL | MODE_VISUAL | MODE_SELECT:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001972 c1 = 'n';
1973 c2 = 'v';
1974 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01001975 case MODE_NORMAL | MODE_VISUAL | MODE_OP_PENDING:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001976 c1 = 'n';
1977 c2 = 'x';
1978 c3 = 'o';
1979 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01001980 case MODE_NORMAL | MODE_SELECT | MODE_OP_PENDING:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001981 c1 = 'n';
1982 c2 = 's';
1983 c3 = 'o';
1984 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01001985 case MODE_VISUAL | MODE_SELECT | MODE_OP_PENDING:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001986 c1 = 'v';
1987 c2 = 'o';
1988 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01001989 case MODE_CMDLINE | MODE_INSERT:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001990 if (!abbr)
1991 cmd = "map!";
1992 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01001993 case MODE_CMDLINE:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001994 c1 = 'c';
1995 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01001996 case MODE_INSERT:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001997 c1 = 'i';
1998 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01001999 case MODE_LANGMAP:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002000 c1 = 'l';
2001 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01002002 case MODE_TERMINAL:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002003 c1 = 't';
2004 break;
2005 default:
Bram Moolenaar6d057012021-12-31 18:49:43 +00002006 iemsg(_(e_makemap_illegal_mode));
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002007 return FAIL;
2008 }
2009 do // do this twice if c2 is set, 3 times with c3
2010 {
2011 // When outputting <> form, need to make sure that 'cpo'
2012 // is set to the Vim default.
2013 if (!did_cpo)
2014 {
2015 if (*mp->m_str == NUL) // will use <Nop>
2016 did_cpo = TRUE;
2017 else
2018 for (i = 0; i < 2; ++i)
2019 for (p = (i ? mp->m_str : mp->m_keys); *p; ++p)
2020 if (*p == K_SPECIAL || *p == NL)
2021 did_cpo = TRUE;
2022 if (did_cpo)
2023 {
2024 if (fprintf(fd, "let s:cpo_save=&cpo") < 0
2025 || put_eol(fd) < 0
2026 || fprintf(fd, "set cpo&vim") < 0
2027 || put_eol(fd) < 0)
2028 return FAIL;
2029 }
2030 }
2031 if (c1 && putc(c1, fd) < 0)
2032 return FAIL;
2033 if (mp->m_noremap != REMAP_YES && fprintf(fd, "nore") < 0)
2034 return FAIL;
2035 if (fputs(cmd, fd) < 0)
2036 return FAIL;
2037 if (buf != NULL && fputs(" <buffer>", fd) < 0)
2038 return FAIL;
2039 if (mp->m_nowait && fputs(" <nowait>", fd) < 0)
2040 return FAIL;
2041 if (mp->m_silent && fputs(" <silent>", fd) < 0)
2042 return FAIL;
2043#ifdef FEAT_EVAL
2044 if (mp->m_noremap == REMAP_SCRIPT
2045 && fputs("<script>", fd) < 0)
2046 return FAIL;
2047 if (mp->m_expr && fputs(" <expr>", fd) < 0)
2048 return FAIL;
2049#endif
2050
2051 if ( putc(' ', fd) < 0
2052 || put_escstr(fd, mp->m_keys, 0) == FAIL
2053 || putc(' ', fd) < 0
2054 || put_escstr(fd, mp->m_str, 1) == FAIL
2055 || put_eol(fd) < 0)
2056 return FAIL;
2057 c1 = c2;
2058 c2 = c3;
2059 c3 = NUL;
2060 } while (c1 != NUL);
2061 }
2062 }
2063
2064 if (did_cpo)
2065 if (fprintf(fd, "let &cpo=s:cpo_save") < 0
2066 || put_eol(fd) < 0
2067 || fprintf(fd, "unlet s:cpo_save") < 0
2068 || put_eol(fd) < 0)
2069 return FAIL;
2070 return OK;
2071}
2072
2073/*
2074 * write escape string to file
2075 * "what": 0 for :map lhs, 1 for :map rhs, 2 for :set
2076 *
2077 * return FAIL for failure, OK otherwise
2078 */
2079 int
2080put_escstr(FILE *fd, char_u *strstart, int what)
2081{
2082 char_u *str = strstart;
2083 int c;
2084 int modifiers;
2085
2086 // :map xx <Nop>
2087 if (*str == NUL && what == 1)
2088 {
2089 if (fprintf(fd, "<Nop>") < 0)
2090 return FAIL;
2091 return OK;
2092 }
2093
2094 for ( ; *str != NUL; ++str)
2095 {
2096 char_u *p;
2097
2098 // Check for a multi-byte character, which may contain escaped
2099 // K_SPECIAL and CSI bytes
2100 p = mb_unescape(&str);
2101 if (p != NULL)
2102 {
2103 while (*p != NUL)
2104 if (fputc(*p++, fd) < 0)
2105 return FAIL;
2106 --str;
2107 continue;
2108 }
2109
2110 c = *str;
2111 // Special key codes have to be translated to be able to make sense
2112 // when they are read back.
2113 if (c == K_SPECIAL && what != 2)
2114 {
Bram Moolenaar02c037a2020-08-30 19:26:45 +02002115 modifiers = 0;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002116 if (str[1] == KS_MODIFIER)
2117 {
2118 modifiers = str[2];
2119 str += 3;
2120 c = *str;
2121 }
2122 if (c == K_SPECIAL)
2123 {
2124 c = TO_SPECIAL(str[1], str[2]);
2125 str += 2;
2126 }
2127 if (IS_SPECIAL(c) || modifiers) // special key
2128 {
2129 if (fputs((char *)get_special_key_name(c, modifiers), fd) < 0)
2130 return FAIL;
2131 continue;
2132 }
2133 }
2134
2135 // A '\n' in a map command should be written as <NL>.
2136 // A '\n' in a set command should be written as \^V^J.
2137 if (c == NL)
2138 {
2139 if (what == 2)
2140 {
Bram Moolenaar424bcae2022-01-31 14:59:41 +00002141 if (fprintf(fd, "\\\026\n") < 0)
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002142 return FAIL;
2143 }
2144 else
2145 {
2146 if (fprintf(fd, "<NL>") < 0)
2147 return FAIL;
2148 }
2149 continue;
2150 }
2151
2152 // Some characters have to be escaped with CTRL-V to
2153 // prevent them from misinterpreted in DoOneCmd().
2154 // A space, Tab and '"' has to be escaped with a backslash to
2155 // prevent it to be misinterpreted in do_set().
2156 // A space has to be escaped with a CTRL-V when it's at the start of a
2157 // ":map" rhs.
2158 // A '<' has to be escaped with a CTRL-V to prevent it being
2159 // interpreted as the start of a special key name.
2160 // A space in the lhs of a :map needs a CTRL-V.
2161 if (what == 2 && (VIM_ISWHITE(c) || c == '"' || c == '\\'))
2162 {
2163 if (putc('\\', fd) < 0)
2164 return FAIL;
2165 }
2166 else if (c < ' ' || c > '~' || c == '|'
2167 || (what == 0 && c == ' ')
2168 || (what == 1 && str == strstart && c == ' ')
2169 || (what != 2 && c == '<'))
2170 {
2171 if (putc(Ctrl_V, fd) < 0)
2172 return FAIL;
2173 }
2174 if (putc(c, fd) < 0)
2175 return FAIL;
2176 }
2177 return OK;
2178}
2179
2180/*
2181 * Check all mappings for the presence of special key codes.
2182 * Used after ":set term=xxx".
2183 */
2184 void
2185check_map_keycodes(void)
2186{
2187 mapblock_T *mp;
2188 char_u *p;
2189 int i;
2190 char_u buf[3];
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002191 int abbr;
2192 int hash;
2193 buf_T *bp;
Bram Moolenaare31ee862020-01-07 20:59:34 +01002194 ESTACK_CHECK_DECLARATION
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002195
2196 validate_maphash();
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002197 // avoids giving error messages
2198 estack_push(ETYPE_INTERNAL, (char_u *)"mappings", 0);
Bram Moolenaare31ee862020-01-07 20:59:34 +01002199 ESTACK_CHECK_SETUP
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002200
Bram Moolenaar32aa1022019-11-02 22:54:41 +01002201 // Do this once for each buffer, and then once for global
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002202 // mappings/abbreviations with bp == NULL
2203 for (bp = firstbuf; ; bp = bp->b_next)
2204 {
2205 // Do the loop twice: Once for mappings, once for abbreviations.
2206 // Then loop over all map hash lists.
2207 for (abbr = 0; abbr <= 1; ++abbr)
2208 for (hash = 0; hash < 256; ++hash)
2209 {
2210 if (abbr)
2211 {
2212 if (hash) // there is only one abbr list
2213 break;
2214 if (bp != NULL)
2215 mp = bp->b_first_abbr;
2216 else
2217 mp = first_abbr;
2218 }
2219 else
2220 {
2221 if (bp != NULL)
2222 mp = bp->b_maphash[hash];
2223 else
2224 mp = maphash[hash];
2225 }
2226 for ( ; mp != NULL; mp = mp->m_next)
2227 {
2228 for (i = 0; i <= 1; ++i) // do this twice
2229 {
2230 if (i == 0)
2231 p = mp->m_keys; // once for the "from" part
2232 else
2233 p = mp->m_str; // and once for the "to" part
2234 while (*p)
2235 {
2236 if (*p == K_SPECIAL)
2237 {
2238 ++p;
2239 if (*p < 128) // for "normal" tcap entries
2240 {
2241 buf[0] = p[0];
2242 buf[1] = p[1];
2243 buf[2] = NUL;
2244 (void)add_termcap_entry(buf, FALSE);
2245 }
2246 ++p;
2247 }
2248 ++p;
2249 }
2250 }
2251 }
2252 }
2253 if (bp == NULL)
2254 break;
2255 }
Bram Moolenaare31ee862020-01-07 20:59:34 +01002256 ESTACK_CHECK_NOW
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002257 estack_pop();
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002258}
2259
2260#if defined(FEAT_EVAL) || defined(PROTO)
2261/*
2262 * Check the string "keys" against the lhs of all mappings.
2263 * Return pointer to rhs of mapping (mapblock->m_str).
2264 * NULL when no mapping found.
2265 */
2266 char_u *
2267check_map(
2268 char_u *keys,
2269 int mode,
2270 int exact, // require exact match
2271 int ign_mod, // ignore preceding modifier
2272 int abbr, // do abbreviations
2273 mapblock_T **mp_ptr, // return: pointer to mapblock or NULL
2274 int *local_ptr) // return: buffer-local mapping or NULL
2275{
2276 int hash;
2277 int len, minlen;
2278 mapblock_T *mp;
2279 char_u *s;
2280 int local;
2281
2282 validate_maphash();
2283
2284 len = (int)STRLEN(keys);
2285 for (local = 1; local >= 0; --local)
2286 // loop over all hash lists
2287 for (hash = 0; hash < 256; ++hash)
2288 {
2289 if (abbr)
2290 {
2291 if (hash > 0) // there is only one list.
2292 break;
2293 if (local)
2294 mp = curbuf->b_first_abbr;
2295 else
2296 mp = first_abbr;
2297 }
2298 else if (local)
2299 mp = curbuf->b_maphash[hash];
2300 else
2301 mp = maphash[hash];
2302 for ( ; mp != NULL; mp = mp->m_next)
2303 {
2304 // skip entries with wrong mode, wrong length and not matching
2305 // ones
2306 if ((mp->m_mode & mode) && (!exact || mp->m_keylen == len))
2307 {
2308 if (len > mp->m_keylen)
2309 minlen = mp->m_keylen;
2310 else
2311 minlen = len;
2312 s = mp->m_keys;
2313 if (ign_mod && s[0] == K_SPECIAL && s[1] == KS_MODIFIER
2314 && s[2] != NUL)
2315 {
2316 s += 3;
2317 if (len > mp->m_keylen - 3)
2318 minlen = mp->m_keylen - 3;
2319 }
2320 if (STRNCMP(s, keys, minlen) == 0)
2321 {
2322 if (mp_ptr != NULL)
2323 *mp_ptr = mp;
2324 if (local_ptr != NULL)
2325 *local_ptr = local;
2326 return mp->m_str;
2327 }
2328 }
2329 }
2330 }
2331
2332 return NULL;
2333}
2334
Ernie Rael659c2402022-04-24 18:40:28 +01002335/*
zeertzjqc207fd22022-06-29 10:37:40 +01002336 * "hasmapto()" function
2337 */
2338 void
2339f_hasmapto(typval_T *argvars, typval_T *rettv)
2340{
2341 char_u *name;
2342 char_u *mode;
2343 char_u buf[NUMBUFLEN];
2344 int abbr = FALSE;
2345
2346 if (in_vim9script()
2347 && (check_for_string_arg(argvars, 0) == FAIL
2348 || check_for_opt_string_arg(argvars, 1) == FAIL
2349 || (argvars[1].v_type != VAR_UNKNOWN
2350 && check_for_opt_bool_arg(argvars, 2) == FAIL)))
2351 return;
2352
2353 name = tv_get_string(&argvars[0]);
2354 if (argvars[1].v_type == VAR_UNKNOWN)
2355 mode = (char_u *)"nvo";
2356 else
2357 {
2358 mode = tv_get_string_buf(&argvars[1], buf);
2359 if (argvars[2].v_type != VAR_UNKNOWN)
2360 abbr = (int)tv_get_bool(&argvars[2]);
2361 }
2362
2363 if (map_to_exists(name, mode, abbr))
2364 rettv->vval.v_number = TRUE;
2365 else
2366 rettv->vval.v_number = FALSE;
2367}
2368
2369/*
Ernie Rael659c2402022-04-24 18:40:28 +01002370 * Fill in the empty dictionary with items as defined by maparg builtin.
2371 */
2372 static void
2373mapblock2dict(
2374 mapblock_T *mp,
2375 dict_T *dict,
2376 char_u *lhsrawalt, // may be NULL
Ernie Rael51d04d12022-05-04 15:40:22 +01002377 int buffer_local, // false if not buffer local mapping
2378 int abbr) // true if abbreviation
Ernie Rael659c2402022-04-24 18:40:28 +01002379{
zeertzjqcdc83932022-09-12 13:38:41 +01002380 char_u *lhs = str2special_save(mp->m_keys, TRUE, FALSE);
Ernie Rael659c2402022-04-24 18:40:28 +01002381 char_u *mapmode = map_mode_to_chars(mp->m_mode);
2382
2383 dict_add_string(dict, "lhs", lhs);
2384 vim_free(lhs);
2385 dict_add_string(dict, "lhsraw", mp->m_keys);
2386 if (lhsrawalt)
2387 // Also add the value for the simplified entry.
2388 dict_add_string(dict, "lhsrawalt", lhsrawalt);
2389 dict_add_string(dict, "rhs", mp->m_orig_str);
2390 dict_add_number(dict, "noremap", mp->m_noremap ? 1L : 0L);
2391 dict_add_number(dict, "script", mp->m_noremap == REMAP_SCRIPT
2392 ? 1L : 0L);
2393 dict_add_number(dict, "expr", mp->m_expr ? 1L : 0L);
2394 dict_add_number(dict, "silent", mp->m_silent ? 1L : 0L);
2395 dict_add_number(dict, "sid", (long)mp->m_script_ctx.sc_sid);
2396 dict_add_number(dict, "scriptversion",
2397 (long)mp->m_script_ctx.sc_version);
2398 dict_add_number(dict, "lnum", (long)mp->m_script_ctx.sc_lnum);
2399 dict_add_number(dict, "buffer", (long)buffer_local);
2400 dict_add_number(dict, "nowait", mp->m_nowait ? 1L : 0L);
2401 dict_add_string(dict, "mode", mapmode);
Ernie Rael51d04d12022-05-04 15:40:22 +01002402 dict_add_number(dict, "abbr", abbr ? 1L : 0L);
Ernie Raeld8f5f762022-05-10 17:50:39 +01002403 dict_add_number(dict, "mode_bits", mp->m_mode);
Ernie Rael659c2402022-04-24 18:40:28 +01002404
2405 vim_free(mapmode);
2406}
2407
Yegappan Lakshmanan4a155042021-07-30 21:32:45 +02002408 static void
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002409get_maparg(typval_T *argvars, typval_T *rettv, int exact)
2410{
2411 char_u *keys;
Bram Moolenaar9c652532020-05-24 13:10:18 +02002412 char_u *keys_simplified;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002413 char_u *which;
2414 char_u buf[NUMBUFLEN];
2415 char_u *keys_buf = NULL;
Bram Moolenaar9c652532020-05-24 13:10:18 +02002416 char_u *alt_keys_buf = NULL;
2417 int did_simplify = FALSE;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002418 char_u *rhs;
2419 int mode;
2420 int abbr = FALSE;
2421 int get_dict = FALSE;
zeertzjq2c8a7eb2022-04-26 21:36:21 +01002422 mapblock_T *mp = NULL;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002423 int buffer_local;
Bram Moolenaar9c652532020-05-24 13:10:18 +02002424 int flags = REPTERM_FROM_PART | REPTERM_DO_LT;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002425
2426 // return empty string for failure
2427 rettv->v_type = VAR_STRING;
2428 rettv->vval.v_string = NULL;
2429
2430 keys = tv_get_string(&argvars[0]);
2431 if (*keys == NUL)
2432 return;
2433
2434 if (argvars[1].v_type != VAR_UNKNOWN)
2435 {
2436 which = tv_get_string_buf_chk(&argvars[1], buf);
2437 if (argvars[2].v_type != VAR_UNKNOWN)
2438 {
Bram Moolenaar04d594b2020-09-02 22:25:35 +02002439 abbr = (int)tv_get_bool(&argvars[2]);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002440 if (argvars[3].v_type != VAR_UNKNOWN)
Bram Moolenaar04d594b2020-09-02 22:25:35 +02002441 get_dict = (int)tv_get_bool(&argvars[3]);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002442 }
2443 }
2444 else
2445 which = (char_u *)"";
2446 if (which == NULL)
2447 return;
2448
2449 mode = get_map_mode(&which, 0);
2450
Bram Moolenaar9c652532020-05-24 13:10:18 +02002451 keys_simplified = replace_termcodes(keys, &keys_buf, flags, &did_simplify);
2452 rhs = check_map(keys_simplified, mode, exact, FALSE, abbr,
2453 &mp, &buffer_local);
2454 if (did_simplify)
2455 {
2456 // When the lhs is being simplified the not-simplified keys are
Bram Moolenaar8e7d6222020-12-18 19:49:56 +01002457 // preferred for printing, like in do_map().
Bram Moolenaar9c652532020-05-24 13:10:18 +02002458 (void)replace_termcodes(keys, &alt_keys_buf,
2459 flags | REPTERM_NO_SIMPLIFY, NULL);
2460 rhs = check_map(alt_keys_buf, mode, exact, FALSE, abbr, &mp,
2461 &buffer_local);
2462 }
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002463
2464 if (!get_dict)
2465 {
2466 // Return a string.
2467 if (rhs != NULL)
2468 {
2469 if (*rhs == NUL)
2470 rettv->vval.v_string = vim_strsave((char_u *)"<Nop>");
2471 else
zeertzjqcdc83932022-09-12 13:38:41 +01002472 rettv->vval.v_string = str2special_save(rhs, FALSE, FALSE);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002473 }
2474
2475 }
Bram Moolenaar93a10962022-06-16 11:42:09 +01002476 else if (rettv_dict_alloc(rettv) == OK && rhs != NULL)
Ernie Rael659c2402022-04-24 18:40:28 +01002477 mapblock2dict(mp, rettv->vval.v_dict,
Ernie Rael51d04d12022-05-04 15:40:22 +01002478 did_simplify ? keys_simplified : NULL,
2479 buffer_local, abbr);
Bram Moolenaar9c652532020-05-24 13:10:18 +02002480
2481 vim_free(keys_buf);
2482 vim_free(alt_keys_buf);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002483}
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002484
2485/*
Ernie Rael09661202022-04-25 14:40:44 +01002486 * "maplist()" function
Ernie Rael659c2402022-04-24 18:40:28 +01002487 */
2488 void
Ernie Rael09661202022-04-25 14:40:44 +01002489f_maplist(typval_T *argvars UNUSED, typval_T *rettv)
Ernie Rael659c2402022-04-24 18:40:28 +01002490{
2491 dict_T *d;
2492 mapblock_T *mp;
2493 int buffer_local;
2494 char_u *keys_buf;
2495 int did_simplify;
2496 int hash;
2497 char_u *lhs;
2498 const int flags = REPTERM_FROM_PART | REPTERM_DO_LT;
Ernie Rael09661202022-04-25 14:40:44 +01002499 int abbr = FALSE;
2500
2501 if (in_vim9script() && check_for_opt_bool_arg(argvars, 0) == FAIL)
2502 return;
2503 if (argvars[0].v_type != VAR_UNKNOWN)
2504 abbr = tv_get_bool(&argvars[0]);
Ernie Rael659c2402022-04-24 18:40:28 +01002505
Bram Moolenaar93a10962022-06-16 11:42:09 +01002506 if (rettv_list_alloc(rettv) == FAIL)
Ernie Rael659c2402022-04-24 18:40:28 +01002507 return;
2508
2509 validate_maphash();
2510
2511 // Do it twice: once for global maps and once for local maps.
2512 for (buffer_local = 0; buffer_local <= 1; ++buffer_local)
2513 {
2514 for (hash = 0; hash < 256; ++hash)
2515 {
Ernie Rael09661202022-04-25 14:40:44 +01002516 if (abbr)
2517 {
2518 if (hash > 0) // there is only one abbr list
2519 break;
2520 if (buffer_local)
2521 mp = curbuf->b_first_abbr;
2522 else
2523 mp = first_abbr;
2524 }
2525 else if (buffer_local)
Ernie Rael659c2402022-04-24 18:40:28 +01002526 mp = curbuf->b_maphash[hash];
2527 else
2528 mp = maphash[hash];
2529 for (; mp; mp = mp->m_next)
2530 {
2531 if (mp->m_simplified)
2532 continue;
2533 if ((d = dict_alloc()) == NULL)
2534 return;
2535 if (list_append_dict(rettv->vval.v_list, d) == FAIL)
2536 return;
2537
2538 keys_buf = NULL;
2539 did_simplify = FALSE;
2540
zeertzjqcdc83932022-09-12 13:38:41 +01002541 lhs = str2special_save(mp->m_keys, TRUE, FALSE);
Ernie Rael659c2402022-04-24 18:40:28 +01002542 (void)replace_termcodes(lhs, &keys_buf, flags, &did_simplify);
2543 vim_free(lhs);
2544
2545 mapblock2dict(mp, d,
Ernie Rael51d04d12022-05-04 15:40:22 +01002546 did_simplify ? keys_buf : NULL,
2547 buffer_local, abbr);
Ernie Rael659c2402022-04-24 18:40:28 +01002548 vim_free(keys_buf);
2549 }
2550 }
2551 }
2552}
2553
2554/*
Yegappan Lakshmanan4a155042021-07-30 21:32:45 +02002555 * "maparg()" function
2556 */
2557 void
2558f_maparg(typval_T *argvars, typval_T *rettv)
2559{
2560 if (in_vim9script()
2561 && (check_for_string_arg(argvars, 0) == FAIL
2562 || check_for_opt_string_arg(argvars, 1) == FAIL
2563 || (argvars[1].v_type != VAR_UNKNOWN
2564 && (check_for_opt_bool_arg(argvars, 2) == FAIL
2565 || (argvars[2].v_type != VAR_UNKNOWN
2566 && check_for_opt_bool_arg(argvars, 3) == FAIL)))))
2567 return;
2568
2569 get_maparg(argvars, rettv, TRUE);
2570}
2571
2572/*
2573 * "mapcheck()" function
2574 */
2575 void
2576f_mapcheck(typval_T *argvars, typval_T *rettv)
2577{
2578 if (in_vim9script()
2579 && (check_for_string_arg(argvars, 0) == FAIL
2580 || check_for_opt_string_arg(argvars, 1) == FAIL
2581 || (argvars[1].v_type != VAR_UNKNOWN
2582 && check_for_opt_bool_arg(argvars, 2) == FAIL)))
2583 return;
2584
2585 get_maparg(argvars, rettv, FALSE);
2586}
2587
2588/*
Ernie Rael51d04d12022-05-04 15:40:22 +01002589 * Get the mapping mode from the mode string.
2590 * It may contain multiple characters, eg "nox", or "!", or ' '
2591 * Return 0 if there is an error.
2592 */
2593 static int
2594get_map_mode_string(char_u *mode_string, int abbr)
2595{
2596 char_u *p = mode_string;
2597 int mode = 0;
2598 int tmode;
2599 int modec;
Bram Moolenaar24959102022-05-07 20:01:16 +01002600 const int MASK_V = MODE_VISUAL | MODE_SELECT;
2601 const int MASK_MAP = MODE_VISUAL | MODE_SELECT | MODE_NORMAL
2602 | MODE_OP_PENDING;
2603 const int MASK_BANG = MODE_INSERT | MODE_CMDLINE;
Ernie Rael51d04d12022-05-04 15:40:22 +01002604
2605 if (*p == NUL)
2606 p = (char_u *)" "; // compatibility
2607 while ((modec = *p++))
2608 {
2609 switch (modec)
2610 {
Bram Moolenaar24959102022-05-07 20:01:16 +01002611 case 'i': tmode = MODE_INSERT; break;
2612 case 'l': tmode = MODE_LANGMAP; break;
2613 case 'c': tmode = MODE_CMDLINE; break;
2614 case 'n': tmode = MODE_NORMAL; break;
2615 case 'x': tmode = MODE_VISUAL; break;
2616 case 's': tmode = MODE_SELECT; break;
2617 case 'o': tmode = MODE_OP_PENDING; break;
2618 case 't': tmode = MODE_TERMINAL; break;
Ernie Rael51d04d12022-05-04 15:40:22 +01002619 case 'v': tmode = MASK_V; break;
2620 case '!': tmode = MASK_BANG; break;
2621 case ' ': tmode = MASK_MAP; break;
2622 default:
2623 return 0; // error, unknown mode character
2624 }
2625 mode |= tmode;
2626 }
2627 if ((abbr && (mode & ~MASK_BANG) != 0)
2628 || (!abbr && (mode & (mode-1)) != 0 // more than one bit set
2629 && (
2630 // false if multiple bits set in mode and mode is fully
2631 // contained in one mask
2632 !(((mode & MASK_BANG) != 0 && (mode & ~MASK_BANG) == 0)
2633 || ((mode & MASK_MAP) != 0 && (mode & ~MASK_MAP) == 0)))))
2634 return 0;
2635
2636 return mode;
2637}
2638
2639/*
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002640 * "mapset()" function
2641 */
2642 void
2643f_mapset(typval_T *argvars, typval_T *rettv UNUSED)
2644{
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002645 char_u *which;
2646 int mode;
2647 char_u buf[NUMBUFLEN];
2648 int is_abbr;
2649 dict_T *d;
2650 char_u *lhs;
Bram Moolenaar9c652532020-05-24 13:10:18 +02002651 char_u *lhsraw;
2652 char_u *lhsrawalt;
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002653 char_u *rhs;
Bram Moolenaarc94c1462020-05-22 20:01:06 +02002654 char_u *orig_rhs;
2655 char_u *arg_buf = NULL;
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002656 int noremap;
2657 int expr;
2658 int silent;
Bram Moolenaar7ba1e4d2021-04-24 13:12:38 +02002659 int buffer;
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002660 scid_T sid;
Bram Moolenaara9528b32022-01-18 20:51:35 +00002661 int scriptversion;
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002662 linenr_T lnum;
2663 mapblock_T **map_table = maphash;
2664 mapblock_T **abbr_table = &first_abbr;
2665 int nowait;
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002666 char_u *arg;
Ernie Rael51d04d12022-05-04 15:40:22 +01002667 int dict_only;
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002668
Ernie Rael51d04d12022-05-04 15:40:22 +01002669 // If first arg is a dict, then that's the only arg permitted.
2670 dict_only = argvars[0].v_type == VAR_DICT;
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02002671 if (in_vim9script()
Ernie Rael51d04d12022-05-04 15:40:22 +01002672 && (check_for_string_or_dict_arg(argvars, 0) == FAIL
2673 || (dict_only && check_for_unknown_arg(argvars, 1) == FAIL)
2674 || (!dict_only
2675 && (check_for_string_arg(argvars, 0) == FAIL
2676 || check_for_bool_arg(argvars, 1) == FAIL
2677 || check_for_dict_arg(argvars, 2) == FAIL))))
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02002678 return;
2679
Ernie Rael51d04d12022-05-04 15:40:22 +01002680 if (dict_only)
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002681 {
Ernie Rael51d04d12022-05-04 15:40:22 +01002682 d = argvars[0].vval.v_dict;
Bram Moolenaard61efa52022-07-23 09:52:04 +01002683 which = dict_get_string(d, "mode", FALSE);
2684 is_abbr = dict_get_bool(d, "abbr", -1);
Ernie Rael51d04d12022-05-04 15:40:22 +01002685 if (which == NULL || is_abbr < 0)
2686 {
2687 emsg(_(e_entries_missing_in_mapset_dict_argument));
2688 return;
2689 }
2690 }
2691 else
2692 {
2693 which = tv_get_string_buf_chk(&argvars[0], buf);
2694 if (which == NULL)
2695 return;
2696 is_abbr = (int)tv_get_bool(&argvars[1]);
2697
Yegappan Lakshmanan04c4c572022-08-30 19:48:24 +01002698 if (check_for_dict_arg(argvars, 2) == FAIL)
Ernie Rael51d04d12022-05-04 15:40:22 +01002699 return;
Ernie Rael51d04d12022-05-04 15:40:22 +01002700 d = argvars[2].vval.v_dict;
2701 }
2702 mode = get_map_mode_string(which, is_abbr);
2703 if (mode == 0)
2704 {
2705 semsg(_(e_illegal_map_mode_string_str), which);
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002706 return;
2707 }
Ernie Rael51d04d12022-05-04 15:40:22 +01002708
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002709
2710 // Get the values in the same order as above in get_maparg().
Bram Moolenaard61efa52022-07-23 09:52:04 +01002711 lhs = dict_get_string(d, "lhs", FALSE);
2712 lhsraw = dict_get_string(d, "lhsraw", FALSE);
2713 lhsrawalt = dict_get_string(d, "lhsrawalt", FALSE);
2714 rhs = dict_get_string(d, "rhs", FALSE);
Bram Moolenaar9c652532020-05-24 13:10:18 +02002715 if (lhs == NULL || lhsraw == NULL || rhs == NULL)
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002716 {
Bram Moolenaarb09feaa2022-01-02 20:20:45 +00002717 emsg(_(e_entries_missing_in_mapset_dict_argument));
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002718 return;
2719 }
Bram Moolenaarc94c1462020-05-22 20:01:06 +02002720 orig_rhs = rhs;
zeertzjq92a3d202022-08-31 16:40:17 +01002721 if (STRICMP(rhs, "<nop>") == 0) // "<Nop>" means nothing
2722 rhs = (char_u *)"";
2723 else
2724 rhs = replace_termcodes(rhs, &arg_buf,
Bram Moolenaarc94c1462020-05-22 20:01:06 +02002725 REPTERM_DO_LT | REPTERM_SPECIAL, NULL);
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002726
Bram Moolenaard61efa52022-07-23 09:52:04 +01002727 noremap = dict_get_number(d, "noremap") ? REMAP_NONE: 0;
2728 if (dict_get_number(d, "script") != 0)
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002729 noremap = REMAP_SCRIPT;
Bram Moolenaard61efa52022-07-23 09:52:04 +01002730 expr = dict_get_number(d, "expr") != 0;
2731 silent = dict_get_number(d, "silent") != 0;
2732 sid = dict_get_number(d, "sid");
2733 scriptversion = dict_get_number(d, "scriptversion");
2734 lnum = dict_get_number(d, "lnum");
2735 buffer = dict_get_number(d, "buffer");
2736 nowait = dict_get_number(d, "nowait") != 0;
Bram Moolenaar7ba1e4d2021-04-24 13:12:38 +02002737 // mode from the dict is not used
2738
2739 if (buffer)
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002740 {
2741 map_table = curbuf->b_maphash;
2742 abbr_table = &curbuf->b_first_abbr;
2743 }
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002744
2745 // Delete any existing mapping for this lhs and mode.
Bram Moolenaar7ba1e4d2021-04-24 13:12:38 +02002746 if (buffer)
2747 {
2748 arg = alloc(STRLEN(lhs) + STRLEN("<buffer>") + 1);
2749 if (arg == NULL)
2750 return;
2751 STRCPY(arg, "<buffer>");
2752 STRCPY(arg + 8, lhs);
2753 }
2754 else
2755 {
2756 arg = vim_strsave(lhs);
2757 if (arg == NULL)
2758 return;
2759 }
zeertzjq44068e92022-06-16 11:14:55 +01002760 do_map(MAPTYPE_UNMAP, arg, mode, is_abbr);
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002761 vim_free(arg);
2762
Bram Moolenaar9c652532020-05-24 13:10:18 +02002763 (void)map_add(map_table, abbr_table, lhsraw, rhs, orig_rhs, noremap,
Bram Moolenaara9528b32022-01-18 20:51:35 +00002764 nowait, silent, mode, is_abbr, expr, sid, scriptversion, lnum, 0);
Bram Moolenaar9c652532020-05-24 13:10:18 +02002765 if (lhsrawalt != NULL)
2766 (void)map_add(map_table, abbr_table, lhsrawalt, rhs, orig_rhs, noremap,
Bram Moolenaara9528b32022-01-18 20:51:35 +00002767 nowait, silent, mode, is_abbr, expr, sid, scriptversion,
2768 lnum, 1);
Bram Moolenaarc94c1462020-05-22 20:01:06 +02002769 vim_free(arg_buf);
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002770}
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002771#endif
2772
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002773
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002774#if defined(MSWIN) || defined(MACOS_X)
2775
Bram Moolenaar24959102022-05-07 20:01:16 +01002776# define VIS_SEL (MODE_VISUAL | MODE_SELECT) // abbreviation
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002777
2778/*
2779 * Default mappings for some often used keys.
2780 */
2781struct initmap
2782{
2783 char_u *arg;
2784 int mode;
2785};
2786
2787# ifdef FEAT_GUI_MSWIN
2788// Use the Windows (CUA) keybindings. (GUI)
2789static struct initmap initmappings[] =
2790{
2791 // paste, copy and cut
Bram Moolenaar24959102022-05-07 20:01:16 +01002792 {(char_u *)"<S-Insert> \"*P", MODE_NORMAL},
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002793 {(char_u *)"<S-Insert> \"-d\"*P", VIS_SEL},
Bram Moolenaar24959102022-05-07 20:01:16 +01002794 {(char_u *)"<S-Insert> <C-R><C-O>*", MODE_INSERT | MODE_CMDLINE},
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002795 {(char_u *)"<C-Insert> \"*y", VIS_SEL},
2796 {(char_u *)"<S-Del> \"*d", VIS_SEL},
2797 {(char_u *)"<C-Del> \"*d", VIS_SEL},
2798 {(char_u *)"<C-X> \"*d", VIS_SEL},
2799 // Missing: CTRL-C (cancel) and CTRL-V (block selection)
2800};
2801# endif
2802
2803# if defined(MSWIN) && (!defined(FEAT_GUI) || defined(VIMDLL))
2804// Use the Windows (CUA) keybindings. (Console)
2805static struct initmap cinitmappings[] =
2806{
Bram Moolenaar24959102022-05-07 20:01:16 +01002807 {(char_u *)"\316w <C-Home>", MODE_NORMAL | VIS_SEL},
2808 {(char_u *)"\316w <C-Home>", MODE_INSERT | MODE_CMDLINE},
2809 {(char_u *)"\316u <C-End>", MODE_NORMAL | VIS_SEL},
2810 {(char_u *)"\316u <C-End>", MODE_INSERT | MODE_CMDLINE},
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002811
2812 // paste, copy and cut
2813# ifdef FEAT_CLIPBOARD
Bram Moolenaar24959102022-05-07 20:01:16 +01002814 {(char_u *)"\316\324 \"*P", MODE_NORMAL}, // SHIFT-Insert is "*P
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002815 {(char_u *)"\316\324 \"-d\"*P", VIS_SEL}, // SHIFT-Insert is "-d"*P
Bram Moolenaar24959102022-05-07 20:01:16 +01002816 {(char_u *)"\316\324 \022\017*", MODE_INSERT}, // SHIFT-Insert is ^R^O*
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002817 {(char_u *)"\316\325 \"*y", VIS_SEL}, // CTRL-Insert is "*y
2818 {(char_u *)"\316\327 \"*d", VIS_SEL}, // SHIFT-Del is "*d
2819 {(char_u *)"\316\330 \"*d", VIS_SEL}, // CTRL-Del is "*d
2820 {(char_u *)"\030 \"*d", VIS_SEL}, // CTRL-X is "*d
2821# else
Bram Moolenaar24959102022-05-07 20:01:16 +01002822 {(char_u *)"\316\324 P", MODE_NORMAL}, // SHIFT-Insert is P
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002823 {(char_u *)"\316\324 \"-dP", VIS_SEL}, // SHIFT-Insert is "-dP
Bram Moolenaar24959102022-05-07 20:01:16 +01002824 {(char_u *)"\316\324 \022\017\"", MODE_INSERT}, // SHIFT-Insert is ^R^O"
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002825 {(char_u *)"\316\325 y", VIS_SEL}, // CTRL-Insert is y
2826 {(char_u *)"\316\327 d", VIS_SEL}, // SHIFT-Del is d
2827 {(char_u *)"\316\330 d", VIS_SEL}, // CTRL-Del is d
2828# endif
2829};
2830# endif
2831
2832# if defined(MACOS_X)
2833static struct initmap initmappings[] =
2834{
2835 // Use the Standard MacOS binding.
2836 // paste, copy and cut
Bram Moolenaar24959102022-05-07 20:01:16 +01002837 {(char_u *)"<D-v> \"*P", MODE_NORMAL},
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002838 {(char_u *)"<D-v> \"-d\"*P", VIS_SEL},
Bram Moolenaar24959102022-05-07 20:01:16 +01002839 {(char_u *)"<D-v> <C-R>*", MODE_INSERT | MODE_CMDLINE},
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002840 {(char_u *)"<D-c> \"*y", VIS_SEL},
2841 {(char_u *)"<D-x> \"*d", VIS_SEL},
2842 {(char_u *)"<Backspace> \"-d", VIS_SEL},
2843};
2844# endif
2845
2846# undef VIS_SEL
2847#endif
2848
2849/*
2850 * Set up default mappings.
2851 */
2852 void
2853init_mappings(void)
2854{
2855#if defined(MSWIN) || defined(MACOS_X)
2856 int i;
2857
2858# if defined(MSWIN) && (!defined(FEAT_GUI_MSWIN) || defined(VIMDLL))
2859# ifdef VIMDLL
2860 if (!gui.starting)
2861# endif
2862 {
K.Takataeeec2542021-06-02 13:28:16 +02002863 for (i = 0; i < (int)ARRAY_LENGTH(cinitmappings); ++i)
zeertzjq44068e92022-06-16 11:14:55 +01002864 add_map(cinitmappings[i].arg, cinitmappings[i].mode, FALSE);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002865 }
2866# endif
2867# if defined(FEAT_GUI_MSWIN) || defined(MACOS_X)
K.Takataeeec2542021-06-02 13:28:16 +02002868 for (i = 0; i < (int)ARRAY_LENGTH(initmappings); ++i)
zeertzjq44068e92022-06-16 11:14:55 +01002869 add_map(initmappings[i].arg, initmappings[i].mode, FALSE);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002870# endif
2871#endif
2872}
2873
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002874/*
2875 * Add a mapping "map" for mode "mode".
zeertzjq44068e92022-06-16 11:14:55 +01002876 * When "nore" is TRUE use MAPTYPE_NOREMAP.
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002877 * Need to put string in allocated memory, because do_map() will modify it.
2878 */
2879 void
zeertzjq44068e92022-06-16 11:14:55 +01002880add_map(char_u *map, int mode, int nore)
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002881{
2882 char_u *s;
2883 char_u *cpo_save = p_cpo;
2884
Bram Moolenaare5a2dc82021-01-03 19:52:05 +01002885 p_cpo = empty_option; // Allow <> notation
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002886 s = vim_strsave(map);
2887 if (s != NULL)
2888 {
zeertzjq44068e92022-06-16 11:14:55 +01002889 (void)do_map(nore ? MAPTYPE_NOREMAP : MAPTYPE_MAP, s, mode, FALSE);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002890 vim_free(s);
2891 }
2892 p_cpo = cpo_save;
2893}
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002894
Bram Moolenaare677df82019-09-02 22:31:11 +02002895#if defined(FEAT_LANGMAP) || defined(PROTO)
2896/*
2897 * Any character has an equivalent 'langmap' character. This is used for
2898 * keyboards that have a special language mode that sends characters above
2899 * 128 (although other characters can be translated too). The "to" field is a
2900 * Vim command character. This avoids having to switch the keyboard back to
2901 * ASCII mode when leaving Insert mode.
2902 *
2903 * langmap_mapchar[] maps any of 256 chars to an ASCII char used for Vim
2904 * commands.
2905 * langmap_mapga.ga_data is a sorted table of langmap_entry_T. This does the
2906 * same as langmap_mapchar[] for characters >= 256.
2907 *
2908 * Use growarray for 'langmap' chars >= 256
2909 */
2910typedef struct
2911{
2912 int from;
2913 int to;
2914} langmap_entry_T;
2915
2916static garray_T langmap_mapga;
2917
2918/*
2919 * Search for an entry in "langmap_mapga" for "from". If found set the "to"
2920 * field. If not found insert a new entry at the appropriate location.
2921 */
2922 static void
2923langmap_set_entry(int from, int to)
2924{
2925 langmap_entry_T *entries = (langmap_entry_T *)(langmap_mapga.ga_data);
2926 int a = 0;
2927 int b = langmap_mapga.ga_len;
2928
2929 // Do a binary search for an existing entry.
2930 while (a != b)
2931 {
2932 int i = (a + b) / 2;
2933 int d = entries[i].from - from;
2934
2935 if (d == 0)
2936 {
2937 entries[i].to = to;
2938 return;
2939 }
2940 if (d < 0)
2941 a = i + 1;
2942 else
2943 b = i;
2944 }
2945
2946 if (ga_grow(&langmap_mapga, 1) != OK)
2947 return; // out of memory
2948
2949 // insert new entry at position "a"
2950 entries = (langmap_entry_T *)(langmap_mapga.ga_data) + a;
2951 mch_memmove(entries + 1, entries,
2952 (langmap_mapga.ga_len - a) * sizeof(langmap_entry_T));
2953 ++langmap_mapga.ga_len;
2954 entries[0].from = from;
2955 entries[0].to = to;
2956}
2957
2958/*
2959 * Apply 'langmap' to multi-byte character "c" and return the result.
2960 */
2961 int
2962langmap_adjust_mb(int c)
2963{
2964 langmap_entry_T *entries = (langmap_entry_T *)(langmap_mapga.ga_data);
2965 int a = 0;
2966 int b = langmap_mapga.ga_len;
2967
2968 while (a != b)
2969 {
2970 int i = (a + b) / 2;
2971 int d = entries[i].from - c;
2972
2973 if (d == 0)
2974 return entries[i].to; // found matching entry
2975 if (d < 0)
2976 a = i + 1;
2977 else
2978 b = i;
2979 }
2980 return c; // no entry found, return "c" unmodified
2981}
2982
2983 void
2984langmap_init(void)
2985{
2986 int i;
2987
2988 for (i = 0; i < 256; i++)
2989 langmap_mapchar[i] = i; // we init with a one-to-one map
2990 ga_init2(&langmap_mapga, sizeof(langmap_entry_T), 8);
2991}
2992
2993/*
2994 * Called when langmap option is set; the language map can be
2995 * changed at any time!
2996 */
2997 void
2998langmap_set(void)
2999{
3000 char_u *p;
3001 char_u *p2;
3002 int from, to;
3003
3004 ga_clear(&langmap_mapga); // clear the previous map first
3005 langmap_init(); // back to one-to-one map
3006
3007 for (p = p_langmap; p[0] != NUL; )
3008 {
3009 for (p2 = p; p2[0] != NUL && p2[0] != ',' && p2[0] != ';';
3010 MB_PTR_ADV(p2))
3011 {
3012 if (p2[0] == '\\' && p2[1] != NUL)
3013 ++p2;
3014 }
3015 if (p2[0] == ';')
3016 ++p2; // abcd;ABCD form, p2 points to A
3017 else
3018 p2 = NULL; // aAbBcCdD form, p2 is NULL
3019 while (p[0])
3020 {
3021 if (p[0] == ',')
3022 {
3023 ++p;
3024 break;
3025 }
3026 if (p[0] == '\\' && p[1] != NUL)
3027 ++p;
3028 from = (*mb_ptr2char)(p);
3029 to = NUL;
3030 if (p2 == NULL)
3031 {
3032 MB_PTR_ADV(p);
3033 if (p[0] != ',')
3034 {
3035 if (p[0] == '\\')
3036 ++p;
3037 to = (*mb_ptr2char)(p);
3038 }
3039 }
3040 else
3041 {
3042 if (p2[0] != ',')
3043 {
3044 if (p2[0] == '\\')
3045 ++p2;
3046 to = (*mb_ptr2char)(p2);
3047 }
3048 }
3049 if (to == NUL)
3050 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00003051 semsg(_(e_langmap_matching_character_missing_for_str),
Bram Moolenaare677df82019-09-02 22:31:11 +02003052 transchar(from));
3053 return;
3054 }
3055
3056 if (from >= 256)
3057 langmap_set_entry(from, to);
3058 else
3059 langmap_mapchar[from & 255] = to;
3060
3061 // Advance to next pair
3062 MB_PTR_ADV(p);
3063 if (p2 != NULL)
3064 {
3065 MB_PTR_ADV(p2);
3066 if (*p == ';')
3067 {
3068 p = p2;
3069 if (p[0] != NUL)
3070 {
3071 if (p[0] != ',')
3072 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00003073 semsg(_(e_langmap_extra_characters_after_semicolon_str), p);
Bram Moolenaare677df82019-09-02 22:31:11 +02003074 return;
3075 }
3076 ++p;
3077 }
3078 break;
3079 }
3080 }
3081 }
3082 }
3083}
3084#endif
3085
Bram Moolenaarb66bab32019-08-01 14:28:24 +02003086 static void
3087do_exmap(exarg_T *eap, int isabbrev)
3088{
3089 int mode;
3090 char_u *cmdp;
3091
3092 cmdp = eap->cmd;
3093 mode = get_map_mode(&cmdp, eap->forceit || isabbrev);
3094
zeertzjq44068e92022-06-16 11:14:55 +01003095 switch (do_map(*cmdp == 'n' ? MAPTYPE_NOREMAP
3096 : *cmdp == 'u' ? MAPTYPE_UNMAP : MAPTYPE_MAP,
Bram Moolenaarb66bab32019-08-01 14:28:24 +02003097 eap->arg, mode, isabbrev))
3098 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00003099 case 1: emsg(_(e_invalid_argument));
Bram Moolenaarb66bab32019-08-01 14:28:24 +02003100 break;
Bram Moolenaare29a27f2021-07-20 21:07:36 +02003101 case 2: emsg((isabbrev ? _(e_no_such_abbreviation)
3102 : _(e_no_such_mapping)));
Bram Moolenaarb66bab32019-08-01 14:28:24 +02003103 break;
3104 }
3105}
3106
3107/*
3108 * ":abbreviate" and friends.
3109 */
3110 void
3111ex_abbreviate(exarg_T *eap)
3112{
3113 do_exmap(eap, TRUE); // almost the same as mapping
3114}
3115
3116/*
3117 * ":map" and friends.
3118 */
3119 void
3120ex_map(exarg_T *eap)
3121{
3122 // If we are sourcing .exrc or .vimrc in current directory we
3123 // print the mappings for security reasons.
3124 if (secure)
3125 {
3126 secure = 2;
3127 msg_outtrans(eap->cmd);
3128 msg_putchar('\n');
3129 }
3130 do_exmap(eap, FALSE);
3131}
3132
3133/*
3134 * ":unmap" and friends.
3135 */
3136 void
3137ex_unmap(exarg_T *eap)
3138{
3139 do_exmap(eap, FALSE);
3140}
3141
3142/*
3143 * ":mapclear" and friends.
3144 */
3145 void
3146ex_mapclear(exarg_T *eap)
3147{
3148 map_clear(eap->cmd, eap->arg, eap->forceit, FALSE);
3149}
3150
3151/*
3152 * ":abclear" and friends.
3153 */
3154 void
3155ex_abclear(exarg_T *eap)
3156{
3157 map_clear(eap->cmd, eap->arg, TRUE, TRUE);
3158}