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