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