1 /* vi:set ts=8 sts=4 sw=4: 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 * search.c: code for normal mode searching commands 11 */ 12 13 #include "vim.h" 14 15 static void save_re_pat __ARGS((int idx, char_u *pat, int magic)); 16 #ifdef FEAT_EVAL 17 static int first_submatch __ARGS((regmmatch_T *rp)); 18 #endif 19 static int check_prevcol __ARGS((char_u *linep, int col, int ch, int *prevcol)); 20 static int inmacro __ARGS((char_u *, char_u *)); 21 static int check_linecomment __ARGS((char_u *line)); 22 static int cls __ARGS((void)); 23 static int skip_chars __ARGS((int, int)); 24 #ifdef FEAT_TEXTOBJ 25 static void back_in_line __ARGS((void)); 26 static void find_first_blank __ARGS((pos_T *)); 27 static void findsent_forward __ARGS((long count, int at_start_sent)); 28 #endif 29 #ifdef FEAT_FIND_ID 30 static void show_pat_in_path __ARGS((char_u *, int, 31 int, int, FILE *, linenr_T *, long)); 32 #endif 33 #ifdef FEAT_VIMINFO 34 static void wvsp_one __ARGS((FILE *fp, int idx, char *s, int sc)); 35 #endif 36 37 static char_u *top_bot_msg = (char_u *)N_("search hit TOP, continuing at BOTTOM"); 38 static char_u *bot_top_msg = (char_u *)N_("search hit BOTTOM, continuing at TOP"); 39 40 /* 41 * This file contains various searching-related routines. These fall into 42 * three groups: 43 * 1. string searches (for /, ?, n, and N) 44 * 2. character searches within a single line (for f, F, t, T, etc) 45 * 3. "other" kinds of searches like the '%' command, and 'word' searches. 46 */ 47 48 /* 49 * String searches 50 * 51 * The string search functions are divided into two levels: 52 * lowest: searchit(); uses an pos_T for starting position and found match. 53 * Highest: do_search(); uses curwin->w_cursor; calls searchit(). 54 * 55 * The last search pattern is remembered for repeating the same search. 56 * This pattern is shared between the :g, :s, ? and / commands. 57 * This is in search_regcomp(). 58 * 59 * The actual string matching is done using a heavily modified version of 60 * Henry Spencer's regular expression library. See regexp.c. 61 */ 62 63 /* The offset for a search command is store in a soff struct */ 64 /* Note: only spats[0].off is really used */ 65 struct soffset 66 { 67 int dir; /* search direction */ 68 int line; /* search has line offset */ 69 int end; /* search set cursor at end */ 70 long off; /* line or char offset */ 71 }; 72 73 /* A search pattern and its attributes are stored in a spat struct */ 74 struct spat 75 { 76 char_u *pat; /* the pattern (in allocated memory) or NULL */ 77 int magic; /* magicness of the pattern */ 78 int no_scs; /* no smarcase for this pattern */ 79 struct soffset off; 80 }; 81 82 /* 83 * Two search patterns are remembered: One for the :substitute command and 84 * one for other searches. last_idx points to the one that was used the last 85 * time. 86 */ 87 static struct spat spats[2] = 88 { 89 {NULL, TRUE, FALSE, {'/', 0, 0, 0L}}, /* last used search pat */ 90 {NULL, TRUE, FALSE, {'/', 0, 0, 0L}} /* last used substitute pat */ 91 }; 92 93 static int last_idx = 0; /* index in spats[] for RE_LAST */ 94 95 #if defined(FEAT_AUTOCMD) || defined(FEAT_EVAL) || defined(PROTO) 96 /* copy of spats[], for keeping the search patterns while executing autocmds */ 97 static struct spat saved_spats[2]; 98 static int saved_last_idx = 0; 99 # ifdef FEAT_SEARCH_EXTRA 100 static int saved_no_hlsearch = 0; 101 # endif 102 #endif 103 104 static char_u *mr_pattern = NULL; /* pattern used by search_regcomp() */ 105 #ifdef FEAT_RIGHTLEFT 106 static int mr_pattern_alloced = FALSE; /* mr_pattern was allocated */ 107 static char_u *reverse_text __ARGS((char_u *s)); 108 #endif 109 110 #ifdef FEAT_FIND_ID 111 /* 112 * Type used by find_pattern_in_path() to remember which included files have 113 * been searched already. 114 */ 115 typedef struct SearchedFile 116 { 117 FILE *fp; /* File pointer */ 118 char_u *name; /* Full name of file */ 119 linenr_T lnum; /* Line we were up to in file */ 120 int matched; /* Found a match in this file */ 121 } SearchedFile; 122 #endif 123 124 /* 125 * translate search pattern for vim_regcomp() 126 * 127 * pat_save == RE_SEARCH: save pat in spats[RE_SEARCH].pat (normal search cmd) 128 * pat_save == RE_SUBST: save pat in spats[RE_SUBST].pat (:substitute command) 129 * pat_save == RE_BOTH: save pat in both patterns (:global command) 130 * pat_use == RE_SEARCH: use previous search pattern if "pat" is NULL 131 * pat_use == RE_SUBST: use previous sustitute pattern if "pat" is NULL 132 * pat_use == RE_LAST: use last used pattern if "pat" is NULL 133 * options & SEARCH_HIS: put search string in history 134 * options & SEARCH_KEEP: keep previous search pattern 135 * 136 * returns FAIL if failed, OK otherwise. 137 */ 138 int 139 search_regcomp(pat, pat_save, pat_use, options, regmatch) 140 char_u *pat; 141 int pat_save; 142 int pat_use; 143 int options; 144 regmmatch_T *regmatch; /* return: pattern and ignore-case flag */ 145 { 146 int magic; 147 int i; 148 149 rc_did_emsg = FALSE; 150 magic = p_magic; 151 152 /* 153 * If no pattern given, use a previously defined pattern. 154 */ 155 if (pat == NULL || *pat == NUL) 156 { 157 if (pat_use == RE_LAST) 158 i = last_idx; 159 else 160 i = pat_use; 161 if (spats[i].pat == NULL) /* pattern was never defined */ 162 { 163 if (pat_use == RE_SUBST) 164 EMSG(_(e_nopresub)); 165 else 166 EMSG(_(e_noprevre)); 167 rc_did_emsg = TRUE; 168 return FAIL; 169 } 170 pat = spats[i].pat; 171 magic = spats[i].magic; 172 no_smartcase = spats[i].no_scs; 173 } 174 #ifdef FEAT_CMDHIST 175 else if (options & SEARCH_HIS) /* put new pattern in history */ 176 add_to_history(HIST_SEARCH, pat, TRUE, NUL); 177 #endif 178 179 #ifdef FEAT_RIGHTLEFT 180 if (mr_pattern_alloced) 181 { 182 vim_free(mr_pattern); 183 mr_pattern_alloced = FALSE; 184 } 185 186 if (curwin->w_p_rl && *curwin->w_p_rlc == 's') 187 { 188 char_u *rev_pattern; 189 190 rev_pattern = reverse_text(pat); 191 if (rev_pattern == NULL) 192 mr_pattern = pat; /* out of memory, keep normal pattern. */ 193 else 194 { 195 mr_pattern = rev_pattern; 196 mr_pattern_alloced = TRUE; 197 } 198 } 199 else 200 #endif 201 mr_pattern = pat; 202 203 /* 204 * Save the currently used pattern in the appropriate place, 205 * unless the pattern should not be remembered. 206 */ 207 if (!(options & SEARCH_KEEP)) 208 { 209 /* search or global command */ 210 if (pat_save == RE_SEARCH || pat_save == RE_BOTH) 211 save_re_pat(RE_SEARCH, pat, magic); 212 /* substitute or global command */ 213 if (pat_save == RE_SUBST || pat_save == RE_BOTH) 214 save_re_pat(RE_SUBST, pat, magic); 215 } 216 217 regmatch->rmm_ic = ignorecase(pat); 218 regmatch->regprog = vim_regcomp(pat, magic ? RE_MAGIC : 0); 219 if (regmatch->regprog == NULL) 220 return FAIL; 221 return OK; 222 } 223 224 /* 225 * Get search pattern used by search_regcomp(). 226 */ 227 char_u * 228 get_search_pat() 229 { 230 return mr_pattern; 231 } 232 233 #ifdef FEAT_RIGHTLEFT 234 /* 235 * Reverse text into allocated memory. 236 * Returns the allocated string, NULL when out of memory. 237 */ 238 static char_u * 239 reverse_text(s) 240 char_u *s; 241 { 242 unsigned len; 243 unsigned s_i, rev_i; 244 char_u *rev; 245 246 /* 247 * Reverse the pattern. 248 */ 249 len = (unsigned)STRLEN(s); 250 rev = alloc(len + 1); 251 if (rev != NULL) 252 { 253 rev_i = len; 254 for (s_i = 0; s_i < len; ++s_i) 255 { 256 # ifdef FEAT_MBYTE 257 if (has_mbyte) 258 { 259 int mb_len; 260 261 mb_len = (*mb_ptr2len_check)(s + s_i); 262 rev_i -= mb_len; 263 mch_memmove(rev + rev_i, s + s_i, mb_len); 264 s_i += mb_len - 1; 265 } 266 else 267 # endif 268 rev[--rev_i] = s[s_i]; 269 270 } 271 rev[len] = NUL; 272 } 273 return rev; 274 } 275 #endif 276 277 static void 278 save_re_pat(idx, pat, magic) 279 int idx; 280 char_u *pat; 281 int magic; 282 { 283 if (spats[idx].pat != pat) 284 { 285 vim_free(spats[idx].pat); 286 spats[idx].pat = vim_strsave(pat); 287 spats[idx].magic = magic; 288 spats[idx].no_scs = no_smartcase; 289 last_idx = idx; 290 #ifdef FEAT_SEARCH_EXTRA 291 /* If 'hlsearch' set and search pat changed: need redraw. */ 292 if (p_hls) 293 redraw_all_later(NOT_VALID); 294 no_hlsearch = FALSE; 295 #endif 296 } 297 } 298 299 #if defined(FEAT_AUTOCMD) || defined(FEAT_EVAL) || defined(PROTO) 300 /* 301 * Save the search patterns, so they can be restored later. 302 * Used before/after executing autocommands and user functions. 303 */ 304 static int save_level = 0; 305 306 void 307 save_search_patterns() 308 { 309 if (save_level++ == 0) 310 { 311 saved_spats[0] = spats[0]; 312 if (spats[0].pat != NULL) 313 saved_spats[0].pat = vim_strsave(spats[0].pat); 314 saved_spats[1] = spats[1]; 315 if (spats[1].pat != NULL) 316 saved_spats[1].pat = vim_strsave(spats[1].pat); 317 saved_last_idx = last_idx; 318 # ifdef FEAT_SEARCH_EXTRA 319 saved_no_hlsearch = no_hlsearch; 320 # endif 321 } 322 } 323 324 void 325 restore_search_patterns() 326 { 327 if (--save_level == 0) 328 { 329 vim_free(spats[0].pat); 330 spats[0] = saved_spats[0]; 331 vim_free(spats[1].pat); 332 spats[1] = saved_spats[1]; 333 last_idx = saved_last_idx; 334 # ifdef FEAT_SEARCH_EXTRA 335 no_hlsearch = saved_no_hlsearch; 336 # endif 337 } 338 } 339 #endif 340 341 /* 342 * Return TRUE when case should be ignored for search pattern "pat". 343 * Uses the 'ignorecase' and 'smartcase' options. 344 */ 345 int 346 ignorecase(pat) 347 char_u *pat; 348 { 349 char_u *p; 350 int ic; 351 352 ic = p_ic; 353 if (ic && !no_smartcase && p_scs 354 #ifdef FEAT_INS_EXPAND 355 && !(ctrl_x_mode && curbuf->b_p_inf) 356 #endif 357 ) 358 { 359 /* don't ignore case if pattern has uppercase */ 360 for (p = pat; *p; ) 361 { 362 #ifdef FEAT_MBYTE 363 int l; 364 365 if (has_mbyte && (l = (*mb_ptr2len_check)(p)) > 1) 366 { 367 if (enc_utf8 && utf_isupper(utf_ptr2char(p))) 368 { 369 ic = FALSE; 370 break; 371 } 372 p += l; 373 } 374 else 375 #endif 376 if (*p == '\\' && p[1] != NUL) /* skip "\S" et al. */ 377 p += 2; 378 else if (isupper(*p++)) 379 { 380 ic = FALSE; 381 break; 382 } 383 } 384 } 385 no_smartcase = FALSE; 386 387 return ic; 388 } 389 390 char_u * 391 last_search_pat() 392 { 393 return spats[last_idx].pat; 394 } 395 396 /* 397 * Reset search direction to forward. For "gd" and "gD" commands. 398 */ 399 void 400 reset_search_dir() 401 { 402 spats[0].off.dir = '/'; 403 } 404 405 #if defined(FEAT_EVAL) || defined(FEAT_VIMINFO) 406 /* 407 * Set the last search pattern. For ":let @/ =" and viminfo. 408 * Also set the saved search pattern, so that this works in an autocommand. 409 */ 410 void 411 set_last_search_pat(s, idx, magic, setlast) 412 char_u *s; 413 int idx; 414 int magic; 415 int setlast; 416 { 417 vim_free(spats[idx].pat); 418 /* An empty string means that nothing should be matched. */ 419 if (*s == NUL) 420 spats[idx].pat = NULL; 421 else 422 spats[idx].pat = vim_strsave(s); 423 spats[idx].magic = magic; 424 spats[idx].no_scs = FALSE; 425 spats[idx].off.dir = '/'; 426 spats[idx].off.line = FALSE; 427 spats[idx].off.end = FALSE; 428 spats[idx].off.off = 0; 429 if (setlast) 430 last_idx = idx; 431 if (save_level) 432 { 433 vim_free(saved_spats[idx].pat); 434 saved_spats[idx] = spats[0]; 435 if (spats[idx].pat == NULL) 436 saved_spats[idx].pat = NULL; 437 else 438 saved_spats[idx].pat = vim_strsave(spats[idx].pat); 439 saved_last_idx = last_idx; 440 } 441 # ifdef FEAT_SEARCH_EXTRA 442 /* If 'hlsearch' set and search pat changed: need redraw. */ 443 if (p_hls && idx == last_idx && !no_hlsearch) 444 redraw_all_later(NOT_VALID); 445 # endif 446 } 447 #endif 448 449 #ifdef FEAT_SEARCH_EXTRA 450 /* 451 * Get a regexp program for the last used search pattern. 452 * This is used for highlighting all matches in a window. 453 * Values returned in regmatch->regprog and regmatch->rmm_ic. 454 */ 455 void 456 last_pat_prog(regmatch) 457 regmmatch_T *regmatch; 458 { 459 if (spats[last_idx].pat == NULL) 460 { 461 regmatch->regprog = NULL; 462 return; 463 } 464 ++emsg_off; /* So it doesn't beep if bad expr */ 465 (void)search_regcomp((char_u *)"", 0, last_idx, SEARCH_KEEP, regmatch); 466 --emsg_off; 467 } 468 #endif 469 470 /* 471 * lowest level search function. 472 * Search for 'count'th occurrence of pattern 'pat' in direction 'dir'. 473 * Start at position 'pos' and return the found position in 'pos'. 474 * 475 * if (options & SEARCH_MSG) == 0 don't give any messages 476 * if (options & SEARCH_MSG) == SEARCH_NFMSG don't give 'notfound' messages 477 * if (options & SEARCH_MSG) == SEARCH_MSG give all messages 478 * if (options & SEARCH_HIS) put search pattern in history 479 * if (options & SEARCH_END) return position at end of match 480 * if (options & SEARCH_START) accept match at pos itself 481 * if (options & SEARCH_KEEP) keep previous search pattern 482 * if (options & SEARCH_FOLD) match only once in a closed fold 483 * if (options & SEARCH_PEEK) check for typed char, cancel search 484 * 485 * Return FAIL (zero) for failure, non-zero for success. 486 * When FEAT_EVAL is defined, returns the index of the first matching 487 * subpattern plus one; one if there was none. 488 */ 489 int 490 searchit(win, buf, pos, dir, pat, count, options, pat_use) 491 win_T *win; /* window to search in; can be NULL for a 492 buffer without a window! */ 493 buf_T *buf; 494 pos_T *pos; 495 int dir; 496 char_u *pat; 497 long count; 498 int options; 499 int pat_use; 500 { 501 int found; 502 linenr_T lnum; /* no init to shut up Apollo cc */ 503 regmmatch_T regmatch; 504 char_u *ptr; 505 colnr_T matchcol; 506 colnr_T startcol; 507 lpos_T endpos; 508 int loop; 509 pos_T start_pos; 510 int at_first_line; 511 int extra_col; 512 int match_ok; 513 long nmatched; 514 int submatch = 0; 515 linenr_T first_lnum; 516 #ifdef FEAT_SEARCH_EXTRA 517 int break_loop = FALSE; 518 #else 519 # define break_loop FALSE 520 #endif 521 522 if (search_regcomp(pat, RE_SEARCH, pat_use, 523 (options & (SEARCH_HIS + SEARCH_KEEP)), ®match) == FAIL) 524 { 525 if ((options & SEARCH_MSG) && !rc_did_emsg) 526 EMSG2(_("E383: Invalid search string: %s"), mr_pattern); 527 return FAIL; 528 } 529 530 if (options & SEARCH_START) 531 extra_col = 0; 532 #ifdef FEAT_MBYTE 533 /* Watch out for the "col" being MAXCOL - 2, used in a closed fold. */ 534 else if (has_mbyte && pos->lnum >= 1 && pos->lnum <= buf->b_ml.ml_line_count 535 && pos->col < MAXCOL - 2) 536 extra_col = (*mb_ptr2len_check)(ml_get_buf(buf, pos->lnum, FALSE) 537 + pos->col); 538 #endif 539 else 540 extra_col = 1; 541 542 /* 543 * find the string 544 */ 545 called_emsg = FALSE; 546 do /* loop for count */ 547 { 548 start_pos = *pos; /* remember start pos for detecting no match */ 549 found = 0; /* default: not found */ 550 at_first_line = TRUE; /* default: start in first line */ 551 if (pos->lnum == 0) /* correct lnum for when starting in line 0 */ 552 { 553 pos->lnum = 1; 554 pos->col = 0; 555 at_first_line = FALSE; /* not in first line now */ 556 } 557 558 /* 559 * Start searching in current line, unless searching backwards and 560 * we're in column 0. 561 */ 562 if (dir == BACKWARD && start_pos.col == 0) 563 { 564 lnum = pos->lnum - 1; 565 at_first_line = FALSE; 566 } 567 else 568 lnum = pos->lnum; 569 570 for (loop = 0; loop <= 1; ++loop) /* loop twice if 'wrapscan' set */ 571 { 572 for ( ; lnum > 0 && lnum <= buf->b_ml.ml_line_count; 573 lnum += dir, at_first_line = FALSE) 574 { 575 /* 576 * Look for a match somewhere in the line. 577 */ 578 first_lnum = lnum; 579 nmatched = vim_regexec_multi(®match, win, buf, 580 lnum, (colnr_T)0); 581 /* Abort searching on an error (e.g., out of stack). */ 582 if (called_emsg) 583 break; 584 if (nmatched > 0) 585 { 586 /* match may actually be in another line when using \zs */ 587 lnum += regmatch.startpos[0].lnum; 588 ptr = ml_get_buf(buf, lnum, FALSE); 589 startcol = regmatch.startpos[0].col; 590 endpos = regmatch.endpos[0]; 591 # ifdef FEAT_EVAL 592 submatch = first_submatch(®match); 593 # endif 594 595 /* 596 * Forward search in the first line: match should be after 597 * the start position. If not, continue at the end of the 598 * match (this is vi compatible) or on the next char. 599 */ 600 if (dir == FORWARD && at_first_line) 601 { 602 match_ok = TRUE; 603 /* 604 * When match lands on a NUL the cursor will be put 605 * one back afterwards, compare with that position, 606 * otherwise "/$" will get stuck on end of line. 607 */ 608 while ((options & SEARCH_END) 609 ? (nmatched == 1 610 && (int)endpos.col - 1 611 < (int)start_pos.col + extra_col) 612 : ((int)startcol - (ptr[startcol] == NUL) 613 < (int)start_pos.col + extra_col)) 614 { 615 /* 616 * If vi-compatible searching, continue at the end 617 * of the match, otherwise continue one position 618 * forward. 619 */ 620 if (vim_strchr(p_cpo, CPO_SEARCH) != NULL) 621 { 622 if (nmatched > 1) 623 { 624 /* end is in next line, thus no match in 625 * this line */ 626 match_ok = FALSE; 627 break; 628 } 629 matchcol = endpos.col; 630 /* for empty match: advance one char */ 631 if (matchcol == startcol 632 && ptr[matchcol] != NUL) 633 { 634 #ifdef FEAT_MBYTE 635 if (has_mbyte) 636 matchcol += 637 (*mb_ptr2len_check)(ptr + matchcol); 638 else 639 #endif 640 ++matchcol; 641 } 642 } 643 else 644 { 645 matchcol = startcol; 646 if (ptr[matchcol] != NUL) 647 { 648 #ifdef FEAT_MBYTE 649 if (has_mbyte) 650 matchcol += (*mb_ptr2len_check)(ptr 651 + matchcol); 652 else 653 #endif 654 ++matchcol; 655 } 656 } 657 if (ptr[matchcol] == NUL 658 || (nmatched = vim_regexec_multi(®match, 659 win, buf, lnum, matchcol)) == 0) 660 { 661 match_ok = FALSE; 662 break; 663 } 664 startcol = regmatch.startpos[0].col; 665 endpos = regmatch.endpos[0]; 666 # ifdef FEAT_EVAL 667 submatch = first_submatch(®match); 668 # endif 669 670 /* Need to get the line pointer again, a 671 * multi-line search may have made it invalid. */ 672 ptr = ml_get_buf(buf, lnum, FALSE); 673 } 674 if (!match_ok) 675 continue; 676 } 677 if (dir == BACKWARD) 678 { 679 /* 680 * Now, if there are multiple matches on this line, 681 * we have to get the last one. Or the last one before 682 * the cursor, if we're on that line. 683 * When putting the new cursor at the end, compare 684 * relative to the end of the match. 685 */ 686 match_ok = FALSE; 687 for (;;) 688 { 689 if (!at_first_line 690 || ((options & SEARCH_END) 691 ? (nmatched == 1 692 && (int)regmatch.endpos[0].col - 1 693 + extra_col 694 <= (int)start_pos.col) 695 : ((int)regmatch.startpos[0].col 696 + extra_col 697 <= (int)start_pos.col))) 698 { 699 /* Remember this position, we use it if it's 700 * the last match in the line. */ 701 match_ok = TRUE; 702 startcol = regmatch.startpos[0].col; 703 endpos = regmatch.endpos[0]; 704 # ifdef FEAT_EVAL 705 submatch = first_submatch(®match); 706 # endif 707 } 708 else 709 break; 710 711 /* 712 * We found a valid match, now check if there is 713 * another one after it. 714 * If vi-compatible searching, continue at the end 715 * of the match, otherwise continue one position 716 * forward. 717 */ 718 if (vim_strchr(p_cpo, CPO_SEARCH) != NULL) 719 { 720 if (nmatched > 1) 721 break; 722 matchcol = endpos.col; 723 /* for empty match: advance one char */ 724 if (matchcol == startcol 725 && ptr[matchcol] != NUL) 726 { 727 #ifdef FEAT_MBYTE 728 if (has_mbyte) 729 matchcol += 730 (*mb_ptr2len_check)(ptr + matchcol); 731 else 732 #endif 733 ++matchcol; 734 } 735 } 736 else 737 { 738 matchcol = startcol; 739 if (ptr[matchcol] != NUL) 740 { 741 #ifdef FEAT_MBYTE 742 if (has_mbyte) 743 matchcol += 744 (*mb_ptr2len_check)(ptr + matchcol); 745 else 746 #endif 747 ++matchcol; 748 } 749 } 750 if (ptr[matchcol] == NUL 751 || (nmatched = vim_regexec_multi(®match, 752 win, buf, lnum, matchcol)) == 0) 753 break; 754 755 /* Need to get the line pointer again, a 756 * multi-line search may have made it invalid. */ 757 ptr = ml_get_buf(buf, lnum, FALSE); 758 } 759 760 /* 761 * If there is only a match after the cursor, skip 762 * this match. 763 */ 764 if (!match_ok) 765 continue; 766 } 767 768 if (options & SEARCH_END && !(options & SEARCH_NOOF)) 769 { 770 pos->lnum = endpos.lnum + first_lnum; 771 pos->col = endpos.col - 1; 772 } 773 else 774 { 775 pos->lnum = lnum; 776 pos->col = startcol; 777 } 778 #ifdef FEAT_VIRTUALEDIT 779 pos->coladd = 0; 780 #endif 781 found = 1; 782 783 /* Set variables used for 'incsearch' highlighting. */ 784 search_match_lines = endpos.lnum - (lnum - first_lnum); 785 search_match_endcol = endpos.col; 786 break; 787 } 788 line_breakcheck(); /* stop if ctrl-C typed */ 789 if (got_int) 790 break; 791 792 #ifdef FEAT_SEARCH_EXTRA 793 /* Cancel searching if a character was typed. Used for 794 * 'incsearch'. Don't check too often, that would slowdown 795 * searching too much. */ 796 if ((options & SEARCH_PEEK) 797 && ((lnum - pos->lnum) & 0x3f) == 0 798 && char_avail()) 799 { 800 break_loop = TRUE; 801 break; 802 } 803 #endif 804 805 if (loop && lnum == start_pos.lnum) 806 break; /* if second loop, stop where started */ 807 } 808 at_first_line = FALSE; 809 810 /* 811 * Stop the search if wrapscan isn't set, after an interrupt, 812 * after a match and after looping twice. 813 */ 814 if (!p_ws || got_int || called_emsg || break_loop || found || loop) 815 break; 816 817 /* 818 * If 'wrapscan' is set we continue at the other end of the file. 819 * If 'shortmess' does not contain 's', we give a message. 820 * This message is also remembered in keep_msg for when the screen 821 * is redrawn. The keep_msg is cleared whenever another message is 822 * written. 823 */ 824 if (dir == BACKWARD) /* start second loop at the other end */ 825 { 826 lnum = buf->b_ml.ml_line_count; 827 if (!shortmess(SHM_SEARCH) && (options & SEARCH_MSG)) 828 give_warning((char_u *)_(top_bot_msg), TRUE); 829 } 830 else 831 { 832 lnum = 1; 833 if (!shortmess(SHM_SEARCH) && (options & SEARCH_MSG)) 834 give_warning((char_u *)_(bot_top_msg), TRUE); 835 } 836 } 837 if (got_int || called_emsg || break_loop) 838 break; 839 } 840 while (--count > 0 && found); /* stop after count matches or no match */ 841 842 vim_free(regmatch.regprog); 843 844 if (!found) /* did not find it */ 845 { 846 if (got_int) 847 EMSG(_(e_interr)); 848 else if ((options & SEARCH_MSG) == SEARCH_MSG) 849 { 850 if (p_ws) 851 EMSG2(_(e_patnotf2), mr_pattern); 852 else if (lnum == 0) 853 EMSG2(_("E384: search hit TOP without match for: %s"), 854 mr_pattern); 855 else 856 EMSG2(_("E385: search hit BOTTOM without match for: %s"), 857 mr_pattern); 858 } 859 return FAIL; 860 } 861 862 return submatch + 1; 863 } 864 865 #ifdef FEAT_EVAL 866 /* 867 * Return the number of the first subpat that matched. 868 */ 869 static int 870 first_submatch(rp) 871 regmmatch_T *rp; 872 { 873 int submatch; 874 875 for (submatch = 1; ; ++submatch) 876 { 877 if (rp->startpos[submatch].lnum >= 0) 878 break; 879 if (submatch == 9) 880 { 881 submatch = 0; 882 break; 883 } 884 } 885 return submatch; 886 } 887 #endif 888 889 /* 890 * Highest level string search function. 891 * Search for the 'count'th occurence of pattern 'pat' in direction 'dirc' 892 * If 'dirc' is 0: use previous dir. 893 * If 'pat' is NULL or empty : use previous string. 894 * If 'options & SEARCH_REV' : go in reverse of previous dir. 895 * If 'options & SEARCH_ECHO': echo the search command and handle options 896 * If 'options & SEARCH_MSG' : may give error message 897 * If 'options & SEARCH_OPT' : interpret optional flags 898 * If 'options & SEARCH_HIS' : put search pattern in history 899 * If 'options & SEARCH_NOOF': don't add offset to position 900 * If 'options & SEARCH_MARK': set previous context mark 901 * If 'options & SEARCH_KEEP': keep previous search pattern 902 * If 'options & SEARCH_START': accept match at curpos itself 903 * If 'options & SEARCH_PEEK': check for typed char, cancel search 904 * 905 * Careful: If spats[0].off.line == TRUE and spats[0].off.off == 0 this 906 * makes the movement linewise without moving the match position. 907 * 908 * return 0 for failure, 1 for found, 2 for found and line offset added 909 */ 910 int 911 do_search(oap, dirc, pat, count, options) 912 oparg_T *oap; /* can be NULL */ 913 int dirc; /* '/' or '?' */ 914 char_u *pat; 915 long count; 916 int options; 917 { 918 pos_T pos; /* position of the last match */ 919 char_u *searchstr; 920 struct soffset old_off; 921 int retval; /* Return value */ 922 char_u *p; 923 long c; 924 char_u *dircp; 925 char_u *strcopy = NULL; 926 char_u *ps; 927 928 /* 929 * A line offset is not remembered, this is vi compatible. 930 */ 931 if (spats[0].off.line && vim_strchr(p_cpo, CPO_LINEOFF) != NULL) 932 { 933 spats[0].off.line = FALSE; 934 spats[0].off.off = 0; 935 } 936 937 /* 938 * Save the values for when (options & SEARCH_KEEP) is used. 939 * (there is no "if ()" around this because gcc wants them initialized) 940 */ 941 old_off = spats[0].off; 942 943 pos = curwin->w_cursor; /* start searching at the cursor position */ 944 945 /* 946 * Find out the direction of the search. 947 */ 948 if (dirc == 0) 949 dirc = spats[0].off.dir; 950 else 951 spats[0].off.dir = dirc; 952 if (options & SEARCH_REV) 953 { 954 #ifdef WIN32 955 /* There is a bug in the Visual C++ 2.2 compiler which means that 956 * dirc always ends up being '/' */ 957 dirc = (dirc == '/') ? '?' : '/'; 958 #else 959 if (dirc == '/') 960 dirc = '?'; 961 else 962 dirc = '/'; 963 #endif 964 } 965 966 #ifdef FEAT_FOLDING 967 /* If the cursor is in a closed fold, don't find another match in the same 968 * fold. */ 969 if (dirc == '/') 970 { 971 if (hasFolding(pos.lnum, NULL, &pos.lnum)) 972 pos.col = MAXCOL - 2; /* avoid overflow when adding 1 */ 973 } 974 else 975 { 976 if (hasFolding(pos.lnum, &pos.lnum, NULL)) 977 pos.col = 0; 978 } 979 #endif 980 981 #ifdef FEAT_SEARCH_EXTRA 982 /* 983 * Turn 'hlsearch' highlighting back on. 984 */ 985 if (no_hlsearch && !(options & SEARCH_KEEP)) 986 { 987 redraw_all_later(NOT_VALID); 988 no_hlsearch = FALSE; 989 } 990 #endif 991 992 /* 993 * Repeat the search when pattern followed by ';', e.g. "/foo/;?bar". 994 */ 995 for (;;) 996 { 997 searchstr = pat; 998 dircp = NULL; 999 /* use previous pattern */ 1000 if (pat == NULL || *pat == NUL || *pat == dirc) 1001 { 1002 if (spats[RE_SEARCH].pat == NULL) /* no previous pattern */ 1003 { 1004 EMSG(_(e_noprevre)); 1005 retval = 0; 1006 goto end_do_search; 1007 } 1008 /* make search_regcomp() use spats[RE_SEARCH].pat */ 1009 searchstr = (char_u *)""; 1010 } 1011 1012 if (pat != NULL && *pat != NUL) /* look for (new) offset */ 1013 { 1014 /* 1015 * Find end of regular expression. 1016 * If there is a matching '/' or '?', toss it. 1017 */ 1018 ps = strcopy; 1019 p = skip_regexp(pat, dirc, (int)p_magic, &strcopy); 1020 if (strcopy != ps) 1021 { 1022 /* made a copy of "pat" to change "\?" to "?" */ 1023 searchcmdlen += STRLEN(pat) - STRLEN(strcopy); 1024 pat = strcopy; 1025 searchstr = strcopy; 1026 } 1027 if (*p == dirc) 1028 { 1029 dircp = p; /* remember where we put the NUL */ 1030 *p++ = NUL; 1031 } 1032 spats[0].off.line = FALSE; 1033 spats[0].off.end = FALSE; 1034 spats[0].off.off = 0; 1035 /* 1036 * Check for a line offset or a character offset. 1037 * For get_address (echo off) we don't check for a character 1038 * offset, because it is meaningless and the 's' could be a 1039 * substitute command. 1040 */ 1041 if (*p == '+' || *p == '-' || VIM_ISDIGIT(*p)) 1042 spats[0].off.line = TRUE; 1043 else if ((options & SEARCH_OPT) && 1044 (*p == 'e' || *p == 's' || *p == 'b')) 1045 { 1046 if (*p == 'e') /* end */ 1047 spats[0].off.end = SEARCH_END; 1048 ++p; 1049 } 1050 if (VIM_ISDIGIT(*p) || *p == '+' || *p == '-') /* got an offset */ 1051 { 1052 /* 'nr' or '+nr' or '-nr' */ 1053 if (VIM_ISDIGIT(*p) || VIM_ISDIGIT(*(p + 1))) 1054 spats[0].off.off = atol((char *)p); 1055 else if (*p == '-') /* single '-' */ 1056 spats[0].off.off = -1; 1057 else /* single '+' */ 1058 spats[0].off.off = 1; 1059 ++p; 1060 while (VIM_ISDIGIT(*p)) /* skip number */ 1061 ++p; 1062 } 1063 1064 /* compute length of search command for get_address() */ 1065 searchcmdlen += (int)(p - pat); 1066 1067 pat = p; /* put pat after search command */ 1068 } 1069 1070 if ((options & SEARCH_ECHO) && messaging() 1071 && !cmd_silent && msg_silent == 0) 1072 { 1073 char_u *msgbuf; 1074 char_u *trunc; 1075 1076 if (*searchstr == NUL) 1077 p = spats[last_idx].pat; 1078 else 1079 p = searchstr; 1080 msgbuf = alloc((unsigned)(STRLEN(p) + 40)); 1081 if (msgbuf != NULL) 1082 { 1083 msgbuf[0] = dirc; 1084 STRCPY(msgbuf + 1, p); 1085 if (spats[0].off.line || spats[0].off.end || spats[0].off.off) 1086 { 1087 p = msgbuf + STRLEN(msgbuf); 1088 *p++ = dirc; 1089 if (spats[0].off.end) 1090 *p++ = 'e'; 1091 else if (!spats[0].off.line) 1092 *p++ = 's'; 1093 if (spats[0].off.off > 0 || spats[0].off.line) 1094 *p++ = '+'; 1095 if (spats[0].off.off != 0 || spats[0].off.line) 1096 sprintf((char *)p, "%ld", spats[0].off.off); 1097 else 1098 *p = NUL; 1099 } 1100 1101 msg_start(); 1102 trunc = msg_strtrunc(msgbuf); 1103 1104 #ifdef FEAT_RIGHTLEFT 1105 /* The search pattern could be shown on the right in rightleft 1106 * mode, but the 'ruler' and 'showcmd' area use it too, thus 1107 * it would be blanked out again very soon. Show it on the 1108 * left, but do reverse the text. */ 1109 if (curwin->w_p_rl && *curwin->w_p_rlc == 's') 1110 { 1111 char_u *r; 1112 1113 r = reverse_text(trunc != NULL ? trunc : msgbuf); 1114 if (r != NULL) 1115 { 1116 vim_free(trunc); 1117 trunc = r; 1118 } 1119 } 1120 #endif 1121 if (trunc != NULL) 1122 { 1123 msg_outtrans(trunc); 1124 vim_free(trunc); 1125 } 1126 else 1127 msg_outtrans(msgbuf); 1128 msg_clr_eos(); 1129 msg_check(); 1130 vim_free(msgbuf); 1131 1132 gotocmdline(FALSE); 1133 out_flush(); 1134 msg_nowait = TRUE; /* don't wait for this message */ 1135 } 1136 } 1137 1138 /* 1139 * If there is a character offset, subtract it from the current 1140 * position, so we don't get stuck at "?pat?e+2" or "/pat/s-2". 1141 * Skip this if pos.col is near MAXCOL (closed fold). 1142 * This is not done for a line offset, because then we would not be vi 1143 * compatible. 1144 */ 1145 if (!spats[0].off.line && spats[0].off.off && pos.col < MAXCOL - 2) 1146 { 1147 if (spats[0].off.off > 0) 1148 { 1149 for (c = spats[0].off.off; c; --c) 1150 if (decl(&pos) == -1) 1151 break; 1152 if (c) /* at start of buffer */ 1153 { 1154 pos.lnum = 0; /* allow lnum == 0 here */ 1155 pos.col = MAXCOL; 1156 } 1157 } 1158 else 1159 { 1160 for (c = spats[0].off.off; c; ++c) 1161 if (incl(&pos) == -1) 1162 break; 1163 if (c) /* at end of buffer */ 1164 { 1165 pos.lnum = curbuf->b_ml.ml_line_count + 1; 1166 pos.col = 0; 1167 } 1168 } 1169 } 1170 1171 #ifdef FEAT_FKMAP /* when in Farsi mode, reverse the character flow */ 1172 if (p_altkeymap && curwin->w_p_rl) 1173 lrFswap(searchstr,0); 1174 #endif 1175 1176 c = searchit(curwin, curbuf, &pos, dirc == '/' ? FORWARD : BACKWARD, 1177 searchstr, count, spats[0].off.end + (options & 1178 (SEARCH_KEEP + SEARCH_PEEK + SEARCH_HIS 1179 + SEARCH_MSG + SEARCH_START 1180 + ((pat != NULL && *pat == ';') ? 0 : SEARCH_NOOF))), 1181 RE_LAST); 1182 1183 if (dircp != NULL) 1184 *dircp = dirc; /* restore second '/' or '?' for normal_cmd() */ 1185 if (c == FAIL) 1186 { 1187 retval = 0; 1188 goto end_do_search; 1189 } 1190 if (spats[0].off.end && oap != NULL) 1191 oap->inclusive = TRUE; /* 'e' includes last character */ 1192 1193 retval = 1; /* pattern found */ 1194 1195 /* 1196 * Add character and/or line offset 1197 */ 1198 if (!(options & SEARCH_NOOF) || *pat == ';') 1199 { 1200 if (spats[0].off.line) /* Add the offset to the line number. */ 1201 { 1202 c = pos.lnum + spats[0].off.off; 1203 if (c < 1) 1204 pos.lnum = 1; 1205 else if (c > curbuf->b_ml.ml_line_count) 1206 pos.lnum = curbuf->b_ml.ml_line_count; 1207 else 1208 pos.lnum = c; 1209 pos.col = 0; 1210 1211 retval = 2; /* pattern found, line offset added */ 1212 } 1213 else if (pos.col < MAXCOL - 2) /* just in case */ 1214 { 1215 /* to the right, check for end of file */ 1216 if (spats[0].off.off > 0) 1217 { 1218 for (c = spats[0].off.off; c; --c) 1219 if (incl(&pos) == -1) 1220 break; 1221 } 1222 /* to the left, check for start of file */ 1223 else 1224 { 1225 if ((c = pos.col + spats[0].off.off) >= 0) 1226 pos.col = c; 1227 else 1228 for (c = spats[0].off.off; c; ++c) 1229 if (decl(&pos) == -1) 1230 break; 1231 } 1232 } 1233 } 1234 1235 /* 1236 * The search command can be followed by a ';' to do another search. 1237 * For example: "/pat/;/foo/+3;?bar" 1238 * This is like doing another search command, except: 1239 * - The remembered direction '/' or '?' is from the first search. 1240 * - When an error happens the cursor isn't moved at all. 1241 * Don't do this when called by get_address() (it handles ';' itself). 1242 */ 1243 if (!(options & SEARCH_OPT) || pat == NULL || *pat != ';') 1244 break; 1245 1246 dirc = *++pat; 1247 if (dirc != '?' && dirc != '/') 1248 { 1249 retval = 0; 1250 EMSG(_("E386: Expected '?' or '/' after ';'")); 1251 goto end_do_search; 1252 } 1253 ++pat; 1254 } 1255 1256 if (options & SEARCH_MARK) 1257 setpcmark(); 1258 curwin->w_cursor = pos; 1259 curwin->w_set_curswant = TRUE; 1260 1261 end_do_search: 1262 if (options & SEARCH_KEEP) 1263 spats[0].off = old_off; 1264 vim_free(strcopy); 1265 1266 return retval; 1267 } 1268 1269 #if defined(FEAT_INS_EXPAND) || defined(PROTO) 1270 /* 1271 * search_for_exact_line(buf, pos, dir, pat) 1272 * 1273 * Search for a line starting with the given pattern (ignoring leading 1274 * white-space), starting from pos and going in direction dir. pos will 1275 * contain the position of the match found. Blank lines match only if 1276 * ADDING is set. if p_ic is set then the pattern must be in lowercase. 1277 * Return OK for success, or FAIL if no line found. 1278 */ 1279 int 1280 search_for_exact_line(buf, pos, dir, pat) 1281 buf_T *buf; 1282 pos_T *pos; 1283 int dir; 1284 char_u *pat; 1285 { 1286 linenr_T start = 0; 1287 char_u *ptr; 1288 char_u *p; 1289 1290 if (buf->b_ml.ml_line_count == 0) 1291 return FAIL; 1292 for (;;) 1293 { 1294 pos->lnum += dir; 1295 if (pos->lnum < 1) 1296 { 1297 if (p_ws) 1298 { 1299 pos->lnum = buf->b_ml.ml_line_count; 1300 if (!shortmess(SHM_SEARCH)) 1301 give_warning((char_u *)_(top_bot_msg), TRUE); 1302 } 1303 else 1304 { 1305 pos->lnum = 1; 1306 break; 1307 } 1308 } 1309 else if (pos->lnum > buf->b_ml.ml_line_count) 1310 { 1311 if (p_ws) 1312 { 1313 pos->lnum = 1; 1314 if (!shortmess(SHM_SEARCH)) 1315 give_warning((char_u *)_(bot_top_msg), TRUE); 1316 } 1317 else 1318 { 1319 pos->lnum = 1; 1320 break; 1321 } 1322 } 1323 if (pos->lnum == start) 1324 break; 1325 if (start == 0) 1326 start = pos->lnum; 1327 ptr = ml_get_buf(buf, pos->lnum, FALSE); 1328 p = skipwhite(ptr); 1329 pos->col = (colnr_T) (p - ptr); 1330 1331 /* when adding lines the matching line may be empty but it is not 1332 * ignored because we are interested in the next line -- Acevedo */ 1333 if ((continue_status & CONT_ADDING) && !(continue_status & CONT_SOL)) 1334 { 1335 if ((p_ic ? MB_STRICMP(p, pat) : STRCMP(p, pat)) == 0) 1336 return OK; 1337 } 1338 else if (*p != NUL) /* ignore empty lines */ 1339 { /* expanding lines or words */ 1340 if ((p_ic ? MB_STRNICMP(p, pat, completion_length) 1341 : STRNCMP(p, pat, completion_length)) == 0) 1342 return OK; 1343 } 1344 } 1345 return FAIL; 1346 } 1347 #endif /* FEAT_INS_EXPAND */ 1348 1349 /* 1350 * Character Searches 1351 */ 1352 1353 /* 1354 * Search for a character in a line. If "t_cmd" is FALSE, move to the 1355 * position of the character, otherwise move to just before the char. 1356 * Do this "cap->count1" times. 1357 * Return FAIL or OK. 1358 */ 1359 int 1360 searchc(cap, t_cmd) 1361 cmdarg_T *cap; 1362 int t_cmd; 1363 { 1364 int c = cap->nchar; /* char to search for */ 1365 int dir = cap->arg; /* TRUE for searching forward */ 1366 long count = cap->count1; /* repeat count */ 1367 static int lastc = NUL; /* last character searched for */ 1368 static int lastcdir; /* last direction of character search */ 1369 static int last_t_cmd; /* last search t_cmd */ 1370 int col; 1371 char_u *p; 1372 int len; 1373 #ifdef FEAT_MBYTE 1374 static char_u bytes[MB_MAXBYTES]; 1375 static int bytelen = 1; /* >1 for multi-byte char */ 1376 #endif 1377 1378 if (c != NUL) /* normal search: remember args for repeat */ 1379 { 1380 if (!KeyStuffed) /* don't remember when redoing */ 1381 { 1382 lastc = c; 1383 lastcdir = dir; 1384 last_t_cmd = t_cmd; 1385 #ifdef FEAT_MBYTE 1386 bytelen = (*mb_char2bytes)(c, bytes); 1387 if (cap->ncharC1 != 0) 1388 { 1389 bytelen += (*mb_char2bytes)(cap->ncharC1, bytes + bytelen); 1390 if (cap->ncharC2 != 0) 1391 bytelen += (*mb_char2bytes)(cap->ncharC2, bytes + bytelen); 1392 } 1393 #endif 1394 } 1395 } 1396 else /* repeat previous search */ 1397 { 1398 if (lastc == NUL) 1399 return FAIL; 1400 if (dir) /* repeat in opposite direction */ 1401 dir = -lastcdir; 1402 else 1403 dir = lastcdir; 1404 t_cmd = last_t_cmd; 1405 c = lastc; 1406 /* For multi-byte re-use last bytes[] and bytelen. */ 1407 } 1408 1409 p = ml_get_curline(); 1410 col = curwin->w_cursor.col; 1411 len = (int)STRLEN(p); 1412 1413 while (count--) 1414 { 1415 #ifdef FEAT_MBYTE 1416 if (has_mbyte) 1417 { 1418 for (;;) 1419 { 1420 if (dir > 0) 1421 { 1422 col += (*mb_ptr2len_check)(p + col); 1423 if (col >= len) 1424 return FAIL; 1425 } 1426 else 1427 { 1428 if (col == 0) 1429 return FAIL; 1430 col -= (*mb_head_off)(p, p + col - 1) + 1; 1431 } 1432 if (bytelen == 1) 1433 { 1434 if (p[col] == c) 1435 break; 1436 } 1437 else 1438 { 1439 if (vim_memcmp(p + col, bytes, bytelen) == 0) 1440 break; 1441 } 1442 } 1443 } 1444 else 1445 #endif 1446 { 1447 for (;;) 1448 { 1449 if ((col += dir) < 0 || col >= len) 1450 return FAIL; 1451 if (p[col] == c) 1452 break; 1453 } 1454 } 1455 } 1456 1457 if (t_cmd) 1458 { 1459 /* backup to before the character (possibly double-byte) */ 1460 col -= dir; 1461 #ifdef FEAT_MBYTE 1462 if (has_mbyte) 1463 { 1464 if (dir < 0) 1465 /* Landed on the search char which is bytelen long */ 1466 col += bytelen - 1; 1467 else 1468 /* To previous char, which may be multi-byte. */ 1469 col -= (*mb_head_off)(p, p + col); 1470 } 1471 #endif 1472 } 1473 curwin->w_cursor.col = col; 1474 1475 return OK; 1476 } 1477 1478 /* 1479 * "Other" Searches 1480 */ 1481 1482 /* 1483 * findmatch - find the matching paren or brace 1484 * 1485 * Improvement over vi: Braces inside quotes are ignored. 1486 */ 1487 pos_T * 1488 findmatch(oap, initc) 1489 oparg_T *oap; 1490 int initc; 1491 { 1492 return findmatchlimit(oap, initc, 0, 0); 1493 } 1494 1495 /* 1496 * Return TRUE if the character before "linep[col]" equals "ch". 1497 * Return FALSE if "col" is zero. 1498 * Update "*prevcol" to the column of the previous character, unless "prevcol" 1499 * is NULL. 1500 * Handles multibyte string correctly. 1501 */ 1502 static int 1503 check_prevcol(linep, col, ch, prevcol) 1504 char_u *linep; 1505 int col; 1506 int ch; 1507 int *prevcol; 1508 { 1509 --col; 1510 #ifdef FEAT_MBYTE 1511 if (col > 0 && has_mbyte) 1512 col -= (*mb_head_off)(linep, linep + col); 1513 #endif 1514 if (prevcol) 1515 *prevcol = col; 1516 return (col >= 0 && linep[col] == ch) ? TRUE : FALSE; 1517 } 1518 1519 /* 1520 * findmatchlimit -- find the matching paren or brace, if it exists within 1521 * maxtravel lines of here. A maxtravel of 0 means search until falling off 1522 * the edge of the file. 1523 * 1524 * "initc" is the character to find a match for. NUL means to find the 1525 * character at or after the cursor. 1526 * 1527 * flags: FM_BACKWARD search backwards (when initc is '/', '*' or '#') 1528 * FM_FORWARD search forwards (when initc is '/', '*' or '#') 1529 * FM_BLOCKSTOP stop at start/end of block ({ or } in column 0) 1530 * FM_SKIPCOMM skip comments (not implemented yet!) 1531 */ 1532 1533 pos_T * 1534 findmatchlimit(oap, initc, flags, maxtravel) 1535 oparg_T *oap; 1536 int initc; 1537 int flags; 1538 int maxtravel; 1539 { 1540 static pos_T pos; /* current search position */ 1541 int findc = 0; /* matching brace */ 1542 int c; 1543 int count = 0; /* cumulative number of braces */ 1544 int backwards = FALSE; /* init for gcc */ 1545 int inquote = FALSE; /* TRUE when inside quotes */ 1546 char_u *linep; /* pointer to current line */ 1547 char_u *ptr; 1548 int do_quotes; /* check for quotes in current line */ 1549 int at_start; /* do_quotes value at start position */ 1550 int hash_dir = 0; /* Direction searched for # things */ 1551 int comment_dir = 0; /* Direction searched for comments */ 1552 pos_T match_pos; /* Where last slash-star was found */ 1553 int start_in_quotes; /* start position is in quotes */ 1554 int traveled = 0; /* how far we've searched so far */ 1555 int ignore_cend = FALSE; /* ignore comment end */ 1556 int cpo_match; /* vi compatible matching */ 1557 int cpo_bsl; /* don't recognize backslashes */ 1558 int match_escaped = 0; /* search for escaped match */ 1559 int dir; /* Direction to search */ 1560 int comment_col = MAXCOL; /* start of / / comment */ 1561 1562 pos = curwin->w_cursor; 1563 linep = ml_get(pos.lnum); 1564 1565 cpo_match = (vim_strchr(p_cpo, CPO_MATCH) != NULL); 1566 cpo_bsl = (vim_strchr(p_cpo, CPO_MATCHBSL) != NULL); 1567 1568 /* Direction to search when initc is '/', '*' or '#' */ 1569 if (flags & FM_BACKWARD) 1570 dir = BACKWARD; 1571 else if (flags & FM_FORWARD) 1572 dir = FORWARD; 1573 else 1574 dir = 0; 1575 1576 /* 1577 * if initc given, look in the table for the matching character 1578 * '/' and '*' are special cases: look for start or end of comment. 1579 * When '/' is used, we ignore running backwards into an star-slash, for 1580 * "[*" command, we just want to find any comment. 1581 */ 1582 if (initc == '/' || initc == '*') 1583 { 1584 comment_dir = dir; 1585 if (initc == '/') 1586 ignore_cend = TRUE; 1587 backwards = (dir == FORWARD) ? FALSE : TRUE; 1588 initc = NUL; 1589 } 1590 else if (initc != '#' && initc != NUL) 1591 { 1592 /* 'matchpairs' is "x:y,x:y" */ 1593 for (ptr = curbuf->b_p_mps; *ptr; ptr += 2) 1594 { 1595 if (*ptr == initc) 1596 { 1597 findc = initc; 1598 initc = ptr[2]; 1599 backwards = TRUE; 1600 break; 1601 } 1602 ptr += 2; 1603 if (*ptr == initc) 1604 { 1605 findc = initc; 1606 initc = ptr[-2]; 1607 backwards = FALSE; 1608 break; 1609 } 1610 if (ptr[1] != ',') 1611 break; 1612 } 1613 if (!findc) /* invalid initc! */ 1614 return NULL; 1615 } 1616 /* 1617 * Either initc is '#', or no initc was given and we need to look under the 1618 * cursor. 1619 */ 1620 else 1621 { 1622 if (initc == '#') 1623 { 1624 hash_dir = dir; 1625 } 1626 else 1627 { 1628 /* 1629 * initc was not given, must look for something to match under 1630 * or near the cursor. 1631 * Only check for special things when 'cpo' doesn't have '%'. 1632 */ 1633 if (!cpo_match) 1634 { 1635 /* Are we before or at #if, #else etc.? */ 1636 ptr = skipwhite(linep); 1637 if (*ptr == '#' && pos.col <= (colnr_T)(ptr - linep)) 1638 { 1639 ptr = skipwhite(ptr + 1); 1640 if ( STRNCMP(ptr, "if", 2) == 0 1641 || STRNCMP(ptr, "endif", 5) == 0 1642 || STRNCMP(ptr, "el", 2) == 0) 1643 hash_dir = 1; 1644 } 1645 1646 /* Are we on a comment? */ 1647 else if (linep[pos.col] == '/') 1648 { 1649 if (linep[pos.col + 1] == '*') 1650 { 1651 comment_dir = FORWARD; 1652 backwards = FALSE; 1653 pos.col++; 1654 } 1655 else if (pos.col > 0 && linep[pos.col - 1] == '*') 1656 { 1657 comment_dir = BACKWARD; 1658 backwards = TRUE; 1659 pos.col--; 1660 } 1661 } 1662 else if (linep[pos.col] == '*') 1663 { 1664 if (linep[pos.col + 1] == '/') 1665 { 1666 comment_dir = BACKWARD; 1667 backwards = TRUE; 1668 } 1669 else if (pos.col > 0 && linep[pos.col - 1] == '/') 1670 { 1671 comment_dir = FORWARD; 1672 backwards = FALSE; 1673 } 1674 } 1675 } 1676 1677 /* 1678 * If we are not on a comment or the # at the start of a line, then 1679 * look for brace anywhere on this line after the cursor. 1680 */ 1681 if (!hash_dir && !comment_dir) 1682 { 1683 /* 1684 * Find the brace under or after the cursor. 1685 * If beyond the end of the line, use the last character in 1686 * the line. 1687 */ 1688 if (linep[pos.col] == NUL && pos.col) 1689 --pos.col; 1690 for (;;) 1691 { 1692 initc = linep[pos.col]; 1693 if (initc == NUL) 1694 break; 1695 1696 for (ptr = curbuf->b_p_mps; *ptr; ++ptr) 1697 { 1698 if (*ptr == initc) 1699 { 1700 findc = ptr[2]; 1701 backwards = FALSE; 1702 break; 1703 } 1704 ptr += 2; 1705 if (*ptr == initc) 1706 { 1707 findc = ptr[-2]; 1708 backwards = TRUE; 1709 break; 1710 } 1711 if (!*++ptr) 1712 break; 1713 } 1714 if (findc) 1715 break; 1716 #ifdef FEAT_MBYTE 1717 if (has_mbyte) 1718 pos.col += (*mb_ptr2len_check)(linep + pos.col); 1719 else 1720 #endif 1721 ++pos.col; 1722 } 1723 if (!findc) 1724 { 1725 /* no brace in the line, maybe use " #if" then */ 1726 if (!cpo_match && *skipwhite(linep) == '#') 1727 hash_dir = 1; 1728 else 1729 return NULL; 1730 } 1731 else if (!cpo_bsl) 1732 { 1733 int col, bslcnt = 0; 1734 1735 /* Set "match_escaped" if there are an odd number of 1736 * backslashes. */ 1737 for (col = pos.col; check_prevcol(linep, col, '\\', &col);) 1738 bslcnt++; 1739 match_escaped = (bslcnt & 1); 1740 } 1741 } 1742 } 1743 if (hash_dir) 1744 { 1745 /* 1746 * Look for matching #if, #else, #elif, or #endif 1747 */ 1748 if (oap != NULL) 1749 oap->motion_type = MLINE; /* Linewise for this case only */ 1750 if (initc != '#') 1751 { 1752 ptr = skipwhite(skipwhite(linep) + 1); 1753 if (STRNCMP(ptr, "if", 2) == 0 || STRNCMP(ptr, "el", 2) == 0) 1754 hash_dir = 1; 1755 else if (STRNCMP(ptr, "endif", 5) == 0) 1756 hash_dir = -1; 1757 else 1758 return NULL; 1759 } 1760 pos.col = 0; 1761 while (!got_int) 1762 { 1763 if (hash_dir > 0) 1764 { 1765 if (pos.lnum == curbuf->b_ml.ml_line_count) 1766 break; 1767 } 1768 else if (pos.lnum == 1) 1769 break; 1770 pos.lnum += hash_dir; 1771 linep = ml_get(pos.lnum); 1772 line_breakcheck(); /* check for CTRL-C typed */ 1773 ptr = skipwhite(linep); 1774 if (*ptr != '#') 1775 continue; 1776 pos.col = (colnr_T) (ptr - linep); 1777 ptr = skipwhite(ptr + 1); 1778 if (hash_dir > 0) 1779 { 1780 if (STRNCMP(ptr, "if", 2) == 0) 1781 count++; 1782 else if (STRNCMP(ptr, "el", 2) == 0) 1783 { 1784 if (count == 0) 1785 return &pos; 1786 } 1787 else if (STRNCMP(ptr, "endif", 5) == 0) 1788 { 1789 if (count == 0) 1790 return &pos; 1791 count--; 1792 } 1793 } 1794 else 1795 { 1796 if (STRNCMP(ptr, "if", 2) == 0) 1797 { 1798 if (count == 0) 1799 return &pos; 1800 count--; 1801 } 1802 else if (initc == '#' && STRNCMP(ptr, "el", 2) == 0) 1803 { 1804 if (count == 0) 1805 return &pos; 1806 } 1807 else if (STRNCMP(ptr, "endif", 5) == 0) 1808 count++; 1809 } 1810 } 1811 return NULL; 1812 } 1813 } 1814 1815 #ifdef FEAT_RIGHTLEFT 1816 /* This is just guessing: when 'rightleft' is set, search for a maching 1817 * paren/brace in the other direction. */ 1818 if (curwin->w_p_rl && vim_strchr((char_u *)"()[]{}<>", initc) != NULL) 1819 backwards = !backwards; 1820 #endif 1821 1822 do_quotes = -1; 1823 start_in_quotes = MAYBE; 1824 /* backward search: Check if this line contains a single-line comment */ 1825 if (backwards && comment_dir) 1826 comment_col = check_linecomment(linep); 1827 while (!got_int) 1828 { 1829 /* 1830 * Go to the next position, forward or backward. We could use 1831 * inc() and dec() here, but that is much slower 1832 */ 1833 if (backwards) 1834 { 1835 if (pos.col == 0) /* at start of line, go to prev. one */ 1836 { 1837 if (pos.lnum == 1) /* start of file */ 1838 break; 1839 --pos.lnum; 1840 1841 if (maxtravel && traveled++ > maxtravel) 1842 break; 1843 1844 linep = ml_get(pos.lnum); 1845 pos.col = (colnr_T)STRLEN(linep); /* pos.col on trailing NUL */ 1846 do_quotes = -1; 1847 line_breakcheck(); 1848 1849 /* Check if this line contains a single-line comment */ 1850 if (comment_dir) 1851 comment_col = check_linecomment(linep); 1852 } 1853 else 1854 { 1855 --pos.col; 1856 #ifdef FEAT_MBYTE 1857 if (has_mbyte) 1858 pos.col -= (*mb_head_off)(linep, linep + pos.col); 1859 #endif 1860 } 1861 } 1862 else /* forward search */ 1863 { 1864 if (linep[pos.col] == NUL) /* at end of line, go to next one */ 1865 { 1866 if (pos.lnum == curbuf->b_ml.ml_line_count) /* end of file */ 1867 break; 1868 ++pos.lnum; 1869 1870 if (maxtravel && traveled++ > maxtravel) 1871 break; 1872 1873 linep = ml_get(pos.lnum); 1874 pos.col = 0; 1875 do_quotes = -1; 1876 line_breakcheck(); 1877 } 1878 else 1879 { 1880 #ifdef FEAT_MBYTE 1881 if (has_mbyte) 1882 pos.col += (*mb_ptr2len_check)(linep + pos.col); 1883 else 1884 #endif 1885 ++pos.col; 1886 } 1887 } 1888 1889 /* 1890 * If FM_BLOCKSTOP given, stop at a '{' or '}' in column 0. 1891 */ 1892 if (pos.col == 0 && (flags & FM_BLOCKSTOP) && 1893 (linep[0] == '{' || linep[0] == '}')) 1894 { 1895 if (linep[0] == findc && count == 0) /* match! */ 1896 return &pos; 1897 break; /* out of scope */ 1898 } 1899 1900 if (comment_dir) 1901 { 1902 /* Note: comments do not nest, and we ignore quotes in them */ 1903 /* TODO: ignore comment brackets inside strings */ 1904 if (comment_dir == FORWARD) 1905 { 1906 if (linep[pos.col] == '*' && linep[pos.col + 1] == '/') 1907 { 1908 pos.col++; 1909 return &pos; 1910 } 1911 } 1912 else /* Searching backwards */ 1913 { 1914 /* 1915 * A comment may contain / * or / /, it may also start or end 1916 * with / * /. Ignore a / * after / /. 1917 */ 1918 if (pos.col == 0) 1919 continue; 1920 else if ( linep[pos.col - 1] == '/' 1921 && linep[pos.col] == '*' 1922 && (int)pos.col < comment_col) 1923 { 1924 count++; 1925 match_pos = pos; 1926 match_pos.col--; 1927 } 1928 else if (linep[pos.col - 1] == '*' && linep[pos.col] == '/') 1929 { 1930 if (count > 0) 1931 pos = match_pos; 1932 else if (pos.col > 1 && linep[pos.col - 2] == '/' 1933 && (int)pos.col <= comment_col) 1934 pos.col -= 2; 1935 else if (ignore_cend) 1936 continue; 1937 else 1938 return NULL; 1939 return &pos; 1940 } 1941 } 1942 continue; 1943 } 1944 1945 /* 1946 * If smart matching ('cpoptions' does not contain '%'), braces inside 1947 * of quotes are ignored, but only if there is an even number of 1948 * quotes in the line. 1949 */ 1950 if (cpo_match) 1951 do_quotes = 0; 1952 else if (do_quotes == -1) 1953 { 1954 /* 1955 * Count the number of quotes in the line, skipping \" and '"'. 1956 * Watch out for "\\". 1957 */ 1958 at_start = do_quotes; 1959 for (ptr = linep; *ptr; ++ptr) 1960 { 1961 if (ptr == linep + pos.col + backwards) 1962 at_start = (do_quotes & 1); 1963 if (*ptr == '"' 1964 && (ptr == linep || ptr[-1] != '\'' || ptr[1] != '\'')) 1965 ++do_quotes; 1966 if (*ptr == '\\' && ptr[1] != NUL) 1967 ++ptr; 1968 } 1969 do_quotes &= 1; /* result is 1 with even number of quotes */ 1970 1971 /* 1972 * If we find an uneven count, check current line and previous 1973 * one for a '\' at the end. 1974 */ 1975 if (!do_quotes) 1976 { 1977 inquote = FALSE; 1978 if (ptr[-1] == '\\') 1979 { 1980 do_quotes = 1; 1981 if (start_in_quotes == MAYBE) 1982 { 1983 /* Do we need to use at_start here? */ 1984 inquote = TRUE; 1985 start_in_quotes = TRUE; 1986 } 1987 else if (backwards) 1988 inquote = TRUE; 1989 } 1990 if (pos.lnum > 1) 1991 { 1992 ptr = ml_get(pos.lnum - 1); 1993 if (*ptr && *(ptr + STRLEN(ptr) - 1) == '\\') 1994 { 1995 do_quotes = 1; 1996 if (start_in_quotes == MAYBE) 1997 { 1998 inquote = at_start; 1999 if (inquote) 2000 start_in_quotes = TRUE; 2001 } 2002 else if (!backwards) 2003 inquote = TRUE; 2004 } 2005 } 2006 } 2007 } 2008 if (start_in_quotes == MAYBE) 2009 start_in_quotes = FALSE; 2010 2011 /* 2012 * If 'smartmatch' is set: 2013 * Things inside quotes are ignored by setting 'inquote'. If we 2014 * find a quote without a preceding '\' invert 'inquote'. At the 2015 * end of a line not ending in '\' we reset 'inquote'. 2016 * 2017 * In lines with an uneven number of quotes (without preceding '\') 2018 * we do not know which part to ignore. Therefore we only set 2019 * inquote if the number of quotes in a line is even, unless this 2020 * line or the previous one ends in a '\'. Complicated, isn't it? 2021 */ 2022 switch (c = linep[pos.col]) 2023 { 2024 case NUL: 2025 /* at end of line without trailing backslash, reset inquote */ 2026 if (pos.col == 0 || linep[pos.col - 1] != '\\') 2027 { 2028 inquote = FALSE; 2029 start_in_quotes = FALSE; 2030 } 2031 break; 2032 2033 case '"': 2034 /* a quote that is preceded with an odd number of backslashes is 2035 * ignored */ 2036 if (do_quotes) 2037 { 2038 int col; 2039 2040 for (col = pos.col - 1; col >= 0; --col) 2041 if (linep[col] != '\\') 2042 break; 2043 if ((((int)pos.col - 1 - col) & 1) == 0) 2044 { 2045 inquote = !inquote; 2046 start_in_quotes = FALSE; 2047 } 2048 } 2049 break; 2050 2051 /* 2052 * If smart matching ('cpoptions' does not contain '%'): 2053 * Skip things in single quotes: 'x' or '\x'. Be careful for single 2054 * single quotes, eg jon's. Things like '\233' or '\x3f' are not 2055 * skipped, there is never a brace in them. 2056 * Ignore this when finding matches for `'. 2057 */ 2058 case '\'': 2059 if (!cpo_match && initc != '\'' && findc != '\'') 2060 { 2061 if (backwards) 2062 { 2063 if (pos.col > 1) 2064 { 2065 if (linep[pos.col - 2] == '\'') 2066 { 2067 pos.col -= 2; 2068 break; 2069 } 2070 else if (linep[pos.col - 2] == '\\' && 2071 pos.col > 2 && linep[pos.col - 3] == '\'') 2072 { 2073 pos.col -= 3; 2074 break; 2075 } 2076 } 2077 } 2078 else if (linep[pos.col + 1]) /* forward search */ 2079 { 2080 if (linep[pos.col + 1] == '\\' && 2081 linep[pos.col + 2] && linep[pos.col + 3] == '\'') 2082 { 2083 pos.col += 3; 2084 break; 2085 } 2086 else if (linep[pos.col + 2] == '\'') 2087 { 2088 pos.col += 2; 2089 break; 2090 } 2091 } 2092 } 2093 /* FALLTHROUGH */ 2094 2095 default: 2096 #ifdef FEAT_LISP 2097 /* For Lisp skip over backslashed (), {} and []. */ 2098 if (curbuf->b_p_lisp 2099 && vim_strchr((char_u *)"(){}[]", c) != NULL 2100 && pos.col > 0 2101 && check_prevcol(linep, pos.col, '\\', NULL)) 2102 break; 2103 #endif 2104 2105 /* Check for match outside of quotes, and inside of 2106 * quotes when the start is also inside of quotes. */ 2107 if ((!inquote || start_in_quotes == TRUE) 2108 && (c == initc || c == findc)) 2109 { 2110 int col, bslcnt = 0; 2111 2112 if (!cpo_bsl) 2113 { 2114 for (col = pos.col; check_prevcol(linep, col, '\\', &col);) 2115 bslcnt++; 2116 } 2117 /* Only accept a match when 'M' is in 'cpo' or when ecaping is 2118 * what we expect. */ 2119 if (cpo_bsl || (bslcnt & 1) == match_escaped) 2120 { 2121 if (c == initc) 2122 count++; 2123 else 2124 { 2125 if (count == 0) 2126 return &pos; 2127 count--; 2128 } 2129 } 2130 } 2131 } 2132 } 2133 2134 if (comment_dir == BACKWARD && count > 0) 2135 { 2136 pos = match_pos; 2137 return &pos; 2138 } 2139 return (pos_T *)NULL; /* never found it */ 2140 } 2141 2142 /* 2143 * Check if line[] contains a / / comment. 2144 * Return MAXCOL if not, otherwise return the column. 2145 * TODO: skip strings. 2146 */ 2147 static int 2148 check_linecomment(line) 2149 char_u *line; 2150 { 2151 char_u *p; 2152 2153 p = line; 2154 while ((p = vim_strchr(p, '/')) != NULL) 2155 { 2156 if (p[1] == '/') 2157 break; 2158 ++p; 2159 } 2160 2161 if (p == NULL) 2162 return MAXCOL; 2163 return (int)(p - line); 2164 } 2165 2166 /* 2167 * Move cursor briefly to character matching the one under the cursor. 2168 * Used for Insert mode and "r" command. 2169 * Show the match only if it is visible on the screen. 2170 * If there isn't a match, then beep. 2171 */ 2172 void 2173 showmatch(c) 2174 int c; /* char to show match for */ 2175 { 2176 pos_T *lpos, save_cursor; 2177 pos_T mpos; 2178 colnr_T vcol; 2179 long save_so; 2180 long save_siso; 2181 #ifdef CURSOR_SHAPE 2182 int save_state; 2183 #endif 2184 colnr_T save_dollar_vcol; 2185 char_u *p; 2186 2187 /* 2188 * Only show match for chars in the 'matchpairs' option. 2189 */ 2190 /* 'matchpairs' is "x:y,x:y" */ 2191 for (p = curbuf->b_p_mps; *p != NUL; p += 2) 2192 { 2193 #ifdef FEAT_RIGHTLEFT 2194 if (*p == c && (curwin->w_p_rl ^ p_ri)) 2195 break; 2196 #endif 2197 p += 2; 2198 if (*p == c 2199 #ifdef FEAT_RIGHTLEFT 2200 && !(curwin->w_p_rl ^ p_ri) 2201 #endif 2202 ) 2203 break; 2204 if (p[1] != ',') 2205 return; 2206 } 2207 2208 if ((lpos = findmatch(NULL, NUL)) == NULL) /* no match, so beep */ 2209 vim_beep(); 2210 else if (lpos->lnum >= curwin->w_topline) 2211 { 2212 if (!curwin->w_p_wrap) 2213 getvcol(curwin, lpos, NULL, &vcol, NULL); 2214 if (curwin->w_p_wrap || (vcol >= curwin->w_leftcol 2215 && vcol < curwin->w_leftcol + W_WIDTH(curwin))) 2216 { 2217 mpos = *lpos; /* save the pos, update_screen() may change it */ 2218 save_cursor = curwin->w_cursor; 2219 save_so = p_so; 2220 save_siso = p_siso; 2221 /* Handle "$" in 'cpo': If the ')' is typed on top of the "$", 2222 * stop displaying the "$". */ 2223 if (dollar_vcol > 0 && dollar_vcol == curwin->w_virtcol) 2224 dollar_vcol = 0; 2225 ++curwin->w_virtcol; /* do display ')' just before "$" */ 2226 update_screen(VALID); /* show the new char first */ 2227 2228 save_dollar_vcol = dollar_vcol; 2229 #ifdef CURSOR_SHAPE 2230 save_state = State; 2231 State = SHOWMATCH; 2232 ui_cursor_shape(); /* may show different cursor shape */ 2233 #endif 2234 curwin->w_cursor = mpos; /* move to matching char */ 2235 p_so = 0; /* don't use 'scrolloff' here */ 2236 p_siso = 0; /* don't use 'sidescrolloff' here */ 2237 showruler(FALSE); 2238 setcursor(); 2239 cursor_on(); /* make sure that the cursor is shown */ 2240 out_flush(); 2241 #ifdef FEAT_GUI 2242 if (gui.in_use) 2243 { 2244 gui_update_cursor(TRUE, FALSE); 2245 gui_mch_flush(); 2246 } 2247 #endif 2248 /* Restore dollar_vcol(), because setcursor() may call curs_rows() 2249 * which resets it if the matching position is in a previous line 2250 * and has a higher column number. */ 2251 dollar_vcol = save_dollar_vcol; 2252 2253 /* 2254 * brief pause, unless 'm' is present in 'cpo' and a character is 2255 * available. 2256 */ 2257 if (vim_strchr(p_cpo, CPO_SHOWMATCH) != NULL) 2258 ui_delay(p_mat * 100L, TRUE); 2259 else if (!char_avail()) 2260 ui_delay(p_mat * 100L, FALSE); 2261 curwin->w_cursor = save_cursor; /* restore cursor position */ 2262 p_so = save_so; 2263 p_siso = save_siso; 2264 #ifdef CURSOR_SHAPE 2265 State = save_state; 2266 ui_cursor_shape(); /* may show different cursor shape */ 2267 #endif 2268 } 2269 } 2270 } 2271 2272 /* 2273 * findsent(dir, count) - Find the start of the next sentence in direction 2274 * 'dir' Sentences are supposed to end in ".", "!" or "?" followed by white 2275 * space or a line break. Also stop at an empty line. 2276 * Return OK if the next sentence was found. 2277 */ 2278 int 2279 findsent(dir, count) 2280 int dir; 2281 long count; 2282 { 2283 pos_T pos, tpos; 2284 int c; 2285 int (*func) __ARGS((pos_T *)); 2286 int startlnum; 2287 int noskip = FALSE; /* do not skip blanks */ 2288 int cpo_J; 2289 2290 pos = curwin->w_cursor; 2291 if (dir == FORWARD) 2292 func = incl; 2293 else 2294 func = decl; 2295 2296 while (count--) 2297 { 2298 /* 2299 * if on an empty line, skip upto a non-empty line 2300 */ 2301 if (gchar_pos(&pos) == NUL) 2302 { 2303 do 2304 if ((*func)(&pos) == -1) 2305 break; 2306 while (gchar_pos(&pos) == NUL); 2307 if (dir == FORWARD) 2308 goto found; 2309 } 2310 /* 2311 * if on the start of a paragraph or a section and searching forward, 2312 * go to the next line 2313 */ 2314 else if (dir == FORWARD && pos.col == 0 && 2315 startPS(pos.lnum, NUL, FALSE)) 2316 { 2317 if (pos.lnum == curbuf->b_ml.ml_line_count) 2318 return FAIL; 2319 ++pos.lnum; 2320 goto found; 2321 } 2322 else if (dir == BACKWARD) 2323 decl(&pos); 2324 2325 /* go back to the previous non-blank char */ 2326 while ((c = gchar_pos(&pos)) == ' ' || c == '\t' || 2327 (dir == BACKWARD && vim_strchr((char_u *)".!?)]\"'", c) != NULL)) 2328 { 2329 if (decl(&pos) == -1) 2330 break; 2331 /* when going forward: Stop in front of empty line */ 2332 if (lineempty(pos.lnum) && dir == FORWARD) 2333 { 2334 incl(&pos); 2335 goto found; 2336 } 2337 } 2338 2339 /* remember the line where the search started */ 2340 startlnum = pos.lnum; 2341 cpo_J = vim_strchr(p_cpo, CPO_ENDOFSENT) != NULL; 2342 2343 for (;;) /* find end of sentence */ 2344 { 2345 c = gchar_pos(&pos); 2346 if (c == NUL || (pos.col == 0 && startPS(pos.lnum, NUL, FALSE))) 2347 { 2348 if (dir == BACKWARD && pos.lnum != startlnum) 2349 ++pos.lnum; 2350 break; 2351 } 2352 if (c == '.' || c == '!' || c == '?') 2353 { 2354 tpos = pos; 2355 do 2356 if ((c = inc(&tpos)) == -1) 2357 break; 2358 while (vim_strchr((char_u *)")]\"'", c = gchar_pos(&tpos)) 2359 != NULL); 2360 if (c == -1 || (!cpo_J && (c == ' ' || c == '\t')) || c == NUL 2361 || (cpo_J && (c == ' ' && inc(&tpos) >= 0 2362 && gchar_pos(&tpos) == ' '))) 2363 { 2364 pos = tpos; 2365 if (gchar_pos(&pos) == NUL) /* skip NUL at EOL */ 2366 inc(&pos); 2367 break; 2368 } 2369 } 2370 if ((*func)(&pos) == -1) 2371 { 2372 if (count) 2373 return FAIL; 2374 noskip = TRUE; 2375 break; 2376 } 2377 } 2378 found: 2379 /* skip white space */ 2380 while (!noskip && ((c = gchar_pos(&pos)) == ' ' || c == '\t')) 2381 if (incl(&pos) == -1) 2382 break; 2383 } 2384 2385 setpcmark(); 2386 curwin->w_cursor = pos; 2387 return OK; 2388 } 2389 2390 /* 2391 * findpar(dir, count, what) - Find the next paragraph in direction 'dir' 2392 * Paragraphs are currently supposed to be separated by empty lines. 2393 * Return TRUE if the next paragraph was found. 2394 * If 'what' is '{' or '}' we go to the next section. 2395 * If 'both' is TRUE also stop at '}'. 2396 */ 2397 int 2398 findpar(oap, dir, count, what, both) 2399 oparg_T *oap; 2400 int dir; 2401 long count; 2402 int what; 2403 int both; 2404 { 2405 linenr_T curr; 2406 int did_skip; /* TRUE after separating lines have been skipped */ 2407 int first; /* TRUE on first line */ 2408 #ifdef FEAT_FOLDING 2409 linenr_T fold_first; /* first line of a closed fold */ 2410 linenr_T fold_last; /* last line of a closed fold */ 2411 int fold_skipped; /* TRUE if a closed fold was skipped this 2412 iteration */ 2413 #endif 2414 2415 curr = curwin->w_cursor.lnum; 2416 2417 while (count--) 2418 { 2419 did_skip = FALSE; 2420 for (first = TRUE; ; first = FALSE) 2421 { 2422 if (*ml_get(curr) != NUL) 2423 did_skip = TRUE; 2424 2425 #ifdef FEAT_FOLDING 2426 /* skip folded lines */ 2427 fold_skipped = FALSE; 2428 if (first && hasFolding(curr, &fold_first, &fold_last)) 2429 { 2430 curr = ((dir > 0) ? fold_last : fold_first) + dir; 2431 fold_skipped = TRUE; 2432 } 2433 #endif 2434 2435 if (!first && did_skip && startPS(curr, what, both)) 2436 break; 2437 2438 #ifdef FEAT_FOLDING 2439 if (fold_skipped) 2440 curr -= dir; 2441 #endif 2442 if ((curr += dir) < 1 || curr > curbuf->b_ml.ml_line_count) 2443 { 2444 if (count) 2445 return FALSE; 2446 curr -= dir; 2447 break; 2448 } 2449 } 2450 } 2451 setpcmark(); 2452 if (both && *ml_get(curr) == '}') /* include line with '}' */ 2453 ++curr; 2454 curwin->w_cursor.lnum = curr; 2455 if (curr == curbuf->b_ml.ml_line_count && what != '}') 2456 { 2457 if ((curwin->w_cursor.col = (colnr_T)STRLEN(ml_get(curr))) != 0) 2458 { 2459 --curwin->w_cursor.col; 2460 oap->inclusive = TRUE; 2461 } 2462 } 2463 else 2464 curwin->w_cursor.col = 0; 2465 return TRUE; 2466 } 2467 2468 /* 2469 * check if the string 's' is a nroff macro that is in option 'opt' 2470 */ 2471 static int 2472 inmacro(opt, s) 2473 char_u *opt; 2474 char_u *s; 2475 { 2476 char_u *macro; 2477 2478 for (macro = opt; macro[0]; ++macro) 2479 { 2480 /* Accept two characters in the option being equal to two characters 2481 * in the line. A space in the option matches with a space in the 2482 * line or the line having ended. */ 2483 if ( (macro[0] == s[0] 2484 || (macro[0] == ' ' 2485 && (s[0] == NUL || s[0] == ' '))) 2486 && (macro[1] == s[1] 2487 || ((macro[1] == NUL || macro[1] == ' ') 2488 && (s[0] == NUL || s[1] == NUL || s[1] == ' ')))) 2489 break; 2490 ++macro; 2491 if (macro[0] == NUL) 2492 break; 2493 } 2494 return (macro[0] != NUL); 2495 } 2496 2497 /* 2498 * startPS: return TRUE if line 'lnum' is the start of a section or paragraph. 2499 * If 'para' is '{' or '}' only check for sections. 2500 * If 'both' is TRUE also stop at '}' 2501 */ 2502 int 2503 startPS(lnum, para, both) 2504 linenr_T lnum; 2505 int para; 2506 int both; 2507 { 2508 char_u *s; 2509 2510 s = ml_get(lnum); 2511 if (*s == para || *s == '\f' || (both && *s == '}')) 2512 return TRUE; 2513 if (*s == '.' && (inmacro(p_sections, s + 1) || 2514 (!para && inmacro(p_para, s + 1)))) 2515 return TRUE; 2516 return FALSE; 2517 } 2518 2519 /* 2520 * The following routines do the word searches performed by the 'w', 'W', 2521 * 'b', 'B', 'e', and 'E' commands. 2522 */ 2523 2524 /* 2525 * To perform these searches, characters are placed into one of three 2526 * classes, and transitions between classes determine word boundaries. 2527 * 2528 * The classes are: 2529 * 2530 * 0 - white space 2531 * 1 - punctuation 2532 * 2 or higher - keyword characters (letters, digits and underscore) 2533 */ 2534 2535 static int cls_bigword; /* TRUE for "W", "B" or "E" */ 2536 2537 /* 2538 * cls() - returns the class of character at curwin->w_cursor 2539 * 2540 * If a 'W', 'B', or 'E' motion is being done (cls_bigword == TRUE), chars 2541 * from class 2 and higher are reported as class 1 since only white space 2542 * boundaries are of interest. 2543 */ 2544 static int 2545 cls() 2546 { 2547 int c; 2548 2549 c = gchar_cursor(); 2550 #ifdef FEAT_FKMAP /* when 'akm' (Farsi mode), take care of Farsi blank */ 2551 if (p_altkeymap && c == F_BLANK) 2552 return 0; 2553 #endif 2554 if (c == ' ' || c == '\t' || c == NUL) 2555 return 0; 2556 #ifdef FEAT_MBYTE 2557 if (enc_dbcs != 0 && c > 0xFF) 2558 { 2559 /* If cls_bigword, report multi-byte chars as class 1. */ 2560 if (enc_dbcs == DBCS_KOR && cls_bigword) 2561 return 1; 2562 2563 /* process code leading/trailing bytes */ 2564 return dbcs_class(((unsigned)c >> 8), (c & 0xFF)); 2565 } 2566 if (enc_utf8) 2567 { 2568 c = utf_class(c); 2569 if (c != 0 && cls_bigword) 2570 return 1; 2571 return c; 2572 } 2573 #endif 2574 2575 /* If cls_bigword is TRUE, report all non-blanks as class 1. */ 2576 if (cls_bigword) 2577 return 1; 2578 2579 if (vim_iswordc(c)) 2580 return 2; 2581 return 1; 2582 } 2583 2584 2585 /* 2586 * fwd_word(count, type, eol) - move forward one word 2587 * 2588 * Returns FAIL if the cursor was already at the end of the file. 2589 * If eol is TRUE, last word stops at end of line (for operators). 2590 */ 2591 int 2592 fwd_word(count, bigword, eol) 2593 long count; 2594 int bigword; /* "W", "E" or "B" */ 2595 int eol; 2596 { 2597 int sclass; /* starting class */ 2598 int i; 2599 int last_line; 2600 2601 #ifdef FEAT_VIRTUALEDIT 2602 curwin->w_cursor.coladd = 0; 2603 #endif 2604 cls_bigword = bigword; 2605 while (--count >= 0) 2606 { 2607 #ifdef FEAT_FOLDING 2608 /* When inside a range of folded lines, move to the last char of the 2609 * last line. */ 2610 if (hasFolding(curwin->w_cursor.lnum, NULL, &curwin->w_cursor.lnum)) 2611 coladvance((colnr_T)MAXCOL); 2612 #endif 2613 sclass = cls(); 2614 2615 /* 2616 * We always move at least one character, unless on the last 2617 * character in the buffer. 2618 */ 2619 last_line = (curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count); 2620 i = inc_cursor(); 2621 if (i == -1 || (i >= 1 && last_line)) /* started at last char in file */ 2622 return FAIL; 2623 if (i == 1 && eol && count == 0) /* started at last char in line */ 2624 return OK; 2625 2626 /* 2627 * Go one char past end of current word (if any) 2628 */ 2629 if (sclass != 0) 2630 while (cls() == sclass) 2631 { 2632 i = inc_cursor(); 2633 if (i == -1 || (i >= 1 && eol && count == 0)) 2634 return OK; 2635 } 2636 2637 /* 2638 * go to next non-white 2639 */ 2640 while (cls() == 0) 2641 { 2642 /* 2643 * We'll stop if we land on a blank line 2644 */ 2645 if (curwin->w_cursor.col == 0 && *ml_get_curline() == NUL) 2646 break; 2647 2648 i = inc_cursor(); 2649 if (i == -1 || (i >= 1 && eol && count == 0)) 2650 return OK; 2651 } 2652 } 2653 return OK; 2654 } 2655 2656 /* 2657 * bck_word() - move backward 'count' words 2658 * 2659 * If stop is TRUE and we are already on the start of a word, move one less. 2660 * 2661 * Returns FAIL if top of the file was reached. 2662 */ 2663 int 2664 bck_word(count, bigword, stop) 2665 long count; 2666 int bigword; 2667 int stop; 2668 { 2669 int sclass; /* starting class */ 2670 2671 #ifdef FEAT_VIRTUALEDIT 2672 curwin->w_cursor.coladd = 0; 2673 #endif 2674 cls_bigword = bigword; 2675 while (--count >= 0) 2676 { 2677 #ifdef FEAT_FOLDING 2678 /* When inside a range of folded lines, move to the first char of the 2679 * first line. */ 2680 if (hasFolding(curwin->w_cursor.lnum, &curwin->w_cursor.lnum, NULL)) 2681 curwin->w_cursor.col = 0; 2682 #endif 2683 sclass = cls(); 2684 if (dec_cursor() == -1) /* started at start of file */ 2685 return FAIL; 2686 2687 if (!stop || sclass == cls() || sclass == 0) 2688 { 2689 /* 2690 * Skip white space before the word. 2691 * Stop on an empty line. 2692 */ 2693 while (cls() == 0) 2694 { 2695 if (curwin->w_cursor.col == 0 2696 && lineempty(curwin->w_cursor.lnum)) 2697 goto finished; 2698 if (dec_cursor() == -1) /* hit start of file, stop here */ 2699 return OK; 2700 } 2701 2702 /* 2703 * Move backward to start of this word. 2704 */ 2705 if (skip_chars(cls(), BACKWARD)) 2706 return OK; 2707 } 2708 2709 inc_cursor(); /* overshot - forward one */ 2710 finished: 2711 stop = FALSE; 2712 } 2713 return OK; 2714 } 2715 2716 /* 2717 * end_word() - move to the end of the word 2718 * 2719 * There is an apparent bug in the 'e' motion of the real vi. At least on the 2720 * System V Release 3 version for the 80386. Unlike 'b' and 'w', the 'e' 2721 * motion crosses blank lines. When the real vi crosses a blank line in an 2722 * 'e' motion, the cursor is placed on the FIRST character of the next 2723 * non-blank line. The 'E' command, however, works correctly. Since this 2724 * appears to be a bug, I have not duplicated it here. 2725 * 2726 * Returns FAIL if end of the file was reached. 2727 * 2728 * If stop is TRUE and we are already on the end of a word, move one less. 2729 * If empty is TRUE stop on an empty line. 2730 */ 2731 int 2732 end_word(count, bigword, stop, empty) 2733 long count; 2734 int bigword; 2735 int stop; 2736 int empty; 2737 { 2738 int sclass; /* starting class */ 2739 2740 #ifdef FEAT_VIRTUALEDIT 2741 curwin->w_cursor.coladd = 0; 2742 #endif 2743 cls_bigword = bigword; 2744 while (--count >= 0) 2745 { 2746 #ifdef FEAT_FOLDING 2747 /* When inside a range of folded lines, move to the last char of the 2748 * last line. */ 2749 if (hasFolding(curwin->w_cursor.lnum, NULL, &curwin->w_cursor.lnum)) 2750 coladvance((colnr_T)MAXCOL); 2751 #endif 2752 sclass = cls(); 2753 if (inc_cursor() == -1) 2754 return FAIL; 2755 2756 /* 2757 * If we're in the middle of a word, we just have to move to the end 2758 * of it. 2759 */ 2760 if (cls() == sclass && sclass != 0) 2761 { 2762 /* 2763 * Move forward to end of the current word 2764 */ 2765 if (skip_chars(sclass, FORWARD)) 2766 return FAIL; 2767 } 2768 else if (!stop || sclass == 0) 2769 { 2770 /* 2771 * We were at the end of a word. Go to the end of the next word. 2772 * First skip white space, if 'empty' is TRUE, stop at empty line. 2773 */ 2774 while (cls() == 0) 2775 { 2776 if (empty && curwin->w_cursor.col == 0 2777 && lineempty(curwin->w_cursor.lnum)) 2778 goto finished; 2779 if (inc_cursor() == -1) /* hit end of file, stop here */ 2780 return FAIL; 2781 } 2782 2783 /* 2784 * Move forward to the end of this word. 2785 */ 2786 if (skip_chars(cls(), FORWARD)) 2787 return FAIL; 2788 } 2789 dec_cursor(); /* overshot - one char backward */ 2790 finished: 2791 stop = FALSE; /* we move only one word less */ 2792 } 2793 return OK; 2794 } 2795 2796 /* 2797 * Move back to the end of the word. 2798 * 2799 * Returns FAIL if start of the file was reached. 2800 */ 2801 int 2802 bckend_word(count, bigword, eol) 2803 long count; 2804 int bigword; /* TRUE for "B" */ 2805 int eol; /* TRUE: stop at end of line. */ 2806 { 2807 int sclass; /* starting class */ 2808 int i; 2809 2810 #ifdef FEAT_VIRTUALEDIT 2811 curwin->w_cursor.coladd = 0; 2812 #endif 2813 cls_bigword = bigword; 2814 while (--count >= 0) 2815 { 2816 sclass = cls(); 2817 if ((i = dec_cursor()) == -1) 2818 return FAIL; 2819 if (eol && i == 1) 2820 return OK; 2821 2822 /* 2823 * Move backward to before the start of this word. 2824 */ 2825 if (sclass != 0) 2826 { 2827 while (cls() == sclass) 2828 if ((i = dec_cursor()) == -1 || (eol && i == 1)) 2829 return OK; 2830 } 2831 2832 /* 2833 * Move backward to end of the previous word 2834 */ 2835 while (cls() == 0) 2836 { 2837 if (curwin->w_cursor.col == 0 && lineempty(curwin->w_cursor.lnum)) 2838 break; 2839 if ((i = dec_cursor()) == -1 || (eol && i == 1)) 2840 return OK; 2841 } 2842 } 2843 return OK; 2844 } 2845 2846 /* 2847 * Skip a row of characters of the same class. 2848 * Return TRUE when end-of-file reached, FALSE otherwise. 2849 */ 2850 static int 2851 skip_chars(cclass, dir) 2852 int cclass; 2853 int dir; 2854 { 2855 while (cls() == cclass) 2856 if ((dir == FORWARD ? inc_cursor() : dec_cursor()) == -1) 2857 return TRUE; 2858 return FALSE; 2859 } 2860 2861 #ifdef FEAT_TEXTOBJ 2862 /* 2863 * Go back to the start of the word or the start of white space 2864 */ 2865 static void 2866 back_in_line() 2867 { 2868 int sclass; /* starting class */ 2869 2870 sclass = cls(); 2871 for (;;) 2872 { 2873 if (curwin->w_cursor.col == 0) /* stop at start of line */ 2874 break; 2875 dec_cursor(); 2876 if (cls() != sclass) /* stop at start of word */ 2877 { 2878 inc_cursor(); 2879 break; 2880 } 2881 } 2882 } 2883 2884 static void 2885 find_first_blank(posp) 2886 pos_T *posp; 2887 { 2888 int c; 2889 2890 while (decl(posp) != -1) 2891 { 2892 c = gchar_pos(posp); 2893 if (!vim_iswhite(c)) 2894 { 2895 incl(posp); 2896 break; 2897 } 2898 } 2899 } 2900 2901 /* 2902 * Skip count/2 sentences and count/2 separating white spaces. 2903 */ 2904 static void 2905 findsent_forward(count, at_start_sent) 2906 long count; 2907 int at_start_sent; /* cursor is at start of sentence */ 2908 { 2909 while (count--) 2910 { 2911 findsent(FORWARD, 1L); 2912 if (at_start_sent) 2913 find_first_blank(&curwin->w_cursor); 2914 if (count == 0 || at_start_sent) 2915 decl(&curwin->w_cursor); 2916 at_start_sent = !at_start_sent; 2917 } 2918 } 2919 2920 /* 2921 * Find word under cursor, cursor at end. 2922 * Used while an operator is pending, and in Visual mode. 2923 */ 2924 int 2925 current_word(oap, count, include, bigword) 2926 oparg_T *oap; 2927 long count; 2928 int include; /* TRUE: include word and white space */ 2929 int bigword; /* FALSE == word, TRUE == WORD */ 2930 { 2931 pos_T start_pos; 2932 pos_T pos; 2933 int inclusive = TRUE; 2934 int include_white = FALSE; 2935 2936 cls_bigword = bigword; 2937 2938 #ifdef FEAT_VISUAL 2939 /* Correct cursor when 'selection' is exclusive */ 2940 if (VIsual_active && *p_sel == 'e' && lt(VIsual, curwin->w_cursor)) 2941 dec_cursor(); 2942 2943 /* 2944 * When Visual mode is not active, or when the VIsual area is only one 2945 * character, select the word and/or white space under the cursor. 2946 */ 2947 if (!VIsual_active || equalpos(curwin->w_cursor, VIsual)) 2948 #endif 2949 { 2950 /* 2951 * Go to start of current word or white space. 2952 */ 2953 back_in_line(); 2954 start_pos = curwin->w_cursor; 2955 2956 /* 2957 * If the start is on white space, and white space should be included 2958 * (" word"), or start is not on white space, and white space should 2959 * not be included ("word"), find end of word. 2960 */ 2961 if ((cls() == 0) == include) 2962 { 2963 if (end_word(1L, bigword, TRUE, TRUE) == FAIL) 2964 return FAIL; 2965 } 2966 else 2967 { 2968 /* 2969 * If the start is not on white space, and white space should be 2970 * included ("word "), or start is on white space and white 2971 * space should not be included (" "), find start of word. 2972 * If we end up in the first column of the next line (single char 2973 * word) back up to end of the line. 2974 */ 2975 fwd_word(1L, bigword, TRUE); 2976 if (curwin->w_cursor.col == 0) 2977 decl(&curwin->w_cursor); 2978 else 2979 oneleft(); 2980 2981 if (include) 2982 include_white = TRUE; 2983 } 2984 2985 #ifdef FEAT_VISUAL 2986 if (VIsual_active) 2987 { 2988 /* should do something when inclusive == FALSE ! */ 2989 VIsual = start_pos; 2990 redraw_curbuf_later(INVERTED); /* update the inversion */ 2991 } 2992 else 2993 #endif 2994 { 2995 oap->start = start_pos; 2996 oap->motion_type = MCHAR; 2997 } 2998 --count; 2999 } 3000 3001 /* 3002 * When count is still > 0, extend with more objects. 3003 */ 3004 while (count > 0) 3005 { 3006 inclusive = TRUE; 3007 #ifdef FEAT_VISUAL 3008 if (VIsual_active && lt(curwin->w_cursor, VIsual)) 3009 { 3010 /* 3011 * In Visual mode, with cursor at start: move cursor back. 3012 */ 3013 if (decl(&curwin->w_cursor) == -1) 3014 return FAIL; 3015 if (include != (cls() != 0)) 3016 { 3017 if (bck_word(1L, bigword, TRUE) == FAIL) 3018 return FAIL; 3019 } 3020 else 3021 { 3022 if (bckend_word(1L, bigword, TRUE) == FAIL) 3023 return FAIL; 3024 (void)incl(&curwin->w_cursor); 3025 } 3026 } 3027 else 3028 #endif 3029 { 3030 /* 3031 * Move cursor forward one word and/or white area. 3032 */ 3033 if (incl(&curwin->w_cursor) == -1) 3034 return FAIL; 3035 if (include != (cls() == 0)) 3036 { 3037 if (fwd_word(1L, bigword, TRUE) == FAIL) 3038 return FAIL; 3039 /* 3040 * If end is just past a new-line, we don't want to include 3041 * the first character on the line 3042 */ 3043 if (oneleft() == FAIL) /* put cursor on last char of white */ 3044 inclusive = FALSE; 3045 } 3046 else 3047 { 3048 if (end_word(1L, bigword, TRUE, TRUE) == FAIL) 3049 return FAIL; 3050 } 3051 } 3052 --count; 3053 } 3054 3055 if (include_white && cls() != 0) 3056 { 3057 /* 3058 * If we don't include white space at the end, move the start 3059 * to include some white space there. This makes "daw" work 3060 * better on the last word in a sentence (and "2daw" on last-but-one 3061 * word). But don't delete white space at start of line (indent). 3062 */ 3063 pos = curwin->w_cursor; /* save cursor position */ 3064 curwin->w_cursor = start_pos; 3065 if (oneleft() == OK) 3066 { 3067 back_in_line(); 3068 if (cls() == 0 && curwin->w_cursor.col > 0) 3069 { 3070 #ifdef FEAT_VISUAL 3071 if (VIsual_active) 3072 VIsual = curwin->w_cursor; 3073 else 3074 #endif 3075 oap->start = curwin->w_cursor; 3076 } 3077 } 3078 curwin->w_cursor = pos; /* put cursor back at end */ 3079 } 3080 3081 #ifdef FEAT_VISUAL 3082 if (VIsual_active) 3083 { 3084 if (*p_sel == 'e' && inclusive && ltoreq(VIsual, curwin->w_cursor)) 3085 inc_cursor(); 3086 if (VIsual_mode == 'V') 3087 { 3088 VIsual_mode = 'v'; 3089 redraw_cmdline = TRUE; /* show mode later */ 3090 } 3091 } 3092 else 3093 #endif 3094 oap->inclusive = inclusive; 3095 3096 return OK; 3097 } 3098 3099 /* 3100 * Find sentence(s) under the cursor, cursor at end. 3101 * When Visual active, extend it by one or more sentences. 3102 */ 3103 int 3104 current_sent(oap, count, include) 3105 oparg_T *oap; 3106 long count; 3107 int include; 3108 { 3109 pos_T start_pos; 3110 pos_T pos; 3111 int start_blank; 3112 int c; 3113 int at_start_sent; 3114 long ncount; 3115 3116 start_pos = curwin->w_cursor; 3117 pos = start_pos; 3118 findsent(FORWARD, 1L); /* Find start of next sentence. */ 3119 3120 #ifdef FEAT_VISUAL 3121 /* 3122 * When visual area is bigger than one character: Extend it. 3123 */ 3124 if (VIsual_active && !equalpos(start_pos, VIsual)) 3125 { 3126 extend: 3127 if (lt(start_pos, VIsual)) 3128 { 3129 /* 3130 * Cursor at start of Visual area. 3131 * Find out where we are: 3132 * - in the white space before a sentence 3133 * - in a sentence or just after it 3134 * - at the start of a sentence 3135 */ 3136 at_start_sent = TRUE; 3137 decl(&pos); 3138 while (lt(pos, curwin->w_cursor)) 3139 { 3140 c = gchar_pos(&pos); 3141 if (!vim_iswhite(c)) 3142 { 3143 at_start_sent = FALSE; 3144 break; 3145 } 3146 incl(&pos); 3147 } 3148 if (!at_start_sent) 3149 { 3150 findsent(BACKWARD, 1L); 3151 if (equalpos(curwin->w_cursor, start_pos)) 3152 at_start_sent = TRUE; /* exactly at start of sentence */ 3153 else 3154 /* inside a sentence, go to its end (start of next) */ 3155 findsent(FORWARD, 1L); 3156 } 3157 if (include) /* "as" gets twice as much as "is" */ 3158 count *= 2; 3159 while (count--) 3160 { 3161 if (at_start_sent) 3162 find_first_blank(&curwin->w_cursor); 3163 c = gchar_cursor(); 3164 if (!at_start_sent || (!include && !vim_iswhite(c))) 3165 findsent(BACKWARD, 1L); 3166 at_start_sent = !at_start_sent; 3167 } 3168 } 3169 else 3170 { 3171 /* 3172 * Cursor at end of Visual area. 3173 * Find out where we are: 3174 * - just before a sentence 3175 * - just before or in the white space before a sentence 3176 * - in a sentence 3177 */ 3178 incl(&pos); 3179 at_start_sent = TRUE; 3180 if (!equalpos(pos, curwin->w_cursor)) /* not just before a sentence */ 3181 { 3182 at_start_sent = FALSE; 3183 while (lt(pos, curwin->w_cursor)) 3184 { 3185 c = gchar_pos(&pos); 3186 if (!vim_iswhite(c)) 3187 { 3188 at_start_sent = TRUE; 3189 break; 3190 } 3191 incl(&pos); 3192 } 3193 if (at_start_sent) /* in the sentence */ 3194 findsent(BACKWARD, 1L); 3195 else /* in/before white before a sentence */ 3196 curwin->w_cursor = start_pos; 3197 } 3198 3199 if (include) /* "as" gets twice as much as "is" */ 3200 count *= 2; 3201 findsent_forward(count, at_start_sent); 3202 if (*p_sel == 'e') 3203 ++curwin->w_cursor.col; 3204 } 3205 return OK; 3206 } 3207 #endif 3208 3209 /* 3210 * If cursor started on blank, check if it is just before the start of the 3211 * next sentence. 3212 */ 3213 while (c = gchar_pos(&pos), vim_iswhite(c)) /* vim_iswhite() is a macro */ 3214 incl(&pos); 3215 if (equalpos(pos, curwin->w_cursor)) 3216 { 3217 start_blank = TRUE; 3218 find_first_blank(&start_pos); /* go back to first blank */ 3219 } 3220 else 3221 { 3222 start_blank = FALSE; 3223 findsent(BACKWARD, 1L); 3224 start_pos = curwin->w_cursor; 3225 } 3226 if (include) 3227 ncount = count * 2; 3228 else 3229 { 3230 ncount = count; 3231 if (start_blank) 3232 --ncount; 3233 } 3234 if (ncount) 3235 findsent_forward(ncount, TRUE); 3236 else 3237 decl(&curwin->w_cursor); 3238 3239 if (include) 3240 { 3241 /* 3242 * If the blank in front of the sentence is included, exclude the 3243 * blanks at the end of the sentence, go back to the first blank. 3244 * If there are no trailing blanks, try to include leading blanks. 3245 */ 3246 if (start_blank) 3247 { 3248 find_first_blank(&curwin->w_cursor); 3249 c = gchar_pos(&curwin->w_cursor); /* vim_iswhite() is a macro */ 3250 if (vim_iswhite(c)) 3251 decl(&curwin->w_cursor); 3252 } 3253 else if (c = gchar_cursor(), !vim_iswhite(c)) 3254 find_first_blank(&start_pos); 3255 } 3256 3257 #ifdef FEAT_VISUAL 3258 if (VIsual_active) 3259 { 3260 /* avoid getting stuck with "is" on a single space before a sent. */ 3261 if (equalpos(start_pos, curwin->w_cursor)) 3262 goto extend; 3263 if (*p_sel == 'e') 3264 ++curwin->w_cursor.col; 3265 VIsual = start_pos; 3266 VIsual_mode = 'v'; 3267 redraw_curbuf_later(INVERTED); /* update the inversion */ 3268 } 3269 else 3270 #endif 3271 { 3272 /* include a newline after the sentence, if there is one */ 3273 if (incl(&curwin->w_cursor) == -1) 3274 oap->inclusive = TRUE; 3275 else 3276 oap->inclusive = FALSE; 3277 oap->start = start_pos; 3278 oap->motion_type = MCHAR; 3279 } 3280 return OK; 3281 } 3282 3283 int 3284 current_block(oap, count, include, what, other) 3285 oparg_T *oap; 3286 long count; 3287 int include; /* TRUE == include white space */ 3288 int what; /* '(', '{', etc. */ 3289 int other; /* ')', '}', etc. */ 3290 { 3291 pos_T old_pos; 3292 pos_T *pos = NULL; 3293 pos_T start_pos; 3294 pos_T *end_pos; 3295 pos_T old_start, old_end; 3296 char_u *save_cpo; 3297 int sol = FALSE; /* { at start of line */ 3298 3299 old_pos = curwin->w_cursor; 3300 old_end = curwin->w_cursor; /* remember where we started */ 3301 old_start = old_end; 3302 3303 /* 3304 * If we start on '(', '{', ')', '}', etc., use the whole block inclusive. 3305 */ 3306 #ifdef FEAT_VISUAL 3307 if (!VIsual_active || equalpos(VIsual, curwin->w_cursor)) 3308 #endif 3309 { 3310 setpcmark(); 3311 if (what == '{') /* ignore indent */ 3312 while (inindent(1)) 3313 if (inc_cursor() != 0) 3314 break; 3315 if (gchar_cursor() == what) /* cursor on '(' or '{' */ 3316 ++curwin->w_cursor.col; 3317 } 3318 #ifdef FEAT_VISUAL 3319 else if (lt(VIsual, curwin->w_cursor)) 3320 { 3321 old_start = VIsual; 3322 curwin->w_cursor = VIsual; /* cursor at low end of Visual */ 3323 } 3324 else 3325 old_end = VIsual; 3326 #endif 3327 3328 /* 3329 * Search backwards for unclosed '(', '{', etc.. 3330 * Put this position in start_pos. 3331 * Ignory quotes here. 3332 */ 3333 save_cpo = p_cpo; 3334 p_cpo = (char_u *)"%"; 3335 while (count-- > 0) 3336 { 3337 if ((pos = findmatch(NULL, what)) == NULL) 3338 break; 3339 curwin->w_cursor = *pos; 3340 start_pos = *pos; /* the findmatch for end_pos will overwrite *pos */ 3341 } 3342 p_cpo = save_cpo; 3343 3344 /* 3345 * Search for matching ')', '}', etc. 3346 * Put this position in curwin->w_cursor. 3347 */ 3348 if (pos == NULL || (end_pos = findmatch(NULL, other)) == NULL) 3349 { 3350 curwin->w_cursor = old_pos; 3351 return FAIL; 3352 } 3353 curwin->w_cursor = *end_pos; 3354 3355 /* 3356 * Try to exclude the '(', '{', ')', '}', etc. when "include" is FALSE. 3357 * If the ending '}' is only preceded by indent, skip that indent. 3358 * But only if the resulting area is not smaller than what we started with. 3359 */ 3360 while (!include) 3361 { 3362 incl(&start_pos); 3363 sol = (curwin->w_cursor.col == 0); 3364 decl(&curwin->w_cursor); 3365 if (what == '{') 3366 while (inindent(1)) 3367 { 3368 sol = TRUE; 3369 if (decl(&curwin->w_cursor) != 0) 3370 break; 3371 } 3372 #ifdef FEAT_VISUAL 3373 /* 3374 * In Visual mode, when the resulting area is not bigger than what we 3375 * started with, extend it to the next block, and then exclude again. 3376 */ 3377 if (!lt(start_pos, old_start) && !lt(old_end, curwin->w_cursor) 3378 && VIsual_active) 3379 { 3380 curwin->w_cursor = old_start; 3381 decl(&curwin->w_cursor); 3382 if ((pos = findmatch(NULL, what)) == NULL) 3383 { 3384 curwin->w_cursor = old_pos; 3385 return FAIL; 3386 } 3387 start_pos = *pos; 3388 curwin->w_cursor = *pos; 3389 if ((end_pos = findmatch(NULL, other)) == NULL) 3390 { 3391 curwin->w_cursor = old_pos; 3392 return FAIL; 3393 } 3394 curwin->w_cursor = *end_pos; 3395 } 3396 else 3397 #endif 3398 break; 3399 } 3400 3401 #ifdef FEAT_VISUAL 3402 if (VIsual_active) 3403 { 3404 if (*p_sel == 'e') 3405 ++curwin->w_cursor.col; 3406 if (sol) 3407 inc(&curwin->w_cursor); /* include the line break */ 3408 VIsual = start_pos; 3409 VIsual_mode = 'v'; 3410 redraw_curbuf_later(INVERTED); /* update the inversion */ 3411 showmode(); 3412 } 3413 else 3414 #endif 3415 { 3416 oap->start = start_pos; 3417 oap->motion_type = MCHAR; 3418 if (sol) 3419 { 3420 incl(&curwin->w_cursor); 3421 oap->inclusive = FALSE; 3422 } 3423 else 3424 oap->inclusive = TRUE; 3425 } 3426 3427 return OK; 3428 } 3429 3430 int 3431 current_par(oap, count, include, type) 3432 oparg_T *oap; 3433 long count; 3434 int include; /* TRUE == include white space */ 3435 int type; /* 'p' for paragraph, 'S' for section */ 3436 { 3437 linenr_T start_lnum; 3438 linenr_T end_lnum; 3439 int white_in_front; 3440 int dir; 3441 int start_is_white; 3442 int prev_start_is_white; 3443 int retval = OK; 3444 int do_white = FALSE; 3445 int t; 3446 int i; 3447 3448 if (type == 'S') /* not implemented yet */ 3449 return FAIL; 3450 3451 start_lnum = curwin->w_cursor.lnum; 3452 3453 #ifdef FEAT_VISUAL 3454 /* 3455 * When visual area is more than one line: extend it. 3456 */ 3457 if (VIsual_active && start_lnum != VIsual.lnum) 3458 { 3459 extend: 3460 if (start_lnum < VIsual.lnum) 3461 dir = BACKWARD; 3462 else 3463 dir = FORWARD; 3464 for (i = count; --i >= 0; ) 3465 { 3466 if (start_lnum == 3467 (dir == BACKWARD ? 1 : curbuf->b_ml.ml_line_count)) 3468 { 3469 retval = FAIL; 3470 break; 3471 } 3472 3473 prev_start_is_white = -1; 3474 for (t = 0; t < 2; ++t) 3475 { 3476 start_lnum += dir; 3477 start_is_white = linewhite(start_lnum); 3478 if (prev_start_is_white == start_is_white) 3479 { 3480 start_lnum -= dir; 3481 break; 3482 } 3483 for (;;) 3484 { 3485 if (start_lnum == (dir == BACKWARD 3486 ? 1 : curbuf->b_ml.ml_line_count)) 3487 break; 3488 if (start_is_white != linewhite(start_lnum + dir) 3489 || (!start_is_white 3490 && startPS(start_lnum + (dir > 0 3491 ? 1 : 0), 0, 0))) 3492 break; 3493 start_lnum += dir; 3494 } 3495 if (!include) 3496 break; 3497 if (start_lnum == (dir == BACKWARD 3498 ? 1 : curbuf->b_ml.ml_line_count)) 3499 break; 3500 prev_start_is_white = start_is_white; 3501 } 3502 } 3503 curwin->w_cursor.lnum = start_lnum; 3504 curwin->w_cursor.col = 0; 3505 return retval; 3506 } 3507 #endif 3508 3509 /* 3510 * First move back to the start_lnum of the paragraph or white lines 3511 */ 3512 white_in_front = linewhite(start_lnum); 3513 while (start_lnum > 1) 3514 { 3515 if (white_in_front) /* stop at first white line */ 3516 { 3517 if (!linewhite(start_lnum - 1)) 3518 break; 3519 } 3520 else /* stop at first non-white line of start of paragraph */ 3521 { 3522 if (linewhite(start_lnum - 1) || startPS(start_lnum, 0, 0)) 3523 break; 3524 } 3525 --start_lnum; 3526 } 3527 3528 /* 3529 * Move past the end of any white lines. 3530 */ 3531 end_lnum = start_lnum; 3532 while (linewhite(end_lnum) && end_lnum < curbuf->b_ml.ml_line_count) 3533 ++end_lnum; 3534 3535 --end_lnum; 3536 i = count; 3537 if (!include && white_in_front) 3538 --i; 3539 while (i--) 3540 { 3541 if (end_lnum == curbuf->b_ml.ml_line_count) 3542 return FAIL; 3543 3544 if (!include) 3545 do_white = linewhite(end_lnum + 1); 3546 3547 if (include || !do_white) 3548 { 3549 ++end_lnum; 3550 /* 3551 * skip to end of paragraph 3552 */ 3553 while (end_lnum < curbuf->b_ml.ml_line_count 3554 && !linewhite(end_lnum + 1) 3555 && !startPS(end_lnum + 1, 0, 0)) 3556 ++end_lnum; 3557 } 3558 3559 if (i == 0 && white_in_front && include) 3560 break; 3561 3562 /* 3563 * skip to end of white lines after paragraph 3564 */ 3565 if (include || do_white) 3566 while (end_lnum < curbuf->b_ml.ml_line_count 3567 && linewhite(end_lnum + 1)) 3568 ++end_lnum; 3569 } 3570 3571 /* 3572 * If there are no empty lines at the end, try to find some empty lines at 3573 * the start (unless that has been done already). 3574 */ 3575 if (!white_in_front && !linewhite(end_lnum) && include) 3576 while (start_lnum > 1 && linewhite(start_lnum - 1)) 3577 --start_lnum; 3578 3579 #ifdef FEAT_VISUAL 3580 if (VIsual_active) 3581 { 3582 /* Problem: when doing "Vipipip" nothing happens in a single white 3583 * line, we get stuck there. Trap this here. */ 3584 if (VIsual_mode == 'V' && start_lnum == curwin->w_cursor.lnum) 3585 goto extend; 3586 VIsual.lnum = start_lnum; 3587 VIsual_mode = 'V'; 3588 redraw_curbuf_later(INVERTED); /* update the inversion */ 3589 showmode(); 3590 } 3591 else 3592 #endif 3593 { 3594 oap->start.lnum = start_lnum; 3595 oap->start.col = 0; 3596 oap->motion_type = MLINE; 3597 } 3598 curwin->w_cursor.lnum = end_lnum; 3599 curwin->w_cursor.col = 0; 3600 3601 return OK; 3602 } 3603 #endif 3604 3605 #if defined(FEAT_LISP) || defined(FEAT_CINDENT) || defined(FEAT_TEXTOBJ) \ 3606 || defined(PROTO) 3607 /* 3608 * return TRUE if line 'lnum' is empty or has white chars only. 3609 */ 3610 int 3611 linewhite(lnum) 3612 linenr_T lnum; 3613 { 3614 char_u *p; 3615 3616 p = skipwhite(ml_get(lnum)); 3617 return (*p == NUL); 3618 } 3619 #endif 3620 3621 #if defined(FEAT_FIND_ID) || defined(PROTO) 3622 /* 3623 * Find identifiers or defines in included files. 3624 * if p_ic && (continue_status & CONT_SOL) then ptr must be in lowercase. 3625 */ 3626 /*ARGSUSED*/ 3627 void 3628 find_pattern_in_path(ptr, dir, len, whole, skip_comments, 3629 type, count, action, start_lnum, end_lnum) 3630 char_u *ptr; /* pointer to search pattern */ 3631 int dir; /* direction of expansion */ 3632 int len; /* length of search pattern */ 3633 int whole; /* match whole words only */ 3634 int skip_comments; /* don't match inside comments */ 3635 int type; /* Type of search; are we looking for a type? 3636 a macro? */ 3637 long count; 3638 int action; /* What to do when we find it */ 3639 linenr_T start_lnum; /* first line to start searching */ 3640 linenr_T end_lnum; /* last line for searching */ 3641 { 3642 SearchedFile *files; /* Stack of included files */ 3643 SearchedFile *bigger; /* When we need more space */ 3644 int max_path_depth = 50; 3645 long match_count = 1; 3646 3647 char_u *pat; 3648 char_u *new_fname; 3649 char_u *curr_fname = curbuf->b_fname; 3650 char_u *prev_fname = NULL; 3651 linenr_T lnum; 3652 int depth; 3653 int depth_displayed; /* For type==CHECK_PATH */ 3654 int old_files; 3655 int already_searched; 3656 char_u *file_line; 3657 char_u *line; 3658 char_u *p; 3659 char_u save_char; 3660 int define_matched; 3661 regmatch_T regmatch; 3662 regmatch_T incl_regmatch; 3663 regmatch_T def_regmatch; 3664 int matched = FALSE; 3665 int did_show = FALSE; 3666 int found = FALSE; 3667 int i; 3668 char_u *already = NULL; 3669 char_u *startp = NULL; 3670 #ifdef RISCOS 3671 int previous_munging = __riscosify_control; 3672 #endif 3673 #if defined(FEAT_WINDOWS) && defined(FEAT_QUICKFIX) 3674 win_T *curwin_save = NULL; 3675 #endif 3676 3677 regmatch.regprog = NULL; 3678 incl_regmatch.regprog = NULL; 3679 def_regmatch.regprog = NULL; 3680 3681 file_line = alloc(LSIZE); 3682 if (file_line == NULL) 3683 return; 3684 3685 #ifdef RISCOS 3686 /* UnixLib knows best how to munge c file names - turn munging back on. */ 3687 __riscosify_control = __RISCOSIFY_LONG_TRUNCATE; 3688 #endif 3689 3690 if (type != CHECK_PATH && type != FIND_DEFINE 3691 #ifdef FEAT_INS_EXPAND 3692 /* when CONT_SOL is set compare "ptr" with the beginning of the line 3693 * is faster than quote_meta/regcomp/regexec "ptr" -- Acevedo */ 3694 && !(continue_status & CONT_SOL) 3695 #endif 3696 ) 3697 { 3698 pat = alloc(len + 5); 3699 if (pat == NULL) 3700 goto fpip_end; 3701 sprintf((char *)pat, whole ? "\\<%.*s\\>" : "%.*s", len, ptr); 3702 /* ignore case according to p_ic, p_scs and pat */ 3703 regmatch.rm_ic = ignorecase(pat); 3704 regmatch.regprog = vim_regcomp(pat, p_magic ? RE_MAGIC : 0); 3705 vim_free(pat); 3706 if (regmatch.regprog == NULL) 3707 goto fpip_end; 3708 } 3709 if (*curbuf->b_p_inc != NUL || *p_inc != NUL) 3710 { 3711 incl_regmatch.regprog = vim_regcomp(*curbuf->b_p_inc == NUL 3712 ? p_inc : curbuf->b_p_inc, p_magic ? RE_MAGIC : 0); 3713 if (incl_regmatch.regprog == NULL) 3714 goto fpip_end; 3715 incl_regmatch.rm_ic = FALSE; /* don't ignore case in incl. pat. */ 3716 } 3717 if (type == FIND_DEFINE && (*curbuf->b_p_def != NUL || *p_def != NUL)) 3718 { 3719 def_regmatch.regprog = vim_regcomp(*curbuf->b_p_def == NUL 3720 ? p_def : curbuf->b_p_def, p_magic ? RE_MAGIC : 0); 3721 if (def_regmatch.regprog == NULL) 3722 goto fpip_end; 3723 def_regmatch.rm_ic = FALSE; /* don't ignore case in define pat. */ 3724 } 3725 files = (SearchedFile *)lalloc_clear((long_u) 3726 (max_path_depth * sizeof(SearchedFile)), TRUE); 3727 if (files == NULL) 3728 goto fpip_end; 3729 old_files = max_path_depth; 3730 depth = depth_displayed = -1; 3731 3732 lnum = start_lnum; 3733 if (end_lnum > curbuf->b_ml.ml_line_count) 3734 end_lnum = curbuf->b_ml.ml_line_count; 3735 if (lnum > end_lnum) /* do at least one line */ 3736 lnum = end_lnum; 3737 line = ml_get(lnum); 3738 3739 for (;;) 3740 { 3741 if (incl_regmatch.regprog != NULL 3742 && vim_regexec(&incl_regmatch, line, (colnr_T)0)) 3743 { 3744 new_fname = file_name_in_line(incl_regmatch.endp[0], 3745 0, FNAME_EXP|FNAME_INCL|FNAME_REL, 1L, 3746 curr_fname == curbuf->b_fname 3747 ? curbuf->b_ffname : curr_fname); 3748 already_searched = FALSE; 3749 if (new_fname != NULL) 3750 { 3751 /* Check whether we have already searched in this file */ 3752 for (i = 0;; i++) 3753 { 3754 if (i == depth + 1) 3755 i = old_files; 3756 if (i == max_path_depth) 3757 break; 3758 if (fullpathcmp(new_fname, files[i].name, TRUE) & FPC_SAME) 3759 { 3760 if (type != CHECK_PATH && 3761 action == ACTION_SHOW_ALL && files[i].matched) 3762 { 3763 msg_putchar('\n'); /* cursor below last one */ 3764 if (!got_int) /* don't display if 'q' 3765 typed at "--more--" 3766 mesage */ 3767 { 3768 msg_home_replace_hl(new_fname); 3769 MSG_PUTS(_(" (includes previously listed match)")); 3770 prev_fname = NULL; 3771 } 3772 } 3773 vim_free(new_fname); 3774 new_fname = NULL; 3775 already_searched = TRUE; 3776 break; 3777 } 3778 } 3779 } 3780 3781 if (type == CHECK_PATH && (action == ACTION_SHOW_ALL 3782 || (new_fname == NULL && !already_searched))) 3783 { 3784 if (did_show) 3785 msg_putchar('\n'); /* cursor below last one */ 3786 else 3787 { 3788 gotocmdline(TRUE); /* cursor at status line */ 3789 MSG_PUTS_TITLE(_("--- Included files ")); 3790 if (action != ACTION_SHOW_ALL) 3791 MSG_PUTS_TITLE(_("not found ")); 3792 MSG_PUTS_TITLE(_("in path ---\n")); 3793 } 3794 did_show = TRUE; 3795 while (depth_displayed < depth && !got_int) 3796 { 3797 ++depth_displayed; 3798 for (i = 0; i < depth_displayed; i++) 3799 MSG_PUTS(" "); 3800 msg_home_replace(files[depth_displayed].name); 3801 MSG_PUTS(" -->\n"); 3802 } 3803 if (!got_int) /* don't display if 'q' typed 3804 for "--more--" message */ 3805 { 3806 for (i = 0; i <= depth_displayed; i++) 3807 MSG_PUTS(" "); 3808 if (new_fname != NULL) 3809 { 3810 /* using "new_fname" is more reliable, e.g., when 3811 * 'includeexpr' is set. */ 3812 msg_outtrans_attr(new_fname, hl_attr(HLF_D)); 3813 } 3814 else 3815 { 3816 /* 3817 * Isolate the file name. 3818 * Include the surrounding "" or <> if present. 3819 */ 3820 for (p = incl_regmatch.endp[0]; !vim_isfilec(*p); p++) 3821 ; 3822 for (i = 0; vim_isfilec(p[i]); i++) 3823 ; 3824 if (i == 0) 3825 { 3826 /* Nothing found, use the rest of the line. */ 3827 p = incl_regmatch.endp[0]; 3828 i = STRLEN(p); 3829 } 3830 else 3831 { 3832 if (p[-1] == '"' || p[-1] == '<') 3833 { 3834 --p; 3835 ++i; 3836 } 3837 if (p[i] == '"' || p[i] == '>') 3838 ++i; 3839 } 3840 save_char = p[i]; 3841 p[i] = NUL; 3842 msg_outtrans_attr(p, hl_attr(HLF_D)); 3843 p[i] = save_char; 3844 } 3845 3846 if (new_fname == NULL && action == ACTION_SHOW_ALL) 3847 { 3848 if (already_searched) 3849 MSG_PUTS(_(" (Already listed)")); 3850 else 3851 MSG_PUTS(_(" NOT FOUND")); 3852 } 3853 } 3854 out_flush(); /* output each line directly */ 3855 } 3856 3857 if (new_fname != NULL) 3858 { 3859 /* Push the new file onto the file stack */ 3860 if (depth + 1 == old_files) 3861 { 3862 bigger = (SearchedFile *)lalloc((long_u)( 3863 max_path_depth * 2 * sizeof(SearchedFile)), TRUE); 3864 if (bigger != NULL) 3865 { 3866 for (i = 0; i <= depth; i++) 3867 bigger[i] = files[i]; 3868 for (i = depth + 1; i < old_files + max_path_depth; i++) 3869 { 3870 bigger[i].fp = NULL; 3871 bigger[i].name = NULL; 3872 bigger[i].lnum = 0; 3873 bigger[i].matched = FALSE; 3874 } 3875 for (i = old_files; i < max_path_depth; i++) 3876 bigger[i + max_path_depth] = files[i]; 3877 old_files += max_path_depth; 3878 max_path_depth *= 2; 3879 vim_free(files); 3880 files = bigger; 3881 } 3882 } 3883 if ((files[depth + 1].fp = mch_fopen((char *)new_fname, "r")) 3884 == NULL) 3885 vim_free(new_fname); 3886 else 3887 { 3888 if (++depth == old_files) 3889 { 3890 /* 3891 * lalloc() for 'bigger' must have failed above. We 3892 * will forget one of our already visited files now. 3893 */ 3894 vim_free(files[old_files].name); 3895 ++old_files; 3896 } 3897 files[depth].name = curr_fname = new_fname; 3898 files[depth].lnum = 0; 3899 files[depth].matched = FALSE; 3900 #ifdef FEAT_INS_EXPAND 3901 if (action == ACTION_EXPAND) 3902 { 3903 sprintf((char*)IObuff, _("Scanning included file: %s"), 3904 (char *)new_fname); 3905 msg_trunc_attr(IObuff, TRUE, hl_attr(HLF_R)); 3906 } 3907 #endif 3908 } 3909 } 3910 } 3911 else 3912 { 3913 /* 3914 * Check if the line is a define (type == FIND_DEFINE) 3915 */ 3916 p = line; 3917 search_line: 3918 define_matched = FALSE; 3919 if (def_regmatch.regprog != NULL 3920 && vim_regexec(&def_regmatch, line, (colnr_T)0)) 3921 { 3922 /* 3923 * Pattern must be first identifier after 'define', so skip 3924 * to that position before checking for match of pattern. Also 3925 * don't let it match beyond the end of this identifier. 3926 */ 3927 p = def_regmatch.endp[0]; 3928 while (*p && !vim_iswordc(*p)) 3929 p++; 3930 define_matched = TRUE; 3931 } 3932 3933 /* 3934 * Look for a match. Don't do this if we are looking for a 3935 * define and this line didn't match define_prog above. 3936 */ 3937 if (def_regmatch.regprog == NULL || define_matched) 3938 { 3939 if (define_matched 3940 #ifdef FEAT_INS_EXPAND 3941 || (continue_status & CONT_SOL) 3942 #endif 3943 ) 3944 { 3945 /* compare the first "len" chars from "ptr" */ 3946 startp = skipwhite(p); 3947 if (p_ic) 3948 matched = !MB_STRNICMP(startp, ptr, len); 3949 else 3950 matched = !STRNCMP(startp, ptr, len); 3951 if (matched && define_matched && whole 3952 && vim_iswordc(startp[len])) 3953 matched = FALSE; 3954 } 3955 else if (regmatch.regprog != NULL 3956 && vim_regexec(®match, line, (colnr_T)(p - line))) 3957 { 3958 matched = TRUE; 3959 startp = regmatch.startp[0]; 3960 /* 3961 * Check if the line is not a comment line (unless we are 3962 * looking for a define). A line starting with "# define" 3963 * is not considered to be a comment line. 3964 */ 3965 if (!define_matched && skip_comments) 3966 { 3967 #ifdef FEAT_COMMENTS 3968 if ((*line != '#' || 3969 STRNCMP(skipwhite(line + 1), "define", 6) != 0) 3970 && get_leader_len(line, NULL, FALSE)) 3971 matched = FALSE; 3972 3973 /* 3974 * Also check for a "/ *" or "/ /" before the match. 3975 * Skips lines like "int backwards; / * normal index 3976 * * /" when looking for "normal". 3977 * Note: Doesn't skip "/ *" in comments. 3978 */ 3979 p = skipwhite(line); 3980 if (matched 3981 || (p[0] == '/' && p[1] == '*') || p[0] == '*') 3982 #endif 3983 for (p = line; *p && p < startp; ++p) 3984 { 3985 if (matched 3986 && p[0] == '/' 3987 && (p[1] == '*' || p[1] == '/')) 3988 { 3989 matched = FALSE; 3990 /* After "//" all text is comment */ 3991 if (p[1] == '/') 3992 break; 3993 ++p; 3994 } 3995 else if (!matched && p[0] == '*' && p[1] == '/') 3996 { 3997 /* Can find match after "* /". */ 3998 matched = TRUE; 3999 ++p; 4000 } 4001 } 4002 } 4003 } 4004 } 4005 } 4006 if (matched) 4007 { 4008 #ifdef FEAT_INS_EXPAND 4009 if (action == ACTION_EXPAND) 4010 { 4011 int reuse = 0; 4012 int add_r; 4013 char_u *aux; 4014 4015 if (depth == -1 && lnum == curwin->w_cursor.lnum) 4016 break; 4017 found = TRUE; 4018 aux = p = startp; 4019 if (continue_status & CONT_ADDING) 4020 { 4021 p += completion_length; 4022 if (vim_iswordp(p)) 4023 goto exit_matched; 4024 p = find_word_start(p); 4025 } 4026 p = find_word_end(p); 4027 i = (int)(p - aux); 4028 4029 if ((continue_status & CONT_ADDING) && i == completion_length) 4030 { 4031 /* get the next line */ 4032 /* IOSIZE > completion_length, so the STRNCPY works */ 4033 STRNCPY(IObuff, aux, i); 4034 if (!( depth < 0 4035 && lnum < end_lnum 4036 && (line = ml_get(++lnum)) != NULL) 4037 && !( depth >= 0 4038 && !vim_fgets(line = file_line, 4039 LSIZE, files[depth].fp))) 4040 goto exit_matched; 4041 4042 /* we read a line, set "already" to check this "line" later 4043 * if depth >= 0 we'll increase files[depth].lnum far 4044 * bellow -- Acevedo */ 4045 already = aux = p = skipwhite(line); 4046 p = find_word_start(p); 4047 p = find_word_end(p); 4048 if (p > aux) 4049 { 4050 if (*aux != ')' && IObuff[i-1] != TAB) 4051 { 4052 if (IObuff[i-1] != ' ') 4053 IObuff[i++] = ' '; 4054 /* IObuf =~ "\(\k\|\i\).* ", thus i >= 2*/ 4055 if (p_js 4056 && (IObuff[i-2] == '.' 4057 || (vim_strchr(p_cpo, CPO_JOINSP) == NULL 4058 && (IObuff[i-2] == '?' 4059 || IObuff[i-2] == '!')))) 4060 IObuff[i++] = ' '; 4061 } 4062 /* copy as much as posible of the new word */ 4063 if (p - aux >= IOSIZE - i) 4064 p = aux + IOSIZE - i - 1; 4065 STRNCPY(IObuff + i, aux, p - aux); 4066 i += (int)(p - aux); 4067 reuse |= CONT_S_IPOS; 4068 } 4069 IObuff[i] = NUL; 4070 aux = IObuff; 4071 4072 if (i == completion_length) 4073 goto exit_matched; 4074 } 4075 4076 add_r = ins_compl_add_infercase(aux, i, 4077 curr_fname == curbuf->b_fname ? NULL : curr_fname, 4078 dir, reuse); 4079 if (add_r == OK) 4080 /* if dir was BACKWARD then honor it just once */ 4081 dir = FORWARD; 4082 else if (add_r == RET_ERROR) 4083 break; 4084 } 4085 else 4086 #endif 4087 if (action == ACTION_SHOW_ALL) 4088 { 4089 found = TRUE; 4090 if (!did_show) 4091 gotocmdline(TRUE); /* cursor at status line */ 4092 if (curr_fname != prev_fname) 4093 { 4094 if (did_show) 4095 msg_putchar('\n'); /* cursor below last one */ 4096 if (!got_int) /* don't display if 'q' typed 4097 at "--more--" mesage */ 4098 msg_home_replace_hl(curr_fname); 4099 prev_fname = curr_fname; 4100 } 4101 did_show = TRUE; 4102 if (!got_int) 4103 show_pat_in_path(line, type, TRUE, action, 4104 (depth == -1) ? NULL : files[depth].fp, 4105 (depth == -1) ? &lnum : &files[depth].lnum, 4106 match_count++); 4107 4108 /* Set matched flag for this file and all the ones that 4109 * include it */ 4110 for (i = 0; i <= depth; ++i) 4111 files[i].matched = TRUE; 4112 } 4113 else if (--count <= 0) 4114 { 4115 found = TRUE; 4116 if (depth == -1 && lnum == curwin->w_cursor.lnum 4117 #if defined(FEAT_WINDOWS) && defined(FEAT_QUICKFIX) 4118 && g_do_tagpreview == 0 4119 #endif 4120 ) 4121 EMSG(_("E387: Match is on current line")); 4122 else if (action == ACTION_SHOW) 4123 { 4124 show_pat_in_path(line, type, did_show, action, 4125 (depth == -1) ? NULL : files[depth].fp, 4126 (depth == -1) ? &lnum : &files[depth].lnum, 1L); 4127 did_show = TRUE; 4128 } 4129 else 4130 { 4131 #ifdef FEAT_GUI 4132 need_mouse_correct = TRUE; 4133 #endif 4134 #if defined(FEAT_WINDOWS) && defined(FEAT_QUICKFIX) 4135 /* ":psearch" uses the preview window */ 4136 if (g_do_tagpreview != 0) 4137 { 4138 curwin_save = curwin; 4139 prepare_tagpreview(); 4140 } 4141 #endif 4142 if (action == ACTION_SPLIT) 4143 { 4144 #ifdef FEAT_WINDOWS 4145 if (win_split(0, 0) == FAIL) 4146 #endif 4147 break; 4148 #ifdef FEAT_SCROLLBIND 4149 curwin->w_p_scb = FALSE; 4150 #endif 4151 } 4152 if (depth == -1) 4153 { 4154 /* match in current file */ 4155 #if defined(FEAT_WINDOWS) && defined(FEAT_QUICKFIX) 4156 if (g_do_tagpreview != 0) 4157 { 4158 if (getfile(0, curwin_save->w_buffer->b_fname, 4159 NULL, TRUE, lnum, FALSE) > 0) 4160 break; /* failed to jump to file */ 4161 } 4162 else 4163 #endif 4164 setpcmark(); 4165 curwin->w_cursor.lnum = lnum; 4166 } 4167 else 4168 { 4169 if (getfile(0, files[depth].name, NULL, TRUE, 4170 files[depth].lnum, FALSE) > 0) 4171 break; /* failed to jump to file */ 4172 /* autocommands may have changed the lnum, we don't 4173 * want that here */ 4174 curwin->w_cursor.lnum = files[depth].lnum; 4175 } 4176 } 4177 if (action != ACTION_SHOW) 4178 { 4179 curwin->w_cursor.col = (colnr_T) (startp - line); 4180 curwin->w_set_curswant = TRUE; 4181 } 4182 4183 #if defined(FEAT_WINDOWS) && defined(FEAT_QUICKFIX) 4184 if (g_do_tagpreview != 0 4185 && curwin != curwin_save && win_valid(curwin_save)) 4186 { 4187 /* Return cursor to where we were */ 4188 validate_cursor(); 4189 redraw_later(VALID); 4190 win_enter(curwin_save, TRUE); 4191 } 4192 #endif 4193 break; 4194 } 4195 #ifdef FEAT_INS_EXPAND 4196 exit_matched: 4197 #endif 4198 matched = FALSE; 4199 /* look for other matches in the rest of the line if we 4200 * are not at the end of it already */ 4201 if (def_regmatch.regprog == NULL 4202 #ifdef FEAT_INS_EXPAND 4203 && action == ACTION_EXPAND 4204 && !(continue_status & CONT_SOL) 4205 #endif 4206 && *(p = startp + 1)) 4207 goto search_line; 4208 } 4209 line_breakcheck(); 4210 #ifdef FEAT_INS_EXPAND 4211 if (action == ACTION_EXPAND) 4212 ins_compl_check_keys(); 4213 if (got_int || completion_interrupted) 4214 #else 4215 if (got_int) 4216 #endif 4217 break; 4218 4219 /* 4220 * Read the next line. When reading an included file and encountering 4221 * end-of-file, close the file and continue in the file that included 4222 * it. 4223 */ 4224 while (depth >= 0 && !already 4225 && vim_fgets(line = file_line, LSIZE, files[depth].fp)) 4226 { 4227 fclose(files[depth].fp); 4228 --old_files; 4229 files[old_files].name = files[depth].name; 4230 files[old_files].matched = files[depth].matched; 4231 --depth; 4232 curr_fname = (depth == -1) ? curbuf->b_fname 4233 : files[depth].name; 4234 if (depth < depth_displayed) 4235 depth_displayed = depth; 4236 } 4237 if (depth >= 0) /* we could read the line */ 4238 files[depth].lnum++; 4239 else if (!already) 4240 { 4241 if (++lnum > end_lnum) 4242 break; 4243 line = ml_get(lnum); 4244 } 4245 already = NULL; 4246 } 4247 /* End of big for (;;) loop. */ 4248 4249 /* Close any files that are still open. */ 4250 for (i = 0; i <= depth; i++) 4251 { 4252 fclose(files[i].fp); 4253 vim_free(files[i].name); 4254 } 4255 for (i = old_files; i < max_path_depth; i++) 4256 vim_free(files[i].name); 4257 vim_free(files); 4258 4259 if (type == CHECK_PATH) 4260 { 4261 if (!did_show) 4262 { 4263 if (action != ACTION_SHOW_ALL) 4264 MSG(_("All included files were found")); 4265 else 4266 MSG(_("No included files")); 4267 } 4268 } 4269 else if (!found 4270 #ifdef FEAT_INS_EXPAND 4271 && action != ACTION_EXPAND 4272 #endif 4273 ) 4274 { 4275 #ifdef FEAT_INS_EXPAND 4276 if (got_int || completion_interrupted) 4277 #else 4278 if (got_int) 4279 #endif 4280 EMSG(_(e_interr)); 4281 else if (type == FIND_DEFINE) 4282 EMSG(_("E388: Couldn't find definition")); 4283 else 4284 EMSG(_("E389: Couldn't find pattern")); 4285 } 4286 if (action == ACTION_SHOW || action == ACTION_SHOW_ALL) 4287 msg_end(); 4288 4289 fpip_end: 4290 vim_free(file_line); 4291 vim_free(regmatch.regprog); 4292 vim_free(incl_regmatch.regprog); 4293 vim_free(def_regmatch.regprog); 4294 4295 #ifdef RISCOS 4296 /* Restore previous file munging state. */ 4297 __riscosify_control = previous_munging; 4298 #endif 4299 } 4300 4301 static void 4302 show_pat_in_path(line, type, did_show, action, fp, lnum, count) 4303 char_u *line; 4304 int type; 4305 int did_show; 4306 int action; 4307 FILE *fp; 4308 linenr_T *lnum; 4309 long count; 4310 { 4311 char_u *p; 4312 4313 if (did_show) 4314 msg_putchar('\n'); /* cursor below last one */ 4315 else 4316 gotocmdline(TRUE); /* cursor at status line */ 4317 if (got_int) /* 'q' typed at "--more--" message */ 4318 return; 4319 for (;;) 4320 { 4321 p = line + STRLEN(line) - 1; 4322 if (fp != NULL) 4323 { 4324 /* We used fgets(), so get rid of newline at end */ 4325 if (p >= line && *p == '\n') 4326 --p; 4327 if (p >= line && *p == '\r') 4328 --p; 4329 *(p + 1) = NUL; 4330 } 4331 if (action == ACTION_SHOW_ALL) 4332 { 4333 sprintf((char *)IObuff, "%3ld: ", count); /* show match nr */ 4334 msg_puts(IObuff); 4335 sprintf((char *)IObuff, "%4ld", *lnum); /* show line nr */ 4336 /* Highlight line numbers */ 4337 msg_puts_attr(IObuff, hl_attr(HLF_N)); 4338 MSG_PUTS(" "); 4339 } 4340 msg_prt_line(line); 4341 out_flush(); /* show one line at a time */ 4342 4343 /* Definition continues until line that doesn't end with '\' */ 4344 if (got_int || type != FIND_DEFINE || p < line || *p != '\\') 4345 break; 4346 4347 if (fp != NULL) 4348 { 4349 if (vim_fgets(line, LSIZE, fp)) /* end of file */ 4350 break; 4351 ++*lnum; 4352 } 4353 else 4354 { 4355 if (++*lnum > curbuf->b_ml.ml_line_count) 4356 break; 4357 line = ml_get(*lnum); 4358 } 4359 msg_putchar('\n'); 4360 } 4361 } 4362 #endif 4363 4364 #ifdef FEAT_VIMINFO 4365 int 4366 read_viminfo_search_pattern(virp, force) 4367 vir_T *virp; 4368 int force; 4369 { 4370 char_u *lp; 4371 int idx = -1; 4372 int magic = FALSE; 4373 int no_scs = FALSE; 4374 int off_line = FALSE; 4375 int off_end = FALSE; 4376 long off = 0; 4377 int setlast = FALSE; 4378 #ifdef FEAT_SEARCH_EXTRA 4379 static int hlsearch_on = FALSE; 4380 #endif 4381 char_u *val; 4382 4383 /* 4384 * Old line types: 4385 * "/pat", "&pat": search/subst. pat 4386 * "~/pat", "~&pat": last used search/subst. pat 4387 * New line types: 4388 * "~h", "~H": hlsearch highlighting off/on 4389 * "~<magic><smartcase><line><end><off><last><which>pat" 4390 * <magic>: 'm' off, 'M' on 4391 * <smartcase>: 's' off, 'S' on 4392 * <line>: 'L' line offset, 'l' char offset 4393 * <end>: 'E' from end, 'e' from start 4394 * <off>: decimal, offset 4395 * <last>: '~' last used pattern 4396 * <which>: '/' search pat, '&' subst. pat 4397 */ 4398 lp = virp->vir_line; 4399 if (lp[0] == '~' && (lp[1] == 'm' || lp[1] == 'M')) /* new line type */ 4400 { 4401 if (lp[1] == 'M') /* magic on */ 4402 magic = TRUE; 4403 if (lp[2] == 's') 4404 no_scs = TRUE; 4405 if (lp[3] == 'L') 4406 off_line = TRUE; 4407 if (lp[4] == 'E') 4408 off_end = SEARCH_END; 4409 lp += 5; 4410 off = getdigits(&lp); 4411 } 4412 if (lp[0] == '~') /* use this pattern for last-used pattern */ 4413 { 4414 setlast = TRUE; 4415 lp++; 4416 } 4417 if (lp[0] == '/') 4418 idx = RE_SEARCH; 4419 else if (lp[0] == '&') 4420 idx = RE_SUBST; 4421 #ifdef FEAT_SEARCH_EXTRA 4422 else if (lp[0] == 'h') /* ~h: 'hlsearch' highlighting off */ 4423 hlsearch_on = FALSE; 4424 else if (lp[0] == 'H') /* ~H: 'hlsearch' highlighting on */ 4425 hlsearch_on = TRUE; 4426 #endif 4427 if (idx >= 0) 4428 { 4429 if (force || spats[idx].pat == NULL) 4430 { 4431 val = viminfo_readstring(virp, (int)(lp - virp->vir_line + 1), 4432 TRUE); 4433 if (val != NULL) 4434 { 4435 set_last_search_pat(val, idx, magic, setlast); 4436 vim_free(val); 4437 spats[idx].no_scs = no_scs; 4438 spats[idx].off.line = off_line; 4439 spats[idx].off.end = off_end; 4440 spats[idx].off.off = off; 4441 #ifdef FEAT_SEARCH_EXTRA 4442 if (setlast) 4443 no_hlsearch = !hlsearch_on; 4444 #endif 4445 } 4446 } 4447 } 4448 return viminfo_readline(virp); 4449 } 4450 4451 void 4452 write_viminfo_search_pattern(fp) 4453 FILE *fp; 4454 { 4455 if (get_viminfo_parameter('/') != 0) 4456 { 4457 #ifdef FEAT_SEARCH_EXTRA 4458 fprintf(fp, "\n# hlsearch on (H) or off (h):\n~%c", 4459 (no_hlsearch || find_viminfo_parameter('h') != NULL) ? 'h' : 'H'); 4460 #endif 4461 wvsp_one(fp, RE_SEARCH, "", '/'); 4462 wvsp_one(fp, RE_SUBST, "Substitute ", '&'); 4463 } 4464 } 4465 4466 static void 4467 wvsp_one(fp, idx, s, sc) 4468 FILE *fp; /* file to write to */ 4469 int idx; /* spats[] index */ 4470 char *s; /* search pat */ 4471 int sc; /* dir char */ 4472 { 4473 if (spats[idx].pat != NULL) 4474 { 4475 fprintf(fp, "\n# Last %sSearch Pattern:\n~", s); 4476 /* off.dir is not stored, it's reset to forward */ 4477 fprintf(fp, "%c%c%c%c%ld%s%c", 4478 spats[idx].magic ? 'M' : 'm', /* magic */ 4479 spats[idx].no_scs ? 's' : 'S', /* smartcase */ 4480 spats[idx].off.line ? 'L' : 'l', /* line offset */ 4481 spats[idx].off.end ? 'E' : 'e', /* offset from end */ 4482 spats[idx].off.off, /* offset */ 4483 last_idx == idx ? "~" : "", /* last used pat */ 4484 sc); 4485 viminfo_writestring(fp, spats[idx].pat); 4486 } 4487 } 4488 #endif /* FEAT_VIMINFO */ 4489