blob: fb130ae4f0b6c3b79320edf918c21a36baf1472b [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 Moolenaarbb8cac52022-01-05 18:16:53 +000048 semsg(_(e_invalid_id_nr_must_be_greater_than_or_equal_to_one_1), 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 Moolenaarbb8cac52022-01-05 18:16:53 +0000236 semsg(_(e_invalid_id_nr_must_be_greater_than_or_equal_to_one_2), 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;
Bram Moolenaar693ccd12022-04-16 12:04:37 +0100399 shl->has_cursor = FALSE;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200400 posmatch->cur = found + 1;
401 return 1;
402 }
403 return 0;
404}
405
406/*
407 * Search for a next 'hlsearch' or match.
408 * Uses shl->buf.
409 * Sets shl->lnum and shl->rm contents.
410 * Note: Assumes a previous match is always before "lnum", unless
411 * shl->lnum is zero.
412 * Careful: Any pointers for buffer lines will become invalid.
413 */
414 static void
415next_search_hl(
416 win_T *win,
417 match_T *search_hl,
418 match_T *shl, // points to search_hl or a match
419 linenr_T lnum,
420 colnr_T mincol, // minimal column for a match
421 matchitem_T *cur) // to retrieve match positions if any
422{
423 linenr_T l;
424 colnr_T matchcol;
425 long nmatched;
426 int called_emsg_before = called_emsg;
427
428 // for :{range}s/pat only highlight inside the range
Bram Moolenaar94fb8272021-12-29 19:22:44 +0000429 if ((lnum < search_first_line || lnum > search_last_line) && cur == NULL)
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200430 {
431 shl->lnum = 0;
432 return;
433 }
434
435 if (shl->lnum != 0)
436 {
437 // Check for three situations:
438 // 1. If the "lnum" is below a previous match, start a new search.
439 // 2. If the previous match includes "mincol", use it.
440 // 3. Continue after the previous match.
441 l = shl->lnum + shl->rm.endpos[0].lnum - shl->rm.startpos[0].lnum;
442 if (lnum > l)
443 shl->lnum = 0;
444 else if (lnum < l || shl->rm.endpos[0].col > mincol)
445 return;
446 }
447
448 // Repeat searching for a match until one is found that includes "mincol"
449 // or none is found in this line.
450 for (;;)
451 {
452# ifdef FEAT_RELTIME
453 // Stop searching after passing the time limit.
454 if (profile_passed_limit(&(shl->tm)))
455 {
456 shl->lnum = 0; // no match found in time
457 break;
458 }
459# endif
460 // Three situations:
461 // 1. No useful previous match: search from start of line.
462 // 2. Not Vi compatible or empty match: continue at next character.
463 // Break the loop if this is beyond the end of the line.
464 // 3. Vi compatible searching: continue at end of previous match.
465 if (shl->lnum == 0)
466 matchcol = 0;
467 else if (vim_strchr(p_cpo, CPO_SEARCH) == NULL
468 || (shl->rm.endpos[0].lnum == 0
469 && shl->rm.endpos[0].col <= shl->rm.startpos[0].col))
470 {
471 char_u *ml;
472
473 matchcol = shl->rm.startpos[0].col;
474 ml = ml_get_buf(shl->buf, lnum, FALSE) + matchcol;
475 if (*ml == NUL)
476 {
477 ++matchcol;
478 shl->lnum = 0;
479 break;
480 }
481 if (has_mbyte)
482 matchcol += mb_ptr2len(ml);
483 else
484 ++matchcol;
485 }
486 else
487 matchcol = shl->rm.endpos[0].col;
488
489 shl->lnum = lnum;
490 if (shl->rm.regprog != NULL)
491 {
492 // Remember whether shl->rm is using a copy of the regprog in
493 // cur->match.
494 int regprog_is_copy = (shl != search_hl && cur != NULL
495 && shl == &cur->hl
496 && cur->match.regprog == cur->hl.rm.regprog);
497 int timed_out = FALSE;
498
499 nmatched = vim_regexec_multi(&shl->rm, win, shl->buf, lnum,
500 matchcol,
501#ifdef FEAT_RELTIME
502 &(shl->tm), &timed_out
503#else
504 NULL, NULL
505#endif
506 );
507 // Copy the regprog, in case it got freed and recompiled.
508 if (regprog_is_copy)
509 cur->match.regprog = cur->hl.rm.regprog;
510
511 if (called_emsg > called_emsg_before || got_int || timed_out)
512 {
513 // Error while handling regexp: stop using this regexp.
514 if (shl == search_hl)
515 {
516 // don't free regprog in the match list, it's a copy
517 vim_regfree(shl->rm.regprog);
518 set_no_hlsearch(TRUE);
519 }
520 shl->rm.regprog = NULL;
521 shl->lnum = 0;
522 got_int = FALSE; // avoid the "Type :quit to exit Vim" message
523 break;
524 }
525 }
526 else if (cur != NULL)
527 nmatched = next_search_hl_pos(shl, lnum, &(cur->pos), matchcol);
528 else
529 nmatched = 0;
530 if (nmatched == 0)
531 {
532 shl->lnum = 0; // no match found
533 break;
534 }
535 if (shl->rm.startpos[0].lnum > 0
536 || shl->rm.startpos[0].col >= mincol
537 || nmatched > 1
538 || shl->rm.endpos[0].col > mincol)
539 {
540 shl->lnum += shl->rm.startpos[0].lnum;
541 break; // useful match found
542 }
543 }
544}
545
546/*
547 * Advance to the match in window "wp" line "lnum" or past it.
548 */
549 void
550prepare_search_hl(win_T *wp, match_T *search_hl, linenr_T lnum)
551{
552 matchitem_T *cur; // points to the match list
553 match_T *shl; // points to search_hl or a match
554 int shl_flag; // flag to indicate whether search_hl
555 // has been processed or not
556 int pos_inprogress; // marks that position match search is
557 // in progress
558 int n;
559
560 // When using a multi-line pattern, start searching at the top
561 // of the window or just after a closed fold.
562 // Do this both for search_hl and the match list.
563 cur = wp->w_match_head;
564 shl_flag = WIN_IS_POPUP(wp); // skip search_hl in a popup window
565 while (cur != NULL || shl_flag == FALSE)
566 {
567 if (shl_flag == FALSE)
568 {
569 shl = search_hl;
570 shl_flag = TRUE;
571 }
572 else
573 shl = &cur->hl;
574 if (shl->rm.regprog != NULL
575 && shl->lnum == 0
576 && re_multiline(shl->rm.regprog))
577 {
578 if (shl->first_lnum == 0)
579 {
580# ifdef FEAT_FOLDING
581 for (shl->first_lnum = lnum;
582 shl->first_lnum > wp->w_topline; --shl->first_lnum)
583 if (hasFoldingWin(wp, shl->first_lnum - 1,
584 NULL, NULL, TRUE, NULL))
585 break;
586# else
587 shl->first_lnum = wp->w_topline;
588# endif
589 }
590 if (cur != NULL)
591 cur->pos.cur = 0;
592 pos_inprogress = TRUE;
593 n = 0;
594 while (shl->first_lnum < lnum && (shl->rm.regprog != NULL
595 || (cur != NULL && pos_inprogress)))
596 {
597 next_search_hl(wp, search_hl, shl, shl->first_lnum, (colnr_T)n,
598 shl == search_hl ? NULL : cur);
599 pos_inprogress = cur == NULL || cur->pos.cur == 0
600 ? FALSE : TRUE;
601 if (shl->lnum != 0)
602 {
603 shl->first_lnum = shl->lnum
604 + shl->rm.endpos[0].lnum
605 - shl->rm.startpos[0].lnum;
606 n = shl->rm.endpos[0].col;
607 }
608 else
609 {
610 ++shl->first_lnum;
611 n = 0;
612 }
613 }
614 }
615 if (shl != search_hl && cur != NULL)
616 cur = cur->next;
617 }
618}
619
620/*
Bram Moolenaar9b367502022-04-22 20:07:21 +0100621 * Update "shl->has_cursor" based on the match in "shl" and the cursor
622 * position.
623 */
624 static void
625check_cur_search_hl(win_T *wp, match_T *shl)
626{
627 long linecount = shl->rm.endpos[0].lnum - shl->rm.startpos[0].lnum;
628
629 if (wp->w_cursor.lnum >= shl->lnum
630 && wp->w_cursor.lnum <= shl->lnum + shl->rm.endpos[0].lnum
631 && (wp->w_cursor.lnum > shl->lnum
632 || wp->w_cursor.col >= shl->rm.startpos[0].col)
633 && (wp->w_cursor.lnum < shl->lnum + linecount
634 || wp->w_cursor.col < shl->rm.endpos[0].col))
635 shl->has_cursor = TRUE;
636 else
637 shl->has_cursor = FALSE;
638}
639
640/*
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200641 * Prepare for 'hlsearch' and match highlighting in one window line.
642 * Return TRUE if there is such highlighting and set "search_attr" to the
643 * current highlight attribute.
644 */
645 int
646prepare_search_hl_line(
647 win_T *wp,
648 linenr_T lnum,
649 colnr_T mincol,
650 char_u **line,
651 match_T *search_hl,
652 int *search_attr)
653{
654 matchitem_T *cur; // points to the match list
655 match_T *shl; // points to search_hl or a match
656 int shl_flag; // flag to indicate whether search_hl
657 // has been processed or not
658 int area_highlighting = FALSE;
659
660 // Handle highlighting the last used search pattern and matches.
661 // Do this for both search_hl and the match list.
662 // Do not use search_hl in a popup window.
663 cur = wp->w_match_head;
664 shl_flag = WIN_IS_POPUP(wp);
665 while (cur != NULL || shl_flag == FALSE)
666 {
667 if (shl_flag == FALSE)
668 {
669 shl = search_hl;
670 shl_flag = TRUE;
671 }
672 else
673 shl = &cur->hl;
674 shl->startcol = MAXCOL;
675 shl->endcol = MAXCOL;
LemonBoya4399382022-04-09 21:04:08 +0100676 shl->lines = 0;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200677 shl->attr_cur = 0;
678 shl->is_addpos = FALSE;
Bram Moolenaar693ccd12022-04-16 12:04:37 +0100679 shl->has_cursor = FALSE;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200680 if (cur != NULL)
681 cur->pos.cur = 0;
682 next_search_hl(wp, search_hl, shl, lnum, mincol,
683 shl == search_hl ? NULL : cur);
684
685 // Need to get the line again, a multi-line regexp may have made it
686 // invalid.
687 *line = ml_get_buf(wp->w_buffer, lnum, FALSE);
688
689 if (shl->lnum != 0 && shl->lnum <= lnum)
690 {
691 if (shl->lnum == lnum)
692 shl->startcol = shl->rm.startpos[0].col;
693 else
694 shl->startcol = 0;
695 if (lnum == shl->lnum + shl->rm.endpos[0].lnum
696 - shl->rm.startpos[0].lnum)
697 shl->endcol = shl->rm.endpos[0].col;
698 else
699 shl->endcol = MAXCOL;
Bram Moolenaar9b367502022-04-22 20:07:21 +0100700 shl->lines = shl->rm.endpos[0].lnum - shl->rm.startpos[0].lnum;
701 if (shl->lines == 0)
LemonBoya4399382022-04-09 21:04:08 +0100702 shl->lines = 1;
Bram Moolenaar693ccd12022-04-16 12:04:37 +0100703
704 // check if the cursor is in the match before changing the columns
Bram Moolenaar9b367502022-04-22 20:07:21 +0100705 if (shl == search_hl)
706 check_cur_search_hl(wp, shl);
Bram Moolenaar693ccd12022-04-16 12:04:37 +0100707
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200708 // Highlight one character for an empty match.
709 if (shl->startcol == shl->endcol)
710 {
711 if (has_mbyte && (*line)[shl->endcol] != NUL)
712 shl->endcol += (*mb_ptr2len)((*line) + shl->endcol);
713 else
714 ++shl->endcol;
715 }
716 if ((long)shl->startcol < mincol) // match at leftcol
717 {
718 shl->attr_cur = shl->attr;
719 *search_attr = shl->attr;
720 }
721 area_highlighting = TRUE;
722 }
723 if (shl != search_hl && cur != NULL)
724 cur = cur->next;
725 }
726 return area_highlighting;
727}
728
729/*
730 * For a position in a line: Check for start/end of 'hlsearch' and other
731 * matches.
732 * After end, check for start/end of next match.
733 * When another match, have to check for start again.
734 * Watch out for matching an empty string!
Bram Moolenaar0c359af2021-11-29 19:18:57 +0000735 * "on_last_col" is set to TRUE with non-zero search_attr and the next column
736 * is endcol.
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200737 * Return the updated search_attr.
738 */
739 int
740update_search_hl(
741 win_T *wp,
742 linenr_T lnum,
743 colnr_T col,
744 char_u **line,
745 match_T *search_hl,
746 int *has_match_conc UNUSED,
747 int *match_conc UNUSED,
748 int did_line_attr,
Bram Moolenaar0c359af2021-11-29 19:18:57 +0000749 int lcs_eol_one,
750 int *on_last_col)
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200751{
752 matchitem_T *cur; // points to the match list
753 match_T *shl; // points to search_hl or a match
754 int shl_flag; // flag to indicate whether search_hl
755 // has been processed or not
756 int pos_inprogress; // marks that position match search is in
757 // progress
758 int search_attr = 0;
759
760
761 // Do this for 'search_hl' and the match list (ordered by priority).
762 cur = wp->w_match_head;
763 shl_flag = WIN_IS_POPUP(wp);
764 while (cur != NULL || shl_flag == FALSE)
765 {
766 if (shl_flag == FALSE
767 && (cur == NULL
768 || cur->priority > SEARCH_HL_PRIORITY))
769 {
770 shl = search_hl;
771 shl_flag = TRUE;
772 }
773 else
774 shl = &cur->hl;
775 if (cur != NULL)
776 cur->pos.cur = 0;
777 pos_inprogress = TRUE;
778 while (shl->rm.regprog != NULL || (cur != NULL && pos_inprogress))
779 {
780 if (shl->startcol != MAXCOL
781 && col >= shl->startcol
782 && col < shl->endcol)
783 {
784 int next_col = col + mb_ptr2len(*line + col);
785
786 if (shl->endcol < next_col)
787 shl->endcol = next_col;
788 shl->attr_cur = shl->attr;
789# ifdef FEAT_CONCEAL
790 // Match with the "Conceal" group results in hiding
791 // the match.
792 if (cur != NULL
793 && shl != search_hl
794 && syn_name2id((char_u *)"Conceal") == cur->hlg_id)
795 {
796 *has_match_conc = col == shl->startcol ? 2 : 1;
797 *match_conc = cur->conceal_char;
798 }
799 else
800 *has_match_conc = 0;
801# endif
LemonBoya4399382022-04-09 21:04:08 +0100802 // Highlight the match were the cursor is using the CurSearch
803 // group.
Bram Moolenaar693ccd12022-04-16 12:04:37 +0100804 if (shl == search_hl && shl->has_cursor)
LemonBoya4399382022-04-09 21:04:08 +0100805 shl->attr_cur = HL_ATTR(HLF_LC);
LemonBoya4399382022-04-09 21:04:08 +0100806
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200807 }
808 else if (col == shl->endcol)
809 {
810 shl->attr_cur = 0;
811 next_search_hl(wp, search_hl, shl, lnum, col,
812 shl == search_hl ? NULL : cur);
813 pos_inprogress = !(cur == NULL || cur->pos.cur == 0);
814
815 // Need to get the line again, a multi-line regexp may have
816 // made it invalid.
817 *line = ml_get_buf(wp->w_buffer, lnum, FALSE);
818
819 if (shl->lnum == lnum)
820 {
821 shl->startcol = shl->rm.startpos[0].col;
822 if (shl->rm.endpos[0].lnum == 0)
823 shl->endcol = shl->rm.endpos[0].col;
824 else
825 shl->endcol = MAXCOL;
826
Bram Moolenaar9b367502022-04-22 20:07:21 +0100827 // check if the cursor is in the match
828 if (shl == search_hl)
829 check_cur_search_hl(wp, shl);
830
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200831 if (shl->startcol == shl->endcol)
832 {
833 // highlight empty match, try again after
834 // it
835 if (has_mbyte)
Bram Moolenaar41f08952021-02-22 22:13:49 +0100836 {
837 char_u *p = *line + shl->endcol;
838
839 if (*p == NUL)
840 // consistent with non-mbyte
841 ++shl->endcol;
842 else
843 shl->endcol += (*mb_ptr2len)(p);
844 }
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200845 else
846 ++shl->endcol;
847 }
848
849 // Loop to check if the match starts at the
850 // current position
851 continue;
852 }
853 }
854 break;
855 }
856 if (shl != search_hl && cur != NULL)
857 cur = cur->next;
858 }
859
860 // Use attributes from match with highest priority among 'search_hl' and
861 // the match list.
862 cur = wp->w_match_head;
863 shl_flag = WIN_IS_POPUP(wp);
864 while (cur != NULL || shl_flag == FALSE)
865 {
866 if (shl_flag == FALSE
867 && (cur == NULL ||
868 cur->priority > SEARCH_HL_PRIORITY))
869 {
870 shl = search_hl;
871 shl_flag = TRUE;
872 }
873 else
874 shl = &cur->hl;
875 if (shl->attr_cur != 0)
Bram Moolenaar0c359af2021-11-29 19:18:57 +0000876 {
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200877 search_attr = shl->attr_cur;
Bram Moolenaar0c359af2021-11-29 19:18:57 +0000878 *on_last_col = col + 1 >= shl->endcol;
879 }
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200880 if (shl != search_hl && cur != NULL)
881 cur = cur->next;
882 }
883 // Only highlight one character after the last column.
884 if (*(*line + col) == NUL && (did_line_attr >= 1
885 || (wp->w_p_list && lcs_eol_one == -1)))
886 search_attr = 0;
887 return search_attr;
888}
889
890 int
891get_prevcol_hl_flag(win_T *wp, match_T *search_hl, long curcol)
892{
893 long prevcol = curcol;
894 int prevcol_hl_flag = FALSE;
895 matchitem_T *cur; // points to the match list
896
Bram Moolenaar41f08952021-02-22 22:13:49 +0100897#if defined(FEAT_PROP_POPUP)
898 // don't do this in a popup window
899 if (popup_is_popup(wp))
900 return FALSE;
901#endif
902
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200903 // we're not really at that column when skipping some text
904 if ((long)(wp->w_p_wrap ? wp->w_skipcol : wp->w_leftcol) > prevcol)
905 ++prevcol;
906
Bram Moolenaar41f08952021-02-22 22:13:49 +0100907 // Highlight a character after the end of the line if the match started
908 // at the end of the line or when the match continues in the next line
909 // (match includes the line break).
910 if (!search_hl->is_addpos && (prevcol == (long)search_hl->startcol
911 || (prevcol > (long)search_hl->startcol
912 && search_hl->endcol == MAXCOL)))
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200913 prevcol_hl_flag = TRUE;
914 else
915 {
916 cur = wp->w_match_head;
917 while (cur != NULL)
918 {
Bram Moolenaar41f08952021-02-22 22:13:49 +0100919 if (!cur->hl.is_addpos && (prevcol == (long)cur->hl.startcol
920 || (prevcol > (long)cur->hl.startcol
921 && cur->hl.endcol == MAXCOL)))
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200922 {
923 prevcol_hl_flag = TRUE;
924 break;
925 }
926 cur = cur->next;
927 }
928 }
929 return prevcol_hl_flag;
930}
931
932/*
933 * Get highlighting for the char after the text in "char_attr" from 'hlsearch'
934 * or match highlighting.
935 */
936 void
937get_search_match_hl(win_T *wp, match_T *search_hl, long col, int *char_attr)
938{
939 matchitem_T *cur; // points to the match list
940 match_T *shl; // points to search_hl or a match
941 int shl_flag; // flag to indicate whether search_hl
942 // has been processed or not
943
944 cur = wp->w_match_head;
945 shl_flag = WIN_IS_POPUP(wp);
946 while (cur != NULL || shl_flag == FALSE)
947 {
948 if (shl_flag == FALSE
949 && ((cur != NULL
950 && cur->priority > SEARCH_HL_PRIORITY)
951 || cur == NULL))
952 {
953 shl = search_hl;
954 shl_flag = TRUE;
955 }
956 else
957 shl = &cur->hl;
958 if (col - 1 == (long)shl->startcol
959 && (shl == search_hl || !shl->is_addpos))
960 *char_attr = shl->attr;
961 if (shl != search_hl && cur != NULL)
962 cur = cur->next;
963 }
964}
965
966#endif // FEAT_SEARCH_EXTRA
967
968#if defined(FEAT_EVAL) || defined(PROTO)
969# ifdef FEAT_SEARCH_EXTRA
970 static int
971matchadd_dict_arg(typval_T *tv, char_u **conceal_char, win_T **win)
972{
973 dictitem_T *di;
974
975 if (tv->v_type != VAR_DICT)
976 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +0000977 emsg(_(e_dictionary_required));
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200978 return FAIL;
979 }
980
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +0100981 if (dict_has_key(tv->vval.v_dict, "conceal"))
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200982 *conceal_char = dict_get_string(tv->vval.v_dict,
983 (char_u *)"conceal", FALSE);
984
985 if ((di = dict_find(tv->vval.v_dict, (char_u *)"window", -1)) != NULL)
986 {
987 *win = find_win_by_nr_or_id(&di->di_tv);
988 if (*win == NULL)
989 {
Bram Moolenaar3a846e62022-01-01 16:21:00 +0000990 emsg(_(e_invalid_window_number));
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200991 return FAIL;
992 }
993 }
994
995 return OK;
996}
997#endif
998
999/*
1000 * "clearmatches()" function
1001 */
1002 void
1003f_clearmatches(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
1004{
1005#ifdef FEAT_SEARCH_EXTRA
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001006 win_T *win;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001007
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001008 if (in_vim9script() && check_for_opt_number_arg(argvars, 0) == FAIL)
1009 return;
1010
1011 win = get_optional_window(argvars, 0);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001012 if (win != NULL)
1013 clear_matches(win);
1014#endif
1015}
1016
1017/*
1018 * "getmatches()" function
1019 */
1020 void
1021f_getmatches(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
1022{
1023# ifdef FEAT_SEARCH_EXTRA
1024 dict_T *dict;
1025 matchitem_T *cur;
1026 int i;
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001027 win_T *win;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001028
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001029 if (in_vim9script() && check_for_opt_number_arg(argvars, 0) == FAIL)
1030 return;
1031
1032 win = get_optional_window(argvars, 0);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001033 if (rettv_list_alloc(rettv) == FAIL || win == NULL)
1034 return;
1035
1036 cur = win->w_match_head;
1037 while (cur != NULL)
1038 {
1039 dict = dict_alloc();
1040 if (dict == NULL)
1041 return;
1042 if (cur->match.regprog == NULL)
1043 {
1044 // match added with matchaddpos()
1045 for (i = 0; i < MAXPOSMATCH; ++i)
1046 {
1047 llpos_T *llpos;
1048 char buf[30]; // use 30 to avoid compiler warning
1049 list_T *l;
1050
1051 llpos = &cur->pos.pos[i];
1052 if (llpos->lnum == 0)
1053 break;
1054 l = list_alloc();
1055 if (l == NULL)
1056 break;
1057 list_append_number(l, (varnumber_T)llpos->lnum);
1058 if (llpos->col > 0)
1059 {
1060 list_append_number(l, (varnumber_T)llpos->col);
1061 list_append_number(l, (varnumber_T)llpos->len);
1062 }
1063 sprintf(buf, "pos%d", i + 1);
1064 dict_add_list(dict, buf, l);
1065 }
1066 }
1067 else
1068 {
1069 dict_add_string(dict, "pattern", cur->pattern);
1070 }
1071 dict_add_string(dict, "group", syn_id2name(cur->hlg_id));
1072 dict_add_number(dict, "priority", (long)cur->priority);
1073 dict_add_number(dict, "id", (long)cur->id);
1074# if defined(FEAT_CONCEAL)
1075 if (cur->conceal_char)
1076 {
1077 char_u buf[MB_MAXBYTES + 1];
1078
=?UTF-8?q?Dundar=20G=C3=B6c?=420fabc2022-01-28 15:28:04 +00001079 buf[(*mb_char2bytes)(cur->conceal_char, buf)] = NUL;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001080 dict_add_string(dict, "conceal", (char_u *)&buf);
1081 }
1082# endif
1083 list_append_dict(rettv->vval.v_list, dict);
1084 cur = cur->next;
1085 }
1086# endif
1087}
1088
1089/*
1090 * "setmatches()" function
1091 */
1092 void
1093f_setmatches(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
1094{
1095#ifdef FEAT_SEARCH_EXTRA
1096 list_T *l;
1097 listitem_T *li;
1098 dict_T *d;
1099 list_T *s = NULL;
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02001100 win_T *win;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001101
1102 rettv->vval.v_number = -1;
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02001103
1104 if (in_vim9script()
1105 && (check_for_list_arg(argvars, 0) == FAIL
1106 || check_for_opt_number_arg(argvars, 1) == FAIL))
1107 return;
1108
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001109 if (argvars[0].v_type != VAR_LIST)
1110 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00001111 emsg(_(e_list_required));
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001112 return;
1113 }
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02001114 win = get_optional_window(argvars, 1);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001115 if (win == NULL)
1116 return;
1117
1118 if ((l = argvars[0].vval.v_list) != NULL)
1119 {
1120 // To some extent make sure that we are dealing with a list from
1121 // "getmatches()".
1122 li = l->lv_first;
1123 while (li != NULL)
1124 {
1125 if (li->li_tv.v_type != VAR_DICT
1126 || (d = li->li_tv.vval.v_dict) == NULL)
1127 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001128 emsg(_(e_invalid_argument));
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001129 return;
1130 }
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01001131 if (!(dict_has_key(d, "group")
1132 && (dict_has_key(d, "pattern")
1133 || dict_has_key(d, "pos1"))
1134 && dict_has_key(d, "priority")
1135 && dict_has_key(d, "id")))
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001136 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001137 emsg(_(e_invalid_argument));
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001138 return;
1139 }
1140 li = li->li_next;
1141 }
1142
1143 clear_matches(win);
1144 li = l->lv_first;
1145 while (li != NULL)
1146 {
1147 int i = 0;
1148 char buf[30]; // use 30 to avoid compiler warning
1149 dictitem_T *di;
1150 char_u *group;
1151 int priority;
1152 int id;
1153 char_u *conceal;
1154
1155 d = li->li_tv.vval.v_dict;
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01001156 if (!dict_has_key(d, "pattern"))
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001157 {
1158 if (s == NULL)
1159 {
1160 s = list_alloc();
1161 if (s == NULL)
1162 return;
1163 }
1164
1165 // match from matchaddpos()
1166 for (i = 1; i < 9; i++)
1167 {
1168 sprintf((char *)buf, (char *)"pos%d", i);
1169 if ((di = dict_find(d, (char_u *)buf, -1)) != NULL)
1170 {
1171 if (di->di_tv.v_type != VAR_LIST)
1172 return;
1173
1174 list_append_tv(s, &di->di_tv);
1175 s->lv_refcount++;
1176 }
1177 else
1178 break;
1179 }
1180 }
1181
1182 group = dict_get_string(d, (char_u *)"group", TRUE);
1183 priority = (int)dict_get_number(d, (char_u *)"priority");
1184 id = (int)dict_get_number(d, (char_u *)"id");
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01001185 conceal = dict_has_key(d, "conceal")
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001186 ? dict_get_string(d, (char_u *)"conceal", TRUE)
1187 : NULL;
1188 if (i == 0)
1189 {
1190 match_add(win, group,
1191 dict_get_string(d, (char_u *)"pattern", FALSE),
1192 priority, id, NULL, conceal);
1193 }
1194 else
1195 {
1196 match_add(win, group, NULL, priority, id, s, conceal);
1197 list_unref(s);
1198 s = NULL;
1199 }
1200 vim_free(group);
1201 vim_free(conceal);
1202
1203 li = li->li_next;
1204 }
1205 rettv->vval.v_number = 0;
1206 }
1207#endif
1208}
1209
1210/*
1211 * "matchadd()" function
1212 */
1213 void
1214f_matchadd(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
1215{
1216# ifdef FEAT_SEARCH_EXTRA
1217 char_u buf[NUMBUFLEN];
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02001218 char_u *grp; // group
1219 char_u *pat; // pattern
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001220 int prio = 10; // default priority
1221 int id = -1;
1222 int error = FALSE;
1223 char_u *conceal_char = NULL;
1224 win_T *win = curwin;
1225
1226 rettv->vval.v_number = -1;
1227
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02001228 if (in_vim9script()
1229 && (check_for_string_arg(argvars, 0) == FAIL
1230 || check_for_string_arg(argvars, 1) == FAIL
1231 || check_for_opt_number_arg(argvars, 2) == FAIL
1232 || (argvars[2].v_type != VAR_UNKNOWN
1233 && (check_for_opt_number_arg(argvars, 3) == FAIL
1234 || (argvars[3].v_type != VAR_UNKNOWN
1235 && check_for_opt_dict_arg(argvars, 4) == FAIL)))))
1236 return;
1237
1238 grp = tv_get_string_buf_chk(&argvars[0], buf); // group
1239 pat = tv_get_string_buf_chk(&argvars[1], buf); // pattern
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001240 if (grp == NULL || pat == NULL)
1241 return;
1242 if (argvars[2].v_type != VAR_UNKNOWN)
1243 {
1244 prio = (int)tv_get_number_chk(&argvars[2], &error);
1245 if (argvars[3].v_type != VAR_UNKNOWN)
1246 {
1247 id = (int)tv_get_number_chk(&argvars[3], &error);
1248 if (argvars[4].v_type != VAR_UNKNOWN
1249 && matchadd_dict_arg(&argvars[4], &conceal_char, &win) == FAIL)
1250 return;
1251 }
1252 }
1253 if (error == TRUE)
1254 return;
1255 if (id >= 1 && id <= 3)
1256 {
Bram Moolenaar677658a2022-01-05 16:09:06 +00001257 semsg(_(e_id_is_reserved_for_match_nr), id);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001258 return;
1259 }
1260
1261 rettv->vval.v_number = match_add(win, grp, pat, prio, id, NULL,
1262 conceal_char);
1263# endif
1264}
1265
1266/*
1267 * "matchaddpos()" function
1268 */
1269 void
1270f_matchaddpos(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
1271{
1272# ifdef FEAT_SEARCH_EXTRA
1273 char_u buf[NUMBUFLEN];
1274 char_u *group;
1275 int prio = 10;
1276 int id = -1;
1277 int error = FALSE;
1278 list_T *l;
1279 char_u *conceal_char = NULL;
1280 win_T *win = curwin;
1281
1282 rettv->vval.v_number = -1;
1283
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02001284 if (in_vim9script()
1285 && (check_for_string_arg(argvars, 0) == FAIL
1286 || check_for_list_arg(argvars, 1) == FAIL
1287 || check_for_opt_number_arg(argvars, 2) == FAIL
1288 || (argvars[2].v_type != VAR_UNKNOWN
1289 && (check_for_opt_number_arg(argvars, 3) == FAIL
1290 || (argvars[3].v_type != VAR_UNKNOWN
1291 && check_for_opt_dict_arg(argvars, 4) == FAIL)))))
1292 return;
1293
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001294 group = tv_get_string_buf_chk(&argvars[0], buf);
1295 if (group == NULL)
1296 return;
1297
1298 if (argvars[1].v_type != VAR_LIST)
1299 {
Bram Moolenaar3a846e62022-01-01 16:21:00 +00001300 semsg(_(e_argument_of_str_must_be_list), "matchaddpos()");
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001301 return;
1302 }
1303 l = argvars[1].vval.v_list;
1304 if (l == NULL)
1305 return;
1306
1307 if (argvars[2].v_type != VAR_UNKNOWN)
1308 {
1309 prio = (int)tv_get_number_chk(&argvars[2], &error);
1310 if (argvars[3].v_type != VAR_UNKNOWN)
1311 {
1312 id = (int)tv_get_number_chk(&argvars[3], &error);
1313
1314 if (argvars[4].v_type != VAR_UNKNOWN
1315 && matchadd_dict_arg(&argvars[4], &conceal_char, &win) == FAIL)
1316 return;
1317 }
1318 }
1319 if (error == TRUE)
1320 return;
1321
1322 // id == 3 is ok because matchaddpos() is supposed to substitute :3match
1323 if (id == 1 || id == 2)
1324 {
Bram Moolenaar677658a2022-01-05 16:09:06 +00001325 semsg(_(e_id_is_reserved_for_match_nr), id);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001326 return;
1327 }
1328
1329 rettv->vval.v_number = match_add(win, group, NULL, prio, id, l,
1330 conceal_char);
1331# endif
1332}
1333
1334/*
1335 * "matcharg()" function
1336 */
1337 void
1338f_matcharg(typval_T *argvars UNUSED, typval_T *rettv)
1339{
1340 if (rettv_list_alloc(rettv) == OK)
1341 {
1342# ifdef FEAT_SEARCH_EXTRA
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001343 int id;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001344 matchitem_T *m;
1345
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001346 if (in_vim9script() && check_for_number_arg(argvars, 0) == FAIL)
1347 return;
1348
1349 id = (int)tv_get_number(&argvars[0]);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001350 if (id >= 1 && id <= 3)
1351 {
=?UTF-8?q?Dundar=20G=C3=B6c?=420fabc2022-01-28 15:28:04 +00001352 if ((m = get_match(curwin, id)) != NULL)
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001353 {
1354 list_append_string(rettv->vval.v_list,
1355 syn_id2name(m->hlg_id), -1);
1356 list_append_string(rettv->vval.v_list, m->pattern, -1);
1357 }
1358 else
1359 {
1360 list_append_string(rettv->vval.v_list, NULL, -1);
1361 list_append_string(rettv->vval.v_list, NULL, -1);
1362 }
1363 }
1364# endif
1365 }
1366}
1367
1368/*
1369 * "matchdelete()" function
1370 */
1371 void
1372f_matchdelete(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
1373{
1374# ifdef FEAT_SEARCH_EXTRA
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001375 win_T *win;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001376
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001377 if (in_vim9script()
1378 && (check_for_number_arg(argvars, 0) == FAIL
1379 || check_for_opt_number_arg(argvars, 1) == FAIL))
1380 return;
1381
1382 win = get_optional_window(argvars, 1);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001383 if (win == NULL)
1384 rettv->vval.v_number = -1;
1385 else
1386 rettv->vval.v_number = match_delete(win,
1387 (int)tv_get_number(&argvars[0]), TRUE);
1388# endif
1389}
1390#endif
1391
1392#if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
1393/*
1394 * ":[N]match {group} {pattern}"
1395 * Sets nextcmd to the start of the next command, if any. Also called when
1396 * skipping commands to find the next command.
1397 */
1398 void
1399ex_match(exarg_T *eap)
1400{
1401 char_u *p;
1402 char_u *g = NULL;
1403 char_u *end;
1404 int c;
1405 int id;
1406
1407 if (eap->line2 <= 3)
1408 id = eap->line2;
1409 else
1410 {
Bram Moolenaar451c2e32020-08-15 16:33:28 +02001411 emsg(_(e_invalid_command));
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001412 return;
1413 }
1414
1415 // First clear any old pattern.
1416 if (!eap->skip)
1417 match_delete(curwin, id, FALSE);
1418
1419 if (ends_excmd2(eap->cmd, eap->arg))
1420 end = eap->arg;
1421 else if ((STRNICMP(eap->arg, "none", 4) == 0
1422 && (VIM_ISWHITE(eap->arg[4])
1423 || ends_excmd2(eap->arg, eap->arg + 4))))
1424 end = eap->arg + 4;
1425 else
1426 {
1427 p = skiptowhite(eap->arg);
1428 if (!eap->skip)
1429 g = vim_strnsave(eap->arg, p - eap->arg);
1430 p = skipwhite(p);
1431 if (*p == NUL)
1432 {
1433 // There must be two arguments.
1434 vim_free(g);
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001435 semsg(_(e_invalid_argument_str), eap->arg);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001436 return;
1437 }
1438 end = skip_regexp(p + 1, *p, TRUE);
1439 if (!eap->skip)
1440 {
1441 if (*end != NUL && !ends_excmd2(end, skipwhite(end + 1)))
1442 {
1443 vim_free(g);
Bram Moolenaar74409f62022-01-01 15:58:22 +00001444 eap->errmsg = ex_errmsg(e_trailing_characters_str, end);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001445 return;
1446 }
1447 if (*end != *p)
1448 {
1449 vim_free(g);
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001450 semsg(_(e_invalid_argument_str), p);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001451 return;
1452 }
1453
1454 c = *end;
1455 *end = NUL;
1456 match_add(curwin, g, p + 1, 10, id, NULL, NULL);
1457 vim_free(g);
1458 *end = c;
1459 }
1460 }
1461 eap->nextcmd = find_nextcmd(end);
1462}
1463#endif