blob: ecab28cef38645b0da311332c565871f77628eab [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 }
53 if (id != -1)
54 {
55 cur = wp->w_match_head;
56 while (cur != NULL)
57 {
Bram Moolenaar50faf022022-09-29 12:50:17 +010058 if (cur->mit_id == id)
Bram Moolenaar06cf97e2020-06-28 13:17:26 +020059 {
Bram Moolenaar9d00e4a2022-01-05 17:49:15 +000060 semsg(_(e_id_already_taken_nr), id);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +020061 return -1;
62 }
Bram Moolenaar50faf022022-09-29 12:50:17 +010063 cur = cur->mit_next;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +020064 }
65 }
66 if ((hlg_id = syn_namen2id(grp, (int)STRLEN(grp))) == 0)
67 {
Bram Moolenaare29a27f2021-07-20 21:07:36 +020068 semsg(_(e_no_such_highlight_group_name_str), grp);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +020069 return -1;
70 }
71 if (pat != NULL && (regprog = vim_regcomp(pat, RE_MAGIC)) == NULL)
72 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +000073 semsg(_(e_invalid_argument_str), pat);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +020074 return -1;
75 }
76
77 // Find available match ID.
78 while (id == -1)
79 {
80 cur = wp->w_match_head;
Bram Moolenaar50faf022022-09-29 12:50:17 +010081 while (cur != NULL && cur->mit_id != wp->w_next_match_id)
82 cur = cur->mit_next;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +020083 if (cur == NULL)
84 id = wp->w_next_match_id;
85 wp->w_next_match_id++;
86 }
87
88 // Build new match.
89 m = ALLOC_CLEAR_ONE(matchitem_T);
Bram Moolenaar50faf022022-09-29 12:50:17 +010090 if (m == NULL)
91 return -1;
92 if (pos_list != NULL)
93 {
94 m->mit_pos_array = ALLOC_CLEAR_MULT(llpos_T, pos_list->lv_len);
95 if (m->mit_pos_array == NULL)
96 {
97 vim_free(m);
98 return -1;
99 }
100 m->mit_pos_count = pos_list->lv_len;
101 }
102 m->mit_id = id;
103 m->mit_priority = prio;
104 m->mit_pattern = pat == NULL ? NULL : vim_strsave(pat);
105 m->mit_hlg_id = hlg_id;
106 m->mit_match.regprog = regprog;
107 m->mit_match.rmm_ic = FALSE;
108 m->mit_match.rmm_maxcol = 0;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200109# if defined(FEAT_CONCEAL)
Bram Moolenaar50faf022022-09-29 12:50:17 +0100110 m->mit_conceal_char = 0;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200111 if (conceal_char != NULL)
Bram Moolenaar50faf022022-09-29 12:50:17 +0100112 m->mit_conceal_char = (*mb_ptr2char)(conceal_char);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200113# endif
114
115 // Set up position matches
116 if (pos_list != NULL)
117 {
118 linenr_T toplnum = 0;
119 linenr_T botlnum = 0;
120 listitem_T *li;
121 int i;
122
123 CHECK_LIST_MATERIALIZE(pos_list);
Bram Moolenaar50faf022022-09-29 12:50:17 +0100124 for (i = 0, li = pos_list->lv_first; li != NULL; i++, li = li->li_next)
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200125 {
126 linenr_T lnum = 0;
127 colnr_T col = 0;
128 int len = 1;
129 list_T *subl;
130 listitem_T *subli;
131 int error = FALSE;
132
133 if (li->li_tv.v_type == VAR_LIST)
134 {
135 subl = li->li_tv.vval.v_list;
136 if (subl == NULL)
137 goto fail;
138 subli = subl->lv_first;
139 if (subli == NULL)
140 goto fail;
141 lnum = tv_get_number_chk(&subli->li_tv, &error);
142 if (error == TRUE)
143 goto fail;
144 if (lnum == 0)
145 {
146 --i;
147 continue;
148 }
Bram Moolenaar50faf022022-09-29 12:50:17 +0100149 m->mit_pos_array[i].lnum = lnum;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200150 subli = subli->li_next;
151 if (subli != NULL)
152 {
153 col = tv_get_number_chk(&subli->li_tv, &error);
154 if (error == TRUE)
155 goto fail;
156 subli = subli->li_next;
157 if (subli != NULL)
158 {
159 len = tv_get_number_chk(&subli->li_tv, &error);
160 if (error == TRUE)
161 goto fail;
162 }
163 }
Bram Moolenaar50faf022022-09-29 12:50:17 +0100164 m->mit_pos_array[i].col = col;
165 m->mit_pos_array[i].len = len;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200166 }
167 else if (li->li_tv.v_type == VAR_NUMBER)
168 {
169 if (li->li_tv.vval.v_number == 0)
170 {
171 --i;
172 continue;
173 }
Bram Moolenaar50faf022022-09-29 12:50:17 +0100174 m->mit_pos_array[i].lnum = li->li_tv.vval.v_number;
175 m->mit_pos_array[i].col = 0;
176 m->mit_pos_array[i].len = 0;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200177 }
178 else
179 {
Bram Moolenaar9a846fb2022-01-01 21:59:18 +0000180 emsg(_(e_list_or_number_required));
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200181 goto fail;
182 }
183 if (toplnum == 0 || lnum < toplnum)
184 toplnum = lnum;
185 if (botlnum == 0 || lnum >= botlnum)
186 botlnum = lnum + 1;
187 }
188
189 // Calculate top and bottom lines for redrawing area
190 if (toplnum != 0)
191 {
192 if (wp->w_buffer->b_mod_set)
193 {
194 if (wp->w_buffer->b_mod_top > toplnum)
195 wp->w_buffer->b_mod_top = toplnum;
196 if (wp->w_buffer->b_mod_bot < botlnum)
197 wp->w_buffer->b_mod_bot = botlnum;
198 }
199 else
200 {
201 wp->w_buffer->b_mod_set = TRUE;
202 wp->w_buffer->b_mod_top = toplnum;
203 wp->w_buffer->b_mod_bot = botlnum;
204 wp->w_buffer->b_mod_xlines = 0;
205 }
Bram Moolenaar50faf022022-09-29 12:50:17 +0100206 m->mit_toplnum = toplnum;
207 m->mit_botlnum = botlnum;
Bram Moolenaara4d158b2022-08-14 14:17:45 +0100208 rtype = UPD_VALID;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200209 }
210 }
211
212 // Insert new match. The match list is in ascending order with regard to
213 // the match priorities.
214 cur = wp->w_match_head;
215 prev = cur;
Bram Moolenaar50faf022022-09-29 12:50:17 +0100216 while (cur != NULL && prio >= cur->mit_priority)
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200217 {
218 prev = cur;
Bram Moolenaar50faf022022-09-29 12:50:17 +0100219 cur = cur->mit_next;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200220 }
221 if (cur == prev)
222 wp->w_match_head = m;
223 else
Bram Moolenaar50faf022022-09-29 12:50:17 +0100224 prev->mit_next = m;
225 m->mit_next = cur;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200226
227 redraw_win_later(wp, rtype);
228 return id;
229
230fail:
Bram Moolenaar50faf022022-09-29 12:50:17 +0100231 vim_free(m->mit_pattern);
232 vim_free(m->mit_pos_array);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200233 vim_free(m);
234 return -1;
235}
236
237/*
238 * Delete match with ID 'id' in the match list of window 'wp'.
239 * Print error messages if 'perr' is TRUE.
240 */
241 static int
242match_delete(win_T *wp, int id, int perr)
243{
244 matchitem_T *cur = wp->w_match_head;
245 matchitem_T *prev = cur;
Bram Moolenaara4d158b2022-08-14 14:17:45 +0100246 int rtype = UPD_SOME_VALID;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200247
248 if (id < 1)
249 {
250 if (perr == TRUE)
Bram Moolenaar50faf022022-09-29 12:50:17 +0100251 semsg(_(e_invalid_id_nr_must_be_greater_than_or_equal_to_one_2),
252 id);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200253 return -1;
254 }
Bram Moolenaar50faf022022-09-29 12:50:17 +0100255 while (cur != NULL && cur->mit_id != id)
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200256 {
257 prev = cur;
Bram Moolenaar50faf022022-09-29 12:50:17 +0100258 cur = cur->mit_next;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200259 }
260 if (cur == NULL)
261 {
262 if (perr == TRUE)
Bram Moolenaar9d00e4a2022-01-05 17:49:15 +0000263 semsg(_(e_id_not_found_nr), id);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200264 return -1;
265 }
266 if (cur == prev)
Bram Moolenaar50faf022022-09-29 12:50:17 +0100267 wp->w_match_head = cur->mit_next;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200268 else
Bram Moolenaar50faf022022-09-29 12:50:17 +0100269 prev->mit_next = cur->mit_next;
270 vim_regfree(cur->mit_match.regprog);
271 vim_free(cur->mit_pattern);
272 if (cur->mit_toplnum != 0)
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200273 {
274 if (wp->w_buffer->b_mod_set)
275 {
Bram Moolenaar50faf022022-09-29 12:50:17 +0100276 if (wp->w_buffer->b_mod_top > cur->mit_toplnum)
277 wp->w_buffer->b_mod_top = cur->mit_toplnum;
278 if (wp->w_buffer->b_mod_bot < cur->mit_botlnum)
279 wp->w_buffer->b_mod_bot = cur->mit_botlnum;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200280 }
281 else
282 {
283 wp->w_buffer->b_mod_set = TRUE;
Bram Moolenaar50faf022022-09-29 12:50:17 +0100284 wp->w_buffer->b_mod_top = cur->mit_toplnum;
285 wp->w_buffer->b_mod_bot = cur->mit_botlnum;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200286 wp->w_buffer->b_mod_xlines = 0;
287 }
Bram Moolenaara4d158b2022-08-14 14:17:45 +0100288 rtype = UPD_VALID;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200289 }
Bram Moolenaar50faf022022-09-29 12:50:17 +0100290 vim_free(cur->mit_pos_array);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200291 vim_free(cur);
292 redraw_win_later(wp, rtype);
293 return 0;
294}
295
296/*
297 * Delete all matches in the match list of window 'wp'.
298 */
299 void
300clear_matches(win_T *wp)
301{
302 matchitem_T *m;
303
304 while (wp->w_match_head != NULL)
305 {
Bram Moolenaar50faf022022-09-29 12:50:17 +0100306 m = wp->w_match_head->mit_next;
307 vim_regfree(wp->w_match_head->mit_match.regprog);
308 vim_free(wp->w_match_head->mit_pattern);
309 vim_free(wp->w_match_head->mit_pos_array);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200310 vim_free(wp->w_match_head);
311 wp->w_match_head = m;
312 }
Bram Moolenaara4d158b2022-08-14 14:17:45 +0100313 redraw_win_later(wp, UPD_SOME_VALID);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200314}
315
316/*
317 * Get match from ID 'id' in window 'wp'.
318 * Return NULL if match not found.
319 */
320 static matchitem_T *
321get_match(win_T *wp, int id)
322{
323 matchitem_T *cur = wp->w_match_head;
324
Bram Moolenaar50faf022022-09-29 12:50:17 +0100325 while (cur != NULL && cur->mit_id != id)
326 cur = cur->mit_next;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200327 return cur;
328}
329
330/*
331 * Init for calling prepare_search_hl().
332 */
333 void
334init_search_hl(win_T *wp, match_T *search_hl)
335{
336 matchitem_T *cur;
337
338 // Setup for match and 'hlsearch' highlighting. Disable any previous
339 // match
340 cur = wp->w_match_head;
341 while (cur != NULL)
342 {
Bram Moolenaar50faf022022-09-29 12:50:17 +0100343 cur->mit_hl.rm = cur->mit_match;
344 if (cur->mit_hlg_id == 0)
345 cur->mit_hl.attr = 0;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200346 else
Bram Moolenaar50faf022022-09-29 12:50:17 +0100347 cur->mit_hl.attr = syn_id2attr(cur->mit_hlg_id);
348 cur->mit_hl.buf = wp->w_buffer;
349 cur->mit_hl.lnum = 0;
350 cur->mit_hl.first_lnum = 0;
351 cur = cur->mit_next;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200352 }
353 search_hl->buf = wp->w_buffer;
354 search_hl->lnum = 0;
355 search_hl->first_lnum = 0;
356 // time limit is set at the toplevel, for all windows
357}
358
359/*
360 * If there is a match fill "shl" and return one.
361 * Return zero otherwise.
362 */
363 static int
364next_search_hl_pos(
365 match_T *shl, // points to a match
366 linenr_T lnum,
Bram Moolenaar50faf022022-09-29 12:50:17 +0100367 matchitem_T *match, // match item with positions
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200368 colnr_T mincol) // minimal column for a match
369{
370 int i;
371 int found = -1;
372
Bram Moolenaar50faf022022-09-29 12:50:17 +0100373 for (i = match->mit_pos_cur; i < match->mit_pos_count; i++)
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200374 {
Bram Moolenaar50faf022022-09-29 12:50:17 +0100375 llpos_T *pos = &match->mit_pos_array[i];
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200376
377 if (pos->lnum == 0)
378 break;
379 if (pos->len == 0 && pos->col < mincol)
380 continue;
381 if (pos->lnum == lnum)
382 {
383 if (found >= 0)
384 {
Bram Moolenaar50faf022022-09-29 12:50:17 +0100385 // if this match comes before the one at "found" then swap them
386 if (pos->col < match->mit_pos_array[found].col)
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200387 {
388 llpos_T tmp = *pos;
389
Bram Moolenaar50faf022022-09-29 12:50:17 +0100390 *pos = match->mit_pos_array[found];
391 match->mit_pos_array[found] = tmp;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200392 }
393 }
394 else
395 found = i;
396 }
397 }
Bram Moolenaar50faf022022-09-29 12:50:17 +0100398 match->mit_pos_cur = 0;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200399 if (found >= 0)
400 {
Bram Moolenaar50faf022022-09-29 12:50:17 +0100401 colnr_T start = match->mit_pos_array[found].col == 0
402 ? 0 : match->mit_pos_array[found].col - 1;
403 colnr_T end = match->mit_pos_array[found].col == 0
404 ? MAXCOL : start + match->mit_pos_array[found].len;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200405
406 shl->lnum = lnum;
407 shl->rm.startpos[0].lnum = 0;
408 shl->rm.startpos[0].col = start;
409 shl->rm.endpos[0].lnum = 0;
410 shl->rm.endpos[0].col = end;
411 shl->is_addpos = TRUE;
Bram Moolenaar693ccd12022-04-16 12:04:37 +0100412 shl->has_cursor = FALSE;
Bram Moolenaar50faf022022-09-29 12:50:17 +0100413 match->mit_pos_cur = found + 1;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200414 return 1;
415 }
416 return 0;
417}
418
419/*
420 * Search for a next 'hlsearch' or match.
421 * Uses shl->buf.
422 * Sets shl->lnum and shl->rm contents.
423 * Note: Assumes a previous match is always before "lnum", unless
424 * shl->lnum is zero.
425 * Careful: Any pointers for buffer lines will become invalid.
426 */
427 static void
428next_search_hl(
429 win_T *win,
430 match_T *search_hl,
431 match_T *shl, // points to search_hl or a match
432 linenr_T lnum,
433 colnr_T mincol, // minimal column for a match
434 matchitem_T *cur) // to retrieve match positions if any
435{
436 linenr_T l;
437 colnr_T matchcol;
438 long nmatched;
439 int called_emsg_before = called_emsg;
Paul Ollis65745772022-06-05 16:55:54 +0100440 int timed_out = FALSE;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200441
442 // for :{range}s/pat only highlight inside the range
Bram Moolenaar94fb8272021-12-29 19:22:44 +0000443 if ((lnum < search_first_line || lnum > search_last_line) && cur == NULL)
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200444 {
445 shl->lnum = 0;
446 return;
447 }
448
449 if (shl->lnum != 0)
450 {
451 // Check for three situations:
452 // 1. If the "lnum" is below a previous match, start a new search.
453 // 2. If the previous match includes "mincol", use it.
454 // 3. Continue after the previous match.
455 l = shl->lnum + shl->rm.endpos[0].lnum - shl->rm.startpos[0].lnum;
456 if (lnum > l)
457 shl->lnum = 0;
458 else if (lnum < l || shl->rm.endpos[0].col > mincol)
459 return;
460 }
461
462 // Repeat searching for a match until one is found that includes "mincol"
463 // or none is found in this line.
464 for (;;)
465 {
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200466 // Three situations:
467 // 1. No useful previous match: search from start of line.
468 // 2. Not Vi compatible or empty match: continue at next character.
469 // Break the loop if this is beyond the end of the line.
470 // 3. Vi compatible searching: continue at end of previous match.
471 if (shl->lnum == 0)
472 matchcol = 0;
473 else if (vim_strchr(p_cpo, CPO_SEARCH) == NULL
474 || (shl->rm.endpos[0].lnum == 0
475 && shl->rm.endpos[0].col <= shl->rm.startpos[0].col))
476 {
477 char_u *ml;
478
479 matchcol = shl->rm.startpos[0].col;
480 ml = ml_get_buf(shl->buf, lnum, FALSE) + matchcol;
481 if (*ml == NUL)
482 {
483 ++matchcol;
484 shl->lnum = 0;
485 break;
486 }
487 if (has_mbyte)
488 matchcol += mb_ptr2len(ml);
489 else
490 ++matchcol;
491 }
492 else
493 matchcol = shl->rm.endpos[0].col;
494
495 shl->lnum = lnum;
496 if (shl->rm.regprog != NULL)
497 {
498 // Remember whether shl->rm is using a copy of the regprog in
Bram Moolenaar50faf022022-09-29 12:50:17 +0100499 // cur->mit_match.
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200500 int regprog_is_copy = (shl != search_hl && cur != NULL
Bram Moolenaar50faf022022-09-29 12:50:17 +0100501 && shl == &cur->mit_hl
502 && cur->mit_match.regprog == cur->mit_hl.rm.regprog);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200503
504 nmatched = vim_regexec_multi(&shl->rm, win, shl->buf, lnum,
Paul Ollis65745772022-06-05 16:55:54 +0100505 matchcol, &timed_out);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200506 // Copy the regprog, in case it got freed and recompiled.
507 if (regprog_is_copy)
Bram Moolenaar50faf022022-09-29 12:50:17 +0100508 cur->mit_match.regprog = cur->mit_hl.rm.regprog;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200509
510 if (called_emsg > called_emsg_before || got_int || timed_out)
511 {
512 // Error while handling regexp: stop using this regexp.
513 if (shl == search_hl)
514 {
515 // don't free regprog in the match list, it's a copy
516 vim_regfree(shl->rm.regprog);
517 set_no_hlsearch(TRUE);
518 }
519 shl->rm.regprog = NULL;
520 shl->lnum = 0;
521 got_int = FALSE; // avoid the "Type :quit to exit Vim" message
522 break;
523 }
524 }
525 else if (cur != NULL)
Bram Moolenaar50faf022022-09-29 12:50:17 +0100526 nmatched = next_search_hl_pos(shl, lnum, cur, matchcol);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200527 else
528 nmatched = 0;
529 if (nmatched == 0)
530 {
531 shl->lnum = 0; // no match found
532 break;
533 }
534 if (shl->rm.startpos[0].lnum > 0
535 || shl->rm.startpos[0].col >= mincol
536 || nmatched > 1
537 || shl->rm.endpos[0].col > mincol)
538 {
539 shl->lnum += shl->rm.startpos[0].lnum;
540 break; // useful match found
541 }
542 }
543}
544
545/*
546 * Advance to the match in window "wp" line "lnum" or past it.
547 */
548 void
549prepare_search_hl(win_T *wp, match_T *search_hl, linenr_T lnum)
550{
551 matchitem_T *cur; // points to the match list
552 match_T *shl; // points to search_hl or a match
553 int shl_flag; // flag to indicate whether search_hl
554 // has been processed or not
555 int pos_inprogress; // marks that position match search is
556 // in progress
557 int n;
558
559 // When using a multi-line pattern, start searching at the top
560 // of the window or just after a closed fold.
561 // Do this both for search_hl and the match list.
562 cur = wp->w_match_head;
563 shl_flag = WIN_IS_POPUP(wp); // skip search_hl in a popup window
564 while (cur != NULL || shl_flag == FALSE)
565 {
566 if (shl_flag == FALSE)
567 {
568 shl = search_hl;
569 shl_flag = TRUE;
570 }
571 else
Bram Moolenaar50faf022022-09-29 12:50:17 +0100572 shl = &cur->mit_hl;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200573 if (shl->rm.regprog != NULL
574 && shl->lnum == 0
575 && re_multiline(shl->rm.regprog))
576 {
577 if (shl->first_lnum == 0)
578 {
579# ifdef FEAT_FOLDING
580 for (shl->first_lnum = lnum;
581 shl->first_lnum > wp->w_topline; --shl->first_lnum)
582 if (hasFoldingWin(wp, shl->first_lnum - 1,
583 NULL, NULL, TRUE, NULL))
584 break;
585# else
586 shl->first_lnum = wp->w_topline;
587# endif
588 }
589 if (cur != NULL)
Bram Moolenaar50faf022022-09-29 12:50:17 +0100590 cur->mit_pos_cur = 0;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200591 pos_inprogress = TRUE;
592 n = 0;
593 while (shl->first_lnum < lnum && (shl->rm.regprog != NULL
594 || (cur != NULL && pos_inprogress)))
595 {
596 next_search_hl(wp, search_hl, shl, shl->first_lnum, (colnr_T)n,
597 shl == search_hl ? NULL : cur);
Bram Moolenaar50faf022022-09-29 12:50:17 +0100598 pos_inprogress = cur == NULL || cur->mit_pos_cur == 0
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200599 ? FALSE : TRUE;
600 if (shl->lnum != 0)
601 {
602 shl->first_lnum = shl->lnum
603 + shl->rm.endpos[0].lnum
604 - shl->rm.startpos[0].lnum;
605 n = shl->rm.endpos[0].col;
606 }
607 else
608 {
609 ++shl->first_lnum;
610 n = 0;
611 }
612 }
613 }
614 if (shl != search_hl && cur != NULL)
Bram Moolenaar50faf022022-09-29 12:50:17 +0100615 cur = cur->mit_next;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200616 }
617}
618
619/*
Bram Moolenaar9b367502022-04-22 20:07:21 +0100620 * Update "shl->has_cursor" based on the match in "shl" and the cursor
621 * position.
622 */
623 static void
624check_cur_search_hl(win_T *wp, match_T *shl)
625{
zeertzjq8279cfe2022-04-23 12:05:51 +0100626 linenr_T linecount = shl->rm.endpos[0].lnum - shl->rm.startpos[0].lnum;
Bram Moolenaar9b367502022-04-22 20:07:21 +0100627
628 if (wp->w_cursor.lnum >= shl->lnum
zeertzjq8279cfe2022-04-23 12:05:51 +0100629 && wp->w_cursor.lnum <= shl->lnum + linecount
Bram Moolenaar9b367502022-04-22 20:07:21 +0100630 && (wp->w_cursor.lnum > shl->lnum
631 || wp->w_cursor.col >= shl->rm.startpos[0].col)
632 && (wp->w_cursor.lnum < shl->lnum + linecount
633 || wp->w_cursor.col < shl->rm.endpos[0].col))
634 shl->has_cursor = TRUE;
635 else
636 shl->has_cursor = FALSE;
637}
638
639/*
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200640 * Prepare for 'hlsearch' and match highlighting in one window line.
641 * Return TRUE if there is such highlighting and set "search_attr" to the
642 * current highlight attribute.
643 */
644 int
645prepare_search_hl_line(
646 win_T *wp,
647 linenr_T lnum,
648 colnr_T mincol,
649 char_u **line,
650 match_T *search_hl,
651 int *search_attr)
652{
653 matchitem_T *cur; // points to the match list
654 match_T *shl; // points to search_hl or a match
655 int shl_flag; // flag to indicate whether search_hl
656 // has been processed or not
657 int area_highlighting = FALSE;
658
659 // Handle highlighting the last used search pattern and matches.
660 // Do this for both search_hl and the match list.
661 // Do not use search_hl in a popup window.
662 cur = wp->w_match_head;
663 shl_flag = WIN_IS_POPUP(wp);
664 while (cur != NULL || shl_flag == FALSE)
665 {
666 if (shl_flag == FALSE)
667 {
668 shl = search_hl;
669 shl_flag = TRUE;
670 }
671 else
Bram Moolenaar50faf022022-09-29 12:50:17 +0100672 shl = &cur->mit_hl;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200673 shl->startcol = MAXCOL;
674 shl->endcol = MAXCOL;
675 shl->attr_cur = 0;
676 shl->is_addpos = FALSE;
Bram Moolenaar693ccd12022-04-16 12:04:37 +0100677 shl->has_cursor = FALSE;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200678 if (cur != NULL)
Bram Moolenaar50faf022022-09-29 12:50:17 +0100679 cur->mit_pos_cur = 0;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200680 next_search_hl(wp, search_hl, shl, lnum, mincol,
681 shl == search_hl ? NULL : cur);
682
683 // Need to get the line again, a multi-line regexp may have made it
684 // invalid.
685 *line = ml_get_buf(wp->w_buffer, lnum, FALSE);
686
687 if (shl->lnum != 0 && shl->lnum <= lnum)
688 {
689 if (shl->lnum == lnum)
690 shl->startcol = shl->rm.startpos[0].col;
691 else
692 shl->startcol = 0;
693 if (lnum == shl->lnum + shl->rm.endpos[0].lnum
694 - shl->rm.startpos[0].lnum)
695 shl->endcol = shl->rm.endpos[0].col;
696 else
697 shl->endcol = MAXCOL;
Bram Moolenaar693ccd12022-04-16 12:04:37 +0100698
699 // check if the cursor is in the match before changing the columns
Bram Moolenaar9b367502022-04-22 20:07:21 +0100700 if (shl == search_hl)
701 check_cur_search_hl(wp, shl);
Bram Moolenaar693ccd12022-04-16 12:04:37 +0100702
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200703 // Highlight one character for an empty match.
704 if (shl->startcol == shl->endcol)
705 {
706 if (has_mbyte && (*line)[shl->endcol] != NUL)
707 shl->endcol += (*mb_ptr2len)((*line) + shl->endcol);
708 else
709 ++shl->endcol;
710 }
711 if ((long)shl->startcol < mincol) // match at leftcol
712 {
713 shl->attr_cur = shl->attr;
714 *search_attr = shl->attr;
715 }
716 area_highlighting = TRUE;
717 }
718 if (shl != search_hl && cur != NULL)
Bram Moolenaar50faf022022-09-29 12:50:17 +0100719 cur = cur->mit_next;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200720 }
721 return area_highlighting;
722}
723
724/*
725 * For a position in a line: Check for start/end of 'hlsearch' and other
726 * matches.
727 * After end, check for start/end of next match.
728 * When another match, have to check for start again.
729 * Watch out for matching an empty string!
Bram Moolenaar0c359af2021-11-29 19:18:57 +0000730 * "on_last_col" is set to TRUE with non-zero search_attr and the next column
731 * is endcol.
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200732 * Return the updated search_attr.
733 */
734 int
735update_search_hl(
736 win_T *wp,
737 linenr_T lnum,
738 colnr_T col,
739 char_u **line,
740 match_T *search_hl,
741 int *has_match_conc UNUSED,
742 int *match_conc UNUSED,
743 int did_line_attr,
Bram Moolenaar0c359af2021-11-29 19:18:57 +0000744 int lcs_eol_one,
745 int *on_last_col)
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200746{
747 matchitem_T *cur; // points to the match list
748 match_T *shl; // points to search_hl or a match
749 int shl_flag; // flag to indicate whether search_hl
750 // has been processed or not
751 int pos_inprogress; // marks that position match search is in
752 // progress
753 int search_attr = 0;
754
755
756 // Do this for 'search_hl' and the match list (ordered by priority).
757 cur = wp->w_match_head;
758 shl_flag = WIN_IS_POPUP(wp);
759 while (cur != NULL || shl_flag == FALSE)
760 {
761 if (shl_flag == FALSE
762 && (cur == NULL
Bram Moolenaar50faf022022-09-29 12:50:17 +0100763 || cur->mit_priority > SEARCH_HL_PRIORITY))
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200764 {
765 shl = search_hl;
766 shl_flag = TRUE;
767 }
768 else
Bram Moolenaar50faf022022-09-29 12:50:17 +0100769 shl = &cur->mit_hl;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200770 if (cur != NULL)
Bram Moolenaar50faf022022-09-29 12:50:17 +0100771 cur->mit_pos_cur = 0;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200772 pos_inprogress = TRUE;
773 while (shl->rm.regprog != NULL || (cur != NULL && pos_inprogress))
774 {
775 if (shl->startcol != MAXCOL
776 && col >= shl->startcol
777 && col < shl->endcol)
778 {
779 int next_col = col + mb_ptr2len(*line + col);
780
781 if (shl->endcol < next_col)
782 shl->endcol = next_col;
783 shl->attr_cur = shl->attr;
784# ifdef FEAT_CONCEAL
785 // Match with the "Conceal" group results in hiding
786 // the match.
787 if (cur != NULL
788 && shl != search_hl
Bram Moolenaar50faf022022-09-29 12:50:17 +0100789 && syn_name2id((char_u *)"Conceal") == cur->mit_hlg_id)
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200790 {
791 *has_match_conc = col == shl->startcol ? 2 : 1;
Bram Moolenaar50faf022022-09-29 12:50:17 +0100792 *match_conc = cur->mit_conceal_char;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200793 }
794 else
795 *has_match_conc = 0;
796# endif
LemonBoya4399382022-04-09 21:04:08 +0100797 // Highlight the match were the cursor is using the CurSearch
798 // group.
Bram Moolenaar693ccd12022-04-16 12:04:37 +0100799 if (shl == search_hl && shl->has_cursor)
Bram Moolenaar368137a2022-05-31 13:43:12 +0100800 {
LemonBoya4399382022-04-09 21:04:08 +0100801 shl->attr_cur = HL_ATTR(HLF_LC);
Bram Moolenaar368137a2022-05-31 13:43:12 +0100802 if (shl->attr_cur != shl->attr)
803 search_hl_has_cursor_lnum = lnum;
804 }
LemonBoya4399382022-04-09 21:04:08 +0100805
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200806 }
807 else if (col == shl->endcol)
808 {
809 shl->attr_cur = 0;
810 next_search_hl(wp, search_hl, shl, lnum, col,
811 shl == search_hl ? NULL : cur);
Bram Moolenaar50faf022022-09-29 12:50:17 +0100812 pos_inprogress = !(cur == NULL || cur->mit_pos_cur == 0);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200813
814 // Need to get the line again, a multi-line regexp may have
815 // made it invalid.
816 *line = ml_get_buf(wp->w_buffer, lnum, FALSE);
817
818 if (shl->lnum == lnum)
819 {
820 shl->startcol = shl->rm.startpos[0].col;
821 if (shl->rm.endpos[0].lnum == 0)
822 shl->endcol = shl->rm.endpos[0].col;
823 else
824 shl->endcol = MAXCOL;
825
Bram Moolenaar9b367502022-04-22 20:07:21 +0100826 // check if the cursor is in the match
827 if (shl == search_hl)
828 check_cur_search_hl(wp, shl);
829
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200830 if (shl->startcol == shl->endcol)
831 {
832 // highlight empty match, try again after
833 // it
834 if (has_mbyte)
Bram Moolenaar41f08952021-02-22 22:13:49 +0100835 {
836 char_u *p = *line + shl->endcol;
837
838 if (*p == NUL)
839 // consistent with non-mbyte
840 ++shl->endcol;
841 else
842 shl->endcol += (*mb_ptr2len)(p);
843 }
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200844 else
845 ++shl->endcol;
846 }
847
848 // Loop to check if the match starts at the
849 // current position
850 continue;
851 }
852 }
853 break;
854 }
855 if (shl != search_hl && cur != NULL)
Bram Moolenaar50faf022022-09-29 12:50:17 +0100856 cur = cur->mit_next;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200857 }
858
859 // Use attributes from match with highest priority among 'search_hl' and
860 // the match list.
861 cur = wp->w_match_head;
862 shl_flag = WIN_IS_POPUP(wp);
863 while (cur != NULL || shl_flag == FALSE)
864 {
865 if (shl_flag == FALSE
866 && (cur == NULL ||
Bram Moolenaar50faf022022-09-29 12:50:17 +0100867 cur->mit_priority > SEARCH_HL_PRIORITY))
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200868 {
869 shl = search_hl;
870 shl_flag = TRUE;
871 }
872 else
Bram Moolenaar50faf022022-09-29 12:50:17 +0100873 shl = &cur->mit_hl;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200874 if (shl->attr_cur != 0)
Bram Moolenaar0c359af2021-11-29 19:18:57 +0000875 {
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200876 search_attr = shl->attr_cur;
Bram Moolenaar0c359af2021-11-29 19:18:57 +0000877 *on_last_col = col + 1 >= shl->endcol;
878 }
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200879 if (shl != search_hl && cur != NULL)
Bram Moolenaar50faf022022-09-29 12:50:17 +0100880 cur = cur->mit_next;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200881 }
882 // Only highlight one character after the last column.
883 if (*(*line + col) == NUL && (did_line_attr >= 1
884 || (wp->w_p_list && lcs_eol_one == -1)))
885 search_attr = 0;
886 return search_attr;
887}
888
889 int
890get_prevcol_hl_flag(win_T *wp, match_T *search_hl, long curcol)
891{
892 long prevcol = curcol;
893 int prevcol_hl_flag = FALSE;
894 matchitem_T *cur; // points to the match list
895
Bram Moolenaar41f08952021-02-22 22:13:49 +0100896#if defined(FEAT_PROP_POPUP)
897 // don't do this in a popup window
898 if (popup_is_popup(wp))
899 return FALSE;
900#endif
901
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200902 // we're not really at that column when skipping some text
903 if ((long)(wp->w_p_wrap ? wp->w_skipcol : wp->w_leftcol) > prevcol)
904 ++prevcol;
905
Bram Moolenaar41f08952021-02-22 22:13:49 +0100906 // Highlight a character after the end of the line if the match started
907 // at the end of the line or when the match continues in the next line
908 // (match includes the line break).
909 if (!search_hl->is_addpos && (prevcol == (long)search_hl->startcol
910 || (prevcol > (long)search_hl->startcol
911 && search_hl->endcol == MAXCOL)))
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200912 prevcol_hl_flag = TRUE;
913 else
914 {
915 cur = wp->w_match_head;
916 while (cur != NULL)
917 {
Bram Moolenaar50faf022022-09-29 12:50:17 +0100918 if (!cur->mit_hl.is_addpos && (prevcol == (long)cur->mit_hl.startcol
919 || (prevcol > (long)cur->mit_hl.startcol
920 && cur->mit_hl.endcol == MAXCOL)))
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200921 {
922 prevcol_hl_flag = TRUE;
923 break;
924 }
Bram Moolenaar50faf022022-09-29 12:50:17 +0100925 cur = cur->mit_next;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200926 }
927 }
928 return prevcol_hl_flag;
929}
930
931/*
932 * Get highlighting for the char after the text in "char_attr" from 'hlsearch'
933 * or match highlighting.
934 */
935 void
936get_search_match_hl(win_T *wp, match_T *search_hl, long col, int *char_attr)
937{
938 matchitem_T *cur; // points to the match list
939 match_T *shl; // points to search_hl or a match
940 int shl_flag; // flag to indicate whether search_hl
941 // has been processed or not
942
943 cur = wp->w_match_head;
944 shl_flag = WIN_IS_POPUP(wp);
945 while (cur != NULL || shl_flag == FALSE)
946 {
947 if (shl_flag == FALSE
948 && ((cur != NULL
Bram Moolenaar50faf022022-09-29 12:50:17 +0100949 && cur->mit_priority > SEARCH_HL_PRIORITY)
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200950 || cur == NULL))
951 {
952 shl = search_hl;
953 shl_flag = TRUE;
954 }
955 else
Bram Moolenaar50faf022022-09-29 12:50:17 +0100956 shl = &cur->mit_hl;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200957 if (col - 1 == (long)shl->startcol
958 && (shl == search_hl || !shl->is_addpos))
959 *char_attr = shl->attr;
960 if (shl != search_hl && cur != NULL)
Bram Moolenaar50faf022022-09-29 12:50:17 +0100961 cur = cur->mit_next;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200962 }
963}
964
965#endif // FEAT_SEARCH_EXTRA
966
967#if defined(FEAT_EVAL) || defined(PROTO)
968# ifdef FEAT_SEARCH_EXTRA
969 static int
970matchadd_dict_arg(typval_T *tv, char_u **conceal_char, win_T **win)
971{
972 dictitem_T *di;
973
974 if (tv->v_type != VAR_DICT)
975 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +0000976 emsg(_(e_dictionary_required));
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200977 return FAIL;
978 }
979
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +0100980 if (dict_has_key(tv->vval.v_dict, "conceal"))
Bram Moolenaard61efa52022-07-23 09:52:04 +0100981 *conceal_char = dict_get_string(tv->vval.v_dict, "conceal", FALSE);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200982
983 if ((di = dict_find(tv->vval.v_dict, (char_u *)"window", -1)) != NULL)
984 {
985 *win = find_win_by_nr_or_id(&di->di_tv);
986 if (*win == NULL)
987 {
Bram Moolenaar3a846e62022-01-01 16:21:00 +0000988 emsg(_(e_invalid_window_number));
Bram Moolenaar06cf97e2020-06-28 13:17:26 +0200989 return FAIL;
990 }
991 }
992
993 return OK;
994}
995#endif
996
997/*
998 * "clearmatches()" function
999 */
1000 void
1001f_clearmatches(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
1002{
1003#ifdef FEAT_SEARCH_EXTRA
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001004 win_T *win;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001005
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001006 if (in_vim9script() && check_for_opt_number_arg(argvars, 0) == FAIL)
1007 return;
1008
1009 win = get_optional_window(argvars, 0);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001010 if (win != NULL)
1011 clear_matches(win);
1012#endif
1013}
1014
1015/*
1016 * "getmatches()" function
1017 */
1018 void
1019f_getmatches(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
1020{
1021# ifdef FEAT_SEARCH_EXTRA
1022 dict_T *dict;
1023 matchitem_T *cur;
1024 int i;
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001025 win_T *win;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001026
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001027 if (in_vim9script() && check_for_opt_number_arg(argvars, 0) == FAIL)
1028 return;
1029
1030 win = get_optional_window(argvars, 0);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001031 if (rettv_list_alloc(rettv) == FAIL || win == NULL)
1032 return;
1033
1034 cur = win->w_match_head;
1035 while (cur != NULL)
1036 {
1037 dict = dict_alloc();
1038 if (dict == NULL)
1039 return;
Bram Moolenaar50faf022022-09-29 12:50:17 +01001040 if (cur->mit_match.regprog == NULL)
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001041 {
1042 // match added with matchaddpos()
Bram Moolenaar50faf022022-09-29 12:50:17 +01001043 for (i = 0; i < cur->mit_pos_count; ++i)
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001044 {
1045 llpos_T *llpos;
1046 char buf[30]; // use 30 to avoid compiler warning
1047 list_T *l;
1048
Bram Moolenaar50faf022022-09-29 12:50:17 +01001049 llpos = &cur->mit_pos_array[i];
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001050 if (llpos->lnum == 0)
1051 break;
1052 l = list_alloc();
1053 if (l == NULL)
1054 break;
1055 list_append_number(l, (varnumber_T)llpos->lnum);
1056 if (llpos->col > 0)
1057 {
1058 list_append_number(l, (varnumber_T)llpos->col);
1059 list_append_number(l, (varnumber_T)llpos->len);
1060 }
1061 sprintf(buf, "pos%d", i + 1);
1062 dict_add_list(dict, buf, l);
1063 }
1064 }
1065 else
1066 {
Bram Moolenaar50faf022022-09-29 12:50:17 +01001067 dict_add_string(dict, "pattern", cur->mit_pattern);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001068 }
Bram Moolenaar50faf022022-09-29 12:50:17 +01001069 dict_add_string(dict, "group", syn_id2name(cur->mit_hlg_id));
1070 dict_add_number(dict, "priority", (long)cur->mit_priority);
1071 dict_add_number(dict, "id", (long)cur->mit_id);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001072# if defined(FEAT_CONCEAL)
Bram Moolenaar50faf022022-09-29 12:50:17 +01001073 if (cur->mit_conceal_char)
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001074 {
1075 char_u buf[MB_MAXBYTES + 1];
1076
Bram Moolenaar50faf022022-09-29 12:50:17 +01001077 buf[(*mb_char2bytes)(cur->mit_conceal_char, buf)] = NUL;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001078 dict_add_string(dict, "conceal", (char_u *)&buf);
1079 }
1080# endif
1081 list_append_dict(rettv->vval.v_list, dict);
Bram Moolenaar50faf022022-09-29 12:50:17 +01001082 cur = cur->mit_next;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001083 }
1084# endif
1085}
1086
1087/*
1088 * "setmatches()" function
1089 */
1090 void
1091f_setmatches(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
1092{
1093#ifdef FEAT_SEARCH_EXTRA
1094 list_T *l;
1095 listitem_T *li;
1096 dict_T *d;
1097 list_T *s = NULL;
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02001098 win_T *win;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001099
1100 rettv->vval.v_number = -1;
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02001101
1102 if (in_vim9script()
1103 && (check_for_list_arg(argvars, 0) == FAIL
1104 || check_for_opt_number_arg(argvars, 1) == FAIL))
1105 return;
1106
Bram Moolenaard83392a2022-09-01 12:22:46 +01001107 if (check_for_list_arg(argvars, 0) == FAIL)
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001108 return;
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02001109 win = get_optional_window(argvars, 1);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001110 if (win == NULL)
1111 return;
1112
1113 if ((l = argvars[0].vval.v_list) != NULL)
1114 {
1115 // To some extent make sure that we are dealing with a list from
1116 // "getmatches()".
1117 li = l->lv_first;
1118 while (li != NULL)
1119 {
1120 if (li->li_tv.v_type != VAR_DICT
1121 || (d = li->li_tv.vval.v_dict) == NULL)
1122 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001123 emsg(_(e_invalid_argument));
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001124 return;
1125 }
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01001126 if (!(dict_has_key(d, "group")
1127 && (dict_has_key(d, "pattern")
1128 || dict_has_key(d, "pos1"))
1129 && dict_has_key(d, "priority")
1130 && dict_has_key(d, "id")))
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001131 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001132 emsg(_(e_invalid_argument));
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001133 return;
1134 }
1135 li = li->li_next;
1136 }
1137
1138 clear_matches(win);
1139 li = l->lv_first;
1140 while (li != NULL)
1141 {
1142 int i = 0;
1143 char buf[30]; // use 30 to avoid compiler warning
1144 dictitem_T *di;
1145 char_u *group;
1146 int priority;
1147 int id;
1148 char_u *conceal;
1149
1150 d = li->li_tv.vval.v_dict;
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01001151 if (!dict_has_key(d, "pattern"))
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001152 {
1153 if (s == NULL)
1154 {
1155 s = list_alloc();
1156 if (s == NULL)
1157 return;
1158 }
1159
1160 // match from matchaddpos()
1161 for (i = 1; i < 9; i++)
1162 {
1163 sprintf((char *)buf, (char *)"pos%d", i);
1164 if ((di = dict_find(d, (char_u *)buf, -1)) != NULL)
1165 {
1166 if (di->di_tv.v_type != VAR_LIST)
1167 return;
1168
1169 list_append_tv(s, &di->di_tv);
1170 s->lv_refcount++;
1171 }
1172 else
1173 break;
1174 }
1175 }
1176
Bram Moolenaard61efa52022-07-23 09:52:04 +01001177 group = dict_get_string(d, "group", TRUE);
1178 priority = (int)dict_get_number(d, "priority");
1179 id = (int)dict_get_number(d, "id");
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01001180 conceal = dict_has_key(d, "conceal")
Bram Moolenaard61efa52022-07-23 09:52:04 +01001181 ? dict_get_string(d, "conceal", TRUE)
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001182 : NULL;
1183 if (i == 0)
1184 {
1185 match_add(win, group,
Bram Moolenaard61efa52022-07-23 09:52:04 +01001186 dict_get_string(d, "pattern", FALSE),
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001187 priority, id, NULL, conceal);
1188 }
1189 else
1190 {
1191 match_add(win, group, NULL, priority, id, s, conceal);
1192 list_unref(s);
1193 s = NULL;
1194 }
1195 vim_free(group);
1196 vim_free(conceal);
1197
1198 li = li->li_next;
1199 }
1200 rettv->vval.v_number = 0;
1201 }
1202#endif
1203}
1204
1205/*
1206 * "matchadd()" function
1207 */
1208 void
1209f_matchadd(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
1210{
1211# ifdef FEAT_SEARCH_EXTRA
1212 char_u buf[NUMBUFLEN];
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02001213 char_u *grp; // group
1214 char_u *pat; // pattern
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001215 int prio = 10; // default priority
1216 int id = -1;
1217 int error = FALSE;
1218 char_u *conceal_char = NULL;
1219 win_T *win = curwin;
1220
1221 rettv->vval.v_number = -1;
1222
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02001223 if (in_vim9script()
1224 && (check_for_string_arg(argvars, 0) == FAIL
1225 || check_for_string_arg(argvars, 1) == FAIL
1226 || check_for_opt_number_arg(argvars, 2) == FAIL
1227 || (argvars[2].v_type != VAR_UNKNOWN
1228 && (check_for_opt_number_arg(argvars, 3) == FAIL
1229 || (argvars[3].v_type != VAR_UNKNOWN
1230 && check_for_opt_dict_arg(argvars, 4) == FAIL)))))
1231 return;
1232
1233 grp = tv_get_string_buf_chk(&argvars[0], buf); // group
1234 pat = tv_get_string_buf_chk(&argvars[1], buf); // pattern
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001235 if (grp == NULL || pat == NULL)
1236 return;
1237 if (argvars[2].v_type != VAR_UNKNOWN)
1238 {
1239 prio = (int)tv_get_number_chk(&argvars[2], &error);
1240 if (argvars[3].v_type != VAR_UNKNOWN)
1241 {
1242 id = (int)tv_get_number_chk(&argvars[3], &error);
1243 if (argvars[4].v_type != VAR_UNKNOWN
1244 && matchadd_dict_arg(&argvars[4], &conceal_char, &win) == FAIL)
1245 return;
1246 }
1247 }
1248 if (error == TRUE)
1249 return;
1250 if (id >= 1 && id <= 3)
1251 {
Bram Moolenaar677658a2022-01-05 16:09:06 +00001252 semsg(_(e_id_is_reserved_for_match_nr), id);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001253 return;
1254 }
1255
1256 rettv->vval.v_number = match_add(win, grp, pat, prio, id, NULL,
1257 conceal_char);
1258# endif
1259}
1260
1261/*
1262 * "matchaddpos()" function
1263 */
1264 void
1265f_matchaddpos(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
1266{
1267# ifdef FEAT_SEARCH_EXTRA
1268 char_u buf[NUMBUFLEN];
1269 char_u *group;
1270 int prio = 10;
1271 int id = -1;
1272 int error = FALSE;
1273 list_T *l;
1274 char_u *conceal_char = NULL;
1275 win_T *win = curwin;
1276
1277 rettv->vval.v_number = -1;
1278
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02001279 if (in_vim9script()
1280 && (check_for_string_arg(argvars, 0) == FAIL
1281 || check_for_list_arg(argvars, 1) == FAIL
1282 || check_for_opt_number_arg(argvars, 2) == FAIL
1283 || (argvars[2].v_type != VAR_UNKNOWN
1284 && (check_for_opt_number_arg(argvars, 3) == FAIL
1285 || (argvars[3].v_type != VAR_UNKNOWN
1286 && check_for_opt_dict_arg(argvars, 4) == FAIL)))))
1287 return;
1288
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001289 group = tv_get_string_buf_chk(&argvars[0], buf);
1290 if (group == NULL)
1291 return;
1292
1293 if (argvars[1].v_type != VAR_LIST)
1294 {
Bram Moolenaar3a846e62022-01-01 16:21:00 +00001295 semsg(_(e_argument_of_str_must_be_list), "matchaddpos()");
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001296 return;
1297 }
1298 l = argvars[1].vval.v_list;
1299 if (l == NULL)
1300 return;
1301
1302 if (argvars[2].v_type != VAR_UNKNOWN)
1303 {
1304 prio = (int)tv_get_number_chk(&argvars[2], &error);
1305 if (argvars[3].v_type != VAR_UNKNOWN)
1306 {
1307 id = (int)tv_get_number_chk(&argvars[3], &error);
1308
1309 if (argvars[4].v_type != VAR_UNKNOWN
1310 && matchadd_dict_arg(&argvars[4], &conceal_char, &win) == FAIL)
1311 return;
1312 }
1313 }
1314 if (error == TRUE)
1315 return;
1316
1317 // id == 3 is ok because matchaddpos() is supposed to substitute :3match
1318 if (id == 1 || id == 2)
1319 {
Bram Moolenaar677658a2022-01-05 16:09:06 +00001320 semsg(_(e_id_is_reserved_for_match_nr), id);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001321 return;
1322 }
1323
1324 rettv->vval.v_number = match_add(win, group, NULL, prio, id, l,
1325 conceal_char);
1326# endif
1327}
1328
1329/*
1330 * "matcharg()" function
1331 */
1332 void
1333f_matcharg(typval_T *argvars UNUSED, typval_T *rettv)
1334{
1335 if (rettv_list_alloc(rettv) == OK)
1336 {
1337# ifdef FEAT_SEARCH_EXTRA
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001338 int id;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001339 matchitem_T *m;
1340
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001341 if (in_vim9script() && check_for_number_arg(argvars, 0) == FAIL)
1342 return;
1343
1344 id = (int)tv_get_number(&argvars[0]);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001345 if (id >= 1 && id <= 3)
1346 {
=?UTF-8?q?Dundar=20G=C3=B6c?=420fabc2022-01-28 15:28:04 +00001347 if ((m = get_match(curwin, id)) != NULL)
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001348 {
1349 list_append_string(rettv->vval.v_list,
Bram Moolenaar50faf022022-09-29 12:50:17 +01001350 syn_id2name(m->mit_hlg_id), -1);
1351 list_append_string(rettv->vval.v_list, m->mit_pattern, -1);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001352 }
1353 else
1354 {
1355 list_append_string(rettv->vval.v_list, NULL, -1);
1356 list_append_string(rettv->vval.v_list, NULL, -1);
1357 }
1358 }
1359# endif
1360 }
1361}
1362
1363/*
1364 * "matchdelete()" function
1365 */
1366 void
1367f_matchdelete(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
1368{
1369# ifdef FEAT_SEARCH_EXTRA
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001370 win_T *win;
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001371
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001372 if (in_vim9script()
1373 && (check_for_number_arg(argvars, 0) == FAIL
1374 || check_for_opt_number_arg(argvars, 1) == FAIL))
1375 return;
1376
1377 win = get_optional_window(argvars, 1);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001378 if (win == NULL)
1379 rettv->vval.v_number = -1;
1380 else
1381 rettv->vval.v_number = match_delete(win,
1382 (int)tv_get_number(&argvars[0]), TRUE);
1383# endif
1384}
1385#endif
1386
1387#if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
1388/*
1389 * ":[N]match {group} {pattern}"
1390 * Sets nextcmd to the start of the next command, if any. Also called when
1391 * skipping commands to find the next command.
1392 */
1393 void
1394ex_match(exarg_T *eap)
1395{
1396 char_u *p;
1397 char_u *g = NULL;
1398 char_u *end;
1399 int c;
1400 int id;
1401
1402 if (eap->line2 <= 3)
1403 id = eap->line2;
1404 else
1405 {
Bram Moolenaar451c2e32020-08-15 16:33:28 +02001406 emsg(_(e_invalid_command));
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001407 return;
1408 }
1409
1410 // First clear any old pattern.
1411 if (!eap->skip)
1412 match_delete(curwin, id, FALSE);
1413
1414 if (ends_excmd2(eap->cmd, eap->arg))
1415 end = eap->arg;
1416 else if ((STRNICMP(eap->arg, "none", 4) == 0
1417 && (VIM_ISWHITE(eap->arg[4])
1418 || ends_excmd2(eap->arg, eap->arg + 4))))
1419 end = eap->arg + 4;
1420 else
1421 {
1422 p = skiptowhite(eap->arg);
1423 if (!eap->skip)
1424 g = vim_strnsave(eap->arg, p - eap->arg);
1425 p = skipwhite(p);
1426 if (*p == NUL)
1427 {
1428 // There must be two arguments.
1429 vim_free(g);
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001430 semsg(_(e_invalid_argument_str), eap->arg);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001431 return;
1432 }
1433 end = skip_regexp(p + 1, *p, TRUE);
1434 if (!eap->skip)
1435 {
1436 if (*end != NUL && !ends_excmd2(end, skipwhite(end + 1)))
1437 {
1438 vim_free(g);
Bram Moolenaar74409f62022-01-01 15:58:22 +00001439 eap->errmsg = ex_errmsg(e_trailing_characters_str, end);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001440 return;
1441 }
1442 if (*end != *p)
1443 {
1444 vim_free(g);
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001445 semsg(_(e_invalid_argument_str), p);
Bram Moolenaar06cf97e2020-06-28 13:17:26 +02001446 return;
1447 }
1448
1449 c = *end;
1450 *end = NUL;
1451 match_add(curwin, g, p + 1, 10, id, NULL, NULL);
1452 vim_free(g);
1453 *end = c;
1454 }
1455 }
1456 eap->nextcmd = find_nextcmd(end);
1457}
1458#endif