Mercurial > vim
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 |