1 | // This is an open source non-commercial project. Dear PVS-Studio, please check |
2 | // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com |
3 | |
4 | /* |
5 | * getchar.c |
6 | * |
7 | * functions related with getting a character from the user/mapping/redo/... |
8 | * |
9 | * manipulations with redo buffer and stuff buffer |
10 | * mappings and abbreviations |
11 | */ |
12 | |
13 | #include <assert.h> |
14 | #include <stdbool.h> |
15 | #include <string.h> |
16 | #include <inttypes.h> |
17 | |
18 | #include "nvim/assert.h" |
19 | #include "nvim/vim.h" |
20 | #include "nvim/ascii.h" |
21 | #include "nvim/getchar.h" |
22 | #include "nvim/buffer_defs.h" |
23 | #include "nvim/charset.h" |
24 | #include "nvim/cursor.h" |
25 | #include "nvim/edit.h" |
26 | #include "nvim/eval.h" |
27 | #include "nvim/ex_docmd.h" |
28 | #include "nvim/ex_getln.h" |
29 | #include "nvim/func_attr.h" |
30 | #include "nvim/main.h" |
31 | #include "nvim/mbyte.h" |
32 | #include "nvim/memline.h" |
33 | #include "nvim/memory.h" |
34 | #include "nvim/message.h" |
35 | #include "nvim/misc1.h" |
36 | #include "nvim/keymap.h" |
37 | #include "nvim/garray.h" |
38 | #include "nvim/move.h" |
39 | #include "nvim/normal.h" |
40 | #include "nvim/ops.h" |
41 | #include "nvim/option.h" |
42 | #include "nvim/regexp.h" |
43 | #include "nvim/screen.h" |
44 | #include "nvim/state.h" |
45 | #include "nvim/strings.h" |
46 | #include "nvim/ui.h" |
47 | #include "nvim/undo.h" |
48 | #include "nvim/event/loop.h" |
49 | #include "nvim/os/input.h" |
50 | #include "nvim/os/os.h" |
51 | #include "nvim/os/fileio.h" |
52 | #include "nvim/api/private/handle.h" |
53 | |
54 | |
55 | /// Index in scriptin |
56 | static int curscript = 0; |
57 | FileDescriptor *scriptin[NSCRIPT] = { NULL }; |
58 | |
59 | /* |
60 | * These buffers are used for storing: |
61 | * - stuffed characters: A command that is translated into another command. |
62 | * - redo characters: will redo the last change. |
63 | * - recorded characters: for the "q" command. |
64 | * |
65 | * The bytes are stored like in the typeahead buffer: |
66 | * - K_SPECIAL introduces a special key (two more bytes follow). A literal |
67 | * K_SPECIAL is stored as K_SPECIAL KS_SPECIAL KE_FILLER. |
68 | * - CSI introduces a GUI termcap code (also when gui.in_use is FALSE, |
69 | * otherwise switching the GUI on would make mappings invalid). |
70 | * A literal CSI is stored as CSI KS_EXTRA KE_CSI. |
71 | * These translations are also done on multi-byte characters! |
72 | * |
73 | * Escaping CSI bytes is done by the system-specific input functions, called |
74 | * by ui_inchar(). |
75 | * Escaping K_SPECIAL is done by inchar(). |
76 | * Un-escaping is done by vgetc(). |
77 | */ |
78 | |
79 | #define MINIMAL_SIZE 20 /* minimal size for b_str */ |
80 | |
81 | static buffheader_T redobuff = { { NULL, { NUL } }, NULL, 0, 0 }; |
82 | static buffheader_T old_redobuff = { { NULL, { NUL } }, NULL, 0, 0 }; |
83 | static buffheader_T recordbuff = { { NULL, { NUL } }, NULL, 0, 0 }; |
84 | |
85 | // First read ahead buffer. Used for translated commands. |
86 | static buffheader_T readbuf1 = { { NULL, { NUL } }, NULL, 0, 0 }; |
87 | |
88 | // Second read ahead buffer. Used for redo. |
89 | static buffheader_T readbuf2 = { { NULL, { NUL } }, NULL, 0, 0 }; |
90 | |
91 | static int typeahead_char = 0; /* typeahead char that's not flushed */ |
92 | |
93 | /* |
94 | * when block_redo is TRUE redo buffer will not be changed |
95 | * used by edit() to repeat insertions and 'V' command for redoing |
96 | */ |
97 | static int block_redo = FALSE; |
98 | |
99 | // Make a hash value for a mapping. |
100 | // "mode" is the lower 4 bits of the State for the mapping. |
101 | // "c1" is the first character of the "lhs". |
102 | // Returns a value between 0 and 255, index in maphash. |
103 | // Put Normal/Visual mode mappings mostly separately from Insert/Cmdline mode. |
104 | #define MAP_HASH(mode, \ |
105 | c1) (((mode) & \ |
106 | (NORMAL + VISUAL + SELECTMODE + \ |
107 | OP_PENDING + TERM_FOCUS)) ? (c1) : ((c1) ^ 0x80)) |
108 | |
109 | // Each mapping is put in one of the MAX_MAPHASH hash lists, |
110 | // to speed up finding it. |
111 | static mapblock_T *(maphash[MAX_MAPHASH]); |
112 | static bool maphash_valid = false; |
113 | |
114 | /* |
115 | * List used for abbreviations. |
116 | */ |
117 | static mapblock_T *first_abbr = NULL; /* first entry in abbrlist */ |
118 | |
119 | static int KeyNoremap = 0; /* remapping flags */ |
120 | |
121 | /* |
122 | * Variables used by vgetorpeek() and flush_buffers() |
123 | * |
124 | * typebuf.tb_buf[] contains all characters that are not consumed yet. |
125 | * typebuf.tb_buf[typebuf.tb_off] is the first valid character. |
126 | * typebuf.tb_buf[typebuf.tb_off + typebuf.tb_len - 1] is the last valid char. |
127 | * typebuf.tb_buf[typebuf.tb_off + typebuf.tb_len] must be NUL. |
128 | * The head of the buffer may contain the result of mappings, abbreviations |
129 | * and @a commands. The length of this part is typebuf.tb_maplen. |
130 | * typebuf.tb_silent is the part where <silent> applies. |
131 | * After the head are characters that come from the terminal. |
132 | * typebuf.tb_no_abbr_cnt is the number of characters in typebuf.tb_buf that |
133 | * should not be considered for abbreviations. |
134 | * Some parts of typebuf.tb_buf may not be mapped. These parts are remembered |
135 | * in typebuf.tb_noremap[], which is the same length as typebuf.tb_buf and |
136 | * contains RM_NONE for the characters that are not to be remapped. |
137 | * typebuf.tb_noremap[typebuf.tb_off] is the first valid flag. |
138 | * (typebuf has been put in globals.h, because check_termcode() needs it). |
139 | */ |
140 | #define RM_YES 0 /* tb_noremap: remap */ |
141 | #define RM_NONE 1 /* tb_noremap: don't remap */ |
142 | #define RM_SCRIPT 2 /* tb_noremap: remap local script mappings */ |
143 | #define RM_ABBR 4 /* tb_noremap: don't remap, do abbrev. */ |
144 | |
145 | /* typebuf.tb_buf has three parts: room in front (for result of mappings), the |
146 | * middle for typeahead and room for new characters (which needs to be 3 * |
147 | * MAXMAPLEN) for the Amiga). |
148 | */ |
149 | #define TYPELEN_INIT (5 * (MAXMAPLEN + 3)) |
150 | static char_u typebuf_init[TYPELEN_INIT]; /* initial typebuf.tb_buf */ |
151 | static char_u noremapbuf_init[TYPELEN_INIT]; /* initial typebuf.tb_noremap */ |
152 | |
153 | static size_t last_recorded_len = 0; // number of last recorded chars |
154 | |
155 | #ifdef INCLUDE_GENERATED_DECLARATIONS |
156 | # include "getchar.c.generated.h" |
157 | #endif |
158 | |
159 | /* |
160 | * Free and clear a buffer. |
161 | */ |
162 | void free_buff(buffheader_T *buf) |
163 | { |
164 | buffblock_T *p, *np; |
165 | |
166 | for (p = buf->bh_first.b_next; p != NULL; p = np) { |
167 | np = p->b_next; |
168 | xfree(p); |
169 | } |
170 | buf->bh_first.b_next = NULL; |
171 | } |
172 | |
173 | /* |
174 | * Return the contents of a buffer as a single string. |
175 | * K_SPECIAL and CSI in the returned string are escaped. |
176 | */ |
177 | static char_u *get_buffcont(buffheader_T *buffer, |
178 | int dozero // count == zero is not an error |
179 | ) |
180 | { |
181 | size_t count = 0; |
182 | char_u *p = NULL; |
183 | char_u *p2; |
184 | |
185 | // compute the total length of the string |
186 | for (const buffblock_T *bp = buffer->bh_first.b_next; |
187 | bp != NULL; bp = bp->b_next) { |
188 | count += STRLEN(bp->b_str); |
189 | } |
190 | |
191 | if (count || dozero) { |
192 | p = xmalloc(count + 1); |
193 | p2 = p; |
194 | for (const buffblock_T *bp = buffer->bh_first.b_next; |
195 | bp != NULL; bp = bp->b_next) { |
196 | for (const char_u *str = bp->b_str; *str;) { |
197 | *p2++ = *str++; |
198 | } |
199 | } |
200 | *p2 = NUL; |
201 | } |
202 | return p; |
203 | } |
204 | |
205 | /* |
206 | * Return the contents of the record buffer as a single string |
207 | * and clear the record buffer. |
208 | * K_SPECIAL and CSI in the returned string are escaped. |
209 | */ |
210 | char_u *get_recorded(void) |
211 | { |
212 | char_u *p; |
213 | size_t len; |
214 | |
215 | p = get_buffcont(&recordbuff, TRUE); |
216 | free_buff(&recordbuff); |
217 | |
218 | /* |
219 | * Remove the characters that were added the last time, these must be the |
220 | * (possibly mapped) characters that stopped the recording. |
221 | */ |
222 | len = STRLEN(p); |
223 | if (len >= last_recorded_len) { |
224 | len -= last_recorded_len; |
225 | p[len] = NUL; |
226 | } |
227 | |
228 | /* |
229 | * When stopping recording from Insert mode with CTRL-O q, also remove the |
230 | * CTRL-O. |
231 | */ |
232 | if (len > 0 && restart_edit != 0 && p[len - 1] == Ctrl_O) |
233 | p[len - 1] = NUL; |
234 | |
235 | return p; |
236 | } |
237 | |
238 | /* |
239 | * Return the contents of the redo buffer as a single string. |
240 | * K_SPECIAL and CSI in the returned string are escaped. |
241 | */ |
242 | char_u *get_inserted(void) |
243 | { |
244 | return get_buffcont(&redobuff, FALSE); |
245 | } |
246 | |
247 | /// Add string after the current block of the given buffer |
248 | /// |
249 | /// K_SPECIAL and CSI should have been escaped already. |
250 | /// |
251 | /// @param[out] buf Buffer to add to. |
252 | /// @param[in] s String to add. |
253 | /// @param[in] slen String length or -1 for NUL-terminated string. |
254 | static void add_buff(buffheader_T *const buf, const char *const s, |
255 | ptrdiff_t slen) |
256 | { |
257 | if (slen < 0) { |
258 | slen = (ptrdiff_t)strlen(s); |
259 | } |
260 | if (slen == 0) { // don't add empty strings |
261 | return; |
262 | } |
263 | |
264 | if (buf->bh_first.b_next == NULL) { // first add to list |
265 | buf->bh_space = 0; |
266 | buf->bh_curr = &(buf->bh_first); |
267 | } else if (buf->bh_curr == NULL) { // buffer has already been read |
268 | IEMSG(_("E222: Add to read buffer" )); |
269 | return; |
270 | } else if (buf->bh_index != 0) { |
271 | memmove(buf->bh_first.b_next->b_str, |
272 | buf->bh_first.b_next->b_str + buf->bh_index, |
273 | STRLEN(buf->bh_first.b_next->b_str + buf->bh_index) + 1); |
274 | } |
275 | buf->bh_index = 0; |
276 | |
277 | size_t len; |
278 | if (buf->bh_space >= (size_t)slen) { |
279 | len = STRLEN(buf->bh_curr->b_str); |
280 | STRLCPY(buf->bh_curr->b_str + len, s, slen + 1); |
281 | buf->bh_space -= (size_t)slen; |
282 | } else { |
283 | if (slen < MINIMAL_SIZE) { |
284 | len = MINIMAL_SIZE; |
285 | } else { |
286 | len = (size_t)slen; |
287 | } |
288 | buffblock_T *p = xmalloc(sizeof(buffblock_T) + len); |
289 | buf->bh_space = len - (size_t)slen; |
290 | STRLCPY(p->b_str, s, slen + 1); |
291 | |
292 | p->b_next = buf->bh_curr->b_next; |
293 | buf->bh_curr->b_next = p; |
294 | buf->bh_curr = p; |
295 | } |
296 | return; |
297 | } |
298 | |
299 | /* |
300 | * Add number "n" to buffer "buf". |
301 | */ |
302 | static void add_num_buff(buffheader_T *buf, long n) |
303 | { |
304 | char number[32]; |
305 | snprintf(number, sizeof(number), "%ld" , n); |
306 | add_buff(buf, number, -1L); |
307 | } |
308 | |
309 | /* |
310 | * Add character 'c' to buffer "buf". |
311 | * Translates special keys, NUL, CSI, K_SPECIAL and multibyte characters. |
312 | */ |
313 | static void add_char_buff(buffheader_T *buf, int c) |
314 | { |
315 | uint8_t bytes[MB_MAXBYTES + 1]; |
316 | |
317 | int len; |
318 | if (IS_SPECIAL(c)) { |
319 | len = 1; |
320 | } else { |
321 | len = utf_char2bytes(c, bytes); |
322 | } |
323 | |
324 | for (int i = 0; i < len; i++) { |
325 | if (!IS_SPECIAL(c)) { |
326 | c = bytes[i]; |
327 | } |
328 | |
329 | char temp[4]; |
330 | if (IS_SPECIAL(c) || c == K_SPECIAL || c == NUL) { |
331 | // Translate special key code into three byte sequence. |
332 | temp[0] = (char)K_SPECIAL; |
333 | temp[1] = (char)K_SECOND(c); |
334 | temp[2] = (char)K_THIRD(c); |
335 | temp[3] = NUL; |
336 | } else { |
337 | temp[0] = (char)c; |
338 | temp[1] = NUL; |
339 | } |
340 | add_buff(buf, temp, -1L); |
341 | } |
342 | } |
343 | |
344 | /* |
345 | * Get one byte from the read buffers. Use readbuf1 one first, use readbuf2 |
346 | * if that one is empty. |
347 | * If advance == TRUE go to the next char. |
348 | * No translation is done K_SPECIAL and CSI are escaped. |
349 | */ |
350 | static int read_readbuffers(int advance) |
351 | { |
352 | int c; |
353 | |
354 | c = read_readbuf(&readbuf1, advance); |
355 | if (c == NUL) |
356 | c = read_readbuf(&readbuf2, advance); |
357 | return c; |
358 | } |
359 | |
360 | static int read_readbuf(buffheader_T *buf, int advance) |
361 | { |
362 | char_u c; |
363 | |
364 | if (buf->bh_first.b_next == NULL) { // buffer is empty |
365 | return NUL; |
366 | } |
367 | |
368 | buffblock_T *const curr = buf->bh_first.b_next; |
369 | c = curr->b_str[buf->bh_index]; |
370 | |
371 | if (advance) { |
372 | if (curr->b_str[++buf->bh_index] == NUL) { |
373 | buf->bh_first.b_next = curr->b_next; |
374 | xfree(curr); |
375 | buf->bh_index = 0; |
376 | } |
377 | } |
378 | return c; |
379 | } |
380 | |
381 | /* |
382 | * Prepare the read buffers for reading (if they contain something). |
383 | */ |
384 | static void start_stuff(void) |
385 | { |
386 | if (readbuf1.bh_first.b_next != NULL) { |
387 | readbuf1.bh_curr = &(readbuf1.bh_first); |
388 | readbuf1.bh_space = 0; |
389 | } |
390 | if (readbuf2.bh_first.b_next != NULL) { |
391 | readbuf2.bh_curr = &(readbuf2.bh_first); |
392 | readbuf2.bh_space = 0; |
393 | } |
394 | } |
395 | |
396 | /* |
397 | * Return TRUE if the stuff buffer is empty. |
398 | */ |
399 | int stuff_empty(void) |
400 | { |
401 | return (readbuf1.bh_first.b_next == NULL && readbuf2.bh_first.b_next == NULL); |
402 | } |
403 | |
404 | /* |
405 | * Return TRUE if readbuf1 is empty. There may still be redo characters in |
406 | * redbuf2. |
407 | */ |
408 | int readbuf1_empty(void) |
409 | { |
410 | return (readbuf1.bh_first.b_next == NULL); |
411 | } |
412 | |
413 | /* |
414 | * Set a typeahead character that won't be flushed. |
415 | */ |
416 | void typeahead_noflush(int c) |
417 | { |
418 | typeahead_char = c; |
419 | } |
420 | |
421 | /* |
422 | * Remove the contents of the stuff buffer and the mapped characters in the |
423 | * typeahead buffer (used in case of an error). If "flush_typeahead" is true, |
424 | * flush all typeahead characters (used when interrupted by a CTRL-C). |
425 | */ |
426 | void flush_buffers(flush_buffers_T flush_typeahead) |
427 | { |
428 | init_typebuf(); |
429 | |
430 | start_stuff(); |
431 | while (read_readbuffers(TRUE) != NUL) { |
432 | } |
433 | |
434 | if (flush_typeahead == FLUSH_MINIMAL) { |
435 | // remove mapped characters at the start only |
436 | typebuf.tb_off += typebuf.tb_maplen; |
437 | typebuf.tb_len -= typebuf.tb_maplen; |
438 | } else { |
439 | // remove typeahead |
440 | if (flush_typeahead == FLUSH_INPUT) { |
441 | // We have to get all characters, because we may delete the first |
442 | // part of an escape sequence. In an xterm we get one char at a |
443 | // time and we have to get them all. |
444 | while (inchar(typebuf.tb_buf, typebuf.tb_buflen - 1, 10L) != 0) { |
445 | } |
446 | } |
447 | typebuf.tb_off = MAXMAPLEN; |
448 | typebuf.tb_len = 0; |
449 | // Reset the flag that text received from a client or from feedkeys() |
450 | // was inserted in the typeahead buffer. |
451 | typebuf_was_filled = false; |
452 | } |
453 | typebuf.tb_maplen = 0; |
454 | typebuf.tb_silent = 0; |
455 | cmd_silent = false; |
456 | typebuf.tb_no_abbr_cnt = 0; |
457 | } |
458 | |
459 | /* |
460 | * The previous contents of the redo buffer is kept in old_redobuffer. |
461 | * This is used for the CTRL-O <.> command in insert mode. |
462 | */ |
463 | void ResetRedobuff(void) |
464 | { |
465 | if (!block_redo) { |
466 | free_buff(&old_redobuff); |
467 | old_redobuff = redobuff; |
468 | redobuff.bh_first.b_next = NULL; |
469 | } |
470 | } |
471 | |
472 | /* |
473 | * Discard the contents of the redo buffer and restore the previous redo |
474 | * buffer. |
475 | */ |
476 | void CancelRedo(void) |
477 | { |
478 | if (!block_redo) { |
479 | free_buff(&redobuff); |
480 | redobuff = old_redobuff; |
481 | old_redobuff.bh_first.b_next = NULL; |
482 | start_stuff(); |
483 | while (read_readbuffers(TRUE) != NUL) { |
484 | } |
485 | } |
486 | } |
487 | |
488 | /// Save redobuff and old_redobuff to save_redobuff and save_old_redobuff. |
489 | /// Used before executing autocommands and user functions. |
490 | void saveRedobuff(save_redo_T *save_redo) |
491 | { |
492 | save_redo->sr_redobuff = redobuff; |
493 | redobuff.bh_first.b_next = NULL; |
494 | save_redo->sr_old_redobuff = old_redobuff; |
495 | old_redobuff.bh_first.b_next = NULL; |
496 | |
497 | // Make a copy, so that ":normal ." in a function works. |
498 | char *const s = (char *)get_buffcont(&save_redo->sr_redobuff, false); |
499 | if (s != NULL) { |
500 | add_buff(&redobuff, s, -1L); |
501 | xfree(s); |
502 | } |
503 | } |
504 | |
505 | /// Restore redobuff and old_redobuff from save_redobuff and save_old_redobuff. |
506 | /// Used after executing autocommands and user functions. |
507 | void restoreRedobuff(save_redo_T *save_redo) |
508 | { |
509 | free_buff(&redobuff); |
510 | redobuff = save_redo->sr_redobuff; |
511 | free_buff(&old_redobuff); |
512 | old_redobuff = save_redo->sr_old_redobuff; |
513 | } |
514 | |
515 | /* |
516 | * Append "s" to the redo buffer. |
517 | * K_SPECIAL and CSI should already have been escaped. |
518 | */ |
519 | void AppendToRedobuff(const char *s) |
520 | { |
521 | if (!block_redo) { |
522 | add_buff(&redobuff, (const char *)s, -1L); |
523 | } |
524 | } |
525 | |
526 | /// Append to Redo buffer literally, escaping special characters with CTRL-V. |
527 | /// K_SPECIAL and CSI are escaped as well. |
528 | /// |
529 | /// @param str String to append |
530 | /// @param len Length of `str` or -1 for up to the NUL. |
531 | void AppendToRedobuffLit(const char_u *str, int len) |
532 | { |
533 | if (block_redo) { |
534 | return; |
535 | } |
536 | |
537 | const char *s = (const char *)str; |
538 | while (len < 0 ? *s != NUL : s - (const char *)str < len) { |
539 | // Put a string of normal characters in the redo buffer (that's |
540 | // faster). |
541 | const char *start = s; |
542 | while (*s >= ' ' && *s < DEL && (len < 0 || s - (const char *)str < len)) { |
543 | s++; |
544 | } |
545 | |
546 | // Don't put '0' or '^' as last character, just in case a CTRL-D is |
547 | // typed next. |
548 | if (*s == NUL && (s[-1] == '0' || s[-1] == '^')) { |
549 | s--; |
550 | } |
551 | if (s > start) { |
552 | add_buff(&redobuff, start, (long)(s - start)); |
553 | } |
554 | |
555 | if (*s == NUL || (len >= 0 && s - (const char *)str >= len)) { |
556 | break; |
557 | } |
558 | |
559 | // Handle a special or multibyte character. |
560 | // Composing chars separately are handled separately. |
561 | const int c = (has_mbyte |
562 | ? mb_cptr2char_adv((const char_u **)&s) |
563 | : (uint8_t)(*s++)); |
564 | if (c < ' ' || c == DEL || (*s == NUL && (c == '0' || c == '^'))) { |
565 | add_char_buff(&redobuff, Ctrl_V); |
566 | } |
567 | |
568 | // CTRL-V '0' must be inserted as CTRL-V 048. |
569 | if (*s == NUL && c == '0') { |
570 | add_buff(&redobuff, "048" , 3L); |
571 | } else { |
572 | add_char_buff(&redobuff, c); |
573 | } |
574 | } |
575 | } |
576 | |
577 | /* |
578 | * Append a character to the redo buffer. |
579 | * Translates special keys, NUL, CSI, K_SPECIAL and multibyte characters. |
580 | */ |
581 | void AppendCharToRedobuff(int c) |
582 | { |
583 | if (!block_redo) |
584 | add_char_buff(&redobuff, c); |
585 | } |
586 | |
587 | /* |
588 | * Append a number to the redo buffer. |
589 | */ |
590 | void AppendNumberToRedobuff(long n) |
591 | { |
592 | if (!block_redo) |
593 | add_num_buff(&redobuff, n); |
594 | } |
595 | |
596 | /* |
597 | * Append string "s" to the stuff buffer. |
598 | * CSI and K_SPECIAL must already have been escaped. |
599 | */ |
600 | void stuffReadbuff(const char *s) |
601 | { |
602 | add_buff(&readbuf1, s, -1L); |
603 | } |
604 | |
605 | /// Append string "s" to the redo stuff buffer. |
606 | /// @remark CSI and K_SPECIAL must already have been escaped. |
607 | void stuffRedoReadbuff(const char *s) |
608 | { |
609 | add_buff(&readbuf2, s, -1L); |
610 | } |
611 | |
612 | void stuffReadbuffLen(const char *s, long len) |
613 | { |
614 | add_buff(&readbuf1, s, len); |
615 | } |
616 | |
617 | /* |
618 | * Stuff "s" into the stuff buffer, leaving special key codes unmodified and |
619 | * escaping other K_SPECIAL and CSI bytes. |
620 | * Change CR, LF and ESC into a space. |
621 | */ |
622 | void stuffReadbuffSpec(const char *s) |
623 | { |
624 | while (*s != NUL) { |
625 | if ((uint8_t)(*s) == K_SPECIAL && s[1] != NUL && s[2] != NUL) { |
626 | // Insert special key literally. |
627 | stuffReadbuffLen(s, 3); |
628 | s += 3; |
629 | } else { |
630 | int c = mb_ptr2char_adv((const char_u **)&s); |
631 | if (c == CAR || c == NL || c == ESC) { |
632 | c = ' '; |
633 | } |
634 | stuffcharReadbuff(c); |
635 | } |
636 | } |
637 | } |
638 | |
639 | /* |
640 | * Append a character to the stuff buffer. |
641 | * Translates special keys, NUL, CSI, K_SPECIAL and multibyte characters. |
642 | */ |
643 | void stuffcharReadbuff(int c) |
644 | { |
645 | add_char_buff(&readbuf1, c); |
646 | } |
647 | |
648 | /* |
649 | * Append a number to the stuff buffer. |
650 | */ |
651 | void stuffnumReadbuff(long n) |
652 | { |
653 | add_num_buff(&readbuf1, n); |
654 | } |
655 | |
656 | // Read a character from the redo buffer. Translates K_SPECIAL, CSI and |
657 | // multibyte characters. |
658 | // The redo buffer is left as it is. |
659 | // If init is true, prepare for redo, return FAIL if nothing to redo, OK |
660 | // otherwise. |
661 | // If old_redo is true, use old_redobuff instead of redobuff. |
662 | static int read_redo(bool init, bool old_redo) |
663 | { |
664 | static buffblock_T *bp; |
665 | static char_u *p; |
666 | int c; |
667 | int n; |
668 | char_u buf[MB_MAXBYTES + 1]; |
669 | int i; |
670 | |
671 | if (init) { |
672 | bp = old_redo ? old_redobuff.bh_first.b_next : redobuff.bh_first.b_next; |
673 | if (bp == NULL) { |
674 | return FAIL; |
675 | } |
676 | p = bp->b_str; |
677 | return OK; |
678 | } |
679 | if ((c = *p) == NUL) { |
680 | return c; |
681 | } |
682 | /* Reverse the conversion done by add_char_buff() */ |
683 | /* For a multi-byte character get all the bytes and return the |
684 | * converted character. */ |
685 | if (has_mbyte && (c != K_SPECIAL || p[1] == KS_SPECIAL)) |
686 | n = MB_BYTE2LEN_CHECK(c); |
687 | else |
688 | n = 1; |
689 | for (i = 0;; ++i) { |
690 | if (c == K_SPECIAL) { /* special key or escaped K_SPECIAL */ |
691 | c = TO_SPECIAL(p[1], p[2]); |
692 | p += 2; |
693 | } |
694 | if (*++p == NUL && bp->b_next != NULL) { |
695 | bp = bp->b_next; |
696 | p = bp->b_str; |
697 | } |
698 | buf[i] = (char_u)c; |
699 | if (i == n - 1) { // last byte of a character |
700 | if (n != 1) { |
701 | c = utf_ptr2char(buf); |
702 | } |
703 | break; |
704 | } |
705 | c = *p; |
706 | if (c == NUL) /* cannot happen? */ |
707 | break; |
708 | } |
709 | |
710 | return c; |
711 | } |
712 | |
713 | // Copy the rest of the redo buffer into the stuff buffer (in a slow way). |
714 | // If old_redo is true, use old_redobuff instead of redobuff. |
715 | // The escaped K_SPECIAL and CSI are copied without translation. |
716 | static void copy_redo(bool old_redo) |
717 | { |
718 | int c; |
719 | |
720 | while ((c = read_redo(false, old_redo)) != NUL) { |
721 | add_char_buff(&readbuf2, c); |
722 | } |
723 | } |
724 | |
725 | // Stuff the redo buffer into readbuf2. |
726 | // Insert the redo count into the command. |
727 | // If "old_redo" is true, the last but one command is repeated |
728 | // instead of the last command (inserting text). This is used for |
729 | // CTRL-O <.> in insert mode |
730 | // |
731 | // return FAIL for failure, OK otherwise |
732 | int start_redo(long count, bool old_redo) |
733 | { |
734 | int c; |
735 | |
736 | // init the pointers; return if nothing to redo |
737 | if (read_redo(true, old_redo) == FAIL) { |
738 | return FAIL; |
739 | } |
740 | |
741 | c = read_redo(false, old_redo); |
742 | |
743 | /* copy the buffer name, if present */ |
744 | if (c == '"') { |
745 | add_buff(&readbuf2, "\"" , 1L); |
746 | c = read_redo(false, old_redo); |
747 | |
748 | /* if a numbered buffer is used, increment the number */ |
749 | if (c >= '1' && c < '9') |
750 | ++c; |
751 | add_char_buff(&readbuf2, c); |
752 | |
753 | // the expression register should be re-evaluated |
754 | if (c == '=') { |
755 | add_char_buff(&readbuf2, CAR); |
756 | cmd_silent = true; |
757 | } |
758 | |
759 | c = read_redo(false, old_redo); |
760 | } |
761 | |
762 | if (c == 'v') { /* redo Visual */ |
763 | VIsual = curwin->w_cursor; |
764 | VIsual_active = true; |
765 | VIsual_select = false; |
766 | VIsual_reselect = true; |
767 | redo_VIsual_busy = true; |
768 | c = read_redo(false, old_redo); |
769 | } |
770 | |
771 | // try to enter the count (in place of a previous count) |
772 | if (count) { |
773 | while (ascii_isdigit(c)) { // skip "old" count |
774 | c = read_redo(false, old_redo); |
775 | } |
776 | add_num_buff(&readbuf2, count); |
777 | } |
778 | |
779 | /* copy from the redo buffer into the stuff buffer */ |
780 | add_char_buff(&readbuf2, c); |
781 | copy_redo(old_redo); |
782 | return OK; |
783 | } |
784 | |
785 | /* |
786 | * Repeat the last insert (R, o, O, a, A, i or I command) by stuffing |
787 | * the redo buffer into readbuf2. |
788 | * return FAIL for failure, OK otherwise |
789 | */ |
790 | int start_redo_ins(void) |
791 | { |
792 | int c; |
793 | |
794 | if (read_redo(true, false) == FAIL) { |
795 | return FAIL; |
796 | } |
797 | start_stuff(); |
798 | |
799 | // skip the count and the command character |
800 | while ((c = read_redo(false, false)) != NUL) { |
801 | if (vim_strchr((char_u *)"AaIiRrOo" , c) != NULL) { |
802 | if (c == 'O' || c == 'o') { |
803 | add_buff(&readbuf2, NL_STR, -1L); |
804 | } |
805 | break; |
806 | } |
807 | } |
808 | |
809 | // copy the typed text from the redo buffer into the stuff buffer |
810 | copy_redo(false); |
811 | block_redo = true; |
812 | return OK; |
813 | } |
814 | |
815 | void stop_redo_ins(void) |
816 | { |
817 | block_redo = FALSE; |
818 | } |
819 | |
820 | /* |
821 | * Initialize typebuf.tb_buf to point to typebuf_init. |
822 | * alloc() cannot be used here: In out-of-memory situations it would |
823 | * be impossible to type anything. |
824 | */ |
825 | static void init_typebuf(void) |
826 | { |
827 | if (typebuf.tb_buf == NULL) { |
828 | typebuf.tb_buf = typebuf_init; |
829 | typebuf.tb_noremap = noremapbuf_init; |
830 | typebuf.tb_buflen = TYPELEN_INIT; |
831 | typebuf.tb_len = 0; |
832 | typebuf.tb_off = MAXMAPLEN + 4; |
833 | typebuf.tb_change_cnt = 1; |
834 | } |
835 | } |
836 | |
837 | /* |
838 | * insert a string in position 'offset' in the typeahead buffer (for "@r" |
839 | * and ":normal" command, vgetorpeek() and check_termcode()) |
840 | * |
841 | * If noremap is REMAP_YES, new string can be mapped again. |
842 | * If noremap is REMAP_NONE, new string cannot be mapped again. |
843 | * If noremap is REMAP_SKIP, fist char of new string cannot be mapped again, |
844 | * but abbreviations are allowed. |
845 | * If noremap is REMAP_SCRIPT, new string cannot be mapped again, except for |
846 | * script-local mappings. |
847 | * If noremap is > 0, that many characters of the new string cannot be mapped. |
848 | * |
849 | * If nottyped is TRUE, the string does not return KeyTyped (don't use when |
850 | * offset is non-zero!). |
851 | * |
852 | * If silent is true, cmd_silent is set when the characters are obtained. |
853 | * |
854 | * return FAIL for failure, OK otherwise |
855 | */ |
856 | int ins_typebuf(char_u *str, int noremap, int offset, int nottyped, bool silent) |
857 | { |
858 | char_u *s1, *s2; |
859 | int newlen; |
860 | int addlen; |
861 | int i; |
862 | int newoff; |
863 | int val; |
864 | int nrm; |
865 | |
866 | init_typebuf(); |
867 | if (++typebuf.tb_change_cnt == 0) |
868 | typebuf.tb_change_cnt = 1; |
869 | |
870 | addlen = (int)STRLEN(str); |
871 | |
872 | if (offset == 0 && addlen <= typebuf.tb_off) { |
873 | // Easy case: there is room in front of typebuf.tb_buf[typebuf.tb_off] |
874 | typebuf.tb_off -= addlen; |
875 | memmove(typebuf.tb_buf + typebuf.tb_off, str, (size_t)addlen); |
876 | } else if (typebuf.tb_len == 0 |
877 | && typebuf.tb_buflen >= addlen + 3 * (MAXMAPLEN + 4)) { |
878 | // Buffer is empty and string fits in the existing buffer. |
879 | // Leave some space before and after, if possible. |
880 | typebuf.tb_off = (typebuf.tb_buflen - addlen - 3 * (MAXMAPLEN + 4)) / 2; |
881 | memmove(typebuf.tb_buf + typebuf.tb_off, str, (size_t)addlen); |
882 | } else { |
883 | // Need to allocate a new buffer. |
884 | // In typebuf.tb_buf there must always be room for 3 * (MAXMAPLEN + 4) |
885 | // characters. We add some extra room to avoid having to allocate too |
886 | // often. |
887 | newoff = MAXMAPLEN + 4; |
888 | newlen = typebuf.tb_len + addlen + newoff + 4 * (MAXMAPLEN + 4); |
889 | if (newlen < 0) { /* string is getting too long */ |
890 | EMSG(_(e_toocompl)); /* also calls flush_buffers */ |
891 | setcursor(); |
892 | return FAIL; |
893 | } |
894 | s1 = xmalloc((size_t)newlen); |
895 | s2 = xmalloc((size_t)newlen); |
896 | typebuf.tb_buflen = newlen; |
897 | |
898 | // copy the old chars, before the insertion point |
899 | memmove(s1 + newoff, typebuf.tb_buf + typebuf.tb_off, (size_t)offset); |
900 | // copy the new chars |
901 | memmove(s1 + newoff + offset, str, (size_t)addlen); |
902 | // copy the old chars, after the insertion point, including the NUL at |
903 | // the end |
904 | int bytes = typebuf.tb_len - offset + 1; |
905 | assert(bytes > 0); |
906 | memmove(s1 + newoff + offset + addlen, |
907 | typebuf.tb_buf + typebuf.tb_off + offset, (size_t)bytes); |
908 | if (typebuf.tb_buf != typebuf_init) { |
909 | xfree(typebuf.tb_buf); |
910 | } |
911 | typebuf.tb_buf = s1; |
912 | |
913 | memmove(s2 + newoff, typebuf.tb_noremap + typebuf.tb_off, |
914 | (size_t)offset); |
915 | memmove(s2 + newoff + offset + addlen, |
916 | typebuf.tb_noremap + typebuf.tb_off + offset, |
917 | (size_t)(typebuf.tb_len - offset)); |
918 | if (typebuf.tb_noremap != noremapbuf_init) |
919 | xfree(typebuf.tb_noremap); |
920 | typebuf.tb_noremap = s2; |
921 | |
922 | typebuf.tb_off = newoff; |
923 | } |
924 | typebuf.tb_len += addlen; |
925 | |
926 | /* If noremap == REMAP_SCRIPT: do remap script-local mappings. */ |
927 | if (noremap == REMAP_SCRIPT) |
928 | val = RM_SCRIPT; |
929 | else if (noremap == REMAP_SKIP) |
930 | val = RM_ABBR; |
931 | else |
932 | val = RM_NONE; |
933 | |
934 | /* |
935 | * Adjust typebuf.tb_noremap[] for the new characters: |
936 | * If noremap == REMAP_NONE or REMAP_SCRIPT: new characters are |
937 | * (sometimes) not remappable |
938 | * If noremap == REMAP_YES: all the new characters are mappable |
939 | * If noremap > 0: "noremap" characters are not remappable, the rest |
940 | * mappable |
941 | */ |
942 | if (noremap == REMAP_SKIP) |
943 | nrm = 1; |
944 | else if (noremap < 0) |
945 | nrm = addlen; |
946 | else |
947 | nrm = noremap; |
948 | for (i = 0; i < addlen; ++i) |
949 | typebuf.tb_noremap[typebuf.tb_off + i + offset] = |
950 | (char_u)((--nrm >= 0) ? val : RM_YES); |
951 | |
952 | /* tb_maplen and tb_silent only remember the length of mapped and/or |
953 | * silent mappings at the start of the buffer, assuming that a mapped |
954 | * sequence doesn't result in typed characters. */ |
955 | if (nottyped || typebuf.tb_maplen > offset) |
956 | typebuf.tb_maplen += addlen; |
957 | if (silent || typebuf.tb_silent > offset) { |
958 | typebuf.tb_silent += addlen; |
959 | cmd_silent = true; |
960 | } |
961 | if (typebuf.tb_no_abbr_cnt && offset == 0) /* and not used for abbrev.s */ |
962 | typebuf.tb_no_abbr_cnt += addlen; |
963 | |
964 | return OK; |
965 | } |
966 | |
967 | /* |
968 | * Put character "c" back into the typeahead buffer. |
969 | * Can be used for a character obtained by vgetc() that needs to be put back. |
970 | * Uses cmd_silent, KeyTyped and KeyNoremap to restore the flags belonging to |
971 | * the char. |
972 | */ |
973 | void ins_char_typebuf(int c) |
974 | { |
975 | char_u buf[MB_MAXBYTES + 1]; |
976 | if (IS_SPECIAL(c)) { |
977 | buf[0] = K_SPECIAL; |
978 | buf[1] = (char_u)K_SECOND(c); |
979 | buf[2] = (char_u)K_THIRD(c); |
980 | buf[3] = NUL; |
981 | } else { |
982 | buf[utf_char2bytes(c, buf)] = NUL; |
983 | } |
984 | (void)ins_typebuf(buf, KeyNoremap, 0, !KeyTyped, cmd_silent); |
985 | } |
986 | |
987 | /* |
988 | * Return TRUE if the typeahead buffer was changed (while waiting for a |
989 | * character to arrive). Happens when a message was received from a client or |
990 | * from feedkeys(). |
991 | * But check in a more generic way to avoid trouble: When "typebuf.tb_buf" |
992 | * changed it was reallocated and the old pointer can no longer be used. |
993 | * Or "typebuf.tb_off" may have been changed and we would overwrite characters |
994 | * that was just added. |
995 | */ |
996 | int |
997 | typebuf_changed ( |
998 | int tb_change_cnt /* old value of typebuf.tb_change_cnt */ |
999 | ) |
1000 | { |
1001 | return tb_change_cnt != 0 && (typebuf.tb_change_cnt != tb_change_cnt |
1002 | || typebuf_was_filled |
1003 | ); |
1004 | } |
1005 | |
1006 | /* |
1007 | * Return TRUE if there are no characters in the typeahead buffer that have |
1008 | * not been typed (result from a mapping or come from ":normal"). |
1009 | */ |
1010 | int typebuf_typed(void) |
1011 | { |
1012 | return typebuf.tb_maplen == 0; |
1013 | } |
1014 | |
1015 | /* |
1016 | * Return the number of characters that are mapped (or not typed). |
1017 | */ |
1018 | int typebuf_maplen(void) |
1019 | { |
1020 | return typebuf.tb_maplen; |
1021 | } |
1022 | |
1023 | /* |
1024 | * remove "len" characters from typebuf.tb_buf[typebuf.tb_off + offset] |
1025 | */ |
1026 | void del_typebuf(int len, int offset) |
1027 | { |
1028 | int i; |
1029 | |
1030 | if (len == 0) |
1031 | return; /* nothing to do */ |
1032 | |
1033 | typebuf.tb_len -= len; |
1034 | |
1035 | /* |
1036 | * Easy case: Just increase typebuf.tb_off. |
1037 | */ |
1038 | if (offset == 0 && typebuf.tb_buflen - (typebuf.tb_off + len) |
1039 | >= 3 * MAXMAPLEN + 3) |
1040 | typebuf.tb_off += len; |
1041 | /* |
1042 | * Have to move the characters in typebuf.tb_buf[] and typebuf.tb_noremap[] |
1043 | */ |
1044 | else { |
1045 | i = typebuf.tb_off + offset; |
1046 | /* |
1047 | * Leave some extra room at the end to avoid reallocation. |
1048 | */ |
1049 | if (typebuf.tb_off > MAXMAPLEN) { |
1050 | memmove(typebuf.tb_buf + MAXMAPLEN, |
1051 | typebuf.tb_buf + typebuf.tb_off, (size_t)offset); |
1052 | memmove(typebuf.tb_noremap + MAXMAPLEN, |
1053 | typebuf.tb_noremap + typebuf.tb_off, (size_t)offset); |
1054 | typebuf.tb_off = MAXMAPLEN; |
1055 | } |
1056 | // adjust typebuf.tb_buf (include the NUL at the end) |
1057 | int bytes = typebuf.tb_len - offset + 1; |
1058 | assert(bytes > 0); |
1059 | memmove(typebuf.tb_buf + typebuf.tb_off + offset, |
1060 | typebuf.tb_buf + i + len, (size_t)bytes); |
1061 | // adjust typebuf.tb_noremap[] |
1062 | memmove(typebuf.tb_noremap + typebuf.tb_off + offset, |
1063 | typebuf.tb_noremap + i + len, |
1064 | (size_t)(typebuf.tb_len - offset)); |
1065 | } |
1066 | |
1067 | if (typebuf.tb_maplen > offset) { /* adjust tb_maplen */ |
1068 | if (typebuf.tb_maplen < offset + len) |
1069 | typebuf.tb_maplen = offset; |
1070 | else |
1071 | typebuf.tb_maplen -= len; |
1072 | } |
1073 | if (typebuf.tb_silent > offset) { /* adjust tb_silent */ |
1074 | if (typebuf.tb_silent < offset + len) |
1075 | typebuf.tb_silent = offset; |
1076 | else |
1077 | typebuf.tb_silent -= len; |
1078 | } |
1079 | if (typebuf.tb_no_abbr_cnt > offset) { /* adjust tb_no_abbr_cnt */ |
1080 | if (typebuf.tb_no_abbr_cnt < offset + len) |
1081 | typebuf.tb_no_abbr_cnt = offset; |
1082 | else |
1083 | typebuf.tb_no_abbr_cnt -= len; |
1084 | } |
1085 | |
1086 | /* Reset the flag that text received from a client or from feedkeys() |
1087 | * was inserted in the typeahead buffer. */ |
1088 | typebuf_was_filled = false; |
1089 | if (++typebuf.tb_change_cnt == 0) { |
1090 | typebuf.tb_change_cnt = 1; |
1091 | } |
1092 | } |
1093 | |
1094 | /* |
1095 | * Write typed characters to script file. |
1096 | * If recording is on put the character in the recordbuffer. |
1097 | */ |
1098 | static void gotchars(char_u *chars, size_t len) |
1099 | { |
1100 | char_u *s = chars; |
1101 | int c; |
1102 | |
1103 | // remember how many chars were last recorded |
1104 | if (reg_recording != 0) { |
1105 | last_recorded_len += len; |
1106 | } |
1107 | |
1108 | while (len--) { |
1109 | // Handle one byte at a time; no translation to be done. |
1110 | c = *s++; |
1111 | updatescript(c); |
1112 | |
1113 | if (reg_recording != 0) { |
1114 | char buf[2] = { (char)c, NUL }; |
1115 | add_buff(&recordbuff, buf, 1L); |
1116 | } |
1117 | } |
1118 | may_sync_undo(); |
1119 | |
1120 | /* output "debug mode" message next time in debug mode */ |
1121 | debug_did_msg = FALSE; |
1122 | |
1123 | /* Since characters have been typed, consider the following to be in |
1124 | * another mapping. Search string will be kept in history. */ |
1125 | ++maptick; |
1126 | } |
1127 | |
1128 | /* |
1129 | * Sync undo. Called when typed characters are obtained from the typeahead |
1130 | * buffer, or when a menu is used. |
1131 | * Do not sync: |
1132 | * - In Insert mode, unless cursor key has been used. |
1133 | * - While reading a script file. |
1134 | * - When no_u_sync is non-zero. |
1135 | */ |
1136 | void may_sync_undo(void) |
1137 | { |
1138 | if ((!(State & (INSERT + CMDLINE)) || arrow_used) |
1139 | && scriptin[curscript] == NULL) |
1140 | u_sync(FALSE); |
1141 | } |
1142 | |
1143 | /* |
1144 | * Make "typebuf" empty and allocate new buffers. |
1145 | */ |
1146 | void alloc_typebuf(void) |
1147 | { |
1148 | typebuf.tb_buf = xmalloc(TYPELEN_INIT); |
1149 | typebuf.tb_noremap = xmalloc(TYPELEN_INIT); |
1150 | typebuf.tb_buflen = TYPELEN_INIT; |
1151 | typebuf.tb_off = MAXMAPLEN + 4; // can insert without realloc |
1152 | typebuf.tb_len = 0; |
1153 | typebuf.tb_maplen = 0; |
1154 | typebuf.tb_silent = 0; |
1155 | typebuf.tb_no_abbr_cnt = 0; |
1156 | if (++typebuf.tb_change_cnt == 0) |
1157 | typebuf.tb_change_cnt = 1; |
1158 | } |
1159 | |
1160 | /* |
1161 | * Free the buffers of "typebuf". |
1162 | */ |
1163 | void free_typebuf(void) |
1164 | { |
1165 | if (typebuf.tb_buf == typebuf_init) { |
1166 | internal_error("Free typebuf 1" ); |
1167 | } else { |
1168 | xfree(typebuf.tb_buf); |
1169 | } |
1170 | if (typebuf.tb_noremap == noremapbuf_init) { |
1171 | internal_error("Free typebuf 2" ); |
1172 | } else { |
1173 | xfree(typebuf.tb_noremap); |
1174 | } |
1175 | } |
1176 | |
1177 | /* |
1178 | * When doing ":so! file", the current typeahead needs to be saved, and |
1179 | * restored when "file" has been read completely. |
1180 | */ |
1181 | static typebuf_T saved_typebuf[NSCRIPT]; |
1182 | |
1183 | void save_typebuf(void) |
1184 | { |
1185 | init_typebuf(); |
1186 | saved_typebuf[curscript] = typebuf; |
1187 | alloc_typebuf(); |
1188 | } |
1189 | |
1190 | static int old_char = -1; // character put back by vungetc() |
1191 | static int old_mod_mask; // mod_mask for ungotten character |
1192 | static int old_mouse_grid; // mouse_grid related to old_char |
1193 | static int old_mouse_row; // mouse_row related to old_char |
1194 | static int old_mouse_col; // mouse_col related to old_char |
1195 | |
1196 | |
1197 | /* |
1198 | * Save all three kinds of typeahead, so that the user must type at a prompt. |
1199 | */ |
1200 | void save_typeahead(tasave_T *tp) |
1201 | { |
1202 | tp->save_typebuf = typebuf; |
1203 | alloc_typebuf(); |
1204 | tp->typebuf_valid = TRUE; |
1205 | tp->old_char = old_char; |
1206 | tp->old_mod_mask = old_mod_mask; |
1207 | old_char = -1; |
1208 | |
1209 | tp->save_readbuf1 = readbuf1; |
1210 | readbuf1.bh_first.b_next = NULL; |
1211 | tp->save_readbuf2 = readbuf2; |
1212 | readbuf2.bh_first.b_next = NULL; |
1213 | } |
1214 | |
1215 | /* |
1216 | * Restore the typeahead to what it was before calling save_typeahead(). |
1217 | * The allocated memory is freed, can only be called once! |
1218 | */ |
1219 | void restore_typeahead(tasave_T *tp) |
1220 | { |
1221 | if (tp->typebuf_valid) { |
1222 | free_typebuf(); |
1223 | typebuf = tp->save_typebuf; |
1224 | } |
1225 | |
1226 | old_char = tp->old_char; |
1227 | old_mod_mask = tp->old_mod_mask; |
1228 | |
1229 | free_buff(&readbuf1); |
1230 | readbuf1 = tp->save_readbuf1; |
1231 | free_buff(&readbuf2); |
1232 | readbuf2 = tp->save_readbuf2; |
1233 | } |
1234 | |
1235 | /* |
1236 | * Open a new script file for the ":source!" command. |
1237 | */ |
1238 | void |
1239 | openscript ( |
1240 | char_u *name, |
1241 | int directly /* when TRUE execute directly */ |
1242 | ) |
1243 | { |
1244 | if (curscript + 1 == NSCRIPT) { |
1245 | EMSG(_(e_nesting)); |
1246 | return; |
1247 | } |
1248 | |
1249 | // Disallow sourcing a file in the sandbox, the commands would be executed |
1250 | // later, possibly outside of the sandbox. |
1251 | if (check_secure()) { |
1252 | return; |
1253 | } |
1254 | |
1255 | if (ignore_script) { |
1256 | // Not reading from script, also don't open one. Warning message? |
1257 | return; |
1258 | } |
1259 | |
1260 | if (scriptin[curscript] != NULL) /* already reading script */ |
1261 | ++curscript; |
1262 | /* use NameBuff for expanded name */ |
1263 | expand_env(name, NameBuff, MAXPATHL); |
1264 | int error; |
1265 | if ((scriptin[curscript] = file_open_new(&error, (char *)NameBuff, |
1266 | kFileReadOnly, 0)) == NULL) { |
1267 | emsgf(_(e_notopen_2), name, os_strerror(error)); |
1268 | if (curscript) { |
1269 | curscript--; |
1270 | } |
1271 | return; |
1272 | } |
1273 | save_typebuf(); |
1274 | |
1275 | /* |
1276 | * Execute the commands from the file right now when using ":source!" |
1277 | * after ":global" or ":argdo" or in a loop. Also when another command |
1278 | * follows. This means the display won't be updated. Don't do this |
1279 | * always, "make test" would fail. |
1280 | */ |
1281 | if (directly) { |
1282 | oparg_T oa; |
1283 | int oldcurscript; |
1284 | int save_State = State; |
1285 | int save_restart_edit = restart_edit; |
1286 | int save_insertmode = p_im; |
1287 | int save_finish_op = finish_op; |
1288 | int save_msg_scroll = msg_scroll; |
1289 | |
1290 | State = NORMAL; |
1291 | msg_scroll = FALSE; /* no msg scrolling in Normal mode */ |
1292 | restart_edit = 0; /* don't go to Insert mode */ |
1293 | p_im = FALSE; /* don't use 'insertmode' */ |
1294 | clear_oparg(&oa); |
1295 | finish_op = FALSE; |
1296 | |
1297 | oldcurscript = curscript; |
1298 | do { |
1299 | update_topline_cursor(); // update cursor position and topline |
1300 | normal_cmd(&oa, false); // execute one command |
1301 | vpeekc(); // check for end of file |
1302 | } while (scriptin[oldcurscript] != NULL); |
1303 | |
1304 | State = save_State; |
1305 | msg_scroll = save_msg_scroll; |
1306 | restart_edit = save_restart_edit; |
1307 | p_im = save_insertmode; |
1308 | finish_op = save_finish_op; |
1309 | } |
1310 | } |
1311 | |
1312 | /* |
1313 | * Close the currently active input script. |
1314 | */ |
1315 | static void closescript(void) |
1316 | { |
1317 | free_typebuf(); |
1318 | typebuf = saved_typebuf[curscript]; |
1319 | |
1320 | file_free(scriptin[curscript], false); |
1321 | scriptin[curscript] = NULL; |
1322 | if (curscript > 0) |
1323 | --curscript; |
1324 | } |
1325 | |
1326 | #if defined(EXITFREE) |
1327 | void close_all_scripts(void) |
1328 | { |
1329 | while (scriptin[0] != NULL) |
1330 | closescript(); |
1331 | } |
1332 | |
1333 | #endif |
1334 | |
1335 | /* |
1336 | * Return TRUE when reading keys from a script file. |
1337 | */ |
1338 | int using_script(void) |
1339 | { |
1340 | return scriptin[curscript] != NULL; |
1341 | } |
1342 | |
1343 | /// This function is called just before doing a blocking wait. Thus after |
1344 | /// waiting 'updatetime' for a character to arrive. |
1345 | void before_blocking(void) |
1346 | { |
1347 | updatescript(0); |
1348 | if (may_garbage_collect) { |
1349 | garbage_collect(false); |
1350 | } |
1351 | } |
1352 | |
1353 | /// updatescript() is called when a character can be written to the script file |
1354 | /// or when we have waited some time for a character (c == 0). |
1355 | /// |
1356 | /// All the changed memfiles are synced if c == 0 or when the number of typed |
1357 | /// characters reaches 'updatecount' and 'updatecount' is non-zero. |
1358 | static void updatescript(int c) |
1359 | { |
1360 | static int count = 0; |
1361 | |
1362 | if (c && scriptout) { |
1363 | putc(c, scriptout); |
1364 | } |
1365 | bool idle = (c == 0); |
1366 | if (idle || (p_uc > 0 && ++count >= p_uc)) { |
1367 | ml_sync_all(idle, true, |
1368 | (!!p_fs || idle)); // Always fsync at idle (CursorHold). |
1369 | count = 0; |
1370 | } |
1371 | } |
1372 | |
1373 | /* |
1374 | * Get the next input character. |
1375 | * Can return a special key or a multi-byte character. |
1376 | * Can return NUL when called recursively, use safe_vgetc() if that's not |
1377 | * wanted. |
1378 | * This translates escaped K_SPECIAL and CSI bytes to a K_SPECIAL or CSI byte. |
1379 | * Collects the bytes of a multibyte character into the whole character. |
1380 | * Returns the modifiers in the global "mod_mask". |
1381 | */ |
1382 | int vgetc(void) |
1383 | { |
1384 | int c, c2; |
1385 | int n; |
1386 | char_u buf[MB_MAXBYTES + 1]; |
1387 | int i; |
1388 | |
1389 | // Do garbage collection when garbagecollect() was called previously and |
1390 | // we are now at the toplevel. |
1391 | if (may_garbage_collect && want_garbage_collect) { |
1392 | garbage_collect(false); |
1393 | } |
1394 | |
1395 | /* |
1396 | * If a character was put back with vungetc, it was already processed. |
1397 | * Return it directly. |
1398 | */ |
1399 | if (old_char != -1) { |
1400 | c = old_char; |
1401 | old_char = -1; |
1402 | mod_mask = old_mod_mask; |
1403 | mouse_grid = old_mouse_grid; |
1404 | mouse_row = old_mouse_row; |
1405 | mouse_col = old_mouse_col; |
1406 | } else { |
1407 | mod_mask = 0x0; |
1408 | last_recorded_len = 0; |
1409 | for (;; ) { // this is done twice if there are modifiers |
1410 | bool did_inc = false; |
1411 | if (mod_mask) { // no mapping after modifier has been read |
1412 | no_mapping++; |
1413 | did_inc = true; // mod_mask may change value |
1414 | } |
1415 | c = vgetorpeek(true); |
1416 | if (did_inc) { |
1417 | no_mapping--; |
1418 | } |
1419 | |
1420 | // Get two extra bytes for special keys |
1421 | if (c == K_SPECIAL) { |
1422 | no_mapping++; |
1423 | c2 = vgetorpeek(true); // no mapping for these chars |
1424 | c = vgetorpeek(true); |
1425 | no_mapping--; |
1426 | if (c2 == KS_MODIFIER) { |
1427 | mod_mask = c; |
1428 | continue; |
1429 | } |
1430 | c = TO_SPECIAL(c2, c); |
1431 | |
1432 | } |
1433 | |
1434 | // a keypad or special function key was not mapped, use it like |
1435 | // its ASCII equivalent |
1436 | switch (c) { |
1437 | case K_KPLUS: c = '+'; break; |
1438 | case K_KMINUS: c = '-'; break; |
1439 | case K_KDIVIDE: c = '/'; break; |
1440 | case K_KMULTIPLY: c = '*'; break; |
1441 | case K_KENTER: c = CAR; break; |
1442 | case K_KPOINT: c = '.'; break; |
1443 | case K_KCOMMA: c = ','; break; |
1444 | case K_KEQUAL: c = '='; break; |
1445 | case K_K0: c = '0'; break; |
1446 | case K_K1: c = '1'; break; |
1447 | case K_K2: c = '2'; break; |
1448 | case K_K3: c = '3'; break; |
1449 | case K_K4: c = '4'; break; |
1450 | case K_K5: c = '5'; break; |
1451 | case K_K6: c = '6'; break; |
1452 | case K_K7: c = '7'; break; |
1453 | case K_K8: c = '8'; break; |
1454 | case K_K9: c = '9'; break; |
1455 | |
1456 | case K_XHOME: |
1457 | case K_ZHOME: |
1458 | if (mod_mask == MOD_MASK_SHIFT) { |
1459 | c = K_S_HOME; |
1460 | mod_mask = 0; |
1461 | } else if (mod_mask == MOD_MASK_CTRL) { |
1462 | c = K_C_HOME; |
1463 | mod_mask = 0; |
1464 | } else { |
1465 | c = K_HOME; |
1466 | } |
1467 | break; |
1468 | case K_XEND: |
1469 | case K_ZEND: |
1470 | if (mod_mask == MOD_MASK_SHIFT) { |
1471 | c = K_S_END; |
1472 | mod_mask = 0; |
1473 | } else if (mod_mask == MOD_MASK_CTRL) { |
1474 | c = K_C_END; |
1475 | mod_mask = 0; |
1476 | } else { |
1477 | c = K_END; |
1478 | } |
1479 | break; |
1480 | |
1481 | case K_XUP: c = K_UP; break; |
1482 | case K_XDOWN: c = K_DOWN; break; |
1483 | case K_XLEFT: c = K_LEFT; break; |
1484 | case K_XRIGHT: c = K_RIGHT; break; |
1485 | } |
1486 | |
1487 | // For a multi-byte character get all the bytes and return the |
1488 | // converted character. |
1489 | // Note: This will loop until enough bytes are received! |
1490 | if ((n = MB_BYTE2LEN_CHECK(c)) > 1) { |
1491 | no_mapping++; |
1492 | buf[0] = (char_u)c; |
1493 | for (i = 1; i < n; i++) { |
1494 | buf[i] = (char_u)vgetorpeek(true); |
1495 | if (buf[i] == K_SPECIAL |
1496 | ) { |
1497 | // Must be a K_SPECIAL - KS_SPECIAL - KE_FILLER sequence, |
1498 | // which represents a K_SPECIAL (0x80), |
1499 | // or a CSI - KS_EXTRA - KE_CSI sequence, which represents |
1500 | // a CSI (0x9B), |
1501 | // of a K_SPECIAL - KS_EXTRA - KE_CSI, which is CSI too. |
1502 | c = vgetorpeek(true); |
1503 | if (vgetorpeek(true) == (int)KE_CSI && c == KS_EXTRA) { |
1504 | buf[i] = CSI; |
1505 | } |
1506 | } |
1507 | } |
1508 | no_mapping--; |
1509 | c = utf_ptr2char(buf); |
1510 | } |
1511 | |
1512 | break; |
1513 | } |
1514 | } |
1515 | |
1516 | /* |
1517 | * In the main loop "may_garbage_collect" can be set to do garbage |
1518 | * collection in the first next vgetc(). It's disabled after that to |
1519 | * avoid internally used Lists and Dicts to be freed. |
1520 | */ |
1521 | may_garbage_collect = FALSE; |
1522 | |
1523 | return c; |
1524 | } |
1525 | |
1526 | /* |
1527 | * Like vgetc(), but never return a NUL when called recursively, get a key |
1528 | * directly from the user (ignoring typeahead). |
1529 | */ |
1530 | int safe_vgetc(void) |
1531 | { |
1532 | int c; |
1533 | |
1534 | c = vgetc(); |
1535 | if (c == NUL) { |
1536 | c = get_keystroke(NULL); |
1537 | } |
1538 | return c; |
1539 | } |
1540 | |
1541 | /* |
1542 | * Like safe_vgetc(), but loop to handle K_IGNORE. |
1543 | * Also ignore scrollbar events. |
1544 | */ |
1545 | int plain_vgetc(void) |
1546 | { |
1547 | int c; |
1548 | |
1549 | do { |
1550 | c = safe_vgetc(); |
1551 | } while (c == K_IGNORE || c == K_VER_SCROLLBAR || c == K_HOR_SCROLLBAR); |
1552 | return c; |
1553 | } |
1554 | |
1555 | /* |
1556 | * Check if a character is available, such that vgetc() will not block. |
1557 | * If the next character is a special character or multi-byte, the returned |
1558 | * character is not valid!. |
1559 | * Returns NUL if no character is available. |
1560 | */ |
1561 | int vpeekc(void) |
1562 | { |
1563 | if (old_char != -1) |
1564 | return old_char; |
1565 | return vgetorpeek(FALSE); |
1566 | } |
1567 | |
1568 | /* |
1569 | * Check if any character is available, also half an escape sequence. |
1570 | * Trick: when no typeahead found, but there is something in the typeahead |
1571 | * buffer, it must be an ESC that is recognized as the start of a key code. |
1572 | */ |
1573 | int vpeekc_any(void) |
1574 | { |
1575 | int c; |
1576 | |
1577 | c = vpeekc(); |
1578 | if (c == NUL && typebuf.tb_len > 0) |
1579 | c = ESC; |
1580 | return c; |
1581 | } |
1582 | |
1583 | /* |
1584 | * Call vpeekc() without causing anything to be mapped. |
1585 | * Return TRUE if a character is available, FALSE otherwise. |
1586 | */ |
1587 | int char_avail(void) |
1588 | { |
1589 | int retval; |
1590 | |
1591 | no_mapping++; |
1592 | retval = vpeekc(); |
1593 | no_mapping--; |
1594 | return retval != NUL; |
1595 | } |
1596 | |
1597 | void |
1598 | vungetc ( /* unget one character (can only be done once!) */ |
1599 | int c |
1600 | ) |
1601 | { |
1602 | old_char = c; |
1603 | old_mod_mask = mod_mask; |
1604 | old_mouse_grid = mouse_grid; |
1605 | old_mouse_row = mouse_row; |
1606 | old_mouse_col = mouse_col; |
1607 | } |
1608 | |
1609 | /// Gets a character: |
1610 | /// 1. from the stuffbuffer |
1611 | /// This is used for abbreviated commands like "D" -> "d$". |
1612 | /// Also used to redo a command for ".". |
1613 | /// 2. from the typeahead buffer |
1614 | /// Stores text obtained previously but not used yet. |
1615 | /// Also stores the result of mappings. |
1616 | /// Also used for the ":normal" command. |
1617 | /// 3. from the user |
1618 | /// This may do a blocking wait if "advance" is TRUE. |
1619 | /// |
1620 | /// if "advance" is TRUE (vgetc()): |
1621 | /// Really get the character. |
1622 | /// KeyTyped is set to TRUE in the case the user typed the key. |
1623 | /// KeyStuffed is TRUE if the character comes from the stuff buffer. |
1624 | /// if "advance" is FALSE (vpeekc()): |
1625 | /// Just look whether there is a character available. |
1626 | /// Return NUL if not. |
1627 | /// |
1628 | /// When `no_mapping` (global) is zero, checks for mappings in the current mode. |
1629 | /// Only returns one byte (of a multi-byte character). |
1630 | /// K_SPECIAL and CSI may be escaped, need to get two more bytes then. |
1631 | static int vgetorpeek(int advance) |
1632 | { |
1633 | int c, c1; |
1634 | int keylen; |
1635 | char_u *s; |
1636 | mapblock_T *mp; |
1637 | mapblock_T *mp2; |
1638 | mapblock_T *mp_match; |
1639 | int mp_match_len = 0; |
1640 | int timedout = FALSE; /* waited for more than 1 second |
1641 | for mapping to complete */ |
1642 | int mapdepth = 0; /* check for recursive mapping */ |
1643 | int mode_deleted = FALSE; /* set when mode has been deleted */ |
1644 | int local_State; |
1645 | int mlen; |
1646 | int max_mlen; |
1647 | int i; |
1648 | int new_wcol, new_wrow; |
1649 | int n; |
1650 | int nolmaplen; |
1651 | int old_wcol, old_wrow; |
1652 | int wait_tb_len; |
1653 | |
1654 | /* |
1655 | * This function doesn't work very well when called recursively. This may |
1656 | * happen though, because of: |
1657 | * 1. The call to add_to_showcmd(). char_avail() is then used to check if |
1658 | * there is a character available, which calls this function. In that |
1659 | * case we must return NUL, to indicate no character is available. |
1660 | * 2. A GUI callback function writes to the screen, causing a |
1661 | * wait_return(). |
1662 | * Using ":normal" can also do this, but it saves the typeahead buffer, |
1663 | * thus it should be OK. But don't get a key from the user then. |
1664 | */ |
1665 | if (vgetc_busy > 0 |
1666 | && ex_normal_busy == 0 |
1667 | ) |
1668 | return NUL; |
1669 | |
1670 | local_State = get_real_state(); |
1671 | |
1672 | ++vgetc_busy; |
1673 | |
1674 | if (advance) |
1675 | KeyStuffed = FALSE; |
1676 | |
1677 | init_typebuf(); |
1678 | start_stuff(); |
1679 | if (advance && typebuf.tb_maplen == 0) { |
1680 | reg_executing = 0; |
1681 | } |
1682 | do { |
1683 | /* |
1684 | * get a character: 1. from the stuffbuffer |
1685 | */ |
1686 | if (typeahead_char != 0) { |
1687 | c = typeahead_char; |
1688 | if (advance) |
1689 | typeahead_char = 0; |
1690 | } else { |
1691 | c = read_readbuffers(advance); |
1692 | } |
1693 | if (c != NUL && !got_int) { |
1694 | if (advance) { |
1695 | // KeyTyped = false; When the command that stuffed something |
1696 | // was typed, behave like the stuffed command was typed. |
1697 | // needed for CTRL-W CTRL-] to open a fold, for example. |
1698 | KeyStuffed = true; |
1699 | } |
1700 | if (typebuf.tb_no_abbr_cnt == 0) |
1701 | typebuf.tb_no_abbr_cnt = 1; /* no abbreviations now */ |
1702 | } else { |
1703 | /* |
1704 | * Loop until we either find a matching mapped key, or we |
1705 | * are sure that it is not a mapped key. |
1706 | * If a mapped key sequence is found we go back to the start to |
1707 | * try re-mapping. |
1708 | */ |
1709 | for (;; ) { |
1710 | /* |
1711 | * os_breakcheck() is slow, don't use it too often when |
1712 | * inside a mapping. But call it each time for typed |
1713 | * characters. |
1714 | */ |
1715 | if (typebuf.tb_maplen) |
1716 | line_breakcheck(); |
1717 | else |
1718 | os_breakcheck(); /* check for CTRL-C */ |
1719 | keylen = 0; |
1720 | if (got_int) { |
1721 | // flush all input |
1722 | c = inchar(typebuf.tb_buf, typebuf.tb_buflen - 1, 0L); |
1723 | // If inchar() returns TRUE (script file was active) or we |
1724 | // are inside a mapping, get out of insert mode. |
1725 | // Otherwise we behave like having gotten a CTRL-C. |
1726 | // As a result typing CTRL-C in insert mode will |
1727 | // really insert a CTRL-C. |
1728 | if ((c || typebuf.tb_maplen) |
1729 | && (State & (INSERT + CMDLINE))) { |
1730 | c = ESC; |
1731 | } else { |
1732 | c = Ctrl_C; |
1733 | } |
1734 | flush_buffers(FLUSH_INPUT); // flush all typeahead |
1735 | |
1736 | if (advance) { |
1737 | /* Also record this character, it might be needed to |
1738 | * get out of Insert mode. */ |
1739 | *typebuf.tb_buf = (char_u)c; |
1740 | gotchars(typebuf.tb_buf, 1); |
1741 | } |
1742 | cmd_silent = false; |
1743 | |
1744 | break; |
1745 | } else if (typebuf.tb_len > 0) { |
1746 | /* |
1747 | * Check for a mappable key sequence. |
1748 | * Walk through one maphash[] list until we find an |
1749 | * entry that matches. |
1750 | * |
1751 | * Don't look for mappings if: |
1752 | * - no_mapping set: mapping disabled (e.g. for CTRL-V) |
1753 | * - maphash_valid not set: no mappings present. |
1754 | * - typebuf.tb_buf[typebuf.tb_off] should not be remapped |
1755 | * - in insert or cmdline mode and 'paste' option set |
1756 | * - waiting for "hit return to continue" and CR or SPACE |
1757 | * typed |
1758 | * - waiting for a char with --more-- |
1759 | * - in Ctrl-X mode, and we get a valid char for that mode |
1760 | */ |
1761 | mp = NULL; |
1762 | max_mlen = 0; |
1763 | c1 = typebuf.tb_buf[typebuf.tb_off]; |
1764 | if (no_mapping == 0 && maphash_valid |
1765 | && (no_zero_mapping == 0 || c1 != '0') |
1766 | && (typebuf.tb_maplen == 0 |
1767 | || (p_remap |
1768 | && (typebuf.tb_noremap[typebuf.tb_off] |
1769 | & (RM_NONE|RM_ABBR)) == 0)) |
1770 | && !(p_paste && (State & (INSERT + CMDLINE))) |
1771 | && !(State == HITRETURN && (c1 == CAR || c1 == ' ')) |
1772 | && State != ASKMORE |
1773 | && State != CONFIRM |
1774 | && !((ctrl_x_mode_not_default() && vim_is_ctrl_x_key(c1)) |
1775 | || ((compl_cont_status & CONT_LOCAL) |
1776 | && (c1 == Ctrl_N || c1 == Ctrl_P))) |
1777 | ) { |
1778 | if (c1 == K_SPECIAL) { |
1779 | nolmaplen = 2; |
1780 | } else { |
1781 | LANGMAP_ADJUST(c1, (State & (CMDLINE | INSERT)) == 0 |
1782 | && get_real_state() != SELECTMODE); |
1783 | nolmaplen = 0; |
1784 | } |
1785 | /* First try buffer-local mappings. */ |
1786 | mp = curbuf->b_maphash[MAP_HASH(local_State, c1)]; |
1787 | mp2 = maphash[MAP_HASH(local_State, c1)]; |
1788 | if (mp == NULL) { |
1789 | /* There are no buffer-local mappings. */ |
1790 | mp = mp2; |
1791 | mp2 = NULL; |
1792 | } |
1793 | /* |
1794 | * Loop until a partly matching mapping is found or |
1795 | * all (local) mappings have been checked. |
1796 | * The longest full match is remembered in "mp_match". |
1797 | * A full match is only accepted if there is no partly |
1798 | * match, so "aa" and "aaa" can both be mapped. |
1799 | */ |
1800 | mp_match = NULL; |
1801 | mp_match_len = 0; |
1802 | for (; mp != NULL; |
1803 | mp->m_next == NULL ? (mp = mp2, mp2 = NULL) : |
1804 | (mp = mp->m_next)) { |
1805 | /* |
1806 | * Only consider an entry if the first character |
1807 | * matches and it is for the current state. |
1808 | * Skip ":lmap" mappings if keys were mapped. |
1809 | */ |
1810 | if (mp->m_keys[0] == c1 |
1811 | && (mp->m_mode & local_State) |
1812 | && ((mp->m_mode & LANGMAP) == 0 |
1813 | || typebuf.tb_maplen == 0)) { |
1814 | int nomap = nolmaplen; |
1815 | int c2; |
1816 | /* find the match length of this mapping */ |
1817 | for (mlen = 1; mlen < typebuf.tb_len; ++mlen) { |
1818 | c2 = typebuf.tb_buf[typebuf.tb_off + mlen]; |
1819 | if (nomap > 0) |
1820 | --nomap; |
1821 | else if (c2 == K_SPECIAL) |
1822 | nomap = 2; |
1823 | else |
1824 | LANGMAP_ADJUST(c2, TRUE); |
1825 | if (mp->m_keys[mlen] != c2) |
1826 | break; |
1827 | } |
1828 | |
1829 | /* Don't allow mapping the first byte(s) of a |
1830 | * multi-byte char. Happens when mapping |
1831 | * <M-a> and then changing 'encoding'. Beware |
1832 | * that 0x80 is escaped. */ |
1833 | char_u *p1 = mp->m_keys; |
1834 | char_u *p2 = (char_u *)mb_unescape((const char **)&p1); |
1835 | |
1836 | if (has_mbyte && p2 != NULL && MB_BYTE2LEN(c1) > MB_PTR2LEN(p2)) |
1837 | mlen = 0; |
1838 | /* |
1839 | * Check an entry whether it matches. |
1840 | * - Full match: mlen == keylen |
1841 | * - Partly match: mlen == typebuf.tb_len |
1842 | */ |
1843 | keylen = mp->m_keylen; |
1844 | if (mlen == keylen |
1845 | || (mlen == typebuf.tb_len |
1846 | && typebuf.tb_len < keylen)) { |
1847 | /* |
1848 | * If only script-local mappings are |
1849 | * allowed, check if the mapping starts |
1850 | * with K_SNR. |
1851 | */ |
1852 | s = typebuf.tb_noremap + typebuf.tb_off; |
1853 | if (*s == RM_SCRIPT |
1854 | && (mp->m_keys[0] != K_SPECIAL |
1855 | || mp->m_keys[1] != KS_EXTRA |
1856 | || mp->m_keys[2] |
1857 | != (int)KE_SNR)) |
1858 | continue; |
1859 | /* |
1860 | * If one of the typed keys cannot be |
1861 | * remapped, skip the entry. |
1862 | */ |
1863 | for (n = mlen; --n >= 0; ) |
1864 | if (*s++ & (RM_NONE|RM_ABBR)) |
1865 | break; |
1866 | if (n >= 0) |
1867 | continue; |
1868 | |
1869 | if (keylen > typebuf.tb_len) { |
1870 | if (!timedout && !(mp_match != NULL |
1871 | && mp_match->m_nowait)) { |
1872 | /* break at a partly match */ |
1873 | keylen = KEYLEN_PART_MAP; |
1874 | break; |
1875 | } |
1876 | } else if (keylen > mp_match_len |
1877 | || (keylen == mp_match_len |
1878 | && mp_match != NULL |
1879 | && (mp_match->m_mode & LANGMAP) == 0 |
1880 | && (mp->m_mode & LANGMAP) != 0)) { |
1881 | // found a longer match |
1882 | mp_match = mp; |
1883 | mp_match_len = keylen; |
1884 | } |
1885 | } else { |
1886 | // No match; may have to check for termcode at next character. |
1887 | if (max_mlen < mlen) { |
1888 | max_mlen = mlen; |
1889 | } |
1890 | } |
1891 | } |
1892 | } |
1893 | |
1894 | /* If no partly match found, use the longest full |
1895 | * match. */ |
1896 | if (keylen != KEYLEN_PART_MAP) { |
1897 | mp = mp_match; |
1898 | keylen = mp_match_len; |
1899 | } |
1900 | } |
1901 | |
1902 | if (*p_pt != NUL && mp == NULL && (State & (INSERT|NORMAL))) { |
1903 | bool match = typebuf_match_len(p_pt, &mlen); |
1904 | if (match) { |
1905 | // write chars to script file(s) |
1906 | if (mlen > typebuf.tb_maplen) { |
1907 | gotchars(typebuf.tb_buf + typebuf.tb_off + typebuf.tb_maplen, |
1908 | (size_t)(mlen - typebuf.tb_maplen)); |
1909 | } |
1910 | |
1911 | del_typebuf(mlen, 0); // Remove the chars. |
1912 | set_option_value("paste" , !p_paste, NULL, 0); |
1913 | if (!(State & INSERT)) { |
1914 | msg_col = 0; |
1915 | msg_row = Rows - 1; |
1916 | msg_clr_eos(); // clear ruler |
1917 | } |
1918 | status_redraw_all(); |
1919 | redraw_statuslines(); |
1920 | showmode(); |
1921 | setcursor(); |
1922 | continue; |
1923 | } |
1924 | /* Need more chars for partly match. */ |
1925 | if (mlen == typebuf.tb_len) |
1926 | keylen = KEYLEN_PART_KEY; |
1927 | else if (max_mlen < mlen) |
1928 | /* no match, may have to check for termcode at |
1929 | * next character */ |
1930 | max_mlen = mlen + 1; |
1931 | } |
1932 | |
1933 | if ((mp == NULL || max_mlen >= mp_match_len) |
1934 | && keylen != KEYLEN_PART_MAP) { |
1935 | // No matching mapping found or found a non-matching mapping that |
1936 | // matches at least what the matching mapping matched |
1937 | keylen = 0; |
1938 | (void)keylen; // suppress clang/dead assignment |
1939 | // If there was no mapping, use the character from the typeahead |
1940 | // buffer right here. Otherwise, use the mapping (loop around). |
1941 | if (mp == NULL) { |
1942 | // get a character: 2. from the typeahead buffer |
1943 | c = typebuf.tb_buf[typebuf.tb_off] & 255; |
1944 | if (advance) { // remove chars from tb_buf |
1945 | cmd_silent = (typebuf.tb_silent > 0); |
1946 | if (typebuf.tb_maplen > 0) { |
1947 | KeyTyped = false; |
1948 | } else { |
1949 | KeyTyped = true; |
1950 | // write char to script file(s) |
1951 | gotchars(typebuf.tb_buf + typebuf.tb_off, 1); |
1952 | } |
1953 | KeyNoremap = typebuf.tb_noremap[typebuf.tb_off]; |
1954 | del_typebuf(1, 0); |
1955 | } |
1956 | break; // got character, break for loop |
1957 | } else { |
1958 | keylen = mp_match_len; |
1959 | } |
1960 | } |
1961 | |
1962 | /* complete match */ |
1963 | if (keylen >= 0 && keylen <= typebuf.tb_len) { |
1964 | int save_m_expr; |
1965 | int save_m_noremap; |
1966 | int save_m_silent; |
1967 | char_u *save_m_keys; |
1968 | char_u *save_m_str; |
1969 | |
1970 | // Write chars to script file(s) |
1971 | // Note: :lmap mappings are written *after* being applied. #5658 |
1972 | if (keylen > typebuf.tb_maplen && (mp->m_mode & LANGMAP) == 0) { |
1973 | gotchars(typebuf.tb_buf + typebuf.tb_off + typebuf.tb_maplen, |
1974 | (size_t)(keylen - typebuf.tb_maplen)); |
1975 | } |
1976 | |
1977 | cmd_silent = (typebuf.tb_silent > 0); |
1978 | del_typebuf(keylen, 0); /* remove the mapped keys */ |
1979 | |
1980 | /* |
1981 | * Put the replacement string in front of mapstr. |
1982 | * The depth check catches ":map x y" and ":map y x". |
1983 | */ |
1984 | if (++mapdepth >= p_mmd) { |
1985 | EMSG(_("E223: recursive mapping" )); |
1986 | if (State & CMDLINE) |
1987 | redrawcmdline(); |
1988 | else |
1989 | setcursor(); |
1990 | flush_buffers(FLUSH_MINIMAL); |
1991 | mapdepth = 0; // for next one |
1992 | c = -1; |
1993 | break; |
1994 | } |
1995 | |
1996 | /* |
1997 | * In Select mode and a Visual mode mapping is used: |
1998 | * Switch to Visual mode temporarily. Append K_SELECT |
1999 | * to switch back to Select mode. |
2000 | */ |
2001 | if (VIsual_active && VIsual_select |
2002 | && (mp->m_mode & VISUAL)) { |
2003 | VIsual_select = FALSE; |
2004 | (void)ins_typebuf(K_SELECT_STRING, REMAP_NONE, |
2005 | 0, TRUE, FALSE); |
2006 | } |
2007 | |
2008 | /* Copy the values from *mp that are used, because |
2009 | * evaluating the expression may invoke a function |
2010 | * that redefines the mapping, thereby making *mp |
2011 | * invalid. */ |
2012 | save_m_expr = mp->m_expr; |
2013 | save_m_noremap = mp->m_noremap; |
2014 | save_m_silent = mp->m_silent; |
2015 | save_m_keys = NULL; /* only saved when needed */ |
2016 | save_m_str = NULL; /* only saved when needed */ |
2017 | |
2018 | /* |
2019 | * Handle ":map <expr>": evaluate the {rhs} as an |
2020 | * expression. Also save and restore the command line |
2021 | * for "normal :". |
2022 | */ |
2023 | if (mp->m_expr) { |
2024 | int save_vgetc_busy = vgetc_busy; |
2025 | |
2026 | vgetc_busy = 0; |
2027 | save_m_keys = vim_strsave(mp->m_keys); |
2028 | save_m_str = vim_strsave(mp->m_str); |
2029 | s = eval_map_expr(save_m_str, NUL); |
2030 | vgetc_busy = save_vgetc_busy; |
2031 | } else |
2032 | s = mp->m_str; |
2033 | |
2034 | /* |
2035 | * Insert the 'to' part in the typebuf.tb_buf. |
2036 | * If 'from' field is the same as the start of the |
2037 | * 'to' field, don't remap the first character (but do |
2038 | * allow abbreviations). |
2039 | * If m_noremap is set, don't remap the whole 'to' |
2040 | * part. |
2041 | */ |
2042 | if (s == NULL) |
2043 | i = FAIL; |
2044 | else { |
2045 | int noremap; |
2046 | |
2047 | // If this is a LANGMAP mapping, then we didn't record the keys |
2048 | // at the start of the function and have to record them now. |
2049 | if (keylen > typebuf.tb_maplen && (mp->m_mode & LANGMAP) != 0) { |
2050 | gotchars(s, STRLEN(s)); |
2051 | } |
2052 | |
2053 | if (save_m_noremap != REMAP_YES) |
2054 | noremap = save_m_noremap; |
2055 | else if ( |
2056 | STRNCMP(s, save_m_keys != NULL |
2057 | ? save_m_keys : mp->m_keys, |
2058 | (size_t)keylen) |
2059 | != 0) |
2060 | noremap = REMAP_YES; |
2061 | else |
2062 | noremap = REMAP_SKIP; |
2063 | i = ins_typebuf(s, noremap, |
2064 | 0, TRUE, cmd_silent || save_m_silent); |
2065 | if (save_m_expr) |
2066 | xfree(s); |
2067 | } |
2068 | xfree(save_m_keys); |
2069 | xfree(save_m_str); |
2070 | if (i == FAIL) { |
2071 | c = -1; |
2072 | break; |
2073 | } |
2074 | continue; |
2075 | } |
2076 | } |
2077 | |
2078 | /* |
2079 | * get a character: 3. from the user - handle <Esc> in Insert mode |
2080 | */ |
2081 | /* |
2082 | * special case: if we get an <ESC> in insert mode and there |
2083 | * are no more characters at once, we pretend to go out of |
2084 | * insert mode. This prevents the one second delay after |
2085 | * typing an <ESC>. If we get something after all, we may |
2086 | * have to redisplay the mode. That the cursor is in the wrong |
2087 | * place does not matter. |
2088 | */ |
2089 | c = 0; |
2090 | new_wcol = curwin->w_wcol; |
2091 | new_wrow = curwin->w_wrow; |
2092 | if (advance |
2093 | && typebuf.tb_len == 1 |
2094 | && typebuf.tb_buf[typebuf.tb_off] == ESC |
2095 | && !no_mapping |
2096 | && ex_normal_busy == 0 |
2097 | && typebuf.tb_maplen == 0 |
2098 | && (State & INSERT) |
2099 | && (p_timeout |
2100 | || (keylen == KEYLEN_PART_KEY && p_ttimeout)) |
2101 | && (c = inchar(typebuf.tb_buf + typebuf.tb_off + typebuf.tb_len, |
2102 | 3, 25L)) == 0) { |
2103 | colnr_T col = 0, vcol; |
2104 | char_u *ptr; |
2105 | |
2106 | if (mode_displayed) { |
2107 | unshowmode(TRUE); |
2108 | mode_deleted = TRUE; |
2109 | } |
2110 | validate_cursor(); |
2111 | old_wcol = curwin->w_wcol; |
2112 | old_wrow = curwin->w_wrow; |
2113 | |
2114 | /* move cursor left, if possible */ |
2115 | if (curwin->w_cursor.col != 0) { |
2116 | if (curwin->w_wcol > 0) { |
2117 | if (did_ai) { |
2118 | /* |
2119 | * We are expecting to truncate the trailing |
2120 | * white-space, so find the last non-white |
2121 | * character -- webb |
2122 | */ |
2123 | col = vcol = curwin->w_wcol = 0; |
2124 | ptr = get_cursor_line_ptr(); |
2125 | while (col < curwin->w_cursor.col) { |
2126 | if (!ascii_iswhite(ptr[col])) |
2127 | curwin->w_wcol = vcol; |
2128 | vcol += lbr_chartabsize(ptr, ptr + col, |
2129 | (colnr_T)vcol); |
2130 | if (has_mbyte) |
2131 | col += (*mb_ptr2len)(ptr + col); |
2132 | else |
2133 | ++col; |
2134 | } |
2135 | curwin->w_wrow = curwin->w_cline_row |
2136 | + curwin->w_wcol / curwin->w_width_inner; |
2137 | curwin->w_wcol %= curwin->w_width_inner; |
2138 | curwin->w_wcol += curwin_col_off(); |
2139 | col = 0; /* no correction needed */ |
2140 | } else { |
2141 | --curwin->w_wcol; |
2142 | col = curwin->w_cursor.col - 1; |
2143 | } |
2144 | } else if (curwin->w_p_wrap && curwin->w_wrow) { |
2145 | curwin->w_wrow--; |
2146 | curwin->w_wcol = curwin->w_width_inner - 1; |
2147 | col = curwin->w_cursor.col - 1; |
2148 | } |
2149 | if (col > 0 && curwin->w_wcol > 0) { |
2150 | // Correct when the cursor is on the right halve |
2151 | // of a double-wide character. |
2152 | ptr = get_cursor_line_ptr(); |
2153 | col -= utf_head_off(ptr, ptr + col); |
2154 | if (utf_ptr2cells(ptr + col) > 1) { |
2155 | curwin->w_wcol--; |
2156 | } |
2157 | } |
2158 | } |
2159 | setcursor(); |
2160 | ui_flush(); |
2161 | new_wcol = curwin->w_wcol; |
2162 | new_wrow = curwin->w_wrow; |
2163 | curwin->w_wcol = old_wcol; |
2164 | curwin->w_wrow = old_wrow; |
2165 | } |
2166 | if (c < 0) |
2167 | continue; /* end of input script reached */ |
2168 | |
2169 | // Allow mapping for just typed characters. When we get here c |
2170 | // is the number of extra bytes and typebuf.tb_len is 1. |
2171 | for (n = 1; n <= c; n++) { |
2172 | typebuf.tb_noremap[typebuf.tb_off + n] = RM_YES; |
2173 | } |
2174 | typebuf.tb_len += c; |
2175 | |
2176 | /* buffer full, don't map */ |
2177 | if (typebuf.tb_len >= typebuf.tb_maplen + MAXMAPLEN) { |
2178 | timedout = TRUE; |
2179 | continue; |
2180 | } |
2181 | |
2182 | if (ex_normal_busy > 0) { |
2183 | static int tc = 0; |
2184 | |
2185 | /* No typeahead left and inside ":normal". Must return |
2186 | * something to avoid getting stuck. When an incomplete |
2187 | * mapping is present, behave like it timed out. */ |
2188 | if (typebuf.tb_len > 0) { |
2189 | timedout = TRUE; |
2190 | continue; |
2191 | } |
2192 | /* When 'insertmode' is set, ESC just beeps in Insert |
2193 | * mode. Use CTRL-L to make edit() return. |
2194 | * For the command line only CTRL-C always breaks it. |
2195 | * For the cmdline window: Alternate between ESC and |
2196 | * CTRL-C: ESC for most situations and CTRL-C to close the |
2197 | * cmdline window. */ |
2198 | if (p_im && (State & INSERT)) |
2199 | c = Ctrl_L; |
2200 | else if ((State & CMDLINE) |
2201 | || (cmdwin_type > 0 && tc == ESC) |
2202 | ) |
2203 | c = Ctrl_C; |
2204 | else |
2205 | c = ESC; |
2206 | tc = c; |
2207 | break; |
2208 | } |
2209 | |
2210 | /* |
2211 | * get a character: 3. from the user - update display |
2212 | */ |
2213 | /* In insert mode a screen update is skipped when characters |
2214 | * are still available. But when those available characters |
2215 | * are part of a mapping, and we are going to do a blocking |
2216 | * wait here. Need to update the screen to display the |
2217 | * changed text so far. Also for when 'lazyredraw' is set and |
2218 | * redrawing was postponed because there was something in the |
2219 | * input buffer (e.g., termresponse). */ |
2220 | if (((State & INSERT) != 0 || p_lz) && (State & CMDLINE) == 0 |
2221 | && advance && must_redraw != 0 && !need_wait_return) { |
2222 | update_screen(0); |
2223 | setcursor(); /* put cursor back where it belongs */ |
2224 | } |
2225 | |
2226 | /* |
2227 | * If we have a partial match (and are going to wait for more |
2228 | * input from the user), show the partially matched characters |
2229 | * to the user with showcmd. |
2230 | */ |
2231 | i = 0; |
2232 | c1 = 0; |
2233 | if (typebuf.tb_len > 0 && advance && !exmode_active) { |
2234 | if (((State & (NORMAL | INSERT)) || State == LANGMAP) |
2235 | && State != HITRETURN) { |
2236 | /* this looks nice when typing a dead character map */ |
2237 | if (State & INSERT |
2238 | && ptr2cells(typebuf.tb_buf + typebuf.tb_off |
2239 | + typebuf.tb_len - 1) == 1) { |
2240 | edit_putchar(typebuf.tb_buf[typebuf.tb_off |
2241 | + typebuf.tb_len - 1], FALSE); |
2242 | setcursor(); /* put cursor back where it belongs */ |
2243 | c1 = 1; |
2244 | } |
2245 | /* need to use the col and row from above here */ |
2246 | old_wcol = curwin->w_wcol; |
2247 | old_wrow = curwin->w_wrow; |
2248 | curwin->w_wcol = new_wcol; |
2249 | curwin->w_wrow = new_wrow; |
2250 | push_showcmd(); |
2251 | if (typebuf.tb_len > SHOWCMD_COLS) |
2252 | i = typebuf.tb_len - SHOWCMD_COLS; |
2253 | while (i < typebuf.tb_len) |
2254 | (void)add_to_showcmd(typebuf.tb_buf[typebuf.tb_off |
2255 | + i++]); |
2256 | curwin->w_wcol = old_wcol; |
2257 | curwin->w_wrow = old_wrow; |
2258 | } |
2259 | |
2260 | // this looks nice when typing a dead character map |
2261 | if ((State & CMDLINE) && cmdline_star == 0) { |
2262 | char_u *p = typebuf.tb_buf + typebuf.tb_off + typebuf.tb_len - 1; |
2263 | if (ptr2cells(p) == 1 && *p < 128) { |
2264 | putcmdline((char)(*p), false); |
2265 | c1 = 1; |
2266 | } |
2267 | } |
2268 | } |
2269 | |
2270 | /* |
2271 | * get a character: 3. from the user - get it |
2272 | */ |
2273 | if (typebuf.tb_len == 0) { |
2274 | // timedout may have been set while waiting for a mapping |
2275 | // that has a <Nop> RHS. |
2276 | timedout = false; |
2277 | } |
2278 | |
2279 | long wait_time = 0; |
2280 | |
2281 | if (advance) { |
2282 | if (typebuf.tb_len == 0 |
2283 | || !(p_timeout || (p_ttimeout && keylen == KEYLEN_PART_KEY))) { |
2284 | // blocking wait |
2285 | wait_time = -1L; |
2286 | } else if (keylen == KEYLEN_PART_KEY && p_ttm >= 0) { |
2287 | wait_time = p_ttm; |
2288 | } else { |
2289 | wait_time = p_tm; |
2290 | } |
2291 | } |
2292 | |
2293 | wait_tb_len = typebuf.tb_len; |
2294 | c = inchar(typebuf.tb_buf + typebuf.tb_off + typebuf.tb_len, |
2295 | typebuf.tb_buflen - typebuf.tb_off - typebuf.tb_len - 1, |
2296 | wait_time); |
2297 | |
2298 | if (i != 0) |
2299 | pop_showcmd(); |
2300 | if (c1 == 1) { |
2301 | if (State & INSERT) |
2302 | edit_unputchar(); |
2303 | if (State & CMDLINE) |
2304 | unputcmdline(); |
2305 | else |
2306 | setcursor(); /* put cursor back where it belongs */ |
2307 | } |
2308 | |
2309 | if (c < 0) |
2310 | continue; /* end of input script reached */ |
2311 | if (c == NUL) { /* no character available */ |
2312 | if (!advance) |
2313 | break; |
2314 | if (wait_tb_len > 0) { /* timed out */ |
2315 | timedout = TRUE; |
2316 | continue; |
2317 | } |
2318 | } else { /* allow mapping for just typed characters */ |
2319 | while (typebuf.tb_buf[typebuf.tb_off |
2320 | + typebuf.tb_len] != NUL) |
2321 | typebuf.tb_noremap[typebuf.tb_off |
2322 | + typebuf.tb_len++] = RM_YES; |
2323 | } |
2324 | } /* for (;;) */ |
2325 | } /* if (!character from stuffbuf) */ |
2326 | |
2327 | /* if advance is FALSE don't loop on NULs */ |
2328 | } while (c < 0 || (advance && c == NUL)); |
2329 | |
2330 | /* |
2331 | * The "INSERT" message is taken care of here: |
2332 | * if we return an ESC to exit insert mode, the message is deleted |
2333 | * if we don't return an ESC but deleted the message before, redisplay it |
2334 | */ |
2335 | if (advance && p_smd && msg_silent == 0 && (State & INSERT)) { |
2336 | if (c == ESC && !mode_deleted && !no_mapping && mode_displayed) { |
2337 | if (typebuf.tb_len && !KeyTyped) |
2338 | redraw_cmdline = TRUE; /* delete mode later */ |
2339 | else |
2340 | unshowmode(FALSE); |
2341 | } else if (c != ESC && mode_deleted) { |
2342 | if (typebuf.tb_len && !KeyTyped) |
2343 | redraw_cmdline = TRUE; /* show mode later */ |
2344 | else |
2345 | showmode(); |
2346 | } |
2347 | } |
2348 | |
2349 | if (timedout && c == ESC) { |
2350 | char_u nop_buf[3]; |
2351 | |
2352 | // When recording there will be no timeout. Add a <Nop> after the ESC |
2353 | // to avoid that it forms a key code with following characters. |
2354 | nop_buf[0] = K_SPECIAL; |
2355 | nop_buf[1] = KS_EXTRA; |
2356 | nop_buf[2] = KE_NOP; |
2357 | gotchars(nop_buf, 3); |
2358 | } |
2359 | |
2360 | --vgetc_busy; |
2361 | |
2362 | return c; |
2363 | } |
2364 | |
2365 | /* |
2366 | * inchar() - get one character from |
2367 | * 1. a scriptfile |
2368 | * 2. the keyboard |
2369 | * |
2370 | * As much characters as we can get (upto 'maxlen') are put in "buf" and |
2371 | * NUL terminated (buffer length must be 'maxlen' + 1). |
2372 | * Minimum for "maxlen" is 3!!!! |
2373 | * |
2374 | * "tb_change_cnt" is the value of typebuf.tb_change_cnt if "buf" points into |
2375 | * it. When typebuf.tb_change_cnt changes (e.g., when a message is received |
2376 | * from a remote client) "buf" can no longer be used. "tb_change_cnt" is 0 |
2377 | * otherwise. |
2378 | * |
2379 | * If we got an interrupt all input is read until none is available. |
2380 | * |
2381 | * If wait_time == 0 there is no waiting for the char. |
2382 | * If wait_time == n we wait for n msec for a character to arrive. |
2383 | * If wait_time == -1 we wait forever for a character to arrive. |
2384 | * |
2385 | * Return the number of obtained characters. |
2386 | * Return -1 when end of input script reached. |
2387 | */ |
2388 | int inchar( |
2389 | char_u *buf, |
2390 | int maxlen, |
2391 | long wait_time // milli seconds |
2392 | ) |
2393 | { |
2394 | int len = 0; // Init for GCC. |
2395 | int retesc = false; // Return ESC with gotint. |
2396 | const int tb_change_cnt = typebuf.tb_change_cnt; |
2397 | |
2398 | if (wait_time == -1L || wait_time > 100L) { |
2399 | // flush output before waiting |
2400 | ui_flush(); |
2401 | } |
2402 | |
2403 | /* |
2404 | * Don't reset these when at the hit-return prompt, otherwise an endless |
2405 | * recursive loop may result (write error in swapfile, hit-return, timeout |
2406 | * on char wait, flush swapfile, write error....). |
2407 | */ |
2408 | if (State != HITRETURN) { |
2409 | did_outofmem_msg = FALSE; /* display out of memory message (again) */ |
2410 | did_swapwrite_msg = FALSE; /* display swap file write error again */ |
2411 | } |
2412 | undo_off = FALSE; /* restart undo now */ |
2413 | |
2414 | // Get a character from a script file if there is one. |
2415 | // If interrupted: Stop reading script files, close them all. |
2416 | ptrdiff_t read_size = -1; |
2417 | while (scriptin[curscript] != NULL && read_size <= 0 && !ignore_script) { |
2418 | char script_char; |
2419 | if (got_int |
2420 | || (read_size = file_read(scriptin[curscript], &script_char, 1)) != 1) { |
2421 | // Reached EOF or some error occurred. |
2422 | // Careful: closescript() frees typebuf.tb_buf[] and buf[] may |
2423 | // point inside typebuf.tb_buf[]. Don't use buf[] after this! |
2424 | closescript(); |
2425 | // When reading script file is interrupted, return an ESC to get |
2426 | // back to normal mode. |
2427 | // Otherwise return -1, because typebuf.tb_buf[] has changed. |
2428 | if (got_int) { |
2429 | retesc = true; |
2430 | } else { |
2431 | return -1; |
2432 | } |
2433 | } else { |
2434 | buf[0] = (char_u)script_char; |
2435 | len = 1; |
2436 | } |
2437 | } |
2438 | |
2439 | if (read_size <= 0) { // Did not get a character from script. |
2440 | // If we got an interrupt, skip all previously typed characters and |
2441 | // return TRUE if quit reading script file. |
2442 | // Stop reading typeahead when a single CTRL-C was read, |
2443 | // fill_input_buf() returns this when not able to read from stdin. |
2444 | // Don't use buf[] here, closescript() may have freed typebuf.tb_buf[] |
2445 | // and buf may be pointing inside typebuf.tb_buf[]. |
2446 | if (got_int) { |
2447 | #define DUM_LEN MAXMAPLEN * 3 + 3 |
2448 | char_u dum[DUM_LEN + 1]; |
2449 | |
2450 | for (;; ) { |
2451 | len = os_inchar(dum, DUM_LEN, 0L, 0, NULL); |
2452 | if (len == 0 || (len == 1 && dum[0] == 3)) { |
2453 | break; |
2454 | } |
2455 | } |
2456 | return retesc; |
2457 | } |
2458 | |
2459 | // Always flush the output characters when getting input characters |
2460 | // from the user. |
2461 | ui_flush(); |
2462 | |
2463 | // Fill up to a third of the buffer, because each character may be |
2464 | // tripled below. |
2465 | len = os_inchar(buf, maxlen / 3, (int)wait_time, tb_change_cnt, NULL); |
2466 | } |
2467 | |
2468 | // If the typebuf was changed further down, it is like nothing was added by |
2469 | // this call. |
2470 | if (typebuf_changed(tb_change_cnt)) { |
2471 | return 0; |
2472 | } |
2473 | |
2474 | // Note the change in the typeahead buffer, this matters for when |
2475 | // vgetorpeek() is called recursively, e.g. using getchar(1) in a timer |
2476 | // function. |
2477 | if (len > 0 && ++typebuf.tb_change_cnt == 0) { |
2478 | typebuf.tb_change_cnt = 1; |
2479 | } |
2480 | |
2481 | return fix_input_buffer(buf, len); |
2482 | } |
2483 | |
2484 | /* |
2485 | * Fix typed characters for use by vgetc() and check_termcode(). |
2486 | * buf[] must have room to triple the number of bytes! |
2487 | * Returns the new length. |
2488 | */ |
2489 | int fix_input_buffer(char_u *buf, int len) |
2490 | { |
2491 | if (!using_script()) { |
2492 | // Should not escape K_SPECIAL/CSI reading input from the user because vim |
2493 | // key codes keys are processed in input.c/input_enqueue. |
2494 | buf[len] = NUL; |
2495 | return len; |
2496 | } |
2497 | |
2498 | // Reading from script, need to process special bytes |
2499 | int i; |
2500 | char_u *p = buf; |
2501 | |
2502 | // Two characters are special: NUL and K_SPECIAL. |
2503 | // Replace NUL by K_SPECIAL KS_ZERO KE_FILLER |
2504 | // Replace K_SPECIAL by K_SPECIAL KS_SPECIAL KE_FILLER |
2505 | // Replace CSI by K_SPECIAL KS_EXTRA KE_CSI |
2506 | for (i = len; --i >= 0; ++p) { |
2507 | if (p[0] == NUL |
2508 | || (p[0] == K_SPECIAL |
2509 | && (i < 2 || p[1] != KS_EXTRA))) { |
2510 | memmove(p + 3, p + 1, (size_t)i); |
2511 | p[2] = (char_u)K_THIRD(p[0]); |
2512 | p[1] = (char_u)K_SECOND(p[0]); |
2513 | p[0] = K_SPECIAL; |
2514 | p += 2; |
2515 | len += 2; |
2516 | } |
2517 | } |
2518 | *p = NUL; // add trailing NUL |
2519 | return len; |
2520 | } |
2521 | |
2522 | /// Replace termcodes in the given LHS and RHS and store the results into the |
2523 | /// `lhs` and `rhs` of the given @ref MapArguments struct. |
2524 | /// |
2525 | /// `rhs` and `orig_rhs` will both point to new allocated buffers. `orig_rhs` |
2526 | /// will hold a copy of the given `orig_rhs`. |
2527 | /// |
2528 | /// The `*_len` variables will be set appropriately. If the length of |
2529 | /// the final `lhs` exceeds `MAXMAPLEN`, `lhs_len` will be set equal to the |
2530 | /// original larger length and `lhs` will be truncated. |
2531 | /// |
2532 | /// If RHS is equal to "<Nop>", `rhs` will be the empty string, `rhs_len` |
2533 | /// will be zero, and `rhs_is_noop` will be set to true. |
2534 | /// |
2535 | /// Any memory allocated by @ref replace_termcodes is freed before this function |
2536 | /// returns. |
2537 | /// |
2538 | /// @param[in] orig_lhs Original mapping LHS, with characters to replace. |
2539 | /// @param[in] orig_lhs_len `strlen` of orig_lhs. |
2540 | /// @param[in] orig_rhs Original mapping RHS, with characters to replace. |
2541 | /// @param[in] orig_rhs_len `strlen` of orig_rhs. |
2542 | /// @param[in] cpo_flags See param docs for @ref replace_termcodes. |
2543 | /// @param[out] mapargs MapArguments struct holding the replaced strings. |
2544 | void set_maparg_lhs_rhs(const char_u *orig_lhs, const size_t orig_lhs_len, |
2545 | const char_u *orig_rhs, const size_t orig_rhs_len, |
2546 | int cpo_flags, MapArguments *mapargs) |
2547 | { |
2548 | char_u *lhs_buf = NULL; |
2549 | char_u *rhs_buf = NULL; |
2550 | |
2551 | // If mapping has been given as ^V<C_UP> say, then replace the term codes |
2552 | // with the appropriate two bytes. If it is a shifted special key, unshift |
2553 | // it too, giving another two bytes. |
2554 | // |
2555 | // replace_termcodes() may move the result to allocated memory, which |
2556 | // needs to be freed later (*lhs_buf and *rhs_buf). |
2557 | // replace_termcodes() also removes CTRL-Vs and sometimes backslashes. |
2558 | char_u *replaced = replace_termcodes(orig_lhs, orig_lhs_len, &lhs_buf, |
2559 | true, true, true, cpo_flags); |
2560 | mapargs->lhs_len = STRLEN(replaced); |
2561 | xstrlcpy((char *)mapargs->lhs, (char *)replaced, sizeof(mapargs->lhs)); |
2562 | |
2563 | mapargs->orig_rhs_len = orig_rhs_len; |
2564 | mapargs->orig_rhs = xcalloc(mapargs->orig_rhs_len + 1, sizeof(char_u)); |
2565 | xstrlcpy((char *)mapargs->orig_rhs, (char *)orig_rhs, |
2566 | mapargs->orig_rhs_len + 1); |
2567 | |
2568 | if (STRICMP(orig_rhs, "<nop>" ) == 0) { // "<Nop>" means nothing |
2569 | mapargs->rhs = xcalloc(1, sizeof(char_u)); // single null-char |
2570 | mapargs->rhs_len = 0; |
2571 | mapargs->rhs_is_noop = true; |
2572 | } else { |
2573 | replaced = replace_termcodes(orig_rhs, orig_rhs_len, &rhs_buf, |
2574 | false, true, true, cpo_flags); |
2575 | mapargs->rhs_len = STRLEN(replaced); |
2576 | mapargs->rhs_is_noop = false; |
2577 | mapargs->rhs = xcalloc(mapargs->rhs_len + 1, sizeof(char_u)); |
2578 | xstrlcpy((char *)mapargs->rhs, (char *)replaced, mapargs->rhs_len + 1); |
2579 | } |
2580 | |
2581 | xfree(lhs_buf); |
2582 | xfree(rhs_buf); |
2583 | } |
2584 | |
2585 | /// Parse a string of |:map-arguments| into a @ref MapArguments struct. |
2586 | /// |
2587 | /// Termcodes, backslashes, CTRL-V's, etc. inside the extracted {lhs} and |
2588 | /// {rhs} are replaced by @ref set_maparg_lhs_rhs. |
2589 | /// |
2590 | /// rhs and orig_rhs in the returned mapargs will be set to null or a pointer |
2591 | /// to allocated memory and should be freed even on error. |
2592 | /// |
2593 | /// @param[in] strargs String of map args, e.g. "<buffer> <expr><silent>". |
2594 | /// May contain leading or trailing whitespace. |
2595 | /// @param[in] is_unmap True, if strargs should be parsed like an |:unmap| |
2596 | /// command. |:unmap| commands interpret *all* text to the |
2597 | /// right of the last map argument as the {lhs} of the |
2598 | /// mapping, i.e. a literal ' ' character is treated like |
2599 | /// a "<space>", rather than separating the {lhs} from the |
2600 | /// {rhs}. |
2601 | /// @param[out] mapargs MapArguments struct holding all extracted argument |
2602 | /// values. |
2603 | /// @return 0 on success, 1 if invalid arguments are detected. |
2604 | int str_to_mapargs(const char_u *strargs, bool is_unmap, MapArguments *mapargs) |
2605 | { |
2606 | const char_u *to_parse = strargs; |
2607 | to_parse = skipwhite(to_parse); |
2608 | MapArguments parsed_args; // copy these into mapargs "all at once" when done |
2609 | memset(&parsed_args, 0, sizeof(parsed_args)); |
2610 | |
2611 | // Accept <buffer>, <nowait>, <silent>, <expr>, <script>, and <unique> in |
2612 | // any order. |
2613 | while (true) { |
2614 | if (STRNCMP(to_parse, "<buffer>" , 8) == 0) { |
2615 | to_parse = skipwhite(to_parse + 8); |
2616 | parsed_args.buffer = true; |
2617 | continue; |
2618 | } |
2619 | |
2620 | if (STRNCMP(to_parse, "<nowait>" , 8) == 0) { |
2621 | to_parse = skipwhite(to_parse + 8); |
2622 | parsed_args.nowait = true; |
2623 | continue; |
2624 | } |
2625 | |
2626 | if (STRNCMP(to_parse, "<silent>" , 8) == 0) { |
2627 | to_parse = skipwhite(to_parse + 8); |
2628 | parsed_args.silent = true; |
2629 | continue; |
2630 | } |
2631 | |
2632 | // Ignore obsolete "<special>" modifier. |
2633 | if (STRNCMP(to_parse, "<special>" , 9) == 0) { |
2634 | to_parse = skipwhite(to_parse + 9); |
2635 | continue; |
2636 | } |
2637 | |
2638 | if (STRNCMP(to_parse, "<script>" , 8) == 0) { |
2639 | to_parse = skipwhite(to_parse + 8); |
2640 | parsed_args.script = true; |
2641 | continue; |
2642 | } |
2643 | |
2644 | if (STRNCMP(to_parse, "<expr>" , 6) == 0) { |
2645 | to_parse = skipwhite(to_parse + 6); |
2646 | parsed_args.expr = true; |
2647 | continue; |
2648 | } |
2649 | |
2650 | if (STRNCMP(to_parse, "<unique>" , 8) == 0) { |
2651 | to_parse = skipwhite(to_parse + 8); |
2652 | parsed_args.unique = true; |
2653 | continue; |
2654 | } |
2655 | break; |
2656 | } |
2657 | |
2658 | // Find the next whitespace character, call that the end of {lhs}. |
2659 | // |
2660 | // If a character (e.g. whitespace) is immediately preceded by a CTRL-V, |
2661 | // "scan past" that character, i.e. don't "terminate" LHS with that character |
2662 | // if it's whitespace. |
2663 | // |
2664 | // Treat backslash like CTRL-V when 'cpoptions' does not contain 'B'. |
2665 | // |
2666 | // With :unmap, literal white space is included in the {lhs}; there is no |
2667 | // separate {rhs}. |
2668 | const char_u *lhs_end = to_parse; |
2669 | bool do_backslash = (vim_strchr(p_cpo, CPO_BSLASH) == NULL); |
2670 | while (*lhs_end && (is_unmap || !ascii_iswhite(*lhs_end))) { |
2671 | if ((lhs_end[0] == Ctrl_V || (do_backslash && lhs_end[0] == '\\')) |
2672 | && lhs_end[1] != NUL) { |
2673 | lhs_end++; // skip CTRL-V or backslash |
2674 | } |
2675 | lhs_end++; |
2676 | } |
2677 | |
2678 | // {lhs_end} is a pointer to the "terminating whitespace" after {lhs}. |
2679 | // Use that to initialize {rhs_start}. |
2680 | const char_u *rhs_start = skipwhite(lhs_end); |
2681 | |
2682 | // Given {lhs} might be larger than MAXMAPLEN before replace_termcodes |
2683 | // (e.g. "<Space>" is longer than ' '), so first copy into a buffer. |
2684 | size_t orig_lhs_len = (size_t)(lhs_end - to_parse); |
2685 | char_u *lhs_to_replace = xcalloc(orig_lhs_len + 1, sizeof(char_u)); |
2686 | xstrlcpy((char *)lhs_to_replace, (char *)to_parse, orig_lhs_len + 1); |
2687 | |
2688 | size_t orig_rhs_len = STRLEN(rhs_start); |
2689 | set_maparg_lhs_rhs(lhs_to_replace, orig_lhs_len, |
2690 | rhs_start, orig_rhs_len, |
2691 | CPO_TO_CPO_FLAGS, &parsed_args); |
2692 | |
2693 | xfree(lhs_to_replace); |
2694 | |
2695 | *mapargs = parsed_args; |
2696 | |
2697 | if (parsed_args.lhs_len > MAXMAPLEN) { |
2698 | return 1; |
2699 | } |
2700 | return 0; |
2701 | } |
2702 | |
2703 | /// Sets or removes a mapping or abbreviation in buffer `buf`. |
2704 | /// |
2705 | /// @param maptype @see do_map |
2706 | /// @param args Fully parsed and "preprocessed" arguments for the |
2707 | /// (un)map/abbrev command. Termcodes should have already been |
2708 | /// replaced; whitespace, `<` and `>` signs, etc. in {lhs} and |
2709 | /// {rhs} are assumed to be literal components of the mapping. |
2710 | /// @param mode @see do_map |
2711 | /// @param is_abbrev @see do_map |
2712 | /// @param buf Target Buffer |
2713 | int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev, |
2714 | buf_T *buf) |
2715 | { |
2716 | mapblock_T *mp, **mpp; |
2717 | char_u *p; |
2718 | int n; |
2719 | int len = 0; // init for GCC |
2720 | int did_it = false; |
2721 | int did_local = false; |
2722 | int round; |
2723 | int retval = 0; |
2724 | int hash; |
2725 | int new_hash; |
2726 | mapblock_T **abbr_table; |
2727 | mapblock_T **map_table; |
2728 | int noremap; |
2729 | |
2730 | map_table = maphash; |
2731 | abbr_table = &first_abbr; |
2732 | |
2733 | // For ":noremap" don't remap, otherwise do remap. |
2734 | if (maptype == 2) { |
2735 | noremap = REMAP_NONE; |
2736 | } else { |
2737 | noremap = REMAP_YES; |
2738 | } |
2739 | |
2740 | if (args->buffer) { |
2741 | // If <buffer> was given, we'll be searching through the buffer's |
2742 | // mappings/abbreviations, not the globals. |
2743 | map_table = buf->b_maphash; |
2744 | abbr_table = &buf->b_first_abbr; |
2745 | } |
2746 | if (args->script) { |
2747 | noremap = REMAP_SCRIPT; |
2748 | } |
2749 | |
2750 | validate_maphash(); |
2751 | |
2752 | bool has_lhs = (args->lhs[0] != NUL); |
2753 | bool has_rhs = (args->rhs[0] != NUL) || args->rhs_is_noop; |
2754 | |
2755 | // check for :unmap without argument |
2756 | if (maptype == 1 && !has_lhs) { |
2757 | retval = 1; |
2758 | goto theend; |
2759 | } |
2760 | |
2761 | char_u *lhs = (char_u *)&args->lhs; |
2762 | char_u *rhs = (char_u *)args->rhs; |
2763 | char_u *orig_rhs = args->orig_rhs; |
2764 | |
2765 | // check arguments and translate function keys |
2766 | if (has_lhs) { |
2767 | len = (int)args->lhs_len; |
2768 | if (len > MAXMAPLEN) { |
2769 | retval = 1; |
2770 | goto theend; |
2771 | } |
2772 | |
2773 | if (is_abbrev && maptype != 1) { |
2774 | // |
2775 | // If an abbreviation ends in a keyword character, the |
2776 | // rest must be all keyword-char or all non-keyword-char. |
2777 | // Otherwise we won't be able to find the start of it in a |
2778 | // vi-compatible way. |
2779 | // |
2780 | if (has_mbyte) { |
2781 | int first, last; |
2782 | int same = -1; |
2783 | |
2784 | first = vim_iswordp(lhs); |
2785 | last = first; |
2786 | p = lhs + (*mb_ptr2len)(lhs); |
2787 | n = 1; |
2788 | while (p < lhs + len) { |
2789 | n++; // nr of (multi-byte) chars |
2790 | last = vim_iswordp(p); // type of last char |
2791 | if (same == -1 && last != first) { |
2792 | same = n - 1; // count of same char type |
2793 | } |
2794 | p += (*mb_ptr2len)(p); |
2795 | } |
2796 | if (last && n > 2 && same >= 0 && same < n - 1) { |
2797 | retval = 1; |
2798 | goto theend; |
2799 | } |
2800 | } else if (vim_iswordc(lhs[len - 1])) { // ends in keyword char |
2801 | for (n = 0; n < len - 2; n++) { |
2802 | if (vim_iswordc(lhs[n]) != vim_iswordc(lhs[len - 2])) { |
2803 | retval = 1; |
2804 | goto theend; |
2805 | } |
2806 | } // for |
2807 | } |
2808 | // An abbreviation cannot contain white space. |
2809 | for (n = 0; n < len; n++) { |
2810 | if (ascii_iswhite(lhs[n])) { |
2811 | retval = 1; |
2812 | goto theend; |
2813 | } |
2814 | } // for |
2815 | } |
2816 | } |
2817 | |
2818 | if (has_lhs && has_rhs && is_abbrev) { // if we will add an abbreviation, |
2819 | no_abbr = false; // reset flag that indicates there are no abbreviations |
2820 | } |
2821 | |
2822 | if (!has_lhs || (maptype != 1 && !has_rhs)) { |
2823 | msg_start(); |
2824 | } |
2825 | |
2826 | // Check if a new local mapping wasn't already defined globally. |
2827 | if (map_table == buf->b_maphash && has_lhs && has_rhs && maptype != 1) { |
2828 | // need to loop over all global hash lists |
2829 | for (hash = 0; hash < 256 && !got_int; hash++) { |
2830 | if (is_abbrev) { |
2831 | if (hash != 0) { // there is only one abbreviation list |
2832 | break; |
2833 | } |
2834 | mp = first_abbr; |
2835 | } else { |
2836 | mp = maphash[hash]; |
2837 | } |
2838 | for (; mp != NULL && !got_int; mp = mp->m_next) { |
2839 | // check entries with the same mode |
2840 | if ((mp->m_mode & mode) != 0 |
2841 | && mp->m_keylen == len |
2842 | && args->unique |
2843 | && STRNCMP(mp->m_keys, lhs, (size_t)len) == 0) { |
2844 | if (is_abbrev) { |
2845 | EMSG2(_("E224: global abbreviation already exists for %s" ), |
2846 | mp->m_keys); |
2847 | } else { |
2848 | EMSG2(_("E225: global mapping already exists for %s" ), mp->m_keys); |
2849 | } |
2850 | retval = 5; |
2851 | goto theend; |
2852 | } |
2853 | } |
2854 | } |
2855 | } |
2856 | |
2857 | // When listing global mappings, also list buffer-local ones here. |
2858 | if (map_table != buf->b_maphash && !has_rhs && maptype != 1) { |
2859 | // need to loop over all global hash lists |
2860 | for (hash = 0; hash < 256 && !got_int; hash++) { |
2861 | if (is_abbrev) { |
2862 | if (hash != 0) { // there is only one abbreviation list |
2863 | break; |
2864 | } |
2865 | mp = buf->b_first_abbr; |
2866 | } else { |
2867 | mp = buf->b_maphash[hash]; |
2868 | } |
2869 | for (; mp != NULL && !got_int; mp = mp->m_next) { |
2870 | // check entries with the same mode |
2871 | if ((mp->m_mode & mode) != 0) { |
2872 | if (!has_lhs) { // show all entries |
2873 | showmap(mp, true); |
2874 | did_local = true; |
2875 | } else { |
2876 | n = mp->m_keylen; |
2877 | if (STRNCMP(mp->m_keys, lhs, (size_t)(n < len ? n : len)) == 0) { |
2878 | showmap(mp, true); |
2879 | did_local = true; |
2880 | } |
2881 | } |
2882 | } |
2883 | } |
2884 | } |
2885 | } |
2886 | |
2887 | // Find an entry in the maphash[] list that matches. |
2888 | // For :unmap we may loop two times: once to try to unmap an entry with a |
2889 | // matching 'from' part, a second time, if the first fails, to unmap an |
2890 | // entry with a matching 'to' part. This was done to allow ":ab foo bar" |
2891 | // to be unmapped by typing ":unab foo", where "foo" will be replaced by |
2892 | // "bar" because of the abbreviation. |
2893 | for (round = 0; (round == 0 || maptype == 1) && round <= 1 |
2894 | && !did_it && !got_int; round++) { |
2895 | // need to loop over all hash lists |
2896 | for (hash = 0; hash < 256 && !got_int; hash++) { |
2897 | if (is_abbrev) { |
2898 | if (hash > 0) { // there is only one abbreviation list |
2899 | break; |
2900 | } |
2901 | mpp = abbr_table; |
2902 | } else { |
2903 | mpp = &(map_table[hash]); |
2904 | } |
2905 | for (mp = *mpp; mp != NULL && !got_int; mp = *mpp) { |
2906 | if (!(mp->m_mode & mode)) { // skip entries with wrong mode |
2907 | mpp = &(mp->m_next); |
2908 | continue; |
2909 | } |
2910 | if (!has_lhs) { // show all entries |
2911 | showmap(mp, map_table != maphash); |
2912 | did_it = true; |
2913 | } else { // do we have a match? |
2914 | if (round) { // second round: Try unmap "rhs" string |
2915 | n = (int)STRLEN(mp->m_str); |
2916 | p = mp->m_str; |
2917 | } else { |
2918 | n = mp->m_keylen; |
2919 | p = mp->m_keys; |
2920 | } |
2921 | if (STRNCMP(p, lhs, (size_t)(n < len ? n : len)) == 0) { |
2922 | if (maptype == 1) { // delete entry |
2923 | // Only accept a full match. For abbreviations we |
2924 | // ignore trailing space when matching with the |
2925 | // "lhs", since an abbreviation can't have |
2926 | // trailing space. |
2927 | if (n != len && (!is_abbrev || round || n > len |
2928 | || *skipwhite(lhs + n) != NUL)) { |
2929 | mpp = &(mp->m_next); |
2930 | continue; |
2931 | } |
2932 | // We reset the indicated mode bits. If nothing is |
2933 | // left the entry is deleted below. |
2934 | mp->m_mode &= ~mode; |
2935 | did_it = true; // remember we did something |
2936 | } else if (!has_rhs) { // show matching entry |
2937 | showmap(mp, map_table != maphash); |
2938 | did_it = true; |
2939 | } else if (n != len) { // new entry is ambiguous |
2940 | mpp = &(mp->m_next); |
2941 | continue; |
2942 | } else if (args->unique) { |
2943 | if (is_abbrev) { |
2944 | EMSG2(_("E226: abbreviation already exists for %s" ), p); |
2945 | } else { |
2946 | EMSG2(_("E227: mapping already exists for %s" ), p); |
2947 | } |
2948 | retval = 5; |
2949 | goto theend; |
2950 | } else { // new rhs for existing entry |
2951 | mp->m_mode &= ~mode; // remove mode bits |
2952 | if (mp->m_mode == 0 && !did_it) { // reuse entry |
2953 | xfree(mp->m_str); |
2954 | mp->m_str = vim_strsave(rhs); |
2955 | xfree(mp->m_orig_str); |
2956 | mp->m_orig_str = vim_strsave(orig_rhs); |
2957 | mp->m_noremap = noremap; |
2958 | mp->m_nowait = args->nowait; |
2959 | mp->m_silent = args->silent; |
2960 | mp->m_mode = mode; |
2961 | mp->m_expr = args->expr; |
2962 | mp->m_script_ctx = current_sctx; |
2963 | mp->m_script_ctx.sc_lnum += sourcing_lnum; |
2964 | did_it = true; |
2965 | } |
2966 | } |
2967 | if (mp->m_mode == 0) { // entry can be deleted |
2968 | mapblock_free(mpp); |
2969 | continue; // continue with *mpp |
2970 | } |
2971 | |
2972 | // May need to put this entry into another hash list. |
2973 | new_hash = MAP_HASH(mp->m_mode, mp->m_keys[0]); |
2974 | if (!is_abbrev && new_hash != hash) { |
2975 | *mpp = mp->m_next; |
2976 | mp->m_next = map_table[new_hash]; |
2977 | map_table[new_hash] = mp; |
2978 | |
2979 | continue; // continue with *mpp |
2980 | } |
2981 | } |
2982 | } |
2983 | mpp = &(mp->m_next); |
2984 | } |
2985 | } |
2986 | } |
2987 | |
2988 | if (maptype == 1) { // delete entry |
2989 | if (!did_it) { |
2990 | retval = 2; // no match |
2991 | } else if (*lhs == Ctrl_C) { |
2992 | // If CTRL-C has been unmapped, reuse it for Interrupting. |
2993 | if (map_table == buf->b_maphash) { |
2994 | buf->b_mapped_ctrl_c &= ~mode; |
2995 | } else { |
2996 | mapped_ctrl_c &= ~mode; |
2997 | } |
2998 | } |
2999 | goto theend; |
3000 | } |
3001 | |
3002 | if (!has_lhs || !has_rhs) { // print entries |
3003 | if (!did_it && !did_local) { |
3004 | if (is_abbrev) { |
3005 | MSG(_("No abbreviation found" )); |
3006 | } else { |
3007 | MSG(_("No mapping found" )); |
3008 | } |
3009 | } |
3010 | goto theend; // listing finished |
3011 | } |
3012 | |
3013 | if (did_it) { // have added the new entry already |
3014 | goto theend; |
3015 | } |
3016 | |
3017 | // Get here when adding a new entry to the maphash[] list or abbrlist. |
3018 | mp = xmalloc(sizeof(mapblock_T)); |
3019 | |
3020 | // If CTRL-C has been mapped, don't always use it for Interrupting. |
3021 | if (*lhs == Ctrl_C) { |
3022 | if (map_table == buf->b_maphash) { |
3023 | buf->b_mapped_ctrl_c |= mode; |
3024 | } else { |
3025 | mapped_ctrl_c |= mode; |
3026 | } |
3027 | } |
3028 | |
3029 | mp->m_keys = vim_strsave(lhs); |
3030 | mp->m_str = vim_strsave(rhs); |
3031 | mp->m_orig_str = vim_strsave(orig_rhs); |
3032 | mp->m_keylen = (int)STRLEN(mp->m_keys); |
3033 | mp->m_noremap = noremap; |
3034 | mp->m_nowait = args->nowait; |
3035 | mp->m_silent = args->silent; |
3036 | mp->m_mode = mode; |
3037 | mp->m_expr = args->expr; |
3038 | mp->m_script_ctx = current_sctx; |
3039 | mp->m_script_ctx.sc_lnum += sourcing_lnum; |
3040 | |
3041 | // add the new entry in front of the abbrlist or maphash[] list |
3042 | if (is_abbrev) { |
3043 | mp->m_next = *abbr_table; |
3044 | *abbr_table = mp; |
3045 | } else { |
3046 | n = MAP_HASH(mp->m_mode, mp->m_keys[0]); |
3047 | mp->m_next = map_table[n]; |
3048 | map_table[n] = mp; |
3049 | } |
3050 | |
3051 | theend: |
3052 | return retval; |
3053 | } |
3054 | |
3055 | |
3056 | /// Set or remove a mapping or an abbreviation in the current buffer, OR |
3057 | /// display (matching) mappings/abbreviations. |
3058 | /// |
3059 | /// ```vim |
3060 | /// map[!] " show all key mappings |
3061 | /// map[!] {lhs} " show key mapping for {lhs} |
3062 | /// map[!] {lhs} {rhs} " set key mapping for {lhs} to {rhs} |
3063 | /// noremap[!] {lhs} {rhs} " same, but no remapping for {rhs} |
3064 | /// unmap[!] {lhs} " remove key mapping for {lhs} |
3065 | /// abbr " show all abbreviations |
3066 | /// abbr {lhs} " show abbreviations for {lhs} |
3067 | /// abbr {lhs} {rhs} " set abbreviation for {lhs} to {rhs} |
3068 | /// noreabbr {lhs} {rhs} " same, but no remapping for {rhs} |
3069 | /// unabbr {lhs} " remove abbreviation for {lhs} |
3070 | /// |
3071 | /// for :map mode is NORMAL + VISUAL + SELECTMODE + OP_PENDING |
3072 | /// for :map! mode is INSERT + CMDLINE |
3073 | /// for :cmap mode is CMDLINE |
3074 | /// for :imap mode is INSERT |
3075 | /// for :lmap mode is LANGMAP |
3076 | /// for :nmap mode is NORMAL |
3077 | /// for :vmap mode is VISUAL + SELECTMODE |
3078 | /// for :xmap mode is VISUAL |
3079 | /// for :smap mode is SELECTMODE |
3080 | /// for :omap mode is OP_PENDING |
3081 | /// for :tmap mode is TERM_FOCUS |
3082 | /// |
3083 | /// for :abbr mode is INSERT + CMDLINE |
3084 | /// for :iabbr mode is INSERT |
3085 | /// for :cabbr mode is CMDLINE |
3086 | /// ``` |
3087 | /// |
3088 | /// @param maptype 0 for |:map|, 1 for |:unmap|, 2 for |noremap|. |
3089 | /// @param arg C-string containing the arguments of the map/abbrev |
3090 | /// command, i.e. everything except the initial `:[X][nore]map`. |
3091 | /// - Cannot be a read-only string; it will be modified. |
3092 | /// @param mode Bitflags representing the mode in which to set the mapping. |
3093 | /// See @ref get_map_mode. |
3094 | /// @param is_abbrev True if setting an abbreviation, false otherwise. |
3095 | /// |
3096 | /// @return 0 on success. On failure, will return one of the following: |
3097 | /// - 1 for invalid arguments |
3098 | /// - 2 for no match |
3099 | /// - 4 for out of mem (deprecated, WON'T HAPPEN) |
3100 | /// - 5 for entry not unique |
3101 | /// |
3102 | int do_map(int maptype, char_u *arg, int mode, bool is_abbrev) |
3103 | { |
3104 | MapArguments parsed_args; |
3105 | int result = str_to_mapargs(arg, maptype == 1, &parsed_args); |
3106 | switch (result) { |
3107 | case 0: |
3108 | break; |
3109 | case 1: |
3110 | result = 1; // invalid arguments |
3111 | goto free_and_return; |
3112 | default: |
3113 | assert(false && "Unknown return code from str_to_mapargs!" ); |
3114 | result = -1; |
3115 | goto free_and_return; |
3116 | } // switch |
3117 | |
3118 | result = buf_do_map(maptype, &parsed_args, mode, is_abbrev, curbuf); |
3119 | |
3120 | free_and_return: |
3121 | xfree(parsed_args.rhs); |
3122 | xfree(parsed_args.orig_rhs); |
3123 | return result; |
3124 | } |
3125 | |
3126 | /* |
3127 | * Delete one entry from the abbrlist or maphash[]. |
3128 | * "mpp" is a pointer to the m_next field of the PREVIOUS entry! |
3129 | */ |
3130 | static void mapblock_free(mapblock_T **mpp) |
3131 | { |
3132 | mapblock_T *mp; |
3133 | |
3134 | mp = *mpp; |
3135 | xfree(mp->m_keys); |
3136 | xfree(mp->m_str); |
3137 | xfree(mp->m_orig_str); |
3138 | *mpp = mp->m_next; |
3139 | xfree(mp); |
3140 | } |
3141 | |
3142 | /* |
3143 | * Initialize maphash[] for first use. |
3144 | */ |
3145 | static void validate_maphash(void) |
3146 | { |
3147 | if (!maphash_valid) { |
3148 | memset(maphash, 0, sizeof(maphash)); |
3149 | maphash_valid = TRUE; |
3150 | } |
3151 | } |
3152 | |
3153 | /* |
3154 | * Get the mapping mode from the command name. |
3155 | */ |
3156 | int get_map_mode(char_u **cmdp, int forceit) |
3157 | { |
3158 | char_u *p; |
3159 | int modec; |
3160 | int mode; |
3161 | |
3162 | p = *cmdp; |
3163 | modec = *p++; |
3164 | if (modec == 'i') |
3165 | mode = INSERT; /* :imap */ |
3166 | else if (modec == 'l') |
3167 | mode = LANGMAP; /* :lmap */ |
3168 | else if (modec == 'c') |
3169 | mode = CMDLINE; /* :cmap */ |
3170 | else if (modec == 'n' && *p != 'o') /* avoid :noremap */ |
3171 | mode = NORMAL; /* :nmap */ |
3172 | else if (modec == 'v') |
3173 | mode = VISUAL + SELECTMODE; /* :vmap */ |
3174 | else if (modec == 'x') |
3175 | mode = VISUAL; /* :xmap */ |
3176 | else if (modec == 's') |
3177 | mode = SELECTMODE; /* :smap */ |
3178 | else if (modec == 'o') |
3179 | mode = OP_PENDING; /* :omap */ |
3180 | else if (modec == 't') |
3181 | mode = TERM_FOCUS; // :tmap |
3182 | else { |
3183 | --p; |
3184 | if (forceit) |
3185 | mode = INSERT + CMDLINE; /* :map ! */ |
3186 | else |
3187 | mode = VISUAL + SELECTMODE + NORMAL + OP_PENDING; /* :map */ |
3188 | } |
3189 | |
3190 | *cmdp = p; |
3191 | return mode; |
3192 | } |
3193 | |
3194 | /* |
3195 | * Clear all mappings or abbreviations. |
3196 | * 'abbr' should be FALSE for mappings, TRUE for abbreviations. |
3197 | */ |
3198 | void map_clear_mode(char_u *cmdp, char_u *arg, int forceit, int abbr) |
3199 | { |
3200 | int mode; |
3201 | int local; |
3202 | |
3203 | local = (STRCMP(arg, "<buffer>" ) == 0); |
3204 | if (!local && *arg != NUL) { |
3205 | EMSG(_(e_invarg)); |
3206 | return; |
3207 | } |
3208 | |
3209 | mode = get_map_mode(&cmdp, forceit); |
3210 | map_clear_int(curbuf, mode, |
3211 | local, |
3212 | abbr); |
3213 | } |
3214 | |
3215 | /* |
3216 | * Clear all mappings in "mode". |
3217 | */ |
3218 | void |
3219 | map_clear_int ( |
3220 | buf_T *buf, /* buffer for local mappings */ |
3221 | int mode, /* mode in which to delete */ |
3222 | int local, /* TRUE for buffer-local mappings */ |
3223 | int abbr /* TRUE for abbreviations */ |
3224 | ) |
3225 | { |
3226 | mapblock_T *mp, **mpp; |
3227 | int hash; |
3228 | int new_hash; |
3229 | |
3230 | validate_maphash(); |
3231 | |
3232 | for (hash = 0; hash < 256; ++hash) { |
3233 | if (abbr) { |
3234 | if (hash > 0) /* there is only one abbrlist */ |
3235 | break; |
3236 | if (local) |
3237 | mpp = &buf->b_first_abbr; |
3238 | else |
3239 | mpp = &first_abbr; |
3240 | } else { |
3241 | if (local) |
3242 | mpp = &buf->b_maphash[hash]; |
3243 | else |
3244 | mpp = &maphash[hash]; |
3245 | } |
3246 | while (*mpp != NULL) { |
3247 | mp = *mpp; |
3248 | if (mp->m_mode & mode) { |
3249 | mp->m_mode &= ~mode; |
3250 | if (mp->m_mode == 0) { // entry can be deleted |
3251 | mapblock_free(mpp); |
3252 | continue; |
3253 | } |
3254 | /* |
3255 | * May need to put this entry into another hash list. |
3256 | */ |
3257 | new_hash = MAP_HASH(mp->m_mode, mp->m_keys[0]); |
3258 | if (!abbr && new_hash != hash) { |
3259 | *mpp = mp->m_next; |
3260 | if (local) { |
3261 | mp->m_next = buf->b_maphash[new_hash]; |
3262 | buf->b_maphash[new_hash] = mp; |
3263 | } else { |
3264 | mp->m_next = maphash[new_hash]; |
3265 | maphash[new_hash] = mp; |
3266 | } |
3267 | continue; /* continue with *mpp */ |
3268 | } |
3269 | } |
3270 | mpp = &(mp->m_next); |
3271 | } |
3272 | } |
3273 | } |
3274 | |
3275 | /// Return characters to represent the map mode in an allocated string |
3276 | /// |
3277 | /// @return [allocated] NUL-terminated string with characters. |
3278 | char *map_mode_to_chars(int mode) |
3279 | FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_RET |
3280 | { |
3281 | garray_T mapmode; |
3282 | |
3283 | ga_init(&mapmode, 1, 7); |
3284 | |
3285 | if ((mode & (INSERT + CMDLINE)) == INSERT + CMDLINE) { |
3286 | ga_append(&mapmode, '!'); // :map! |
3287 | } else if (mode & INSERT) { |
3288 | ga_append(&mapmode, 'i'); // :imap |
3289 | } else if (mode & LANGMAP) { |
3290 | ga_append(&mapmode, 'l'); // :lmap |
3291 | } else if (mode & CMDLINE) { |
3292 | ga_append(&mapmode, 'c'); // :cmap |
3293 | } else if ((mode & (NORMAL + VISUAL + SELECTMODE + OP_PENDING)) |
3294 | == NORMAL + VISUAL + SELECTMODE + OP_PENDING) { |
3295 | ga_append(&mapmode, ' '); // :map |
3296 | } else { |
3297 | if (mode & NORMAL) { |
3298 | ga_append(&mapmode, 'n'); // :nmap |
3299 | } |
3300 | if (mode & OP_PENDING) { |
3301 | ga_append(&mapmode, 'o'); // :omap |
3302 | } |
3303 | if (mode & TERM_FOCUS) { |
3304 | ga_append(&mapmode, 't'); // :tmap |
3305 | } |
3306 | if ((mode & (VISUAL + SELECTMODE)) == VISUAL + SELECTMODE) { |
3307 | ga_append(&mapmode, 'v'); // :vmap |
3308 | } else { |
3309 | if (mode & VISUAL) { |
3310 | ga_append(&mapmode, 'x'); // :xmap |
3311 | } |
3312 | if (mode & SELECTMODE) { |
3313 | ga_append(&mapmode, 's'); // :smap |
3314 | } |
3315 | } |
3316 | } |
3317 | |
3318 | ga_append(&mapmode, NUL); |
3319 | return (char *)mapmode.ga_data; |
3320 | } |
3321 | |
3322 | static void |
3323 | showmap ( |
3324 | mapblock_T *mp, |
3325 | int local /* TRUE for buffer-local map */ |
3326 | ) |
3327 | { |
3328 | size_t len = 1; |
3329 | |
3330 | if (message_filtered(mp->m_keys) && message_filtered(mp->m_str)) { |
3331 | return; |
3332 | } |
3333 | |
3334 | if (msg_didout || msg_silent != 0) { |
3335 | msg_putchar('\n'); |
3336 | if (got_int) /* 'q' typed at MORE prompt */ |
3337 | return; |
3338 | } |
3339 | |
3340 | { |
3341 | char *const mapchars = map_mode_to_chars(mp->m_mode); |
3342 | msg_puts(mapchars); |
3343 | len = strlen(mapchars); |
3344 | xfree(mapchars); |
3345 | } |
3346 | |
3347 | while (++len <= 3) |
3348 | msg_putchar(' '); |
3349 | |
3350 | // Display the LHS. Get length of what we write. |
3351 | len = (size_t)msg_outtrans_special(mp->m_keys, true); |
3352 | do { |
3353 | msg_putchar(' '); /* padd with blanks */ |
3354 | ++len; |
3355 | } while (len < 12); |
3356 | |
3357 | if (mp->m_noremap == REMAP_NONE) { |
3358 | msg_puts_attr("*" , HL_ATTR(HLF_8)); |
3359 | } else if (mp->m_noremap == REMAP_SCRIPT) { |
3360 | msg_puts_attr("&" , HL_ATTR(HLF_8)); |
3361 | } else { |
3362 | msg_putchar(' '); |
3363 | } |
3364 | |
3365 | if (local) |
3366 | msg_putchar('@'); |
3367 | else |
3368 | msg_putchar(' '); |
3369 | |
3370 | /* Use FALSE below if we only want things like <Up> to show up as such on |
3371 | * the rhs, and not M-x etc, TRUE gets both -- webb */ |
3372 | if (*mp->m_str == NUL) { |
3373 | msg_puts_attr("<Nop>" , HL_ATTR(HLF_8)); |
3374 | } else { |
3375 | // Remove escaping of CSI, because "m_str" is in a format to be used |
3376 | // as typeahead. |
3377 | char_u *s = vim_strsave(mp->m_str); |
3378 | vim_unescape_csi(s); |
3379 | msg_outtrans_special(s, FALSE); |
3380 | xfree(s); |
3381 | } |
3382 | if (p_verbose > 0) { |
3383 | last_set_msg(mp->m_script_ctx); |
3384 | } |
3385 | ui_flush(); // show one line at a time |
3386 | } |
3387 | |
3388 | /// Check if a map exists that has given string in the rhs |
3389 | /// |
3390 | /// Also checks mappings local to the current buffer. |
3391 | /// |
3392 | /// @param[in] str String which mapping must have in the rhs. Termcap codes |
3393 | /// are recognized in this argument. |
3394 | /// @param[in] modechars Mode(s) in which mappings are checked. |
3395 | /// @param[in] abbr true if checking abbreviations in place of mappings. |
3396 | /// |
3397 | /// @return true if there is at least one mapping with given parameters. |
3398 | bool map_to_exists(const char *const str, const char *const modechars, |
3399 | const bool abbr) |
3400 | FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE |
3401 | { |
3402 | int mode = 0; |
3403 | int retval; |
3404 | |
3405 | char_u *buf; |
3406 | char_u *const rhs = replace_termcodes((const char_u *)str, strlen(str), &buf, |
3407 | false, true, true, |
3408 | CPO_TO_CPO_FLAGS); |
3409 | |
3410 | #define MAPMODE(mode, modechars, chr, modeflags) \ |
3411 | do { \ |
3412 | if (strchr(modechars, chr) != NULL) { \ |
3413 | mode |= modeflags; \ |
3414 | } \ |
3415 | } while (0) |
3416 | MAPMODE(mode, modechars, 'n', NORMAL); |
3417 | MAPMODE(mode, modechars, 'v', VISUAL|SELECTMODE); |
3418 | MAPMODE(mode, modechars, 'x', VISUAL); |
3419 | MAPMODE(mode, modechars, 's', SELECTMODE); |
3420 | MAPMODE(mode, modechars, 'o', OP_PENDING); |
3421 | MAPMODE(mode, modechars, 'i', INSERT); |
3422 | MAPMODE(mode, modechars, 'l', LANGMAP); |
3423 | MAPMODE(mode, modechars, 'c', CMDLINE); |
3424 | #undef MAPMODE |
3425 | |
3426 | retval = map_to_exists_mode((const char *)rhs, mode, abbr); |
3427 | xfree(buf); |
3428 | |
3429 | return retval; |
3430 | } |
3431 | |
3432 | /// Check if a map exists that has given string in the rhs |
3433 | /// |
3434 | /// Also checks mappings local to the current buffer. |
3435 | /// |
3436 | /// @param[in] rhs String which mapping must have in the rhs. Termcap codes |
3437 | /// are recognized in this argument. |
3438 | /// @param[in] mode Mode(s) in which mappings are checked. |
3439 | /// @param[in] abbr true if checking abbreviations in place of mappings. |
3440 | /// |
3441 | /// @return true if there is at least one mapping with given parameters. |
3442 | int map_to_exists_mode(const char *const rhs, const int mode, const bool abbr) |
3443 | { |
3444 | mapblock_T *mp; |
3445 | int hash; |
3446 | bool exp_buffer = false; |
3447 | |
3448 | validate_maphash(); |
3449 | |
3450 | // Do it twice: once for global maps and once for local maps. |
3451 | for (;;) { |
3452 | for (hash = 0; hash < 256; hash++) { |
3453 | if (abbr) { |
3454 | if (hash > 0) { // There is only one abbr list. |
3455 | break; |
3456 | } |
3457 | if (exp_buffer) { |
3458 | mp = curbuf->b_first_abbr; |
3459 | } else { |
3460 | mp = first_abbr; |
3461 | } |
3462 | } else if (exp_buffer) { |
3463 | mp = curbuf->b_maphash[hash]; |
3464 | } else { |
3465 | mp = maphash[hash]; |
3466 | } |
3467 | for (; mp; mp = mp->m_next) { |
3468 | if ((mp->m_mode & mode) |
3469 | && strstr((char *)mp->m_str, rhs) != NULL) { |
3470 | return true; |
3471 | } |
3472 | } |
3473 | } |
3474 | if (exp_buffer) { |
3475 | break; |
3476 | } |
3477 | exp_buffer = true; |
3478 | } |
3479 | |
3480 | return false; |
3481 | } |
3482 | |
3483 | /* |
3484 | * Used below when expanding mapping/abbreviation names. |
3485 | */ |
3486 | static int expand_mapmodes = 0; |
3487 | static int expand_isabbrev = 0; |
3488 | static int expand_buffer = FALSE; |
3489 | |
3490 | /* |
3491 | * Work out what to complete when doing command line completion of mapping |
3492 | * or abbreviation names. |
3493 | */ |
3494 | char_u * |
3495 | set_context_in_map_cmd ( |
3496 | expand_T *xp, |
3497 | char_u *cmd, |
3498 | char_u *arg, |
3499 | int forceit, /* TRUE if '!' given */ |
3500 | int isabbrev, /* TRUE if abbreviation */ |
3501 | int isunmap, /* TRUE if unmap/unabbrev command */ |
3502 | cmdidx_T cmdidx |
3503 | ) |
3504 | { |
3505 | if (forceit && cmdidx != CMD_map && cmdidx != CMD_unmap) |
3506 | xp->xp_context = EXPAND_NOTHING; |
3507 | else { |
3508 | if (isunmap) |
3509 | expand_mapmodes = get_map_mode(&cmd, forceit || isabbrev); |
3510 | else { |
3511 | expand_mapmodes = INSERT + CMDLINE; |
3512 | if (!isabbrev) |
3513 | expand_mapmodes += VISUAL + SELECTMODE + NORMAL + OP_PENDING; |
3514 | } |
3515 | expand_isabbrev = isabbrev; |
3516 | xp->xp_context = EXPAND_MAPPINGS; |
3517 | expand_buffer = FALSE; |
3518 | for (;; ) { |
3519 | if (STRNCMP(arg, "<buffer>" , 8) == 0) { |
3520 | expand_buffer = TRUE; |
3521 | arg = skipwhite(arg + 8); |
3522 | continue; |
3523 | } |
3524 | if (STRNCMP(arg, "<unique>" , 8) == 0) { |
3525 | arg = skipwhite(arg + 8); |
3526 | continue; |
3527 | } |
3528 | if (STRNCMP(arg, "<nowait>" , 8) == 0) { |
3529 | arg = skipwhite(arg + 8); |
3530 | continue; |
3531 | } |
3532 | if (STRNCMP(arg, "<silent>" , 8) == 0) { |
3533 | arg = skipwhite(arg + 8); |
3534 | continue; |
3535 | } |
3536 | if (STRNCMP(arg, "<special>" , 9) == 0) { |
3537 | arg = skipwhite(arg + 9); |
3538 | continue; |
3539 | } |
3540 | if (STRNCMP(arg, "<script>" , 8) == 0) { |
3541 | arg = skipwhite(arg + 8); |
3542 | continue; |
3543 | } |
3544 | if (STRNCMP(arg, "<expr>" , 6) == 0) { |
3545 | arg = skipwhite(arg + 6); |
3546 | continue; |
3547 | } |
3548 | break; |
3549 | } |
3550 | xp->xp_pattern = arg; |
3551 | } |
3552 | |
3553 | return NULL; |
3554 | } |
3555 | |
3556 | // Find all mapping/abbreviation names that match regexp "regmatch". |
3557 | // For command line expansion of ":[un]map" and ":[un]abbrev" in all modes. |
3558 | // Return OK if matches found, FAIL otherwise. |
3559 | int ExpandMappings(regmatch_T *regmatch, int *num_file, char_u ***file) |
3560 | { |
3561 | mapblock_T *mp; |
3562 | int hash; |
3563 | int count; |
3564 | int round; |
3565 | char_u *p; |
3566 | int i; |
3567 | |
3568 | validate_maphash(); |
3569 | |
3570 | *num_file = 0; /* return values in case of FAIL */ |
3571 | *file = NULL; |
3572 | |
3573 | /* |
3574 | * round == 1: Count the matches. |
3575 | * round == 2: Build the array to keep the matches. |
3576 | */ |
3577 | for (round = 1; round <= 2; ++round) { |
3578 | count = 0; |
3579 | |
3580 | for (i = 0; i < 7; i++) { |
3581 | if (i == 0) { |
3582 | p = (char_u *)"<silent>" ; |
3583 | } else if (i == 1) { |
3584 | p = (char_u *)"<unique>" ; |
3585 | } else if (i == 2) { |
3586 | p = (char_u *)"<script>" ; |
3587 | } else if (i == 3) { |
3588 | p = (char_u *)"<expr>" ; |
3589 | } else if (i == 4 && !expand_buffer) { |
3590 | p = (char_u *)"<buffer>" ; |
3591 | } else if (i == 5) { |
3592 | p = (char_u *)"<nowait>" ; |
3593 | } else if (i == 6) { |
3594 | p = (char_u *)"<special>" ; |
3595 | } else { |
3596 | continue; |
3597 | } |
3598 | |
3599 | if (vim_regexec(regmatch, p, (colnr_T)0)) { |
3600 | if (round == 1) |
3601 | ++count; |
3602 | else |
3603 | (*file)[count++] = vim_strsave(p); |
3604 | } |
3605 | } |
3606 | |
3607 | for (hash = 0; hash < 256; ++hash) { |
3608 | if (expand_isabbrev) { |
3609 | if (hash > 0) /* only one abbrev list */ |
3610 | break; /* for (hash) */ |
3611 | mp = first_abbr; |
3612 | } else if (expand_buffer) |
3613 | mp = curbuf->b_maphash[hash]; |
3614 | else |
3615 | mp = maphash[hash]; |
3616 | for (; mp; mp = mp->m_next) { |
3617 | if (mp->m_mode & expand_mapmodes) { |
3618 | p = translate_mapping(mp->m_keys, CPO_TO_CPO_FLAGS); |
3619 | if (p != NULL && vim_regexec(regmatch, p, (colnr_T)0)) { |
3620 | if (round == 1) |
3621 | ++count; |
3622 | else { |
3623 | (*file)[count++] = p; |
3624 | p = NULL; |
3625 | } |
3626 | } |
3627 | xfree(p); |
3628 | } |
3629 | } /* for (mp) */ |
3630 | } /* for (hash) */ |
3631 | |
3632 | if (count == 0) /* no match found */ |
3633 | break; /* for (round) */ |
3634 | |
3635 | if (round == 1) { |
3636 | *file = (char_u **)xmalloc((size_t)count * sizeof(char_u *)); |
3637 | } |
3638 | } /* for (round) */ |
3639 | |
3640 | if (count > 1) { |
3641 | char_u **ptr1; |
3642 | char_u **ptr2; |
3643 | char_u **ptr3; |
3644 | |
3645 | /* Sort the matches */ |
3646 | sort_strings(*file, count); |
3647 | |
3648 | /* Remove multiple entries */ |
3649 | ptr1 = *file; |
3650 | ptr2 = ptr1 + 1; |
3651 | ptr3 = ptr1 + count; |
3652 | |
3653 | while (ptr2 < ptr3) { |
3654 | if (STRCMP(*ptr1, *ptr2)) |
3655 | *++ptr1 = *ptr2++; |
3656 | else { |
3657 | xfree(*ptr2++); |
3658 | count--; |
3659 | } |
3660 | } |
3661 | } |
3662 | |
3663 | *num_file = count; |
3664 | return count == 0 ? FAIL : OK; |
3665 | } |
3666 | |
3667 | /* |
3668 | * Check for an abbreviation. |
3669 | * Cursor is at ptr[col]. |
3670 | * When inserting, mincol is where insert started. |
3671 | * For the command line, mincol is what is to be skipped over. |
3672 | * "c" is the character typed before check_abbr was called. It may have |
3673 | * ABBR_OFF added to avoid prepending a CTRL-V to it. |
3674 | * |
3675 | * Historic vi practice: The last character of an abbreviation must be an id |
3676 | * character ([a-zA-Z0-9_]). The characters in front of it must be all id |
3677 | * characters or all non-id characters. This allows for abbr. "#i" to |
3678 | * "#include". |
3679 | * |
3680 | * Vim addition: Allow for abbreviations that end in a non-keyword character. |
3681 | * Then there must be white space before the abbr. |
3682 | * |
3683 | * return TRUE if there is an abbreviation, FALSE if not |
3684 | */ |
3685 | int check_abbr(int c, char_u *ptr, int col, int mincol) |
3686 | { |
3687 | int len; |
3688 | int scol; /* starting column of the abbr. */ |
3689 | int j; |
3690 | char_u *s; |
3691 | char_u tb[MB_MAXBYTES + 4]; |
3692 | mapblock_T *mp; |
3693 | mapblock_T *mp2; |
3694 | int clen = 0; /* length in characters */ |
3695 | int is_id = TRUE; |
3696 | int vim_abbr; |
3697 | |
3698 | if (typebuf.tb_no_abbr_cnt) /* abbrev. are not recursive */ |
3699 | return FALSE; |
3700 | |
3701 | /* no remapping implies no abbreviation, except for CTRL-] */ |
3702 | if ((KeyNoremap & (RM_NONE|RM_SCRIPT)) != 0 && c != Ctrl_RSB) |
3703 | return FALSE; |
3704 | |
3705 | /* |
3706 | * Check for word before the cursor: If it ends in a keyword char all |
3707 | * chars before it must be keyword chars or non-keyword chars, but not |
3708 | * white space. If it ends in a non-keyword char we accept any characters |
3709 | * before it except white space. |
3710 | */ |
3711 | if (col == 0) /* cannot be an abbr. */ |
3712 | return FALSE; |
3713 | |
3714 | if (has_mbyte) { |
3715 | char_u *p; |
3716 | |
3717 | p = mb_prevptr(ptr, ptr + col); |
3718 | if (!vim_iswordp(p)) |
3719 | vim_abbr = TRUE; /* Vim added abbr. */ |
3720 | else { |
3721 | vim_abbr = FALSE; /* vi compatible abbr. */ |
3722 | if (p > ptr) |
3723 | is_id = vim_iswordp(mb_prevptr(ptr, p)); |
3724 | } |
3725 | clen = 1; |
3726 | while (p > ptr + mincol) { |
3727 | p = mb_prevptr(ptr, p); |
3728 | if (ascii_isspace(*p) || (!vim_abbr && is_id != vim_iswordp(p))) { |
3729 | p += (*mb_ptr2len)(p); |
3730 | break; |
3731 | } |
3732 | ++clen; |
3733 | } |
3734 | scol = (int)(p - ptr); |
3735 | } else { |
3736 | if (!vim_iswordc(ptr[col - 1])) |
3737 | vim_abbr = TRUE; /* Vim added abbr. */ |
3738 | else { |
3739 | vim_abbr = FALSE; /* vi compatible abbr. */ |
3740 | if (col > 1) |
3741 | is_id = vim_iswordc(ptr[col - 2]); |
3742 | } |
3743 | for (scol = col - 1; scol > 0 && !ascii_isspace(ptr[scol - 1]) |
3744 | && (vim_abbr || is_id == vim_iswordc(ptr[scol - 1])); --scol) |
3745 | ; |
3746 | } |
3747 | |
3748 | if (scol < mincol) |
3749 | scol = mincol; |
3750 | if (scol < col) { /* there is a word in front of the cursor */ |
3751 | ptr += scol; |
3752 | len = col - scol; |
3753 | mp = curbuf->b_first_abbr; |
3754 | mp2 = first_abbr; |
3755 | if (mp == NULL) { |
3756 | mp = mp2; |
3757 | mp2 = NULL; |
3758 | } |
3759 | for (; mp; |
3760 | mp->m_next == NULL ? (mp = mp2, mp2 = NULL) : |
3761 | (mp = mp->m_next)) { |
3762 | int qlen = mp->m_keylen; |
3763 | char_u *q = mp->m_keys; |
3764 | int match; |
3765 | |
3766 | if (strchr((const char *)mp->m_keys, K_SPECIAL) != NULL) { |
3767 | // Might have CSI escaped mp->m_keys. |
3768 | q = vim_strsave(mp->m_keys); |
3769 | vim_unescape_csi(q); |
3770 | qlen = (int)STRLEN(q); |
3771 | } |
3772 | /* find entries with right mode and keys */ |
3773 | match = (mp->m_mode & State) |
3774 | && qlen == len |
3775 | && !STRNCMP(q, ptr, (size_t)len); |
3776 | if (q != mp->m_keys) { |
3777 | xfree(q); |
3778 | } |
3779 | if (match) { |
3780 | break; |
3781 | } |
3782 | } |
3783 | if (mp != NULL) { |
3784 | /* |
3785 | * Found a match: |
3786 | * Insert the rest of the abbreviation in typebuf.tb_buf[]. |
3787 | * This goes from end to start. |
3788 | * |
3789 | * Characters 0x000 - 0x100: normal chars, may need CTRL-V, |
3790 | * except K_SPECIAL: Becomes K_SPECIAL KS_SPECIAL KE_FILLER |
3791 | * Characters where IS_SPECIAL() == TRUE: key codes, need |
3792 | * K_SPECIAL. Other characters (with ABBR_OFF): don't use CTRL-V. |
3793 | * |
3794 | * Character CTRL-] is treated specially - it completes the |
3795 | * abbreviation, but is not inserted into the input stream. |
3796 | */ |
3797 | j = 0; |
3798 | if (c != Ctrl_RSB) { |
3799 | /* special key code, split up */ |
3800 | if (IS_SPECIAL(c) || c == K_SPECIAL) { |
3801 | tb[j++] = K_SPECIAL; |
3802 | tb[j++] = (char_u)K_SECOND(c); |
3803 | tb[j++] = (char_u)K_THIRD(c); |
3804 | } else { |
3805 | if (c < ABBR_OFF && (c < ' ' || c > '~')) { |
3806 | tb[j++] = Ctrl_V; // special char needs CTRL-V |
3807 | } |
3808 | // if ABBR_OFF has been added, remove it here. |
3809 | if (c >= ABBR_OFF) { |
3810 | c -= ABBR_OFF; |
3811 | } |
3812 | j += utf_char2bytes(c, tb + j); |
3813 | } |
3814 | tb[j] = NUL; |
3815 | /* insert the last typed char */ |
3816 | (void)ins_typebuf(tb, 1, 0, TRUE, mp->m_silent); |
3817 | } |
3818 | if (mp->m_expr) |
3819 | s = eval_map_expr(mp->m_str, c); |
3820 | else |
3821 | s = mp->m_str; |
3822 | if (s != NULL) { |
3823 | /* insert the to string */ |
3824 | (void)ins_typebuf(s, mp->m_noremap, 0, TRUE, mp->m_silent); |
3825 | /* no abbrev. for these chars */ |
3826 | typebuf.tb_no_abbr_cnt += (int)STRLEN(s) + j + 1; |
3827 | if (mp->m_expr) |
3828 | xfree(s); |
3829 | } |
3830 | |
3831 | tb[0] = Ctrl_H; |
3832 | tb[1] = NUL; |
3833 | if (has_mbyte) |
3834 | len = clen; /* Delete characters instead of bytes */ |
3835 | while (len-- > 0) /* delete the from string */ |
3836 | (void)ins_typebuf(tb, 1, 0, TRUE, mp->m_silent); |
3837 | return TRUE; |
3838 | } |
3839 | } |
3840 | return FALSE; |
3841 | } |
3842 | |
3843 | /* |
3844 | * Evaluate the RHS of a mapping or abbreviations and take care of escaping |
3845 | * special characters. |
3846 | */ |
3847 | static char_u * |
3848 | eval_map_expr ( |
3849 | char_u *str, |
3850 | int c /* NUL or typed character for abbreviation */ |
3851 | ) |
3852 | { |
3853 | char_u *res; |
3854 | char_u *p; |
3855 | char_u *expr; |
3856 | char_u *save_cmd; |
3857 | pos_T save_cursor; |
3858 | int save_msg_col; |
3859 | int save_msg_row; |
3860 | |
3861 | /* Remove escaping of CSI, because "str" is in a format to be used as |
3862 | * typeahead. */ |
3863 | expr = vim_strsave(str); |
3864 | vim_unescape_csi(expr); |
3865 | |
3866 | save_cmd = save_cmdline_alloc(); |
3867 | |
3868 | /* Forbid changing text or using ":normal" to avoid most of the bad side |
3869 | * effects. Also restore the cursor position. */ |
3870 | ++textlock; |
3871 | ++ex_normal_lock; |
3872 | set_vim_var_char(c); /* set v:char to the typed character */ |
3873 | save_cursor = curwin->w_cursor; |
3874 | save_msg_col = msg_col; |
3875 | save_msg_row = msg_row; |
3876 | p = eval_to_string(expr, NULL, FALSE); |
3877 | --textlock; |
3878 | --ex_normal_lock; |
3879 | curwin->w_cursor = save_cursor; |
3880 | msg_col = save_msg_col; |
3881 | msg_row = save_msg_row; |
3882 | |
3883 | restore_cmdline_alloc(save_cmd); |
3884 | xfree(expr); |
3885 | |
3886 | if (p == NULL) |
3887 | return NULL; |
3888 | /* Escape CSI in the result to be able to use the string as typeahead. */ |
3889 | res = vim_strsave_escape_csi(p); |
3890 | xfree(p); |
3891 | |
3892 | return res; |
3893 | } |
3894 | |
3895 | /* |
3896 | * Copy "p" to allocated memory, escaping K_SPECIAL and CSI so that the result |
3897 | * can be put in the typeahead buffer. |
3898 | */ |
3899 | char_u *vim_strsave_escape_csi(char_u *p) |
3900 | { |
3901 | // Need a buffer to hold up to three times as much. Four in case of an |
3902 | // illegal utf-8 byte: |
3903 | // 0xc0 -> 0xc3 - 0x80 -> 0xc3 K_SPECIAL KS_SPECIAL KE_FILLER |
3904 | char_u *res = xmalloc(STRLEN(p) * 4 + 1); |
3905 | char_u *d = res; |
3906 | for (char_u *s = p; *s != NUL; ) { |
3907 | if (s[0] == K_SPECIAL && s[1] != NUL && s[2] != NUL) { |
3908 | /* Copy special key unmodified. */ |
3909 | *d++ = *s++; |
3910 | *d++ = *s++; |
3911 | *d++ = *s++; |
3912 | } else { |
3913 | // Add character, possibly multi-byte to destination, escaping |
3914 | // CSI and K_SPECIAL. Be careful, it can be an illegal byte! |
3915 | d = add_char2buf(PTR2CHAR(s), d); |
3916 | s += MB_CPTR2LEN(s); |
3917 | } |
3918 | } |
3919 | *d = NUL; |
3920 | |
3921 | return res; |
3922 | } |
3923 | |
3924 | /* |
3925 | * Remove escaping from CSI and K_SPECIAL characters. Reverse of |
3926 | * vim_strsave_escape_csi(). Works in-place. |
3927 | */ |
3928 | void vim_unescape_csi(char_u *p) |
3929 | { |
3930 | char_u *s = p, *d = p; |
3931 | |
3932 | while (*s != NUL) { |
3933 | if (s[0] == K_SPECIAL && s[1] == KS_SPECIAL && s[2] == KE_FILLER) { |
3934 | *d++ = K_SPECIAL; |
3935 | s += 3; |
3936 | } else if ((s[0] == K_SPECIAL || s[0] == CSI) |
3937 | && s[1] == KS_EXTRA && s[2] == (int)KE_CSI) { |
3938 | *d++ = CSI; |
3939 | s += 3; |
3940 | } else |
3941 | *d++ = *s++; |
3942 | } |
3943 | *d = NUL; |
3944 | } |
3945 | |
3946 | /* |
3947 | * Write map commands for the current mappings to an .exrc file. |
3948 | * Return FAIL on error, OK otherwise. |
3949 | */ |
3950 | int |
3951 | makemap( |
3952 | FILE *fd, |
3953 | buf_T *buf // buffer for local mappings or NULL |
3954 | ) |
3955 | { |
3956 | mapblock_T *mp; |
3957 | char_u c1, c2, c3; |
3958 | char_u *p; |
3959 | char *cmd; |
3960 | int abbr; |
3961 | int hash; |
3962 | bool did_cpo = false; |
3963 | |
3964 | validate_maphash(); |
3965 | |
3966 | // Do the loop twice: Once for mappings, once for abbreviations. |
3967 | // Then loop over all map hash lists. |
3968 | for (abbr = 0; abbr < 2; abbr++) { |
3969 | for (hash = 0; hash < 256; hash++) { |
3970 | if (abbr) { |
3971 | if (hash > 0) { // there is only one abbr list |
3972 | break; |
3973 | } |
3974 | if (buf != NULL) { |
3975 | mp = buf->b_first_abbr; |
3976 | } else { |
3977 | mp = first_abbr; |
3978 | } |
3979 | } else { |
3980 | if (buf != NULL) { |
3981 | mp = buf->b_maphash[hash]; |
3982 | } else { |
3983 | mp = maphash[hash]; |
3984 | } |
3985 | } |
3986 | |
3987 | for (; mp; mp = mp->m_next) { |
3988 | // skip script-local mappings |
3989 | if (mp->m_noremap == REMAP_SCRIPT) { |
3990 | continue; |
3991 | } |
3992 | |
3993 | // skip mappings that contain a <SNR> (script-local thing), |
3994 | // they probably don't work when loaded again |
3995 | for (p = mp->m_str; *p != NUL; p++) { |
3996 | if (p[0] == K_SPECIAL && p[1] == KS_EXTRA |
3997 | && p[2] == (int)KE_SNR) { |
3998 | break; |
3999 | } |
4000 | } |
4001 | if (*p != NUL) { |
4002 | continue; |
4003 | } |
4004 | |
4005 | // It's possible to create a mapping and then ":unmap" certain |
4006 | // modes. We recreate this here by mapping the individual |
4007 | // modes, which requires up to three of them. |
4008 | c1 = NUL; |
4009 | c2 = NUL; |
4010 | c3 = NUL; |
4011 | if (abbr) { |
4012 | cmd = "abbr" ; |
4013 | } else { |
4014 | cmd = "map" ; |
4015 | } |
4016 | switch (mp->m_mode) { |
4017 | case NORMAL + VISUAL + SELECTMODE + OP_PENDING: |
4018 | break; |
4019 | case NORMAL: |
4020 | c1 = 'n'; |
4021 | break; |
4022 | case VISUAL: |
4023 | c1 = 'x'; |
4024 | break; |
4025 | case SELECTMODE: |
4026 | c1 = 's'; |
4027 | break; |
4028 | case OP_PENDING: |
4029 | c1 = 'o'; |
4030 | break; |
4031 | case NORMAL + VISUAL: |
4032 | c1 = 'n'; |
4033 | c2 = 'x'; |
4034 | break; |
4035 | case NORMAL + SELECTMODE: |
4036 | c1 = 'n'; |
4037 | c2 = 's'; |
4038 | break; |
4039 | case NORMAL + OP_PENDING: |
4040 | c1 = 'n'; |
4041 | c2 = 'o'; |
4042 | break; |
4043 | case VISUAL + SELECTMODE: |
4044 | c1 = 'v'; |
4045 | break; |
4046 | case VISUAL + OP_PENDING: |
4047 | c1 = 'x'; |
4048 | c2 = 'o'; |
4049 | break; |
4050 | case SELECTMODE + OP_PENDING: |
4051 | c1 = 's'; |
4052 | c2 = 'o'; |
4053 | break; |
4054 | case NORMAL + VISUAL + SELECTMODE: |
4055 | c1 = 'n'; |
4056 | c2 = 'v'; |
4057 | break; |
4058 | case NORMAL + VISUAL + OP_PENDING: |
4059 | c1 = 'n'; |
4060 | c2 = 'x'; |
4061 | c3 = 'o'; |
4062 | break; |
4063 | case NORMAL + SELECTMODE + OP_PENDING: |
4064 | c1 = 'n'; |
4065 | c2 = 's'; |
4066 | c3 = 'o'; |
4067 | break; |
4068 | case VISUAL + SELECTMODE + OP_PENDING: |
4069 | c1 = 'v'; |
4070 | c2 = 'o'; |
4071 | break; |
4072 | case CMDLINE + INSERT: |
4073 | if (!abbr) { |
4074 | cmd = "map!" ; |
4075 | } |
4076 | break; |
4077 | case CMDLINE: |
4078 | c1 = 'c'; |
4079 | break; |
4080 | case INSERT: |
4081 | c1 = 'i'; |
4082 | break; |
4083 | case LANGMAP: |
4084 | c1 = 'l'; |
4085 | break; |
4086 | case TERM_FOCUS: |
4087 | c1 = 't'; |
4088 | break; |
4089 | default: |
4090 | IEMSG(_("E228: makemap: Illegal mode" )); |
4091 | return FAIL; |
4092 | } |
4093 | do { |
4094 | // do this twice if c2 is set, 3 times with c3 */ |
4095 | // When outputting <> form, need to make sure that 'cpo' |
4096 | // is set to the Vim default. |
4097 | if (!did_cpo) { |
4098 | if (*mp->m_str == NUL) { // Will use <Nop>. |
4099 | did_cpo = true; |
4100 | } else { |
4101 | const char specials[] = { (char)(uint8_t)K_SPECIAL, NL, NUL }; |
4102 | if (strpbrk((const char *)mp->m_str, specials) != NULL |
4103 | || strpbrk((const char *)mp->m_keys, specials) != NULL) { |
4104 | did_cpo = true; |
4105 | } |
4106 | } |
4107 | if (did_cpo) { |
4108 | if (fprintf(fd, "let s:cpo_save=&cpo" ) < 0 |
4109 | || put_eol(fd) < 0 |
4110 | || fprintf(fd, "set cpo&vim" ) < 0 |
4111 | || put_eol(fd) < 0) { |
4112 | return FAIL; |
4113 | } |
4114 | } |
4115 | } |
4116 | if (c1 && putc(c1, fd) < 0) { |
4117 | return FAIL; |
4118 | } |
4119 | if (mp->m_noremap != REMAP_YES && fprintf(fd, "nore" ) < 0) { |
4120 | return FAIL; |
4121 | } |
4122 | if (fputs(cmd, fd) < 0) { |
4123 | return FAIL; |
4124 | } |
4125 | if (buf != NULL && fputs(" <buffer>" , fd) < 0) { |
4126 | return FAIL; |
4127 | } |
4128 | if (mp->m_nowait && fputs(" <nowait>" , fd) < 0) { |
4129 | return FAIL; |
4130 | } |
4131 | if (mp->m_silent && fputs(" <silent>" , fd) < 0) { |
4132 | return FAIL; |
4133 | } |
4134 | if (mp->m_expr && fputs(" <expr>" , fd) < 0) { |
4135 | return FAIL; |
4136 | } |
4137 | |
4138 | if (putc(' ', fd) < 0 |
4139 | || put_escstr(fd, mp->m_keys, 0) == FAIL |
4140 | || putc(' ', fd) < 0 |
4141 | || put_escstr(fd, mp->m_str, 1) == FAIL |
4142 | || put_eol(fd) < 0) { |
4143 | return FAIL; |
4144 | } |
4145 | c1 = c2; |
4146 | c2 = c3; |
4147 | c3 = NUL; |
4148 | } while (c1 != NUL); |
4149 | } |
4150 | } |
4151 | } |
4152 | if (did_cpo) { |
4153 | if (fprintf(fd, "let &cpo=s:cpo_save" ) < 0 |
4154 | || put_eol(fd) < 0 |
4155 | || fprintf(fd, "unlet s:cpo_save" ) < 0 |
4156 | || put_eol(fd) < 0) { |
4157 | return FAIL; |
4158 | } |
4159 | } |
4160 | return OK; |
4161 | } |
4162 | |
4163 | // write escape string to file |
4164 | // "what": 0 for :map lhs, 1 for :map rhs, 2 for :set |
4165 | // |
4166 | // return FAIL for failure, OK otherwise |
4167 | int put_escstr(FILE *fd, char_u *strstart, int what) |
4168 | { |
4169 | char_u *str = strstart; |
4170 | int c; |
4171 | int modifiers; |
4172 | |
4173 | // :map xx <Nop> |
4174 | if (*str == NUL && what == 1) { |
4175 | if (fprintf(fd, "<Nop>" ) < 0) |
4176 | return FAIL; |
4177 | return OK; |
4178 | } |
4179 | |
4180 | for (; *str != NUL; str++) { |
4181 | // Check for a multi-byte character, which may contain escaped |
4182 | // K_SPECIAL and CSI bytes. |
4183 | const char *p = mb_unescape((const char **)&str); |
4184 | if (p != NULL) { |
4185 | while (*p != NUL) |
4186 | if (fputc(*p++, fd) < 0) |
4187 | return FAIL; |
4188 | --str; |
4189 | continue; |
4190 | } |
4191 | |
4192 | c = *str; |
4193 | /* |
4194 | * Special key codes have to be translated to be able to make sense |
4195 | * when they are read back. |
4196 | */ |
4197 | if (c == K_SPECIAL && what != 2) { |
4198 | modifiers = 0x0; |
4199 | if (str[1] == KS_MODIFIER) { |
4200 | modifiers = str[2]; |
4201 | str += 3; |
4202 | c = *str; |
4203 | } |
4204 | if (c == K_SPECIAL) { |
4205 | c = TO_SPECIAL(str[1], str[2]); |
4206 | str += 2; |
4207 | } |
4208 | if (IS_SPECIAL(c) || modifiers) { /* special key */ |
4209 | if (fputs((char *)get_special_key_name(c, modifiers), fd) < 0) |
4210 | return FAIL; |
4211 | continue; |
4212 | } |
4213 | } |
4214 | |
4215 | /* |
4216 | * A '\n' in a map command should be written as <NL>. |
4217 | * A '\n' in a set command should be written as \^V^J. |
4218 | */ |
4219 | if (c == NL) { |
4220 | if (what == 2) { |
4221 | if (fprintf(fd, "\\\026\n" ) < 0) |
4222 | return FAIL; |
4223 | } else { |
4224 | if (fprintf(fd, "<NL>" ) < 0) |
4225 | return FAIL; |
4226 | } |
4227 | continue; |
4228 | } |
4229 | |
4230 | /* |
4231 | * Some characters have to be escaped with CTRL-V to |
4232 | * prevent them from misinterpreted in DoOneCmd(). |
4233 | * A space, Tab and '"' has to be escaped with a backslash to |
4234 | * prevent it to be misinterpreted in do_set(). |
4235 | * A space has to be escaped with a CTRL-V when it's at the start of a |
4236 | * ":map" rhs. |
4237 | * A '<' has to be escaped with a CTRL-V to prevent it being |
4238 | * interpreted as the start of a special key name. |
4239 | * A space in the lhs of a :map needs a CTRL-V. |
4240 | */ |
4241 | if (what == 2 && (ascii_iswhite(c) || c == '"' || c == '\\')) { |
4242 | if (putc('\\', fd) < 0) |
4243 | return FAIL; |
4244 | } else if (c < ' ' || c > '~' || c == '|' |
4245 | || (what == 0 && c == ' ') |
4246 | || (what == 1 && str == strstart && c == ' ') |
4247 | || (what != 2 && c == '<')) { |
4248 | if (putc(Ctrl_V, fd) < 0) |
4249 | return FAIL; |
4250 | } |
4251 | if (putc(c, fd) < 0) |
4252 | return FAIL; |
4253 | } |
4254 | return OK; |
4255 | } |
4256 | |
4257 | /* |
4258 | * Check the string "keys" against the lhs of all mappings. |
4259 | * Return pointer to rhs of mapping (mapblock->m_str). |
4260 | * NULL when no mapping found. |
4261 | */ |
4262 | char_u * |
4263 | check_map ( |
4264 | char_u *keys, |
4265 | int mode, |
4266 | int exact, /* require exact match */ |
4267 | int ign_mod, /* ignore preceding modifier */ |
4268 | int abbr, /* do abbreviations */ |
4269 | mapblock_T **mp_ptr, /* return: pointer to mapblock or NULL */ |
4270 | int *local_ptr /* return: buffer-local mapping or NULL */ |
4271 | ) |
4272 | { |
4273 | int hash; |
4274 | int len, minlen; |
4275 | mapblock_T *mp; |
4276 | int local; |
4277 | |
4278 | validate_maphash(); |
4279 | |
4280 | len = (int)STRLEN(keys); |
4281 | for (local = 1; local >= 0; --local) |
4282 | /* loop over all hash lists */ |
4283 | for (hash = 0; hash < 256; ++hash) { |
4284 | if (abbr) { |
4285 | if (hash > 0) /* there is only one list. */ |
4286 | break; |
4287 | if (local) |
4288 | mp = curbuf->b_first_abbr; |
4289 | else |
4290 | mp = first_abbr; |
4291 | } else if (local) |
4292 | mp = curbuf->b_maphash[hash]; |
4293 | else |
4294 | mp = maphash[hash]; |
4295 | for (; mp != NULL; mp = mp->m_next) { |
4296 | /* skip entries with wrong mode, wrong length and not matching |
4297 | * ones */ |
4298 | if ((mp->m_mode & mode) && (!exact || mp->m_keylen == len)) { |
4299 | char_u *s = mp->m_keys; |
4300 | int keylen = mp->m_keylen; |
4301 | if (ign_mod && keylen >= 3 |
4302 | && s[0] == K_SPECIAL && s[1] == KS_MODIFIER) { |
4303 | s += 3; |
4304 | keylen -= 3; |
4305 | } |
4306 | minlen = keylen < len ? keylen : len; |
4307 | if (STRNCMP(s, keys, minlen) == 0) { |
4308 | if (mp_ptr != NULL) |
4309 | *mp_ptr = mp; |
4310 | if (local_ptr != NULL) |
4311 | *local_ptr = local; |
4312 | return mp->m_str; |
4313 | } |
4314 | } |
4315 | } |
4316 | } |
4317 | |
4318 | return NULL; |
4319 | } |
4320 | |
4321 | |
4322 | /* |
4323 | * Add a mapping "map" for mode "mode". |
4324 | * Need to put string in allocated memory, because do_map() will modify it. |
4325 | */ |
4326 | void add_map(char_u *map, int mode) |
4327 | { |
4328 | char_u *s; |
4329 | char_u *cpo_save = p_cpo; |
4330 | |
4331 | p_cpo = (char_u *)"" ; /* Allow <> notation */ |
4332 | s = vim_strsave(map); |
4333 | (void)do_map(0, s, mode, FALSE); |
4334 | xfree(s); |
4335 | p_cpo = cpo_save; |
4336 | } |
4337 | |
4338 | // Translate an internal mapping/abbreviation representation into the |
4339 | // corresponding external one recognized by :map/:abbrev commands. |
4340 | // |
4341 | // This function is called when expanding mappings/abbreviations on the |
4342 | // command-line. |
4343 | // |
4344 | // It uses a growarray to build the translation string since the latter can be |
4345 | // wider than the original description. The caller has to free the string |
4346 | // afterwards. |
4347 | // |
4348 | // Returns NULL when there is a problem. |
4349 | static char_u * translate_mapping ( |
4350 | char_u *str, |
4351 | int cpo_flags // Value of various flags present in &cpo |
4352 | ) |
4353 | { |
4354 | garray_T ga; |
4355 | ga_init(&ga, 1, 40); |
4356 | |
4357 | bool cpo_bslash = !(cpo_flags&FLAG_CPO_BSLASH); |
4358 | |
4359 | for (; *str; ++str) { |
4360 | int c = *str; |
4361 | if (c == K_SPECIAL && str[1] != NUL && str[2] != NUL) { |
4362 | int modifiers = 0; |
4363 | if (str[1] == KS_MODIFIER) { |
4364 | str++; |
4365 | modifiers = *++str; |
4366 | c = *++str; |
4367 | } |
4368 | |
4369 | if (c == K_SPECIAL && str[1] != NUL && str[2] != NUL) { |
4370 | c = TO_SPECIAL(str[1], str[2]); |
4371 | if (c == K_ZERO) { |
4372 | // display <Nul> as ^@ |
4373 | c = NUL; |
4374 | } |
4375 | str += 2; |
4376 | } |
4377 | if (IS_SPECIAL(c) || modifiers) { // special key |
4378 | ga_concat(&ga, get_special_key_name(c, modifiers)); |
4379 | continue; /* for (str) */ |
4380 | } |
4381 | } |
4382 | |
4383 | if (c == ' ' || c == '\t' || c == Ctrl_J || c == Ctrl_V |
4384 | || (c == '\\' && !cpo_bslash)) { |
4385 | ga_append(&ga, cpo_bslash ? Ctrl_V : '\\'); |
4386 | } |
4387 | |
4388 | if (c) { |
4389 | ga_append(&ga, (char)c); |
4390 | } |
4391 | } |
4392 | ga_append(&ga, NUL); |
4393 | return (char_u *)(ga.ga_data); |
4394 | } |
4395 | |
4396 | static bool typebuf_match_len(const uint8_t *str, int *mlen) |
4397 | { |
4398 | int i; |
4399 | for (i = 0; i < typebuf.tb_len && str[i]; i++) { |
4400 | if (str[i] != typebuf.tb_buf[typebuf.tb_off + i]) |
4401 | break; |
4402 | } |
4403 | *mlen = i; |
4404 | return str[i] == NUL; // matched the whole string |
4405 | } |
4406 | |
4407 | /// Retrieve the mapblock at the index either globally or for a certain buffer |
4408 | /// |
4409 | /// @param index The index in the maphash[] |
4410 | /// @param buf The buffer to get the maphash from. NULL for global |
4411 | mapblock_T *get_maphash(int index, buf_T *buf) |
4412 | FUNC_ATTR_PURE |
4413 | { |
4414 | if (index >= MAX_MAPHASH) { |
4415 | return NULL; |
4416 | } |
4417 | |
4418 | return (buf == NULL) ? maphash[index] : buf->b_maphash[index]; |
4419 | } |
4420 | |
4421 | /// Get command argument for <Cmd> key |
4422 | char_u * getcmdkeycmd(int promptc, void *cookie, int indent) |
4423 | { |
4424 | garray_T line_ga; |
4425 | int c1 = -1, c2; |
4426 | int cmod = 0; |
4427 | bool aborted = false; |
4428 | |
4429 | ga_init(&line_ga, 1, 32); |
4430 | |
4431 | no_mapping++; |
4432 | |
4433 | got_int = false; |
4434 | while (c1 != NUL && !aborted) { |
4435 | ga_grow(&line_ga, 32); |
4436 | |
4437 | if (vgetorpeek(false) == NUL) { |
4438 | // incomplete <Cmd> is an error, because there is not much the user |
4439 | // could do in this state. |
4440 | EMSG(e_cmdmap_err); |
4441 | aborted = true; |
4442 | break; |
4443 | } |
4444 | |
4445 | // Get one character at a time. |
4446 | c1 = vgetorpeek(true); |
4447 | // Get two extra bytes for special keys |
4448 | if (c1 == K_SPECIAL) { |
4449 | c1 = vgetorpeek(true); // no mapping for these chars |
4450 | c2 = vgetorpeek(true); |
4451 | if (c1 == KS_MODIFIER) { |
4452 | cmod = c2; |
4453 | continue; |
4454 | } |
4455 | c1 = TO_SPECIAL(c1, c2); |
4456 | } |
4457 | |
4458 | |
4459 | if (got_int) { |
4460 | aborted = true; |
4461 | } else if (c1 == '\r' || c1 == '\n') { |
4462 | c1 = NUL; // end the line |
4463 | } else if (c1 == ESC) { |
4464 | aborted = true; |
4465 | } else if (c1 == K_COMMAND) { |
4466 | // special case to give nicer error message |
4467 | EMSG(e_cmdmap_repeated); |
4468 | aborted = true; |
4469 | } else if (IS_SPECIAL(c1)) { |
4470 | if (c1 == K_SNR) { |
4471 | ga_append(&line_ga, (char)K_SPECIAL); |
4472 | ga_append(&line_ga, (char)KS_EXTRA); |
4473 | ga_append(&line_ga, (char)KE_SNR); |
4474 | } else { |
4475 | EMSG2(e_cmdmap_key, get_special_key_name(c1, cmod)); |
4476 | aborted = true; |
4477 | } |
4478 | } else { |
4479 | ga_append(&line_ga, (char)c1); |
4480 | } |
4481 | |
4482 | cmod = 0; |
4483 | } |
4484 | |
4485 | no_mapping--; |
4486 | |
4487 | if (aborted) { |
4488 | ga_clear(&line_ga); |
4489 | } |
4490 | |
4491 | return (char_u *)line_ga.ga_data; |
4492 | } |
4493 | |