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 #ifdef FEAT_LISP 1562 int lispcomm = FALSE; /* inside of Lisp-style comment */ 1563 int lisp = curbuf->b_p_lisp; /* engage Lisp-specific hacks ;) */ 1564 #endif 1565 1566 pos = curwin->w_cursor; 1567 linep = ml_get(pos.lnum); 1568 1569 cpo_match = (vim_strchr(p_cpo, CPO_MATCH) != NULL); 1570 cpo_bsl = (vim_strchr(p_cpo, CPO_MATCHBSL) != NULL); 1571 1572 /* Direction to search when initc is '/', '*' or '#' */ 1573 if (flags & FM_BACKWARD) 1574 dir = BACKWARD; 1575 else if (flags & FM_FORWARD) 1576 dir = FORWARD; 1577 else 1578 dir = 0; 1579 1580 /* 1581 * if initc given, look in the table for the matching character 1582 * '/' and '*' are special cases: look for start or end of comment. 1583 * When '/' is used, we ignore running backwards into an star-slash, for 1584 * "[*" command, we just want to find any comment. 1585 */ 1586 if (initc == '/' || initc == '*') 1587 { 1588 comment_dir = dir; 1589 if (initc == '/') 1590 ignore_cend = TRUE; 1591 backwards = (dir == FORWARD) ? FALSE : TRUE; 1592 initc = NUL; 1593 } 1594 else if (initc != '#' && initc != NUL) 1595 { 1596 /* 'matchpairs' is "x:y,x:y" */ 1597 for (ptr = curbuf->b_p_mps; *ptr; ptr += 2) 1598 { 1599 if (*ptr == initc) 1600 { 1601 findc = initc; 1602 initc = ptr[2]; 1603 backwards = TRUE; 1604 break; 1605 } 1606 ptr += 2; 1607 if (*ptr == initc) 1608 { 1609 findc = initc; 1610 initc = ptr[-2]; 1611 backwards = FALSE; 1612 break; 1613 } 1614 if (ptr[1] != ',') 1615 break; 1616 } 1617 if (!findc) /* invalid initc! */ 1618 return NULL; 1619 } 1620 /* 1621 * Either initc is '#', or no initc was given and we need to look under the 1622 * cursor. 1623 */ 1624 else 1625 { 1626 if (initc == '#') 1627 { 1628 hash_dir = dir; 1629 } 1630 else 1631 { 1632 /* 1633 * initc was not given, must look for something to match under 1634 * or near the cursor. 1635 * Only check for special things when 'cpo' doesn't have '%'. 1636 */ 1637 if (!cpo_match) 1638 { 1639 /* Are we before or at #if, #else etc.? */ 1640 ptr = skipwhite(linep); 1641 if (*ptr == '#' && pos.col <= (colnr_T)(ptr - linep)) 1642 { 1643 ptr = skipwhite(ptr + 1); 1644 if ( STRNCMP(ptr, "if", 2) == 0 1645 || STRNCMP(ptr, "endif", 5) == 0 1646 || STRNCMP(ptr, "el", 2) == 0) 1647 hash_dir = 1; 1648 } 1649 1650 /* Are we on a comment? */ 1651 else if (linep[pos.col] == '/') 1652 { 1653 if (linep[pos.col + 1] == '*') 1654 { 1655 comment_dir = FORWARD; 1656 backwards = FALSE; 1657 pos.col++; 1658 } 1659 else if (pos.col > 0 && linep[pos.col - 1] == '*') 1660 { 1661 comment_dir = BACKWARD; 1662 backwards = TRUE; 1663 pos.col--; 1664 } 1665 } 1666 else if (linep[pos.col] == '*') 1667 { 1668 if (linep[pos.col + 1] == '/') 1669 { 1670 comment_dir = BACKWARD; 1671 backwards = TRUE; 1672 } 1673 else if (pos.col > 0 && linep[pos.col - 1] == '/') 1674 { 1675 comment_dir = FORWARD; 1676 backwards = FALSE; 1677 } 1678 } 1679 } 1680 1681 /* 1682 * If we are not on a comment or the # at the start of a line, then 1683 * look for brace anywhere on this line after the cursor. 1684 */ 1685 if (!hash_dir && !comment_dir) 1686 { 1687 /* 1688 * Find the brace under or after the cursor. 1689 * If beyond the end of the line, use the last character in 1690 * the line. 1691 */ 1692 if (linep[pos.col] == NUL && pos.col) 1693 --pos.col; 1694 for (;;) 1695 { 1696 initc = linep[pos.col]; 1697 if (initc == NUL) 1698 break; 1699 1700 for (ptr = curbuf->b_p_mps; *ptr; ++ptr) 1701 { 1702 if (*ptr == initc) 1703 { 1704 findc = ptr[2]; 1705 backwards = FALSE; 1706 break; 1707 } 1708 ptr += 2; 1709 if (*ptr == initc) 1710 { 1711 findc = ptr[-2]; 1712 backwards = TRUE; 1713 break; 1714 } 1715 if (!*++ptr) 1716 break; 1717 } 1718 if (findc) 1719 break; 1720 #ifdef FEAT_MBYTE 1721 if (has_mbyte) 1722 pos.col += (*mb_ptr2len_check)(linep + pos.col); 1723 else 1724 #endif 1725 ++pos.col; 1726 } 1727 if (!findc) 1728 { 1729 /* no brace in the line, maybe use " #if" then */ 1730 if (!cpo_match && *skipwhite(linep) == '#') 1731 hash_dir = 1; 1732 else 1733 return NULL; 1734 } 1735 else if (!cpo_bsl) 1736 { 1737 int col, bslcnt = 0; 1738 1739 /* Set "match_escaped" if there are an odd number of 1740 * backslashes. */ 1741 for (col = pos.col; check_prevcol(linep, col, '\\', &col);) 1742 bslcnt++; 1743 match_escaped = (bslcnt & 1); 1744 } 1745 } 1746 } 1747 if (hash_dir) 1748 { 1749 /* 1750 * Look for matching #if, #else, #elif, or #endif 1751 */ 1752 if (oap != NULL) 1753 oap->motion_type = MLINE; /* Linewise for this case only */ 1754 if (initc != '#') 1755 { 1756 ptr = skipwhite(skipwhite(linep) + 1); 1757 if (STRNCMP(ptr, "if", 2) == 0 || STRNCMP(ptr, "el", 2) == 0) 1758 hash_dir = 1; 1759 else if (STRNCMP(ptr, "endif", 5) == 0) 1760 hash_dir = -1; 1761 else 1762 return NULL; 1763 } 1764 pos.col = 0; 1765 while (!got_int) 1766 { 1767 if (hash_dir > 0) 1768 { 1769 if (pos.lnum == curbuf->b_ml.ml_line_count) 1770 break; 1771 } 1772 else if (pos.lnum == 1) 1773 break; 1774 pos.lnum += hash_dir; 1775 linep = ml_get(pos.lnum); 1776 line_breakcheck(); /* check for CTRL-C typed */ 1777 ptr = skipwhite(linep); 1778 if (*ptr != '#') 1779 continue; 1780 pos.col = (colnr_T) (ptr - linep); 1781 ptr = skipwhite(ptr + 1); 1782 if (hash_dir > 0) 1783 { 1784 if (STRNCMP(ptr, "if", 2) == 0) 1785 count++; 1786 else if (STRNCMP(ptr, "el", 2) == 0) 1787 { 1788 if (count == 0) 1789 return &pos; 1790 } 1791 else if (STRNCMP(ptr, "endif", 5) == 0) 1792 { 1793 if (count == 0) 1794 return &pos; 1795 count--; 1796 } 1797 } 1798 else 1799 { 1800 if (STRNCMP(ptr, "if", 2) == 0) 1801 { 1802 if (count == 0) 1803 return &pos; 1804 count--; 1805 } 1806 else if (initc == '#' && STRNCMP(ptr, "el", 2) == 0) 1807 { 1808 if (count == 0) 1809 return &pos; 1810 } 1811 else if (STRNCMP(ptr, "endif", 5) == 0) 1812 count++; 1813 } 1814 } 1815 return NULL; 1816 } 1817 } 1818 1819 #ifdef FEAT_RIGHTLEFT 1820 /* This is just guessing: when 'rightleft' is set, search for a maching 1821 * paren/brace in the other direction. */ 1822 if (curwin->w_p_rl && vim_strchr((char_u *)"()[]{}<>", initc) != NULL) 1823 backwards = !backwards; 1824 #endif 1825 1826 do_quotes = -1; 1827 start_in_quotes = MAYBE; 1828 /* backward search: Check if this line contains a single-line comment */ 1829 if ((backwards && comment_dir) 1830 #ifdef FEAT_LISP 1831 || lisp 1832 #endif 1833 ) 1834 comment_col = check_linecomment(linep); 1835 #ifdef FEAT_LISP 1836 if (lisp && comment_col != MAXCOL && pos.col > (colnr_T)comment_col) 1837 lispcomm = TRUE; /* find match inside this comment */ 1838 #endif 1839 while (!got_int) 1840 { 1841 /* 1842 * Go to the next position, forward or backward. We could use 1843 * inc() and dec() here, but that is much slower 1844 */ 1845 if (backwards) 1846 { 1847 #ifdef FEAT_LISP 1848 /* char to match is inside of comment, don't search outside */ 1849 if (lispcomm && pos.col < (colnr_T)comment_col) 1850 break; 1851 #endif 1852 if (pos.col == 0) /* at start of line, go to prev. one */ 1853 { 1854 if (pos.lnum == 1) /* start of file */ 1855 break; 1856 --pos.lnum; 1857 1858 if (maxtravel && traveled++ > maxtravel) 1859 break; 1860 1861 linep = ml_get(pos.lnum); 1862 pos.col = (colnr_T)STRLEN(linep); /* pos.col on trailing NUL */ 1863 do_quotes = -1; 1864 line_breakcheck(); 1865 1866 /* Check if this line contains a single-line comment */ 1867 if (comment_dir 1868 #ifdef FEAT_LISP 1869 || lisp 1870 #endif 1871 ) 1872 comment_col = check_linecomment(linep); 1873 #ifdef FEAT_LISP 1874 /* skip comment */ 1875 if (lisp && comment_col != MAXCOL) 1876 pos.col = comment_col; 1877 #endif 1878 } 1879 else 1880 { 1881 --pos.col; 1882 #ifdef FEAT_MBYTE 1883 if (has_mbyte) 1884 pos.col -= (*mb_head_off)(linep, linep + pos.col); 1885 #endif 1886 } 1887 } 1888 else /* forward search */ 1889 { 1890 if (linep[pos.col] == NUL 1891 /* at end of line, go to next one */ 1892 #ifdef FEAT_LISP 1893 /* don't search for match in comment */ 1894 || (lisp && comment_col != MAXCOL 1895 && pos.col == (colnr_T)comment_col) 1896 #endif 1897 ) 1898 { 1899 if (pos.lnum == curbuf->b_ml.ml_line_count /* end of file */ 1900 #ifdef FEAT_LISP 1901 /* line is exhausted and comment with it, 1902 * don't search for match in code */ 1903 || lispcomm 1904 #endif 1905 ) 1906 break; 1907 ++pos.lnum; 1908 1909 if (maxtravel && traveled++ > maxtravel) 1910 break; 1911 1912 linep = ml_get(pos.lnum); 1913 pos.col = 0; 1914 do_quotes = -1; 1915 line_breakcheck(); 1916 #ifdef FEAT_LISP 1917 if (lisp) /* find comment pos in new line */ 1918 comment_col = check_linecomment(linep); 1919 #endif 1920 } 1921 else 1922 { 1923 #ifdef FEAT_MBYTE 1924 if (has_mbyte) 1925 pos.col += (*mb_ptr2len_check)(linep + pos.col); 1926 else 1927 #endif 1928 ++pos.col; 1929 } 1930 } 1931 1932 /* 1933 * If FM_BLOCKSTOP given, stop at a '{' or '}' in column 0. 1934 */ 1935 if (pos.col == 0 && (flags & FM_BLOCKSTOP) && 1936 (linep[0] == '{' || linep[0] == '}')) 1937 { 1938 if (linep[0] == findc && count == 0) /* match! */ 1939 return &pos; 1940 break; /* out of scope */ 1941 } 1942 1943 if (comment_dir) 1944 { 1945 /* Note: comments do not nest, and we ignore quotes in them */ 1946 /* TODO: ignore comment brackets inside strings */ 1947 if (comment_dir == FORWARD) 1948 { 1949 if (linep[pos.col] == '*' && linep[pos.col + 1] == '/') 1950 { 1951 pos.col++; 1952 return &pos; 1953 } 1954 } 1955 else /* Searching backwards */ 1956 { 1957 /* 1958 * A comment may contain / * or / /, it may also start or end 1959 * with / * /. Ignore a / * after / /. 1960 */ 1961 if (pos.col == 0) 1962 continue; 1963 else if ( linep[pos.col - 1] == '/' 1964 && linep[pos.col] == '*' 1965 && (int)pos.col < comment_col) 1966 { 1967 count++; 1968 match_pos = pos; 1969 match_pos.col--; 1970 } 1971 else if (linep[pos.col - 1] == '*' && linep[pos.col] == '/') 1972 { 1973 if (count > 0) 1974 pos = match_pos; 1975 else if (pos.col > 1 && linep[pos.col - 2] == '/' 1976 && (int)pos.col <= comment_col) 1977 pos.col -= 2; 1978 else if (ignore_cend) 1979 continue; 1980 else 1981 return NULL; 1982 return &pos; 1983 } 1984 } 1985 continue; 1986 } 1987 1988 /* 1989 * If smart matching ('cpoptions' does not contain '%'), braces inside 1990 * of quotes are ignored, but only if there is an even number of 1991 * quotes in the line. 1992 */ 1993 if (cpo_match) 1994 do_quotes = 0; 1995 else if (do_quotes == -1) 1996 { 1997 /* 1998 * Count the number of quotes in the line, skipping \" and '"'. 1999 * Watch out for "\\". 2000 */ 2001 at_start = do_quotes; 2002 for (ptr = linep; *ptr; ++ptr) 2003 { 2004 if (ptr == linep + pos.col + backwards) 2005 at_start = (do_quotes & 1); 2006 if (*ptr == '"' 2007 && (ptr == linep || ptr[-1] != '\'' || ptr[1] != '\'')) 2008 ++do_quotes; 2009 if (*ptr == '\\' && ptr[1] != NUL) 2010 ++ptr; 2011 } 2012 do_quotes &= 1; /* result is 1 with even number of quotes */ 2013 2014 /* 2015 * If we find an uneven count, check current line and previous 2016 * one for a '\' at the end. 2017 */ 2018 if (!do_quotes) 2019 { 2020 inquote = FALSE; 2021 if (ptr[-1] == '\\') 2022 { 2023 do_quotes = 1; 2024 if (start_in_quotes == MAYBE) 2025 { 2026 /* Do we need to use at_start here? */ 2027 inquote = TRUE; 2028 start_in_quotes = TRUE; 2029 } 2030 else if (backwards) 2031 inquote = TRUE; 2032 } 2033 if (pos.lnum > 1) 2034 { 2035 ptr = ml_get(pos.lnum - 1); 2036 if (*ptr && *(ptr + STRLEN(ptr) - 1) == '\\') 2037 { 2038 do_quotes = 1; 2039 if (start_in_quotes == MAYBE) 2040 { 2041 inquote = at_start; 2042 if (inquote) 2043 start_in_quotes = TRUE; 2044 } 2045 else if (!backwards) 2046 inquote = TRUE; 2047 } 2048 } 2049 } 2050 } 2051 if (start_in_quotes == MAYBE) 2052 start_in_quotes = FALSE; 2053 2054 /* 2055 * If 'smartmatch' is set: 2056 * Things inside quotes are ignored by setting 'inquote'. If we 2057 * find a quote without a preceding '\' invert 'inquote'. At the 2058 * end of a line not ending in '\' we reset 'inquote'. 2059 * 2060 * In lines with an uneven number of quotes (without preceding '\') 2061 * we do not know which part to ignore. Therefore we only set 2062 * inquote if the number of quotes in a line is even, unless this 2063 * line or the previous one ends in a '\'. Complicated, isn't it? 2064 */ 2065 switch (c = linep[pos.col]) 2066 { 2067 case NUL: 2068 /* at end of line without trailing backslash, reset inquote */ 2069 if (pos.col == 0 || linep[pos.col - 1] != '\\') 2070 { 2071 inquote = FALSE; 2072 start_in_quotes = FALSE; 2073 } 2074 break; 2075 2076 case '"': 2077 /* a quote that is preceded with an odd number of backslashes is 2078 * ignored */ 2079 if (do_quotes) 2080 { 2081 int col; 2082 2083 for (col = pos.col - 1; col >= 0; --col) 2084 if (linep[col] != '\\') 2085 break; 2086 if ((((int)pos.col - 1 - col) & 1) == 0) 2087 { 2088 inquote = !inquote; 2089 start_in_quotes = FALSE; 2090 } 2091 } 2092 break; 2093 2094 /* 2095 * If smart matching ('cpoptions' does not contain '%'): 2096 * Skip things in single quotes: 'x' or '\x'. Be careful for single 2097 * single quotes, eg jon's. Things like '\233' or '\x3f' are not 2098 * skipped, there is never a brace in them. 2099 * Ignore this when finding matches for `'. 2100 */ 2101 case '\'': 2102 if (!cpo_match && initc != '\'' && findc != '\'') 2103 { 2104 if (backwards) 2105 { 2106 if (pos.col > 1) 2107 { 2108 if (linep[pos.col - 2] == '\'') 2109 { 2110 pos.col -= 2; 2111 break; 2112 } 2113 else if (linep[pos.col - 2] == '\\' && 2114 pos.col > 2 && linep[pos.col - 3] == '\'') 2115 { 2116 pos.col -= 3; 2117 break; 2118 } 2119 } 2120 } 2121 else if (linep[pos.col + 1]) /* forward search */ 2122 { 2123 if (linep[pos.col + 1] == '\\' && 2124 linep[pos.col + 2] && linep[pos.col + 3] == '\'') 2125 { 2126 pos.col += 3; 2127 break; 2128 } 2129 else if (linep[pos.col + 2] == '\'') 2130 { 2131 pos.col += 2; 2132 break; 2133 } 2134 } 2135 } 2136 /* FALLTHROUGH */ 2137 2138 default: 2139 #ifdef FEAT_LISP 2140 /* 2141 * For Lisp skip over backslashed (), {} and []. 2142 * (actually, we skip #\( et al) 2143 */ 2144 if (curbuf->b_p_lisp 2145 && vim_strchr((char_u *)"(){}[]", c) != NULL 2146 && pos.col > 1 2147 && check_prevcol(linep, pos.col, '\\', NULL) 2148 && check_prevcol(linep, pos.col - 1, '#', NULL)) 2149 break; 2150 #endif 2151 2152 /* Check for match outside of quotes, and inside of 2153 * quotes when the start is also inside of quotes. */ 2154 if ((!inquote || start_in_quotes == TRUE) 2155 && (c == initc || c == findc)) 2156 { 2157 int col, bslcnt = 0; 2158 2159 if (!cpo_bsl) 2160 { 2161 for (col = pos.col; check_prevcol(linep, col, '\\', &col);) 2162 bslcnt++; 2163 } 2164 /* Only accept a match when 'M' is in 'cpo' or when ecaping is 2165 * what we expect. */ 2166 if (cpo_bsl || (bslcnt & 1) == match_escaped) 2167 { 2168 if (c == initc) 2169 count++; 2170 else 2171 { 2172 if (count == 0) 2173 return &pos; 2174 count--; 2175 } 2176 } 2177 } 2178 } 2179 } 2180 2181 if (comment_dir == BACKWARD && count > 0) 2182 { 2183 pos = match_pos; 2184 return &pos; 2185 } 2186 return (pos_T *)NULL; /* never found it */ 2187 } 2188 2189 /* 2190 * Check if line[] contains a / / comment. 2191 * Return MAXCOL if not, otherwise return the column. 2192 * TODO: skip strings. 2193 */ 2194 static int 2195 check_linecomment(line) 2196 char_u *line; 2197 { 2198 char_u *p; 2199 2200 p = line; 2201 #ifdef FEAT_LISP 2202 /* skip Lispish one-line comments */ 2203 if (curbuf->b_p_lisp) 2204 { 2205 if (vim_strchr(p, ';') != NULL) /* there may be comments */ 2206 { 2207 int instr = FALSE; /* inside of string */ 2208 2209 p = line; /* scan from start */ 2210 while ((p = vim_strpbrk(p, "\";")) != NULL) 2211 { 2212 if (*p == '"') 2213 { 2214 if (instr) 2215 { 2216 if (*(p - 1) != '\\') /* skip escaped quote */ 2217 instr = FALSE; 2218 } 2219 else if (p == line || ((p - line) >= 2 2220 /* skip #\" form */ 2221 && *(p - 1) != '\\' && *(p - 2) != '#')) 2222 instr = TRUE; 2223 } 2224 else if (!instr && ((p - line) < 2 2225 || (*(p - 1) != '\\' && *(p - 2) != '#'))) 2226 break; /* found! */ 2227 ++p; 2228 } 2229 } 2230 else 2231 p = NULL; 2232 } 2233 else 2234 #endif 2235 while ((p = vim_strchr(p, '/')) != NULL) 2236 { 2237 if (p[1] == '/') 2238 break; 2239 ++p; 2240 } 2241 2242 if (p == NULL) 2243 return MAXCOL; 2244 return (int)(p - line); 2245 } 2246 2247 /* 2248 * Move cursor briefly to character matching the one under the cursor. 2249 * Used for Insert mode and "r" command. 2250 * Show the match only if it is visible on the screen. 2251 * If there isn't a match, then beep. 2252 */ 2253 void 2254 showmatch(c) 2255 int c; /* char to show match for */ 2256 { 2257 pos_T *lpos, save_cursor; 2258 pos_T mpos; 2259 colnr_T vcol; 2260 long save_so; 2261 long save_siso; 2262 #ifdef CURSOR_SHAPE 2263 int save_state; 2264 #endif 2265 colnr_T save_dollar_vcol; 2266 char_u *p; 2267 2268 /* 2269 * Only show match for chars in the 'matchpairs' option. 2270 */ 2271 /* 'matchpairs' is "x:y,x:y" */ 2272 for (p = curbuf->b_p_mps; *p != NUL; p += 2) 2273 { 2274 #ifdef FEAT_RIGHTLEFT 2275 if (*p == c && (curwin->w_p_rl ^ p_ri)) 2276 break; 2277 #endif 2278 p += 2; 2279 if (*p == c 2280 #ifdef FEAT_RIGHTLEFT 2281 && !(curwin->w_p_rl ^ p_ri) 2282 #endif 2283 ) 2284 break; 2285 if (p[1] != ',') 2286 return; 2287 } 2288 2289 if ((lpos = findmatch(NULL, NUL)) == NULL) /* no match, so beep */ 2290 vim_beep(); 2291 else if (lpos->lnum >= curwin->w_topline) 2292 { 2293 if (!curwin->w_p_wrap) 2294 getvcol(curwin, lpos, NULL, &vcol, NULL); 2295 if (curwin->w_p_wrap || (vcol >= curwin->w_leftcol 2296 && vcol < curwin->w_leftcol + W_WIDTH(curwin))) 2297 { 2298 mpos = *lpos; /* save the pos, update_screen() may change it */ 2299 save_cursor = curwin->w_cursor; 2300 save_so = p_so; 2301 save_siso = p_siso; 2302 /* Handle "$" in 'cpo': If the ')' is typed on top of the "$", 2303 * stop displaying the "$". */ 2304 if (dollar_vcol > 0 && dollar_vcol == curwin->w_virtcol) 2305 dollar_vcol = 0; 2306 ++curwin->w_virtcol; /* do display ')' just before "$" */ 2307 update_screen(VALID); /* show the new char first */ 2308 2309 save_dollar_vcol = dollar_vcol; 2310 #ifdef CURSOR_SHAPE 2311 save_state = State; 2312 State = SHOWMATCH; 2313 ui_cursor_shape(); /* may show different cursor shape */ 2314 #endif 2315 curwin->w_cursor = mpos; /* move to matching char */ 2316 p_so = 0; /* don't use 'scrolloff' here */ 2317 p_siso = 0; /* don't use 'sidescrolloff' here */ 2318 showruler(FALSE); 2319 setcursor(); 2320 cursor_on(); /* make sure that the cursor is shown */ 2321 out_flush(); 2322 #ifdef FEAT_GUI 2323 if (gui.in_use) 2324 { 2325 gui_update_cursor(TRUE, FALSE); 2326 gui_mch_flush(); 2327 } 2328 #endif 2329 /* Restore dollar_vcol(), because setcursor() may call curs_rows() 2330 * which resets it if the matching position is in a previous line 2331 * and has a higher column number. */ 2332 dollar_vcol = save_dollar_vcol; 2333 2334 /* 2335 * brief pause, unless 'm' is present in 'cpo' and a character is 2336 * available. 2337 */ 2338 if (vim_strchr(p_cpo, CPO_SHOWMATCH) != NULL) 2339 ui_delay(p_mat * 100L, TRUE); 2340 else if (!char_avail()) 2341 ui_delay(p_mat * 100L, FALSE); 2342 curwin->w_cursor = save_cursor; /* restore cursor position */ 2343 p_so = save_so; 2344 p_siso = save_siso; 2345 #ifdef CURSOR_SHAPE 2346 State = save_state; 2347 ui_cursor_shape(); /* may show different cursor shape */ 2348 #endif 2349 } 2350 } 2351 } 2352 2353 /* 2354 * findsent(dir, count) - Find the start of the next sentence in direction 2355 * 'dir' Sentences are supposed to end in ".", "!" or "?" followed by white 2356 * space or a line break. Also stop at an empty line. 2357 * Return OK if the next sentence was found. 2358 */ 2359 int 2360 findsent(dir, count) 2361 int dir; 2362 long count; 2363 { 2364 pos_T pos, tpos; 2365 int c; 2366 int (*func) __ARGS((pos_T *)); 2367 int startlnum; 2368 int noskip = FALSE; /* do not skip blanks */ 2369 int cpo_J; 2370 2371 pos = curwin->w_cursor; 2372 if (dir == FORWARD) 2373 func = incl; 2374 else 2375 func = decl; 2376 2377 while (count--) 2378 { 2379 /* 2380 * if on an empty line, skip upto a non-empty line 2381 */ 2382 if (gchar_pos(&pos) == NUL) 2383 { 2384 do 2385 if ((*func)(&pos) == -1) 2386 break; 2387 while (gchar_pos(&pos) == NUL); 2388 if (dir == FORWARD) 2389 goto found; 2390 } 2391 /* 2392 * if on the start of a paragraph or a section and searching forward, 2393 * go to the next line 2394 */ 2395 else if (dir == FORWARD && pos.col == 0 && 2396 startPS(pos.lnum, NUL, FALSE)) 2397 { 2398 if (pos.lnum == curbuf->b_ml.ml_line_count) 2399 return FAIL; 2400 ++pos.lnum; 2401 goto found; 2402 } 2403 else if (dir == BACKWARD) 2404 decl(&pos); 2405 2406 /* go back to the previous non-blank char */ 2407 while ((c = gchar_pos(&pos)) == ' ' || c == '\t' || 2408 (dir == BACKWARD && vim_strchr((char_u *)".!?)]\"'", c) != NULL)) 2409 { 2410 if (decl(&pos) == -1) 2411 break; 2412 /* when going forward: Stop in front of empty line */ 2413 if (lineempty(pos.lnum) && dir == FORWARD) 2414 { 2415 incl(&pos); 2416 goto found; 2417 } 2418 } 2419 2420 /* remember the line where the search started */ 2421 startlnum = pos.lnum; 2422 cpo_J = vim_strchr(p_cpo, CPO_ENDOFSENT) != NULL; 2423 2424 for (;;) /* find end of sentence */ 2425 { 2426 c = gchar_pos(&pos); 2427 if (c == NUL || (pos.col == 0 && startPS(pos.lnum, NUL, FALSE))) 2428 { 2429 if (dir == BACKWARD && pos.lnum != startlnum) 2430 ++pos.lnum; 2431 break; 2432 } 2433 if (c == '.' || c == '!' || c == '?') 2434 { 2435 tpos = pos; 2436 do 2437 if ((c = inc(&tpos)) == -1) 2438 break; 2439 while (vim_strchr((char_u *)")]\"'", c = gchar_pos(&tpos)) 2440 != NULL); 2441 if (c == -1 || (!cpo_J && (c == ' ' || c == '\t')) || c == NUL 2442 || (cpo_J && (c == ' ' && inc(&tpos) >= 0 2443 && gchar_pos(&tpos) == ' '))) 2444 { 2445 pos = tpos; 2446 if (gchar_pos(&pos) == NUL) /* skip NUL at EOL */ 2447 inc(&pos); 2448 break; 2449 } 2450 } 2451 if ((*func)(&pos) == -1) 2452 { 2453 if (count) 2454 return FAIL; 2455 noskip = TRUE; 2456 break; 2457 } 2458 } 2459 found: 2460 /* skip white space */ 2461 while (!noskip && ((c = gchar_pos(&pos)) == ' ' || c == '\t')) 2462 if (incl(&pos) == -1) 2463 break; 2464 } 2465 2466 setpcmark(); 2467 curwin->w_cursor = pos; 2468 return OK; 2469 } 2470 2471 /* 2472 * findpar(dir, count, what) - Find the next paragraph in direction 'dir' 2473 * Paragraphs are currently supposed to be separated by empty lines. 2474 * Return TRUE if the next paragraph was found. 2475 * If 'what' is '{' or '}' we go to the next section. 2476 * If 'both' is TRUE also stop at '}'. 2477 */ 2478 int 2479 findpar(oap, dir, count, what, both) 2480 oparg_T *oap; 2481 int dir; 2482 long count; 2483 int what; 2484 int both; 2485 { 2486 linenr_T curr; 2487 int did_skip; /* TRUE after separating lines have been skipped */ 2488 int first; /* TRUE on first line */ 2489 #ifdef FEAT_FOLDING 2490 linenr_T fold_first; /* first line of a closed fold */ 2491 linenr_T fold_last; /* last line of a closed fold */ 2492 int fold_skipped; /* TRUE if a closed fold was skipped this 2493 iteration */ 2494 #endif 2495 2496 curr = curwin->w_cursor.lnum; 2497 2498 while (count--) 2499 { 2500 did_skip = FALSE; 2501 for (first = TRUE; ; first = FALSE) 2502 { 2503 if (*ml_get(curr) != NUL) 2504 did_skip = TRUE; 2505 2506 #ifdef FEAT_FOLDING 2507 /* skip folded lines */ 2508 fold_skipped = FALSE; 2509 if (first && hasFolding(curr, &fold_first, &fold_last)) 2510 { 2511 curr = ((dir > 0) ? fold_last : fold_first) + dir; 2512 fold_skipped = TRUE; 2513 } 2514 #endif 2515 2516 if (!first && did_skip && startPS(curr, what, both)) 2517 break; 2518 2519 #ifdef FEAT_FOLDING 2520 if (fold_skipped) 2521 curr -= dir; 2522 #endif 2523 if ((curr += dir) < 1 || curr > curbuf->b_ml.ml_line_count) 2524 { 2525 if (count) 2526 return FALSE; 2527 curr -= dir; 2528 break; 2529 } 2530 } 2531 } 2532 setpcmark(); 2533 if (both && *ml_get(curr) == '}') /* include line with '}' */ 2534 ++curr; 2535 curwin->w_cursor.lnum = curr; 2536 if (curr == curbuf->b_ml.ml_line_count && what != '}') 2537 { 2538 if ((curwin->w_cursor.col = (colnr_T)STRLEN(ml_get(curr))) != 0) 2539 { 2540 --curwin->w_cursor.col; 2541 oap->inclusive = TRUE; 2542 } 2543 } 2544 else 2545 curwin->w_cursor.col = 0; 2546 return TRUE; 2547 } 2548 2549 /* 2550 * check if the string 's' is a nroff macro that is in option 'opt' 2551 */ 2552 static int 2553 inmacro(opt, s) 2554 char_u *opt; 2555 char_u *s; 2556 { 2557 char_u *macro; 2558 2559 for (macro = opt; macro[0]; ++macro) 2560 { 2561 /* Accept two characters in the option being equal to two characters 2562 * in the line. A space in the option matches with a space in the 2563 * line or the line having ended. */ 2564 if ( (macro[0] == s[0] 2565 || (macro[0] == ' ' 2566 && (s[0] == NUL || s[0] == ' '))) 2567 && (macro[1] == s[1] 2568 || ((macro[1] == NUL || macro[1] == ' ') 2569 && (s[0] == NUL || s[1] == NUL || s[1] == ' ')))) 2570 break; 2571 ++macro; 2572 if (macro[0] == NUL) 2573 break; 2574 } 2575 return (macro[0] != NUL); 2576 } 2577 2578 /* 2579 * startPS: return TRUE if line 'lnum' is the start of a section or paragraph. 2580 * If 'para' is '{' or '}' only check for sections. 2581 * If 'both' is TRUE also stop at '}' 2582 */ 2583 int 2584 startPS(lnum, para, both) 2585 linenr_T lnum; 2586 int para; 2587 int both; 2588 { 2589 char_u *s; 2590 2591 s = ml_get(lnum); 2592 if (*s == para || *s == '\f' || (both && *s == '}')) 2593 return TRUE; 2594 if (*s == '.' && (inmacro(p_sections, s + 1) || 2595 (!para && inmacro(p_para, s + 1)))) 2596 return TRUE; 2597 return FALSE; 2598 } 2599 2600 /* 2601 * The following routines do the word searches performed by the 'w', 'W', 2602 * 'b', 'B', 'e', and 'E' commands. 2603 */ 2604 2605 /* 2606 * To perform these searches, characters are placed into one of three 2607 * classes, and transitions between classes determine word boundaries. 2608 * 2609 * The classes are: 2610 * 2611 * 0 - white space 2612 * 1 - punctuation 2613 * 2 or higher - keyword characters (letters, digits and underscore) 2614 */ 2615 2616 static int cls_bigword; /* TRUE for "W", "B" or "E" */ 2617 2618 /* 2619 * cls() - returns the class of character at curwin->w_cursor 2620 * 2621 * If a 'W', 'B', or 'E' motion is being done (cls_bigword == TRUE), chars 2622 * from class 2 and higher are reported as class 1 since only white space 2623 * boundaries are of interest. 2624 */ 2625 static int 2626 cls() 2627 { 2628 int c; 2629 2630 c = gchar_cursor(); 2631 #ifdef FEAT_FKMAP /* when 'akm' (Farsi mode), take care of Farsi blank */ 2632 if (p_altkeymap && c == F_BLANK) 2633 return 0; 2634 #endif 2635 if (c == ' ' || c == '\t' || c == NUL) 2636 return 0; 2637 #ifdef FEAT_MBYTE 2638 if (enc_dbcs != 0 && c > 0xFF) 2639 { 2640 /* If cls_bigword, report multi-byte chars as class 1. */ 2641 if (enc_dbcs == DBCS_KOR && cls_bigword) 2642 return 1; 2643 2644 /* process code leading/trailing bytes */ 2645 return dbcs_class(((unsigned)c >> 8), (c & 0xFF)); 2646 } 2647 if (enc_utf8) 2648 { 2649 c = utf_class(c); 2650 if (c != 0 && cls_bigword) 2651 return 1; 2652 return c; 2653 } 2654 #endif 2655 2656 /* If cls_bigword is TRUE, report all non-blanks as class 1. */ 2657 if (cls_bigword) 2658 return 1; 2659 2660 if (vim_iswordc(c)) 2661 return 2; 2662 return 1; 2663 } 2664 2665 2666 /* 2667 * fwd_word(count, type, eol) - move forward one word 2668 * 2669 * Returns FAIL if the cursor was already at the end of the file. 2670 * If eol is TRUE, last word stops at end of line (for operators). 2671 */ 2672 int 2673 fwd_word(count, bigword, eol) 2674 long count; 2675 int bigword; /* "W", "E" or "B" */ 2676 int eol; 2677 { 2678 int sclass; /* starting class */ 2679 int i; 2680 int last_line; 2681 2682 #ifdef FEAT_VIRTUALEDIT 2683 curwin->w_cursor.coladd = 0; 2684 #endif 2685 cls_bigword = bigword; 2686 while (--count >= 0) 2687 { 2688 #ifdef FEAT_FOLDING 2689 /* When inside a range of folded lines, move to the last char of the 2690 * last line. */ 2691 if (hasFolding(curwin->w_cursor.lnum, NULL, &curwin->w_cursor.lnum)) 2692 coladvance((colnr_T)MAXCOL); 2693 #endif 2694 sclass = cls(); 2695 2696 /* 2697 * We always move at least one character, unless on the last 2698 * character in the buffer. 2699 */ 2700 last_line = (curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count); 2701 i = inc_cursor(); 2702 if (i == -1 || (i >= 1 && last_line)) /* started at last char in file */ 2703 return FAIL; 2704 if (i == 1 && eol && count == 0) /* started at last char in line */ 2705 return OK; 2706 2707 /* 2708 * Go one char past end of current word (if any) 2709 */ 2710 if (sclass != 0) 2711 while (cls() == sclass) 2712 { 2713 i = inc_cursor(); 2714 if (i == -1 || (i >= 1 && eol && count == 0)) 2715 return OK; 2716 } 2717 2718 /* 2719 * go to next non-white 2720 */ 2721 while (cls() == 0) 2722 { 2723 /* 2724 * We'll stop if we land on a blank line 2725 */ 2726 if (curwin->w_cursor.col == 0 && *ml_get_curline() == NUL) 2727 break; 2728 2729 i = inc_cursor(); 2730 if (i == -1 || (i >= 1 && eol && count == 0)) 2731 return OK; 2732 } 2733 } 2734 return OK; 2735 } 2736 2737 /* 2738 * bck_word() - move backward 'count' words 2739 * 2740 * If stop is TRUE and we are already on the start of a word, move one less. 2741 * 2742 * Returns FAIL if top of the file was reached. 2743 */ 2744 int 2745 bck_word(count, bigword, stop) 2746 long count; 2747 int bigword; 2748 int stop; 2749 { 2750 int sclass; /* starting class */ 2751 2752 #ifdef FEAT_VIRTUALEDIT 2753 curwin->w_cursor.coladd = 0; 2754 #endif 2755 cls_bigword = bigword; 2756 while (--count >= 0) 2757 { 2758 #ifdef FEAT_FOLDING 2759 /* When inside a range of folded lines, move to the first char of the 2760 * first line. */ 2761 if (hasFolding(curwin->w_cursor.lnum, &curwin->w_cursor.lnum, NULL)) 2762 curwin->w_cursor.col = 0; 2763 #endif 2764 sclass = cls(); 2765 if (dec_cursor() == -1) /* started at start of file */ 2766 return FAIL; 2767 2768 if (!stop || sclass == cls() || sclass == 0) 2769 { 2770 /* 2771 * Skip white space before the word. 2772 * Stop on an empty line. 2773 */ 2774 while (cls() == 0) 2775 { 2776 if (curwin->w_cursor.col == 0 2777 && lineempty(curwin->w_cursor.lnum)) 2778 goto finished; 2779 if (dec_cursor() == -1) /* hit start of file, stop here */ 2780 return OK; 2781 } 2782 2783 /* 2784 * Move backward to start of this word. 2785 */ 2786 if (skip_chars(cls(), BACKWARD)) 2787 return OK; 2788 } 2789 2790 inc_cursor(); /* overshot - forward one */ 2791 finished: 2792 stop = FALSE; 2793 } 2794 return OK; 2795 } 2796 2797 /* 2798 * end_word() - move to the end of the word 2799 * 2800 * There is an apparent bug in the 'e' motion of the real vi. At least on the 2801 * System V Release 3 version for the 80386. Unlike 'b' and 'w', the 'e' 2802 * motion crosses blank lines. When the real vi crosses a blank line in an 2803 * 'e' motion, the cursor is placed on the FIRST character of the next 2804 * non-blank line. The 'E' command, however, works correctly. Since this 2805 * appears to be a bug, I have not duplicated it here. 2806 * 2807 * Returns FAIL if end of the file was reached. 2808 * 2809 * If stop is TRUE and we are already on the end of a word, move one less. 2810 * If empty is TRUE stop on an empty line. 2811 */ 2812 int 2813 end_word(count, bigword, stop, empty) 2814 long count; 2815 int bigword; 2816 int stop; 2817 int empty; 2818 { 2819 int sclass; /* starting class */ 2820 2821 #ifdef FEAT_VIRTUALEDIT 2822 curwin->w_cursor.coladd = 0; 2823 #endif 2824 cls_bigword = bigword; 2825 while (--count >= 0) 2826 { 2827 #ifdef FEAT_FOLDING 2828 /* When inside a range of folded lines, move to the last char of the 2829 * last line. */ 2830 if (hasFolding(curwin->w_cursor.lnum, NULL, &curwin->w_cursor.lnum)) 2831 coladvance((colnr_T)MAXCOL); 2832 #endif 2833 sclass = cls(); 2834 if (inc_cursor() == -1) 2835 return FAIL; 2836 2837 /* 2838 * If we're in the middle of a word, we just have to move to the end 2839 * of it. 2840 */ 2841 if (cls() == sclass && sclass != 0) 2842 { 2843 /* 2844 * Move forward to end of the current word 2845 */ 2846 if (skip_chars(sclass, FORWARD)) 2847 return FAIL; 2848 } 2849 else if (!stop || sclass == 0) 2850 { 2851 /* 2852 * We were at the end of a word. Go to the end of the next word. 2853 * First skip white space, if 'empty' is TRUE, stop at empty line. 2854 */ 2855 while (cls() == 0) 2856 { 2857 if (empty && curwin->w_cursor.col == 0 2858 && lineempty(curwin->w_cursor.lnum)) 2859 goto finished; 2860 if (inc_cursor() == -1) /* hit end of file, stop here */ 2861 return FAIL; 2862 } 2863 2864 /* 2865 * Move forward to the end of this word. 2866 */ 2867 if (skip_chars(cls(), FORWARD)) 2868 return FAIL; 2869 } 2870 dec_cursor(); /* overshot - one char backward */ 2871 finished: 2872 stop = FALSE; /* we move only one word less */ 2873 } 2874 return OK; 2875 } 2876 2877 /* 2878 * Move back to the end of the word. 2879 * 2880 * Returns FAIL if start of the file was reached. 2881 */ 2882 int 2883 bckend_word(count, bigword, eol) 2884 long count; 2885 int bigword; /* TRUE for "B" */ 2886 int eol; /* TRUE: stop at end of line. */ 2887 { 2888 int sclass; /* starting class */ 2889 int i; 2890 2891 #ifdef FEAT_VIRTUALEDIT 2892 curwin->w_cursor.coladd = 0; 2893 #endif 2894 cls_bigword = bigword; 2895 while (--count >= 0) 2896 { 2897 sclass = cls(); 2898 if ((i = dec_cursor()) == -1) 2899 return FAIL; 2900 if (eol && i == 1) 2901 return OK; 2902 2903 /* 2904 * Move backward to before the start of this word. 2905 */ 2906 if (sclass != 0) 2907 { 2908 while (cls() == sclass) 2909 if ((i = dec_cursor()) == -1 || (eol && i == 1)) 2910 return OK; 2911 } 2912 2913 /* 2914 * Move backward to end of the previous word 2915 */ 2916 while (cls() == 0) 2917 { 2918 if (curwin->w_cursor.col == 0 && lineempty(curwin->w_cursor.lnum)) 2919 break; 2920 if ((i = dec_cursor()) == -1 || (eol && i == 1)) 2921 return OK; 2922 } 2923 } 2924 return OK; 2925 } 2926 2927 /* 2928 * Skip a row of characters of the same class. 2929 * Return TRUE when end-of-file reached, FALSE otherwise. 2930 */ 2931 static int 2932 skip_chars(cclass, dir) 2933 int cclass; 2934 int dir; 2935 { 2936 while (cls() == cclass) 2937 if ((dir == FORWARD ? inc_cursor() : dec_cursor()) == -1) 2938 return TRUE; 2939 return FALSE; 2940 } 2941 2942 #ifdef FEAT_TEXTOBJ 2943 /* 2944 * Go back to the start of the word or the start of white space 2945 */ 2946 static void 2947 back_in_line() 2948 { 2949 int sclass; /* starting class */ 2950 2951 sclass = cls(); 2952 for (;;) 2953 { 2954 if (curwin->w_cursor.col == 0) /* stop at start of line */ 2955 break; 2956 dec_cursor(); 2957 if (cls() != sclass) /* stop at start of word */ 2958 { 2959 inc_cursor(); 2960 break; 2961 } 2962 } 2963 } 2964 2965 static void 2966 find_first_blank(posp) 2967 pos_T *posp; 2968 { 2969 int c; 2970 2971 while (decl(posp) != -1) 2972 { 2973 c = gchar_pos(posp); 2974 if (!vim_iswhite(c)) 2975 { 2976 incl(posp); 2977 break; 2978 } 2979 } 2980 } 2981 2982 /* 2983 * Skip count/2 sentences and count/2 separating white spaces. 2984 */ 2985 static void 2986 findsent_forward(count, at_start_sent) 2987 long count; 2988 int at_start_sent; /* cursor is at start of sentence */ 2989 { 2990 while (count--) 2991 { 2992 findsent(FORWARD, 1L); 2993 if (at_start_sent) 2994 find_first_blank(&curwin->w_cursor); 2995 if (count == 0 || at_start_sent) 2996 decl(&curwin->w_cursor); 2997 at_start_sent = !at_start_sent; 2998 } 2999 } 3000 3001 /* 3002 * Find word under cursor, cursor at end. 3003 * Used while an operator is pending, and in Visual mode. 3004 */ 3005 int 3006 current_word(oap, count, include, bigword) 3007 oparg_T *oap; 3008 long count; 3009 int include; /* TRUE: include word and white space */ 3010 int bigword; /* FALSE == word, TRUE == WORD */ 3011 { 3012 pos_T start_pos; 3013 pos_T pos; 3014 int inclusive = TRUE; 3015 int include_white = FALSE; 3016 3017 cls_bigword = bigword; 3018 3019 #ifdef FEAT_VISUAL 3020 /* Correct cursor when 'selection' is exclusive */ 3021 if (VIsual_active && *p_sel == 'e' && lt(VIsual, curwin->w_cursor)) 3022 dec_cursor(); 3023 3024 /* 3025 * When Visual mode is not active, or when the VIsual area is only one 3026 * character, select the word and/or white space under the cursor. 3027 */ 3028 if (!VIsual_active || equalpos(curwin->w_cursor, VIsual)) 3029 #endif 3030 { 3031 /* 3032 * Go to start of current word or white space. 3033 */ 3034 back_in_line(); 3035 start_pos = curwin->w_cursor; 3036 3037 /* 3038 * If the start is on white space, and white space should be included 3039 * (" word"), or start is not on white space, and white space should 3040 * not be included ("word"), find end of word. 3041 */ 3042 if ((cls() == 0) == include) 3043 { 3044 if (end_word(1L, bigword, TRUE, TRUE) == FAIL) 3045 return FAIL; 3046 } 3047 else 3048 { 3049 /* 3050 * If the start is not on white space, and white space should be 3051 * included ("word "), or start is on white space and white 3052 * space should not be included (" "), find start of word. 3053 * If we end up in the first column of the next line (single char 3054 * word) back up to end of the line. 3055 */ 3056 fwd_word(1L, bigword, TRUE); 3057 if (curwin->w_cursor.col == 0) 3058 decl(&curwin->w_cursor); 3059 else 3060 oneleft(); 3061 3062 if (include) 3063 include_white = TRUE; 3064 } 3065 3066 #ifdef FEAT_VISUAL 3067 if (VIsual_active) 3068 { 3069 /* should do something when inclusive == FALSE ! */ 3070 VIsual = start_pos; 3071 redraw_curbuf_later(INVERTED); /* update the inversion */ 3072 } 3073 else 3074 #endif 3075 { 3076 oap->start = start_pos; 3077 oap->motion_type = MCHAR; 3078 } 3079 --count; 3080 } 3081 3082 /* 3083 * When count is still > 0, extend with more objects. 3084 */ 3085 while (count > 0) 3086 { 3087 inclusive = TRUE; 3088 #ifdef FEAT_VISUAL 3089 if (VIsual_active && lt(curwin->w_cursor, VIsual)) 3090 { 3091 /* 3092 * In Visual mode, with cursor at start: move cursor back. 3093 */ 3094 if (decl(&curwin->w_cursor) == -1) 3095 return FAIL; 3096 if (include != (cls() != 0)) 3097 { 3098 if (bck_word(1L, bigword, TRUE) == FAIL) 3099 return FAIL; 3100 } 3101 else 3102 { 3103 if (bckend_word(1L, bigword, TRUE) == FAIL) 3104 return FAIL; 3105 (void)incl(&curwin->w_cursor); 3106 } 3107 } 3108 else 3109 #endif 3110 { 3111 /* 3112 * Move cursor forward one word and/or white area. 3113 */ 3114 if (incl(&curwin->w_cursor) == -1) 3115 return FAIL; 3116 if (include != (cls() == 0)) 3117 { 3118 if (fwd_word(1L, bigword, TRUE) == FAIL) 3119 return FAIL; 3120 /* 3121 * If end is just past a new-line, we don't want to include 3122 * the first character on the line 3123 */ 3124 if (oneleft() == FAIL) /* put cursor on last char of white */ 3125 inclusive = FALSE; 3126 } 3127 else 3128 { 3129 if (end_word(1L, bigword, TRUE, TRUE) == FAIL) 3130 return FAIL; 3131 } 3132 } 3133 --count; 3134 } 3135 3136 if (include_white && (cls() != 0 3137 || (curwin->w_cursor.col == 0 && !inclusive))) 3138 { 3139 /* 3140 * If we don't include white space at the end, move the start 3141 * to include some white space there. This makes "daw" work 3142 * better on the last word in a sentence (and "2daw" on last-but-one 3143 * word). Also when "2daw" deletes "word." at the end of the line 3144 * (cursor is at start of next line). 3145 * But don't delete white space at start of line (indent). 3146 */ 3147 pos = curwin->w_cursor; /* save cursor position */ 3148 curwin->w_cursor = start_pos; 3149 if (oneleft() == OK) 3150 { 3151 back_in_line(); 3152 if (cls() == 0 && curwin->w_cursor.col > 0) 3153 { 3154 #ifdef FEAT_VISUAL 3155 if (VIsual_active) 3156 VIsual = curwin->w_cursor; 3157 else 3158 #endif 3159 oap->start = curwin->w_cursor; 3160 } 3161 } 3162 curwin->w_cursor = pos; /* put cursor back at end */ 3163 } 3164 3165 #ifdef FEAT_VISUAL 3166 if (VIsual_active) 3167 { 3168 if (*p_sel == 'e' && inclusive && ltoreq(VIsual, curwin->w_cursor)) 3169 inc_cursor(); 3170 if (VIsual_mode == 'V') 3171 { 3172 VIsual_mode = 'v'; 3173 redraw_cmdline = TRUE; /* show mode later */ 3174 } 3175 } 3176 else 3177 #endif 3178 oap->inclusive = inclusive; 3179 3180 return OK; 3181 } 3182 3183 /* 3184 * Find sentence(s) under the cursor, cursor at end. 3185 * When Visual active, extend it by one or more sentences. 3186 */ 3187 int 3188 current_sent(oap, count, include) 3189 oparg_T *oap; 3190 long count; 3191 int include; 3192 { 3193 pos_T start_pos; 3194 pos_T pos; 3195 int start_blank; 3196 int c; 3197 int at_start_sent; 3198 long ncount; 3199 3200 start_pos = curwin->w_cursor; 3201 pos = start_pos; 3202 findsent(FORWARD, 1L); /* Find start of next sentence. */ 3203 3204 #ifdef FEAT_VISUAL 3205 /* 3206 * When visual area is bigger than one character: Extend it. 3207 */ 3208 if (VIsual_active && !equalpos(start_pos, VIsual)) 3209 { 3210 extend: 3211 if (lt(start_pos, VIsual)) 3212 { 3213 /* 3214 * Cursor at start of Visual area. 3215 * Find out where we are: 3216 * - in the white space before a sentence 3217 * - in a sentence or just after it 3218 * - at the start of a sentence 3219 */ 3220 at_start_sent = TRUE; 3221 decl(&pos); 3222 while (lt(pos, curwin->w_cursor)) 3223 { 3224 c = gchar_pos(&pos); 3225 if (!vim_iswhite(c)) 3226 { 3227 at_start_sent = FALSE; 3228 break; 3229 } 3230 incl(&pos); 3231 } 3232 if (!at_start_sent) 3233 { 3234 findsent(BACKWARD, 1L); 3235 if (equalpos(curwin->w_cursor, start_pos)) 3236 at_start_sent = TRUE; /* exactly at start of sentence */ 3237 else 3238 /* inside a sentence, go to its end (start of next) */ 3239 findsent(FORWARD, 1L); 3240 } 3241 if (include) /* "as" gets twice as much as "is" */ 3242 count *= 2; 3243 while (count--) 3244 { 3245 if (at_start_sent) 3246 find_first_blank(&curwin->w_cursor); 3247 c = gchar_cursor(); 3248 if (!at_start_sent || (!include && !vim_iswhite(c))) 3249 findsent(BACKWARD, 1L); 3250 at_start_sent = !at_start_sent; 3251 } 3252 } 3253 else 3254 { 3255 /* 3256 * Cursor at end of Visual area. 3257 * Find out where we are: 3258 * - just before a sentence 3259 * - just before or in the white space before a sentence 3260 * - in a sentence 3261 */ 3262 incl(&pos); 3263 at_start_sent = TRUE; 3264 if (!equalpos(pos, curwin->w_cursor)) /* not just before a sentence */ 3265 { 3266 at_start_sent = FALSE; 3267 while (lt(pos, curwin->w_cursor)) 3268 { 3269 c = gchar_pos(&pos); 3270 if (!vim_iswhite(c)) 3271 { 3272 at_start_sent = TRUE; 3273 break; 3274 } 3275 incl(&pos); 3276 } 3277 if (at_start_sent) /* in the sentence */ 3278 findsent(BACKWARD, 1L); 3279 else /* in/before white before a sentence */ 3280 curwin->w_cursor = start_pos; 3281 } 3282 3283 if (include) /* "as" gets twice as much as "is" */ 3284 count *= 2; 3285 findsent_forward(count, at_start_sent); 3286 if (*p_sel == 'e') 3287 ++curwin->w_cursor.col; 3288 } 3289 return OK; 3290 } 3291 #endif 3292 3293 /* 3294 * If cursor started on blank, check if it is just before the start of the 3295 * next sentence. 3296 */ 3297 while (c = gchar_pos(&pos), vim_iswhite(c)) /* vim_iswhite() is a macro */ 3298 incl(&pos); 3299 if (equalpos(pos, curwin->w_cursor)) 3300 { 3301 start_blank = TRUE; 3302 find_first_blank(&start_pos); /* go back to first blank */ 3303 } 3304 else 3305 { 3306 start_blank = FALSE; 3307 findsent(BACKWARD, 1L); 3308 start_pos = curwin->w_cursor; 3309 } 3310 if (include) 3311 ncount = count * 2; 3312 else 3313 { 3314 ncount = count; 3315 if (start_blank) 3316 --ncount; 3317 } 3318 if (ncount) 3319 findsent_forward(ncount, TRUE); 3320 else 3321 decl(&curwin->w_cursor); 3322 3323 if (include) 3324 { 3325 /* 3326 * If the blank in front of the sentence is included, exclude the 3327 * blanks at the end of the sentence, go back to the first blank. 3328 * If there are no trailing blanks, try to include leading blanks. 3329 */ 3330 if (start_blank) 3331 { 3332 find_first_blank(&curwin->w_cursor); 3333 c = gchar_pos(&curwin->w_cursor); /* vim_iswhite() is a macro */ 3334 if (vim_iswhite(c)) 3335 decl(&curwin->w_cursor); 3336 } 3337 else if (c = gchar_cursor(), !vim_iswhite(c)) 3338 find_first_blank(&start_pos); 3339 } 3340 3341 #ifdef FEAT_VISUAL 3342 if (VIsual_active) 3343 { 3344 /* avoid getting stuck with "is" on a single space before a sent. */ 3345 if (equalpos(start_pos, curwin->w_cursor)) 3346 goto extend; 3347 if (*p_sel == 'e') 3348 ++curwin->w_cursor.col; 3349 VIsual = start_pos; 3350 VIsual_mode = 'v'; 3351 redraw_curbuf_later(INVERTED); /* update the inversion */ 3352 } 3353 else 3354 #endif 3355 { 3356 /* include a newline after the sentence, if there is one */ 3357 if (incl(&curwin->w_cursor) == -1) 3358 oap->inclusive = TRUE; 3359 else 3360 oap->inclusive = FALSE; 3361 oap->start = start_pos; 3362 oap->motion_type = MCHAR; 3363 } 3364 return OK; 3365 } 3366 3367 int 3368 current_block(oap, count, include, what, other) 3369 oparg_T *oap; 3370 long count; 3371 int include; /* TRUE == include white space */ 3372 int what; /* '(', '{', etc. */ 3373 int other; /* ')', '}', etc. */ 3374 { 3375 pos_T old_pos; 3376 pos_T *pos = NULL; 3377 pos_T start_pos; 3378 pos_T *end_pos; 3379 pos_T old_start, old_end; 3380 char_u *save_cpo; 3381 int sol = FALSE; /* { at start of line */ 3382 3383 old_pos = curwin->w_cursor; 3384 old_end = curwin->w_cursor; /* remember where we started */ 3385 old_start = old_end; 3386 3387 /* 3388 * If we start on '(', '{', ')', '}', etc., use the whole block inclusive. 3389 */ 3390 #ifdef FEAT_VISUAL 3391 if (!VIsual_active || equalpos(VIsual, curwin->w_cursor)) 3392 #endif 3393 { 3394 setpcmark(); 3395 if (what == '{') /* ignore indent */ 3396 while (inindent(1)) 3397 if (inc_cursor() != 0) 3398 break; 3399 if (gchar_cursor() == what) /* cursor on '(' or '{' */ 3400 ++curwin->w_cursor.col; 3401 } 3402 #ifdef FEAT_VISUAL 3403 else if (lt(VIsual, curwin->w_cursor)) 3404 { 3405 old_start = VIsual; 3406 curwin->w_cursor = VIsual; /* cursor at low end of Visual */ 3407 } 3408 else 3409 old_end = VIsual; 3410 #endif 3411 3412 /* 3413 * Search backwards for unclosed '(', '{', etc.. 3414 * Put this position in start_pos. 3415 * Ignory quotes here. 3416 */ 3417 save_cpo = p_cpo; 3418 p_cpo = (char_u *)"%"; 3419 while (count-- > 0) 3420 { 3421 if ((pos = findmatch(NULL, what)) == NULL) 3422 break; 3423 curwin->w_cursor = *pos; 3424 start_pos = *pos; /* the findmatch for end_pos will overwrite *pos */ 3425 } 3426 p_cpo = save_cpo; 3427 3428 /* 3429 * Search for matching ')', '}', etc. 3430 * Put this position in curwin->w_cursor. 3431 */ 3432 if (pos == NULL || (end_pos = findmatch(NULL, other)) == NULL) 3433 { 3434 curwin->w_cursor = old_pos; 3435 return FAIL; 3436 } 3437 curwin->w_cursor = *end_pos; 3438 3439 /* 3440 * Try to exclude the '(', '{', ')', '}', etc. when "include" is FALSE. 3441 * If the ending '}' is only preceded by indent, skip that indent. 3442 * But only if the resulting area is not smaller than what we started with. 3443 */ 3444 while (!include) 3445 { 3446 incl(&start_pos); 3447 sol = (curwin->w_cursor.col == 0); 3448 decl(&curwin->w_cursor); 3449 if (what == '{') 3450 while (inindent(1)) 3451 { 3452 sol = TRUE; 3453 if (decl(&curwin->w_cursor) != 0) 3454 break; 3455 } 3456 #ifdef FEAT_VISUAL 3457 /* 3458 * In Visual mode, when the resulting area is not bigger than what we 3459 * started with, extend it to the next block, and then exclude again. 3460 */ 3461 if (!lt(start_pos, old_start) && !lt(old_end, curwin->w_cursor) 3462 && VIsual_active) 3463 { 3464 curwin->w_cursor = old_start; 3465 decl(&curwin->w_cursor); 3466 if ((pos = findmatch(NULL, what)) == NULL) 3467 { 3468 curwin->w_cursor = old_pos; 3469 return FAIL; 3470 } 3471 start_pos = *pos; 3472 curwin->w_cursor = *pos; 3473 if ((end_pos = findmatch(NULL, other)) == NULL) 3474 { 3475 curwin->w_cursor = old_pos; 3476 return FAIL; 3477 } 3478 curwin->w_cursor = *end_pos; 3479 } 3480 else 3481 #endif 3482 break; 3483 } 3484 3485 #ifdef FEAT_VISUAL 3486 if (VIsual_active) 3487 { 3488 if (*p_sel == 'e') 3489 ++curwin->w_cursor.col; 3490 if (sol) 3491 inc(&curwin->w_cursor); /* include the line break */ 3492 VIsual = start_pos; 3493 VIsual_mode = 'v'; 3494 redraw_curbuf_later(INVERTED); /* update the inversion */ 3495 showmode(); 3496 } 3497 else 3498 #endif 3499 { 3500 oap->start = start_pos; 3501 oap->motion_type = MCHAR; 3502 if (sol) 3503 { 3504 incl(&curwin->w_cursor); 3505 oap->inclusive = FALSE; 3506 } 3507 else 3508 oap->inclusive = TRUE; 3509 } 3510 3511 return OK; 3512 } 3513 3514 int 3515 current_par(oap, count, include, type) 3516 oparg_T *oap; 3517 long count; 3518 int include; /* TRUE == include white space */ 3519 int type; /* 'p' for paragraph, 'S' for section */ 3520 { 3521 linenr_T start_lnum; 3522 linenr_T end_lnum; 3523 int white_in_front; 3524 int dir; 3525 int start_is_white; 3526 int prev_start_is_white; 3527 int retval = OK; 3528 int do_white = FALSE; 3529 int t; 3530 int i; 3531 3532 if (type == 'S') /* not implemented yet */ 3533 return FAIL; 3534 3535 start_lnum = curwin->w_cursor.lnum; 3536 3537 #ifdef FEAT_VISUAL 3538 /* 3539 * When visual area is more than one line: extend it. 3540 */ 3541 if (VIsual_active && start_lnum != VIsual.lnum) 3542 { 3543 extend: 3544 if (start_lnum < VIsual.lnum) 3545 dir = BACKWARD; 3546 else 3547 dir = FORWARD; 3548 for (i = count; --i >= 0; ) 3549 { 3550 if (start_lnum == 3551 (dir == BACKWARD ? 1 : curbuf->b_ml.ml_line_count)) 3552 { 3553 retval = FAIL; 3554 break; 3555 } 3556 3557 prev_start_is_white = -1; 3558 for (t = 0; t < 2; ++t) 3559 { 3560 start_lnum += dir; 3561 start_is_white = linewhite(start_lnum); 3562 if (prev_start_is_white == start_is_white) 3563 { 3564 start_lnum -= dir; 3565 break; 3566 } 3567 for (;;) 3568 { 3569 if (start_lnum == (dir == BACKWARD 3570 ? 1 : curbuf->b_ml.ml_line_count)) 3571 break; 3572 if (start_is_white != linewhite(start_lnum + dir) 3573 || (!start_is_white 3574 && startPS(start_lnum + (dir > 0 3575 ? 1 : 0), 0, 0))) 3576 break; 3577 start_lnum += dir; 3578 } 3579 if (!include) 3580 break; 3581 if (start_lnum == (dir == BACKWARD 3582 ? 1 : curbuf->b_ml.ml_line_count)) 3583 break; 3584 prev_start_is_white = start_is_white; 3585 } 3586 } 3587 curwin->w_cursor.lnum = start_lnum; 3588 curwin->w_cursor.col = 0; 3589 return retval; 3590 } 3591 #endif 3592 3593 /* 3594 * First move back to the start_lnum of the paragraph or white lines 3595 */ 3596 white_in_front = linewhite(start_lnum); 3597 while (start_lnum > 1) 3598 { 3599 if (white_in_front) /* stop at first white line */ 3600 { 3601 if (!linewhite(start_lnum - 1)) 3602 break; 3603 } 3604 else /* stop at first non-white line of start of paragraph */ 3605 { 3606 if (linewhite(start_lnum - 1) || startPS(start_lnum, 0, 0)) 3607 break; 3608 } 3609 --start_lnum; 3610 } 3611 3612 /* 3613 * Move past the end of any white lines. 3614 */ 3615 end_lnum = start_lnum; 3616 while (linewhite(end_lnum) && end_lnum < curbuf->b_ml.ml_line_count) 3617 ++end_lnum; 3618 3619 --end_lnum; 3620 i = count; 3621 if (!include && white_in_front) 3622 --i; 3623 while (i--) 3624 { 3625 if (end_lnum == curbuf->b_ml.ml_line_count) 3626 return FAIL; 3627 3628 if (!include) 3629 do_white = linewhite(end_lnum + 1); 3630 3631 if (include || !do_white) 3632 { 3633 ++end_lnum; 3634 /* 3635 * skip to end of paragraph 3636 */ 3637 while (end_lnum < curbuf->b_ml.ml_line_count 3638 && !linewhite(end_lnum + 1) 3639 && !startPS(end_lnum + 1, 0, 0)) 3640 ++end_lnum; 3641 } 3642 3643 if (i == 0 && white_in_front && include) 3644 break; 3645 3646 /* 3647 * skip to end of white lines after paragraph 3648 */ 3649 if (include || do_white) 3650 while (end_lnum < curbuf->b_ml.ml_line_count 3651 && linewhite(end_lnum + 1)) 3652 ++end_lnum; 3653 } 3654 3655 /* 3656 * If there are no empty lines at the end, try to find some empty lines at 3657 * the start (unless that has been done already). 3658 */ 3659 if (!white_in_front && !linewhite(end_lnum) && include) 3660 while (start_lnum > 1 && linewhite(start_lnum - 1)) 3661 --start_lnum; 3662 3663 #ifdef FEAT_VISUAL 3664 if (VIsual_active) 3665 { 3666 /* Problem: when doing "Vipipip" nothing happens in a single white 3667 * line, we get stuck there. Trap this here. */ 3668 if (VIsual_mode == 'V' && start_lnum == curwin->w_cursor.lnum) 3669 goto extend; 3670 VIsual.lnum = start_lnum; 3671 VIsual_mode = 'V'; 3672 redraw_curbuf_later(INVERTED); /* update the inversion */ 3673 showmode(); 3674 } 3675 else 3676 #endif 3677 { 3678 oap->start.lnum = start_lnum; 3679 oap->start.col = 0; 3680 oap->motion_type = MLINE; 3681 } 3682 curwin->w_cursor.lnum = end_lnum; 3683 curwin->w_cursor.col = 0; 3684 3685 return OK; 3686 } 3687 3688 static int find_next_quote __ARGS((char_u *top_ptr, int col, int quotechar, char_u *escape)); 3689 static int find_prev_quote __ARGS((char_u *line, int col_start, int quotechar, char_u *escape)); 3690 3691 /* 3692 * Search quote char from string line[col]. 3693 * Quote character escaped by one of the characters in "escape" is not counted 3694 * as a quote. 3695 * Returns column number of "quotechar" or -1 when not found. 3696 */ 3697 static int 3698 find_next_quote(line, col, quotechar, escape) 3699 char_u *line; 3700 int col; 3701 int quotechar; 3702 char_u *escape; /* escape characters, can be NULL */ 3703 { 3704 int c; 3705 3706 while (1) 3707 { 3708 c = line[col]; 3709 if (c == NUL) 3710 return -1; 3711 else if (escape != NULL && vim_strchr(escape, c)) 3712 ++col; 3713 else if (c == quotechar) 3714 break; 3715 #ifdef FEAT_MBYTE 3716 if (has_mbyte) 3717 col += (*mb_ptr2len_check)(line + col); 3718 else 3719 #endif 3720 ++col; 3721 } 3722 return col; 3723 } 3724 3725 /* 3726 * Search backwards in "line" from column "col_start" to find "quotechar". 3727 * Quote character escaped by one of the characters in "escape" is not counted 3728 * as a quote. 3729 * Return the found column or zero. 3730 */ 3731 static int 3732 find_prev_quote(line, col_start, quotechar, escape) 3733 char_u *line; 3734 int col_start; 3735 int quotechar; 3736 char_u *escape; /* escape characters, can be NULL */ 3737 { 3738 int n; 3739 3740 while (col_start > 0) 3741 { 3742 --col_start; 3743 #ifdef FEAT_MBYTE 3744 col_start -= (*mb_head_off)(line, line + col_start); 3745 #endif 3746 n = 0; 3747 if (escape != NULL) 3748 while (col_start - n > 0 && vim_strchr(escape, 3749 line[col_start - n - 1]) != NULL) 3750 ++n; 3751 if (n & 1) 3752 col_start -= n; /* uneven number of escape chars, skip it */ 3753 else if (line[col_start] == quotechar) 3754 break; 3755 } 3756 return col_start; 3757 } 3758 3759 /* 3760 * Find quote under the cursor, cursor at end. 3761 * Returns TRUE if found, else FALSE. 3762 */ 3763 /*ARGSUSED*/ 3764 int 3765 current_quote(oap, count, include, quotechar) 3766 oparg_T *oap; 3767 long count; /* not used */ 3768 int include; /* TRUE == include quote char */ 3769 int quotechar; /* Quote character */ 3770 { 3771 char_u *line = ml_get_curline(); 3772 int col_end; 3773 int col_start = curwin->w_cursor.col; 3774 int inclusive = FALSE; 3775 #ifdef FEAT_VISUAL 3776 int vis_empty = TRUE; /* Visual selection <= 1 char */ 3777 int vis_bef_curs = FALSE; /* Visual starts before cursor */ 3778 3779 /* Correct cursor when 'selection' is exclusive */ 3780 if (VIsual_active) 3781 { 3782 if (*p_sel == 'e' && vis_bef_curs) 3783 dec_cursor(); 3784 vis_empty = equalpos(VIsual, curwin->w_cursor); 3785 vis_bef_curs = lt(VIsual, curwin->w_cursor); 3786 } 3787 if (!vis_empty && line[col_start] == quotechar) 3788 { 3789 /* Already selecting something and on a quote character. Find the 3790 * next quoted string. */ 3791 if (vis_bef_curs) 3792 { 3793 /* Assume we are on a closing quote: move to after the next 3794 * opening quote. */ 3795 col_start = find_next_quote(line, col_start + 1, quotechar, NULL); 3796 if (col_start < 0) 3797 return FALSE; 3798 col_end = find_next_quote(line, col_start + 1, quotechar, 3799 curbuf->b_p_qe); 3800 if (col_end < 0) 3801 { 3802 /* We were on a starting quote perhaps? */ 3803 col_end = col_start; 3804 col_start = curwin->w_cursor.col; 3805 } 3806 } 3807 else 3808 { 3809 col_end = find_prev_quote(line, col_start, quotechar, NULL); 3810 if (line[col_end] != quotechar) 3811 return FALSE; 3812 col_start = find_prev_quote(line, col_end, quotechar, 3813 curbuf->b_p_qe); 3814 if (line[col_start] != quotechar) 3815 { 3816 /* We were on an ending quote perhaps? */ 3817 col_start = col_end; 3818 col_end = curwin->w_cursor.col; 3819 } 3820 } 3821 } 3822 else 3823 #endif 3824 3825 if (line[col_start] == quotechar 3826 #ifdef FEAT_VISUAL 3827 || !vis_empty 3828 #endif 3829 ) 3830 { 3831 int first_col = col_start; 3832 3833 #ifdef FEAT_VISUAL 3834 if (!vis_empty) 3835 { 3836 if (vis_bef_curs) 3837 first_col = find_next_quote(line, col_start, quotechar, NULL); 3838 else 3839 first_col = find_prev_quote(line, col_start, quotechar, NULL); 3840 } 3841 #endif 3842 /* The cursor is on a quote, we don't know if it's the opening or 3843 * closing quote. Search from the start of the line to find out. 3844 * Also do this when there is a Visual area, a' may leave the cursor 3845 * in between two strings. */ 3846 col_start = 0; 3847 while (1) 3848 { 3849 /* Find open quote character. */ 3850 col_start = find_next_quote(line, col_start, quotechar, NULL); 3851 if (col_start < 0 || col_start > first_col) 3852 return FALSE; 3853 /* Find close quote character. */ 3854 col_end = find_next_quote(line, col_start + 1, quotechar, 3855 curbuf->b_p_qe); 3856 if (col_end < 0) 3857 return FALSE; 3858 /* If is cursor between start and end quote character, it is 3859 * target text object. */ 3860 if (col_start <= first_col && first_col <= col_end) 3861 break; 3862 col_start = col_end + 1; 3863 } 3864 } 3865 else 3866 { 3867 /* Search backward for a starting quote. */ 3868 col_start = find_prev_quote(line, col_start, quotechar, curbuf->b_p_qe); 3869 if (line[col_start] != quotechar) 3870 { 3871 /* No quote before the cursor, look after the cursor. */ 3872 col_start = find_next_quote(line, col_start, quotechar, NULL); 3873 if (col_start < 0) 3874 return FALSE; 3875 } 3876 3877 /* Find close quote character. */ 3878 col_end = find_next_quote(line, col_start + 1, quotechar, 3879 curbuf->b_p_qe); 3880 if (col_end < 0) 3881 return FALSE; 3882 } 3883 3884 /* When "include" is TRUE, include spaces after closing quote or before 3885 * the starting quote. */ 3886 if (include) 3887 { 3888 if (vim_iswhite(line[col_end + 1])) 3889 while (vim_iswhite(line[col_end + 1])) 3890 ++col_end; 3891 else 3892 while (col_start > 0 && vim_iswhite(line[col_start - 1])) 3893 --col_start; 3894 } 3895 3896 /* Set start position */ 3897 if (!include) 3898 ++col_start; 3899 curwin->w_cursor.col = col_start; 3900 #ifdef FEAT_VISUAL 3901 if (VIsual_active) 3902 { 3903 if (vis_empty) 3904 { 3905 VIsual = curwin->w_cursor; 3906 redraw_curbuf_later(INVERTED); 3907 } 3908 } 3909 else 3910 #endif 3911 { 3912 oap->start = curwin->w_cursor; 3913 oap->motion_type = MCHAR; 3914 } 3915 3916 /* Set end position. */ 3917 curwin->w_cursor.col = col_end; 3918 if (include && inc_cursor() == 2) 3919 inclusive = TRUE; 3920 #ifdef FEAT_VISUAL 3921 if (VIsual_active) 3922 { 3923 if (vis_empty || vis_bef_curs) 3924 { 3925 /* decrement cursor when 'selection' is not exclusive */ 3926 if (*p_sel != 'e') 3927 dec_cursor(); 3928 } 3929 else 3930 { 3931 /* Cursor is at start of Visual area. */ 3932 curwin->w_cursor.col = col_start; 3933 } 3934 if (VIsual_mode == 'V') 3935 { 3936 VIsual_mode = 'v'; 3937 redraw_cmdline = TRUE; /* show mode later */ 3938 } 3939 } 3940 else 3941 #endif 3942 { 3943 /* Set inclusive and other oap's flags. */ 3944 oap->inclusive = inclusive; 3945 } 3946 3947 return OK; 3948 } 3949 3950 #endif /* FEAT_TEXTOBJ */ 3951 3952 #if defined(FEAT_LISP) || defined(FEAT_CINDENT) || defined(FEAT_TEXTOBJ) \ 3953 || defined(PROTO) 3954 /* 3955 * return TRUE if line 'lnum' is empty or has white chars only. 3956 */ 3957 int 3958 linewhite(lnum) 3959 linenr_T lnum; 3960 { 3961 char_u *p; 3962 3963 p = skipwhite(ml_get(lnum)); 3964 return (*p == NUL); 3965 } 3966 #endif 3967 3968 #if defined(FEAT_FIND_ID) || defined(PROTO) 3969 /* 3970 * Find identifiers or defines in included files. 3971 * if p_ic && (continue_status & CONT_SOL) then ptr must be in lowercase. 3972 */ 3973 /*ARGSUSED*/ 3974 void 3975 find_pattern_in_path(ptr, dir, len, whole, skip_comments, 3976 type, count, action, start_lnum, end_lnum) 3977 char_u *ptr; /* pointer to search pattern */ 3978 int dir; /* direction of expansion */ 3979 int len; /* length of search pattern */ 3980 int whole; /* match whole words only */ 3981 int skip_comments; /* don't match inside comments */ 3982 int type; /* Type of search; are we looking for a type? 3983 a macro? */ 3984 long count; 3985 int action; /* What to do when we find it */ 3986 linenr_T start_lnum; /* first line to start searching */ 3987 linenr_T end_lnum; /* last line for searching */ 3988 { 3989 SearchedFile *files; /* Stack of included files */ 3990 SearchedFile *bigger; /* When we need more space */ 3991 int max_path_depth = 50; 3992 long match_count = 1; 3993 3994 char_u *pat; 3995 char_u *new_fname; 3996 char_u *curr_fname = curbuf->b_fname; 3997 char_u *prev_fname = NULL; 3998 linenr_T lnum; 3999 int depth; 4000 int depth_displayed; /* For type==CHECK_PATH */ 4001 int old_files; 4002 int already_searched; 4003 char_u *file_line; 4004 char_u *line; 4005 char_u *p; 4006 char_u save_char; 4007 int define_matched; 4008 regmatch_T regmatch; 4009 regmatch_T incl_regmatch; 4010 regmatch_T def_regmatch; 4011 int matched = FALSE; 4012 int did_show = FALSE; 4013 int found = FALSE; 4014 int i; 4015 char_u *already = NULL; 4016 char_u *startp = NULL; 4017 #ifdef RISCOS 4018 int previous_munging = __riscosify_control; 4019 #endif 4020 #if defined(FEAT_WINDOWS) && defined(FEAT_QUICKFIX) 4021 win_T *curwin_save = NULL; 4022 #endif 4023 4024 regmatch.regprog = NULL; 4025 incl_regmatch.regprog = NULL; 4026 def_regmatch.regprog = NULL; 4027 4028 file_line = alloc(LSIZE); 4029 if (file_line == NULL) 4030 return; 4031 4032 #ifdef RISCOS 4033 /* UnixLib knows best how to munge c file names - turn munging back on. */ 4034 __riscosify_control = __RISCOSIFY_LONG_TRUNCATE; 4035 #endif 4036 4037 if (type != CHECK_PATH && type != FIND_DEFINE 4038 #ifdef FEAT_INS_EXPAND 4039 /* when CONT_SOL is set compare "ptr" with the beginning of the line 4040 * is faster than quote_meta/regcomp/regexec "ptr" -- Acevedo */ 4041 && !(continue_status & CONT_SOL) 4042 #endif 4043 ) 4044 { 4045 pat = alloc(len + 5); 4046 if (pat == NULL) 4047 goto fpip_end; 4048 sprintf((char *)pat, whole ? "\\<%.*s\\>" : "%.*s", len, ptr); 4049 /* ignore case according to p_ic, p_scs and pat */ 4050 regmatch.rm_ic = ignorecase(pat); 4051 regmatch.regprog = vim_regcomp(pat, p_magic ? RE_MAGIC : 0); 4052 vim_free(pat); 4053 if (regmatch.regprog == NULL) 4054 goto fpip_end; 4055 } 4056 if (*curbuf->b_p_inc != NUL || *p_inc != NUL) 4057 { 4058 incl_regmatch.regprog = vim_regcomp(*curbuf->b_p_inc == NUL 4059 ? p_inc : curbuf->b_p_inc, p_magic ? RE_MAGIC : 0); 4060 if (incl_regmatch.regprog == NULL) 4061 goto fpip_end; 4062 incl_regmatch.rm_ic = FALSE; /* don't ignore case in incl. pat. */ 4063 } 4064 if (type == FIND_DEFINE && (*curbuf->b_p_def != NUL || *p_def != NUL)) 4065 { 4066 def_regmatch.regprog = vim_regcomp(*curbuf->b_p_def == NUL 4067 ? p_def : curbuf->b_p_def, p_magic ? RE_MAGIC : 0); 4068 if (def_regmatch.regprog == NULL) 4069 goto fpip_end; 4070 def_regmatch.rm_ic = FALSE; /* don't ignore case in define pat. */ 4071 } 4072 files = (SearchedFile *)lalloc_clear((long_u) 4073 (max_path_depth * sizeof(SearchedFile)), TRUE); 4074 if (files == NULL) 4075 goto fpip_end; 4076 old_files = max_path_depth; 4077 depth = depth_displayed = -1; 4078 4079 lnum = start_lnum; 4080 if (end_lnum > curbuf->b_ml.ml_line_count) 4081 end_lnum = curbuf->b_ml.ml_line_count; 4082 if (lnum > end_lnum) /* do at least one line */ 4083 lnum = end_lnum; 4084 line = ml_get(lnum); 4085 4086 for (;;) 4087 { 4088 if (incl_regmatch.regprog != NULL 4089 && vim_regexec(&incl_regmatch, line, (colnr_T)0)) 4090 { 4091 new_fname = file_name_in_line(incl_regmatch.endp[0], 4092 0, FNAME_EXP|FNAME_INCL|FNAME_REL, 1L, 4093 curr_fname == curbuf->b_fname 4094 ? curbuf->b_ffname : curr_fname); 4095 already_searched = FALSE; 4096 if (new_fname != NULL) 4097 { 4098 /* Check whether we have already searched in this file */ 4099 for (i = 0;; i++) 4100 { 4101 if (i == depth + 1) 4102 i = old_files; 4103 if (i == max_path_depth) 4104 break; 4105 if (fullpathcmp(new_fname, files[i].name, TRUE) & FPC_SAME) 4106 { 4107 if (type != CHECK_PATH && 4108 action == ACTION_SHOW_ALL && files[i].matched) 4109 { 4110 msg_putchar('\n'); /* cursor below last one */ 4111 if (!got_int) /* don't display if 'q' 4112 typed at "--more--" 4113 mesage */ 4114 { 4115 msg_home_replace_hl(new_fname); 4116 MSG_PUTS(_(" (includes previously listed match)")); 4117 prev_fname = NULL; 4118 } 4119 } 4120 vim_free(new_fname); 4121 new_fname = NULL; 4122 already_searched = TRUE; 4123 break; 4124 } 4125 } 4126 } 4127 4128 if (type == CHECK_PATH && (action == ACTION_SHOW_ALL 4129 || (new_fname == NULL && !already_searched))) 4130 { 4131 if (did_show) 4132 msg_putchar('\n'); /* cursor below last one */ 4133 else 4134 { 4135 gotocmdline(TRUE); /* cursor at status line */ 4136 MSG_PUTS_TITLE(_("--- Included files ")); 4137 if (action != ACTION_SHOW_ALL) 4138 MSG_PUTS_TITLE(_("not found ")); 4139 MSG_PUTS_TITLE(_("in path ---\n")); 4140 } 4141 did_show = TRUE; 4142 while (depth_displayed < depth && !got_int) 4143 { 4144 ++depth_displayed; 4145 for (i = 0; i < depth_displayed; i++) 4146 MSG_PUTS(" "); 4147 msg_home_replace(files[depth_displayed].name); 4148 MSG_PUTS(" -->\n"); 4149 } 4150 if (!got_int) /* don't display if 'q' typed 4151 for "--more--" message */ 4152 { 4153 for (i = 0; i <= depth_displayed; i++) 4154 MSG_PUTS(" "); 4155 if (new_fname != NULL) 4156 { 4157 /* using "new_fname" is more reliable, e.g., when 4158 * 'includeexpr' is set. */ 4159 msg_outtrans_attr(new_fname, hl_attr(HLF_D)); 4160 } 4161 else 4162 { 4163 /* 4164 * Isolate the file name. 4165 * Include the surrounding "" or <> if present. 4166 */ 4167 for (p = incl_regmatch.endp[0]; !vim_isfilec(*p); p++) 4168 ; 4169 for (i = 0; vim_isfilec(p[i]); i++) 4170 ; 4171 if (i == 0) 4172 { 4173 /* Nothing found, use the rest of the line. */ 4174 p = incl_regmatch.endp[0]; 4175 i = STRLEN(p); 4176 } 4177 else 4178 { 4179 if (p[-1] == '"' || p[-1] == '<') 4180 { 4181 --p; 4182 ++i; 4183 } 4184 if (p[i] == '"' || p[i] == '>') 4185 ++i; 4186 } 4187 save_char = p[i]; 4188 p[i] = NUL; 4189 msg_outtrans_attr(p, hl_attr(HLF_D)); 4190 p[i] = save_char; 4191 } 4192 4193 if (new_fname == NULL && action == ACTION_SHOW_ALL) 4194 { 4195 if (already_searched) 4196 MSG_PUTS(_(" (Already listed)")); 4197 else 4198 MSG_PUTS(_(" NOT FOUND")); 4199 } 4200 } 4201 out_flush(); /* output each line directly */ 4202 } 4203 4204 if (new_fname != NULL) 4205 { 4206 /* Push the new file onto the file stack */ 4207 if (depth + 1 == old_files) 4208 { 4209 bigger = (SearchedFile *)lalloc((long_u)( 4210 max_path_depth * 2 * sizeof(SearchedFile)), TRUE); 4211 if (bigger != NULL) 4212 { 4213 for (i = 0; i <= depth; i++) 4214 bigger[i] = files[i]; 4215 for (i = depth + 1; i < old_files + max_path_depth; i++) 4216 { 4217 bigger[i].fp = NULL; 4218 bigger[i].name = NULL; 4219 bigger[i].lnum = 0; 4220 bigger[i].matched = FALSE; 4221 } 4222 for (i = old_files; i < max_path_depth; i++) 4223 bigger[i + max_path_depth] = files[i]; 4224 old_files += max_path_depth; 4225 max_path_depth *= 2; 4226 vim_free(files); 4227 files = bigger; 4228 } 4229 } 4230 if ((files[depth + 1].fp = mch_fopen((char *)new_fname, "r")) 4231 == NULL) 4232 vim_free(new_fname); 4233 else 4234 { 4235 if (++depth == old_files) 4236 { 4237 /* 4238 * lalloc() for 'bigger' must have failed above. We 4239 * will forget one of our already visited files now. 4240 */ 4241 vim_free(files[old_files].name); 4242 ++old_files; 4243 } 4244 files[depth].name = curr_fname = new_fname; 4245 files[depth].lnum = 0; 4246 files[depth].matched = FALSE; 4247 #ifdef FEAT_INS_EXPAND 4248 if (action == ACTION_EXPAND) 4249 { 4250 sprintf((char*)IObuff, _("Scanning included file: %s"), 4251 (char *)new_fname); 4252 msg_trunc_attr(IObuff, TRUE, hl_attr(HLF_R)); 4253 } 4254 #endif 4255 } 4256 } 4257 } 4258 else 4259 { 4260 /* 4261 * Check if the line is a define (type == FIND_DEFINE) 4262 */ 4263 p = line; 4264 search_line: 4265 define_matched = FALSE; 4266 if (def_regmatch.regprog != NULL 4267 && vim_regexec(&def_regmatch, line, (colnr_T)0)) 4268 { 4269 /* 4270 * Pattern must be first identifier after 'define', so skip 4271 * to that position before checking for match of pattern. Also 4272 * don't let it match beyond the end of this identifier. 4273 */ 4274 p = def_regmatch.endp[0]; 4275 while (*p && !vim_iswordc(*p)) 4276 p++; 4277 define_matched = TRUE; 4278 } 4279 4280 /* 4281 * Look for a match. Don't do this if we are looking for a 4282 * define and this line didn't match define_prog above. 4283 */ 4284 if (def_regmatch.regprog == NULL || define_matched) 4285 { 4286 if (define_matched 4287 #ifdef FEAT_INS_EXPAND 4288 || (continue_status & CONT_SOL) 4289 #endif 4290 ) 4291 { 4292 /* compare the first "len" chars from "ptr" */ 4293 startp = skipwhite(p); 4294 if (p_ic) 4295 matched = !MB_STRNICMP(startp, ptr, len); 4296 else 4297 matched = !STRNCMP(startp, ptr, len); 4298 if (matched && define_matched && whole 4299 && vim_iswordc(startp[len])) 4300 matched = FALSE; 4301 } 4302 else if (regmatch.regprog != NULL 4303 && vim_regexec(®match, line, (colnr_T)(p - line))) 4304 { 4305 matched = TRUE; 4306 startp = regmatch.startp[0]; 4307 /* 4308 * Check if the line is not a comment line (unless we are 4309 * looking for a define). A line starting with "# define" 4310 * is not considered to be a comment line. 4311 */ 4312 if (!define_matched && skip_comments) 4313 { 4314 #ifdef FEAT_COMMENTS 4315 if ((*line != '#' || 4316 STRNCMP(skipwhite(line + 1), "define", 6) != 0) 4317 && get_leader_len(line, NULL, FALSE)) 4318 matched = FALSE; 4319 4320 /* 4321 * Also check for a "/ *" or "/ /" before the match. 4322 * Skips lines like "int backwards; / * normal index 4323 * * /" when looking for "normal". 4324 * Note: Doesn't skip "/ *" in comments. 4325 */ 4326 p = skipwhite(line); 4327 if (matched 4328 || (p[0] == '/' && p[1] == '*') || p[0] == '*') 4329 #endif 4330 for (p = line; *p && p < startp; ++p) 4331 { 4332 if (matched 4333 && p[0] == '/' 4334 && (p[1] == '*' || p[1] == '/')) 4335 { 4336 matched = FALSE; 4337 /* After "//" all text is comment */ 4338 if (p[1] == '/') 4339 break; 4340 ++p; 4341 } 4342 else if (!matched && p[0] == '*' && p[1] == '/') 4343 { 4344 /* Can find match after "* /". */ 4345 matched = TRUE; 4346 ++p; 4347 } 4348 } 4349 } 4350 } 4351 } 4352 } 4353 if (matched) 4354 { 4355 #ifdef FEAT_INS_EXPAND 4356 if (action == ACTION_EXPAND) 4357 { 4358 int reuse = 0; 4359 int add_r; 4360 char_u *aux; 4361 4362 if (depth == -1 && lnum == curwin->w_cursor.lnum) 4363 break; 4364 found = TRUE; 4365 aux = p = startp; 4366 if (continue_status & CONT_ADDING) 4367 { 4368 p += completion_length; 4369 if (vim_iswordp(p)) 4370 goto exit_matched; 4371 p = find_word_start(p); 4372 } 4373 p = find_word_end(p); 4374 i = (int)(p - aux); 4375 4376 if ((continue_status & CONT_ADDING) && i == completion_length) 4377 { 4378 /* get the next line */ 4379 /* IOSIZE > completion_length, so the STRNCPY works */ 4380 STRNCPY(IObuff, aux, i); 4381 if (!( depth < 0 4382 && lnum < end_lnum 4383 && (line = ml_get(++lnum)) != NULL) 4384 && !( depth >= 0 4385 && !vim_fgets(line = file_line, 4386 LSIZE, files[depth].fp))) 4387 goto exit_matched; 4388 4389 /* we read a line, set "already" to check this "line" later 4390 * if depth >= 0 we'll increase files[depth].lnum far 4391 * bellow -- Acevedo */ 4392 already = aux = p = skipwhite(line); 4393 p = find_word_start(p); 4394 p = find_word_end(p); 4395 if (p > aux) 4396 { 4397 if (*aux != ')' && IObuff[i-1] != TAB) 4398 { 4399 if (IObuff[i-1] != ' ') 4400 IObuff[i++] = ' '; 4401 /* IObuf =~ "\(\k\|\i\).* ", thus i >= 2*/ 4402 if (p_js 4403 && (IObuff[i-2] == '.' 4404 || (vim_strchr(p_cpo, CPO_JOINSP) == NULL 4405 && (IObuff[i-2] == '?' 4406 || IObuff[i-2] == '!')))) 4407 IObuff[i++] = ' '; 4408 } 4409 /* copy as much as posible of the new word */ 4410 if (p - aux >= IOSIZE - i) 4411 p = aux + IOSIZE - i - 1; 4412 STRNCPY(IObuff + i, aux, p - aux); 4413 i += (int)(p - aux); 4414 reuse |= CONT_S_IPOS; 4415 } 4416 IObuff[i] = NUL; 4417 aux = IObuff; 4418 4419 if (i == completion_length) 4420 goto exit_matched; 4421 } 4422 4423 add_r = ins_compl_add_infercase(aux, i, 4424 curr_fname == curbuf->b_fname ? NULL : curr_fname, 4425 dir, reuse); 4426 if (add_r == OK) 4427 /* if dir was BACKWARD then honor it just once */ 4428 dir = FORWARD; 4429 else if (add_r == RET_ERROR) 4430 break; 4431 } 4432 else 4433 #endif 4434 if (action == ACTION_SHOW_ALL) 4435 { 4436 found = TRUE; 4437 if (!did_show) 4438 gotocmdline(TRUE); /* cursor at status line */ 4439 if (curr_fname != prev_fname) 4440 { 4441 if (did_show) 4442 msg_putchar('\n'); /* cursor below last one */ 4443 if (!got_int) /* don't display if 'q' typed 4444 at "--more--" mesage */ 4445 msg_home_replace_hl(curr_fname); 4446 prev_fname = curr_fname; 4447 } 4448 did_show = TRUE; 4449 if (!got_int) 4450 show_pat_in_path(line, type, TRUE, action, 4451 (depth == -1) ? NULL : files[depth].fp, 4452 (depth == -1) ? &lnum : &files[depth].lnum, 4453 match_count++); 4454 4455 /* Set matched flag for this file and all the ones that 4456 * include it */ 4457 for (i = 0; i <= depth; ++i) 4458 files[i].matched = TRUE; 4459 } 4460 else if (--count <= 0) 4461 { 4462 found = TRUE; 4463 if (depth == -1 && lnum == curwin->w_cursor.lnum 4464 #if defined(FEAT_WINDOWS) && defined(FEAT_QUICKFIX) 4465 && g_do_tagpreview == 0 4466 #endif 4467 ) 4468 EMSG(_("E387: Match is on current line")); 4469 else if (action == ACTION_SHOW) 4470 { 4471 show_pat_in_path(line, type, did_show, action, 4472 (depth == -1) ? NULL : files[depth].fp, 4473 (depth == -1) ? &lnum : &files[depth].lnum, 1L); 4474 did_show = TRUE; 4475 } 4476 else 4477 { 4478 #ifdef FEAT_GUI 4479 need_mouse_correct = TRUE; 4480 #endif 4481 #if defined(FEAT_WINDOWS) && defined(FEAT_QUICKFIX) 4482 /* ":psearch" uses the preview window */ 4483 if (g_do_tagpreview != 0) 4484 { 4485 curwin_save = curwin; 4486 prepare_tagpreview(); 4487 } 4488 #endif 4489 if (action == ACTION_SPLIT) 4490 { 4491 #ifdef FEAT_WINDOWS 4492 if (win_split(0, 0) == FAIL) 4493 #endif 4494 break; 4495 #ifdef FEAT_SCROLLBIND 4496 curwin->w_p_scb = FALSE; 4497 #endif 4498 } 4499 if (depth == -1) 4500 { 4501 /* match in current file */ 4502 #if defined(FEAT_WINDOWS) && defined(FEAT_QUICKFIX) 4503 if (g_do_tagpreview != 0) 4504 { 4505 if (getfile(0, curwin_save->w_buffer->b_fname, 4506 NULL, TRUE, lnum, FALSE) > 0) 4507 break; /* failed to jump to file */ 4508 } 4509 else 4510 #endif 4511 setpcmark(); 4512 curwin->w_cursor.lnum = lnum; 4513 } 4514 else 4515 { 4516 if (getfile(0, files[depth].name, NULL, TRUE, 4517 files[depth].lnum, FALSE) > 0) 4518 break; /* failed to jump to file */ 4519 /* autocommands may have changed the lnum, we don't 4520 * want that here */ 4521 curwin->w_cursor.lnum = files[depth].lnum; 4522 } 4523 } 4524 if (action != ACTION_SHOW) 4525 { 4526 curwin->w_cursor.col = (colnr_T) (startp - line); 4527 curwin->w_set_curswant = TRUE; 4528 } 4529 4530 #if defined(FEAT_WINDOWS) && defined(FEAT_QUICKFIX) 4531 if (g_do_tagpreview != 0 4532 && curwin != curwin_save && win_valid(curwin_save)) 4533 { 4534 /* Return cursor to where we were */ 4535 validate_cursor(); 4536 redraw_later(VALID); 4537 win_enter(curwin_save, TRUE); 4538 } 4539 #endif 4540 break; 4541 } 4542 #ifdef FEAT_INS_EXPAND 4543 exit_matched: 4544 #endif 4545 matched = FALSE; 4546 /* look for other matches in the rest of the line if we 4547 * are not at the end of it already */ 4548 if (def_regmatch.regprog == NULL 4549 #ifdef FEAT_INS_EXPAND 4550 && action == ACTION_EXPAND 4551 && !(continue_status & CONT_SOL) 4552 #endif 4553 && *(p = startp + 1)) 4554 goto search_line; 4555 } 4556 line_breakcheck(); 4557 #ifdef FEAT_INS_EXPAND 4558 if (action == ACTION_EXPAND) 4559 ins_compl_check_keys(); 4560 if (got_int || completion_interrupted) 4561 #else 4562 if (got_int) 4563 #endif 4564 break; 4565 4566 /* 4567 * Read the next line. When reading an included file and encountering 4568 * end-of-file, close the file and continue in the file that included 4569 * it. 4570 */ 4571 while (depth >= 0 && !already 4572 && vim_fgets(line = file_line, LSIZE, files[depth].fp)) 4573 { 4574 fclose(files[depth].fp); 4575 --old_files; 4576 files[old_files].name = files[depth].name; 4577 files[old_files].matched = files[depth].matched; 4578 --depth; 4579 curr_fname = (depth == -1) ? curbuf->b_fname 4580 : files[depth].name; 4581 if (depth < depth_displayed) 4582 depth_displayed = depth; 4583 } 4584 if (depth >= 0) /* we could read the line */ 4585 files[depth].lnum++; 4586 else if (!already) 4587 { 4588 if (++lnum > end_lnum) 4589 break; 4590 line = ml_get(lnum); 4591 } 4592 already = NULL; 4593 } 4594 /* End of big for (;;) loop. */ 4595 4596 /* Close any files that are still open. */ 4597 for (i = 0; i <= depth; i++) 4598 { 4599 fclose(files[i].fp); 4600 vim_free(files[i].name); 4601 } 4602 for (i = old_files; i < max_path_depth; i++) 4603 vim_free(files[i].name); 4604 vim_free(files); 4605 4606 if (type == CHECK_PATH) 4607 { 4608 if (!did_show) 4609 { 4610 if (action != ACTION_SHOW_ALL) 4611 MSG(_("All included files were found")); 4612 else 4613 MSG(_("No included files")); 4614 } 4615 } 4616 else if (!found 4617 #ifdef FEAT_INS_EXPAND 4618 && action != ACTION_EXPAND 4619 #endif 4620 ) 4621 { 4622 #ifdef FEAT_INS_EXPAND 4623 if (got_int || completion_interrupted) 4624 #else 4625 if (got_int) 4626 #endif 4627 EMSG(_(e_interr)); 4628 else if (type == FIND_DEFINE) 4629 EMSG(_("E388: Couldn't find definition")); 4630 else 4631 EMSG(_("E389: Couldn't find pattern")); 4632 } 4633 if (action == ACTION_SHOW || action == ACTION_SHOW_ALL) 4634 msg_end(); 4635 4636 fpip_end: 4637 vim_free(file_line); 4638 vim_free(regmatch.regprog); 4639 vim_free(incl_regmatch.regprog); 4640 vim_free(def_regmatch.regprog); 4641 4642 #ifdef RISCOS 4643 /* Restore previous file munging state. */ 4644 __riscosify_control = previous_munging; 4645 #endif 4646 } 4647 4648 static void 4649 show_pat_in_path(line, type, did_show, action, fp, lnum, count) 4650 char_u *line; 4651 int type; 4652 int did_show; 4653 int action; 4654 FILE *fp; 4655 linenr_T *lnum; 4656 long count; 4657 { 4658 char_u *p; 4659 4660 if (did_show) 4661 msg_putchar('\n'); /* cursor below last one */ 4662 else 4663 gotocmdline(TRUE); /* cursor at status line */ 4664 if (got_int) /* 'q' typed at "--more--" message */ 4665 return; 4666 for (;;) 4667 { 4668 p = line + STRLEN(line) - 1; 4669 if (fp != NULL) 4670 { 4671 /* We used fgets(), so get rid of newline at end */ 4672 if (p >= line && *p == '\n') 4673 --p; 4674 if (p >= line && *p == '\r') 4675 --p; 4676 *(p + 1) = NUL; 4677 } 4678 if (action == ACTION_SHOW_ALL) 4679 { 4680 sprintf((char *)IObuff, "%3ld: ", count); /* show match nr */ 4681 msg_puts(IObuff); 4682 sprintf((char *)IObuff, "%4ld", *lnum); /* show line nr */ 4683 /* Highlight line numbers */ 4684 msg_puts_attr(IObuff, hl_attr(HLF_N)); 4685 MSG_PUTS(" "); 4686 } 4687 msg_prt_line(line); 4688 out_flush(); /* show one line at a time */ 4689 4690 /* Definition continues until line that doesn't end with '\' */ 4691 if (got_int || type != FIND_DEFINE || p < line || *p != '\\') 4692 break; 4693 4694 if (fp != NULL) 4695 { 4696 if (vim_fgets(line, LSIZE, fp)) /* end of file */ 4697 break; 4698 ++*lnum; 4699 } 4700 else 4701 { 4702 if (++*lnum > curbuf->b_ml.ml_line_count) 4703 break; 4704 line = ml_get(*lnum); 4705 } 4706 msg_putchar('\n'); 4707 } 4708 } 4709 #endif 4710 4711 #ifdef FEAT_VIMINFO 4712 int 4713 read_viminfo_search_pattern(virp, force) 4714 vir_T *virp; 4715 int force; 4716 { 4717 char_u *lp; 4718 int idx = -1; 4719 int magic = FALSE; 4720 int no_scs = FALSE; 4721 int off_line = FALSE; 4722 int off_end = FALSE; 4723 long off = 0; 4724 int setlast = FALSE; 4725 #ifdef FEAT_SEARCH_EXTRA 4726 static int hlsearch_on = FALSE; 4727 #endif 4728 char_u *val; 4729 4730 /* 4731 * Old line types: 4732 * "/pat", "&pat": search/subst. pat 4733 * "~/pat", "~&pat": last used search/subst. pat 4734 * New line types: 4735 * "~h", "~H": hlsearch highlighting off/on 4736 * "~<magic><smartcase><line><end><off><last><which>pat" 4737 * <magic>: 'm' off, 'M' on 4738 * <smartcase>: 's' off, 'S' on 4739 * <line>: 'L' line offset, 'l' char offset 4740 * <end>: 'E' from end, 'e' from start 4741 * <off>: decimal, offset 4742 * <last>: '~' last used pattern 4743 * <which>: '/' search pat, '&' subst. pat 4744 */ 4745 lp = virp->vir_line; 4746 if (lp[0] == '~' && (lp[1] == 'm' || lp[1] == 'M')) /* new line type */ 4747 { 4748 if (lp[1] == 'M') /* magic on */ 4749 magic = TRUE; 4750 if (lp[2] == 's') 4751 no_scs = TRUE; 4752 if (lp[3] == 'L') 4753 off_line = TRUE; 4754 if (lp[4] == 'E') 4755 off_end = SEARCH_END; 4756 lp += 5; 4757 off = getdigits(&lp); 4758 } 4759 if (lp[0] == '~') /* use this pattern for last-used pattern */ 4760 { 4761 setlast = TRUE; 4762 lp++; 4763 } 4764 if (lp[0] == '/') 4765 idx = RE_SEARCH; 4766 else if (lp[0] == '&') 4767 idx = RE_SUBST; 4768 #ifdef FEAT_SEARCH_EXTRA 4769 else if (lp[0] == 'h') /* ~h: 'hlsearch' highlighting off */ 4770 hlsearch_on = FALSE; 4771 else if (lp[0] == 'H') /* ~H: 'hlsearch' highlighting on */ 4772 hlsearch_on = TRUE; 4773 #endif 4774 if (idx >= 0) 4775 { 4776 if (force || spats[idx].pat == NULL) 4777 { 4778 val = viminfo_readstring(virp, (int)(lp - virp->vir_line + 1), 4779 TRUE); 4780 if (val != NULL) 4781 { 4782 set_last_search_pat(val, idx, magic, setlast); 4783 vim_free(val); 4784 spats[idx].no_scs = no_scs; 4785 spats[idx].off.line = off_line; 4786 spats[idx].off.end = off_end; 4787 spats[idx].off.off = off; 4788 #ifdef FEAT_SEARCH_EXTRA 4789 if (setlast) 4790 no_hlsearch = !hlsearch_on; 4791 #endif 4792 } 4793 } 4794 } 4795 return viminfo_readline(virp); 4796 } 4797 4798 void 4799 write_viminfo_search_pattern(fp) 4800 FILE *fp; 4801 { 4802 if (get_viminfo_parameter('/') != 0) 4803 { 4804 #ifdef FEAT_SEARCH_EXTRA 4805 fprintf(fp, "\n# hlsearch on (H) or off (h):\n~%c", 4806 (no_hlsearch || find_viminfo_parameter('h') != NULL) ? 'h' : 'H'); 4807 #endif 4808 wvsp_one(fp, RE_SEARCH, "", '/'); 4809 wvsp_one(fp, RE_SUBST, "Substitute ", '&'); 4810 } 4811 } 4812 4813 static void 4814 wvsp_one(fp, idx, s, sc) 4815 FILE *fp; /* file to write to */ 4816 int idx; /* spats[] index */ 4817 char *s; /* search pat */ 4818 int sc; /* dir char */ 4819 { 4820 if (spats[idx].pat != NULL) 4821 { 4822 fprintf(fp, "\n# Last %sSearch Pattern:\n~", s); 4823 /* off.dir is not stored, it's reset to forward */ 4824 fprintf(fp, "%c%c%c%c%ld%s%c", 4825 spats[idx].magic ? 'M' : 'm', /* magic */ 4826 spats[idx].no_scs ? 's' : 'S', /* smartcase */ 4827 spats[idx].off.line ? 'L' : 'l', /* line offset */ 4828 spats[idx].off.end ? 'E' : 'e', /* offset from end */ 4829 spats[idx].off.off, /* offset */ 4830 last_idx == idx ? "~" : "", /* last used pat */ 4831 sc); 4832 viminfo_writestring(fp, spats[idx].pat); 4833 } 4834 } 4835 #endif /* FEAT_VIMINFO */ 4836