blob: 4bdd07e7778a8c7b57ba1a4fc72d97d63e0d4f57 [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{
Yegappan Lakshmanane8575982023-01-14 12:32:28 +000070 if (maphash_valid)
71 return;
72
73 CLEAR_FIELD(maphash);
74 maphash_valid = TRUE;
Bram Moolenaarb66bab32019-08-01 14:28:24 +020075}
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 {
zeertzjq997b8a02023-02-19 21:00:31 +0000183 msg_putchar(' '); // pad with blanks
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200184 ++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,
zeertzjqbfc7cbd2023-04-07 22:09:46 +0100230 scid_T sid, // 0 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;
zeertzjqbfc7cbd2023-04-07 22:09:46 +0100269 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 Moolenaar63a2e362022-11-23 20:20:18 +0000315 if (p_verbose > 0 && keyround == 1)
316 {
317 if (seenModifyOtherKeys)
Bram Moolenaarc255b782022-11-26 19:16:48 +0000318 msg_puts(_("Seen modifyOtherKeys: true\n"));
319
320 if (modify_otherkeys_state != MOKS_INITIAL)
321 {
322 char *name = _("Unknown");
323 switch (modify_otherkeys_state)
324 {
325 case MOKS_INITIAL: break;
326 case MOKS_OFF: name = _("Off"); break;
327 case MOKS_ENABLED: name = _("On"); break;
328 case MOKS_DISABLED: name = _("Disabled"); break;
Bram Moolenaar9d1184c2022-12-16 18:33:20 +0000329 case MOKS_AFTER_T_TE: name = _("Cleared"); break;
Bram Moolenaarc255b782022-11-26 19:16:48 +0000330 }
331
332 char buf[200];
333 vim_snprintf(buf, sizeof(buf),
334 _("modifyOtherKeys detected: %s\n"), name);
335 msg_puts(buf);
336 }
337
Bram Moolenaar63a2e362022-11-23 20:20:18 +0000338 if (kitty_protocol_state != KKPS_INITIAL)
339 {
340 char *name = _("Unknown");
341 switch (kitty_protocol_state)
342 {
343 case KKPS_INITIAL: break;
344 case KKPS_OFF: name = _("Off"); break;
345 case KKPS_ENABLED: name = _("On"); break;
346 case KKPS_DISABLED: name = _("Disabled"); break;
Bram Moolenaar9d1184c2022-12-16 18:33:20 +0000347 case KKPS_AFTER_T_TE: name = _("Cleared"); break;
Bram Moolenaar63a2e362022-11-23 20:20:18 +0000348 }
349
350 char buf[200];
Bram Moolenaarc255b782022-11-26 19:16:48 +0000351 vim_snprintf(buf, sizeof(buf),
352 _("Kitty keyboard protocol: %s\n"), name);
Bram Moolenaar63a2e362022-11-23 20:20:18 +0000353 msg_puts(buf);
354 }
355 }
Bram Moolenaar9f62ea02022-10-19 13:07:03 +0100356
357 // need to loop over all global hash lists
358 for (int hash = 0; hash < 256 && !got_int; ++hash)
359 {
360 mapblock_T *mp;
361
362 if (abbrev)
363 {
364 if (hash != 0) // there is only one abbreviation list
365 break;
366 mp = curbuf->b_first_abbr;
367 }
368 else
369 mp = curbuf->b_maphash[hash];
370 for ( ; mp != NULL && !got_int; mp = mp->m_next)
371 {
372 // check entries with the same mode
373 if (!mp->m_simplified && (mp->m_mode & mode) != 0)
374 {
375 if (!haskey) // show all entries
376 {
377 showmap(mp, TRUE);
378 *did_local = TRUE;
379 }
380 else
381 {
382 int n = mp->m_keylen;
383 if (STRNCMP(mp->m_keys, keys,
384 (size_t)(n < keys_len ? n : keys_len)) == 0)
385 {
386 showmap(mp, TRUE);
387 *did_local = TRUE;
388 }
389 }
390 }
391 }
392 }
Bram Moolenaarbf533e42022-11-13 20:43:19 +0000393
394 --map_locked;
Bram Moolenaar9f62ea02022-10-19 13:07:03 +0100395}
396
397/*
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200398 * map[!] : show all key mappings
399 * map[!] {lhs} : show key mapping for {lhs}
400 * map[!] {lhs} {rhs} : set key mapping for {lhs} to {rhs}
401 * noremap[!] {lhs} {rhs} : same, but no remapping for {rhs}
402 * unmap[!] {lhs} : remove key mapping for {lhs}
403 * abbr : show all abbreviations
404 * abbr {lhs} : show abbreviations for {lhs}
405 * abbr {lhs} {rhs} : set abbreviation for {lhs} to {rhs}
406 * noreabbr {lhs} {rhs} : same, but no remapping for {rhs}
407 * unabbr {lhs} : remove abbreviation for {lhs}
408 *
zeertzjq44068e92022-06-16 11:14:55 +0100409 * maptype: MAPTYPE_MAP for :map
410 * MAPTYPE_UNMAP for :unmap
411 * MAPTYPE_NOREMAP for noremap
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200412 *
413 * arg is pointer to any arguments. Note: arg cannot be a read-only string,
414 * it will be modified.
415 *
Bram Moolenaar24959102022-05-07 20:01:16 +0100416 * for :map mode is MODE_NORMAL | MODE_VISUAL | MODE_SELECT | MODE_OP_PENDING
417 * for :map! mode is MODE_INSERT | MODE_CMDLINE
418 * for :cmap mode is MODE_CMDLINE
419 * for :imap mode is MODE_INSERT
420 * for :lmap mode is MODE_LANGMAP
421 * for :nmap mode is MODE_NORMAL
422 * for :vmap mode is MODE_VISUAL | MODE_SELECT
423 * for :xmap mode is MODE_VISUAL
424 * for :smap mode is MODE_SELECT
425 * for :omap mode is MODE_OP_PENDING
426 * for :tmap mode is MODE_TERMINAL
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200427 *
Bram Moolenaar24959102022-05-07 20:01:16 +0100428 * for :abbr mode is MODE_INSERT | MODE_CMDLINE
429 * for :iabbr mode is MODE_INSERT
430 * for :cabbr mode is MODE_CMDLINE
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200431 *
432 * Return 0 for success
433 * 1 for invalid arguments
434 * 2 for no match
435 * 4 for out of mem
436 * 5 for entry not unique
437 */
438 int
439do_map(
440 int maptype,
441 char_u *arg,
442 int mode,
443 int abbrev) // not a mapping but an abbreviation
444{
445 char_u *keys;
446 mapblock_T *mp, **mpp;
447 char_u *rhs;
448 char_u *p;
449 int n;
450 int len = 0; // init for GCC
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200451 int hasarg;
452 int haskey;
Bram Moolenaar459fd782019-10-13 16:43:39 +0200453 int do_print;
454 int keyround;
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200455 char_u *keys_buf = NULL;
Bram Moolenaar459fd782019-10-13 16:43:39 +0200456 char_u *alt_keys_buf = NULL;
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200457 char_u *arg_buf = NULL;
458 int retval = 0;
459 int do_backslash;
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200460 mapblock_T **abbr_table;
461 mapblock_T **map_table;
462 int unique = FALSE;
463 int nowait = FALSE;
464 int silent = FALSE;
465 int special = FALSE;
466#ifdef FEAT_EVAL
467 int expr = FALSE;
468#endif
Bram Moolenaar459fd782019-10-13 16:43:39 +0200469 int did_simplify = FALSE;
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200470 int noremap;
471 char_u *orig_rhs;
472
473 keys = arg;
474 map_table = maphash;
475 abbr_table = &first_abbr;
476
477 // For ":noremap" don't remap, otherwise do remap.
zeertzjq44068e92022-06-16 11:14:55 +0100478 if (maptype == MAPTYPE_NOREMAP)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200479 noremap = REMAP_NONE;
480 else
481 noremap = REMAP_YES;
482
483 // Accept <buffer>, <nowait>, <silent>, <expr> <script> and <unique> in
484 // any order.
485 for (;;)
486 {
487 // Check for "<buffer>": mapping local to buffer.
488 if (STRNCMP(keys, "<buffer>", 8) == 0)
489 {
490 keys = skipwhite(keys + 8);
491 map_table = curbuf->b_maphash;
492 abbr_table = &curbuf->b_first_abbr;
493 continue;
494 }
495
496 // Check for "<nowait>": don't wait for more characters.
497 if (STRNCMP(keys, "<nowait>", 8) == 0)
498 {
499 keys = skipwhite(keys + 8);
500 nowait = TRUE;
501 continue;
502 }
503
504 // Check for "<silent>": don't echo commands.
505 if (STRNCMP(keys, "<silent>", 8) == 0)
506 {
507 keys = skipwhite(keys + 8);
508 silent = TRUE;
509 continue;
510 }
511
512 // Check for "<special>": accept special keys in <>
513 if (STRNCMP(keys, "<special>", 9) == 0)
514 {
515 keys = skipwhite(keys + 9);
516 special = TRUE;
517 continue;
518 }
519
520#ifdef FEAT_EVAL
521 // Check for "<script>": remap script-local mappings only
522 if (STRNCMP(keys, "<script>", 8) == 0)
523 {
524 keys = skipwhite(keys + 8);
525 noremap = REMAP_SCRIPT;
526 continue;
527 }
528
529 // Check for "<expr>": {rhs} is an expression.
530 if (STRNCMP(keys, "<expr>", 6) == 0)
531 {
532 keys = skipwhite(keys + 6);
533 expr = TRUE;
534 continue;
535 }
536#endif
537 // Check for "<unique>": don't overwrite an existing mapping.
538 if (STRNCMP(keys, "<unique>", 8) == 0)
539 {
540 keys = skipwhite(keys + 8);
541 unique = TRUE;
542 continue;
543 }
544 break;
545 }
546
547 validate_maphash();
548
549 // Find end of keys and skip CTRL-Vs (and backslashes) in it.
550 // Accept backslash like CTRL-V when 'cpoptions' does not contain 'B'.
551 // with :unmap white space is included in the keys, no argument possible.
552 p = keys;
553 do_backslash = (vim_strchr(p_cpo, CPO_BSLASH) == NULL);
zeertzjq44068e92022-06-16 11:14:55 +0100554 while (*p && (maptype == MAPTYPE_UNMAP || !VIM_ISWHITE(*p)))
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200555 {
556 if ((p[0] == Ctrl_V || (do_backslash && p[0] == '\\')) &&
557 p[1] != NUL)
558 ++p; // skip CTRL-V or backslash
559 ++p;
560 }
561 if (*p != NUL)
562 *p++ = NUL;
563
564 p = skipwhite(p);
565 rhs = p;
566 hasarg = (*rhs != NUL);
567 haskey = (*keys != NUL);
zeertzjq44068e92022-06-16 11:14:55 +0100568 do_print = !haskey || (maptype != MAPTYPE_UNMAP && !hasarg);
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200569
570 // check for :unmap without argument
zeertzjq44068e92022-06-16 11:14:55 +0100571 if (maptype == MAPTYPE_UNMAP && !haskey)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200572 {
573 retval = 1;
574 goto theend;
575 }
576
577 // If mapping has been given as ^V<C_UP> say, then replace the term codes
578 // with the appropriate two bytes. If it is a shifted special key, unshift
579 // it too, giving another two bytes.
580 // replace_termcodes() may move the result to allocated memory, which
581 // needs to be freed later (*keys_buf and *arg_buf).
582 // replace_termcodes() also removes CTRL-Vs and sometimes backslashes.
Bram Moolenaar459fd782019-10-13 16:43:39 +0200583 // If something like <C-H> is simplified to 0x08 then mark it as simplified
Yegappan Lakshmanane8575982023-01-14 12:32:28 +0000584 // and also add an entry with a modifier, which will work when using a key
585 // protocol.
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200586 if (haskey)
Bram Moolenaar459fd782019-10-13 16:43:39 +0200587 {
588 char_u *new_keys;
589 int flags = REPTERM_FROM_PART | REPTERM_DO_LT;
590
591 if (special)
592 flags |= REPTERM_SPECIAL;
593 new_keys = replace_termcodes(keys, &keys_buf, flags, &did_simplify);
594 if (did_simplify)
595 (void)replace_termcodes(keys, &alt_keys_buf,
596 flags | REPTERM_NO_SIMPLIFY, NULL);
597 keys = new_keys;
598 }
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200599 orig_rhs = rhs;
600 if (hasarg)
601 {
602 if (STRICMP(rhs, "<nop>") == 0) // "<Nop>" means nothing
603 rhs = (char_u *)"";
604 else
Bram Moolenaar459fd782019-10-13 16:43:39 +0200605 rhs = replace_termcodes(rhs, &arg_buf,
606 REPTERM_DO_LT | (special ? REPTERM_SPECIAL : 0), NULL);
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200607 }
608
Bram Moolenaar459fd782019-10-13 16:43:39 +0200609 /*
610 * The following is done twice if we have two versions of keys:
611 * "alt_keys_buf" is not NULL.
612 */
613 for (keyround = 1; keyround <= 2; ++keyround)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200614 {
Bram Moolenaar459fd782019-10-13 16:43:39 +0200615 int did_it = FALSE;
616 int did_local = FALSE;
Bram Moolenaar87f74102022-04-25 18:59:25 +0100617 int keyround1_simplified = keyround == 1 && did_simplify;
Bram Moolenaar459fd782019-10-13 16:43:39 +0200618 int round;
Bram Moolenaar459fd782019-10-13 16:43:39 +0200619
620 if (keyround == 2)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200621 {
Bram Moolenaar459fd782019-10-13 16:43:39 +0200622 if (alt_keys_buf == NULL)
623 break;
624 keys = alt_keys_buf;
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200625 }
Bram Moolenaar459fd782019-10-13 16:43:39 +0200626 else if (alt_keys_buf != NULL && do_print)
627 // when printing always use the not-simplified map
628 keys = alt_keys_buf;
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200629
Bram Moolenaar459fd782019-10-13 16:43:39 +0200630 // check arguments and translate function keys
631 if (haskey)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200632 {
Bram Moolenaar459fd782019-10-13 16:43:39 +0200633 len = (int)STRLEN(keys);
634 if (len > MAXMAPLEN) // maximum length of MAXMAPLEN chars
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200635 {
Bram Moolenaar459fd782019-10-13 16:43:39 +0200636 retval = 1;
637 goto theend;
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200638 }
Bram Moolenaar459fd782019-10-13 16:43:39 +0200639
zeertzjq44068e92022-06-16 11:14:55 +0100640 if (abbrev && maptype != MAPTYPE_UNMAP)
Bram Moolenaar459fd782019-10-13 16:43:39 +0200641 {
642 // If an abbreviation ends in a keyword character, the
643 // rest must be all keyword-char or all non-keyword-char.
644 // Otherwise we won't be able to find the start of it in a
645 // vi-compatible way.
646 if (has_mbyte)
647 {
648 int first, last;
649 int same = -1;
650
651 first = vim_iswordp(keys);
652 last = first;
653 p = keys + (*mb_ptr2len)(keys);
654 n = 1;
655 while (p < keys + len)
656 {
657 ++n; // nr of (multi-byte) chars
658 last = vim_iswordp(p); // type of last char
659 if (same == -1 && last != first)
660 same = n - 1; // count of same char type
661 p += (*mb_ptr2len)(p);
662 }
663 if (last && n > 2 && same >= 0 && same < n - 1)
664 {
665 retval = 1;
666 goto theend;
667 }
668 }
669 else if (vim_iswordc(keys[len - 1]))
670 // ends in keyword char
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200671 for (n = 0; n < len - 2; ++n)
672 if (vim_iswordc(keys[n]) != vim_iswordc(keys[len - 2]))
673 {
674 retval = 1;
675 goto theend;
676 }
Bram Moolenaar459fd782019-10-13 16:43:39 +0200677 // An abbreviation cannot contain white space.
678 for (n = 0; n < len; ++n)
679 if (VIM_ISWHITE(keys[n]))
680 {
681 retval = 1;
682 goto theend;
683 }
684 }
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200685 }
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200686
Bram Moolenaar459fd782019-10-13 16:43:39 +0200687 if (haskey && hasarg && abbrev) // if we will add an abbreviation
688 no_abbr = FALSE; // reset flag that indicates there are
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200689 // no abbreviations
690
Bram Moolenaar459fd782019-10-13 16:43:39 +0200691 if (do_print)
692 msg_start();
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200693
Bram Moolenaar459fd782019-10-13 16:43:39 +0200694 // Check if a new local mapping wasn't already defined globally.
Bram Moolenaar4c9243f2020-05-22 13:10:44 +0200695 if (unique && map_table == curbuf->b_maphash
zeertzjq44068e92022-06-16 11:14:55 +0100696 && haskey && hasarg && maptype != MAPTYPE_UNMAP)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200697 {
Bram Moolenaar459fd782019-10-13 16:43:39 +0200698 // need to loop over all global hash lists
Bram Moolenaar9f62ea02022-10-19 13:07:03 +0100699 for (int hash = 0; hash < 256 && !got_int; ++hash)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200700 {
Bram Moolenaar459fd782019-10-13 16:43:39 +0200701 if (abbrev)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200702 {
Bram Moolenaar459fd782019-10-13 16:43:39 +0200703 if (hash != 0) // there is only one abbreviation list
704 break;
705 mp = first_abbr;
706 }
707 else
708 mp = maphash[hash];
709 for ( ; mp != NULL && !got_int; mp = mp->m_next)
710 {
711 // check entries with the same mode
712 if ((mp->m_mode & mode) != 0
713 && mp->m_keylen == len
Bram Moolenaar459fd782019-10-13 16:43:39 +0200714 && STRNCMP(mp->m_keys, keys, (size_t)len) == 0)
715 {
716 if (abbrev)
Bram Moolenaar6d057012021-12-31 18:49:43 +0000717 semsg(
718 _(e_global_abbreviation_already_exists_for_str),
Bram Moolenaar459fd782019-10-13 16:43:39 +0200719 mp->m_keys);
720 else
Bram Moolenaar6d057012021-12-31 18:49:43 +0000721 semsg(_(e_global_mapping_already_exists_for_str),
Bram Moolenaar459fd782019-10-13 16:43:39 +0200722 mp->m_keys);
723 retval = 5;
724 goto theend;
725 }
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200726 }
727 }
728 }
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200729
Bram Moolenaar459fd782019-10-13 16:43:39 +0200730 // When listing global mappings, also list buffer-local ones here.
zeertzjq44068e92022-06-16 11:14:55 +0100731 if (map_table != curbuf->b_maphash && !hasarg
732 && maptype != MAPTYPE_UNMAP)
Bram Moolenaar9f62ea02022-10-19 13:07:03 +0100733 list_mappings(keyround, abbrev, haskey, keys, len,
734 mode, &did_local);
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200735
Bram Moolenaar459fd782019-10-13 16:43:39 +0200736 // Find an entry in the maphash[] list that matches.
737 // For :unmap we may loop two times: once to try to unmap an entry with
738 // a matching 'from' part, a second time, if the first fails, to unmap
zeertzjqa3f83fe2021-11-22 12:47:39 +0000739 // an entry with a matching 'to' part. This was done to allow
740 // ":ab foo bar" to be unmapped by typing ":unab foo", where "foo" will
741 // be replaced by "bar" because of the abbreviation.
zeertzjq44068e92022-06-16 11:14:55 +0100742 for (round = 0; (round == 0 || maptype == MAPTYPE_UNMAP) && round <= 1
Bram Moolenaar459fd782019-10-13 16:43:39 +0200743 && !did_it && !got_int; ++round)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200744 {
Bram Moolenaar459fd782019-10-13 16:43:39 +0200745 // need to loop over all hash lists
Bram Moolenaar9f62ea02022-10-19 13:07:03 +0100746 for (int hash = 0; hash < 256 && !got_int; ++hash)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200747 {
Bram Moolenaar459fd782019-10-13 16:43:39 +0200748 if (abbrev)
749 {
750 if (hash > 0) // there is only one abbreviation list
751 break;
752 mpp = abbr_table;
753 }
754 else
755 mpp = &(map_table[hash]);
756 for (mp = *mpp; mp != NULL && !got_int; mp = *mpp)
757 {
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200758
Bram Moolenaarfafb4b12019-10-16 18:34:57 +0200759 if ((mp->m_mode & mode) == 0)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200760 {
Bram Moolenaarfafb4b12019-10-16 18:34:57 +0200761 // skip entries with wrong mode
Bram Moolenaar459fd782019-10-13 16:43:39 +0200762 mpp = &(mp->m_next);
763 continue;
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200764 }
Bram Moolenaar459fd782019-10-13 16:43:39 +0200765 if (!haskey) // show all entries
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200766 {
Bram Moolenaarfafb4b12019-10-16 18:34:57 +0200767 if (!mp->m_simplified)
768 {
769 showmap(mp, map_table != maphash);
770 did_it = TRUE;
771 }
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200772 }
Bram Moolenaar459fd782019-10-13 16:43:39 +0200773 else // do we have a match?
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200774 {
Bram Moolenaar459fd782019-10-13 16:43:39 +0200775 if (round) // second round: Try unmap "rhs" string
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200776 {
Bram Moolenaar459fd782019-10-13 16:43:39 +0200777 n = (int)STRLEN(mp->m_str);
778 p = mp->m_str;
779 }
780 else
781 {
782 n = mp->m_keylen;
783 p = mp->m_keys;
784 }
785 if (STRNCMP(p, keys, (size_t)(n < len ? n : len)) == 0)
786 {
zeertzjq44068e92022-06-16 11:14:55 +0100787 if (maptype == MAPTYPE_UNMAP)
Bram Moolenaar459fd782019-10-13 16:43:39 +0200788 {
789 // Delete entry.
790 // Only accept a full match. For abbreviations
791 // we ignore trailing space when matching with
792 // the "lhs", since an abbreviation can't have
793 // trailing space.
794 if (n != len && (!abbrev || round || n > len
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200795 || *skipwhite(keys + n) != NUL))
Bram Moolenaar459fd782019-10-13 16:43:39 +0200796 {
797 mpp = &(mp->m_next);
798 continue;
799 }
zeertzjqabeb09b2022-04-26 12:29:43 +0100800 // In keyround for simplified keys, don't unmap
801 // a mapping without m_simplified flag.
Bram Moolenaar87f74102022-04-25 18:59:25 +0100802 if (keyround1_simplified && !mp->m_simplified)
zeertzjqa4e33322022-04-24 17:07:53 +0100803 break;
Bram Moolenaar459fd782019-10-13 16:43:39 +0200804 // We reset the indicated mode bits. If nothing
805 // is left the entry is deleted below.
806 mp->m_mode &= ~mode;
807 did_it = TRUE; // remember we did something
808 }
809 else if (!hasarg) // show matching entry
810 {
Bram Moolenaarfafb4b12019-10-16 18:34:57 +0200811 if (!mp->m_simplified)
812 {
813 showmap(mp, map_table != maphash);
814 did_it = TRUE;
815 }
Bram Moolenaar459fd782019-10-13 16:43:39 +0200816 }
817 else if (n != len) // new entry is ambiguous
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200818 {
819 mpp = &(mp->m_next);
820 continue;
821 }
Bram Moolenaar459fd782019-10-13 16:43:39 +0200822 else if (unique)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200823 {
Bram Moolenaar459fd782019-10-13 16:43:39 +0200824 if (abbrev)
Bram Moolenaar6d057012021-12-31 18:49:43 +0000825 semsg(
826 _(e_abbreviation_already_exists_for_str),
Bram Moolenaar459fd782019-10-13 16:43:39 +0200827 p);
828 else
Bram Moolenaar6d057012021-12-31 18:49:43 +0000829 semsg(_(e_mapping_already_exists_for_str),
Bram Moolenaar459fd782019-10-13 16:43:39 +0200830 p);
831 retval = 5;
832 goto theend;
833 }
834 else
835 {
836 // new rhs for existing entry
837 mp->m_mode &= ~mode; // remove mode bits
838 if (mp->m_mode == 0 && !did_it) // reuse entry
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200839 {
Bram Moolenaar459fd782019-10-13 16:43:39 +0200840 char_u *newstr = vim_strsave(rhs);
841
842 if (newstr == NULL)
843 {
844 retval = 4; // no mem
845 goto theend;
846 }
847 vim_free(mp->m_str);
848 mp->m_str = newstr;
849 vim_free(mp->m_orig_str);
850 mp->m_orig_str = vim_strsave(orig_rhs);
851 mp->m_noremap = noremap;
852 mp->m_nowait = nowait;
853 mp->m_silent = silent;
854 mp->m_mode = mode;
Bram Moolenaar87f74102022-04-25 18:59:25 +0100855 mp->m_simplified = keyround1_simplified;
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200856#ifdef FEAT_EVAL
Bram Moolenaar459fd782019-10-13 16:43:39 +0200857 mp->m_expr = expr;
858 mp->m_script_ctx = current_sctx;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +0100859 mp->m_script_ctx.sc_lnum += SOURCING_LNUM;
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200860#endif
Bram Moolenaar459fd782019-10-13 16:43:39 +0200861 did_it = TRUE;
862 }
863 }
864 if (mp->m_mode == 0) // entry can be deleted
865 {
866 map_free(mpp);
867 continue; // continue with *mpp
868 }
869
870 // May need to put this entry into another hash
871 // list.
Bram Moolenaar9f62ea02022-10-19 13:07:03 +0100872 int new_hash = MAP_HASH(mp->m_mode, mp->m_keys[0]);
Bram Moolenaar459fd782019-10-13 16:43:39 +0200873 if (!abbrev && new_hash != hash)
874 {
875 *mpp = mp->m_next;
876 mp->m_next = map_table[new_hash];
877 map_table[new_hash] = mp;
878
879 continue; // continue with *mpp
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200880 }
881 }
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200882 }
Bram Moolenaar459fd782019-10-13 16:43:39 +0200883 mpp = &(mp->m_next);
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200884 }
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200885 }
886 }
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200887
zeertzjq44068e92022-06-16 11:14:55 +0100888 if (maptype == MAPTYPE_UNMAP)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200889 {
Bram Moolenaar459fd782019-10-13 16:43:39 +0200890 // delete entry
891 if (!did_it)
zeertzjqa4e33322022-04-24 17:07:53 +0100892 {
Bram Moolenaar87f74102022-04-25 18:59:25 +0100893 if (!keyround1_simplified)
zeertzjqa4e33322022-04-24 17:07:53 +0100894 retval = 2; // no match
895 }
Bram Moolenaar459fd782019-10-13 16:43:39 +0200896 else if (*keys == Ctrl_C)
897 {
898 // If CTRL-C has been unmapped, reuse it for Interrupting.
899 if (map_table == curbuf->b_maphash)
900 curbuf->b_mapped_ctrl_c &= ~mode;
901 else
902 mapped_ctrl_c &= ~mode;
903 }
904 continue;
905 }
906
907 if (!haskey || !hasarg)
908 {
909 // print entries
910 if (!did_it && !did_local)
911 {
912 if (abbrev)
913 msg(_("No abbreviation found"));
914 else
915 msg(_("No mapping found"));
916 }
917 goto theend; // listing finished
918 }
919
920 if (did_it)
921 continue; // have added the new entry already
922
923 // Get here when adding a new entry to the maphash[] list or abbrlist.
Bram Moolenaar5a80f8a2020-05-22 13:38:18 +0200924 if (map_add(map_table, abbr_table, keys, rhs, orig_rhs,
925 noremap, nowait, silent, mode, abbrev,
Bram Moolenaar4c9243f2020-05-22 13:10:44 +0200926#ifdef FEAT_EVAL
zeertzjqbfc7cbd2023-04-07 22:09:46 +0100927 expr, /* sid */ 0, /* scriptversion */ 0, /* lnum */ 0,
Bram Moolenaar4c9243f2020-05-22 13:10:44 +0200928#endif
Bram Moolenaar87f74102022-04-25 18:59:25 +0100929 keyround1_simplified) == FAIL)
Bram Moolenaar459fd782019-10-13 16:43:39 +0200930 {
931 retval = 4; // no mem
932 goto theend;
933 }
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200934 }
935
936theend:
937 vim_free(keys_buf);
Bram Moolenaar459fd782019-10-13 16:43:39 +0200938 vim_free(alt_keys_buf);
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200939 vim_free(arg_buf);
940 return retval;
941}
942
943/*
944 * Get the mapping mode from the command name.
945 */
946 static int
947get_map_mode(char_u **cmdp, int forceit)
948{
949 char_u *p;
950 int modec;
951 int mode;
952
953 p = *cmdp;
954 modec = *p++;
955 if (modec == 'i')
Bram Moolenaar24959102022-05-07 20:01:16 +0100956 mode = MODE_INSERT; // :imap
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200957 else if (modec == 'l')
Bram Moolenaar24959102022-05-07 20:01:16 +0100958 mode = MODE_LANGMAP; // :lmap
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200959 else if (modec == 'c')
Bram Moolenaar24959102022-05-07 20:01:16 +0100960 mode = MODE_CMDLINE; // :cmap
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200961 else if (modec == 'n' && *p != 'o') // avoid :noremap
Bram Moolenaar24959102022-05-07 20:01:16 +0100962 mode = MODE_NORMAL; // :nmap
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200963 else if (modec == 'v')
Bram Moolenaar24959102022-05-07 20:01:16 +0100964 mode = MODE_VISUAL | MODE_SELECT; // :vmap
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200965 else if (modec == 'x')
Bram Moolenaar24959102022-05-07 20:01:16 +0100966 mode = MODE_VISUAL; // :xmap
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200967 else if (modec == 's')
Bram Moolenaar24959102022-05-07 20:01:16 +0100968 mode = MODE_SELECT; // :smap
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200969 else if (modec == 'o')
Bram Moolenaar24959102022-05-07 20:01:16 +0100970 mode = MODE_OP_PENDING; // :omap
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200971 else if (modec == 't')
Bram Moolenaar24959102022-05-07 20:01:16 +0100972 mode = MODE_TERMINAL; // :tmap
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200973 else
974 {
975 --p;
976 if (forceit)
Bram Moolenaar24959102022-05-07 20:01:16 +0100977 mode = MODE_INSERT | MODE_CMDLINE; // :map !
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200978 else
Bram Moolenaar24959102022-05-07 20:01:16 +0100979 mode = MODE_VISUAL | MODE_SELECT | MODE_NORMAL | MODE_OP_PENDING;
980 // :map
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200981 }
982
983 *cmdp = p;
984 return mode;
985}
986
987/*
zeertzjqc207fd22022-06-29 10:37:40 +0100988 * Clear all mappings (":mapclear") or abbreviations (":abclear").
989 * "abbr" should be FALSE for mappings, TRUE for abbreviations.
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200990 */
991 static void
992map_clear(
993 char_u *cmdp,
zeertzjqc207fd22022-06-29 10:37:40 +0100994 char_u *arg,
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200995 int forceit,
996 int abbr)
997{
998 int mode;
999 int local;
1000
1001 local = (STRCMP(arg, "<buffer>") == 0);
1002 if (!local && *arg != NUL)
1003 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001004 emsg(_(e_invalid_argument));
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001005 return;
1006 }
1007
1008 mode = get_map_mode(&cmdp, forceit);
zeertzjqc207fd22022-06-29 10:37:40 +01001009 map_clear_mode(curbuf, mode, local, abbr);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001010}
1011
1012/*
Bram Moolenaarbf533e42022-11-13 20:43:19 +00001013 * If "map_locked" is set then give an error and return TRUE.
1014 * Otherwise return FALSE.
1015 */
1016 static int
1017is_map_locked(void)
1018{
1019 if (map_locked > 0)
1020 {
1021 emsg(_(e_cannot_change_mappings_while_listing));
1022 return TRUE;
1023 }
1024 return FALSE;
1025}
1026
1027/*
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001028 * Clear all mappings in "mode".
1029 */
1030 void
zeertzjqc207fd22022-06-29 10:37:40 +01001031map_clear_mode(
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001032 buf_T *buf, // buffer for local mappings
1033 int mode, // mode in which to delete
1034 int local, // TRUE for buffer-local mappings
1035 int abbr) // TRUE for abbreviations
1036{
1037 mapblock_T *mp, **mpp;
1038 int hash;
1039 int new_hash;
1040
Bram Moolenaarbf533e42022-11-13 20:43:19 +00001041 if (is_map_locked())
1042 return;
1043
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001044 validate_maphash();
1045
1046 for (hash = 0; hash < 256; ++hash)
1047 {
1048 if (abbr)
1049 {
1050 if (hash > 0) // there is only one abbrlist
1051 break;
1052 if (local)
1053 mpp = &buf->b_first_abbr;
1054 else
1055 mpp = &first_abbr;
1056 }
1057 else
1058 {
1059 if (local)
1060 mpp = &buf->b_maphash[hash];
1061 else
1062 mpp = &maphash[hash];
1063 }
1064 while (*mpp != NULL)
1065 {
1066 mp = *mpp;
1067 if (mp->m_mode & mode)
1068 {
1069 mp->m_mode &= ~mode;
1070 if (mp->m_mode == 0) // entry can be deleted
1071 {
1072 map_free(mpp);
1073 continue;
1074 }
1075 // May need to put this entry into another hash list.
1076 new_hash = MAP_HASH(mp->m_mode, mp->m_keys[0]);
1077 if (!abbr && new_hash != hash)
1078 {
1079 *mpp = mp->m_next;
1080 if (local)
1081 {
1082 mp->m_next = buf->b_maphash[new_hash];
1083 buf->b_maphash[new_hash] = mp;
1084 }
1085 else
1086 {
1087 mp->m_next = maphash[new_hash];
1088 maphash[new_hash] = mp;
1089 }
1090 continue; // continue with *mpp
1091 }
1092 }
1093 mpp = &(mp->m_next);
1094 }
1095 }
1096}
1097
1098#if defined(FEAT_EVAL) || defined(PROTO)
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001099 int
Bram Moolenaar581ba392019-09-03 22:08:33 +02001100mode_str2flags(char_u *modechars)
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001101{
1102 int mode = 0;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001103
1104 if (vim_strchr(modechars, 'n') != NULL)
Bram Moolenaar24959102022-05-07 20:01:16 +01001105 mode |= MODE_NORMAL;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001106 if (vim_strchr(modechars, 'v') != NULL)
Bram Moolenaar24959102022-05-07 20:01:16 +01001107 mode |= MODE_VISUAL | MODE_SELECT;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001108 if (vim_strchr(modechars, 'x') != NULL)
Bram Moolenaar24959102022-05-07 20:01:16 +01001109 mode |= MODE_VISUAL;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001110 if (vim_strchr(modechars, 's') != NULL)
Bram Moolenaar24959102022-05-07 20:01:16 +01001111 mode |= MODE_SELECT;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001112 if (vim_strchr(modechars, 'o') != NULL)
Bram Moolenaar24959102022-05-07 20:01:16 +01001113 mode |= MODE_OP_PENDING;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001114 if (vim_strchr(modechars, 'i') != NULL)
Bram Moolenaar24959102022-05-07 20:01:16 +01001115 mode |= MODE_INSERT;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001116 if (vim_strchr(modechars, 'l') != NULL)
Bram Moolenaar24959102022-05-07 20:01:16 +01001117 mode |= MODE_LANGMAP;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001118 if (vim_strchr(modechars, 'c') != NULL)
Bram Moolenaar24959102022-05-07 20:01:16 +01001119 mode |= MODE_CMDLINE;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001120
Bram Moolenaar581ba392019-09-03 22:08:33 +02001121 return mode;
1122}
1123
1124/*
1125 * Return TRUE if a map exists that has "str" in the rhs for mode "modechars".
1126 * Recognize termcap codes in "str".
1127 * Also checks mappings local to the current buffer.
1128 */
1129 int
1130map_to_exists(char_u *str, char_u *modechars, int abbr)
1131{
1132 char_u *rhs;
1133 char_u *buf;
1134 int retval;
1135
Bram Moolenaar459fd782019-10-13 16:43:39 +02001136 rhs = replace_termcodes(str, &buf, REPTERM_DO_LT, NULL);
Bram Moolenaar581ba392019-09-03 22:08:33 +02001137
1138 retval = map_to_exists_mode(rhs, mode_str2flags(modechars), abbr);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001139 vim_free(buf);
1140
1141 return retval;
1142}
1143#endif
1144
1145/*
1146 * Return TRUE if a map exists that has "str" in the rhs for mode "mode".
1147 * Also checks mappings local to the current buffer.
1148 */
1149 int
1150map_to_exists_mode(char_u *rhs, int mode, int abbr)
1151{
1152 mapblock_T *mp;
1153 int hash;
1154 int exp_buffer = FALSE;
1155
1156 validate_maphash();
1157
1158 // Do it twice: once for global maps and once for local maps.
1159 for (;;)
1160 {
1161 for (hash = 0; hash < 256; ++hash)
1162 {
1163 if (abbr)
1164 {
1165 if (hash > 0) // there is only one abbr list
1166 break;
1167 if (exp_buffer)
1168 mp = curbuf->b_first_abbr;
1169 else
1170 mp = first_abbr;
1171 }
1172 else if (exp_buffer)
1173 mp = curbuf->b_maphash[hash];
1174 else
1175 mp = maphash[hash];
1176 for (; mp; mp = mp->m_next)
1177 {
1178 if ((mp->m_mode & mode)
1179 && strstr((char *)mp->m_str, (char *)rhs) != NULL)
1180 return TRUE;
1181 }
1182 }
1183 if (exp_buffer)
1184 break;
1185 exp_buffer = TRUE;
1186 }
1187
1188 return FALSE;
1189}
1190
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001191/*
1192 * Used below when expanding mapping/abbreviation names.
1193 */
1194static int expand_mapmodes = 0;
1195static int expand_isabbrev = 0;
1196static int expand_buffer = FALSE;
1197
1198/*
Bram Moolenaar7f51bbe2020-01-24 20:21:19 +01001199 * Translate an internal mapping/abbreviation representation into the
1200 * corresponding external one recognized by :map/:abbrev commands.
1201 * Respects the current B/k/< settings of 'cpoption'.
1202 *
1203 * This function is called when expanding mappings/abbreviations on the
1204 * command-line.
1205 *
1206 * It uses a growarray to build the translation string since the latter can be
1207 * wider than the original description. The caller has to free the string
1208 * afterwards.
1209 *
1210 * Returns NULL when there is a problem.
1211 */
1212 static char_u *
1213translate_mapping(char_u *str)
1214{
1215 garray_T ga;
1216 int c;
1217 int modifiers;
1218 int cpo_bslash;
1219 int cpo_special;
1220
1221 ga_init(&ga);
1222 ga.ga_itemsize = 1;
1223 ga.ga_growsize = 40;
1224
1225 cpo_bslash = (vim_strchr(p_cpo, CPO_BSLASH) != NULL);
1226 cpo_special = (vim_strchr(p_cpo, CPO_SPECI) != NULL);
1227
1228 for (; *str; ++str)
1229 {
1230 c = *str;
1231 if (c == K_SPECIAL && str[1] != NUL && str[2] != NUL)
1232 {
1233 modifiers = 0;
1234 if (str[1] == KS_MODIFIER)
1235 {
1236 str++;
1237 modifiers = *++str;
1238 c = *++str;
1239 }
1240 if (c == K_SPECIAL && str[1] != NUL && str[2] != NUL)
1241 {
1242 if (cpo_special)
1243 {
1244 ga_clear(&ga);
1245 return NULL;
1246 }
1247 c = TO_SPECIAL(str[1], str[2]);
1248 if (c == K_ZERO) // display <Nul> as ^@
1249 c = NUL;
1250 str += 2;
1251 }
1252 if (IS_SPECIAL(c) || modifiers) // special key
1253 {
1254 if (cpo_special)
1255 {
1256 ga_clear(&ga);
1257 return NULL;
1258 }
1259 ga_concat(&ga, get_special_key_name(c, modifiers));
1260 continue; // for (str)
1261 }
1262 }
1263 if (c == ' ' || c == '\t' || c == Ctrl_J || c == Ctrl_V
1264 || (c == '<' && !cpo_special) || (c == '\\' && !cpo_bslash))
1265 ga_append(&ga, cpo_bslash ? Ctrl_V : '\\');
1266 if (c)
1267 ga_append(&ga, c);
1268 }
1269 ga_append(&ga, NUL);
1270 return (char_u *)(ga.ga_data);
1271}
1272
1273/*
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001274 * Work out what to complete when doing command line completion of mapping
1275 * or abbreviation names.
1276 */
1277 char_u *
1278set_context_in_map_cmd(
1279 expand_T *xp,
1280 char_u *cmd,
1281 char_u *arg,
1282 int forceit, // TRUE if '!' given
1283 int isabbrev, // TRUE if abbreviation
1284 int isunmap, // TRUE if unmap/unabbrev command
1285 cmdidx_T cmdidx)
1286{
1287 if (forceit && cmdidx != CMD_map && cmdidx != CMD_unmap)
1288 xp->xp_context = EXPAND_NOTHING;
1289 else
1290 {
1291 if (isunmap)
1292 expand_mapmodes = get_map_mode(&cmd, forceit || isabbrev);
1293 else
1294 {
Bram Moolenaar24959102022-05-07 20:01:16 +01001295 expand_mapmodes = MODE_INSERT | MODE_CMDLINE;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001296 if (!isabbrev)
Bram Moolenaar24959102022-05-07 20:01:16 +01001297 expand_mapmodes += MODE_VISUAL | MODE_SELECT | MODE_NORMAL
1298 | MODE_OP_PENDING;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001299 }
1300 expand_isabbrev = isabbrev;
1301 xp->xp_context = EXPAND_MAPPINGS;
1302 expand_buffer = FALSE;
1303 for (;;)
1304 {
1305 if (STRNCMP(arg, "<buffer>", 8) == 0)
1306 {
1307 expand_buffer = TRUE;
1308 arg = skipwhite(arg + 8);
1309 continue;
1310 }
1311 if (STRNCMP(arg, "<unique>", 8) == 0)
1312 {
1313 arg = skipwhite(arg + 8);
1314 continue;
1315 }
1316 if (STRNCMP(arg, "<nowait>", 8) == 0)
1317 {
1318 arg = skipwhite(arg + 8);
1319 continue;
1320 }
1321 if (STRNCMP(arg, "<silent>", 8) == 0)
1322 {
1323 arg = skipwhite(arg + 8);
1324 continue;
1325 }
1326 if (STRNCMP(arg, "<special>", 9) == 0)
1327 {
1328 arg = skipwhite(arg + 9);
1329 continue;
1330 }
1331#ifdef FEAT_EVAL
1332 if (STRNCMP(arg, "<script>", 8) == 0)
1333 {
1334 arg = skipwhite(arg + 8);
1335 continue;
1336 }
1337 if (STRNCMP(arg, "<expr>", 6) == 0)
1338 {
1339 arg = skipwhite(arg + 6);
1340 continue;
1341 }
1342#endif
1343 break;
1344 }
1345 xp->xp_pattern = arg;
1346 }
1347
1348 return NULL;
1349}
1350
1351/*
1352 * Find all mapping/abbreviation names that match regexp "regmatch"'.
1353 * For command line expansion of ":[un]map" and ":[un]abbrev" in all modes.
1354 * Return OK if matches found, FAIL otherwise.
1355 */
1356 int
1357ExpandMappings(
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001358 char_u *pat,
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001359 regmatch_T *regmatch,
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001360 int *numMatches,
1361 char_u ***matches)
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001362{
1363 mapblock_T *mp;
Yegappan Lakshmanan5de4c432022-02-28 13:28:38 +00001364 garray_T ga;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001365 int hash;
1366 int count;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001367 char_u *p;
1368 int i;
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001369 int fuzzy;
1370 int match;
Yasuhiro Matsumoto09f68a52022-06-18 16:48:36 +01001371 int score = 0;
Yegappan Lakshmanan5de4c432022-02-28 13:28:38 +00001372 fuzmatch_str_T *fuzmatch;
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001373
1374 fuzzy = cmdline_fuzzy_complete(pat);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001375
1376 validate_maphash();
1377
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001378 *numMatches = 0; // return values in case of FAIL
1379 *matches = NULL;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001380
Yegappan Lakshmanan5de4c432022-02-28 13:28:38 +00001381 if (!fuzzy)
1382 ga_init2(&ga, sizeof(char *), 3);
1383 else
1384 ga_init2(&ga, sizeof(fuzmatch_str_T), 3);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001385
Yegappan Lakshmanan5de4c432022-02-28 13:28:38 +00001386 // First search in map modifier arguments
1387 for (i = 0; i < 7; ++i)
1388 {
1389 if (i == 0)
1390 p = (char_u *)"<silent>";
1391 else if (i == 1)
1392 p = (char_u *)"<unique>";
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001393#ifdef FEAT_EVAL
Yegappan Lakshmanan5de4c432022-02-28 13:28:38 +00001394 else if (i == 2)
1395 p = (char_u *)"<script>";
1396 else if (i == 3)
1397 p = (char_u *)"<expr>";
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001398#endif
Yegappan Lakshmanan5de4c432022-02-28 13:28:38 +00001399 else if (i == 4 && !expand_buffer)
1400 p = (char_u *)"<buffer>";
1401 else if (i == 5)
1402 p = (char_u *)"<nowait>";
1403 else if (i == 6)
1404 p = (char_u *)"<special>";
1405 else
1406 continue;
1407
1408 if (!fuzzy)
1409 match = vim_regexec(regmatch, p, (colnr_T)0);
1410 else
1411 {
1412 score = fuzzy_match_str(p, pat);
1413 match = (score != 0);
1414 }
1415
1416 if (!match)
1417 continue;
1418
1419 if (ga_grow(&ga, 1) == FAIL)
1420 break;
1421
1422 if (fuzzy)
1423 {
1424 fuzmatch = &((fuzmatch_str_T *)ga.ga_data)[ga.ga_len];
1425 fuzmatch->idx = ga.ga_len;
1426 fuzmatch->str = vim_strsave(p);
1427 fuzmatch->score = score;
1428 }
1429 else
1430 ((char_u **)ga.ga_data)[ga.ga_len] = vim_strsave(p);
1431 ++ga.ga_len;
1432 }
1433
1434 for (hash = 0; hash < 256; ++hash)
1435 {
1436 if (expand_isabbrev)
1437 {
1438 if (hash > 0) // only one abbrev list
1439 break; // for (hash)
1440 mp = first_abbr;
1441 }
1442 else if (expand_buffer)
1443 mp = curbuf->b_maphash[hash];
1444 else
1445 mp = maphash[hash];
1446 for (; mp; mp = mp->m_next)
1447 {
zeertzjq997b8a02023-02-19 21:00:31 +00001448 if (mp->m_simplified || !(mp->m_mode & expand_mapmodes))
Yegappan Lakshmanan5de4c432022-02-28 13:28:38 +00001449 continue;
1450
1451 p = translate_mapping(mp->m_keys);
1452 if (p == NULL)
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001453 continue;
1454
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001455 if (!fuzzy)
1456 match = vim_regexec(regmatch, p, (colnr_T)0);
1457 else
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001458 {
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001459 score = fuzzy_match_str(p, pat);
1460 match = (score != 0);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001461 }
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001462
1463 if (!match)
Yegappan Lakshmanan5de4c432022-02-28 13:28:38 +00001464 {
1465 vim_free(p);
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001466 continue;
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001467 }
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001468
Yegappan Lakshmanan5de4c432022-02-28 13:28:38 +00001469 if (ga_grow(&ga, 1) == FAIL)
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001470 {
Yegappan Lakshmanan5de4c432022-02-28 13:28:38 +00001471 vim_free(p);
1472 break;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001473 }
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001474
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001475 if (fuzzy)
1476 {
Yegappan Lakshmanan5de4c432022-02-28 13:28:38 +00001477 fuzmatch = &((fuzmatch_str_T *)ga.ga_data)[ga.ga_len];
1478 fuzmatch->idx = ga.ga_len;
1479 fuzmatch->str = p;
1480 fuzmatch->score = score;
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001481 }
1482 else
Yegappan Lakshmanan5de4c432022-02-28 13:28:38 +00001483 ((char_u **)ga.ga_data)[ga.ga_len] = p;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001484
Yegappan Lakshmanan5de4c432022-02-28 13:28:38 +00001485 ++ga.ga_len;
1486 } // for (mp)
1487 } // for (hash)
1488
1489 if (ga.ga_len == 0)
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001490 return FAIL;
1491
Yegappan Lakshmanan5de4c432022-02-28 13:28:38 +00001492 if (!fuzzy)
1493 {
1494 *matches = ga.ga_data;
1495 *numMatches = ga.ga_len;
1496 }
1497 else
1498 {
1499 if (fuzzymatches_to_strmatches(ga.ga_data, matches, ga.ga_len,
1500 FALSE) == FAIL)
1501 return FAIL;
1502 *numMatches = ga.ga_len;
1503 }
1504
1505 count = *numMatches;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001506 if (count > 1)
1507 {
1508 char_u **ptr1;
1509 char_u **ptr2;
1510 char_u **ptr3;
1511
1512 // Sort the matches
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001513 // Fuzzy matching already sorts the matches
1514 if (!fuzzy)
1515 sort_strings(*matches, count);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001516
1517 // Remove multiple entries
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001518 ptr1 = *matches;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001519 ptr2 = ptr1 + 1;
1520 ptr3 = ptr1 + count;
1521
1522 while (ptr2 < ptr3)
1523 {
1524 if (STRCMP(*ptr1, *ptr2))
1525 *++ptr1 = *ptr2++;
1526 else
1527 {
1528 vim_free(*ptr2++);
1529 count--;
1530 }
1531 }
1532 }
1533
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001534 *numMatches = count;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001535 return (count == 0 ? FAIL : OK);
1536}
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001537
1538/*
1539 * Check for an abbreviation.
1540 * Cursor is at ptr[col].
1541 * When inserting, mincol is where insert started.
1542 * For the command line, mincol is what is to be skipped over.
1543 * "c" is the character typed before check_abbr was called. It may have
1544 * ABBR_OFF added to avoid prepending a CTRL-V to it.
1545 *
1546 * Historic vi practice: The last character of an abbreviation must be an id
1547 * character ([a-zA-Z0-9_]). The characters in front of it must be all id
1548 * characters or all non-id characters. This allows for abbr. "#i" to
1549 * "#include".
1550 *
1551 * Vim addition: Allow for abbreviations that end in a non-keyword character.
1552 * Then there must be white space before the abbr.
1553 *
1554 * return TRUE if there is an abbreviation, FALSE if not
1555 */
1556 int
1557check_abbr(
1558 int c,
1559 char_u *ptr,
1560 int col,
1561 int mincol)
1562{
1563 int len;
1564 int scol; // starting column of the abbr.
1565 int j;
1566 char_u *s;
1567 char_u tb[MB_MAXBYTES + 4];
1568 mapblock_T *mp;
1569 mapblock_T *mp2;
1570 int clen = 0; // length in characters
1571 int is_id = TRUE;
1572 int vim_abbr;
1573
1574 if (typebuf.tb_no_abbr_cnt) // abbrev. are not recursive
1575 return FALSE;
1576
1577 // no remapping implies no abbreviation, except for CTRL-]
1578 if (noremap_keys() && c != Ctrl_RSB)
1579 return FALSE;
1580
1581 // Check for word before the cursor: If it ends in a keyword char all
1582 // chars before it must be keyword chars or non-keyword chars, but not
1583 // white space. If it ends in a non-keyword char we accept any characters
1584 // before it except white space.
1585 if (col == 0) // cannot be an abbr.
1586 return FALSE;
1587
1588 if (has_mbyte)
1589 {
1590 char_u *p;
1591
1592 p = mb_prevptr(ptr, ptr + col);
1593 if (!vim_iswordp(p))
1594 vim_abbr = TRUE; // Vim added abbr.
1595 else
1596 {
1597 vim_abbr = FALSE; // vi compatible abbr.
1598 if (p > ptr)
1599 is_id = vim_iswordp(mb_prevptr(ptr, p));
1600 }
1601 clen = 1;
1602 while (p > ptr + mincol)
1603 {
1604 p = mb_prevptr(ptr, p);
1605 if (vim_isspace(*p) || (!vim_abbr && is_id != vim_iswordp(p)))
1606 {
1607 p += (*mb_ptr2len)(p);
1608 break;
1609 }
1610 ++clen;
1611 }
1612 scol = (int)(p - ptr);
1613 }
1614 else
1615 {
1616 if (!vim_iswordc(ptr[col - 1]))
1617 vim_abbr = TRUE; // Vim added abbr.
1618 else
1619 {
1620 vim_abbr = FALSE; // vi compatible abbr.
1621 if (col > 1)
1622 is_id = vim_iswordc(ptr[col - 2]);
1623 }
1624 for (scol = col - 1; scol > 0 && !vim_isspace(ptr[scol - 1])
1625 && (vim_abbr || is_id == vim_iswordc(ptr[scol - 1])); --scol)
1626 ;
1627 }
1628
1629 if (scol < mincol)
1630 scol = mincol;
1631 if (scol < col) // there is a word in front of the cursor
1632 {
1633 ptr += scol;
1634 len = col - scol;
1635 mp = curbuf->b_first_abbr;
1636 mp2 = first_abbr;
1637 if (mp == NULL)
1638 {
1639 mp = mp2;
1640 mp2 = NULL;
1641 }
1642 for ( ; mp; mp->m_next == NULL
1643 ? (mp = mp2, mp2 = NULL) : (mp = mp->m_next))
1644 {
1645 int qlen = mp->m_keylen;
1646 char_u *q = mp->m_keys;
1647 int match;
1648
1649 if (vim_strbyte(mp->m_keys, K_SPECIAL) != NULL)
1650 {
1651 char_u *qe = vim_strsave(mp->m_keys);
1652
1653 // might have CSI escaped mp->m_keys
1654 if (qe != NULL)
1655 {
1656 q = qe;
1657 vim_unescape_csi(q);
1658 qlen = (int)STRLEN(q);
1659 }
1660 }
1661
1662 // find entries with right mode and keys
1663 match = (mp->m_mode & State)
1664 && qlen == len
1665 && !STRNCMP(q, ptr, (size_t)len);
1666 if (q != mp->m_keys)
1667 vim_free(q);
1668 if (match)
1669 break;
1670 }
1671 if (mp != NULL)
1672 {
Bram Moolenaar94075b22022-01-18 20:30:39 +00001673 int noremap;
1674 int silent;
1675#ifdef FEAT_EVAL
1676 int expr;
1677#endif
1678
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001679 // Found a match:
1680 // Insert the rest of the abbreviation in typebuf.tb_buf[].
1681 // This goes from end to start.
1682 //
1683 // Characters 0x000 - 0x100: normal chars, may need CTRL-V,
1684 // except K_SPECIAL: Becomes K_SPECIAL KS_SPECIAL KE_FILLER
1685 // Characters where IS_SPECIAL() == TRUE: key codes, need
1686 // K_SPECIAL. Other characters (with ABBR_OFF): don't use CTRL-V.
1687 //
1688 // Character CTRL-] is treated specially - it completes the
1689 // abbreviation, but is not inserted into the input stream.
1690 j = 0;
1691 if (c != Ctrl_RSB)
1692 {
1693 // special key code, split up
1694 if (IS_SPECIAL(c) || c == K_SPECIAL)
1695 {
1696 tb[j++] = K_SPECIAL;
1697 tb[j++] = K_SECOND(c);
1698 tb[j++] = K_THIRD(c);
1699 }
1700 else
1701 {
1702 if (c < ABBR_OFF && (c < ' ' || c > '~'))
1703 tb[j++] = Ctrl_V; // special char needs CTRL-V
1704 if (has_mbyte)
1705 {
Bram Moolenaar4934ed32021-04-30 19:43:11 +02001706 int newlen;
1707 char_u *escaped;
1708
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001709 // if ABBR_OFF has been added, remove it here
1710 if (c >= ABBR_OFF)
1711 c -= ABBR_OFF;
Bram Moolenaar4934ed32021-04-30 19:43:11 +02001712 newlen = (*mb_char2bytes)(c, tb + j);
1713 tb[j + newlen] = NUL;
1714 // Need to escape K_SPECIAL.
1715 escaped = vim_strsave_escape_csi(tb + j);
1716 if (escaped != NULL)
1717 {
Bram Moolenaar551c1ae2021-05-03 18:57:05 +02001718 newlen = (int)STRLEN(escaped);
Bram Moolenaar4934ed32021-04-30 19:43:11 +02001719 mch_memmove(tb + j, escaped, newlen);
1720 j += newlen;
1721 vim_free(escaped);
1722 }
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001723 }
1724 else
1725 tb[j++] = c;
1726 }
1727 tb[j] = NUL;
1728 // insert the last typed char
1729 (void)ins_typebuf(tb, 1, 0, TRUE, mp->m_silent);
1730 }
Bram Moolenaar94075b22022-01-18 20:30:39 +00001731
1732 // copy values here, calling eval_map_expr() may make "mp" invalid!
1733 noremap = mp->m_noremap;
1734 silent = mp->m_silent;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001735#ifdef FEAT_EVAL
Bram Moolenaar94075b22022-01-18 20:30:39 +00001736 expr = mp->m_expr;
1737
1738 if (expr)
Bram Moolenaar19db9e62022-01-11 11:58:19 +00001739 s = eval_map_expr(mp, c);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001740 else
1741#endif
1742 s = mp->m_str;
1743 if (s != NULL)
1744 {
1745 // insert the to string
Bram Moolenaar94075b22022-01-18 20:30:39 +00001746 (void)ins_typebuf(s, noremap, 0, TRUE, silent);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001747 // no abbrev. for these chars
1748 typebuf.tb_no_abbr_cnt += (int)STRLEN(s) + j + 1;
1749#ifdef FEAT_EVAL
Bram Moolenaar94075b22022-01-18 20:30:39 +00001750 if (expr)
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001751 vim_free(s);
1752#endif
1753 }
1754
1755 tb[0] = Ctrl_H;
1756 tb[1] = NUL;
1757 if (has_mbyte)
1758 len = clen; // Delete characters instead of bytes
1759 while (len-- > 0) // delete the from string
Bram Moolenaar94075b22022-01-18 20:30:39 +00001760 (void)ins_typebuf(tb, 1, 0, TRUE, silent);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001761 return TRUE;
1762 }
1763 }
1764 return FALSE;
1765}
1766
1767#ifdef FEAT_EVAL
1768/*
1769 * Evaluate the RHS of a mapping or abbreviations and take care of escaping
1770 * special characters.
Bram Moolenaar94075b22022-01-18 20:30:39 +00001771 * Careful: after this "mp" will be invalid if the mapping was deleted.
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001772 */
1773 char_u *
1774eval_map_expr(
Bram Moolenaar19db9e62022-01-11 11:58:19 +00001775 mapblock_T *mp,
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001776 int c) // NUL or typed character for abbreviation
1777{
1778 char_u *res;
1779 char_u *p;
1780 char_u *expr;
1781 pos_T save_cursor;
1782 int save_msg_col;
1783 int save_msg_row;
Bram Moolenaar19db9e62022-01-11 11:58:19 +00001784 scid_T save_sctx_sid = current_sctx.sc_sid;
1785 int save_sctx_version = current_sctx.sc_version;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001786
1787 // Remove escaping of CSI, because "str" is in a format to be used as
1788 // typeahead.
Bram Moolenaar19db9e62022-01-11 11:58:19 +00001789 expr = vim_strsave(mp->m_str);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001790 if (expr == NULL)
1791 return NULL;
1792 vim_unescape_csi(expr);
1793
1794 // Forbid changing text or using ":normal" to avoid most of the bad side
1795 // effects. Also restore the cursor position.
zeertzjqcfe45652022-05-27 17:26:55 +01001796 ++textlock;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001797 ++ex_normal_lock;
1798 set_vim_var_char(c); // set v:char to the typed character
1799 save_cursor = curwin->w_cursor;
1800 save_msg_col = msg_col;
1801 save_msg_row = msg_row;
Bram Moolenaar19db9e62022-01-11 11:58:19 +00001802 if (mp->m_script_ctx.sc_version == SCRIPT_VERSION_VIM9)
1803 {
1804 current_sctx.sc_sid = mp->m_script_ctx.sc_sid;
1805 current_sctx.sc_version = SCRIPT_VERSION_VIM9;
1806 }
1807
1808 // Note: the evaluation may make "mp" invalid.
Bram Moolenaara4e0b972022-10-01 19:43:52 +01001809 p = eval_to_string(expr, FALSE, FALSE);
Bram Moolenaar19db9e62022-01-11 11:58:19 +00001810
zeertzjqcfe45652022-05-27 17:26:55 +01001811 --textlock;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001812 --ex_normal_lock;
1813 curwin->w_cursor = save_cursor;
1814 msg_col = save_msg_col;
1815 msg_row = save_msg_row;
Bram Moolenaar19db9e62022-01-11 11:58:19 +00001816 current_sctx.sc_sid = save_sctx_sid;
1817 current_sctx.sc_version = save_sctx_version;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001818
1819 vim_free(expr);
1820
1821 if (p == NULL)
1822 return NULL;
1823 // Escape CSI in the result to be able to use the string as typeahead.
1824 res = vim_strsave_escape_csi(p);
1825 vim_free(p);
1826
1827 return res;
1828}
1829#endif
1830
1831/*
1832 * Copy "p" to allocated memory, escaping K_SPECIAL and CSI so that the result
1833 * can be put in the typeahead buffer.
1834 * Returns NULL when out of memory.
1835 */
1836 char_u *
Bram Moolenaar957cf672020-11-12 14:21:06 +01001837vim_strsave_escape_csi(char_u *p)
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001838{
1839 char_u *res;
1840 char_u *s, *d;
1841
1842 // Need a buffer to hold up to three times as much. Four in case of an
1843 // illegal utf-8 byte:
1844 // 0xc0 -> 0xc3 0x80 -> 0xc3 K_SPECIAL KS_SPECIAL KE_FILLER
1845 res = alloc(STRLEN(p) * 4 + 1);
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00001846 if (res == NULL)
1847 return NULL;
1848
1849 d = res;
1850 for (s = p; *s != NUL; )
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001851 {
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00001852 if ((s[0] == K_SPECIAL
zeertzjq2cd0f272022-10-04 20:14:28 +01001853#ifdef FEAT_GUI
1854 || (gui.in_use && s[0] == CSI)
1855#endif
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00001856 ) && s[1] != NUL && s[2] != NUL)
1857 {
1858 // Copy special key unmodified.
1859 *d++ = *s++;
1860 *d++ = *s++;
1861 *d++ = *s++;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001862 }
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00001863 else
1864 {
1865 // Add character, possibly multi-byte to destination, escaping
1866 // CSI and K_SPECIAL. Be careful, it can be an illegal byte!
1867 d = add_char2buf(PTR2CHAR(s), d);
1868 s += MB_CPTR2LEN(s);
1869 }
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001870 }
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00001871 *d = NUL;
1872
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001873 return res;
1874}
1875
1876/*
1877 * Remove escaping from CSI and K_SPECIAL characters. Reverse of
1878 * vim_strsave_escape_csi(). Works in-place.
1879 */
1880 void
1881vim_unescape_csi(char_u *p)
1882{
1883 char_u *s = p, *d = p;
1884
1885 while (*s != NUL)
1886 {
1887 if (s[0] == K_SPECIAL && s[1] == KS_SPECIAL && s[2] == KE_FILLER)
1888 {
1889 *d++ = K_SPECIAL;
1890 s += 3;
1891 }
1892 else if ((s[0] == K_SPECIAL || s[0] == CSI)
1893 && s[1] == KS_EXTRA && s[2] == (int)KE_CSI)
1894 {
1895 *d++ = CSI;
1896 s += 3;
1897 }
1898 else
1899 *d++ = *s++;
1900 }
1901 *d = NUL;
1902}
1903
1904/*
1905 * Write map commands for the current mappings to an .exrc file.
1906 * Return FAIL on error, OK otherwise.
1907 */
1908 int
1909makemap(
1910 FILE *fd,
1911 buf_T *buf) // buffer for local mappings or NULL
1912{
1913 mapblock_T *mp;
1914 char_u c1, c2, c3;
1915 char_u *p;
1916 char *cmd;
1917 int abbr;
1918 int hash;
1919 int did_cpo = FALSE;
1920 int i;
1921
1922 validate_maphash();
1923
1924 // Do the loop twice: Once for mappings, once for abbreviations.
1925 // Then loop over all map hash lists.
1926 for (abbr = 0; abbr < 2; ++abbr)
1927 for (hash = 0; hash < 256; ++hash)
1928 {
1929 if (abbr)
1930 {
1931 if (hash > 0) // there is only one abbr list
1932 break;
1933 if (buf != NULL)
1934 mp = buf->b_first_abbr;
1935 else
1936 mp = first_abbr;
1937 }
1938 else
1939 {
1940 if (buf != NULL)
1941 mp = buf->b_maphash[hash];
1942 else
1943 mp = maphash[hash];
1944 }
1945
1946 for ( ; mp; mp = mp->m_next)
1947 {
1948 // skip script-local mappings
1949 if (mp->m_noremap == REMAP_SCRIPT)
1950 continue;
1951
1952 // skip mappings that contain a <SNR> (script-local thing),
1953 // they probably don't work when loaded again
1954 for (p = mp->m_str; *p != NUL; ++p)
1955 if (p[0] == K_SPECIAL && p[1] == KS_EXTRA
1956 && p[2] == (int)KE_SNR)
1957 break;
1958 if (*p != NUL)
1959 continue;
1960
1961 // It's possible to create a mapping and then ":unmap" certain
1962 // modes. We recreate this here by mapping the individual
1963 // modes, which requires up to three of them.
1964 c1 = NUL;
1965 c2 = NUL;
1966 c3 = NUL;
1967 if (abbr)
1968 cmd = "abbr";
1969 else
1970 cmd = "map";
1971 switch (mp->m_mode)
1972 {
Bram Moolenaar24959102022-05-07 20:01:16 +01001973 case MODE_NORMAL | MODE_VISUAL | MODE_SELECT
1974 | MODE_OP_PENDING:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001975 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01001976 case MODE_NORMAL:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001977 c1 = 'n';
1978 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01001979 case MODE_VISUAL:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001980 c1 = 'x';
1981 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01001982 case MODE_SELECT:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001983 c1 = 's';
1984 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01001985 case MODE_OP_PENDING:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001986 c1 = 'o';
1987 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01001988 case MODE_NORMAL | MODE_VISUAL:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001989 c1 = 'n';
1990 c2 = 'x';
1991 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01001992 case MODE_NORMAL | MODE_SELECT:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001993 c1 = 'n';
1994 c2 = 's';
1995 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01001996 case MODE_NORMAL | MODE_OP_PENDING:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001997 c1 = 'n';
1998 c2 = 'o';
1999 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01002000 case MODE_VISUAL | MODE_SELECT:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002001 c1 = 'v';
2002 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01002003 case MODE_VISUAL | MODE_OP_PENDING:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002004 c1 = 'x';
2005 c2 = 'o';
2006 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01002007 case MODE_SELECT | MODE_OP_PENDING:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002008 c1 = 's';
2009 c2 = 'o';
2010 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01002011 case MODE_NORMAL | MODE_VISUAL | MODE_SELECT:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002012 c1 = 'n';
2013 c2 = 'v';
2014 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01002015 case MODE_NORMAL | MODE_VISUAL | MODE_OP_PENDING:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002016 c1 = 'n';
2017 c2 = 'x';
2018 c3 = 'o';
2019 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01002020 case MODE_NORMAL | MODE_SELECT | MODE_OP_PENDING:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002021 c1 = 'n';
2022 c2 = 's';
2023 c3 = 'o';
2024 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01002025 case MODE_VISUAL | MODE_SELECT | MODE_OP_PENDING:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002026 c1 = 'v';
2027 c2 = 'o';
2028 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01002029 case MODE_CMDLINE | MODE_INSERT:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002030 if (!abbr)
2031 cmd = "map!";
2032 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01002033 case MODE_CMDLINE:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002034 c1 = 'c';
2035 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01002036 case MODE_INSERT:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002037 c1 = 'i';
2038 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01002039 case MODE_LANGMAP:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002040 c1 = 'l';
2041 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01002042 case MODE_TERMINAL:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002043 c1 = 't';
2044 break;
2045 default:
Bram Moolenaar6d057012021-12-31 18:49:43 +00002046 iemsg(_(e_makemap_illegal_mode));
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002047 return FAIL;
2048 }
2049 do // do this twice if c2 is set, 3 times with c3
2050 {
2051 // When outputting <> form, need to make sure that 'cpo'
2052 // is set to the Vim default.
2053 if (!did_cpo)
2054 {
2055 if (*mp->m_str == NUL) // will use <Nop>
2056 did_cpo = TRUE;
2057 else
2058 for (i = 0; i < 2; ++i)
2059 for (p = (i ? mp->m_str : mp->m_keys); *p; ++p)
2060 if (*p == K_SPECIAL || *p == NL)
2061 did_cpo = TRUE;
2062 if (did_cpo)
2063 {
2064 if (fprintf(fd, "let s:cpo_save=&cpo") < 0
2065 || put_eol(fd) < 0
2066 || fprintf(fd, "set cpo&vim") < 0
2067 || put_eol(fd) < 0)
2068 return FAIL;
2069 }
2070 }
2071 if (c1 && putc(c1, fd) < 0)
2072 return FAIL;
2073 if (mp->m_noremap != REMAP_YES && fprintf(fd, "nore") < 0)
2074 return FAIL;
2075 if (fputs(cmd, fd) < 0)
2076 return FAIL;
2077 if (buf != NULL && fputs(" <buffer>", fd) < 0)
2078 return FAIL;
2079 if (mp->m_nowait && fputs(" <nowait>", fd) < 0)
2080 return FAIL;
2081 if (mp->m_silent && fputs(" <silent>", fd) < 0)
2082 return FAIL;
2083#ifdef FEAT_EVAL
2084 if (mp->m_noremap == REMAP_SCRIPT
2085 && fputs("<script>", fd) < 0)
2086 return FAIL;
2087 if (mp->m_expr && fputs(" <expr>", fd) < 0)
2088 return FAIL;
2089#endif
2090
2091 if ( putc(' ', fd) < 0
2092 || put_escstr(fd, mp->m_keys, 0) == FAIL
2093 || putc(' ', fd) < 0
2094 || put_escstr(fd, mp->m_str, 1) == FAIL
2095 || put_eol(fd) < 0)
2096 return FAIL;
2097 c1 = c2;
2098 c2 = c3;
2099 c3 = NUL;
2100 } while (c1 != NUL);
2101 }
2102 }
2103
2104 if (did_cpo)
2105 if (fprintf(fd, "let &cpo=s:cpo_save") < 0
2106 || put_eol(fd) < 0
2107 || fprintf(fd, "unlet s:cpo_save") < 0
2108 || put_eol(fd) < 0)
2109 return FAIL;
2110 return OK;
2111}
2112
2113/*
2114 * write escape string to file
2115 * "what": 0 for :map lhs, 1 for :map rhs, 2 for :set
2116 *
2117 * return FAIL for failure, OK otherwise
2118 */
2119 int
2120put_escstr(FILE *fd, char_u *strstart, int what)
2121{
2122 char_u *str = strstart;
2123 int c;
2124 int modifiers;
2125
2126 // :map xx <Nop>
2127 if (*str == NUL && what == 1)
2128 {
2129 if (fprintf(fd, "<Nop>") < 0)
2130 return FAIL;
2131 return OK;
2132 }
2133
2134 for ( ; *str != NUL; ++str)
2135 {
2136 char_u *p;
2137
2138 // Check for a multi-byte character, which may contain escaped
2139 // K_SPECIAL and CSI bytes
2140 p = mb_unescape(&str);
2141 if (p != NULL)
2142 {
2143 while (*p != NUL)
2144 if (fputc(*p++, fd) < 0)
2145 return FAIL;
2146 --str;
2147 continue;
2148 }
2149
2150 c = *str;
2151 // Special key codes have to be translated to be able to make sense
2152 // when they are read back.
2153 if (c == K_SPECIAL && what != 2)
2154 {
Bram Moolenaar02c037a2020-08-30 19:26:45 +02002155 modifiers = 0;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002156 if (str[1] == KS_MODIFIER)
2157 {
2158 modifiers = str[2];
2159 str += 3;
2160 c = *str;
2161 }
2162 if (c == K_SPECIAL)
2163 {
2164 c = TO_SPECIAL(str[1], str[2]);
2165 str += 2;
2166 }
2167 if (IS_SPECIAL(c) || modifiers) // special key
2168 {
2169 if (fputs((char *)get_special_key_name(c, modifiers), fd) < 0)
2170 return FAIL;
2171 continue;
2172 }
2173 }
2174
2175 // A '\n' in a map command should be written as <NL>.
2176 // A '\n' in a set command should be written as \^V^J.
2177 if (c == NL)
2178 {
2179 if (what == 2)
2180 {
Bram Moolenaar424bcae2022-01-31 14:59:41 +00002181 if (fprintf(fd, "\\\026\n") < 0)
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002182 return FAIL;
2183 }
2184 else
2185 {
2186 if (fprintf(fd, "<NL>") < 0)
2187 return FAIL;
2188 }
2189 continue;
2190 }
2191
2192 // Some characters have to be escaped with CTRL-V to
2193 // prevent them from misinterpreted in DoOneCmd().
2194 // A space, Tab and '"' has to be escaped with a backslash to
2195 // prevent it to be misinterpreted in do_set().
2196 // A space has to be escaped with a CTRL-V when it's at the start of a
2197 // ":map" rhs.
2198 // A '<' has to be escaped with a CTRL-V to prevent it being
2199 // interpreted as the start of a special key name.
2200 // A space in the lhs of a :map needs a CTRL-V.
2201 if (what == 2 && (VIM_ISWHITE(c) || c == '"' || c == '\\'))
2202 {
2203 if (putc('\\', fd) < 0)
2204 return FAIL;
2205 }
2206 else if (c < ' ' || c > '~' || c == '|'
2207 || (what == 0 && c == ' ')
2208 || (what == 1 && str == strstart && c == ' ')
2209 || (what != 2 && c == '<'))
2210 {
2211 if (putc(Ctrl_V, fd) < 0)
2212 return FAIL;
2213 }
2214 if (putc(c, fd) < 0)
2215 return FAIL;
2216 }
2217 return OK;
2218}
2219
2220/*
2221 * Check all mappings for the presence of special key codes.
2222 * Used after ":set term=xxx".
2223 */
2224 void
2225check_map_keycodes(void)
2226{
2227 mapblock_T *mp;
2228 char_u *p;
2229 int i;
2230 char_u buf[3];
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002231 int abbr;
2232 int hash;
2233 buf_T *bp;
ichizok7e5fe382023-04-15 13:17:50 +01002234 ESTACK_CHECK_DECLARATION;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002235
2236 validate_maphash();
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002237 // avoids giving error messages
2238 estack_push(ETYPE_INTERNAL, (char_u *)"mappings", 0);
ichizok7e5fe382023-04-15 13:17:50 +01002239 ESTACK_CHECK_SETUP;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002240
Bram Moolenaar32aa1022019-11-02 22:54:41 +01002241 // Do this once for each buffer, and then once for global
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002242 // mappings/abbreviations with bp == NULL
2243 for (bp = firstbuf; ; bp = bp->b_next)
2244 {
2245 // Do the loop twice: Once for mappings, once for abbreviations.
2246 // Then loop over all map hash lists.
2247 for (abbr = 0; abbr <= 1; ++abbr)
2248 for (hash = 0; hash < 256; ++hash)
2249 {
2250 if (abbr)
2251 {
2252 if (hash) // there is only one abbr list
2253 break;
2254 if (bp != NULL)
2255 mp = bp->b_first_abbr;
2256 else
2257 mp = first_abbr;
2258 }
2259 else
2260 {
2261 if (bp != NULL)
2262 mp = bp->b_maphash[hash];
2263 else
2264 mp = maphash[hash];
2265 }
2266 for ( ; mp != NULL; mp = mp->m_next)
2267 {
2268 for (i = 0; i <= 1; ++i) // do this twice
2269 {
2270 if (i == 0)
2271 p = mp->m_keys; // once for the "from" part
2272 else
2273 p = mp->m_str; // and once for the "to" part
2274 while (*p)
2275 {
2276 if (*p == K_SPECIAL)
2277 {
2278 ++p;
2279 if (*p < 128) // for "normal" tcap entries
2280 {
2281 buf[0] = p[0];
2282 buf[1] = p[1];
2283 buf[2] = NUL;
2284 (void)add_termcap_entry(buf, FALSE);
2285 }
2286 ++p;
2287 }
2288 ++p;
2289 }
2290 }
2291 }
2292 }
2293 if (bp == NULL)
2294 break;
2295 }
ichizok7e5fe382023-04-15 13:17:50 +01002296 ESTACK_CHECK_NOW;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002297 estack_pop();
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002298}
2299
2300#if defined(FEAT_EVAL) || defined(PROTO)
2301/*
2302 * Check the string "keys" against the lhs of all mappings.
2303 * Return pointer to rhs of mapping (mapblock->m_str).
2304 * NULL when no mapping found.
2305 */
2306 char_u *
2307check_map(
2308 char_u *keys,
2309 int mode,
2310 int exact, // require exact match
2311 int ign_mod, // ignore preceding modifier
2312 int abbr, // do abbreviations
2313 mapblock_T **mp_ptr, // return: pointer to mapblock or NULL
2314 int *local_ptr) // return: buffer-local mapping or NULL
2315{
2316 int hash;
2317 int len, minlen;
2318 mapblock_T *mp;
2319 char_u *s;
2320 int local;
2321
2322 validate_maphash();
2323
2324 len = (int)STRLEN(keys);
2325 for (local = 1; local >= 0; --local)
2326 // loop over all hash lists
2327 for (hash = 0; hash < 256; ++hash)
2328 {
2329 if (abbr)
2330 {
2331 if (hash > 0) // there is only one list.
2332 break;
2333 if (local)
2334 mp = curbuf->b_first_abbr;
2335 else
2336 mp = first_abbr;
2337 }
2338 else if (local)
2339 mp = curbuf->b_maphash[hash];
2340 else
2341 mp = maphash[hash];
2342 for ( ; mp != NULL; mp = mp->m_next)
2343 {
2344 // skip entries with wrong mode, wrong length and not matching
2345 // ones
2346 if ((mp->m_mode & mode) && (!exact || mp->m_keylen == len))
2347 {
2348 if (len > mp->m_keylen)
2349 minlen = mp->m_keylen;
2350 else
2351 minlen = len;
2352 s = mp->m_keys;
2353 if (ign_mod && s[0] == K_SPECIAL && s[1] == KS_MODIFIER
2354 && s[2] != NUL)
2355 {
2356 s += 3;
2357 if (len > mp->m_keylen - 3)
2358 minlen = mp->m_keylen - 3;
2359 }
2360 if (STRNCMP(s, keys, minlen) == 0)
2361 {
2362 if (mp_ptr != NULL)
2363 *mp_ptr = mp;
2364 if (local_ptr != NULL)
2365 *local_ptr = local;
2366 return mp->m_str;
2367 }
2368 }
2369 }
2370 }
2371
2372 return NULL;
2373}
2374
Ernie Rael659c2402022-04-24 18:40:28 +01002375/*
zeertzjqc207fd22022-06-29 10:37:40 +01002376 * "hasmapto()" function
2377 */
2378 void
2379f_hasmapto(typval_T *argvars, typval_T *rettv)
2380{
2381 char_u *name;
2382 char_u *mode;
2383 char_u buf[NUMBUFLEN];
2384 int abbr = FALSE;
2385
2386 if (in_vim9script()
2387 && (check_for_string_arg(argvars, 0) == FAIL
2388 || check_for_opt_string_arg(argvars, 1) == FAIL
2389 || (argvars[1].v_type != VAR_UNKNOWN
2390 && check_for_opt_bool_arg(argvars, 2) == FAIL)))
2391 return;
2392
2393 name = tv_get_string(&argvars[0]);
2394 if (argvars[1].v_type == VAR_UNKNOWN)
2395 mode = (char_u *)"nvo";
2396 else
2397 {
2398 mode = tv_get_string_buf(&argvars[1], buf);
2399 if (argvars[2].v_type != VAR_UNKNOWN)
2400 abbr = (int)tv_get_bool(&argvars[2]);
2401 }
2402
2403 if (map_to_exists(name, mode, abbr))
2404 rettv->vval.v_number = TRUE;
2405 else
2406 rettv->vval.v_number = FALSE;
2407}
2408
2409/*
Ernie Rael659c2402022-04-24 18:40:28 +01002410 * Fill in the empty dictionary with items as defined by maparg builtin.
2411 */
2412 static void
2413mapblock2dict(
2414 mapblock_T *mp,
2415 dict_T *dict,
2416 char_u *lhsrawalt, // may be NULL
Ernie Rael51d04d12022-05-04 15:40:22 +01002417 int buffer_local, // false if not buffer local mapping
2418 int abbr) // true if abbreviation
Ernie Rael659c2402022-04-24 18:40:28 +01002419{
zeertzjqcdc83932022-09-12 13:38:41 +01002420 char_u *lhs = str2special_save(mp->m_keys, TRUE, FALSE);
Ernie Rael659c2402022-04-24 18:40:28 +01002421 char_u *mapmode = map_mode_to_chars(mp->m_mode);
2422
2423 dict_add_string(dict, "lhs", lhs);
2424 vim_free(lhs);
2425 dict_add_string(dict, "lhsraw", mp->m_keys);
2426 if (lhsrawalt)
2427 // Also add the value for the simplified entry.
2428 dict_add_string(dict, "lhsrawalt", lhsrawalt);
2429 dict_add_string(dict, "rhs", mp->m_orig_str);
2430 dict_add_number(dict, "noremap", mp->m_noremap ? 1L : 0L);
2431 dict_add_number(dict, "script", mp->m_noremap == REMAP_SCRIPT
2432 ? 1L : 0L);
2433 dict_add_number(dict, "expr", mp->m_expr ? 1L : 0L);
2434 dict_add_number(dict, "silent", mp->m_silent ? 1L : 0L);
2435 dict_add_number(dict, "sid", (long)mp->m_script_ctx.sc_sid);
2436 dict_add_number(dict, "scriptversion",
2437 (long)mp->m_script_ctx.sc_version);
2438 dict_add_number(dict, "lnum", (long)mp->m_script_ctx.sc_lnum);
2439 dict_add_number(dict, "buffer", (long)buffer_local);
2440 dict_add_number(dict, "nowait", mp->m_nowait ? 1L : 0L);
2441 dict_add_string(dict, "mode", mapmode);
Ernie Rael51d04d12022-05-04 15:40:22 +01002442 dict_add_number(dict, "abbr", abbr ? 1L : 0L);
Ernie Raeld8f5f762022-05-10 17:50:39 +01002443 dict_add_number(dict, "mode_bits", mp->m_mode);
Ernie Rael659c2402022-04-24 18:40:28 +01002444
2445 vim_free(mapmode);
2446}
2447
Yegappan Lakshmanan4a155042021-07-30 21:32:45 +02002448 static void
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002449get_maparg(typval_T *argvars, typval_T *rettv, int exact)
2450{
2451 char_u *keys;
Bram Moolenaar9c652532020-05-24 13:10:18 +02002452 char_u *keys_simplified;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002453 char_u *which;
2454 char_u buf[NUMBUFLEN];
2455 char_u *keys_buf = NULL;
Bram Moolenaar9c652532020-05-24 13:10:18 +02002456 char_u *alt_keys_buf = NULL;
2457 int did_simplify = FALSE;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002458 char_u *rhs;
2459 int mode;
2460 int abbr = FALSE;
2461 int get_dict = FALSE;
zeertzjq2c8a7eb2022-04-26 21:36:21 +01002462 mapblock_T *mp = NULL;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002463 int buffer_local;
Bram Moolenaar9c652532020-05-24 13:10:18 +02002464 int flags = REPTERM_FROM_PART | REPTERM_DO_LT;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002465
2466 // return empty string for failure
2467 rettv->v_type = VAR_STRING;
2468 rettv->vval.v_string = NULL;
2469
2470 keys = tv_get_string(&argvars[0]);
2471 if (*keys == NUL)
2472 return;
2473
2474 if (argvars[1].v_type != VAR_UNKNOWN)
2475 {
2476 which = tv_get_string_buf_chk(&argvars[1], buf);
2477 if (argvars[2].v_type != VAR_UNKNOWN)
2478 {
Bram Moolenaar04d594b2020-09-02 22:25:35 +02002479 abbr = (int)tv_get_bool(&argvars[2]);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002480 if (argvars[3].v_type != VAR_UNKNOWN)
Bram Moolenaar04d594b2020-09-02 22:25:35 +02002481 get_dict = (int)tv_get_bool(&argvars[3]);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002482 }
2483 }
2484 else
2485 which = (char_u *)"";
2486 if (which == NULL)
2487 return;
2488
2489 mode = get_map_mode(&which, 0);
2490
Bram Moolenaar9c652532020-05-24 13:10:18 +02002491 keys_simplified = replace_termcodes(keys, &keys_buf, flags, &did_simplify);
2492 rhs = check_map(keys_simplified, mode, exact, FALSE, abbr,
2493 &mp, &buffer_local);
2494 if (did_simplify)
2495 {
2496 // When the lhs is being simplified the not-simplified keys are
Bram Moolenaar8e7d6222020-12-18 19:49:56 +01002497 // preferred for printing, like in do_map().
Bram Moolenaar9c652532020-05-24 13:10:18 +02002498 (void)replace_termcodes(keys, &alt_keys_buf,
2499 flags | REPTERM_NO_SIMPLIFY, NULL);
2500 rhs = check_map(alt_keys_buf, mode, exact, FALSE, abbr, &mp,
2501 &buffer_local);
2502 }
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002503
2504 if (!get_dict)
2505 {
2506 // Return a string.
2507 if (rhs != NULL)
2508 {
2509 if (*rhs == NUL)
2510 rettv->vval.v_string = vim_strsave((char_u *)"<Nop>");
2511 else
zeertzjqcdc83932022-09-12 13:38:41 +01002512 rettv->vval.v_string = str2special_save(rhs, FALSE, FALSE);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002513 }
2514
2515 }
Bram Moolenaar93a10962022-06-16 11:42:09 +01002516 else if (rettv_dict_alloc(rettv) == OK && rhs != NULL)
Ernie Rael659c2402022-04-24 18:40:28 +01002517 mapblock2dict(mp, rettv->vval.v_dict,
Ernie Rael51d04d12022-05-04 15:40:22 +01002518 did_simplify ? keys_simplified : NULL,
2519 buffer_local, abbr);
Bram Moolenaar9c652532020-05-24 13:10:18 +02002520
2521 vim_free(keys_buf);
2522 vim_free(alt_keys_buf);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002523}
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002524
2525/*
Ernie Rael09661202022-04-25 14:40:44 +01002526 * "maplist()" function
Ernie Rael659c2402022-04-24 18:40:28 +01002527 */
2528 void
Ernie Rael09661202022-04-25 14:40:44 +01002529f_maplist(typval_T *argvars UNUSED, typval_T *rettv)
Ernie Rael659c2402022-04-24 18:40:28 +01002530{
2531 dict_T *d;
2532 mapblock_T *mp;
2533 int buffer_local;
2534 char_u *keys_buf;
2535 int did_simplify;
2536 int hash;
2537 char_u *lhs;
2538 const int flags = REPTERM_FROM_PART | REPTERM_DO_LT;
Ernie Rael09661202022-04-25 14:40:44 +01002539 int abbr = FALSE;
2540
2541 if (in_vim9script() && check_for_opt_bool_arg(argvars, 0) == FAIL)
2542 return;
2543 if (argvars[0].v_type != VAR_UNKNOWN)
2544 abbr = tv_get_bool(&argvars[0]);
Ernie Rael659c2402022-04-24 18:40:28 +01002545
Bram Moolenaar93a10962022-06-16 11:42:09 +01002546 if (rettv_list_alloc(rettv) == FAIL)
Ernie Rael659c2402022-04-24 18:40:28 +01002547 return;
2548
2549 validate_maphash();
2550
2551 // Do it twice: once for global maps and once for local maps.
2552 for (buffer_local = 0; buffer_local <= 1; ++buffer_local)
2553 {
2554 for (hash = 0; hash < 256; ++hash)
2555 {
Ernie Rael09661202022-04-25 14:40:44 +01002556 if (abbr)
2557 {
2558 if (hash > 0) // there is only one abbr list
2559 break;
2560 if (buffer_local)
2561 mp = curbuf->b_first_abbr;
2562 else
2563 mp = first_abbr;
2564 }
2565 else if (buffer_local)
Ernie Rael659c2402022-04-24 18:40:28 +01002566 mp = curbuf->b_maphash[hash];
2567 else
2568 mp = maphash[hash];
2569 for (; mp; mp = mp->m_next)
2570 {
2571 if (mp->m_simplified)
2572 continue;
2573 if ((d = dict_alloc()) == NULL)
2574 return;
2575 if (list_append_dict(rettv->vval.v_list, d) == FAIL)
2576 return;
2577
2578 keys_buf = NULL;
2579 did_simplify = FALSE;
2580
zeertzjqcdc83932022-09-12 13:38:41 +01002581 lhs = str2special_save(mp->m_keys, TRUE, FALSE);
Ernie Rael659c2402022-04-24 18:40:28 +01002582 (void)replace_termcodes(lhs, &keys_buf, flags, &did_simplify);
2583 vim_free(lhs);
2584
2585 mapblock2dict(mp, d,
Ernie Rael51d04d12022-05-04 15:40:22 +01002586 did_simplify ? keys_buf : NULL,
2587 buffer_local, abbr);
Ernie Rael659c2402022-04-24 18:40:28 +01002588 vim_free(keys_buf);
2589 }
2590 }
2591 }
2592}
2593
2594/*
Yegappan Lakshmanan4a155042021-07-30 21:32:45 +02002595 * "maparg()" function
2596 */
2597 void
2598f_maparg(typval_T *argvars, typval_T *rettv)
2599{
2600 if (in_vim9script()
2601 && (check_for_string_arg(argvars, 0) == FAIL
2602 || check_for_opt_string_arg(argvars, 1) == FAIL
2603 || (argvars[1].v_type != VAR_UNKNOWN
2604 && (check_for_opt_bool_arg(argvars, 2) == FAIL
2605 || (argvars[2].v_type != VAR_UNKNOWN
2606 && check_for_opt_bool_arg(argvars, 3) == FAIL)))))
2607 return;
2608
2609 get_maparg(argvars, rettv, TRUE);
2610}
2611
2612/*
2613 * "mapcheck()" function
2614 */
2615 void
2616f_mapcheck(typval_T *argvars, typval_T *rettv)
2617{
2618 if (in_vim9script()
2619 && (check_for_string_arg(argvars, 0) == FAIL
2620 || check_for_opt_string_arg(argvars, 1) == FAIL
2621 || (argvars[1].v_type != VAR_UNKNOWN
2622 && check_for_opt_bool_arg(argvars, 2) == FAIL)))
2623 return;
2624
2625 get_maparg(argvars, rettv, FALSE);
2626}
2627
2628/*
Ernie Rael51d04d12022-05-04 15:40:22 +01002629 * Get the mapping mode from the mode string.
2630 * It may contain multiple characters, eg "nox", or "!", or ' '
2631 * Return 0 if there is an error.
2632 */
2633 static int
2634get_map_mode_string(char_u *mode_string, int abbr)
2635{
2636 char_u *p = mode_string;
2637 int mode = 0;
2638 int tmode;
2639 int modec;
Bram Moolenaar24959102022-05-07 20:01:16 +01002640 const int MASK_V = MODE_VISUAL | MODE_SELECT;
2641 const int MASK_MAP = MODE_VISUAL | MODE_SELECT | MODE_NORMAL
2642 | MODE_OP_PENDING;
2643 const int MASK_BANG = MODE_INSERT | MODE_CMDLINE;
Ernie Rael51d04d12022-05-04 15:40:22 +01002644
2645 if (*p == NUL)
2646 p = (char_u *)" "; // compatibility
2647 while ((modec = *p++))
2648 {
2649 switch (modec)
2650 {
Bram Moolenaar24959102022-05-07 20:01:16 +01002651 case 'i': tmode = MODE_INSERT; break;
2652 case 'l': tmode = MODE_LANGMAP; break;
2653 case 'c': tmode = MODE_CMDLINE; break;
2654 case 'n': tmode = MODE_NORMAL; break;
2655 case 'x': tmode = MODE_VISUAL; break;
2656 case 's': tmode = MODE_SELECT; break;
2657 case 'o': tmode = MODE_OP_PENDING; break;
2658 case 't': tmode = MODE_TERMINAL; break;
Ernie Rael51d04d12022-05-04 15:40:22 +01002659 case 'v': tmode = MASK_V; break;
2660 case '!': tmode = MASK_BANG; break;
2661 case ' ': tmode = MASK_MAP; break;
2662 default:
2663 return 0; // error, unknown mode character
2664 }
2665 mode |= tmode;
2666 }
2667 if ((abbr && (mode & ~MASK_BANG) != 0)
2668 || (!abbr && (mode & (mode-1)) != 0 // more than one bit set
2669 && (
2670 // false if multiple bits set in mode and mode is fully
2671 // contained in one mask
2672 !(((mode & MASK_BANG) != 0 && (mode & ~MASK_BANG) == 0)
2673 || ((mode & MASK_MAP) != 0 && (mode & ~MASK_MAP) == 0)))))
2674 return 0;
2675
2676 return mode;
2677}
2678
2679/*
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002680 * "mapset()" function
2681 */
2682 void
2683f_mapset(typval_T *argvars, typval_T *rettv UNUSED)
2684{
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002685 char_u *which;
2686 int mode;
2687 char_u buf[NUMBUFLEN];
2688 int is_abbr;
2689 dict_T *d;
2690 char_u *lhs;
Bram Moolenaar9c652532020-05-24 13:10:18 +02002691 char_u *lhsraw;
2692 char_u *lhsrawalt;
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002693 char_u *rhs;
Bram Moolenaarc94c1462020-05-22 20:01:06 +02002694 char_u *orig_rhs;
2695 char_u *arg_buf = NULL;
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002696 int noremap;
2697 int expr;
2698 int silent;
Bram Moolenaar7ba1e4d2021-04-24 13:12:38 +02002699 int buffer;
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002700 scid_T sid;
Bram Moolenaara9528b32022-01-18 20:51:35 +00002701 int scriptversion;
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002702 linenr_T lnum;
2703 mapblock_T **map_table = maphash;
2704 mapblock_T **abbr_table = &first_abbr;
2705 int nowait;
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002706 char_u *arg;
Ernie Rael51d04d12022-05-04 15:40:22 +01002707 int dict_only;
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002708
Ernie Rael51d04d12022-05-04 15:40:22 +01002709 // If first arg is a dict, then that's the only arg permitted.
2710 dict_only = argvars[0].v_type == VAR_DICT;
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02002711 if (in_vim9script()
Ernie Rael51d04d12022-05-04 15:40:22 +01002712 && (check_for_string_or_dict_arg(argvars, 0) == FAIL
2713 || (dict_only && check_for_unknown_arg(argvars, 1) == FAIL)
2714 || (!dict_only
2715 && (check_for_string_arg(argvars, 0) == FAIL
2716 || check_for_bool_arg(argvars, 1) == FAIL
2717 || check_for_dict_arg(argvars, 2) == FAIL))))
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02002718 return;
2719
Ernie Rael51d04d12022-05-04 15:40:22 +01002720 if (dict_only)
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002721 {
Ernie Rael51d04d12022-05-04 15:40:22 +01002722 d = argvars[0].vval.v_dict;
Bram Moolenaard61efa52022-07-23 09:52:04 +01002723 which = dict_get_string(d, "mode", FALSE);
2724 is_abbr = dict_get_bool(d, "abbr", -1);
Ernie Rael51d04d12022-05-04 15:40:22 +01002725 if (which == NULL || is_abbr < 0)
2726 {
2727 emsg(_(e_entries_missing_in_mapset_dict_argument));
2728 return;
2729 }
2730 }
2731 else
2732 {
2733 which = tv_get_string_buf_chk(&argvars[0], buf);
2734 if (which == NULL)
2735 return;
2736 is_abbr = (int)tv_get_bool(&argvars[1]);
2737
Yegappan Lakshmanan04c4c572022-08-30 19:48:24 +01002738 if (check_for_dict_arg(argvars, 2) == FAIL)
Ernie Rael51d04d12022-05-04 15:40:22 +01002739 return;
Ernie Rael51d04d12022-05-04 15:40:22 +01002740 d = argvars[2].vval.v_dict;
2741 }
2742 mode = get_map_mode_string(which, is_abbr);
2743 if (mode == 0)
2744 {
2745 semsg(_(e_illegal_map_mode_string_str), which);
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002746 return;
2747 }
Ernie Rael51d04d12022-05-04 15:40:22 +01002748
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002749
2750 // Get the values in the same order as above in get_maparg().
Bram Moolenaard61efa52022-07-23 09:52:04 +01002751 lhs = dict_get_string(d, "lhs", FALSE);
2752 lhsraw = dict_get_string(d, "lhsraw", FALSE);
2753 lhsrawalt = dict_get_string(d, "lhsrawalt", FALSE);
2754 rhs = dict_get_string(d, "rhs", FALSE);
Bram Moolenaar9c652532020-05-24 13:10:18 +02002755 if (lhs == NULL || lhsraw == NULL || rhs == NULL)
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002756 {
Bram Moolenaarb09feaa2022-01-02 20:20:45 +00002757 emsg(_(e_entries_missing_in_mapset_dict_argument));
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002758 return;
2759 }
Bram Moolenaarc94c1462020-05-22 20:01:06 +02002760 orig_rhs = rhs;
zeertzjq92a3d202022-08-31 16:40:17 +01002761 if (STRICMP(rhs, "<nop>") == 0) // "<Nop>" means nothing
2762 rhs = (char_u *)"";
2763 else
2764 rhs = replace_termcodes(rhs, &arg_buf,
Bram Moolenaarc94c1462020-05-22 20:01:06 +02002765 REPTERM_DO_LT | REPTERM_SPECIAL, NULL);
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002766
Bram Moolenaard61efa52022-07-23 09:52:04 +01002767 noremap = dict_get_number(d, "noremap") ? REMAP_NONE: 0;
2768 if (dict_get_number(d, "script") != 0)
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002769 noremap = REMAP_SCRIPT;
Bram Moolenaard61efa52022-07-23 09:52:04 +01002770 expr = dict_get_number(d, "expr") != 0;
2771 silent = dict_get_number(d, "silent") != 0;
2772 sid = dict_get_number(d, "sid");
2773 scriptversion = dict_get_number(d, "scriptversion");
2774 lnum = dict_get_number(d, "lnum");
2775 buffer = dict_get_number(d, "buffer");
2776 nowait = dict_get_number(d, "nowait") != 0;
Bram Moolenaar7ba1e4d2021-04-24 13:12:38 +02002777 // mode from the dict is not used
2778
2779 if (buffer)
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002780 {
2781 map_table = curbuf->b_maphash;
2782 abbr_table = &curbuf->b_first_abbr;
2783 }
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002784
2785 // Delete any existing mapping for this lhs and mode.
Bram Moolenaar7ba1e4d2021-04-24 13:12:38 +02002786 if (buffer)
2787 {
2788 arg = alloc(STRLEN(lhs) + STRLEN("<buffer>") + 1);
2789 if (arg == NULL)
2790 return;
2791 STRCPY(arg, "<buffer>");
2792 STRCPY(arg + 8, lhs);
2793 }
2794 else
2795 {
2796 arg = vim_strsave(lhs);
2797 if (arg == NULL)
2798 return;
2799 }
zeertzjq44068e92022-06-16 11:14:55 +01002800 do_map(MAPTYPE_UNMAP, arg, mode, is_abbr);
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002801 vim_free(arg);
2802
Bram Moolenaar9c652532020-05-24 13:10:18 +02002803 (void)map_add(map_table, abbr_table, lhsraw, rhs, orig_rhs, noremap,
Bram Moolenaara9528b32022-01-18 20:51:35 +00002804 nowait, silent, mode, is_abbr, expr, sid, scriptversion, lnum, 0);
Bram Moolenaar9c652532020-05-24 13:10:18 +02002805 if (lhsrawalt != NULL)
2806 (void)map_add(map_table, abbr_table, lhsrawalt, rhs, orig_rhs, noremap,
Bram Moolenaara9528b32022-01-18 20:51:35 +00002807 nowait, silent, mode, is_abbr, expr, sid, scriptversion,
2808 lnum, 1);
Bram Moolenaarc94c1462020-05-22 20:01:06 +02002809 vim_free(arg_buf);
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002810}
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002811#endif
2812
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002813
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002814#if defined(MSWIN) || defined(MACOS_X)
2815
Bram Moolenaar24959102022-05-07 20:01:16 +01002816# define VIS_SEL (MODE_VISUAL | MODE_SELECT) // abbreviation
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002817
2818/*
2819 * Default mappings for some often used keys.
2820 */
2821struct initmap
2822{
2823 char_u *arg;
2824 int mode;
2825};
2826
2827# ifdef FEAT_GUI_MSWIN
2828// Use the Windows (CUA) keybindings. (GUI)
2829static struct initmap initmappings[] =
2830{
2831 // paste, copy and cut
Bram Moolenaar24959102022-05-07 20:01:16 +01002832 {(char_u *)"<S-Insert> \"*P", MODE_NORMAL},
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002833 {(char_u *)"<S-Insert> \"-d\"*P", VIS_SEL},
Bram Moolenaar24959102022-05-07 20:01:16 +01002834 {(char_u *)"<S-Insert> <C-R><C-O>*", MODE_INSERT | MODE_CMDLINE},
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002835 {(char_u *)"<C-Insert> \"*y", VIS_SEL},
2836 {(char_u *)"<S-Del> \"*d", VIS_SEL},
2837 {(char_u *)"<C-Del> \"*d", VIS_SEL},
2838 {(char_u *)"<C-X> \"*d", VIS_SEL},
2839 // Missing: CTRL-C (cancel) and CTRL-V (block selection)
2840};
2841# endif
2842
2843# if defined(MSWIN) && (!defined(FEAT_GUI) || defined(VIMDLL))
2844// Use the Windows (CUA) keybindings. (Console)
2845static struct initmap cinitmappings[] =
2846{
Bram Moolenaar24959102022-05-07 20:01:16 +01002847 {(char_u *)"\316w <C-Home>", MODE_NORMAL | VIS_SEL},
2848 {(char_u *)"\316w <C-Home>", MODE_INSERT | MODE_CMDLINE},
2849 {(char_u *)"\316u <C-End>", MODE_NORMAL | VIS_SEL},
2850 {(char_u *)"\316u <C-End>", MODE_INSERT | MODE_CMDLINE},
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002851
2852 // paste, copy and cut
2853# ifdef FEAT_CLIPBOARD
Bram Moolenaar24959102022-05-07 20:01:16 +01002854 {(char_u *)"\316\324 \"*P", MODE_NORMAL}, // SHIFT-Insert is "*P
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002855 {(char_u *)"\316\324 \"-d\"*P", VIS_SEL}, // SHIFT-Insert is "-d"*P
Bram Moolenaar24959102022-05-07 20:01:16 +01002856 {(char_u *)"\316\324 \022\017*", MODE_INSERT}, // SHIFT-Insert is ^R^O*
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002857 {(char_u *)"\316\325 \"*y", VIS_SEL}, // CTRL-Insert is "*y
2858 {(char_u *)"\316\327 \"*d", VIS_SEL}, // SHIFT-Del is "*d
2859 {(char_u *)"\316\330 \"*d", VIS_SEL}, // CTRL-Del is "*d
2860 {(char_u *)"\030 \"*d", VIS_SEL}, // CTRL-X is "*d
2861# else
Bram Moolenaar24959102022-05-07 20:01:16 +01002862 {(char_u *)"\316\324 P", MODE_NORMAL}, // SHIFT-Insert is P
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002863 {(char_u *)"\316\324 \"-dP", VIS_SEL}, // SHIFT-Insert is "-dP
Bram Moolenaar24959102022-05-07 20:01:16 +01002864 {(char_u *)"\316\324 \022\017\"", MODE_INSERT}, // SHIFT-Insert is ^R^O"
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002865 {(char_u *)"\316\325 y", VIS_SEL}, // CTRL-Insert is y
2866 {(char_u *)"\316\327 d", VIS_SEL}, // SHIFT-Del is d
2867 {(char_u *)"\316\330 d", VIS_SEL}, // CTRL-Del is d
2868# endif
2869};
2870# endif
2871
2872# if defined(MACOS_X)
2873static struct initmap initmappings[] =
2874{
2875 // Use the Standard MacOS binding.
2876 // paste, copy and cut
Bram Moolenaar24959102022-05-07 20:01:16 +01002877 {(char_u *)"<D-v> \"*P", MODE_NORMAL},
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002878 {(char_u *)"<D-v> \"-d\"*P", VIS_SEL},
Bram Moolenaar24959102022-05-07 20:01:16 +01002879 {(char_u *)"<D-v> <C-R>*", MODE_INSERT | MODE_CMDLINE},
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002880 {(char_u *)"<D-c> \"*y", VIS_SEL},
2881 {(char_u *)"<D-x> \"*d", VIS_SEL},
2882 {(char_u *)"<Backspace> \"-d", VIS_SEL},
2883};
2884# endif
2885
2886# undef VIS_SEL
2887#endif
2888
2889/*
2890 * Set up default mappings.
2891 */
2892 void
2893init_mappings(void)
2894{
2895#if defined(MSWIN) || defined(MACOS_X)
2896 int i;
2897
2898# if defined(MSWIN) && (!defined(FEAT_GUI_MSWIN) || defined(VIMDLL))
2899# ifdef VIMDLL
2900 if (!gui.starting)
2901# endif
2902 {
K.Takataeeec2542021-06-02 13:28:16 +02002903 for (i = 0; i < (int)ARRAY_LENGTH(cinitmappings); ++i)
zeertzjq44068e92022-06-16 11:14:55 +01002904 add_map(cinitmappings[i].arg, cinitmappings[i].mode, FALSE);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002905 }
2906# endif
2907# if defined(FEAT_GUI_MSWIN) || defined(MACOS_X)
K.Takataeeec2542021-06-02 13:28:16 +02002908 for (i = 0; i < (int)ARRAY_LENGTH(initmappings); ++i)
zeertzjq44068e92022-06-16 11:14:55 +01002909 add_map(initmappings[i].arg, initmappings[i].mode, FALSE);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002910# endif
2911#endif
2912}
2913
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002914/*
2915 * Add a mapping "map" for mode "mode".
zeertzjq44068e92022-06-16 11:14:55 +01002916 * When "nore" is TRUE use MAPTYPE_NOREMAP.
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002917 * Need to put string in allocated memory, because do_map() will modify it.
2918 */
2919 void
zeertzjq44068e92022-06-16 11:14:55 +01002920add_map(char_u *map, int mode, int nore)
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002921{
2922 char_u *s;
2923 char_u *cpo_save = p_cpo;
2924
Bram Moolenaare5a2dc82021-01-03 19:52:05 +01002925 p_cpo = empty_option; // Allow <> notation
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002926 s = vim_strsave(map);
2927 if (s != NULL)
2928 {
zeertzjq44068e92022-06-16 11:14:55 +01002929 (void)do_map(nore ? MAPTYPE_NOREMAP : MAPTYPE_MAP, s, mode, FALSE);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002930 vim_free(s);
2931 }
2932 p_cpo = cpo_save;
2933}
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002934
Bram Moolenaare677df82019-09-02 22:31:11 +02002935#if defined(FEAT_LANGMAP) || defined(PROTO)
2936/*
2937 * Any character has an equivalent 'langmap' character. This is used for
2938 * keyboards that have a special language mode that sends characters above
2939 * 128 (although other characters can be translated too). The "to" field is a
2940 * Vim command character. This avoids having to switch the keyboard back to
2941 * ASCII mode when leaving Insert mode.
2942 *
2943 * langmap_mapchar[] maps any of 256 chars to an ASCII char used for Vim
2944 * commands.
2945 * langmap_mapga.ga_data is a sorted table of langmap_entry_T. This does the
2946 * same as langmap_mapchar[] for characters >= 256.
2947 *
2948 * Use growarray for 'langmap' chars >= 256
2949 */
2950typedef struct
2951{
2952 int from;
2953 int to;
2954} langmap_entry_T;
2955
2956static garray_T langmap_mapga;
2957
2958/*
2959 * Search for an entry in "langmap_mapga" for "from". If found set the "to"
2960 * field. If not found insert a new entry at the appropriate location.
2961 */
2962 static void
2963langmap_set_entry(int from, int to)
2964{
2965 langmap_entry_T *entries = (langmap_entry_T *)(langmap_mapga.ga_data);
2966 int a = 0;
2967 int b = langmap_mapga.ga_len;
2968
2969 // Do a binary search for an existing entry.
2970 while (a != b)
2971 {
2972 int i = (a + b) / 2;
2973 int d = entries[i].from - from;
2974
2975 if (d == 0)
2976 {
2977 entries[i].to = to;
2978 return;
2979 }
2980 if (d < 0)
2981 a = i + 1;
2982 else
2983 b = i;
2984 }
2985
Yegappan Lakshmananfadc02a2023-01-27 21:03:12 +00002986 if (ga_grow(&langmap_mapga, 1) == FAIL)
Bram Moolenaare677df82019-09-02 22:31:11 +02002987 return; // out of memory
2988
2989 // insert new entry at position "a"
2990 entries = (langmap_entry_T *)(langmap_mapga.ga_data) + a;
2991 mch_memmove(entries + 1, entries,
2992 (langmap_mapga.ga_len - a) * sizeof(langmap_entry_T));
2993 ++langmap_mapga.ga_len;
2994 entries[0].from = from;
2995 entries[0].to = to;
2996}
2997
2998/*
2999 * Apply 'langmap' to multi-byte character "c" and return the result.
3000 */
3001 int
3002langmap_adjust_mb(int c)
3003{
3004 langmap_entry_T *entries = (langmap_entry_T *)(langmap_mapga.ga_data);
3005 int a = 0;
3006 int b = langmap_mapga.ga_len;
3007
3008 while (a != b)
3009 {
3010 int i = (a + b) / 2;
3011 int d = entries[i].from - c;
3012
3013 if (d == 0)
3014 return entries[i].to; // found matching entry
3015 if (d < 0)
3016 a = i + 1;
3017 else
3018 b = i;
3019 }
3020 return c; // no entry found, return "c" unmodified
3021}
3022
3023 void
3024langmap_init(void)
3025{
3026 int i;
3027
3028 for (i = 0; i < 256; i++)
3029 langmap_mapchar[i] = i; // we init with a one-to-one map
3030 ga_init2(&langmap_mapga, sizeof(langmap_entry_T), 8);
3031}
3032
3033/*
3034 * Called when langmap option is set; the language map can be
3035 * changed at any time!
3036 */
Yegappan Lakshmananaf936912023-02-20 12:16:39 +00003037 char *
3038did_set_langmap(optset_T *args UNUSED)
Bram Moolenaare677df82019-09-02 22:31:11 +02003039{
3040 char_u *p;
3041 char_u *p2;
3042 int from, to;
3043
3044 ga_clear(&langmap_mapga); // clear the previous map first
3045 langmap_init(); // back to one-to-one map
3046
3047 for (p = p_langmap; p[0] != NUL; )
3048 {
3049 for (p2 = p; p2[0] != NUL && p2[0] != ',' && p2[0] != ';';
3050 MB_PTR_ADV(p2))
3051 {
3052 if (p2[0] == '\\' && p2[1] != NUL)
3053 ++p2;
3054 }
3055 if (p2[0] == ';')
3056 ++p2; // abcd;ABCD form, p2 points to A
3057 else
3058 p2 = NULL; // aAbBcCdD form, p2 is NULL
3059 while (p[0])
3060 {
3061 if (p[0] == ',')
3062 {
3063 ++p;
3064 break;
3065 }
3066 if (p[0] == '\\' && p[1] != NUL)
3067 ++p;
3068 from = (*mb_ptr2char)(p);
3069 to = NUL;
3070 if (p2 == NULL)
3071 {
3072 MB_PTR_ADV(p);
3073 if (p[0] != ',')
3074 {
3075 if (p[0] == '\\')
3076 ++p;
3077 to = (*mb_ptr2char)(p);
3078 }
3079 }
3080 else
3081 {
3082 if (p2[0] != ',')
3083 {
3084 if (p2[0] == '\\')
3085 ++p2;
3086 to = (*mb_ptr2char)(p2);
3087 }
3088 }
3089 if (to == NUL)
3090 {
Yegappan Lakshmanan5da901b2023-02-27 12:47:47 +00003091 sprintf(args->os_errbuf,
3092 _(e_langmap_matching_character_missing_for_str),
3093 transchar(from));
3094 return args->os_errbuf;
Bram Moolenaare677df82019-09-02 22:31:11 +02003095 }
3096
3097 if (from >= 256)
3098 langmap_set_entry(from, to);
3099 else
3100 langmap_mapchar[from & 255] = to;
3101
3102 // Advance to next pair
3103 MB_PTR_ADV(p);
3104 if (p2 != NULL)
3105 {
3106 MB_PTR_ADV(p2);
3107 if (*p == ';')
3108 {
3109 p = p2;
3110 if (p[0] != NUL)
3111 {
3112 if (p[0] != ',')
3113 {
Yegappan Lakshmanan5da901b2023-02-27 12:47:47 +00003114 sprintf(args->os_errbuf,
3115 _(e_langmap_extra_characters_after_semicolon_str),
3116 p);
3117 return args->os_errbuf;
Bram Moolenaare677df82019-09-02 22:31:11 +02003118 }
3119 ++p;
3120 }
3121 break;
3122 }
3123 }
3124 }
3125 }
Yegappan Lakshmananaf936912023-02-20 12:16:39 +00003126
3127 return NULL;
Bram Moolenaare677df82019-09-02 22:31:11 +02003128}
3129#endif
3130
Bram Moolenaarb66bab32019-08-01 14:28:24 +02003131 static void
3132do_exmap(exarg_T *eap, int isabbrev)
3133{
3134 int mode;
3135 char_u *cmdp;
3136
3137 cmdp = eap->cmd;
3138 mode = get_map_mode(&cmdp, eap->forceit || isabbrev);
3139
zeertzjq44068e92022-06-16 11:14:55 +01003140 switch (do_map(*cmdp == 'n' ? MAPTYPE_NOREMAP
3141 : *cmdp == 'u' ? MAPTYPE_UNMAP : MAPTYPE_MAP,
Bram Moolenaarb66bab32019-08-01 14:28:24 +02003142 eap->arg, mode, isabbrev))
3143 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00003144 case 1: emsg(_(e_invalid_argument));
Bram Moolenaarb66bab32019-08-01 14:28:24 +02003145 break;
Bram Moolenaare29a27f2021-07-20 21:07:36 +02003146 case 2: emsg((isabbrev ? _(e_no_such_abbreviation)
3147 : _(e_no_such_mapping)));
Bram Moolenaarb66bab32019-08-01 14:28:24 +02003148 break;
3149 }
3150}
3151
3152/*
3153 * ":abbreviate" and friends.
3154 */
3155 void
3156ex_abbreviate(exarg_T *eap)
3157{
3158 do_exmap(eap, TRUE); // almost the same as mapping
3159}
3160
3161/*
3162 * ":map" and friends.
3163 */
3164 void
3165ex_map(exarg_T *eap)
3166{
3167 // If we are sourcing .exrc or .vimrc in current directory we
3168 // print the mappings for security reasons.
3169 if (secure)
3170 {
3171 secure = 2;
3172 msg_outtrans(eap->cmd);
3173 msg_putchar('\n');
3174 }
3175 do_exmap(eap, FALSE);
3176}
3177
3178/*
3179 * ":unmap" and friends.
3180 */
3181 void
3182ex_unmap(exarg_T *eap)
3183{
3184 do_exmap(eap, FALSE);
3185}
3186
3187/*
3188 * ":mapclear" and friends.
3189 */
3190 void
3191ex_mapclear(exarg_T *eap)
3192{
3193 map_clear(eap->cmd, eap->arg, eap->forceit, FALSE);
3194}
3195
3196/*
3197 * ":abclear" and friends.
3198 */
3199 void
3200ex_abclear(exarg_T *eap)
3201{
3202 map_clear(eap->cmd, eap->arg, TRUE, TRUE);
3203}