blob: ef2587947dcb5a0ef9d17ad3d24c7c382c680003 [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/*
Bram Moolenaar50faf022022-09-29 12:50:17 +010021 * Add match to the match list of window "wp".
22 * If "pat" is not NULL the pattern will be highlighted with the group "grp"
23 * with priority "prio".
24 * If "pos_list" is not NULL the list of posisions defines the highlights.
25 * Optionally, a desired ID "id" can be specified (greater than or equal to 1).
26 * If no particular ID is desired, -1 must be specified for "id".
Bram Moolenaar06cf97e2020-06-28 13:17:26 +020027 * Return ID of added match, -1 on failure.
28 */
29 static int
30match_add(
31 win_T *wp,
32 char_u *grp,
33 char_u *pat,
34 int prio,
35 int id,
36 list_T *pos_list,
37 char_u *conceal_char UNUSED) // pointer to conceal replacement char
38{
39 matchitem_T *cur;
40 matchitem_T *prev;
41 matchitem_T *m;
42 int hlg_id;
43 regprog_T *regprog = NULL;
Bram Moolenaara4d158b2022-08-14 14:17:45 +010044 int rtype = UPD_SOME_VALID;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +020045
46 if (*grp == NUL || (pat != NULL && *pat == NUL))
47 return -1;
48 if (id < -1 || id == 0)
49 {
Bram Moolenaarbb8cac52022-01-05 18:16:53 +000050 semsg(_(e_invalid_id_nr_must_be_greater_than_or_equal_to_one_1), id);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +020051 return -1;
52 }
Bram Moolenaar9f573a82022-09-29 13:50:08 +010053 if (id == -1)
Bram Moolenaar06cf97e2020-06-28 13:17:26 +020054 {
Bram Moolenaar9f573a82022-09-29 13:50:08 +010055 // use the next available match ID
56 id = wp->w_next_match_id++;
57 }
58 else
59 {
60 // check the given ID is not already in use
61 for (cur = wp->w_match_head; cur != NULL; cur = cur->mit_next)
Bram Moolenaar50faf022022-09-29 12:50:17 +010062 if (cur->mit_id == id)
Bram Moolenaar06cf97e2020-06-28 13:17:26 +020063 {
Bram Moolenaar9d00e4a2022-01-05 17:49:15 +000064 semsg(_(e_id_already_taken_nr), id);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +020065 return -1;
66 }
Bram Moolenaar9f573a82022-09-29 13:50:08 +010067
68 // Make sure the next match ID is always higher than the highest
69 // manually selected ID. Add some extra in case a few more IDs are
70 // added soon.
71 if (wp->w_next_match_id < id + 100)
72 wp->w_next_match_id = id + 100;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +020073 }
Bram Moolenaar9f573a82022-09-29 13:50:08 +010074
Bram Moolenaar06cf97e2020-06-28 13:17:26 +020075 if ((hlg_id = syn_namen2id(grp, (int)STRLEN(grp))) == 0)
76 {
Bram Moolenaare29a27f2021-07-20 21:07:36 +020077 semsg(_(e_no_such_highlight_group_name_str), grp);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +020078 return -1;
79 }
80 if (pat != NULL && (regprog = vim_regcomp(pat, RE_MAGIC)) == NULL)
81 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +000082 semsg(_(e_invalid_argument_str), pat);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +020083 return -1;
84 }
85
Bram Moolenaar06cf97e2020-06-28 13:17:26 +020086 // Build new match.
87 m = ALLOC_CLEAR_ONE(matchitem_T);
Bram Moolenaar50faf022022-09-29 12:50:17 +010088 if (m == NULL)
89 return -1;
Christian Brabandtf7d31ad2024-04-16 22:23:17 +020090 if (pos_list != NULL && pos_list->lv_len > 0)
Bram Moolenaar50faf022022-09-29 12:50:17 +010091 {
92 m->mit_pos_array = ALLOC_CLEAR_MULT(llpos_T, pos_list->lv_len);
93 if (m->mit_pos_array == NULL)
94 {
95 vim_free(m);
96 return -1;
97 }
98 m->mit_pos_count = pos_list->lv_len;
99 }
100 m->mit_id = id;
101 m->mit_priority = prio;
102 m->mit_pattern = pat == NULL ? NULL : vim_strsave(pat);
103 m->mit_hlg_id = hlg_id;
104 m->mit_match.regprog = regprog;
105 m->mit_match.rmm_ic = FALSE;
106 m->mit_match.rmm_maxcol = 0;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200107# if defined(FEAT_CONCEAL)
Bram Moolenaar50faf022022-09-29 12:50:17 +0100108 m->mit_conceal_char = 0;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200109 if (conceal_char != NULL)
Bram Moolenaar50faf022022-09-29 12:50:17 +0100110 m->mit_conceal_char = (*mb_ptr2char)(conceal_char);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200111# endif
112
113 // Set up position matches
114 if (pos_list != NULL)
115 {
116 linenr_T toplnum = 0;
117 linenr_T botlnum = 0;
118 listitem_T *li;
119 int i;
120
121 CHECK_LIST_MATERIALIZE(pos_list);
Bram Moolenaar50faf022022-09-29 12:50:17 +0100122 for (i = 0, li = pos_list->lv_first; li != NULL; i++, li = li->li_next)
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200123 {
124 linenr_T lnum = 0;
125 colnr_T col = 0;
126 int len = 1;
127 list_T *subl;
128 listitem_T *subli;
129 int error = FALSE;
130
131 if (li->li_tv.v_type == VAR_LIST)
132 {
133 subl = li->li_tv.vval.v_list;
134 if (subl == NULL)
135 goto fail;
136 subli = subl->lv_first;
137 if (subli == NULL)
138 goto fail;
139 lnum = tv_get_number_chk(&subli->li_tv, &error);
140 if (error == TRUE)
141 goto fail;
142 if (lnum == 0)
143 {
144 --i;
145 continue;
146 }
Bram Moolenaar50faf022022-09-29 12:50:17 +0100147 m->mit_pos_array[i].lnum = lnum;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200148 subli = subli->li_next;
149 if (subli != NULL)
150 {
151 col = tv_get_number_chk(&subli->li_tv, &error);
152 if (error == TRUE)
153 goto fail;
154 subli = subli->li_next;
155 if (subli != NULL)
156 {
157 len = tv_get_number_chk(&subli->li_tv, &error);
158 if (error == TRUE)
159 goto fail;
160 }
161 }
Bram Moolenaar50faf022022-09-29 12:50:17 +0100162 m->mit_pos_array[i].col = col;
163 m->mit_pos_array[i].len = len;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200164 }
165 else if (li->li_tv.v_type == VAR_NUMBER)
166 {
167 if (li->li_tv.vval.v_number == 0)
168 {
169 --i;
170 continue;
171 }
Bram Moolenaar50faf022022-09-29 12:50:17 +0100172 m->mit_pos_array[i].lnum = li->li_tv.vval.v_number;
173 m->mit_pos_array[i].col = 0;
174 m->mit_pos_array[i].len = 0;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200175 }
176 else
177 {
Bram Moolenaar9a846fb2022-01-01 21:59:18 +0000178 emsg(_(e_list_or_number_required));
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200179 goto fail;
180 }
181 if (toplnum == 0 || lnum < toplnum)
182 toplnum = lnum;
183 if (botlnum == 0 || lnum >= botlnum)
184 botlnum = lnum + 1;
185 }
186
187 // Calculate top and bottom lines for redrawing area
188 if (toplnum != 0)
189 {
Luuk van Baal7bbb0f32025-02-22 09:19:04 +0100190 redraw_win_range_later(wp, toplnum, botlnum);
Bram Moolenaar50faf022022-09-29 12:50:17 +0100191 m->mit_toplnum = toplnum;
192 m->mit_botlnum = botlnum;
Bram Moolenaara4d158b2022-08-14 14:17:45 +0100193 rtype = UPD_VALID;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200194 }
195 }
196
197 // Insert new match. The match list is in ascending order with regard to
198 // the match priorities.
199 cur = wp->w_match_head;
200 prev = cur;
Bram Moolenaar50faf022022-09-29 12:50:17 +0100201 while (cur != NULL && prio >= cur->mit_priority)
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200202 {
203 prev = cur;
Bram Moolenaar50faf022022-09-29 12:50:17 +0100204 cur = cur->mit_next;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200205 }
206 if (cur == prev)
207 wp->w_match_head = m;
208 else
Bram Moolenaar50faf022022-09-29 12:50:17 +0100209 prev->mit_next = m;
210 m->mit_next = cur;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200211
212 redraw_win_later(wp, rtype);
213 return id;
214
215fail:
Bram Moolenaar50faf022022-09-29 12:50:17 +0100216 vim_free(m->mit_pattern);
217 vim_free(m->mit_pos_array);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200218 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 Moolenaar50faf022022-09-29 12:50:17 +0100236 semsg(_(e_invalid_id_nr_must_be_greater_than_or_equal_to_one_2),
237 id);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200238 return -1;
239 }
Bram Moolenaar50faf022022-09-29 12:50:17 +0100240 while (cur != NULL && cur->mit_id != id)
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200241 {
242 prev = cur;
Bram Moolenaar50faf022022-09-29 12:50:17 +0100243 cur = cur->mit_next;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200244 }
245 if (cur == NULL)
246 {
247 if (perr == TRUE)
Bram Moolenaar9d00e4a2022-01-05 17:49:15 +0000248 semsg(_(e_id_not_found_nr), id);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200249 return -1;
250 }
251 if (cur == prev)
Bram Moolenaar50faf022022-09-29 12:50:17 +0100252 wp->w_match_head = cur->mit_next;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200253 else
Bram Moolenaar50faf022022-09-29 12:50:17 +0100254 prev->mit_next = cur->mit_next;
255 vim_regfree(cur->mit_match.regprog);
256 vim_free(cur->mit_pattern);
257 if (cur->mit_toplnum != 0)
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200258 {
Luuk van Baal7bbb0f32025-02-22 09:19:04 +0100259 redraw_win_range_later(wp, cur->mit_toplnum, cur->mit_botlnum);
Bram Moolenaara4d158b2022-08-14 14:17:45 +0100260 rtype = UPD_VALID;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200261 }
Bram Moolenaar50faf022022-09-29 12:50:17 +0100262 vim_free(cur->mit_pos_array);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200263 vim_free(cur);
264 redraw_win_later(wp, rtype);
265 return 0;
266}
267
268/*
269 * Delete all matches in the match list of window 'wp'.
270 */
271 void
272clear_matches(win_T *wp)
273{
274 matchitem_T *m;
275
276 while (wp->w_match_head != NULL)
277 {
Bram Moolenaar50faf022022-09-29 12:50:17 +0100278 m = wp->w_match_head->mit_next;
279 vim_regfree(wp->w_match_head->mit_match.regprog);
280 vim_free(wp->w_match_head->mit_pattern);
281 vim_free(wp->w_match_head->mit_pos_array);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200282 vim_free(wp->w_match_head);
283 wp->w_match_head = m;
284 }
Bram Moolenaara4d158b2022-08-14 14:17:45 +0100285 redraw_win_later(wp, UPD_SOME_VALID);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200286}
287
288/*
289 * Get match from ID 'id' in window 'wp'.
290 * Return NULL if match not found.
291 */
292 static matchitem_T *
293get_match(win_T *wp, int id)
294{
295 matchitem_T *cur = wp->w_match_head;
296
Bram Moolenaar50faf022022-09-29 12:50:17 +0100297 while (cur != NULL && cur->mit_id != id)
298 cur = cur->mit_next;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200299 return cur;
300}
301
302/*
303 * Init for calling prepare_search_hl().
304 */
305 void
306init_search_hl(win_T *wp, match_T *search_hl)
307{
308 matchitem_T *cur;
309
310 // Setup for match and 'hlsearch' highlighting. Disable any previous
311 // match
312 cur = wp->w_match_head;
313 while (cur != NULL)
314 {
Bram Moolenaar50faf022022-09-29 12:50:17 +0100315 cur->mit_hl.rm = cur->mit_match;
316 if (cur->mit_hlg_id == 0)
317 cur->mit_hl.attr = 0;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200318 else
Bram Moolenaar50faf022022-09-29 12:50:17 +0100319 cur->mit_hl.attr = syn_id2attr(cur->mit_hlg_id);
320 cur->mit_hl.buf = wp->w_buffer;
321 cur->mit_hl.lnum = 0;
322 cur->mit_hl.first_lnum = 0;
323 cur = cur->mit_next;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200324 }
325 search_hl->buf = wp->w_buffer;
326 search_hl->lnum = 0;
327 search_hl->first_lnum = 0;
328 // time limit is set at the toplevel, for all windows
329}
330
331/*
332 * If there is a match fill "shl" and return one.
333 * Return zero otherwise.
334 */
335 static int
336next_search_hl_pos(
337 match_T *shl, // points to a match
338 linenr_T lnum,
Bram Moolenaar50faf022022-09-29 12:50:17 +0100339 matchitem_T *match, // match item with positions
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200340 colnr_T mincol) // minimal column for a match
341{
342 int i;
343 int found = -1;
344
Bram Moolenaar50faf022022-09-29 12:50:17 +0100345 for (i = match->mit_pos_cur; i < match->mit_pos_count; i++)
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200346 {
Bram Moolenaar50faf022022-09-29 12:50:17 +0100347 llpos_T *pos = &match->mit_pos_array[i];
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200348
349 if (pos->lnum == 0)
350 break;
351 if (pos->len == 0 && pos->col < mincol)
352 continue;
353 if (pos->lnum == lnum)
354 {
355 if (found >= 0)
356 {
Bram Moolenaar50faf022022-09-29 12:50:17 +0100357 // if this match comes before the one at "found" then swap them
358 if (pos->col < match->mit_pos_array[found].col)
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200359 {
360 llpos_T tmp = *pos;
361
Bram Moolenaar50faf022022-09-29 12:50:17 +0100362 *pos = match->mit_pos_array[found];
363 match->mit_pos_array[found] = tmp;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200364 }
365 }
366 else
367 found = i;
368 }
369 }
Bram Moolenaar50faf022022-09-29 12:50:17 +0100370 match->mit_pos_cur = 0;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200371 if (found >= 0)
372 {
Bram Moolenaar50faf022022-09-29 12:50:17 +0100373 colnr_T start = match->mit_pos_array[found].col == 0
374 ? 0 : match->mit_pos_array[found].col - 1;
375 colnr_T end = match->mit_pos_array[found].col == 0
376 ? MAXCOL : start + match->mit_pos_array[found].len;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200377
378 shl->lnum = lnum;
379 shl->rm.startpos[0].lnum = 0;
380 shl->rm.startpos[0].col = start;
381 shl->rm.endpos[0].lnum = 0;
382 shl->rm.endpos[0].col = end;
383 shl->is_addpos = TRUE;
Bram Moolenaar693ccd12022-04-16 12:04:37 +0100384 shl->has_cursor = FALSE;
Bram Moolenaar50faf022022-09-29 12:50:17 +0100385 match->mit_pos_cur = found + 1;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200386 return 1;
387 }
388 return 0;
389}
390
391/*
392 * Search for a next 'hlsearch' or match.
393 * Uses shl->buf.
394 * Sets shl->lnum and shl->rm contents.
395 * Note: Assumes a previous match is always before "lnum", unless
396 * shl->lnum is zero.
397 * Careful: Any pointers for buffer lines will become invalid.
398 */
399 static void
400next_search_hl(
401 win_T *win,
402 match_T *search_hl,
403 match_T *shl, // points to search_hl or a match
404 linenr_T lnum,
405 colnr_T mincol, // minimal column for a match
406 matchitem_T *cur) // to retrieve match positions if any
407{
408 linenr_T l;
409 colnr_T matchcol;
410 long nmatched;
411 int called_emsg_before = called_emsg;
Bram Moolenaar88456cd2022-11-18 22:14:09 +0000412 int timed_out = FALSE;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200413
414 // for :{range}s/pat only highlight inside the range
Bram Moolenaar94fb8272021-12-29 19:22:44 +0000415 if ((lnum < search_first_line || lnum > search_last_line) && cur == NULL)
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200416 {
417 shl->lnum = 0;
418 return;
419 }
420
421 if (shl->lnum != 0)
422 {
423 // Check for three situations:
424 // 1. If the "lnum" is below a previous match, start a new search.
425 // 2. If the previous match includes "mincol", use it.
426 // 3. Continue after the previous match.
427 l = shl->lnum + shl->rm.endpos[0].lnum - shl->rm.startpos[0].lnum;
428 if (lnum > l)
429 shl->lnum = 0;
430 else if (lnum < l || shl->rm.endpos[0].col > mincol)
431 return;
432 }
433
434 // Repeat searching for a match until one is found that includes "mincol"
435 // or none is found in this line.
436 for (;;)
437 {
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200438 // Three situations:
439 // 1. No useful previous match: search from start of line.
440 // 2. Not Vi compatible or empty match: continue at next character.
441 // Break the loop if this is beyond the end of the line.
442 // 3. Vi compatible searching: continue at end of previous match.
443 if (shl->lnum == 0)
444 matchcol = 0;
445 else if (vim_strchr(p_cpo, CPO_SEARCH) == NULL
446 || (shl->rm.endpos[0].lnum == 0
447 && shl->rm.endpos[0].col <= shl->rm.startpos[0].col))
448 {
449 char_u *ml;
450
451 matchcol = shl->rm.startpos[0].col;
452 ml = ml_get_buf(shl->buf, lnum, FALSE) + matchcol;
453 if (*ml == NUL)
454 {
455 ++matchcol;
456 shl->lnum = 0;
457 break;
458 }
459 if (has_mbyte)
460 matchcol += mb_ptr2len(ml);
461 else
462 ++matchcol;
463 }
464 else
465 matchcol = shl->rm.endpos[0].col;
466
467 shl->lnum = lnum;
468 if (shl->rm.regprog != NULL)
469 {
470 // Remember whether shl->rm is using a copy of the regprog in
Bram Moolenaar50faf022022-09-29 12:50:17 +0100471 // cur->mit_match.
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200472 int regprog_is_copy = (shl != search_hl && cur != NULL
Bram Moolenaar50faf022022-09-29 12:50:17 +0100473 && shl == &cur->mit_hl
474 && cur->mit_match.regprog == cur->mit_hl.rm.regprog);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200475
476 nmatched = vim_regexec_multi(&shl->rm, win, shl->buf, lnum,
Paul Ollis65745772022-06-05 16:55:54 +0100477 matchcol, &timed_out);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200478 // Copy the regprog, in case it got freed and recompiled.
479 if (regprog_is_copy)
Bram Moolenaar50faf022022-09-29 12:50:17 +0100480 cur->mit_match.regprog = cur->mit_hl.rm.regprog;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200481
482 if (called_emsg > called_emsg_before || got_int || timed_out)
483 {
484 // Error while handling regexp: stop using this regexp.
485 if (shl == search_hl)
486 {
487 // don't free regprog in the match list, it's a copy
488 vim_regfree(shl->rm.regprog);
489 set_no_hlsearch(TRUE);
490 }
491 shl->rm.regprog = NULL;
492 shl->lnum = 0;
493 got_int = FALSE; // avoid the "Type :quit to exit Vim" message
494 break;
495 }
496 }
497 else if (cur != NULL)
Bram Moolenaar50faf022022-09-29 12:50:17 +0100498 nmatched = next_search_hl_pos(shl, lnum, cur, matchcol);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200499 else
500 nmatched = 0;
501 if (nmatched == 0)
502 {
503 shl->lnum = 0; // no match found
504 break;
505 }
506 if (shl->rm.startpos[0].lnum > 0
507 || shl->rm.startpos[0].col >= mincol
508 || nmatched > 1
509 || shl->rm.endpos[0].col > mincol)
510 {
511 shl->lnum += shl->rm.startpos[0].lnum;
512 break; // useful match found
513 }
514 }
515}
516
517/*
518 * Advance to the match in window "wp" line "lnum" or past it.
519 */
520 void
521prepare_search_hl(win_T *wp, match_T *search_hl, linenr_T lnum)
522{
523 matchitem_T *cur; // points to the match list
524 match_T *shl; // points to search_hl or a match
525 int shl_flag; // flag to indicate whether search_hl
526 // has been processed or not
527 int pos_inprogress; // marks that position match search is
528 // in progress
529 int n;
530
531 // When using a multi-line pattern, start searching at the top
532 // of the window or just after a closed fold.
533 // Do this both for search_hl and the match list.
534 cur = wp->w_match_head;
535 shl_flag = WIN_IS_POPUP(wp); // skip search_hl in a popup window
536 while (cur != NULL || shl_flag == FALSE)
537 {
538 if (shl_flag == FALSE)
539 {
540 shl = search_hl;
541 shl_flag = TRUE;
542 }
543 else
Bram Moolenaar50faf022022-09-29 12:50:17 +0100544 shl = &cur->mit_hl;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200545 if (shl->rm.regprog != NULL
546 && shl->lnum == 0
547 && re_multiline(shl->rm.regprog))
548 {
549 if (shl->first_lnum == 0)
550 {
551# ifdef FEAT_FOLDING
552 for (shl->first_lnum = lnum;
553 shl->first_lnum > wp->w_topline; --shl->first_lnum)
554 if (hasFoldingWin(wp, shl->first_lnum - 1,
555 NULL, NULL, TRUE, NULL))
556 break;
557# else
558 shl->first_lnum = wp->w_topline;
559# endif
560 }
561 if (cur != NULL)
Bram Moolenaar50faf022022-09-29 12:50:17 +0100562 cur->mit_pos_cur = 0;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200563 pos_inprogress = TRUE;
564 n = 0;
565 while (shl->first_lnum < lnum && (shl->rm.regprog != NULL
566 || (cur != NULL && pos_inprogress)))
567 {
568 next_search_hl(wp, search_hl, shl, shl->first_lnum, (colnr_T)n,
569 shl == search_hl ? NULL : cur);
Bram Moolenaar50faf022022-09-29 12:50:17 +0100570 pos_inprogress = cur == NULL || cur->mit_pos_cur == 0
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200571 ? FALSE : TRUE;
572 if (shl->lnum != 0)
573 {
574 shl->first_lnum = shl->lnum
575 + shl->rm.endpos[0].lnum
576 - shl->rm.startpos[0].lnum;
577 n = shl->rm.endpos[0].col;
578 }
579 else
580 {
581 ++shl->first_lnum;
582 n = 0;
583 }
584 }
585 }
586 if (shl != search_hl && cur != NULL)
Bram Moolenaar50faf022022-09-29 12:50:17 +0100587 cur = cur->mit_next;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200588 }
589}
590
591/*
Bram Moolenaar9b367502022-04-22 20:07:21 +0100592 * Update "shl->has_cursor" based on the match in "shl" and the cursor
593 * position.
594 */
595 static void
596check_cur_search_hl(win_T *wp, match_T *shl)
597{
zeertzjq8279cfe2022-04-23 12:05:51 +0100598 linenr_T linecount = shl->rm.endpos[0].lnum - shl->rm.startpos[0].lnum;
Bram Moolenaar9b367502022-04-22 20:07:21 +0100599
600 if (wp->w_cursor.lnum >= shl->lnum
zeertzjq8279cfe2022-04-23 12:05:51 +0100601 && wp->w_cursor.lnum <= shl->lnum + linecount
Bram Moolenaar9b367502022-04-22 20:07:21 +0100602 && (wp->w_cursor.lnum > shl->lnum
603 || wp->w_cursor.col >= shl->rm.startpos[0].col)
604 && (wp->w_cursor.lnum < shl->lnum + linecount
605 || wp->w_cursor.col < shl->rm.endpos[0].col))
606 shl->has_cursor = TRUE;
607 else
608 shl->has_cursor = FALSE;
609}
610
611/*
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200612 * Prepare for 'hlsearch' and match highlighting in one window line.
613 * Return TRUE if there is such highlighting and set "search_attr" to the
614 * current highlight attribute.
615 */
616 int
617prepare_search_hl_line(
618 win_T *wp,
619 linenr_T lnum,
620 colnr_T mincol,
621 char_u **line,
622 match_T *search_hl,
623 int *search_attr)
624{
625 matchitem_T *cur; // points to the match list
626 match_T *shl; // points to search_hl or a match
627 int shl_flag; // flag to indicate whether search_hl
628 // has been processed or not
629 int area_highlighting = FALSE;
630
631 // Handle highlighting the last used search pattern and matches.
632 // Do this for both search_hl and the match list.
633 // Do not use search_hl in a popup window.
634 cur = wp->w_match_head;
635 shl_flag = WIN_IS_POPUP(wp);
636 while (cur != NULL || shl_flag == FALSE)
637 {
638 if (shl_flag == FALSE)
639 {
640 shl = search_hl;
641 shl_flag = TRUE;
642 }
643 else
Bram Moolenaar50faf022022-09-29 12:50:17 +0100644 shl = &cur->mit_hl;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200645 shl->startcol = MAXCOL;
646 shl->endcol = MAXCOL;
647 shl->attr_cur = 0;
648 shl->is_addpos = FALSE;
Bram Moolenaar693ccd12022-04-16 12:04:37 +0100649 shl->has_cursor = FALSE;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200650 if (cur != NULL)
Bram Moolenaar50faf022022-09-29 12:50:17 +0100651 cur->mit_pos_cur = 0;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200652 next_search_hl(wp, search_hl, shl, lnum, mincol,
653 shl == search_hl ? NULL : cur);
654
655 // Need to get the line again, a multi-line regexp may have made it
656 // invalid.
657 *line = ml_get_buf(wp->w_buffer, lnum, FALSE);
658
659 if (shl->lnum != 0 && shl->lnum <= lnum)
660 {
661 if (shl->lnum == lnum)
662 shl->startcol = shl->rm.startpos[0].col;
663 else
664 shl->startcol = 0;
665 if (lnum == shl->lnum + shl->rm.endpos[0].lnum
666 - shl->rm.startpos[0].lnum)
667 shl->endcol = shl->rm.endpos[0].col;
668 else
669 shl->endcol = MAXCOL;
Bram Moolenaar693ccd12022-04-16 12:04:37 +0100670
671 // check if the cursor is in the match before changing the columns
Bram Moolenaar9b367502022-04-22 20:07:21 +0100672 if (shl == search_hl)
673 check_cur_search_hl(wp, shl);
Bram Moolenaar693ccd12022-04-16 12:04:37 +0100674
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200675 // Highlight one character for an empty match.
676 if (shl->startcol == shl->endcol)
677 {
678 if (has_mbyte && (*line)[shl->endcol] != NUL)
679 shl->endcol += (*mb_ptr2len)((*line) + shl->endcol);
680 else
681 ++shl->endcol;
682 }
683 if ((long)shl->startcol < mincol) // match at leftcol
684 {
685 shl->attr_cur = shl->attr;
686 *search_attr = shl->attr;
687 }
688 area_highlighting = TRUE;
689 }
690 if (shl != search_hl && cur != NULL)
Bram Moolenaar50faf022022-09-29 12:50:17 +0100691 cur = cur->mit_next;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200692 }
693 return area_highlighting;
694}
695
696/*
697 * For a position in a line: Check for start/end of 'hlsearch' and other
698 * matches.
699 * After end, check for start/end of next match.
700 * When another match, have to check for start again.
701 * Watch out for matching an empty string!
Bram Moolenaar0c359af2021-11-29 19:18:57 +0000702 * "on_last_col" is set to TRUE with non-zero search_attr and the next column
703 * is endcol.
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200704 * Return the updated search_attr.
705 */
706 int
707update_search_hl(
708 win_T *wp,
709 linenr_T lnum,
710 colnr_T col,
711 char_u **line,
712 match_T *search_hl,
713 int *has_match_conc UNUSED,
714 int *match_conc UNUSED,
715 int did_line_attr,
Bram Moolenaar0c359af2021-11-29 19:18:57 +0000716 int lcs_eol_one,
717 int *on_last_col)
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200718{
719 matchitem_T *cur; // points to the match list
720 match_T *shl; // points to search_hl or a match
721 int shl_flag; // flag to indicate whether search_hl
722 // has been processed or not
723 int pos_inprogress; // marks that position match search is in
724 // progress
725 int search_attr = 0;
726
727
728 // Do this for 'search_hl' and the match list (ordered by priority).
729 cur = wp->w_match_head;
730 shl_flag = WIN_IS_POPUP(wp);
731 while (cur != NULL || shl_flag == FALSE)
732 {
733 if (shl_flag == FALSE
734 && (cur == NULL
Bram Moolenaar50faf022022-09-29 12:50:17 +0100735 || cur->mit_priority > SEARCH_HL_PRIORITY))
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200736 {
737 shl = search_hl;
738 shl_flag = TRUE;
739 }
740 else
Bram Moolenaar50faf022022-09-29 12:50:17 +0100741 shl = &cur->mit_hl;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200742 if (cur != NULL)
Bram Moolenaar50faf022022-09-29 12:50:17 +0100743 cur->mit_pos_cur = 0;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200744 pos_inprogress = TRUE;
745 while (shl->rm.regprog != NULL || (cur != NULL && pos_inprogress))
746 {
747 if (shl->startcol != MAXCOL
748 && col >= shl->startcol
749 && col < shl->endcol)
750 {
751 int next_col = col + mb_ptr2len(*line + col);
752
753 if (shl->endcol < next_col)
754 shl->endcol = next_col;
755 shl->attr_cur = shl->attr;
756# ifdef FEAT_CONCEAL
757 // Match with the "Conceal" group results in hiding
758 // the match.
759 if (cur != NULL
760 && shl != search_hl
Bram Moolenaar50faf022022-09-29 12:50:17 +0100761 && syn_name2id((char_u *)"Conceal") == cur->mit_hlg_id)
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200762 {
763 *has_match_conc = col == shl->startcol ? 2 : 1;
Bram Moolenaar50faf022022-09-29 12:50:17 +0100764 *match_conc = cur->mit_conceal_char;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200765 }
766 else
767 *has_match_conc = 0;
768# endif
LemonBoya4399382022-04-09 21:04:08 +0100769 // Highlight the match were the cursor is using the CurSearch
770 // group.
Bram Moolenaar693ccd12022-04-16 12:04:37 +0100771 if (shl == search_hl && shl->has_cursor)
Bram Moolenaar368137a2022-05-31 13:43:12 +0100772 {
LemonBoya4399382022-04-09 21:04:08 +0100773 shl->attr_cur = HL_ATTR(HLF_LC);
Bram Moolenaar368137a2022-05-31 13:43:12 +0100774 if (shl->attr_cur != shl->attr)
775 search_hl_has_cursor_lnum = lnum;
776 }
LemonBoya4399382022-04-09 21:04:08 +0100777
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200778 }
779 else if (col == shl->endcol)
780 {
781 shl->attr_cur = 0;
782 next_search_hl(wp, search_hl, shl, lnum, col,
783 shl == search_hl ? NULL : cur);
Bram Moolenaar50faf022022-09-29 12:50:17 +0100784 pos_inprogress = !(cur == NULL || cur->mit_pos_cur == 0);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200785
786 // Need to get the line again, a multi-line regexp may have
787 // made it invalid.
788 *line = ml_get_buf(wp->w_buffer, lnum, FALSE);
789
790 if (shl->lnum == lnum)
791 {
792 shl->startcol = shl->rm.startpos[0].col;
793 if (shl->rm.endpos[0].lnum == 0)
794 shl->endcol = shl->rm.endpos[0].col;
795 else
796 shl->endcol = MAXCOL;
797
Bram Moolenaar9b367502022-04-22 20:07:21 +0100798 // check if the cursor is in the match
799 if (shl == search_hl)
800 check_cur_search_hl(wp, shl);
801
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200802 if (shl->startcol == shl->endcol)
803 {
804 // highlight empty match, try again after
805 // it
806 if (has_mbyte)
Bram Moolenaar41f08952021-02-22 22:13:49 +0100807 {
808 char_u *p = *line + shl->endcol;
809
810 if (*p == NUL)
811 // consistent with non-mbyte
812 ++shl->endcol;
813 else
814 shl->endcol += (*mb_ptr2len)(p);
815 }
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200816 else
817 ++shl->endcol;
818 }
819
820 // Loop to check if the match starts at the
821 // current position
822 continue;
823 }
824 }
825 break;
826 }
827 if (shl != search_hl && cur != NULL)
Bram Moolenaar50faf022022-09-29 12:50:17 +0100828 cur = cur->mit_next;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200829 }
830
831 // Use attributes from match with highest priority among 'search_hl' and
832 // the match list.
833 cur = wp->w_match_head;
834 shl_flag = WIN_IS_POPUP(wp);
835 while (cur != NULL || shl_flag == FALSE)
836 {
837 if (shl_flag == FALSE
838 && (cur == NULL ||
Bram Moolenaar50faf022022-09-29 12:50:17 +0100839 cur->mit_priority > SEARCH_HL_PRIORITY))
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200840 {
841 shl = search_hl;
842 shl_flag = TRUE;
843 }
844 else
Bram Moolenaar50faf022022-09-29 12:50:17 +0100845 shl = &cur->mit_hl;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200846 if (shl->attr_cur != 0)
Bram Moolenaar0c359af2021-11-29 19:18:57 +0000847 {
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200848 search_attr = shl->attr_cur;
Bram Moolenaar0c359af2021-11-29 19:18:57 +0000849 *on_last_col = col + 1 >= shl->endcol;
850 }
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200851 if (shl != search_hl && cur != NULL)
Bram Moolenaar50faf022022-09-29 12:50:17 +0100852 cur = cur->mit_next;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200853 }
854 // Only highlight one character after the last column.
855 if (*(*line + col) == NUL && (did_line_attr >= 1
856 || (wp->w_p_list && lcs_eol_one == -1)))
857 search_attr = 0;
858 return search_attr;
859}
860
861 int
862get_prevcol_hl_flag(win_T *wp, match_T *search_hl, long curcol)
863{
864 long prevcol = curcol;
865 int prevcol_hl_flag = FALSE;
866 matchitem_T *cur; // points to the match list
867
Bram Moolenaar41f08952021-02-22 22:13:49 +0100868#if defined(FEAT_PROP_POPUP)
869 // don't do this in a popup window
870 if (popup_is_popup(wp))
871 return FALSE;
872#endif
873
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200874 // we're not really at that column when skipping some text
875 if ((long)(wp->w_p_wrap ? wp->w_skipcol : wp->w_leftcol) > prevcol)
876 ++prevcol;
877
Bram Moolenaar41f08952021-02-22 22:13:49 +0100878 // Highlight a character after the end of the line if the match started
879 // at the end of the line or when the match continues in the next line
880 // (match includes the line break).
881 if (!search_hl->is_addpos && (prevcol == (long)search_hl->startcol
882 || (prevcol > (long)search_hl->startcol
883 && search_hl->endcol == MAXCOL)))
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200884 prevcol_hl_flag = TRUE;
885 else
886 {
887 cur = wp->w_match_head;
888 while (cur != NULL)
889 {
Bram Moolenaar50faf022022-09-29 12:50:17 +0100890 if (!cur->mit_hl.is_addpos && (prevcol == (long)cur->mit_hl.startcol
891 || (prevcol > (long)cur->mit_hl.startcol
892 && cur->mit_hl.endcol == MAXCOL)))
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200893 {
894 prevcol_hl_flag = TRUE;
895 break;
896 }
Bram Moolenaar50faf022022-09-29 12:50:17 +0100897 cur = cur->mit_next;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200898 }
899 }
900 return prevcol_hl_flag;
901}
902
903/*
904 * Get highlighting for the char after the text in "char_attr" from 'hlsearch'
905 * or match highlighting.
906 */
907 void
908get_search_match_hl(win_T *wp, match_T *search_hl, long col, int *char_attr)
909{
910 matchitem_T *cur; // points to the match list
911 match_T *shl; // points to search_hl or a match
912 int shl_flag; // flag to indicate whether search_hl
913 // has been processed or not
914
915 cur = wp->w_match_head;
916 shl_flag = WIN_IS_POPUP(wp);
917 while (cur != NULL || shl_flag == FALSE)
918 {
919 if (shl_flag == FALSE
920 && ((cur != NULL
Bram Moolenaar50faf022022-09-29 12:50:17 +0100921 && cur->mit_priority > SEARCH_HL_PRIORITY)
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200922 || cur == NULL))
923 {
924 shl = search_hl;
925 shl_flag = TRUE;
926 }
927 else
Bram Moolenaar50faf022022-09-29 12:50:17 +0100928 shl = &cur->mit_hl;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200929 if (col - 1 == (long)shl->startcol
930 && (shl == search_hl || !shl->is_addpos))
931 *char_attr = shl->attr;
932 if (shl != search_hl && cur != NULL)
Bram Moolenaar50faf022022-09-29 12:50:17 +0100933 cur = cur->mit_next;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200934 }
935}
936
937#endif // FEAT_SEARCH_EXTRA
938
939#if defined(FEAT_EVAL) || defined(PROTO)
940# ifdef FEAT_SEARCH_EXTRA
941 static int
942matchadd_dict_arg(typval_T *tv, char_u **conceal_char, win_T **win)
943{
944 dictitem_T *di;
945
946 if (tv->v_type != VAR_DICT)
947 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +0000948 emsg(_(e_dictionary_required));
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200949 return FAIL;
950 }
951
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +0100952 if (dict_has_key(tv->vval.v_dict, "conceal"))
Bram Moolenaard61efa52022-07-23 09:52:04 +0100953 *conceal_char = dict_get_string(tv->vval.v_dict, "conceal", FALSE);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200954
Yegappan Lakshmanane8575982023-01-14 12:32:28 +0000955 if ((di = dict_find(tv->vval.v_dict, (char_u *)"window", -1)) == NULL)
956 return OK;
957
958 *win = find_win_by_nr_or_id(&di->di_tv);
959 if (*win == NULL)
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200960 {
Yegappan Lakshmanane8575982023-01-14 12:32:28 +0000961 emsg(_(e_invalid_window_number));
962 return FAIL;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200963 }
964
965 return OK;
966}
967#endif
968
969/*
970 * "clearmatches()" function
971 */
972 void
973f_clearmatches(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
974{
975#ifdef FEAT_SEARCH_EXTRA
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200976 win_T *win;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200977
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200978 if (in_vim9script() && check_for_opt_number_arg(argvars, 0) == FAIL)
979 return;
980
981 win = get_optional_window(argvars, 0);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200982 if (win != NULL)
983 clear_matches(win);
984#endif
985}
986
987/*
988 * "getmatches()" function
989 */
990 void
991f_getmatches(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
992{
993# ifdef FEAT_SEARCH_EXTRA
994 dict_T *dict;
995 matchitem_T *cur;
996 int i;
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200997 win_T *win;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200998
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200999 if (in_vim9script() && check_for_opt_number_arg(argvars, 0) == FAIL)
1000 return;
1001
1002 win = get_optional_window(argvars, 0);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001003 if (rettv_list_alloc(rettv) == FAIL || win == NULL)
1004 return;
1005
1006 cur = win->w_match_head;
1007 while (cur != NULL)
1008 {
1009 dict = dict_alloc();
1010 if (dict == NULL)
1011 return;
Bram Moolenaar50faf022022-09-29 12:50:17 +01001012 if (cur->mit_match.regprog == NULL)
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001013 {
1014 // match added with matchaddpos()
Bram Moolenaar50faf022022-09-29 12:50:17 +01001015 for (i = 0; i < cur->mit_pos_count; ++i)
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001016 {
1017 llpos_T *llpos;
1018 char buf[30]; // use 30 to avoid compiler warning
1019 list_T *l;
1020
Bram Moolenaar50faf022022-09-29 12:50:17 +01001021 llpos = &cur->mit_pos_array[i];
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001022 if (llpos->lnum == 0)
1023 break;
1024 l = list_alloc();
1025 if (l == NULL)
1026 break;
1027 list_append_number(l, (varnumber_T)llpos->lnum);
1028 if (llpos->col > 0)
1029 {
1030 list_append_number(l, (varnumber_T)llpos->col);
1031 list_append_number(l, (varnumber_T)llpos->len);
1032 }
1033 sprintf(buf, "pos%d", i + 1);
1034 dict_add_list(dict, buf, l);
1035 }
1036 }
1037 else
1038 {
Bram Moolenaar50faf022022-09-29 12:50:17 +01001039 dict_add_string(dict, "pattern", cur->mit_pattern);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001040 }
Bram Moolenaar50faf022022-09-29 12:50:17 +01001041 dict_add_string(dict, "group", syn_id2name(cur->mit_hlg_id));
1042 dict_add_number(dict, "priority", (long)cur->mit_priority);
1043 dict_add_number(dict, "id", (long)cur->mit_id);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001044# if defined(FEAT_CONCEAL)
Bram Moolenaar50faf022022-09-29 12:50:17 +01001045 if (cur->mit_conceal_char)
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001046 {
1047 char_u buf[MB_MAXBYTES + 1];
1048
Bram Moolenaar50faf022022-09-29 12:50:17 +01001049 buf[(*mb_char2bytes)(cur->mit_conceal_char, buf)] = NUL;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001050 dict_add_string(dict, "conceal", (char_u *)&buf);
1051 }
1052# endif
1053 list_append_dict(rettv->vval.v_list, dict);
Bram Moolenaar50faf022022-09-29 12:50:17 +01001054 cur = cur->mit_next;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001055 }
1056# endif
1057}
1058
1059/*
1060 * "setmatches()" function
1061 */
1062 void
1063f_setmatches(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
1064{
1065#ifdef FEAT_SEARCH_EXTRA
1066 list_T *l;
1067 listitem_T *li;
1068 dict_T *d;
1069 list_T *s = NULL;
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02001070 win_T *win;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001071
1072 rettv->vval.v_number = -1;
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02001073
1074 if (in_vim9script()
1075 && (check_for_list_arg(argvars, 0) == FAIL
1076 || check_for_opt_number_arg(argvars, 1) == FAIL))
1077 return;
1078
Bram Moolenaard83392a2022-09-01 12:22:46 +01001079 if (check_for_list_arg(argvars, 0) == FAIL)
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001080 return;
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02001081 win = get_optional_window(argvars, 1);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001082 if (win == NULL)
1083 return;
1084
1085 if ((l = argvars[0].vval.v_list) != NULL)
1086 {
1087 // To some extent make sure that we are dealing with a list from
1088 // "getmatches()".
1089 li = l->lv_first;
1090 while (li != NULL)
1091 {
1092 if (li->li_tv.v_type != VAR_DICT
1093 || (d = li->li_tv.vval.v_dict) == NULL)
1094 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001095 emsg(_(e_invalid_argument));
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001096 return;
1097 }
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01001098 if (!(dict_has_key(d, "group")
1099 && (dict_has_key(d, "pattern")
1100 || dict_has_key(d, "pos1"))
1101 && dict_has_key(d, "priority")
1102 && dict_has_key(d, "id")))
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001103 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001104 emsg(_(e_invalid_argument));
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001105 return;
1106 }
1107 li = li->li_next;
1108 }
1109
1110 clear_matches(win);
1111 li = l->lv_first;
1112 while (li != NULL)
1113 {
1114 int i = 0;
1115 char buf[30]; // use 30 to avoid compiler warning
1116 dictitem_T *di;
1117 char_u *group;
1118 int priority;
1119 int id;
1120 char_u *conceal;
1121
1122 d = li->li_tv.vval.v_dict;
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01001123 if (!dict_has_key(d, "pattern"))
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001124 {
1125 if (s == NULL)
1126 {
1127 s = list_alloc();
1128 if (s == NULL)
1129 return;
1130 }
1131
1132 // match from matchaddpos()
1133 for (i = 1; i < 9; i++)
1134 {
1135 sprintf((char *)buf, (char *)"pos%d", i);
1136 if ((di = dict_find(d, (char_u *)buf, -1)) != NULL)
1137 {
1138 if (di->di_tv.v_type != VAR_LIST)
1139 return;
1140
1141 list_append_tv(s, &di->di_tv);
1142 s->lv_refcount++;
1143 }
1144 else
1145 break;
1146 }
1147 }
1148
Bram Moolenaard61efa52022-07-23 09:52:04 +01001149 group = dict_get_string(d, "group", TRUE);
1150 priority = (int)dict_get_number(d, "priority");
1151 id = (int)dict_get_number(d, "id");
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01001152 conceal = dict_has_key(d, "conceal")
Bram Moolenaard61efa52022-07-23 09:52:04 +01001153 ? dict_get_string(d, "conceal", TRUE)
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001154 : NULL;
1155 if (i == 0)
1156 {
1157 match_add(win, group,
Bram Moolenaard61efa52022-07-23 09:52:04 +01001158 dict_get_string(d, "pattern", FALSE),
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001159 priority, id, NULL, conceal);
1160 }
1161 else
1162 {
1163 match_add(win, group, NULL, priority, id, s, conceal);
1164 list_unref(s);
1165 s = NULL;
1166 }
1167 vim_free(group);
1168 vim_free(conceal);
1169
1170 li = li->li_next;
1171 }
1172 rettv->vval.v_number = 0;
1173 }
1174#endif
1175}
1176
1177/*
1178 * "matchadd()" function
1179 */
1180 void
1181f_matchadd(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
1182{
1183# ifdef FEAT_SEARCH_EXTRA
1184 char_u buf[NUMBUFLEN];
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02001185 char_u *grp; // group
1186 char_u *pat; // pattern
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001187 int prio = 10; // default priority
1188 int id = -1;
1189 int error = FALSE;
1190 char_u *conceal_char = NULL;
1191 win_T *win = curwin;
1192
1193 rettv->vval.v_number = -1;
1194
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02001195 if (in_vim9script()
1196 && (check_for_string_arg(argvars, 0) == FAIL
1197 || check_for_string_arg(argvars, 1) == FAIL
1198 || check_for_opt_number_arg(argvars, 2) == FAIL
1199 || (argvars[2].v_type != VAR_UNKNOWN
1200 && (check_for_opt_number_arg(argvars, 3) == FAIL
1201 || (argvars[3].v_type != VAR_UNKNOWN
1202 && check_for_opt_dict_arg(argvars, 4) == FAIL)))))
1203 return;
1204
1205 grp = tv_get_string_buf_chk(&argvars[0], buf); // group
1206 pat = tv_get_string_buf_chk(&argvars[1], buf); // pattern
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001207 if (grp == NULL || pat == NULL)
1208 return;
1209 if (argvars[2].v_type != VAR_UNKNOWN)
1210 {
1211 prio = (int)tv_get_number_chk(&argvars[2], &error);
1212 if (argvars[3].v_type != VAR_UNKNOWN)
1213 {
1214 id = (int)tv_get_number_chk(&argvars[3], &error);
1215 if (argvars[4].v_type != VAR_UNKNOWN
1216 && matchadd_dict_arg(&argvars[4], &conceal_char, &win) == FAIL)
1217 return;
1218 }
1219 }
1220 if (error == TRUE)
1221 return;
1222 if (id >= 1 && id <= 3)
1223 {
Bram Moolenaar677658a2022-01-05 16:09:06 +00001224 semsg(_(e_id_is_reserved_for_match_nr), id);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001225 return;
1226 }
1227
1228 rettv->vval.v_number = match_add(win, grp, pat, prio, id, NULL,
1229 conceal_char);
1230# endif
1231}
1232
1233/*
1234 * "matchaddpos()" function
1235 */
1236 void
1237f_matchaddpos(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
1238{
1239# ifdef FEAT_SEARCH_EXTRA
1240 char_u buf[NUMBUFLEN];
1241 char_u *group;
1242 int prio = 10;
1243 int id = -1;
1244 int error = FALSE;
1245 list_T *l;
1246 char_u *conceal_char = NULL;
1247 win_T *win = curwin;
1248
1249 rettv->vval.v_number = -1;
1250
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02001251 if (in_vim9script()
1252 && (check_for_string_arg(argvars, 0) == FAIL
1253 || check_for_list_arg(argvars, 1) == FAIL
1254 || check_for_opt_number_arg(argvars, 2) == FAIL
1255 || (argvars[2].v_type != VAR_UNKNOWN
1256 && (check_for_opt_number_arg(argvars, 3) == FAIL
1257 || (argvars[3].v_type != VAR_UNKNOWN
1258 && check_for_opt_dict_arg(argvars, 4) == FAIL)))))
1259 return;
1260
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001261 group = tv_get_string_buf_chk(&argvars[0], buf);
1262 if (group == NULL)
1263 return;
1264
1265 if (argvars[1].v_type != VAR_LIST)
1266 {
Bram Moolenaar3a846e62022-01-01 16:21:00 +00001267 semsg(_(e_argument_of_str_must_be_list), "matchaddpos()");
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001268 return;
1269 }
1270 l = argvars[1].vval.v_list;
Christian Brabandtf7d31ad2024-04-16 22:23:17 +02001271 if (l == NULL || l->lv_len == 0)
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001272 return;
1273
1274 if (argvars[2].v_type != VAR_UNKNOWN)
1275 {
1276 prio = (int)tv_get_number_chk(&argvars[2], &error);
1277 if (argvars[3].v_type != VAR_UNKNOWN)
1278 {
1279 id = (int)tv_get_number_chk(&argvars[3], &error);
1280
1281 if (argvars[4].v_type != VAR_UNKNOWN
1282 && matchadd_dict_arg(&argvars[4], &conceal_char, &win) == FAIL)
1283 return;
1284 }
1285 }
1286 if (error == TRUE)
1287 return;
1288
1289 // id == 3 is ok because matchaddpos() is supposed to substitute :3match
1290 if (id == 1 || id == 2)
1291 {
Bram Moolenaar677658a2022-01-05 16:09:06 +00001292 semsg(_(e_id_is_reserved_for_match_nr), id);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001293 return;
1294 }
1295
1296 rettv->vval.v_number = match_add(win, group, NULL, prio, id, l,
1297 conceal_char);
1298# endif
1299}
1300
1301/*
1302 * "matcharg()" function
1303 */
1304 void
1305f_matcharg(typval_T *argvars UNUSED, typval_T *rettv)
1306{
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00001307 if (rettv_list_alloc(rettv) != OK)
1308 return;
1309
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001310# ifdef FEAT_SEARCH_EXTRA
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00001311 int id;
1312 matchitem_T *m;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001313
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00001314 if (in_vim9script() && check_for_number_arg(argvars, 0) == FAIL)
1315 return;
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001316
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00001317 id = (int)tv_get_number(&argvars[0]);
1318 if (id >= 1 && id <= 3)
1319 {
1320 if ((m = get_match(curwin, id)) != NULL)
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001321 {
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00001322 list_append_string(rettv->vval.v_list,
1323 syn_id2name(m->mit_hlg_id), -1);
1324 list_append_string(rettv->vval.v_list, m->mit_pattern, -1);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001325 }
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00001326 else
1327 {
1328 list_append_string(rettv->vval.v_list, NULL, -1);
1329 list_append_string(rettv->vval.v_list, NULL, -1);
1330 }
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001331 }
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00001332# endif
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001333}
1334
1335/*
1336 * "matchdelete()" function
1337 */
1338 void
1339f_matchdelete(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
1340{
1341# ifdef FEAT_SEARCH_EXTRA
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001342 win_T *win;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001343
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001344 if (in_vim9script()
1345 && (check_for_number_arg(argvars, 0) == FAIL
1346 || check_for_opt_number_arg(argvars, 1) == FAIL))
1347 return;
1348
1349 win = get_optional_window(argvars, 1);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001350 if (win == NULL)
1351 rettv->vval.v_number = -1;
1352 else
1353 rettv->vval.v_number = match_delete(win,
1354 (int)tv_get_number(&argvars[0]), TRUE);
1355# endif
1356}
1357#endif
1358
1359#if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
1360/*
1361 * ":[N]match {group} {pattern}"
1362 * Sets nextcmd to the start of the next command, if any. Also called when
1363 * skipping commands to find the next command.
1364 */
1365 void
1366ex_match(exarg_T *eap)
1367{
1368 char_u *p;
1369 char_u *g = NULL;
1370 char_u *end;
1371 int c;
1372 int id;
1373
1374 if (eap->line2 <= 3)
1375 id = eap->line2;
1376 else
1377 {
Bram Moolenaar451c2e32020-08-15 16:33:28 +02001378 emsg(_(e_invalid_command));
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001379 return;
1380 }
1381
1382 // First clear any old pattern.
1383 if (!eap->skip)
1384 match_delete(curwin, id, FALSE);
1385
1386 if (ends_excmd2(eap->cmd, eap->arg))
1387 end = eap->arg;
1388 else if ((STRNICMP(eap->arg, "none", 4) == 0
1389 && (VIM_ISWHITE(eap->arg[4])
1390 || ends_excmd2(eap->arg, eap->arg + 4))))
1391 end = eap->arg + 4;
1392 else
1393 {
1394 p = skiptowhite(eap->arg);
1395 if (!eap->skip)
1396 g = vim_strnsave(eap->arg, p - eap->arg);
1397 p = skipwhite(p);
1398 if (*p == NUL)
1399 {
1400 // There must be two arguments.
1401 vim_free(g);
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001402 semsg(_(e_invalid_argument_str), eap->arg);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001403 return;
1404 }
1405 end = skip_regexp(p + 1, *p, TRUE);
1406 if (!eap->skip)
1407 {
1408 if (*end != NUL && !ends_excmd2(end, skipwhite(end + 1)))
1409 {
1410 vim_free(g);
Bram Moolenaar74409f62022-01-01 15:58:22 +00001411 eap->errmsg = ex_errmsg(e_trailing_characters_str, end);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001412 return;
1413 }
1414 if (*end != *p)
1415 {
1416 vim_free(g);
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001417 semsg(_(e_invalid_argument_str), p);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001418 return;
1419 }
1420
1421 c = *end;
1422 *end = NUL;
1423 match_add(curwin, g, p + 1, 10, id, NULL, NULL);
1424 vim_free(g);
1425 *end = c;
1426 }
1427 }
1428 eap->nextcmd = find_nextcmd(end);
1429}
1430#endif