blob: 491f8c03a0c10050c30d61a2e669796ffed931ea [file] [log] [blame]
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001/* vi:set ts=8 sts=4 sw=4 noet:
2 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
10/*
11 * match.c: functions for highlighting matches
12 */
13
14#include "vim.h"
15
16#if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
17
18# define SEARCH_HL_PRIORITY 0
19
20/*
21 * Add match to the match list of window 'wp'. The pattern 'pat' will be
22 * highlighted with the group 'grp' with priority 'prio'.
23 * Optionally, a desired ID 'id' can be specified (greater than or equal to 1).
24 * If no particular ID is desired, -1 must be specified for 'id'.
25 * Return ID of added match, -1 on failure.
26 */
27 static int
28match_add(
29 win_T *wp,
30 char_u *grp,
31 char_u *pat,
32 int prio,
33 int id,
34 list_T *pos_list,
35 char_u *conceal_char UNUSED) // pointer to conceal replacement char
36{
37 matchitem_T *cur;
38 matchitem_T *prev;
39 matchitem_T *m;
40 int hlg_id;
41 regprog_T *regprog = NULL;
42 int rtype = SOME_VALID;
43
44 if (*grp == NUL || (pat != NULL && *pat == NUL))
45 return -1;
46 if (id < -1 || id == 0)
47 {
Bram Moolenaar677658a2022-01-05 16:09:06 +000048 semsg(_(e_invalid_id_nr_must_be_greater_than_or_equal_to_one), id);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +020049 return -1;
50 }
51 if (id != -1)
52 {
53 cur = wp->w_match_head;
54 while (cur != NULL)
55 {
56 if (cur->id == id)
57 {
Bram Moolenaar9d00e4a2022-01-05 17:49:15 +000058 semsg(_(e_id_already_taken_nr), id);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +020059 return -1;
60 }
61 cur = cur->next;
62 }
63 }
64 if ((hlg_id = syn_namen2id(grp, (int)STRLEN(grp))) == 0)
65 {
Bram Moolenaare29a27f2021-07-20 21:07:36 +020066 semsg(_(e_no_such_highlight_group_name_str), grp);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +020067 return -1;
68 }
69 if (pat != NULL && (regprog = vim_regcomp(pat, RE_MAGIC)) == NULL)
70 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +000071 semsg(_(e_invalid_argument_str), pat);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +020072 return -1;
73 }
74
75 // Find available match ID.
76 while (id == -1)
77 {
78 cur = wp->w_match_head;
79 while (cur != NULL && cur->id != wp->w_next_match_id)
80 cur = cur->next;
81 if (cur == NULL)
82 id = wp->w_next_match_id;
83 wp->w_next_match_id++;
84 }
85
86 // Build new match.
87 m = ALLOC_CLEAR_ONE(matchitem_T);
88 m->id = id;
89 m->priority = prio;
90 m->pattern = pat == NULL ? NULL : vim_strsave(pat);
91 m->hlg_id = hlg_id;
92 m->match.regprog = regprog;
93 m->match.rmm_ic = FALSE;
94 m->match.rmm_maxcol = 0;
95# if defined(FEAT_CONCEAL)
96 m->conceal_char = 0;
97 if (conceal_char != NULL)
98 m->conceal_char = (*mb_ptr2char)(conceal_char);
99# endif
100
101 // Set up position matches
102 if (pos_list != NULL)
103 {
104 linenr_T toplnum = 0;
105 linenr_T botlnum = 0;
106 listitem_T *li;
107 int i;
108
109 CHECK_LIST_MATERIALIZE(pos_list);
110 for (i = 0, li = pos_list->lv_first; li != NULL && i < MAXPOSMATCH;
111 i++, li = li->li_next)
112 {
113 linenr_T lnum = 0;
114 colnr_T col = 0;
115 int len = 1;
116 list_T *subl;
117 listitem_T *subli;
118 int error = FALSE;
119
120 if (li->li_tv.v_type == VAR_LIST)
121 {
122 subl = li->li_tv.vval.v_list;
123 if (subl == NULL)
124 goto fail;
125 subli = subl->lv_first;
126 if (subli == NULL)
127 goto fail;
128 lnum = tv_get_number_chk(&subli->li_tv, &error);
129 if (error == TRUE)
130 goto fail;
131 if (lnum == 0)
132 {
133 --i;
134 continue;
135 }
136 m->pos.pos[i].lnum = lnum;
137 subli = subli->li_next;
138 if (subli != NULL)
139 {
140 col = tv_get_number_chk(&subli->li_tv, &error);
141 if (error == TRUE)
142 goto fail;
143 subli = subli->li_next;
144 if (subli != NULL)
145 {
146 len = tv_get_number_chk(&subli->li_tv, &error);
147 if (error == TRUE)
148 goto fail;
149 }
150 }
151 m->pos.pos[i].col = col;
152 m->pos.pos[i].len = len;
153 }
154 else if (li->li_tv.v_type == VAR_NUMBER)
155 {
156 if (li->li_tv.vval.v_number == 0)
157 {
158 --i;
159 continue;
160 }
161 m->pos.pos[i].lnum = li->li_tv.vval.v_number;
162 m->pos.pos[i].col = 0;
163 m->pos.pos[i].len = 0;
164 }
165 else
166 {
Bram Moolenaar9a846fb2022-01-01 21:59:18 +0000167 emsg(_(e_list_or_number_required));
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200168 goto fail;
169 }
170 if (toplnum == 0 || lnum < toplnum)
171 toplnum = lnum;
172 if (botlnum == 0 || lnum >= botlnum)
173 botlnum = lnum + 1;
174 }
175
176 // Calculate top and bottom lines for redrawing area
177 if (toplnum != 0)
178 {
179 if (wp->w_buffer->b_mod_set)
180 {
181 if (wp->w_buffer->b_mod_top > toplnum)
182 wp->w_buffer->b_mod_top = toplnum;
183 if (wp->w_buffer->b_mod_bot < botlnum)
184 wp->w_buffer->b_mod_bot = botlnum;
185 }
186 else
187 {
188 wp->w_buffer->b_mod_set = TRUE;
189 wp->w_buffer->b_mod_top = toplnum;
190 wp->w_buffer->b_mod_bot = botlnum;
191 wp->w_buffer->b_mod_xlines = 0;
192 }
193 m->pos.toplnum = toplnum;
194 m->pos.botlnum = botlnum;
195 rtype = VALID;
196 }
197 }
198
199 // Insert new match. The match list is in ascending order with regard to
200 // the match priorities.
201 cur = wp->w_match_head;
202 prev = cur;
203 while (cur != NULL && prio >= cur->priority)
204 {
205 prev = cur;
206 cur = cur->next;
207 }
208 if (cur == prev)
209 wp->w_match_head = m;
210 else
211 prev->next = m;
212 m->next = cur;
213
214 redraw_win_later(wp, rtype);
215 return id;
216
217fail:
218 vim_free(m);
219 return -1;
220}
221
222/*
223 * Delete match with ID 'id' in the match list of window 'wp'.
224 * Print error messages if 'perr' is TRUE.
225 */
226 static int
227match_delete(win_T *wp, int id, int perr)
228{
229 matchitem_T *cur = wp->w_match_head;
230 matchitem_T *prev = cur;
231 int rtype = SOME_VALID;
232
233 if (id < 1)
234 {
235 if (perr == TRUE)
Bram Moolenaar9d00e4a2022-01-05 17:49:15 +0000236 semsg(_(e_invalid_id_nr_must_be_greater_than_or_equal_to_one), id);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200237 return -1;
238 }
239 while (cur != NULL && cur->id != id)
240 {
241 prev = cur;
242 cur = cur->next;
243 }
244 if (cur == NULL)
245 {
246 if (perr == TRUE)
Bram Moolenaar9d00e4a2022-01-05 17:49:15 +0000247 semsg(_(e_id_not_found_nr), id);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200248 return -1;
249 }
250 if (cur == prev)
251 wp->w_match_head = cur->next;
252 else
253 prev->next = cur->next;
254 vim_regfree(cur->match.regprog);
255 vim_free(cur->pattern);
256 if (cur->pos.toplnum != 0)
257 {
258 if (wp->w_buffer->b_mod_set)
259 {
260 if (wp->w_buffer->b_mod_top > cur->pos.toplnum)
261 wp->w_buffer->b_mod_top = cur->pos.toplnum;
262 if (wp->w_buffer->b_mod_bot < cur->pos.botlnum)
263 wp->w_buffer->b_mod_bot = cur->pos.botlnum;
264 }
265 else
266 {
267 wp->w_buffer->b_mod_set = TRUE;
268 wp->w_buffer->b_mod_top = cur->pos.toplnum;
269 wp->w_buffer->b_mod_bot = cur->pos.botlnum;
270 wp->w_buffer->b_mod_xlines = 0;
271 }
272 rtype = VALID;
273 }
274 vim_free(cur);
275 redraw_win_later(wp, rtype);
276 return 0;
277}
278
279/*
280 * Delete all matches in the match list of window 'wp'.
281 */
282 void
283clear_matches(win_T *wp)
284{
285 matchitem_T *m;
286
287 while (wp->w_match_head != NULL)
288 {
289 m = wp->w_match_head->next;
290 vim_regfree(wp->w_match_head->match.regprog);
291 vim_free(wp->w_match_head->pattern);
292 vim_free(wp->w_match_head);
293 wp->w_match_head = m;
294 }
295 redraw_win_later(wp, SOME_VALID);
296}
297
298/*
299 * Get match from ID 'id' in window 'wp'.
300 * Return NULL if match not found.
301 */
302 static matchitem_T *
303get_match(win_T *wp, int id)
304{
305 matchitem_T *cur = wp->w_match_head;
306
307 while (cur != NULL && cur->id != id)
308 cur = cur->next;
309 return cur;
310}
311
312/*
313 * Init for calling prepare_search_hl().
314 */
315 void
316init_search_hl(win_T *wp, match_T *search_hl)
317{
318 matchitem_T *cur;
319
320 // Setup for match and 'hlsearch' highlighting. Disable any previous
321 // match
322 cur = wp->w_match_head;
323 while (cur != NULL)
324 {
325 cur->hl.rm = cur->match;
326 if (cur->hlg_id == 0)
327 cur->hl.attr = 0;
328 else
329 cur->hl.attr = syn_id2attr(cur->hlg_id);
330 cur->hl.buf = wp->w_buffer;
331 cur->hl.lnum = 0;
332 cur->hl.first_lnum = 0;
333# ifdef FEAT_RELTIME
334 // Set the time limit to 'redrawtime'.
335 profile_setlimit(p_rdt, &(cur->hl.tm));
336# endif
337 cur = cur->next;
338 }
339 search_hl->buf = wp->w_buffer;
340 search_hl->lnum = 0;
341 search_hl->first_lnum = 0;
342 // time limit is set at the toplevel, for all windows
343}
344
345/*
346 * If there is a match fill "shl" and return one.
347 * Return zero otherwise.
348 */
349 static int
350next_search_hl_pos(
351 match_T *shl, // points to a match
352 linenr_T lnum,
353 posmatch_T *posmatch, // match positions
354 colnr_T mincol) // minimal column for a match
355{
356 int i;
357 int found = -1;
358
359 for (i = posmatch->cur; i < MAXPOSMATCH; i++)
360 {
361 llpos_T *pos = &posmatch->pos[i];
362
363 if (pos->lnum == 0)
364 break;
365 if (pos->len == 0 && pos->col < mincol)
366 continue;
367 if (pos->lnum == lnum)
368 {
369 if (found >= 0)
370 {
371 // if this match comes before the one at "found" then swap
372 // them
373 if (pos->col < posmatch->pos[found].col)
374 {
375 llpos_T tmp = *pos;
376
377 *pos = posmatch->pos[found];
378 posmatch->pos[found] = tmp;
379 }
380 }
381 else
382 found = i;
383 }
384 }
385 posmatch->cur = 0;
386 if (found >= 0)
387 {
388 colnr_T start = posmatch->pos[found].col == 0
389 ? 0 : posmatch->pos[found].col - 1;
390 colnr_T end = posmatch->pos[found].col == 0
391 ? MAXCOL : start + posmatch->pos[found].len;
392
393 shl->lnum = lnum;
394 shl->rm.startpos[0].lnum = 0;
395 shl->rm.startpos[0].col = start;
396 shl->rm.endpos[0].lnum = 0;
397 shl->rm.endpos[0].col = end;
398 shl->is_addpos = TRUE;
399 posmatch->cur = found + 1;
400 return 1;
401 }
402 return 0;
403}
404
405/*
406 * Search for a next 'hlsearch' or match.
407 * Uses shl->buf.
408 * Sets shl->lnum and shl->rm contents.
409 * Note: Assumes a previous match is always before "lnum", unless
410 * shl->lnum is zero.
411 * Careful: Any pointers for buffer lines will become invalid.
412 */
413 static void
414next_search_hl(
415 win_T *win,
416 match_T *search_hl,
417 match_T *shl, // points to search_hl or a match
418 linenr_T lnum,
419 colnr_T mincol, // minimal column for a match
420 matchitem_T *cur) // to retrieve match positions if any
421{
422 linenr_T l;
423 colnr_T matchcol;
424 long nmatched;
425 int called_emsg_before = called_emsg;
426
427 // for :{range}s/pat only highlight inside the range
Bram Moolenaar94fb8272021-12-29 19:22:44 +0000428 if ((lnum < search_first_line || lnum > search_last_line) && cur == NULL)
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200429 {
430 shl->lnum = 0;
431 return;
432 }
433
434 if (shl->lnum != 0)
435 {
436 // Check for three situations:
437 // 1. If the "lnum" is below a previous match, start a new search.
438 // 2. If the previous match includes "mincol", use it.
439 // 3. Continue after the previous match.
440 l = shl->lnum + shl->rm.endpos[0].lnum - shl->rm.startpos[0].lnum;
441 if (lnum > l)
442 shl->lnum = 0;
443 else if (lnum < l || shl->rm.endpos[0].col > mincol)
444 return;
445 }
446
447 // Repeat searching for a match until one is found that includes "mincol"
448 // or none is found in this line.
449 for (;;)
450 {
451# ifdef FEAT_RELTIME
452 // Stop searching after passing the time limit.
453 if (profile_passed_limit(&(shl->tm)))
454 {
455 shl->lnum = 0; // no match found in time
456 break;
457 }
458# endif
459 // Three situations:
460 // 1. No useful previous match: search from start of line.
461 // 2. Not Vi compatible or empty match: continue at next character.
462 // Break the loop if this is beyond the end of the line.
463 // 3. Vi compatible searching: continue at end of previous match.
464 if (shl->lnum == 0)
465 matchcol = 0;
466 else if (vim_strchr(p_cpo, CPO_SEARCH) == NULL
467 || (shl->rm.endpos[0].lnum == 0
468 && shl->rm.endpos[0].col <= shl->rm.startpos[0].col))
469 {
470 char_u *ml;
471
472 matchcol = shl->rm.startpos[0].col;
473 ml = ml_get_buf(shl->buf, lnum, FALSE) + matchcol;
474 if (*ml == NUL)
475 {
476 ++matchcol;
477 shl->lnum = 0;
478 break;
479 }
480 if (has_mbyte)
481 matchcol += mb_ptr2len(ml);
482 else
483 ++matchcol;
484 }
485 else
486 matchcol = shl->rm.endpos[0].col;
487
488 shl->lnum = lnum;
489 if (shl->rm.regprog != NULL)
490 {
491 // Remember whether shl->rm is using a copy of the regprog in
492 // cur->match.
493 int regprog_is_copy = (shl != search_hl && cur != NULL
494 && shl == &cur->hl
495 && cur->match.regprog == cur->hl.rm.regprog);
496 int timed_out = FALSE;
497
498 nmatched = vim_regexec_multi(&shl->rm, win, shl->buf, lnum,
499 matchcol,
500#ifdef FEAT_RELTIME
501 &(shl->tm), &timed_out
502#else
503 NULL, NULL
504#endif
505 );
506 // Copy the regprog, in case it got freed and recompiled.
507 if (regprog_is_copy)
508 cur->match.regprog = cur->hl.rm.regprog;
509
510 if (called_emsg > called_emsg_before || got_int || timed_out)
511 {
512 // Error while handling regexp: stop using this regexp.
513 if (shl == search_hl)
514 {
515 // don't free regprog in the match list, it's a copy
516 vim_regfree(shl->rm.regprog);
517 set_no_hlsearch(TRUE);
518 }
519 shl->rm.regprog = NULL;
520 shl->lnum = 0;
521 got_int = FALSE; // avoid the "Type :quit to exit Vim" message
522 break;
523 }
524 }
525 else if (cur != NULL)
526 nmatched = next_search_hl_pos(shl, lnum, &(cur->pos), matchcol);
527 else
528 nmatched = 0;
529 if (nmatched == 0)
530 {
531 shl->lnum = 0; // no match found
532 break;
533 }
534 if (shl->rm.startpos[0].lnum > 0
535 || shl->rm.startpos[0].col >= mincol
536 || nmatched > 1
537 || shl->rm.endpos[0].col > mincol)
538 {
539 shl->lnum += shl->rm.startpos[0].lnum;
540 break; // useful match found
541 }
542 }
543}
544
545/*
546 * Advance to the match in window "wp" line "lnum" or past it.
547 */
548 void
549prepare_search_hl(win_T *wp, match_T *search_hl, linenr_T lnum)
550{
551 matchitem_T *cur; // points to the match list
552 match_T *shl; // points to search_hl or a match
553 int shl_flag; // flag to indicate whether search_hl
554 // has been processed or not
555 int pos_inprogress; // marks that position match search is
556 // in progress
557 int n;
558
559 // When using a multi-line pattern, start searching at the top
560 // of the window or just after a closed fold.
561 // Do this both for search_hl and the match list.
562 cur = wp->w_match_head;
563 shl_flag = WIN_IS_POPUP(wp); // skip search_hl in a popup window
564 while (cur != NULL || shl_flag == FALSE)
565 {
566 if (shl_flag == FALSE)
567 {
568 shl = search_hl;
569 shl_flag = TRUE;
570 }
571 else
572 shl = &cur->hl;
573 if (shl->rm.regprog != NULL
574 && shl->lnum == 0
575 && re_multiline(shl->rm.regprog))
576 {
577 if (shl->first_lnum == 0)
578 {
579# ifdef FEAT_FOLDING
580 for (shl->first_lnum = lnum;
581 shl->first_lnum > wp->w_topline; --shl->first_lnum)
582 if (hasFoldingWin(wp, shl->first_lnum - 1,
583 NULL, NULL, TRUE, NULL))
584 break;
585# else
586 shl->first_lnum = wp->w_topline;
587# endif
588 }
589 if (cur != NULL)
590 cur->pos.cur = 0;
591 pos_inprogress = TRUE;
592 n = 0;
593 while (shl->first_lnum < lnum && (shl->rm.regprog != NULL
594 || (cur != NULL && pos_inprogress)))
595 {
596 next_search_hl(wp, search_hl, shl, shl->first_lnum, (colnr_T)n,
597 shl == search_hl ? NULL : cur);
598 pos_inprogress = cur == NULL || cur->pos.cur == 0
599 ? FALSE : TRUE;
600 if (shl->lnum != 0)
601 {
602 shl->first_lnum = shl->lnum
603 + shl->rm.endpos[0].lnum
604 - shl->rm.startpos[0].lnum;
605 n = shl->rm.endpos[0].col;
606 }
607 else
608 {
609 ++shl->first_lnum;
610 n = 0;
611 }
612 }
613 }
614 if (shl != search_hl && cur != NULL)
615 cur = cur->next;
616 }
617}
618
619/*
620 * Prepare for 'hlsearch' and match highlighting in one window line.
621 * Return TRUE if there is such highlighting and set "search_attr" to the
622 * current highlight attribute.
623 */
624 int
625prepare_search_hl_line(
626 win_T *wp,
627 linenr_T lnum,
628 colnr_T mincol,
629 char_u **line,
630 match_T *search_hl,
631 int *search_attr)
632{
633 matchitem_T *cur; // points to the match list
634 match_T *shl; // points to search_hl or a match
635 int shl_flag; // flag to indicate whether search_hl
636 // has been processed or not
637 int area_highlighting = FALSE;
638
639 // Handle highlighting the last used search pattern and matches.
640 // Do this for both search_hl and the match list.
641 // Do not use search_hl in a popup window.
642 cur = wp->w_match_head;
643 shl_flag = WIN_IS_POPUP(wp);
644 while (cur != NULL || shl_flag == FALSE)
645 {
646 if (shl_flag == FALSE)
647 {
648 shl = search_hl;
649 shl_flag = TRUE;
650 }
651 else
652 shl = &cur->hl;
653 shl->startcol = MAXCOL;
654 shl->endcol = MAXCOL;
655 shl->attr_cur = 0;
656 shl->is_addpos = FALSE;
657 if (cur != NULL)
658 cur->pos.cur = 0;
659 next_search_hl(wp, search_hl, shl, lnum, mincol,
660 shl == search_hl ? NULL : cur);
661
662 // Need to get the line again, a multi-line regexp may have made it
663 // invalid.
664 *line = ml_get_buf(wp->w_buffer, lnum, FALSE);
665
666 if (shl->lnum != 0 && shl->lnum <= lnum)
667 {
668 if (shl->lnum == lnum)
669 shl->startcol = shl->rm.startpos[0].col;
670 else
671 shl->startcol = 0;
672 if (lnum == shl->lnum + shl->rm.endpos[0].lnum
673 - shl->rm.startpos[0].lnum)
674 shl->endcol = shl->rm.endpos[0].col;
675 else
676 shl->endcol = MAXCOL;
677 // Highlight one character for an empty match.
678 if (shl->startcol == shl->endcol)
679 {
680 if (has_mbyte && (*line)[shl->endcol] != NUL)
681 shl->endcol += (*mb_ptr2len)((*line) + shl->endcol);
682 else
683 ++shl->endcol;
684 }
685 if ((long)shl->startcol < mincol) // match at leftcol
686 {
687 shl->attr_cur = shl->attr;
688 *search_attr = shl->attr;
689 }
690 area_highlighting = TRUE;
691 }
692 if (shl != search_hl && cur != NULL)
693 cur = cur->next;
694 }
695 return area_highlighting;
696}
697
698/*
699 * For a position in a line: Check for start/end of 'hlsearch' and other
700 * matches.
701 * After end, check for start/end of next match.
702 * When another match, have to check for start again.
703 * Watch out for matching an empty string!
Bram Moolenaar0c359af2021-11-29 19:18:57 +0000704 * "on_last_col" is set to TRUE with non-zero search_attr and the next column
705 * is endcol.
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200706 * Return the updated search_attr.
707 */
708 int
709update_search_hl(
710 win_T *wp,
711 linenr_T lnum,
712 colnr_T col,
713 char_u **line,
714 match_T *search_hl,
715 int *has_match_conc UNUSED,
716 int *match_conc UNUSED,
717 int did_line_attr,
Bram Moolenaar0c359af2021-11-29 19:18:57 +0000718 int lcs_eol_one,
719 int *on_last_col)
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200720{
721 matchitem_T *cur; // points to the match list
722 match_T *shl; // points to search_hl or a match
723 int shl_flag; // flag to indicate whether search_hl
724 // has been processed or not
725 int pos_inprogress; // marks that position match search is in
726 // progress
727 int search_attr = 0;
728
729
730 // Do this for 'search_hl' and the match list (ordered by priority).
731 cur = wp->w_match_head;
732 shl_flag = WIN_IS_POPUP(wp);
733 while (cur != NULL || shl_flag == FALSE)
734 {
735 if (shl_flag == FALSE
736 && (cur == NULL
737 || cur->priority > SEARCH_HL_PRIORITY))
738 {
739 shl = search_hl;
740 shl_flag = TRUE;
741 }
742 else
743 shl = &cur->hl;
744 if (cur != NULL)
745 cur->pos.cur = 0;
746 pos_inprogress = TRUE;
747 while (shl->rm.regprog != NULL || (cur != NULL && pos_inprogress))
748 {
749 if (shl->startcol != MAXCOL
750 && col >= shl->startcol
751 && col < shl->endcol)
752 {
753 int next_col = col + mb_ptr2len(*line + col);
754
755 if (shl->endcol < next_col)
756 shl->endcol = next_col;
757 shl->attr_cur = shl->attr;
758# ifdef FEAT_CONCEAL
759 // Match with the "Conceal" group results in hiding
760 // the match.
761 if (cur != NULL
762 && shl != search_hl
763 && syn_name2id((char_u *)"Conceal") == cur->hlg_id)
764 {
765 *has_match_conc = col == shl->startcol ? 2 : 1;
766 *match_conc = cur->conceal_char;
767 }
768 else
769 *has_match_conc = 0;
770# endif
771 }
772 else if (col == shl->endcol)
773 {
774 shl->attr_cur = 0;
775 next_search_hl(wp, search_hl, shl, lnum, col,
776 shl == search_hl ? NULL : cur);
777 pos_inprogress = !(cur == NULL || cur->pos.cur == 0);
778
779 // Need to get the line again, a multi-line regexp may have
780 // made it invalid.
781 *line = ml_get_buf(wp->w_buffer, lnum, FALSE);
782
783 if (shl->lnum == lnum)
784 {
785 shl->startcol = shl->rm.startpos[0].col;
786 if (shl->rm.endpos[0].lnum == 0)
787 shl->endcol = shl->rm.endpos[0].col;
788 else
789 shl->endcol = MAXCOL;
790
791 if (shl->startcol == shl->endcol)
792 {
793 // highlight empty match, try again after
794 // it
795 if (has_mbyte)
Bram Moolenaar41f08952021-02-22 22:13:49 +0100796 {
797 char_u *p = *line + shl->endcol;
798
799 if (*p == NUL)
800 // consistent with non-mbyte
801 ++shl->endcol;
802 else
803 shl->endcol += (*mb_ptr2len)(p);
804 }
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200805 else
806 ++shl->endcol;
807 }
808
809 // Loop to check if the match starts at the
810 // current position
811 continue;
812 }
813 }
814 break;
815 }
816 if (shl != search_hl && cur != NULL)
817 cur = cur->next;
818 }
819
820 // Use attributes from match with highest priority among 'search_hl' and
821 // the match list.
822 cur = wp->w_match_head;
823 shl_flag = WIN_IS_POPUP(wp);
824 while (cur != NULL || shl_flag == FALSE)
825 {
826 if (shl_flag == FALSE
827 && (cur == NULL ||
828 cur->priority > SEARCH_HL_PRIORITY))
829 {
830 shl = search_hl;
831 shl_flag = TRUE;
832 }
833 else
834 shl = &cur->hl;
835 if (shl->attr_cur != 0)
Bram Moolenaar0c359af2021-11-29 19:18:57 +0000836 {
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200837 search_attr = shl->attr_cur;
Bram Moolenaar0c359af2021-11-29 19:18:57 +0000838 *on_last_col = col + 1 >= shl->endcol;
839 }
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200840 if (shl != search_hl && cur != NULL)
841 cur = cur->next;
842 }
843 // Only highlight one character after the last column.
844 if (*(*line + col) == NUL && (did_line_attr >= 1
845 || (wp->w_p_list && lcs_eol_one == -1)))
846 search_attr = 0;
847 return search_attr;
848}
849
850 int
851get_prevcol_hl_flag(win_T *wp, match_T *search_hl, long curcol)
852{
853 long prevcol = curcol;
854 int prevcol_hl_flag = FALSE;
855 matchitem_T *cur; // points to the match list
856
Bram Moolenaar41f08952021-02-22 22:13:49 +0100857#if defined(FEAT_PROP_POPUP)
858 // don't do this in a popup window
859 if (popup_is_popup(wp))
860 return FALSE;
861#endif
862
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200863 // we're not really at that column when skipping some text
864 if ((long)(wp->w_p_wrap ? wp->w_skipcol : wp->w_leftcol) > prevcol)
865 ++prevcol;
866
Bram Moolenaar41f08952021-02-22 22:13:49 +0100867 // Highlight a character after the end of the line if the match started
868 // at the end of the line or when the match continues in the next line
869 // (match includes the line break).
870 if (!search_hl->is_addpos && (prevcol == (long)search_hl->startcol
871 || (prevcol > (long)search_hl->startcol
872 && search_hl->endcol == MAXCOL)))
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200873 prevcol_hl_flag = TRUE;
874 else
875 {
876 cur = wp->w_match_head;
877 while (cur != NULL)
878 {
Bram Moolenaar41f08952021-02-22 22:13:49 +0100879 if (!cur->hl.is_addpos && (prevcol == (long)cur->hl.startcol
880 || (prevcol > (long)cur->hl.startcol
881 && cur->hl.endcol == MAXCOL)))
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200882 {
883 prevcol_hl_flag = TRUE;
884 break;
885 }
886 cur = cur->next;
887 }
888 }
889 return prevcol_hl_flag;
890}
891
892/*
893 * Get highlighting for the char after the text in "char_attr" from 'hlsearch'
894 * or match highlighting.
895 */
896 void
897get_search_match_hl(win_T *wp, match_T *search_hl, long col, int *char_attr)
898{
899 matchitem_T *cur; // points to the match list
900 match_T *shl; // points to search_hl or a match
901 int shl_flag; // flag to indicate whether search_hl
902 // has been processed or not
903
904 cur = wp->w_match_head;
905 shl_flag = WIN_IS_POPUP(wp);
906 while (cur != NULL || shl_flag == FALSE)
907 {
908 if (shl_flag == FALSE
909 && ((cur != NULL
910 && cur->priority > SEARCH_HL_PRIORITY)
911 || cur == NULL))
912 {
913 shl = search_hl;
914 shl_flag = TRUE;
915 }
916 else
917 shl = &cur->hl;
918 if (col - 1 == (long)shl->startcol
919 && (shl == search_hl || !shl->is_addpos))
920 *char_attr = shl->attr;
921 if (shl != search_hl && cur != NULL)
922 cur = cur->next;
923 }
924}
925
926#endif // FEAT_SEARCH_EXTRA
927
928#if defined(FEAT_EVAL) || defined(PROTO)
929# ifdef FEAT_SEARCH_EXTRA
930 static int
931matchadd_dict_arg(typval_T *tv, char_u **conceal_char, win_T **win)
932{
933 dictitem_T *di;
934
935 if (tv->v_type != VAR_DICT)
936 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +0000937 emsg(_(e_dictionary_required));
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200938 return FAIL;
939 }
940
941 if (dict_find(tv->vval.v_dict, (char_u *)"conceal", -1) != NULL)
942 *conceal_char = dict_get_string(tv->vval.v_dict,
943 (char_u *)"conceal", FALSE);
944
945 if ((di = dict_find(tv->vval.v_dict, (char_u *)"window", -1)) != NULL)
946 {
947 *win = find_win_by_nr_or_id(&di->di_tv);
948 if (*win == NULL)
949 {
Bram Moolenaar3a846e62022-01-01 16:21:00 +0000950 emsg(_(e_invalid_window_number));
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200951 return FAIL;
952 }
953 }
954
955 return OK;
956}
957#endif
958
959/*
960 * "clearmatches()" function
961 */
962 void
963f_clearmatches(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
964{
965#ifdef FEAT_SEARCH_EXTRA
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200966 win_T *win;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200967
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200968 if (in_vim9script() && check_for_opt_number_arg(argvars, 0) == FAIL)
969 return;
970
971 win = get_optional_window(argvars, 0);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200972 if (win != NULL)
973 clear_matches(win);
974#endif
975}
976
977/*
978 * "getmatches()" function
979 */
980 void
981f_getmatches(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
982{
983# ifdef FEAT_SEARCH_EXTRA
984 dict_T *dict;
985 matchitem_T *cur;
986 int i;
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200987 win_T *win;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200988
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200989 if (in_vim9script() && check_for_opt_number_arg(argvars, 0) == FAIL)
990 return;
991
992 win = get_optional_window(argvars, 0);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200993 if (rettv_list_alloc(rettv) == FAIL || win == NULL)
994 return;
995
996 cur = win->w_match_head;
997 while (cur != NULL)
998 {
999 dict = dict_alloc();
1000 if (dict == NULL)
1001 return;
1002 if (cur->match.regprog == NULL)
1003 {
1004 // match added with matchaddpos()
1005 for (i = 0; i < MAXPOSMATCH; ++i)
1006 {
1007 llpos_T *llpos;
1008 char buf[30]; // use 30 to avoid compiler warning
1009 list_T *l;
1010
1011 llpos = &cur->pos.pos[i];
1012 if (llpos->lnum == 0)
1013 break;
1014 l = list_alloc();
1015 if (l == NULL)
1016 break;
1017 list_append_number(l, (varnumber_T)llpos->lnum);
1018 if (llpos->col > 0)
1019 {
1020 list_append_number(l, (varnumber_T)llpos->col);
1021 list_append_number(l, (varnumber_T)llpos->len);
1022 }
1023 sprintf(buf, "pos%d", i + 1);
1024 dict_add_list(dict, buf, l);
1025 }
1026 }
1027 else
1028 {
1029 dict_add_string(dict, "pattern", cur->pattern);
1030 }
1031 dict_add_string(dict, "group", syn_id2name(cur->hlg_id));
1032 dict_add_number(dict, "priority", (long)cur->priority);
1033 dict_add_number(dict, "id", (long)cur->id);
1034# if defined(FEAT_CONCEAL)
1035 if (cur->conceal_char)
1036 {
1037 char_u buf[MB_MAXBYTES + 1];
1038
1039 buf[(*mb_char2bytes)((int)cur->conceal_char, buf)] = NUL;
1040 dict_add_string(dict, "conceal", (char_u *)&buf);
1041 }
1042# endif
1043 list_append_dict(rettv->vval.v_list, dict);
1044 cur = cur->next;
1045 }
1046# endif
1047}
1048
1049/*
1050 * "setmatches()" function
1051 */
1052 void
1053f_setmatches(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
1054{
1055#ifdef FEAT_SEARCH_EXTRA
1056 list_T *l;
1057 listitem_T *li;
1058 dict_T *d;
1059 list_T *s = NULL;
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02001060 win_T *win;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001061
1062 rettv->vval.v_number = -1;
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02001063
1064 if (in_vim9script()
1065 && (check_for_list_arg(argvars, 0) == FAIL
1066 || check_for_opt_number_arg(argvars, 1) == FAIL))
1067 return;
1068
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001069 if (argvars[0].v_type != VAR_LIST)
1070 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00001071 emsg(_(e_list_required));
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001072 return;
1073 }
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02001074 win = get_optional_window(argvars, 1);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001075 if (win == NULL)
1076 return;
1077
1078 if ((l = argvars[0].vval.v_list) != NULL)
1079 {
1080 // To some extent make sure that we are dealing with a list from
1081 // "getmatches()".
1082 li = l->lv_first;
1083 while (li != NULL)
1084 {
1085 if (li->li_tv.v_type != VAR_DICT
1086 || (d = li->li_tv.vval.v_dict) == NULL)
1087 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001088 emsg(_(e_invalid_argument));
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001089 return;
1090 }
1091 if (!(dict_find(d, (char_u *)"group", -1) != NULL
1092 && (dict_find(d, (char_u *)"pattern", -1) != NULL
1093 || dict_find(d, (char_u *)"pos1", -1) != NULL)
1094 && dict_find(d, (char_u *)"priority", -1) != NULL
1095 && dict_find(d, (char_u *)"id", -1) != NULL))
1096 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001097 emsg(_(e_invalid_argument));
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001098 return;
1099 }
1100 li = li->li_next;
1101 }
1102
1103 clear_matches(win);
1104 li = l->lv_first;
1105 while (li != NULL)
1106 {
1107 int i = 0;
1108 char buf[30]; // use 30 to avoid compiler warning
1109 dictitem_T *di;
1110 char_u *group;
1111 int priority;
1112 int id;
1113 char_u *conceal;
1114
1115 d = li->li_tv.vval.v_dict;
1116 if (dict_find(d, (char_u *)"pattern", -1) == NULL)
1117 {
1118 if (s == NULL)
1119 {
1120 s = list_alloc();
1121 if (s == NULL)
1122 return;
1123 }
1124
1125 // match from matchaddpos()
1126 for (i = 1; i < 9; i++)
1127 {
1128 sprintf((char *)buf, (char *)"pos%d", i);
1129 if ((di = dict_find(d, (char_u *)buf, -1)) != NULL)
1130 {
1131 if (di->di_tv.v_type != VAR_LIST)
1132 return;
1133
1134 list_append_tv(s, &di->di_tv);
1135 s->lv_refcount++;
1136 }
1137 else
1138 break;
1139 }
1140 }
1141
1142 group = dict_get_string(d, (char_u *)"group", TRUE);
1143 priority = (int)dict_get_number(d, (char_u *)"priority");
1144 id = (int)dict_get_number(d, (char_u *)"id");
1145 conceal = dict_find(d, (char_u *)"conceal", -1) != NULL
1146 ? dict_get_string(d, (char_u *)"conceal", TRUE)
1147 : NULL;
1148 if (i == 0)
1149 {
1150 match_add(win, group,
1151 dict_get_string(d, (char_u *)"pattern", FALSE),
1152 priority, id, NULL, conceal);
1153 }
1154 else
1155 {
1156 match_add(win, group, NULL, priority, id, s, conceal);
1157 list_unref(s);
1158 s = NULL;
1159 }
1160 vim_free(group);
1161 vim_free(conceal);
1162
1163 li = li->li_next;
1164 }
1165 rettv->vval.v_number = 0;
1166 }
1167#endif
1168}
1169
1170/*
1171 * "matchadd()" function
1172 */
1173 void
1174f_matchadd(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
1175{
1176# ifdef FEAT_SEARCH_EXTRA
1177 char_u buf[NUMBUFLEN];
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02001178 char_u *grp; // group
1179 char_u *pat; // pattern
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001180 int prio = 10; // default priority
1181 int id = -1;
1182 int error = FALSE;
1183 char_u *conceal_char = NULL;
1184 win_T *win = curwin;
1185
1186 rettv->vval.v_number = -1;
1187
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02001188 if (in_vim9script()
1189 && (check_for_string_arg(argvars, 0) == FAIL
1190 || check_for_string_arg(argvars, 1) == FAIL
1191 || check_for_opt_number_arg(argvars, 2) == FAIL
1192 || (argvars[2].v_type != VAR_UNKNOWN
1193 && (check_for_opt_number_arg(argvars, 3) == FAIL
1194 || (argvars[3].v_type != VAR_UNKNOWN
1195 && check_for_opt_dict_arg(argvars, 4) == FAIL)))))
1196 return;
1197
1198 grp = tv_get_string_buf_chk(&argvars[0], buf); // group
1199 pat = tv_get_string_buf_chk(&argvars[1], buf); // pattern
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001200 if (grp == NULL || pat == NULL)
1201 return;
1202 if (argvars[2].v_type != VAR_UNKNOWN)
1203 {
1204 prio = (int)tv_get_number_chk(&argvars[2], &error);
1205 if (argvars[3].v_type != VAR_UNKNOWN)
1206 {
1207 id = (int)tv_get_number_chk(&argvars[3], &error);
1208 if (argvars[4].v_type != VAR_UNKNOWN
1209 && matchadd_dict_arg(&argvars[4], &conceal_char, &win) == FAIL)
1210 return;
1211 }
1212 }
1213 if (error == TRUE)
1214 return;
1215 if (id >= 1 && id <= 3)
1216 {
Bram Moolenaar677658a2022-01-05 16:09:06 +00001217 semsg(_(e_id_is_reserved_for_match_nr), id);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001218 return;
1219 }
1220
1221 rettv->vval.v_number = match_add(win, grp, pat, prio, id, NULL,
1222 conceal_char);
1223# endif
1224}
1225
1226/*
1227 * "matchaddpos()" function
1228 */
1229 void
1230f_matchaddpos(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
1231{
1232# ifdef FEAT_SEARCH_EXTRA
1233 char_u buf[NUMBUFLEN];
1234 char_u *group;
1235 int prio = 10;
1236 int id = -1;
1237 int error = FALSE;
1238 list_T *l;
1239 char_u *conceal_char = NULL;
1240 win_T *win = curwin;
1241
1242 rettv->vval.v_number = -1;
1243
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02001244 if (in_vim9script()
1245 && (check_for_string_arg(argvars, 0) == FAIL
1246 || check_for_list_arg(argvars, 1) == FAIL
1247 || check_for_opt_number_arg(argvars, 2) == FAIL
1248 || (argvars[2].v_type != VAR_UNKNOWN
1249 && (check_for_opt_number_arg(argvars, 3) == FAIL
1250 || (argvars[3].v_type != VAR_UNKNOWN
1251 && check_for_opt_dict_arg(argvars, 4) == FAIL)))))
1252 return;
1253
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001254 group = tv_get_string_buf_chk(&argvars[0], buf);
1255 if (group == NULL)
1256 return;
1257
1258 if (argvars[1].v_type != VAR_LIST)
1259 {
Bram Moolenaar3a846e62022-01-01 16:21:00 +00001260 semsg(_(e_argument_of_str_must_be_list), "matchaddpos()");
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001261 return;
1262 }
1263 l = argvars[1].vval.v_list;
1264 if (l == NULL)
1265 return;
1266
1267 if (argvars[2].v_type != VAR_UNKNOWN)
1268 {
1269 prio = (int)tv_get_number_chk(&argvars[2], &error);
1270 if (argvars[3].v_type != VAR_UNKNOWN)
1271 {
1272 id = (int)tv_get_number_chk(&argvars[3], &error);
1273
1274 if (argvars[4].v_type != VAR_UNKNOWN
1275 && matchadd_dict_arg(&argvars[4], &conceal_char, &win) == FAIL)
1276 return;
1277 }
1278 }
1279 if (error == TRUE)
1280 return;
1281
1282 // id == 3 is ok because matchaddpos() is supposed to substitute :3match
1283 if (id == 1 || id == 2)
1284 {
Bram Moolenaar677658a2022-01-05 16:09:06 +00001285 semsg(_(e_id_is_reserved_for_match_nr), id);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001286 return;
1287 }
1288
1289 rettv->vval.v_number = match_add(win, group, NULL, prio, id, l,
1290 conceal_char);
1291# endif
1292}
1293
1294/*
1295 * "matcharg()" function
1296 */
1297 void
1298f_matcharg(typval_T *argvars UNUSED, typval_T *rettv)
1299{
1300 if (rettv_list_alloc(rettv) == OK)
1301 {
1302# ifdef FEAT_SEARCH_EXTRA
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001303 int id;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001304 matchitem_T *m;
1305
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001306 if (in_vim9script() && check_for_number_arg(argvars, 0) == FAIL)
1307 return;
1308
1309 id = (int)tv_get_number(&argvars[0]);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001310 if (id >= 1 && id <= 3)
1311 {
1312 if ((m = (matchitem_T *)get_match(curwin, id)) != NULL)
1313 {
1314 list_append_string(rettv->vval.v_list,
1315 syn_id2name(m->hlg_id), -1);
1316 list_append_string(rettv->vval.v_list, m->pattern, -1);
1317 }
1318 else
1319 {
1320 list_append_string(rettv->vval.v_list, NULL, -1);
1321 list_append_string(rettv->vval.v_list, NULL, -1);
1322 }
1323 }
1324# endif
1325 }
1326}
1327
1328/*
1329 * "matchdelete()" function
1330 */
1331 void
1332f_matchdelete(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
1333{
1334# ifdef FEAT_SEARCH_EXTRA
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001335 win_T *win;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001336
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001337 if (in_vim9script()
1338 && (check_for_number_arg(argvars, 0) == FAIL
1339 || check_for_opt_number_arg(argvars, 1) == FAIL))
1340 return;
1341
1342 win = get_optional_window(argvars, 1);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001343 if (win == NULL)
1344 rettv->vval.v_number = -1;
1345 else
1346 rettv->vval.v_number = match_delete(win,
1347 (int)tv_get_number(&argvars[0]), TRUE);
1348# endif
1349}
1350#endif
1351
1352#if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
1353/*
1354 * ":[N]match {group} {pattern}"
1355 * Sets nextcmd to the start of the next command, if any. Also called when
1356 * skipping commands to find the next command.
1357 */
1358 void
1359ex_match(exarg_T *eap)
1360{
1361 char_u *p;
1362 char_u *g = NULL;
1363 char_u *end;
1364 int c;
1365 int id;
1366
1367 if (eap->line2 <= 3)
1368 id = eap->line2;
1369 else
1370 {
Bram Moolenaar451c2e32020-08-15 16:33:28 +02001371 emsg(_(e_invalid_command));
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001372 return;
1373 }
1374
1375 // First clear any old pattern.
1376 if (!eap->skip)
1377 match_delete(curwin, id, FALSE);
1378
1379 if (ends_excmd2(eap->cmd, eap->arg))
1380 end = eap->arg;
1381 else if ((STRNICMP(eap->arg, "none", 4) == 0
1382 && (VIM_ISWHITE(eap->arg[4])
1383 || ends_excmd2(eap->arg, eap->arg + 4))))
1384 end = eap->arg + 4;
1385 else
1386 {
1387 p = skiptowhite(eap->arg);
1388 if (!eap->skip)
1389 g = vim_strnsave(eap->arg, p - eap->arg);
1390 p = skipwhite(p);
1391 if (*p == NUL)
1392 {
1393 // There must be two arguments.
1394 vim_free(g);
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001395 semsg(_(e_invalid_argument_str), eap->arg);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001396 return;
1397 }
1398 end = skip_regexp(p + 1, *p, TRUE);
1399 if (!eap->skip)
1400 {
1401 if (*end != NUL && !ends_excmd2(end, skipwhite(end + 1)))
1402 {
1403 vim_free(g);
Bram Moolenaar74409f62022-01-01 15:58:22 +00001404 eap->errmsg = ex_errmsg(e_trailing_characters_str, end);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001405 return;
1406 }
1407 if (*end != *p)
1408 {
1409 vim_free(g);
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001410 semsg(_(e_invalid_argument_str), p);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001411 return;
1412 }
1413
1414 c = *end;
1415 *end = NUL;
1416 match_add(curwin, g, p + 1, 10, id, NULL, NULL);
1417 vim_free(g);
1418 *end = c;
1419 }
1420 }
1421 eap->nextcmd = find_nextcmd(end);
1422}
1423#endif