comparison src/match.c @ 21054:b1fac55cf8a3 v8.2.1078

patch 8.2.1078: highlight and match functionality together in one file Commit: https://github.com/vim/vim/commit/06cf97e714fd8bf9b35ff5f8a6f2302c79acdd03 Author: Bram Moolenaar <Bram@vim.org> Date: Sun Jun 28 13:17:26 2020 +0200 patch 8.2.1078: highlight and match functionality together in one file Problem: Highlight and match functionality together in one file. Solution: Move match functionality to a separate file. (Yegappan Lakshmanan, closes #6352)
author Bram Moolenaar <Bram@vim.org>
date Sun, 28 Jun 2020 13:30:04 +0200
parents
children a422bd80b434
comparison
equal deleted inserted replaced
21053:c1f0a0815090 21054:b1fac55cf8a3
1 /* vi:set ts=8 sts=4 sw=4 noet:
2 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
10 /*
11 * match.c: functions for highlighting matches
12 */
13
14 #include "vim.h"
15
16 #if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
17
18 # define SEARCH_HL_PRIORITY 0
19
20 /*
21 * Add match to the match list of window 'wp'. The pattern 'pat' will be
22 * highlighted with the group 'grp' with priority 'prio'.
23 * Optionally, a desired ID 'id' can be specified (greater than or equal to 1).
24 * If no particular ID is desired, -1 must be specified for 'id'.
25 * Return ID of added match, -1 on failure.
26 */
27 static int
28 match_add(
29 win_T *wp,
30 char_u *grp,
31 char_u *pat,
32 int prio,
33 int id,
34 list_T *pos_list,
35 char_u *conceal_char UNUSED) // pointer to conceal replacement char
36 {
37 matchitem_T *cur;
38 matchitem_T *prev;
39 matchitem_T *m;
40 int hlg_id;
41 regprog_T *regprog = NULL;
42 int rtype = SOME_VALID;
43
44 if (*grp == NUL || (pat != NULL && *pat == NUL))
45 return -1;
46 if (id < -1 || id == 0)
47 {
48 semsg(_("E799: Invalid ID: %d (must be greater than or equal to 1)"),
49 id);
50 return -1;
51 }
52 if (id != -1)
53 {
54 cur = wp->w_match_head;
55 while (cur != NULL)
56 {
57 if (cur->id == id)
58 {
59 semsg(_("E801: ID already taken: %d"), id);
60 return -1;
61 }
62 cur = cur->next;
63 }
64 }
65 if ((hlg_id = syn_namen2id(grp, (int)STRLEN(grp))) == 0)
66 {
67 semsg(_(e_nogroup), grp);
68 return -1;
69 }
70 if (pat != NULL && (regprog = vim_regcomp(pat, RE_MAGIC)) == NULL)
71 {
72 semsg(_(e_invarg2), pat);
73 return -1;
74 }
75
76 // Find available match ID.
77 while (id == -1)
78 {
79 cur = wp->w_match_head;
80 while (cur != NULL && cur->id != wp->w_next_match_id)
81 cur = cur->next;
82 if (cur == NULL)
83 id = wp->w_next_match_id;
84 wp->w_next_match_id++;
85 }
86
87 // Build new match.
88 m = ALLOC_CLEAR_ONE(matchitem_T);
89 m->id = id;
90 m->priority = prio;
91 m->pattern = pat == NULL ? NULL : vim_strsave(pat);
92 m->hlg_id = hlg_id;
93 m->match.regprog = regprog;
94 m->match.rmm_ic = FALSE;
95 m->match.rmm_maxcol = 0;
96 # if defined(FEAT_CONCEAL)
97 m->conceal_char = 0;
98 if (conceal_char != NULL)
99 m->conceal_char = (*mb_ptr2char)(conceal_char);
100 # endif
101
102 // Set up position matches
103 if (pos_list != NULL)
104 {
105 linenr_T toplnum = 0;
106 linenr_T botlnum = 0;
107 listitem_T *li;
108 int i;
109
110 CHECK_LIST_MATERIALIZE(pos_list);
111 for (i = 0, li = pos_list->lv_first; li != NULL && i < MAXPOSMATCH;
112 i++, li = li->li_next)
113 {
114 linenr_T lnum = 0;
115 colnr_T col = 0;
116 int len = 1;
117 list_T *subl;
118 listitem_T *subli;
119 int error = FALSE;
120
121 if (li->li_tv.v_type == VAR_LIST)
122 {
123 subl = li->li_tv.vval.v_list;
124 if (subl == NULL)
125 goto fail;
126 subli = subl->lv_first;
127 if (subli == NULL)
128 goto fail;
129 lnum = tv_get_number_chk(&subli->li_tv, &error);
130 if (error == TRUE)
131 goto fail;
132 if (lnum == 0)
133 {
134 --i;
135 continue;
136 }
137 m->pos.pos[i].lnum = lnum;
138 subli = subli->li_next;
139 if (subli != NULL)
140 {
141 col = tv_get_number_chk(&subli->li_tv, &error);
142 if (error == TRUE)
143 goto fail;
144 subli = subli->li_next;
145 if (subli != NULL)
146 {
147 len = tv_get_number_chk(&subli->li_tv, &error);
148 if (error == TRUE)
149 goto fail;
150 }
151 }
152 m->pos.pos[i].col = col;
153 m->pos.pos[i].len = len;
154 }
155 else if (li->li_tv.v_type == VAR_NUMBER)
156 {
157 if (li->li_tv.vval.v_number == 0)
158 {
159 --i;
160 continue;
161 }
162 m->pos.pos[i].lnum = li->li_tv.vval.v_number;
163 m->pos.pos[i].col = 0;
164 m->pos.pos[i].len = 0;
165 }
166 else
167 {
168 emsg(_("E290: List or number required"));
169 goto fail;
170 }
171 if (toplnum == 0 || lnum < toplnum)
172 toplnum = lnum;
173 if (botlnum == 0 || lnum >= botlnum)
174 botlnum = lnum + 1;
175 }
176
177 // Calculate top and bottom lines for redrawing area
178 if (toplnum != 0)
179 {
180 if (wp->w_buffer->b_mod_set)
181 {
182 if (wp->w_buffer->b_mod_top > toplnum)
183 wp->w_buffer->b_mod_top = toplnum;
184 if (wp->w_buffer->b_mod_bot < botlnum)
185 wp->w_buffer->b_mod_bot = botlnum;
186 }
187 else
188 {
189 wp->w_buffer->b_mod_set = TRUE;
190 wp->w_buffer->b_mod_top = toplnum;
191 wp->w_buffer->b_mod_bot = botlnum;
192 wp->w_buffer->b_mod_xlines = 0;
193 }
194 m->pos.toplnum = toplnum;
195 m->pos.botlnum = botlnum;
196 rtype = VALID;
197 }
198 }
199
200 // Insert new match. The match list is in ascending order with regard to
201 // the match priorities.
202 cur = wp->w_match_head;
203 prev = cur;
204 while (cur != NULL && prio >= cur->priority)
205 {
206 prev = cur;
207 cur = cur->next;
208 }
209 if (cur == prev)
210 wp->w_match_head = m;
211 else
212 prev->next = m;
213 m->next = cur;
214
215 redraw_win_later(wp, rtype);
216 return id;
217
218 fail:
219 vim_free(m);
220 return -1;
221 }
222
223 /*
224 * Delete match with ID 'id' in the match list of window 'wp'.
225 * Print error messages if 'perr' is TRUE.
226 */
227 static int
228 match_delete(win_T *wp, int id, int perr)
229 {
230 matchitem_T *cur = wp->w_match_head;
231 matchitem_T *prev = cur;
232 int rtype = SOME_VALID;
233
234 if (id < 1)
235 {
236 if (perr == TRUE)
237 semsg(_("E802: Invalid ID: %d (must be greater than or equal to 1)"),
238 id);
239 return -1;
240 }
241 while (cur != NULL && cur->id != id)
242 {
243 prev = cur;
244 cur = cur->next;
245 }
246 if (cur == NULL)
247 {
248 if (perr == TRUE)
249 semsg(_("E803: ID not found: %d"), id);
250 return -1;
251 }
252 if (cur == prev)
253 wp->w_match_head = cur->next;
254 else
255 prev->next = cur->next;
256 vim_regfree(cur->match.regprog);
257 vim_free(cur->pattern);
258 if (cur->pos.toplnum != 0)
259 {
260 if (wp->w_buffer->b_mod_set)
261 {
262 if (wp->w_buffer->b_mod_top > cur->pos.toplnum)
263 wp->w_buffer->b_mod_top = cur->pos.toplnum;
264 if (wp->w_buffer->b_mod_bot < cur->pos.botlnum)
265 wp->w_buffer->b_mod_bot = cur->pos.botlnum;
266 }
267 else
268 {
269 wp->w_buffer->b_mod_set = TRUE;
270 wp->w_buffer->b_mod_top = cur->pos.toplnum;
271 wp->w_buffer->b_mod_bot = cur->pos.botlnum;
272 wp->w_buffer->b_mod_xlines = 0;
273 }
274 rtype = VALID;
275 }
276 vim_free(cur);
277 redraw_win_later(wp, rtype);
278 return 0;
279 }
280
281 /*
282 * Delete all matches in the match list of window 'wp'.
283 */
284 void
285 clear_matches(win_T *wp)
286 {
287 matchitem_T *m;
288
289 while (wp->w_match_head != NULL)
290 {
291 m = wp->w_match_head->next;
292 vim_regfree(wp->w_match_head->match.regprog);
293 vim_free(wp->w_match_head->pattern);
294 vim_free(wp->w_match_head);
295 wp->w_match_head = m;
296 }
297 redraw_win_later(wp, SOME_VALID);
298 }
299
300 /*
301 * Get match from ID 'id' in window 'wp'.
302 * Return NULL if match not found.
303 */
304 static matchitem_T *
305 get_match(win_T *wp, int id)
306 {
307 matchitem_T *cur = wp->w_match_head;
308
309 while (cur != NULL && cur->id != id)
310 cur = cur->next;
311 return cur;
312 }
313
314 /*
315 * Init for calling prepare_search_hl().
316 */
317 void
318 init_search_hl(win_T *wp, match_T *search_hl)
319 {
320 matchitem_T *cur;
321
322 // Setup for match and 'hlsearch' highlighting. Disable any previous
323 // match
324 cur = wp->w_match_head;
325 while (cur != NULL)
326 {
327 cur->hl.rm = cur->match;
328 if (cur->hlg_id == 0)
329 cur->hl.attr = 0;
330 else
331 cur->hl.attr = syn_id2attr(cur->hlg_id);
332 cur->hl.buf = wp->w_buffer;
333 cur->hl.lnum = 0;
334 cur->hl.first_lnum = 0;
335 # ifdef FEAT_RELTIME
336 // Set the time limit to 'redrawtime'.
337 profile_setlimit(p_rdt, &(cur->hl.tm));
338 # endif
339 cur = cur->next;
340 }
341 search_hl->buf = wp->w_buffer;
342 search_hl->lnum = 0;
343 search_hl->first_lnum = 0;
344 // time limit is set at the toplevel, for all windows
345 }
346
347 /*
348 * If there is a match fill "shl" and return one.
349 * Return zero otherwise.
350 */
351 static int
352 next_search_hl_pos(
353 match_T *shl, // points to a match
354 linenr_T lnum,
355 posmatch_T *posmatch, // match positions
356 colnr_T mincol) // minimal column for a match
357 {
358 int i;
359 int found = -1;
360
361 for (i = posmatch->cur; i < MAXPOSMATCH; i++)
362 {
363 llpos_T *pos = &posmatch->pos[i];
364
365 if (pos->lnum == 0)
366 break;
367 if (pos->len == 0 && pos->col < mincol)
368 continue;
369 if (pos->lnum == lnum)
370 {
371 if (found >= 0)
372 {
373 // if this match comes before the one at "found" then swap
374 // them
375 if (pos->col < posmatch->pos[found].col)
376 {
377 llpos_T tmp = *pos;
378
379 *pos = posmatch->pos[found];
380 posmatch->pos[found] = tmp;
381 }
382 }
383 else
384 found = i;
385 }
386 }
387 posmatch->cur = 0;
388 if (found >= 0)
389 {
390 colnr_T start = posmatch->pos[found].col == 0
391 ? 0 : posmatch->pos[found].col - 1;
392 colnr_T end = posmatch->pos[found].col == 0
393 ? MAXCOL : start + posmatch->pos[found].len;
394
395 shl->lnum = lnum;
396 shl->rm.startpos[0].lnum = 0;
397 shl->rm.startpos[0].col = start;
398 shl->rm.endpos[0].lnum = 0;
399 shl->rm.endpos[0].col = end;
400 shl->is_addpos = TRUE;
401 posmatch->cur = found + 1;
402 return 1;
403 }
404 return 0;
405 }
406
407 /*
408 * Search for a next 'hlsearch' or match.
409 * Uses shl->buf.
410 * Sets shl->lnum and shl->rm contents.
411 * Note: Assumes a previous match is always before "lnum", unless
412 * shl->lnum is zero.
413 * Careful: Any pointers for buffer lines will become invalid.
414 */
415 static void
416 next_search_hl(
417 win_T *win,
418 match_T *search_hl,
419 match_T *shl, // points to search_hl or a match
420 linenr_T lnum,
421 colnr_T mincol, // minimal column for a match
422 matchitem_T *cur) // to retrieve match positions if any
423 {
424 linenr_T l;
425 colnr_T matchcol;
426 long nmatched;
427 int called_emsg_before = called_emsg;
428
429 // for :{range}s/pat only highlight inside the range
430 if (lnum < search_first_line || lnum > search_last_line)
431 {
432 shl->lnum = 0;
433 return;
434 }
435
436 if (shl->lnum != 0)
437 {
438 // Check for three situations:
439 // 1. If the "lnum" is below a previous match, start a new search.
440 // 2. If the previous match includes "mincol", use it.
441 // 3. Continue after the previous match.
442 l = shl->lnum + shl->rm.endpos[0].lnum - shl->rm.startpos[0].lnum;
443 if (lnum > l)
444 shl->lnum = 0;
445 else if (lnum < l || shl->rm.endpos[0].col > mincol)
446 return;
447 }
448
449 // Repeat searching for a match until one is found that includes "mincol"
450 // or none is found in this line.
451 for (;;)
452 {
453 # ifdef FEAT_RELTIME
454 // Stop searching after passing the time limit.
455 if (profile_passed_limit(&(shl->tm)))
456 {
457 shl->lnum = 0; // no match found in time
458 break;
459 }
460 # endif
461 // Three situations:
462 // 1. No useful previous match: search from start of line.
463 // 2. Not Vi compatible or empty match: continue at next character.
464 // Break the loop if this is beyond the end of the line.
465 // 3. Vi compatible searching: continue at end of previous match.
466 if (shl->lnum == 0)
467 matchcol = 0;
468 else if (vim_strchr(p_cpo, CPO_SEARCH) == NULL
469 || (shl->rm.endpos[0].lnum == 0
470 && shl->rm.endpos[0].col <= shl->rm.startpos[0].col))
471 {
472 char_u *ml;
473
474 matchcol = shl->rm.startpos[0].col;
475 ml = ml_get_buf(shl->buf, lnum, FALSE) + matchcol;
476 if (*ml == NUL)
477 {
478 ++matchcol;
479 shl->lnum = 0;
480 break;
481 }
482 if (has_mbyte)
483 matchcol += mb_ptr2len(ml);
484 else
485 ++matchcol;
486 }
487 else
488 matchcol = shl->rm.endpos[0].col;
489
490 shl->lnum = lnum;
491 if (shl->rm.regprog != NULL)
492 {
493 // Remember whether shl->rm is using a copy of the regprog in
494 // cur->match.
495 int regprog_is_copy = (shl != search_hl && cur != NULL
496 && shl == &cur->hl
497 && cur->match.regprog == cur->hl.rm.regprog);
498 int timed_out = FALSE;
499
500 nmatched = vim_regexec_multi(&shl->rm, win, shl->buf, lnum,
501 matchcol,
502 #ifdef FEAT_RELTIME
503 &(shl->tm), &timed_out
504 #else
505 NULL, NULL
506 #endif
507 );
508 // Copy the regprog, in case it got freed and recompiled.
509 if (regprog_is_copy)
510 cur->match.regprog = cur->hl.rm.regprog;
511
512 if (called_emsg > called_emsg_before || got_int || timed_out)
513 {
514 // Error while handling regexp: stop using this regexp.
515 if (shl == search_hl)
516 {
517 // don't free regprog in the match list, it's a copy
518 vim_regfree(shl->rm.regprog);
519 set_no_hlsearch(TRUE);
520 }
521 shl->rm.regprog = NULL;
522 shl->lnum = 0;
523 got_int = FALSE; // avoid the "Type :quit to exit Vim" message
524 break;
525 }
526 }
527 else if (cur != NULL)
528 nmatched = next_search_hl_pos(shl, lnum, &(cur->pos), matchcol);
529 else
530 nmatched = 0;
531 if (nmatched == 0)
532 {
533 shl->lnum = 0; // no match found
534 break;
535 }
536 if (shl->rm.startpos[0].lnum > 0
537 || shl->rm.startpos[0].col >= mincol
538 || nmatched > 1
539 || shl->rm.endpos[0].col > mincol)
540 {
541 shl->lnum += shl->rm.startpos[0].lnum;
542 break; // useful match found
543 }
544 }
545 }
546
547 /*
548 * Advance to the match in window "wp" line "lnum" or past it.
549 */
550 void
551 prepare_search_hl(win_T *wp, match_T *search_hl, linenr_T lnum)
552 {
553 matchitem_T *cur; // points to the match list
554 match_T *shl; // points to search_hl or a match
555 int shl_flag; // flag to indicate whether search_hl
556 // has been processed or not
557 int pos_inprogress; // marks that position match search is
558 // in progress
559 int n;
560
561 // When using a multi-line pattern, start searching at the top
562 // of the window or just after a closed fold.
563 // Do this both for search_hl and the match list.
564 cur = wp->w_match_head;
565 shl_flag = WIN_IS_POPUP(wp); // skip search_hl in a popup window
566 while (cur != NULL || shl_flag == FALSE)
567 {
568 if (shl_flag == FALSE)
569 {
570 shl = search_hl;
571 shl_flag = TRUE;
572 }
573 else
574 shl = &cur->hl;
575 if (shl->rm.regprog != NULL
576 && shl->lnum == 0
577 && re_multiline(shl->rm.regprog))
578 {
579 if (shl->first_lnum == 0)
580 {
581 # ifdef FEAT_FOLDING
582 for (shl->first_lnum = lnum;
583 shl->first_lnum > wp->w_topline; --shl->first_lnum)
584 if (hasFoldingWin(wp, shl->first_lnum - 1,
585 NULL, NULL, TRUE, NULL))
586 break;
587 # else
588 shl->first_lnum = wp->w_topline;
589 # endif
590 }
591 if (cur != NULL)
592 cur->pos.cur = 0;
593 pos_inprogress = TRUE;
594 n = 0;
595 while (shl->first_lnum < lnum && (shl->rm.regprog != NULL
596 || (cur != NULL && pos_inprogress)))
597 {
598 next_search_hl(wp, search_hl, shl, shl->first_lnum, (colnr_T)n,
599 shl == search_hl ? NULL : cur);
600 pos_inprogress = cur == NULL || cur->pos.cur == 0
601 ? FALSE : TRUE;
602 if (shl->lnum != 0)
603 {
604 shl->first_lnum = shl->lnum
605 + shl->rm.endpos[0].lnum
606 - shl->rm.startpos[0].lnum;
607 n = shl->rm.endpos[0].col;
608 }
609 else
610 {
611 ++shl->first_lnum;
612 n = 0;
613 }
614 }
615 }
616 if (shl != search_hl && cur != NULL)
617 cur = cur->next;
618 }
619 }
620
621 /*
622 * Prepare for 'hlsearch' and match highlighting in one window line.
623 * Return TRUE if there is such highlighting and set "search_attr" to the
624 * current highlight attribute.
625 */
626 int
627 prepare_search_hl_line(
628 win_T *wp,
629 linenr_T lnum,
630 colnr_T mincol,
631 char_u **line,
632 match_T *search_hl,
633 int *search_attr)
634 {
635 matchitem_T *cur; // points to the match list
636 match_T *shl; // points to search_hl or a match
637 int shl_flag; // flag to indicate whether search_hl
638 // has been processed or not
639 int area_highlighting = FALSE;
640
641 // Handle highlighting the last used search pattern and matches.
642 // Do this for both search_hl and the match list.
643 // Do not use search_hl in a popup window.
644 cur = wp->w_match_head;
645 shl_flag = WIN_IS_POPUP(wp);
646 while (cur != NULL || shl_flag == FALSE)
647 {
648 if (shl_flag == FALSE)
649 {
650 shl = search_hl;
651 shl_flag = TRUE;
652 }
653 else
654 shl = &cur->hl;
655 shl->startcol = MAXCOL;
656 shl->endcol = MAXCOL;
657 shl->attr_cur = 0;
658 shl->is_addpos = FALSE;
659 if (cur != NULL)
660 cur->pos.cur = 0;
661 next_search_hl(wp, search_hl, shl, lnum, mincol,
662 shl == search_hl ? NULL : cur);
663
664 // Need to get the line again, a multi-line regexp may have made it
665 // invalid.
666 *line = ml_get_buf(wp->w_buffer, lnum, FALSE);
667
668 if (shl->lnum != 0 && shl->lnum <= lnum)
669 {
670 if (shl->lnum == lnum)
671 shl->startcol = shl->rm.startpos[0].col;
672 else
673 shl->startcol = 0;
674 if (lnum == shl->lnum + shl->rm.endpos[0].lnum
675 - shl->rm.startpos[0].lnum)
676 shl->endcol = shl->rm.endpos[0].col;
677 else
678 shl->endcol = MAXCOL;
679 // Highlight one character for an empty match.
680 if (shl->startcol == shl->endcol)
681 {
682 if (has_mbyte && (*line)[shl->endcol] != NUL)
683 shl->endcol += (*mb_ptr2len)((*line) + shl->endcol);
684 else
685 ++shl->endcol;
686 }
687 if ((long)shl->startcol < mincol) // match at leftcol
688 {
689 shl->attr_cur = shl->attr;
690 *search_attr = shl->attr;
691 }
692 area_highlighting = TRUE;
693 }
694 if (shl != search_hl && cur != NULL)
695 cur = cur->next;
696 }
697 return area_highlighting;
698 }
699
700 /*
701 * For a position in a line: Check for start/end of 'hlsearch' and other
702 * matches.
703 * After end, check for start/end of next match.
704 * When another match, have to check for start again.
705 * Watch out for matching an empty string!
706 * Return the updated search_attr.
707 */
708 int
709 update_search_hl(
710 win_T *wp,
711 linenr_T lnum,
712 colnr_T col,
713 char_u **line,
714 match_T *search_hl,
715 int *has_match_conc UNUSED,
716 int *match_conc UNUSED,
717 int did_line_attr,
718 int lcs_eol_one)
719 {
720 matchitem_T *cur; // points to the match list
721 match_T *shl; // points to search_hl or a match
722 int shl_flag; // flag to indicate whether search_hl
723 // has been processed or not
724 int pos_inprogress; // marks that position match search is in
725 // progress
726 int search_attr = 0;
727
728
729 // Do this for 'search_hl' and the match list (ordered by priority).
730 cur = wp->w_match_head;
731 shl_flag = WIN_IS_POPUP(wp);
732 while (cur != NULL || shl_flag == FALSE)
733 {
734 if (shl_flag == FALSE
735 && (cur == NULL
736 || cur->priority > SEARCH_HL_PRIORITY))
737 {
738 shl = search_hl;
739 shl_flag = TRUE;
740 }
741 else
742 shl = &cur->hl;
743 if (cur != NULL)
744 cur->pos.cur = 0;
745 pos_inprogress = TRUE;
746 while (shl->rm.regprog != NULL || (cur != NULL && pos_inprogress))
747 {
748 if (shl->startcol != MAXCOL
749 && col >= shl->startcol
750 && col < shl->endcol)
751 {
752 int next_col = col + mb_ptr2len(*line + col);
753
754 if (shl->endcol < next_col)
755 shl->endcol = next_col;
756 shl->attr_cur = shl->attr;
757 # ifdef FEAT_CONCEAL
758 // Match with the "Conceal" group results in hiding
759 // the match.
760 if (cur != NULL
761 && shl != search_hl
762 && syn_name2id((char_u *)"Conceal") == cur->hlg_id)
763 {
764 *has_match_conc = col == shl->startcol ? 2 : 1;
765 *match_conc = cur->conceal_char;
766 }
767 else
768 *has_match_conc = 0;
769 # endif
770 }
771 else if (col == shl->endcol)
772 {
773 shl->attr_cur = 0;
774 next_search_hl(wp, search_hl, shl, lnum, col,
775 shl == search_hl ? NULL : cur);
776 pos_inprogress = !(cur == NULL || cur->pos.cur == 0);
777
778 // Need to get the line again, a multi-line regexp may have
779 // made it invalid.
780 *line = ml_get_buf(wp->w_buffer, lnum, FALSE);
781
782 if (shl->lnum == lnum)
783 {
784 shl->startcol = shl->rm.startpos[0].col;
785 if (shl->rm.endpos[0].lnum == 0)
786 shl->endcol = shl->rm.endpos[0].col;
787 else
788 shl->endcol = MAXCOL;
789
790 if (shl->startcol == shl->endcol)
791 {
792 // highlight empty match, try again after
793 // it
794 if (has_mbyte)
795 shl->endcol += (*mb_ptr2len)(*line + shl->endcol);
796 else
797 ++shl->endcol;
798 }
799
800 // Loop to check if the match starts at the
801 // current position
802 continue;
803 }
804 }
805 break;
806 }
807 if (shl != search_hl && cur != NULL)
808 cur = cur->next;
809 }
810
811 // Use attributes from match with highest priority among 'search_hl' and
812 // the match list.
813 cur = wp->w_match_head;
814 shl_flag = WIN_IS_POPUP(wp);
815 while (cur != NULL || shl_flag == FALSE)
816 {
817 if (shl_flag == FALSE
818 && (cur == NULL ||
819 cur->priority > SEARCH_HL_PRIORITY))
820 {
821 shl = search_hl;
822 shl_flag = TRUE;
823 }
824 else
825 shl = &cur->hl;
826 if (shl->attr_cur != 0)
827 search_attr = shl->attr_cur;
828 if (shl != search_hl && cur != NULL)
829 cur = cur->next;
830 }
831 // Only highlight one character after the last column.
832 if (*(*line + col) == NUL && (did_line_attr >= 1
833 || (wp->w_p_list && lcs_eol_one == -1)))
834 search_attr = 0;
835 return search_attr;
836 }
837
838 int
839 get_prevcol_hl_flag(win_T *wp, match_T *search_hl, long curcol)
840 {
841 long prevcol = curcol;
842 int prevcol_hl_flag = FALSE;
843 matchitem_T *cur; // points to the match list
844
845 // we're not really at that column when skipping some text
846 if ((long)(wp->w_p_wrap ? wp->w_skipcol : wp->w_leftcol) > prevcol)
847 ++prevcol;
848
849 if (!search_hl->is_addpos && prevcol == (long)search_hl->startcol)
850 prevcol_hl_flag = TRUE;
851 else
852 {
853 cur = wp->w_match_head;
854 while (cur != NULL)
855 {
856 if (!cur->hl.is_addpos && prevcol == (long)cur->hl.startcol)
857 {
858 prevcol_hl_flag = TRUE;
859 break;
860 }
861 cur = cur->next;
862 }
863 }
864 return prevcol_hl_flag;
865 }
866
867 /*
868 * Get highlighting for the char after the text in "char_attr" from 'hlsearch'
869 * or match highlighting.
870 */
871 void
872 get_search_match_hl(win_T *wp, match_T *search_hl, long col, int *char_attr)
873 {
874 matchitem_T *cur; // points to the match list
875 match_T *shl; // points to search_hl or a match
876 int shl_flag; // flag to indicate whether search_hl
877 // has been processed or not
878
879 cur = wp->w_match_head;
880 shl_flag = WIN_IS_POPUP(wp);
881 while (cur != NULL || shl_flag == FALSE)
882 {
883 if (shl_flag == FALSE
884 && ((cur != NULL
885 && cur->priority > SEARCH_HL_PRIORITY)
886 || cur == NULL))
887 {
888 shl = search_hl;
889 shl_flag = TRUE;
890 }
891 else
892 shl = &cur->hl;
893 if (col - 1 == (long)shl->startcol
894 && (shl == search_hl || !shl->is_addpos))
895 *char_attr = shl->attr;
896 if (shl != search_hl && cur != NULL)
897 cur = cur->next;
898 }
899 }
900
901 #endif // FEAT_SEARCH_EXTRA
902
903 #if defined(FEAT_EVAL) || defined(PROTO)
904 # ifdef FEAT_SEARCH_EXTRA
905 static int
906 matchadd_dict_arg(typval_T *tv, char_u **conceal_char, win_T **win)
907 {
908 dictitem_T *di;
909
910 if (tv->v_type != VAR_DICT)
911 {
912 emsg(_(e_dictreq));
913 return FAIL;
914 }
915
916 if (dict_find(tv->vval.v_dict, (char_u *)"conceal", -1) != NULL)
917 *conceal_char = dict_get_string(tv->vval.v_dict,
918 (char_u *)"conceal", FALSE);
919
920 if ((di = dict_find(tv->vval.v_dict, (char_u *)"window", -1)) != NULL)
921 {
922 *win = find_win_by_nr_or_id(&di->di_tv);
923 if (*win == NULL)
924 {
925 emsg(_(e_invalwindow));
926 return FAIL;
927 }
928 }
929
930 return OK;
931 }
932 #endif
933
934 /*
935 * "clearmatches()" function
936 */
937 void
938 f_clearmatches(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
939 {
940 #ifdef FEAT_SEARCH_EXTRA
941 win_T *win = get_optional_window(argvars, 0);
942
943 if (win != NULL)
944 clear_matches(win);
945 #endif
946 }
947
948 /*
949 * "getmatches()" function
950 */
951 void
952 f_getmatches(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
953 {
954 # ifdef FEAT_SEARCH_EXTRA
955 dict_T *dict;
956 matchitem_T *cur;
957 int i;
958 win_T *win = get_optional_window(argvars, 0);
959
960 if (rettv_list_alloc(rettv) == FAIL || win == NULL)
961 return;
962
963 cur = win->w_match_head;
964 while (cur != NULL)
965 {
966 dict = dict_alloc();
967 if (dict == NULL)
968 return;
969 if (cur->match.regprog == NULL)
970 {
971 // match added with matchaddpos()
972 for (i = 0; i < MAXPOSMATCH; ++i)
973 {
974 llpos_T *llpos;
975 char buf[30]; // use 30 to avoid compiler warning
976 list_T *l;
977
978 llpos = &cur->pos.pos[i];
979 if (llpos->lnum == 0)
980 break;
981 l = list_alloc();
982 if (l == NULL)
983 break;
984 list_append_number(l, (varnumber_T)llpos->lnum);
985 if (llpos->col > 0)
986 {
987 list_append_number(l, (varnumber_T)llpos->col);
988 list_append_number(l, (varnumber_T)llpos->len);
989 }
990 sprintf(buf, "pos%d", i + 1);
991 dict_add_list(dict, buf, l);
992 }
993 }
994 else
995 {
996 dict_add_string(dict, "pattern", cur->pattern);
997 }
998 dict_add_string(dict, "group", syn_id2name(cur->hlg_id));
999 dict_add_number(dict, "priority", (long)cur->priority);
1000 dict_add_number(dict, "id", (long)cur->id);
1001 # if defined(FEAT_CONCEAL)
1002 if (cur->conceal_char)
1003 {
1004 char_u buf[MB_MAXBYTES + 1];
1005
1006 buf[(*mb_char2bytes)((int)cur->conceal_char, buf)] = NUL;
1007 dict_add_string(dict, "conceal", (char_u *)&buf);
1008 }
1009 # endif
1010 list_append_dict(rettv->vval.v_list, dict);
1011 cur = cur->next;
1012 }
1013 # endif
1014 }
1015
1016 /*
1017 * "setmatches()" function
1018 */
1019 void
1020 f_setmatches(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
1021 {
1022 #ifdef FEAT_SEARCH_EXTRA
1023 list_T *l;
1024 listitem_T *li;
1025 dict_T *d;
1026 list_T *s = NULL;
1027 win_T *win = get_optional_window(argvars, 1);
1028
1029 rettv->vval.v_number = -1;
1030 if (argvars[0].v_type != VAR_LIST)
1031 {
1032 emsg(_(e_listreq));
1033 return;
1034 }
1035 if (win == NULL)
1036 return;
1037
1038 if ((l = argvars[0].vval.v_list) != NULL)
1039 {
1040 // To some extent make sure that we are dealing with a list from
1041 // "getmatches()".
1042 li = l->lv_first;
1043 while (li != NULL)
1044 {
1045 if (li->li_tv.v_type != VAR_DICT
1046 || (d = li->li_tv.vval.v_dict) == NULL)
1047 {
1048 emsg(_(e_invarg));
1049 return;
1050 }
1051 if (!(dict_find(d, (char_u *)"group", -1) != NULL
1052 && (dict_find(d, (char_u *)"pattern", -1) != NULL
1053 || dict_find(d, (char_u *)"pos1", -1) != NULL)
1054 && dict_find(d, (char_u *)"priority", -1) != NULL
1055 && dict_find(d, (char_u *)"id", -1) != NULL))
1056 {
1057 emsg(_(e_invarg));
1058 return;
1059 }
1060 li = li->li_next;
1061 }
1062
1063 clear_matches(win);
1064 li = l->lv_first;
1065 while (li != NULL)
1066 {
1067 int i = 0;
1068 char buf[30]; // use 30 to avoid compiler warning
1069 dictitem_T *di;
1070 char_u *group;
1071 int priority;
1072 int id;
1073 char_u *conceal;
1074
1075 d = li->li_tv.vval.v_dict;
1076 if (dict_find(d, (char_u *)"pattern", -1) == NULL)
1077 {
1078 if (s == NULL)
1079 {
1080 s = list_alloc();
1081 if (s == NULL)
1082 return;
1083 }
1084
1085 // match from matchaddpos()
1086 for (i = 1; i < 9; i++)
1087 {
1088 sprintf((char *)buf, (char *)"pos%d", i);
1089 if ((di = dict_find(d, (char_u *)buf, -1)) != NULL)
1090 {
1091 if (di->di_tv.v_type != VAR_LIST)
1092 return;
1093
1094 list_append_tv(s, &di->di_tv);
1095 s->lv_refcount++;
1096 }
1097 else
1098 break;
1099 }
1100 }
1101
1102 group = dict_get_string(d, (char_u *)"group", TRUE);
1103 priority = (int)dict_get_number(d, (char_u *)"priority");
1104 id = (int)dict_get_number(d, (char_u *)"id");
1105 conceal = dict_find(d, (char_u *)"conceal", -1) != NULL
1106 ? dict_get_string(d, (char_u *)"conceal", TRUE)
1107 : NULL;
1108 if (i == 0)
1109 {
1110 match_add(win, group,
1111 dict_get_string(d, (char_u *)"pattern", FALSE),
1112 priority, id, NULL, conceal);
1113 }
1114 else
1115 {
1116 match_add(win, group, NULL, priority, id, s, conceal);
1117 list_unref(s);
1118 s = NULL;
1119 }
1120 vim_free(group);
1121 vim_free(conceal);
1122
1123 li = li->li_next;
1124 }
1125 rettv->vval.v_number = 0;
1126 }
1127 #endif
1128 }
1129
1130 /*
1131 * "matchadd()" function
1132 */
1133 void
1134 f_matchadd(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
1135 {
1136 # ifdef FEAT_SEARCH_EXTRA
1137 char_u buf[NUMBUFLEN];
1138 char_u *grp = tv_get_string_buf_chk(&argvars[0], buf); // group
1139 char_u *pat = tv_get_string_buf_chk(&argvars[1], buf); // pattern
1140 int prio = 10; // default priority
1141 int id = -1;
1142 int error = FALSE;
1143 char_u *conceal_char = NULL;
1144 win_T *win = curwin;
1145
1146 rettv->vval.v_number = -1;
1147
1148 if (grp == NULL || pat == NULL)
1149 return;
1150 if (argvars[2].v_type != VAR_UNKNOWN)
1151 {
1152 prio = (int)tv_get_number_chk(&argvars[2], &error);
1153 if (argvars[3].v_type != VAR_UNKNOWN)
1154 {
1155 id = (int)tv_get_number_chk(&argvars[3], &error);
1156 if (argvars[4].v_type != VAR_UNKNOWN
1157 && matchadd_dict_arg(&argvars[4], &conceal_char, &win) == FAIL)
1158 return;
1159 }
1160 }
1161 if (error == TRUE)
1162 return;
1163 if (id >= 1 && id <= 3)
1164 {
1165 semsg(_("E798: ID is reserved for \":match\": %d"), id);
1166 return;
1167 }
1168
1169 rettv->vval.v_number = match_add(win, grp, pat, prio, id, NULL,
1170 conceal_char);
1171 # endif
1172 }
1173
1174 /*
1175 * "matchaddpos()" function
1176 */
1177 void
1178 f_matchaddpos(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
1179 {
1180 # ifdef FEAT_SEARCH_EXTRA
1181 char_u buf[NUMBUFLEN];
1182 char_u *group;
1183 int prio = 10;
1184 int id = -1;
1185 int error = FALSE;
1186 list_T *l;
1187 char_u *conceal_char = NULL;
1188 win_T *win = curwin;
1189
1190 rettv->vval.v_number = -1;
1191
1192 group = tv_get_string_buf_chk(&argvars[0], buf);
1193 if (group == NULL)
1194 return;
1195
1196 if (argvars[1].v_type != VAR_LIST)
1197 {
1198 semsg(_(e_listarg), "matchaddpos()");
1199 return;
1200 }
1201 l = argvars[1].vval.v_list;
1202 if (l == NULL)
1203 return;
1204
1205 if (argvars[2].v_type != VAR_UNKNOWN)
1206 {
1207 prio = (int)tv_get_number_chk(&argvars[2], &error);
1208 if (argvars[3].v_type != VAR_UNKNOWN)
1209 {
1210 id = (int)tv_get_number_chk(&argvars[3], &error);
1211
1212 if (argvars[4].v_type != VAR_UNKNOWN
1213 && matchadd_dict_arg(&argvars[4], &conceal_char, &win) == FAIL)
1214 return;
1215 }
1216 }
1217 if (error == TRUE)
1218 return;
1219
1220 // id == 3 is ok because matchaddpos() is supposed to substitute :3match
1221 if (id == 1 || id == 2)
1222 {
1223 semsg(_("E798: ID is reserved for \":match\": %d"), id);
1224 return;
1225 }
1226
1227 rettv->vval.v_number = match_add(win, group, NULL, prio, id, l,
1228 conceal_char);
1229 # endif
1230 }
1231
1232 /*
1233 * "matcharg()" function
1234 */
1235 void
1236 f_matcharg(typval_T *argvars UNUSED, typval_T *rettv)
1237 {
1238 if (rettv_list_alloc(rettv) == OK)
1239 {
1240 # ifdef FEAT_SEARCH_EXTRA
1241 int id = (int)tv_get_number(&argvars[0]);
1242 matchitem_T *m;
1243
1244 if (id >= 1 && id <= 3)
1245 {
1246 if ((m = (matchitem_T *)get_match(curwin, id)) != NULL)
1247 {
1248 list_append_string(rettv->vval.v_list,
1249 syn_id2name(m->hlg_id), -1);
1250 list_append_string(rettv->vval.v_list, m->pattern, -1);
1251 }
1252 else
1253 {
1254 list_append_string(rettv->vval.v_list, NULL, -1);
1255 list_append_string(rettv->vval.v_list, NULL, -1);
1256 }
1257 }
1258 # endif
1259 }
1260 }
1261
1262 /*
1263 * "matchdelete()" function
1264 */
1265 void
1266 f_matchdelete(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
1267 {
1268 # ifdef FEAT_SEARCH_EXTRA
1269 win_T *win = get_optional_window(argvars, 1);
1270
1271 if (win == NULL)
1272 rettv->vval.v_number = -1;
1273 else
1274 rettv->vval.v_number = match_delete(win,
1275 (int)tv_get_number(&argvars[0]), TRUE);
1276 # endif
1277 }
1278 #endif
1279
1280 #if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
1281 /*
1282 * ":[N]match {group} {pattern}"
1283 * Sets nextcmd to the start of the next command, if any. Also called when
1284 * skipping commands to find the next command.
1285 */
1286 void
1287 ex_match(exarg_T *eap)
1288 {
1289 char_u *p;
1290 char_u *g = NULL;
1291 char_u *end;
1292 int c;
1293 int id;
1294
1295 if (eap->line2 <= 3)
1296 id = eap->line2;
1297 else
1298 {
1299 emsg(_(e_invcmd));
1300 return;
1301 }
1302
1303 // First clear any old pattern.
1304 if (!eap->skip)
1305 match_delete(curwin, id, FALSE);
1306
1307 if (ends_excmd2(eap->cmd, eap->arg))
1308 end = eap->arg;
1309 else if ((STRNICMP(eap->arg, "none", 4) == 0
1310 && (VIM_ISWHITE(eap->arg[4])
1311 || ends_excmd2(eap->arg, eap->arg + 4))))
1312 end = eap->arg + 4;
1313 else
1314 {
1315 p = skiptowhite(eap->arg);
1316 if (!eap->skip)
1317 g = vim_strnsave(eap->arg, p - eap->arg);
1318 p = skipwhite(p);
1319 if (*p == NUL)
1320 {
1321 // There must be two arguments.
1322 vim_free(g);
1323 semsg(_(e_invarg2), eap->arg);
1324 return;
1325 }
1326 end = skip_regexp(p + 1, *p, TRUE);
1327 if (!eap->skip)
1328 {
1329 if (*end != NUL && !ends_excmd2(end, skipwhite(end + 1)))
1330 {
1331 vim_free(g);
1332 eap->errmsg = e_trailing;
1333 return;
1334 }
1335 if (*end != *p)
1336 {
1337 vim_free(g);
1338 semsg(_(e_invarg2), p);
1339 return;
1340 }
1341
1342 c = *end;
1343 *end = NUL;
1344 match_add(curwin, g, p + 1, 10, id, NULL, NULL);
1345 vim_free(g);
1346 *end = c;
1347 }
1348 }
1349 eap->nextcmd = find_nextcmd(end);
1350 }
1351 #endif