blob: bc50757b37a516f01af8dce87b8d8024a83b7ad8 [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 {
190 if (wp->w_buffer->b_mod_set)
191 {
192 if (wp->w_buffer->b_mod_top > toplnum)
193 wp->w_buffer->b_mod_top = toplnum;
194 if (wp->w_buffer->b_mod_bot < botlnum)
195 wp->w_buffer->b_mod_bot = botlnum;
196 }
197 else
198 {
199 wp->w_buffer->b_mod_set = TRUE;
200 wp->w_buffer->b_mod_top = toplnum;
201 wp->w_buffer->b_mod_bot = botlnum;
202 wp->w_buffer->b_mod_xlines = 0;
203 }
Bram Moolenaar50faf022022-09-29 12:50:17 +0100204 m->mit_toplnum = toplnum;
205 m->mit_botlnum = botlnum;
Bram Moolenaara4d158b2022-08-14 14:17:45 +0100206 rtype = UPD_VALID;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200207 }
208 }
209
210 // Insert new match. The match list is in ascending order with regard to
211 // the match priorities.
212 cur = wp->w_match_head;
213 prev = cur;
Bram Moolenaar50faf022022-09-29 12:50:17 +0100214 while (cur != NULL && prio >= cur->mit_priority)
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200215 {
216 prev = cur;
Bram Moolenaar50faf022022-09-29 12:50:17 +0100217 cur = cur->mit_next;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200218 }
219 if (cur == prev)
220 wp->w_match_head = m;
221 else
Bram Moolenaar50faf022022-09-29 12:50:17 +0100222 prev->mit_next = m;
223 m->mit_next = cur;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200224
225 redraw_win_later(wp, rtype);
226 return id;
227
228fail:
Bram Moolenaar50faf022022-09-29 12:50:17 +0100229 vim_free(m->mit_pattern);
230 vim_free(m->mit_pos_array);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200231 vim_free(m);
232 return -1;
233}
234
235/*
236 * Delete match with ID 'id' in the match list of window 'wp'.
237 * Print error messages if 'perr' is TRUE.
238 */
239 static int
240match_delete(win_T *wp, int id, int perr)
241{
242 matchitem_T *cur = wp->w_match_head;
243 matchitem_T *prev = cur;
Bram Moolenaara4d158b2022-08-14 14:17:45 +0100244 int rtype = UPD_SOME_VALID;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200245
246 if (id < 1)
247 {
248 if (perr == TRUE)
Bram Moolenaar50faf022022-09-29 12:50:17 +0100249 semsg(_(e_invalid_id_nr_must_be_greater_than_or_equal_to_one_2),
250 id);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200251 return -1;
252 }
Bram Moolenaar50faf022022-09-29 12:50:17 +0100253 while (cur != NULL && cur->mit_id != id)
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200254 {
255 prev = cur;
Bram Moolenaar50faf022022-09-29 12:50:17 +0100256 cur = cur->mit_next;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200257 }
258 if (cur == NULL)
259 {
260 if (perr == TRUE)
Bram Moolenaar9d00e4a2022-01-05 17:49:15 +0000261 semsg(_(e_id_not_found_nr), id);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200262 return -1;
263 }
264 if (cur == prev)
Bram Moolenaar50faf022022-09-29 12:50:17 +0100265 wp->w_match_head = cur->mit_next;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200266 else
Bram Moolenaar50faf022022-09-29 12:50:17 +0100267 prev->mit_next = cur->mit_next;
268 vim_regfree(cur->mit_match.regprog);
269 vim_free(cur->mit_pattern);
270 if (cur->mit_toplnum != 0)
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200271 {
272 if (wp->w_buffer->b_mod_set)
273 {
Bram Moolenaar50faf022022-09-29 12:50:17 +0100274 if (wp->w_buffer->b_mod_top > cur->mit_toplnum)
275 wp->w_buffer->b_mod_top = cur->mit_toplnum;
276 if (wp->w_buffer->b_mod_bot < cur->mit_botlnum)
277 wp->w_buffer->b_mod_bot = cur->mit_botlnum;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200278 }
279 else
280 {
281 wp->w_buffer->b_mod_set = TRUE;
Bram Moolenaar50faf022022-09-29 12:50:17 +0100282 wp->w_buffer->b_mod_top = cur->mit_toplnum;
283 wp->w_buffer->b_mod_bot = cur->mit_botlnum;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200284 wp->w_buffer->b_mod_xlines = 0;
285 }
Bram Moolenaara4d158b2022-08-14 14:17:45 +0100286 rtype = UPD_VALID;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200287 }
Bram Moolenaar50faf022022-09-29 12:50:17 +0100288 vim_free(cur->mit_pos_array);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200289 vim_free(cur);
290 redraw_win_later(wp, rtype);
291 return 0;
292}
293
294/*
295 * Delete all matches in the match list of window 'wp'.
296 */
297 void
298clear_matches(win_T *wp)
299{
300 matchitem_T *m;
301
302 while (wp->w_match_head != NULL)
303 {
Bram Moolenaar50faf022022-09-29 12:50:17 +0100304 m = wp->w_match_head->mit_next;
305 vim_regfree(wp->w_match_head->mit_match.regprog);
306 vim_free(wp->w_match_head->mit_pattern);
307 vim_free(wp->w_match_head->mit_pos_array);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200308 vim_free(wp->w_match_head);
309 wp->w_match_head = m;
310 }
Bram Moolenaara4d158b2022-08-14 14:17:45 +0100311 redraw_win_later(wp, UPD_SOME_VALID);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200312}
313
314/*
315 * Get match from ID 'id' in window 'wp'.
316 * Return NULL if match not found.
317 */
318 static matchitem_T *
319get_match(win_T *wp, int id)
320{
321 matchitem_T *cur = wp->w_match_head;
322
Bram Moolenaar50faf022022-09-29 12:50:17 +0100323 while (cur != NULL && cur->mit_id != id)
324 cur = cur->mit_next;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200325 return cur;
326}
327
328/*
329 * Init for calling prepare_search_hl().
330 */
331 void
332init_search_hl(win_T *wp, match_T *search_hl)
333{
334 matchitem_T *cur;
335
336 // Setup for match and 'hlsearch' highlighting. Disable any previous
337 // match
338 cur = wp->w_match_head;
339 while (cur != NULL)
340 {
Bram Moolenaar50faf022022-09-29 12:50:17 +0100341 cur->mit_hl.rm = cur->mit_match;
342 if (cur->mit_hlg_id == 0)
343 cur->mit_hl.attr = 0;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200344 else
Bram Moolenaar50faf022022-09-29 12:50:17 +0100345 cur->mit_hl.attr = syn_id2attr(cur->mit_hlg_id);
346 cur->mit_hl.buf = wp->w_buffer;
347 cur->mit_hl.lnum = 0;
348 cur->mit_hl.first_lnum = 0;
349 cur = cur->mit_next;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200350 }
351 search_hl->buf = wp->w_buffer;
352 search_hl->lnum = 0;
353 search_hl->first_lnum = 0;
354 // time limit is set at the toplevel, for all windows
355}
356
357/*
358 * If there is a match fill "shl" and return one.
359 * Return zero otherwise.
360 */
361 static int
362next_search_hl_pos(
363 match_T *shl, // points to a match
364 linenr_T lnum,
Bram Moolenaar50faf022022-09-29 12:50:17 +0100365 matchitem_T *match, // match item with positions
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200366 colnr_T mincol) // minimal column for a match
367{
368 int i;
369 int found = -1;
370
Bram Moolenaar50faf022022-09-29 12:50:17 +0100371 for (i = match->mit_pos_cur; i < match->mit_pos_count; i++)
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200372 {
Bram Moolenaar50faf022022-09-29 12:50:17 +0100373 llpos_T *pos = &match->mit_pos_array[i];
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200374
375 if (pos->lnum == 0)
376 break;
377 if (pos->len == 0 && pos->col < mincol)
378 continue;
379 if (pos->lnum == lnum)
380 {
381 if (found >= 0)
382 {
Bram Moolenaar50faf022022-09-29 12:50:17 +0100383 // if this match comes before the one at "found" then swap them
384 if (pos->col < match->mit_pos_array[found].col)
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200385 {
386 llpos_T tmp = *pos;
387
Bram Moolenaar50faf022022-09-29 12:50:17 +0100388 *pos = match->mit_pos_array[found];
389 match->mit_pos_array[found] = tmp;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200390 }
391 }
392 else
393 found = i;
394 }
395 }
Bram Moolenaar50faf022022-09-29 12:50:17 +0100396 match->mit_pos_cur = 0;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200397 if (found >= 0)
398 {
Bram Moolenaar50faf022022-09-29 12:50:17 +0100399 colnr_T start = match->mit_pos_array[found].col == 0
400 ? 0 : match->mit_pos_array[found].col - 1;
401 colnr_T end = match->mit_pos_array[found].col == 0
402 ? MAXCOL : start + match->mit_pos_array[found].len;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200403
404 shl->lnum = lnum;
405 shl->rm.startpos[0].lnum = 0;
406 shl->rm.startpos[0].col = start;
407 shl->rm.endpos[0].lnum = 0;
408 shl->rm.endpos[0].col = end;
409 shl->is_addpos = TRUE;
Bram Moolenaar693ccd12022-04-16 12:04:37 +0100410 shl->has_cursor = FALSE;
Bram Moolenaar50faf022022-09-29 12:50:17 +0100411 match->mit_pos_cur = found + 1;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200412 return 1;
413 }
414 return 0;
415}
416
417/*
418 * Search for a next 'hlsearch' or match.
419 * Uses shl->buf.
420 * Sets shl->lnum and shl->rm contents.
421 * Note: Assumes a previous match is always before "lnum", unless
422 * shl->lnum is zero.
423 * Careful: Any pointers for buffer lines will become invalid.
424 */
425 static void
426next_search_hl(
427 win_T *win,
428 match_T *search_hl,
429 match_T *shl, // points to search_hl or a match
430 linenr_T lnum,
431 colnr_T mincol, // minimal column for a match
432 matchitem_T *cur) // to retrieve match positions if any
433{
434 linenr_T l;
435 colnr_T matchcol;
436 long nmatched;
437 int called_emsg_before = called_emsg;
Bram Moolenaar88456cd2022-11-18 22:14:09 +0000438 int timed_out = FALSE;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200439
440 // for :{range}s/pat only highlight inside the range
Bram Moolenaar94fb8272021-12-29 19:22:44 +0000441 if ((lnum < search_first_line || lnum > search_last_line) && cur == NULL)
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200442 {
443 shl->lnum = 0;
444 return;
445 }
446
447 if (shl->lnum != 0)
448 {
449 // Check for three situations:
450 // 1. If the "lnum" is below a previous match, start a new search.
451 // 2. If the previous match includes "mincol", use it.
452 // 3. Continue after the previous match.
453 l = shl->lnum + shl->rm.endpos[0].lnum - shl->rm.startpos[0].lnum;
454 if (lnum > l)
455 shl->lnum = 0;
456 else if (lnum < l || shl->rm.endpos[0].col > mincol)
457 return;
458 }
459
460 // Repeat searching for a match until one is found that includes "mincol"
461 // or none is found in this line.
462 for (;;)
463 {
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200464 // Three situations:
465 // 1. No useful previous match: search from start of line.
466 // 2. Not Vi compatible or empty match: continue at next character.
467 // Break the loop if this is beyond the end of the line.
468 // 3. Vi compatible searching: continue at end of previous match.
469 if (shl->lnum == 0)
470 matchcol = 0;
471 else if (vim_strchr(p_cpo, CPO_SEARCH) == NULL
472 || (shl->rm.endpos[0].lnum == 0
473 && shl->rm.endpos[0].col <= shl->rm.startpos[0].col))
474 {
475 char_u *ml;
476
477 matchcol = shl->rm.startpos[0].col;
478 ml = ml_get_buf(shl->buf, lnum, FALSE) + matchcol;
479 if (*ml == NUL)
480 {
481 ++matchcol;
482 shl->lnum = 0;
483 break;
484 }
485 if (has_mbyte)
486 matchcol += mb_ptr2len(ml);
487 else
488 ++matchcol;
489 }
490 else
491 matchcol = shl->rm.endpos[0].col;
492
493 shl->lnum = lnum;
494 if (shl->rm.regprog != NULL)
495 {
496 // Remember whether shl->rm is using a copy of the regprog in
Bram Moolenaar50faf022022-09-29 12:50:17 +0100497 // cur->mit_match.
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200498 int regprog_is_copy = (shl != search_hl && cur != NULL
Bram Moolenaar50faf022022-09-29 12:50:17 +0100499 && shl == &cur->mit_hl
500 && cur->mit_match.regprog == cur->mit_hl.rm.regprog);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200501
502 nmatched = vim_regexec_multi(&shl->rm, win, shl->buf, lnum,
Paul Ollis65745772022-06-05 16:55:54 +0100503 matchcol, &timed_out);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200504 // Copy the regprog, in case it got freed and recompiled.
505 if (regprog_is_copy)
Bram Moolenaar50faf022022-09-29 12:50:17 +0100506 cur->mit_match.regprog = cur->mit_hl.rm.regprog;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200507
508 if (called_emsg > called_emsg_before || got_int || timed_out)
509 {
510 // Error while handling regexp: stop using this regexp.
511 if (shl == search_hl)
512 {
513 // don't free regprog in the match list, it's a copy
514 vim_regfree(shl->rm.regprog);
515 set_no_hlsearch(TRUE);
516 }
517 shl->rm.regprog = NULL;
518 shl->lnum = 0;
519 got_int = FALSE; // avoid the "Type :quit to exit Vim" message
520 break;
521 }
522 }
523 else if (cur != NULL)
Bram Moolenaar50faf022022-09-29 12:50:17 +0100524 nmatched = next_search_hl_pos(shl, lnum, cur, matchcol);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200525 else
526 nmatched = 0;
527 if (nmatched == 0)
528 {
529 shl->lnum = 0; // no match found
530 break;
531 }
532 if (shl->rm.startpos[0].lnum > 0
533 || shl->rm.startpos[0].col >= mincol
534 || nmatched > 1
535 || shl->rm.endpos[0].col > mincol)
536 {
537 shl->lnum += shl->rm.startpos[0].lnum;
538 break; // useful match found
539 }
540 }
541}
542
543/*
544 * Advance to the match in window "wp" line "lnum" or past it.
545 */
546 void
547prepare_search_hl(win_T *wp, match_T *search_hl, linenr_T lnum)
548{
549 matchitem_T *cur; // points to the match list
550 match_T *shl; // points to search_hl or a match
551 int shl_flag; // flag to indicate whether search_hl
552 // has been processed or not
553 int pos_inprogress; // marks that position match search is
554 // in progress
555 int n;
556
557 // When using a multi-line pattern, start searching at the top
558 // of the window or just after a closed fold.
559 // Do this both for search_hl and the match list.
560 cur = wp->w_match_head;
561 shl_flag = WIN_IS_POPUP(wp); // skip search_hl in a popup window
562 while (cur != NULL || shl_flag == FALSE)
563 {
564 if (shl_flag == FALSE)
565 {
566 shl = search_hl;
567 shl_flag = TRUE;
568 }
569 else
Bram Moolenaar50faf022022-09-29 12:50:17 +0100570 shl = &cur->mit_hl;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200571 if (shl->rm.regprog != NULL
572 && shl->lnum == 0
573 && re_multiline(shl->rm.regprog))
574 {
575 if (shl->first_lnum == 0)
576 {
577# ifdef FEAT_FOLDING
578 for (shl->first_lnum = lnum;
579 shl->first_lnum > wp->w_topline; --shl->first_lnum)
580 if (hasFoldingWin(wp, shl->first_lnum - 1,
581 NULL, NULL, TRUE, NULL))
582 break;
583# else
584 shl->first_lnum = wp->w_topline;
585# endif
586 }
587 if (cur != NULL)
Bram Moolenaar50faf022022-09-29 12:50:17 +0100588 cur->mit_pos_cur = 0;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200589 pos_inprogress = TRUE;
590 n = 0;
591 while (shl->first_lnum < lnum && (shl->rm.regprog != NULL
592 || (cur != NULL && pos_inprogress)))
593 {
594 next_search_hl(wp, search_hl, shl, shl->first_lnum, (colnr_T)n,
595 shl == search_hl ? NULL : cur);
Bram Moolenaar50faf022022-09-29 12:50:17 +0100596 pos_inprogress = cur == NULL || cur->mit_pos_cur == 0
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200597 ? FALSE : TRUE;
598 if (shl->lnum != 0)
599 {
600 shl->first_lnum = shl->lnum
601 + shl->rm.endpos[0].lnum
602 - shl->rm.startpos[0].lnum;
603 n = shl->rm.endpos[0].col;
604 }
605 else
606 {
607 ++shl->first_lnum;
608 n = 0;
609 }
610 }
611 }
612 if (shl != search_hl && cur != NULL)
Bram Moolenaar50faf022022-09-29 12:50:17 +0100613 cur = cur->mit_next;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200614 }
615}
616
617/*
Bram Moolenaar9b367502022-04-22 20:07:21 +0100618 * Update "shl->has_cursor" based on the match in "shl" and the cursor
619 * position.
620 */
621 static void
622check_cur_search_hl(win_T *wp, match_T *shl)
623{
zeertzjq8279cfe2022-04-23 12:05:51 +0100624 linenr_T linecount = shl->rm.endpos[0].lnum - shl->rm.startpos[0].lnum;
Bram Moolenaar9b367502022-04-22 20:07:21 +0100625
626 if (wp->w_cursor.lnum >= shl->lnum
zeertzjq8279cfe2022-04-23 12:05:51 +0100627 && wp->w_cursor.lnum <= shl->lnum + linecount
Bram Moolenaar9b367502022-04-22 20:07:21 +0100628 && (wp->w_cursor.lnum > shl->lnum
629 || wp->w_cursor.col >= shl->rm.startpos[0].col)
630 && (wp->w_cursor.lnum < shl->lnum + linecount
631 || wp->w_cursor.col < shl->rm.endpos[0].col))
632 shl->has_cursor = TRUE;
633 else
634 shl->has_cursor = FALSE;
635}
636
637/*
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200638 * Prepare for 'hlsearch' and match highlighting in one window line.
639 * Return TRUE if there is such highlighting and set "search_attr" to the
640 * current highlight attribute.
641 */
642 int
643prepare_search_hl_line(
644 win_T *wp,
645 linenr_T lnum,
646 colnr_T mincol,
647 char_u **line,
648 match_T *search_hl,
649 int *search_attr)
650{
651 matchitem_T *cur; // points to the match list
652 match_T *shl; // points to search_hl or a match
653 int shl_flag; // flag to indicate whether search_hl
654 // has been processed or not
655 int area_highlighting = FALSE;
656
657 // Handle highlighting the last used search pattern and matches.
658 // Do this for both search_hl and the match list.
659 // Do not use search_hl in a popup window.
660 cur = wp->w_match_head;
661 shl_flag = WIN_IS_POPUP(wp);
662 while (cur != NULL || shl_flag == FALSE)
663 {
664 if (shl_flag == FALSE)
665 {
666 shl = search_hl;
667 shl_flag = TRUE;
668 }
669 else
Bram Moolenaar50faf022022-09-29 12:50:17 +0100670 shl = &cur->mit_hl;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200671 shl->startcol = MAXCOL;
672 shl->endcol = MAXCOL;
673 shl->attr_cur = 0;
674 shl->is_addpos = FALSE;
Bram Moolenaar693ccd12022-04-16 12:04:37 +0100675 shl->has_cursor = FALSE;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200676 if (cur != NULL)
Bram Moolenaar50faf022022-09-29 12:50:17 +0100677 cur->mit_pos_cur = 0;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200678 next_search_hl(wp, search_hl, shl, lnum, mincol,
679 shl == search_hl ? NULL : cur);
680
681 // Need to get the line again, a multi-line regexp may have made it
682 // invalid.
683 *line = ml_get_buf(wp->w_buffer, lnum, FALSE);
684
685 if (shl->lnum != 0 && shl->lnum <= lnum)
686 {
687 if (shl->lnum == lnum)
688 shl->startcol = shl->rm.startpos[0].col;
689 else
690 shl->startcol = 0;
691 if (lnum == shl->lnum + shl->rm.endpos[0].lnum
692 - shl->rm.startpos[0].lnum)
693 shl->endcol = shl->rm.endpos[0].col;
694 else
695 shl->endcol = MAXCOL;
Bram Moolenaar693ccd12022-04-16 12:04:37 +0100696
697 // check if the cursor is in the match before changing the columns
Bram Moolenaar9b367502022-04-22 20:07:21 +0100698 if (shl == search_hl)
699 check_cur_search_hl(wp, shl);
Bram Moolenaar693ccd12022-04-16 12:04:37 +0100700
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200701 // Highlight one character for an empty match.
702 if (shl->startcol == shl->endcol)
703 {
704 if (has_mbyte && (*line)[shl->endcol] != NUL)
705 shl->endcol += (*mb_ptr2len)((*line) + shl->endcol);
706 else
707 ++shl->endcol;
708 }
709 if ((long)shl->startcol < mincol) // match at leftcol
710 {
711 shl->attr_cur = shl->attr;
712 *search_attr = shl->attr;
713 }
714 area_highlighting = TRUE;
715 }
716 if (shl != search_hl && cur != NULL)
Bram Moolenaar50faf022022-09-29 12:50:17 +0100717 cur = cur->mit_next;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200718 }
719 return area_highlighting;
720}
721
722/*
723 * For a position in a line: Check for start/end of 'hlsearch' and other
724 * matches.
725 * After end, check for start/end of next match.
726 * When another match, have to check for start again.
727 * Watch out for matching an empty string!
Bram Moolenaar0c359af2021-11-29 19:18:57 +0000728 * "on_last_col" is set to TRUE with non-zero search_attr and the next column
729 * is endcol.
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200730 * Return the updated search_attr.
731 */
732 int
733update_search_hl(
734 win_T *wp,
735 linenr_T lnum,
736 colnr_T col,
737 char_u **line,
738 match_T *search_hl,
739 int *has_match_conc UNUSED,
740 int *match_conc UNUSED,
741 int did_line_attr,
Bram Moolenaar0c359af2021-11-29 19:18:57 +0000742 int lcs_eol_one,
743 int *on_last_col)
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200744{
745 matchitem_T *cur; // points to the match list
746 match_T *shl; // points to search_hl or a match
747 int shl_flag; // flag to indicate whether search_hl
748 // has been processed or not
749 int pos_inprogress; // marks that position match search is in
750 // progress
751 int search_attr = 0;
752
753
754 // Do this for 'search_hl' and the match list (ordered by priority).
755 cur = wp->w_match_head;
756 shl_flag = WIN_IS_POPUP(wp);
757 while (cur != NULL || shl_flag == FALSE)
758 {
759 if (shl_flag == FALSE
760 && (cur == NULL
Bram Moolenaar50faf022022-09-29 12:50:17 +0100761 || cur->mit_priority > SEARCH_HL_PRIORITY))
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200762 {
763 shl = search_hl;
764 shl_flag = TRUE;
765 }
766 else
Bram Moolenaar50faf022022-09-29 12:50:17 +0100767 shl = &cur->mit_hl;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200768 if (cur != NULL)
Bram Moolenaar50faf022022-09-29 12:50:17 +0100769 cur->mit_pos_cur = 0;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200770 pos_inprogress = TRUE;
771 while (shl->rm.regprog != NULL || (cur != NULL && pos_inprogress))
772 {
773 if (shl->startcol != MAXCOL
774 && col >= shl->startcol
775 && col < shl->endcol)
776 {
777 int next_col = col + mb_ptr2len(*line + col);
778
779 if (shl->endcol < next_col)
780 shl->endcol = next_col;
781 shl->attr_cur = shl->attr;
782# ifdef FEAT_CONCEAL
783 // Match with the "Conceal" group results in hiding
784 // the match.
785 if (cur != NULL
786 && shl != search_hl
Bram Moolenaar50faf022022-09-29 12:50:17 +0100787 && syn_name2id((char_u *)"Conceal") == cur->mit_hlg_id)
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200788 {
789 *has_match_conc = col == shl->startcol ? 2 : 1;
Bram Moolenaar50faf022022-09-29 12:50:17 +0100790 *match_conc = cur->mit_conceal_char;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200791 }
792 else
793 *has_match_conc = 0;
794# endif
LemonBoya4399382022-04-09 21:04:08 +0100795 // Highlight the match were the cursor is using the CurSearch
796 // group.
Bram Moolenaar693ccd12022-04-16 12:04:37 +0100797 if (shl == search_hl && shl->has_cursor)
Bram Moolenaar368137a2022-05-31 13:43:12 +0100798 {
LemonBoya4399382022-04-09 21:04:08 +0100799 shl->attr_cur = HL_ATTR(HLF_LC);
Bram Moolenaar368137a2022-05-31 13:43:12 +0100800 if (shl->attr_cur != shl->attr)
801 search_hl_has_cursor_lnum = lnum;
802 }
LemonBoya4399382022-04-09 21:04:08 +0100803
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200804 }
805 else if (col == shl->endcol)
806 {
807 shl->attr_cur = 0;
808 next_search_hl(wp, search_hl, shl, lnum, col,
809 shl == search_hl ? NULL : cur);
Bram Moolenaar50faf022022-09-29 12:50:17 +0100810 pos_inprogress = !(cur == NULL || cur->mit_pos_cur == 0);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200811
812 // Need to get the line again, a multi-line regexp may have
813 // made it invalid.
814 *line = ml_get_buf(wp->w_buffer, lnum, FALSE);
815
816 if (shl->lnum == lnum)
817 {
818 shl->startcol = shl->rm.startpos[0].col;
819 if (shl->rm.endpos[0].lnum == 0)
820 shl->endcol = shl->rm.endpos[0].col;
821 else
822 shl->endcol = MAXCOL;
823
Bram Moolenaar9b367502022-04-22 20:07:21 +0100824 // check if the cursor is in the match
825 if (shl == search_hl)
826 check_cur_search_hl(wp, shl);
827
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200828 if (shl->startcol == shl->endcol)
829 {
830 // highlight empty match, try again after
831 // it
832 if (has_mbyte)
Bram Moolenaar41f08952021-02-22 22:13:49 +0100833 {
834 char_u *p = *line + shl->endcol;
835
836 if (*p == NUL)
837 // consistent with non-mbyte
838 ++shl->endcol;
839 else
840 shl->endcol += (*mb_ptr2len)(p);
841 }
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200842 else
843 ++shl->endcol;
844 }
845
846 // Loop to check if the match starts at the
847 // current position
848 continue;
849 }
850 }
851 break;
852 }
853 if (shl != search_hl && cur != NULL)
Bram Moolenaar50faf022022-09-29 12:50:17 +0100854 cur = cur->mit_next;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200855 }
856
857 // Use attributes from match with highest priority among 'search_hl' and
858 // the match list.
859 cur = wp->w_match_head;
860 shl_flag = WIN_IS_POPUP(wp);
861 while (cur != NULL || shl_flag == FALSE)
862 {
863 if (shl_flag == FALSE
864 && (cur == NULL ||
Bram Moolenaar50faf022022-09-29 12:50:17 +0100865 cur->mit_priority > SEARCH_HL_PRIORITY))
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200866 {
867 shl = search_hl;
868 shl_flag = TRUE;
869 }
870 else
Bram Moolenaar50faf022022-09-29 12:50:17 +0100871 shl = &cur->mit_hl;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200872 if (shl->attr_cur != 0)
Bram Moolenaar0c359af2021-11-29 19:18:57 +0000873 {
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200874 search_attr = shl->attr_cur;
Bram Moolenaar0c359af2021-11-29 19:18:57 +0000875 *on_last_col = col + 1 >= shl->endcol;
876 }
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200877 if (shl != search_hl && cur != NULL)
Bram Moolenaar50faf022022-09-29 12:50:17 +0100878 cur = cur->mit_next;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200879 }
880 // Only highlight one character after the last column.
881 if (*(*line + col) == NUL && (did_line_attr >= 1
882 || (wp->w_p_list && lcs_eol_one == -1)))
883 search_attr = 0;
884 return search_attr;
885}
886
887 int
888get_prevcol_hl_flag(win_T *wp, match_T *search_hl, long curcol)
889{
890 long prevcol = curcol;
891 int prevcol_hl_flag = FALSE;
892 matchitem_T *cur; // points to the match list
893
Bram Moolenaar41f08952021-02-22 22:13:49 +0100894#if defined(FEAT_PROP_POPUP)
895 // don't do this in a popup window
896 if (popup_is_popup(wp))
897 return FALSE;
898#endif
899
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200900 // we're not really at that column when skipping some text
901 if ((long)(wp->w_p_wrap ? wp->w_skipcol : wp->w_leftcol) > prevcol)
902 ++prevcol;
903
Bram Moolenaar41f08952021-02-22 22:13:49 +0100904 // Highlight a character after the end of the line if the match started
905 // at the end of the line or when the match continues in the next line
906 // (match includes the line break).
907 if (!search_hl->is_addpos && (prevcol == (long)search_hl->startcol
908 || (prevcol > (long)search_hl->startcol
909 && search_hl->endcol == MAXCOL)))
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200910 prevcol_hl_flag = TRUE;
911 else
912 {
913 cur = wp->w_match_head;
914 while (cur != NULL)
915 {
Bram Moolenaar50faf022022-09-29 12:50:17 +0100916 if (!cur->mit_hl.is_addpos && (prevcol == (long)cur->mit_hl.startcol
917 || (prevcol > (long)cur->mit_hl.startcol
918 && cur->mit_hl.endcol == MAXCOL)))
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200919 {
920 prevcol_hl_flag = TRUE;
921 break;
922 }
Bram Moolenaar50faf022022-09-29 12:50:17 +0100923 cur = cur->mit_next;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200924 }
925 }
926 return prevcol_hl_flag;
927}
928
929/*
930 * Get highlighting for the char after the text in "char_attr" from 'hlsearch'
931 * or match highlighting.
932 */
933 void
934get_search_match_hl(win_T *wp, match_T *search_hl, long col, int *char_attr)
935{
936 matchitem_T *cur; // points to the match list
937 match_T *shl; // points to search_hl or a match
938 int shl_flag; // flag to indicate whether search_hl
939 // has been processed or not
940
941 cur = wp->w_match_head;
942 shl_flag = WIN_IS_POPUP(wp);
943 while (cur != NULL || shl_flag == FALSE)
944 {
945 if (shl_flag == FALSE
946 && ((cur != NULL
Bram Moolenaar50faf022022-09-29 12:50:17 +0100947 && cur->mit_priority > SEARCH_HL_PRIORITY)
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200948 || cur == NULL))
949 {
950 shl = search_hl;
951 shl_flag = TRUE;
952 }
953 else
Bram Moolenaar50faf022022-09-29 12:50:17 +0100954 shl = &cur->mit_hl;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200955 if (col - 1 == (long)shl->startcol
956 && (shl == search_hl || !shl->is_addpos))
957 *char_attr = shl->attr;
958 if (shl != search_hl && cur != NULL)
Bram Moolenaar50faf022022-09-29 12:50:17 +0100959 cur = cur->mit_next;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200960 }
961}
962
963#endif // FEAT_SEARCH_EXTRA
964
965#if defined(FEAT_EVAL) || defined(PROTO)
966# ifdef FEAT_SEARCH_EXTRA
967 static int
968matchadd_dict_arg(typval_T *tv, char_u **conceal_char, win_T **win)
969{
970 dictitem_T *di;
971
972 if (tv->v_type != VAR_DICT)
973 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +0000974 emsg(_(e_dictionary_required));
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200975 return FAIL;
976 }
977
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +0100978 if (dict_has_key(tv->vval.v_dict, "conceal"))
Bram Moolenaard61efa52022-07-23 09:52:04 +0100979 *conceal_char = dict_get_string(tv->vval.v_dict, "conceal", FALSE);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200980
Yegappan Lakshmanane8575982023-01-14 12:32:28 +0000981 if ((di = dict_find(tv->vval.v_dict, (char_u *)"window", -1)) == NULL)
982 return OK;
983
984 *win = find_win_by_nr_or_id(&di->di_tv);
985 if (*win == NULL)
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200986 {
Yegappan Lakshmanane8575982023-01-14 12:32:28 +0000987 emsg(_(e_invalid_window_number));
988 return FAIL;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200989 }
990
991 return OK;
992}
993#endif
994
995/*
996 * "clearmatches()" function
997 */
998 void
999f_clearmatches(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
1000{
1001#ifdef FEAT_SEARCH_EXTRA
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001002 win_T *win;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001003
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001004 if (in_vim9script() && check_for_opt_number_arg(argvars, 0) == FAIL)
1005 return;
1006
1007 win = get_optional_window(argvars, 0);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001008 if (win != NULL)
1009 clear_matches(win);
1010#endif
1011}
1012
1013/*
1014 * "getmatches()" function
1015 */
1016 void
1017f_getmatches(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
1018{
1019# ifdef FEAT_SEARCH_EXTRA
1020 dict_T *dict;
1021 matchitem_T *cur;
1022 int i;
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001023 win_T *win;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001024
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001025 if (in_vim9script() && check_for_opt_number_arg(argvars, 0) == FAIL)
1026 return;
1027
1028 win = get_optional_window(argvars, 0);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001029 if (rettv_list_alloc(rettv) == FAIL || win == NULL)
1030 return;
1031
1032 cur = win->w_match_head;
1033 while (cur != NULL)
1034 {
1035 dict = dict_alloc();
1036 if (dict == NULL)
1037 return;
Bram Moolenaar50faf022022-09-29 12:50:17 +01001038 if (cur->mit_match.regprog == NULL)
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001039 {
1040 // match added with matchaddpos()
Bram Moolenaar50faf022022-09-29 12:50:17 +01001041 for (i = 0; i < cur->mit_pos_count; ++i)
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001042 {
1043 llpos_T *llpos;
1044 char buf[30]; // use 30 to avoid compiler warning
1045 list_T *l;
1046
Bram Moolenaar50faf022022-09-29 12:50:17 +01001047 llpos = &cur->mit_pos_array[i];
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001048 if (llpos->lnum == 0)
1049 break;
1050 l = list_alloc();
1051 if (l == NULL)
1052 break;
1053 list_append_number(l, (varnumber_T)llpos->lnum);
1054 if (llpos->col > 0)
1055 {
1056 list_append_number(l, (varnumber_T)llpos->col);
1057 list_append_number(l, (varnumber_T)llpos->len);
1058 }
1059 sprintf(buf, "pos%d", i + 1);
1060 dict_add_list(dict, buf, l);
1061 }
1062 }
1063 else
1064 {
Bram Moolenaar50faf022022-09-29 12:50:17 +01001065 dict_add_string(dict, "pattern", cur->mit_pattern);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001066 }
Bram Moolenaar50faf022022-09-29 12:50:17 +01001067 dict_add_string(dict, "group", syn_id2name(cur->mit_hlg_id));
1068 dict_add_number(dict, "priority", (long)cur->mit_priority);
1069 dict_add_number(dict, "id", (long)cur->mit_id);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001070# if defined(FEAT_CONCEAL)
Bram Moolenaar50faf022022-09-29 12:50:17 +01001071 if (cur->mit_conceal_char)
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001072 {
1073 char_u buf[MB_MAXBYTES + 1];
1074
Bram Moolenaar50faf022022-09-29 12:50:17 +01001075 buf[(*mb_char2bytes)(cur->mit_conceal_char, buf)] = NUL;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001076 dict_add_string(dict, "conceal", (char_u *)&buf);
1077 }
1078# endif
1079 list_append_dict(rettv->vval.v_list, dict);
Bram Moolenaar50faf022022-09-29 12:50:17 +01001080 cur = cur->mit_next;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001081 }
1082# endif
1083}
1084
1085/*
1086 * "setmatches()" function
1087 */
1088 void
1089f_setmatches(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
1090{
1091#ifdef FEAT_SEARCH_EXTRA
1092 list_T *l;
1093 listitem_T *li;
1094 dict_T *d;
1095 list_T *s = NULL;
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02001096 win_T *win;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001097
1098 rettv->vval.v_number = -1;
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02001099
1100 if (in_vim9script()
1101 && (check_for_list_arg(argvars, 0) == FAIL
1102 || check_for_opt_number_arg(argvars, 1) == FAIL))
1103 return;
1104
Bram Moolenaard83392a2022-09-01 12:22:46 +01001105 if (check_for_list_arg(argvars, 0) == FAIL)
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001106 return;
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02001107 win = get_optional_window(argvars, 1);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001108 if (win == NULL)
1109 return;
1110
1111 if ((l = argvars[0].vval.v_list) != NULL)
1112 {
1113 // To some extent make sure that we are dealing with a list from
1114 // "getmatches()".
1115 li = l->lv_first;
1116 while (li != NULL)
1117 {
1118 if (li->li_tv.v_type != VAR_DICT
1119 || (d = li->li_tv.vval.v_dict) == NULL)
1120 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001121 emsg(_(e_invalid_argument));
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001122 return;
1123 }
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01001124 if (!(dict_has_key(d, "group")
1125 && (dict_has_key(d, "pattern")
1126 || dict_has_key(d, "pos1"))
1127 && dict_has_key(d, "priority")
1128 && dict_has_key(d, "id")))
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001129 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001130 emsg(_(e_invalid_argument));
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001131 return;
1132 }
1133 li = li->li_next;
1134 }
1135
1136 clear_matches(win);
1137 li = l->lv_first;
1138 while (li != NULL)
1139 {
1140 int i = 0;
1141 char buf[30]; // use 30 to avoid compiler warning
1142 dictitem_T *di;
1143 char_u *group;
1144 int priority;
1145 int id;
1146 char_u *conceal;
1147
1148 d = li->li_tv.vval.v_dict;
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01001149 if (!dict_has_key(d, "pattern"))
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001150 {
1151 if (s == NULL)
1152 {
1153 s = list_alloc();
1154 if (s == NULL)
1155 return;
1156 }
1157
1158 // match from matchaddpos()
1159 for (i = 1; i < 9; i++)
1160 {
1161 sprintf((char *)buf, (char *)"pos%d", i);
1162 if ((di = dict_find(d, (char_u *)buf, -1)) != NULL)
1163 {
1164 if (di->di_tv.v_type != VAR_LIST)
1165 return;
1166
1167 list_append_tv(s, &di->di_tv);
1168 s->lv_refcount++;
1169 }
1170 else
1171 break;
1172 }
1173 }
1174
Bram Moolenaard61efa52022-07-23 09:52:04 +01001175 group = dict_get_string(d, "group", TRUE);
1176 priority = (int)dict_get_number(d, "priority");
1177 id = (int)dict_get_number(d, "id");
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01001178 conceal = dict_has_key(d, "conceal")
Bram Moolenaard61efa52022-07-23 09:52:04 +01001179 ? dict_get_string(d, "conceal", TRUE)
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001180 : NULL;
1181 if (i == 0)
1182 {
1183 match_add(win, group,
Bram Moolenaard61efa52022-07-23 09:52:04 +01001184 dict_get_string(d, "pattern", FALSE),
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001185 priority, id, NULL, conceal);
1186 }
1187 else
1188 {
1189 match_add(win, group, NULL, priority, id, s, conceal);
1190 list_unref(s);
1191 s = NULL;
1192 }
1193 vim_free(group);
1194 vim_free(conceal);
1195
1196 li = li->li_next;
1197 }
1198 rettv->vval.v_number = 0;
1199 }
1200#endif
1201}
1202
1203/*
1204 * "matchadd()" function
1205 */
1206 void
1207f_matchadd(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
1208{
1209# ifdef FEAT_SEARCH_EXTRA
1210 char_u buf[NUMBUFLEN];
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02001211 char_u *grp; // group
1212 char_u *pat; // pattern
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001213 int prio = 10; // default priority
1214 int id = -1;
1215 int error = FALSE;
1216 char_u *conceal_char = NULL;
1217 win_T *win = curwin;
1218
1219 rettv->vval.v_number = -1;
1220
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02001221 if (in_vim9script()
1222 && (check_for_string_arg(argvars, 0) == FAIL
1223 || check_for_string_arg(argvars, 1) == FAIL
1224 || check_for_opt_number_arg(argvars, 2) == FAIL
1225 || (argvars[2].v_type != VAR_UNKNOWN
1226 && (check_for_opt_number_arg(argvars, 3) == FAIL
1227 || (argvars[3].v_type != VAR_UNKNOWN
1228 && check_for_opt_dict_arg(argvars, 4) == FAIL)))))
1229 return;
1230
1231 grp = tv_get_string_buf_chk(&argvars[0], buf); // group
1232 pat = tv_get_string_buf_chk(&argvars[1], buf); // pattern
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001233 if (grp == NULL || pat == NULL)
1234 return;
1235 if (argvars[2].v_type != VAR_UNKNOWN)
1236 {
1237 prio = (int)tv_get_number_chk(&argvars[2], &error);
1238 if (argvars[3].v_type != VAR_UNKNOWN)
1239 {
1240 id = (int)tv_get_number_chk(&argvars[3], &error);
1241 if (argvars[4].v_type != VAR_UNKNOWN
1242 && matchadd_dict_arg(&argvars[4], &conceal_char, &win) == FAIL)
1243 return;
1244 }
1245 }
1246 if (error == TRUE)
1247 return;
1248 if (id >= 1 && id <= 3)
1249 {
Bram Moolenaar677658a2022-01-05 16:09:06 +00001250 semsg(_(e_id_is_reserved_for_match_nr), id);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001251 return;
1252 }
1253
1254 rettv->vval.v_number = match_add(win, grp, pat, prio, id, NULL,
1255 conceal_char);
1256# endif
1257}
1258
1259/*
1260 * "matchaddpos()" function
1261 */
1262 void
1263f_matchaddpos(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
1264{
1265# ifdef FEAT_SEARCH_EXTRA
1266 char_u buf[NUMBUFLEN];
1267 char_u *group;
1268 int prio = 10;
1269 int id = -1;
1270 int error = FALSE;
1271 list_T *l;
1272 char_u *conceal_char = NULL;
1273 win_T *win = curwin;
1274
1275 rettv->vval.v_number = -1;
1276
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02001277 if (in_vim9script()
1278 && (check_for_string_arg(argvars, 0) == FAIL
1279 || check_for_list_arg(argvars, 1) == FAIL
1280 || check_for_opt_number_arg(argvars, 2) == FAIL
1281 || (argvars[2].v_type != VAR_UNKNOWN
1282 && (check_for_opt_number_arg(argvars, 3) == FAIL
1283 || (argvars[3].v_type != VAR_UNKNOWN
1284 && check_for_opt_dict_arg(argvars, 4) == FAIL)))))
1285 return;
1286
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001287 group = tv_get_string_buf_chk(&argvars[0], buf);
1288 if (group == NULL)
1289 return;
1290
1291 if (argvars[1].v_type != VAR_LIST)
1292 {
Bram Moolenaar3a846e62022-01-01 16:21:00 +00001293 semsg(_(e_argument_of_str_must_be_list), "matchaddpos()");
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001294 return;
1295 }
1296 l = argvars[1].vval.v_list;
Christian Brabandtf7d31ad2024-04-16 22:23:17 +02001297 if (l == NULL || l->lv_len == 0)
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001298 return;
1299
1300 if (argvars[2].v_type != VAR_UNKNOWN)
1301 {
1302 prio = (int)tv_get_number_chk(&argvars[2], &error);
1303 if (argvars[3].v_type != VAR_UNKNOWN)
1304 {
1305 id = (int)tv_get_number_chk(&argvars[3], &error);
1306
1307 if (argvars[4].v_type != VAR_UNKNOWN
1308 && matchadd_dict_arg(&argvars[4], &conceal_char, &win) == FAIL)
1309 return;
1310 }
1311 }
1312 if (error == TRUE)
1313 return;
1314
1315 // id == 3 is ok because matchaddpos() is supposed to substitute :3match
1316 if (id == 1 || id == 2)
1317 {
Bram Moolenaar677658a2022-01-05 16:09:06 +00001318 semsg(_(e_id_is_reserved_for_match_nr), id);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001319 return;
1320 }
1321
1322 rettv->vval.v_number = match_add(win, group, NULL, prio, id, l,
1323 conceal_char);
1324# endif
1325}
1326
1327/*
1328 * "matcharg()" function
1329 */
1330 void
1331f_matcharg(typval_T *argvars UNUSED, typval_T *rettv)
1332{
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00001333 if (rettv_list_alloc(rettv) != OK)
1334 return;
1335
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001336# ifdef FEAT_SEARCH_EXTRA
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00001337 int id;
1338 matchitem_T *m;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001339
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00001340 if (in_vim9script() && check_for_number_arg(argvars, 0) == FAIL)
1341 return;
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001342
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00001343 id = (int)tv_get_number(&argvars[0]);
1344 if (id >= 1 && id <= 3)
1345 {
1346 if ((m = get_match(curwin, id)) != NULL)
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001347 {
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00001348 list_append_string(rettv->vval.v_list,
1349 syn_id2name(m->mit_hlg_id), -1);
1350 list_append_string(rettv->vval.v_list, m->mit_pattern, -1);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001351 }
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00001352 else
1353 {
1354 list_append_string(rettv->vval.v_list, NULL, -1);
1355 list_append_string(rettv->vval.v_list, NULL, -1);
1356 }
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001357 }
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00001358# endif
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001359}
1360
1361/*
1362 * "matchdelete()" function
1363 */
1364 void
1365f_matchdelete(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
1366{
1367# ifdef FEAT_SEARCH_EXTRA
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001368 win_T *win;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001369
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001370 if (in_vim9script()
1371 && (check_for_number_arg(argvars, 0) == FAIL
1372 || check_for_opt_number_arg(argvars, 1) == FAIL))
1373 return;
1374
1375 win = get_optional_window(argvars, 1);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001376 if (win == NULL)
1377 rettv->vval.v_number = -1;
1378 else
1379 rettv->vval.v_number = match_delete(win,
1380 (int)tv_get_number(&argvars[0]), TRUE);
1381# endif
1382}
1383#endif
1384
1385#if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
1386/*
1387 * ":[N]match {group} {pattern}"
1388 * Sets nextcmd to the start of the next command, if any. Also called when
1389 * skipping commands to find the next command.
1390 */
1391 void
1392ex_match(exarg_T *eap)
1393{
1394 char_u *p;
1395 char_u *g = NULL;
1396 char_u *end;
1397 int c;
1398 int id;
1399
1400 if (eap->line2 <= 3)
1401 id = eap->line2;
1402 else
1403 {
Bram Moolenaar451c2e32020-08-15 16:33:28 +02001404 emsg(_(e_invalid_command));
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001405 return;
1406 }
1407
1408 // First clear any old pattern.
1409 if (!eap->skip)
1410 match_delete(curwin, id, FALSE);
1411
1412 if (ends_excmd2(eap->cmd, eap->arg))
1413 end = eap->arg;
1414 else if ((STRNICMP(eap->arg, "none", 4) == 0
1415 && (VIM_ISWHITE(eap->arg[4])
1416 || ends_excmd2(eap->arg, eap->arg + 4))))
1417 end = eap->arg + 4;
1418 else
1419 {
1420 p = skiptowhite(eap->arg);
1421 if (!eap->skip)
1422 g = vim_strnsave(eap->arg, p - eap->arg);
1423 p = skipwhite(p);
1424 if (*p == NUL)
1425 {
1426 // There must be two arguments.
1427 vim_free(g);
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001428 semsg(_(e_invalid_argument_str), eap->arg);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001429 return;
1430 }
1431 end = skip_regexp(p + 1, *p, TRUE);
1432 if (!eap->skip)
1433 {
1434 if (*end != NUL && !ends_excmd2(end, skipwhite(end + 1)))
1435 {
1436 vim_free(g);
Bram Moolenaar74409f62022-01-01 15:58:22 +00001437 eap->errmsg = ex_errmsg(e_trailing_characters_str, end);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001438 return;
1439 }
1440 if (*end != *p)
1441 {
1442 vim_free(g);
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001443 semsg(_(e_invalid_argument_str), p);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001444 return;
1445 }
1446
1447 c = *end;
1448 *end = NUL;
1449 match_add(curwin, g, p + 1, 10, id, NULL, NULL);
1450 vim_free(g);
1451 *end = c;
1452 }
1453 }
1454 eap->nextcmd = find_nextcmd(end);
1455}
1456#endif