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 = ex_errmsg(e_trailing_arg, end); 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 1352