blob: 228d073a182694f49f73b9151b0ed7df0eb32884 [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
27/*
28 * Make a hash value for a mapping.
29 * "mode" is the lower 4 bits of the State for the mapping.
30 * "c1" is the first character of the "lhs".
31 * Returns a value between 0 and 255, index in maphash.
32 * Put Normal/Visual mode mappings mostly separately from Insert/Cmdline mode.
33 */
Bram Moolenaar24959102022-05-07 20:01:16 +010034#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 +020035
36/*
37 * Get the start of the hashed map list for "state" and first character "c".
38 */
39 mapblock_T *
40get_maphash_list(int state, int c)
41{
42 return maphash[MAP_HASH(state, c)];
43}
44
45/*
46 * Get the buffer-local hashed map list for "state" and first character "c".
47 */
48 mapblock_T *
49get_buf_maphash_list(int state, int c)
50{
51 return curbuf->b_maphash[MAP_HASH(state, c)];
52}
53
54 int
55is_maphash_valid(void)
56{
57 return maphash_valid;
58}
59
60/*
61 * Initialize maphash[] for first use.
62 */
63 static void
64validate_maphash(void)
65{
66 if (!maphash_valid)
67 {
Bram Moolenaara80faa82020-04-12 19:37:17 +020068 CLEAR_FIELD(maphash);
Bram Moolenaarb66bab32019-08-01 14:28:24 +020069 maphash_valid = TRUE;
70 }
71}
72
73/*
74 * Delete one entry from the abbrlist or maphash[].
75 * "mpp" is a pointer to the m_next field of the PREVIOUS entry!
76 */
77 static void
78map_free(mapblock_T **mpp)
79{
80 mapblock_T *mp;
81
82 mp = *mpp;
83 vim_free(mp->m_keys);
84 vim_free(mp->m_str);
85 vim_free(mp->m_orig_str);
86 *mpp = mp->m_next;
Bram Moolenaard648c012022-01-16 14:58:34 +000087#ifdef FEAT_EVAL
Bram Moolenaarf61c89d2022-01-19 22:51:48 +000088 reset_last_used_map(mp);
Bram Moolenaard648c012022-01-16 14:58:34 +000089#endif
Bram Moolenaar8aa0e6c2022-01-20 11:27:58 +000090 vim_free(mp);
Bram Moolenaarb66bab32019-08-01 14:28:24 +020091}
92
93/*
94 * Return characters to represent the map mode in an allocated string.
95 * Returns NULL when out of memory.
96 */
97 static char_u *
98map_mode_to_chars(int mode)
99{
100 garray_T mapmode;
101
102 ga_init2(&mapmode, 1, 7);
103
Bram Moolenaar24959102022-05-07 20:01:16 +0100104 if ((mode & (MODE_INSERT | MODE_CMDLINE)) == (MODE_INSERT | MODE_CMDLINE))
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200105 ga_append(&mapmode, '!'); // :map!
Bram Moolenaar24959102022-05-07 20:01:16 +0100106 else if (mode & MODE_INSERT)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200107 ga_append(&mapmode, 'i'); // :imap
Bram Moolenaar24959102022-05-07 20:01:16 +0100108 else if (mode & MODE_LANGMAP)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200109 ga_append(&mapmode, 'l'); // :lmap
Bram Moolenaar24959102022-05-07 20:01:16 +0100110 else if (mode & MODE_CMDLINE)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200111 ga_append(&mapmode, 'c'); // :cmap
Bram Moolenaar24959102022-05-07 20:01:16 +0100112 else if ((mode
113 & (MODE_NORMAL | MODE_VISUAL | MODE_SELECT | MODE_OP_PENDING))
114 == (MODE_NORMAL | MODE_VISUAL | MODE_SELECT | MODE_OP_PENDING))
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200115 ga_append(&mapmode, ' '); // :map
116 else
117 {
Bram Moolenaar24959102022-05-07 20:01:16 +0100118 if (mode & MODE_NORMAL)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200119 ga_append(&mapmode, 'n'); // :nmap
Bram Moolenaar24959102022-05-07 20:01:16 +0100120 if (mode & MODE_OP_PENDING)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200121 ga_append(&mapmode, 'o'); // :omap
Bram Moolenaar24959102022-05-07 20:01:16 +0100122 if (mode & MODE_TERMINAL)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200123 ga_append(&mapmode, 't'); // :tmap
Bram Moolenaar24959102022-05-07 20:01:16 +0100124 if ((mode & (MODE_VISUAL | MODE_SELECT)) == (MODE_VISUAL | MODE_SELECT))
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200125 ga_append(&mapmode, 'v'); // :vmap
126 else
127 {
Bram Moolenaar24959102022-05-07 20:01:16 +0100128 if (mode & MODE_VISUAL)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200129 ga_append(&mapmode, 'x'); // :xmap
Bram Moolenaar24959102022-05-07 20:01:16 +0100130 if (mode & MODE_SELECT)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200131 ga_append(&mapmode, 's'); // :smap
132 }
133 }
134
135 ga_append(&mapmode, NUL);
136 return (char_u *)mapmode.ga_data;
137}
138
139 static void
140showmap(
141 mapblock_T *mp,
142 int local) // TRUE for buffer-local map
143{
144 int len = 1;
145 char_u *mapchars;
146
147 if (message_filtered(mp->m_keys) && message_filtered(mp->m_str))
148 return;
149
150 if (msg_didout || msg_silent != 0)
151 {
152 msg_putchar('\n');
153 if (got_int) // 'q' typed at MORE prompt
154 return;
155 }
156
157 mapchars = map_mode_to_chars(mp->m_mode);
158 if (mapchars != NULL)
159 {
160 msg_puts((char *)mapchars);
161 len = (int)STRLEN(mapchars);
162 vim_free(mapchars);
163 }
164
165 while (++len <= 3)
166 msg_putchar(' ');
167
168 // Display the LHS. Get length of what we write.
169 len = msg_outtrans_special(mp->m_keys, TRUE, 0);
170 do
171 {
172 msg_putchar(' '); // padd with blanks
173 ++len;
174 } while (len < 12);
175
176 if (mp->m_noremap == REMAP_NONE)
177 msg_puts_attr("*", HL_ATTR(HLF_8));
178 else if (mp->m_noremap == REMAP_SCRIPT)
179 msg_puts_attr("&", HL_ATTR(HLF_8));
180 else
181 msg_putchar(' ');
182
183 if (local)
184 msg_putchar('@');
185 else
186 msg_putchar(' ');
187
188 // Use FALSE below if we only want things like <Up> to show up as such on
189 // the rhs, and not M-x etc, TRUE gets both -- webb
190 if (*mp->m_str == NUL)
191 msg_puts_attr("<Nop>", HL_ATTR(HLF_8));
192 else
zeertzjqac402f42022-05-04 18:51:43 +0100193 msg_outtrans_special(mp->m_str, FALSE, 0);
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200194#ifdef FEAT_EVAL
195 if (p_verbose > 0)
196 last_set_msg(mp->m_script_ctx);
197#endif
Bram Moolenaard288eaa2022-02-16 18:27:55 +0000198 msg_clr_eos();
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200199 out_flush(); // show one line at a time
200}
201
Bram Moolenaar4c9243f2020-05-22 13:10:44 +0200202 static int
203map_add(
204 mapblock_T **map_table,
205 mapblock_T **abbr_table,
206 char_u *keys,
207 char_u *rhs,
208 char_u *orig_rhs,
Bram Moolenaar4c9243f2020-05-22 13:10:44 +0200209 int noremap,
210 int nowait,
211 int silent,
212 int mode,
213 int is_abbr,
214#ifdef FEAT_EVAL
Bram Moolenaar5a80f8a2020-05-22 13:38:18 +0200215 int expr,
Bram Moolenaar4c9243f2020-05-22 13:10:44 +0200216 scid_T sid, // -1 to use current_sctx
Bram Moolenaara9528b32022-01-18 20:51:35 +0000217 int scriptversion,
Bram Moolenaar4c9243f2020-05-22 13:10:44 +0200218 linenr_T lnum,
219#endif
220 int simplified)
221{
Bram Moolenaar94075b22022-01-18 20:30:39 +0000222 mapblock_T *mp = ALLOC_CLEAR_ONE(mapblock_T);
Bram Moolenaar4c9243f2020-05-22 13:10:44 +0200223
224 if (mp == NULL)
225 return FAIL;
226
227 // If CTRL-C has been mapped, don't always use it for Interrupting.
228 if (*keys == Ctrl_C)
229 {
230 if (map_table == curbuf->b_maphash)
231 curbuf->b_mapped_ctrl_c |= mode;
232 else
233 mapped_ctrl_c |= mode;
234 }
235
236 mp->m_keys = vim_strsave(keys);
237 mp->m_str = vim_strsave(rhs);
238 mp->m_orig_str = vim_strsave(orig_rhs);
239 if (mp->m_keys == NULL || mp->m_str == NULL)
240 {
241 vim_free(mp->m_keys);
242 vim_free(mp->m_str);
243 vim_free(mp->m_orig_str);
244 vim_free(mp);
245 return FAIL;
246 }
247 mp->m_keylen = (int)STRLEN(mp->m_keys);
248 mp->m_noremap = noremap;
249 mp->m_nowait = nowait;
250 mp->m_silent = silent;
251 mp->m_mode = mode;
252 mp->m_simplified = simplified;
253#ifdef FEAT_EVAL
254 mp->m_expr = expr;
Bram Moolenaara9528b32022-01-18 20:51:35 +0000255 if (sid > 0)
Bram Moolenaar4c9243f2020-05-22 13:10:44 +0200256 {
257 mp->m_script_ctx.sc_sid = sid;
258 mp->m_script_ctx.sc_lnum = lnum;
Bram Moolenaara9528b32022-01-18 20:51:35 +0000259 mp->m_script_ctx.sc_version = scriptversion;
Bram Moolenaar4c9243f2020-05-22 13:10:44 +0200260 }
261 else
262 {
263 mp->m_script_ctx = current_sctx;
264 mp->m_script_ctx.sc_lnum += SOURCING_LNUM;
265 }
266#endif
267
268 // add the new entry in front of the abbrlist or maphash[] list
269 if (is_abbr)
270 {
271 mp->m_next = *abbr_table;
272 *abbr_table = mp;
273 }
274 else
275 {
276 int n = MAP_HASH(mp->m_mode, mp->m_keys[0]);
277
278 mp->m_next = map_table[n];
279 map_table[n] = mp;
280 }
281 return OK;
282}
283
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200284/*
285 * map[!] : show all key mappings
286 * map[!] {lhs} : show key mapping for {lhs}
287 * map[!] {lhs} {rhs} : set key mapping for {lhs} to {rhs}
288 * noremap[!] {lhs} {rhs} : same, but no remapping for {rhs}
289 * unmap[!] {lhs} : remove key mapping for {lhs}
290 * abbr : show all abbreviations
291 * abbr {lhs} : show abbreviations for {lhs}
292 * abbr {lhs} {rhs} : set abbreviation for {lhs} to {rhs}
293 * noreabbr {lhs} {rhs} : same, but no remapping for {rhs}
294 * unabbr {lhs} : remove abbreviation for {lhs}
295 *
296 * maptype: 0 for :map, 1 for :unmap, 2 for noremap.
297 *
298 * arg is pointer to any arguments. Note: arg cannot be a read-only string,
299 * it will be modified.
300 *
Bram Moolenaar24959102022-05-07 20:01:16 +0100301 * for :map mode is MODE_NORMAL | MODE_VISUAL | MODE_SELECT | MODE_OP_PENDING
302 * for :map! mode is MODE_INSERT | MODE_CMDLINE
303 * for :cmap mode is MODE_CMDLINE
304 * for :imap mode is MODE_INSERT
305 * for :lmap mode is MODE_LANGMAP
306 * for :nmap mode is MODE_NORMAL
307 * for :vmap mode is MODE_VISUAL | MODE_SELECT
308 * for :xmap mode is MODE_VISUAL
309 * for :smap mode is MODE_SELECT
310 * for :omap mode is MODE_OP_PENDING
311 * for :tmap mode is MODE_TERMINAL
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200312 *
Bram Moolenaar24959102022-05-07 20:01:16 +0100313 * for :abbr mode is MODE_INSERT | MODE_CMDLINE
314 * for :iabbr mode is MODE_INSERT
315 * for :cabbr mode is MODE_CMDLINE
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200316 *
317 * Return 0 for success
318 * 1 for invalid arguments
319 * 2 for no match
320 * 4 for out of mem
321 * 5 for entry not unique
322 */
323 int
324do_map(
325 int maptype,
326 char_u *arg,
327 int mode,
328 int abbrev) // not a mapping but an abbreviation
329{
330 char_u *keys;
331 mapblock_T *mp, **mpp;
332 char_u *rhs;
333 char_u *p;
334 int n;
335 int len = 0; // init for GCC
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200336 int hasarg;
337 int haskey;
Bram Moolenaar459fd782019-10-13 16:43:39 +0200338 int do_print;
339 int keyround;
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200340 char_u *keys_buf = NULL;
Bram Moolenaar459fd782019-10-13 16:43:39 +0200341 char_u *alt_keys_buf = NULL;
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200342 char_u *arg_buf = NULL;
343 int retval = 0;
344 int do_backslash;
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200345 mapblock_T **abbr_table;
346 mapblock_T **map_table;
347 int unique = FALSE;
348 int nowait = FALSE;
349 int silent = FALSE;
350 int special = FALSE;
351#ifdef FEAT_EVAL
352 int expr = FALSE;
353#endif
Bram Moolenaar459fd782019-10-13 16:43:39 +0200354 int did_simplify = FALSE;
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200355 int noremap;
356 char_u *orig_rhs;
357
358 keys = arg;
359 map_table = maphash;
360 abbr_table = &first_abbr;
361
362 // For ":noremap" don't remap, otherwise do remap.
363 if (maptype == 2)
364 noremap = REMAP_NONE;
365 else
366 noremap = REMAP_YES;
367
368 // Accept <buffer>, <nowait>, <silent>, <expr> <script> and <unique> in
369 // any order.
370 for (;;)
371 {
372 // Check for "<buffer>": mapping local to buffer.
373 if (STRNCMP(keys, "<buffer>", 8) == 0)
374 {
375 keys = skipwhite(keys + 8);
376 map_table = curbuf->b_maphash;
377 abbr_table = &curbuf->b_first_abbr;
378 continue;
379 }
380
381 // Check for "<nowait>": don't wait for more characters.
382 if (STRNCMP(keys, "<nowait>", 8) == 0)
383 {
384 keys = skipwhite(keys + 8);
385 nowait = TRUE;
386 continue;
387 }
388
389 // Check for "<silent>": don't echo commands.
390 if (STRNCMP(keys, "<silent>", 8) == 0)
391 {
392 keys = skipwhite(keys + 8);
393 silent = TRUE;
394 continue;
395 }
396
397 // Check for "<special>": accept special keys in <>
398 if (STRNCMP(keys, "<special>", 9) == 0)
399 {
400 keys = skipwhite(keys + 9);
401 special = TRUE;
402 continue;
403 }
404
405#ifdef FEAT_EVAL
406 // Check for "<script>": remap script-local mappings only
407 if (STRNCMP(keys, "<script>", 8) == 0)
408 {
409 keys = skipwhite(keys + 8);
410 noremap = REMAP_SCRIPT;
411 continue;
412 }
413
414 // Check for "<expr>": {rhs} is an expression.
415 if (STRNCMP(keys, "<expr>", 6) == 0)
416 {
417 keys = skipwhite(keys + 6);
418 expr = TRUE;
419 continue;
420 }
421#endif
422 // Check for "<unique>": don't overwrite an existing mapping.
423 if (STRNCMP(keys, "<unique>", 8) == 0)
424 {
425 keys = skipwhite(keys + 8);
426 unique = TRUE;
427 continue;
428 }
429 break;
430 }
431
432 validate_maphash();
433
434 // Find end of keys and skip CTRL-Vs (and backslashes) in it.
435 // Accept backslash like CTRL-V when 'cpoptions' does not contain 'B'.
436 // with :unmap white space is included in the keys, no argument possible.
437 p = keys;
438 do_backslash = (vim_strchr(p_cpo, CPO_BSLASH) == NULL);
439 while (*p && (maptype == 1 || !VIM_ISWHITE(*p)))
440 {
441 if ((p[0] == Ctrl_V || (do_backslash && p[0] == '\\')) &&
442 p[1] != NUL)
443 ++p; // skip CTRL-V or backslash
444 ++p;
445 }
446 if (*p != NUL)
447 *p++ = NUL;
448
449 p = skipwhite(p);
450 rhs = p;
451 hasarg = (*rhs != NUL);
452 haskey = (*keys != NUL);
Bram Moolenaar459fd782019-10-13 16:43:39 +0200453 do_print = !haskey || (maptype != 1 && !hasarg);
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200454
455 // check for :unmap without argument
456 if (maptype == 1 && !haskey)
457 {
458 retval = 1;
459 goto theend;
460 }
461
462 // If mapping has been given as ^V<C_UP> say, then replace the term codes
463 // with the appropriate two bytes. If it is a shifted special key, unshift
464 // it too, giving another two bytes.
465 // replace_termcodes() may move the result to allocated memory, which
466 // needs to be freed later (*keys_buf and *arg_buf).
467 // replace_termcodes() also removes CTRL-Vs and sometimes backslashes.
Bram Moolenaar459fd782019-10-13 16:43:39 +0200468 // If something like <C-H> is simplified to 0x08 then mark it as simplified
469 // and also add a n entry with a modifier, which will work when
470 // modifyOtherKeys is working.
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200471 if (haskey)
Bram Moolenaar459fd782019-10-13 16:43:39 +0200472 {
473 char_u *new_keys;
474 int flags = REPTERM_FROM_PART | REPTERM_DO_LT;
475
476 if (special)
477 flags |= REPTERM_SPECIAL;
478 new_keys = replace_termcodes(keys, &keys_buf, flags, &did_simplify);
479 if (did_simplify)
480 (void)replace_termcodes(keys, &alt_keys_buf,
481 flags | REPTERM_NO_SIMPLIFY, NULL);
482 keys = new_keys;
483 }
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200484 orig_rhs = rhs;
485 if (hasarg)
486 {
487 if (STRICMP(rhs, "<nop>") == 0) // "<Nop>" means nothing
488 rhs = (char_u *)"";
489 else
Bram Moolenaar459fd782019-10-13 16:43:39 +0200490 rhs = replace_termcodes(rhs, &arg_buf,
491 REPTERM_DO_LT | (special ? REPTERM_SPECIAL : 0), NULL);
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200492 }
493
Bram Moolenaar459fd782019-10-13 16:43:39 +0200494 /*
495 * The following is done twice if we have two versions of keys:
496 * "alt_keys_buf" is not NULL.
497 */
498 for (keyround = 1; keyround <= 2; ++keyround)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200499 {
Bram Moolenaar459fd782019-10-13 16:43:39 +0200500 int did_it = FALSE;
501 int did_local = FALSE;
Bram Moolenaar87f74102022-04-25 18:59:25 +0100502 int keyround1_simplified = keyround == 1 && did_simplify;
Bram Moolenaar459fd782019-10-13 16:43:39 +0200503 int round;
504 int hash;
505 int new_hash;
506
507 if (keyround == 2)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200508 {
Bram Moolenaar459fd782019-10-13 16:43:39 +0200509 if (alt_keys_buf == NULL)
510 break;
511 keys = alt_keys_buf;
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200512 }
Bram Moolenaar459fd782019-10-13 16:43:39 +0200513 else if (alt_keys_buf != NULL && do_print)
514 // when printing always use the not-simplified map
515 keys = alt_keys_buf;
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200516
Bram Moolenaar459fd782019-10-13 16:43:39 +0200517 // check arguments and translate function keys
518 if (haskey)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200519 {
Bram Moolenaar459fd782019-10-13 16:43:39 +0200520 len = (int)STRLEN(keys);
521 if (len > MAXMAPLEN) // maximum length of MAXMAPLEN chars
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200522 {
Bram Moolenaar459fd782019-10-13 16:43:39 +0200523 retval = 1;
524 goto theend;
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200525 }
Bram Moolenaar459fd782019-10-13 16:43:39 +0200526
527 if (abbrev && maptype != 1)
528 {
529 // If an abbreviation ends in a keyword character, the
530 // rest must be all keyword-char or all non-keyword-char.
531 // Otherwise we won't be able to find the start of it in a
532 // vi-compatible way.
533 if (has_mbyte)
534 {
535 int first, last;
536 int same = -1;
537
538 first = vim_iswordp(keys);
539 last = first;
540 p = keys + (*mb_ptr2len)(keys);
541 n = 1;
542 while (p < keys + len)
543 {
544 ++n; // nr of (multi-byte) chars
545 last = vim_iswordp(p); // type of last char
546 if (same == -1 && last != first)
547 same = n - 1; // count of same char type
548 p += (*mb_ptr2len)(p);
549 }
550 if (last && n > 2 && same >= 0 && same < n - 1)
551 {
552 retval = 1;
553 goto theend;
554 }
555 }
556 else if (vim_iswordc(keys[len - 1]))
557 // ends in keyword char
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200558 for (n = 0; n < len - 2; ++n)
559 if (vim_iswordc(keys[n]) != vim_iswordc(keys[len - 2]))
560 {
561 retval = 1;
562 goto theend;
563 }
Bram Moolenaar459fd782019-10-13 16:43:39 +0200564 // An abbreviation cannot contain white space.
565 for (n = 0; n < len; ++n)
566 if (VIM_ISWHITE(keys[n]))
567 {
568 retval = 1;
569 goto theend;
570 }
571 }
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200572 }
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200573
Bram Moolenaar459fd782019-10-13 16:43:39 +0200574 if (haskey && hasarg && abbrev) // if we will add an abbreviation
575 no_abbr = FALSE; // reset flag that indicates there are
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200576 // no abbreviations
577
Bram Moolenaar459fd782019-10-13 16:43:39 +0200578 if (do_print)
579 msg_start();
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200580
Bram Moolenaar459fd782019-10-13 16:43:39 +0200581 // Check if a new local mapping wasn't already defined globally.
Bram Moolenaar4c9243f2020-05-22 13:10:44 +0200582 if (unique && map_table == curbuf->b_maphash
583 && haskey && hasarg && maptype != 1)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200584 {
Bram Moolenaar459fd782019-10-13 16:43:39 +0200585 // need to loop over all global hash lists
586 for (hash = 0; hash < 256 && !got_int; ++hash)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200587 {
Bram Moolenaar459fd782019-10-13 16:43:39 +0200588 if (abbrev)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200589 {
Bram Moolenaar459fd782019-10-13 16:43:39 +0200590 if (hash != 0) // there is only one abbreviation list
591 break;
592 mp = first_abbr;
593 }
594 else
595 mp = maphash[hash];
596 for ( ; mp != NULL && !got_int; mp = mp->m_next)
597 {
598 // check entries with the same mode
599 if ((mp->m_mode & mode) != 0
600 && mp->m_keylen == len
Bram Moolenaar459fd782019-10-13 16:43:39 +0200601 && STRNCMP(mp->m_keys, keys, (size_t)len) == 0)
602 {
603 if (abbrev)
Bram Moolenaar6d057012021-12-31 18:49:43 +0000604 semsg(
605 _(e_global_abbreviation_already_exists_for_str),
Bram Moolenaar459fd782019-10-13 16:43:39 +0200606 mp->m_keys);
607 else
Bram Moolenaar6d057012021-12-31 18:49:43 +0000608 semsg(_(e_global_mapping_already_exists_for_str),
Bram Moolenaar459fd782019-10-13 16:43:39 +0200609 mp->m_keys);
610 retval = 5;
611 goto theend;
612 }
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200613 }
614 }
615 }
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200616
Bram Moolenaar459fd782019-10-13 16:43:39 +0200617 // When listing global mappings, also list buffer-local ones here.
618 if (map_table != curbuf->b_maphash && !hasarg && maptype != 1)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200619 {
Bram Moolenaar459fd782019-10-13 16:43:39 +0200620 // need to loop over all global hash lists
621 for (hash = 0; hash < 256 && !got_int; ++hash)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200622 {
Bram Moolenaar459fd782019-10-13 16:43:39 +0200623 if (abbrev)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200624 {
Bram Moolenaar459fd782019-10-13 16:43:39 +0200625 if (hash != 0) // there is only one abbreviation list
626 break;
627 mp = curbuf->b_first_abbr;
628 }
629 else
630 mp = curbuf->b_maphash[hash];
631 for ( ; mp != NULL && !got_int; mp = mp->m_next)
632 {
633 // check entries with the same mode
Bram Moolenaarfafb4b12019-10-16 18:34:57 +0200634 if (!mp->m_simplified && (mp->m_mode & mode) != 0)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200635 {
Bram Moolenaar459fd782019-10-13 16:43:39 +0200636 if (!haskey) // show all entries
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200637 {
638 showmap(mp, TRUE);
639 did_local = TRUE;
640 }
Bram Moolenaar459fd782019-10-13 16:43:39 +0200641 else
642 {
643 n = mp->m_keylen;
644 if (STRNCMP(mp->m_keys, keys,
645 (size_t)(n < len ? n : len)) == 0)
646 {
647 showmap(mp, TRUE);
648 did_local = TRUE;
649 }
650 }
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200651 }
652 }
653 }
654 }
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200655
Bram Moolenaar459fd782019-10-13 16:43:39 +0200656 // Find an entry in the maphash[] list that matches.
657 // For :unmap we may loop two times: once to try to unmap an entry with
658 // a matching 'from' part, a second time, if the first fails, to unmap
zeertzjqa3f83fe2021-11-22 12:47:39 +0000659 // an entry with a matching 'to' part. This was done to allow
660 // ":ab foo bar" to be unmapped by typing ":unab foo", where "foo" will
661 // be replaced by "bar" because of the abbreviation.
Bram Moolenaar459fd782019-10-13 16:43:39 +0200662 for (round = 0; (round == 0 || maptype == 1) && round <= 1
663 && !did_it && !got_int; ++round)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200664 {
Bram Moolenaar459fd782019-10-13 16:43:39 +0200665 // need to loop over all hash lists
666 for (hash = 0; hash < 256 && !got_int; ++hash)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200667 {
Bram Moolenaar459fd782019-10-13 16:43:39 +0200668 if (abbrev)
669 {
670 if (hash > 0) // there is only one abbreviation list
671 break;
672 mpp = abbr_table;
673 }
674 else
675 mpp = &(map_table[hash]);
676 for (mp = *mpp; mp != NULL && !got_int; mp = *mpp)
677 {
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200678
Bram Moolenaarfafb4b12019-10-16 18:34:57 +0200679 if ((mp->m_mode & mode) == 0)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200680 {
Bram Moolenaarfafb4b12019-10-16 18:34:57 +0200681 // skip entries with wrong mode
Bram Moolenaar459fd782019-10-13 16:43:39 +0200682 mpp = &(mp->m_next);
683 continue;
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200684 }
Bram Moolenaar459fd782019-10-13 16:43:39 +0200685 if (!haskey) // show all entries
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200686 {
Bram Moolenaarfafb4b12019-10-16 18:34:57 +0200687 if (!mp->m_simplified)
688 {
689 showmap(mp, map_table != maphash);
690 did_it = TRUE;
691 }
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200692 }
Bram Moolenaar459fd782019-10-13 16:43:39 +0200693 else // do we have a match?
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200694 {
Bram Moolenaar459fd782019-10-13 16:43:39 +0200695 if (round) // second round: Try unmap "rhs" string
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200696 {
Bram Moolenaar459fd782019-10-13 16:43:39 +0200697 n = (int)STRLEN(mp->m_str);
698 p = mp->m_str;
699 }
700 else
701 {
702 n = mp->m_keylen;
703 p = mp->m_keys;
704 }
705 if (STRNCMP(p, keys, (size_t)(n < len ? n : len)) == 0)
706 {
707 if (maptype == 1)
708 {
709 // Delete entry.
710 // Only accept a full match. For abbreviations
711 // we ignore trailing space when matching with
712 // the "lhs", since an abbreviation can't have
713 // trailing space.
714 if (n != len && (!abbrev || round || n > len
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200715 || *skipwhite(keys + n) != NUL))
Bram Moolenaar459fd782019-10-13 16:43:39 +0200716 {
717 mpp = &(mp->m_next);
718 continue;
719 }
zeertzjqabeb09b2022-04-26 12:29:43 +0100720 // In keyround for simplified keys, don't unmap
721 // a mapping without m_simplified flag.
Bram Moolenaar87f74102022-04-25 18:59:25 +0100722 if (keyround1_simplified && !mp->m_simplified)
zeertzjqa4e33322022-04-24 17:07:53 +0100723 break;
Bram Moolenaar459fd782019-10-13 16:43:39 +0200724 // We reset the indicated mode bits. If nothing
725 // is left the entry is deleted below.
726 mp->m_mode &= ~mode;
727 did_it = TRUE; // remember we did something
728 }
729 else if (!hasarg) // show matching entry
730 {
Bram Moolenaarfafb4b12019-10-16 18:34:57 +0200731 if (!mp->m_simplified)
732 {
733 showmap(mp, map_table != maphash);
734 did_it = TRUE;
735 }
Bram Moolenaar459fd782019-10-13 16:43:39 +0200736 }
737 else if (n != len) // new entry is ambiguous
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200738 {
739 mpp = &(mp->m_next);
740 continue;
741 }
Bram Moolenaar459fd782019-10-13 16:43:39 +0200742 else if (unique)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200743 {
Bram Moolenaar459fd782019-10-13 16:43:39 +0200744 if (abbrev)
Bram Moolenaar6d057012021-12-31 18:49:43 +0000745 semsg(
746 _(e_abbreviation_already_exists_for_str),
Bram Moolenaar459fd782019-10-13 16:43:39 +0200747 p);
748 else
Bram Moolenaar6d057012021-12-31 18:49:43 +0000749 semsg(_(e_mapping_already_exists_for_str),
Bram Moolenaar459fd782019-10-13 16:43:39 +0200750 p);
751 retval = 5;
752 goto theend;
753 }
754 else
755 {
756 // new rhs for existing entry
757 mp->m_mode &= ~mode; // remove mode bits
758 if (mp->m_mode == 0 && !did_it) // reuse entry
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200759 {
Bram Moolenaar459fd782019-10-13 16:43:39 +0200760 char_u *newstr = vim_strsave(rhs);
761
762 if (newstr == NULL)
763 {
764 retval = 4; // no mem
765 goto theend;
766 }
767 vim_free(mp->m_str);
768 mp->m_str = newstr;
769 vim_free(mp->m_orig_str);
770 mp->m_orig_str = vim_strsave(orig_rhs);
771 mp->m_noremap = noremap;
772 mp->m_nowait = nowait;
773 mp->m_silent = silent;
774 mp->m_mode = mode;
Bram Moolenaar87f74102022-04-25 18:59:25 +0100775 mp->m_simplified = keyround1_simplified;
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200776#ifdef FEAT_EVAL
Bram Moolenaar459fd782019-10-13 16:43:39 +0200777 mp->m_expr = expr;
778 mp->m_script_ctx = current_sctx;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +0100779 mp->m_script_ctx.sc_lnum += SOURCING_LNUM;
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200780#endif
Bram Moolenaar459fd782019-10-13 16:43:39 +0200781 did_it = TRUE;
782 }
783 }
784 if (mp->m_mode == 0) // entry can be deleted
785 {
786 map_free(mpp);
787 continue; // continue with *mpp
788 }
789
790 // May need to put this entry into another hash
791 // list.
792 new_hash = MAP_HASH(mp->m_mode, mp->m_keys[0]);
793 if (!abbrev && new_hash != hash)
794 {
795 *mpp = mp->m_next;
796 mp->m_next = map_table[new_hash];
797 map_table[new_hash] = mp;
798
799 continue; // continue with *mpp
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200800 }
801 }
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200802 }
Bram Moolenaar459fd782019-10-13 16:43:39 +0200803 mpp = &(mp->m_next);
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200804 }
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200805 }
806 }
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200807
Bram Moolenaar459fd782019-10-13 16:43:39 +0200808 if (maptype == 1)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200809 {
Bram Moolenaar459fd782019-10-13 16:43:39 +0200810 // delete entry
811 if (!did_it)
zeertzjqa4e33322022-04-24 17:07:53 +0100812 {
Bram Moolenaar87f74102022-04-25 18:59:25 +0100813 if (!keyround1_simplified)
zeertzjqa4e33322022-04-24 17:07:53 +0100814 retval = 2; // no match
815 }
Bram Moolenaar459fd782019-10-13 16:43:39 +0200816 else if (*keys == Ctrl_C)
817 {
818 // If CTRL-C has been unmapped, reuse it for Interrupting.
819 if (map_table == curbuf->b_maphash)
820 curbuf->b_mapped_ctrl_c &= ~mode;
821 else
822 mapped_ctrl_c &= ~mode;
823 }
824 continue;
825 }
826
827 if (!haskey || !hasarg)
828 {
829 // print entries
830 if (!did_it && !did_local)
831 {
832 if (abbrev)
833 msg(_("No abbreviation found"));
834 else
835 msg(_("No mapping found"));
836 }
837 goto theend; // listing finished
838 }
839
840 if (did_it)
841 continue; // have added the new entry already
842
843 // Get here when adding a new entry to the maphash[] list or abbrlist.
Bram Moolenaar5a80f8a2020-05-22 13:38:18 +0200844 if (map_add(map_table, abbr_table, keys, rhs, orig_rhs,
845 noremap, nowait, silent, mode, abbrev,
Bram Moolenaar4c9243f2020-05-22 13:10:44 +0200846#ifdef FEAT_EVAL
Bram Moolenaara9528b32022-01-18 20:51:35 +0000847 expr, /* sid */ -1, /* scriptversion */ 0, /* lnum */ 0,
Bram Moolenaar4c9243f2020-05-22 13:10:44 +0200848#endif
Bram Moolenaar87f74102022-04-25 18:59:25 +0100849 keyround1_simplified) == FAIL)
Bram Moolenaar459fd782019-10-13 16:43:39 +0200850 {
851 retval = 4; // no mem
852 goto theend;
853 }
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200854 }
855
856theend:
857 vim_free(keys_buf);
Bram Moolenaar459fd782019-10-13 16:43:39 +0200858 vim_free(alt_keys_buf);
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200859 vim_free(arg_buf);
860 return retval;
861}
862
863/*
864 * Get the mapping mode from the command name.
865 */
866 static int
867get_map_mode(char_u **cmdp, int forceit)
868{
869 char_u *p;
870 int modec;
871 int mode;
872
873 p = *cmdp;
874 modec = *p++;
875 if (modec == 'i')
Bram Moolenaar24959102022-05-07 20:01:16 +0100876 mode = MODE_INSERT; // :imap
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200877 else if (modec == 'l')
Bram Moolenaar24959102022-05-07 20:01:16 +0100878 mode = MODE_LANGMAP; // :lmap
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200879 else if (modec == 'c')
Bram Moolenaar24959102022-05-07 20:01:16 +0100880 mode = MODE_CMDLINE; // :cmap
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200881 else if (modec == 'n' && *p != 'o') // avoid :noremap
Bram Moolenaar24959102022-05-07 20:01:16 +0100882 mode = MODE_NORMAL; // :nmap
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200883 else if (modec == 'v')
Bram Moolenaar24959102022-05-07 20:01:16 +0100884 mode = MODE_VISUAL | MODE_SELECT; // :vmap
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200885 else if (modec == 'x')
Bram Moolenaar24959102022-05-07 20:01:16 +0100886 mode = MODE_VISUAL; // :xmap
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200887 else if (modec == 's')
Bram Moolenaar24959102022-05-07 20:01:16 +0100888 mode = MODE_SELECT; // :smap
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200889 else if (modec == 'o')
Bram Moolenaar24959102022-05-07 20:01:16 +0100890 mode = MODE_OP_PENDING; // :omap
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200891 else if (modec == 't')
Bram Moolenaar24959102022-05-07 20:01:16 +0100892 mode = MODE_TERMINAL; // :tmap
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200893 else
894 {
895 --p;
896 if (forceit)
Bram Moolenaar24959102022-05-07 20:01:16 +0100897 mode = MODE_INSERT | MODE_CMDLINE; // :map !
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200898 else
Bram Moolenaar24959102022-05-07 20:01:16 +0100899 mode = MODE_VISUAL | MODE_SELECT | MODE_NORMAL | MODE_OP_PENDING;
900 // :map
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200901 }
902
903 *cmdp = p;
904 return mode;
905}
906
907/*
908 * Clear all mappings or abbreviations.
909 * 'abbr' should be FALSE for mappings, TRUE for abbreviations.
910 */
911 static void
912map_clear(
913 char_u *cmdp,
914 char_u *arg UNUSED,
915 int forceit,
916 int abbr)
917{
918 int mode;
919 int local;
920
921 local = (STRCMP(arg, "<buffer>") == 0);
922 if (!local && *arg != NUL)
923 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +0000924 emsg(_(e_invalid_argument));
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200925 return;
926 }
927
928 mode = get_map_mode(&cmdp, forceit);
929 map_clear_int(curbuf, mode, local, abbr);
930}
931
932/*
933 * Clear all mappings in "mode".
934 */
935 void
936map_clear_int(
937 buf_T *buf, // buffer for local mappings
938 int mode, // mode in which to delete
939 int local, // TRUE for buffer-local mappings
940 int abbr) // TRUE for abbreviations
941{
942 mapblock_T *mp, **mpp;
943 int hash;
944 int new_hash;
945
946 validate_maphash();
947
948 for (hash = 0; hash < 256; ++hash)
949 {
950 if (abbr)
951 {
952 if (hash > 0) // there is only one abbrlist
953 break;
954 if (local)
955 mpp = &buf->b_first_abbr;
956 else
957 mpp = &first_abbr;
958 }
959 else
960 {
961 if (local)
962 mpp = &buf->b_maphash[hash];
963 else
964 mpp = &maphash[hash];
965 }
966 while (*mpp != NULL)
967 {
968 mp = *mpp;
969 if (mp->m_mode & mode)
970 {
971 mp->m_mode &= ~mode;
972 if (mp->m_mode == 0) // entry can be deleted
973 {
974 map_free(mpp);
975 continue;
976 }
977 // May need to put this entry into another hash list.
978 new_hash = MAP_HASH(mp->m_mode, mp->m_keys[0]);
979 if (!abbr && new_hash != hash)
980 {
981 *mpp = mp->m_next;
982 if (local)
983 {
984 mp->m_next = buf->b_maphash[new_hash];
985 buf->b_maphash[new_hash] = mp;
986 }
987 else
988 {
989 mp->m_next = maphash[new_hash];
990 maphash[new_hash] = mp;
991 }
992 continue; // continue with *mpp
993 }
994 }
995 mpp = &(mp->m_next);
996 }
997 }
998}
999
1000#if defined(FEAT_EVAL) || defined(PROTO)
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001001 int
Bram Moolenaar581ba392019-09-03 22:08:33 +02001002mode_str2flags(char_u *modechars)
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001003{
1004 int mode = 0;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001005
1006 if (vim_strchr(modechars, 'n') != NULL)
Bram Moolenaar24959102022-05-07 20:01:16 +01001007 mode |= MODE_NORMAL;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001008 if (vim_strchr(modechars, 'v') != NULL)
Bram Moolenaar24959102022-05-07 20:01:16 +01001009 mode |= MODE_VISUAL | MODE_SELECT;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001010 if (vim_strchr(modechars, 'x') != NULL)
Bram Moolenaar24959102022-05-07 20:01:16 +01001011 mode |= MODE_VISUAL;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001012 if (vim_strchr(modechars, 's') != NULL)
Bram Moolenaar24959102022-05-07 20:01:16 +01001013 mode |= MODE_SELECT;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001014 if (vim_strchr(modechars, 'o') != NULL)
Bram Moolenaar24959102022-05-07 20:01:16 +01001015 mode |= MODE_OP_PENDING;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001016 if (vim_strchr(modechars, 'i') != NULL)
Bram Moolenaar24959102022-05-07 20:01:16 +01001017 mode |= MODE_INSERT;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001018 if (vim_strchr(modechars, 'l') != NULL)
Bram Moolenaar24959102022-05-07 20:01:16 +01001019 mode |= MODE_LANGMAP;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001020 if (vim_strchr(modechars, 'c') != NULL)
Bram Moolenaar24959102022-05-07 20:01:16 +01001021 mode |= MODE_CMDLINE;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001022
Bram Moolenaar581ba392019-09-03 22:08:33 +02001023 return mode;
1024}
1025
1026/*
1027 * Return TRUE if a map exists that has "str" in the rhs for mode "modechars".
1028 * Recognize termcap codes in "str".
1029 * Also checks mappings local to the current buffer.
1030 */
1031 int
1032map_to_exists(char_u *str, char_u *modechars, int abbr)
1033{
1034 char_u *rhs;
1035 char_u *buf;
1036 int retval;
1037
Bram Moolenaar459fd782019-10-13 16:43:39 +02001038 rhs = replace_termcodes(str, &buf, REPTERM_DO_LT, NULL);
Bram Moolenaar581ba392019-09-03 22:08:33 +02001039
1040 retval = map_to_exists_mode(rhs, mode_str2flags(modechars), abbr);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001041 vim_free(buf);
1042
1043 return retval;
1044}
1045#endif
1046
1047/*
1048 * Return TRUE if a map exists that has "str" in the rhs for mode "mode".
1049 * Also checks mappings local to the current buffer.
1050 */
1051 int
1052map_to_exists_mode(char_u *rhs, int mode, int abbr)
1053{
1054 mapblock_T *mp;
1055 int hash;
1056 int exp_buffer = FALSE;
1057
1058 validate_maphash();
1059
1060 // Do it twice: once for global maps and once for local maps.
1061 for (;;)
1062 {
1063 for (hash = 0; hash < 256; ++hash)
1064 {
1065 if (abbr)
1066 {
1067 if (hash > 0) // there is only one abbr list
1068 break;
1069 if (exp_buffer)
1070 mp = curbuf->b_first_abbr;
1071 else
1072 mp = first_abbr;
1073 }
1074 else if (exp_buffer)
1075 mp = curbuf->b_maphash[hash];
1076 else
1077 mp = maphash[hash];
1078 for (; mp; mp = mp->m_next)
1079 {
1080 if ((mp->m_mode & mode)
1081 && strstr((char *)mp->m_str, (char *)rhs) != NULL)
1082 return TRUE;
1083 }
1084 }
1085 if (exp_buffer)
1086 break;
1087 exp_buffer = TRUE;
1088 }
1089
1090 return FALSE;
1091}
1092
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001093/*
1094 * Used below when expanding mapping/abbreviation names.
1095 */
1096static int expand_mapmodes = 0;
1097static int expand_isabbrev = 0;
1098static int expand_buffer = FALSE;
1099
1100/*
Bram Moolenaar7f51bbe2020-01-24 20:21:19 +01001101 * Translate an internal mapping/abbreviation representation into the
1102 * corresponding external one recognized by :map/:abbrev commands.
1103 * Respects the current B/k/< settings of 'cpoption'.
1104 *
1105 * This function is called when expanding mappings/abbreviations on the
1106 * command-line.
1107 *
1108 * It uses a growarray to build the translation string since the latter can be
1109 * wider than the original description. The caller has to free the string
1110 * afterwards.
1111 *
1112 * Returns NULL when there is a problem.
1113 */
1114 static char_u *
1115translate_mapping(char_u *str)
1116{
1117 garray_T ga;
1118 int c;
1119 int modifiers;
1120 int cpo_bslash;
1121 int cpo_special;
1122
1123 ga_init(&ga);
1124 ga.ga_itemsize = 1;
1125 ga.ga_growsize = 40;
1126
1127 cpo_bslash = (vim_strchr(p_cpo, CPO_BSLASH) != NULL);
1128 cpo_special = (vim_strchr(p_cpo, CPO_SPECI) != NULL);
1129
1130 for (; *str; ++str)
1131 {
1132 c = *str;
1133 if (c == K_SPECIAL && str[1] != NUL && str[2] != NUL)
1134 {
1135 modifiers = 0;
1136 if (str[1] == KS_MODIFIER)
1137 {
1138 str++;
1139 modifiers = *++str;
1140 c = *++str;
1141 }
1142 if (c == K_SPECIAL && str[1] != NUL && str[2] != NUL)
1143 {
1144 if (cpo_special)
1145 {
1146 ga_clear(&ga);
1147 return NULL;
1148 }
1149 c = TO_SPECIAL(str[1], str[2]);
1150 if (c == K_ZERO) // display <Nul> as ^@
1151 c = NUL;
1152 str += 2;
1153 }
1154 if (IS_SPECIAL(c) || modifiers) // special key
1155 {
1156 if (cpo_special)
1157 {
1158 ga_clear(&ga);
1159 return NULL;
1160 }
1161 ga_concat(&ga, get_special_key_name(c, modifiers));
1162 continue; // for (str)
1163 }
1164 }
1165 if (c == ' ' || c == '\t' || c == Ctrl_J || c == Ctrl_V
1166 || (c == '<' && !cpo_special) || (c == '\\' && !cpo_bslash))
1167 ga_append(&ga, cpo_bslash ? Ctrl_V : '\\');
1168 if (c)
1169 ga_append(&ga, c);
1170 }
1171 ga_append(&ga, NUL);
1172 return (char_u *)(ga.ga_data);
1173}
1174
1175/*
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001176 * Work out what to complete when doing command line completion of mapping
1177 * or abbreviation names.
1178 */
1179 char_u *
1180set_context_in_map_cmd(
1181 expand_T *xp,
1182 char_u *cmd,
1183 char_u *arg,
1184 int forceit, // TRUE if '!' given
1185 int isabbrev, // TRUE if abbreviation
1186 int isunmap, // TRUE if unmap/unabbrev command
1187 cmdidx_T cmdidx)
1188{
1189 if (forceit && cmdidx != CMD_map && cmdidx != CMD_unmap)
1190 xp->xp_context = EXPAND_NOTHING;
1191 else
1192 {
1193 if (isunmap)
1194 expand_mapmodes = get_map_mode(&cmd, forceit || isabbrev);
1195 else
1196 {
Bram Moolenaar24959102022-05-07 20:01:16 +01001197 expand_mapmodes = MODE_INSERT | MODE_CMDLINE;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001198 if (!isabbrev)
Bram Moolenaar24959102022-05-07 20:01:16 +01001199 expand_mapmodes += MODE_VISUAL | MODE_SELECT | MODE_NORMAL
1200 | MODE_OP_PENDING;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001201 }
1202 expand_isabbrev = isabbrev;
1203 xp->xp_context = EXPAND_MAPPINGS;
1204 expand_buffer = FALSE;
1205 for (;;)
1206 {
1207 if (STRNCMP(arg, "<buffer>", 8) == 0)
1208 {
1209 expand_buffer = TRUE;
1210 arg = skipwhite(arg + 8);
1211 continue;
1212 }
1213 if (STRNCMP(arg, "<unique>", 8) == 0)
1214 {
1215 arg = skipwhite(arg + 8);
1216 continue;
1217 }
1218 if (STRNCMP(arg, "<nowait>", 8) == 0)
1219 {
1220 arg = skipwhite(arg + 8);
1221 continue;
1222 }
1223 if (STRNCMP(arg, "<silent>", 8) == 0)
1224 {
1225 arg = skipwhite(arg + 8);
1226 continue;
1227 }
1228 if (STRNCMP(arg, "<special>", 9) == 0)
1229 {
1230 arg = skipwhite(arg + 9);
1231 continue;
1232 }
1233#ifdef FEAT_EVAL
1234 if (STRNCMP(arg, "<script>", 8) == 0)
1235 {
1236 arg = skipwhite(arg + 8);
1237 continue;
1238 }
1239 if (STRNCMP(arg, "<expr>", 6) == 0)
1240 {
1241 arg = skipwhite(arg + 6);
1242 continue;
1243 }
1244#endif
1245 break;
1246 }
1247 xp->xp_pattern = arg;
1248 }
1249
1250 return NULL;
1251}
1252
1253/*
1254 * Find all mapping/abbreviation names that match regexp "regmatch"'.
1255 * For command line expansion of ":[un]map" and ":[un]abbrev" in all modes.
1256 * Return OK if matches found, FAIL otherwise.
1257 */
1258 int
1259ExpandMappings(
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001260 char_u *pat,
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001261 regmatch_T *regmatch,
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001262 int *numMatches,
1263 char_u ***matches)
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001264{
1265 mapblock_T *mp;
Yegappan Lakshmanan5de4c432022-02-28 13:28:38 +00001266 garray_T ga;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001267 int hash;
1268 int count;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001269 char_u *p;
1270 int i;
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001271 int fuzzy;
1272 int match;
1273 int score;
Yegappan Lakshmanan5de4c432022-02-28 13:28:38 +00001274 fuzmatch_str_T *fuzmatch;
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001275
1276 fuzzy = cmdline_fuzzy_complete(pat);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001277
1278 validate_maphash();
1279
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001280 *numMatches = 0; // return values in case of FAIL
1281 *matches = NULL;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001282
Yegappan Lakshmanan5de4c432022-02-28 13:28:38 +00001283 if (!fuzzy)
1284 ga_init2(&ga, sizeof(char *), 3);
1285 else
1286 ga_init2(&ga, sizeof(fuzmatch_str_T), 3);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001287
Yegappan Lakshmanan5de4c432022-02-28 13:28:38 +00001288 // First search in map modifier arguments
1289 for (i = 0; i < 7; ++i)
1290 {
1291 if (i == 0)
1292 p = (char_u *)"<silent>";
1293 else if (i == 1)
1294 p = (char_u *)"<unique>";
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001295#ifdef FEAT_EVAL
Yegappan Lakshmanan5de4c432022-02-28 13:28:38 +00001296 else if (i == 2)
1297 p = (char_u *)"<script>";
1298 else if (i == 3)
1299 p = (char_u *)"<expr>";
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001300#endif
Yegappan Lakshmanan5de4c432022-02-28 13:28:38 +00001301 else if (i == 4 && !expand_buffer)
1302 p = (char_u *)"<buffer>";
1303 else if (i == 5)
1304 p = (char_u *)"<nowait>";
1305 else if (i == 6)
1306 p = (char_u *)"<special>";
1307 else
1308 continue;
1309
1310 if (!fuzzy)
1311 match = vim_regexec(regmatch, p, (colnr_T)0);
1312 else
1313 {
1314 score = fuzzy_match_str(p, pat);
1315 match = (score != 0);
1316 }
1317
1318 if (!match)
1319 continue;
1320
1321 if (ga_grow(&ga, 1) == FAIL)
1322 break;
1323
1324 if (fuzzy)
1325 {
1326 fuzmatch = &((fuzmatch_str_T *)ga.ga_data)[ga.ga_len];
1327 fuzmatch->idx = ga.ga_len;
1328 fuzmatch->str = vim_strsave(p);
1329 fuzmatch->score = score;
1330 }
1331 else
1332 ((char_u **)ga.ga_data)[ga.ga_len] = vim_strsave(p);
1333 ++ga.ga_len;
1334 }
1335
1336 for (hash = 0; hash < 256; ++hash)
1337 {
1338 if (expand_isabbrev)
1339 {
1340 if (hash > 0) // only one abbrev list
1341 break; // for (hash)
1342 mp = first_abbr;
1343 }
1344 else if (expand_buffer)
1345 mp = curbuf->b_maphash[hash];
1346 else
1347 mp = maphash[hash];
1348 for (; mp; mp = mp->m_next)
1349 {
1350 if (!(mp->m_mode & expand_mapmodes))
1351 continue;
1352
1353 p = translate_mapping(mp->m_keys);
1354 if (p == NULL)
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001355 continue;
1356
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001357 if (!fuzzy)
1358 match = vim_regexec(regmatch, p, (colnr_T)0);
1359 else
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001360 {
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001361 score = fuzzy_match_str(p, pat);
1362 match = (score != 0);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001363 }
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001364
1365 if (!match)
Yegappan Lakshmanan5de4c432022-02-28 13:28:38 +00001366 {
1367 vim_free(p);
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001368 continue;
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001369 }
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001370
Yegappan Lakshmanan5de4c432022-02-28 13:28:38 +00001371 if (ga_grow(&ga, 1) == FAIL)
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001372 {
Yegappan Lakshmanan5de4c432022-02-28 13:28:38 +00001373 vim_free(p);
1374 break;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001375 }
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001376
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001377 if (fuzzy)
1378 {
Yegappan Lakshmanan5de4c432022-02-28 13:28:38 +00001379 fuzmatch = &((fuzmatch_str_T *)ga.ga_data)[ga.ga_len];
1380 fuzmatch->idx = ga.ga_len;
1381 fuzmatch->str = p;
1382 fuzmatch->score = score;
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001383 }
1384 else
Yegappan Lakshmanan5de4c432022-02-28 13:28:38 +00001385 ((char_u **)ga.ga_data)[ga.ga_len] = p;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001386
Yegappan Lakshmanan5de4c432022-02-28 13:28:38 +00001387 ++ga.ga_len;
1388 } // for (mp)
1389 } // for (hash)
1390
1391 if (ga.ga_len == 0)
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001392 return FAIL;
1393
Yegappan Lakshmanan5de4c432022-02-28 13:28:38 +00001394 if (!fuzzy)
1395 {
1396 *matches = ga.ga_data;
1397 *numMatches = ga.ga_len;
1398 }
1399 else
1400 {
1401 if (fuzzymatches_to_strmatches(ga.ga_data, matches, ga.ga_len,
1402 FALSE) == FAIL)
1403 return FAIL;
1404 *numMatches = ga.ga_len;
1405 }
1406
1407 count = *numMatches;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001408 if (count > 1)
1409 {
1410 char_u **ptr1;
1411 char_u **ptr2;
1412 char_u **ptr3;
1413
1414 // Sort the matches
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001415 // Fuzzy matching already sorts the matches
1416 if (!fuzzy)
1417 sort_strings(*matches, count);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001418
1419 // Remove multiple entries
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001420 ptr1 = *matches;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001421 ptr2 = ptr1 + 1;
1422 ptr3 = ptr1 + count;
1423
1424 while (ptr2 < ptr3)
1425 {
1426 if (STRCMP(*ptr1, *ptr2))
1427 *++ptr1 = *ptr2++;
1428 else
1429 {
1430 vim_free(*ptr2++);
1431 count--;
1432 }
1433 }
1434 }
1435
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001436 *numMatches = count;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001437 return (count == 0 ? FAIL : OK);
1438}
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001439
1440/*
1441 * Check for an abbreviation.
1442 * Cursor is at ptr[col].
1443 * When inserting, mincol is where insert started.
1444 * For the command line, mincol is what is to be skipped over.
1445 * "c" is the character typed before check_abbr was called. It may have
1446 * ABBR_OFF added to avoid prepending a CTRL-V to it.
1447 *
1448 * Historic vi practice: The last character of an abbreviation must be an id
1449 * character ([a-zA-Z0-9_]). The characters in front of it must be all id
1450 * characters or all non-id characters. This allows for abbr. "#i" to
1451 * "#include".
1452 *
1453 * Vim addition: Allow for abbreviations that end in a non-keyword character.
1454 * Then there must be white space before the abbr.
1455 *
1456 * return TRUE if there is an abbreviation, FALSE if not
1457 */
1458 int
1459check_abbr(
1460 int c,
1461 char_u *ptr,
1462 int col,
1463 int mincol)
1464{
1465 int len;
1466 int scol; // starting column of the abbr.
1467 int j;
1468 char_u *s;
1469 char_u tb[MB_MAXBYTES + 4];
1470 mapblock_T *mp;
1471 mapblock_T *mp2;
1472 int clen = 0; // length in characters
1473 int is_id = TRUE;
1474 int vim_abbr;
1475
1476 if (typebuf.tb_no_abbr_cnt) // abbrev. are not recursive
1477 return FALSE;
1478
1479 // no remapping implies no abbreviation, except for CTRL-]
1480 if (noremap_keys() && c != Ctrl_RSB)
1481 return FALSE;
1482
1483 // Check for word before the cursor: If it ends in a keyword char all
1484 // chars before it must be keyword chars or non-keyword chars, but not
1485 // white space. If it ends in a non-keyword char we accept any characters
1486 // before it except white space.
1487 if (col == 0) // cannot be an abbr.
1488 return FALSE;
1489
1490 if (has_mbyte)
1491 {
1492 char_u *p;
1493
1494 p = mb_prevptr(ptr, ptr + col);
1495 if (!vim_iswordp(p))
1496 vim_abbr = TRUE; // Vim added abbr.
1497 else
1498 {
1499 vim_abbr = FALSE; // vi compatible abbr.
1500 if (p > ptr)
1501 is_id = vim_iswordp(mb_prevptr(ptr, p));
1502 }
1503 clen = 1;
1504 while (p > ptr + mincol)
1505 {
1506 p = mb_prevptr(ptr, p);
1507 if (vim_isspace(*p) || (!vim_abbr && is_id != vim_iswordp(p)))
1508 {
1509 p += (*mb_ptr2len)(p);
1510 break;
1511 }
1512 ++clen;
1513 }
1514 scol = (int)(p - ptr);
1515 }
1516 else
1517 {
1518 if (!vim_iswordc(ptr[col - 1]))
1519 vim_abbr = TRUE; // Vim added abbr.
1520 else
1521 {
1522 vim_abbr = FALSE; // vi compatible abbr.
1523 if (col > 1)
1524 is_id = vim_iswordc(ptr[col - 2]);
1525 }
1526 for (scol = col - 1; scol > 0 && !vim_isspace(ptr[scol - 1])
1527 && (vim_abbr || is_id == vim_iswordc(ptr[scol - 1])); --scol)
1528 ;
1529 }
1530
1531 if (scol < mincol)
1532 scol = mincol;
1533 if (scol < col) // there is a word in front of the cursor
1534 {
1535 ptr += scol;
1536 len = col - scol;
1537 mp = curbuf->b_first_abbr;
1538 mp2 = first_abbr;
1539 if (mp == NULL)
1540 {
1541 mp = mp2;
1542 mp2 = NULL;
1543 }
1544 for ( ; mp; mp->m_next == NULL
1545 ? (mp = mp2, mp2 = NULL) : (mp = mp->m_next))
1546 {
1547 int qlen = mp->m_keylen;
1548 char_u *q = mp->m_keys;
1549 int match;
1550
1551 if (vim_strbyte(mp->m_keys, K_SPECIAL) != NULL)
1552 {
1553 char_u *qe = vim_strsave(mp->m_keys);
1554
1555 // might have CSI escaped mp->m_keys
1556 if (qe != NULL)
1557 {
1558 q = qe;
1559 vim_unescape_csi(q);
1560 qlen = (int)STRLEN(q);
1561 }
1562 }
1563
1564 // find entries with right mode and keys
1565 match = (mp->m_mode & State)
1566 && qlen == len
1567 && !STRNCMP(q, ptr, (size_t)len);
1568 if (q != mp->m_keys)
1569 vim_free(q);
1570 if (match)
1571 break;
1572 }
1573 if (mp != NULL)
1574 {
Bram Moolenaar94075b22022-01-18 20:30:39 +00001575 int noremap;
1576 int silent;
1577#ifdef FEAT_EVAL
1578 int expr;
1579#endif
1580
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001581 // Found a match:
1582 // Insert the rest of the abbreviation in typebuf.tb_buf[].
1583 // This goes from end to start.
1584 //
1585 // Characters 0x000 - 0x100: normal chars, may need CTRL-V,
1586 // except K_SPECIAL: Becomes K_SPECIAL KS_SPECIAL KE_FILLER
1587 // Characters where IS_SPECIAL() == TRUE: key codes, need
1588 // K_SPECIAL. Other characters (with ABBR_OFF): don't use CTRL-V.
1589 //
1590 // Character CTRL-] is treated specially - it completes the
1591 // abbreviation, but is not inserted into the input stream.
1592 j = 0;
1593 if (c != Ctrl_RSB)
1594 {
1595 // special key code, split up
1596 if (IS_SPECIAL(c) || c == K_SPECIAL)
1597 {
1598 tb[j++] = K_SPECIAL;
1599 tb[j++] = K_SECOND(c);
1600 tb[j++] = K_THIRD(c);
1601 }
1602 else
1603 {
1604 if (c < ABBR_OFF && (c < ' ' || c > '~'))
1605 tb[j++] = Ctrl_V; // special char needs CTRL-V
1606 if (has_mbyte)
1607 {
Bram Moolenaar4934ed32021-04-30 19:43:11 +02001608 int newlen;
1609 char_u *escaped;
1610
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001611 // if ABBR_OFF has been added, remove it here
1612 if (c >= ABBR_OFF)
1613 c -= ABBR_OFF;
Bram Moolenaar4934ed32021-04-30 19:43:11 +02001614 newlen = (*mb_char2bytes)(c, tb + j);
1615 tb[j + newlen] = NUL;
1616 // Need to escape K_SPECIAL.
1617 escaped = vim_strsave_escape_csi(tb + j);
1618 if (escaped != NULL)
1619 {
Bram Moolenaar551c1ae2021-05-03 18:57:05 +02001620 newlen = (int)STRLEN(escaped);
Bram Moolenaar4934ed32021-04-30 19:43:11 +02001621 mch_memmove(tb + j, escaped, newlen);
1622 j += newlen;
1623 vim_free(escaped);
1624 }
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001625 }
1626 else
1627 tb[j++] = c;
1628 }
1629 tb[j] = NUL;
1630 // insert the last typed char
1631 (void)ins_typebuf(tb, 1, 0, TRUE, mp->m_silent);
1632 }
Bram Moolenaar94075b22022-01-18 20:30:39 +00001633
1634 // copy values here, calling eval_map_expr() may make "mp" invalid!
1635 noremap = mp->m_noremap;
1636 silent = mp->m_silent;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001637#ifdef FEAT_EVAL
Bram Moolenaar94075b22022-01-18 20:30:39 +00001638 expr = mp->m_expr;
1639
1640 if (expr)
Bram Moolenaar19db9e62022-01-11 11:58:19 +00001641 s = eval_map_expr(mp, c);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001642 else
1643#endif
1644 s = mp->m_str;
1645 if (s != NULL)
1646 {
1647 // insert the to string
Bram Moolenaar94075b22022-01-18 20:30:39 +00001648 (void)ins_typebuf(s, noremap, 0, TRUE, silent);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001649 // no abbrev. for these chars
1650 typebuf.tb_no_abbr_cnt += (int)STRLEN(s) + j + 1;
1651#ifdef FEAT_EVAL
Bram Moolenaar94075b22022-01-18 20:30:39 +00001652 if (expr)
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001653 vim_free(s);
1654#endif
1655 }
1656
1657 tb[0] = Ctrl_H;
1658 tb[1] = NUL;
1659 if (has_mbyte)
1660 len = clen; // Delete characters instead of bytes
1661 while (len-- > 0) // delete the from string
Bram Moolenaar94075b22022-01-18 20:30:39 +00001662 (void)ins_typebuf(tb, 1, 0, TRUE, silent);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001663 return TRUE;
1664 }
1665 }
1666 return FALSE;
1667}
1668
1669#ifdef FEAT_EVAL
1670/*
1671 * Evaluate the RHS of a mapping or abbreviations and take care of escaping
1672 * special characters.
Bram Moolenaar94075b22022-01-18 20:30:39 +00001673 * Careful: after this "mp" will be invalid if the mapping was deleted.
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001674 */
1675 char_u *
1676eval_map_expr(
Bram Moolenaar19db9e62022-01-11 11:58:19 +00001677 mapblock_T *mp,
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001678 int c) // NUL or typed character for abbreviation
1679{
1680 char_u *res;
1681 char_u *p;
1682 char_u *expr;
1683 pos_T save_cursor;
1684 int save_msg_col;
1685 int save_msg_row;
Bram Moolenaar19db9e62022-01-11 11:58:19 +00001686 scid_T save_sctx_sid = current_sctx.sc_sid;
1687 int save_sctx_version = current_sctx.sc_version;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001688
1689 // Remove escaping of CSI, because "str" is in a format to be used as
1690 // typeahead.
Bram Moolenaar19db9e62022-01-11 11:58:19 +00001691 expr = vim_strsave(mp->m_str);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001692 if (expr == NULL)
1693 return NULL;
1694 vim_unescape_csi(expr);
1695
1696 // Forbid changing text or using ":normal" to avoid most of the bad side
1697 // effects. Also restore the cursor position.
zeertzjqcfe45652022-05-27 17:26:55 +01001698 ++textlock;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001699 ++ex_normal_lock;
1700 set_vim_var_char(c); // set v:char to the typed character
1701 save_cursor = curwin->w_cursor;
1702 save_msg_col = msg_col;
1703 save_msg_row = msg_row;
Bram Moolenaar19db9e62022-01-11 11:58:19 +00001704 if (mp->m_script_ctx.sc_version == SCRIPT_VERSION_VIM9)
1705 {
1706 current_sctx.sc_sid = mp->m_script_ctx.sc_sid;
1707 current_sctx.sc_version = SCRIPT_VERSION_VIM9;
1708 }
1709
1710 // Note: the evaluation may make "mp" invalid.
Bram Moolenaarb171fb12020-06-24 20:34:03 +02001711 p = eval_to_string(expr, FALSE);
Bram Moolenaar19db9e62022-01-11 11:58:19 +00001712
zeertzjqcfe45652022-05-27 17:26:55 +01001713 --textlock;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001714 --ex_normal_lock;
1715 curwin->w_cursor = save_cursor;
1716 msg_col = save_msg_col;
1717 msg_row = save_msg_row;
Bram Moolenaar19db9e62022-01-11 11:58:19 +00001718 current_sctx.sc_sid = save_sctx_sid;
1719 current_sctx.sc_version = save_sctx_version;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001720
1721 vim_free(expr);
1722
1723 if (p == NULL)
1724 return NULL;
1725 // Escape CSI in the result to be able to use the string as typeahead.
1726 res = vim_strsave_escape_csi(p);
1727 vim_free(p);
1728
1729 return res;
1730}
1731#endif
1732
1733/*
1734 * Copy "p" to allocated memory, escaping K_SPECIAL and CSI so that the result
1735 * can be put in the typeahead buffer.
1736 * Returns NULL when out of memory.
1737 */
1738 char_u *
Bram Moolenaar957cf672020-11-12 14:21:06 +01001739vim_strsave_escape_csi(char_u *p)
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001740{
1741 char_u *res;
1742 char_u *s, *d;
1743
1744 // Need a buffer to hold up to three times as much. Four in case of an
1745 // illegal utf-8 byte:
1746 // 0xc0 -> 0xc3 0x80 -> 0xc3 K_SPECIAL KS_SPECIAL KE_FILLER
1747 res = alloc(STRLEN(p) * 4 + 1);
1748 if (res != NULL)
1749 {
1750 d = res;
1751 for (s = p; *s != NUL; )
1752 {
1753 if (s[0] == K_SPECIAL && s[1] != NUL && s[2] != NUL)
1754 {
1755 // Copy special key unmodified.
1756 *d++ = *s++;
1757 *d++ = *s++;
1758 *d++ = *s++;
1759 }
1760 else
1761 {
1762 // Add character, possibly multi-byte to destination, escaping
1763 // CSI and K_SPECIAL. Be careful, it can be an illegal byte!
1764 d = add_char2buf(PTR2CHAR(s), d);
1765 s += MB_CPTR2LEN(s);
1766 }
1767 }
1768 *d = NUL;
1769 }
1770 return res;
1771}
1772
1773/*
1774 * Remove escaping from CSI and K_SPECIAL characters. Reverse of
1775 * vim_strsave_escape_csi(). Works in-place.
1776 */
1777 void
1778vim_unescape_csi(char_u *p)
1779{
1780 char_u *s = p, *d = p;
1781
1782 while (*s != NUL)
1783 {
1784 if (s[0] == K_SPECIAL && s[1] == KS_SPECIAL && s[2] == KE_FILLER)
1785 {
1786 *d++ = K_SPECIAL;
1787 s += 3;
1788 }
1789 else if ((s[0] == K_SPECIAL || s[0] == CSI)
1790 && s[1] == KS_EXTRA && s[2] == (int)KE_CSI)
1791 {
1792 *d++ = CSI;
1793 s += 3;
1794 }
1795 else
1796 *d++ = *s++;
1797 }
1798 *d = NUL;
1799}
1800
1801/*
1802 * Write map commands for the current mappings to an .exrc file.
1803 * Return FAIL on error, OK otherwise.
1804 */
1805 int
1806makemap(
1807 FILE *fd,
1808 buf_T *buf) // buffer for local mappings or NULL
1809{
1810 mapblock_T *mp;
1811 char_u c1, c2, c3;
1812 char_u *p;
1813 char *cmd;
1814 int abbr;
1815 int hash;
1816 int did_cpo = FALSE;
1817 int i;
1818
1819 validate_maphash();
1820
1821 // Do the loop twice: Once for mappings, once for abbreviations.
1822 // Then loop over all map hash lists.
1823 for (abbr = 0; abbr < 2; ++abbr)
1824 for (hash = 0; hash < 256; ++hash)
1825 {
1826 if (abbr)
1827 {
1828 if (hash > 0) // there is only one abbr list
1829 break;
1830 if (buf != NULL)
1831 mp = buf->b_first_abbr;
1832 else
1833 mp = first_abbr;
1834 }
1835 else
1836 {
1837 if (buf != NULL)
1838 mp = buf->b_maphash[hash];
1839 else
1840 mp = maphash[hash];
1841 }
1842
1843 for ( ; mp; mp = mp->m_next)
1844 {
1845 // skip script-local mappings
1846 if (mp->m_noremap == REMAP_SCRIPT)
1847 continue;
1848
1849 // skip mappings that contain a <SNR> (script-local thing),
1850 // they probably don't work when loaded again
1851 for (p = mp->m_str; *p != NUL; ++p)
1852 if (p[0] == K_SPECIAL && p[1] == KS_EXTRA
1853 && p[2] == (int)KE_SNR)
1854 break;
1855 if (*p != NUL)
1856 continue;
1857
1858 // It's possible to create a mapping and then ":unmap" certain
1859 // modes. We recreate this here by mapping the individual
1860 // modes, which requires up to three of them.
1861 c1 = NUL;
1862 c2 = NUL;
1863 c3 = NUL;
1864 if (abbr)
1865 cmd = "abbr";
1866 else
1867 cmd = "map";
1868 switch (mp->m_mode)
1869 {
Bram Moolenaar24959102022-05-07 20:01:16 +01001870 case MODE_NORMAL | MODE_VISUAL | MODE_SELECT
1871 | MODE_OP_PENDING:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001872 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01001873 case MODE_NORMAL:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001874 c1 = 'n';
1875 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01001876 case MODE_VISUAL:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001877 c1 = 'x';
1878 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01001879 case MODE_SELECT:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001880 c1 = 's';
1881 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01001882 case MODE_OP_PENDING:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001883 c1 = 'o';
1884 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01001885 case MODE_NORMAL | MODE_VISUAL:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001886 c1 = 'n';
1887 c2 = 'x';
1888 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01001889 case MODE_NORMAL | MODE_SELECT:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001890 c1 = 'n';
1891 c2 = 's';
1892 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01001893 case MODE_NORMAL | MODE_OP_PENDING:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001894 c1 = 'n';
1895 c2 = 'o';
1896 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01001897 case MODE_VISUAL | MODE_SELECT:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001898 c1 = 'v';
1899 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01001900 case MODE_VISUAL | MODE_OP_PENDING:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001901 c1 = 'x';
1902 c2 = 'o';
1903 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01001904 case MODE_SELECT | MODE_OP_PENDING:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001905 c1 = 's';
1906 c2 = 'o';
1907 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01001908 case MODE_NORMAL | MODE_VISUAL | MODE_SELECT:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001909 c1 = 'n';
1910 c2 = 'v';
1911 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01001912 case MODE_NORMAL | MODE_VISUAL | MODE_OP_PENDING:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001913 c1 = 'n';
1914 c2 = 'x';
1915 c3 = 'o';
1916 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01001917 case MODE_NORMAL | MODE_SELECT | MODE_OP_PENDING:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001918 c1 = 'n';
1919 c2 = 's';
1920 c3 = 'o';
1921 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01001922 case MODE_VISUAL | MODE_SELECT | MODE_OP_PENDING:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001923 c1 = 'v';
1924 c2 = 'o';
1925 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01001926 case MODE_CMDLINE | MODE_INSERT:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001927 if (!abbr)
1928 cmd = "map!";
1929 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01001930 case MODE_CMDLINE:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001931 c1 = 'c';
1932 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01001933 case MODE_INSERT:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001934 c1 = 'i';
1935 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01001936 case MODE_LANGMAP:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001937 c1 = 'l';
1938 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01001939 case MODE_TERMINAL:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001940 c1 = 't';
1941 break;
1942 default:
Bram Moolenaar6d057012021-12-31 18:49:43 +00001943 iemsg(_(e_makemap_illegal_mode));
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001944 return FAIL;
1945 }
1946 do // do this twice if c2 is set, 3 times with c3
1947 {
1948 // When outputting <> form, need to make sure that 'cpo'
1949 // is set to the Vim default.
1950 if (!did_cpo)
1951 {
1952 if (*mp->m_str == NUL) // will use <Nop>
1953 did_cpo = TRUE;
1954 else
1955 for (i = 0; i < 2; ++i)
1956 for (p = (i ? mp->m_str : mp->m_keys); *p; ++p)
1957 if (*p == K_SPECIAL || *p == NL)
1958 did_cpo = TRUE;
1959 if (did_cpo)
1960 {
1961 if (fprintf(fd, "let s:cpo_save=&cpo") < 0
1962 || put_eol(fd) < 0
1963 || fprintf(fd, "set cpo&vim") < 0
1964 || put_eol(fd) < 0)
1965 return FAIL;
1966 }
1967 }
1968 if (c1 && putc(c1, fd) < 0)
1969 return FAIL;
1970 if (mp->m_noremap != REMAP_YES && fprintf(fd, "nore") < 0)
1971 return FAIL;
1972 if (fputs(cmd, fd) < 0)
1973 return FAIL;
1974 if (buf != NULL && fputs(" <buffer>", fd) < 0)
1975 return FAIL;
1976 if (mp->m_nowait && fputs(" <nowait>", fd) < 0)
1977 return FAIL;
1978 if (mp->m_silent && fputs(" <silent>", fd) < 0)
1979 return FAIL;
1980#ifdef FEAT_EVAL
1981 if (mp->m_noremap == REMAP_SCRIPT
1982 && fputs("<script>", fd) < 0)
1983 return FAIL;
1984 if (mp->m_expr && fputs(" <expr>", fd) < 0)
1985 return FAIL;
1986#endif
1987
1988 if ( putc(' ', fd) < 0
1989 || put_escstr(fd, mp->m_keys, 0) == FAIL
1990 || putc(' ', fd) < 0
1991 || put_escstr(fd, mp->m_str, 1) == FAIL
1992 || put_eol(fd) < 0)
1993 return FAIL;
1994 c1 = c2;
1995 c2 = c3;
1996 c3 = NUL;
1997 } while (c1 != NUL);
1998 }
1999 }
2000
2001 if (did_cpo)
2002 if (fprintf(fd, "let &cpo=s:cpo_save") < 0
2003 || put_eol(fd) < 0
2004 || fprintf(fd, "unlet s:cpo_save") < 0
2005 || put_eol(fd) < 0)
2006 return FAIL;
2007 return OK;
2008}
2009
2010/*
2011 * write escape string to file
2012 * "what": 0 for :map lhs, 1 for :map rhs, 2 for :set
2013 *
2014 * return FAIL for failure, OK otherwise
2015 */
2016 int
2017put_escstr(FILE *fd, char_u *strstart, int what)
2018{
2019 char_u *str = strstart;
2020 int c;
2021 int modifiers;
2022
2023 // :map xx <Nop>
2024 if (*str == NUL && what == 1)
2025 {
2026 if (fprintf(fd, "<Nop>") < 0)
2027 return FAIL;
2028 return OK;
2029 }
2030
2031 for ( ; *str != NUL; ++str)
2032 {
2033 char_u *p;
2034
2035 // Check for a multi-byte character, which may contain escaped
2036 // K_SPECIAL and CSI bytes
2037 p = mb_unescape(&str);
2038 if (p != NULL)
2039 {
2040 while (*p != NUL)
2041 if (fputc(*p++, fd) < 0)
2042 return FAIL;
2043 --str;
2044 continue;
2045 }
2046
2047 c = *str;
2048 // Special key codes have to be translated to be able to make sense
2049 // when they are read back.
2050 if (c == K_SPECIAL && what != 2)
2051 {
Bram Moolenaar02c037a2020-08-30 19:26:45 +02002052 modifiers = 0;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002053 if (str[1] == KS_MODIFIER)
2054 {
2055 modifiers = str[2];
2056 str += 3;
2057 c = *str;
2058 }
2059 if (c == K_SPECIAL)
2060 {
2061 c = TO_SPECIAL(str[1], str[2]);
2062 str += 2;
2063 }
2064 if (IS_SPECIAL(c) || modifiers) // special key
2065 {
2066 if (fputs((char *)get_special_key_name(c, modifiers), fd) < 0)
2067 return FAIL;
2068 continue;
2069 }
2070 }
2071
2072 // A '\n' in a map command should be written as <NL>.
2073 // A '\n' in a set command should be written as \^V^J.
2074 if (c == NL)
2075 {
2076 if (what == 2)
2077 {
Bram Moolenaar424bcae2022-01-31 14:59:41 +00002078 if (fprintf(fd, "\\\026\n") < 0)
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002079 return FAIL;
2080 }
2081 else
2082 {
2083 if (fprintf(fd, "<NL>") < 0)
2084 return FAIL;
2085 }
2086 continue;
2087 }
2088
2089 // Some characters have to be escaped with CTRL-V to
2090 // prevent them from misinterpreted in DoOneCmd().
2091 // A space, Tab and '"' has to be escaped with a backslash to
2092 // prevent it to be misinterpreted in do_set().
2093 // A space has to be escaped with a CTRL-V when it's at the start of a
2094 // ":map" rhs.
2095 // A '<' has to be escaped with a CTRL-V to prevent it being
2096 // interpreted as the start of a special key name.
2097 // A space in the lhs of a :map needs a CTRL-V.
2098 if (what == 2 && (VIM_ISWHITE(c) || c == '"' || c == '\\'))
2099 {
2100 if (putc('\\', fd) < 0)
2101 return FAIL;
2102 }
2103 else if (c < ' ' || c > '~' || c == '|'
2104 || (what == 0 && c == ' ')
2105 || (what == 1 && str == strstart && c == ' ')
2106 || (what != 2 && c == '<'))
2107 {
2108 if (putc(Ctrl_V, fd) < 0)
2109 return FAIL;
2110 }
2111 if (putc(c, fd) < 0)
2112 return FAIL;
2113 }
2114 return OK;
2115}
2116
2117/*
2118 * Check all mappings for the presence of special key codes.
2119 * Used after ":set term=xxx".
2120 */
2121 void
2122check_map_keycodes(void)
2123{
2124 mapblock_T *mp;
2125 char_u *p;
2126 int i;
2127 char_u buf[3];
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002128 int abbr;
2129 int hash;
2130 buf_T *bp;
Bram Moolenaare31ee862020-01-07 20:59:34 +01002131 ESTACK_CHECK_DECLARATION
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002132
2133 validate_maphash();
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002134 // avoids giving error messages
2135 estack_push(ETYPE_INTERNAL, (char_u *)"mappings", 0);
Bram Moolenaare31ee862020-01-07 20:59:34 +01002136 ESTACK_CHECK_SETUP
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002137
Bram Moolenaar32aa1022019-11-02 22:54:41 +01002138 // Do this once for each buffer, and then once for global
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002139 // mappings/abbreviations with bp == NULL
2140 for (bp = firstbuf; ; bp = bp->b_next)
2141 {
2142 // Do the loop twice: Once for mappings, once for abbreviations.
2143 // Then loop over all map hash lists.
2144 for (abbr = 0; abbr <= 1; ++abbr)
2145 for (hash = 0; hash < 256; ++hash)
2146 {
2147 if (abbr)
2148 {
2149 if (hash) // there is only one abbr list
2150 break;
2151 if (bp != NULL)
2152 mp = bp->b_first_abbr;
2153 else
2154 mp = first_abbr;
2155 }
2156 else
2157 {
2158 if (bp != NULL)
2159 mp = bp->b_maphash[hash];
2160 else
2161 mp = maphash[hash];
2162 }
2163 for ( ; mp != NULL; mp = mp->m_next)
2164 {
2165 for (i = 0; i <= 1; ++i) // do this twice
2166 {
2167 if (i == 0)
2168 p = mp->m_keys; // once for the "from" part
2169 else
2170 p = mp->m_str; // and once for the "to" part
2171 while (*p)
2172 {
2173 if (*p == K_SPECIAL)
2174 {
2175 ++p;
2176 if (*p < 128) // for "normal" tcap entries
2177 {
2178 buf[0] = p[0];
2179 buf[1] = p[1];
2180 buf[2] = NUL;
2181 (void)add_termcap_entry(buf, FALSE);
2182 }
2183 ++p;
2184 }
2185 ++p;
2186 }
2187 }
2188 }
2189 }
2190 if (bp == NULL)
2191 break;
2192 }
Bram Moolenaare31ee862020-01-07 20:59:34 +01002193 ESTACK_CHECK_NOW
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002194 estack_pop();
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002195}
2196
2197#if defined(FEAT_EVAL) || defined(PROTO)
2198/*
2199 * Check the string "keys" against the lhs of all mappings.
2200 * Return pointer to rhs of mapping (mapblock->m_str).
2201 * NULL when no mapping found.
2202 */
2203 char_u *
2204check_map(
2205 char_u *keys,
2206 int mode,
2207 int exact, // require exact match
2208 int ign_mod, // ignore preceding modifier
2209 int abbr, // do abbreviations
2210 mapblock_T **mp_ptr, // return: pointer to mapblock or NULL
2211 int *local_ptr) // return: buffer-local mapping or NULL
2212{
2213 int hash;
2214 int len, minlen;
2215 mapblock_T *mp;
2216 char_u *s;
2217 int local;
2218
2219 validate_maphash();
2220
2221 len = (int)STRLEN(keys);
2222 for (local = 1; local >= 0; --local)
2223 // loop over all hash lists
2224 for (hash = 0; hash < 256; ++hash)
2225 {
2226 if (abbr)
2227 {
2228 if (hash > 0) // there is only one list.
2229 break;
2230 if (local)
2231 mp = curbuf->b_first_abbr;
2232 else
2233 mp = first_abbr;
2234 }
2235 else if (local)
2236 mp = curbuf->b_maphash[hash];
2237 else
2238 mp = maphash[hash];
2239 for ( ; mp != NULL; mp = mp->m_next)
2240 {
2241 // skip entries with wrong mode, wrong length and not matching
2242 // ones
2243 if ((mp->m_mode & mode) && (!exact || mp->m_keylen == len))
2244 {
2245 if (len > mp->m_keylen)
2246 minlen = mp->m_keylen;
2247 else
2248 minlen = len;
2249 s = mp->m_keys;
2250 if (ign_mod && s[0] == K_SPECIAL && s[1] == KS_MODIFIER
2251 && s[2] != NUL)
2252 {
2253 s += 3;
2254 if (len > mp->m_keylen - 3)
2255 minlen = mp->m_keylen - 3;
2256 }
2257 if (STRNCMP(s, keys, minlen) == 0)
2258 {
2259 if (mp_ptr != NULL)
2260 *mp_ptr = mp;
2261 if (local_ptr != NULL)
2262 *local_ptr = local;
2263 return mp->m_str;
2264 }
2265 }
2266 }
2267 }
2268
2269 return NULL;
2270}
2271
Ernie Rael659c2402022-04-24 18:40:28 +01002272/*
2273 * Fill in the empty dictionary with items as defined by maparg builtin.
2274 */
2275 static void
2276mapblock2dict(
2277 mapblock_T *mp,
2278 dict_T *dict,
2279 char_u *lhsrawalt, // may be NULL
Ernie Rael51d04d12022-05-04 15:40:22 +01002280 int buffer_local, // false if not buffer local mapping
2281 int abbr) // true if abbreviation
Ernie Rael659c2402022-04-24 18:40:28 +01002282{
2283 char_u *lhs = str2special_save(mp->m_keys, TRUE);
2284 char_u *mapmode = map_mode_to_chars(mp->m_mode);
2285
2286 dict_add_string(dict, "lhs", lhs);
2287 vim_free(lhs);
2288 dict_add_string(dict, "lhsraw", mp->m_keys);
2289 if (lhsrawalt)
2290 // Also add the value for the simplified entry.
2291 dict_add_string(dict, "lhsrawalt", lhsrawalt);
2292 dict_add_string(dict, "rhs", mp->m_orig_str);
2293 dict_add_number(dict, "noremap", mp->m_noremap ? 1L : 0L);
2294 dict_add_number(dict, "script", mp->m_noremap == REMAP_SCRIPT
2295 ? 1L : 0L);
2296 dict_add_number(dict, "expr", mp->m_expr ? 1L : 0L);
2297 dict_add_number(dict, "silent", mp->m_silent ? 1L : 0L);
2298 dict_add_number(dict, "sid", (long)mp->m_script_ctx.sc_sid);
2299 dict_add_number(dict, "scriptversion",
2300 (long)mp->m_script_ctx.sc_version);
2301 dict_add_number(dict, "lnum", (long)mp->m_script_ctx.sc_lnum);
2302 dict_add_number(dict, "buffer", (long)buffer_local);
2303 dict_add_number(dict, "nowait", mp->m_nowait ? 1L : 0L);
2304 dict_add_string(dict, "mode", mapmode);
Ernie Rael51d04d12022-05-04 15:40:22 +01002305 dict_add_number(dict, "abbr", abbr ? 1L : 0L);
Ernie Raeld8f5f762022-05-10 17:50:39 +01002306 dict_add_number(dict, "mode_bits", mp->m_mode);
Ernie Rael659c2402022-04-24 18:40:28 +01002307
2308 vim_free(mapmode);
2309}
2310
Yegappan Lakshmanan4a155042021-07-30 21:32:45 +02002311 static void
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002312get_maparg(typval_T *argvars, typval_T *rettv, int exact)
2313{
2314 char_u *keys;
Bram Moolenaar9c652532020-05-24 13:10:18 +02002315 char_u *keys_simplified;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002316 char_u *which;
2317 char_u buf[NUMBUFLEN];
2318 char_u *keys_buf = NULL;
Bram Moolenaar9c652532020-05-24 13:10:18 +02002319 char_u *alt_keys_buf = NULL;
2320 int did_simplify = FALSE;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002321 char_u *rhs;
2322 int mode;
2323 int abbr = FALSE;
2324 int get_dict = FALSE;
zeertzjq2c8a7eb2022-04-26 21:36:21 +01002325 mapblock_T *mp = NULL;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002326 int buffer_local;
Bram Moolenaar9c652532020-05-24 13:10:18 +02002327 int flags = REPTERM_FROM_PART | REPTERM_DO_LT;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002328
2329 // return empty string for failure
2330 rettv->v_type = VAR_STRING;
2331 rettv->vval.v_string = NULL;
2332
2333 keys = tv_get_string(&argvars[0]);
2334 if (*keys == NUL)
2335 return;
2336
2337 if (argvars[1].v_type != VAR_UNKNOWN)
2338 {
2339 which = tv_get_string_buf_chk(&argvars[1], buf);
2340 if (argvars[2].v_type != VAR_UNKNOWN)
2341 {
Bram Moolenaar04d594b2020-09-02 22:25:35 +02002342 abbr = (int)tv_get_bool(&argvars[2]);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002343 if (argvars[3].v_type != VAR_UNKNOWN)
Bram Moolenaar04d594b2020-09-02 22:25:35 +02002344 get_dict = (int)tv_get_bool(&argvars[3]);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002345 }
2346 }
2347 else
2348 which = (char_u *)"";
2349 if (which == NULL)
2350 return;
2351
2352 mode = get_map_mode(&which, 0);
2353
Bram Moolenaar9c652532020-05-24 13:10:18 +02002354 keys_simplified = replace_termcodes(keys, &keys_buf, flags, &did_simplify);
2355 rhs = check_map(keys_simplified, mode, exact, FALSE, abbr,
2356 &mp, &buffer_local);
2357 if (did_simplify)
2358 {
2359 // When the lhs is being simplified the not-simplified keys are
Bram Moolenaar8e7d6222020-12-18 19:49:56 +01002360 // preferred for printing, like in do_map().
Bram Moolenaar9c652532020-05-24 13:10:18 +02002361 (void)replace_termcodes(keys, &alt_keys_buf,
2362 flags | REPTERM_NO_SIMPLIFY, NULL);
2363 rhs = check_map(alt_keys_buf, mode, exact, FALSE, abbr, &mp,
2364 &buffer_local);
2365 }
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002366
2367 if (!get_dict)
2368 {
2369 // Return a string.
2370 if (rhs != NULL)
2371 {
2372 if (*rhs == NUL)
2373 rettv->vval.v_string = vim_strsave((char_u *)"<Nop>");
2374 else
2375 rettv->vval.v_string = str2special_save(rhs, FALSE);
2376 }
2377
2378 }
2379 else if (rettv_dict_alloc(rettv) != FAIL && rhs != NULL)
Ernie Rael659c2402022-04-24 18:40:28 +01002380 mapblock2dict(mp, rettv->vval.v_dict,
Ernie Rael51d04d12022-05-04 15:40:22 +01002381 did_simplify ? keys_simplified : NULL,
2382 buffer_local, abbr);
Bram Moolenaar9c652532020-05-24 13:10:18 +02002383
2384 vim_free(keys_buf);
2385 vim_free(alt_keys_buf);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002386}
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002387
2388/*
Ernie Rael09661202022-04-25 14:40:44 +01002389 * "maplist()" function
Ernie Rael659c2402022-04-24 18:40:28 +01002390 */
2391 void
Ernie Rael09661202022-04-25 14:40:44 +01002392f_maplist(typval_T *argvars UNUSED, typval_T *rettv)
Ernie Rael659c2402022-04-24 18:40:28 +01002393{
2394 dict_T *d;
2395 mapblock_T *mp;
2396 int buffer_local;
2397 char_u *keys_buf;
2398 int did_simplify;
2399 int hash;
2400 char_u *lhs;
2401 const int flags = REPTERM_FROM_PART | REPTERM_DO_LT;
Ernie Rael09661202022-04-25 14:40:44 +01002402 int abbr = FALSE;
2403
2404 if (in_vim9script() && check_for_opt_bool_arg(argvars, 0) == FAIL)
2405 return;
2406 if (argvars[0].v_type != VAR_UNKNOWN)
2407 abbr = tv_get_bool(&argvars[0]);
Ernie Rael659c2402022-04-24 18:40:28 +01002408
2409 if (rettv_list_alloc(rettv) != OK)
2410 return;
2411
2412 validate_maphash();
2413
2414 // Do it twice: once for global maps and once for local maps.
2415 for (buffer_local = 0; buffer_local <= 1; ++buffer_local)
2416 {
2417 for (hash = 0; hash < 256; ++hash)
2418 {
Ernie Rael09661202022-04-25 14:40:44 +01002419 if (abbr)
2420 {
2421 if (hash > 0) // there is only one abbr list
2422 break;
2423 if (buffer_local)
2424 mp = curbuf->b_first_abbr;
2425 else
2426 mp = first_abbr;
2427 }
2428 else if (buffer_local)
Ernie Rael659c2402022-04-24 18:40:28 +01002429 mp = curbuf->b_maphash[hash];
2430 else
2431 mp = maphash[hash];
2432 for (; mp; mp = mp->m_next)
2433 {
2434 if (mp->m_simplified)
2435 continue;
2436 if ((d = dict_alloc()) == NULL)
2437 return;
2438 if (list_append_dict(rettv->vval.v_list, d) == FAIL)
2439 return;
2440
2441 keys_buf = NULL;
2442 did_simplify = FALSE;
2443
2444 lhs = str2special_save(mp->m_keys, TRUE);
2445 (void)replace_termcodes(lhs, &keys_buf, flags, &did_simplify);
2446 vim_free(lhs);
2447
2448 mapblock2dict(mp, d,
Ernie Rael51d04d12022-05-04 15:40:22 +01002449 did_simplify ? keys_buf : NULL,
2450 buffer_local, abbr);
Ernie Rael659c2402022-04-24 18:40:28 +01002451 vim_free(keys_buf);
2452 }
2453 }
2454 }
2455}
2456
2457/*
Yegappan Lakshmanan4a155042021-07-30 21:32:45 +02002458 * "maparg()" function
2459 */
2460 void
2461f_maparg(typval_T *argvars, typval_T *rettv)
2462{
2463 if (in_vim9script()
2464 && (check_for_string_arg(argvars, 0) == FAIL
2465 || check_for_opt_string_arg(argvars, 1) == FAIL
2466 || (argvars[1].v_type != VAR_UNKNOWN
2467 && (check_for_opt_bool_arg(argvars, 2) == FAIL
2468 || (argvars[2].v_type != VAR_UNKNOWN
2469 && check_for_opt_bool_arg(argvars, 3) == FAIL)))))
2470 return;
2471
2472 get_maparg(argvars, rettv, TRUE);
2473}
2474
2475/*
2476 * "mapcheck()" function
2477 */
2478 void
2479f_mapcheck(typval_T *argvars, typval_T *rettv)
2480{
2481 if (in_vim9script()
2482 && (check_for_string_arg(argvars, 0) == FAIL
2483 || check_for_opt_string_arg(argvars, 1) == FAIL
2484 || (argvars[1].v_type != VAR_UNKNOWN
2485 && check_for_opt_bool_arg(argvars, 2) == FAIL)))
2486 return;
2487
2488 get_maparg(argvars, rettv, FALSE);
2489}
2490
2491/*
Ernie Rael51d04d12022-05-04 15:40:22 +01002492 * Get the mapping mode from the mode string.
2493 * It may contain multiple characters, eg "nox", or "!", or ' '
2494 * Return 0 if there is an error.
2495 */
2496 static int
2497get_map_mode_string(char_u *mode_string, int abbr)
2498{
2499 char_u *p = mode_string;
2500 int mode = 0;
2501 int tmode;
2502 int modec;
Bram Moolenaar24959102022-05-07 20:01:16 +01002503 const int MASK_V = MODE_VISUAL | MODE_SELECT;
2504 const int MASK_MAP = MODE_VISUAL | MODE_SELECT | MODE_NORMAL
2505 | MODE_OP_PENDING;
2506 const int MASK_BANG = MODE_INSERT | MODE_CMDLINE;
Ernie Rael51d04d12022-05-04 15:40:22 +01002507
2508 if (*p == NUL)
2509 p = (char_u *)" "; // compatibility
2510 while ((modec = *p++))
2511 {
2512 switch (modec)
2513 {
Bram Moolenaar24959102022-05-07 20:01:16 +01002514 case 'i': tmode = MODE_INSERT; break;
2515 case 'l': tmode = MODE_LANGMAP; break;
2516 case 'c': tmode = MODE_CMDLINE; break;
2517 case 'n': tmode = MODE_NORMAL; break;
2518 case 'x': tmode = MODE_VISUAL; break;
2519 case 's': tmode = MODE_SELECT; break;
2520 case 'o': tmode = MODE_OP_PENDING; break;
2521 case 't': tmode = MODE_TERMINAL; break;
Ernie Rael51d04d12022-05-04 15:40:22 +01002522 case 'v': tmode = MASK_V; break;
2523 case '!': tmode = MASK_BANG; break;
2524 case ' ': tmode = MASK_MAP; break;
2525 default:
2526 return 0; // error, unknown mode character
2527 }
2528 mode |= tmode;
2529 }
2530 if ((abbr && (mode & ~MASK_BANG) != 0)
2531 || (!abbr && (mode & (mode-1)) != 0 // more than one bit set
2532 && (
2533 // false if multiple bits set in mode and mode is fully
2534 // contained in one mask
2535 !(((mode & MASK_BANG) != 0 && (mode & ~MASK_BANG) == 0)
2536 || ((mode & MASK_MAP) != 0 && (mode & ~MASK_MAP) == 0)))))
2537 return 0;
2538
2539 return mode;
2540}
2541
2542/*
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002543 * "mapset()" function
2544 */
2545 void
2546f_mapset(typval_T *argvars, typval_T *rettv UNUSED)
2547{
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002548 char_u *keys_buf = NULL;
2549 char_u *which;
2550 int mode;
2551 char_u buf[NUMBUFLEN];
2552 int is_abbr;
2553 dict_T *d;
2554 char_u *lhs;
Bram Moolenaar9c652532020-05-24 13:10:18 +02002555 char_u *lhsraw;
2556 char_u *lhsrawalt;
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002557 char_u *rhs;
Bram Moolenaarc94c1462020-05-22 20:01:06 +02002558 char_u *orig_rhs;
2559 char_u *arg_buf = NULL;
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002560 int noremap;
2561 int expr;
2562 int silent;
Bram Moolenaar7ba1e4d2021-04-24 13:12:38 +02002563 int buffer;
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002564 scid_T sid;
Bram Moolenaara9528b32022-01-18 20:51:35 +00002565 int scriptversion;
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002566 linenr_T lnum;
2567 mapblock_T **map_table = maphash;
2568 mapblock_T **abbr_table = &first_abbr;
2569 int nowait;
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002570 char_u *arg;
Ernie Rael51d04d12022-05-04 15:40:22 +01002571 int dict_only;
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002572
Ernie Rael51d04d12022-05-04 15:40:22 +01002573 // If first arg is a dict, then that's the only arg permitted.
2574 dict_only = argvars[0].v_type == VAR_DICT;
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02002575 if (in_vim9script()
Ernie Rael51d04d12022-05-04 15:40:22 +01002576 && (check_for_string_or_dict_arg(argvars, 0) == FAIL
2577 || (dict_only && check_for_unknown_arg(argvars, 1) == FAIL)
2578 || (!dict_only
2579 && (check_for_string_arg(argvars, 0) == FAIL
2580 || check_for_bool_arg(argvars, 1) == FAIL
2581 || check_for_dict_arg(argvars, 2) == FAIL))))
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02002582 return;
2583
Ernie Rael51d04d12022-05-04 15:40:22 +01002584 if (dict_only)
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002585 {
Ernie Rael51d04d12022-05-04 15:40:22 +01002586 d = argvars[0].vval.v_dict;
2587 which = dict_get_string(d, (char_u *)"mode", FALSE);
2588 is_abbr = dict_get_bool(d, (char_u *)"abbr", -1);
2589 if (which == NULL || is_abbr < 0)
2590 {
2591 emsg(_(e_entries_missing_in_mapset_dict_argument));
2592 return;
2593 }
2594 }
2595 else
2596 {
2597 which = tv_get_string_buf_chk(&argvars[0], buf);
2598 if (which == NULL)
2599 return;
2600 is_abbr = (int)tv_get_bool(&argvars[1]);
2601
2602 if (argvars[2].v_type != VAR_DICT)
2603 {
2604 emsg(_(e_dictionary_required));
2605 return;
2606 }
2607 d = argvars[2].vval.v_dict;
2608 }
2609 mode = get_map_mode_string(which, is_abbr);
2610 if (mode == 0)
2611 {
2612 semsg(_(e_illegal_map_mode_string_str), which);
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002613 return;
2614 }
Ernie Rael51d04d12022-05-04 15:40:22 +01002615
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002616
2617 // Get the values in the same order as above in get_maparg().
2618 lhs = dict_get_string(d, (char_u *)"lhs", FALSE);
Bram Moolenaar9c652532020-05-24 13:10:18 +02002619 lhsraw = dict_get_string(d, (char_u *)"lhsraw", FALSE);
2620 lhsrawalt = dict_get_string(d, (char_u *)"lhsrawalt", FALSE);
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002621 rhs = dict_get_string(d, (char_u *)"rhs", FALSE);
Bram Moolenaar9c652532020-05-24 13:10:18 +02002622 if (lhs == NULL || lhsraw == NULL || rhs == NULL)
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002623 {
Bram Moolenaarb09feaa2022-01-02 20:20:45 +00002624 emsg(_(e_entries_missing_in_mapset_dict_argument));
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002625 return;
2626 }
Bram Moolenaarc94c1462020-05-22 20:01:06 +02002627 orig_rhs = rhs;
2628 rhs = replace_termcodes(rhs, &arg_buf,
2629 REPTERM_DO_LT | REPTERM_SPECIAL, NULL);
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002630
2631 noremap = dict_get_number(d, (char_u *)"noremap") ? REMAP_NONE: 0;
2632 if (dict_get_number(d, (char_u *)"script") != 0)
2633 noremap = REMAP_SCRIPT;
2634 expr = dict_get_number(d, (char_u *)"expr") != 0;
2635 silent = dict_get_number(d, (char_u *)"silent") != 0;
2636 sid = dict_get_number(d, (char_u *)"sid");
Bram Moolenaara9528b32022-01-18 20:51:35 +00002637 scriptversion = dict_get_number(d, (char_u *)"scriptversion");
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002638 lnum = dict_get_number(d, (char_u *)"lnum");
Bram Moolenaar7ba1e4d2021-04-24 13:12:38 +02002639 buffer = dict_get_number(d, (char_u *)"buffer");
2640 nowait = dict_get_number(d, (char_u *)"nowait") != 0;
2641 // mode from the dict is not used
2642
2643 if (buffer)
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002644 {
2645 map_table = curbuf->b_maphash;
2646 abbr_table = &curbuf->b_first_abbr;
2647 }
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002648
2649 // Delete any existing mapping for this lhs and mode.
Bram Moolenaar7ba1e4d2021-04-24 13:12:38 +02002650 if (buffer)
2651 {
2652 arg = alloc(STRLEN(lhs) + STRLEN("<buffer>") + 1);
2653 if (arg == NULL)
2654 return;
2655 STRCPY(arg, "<buffer>");
2656 STRCPY(arg + 8, lhs);
2657 }
2658 else
2659 {
2660 arg = vim_strsave(lhs);
2661 if (arg == NULL)
2662 return;
2663 }
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002664 do_map(1, arg, mode, is_abbr);
2665 vim_free(arg);
2666
Bram Moolenaar9c652532020-05-24 13:10:18 +02002667 (void)map_add(map_table, abbr_table, lhsraw, rhs, orig_rhs, noremap,
Bram Moolenaara9528b32022-01-18 20:51:35 +00002668 nowait, silent, mode, is_abbr, expr, sid, scriptversion, lnum, 0);
Bram Moolenaar9c652532020-05-24 13:10:18 +02002669 if (lhsrawalt != NULL)
2670 (void)map_add(map_table, abbr_table, lhsrawalt, rhs, orig_rhs, noremap,
Bram Moolenaara9528b32022-01-18 20:51:35 +00002671 nowait, silent, mode, is_abbr, expr, sid, scriptversion,
2672 lnum, 1);
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002673 vim_free(keys_buf);
Bram Moolenaarc94c1462020-05-22 20:01:06 +02002674 vim_free(arg_buf);
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002675}
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002676#endif
2677
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002678
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002679#if defined(MSWIN) || defined(MACOS_X)
2680
Bram Moolenaar24959102022-05-07 20:01:16 +01002681# define VIS_SEL (MODE_VISUAL | MODE_SELECT) // abbreviation
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002682
2683/*
2684 * Default mappings for some often used keys.
2685 */
2686struct initmap
2687{
2688 char_u *arg;
2689 int mode;
2690};
2691
2692# ifdef FEAT_GUI_MSWIN
2693// Use the Windows (CUA) keybindings. (GUI)
2694static struct initmap initmappings[] =
2695{
2696 // paste, copy and cut
Bram Moolenaar24959102022-05-07 20:01:16 +01002697 {(char_u *)"<S-Insert> \"*P", MODE_NORMAL},
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002698 {(char_u *)"<S-Insert> \"-d\"*P", VIS_SEL},
Bram Moolenaar24959102022-05-07 20:01:16 +01002699 {(char_u *)"<S-Insert> <C-R><C-O>*", MODE_INSERT | MODE_CMDLINE},
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002700 {(char_u *)"<C-Insert> \"*y", VIS_SEL},
2701 {(char_u *)"<S-Del> \"*d", VIS_SEL},
2702 {(char_u *)"<C-Del> \"*d", VIS_SEL},
2703 {(char_u *)"<C-X> \"*d", VIS_SEL},
2704 // Missing: CTRL-C (cancel) and CTRL-V (block selection)
2705};
2706# endif
2707
2708# if defined(MSWIN) && (!defined(FEAT_GUI) || defined(VIMDLL))
2709// Use the Windows (CUA) keybindings. (Console)
2710static struct initmap cinitmappings[] =
2711{
Bram Moolenaar24959102022-05-07 20:01:16 +01002712 {(char_u *)"\316w <C-Home>", MODE_NORMAL | VIS_SEL},
2713 {(char_u *)"\316w <C-Home>", MODE_INSERT | MODE_CMDLINE},
2714 {(char_u *)"\316u <C-End>", MODE_NORMAL | VIS_SEL},
2715 {(char_u *)"\316u <C-End>", MODE_INSERT | MODE_CMDLINE},
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002716
2717 // paste, copy and cut
2718# ifdef FEAT_CLIPBOARD
Bram Moolenaar24959102022-05-07 20:01:16 +01002719 {(char_u *)"\316\324 \"*P", MODE_NORMAL}, // SHIFT-Insert is "*P
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002720 {(char_u *)"\316\324 \"-d\"*P", VIS_SEL}, // SHIFT-Insert is "-d"*P
Bram Moolenaar24959102022-05-07 20:01:16 +01002721 {(char_u *)"\316\324 \022\017*", MODE_INSERT}, // SHIFT-Insert is ^R^O*
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002722 {(char_u *)"\316\325 \"*y", VIS_SEL}, // CTRL-Insert is "*y
2723 {(char_u *)"\316\327 \"*d", VIS_SEL}, // SHIFT-Del is "*d
2724 {(char_u *)"\316\330 \"*d", VIS_SEL}, // CTRL-Del is "*d
2725 {(char_u *)"\030 \"*d", VIS_SEL}, // CTRL-X is "*d
2726# else
Bram Moolenaar24959102022-05-07 20:01:16 +01002727 {(char_u *)"\316\324 P", MODE_NORMAL}, // SHIFT-Insert is P
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002728 {(char_u *)"\316\324 \"-dP", VIS_SEL}, // SHIFT-Insert is "-dP
Bram Moolenaar24959102022-05-07 20:01:16 +01002729 {(char_u *)"\316\324 \022\017\"", MODE_INSERT}, // SHIFT-Insert is ^R^O"
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002730 {(char_u *)"\316\325 y", VIS_SEL}, // CTRL-Insert is y
2731 {(char_u *)"\316\327 d", VIS_SEL}, // SHIFT-Del is d
2732 {(char_u *)"\316\330 d", VIS_SEL}, // CTRL-Del is d
2733# endif
2734};
2735# endif
2736
2737# if defined(MACOS_X)
2738static struct initmap initmappings[] =
2739{
2740 // Use the Standard MacOS binding.
2741 // paste, copy and cut
Bram Moolenaar24959102022-05-07 20:01:16 +01002742 {(char_u *)"<D-v> \"*P", MODE_NORMAL},
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002743 {(char_u *)"<D-v> \"-d\"*P", VIS_SEL},
Bram Moolenaar24959102022-05-07 20:01:16 +01002744 {(char_u *)"<D-v> <C-R>*", MODE_INSERT | MODE_CMDLINE},
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002745 {(char_u *)"<D-c> \"*y", VIS_SEL},
2746 {(char_u *)"<D-x> \"*d", VIS_SEL},
2747 {(char_u *)"<Backspace> \"-d", VIS_SEL},
2748};
2749# endif
2750
2751# undef VIS_SEL
2752#endif
2753
2754/*
2755 * Set up default mappings.
2756 */
2757 void
2758init_mappings(void)
2759{
2760#if defined(MSWIN) || defined(MACOS_X)
2761 int i;
2762
2763# if defined(MSWIN) && (!defined(FEAT_GUI_MSWIN) || defined(VIMDLL))
2764# ifdef VIMDLL
2765 if (!gui.starting)
2766# endif
2767 {
K.Takataeeec2542021-06-02 13:28:16 +02002768 for (i = 0; i < (int)ARRAY_LENGTH(cinitmappings); ++i)
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002769 add_map(cinitmappings[i].arg, cinitmappings[i].mode);
2770 }
2771# endif
2772# if defined(FEAT_GUI_MSWIN) || defined(MACOS_X)
K.Takataeeec2542021-06-02 13:28:16 +02002773 for (i = 0; i < (int)ARRAY_LENGTH(initmappings); ++i)
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002774 add_map(initmappings[i].arg, initmappings[i].mode);
2775# endif
2776#endif
2777}
2778
2779#if defined(MSWIN) || defined(FEAT_CMDWIN) || defined(MACOS_X) \
2780 || defined(PROTO)
2781/*
2782 * Add a mapping "map" for mode "mode".
2783 * Need to put string in allocated memory, because do_map() will modify it.
2784 */
2785 void
2786add_map(char_u *map, int mode)
2787{
2788 char_u *s;
2789 char_u *cpo_save = p_cpo;
2790
Bram Moolenaare5a2dc82021-01-03 19:52:05 +01002791 p_cpo = empty_option; // Allow <> notation
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002792 s = vim_strsave(map);
2793 if (s != NULL)
2794 {
2795 (void)do_map(0, s, mode, FALSE);
2796 vim_free(s);
2797 }
2798 p_cpo = cpo_save;
2799}
2800#endif
2801
Bram Moolenaare677df82019-09-02 22:31:11 +02002802#if defined(FEAT_LANGMAP) || defined(PROTO)
2803/*
2804 * Any character has an equivalent 'langmap' character. This is used for
2805 * keyboards that have a special language mode that sends characters above
2806 * 128 (although other characters can be translated too). The "to" field is a
2807 * Vim command character. This avoids having to switch the keyboard back to
2808 * ASCII mode when leaving Insert mode.
2809 *
2810 * langmap_mapchar[] maps any of 256 chars to an ASCII char used for Vim
2811 * commands.
2812 * langmap_mapga.ga_data is a sorted table of langmap_entry_T. This does the
2813 * same as langmap_mapchar[] for characters >= 256.
2814 *
2815 * Use growarray for 'langmap' chars >= 256
2816 */
2817typedef struct
2818{
2819 int from;
2820 int to;
2821} langmap_entry_T;
2822
2823static garray_T langmap_mapga;
2824
2825/*
2826 * Search for an entry in "langmap_mapga" for "from". If found set the "to"
2827 * field. If not found insert a new entry at the appropriate location.
2828 */
2829 static void
2830langmap_set_entry(int from, int to)
2831{
2832 langmap_entry_T *entries = (langmap_entry_T *)(langmap_mapga.ga_data);
2833 int a = 0;
2834 int b = langmap_mapga.ga_len;
2835
2836 // Do a binary search for an existing entry.
2837 while (a != b)
2838 {
2839 int i = (a + b) / 2;
2840 int d = entries[i].from - from;
2841
2842 if (d == 0)
2843 {
2844 entries[i].to = to;
2845 return;
2846 }
2847 if (d < 0)
2848 a = i + 1;
2849 else
2850 b = i;
2851 }
2852
2853 if (ga_grow(&langmap_mapga, 1) != OK)
2854 return; // out of memory
2855
2856 // insert new entry at position "a"
2857 entries = (langmap_entry_T *)(langmap_mapga.ga_data) + a;
2858 mch_memmove(entries + 1, entries,
2859 (langmap_mapga.ga_len - a) * sizeof(langmap_entry_T));
2860 ++langmap_mapga.ga_len;
2861 entries[0].from = from;
2862 entries[0].to = to;
2863}
2864
2865/*
2866 * Apply 'langmap' to multi-byte character "c" and return the result.
2867 */
2868 int
2869langmap_adjust_mb(int c)
2870{
2871 langmap_entry_T *entries = (langmap_entry_T *)(langmap_mapga.ga_data);
2872 int a = 0;
2873 int b = langmap_mapga.ga_len;
2874
2875 while (a != b)
2876 {
2877 int i = (a + b) / 2;
2878 int d = entries[i].from - c;
2879
2880 if (d == 0)
2881 return entries[i].to; // found matching entry
2882 if (d < 0)
2883 a = i + 1;
2884 else
2885 b = i;
2886 }
2887 return c; // no entry found, return "c" unmodified
2888}
2889
2890 void
2891langmap_init(void)
2892{
2893 int i;
2894
2895 for (i = 0; i < 256; i++)
2896 langmap_mapchar[i] = i; // we init with a one-to-one map
2897 ga_init2(&langmap_mapga, sizeof(langmap_entry_T), 8);
2898}
2899
2900/*
2901 * Called when langmap option is set; the language map can be
2902 * changed at any time!
2903 */
2904 void
2905langmap_set(void)
2906{
2907 char_u *p;
2908 char_u *p2;
2909 int from, to;
2910
2911 ga_clear(&langmap_mapga); // clear the previous map first
2912 langmap_init(); // back to one-to-one map
2913
2914 for (p = p_langmap; p[0] != NUL; )
2915 {
2916 for (p2 = p; p2[0] != NUL && p2[0] != ',' && p2[0] != ';';
2917 MB_PTR_ADV(p2))
2918 {
2919 if (p2[0] == '\\' && p2[1] != NUL)
2920 ++p2;
2921 }
2922 if (p2[0] == ';')
2923 ++p2; // abcd;ABCD form, p2 points to A
2924 else
2925 p2 = NULL; // aAbBcCdD form, p2 is NULL
2926 while (p[0])
2927 {
2928 if (p[0] == ',')
2929 {
2930 ++p;
2931 break;
2932 }
2933 if (p[0] == '\\' && p[1] != NUL)
2934 ++p;
2935 from = (*mb_ptr2char)(p);
2936 to = NUL;
2937 if (p2 == NULL)
2938 {
2939 MB_PTR_ADV(p);
2940 if (p[0] != ',')
2941 {
2942 if (p[0] == '\\')
2943 ++p;
2944 to = (*mb_ptr2char)(p);
2945 }
2946 }
2947 else
2948 {
2949 if (p2[0] != ',')
2950 {
2951 if (p2[0] == '\\')
2952 ++p2;
2953 to = (*mb_ptr2char)(p2);
2954 }
2955 }
2956 if (to == NUL)
2957 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00002958 semsg(_(e_langmap_matching_character_missing_for_str),
Bram Moolenaare677df82019-09-02 22:31:11 +02002959 transchar(from));
2960 return;
2961 }
2962
2963 if (from >= 256)
2964 langmap_set_entry(from, to);
2965 else
2966 langmap_mapchar[from & 255] = to;
2967
2968 // Advance to next pair
2969 MB_PTR_ADV(p);
2970 if (p2 != NULL)
2971 {
2972 MB_PTR_ADV(p2);
2973 if (*p == ';')
2974 {
2975 p = p2;
2976 if (p[0] != NUL)
2977 {
2978 if (p[0] != ',')
2979 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00002980 semsg(_(e_langmap_extra_characters_after_semicolon_str), p);
Bram Moolenaare677df82019-09-02 22:31:11 +02002981 return;
2982 }
2983 ++p;
2984 }
2985 break;
2986 }
2987 }
2988 }
2989 }
2990}
2991#endif
2992
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002993 static void
2994do_exmap(exarg_T *eap, int isabbrev)
2995{
2996 int mode;
2997 char_u *cmdp;
2998
2999 cmdp = eap->cmd;
3000 mode = get_map_mode(&cmdp, eap->forceit || isabbrev);
3001
3002 switch (do_map((*cmdp == 'n') ? 2 : (*cmdp == 'u'),
3003 eap->arg, mode, isabbrev))
3004 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00003005 case 1: emsg(_(e_invalid_argument));
Bram Moolenaarb66bab32019-08-01 14:28:24 +02003006 break;
Bram Moolenaare29a27f2021-07-20 21:07:36 +02003007 case 2: emsg((isabbrev ? _(e_no_such_abbreviation)
3008 : _(e_no_such_mapping)));
Bram Moolenaarb66bab32019-08-01 14:28:24 +02003009 break;
3010 }
3011}
3012
3013/*
3014 * ":abbreviate" and friends.
3015 */
3016 void
3017ex_abbreviate(exarg_T *eap)
3018{
3019 do_exmap(eap, TRUE); // almost the same as mapping
3020}
3021
3022/*
3023 * ":map" and friends.
3024 */
3025 void
3026ex_map(exarg_T *eap)
3027{
3028 // If we are sourcing .exrc or .vimrc in current directory we
3029 // print the mappings for security reasons.
3030 if (secure)
3031 {
3032 secure = 2;
3033 msg_outtrans(eap->cmd);
3034 msg_putchar('\n');
3035 }
3036 do_exmap(eap, FALSE);
3037}
3038
3039/*
3040 * ":unmap" and friends.
3041 */
3042 void
3043ex_unmap(exarg_T *eap)
3044{
3045 do_exmap(eap, FALSE);
3046}
3047
3048/*
3049 * ":mapclear" and friends.
3050 */
3051 void
3052ex_mapclear(exarg_T *eap)
3053{
3054 map_clear(eap->cmd, eap->arg, eap->forceit, FALSE);
3055}
3056
3057/*
3058 * ":abclear" and friends.
3059 */
3060 void
3061ex_abclear(exarg_T *eap)
3062{
3063 map_clear(eap->cmd, eap->arg, TRUE, TRUE);
3064}