blob: 2d378b55c21a03834a24a73c4b2e383b588fa554 [file] [log] [blame]
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001/* vi:set ts=8 sts=4 sw=4 noet:
2 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
10/*
zeertzjqa3f83fe2021-11-22 12:47:39 +000011 * map.c: Code for mappings and abbreviations.
Bram Moolenaarb66bab32019-08-01 14:28:24 +020012 */
13
14#include "vim.h"
15
16/*
17 * List used for abbreviations.
18 */
19static mapblock_T *first_abbr = NULL; // first entry in abbrlist
20
21/*
22 * Each mapping is put in one of the 256 hash lists, to speed up finding it.
23 */
24static mapblock_T *(maphash[256]);
25static int maphash_valid = FALSE;
26
Bram Moolenaarbf533e42022-11-13 20:43:19 +000027// When non-zero then no mappings can be added or removed. Prevents mappings
28// to change while listing them.
29static int map_locked = 0;
30
Bram Moolenaarb66bab32019-08-01 14:28:24 +020031/*
32 * Make a hash value for a mapping.
33 * "mode" is the lower 4 bits of the State for the mapping.
34 * "c1" is the first character of the "lhs".
35 * Returns a value between 0 and 255, index in maphash.
36 * Put Normal/Visual mode mappings mostly separately from Insert/Cmdline mode.
37 */
Bram Moolenaar24959102022-05-07 20:01:16 +010038#define MAP_HASH(mode, c1) (((mode) & (MODE_NORMAL | MODE_VISUAL | MODE_SELECT | MODE_OP_PENDING | MODE_TERMINAL)) ? (c1) : ((c1) ^ 0x80))
Bram Moolenaarb66bab32019-08-01 14:28:24 +020039
40/*
41 * Get the start of the hashed map list for "state" and first character "c".
42 */
43 mapblock_T *
44get_maphash_list(int state, int c)
45{
46 return maphash[MAP_HASH(state, c)];
47}
48
49/*
50 * Get the buffer-local hashed map list for "state" and first character "c".
51 */
52 mapblock_T *
53get_buf_maphash_list(int state, int c)
54{
55 return curbuf->b_maphash[MAP_HASH(state, c)];
56}
57
58 int
59is_maphash_valid(void)
60{
61 return maphash_valid;
62}
63
64/*
65 * Initialize maphash[] for first use.
66 */
67 static void
68validate_maphash(void)
69{
Yegappan Lakshmanane8575982023-01-14 12:32:28 +000070 if (maphash_valid)
71 return;
72
73 CLEAR_FIELD(maphash);
74 maphash_valid = TRUE;
Bram Moolenaarb66bab32019-08-01 14:28:24 +020075}
76
77/*
78 * Delete one entry from the abbrlist or maphash[].
79 * "mpp" is a pointer to the m_next field of the PREVIOUS entry!
80 */
81 static void
82map_free(mapblock_T **mpp)
83{
84 mapblock_T *mp;
85
86 mp = *mpp;
87 vim_free(mp->m_keys);
zeertzjq9d997ad2024-07-29 21:10:07 +020088 if (mp->m_alt != NULL)
89 mp->m_alt->m_alt = NULL;
Bram Moolenaarb66bab32019-08-01 14:28:24 +020090 vim_free(mp->m_str);
91 vim_free(mp->m_orig_str);
92 *mpp = mp->m_next;
Bram Moolenaard648c012022-01-16 14:58:34 +000093#ifdef FEAT_EVAL
Bram Moolenaarf61c89d2022-01-19 22:51:48 +000094 reset_last_used_map(mp);
Bram Moolenaard648c012022-01-16 14:58:34 +000095#endif
Bram Moolenaar8aa0e6c2022-01-20 11:27:58 +000096 vim_free(mp);
Bram Moolenaarb66bab32019-08-01 14:28:24 +020097}
98
99/*
100 * Return characters to represent the map mode in an allocated string.
101 * Returns NULL when out of memory.
102 */
103 static char_u *
104map_mode_to_chars(int mode)
105{
106 garray_T mapmode;
107
108 ga_init2(&mapmode, 1, 7);
109
Bram Moolenaar24959102022-05-07 20:01:16 +0100110 if ((mode & (MODE_INSERT | MODE_CMDLINE)) == (MODE_INSERT | MODE_CMDLINE))
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200111 ga_append(&mapmode, '!'); // :map!
Bram Moolenaar24959102022-05-07 20:01:16 +0100112 else if (mode & MODE_INSERT)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200113 ga_append(&mapmode, 'i'); // :imap
Bram Moolenaar24959102022-05-07 20:01:16 +0100114 else if (mode & MODE_LANGMAP)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200115 ga_append(&mapmode, 'l'); // :lmap
Bram Moolenaar24959102022-05-07 20:01:16 +0100116 else if (mode & MODE_CMDLINE)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200117 ga_append(&mapmode, 'c'); // :cmap
Bram Moolenaar24959102022-05-07 20:01:16 +0100118 else if ((mode
119 & (MODE_NORMAL | MODE_VISUAL | MODE_SELECT | MODE_OP_PENDING))
120 == (MODE_NORMAL | MODE_VISUAL | MODE_SELECT | MODE_OP_PENDING))
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200121 ga_append(&mapmode, ' '); // :map
122 else
123 {
Bram Moolenaar24959102022-05-07 20:01:16 +0100124 if (mode & MODE_NORMAL)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200125 ga_append(&mapmode, 'n'); // :nmap
Bram Moolenaar24959102022-05-07 20:01:16 +0100126 if (mode & MODE_OP_PENDING)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200127 ga_append(&mapmode, 'o'); // :omap
Bram Moolenaar24959102022-05-07 20:01:16 +0100128 if (mode & MODE_TERMINAL)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200129 ga_append(&mapmode, 't'); // :tmap
Bram Moolenaar24959102022-05-07 20:01:16 +0100130 if ((mode & (MODE_VISUAL | MODE_SELECT)) == (MODE_VISUAL | MODE_SELECT))
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200131 ga_append(&mapmode, 'v'); // :vmap
132 else
133 {
Bram Moolenaar24959102022-05-07 20:01:16 +0100134 if (mode & MODE_VISUAL)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200135 ga_append(&mapmode, 'x'); // :xmap
Bram Moolenaar24959102022-05-07 20:01:16 +0100136 if (mode & MODE_SELECT)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200137 ga_append(&mapmode, 's'); // :smap
138 }
139 }
140
141 ga_append(&mapmode, NUL);
142 return (char_u *)mapmode.ga_data;
143}
144
Bram Moolenaar9f62ea02022-10-19 13:07:03 +0100145/*
146 * Output a line for one mapping.
147 */
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200148 static void
149showmap(
150 mapblock_T *mp,
151 int local) // TRUE for buffer-local map
152{
153 int len = 1;
154 char_u *mapchars;
155
156 if (message_filtered(mp->m_keys) && message_filtered(mp->m_str))
157 return;
158
Bram Moolenaarbf533e42022-11-13 20:43:19 +0000159 // Prevent mappings to be cleared while at the more prompt.
160 // Must jump to "theend" instead of returning.
161 ++map_locked;
162
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200163 if (msg_didout || msg_silent != 0)
164 {
165 msg_putchar('\n');
166 if (got_int) // 'q' typed at MORE prompt
Bram Moolenaarbf533e42022-11-13 20:43:19 +0000167 goto theend;
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200168 }
169
170 mapchars = map_mode_to_chars(mp->m_mode);
171 if (mapchars != NULL)
172 {
173 msg_puts((char *)mapchars);
174 len = (int)STRLEN(mapchars);
175 vim_free(mapchars);
176 }
177
178 while (++len <= 3)
179 msg_putchar(' ');
180
181 // Display the LHS. Get length of what we write.
182 len = msg_outtrans_special(mp->m_keys, TRUE, 0);
183 do
184 {
zeertzjq997b8a02023-02-19 21:00:31 +0000185 msg_putchar(' '); // pad with blanks
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200186 ++len;
187 } while (len < 12);
188
189 if (mp->m_noremap == REMAP_NONE)
190 msg_puts_attr("*", HL_ATTR(HLF_8));
191 else if (mp->m_noremap == REMAP_SCRIPT)
192 msg_puts_attr("&", HL_ATTR(HLF_8));
193 else
194 msg_putchar(' ');
195
196 if (local)
197 msg_putchar('@');
198 else
199 msg_putchar(' ');
200
201 // Use FALSE below if we only want things like <Up> to show up as such on
202 // the rhs, and not M-x etc, TRUE gets both -- webb
203 if (*mp->m_str == NUL)
204 msg_puts_attr("<Nop>", HL_ATTR(HLF_8));
205 else
zeertzjqac402f42022-05-04 18:51:43 +0100206 msg_outtrans_special(mp->m_str, FALSE, 0);
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200207#ifdef FEAT_EVAL
208 if (p_verbose > 0)
209 last_set_msg(mp->m_script_ctx);
210#endif
Bram Moolenaard288eaa2022-02-16 18:27:55 +0000211 msg_clr_eos();
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200212 out_flush(); // show one line at a time
Bram Moolenaarbf533e42022-11-13 20:43:19 +0000213
214theend:
215 --map_locked;
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200216}
217
zeertzjq9d997ad2024-07-29 21:10:07 +0200218 static mapblock_T *
Bram Moolenaar4c9243f2020-05-22 13:10:44 +0200219map_add(
220 mapblock_T **map_table,
221 mapblock_T **abbr_table,
222 char_u *keys,
223 char_u *rhs,
224 char_u *orig_rhs,
Bram Moolenaar4c9243f2020-05-22 13:10:44 +0200225 int noremap,
226 int nowait,
227 int silent,
228 int mode,
229 int is_abbr,
230#ifdef FEAT_EVAL
Bram Moolenaar5a80f8a2020-05-22 13:38:18 +0200231 int expr,
zeertzjqbfc7cbd2023-04-07 22:09:46 +0100232 scid_T sid, // 0 to use current_sctx
Bram Moolenaara9528b32022-01-18 20:51:35 +0000233 int scriptversion,
Bram Moolenaar4c9243f2020-05-22 13:10:44 +0200234 linenr_T lnum,
235#endif
236 int simplified)
237{
Bram Moolenaar94075b22022-01-18 20:30:39 +0000238 mapblock_T *mp = ALLOC_CLEAR_ONE(mapblock_T);
Bram Moolenaar4c9243f2020-05-22 13:10:44 +0200239
240 if (mp == NULL)
zeertzjq9d997ad2024-07-29 21:10:07 +0200241 return NULL;
Bram Moolenaar4c9243f2020-05-22 13:10:44 +0200242
243 // If CTRL-C has been mapped, don't always use it for Interrupting.
244 if (*keys == Ctrl_C)
245 {
246 if (map_table == curbuf->b_maphash)
247 curbuf->b_mapped_ctrl_c |= mode;
248 else
249 mapped_ctrl_c |= mode;
250 }
251
252 mp->m_keys = vim_strsave(keys);
253 mp->m_str = vim_strsave(rhs);
254 mp->m_orig_str = vim_strsave(orig_rhs);
255 if (mp->m_keys == NULL || mp->m_str == NULL)
256 {
257 vim_free(mp->m_keys);
258 vim_free(mp->m_str);
259 vim_free(mp->m_orig_str);
260 vim_free(mp);
zeertzjq9d997ad2024-07-29 21:10:07 +0200261 return NULL;
Bram Moolenaar4c9243f2020-05-22 13:10:44 +0200262 }
263 mp->m_keylen = (int)STRLEN(mp->m_keys);
264 mp->m_noremap = noremap;
265 mp->m_nowait = nowait;
266 mp->m_silent = silent;
267 mp->m_mode = mode;
268 mp->m_simplified = simplified;
269#ifdef FEAT_EVAL
270 mp->m_expr = expr;
zeertzjqbfc7cbd2023-04-07 22:09:46 +0100271 if (sid != 0)
Bram Moolenaar4c9243f2020-05-22 13:10:44 +0200272 {
273 mp->m_script_ctx.sc_sid = sid;
274 mp->m_script_ctx.sc_lnum = lnum;
Bram Moolenaara9528b32022-01-18 20:51:35 +0000275 mp->m_script_ctx.sc_version = scriptversion;
Bram Moolenaar4c9243f2020-05-22 13:10:44 +0200276 }
277 else
278 {
279 mp->m_script_ctx = current_sctx;
280 mp->m_script_ctx.sc_lnum += SOURCING_LNUM;
281 }
282#endif
283
284 // add the new entry in front of the abbrlist or maphash[] list
285 if (is_abbr)
286 {
287 mp->m_next = *abbr_table;
288 *abbr_table = mp;
289 }
290 else
291 {
292 int n = MAP_HASH(mp->m_mode, mp->m_keys[0]);
293
294 mp->m_next = map_table[n];
295 map_table[n] = mp;
296 }
zeertzjq9d997ad2024-07-29 21:10:07 +0200297 return mp;
Bram Moolenaar4c9243f2020-05-22 13:10:44 +0200298}
299
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200300/*
Bram Moolenaar9f62ea02022-10-19 13:07:03 +0100301 * List mappings. When "haskey" is FALSE all mappings, otherwise mappings that
302 * match "keys[keys_len]".
303 */
304 static void
305list_mappings(
306 int keyround,
307 int abbrev,
308 int haskey,
309 char_u *keys,
310 int keys_len,
311 int mode,
312 int *did_local)
313{
Bram Moolenaarbf533e42022-11-13 20:43:19 +0000314 // Prevent mappings to be cleared while at the more prompt.
315 ++map_locked;
316
Bram Moolenaar63a2e362022-11-23 20:20:18 +0000317 if (p_verbose > 0 && keyround == 1)
318 {
319 if (seenModifyOtherKeys)
Bram Moolenaarc255b782022-11-26 19:16:48 +0000320 msg_puts(_("Seen modifyOtherKeys: true\n"));
321
322 if (modify_otherkeys_state != MOKS_INITIAL)
323 {
324 char *name = _("Unknown");
325 switch (modify_otherkeys_state)
326 {
327 case MOKS_INITIAL: break;
328 case MOKS_OFF: name = _("Off"); break;
329 case MOKS_ENABLED: name = _("On"); break;
330 case MOKS_DISABLED: name = _("Disabled"); break;
Bram Moolenaar9d1184c2022-12-16 18:33:20 +0000331 case MOKS_AFTER_T_TE: name = _("Cleared"); break;
Bram Moolenaarc255b782022-11-26 19:16:48 +0000332 }
333
334 char buf[200];
335 vim_snprintf(buf, sizeof(buf),
336 _("modifyOtherKeys detected: %s\n"), name);
337 msg_puts(buf);
338 }
339
Bram Moolenaar63a2e362022-11-23 20:20:18 +0000340 if (kitty_protocol_state != KKPS_INITIAL)
341 {
342 char *name = _("Unknown");
343 switch (kitty_protocol_state)
344 {
345 case KKPS_INITIAL: break;
346 case KKPS_OFF: name = _("Off"); break;
347 case KKPS_ENABLED: name = _("On"); break;
348 case KKPS_DISABLED: name = _("Disabled"); break;
Bram Moolenaar9d1184c2022-12-16 18:33:20 +0000349 case KKPS_AFTER_T_TE: name = _("Cleared"); break;
Bram Moolenaar63a2e362022-11-23 20:20:18 +0000350 }
351
352 char buf[200];
Bram Moolenaarc255b782022-11-26 19:16:48 +0000353 vim_snprintf(buf, sizeof(buf),
354 _("Kitty keyboard protocol: %s\n"), name);
Bram Moolenaar63a2e362022-11-23 20:20:18 +0000355 msg_puts(buf);
356 }
357 }
Bram Moolenaar9f62ea02022-10-19 13:07:03 +0100358
359 // need to loop over all global hash lists
360 for (int hash = 0; hash < 256 && !got_int; ++hash)
361 {
362 mapblock_T *mp;
363
364 if (abbrev)
365 {
366 if (hash != 0) // there is only one abbreviation list
367 break;
368 mp = curbuf->b_first_abbr;
369 }
370 else
371 mp = curbuf->b_maphash[hash];
372 for ( ; mp != NULL && !got_int; mp = mp->m_next)
373 {
374 // check entries with the same mode
375 if (!mp->m_simplified && (mp->m_mode & mode) != 0)
376 {
377 if (!haskey) // show all entries
378 {
379 showmap(mp, TRUE);
380 *did_local = TRUE;
381 }
382 else
383 {
384 int n = mp->m_keylen;
385 if (STRNCMP(mp->m_keys, keys,
386 (size_t)(n < keys_len ? n : keys_len)) == 0)
387 {
388 showmap(mp, TRUE);
389 *did_local = TRUE;
390 }
391 }
392 }
393 }
394 }
Bram Moolenaarbf533e42022-11-13 20:43:19 +0000395
396 --map_locked;
Bram Moolenaar9f62ea02022-10-19 13:07:03 +0100397}
398
399/*
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200400 * map[!] : show all key mappings
401 * map[!] {lhs} : show key mapping for {lhs}
402 * map[!] {lhs} {rhs} : set key mapping for {lhs} to {rhs}
403 * noremap[!] {lhs} {rhs} : same, but no remapping for {rhs}
404 * unmap[!] {lhs} : remove key mapping for {lhs}
405 * abbr : show all abbreviations
406 * abbr {lhs} : show abbreviations for {lhs}
407 * abbr {lhs} {rhs} : set abbreviation for {lhs} to {rhs}
408 * noreabbr {lhs} {rhs} : same, but no remapping for {rhs}
409 * unabbr {lhs} : remove abbreviation for {lhs}
410 *
zeertzjqfdf135a2024-10-24 21:43:43 +0200411 * maptype: MAPTYPE_MAP for :map or :abbr
412 * MAPTYPE_UNMAP for :unmap or :unabbr
413 * MAPTYPE_NOREMAP for :noremap or :noreabbr
414 * MAPTYPE_UNMAP_LHS is like MAPTYPE_UNMAP, but doesn't try to match
415 * with {rhs} if there is no match with {lhs}.
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200416 *
417 * arg is pointer to any arguments. Note: arg cannot be a read-only string,
418 * it will be modified.
419 *
Bram Moolenaar24959102022-05-07 20:01:16 +0100420 * for :map mode is MODE_NORMAL | MODE_VISUAL | MODE_SELECT | MODE_OP_PENDING
421 * for :map! mode is MODE_INSERT | MODE_CMDLINE
422 * for :cmap mode is MODE_CMDLINE
423 * for :imap mode is MODE_INSERT
424 * for :lmap mode is MODE_LANGMAP
425 * for :nmap mode is MODE_NORMAL
426 * for :vmap mode is MODE_VISUAL | MODE_SELECT
427 * for :xmap mode is MODE_VISUAL
428 * for :smap mode is MODE_SELECT
429 * for :omap mode is MODE_OP_PENDING
430 * for :tmap mode is MODE_TERMINAL
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200431 *
Bram Moolenaar24959102022-05-07 20:01:16 +0100432 * for :abbr mode is MODE_INSERT | MODE_CMDLINE
433 * for :iabbr mode is MODE_INSERT
434 * for :cabbr mode is MODE_CMDLINE
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200435 *
436 * Return 0 for success
437 * 1 for invalid arguments
438 * 2 for no match
439 * 4 for out of mem
440 * 5 for entry not unique
441 */
442 int
443do_map(
444 int maptype,
445 char_u *arg,
446 int mode,
447 int abbrev) // not a mapping but an abbreviation
448{
449 char_u *keys;
450 mapblock_T *mp, **mpp;
zeertzjq9d997ad2024-07-29 21:10:07 +0200451 mapblock_T *mp_result[2] = {NULL, NULL};
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200452 char_u *rhs;
453 char_u *p;
454 int n;
455 int len = 0; // init for GCC
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200456 int hasarg;
457 int haskey;
Bram Moolenaar459fd782019-10-13 16:43:39 +0200458 int do_print;
459 int keyround;
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200460 char_u *keys_buf = NULL;
Bram Moolenaar459fd782019-10-13 16:43:39 +0200461 char_u *alt_keys_buf = NULL;
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200462 char_u *arg_buf = NULL;
463 int retval = 0;
464 int do_backslash;
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200465 mapblock_T **abbr_table;
466 mapblock_T **map_table;
467 int unique = FALSE;
468 int nowait = FALSE;
469 int silent = FALSE;
470 int special = FALSE;
471#ifdef FEAT_EVAL
472 int expr = FALSE;
473#endif
Bram Moolenaar459fd782019-10-13 16:43:39 +0200474 int did_simplify = FALSE;
zeertzjqfdf135a2024-10-24 21:43:43 +0200475 int unmap_lhs_only = FALSE;
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200476 int noremap;
477 char_u *orig_rhs;
478
479 keys = arg;
480 map_table = maphash;
481 abbr_table = &first_abbr;
482
zeertzjqfdf135a2024-10-24 21:43:43 +0200483 if (maptype == MAPTYPE_UNMAP_LHS)
484 {
485 unmap_lhs_only = TRUE;
486 maptype = MAPTYPE_UNMAP;
487 }
488
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200489 // For ":noremap" don't remap, otherwise do remap.
zeertzjq44068e92022-06-16 11:14:55 +0100490 if (maptype == MAPTYPE_NOREMAP)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200491 noremap = REMAP_NONE;
492 else
493 noremap = REMAP_YES;
494
495 // Accept <buffer>, <nowait>, <silent>, <expr> <script> and <unique> in
496 // any order.
497 for (;;)
498 {
499 // Check for "<buffer>": mapping local to buffer.
500 if (STRNCMP(keys, "<buffer>", 8) == 0)
501 {
502 keys = skipwhite(keys + 8);
503 map_table = curbuf->b_maphash;
504 abbr_table = &curbuf->b_first_abbr;
505 continue;
506 }
507
508 // Check for "<nowait>": don't wait for more characters.
509 if (STRNCMP(keys, "<nowait>", 8) == 0)
510 {
511 keys = skipwhite(keys + 8);
512 nowait = TRUE;
513 continue;
514 }
515
516 // Check for "<silent>": don't echo commands.
517 if (STRNCMP(keys, "<silent>", 8) == 0)
518 {
519 keys = skipwhite(keys + 8);
520 silent = TRUE;
521 continue;
522 }
523
524 // Check for "<special>": accept special keys in <>
525 if (STRNCMP(keys, "<special>", 9) == 0)
526 {
527 keys = skipwhite(keys + 9);
528 special = TRUE;
529 continue;
530 }
531
532#ifdef FEAT_EVAL
533 // Check for "<script>": remap script-local mappings only
534 if (STRNCMP(keys, "<script>", 8) == 0)
535 {
536 keys = skipwhite(keys + 8);
537 noremap = REMAP_SCRIPT;
538 continue;
539 }
540
541 // Check for "<expr>": {rhs} is an expression.
542 if (STRNCMP(keys, "<expr>", 6) == 0)
543 {
544 keys = skipwhite(keys + 6);
545 expr = TRUE;
546 continue;
547 }
548#endif
549 // Check for "<unique>": don't overwrite an existing mapping.
550 if (STRNCMP(keys, "<unique>", 8) == 0)
551 {
552 keys = skipwhite(keys + 8);
553 unique = TRUE;
554 continue;
555 }
556 break;
557 }
558
559 validate_maphash();
560
561 // Find end of keys and skip CTRL-Vs (and backslashes) in it.
562 // Accept backslash like CTRL-V when 'cpoptions' does not contain 'B'.
563 // with :unmap white space is included in the keys, no argument possible.
564 p = keys;
565 do_backslash = (vim_strchr(p_cpo, CPO_BSLASH) == NULL);
zeertzjq44068e92022-06-16 11:14:55 +0100566 while (*p && (maptype == MAPTYPE_UNMAP || !VIM_ISWHITE(*p)))
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200567 {
568 if ((p[0] == Ctrl_V || (do_backslash && p[0] == '\\')) &&
569 p[1] != NUL)
570 ++p; // skip CTRL-V or backslash
571 ++p;
572 }
573 if (*p != NUL)
574 *p++ = NUL;
575
576 p = skipwhite(p);
577 rhs = p;
578 hasarg = (*rhs != NUL);
579 haskey = (*keys != NUL);
zeertzjq44068e92022-06-16 11:14:55 +0100580 do_print = !haskey || (maptype != MAPTYPE_UNMAP && !hasarg);
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200581
582 // check for :unmap without argument
zeertzjq44068e92022-06-16 11:14:55 +0100583 if (maptype == MAPTYPE_UNMAP && !haskey)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200584 {
585 retval = 1;
586 goto theend;
587 }
588
589 // If mapping has been given as ^V<C_UP> say, then replace the term codes
590 // with the appropriate two bytes. If it is a shifted special key, unshift
591 // it too, giving another two bytes.
592 // replace_termcodes() may move the result to allocated memory, which
593 // needs to be freed later (*keys_buf and *arg_buf).
594 // replace_termcodes() also removes CTRL-Vs and sometimes backslashes.
Bram Moolenaar459fd782019-10-13 16:43:39 +0200595 // If something like <C-H> is simplified to 0x08 then mark it as simplified
Yegappan Lakshmanane8575982023-01-14 12:32:28 +0000596 // and also add an entry with a modifier, which will work when using a key
597 // protocol.
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200598 if (haskey)
Bram Moolenaar459fd782019-10-13 16:43:39 +0200599 {
600 char_u *new_keys;
601 int flags = REPTERM_FROM_PART | REPTERM_DO_LT;
602
603 if (special)
604 flags |= REPTERM_SPECIAL;
zeertzjq7e0bae02023-08-11 23:15:38 +0200605 new_keys = replace_termcodes(keys, &keys_buf, 0, flags, &did_simplify);
Bram Moolenaar459fd782019-10-13 16:43:39 +0200606 if (did_simplify)
zeertzjq7e0bae02023-08-11 23:15:38 +0200607 (void)replace_termcodes(keys, &alt_keys_buf, 0,
Bram Moolenaar459fd782019-10-13 16:43:39 +0200608 flags | REPTERM_NO_SIMPLIFY, NULL);
609 keys = new_keys;
610 }
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200611 orig_rhs = rhs;
612 if (hasarg)
613 {
614 if (STRICMP(rhs, "<nop>") == 0) // "<Nop>" means nothing
615 rhs = (char_u *)"";
616 else
zeertzjq7e0bae02023-08-11 23:15:38 +0200617 rhs = replace_termcodes(rhs, &arg_buf, 0,
Bram Moolenaar459fd782019-10-13 16:43:39 +0200618 REPTERM_DO_LT | (special ? REPTERM_SPECIAL : 0), NULL);
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200619 }
620
Bram Moolenaar459fd782019-10-13 16:43:39 +0200621 /*
622 * The following is done twice if we have two versions of keys:
623 * "alt_keys_buf" is not NULL.
624 */
625 for (keyround = 1; keyround <= 2; ++keyround)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200626 {
Bram Moolenaar459fd782019-10-13 16:43:39 +0200627 int did_it = FALSE;
628 int did_local = FALSE;
Bram Moolenaar87f74102022-04-25 18:59:25 +0100629 int keyround1_simplified = keyround == 1 && did_simplify;
Bram Moolenaar459fd782019-10-13 16:43:39 +0200630 int round;
zeertzjqfdf135a2024-10-24 21:43:43 +0200631 int num_rounds;
Bram Moolenaar459fd782019-10-13 16:43:39 +0200632
633 if (keyround == 2)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200634 {
Bram Moolenaar459fd782019-10-13 16:43:39 +0200635 if (alt_keys_buf == NULL)
636 break;
637 keys = alt_keys_buf;
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200638 }
Bram Moolenaar459fd782019-10-13 16:43:39 +0200639 else if (alt_keys_buf != NULL && do_print)
640 // when printing always use the not-simplified map
641 keys = alt_keys_buf;
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200642
Bram Moolenaar459fd782019-10-13 16:43:39 +0200643 // check arguments and translate function keys
644 if (haskey)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200645 {
Bram Moolenaar459fd782019-10-13 16:43:39 +0200646 len = (int)STRLEN(keys);
647 if (len > MAXMAPLEN) // maximum length of MAXMAPLEN chars
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200648 {
Bram Moolenaar459fd782019-10-13 16:43:39 +0200649 retval = 1;
650 goto theend;
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200651 }
Bram Moolenaar459fd782019-10-13 16:43:39 +0200652
zeertzjq44068e92022-06-16 11:14:55 +0100653 if (abbrev && maptype != MAPTYPE_UNMAP)
Bram Moolenaar459fd782019-10-13 16:43:39 +0200654 {
655 // If an abbreviation ends in a keyword character, the
656 // rest must be all keyword-char or all non-keyword-char.
657 // Otherwise we won't be able to find the start of it in a
658 // vi-compatible way.
659 if (has_mbyte)
660 {
661 int first, last;
662 int same = -1;
663
664 first = vim_iswordp(keys);
665 last = first;
666 p = keys + (*mb_ptr2len)(keys);
667 n = 1;
668 while (p < keys + len)
669 {
670 ++n; // nr of (multi-byte) chars
671 last = vim_iswordp(p); // type of last char
672 if (same == -1 && last != first)
673 same = n - 1; // count of same char type
674 p += (*mb_ptr2len)(p);
675 }
676 if (last && n > 2 && same >= 0 && same < n - 1)
677 {
678 retval = 1;
679 goto theend;
680 }
681 }
682 else if (vim_iswordc(keys[len - 1]))
683 // ends in keyword char
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200684 for (n = 0; n < len - 2; ++n)
685 if (vim_iswordc(keys[n]) != vim_iswordc(keys[len - 2]))
686 {
687 retval = 1;
688 goto theend;
689 }
Bram Moolenaar459fd782019-10-13 16:43:39 +0200690 // An abbreviation cannot contain white space.
691 for (n = 0; n < len; ++n)
692 if (VIM_ISWHITE(keys[n]))
693 {
694 retval = 1;
695 goto theend;
696 }
697 }
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200698 }
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200699
Bram Moolenaar459fd782019-10-13 16:43:39 +0200700 if (haskey && hasarg && abbrev) // if we will add an abbreviation
701 no_abbr = FALSE; // reset flag that indicates there are
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200702 // no abbreviations
703
Bram Moolenaar459fd782019-10-13 16:43:39 +0200704 if (do_print)
705 msg_start();
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200706
Bram Moolenaar459fd782019-10-13 16:43:39 +0200707 // Check if a new local mapping wasn't already defined globally.
Bram Moolenaar4c9243f2020-05-22 13:10:44 +0200708 if (unique && map_table == curbuf->b_maphash
zeertzjq44068e92022-06-16 11:14:55 +0100709 && haskey && hasarg && maptype != MAPTYPE_UNMAP)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200710 {
Bram Moolenaar459fd782019-10-13 16:43:39 +0200711 // need to loop over all global hash lists
Bram Moolenaar9f62ea02022-10-19 13:07:03 +0100712 for (int hash = 0; hash < 256 && !got_int; ++hash)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200713 {
Bram Moolenaar459fd782019-10-13 16:43:39 +0200714 if (abbrev)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200715 {
Bram Moolenaar459fd782019-10-13 16:43:39 +0200716 if (hash != 0) // there is only one abbreviation list
717 break;
718 mp = first_abbr;
719 }
720 else
721 mp = maphash[hash];
722 for ( ; mp != NULL && !got_int; mp = mp->m_next)
723 {
724 // check entries with the same mode
725 if ((mp->m_mode & mode) != 0
726 && mp->m_keylen == len
Bram Moolenaar459fd782019-10-13 16:43:39 +0200727 && STRNCMP(mp->m_keys, keys, (size_t)len) == 0)
728 {
729 if (abbrev)
Bram Moolenaar6d057012021-12-31 18:49:43 +0000730 semsg(
731 _(e_global_abbreviation_already_exists_for_str),
Bram Moolenaar459fd782019-10-13 16:43:39 +0200732 mp->m_keys);
733 else
Bram Moolenaar6d057012021-12-31 18:49:43 +0000734 semsg(_(e_global_mapping_already_exists_for_str),
Bram Moolenaar459fd782019-10-13 16:43:39 +0200735 mp->m_keys);
736 retval = 5;
737 goto theend;
738 }
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200739 }
740 }
741 }
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200742
Bram Moolenaar459fd782019-10-13 16:43:39 +0200743 // When listing global mappings, also list buffer-local ones here.
zeertzjq44068e92022-06-16 11:14:55 +0100744 if (map_table != curbuf->b_maphash && !hasarg
745 && maptype != MAPTYPE_UNMAP)
Bram Moolenaar9f62ea02022-10-19 13:07:03 +0100746 list_mappings(keyround, abbrev, haskey, keys, len,
747 mode, &did_local);
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200748
Bram Moolenaar459fd782019-10-13 16:43:39 +0200749 // Find an entry in the maphash[] list that matches.
750 // For :unmap we may loop two times: once to try to unmap an entry with
751 // a matching 'from' part, a second time, if the first fails, to unmap
zeertzjqa3f83fe2021-11-22 12:47:39 +0000752 // an entry with a matching 'to' part. This was done to allow
753 // ":ab foo bar" to be unmapped by typing ":unab foo", where "foo" will
754 // be replaced by "bar" because of the abbreviation.
zeertzjqfdf135a2024-10-24 21:43:43 +0200755 num_rounds = maptype == MAPTYPE_UNMAP && !unmap_lhs_only ? 2 : 1;
756 for (round = 0; round < num_rounds && !did_it && !got_int; ++round)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200757 {
Bram Moolenaar459fd782019-10-13 16:43:39 +0200758 // need to loop over all hash lists
Bram Moolenaar9f62ea02022-10-19 13:07:03 +0100759 for (int hash = 0; hash < 256 && !got_int; ++hash)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200760 {
Bram Moolenaar459fd782019-10-13 16:43:39 +0200761 if (abbrev)
762 {
763 if (hash > 0) // there is only one abbreviation list
764 break;
765 mpp = abbr_table;
766 }
767 else
768 mpp = &(map_table[hash]);
769 for (mp = *mpp; mp != NULL && !got_int; mp = *mpp)
770 {
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200771
Bram Moolenaarfafb4b12019-10-16 18:34:57 +0200772 if ((mp->m_mode & mode) == 0)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200773 {
Bram Moolenaarfafb4b12019-10-16 18:34:57 +0200774 // skip entries with wrong mode
Bram Moolenaar459fd782019-10-13 16:43:39 +0200775 mpp = &(mp->m_next);
776 continue;
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200777 }
Bram Moolenaar459fd782019-10-13 16:43:39 +0200778 if (!haskey) // show all entries
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200779 {
Bram Moolenaarfafb4b12019-10-16 18:34:57 +0200780 if (!mp->m_simplified)
781 {
782 showmap(mp, map_table != maphash);
783 did_it = TRUE;
784 }
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200785 }
Bram Moolenaar459fd782019-10-13 16:43:39 +0200786 else // do we have a match?
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200787 {
Bram Moolenaar459fd782019-10-13 16:43:39 +0200788 if (round) // second round: Try unmap "rhs" string
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200789 {
Bram Moolenaar459fd782019-10-13 16:43:39 +0200790 n = (int)STRLEN(mp->m_str);
791 p = mp->m_str;
792 }
793 else
794 {
795 n = mp->m_keylen;
796 p = mp->m_keys;
797 }
798 if (STRNCMP(p, keys, (size_t)(n < len ? n : len)) == 0)
799 {
zeertzjq44068e92022-06-16 11:14:55 +0100800 if (maptype == MAPTYPE_UNMAP)
Bram Moolenaar459fd782019-10-13 16:43:39 +0200801 {
802 // Delete entry.
803 // Only accept a full match. For abbreviations
804 // we ignore trailing space when matching with
805 // the "lhs", since an abbreviation can't have
806 // trailing space.
807 if (n != len && (!abbrev || round || n > len
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200808 || *skipwhite(keys + n) != NUL))
Bram Moolenaar459fd782019-10-13 16:43:39 +0200809 {
810 mpp = &(mp->m_next);
811 continue;
812 }
zeertzjqabeb09b2022-04-26 12:29:43 +0100813 // In keyround for simplified keys, don't unmap
814 // a mapping without m_simplified flag.
Bram Moolenaar87f74102022-04-25 18:59:25 +0100815 if (keyround1_simplified && !mp->m_simplified)
zeertzjqa4e33322022-04-24 17:07:53 +0100816 break;
Bram Moolenaar459fd782019-10-13 16:43:39 +0200817 // We reset the indicated mode bits. If nothing
818 // is left the entry is deleted below.
819 mp->m_mode &= ~mode;
820 did_it = TRUE; // remember we did something
821 }
822 else if (!hasarg) // show matching entry
823 {
Bram Moolenaarfafb4b12019-10-16 18:34:57 +0200824 if (!mp->m_simplified)
825 {
826 showmap(mp, map_table != maphash);
827 did_it = TRUE;
828 }
Bram Moolenaar459fd782019-10-13 16:43:39 +0200829 }
830 else if (n != len) // new entry is ambiguous
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200831 {
832 mpp = &(mp->m_next);
833 continue;
834 }
Bram Moolenaar459fd782019-10-13 16:43:39 +0200835 else if (unique)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200836 {
Bram Moolenaar459fd782019-10-13 16:43:39 +0200837 if (abbrev)
Bram Moolenaar6d057012021-12-31 18:49:43 +0000838 semsg(
839 _(e_abbreviation_already_exists_for_str),
Bram Moolenaar459fd782019-10-13 16:43:39 +0200840 p);
841 else
Bram Moolenaar6d057012021-12-31 18:49:43 +0000842 semsg(_(e_mapping_already_exists_for_str),
Bram Moolenaar459fd782019-10-13 16:43:39 +0200843 p);
844 retval = 5;
845 goto theend;
846 }
847 else
848 {
849 // new rhs for existing entry
850 mp->m_mode &= ~mode; // remove mode bits
851 if (mp->m_mode == 0 && !did_it) // reuse entry
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200852 {
Bram Moolenaar459fd782019-10-13 16:43:39 +0200853 char_u *newstr = vim_strsave(rhs);
854
855 if (newstr == NULL)
856 {
857 retval = 4; // no mem
858 goto theend;
859 }
zeertzjq9d997ad2024-07-29 21:10:07 +0200860 if (mp->m_alt != NULL)
861 mp->m_alt = mp->m_alt->m_alt = NULL;
Bram Moolenaar459fd782019-10-13 16:43:39 +0200862 vim_free(mp->m_str);
863 mp->m_str = newstr;
864 vim_free(mp->m_orig_str);
865 mp->m_orig_str = vim_strsave(orig_rhs);
866 mp->m_noremap = noremap;
867 mp->m_nowait = nowait;
868 mp->m_silent = silent;
869 mp->m_mode = mode;
Bram Moolenaar87f74102022-04-25 18:59:25 +0100870 mp->m_simplified = keyround1_simplified;
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200871#ifdef FEAT_EVAL
Bram Moolenaar459fd782019-10-13 16:43:39 +0200872 mp->m_expr = expr;
873 mp->m_script_ctx = current_sctx;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +0100874 mp->m_script_ctx.sc_lnum += SOURCING_LNUM;
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200875#endif
zeertzjq9d997ad2024-07-29 21:10:07 +0200876 mp_result[keyround - 1] = mp;
Bram Moolenaar459fd782019-10-13 16:43:39 +0200877 did_it = TRUE;
878 }
879 }
880 if (mp->m_mode == 0) // entry can be deleted
881 {
882 map_free(mpp);
883 continue; // continue with *mpp
884 }
885
886 // May need to put this entry into another hash
887 // list.
Bram Moolenaar9f62ea02022-10-19 13:07:03 +0100888 int new_hash = MAP_HASH(mp->m_mode, mp->m_keys[0]);
Bram Moolenaar459fd782019-10-13 16:43:39 +0200889 if (!abbrev && new_hash != hash)
890 {
891 *mpp = mp->m_next;
892 mp->m_next = map_table[new_hash];
893 map_table[new_hash] = mp;
894
895 continue; // continue with *mpp
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200896 }
897 }
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200898 }
Bram Moolenaar459fd782019-10-13 16:43:39 +0200899 mpp = &(mp->m_next);
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200900 }
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200901 }
902 }
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200903
zeertzjq44068e92022-06-16 11:14:55 +0100904 if (maptype == MAPTYPE_UNMAP)
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200905 {
Bram Moolenaar459fd782019-10-13 16:43:39 +0200906 // delete entry
907 if (!did_it)
zeertzjqa4e33322022-04-24 17:07:53 +0100908 {
Bram Moolenaar87f74102022-04-25 18:59:25 +0100909 if (!keyround1_simplified)
zeertzjqa4e33322022-04-24 17:07:53 +0100910 retval = 2; // no match
911 }
Bram Moolenaar459fd782019-10-13 16:43:39 +0200912 else if (*keys == Ctrl_C)
913 {
914 // If CTRL-C has been unmapped, reuse it for Interrupting.
915 if (map_table == curbuf->b_maphash)
916 curbuf->b_mapped_ctrl_c &= ~mode;
917 else
918 mapped_ctrl_c &= ~mode;
919 }
920 continue;
921 }
922
923 if (!haskey || !hasarg)
924 {
925 // print entries
926 if (!did_it && !did_local)
927 {
928 if (abbrev)
929 msg(_("No abbreviation found"));
930 else
931 msg(_("No mapping found"));
932 }
933 goto theend; // listing finished
934 }
935
936 if (did_it)
937 continue; // have added the new entry already
938
939 // Get here when adding a new entry to the maphash[] list or abbrlist.
zeertzjq9d997ad2024-07-29 21:10:07 +0200940 mp_result[keyround - 1] = map_add(map_table, abbr_table, keys,
941 rhs, orig_rhs, noremap, nowait, silent, mode, abbrev,
Bram Moolenaar4c9243f2020-05-22 13:10:44 +0200942#ifdef FEAT_EVAL
zeertzjqbfc7cbd2023-04-07 22:09:46 +0100943 expr, /* sid */ 0, /* scriptversion */ 0, /* lnum */ 0,
Bram Moolenaar4c9243f2020-05-22 13:10:44 +0200944#endif
zeertzjq9d997ad2024-07-29 21:10:07 +0200945 keyround1_simplified);
946 if (mp_result[keyround - 1] == NULL)
Bram Moolenaar459fd782019-10-13 16:43:39 +0200947 {
948 retval = 4; // no mem
949 goto theend;
950 }
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200951 }
952
zeertzjq9d997ad2024-07-29 21:10:07 +0200953 if (mp_result[0] != NULL && mp_result[1] != NULL)
954 {
955 mp_result[0]->m_alt = mp_result[1];
956 mp_result[1]->m_alt = mp_result[0];
957 }
958
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200959theend:
960 vim_free(keys_buf);
Bram Moolenaar459fd782019-10-13 16:43:39 +0200961 vim_free(alt_keys_buf);
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200962 vim_free(arg_buf);
963 return retval;
964}
965
966/*
967 * Get the mapping mode from the command name.
968 */
969 static int
970get_map_mode(char_u **cmdp, int forceit)
971{
972 char_u *p;
973 int modec;
974 int mode;
975
976 p = *cmdp;
977 modec = *p++;
978 if (modec == 'i')
Bram Moolenaar24959102022-05-07 20:01:16 +0100979 mode = MODE_INSERT; // :imap
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200980 else if (modec == 'l')
Bram Moolenaar24959102022-05-07 20:01:16 +0100981 mode = MODE_LANGMAP; // :lmap
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200982 else if (modec == 'c')
Bram Moolenaar24959102022-05-07 20:01:16 +0100983 mode = MODE_CMDLINE; // :cmap
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200984 else if (modec == 'n' && *p != 'o') // avoid :noremap
Bram Moolenaar24959102022-05-07 20:01:16 +0100985 mode = MODE_NORMAL; // :nmap
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200986 else if (modec == 'v')
Bram Moolenaar24959102022-05-07 20:01:16 +0100987 mode = MODE_VISUAL | MODE_SELECT; // :vmap
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200988 else if (modec == 'x')
Bram Moolenaar24959102022-05-07 20:01:16 +0100989 mode = MODE_VISUAL; // :xmap
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200990 else if (modec == 's')
Bram Moolenaar24959102022-05-07 20:01:16 +0100991 mode = MODE_SELECT; // :smap
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200992 else if (modec == 'o')
Bram Moolenaar24959102022-05-07 20:01:16 +0100993 mode = MODE_OP_PENDING; // :omap
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200994 else if (modec == 't')
Bram Moolenaar24959102022-05-07 20:01:16 +0100995 mode = MODE_TERMINAL; // :tmap
Bram Moolenaarb66bab32019-08-01 14:28:24 +0200996 else
997 {
998 --p;
999 if (forceit)
Bram Moolenaar24959102022-05-07 20:01:16 +01001000 mode = MODE_INSERT | MODE_CMDLINE; // :map !
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001001 else
Bram Moolenaar24959102022-05-07 20:01:16 +01001002 mode = MODE_VISUAL | MODE_SELECT | MODE_NORMAL | MODE_OP_PENDING;
1003 // :map
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001004 }
1005
1006 *cmdp = p;
1007 return mode;
1008}
1009
1010/*
zeertzjqc207fd22022-06-29 10:37:40 +01001011 * Clear all mappings (":mapclear") or abbreviations (":abclear").
1012 * "abbr" should be FALSE for mappings, TRUE for abbreviations.
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001013 */
1014 static void
1015map_clear(
1016 char_u *cmdp,
zeertzjqc207fd22022-06-29 10:37:40 +01001017 char_u *arg,
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001018 int forceit,
1019 int abbr)
1020{
1021 int mode;
1022 int local;
1023
1024 local = (STRCMP(arg, "<buffer>") == 0);
1025 if (!local && *arg != NUL)
1026 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001027 emsg(_(e_invalid_argument));
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001028 return;
1029 }
1030
1031 mode = get_map_mode(&cmdp, forceit);
zeertzjqc207fd22022-06-29 10:37:40 +01001032 map_clear_mode(curbuf, mode, local, abbr);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001033}
1034
1035/*
Bram Moolenaarbf533e42022-11-13 20:43:19 +00001036 * If "map_locked" is set then give an error and return TRUE.
1037 * Otherwise return FALSE.
1038 */
1039 static int
1040is_map_locked(void)
1041{
1042 if (map_locked > 0)
1043 {
1044 emsg(_(e_cannot_change_mappings_while_listing));
1045 return TRUE;
1046 }
1047 return FALSE;
1048}
1049
1050/*
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001051 * Clear all mappings in "mode".
1052 */
1053 void
zeertzjqc207fd22022-06-29 10:37:40 +01001054map_clear_mode(
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001055 buf_T *buf, // buffer for local mappings
1056 int mode, // mode in which to delete
1057 int local, // TRUE for buffer-local mappings
1058 int abbr) // TRUE for abbreviations
1059{
1060 mapblock_T *mp, **mpp;
1061 int hash;
1062 int new_hash;
1063
Bram Moolenaarbf533e42022-11-13 20:43:19 +00001064 if (is_map_locked())
1065 return;
1066
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001067 validate_maphash();
1068
1069 for (hash = 0; hash < 256; ++hash)
1070 {
1071 if (abbr)
1072 {
1073 if (hash > 0) // there is only one abbrlist
1074 break;
1075 if (local)
1076 mpp = &buf->b_first_abbr;
1077 else
1078 mpp = &first_abbr;
1079 }
1080 else
1081 {
1082 if (local)
1083 mpp = &buf->b_maphash[hash];
1084 else
1085 mpp = &maphash[hash];
1086 }
1087 while (*mpp != NULL)
1088 {
1089 mp = *mpp;
1090 if (mp->m_mode & mode)
1091 {
1092 mp->m_mode &= ~mode;
1093 if (mp->m_mode == 0) // entry can be deleted
1094 {
1095 map_free(mpp);
1096 continue;
1097 }
1098 // May need to put this entry into another hash list.
1099 new_hash = MAP_HASH(mp->m_mode, mp->m_keys[0]);
1100 if (!abbr && new_hash != hash)
1101 {
1102 *mpp = mp->m_next;
1103 if (local)
1104 {
1105 mp->m_next = buf->b_maphash[new_hash];
1106 buf->b_maphash[new_hash] = mp;
1107 }
1108 else
1109 {
1110 mp->m_next = maphash[new_hash];
1111 maphash[new_hash] = mp;
1112 }
1113 continue; // continue with *mpp
1114 }
1115 }
1116 mpp = &(mp->m_next);
1117 }
1118 }
1119}
1120
1121#if defined(FEAT_EVAL) || defined(PROTO)
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001122 int
Bram Moolenaar581ba392019-09-03 22:08:33 +02001123mode_str2flags(char_u *modechars)
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001124{
1125 int mode = 0;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001126
1127 if (vim_strchr(modechars, 'n') != NULL)
Bram Moolenaar24959102022-05-07 20:01:16 +01001128 mode |= MODE_NORMAL;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001129 if (vim_strchr(modechars, 'v') != NULL)
Bram Moolenaar24959102022-05-07 20:01:16 +01001130 mode |= MODE_VISUAL | MODE_SELECT;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001131 if (vim_strchr(modechars, 'x') != NULL)
Bram Moolenaar24959102022-05-07 20:01:16 +01001132 mode |= MODE_VISUAL;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001133 if (vim_strchr(modechars, 's') != NULL)
Bram Moolenaar24959102022-05-07 20:01:16 +01001134 mode |= MODE_SELECT;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001135 if (vim_strchr(modechars, 'o') != NULL)
Bram Moolenaar24959102022-05-07 20:01:16 +01001136 mode |= MODE_OP_PENDING;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001137 if (vim_strchr(modechars, 'i') != NULL)
Bram Moolenaar24959102022-05-07 20:01:16 +01001138 mode |= MODE_INSERT;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001139 if (vim_strchr(modechars, 'l') != NULL)
Bram Moolenaar24959102022-05-07 20:01:16 +01001140 mode |= MODE_LANGMAP;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001141 if (vim_strchr(modechars, 'c') != NULL)
Bram Moolenaar24959102022-05-07 20:01:16 +01001142 mode |= MODE_CMDLINE;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001143
Bram Moolenaar581ba392019-09-03 22:08:33 +02001144 return mode;
1145}
1146
1147/*
1148 * Return TRUE if a map exists that has "str" in the rhs for mode "modechars".
1149 * Recognize termcap codes in "str".
1150 * Also checks mappings local to the current buffer.
1151 */
1152 int
1153map_to_exists(char_u *str, char_u *modechars, int abbr)
1154{
1155 char_u *rhs;
1156 char_u *buf;
1157 int retval;
1158
zeertzjq7e0bae02023-08-11 23:15:38 +02001159 rhs = replace_termcodes(str, &buf, 0, REPTERM_DO_LT, NULL);
Bram Moolenaar581ba392019-09-03 22:08:33 +02001160
1161 retval = map_to_exists_mode(rhs, mode_str2flags(modechars), abbr);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001162 vim_free(buf);
1163
1164 return retval;
1165}
1166#endif
1167
1168/*
1169 * Return TRUE if a map exists that has "str" in the rhs for mode "mode".
1170 * Also checks mappings local to the current buffer.
1171 */
1172 int
1173map_to_exists_mode(char_u *rhs, int mode, int abbr)
1174{
1175 mapblock_T *mp;
1176 int hash;
1177 int exp_buffer = FALSE;
1178
1179 validate_maphash();
1180
1181 // Do it twice: once for global maps and once for local maps.
1182 for (;;)
1183 {
1184 for (hash = 0; hash < 256; ++hash)
1185 {
1186 if (abbr)
1187 {
1188 if (hash > 0) // there is only one abbr list
1189 break;
1190 if (exp_buffer)
1191 mp = curbuf->b_first_abbr;
1192 else
1193 mp = first_abbr;
1194 }
1195 else if (exp_buffer)
1196 mp = curbuf->b_maphash[hash];
1197 else
1198 mp = maphash[hash];
1199 for (; mp; mp = mp->m_next)
1200 {
1201 if ((mp->m_mode & mode)
1202 && strstr((char *)mp->m_str, (char *)rhs) != NULL)
1203 return TRUE;
1204 }
1205 }
1206 if (exp_buffer)
1207 break;
1208 exp_buffer = TRUE;
1209 }
1210
1211 return FALSE;
1212}
1213
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001214/*
1215 * Used below when expanding mapping/abbreviation names.
1216 */
1217static int expand_mapmodes = 0;
1218static int expand_isabbrev = 0;
1219static int expand_buffer = FALSE;
1220
1221/*
Bram Moolenaar7f51bbe2020-01-24 20:21:19 +01001222 * Translate an internal mapping/abbreviation representation into the
1223 * corresponding external one recognized by :map/:abbrev commands.
1224 * Respects the current B/k/< settings of 'cpoption'.
1225 *
1226 * This function is called when expanding mappings/abbreviations on the
1227 * command-line.
1228 *
1229 * It uses a growarray to build the translation string since the latter can be
1230 * wider than the original description. The caller has to free the string
1231 * afterwards.
1232 *
1233 * Returns NULL when there is a problem.
1234 */
1235 static char_u *
1236translate_mapping(char_u *str)
1237{
1238 garray_T ga;
1239 int c;
1240 int modifiers;
1241 int cpo_bslash;
1242 int cpo_special;
1243
1244 ga_init(&ga);
1245 ga.ga_itemsize = 1;
1246 ga.ga_growsize = 40;
1247
1248 cpo_bslash = (vim_strchr(p_cpo, CPO_BSLASH) != NULL);
1249 cpo_special = (vim_strchr(p_cpo, CPO_SPECI) != NULL);
1250
1251 for (; *str; ++str)
1252 {
1253 c = *str;
1254 if (c == K_SPECIAL && str[1] != NUL && str[2] != NUL)
1255 {
1256 modifiers = 0;
1257 if (str[1] == KS_MODIFIER)
1258 {
1259 str++;
1260 modifiers = *++str;
1261 c = *++str;
1262 }
1263 if (c == K_SPECIAL && str[1] != NUL && str[2] != NUL)
1264 {
1265 if (cpo_special)
1266 {
1267 ga_clear(&ga);
1268 return NULL;
1269 }
1270 c = TO_SPECIAL(str[1], str[2]);
1271 if (c == K_ZERO) // display <Nul> as ^@
1272 c = NUL;
1273 str += 2;
1274 }
1275 if (IS_SPECIAL(c) || modifiers) // special key
1276 {
1277 if (cpo_special)
1278 {
1279 ga_clear(&ga);
1280 return NULL;
1281 }
1282 ga_concat(&ga, get_special_key_name(c, modifiers));
1283 continue; // for (str)
1284 }
1285 }
1286 if (c == ' ' || c == '\t' || c == Ctrl_J || c == Ctrl_V
1287 || (c == '<' && !cpo_special) || (c == '\\' && !cpo_bslash))
1288 ga_append(&ga, cpo_bslash ? Ctrl_V : '\\');
1289 if (c)
1290 ga_append(&ga, c);
1291 }
1292 ga_append(&ga, NUL);
1293 return (char_u *)(ga.ga_data);
1294}
1295
1296/*
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001297 * Work out what to complete when doing command line completion of mapping
1298 * or abbreviation names.
1299 */
1300 char_u *
1301set_context_in_map_cmd(
1302 expand_T *xp,
1303 char_u *cmd,
1304 char_u *arg,
1305 int forceit, // TRUE if '!' given
1306 int isabbrev, // TRUE if abbreviation
1307 int isunmap, // TRUE if unmap/unabbrev command
1308 cmdidx_T cmdidx)
1309{
1310 if (forceit && cmdidx != CMD_map && cmdidx != CMD_unmap)
1311 xp->xp_context = EXPAND_NOTHING;
1312 else
1313 {
1314 if (isunmap)
1315 expand_mapmodes = get_map_mode(&cmd, forceit || isabbrev);
1316 else
1317 {
Bram Moolenaar24959102022-05-07 20:01:16 +01001318 expand_mapmodes = MODE_INSERT | MODE_CMDLINE;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001319 if (!isabbrev)
Bram Moolenaar24959102022-05-07 20:01:16 +01001320 expand_mapmodes += MODE_VISUAL | MODE_SELECT | MODE_NORMAL
1321 | MODE_OP_PENDING;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001322 }
1323 expand_isabbrev = isabbrev;
1324 xp->xp_context = EXPAND_MAPPINGS;
1325 expand_buffer = FALSE;
1326 for (;;)
1327 {
1328 if (STRNCMP(arg, "<buffer>", 8) == 0)
1329 {
1330 expand_buffer = TRUE;
1331 arg = skipwhite(arg + 8);
1332 continue;
1333 }
1334 if (STRNCMP(arg, "<unique>", 8) == 0)
1335 {
1336 arg = skipwhite(arg + 8);
1337 continue;
1338 }
1339 if (STRNCMP(arg, "<nowait>", 8) == 0)
1340 {
1341 arg = skipwhite(arg + 8);
1342 continue;
1343 }
1344 if (STRNCMP(arg, "<silent>", 8) == 0)
1345 {
1346 arg = skipwhite(arg + 8);
1347 continue;
1348 }
1349 if (STRNCMP(arg, "<special>", 9) == 0)
1350 {
1351 arg = skipwhite(arg + 9);
1352 continue;
1353 }
1354#ifdef FEAT_EVAL
1355 if (STRNCMP(arg, "<script>", 8) == 0)
1356 {
1357 arg = skipwhite(arg + 8);
1358 continue;
1359 }
1360 if (STRNCMP(arg, "<expr>", 6) == 0)
1361 {
1362 arg = skipwhite(arg + 6);
1363 continue;
1364 }
1365#endif
1366 break;
1367 }
1368 xp->xp_pattern = arg;
1369 }
1370
1371 return NULL;
1372}
1373
1374/*
1375 * Find all mapping/abbreviation names that match regexp "regmatch"'.
1376 * For command line expansion of ":[un]map" and ":[un]abbrev" in all modes.
1377 * Return OK if matches found, FAIL otherwise.
1378 */
1379 int
1380ExpandMappings(
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001381 char_u *pat,
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001382 regmatch_T *regmatch,
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001383 int *numMatches,
1384 char_u ***matches)
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001385{
1386 mapblock_T *mp;
Yegappan Lakshmanan5de4c432022-02-28 13:28:38 +00001387 garray_T ga;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001388 int hash;
1389 int count;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001390 char_u *p;
1391 int i;
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001392 int fuzzy;
1393 int match;
Yasuhiro Matsumoto09f68a52022-06-18 16:48:36 +01001394 int score = 0;
Yegappan Lakshmanan5de4c432022-02-28 13:28:38 +00001395 fuzmatch_str_T *fuzmatch;
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001396
1397 fuzzy = cmdline_fuzzy_complete(pat);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001398
1399 validate_maphash();
1400
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001401 *numMatches = 0; // return values in case of FAIL
1402 *matches = NULL;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001403
Yegappan Lakshmanan5de4c432022-02-28 13:28:38 +00001404 if (!fuzzy)
1405 ga_init2(&ga, sizeof(char *), 3);
1406 else
1407 ga_init2(&ga, sizeof(fuzmatch_str_T), 3);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001408
Yegappan Lakshmanan5de4c432022-02-28 13:28:38 +00001409 // First search in map modifier arguments
1410 for (i = 0; i < 7; ++i)
1411 {
1412 if (i == 0)
1413 p = (char_u *)"<silent>";
1414 else if (i == 1)
1415 p = (char_u *)"<unique>";
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001416#ifdef FEAT_EVAL
Yegappan Lakshmanan5de4c432022-02-28 13:28:38 +00001417 else if (i == 2)
1418 p = (char_u *)"<script>";
1419 else if (i == 3)
1420 p = (char_u *)"<expr>";
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001421#endif
Yegappan Lakshmanan5de4c432022-02-28 13:28:38 +00001422 else if (i == 4 && !expand_buffer)
1423 p = (char_u *)"<buffer>";
1424 else if (i == 5)
1425 p = (char_u *)"<nowait>";
1426 else if (i == 6)
1427 p = (char_u *)"<special>";
1428 else
1429 continue;
1430
1431 if (!fuzzy)
1432 match = vim_regexec(regmatch, p, (colnr_T)0);
1433 else
1434 {
1435 score = fuzzy_match_str(p, pat);
1436 match = (score != 0);
1437 }
1438
1439 if (!match)
1440 continue;
1441
1442 if (ga_grow(&ga, 1) == FAIL)
1443 break;
1444
1445 if (fuzzy)
1446 {
1447 fuzmatch = &((fuzmatch_str_T *)ga.ga_data)[ga.ga_len];
1448 fuzmatch->idx = ga.ga_len;
1449 fuzmatch->str = vim_strsave(p);
1450 fuzmatch->score = score;
1451 }
1452 else
1453 ((char_u **)ga.ga_data)[ga.ga_len] = vim_strsave(p);
1454 ++ga.ga_len;
1455 }
1456
1457 for (hash = 0; hash < 256; ++hash)
1458 {
1459 if (expand_isabbrev)
1460 {
1461 if (hash > 0) // only one abbrev list
1462 break; // for (hash)
1463 mp = first_abbr;
1464 }
1465 else if (expand_buffer)
1466 mp = curbuf->b_maphash[hash];
1467 else
1468 mp = maphash[hash];
1469 for (; mp; mp = mp->m_next)
1470 {
zeertzjq997b8a02023-02-19 21:00:31 +00001471 if (mp->m_simplified || !(mp->m_mode & expand_mapmodes))
Yegappan Lakshmanan5de4c432022-02-28 13:28:38 +00001472 continue;
1473
1474 p = translate_mapping(mp->m_keys);
1475 if (p == NULL)
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001476 continue;
1477
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001478 if (!fuzzy)
1479 match = vim_regexec(regmatch, p, (colnr_T)0);
1480 else
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001481 {
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001482 score = fuzzy_match_str(p, pat);
1483 match = (score != 0);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001484 }
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001485
1486 if (!match)
Yegappan Lakshmanan5de4c432022-02-28 13:28:38 +00001487 {
1488 vim_free(p);
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001489 continue;
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001490 }
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001491
Yegappan Lakshmanan5de4c432022-02-28 13:28:38 +00001492 if (ga_grow(&ga, 1) == FAIL)
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001493 {
Yegappan Lakshmanan5de4c432022-02-28 13:28:38 +00001494 vim_free(p);
1495 break;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001496 }
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001497
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001498 if (fuzzy)
1499 {
Yegappan Lakshmanan5de4c432022-02-28 13:28:38 +00001500 fuzmatch = &((fuzmatch_str_T *)ga.ga_data)[ga.ga_len];
1501 fuzmatch->idx = ga.ga_len;
1502 fuzmatch->str = p;
1503 fuzmatch->score = score;
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001504 }
1505 else
Yegappan Lakshmanan5de4c432022-02-28 13:28:38 +00001506 ((char_u **)ga.ga_data)[ga.ga_len] = p;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001507
Yegappan Lakshmanan5de4c432022-02-28 13:28:38 +00001508 ++ga.ga_len;
1509 } // for (mp)
1510 } // for (hash)
1511
1512 if (ga.ga_len == 0)
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001513 return FAIL;
1514
Yegappan Lakshmanan5de4c432022-02-28 13:28:38 +00001515 if (!fuzzy)
1516 {
1517 *matches = ga.ga_data;
1518 *numMatches = ga.ga_len;
1519 }
1520 else
1521 {
1522 if (fuzzymatches_to_strmatches(ga.ga_data, matches, ga.ga_len,
1523 FALSE) == FAIL)
1524 return FAIL;
1525 *numMatches = ga.ga_len;
1526 }
1527
1528 count = *numMatches;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001529 if (count > 1)
1530 {
1531 char_u **ptr1;
1532 char_u **ptr2;
1533 char_u **ptr3;
1534
1535 // Sort the matches
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001536 // Fuzzy matching already sorts the matches
1537 if (!fuzzy)
1538 sort_strings(*matches, count);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001539
1540 // Remove multiple entries
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001541 ptr1 = *matches;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001542 ptr2 = ptr1 + 1;
1543 ptr3 = ptr1 + count;
1544
1545 while (ptr2 < ptr3)
1546 {
1547 if (STRCMP(*ptr1, *ptr2))
1548 *++ptr1 = *ptr2++;
1549 else
1550 {
1551 vim_free(*ptr2++);
1552 count--;
1553 }
1554 }
1555 }
1556
Yegappan Lakshmanan6caeda22022-02-27 12:07:30 +00001557 *numMatches = count;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001558 return (count == 0 ? FAIL : OK);
1559}
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001560
1561/*
1562 * Check for an abbreviation.
1563 * Cursor is at ptr[col].
1564 * When inserting, mincol is where insert started.
1565 * For the command line, mincol is what is to be skipped over.
1566 * "c" is the character typed before check_abbr was called. It may have
1567 * ABBR_OFF added to avoid prepending a CTRL-V to it.
1568 *
1569 * Historic vi practice: The last character of an abbreviation must be an id
1570 * character ([a-zA-Z0-9_]). The characters in front of it must be all id
1571 * characters or all non-id characters. This allows for abbr. "#i" to
1572 * "#include".
1573 *
1574 * Vim addition: Allow for abbreviations that end in a non-keyword character.
1575 * Then there must be white space before the abbr.
1576 *
1577 * return TRUE if there is an abbreviation, FALSE if not
1578 */
1579 int
1580check_abbr(
1581 int c,
1582 char_u *ptr,
1583 int col,
1584 int mincol)
1585{
1586 int len;
1587 int scol; // starting column of the abbr.
1588 int j;
1589 char_u *s;
1590 char_u tb[MB_MAXBYTES + 4];
1591 mapblock_T *mp;
1592 mapblock_T *mp2;
1593 int clen = 0; // length in characters
1594 int is_id = TRUE;
1595 int vim_abbr;
1596
1597 if (typebuf.tb_no_abbr_cnt) // abbrev. are not recursive
1598 return FALSE;
1599
1600 // no remapping implies no abbreviation, except for CTRL-]
1601 if (noremap_keys() && c != Ctrl_RSB)
1602 return FALSE;
1603
1604 // Check for word before the cursor: If it ends in a keyword char all
1605 // chars before it must be keyword chars or non-keyword chars, but not
1606 // white space. If it ends in a non-keyword char we accept any characters
1607 // before it except white space.
1608 if (col == 0) // cannot be an abbr.
1609 return FALSE;
1610
1611 if (has_mbyte)
1612 {
1613 char_u *p;
1614
1615 p = mb_prevptr(ptr, ptr + col);
1616 if (!vim_iswordp(p))
1617 vim_abbr = TRUE; // Vim added abbr.
1618 else
1619 {
1620 vim_abbr = FALSE; // vi compatible abbr.
1621 if (p > ptr)
1622 is_id = vim_iswordp(mb_prevptr(ptr, p));
1623 }
1624 clen = 1;
1625 while (p > ptr + mincol)
1626 {
1627 p = mb_prevptr(ptr, p);
1628 if (vim_isspace(*p) || (!vim_abbr && is_id != vim_iswordp(p)))
1629 {
1630 p += (*mb_ptr2len)(p);
1631 break;
1632 }
1633 ++clen;
1634 }
1635 scol = (int)(p - ptr);
1636 }
1637 else
1638 {
1639 if (!vim_iswordc(ptr[col - 1]))
1640 vim_abbr = TRUE; // Vim added abbr.
1641 else
1642 {
1643 vim_abbr = FALSE; // vi compatible abbr.
1644 if (col > 1)
1645 is_id = vim_iswordc(ptr[col - 2]);
1646 }
1647 for (scol = col - 1; scol > 0 && !vim_isspace(ptr[scol - 1])
1648 && (vim_abbr || is_id == vim_iswordc(ptr[scol - 1])); --scol)
1649 ;
1650 }
1651
1652 if (scol < mincol)
1653 scol = mincol;
1654 if (scol < col) // there is a word in front of the cursor
1655 {
1656 ptr += scol;
1657 len = col - scol;
1658 mp = curbuf->b_first_abbr;
1659 mp2 = first_abbr;
1660 if (mp == NULL)
1661 {
1662 mp = mp2;
1663 mp2 = NULL;
1664 }
1665 for ( ; mp; mp->m_next == NULL
1666 ? (mp = mp2, mp2 = NULL) : (mp = mp->m_next))
1667 {
1668 int qlen = mp->m_keylen;
1669 char_u *q = mp->m_keys;
1670 int match;
1671
1672 if (vim_strbyte(mp->m_keys, K_SPECIAL) != NULL)
1673 {
1674 char_u *qe = vim_strsave(mp->m_keys);
1675
1676 // might have CSI escaped mp->m_keys
1677 if (qe != NULL)
1678 {
1679 q = qe;
1680 vim_unescape_csi(q);
1681 qlen = (int)STRLEN(q);
1682 }
1683 }
1684
1685 // find entries with right mode and keys
1686 match = (mp->m_mode & State)
1687 && qlen == len
1688 && !STRNCMP(q, ptr, (size_t)len);
1689 if (q != mp->m_keys)
1690 vim_free(q);
1691 if (match)
1692 break;
1693 }
1694 if (mp != NULL)
1695 {
Bram Moolenaar94075b22022-01-18 20:30:39 +00001696 int noremap;
1697 int silent;
1698#ifdef FEAT_EVAL
1699 int expr;
1700#endif
1701
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001702 // Found a match:
1703 // Insert the rest of the abbreviation in typebuf.tb_buf[].
1704 // This goes from end to start.
1705 //
1706 // Characters 0x000 - 0x100: normal chars, may need CTRL-V,
1707 // except K_SPECIAL: Becomes K_SPECIAL KS_SPECIAL KE_FILLER
1708 // Characters where IS_SPECIAL() == TRUE: key codes, need
1709 // K_SPECIAL. Other characters (with ABBR_OFF): don't use CTRL-V.
1710 //
1711 // Character CTRL-] is treated specially - it completes the
1712 // abbreviation, but is not inserted into the input stream.
1713 j = 0;
1714 if (c != Ctrl_RSB)
1715 {
1716 // special key code, split up
1717 if (IS_SPECIAL(c) || c == K_SPECIAL)
1718 {
1719 tb[j++] = K_SPECIAL;
1720 tb[j++] = K_SECOND(c);
1721 tb[j++] = K_THIRD(c);
1722 }
1723 else
1724 {
1725 if (c < ABBR_OFF && (c < ' ' || c > '~'))
1726 tb[j++] = Ctrl_V; // special char needs CTRL-V
1727 if (has_mbyte)
1728 {
Bram Moolenaar4934ed32021-04-30 19:43:11 +02001729 int newlen;
1730 char_u *escaped;
1731
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001732 // if ABBR_OFF has been added, remove it here
1733 if (c >= ABBR_OFF)
1734 c -= ABBR_OFF;
Bram Moolenaar4934ed32021-04-30 19:43:11 +02001735 newlen = (*mb_char2bytes)(c, tb + j);
1736 tb[j + newlen] = NUL;
1737 // Need to escape K_SPECIAL.
1738 escaped = vim_strsave_escape_csi(tb + j);
1739 if (escaped != NULL)
1740 {
Bram Moolenaar551c1ae2021-05-03 18:57:05 +02001741 newlen = (int)STRLEN(escaped);
Bram Moolenaar4934ed32021-04-30 19:43:11 +02001742 mch_memmove(tb + j, escaped, newlen);
1743 j += newlen;
1744 vim_free(escaped);
1745 }
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001746 }
1747 else
1748 tb[j++] = c;
1749 }
1750 tb[j] = NUL;
1751 // insert the last typed char
1752 (void)ins_typebuf(tb, 1, 0, TRUE, mp->m_silent);
1753 }
Bram Moolenaar94075b22022-01-18 20:30:39 +00001754
1755 // copy values here, calling eval_map_expr() may make "mp" invalid!
1756 noremap = mp->m_noremap;
1757 silent = mp->m_silent;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001758#ifdef FEAT_EVAL
Bram Moolenaar94075b22022-01-18 20:30:39 +00001759 expr = mp->m_expr;
1760
1761 if (expr)
Bram Moolenaar19db9e62022-01-11 11:58:19 +00001762 s = eval_map_expr(mp, c);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001763 else
1764#endif
1765 s = mp->m_str;
1766 if (s != NULL)
1767 {
1768 // insert the to string
Bram Moolenaar94075b22022-01-18 20:30:39 +00001769 (void)ins_typebuf(s, noremap, 0, TRUE, silent);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001770 // no abbrev. for these chars
1771 typebuf.tb_no_abbr_cnt += (int)STRLEN(s) + j + 1;
1772#ifdef FEAT_EVAL
Bram Moolenaar94075b22022-01-18 20:30:39 +00001773 if (expr)
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001774 vim_free(s);
1775#endif
1776 }
1777
1778 tb[0] = Ctrl_H;
1779 tb[1] = NUL;
1780 if (has_mbyte)
1781 len = clen; // Delete characters instead of bytes
1782 while (len-- > 0) // delete the from string
Bram Moolenaar94075b22022-01-18 20:30:39 +00001783 (void)ins_typebuf(tb, 1, 0, TRUE, silent);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001784 return TRUE;
1785 }
1786 }
1787 return FALSE;
1788}
1789
1790#ifdef FEAT_EVAL
1791/*
1792 * Evaluate the RHS of a mapping or abbreviations and take care of escaping
1793 * special characters.
Bram Moolenaar94075b22022-01-18 20:30:39 +00001794 * Careful: after this "mp" will be invalid if the mapping was deleted.
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001795 */
1796 char_u *
1797eval_map_expr(
Bram Moolenaar19db9e62022-01-11 11:58:19 +00001798 mapblock_T *mp,
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001799 int c) // NUL or typed character for abbreviation
1800{
1801 char_u *res;
1802 char_u *p;
1803 char_u *expr;
1804 pos_T save_cursor;
1805 int save_msg_col;
1806 int save_msg_row;
Bram Moolenaar19db9e62022-01-11 11:58:19 +00001807 scid_T save_sctx_sid = current_sctx.sc_sid;
1808 int save_sctx_version = current_sctx.sc_version;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001809
1810 // Remove escaping of CSI, because "str" is in a format to be used as
1811 // typeahead.
Bram Moolenaar19db9e62022-01-11 11:58:19 +00001812 expr = vim_strsave(mp->m_str);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001813 if (expr == NULL)
1814 return NULL;
1815 vim_unescape_csi(expr);
1816
1817 // Forbid changing text or using ":normal" to avoid most of the bad side
1818 // effects. Also restore the cursor position.
zeertzjqcfe45652022-05-27 17:26:55 +01001819 ++textlock;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001820 ++ex_normal_lock;
1821 set_vim_var_char(c); // set v:char to the typed character
1822 save_cursor = curwin->w_cursor;
1823 save_msg_col = msg_col;
1824 save_msg_row = msg_row;
Bram Moolenaar19db9e62022-01-11 11:58:19 +00001825 if (mp->m_script_ctx.sc_version == SCRIPT_VERSION_VIM9)
1826 {
1827 current_sctx.sc_sid = mp->m_script_ctx.sc_sid;
1828 current_sctx.sc_version = SCRIPT_VERSION_VIM9;
1829 }
1830
1831 // Note: the evaluation may make "mp" invalid.
Bram Moolenaara4e0b972022-10-01 19:43:52 +01001832 p = eval_to_string(expr, FALSE, FALSE);
Bram Moolenaar19db9e62022-01-11 11:58:19 +00001833
zeertzjqcfe45652022-05-27 17:26:55 +01001834 --textlock;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001835 --ex_normal_lock;
1836 curwin->w_cursor = save_cursor;
1837 msg_col = save_msg_col;
1838 msg_row = save_msg_row;
Bram Moolenaar19db9e62022-01-11 11:58:19 +00001839 current_sctx.sc_sid = save_sctx_sid;
1840 current_sctx.sc_version = save_sctx_version;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001841
1842 vim_free(expr);
1843
1844 if (p == NULL)
1845 return NULL;
1846 // Escape CSI in the result to be able to use the string as typeahead.
1847 res = vim_strsave_escape_csi(p);
1848 vim_free(p);
1849
1850 return res;
1851}
1852#endif
1853
1854/*
1855 * Copy "p" to allocated memory, escaping K_SPECIAL and CSI so that the result
1856 * can be put in the typeahead buffer.
1857 * Returns NULL when out of memory.
1858 */
1859 char_u *
Bram Moolenaar957cf672020-11-12 14:21:06 +01001860vim_strsave_escape_csi(char_u *p)
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001861{
1862 char_u *res;
1863 char_u *s, *d;
1864
1865 // Need a buffer to hold up to three times as much. Four in case of an
1866 // illegal utf-8 byte:
1867 // 0xc0 -> 0xc3 0x80 -> 0xc3 K_SPECIAL KS_SPECIAL KE_FILLER
1868 res = alloc(STRLEN(p) * 4 + 1);
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00001869 if (res == NULL)
1870 return NULL;
1871
1872 d = res;
1873 for (s = p; *s != NUL; )
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001874 {
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00001875 if ((s[0] == K_SPECIAL
zeertzjq2cd0f272022-10-04 20:14:28 +01001876#ifdef FEAT_GUI
1877 || (gui.in_use && s[0] == CSI)
1878#endif
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00001879 ) && s[1] != NUL && s[2] != NUL)
1880 {
1881 // Copy special key unmodified.
1882 *d++ = *s++;
1883 *d++ = *s++;
1884 *d++ = *s++;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001885 }
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00001886 else
1887 {
1888 // Add character, possibly multi-byte to destination, escaping
1889 // CSI and K_SPECIAL. Be careful, it can be an illegal byte!
1890 d = add_char2buf(PTR2CHAR(s), d);
1891 s += MB_CPTR2LEN(s);
1892 }
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001893 }
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00001894 *d = NUL;
1895
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001896 return res;
1897}
1898
1899/*
1900 * Remove escaping from CSI and K_SPECIAL characters. Reverse of
1901 * vim_strsave_escape_csi(). Works in-place.
1902 */
1903 void
1904vim_unescape_csi(char_u *p)
1905{
1906 char_u *s = p, *d = p;
1907
1908 while (*s != NUL)
1909 {
1910 if (s[0] == K_SPECIAL && s[1] == KS_SPECIAL && s[2] == KE_FILLER)
1911 {
1912 *d++ = K_SPECIAL;
1913 s += 3;
1914 }
1915 else if ((s[0] == K_SPECIAL || s[0] == CSI)
1916 && s[1] == KS_EXTRA && s[2] == (int)KE_CSI)
1917 {
1918 *d++ = CSI;
1919 s += 3;
1920 }
1921 else
1922 *d++ = *s++;
1923 }
1924 *d = NUL;
1925}
1926
1927/*
1928 * Write map commands for the current mappings to an .exrc file.
1929 * Return FAIL on error, OK otherwise.
1930 */
1931 int
1932makemap(
1933 FILE *fd,
1934 buf_T *buf) // buffer for local mappings or NULL
1935{
1936 mapblock_T *mp;
1937 char_u c1, c2, c3;
1938 char_u *p;
1939 char *cmd;
1940 int abbr;
1941 int hash;
1942 int did_cpo = FALSE;
1943 int i;
1944
1945 validate_maphash();
1946
1947 // Do the loop twice: Once for mappings, once for abbreviations.
1948 // Then loop over all map hash lists.
1949 for (abbr = 0; abbr < 2; ++abbr)
1950 for (hash = 0; hash < 256; ++hash)
1951 {
1952 if (abbr)
1953 {
1954 if (hash > 0) // there is only one abbr list
1955 break;
1956 if (buf != NULL)
1957 mp = buf->b_first_abbr;
1958 else
1959 mp = first_abbr;
1960 }
1961 else
1962 {
1963 if (buf != NULL)
1964 mp = buf->b_maphash[hash];
1965 else
1966 mp = maphash[hash];
1967 }
1968
1969 for ( ; mp; mp = mp->m_next)
1970 {
1971 // skip script-local mappings
1972 if (mp->m_noremap == REMAP_SCRIPT)
1973 continue;
1974
1975 // skip mappings that contain a <SNR> (script-local thing),
1976 // they probably don't work when loaded again
1977 for (p = mp->m_str; *p != NUL; ++p)
1978 if (p[0] == K_SPECIAL && p[1] == KS_EXTRA
1979 && p[2] == (int)KE_SNR)
1980 break;
1981 if (*p != NUL)
1982 continue;
1983
1984 // It's possible to create a mapping and then ":unmap" certain
1985 // modes. We recreate this here by mapping the individual
1986 // modes, which requires up to three of them.
1987 c1 = NUL;
1988 c2 = NUL;
1989 c3 = NUL;
1990 if (abbr)
1991 cmd = "abbr";
1992 else
1993 cmd = "map";
1994 switch (mp->m_mode)
1995 {
Bram Moolenaar24959102022-05-07 20:01:16 +01001996 case MODE_NORMAL | MODE_VISUAL | MODE_SELECT
1997 | MODE_OP_PENDING:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02001998 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01001999 case MODE_NORMAL:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002000 c1 = 'n';
2001 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01002002 case MODE_VISUAL:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002003 c1 = 'x';
2004 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01002005 case MODE_SELECT:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002006 c1 = 's';
2007 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01002008 case MODE_OP_PENDING:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002009 c1 = 'o';
2010 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01002011 case MODE_NORMAL | MODE_VISUAL:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002012 c1 = 'n';
2013 c2 = 'x';
2014 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01002015 case MODE_NORMAL | MODE_SELECT:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002016 c1 = 'n';
2017 c2 = 's';
2018 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01002019 case MODE_NORMAL | MODE_OP_PENDING:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002020 c1 = 'n';
2021 c2 = 'o';
2022 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01002023 case MODE_VISUAL | MODE_SELECT:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002024 c1 = 'v';
2025 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01002026 case MODE_VISUAL | MODE_OP_PENDING:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002027 c1 = 'x';
2028 c2 = 'o';
2029 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01002030 case MODE_SELECT | MODE_OP_PENDING:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002031 c1 = 's';
2032 c2 = 'o';
2033 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01002034 case MODE_NORMAL | MODE_VISUAL | MODE_SELECT:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002035 c1 = 'n';
2036 c2 = 'v';
2037 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01002038 case MODE_NORMAL | MODE_VISUAL | MODE_OP_PENDING:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002039 c1 = 'n';
2040 c2 = 'x';
2041 c3 = 'o';
2042 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01002043 case MODE_NORMAL | MODE_SELECT | MODE_OP_PENDING:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002044 c1 = 'n';
2045 c2 = 's';
2046 c3 = 'o';
2047 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01002048 case MODE_VISUAL | MODE_SELECT | MODE_OP_PENDING:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002049 c1 = 'v';
2050 c2 = 'o';
2051 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01002052 case MODE_CMDLINE | MODE_INSERT:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002053 if (!abbr)
2054 cmd = "map!";
2055 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01002056 case MODE_CMDLINE:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002057 c1 = 'c';
2058 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01002059 case MODE_INSERT:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002060 c1 = 'i';
2061 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01002062 case MODE_LANGMAP:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002063 c1 = 'l';
2064 break;
Bram Moolenaar24959102022-05-07 20:01:16 +01002065 case MODE_TERMINAL:
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002066 c1 = 't';
2067 break;
2068 default:
RestorerZ68ebcee2023-05-31 17:12:14 +01002069 iemsg(e_makemap_illegal_mode);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002070 return FAIL;
2071 }
2072 do // do this twice if c2 is set, 3 times with c3
2073 {
2074 // When outputting <> form, need to make sure that 'cpo'
2075 // is set to the Vim default.
2076 if (!did_cpo)
2077 {
2078 if (*mp->m_str == NUL) // will use <Nop>
2079 did_cpo = TRUE;
2080 else
2081 for (i = 0; i < 2; ++i)
2082 for (p = (i ? mp->m_str : mp->m_keys); *p; ++p)
2083 if (*p == K_SPECIAL || *p == NL)
2084 did_cpo = TRUE;
2085 if (did_cpo)
2086 {
2087 if (fprintf(fd, "let s:cpo_save=&cpo") < 0
2088 || put_eol(fd) < 0
2089 || fprintf(fd, "set cpo&vim") < 0
2090 || put_eol(fd) < 0)
2091 return FAIL;
2092 }
2093 }
2094 if (c1 && putc(c1, fd) < 0)
2095 return FAIL;
2096 if (mp->m_noremap != REMAP_YES && fprintf(fd, "nore") < 0)
2097 return FAIL;
2098 if (fputs(cmd, fd) < 0)
2099 return FAIL;
2100 if (buf != NULL && fputs(" <buffer>", fd) < 0)
2101 return FAIL;
2102 if (mp->m_nowait && fputs(" <nowait>", fd) < 0)
2103 return FAIL;
2104 if (mp->m_silent && fputs(" <silent>", fd) < 0)
2105 return FAIL;
2106#ifdef FEAT_EVAL
2107 if (mp->m_noremap == REMAP_SCRIPT
2108 && fputs("<script>", fd) < 0)
2109 return FAIL;
2110 if (mp->m_expr && fputs(" <expr>", fd) < 0)
2111 return FAIL;
2112#endif
2113
2114 if ( putc(' ', fd) < 0
2115 || put_escstr(fd, mp->m_keys, 0) == FAIL
2116 || putc(' ', fd) < 0
2117 || put_escstr(fd, mp->m_str, 1) == FAIL
2118 || put_eol(fd) < 0)
2119 return FAIL;
2120 c1 = c2;
2121 c2 = c3;
2122 c3 = NUL;
2123 } while (c1 != NUL);
2124 }
2125 }
2126
2127 if (did_cpo)
2128 if (fprintf(fd, "let &cpo=s:cpo_save") < 0
2129 || put_eol(fd) < 0
2130 || fprintf(fd, "unlet s:cpo_save") < 0
2131 || put_eol(fd) < 0)
2132 return FAIL;
2133 return OK;
2134}
2135
2136/*
2137 * write escape string to file
2138 * "what": 0 for :map lhs, 1 for :map rhs, 2 for :set
2139 *
2140 * return FAIL for failure, OK otherwise
2141 */
2142 int
2143put_escstr(FILE *fd, char_u *strstart, int what)
2144{
2145 char_u *str = strstart;
2146 int c;
2147 int modifiers;
2148
2149 // :map xx <Nop>
2150 if (*str == NUL && what == 1)
2151 {
2152 if (fprintf(fd, "<Nop>") < 0)
2153 return FAIL;
2154 return OK;
2155 }
2156
2157 for ( ; *str != NUL; ++str)
2158 {
2159 char_u *p;
2160
2161 // Check for a multi-byte character, which may contain escaped
2162 // K_SPECIAL and CSI bytes
2163 p = mb_unescape(&str);
2164 if (p != NULL)
2165 {
2166 while (*p != NUL)
2167 if (fputc(*p++, fd) < 0)
2168 return FAIL;
2169 --str;
2170 continue;
2171 }
2172
2173 c = *str;
2174 // Special key codes have to be translated to be able to make sense
2175 // when they are read back.
2176 if (c == K_SPECIAL && what != 2)
2177 {
Bram Moolenaar02c037a2020-08-30 19:26:45 +02002178 modifiers = 0;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002179 if (str[1] == KS_MODIFIER)
2180 {
2181 modifiers = str[2];
2182 str += 3;
GuyBrush5b07aff2025-05-22 22:19:25 +02002183
2184 // Modifiers can be applied too to multi-byte characters.
2185 p = mb_unescape(&str);
2186
2187 if (p == NULL)
2188 c = *str;
2189 else
2190 {
2191 // retrieve codepoint (character number) from unescaped string
2192 c = (*mb_ptr2char)(p);
2193 --str;
2194 }
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002195 }
2196 if (c == K_SPECIAL)
2197 {
2198 c = TO_SPECIAL(str[1], str[2]);
2199 str += 2;
2200 }
2201 if (IS_SPECIAL(c) || modifiers) // special key
2202 {
2203 if (fputs((char *)get_special_key_name(c, modifiers), fd) < 0)
2204 return FAIL;
2205 continue;
2206 }
2207 }
2208
2209 // A '\n' in a map command should be written as <NL>.
2210 // A '\n' in a set command should be written as \^V^J.
2211 if (c == NL)
2212 {
2213 if (what == 2)
2214 {
Bram Moolenaar424bcae2022-01-31 14:59:41 +00002215 if (fprintf(fd, "\\\026\n") < 0)
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002216 return FAIL;
2217 }
2218 else
2219 {
2220 if (fprintf(fd, "<NL>") < 0)
2221 return FAIL;
2222 }
2223 continue;
2224 }
2225
2226 // Some characters have to be escaped with CTRL-V to
2227 // prevent them from misinterpreted in DoOneCmd().
2228 // A space, Tab and '"' has to be escaped with a backslash to
2229 // prevent it to be misinterpreted in do_set().
2230 // A space has to be escaped with a CTRL-V when it's at the start of a
2231 // ":map" rhs.
2232 // A '<' has to be escaped with a CTRL-V to prevent it being
2233 // interpreted as the start of a special key name.
2234 // A space in the lhs of a :map needs a CTRL-V.
2235 if (what == 2 && (VIM_ISWHITE(c) || c == '"' || c == '\\'))
2236 {
2237 if (putc('\\', fd) < 0)
2238 return FAIL;
2239 }
2240 else if (c < ' ' || c > '~' || c == '|'
2241 || (what == 0 && c == ' ')
2242 || (what == 1 && str == strstart && c == ' ')
2243 || (what != 2 && c == '<'))
2244 {
2245 if (putc(Ctrl_V, fd) < 0)
2246 return FAIL;
2247 }
2248 if (putc(c, fd) < 0)
2249 return FAIL;
2250 }
2251 return OK;
2252}
2253
2254/*
2255 * Check all mappings for the presence of special key codes.
2256 * Used after ":set term=xxx".
2257 */
2258 void
2259check_map_keycodes(void)
2260{
2261 mapblock_T *mp;
2262 char_u *p;
2263 int i;
2264 char_u buf[3];
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002265 int abbr;
2266 int hash;
2267 buf_T *bp;
ichizok7e5fe382023-04-15 13:17:50 +01002268 ESTACK_CHECK_DECLARATION;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002269
2270 validate_maphash();
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002271 // avoids giving error messages
2272 estack_push(ETYPE_INTERNAL, (char_u *)"mappings", 0);
ichizok7e5fe382023-04-15 13:17:50 +01002273 ESTACK_CHECK_SETUP;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002274
Bram Moolenaar32aa1022019-11-02 22:54:41 +01002275 // Do this once for each buffer, and then once for global
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002276 // mappings/abbreviations with bp == NULL
2277 for (bp = firstbuf; ; bp = bp->b_next)
2278 {
2279 // Do the loop twice: Once for mappings, once for abbreviations.
2280 // Then loop over all map hash lists.
2281 for (abbr = 0; abbr <= 1; ++abbr)
2282 for (hash = 0; hash < 256; ++hash)
2283 {
2284 if (abbr)
2285 {
2286 if (hash) // there is only one abbr list
2287 break;
2288 if (bp != NULL)
2289 mp = bp->b_first_abbr;
2290 else
2291 mp = first_abbr;
2292 }
2293 else
2294 {
2295 if (bp != NULL)
2296 mp = bp->b_maphash[hash];
2297 else
2298 mp = maphash[hash];
2299 }
2300 for ( ; mp != NULL; mp = mp->m_next)
2301 {
2302 for (i = 0; i <= 1; ++i) // do this twice
2303 {
2304 if (i == 0)
2305 p = mp->m_keys; // once for the "from" part
2306 else
2307 p = mp->m_str; // and once for the "to" part
2308 while (*p)
2309 {
2310 if (*p == K_SPECIAL)
2311 {
2312 ++p;
2313 if (*p < 128) // for "normal" tcap entries
2314 {
2315 buf[0] = p[0];
2316 buf[1] = p[1];
2317 buf[2] = NUL;
2318 (void)add_termcap_entry(buf, FALSE);
2319 }
2320 ++p;
2321 }
2322 ++p;
2323 }
2324 }
2325 }
2326 }
2327 if (bp == NULL)
2328 break;
2329 }
ichizok7e5fe382023-04-15 13:17:50 +01002330 ESTACK_CHECK_NOW;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01002331 estack_pop();
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002332}
2333
2334#if defined(FEAT_EVAL) || defined(PROTO)
2335/*
2336 * Check the string "keys" against the lhs of all mappings.
2337 * Return pointer to rhs of mapping (mapblock->m_str).
2338 * NULL when no mapping found.
2339 */
2340 char_u *
2341check_map(
2342 char_u *keys,
2343 int mode,
2344 int exact, // require exact match
2345 int ign_mod, // ignore preceding modifier
2346 int abbr, // do abbreviations
2347 mapblock_T **mp_ptr, // return: pointer to mapblock or NULL
2348 int *local_ptr) // return: buffer-local mapping or NULL
2349{
2350 int hash;
2351 int len, minlen;
2352 mapblock_T *mp;
2353 char_u *s;
2354 int local;
2355
2356 validate_maphash();
2357
2358 len = (int)STRLEN(keys);
2359 for (local = 1; local >= 0; --local)
2360 // loop over all hash lists
2361 for (hash = 0; hash < 256; ++hash)
2362 {
2363 if (abbr)
2364 {
2365 if (hash > 0) // there is only one list.
2366 break;
2367 if (local)
2368 mp = curbuf->b_first_abbr;
2369 else
2370 mp = first_abbr;
2371 }
2372 else if (local)
2373 mp = curbuf->b_maphash[hash];
2374 else
2375 mp = maphash[hash];
2376 for ( ; mp != NULL; mp = mp->m_next)
2377 {
2378 // skip entries with wrong mode, wrong length and not matching
2379 // ones
2380 if ((mp->m_mode & mode) && (!exact || mp->m_keylen == len))
2381 {
2382 if (len > mp->m_keylen)
2383 minlen = mp->m_keylen;
2384 else
2385 minlen = len;
2386 s = mp->m_keys;
2387 if (ign_mod && s[0] == K_SPECIAL && s[1] == KS_MODIFIER
2388 && s[2] != NUL)
2389 {
2390 s += 3;
2391 if (len > mp->m_keylen - 3)
2392 minlen = mp->m_keylen - 3;
2393 }
2394 if (STRNCMP(s, keys, minlen) == 0)
2395 {
2396 if (mp_ptr != NULL)
2397 *mp_ptr = mp;
2398 if (local_ptr != NULL)
2399 *local_ptr = local;
2400 return mp->m_str;
2401 }
2402 }
2403 }
2404 }
2405
2406 return NULL;
2407}
2408
Ernie Rael659c2402022-04-24 18:40:28 +01002409/*
zeertzjqc207fd22022-06-29 10:37:40 +01002410 * "hasmapto()" function
2411 */
2412 void
2413f_hasmapto(typval_T *argvars, typval_T *rettv)
2414{
2415 char_u *name;
2416 char_u *mode;
2417 char_u buf[NUMBUFLEN];
2418 int abbr = FALSE;
2419
2420 if (in_vim9script()
2421 && (check_for_string_arg(argvars, 0) == FAIL
2422 || check_for_opt_string_arg(argvars, 1) == FAIL
2423 || (argvars[1].v_type != VAR_UNKNOWN
2424 && check_for_opt_bool_arg(argvars, 2) == FAIL)))
2425 return;
2426
2427 name = tv_get_string(&argvars[0]);
2428 if (argvars[1].v_type == VAR_UNKNOWN)
2429 mode = (char_u *)"nvo";
2430 else
2431 {
2432 mode = tv_get_string_buf(&argvars[1], buf);
2433 if (argvars[2].v_type != VAR_UNKNOWN)
2434 abbr = (int)tv_get_bool(&argvars[2]);
2435 }
2436
2437 if (map_to_exists(name, mode, abbr))
2438 rettv->vval.v_number = TRUE;
2439 else
2440 rettv->vval.v_number = FALSE;
2441}
2442
2443/*
Ernie Rael659c2402022-04-24 18:40:28 +01002444 * Fill in the empty dictionary with items as defined by maparg builtin.
2445 */
2446 static void
2447mapblock2dict(
2448 mapblock_T *mp,
2449 dict_T *dict,
2450 char_u *lhsrawalt, // may be NULL
Ernie Rael51d04d12022-05-04 15:40:22 +01002451 int buffer_local, // false if not buffer local mapping
2452 int abbr) // true if abbreviation
Ernie Rael659c2402022-04-24 18:40:28 +01002453{
zeertzjqcdc83932022-09-12 13:38:41 +01002454 char_u *lhs = str2special_save(mp->m_keys, TRUE, FALSE);
Ernie Rael659c2402022-04-24 18:40:28 +01002455 char_u *mapmode = map_mode_to_chars(mp->m_mode);
2456
2457 dict_add_string(dict, "lhs", lhs);
2458 vim_free(lhs);
2459 dict_add_string(dict, "lhsraw", mp->m_keys);
2460 if (lhsrawalt)
2461 // Also add the value for the simplified entry.
2462 dict_add_string(dict, "lhsrawalt", lhsrawalt);
2463 dict_add_string(dict, "rhs", mp->m_orig_str);
2464 dict_add_number(dict, "noremap", mp->m_noremap ? 1L : 0L);
2465 dict_add_number(dict, "script", mp->m_noremap == REMAP_SCRIPT
2466 ? 1L : 0L);
2467 dict_add_number(dict, "expr", mp->m_expr ? 1L : 0L);
2468 dict_add_number(dict, "silent", mp->m_silent ? 1L : 0L);
2469 dict_add_number(dict, "sid", (long)mp->m_script_ctx.sc_sid);
2470 dict_add_number(dict, "scriptversion",
2471 (long)mp->m_script_ctx.sc_version);
2472 dict_add_number(dict, "lnum", (long)mp->m_script_ctx.sc_lnum);
2473 dict_add_number(dict, "buffer", (long)buffer_local);
2474 dict_add_number(dict, "nowait", mp->m_nowait ? 1L : 0L);
2475 dict_add_string(dict, "mode", mapmode);
Ernie Rael51d04d12022-05-04 15:40:22 +01002476 dict_add_number(dict, "abbr", abbr ? 1L : 0L);
Ernie Raeld8f5f762022-05-10 17:50:39 +01002477 dict_add_number(dict, "mode_bits", mp->m_mode);
Ernie Rael659c2402022-04-24 18:40:28 +01002478
2479 vim_free(mapmode);
2480}
2481
Yegappan Lakshmanan4a155042021-07-30 21:32:45 +02002482 static void
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002483get_maparg(typval_T *argvars, typval_T *rettv, int exact)
2484{
2485 char_u *keys;
Bram Moolenaar9c652532020-05-24 13:10:18 +02002486 char_u *keys_simplified;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002487 char_u *which;
2488 char_u buf[NUMBUFLEN];
2489 char_u *keys_buf = NULL;
Bram Moolenaar9c652532020-05-24 13:10:18 +02002490 char_u *alt_keys_buf = NULL;
2491 int did_simplify = FALSE;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002492 char_u *rhs;
2493 int mode;
2494 int abbr = FALSE;
2495 int get_dict = FALSE;
zeertzjq2c8a7eb2022-04-26 21:36:21 +01002496 mapblock_T *mp = NULL;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002497 int buffer_local;
Bram Moolenaar9c652532020-05-24 13:10:18 +02002498 int flags = REPTERM_FROM_PART | REPTERM_DO_LT;
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002499
2500 // return empty string for failure
2501 rettv->v_type = VAR_STRING;
2502 rettv->vval.v_string = NULL;
2503
2504 keys = tv_get_string(&argvars[0]);
2505 if (*keys == NUL)
2506 return;
2507
2508 if (argvars[1].v_type != VAR_UNKNOWN)
2509 {
2510 which = tv_get_string_buf_chk(&argvars[1], buf);
2511 if (argvars[2].v_type != VAR_UNKNOWN)
2512 {
Bram Moolenaar04d594b2020-09-02 22:25:35 +02002513 abbr = (int)tv_get_bool(&argvars[2]);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002514 if (argvars[3].v_type != VAR_UNKNOWN)
Bram Moolenaar04d594b2020-09-02 22:25:35 +02002515 get_dict = (int)tv_get_bool(&argvars[3]);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002516 }
2517 }
2518 else
2519 which = (char_u *)"";
2520 if (which == NULL)
2521 return;
2522
2523 mode = get_map_mode(&which, 0);
2524
zeertzjq7e0bae02023-08-11 23:15:38 +02002525 keys_simplified = replace_termcodes(keys, &keys_buf, 0, flags,
2526 &did_simplify);
Bram Moolenaar9c652532020-05-24 13:10:18 +02002527 rhs = check_map(keys_simplified, mode, exact, FALSE, abbr,
2528 &mp, &buffer_local);
2529 if (did_simplify)
2530 {
2531 // When the lhs is being simplified the not-simplified keys are
Bram Moolenaar8e7d6222020-12-18 19:49:56 +01002532 // preferred for printing, like in do_map().
zeertzjq7e0bae02023-08-11 23:15:38 +02002533 (void)replace_termcodes(keys, &alt_keys_buf, 0,
Bram Moolenaar9c652532020-05-24 13:10:18 +02002534 flags | REPTERM_NO_SIMPLIFY, NULL);
2535 rhs = check_map(alt_keys_buf, mode, exact, FALSE, abbr, &mp,
2536 &buffer_local);
2537 }
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002538
2539 if (!get_dict)
2540 {
2541 // Return a string.
2542 if (rhs != NULL)
2543 {
2544 if (*rhs == NUL)
2545 rettv->vval.v_string = vim_strsave((char_u *)"<Nop>");
2546 else
zeertzjqcdc83932022-09-12 13:38:41 +01002547 rettv->vval.v_string = str2special_save(rhs, FALSE, FALSE);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002548 }
2549
2550 }
Bram Moolenaar93a10962022-06-16 11:42:09 +01002551 else if (rettv_dict_alloc(rettv) == OK && rhs != NULL)
Ernie Rael659c2402022-04-24 18:40:28 +01002552 mapblock2dict(mp, rettv->vval.v_dict,
Ernie Rael51d04d12022-05-04 15:40:22 +01002553 did_simplify ? keys_simplified : NULL,
2554 buffer_local, abbr);
Bram Moolenaar9c652532020-05-24 13:10:18 +02002555
2556 vim_free(keys_buf);
2557 vim_free(alt_keys_buf);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002558}
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002559
2560/*
Ernie Rael09661202022-04-25 14:40:44 +01002561 * "maplist()" function
Ernie Rael659c2402022-04-24 18:40:28 +01002562 */
2563 void
Ernie Rael09661202022-04-25 14:40:44 +01002564f_maplist(typval_T *argvars UNUSED, typval_T *rettv)
Ernie Rael659c2402022-04-24 18:40:28 +01002565{
2566 dict_T *d;
2567 mapblock_T *mp;
2568 int buffer_local;
2569 char_u *keys_buf;
2570 int did_simplify;
2571 int hash;
2572 char_u *lhs;
2573 const int flags = REPTERM_FROM_PART | REPTERM_DO_LT;
Ernie Rael09661202022-04-25 14:40:44 +01002574 int abbr = FALSE;
2575
2576 if (in_vim9script() && check_for_opt_bool_arg(argvars, 0) == FAIL)
2577 return;
2578 if (argvars[0].v_type != VAR_UNKNOWN)
2579 abbr = tv_get_bool(&argvars[0]);
Ernie Rael659c2402022-04-24 18:40:28 +01002580
Bram Moolenaar93a10962022-06-16 11:42:09 +01002581 if (rettv_list_alloc(rettv) == FAIL)
Ernie Rael659c2402022-04-24 18:40:28 +01002582 return;
2583
2584 validate_maphash();
2585
2586 // Do it twice: once for global maps and once for local maps.
2587 for (buffer_local = 0; buffer_local <= 1; ++buffer_local)
2588 {
2589 for (hash = 0; hash < 256; ++hash)
2590 {
Ernie Rael09661202022-04-25 14:40:44 +01002591 if (abbr)
2592 {
2593 if (hash > 0) // there is only one abbr list
2594 break;
2595 if (buffer_local)
2596 mp = curbuf->b_first_abbr;
2597 else
2598 mp = first_abbr;
2599 }
2600 else if (buffer_local)
Ernie Rael659c2402022-04-24 18:40:28 +01002601 mp = curbuf->b_maphash[hash];
2602 else
2603 mp = maphash[hash];
2604 for (; mp; mp = mp->m_next)
2605 {
2606 if (mp->m_simplified)
2607 continue;
2608 if ((d = dict_alloc()) == NULL)
2609 return;
2610 if (list_append_dict(rettv->vval.v_list, d) == FAIL)
Christian Brabandt29269a72024-04-16 22:44:31 +02002611 {
2612 dict_unref(d);
Ernie Rael659c2402022-04-24 18:40:28 +01002613 return;
Christian Brabandt29269a72024-04-16 22:44:31 +02002614 }
Ernie Rael659c2402022-04-24 18:40:28 +01002615
2616 keys_buf = NULL;
2617 did_simplify = FALSE;
2618
zeertzjqcdc83932022-09-12 13:38:41 +01002619 lhs = str2special_save(mp->m_keys, TRUE, FALSE);
zeertzjq7e0bae02023-08-11 23:15:38 +02002620 (void)replace_termcodes(lhs, &keys_buf, 0, flags,
2621 &did_simplify);
Ernie Rael659c2402022-04-24 18:40:28 +01002622 vim_free(lhs);
2623
2624 mapblock2dict(mp, d,
Ernie Rael51d04d12022-05-04 15:40:22 +01002625 did_simplify ? keys_buf : NULL,
2626 buffer_local, abbr);
Ernie Rael659c2402022-04-24 18:40:28 +01002627 vim_free(keys_buf);
2628 }
2629 }
2630 }
2631}
2632
2633/*
Yegappan Lakshmanan4a155042021-07-30 21:32:45 +02002634 * "maparg()" function
2635 */
2636 void
2637f_maparg(typval_T *argvars, typval_T *rettv)
2638{
2639 if (in_vim9script()
2640 && (check_for_string_arg(argvars, 0) == FAIL
2641 || check_for_opt_string_arg(argvars, 1) == FAIL
2642 || (argvars[1].v_type != VAR_UNKNOWN
2643 && (check_for_opt_bool_arg(argvars, 2) == FAIL
2644 || (argvars[2].v_type != VAR_UNKNOWN
2645 && check_for_opt_bool_arg(argvars, 3) == FAIL)))))
2646 return;
2647
2648 get_maparg(argvars, rettv, TRUE);
2649}
2650
2651/*
2652 * "mapcheck()" function
2653 */
2654 void
2655f_mapcheck(typval_T *argvars, typval_T *rettv)
2656{
2657 if (in_vim9script()
2658 && (check_for_string_arg(argvars, 0) == FAIL
2659 || check_for_opt_string_arg(argvars, 1) == FAIL
2660 || (argvars[1].v_type != VAR_UNKNOWN
2661 && check_for_opt_bool_arg(argvars, 2) == FAIL)))
2662 return;
2663
2664 get_maparg(argvars, rettv, FALSE);
2665}
2666
2667/*
Ernie Rael51d04d12022-05-04 15:40:22 +01002668 * Get the mapping mode from the mode string.
2669 * It may contain multiple characters, eg "nox", or "!", or ' '
2670 * Return 0 if there is an error.
2671 */
2672 static int
2673get_map_mode_string(char_u *mode_string, int abbr)
2674{
2675 char_u *p = mode_string;
2676 int mode = 0;
2677 int tmode;
2678 int modec;
Bram Moolenaar24959102022-05-07 20:01:16 +01002679 const int MASK_V = MODE_VISUAL | MODE_SELECT;
2680 const int MASK_MAP = MODE_VISUAL | MODE_SELECT | MODE_NORMAL
2681 | MODE_OP_PENDING;
2682 const int MASK_BANG = MODE_INSERT | MODE_CMDLINE;
Ernie Rael51d04d12022-05-04 15:40:22 +01002683
2684 if (*p == NUL)
2685 p = (char_u *)" "; // compatibility
2686 while ((modec = *p++))
2687 {
2688 switch (modec)
2689 {
Bram Moolenaar24959102022-05-07 20:01:16 +01002690 case 'i': tmode = MODE_INSERT; break;
2691 case 'l': tmode = MODE_LANGMAP; break;
2692 case 'c': tmode = MODE_CMDLINE; break;
2693 case 'n': tmode = MODE_NORMAL; break;
2694 case 'x': tmode = MODE_VISUAL; break;
2695 case 's': tmode = MODE_SELECT; break;
2696 case 'o': tmode = MODE_OP_PENDING; break;
2697 case 't': tmode = MODE_TERMINAL; break;
Ernie Rael51d04d12022-05-04 15:40:22 +01002698 case 'v': tmode = MASK_V; break;
2699 case '!': tmode = MASK_BANG; break;
2700 case ' ': tmode = MASK_MAP; break;
2701 default:
2702 return 0; // error, unknown mode character
2703 }
2704 mode |= tmode;
2705 }
2706 if ((abbr && (mode & ~MASK_BANG) != 0)
2707 || (!abbr && (mode & (mode-1)) != 0 // more than one bit set
2708 && (
2709 // false if multiple bits set in mode and mode is fully
2710 // contained in one mask
2711 !(((mode & MASK_BANG) != 0 && (mode & ~MASK_BANG) == 0)
2712 || ((mode & MASK_MAP) != 0 && (mode & ~MASK_MAP) == 0)))))
2713 return 0;
2714
2715 return mode;
2716}
2717
2718/*
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002719 * "mapset()" function
2720 */
2721 void
2722f_mapset(typval_T *argvars, typval_T *rettv UNUSED)
2723{
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002724 char_u *which;
2725 int mode;
2726 char_u buf[NUMBUFLEN];
2727 int is_abbr;
2728 dict_T *d;
2729 char_u *lhs;
Bram Moolenaar9c652532020-05-24 13:10:18 +02002730 char_u *lhsraw;
2731 char_u *lhsrawalt;
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002732 char_u *rhs;
Bram Moolenaarc94c1462020-05-22 20:01:06 +02002733 char_u *orig_rhs;
2734 char_u *arg_buf = NULL;
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002735 int noremap;
2736 int expr;
2737 int silent;
Bram Moolenaar7ba1e4d2021-04-24 13:12:38 +02002738 int buffer;
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002739 scid_T sid;
Bram Moolenaara9528b32022-01-18 20:51:35 +00002740 int scriptversion;
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002741 linenr_T lnum;
2742 mapblock_T **map_table = maphash;
2743 mapblock_T **abbr_table = &first_abbr;
2744 int nowait;
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002745 char_u *arg;
Ernie Rael51d04d12022-05-04 15:40:22 +01002746 int dict_only;
zeertzjq9d997ad2024-07-29 21:10:07 +02002747 mapblock_T *mp_result[2] = {NULL, NULL};
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002748
Ernie Rael51d04d12022-05-04 15:40:22 +01002749 // If first arg is a dict, then that's the only arg permitted.
2750 dict_only = argvars[0].v_type == VAR_DICT;
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02002751 if (in_vim9script()
Ernie Rael51d04d12022-05-04 15:40:22 +01002752 && (check_for_string_or_dict_arg(argvars, 0) == FAIL
2753 || (dict_only && check_for_unknown_arg(argvars, 1) == FAIL)
2754 || (!dict_only
2755 && (check_for_string_arg(argvars, 0) == FAIL
2756 || check_for_bool_arg(argvars, 1) == FAIL
2757 || check_for_dict_arg(argvars, 2) == FAIL))))
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02002758 return;
2759
Ernie Rael51d04d12022-05-04 15:40:22 +01002760 if (dict_only)
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002761 {
Ernie Rael51d04d12022-05-04 15:40:22 +01002762 d = argvars[0].vval.v_dict;
Bram Moolenaard61efa52022-07-23 09:52:04 +01002763 which = dict_get_string(d, "mode", FALSE);
2764 is_abbr = dict_get_bool(d, "abbr", -1);
Ernie Rael51d04d12022-05-04 15:40:22 +01002765 if (which == NULL || is_abbr < 0)
2766 {
2767 emsg(_(e_entries_missing_in_mapset_dict_argument));
2768 return;
2769 }
2770 }
2771 else
2772 {
2773 which = tv_get_string_buf_chk(&argvars[0], buf);
2774 if (which == NULL)
2775 return;
2776 is_abbr = (int)tv_get_bool(&argvars[1]);
2777
Yegappan Lakshmanan04c4c572022-08-30 19:48:24 +01002778 if (check_for_dict_arg(argvars, 2) == FAIL)
Ernie Rael51d04d12022-05-04 15:40:22 +01002779 return;
Ernie Rael51d04d12022-05-04 15:40:22 +01002780 d = argvars[2].vval.v_dict;
2781 }
2782 mode = get_map_mode_string(which, is_abbr);
2783 if (mode == 0)
2784 {
2785 semsg(_(e_illegal_map_mode_string_str), which);
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002786 return;
2787 }
Ernie Rael51d04d12022-05-04 15:40:22 +01002788
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002789
2790 // Get the values in the same order as above in get_maparg().
Bram Moolenaard61efa52022-07-23 09:52:04 +01002791 lhs = dict_get_string(d, "lhs", FALSE);
2792 lhsraw = dict_get_string(d, "lhsraw", FALSE);
2793 lhsrawalt = dict_get_string(d, "lhsrawalt", FALSE);
2794 rhs = dict_get_string(d, "rhs", FALSE);
Bram Moolenaar9c652532020-05-24 13:10:18 +02002795 if (lhs == NULL || lhsraw == NULL || rhs == NULL)
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002796 {
Bram Moolenaarb09feaa2022-01-02 20:20:45 +00002797 emsg(_(e_entries_missing_in_mapset_dict_argument));
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002798 return;
2799 }
Bram Moolenaarc94c1462020-05-22 20:01:06 +02002800 orig_rhs = rhs;
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002801
Bram Moolenaard61efa52022-07-23 09:52:04 +01002802 noremap = dict_get_number(d, "noremap") ? REMAP_NONE: 0;
2803 if (dict_get_number(d, "script") != 0)
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002804 noremap = REMAP_SCRIPT;
Bram Moolenaard61efa52022-07-23 09:52:04 +01002805 expr = dict_get_number(d, "expr") != 0;
2806 silent = dict_get_number(d, "silent") != 0;
2807 sid = dict_get_number(d, "sid");
2808 scriptversion = dict_get_number(d, "scriptversion");
2809 lnum = dict_get_number(d, "lnum");
2810 buffer = dict_get_number(d, "buffer");
2811 nowait = dict_get_number(d, "nowait") != 0;
Bram Moolenaar7ba1e4d2021-04-24 13:12:38 +02002812 // mode from the dict is not used
2813
zeertzjq7e0bae02023-08-11 23:15:38 +02002814 if (STRICMP(rhs, "<nop>") == 0) // "<Nop>" means nothing
2815 rhs = (char_u *)"";
2816 else
2817 rhs = replace_termcodes(rhs, &arg_buf, sid,
2818 REPTERM_DO_LT | REPTERM_SPECIAL, NULL);
2819
Bram Moolenaar7ba1e4d2021-04-24 13:12:38 +02002820 if (buffer)
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002821 {
2822 map_table = curbuf->b_maphash;
2823 abbr_table = &curbuf->b_first_abbr;
2824 }
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002825
2826 // Delete any existing mapping for this lhs and mode.
Bram Moolenaar7ba1e4d2021-04-24 13:12:38 +02002827 if (buffer)
2828 {
2829 arg = alloc(STRLEN(lhs) + STRLEN("<buffer>") + 1);
2830 if (arg == NULL)
2831 return;
2832 STRCPY(arg, "<buffer>");
2833 STRCPY(arg + 8, lhs);
2834 }
2835 else
2836 {
2837 arg = vim_strsave(lhs);
2838 if (arg == NULL)
2839 return;
2840 }
zeertzjqfdf135a2024-10-24 21:43:43 +02002841 do_map(MAPTYPE_UNMAP_LHS, arg, mode, is_abbr);
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002842 vim_free(arg);
2843
zeertzjq9d997ad2024-07-29 21:10:07 +02002844 mp_result[0] = map_add(map_table, abbr_table, lhsraw, rhs, orig_rhs,
2845 noremap, nowait, silent, mode, is_abbr, expr, sid,
2846 scriptversion, lnum, 0);
Bram Moolenaar9c652532020-05-24 13:10:18 +02002847 if (lhsrawalt != NULL)
zeertzjq9d997ad2024-07-29 21:10:07 +02002848 mp_result[1] = map_add(map_table, abbr_table, lhsrawalt, rhs, orig_rhs,
2849 noremap, nowait, silent, mode, is_abbr, expr, sid,
2850 scriptversion, lnum, 1);
2851
2852 if (mp_result[0] != NULL && mp_result[1] != NULL)
2853 {
2854 mp_result[0]->m_alt = mp_result[1];
2855 mp_result[1]->m_alt = mp_result[0];
2856 }
2857
Bram Moolenaarc94c1462020-05-22 20:01:06 +02002858 vim_free(arg_buf);
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002859}
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002860#endif
2861
Bram Moolenaar4c9243f2020-05-22 13:10:44 +02002862
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002863#if defined(MSWIN) || defined(MACOS_X)
2864
Bram Moolenaar24959102022-05-07 20:01:16 +01002865# define VIS_SEL (MODE_VISUAL | MODE_SELECT) // abbreviation
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002866
2867/*
2868 * Default mappings for some often used keys.
2869 */
2870struct initmap
2871{
2872 char_u *arg;
2873 int mode;
2874};
2875
2876# ifdef FEAT_GUI_MSWIN
2877// Use the Windows (CUA) keybindings. (GUI)
2878static struct initmap initmappings[] =
2879{
2880 // paste, copy and cut
Bram Moolenaar24959102022-05-07 20:01:16 +01002881 {(char_u *)"<S-Insert> \"*P", MODE_NORMAL},
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002882 {(char_u *)"<S-Insert> \"-d\"*P", VIS_SEL},
Bram Moolenaar24959102022-05-07 20:01:16 +01002883 {(char_u *)"<S-Insert> <C-R><C-O>*", MODE_INSERT | MODE_CMDLINE},
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002884 {(char_u *)"<C-Insert> \"*y", VIS_SEL},
2885 {(char_u *)"<S-Del> \"*d", VIS_SEL},
2886 {(char_u *)"<C-Del> \"*d", VIS_SEL},
2887 {(char_u *)"<C-X> \"*d", VIS_SEL},
2888 // Missing: CTRL-C (cancel) and CTRL-V (block selection)
2889};
2890# endif
2891
2892# if defined(MSWIN) && (!defined(FEAT_GUI) || defined(VIMDLL))
2893// Use the Windows (CUA) keybindings. (Console)
2894static struct initmap cinitmappings[] =
2895{
Bram Moolenaar24959102022-05-07 20:01:16 +01002896 {(char_u *)"\316w <C-Home>", MODE_NORMAL | VIS_SEL},
2897 {(char_u *)"\316w <C-Home>", MODE_INSERT | MODE_CMDLINE},
2898 {(char_u *)"\316u <C-End>", MODE_NORMAL | VIS_SEL},
2899 {(char_u *)"\316u <C-End>", MODE_INSERT | MODE_CMDLINE},
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002900
2901 // paste, copy and cut
2902# ifdef FEAT_CLIPBOARD
Bram Moolenaar24959102022-05-07 20:01:16 +01002903 {(char_u *)"\316\324 \"*P", MODE_NORMAL}, // SHIFT-Insert is "*P
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002904 {(char_u *)"\316\324 \"-d\"*P", VIS_SEL}, // SHIFT-Insert is "-d"*P
Bram Moolenaar24959102022-05-07 20:01:16 +01002905 {(char_u *)"\316\324 \022\017*", MODE_INSERT}, // SHIFT-Insert is ^R^O*
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002906 {(char_u *)"\316\325 \"*y", VIS_SEL}, // CTRL-Insert is "*y
2907 {(char_u *)"\316\327 \"*d", VIS_SEL}, // SHIFT-Del is "*d
2908 {(char_u *)"\316\330 \"*d", VIS_SEL}, // CTRL-Del is "*d
2909 {(char_u *)"\030 \"*d", VIS_SEL}, // CTRL-X is "*d
2910# else
Bram Moolenaar24959102022-05-07 20:01:16 +01002911 {(char_u *)"\316\324 P", MODE_NORMAL}, // SHIFT-Insert is P
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002912 {(char_u *)"\316\324 \"-dP", VIS_SEL}, // SHIFT-Insert is "-dP
Bram Moolenaar24959102022-05-07 20:01:16 +01002913 {(char_u *)"\316\324 \022\017\"", MODE_INSERT}, // SHIFT-Insert is ^R^O"
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002914 {(char_u *)"\316\325 y", VIS_SEL}, // CTRL-Insert is y
2915 {(char_u *)"\316\327 d", VIS_SEL}, // SHIFT-Del is d
2916 {(char_u *)"\316\330 d", VIS_SEL}, // CTRL-Del is d
2917# endif
2918};
2919# endif
2920
2921# if defined(MACOS_X)
2922static struct initmap initmappings[] =
2923{
2924 // Use the Standard MacOS binding.
2925 // paste, copy and cut
Bram Moolenaar24959102022-05-07 20:01:16 +01002926 {(char_u *)"<D-v> \"*P", MODE_NORMAL},
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002927 {(char_u *)"<D-v> \"-d\"*P", VIS_SEL},
Bram Moolenaar24959102022-05-07 20:01:16 +01002928 {(char_u *)"<D-v> <C-R>*", MODE_INSERT | MODE_CMDLINE},
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002929 {(char_u *)"<D-c> \"*y", VIS_SEL},
2930 {(char_u *)"<D-x> \"*d", VIS_SEL},
2931 {(char_u *)"<Backspace> \"-d", VIS_SEL},
2932};
2933# endif
2934
2935# undef VIS_SEL
2936#endif
2937
2938/*
2939 * Set up default mappings.
2940 */
2941 void
2942init_mappings(void)
2943{
2944#if defined(MSWIN) || defined(MACOS_X)
2945 int i;
2946
2947# if defined(MSWIN) && (!defined(FEAT_GUI_MSWIN) || defined(VIMDLL))
2948# ifdef VIMDLL
2949 if (!gui.starting)
2950# endif
2951 {
K.Takataeeec2542021-06-02 13:28:16 +02002952 for (i = 0; i < (int)ARRAY_LENGTH(cinitmappings); ++i)
zeertzjq44068e92022-06-16 11:14:55 +01002953 add_map(cinitmappings[i].arg, cinitmappings[i].mode, FALSE);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002954 }
2955# endif
2956# if defined(FEAT_GUI_MSWIN) || defined(MACOS_X)
K.Takataeeec2542021-06-02 13:28:16 +02002957 for (i = 0; i < (int)ARRAY_LENGTH(initmappings); ++i)
zeertzjq44068e92022-06-16 11:14:55 +01002958 add_map(initmappings[i].arg, initmappings[i].mode, FALSE);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002959# endif
2960#endif
2961}
2962
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002963/*
2964 * Add a mapping "map" for mode "mode".
zeertzjq44068e92022-06-16 11:14:55 +01002965 * When "nore" is TRUE use MAPTYPE_NOREMAP.
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002966 * Need to put string in allocated memory, because do_map() will modify it.
2967 */
2968 void
zeertzjq44068e92022-06-16 11:14:55 +01002969add_map(char_u *map, int mode, int nore)
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002970{
2971 char_u *s;
2972 char_u *cpo_save = p_cpo;
2973
Bram Moolenaare5a2dc82021-01-03 19:52:05 +01002974 p_cpo = empty_option; // Allow <> notation
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002975 s = vim_strsave(map);
2976 if (s != NULL)
2977 {
zeertzjq44068e92022-06-16 11:14:55 +01002978 (void)do_map(nore ? MAPTYPE_NOREMAP : MAPTYPE_MAP, s, mode, FALSE);
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002979 vim_free(s);
2980 }
2981 p_cpo = cpo_save;
2982}
Bram Moolenaarb66bab32019-08-01 14:28:24 +02002983
Bram Moolenaare677df82019-09-02 22:31:11 +02002984#if defined(FEAT_LANGMAP) || defined(PROTO)
2985/*
2986 * Any character has an equivalent 'langmap' character. This is used for
2987 * keyboards that have a special language mode that sends characters above
2988 * 128 (although other characters can be translated too). The "to" field is a
2989 * Vim command character. This avoids having to switch the keyboard back to
2990 * ASCII mode when leaving Insert mode.
2991 *
2992 * langmap_mapchar[] maps any of 256 chars to an ASCII char used for Vim
2993 * commands.
2994 * langmap_mapga.ga_data is a sorted table of langmap_entry_T. This does the
2995 * same as langmap_mapchar[] for characters >= 256.
2996 *
2997 * Use growarray for 'langmap' chars >= 256
2998 */
2999typedef struct
3000{
3001 int from;
3002 int to;
3003} langmap_entry_T;
3004
3005static garray_T langmap_mapga;
3006
3007/*
3008 * Search for an entry in "langmap_mapga" for "from". If found set the "to"
3009 * field. If not found insert a new entry at the appropriate location.
3010 */
3011 static void
3012langmap_set_entry(int from, int to)
3013{
3014 langmap_entry_T *entries = (langmap_entry_T *)(langmap_mapga.ga_data);
3015 int a = 0;
3016 int b = langmap_mapga.ga_len;
3017
3018 // Do a binary search for an existing entry.
3019 while (a != b)
3020 {
3021 int i = (a + b) / 2;
3022 int d = entries[i].from - from;
3023
3024 if (d == 0)
3025 {
3026 entries[i].to = to;
3027 return;
3028 }
3029 if (d < 0)
3030 a = i + 1;
3031 else
3032 b = i;
3033 }
3034
Yegappan Lakshmananfadc02a2023-01-27 21:03:12 +00003035 if (ga_grow(&langmap_mapga, 1) == FAIL)
Bram Moolenaare677df82019-09-02 22:31:11 +02003036 return; // out of memory
3037
3038 // insert new entry at position "a"
3039 entries = (langmap_entry_T *)(langmap_mapga.ga_data) + a;
3040 mch_memmove(entries + 1, entries,
3041 (langmap_mapga.ga_len - a) * sizeof(langmap_entry_T));
3042 ++langmap_mapga.ga_len;
3043 entries[0].from = from;
3044 entries[0].to = to;
3045}
3046
3047/*
3048 * Apply 'langmap' to multi-byte character "c" and return the result.
3049 */
3050 int
3051langmap_adjust_mb(int c)
3052{
3053 langmap_entry_T *entries = (langmap_entry_T *)(langmap_mapga.ga_data);
3054 int a = 0;
3055 int b = langmap_mapga.ga_len;
3056
3057 while (a != b)
3058 {
3059 int i = (a + b) / 2;
3060 int d = entries[i].from - c;
3061
3062 if (d == 0)
3063 return entries[i].to; // found matching entry
3064 if (d < 0)
3065 a = i + 1;
3066 else
3067 b = i;
3068 }
3069 return c; // no entry found, return "c" unmodified
3070}
3071
3072 void
3073langmap_init(void)
3074{
3075 int i;
3076
3077 for (i = 0; i < 256; i++)
3078 langmap_mapchar[i] = i; // we init with a one-to-one map
3079 ga_init2(&langmap_mapga, sizeof(langmap_entry_T), 8);
3080}
3081
3082/*
3083 * Called when langmap option is set; the language map can be
3084 * changed at any time!
3085 */
Yegappan Lakshmananaf936912023-02-20 12:16:39 +00003086 char *
3087did_set_langmap(optset_T *args UNUSED)
Bram Moolenaare677df82019-09-02 22:31:11 +02003088{
3089 char_u *p;
3090 char_u *p2;
3091 int from, to;
3092
3093 ga_clear(&langmap_mapga); // clear the previous map first
3094 langmap_init(); // back to one-to-one map
3095
3096 for (p = p_langmap; p[0] != NUL; )
3097 {
3098 for (p2 = p; p2[0] != NUL && p2[0] != ',' && p2[0] != ';';
3099 MB_PTR_ADV(p2))
3100 {
3101 if (p2[0] == '\\' && p2[1] != NUL)
3102 ++p2;
3103 }
3104 if (p2[0] == ';')
3105 ++p2; // abcd;ABCD form, p2 points to A
3106 else
3107 p2 = NULL; // aAbBcCdD form, p2 is NULL
3108 while (p[0])
3109 {
3110 if (p[0] == ',')
3111 {
3112 ++p;
3113 break;
3114 }
3115 if (p[0] == '\\' && p[1] != NUL)
3116 ++p;
3117 from = (*mb_ptr2char)(p);
3118 to = NUL;
3119 if (p2 == NULL)
3120 {
3121 MB_PTR_ADV(p);
3122 if (p[0] != ',')
3123 {
3124 if (p[0] == '\\')
3125 ++p;
3126 to = (*mb_ptr2char)(p);
3127 }
3128 }
3129 else
3130 {
3131 if (p2[0] != ',')
3132 {
3133 if (p2[0] == '\\')
3134 ++p2;
3135 to = (*mb_ptr2char)(p2);
3136 }
3137 }
3138 if (to == NUL)
3139 {
Yegappan Lakshmanan5da901b2023-02-27 12:47:47 +00003140 sprintf(args->os_errbuf,
3141 _(e_langmap_matching_character_missing_for_str),
3142 transchar(from));
3143 return args->os_errbuf;
Bram Moolenaare677df82019-09-02 22:31:11 +02003144 }
3145
3146 if (from >= 256)
3147 langmap_set_entry(from, to);
3148 else
3149 langmap_mapchar[from & 255] = to;
3150
3151 // Advance to next pair
3152 MB_PTR_ADV(p);
3153 if (p2 != NULL)
3154 {
3155 MB_PTR_ADV(p2);
3156 if (*p == ';')
3157 {
3158 p = p2;
3159 if (p[0] != NUL)
3160 {
3161 if (p[0] != ',')
3162 {
Zoltan Arpadffy1c8e2332023-12-05 16:04:23 +01003163 vim_snprintf(args->os_errbuf, args->os_errbuflen,
Yegappan Lakshmanan5da901b2023-02-27 12:47:47 +00003164 _(e_langmap_extra_characters_after_semicolon_str),
3165 p);
3166 return args->os_errbuf;
Bram Moolenaare677df82019-09-02 22:31:11 +02003167 }
3168 ++p;
3169 }
3170 break;
3171 }
3172 }
3173 }
3174 }
Yegappan Lakshmananaf936912023-02-20 12:16:39 +00003175
3176 return NULL;
Bram Moolenaare677df82019-09-02 22:31:11 +02003177}
3178#endif
3179
Bram Moolenaarb66bab32019-08-01 14:28:24 +02003180 static void
3181do_exmap(exarg_T *eap, int isabbrev)
3182{
3183 int mode;
3184 char_u *cmdp;
3185
3186 cmdp = eap->cmd;
3187 mode = get_map_mode(&cmdp, eap->forceit || isabbrev);
3188
zeertzjq44068e92022-06-16 11:14:55 +01003189 switch (do_map(*cmdp == 'n' ? MAPTYPE_NOREMAP
3190 : *cmdp == 'u' ? MAPTYPE_UNMAP : MAPTYPE_MAP,
Bram Moolenaarb66bab32019-08-01 14:28:24 +02003191 eap->arg, mode, isabbrev))
3192 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00003193 case 1: emsg(_(e_invalid_argument));
Bram Moolenaarb66bab32019-08-01 14:28:24 +02003194 break;
Bram Moolenaare29a27f2021-07-20 21:07:36 +02003195 case 2: emsg((isabbrev ? _(e_no_such_abbreviation)
3196 : _(e_no_such_mapping)));
Bram Moolenaarb66bab32019-08-01 14:28:24 +02003197 break;
3198 }
3199}
3200
3201/*
3202 * ":abbreviate" and friends.
3203 */
3204 void
3205ex_abbreviate(exarg_T *eap)
3206{
3207 do_exmap(eap, TRUE); // almost the same as mapping
3208}
3209
3210/*
3211 * ":map" and friends.
3212 */
3213 void
3214ex_map(exarg_T *eap)
3215{
3216 // If we are sourcing .exrc or .vimrc in current directory we
3217 // print the mappings for security reasons.
3218 if (secure)
3219 {
3220 secure = 2;
3221 msg_outtrans(eap->cmd);
3222 msg_putchar('\n');
3223 }
3224 do_exmap(eap, FALSE);
3225}
3226
3227/*
3228 * ":unmap" and friends.
3229 */
3230 void
3231ex_unmap(exarg_T *eap)
3232{
3233 do_exmap(eap, FALSE);
3234}
3235
3236/*
3237 * ":mapclear" and friends.
3238 */
3239 void
3240ex_mapclear(exarg_T *eap)
3241{
3242 map_clear(eap->cmd, eap->arg, eap->forceit, FALSE);
3243}
3244
3245/*
3246 * ":abclear" and friends.
3247 */
3248 void
3249ex_abclear(exarg_T *eap)
3250{
3251 map_clear(eap->cmd, eap->arg, TRUE, TRUE);
3252}