1 /* vi:set ts=8 sts=4 sw=4 noet: 2 * 3 * VIM - Vi IMproved by Bram Moolenaar 4 * 5 * Do ":help uganda" in Vim to read copying and usage conditions. 6 * Do ":help credits" in Vim to see a list of people who contributed. 7 * See README.txt for an overview of the Vim source code. 8 */ 9 /* 10 * search.c: code for normal mode searching commands 11 */ 12 13 #include "vim.h" 14 15 #ifdef FEAT_EVAL 16 static void set_vv_searchforward(void); 17 static int first_submatch(regmmatch_T *rp); 18 #endif 19 static int check_prevcol(char_u *linep, int col, int ch, int *prevcol); 20 static int inmacro(char_u *, char_u *); 21 static int check_linecomment(char_u *line); 22 static int cls(void); 23 static int skip_chars(int, int); 24 #ifdef FEAT_TEXTOBJ 25 static void back_in_line(void); 26 static void find_first_blank(pos_T *); 27 static void findsent_forward(long count, int at_start_sent); 28 #endif 29 #ifdef FEAT_FIND_ID 30 static void show_pat_in_path(char_u *, int, 31 int, int, FILE *, linenr_T *, long); 32 #endif 33 #ifdef FEAT_VIMINFO 34 static void wvsp_one(FILE *fp, int idx, char *s, int sc); 35 #endif 36 37 /* 38 * This file contains various searching-related routines. These fall into 39 * three groups: 40 * 1. string searches (for /, ?, n, and N) 41 * 2. character searches within a single line (for f, F, t, T, etc) 42 * 3. "other" kinds of searches like the '%' command, and 'word' searches. 43 */ 44 45 /* 46 * String searches 47 * 48 * The string search functions are divided into two levels: 49 * lowest: searchit(); uses an pos_T for starting position and found match. 50 * Highest: do_search(); uses curwin->w_cursor; calls searchit(). 51 * 52 * The last search pattern is remembered for repeating the same search. 53 * This pattern is shared between the :g, :s, ? and / commands. 54 * This is in search_regcomp(). 55 * 56 * The actual string matching is done using a heavily modified version of 57 * Henry Spencer's regular expression library. See regexp.c. 58 */ 59 60 /* The offset for a search command is store in a soff struct */ 61 /* Note: only spats[0].off is really used */ 62 struct soffset 63 { 64 int dir; /* search direction, '/' or '?' */ 65 int line; /* search has line offset */ 66 int end; /* search set cursor at end */ 67 long off; /* line or char offset */ 68 }; 69 70 /* A search pattern and its attributes are stored in a spat struct */ 71 struct spat 72 { 73 char_u *pat; /* the pattern (in allocated memory) or NULL */ 74 int magic; /* magicness of the pattern */ 75 int no_scs; /* no smartcase for this pattern */ 76 struct soffset off; 77 }; 78 79 /* 80 * Two search patterns are remembered: One for the :substitute command and 81 * one for other searches. last_idx points to the one that was used the last 82 * time. 83 */ 84 static struct spat spats[2] = 85 { 86 {NULL, TRUE, FALSE, {'/', 0, 0, 0L}}, /* last used search pat */ 87 {NULL, TRUE, FALSE, {'/', 0, 0, 0L}} /* last used substitute pat */ 88 }; 89 90 static int last_idx = 0; /* index in spats[] for RE_LAST */ 91 92 static char_u lastc[2] = {NUL, NUL}; /* last character searched for */ 93 static int lastcdir = FORWARD; /* last direction of character search */ 94 static int last_t_cmd = TRUE; /* last search t_cmd */ 95 #ifdef FEAT_MBYTE 96 static char_u lastc_bytes[MB_MAXBYTES + 1]; 97 static int lastc_bytelen = 1; /* >1 for multi-byte char */ 98 #endif 99 100 #if defined(FEAT_AUTOCMD) || defined(FEAT_EVAL) || defined(PROTO) 101 /* copy of spats[], for keeping the search patterns while executing autocmds */ 102 static struct spat saved_spats[2]; 103 static int saved_last_idx = 0; 104 # ifdef FEAT_SEARCH_EXTRA 105 static int saved_no_hlsearch = 0; 106 # endif 107 #endif 108 109 static char_u *mr_pattern = NULL; /* pattern used by search_regcomp() */ 110 #ifdef FEAT_RIGHTLEFT 111 static int mr_pattern_alloced = FALSE; /* mr_pattern was allocated */ 112 #endif 113 114 #ifdef FEAT_FIND_ID 115 /* 116 * Type used by find_pattern_in_path() to remember which included files have 117 * been searched already. 118 */ 119 typedef struct SearchedFile 120 { 121 FILE *fp; /* File pointer */ 122 char_u *name; /* Full name of file */ 123 linenr_T lnum; /* Line we were up to in file */ 124 int matched; /* Found a match in this file */ 125 } SearchedFile; 126 #endif 127 128 /* 129 * translate search pattern for vim_regcomp() 130 * 131 * pat_save == RE_SEARCH: save pat in spats[RE_SEARCH].pat (normal search cmd) 132 * pat_save == RE_SUBST: save pat in spats[RE_SUBST].pat (:substitute command) 133 * pat_save == RE_BOTH: save pat in both patterns (:global command) 134 * pat_use == RE_SEARCH: use previous search pattern if "pat" is NULL 135 * pat_use == RE_SUBST: use previous substitute pattern if "pat" is NULL 136 * pat_use == RE_LAST: use last used pattern if "pat" is NULL 137 * options & SEARCH_HIS: put search string in history 138 * options & SEARCH_KEEP: keep previous search pattern 139 * 140 * returns FAIL if failed, OK otherwise. 141 */ 142 int 143 search_regcomp( 144 char_u *pat, 145 int pat_save, 146 int pat_use, 147 int options, 148 regmmatch_T *regmatch) /* return: pattern and ignore-case flag */ 149 { 150 int magic; 151 int i; 152 153 rc_did_emsg = FALSE; 154 magic = p_magic; 155 156 /* 157 * If no pattern given, use a previously defined pattern. 158 */ 159 if (pat == NULL || *pat == NUL) 160 { 161 if (pat_use == RE_LAST) 162 i = last_idx; 163 else 164 i = pat_use; 165 if (spats[i].pat == NULL) /* pattern was never defined */ 166 { 167 if (pat_use == RE_SUBST) 168 EMSG(_(e_nopresub)); 169 else 170 EMSG(_(e_noprevre)); 171 rc_did_emsg = TRUE; 172 return FAIL; 173 } 174 pat = spats[i].pat; 175 magic = spats[i].magic; 176 no_smartcase = spats[i].no_scs; 177 } 178 #ifdef FEAT_CMDHIST 179 else if (options & SEARCH_HIS) /* put new pattern in history */ 180 add_to_history(HIST_SEARCH, pat, TRUE, NUL); 181 #endif 182 183 #ifdef FEAT_RIGHTLEFT 184 if (mr_pattern_alloced) 185 { 186 vim_free(mr_pattern); 187 mr_pattern_alloced = FALSE; 188 } 189 190 if (curwin->w_p_rl && *curwin->w_p_rlc == 's') 191 { 192 char_u *rev_pattern; 193 194 rev_pattern = reverse_text(pat); 195 if (rev_pattern == NULL) 196 mr_pattern = pat; /* out of memory, keep normal pattern. */ 197 else 198 { 199 mr_pattern = rev_pattern; 200 mr_pattern_alloced = TRUE; 201 } 202 } 203 else 204 #endif 205 mr_pattern = pat; 206 207 /* 208 * Save the currently used pattern in the appropriate place, 209 * unless the pattern should not be remembered. 210 */ 211 if (!(options & SEARCH_KEEP) && !cmdmod.keeppatterns) 212 { 213 /* search or global command */ 214 if (pat_save == RE_SEARCH || pat_save == RE_BOTH) 215 save_re_pat(RE_SEARCH, pat, magic); 216 /* substitute or global command */ 217 if (pat_save == RE_SUBST || pat_save == RE_BOTH) 218 save_re_pat(RE_SUBST, pat, magic); 219 } 220 221 regmatch->rmm_ic = ignorecase(pat); 222 regmatch->rmm_maxcol = 0; 223 regmatch->regprog = vim_regcomp(pat, magic ? RE_MAGIC : 0); 224 if (regmatch->regprog == NULL) 225 return FAIL; 226 return OK; 227 } 228 229 /* 230 * Get search pattern used by search_regcomp(). 231 */ 232 char_u * 233 get_search_pat(void) 234 { 235 return mr_pattern; 236 } 237 238 #if defined(FEAT_RIGHTLEFT) || defined(PROTO) 239 /* 240 * Reverse text into allocated memory. 241 * Returns the allocated string, NULL when out of memory. 242 */ 243 char_u * 244 reverse_text(char_u *s) 245 { 246 unsigned len; 247 unsigned s_i, rev_i; 248 char_u *rev; 249 250 /* 251 * Reverse the pattern. 252 */ 253 len = (unsigned)STRLEN(s); 254 rev = alloc(len + 1); 255 if (rev != NULL) 256 { 257 rev_i = len; 258 for (s_i = 0; s_i < len; ++s_i) 259 { 260 # ifdef FEAT_MBYTE 261 if (has_mbyte) 262 { 263 int mb_len; 264 265 mb_len = (*mb_ptr2len)(s + s_i); 266 rev_i -= mb_len; 267 mch_memmove(rev + rev_i, s + s_i, mb_len); 268 s_i += mb_len - 1; 269 } 270 else 271 # endif 272 rev[--rev_i] = s[s_i]; 273 274 } 275 rev[len] = NUL; 276 } 277 return rev; 278 } 279 #endif 280 281 void 282 save_re_pat(int idx, char_u *pat, int magic) 283 { 284 if (spats[idx].pat != pat) 285 { 286 vim_free(spats[idx].pat); 287 spats[idx].pat = vim_strsave(pat); 288 spats[idx].magic = magic; 289 spats[idx].no_scs = no_smartcase; 290 last_idx = idx; 291 #ifdef FEAT_SEARCH_EXTRA 292 /* If 'hlsearch' set and search pat changed: need redraw. */ 293 if (p_hls) 294 redraw_all_later(SOME_VALID); 295 SET_NO_HLSEARCH(FALSE); 296 #endif 297 } 298 } 299 300 #if defined(FEAT_AUTOCMD) || defined(FEAT_EVAL) || defined(PROTO) 301 /* 302 * Save the search patterns, so they can be restored later. 303 * Used before/after executing autocommands and user functions. 304 */ 305 static int save_level = 0; 306 307 void 308 save_search_patterns(void) 309 { 310 if (save_level++ == 0) 311 { 312 saved_spats[0] = spats[0]; 313 if (spats[0].pat != NULL) 314 saved_spats[0].pat = vim_strsave(spats[0].pat); 315 saved_spats[1] = spats[1]; 316 if (spats[1].pat != NULL) 317 saved_spats[1].pat = vim_strsave(spats[1].pat); 318 saved_last_idx = last_idx; 319 # ifdef FEAT_SEARCH_EXTRA 320 saved_no_hlsearch = no_hlsearch; 321 # endif 322 } 323 } 324 325 void 326 restore_search_patterns(void) 327 { 328 if (--save_level == 0) 329 { 330 vim_free(spats[0].pat); 331 spats[0] = saved_spats[0]; 332 #if defined(FEAT_EVAL) 333 set_vv_searchforward(); 334 #endif 335 vim_free(spats[1].pat); 336 spats[1] = saved_spats[1]; 337 last_idx = saved_last_idx; 338 # ifdef FEAT_SEARCH_EXTRA 339 SET_NO_HLSEARCH(saved_no_hlsearch); 340 # endif 341 } 342 } 343 #endif 344 345 #if defined(EXITFREE) || defined(PROTO) 346 void 347 free_search_patterns(void) 348 { 349 vim_free(spats[0].pat); 350 vim_free(spats[1].pat); 351 352 # ifdef FEAT_RIGHTLEFT 353 if (mr_pattern_alloced) 354 { 355 vim_free(mr_pattern); 356 mr_pattern_alloced = FALSE; 357 mr_pattern = NULL; 358 } 359 # endif 360 } 361 #endif 362 363 /* 364 * Return TRUE when case should be ignored for search pattern "pat". 365 * Uses the 'ignorecase' and 'smartcase' options. 366 */ 367 int 368 ignorecase(char_u *pat) 369 { 370 return ignorecase_opt(pat, p_ic, p_scs); 371 } 372 373 /* 374 * As ignorecase() put pass the "ic" and "scs" flags. 375 */ 376 int 377 ignorecase_opt(char_u *pat, int ic_in, int scs) 378 { 379 int ic = ic_in; 380 381 if (ic && !no_smartcase && scs 382 #ifdef FEAT_INS_EXPAND 383 && !(ctrl_x_mode && curbuf->b_p_inf) 384 #endif 385 ) 386 ic = !pat_has_uppercase(pat); 387 no_smartcase = FALSE; 388 389 return ic; 390 } 391 392 /* 393 * Return TRUE if pattern "pat" has an uppercase character. 394 */ 395 int 396 pat_has_uppercase(char_u *pat) 397 { 398 char_u *p = pat; 399 400 while (*p != NUL) 401 { 402 #ifdef FEAT_MBYTE 403 int l; 404 405 if (has_mbyte && (l = (*mb_ptr2len)(p)) > 1) 406 { 407 if (enc_utf8 && utf_isupper(utf_ptr2char(p))) 408 return TRUE; 409 p += l; 410 } 411 else 412 #endif 413 if (*p == '\\') 414 { 415 if (p[1] == '_' && p[2] != NUL) /* skip "\_X" */ 416 p += 3; 417 else if (p[1] == '%' && p[2] != NUL) /* skip "\%X" */ 418 p += 3; 419 else if (p[1] != NUL) /* skip "\X" */ 420 p += 2; 421 else 422 p += 1; 423 } 424 else if (MB_ISUPPER(*p)) 425 return TRUE; 426 else 427 ++p; 428 } 429 return FALSE; 430 } 431 432 char_u * 433 last_csearch(void) 434 { 435 #ifdef FEAT_MBYTE 436 return lastc_bytes; 437 #else 438 return lastc; 439 #endif 440 } 441 442 int 443 last_csearch_forward(void) 444 { 445 return lastcdir == FORWARD; 446 } 447 448 int 449 last_csearch_until(void) 450 { 451 return last_t_cmd == TRUE; 452 } 453 454 void 455 set_last_csearch(int c, char_u *s UNUSED, int len UNUSED) 456 { 457 *lastc = c; 458 #ifdef FEAT_MBYTE 459 lastc_bytelen = len; 460 if (len) 461 memcpy(lastc_bytes, s, len); 462 else 463 vim_memset(lastc_bytes, 0, sizeof(lastc_bytes)); 464 #endif 465 } 466 467 void 468 set_csearch_direction(int cdir) 469 { 470 lastcdir = cdir; 471 } 472 473 void 474 set_csearch_until(int t_cmd) 475 { 476 last_t_cmd = t_cmd; 477 } 478 479 char_u * 480 last_search_pat(void) 481 { 482 return spats[last_idx].pat; 483 } 484 485 /* 486 * Reset search direction to forward. For "gd" and "gD" commands. 487 */ 488 void 489 reset_search_dir(void) 490 { 491 spats[0].off.dir = '/'; 492 #if defined(FEAT_EVAL) 493 set_vv_searchforward(); 494 #endif 495 } 496 497 #if defined(FEAT_EVAL) || defined(FEAT_VIMINFO) 498 /* 499 * Set the last search pattern. For ":let @/ =" and viminfo. 500 * Also set the saved search pattern, so that this works in an autocommand. 501 */ 502 void 503 set_last_search_pat( 504 char_u *s, 505 int idx, 506 int magic, 507 int setlast) 508 { 509 vim_free(spats[idx].pat); 510 /* An empty string means that nothing should be matched. */ 511 if (*s == NUL) 512 spats[idx].pat = NULL; 513 else 514 spats[idx].pat = vim_strsave(s); 515 spats[idx].magic = magic; 516 spats[idx].no_scs = FALSE; 517 spats[idx].off.dir = '/'; 518 #if defined(FEAT_EVAL) 519 set_vv_searchforward(); 520 #endif 521 spats[idx].off.line = FALSE; 522 spats[idx].off.end = FALSE; 523 spats[idx].off.off = 0; 524 if (setlast) 525 last_idx = idx; 526 if (save_level) 527 { 528 vim_free(saved_spats[idx].pat); 529 saved_spats[idx] = spats[0]; 530 if (spats[idx].pat == NULL) 531 saved_spats[idx].pat = NULL; 532 else 533 saved_spats[idx].pat = vim_strsave(spats[idx].pat); 534 saved_last_idx = last_idx; 535 } 536 # ifdef FEAT_SEARCH_EXTRA 537 /* If 'hlsearch' set and search pat changed: need redraw. */ 538 if (p_hls && idx == last_idx && !no_hlsearch) 539 redraw_all_later(SOME_VALID); 540 # endif 541 } 542 #endif 543 544 #ifdef FEAT_SEARCH_EXTRA 545 /* 546 * Get a regexp program for the last used search pattern. 547 * This is used for highlighting all matches in a window. 548 * Values returned in regmatch->regprog and regmatch->rmm_ic. 549 */ 550 void 551 last_pat_prog(regmmatch_T *regmatch) 552 { 553 if (spats[last_idx].pat == NULL) 554 { 555 regmatch->regprog = NULL; 556 return; 557 } 558 ++emsg_off; /* So it doesn't beep if bad expr */ 559 (void)search_regcomp((char_u *)"", 0, last_idx, SEARCH_KEEP, regmatch); 560 --emsg_off; 561 } 562 #endif 563 564 /* 565 * Lowest level search function. 566 * Search for 'count'th occurrence of pattern 'pat' in direction 'dir'. 567 * Start at position 'pos' and return the found position in 'pos'. 568 * 569 * if (options & SEARCH_MSG) == 0 don't give any messages 570 * if (options & SEARCH_MSG) == SEARCH_NFMSG don't give 'notfound' messages 571 * if (options & SEARCH_MSG) == SEARCH_MSG give all messages 572 * if (options & SEARCH_HIS) put search pattern in history 573 * if (options & SEARCH_END) return position at end of match 574 * if (options & SEARCH_START) accept match at pos itself 575 * if (options & SEARCH_KEEP) keep previous search pattern 576 * if (options & SEARCH_FOLD) match only once in a closed fold 577 * if (options & SEARCH_PEEK) check for typed char, cancel search 578 * if (options & SEARCH_COL) start at pos->col instead of zero 579 * 580 * Return FAIL (zero) for failure, non-zero for success. 581 * When FEAT_EVAL is defined, returns the index of the first matching 582 * subpattern plus one; one if there was none. 583 */ 584 int 585 searchit( 586 win_T *win, /* window to search in; can be NULL for a 587 buffer without a window! */ 588 buf_T *buf, 589 pos_T *pos, 590 int dir, 591 char_u *pat, 592 long count, 593 int options, 594 int pat_use, /* which pattern to use when "pat" is empty */ 595 linenr_T stop_lnum, /* stop after this line number when != 0 */ 596 proftime_T *tm UNUSED, /* timeout limit or NULL */ 597 int *timed_out UNUSED) /* set when timed out or NULL */ 598 { 599 int found; 600 linenr_T lnum; /* no init to shut up Apollo cc */ 601 colnr_T col; 602 regmmatch_T regmatch; 603 char_u *ptr; 604 colnr_T matchcol; 605 lpos_T endpos; 606 lpos_T matchpos; 607 int loop; 608 pos_T start_pos; 609 int at_first_line; 610 int extra_col; 611 int start_char_len; 612 int match_ok; 613 long nmatched; 614 int submatch = 0; 615 int first_match = TRUE; 616 int save_called_emsg = called_emsg; 617 #ifdef FEAT_SEARCH_EXTRA 618 int break_loop = FALSE; 619 #endif 620 621 if (search_regcomp(pat, RE_SEARCH, pat_use, 622 (options & (SEARCH_HIS + SEARCH_KEEP)), ®match) == FAIL) 623 { 624 if ((options & SEARCH_MSG) && !rc_did_emsg) 625 EMSG2(_("E383: Invalid search string: %s"), mr_pattern); 626 return FAIL; 627 } 628 629 /* 630 * find the string 631 */ 632 called_emsg = FALSE; 633 do /* loop for count */ 634 { 635 /* When not accepting a match at the start position set "extra_col" to 636 * a non-zero value. Don't do that when starting at MAXCOL, since 637 * MAXCOL + 1 is zero. */ 638 if (pos->col == MAXCOL) 639 start_char_len = 0; 640 #ifdef FEAT_MBYTE 641 /* Watch out for the "col" being MAXCOL - 2, used in a closed fold. */ 642 else if (has_mbyte 643 && pos->lnum >= 1 && pos->lnum <= buf->b_ml.ml_line_count 644 && pos->col < MAXCOL - 2) 645 { 646 ptr = ml_get_buf(buf, pos->lnum, FALSE) + pos->col; 647 if (*ptr == NUL) 648 start_char_len = 1; 649 else 650 start_char_len = (*mb_ptr2len)(ptr); 651 } 652 #endif 653 else 654 start_char_len = 1; 655 if (dir == FORWARD) 656 { 657 if (options & SEARCH_START) 658 extra_col = 0; 659 else 660 extra_col = start_char_len; 661 } 662 else 663 { 664 if (options & SEARCH_START) 665 extra_col = start_char_len; 666 else 667 extra_col = 0; 668 } 669 670 start_pos = *pos; /* remember start pos for detecting no match */ 671 found = 0; /* default: not found */ 672 at_first_line = TRUE; /* default: start in first line */ 673 if (pos->lnum == 0) /* correct lnum for when starting in line 0 */ 674 { 675 pos->lnum = 1; 676 pos->col = 0; 677 at_first_line = FALSE; /* not in first line now */ 678 } 679 680 /* 681 * Start searching in current line, unless searching backwards and 682 * we're in column 0. 683 * If we are searching backwards, in column 0, and not including the 684 * current position, gain some efficiency by skipping back a line. 685 * Otherwise begin the search in the current line. 686 */ 687 if (dir == BACKWARD && start_pos.col == 0 688 && (options & SEARCH_START) == 0) 689 { 690 lnum = pos->lnum - 1; 691 at_first_line = FALSE; 692 } 693 else 694 lnum = pos->lnum; 695 696 for (loop = 0; loop <= 1; ++loop) /* loop twice if 'wrapscan' set */ 697 { 698 for ( ; lnum > 0 && lnum <= buf->b_ml.ml_line_count; 699 lnum += dir, at_first_line = FALSE) 700 { 701 /* Stop after checking "stop_lnum", if it's set. */ 702 if (stop_lnum != 0 && (dir == FORWARD 703 ? lnum > stop_lnum : lnum < stop_lnum)) 704 break; 705 #ifdef FEAT_RELTIME 706 /* Stop after passing the "tm" time limit. */ 707 if (tm != NULL && profile_passed_limit(tm)) 708 break; 709 #endif 710 711 /* 712 * Look for a match somewhere in line "lnum". 713 */ 714 col = at_first_line && (options & SEARCH_COL) ? pos->col 715 : (colnr_T)0; 716 nmatched = vim_regexec_multi(®match, win, buf, 717 lnum, col, 718 #ifdef FEAT_RELTIME 719 tm, timed_out 720 #else 721 NULL, NULL 722 #endif 723 ); 724 /* Abort searching on an error (e.g., out of stack). */ 725 if (called_emsg 726 #ifdef FEAT_RELTIME 727 || (timed_out != NULL && *timed_out) 728 #endif 729 ) 730 break; 731 if (nmatched > 0) 732 { 733 /* match may actually be in another line when using \zs */ 734 matchpos = regmatch.startpos[0]; 735 endpos = regmatch.endpos[0]; 736 #ifdef FEAT_EVAL 737 submatch = first_submatch(®match); 738 #endif 739 /* "lnum" may be past end of buffer for "\n\zs". */ 740 if (lnum + matchpos.lnum > buf->b_ml.ml_line_count) 741 ptr = (char_u *)""; 742 else 743 ptr = ml_get_buf(buf, lnum + matchpos.lnum, FALSE); 744 745 /* 746 * Forward search in the first line: match should be after 747 * the start position. If not, continue at the end of the 748 * match (this is vi compatible) or on the next char. 749 */ 750 if (dir == FORWARD && at_first_line) 751 { 752 match_ok = TRUE; 753 /* 754 * When the match starts in a next line it's certainly 755 * past the start position. 756 * When match lands on a NUL the cursor will be put 757 * one back afterwards, compare with that position, 758 * otherwise "/$" will get stuck on end of line. 759 */ 760 while (matchpos.lnum == 0 761 && ((options & SEARCH_END) && first_match 762 ? (nmatched == 1 763 && (int)endpos.col - 1 764 < (int)start_pos.col + extra_col) 765 : ((int)matchpos.col 766 - (ptr[matchpos.col] == NUL) 767 < (int)start_pos.col + extra_col))) 768 { 769 /* 770 * If vi-compatible searching, continue at the end 771 * of the match, otherwise continue one position 772 * forward. 773 */ 774 if (vim_strchr(p_cpo, CPO_SEARCH) != NULL) 775 { 776 if (nmatched > 1) 777 { 778 /* end is in next line, thus no match in 779 * this line */ 780 match_ok = FALSE; 781 break; 782 } 783 matchcol = endpos.col; 784 /* for empty match: advance one char */ 785 if (matchcol == matchpos.col 786 && ptr[matchcol] != NUL) 787 { 788 #ifdef FEAT_MBYTE 789 if (has_mbyte) 790 matchcol += 791 (*mb_ptr2len)(ptr + matchcol); 792 else 793 #endif 794 ++matchcol; 795 } 796 } 797 else 798 { 799 matchcol = matchpos.col; 800 if (ptr[matchcol] != NUL) 801 { 802 #ifdef FEAT_MBYTE 803 if (has_mbyte) 804 matchcol += (*mb_ptr2len)(ptr 805 + matchcol); 806 else 807 #endif 808 ++matchcol; 809 } 810 } 811 if (matchcol == 0 && (options & SEARCH_START)) 812 break; 813 if (ptr[matchcol] == NUL 814 || (nmatched = vim_regexec_multi(®match, 815 win, buf, lnum + matchpos.lnum, 816 matchcol, 817 #ifdef FEAT_RELTIME 818 tm, timed_out 819 #else 820 NULL, NULL 821 #endif 822 )) == 0) 823 { 824 match_ok = FALSE; 825 break; 826 } 827 matchpos = regmatch.startpos[0]; 828 endpos = regmatch.endpos[0]; 829 # ifdef FEAT_EVAL 830 submatch = first_submatch(®match); 831 # endif 832 833 /* Need to get the line pointer again, a 834 * multi-line search may have made it invalid. */ 835 ptr = ml_get_buf(buf, lnum + matchpos.lnum, FALSE); 836 } 837 if (!match_ok) 838 continue; 839 } 840 if (dir == BACKWARD) 841 { 842 /* 843 * Now, if there are multiple matches on this line, 844 * we have to get the last one. Or the last one before 845 * the cursor, if we're on that line. 846 * When putting the new cursor at the end, compare 847 * relative to the end of the match. 848 */ 849 match_ok = FALSE; 850 for (;;) 851 { 852 /* Remember a position that is before the start 853 * position, we use it if it's the last match in 854 * the line. Always accept a position after 855 * wrapping around. */ 856 if (loop 857 || ((options & SEARCH_END) 858 ? (lnum + regmatch.endpos[0].lnum 859 < start_pos.lnum 860 || (lnum + regmatch.endpos[0].lnum 861 == start_pos.lnum 862 && (int)regmatch.endpos[0].col - 1 863 < (int)start_pos.col 864 + extra_col)) 865 : (lnum + regmatch.startpos[0].lnum 866 < start_pos.lnum 867 || (lnum + regmatch.startpos[0].lnum 868 == start_pos.lnum 869 && (int)regmatch.startpos[0].col 870 < (int)start_pos.col 871 + extra_col)))) 872 { 873 match_ok = TRUE; 874 matchpos = regmatch.startpos[0]; 875 endpos = regmatch.endpos[0]; 876 # ifdef FEAT_EVAL 877 submatch = first_submatch(®match); 878 # endif 879 } 880 else 881 break; 882 883 /* 884 * We found a valid match, now check if there is 885 * another one after it. 886 * If vi-compatible searching, continue at the end 887 * of the match, otherwise continue one position 888 * forward. 889 */ 890 if (vim_strchr(p_cpo, CPO_SEARCH) != NULL) 891 { 892 if (nmatched > 1) 893 break; 894 matchcol = endpos.col; 895 /* for empty match: advance one char */ 896 if (matchcol == matchpos.col 897 && ptr[matchcol] != NUL) 898 { 899 #ifdef FEAT_MBYTE 900 if (has_mbyte) 901 matchcol += 902 (*mb_ptr2len)(ptr + matchcol); 903 else 904 #endif 905 ++matchcol; 906 } 907 } 908 else 909 { 910 /* Stop when the match is in a next line. */ 911 if (matchpos.lnum > 0) 912 break; 913 matchcol = matchpos.col; 914 if (ptr[matchcol] != NUL) 915 { 916 #ifdef FEAT_MBYTE 917 if (has_mbyte) 918 matchcol += 919 (*mb_ptr2len)(ptr + matchcol); 920 else 921 #endif 922 ++matchcol; 923 } 924 } 925 if (ptr[matchcol] == NUL 926 || (nmatched = vim_regexec_multi(®match, 927 win, buf, lnum + matchpos.lnum, 928 matchcol, 929 #ifdef FEAT_RELTIME 930 tm, timed_out 931 #else 932 NULL, NULL 933 #endif 934 )) == 0) 935 break; 936 937 /* Need to get the line pointer again, a 938 * multi-line search may have made it invalid. */ 939 ptr = ml_get_buf(buf, lnum + matchpos.lnum, FALSE); 940 } 941 942 /* 943 * If there is only a match after the cursor, skip 944 * this match. 945 */ 946 if (!match_ok) 947 continue; 948 } 949 950 /* With the SEARCH_END option move to the last character 951 * of the match. Don't do it for an empty match, end 952 * should be same as start then. */ 953 if ((options & SEARCH_END) && !(options & SEARCH_NOOF) 954 && !(matchpos.lnum == endpos.lnum 955 && matchpos.col == endpos.col)) 956 { 957 /* For a match in the first column, set the position 958 * on the NUL in the previous line. */ 959 pos->lnum = lnum + endpos.lnum; 960 pos->col = endpos.col; 961 if (endpos.col == 0) 962 { 963 if (pos->lnum > 1) /* just in case */ 964 { 965 --pos->lnum; 966 pos->col = (colnr_T)STRLEN(ml_get_buf(buf, 967 pos->lnum, FALSE)); 968 } 969 } 970 else 971 { 972 --pos->col; 973 #ifdef FEAT_MBYTE 974 if (has_mbyte 975 && pos->lnum <= buf->b_ml.ml_line_count) 976 { 977 ptr = ml_get_buf(buf, pos->lnum, FALSE); 978 pos->col -= (*mb_head_off)(ptr, ptr + pos->col); 979 } 980 #endif 981 } 982 } 983 else 984 { 985 pos->lnum = lnum + matchpos.lnum; 986 pos->col = matchpos.col; 987 } 988 #ifdef FEAT_VIRTUALEDIT 989 pos->coladd = 0; 990 #endif 991 found = 1; 992 first_match = FALSE; 993 994 /* Set variables used for 'incsearch' highlighting. */ 995 search_match_lines = endpos.lnum - matchpos.lnum; 996 search_match_endcol = endpos.col; 997 break; 998 } 999 line_breakcheck(); /* stop if ctrl-C typed */ 1000 if (got_int) 1001 break; 1002 1003 #ifdef FEAT_SEARCH_EXTRA 1004 /* Cancel searching if a character was typed. Used for 1005 * 'incsearch'. Don't check too often, that would slowdown 1006 * searching too much. */ 1007 if ((options & SEARCH_PEEK) 1008 && ((lnum - pos->lnum) & 0x3f) == 0 1009 && char_avail()) 1010 { 1011 break_loop = TRUE; 1012 break; 1013 } 1014 #endif 1015 1016 if (loop && lnum == start_pos.lnum) 1017 break; /* if second loop, stop where started */ 1018 } 1019 at_first_line = FALSE; 1020 1021 /* 1022 * Stop the search if wrapscan isn't set, "stop_lnum" is 1023 * specified, after an interrupt, after a match and after looping 1024 * twice. 1025 */ 1026 if (!p_ws || stop_lnum != 0 || got_int || called_emsg 1027 #ifdef FEAT_RELTIME 1028 || (timed_out != NULL && *timed_out) 1029 #endif 1030 #ifdef FEAT_SEARCH_EXTRA 1031 || break_loop 1032 #endif 1033 || found || loop) 1034 break; 1035 1036 /* 1037 * If 'wrapscan' is set we continue at the other end of the file. 1038 * If 'shortmess' does not contain 's', we give a message. 1039 * This message is also remembered in keep_msg for when the screen 1040 * is redrawn. The keep_msg is cleared whenever another message is 1041 * written. 1042 */ 1043 if (dir == BACKWARD) /* start second loop at the other end */ 1044 lnum = buf->b_ml.ml_line_count; 1045 else 1046 lnum = 1; 1047 if (!shortmess(SHM_SEARCH) && (options & SEARCH_MSG)) 1048 give_warning((char_u *)_(dir == BACKWARD 1049 ? top_bot_msg : bot_top_msg), TRUE); 1050 } 1051 if (got_int || called_emsg 1052 #ifdef FEAT_RELTIME 1053 || (timed_out != NULL && *timed_out) 1054 #endif 1055 #ifdef FEAT_SEARCH_EXTRA 1056 || break_loop 1057 #endif 1058 ) 1059 break; 1060 } 1061 while (--count > 0 && found); /* stop after count matches or no match */ 1062 1063 vim_regfree(regmatch.regprog); 1064 1065 called_emsg |= save_called_emsg; 1066 1067 if (!found) /* did not find it */ 1068 { 1069 if (got_int) 1070 EMSG(_(e_interr)); 1071 else if ((options & SEARCH_MSG) == SEARCH_MSG) 1072 { 1073 if (p_ws) 1074 EMSG2(_(e_patnotf2), mr_pattern); 1075 else if (lnum == 0) 1076 EMSG2(_("E384: search hit TOP without match for: %s"), 1077 mr_pattern); 1078 else 1079 EMSG2(_("E385: search hit BOTTOM without match for: %s"), 1080 mr_pattern); 1081 } 1082 return FAIL; 1083 } 1084 1085 /* A pattern like "\n\zs" may go past the last line. */ 1086 if (pos->lnum > buf->b_ml.ml_line_count) 1087 { 1088 pos->lnum = buf->b_ml.ml_line_count; 1089 pos->col = (int)STRLEN(ml_get_buf(buf, pos->lnum, FALSE)); 1090 if (pos->col > 0) 1091 --pos->col; 1092 } 1093 1094 return submatch + 1; 1095 } 1096 1097 #ifdef FEAT_EVAL 1098 void 1099 set_search_direction(int cdir) 1100 { 1101 spats[0].off.dir = cdir; 1102 } 1103 1104 static void 1105 set_vv_searchforward(void) 1106 { 1107 set_vim_var_nr(VV_SEARCHFORWARD, (long)(spats[0].off.dir == '/')); 1108 } 1109 1110 /* 1111 * Return the number of the first subpat that matched. 1112 * Return zero if none of them matched. 1113 */ 1114 static int 1115 first_submatch(regmmatch_T *rp) 1116 { 1117 int submatch; 1118 1119 for (submatch = 1; ; ++submatch) 1120 { 1121 if (rp->startpos[submatch].lnum >= 0) 1122 break; 1123 if (submatch == 9) 1124 { 1125 submatch = 0; 1126 break; 1127 } 1128 } 1129 return submatch; 1130 } 1131 #endif 1132 1133 /* 1134 * Highest level string search function. 1135 * Search for the 'count'th occurrence of pattern 'pat' in direction 'dirc' 1136 * If 'dirc' is 0: use previous dir. 1137 * If 'pat' is NULL or empty : use previous string. 1138 * If 'options & SEARCH_REV' : go in reverse of previous dir. 1139 * If 'options & SEARCH_ECHO': echo the search command and handle options 1140 * If 'options & SEARCH_MSG' : may give error message 1141 * If 'options & SEARCH_OPT' : interpret optional flags 1142 * If 'options & SEARCH_HIS' : put search pattern in history 1143 * If 'options & SEARCH_NOOF': don't add offset to position 1144 * If 'options & SEARCH_MARK': set previous context mark 1145 * If 'options & SEARCH_KEEP': keep previous search pattern 1146 * If 'options & SEARCH_START': accept match at curpos itself 1147 * If 'options & SEARCH_PEEK': check for typed char, cancel search 1148 * 1149 * Careful: If spats[0].off.line == TRUE and spats[0].off.off == 0 this 1150 * makes the movement linewise without moving the match position. 1151 * 1152 * Return 0 for failure, 1 for found, 2 for found and line offset added. 1153 */ 1154 int 1155 do_search( 1156 oparg_T *oap, /* can be NULL */ 1157 int dirc, /* '/' or '?' */ 1158 char_u *pat, 1159 long count, 1160 int options, 1161 proftime_T *tm, /* timeout limit or NULL */ 1162 int *timed_out) /* flag set on timeout or NULL */ 1163 { 1164 pos_T pos; /* position of the last match */ 1165 char_u *searchstr; 1166 struct soffset old_off; 1167 int retval; /* Return value */ 1168 char_u *p; 1169 long c; 1170 char_u *dircp; 1171 char_u *strcopy = NULL; 1172 char_u *ps; 1173 1174 /* 1175 * A line offset is not remembered, this is vi compatible. 1176 */ 1177 if (spats[0].off.line && vim_strchr(p_cpo, CPO_LINEOFF) != NULL) 1178 { 1179 spats[0].off.line = FALSE; 1180 spats[0].off.off = 0; 1181 } 1182 1183 /* 1184 * Save the values for when (options & SEARCH_KEEP) is used. 1185 * (there is no "if ()" around this because gcc wants them initialized) 1186 */ 1187 old_off = spats[0].off; 1188 1189 pos = curwin->w_cursor; /* start searching at the cursor position */ 1190 1191 /* 1192 * Find out the direction of the search. 1193 */ 1194 if (dirc == 0) 1195 dirc = spats[0].off.dir; 1196 else 1197 { 1198 spats[0].off.dir = dirc; 1199 #if defined(FEAT_EVAL) 1200 set_vv_searchforward(); 1201 #endif 1202 } 1203 if (options & SEARCH_REV) 1204 { 1205 #ifdef WIN32 1206 /* There is a bug in the Visual C++ 2.2 compiler which means that 1207 * dirc always ends up being '/' */ 1208 dirc = (dirc == '/') ? '?' : '/'; 1209 #else 1210 if (dirc == '/') 1211 dirc = '?'; 1212 else 1213 dirc = '/'; 1214 #endif 1215 } 1216 1217 #ifdef FEAT_FOLDING 1218 /* If the cursor is in a closed fold, don't find another match in the same 1219 * fold. */ 1220 if (dirc == '/') 1221 { 1222 if (hasFolding(pos.lnum, NULL, &pos.lnum)) 1223 pos.col = MAXCOL - 2; /* avoid overflow when adding 1 */ 1224 } 1225 else 1226 { 1227 if (hasFolding(pos.lnum, &pos.lnum, NULL)) 1228 pos.col = 0; 1229 } 1230 #endif 1231 1232 #ifdef FEAT_SEARCH_EXTRA 1233 /* 1234 * Turn 'hlsearch' highlighting back on. 1235 */ 1236 if (no_hlsearch && !(options & SEARCH_KEEP)) 1237 { 1238 redraw_all_later(SOME_VALID); 1239 SET_NO_HLSEARCH(FALSE); 1240 } 1241 #endif 1242 1243 /* 1244 * Repeat the search when pattern followed by ';', e.g. "/foo/;?bar". 1245 */ 1246 for (;;) 1247 { 1248 searchstr = pat; 1249 dircp = NULL; 1250 /* use previous pattern */ 1251 if (pat == NULL || *pat == NUL || *pat == dirc) 1252 { 1253 if (spats[RE_SEARCH].pat == NULL) /* no previous pattern */ 1254 { 1255 searchstr = spats[RE_SUBST].pat; 1256 if (searchstr == NULL) 1257 { 1258 EMSG(_(e_noprevre)); 1259 retval = 0; 1260 goto end_do_search; 1261 } 1262 } 1263 else 1264 { 1265 /* make search_regcomp() use spats[RE_SEARCH].pat */ 1266 searchstr = (char_u *)""; 1267 } 1268 } 1269 1270 if (pat != NULL && *pat != NUL) /* look for (new) offset */ 1271 { 1272 /* 1273 * Find end of regular expression. 1274 * If there is a matching '/' or '?', toss it. 1275 */ 1276 ps = strcopy; 1277 p = skip_regexp(pat, dirc, (int)p_magic, &strcopy); 1278 if (strcopy != ps) 1279 { 1280 /* made a copy of "pat" to change "\?" to "?" */ 1281 searchcmdlen += (int)(STRLEN(pat) - STRLEN(strcopy)); 1282 pat = strcopy; 1283 searchstr = strcopy; 1284 } 1285 if (*p == dirc) 1286 { 1287 dircp = p; /* remember where we put the NUL */ 1288 *p++ = NUL; 1289 } 1290 spats[0].off.line = FALSE; 1291 spats[0].off.end = FALSE; 1292 spats[0].off.off = 0; 1293 /* 1294 * Check for a line offset or a character offset. 1295 * For get_address (echo off) we don't check for a character 1296 * offset, because it is meaningless and the 's' could be a 1297 * substitute command. 1298 */ 1299 if (*p == '+' || *p == '-' || VIM_ISDIGIT(*p)) 1300 spats[0].off.line = TRUE; 1301 else if ((options & SEARCH_OPT) && 1302 (*p == 'e' || *p == 's' || *p == 'b')) 1303 { 1304 if (*p == 'e') /* end */ 1305 spats[0].off.end = SEARCH_END; 1306 ++p; 1307 } 1308 if (VIM_ISDIGIT(*p) || *p == '+' || *p == '-') /* got an offset */ 1309 { 1310 /* 'nr' or '+nr' or '-nr' */ 1311 if (VIM_ISDIGIT(*p) || VIM_ISDIGIT(*(p + 1))) 1312 spats[0].off.off = atol((char *)p); 1313 else if (*p == '-') /* single '-' */ 1314 spats[0].off.off = -1; 1315 else /* single '+' */ 1316 spats[0].off.off = 1; 1317 ++p; 1318 while (VIM_ISDIGIT(*p)) /* skip number */ 1319 ++p; 1320 } 1321 1322 /* compute length of search command for get_address() */ 1323 searchcmdlen += (int)(p - pat); 1324 1325 pat = p; /* put pat after search command */ 1326 } 1327 1328 if ((options & SEARCH_ECHO) && messaging() 1329 && !cmd_silent && msg_silent == 0) 1330 { 1331 char_u *msgbuf; 1332 char_u *trunc; 1333 1334 if (*searchstr == NUL) 1335 p = spats[last_idx].pat; 1336 else 1337 p = searchstr; 1338 msgbuf = alloc((unsigned)(STRLEN(p) + 40)); 1339 if (msgbuf != NULL) 1340 { 1341 msgbuf[0] = dirc; 1342 #ifdef FEAT_MBYTE 1343 if (enc_utf8 && utf_iscomposing(utf_ptr2char(p))) 1344 { 1345 /* Use a space to draw the composing char on. */ 1346 msgbuf[1] = ' '; 1347 STRCPY(msgbuf + 2, p); 1348 } 1349 else 1350 #endif 1351 STRCPY(msgbuf + 1, p); 1352 if (spats[0].off.line || spats[0].off.end || spats[0].off.off) 1353 { 1354 p = msgbuf + STRLEN(msgbuf); 1355 *p++ = dirc; 1356 if (spats[0].off.end) 1357 *p++ = 'e'; 1358 else if (!spats[0].off.line) 1359 *p++ = 's'; 1360 if (spats[0].off.off > 0 || spats[0].off.line) 1361 *p++ = '+'; 1362 if (spats[0].off.off != 0 || spats[0].off.line) 1363 sprintf((char *)p, "%ld", spats[0].off.off); 1364 else 1365 *p = NUL; 1366 } 1367 1368 msg_start(); 1369 trunc = msg_strtrunc(msgbuf, FALSE); 1370 1371 #ifdef FEAT_RIGHTLEFT 1372 /* The search pattern could be shown on the right in rightleft 1373 * mode, but the 'ruler' and 'showcmd' area use it too, thus 1374 * it would be blanked out again very soon. Show it on the 1375 * left, but do reverse the text. */ 1376 if (curwin->w_p_rl && *curwin->w_p_rlc == 's') 1377 { 1378 char_u *r; 1379 1380 r = reverse_text(trunc != NULL ? trunc : msgbuf); 1381 if (r != NULL) 1382 { 1383 vim_free(trunc); 1384 trunc = r; 1385 } 1386 } 1387 #endif 1388 if (trunc != NULL) 1389 { 1390 msg_outtrans(trunc); 1391 vim_free(trunc); 1392 } 1393 else 1394 msg_outtrans(msgbuf); 1395 msg_clr_eos(); 1396 msg_check(); 1397 vim_free(msgbuf); 1398 1399 gotocmdline(FALSE); 1400 out_flush(); 1401 msg_nowait = TRUE; /* don't wait for this message */ 1402 } 1403 } 1404 1405 /* 1406 * If there is a character offset, subtract it from the current 1407 * position, so we don't get stuck at "?pat?e+2" or "/pat/s-2". 1408 * Skip this if pos.col is near MAXCOL (closed fold). 1409 * This is not done for a line offset, because then we would not be vi 1410 * compatible. 1411 */ 1412 if (!spats[0].off.line && spats[0].off.off && pos.col < MAXCOL - 2) 1413 { 1414 if (spats[0].off.off > 0) 1415 { 1416 for (c = spats[0].off.off; c; --c) 1417 if (decl(&pos) == -1) 1418 break; 1419 if (c) /* at start of buffer */ 1420 { 1421 pos.lnum = 0; /* allow lnum == 0 here */ 1422 pos.col = MAXCOL; 1423 } 1424 } 1425 else 1426 { 1427 for (c = spats[0].off.off; c; ++c) 1428 if (incl(&pos) == -1) 1429 break; 1430 if (c) /* at end of buffer */ 1431 { 1432 pos.lnum = curbuf->b_ml.ml_line_count + 1; 1433 pos.col = 0; 1434 } 1435 } 1436 } 1437 1438 #ifdef FEAT_FKMAP /* when in Farsi mode, reverse the character flow */ 1439 if (p_altkeymap && curwin->w_p_rl) 1440 lrFswap(searchstr,0); 1441 #endif 1442 1443 c = searchit(curwin, curbuf, &pos, dirc == '/' ? FORWARD : BACKWARD, 1444 searchstr, count, spats[0].off.end + (options & 1445 (SEARCH_KEEP + SEARCH_PEEK + SEARCH_HIS 1446 + SEARCH_MSG + SEARCH_START 1447 + ((pat != NULL && *pat == ';') ? 0 : SEARCH_NOOF))), 1448 RE_LAST, (linenr_T)0, tm, timed_out); 1449 1450 if (dircp != NULL) 1451 *dircp = dirc; /* restore second '/' or '?' for normal_cmd() */ 1452 if (c == FAIL) 1453 { 1454 retval = 0; 1455 goto end_do_search; 1456 } 1457 if (spats[0].off.end && oap != NULL) 1458 oap->inclusive = TRUE; /* 'e' includes last character */ 1459 1460 retval = 1; /* pattern found */ 1461 1462 /* 1463 * Add character and/or line offset 1464 */ 1465 if (!(options & SEARCH_NOOF) || (pat != NULL && *pat == ';')) 1466 { 1467 if (spats[0].off.line) /* Add the offset to the line number. */ 1468 { 1469 c = pos.lnum + spats[0].off.off; 1470 if (c < 1) 1471 pos.lnum = 1; 1472 else if (c > curbuf->b_ml.ml_line_count) 1473 pos.lnum = curbuf->b_ml.ml_line_count; 1474 else 1475 pos.lnum = c; 1476 pos.col = 0; 1477 1478 retval = 2; /* pattern found, line offset added */ 1479 } 1480 else if (pos.col < MAXCOL - 2) /* just in case */ 1481 { 1482 /* to the right, check for end of file */ 1483 c = spats[0].off.off; 1484 if (c > 0) 1485 { 1486 while (c-- > 0) 1487 if (incl(&pos) == -1) 1488 break; 1489 } 1490 /* to the left, check for start of file */ 1491 else 1492 { 1493 while (c++ < 0) 1494 if (decl(&pos) == -1) 1495 break; 1496 } 1497 } 1498 } 1499 1500 /* 1501 * The search command can be followed by a ';' to do another search. 1502 * For example: "/pat/;/foo/+3;?bar" 1503 * This is like doing another search command, except: 1504 * - The remembered direction '/' or '?' is from the first search. 1505 * - When an error happens the cursor isn't moved at all. 1506 * Don't do this when called by get_address() (it handles ';' itself). 1507 */ 1508 if (!(options & SEARCH_OPT) || pat == NULL || *pat != ';') 1509 break; 1510 1511 dirc = *++pat; 1512 if (dirc != '?' && dirc != '/') 1513 { 1514 retval = 0; 1515 EMSG(_("E386: Expected '?' or '/' after ';'")); 1516 goto end_do_search; 1517 } 1518 ++pat; 1519 } 1520 1521 if (options & SEARCH_MARK) 1522 setpcmark(); 1523 curwin->w_cursor = pos; 1524 curwin->w_set_curswant = TRUE; 1525 1526 end_do_search: 1527 if ((options & SEARCH_KEEP) || cmdmod.keeppatterns) 1528 spats[0].off = old_off; 1529 vim_free(strcopy); 1530 1531 return retval; 1532 } 1533 1534 #if defined(FEAT_INS_EXPAND) || defined(PROTO) 1535 /* 1536 * search_for_exact_line(buf, pos, dir, pat) 1537 * 1538 * Search for a line starting with the given pattern (ignoring leading 1539 * white-space), starting from pos and going in direction "dir". "pos" will 1540 * contain the position of the match found. Blank lines match only if 1541 * ADDING is set. If p_ic is set then the pattern must be in lowercase. 1542 * Return OK for success, or FAIL if no line found. 1543 */ 1544 int 1545 search_for_exact_line( 1546 buf_T *buf, 1547 pos_T *pos, 1548 int dir, 1549 char_u *pat) 1550 { 1551 linenr_T start = 0; 1552 char_u *ptr; 1553 char_u *p; 1554 1555 if (buf->b_ml.ml_line_count == 0) 1556 return FAIL; 1557 for (;;) 1558 { 1559 pos->lnum += dir; 1560 if (pos->lnum < 1) 1561 { 1562 if (p_ws) 1563 { 1564 pos->lnum = buf->b_ml.ml_line_count; 1565 if (!shortmess(SHM_SEARCH)) 1566 give_warning((char_u *)_(top_bot_msg), TRUE); 1567 } 1568 else 1569 { 1570 pos->lnum = 1; 1571 break; 1572 } 1573 } 1574 else if (pos->lnum > buf->b_ml.ml_line_count) 1575 { 1576 if (p_ws) 1577 { 1578 pos->lnum = 1; 1579 if (!shortmess(SHM_SEARCH)) 1580 give_warning((char_u *)_(bot_top_msg), TRUE); 1581 } 1582 else 1583 { 1584 pos->lnum = 1; 1585 break; 1586 } 1587 } 1588 if (pos->lnum == start) 1589 break; 1590 if (start == 0) 1591 start = pos->lnum; 1592 ptr = ml_get_buf(buf, pos->lnum, FALSE); 1593 p = skipwhite(ptr); 1594 pos->col = (colnr_T) (p - ptr); 1595 1596 /* when adding lines the matching line may be empty but it is not 1597 * ignored because we are interested in the next line -- Acevedo */ 1598 if ((compl_cont_status & CONT_ADDING) 1599 && !(compl_cont_status & CONT_SOL)) 1600 { 1601 if ((p_ic ? MB_STRICMP(p, pat) : STRCMP(p, pat)) == 0) 1602 return OK; 1603 } 1604 else if (*p != NUL) /* ignore empty lines */ 1605 { /* expanding lines or words */ 1606 if ((p_ic ? MB_STRNICMP(p, pat, compl_length) 1607 : STRNCMP(p, pat, compl_length)) == 0) 1608 return OK; 1609 } 1610 } 1611 return FAIL; 1612 } 1613 #endif /* FEAT_INS_EXPAND */ 1614 1615 /* 1616 * Character Searches 1617 */ 1618 1619 /* 1620 * Search for a character in a line. If "t_cmd" is FALSE, move to the 1621 * position of the character, otherwise move to just before the char. 1622 * Do this "cap->count1" times. 1623 * Return FAIL or OK. 1624 */ 1625 int 1626 searchc(cmdarg_T *cap, int t_cmd) 1627 { 1628 int c = cap->nchar; /* char to search for */ 1629 int dir = cap->arg; /* TRUE for searching forward */ 1630 long count = cap->count1; /* repeat count */ 1631 int col; 1632 char_u *p; 1633 int len; 1634 int stop = TRUE; 1635 1636 if (c != NUL) /* normal search: remember args for repeat */ 1637 { 1638 if (!KeyStuffed) /* don't remember when redoing */ 1639 { 1640 *lastc = c; 1641 set_csearch_direction(dir); 1642 set_csearch_until(t_cmd); 1643 #ifdef FEAT_MBYTE 1644 lastc_bytelen = (*mb_char2bytes)(c, lastc_bytes); 1645 if (cap->ncharC1 != 0) 1646 { 1647 lastc_bytelen += (*mb_char2bytes)(cap->ncharC1, 1648 lastc_bytes + lastc_bytelen); 1649 if (cap->ncharC2 != 0) 1650 lastc_bytelen += (*mb_char2bytes)(cap->ncharC2, 1651 lastc_bytes + lastc_bytelen); 1652 } 1653 #endif 1654 } 1655 } 1656 else /* repeat previous search */ 1657 { 1658 if (*lastc == NUL 1659 #ifdef FEAT_MBYTE 1660 && lastc_bytelen == 1 1661 #endif 1662 ) 1663 return FAIL; 1664 if (dir) /* repeat in opposite direction */ 1665 dir = -lastcdir; 1666 else 1667 dir = lastcdir; 1668 t_cmd = last_t_cmd; 1669 c = *lastc; 1670 /* For multi-byte re-use last lastc_bytes[] and lastc_bytelen. */ 1671 1672 /* Force a move of at least one char, so ";" and "," will move the 1673 * cursor, even if the cursor is right in front of char we are looking 1674 * at. */ 1675 if (vim_strchr(p_cpo, CPO_SCOLON) == NULL && count == 1 && t_cmd) 1676 stop = FALSE; 1677 } 1678 1679 if (dir == BACKWARD) 1680 cap->oap->inclusive = FALSE; 1681 else 1682 cap->oap->inclusive = TRUE; 1683 1684 p = ml_get_curline(); 1685 col = curwin->w_cursor.col; 1686 len = (int)STRLEN(p); 1687 1688 while (count--) 1689 { 1690 #ifdef FEAT_MBYTE 1691 if (has_mbyte) 1692 { 1693 for (;;) 1694 { 1695 if (dir > 0) 1696 { 1697 col += (*mb_ptr2len)(p + col); 1698 if (col >= len) 1699 return FAIL; 1700 } 1701 else 1702 { 1703 if (col == 0) 1704 return FAIL; 1705 col -= (*mb_head_off)(p, p + col - 1) + 1; 1706 } 1707 if (lastc_bytelen == 1) 1708 { 1709 if (p[col] == c && stop) 1710 break; 1711 } 1712 else if (STRNCMP(p + col, lastc_bytes, lastc_bytelen) == 0 1713 && stop) 1714 break; 1715 stop = TRUE; 1716 } 1717 } 1718 else 1719 #endif 1720 { 1721 for (;;) 1722 { 1723 if ((col += dir) < 0 || col >= len) 1724 return FAIL; 1725 if (p[col] == c && stop) 1726 break; 1727 stop = TRUE; 1728 } 1729 } 1730 } 1731 1732 if (t_cmd) 1733 { 1734 /* backup to before the character (possibly double-byte) */ 1735 col -= dir; 1736 #ifdef FEAT_MBYTE 1737 if (has_mbyte) 1738 { 1739 if (dir < 0) 1740 /* Landed on the search char which is lastc_bytelen long */ 1741 col += lastc_bytelen - 1; 1742 else 1743 /* To previous char, which may be multi-byte. */ 1744 col -= (*mb_head_off)(p, p + col); 1745 } 1746 #endif 1747 } 1748 curwin->w_cursor.col = col; 1749 1750 return OK; 1751 } 1752 1753 /* 1754 * "Other" Searches 1755 */ 1756 1757 /* 1758 * findmatch - find the matching paren or brace 1759 * 1760 * Improvement over vi: Braces inside quotes are ignored. 1761 */ 1762 pos_T * 1763 findmatch(oparg_T *oap, int initc) 1764 { 1765 return findmatchlimit(oap, initc, 0, 0); 1766 } 1767 1768 /* 1769 * Return TRUE if the character before "linep[col]" equals "ch". 1770 * Return FALSE if "col" is zero. 1771 * Update "*prevcol" to the column of the previous character, unless "prevcol" 1772 * is NULL. 1773 * Handles multibyte string correctly. 1774 */ 1775 static int 1776 check_prevcol( 1777 char_u *linep, 1778 int col, 1779 int ch, 1780 int *prevcol) 1781 { 1782 --col; 1783 #ifdef FEAT_MBYTE 1784 if (col > 0 && has_mbyte) 1785 col -= (*mb_head_off)(linep, linep + col); 1786 #endif 1787 if (prevcol) 1788 *prevcol = col; 1789 return (col >= 0 && linep[col] == ch) ? TRUE : FALSE; 1790 } 1791 1792 static int find_rawstring_end(char_u *linep, pos_T *startpos, pos_T *endpos); 1793 1794 /* 1795 * Raw string start is found at linep[startpos.col - 1]. 1796 * Return TRUE if the matching end can be found between startpos and endpos. 1797 */ 1798 static int 1799 find_rawstring_end(char_u *linep, pos_T *startpos, pos_T *endpos) 1800 { 1801 char_u *p; 1802 char_u *delim_copy; 1803 size_t delim_len; 1804 linenr_T lnum; 1805 int found = FALSE; 1806 1807 for (p = linep + startpos->col + 1; *p && *p != '('; ++p) 1808 ; 1809 delim_len = (p - linep) - startpos->col - 1; 1810 delim_copy = vim_strnsave(linep + startpos->col + 1, (int)delim_len); 1811 if (delim_copy == NULL) 1812 return FALSE; 1813 for (lnum = startpos->lnum; lnum <= endpos->lnum; ++lnum) 1814 { 1815 char_u *line = ml_get(lnum); 1816 1817 for (p = line + (lnum == startpos->lnum 1818 ? startpos->col + 1 : 0); *p; ++p) 1819 { 1820 if (lnum == endpos->lnum && (colnr_T)(p - line) >= endpos->col) 1821 break; 1822 if (*p == ')' && p[delim_len + 1] == '"' 1823 && STRNCMP(delim_copy, p + 1, delim_len) == 0) 1824 { 1825 found = TRUE; 1826 break; 1827 } 1828 } 1829 if (found) 1830 break; 1831 } 1832 vim_free(delim_copy); 1833 return found; 1834 } 1835 1836 /* 1837 * findmatchlimit -- find the matching paren or brace, if it exists within 1838 * maxtravel lines of the cursor. A maxtravel of 0 means search until falling 1839 * off the edge of the file. 1840 * 1841 * "initc" is the character to find a match for. NUL means to find the 1842 * character at or after the cursor. Special values: 1843 * '*' look for C-style comment / * 1844 * '/' look for C-style comment / *, ignoring comment-end 1845 * '#' look for preprocessor directives 1846 * 'R' look for raw string start: R"delim(text)delim" (only backwards) 1847 * 1848 * flags: FM_BACKWARD search backwards (when initc is '/', '*' or '#') 1849 * FM_FORWARD search forwards (when initc is '/', '*' or '#') 1850 * FM_BLOCKSTOP stop at start/end of block ({ or } in column 0) 1851 * FM_SKIPCOMM skip comments (not implemented yet!) 1852 * 1853 * "oap" is only used to set oap->motion_type for a linewise motion, it can be 1854 * NULL 1855 */ 1856 1857 pos_T * 1858 findmatchlimit( 1859 oparg_T *oap, 1860 int initc, 1861 int flags, 1862 int maxtravel) 1863 { 1864 static pos_T pos; /* current search position */ 1865 int findc = 0; /* matching brace */ 1866 int c; 1867 int count = 0; /* cumulative number of braces */ 1868 int backwards = FALSE; /* init for gcc */ 1869 int raw_string = FALSE; /* search for raw string */ 1870 int inquote = FALSE; /* TRUE when inside quotes */ 1871 char_u *linep; /* pointer to current line */ 1872 char_u *ptr; 1873 int do_quotes; /* check for quotes in current line */ 1874 int at_start; /* do_quotes value at start position */ 1875 int hash_dir = 0; /* Direction searched for # things */ 1876 int comment_dir = 0; /* Direction searched for comments */ 1877 pos_T match_pos; /* Where last slash-star was found */ 1878 int start_in_quotes; /* start position is in quotes */ 1879 int traveled = 0; /* how far we've searched so far */ 1880 int ignore_cend = FALSE; /* ignore comment end */ 1881 int cpo_match; /* vi compatible matching */ 1882 int cpo_bsl; /* don't recognize backslashes */ 1883 int match_escaped = 0; /* search for escaped match */ 1884 int dir; /* Direction to search */ 1885 int comment_col = MAXCOL; /* start of / / comment */ 1886 #ifdef FEAT_LISP 1887 int lispcomm = FALSE; /* inside of Lisp-style comment */ 1888 int lisp = curbuf->b_p_lisp; /* engage Lisp-specific hacks ;) */ 1889 #endif 1890 1891 pos = curwin->w_cursor; 1892 #ifdef FEAT_VIRTUALEDIT 1893 pos.coladd = 0; 1894 #endif 1895 linep = ml_get(pos.lnum); 1896 1897 cpo_match = (vim_strchr(p_cpo, CPO_MATCH) != NULL); 1898 cpo_bsl = (vim_strchr(p_cpo, CPO_MATCHBSL) != NULL); 1899 1900 /* Direction to search when initc is '/', '*' or '#' */ 1901 if (flags & FM_BACKWARD) 1902 dir = BACKWARD; 1903 else if (flags & FM_FORWARD) 1904 dir = FORWARD; 1905 else 1906 dir = 0; 1907 1908 /* 1909 * if initc given, look in the table for the matching character 1910 * '/' and '*' are special cases: look for start or end of comment. 1911 * When '/' is used, we ignore running backwards into an star-slash, for 1912 * "[*" command, we just want to find any comment. 1913 */ 1914 if (initc == '/' || initc == '*' || initc == 'R') 1915 { 1916 comment_dir = dir; 1917 if (initc == '/') 1918 ignore_cend = TRUE; 1919 backwards = (dir == FORWARD) ? FALSE : TRUE; 1920 raw_string = (initc == 'R'); 1921 initc = NUL; 1922 } 1923 else if (initc != '#' && initc != NUL) 1924 { 1925 find_mps_values(&initc, &findc, &backwards, TRUE); 1926 if (findc == NUL) 1927 return NULL; 1928 } 1929 else 1930 { 1931 /* 1932 * Either initc is '#', or no initc was given and we need to look 1933 * under the cursor. 1934 */ 1935 if (initc == '#') 1936 { 1937 hash_dir = dir; 1938 } 1939 else 1940 { 1941 /* 1942 * initc was not given, must look for something to match under 1943 * or near the cursor. 1944 * Only check for special things when 'cpo' doesn't have '%'. 1945 */ 1946 if (!cpo_match) 1947 { 1948 /* Are we before or at #if, #else etc.? */ 1949 ptr = skipwhite(linep); 1950 if (*ptr == '#' && pos.col <= (colnr_T)(ptr - linep)) 1951 { 1952 ptr = skipwhite(ptr + 1); 1953 if ( STRNCMP(ptr, "if", 2) == 0 1954 || STRNCMP(ptr, "endif", 5) == 0 1955 || STRNCMP(ptr, "el", 2) == 0) 1956 hash_dir = 1; 1957 } 1958 1959 /* Are we on a comment? */ 1960 else if (linep[pos.col] == '/') 1961 { 1962 if (linep[pos.col + 1] == '*') 1963 { 1964 comment_dir = FORWARD; 1965 backwards = FALSE; 1966 pos.col++; 1967 } 1968 else if (pos.col > 0 && linep[pos.col - 1] == '*') 1969 { 1970 comment_dir = BACKWARD; 1971 backwards = TRUE; 1972 pos.col--; 1973 } 1974 } 1975 else if (linep[pos.col] == '*') 1976 { 1977 if (linep[pos.col + 1] == '/') 1978 { 1979 comment_dir = BACKWARD; 1980 backwards = TRUE; 1981 } 1982 else if (pos.col > 0 && linep[pos.col - 1] == '/') 1983 { 1984 comment_dir = FORWARD; 1985 backwards = FALSE; 1986 } 1987 } 1988 } 1989 1990 /* 1991 * If we are not on a comment or the # at the start of a line, then 1992 * look for brace anywhere on this line after the cursor. 1993 */ 1994 if (!hash_dir && !comment_dir) 1995 { 1996 /* 1997 * Find the brace under or after the cursor. 1998 * If beyond the end of the line, use the last character in 1999 * the line. 2000 */ 2001 if (linep[pos.col] == NUL && pos.col) 2002 --pos.col; 2003 for (;;) 2004 { 2005 initc = PTR2CHAR(linep + pos.col); 2006 if (initc == NUL) 2007 break; 2008 2009 find_mps_values(&initc, &findc, &backwards, FALSE); 2010 if (findc) 2011 break; 2012 pos.col += MB_PTR2LEN(linep + pos.col); 2013 } 2014 if (!findc) 2015 { 2016 /* no brace in the line, maybe use " #if" then */ 2017 if (!cpo_match && *skipwhite(linep) == '#') 2018 hash_dir = 1; 2019 else 2020 return NULL; 2021 } 2022 else if (!cpo_bsl) 2023 { 2024 int col, bslcnt = 0; 2025 2026 /* Set "match_escaped" if there are an odd number of 2027 * backslashes. */ 2028 for (col = pos.col; check_prevcol(linep, col, '\\', &col);) 2029 bslcnt++; 2030 match_escaped = (bslcnt & 1); 2031 } 2032 } 2033 } 2034 if (hash_dir) 2035 { 2036 /* 2037 * Look for matching #if, #else, #elif, or #endif 2038 */ 2039 if (oap != NULL) 2040 oap->motion_type = MLINE; /* Linewise for this case only */ 2041 if (initc != '#') 2042 { 2043 ptr = skipwhite(skipwhite(linep) + 1); 2044 if (STRNCMP(ptr, "if", 2) == 0 || STRNCMP(ptr, "el", 2) == 0) 2045 hash_dir = 1; 2046 else if (STRNCMP(ptr, "endif", 5) == 0) 2047 hash_dir = -1; 2048 else 2049 return NULL; 2050 } 2051 pos.col = 0; 2052 while (!got_int) 2053 { 2054 if (hash_dir > 0) 2055 { 2056 if (pos.lnum == curbuf->b_ml.ml_line_count) 2057 break; 2058 } 2059 else if (pos.lnum == 1) 2060 break; 2061 pos.lnum += hash_dir; 2062 linep = ml_get(pos.lnum); 2063 line_breakcheck(); /* check for CTRL-C typed */ 2064 ptr = skipwhite(linep); 2065 if (*ptr != '#') 2066 continue; 2067 pos.col = (colnr_T) (ptr - linep); 2068 ptr = skipwhite(ptr + 1); 2069 if (hash_dir > 0) 2070 { 2071 if (STRNCMP(ptr, "if", 2) == 0) 2072 count++; 2073 else if (STRNCMP(ptr, "el", 2) == 0) 2074 { 2075 if (count == 0) 2076 return &pos; 2077 } 2078 else if (STRNCMP(ptr, "endif", 5) == 0) 2079 { 2080 if (count == 0) 2081 return &pos; 2082 count--; 2083 } 2084 } 2085 else 2086 { 2087 if (STRNCMP(ptr, "if", 2) == 0) 2088 { 2089 if (count == 0) 2090 return &pos; 2091 count--; 2092 } 2093 else if (initc == '#' && STRNCMP(ptr, "el", 2) == 0) 2094 { 2095 if (count == 0) 2096 return &pos; 2097 } 2098 else if (STRNCMP(ptr, "endif", 5) == 0) 2099 count++; 2100 } 2101 } 2102 return NULL; 2103 } 2104 } 2105 2106 #ifdef FEAT_RIGHTLEFT 2107 /* This is just guessing: when 'rightleft' is set, search for a matching 2108 * paren/brace in the other direction. */ 2109 if (curwin->w_p_rl && vim_strchr((char_u *)"()[]{}<>", initc) != NULL) 2110 backwards = !backwards; 2111 #endif 2112 2113 do_quotes = -1; 2114 start_in_quotes = MAYBE; 2115 CLEAR_POS(&match_pos); 2116 2117 /* backward search: Check if this line contains a single-line comment */ 2118 if ((backwards && comment_dir) 2119 #ifdef FEAT_LISP 2120 || lisp 2121 #endif 2122 ) 2123 comment_col = check_linecomment(linep); 2124 #ifdef FEAT_LISP 2125 if (lisp && comment_col != MAXCOL && pos.col > (colnr_T)comment_col) 2126 lispcomm = TRUE; /* find match inside this comment */ 2127 #endif 2128 while (!got_int) 2129 { 2130 /* 2131 * Go to the next position, forward or backward. We could use 2132 * inc() and dec() here, but that is much slower 2133 */ 2134 if (backwards) 2135 { 2136 #ifdef FEAT_LISP 2137 /* char to match is inside of comment, don't search outside */ 2138 if (lispcomm && pos.col < (colnr_T)comment_col) 2139 break; 2140 #endif 2141 if (pos.col == 0) /* at start of line, go to prev. one */ 2142 { 2143 if (pos.lnum == 1) /* start of file */ 2144 break; 2145 --pos.lnum; 2146 2147 if (maxtravel > 0 && ++traveled > maxtravel) 2148 break; 2149 2150 linep = ml_get(pos.lnum); 2151 pos.col = (colnr_T)STRLEN(linep); /* pos.col on trailing NUL */ 2152 do_quotes = -1; 2153 line_breakcheck(); 2154 2155 /* Check if this line contains a single-line comment */ 2156 if (comment_dir 2157 #ifdef FEAT_LISP 2158 || lisp 2159 #endif 2160 ) 2161 comment_col = check_linecomment(linep); 2162 #ifdef FEAT_LISP 2163 /* skip comment */ 2164 if (lisp && comment_col != MAXCOL) 2165 pos.col = comment_col; 2166 #endif 2167 } 2168 else 2169 { 2170 --pos.col; 2171 #ifdef FEAT_MBYTE 2172 if (has_mbyte) 2173 pos.col -= (*mb_head_off)(linep, linep + pos.col); 2174 #endif 2175 } 2176 } 2177 else /* forward search */ 2178 { 2179 if (linep[pos.col] == NUL 2180 /* at end of line, go to next one */ 2181 #ifdef FEAT_LISP 2182 /* don't search for match in comment */ 2183 || (lisp && comment_col != MAXCOL 2184 && pos.col == (colnr_T)comment_col) 2185 #endif 2186 ) 2187 { 2188 if (pos.lnum == curbuf->b_ml.ml_line_count /* end of file */ 2189 #ifdef FEAT_LISP 2190 /* line is exhausted and comment with it, 2191 * don't search for match in code */ 2192 || lispcomm 2193 #endif 2194 ) 2195 break; 2196 ++pos.lnum; 2197 2198 if (maxtravel && traveled++ > maxtravel) 2199 break; 2200 2201 linep = ml_get(pos.lnum); 2202 pos.col = 0; 2203 do_quotes = -1; 2204 line_breakcheck(); 2205 #ifdef FEAT_LISP 2206 if (lisp) /* find comment pos in new line */ 2207 comment_col = check_linecomment(linep); 2208 #endif 2209 } 2210 else 2211 { 2212 #ifdef FEAT_MBYTE 2213 if (has_mbyte) 2214 pos.col += (*mb_ptr2len)(linep + pos.col); 2215 else 2216 #endif 2217 ++pos.col; 2218 } 2219 } 2220 2221 /* 2222 * If FM_BLOCKSTOP given, stop at a '{' or '}' in column 0. 2223 */ 2224 if (pos.col == 0 && (flags & FM_BLOCKSTOP) && 2225 (linep[0] == '{' || linep[0] == '}')) 2226 { 2227 if (linep[0] == findc && count == 0) /* match! */ 2228 return &pos; 2229 break; /* out of scope */ 2230 } 2231 2232 if (comment_dir) 2233 { 2234 /* Note: comments do not nest, and we ignore quotes in them */ 2235 /* TODO: ignore comment brackets inside strings */ 2236 if (comment_dir == FORWARD) 2237 { 2238 if (linep[pos.col] == '*' && linep[pos.col + 1] == '/') 2239 { 2240 pos.col++; 2241 return &pos; 2242 } 2243 } 2244 else /* Searching backwards */ 2245 { 2246 /* 2247 * A comment may contain / * or / /, it may also start or end 2248 * with / * /. Ignore a / * after / /. 2249 */ 2250 if (pos.col == 0) 2251 continue; 2252 else if (raw_string) 2253 { 2254 if (linep[pos.col - 1] == 'R' 2255 && linep[pos.col] == '"' 2256 && vim_strchr(linep + pos.col + 1, '(') != NULL) 2257 { 2258 /* Possible start of raw string. Now that we have the 2259 * delimiter we can check if it ends before where we 2260 * started searching, or before the previously found 2261 * raw string start. */ 2262 if (!find_rawstring_end(linep, &pos, 2263 count > 0 ? &match_pos : &curwin->w_cursor)) 2264 { 2265 count++; 2266 match_pos = pos; 2267 match_pos.col--; 2268 } 2269 linep = ml_get(pos.lnum); /* may have been released */ 2270 } 2271 } 2272 else if ( linep[pos.col - 1] == '/' 2273 && linep[pos.col] == '*' 2274 && (int)pos.col < comment_col) 2275 { 2276 count++; 2277 match_pos = pos; 2278 match_pos.col--; 2279 } 2280 else if (linep[pos.col - 1] == '*' && linep[pos.col] == '/') 2281 { 2282 if (count > 0) 2283 pos = match_pos; 2284 else if (pos.col > 1 && linep[pos.col - 2] == '/' 2285 && (int)pos.col <= comment_col) 2286 pos.col -= 2; 2287 else if (ignore_cend) 2288 continue; 2289 else 2290 return NULL; 2291 return &pos; 2292 } 2293 } 2294 continue; 2295 } 2296 2297 /* 2298 * If smart matching ('cpoptions' does not contain '%'), braces inside 2299 * of quotes are ignored, but only if there is an even number of 2300 * quotes in the line. 2301 */ 2302 if (cpo_match) 2303 do_quotes = 0; 2304 else if (do_quotes == -1) 2305 { 2306 /* 2307 * Count the number of quotes in the line, skipping \" and '"'. 2308 * Watch out for "\\". 2309 */ 2310 at_start = do_quotes; 2311 for (ptr = linep; *ptr; ++ptr) 2312 { 2313 if (ptr == linep + pos.col + backwards) 2314 at_start = (do_quotes & 1); 2315 if (*ptr == '"' 2316 && (ptr == linep || ptr[-1] != '\'' || ptr[1] != '\'')) 2317 ++do_quotes; 2318 if (*ptr == '\\' && ptr[1] != NUL) 2319 ++ptr; 2320 } 2321 do_quotes &= 1; /* result is 1 with even number of quotes */ 2322 2323 /* 2324 * If we find an uneven count, check current line and previous 2325 * one for a '\' at the end. 2326 */ 2327 if (!do_quotes) 2328 { 2329 inquote = FALSE; 2330 if (ptr[-1] == '\\') 2331 { 2332 do_quotes = 1; 2333 if (start_in_quotes == MAYBE) 2334 { 2335 /* Do we need to use at_start here? */ 2336 inquote = TRUE; 2337 start_in_quotes = TRUE; 2338 } 2339 else if (backwards) 2340 inquote = TRUE; 2341 } 2342 if (pos.lnum > 1) 2343 { 2344 ptr = ml_get(pos.lnum - 1); 2345 if (*ptr && *(ptr + STRLEN(ptr) - 1) == '\\') 2346 { 2347 do_quotes = 1; 2348 if (start_in_quotes == MAYBE) 2349 { 2350 inquote = at_start; 2351 if (inquote) 2352 start_in_quotes = TRUE; 2353 } 2354 else if (!backwards) 2355 inquote = TRUE; 2356 } 2357 2358 /* ml_get() only keeps one line, need to get linep again */ 2359 linep = ml_get(pos.lnum); 2360 } 2361 } 2362 } 2363 if (start_in_quotes == MAYBE) 2364 start_in_quotes = FALSE; 2365 2366 /* 2367 * If 'smartmatch' is set: 2368 * Things inside quotes are ignored by setting 'inquote'. If we 2369 * find a quote without a preceding '\' invert 'inquote'. At the 2370 * end of a line not ending in '\' we reset 'inquote'. 2371 * 2372 * In lines with an uneven number of quotes (without preceding '\') 2373 * we do not know which part to ignore. Therefore we only set 2374 * inquote if the number of quotes in a line is even, unless this 2375 * line or the previous one ends in a '\'. Complicated, isn't it? 2376 */ 2377 c = PTR2CHAR(linep + pos.col); 2378 switch (c) 2379 { 2380 case NUL: 2381 /* at end of line without trailing backslash, reset inquote */ 2382 if (pos.col == 0 || linep[pos.col - 1] != '\\') 2383 { 2384 inquote = FALSE; 2385 start_in_quotes = FALSE; 2386 } 2387 break; 2388 2389 case '"': 2390 /* a quote that is preceded with an odd number of backslashes is 2391 * ignored */ 2392 if (do_quotes) 2393 { 2394 int col; 2395 2396 for (col = pos.col - 1; col >= 0; --col) 2397 if (linep[col] != '\\') 2398 break; 2399 if ((((int)pos.col - 1 - col) & 1) == 0) 2400 { 2401 inquote = !inquote; 2402 start_in_quotes = FALSE; 2403 } 2404 } 2405 break; 2406 2407 /* 2408 * If smart matching ('cpoptions' does not contain '%'): 2409 * Skip things in single quotes: 'x' or '\x'. Be careful for single 2410 * single quotes, eg jon's. Things like '\233' or '\x3f' are not 2411 * skipped, there is never a brace in them. 2412 * Ignore this when finding matches for `'. 2413 */ 2414 case '\'': 2415 if (!cpo_match && initc != '\'' && findc != '\'') 2416 { 2417 if (backwards) 2418 { 2419 if (pos.col > 1) 2420 { 2421 if (linep[pos.col - 2] == '\'') 2422 { 2423 pos.col -= 2; 2424 break; 2425 } 2426 else if (linep[pos.col - 2] == '\\' && 2427 pos.col > 2 && linep[pos.col - 3] == '\'') 2428 { 2429 pos.col -= 3; 2430 break; 2431 } 2432 } 2433 } 2434 else if (linep[pos.col + 1]) /* forward search */ 2435 { 2436 if (linep[pos.col + 1] == '\\' && 2437 linep[pos.col + 2] && linep[pos.col + 3] == '\'') 2438 { 2439 pos.col += 3; 2440 break; 2441 } 2442 else if (linep[pos.col + 2] == '\'') 2443 { 2444 pos.col += 2; 2445 break; 2446 } 2447 } 2448 } 2449 /* FALLTHROUGH */ 2450 2451 default: 2452 #ifdef FEAT_LISP 2453 /* 2454 * For Lisp skip over backslashed (), {} and []. 2455 * (actually, we skip #\( et al) 2456 */ 2457 if (curbuf->b_p_lisp 2458 && vim_strchr((char_u *)"(){}[]", c) != NULL 2459 && pos.col > 1 2460 && check_prevcol(linep, pos.col, '\\', NULL) 2461 && check_prevcol(linep, pos.col - 1, '#', NULL)) 2462 break; 2463 #endif 2464 2465 /* Check for match outside of quotes, and inside of 2466 * quotes when the start is also inside of quotes. */ 2467 if ((!inquote || start_in_quotes == TRUE) 2468 && (c == initc || c == findc)) 2469 { 2470 int col, bslcnt = 0; 2471 2472 if (!cpo_bsl) 2473 { 2474 for (col = pos.col; check_prevcol(linep, col, '\\', &col);) 2475 bslcnt++; 2476 } 2477 /* Only accept a match when 'M' is in 'cpo' or when escaping 2478 * is what we expect. */ 2479 if (cpo_bsl || (bslcnt & 1) == match_escaped) 2480 { 2481 if (c == initc) 2482 count++; 2483 else 2484 { 2485 if (count == 0) 2486 return &pos; 2487 count--; 2488 } 2489 } 2490 } 2491 } 2492 } 2493 2494 if (comment_dir == BACKWARD && count > 0) 2495 { 2496 pos = match_pos; 2497 return &pos; 2498 } 2499 return (pos_T *)NULL; /* never found it */ 2500 } 2501 2502 /* 2503 * Check if line[] contains a / / comment. 2504 * Return MAXCOL if not, otherwise return the column. 2505 * TODO: skip strings. 2506 */ 2507 static int 2508 check_linecomment(char_u *line) 2509 { 2510 char_u *p; 2511 2512 p = line; 2513 #ifdef FEAT_LISP 2514 /* skip Lispish one-line comments */ 2515 if (curbuf->b_p_lisp) 2516 { 2517 if (vim_strchr(p, ';') != NULL) /* there may be comments */ 2518 { 2519 int in_str = FALSE; /* inside of string */ 2520 2521 p = line; /* scan from start */ 2522 while ((p = vim_strpbrk(p, (char_u *)"\";")) != NULL) 2523 { 2524 if (*p == '"') 2525 { 2526 if (in_str) 2527 { 2528 if (*(p - 1) != '\\') /* skip escaped quote */ 2529 in_str = FALSE; 2530 } 2531 else if (p == line || ((p - line) >= 2 2532 /* skip #\" form */ 2533 && *(p - 1) != '\\' && *(p - 2) != '#')) 2534 in_str = TRUE; 2535 } 2536 else if (!in_str && ((p - line) < 2 2537 || (*(p - 1) != '\\' && *(p - 2) != '#'))) 2538 break; /* found! */ 2539 ++p; 2540 } 2541 } 2542 else 2543 p = NULL; 2544 } 2545 else 2546 #endif 2547 while ((p = vim_strchr(p, '/')) != NULL) 2548 { 2549 /* accept a double /, unless it's preceded with * and followed by *, 2550 * because * / / * is an end and start of a C comment */ 2551 if (p[1] == '/' && (p == line || p[-1] != '*' || p[2] != '*')) 2552 break; 2553 ++p; 2554 } 2555 2556 if (p == NULL) 2557 return MAXCOL; 2558 return (int)(p - line); 2559 } 2560 2561 /* 2562 * Move cursor briefly to character matching the one under the cursor. 2563 * Used for Insert mode and "r" command. 2564 * Show the match only if it is visible on the screen. 2565 * If there isn't a match, then beep. 2566 */ 2567 void 2568 showmatch( 2569 int c) /* char to show match for */ 2570 { 2571 pos_T *lpos, save_cursor; 2572 pos_T mpos; 2573 colnr_T vcol; 2574 long save_so; 2575 long save_siso; 2576 #ifdef CURSOR_SHAPE 2577 int save_state; 2578 #endif 2579 colnr_T save_dollar_vcol; 2580 char_u *p; 2581 2582 /* 2583 * Only show match for chars in the 'matchpairs' option. 2584 */ 2585 /* 'matchpairs' is "x:y,x:y" */ 2586 for (p = curbuf->b_p_mps; *p != NUL; ++p) 2587 { 2588 #ifdef FEAT_RIGHTLEFT 2589 if (PTR2CHAR(p) == c && (curwin->w_p_rl ^ p_ri)) 2590 break; 2591 #endif 2592 p += MB_PTR2LEN(p) + 1; 2593 if (PTR2CHAR(p) == c 2594 #ifdef FEAT_RIGHTLEFT 2595 && !(curwin->w_p_rl ^ p_ri) 2596 #endif 2597 ) 2598 break; 2599 p += MB_PTR2LEN(p); 2600 if (*p == NUL) 2601 return; 2602 } 2603 2604 if ((lpos = findmatch(NULL, NUL)) == NULL) /* no match, so beep */ 2605 vim_beep(BO_MATCH); 2606 else if (lpos->lnum >= curwin->w_topline && lpos->lnum < curwin->w_botline) 2607 { 2608 if (!curwin->w_p_wrap) 2609 getvcol(curwin, lpos, NULL, &vcol, NULL); 2610 if (curwin->w_p_wrap || (vcol >= curwin->w_leftcol 2611 && vcol < curwin->w_leftcol + W_WIDTH(curwin))) 2612 { 2613 mpos = *lpos; /* save the pos, update_screen() may change it */ 2614 save_cursor = curwin->w_cursor; 2615 save_so = p_so; 2616 save_siso = p_siso; 2617 /* Handle "$" in 'cpo': If the ')' is typed on top of the "$", 2618 * stop displaying the "$". */ 2619 if (dollar_vcol >= 0 && dollar_vcol == curwin->w_virtcol) 2620 dollar_vcol = -1; 2621 ++curwin->w_virtcol; /* do display ')' just before "$" */ 2622 update_screen(VALID); /* show the new char first */ 2623 2624 save_dollar_vcol = dollar_vcol; 2625 #ifdef CURSOR_SHAPE 2626 save_state = State; 2627 State = SHOWMATCH; 2628 ui_cursor_shape(); /* may show different cursor shape */ 2629 #endif 2630 curwin->w_cursor = mpos; /* move to matching char */ 2631 p_so = 0; /* don't use 'scrolloff' here */ 2632 p_siso = 0; /* don't use 'sidescrolloff' here */ 2633 showruler(FALSE); 2634 setcursor(); 2635 cursor_on(); /* make sure that the cursor is shown */ 2636 out_flush(); 2637 #ifdef FEAT_GUI 2638 if (gui.in_use) 2639 { 2640 gui_update_cursor(TRUE, FALSE); 2641 gui_mch_flush(); 2642 } 2643 #endif 2644 /* Restore dollar_vcol(), because setcursor() may call curs_rows() 2645 * which resets it if the matching position is in a previous line 2646 * and has a higher column number. */ 2647 dollar_vcol = save_dollar_vcol; 2648 2649 /* 2650 * brief pause, unless 'm' is present in 'cpo' and a character is 2651 * available. 2652 */ 2653 if (vim_strchr(p_cpo, CPO_SHOWMATCH) != NULL) 2654 ui_delay(p_mat * 100L, TRUE); 2655 else if (!char_avail()) 2656 ui_delay(p_mat * 100L, FALSE); 2657 curwin->w_cursor = save_cursor; /* restore cursor position */ 2658 p_so = save_so; 2659 p_siso = save_siso; 2660 #ifdef CURSOR_SHAPE 2661 State = save_state; 2662 ui_cursor_shape(); /* may show different cursor shape */ 2663 #endif 2664 } 2665 } 2666 } 2667 2668 /* 2669 * findsent(dir, count) - Find the start of the next sentence in direction 2670 * "dir" Sentences are supposed to end in ".", "!" or "?" followed by white 2671 * space or a line break. Also stop at an empty line. 2672 * Return OK if the next sentence was found. 2673 */ 2674 int 2675 findsent(int dir, long count) 2676 { 2677 pos_T pos, tpos; 2678 int c; 2679 int (*func)(pos_T *); 2680 int startlnum; 2681 int noskip = FALSE; /* do not skip blanks */ 2682 int cpo_J; 2683 int found_dot; 2684 2685 pos = curwin->w_cursor; 2686 if (dir == FORWARD) 2687 func = incl; 2688 else 2689 func = decl; 2690 2691 while (count--) 2692 { 2693 /* 2694 * if on an empty line, skip upto a non-empty line 2695 */ 2696 if (gchar_pos(&pos) == NUL) 2697 { 2698 do 2699 if ((*func)(&pos) == -1) 2700 break; 2701 while (gchar_pos(&pos) == NUL); 2702 if (dir == FORWARD) 2703 goto found; 2704 } 2705 /* 2706 * if on the start of a paragraph or a section and searching forward, 2707 * go to the next line 2708 */ 2709 else if (dir == FORWARD && pos.col == 0 && 2710 startPS(pos.lnum, NUL, FALSE)) 2711 { 2712 if (pos.lnum == curbuf->b_ml.ml_line_count) 2713 return FAIL; 2714 ++pos.lnum; 2715 goto found; 2716 } 2717 else if (dir == BACKWARD) 2718 decl(&pos); 2719 2720 /* go back to the previous non-blank char */ 2721 found_dot = FALSE; 2722 while ((c = gchar_pos(&pos)) == ' ' || c == '\t' || 2723 (dir == BACKWARD && vim_strchr((char_u *)".!?)]\"'", c) != NULL)) 2724 { 2725 if (vim_strchr((char_u *)".!?", c) != NULL) 2726 { 2727 /* Only skip over a '.', '!' and '?' once. */ 2728 if (found_dot) 2729 break; 2730 found_dot = TRUE; 2731 } 2732 if (decl(&pos) == -1) 2733 break; 2734 /* when going forward: Stop in front of empty line */ 2735 if (LINEEMPTY(pos.lnum) && dir == FORWARD) 2736 { 2737 incl(&pos); 2738 goto found; 2739 } 2740 } 2741 2742 /* remember the line where the search started */ 2743 startlnum = pos.lnum; 2744 cpo_J = vim_strchr(p_cpo, CPO_ENDOFSENT) != NULL; 2745 2746 for (;;) /* find end of sentence */ 2747 { 2748 c = gchar_pos(&pos); 2749 if (c == NUL || (pos.col == 0 && startPS(pos.lnum, NUL, FALSE))) 2750 { 2751 if (dir == BACKWARD && pos.lnum != startlnum) 2752 ++pos.lnum; 2753 break; 2754 } 2755 if (c == '.' || c == '!' || c == '?') 2756 { 2757 tpos = pos; 2758 do 2759 if ((c = inc(&tpos)) == -1) 2760 break; 2761 while (vim_strchr((char_u *)")]\"'", c = gchar_pos(&tpos)) 2762 != NULL); 2763 if (c == -1 || (!cpo_J && (c == ' ' || c == '\t')) || c == NUL 2764 || (cpo_J && (c == ' ' && inc(&tpos) >= 0 2765 && gchar_pos(&tpos) == ' '))) 2766 { 2767 pos = tpos; 2768 if (gchar_pos(&pos) == NUL) /* skip NUL at EOL */ 2769 inc(&pos); 2770 break; 2771 } 2772 } 2773 if ((*func)(&pos) == -1) 2774 { 2775 if (count) 2776 return FAIL; 2777 noskip = TRUE; 2778 break; 2779 } 2780 } 2781 found: 2782 /* skip white space */ 2783 while (!noskip && ((c = gchar_pos(&pos)) == ' ' || c == '\t')) 2784 if (incl(&pos) == -1) 2785 break; 2786 } 2787 2788 setpcmark(); 2789 curwin->w_cursor = pos; 2790 return OK; 2791 } 2792 2793 /* 2794 * Find the next paragraph or section in direction 'dir'. 2795 * Paragraphs are currently supposed to be separated by empty lines. 2796 * If 'what' is NUL we go to the next paragraph. 2797 * If 'what' is '{' or '}' we go to the next section. 2798 * If 'both' is TRUE also stop at '}'. 2799 * Return TRUE if the next paragraph or section was found. 2800 */ 2801 int 2802 findpar( 2803 int *pincl, /* Return: TRUE if last char is to be included */ 2804 int dir, 2805 long count, 2806 int what, 2807 int both) 2808 { 2809 linenr_T curr; 2810 int did_skip; /* TRUE after separating lines have been skipped */ 2811 int first; /* TRUE on first line */ 2812 int posix = (vim_strchr(p_cpo, CPO_PARA) != NULL); 2813 #ifdef FEAT_FOLDING 2814 linenr_T fold_first; /* first line of a closed fold */ 2815 linenr_T fold_last; /* last line of a closed fold */ 2816 int fold_skipped; /* TRUE if a closed fold was skipped this 2817 iteration */ 2818 #endif 2819 2820 curr = curwin->w_cursor.lnum; 2821 2822 while (count--) 2823 { 2824 did_skip = FALSE; 2825 for (first = TRUE; ; first = FALSE) 2826 { 2827 if (*ml_get(curr) != NUL) 2828 did_skip = TRUE; 2829 2830 #ifdef FEAT_FOLDING 2831 /* skip folded lines */ 2832 fold_skipped = FALSE; 2833 if (first && hasFolding(curr, &fold_first, &fold_last)) 2834 { 2835 curr = ((dir > 0) ? fold_last : fold_first) + dir; 2836 fold_skipped = TRUE; 2837 } 2838 #endif 2839 2840 /* POSIX has it's own ideas of what a paragraph boundary is and it 2841 * doesn't match historical Vi: It also stops at a "{" in the 2842 * first column and at an empty line. */ 2843 if (!first && did_skip && (startPS(curr, what, both) 2844 || (posix && what == NUL && *ml_get(curr) == '{'))) 2845 break; 2846 2847 #ifdef FEAT_FOLDING 2848 if (fold_skipped) 2849 curr -= dir; 2850 #endif 2851 if ((curr += dir) < 1 || curr > curbuf->b_ml.ml_line_count) 2852 { 2853 if (count) 2854 return FALSE; 2855 curr -= dir; 2856 break; 2857 } 2858 } 2859 } 2860 setpcmark(); 2861 if (both && *ml_get(curr) == '}') /* include line with '}' */ 2862 ++curr; 2863 curwin->w_cursor.lnum = curr; 2864 if (curr == curbuf->b_ml.ml_line_count && what != '}') 2865 { 2866 char_u *line = ml_get(curr); 2867 2868 /* Put the cursor on the last character in the last line and make the 2869 * motion inclusive. */ 2870 if ((curwin->w_cursor.col = (colnr_T)STRLEN(line)) != 0) 2871 { 2872 --curwin->w_cursor.col; 2873 #ifdef FEAT_MBYTE 2874 curwin->w_cursor.col -= 2875 (*mb_head_off)(line, line + curwin->w_cursor.col); 2876 #endif 2877 *pincl = TRUE; 2878 } 2879 } 2880 else 2881 curwin->w_cursor.col = 0; 2882 return TRUE; 2883 } 2884 2885 /* 2886 * check if the string 's' is a nroff macro that is in option 'opt' 2887 */ 2888 static int 2889 inmacro(char_u *opt, char_u *s) 2890 { 2891 char_u *macro; 2892 2893 for (macro = opt; macro[0]; ++macro) 2894 { 2895 /* Accept two characters in the option being equal to two characters 2896 * in the line. A space in the option matches with a space in the 2897 * line or the line having ended. */ 2898 if ( (macro[0] == s[0] 2899 || (macro[0] == ' ' 2900 && (s[0] == NUL || s[0] == ' '))) 2901 && (macro[1] == s[1] 2902 || ((macro[1] == NUL || macro[1] == ' ') 2903 && (s[0] == NUL || s[1] == NUL || s[1] == ' ')))) 2904 break; 2905 ++macro; 2906 if (macro[0] == NUL) 2907 break; 2908 } 2909 return (macro[0] != NUL); 2910 } 2911 2912 /* 2913 * startPS: return TRUE if line 'lnum' is the start of a section or paragraph. 2914 * If 'para' is '{' or '}' only check for sections. 2915 * If 'both' is TRUE also stop at '}' 2916 */ 2917 int 2918 startPS(linenr_T lnum, int para, int both) 2919 { 2920 char_u *s; 2921 2922 s = ml_get(lnum); 2923 if (*s == para || *s == '\f' || (both && *s == '}')) 2924 return TRUE; 2925 if (*s == '.' && (inmacro(p_sections, s + 1) || 2926 (!para && inmacro(p_para, s + 1)))) 2927 return TRUE; 2928 return FALSE; 2929 } 2930 2931 /* 2932 * The following routines do the word searches performed by the 'w', 'W', 2933 * 'b', 'B', 'e', and 'E' commands. 2934 */ 2935 2936 /* 2937 * To perform these searches, characters are placed into one of three 2938 * classes, and transitions between classes determine word boundaries. 2939 * 2940 * The classes are: 2941 * 2942 * 0 - white space 2943 * 1 - punctuation 2944 * 2 or higher - keyword characters (letters, digits and underscore) 2945 */ 2946 2947 static int cls_bigword; /* TRUE for "W", "B" or "E" */ 2948 2949 /* 2950 * cls() - returns the class of character at curwin->w_cursor 2951 * 2952 * If a 'W', 'B', or 'E' motion is being done (cls_bigword == TRUE), chars 2953 * from class 2 and higher are reported as class 1 since only white space 2954 * boundaries are of interest. 2955 */ 2956 static int 2957 cls(void) 2958 { 2959 int c; 2960 2961 c = gchar_cursor(); 2962 #ifdef FEAT_FKMAP /* when 'akm' (Farsi mode), take care of Farsi blank */ 2963 if (p_altkeymap && c == F_BLANK) 2964 return 0; 2965 #endif 2966 if (c == ' ' || c == '\t' || c == NUL) 2967 return 0; 2968 #ifdef FEAT_MBYTE 2969 if (enc_dbcs != 0 && c > 0xFF) 2970 { 2971 /* If cls_bigword, report multi-byte chars as class 1. */ 2972 if (enc_dbcs == DBCS_KOR && cls_bigword) 2973 return 1; 2974 2975 /* process code leading/trailing bytes */ 2976 return dbcs_class(((unsigned)c >> 8), (c & 0xFF)); 2977 } 2978 if (enc_utf8) 2979 { 2980 c = utf_class(c); 2981 if (c != 0 && cls_bigword) 2982 return 1; 2983 return c; 2984 } 2985 #endif 2986 2987 /* If cls_bigword is TRUE, report all non-blanks as class 1. */ 2988 if (cls_bigword) 2989 return 1; 2990 2991 if (vim_iswordc(c)) 2992 return 2; 2993 return 1; 2994 } 2995 2996 2997 /* 2998 * fwd_word(count, type, eol) - move forward one word 2999 * 3000 * Returns FAIL if the cursor was already at the end of the file. 3001 * If eol is TRUE, last word stops at end of line (for operators). 3002 */ 3003 int 3004 fwd_word( 3005 long count, 3006 int bigword, /* "W", "E" or "B" */ 3007 int eol) 3008 { 3009 int sclass; /* starting class */ 3010 int i; 3011 int last_line; 3012 3013 #ifdef FEAT_VIRTUALEDIT 3014 curwin->w_cursor.coladd = 0; 3015 #endif 3016 cls_bigword = bigword; 3017 while (--count >= 0) 3018 { 3019 #ifdef FEAT_FOLDING 3020 /* When inside a range of folded lines, move to the last char of the 3021 * last line. */ 3022 if (hasFolding(curwin->w_cursor.lnum, NULL, &curwin->w_cursor.lnum)) 3023 coladvance((colnr_T)MAXCOL); 3024 #endif 3025 sclass = cls(); 3026 3027 /* 3028 * We always move at least one character, unless on the last 3029 * character in the buffer. 3030 */ 3031 last_line = (curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count); 3032 i = inc_cursor(); 3033 if (i == -1 || (i >= 1 && last_line)) /* started at last char in file */ 3034 return FAIL; 3035 if (i >= 1 && eol && count == 0) /* started at last char in line */ 3036 return OK; 3037 3038 /* 3039 * Go one char past end of current word (if any) 3040 */ 3041 if (sclass != 0) 3042 while (cls() == sclass) 3043 { 3044 i = inc_cursor(); 3045 if (i == -1 || (i >= 1 && eol && count == 0)) 3046 return OK; 3047 } 3048 3049 /* 3050 * go to next non-white 3051 */ 3052 while (cls() == 0) 3053 { 3054 /* 3055 * We'll stop if we land on a blank line 3056 */ 3057 if (curwin->w_cursor.col == 0 && *ml_get_curline() == NUL) 3058 break; 3059 3060 i = inc_cursor(); 3061 if (i == -1 || (i >= 1 && eol && count == 0)) 3062 return OK; 3063 } 3064 } 3065 return OK; 3066 } 3067 3068 /* 3069 * bck_word() - move backward 'count' words 3070 * 3071 * If stop is TRUE and we are already on the start of a word, move one less. 3072 * 3073 * Returns FAIL if top of the file was reached. 3074 */ 3075 int 3076 bck_word(long count, int bigword, int stop) 3077 { 3078 int sclass; /* starting class */ 3079 3080 #ifdef FEAT_VIRTUALEDIT 3081 curwin->w_cursor.coladd = 0; 3082 #endif 3083 cls_bigword = bigword; 3084 while (--count >= 0) 3085 { 3086 #ifdef FEAT_FOLDING 3087 /* When inside a range of folded lines, move to the first char of the 3088 * first line. */ 3089 if (hasFolding(curwin->w_cursor.lnum, &curwin->w_cursor.lnum, NULL)) 3090 curwin->w_cursor.col = 0; 3091 #endif 3092 sclass = cls(); 3093 if (dec_cursor() == -1) /* started at start of file */ 3094 return FAIL; 3095 3096 if (!stop || sclass == cls() || sclass == 0) 3097 { 3098 /* 3099 * Skip white space before the word. 3100 * Stop on an empty line. 3101 */ 3102 while (cls() == 0) 3103 { 3104 if (curwin->w_cursor.col == 0 3105 && LINEEMPTY(curwin->w_cursor.lnum)) 3106 goto finished; 3107 if (dec_cursor() == -1) /* hit start of file, stop here */ 3108 return OK; 3109 } 3110 3111 /* 3112 * Move backward to start of this word. 3113 */ 3114 if (skip_chars(cls(), BACKWARD)) 3115 return OK; 3116 } 3117 3118 inc_cursor(); /* overshot - forward one */ 3119 finished: 3120 stop = FALSE; 3121 } 3122 return OK; 3123 } 3124 3125 /* 3126 * end_word() - move to the end of the word 3127 * 3128 * There is an apparent bug in the 'e' motion of the real vi. At least on the 3129 * System V Release 3 version for the 80386. Unlike 'b' and 'w', the 'e' 3130 * motion crosses blank lines. When the real vi crosses a blank line in an 3131 * 'e' motion, the cursor is placed on the FIRST character of the next 3132 * non-blank line. The 'E' command, however, works correctly. Since this 3133 * appears to be a bug, I have not duplicated it here. 3134 * 3135 * Returns FAIL if end of the file was reached. 3136 * 3137 * If stop is TRUE and we are already on the end of a word, move one less. 3138 * If empty is TRUE stop on an empty line. 3139 */ 3140 int 3141 end_word( 3142 long count, 3143 int bigword, 3144 int stop, 3145 int empty) 3146 { 3147 int sclass; /* starting class */ 3148 3149 #ifdef FEAT_VIRTUALEDIT 3150 curwin->w_cursor.coladd = 0; 3151 #endif 3152 cls_bigword = bigword; 3153 while (--count >= 0) 3154 { 3155 #ifdef FEAT_FOLDING 3156 /* When inside a range of folded lines, move to the last char of the 3157 * last line. */ 3158 if (hasFolding(curwin->w_cursor.lnum, NULL, &curwin->w_cursor.lnum)) 3159 coladvance((colnr_T)MAXCOL); 3160 #endif 3161 sclass = cls(); 3162 if (inc_cursor() == -1) 3163 return FAIL; 3164 3165 /* 3166 * If we're in the middle of a word, we just have to move to the end 3167 * of it. 3168 */ 3169 if (cls() == sclass && sclass != 0) 3170 { 3171 /* 3172 * Move forward to end of the current word 3173 */ 3174 if (skip_chars(sclass, FORWARD)) 3175 return FAIL; 3176 } 3177 else if (!stop || sclass == 0) 3178 { 3179 /* 3180 * We were at the end of a word. Go to the end of the next word. 3181 * First skip white space, if 'empty' is TRUE, stop at empty line. 3182 */ 3183 while (cls() == 0) 3184 { 3185 if (empty && curwin->w_cursor.col == 0 3186 && LINEEMPTY(curwin->w_cursor.lnum)) 3187 goto finished; 3188 if (inc_cursor() == -1) /* hit end of file, stop here */ 3189 return FAIL; 3190 } 3191 3192 /* 3193 * Move forward to the end of this word. 3194 */ 3195 if (skip_chars(cls(), FORWARD)) 3196 return FAIL; 3197 } 3198 dec_cursor(); /* overshot - one char backward */ 3199 finished: 3200 stop = FALSE; /* we move only one word less */ 3201 } 3202 return OK; 3203 } 3204 3205 /* 3206 * Move back to the end of the word. 3207 * 3208 * Returns FAIL if start of the file was reached. 3209 */ 3210 int 3211 bckend_word( 3212 long count, 3213 int bigword, /* TRUE for "B" */ 3214 int eol) /* TRUE: stop at end of line. */ 3215 { 3216 int sclass; /* starting class */ 3217 int i; 3218 3219 #ifdef FEAT_VIRTUALEDIT 3220 curwin->w_cursor.coladd = 0; 3221 #endif 3222 cls_bigword = bigword; 3223 while (--count >= 0) 3224 { 3225 sclass = cls(); 3226 if ((i = dec_cursor()) == -1) 3227 return FAIL; 3228 if (eol && i == 1) 3229 return OK; 3230 3231 /* 3232 * Move backward to before the start of this word. 3233 */ 3234 if (sclass != 0) 3235 { 3236 while (cls() == sclass) 3237 if ((i = dec_cursor()) == -1 || (eol && i == 1)) 3238 return OK; 3239 } 3240 3241 /* 3242 * Move backward to end of the previous word 3243 */ 3244 while (cls() == 0) 3245 { 3246 if (curwin->w_cursor.col == 0 && LINEEMPTY(curwin->w_cursor.lnum)) 3247 break; 3248 if ((i = dec_cursor()) == -1 || (eol && i == 1)) 3249 return OK; 3250 } 3251 } 3252 return OK; 3253 } 3254 3255 /* 3256 * Skip a row of characters of the same class. 3257 * Return TRUE when end-of-file reached, FALSE otherwise. 3258 */ 3259 static int 3260 skip_chars(int cclass, int dir) 3261 { 3262 while (cls() == cclass) 3263 if ((dir == FORWARD ? inc_cursor() : dec_cursor()) == -1) 3264 return TRUE; 3265 return FALSE; 3266 } 3267 3268 #ifdef FEAT_TEXTOBJ 3269 /* 3270 * Go back to the start of the word or the start of white space 3271 */ 3272 static void 3273 back_in_line(void) 3274 { 3275 int sclass; /* starting class */ 3276 3277 sclass = cls(); 3278 for (;;) 3279 { 3280 if (curwin->w_cursor.col == 0) /* stop at start of line */ 3281 break; 3282 dec_cursor(); 3283 if (cls() != sclass) /* stop at start of word */ 3284 { 3285 inc_cursor(); 3286 break; 3287 } 3288 } 3289 } 3290 3291 static void 3292 find_first_blank(pos_T *posp) 3293 { 3294 int c; 3295 3296 while (decl(posp) != -1) 3297 { 3298 c = gchar_pos(posp); 3299 if (!VIM_ISWHITE(c)) 3300 { 3301 incl(posp); 3302 break; 3303 } 3304 } 3305 } 3306 3307 /* 3308 * Skip count/2 sentences and count/2 separating white spaces. 3309 */ 3310 static void 3311 findsent_forward( 3312 long count, 3313 int at_start_sent) /* cursor is at start of sentence */ 3314 { 3315 while (count--) 3316 { 3317 findsent(FORWARD, 1L); 3318 if (at_start_sent) 3319 find_first_blank(&curwin->w_cursor); 3320 if (count == 0 || at_start_sent) 3321 decl(&curwin->w_cursor); 3322 at_start_sent = !at_start_sent; 3323 } 3324 } 3325 3326 /* 3327 * Find word under cursor, cursor at end. 3328 * Used while an operator is pending, and in Visual mode. 3329 */ 3330 int 3331 current_word( 3332 oparg_T *oap, 3333 long count, 3334 int include, /* TRUE: include word and white space */ 3335 int bigword) /* FALSE == word, TRUE == WORD */ 3336 { 3337 pos_T start_pos; 3338 pos_T pos; 3339 int inclusive = TRUE; 3340 int include_white = FALSE; 3341 3342 cls_bigword = bigword; 3343 CLEAR_POS(&start_pos); 3344 3345 /* Correct cursor when 'selection' is exclusive */ 3346 if (VIsual_active && *p_sel == 'e' && LT_POS(VIsual, curwin->w_cursor)) 3347 dec_cursor(); 3348 3349 /* 3350 * When Visual mode is not active, or when the VIsual area is only one 3351 * character, select the word and/or white space under the cursor. 3352 */ 3353 if (!VIsual_active || EQUAL_POS(curwin->w_cursor, VIsual)) 3354 { 3355 /* 3356 * Go to start of current word or white space. 3357 */ 3358 back_in_line(); 3359 start_pos = curwin->w_cursor; 3360 3361 /* 3362 * If the start is on white space, and white space should be included 3363 * (" word"), or start is not on white space, and white space should 3364 * not be included ("word"), find end of word. 3365 */ 3366 if ((cls() == 0) == include) 3367 { 3368 if (end_word(1L, bigword, TRUE, TRUE) == FAIL) 3369 return FAIL; 3370 } 3371 else 3372 { 3373 /* 3374 * If the start is not on white space, and white space should be 3375 * included ("word "), or start is on white space and white 3376 * space should not be included (" "), find start of word. 3377 * If we end up in the first column of the next line (single char 3378 * word) back up to end of the line. 3379 */ 3380 fwd_word(1L, bigword, TRUE); 3381 if (curwin->w_cursor.col == 0) 3382 decl(&curwin->w_cursor); 3383 else 3384 oneleft(); 3385 3386 if (include) 3387 include_white = TRUE; 3388 } 3389 3390 if (VIsual_active) 3391 { 3392 /* should do something when inclusive == FALSE ! */ 3393 VIsual = start_pos; 3394 redraw_curbuf_later(INVERTED); /* update the inversion */ 3395 } 3396 else 3397 { 3398 oap->start = start_pos; 3399 oap->motion_type = MCHAR; 3400 } 3401 --count; 3402 } 3403 3404 /* 3405 * When count is still > 0, extend with more objects. 3406 */ 3407 while (count > 0) 3408 { 3409 inclusive = TRUE; 3410 if (VIsual_active && LT_POS(curwin->w_cursor, VIsual)) 3411 { 3412 /* 3413 * In Visual mode, with cursor at start: move cursor back. 3414 */ 3415 if (decl(&curwin->w_cursor) == -1) 3416 return FAIL; 3417 if (include != (cls() != 0)) 3418 { 3419 if (bck_word(1L, bigword, TRUE) == FAIL) 3420 return FAIL; 3421 } 3422 else 3423 { 3424 if (bckend_word(1L, bigword, TRUE) == FAIL) 3425 return FAIL; 3426 (void)incl(&curwin->w_cursor); 3427 } 3428 } 3429 else 3430 { 3431 /* 3432 * Move cursor forward one word and/or white area. 3433 */ 3434 if (incl(&curwin->w_cursor) == -1) 3435 return FAIL; 3436 if (include != (cls() == 0)) 3437 { 3438 if (fwd_word(1L, bigword, TRUE) == FAIL && count > 1) 3439 return FAIL; 3440 /* 3441 * If end is just past a new-line, we don't want to include 3442 * the first character on the line. 3443 * Put cursor on last char of white. 3444 */ 3445 if (oneleft() == FAIL) 3446 inclusive = FALSE; 3447 } 3448 else 3449 { 3450 if (end_word(1L, bigword, TRUE, TRUE) == FAIL) 3451 return FAIL; 3452 } 3453 } 3454 --count; 3455 } 3456 3457 if (include_white && (cls() != 0 3458 || (curwin->w_cursor.col == 0 && !inclusive))) 3459 { 3460 /* 3461 * If we don't include white space at the end, move the start 3462 * to include some white space there. This makes "daw" work 3463 * better on the last word in a sentence (and "2daw" on last-but-one 3464 * word). Also when "2daw" deletes "word." at the end of the line 3465 * (cursor is at start of next line). 3466 * But don't delete white space at start of line (indent). 3467 */ 3468 pos = curwin->w_cursor; /* save cursor position */ 3469 curwin->w_cursor = start_pos; 3470 if (oneleft() == OK) 3471 { 3472 back_in_line(); 3473 if (cls() == 0 && curwin->w_cursor.col > 0) 3474 { 3475 if (VIsual_active) 3476 VIsual = curwin->w_cursor; 3477 else 3478 oap->start = curwin->w_cursor; 3479 } 3480 } 3481 curwin->w_cursor = pos; /* put cursor back at end */ 3482 } 3483 3484 if (VIsual_active) 3485 { 3486 if (*p_sel == 'e' && inclusive && LTOREQ_POS(VIsual, curwin->w_cursor)) 3487 inc_cursor(); 3488 if (VIsual_mode == 'V') 3489 { 3490 VIsual_mode = 'v'; 3491 redraw_cmdline = TRUE; /* show mode later */ 3492 } 3493 } 3494 else 3495 oap->inclusive = inclusive; 3496 3497 return OK; 3498 } 3499 3500 /* 3501 * Find sentence(s) under the cursor, cursor at end. 3502 * When Visual active, extend it by one or more sentences. 3503 */ 3504 int 3505 current_sent(oparg_T *oap, long count, int include) 3506 { 3507 pos_T start_pos; 3508 pos_T pos; 3509 int start_blank; 3510 int c; 3511 int at_start_sent; 3512 long ncount; 3513 3514 start_pos = curwin->w_cursor; 3515 pos = start_pos; 3516 findsent(FORWARD, 1L); /* Find start of next sentence. */ 3517 3518 /* 3519 * When the Visual area is bigger than one character: Extend it. 3520 */ 3521 if (VIsual_active && !EQUAL_POS(start_pos, VIsual)) 3522 { 3523 extend: 3524 if (LT_POS(start_pos, VIsual)) 3525 { 3526 /* 3527 * Cursor at start of Visual area. 3528 * Find out where we are: 3529 * - in the white space before a sentence 3530 * - in a sentence or just after it 3531 * - at the start of a sentence 3532 */ 3533 at_start_sent = TRUE; 3534 decl(&pos); 3535 while (LT_POS(pos, curwin->w_cursor)) 3536 { 3537 c = gchar_pos(&pos); 3538 if (!VIM_ISWHITE(c)) 3539 { 3540 at_start_sent = FALSE; 3541 break; 3542 } 3543 incl(&pos); 3544 } 3545 if (!at_start_sent) 3546 { 3547 findsent(BACKWARD, 1L); 3548 if (EQUAL_POS(curwin->w_cursor, start_pos)) 3549 at_start_sent = TRUE; /* exactly at start of sentence */ 3550 else 3551 /* inside a sentence, go to its end (start of next) */ 3552 findsent(FORWARD, 1L); 3553 } 3554 if (include) /* "as" gets twice as much as "is" */ 3555 count *= 2; 3556 while (count--) 3557 { 3558 if (at_start_sent) 3559 find_first_blank(&curwin->w_cursor); 3560 c = gchar_cursor(); 3561 if (!at_start_sent || (!include && !VIM_ISWHITE(c))) 3562 findsent(BACKWARD, 1L); 3563 at_start_sent = !at_start_sent; 3564 } 3565 } 3566 else 3567 { 3568 /* 3569 * Cursor at end of Visual area. 3570 * Find out where we are: 3571 * - just before a sentence 3572 * - just before or in the white space before a sentence 3573 * - in a sentence 3574 */ 3575 incl(&pos); 3576 at_start_sent = TRUE; 3577 /* not just before a sentence */ 3578 if (!EQUAL_POS(pos, curwin->w_cursor)) 3579 { 3580 at_start_sent = FALSE; 3581 while (LT_POS(pos, curwin->w_cursor)) 3582 { 3583 c = gchar_pos(&pos); 3584 if (!VIM_ISWHITE(c)) 3585 { 3586 at_start_sent = TRUE; 3587 break; 3588 } 3589 incl(&pos); 3590 } 3591 if (at_start_sent) /* in the sentence */ 3592 findsent(BACKWARD, 1L); 3593 else /* in/before white before a sentence */ 3594 curwin->w_cursor = start_pos; 3595 } 3596 3597 if (include) /* "as" gets twice as much as "is" */ 3598 count *= 2; 3599 findsent_forward(count, at_start_sent); 3600 if (*p_sel == 'e') 3601 ++curwin->w_cursor.col; 3602 } 3603 return OK; 3604 } 3605 3606 /* 3607 * If the cursor started on a blank, check if it is just before the start 3608 * of the next sentence. 3609 */ 3610 while (c = gchar_pos(&pos), VIM_ISWHITE(c)) /* VIM_ISWHITE() is a macro */ 3611 incl(&pos); 3612 if (EQUAL_POS(pos, curwin->w_cursor)) 3613 { 3614 start_blank = TRUE; 3615 find_first_blank(&start_pos); /* go back to first blank */ 3616 } 3617 else 3618 { 3619 start_blank = FALSE; 3620 findsent(BACKWARD, 1L); 3621 start_pos = curwin->w_cursor; 3622 } 3623 if (include) 3624 ncount = count * 2; 3625 else 3626 { 3627 ncount = count; 3628 if (start_blank) 3629 --ncount; 3630 } 3631 if (ncount > 0) 3632 findsent_forward(ncount, TRUE); 3633 else 3634 decl(&curwin->w_cursor); 3635 3636 if (include) 3637 { 3638 /* 3639 * If the blank in front of the sentence is included, exclude the 3640 * blanks at the end of the sentence, go back to the first blank. 3641 * If there are no trailing blanks, try to include leading blanks. 3642 */ 3643 if (start_blank) 3644 { 3645 find_first_blank(&curwin->w_cursor); 3646 c = gchar_pos(&curwin->w_cursor); /* VIM_ISWHITE() is a macro */ 3647 if (VIM_ISWHITE(c)) 3648 decl(&curwin->w_cursor); 3649 } 3650 else if (c = gchar_cursor(), !VIM_ISWHITE(c)) 3651 find_first_blank(&start_pos); 3652 } 3653 3654 if (VIsual_active) 3655 { 3656 /* Avoid getting stuck with "is" on a single space before a sentence. */ 3657 if (EQUAL_POS(start_pos, curwin->w_cursor)) 3658 goto extend; 3659 if (*p_sel == 'e') 3660 ++curwin->w_cursor.col; 3661 VIsual = start_pos; 3662 VIsual_mode = 'v'; 3663 redraw_cmdline = TRUE; /* show mode later */ 3664 redraw_curbuf_later(INVERTED); /* update the inversion */ 3665 } 3666 else 3667 { 3668 /* include a newline after the sentence, if there is one */ 3669 if (incl(&curwin->w_cursor) == -1) 3670 oap->inclusive = TRUE; 3671 else 3672 oap->inclusive = FALSE; 3673 oap->start = start_pos; 3674 oap->motion_type = MCHAR; 3675 } 3676 return OK; 3677 } 3678 3679 /* 3680 * Find block under the cursor, cursor at end. 3681 * "what" and "other" are two matching parenthesis/brace/etc. 3682 */ 3683 int 3684 current_block( 3685 oparg_T *oap, 3686 long count, 3687 int include, /* TRUE == include white space */ 3688 int what, /* '(', '{', etc. */ 3689 int other) /* ')', '}', etc. */ 3690 { 3691 pos_T old_pos; 3692 pos_T *pos = NULL; 3693 pos_T start_pos; 3694 pos_T *end_pos; 3695 pos_T old_start, old_end; 3696 char_u *save_cpo; 3697 int sol = FALSE; /* '{' at start of line */ 3698 3699 old_pos = curwin->w_cursor; 3700 old_end = curwin->w_cursor; /* remember where we started */ 3701 old_start = old_end; 3702 3703 /* 3704 * If we start on '(', '{', ')', '}', etc., use the whole block inclusive. 3705 */ 3706 if (!VIsual_active || EQUAL_POS(VIsual, curwin->w_cursor)) 3707 { 3708 setpcmark(); 3709 if (what == '{') /* ignore indent */ 3710 while (inindent(1)) 3711 if (inc_cursor() != 0) 3712 break; 3713 if (gchar_cursor() == what) 3714 /* cursor on '(' or '{', move cursor just after it */ 3715 ++curwin->w_cursor.col; 3716 } 3717 else if (LT_POS(VIsual, curwin->w_cursor)) 3718 { 3719 old_start = VIsual; 3720 curwin->w_cursor = VIsual; /* cursor at low end of Visual */ 3721 } 3722 else 3723 old_end = VIsual; 3724 3725 /* 3726 * Search backwards for unclosed '(', '{', etc.. 3727 * Put this position in start_pos. 3728 * Ignore quotes here. Keep the "M" flag in 'cpo', as that is what the 3729 * user wants. 3730 */ 3731 save_cpo = p_cpo; 3732 p_cpo = (char_u *)(vim_strchr(p_cpo, CPO_MATCHBSL) != NULL ? "%M" : "%"); 3733 while (count-- > 0) 3734 { 3735 if ((pos = findmatch(NULL, what)) == NULL) 3736 break; 3737 curwin->w_cursor = *pos; 3738 start_pos = *pos; /* the findmatch for end_pos will overwrite *pos */ 3739 } 3740 p_cpo = save_cpo; 3741 3742 /* 3743 * Search for matching ')', '}', etc. 3744 * Put this position in curwin->w_cursor. 3745 */ 3746 if (pos == NULL || (end_pos = findmatch(NULL, other)) == NULL) 3747 { 3748 curwin->w_cursor = old_pos; 3749 return FAIL; 3750 } 3751 curwin->w_cursor = *end_pos; 3752 3753 /* 3754 * Try to exclude the '(', '{', ')', '}', etc. when "include" is FALSE. 3755 * If the ending '}', ')' or ']' is only preceded by indent, skip that 3756 * indent. But only if the resulting area is not smaller than what we 3757 * started with. 3758 */ 3759 while (!include) 3760 { 3761 incl(&start_pos); 3762 sol = (curwin->w_cursor.col == 0); 3763 decl(&curwin->w_cursor); 3764 while (inindent(1)) 3765 { 3766 sol = TRUE; 3767 if (decl(&curwin->w_cursor) != 0) 3768 break; 3769 } 3770 3771 /* 3772 * In Visual mode, when the resulting area is not bigger than what we 3773 * started with, extend it to the next block, and then exclude again. 3774 */ 3775 if (!LT_POS(start_pos, old_start) && !LT_POS(old_end, curwin->w_cursor) 3776 && VIsual_active) 3777 { 3778 curwin->w_cursor = old_start; 3779 decl(&curwin->w_cursor); 3780 if ((pos = findmatch(NULL, what)) == NULL) 3781 { 3782 curwin->w_cursor = old_pos; 3783 return FAIL; 3784 } 3785 start_pos = *pos; 3786 curwin->w_cursor = *pos; 3787 if ((end_pos = findmatch(NULL, other)) == NULL) 3788 { 3789 curwin->w_cursor = old_pos; 3790 return FAIL; 3791 } 3792 curwin->w_cursor = *end_pos; 3793 } 3794 else 3795 break; 3796 } 3797 3798 if (VIsual_active) 3799 { 3800 if (*p_sel == 'e') 3801 inc(&curwin->w_cursor); 3802 if (sol && gchar_cursor() != NUL) 3803 inc(&curwin->w_cursor); /* include the line break */ 3804 VIsual = start_pos; 3805 VIsual_mode = 'v'; 3806 redraw_curbuf_later(INVERTED); /* update the inversion */ 3807 showmode(); 3808 } 3809 else 3810 { 3811 oap->start = start_pos; 3812 oap->motion_type = MCHAR; 3813 oap->inclusive = FALSE; 3814 if (sol) 3815 incl(&curwin->w_cursor); 3816 else if (LTOREQ_POS(start_pos, curwin->w_cursor)) 3817 /* Include the character under the cursor. */ 3818 oap->inclusive = TRUE; 3819 else 3820 /* End is before the start (no text in between <>, [], etc.): don't 3821 * operate on any text. */ 3822 curwin->w_cursor = start_pos; 3823 } 3824 3825 return OK; 3826 } 3827 3828 static int in_html_tag(int); 3829 3830 /* 3831 * Return TRUE if the cursor is on a "<aaa>" tag. Ignore "<aaa/>". 3832 * When "end_tag" is TRUE return TRUE if the cursor is on "</aaa>". 3833 */ 3834 static int 3835 in_html_tag( 3836 int end_tag) 3837 { 3838 char_u *line = ml_get_curline(); 3839 char_u *p; 3840 int c; 3841 int lc = NUL; 3842 pos_T pos; 3843 3844 #ifdef FEAT_MBYTE 3845 if (enc_dbcs) 3846 { 3847 char_u *lp = NULL; 3848 3849 /* We search forward until the cursor, because searching backwards is 3850 * very slow for DBCS encodings. */ 3851 for (p = line; p < line + curwin->w_cursor.col; MB_PTR_ADV(p)) 3852 if (*p == '>' || *p == '<') 3853 { 3854 lc = *p; 3855 lp = p; 3856 } 3857 if (*p != '<') /* check for '<' under cursor */ 3858 { 3859 if (lc != '<') 3860 return FALSE; 3861 p = lp; 3862 } 3863 } 3864 else 3865 #endif 3866 { 3867 for (p = line + curwin->w_cursor.col; p > line; ) 3868 { 3869 if (*p == '<') /* find '<' under/before cursor */ 3870 break; 3871 MB_PTR_BACK(line, p); 3872 if (*p == '>') /* find '>' before cursor */ 3873 break; 3874 } 3875 if (*p != '<') 3876 return FALSE; 3877 } 3878 3879 pos.lnum = curwin->w_cursor.lnum; 3880 pos.col = (colnr_T)(p - line); 3881 3882 MB_PTR_ADV(p); 3883 if (end_tag) 3884 /* check that there is a '/' after the '<' */ 3885 return *p == '/'; 3886 3887 /* check that there is no '/' after the '<' */ 3888 if (*p == '/') 3889 return FALSE; 3890 3891 /* check that the matching '>' is not preceded by '/' */ 3892 for (;;) 3893 { 3894 if (inc(&pos) < 0) 3895 return FALSE; 3896 c = *ml_get_pos(&pos); 3897 if (c == '>') 3898 break; 3899 lc = c; 3900 } 3901 return lc != '/'; 3902 } 3903 3904 /* 3905 * Find tag block under the cursor, cursor at end. 3906 */ 3907 int 3908 current_tagblock( 3909 oparg_T *oap, 3910 long count_arg, 3911 int include) /* TRUE == include white space */ 3912 { 3913 long count = count_arg; 3914 long n; 3915 pos_T old_pos; 3916 pos_T start_pos; 3917 pos_T end_pos; 3918 pos_T old_start, old_end; 3919 char_u *spat, *epat; 3920 char_u *p; 3921 char_u *cp; 3922 int len; 3923 int r; 3924 int do_include = include; 3925 int save_p_ws = p_ws; 3926 int retval = FAIL; 3927 int is_inclusive = TRUE; 3928 3929 p_ws = FALSE; 3930 3931 old_pos = curwin->w_cursor; 3932 old_end = curwin->w_cursor; /* remember where we started */ 3933 old_start = old_end; 3934 if (!VIsual_active || *p_sel == 'e') 3935 decl(&old_end); /* old_end is inclusive */ 3936 3937 /* 3938 * If we start on "<aaa>" select that block. 3939 */ 3940 if (!VIsual_active || EQUAL_POS(VIsual, curwin->w_cursor)) 3941 { 3942 setpcmark(); 3943 3944 /* ignore indent */ 3945 while (inindent(1)) 3946 if (inc_cursor() != 0) 3947 break; 3948 3949 if (in_html_tag(FALSE)) 3950 { 3951 /* cursor on start tag, move to its '>' */ 3952 while (*ml_get_cursor() != '>') 3953 if (inc_cursor() < 0) 3954 break; 3955 } 3956 else if (in_html_tag(TRUE)) 3957 { 3958 /* cursor on end tag, move to just before it */ 3959 while (*ml_get_cursor() != '<') 3960 if (dec_cursor() < 0) 3961 break; 3962 dec_cursor(); 3963 old_end = curwin->w_cursor; 3964 } 3965 } 3966 else if (LT_POS(VIsual, curwin->w_cursor)) 3967 { 3968 old_start = VIsual; 3969 curwin->w_cursor = VIsual; /* cursor at low end of Visual */ 3970 } 3971 else 3972 old_end = VIsual; 3973 3974 again: 3975 /* 3976 * Search backwards for unclosed "<aaa>". 3977 * Put this position in start_pos. 3978 */ 3979 for (n = 0; n < count; ++n) 3980 { 3981 if (do_searchpair((char_u *)"<[^ \t>/!]\\+\\%(\\_s\\_[^>]\\{-}[^/]>\\|$\\|\\_s\\=>\\)", 3982 (char_u *)"", 3983 (char_u *)"</[^>]*>", BACKWARD, (char_u *)"", 0, 3984 NULL, (linenr_T)0, 0L) <= 0) 3985 { 3986 curwin->w_cursor = old_pos; 3987 goto theend; 3988 } 3989 } 3990 start_pos = curwin->w_cursor; 3991 3992 /* 3993 * Search for matching "</aaa>". First isolate the "aaa". 3994 */ 3995 inc_cursor(); 3996 p = ml_get_cursor(); 3997 for (cp = p; *cp != NUL && *cp != '>' && !VIM_ISWHITE(*cp); MB_PTR_ADV(cp)) 3998 ; 3999 len = (int)(cp - p); 4000 if (len == 0) 4001 { 4002 curwin->w_cursor = old_pos; 4003 goto theend; 4004 } 4005 spat = alloc(len + 31); 4006 epat = alloc(len + 9); 4007 if (spat == NULL || epat == NULL) 4008 { 4009 vim_free(spat); 4010 vim_free(epat); 4011 curwin->w_cursor = old_pos; 4012 goto theend; 4013 } 4014 sprintf((char *)spat, "<%.*s\\>\\%%(\\s\\_[^>]\\{-}[^/]>\\|>\\)\\c", len, p); 4015 sprintf((char *)epat, "</%.*s>\\c", len, p); 4016 4017 r = do_searchpair(spat, (char_u *)"", epat, FORWARD, (char_u *)"", 4018 0, NULL, (linenr_T)0, 0L); 4019 4020 vim_free(spat); 4021 vim_free(epat); 4022 4023 if (r < 1 || LT_POS(curwin->w_cursor, old_end)) 4024 { 4025 /* Can't find other end or it's before the previous end. Could be a 4026 * HTML tag that doesn't have a matching end. Search backwards for 4027 * another starting tag. */ 4028 count = 1; 4029 curwin->w_cursor = start_pos; 4030 goto again; 4031 } 4032 4033 if (do_include || r < 1) 4034 { 4035 /* Include up to the '>'. */ 4036 while (*ml_get_cursor() != '>') 4037 if (inc_cursor() < 0) 4038 break; 4039 } 4040 else 4041 { 4042 char_u *c = ml_get_cursor(); 4043 4044 /* Exclude the '<' of the end tag. 4045 * If the closing tag is on new line, do not decrement cursor, but 4046 * make operation exclusive, so that the linefeed will be selected */ 4047 if (*c == '<' && !VIsual_active && curwin->w_cursor.col == 0) 4048 /* do not decrement cursor */ 4049 is_inclusive = FALSE; 4050 else if (*c == '<') 4051 dec_cursor(); 4052 } 4053 end_pos = curwin->w_cursor; 4054 4055 if (!do_include) 4056 { 4057 /* Exclude the start tag. */ 4058 curwin->w_cursor = start_pos; 4059 while (inc_cursor() >= 0) 4060 if (*ml_get_cursor() == '>') 4061 { 4062 inc_cursor(); 4063 start_pos = curwin->w_cursor; 4064 break; 4065 } 4066 curwin->w_cursor = end_pos; 4067 4068 /* If we now have the same text as before reset "do_include" and try 4069 * again. */ 4070 if (EQUAL_POS(start_pos, old_start) && EQUAL_POS(end_pos, old_end)) 4071 { 4072 do_include = TRUE; 4073 curwin->w_cursor = old_start; 4074 count = count_arg; 4075 goto again; 4076 } 4077 } 4078 4079 if (VIsual_active) 4080 { 4081 /* If the end is before the start there is no text between tags, select 4082 * the char under the cursor. */ 4083 if (LT_POS(end_pos, start_pos)) 4084 curwin->w_cursor = start_pos; 4085 else if (*p_sel == 'e') 4086 inc_cursor(); 4087 VIsual = start_pos; 4088 VIsual_mode = 'v'; 4089 redraw_curbuf_later(INVERTED); /* update the inversion */ 4090 showmode(); 4091 } 4092 else 4093 { 4094 oap->start = start_pos; 4095 oap->motion_type = MCHAR; 4096 if (LT_POS(end_pos, start_pos)) 4097 { 4098 /* End is before the start: there is no text between tags; operate 4099 * on an empty area. */ 4100 curwin->w_cursor = start_pos; 4101 oap->inclusive = FALSE; 4102 } 4103 else 4104 oap->inclusive = is_inclusive; 4105 } 4106 retval = OK; 4107 4108 theend: 4109 p_ws = save_p_ws; 4110 return retval; 4111 } 4112 4113 int 4114 current_par( 4115 oparg_T *oap, 4116 long count, 4117 int include, /* TRUE == include white space */ 4118 int type) /* 'p' for paragraph, 'S' for section */ 4119 { 4120 linenr_T start_lnum; 4121 linenr_T end_lnum; 4122 int white_in_front; 4123 int dir; 4124 int start_is_white; 4125 int prev_start_is_white; 4126 int retval = OK; 4127 int do_white = FALSE; 4128 int t; 4129 int i; 4130 4131 if (type == 'S') /* not implemented yet */ 4132 return FAIL; 4133 4134 start_lnum = curwin->w_cursor.lnum; 4135 4136 /* 4137 * When visual area is more than one line: extend it. 4138 */ 4139 if (VIsual_active && start_lnum != VIsual.lnum) 4140 { 4141 extend: 4142 if (start_lnum < VIsual.lnum) 4143 dir = BACKWARD; 4144 else 4145 dir = FORWARD; 4146 for (i = count; --i >= 0; ) 4147 { 4148 if (start_lnum == 4149 (dir == BACKWARD ? 1 : curbuf->b_ml.ml_line_count)) 4150 { 4151 retval = FAIL; 4152 break; 4153 } 4154 4155 prev_start_is_white = -1; 4156 for (t = 0; t < 2; ++t) 4157 { 4158 start_lnum += dir; 4159 start_is_white = linewhite(start_lnum); 4160 if (prev_start_is_white == start_is_white) 4161 { 4162 start_lnum -= dir; 4163 break; 4164 } 4165 for (;;) 4166 { 4167 if (start_lnum == (dir == BACKWARD 4168 ? 1 : curbuf->b_ml.ml_line_count)) 4169 break; 4170 if (start_is_white != linewhite(start_lnum + dir) 4171 || (!start_is_white 4172 && startPS(start_lnum + (dir > 0 4173 ? 1 : 0), 0, 0))) 4174 break; 4175 start_lnum += dir; 4176 } 4177 if (!include) 4178 break; 4179 if (start_lnum == (dir == BACKWARD 4180 ? 1 : curbuf->b_ml.ml_line_count)) 4181 break; 4182 prev_start_is_white = start_is_white; 4183 } 4184 } 4185 curwin->w_cursor.lnum = start_lnum; 4186 curwin->w_cursor.col = 0; 4187 return retval; 4188 } 4189 4190 /* 4191 * First move back to the start_lnum of the paragraph or white lines 4192 */ 4193 white_in_front = linewhite(start_lnum); 4194 while (start_lnum > 1) 4195 { 4196 if (white_in_front) /* stop at first white line */ 4197 { 4198 if (!linewhite(start_lnum - 1)) 4199 break; 4200 } 4201 else /* stop at first non-white line of start of paragraph */ 4202 { 4203 if (linewhite(start_lnum - 1) || startPS(start_lnum, 0, 0)) 4204 break; 4205 } 4206 --start_lnum; 4207 } 4208 4209 /* 4210 * Move past the end of any white lines. 4211 */ 4212 end_lnum = start_lnum; 4213 while (end_lnum <= curbuf->b_ml.ml_line_count && linewhite(end_lnum)) 4214 ++end_lnum; 4215 4216 --end_lnum; 4217 i = count; 4218 if (!include && white_in_front) 4219 --i; 4220 while (i--) 4221 { 4222 if (end_lnum == curbuf->b_ml.ml_line_count) 4223 return FAIL; 4224 4225 if (!include) 4226 do_white = linewhite(end_lnum + 1); 4227 4228 if (include || !do_white) 4229 { 4230 ++end_lnum; 4231 /* 4232 * skip to end of paragraph 4233 */ 4234 while (end_lnum < curbuf->b_ml.ml_line_count 4235 && !linewhite(end_lnum + 1) 4236 && !startPS(end_lnum + 1, 0, 0)) 4237 ++end_lnum; 4238 } 4239 4240 if (i == 0 && white_in_front && include) 4241 break; 4242 4243 /* 4244 * skip to end of white lines after paragraph 4245 */ 4246 if (include || do_white) 4247 while (end_lnum < curbuf->b_ml.ml_line_count 4248 && linewhite(end_lnum + 1)) 4249 ++end_lnum; 4250 } 4251 4252 /* 4253 * If there are no empty lines at the end, try to find some empty lines at 4254 * the start (unless that has been done already). 4255 */ 4256 if (!white_in_front && !linewhite(end_lnum) && include) 4257 while (start_lnum > 1 && linewhite(start_lnum - 1)) 4258 --start_lnum; 4259 4260 if (VIsual_active) 4261 { 4262 /* Problem: when doing "Vipipip" nothing happens in a single white 4263 * line, we get stuck there. Trap this here. */ 4264 if (VIsual_mode == 'V' && start_lnum == curwin->w_cursor.lnum) 4265 goto extend; 4266 if (VIsual.lnum != start_lnum) 4267 { 4268 VIsual.lnum = start_lnum; 4269 VIsual.col = 0; 4270 } 4271 VIsual_mode = 'V'; 4272 redraw_curbuf_later(INVERTED); /* update the inversion */ 4273 showmode(); 4274 } 4275 else 4276 { 4277 oap->start.lnum = start_lnum; 4278 oap->start.col = 0; 4279 oap->motion_type = MLINE; 4280 } 4281 curwin->w_cursor.lnum = end_lnum; 4282 curwin->w_cursor.col = 0; 4283 4284 return OK; 4285 } 4286 4287 static int find_next_quote(char_u *top_ptr, int col, int quotechar, char_u *escape); 4288 static int find_prev_quote(char_u *line, int col_start, int quotechar, char_u *escape); 4289 4290 /* 4291 * Search quote char from string line[col]. 4292 * Quote character escaped by one of the characters in "escape" is not counted 4293 * as a quote. 4294 * Returns column number of "quotechar" or -1 when not found. 4295 */ 4296 static int 4297 find_next_quote( 4298 char_u *line, 4299 int col, 4300 int quotechar, 4301 char_u *escape) /* escape characters, can be NULL */ 4302 { 4303 int c; 4304 4305 for (;;) 4306 { 4307 c = line[col]; 4308 if (c == NUL) 4309 return -1; 4310 else if (escape != NULL && vim_strchr(escape, c)) 4311 ++col; 4312 else if (c == quotechar) 4313 break; 4314 #ifdef FEAT_MBYTE 4315 if (has_mbyte) 4316 col += (*mb_ptr2len)(line + col); 4317 else 4318 #endif 4319 ++col; 4320 } 4321 return col; 4322 } 4323 4324 /* 4325 * Search backwards in "line" from column "col_start" to find "quotechar". 4326 * Quote character escaped by one of the characters in "escape" is not counted 4327 * as a quote. 4328 * Return the found column or zero. 4329 */ 4330 static int 4331 find_prev_quote( 4332 char_u *line, 4333 int col_start, 4334 int quotechar, 4335 char_u *escape) /* escape characters, can be NULL */ 4336 { 4337 int n; 4338 4339 while (col_start > 0) 4340 { 4341 --col_start; 4342 #ifdef FEAT_MBYTE 4343 col_start -= (*mb_head_off)(line, line + col_start); 4344 #endif 4345 n = 0; 4346 if (escape != NULL) 4347 while (col_start - n > 0 && vim_strchr(escape, 4348 line[col_start - n - 1]) != NULL) 4349 ++n; 4350 if (n & 1) 4351 col_start -= n; /* uneven number of escape chars, skip it */ 4352 else if (line[col_start] == quotechar) 4353 break; 4354 } 4355 return col_start; 4356 } 4357 4358 /* 4359 * Find quote under the cursor, cursor at end. 4360 * Returns TRUE if found, else FALSE. 4361 */ 4362 int 4363 current_quote( 4364 oparg_T *oap, 4365 long count, 4366 int include, /* TRUE == include quote char */ 4367 int quotechar) /* Quote character */ 4368 { 4369 char_u *line = ml_get_curline(); 4370 int col_end; 4371 int col_start = curwin->w_cursor.col; 4372 int inclusive = FALSE; 4373 int vis_empty = TRUE; /* Visual selection <= 1 char */ 4374 int vis_bef_curs = FALSE; /* Visual starts before cursor */ 4375 int inside_quotes = FALSE; /* Looks like "i'" done before */ 4376 int selected_quote = FALSE; /* Has quote inside selection */ 4377 int i; 4378 4379 /* Correct cursor when 'selection' is "exclusive". */ 4380 if (VIsual_active) 4381 { 4382 /* this only works within one line */ 4383 if (VIsual.lnum != curwin->w_cursor.lnum) 4384 return FALSE; 4385 4386 vis_bef_curs = LT_POS(VIsual, curwin->w_cursor); 4387 if (*p_sel == 'e') 4388 { 4389 if (!vis_bef_curs) 4390 { 4391 /* VIsual needs to be start of Visual selection. */ 4392 pos_T t = curwin->w_cursor; 4393 4394 curwin->w_cursor = VIsual; 4395 VIsual = t; 4396 vis_bef_curs = TRUE; 4397 } 4398 dec_cursor(); 4399 } 4400 vis_empty = EQUAL_POS(VIsual, curwin->w_cursor); 4401 } 4402 4403 if (!vis_empty) 4404 { 4405 /* Check if the existing selection exactly spans the text inside 4406 * quotes. */ 4407 if (vis_bef_curs) 4408 { 4409 inside_quotes = VIsual.col > 0 4410 && line[VIsual.col - 1] == quotechar 4411 && line[curwin->w_cursor.col] != NUL 4412 && line[curwin->w_cursor.col + 1] == quotechar; 4413 i = VIsual.col; 4414 col_end = curwin->w_cursor.col; 4415 } 4416 else 4417 { 4418 inside_quotes = curwin->w_cursor.col > 0 4419 && line[curwin->w_cursor.col - 1] == quotechar 4420 && line[VIsual.col] != NUL 4421 && line[VIsual.col + 1] == quotechar; 4422 i = curwin->w_cursor.col; 4423 col_end = VIsual.col; 4424 } 4425 4426 /* Find out if we have a quote in the selection. */ 4427 while (i <= col_end) 4428 if (line[i++] == quotechar) 4429 { 4430 selected_quote = TRUE; 4431 break; 4432 } 4433 } 4434 4435 if (!vis_empty && line[col_start] == quotechar) 4436 { 4437 /* Already selecting something and on a quote character. Find the 4438 * next quoted string. */ 4439 if (vis_bef_curs) 4440 { 4441 /* Assume we are on a closing quote: move to after the next 4442 * opening quote. */ 4443 col_start = find_next_quote(line, col_start + 1, quotechar, NULL); 4444 if (col_start < 0) 4445 return FALSE; 4446 col_end = find_next_quote(line, col_start + 1, quotechar, 4447 curbuf->b_p_qe); 4448 if (col_end < 0) 4449 { 4450 /* We were on a starting quote perhaps? */ 4451 col_end = col_start; 4452 col_start = curwin->w_cursor.col; 4453 } 4454 } 4455 else 4456 { 4457 col_end = find_prev_quote(line, col_start, quotechar, NULL); 4458 if (line[col_end] != quotechar) 4459 return FALSE; 4460 col_start = find_prev_quote(line, col_end, quotechar, 4461 curbuf->b_p_qe); 4462 if (line[col_start] != quotechar) 4463 { 4464 /* We were on an ending quote perhaps? */ 4465 col_start = col_end; 4466 col_end = curwin->w_cursor.col; 4467 } 4468 } 4469 } 4470 else 4471 4472 if (line[col_start] == quotechar || !vis_empty) 4473 { 4474 int first_col = col_start; 4475 4476 if (!vis_empty) 4477 { 4478 if (vis_bef_curs) 4479 first_col = find_next_quote(line, col_start, quotechar, NULL); 4480 else 4481 first_col = find_prev_quote(line, col_start, quotechar, NULL); 4482 } 4483 4484 /* The cursor is on a quote, we don't know if it's the opening or 4485 * closing quote. Search from the start of the line to find out. 4486 * Also do this when there is a Visual area, a' may leave the cursor 4487 * in between two strings. */ 4488 col_start = 0; 4489 for (;;) 4490 { 4491 /* Find open quote character. */ 4492 col_start = find_next_quote(line, col_start, quotechar, NULL); 4493 if (col_start < 0 || col_start > first_col) 4494 return FALSE; 4495 /* Find close quote character. */ 4496 col_end = find_next_quote(line, col_start + 1, quotechar, 4497 curbuf->b_p_qe); 4498 if (col_end < 0) 4499 return FALSE; 4500 /* If is cursor between start and end quote character, it is 4501 * target text object. */ 4502 if (col_start <= first_col && first_col <= col_end) 4503 break; 4504 col_start = col_end + 1; 4505 } 4506 } 4507 else 4508 { 4509 /* Search backward for a starting quote. */ 4510 col_start = find_prev_quote(line, col_start, quotechar, curbuf->b_p_qe); 4511 if (line[col_start] != quotechar) 4512 { 4513 /* No quote before the cursor, look after the cursor. */ 4514 col_start = find_next_quote(line, col_start, quotechar, NULL); 4515 if (col_start < 0) 4516 return FALSE; 4517 } 4518 4519 /* Find close quote character. */ 4520 col_end = find_next_quote(line, col_start + 1, quotechar, 4521 curbuf->b_p_qe); 4522 if (col_end < 0) 4523 return FALSE; 4524 } 4525 4526 /* When "include" is TRUE, include spaces after closing quote or before 4527 * the starting quote. */ 4528 if (include) 4529 { 4530 if (VIM_ISWHITE(line[col_end + 1])) 4531 while (VIM_ISWHITE(line[col_end + 1])) 4532 ++col_end; 4533 else 4534 while (col_start > 0 && VIM_ISWHITE(line[col_start - 1])) 4535 --col_start; 4536 } 4537 4538 /* Set start position. After vi" another i" must include the ". 4539 * For v2i" include the quotes. */ 4540 if (!include && count < 2 && (vis_empty || !inside_quotes)) 4541 ++col_start; 4542 curwin->w_cursor.col = col_start; 4543 if (VIsual_active) 4544 { 4545 /* Set the start of the Visual area when the Visual area was empty, we 4546 * were just inside quotes or the Visual area didn't start at a quote 4547 * and didn't include a quote. 4548 */ 4549 if (vis_empty 4550 || (vis_bef_curs 4551 && !selected_quote 4552 && (inside_quotes 4553 || (line[VIsual.col] != quotechar 4554 && (VIsual.col == 0 4555 || line[VIsual.col - 1] != quotechar))))) 4556 { 4557 VIsual = curwin->w_cursor; 4558 redraw_curbuf_later(INVERTED); 4559 } 4560 } 4561 else 4562 { 4563 oap->start = curwin->w_cursor; 4564 oap->motion_type = MCHAR; 4565 } 4566 4567 /* Set end position. */ 4568 curwin->w_cursor.col = col_end; 4569 if ((include || count > 1 /* After vi" another i" must include the ". */ 4570 || (!vis_empty && inside_quotes) 4571 ) && inc_cursor() == 2) 4572 inclusive = TRUE; 4573 if (VIsual_active) 4574 { 4575 if (vis_empty || vis_bef_curs) 4576 { 4577 /* decrement cursor when 'selection' is not exclusive */ 4578 if (*p_sel != 'e') 4579 dec_cursor(); 4580 } 4581 else 4582 { 4583 /* Cursor is at start of Visual area. Set the end of the Visual 4584 * area when it was just inside quotes or it didn't end at a 4585 * quote. */ 4586 if (inside_quotes 4587 || (!selected_quote 4588 && line[VIsual.col] != quotechar 4589 && (line[VIsual.col] == NUL 4590 || line[VIsual.col + 1] != quotechar))) 4591 { 4592 dec_cursor(); 4593 VIsual = curwin->w_cursor; 4594 } 4595 curwin->w_cursor.col = col_start; 4596 } 4597 if (VIsual_mode == 'V') 4598 { 4599 VIsual_mode = 'v'; 4600 redraw_cmdline = TRUE; /* show mode later */ 4601 } 4602 } 4603 else 4604 { 4605 /* Set inclusive and other oap's flags. */ 4606 oap->inclusive = inclusive; 4607 } 4608 4609 return OK; 4610 } 4611 4612 #endif /* FEAT_TEXTOBJ */ 4613 4614 static int is_one_char(char_u *pattern, int move, pos_T *cur); 4615 4616 /* 4617 * Find next search match under cursor, cursor at end. 4618 * Used while an operator is pending, and in Visual mode. 4619 */ 4620 int 4621 current_search( 4622 long count, 4623 int forward) /* move forward or backwards */ 4624 { 4625 pos_T start_pos; /* position before the pattern */ 4626 pos_T orig_pos; /* position of the cursor at beginning */ 4627 pos_T pos; /* position after the pattern */ 4628 int i; 4629 int dir; 4630 int result; /* result of various function calls */ 4631 char_u old_p_ws = p_ws; 4632 int flags = 0; 4633 pos_T save_VIsual = VIsual; 4634 int one_char; 4635 4636 /* wrapping should not occur */ 4637 p_ws = FALSE; 4638 4639 /* Correct cursor when 'selection' is exclusive */ 4640 if (VIsual_active && *p_sel == 'e' && LT_POS(VIsual, curwin->w_cursor)) 4641 dec_cursor(); 4642 4643 if (VIsual_active) 4644 { 4645 orig_pos = curwin->w_cursor; 4646 4647 pos = curwin->w_cursor; 4648 4649 /* make sure, searching further will extend the match */ 4650 if (VIsual_active) 4651 { 4652 if (forward) 4653 incl(&pos); 4654 else 4655 decl(&pos); 4656 } 4657 } 4658 else 4659 orig_pos = pos = curwin->w_cursor; 4660 4661 /* Is the pattern is zero-width? */ 4662 one_char = is_one_char(spats[last_idx].pat, TRUE, &curwin->w_cursor); 4663 if (one_char == -1) 4664 { 4665 p_ws = old_p_ws; 4666 return FAIL; /* pattern not found */ 4667 } 4668 4669 /* 4670 * The trick is to first search backwards and then search forward again, 4671 * so that a match at the current cursor position will be correctly 4672 * captured. 4673 */ 4674 for (i = 0; i < 2; i++) 4675 { 4676 if (forward) 4677 dir = i; 4678 else 4679 dir = !i; 4680 4681 flags = 0; 4682 if (!dir && !one_char) 4683 flags = SEARCH_END; 4684 4685 result = searchit(curwin, curbuf, &pos, (dir ? FORWARD : BACKWARD), 4686 spats[last_idx].pat, (long) (i ? count : 1), 4687 SEARCH_KEEP | flags, RE_SEARCH, 0, NULL, NULL); 4688 4689 /* First search may fail, but then start searching from the 4690 * beginning of the file (cursor might be on the search match) 4691 * except when Visual mode is active, so that extending the visual 4692 * selection works. */ 4693 if (!result && i) /* not found, abort */ 4694 { 4695 curwin->w_cursor = orig_pos; 4696 if (VIsual_active) 4697 VIsual = save_VIsual; 4698 p_ws = old_p_ws; 4699 return FAIL; 4700 } 4701 else if (!i && !result) 4702 { 4703 if (forward) 4704 { 4705 /* try again from start of buffer */ 4706 CLEAR_POS(&pos); 4707 } 4708 else 4709 { 4710 /* try again from end of buffer */ 4711 /* searching backwards, so set pos to last line and col */ 4712 pos.lnum = curwin->w_buffer->b_ml.ml_line_count; 4713 pos.col = (colnr_T)STRLEN( 4714 ml_get(curwin->w_buffer->b_ml.ml_line_count)); 4715 } 4716 } 4717 p_ws = old_p_ws; 4718 } 4719 4720 start_pos = pos; 4721 flags = forward ? SEARCH_END : 0; 4722 4723 /* Check again from the current cursor position, 4724 * since the next match might actually by only one char wide */ 4725 one_char = is_one_char(spats[last_idx].pat, FALSE, &pos); 4726 if (one_char < 0) 4727 /* search failed, abort */ 4728 return FAIL; 4729 4730 /* move to match, except for zero-width matches, in which case, we are 4731 * already on the next match */ 4732 if (!one_char) 4733 result = searchit(curwin, curbuf, &pos, (forward ? FORWARD : BACKWARD), 4734 spats[last_idx].pat, 0L, flags | SEARCH_KEEP, RE_SEARCH, 0, 4735 NULL, NULL); 4736 4737 if (!VIsual_active) 4738 VIsual = start_pos; 4739 4740 curwin->w_cursor = pos; 4741 VIsual_active = TRUE; 4742 VIsual_mode = 'v'; 4743 4744 if (VIsual_active) 4745 { 4746 redraw_curbuf_later(INVERTED); /* update the inversion */ 4747 if (*p_sel == 'e') 4748 { 4749 /* Correction for exclusive selection depends on the direction. */ 4750 if (forward && LTOREQ_POS(VIsual, curwin->w_cursor)) 4751 inc_cursor(); 4752 else if (!forward && LTOREQ_POS(curwin->w_cursor, VIsual)) 4753 inc(&VIsual); 4754 } 4755 4756 } 4757 4758 #ifdef FEAT_FOLDING 4759 if (fdo_flags & FDO_SEARCH && KeyTyped) 4760 foldOpenCursor(); 4761 #endif 4762 4763 may_start_select('c'); 4764 #ifdef FEAT_MOUSE 4765 setmouse(); 4766 #endif 4767 #ifdef FEAT_CLIPBOARD 4768 /* Make sure the clipboard gets updated. Needed because start and 4769 * end are still the same, and the selection needs to be owned */ 4770 clip_star.vmode = NUL; 4771 #endif 4772 redraw_curbuf_later(INVERTED); 4773 showmode(); 4774 4775 return OK; 4776 } 4777 4778 /* 4779 * Check if the pattern is one character long or zero-width. 4780 * If move is TRUE, check from the beginning of the buffer, else from position 4781 * "cur". 4782 * Returns TRUE, FALSE or -1 for failure. 4783 */ 4784 static int 4785 is_one_char(char_u *pattern, int move, pos_T *cur) 4786 { 4787 regmmatch_T regmatch; 4788 int nmatched = 0; 4789 int result = -1; 4790 pos_T pos; 4791 int save_called_emsg = called_emsg; 4792 int flag = 0; 4793 4794 if (pattern == NULL) 4795 pattern = spats[last_idx].pat; 4796 4797 if (search_regcomp(pattern, RE_SEARCH, RE_SEARCH, 4798 SEARCH_KEEP, ®match) == FAIL) 4799 return -1; 4800 4801 /* init startcol correctly */ 4802 regmatch.startpos[0].col = -1; 4803 /* move to match */ 4804 if (move) 4805 { 4806 CLEAR_POS(&pos); 4807 } 4808 else 4809 { 4810 pos = *cur; 4811 /* accept a match at the cursor position */ 4812 flag = SEARCH_START; 4813 } 4814 4815 if (searchit(curwin, curbuf, &pos, FORWARD, pattern, 1, 4816 SEARCH_KEEP + flag, RE_SEARCH, 0, NULL, NULL) != FAIL) 4817 { 4818 /* Zero-width pattern should match somewhere, then we can check if 4819 * start and end are in the same position. */ 4820 called_emsg = FALSE; 4821 do 4822 { 4823 regmatch.startpos[0].col++; 4824 nmatched = vim_regexec_multi(®match, curwin, curbuf, 4825 pos.lnum, regmatch.startpos[0].col, NULL, NULL); 4826 if (!nmatched) 4827 break; 4828 } while (regmatch.startpos[0].col < pos.col); 4829 4830 if (!called_emsg) 4831 { 4832 result = (nmatched != 0 4833 && regmatch.startpos[0].lnum == regmatch.endpos[0].lnum 4834 && regmatch.startpos[0].col == regmatch.endpos[0].col); 4835 /* one char width */ 4836 if (!result && inc(&pos) >= 0 && pos.col == regmatch.endpos[0].col) 4837 result = TRUE; 4838 } 4839 } 4840 4841 called_emsg |= save_called_emsg; 4842 vim_regfree(regmatch.regprog); 4843 return result; 4844 } 4845 4846 #if defined(FEAT_LISP) || defined(FEAT_CINDENT) || defined(FEAT_TEXTOBJ) \ 4847 || defined(PROTO) 4848 /* 4849 * return TRUE if line 'lnum' is empty or has white chars only. 4850 */ 4851 int 4852 linewhite(linenr_T lnum) 4853 { 4854 char_u *p; 4855 4856 p = skipwhite(ml_get(lnum)); 4857 return (*p == NUL); 4858 } 4859 #endif 4860 4861 #if defined(FEAT_FIND_ID) || defined(PROTO) 4862 /* 4863 * Find identifiers or defines in included files. 4864 * If p_ic && (compl_cont_status & CONT_SOL) then ptr must be in lowercase. 4865 */ 4866 void 4867 find_pattern_in_path( 4868 char_u *ptr, /* pointer to search pattern */ 4869 int dir UNUSED, /* direction of expansion */ 4870 int len, /* length of search pattern */ 4871 int whole, /* match whole words only */ 4872 int skip_comments, /* don't match inside comments */ 4873 int type, /* Type of search; are we looking for a type? 4874 a macro? */ 4875 long count, 4876 int action, /* What to do when we find it */ 4877 linenr_T start_lnum, /* first line to start searching */ 4878 linenr_T end_lnum) /* last line for searching */ 4879 { 4880 SearchedFile *files; /* Stack of included files */ 4881 SearchedFile *bigger; /* When we need more space */ 4882 int max_path_depth = 50; 4883 long match_count = 1; 4884 4885 char_u *pat; 4886 char_u *new_fname; 4887 char_u *curr_fname = curbuf->b_fname; 4888 char_u *prev_fname = NULL; 4889 linenr_T lnum; 4890 int depth; 4891 int depth_displayed; /* For type==CHECK_PATH */ 4892 int old_files; 4893 int already_searched; 4894 char_u *file_line; 4895 char_u *line; 4896 char_u *p; 4897 char_u save_char; 4898 int define_matched; 4899 regmatch_T regmatch; 4900 regmatch_T incl_regmatch; 4901 regmatch_T def_regmatch; 4902 int matched = FALSE; 4903 int did_show = FALSE; 4904 int found = FALSE; 4905 int i; 4906 char_u *already = NULL; 4907 char_u *startp = NULL; 4908 char_u *inc_opt = NULL; 4909 #if defined(FEAT_WINDOWS) && defined(FEAT_QUICKFIX) 4910 win_T *curwin_save = NULL; 4911 #endif 4912 4913 regmatch.regprog = NULL; 4914 incl_regmatch.regprog = NULL; 4915 def_regmatch.regprog = NULL; 4916 4917 file_line = alloc(LSIZE); 4918 if (file_line == NULL) 4919 return; 4920 4921 if (type != CHECK_PATH && type != FIND_DEFINE 4922 #ifdef FEAT_INS_EXPAND 4923 /* when CONT_SOL is set compare "ptr" with the beginning of the line 4924 * is faster than quote_meta/regcomp/regexec "ptr" -- Acevedo */ 4925 && !(compl_cont_status & CONT_SOL) 4926 #endif 4927 ) 4928 { 4929 pat = alloc(len + 5); 4930 if (pat == NULL) 4931 goto fpip_end; 4932 sprintf((char *)pat, whole ? "\\<%.*s\\>" : "%.*s", len, ptr); 4933 /* ignore case according to p_ic, p_scs and pat */ 4934 regmatch.rm_ic = ignorecase(pat); 4935 regmatch.regprog = vim_regcomp(pat, p_magic ? RE_MAGIC : 0); 4936 vim_free(pat); 4937 if (regmatch.regprog == NULL) 4938 goto fpip_end; 4939 } 4940 inc_opt = (*curbuf->b_p_inc == NUL) ? p_inc : curbuf->b_p_inc; 4941 if (*inc_opt != NUL) 4942 { 4943 incl_regmatch.regprog = vim_regcomp(inc_opt, p_magic ? RE_MAGIC : 0); 4944 if (incl_regmatch.regprog == NULL) 4945 goto fpip_end; 4946 incl_regmatch.rm_ic = FALSE; /* don't ignore case in incl. pat. */ 4947 } 4948 if (type == FIND_DEFINE && (*curbuf->b_p_def != NUL || *p_def != NUL)) 4949 { 4950 def_regmatch.regprog = vim_regcomp(*curbuf->b_p_def == NUL 4951 ? p_def : curbuf->b_p_def, p_magic ? RE_MAGIC : 0); 4952 if (def_regmatch.regprog == NULL) 4953 goto fpip_end; 4954 def_regmatch.rm_ic = FALSE; /* don't ignore case in define pat. */ 4955 } 4956 files = (SearchedFile *)lalloc_clear((long_u) 4957 (max_path_depth * sizeof(SearchedFile)), TRUE); 4958 if (files == NULL) 4959 goto fpip_end; 4960 old_files = max_path_depth; 4961 depth = depth_displayed = -1; 4962 4963 lnum = start_lnum; 4964 if (end_lnum > curbuf->b_ml.ml_line_count) 4965 end_lnum = curbuf->b_ml.ml_line_count; 4966 if (lnum > end_lnum) /* do at least one line */ 4967 lnum = end_lnum; 4968 line = ml_get(lnum); 4969 4970 for (;;) 4971 { 4972 if (incl_regmatch.regprog != NULL 4973 && vim_regexec(&incl_regmatch, line, (colnr_T)0)) 4974 { 4975 char_u *p_fname = (curr_fname == curbuf->b_fname) 4976 ? curbuf->b_ffname : curr_fname; 4977 4978 if (inc_opt != NULL && strstr((char *)inc_opt, "\\zs") != NULL) 4979 /* Use text from '\zs' to '\ze' (or end) of 'include'. */ 4980 new_fname = find_file_name_in_path(incl_regmatch.startp[0], 4981 (int)(incl_regmatch.endp[0] - incl_regmatch.startp[0]), 4982 FNAME_EXP|FNAME_INCL|FNAME_REL, 1L, p_fname); 4983 else 4984 /* Use text after match with 'include'. */ 4985 new_fname = file_name_in_line(incl_regmatch.endp[0], 0, 4986 FNAME_EXP|FNAME_INCL|FNAME_REL, 1L, p_fname, NULL); 4987 already_searched = FALSE; 4988 if (new_fname != NULL) 4989 { 4990 /* Check whether we have already searched in this file */ 4991 for (i = 0;; i++) 4992 { 4993 if (i == depth + 1) 4994 i = old_files; 4995 if (i == max_path_depth) 4996 break; 4997 if (fullpathcmp(new_fname, files[i].name, TRUE) & FPC_SAME) 4998 { 4999 if (type != CHECK_PATH && 5000 action == ACTION_SHOW_ALL && files[i].matched) 5001 { 5002 msg_putchar('\n'); /* cursor below last one */ 5003 if (!got_int) /* don't display if 'q' 5004 typed at "--more--" 5005 message */ 5006 { 5007 msg_home_replace_hl(new_fname); 5008 MSG_PUTS(_(" (includes previously listed match)")); 5009 prev_fname = NULL; 5010 } 5011 } 5012 vim_free(new_fname); 5013 new_fname = NULL; 5014 already_searched = TRUE; 5015 break; 5016 } 5017 } 5018 } 5019 5020 if (type == CHECK_PATH && (action == ACTION_SHOW_ALL 5021 || (new_fname == NULL && !already_searched))) 5022 { 5023 if (did_show) 5024 msg_putchar('\n'); /* cursor below last one */ 5025 else 5026 { 5027 gotocmdline(TRUE); /* cursor at status line */ 5028 MSG_PUTS_TITLE(_("--- Included files ")); 5029 if (action != ACTION_SHOW_ALL) 5030 MSG_PUTS_TITLE(_("not found ")); 5031 MSG_PUTS_TITLE(_("in path ---\n")); 5032 } 5033 did_show = TRUE; 5034 while (depth_displayed < depth && !got_int) 5035 { 5036 ++depth_displayed; 5037 for (i = 0; i < depth_displayed; i++) 5038 MSG_PUTS(" "); 5039 msg_home_replace(files[depth_displayed].name); 5040 MSG_PUTS(" -->\n"); 5041 } 5042 if (!got_int) /* don't display if 'q' typed 5043 for "--more--" message */ 5044 { 5045 for (i = 0; i <= depth_displayed; i++) 5046 MSG_PUTS(" "); 5047 if (new_fname != NULL) 5048 { 5049 /* using "new_fname" is more reliable, e.g., when 5050 * 'includeexpr' is set. */ 5051 msg_outtrans_attr(new_fname, HL_ATTR(HLF_D)); 5052 } 5053 else 5054 { 5055 /* 5056 * Isolate the file name. 5057 * Include the surrounding "" or <> if present. 5058 */ 5059 if (inc_opt != NULL 5060 && strstr((char *)inc_opt, "\\zs") != NULL) 5061 { 5062 /* pattern contains \zs, use the match */ 5063 p = incl_regmatch.startp[0]; 5064 i = (int)(incl_regmatch.endp[0] 5065 - incl_regmatch.startp[0]); 5066 } 5067 else 5068 { 5069 /* find the file name after the end of the match */ 5070 for (p = incl_regmatch.endp[0]; 5071 *p && !vim_isfilec(*p); p++) 5072 ; 5073 for (i = 0; vim_isfilec(p[i]); i++) 5074 ; 5075 } 5076 5077 if (i == 0) 5078 { 5079 /* Nothing found, use the rest of the line. */ 5080 p = incl_regmatch.endp[0]; 5081 i = (int)STRLEN(p); 5082 } 5083 /* Avoid checking before the start of the line, can 5084 * happen if \zs appears in the regexp. */ 5085 else if (p > line) 5086 { 5087 if (p[-1] == '"' || p[-1] == '<') 5088 { 5089 --p; 5090 ++i; 5091 } 5092 if (p[i] == '"' || p[i] == '>') 5093 ++i; 5094 } 5095 save_char = p[i]; 5096 p[i] = NUL; 5097 msg_outtrans_attr(p, HL_ATTR(HLF_D)); 5098 p[i] = save_char; 5099 } 5100 5101 if (new_fname == NULL && action == ACTION_SHOW_ALL) 5102 { 5103 if (already_searched) 5104 MSG_PUTS(_(" (Already listed)")); 5105 else 5106 MSG_PUTS(_(" NOT FOUND")); 5107 } 5108 } 5109 out_flush(); /* output each line directly */ 5110 } 5111 5112 if (new_fname != NULL) 5113 { 5114 /* Push the new file onto the file stack */ 5115 if (depth + 1 == old_files) 5116 { 5117 bigger = (SearchedFile *)lalloc((long_u)( 5118 max_path_depth * 2 * sizeof(SearchedFile)), TRUE); 5119 if (bigger != NULL) 5120 { 5121 for (i = 0; i <= depth; i++) 5122 bigger[i] = files[i]; 5123 for (i = depth + 1; i < old_files + max_path_depth; i++) 5124 { 5125 bigger[i].fp = NULL; 5126 bigger[i].name = NULL; 5127 bigger[i].lnum = 0; 5128 bigger[i].matched = FALSE; 5129 } 5130 for (i = old_files; i < max_path_depth; i++) 5131 bigger[i + max_path_depth] = files[i]; 5132 old_files += max_path_depth; 5133 max_path_depth *= 2; 5134 vim_free(files); 5135 files = bigger; 5136 } 5137 } 5138 if ((files[depth + 1].fp = mch_fopen((char *)new_fname, "r")) 5139 == NULL) 5140 vim_free(new_fname); 5141 else 5142 { 5143 if (++depth == old_files) 5144 { 5145 /* 5146 * lalloc() for 'bigger' must have failed above. We 5147 * will forget one of our already visited files now. 5148 */ 5149 vim_free(files[old_files].name); 5150 ++old_files; 5151 } 5152 files[depth].name = curr_fname = new_fname; 5153 files[depth].lnum = 0; 5154 files[depth].matched = FALSE; 5155 #ifdef FEAT_INS_EXPAND 5156 if (action == ACTION_EXPAND) 5157 { 5158 msg_hist_off = TRUE; /* reset in msg_trunc_attr() */ 5159 vim_snprintf((char*)IObuff, IOSIZE, 5160 _("Scanning included file: %s"), 5161 (char *)new_fname); 5162 msg_trunc_attr(IObuff, TRUE, HL_ATTR(HLF_R)); 5163 } 5164 else 5165 #endif 5166 if (p_verbose >= 5) 5167 { 5168 verbose_enter(); 5169 smsg((char_u *)_("Searching included file %s"), 5170 (char *)new_fname); 5171 verbose_leave(); 5172 } 5173 5174 } 5175 } 5176 } 5177 else 5178 { 5179 /* 5180 * Check if the line is a define (type == FIND_DEFINE) 5181 */ 5182 p = line; 5183 search_line: 5184 define_matched = FALSE; 5185 if (def_regmatch.regprog != NULL 5186 && vim_regexec(&def_regmatch, line, (colnr_T)0)) 5187 { 5188 /* 5189 * Pattern must be first identifier after 'define', so skip 5190 * to that position before checking for match of pattern. Also 5191 * don't let it match beyond the end of this identifier. 5192 */ 5193 p = def_regmatch.endp[0]; 5194 while (*p && !vim_iswordc(*p)) 5195 p++; 5196 define_matched = TRUE; 5197 } 5198 5199 /* 5200 * Look for a match. Don't do this if we are looking for a 5201 * define and this line didn't match define_prog above. 5202 */ 5203 if (def_regmatch.regprog == NULL || define_matched) 5204 { 5205 if (define_matched 5206 #ifdef FEAT_INS_EXPAND 5207 || (compl_cont_status & CONT_SOL) 5208 #endif 5209 ) 5210 { 5211 /* compare the first "len" chars from "ptr" */ 5212 startp = skipwhite(p); 5213 if (p_ic) 5214 matched = !MB_STRNICMP(startp, ptr, len); 5215 else 5216 matched = !STRNCMP(startp, ptr, len); 5217 if (matched && define_matched && whole 5218 && vim_iswordc(startp[len])) 5219 matched = FALSE; 5220 } 5221 else if (regmatch.regprog != NULL 5222 && vim_regexec(®match, line, (colnr_T)(p - line))) 5223 { 5224 matched = TRUE; 5225 startp = regmatch.startp[0]; 5226 /* 5227 * Check if the line is not a comment line (unless we are 5228 * looking for a define). A line starting with "# define" 5229 * is not considered to be a comment line. 5230 */ 5231 if (!define_matched && skip_comments) 5232 { 5233 #ifdef FEAT_COMMENTS 5234 if ((*line != '#' || 5235 STRNCMP(skipwhite(line + 1), "define", 6) != 0) 5236 && get_leader_len(line, NULL, FALSE, TRUE)) 5237 matched = FALSE; 5238 5239 /* 5240 * Also check for a "/ *" or "/ /" before the match. 5241 * Skips lines like "int backwards; / * normal index 5242 * * /" when looking for "normal". 5243 * Note: Doesn't skip "/ *" in comments. 5244 */ 5245 p = skipwhite(line); 5246 if (matched 5247 || (p[0] == '/' && p[1] == '*') || p[0] == '*') 5248 #endif 5249 for (p = line; *p && p < startp; ++p) 5250 { 5251 if (matched 5252 && p[0] == '/' 5253 && (p[1] == '*' || p[1] == '/')) 5254 { 5255 matched = FALSE; 5256 /* After "//" all text is comment */ 5257 if (p[1] == '/') 5258 break; 5259 ++p; 5260 } 5261 else if (!matched && p[0] == '*' && p[1] == '/') 5262 { 5263 /* Can find match after "* /". */ 5264 matched = TRUE; 5265 ++p; 5266 } 5267 } 5268 } 5269 } 5270 } 5271 } 5272 if (matched) 5273 { 5274 #ifdef FEAT_INS_EXPAND 5275 if (action == ACTION_EXPAND) 5276 { 5277 int reuse = 0; 5278 int add_r; 5279 char_u *aux; 5280 5281 if (depth == -1 && lnum == curwin->w_cursor.lnum) 5282 break; 5283 found = TRUE; 5284 aux = p = startp; 5285 if (compl_cont_status & CONT_ADDING) 5286 { 5287 p += compl_length; 5288 if (vim_iswordp(p)) 5289 goto exit_matched; 5290 p = find_word_start(p); 5291 } 5292 p = find_word_end(p); 5293 i = (int)(p - aux); 5294 5295 if ((compl_cont_status & CONT_ADDING) && i == compl_length) 5296 { 5297 /* IOSIZE > compl_length, so the STRNCPY works */ 5298 STRNCPY(IObuff, aux, i); 5299 5300 /* Get the next line: when "depth" < 0 from the current 5301 * buffer, otherwise from the included file. Jump to 5302 * exit_matched when past the last line. */ 5303 if (depth < 0) 5304 { 5305 if (lnum >= end_lnum) 5306 goto exit_matched; 5307 line = ml_get(++lnum); 5308 } 5309 else if (vim_fgets(line = file_line, 5310 LSIZE, files[depth].fp)) 5311 goto exit_matched; 5312 5313 /* we read a line, set "already" to check this "line" later 5314 * if depth >= 0 we'll increase files[depth].lnum far 5315 * bellow -- Acevedo */ 5316 already = aux = p = skipwhite(line); 5317 p = find_word_start(p); 5318 p = find_word_end(p); 5319 if (p > aux) 5320 { 5321 if (*aux != ')' && IObuff[i-1] != TAB) 5322 { 5323 if (IObuff[i-1] != ' ') 5324 IObuff[i++] = ' '; 5325 /* IObuf =~ "\(\k\|\i\).* ", thus i >= 2*/ 5326 if (p_js 5327 && (IObuff[i-2] == '.' 5328 || (vim_strchr(p_cpo, CPO_JOINSP) == NULL 5329 && (IObuff[i-2] == '?' 5330 || IObuff[i-2] == '!')))) 5331 IObuff[i++] = ' '; 5332 } 5333 /* copy as much as possible of the new word */ 5334 if (p - aux >= IOSIZE - i) 5335 p = aux + IOSIZE - i - 1; 5336 STRNCPY(IObuff + i, aux, p - aux); 5337 i += (int)(p - aux); 5338 reuse |= CONT_S_IPOS; 5339 } 5340 IObuff[i] = NUL; 5341 aux = IObuff; 5342 5343 if (i == compl_length) 5344 goto exit_matched; 5345 } 5346 5347 add_r = ins_compl_add_infercase(aux, i, p_ic, 5348 curr_fname == curbuf->b_fname ? NULL : curr_fname, 5349 dir, reuse); 5350 if (add_r == OK) 5351 /* if dir was BACKWARD then honor it just once */ 5352 dir = FORWARD; 5353 else if (add_r == FAIL) 5354 break; 5355 } 5356 else 5357 #endif 5358 if (action == ACTION_SHOW_ALL) 5359 { 5360 found = TRUE; 5361 if (!did_show) 5362 gotocmdline(TRUE); /* cursor at status line */ 5363 if (curr_fname != prev_fname) 5364 { 5365 if (did_show) 5366 msg_putchar('\n'); /* cursor below last one */ 5367 if (!got_int) /* don't display if 'q' typed 5368 at "--more--" message */ 5369 msg_home_replace_hl(curr_fname); 5370 prev_fname = curr_fname; 5371 } 5372 did_show = TRUE; 5373 if (!got_int) 5374 show_pat_in_path(line, type, TRUE, action, 5375 (depth == -1) ? NULL : files[depth].fp, 5376 (depth == -1) ? &lnum : &files[depth].lnum, 5377 match_count++); 5378 5379 /* Set matched flag for this file and all the ones that 5380 * include it */ 5381 for (i = 0; i <= depth; ++i) 5382 files[i].matched = TRUE; 5383 } 5384 else if (--count <= 0) 5385 { 5386 found = TRUE; 5387 if (depth == -1 && lnum == curwin->w_cursor.lnum 5388 #if defined(FEAT_WINDOWS) && defined(FEAT_QUICKFIX) 5389 && g_do_tagpreview == 0 5390 #endif 5391 ) 5392 EMSG(_("E387: Match is on current line")); 5393 else if (action == ACTION_SHOW) 5394 { 5395 show_pat_in_path(line, type, did_show, action, 5396 (depth == -1) ? NULL : files[depth].fp, 5397 (depth == -1) ? &lnum : &files[depth].lnum, 1L); 5398 did_show = TRUE; 5399 } 5400 else 5401 { 5402 #ifdef FEAT_GUI 5403 need_mouse_correct = TRUE; 5404 #endif 5405 #if defined(FEAT_WINDOWS) && defined(FEAT_QUICKFIX) 5406 /* ":psearch" uses the preview window */ 5407 if (g_do_tagpreview != 0) 5408 { 5409 curwin_save = curwin; 5410 prepare_tagpreview(TRUE); 5411 } 5412 #endif 5413 if (action == ACTION_SPLIT) 5414 { 5415 #ifdef FEAT_WINDOWS 5416 if (win_split(0, 0) == FAIL) 5417 #endif 5418 break; 5419 RESET_BINDING(curwin); 5420 } 5421 if (depth == -1) 5422 { 5423 /* match in current file */ 5424 #if defined(FEAT_WINDOWS) && defined(FEAT_QUICKFIX) 5425 if (g_do_tagpreview != 0) 5426 { 5427 if (!GETFILE_SUCCESS(getfile( 5428 curwin_save->w_buffer->b_fnum, NULL, 5429 NULL, TRUE, lnum, FALSE))) 5430 break; /* failed to jump to file */ 5431 } 5432 else 5433 #endif 5434 setpcmark(); 5435 curwin->w_cursor.lnum = lnum; 5436 check_cursor(); 5437 } 5438 else 5439 { 5440 if (!GETFILE_SUCCESS(getfile( 5441 0, files[depth].name, NULL, TRUE, 5442 files[depth].lnum, FALSE))) 5443 break; /* failed to jump to file */ 5444 /* autocommands may have changed the lnum, we don't 5445 * want that here */ 5446 curwin->w_cursor.lnum = files[depth].lnum; 5447 } 5448 } 5449 if (action != ACTION_SHOW) 5450 { 5451 curwin->w_cursor.col = (colnr_T)(startp - line); 5452 curwin->w_set_curswant = TRUE; 5453 } 5454 5455 #if defined(FEAT_WINDOWS) && defined(FEAT_QUICKFIX) 5456 if (g_do_tagpreview != 0 5457 && curwin != curwin_save && win_valid(curwin_save)) 5458 { 5459 /* Return cursor to where we were */ 5460 validate_cursor(); 5461 redraw_later(VALID); 5462 win_enter(curwin_save, TRUE); 5463 } 5464 #endif 5465 break; 5466 } 5467 #ifdef FEAT_INS_EXPAND 5468 exit_matched: 5469 #endif 5470 matched = FALSE; 5471 /* look for other matches in the rest of the line if we 5472 * are not at the end of it already */ 5473 if (def_regmatch.regprog == NULL 5474 #ifdef FEAT_INS_EXPAND 5475 && action == ACTION_EXPAND 5476 && !(compl_cont_status & CONT_SOL) 5477 #endif 5478 && *startp != NUL 5479 && *(p = startp + MB_PTR2LEN(startp)) != NUL) 5480 goto search_line; 5481 } 5482 line_breakcheck(); 5483 #ifdef FEAT_INS_EXPAND 5484 if (action == ACTION_EXPAND) 5485 ins_compl_check_keys(30, FALSE); 5486 if (got_int || compl_interrupted) 5487 #else 5488 if (got_int) 5489 #endif 5490 break; 5491 5492 /* 5493 * Read the next line. When reading an included file and encountering 5494 * end-of-file, close the file and continue in the file that included 5495 * it. 5496 */ 5497 while (depth >= 0 && !already 5498 && vim_fgets(line = file_line, LSIZE, files[depth].fp)) 5499 { 5500 fclose(files[depth].fp); 5501 --old_files; 5502 files[old_files].name = files[depth].name; 5503 files[old_files].matched = files[depth].matched; 5504 --depth; 5505 curr_fname = (depth == -1) ? curbuf->b_fname 5506 : files[depth].name; 5507 if (depth < depth_displayed) 5508 depth_displayed = depth; 5509 } 5510 if (depth >= 0) /* we could read the line */ 5511 { 5512 files[depth].lnum++; 5513 /* Remove any CR and LF from the line. */ 5514 i = (int)STRLEN(line); 5515 if (i > 0 && line[i - 1] == '\n') 5516 line[--i] = NUL; 5517 if (i > 0 && line[i - 1] == '\r') 5518 line[--i] = NUL; 5519 } 5520 else if (!already) 5521 { 5522 if (++lnum > end_lnum) 5523 break; 5524 line = ml_get(lnum); 5525 } 5526 already = NULL; 5527 } 5528 /* End of big for (;;) loop. */ 5529 5530 /* Close any files that are still open. */ 5531 for (i = 0; i <= depth; i++) 5532 { 5533 fclose(files[i].fp); 5534 vim_free(files[i].name); 5535 } 5536 for (i = old_files; i < max_path_depth; i++) 5537 vim_free(files[i].name); 5538 vim_free(files); 5539 5540 if (type == CHECK_PATH) 5541 { 5542 if (!did_show) 5543 { 5544 if (action != ACTION_SHOW_ALL) 5545 MSG(_("All included files were found")); 5546 else 5547 MSG(_("No included files")); 5548 } 5549 } 5550 else if (!found 5551 #ifdef FEAT_INS_EXPAND 5552 && action != ACTION_EXPAND 5553 #endif 5554 ) 5555 { 5556 #ifdef FEAT_INS_EXPAND 5557 if (got_int || compl_interrupted) 5558 #else 5559 if (got_int) 5560 #endif 5561 EMSG(_(e_interr)); 5562 else if (type == FIND_DEFINE) 5563 EMSG(_("E388: Couldn't find definition")); 5564 else 5565 EMSG(_("E389: Couldn't find pattern")); 5566 } 5567 if (action == ACTION_SHOW || action == ACTION_SHOW_ALL) 5568 msg_end(); 5569 5570 fpip_end: 5571 vim_free(file_line); 5572 vim_regfree(regmatch.regprog); 5573 vim_regfree(incl_regmatch.regprog); 5574 vim_regfree(def_regmatch.regprog); 5575 } 5576 5577 static void 5578 show_pat_in_path( 5579 char_u *line, 5580 int type, 5581 int did_show, 5582 int action, 5583 FILE *fp, 5584 linenr_T *lnum, 5585 long count) 5586 { 5587 char_u *p; 5588 5589 if (did_show) 5590 msg_putchar('\n'); /* cursor below last one */ 5591 else if (!msg_silent) 5592 gotocmdline(TRUE); /* cursor at status line */ 5593 if (got_int) /* 'q' typed at "--more--" message */ 5594 return; 5595 for (;;) 5596 { 5597 p = line + STRLEN(line) - 1; 5598 if (fp != NULL) 5599 { 5600 /* We used fgets(), so get rid of newline at end */ 5601 if (p >= line && *p == '\n') 5602 --p; 5603 if (p >= line && *p == '\r') 5604 --p; 5605 *(p + 1) = NUL; 5606 } 5607 if (action == ACTION_SHOW_ALL) 5608 { 5609 sprintf((char *)IObuff, "%3ld: ", count); /* show match nr */ 5610 msg_puts(IObuff); 5611 sprintf((char *)IObuff, "%4ld", *lnum); /* show line nr */ 5612 /* Highlight line numbers */ 5613 msg_puts_attr(IObuff, HL_ATTR(HLF_N)); 5614 MSG_PUTS(" "); 5615 } 5616 msg_prt_line(line, FALSE); 5617 out_flush(); /* show one line at a time */ 5618 5619 /* Definition continues until line that doesn't end with '\' */ 5620 if (got_int || type != FIND_DEFINE || p < line || *p != '\\') 5621 break; 5622 5623 if (fp != NULL) 5624 { 5625 if (vim_fgets(line, LSIZE, fp)) /* end of file */ 5626 break; 5627 ++*lnum; 5628 } 5629 else 5630 { 5631 if (++*lnum > curbuf->b_ml.ml_line_count) 5632 break; 5633 line = ml_get(*lnum); 5634 } 5635 msg_putchar('\n'); 5636 } 5637 } 5638 #endif 5639 5640 #ifdef FEAT_VIMINFO 5641 int 5642 read_viminfo_search_pattern(vir_T *virp, int force) 5643 { 5644 char_u *lp; 5645 int idx = -1; 5646 int magic = FALSE; 5647 int no_scs = FALSE; 5648 int off_line = FALSE; 5649 int off_end = 0; 5650 long off = 0; 5651 int setlast = FALSE; 5652 #ifdef FEAT_SEARCH_EXTRA 5653 static int hlsearch_on = FALSE; 5654 #endif 5655 char_u *val; 5656 5657 /* 5658 * Old line types: 5659 * "/pat", "&pat": search/subst. pat 5660 * "~/pat", "~&pat": last used search/subst. pat 5661 * New line types: 5662 * "~h", "~H": hlsearch highlighting off/on 5663 * "~<magic><smartcase><line><end><off><last><which>pat" 5664 * <magic>: 'm' off, 'M' on 5665 * <smartcase>: 's' off, 'S' on 5666 * <line>: 'L' line offset, 'l' char offset 5667 * <end>: 'E' from end, 'e' from start 5668 * <off>: decimal, offset 5669 * <last>: '~' last used pattern 5670 * <which>: '/' search pat, '&' subst. pat 5671 */ 5672 lp = virp->vir_line; 5673 if (lp[0] == '~' && (lp[1] == 'm' || lp[1] == 'M')) /* new line type */ 5674 { 5675 if (lp[1] == 'M') /* magic on */ 5676 magic = TRUE; 5677 if (lp[2] == 's') 5678 no_scs = TRUE; 5679 if (lp[3] == 'L') 5680 off_line = TRUE; 5681 if (lp[4] == 'E') 5682 off_end = SEARCH_END; 5683 lp += 5; 5684 off = getdigits(&lp); 5685 } 5686 if (lp[0] == '~') /* use this pattern for last-used pattern */ 5687 { 5688 setlast = TRUE; 5689 lp++; 5690 } 5691 if (lp[0] == '/') 5692 idx = RE_SEARCH; 5693 else if (lp[0] == '&') 5694 idx = RE_SUBST; 5695 #ifdef FEAT_SEARCH_EXTRA 5696 else if (lp[0] == 'h') /* ~h: 'hlsearch' highlighting off */ 5697 hlsearch_on = FALSE; 5698 else if (lp[0] == 'H') /* ~H: 'hlsearch' highlighting on */ 5699 hlsearch_on = TRUE; 5700 #endif 5701 if (idx >= 0) 5702 { 5703 if (force || spats[idx].pat == NULL) 5704 { 5705 val = viminfo_readstring(virp, (int)(lp - virp->vir_line + 1), 5706 TRUE); 5707 if (val != NULL) 5708 { 5709 set_last_search_pat(val, idx, magic, setlast); 5710 vim_free(val); 5711 spats[idx].no_scs = no_scs; 5712 spats[idx].off.line = off_line; 5713 spats[idx].off.end = off_end; 5714 spats[idx].off.off = off; 5715 #ifdef FEAT_SEARCH_EXTRA 5716 if (setlast) 5717 { 5718 SET_NO_HLSEARCH(!hlsearch_on); 5719 } 5720 #endif 5721 } 5722 } 5723 } 5724 return viminfo_readline(virp); 5725 } 5726 5727 void 5728 write_viminfo_search_pattern(FILE *fp) 5729 { 5730 if (get_viminfo_parameter('/') != 0) 5731 { 5732 #ifdef FEAT_SEARCH_EXTRA 5733 fprintf(fp, "\n# hlsearch on (H) or off (h):\n~%c", 5734 (no_hlsearch || find_viminfo_parameter('h') != NULL) ? 'h' : 'H'); 5735 #endif 5736 wvsp_one(fp, RE_SEARCH, "", '/'); 5737 wvsp_one(fp, RE_SUBST, _("Substitute "), '&'); 5738 } 5739 } 5740 5741 static void 5742 wvsp_one( 5743 FILE *fp, /* file to write to */ 5744 int idx, /* spats[] index */ 5745 char *s, /* search pat */ 5746 int sc) /* dir char */ 5747 { 5748 if (spats[idx].pat != NULL) 5749 { 5750 fprintf(fp, _("\n# Last %sSearch Pattern:\n~"), s); 5751 /* off.dir is not stored, it's reset to forward */ 5752 fprintf(fp, "%c%c%c%c%ld%s%c", 5753 spats[idx].magic ? 'M' : 'm', /* magic */ 5754 spats[idx].no_scs ? 's' : 'S', /* smartcase */ 5755 spats[idx].off.line ? 'L' : 'l', /* line offset */ 5756 spats[idx].off.end ? 'E' : 'e', /* offset from end */ 5757 spats[idx].off.off, /* offset */ 5758 last_idx == idx ? "~" : "", /* last used pat */ 5759 sc); 5760 viminfo_writestring(fp, spats[idx].pat); 5761 } 5762 } 5763 #endif /* FEAT_VIMINFO */ 5764