blob: 91ab6e3f163e08b9efcc460ae05de2ec3ed7d9b2 [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);
zeertzjq9d997ad2024-07-29 21:10:07 +020088 if (mp->m_alt != NULL)
89 mp->m_alt->m_alt = NULL;
Bram Moolenaarb66bab32019-08-01 14:28:24 +020090 vim_free(mp->m_str);
91 vim_free(mp->m_orig_str);
92 *mpp = mp->m_next;
Bram Moolenaard648c012022-01-16 14:58:34 +000093#ifdef FEAT_EVAL
Bram Moolenaarf61c89d2022-01-19 22:51:48 +000094 reset_last_used_map(mp);
Bram Moolenaard648c012022-01-16 14:58:34 +000095#endif
Bram Moolenaar8aa0e6c2022-01-20 11:27:58 +000096 vim_free(mp);
Bram Moolenaarb66bab32019-08-01 14:28:24 +020097}
98
99/*
100 * Return characters to represent the map mode in an allocated string.
101 * Returns NULL when out of memory.
102 */
103 static char_u *
104map_mode_to_chars(int mode)
105{
106 garray_T mapmode;
107
108 ga_init2(&mapmode, 1, 7);
109
Bram Moolenaar24959102022-05-07 20:01:16 +0100110 if ((mode & (MODE_INSERT | MODE_CMDLINE)) == (MODE_INSERT | MODE_CMDLINE))
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200111 ga_append(&mapmode, '!'); // :map!
Bram Moolenaar24959102022-05-07 20:01:16 +0100112 else if (mode & MODE_INSERT)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200113 ga_append(&mapmode, 'i'); // :imap
Bram Moolenaar24959102022-05-07 20:01:16 +0100114 else if (mode & MODE_LANGMAP)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200115 ga_append(&mapmode, 'l'); // :lmap
Bram Moolenaar24959102022-05-07 20:01:16 +0100116 else if (mode & MODE_CMDLINE)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200117 ga_append(&mapmode, 'c'); // :cmap
Bram Moolenaar24959102022-05-07 20:01:16 +0100118 else if ((mode
119 & (MODE_NORMAL | MODE_VISUAL | MODE_SELECT | MODE_OP_PENDING))
120 == (MODE_NORMAL | MODE_VISUAL | MODE_SELECT | MODE_OP_PENDING))
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200121 ga_append(&mapmode, ' '); // :map
122 else
123 {
Bram Moolenaar24959102022-05-07 20:01:16 +0100124 if (mode & MODE_NORMAL)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200125 ga_append(&mapmode, 'n'); // :nmap
Bram Moolenaar24959102022-05-07 20:01:16 +0100126 if (mode & MODE_OP_PENDING)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200127 ga_append(&mapmode, 'o'); // :omap
Bram Moolenaar24959102022-05-07 20:01:16 +0100128 if (mode & MODE_TERMINAL)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200129 ga_append(&mapmode, 't'); // :tmap
Bram Moolenaar24959102022-05-07 20:01:16 +0100130 if ((mode & (MODE_VISUAL | MODE_SELECT)) == (MODE_VISUAL | MODE_SELECT))
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200131 ga_append(&mapmode, 'v'); // :vmap
132 else
133 {
Bram Moolenaar24959102022-05-07 20:01:16 +0100134 if (mode & MODE_VISUAL)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200135 ga_append(&mapmode, 'x'); // :xmap
Bram Moolenaar24959102022-05-07 20:01:16 +0100136 if (mode & MODE_SELECT)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200137 ga_append(&mapmode, 's'); // :smap
138 }
139 }
140
141 ga_append(&mapmode, NUL);
142 return (char_u *)mapmode.ga_data;
143}
144
Bram Moolenaar9f62ea02022-10-19 13:07:03 +0100145/*
146 * Output a line for one mapping.
147 */
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200148 static void
149showmap(
150 mapblock_T *mp,
151 int local) // TRUE for buffer-local map
152{
153 int len = 1;
154 char_u *mapchars;
155
156 if (message_filtered(mp->m_keys) && message_filtered(mp->m_str))
157 return;
158
Bram Moolenaarbf533e42022-11-13 20:43:19 +0000159 // Prevent mappings to be cleared while at the more prompt.
160 // Must jump to "theend" instead of returning.
161 ++map_locked;
162
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200163 if (msg_didout || msg_silent != 0)
164 {
165 msg_putchar('\n');
166 if (got_int) // 'q' typed at MORE prompt
Bram Moolenaarbf533e42022-11-13 20:43:19 +0000167 goto theend;
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200168 }
169
170 mapchars = map_mode_to_chars(mp->m_mode);
171 if (mapchars != NULL)
172 {
173 msg_puts((char *)mapchars);
174 len = (int)STRLEN(mapchars);
175 vim_free(mapchars);
176 }
177
178 while (++len <= 3)
179 msg_putchar(' ');
180
181 // Display the LHS. Get length of what we write.
182 len = msg_outtrans_special(mp->m_keys, TRUE, 0);
183 do
184 {
zeertzjq997b8a02023-02-19 21:00:31 +0000185 msg_putchar(' '); // pad with blanks
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200186 ++len;
187 } while (len < 12);
188
189 if (mp->m_noremap == REMAP_NONE)
190 msg_puts_attr("*", HL_ATTR(HLF_8));
191 else if (mp->m_noremap == REMAP_SCRIPT)
192 msg_puts_attr("&", HL_ATTR(HLF_8));
193 else
194 msg_putchar(' ');
195
196 if (local)
197 msg_putchar('@');
198 else
199 msg_putchar(' ');
200
201 // Use FALSE below if we only want things like <Up> to show up as such on
202 // the rhs, and not M-x etc, TRUE gets both -- webb
203 if (*mp->m_str == NUL)
204 msg_puts_attr("<Nop>", HL_ATTR(HLF_8));
205 else
zeertzjqac402f42022-05-04 18:51:43 +0100206 msg_outtrans_special(mp->m_str, FALSE, 0);
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200207#ifdef FEAT_EVAL
208 if (p_verbose > 0)
209 last_set_msg(mp->m_script_ctx);
210#endif
Bram Moolenaard288eaa2022-02-16 18:27:55 +0000211 msg_clr_eos();
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200212 out_flush(); // show one line at a time
Bram Moolenaarbf533e42022-11-13 20:43:19 +0000213
214theend:
215 --map_locked;
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200216}
217
zeertzjq9d997ad2024-07-29 21:10:07 +0200218 static mapblock_T *
Bram Moolenaar4c9243f2020-05-22 13:10:44 +0200219map_add(
220 mapblock_T **map_table,
221 mapblock_T **abbr_table,
222 char_u *keys,
223 char_u *rhs,
224 char_u *orig_rhs,
Bram Moolenaar4c9243f2020-05-22 13:10:44 +0200225 int noremap,
226 int nowait,
227 int silent,
228 int mode,
229 int is_abbr,
230#ifdef FEAT_EVAL
Bram Moolenaar5a80f8a2020-05-22 13:38:18 +0200231 int expr,
zeertzjqbfc7cbd2023-04-07 22:09:46 +0100232 scid_T sid, // 0 to use current_sctx
Bram Moolenaara9528b32022-01-18 20:51:35 +0000233 int scriptversion,
Bram Moolenaar4c9243f2020-05-22 13:10:44 +0200234 linenr_T lnum,
235#endif
236 int simplified)
237{
Bram Moolenaar94075b22022-01-18 20:30:39 +0000238 mapblock_T *mp = ALLOC_CLEAR_ONE(mapblock_T);
Bram Moolenaar4c9243f2020-05-22 13:10:44 +0200239
240 if (mp == NULL)
zeertzjq9d997ad2024-07-29 21:10:07 +0200241 return NULL;
Bram Moolenaar4c9243f2020-05-22 13:10:44 +0200242
243 // If CTRL-C has been mapped, don't always use it for Interrupting.
244 if (*keys == Ctrl_C)
245 {
246 if (map_table == curbuf->b_maphash)
247 curbuf->b_mapped_ctrl_c |= mode;
248 else
249 mapped_ctrl_c |= mode;
250 }
251
252 mp->m_keys = vim_strsave(keys);
253 mp->m_str = vim_strsave(rhs);
254 mp->m_orig_str = vim_strsave(orig_rhs);
255 if (mp->m_keys == NULL || mp->m_str == NULL)
256 {
257 vim_free(mp->m_keys);
258 vim_free(mp->m_str);
259 vim_free(mp->m_orig_str);
260 vim_free(mp);
zeertzjq9d997ad2024-07-29 21:10:07 +0200261 return NULL;
Bram Moolenaar4c9243f2020-05-22 13:10:44 +0200262 }
263 mp->m_keylen = (int)STRLEN(mp->m_keys);
264 mp->m_noremap = noremap;
265 mp->m_nowait = nowait;
266 mp->m_silent = silent;
267 mp->m_mode = mode;
268 mp->m_simplified = simplified;
269#ifdef FEAT_EVAL
270 mp->m_expr = expr;
zeertzjqbfc7cbd2023-04-07 22:09:46 +0100271 if (sid != 0)
Bram Moolenaar4c9243f2020-05-22 13:10:44 +0200272 {
273 mp->m_script_ctx.sc_sid = sid;
274 mp->m_script_ctx.sc_lnum = lnum;
Bram Moolenaara9528b32022-01-18 20:51:35 +0000275 mp->m_script_ctx.sc_version = scriptversion;
Bram Moolenaar4c9243f2020-05-22 13:10:44 +0200276 }
277 else
278 {
279 mp->m_script_ctx = current_sctx;
280 mp->m_script_ctx.sc_lnum += SOURCING_LNUM;
281 }
282#endif
283
284 // add the new entry in front of the abbrlist or maphash[] list
285 if (is_abbr)
286 {
287 mp->m_next = *abbr_table;
288 *abbr_table = mp;
289 }
290 else
291 {
292 int n = MAP_HASH(mp->m_mode, mp->m_keys[0]);
293
294 mp->m_next = map_table[n];
295 map_table[n] = mp;
296 }
zeertzjq9d997ad2024-07-29 21:10:07 +0200297 return mp;
Bram Moolenaar4c9243f2020-05-22 13:10:44 +0200298}
299
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200300/*
Bram Moolenaar9f62ea02022-10-19 13:07:03 +0100301 * List mappings. When "haskey" is FALSE all mappings, otherwise mappings that
302 * match "keys[keys_len]".
303 */
304 static void
305list_mappings(
306 int keyround,
307 int abbrev,
308 int haskey,
309 char_u *keys,
310 int keys_len,
311 int mode,
312 int *did_local)
313{
Bram Moolenaarbf533e42022-11-13 20:43:19 +0000314 // Prevent mappings to be cleared while at the more prompt.
315 ++map_locked;
316
Bram Moolenaar63a2e362022-11-23 20:20:18 +0000317 if (p_verbose > 0 && keyround == 1)
318 {
319 if (seenModifyOtherKeys)
Bram Moolenaarc255b782022-11-26 19:16:48 +0000320 msg_puts(_("Seen modifyOtherKeys: true\n"));
321
322 if (modify_otherkeys_state != MOKS_INITIAL)
323 {
324 char *name = _("Unknown");
325 switch (modify_otherkeys_state)
326 {
327 case MOKS_INITIAL: break;
328 case MOKS_OFF: name = _("Off"); break;
329 case MOKS_ENABLED: name = _("On"); break;
330 case MOKS_DISABLED: name = _("Disabled"); break;
Bram Moolenaar9d1184c2022-12-16 18:33:20 +0000331 case MOKS_AFTER_T_TE: name = _("Cleared"); break;
Bram Moolenaarc255b782022-11-26 19:16:48 +0000332 }
333
334 char buf[200];
335 vim_snprintf(buf, sizeof(buf),
336 _("modifyOtherKeys detected: %s\n"), name);
337 msg_puts(buf);
338 }
339
Bram Moolenaar63a2e362022-11-23 20:20:18 +0000340 if (kitty_protocol_state != KKPS_INITIAL)
341 {
342 char *name = _("Unknown");
343 switch (kitty_protocol_state)
344 {
345 case KKPS_INITIAL: break;
346 case KKPS_OFF: name = _("Off"); break;
347 case KKPS_ENABLED: name = _("On"); break;
348 case KKPS_DISABLED: name = _("Disabled"); break;
Bram Moolenaar9d1184c2022-12-16 18:33:20 +0000349 case KKPS_AFTER_T_TE: name = _("Cleared"); break;
Bram Moolenaar63a2e362022-11-23 20:20:18 +0000350 }
351
352 char buf[200];
Bram Moolenaarc255b782022-11-26 19:16:48 +0000353 vim_snprintf(buf, sizeof(buf),
354 _("Kitty keyboard protocol: %s\n"), name);
Bram Moolenaar63a2e362022-11-23 20:20:18 +0000355 msg_puts(buf);
356 }
357 }
Bram Moolenaar9f62ea02022-10-19 13:07:03 +0100358
359 // need to loop over all global hash lists
360 for (int hash = 0; hash < 256 && !got_int; ++hash)
361 {
362 mapblock_T *mp;
363
364 if (abbrev)
365 {
366 if (hash != 0) // there is only one abbreviation list
367 break;
368 mp = curbuf->b_first_abbr;
369 }
370 else
371 mp = curbuf->b_maphash[hash];
372 for ( ; mp != NULL && !got_int; mp = mp->m_next)
373 {
374 // check entries with the same mode
375 if (!mp->m_simplified && (mp->m_mode & mode) != 0)
376 {
377 if (!haskey) // show all entries
378 {
379 showmap(mp, TRUE);
380 *did_local = TRUE;
381 }
382 else
383 {
384 int n = mp->m_keylen;
385 if (STRNCMP(mp->m_keys, keys,
386 (size_t)(n < keys_len ? n : keys_len)) == 0)
387 {
388 showmap(mp, TRUE);
389 *did_local = TRUE;
390 }
391 }
392 }
393 }
394 }
Bram Moolenaarbf533e42022-11-13 20:43:19 +0000395
396 --map_locked;
Bram Moolenaar9f62ea02022-10-19 13:07:03 +0100397}
398
399/*
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200400 * map[!] : show all key mappings
401 * map[!] {lhs} : show key mapping for {lhs}
402 * map[!] {lhs} {rhs} : set key mapping for {lhs} to {rhs}
403 * noremap[!] {lhs} {rhs} : same, but no remapping for {rhs}
404 * unmap[!] {lhs} : remove key mapping for {lhs}
405 * abbr : show all abbreviations
406 * abbr {lhs} : show abbreviations for {lhs}
407 * abbr {lhs} {rhs} : set abbreviation for {lhs} to {rhs}
408 * noreabbr {lhs} {rhs} : same, but no remapping for {rhs}
409 * unabbr {lhs} : remove abbreviation for {lhs}
410 *
zeertzjq44068e92022-06-16 11:14:55 +0100411 * maptype: MAPTYPE_MAP for :map
412 * MAPTYPE_UNMAP for :unmap
413 * MAPTYPE_NOREMAP for noremap
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200414 *
415 * arg is pointer to any arguments. Note: arg cannot be a read-only string,
416 * it will be modified.
417 *
Bram Moolenaar24959102022-05-07 20:01:16 +0100418 * for :map mode is MODE_NORMAL | MODE_VISUAL | MODE_SELECT | MODE_OP_PENDING
419 * for :map! mode is MODE_INSERT | MODE_CMDLINE
420 * for :cmap mode is MODE_CMDLINE
421 * for :imap mode is MODE_INSERT
422 * for :lmap mode is MODE_LANGMAP
423 * for :nmap mode is MODE_NORMAL
424 * for :vmap mode is MODE_VISUAL | MODE_SELECT
425 * for :xmap mode is MODE_VISUAL
426 * for :smap mode is MODE_SELECT
427 * for :omap mode is MODE_OP_PENDING
428 * for :tmap mode is MODE_TERMINAL
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200429 *
Bram Moolenaar24959102022-05-07 20:01:16 +0100430 * for :abbr mode is MODE_INSERT | MODE_CMDLINE
431 * for :iabbr mode is MODE_INSERT
432 * for :cabbr mode is MODE_CMDLINE
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200433 *
434 * Return 0 for success
435 * 1 for invalid arguments
436 * 2 for no match
437 * 4 for out of mem
438 * 5 for entry not unique
439 */
440 int
441do_map(
442 int maptype,
443 char_u *arg,
444 int mode,
445 int abbrev) // not a mapping but an abbreviation
446{
447 char_u *keys;
448 mapblock_T *mp, **mpp;
zeertzjq9d997ad2024-07-29 21:10:07 +0200449 mapblock_T *mp_result[2] = {NULL, NULL};
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200450 char_u *rhs;
451 char_u *p;
452 int n;
453 int len = 0; // init for GCC
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200454 int hasarg;
455 int haskey;
Bram Moolenaar459fd782019-10-13 16:43:39 +0200456 int do_print;
457 int keyround;
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200458 char_u *keys_buf = NULL;
Bram Moolenaar459fd782019-10-13 16:43:39 +0200459 char_u *alt_keys_buf = NULL;
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200460 char_u *arg_buf = NULL;
461 int retval = 0;
462 int do_backslash;
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200463 mapblock_T **abbr_table;
464 mapblock_T **map_table;
465 int unique = FALSE;
466 int nowait = FALSE;
467 int silent = FALSE;
468 int special = FALSE;
469#ifdef FEAT_EVAL
470 int expr = FALSE;
471#endif
Bram Moolenaar459fd782019-10-13 16:43:39 +0200472 int did_simplify = FALSE;
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200473 int noremap;
474 char_u *orig_rhs;
475
476 keys = arg;
477 map_table = maphash;
478 abbr_table = &first_abbr;
479
480 // For ":noremap" don't remap, otherwise do remap.
zeertzjq44068e92022-06-16 11:14:55 +0100481 if (maptype == MAPTYPE_NOREMAP)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200482 noremap = REMAP_NONE;
483 else
484 noremap = REMAP_YES;
485
486 // Accept <buffer>, <nowait>, <silent>, <expr> <script> and <unique> in
487 // any order.
488 for (;;)
489 {
490 // Check for "<buffer>": mapping local to buffer.
491 if (STRNCMP(keys, "<buffer>", 8) == 0)
492 {
493 keys = skipwhite(keys + 8);
494 map_table = curbuf->b_maphash;
495 abbr_table = &curbuf->b_first_abbr;
496 continue;
497 }
498
499 // Check for "<nowait>": don't wait for more characters.
500 if (STRNCMP(keys, "<nowait>", 8) == 0)
501 {
502 keys = skipwhite(keys + 8);
503 nowait = TRUE;
504 continue;
505 }
506
507 // Check for "<silent>": don't echo commands.
508 if (STRNCMP(keys, "<silent>", 8) == 0)
509 {
510 keys = skipwhite(keys + 8);
511 silent = TRUE;
512 continue;
513 }
514
515 // Check for "<special>": accept special keys in <>
516 if (STRNCMP(keys, "<special>", 9) == 0)
517 {
518 keys = skipwhite(keys + 9);
519 special = TRUE;
520 continue;
521 }
522
523#ifdef FEAT_EVAL
524 // Check for "<script>": remap script-local mappings only
525 if (STRNCMP(keys, "<script>", 8) == 0)
526 {
527 keys = skipwhite(keys + 8);
528 noremap = REMAP_SCRIPT;
529 continue;
530 }
531
532 // Check for "<expr>": {rhs} is an expression.
533 if (STRNCMP(keys, "<expr>", 6) == 0)
534 {
535 keys = skipwhite(keys + 6);
536 expr = TRUE;
537 continue;
538 }
539#endif
540 // Check for "<unique>": don't overwrite an existing mapping.
541 if (STRNCMP(keys, "<unique>", 8) == 0)
542 {
543 keys = skipwhite(keys + 8);
544 unique = TRUE;
545 continue;
546 }
547 break;
548 }
549
550 validate_maphash();
551
552 // Find end of keys and skip CTRL-Vs (and backslashes) in it.
553 // Accept backslash like CTRL-V when 'cpoptions' does not contain 'B'.
554 // with :unmap white space is included in the keys, no argument possible.
555 p = keys;
556 do_backslash = (vim_strchr(p_cpo, CPO_BSLASH) == NULL);
zeertzjq44068e92022-06-16 11:14:55 +0100557 while (*p && (maptype == MAPTYPE_UNMAP || !VIM_ISWHITE(*p)))
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200558 {
559 if ((p[0] == Ctrl_V || (do_backslash && p[0] == '\\')) &&
560 p[1] != NUL)
561 ++p; // skip CTRL-V or backslash
562 ++p;
563 }
564 if (*p != NUL)
565 *p++ = NUL;
566
567 p = skipwhite(p);
568 rhs = p;
569 hasarg = (*rhs != NUL);
570 haskey = (*keys != NUL);
zeertzjq44068e92022-06-16 11:14:55 +0100571 do_print = !haskey || (maptype != MAPTYPE_UNMAP && !hasarg);
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200572
573 // check for :unmap without argument
zeertzjq44068e92022-06-16 11:14:55 +0100574 if (maptype == MAPTYPE_UNMAP && !haskey)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200575 {
576 retval = 1;
577 goto theend;
578 }
579
580 // If mapping has been given as ^V<C_UP> say, then replace the term codes
581 // with the appropriate two bytes. If it is a shifted special key, unshift
582 // it too, giving another two bytes.
583 // replace_termcodes() may move the result to allocated memory, which
584 // needs to be freed later (*keys_buf and *arg_buf).
585 // replace_termcodes() also removes CTRL-Vs and sometimes backslashes.
Bram Moolenaar459fd782019-10-13 16:43:39 +0200586 // If something like <C-H> is simplified to 0x08 then mark it as simplified
Yegappan Lakshmanane8575982023-01-14 12:32:28 +0000587 // and also add an entry with a modifier, which will work when using a key
588 // protocol.
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200589 if (haskey)
Bram Moolenaar459fd782019-10-13 16:43:39 +0200590 {
591 char_u *new_keys;
592 int flags = REPTERM_FROM_PART | REPTERM_DO_LT;
593
594 if (special)
595 flags |= REPTERM_SPECIAL;
zeertzjq7e0bae02023-08-11 23:15:38 +0200596 new_keys = replace_termcodes(keys, &keys_buf, 0, flags, &did_simplify);
Bram Moolenaar459fd782019-10-13 16:43:39 +0200597 if (did_simplify)
zeertzjq7e0bae02023-08-11 23:15:38 +0200598 (void)replace_termcodes(keys, &alt_keys_buf, 0,
Bram Moolenaar459fd782019-10-13 16:43:39 +0200599 flags | REPTERM_NO_SIMPLIFY, NULL);
600 keys = new_keys;
601 }
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200602 orig_rhs = rhs;
603 if (hasarg)
604 {
605 if (STRICMP(rhs, "<nop>") == 0) // "<Nop>" means nothing
606 rhs = (char_u *)"";
607 else
zeertzjq7e0bae02023-08-11 23:15:38 +0200608 rhs = replace_termcodes(rhs, &arg_buf, 0,
Bram Moolenaar459fd782019-10-13 16:43:39 +0200609 REPTERM_DO_LT | (special ? REPTERM_SPECIAL : 0), NULL);
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200610 }
611
Bram Moolenaar459fd782019-10-13 16:43:39 +0200612 /*
613 * The following is done twice if we have two versions of keys:
614 * "alt_keys_buf" is not NULL.
615 */
616 for (keyround = 1; keyround <= 2; ++keyround)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200617 {
Bram Moolenaar459fd782019-10-13 16:43:39 +0200618 int did_it = FALSE;
619 int did_local = FALSE;
Bram Moolenaar87f74102022-04-25 18:59:25 +0100620 int keyround1_simplified = keyround == 1 && did_simplify;
Bram Moolenaar459fd782019-10-13 16:43:39 +0200621 int round;
Bram Moolenaar459fd782019-10-13 16:43:39 +0200622
623 if (keyround == 2)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200624 {
Bram Moolenaar459fd782019-10-13 16:43:39 +0200625 if (alt_keys_buf == NULL)
626 break;
627 keys = alt_keys_buf;
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200628 }
Bram Moolenaar459fd782019-10-13 16:43:39 +0200629 else if (alt_keys_buf != NULL && do_print)
630 // when printing always use the not-simplified map
631 keys = alt_keys_buf;
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200632
Bram Moolenaar459fd782019-10-13 16:43:39 +0200633 // check arguments and translate function keys
634 if (haskey)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200635 {
Bram Moolenaar459fd782019-10-13 16:43:39 +0200636 len = (int)STRLEN(keys);
637 if (len > MAXMAPLEN) // maximum length of MAXMAPLEN chars
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200638 {
Bram Moolenaar459fd782019-10-13 16:43:39 +0200639 retval = 1;
640 goto theend;
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200641 }
Bram Moolenaar459fd782019-10-13 16:43:39 +0200642
zeertzjq44068e92022-06-16 11:14:55 +0100643 if (abbrev && maptype != MAPTYPE_UNMAP)
Bram Moolenaar459fd782019-10-13 16:43:39 +0200644 {
645 // If an abbreviation ends in a keyword character, the
646 // rest must be all keyword-char or all non-keyword-char.
647 // Otherwise we won't be able to find the start of it in a
648 // vi-compatible way.
649 if (has_mbyte)
650 {
651 int first, last;
652 int same = -1;
653
654 first = vim_iswordp(keys);
655 last = first;
656 p = keys + (*mb_ptr2len)(keys);
657 n = 1;
658 while (p < keys + len)
659 {
660 ++n; // nr of (multi-byte) chars
661 last = vim_iswordp(p); // type of last char
662 if (same == -1 && last != first)
663 same = n - 1; // count of same char type
664 p += (*mb_ptr2len)(p);
665 }
666 if (last && n > 2 && same >= 0 && same < n - 1)
667 {
668 retval = 1;
669 goto theend;
670 }
671 }
672 else if (vim_iswordc(keys[len - 1]))
673 // ends in keyword char
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200674 for (n = 0; n < len - 2; ++n)
675 if (vim_iswordc(keys[n]) != vim_iswordc(keys[len - 2]))
676 {
677 retval = 1;
678 goto theend;
679 }
Bram Moolenaar459fd782019-10-13 16:43:39 +0200680 // An abbreviation cannot contain white space.
681 for (n = 0; n < len; ++n)
682 if (VIM_ISWHITE(keys[n]))
683 {
684 retval = 1;
685 goto theend;
686 }
687 }
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200688 }
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200689
Bram Moolenaar459fd782019-10-13 16:43:39 +0200690 if (haskey && hasarg && abbrev) // if we will add an abbreviation
691 no_abbr = FALSE; // reset flag that indicates there are
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200692 // no abbreviations
693
Bram Moolenaar459fd782019-10-13 16:43:39 +0200694 if (do_print)
695 msg_start();
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200696
Bram Moolenaar459fd782019-10-13 16:43:39 +0200697 // Check if a new local mapping wasn't already defined globally.
Bram Moolenaar4c9243f2020-05-22 13:10:44 +0200698 if (unique && map_table == curbuf->b_maphash
zeertzjq44068e92022-06-16 11:14:55 +0100699 && haskey && hasarg && maptype != MAPTYPE_UNMAP)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200700 {
Bram Moolenaar459fd782019-10-13 16:43:39 +0200701 // need to loop over all global hash lists
Bram Moolenaar9f62ea02022-10-19 13:07:03 +0100702 for (int hash = 0; hash < 256 && !got_int; ++hash)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200703 {
Bram Moolenaar459fd782019-10-13 16:43:39 +0200704 if (abbrev)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200705 {
Bram Moolenaar459fd782019-10-13 16:43:39 +0200706 if (hash != 0) // there is only one abbreviation list
707 break;
708 mp = first_abbr;
709 }
710 else
711 mp = maphash[hash];
712 for ( ; mp != NULL && !got_int; mp = mp->m_next)
713 {
714 // check entries with the same mode
715 if ((mp->m_mode & mode) != 0
716 && mp->m_keylen == len
Bram Moolenaar459fd782019-10-13 16:43:39 +0200717 && STRNCMP(mp->m_keys, keys, (size_t)len) == 0)
718 {
719 if (abbrev)
Bram Moolenaar6d057012021-12-31 18:49:43 +0000720 semsg(
721 _(e_global_abbreviation_already_exists_for_str),
Bram Moolenaar459fd782019-10-13 16:43:39 +0200722 mp->m_keys);
723 else
Bram Moolenaar6d057012021-12-31 18:49:43 +0000724 semsg(_(e_global_mapping_already_exists_for_str),
Bram Moolenaar459fd782019-10-13 16:43:39 +0200725 mp->m_keys);
726 retval = 5;
727 goto theend;
728 }
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200729 }
730 }
731 }
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200732
Bram Moolenaar459fd782019-10-13 16:43:39 +0200733 // When listing global mappings, also list buffer-local ones here.
zeertzjq44068e92022-06-16 11:14:55 +0100734 if (map_table != curbuf->b_maphash && !hasarg
735 && maptype != MAPTYPE_UNMAP)
Bram Moolenaar9f62ea02022-10-19 13:07:03 +0100736 list_mappings(keyround, abbrev, haskey, keys, len,
737 mode, &did_local);
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200738
Bram Moolenaar459fd782019-10-13 16:43:39 +0200739 // Find an entry in the maphash[] list that matches.
740 // For :unmap we may loop two times: once to try to unmap an entry with
741 // a matching 'from' part, a second time, if the first fails, to unmap
zeertzjqa3f83fe2021-11-22 12:47:39 +0000742 // an entry with a matching 'to' part. This was done to allow
743 // ":ab foo bar" to be unmapped by typing ":unab foo", where "foo" will
744 // be replaced by "bar" because of the abbreviation.
zeertzjq44068e92022-06-16 11:14:55 +0100745 for (round = 0; (round == 0 || maptype == MAPTYPE_UNMAP) && round <= 1
Bram Moolenaar459fd782019-10-13 16:43:39 +0200746 && !did_it && !got_int; ++round)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200747 {
Bram Moolenaar459fd782019-10-13 16:43:39 +0200748 // need to loop over all hash lists
Bram Moolenaar9f62ea02022-10-19 13:07:03 +0100749 for (int hash = 0; hash < 256 && !got_int; ++hash)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200750 {
Bram Moolenaar459fd782019-10-13 16:43:39 +0200751 if (abbrev)
752 {
753 if (hash > 0) // there is only one abbreviation list
754 break;
755 mpp = abbr_table;
756 }
757 else
758 mpp = &(map_table[hash]);
759 for (mp = *mpp; mp != NULL && !got_int; mp = *mpp)
760 {
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200761
Bram Moolenaarfafb4b12019-10-16 18:34:57 +0200762 if ((mp->m_mode & mode) == 0)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200763 {
Bram Moolenaarfafb4b12019-10-16 18:34:57 +0200764 // skip entries with wrong mode
Bram Moolenaar459fd782019-10-13 16:43:39 +0200765 mpp = &(mp->m_next);
766 continue;
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200767 }
Bram Moolenaar459fd782019-10-13 16:43:39 +0200768 if (!haskey) // show all entries
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200769 {
Bram Moolenaarfafb4b12019-10-16 18:34:57 +0200770 if (!mp->m_simplified)
771 {
772 showmap(mp, map_table != maphash);
773 did_it = TRUE;
774 }
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200775 }
Bram Moolenaar459fd782019-10-13 16:43:39 +0200776 else // do we have a match?
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200777 {
Bram Moolenaar459fd782019-10-13 16:43:39 +0200778 if (round) // second round: Try unmap "rhs" string
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200779 {
Bram Moolenaar459fd782019-10-13 16:43:39 +0200780 n = (int)STRLEN(mp->m_str);
781 p = mp->m_str;
782 }
783 else
784 {
785 n = mp->m_keylen;
786 p = mp->m_keys;
787 }
788 if (STRNCMP(p, keys, (size_t)(n < len ? n : len)) == 0)
789 {
zeertzjq44068e92022-06-16 11:14:55 +0100790 if (maptype == MAPTYPE_UNMAP)
Bram Moolenaar459fd782019-10-13 16:43:39 +0200791 {
792 // Delete entry.
793 // Only accept a full match. For abbreviations
794 // we ignore trailing space when matching with
795 // the "lhs", since an abbreviation can't have
796 // trailing space.
797 if (n != len && (!abbrev || round || n > len
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200798 || *skipwhite(keys + n) != NUL))
Bram Moolenaar459fd782019-10-13 16:43:39 +0200799 {
800 mpp = &(mp->m_next);
801 continue;
802 }
zeertzjqabeb09b2022-04-26 12:29:43 +0100803 // In keyround for simplified keys, don't unmap
804 // a mapping without m_simplified flag.
Bram Moolenaar87f74102022-04-25 18:59:25 +0100805 if (keyround1_simplified && !mp->m_simplified)
zeertzjqa4e33322022-04-24 17:07:53 +0100806 break;
Bram Moolenaar459fd782019-10-13 16:43:39 +0200807 // We reset the indicated mode bits. If nothing
808 // is left the entry is deleted below.
809 mp->m_mode &= ~mode;
810 did_it = TRUE; // remember we did something
811 }
812 else if (!hasarg) // show matching entry
813 {
Bram Moolenaarfafb4b12019-10-16 18:34:57 +0200814 if (!mp->m_simplified)
815 {
816 showmap(mp, map_table != maphash);
817 did_it = TRUE;
818 }
Bram Moolenaar459fd782019-10-13 16:43:39 +0200819 }
820 else if (n != len) // new entry is ambiguous
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200821 {
822 mpp = &(mp->m_next);
823 continue;
824 }
Bram Moolenaar459fd782019-10-13 16:43:39 +0200825 else if (unique)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200826 {
Bram Moolenaar459fd782019-10-13 16:43:39 +0200827 if (abbrev)
Bram Moolenaar6d057012021-12-31 18:49:43 +0000828 semsg(
829 _(e_abbreviation_already_exists_for_str),
Bram Moolenaar459fd782019-10-13 16:43:39 +0200830 p);
831 else
Bram Moolenaar6d057012021-12-31 18:49:43 +0000832 semsg(_(e_mapping_already_exists_for_str),
Bram Moolenaar459fd782019-10-13 16:43:39 +0200833 p);
834 retval = 5;
835 goto theend;
836 }
837 else
838 {
839 // new rhs for existing entry
840 mp->m_mode &= ~mode; // remove mode bits
841 if (mp->m_mode == 0 && !did_it) // reuse entry
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200842 {
Bram Moolenaar459fd782019-10-13 16:43:39 +0200843 char_u *newstr = vim_strsave(rhs);
844
845 if (newstr == NULL)
846 {
847 retval = 4; // no mem
848 goto theend;
849 }
zeertzjq9d997ad2024-07-29 21:10:07 +0200850 if (mp->m_alt != NULL)
851 mp->m_alt = mp->m_alt->m_alt = NULL;
Bram Moolenaar459fd782019-10-13 16:43:39 +0200852 vim_free(mp->m_str);
853 mp->m_str = newstr;
854 vim_free(mp->m_orig_str);
855 mp->m_orig_str = vim_strsave(orig_rhs);
856 mp->m_noremap = noremap;
857 mp->m_nowait = nowait;
858 mp->m_silent = silent;
859 mp->m_mode = mode;
Bram Moolenaar87f74102022-04-25 18:59:25 +0100860 mp->m_simplified = keyround1_simplified;
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200861#ifdef FEAT_EVAL
Bram Moolenaar459fd782019-10-13 16:43:39 +0200862 mp->m_expr = expr;
863 mp->m_script_ctx = current_sctx;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +0100864 mp->m_script_ctx.sc_lnum += SOURCING_LNUM;
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200865#endif
zeertzjq9d997ad2024-07-29 21:10:07 +0200866 mp_result[keyround - 1] = mp;
Bram Moolenaar459fd782019-10-13 16:43:39 +0200867 did_it = TRUE;
868 }
869 }
870 if (mp->m_mode == 0) // entry can be deleted
871 {
872 map_free(mpp);
873 continue; // continue with *mpp
874 }
875
876 // May need to put this entry into another hash
877 // list.
Bram Moolenaar9f62ea02022-10-19 13:07:03 +0100878 int new_hash = MAP_HASH(mp->m_mode, mp->m_keys[0]);
Bram Moolenaar459fd782019-10-13 16:43:39 +0200879 if (!abbrev && new_hash != hash)
880 {
881 *mpp = mp->m_next;
882 mp->m_next = map_table[new_hash];
883 map_table[new_hash] = mp;
884
885 continue; // continue with *mpp
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200886 }
887 }
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200888 }
Bram Moolenaar459fd782019-10-13 16:43:39 +0200889 mpp = &(mp->m_next);
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200890 }
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200891 }
892 }
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200893
zeertzjq44068e92022-06-16 11:14:55 +0100894 if (maptype == MAPTYPE_UNMAP)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200895 {
Bram Moolenaar459fd782019-10-13 16:43:39 +0200896 // delete entry
897 if (!did_it)
zeertzjqa4e33322022-04-24 17:07:53 +0100898 {
Bram Moolenaar87f74102022-04-25 18:59:25 +0100899 if (!keyround1_simplified)
zeertzjqa4e33322022-04-24 17:07:53 +0100900 retval = 2; // no match
901 }
Bram Moolenaar459fd782019-10-13 16:43:39 +0200902 else if (*keys == Ctrl_C)
903 {
904 // If CTRL-C has been unmapped, reuse it for Interrupting.
905 if (map_table == curbuf->b_maphash)
906 curbuf->b_mapped_ctrl_c &= ~mode;
907 else
908 mapped_ctrl_c &= ~mode;
909 }
910 continue;
911 }
912
913 if (!haskey || !hasarg)
914 {
915 // print entries
916 if (!did_it && !did_local)
917 {
918 if (abbrev)
919 msg(_("No abbreviation found"));
920 else
921 msg(_("No mapping found"));
922 }
923 goto theend; // listing finished
924 }
925
926 if (did_it)
927 continue; // have added the new entry already
928
929 // Get here when adding a new entry to the maphash[] list or abbrlist.
zeertzjq9d997ad2024-07-29 21:10:07 +0200930 mp_result[keyround - 1] = map_add(map_table, abbr_table, keys,
931 rhs, orig_rhs, noremap, nowait, silent, mode, abbrev,
Bram Moolenaar4c9243f2020-05-22 13:10:44 +0200932#ifdef FEAT_EVAL
zeertzjqbfc7cbd2023-04-07 22:09:46 +0100933 expr, /* sid */ 0, /* scriptversion */ 0, /* lnum */ 0,
Bram Moolenaar4c9243f2020-05-22 13:10:44 +0200934#endif
zeertzjq9d997ad2024-07-29 21:10:07 +0200935 keyround1_simplified);
936 if (mp_result[keyround - 1] == NULL)
Bram Moolenaar459fd782019-10-13 16:43:39 +0200937 {
938 retval = 4; // no mem
939 goto theend;
940 }
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200941 }
942
zeertzjq9d997ad2024-07-29 21:10:07 +0200943 if (mp_result[0] != NULL && mp_result[1] != NULL)
944 {
945 mp_result[0]->m_alt = mp_result[1];
946 mp_result[1]->m_alt = mp_result[0];
947 }
948
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200949theend:
950 vim_free(keys_buf);
Bram Moolenaar459fd782019-10-13 16:43:39 +0200951 vim_free(alt_keys_buf);
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200952 vim_free(arg_buf);
953 return retval;
954}
955
956/*
957 * Get the mapping mode from the command name.
958 */
959 static int
960get_map_mode(char_u **cmdp, int forceit)
961{
962 char_u *p;
963 int modec;
964 int mode;
965
966 p = *cmdp;
967 modec = *p++;
968 if (modec == 'i')
Bram Moolenaar24959102022-05-07 20:01:16 +0100969 mode = MODE_INSERT; // :imap
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200970 else if (modec == 'l')
Bram Moolenaar24959102022-05-07 20:01:16 +0100971 mode = MODE_LANGMAP; // :lmap
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200972 else if (modec == 'c')
Bram Moolenaar24959102022-05-07 20:01:16 +0100973 mode = MODE_CMDLINE; // :cmap
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200974 else if (modec == 'n' && *p != 'o') // avoid :noremap
Bram Moolenaar24959102022-05-07 20:01:16 +0100975 mode = MODE_NORMAL; // :nmap
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200976 else if (modec == 'v')
Bram Moolenaar24959102022-05-07 20:01:16 +0100977 mode = MODE_VISUAL | MODE_SELECT; // :vmap
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200978 else if (modec == 'x')
Bram Moolenaar24959102022-05-07 20:01:16 +0100979 mode = MODE_VISUAL; // :xmap
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200980 else if (modec == 's')
Bram Moolenaar24959102022-05-07 20:01:16 +0100981 mode = MODE_SELECT; // :smap
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200982 else if (modec == 'o')
Bram Moolenaar24959102022-05-07 20:01:16 +0100983 mode = MODE_OP_PENDING; // :omap
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200984 else if (modec == 't')
Bram Moolenaar24959102022-05-07 20:01:16 +0100985 mode = MODE_TERMINAL; // :tmap
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200986 else
987 {
988 --p;
989 if (forceit)
Bram Moolenaar24959102022-05-07 20:01:16 +0100990 mode = MODE_INSERT | MODE_CMDLINE; // :map !
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200991 else
Bram Moolenaar24959102022-05-07 20:01:16 +0100992 mode = MODE_VISUAL | MODE_SELECT | MODE_NORMAL | MODE_OP_PENDING;
993 // :map
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200994 }
995
996 *cmdp = p;
997 return mode;
998}
999
1000/*
zeertzjqc207fd22022-06-29 10:37:40 +01001001 * Clear all mappings (":mapclear") or abbreviations (":abclear").
1002 * "abbr" should be FALSE for mappings, TRUE for abbreviations.
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001003 */
1004 static void
1005map_clear(
1006 char_u *cmdp,
zeertzjqc207fd22022-06-29 10:37:40 +01001007 char_u *arg,
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001008 int forceit,
1009 int abbr)
1010{
1011 int mode;
1012 int local;
1013
1014 local = (STRCMP(arg, "<buffer>") == 0);
1015 if (!local && *arg != NUL)
1016 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001017 emsg(_(e_invalid_argument));
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001018 return;
1019 }
1020
1021 mode = get_map_mode(&cmdp, forceit);
zeertzjqc207fd22022-06-29 10:37:40 +01001022 map_clear_mode(curbuf, mode, local, abbr);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001023}
1024
1025/*
Bram Moolenaarbf533e42022-11-13 20:43:19 +00001026 * If "map_locked" is set then give an error and return TRUE.
1027 * Otherwise return FALSE.
1028 */
1029 static int
1030is_map_locked(void)
1031{
1032 if (map_locked > 0)
1033 {
1034 emsg(_(e_cannot_change_mappings_while_listing));
1035 return TRUE;
1036 }
1037 return FALSE;
1038}
1039
1040/*
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001041 * Clear all mappings in "mode".
1042 */
1043 void
zeertzjqc207fd22022-06-29 10:37:40 +01001044map_clear_mode(
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001045 buf_T *buf, // buffer for local mappings
1046 int mode, // mode in which to delete
1047 int local, // TRUE for buffer-local mappings
1048 int abbr) // TRUE for abbreviations
1049{
1050 mapblock_T *mp, **mpp;
1051 int hash;
1052 int new_hash;
1053
Bram Moolenaarbf533e42022-11-13 20:43:19 +00001054 if (is_map_locked())
1055 return;
1056
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001057 validate_maphash();
1058
1059 for (hash = 0; hash < 256; ++hash)
1060 {
1061 if (abbr)
1062 {
1063 if (hash > 0) // there is only one abbrlist
1064 break;
1065 if (local)
1066 mpp = &buf->b_first_abbr;
1067 else
1068 mpp = &first_abbr;
1069 }
1070 else
1071 {
1072 if (local)
1073 mpp = &buf->b_maphash[hash];
1074 else
1075 mpp = &maphash[hash];
1076 }
1077 while (*mpp != NULL)
1078 {
1079 mp = *mpp;
1080 if (mp->m_mode & mode)
1081 {
1082 mp->m_mode &= ~mode;
1083 if (mp->m_mode == 0) // entry can be deleted
1084 {
1085 map_free(mpp);
1086 continue;
1087 }
1088 // May need to put this entry into another hash list.
1089 new_hash = MAP_HASH(mp->m_mode, mp->m_keys[0]);
1090 if (!abbr && new_hash != hash)
1091 {
1092 *mpp = mp->m_next;
1093 if (local)
1094 {
1095 mp->m_next = buf->b_maphash[new_hash];
1096 buf->b_maphash[new_hash] = mp;
1097 }
1098 else
1099 {
1100 mp->m_next = maphash[new_hash];
1101 maphash[new_hash] = mp;
1102 }
1103 continue; // continue with *mpp
1104 }
1105 }
1106 mpp = &(mp->m_next);
1107 }
1108 }
1109}
1110
1111#if defined(FEAT_EVAL) || defined(PROTO)
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001112 int
Bram Moolenaar581ba392019-09-03 22:08:33 +02001113mode_str2flags(char_u *modechars)
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001114{
1115 int mode = 0;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001116
1117 if (vim_strchr(modechars, 'n') != NULL)
Bram Moolenaar24959102022-05-07 20:01:16 +01001118 mode |= MODE_NORMAL;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001119 if (vim_strchr(modechars, 'v') != NULL)
Bram Moolenaar24959102022-05-07 20:01:16 +01001120 mode |= MODE_VISUAL | MODE_SELECT;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001121 if (vim_strchr(modechars, 'x') != NULL)
Bram Moolenaar24959102022-05-07 20:01:16 +01001122 mode |= MODE_VISUAL;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001123 if (vim_strchr(modechars, 's') != NULL)
Bram Moolenaar24959102022-05-07 20:01:16 +01001124 mode |= MODE_SELECT;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001125 if (vim_strchr(modechars, 'o') != NULL)
Bram Moolenaar24959102022-05-07 20:01:16 +01001126 mode |= MODE_OP_PENDING;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001127 if (vim_strchr(modechars, 'i') != NULL)
Bram Moolenaar24959102022-05-07 20:01:16 +01001128 mode |= MODE_INSERT;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001129 if (vim_strchr(modechars, 'l') != NULL)
Bram Moolenaar24959102022-05-07 20:01:16 +01001130 mode |= MODE_LANGMAP;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001131 if (vim_strchr(modechars, 'c') != NULL)
Bram Moolenaar24959102022-05-07 20:01:16 +01001132 mode |= MODE_CMDLINE;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001133
Bram Moolenaar581ba392019-09-03 22:08:33 +02001134 return mode;
1135}
1136
1137/*
1138 * Return TRUE if a map exists that has "str" in the rhs for mode "modechars".
1139 * Recognize termcap codes in "str".
1140 * Also checks mappings local to the current buffer.
1141 */
1142 int
1143map_to_exists(char_u *str, char_u *modechars, int abbr)
1144{
1145 char_u *rhs;
1146 char_u *buf;
1147 int retval;
1148
zeertzjq7e0bae02023-08-11 23:15:38 +02001149 rhs = replace_termcodes(str, &buf, 0, REPTERM_DO_LT, NULL);
Bram Moolenaar581ba392019-09-03 22:08:33 +02001150
1151 retval = map_to_exists_mode(rhs, mode_str2flags(modechars), abbr);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001152 vim_free(buf);
1153
1154 return retval;
1155}
1156#endif
1157
1158/*
1159 * Return TRUE if a map exists that has "str" in the rhs for mode "mode".
1160 * Also checks mappings local to the current buffer.
1161 */
1162 int
1163map_to_exists_mode(char_u *rhs, int mode, int abbr)
1164{
1165 mapblock_T *mp;
1166 int hash;
1167 int exp_buffer = FALSE;
1168
1169 validate_maphash();
1170
1171 // Do it twice: once for global maps and once for local maps.
1172 for (;;)
1173 {
1174 for (hash = 0; hash < 256; ++hash)
1175 {
1176 if (abbr)
1177 {
1178 if (hash > 0) // there is only one abbr list
1179 break;
1180 if (exp_buffer)
1181 mp = curbuf->b_first_abbr;
1182 else
1183 mp = first_abbr;
1184 }
1185 else if (exp_buffer)
1186 mp = curbuf->b_maphash[hash];
1187 else
1188 mp = maphash[hash];
1189 for (; mp; mp = mp->m_next)
1190 {
1191 if ((mp->m_mode & mode)
1192 && strstr((char *)mp->m_str, (char *)rhs) != NULL)
1193 return TRUE;
1194 }
1195 }
1196 if (exp_buffer)
1197 break;
1198 exp_buffer = TRUE;
1199 }
1200
1201 return FALSE;
1202}
1203
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001204/*
1205 * Used below when expanding mapping/abbreviation names.
1206 */
1207static int expand_mapmodes = 0;
1208static int expand_isabbrev = 0;
1209static int expand_buffer = FALSE;
1210
1211/*
Bram Moolenaar7f51bbe2020-01-24 20:21:19 +01001212 * Translate an internal mapping/abbreviation representation into the
1213 * corresponding external one recognized by :map/:abbrev commands.
1214 * Respects the current B/k/< settings of 'cpoption'.
1215 *
1216 * This function is called when expanding mappings/abbreviations on the
1217 * command-line.
1218 *
1219 * It uses a growarray to build the translation string since the latter can be
1220 * wider than the original description. The caller has to free the string
1221 * afterwards.
1222 *
1223 * Returns NULL when there is a problem.
1224 */
1225 static char_u *
1226translate_mapping(char_u *str)
1227{
1228 garray_T ga;
1229 int c;
1230 int modifiers;
1231 int cpo_bslash;
1232 int cpo_special;
1233
1234 ga_init(&ga);
1235 ga.ga_itemsize = 1;
1236 ga.ga_growsize = 40;
1237
1238 cpo_bslash = (vim_strchr(p_cpo, CPO_BSLASH) != NULL);
1239 cpo_special = (vim_strchr(p_cpo, CPO_SPECI) != NULL);
1240
1241 for (; *str; ++str)
1242 {
1243 c = *str;
1244 if (c == K_SPECIAL && str[1] != NUL && str[2] != NUL)
1245 {
1246 modifiers = 0;
1247 if (str[1] == KS_MODIFIER)
1248 {
1249 str++;
1250 modifiers = *++str;
1251 c = *++str;
1252 }
1253 if (c == K_SPECIAL && str[1] != NUL && str[2] != NUL)
1254 {
1255 if (cpo_special)
1256 {
1257 ga_clear(&ga);
1258 return NULL;
1259 }
1260 c = TO_SPECIAL(str[1], str[2]);
1261 if (c == K_ZERO) // display <Nul> as ^@
1262 c = NUL;
1263 str += 2;
1264 }
1265 if (IS_SPECIAL(c) || modifiers) // special key
1266 {
1267 if (cpo_special)
1268 {
1269 ga_clear(&ga);
1270 return NULL;
1271 }
1272 ga_concat(&ga, get_special_key_name(c, modifiers));
1273 continue; // for (str)
1274 }
1275 }
1276 if (c == ' ' || c == '\t' || c == Ctrl_J || c == Ctrl_V
1277 || (c == '<' && !cpo_special) || (c == '\\' && !cpo_bslash))
1278 ga_append(&ga, cpo_bslash ? Ctrl_V : '\\');
1279 if (c)
1280 ga_append(&ga, c);
1281 }
1282 ga_append(&ga, NUL);
1283 return (char_u *)(ga.ga_data);
1284}
1285
1286/*
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001287 * Work out what to complete when doing command line completion of mapping
1288 * or abbreviation names.
1289 */
1290 char_u *
1291set_context_in_map_cmd(
1292 expand_T *xp,
1293 char_u *cmd,
1294 char_u *arg,
1295 int forceit, // TRUE if '!' given
1296 int isabbrev, // TRUE if abbreviation
1297 int isunmap, // TRUE if unmap/unabbrev command
1298 cmdidx_T cmdidx)
1299{
1300 if (forceit && cmdidx != CMD_map && cmdidx != CMD_unmap)
1301 xp->xp_context = EXPAND_NOTHING;
1302 else
1303 {
1304 if (isunmap)
1305 expand_mapmodes = get_map_mode(&cmd, forceit || isabbrev);
1306 else
1307 {
Bram Moolenaar24959102022-05-07 20:01:16 +01001308 expand_mapmodes = MODE_INSERT | MODE_CMDLINE;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001309 if (!isabbrev)
Bram Moolenaar24959102022-05-07 20:01:16 +01001310 expand_mapmodes += MODE_VISUAL | MODE_SELECT | MODE_NORMAL
1311 | MODE_OP_PENDING;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001312 }
1313 expand_isabbrev = isabbrev;
1314 xp->xp_context = EXPAND_MAPPINGS;
1315 expand_buffer = FALSE;
1316 for (;;)
1317 {
1318 if (STRNCMP(arg, "<buffer>", 8) == 0)
1319 {
1320 expand_buffer = TRUE;
1321 arg = skipwhite(arg + 8);
1322 continue;
1323 }
1324 if (STRNCMP(arg, "<unique>", 8) == 0)
1325 {
1326 arg = skipwhite(arg + 8);
1327 continue;
1328 }
1329 if (STRNCMP(arg, "<nowait>", 8) == 0)
1330 {
1331 arg = skipwhite(arg + 8);
1332 continue;
1333 }
1334 if (STRNCMP(arg, "<silent>", 8) == 0)
1335 {
1336 arg = skipwhite(arg + 8);
1337 continue;
1338 }
1339 if (STRNCMP(arg, "<special>", 9) == 0)
1340 {
1341 arg = skipwhite(arg + 9);
1342 continue;
1343 }
1344#ifdef FEAT_EVAL
1345 if (STRNCMP(arg, "<script>", 8) == 0)
1346 {
1347 arg = skipwhite(arg + 8);
1348 continue;
1349 }
1350 if (STRNCMP(arg, "<expr>", 6) == 0)
1351 {
1352 arg = skipwhite(arg + 6);
1353 continue;
1354 }
1355#endif
1356 break;
1357 }
1358 xp->xp_pattern = arg;
1359 }
1360
1361 return NULL;
1362}
1363
1364/*
1365 * Find all mapping/abbreviation names that match regexp "regmatch"'.
1366 * For command line expansion of ":[un]map" and ":[un]abbrev" in all modes.
1367 * Return OK if matches found, FAIL otherwise.
1368 */
1369 int
1370ExpandMappings(
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001371 char_u *pat,
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001372 regmatch_T *regmatch,
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001373 int *numMatches,
1374 char_u ***matches)
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001375{
1376 mapblock_T *mp;
Yegappan Lakshmanan5de4c432022-02-28 13:28:38 +00001377 garray_T ga;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001378 int hash;
1379 int count;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001380 char_u *p;
1381 int i;
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001382 int fuzzy;
1383 int match;
Yasuhiro Matsumoto09f68a52022-06-18 16:48:36 +01001384 int score = 0;
Yegappan Lakshmanan5de4c432022-02-28 13:28:38 +00001385 fuzmatch_str_T *fuzmatch;
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001386
1387 fuzzy = cmdline_fuzzy_complete(pat);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001388
1389 validate_maphash();
1390
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001391 *numMatches = 0; // return values in case of FAIL
1392 *matches = NULL;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001393
Yegappan Lakshmanan5de4c432022-02-28 13:28:38 +00001394 if (!fuzzy)
1395 ga_init2(&ga, sizeof(char *), 3);
1396 else
1397 ga_init2(&ga, sizeof(fuzmatch_str_T), 3);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001398
Yegappan Lakshmanan5de4c432022-02-28 13:28:38 +00001399 // First search in map modifier arguments
1400 for (i = 0; i < 7; ++i)
1401 {
1402 if (i == 0)
1403 p = (char_u *)"<silent>";
1404 else if (i == 1)
1405 p = (char_u *)"<unique>";
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001406#ifdef FEAT_EVAL
Yegappan Lakshmanan5de4c432022-02-28 13:28:38 +00001407 else if (i == 2)
1408 p = (char_u *)"<script>";
1409 else if (i == 3)
1410 p = (char_u *)"<expr>";
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001411#endif
Yegappan Lakshmanan5de4c432022-02-28 13:28:38 +00001412 else if (i == 4 && !expand_buffer)
1413 p = (char_u *)"<buffer>";
1414 else if (i == 5)
1415 p = (char_u *)"<nowait>";
1416 else if (i == 6)
1417 p = (char_u *)"<special>";
1418 else
1419 continue;
1420
1421 if (!fuzzy)
1422 match = vim_regexec(regmatch, p, (colnr_T)0);
1423 else
1424 {
1425 score = fuzzy_match_str(p, pat);
1426 match = (score != 0);
1427 }
1428
1429 if (!match)
1430 continue;
1431
1432 if (ga_grow(&ga, 1) == FAIL)
1433 break;
1434
1435 if (fuzzy)
1436 {
1437 fuzmatch = &((fuzmatch_str_T *)ga.ga_data)[ga.ga_len];
1438 fuzmatch->idx = ga.ga_len;
1439 fuzmatch->str = vim_strsave(p);
1440 fuzmatch->score = score;
1441 }
1442 else
1443 ((char_u **)ga.ga_data)[ga.ga_len] = vim_strsave(p);
1444 ++ga.ga_len;
1445 }
1446
1447 for (hash = 0; hash < 256; ++hash)
1448 {
1449 if (expand_isabbrev)
1450 {
1451 if (hash > 0) // only one abbrev list
1452 break; // for (hash)
1453 mp = first_abbr;
1454 }
1455 else if (expand_buffer)
1456 mp = curbuf->b_maphash[hash];
1457 else
1458 mp = maphash[hash];
1459 for (; mp; mp = mp->m_next)
1460 {
zeertzjq997b8a02023-02-19 21:00:31 +00001461 if (mp->m_simplified || !(mp->m_mode & expand_mapmodes))
Yegappan Lakshmanan5de4c432022-02-28 13:28:38 +00001462 continue;
1463
1464 p = translate_mapping(mp->m_keys);
1465 if (p == NULL)
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001466 continue;
1467
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001468 if (!fuzzy)
1469 match = vim_regexec(regmatch, p, (colnr_T)0);
1470 else
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001471 {
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001472 score = fuzzy_match_str(p, pat);
1473 match = (score != 0);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001474 }
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001475
1476 if (!match)
Yegappan Lakshmanan5de4c432022-02-28 13:28:38 +00001477 {
1478 vim_free(p);
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001479 continue;
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001480 }
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001481
Yegappan Lakshmanan5de4c432022-02-28 13:28:38 +00001482 if (ga_grow(&ga, 1) == FAIL)
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001483 {
Yegappan Lakshmanan5de4c432022-02-28 13:28:38 +00001484 vim_free(p);
1485 break;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001486 }
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001487
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001488 if (fuzzy)
1489 {
Yegappan Lakshmanan5de4c432022-02-28 13:28:38 +00001490 fuzmatch = &((fuzmatch_str_T *)ga.ga_data)[ga.ga_len];
1491 fuzmatch->idx = ga.ga_len;
1492 fuzmatch->str = p;
1493 fuzmatch->score = score;
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001494 }
1495 else
Yegappan Lakshmanan5de4c432022-02-28 13:28:38 +00001496 ((char_u **)ga.ga_data)[ga.ga_len] = p;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001497
Yegappan Lakshmanan5de4c432022-02-28 13:28:38 +00001498 ++ga.ga_len;
1499 } // for (mp)
1500 } // for (hash)
1501
1502 if (ga.ga_len == 0)
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001503 return FAIL;
1504
Yegappan Lakshmanan5de4c432022-02-28 13:28:38 +00001505 if (!fuzzy)
1506 {
1507 *matches = ga.ga_data;
1508 *numMatches = ga.ga_len;
1509 }
1510 else
1511 {
1512 if (fuzzymatches_to_strmatches(ga.ga_data, matches, ga.ga_len,
1513 FALSE) == FAIL)
1514 return FAIL;
1515 *numMatches = ga.ga_len;
1516 }
1517
1518 count = *numMatches;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001519 if (count > 1)
1520 {
1521 char_u **ptr1;
1522 char_u **ptr2;
1523 char_u **ptr3;
1524
1525 // Sort the matches
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001526 // Fuzzy matching already sorts the matches
1527 if (!fuzzy)
1528 sort_strings(*matches, count);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001529
1530 // Remove multiple entries
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001531 ptr1 = *matches;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001532 ptr2 = ptr1 + 1;
1533 ptr3 = ptr1 + count;
1534
1535 while (ptr2 < ptr3)
1536 {
1537 if (STRCMP(*ptr1, *ptr2))
1538 *++ptr1 = *ptr2++;
1539 else
1540 {
1541 vim_free(*ptr2++);
1542 count--;
1543 }
1544 }
1545 }
1546
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001547 *numMatches = count;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001548 return (count == 0 ? FAIL : OK);
1549}
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001550
1551/*
1552 * Check for an abbreviation.
1553 * Cursor is at ptr[col].
1554 * When inserting, mincol is where insert started.
1555 * For the command line, mincol is what is to be skipped over.
1556 * "c" is the character typed before check_abbr was called. It may have
1557 * ABBR_OFF added to avoid prepending a CTRL-V to it.
1558 *
1559 * Historic vi practice: The last character of an abbreviation must be an id
1560 * character ([a-zA-Z0-9_]). The characters in front of it must be all id
1561 * characters or all non-id characters. This allows for abbr. "#i" to
1562 * "#include".
1563 *
1564 * Vim addition: Allow for abbreviations that end in a non-keyword character.
1565 * Then there must be white space before the abbr.
1566 *
1567 * return TRUE if there is an abbreviation, FALSE if not
1568 */
1569 int
1570check_abbr(
1571 int c,
1572 char_u *ptr,
1573 int col,
1574 int mincol)
1575{
1576 int len;
1577 int scol; // starting column of the abbr.
1578 int j;
1579 char_u *s;
1580 char_u tb[MB_MAXBYTES + 4];
1581 mapblock_T *mp;
1582 mapblock_T *mp2;
1583 int clen = 0; // length in characters
1584 int is_id = TRUE;
1585 int vim_abbr;
1586
1587 if (typebuf.tb_no_abbr_cnt) // abbrev. are not recursive
1588 return FALSE;
1589
1590 // no remapping implies no abbreviation, except for CTRL-]
1591 if (noremap_keys() && c != Ctrl_RSB)
1592 return FALSE;
1593
1594 // Check for word before the cursor: If it ends in a keyword char all
1595 // chars before it must be keyword chars or non-keyword chars, but not
1596 // white space. If it ends in a non-keyword char we accept any characters
1597 // before it except white space.
1598 if (col == 0) // cannot be an abbr.
1599 return FALSE;
1600
1601 if (has_mbyte)
1602 {
1603 char_u *p;
1604
1605 p = mb_prevptr(ptr, ptr + col);
1606 if (!vim_iswordp(p))
1607 vim_abbr = TRUE; // Vim added abbr.
1608 else
1609 {
1610 vim_abbr = FALSE; // vi compatible abbr.
1611 if (p > ptr)
1612 is_id = vim_iswordp(mb_prevptr(ptr, p));
1613 }
1614 clen = 1;
1615 while (p > ptr + mincol)
1616 {
1617 p = mb_prevptr(ptr, p);
1618 if (vim_isspace(*p) || (!vim_abbr && is_id != vim_iswordp(p)))
1619 {
1620 p += (*mb_ptr2len)(p);
1621 break;
1622 }
1623 ++clen;
1624 }
1625 scol = (int)(p - ptr);
1626 }
1627 else
1628 {
1629 if (!vim_iswordc(ptr[col - 1]))
1630 vim_abbr = TRUE; // Vim added abbr.
1631 else
1632 {
1633 vim_abbr = FALSE; // vi compatible abbr.
1634 if (col > 1)
1635 is_id = vim_iswordc(ptr[col - 2]);
1636 }
1637 for (scol = col - 1; scol > 0 && !vim_isspace(ptr[scol - 1])
1638 && (vim_abbr || is_id == vim_iswordc(ptr[scol - 1])); --scol)
1639 ;
1640 }
1641
1642 if (scol < mincol)
1643 scol = mincol;
1644 if (scol < col) // there is a word in front of the cursor
1645 {
1646 ptr += scol;
1647 len = col - scol;
1648 mp = curbuf->b_first_abbr;
1649 mp2 = first_abbr;
1650 if (mp == NULL)
1651 {
1652 mp = mp2;
1653 mp2 = NULL;
1654 }
1655 for ( ; mp; mp->m_next == NULL
1656 ? (mp = mp2, mp2 = NULL) : (mp = mp->m_next))
1657 {
1658 int qlen = mp->m_keylen;
1659 char_u *q = mp->m_keys;
1660 int match;
1661
1662 if (vim_strbyte(mp->m_keys, K_SPECIAL) != NULL)
1663 {
1664 char_u *qe = vim_strsave(mp->m_keys);
1665
1666 // might have CSI escaped mp->m_keys
1667 if (qe != NULL)
1668 {
1669 q = qe;
1670 vim_unescape_csi(q);
1671 qlen = (int)STRLEN(q);
1672 }
1673 }
1674
1675 // find entries with right mode and keys
1676 match = (mp->m_mode & State)
1677 && qlen == len
1678 && !STRNCMP(q, ptr, (size_t)len);
1679 if (q != mp->m_keys)
1680 vim_free(q);
1681 if (match)
1682 break;
1683 }
1684 if (mp != NULL)
1685 {
Bram Moolenaar94075b22022-01-18 20:30:39 +00001686 int noremap;
1687 int silent;
1688#ifdef FEAT_EVAL
1689 int expr;
1690#endif
1691
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001692 // Found a match:
1693 // Insert the rest of the abbreviation in typebuf.tb_buf[].
1694 // This goes from end to start.
1695 //
1696 // Characters 0x000 - 0x100: normal chars, may need CTRL-V,
1697 // except K_SPECIAL: Becomes K_SPECIAL KS_SPECIAL KE_FILLER
1698 // Characters where IS_SPECIAL() == TRUE: key codes, need
1699 // K_SPECIAL. Other characters (with ABBR_OFF): don't use CTRL-V.
1700 //
1701 // Character CTRL-] is treated specially - it completes the
1702 // abbreviation, but is not inserted into the input stream.
1703 j = 0;
1704 if (c != Ctrl_RSB)
1705 {
1706 // special key code, split up
1707 if (IS_SPECIAL(c) || c == K_SPECIAL)
1708 {
1709 tb[j++] = K_SPECIAL;
1710 tb[j++] = K_SECOND(c);
1711 tb[j++] = K_THIRD(c);
1712 }
1713 else
1714 {
1715 if (c < ABBR_OFF && (c < ' ' || c > '~'))
1716 tb[j++] = Ctrl_V; // special char needs CTRL-V
1717 if (has_mbyte)
1718 {
Bram Moolenaar4934ed32021-04-30 19:43:11 +02001719 int newlen;
1720 char_u *escaped;
1721
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001722 // if ABBR_OFF has been added, remove it here
1723 if (c >= ABBR_OFF)
1724 c -= ABBR_OFF;
Bram Moolenaar4934ed32021-04-30 19:43:11 +02001725 newlen = (*mb_char2bytes)(c, tb + j);
1726 tb[j + newlen] = NUL;
1727 // Need to escape K_SPECIAL.
1728 escaped = vim_strsave_escape_csi(tb + j);
1729 if (escaped != NULL)
1730 {
Bram Moolenaar551c1ae2021-05-03 18:57:05 +02001731 newlen = (int)STRLEN(escaped);
Bram Moolenaar4934ed32021-04-30 19:43:11 +02001732 mch_memmove(tb + j, escaped, newlen);
1733 j += newlen;
1734 vim_free(escaped);
1735 }
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001736 }
1737 else
1738 tb[j++] = c;
1739 }
1740 tb[j] = NUL;
1741 // insert the last typed char
1742 (void)ins_typebuf(tb, 1, 0, TRUE, mp->m_silent);
1743 }
Bram Moolenaar94075b22022-01-18 20:30:39 +00001744
1745 // copy values here, calling eval_map_expr() may make "mp" invalid!
1746 noremap = mp->m_noremap;
1747 silent = mp->m_silent;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001748#ifdef FEAT_EVAL
Bram Moolenaar94075b22022-01-18 20:30:39 +00001749 expr = mp->m_expr;
1750
1751 if (expr)
Bram Moolenaar19db9e62022-01-11 11:58:19 +00001752 s = eval_map_expr(mp, c);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001753 else
1754#endif
1755 s = mp->m_str;
1756 if (s != NULL)
1757 {
1758 // insert the to string
Bram Moolenaar94075b22022-01-18 20:30:39 +00001759 (void)ins_typebuf(s, noremap, 0, TRUE, silent);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001760 // no abbrev. for these chars
1761 typebuf.tb_no_abbr_cnt += (int)STRLEN(s) + j + 1;
1762#ifdef FEAT_EVAL
Bram Moolenaar94075b22022-01-18 20:30:39 +00001763 if (expr)
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001764 vim_free(s);
1765#endif
1766 }
1767
1768 tb[0] = Ctrl_H;
1769 tb[1] = NUL;
1770 if (has_mbyte)
1771 len = clen; // Delete characters instead of bytes
1772 while (len-- > 0) // delete the from string
Bram Moolenaar94075b22022-01-18 20:30:39 +00001773 (void)ins_typebuf(tb, 1, 0, TRUE, silent);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001774 return TRUE;
1775 }
1776 }
1777 return FALSE;
1778}
1779
1780#ifdef FEAT_EVAL
1781/*
1782 * Evaluate the RHS of a mapping or abbreviations and take care of escaping
1783 * special characters.
Bram Moolenaar94075b22022-01-18 20:30:39 +00001784 * Careful: after this "mp" will be invalid if the mapping was deleted.
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001785 */
1786 char_u *
1787eval_map_expr(
Bram Moolenaar19db9e62022-01-11 11:58:19 +00001788 mapblock_T *mp,
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001789 int c) // NUL or typed character for abbreviation
1790{
1791 char_u *res;
1792 char_u *p;
1793 char_u *expr;
1794 pos_T save_cursor;
1795 int save_msg_col;
1796 int save_msg_row;
Bram Moolenaar19db9e62022-01-11 11:58:19 +00001797 scid_T save_sctx_sid = current_sctx.sc_sid;
1798 int save_sctx_version = current_sctx.sc_version;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001799
1800 // Remove escaping of CSI, because "str" is in a format to be used as
1801 // typeahead.
Bram Moolenaar19db9e62022-01-11 11:58:19 +00001802 expr = vim_strsave(mp->m_str);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001803 if (expr == NULL)
1804 return NULL;
1805 vim_unescape_csi(expr);
1806
1807 // Forbid changing text or using ":normal" to avoid most of the bad side
1808 // effects. Also restore the cursor position.
zeertzjqcfe45652022-05-27 17:26:55 +01001809 ++textlock;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001810 ++ex_normal_lock;
1811 set_vim_var_char(c); // set v:char to the typed character
1812 save_cursor = curwin->w_cursor;
1813 save_msg_col = msg_col;
1814 save_msg_row = msg_row;
Bram Moolenaar19db9e62022-01-11 11:58:19 +00001815 if (mp->m_script_ctx.sc_version == SCRIPT_VERSION_VIM9)
1816 {
1817 current_sctx.sc_sid = mp->m_script_ctx.sc_sid;
1818 current_sctx.sc_version = SCRIPT_VERSION_VIM9;
1819 }
1820
1821 // Note: the evaluation may make "mp" invalid.
Bram Moolenaara4e0b972022-10-01 19:43:52 +01001822 p = eval_to_string(expr, FALSE, FALSE);
Bram Moolenaar19db9e62022-01-11 11:58:19 +00001823
zeertzjqcfe45652022-05-27 17:26:55 +01001824 --textlock;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001825 --ex_normal_lock;
1826 curwin->w_cursor = save_cursor;
1827 msg_col = save_msg_col;
1828 msg_row = save_msg_row;
Bram Moolenaar19db9e62022-01-11 11:58:19 +00001829 current_sctx.sc_sid = save_sctx_sid;
1830 current_sctx.sc_version = save_sctx_version;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001831
1832 vim_free(expr);
1833
1834 if (p == NULL)
1835 return NULL;
1836 // Escape CSI in the result to be able to use the string as typeahead.
1837 res = vim_strsave_escape_csi(p);
1838 vim_free(p);
1839
1840 return res;
1841}
1842#endif
1843
1844/*
1845 * Copy "p" to allocated memory, escaping K_SPECIAL and CSI so that the result
1846 * can be put in the typeahead buffer.
1847 * Returns NULL when out of memory.
1848 */
1849 char_u *
Bram Moolenaar957cf672020-11-12 14:21:06 +01001850vim_strsave_escape_csi(char_u *p)
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001851{
1852 char_u *res;
1853 char_u *s, *d;
1854
1855 // Need a buffer to hold up to three times as much. Four in case of an
1856 // illegal utf-8 byte:
1857 // 0xc0 -> 0xc3 0x80 -> 0xc3 K_SPECIAL KS_SPECIAL KE_FILLER
1858 res = alloc(STRLEN(p) * 4 + 1);
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00001859 if (res == NULL)
1860 return NULL;
1861
1862 d = res;
1863 for (s = p; *s != NUL; )
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001864 {
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00001865 if ((s[0] == K_SPECIAL
zeertzjq2cd0f272022-10-04 20:14:28 +01001866#ifdef FEAT_GUI
1867 || (gui.in_use && s[0] == CSI)
1868#endif
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00001869 ) && s[1] != NUL && s[2] != NUL)
1870 {
1871 // Copy special key unmodified.
1872 *d++ = *s++;
1873 *d++ = *s++;
1874 *d++ = *s++;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001875 }
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00001876 else
1877 {
1878 // Add character, possibly multi-byte to destination, escaping
1879 // CSI and K_SPECIAL. Be careful, it can be an illegal byte!
1880 d = add_char2buf(PTR2CHAR(s), d);
1881 s += MB_CPTR2LEN(s);
1882 }
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001883 }
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00001884 *d = NUL;
1885
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001886 return res;
1887}
1888
1889/*
1890 * Remove escaping from CSI and K_SPECIAL characters. Reverse of
1891 * vim_strsave_escape_csi(). Works in-place.
1892 */
1893 void
1894vim_unescape_csi(char_u *p)
1895{
1896 char_u *s = p, *d = p;
1897
1898 while (*s != NUL)
1899 {
1900 if (s[0] == K_SPECIAL && s[1] == KS_SPECIAL && s[2] == KE_FILLER)
1901 {
1902 *d++ = K_SPECIAL;
1903 s += 3;
1904 }
1905 else if ((s[0] == K_SPECIAL || s[0] == CSI)
1906 && s[1] == KS_EXTRA && s[2] == (int)KE_CSI)
1907 {
1908 *d++ = CSI;
1909 s += 3;
1910 }
1911 else
1912 *d++ = *s++;
1913 }
1914 *d = NUL;
1915}
1916
1917/*
1918 * Write map commands for the current mappings to an .exrc file.
1919 * Return FAIL on error, OK otherwise.
1920 */
1921 int
1922makemap(
1923 FILE *fd,
1924 buf_T *buf) // buffer for local mappings or NULL
1925{
1926 mapblock_T *mp;
1927 char_u c1, c2, c3;
1928 char_u *p;
1929 char *cmd;
1930 int abbr;
1931 int hash;
1932 int did_cpo = FALSE;
1933 int i;
1934
1935 validate_maphash();
1936
1937 // Do the loop twice: Once for mappings, once for abbreviations.
1938 // Then loop over all map hash lists.
1939 for (abbr = 0; abbr < 2; ++abbr)
1940 for (hash = 0; hash < 256; ++hash)
1941 {
1942 if (abbr)
1943 {
1944 if (hash > 0) // there is only one abbr list
1945 break;
1946 if (buf != NULL)
1947 mp = buf->b_first_abbr;
1948 else
1949 mp = first_abbr;
1950 }
1951 else
1952 {
1953 if (buf != NULL)
1954 mp = buf->b_maphash[hash];
1955 else
1956 mp = maphash[hash];
1957 }
1958
1959 for ( ; mp; mp = mp->m_next)
1960 {
1961 // skip script-local mappings
1962 if (mp->m_noremap == REMAP_SCRIPT)
1963 continue;
1964
1965 // skip mappings that contain a <SNR> (script-local thing),
1966 // they probably don't work when loaded again
1967 for (p = mp->m_str; *p != NUL; ++p)
1968 if (p[0] == K_SPECIAL && p[1] == KS_EXTRA
1969 && p[2] == (int)KE_SNR)
1970 break;
1971 if (*p != NUL)
1972 continue;
1973
1974 // It's possible to create a mapping and then ":unmap" certain
1975 // modes. We recreate this here by mapping the individual
1976 // modes, which requires up to three of them.
1977 c1 = NUL;
1978 c2 = NUL;
1979 c3 = NUL;
1980 if (abbr)
1981 cmd = "abbr";
1982 else
1983 cmd = "map";
1984 switch (mp->m_mode)
1985 {
Bram Moolenaar24959102022-05-07 20:01:16 +01001986 case MODE_NORMAL | MODE_VISUAL | MODE_SELECT
1987 | MODE_OP_PENDING:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001988 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01001989 case MODE_NORMAL:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001990 c1 = 'n';
1991 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01001992 case MODE_VISUAL:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001993 c1 = 'x';
1994 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01001995 case MODE_SELECT:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001996 c1 = 's';
1997 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01001998 case MODE_OP_PENDING:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001999 c1 = 'o';
2000 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01002001 case MODE_NORMAL | MODE_VISUAL:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002002 c1 = 'n';
2003 c2 = 'x';
2004 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01002005 case MODE_NORMAL | MODE_SELECT:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002006 c1 = 'n';
2007 c2 = 's';
2008 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01002009 case MODE_NORMAL | MODE_OP_PENDING:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002010 c1 = 'n';
2011 c2 = 'o';
2012 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01002013 case MODE_VISUAL | MODE_SELECT:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002014 c1 = 'v';
2015 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01002016 case MODE_VISUAL | MODE_OP_PENDING:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002017 c1 = 'x';
2018 c2 = 'o';
2019 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01002020 case MODE_SELECT | MODE_OP_PENDING:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002021 c1 = 's';
2022 c2 = 'o';
2023 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01002024 case MODE_NORMAL | MODE_VISUAL | MODE_SELECT:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002025 c1 = 'n';
2026 c2 = 'v';
2027 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01002028 case MODE_NORMAL | MODE_VISUAL | MODE_OP_PENDING:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002029 c1 = 'n';
2030 c2 = 'x';
2031 c3 = 'o';
2032 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01002033 case MODE_NORMAL | MODE_SELECT | MODE_OP_PENDING:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002034 c1 = 'n';
2035 c2 = 's';
2036 c3 = 'o';
2037 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01002038 case MODE_VISUAL | MODE_SELECT | MODE_OP_PENDING:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002039 c1 = 'v';
2040 c2 = 'o';
2041 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01002042 case MODE_CMDLINE | MODE_INSERT:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002043 if (!abbr)
2044 cmd = "map!";
2045 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01002046 case MODE_CMDLINE:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002047 c1 = 'c';
2048 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01002049 case MODE_INSERT:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002050 c1 = 'i';
2051 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01002052 case MODE_LANGMAP:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002053 c1 = 'l';
2054 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01002055 case MODE_TERMINAL:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002056 c1 = 't';
2057 break;
2058 default:
RestorerZ68ebcee2023-05-31 17:12:14 +01002059 iemsg(e_makemap_illegal_mode);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002060 return FAIL;
2061 }
2062 do // do this twice if c2 is set, 3 times with c3
2063 {
2064 // When outputting <> form, need to make sure that 'cpo'
2065 // is set to the Vim default.
2066 if (!did_cpo)
2067 {
2068 if (*mp->m_str == NUL) // will use <Nop>
2069 did_cpo = TRUE;
2070 else
2071 for (i = 0; i < 2; ++i)
2072 for (p = (i ? mp->m_str : mp->m_keys); *p; ++p)
2073 if (*p == K_SPECIAL || *p == NL)
2074 did_cpo = TRUE;
2075 if (did_cpo)
2076 {
2077 if (fprintf(fd, "let s:cpo_save=&cpo") < 0
2078 || put_eol(fd) < 0
2079 || fprintf(fd, "set cpo&vim") < 0
2080 || put_eol(fd) < 0)
2081 return FAIL;
2082 }
2083 }
2084 if (c1 && putc(c1, fd) < 0)
2085 return FAIL;
2086 if (mp->m_noremap != REMAP_YES && fprintf(fd, "nore") < 0)
2087 return FAIL;
2088 if (fputs(cmd, fd) < 0)
2089 return FAIL;
2090 if (buf != NULL && fputs(" <buffer>", fd) < 0)
2091 return FAIL;
2092 if (mp->m_nowait && fputs(" <nowait>", fd) < 0)
2093 return FAIL;
2094 if (mp->m_silent && fputs(" <silent>", fd) < 0)
2095 return FAIL;
2096#ifdef FEAT_EVAL
2097 if (mp->m_noremap == REMAP_SCRIPT
2098 && fputs("<script>", fd) < 0)
2099 return FAIL;
2100 if (mp->m_expr && fputs(" <expr>", fd) < 0)
2101 return FAIL;
2102#endif
2103
2104 if ( putc(' ', fd) < 0
2105 || put_escstr(fd, mp->m_keys, 0) == FAIL
2106 || putc(' ', fd) < 0
2107 || put_escstr(fd, mp->m_str, 1) == FAIL
2108 || put_eol(fd) < 0)
2109 return FAIL;
2110 c1 = c2;
2111 c2 = c3;
2112 c3 = NUL;
2113 } while (c1 != NUL);
2114 }
2115 }
2116
2117 if (did_cpo)
2118 if (fprintf(fd, "let &cpo=s:cpo_save") < 0
2119 || put_eol(fd) < 0
2120 || fprintf(fd, "unlet s:cpo_save") < 0
2121 || put_eol(fd) < 0)
2122 return FAIL;
2123 return OK;
2124}
2125
2126/*
2127 * write escape string to file
2128 * "what": 0 for :map lhs, 1 for :map rhs, 2 for :set
2129 *
2130 * return FAIL for failure, OK otherwise
2131 */
2132 int
2133put_escstr(FILE *fd, char_u *strstart, int what)
2134{
2135 char_u *str = strstart;
2136 int c;
2137 int modifiers;
2138
2139 // :map xx <Nop>
2140 if (*str == NUL && what == 1)
2141 {
2142 if (fprintf(fd, "<Nop>") < 0)
2143 return FAIL;
2144 return OK;
2145 }
2146
2147 for ( ; *str != NUL; ++str)
2148 {
2149 char_u *p;
2150
2151 // Check for a multi-byte character, which may contain escaped
2152 // K_SPECIAL and CSI bytes
2153 p = mb_unescape(&str);
2154 if (p != NULL)
2155 {
2156 while (*p != NUL)
2157 if (fputc(*p++, fd) < 0)
2158 return FAIL;
2159 --str;
2160 continue;
2161 }
2162
2163 c = *str;
2164 // Special key codes have to be translated to be able to make sense
2165 // when they are read back.
2166 if (c == K_SPECIAL && what != 2)
2167 {
Bram Moolenaar02c037a2020-08-30 19:26:45 +02002168 modifiers = 0;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002169 if (str[1] == KS_MODIFIER)
2170 {
2171 modifiers = str[2];
2172 str += 3;
2173 c = *str;
2174 }
2175 if (c == K_SPECIAL)
2176 {
2177 c = TO_SPECIAL(str[1], str[2]);
2178 str += 2;
2179 }
2180 if (IS_SPECIAL(c) || modifiers) // special key
2181 {
2182 if (fputs((char *)get_special_key_name(c, modifiers), fd) < 0)
2183 return FAIL;
2184 continue;
2185 }
2186 }
2187
2188 // A '\n' in a map command should be written as <NL>.
2189 // A '\n' in a set command should be written as \^V^J.
2190 if (c == NL)
2191 {
2192 if (what == 2)
2193 {
Bram Moolenaar424bcae2022-01-31 14:59:41 +00002194 if (fprintf(fd, "\\\026\n") < 0)
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002195 return FAIL;
2196 }
2197 else
2198 {
2199 if (fprintf(fd, "<NL>") < 0)
2200 return FAIL;
2201 }
2202 continue;
2203 }
2204
2205 // Some characters have to be escaped with CTRL-V to
2206 // prevent them from misinterpreted in DoOneCmd().
2207 // A space, Tab and '"' has to be escaped with a backslash to
2208 // prevent it to be misinterpreted in do_set().
2209 // A space has to be escaped with a CTRL-V when it's at the start of a
2210 // ":map" rhs.
2211 // A '<' has to be escaped with a CTRL-V to prevent it being
2212 // interpreted as the start of a special key name.
2213 // A space in the lhs of a :map needs a CTRL-V.
2214 if (what == 2 && (VIM_ISWHITE(c) || c == '"' || c == '\\'))
2215 {
2216 if (putc('\\', fd) < 0)
2217 return FAIL;
2218 }
2219 else if (c < ' ' || c > '~' || c == '|'
2220 || (what == 0 && c == ' ')
2221 || (what == 1 && str == strstart && c == ' ')
2222 || (what != 2 && c == '<'))
2223 {
2224 if (putc(Ctrl_V, fd) < 0)
2225 return FAIL;
2226 }
2227 if (putc(c, fd) < 0)
2228 return FAIL;
2229 }
2230 return OK;
2231}
2232
2233/*
2234 * Check all mappings for the presence of special key codes.
2235 * Used after ":set term=xxx".
2236 */
2237 void
2238check_map_keycodes(void)
2239{
2240 mapblock_T *mp;
2241 char_u *p;
2242 int i;
2243 char_u buf[3];
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002244 int abbr;
2245 int hash;
2246 buf_T *bp;
ichizok7e5fe382023-04-15 13:17:50 +01002247 ESTACK_CHECK_DECLARATION;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002248
2249 validate_maphash();
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002250 // avoids giving error messages
2251 estack_push(ETYPE_INTERNAL, (char_u *)"mappings", 0);
ichizok7e5fe382023-04-15 13:17:50 +01002252 ESTACK_CHECK_SETUP;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002253
Bram Moolenaar32aa1022019-11-02 22:54:41 +01002254 // Do this once for each buffer, and then once for global
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002255 // mappings/abbreviations with bp == NULL
2256 for (bp = firstbuf; ; bp = bp->b_next)
2257 {
2258 // Do the loop twice: Once for mappings, once for abbreviations.
2259 // Then loop over all map hash lists.
2260 for (abbr = 0; abbr <= 1; ++abbr)
2261 for (hash = 0; hash < 256; ++hash)
2262 {
2263 if (abbr)
2264 {
2265 if (hash) // there is only one abbr list
2266 break;
2267 if (bp != NULL)
2268 mp = bp->b_first_abbr;
2269 else
2270 mp = first_abbr;
2271 }
2272 else
2273 {
2274 if (bp != NULL)
2275 mp = bp->b_maphash[hash];
2276 else
2277 mp = maphash[hash];
2278 }
2279 for ( ; mp != NULL; mp = mp->m_next)
2280 {
2281 for (i = 0; i <= 1; ++i) // do this twice
2282 {
2283 if (i == 0)
2284 p = mp->m_keys; // once for the "from" part
2285 else
2286 p = mp->m_str; // and once for the "to" part
2287 while (*p)
2288 {
2289 if (*p == K_SPECIAL)
2290 {
2291 ++p;
2292 if (*p < 128) // for "normal" tcap entries
2293 {
2294 buf[0] = p[0];
2295 buf[1] = p[1];
2296 buf[2] = NUL;
2297 (void)add_termcap_entry(buf, FALSE);
2298 }
2299 ++p;
2300 }
2301 ++p;
2302 }
2303 }
2304 }
2305 }
2306 if (bp == NULL)
2307 break;
2308 }
ichizok7e5fe382023-04-15 13:17:50 +01002309 ESTACK_CHECK_NOW;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002310 estack_pop();
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002311}
2312
2313#if defined(FEAT_EVAL) || defined(PROTO)
2314/*
2315 * Check the string "keys" against the lhs of all mappings.
2316 * Return pointer to rhs of mapping (mapblock->m_str).
2317 * NULL when no mapping found.
2318 */
2319 char_u *
2320check_map(
2321 char_u *keys,
2322 int mode,
2323 int exact, // require exact match
2324 int ign_mod, // ignore preceding modifier
2325 int abbr, // do abbreviations
2326 mapblock_T **mp_ptr, // return: pointer to mapblock or NULL
2327 int *local_ptr) // return: buffer-local mapping or NULL
2328{
2329 int hash;
2330 int len, minlen;
2331 mapblock_T *mp;
2332 char_u *s;
2333 int local;
2334
2335 validate_maphash();
2336
2337 len = (int)STRLEN(keys);
2338 for (local = 1; local >= 0; --local)
2339 // loop over all hash lists
2340 for (hash = 0; hash < 256; ++hash)
2341 {
2342 if (abbr)
2343 {
2344 if (hash > 0) // there is only one list.
2345 break;
2346 if (local)
2347 mp = curbuf->b_first_abbr;
2348 else
2349 mp = first_abbr;
2350 }
2351 else if (local)
2352 mp = curbuf->b_maphash[hash];
2353 else
2354 mp = maphash[hash];
2355 for ( ; mp != NULL; mp = mp->m_next)
2356 {
2357 // skip entries with wrong mode, wrong length and not matching
2358 // ones
2359 if ((mp->m_mode & mode) && (!exact || mp->m_keylen == len))
2360 {
2361 if (len > mp->m_keylen)
2362 minlen = mp->m_keylen;
2363 else
2364 minlen = len;
2365 s = mp->m_keys;
2366 if (ign_mod && s[0] == K_SPECIAL && s[1] == KS_MODIFIER
2367 && s[2] != NUL)
2368 {
2369 s += 3;
2370 if (len > mp->m_keylen - 3)
2371 minlen = mp->m_keylen - 3;
2372 }
2373 if (STRNCMP(s, keys, minlen) == 0)
2374 {
2375 if (mp_ptr != NULL)
2376 *mp_ptr = mp;
2377 if (local_ptr != NULL)
2378 *local_ptr = local;
2379 return mp->m_str;
2380 }
2381 }
2382 }
2383 }
2384
2385 return NULL;
2386}
2387
Ernie Rael659c2402022-04-24 18:40:28 +01002388/*
zeertzjqc207fd22022-06-29 10:37:40 +01002389 * "hasmapto()" function
2390 */
2391 void
2392f_hasmapto(typval_T *argvars, typval_T *rettv)
2393{
2394 char_u *name;
2395 char_u *mode;
2396 char_u buf[NUMBUFLEN];
2397 int abbr = FALSE;
2398
2399 if (in_vim9script()
2400 && (check_for_string_arg(argvars, 0) == FAIL
2401 || check_for_opt_string_arg(argvars, 1) == FAIL
2402 || (argvars[1].v_type != VAR_UNKNOWN
2403 && check_for_opt_bool_arg(argvars, 2) == FAIL)))
2404 return;
2405
2406 name = tv_get_string(&argvars[0]);
2407 if (argvars[1].v_type == VAR_UNKNOWN)
2408 mode = (char_u *)"nvo";
2409 else
2410 {
2411 mode = tv_get_string_buf(&argvars[1], buf);
2412 if (argvars[2].v_type != VAR_UNKNOWN)
2413 abbr = (int)tv_get_bool(&argvars[2]);
2414 }
2415
2416 if (map_to_exists(name, mode, abbr))
2417 rettv->vval.v_number = TRUE;
2418 else
2419 rettv->vval.v_number = FALSE;
2420}
2421
2422/*
Ernie Rael659c2402022-04-24 18:40:28 +01002423 * Fill in the empty dictionary with items as defined by maparg builtin.
2424 */
2425 static void
2426mapblock2dict(
2427 mapblock_T *mp,
2428 dict_T *dict,
2429 char_u *lhsrawalt, // may be NULL
Ernie Rael51d04d12022-05-04 15:40:22 +01002430 int buffer_local, // false if not buffer local mapping
2431 int abbr) // true if abbreviation
Ernie Rael659c2402022-04-24 18:40:28 +01002432{
zeertzjqcdc83932022-09-12 13:38:41 +01002433 char_u *lhs = str2special_save(mp->m_keys, TRUE, FALSE);
Ernie Rael659c2402022-04-24 18:40:28 +01002434 char_u *mapmode = map_mode_to_chars(mp->m_mode);
2435
2436 dict_add_string(dict, "lhs", lhs);
2437 vim_free(lhs);
2438 dict_add_string(dict, "lhsraw", mp->m_keys);
2439 if (lhsrawalt)
2440 // Also add the value for the simplified entry.
2441 dict_add_string(dict, "lhsrawalt", lhsrawalt);
2442 dict_add_string(dict, "rhs", mp->m_orig_str);
2443 dict_add_number(dict, "noremap", mp->m_noremap ? 1L : 0L);
2444 dict_add_number(dict, "script", mp->m_noremap == REMAP_SCRIPT
2445 ? 1L : 0L);
2446 dict_add_number(dict, "expr", mp->m_expr ? 1L : 0L);
2447 dict_add_number(dict, "silent", mp->m_silent ? 1L : 0L);
2448 dict_add_number(dict, "sid", (long)mp->m_script_ctx.sc_sid);
2449 dict_add_number(dict, "scriptversion",
2450 (long)mp->m_script_ctx.sc_version);
2451 dict_add_number(dict, "lnum", (long)mp->m_script_ctx.sc_lnum);
2452 dict_add_number(dict, "buffer", (long)buffer_local);
2453 dict_add_number(dict, "nowait", mp->m_nowait ? 1L : 0L);
2454 dict_add_string(dict, "mode", mapmode);
Ernie Rael51d04d12022-05-04 15:40:22 +01002455 dict_add_number(dict, "abbr", abbr ? 1L : 0L);
Ernie Raeld8f5f762022-05-10 17:50:39 +01002456 dict_add_number(dict, "mode_bits", mp->m_mode);
Ernie Rael659c2402022-04-24 18:40:28 +01002457
2458 vim_free(mapmode);
2459}
2460
Yegappan Lakshmanan4a155042021-07-30 21:32:45 +02002461 static void
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002462get_maparg(typval_T *argvars, typval_T *rettv, int exact)
2463{
2464 char_u *keys;
Bram Moolenaar9c652532020-05-24 13:10:18 +02002465 char_u *keys_simplified;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002466 char_u *which;
2467 char_u buf[NUMBUFLEN];
2468 char_u *keys_buf = NULL;
Bram Moolenaar9c652532020-05-24 13:10:18 +02002469 char_u *alt_keys_buf = NULL;
2470 int did_simplify = FALSE;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002471 char_u *rhs;
2472 int mode;
2473 int abbr = FALSE;
2474 int get_dict = FALSE;
zeertzjq2c8a7eb2022-04-26 21:36:21 +01002475 mapblock_T *mp = NULL;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002476 int buffer_local;
Bram Moolenaar9c652532020-05-24 13:10:18 +02002477 int flags = REPTERM_FROM_PART | REPTERM_DO_LT;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002478
2479 // return empty string for failure
2480 rettv->v_type = VAR_STRING;
2481 rettv->vval.v_string = NULL;
2482
2483 keys = tv_get_string(&argvars[0]);
2484 if (*keys == NUL)
2485 return;
2486
2487 if (argvars[1].v_type != VAR_UNKNOWN)
2488 {
2489 which = tv_get_string_buf_chk(&argvars[1], buf);
2490 if (argvars[2].v_type != VAR_UNKNOWN)
2491 {
Bram Moolenaar04d594b2020-09-02 22:25:35 +02002492 abbr = (int)tv_get_bool(&argvars[2]);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002493 if (argvars[3].v_type != VAR_UNKNOWN)
Bram Moolenaar04d594b2020-09-02 22:25:35 +02002494 get_dict = (int)tv_get_bool(&argvars[3]);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002495 }
2496 }
2497 else
2498 which = (char_u *)"";
2499 if (which == NULL)
2500 return;
2501
2502 mode = get_map_mode(&which, 0);
2503
zeertzjq7e0bae02023-08-11 23:15:38 +02002504 keys_simplified = replace_termcodes(keys, &keys_buf, 0, flags,
2505 &did_simplify);
Bram Moolenaar9c652532020-05-24 13:10:18 +02002506 rhs = check_map(keys_simplified, mode, exact, FALSE, abbr,
2507 &mp, &buffer_local);
2508 if (did_simplify)
2509 {
2510 // When the lhs is being simplified the not-simplified keys are
Bram Moolenaar8e7d6222020-12-18 19:49:56 +01002511 // preferred for printing, like in do_map().
zeertzjq7e0bae02023-08-11 23:15:38 +02002512 (void)replace_termcodes(keys, &alt_keys_buf, 0,
Bram Moolenaar9c652532020-05-24 13:10:18 +02002513 flags | REPTERM_NO_SIMPLIFY, NULL);
2514 rhs = check_map(alt_keys_buf, mode, exact, FALSE, abbr, &mp,
2515 &buffer_local);
2516 }
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002517
2518 if (!get_dict)
2519 {
2520 // Return a string.
2521 if (rhs != NULL)
2522 {
2523 if (*rhs == NUL)
2524 rettv->vval.v_string = vim_strsave((char_u *)"<Nop>");
2525 else
zeertzjqcdc83932022-09-12 13:38:41 +01002526 rettv->vval.v_string = str2special_save(rhs, FALSE, FALSE);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002527 }
2528
2529 }
Bram Moolenaar93a10962022-06-16 11:42:09 +01002530 else if (rettv_dict_alloc(rettv) == OK && rhs != NULL)
Ernie Rael659c2402022-04-24 18:40:28 +01002531 mapblock2dict(mp, rettv->vval.v_dict,
Ernie Rael51d04d12022-05-04 15:40:22 +01002532 did_simplify ? keys_simplified : NULL,
2533 buffer_local, abbr);
Bram Moolenaar9c652532020-05-24 13:10:18 +02002534
2535 vim_free(keys_buf);
2536 vim_free(alt_keys_buf);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002537}
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002538
2539/*
Ernie Rael09661202022-04-25 14:40:44 +01002540 * "maplist()" function
Ernie Rael659c2402022-04-24 18:40:28 +01002541 */
2542 void
Ernie Rael09661202022-04-25 14:40:44 +01002543f_maplist(typval_T *argvars UNUSED, typval_T *rettv)
Ernie Rael659c2402022-04-24 18:40:28 +01002544{
2545 dict_T *d;
2546 mapblock_T *mp;
2547 int buffer_local;
2548 char_u *keys_buf;
2549 int did_simplify;
2550 int hash;
2551 char_u *lhs;
2552 const int flags = REPTERM_FROM_PART | REPTERM_DO_LT;
Ernie Rael09661202022-04-25 14:40:44 +01002553 int abbr = FALSE;
2554
2555 if (in_vim9script() && check_for_opt_bool_arg(argvars, 0) == FAIL)
2556 return;
2557 if (argvars[0].v_type != VAR_UNKNOWN)
2558 abbr = tv_get_bool(&argvars[0]);
Ernie Rael659c2402022-04-24 18:40:28 +01002559
Bram Moolenaar93a10962022-06-16 11:42:09 +01002560 if (rettv_list_alloc(rettv) == FAIL)
Ernie Rael659c2402022-04-24 18:40:28 +01002561 return;
2562
2563 validate_maphash();
2564
2565 // Do it twice: once for global maps and once for local maps.
2566 for (buffer_local = 0; buffer_local <= 1; ++buffer_local)
2567 {
2568 for (hash = 0; hash < 256; ++hash)
2569 {
Ernie Rael09661202022-04-25 14:40:44 +01002570 if (abbr)
2571 {
2572 if (hash > 0) // there is only one abbr list
2573 break;
2574 if (buffer_local)
2575 mp = curbuf->b_first_abbr;
2576 else
2577 mp = first_abbr;
2578 }
2579 else if (buffer_local)
Ernie Rael659c2402022-04-24 18:40:28 +01002580 mp = curbuf->b_maphash[hash];
2581 else
2582 mp = maphash[hash];
2583 for (; mp; mp = mp->m_next)
2584 {
2585 if (mp->m_simplified)
2586 continue;
2587 if ((d = dict_alloc()) == NULL)
2588 return;
2589 if (list_append_dict(rettv->vval.v_list, d) == FAIL)
Christian Brabandt29269a72024-04-16 22:44:31 +02002590 {
2591 dict_unref(d);
Ernie Rael659c2402022-04-24 18:40:28 +01002592 return;
Christian Brabandt29269a72024-04-16 22:44:31 +02002593 }
Ernie Rael659c2402022-04-24 18:40:28 +01002594
2595 keys_buf = NULL;
2596 did_simplify = FALSE;
2597
zeertzjqcdc83932022-09-12 13:38:41 +01002598 lhs = str2special_save(mp->m_keys, TRUE, FALSE);
zeertzjq7e0bae02023-08-11 23:15:38 +02002599 (void)replace_termcodes(lhs, &keys_buf, 0, flags,
2600 &did_simplify);
Ernie Rael659c2402022-04-24 18:40:28 +01002601 vim_free(lhs);
2602
2603 mapblock2dict(mp, d,
Ernie Rael51d04d12022-05-04 15:40:22 +01002604 did_simplify ? keys_buf : NULL,
2605 buffer_local, abbr);
Ernie Rael659c2402022-04-24 18:40:28 +01002606 vim_free(keys_buf);
2607 }
2608 }
2609 }
2610}
2611
2612/*
Yegappan Lakshmanan4a155042021-07-30 21:32:45 +02002613 * "maparg()" function
2614 */
2615 void
2616f_maparg(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 || (argvars[2].v_type != VAR_UNKNOWN
2624 && check_for_opt_bool_arg(argvars, 3) == FAIL)))))
2625 return;
2626
2627 get_maparg(argvars, rettv, TRUE);
2628}
2629
2630/*
2631 * "mapcheck()" function
2632 */
2633 void
2634f_mapcheck(typval_T *argvars, typval_T *rettv)
2635{
2636 if (in_vim9script()
2637 && (check_for_string_arg(argvars, 0) == FAIL
2638 || check_for_opt_string_arg(argvars, 1) == FAIL
2639 || (argvars[1].v_type != VAR_UNKNOWN
2640 && check_for_opt_bool_arg(argvars, 2) == FAIL)))
2641 return;
2642
2643 get_maparg(argvars, rettv, FALSE);
2644}
2645
2646/*
Ernie Rael51d04d12022-05-04 15:40:22 +01002647 * Get the mapping mode from the mode string.
2648 * It may contain multiple characters, eg "nox", or "!", or ' '
2649 * Return 0 if there is an error.
2650 */
2651 static int
2652get_map_mode_string(char_u *mode_string, int abbr)
2653{
2654 char_u *p = mode_string;
2655 int mode = 0;
2656 int tmode;
2657 int modec;
Bram Moolenaar24959102022-05-07 20:01:16 +01002658 const int MASK_V = MODE_VISUAL | MODE_SELECT;
2659 const int MASK_MAP = MODE_VISUAL | MODE_SELECT | MODE_NORMAL
2660 | MODE_OP_PENDING;
2661 const int MASK_BANG = MODE_INSERT | MODE_CMDLINE;
Ernie Rael51d04d12022-05-04 15:40:22 +01002662
2663 if (*p == NUL)
2664 p = (char_u *)" "; // compatibility
2665 while ((modec = *p++))
2666 {
2667 switch (modec)
2668 {
Bram Moolenaar24959102022-05-07 20:01:16 +01002669 case 'i': tmode = MODE_INSERT; break;
2670 case 'l': tmode = MODE_LANGMAP; break;
2671 case 'c': tmode = MODE_CMDLINE; break;
2672 case 'n': tmode = MODE_NORMAL; break;
2673 case 'x': tmode = MODE_VISUAL; break;
2674 case 's': tmode = MODE_SELECT; break;
2675 case 'o': tmode = MODE_OP_PENDING; break;
2676 case 't': tmode = MODE_TERMINAL; break;
Ernie Rael51d04d12022-05-04 15:40:22 +01002677 case 'v': tmode = MASK_V; break;
2678 case '!': tmode = MASK_BANG; break;
2679 case ' ': tmode = MASK_MAP; break;
2680 default:
2681 return 0; // error, unknown mode character
2682 }
2683 mode |= tmode;
2684 }
2685 if ((abbr && (mode & ~MASK_BANG) != 0)
2686 || (!abbr && (mode & (mode-1)) != 0 // more than one bit set
2687 && (
2688 // false if multiple bits set in mode and mode is fully
2689 // contained in one mask
2690 !(((mode & MASK_BANG) != 0 && (mode & ~MASK_BANG) == 0)
2691 || ((mode & MASK_MAP) != 0 && (mode & ~MASK_MAP) == 0)))))
2692 return 0;
2693
2694 return mode;
2695}
2696
2697/*
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002698 * "mapset()" function
2699 */
2700 void
2701f_mapset(typval_T *argvars, typval_T *rettv UNUSED)
2702{
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002703 char_u *which;
2704 int mode;
2705 char_u buf[NUMBUFLEN];
2706 int is_abbr;
2707 dict_T *d;
2708 char_u *lhs;
Bram Moolenaar9c652532020-05-24 13:10:18 +02002709 char_u *lhsraw;
2710 char_u *lhsrawalt;
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002711 char_u *rhs;
Bram Moolenaarc94c1462020-05-22 20:01:06 +02002712 char_u *orig_rhs;
2713 char_u *arg_buf = NULL;
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002714 int noremap;
2715 int expr;
2716 int silent;
Bram Moolenaar7ba1e4d2021-04-24 13:12:38 +02002717 int buffer;
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002718 scid_T sid;
Bram Moolenaara9528b32022-01-18 20:51:35 +00002719 int scriptversion;
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002720 linenr_T lnum;
2721 mapblock_T **map_table = maphash;
2722 mapblock_T **abbr_table = &first_abbr;
2723 int nowait;
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002724 char_u *arg;
Ernie Rael51d04d12022-05-04 15:40:22 +01002725 int dict_only;
zeertzjq9d997ad2024-07-29 21:10:07 +02002726 mapblock_T *mp_result[2] = {NULL, NULL};
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002727
Ernie Rael51d04d12022-05-04 15:40:22 +01002728 // If first arg is a dict, then that's the only arg permitted.
2729 dict_only = argvars[0].v_type == VAR_DICT;
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02002730 if (in_vim9script()
Ernie Rael51d04d12022-05-04 15:40:22 +01002731 && (check_for_string_or_dict_arg(argvars, 0) == FAIL
2732 || (dict_only && check_for_unknown_arg(argvars, 1) == FAIL)
2733 || (!dict_only
2734 && (check_for_string_arg(argvars, 0) == FAIL
2735 || check_for_bool_arg(argvars, 1) == FAIL
2736 || check_for_dict_arg(argvars, 2) == FAIL))))
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02002737 return;
2738
Ernie Rael51d04d12022-05-04 15:40:22 +01002739 if (dict_only)
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002740 {
Ernie Rael51d04d12022-05-04 15:40:22 +01002741 d = argvars[0].vval.v_dict;
Bram Moolenaard61efa52022-07-23 09:52:04 +01002742 which = dict_get_string(d, "mode", FALSE);
2743 is_abbr = dict_get_bool(d, "abbr", -1);
Ernie Rael51d04d12022-05-04 15:40:22 +01002744 if (which == NULL || is_abbr < 0)
2745 {
2746 emsg(_(e_entries_missing_in_mapset_dict_argument));
2747 return;
2748 }
2749 }
2750 else
2751 {
2752 which = tv_get_string_buf_chk(&argvars[0], buf);
2753 if (which == NULL)
2754 return;
2755 is_abbr = (int)tv_get_bool(&argvars[1]);
2756
Yegappan Lakshmanan04c4c572022-08-30 19:48:24 +01002757 if (check_for_dict_arg(argvars, 2) == FAIL)
Ernie Rael51d04d12022-05-04 15:40:22 +01002758 return;
Ernie Rael51d04d12022-05-04 15:40:22 +01002759 d = argvars[2].vval.v_dict;
2760 }
2761 mode = get_map_mode_string(which, is_abbr);
2762 if (mode == 0)
2763 {
2764 semsg(_(e_illegal_map_mode_string_str), which);
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002765 return;
2766 }
Ernie Rael51d04d12022-05-04 15:40:22 +01002767
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002768
2769 // Get the values in the same order as above in get_maparg().
Bram Moolenaard61efa52022-07-23 09:52:04 +01002770 lhs = dict_get_string(d, "lhs", FALSE);
2771 lhsraw = dict_get_string(d, "lhsraw", FALSE);
2772 lhsrawalt = dict_get_string(d, "lhsrawalt", FALSE);
2773 rhs = dict_get_string(d, "rhs", FALSE);
Bram Moolenaar9c652532020-05-24 13:10:18 +02002774 if (lhs == NULL || lhsraw == NULL || rhs == NULL)
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002775 {
Bram Moolenaarb09feaa2022-01-02 20:20:45 +00002776 emsg(_(e_entries_missing_in_mapset_dict_argument));
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002777 return;
2778 }
Bram Moolenaarc94c1462020-05-22 20:01:06 +02002779 orig_rhs = rhs;
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002780
Bram Moolenaard61efa52022-07-23 09:52:04 +01002781 noremap = dict_get_number(d, "noremap") ? REMAP_NONE: 0;
2782 if (dict_get_number(d, "script") != 0)
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002783 noremap = REMAP_SCRIPT;
Bram Moolenaard61efa52022-07-23 09:52:04 +01002784 expr = dict_get_number(d, "expr") != 0;
2785 silent = dict_get_number(d, "silent") != 0;
2786 sid = dict_get_number(d, "sid");
2787 scriptversion = dict_get_number(d, "scriptversion");
2788 lnum = dict_get_number(d, "lnum");
2789 buffer = dict_get_number(d, "buffer");
2790 nowait = dict_get_number(d, "nowait") != 0;
Bram Moolenaar7ba1e4d2021-04-24 13:12:38 +02002791 // mode from the dict is not used
2792
zeertzjq7e0bae02023-08-11 23:15:38 +02002793 if (STRICMP(rhs, "<nop>") == 0) // "<Nop>" means nothing
2794 rhs = (char_u *)"";
2795 else
2796 rhs = replace_termcodes(rhs, &arg_buf, sid,
2797 REPTERM_DO_LT | REPTERM_SPECIAL, NULL);
2798
Bram Moolenaar7ba1e4d2021-04-24 13:12:38 +02002799 if (buffer)
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002800 {
2801 map_table = curbuf->b_maphash;
2802 abbr_table = &curbuf->b_first_abbr;
2803 }
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002804
2805 // Delete any existing mapping for this lhs and mode.
Bram Moolenaar7ba1e4d2021-04-24 13:12:38 +02002806 if (buffer)
2807 {
2808 arg = alloc(STRLEN(lhs) + STRLEN("<buffer>") + 1);
2809 if (arg == NULL)
2810 return;
2811 STRCPY(arg, "<buffer>");
2812 STRCPY(arg + 8, lhs);
2813 }
2814 else
2815 {
2816 arg = vim_strsave(lhs);
2817 if (arg == NULL)
2818 return;
2819 }
zeertzjq44068e92022-06-16 11:14:55 +01002820 do_map(MAPTYPE_UNMAP, arg, mode, is_abbr);
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002821 vim_free(arg);
2822
zeertzjq9d997ad2024-07-29 21:10:07 +02002823 mp_result[0] = map_add(map_table, abbr_table, lhsraw, rhs, orig_rhs,
2824 noremap, nowait, silent, mode, is_abbr, expr, sid,
2825 scriptversion, lnum, 0);
Bram Moolenaar9c652532020-05-24 13:10:18 +02002826 if (lhsrawalt != NULL)
zeertzjq9d997ad2024-07-29 21:10:07 +02002827 mp_result[1] = map_add(map_table, abbr_table, lhsrawalt, rhs, orig_rhs,
2828 noremap, nowait, silent, mode, is_abbr, expr, sid,
2829 scriptversion, lnum, 1);
2830
2831 if (mp_result[0] != NULL && mp_result[1] != NULL)
2832 {
2833 mp_result[0]->m_alt = mp_result[1];
2834 mp_result[1]->m_alt = mp_result[0];
2835 }
2836
Bram Moolenaarc94c1462020-05-22 20:01:06 +02002837 vim_free(arg_buf);
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002838}
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002839#endif
2840
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002841
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002842#if defined(MSWIN) || defined(MACOS_X)
2843
Bram Moolenaar24959102022-05-07 20:01:16 +01002844# define VIS_SEL (MODE_VISUAL | MODE_SELECT) // abbreviation
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002845
2846/*
2847 * Default mappings for some often used keys.
2848 */
2849struct initmap
2850{
2851 char_u *arg;
2852 int mode;
2853};
2854
2855# ifdef FEAT_GUI_MSWIN
2856// Use the Windows (CUA) keybindings. (GUI)
2857static struct initmap initmappings[] =
2858{
2859 // paste, copy and cut
Bram Moolenaar24959102022-05-07 20:01:16 +01002860 {(char_u *)"<S-Insert> \"*P", MODE_NORMAL},
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002861 {(char_u *)"<S-Insert> \"-d\"*P", VIS_SEL},
Bram Moolenaar24959102022-05-07 20:01:16 +01002862 {(char_u *)"<S-Insert> <C-R><C-O>*", MODE_INSERT | MODE_CMDLINE},
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002863 {(char_u *)"<C-Insert> \"*y", VIS_SEL},
2864 {(char_u *)"<S-Del> \"*d", VIS_SEL},
2865 {(char_u *)"<C-Del> \"*d", VIS_SEL},
2866 {(char_u *)"<C-X> \"*d", VIS_SEL},
2867 // Missing: CTRL-C (cancel) and CTRL-V (block selection)
2868};
2869# endif
2870
2871# if defined(MSWIN) && (!defined(FEAT_GUI) || defined(VIMDLL))
2872// Use the Windows (CUA) keybindings. (Console)
2873static struct initmap cinitmappings[] =
2874{
Bram Moolenaar24959102022-05-07 20:01:16 +01002875 {(char_u *)"\316w <C-Home>", MODE_NORMAL | VIS_SEL},
2876 {(char_u *)"\316w <C-Home>", MODE_INSERT | MODE_CMDLINE},
2877 {(char_u *)"\316u <C-End>", MODE_NORMAL | VIS_SEL},
2878 {(char_u *)"\316u <C-End>", MODE_INSERT | MODE_CMDLINE},
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002879
2880 // paste, copy and cut
2881# ifdef FEAT_CLIPBOARD
Bram Moolenaar24959102022-05-07 20:01:16 +01002882 {(char_u *)"\316\324 \"*P", MODE_NORMAL}, // SHIFT-Insert is "*P
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002883 {(char_u *)"\316\324 \"-d\"*P", VIS_SEL}, // SHIFT-Insert is "-d"*P
Bram Moolenaar24959102022-05-07 20:01:16 +01002884 {(char_u *)"\316\324 \022\017*", MODE_INSERT}, // SHIFT-Insert is ^R^O*
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002885 {(char_u *)"\316\325 \"*y", VIS_SEL}, // CTRL-Insert is "*y
2886 {(char_u *)"\316\327 \"*d", VIS_SEL}, // SHIFT-Del is "*d
2887 {(char_u *)"\316\330 \"*d", VIS_SEL}, // CTRL-Del is "*d
2888 {(char_u *)"\030 \"*d", VIS_SEL}, // CTRL-X is "*d
2889# else
Bram Moolenaar24959102022-05-07 20:01:16 +01002890 {(char_u *)"\316\324 P", MODE_NORMAL}, // SHIFT-Insert is P
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002891 {(char_u *)"\316\324 \"-dP", VIS_SEL}, // SHIFT-Insert is "-dP
Bram Moolenaar24959102022-05-07 20:01:16 +01002892 {(char_u *)"\316\324 \022\017\"", MODE_INSERT}, // SHIFT-Insert is ^R^O"
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002893 {(char_u *)"\316\325 y", VIS_SEL}, // CTRL-Insert is y
2894 {(char_u *)"\316\327 d", VIS_SEL}, // SHIFT-Del is d
2895 {(char_u *)"\316\330 d", VIS_SEL}, // CTRL-Del is d
2896# endif
2897};
2898# endif
2899
2900# if defined(MACOS_X)
2901static struct initmap initmappings[] =
2902{
2903 // Use the Standard MacOS binding.
2904 // paste, copy and cut
Bram Moolenaar24959102022-05-07 20:01:16 +01002905 {(char_u *)"<D-v> \"*P", MODE_NORMAL},
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002906 {(char_u *)"<D-v> \"-d\"*P", VIS_SEL},
Bram Moolenaar24959102022-05-07 20:01:16 +01002907 {(char_u *)"<D-v> <C-R>*", MODE_INSERT | MODE_CMDLINE},
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002908 {(char_u *)"<D-c> \"*y", VIS_SEL},
2909 {(char_u *)"<D-x> \"*d", VIS_SEL},
2910 {(char_u *)"<Backspace> \"-d", VIS_SEL},
2911};
2912# endif
2913
2914# undef VIS_SEL
2915#endif
2916
2917/*
2918 * Set up default mappings.
2919 */
2920 void
2921init_mappings(void)
2922{
2923#if defined(MSWIN) || defined(MACOS_X)
2924 int i;
2925
2926# if defined(MSWIN) && (!defined(FEAT_GUI_MSWIN) || defined(VIMDLL))
2927# ifdef VIMDLL
2928 if (!gui.starting)
2929# endif
2930 {
K.Takataeeec2542021-06-02 13:28:16 +02002931 for (i = 0; i < (int)ARRAY_LENGTH(cinitmappings); ++i)
zeertzjq44068e92022-06-16 11:14:55 +01002932 add_map(cinitmappings[i].arg, cinitmappings[i].mode, FALSE);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002933 }
2934# endif
2935# if defined(FEAT_GUI_MSWIN) || defined(MACOS_X)
K.Takataeeec2542021-06-02 13:28:16 +02002936 for (i = 0; i < (int)ARRAY_LENGTH(initmappings); ++i)
zeertzjq44068e92022-06-16 11:14:55 +01002937 add_map(initmappings[i].arg, initmappings[i].mode, FALSE);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002938# endif
2939#endif
2940}
2941
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002942/*
2943 * Add a mapping "map" for mode "mode".
zeertzjq44068e92022-06-16 11:14:55 +01002944 * When "nore" is TRUE use MAPTYPE_NOREMAP.
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002945 * Need to put string in allocated memory, because do_map() will modify it.
2946 */
2947 void
zeertzjq44068e92022-06-16 11:14:55 +01002948add_map(char_u *map, int mode, int nore)
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002949{
2950 char_u *s;
2951 char_u *cpo_save = p_cpo;
2952
Bram Moolenaare5a2dc82021-01-03 19:52:05 +01002953 p_cpo = empty_option; // Allow <> notation
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002954 s = vim_strsave(map);
2955 if (s != NULL)
2956 {
zeertzjq44068e92022-06-16 11:14:55 +01002957 (void)do_map(nore ? MAPTYPE_NOREMAP : MAPTYPE_MAP, s, mode, FALSE);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002958 vim_free(s);
2959 }
2960 p_cpo = cpo_save;
2961}
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002962
Bram Moolenaare677df82019-09-02 22:31:11 +02002963#if defined(FEAT_LANGMAP) || defined(PROTO)
2964/*
2965 * Any character has an equivalent 'langmap' character. This is used for
2966 * keyboards that have a special language mode that sends characters above
2967 * 128 (although other characters can be translated too). The "to" field is a
2968 * Vim command character. This avoids having to switch the keyboard back to
2969 * ASCII mode when leaving Insert mode.
2970 *
2971 * langmap_mapchar[] maps any of 256 chars to an ASCII char used for Vim
2972 * commands.
2973 * langmap_mapga.ga_data is a sorted table of langmap_entry_T. This does the
2974 * same as langmap_mapchar[] for characters >= 256.
2975 *
2976 * Use growarray for 'langmap' chars >= 256
2977 */
2978typedef struct
2979{
2980 int from;
2981 int to;
2982} langmap_entry_T;
2983
2984static garray_T langmap_mapga;
2985
2986/*
2987 * Search for an entry in "langmap_mapga" for "from". If found set the "to"
2988 * field. If not found insert a new entry at the appropriate location.
2989 */
2990 static void
2991langmap_set_entry(int from, int to)
2992{
2993 langmap_entry_T *entries = (langmap_entry_T *)(langmap_mapga.ga_data);
2994 int a = 0;
2995 int b = langmap_mapga.ga_len;
2996
2997 // Do a binary search for an existing entry.
2998 while (a != b)
2999 {
3000 int i = (a + b) / 2;
3001 int d = entries[i].from - from;
3002
3003 if (d == 0)
3004 {
3005 entries[i].to = to;
3006 return;
3007 }
3008 if (d < 0)
3009 a = i + 1;
3010 else
3011 b = i;
3012 }
3013
Yegappan Lakshmananfadc02a2023-01-27 21:03:12 +00003014 if (ga_grow(&langmap_mapga, 1) == FAIL)
Bram Moolenaare677df82019-09-02 22:31:11 +02003015 return; // out of memory
3016
3017 // insert new entry at position "a"
3018 entries = (langmap_entry_T *)(langmap_mapga.ga_data) + a;
3019 mch_memmove(entries + 1, entries,
3020 (langmap_mapga.ga_len - a) * sizeof(langmap_entry_T));
3021 ++langmap_mapga.ga_len;
3022 entries[0].from = from;
3023 entries[0].to = to;
3024}
3025
3026/*
3027 * Apply 'langmap' to multi-byte character "c" and return the result.
3028 */
3029 int
3030langmap_adjust_mb(int c)
3031{
3032 langmap_entry_T *entries = (langmap_entry_T *)(langmap_mapga.ga_data);
3033 int a = 0;
3034 int b = langmap_mapga.ga_len;
3035
3036 while (a != b)
3037 {
3038 int i = (a + b) / 2;
3039 int d = entries[i].from - c;
3040
3041 if (d == 0)
3042 return entries[i].to; // found matching entry
3043 if (d < 0)
3044 a = i + 1;
3045 else
3046 b = i;
3047 }
3048 return c; // no entry found, return "c" unmodified
3049}
3050
3051 void
3052langmap_init(void)
3053{
3054 int i;
3055
3056 for (i = 0; i < 256; i++)
3057 langmap_mapchar[i] = i; // we init with a one-to-one map
3058 ga_init2(&langmap_mapga, sizeof(langmap_entry_T), 8);
3059}
3060
3061/*
3062 * Called when langmap option is set; the language map can be
3063 * changed at any time!
3064 */
Yegappan Lakshmananaf936912023-02-20 12:16:39 +00003065 char *
3066did_set_langmap(optset_T *args UNUSED)
Bram Moolenaare677df82019-09-02 22:31:11 +02003067{
3068 char_u *p;
3069 char_u *p2;
3070 int from, to;
3071
3072 ga_clear(&langmap_mapga); // clear the previous map first
3073 langmap_init(); // back to one-to-one map
3074
3075 for (p = p_langmap; p[0] != NUL; )
3076 {
3077 for (p2 = p; p2[0] != NUL && p2[0] != ',' && p2[0] != ';';
3078 MB_PTR_ADV(p2))
3079 {
3080 if (p2[0] == '\\' && p2[1] != NUL)
3081 ++p2;
3082 }
3083 if (p2[0] == ';')
3084 ++p2; // abcd;ABCD form, p2 points to A
3085 else
3086 p2 = NULL; // aAbBcCdD form, p2 is NULL
3087 while (p[0])
3088 {
3089 if (p[0] == ',')
3090 {
3091 ++p;
3092 break;
3093 }
3094 if (p[0] == '\\' && p[1] != NUL)
3095 ++p;
3096 from = (*mb_ptr2char)(p);
3097 to = NUL;
3098 if (p2 == NULL)
3099 {
3100 MB_PTR_ADV(p);
3101 if (p[0] != ',')
3102 {
3103 if (p[0] == '\\')
3104 ++p;
3105 to = (*mb_ptr2char)(p);
3106 }
3107 }
3108 else
3109 {
3110 if (p2[0] != ',')
3111 {
3112 if (p2[0] == '\\')
3113 ++p2;
3114 to = (*mb_ptr2char)(p2);
3115 }
3116 }
3117 if (to == NUL)
3118 {
Yegappan Lakshmanan5da901b2023-02-27 12:47:47 +00003119 sprintf(args->os_errbuf,
3120 _(e_langmap_matching_character_missing_for_str),
3121 transchar(from));
3122 return args->os_errbuf;
Bram Moolenaare677df82019-09-02 22:31:11 +02003123 }
3124
3125 if (from >= 256)
3126 langmap_set_entry(from, to);
3127 else
3128 langmap_mapchar[from & 255] = to;
3129
3130 // Advance to next pair
3131 MB_PTR_ADV(p);
3132 if (p2 != NULL)
3133 {
3134 MB_PTR_ADV(p2);
3135 if (*p == ';')
3136 {
3137 p = p2;
3138 if (p[0] != NUL)
3139 {
3140 if (p[0] != ',')
3141 {
Zoltan Arpadffy1c8e2332023-12-05 16:04:23 +01003142 vim_snprintf(args->os_errbuf, args->os_errbuflen,
Yegappan Lakshmanan5da901b2023-02-27 12:47:47 +00003143 _(e_langmap_extra_characters_after_semicolon_str),
3144 p);
3145 return args->os_errbuf;
Bram Moolenaare677df82019-09-02 22:31:11 +02003146 }
3147 ++p;
3148 }
3149 break;
3150 }
3151 }
3152 }
3153 }
Yegappan Lakshmananaf936912023-02-20 12:16:39 +00003154
3155 return NULL;
Bram Moolenaare677df82019-09-02 22:31:11 +02003156}
3157#endif
3158
Bram Moolenaarb66bab32019-08-01 14:28:24 +02003159 static void
3160do_exmap(exarg_T *eap, int isabbrev)
3161{
3162 int mode;
3163 char_u *cmdp;
3164
3165 cmdp = eap->cmd;
3166 mode = get_map_mode(&cmdp, eap->forceit || isabbrev);
3167
zeertzjq44068e92022-06-16 11:14:55 +01003168 switch (do_map(*cmdp == 'n' ? MAPTYPE_NOREMAP
3169 : *cmdp == 'u' ? MAPTYPE_UNMAP : MAPTYPE_MAP,
Bram Moolenaarb66bab32019-08-01 14:28:24 +02003170 eap->arg, mode, isabbrev))
3171 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00003172 case 1: emsg(_(e_invalid_argument));
Bram Moolenaarb66bab32019-08-01 14:28:24 +02003173 break;
Bram Moolenaare29a27f2021-07-20 21:07:36 +02003174 case 2: emsg((isabbrev ? _(e_no_such_abbreviation)
3175 : _(e_no_such_mapping)));
Bram Moolenaarb66bab32019-08-01 14:28:24 +02003176 break;
3177 }
3178}
3179
3180/*
3181 * ":abbreviate" and friends.
3182 */
3183 void
3184ex_abbreviate(exarg_T *eap)
3185{
3186 do_exmap(eap, TRUE); // almost the same as mapping
3187}
3188
3189/*
3190 * ":map" and friends.
3191 */
3192 void
3193ex_map(exarg_T *eap)
3194{
3195 // If we are sourcing .exrc or .vimrc in current directory we
3196 // print the mappings for security reasons.
3197 if (secure)
3198 {
3199 secure = 2;
3200 msg_outtrans(eap->cmd);
3201 msg_putchar('\n');
3202 }
3203 do_exmap(eap, FALSE);
3204}
3205
3206/*
3207 * ":unmap" and friends.
3208 */
3209 void
3210ex_unmap(exarg_T *eap)
3211{
3212 do_exmap(eap, FALSE);
3213}
3214
3215/*
3216 * ":mapclear" and friends.
3217 */
3218 void
3219ex_mapclear(exarg_T *eap)
3220{
3221 map_clear(eap->cmd, eap->arg, eap->forceit, FALSE);
3222}
3223
3224/*
3225 * ":abclear" and friends.
3226 */
3227 void
3228ex_abclear(exarg_T *eap)
3229{
3230 map_clear(eap->cmd, eap->arg, TRUE, TRUE);
3231}