blob: ebdaeabcfff5d9fb180cf694404ab48213c53ee6 [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;
Bram Moolenaara4d158b2022-08-14 14:17:45 +010042 int rtype = UPD_SOME_VALID;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +020043
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;
Bram Moolenaara4d158b2022-08-14 14:17:45 +0100195 rtype = UPD_VALID;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200196 }
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;
Bram Moolenaara4d158b2022-08-14 14:17:45 +0100231 int rtype = UPD_SOME_VALID;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200232
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 }
Bram Moolenaara4d158b2022-08-14 14:17:45 +0100272 rtype = UPD_VALID;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200273 }
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 }
Bram Moolenaara4d158b2022-08-14 14:17:45 +0100295 redraw_win_later(wp, UPD_SOME_VALID);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200296}
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;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200333 cur = cur->next;
334 }
335 search_hl->buf = wp->w_buffer;
336 search_hl->lnum = 0;
337 search_hl->first_lnum = 0;
338 // time limit is set at the toplevel, for all windows
339}
340
341/*
342 * If there is a match fill "shl" and return one.
343 * Return zero otherwise.
344 */
345 static int
346next_search_hl_pos(
347 match_T *shl, // points to a match
348 linenr_T lnum,
349 posmatch_T *posmatch, // match positions
350 colnr_T mincol) // minimal column for a match
351{
352 int i;
353 int found = -1;
354
355 for (i = posmatch->cur; i < MAXPOSMATCH; i++)
356 {
357 llpos_T *pos = &posmatch->pos[i];
358
359 if (pos->lnum == 0)
360 break;
361 if (pos->len == 0 && pos->col < mincol)
362 continue;
363 if (pos->lnum == lnum)
364 {
365 if (found >= 0)
366 {
367 // if this match comes before the one at "found" then swap
368 // them
369 if (pos->col < posmatch->pos[found].col)
370 {
371 llpos_T tmp = *pos;
372
373 *pos = posmatch->pos[found];
374 posmatch->pos[found] = tmp;
375 }
376 }
377 else
378 found = i;
379 }
380 }
381 posmatch->cur = 0;
382 if (found >= 0)
383 {
384 colnr_T start = posmatch->pos[found].col == 0
385 ? 0 : posmatch->pos[found].col - 1;
386 colnr_T end = posmatch->pos[found].col == 0
387 ? MAXCOL : start + posmatch->pos[found].len;
388
389 shl->lnum = lnum;
390 shl->rm.startpos[0].lnum = 0;
391 shl->rm.startpos[0].col = start;
392 shl->rm.endpos[0].lnum = 0;
393 shl->rm.endpos[0].col = end;
394 shl->is_addpos = TRUE;
Bram Moolenaar693ccd12022-04-16 12:04:37 +0100395 shl->has_cursor = FALSE;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200396 posmatch->cur = found + 1;
397 return 1;
398 }
399 return 0;
400}
401
402/*
403 * Search for a next 'hlsearch' or match.
404 * Uses shl->buf.
405 * Sets shl->lnum and shl->rm contents.
406 * Note: Assumes a previous match is always before "lnum", unless
407 * shl->lnum is zero.
408 * Careful: Any pointers for buffer lines will become invalid.
409 */
410 static void
411next_search_hl(
412 win_T *win,
413 match_T *search_hl,
414 match_T *shl, // points to search_hl or a match
415 linenr_T lnum,
416 colnr_T mincol, // minimal column for a match
417 matchitem_T *cur) // to retrieve match positions if any
418{
419 linenr_T l;
420 colnr_T matchcol;
421 long nmatched;
422 int called_emsg_before = called_emsg;
Paul Ollis65745772022-06-05 16:55:54 +0100423 int timed_out = FALSE;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200424
425 // for :{range}s/pat only highlight inside the range
Bram Moolenaar94fb8272021-12-29 19:22:44 +0000426 if ((lnum < search_first_line || lnum > search_last_line) && cur == NULL)
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200427 {
428 shl->lnum = 0;
429 return;
430 }
431
432 if (shl->lnum != 0)
433 {
434 // Check for three situations:
435 // 1. If the "lnum" is below a previous match, start a new search.
436 // 2. If the previous match includes "mincol", use it.
437 // 3. Continue after the previous match.
438 l = shl->lnum + shl->rm.endpos[0].lnum - shl->rm.startpos[0].lnum;
439 if (lnum > l)
440 shl->lnum = 0;
441 else if (lnum < l || shl->rm.endpos[0].col > mincol)
442 return;
443 }
444
445 // Repeat searching for a match until one is found that includes "mincol"
446 // or none is found in this line.
447 for (;;)
448 {
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200449 // Three situations:
450 // 1. No useful previous match: search from start of line.
451 // 2. Not Vi compatible or empty match: continue at next character.
452 // Break the loop if this is beyond the end of the line.
453 // 3. Vi compatible searching: continue at end of previous match.
454 if (shl->lnum == 0)
455 matchcol = 0;
456 else if (vim_strchr(p_cpo, CPO_SEARCH) == NULL
457 || (shl->rm.endpos[0].lnum == 0
458 && shl->rm.endpos[0].col <= shl->rm.startpos[0].col))
459 {
460 char_u *ml;
461
462 matchcol = shl->rm.startpos[0].col;
463 ml = ml_get_buf(shl->buf, lnum, FALSE) + matchcol;
464 if (*ml == NUL)
465 {
466 ++matchcol;
467 shl->lnum = 0;
468 break;
469 }
470 if (has_mbyte)
471 matchcol += mb_ptr2len(ml);
472 else
473 ++matchcol;
474 }
475 else
476 matchcol = shl->rm.endpos[0].col;
477
478 shl->lnum = lnum;
479 if (shl->rm.regprog != NULL)
480 {
481 // Remember whether shl->rm is using a copy of the regprog in
482 // cur->match.
483 int regprog_is_copy = (shl != search_hl && cur != NULL
484 && shl == &cur->hl
485 && cur->match.regprog == cur->hl.rm.regprog);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200486
487 nmatched = vim_regexec_multi(&shl->rm, win, shl->buf, lnum,
Paul Ollis65745772022-06-05 16:55:54 +0100488 matchcol, &timed_out);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200489 // Copy the regprog, in case it got freed and recompiled.
490 if (regprog_is_copy)
491 cur->match.regprog = cur->hl.rm.regprog;
492
493 if (called_emsg > called_emsg_before || got_int || timed_out)
494 {
495 // Error while handling regexp: stop using this regexp.
496 if (shl == search_hl)
497 {
498 // don't free regprog in the match list, it's a copy
499 vim_regfree(shl->rm.regprog);
500 set_no_hlsearch(TRUE);
501 }
502 shl->rm.regprog = NULL;
503 shl->lnum = 0;
504 got_int = FALSE; // avoid the "Type :quit to exit Vim" message
505 break;
506 }
507 }
508 else if (cur != NULL)
509 nmatched = next_search_hl_pos(shl, lnum, &(cur->pos), matchcol);
510 else
511 nmatched = 0;
512 if (nmatched == 0)
513 {
514 shl->lnum = 0; // no match found
515 break;
516 }
517 if (shl->rm.startpos[0].lnum > 0
518 || shl->rm.startpos[0].col >= mincol
519 || nmatched > 1
520 || shl->rm.endpos[0].col > mincol)
521 {
522 shl->lnum += shl->rm.startpos[0].lnum;
523 break; // useful match found
524 }
525 }
526}
527
528/*
529 * Advance to the match in window "wp" line "lnum" or past it.
530 */
531 void
532prepare_search_hl(win_T *wp, match_T *search_hl, linenr_T lnum)
533{
534 matchitem_T *cur; // points to the match list
535 match_T *shl; // points to search_hl or a match
536 int shl_flag; // flag to indicate whether search_hl
537 // has been processed or not
538 int pos_inprogress; // marks that position match search is
539 // in progress
540 int n;
541
542 // When using a multi-line pattern, start searching at the top
543 // of the window or just after a closed fold.
544 // Do this both for search_hl and the match list.
545 cur = wp->w_match_head;
546 shl_flag = WIN_IS_POPUP(wp); // skip search_hl in a popup window
547 while (cur != NULL || shl_flag == FALSE)
548 {
549 if (shl_flag == FALSE)
550 {
551 shl = search_hl;
552 shl_flag = TRUE;
553 }
554 else
555 shl = &cur->hl;
556 if (shl->rm.regprog != NULL
557 && shl->lnum == 0
558 && re_multiline(shl->rm.regprog))
559 {
560 if (shl->first_lnum == 0)
561 {
562# ifdef FEAT_FOLDING
563 for (shl->first_lnum = lnum;
564 shl->first_lnum > wp->w_topline; --shl->first_lnum)
565 if (hasFoldingWin(wp, shl->first_lnum - 1,
566 NULL, NULL, TRUE, NULL))
567 break;
568# else
569 shl->first_lnum = wp->w_topline;
570# endif
571 }
572 if (cur != NULL)
573 cur->pos.cur = 0;
574 pos_inprogress = TRUE;
575 n = 0;
576 while (shl->first_lnum < lnum && (shl->rm.regprog != NULL
577 || (cur != NULL && pos_inprogress)))
578 {
579 next_search_hl(wp, search_hl, shl, shl->first_lnum, (colnr_T)n,
580 shl == search_hl ? NULL : cur);
581 pos_inprogress = cur == NULL || cur->pos.cur == 0
582 ? FALSE : TRUE;
583 if (shl->lnum != 0)
584 {
585 shl->first_lnum = shl->lnum
586 + shl->rm.endpos[0].lnum
587 - shl->rm.startpos[0].lnum;
588 n = shl->rm.endpos[0].col;
589 }
590 else
591 {
592 ++shl->first_lnum;
593 n = 0;
594 }
595 }
596 }
597 if (shl != search_hl && cur != NULL)
598 cur = cur->next;
599 }
600}
601
602/*
Bram Moolenaar9b367502022-04-22 20:07:21 +0100603 * Update "shl->has_cursor" based on the match in "shl" and the cursor
604 * position.
605 */
606 static void
607check_cur_search_hl(win_T *wp, match_T *shl)
608{
zeertzjq8279cfe2022-04-23 12:05:51 +0100609 linenr_T linecount = shl->rm.endpos[0].lnum - shl->rm.startpos[0].lnum;
Bram Moolenaar9b367502022-04-22 20:07:21 +0100610
611 if (wp->w_cursor.lnum >= shl->lnum
zeertzjq8279cfe2022-04-23 12:05:51 +0100612 && wp->w_cursor.lnum <= shl->lnum + linecount
Bram Moolenaar9b367502022-04-22 20:07:21 +0100613 && (wp->w_cursor.lnum > shl->lnum
614 || wp->w_cursor.col >= shl->rm.startpos[0].col)
615 && (wp->w_cursor.lnum < shl->lnum + linecount
616 || wp->w_cursor.col < shl->rm.endpos[0].col))
617 shl->has_cursor = TRUE;
618 else
619 shl->has_cursor = FALSE;
620}
621
622/*
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200623 * Prepare for 'hlsearch' and match highlighting in one window line.
624 * Return TRUE if there is such highlighting and set "search_attr" to the
625 * current highlight attribute.
626 */
627 int
628prepare_search_hl_line(
629 win_T *wp,
630 linenr_T lnum,
631 colnr_T mincol,
632 char_u **line,
633 match_T *search_hl,
634 int *search_attr)
635{
636 matchitem_T *cur; // points to the match list
637 match_T *shl; // points to search_hl or a match
638 int shl_flag; // flag to indicate whether search_hl
639 // has been processed or not
640 int area_highlighting = FALSE;
641
642 // Handle highlighting the last used search pattern and matches.
643 // Do this for both search_hl and the match list.
644 // Do not use search_hl in a popup window.
645 cur = wp->w_match_head;
646 shl_flag = WIN_IS_POPUP(wp);
647 while (cur != NULL || shl_flag == FALSE)
648 {
649 if (shl_flag == FALSE)
650 {
651 shl = search_hl;
652 shl_flag = TRUE;
653 }
654 else
655 shl = &cur->hl;
656 shl->startcol = MAXCOL;
657 shl->endcol = MAXCOL;
658 shl->attr_cur = 0;
659 shl->is_addpos = FALSE;
Bram Moolenaar693ccd12022-04-16 12:04:37 +0100660 shl->has_cursor = FALSE;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200661 if (cur != NULL)
662 cur->pos.cur = 0;
663 next_search_hl(wp, search_hl, shl, lnum, mincol,
664 shl == search_hl ? NULL : cur);
665
666 // Need to get the line again, a multi-line regexp may have made it
667 // invalid.
668 *line = ml_get_buf(wp->w_buffer, lnum, FALSE);
669
670 if (shl->lnum != 0 && shl->lnum <= lnum)
671 {
672 if (shl->lnum == lnum)
673 shl->startcol = shl->rm.startpos[0].col;
674 else
675 shl->startcol = 0;
676 if (lnum == shl->lnum + shl->rm.endpos[0].lnum
677 - shl->rm.startpos[0].lnum)
678 shl->endcol = shl->rm.endpos[0].col;
679 else
680 shl->endcol = MAXCOL;
Bram Moolenaar693ccd12022-04-16 12:04:37 +0100681
682 // check if the cursor is in the match before changing the columns
Bram Moolenaar9b367502022-04-22 20:07:21 +0100683 if (shl == search_hl)
684 check_cur_search_hl(wp, shl);
Bram Moolenaar693ccd12022-04-16 12:04:37 +0100685
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200686 // Highlight one character for an empty match.
687 if (shl->startcol == shl->endcol)
688 {
689 if (has_mbyte && (*line)[shl->endcol] != NUL)
690 shl->endcol += (*mb_ptr2len)((*line) + shl->endcol);
691 else
692 ++shl->endcol;
693 }
694 if ((long)shl->startcol < mincol) // match at leftcol
695 {
696 shl->attr_cur = shl->attr;
697 *search_attr = shl->attr;
698 }
699 area_highlighting = TRUE;
700 }
701 if (shl != search_hl && cur != NULL)
702 cur = cur->next;
703 }
704 return area_highlighting;
705}
706
707/*
708 * For a position in a line: Check for start/end of 'hlsearch' and other
709 * matches.
710 * After end, check for start/end of next match.
711 * When another match, have to check for start again.
712 * Watch out for matching an empty string!
Bram Moolenaar0c359af2021-11-29 19:18:57 +0000713 * "on_last_col" is set to TRUE with non-zero search_attr and the next column
714 * is endcol.
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200715 * Return the updated search_attr.
716 */
717 int
718update_search_hl(
719 win_T *wp,
720 linenr_T lnum,
721 colnr_T col,
722 char_u **line,
723 match_T *search_hl,
724 int *has_match_conc UNUSED,
725 int *match_conc UNUSED,
726 int did_line_attr,
Bram Moolenaar0c359af2021-11-29 19:18:57 +0000727 int lcs_eol_one,
728 int *on_last_col)
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200729{
730 matchitem_T *cur; // points to the match list
731 match_T *shl; // points to search_hl or a match
732 int shl_flag; // flag to indicate whether search_hl
733 // has been processed or not
734 int pos_inprogress; // marks that position match search is in
735 // progress
736 int search_attr = 0;
737
738
739 // Do this for 'search_hl' and the match list (ordered by priority).
740 cur = wp->w_match_head;
741 shl_flag = WIN_IS_POPUP(wp);
742 while (cur != NULL || shl_flag == FALSE)
743 {
744 if (shl_flag == FALSE
745 && (cur == NULL
746 || cur->priority > SEARCH_HL_PRIORITY))
747 {
748 shl = search_hl;
749 shl_flag = TRUE;
750 }
751 else
752 shl = &cur->hl;
753 if (cur != NULL)
754 cur->pos.cur = 0;
755 pos_inprogress = TRUE;
756 while (shl->rm.regprog != NULL || (cur != NULL && pos_inprogress))
757 {
758 if (shl->startcol != MAXCOL
759 && col >= shl->startcol
760 && col < shl->endcol)
761 {
762 int next_col = col + mb_ptr2len(*line + col);
763
764 if (shl->endcol < next_col)
765 shl->endcol = next_col;
766 shl->attr_cur = shl->attr;
767# ifdef FEAT_CONCEAL
768 // Match with the "Conceal" group results in hiding
769 // the match.
770 if (cur != NULL
771 && shl != search_hl
772 && syn_name2id((char_u *)"Conceal") == cur->hlg_id)
773 {
774 *has_match_conc = col == shl->startcol ? 2 : 1;
775 *match_conc = cur->conceal_char;
776 }
777 else
778 *has_match_conc = 0;
779# endif
LemonBoya4399382022-04-09 21:04:08 +0100780 // Highlight the match were the cursor is using the CurSearch
781 // group.
Bram Moolenaar693ccd12022-04-16 12:04:37 +0100782 if (shl == search_hl && shl->has_cursor)
Bram Moolenaar368137a2022-05-31 13:43:12 +0100783 {
LemonBoya4399382022-04-09 21:04:08 +0100784 shl->attr_cur = HL_ATTR(HLF_LC);
Bram Moolenaar368137a2022-05-31 13:43:12 +0100785 if (shl->attr_cur != shl->attr)
786 search_hl_has_cursor_lnum = lnum;
787 }
LemonBoya4399382022-04-09 21:04:08 +0100788
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200789 }
790 else if (col == shl->endcol)
791 {
792 shl->attr_cur = 0;
793 next_search_hl(wp, search_hl, shl, lnum, col,
794 shl == search_hl ? NULL : cur);
795 pos_inprogress = !(cur == NULL || cur->pos.cur == 0);
796
797 // Need to get the line again, a multi-line regexp may have
798 // made it invalid.
799 *line = ml_get_buf(wp->w_buffer, lnum, FALSE);
800
801 if (shl->lnum == lnum)
802 {
803 shl->startcol = shl->rm.startpos[0].col;
804 if (shl->rm.endpos[0].lnum == 0)
805 shl->endcol = shl->rm.endpos[0].col;
806 else
807 shl->endcol = MAXCOL;
808
Bram Moolenaar9b367502022-04-22 20:07:21 +0100809 // check if the cursor is in the match
810 if (shl == search_hl)
811 check_cur_search_hl(wp, shl);
812
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200813 if (shl->startcol == shl->endcol)
814 {
815 // highlight empty match, try again after
816 // it
817 if (has_mbyte)
Bram Moolenaar41f08952021-02-22 22:13:49 +0100818 {
819 char_u *p = *line + shl->endcol;
820
821 if (*p == NUL)
822 // consistent with non-mbyte
823 ++shl->endcol;
824 else
825 shl->endcol += (*mb_ptr2len)(p);
826 }
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200827 else
828 ++shl->endcol;
829 }
830
831 // Loop to check if the match starts at the
832 // current position
833 continue;
834 }
835 }
836 break;
837 }
838 if (shl != search_hl && cur != NULL)
839 cur = cur->next;
840 }
841
842 // Use attributes from match with highest priority among 'search_hl' and
843 // the match list.
844 cur = wp->w_match_head;
845 shl_flag = WIN_IS_POPUP(wp);
846 while (cur != NULL || shl_flag == FALSE)
847 {
848 if (shl_flag == FALSE
849 && (cur == NULL ||
850 cur->priority > SEARCH_HL_PRIORITY))
851 {
852 shl = search_hl;
853 shl_flag = TRUE;
854 }
855 else
856 shl = &cur->hl;
857 if (shl->attr_cur != 0)
Bram Moolenaar0c359af2021-11-29 19:18:57 +0000858 {
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200859 search_attr = shl->attr_cur;
Bram Moolenaar0c359af2021-11-29 19:18:57 +0000860 *on_last_col = col + 1 >= shl->endcol;
861 }
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200862 if (shl != search_hl && cur != NULL)
863 cur = cur->next;
864 }
865 // Only highlight one character after the last column.
866 if (*(*line + col) == NUL && (did_line_attr >= 1
867 || (wp->w_p_list && lcs_eol_one == -1)))
868 search_attr = 0;
869 return search_attr;
870}
871
872 int
873get_prevcol_hl_flag(win_T *wp, match_T *search_hl, long curcol)
874{
875 long prevcol = curcol;
876 int prevcol_hl_flag = FALSE;
877 matchitem_T *cur; // points to the match list
878
Bram Moolenaar41f08952021-02-22 22:13:49 +0100879#if defined(FEAT_PROP_POPUP)
880 // don't do this in a popup window
881 if (popup_is_popup(wp))
882 return FALSE;
883#endif
884
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200885 // we're not really at that column when skipping some text
886 if ((long)(wp->w_p_wrap ? wp->w_skipcol : wp->w_leftcol) > prevcol)
887 ++prevcol;
888
Bram Moolenaar41f08952021-02-22 22:13:49 +0100889 // Highlight a character after the end of the line if the match started
890 // at the end of the line or when the match continues in the next line
891 // (match includes the line break).
892 if (!search_hl->is_addpos && (prevcol == (long)search_hl->startcol
893 || (prevcol > (long)search_hl->startcol
894 && search_hl->endcol == MAXCOL)))
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200895 prevcol_hl_flag = TRUE;
896 else
897 {
898 cur = wp->w_match_head;
899 while (cur != NULL)
900 {
Bram Moolenaar41f08952021-02-22 22:13:49 +0100901 if (!cur->hl.is_addpos && (prevcol == (long)cur->hl.startcol
902 || (prevcol > (long)cur->hl.startcol
903 && cur->hl.endcol == MAXCOL)))
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200904 {
905 prevcol_hl_flag = TRUE;
906 break;
907 }
908 cur = cur->next;
909 }
910 }
911 return prevcol_hl_flag;
912}
913
914/*
915 * Get highlighting for the char after the text in "char_attr" from 'hlsearch'
916 * or match highlighting.
917 */
918 void
919get_search_match_hl(win_T *wp, match_T *search_hl, long col, int *char_attr)
920{
921 matchitem_T *cur; // points to the match list
922 match_T *shl; // points to search_hl or a match
923 int shl_flag; // flag to indicate whether search_hl
924 // has been processed or not
925
926 cur = wp->w_match_head;
927 shl_flag = WIN_IS_POPUP(wp);
928 while (cur != NULL || shl_flag == FALSE)
929 {
930 if (shl_flag == FALSE
931 && ((cur != NULL
932 && cur->priority > SEARCH_HL_PRIORITY)
933 || cur == NULL))
934 {
935 shl = search_hl;
936 shl_flag = TRUE;
937 }
938 else
939 shl = &cur->hl;
940 if (col - 1 == (long)shl->startcol
941 && (shl == search_hl || !shl->is_addpos))
942 *char_attr = shl->attr;
943 if (shl != search_hl && cur != NULL)
944 cur = cur->next;
945 }
946}
947
948#endif // FEAT_SEARCH_EXTRA
949
950#if defined(FEAT_EVAL) || defined(PROTO)
951# ifdef FEAT_SEARCH_EXTRA
952 static int
953matchadd_dict_arg(typval_T *tv, char_u **conceal_char, win_T **win)
954{
955 dictitem_T *di;
956
957 if (tv->v_type != VAR_DICT)
958 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +0000959 emsg(_(e_dictionary_required));
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200960 return FAIL;
961 }
962
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +0100963 if (dict_has_key(tv->vval.v_dict, "conceal"))
Bram Moolenaard61efa52022-07-23 09:52:04 +0100964 *conceal_char = dict_get_string(tv->vval.v_dict, "conceal", FALSE);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200965
966 if ((di = dict_find(tv->vval.v_dict, (char_u *)"window", -1)) != NULL)
967 {
968 *win = find_win_by_nr_or_id(&di->di_tv);
969 if (*win == NULL)
970 {
Bram Moolenaar3a846e62022-01-01 16:21:00 +0000971 emsg(_(e_invalid_window_number));
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200972 return FAIL;
973 }
974 }
975
976 return OK;
977}
978#endif
979
980/*
981 * "clearmatches()" function
982 */
983 void
984f_clearmatches(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
985{
986#ifdef FEAT_SEARCH_EXTRA
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 (win != NULL)
994 clear_matches(win);
995#endif
996}
997
998/*
999 * "getmatches()" function
1000 */
1001 void
1002f_getmatches(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
1003{
1004# ifdef FEAT_SEARCH_EXTRA
1005 dict_T *dict;
1006 matchitem_T *cur;
1007 int i;
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001008 win_T *win;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001009
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001010 if (in_vim9script() && check_for_opt_number_arg(argvars, 0) == FAIL)
1011 return;
1012
1013 win = get_optional_window(argvars, 0);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001014 if (rettv_list_alloc(rettv) == FAIL || win == NULL)
1015 return;
1016
1017 cur = win->w_match_head;
1018 while (cur != NULL)
1019 {
1020 dict = dict_alloc();
1021 if (dict == NULL)
1022 return;
1023 if (cur->match.regprog == NULL)
1024 {
1025 // match added with matchaddpos()
1026 for (i = 0; i < MAXPOSMATCH; ++i)
1027 {
1028 llpos_T *llpos;
1029 char buf[30]; // use 30 to avoid compiler warning
1030 list_T *l;
1031
1032 llpos = &cur->pos.pos[i];
1033 if (llpos->lnum == 0)
1034 break;
1035 l = list_alloc();
1036 if (l == NULL)
1037 break;
1038 list_append_number(l, (varnumber_T)llpos->lnum);
1039 if (llpos->col > 0)
1040 {
1041 list_append_number(l, (varnumber_T)llpos->col);
1042 list_append_number(l, (varnumber_T)llpos->len);
1043 }
1044 sprintf(buf, "pos%d", i + 1);
1045 dict_add_list(dict, buf, l);
1046 }
1047 }
1048 else
1049 {
1050 dict_add_string(dict, "pattern", cur->pattern);
1051 }
1052 dict_add_string(dict, "group", syn_id2name(cur->hlg_id));
1053 dict_add_number(dict, "priority", (long)cur->priority);
1054 dict_add_number(dict, "id", (long)cur->id);
1055# if defined(FEAT_CONCEAL)
1056 if (cur->conceal_char)
1057 {
1058 char_u buf[MB_MAXBYTES + 1];
1059
=?UTF-8?q?Dundar=20G=C3=B6c?=420fabc2022-01-28 15:28:04 +00001060 buf[(*mb_char2bytes)(cur->conceal_char, buf)] = NUL;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001061 dict_add_string(dict, "conceal", (char_u *)&buf);
1062 }
1063# endif
1064 list_append_dict(rettv->vval.v_list, dict);
1065 cur = cur->next;
1066 }
1067# endif
1068}
1069
1070/*
1071 * "setmatches()" function
1072 */
1073 void
1074f_setmatches(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
1075{
1076#ifdef FEAT_SEARCH_EXTRA
1077 list_T *l;
1078 listitem_T *li;
1079 dict_T *d;
1080 list_T *s = NULL;
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02001081 win_T *win;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001082
1083 rettv->vval.v_number = -1;
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02001084
1085 if (in_vim9script()
1086 && (check_for_list_arg(argvars, 0) == FAIL
1087 || check_for_opt_number_arg(argvars, 1) == FAIL))
1088 return;
1089
Bram Moolenaard83392a2022-09-01 12:22:46 +01001090 if (check_for_list_arg(argvars, 0) == FAIL)
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001091 return;
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02001092 win = get_optional_window(argvars, 1);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001093 if (win == NULL)
1094 return;
1095
1096 if ((l = argvars[0].vval.v_list) != NULL)
1097 {
1098 // To some extent make sure that we are dealing with a list from
1099 // "getmatches()".
1100 li = l->lv_first;
1101 while (li != NULL)
1102 {
1103 if (li->li_tv.v_type != VAR_DICT
1104 || (d = li->li_tv.vval.v_dict) == NULL)
1105 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001106 emsg(_(e_invalid_argument));
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001107 return;
1108 }
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01001109 if (!(dict_has_key(d, "group")
1110 && (dict_has_key(d, "pattern")
1111 || dict_has_key(d, "pos1"))
1112 && dict_has_key(d, "priority")
1113 && dict_has_key(d, "id")))
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001114 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001115 emsg(_(e_invalid_argument));
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001116 return;
1117 }
1118 li = li->li_next;
1119 }
1120
1121 clear_matches(win);
1122 li = l->lv_first;
1123 while (li != NULL)
1124 {
1125 int i = 0;
1126 char buf[30]; // use 30 to avoid compiler warning
1127 dictitem_T *di;
1128 char_u *group;
1129 int priority;
1130 int id;
1131 char_u *conceal;
1132
1133 d = li->li_tv.vval.v_dict;
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01001134 if (!dict_has_key(d, "pattern"))
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001135 {
1136 if (s == NULL)
1137 {
1138 s = list_alloc();
1139 if (s == NULL)
1140 return;
1141 }
1142
1143 // match from matchaddpos()
1144 for (i = 1; i < 9; i++)
1145 {
1146 sprintf((char *)buf, (char *)"pos%d", i);
1147 if ((di = dict_find(d, (char_u *)buf, -1)) != NULL)
1148 {
1149 if (di->di_tv.v_type != VAR_LIST)
1150 return;
1151
1152 list_append_tv(s, &di->di_tv);
1153 s->lv_refcount++;
1154 }
1155 else
1156 break;
1157 }
1158 }
1159
Bram Moolenaard61efa52022-07-23 09:52:04 +01001160 group = dict_get_string(d, "group", TRUE);
1161 priority = (int)dict_get_number(d, "priority");
1162 id = (int)dict_get_number(d, "id");
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01001163 conceal = dict_has_key(d, "conceal")
Bram Moolenaard61efa52022-07-23 09:52:04 +01001164 ? dict_get_string(d, "conceal", TRUE)
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001165 : NULL;
1166 if (i == 0)
1167 {
1168 match_add(win, group,
Bram Moolenaard61efa52022-07-23 09:52:04 +01001169 dict_get_string(d, "pattern", FALSE),
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001170 priority, id, NULL, conceal);
1171 }
1172 else
1173 {
1174 match_add(win, group, NULL, priority, id, s, conceal);
1175 list_unref(s);
1176 s = NULL;
1177 }
1178 vim_free(group);
1179 vim_free(conceal);
1180
1181 li = li->li_next;
1182 }
1183 rettv->vval.v_number = 0;
1184 }
1185#endif
1186}
1187
1188/*
1189 * "matchadd()" function
1190 */
1191 void
1192f_matchadd(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
1193{
1194# ifdef FEAT_SEARCH_EXTRA
1195 char_u buf[NUMBUFLEN];
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02001196 char_u *grp; // group
1197 char_u *pat; // pattern
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001198 int prio = 10; // default priority
1199 int id = -1;
1200 int error = FALSE;
1201 char_u *conceal_char = NULL;
1202 win_T *win = curwin;
1203
1204 rettv->vval.v_number = -1;
1205
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02001206 if (in_vim9script()
1207 && (check_for_string_arg(argvars, 0) == FAIL
1208 || check_for_string_arg(argvars, 1) == FAIL
1209 || check_for_opt_number_arg(argvars, 2) == FAIL
1210 || (argvars[2].v_type != VAR_UNKNOWN
1211 && (check_for_opt_number_arg(argvars, 3) == FAIL
1212 || (argvars[3].v_type != VAR_UNKNOWN
1213 && check_for_opt_dict_arg(argvars, 4) == FAIL)))))
1214 return;
1215
1216 grp = tv_get_string_buf_chk(&argvars[0], buf); // group
1217 pat = tv_get_string_buf_chk(&argvars[1], buf); // pattern
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001218 if (grp == NULL || pat == NULL)
1219 return;
1220 if (argvars[2].v_type != VAR_UNKNOWN)
1221 {
1222 prio = (int)tv_get_number_chk(&argvars[2], &error);
1223 if (argvars[3].v_type != VAR_UNKNOWN)
1224 {
1225 id = (int)tv_get_number_chk(&argvars[3], &error);
1226 if (argvars[4].v_type != VAR_UNKNOWN
1227 && matchadd_dict_arg(&argvars[4], &conceal_char, &win) == FAIL)
1228 return;
1229 }
1230 }
1231 if (error == TRUE)
1232 return;
1233 if (id >= 1 && id <= 3)
1234 {
Bram Moolenaar677658a2022-01-05 16:09:06 +00001235 semsg(_(e_id_is_reserved_for_match_nr), id);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001236 return;
1237 }
1238
1239 rettv->vval.v_number = match_add(win, grp, pat, prio, id, NULL,
1240 conceal_char);
1241# endif
1242}
1243
1244/*
1245 * "matchaddpos()" function
1246 */
1247 void
1248f_matchaddpos(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
1249{
1250# ifdef FEAT_SEARCH_EXTRA
1251 char_u buf[NUMBUFLEN];
1252 char_u *group;
1253 int prio = 10;
1254 int id = -1;
1255 int error = FALSE;
1256 list_T *l;
1257 char_u *conceal_char = NULL;
1258 win_T *win = curwin;
1259
1260 rettv->vval.v_number = -1;
1261
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02001262 if (in_vim9script()
1263 && (check_for_string_arg(argvars, 0) == FAIL
1264 || check_for_list_arg(argvars, 1) == FAIL
1265 || check_for_opt_number_arg(argvars, 2) == FAIL
1266 || (argvars[2].v_type != VAR_UNKNOWN
1267 && (check_for_opt_number_arg(argvars, 3) == FAIL
1268 || (argvars[3].v_type != VAR_UNKNOWN
1269 && check_for_opt_dict_arg(argvars, 4) == FAIL)))))
1270 return;
1271
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001272 group = tv_get_string_buf_chk(&argvars[0], buf);
1273 if (group == NULL)
1274 return;
1275
1276 if (argvars[1].v_type != VAR_LIST)
1277 {
Bram Moolenaar3a846e62022-01-01 16:21:00 +00001278 semsg(_(e_argument_of_str_must_be_list), "matchaddpos()");
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001279 return;
1280 }
1281 l = argvars[1].vval.v_list;
1282 if (l == NULL)
1283 return;
1284
1285 if (argvars[2].v_type != VAR_UNKNOWN)
1286 {
1287 prio = (int)tv_get_number_chk(&argvars[2], &error);
1288 if (argvars[3].v_type != VAR_UNKNOWN)
1289 {
1290 id = (int)tv_get_number_chk(&argvars[3], &error);
1291
1292 if (argvars[4].v_type != VAR_UNKNOWN
1293 && matchadd_dict_arg(&argvars[4], &conceal_char, &win) == FAIL)
1294 return;
1295 }
1296 }
1297 if (error == TRUE)
1298 return;
1299
1300 // id == 3 is ok because matchaddpos() is supposed to substitute :3match
1301 if (id == 1 || id == 2)
1302 {
Bram Moolenaar677658a2022-01-05 16:09:06 +00001303 semsg(_(e_id_is_reserved_for_match_nr), id);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001304 return;
1305 }
1306
1307 rettv->vval.v_number = match_add(win, group, NULL, prio, id, l,
1308 conceal_char);
1309# endif
1310}
1311
1312/*
1313 * "matcharg()" function
1314 */
1315 void
1316f_matcharg(typval_T *argvars UNUSED, typval_T *rettv)
1317{
1318 if (rettv_list_alloc(rettv) == OK)
1319 {
1320# ifdef FEAT_SEARCH_EXTRA
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001321 int id;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001322 matchitem_T *m;
1323
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001324 if (in_vim9script() && check_for_number_arg(argvars, 0) == FAIL)
1325 return;
1326
1327 id = (int)tv_get_number(&argvars[0]);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001328 if (id >= 1 && id <= 3)
1329 {
=?UTF-8?q?Dundar=20G=C3=B6c?=420fabc2022-01-28 15:28:04 +00001330 if ((m = get_match(curwin, id)) != NULL)
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001331 {
1332 list_append_string(rettv->vval.v_list,
1333 syn_id2name(m->hlg_id), -1);
1334 list_append_string(rettv->vval.v_list, m->pattern, -1);
1335 }
1336 else
1337 {
1338 list_append_string(rettv->vval.v_list, NULL, -1);
1339 list_append_string(rettv->vval.v_list, NULL, -1);
1340 }
1341 }
1342# endif
1343 }
1344}
1345
1346/*
1347 * "matchdelete()" function
1348 */
1349 void
1350f_matchdelete(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
1351{
1352# ifdef FEAT_SEARCH_EXTRA
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001353 win_T *win;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001354
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001355 if (in_vim9script()
1356 && (check_for_number_arg(argvars, 0) == FAIL
1357 || check_for_opt_number_arg(argvars, 1) == FAIL))
1358 return;
1359
1360 win = get_optional_window(argvars, 1);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001361 if (win == NULL)
1362 rettv->vval.v_number = -1;
1363 else
1364 rettv->vval.v_number = match_delete(win,
1365 (int)tv_get_number(&argvars[0]), TRUE);
1366# endif
1367}
1368#endif
1369
1370#if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
1371/*
1372 * ":[N]match {group} {pattern}"
1373 * Sets nextcmd to the start of the next command, if any. Also called when
1374 * skipping commands to find the next command.
1375 */
1376 void
1377ex_match(exarg_T *eap)
1378{
1379 char_u *p;
1380 char_u *g = NULL;
1381 char_u *end;
1382 int c;
1383 int id;
1384
1385 if (eap->line2 <= 3)
1386 id = eap->line2;
1387 else
1388 {
Bram Moolenaar451c2e32020-08-15 16:33:28 +02001389 emsg(_(e_invalid_command));
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001390 return;
1391 }
1392
1393 // First clear any old pattern.
1394 if (!eap->skip)
1395 match_delete(curwin, id, FALSE);
1396
1397 if (ends_excmd2(eap->cmd, eap->arg))
1398 end = eap->arg;
1399 else if ((STRNICMP(eap->arg, "none", 4) == 0
1400 && (VIM_ISWHITE(eap->arg[4])
1401 || ends_excmd2(eap->arg, eap->arg + 4))))
1402 end = eap->arg + 4;
1403 else
1404 {
1405 p = skiptowhite(eap->arg);
1406 if (!eap->skip)
1407 g = vim_strnsave(eap->arg, p - eap->arg);
1408 p = skipwhite(p);
1409 if (*p == NUL)
1410 {
1411 // There must be two arguments.
1412 vim_free(g);
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001413 semsg(_(e_invalid_argument_str), eap->arg);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001414 return;
1415 }
1416 end = skip_regexp(p + 1, *p, TRUE);
1417 if (!eap->skip)
1418 {
1419 if (*end != NUL && !ends_excmd2(end, skipwhite(end + 1)))
1420 {
1421 vim_free(g);
Bram Moolenaar74409f62022-01-01 15:58:22 +00001422 eap->errmsg = ex_errmsg(e_trailing_characters_str, end);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001423 return;
1424 }
1425 if (*end != *p)
1426 {
1427 vim_free(g);
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001428 semsg(_(e_invalid_argument_str), p);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001429 return;
1430 }
1431
1432 c = *end;
1433 *end = NUL;
1434 match_add(curwin, g, p + 1, 10, id, NULL, NULL);
1435 vim_free(g);
1436 *end = c;
1437 }
1438 }
1439 eap->nextcmd = find_nextcmd(end);
1440}
1441#endif