search.c (116759B)
1 // search.c: code for normal mode searching commands 2 3 #include <assert.h> 4 #include <inttypes.h> 5 #include <limits.h> 6 #include <stdbool.h> 7 #include <stdio.h> 8 #include <stdlib.h> 9 #include <string.h> 10 11 #include "nvim/ascii_defs.h" 12 #include "nvim/autocmd.h" 13 #include "nvim/autocmd_defs.h" 14 #include "nvim/buffer.h" 15 #include "nvim/buffer_defs.h" 16 #include "nvim/change.h" 17 #include "nvim/charset.h" 18 #include "nvim/cmdhist.h" 19 #include "nvim/cursor.h" 20 #include "nvim/drawscreen.h" 21 #include "nvim/errors.h" 22 #include "nvim/eval/typval.h" 23 #include "nvim/eval/typval_defs.h" 24 #include "nvim/eval/vars.h" 25 #include "nvim/ex_cmds.h" 26 #include "nvim/ex_cmds_defs.h" 27 #include "nvim/ex_docmd.h" 28 #include "nvim/ex_getln.h" 29 #include "nvim/file_search.h" 30 #include "nvim/fileio.h" 31 #include "nvim/fold.h" 32 #include "nvim/getchar.h" 33 #include "nvim/gettext_defs.h" 34 #include "nvim/globals.h" 35 #include "nvim/highlight_defs.h" 36 #include "nvim/indent_c.h" 37 #include "nvim/insexpand.h" 38 #include "nvim/macros_defs.h" 39 #include "nvim/mark.h" 40 #include "nvim/mark_defs.h" 41 #include "nvim/mbyte.h" 42 #include "nvim/memline.h" 43 #include "nvim/memory.h" 44 #include "nvim/message.h" 45 #include "nvim/mouse.h" 46 #include "nvim/move.h" 47 #include "nvim/normal.h" 48 #include "nvim/option.h" 49 #include "nvim/option_vars.h" 50 #include "nvim/os/fs.h" 51 #include "nvim/os/input.h" 52 #include "nvim/os/time.h" 53 #include "nvim/path.h" 54 #include "nvim/plines.h" 55 #include "nvim/profile.h" 56 #include "nvim/regexp.h" 57 #include "nvim/search.h" 58 #include "nvim/state_defs.h" 59 #include "nvim/strings.h" 60 #include "nvim/tag.h" 61 #include "nvim/ui.h" 62 #include "nvim/ui_defs.h" 63 #include "nvim/vim_defs.h" 64 #include "nvim/window.h" 65 66 #include "search.c.generated.h" 67 68 static const char e_search_hit_top_without_match_for_str[] 69 = N_("E384: Search hit TOP without match for: %s"); 70 static const char e_search_hit_bottom_without_match_for_str[] 71 = N_("E385: Search hit BOTTOM without match for: %s"); 72 73 // This file contains various searching-related routines. These fall into 74 // three groups: 75 // 1. string searches (for /, ?, n, and N) 76 // 2. character searches within a single line (for f, F, t, T, etc) 77 // 3. "other" kinds of searches like the '%' command, and 'word' searches. 78 // 79 // 80 // String searches 81 // 82 // The string search functions are divided into two levels: 83 // lowest: searchit(); uses a pos_T for starting position and found match. 84 // Highest: do_search(); uses curwin->w_cursor; calls searchit(). 85 // 86 // The last search pattern is remembered for repeating the same search. 87 // This pattern is shared between the :g, :s, ? and / commands. 88 // This is in search_regcomp(). 89 // 90 // The actual string matching is done using a heavily modified version of 91 // Henry Spencer's regular expression library. See regexp.c. 92 // 93 // 94 // 95 // Two search patterns are remembered: One for the :substitute command and 96 // one for other searches. last_idx points to the one that was used the last 97 // time. 98 99 static SearchPattern spats[2] = { 100 // Last used search pattern 101 [0] = { NULL, 0, true, false, 0, { '/', false, false, 0 }, NULL }, 102 // Last used substitute pattern 103 [1] = { NULL, 0, true, false, 0, { '/', false, false, 0 }, NULL } 104 }; 105 106 static int last_idx = 0; // index in spats[] for RE_LAST 107 108 static uint8_t lastc[2] = { NUL, NUL }; // last character searched for 109 static Direction lastcdir = FORWARD; // last direction of character search 110 static bool last_t_cmd = true; // last search t_cmd 111 static char lastc_bytes[MAX_SCHAR_SIZE + 1]; 112 static int lastc_bytelen = 1; // >1 for multi-byte char 113 114 // copy of spats[], for keeping the search patterns while executing autocmds 115 static SearchPattern saved_spats[ARRAY_SIZE(spats)]; 116 static char *saved_mr_pattern = NULL; 117 static size_t saved_mr_patternlen = 0; 118 static int saved_spats_last_idx = 0; 119 static bool saved_spats_no_hlsearch = false; 120 121 // allocated copy of pattern used by search_regcomp() 122 static char *mr_pattern = NULL; 123 static size_t mr_patternlen = 0; 124 125 // Type used by find_pattern_in_path() to remember which included files have 126 // been searched already. 127 typedef struct { 128 FILE *fp; // File pointer 129 char *name; // Full name of file 130 linenr_T lnum; // Line we were up to in file 131 int matched; // Found a match in this file 132 } SearchedFile; 133 134 /// translate search pattern for vim_regcomp() 135 /// 136 /// pat_save == RE_SEARCH: save pat in spats[RE_SEARCH].pat (normal search cmd) 137 /// pat_save == RE_SUBST: save pat in spats[RE_SUBST].pat (:substitute command) 138 /// pat_save == RE_BOTH: save pat in both patterns (:global command) 139 /// pat_use == RE_SEARCH: use previous search pattern if "pat" is NULL 140 /// pat_use == RE_SUBST: use previous substitute pattern if "pat" is NULL 141 /// pat_use == RE_LAST: use last used pattern if "pat" is NULL 142 /// options & SEARCH_HIS: put search string in history 143 /// options & SEARCH_KEEP: keep previous search pattern 144 /// 145 /// @param regmatch return: pattern and ignore-case flag 146 /// 147 /// @return FAIL if failed, OK otherwise. 148 int search_regcomp(char *pat, size_t patlen, char **used_pat, int pat_save, int pat_use, 149 int options, regmmatch_T *regmatch) 150 { 151 rc_did_emsg = false; 152 int magic = magic_isset(); 153 154 // If no pattern given, use a previously defined pattern. 155 if (pat == NULL || *pat == NUL) { 156 int i; 157 if (pat_use == RE_LAST) { 158 i = last_idx; 159 } else { 160 i = pat_use; 161 } 162 if (spats[i].pat == NULL) { // pattern was never defined 163 if (pat_use == RE_SUBST) { 164 emsg(_(e_nopresub)); 165 } else { 166 emsg(_(e_noprevre)); 167 } 168 rc_did_emsg = true; 169 return FAIL; 170 } 171 pat = spats[i].pat; 172 patlen = spats[i].patlen; 173 magic = spats[i].magic; 174 no_smartcase = spats[i].no_scs; 175 } else if (options & SEARCH_HIS) { // put new pattern in history 176 add_to_history(HIST_SEARCH, pat, patlen, true, NUL); 177 } 178 179 if (used_pat) { 180 *used_pat = pat; 181 } 182 183 xfree(mr_pattern); 184 if (curwin->w_p_rl && *curwin->w_p_rlc == 's') { 185 mr_pattern = reverse_text(pat); 186 } else { 187 mr_pattern = xstrnsave(pat, patlen); 188 } 189 mr_patternlen = patlen; 190 191 // Save the currently used pattern in the appropriate place, 192 // unless the pattern should not be remembered. 193 if (!(options & SEARCH_KEEP) && (cmdmod.cmod_flags & CMOD_KEEPPATTERNS) == 0) { 194 // search or global command 195 if (pat_save == RE_SEARCH || pat_save == RE_BOTH) { 196 save_re_pat(RE_SEARCH, pat, patlen, magic); 197 } 198 // substitute or global command 199 if (pat_save == RE_SUBST || pat_save == RE_BOTH) { 200 save_re_pat(RE_SUBST, pat, patlen, magic); 201 } 202 } 203 204 regmatch->rmm_ic = ignorecase(pat); 205 regmatch->rmm_maxcol = 0; 206 regmatch->regprog = vim_regcomp(pat, magic ? RE_MAGIC : 0); 207 if (regmatch->regprog == NULL) { 208 return FAIL; 209 } 210 return OK; 211 } 212 213 /// Get search pattern used by search_regcomp(). 214 char *get_search_pat(void) 215 { 216 return mr_pattern; 217 } 218 219 void save_re_pat(int idx, char *pat, size_t patlen, int magic) 220 { 221 if (spats[idx].pat == pat) { 222 return; 223 } 224 225 free_spat(&spats[idx]); 226 spats[idx].pat = xstrnsave(pat, patlen); 227 spats[idx].patlen = patlen; 228 spats[idx].magic = magic; 229 spats[idx].no_scs = no_smartcase; 230 spats[idx].timestamp = os_time(); 231 spats[idx].additional_data = NULL; 232 last_idx = idx; 233 // If 'hlsearch' set and search pat changed: need redraw. 234 if (p_hls) { 235 redraw_all_later(UPD_SOME_VALID); 236 } 237 set_no_hlsearch(false); 238 } 239 240 // Save the search patterns, so they can be restored later. 241 // Used before/after executing autocommands and user functions. 242 static int save_level = 0; 243 244 void save_search_patterns(void) 245 { 246 if (save_level++ != 0) { 247 return; 248 } 249 250 for (size_t i = 0; i < ARRAY_SIZE(spats); i++) { 251 saved_spats[i] = spats[i]; 252 if (spats[i].pat != NULL) { 253 saved_spats[i].pat = xstrnsave(spats[i].pat, spats[i].patlen); 254 saved_spats[i].patlen = spats[i].patlen; 255 } 256 } 257 if (mr_pattern == NULL) { 258 saved_mr_pattern = NULL; 259 saved_mr_patternlen = 0; 260 } else { 261 saved_mr_pattern = xstrnsave(mr_pattern, mr_patternlen); 262 saved_mr_patternlen = mr_patternlen; 263 } 264 saved_spats_last_idx = last_idx; 265 saved_spats_no_hlsearch = no_hlsearch; 266 } 267 268 void restore_search_patterns(void) 269 { 270 if (--save_level != 0) { 271 return; 272 } 273 274 for (size_t i = 0; i < ARRAY_SIZE(spats); i++) { 275 free_spat(&spats[i]); 276 spats[i] = saved_spats[i]; 277 } 278 set_vv_searchforward(); 279 xfree(mr_pattern); 280 mr_pattern = saved_mr_pattern; 281 mr_patternlen = saved_mr_patternlen; 282 last_idx = saved_spats_last_idx; 283 set_no_hlsearch(saved_spats_no_hlsearch); 284 } 285 286 static inline void free_spat(SearchPattern *const spat) 287 { 288 xfree(spat->pat); 289 xfree(spat->additional_data); 290 } 291 292 #if defined(EXITFREE) 293 void free_search_patterns(void) 294 { 295 for (size_t i = 0; i < ARRAY_SIZE(spats); i++) { 296 free_spat(&spats[i]); 297 } 298 CLEAR_FIELD(spats); 299 300 XFREE_CLEAR(mr_pattern); 301 mr_patternlen = 0; 302 } 303 304 #endif 305 306 // copy of spats[RE_SEARCH], for keeping the search patterns while incremental 307 // searching 308 static SearchPattern saved_last_search_spat; 309 static int did_save_last_search_spat = 0; 310 static int saved_last_idx = 0; 311 static bool saved_no_hlsearch = false; 312 static colnr_T saved_search_match_endcol; 313 static linenr_T saved_search_match_lines; 314 315 /// Save and restore the search pattern for incremental highlight search 316 /// feature. 317 /// 318 /// It's similar to but different from save_search_patterns() and 319 /// restore_search_patterns(), because the search pattern must be restored when 320 /// cancelling incremental searching even if it's called inside user functions. 321 void save_last_search_pattern(void) 322 { 323 if (++did_save_last_search_spat != 1) { 324 // nested call, nothing to do 325 return; 326 } 327 328 saved_last_search_spat = spats[RE_SEARCH]; 329 if (spats[RE_SEARCH].pat != NULL) { 330 saved_last_search_spat.pat = xstrnsave(spats[RE_SEARCH].pat, spats[RE_SEARCH].patlen); 331 saved_last_search_spat.patlen = spats[RE_SEARCH].patlen; 332 } 333 saved_last_idx = last_idx; 334 saved_no_hlsearch = no_hlsearch; 335 } 336 337 void restore_last_search_pattern(void) 338 { 339 if (--did_save_last_search_spat > 0) { 340 // nested call, nothing to do 341 return; 342 } 343 if (did_save_last_search_spat != 0) { 344 iemsg("restore_last_search_pattern() called more often than" 345 " save_last_search_pattern()"); 346 return; 347 } 348 349 xfree(spats[RE_SEARCH].pat); 350 spats[RE_SEARCH] = saved_last_search_spat; 351 saved_last_search_spat.pat = NULL; 352 saved_last_search_spat.patlen = 0; 353 set_vv_searchforward(); 354 last_idx = saved_last_idx; 355 set_no_hlsearch(saved_no_hlsearch); 356 } 357 358 /// Save and restore the incsearch highlighting variables. 359 /// This is required so that calling searchcount() at does not invalidate the 360 /// incsearch highlighting. 361 static void save_incsearch_state(void) 362 { 363 saved_search_match_endcol = search_match_endcol; 364 saved_search_match_lines = search_match_lines; 365 } 366 367 static void restore_incsearch_state(void) 368 { 369 search_match_endcol = saved_search_match_endcol; 370 search_match_lines = saved_search_match_lines; 371 } 372 373 char *last_search_pattern(void) 374 { 375 return spats[RE_SEARCH].pat; 376 } 377 378 size_t last_search_pattern_len(void) 379 { 380 return spats[RE_SEARCH].patlen; 381 } 382 383 /// Return true when case should be ignored for search pattern "pat". 384 /// Uses the 'ignorecase' and 'smartcase' options. 385 int ignorecase(char *pat) 386 { 387 return ignorecase_opt(pat, p_ic, p_scs); 388 } 389 390 /// As ignorecase() but pass the "ic" and "scs" flags. 391 int ignorecase_opt(char *pat, int ic_in, int scs) 392 { 393 int ic = ic_in; 394 if (ic && !no_smartcase && scs 395 && !(ctrl_x_mode_not_default() 396 && curbuf->b_p_inf)) { 397 ic = !pat_has_uppercase(pat); 398 } 399 no_smartcase = false; 400 401 return ic; 402 } 403 404 /// Returns true if pattern `pat` has an uppercase character. 405 bool pat_has_uppercase(char *pat) 406 FUNC_ATTR_NONNULL_ALL 407 { 408 char *p = pat; 409 magic_T magic_val = MAGIC_ON; 410 411 // get the magicness of the pattern 412 skip_regexp_ex(pat, NUL, magic_isset(), NULL, NULL, &magic_val); 413 414 while (*p != NUL) { 415 const int l = utfc_ptr2len(p); 416 417 if (l > 1) { 418 if (mb_isupper(utf_ptr2char(p))) { 419 return true; 420 } 421 p += l; 422 } else if (*p == '\\' && magic_val <= MAGIC_ON) { 423 if (p[1] == '_' && p[2] != NUL) { // skip "\_X" 424 p += 3; 425 } else if (p[1] == '%' && p[2] != NUL) { // skip "\%X" 426 p += 3; 427 } else if (p[1] != NUL) { // skip "\X" 428 p += 2; 429 } else { 430 p += 1; 431 } 432 } else if ((*p == '%' || *p == '_') && magic_val == MAGIC_ALL) { 433 if (p[1] != NUL) { // skip "_X" and %X 434 p += 2; 435 } else { 436 p++; 437 } 438 } else if (mb_isupper((uint8_t)(*p))) { 439 return true; 440 } else { 441 p++; 442 } 443 } 444 return false; 445 } 446 447 const char *last_csearch(void) 448 FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT 449 { 450 return lastc_bytes; 451 } 452 453 int last_csearch_forward(void) 454 { 455 return lastcdir == FORWARD; 456 } 457 458 int last_csearch_until(void) 459 { 460 return last_t_cmd; 461 } 462 463 void set_last_csearch(int c, char *s, int len) 464 { 465 *lastc = (uint8_t)c; 466 lastc_bytelen = len; 467 if (len) { 468 memcpy(lastc_bytes, s, (size_t)len); 469 } else { 470 CLEAR_FIELD(lastc_bytes); 471 } 472 } 473 474 void set_csearch_direction(Direction cdir) 475 { 476 lastcdir = cdir; 477 } 478 479 void set_csearch_until(int t_cmd) 480 { 481 last_t_cmd = t_cmd; 482 } 483 484 char *last_search_pat(void) 485 { 486 return spats[last_idx].pat; 487 } 488 489 // Reset search direction to forward. For "gd" and "gD" commands. 490 void reset_search_dir(void) 491 { 492 spats[0].off.dir = '/'; 493 set_vv_searchforward(); 494 } 495 496 // Set the last search pattern. For ":let @/ =" and ShaDa file. 497 // Also set the saved search pattern, so that this works in an autocommand. 498 void set_last_search_pat(const char *s, int idx, int magic, bool setlast) 499 { 500 free_spat(&spats[idx]); 501 // An empty string means that nothing should be matched. 502 if (*s == NUL) { 503 spats[idx].pat = NULL; 504 spats[idx].patlen = 0; 505 } else { 506 spats[idx].patlen = strlen(s); 507 spats[idx].pat = xstrnsave(s, spats[idx].patlen); 508 } 509 spats[idx].timestamp = os_time(); 510 spats[idx].additional_data = NULL; 511 spats[idx].magic = magic; 512 spats[idx].no_scs = false; 513 spats[idx].off.dir = '/'; 514 set_vv_searchforward(); 515 spats[idx].off.line = false; 516 spats[idx].off.end = false; 517 spats[idx].off.off = 0; 518 if (setlast) { 519 last_idx = idx; 520 } 521 if (save_level) { 522 free_spat(&saved_spats[idx]); 523 saved_spats[idx] = spats[0]; 524 if (spats[idx].pat == NULL) { 525 saved_spats[idx].pat = NULL; 526 saved_spats[idx].patlen = 0; 527 } else { 528 saved_spats[idx].pat = xstrnsave(spats[idx].pat, spats[idx].patlen); 529 saved_spats[idx].patlen = spats[idx].patlen; 530 } 531 saved_spats_last_idx = last_idx; 532 } 533 // If 'hlsearch' set and search pat changed: need redraw. 534 if (p_hls && idx == last_idx && !no_hlsearch) { 535 redraw_all_later(UPD_SOME_VALID); 536 } 537 } 538 539 // Get a regexp program for the last used search pattern. 540 // This is used for highlighting all matches in a window. 541 // Values returned in regmatch->regprog and regmatch->rmm_ic. 542 void last_pat_prog(regmmatch_T *regmatch) 543 { 544 if (spats[last_idx].pat == NULL) { 545 regmatch->regprog = NULL; 546 return; 547 } 548 emsg_off++; // So it doesn't beep if bad expr 549 search_regcomp("", 0, NULL, 0, last_idx, SEARCH_KEEP, regmatch); 550 emsg_off--; 551 } 552 553 /// Lowest level search function. 554 /// Search for 'count'th occurrence of pattern "pat" in direction "dir". 555 /// Start at position "pos" and return the found position in "pos". 556 /// 557 /// if (options & SEARCH_MSG) == 0 don't give any messages 558 /// if (options & SEARCH_MSG) == SEARCH_NFMSG don't give 'notfound' messages 559 /// if (options & SEARCH_MSG) == SEARCH_MSG give all messages 560 /// if (options & SEARCH_HIS) put search pattern in history 561 /// if (options & SEARCH_END) return position at end of match 562 /// if (options & SEARCH_START) accept match at pos itself 563 /// if (options & SEARCH_KEEP) keep previous search pattern 564 /// if (options & SEARCH_FOLD) match only once in a closed fold 565 /// if (options & SEARCH_PEEK) check for typed char, cancel search 566 /// if (options & SEARCH_COL) start at pos->col instead of zero 567 /// 568 /// @param win window to search in; can be NULL for a buffer without a window! 569 /// @param end_pos set to end of the match, unless NULL 570 /// @param pat_use which pattern to use when "pat" is empty 571 /// @param extra_arg optional extra arguments, can be NULL 572 /// 573 /// @returns FAIL (zero) for failure, non-zero for success. 574 /// the index of the first matching 575 /// subpattern plus one; one if there was none. 576 int searchit(win_T *win, buf_T *buf, pos_T *pos, pos_T *end_pos, Direction dir, char *pat, 577 size_t patlen, int count, int options, int pat_use, searchit_arg_T *extra_arg) 578 { 579 int found; 580 linenr_T lnum; // no init to shut up Apollo cc 581 regmmatch_T regmatch; 582 char *ptr; 583 colnr_T matchcol; 584 lpos_T endpos; 585 lpos_T matchpos; 586 int loop; 587 int extra_col; 588 int start_char_len; 589 bool match_ok; 590 int nmatched; 591 int submatch = 0; 592 bool first_match = true; 593 const int called_emsg_before = called_emsg; 594 bool break_loop = false; 595 linenr_T stop_lnum = 0; // stop after this line number when != 0 596 proftime_T *tm = NULL; // timeout limit or NULL 597 int *timed_out = NULL; // set when timed out or NULL 598 599 if (extra_arg != NULL) { 600 stop_lnum = extra_arg->sa_stop_lnum; 601 tm = extra_arg->sa_tm; 602 timed_out = &extra_arg->sa_timed_out; 603 } 604 605 if (search_regcomp(pat, patlen, NULL, RE_SEARCH, pat_use, 606 (options & (SEARCH_HIS + SEARCH_KEEP)), ®match) == FAIL) { 607 if ((options & SEARCH_MSG) && !rc_did_emsg) { 608 semsg(_("E383: Invalid search string: %s"), mr_pattern); 609 } 610 return FAIL; 611 } 612 613 const bool search_from_match_end = vim_strchr(p_cpo, CPO_SEARCH) != NULL; 614 615 // find the string 616 do { // loop for count 617 // When not accepting a match at the start position set "extra_col" to a 618 // non-zero value. Don't do that when starting at MAXCOL, since MAXCOL + 1 619 // is zero. 620 if (pos->col == MAXCOL) { 621 start_char_len = 0; 622 } else if (pos->lnum >= 1 623 && pos->lnum <= buf->b_ml.ml_line_count 624 && pos->col < MAXCOL - 2) { 625 // Watch out for the "col" being MAXCOL - 2, used in a closed fold. 626 ptr = ml_get_buf(buf, pos->lnum); 627 if (ml_get_buf_len(buf, pos->lnum) <= pos->col) { 628 start_char_len = 1; 629 } else { 630 start_char_len = utfc_ptr2len(ptr + pos->col); 631 } 632 } else { 633 start_char_len = 1; 634 } 635 if (dir == FORWARD) { 636 extra_col = (options & SEARCH_START) ? 0 : start_char_len; 637 } else { 638 extra_col = (options & SEARCH_START) ? start_char_len : 0; 639 } 640 641 pos_T start_pos = *pos; // remember start pos for detecting no match 642 found = 0; // default: not found 643 int at_first_line = true; // default: start in first line 644 if (pos->lnum == 0) { // correct lnum for when starting in line 0 645 pos->lnum = 1; 646 pos->col = 0; 647 at_first_line = false; // not in first line now 648 } 649 650 // Start searching in current line, unless searching backwards and 651 // we're in column 0. 652 // If we are searching backwards, in column 0, and not including the 653 // current position, gain some efficiency by skipping back a line. 654 // Otherwise begin the search in the current line. 655 if (dir == BACKWARD && start_pos.col == 0 656 && (options & SEARCH_START) == 0) { 657 lnum = pos->lnum - 1; 658 at_first_line = false; 659 } else { 660 lnum = pos->lnum; 661 } 662 663 for (loop = 0; loop <= 1; loop++) { // loop twice if 'wrapscan' set 664 for (; lnum > 0 && lnum <= buf->b_ml.ml_line_count; 665 lnum += dir, at_first_line = false) { 666 // Stop after checking "stop_lnum", if it's set. 667 if (stop_lnum != 0 && (dir == FORWARD 668 ? lnum > stop_lnum : lnum < stop_lnum)) { 669 break; 670 } 671 // Stop after passing the "tm" time limit. 672 if (tm != NULL && profile_passed_limit(*tm)) { 673 break; 674 } 675 676 // Look for a match somewhere in line "lnum". 677 colnr_T col = at_first_line && (options & SEARCH_COL) ? pos->col : 0; 678 nmatched = vim_regexec_multi(®match, win, buf, 679 lnum, col, tm, timed_out); 680 // vim_regexec_multi() may clear "regprog" 681 if (regmatch.regprog == NULL) { 682 break; 683 } 684 // Abort searching on an error (e.g., out of stack). 685 if (called_emsg > called_emsg_before || (timed_out != NULL && *timed_out)) { 686 break; 687 } 688 if (nmatched > 0) { 689 // match may actually be in another line when using \zs 690 matchpos = regmatch.startpos[0]; 691 endpos = regmatch.endpos[0]; 692 submatch = first_submatch(®match); 693 // "lnum" may be past end of buffer for "\n\zs". 694 if (lnum + matchpos.lnum > buf->b_ml.ml_line_count) { 695 ptr = ""; 696 } else { 697 ptr = ml_get_buf(buf, lnum + matchpos.lnum); 698 } 699 700 // Forward search in the first line: match should be after 701 // the start position. If not, continue at the end of the 702 // match (this is vi compatible) or on the next char. 703 if (dir == FORWARD && at_first_line) { 704 match_ok = true; 705 706 // When the match starts in a next line it's certainly 707 // past the start position. 708 // When match lands on a NUL the cursor will be put 709 // one back afterwards, compare with that position, 710 // otherwise "/$" will get stuck on end of line. 711 while (matchpos.lnum == 0 712 && (((options & SEARCH_END) && first_match) 713 ? (nmatched == 1 714 && (int)endpos.col - 1 715 < (int)start_pos.col + extra_col) 716 : ((int)matchpos.col 717 - (ptr[matchpos.col] == NUL) 718 < (int)start_pos.col + extra_col))) { 719 // If vi-compatible searching, continue at the end 720 // of the match, otherwise continue one position 721 // forward. 722 if (search_from_match_end) { 723 if (nmatched > 1) { 724 // end is in next line, thus no match in 725 // this line 726 match_ok = false; 727 break; 728 } 729 matchcol = endpos.col; 730 // for empty match: advance one char 731 if (matchcol == matchpos.col && ptr[matchcol] != NUL) { 732 matchcol += utfc_ptr2len(ptr + matchcol); 733 } 734 } else { 735 // Advance "matchcol" to the next character. 736 // This uses rmm_matchcol, the actual start of 737 // the match, ignoring "\zs". 738 matchcol = regmatch.rmm_matchcol; 739 if (ptr[matchcol] != NUL) { 740 matchcol += utfc_ptr2len(ptr + matchcol); 741 } 742 } 743 if (matchcol == 0 && (options & SEARCH_START)) { 744 break; 745 } 746 if (ptr[matchcol] == NUL 747 || (nmatched = vim_regexec_multi(®match, win, buf, 748 lnum, matchcol, tm, 749 timed_out)) == 0) { 750 match_ok = false; 751 break; 752 } 753 // vim_regexec_multi() may clear "regprog" 754 if (regmatch.regprog == NULL) { 755 break; 756 } 757 matchpos = regmatch.startpos[0]; 758 endpos = regmatch.endpos[0]; 759 submatch = first_submatch(®match); 760 761 // This while-loop only works with matchpos.lnum == 0. 762 // For bigger values the next line pointer ptr might not be a 763 // buffer line. 764 if (matchpos.lnum != 0) { 765 break; 766 } 767 // Need to get the line pointer again, a multi-line search may 768 // have made it invalid. 769 ptr = ml_get_buf(buf, lnum); 770 } 771 if (!match_ok) { 772 continue; 773 } 774 } 775 if (dir == BACKWARD) { 776 // Now, if there are multiple matches on this line, 777 // we have to get the last one. Or the last one before 778 // the cursor, if we're on that line. 779 // When putting the new cursor at the end, compare 780 // relative to the end of the match. 781 match_ok = false; 782 while (true) { 783 // Remember a position that is before the start 784 // position, we use it if it's the last match in 785 // the line. Always accept a position after 786 // wrapping around. 787 if (loop 788 || ((options & SEARCH_END) 789 ? (lnum + regmatch.endpos[0].lnum 790 < start_pos.lnum 791 || (lnum + regmatch.endpos[0].lnum 792 == start_pos.lnum 793 && (int)regmatch.endpos[0].col - 1 794 < (int)start_pos.col + extra_col)) 795 : (lnum + regmatch.startpos[0].lnum 796 < start_pos.lnum 797 || (lnum + regmatch.startpos[0].lnum 798 == start_pos.lnum 799 && (int)regmatch.startpos[0].col 800 < (int)start_pos.col + extra_col)))) { 801 match_ok = true; 802 matchpos = regmatch.startpos[0]; 803 endpos = regmatch.endpos[0]; 804 submatch = first_submatch(®match); 805 } else { 806 break; 807 } 808 809 // We found a valid match, now check if there is 810 // another one after it. 811 // If vi-compatible searching, continue at the end 812 // of the match, otherwise continue one position 813 // forward. 814 if (search_from_match_end) { 815 if (nmatched > 1) { 816 break; 817 } 818 matchcol = endpos.col; 819 // for empty match: advance one char 820 if (matchcol == matchpos.col 821 && ptr[matchcol] != NUL) { 822 matchcol += utfc_ptr2len(ptr + matchcol); 823 } 824 } else { 825 // Stop when the match is in a next line. 826 if (matchpos.lnum > 0) { 827 break; 828 } 829 matchcol = matchpos.col; 830 if (ptr[matchcol] != NUL) { 831 matchcol += utfc_ptr2len(ptr + matchcol); 832 } 833 } 834 if (ptr[matchcol] == NUL 835 || (nmatched = vim_regexec_multi(®match, win, buf, lnum + matchpos.lnum, 836 matchcol, tm, timed_out)) == 0) { 837 // If the search timed out, we did find a match 838 // but it might be the wrong one, so that's not 839 // OK. 840 if (tm != NULL && profile_passed_limit(*tm)) { 841 match_ok = false; 842 } 843 break; 844 } 845 // vim_regexec_multi() may clear "regprog" 846 if (regmatch.regprog == NULL) { 847 break; 848 } 849 // Need to get the line pointer again, a 850 // multi-line search may have made it invalid. 851 ptr = ml_get_buf(buf, lnum + matchpos.lnum); 852 } 853 854 // If there is only a match after the cursor, skip 855 // this match. 856 if (!match_ok) { 857 continue; 858 } 859 } 860 861 // With the SEARCH_END option move to the last character 862 // of the match. Don't do it for an empty match, end 863 // should be same as start then. 864 if ((options & SEARCH_END) && !(options & SEARCH_NOOF) 865 && !(matchpos.lnum == endpos.lnum 866 && matchpos.col == endpos.col)) { 867 // For a match in the first column, set the position 868 // on the NUL in the previous line. 869 pos->lnum = lnum + endpos.lnum; 870 pos->col = endpos.col; 871 if (endpos.col == 0) { 872 if (pos->lnum > 1) { // just in case 873 pos->lnum--; 874 pos->col = ml_get_buf_len(buf, pos->lnum); 875 } 876 } else { 877 pos->col--; 878 if (pos->lnum <= buf->b_ml.ml_line_count) { 879 ptr = ml_get_buf(buf, pos->lnum); 880 pos->col -= utf_head_off(ptr, ptr + pos->col); 881 } 882 } 883 if (end_pos != NULL) { 884 end_pos->lnum = lnum + matchpos.lnum; 885 end_pos->col = matchpos.col; 886 } 887 } else { 888 pos->lnum = lnum + matchpos.lnum; 889 pos->col = matchpos.col; 890 if (end_pos != NULL) { 891 end_pos->lnum = lnum + endpos.lnum; 892 end_pos->col = endpos.col; 893 } 894 } 895 pos->coladd = 0; 896 if (end_pos != NULL) { 897 end_pos->coladd = 0; 898 } 899 found = 1; 900 first_match = false; 901 902 // Set variables used for 'incsearch' highlighting. 903 search_match_lines = endpos.lnum - matchpos.lnum; 904 search_match_endcol = endpos.col; 905 break; 906 } 907 line_breakcheck(); // stop if ctrl-C typed 908 if (got_int) { 909 break; 910 } 911 912 // Cancel searching if a character was typed. Used for 913 // 'incsearch'. Don't check too often, that would slowdown 914 // searching too much. 915 if ((options & SEARCH_PEEK) 916 && ((lnum - pos->lnum) & 0x3f) == 0 917 && char_avail()) { 918 break_loop = true; 919 break; 920 } 921 922 if (loop && lnum == start_pos.lnum) { 923 break; // if second loop, stop where started 924 } 925 } 926 at_first_line = false; 927 928 // vim_regexec_multi() may clear "regprog" 929 if (regmatch.regprog == NULL) { 930 break; 931 } 932 933 // Stop the search if wrapscan isn't set, "stop_lnum" is 934 // specified, after an interrupt, after a match and after looping 935 // twice. 936 if (!p_ws || stop_lnum != 0 || got_int 937 || called_emsg > called_emsg_before 938 || (timed_out != NULL && *timed_out) 939 || break_loop 940 || found || loop) { 941 break; 942 } 943 944 // If 'wrapscan' is set we continue at the other end of the file. 945 // If 'shortmess' does not contain 's', we give a message, but 946 // only, if we won't show the search stat later anyhow, 947 // (so SEARCH_COUNT must be absent). 948 // This message is also remembered in keep_msg for when the screen 949 // is redrawn. The keep_msg is cleared whenever another message is 950 // written. 951 lnum = dir == BACKWARD // start second loop at the other end 952 ? buf->b_ml.ml_line_count 953 : 1; 954 if (!shortmess(SHM_SEARCH) 955 && shortmess(SHM_SEARCHCOUNT) 956 && (options & SEARCH_MSG)) { 957 give_warning(_(dir == BACKWARD ? top_bot_msg : bot_top_msg), true, false); 958 } 959 if (extra_arg != NULL) { 960 extra_arg->sa_wrapped = true; 961 } 962 } 963 if (got_int || called_emsg > called_emsg_before 964 || (timed_out != NULL && *timed_out) 965 || break_loop) { 966 break; 967 } 968 } while (--count > 0 && found); // stop after count matches or no match 969 970 vim_regfree(regmatch.regprog); 971 972 if (!found) { // did not find it 973 if (got_int) { 974 emsg(_(e_interr)); 975 } else if ((options & SEARCH_MSG) == SEARCH_MSG) { 976 if (p_ws) { 977 semsg(_(e_patnotf2), mr_pattern); 978 } else if (lnum == 0) { 979 semsg(_(e_search_hit_top_without_match_for_str), mr_pattern); 980 } else { 981 semsg(_(e_search_hit_bottom_without_match_for_str), mr_pattern); 982 } 983 } 984 return FAIL; 985 } 986 987 // A pattern like "\n\zs" may go past the last line. 988 if (pos->lnum > buf->b_ml.ml_line_count) { 989 pos->lnum = buf->b_ml.ml_line_count; 990 pos->col = ml_get_buf_len(buf, pos->lnum); 991 if (pos->col > 0) { 992 pos->col--; 993 } 994 } 995 996 return submatch + 1; 997 } 998 999 void set_search_direction(int cdir) 1000 { 1001 spats[0].off.dir = (char)cdir; 1002 } 1003 1004 static void set_vv_searchforward(void) 1005 { 1006 set_vim_var_nr(VV_SEARCHFORWARD, spats[0].off.dir == '/'); 1007 } 1008 1009 // Return the number of the first subpat that matched. 1010 // Return zero if none of them matched. 1011 static int first_submatch(regmmatch_T *rp) 1012 { 1013 int submatch; 1014 1015 for (submatch = 1;; submatch++) { 1016 if (rp->startpos[submatch].lnum >= 0) { 1017 break; 1018 } 1019 if (submatch == 9) { 1020 submatch = 0; 1021 break; 1022 } 1023 } 1024 return submatch; 1025 } 1026 1027 /// Highest level string search function. 1028 /// Search for the 'count'th occurrence of pattern 'pat' in direction 'dirc' 1029 /// 1030 /// Careful: If spats[0].off.line == true and spats[0].off.off == 0 this 1031 /// makes the movement linewise without moving the match position. 1032 /// 1033 /// @param dirc if 0: use previous dir. 1034 /// @param pat NULL or empty : use previous string. 1035 /// @param options if true and 1036 /// SEARCH_REV == true : go in reverse of previous dir. 1037 /// SEARCH_ECHO == true : echo the search command and handle options 1038 /// SEARCH_MSG == true : may give error message 1039 /// SEARCH_OPT == true : interpret optional flags 1040 /// SEARCH_HIS == true : put search pattern in history 1041 /// SEARCH_NOOF == true : don't add offset to position 1042 /// SEARCH_MARK == true : set previous context mark 1043 /// SEARCH_KEEP == true : keep previous search pattern 1044 /// SEARCH_START == true : accept match at curpos itself 1045 /// SEARCH_PEEK == true : check for typed char, cancel search 1046 /// @param oap can be NULL 1047 /// @param dirc '/' or '?' 1048 /// @param search_delim delimiter for search, e.g. '%' in s%regex%replacement 1049 /// @param sia optional arguments or NULL 1050 /// 1051 /// @return 0 for failure, 1 for found, 2 for found and line offset added. 1052 int do_search(oparg_T *oap, int dirc, int search_delim, char *pat, size_t patlen, int count, 1053 int options, searchit_arg_T *sia) 1054 { 1055 char *searchstr; 1056 size_t searchstrlen; 1057 int retval; // Return value 1058 char *p; 1059 int64_t c; 1060 char *dircp; 1061 char *strcopy = NULL; 1062 char *ps; 1063 char *msgbuf = NULL; 1064 size_t msgbuflen = 0; 1065 bool has_offset = false; 1066 1067 searchcmdlen = 0; 1068 1069 // A line offset is not remembered, this is vi compatible. 1070 if (spats[0].off.line && vim_strchr(p_cpo, CPO_LINEOFF) != NULL) { 1071 spats[0].off.line = false; 1072 spats[0].off.off = 0; 1073 } 1074 1075 // Save the values for when (options & SEARCH_KEEP) is used. 1076 // (there is no "if ()" around this because gcc wants them initialized) 1077 SearchOffset old_off = spats[0].off; 1078 1079 pos_T pos = curwin->w_cursor; // Position of the last match. 1080 // Start searching at the cursor position. 1081 1082 // Find out the direction of the search. 1083 if (dirc == 0) { 1084 dirc = (uint8_t)spats[0].off.dir; 1085 } else { 1086 spats[0].off.dir = (char)dirc; 1087 set_vv_searchforward(); 1088 } 1089 if (options & SEARCH_REV) { 1090 dirc = dirc == '/' ? '?' : '/'; 1091 } 1092 1093 // If the cursor is in a closed fold, don't find another match in the same 1094 // fold. 1095 if (dirc == '/') { 1096 if (hasFolding(curwin, pos.lnum, NULL, &pos.lnum)) { 1097 pos.col = MAXCOL - 2; // avoid overflow when adding 1 1098 } 1099 } else { 1100 if (hasFolding(curwin, pos.lnum, &pos.lnum, NULL)) { 1101 pos.col = 0; 1102 } 1103 } 1104 1105 // Turn 'hlsearch' highlighting back on. 1106 if (no_hlsearch && !(options & SEARCH_KEEP)) { 1107 redraw_all_later(UPD_SOME_VALID); 1108 set_no_hlsearch(false); 1109 } 1110 1111 // Repeat the search when pattern followed by ';', e.g. "/foo/;?bar". 1112 while (true) { 1113 bool show_top_bot_msg = false; 1114 1115 searchstr = pat; 1116 searchstrlen = patlen; 1117 1118 dircp = NULL; 1119 // use previous pattern 1120 if (pat == NULL || *pat == NUL || *pat == search_delim) { 1121 if (spats[RE_SEARCH].pat == NULL) { // no previous pattern 1122 if (spats[RE_SUBST].pat == NULL) { 1123 emsg(_(e_noprevre)); 1124 retval = 0; 1125 goto end_do_search; 1126 } 1127 searchstr = spats[RE_SUBST].pat; 1128 searchstrlen = spats[RE_SUBST].patlen; 1129 } else { 1130 // make search_regcomp() use spats[RE_SEARCH].pat 1131 searchstr = ""; 1132 searchstrlen = 0; 1133 } 1134 } 1135 1136 if (pat != NULL && *pat != NUL) { // look for (new) offset 1137 // Find end of regular expression. 1138 // If there is a matching '/' or '?', toss it. 1139 ps = strcopy; 1140 p = skip_regexp_ex(pat, search_delim, magic_isset(), &strcopy, NULL, NULL); 1141 if (strcopy != ps) { 1142 size_t len = strlen(strcopy); 1143 // made a copy of "pat" to change "\?" to "?" 1144 searchcmdlen += (int)(patlen - len); 1145 pat = strcopy; 1146 patlen = len; 1147 searchstr = strcopy; 1148 searchstrlen = len; 1149 } 1150 if (*p == search_delim) { 1151 searchstrlen = (size_t)(p - pat); 1152 dircp = p; // remember where we put the NUL 1153 *p++ = NUL; 1154 } 1155 spats[0].off.line = false; 1156 spats[0].off.end = false; 1157 spats[0].off.off = 0; 1158 // Check for a line offset or a character offset. 1159 // For get_address (echo off) we don't check for a character 1160 // offset, because it is meaningless and the 's' could be a 1161 // substitute command. 1162 if (*p == '+' || *p == '-' || ascii_isdigit(*p)) { 1163 spats[0].off.line = true; 1164 } else if ((options & SEARCH_OPT) 1165 && (*p == 'e' || *p == 's' || *p == 'b')) { 1166 if (*p == 'e') { // end 1167 spats[0].off.end = true; 1168 } 1169 p++; 1170 } 1171 if (ascii_isdigit(*p) || *p == '+' || *p == '-') { // got an offset 1172 // 'nr' or '+nr' or '-nr' 1173 if (ascii_isdigit(*p) || ascii_isdigit(*(p + 1))) { 1174 spats[0].off.off = atol(p); 1175 } else if (*p == '-') { // single '-' 1176 spats[0].off.off = -1; 1177 } else { // single '+' 1178 spats[0].off.off = 1; 1179 } 1180 p++; 1181 while (ascii_isdigit(*p)) { // skip number 1182 p++; 1183 } 1184 } 1185 1186 // compute length of search command for get_address() 1187 searchcmdlen += (int)(p - pat); 1188 1189 patlen -= (size_t)(p - pat); 1190 pat = p; // put pat after search command 1191 } 1192 1193 bool show_search_stats = false; 1194 if ((options & SEARCH_ECHO) && messaging() && !msg_silent 1195 && (!cmd_silent || !shortmess(SHM_SEARCHCOUNT))) { 1196 char off_buf[40]; 1197 size_t off_len = 0; 1198 1199 // Compute msg_row early. 1200 msg_start(); 1201 msg_ext_set_kind("search_cmd"); 1202 1203 // Get the offset, so we know how long it is. 1204 if (!cmd_silent 1205 && (spats[0].off.line || spats[0].off.end || spats[0].off.off)) { 1206 off_buf[off_len++] = (char)dirc; 1207 if (spats[0].off.end) { 1208 off_buf[off_len++] = 'e'; 1209 } else if (!spats[0].off.line) { 1210 off_buf[off_len++] = 's'; 1211 } 1212 off_buf[off_len] = NUL; 1213 if (spats[0].off.off != 0 || spats[0].off.line) { 1214 off_len += (size_t)snprintf(off_buf + off_len, sizeof(off_buf) - off_len, 1215 "%+" PRId64, spats[0].off.off); 1216 } 1217 } 1218 1219 size_t plen; 1220 if (*searchstr == NUL) { 1221 p = spats[0].pat; 1222 plen = spats[0].patlen; 1223 } else { 1224 p = searchstr; 1225 plen = searchstrlen; 1226 } 1227 1228 size_t msgbufsize; 1229 if (!shortmess(SHM_SEARCHCOUNT) || cmd_silent) { 1230 // Reserve enough space for the search pattern + offset + 1231 // search stat. Use all the space available, so that the 1232 // search state is right aligned. If there is not enough space 1233 // msg_strtrunc() will shorten in the middle. 1234 if (ui_has(kUIMessages)) { 1235 msgbufsize = 0; // adjusted below 1236 } else if (msg_scrolled != 0 && !cmd_silent) { 1237 // Use all the columns. 1238 msgbufsize = (size_t)((Rows - msg_row) * Columns - 1); 1239 } else { 1240 // Use up to 'showcmd' column. 1241 msgbufsize = (size_t)((Rows - msg_row - 1) * Columns + sc_col - 1); 1242 } 1243 if (msgbufsize < plen + off_len + SEARCH_STAT_BUF_LEN + 3) { 1244 msgbufsize = plen + off_len + SEARCH_STAT_BUF_LEN + 3; 1245 } 1246 } else { 1247 // Reserve enough space for the search pattern + offset. 1248 msgbufsize = plen + off_len + 3; 1249 } 1250 1251 xfree(msgbuf); 1252 msgbuf = xmalloc(msgbufsize); 1253 memset(msgbuf, ' ', msgbufsize); 1254 msgbuflen = msgbufsize - 1; 1255 msgbuf[msgbuflen] = NUL; 1256 1257 // do not fill the msgbuf buffer, if cmd_silent is set, leave it 1258 // empty for the search_stat feature. 1259 if (!cmd_silent) { 1260 ui_busy_start(); 1261 msgbuf[0] = (char)dirc; 1262 if (utf_iscomposing_first(utf_ptr2char(p))) { 1263 // Use a space to draw the composing char on. 1264 msgbuf[1] = ' '; 1265 memmove(msgbuf + 2, p, plen); 1266 } else { 1267 memmove(msgbuf + 1, p, plen); 1268 } 1269 if (off_len > 0) { 1270 memmove(msgbuf + plen + 1, off_buf, off_len); 1271 } 1272 1273 char *trunc = msg_strtrunc(msgbuf, true); 1274 if (trunc != NULL) { 1275 xfree(msgbuf); 1276 msgbuf = trunc; 1277 msgbuflen = strlen(msgbuf); 1278 } 1279 1280 // The search pattern could be shown on the right in rightleft 1281 // mode, but the 'ruler' and 'showcmd' area use it too, thus 1282 // it would be blanked out again very soon. Show it on the 1283 // left, but do reverse the text. 1284 if (curwin->w_p_rl && *curwin->w_p_rlc == 's') { 1285 char *r = reverse_text(msgbuf); 1286 xfree(msgbuf); 1287 msgbuf = r; 1288 msgbuflen = strlen(msgbuf); 1289 // move reversed text to beginning of buffer 1290 while (*r == ' ') { 1291 r++; 1292 } 1293 size_t pat_len = (size_t)(msgbuf + msgbuflen - r); 1294 memmove(msgbuf, r, pat_len); 1295 // overwrite old text 1296 if ((size_t)(r - msgbuf) >= pat_len) { 1297 memset(r, ' ', pat_len); 1298 } else { 1299 memset(msgbuf + pat_len, ' ', (size_t)(r - msgbuf)); 1300 } 1301 } 1302 msg_outtrans(msgbuf, 0, false); 1303 msg_clr_eos(); 1304 msg_check(); 1305 1306 gotocmdline(false); 1307 ui_flush(); 1308 ui_busy_stop(); 1309 msg_nowait = true; // don't wait for this message 1310 } 1311 1312 if (!shortmess(SHM_SEARCHCOUNT)) { 1313 show_search_stats = true; 1314 } 1315 } 1316 1317 // If there is a character offset, subtract it from the current 1318 // position, so we don't get stuck at "?pat?e+2" or "/pat/s-2". 1319 // Skip this if pos.col is near MAXCOL (closed fold). 1320 // This is not done for a line offset, because then we would not be vi 1321 // compatible. 1322 if (!spats[0].off.line && spats[0].off.off && pos.col < MAXCOL - 2) { 1323 if (spats[0].off.off > 0) { 1324 for (c = spats[0].off.off; c; c--) { 1325 if (decl(&pos) == -1) { 1326 break; 1327 } 1328 } 1329 if (c) { // at start of buffer 1330 pos.lnum = 0; // allow lnum == 0 here 1331 pos.col = MAXCOL; 1332 } 1333 } else { 1334 for (c = spats[0].off.off; c; c++) { 1335 if (incl(&pos) == -1) { 1336 break; 1337 } 1338 } 1339 if (c) { // at end of buffer 1340 pos.lnum = curbuf->b_ml.ml_line_count + 1; 1341 pos.col = 0; 1342 } 1343 } 1344 } 1345 1346 c = searchit(curwin, curbuf, &pos, NULL, dirc == '/' ? FORWARD : BACKWARD, 1347 searchstr, searchstrlen, count, 1348 (spats[0].off.end * SEARCH_END 1349 + (options 1350 & (SEARCH_KEEP + SEARCH_PEEK + SEARCH_HIS + SEARCH_MSG 1351 + SEARCH_START 1352 + ((pat != NULL && *pat == ';') ? 0 : SEARCH_NOOF)))), 1353 RE_LAST, sia); 1354 1355 if (dircp != NULL) { 1356 *dircp = (char)search_delim; // restore second '/' or '?' for normal_cmd() 1357 } 1358 1359 if (!shortmess(SHM_SEARCH) && sia && sia->sa_wrapped) { 1360 show_top_bot_msg = true; 1361 } 1362 1363 if (c == FAIL) { 1364 retval = 0; 1365 goto end_do_search; 1366 } 1367 if (spats[0].off.end && oap != NULL) { 1368 oap->inclusive = true; // 'e' includes last character 1369 } 1370 retval = 1; // pattern found 1371 1372 if (sia && sia->sa_wrapped) { 1373 apply_autocmds(EVENT_SEARCHWRAPPED, NULL, NULL, false, NULL); 1374 } 1375 1376 // Add character and/or line offset 1377 if (!(options & SEARCH_NOOF) || (pat != NULL && *pat == ';')) { 1378 pos_T org_pos = pos; 1379 1380 if (spats[0].off.line) { // Add the offset to the line number. 1381 c = pos.lnum + spats[0].off.off; 1382 if (c < 1) { 1383 pos.lnum = 1; 1384 } else if (c > curbuf->b_ml.ml_line_count) { 1385 pos.lnum = curbuf->b_ml.ml_line_count; 1386 } else { 1387 pos.lnum = (linenr_T)c; 1388 } 1389 pos.col = 0; 1390 1391 retval = 2; // pattern found, line offset added 1392 } else if (pos.col < MAXCOL - 2) { // just in case 1393 // to the right, check for end of file 1394 c = spats[0].off.off; 1395 if (c > 0) { 1396 while (c-- > 0) { 1397 if (incl(&pos) == -1) { 1398 break; 1399 } 1400 } 1401 } else { // to the left, check for start of file 1402 while (c++ < 0) { 1403 if (decl(&pos) == -1) { 1404 break; 1405 } 1406 } 1407 } 1408 } 1409 if (!equalpos(pos, org_pos)) { 1410 has_offset = true; 1411 } 1412 } 1413 1414 // Show [1/15] if 'S' is not in 'shortmess'. 1415 if (show_search_stats) { 1416 cmdline_search_stat(dirc, &pos, &curwin->w_cursor, 1417 show_top_bot_msg, msgbuf, msgbuflen, 1418 (count != 1 || has_offset 1419 || (!(fdo_flags & kOptFdoFlagSearch) 1420 && hasFolding(curwin, curwin->w_cursor.lnum, NULL, 1421 NULL))), 1422 (int)p_msc, 1423 SEARCH_STAT_DEF_TIMEOUT); 1424 } 1425 1426 // The search command can be followed by a ';' to do another search. 1427 // For example: "/pat/;/foo/+3;?bar" 1428 // This is like doing another search command, except: 1429 // - The remembered direction '/' or '?' is from the first search. 1430 // - When an error happens the cursor isn't moved at all. 1431 // Don't do this when called by get_address() (it handles ';' itself). 1432 if (!(options & SEARCH_OPT) || pat == NULL || *pat != ';') { 1433 break; 1434 } 1435 1436 dirc = (uint8_t)(*++pat); 1437 search_delim = dirc; 1438 if (dirc != '?' && dirc != '/') { 1439 retval = 0; 1440 emsg(_("E386: Expected '?' or '/' after ';'")); 1441 goto end_do_search; 1442 } 1443 pat++; 1444 patlen--; 1445 } 1446 1447 if (options & SEARCH_MARK) { 1448 setpcmark(); 1449 } 1450 curwin->w_cursor = pos; 1451 curwin->w_set_curswant = true; 1452 1453 end_do_search: 1454 if ((options & SEARCH_KEEP) || (cmdmod.cmod_flags & CMOD_KEEPPATTERNS)) { 1455 spats[0].off = old_off; 1456 } 1457 xfree(strcopy); 1458 xfree(msgbuf); 1459 1460 return retval; 1461 } 1462 1463 // search_for_exact_line(buf, pos, dir, pat) 1464 // 1465 // Search for a line starting with the given pattern (ignoring leading 1466 // white-space), starting from pos and going in direction "dir". "pos" will 1467 // contain the position of the match found. Blank lines match only if 1468 // ADDING is set. If p_ic is set then the pattern must be in lowercase. 1469 // Return OK for success, or FAIL if no line found. 1470 int search_for_exact_line(buf_T *buf, pos_T *pos, Direction dir, char *pat) 1471 { 1472 linenr_T start = 0; 1473 1474 if (buf->b_ml.ml_line_count == 0) { 1475 return FAIL; 1476 } 1477 while (true) { 1478 pos->lnum += dir; 1479 if (pos->lnum < 1) { 1480 if (p_ws) { 1481 pos->lnum = buf->b_ml.ml_line_count; 1482 if (!shortmess(SHM_SEARCH)) { 1483 give_warning(_(top_bot_msg), true, false); 1484 } 1485 } else { 1486 pos->lnum = 1; 1487 break; 1488 } 1489 } else if (pos->lnum > buf->b_ml.ml_line_count) { 1490 if (p_ws) { 1491 pos->lnum = 1; 1492 if (!shortmess(SHM_SEARCH)) { 1493 give_warning(_(bot_top_msg), true, false); 1494 } 1495 } else { 1496 pos->lnum = 1; 1497 break; 1498 } 1499 } 1500 if (pos->lnum == start) { 1501 break; 1502 } 1503 if (start == 0) { 1504 start = pos->lnum; 1505 } 1506 char *ptr = ml_get_buf(buf, pos->lnum); 1507 char *p = skipwhite(ptr); 1508 pos->col = (colnr_T)(p - ptr); 1509 1510 // when adding lines the matching line may be empty but it is not 1511 // ignored because we are interested in the next line -- Acevedo 1512 if (compl_status_adding() && !compl_status_sol()) { 1513 if (mb_strcmp_ic((bool)p_ic, p, pat) == 0) { 1514 return OK; 1515 } 1516 } else if (*p != NUL) { // Ignore empty lines. 1517 // Expanding lines or words. 1518 assert(ins_compl_len() >= 0); 1519 if ((p_ic ? mb_strnicmp(p, pat, (size_t)ins_compl_len()) 1520 : strncmp(p, pat, (size_t)ins_compl_len())) == 0) { 1521 return OK; 1522 } 1523 } 1524 } 1525 return FAIL; 1526 } 1527 1528 // Character Searches 1529 1530 /// Search for a character in a line. If "t_cmd" is false, move to the 1531 /// position of the character, otherwise move to just before the char. 1532 /// Do this "cap->count1" times. 1533 /// Return FAIL or OK. 1534 int searchc(cmdarg_T *cap, bool t_cmd) 1535 FUNC_ATTR_NONNULL_ALL 1536 { 1537 int c = cap->nchar; // char to search for 1538 int dir = cap->arg; // true for searching forward 1539 int count = cap->count1; // repeat count 1540 bool stop = true; 1541 1542 if (c != NUL) { // normal search: remember args for repeat 1543 if (!KeyStuffed) { // don't remember when redoing 1544 *lastc = (uint8_t)c; 1545 set_csearch_direction(dir); 1546 set_csearch_until(t_cmd); 1547 if (cap->nchar_len) { 1548 lastc_bytelen = cap->nchar_len; 1549 memcpy(lastc_bytes, cap->nchar_composing, (size_t)cap->nchar_len); 1550 } else { 1551 lastc_bytelen = utf_char2bytes(c, lastc_bytes); 1552 } 1553 } 1554 } else { // repeat previous search 1555 if (*lastc == NUL && lastc_bytelen <= 1) { 1556 return FAIL; 1557 } 1558 dir = dir // repeat in opposite direction 1559 ? -lastcdir 1560 : lastcdir; 1561 t_cmd = last_t_cmd; 1562 c = *lastc; 1563 // For multi-byte re-use last lastc_bytes[] and lastc_bytelen. 1564 1565 // Force a move of at least one char, so ";" and "," will move the 1566 // cursor, even if the cursor is right in front of char we are looking 1567 // at. 1568 if (vim_strchr(p_cpo, CPO_SCOLON) == NULL && count == 1 && t_cmd) { 1569 stop = false; 1570 } 1571 } 1572 1573 cap->oap->inclusive = dir != BACKWARD; 1574 1575 char *p = get_cursor_line_ptr(); 1576 int col = curwin->w_cursor.col; 1577 int len = get_cursor_line_len(); 1578 1579 while (count--) { 1580 while (true) { 1581 if (dir > 0) { 1582 col += utfc_ptr2len(p + col); 1583 if (col >= len) { 1584 return FAIL; 1585 } 1586 } else { 1587 if (col == 0) { 1588 return FAIL; 1589 } 1590 col -= utf_head_off(p, p + col - 1) + 1; 1591 } 1592 if (lastc_bytelen <= 1) { 1593 if (p[col] == c && stop) { 1594 break; 1595 } 1596 } else if (strncmp(p + col, lastc_bytes, (size_t)lastc_bytelen) == 0 && stop) { 1597 break; 1598 } 1599 stop = true; 1600 } 1601 } 1602 1603 if (t_cmd) { 1604 // Backup to before the character (possibly double-byte). 1605 col -= dir; 1606 if (dir < 0) { 1607 // Landed on the search char which is lastc_bytelen long. 1608 col += lastc_bytelen - 1; 1609 } else { 1610 // To previous char, which may be multi-byte. 1611 col -= utf_head_off(p, p + col); 1612 } 1613 } 1614 curwin->w_cursor.col = col; 1615 1616 return OK; 1617 } 1618 1619 // "Other" Searches 1620 1621 // findmatch - find the matching paren or brace 1622 // 1623 // Improvement over vi: Braces inside quotes are ignored. 1624 pos_T *findmatch(oparg_T *oap, int initc) 1625 { 1626 return findmatchlimit(oap, initc, 0, 0); 1627 } 1628 1629 // Return true if the character before "linep[col]" equals "ch". 1630 // Return false if "col" is zero. 1631 // Update "*prevcol" to the column of the previous character, unless "prevcol" 1632 // is NULL. 1633 // Handles multibyte string correctly. 1634 static bool check_prevcol(char *linep, int col, int ch, int *prevcol) 1635 { 1636 col--; 1637 if (col > 0) { 1638 col -= utf_head_off(linep, linep + col); 1639 } 1640 if (prevcol) { 1641 *prevcol = col; 1642 } 1643 return col >= 0 && (uint8_t)linep[col] == ch; 1644 } 1645 1646 /// Raw string start is found at linep[startpos.col - 1]. 1647 /// 1648 /// @return true if the matching end can be found between startpos and endpos. 1649 static bool find_rawstring_end(char *linep, pos_T *startpos, pos_T *endpos) 1650 { 1651 char *p; 1652 linenr_T lnum; 1653 1654 for (p = linep + startpos->col + 1; *p && *p != '('; p++) {} 1655 1656 size_t delim_len = (size_t)((p - linep) - startpos->col - 1); 1657 char *delim_copy = xmemdupz(linep + startpos->col + 1, delim_len); 1658 bool found = false; 1659 for (lnum = startpos->lnum; lnum <= endpos->lnum; lnum++) { 1660 char *line = ml_get(lnum); 1661 1662 for (p = line + (lnum == startpos->lnum ? startpos->col + 1 : 0); *p; p++) { 1663 if (lnum == endpos->lnum && (colnr_T)(p - line) >= endpos->col) { 1664 break; 1665 } 1666 if (*p == ')' 1667 && strncmp(delim_copy, p + 1, delim_len) == 0 1668 && p[delim_len + 1] == '"') { 1669 found = true; 1670 break; 1671 } 1672 } 1673 if (found) { 1674 break; 1675 } 1676 } 1677 xfree(delim_copy); 1678 return found; 1679 } 1680 1681 /// Check matchpairs option for "*initc". 1682 /// If there is a match set "*initc" to the matching character and "*findc" to 1683 /// the opposite character. Set "*backwards" to the direction. 1684 /// When "switchit" is true swap the direction. 1685 static void find_mps_values(int *initc, int *findc, bool *backwards, bool switchit) 1686 FUNC_ATTR_NONNULL_ALL 1687 { 1688 char *ptr = curbuf->b_p_mps; 1689 1690 while (*ptr != NUL) { 1691 if (utf_ptr2char(ptr) == *initc) { 1692 if (switchit) { 1693 *findc = *initc; 1694 *initc = utf_ptr2char(ptr + utfc_ptr2len(ptr) + 1); 1695 *backwards = true; 1696 } else { 1697 *findc = utf_ptr2char(ptr + utfc_ptr2len(ptr) + 1); 1698 *backwards = false; 1699 } 1700 return; 1701 } 1702 char *prev = ptr; 1703 ptr += utfc_ptr2len(ptr) + 1; 1704 if (utf_ptr2char(ptr) == *initc) { 1705 if (switchit) { 1706 *findc = *initc; 1707 *initc = utf_ptr2char(prev); 1708 *backwards = false; 1709 } else { 1710 *findc = utf_ptr2char(prev); 1711 *backwards = true; 1712 } 1713 return; 1714 } 1715 ptr += utfc_ptr2len(ptr); 1716 if (*ptr == ',') { 1717 ptr++; 1718 } 1719 } 1720 } 1721 1722 // findmatchlimit -- find the matching paren or brace, if it exists within 1723 // maxtravel lines of the cursor. A maxtravel of 0 means search until falling 1724 // off the edge of the file. 1725 // 1726 // "initc" is the character to find a match for. NUL means to find the 1727 // character at or after the cursor. Special values: 1728 // '*' look for C-style comment / * 1729 // '/' look for C-style comment / *, ignoring comment-end 1730 // '#' look for preprocessor directives 1731 // 'R' look for raw string start: R"delim(text)delim" (only backwards) 1732 // 1733 // flags: FM_BACKWARD search backwards (when initc is '/', '*' or '#') 1734 // FM_FORWARD search forwards (when initc is '/', '*' or '#') 1735 // FM_BLOCKSTOP stop at start/end of block ({ or } in column 0) 1736 // FM_SKIPCOMM skip comments (not implemented yet!) 1737 // 1738 // "oap" is only used to set oap->motion_type for a linewise motion, it can be 1739 // NULL 1740 pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel) 1741 { 1742 static pos_T pos; // current search position 1743 int findc = 0; // matching brace 1744 int count = 0; // cumulative number of braces 1745 bool backwards = false; // init for gcc 1746 bool raw_string = false; // search for raw string 1747 bool inquote = false; // true when inside quotes 1748 char *ptr; 1749 int hash_dir = 0; // Direction searched for # things 1750 int comment_dir = 0; // Direction searched for comments 1751 int traveled = 0; // how far we've searched so far 1752 bool ignore_cend = false; // ignore comment end 1753 int match_escaped = 0; // search for escaped match 1754 int dir; // Direction to search 1755 int comment_col = MAXCOL; // start of / / comment 1756 bool lispcomm = false; // inside of Lisp-style comment 1757 bool lisp = curbuf->b_p_lisp; // engage Lisp-specific hacks ;) 1758 1759 pos = curwin->w_cursor; 1760 pos.coladd = 0; 1761 char *linep = ml_get(pos.lnum); // pointer to current line 1762 1763 // vi compatible matching 1764 bool cpo_match = (vim_strchr(p_cpo, CPO_MATCH) != NULL); 1765 // don't recognize backslashes 1766 bool cpo_bsl = (vim_strchr(p_cpo, CPO_MATCHBSL) != NULL); 1767 1768 // Direction to search when initc is '/', '*' or '#' 1769 if (flags & FM_BACKWARD) { 1770 dir = BACKWARD; 1771 } else if (flags & FM_FORWARD) { 1772 dir = FORWARD; 1773 } else { 1774 dir = 0; 1775 } 1776 1777 // if initc given, look in the table for the matching character 1778 // '/' and '*' are special cases: look for start or end of comment. 1779 // When '/' is used, we ignore running backwards into a star-slash, for 1780 // "[*" command, we just want to find any comment. 1781 if (initc == '/' || initc == '*' || initc == 'R') { 1782 comment_dir = dir; 1783 if (initc == '/') { 1784 ignore_cend = true; 1785 } 1786 backwards = (dir == FORWARD) ? false : true; 1787 raw_string = (initc == 'R'); 1788 initc = NUL; 1789 } else if (initc != '#' && initc != NUL) { 1790 find_mps_values(&initc, &findc, &backwards, true); 1791 if (dir) { 1792 backwards = (dir == FORWARD) ? false : true; 1793 } 1794 if (findc == NUL) { 1795 return NULL; 1796 } 1797 } else { 1798 // Either initc is '#', or no initc was given and we need to look 1799 // under the cursor. 1800 if (initc == '#') { 1801 hash_dir = dir; 1802 } else { 1803 // initc was not given, must look for something to match under 1804 // or near the cursor. 1805 // Only check for special things when 'cpo' doesn't have '%'. 1806 if (!cpo_match) { 1807 // Are we before or at #if, #else etc.? 1808 ptr = skipwhite(linep); 1809 if (*ptr == '#' && pos.col <= (colnr_T)(ptr - linep)) { 1810 ptr = skipwhite(ptr + 1); 1811 if (strncmp(ptr, "if", 2) == 0 1812 || strncmp(ptr, "endif", 5) == 0 1813 || strncmp(ptr, "el", 2) == 0) { 1814 hash_dir = 1; 1815 } 1816 } else if (linep[pos.col] == '/') { // Are we on a comment? 1817 if (linep[pos.col + 1] == '*') { 1818 comment_dir = FORWARD; 1819 backwards = false; 1820 pos.col++; 1821 } else if (pos.col > 0 && linep[pos.col - 1] == '*') { 1822 comment_dir = BACKWARD; 1823 backwards = true; 1824 pos.col--; 1825 } 1826 } else if (linep[pos.col] == '*') { 1827 if (linep[pos.col + 1] == '/') { 1828 comment_dir = BACKWARD; 1829 backwards = true; 1830 } else if (pos.col > 0 && linep[pos.col - 1] == '/') { 1831 comment_dir = FORWARD; 1832 backwards = false; 1833 } 1834 } 1835 } 1836 1837 // If we are not on a comment or the # at the start of a line, then 1838 // look for brace anywhere on this line after the cursor. 1839 if (!hash_dir && !comment_dir) { 1840 // Find the brace under or after the cursor. 1841 // If beyond the end of the line, use the last character in 1842 // the line. 1843 if (linep[pos.col] == NUL && pos.col) { 1844 pos.col--; 1845 } 1846 while (true) { 1847 initc = utf_ptr2char(linep + pos.col); 1848 if (initc == NUL) { 1849 break; 1850 } 1851 1852 find_mps_values(&initc, &findc, &backwards, false); 1853 if (findc) { 1854 break; 1855 } 1856 pos.col += utfc_ptr2len(linep + pos.col); 1857 } 1858 if (!findc) { 1859 // no brace in the line, maybe use " #if" then 1860 if (!cpo_match && *skipwhite(linep) == '#') { 1861 hash_dir = 1; 1862 } else { 1863 return NULL; 1864 } 1865 } else if (!cpo_bsl) { 1866 int bslcnt = 0; 1867 1868 // Set "match_escaped" if there are an odd number of 1869 // backslashes. 1870 for (int col = pos.col; check_prevcol(linep, col, '\\', &col);) { 1871 bslcnt++; 1872 } 1873 match_escaped = (bslcnt & 1); 1874 } 1875 } 1876 } 1877 if (hash_dir) { 1878 // Look for matching #if, #else, #elif, or #endif 1879 if (oap != NULL) { 1880 oap->motion_type = kMTLineWise; // Linewise for this case only 1881 } 1882 if (initc != '#') { 1883 ptr = skipwhite(skipwhite(linep) + 1); 1884 if (strncmp(ptr, "if", 2) == 0 || strncmp(ptr, "el", 2) == 0) { 1885 hash_dir = 1; 1886 } else if (strncmp(ptr, "endif", 5) == 0) { 1887 hash_dir = -1; 1888 } else { 1889 return NULL; 1890 } 1891 } 1892 pos.col = 0; 1893 while (!got_int) { 1894 if (hash_dir > 0) { 1895 if (pos.lnum == curbuf->b_ml.ml_line_count) { 1896 break; 1897 } 1898 } else if (pos.lnum == 1) { 1899 break; 1900 } 1901 pos.lnum += hash_dir; 1902 linep = ml_get(pos.lnum); 1903 line_breakcheck(); // check for CTRL-C typed 1904 ptr = skipwhite(linep); 1905 if (*ptr != '#') { 1906 continue; 1907 } 1908 pos.col = (colnr_T)(ptr - linep); 1909 ptr = skipwhite(ptr + 1); 1910 if (hash_dir > 0) { 1911 if (strncmp(ptr, "if", 2) == 0) { 1912 count++; 1913 } else if (strncmp(ptr, "el", 2) == 0) { 1914 if (count == 0) { 1915 return &pos; 1916 } 1917 } else if (strncmp(ptr, "endif", 5) == 0) { 1918 if (count == 0) { 1919 return &pos; 1920 } 1921 count--; 1922 } 1923 } else { 1924 if (strncmp(ptr, "if", 2) == 0) { 1925 if (count == 0) { 1926 return &pos; 1927 } 1928 count--; 1929 } else if (initc == '#' && strncmp(ptr, "el", 2) == 0) { 1930 if (count == 0) { 1931 return &pos; 1932 } 1933 } else if (strncmp(ptr, "endif", 5) == 0) { 1934 count++; 1935 } 1936 } 1937 } 1938 return NULL; 1939 } 1940 } 1941 1942 // This is just guessing: when 'rightleft' is set, search for a matching 1943 // paren/brace in the other direction. 1944 if (curwin->w_p_rl && vim_strchr("()[]{}<>", initc) != NULL) { 1945 backwards = !backwards; 1946 } 1947 1948 int do_quotes = -1; // check for quotes in current line 1949 int at_start; // do_quotes value at start position 1950 TriState start_in_quotes = kNone; // start position is in quotes 1951 pos_T match_pos; // Where last slash-star was found 1952 clearpos(&match_pos); 1953 1954 // backward search: Check if this line contains a single-line comment 1955 if ((backwards && comment_dir) || lisp) { 1956 comment_col = check_linecomment(linep); 1957 } 1958 if (lisp && comment_col != MAXCOL && pos.col > (colnr_T)comment_col) { 1959 lispcomm = true; // find match inside this comment 1960 } 1961 1962 while (!got_int) { 1963 // Go to the next position, forward or backward. We could use 1964 // inc() and dec() here, but that is much slower 1965 if (backwards) { 1966 // char to match is inside of comment, don't search outside 1967 if (lispcomm && pos.col < (colnr_T)comment_col) { 1968 break; 1969 } 1970 if (pos.col == 0) { // at start of line, go to prev. one 1971 if (pos.lnum == 1) { // start of file 1972 break; 1973 } 1974 pos.lnum--; 1975 1976 if (maxtravel > 0 && ++traveled > maxtravel) { 1977 break; 1978 } 1979 1980 linep = ml_get(pos.lnum); 1981 pos.col = ml_get_len(pos.lnum); // pos.col on trailing NUL 1982 do_quotes = -1; 1983 line_breakcheck(); 1984 1985 // Check if this line contains a single-line comment 1986 if (comment_dir || lisp) { 1987 comment_col = check_linecomment(linep); 1988 } 1989 // skip comment 1990 if (lisp && comment_col != MAXCOL) { 1991 pos.col = comment_col; 1992 } 1993 } else { 1994 pos.col--; 1995 pos.col -= utf_head_off(linep, linep + pos.col); 1996 } 1997 } else { // forward search 1998 if (linep[pos.col] == NUL 1999 // at end of line, go to next one 2000 // For lisp don't search for match in comment 2001 || (lisp && comment_col != MAXCOL 2002 && pos.col == (colnr_T)comment_col)) { 2003 if (pos.lnum == curbuf->b_ml.ml_line_count // end of file 2004 // line is exhausted and comment with it, 2005 // don't search for match in code 2006 || lispcomm) { 2007 break; 2008 } 2009 pos.lnum++; 2010 2011 if (maxtravel && traveled++ > maxtravel) { 2012 break; 2013 } 2014 2015 linep = ml_get(pos.lnum); 2016 pos.col = 0; 2017 do_quotes = -1; 2018 line_breakcheck(); 2019 if (lisp) { // find comment pos in new line 2020 comment_col = check_linecomment(linep); 2021 } 2022 } else { 2023 pos.col += utfc_ptr2len(linep + pos.col); 2024 } 2025 } 2026 2027 // If FM_BLOCKSTOP given, stop at a '{' or '}' in column 0. 2028 if (pos.col == 0 && (flags & FM_BLOCKSTOP) 2029 && (linep[0] == '{' || linep[0] == '}')) { 2030 if (linep[0] == findc && count == 0) { // match! 2031 return &pos; 2032 } 2033 break; // out of scope 2034 } 2035 2036 if (comment_dir) { 2037 // Note: comments do not nest, and we ignore quotes in them 2038 // TODO(vim): ignore comment brackets inside strings 2039 if (comment_dir == FORWARD) { 2040 if (linep[pos.col] == '*' && linep[pos.col + 1] == '/') { 2041 pos.col++; 2042 return &pos; 2043 } 2044 } else { // Searching backwards 2045 // A comment may contain / * or / /, it may also start or end 2046 // with / * /. Ignore a / * after / / and after *. 2047 if (pos.col == 0) { 2048 continue; 2049 } else if (raw_string) { 2050 if (linep[pos.col - 1] == 'R' 2051 && linep[pos.col] == '"' 2052 && vim_strchr(linep + pos.col + 1, '(') != NULL) { 2053 // Possible start of raw string. Now that we have the 2054 // delimiter we can check if it ends before where we 2055 // started searching, or before the previously found 2056 // raw string start. 2057 if (!find_rawstring_end(linep, &pos, 2058 count > 0 ? &match_pos : &curwin->w_cursor)) { 2059 count++; 2060 match_pos = pos; 2061 match_pos.col--; 2062 } 2063 linep = ml_get(pos.lnum); // may have been released 2064 } 2065 } else if (linep[pos.col - 1] == '/' 2066 && linep[pos.col] == '*' 2067 && (pos.col == 1 || linep[pos.col - 2] != '*') 2068 && (int)pos.col < comment_col) { 2069 count++; 2070 match_pos = pos; 2071 match_pos.col--; 2072 } else if (linep[pos.col - 1] == '*' && linep[pos.col] == '/') { 2073 if (count > 0) { 2074 pos = match_pos; 2075 } else if (pos.col > 1 && linep[pos.col - 2] == '/' 2076 && (int)pos.col <= comment_col) { 2077 pos.col -= 2; 2078 } else if (ignore_cend) { 2079 continue; 2080 } else { 2081 return NULL; 2082 } 2083 return &pos; 2084 } 2085 } 2086 continue; 2087 } 2088 2089 // If smart matching ('cpoptions' does not contain '%'), braces inside 2090 // of quotes are ignored, but only if there is an even number of 2091 // quotes in the line. 2092 if (cpo_match) { 2093 do_quotes = 0; 2094 } else if (do_quotes == -1) { 2095 // Count the number of quotes in the line, skipping \" and '"'. 2096 // Watch out for "\\". 2097 at_start = do_quotes; 2098 for (ptr = linep; *ptr; ptr++) { 2099 if (ptr == linep + pos.col + backwards) { 2100 at_start = (do_quotes & 1); 2101 } 2102 if (*ptr == '"' 2103 && (ptr == linep || ptr[-1] != '\'' || ptr[1] != '\'')) { 2104 do_quotes++; 2105 } 2106 if (*ptr == '\\' && ptr[1] != NUL) { 2107 ptr++; 2108 } 2109 } 2110 do_quotes &= 1; // result is 1 with even number of quotes 2111 2112 // If we find an uneven count, check current line and previous 2113 // one for a '\' at the end. 2114 if (!do_quotes) { 2115 inquote = false; 2116 if (ptr[-1] == '\\') { 2117 do_quotes = 1; 2118 if (start_in_quotes == kNone) { 2119 // Do we need to use at_start here? 2120 inquote = true; 2121 start_in_quotes = kTrue; 2122 } else if (backwards) { 2123 inquote = true; 2124 } 2125 } 2126 if (pos.lnum > 1) { 2127 ptr = ml_get(pos.lnum - 1); 2128 if (*ptr && *(ptr + ml_get_len(pos.lnum - 1) - 1) == '\\') { 2129 do_quotes = 1; 2130 if (start_in_quotes == kNone) { 2131 inquote = at_start; 2132 if (inquote) { 2133 start_in_quotes = kTrue; 2134 } 2135 } else if (!backwards) { 2136 inquote = true; 2137 } 2138 } 2139 2140 // ml_get() only keeps one line, need to get linep again 2141 linep = ml_get(pos.lnum); 2142 } 2143 } 2144 } 2145 if (start_in_quotes == kNone) { 2146 start_in_quotes = kFalse; 2147 } 2148 2149 // If 'smartmatch' is set: 2150 // Things inside quotes are ignored by setting 'inquote'. If we 2151 // find a quote without a preceding '\' invert 'inquote'. At the 2152 // end of a line not ending in '\' we reset 'inquote'. 2153 // 2154 // In lines with an uneven number of quotes (without preceding '\') 2155 // we do not know which part to ignore. Therefore we only set 2156 // inquote if the number of quotes in a line is even, unless this 2157 // line or the previous one ends in a '\'. Complicated, isn't it? 2158 const int c = utf_ptr2char(linep + pos.col); 2159 switch (c) { 2160 case NUL: 2161 // at end of line without trailing backslash, reset inquote 2162 if (pos.col == 0 || linep[pos.col - 1] != '\\') { 2163 inquote = false; 2164 start_in_quotes = kFalse; 2165 } 2166 break; 2167 2168 case '"': 2169 // a quote that is preceded with an odd number of backslashes is 2170 // ignored 2171 if (do_quotes) { 2172 int col; 2173 2174 for (col = pos.col - 1; col >= 0; col--) { 2175 if (linep[col] != '\\') { 2176 break; 2177 } 2178 } 2179 if ((((int)pos.col - 1 - col) & 1) == 0) { 2180 inquote = !inquote; 2181 start_in_quotes = kFalse; 2182 } 2183 } 2184 break; 2185 2186 // If smart matching ('cpoptions' does not contain '%'): 2187 // Skip things in single quotes: 'x' or '\x'. Be careful for single 2188 // single quotes, eg jon's. Things like '\233' or '\x3f' are not 2189 // skipped, there is never a brace in them. 2190 // Ignore this when finding matches for `'. 2191 case '\'': 2192 if (!cpo_match && initc != '\'' && findc != '\'') { 2193 if (backwards) { 2194 if (pos.col > 1) { 2195 if (linep[pos.col - 2] == '\'') { 2196 pos.col -= 2; 2197 break; 2198 } else if (linep[pos.col - 2] == '\\' 2199 && pos.col > 2 && linep[pos.col - 3] == '\'') { 2200 pos.col -= 3; 2201 break; 2202 } 2203 } 2204 } else if (linep[pos.col + 1]) { // forward search 2205 if (linep[pos.col + 1] == '\\' 2206 && linep[pos.col + 2] && linep[pos.col + 3] == '\'') { 2207 pos.col += 3; 2208 break; 2209 } else if (linep[pos.col + 2] == '\'') { 2210 pos.col += 2; 2211 break; 2212 } 2213 } 2214 } 2215 FALLTHROUGH; 2216 2217 default: 2218 // For Lisp skip over backslashed (), {} and []. 2219 // (actually, we skip #\( et al) 2220 if (curbuf->b_p_lisp 2221 && vim_strchr("(){}[]", c) != NULL 2222 && pos.col > 1 2223 && check_prevcol(linep, pos.col, '\\', NULL) 2224 && check_prevcol(linep, pos.col - 1, '#', NULL)) { 2225 break; 2226 } 2227 2228 // Check for match outside of quotes, and inside of 2229 // quotes when the start is also inside of quotes. 2230 if ((!inquote || start_in_quotes == kTrue) 2231 && (c == initc || c == findc)) { 2232 int bslcnt = 0; 2233 2234 if (!cpo_bsl) { 2235 for (int col = pos.col; check_prevcol(linep, col, '\\', &col);) { 2236 bslcnt++; 2237 } 2238 } 2239 // Only accept a match when 'M' is in 'cpo' or when escaping 2240 // is what we expect. 2241 if (cpo_bsl || (bslcnt & 1) == match_escaped) { 2242 if (c == initc) { 2243 count++; 2244 } else { 2245 if (count == 0) { 2246 return &pos; 2247 } 2248 count--; 2249 } 2250 } 2251 } 2252 } 2253 } 2254 2255 if (comment_dir == BACKWARD && count > 0) { 2256 pos = match_pos; 2257 return &pos; 2258 } 2259 return (pos_T *)NULL; // never found it 2260 } 2261 2262 /// Check if line[] contains a / / comment. 2263 /// @returns MAXCOL if not, otherwise return the column. 2264 int check_linecomment(const char *line) 2265 { 2266 const char *p = line; // scan from start 2267 // skip Lispish one-line comments 2268 if (curbuf->b_p_lisp) { 2269 if (vim_strchr(p, ';') != NULL) { // there may be comments 2270 bool in_str = false; // inside of string 2271 2272 while ((p = strpbrk(p, "\";")) != NULL) { 2273 if (*p == '"') { 2274 if (in_str) { 2275 if (*(p - 1) != '\\') { // skip escaped quote 2276 in_str = false; 2277 } 2278 } else if (p == line || ((p - line) >= 2 2279 // skip #\" form 2280 && *(p - 1) != '\\' && *(p - 2) != '#')) { 2281 in_str = true; 2282 } 2283 } else if (!in_str && ((p - line) < 2 2284 || (*(p - 1) != '\\' && *(p - 2) != '#')) 2285 && !is_pos_in_string(line, (colnr_T)(p - line))) { 2286 break; // found! 2287 } 2288 p++; 2289 } 2290 } else { 2291 p = NULL; 2292 } 2293 } else { 2294 while ((p = vim_strchr(p, '/')) != NULL) { 2295 // Accept a double /, unless it's preceded with * and followed by *, 2296 // because * / / * is an end and start of a C comment. Only 2297 // accept the position if it is not inside a string. 2298 if (p[1] == '/' && (p == line || p[-1] != '*' || p[2] != '*') 2299 && !is_pos_in_string(line, (colnr_T)(p - line))) { 2300 break; 2301 } 2302 p++; 2303 } 2304 } 2305 2306 if (p == NULL) { 2307 return MAXCOL; 2308 } 2309 return (int)(p - line); 2310 } 2311 2312 /// Move cursor briefly to character matching the one under the cursor. 2313 /// Used for Insert mode and "r" command. 2314 /// Show the match only if it is visible on the screen. 2315 /// If there isn't a match, then beep. 2316 /// 2317 /// @param c char to show match for 2318 void showmatch(int c) 2319 { 2320 pos_T *lpos; 2321 colnr_T vcol; 2322 OptInt *so = curwin->w_p_so >= 0 ? &curwin->w_p_so : &p_so; 2323 OptInt *siso = curwin->w_p_siso >= 0 ? &curwin->w_p_siso : &p_siso; 2324 char *p; 2325 2326 // Only show match for chars in the 'matchpairs' option. 2327 // 'matchpairs' is "x:y,x:y" 2328 for (p = curbuf->b_p_mps; *p != NUL; p++) { 2329 if (utf_ptr2char(p) == c && (curwin->w_p_rl ^ p_ri)) { 2330 break; 2331 } 2332 p += utfc_ptr2len(p) + 1; 2333 if (utf_ptr2char(p) == c && !(curwin->w_p_rl ^ p_ri)) { 2334 break; 2335 } 2336 p += utfc_ptr2len(p); 2337 if (*p == NUL) { 2338 return; 2339 } 2340 } 2341 if (*p == NUL) { 2342 return; 2343 } 2344 2345 if ((lpos = findmatch(NULL, NUL)) == NULL) { // no match, so beep 2346 vim_beep(kOptBoFlagShowmatch); 2347 return; 2348 } 2349 2350 if (lpos->lnum < curwin->w_topline || lpos->lnum >= curwin->w_botline) { 2351 return; 2352 } 2353 2354 if (!curwin->w_p_wrap) { 2355 getvcol(curwin, lpos, NULL, &vcol, NULL); 2356 } 2357 2358 bool col_visible = curwin->w_p_wrap 2359 || (vcol >= curwin->w_leftcol 2360 && vcol < curwin->w_leftcol + curwin->w_view_width); 2361 if (!col_visible) { 2362 return; 2363 } 2364 2365 pos_T mpos = *lpos; // save the pos, update_screen() may change it 2366 pos_T save_cursor = curwin->w_cursor; 2367 OptInt save_so = *so; 2368 OptInt save_siso = *siso; 2369 // Handle "$" in 'cpo': If the ')' is typed on top of the "$", 2370 // stop displaying the "$". 2371 if (dollar_vcol >= 0 && dollar_vcol == curwin->w_virtcol) { 2372 dollar_vcol = -1; 2373 } 2374 curwin->w_virtcol++; // do display ')' just before "$" 2375 2376 colnr_T save_dollar_vcol = dollar_vcol; 2377 int save_state = State; 2378 State = MODE_SHOWMATCH; 2379 ui_cursor_shape(); // may show different cursor shape 2380 curwin->w_cursor = mpos; // move to matching char 2381 *so = 0; // don't use 'scrolloff' here 2382 *siso = 0; // don't use 'sidescrolloff' here 2383 show_cursor_info_later(false); 2384 update_screen(); // show the new char 2385 setcursor(); 2386 ui_flush(); 2387 // Restore dollar_vcol(), because setcursor() may call curs_rows() 2388 // which resets it if the matching position is in a previous line 2389 // and has a higher column number. 2390 dollar_vcol = save_dollar_vcol; 2391 2392 // brief pause, unless 'm' is present in 'cpo' and a character is 2393 // available. 2394 if (vim_strchr(p_cpo, CPO_SHOWMATCH) != NULL) { 2395 os_delay((uint64_t)p_mat * 100 + 8, true); 2396 } else if (!char_avail()) { 2397 os_delay((uint64_t)p_mat * 100 + 9, false); 2398 } 2399 curwin->w_cursor = save_cursor; // restore cursor position 2400 *so = save_so; 2401 *siso = save_siso; 2402 State = save_state; 2403 ui_cursor_shape(); // may show different cursor shape 2404 } 2405 2406 /// Find next search match under cursor, cursor at end. 2407 /// Used while an operator is pending, and in Visual mode. 2408 /// 2409 /// @param forward true for forward, false for backward 2410 int current_search(int count, bool forward) 2411 { 2412 bool old_p_ws = p_ws; 2413 pos_T save_VIsual = VIsual; 2414 2415 // Correct cursor when 'selection' is exclusive 2416 if (VIsual_active && *p_sel == 'e' && lt(VIsual, curwin->w_cursor)) { 2417 dec_cursor(); 2418 } 2419 2420 // When searching forward and the cursor is at the start of the Visual 2421 // area, skip the first search backward, otherwise it doesn't move. 2422 const bool skip_first_backward = forward && VIsual_active 2423 && lt(curwin->w_cursor, VIsual); 2424 2425 pos_T pos = curwin->w_cursor; // position after the pattern 2426 pos_T orig_pos = curwin->w_cursor; // position of the cursor at beginning 2427 if (VIsual_active) { 2428 // Searching further will extend the match. 2429 if (forward) { 2430 incl(&pos); 2431 } else { 2432 decl(&pos); 2433 } 2434 } 2435 2436 // Is the pattern is zero-width?, this time, don't care about the direction 2437 int zero_width = is_zero_width(spats[last_idx].pat, spats[last_idx].patlen, 2438 true, &curwin->w_cursor, FORWARD); 2439 if (zero_width == -1) { 2440 return FAIL; // pattern not found 2441 } 2442 2443 pos_T end_pos; // end position of the pattern match 2444 int result; // result of various function calls 2445 2446 // The trick is to first search backwards and then search forward again, 2447 // so that a match at the current cursor position will be correctly 2448 // captured. When "forward" is false do it the other way around. 2449 for (int i = 0; i < 2; i++) { 2450 int dir; 2451 if (forward) { 2452 if (i == 0 && skip_first_backward) { 2453 continue; 2454 } 2455 dir = i; 2456 } else { 2457 dir = !i; 2458 } 2459 2460 int flags = 0; 2461 2462 if (!dir && !zero_width) { 2463 flags = SEARCH_END; 2464 } 2465 end_pos = pos; 2466 2467 // wrapping should not occur in the first round 2468 if (i == 0) { 2469 p_ws = false; 2470 } 2471 2472 result = searchit(curwin, curbuf, &pos, &end_pos, 2473 (dir ? FORWARD : BACKWARD), 2474 spats[last_idx].pat, spats[last_idx].patlen, i ? count : 1, 2475 SEARCH_KEEP | flags, RE_SEARCH, NULL); 2476 2477 p_ws = old_p_ws; 2478 2479 // First search may fail, but then start searching from the 2480 // beginning of the file (cursor might be on the search match) 2481 // except when Visual mode is active, so that extending the visual 2482 // selection works. 2483 if (i == 1 && !result) { // not found, abort 2484 curwin->w_cursor = orig_pos; 2485 if (VIsual_active) { 2486 VIsual = save_VIsual; 2487 } 2488 return FAIL; 2489 } else if (i == 0 && !result) { 2490 if (forward) { // try again from start of buffer 2491 clearpos(&pos); 2492 } else { // try again from end of buffer 2493 // searching backwards, so set pos to last line and col 2494 pos.lnum = curwin->w_buffer->b_ml.ml_line_count; 2495 pos.col = ml_get_len(curwin->w_buffer->b_ml.ml_line_count); 2496 } 2497 } 2498 } 2499 2500 pos_T start_pos = pos; 2501 2502 if (!VIsual_active) { 2503 VIsual = start_pos; 2504 } 2505 2506 // put the cursor after the match 2507 curwin->w_cursor = end_pos; 2508 if (lt(VIsual, end_pos) && forward) { 2509 if (skip_first_backward) { 2510 // put the cursor on the start of the match 2511 curwin->w_cursor = pos; 2512 } else { 2513 // put the cursor on last character of match 2514 dec_cursor(); 2515 } 2516 } else if (VIsual_active && lt(curwin->w_cursor, VIsual) && forward) { 2517 curwin->w_cursor = pos; // put the cursor on the start of the match 2518 } 2519 VIsual_active = true; 2520 VIsual_mode = 'v'; 2521 2522 if (*p_sel == 'e') { 2523 // Correction for exclusive selection depends on the direction. 2524 if (forward && ltoreq(VIsual, curwin->w_cursor)) { 2525 inc_cursor(); 2526 } else if (!forward && ltoreq(curwin->w_cursor, VIsual)) { 2527 inc(&VIsual); 2528 } 2529 } 2530 2531 if (fdo_flags & kOptFdoFlagSearch && KeyTyped) { 2532 foldOpenCursor(); 2533 } 2534 2535 may_start_select('c'); 2536 setmouse(); 2537 redraw_curbuf_later(UPD_INVERTED); 2538 showmode(); 2539 2540 return OK; 2541 } 2542 2543 /// Check if the pattern is zero-width. 2544 /// If move is true, check from the beginning of the buffer, 2545 /// else from position "cur". 2546 /// "direction" is FORWARD or BACKWARD. 2547 /// Returns true, false or -1 for failure. 2548 static int is_zero_width(char *pattern, size_t patternlen, bool move, pos_T *cur, 2549 Direction direction) 2550 { 2551 regmmatch_T regmatch; 2552 int result = -1; 2553 pos_T pos; 2554 const int called_emsg_before = called_emsg; 2555 int flag = 0; 2556 2557 if (pattern == NULL) { 2558 pattern = spats[last_idx].pat; 2559 patternlen = spats[last_idx].patlen; 2560 } 2561 2562 if (search_regcomp(pattern, patternlen, NULL, RE_SEARCH, RE_SEARCH, 2563 SEARCH_KEEP, ®match) == FAIL) { 2564 return -1; 2565 } 2566 2567 // init startcol correctly 2568 regmatch.startpos[0].col = -1; 2569 // move to match 2570 if (move) { 2571 clearpos(&pos); 2572 } else { 2573 pos = *cur; 2574 // accept a match at the cursor position 2575 flag = SEARCH_START; 2576 } 2577 if (searchit(curwin, curbuf, &pos, NULL, direction, pattern, patternlen, 1, 2578 SEARCH_KEEP + flag, RE_SEARCH, NULL) != FAIL) { 2579 int nmatched = 0; 2580 // Zero-width pattern should match somewhere, then we can check if 2581 // start and end are in the same position. 2582 do { 2583 regmatch.startpos[0].col++; 2584 nmatched = vim_regexec_multi(®match, curwin, curbuf, 2585 pos.lnum, regmatch.startpos[0].col, 2586 NULL, NULL); 2587 if (nmatched != 0) { 2588 break; 2589 } 2590 } while (regmatch.regprog != NULL 2591 && direction == FORWARD 2592 ? regmatch.startpos[0].col < pos.col 2593 : regmatch.startpos[0].col > pos.col); 2594 2595 if (called_emsg == called_emsg_before) { 2596 result = (nmatched != 0 2597 && regmatch.startpos[0].lnum == regmatch.endpos[0].lnum 2598 && regmatch.startpos[0].col == regmatch.endpos[0].col); 2599 } 2600 } 2601 2602 vim_regfree(regmatch.regprog); 2603 return result; 2604 } 2605 2606 /// @return true if line 'lnum' is empty or has white chars only. 2607 bool linewhite(linenr_T lnum) 2608 { 2609 char *p = skipwhite(ml_get(lnum)); 2610 return *p == NUL; 2611 } 2612 2613 /// Add the search count "[3/19]" to "msgbuf". 2614 /// See update_search_stat() for other arguments. 2615 static void cmdline_search_stat(int dirc, pos_T *pos, pos_T *cursor_pos, bool show_top_bot_msg, 2616 char *msgbuf, size_t msgbuflen, bool recompute, int maxcount, 2617 int timeout) 2618 { 2619 searchstat_T stat; 2620 2621 update_search_stat(dirc, pos, cursor_pos, &stat, recompute, maxcount, 2622 timeout); 2623 if (stat.cur <= 0) { 2624 return; 2625 } 2626 2627 char t[SEARCH_STAT_BUF_LEN]; 2628 size_t len; 2629 2630 if (curwin->w_p_rl && *curwin->w_p_rlc == 's') { 2631 if (stat.incomplete == 1) { 2632 len = (size_t)vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[?/??]"); 2633 } else if (stat.cnt > maxcount && stat.cur > maxcount) { 2634 len = (size_t)vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[>%d/>%d]", 2635 maxcount, maxcount); 2636 } else if (stat.cnt > maxcount) { 2637 len = (size_t)vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[>%d/%d]", 2638 maxcount, stat.cur); 2639 } else { 2640 len = (size_t)vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[%d/%d]", 2641 stat.cnt, stat.cur); 2642 } 2643 } else { 2644 if (stat.incomplete == 1) { 2645 len = (size_t)vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[?/??]"); 2646 } else if (stat.cnt > maxcount && stat.cur > maxcount) { 2647 len = (size_t)vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[>%d/>%d]", 2648 maxcount, maxcount); 2649 } else if (stat.cnt > maxcount) { 2650 len = (size_t)vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[%d/>%d]", 2651 stat.cur, maxcount); 2652 } else { 2653 len = (size_t)vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[%d/%d]", 2654 stat.cur, stat.cnt); 2655 } 2656 } 2657 2658 if (show_top_bot_msg && len + 2 < SEARCH_STAT_BUF_LEN) { 2659 memmove(t + 2, t, len); 2660 t[0] = 'W'; 2661 t[1] = ' '; 2662 len += 2; 2663 } 2664 2665 if (len > msgbuflen) { 2666 len = msgbuflen; 2667 } 2668 memmove(msgbuf + msgbuflen - len, t, len); 2669 2670 if (dirc == '?' && stat.cur == maxcount + 1) { 2671 stat.cur = -1; 2672 } 2673 2674 // keep the message even after redraw, but don't put in history 2675 msg_ext_overwrite = true; 2676 msg_ext_set_kind("search_count"); 2677 give_warning(msgbuf, false, false); 2678 } 2679 2680 // Add the search count information to "stat". 2681 // "stat" must not be NULL. 2682 // When "recompute" is true always recompute the numbers. 2683 // dirc == 0: don't find the next/previous match (only set the result to "stat") 2684 // dirc == '/': find the next match 2685 // dirc == '?': find the previous match 2686 static void update_search_stat(int dirc, pos_T *pos, pos_T *cursor_pos, searchstat_T *stat, 2687 bool recompute, int maxcount, int timeout) 2688 { 2689 int save_ws = p_ws; 2690 bool wraparound = false; 2691 pos_T p = (*pos); 2692 static pos_T lastpos = { 0, 0, 0 }; 2693 static int cur = 0; 2694 static int cnt = 0; 2695 static bool exact_match = false; 2696 static int incomplete = 0; 2697 static int last_maxcount = 0; 2698 static int chgtick = 0; 2699 static char *lastpat = NULL; 2700 static size_t lastpatlen = 0; 2701 static buf_T *lbuf = NULL; 2702 2703 CLEAR_POINTER(stat); 2704 2705 if (dirc == 0 && !recompute && !EMPTY_POS(lastpos)) { 2706 stat->cur = cur; 2707 stat->cnt = cnt; 2708 stat->exact_match = exact_match; 2709 stat->incomplete = incomplete; 2710 stat->last_maxcount = (int)p_msc; 2711 return; 2712 } 2713 last_maxcount = maxcount; 2714 wraparound = ((dirc == '?' && lt(lastpos, p)) 2715 || (dirc == '/' && lt(p, lastpos))); 2716 2717 // If anything relevant changed the count has to be recomputed. 2718 if (!(chgtick == buf_get_changedtick(curbuf) 2719 && (lastpat != NULL // suppress clang/NULL passed as nonnull parameter 2720 && strncmp(lastpat, spats[last_idx].pat, lastpatlen) == 0 2721 && lastpatlen == spats[last_idx].patlen) 2722 && equalpos(lastpos, *cursor_pos) 2723 && lbuf == curbuf) 2724 || wraparound || cur < 0 || (maxcount > 0 && cur > maxcount) 2725 || recompute) { 2726 cur = 0; 2727 cnt = 0; 2728 exact_match = false; 2729 incomplete = 0; 2730 clearpos(&lastpos); 2731 lbuf = curbuf; 2732 } 2733 2734 // when searching backwards and having jumped to the first occurrence, 2735 // cur must remain greater than 1 2736 if (equalpos(lastpos, *cursor_pos) && !wraparound 2737 && (dirc == 0 || dirc == '/' ? cur < cnt : cur > 1)) { 2738 cur += dirc == 0 ? 0 : dirc == '/' ? 1 : -1; 2739 } else { 2740 proftime_T start; 2741 bool done_search = false; 2742 pos_T endpos = { 0, 0, 0 }; 2743 p_ws = false; 2744 if (timeout > 0) { 2745 start = profile_setlimit(timeout); 2746 } 2747 while (!got_int && searchit(curwin, curbuf, &lastpos, &endpos, 2748 FORWARD, NULL, 0, 1, SEARCH_KEEP, RE_LAST, 2749 NULL) != FAIL) { 2750 done_search = true; 2751 // Stop after passing the time limit. 2752 if (timeout > 0 && profile_passed_limit(start)) { 2753 incomplete = 1; 2754 break; 2755 } 2756 cnt++; 2757 if (ltoreq(lastpos, p)) { 2758 cur = cnt; 2759 if (lt(p, endpos)) { 2760 exact_match = true; 2761 } 2762 } 2763 fast_breakcheck(); 2764 if (maxcount > 0 && cnt > maxcount) { 2765 incomplete = 2; // max count exceeded 2766 break; 2767 } 2768 } 2769 if (got_int) { 2770 cur = -1; // abort 2771 } 2772 if (done_search) { 2773 xfree(lastpat); 2774 lastpat = xstrnsave(spats[last_idx].pat, spats[last_idx].patlen); 2775 lastpatlen = spats[last_idx].patlen; 2776 chgtick = (int)buf_get_changedtick(curbuf); 2777 lbuf = curbuf; 2778 lastpos = p; 2779 } 2780 } 2781 stat->cur = cur; 2782 stat->cnt = cnt; 2783 stat->exact_match = exact_match; 2784 stat->incomplete = incomplete; 2785 stat->last_maxcount = last_maxcount; 2786 p_ws = save_ws; 2787 } 2788 2789 // "searchcount()" function 2790 void f_searchcount(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 2791 { 2792 pos_T pos = curwin->w_cursor; 2793 char *pattern = NULL; 2794 int maxcount = (int)p_msc; 2795 int timeout = SEARCH_STAT_DEF_TIMEOUT; 2796 bool recompute = true; 2797 searchstat_T stat; 2798 2799 tv_dict_alloc_ret(rettv); 2800 2801 if (shortmess(SHM_SEARCHCOUNT)) { // 'shortmess' contains 'S' flag 2802 recompute = true; 2803 } 2804 2805 if (argvars[0].v_type != VAR_UNKNOWN) { 2806 dict_T *dict; 2807 dictitem_T *di; 2808 bool error = false; 2809 2810 if (tv_check_for_nonnull_dict_arg(argvars, 0) == FAIL) { 2811 return; 2812 } 2813 dict = argvars[0].vval.v_dict; 2814 di = tv_dict_find(dict, "timeout", -1); 2815 if (di != NULL) { 2816 timeout = (int)tv_get_number_chk(&di->di_tv, &error); 2817 if (error) { 2818 return; 2819 } 2820 } 2821 di = tv_dict_find(dict, "maxcount", -1); 2822 if (di != NULL) { 2823 maxcount = (int)tv_get_number_chk(&di->di_tv, &error); 2824 if (error) { 2825 return; 2826 } 2827 } 2828 di = tv_dict_find(dict, "recompute", -1); 2829 if (di != NULL) { 2830 recompute = tv_get_number_chk(&di->di_tv, &error); 2831 if (error) { 2832 return; 2833 } 2834 } 2835 di = tv_dict_find(dict, "pattern", -1); 2836 if (di != NULL) { 2837 pattern = (char *)tv_get_string_chk(&di->di_tv); 2838 if (pattern == NULL) { 2839 return; 2840 } 2841 } 2842 di = tv_dict_find(dict, "pos", -1); 2843 if (di != NULL) { 2844 if (di->di_tv.v_type != VAR_LIST) { 2845 semsg(_(e_invarg2), "pos"); 2846 return; 2847 } 2848 if (tv_list_len(di->di_tv.vval.v_list) != 3) { 2849 semsg(_(e_invarg2), "List format should be [lnum, col, off]"); 2850 return; 2851 } 2852 listitem_T *li = tv_list_find(di->di_tv.vval.v_list, 0); 2853 if (li != NULL) { 2854 pos.lnum = (linenr_T)tv_get_number_chk(TV_LIST_ITEM_TV(li), &error); 2855 if (error) { 2856 return; 2857 } 2858 } 2859 li = tv_list_find(di->di_tv.vval.v_list, 1); 2860 if (li != NULL) { 2861 pos.col = (colnr_T)tv_get_number_chk(TV_LIST_ITEM_TV(li), &error) - 1; 2862 if (error) { 2863 return; 2864 } 2865 } 2866 li = tv_list_find(di->di_tv.vval.v_list, 2); 2867 if (li != NULL) { 2868 pos.coladd = (colnr_T)tv_get_number_chk(TV_LIST_ITEM_TV(li), &error); 2869 if (error) { 2870 return; 2871 } 2872 } 2873 } 2874 } 2875 2876 save_last_search_pattern(); 2877 save_incsearch_state(); 2878 if (pattern != NULL) { 2879 if (*pattern == NUL) { 2880 goto the_end; 2881 } 2882 xfree(spats[last_idx].pat); 2883 spats[last_idx].patlen = strlen(pattern); 2884 spats[last_idx].pat = xstrnsave(pattern, spats[last_idx].patlen); 2885 } 2886 if (spats[last_idx].pat == NULL || *spats[last_idx].pat == NUL) { 2887 goto the_end; // the previous pattern was never defined 2888 } 2889 2890 update_search_stat(0, &pos, &pos, &stat, recompute, maxcount, timeout); 2891 2892 tv_dict_add_nr(rettv->vval.v_dict, S_LEN("current"), stat.cur); 2893 tv_dict_add_nr(rettv->vval.v_dict, S_LEN("total"), stat.cnt); 2894 tv_dict_add_nr(rettv->vval.v_dict, S_LEN("exact_match"), stat.exact_match); 2895 tv_dict_add_nr(rettv->vval.v_dict, S_LEN("incomplete"), stat.incomplete); 2896 tv_dict_add_nr(rettv->vval.v_dict, S_LEN("maxcount"), stat.last_maxcount); 2897 2898 the_end: 2899 restore_last_search_pattern(); 2900 restore_incsearch_state(); 2901 } 2902 2903 /// Get line "lnum" and copy it into "buf[LSIZE]". 2904 /// The copy is made because the regexp may make the line invalid when using a 2905 /// mark. 2906 static char *get_line_and_copy(linenr_T lnum, char *buf) 2907 { 2908 char *line = ml_get(lnum); 2909 xstrlcpy(buf, line, LSIZE); 2910 return buf; 2911 } 2912 2913 /// Find identifiers or defines in included files. 2914 /// If p_ic && compl_status_sol() then ptr must be in lowercase. 2915 /// 2916 /// @param ptr pointer to search pattern 2917 /// @param dir direction of expansion 2918 /// @param len length of search pattern 2919 /// @param whole match whole words only 2920 /// @param skip_comments don't match inside comments 2921 /// @param type Type of search; are we looking for a type? a macro? 2922 /// @param action What to do when we find it 2923 /// @param start_lnum first line to start searching 2924 /// @param end_lnum last line for searching 2925 /// @param forceit If true, always switch to the found path 2926 /// @param silent Do not print messages when ACTION_EXPAND 2927 void find_pattern_in_path(char *ptr, Direction dir, size_t len, bool whole, bool skip_comments, 2928 int type, int count, int action, linenr_T start_lnum, linenr_T end_lnum, 2929 bool forceit, bool silent) 2930 { 2931 SearchedFile *files; // Stack of included files 2932 SearchedFile *bigger; // When we need more space 2933 int max_path_depth = 50; 2934 int match_count = 1; 2935 2936 char *new_fname; 2937 char *curr_fname = curbuf->b_fname; 2938 char *prev_fname = NULL; 2939 int depth_displayed; // For type==CHECK_PATH 2940 char *p; 2941 bool define_matched; 2942 regmatch_T regmatch; 2943 regmatch_T incl_regmatch; 2944 regmatch_T def_regmatch; 2945 bool matched = false; 2946 bool did_show = false; 2947 bool found = false; 2948 int i; 2949 char *already = NULL; 2950 char *startp = NULL; 2951 win_T *curwin_save = NULL; 2952 const int l_g_do_tagpreview = g_do_tagpreview; 2953 2954 regmatch.regprog = NULL; 2955 incl_regmatch.regprog = NULL; 2956 def_regmatch.regprog = NULL; 2957 2958 char *file_line = xmalloc(LSIZE); 2959 2960 if (type != CHECK_PATH && type != FIND_DEFINE 2961 // when CONT_SOL is set compare "ptr" with the beginning of the 2962 // line is faster than quote_meta/regcomp/regexec "ptr" -- Acevedo 2963 && !compl_status_sol()) { 2964 size_t patsize = len + 5; 2965 char *pat = xmalloc(patsize); 2966 assert(len <= INT_MAX); 2967 snprintf(pat, patsize, whole ? "\\<%.*s\\>" : "%.*s", (int)len, ptr); 2968 // ignore case according to p_ic, p_scs and pat 2969 regmatch.rm_ic = ignorecase(pat); 2970 regmatch.regprog = vim_regcomp(pat, magic_isset() ? RE_MAGIC : 0); 2971 xfree(pat); 2972 if (regmatch.regprog == NULL) { 2973 goto fpip_end; 2974 } 2975 } 2976 char *inc_opt = (*curbuf->b_p_inc == NUL) ? p_inc : curbuf->b_p_inc; 2977 if (*inc_opt != NUL) { 2978 incl_regmatch.regprog = vim_regcomp(inc_opt, magic_isset() ? RE_MAGIC : 0); 2979 if (incl_regmatch.regprog == NULL) { 2980 goto fpip_end; 2981 } 2982 incl_regmatch.rm_ic = false; // don't ignore case in incl. pat. 2983 } 2984 if (type == FIND_DEFINE && (*curbuf->b_p_def != NUL || *p_def != NUL)) { 2985 def_regmatch.regprog = vim_regcomp(*curbuf->b_p_def == NUL ? p_def : curbuf->b_p_def, 2986 magic_isset() ? RE_MAGIC : 0); 2987 if (def_regmatch.regprog == NULL) { 2988 goto fpip_end; 2989 } 2990 def_regmatch.rm_ic = false; // don't ignore case in define pat. 2991 } 2992 files = xcalloc((size_t)max_path_depth, sizeof(SearchedFile)); 2993 int old_files = max_path_depth; 2994 int depth = depth_displayed = -1; 2995 2996 end_lnum = MIN(end_lnum, curbuf->b_ml.ml_line_count); 2997 linenr_T lnum = MIN(start_lnum, end_lnum); // do at least one line 2998 char *line = get_line_and_copy(lnum, file_line); 2999 3000 while (true) { 3001 if (incl_regmatch.regprog != NULL 3002 && vim_regexec(&incl_regmatch, line, 0)) { 3003 char *p_fname = (curr_fname == curbuf->b_fname) 3004 ? curbuf->b_ffname : curr_fname; 3005 3006 if (strstr(inc_opt, "\\zs") != NULL) { 3007 // Use text from '\zs' to '\ze' (or end) of 'include'. 3008 new_fname = find_file_name_in_path(incl_regmatch.startp[0], 3009 (size_t)(incl_regmatch.endp[0] 3010 - incl_regmatch.startp[0]), 3011 FNAME_EXP|FNAME_INCL|FNAME_REL, 3012 1, p_fname); 3013 } else { 3014 // Use text after match with 'include'. 3015 new_fname = file_name_in_line(incl_regmatch.endp[0], 0, 3016 FNAME_EXP|FNAME_INCL|FNAME_REL, 1, p_fname, 3017 NULL); 3018 } 3019 bool already_searched = false; 3020 if (new_fname != NULL) { 3021 // Check whether we have already searched in this file 3022 for (i = 0;; i++) { 3023 if (i == depth + 1) { 3024 i = old_files; 3025 } 3026 if (i == max_path_depth) { 3027 break; 3028 } 3029 if (path_full_compare(new_fname, files[i].name, true, 3030 true) & kEqualFiles) { 3031 if (type != CHECK_PATH 3032 && action == ACTION_SHOW_ALL && files[i].matched) { 3033 msg_putchar('\n'); // cursor below last one 3034 if (!got_int) { // don't display if 'q' typed at "--more--" message 3035 msg_home_replace(new_fname); 3036 msg_puts(_(" (includes previously listed match)")); 3037 prev_fname = NULL; 3038 } 3039 } 3040 XFREE_CLEAR(new_fname); 3041 already_searched = true; 3042 break; 3043 } 3044 } 3045 } 3046 3047 if (type == CHECK_PATH && (action == ACTION_SHOW_ALL 3048 || (new_fname == NULL && !already_searched))) { 3049 if (did_show) { 3050 msg_putchar('\n'); // cursor below last one 3051 } else { 3052 gotocmdline(true); // cursor at status line 3053 msg_puts_title(_("--- Included files ")); 3054 if (action != ACTION_SHOW_ALL) { 3055 msg_puts_title(_("not found ")); 3056 } 3057 msg_puts_title(_("in path ---\n")); 3058 } 3059 did_show = true; 3060 while (depth_displayed < depth && !got_int) { 3061 depth_displayed++; 3062 for (i = 0; i < depth_displayed; i++) { 3063 msg_puts(" "); 3064 } 3065 msg_home_replace(files[depth_displayed].name); 3066 msg_puts(" -->\n"); 3067 } 3068 if (!got_int) { // don't display if 'q' typed 3069 // for "--more--" message 3070 for (i = 0; i <= depth_displayed; i++) { 3071 msg_puts(" "); 3072 } 3073 if (new_fname != NULL) { 3074 // using "new_fname" is more reliable, e.g., when 3075 // 'includeexpr' is set. 3076 msg_outtrans(new_fname, HLF_D, false); 3077 } else { 3078 // Isolate the file name. 3079 // Include the surrounding "" or <> if present. 3080 if (strstr(inc_opt, "\\zs") != NULL) { 3081 // pattern contains \zs, use the match 3082 p = incl_regmatch.startp[0]; 3083 i = (int)(incl_regmatch.endp[0] 3084 - incl_regmatch.startp[0]); 3085 } else { 3086 // find the file name after the end of the match 3087 for (p = incl_regmatch.endp[0]; 3088 *p && !vim_isfilec((uint8_t)(*p)); p++) {} 3089 for (i = 0; vim_isfilec((uint8_t)p[i]); i++) {} 3090 } 3091 3092 if (i == 0) { 3093 // Nothing found, use the rest of the line. 3094 p = incl_regmatch.endp[0]; 3095 i = (int)strlen(p); 3096 } else if (p > line) { 3097 // Avoid checking before the start of the line, can 3098 // happen if \zs appears in the regexp. 3099 if (p[-1] == '"' || p[-1] == '<') { 3100 p--; 3101 i++; 3102 } 3103 if (p[i] == '"' || p[i] == '>') { 3104 i++; 3105 } 3106 } 3107 char save_char = p[i]; 3108 p[i] = NUL; 3109 msg_outtrans(p, HLF_D, false); 3110 p[i] = save_char; 3111 } 3112 3113 if (new_fname == NULL && action == ACTION_SHOW_ALL) { 3114 if (already_searched) { 3115 msg_puts(_(" (Already listed)")); 3116 } else { 3117 msg_puts(_(" NOT FOUND")); 3118 } 3119 } 3120 } 3121 } 3122 3123 if (new_fname != NULL) { 3124 // Push the new file onto the file stack 3125 if (depth + 1 == old_files) { 3126 bigger = xmalloc((size_t)max_path_depth * 2 * sizeof(SearchedFile)); 3127 for (i = 0; i <= depth; i++) { 3128 bigger[i] = files[i]; 3129 } 3130 for (i = depth + 1; i < old_files + max_path_depth; i++) { 3131 bigger[i].fp = NULL; 3132 bigger[i].name = NULL; 3133 bigger[i].lnum = 0; 3134 bigger[i].matched = false; 3135 } 3136 for (i = old_files; i < max_path_depth; i++) { 3137 bigger[i + max_path_depth] = files[i]; 3138 } 3139 old_files += max_path_depth; 3140 max_path_depth *= 2; 3141 xfree(files); 3142 files = bigger; 3143 } 3144 if ((files[depth + 1].fp = os_fopen(new_fname, "r")) == NULL) { 3145 xfree(new_fname); 3146 } else { 3147 if (++depth == old_files) { 3148 // Something wrong. We will forget one of our already visited files 3149 // now. 3150 xfree(files[old_files].name); 3151 old_files++; 3152 } 3153 files[depth].name = curr_fname = new_fname; 3154 files[depth].lnum = 0; 3155 files[depth].matched = false; 3156 if (action == ACTION_EXPAND && !shortmess(SHM_COMPLETIONSCAN) && !silent) { 3157 msg_hist_off = true; // reset in msg_trunc() 3158 vim_snprintf(IObuff, IOSIZE, 3159 _("Scanning included file: %s"), 3160 new_fname); 3161 msg_trunc(IObuff, true, HLF_R); 3162 } else if (p_verbose >= 5) { 3163 verbose_enter(); 3164 smsg(0, _("Searching included file %s"), new_fname); 3165 verbose_leave(); 3166 } 3167 } 3168 } 3169 } else { 3170 // Check if the line is a define (type == FIND_DEFINE) 3171 p = line; 3172 search_line: 3173 define_matched = false; 3174 if (def_regmatch.regprog != NULL 3175 && vim_regexec(&def_regmatch, line, 0)) { 3176 // Pattern must be first identifier after 'define', so skip 3177 // to that position before checking for match of pattern. Also 3178 // don't let it match beyond the end of this identifier. 3179 p = def_regmatch.endp[0]; 3180 while (*p && !vim_iswordc((uint8_t)(*p))) { 3181 p++; 3182 } 3183 define_matched = true; 3184 } 3185 3186 // Look for a match. Don't do this if we are looking for a 3187 // define and this line didn't match define_prog above. 3188 if (def_regmatch.regprog == NULL || define_matched) { 3189 if (define_matched || compl_status_sol()) { 3190 // compare the first "len" chars from "ptr" 3191 startp = skipwhite(p); 3192 if (p_ic) { 3193 matched = !mb_strnicmp(startp, ptr, len); 3194 } else { 3195 matched = !strncmp(startp, ptr, len); 3196 } 3197 if (matched && define_matched && whole 3198 && vim_iswordc((uint8_t)startp[len])) { 3199 matched = false; 3200 } 3201 } else if (regmatch.regprog != NULL 3202 && vim_regexec(®match, line, (colnr_T)(p - line))) { 3203 matched = true; 3204 startp = regmatch.startp[0]; 3205 // Check if the line is not a comment line (unless we are 3206 // looking for a define). A line starting with "# define" 3207 // is not considered to be a comment line. 3208 if (skip_comments) { 3209 if ((*line != '#' 3210 || strncmp(skipwhite(line + 1), "define", 6) != 0) 3211 && get_leader_len(line, NULL, false, true)) { 3212 matched = false; 3213 } 3214 3215 // Also check for a "/ *" or "/ /" before the match. 3216 // Skips lines like "int backwards; / * normal index 3217 // * /" when looking for "normal". 3218 // Note: Doesn't skip "/ *" in comments. 3219 p = skipwhite(line); 3220 if (matched 3221 || (p[0] == '/' && p[1] == '*') || p[0] == '*') { 3222 for (p = line; *p && p < startp; p++) { 3223 if (matched 3224 && p[0] == '/' 3225 && (p[1] == '*' || p[1] == '/')) { 3226 matched = false; 3227 // After "//" all text is comment 3228 if (p[1] == '/') { 3229 break; 3230 } 3231 p++; 3232 } else if (!matched && p[0] == '*' && p[1] == '/') { 3233 // Can find match after "* /". 3234 matched = true; 3235 p++; 3236 } 3237 } 3238 } 3239 } 3240 } 3241 } 3242 } 3243 if (matched) { 3244 if (action == ACTION_EXPAND) { 3245 bool cont_s_ipos = false; 3246 3247 if (depth == -1 && lnum == curwin->w_cursor.lnum) { 3248 break; 3249 } 3250 found = true; 3251 char *aux = p = startp; 3252 if (compl_status_adding() && (int)strlen(p) >= ins_compl_len()) { 3253 p += ins_compl_len(); 3254 if (vim_iswordp(p)) { 3255 goto exit_matched; 3256 } 3257 p = find_word_start(p); 3258 } 3259 p = find_word_end(p); 3260 i = (int)(p - aux); 3261 3262 if (compl_status_adding() && i == ins_compl_len()) { 3263 // IOSIZE > compl_length, so the strncpy works 3264 strncpy(IObuff, aux, (size_t)i); // NOLINT(runtime/printf) 3265 3266 // Get the next line: when "depth" < 0 from the current 3267 // buffer, otherwise from the included file. Jump to 3268 // exit_matched when past the last line. 3269 if (depth < 0) { 3270 if (lnum >= end_lnum) { 3271 goto exit_matched; 3272 } 3273 line = get_line_and_copy(++lnum, file_line); 3274 } else if (vim_fgets(line = file_line, 3275 LSIZE, files[depth].fp)) { 3276 goto exit_matched; 3277 } 3278 3279 // we read a line, set "already" to check this "line" later 3280 // if depth >= 0 we'll increase files[depth].lnum far 3281 // below -- Acevedo 3282 already = aux = p = skipwhite(line); 3283 p = find_word_start(p); 3284 p = find_word_end(p); 3285 if (p > aux) { 3286 if (*aux != ')' && IObuff[i - 1] != TAB) { 3287 if (IObuff[i - 1] != ' ') { 3288 IObuff[i++] = ' '; 3289 } 3290 // IObuf =~ "\(\k\|\i\).* ", thus i >= 2 3291 if (p_js 3292 && (IObuff[i - 2] == '.' 3293 || IObuff[i - 2] == '?' 3294 || IObuff[i - 2] == '!')) { 3295 IObuff[i++] = ' '; 3296 } 3297 } 3298 // copy as much as possible of the new word 3299 if (p - aux >= IOSIZE - i) { 3300 p = aux + IOSIZE - i - 1; 3301 } 3302 strncpy(IObuff + i, aux, (size_t)(p - aux)); // NOLINT(runtime/printf) 3303 i += (int)(p - aux); 3304 cont_s_ipos = true; 3305 } 3306 IObuff[i] = NUL; 3307 aux = IObuff; 3308 3309 if (i == ins_compl_len()) { 3310 goto exit_matched; 3311 } 3312 } 3313 3314 const int add_r = ins_compl_add_infercase(aux, i, p_ic, 3315 curr_fname == curbuf->b_fname 3316 ? NULL : curr_fname, 3317 dir, cont_s_ipos, 0); 3318 if (add_r == OK) { 3319 // if dir was BACKWARD then honor it just once 3320 dir = FORWARD; 3321 } else if (add_r == FAIL) { 3322 break; 3323 } 3324 } else if (action == ACTION_SHOW_ALL) { 3325 found = true; 3326 if (!did_show) { 3327 gotocmdline(true); // cursor at status line 3328 } 3329 if (curr_fname != prev_fname) { 3330 if (did_show) { 3331 msg_putchar('\n'); // cursor below last one 3332 } 3333 if (!got_int) { // don't display if 'q' typed 3334 // at "--more--" message 3335 msg_home_replace(curr_fname); 3336 } 3337 prev_fname = curr_fname; 3338 } 3339 did_show = true; 3340 if (!got_int) { 3341 show_pat_in_path(line, type, true, action, 3342 (depth == -1) ? NULL : files[depth].fp, 3343 (depth == -1) ? &lnum : &files[depth].lnum, 3344 match_count++); 3345 } 3346 3347 // Set matched flag for this file and all the ones that 3348 // include it 3349 for (i = 0; i <= depth; i++) { 3350 files[i].matched = true; 3351 } 3352 } else if (--count <= 0) { 3353 found = true; 3354 if (depth == -1 && lnum == curwin->w_cursor.lnum 3355 && l_g_do_tagpreview == 0) { 3356 emsg(_("E387: Match is on current line")); 3357 } else if (action == ACTION_SHOW) { 3358 show_pat_in_path(line, type, did_show, action, 3359 (depth == -1) ? NULL : files[depth].fp, 3360 (depth == -1) ? &lnum : &files[depth].lnum, 1); 3361 did_show = true; 3362 } else { 3363 // ":psearch" uses the preview window 3364 if (l_g_do_tagpreview != 0) { 3365 curwin_save = curwin; 3366 prepare_tagpreview(true); 3367 } 3368 if (action == ACTION_SPLIT) { 3369 if (win_split(0, 0) == FAIL) { 3370 break; 3371 } 3372 RESET_BINDING(curwin); 3373 } 3374 if (depth == -1) { 3375 // match in current file 3376 if (l_g_do_tagpreview != 0) { 3377 if (!win_valid(curwin_save)) { 3378 break; 3379 } 3380 if (!GETFILE_SUCCESS(getfile(curwin_save->w_buffer->b_fnum, NULL, 3381 NULL, true, lnum, forceit))) { 3382 break; // failed to jump to file 3383 } 3384 } else { 3385 setpcmark(); 3386 } 3387 curwin->w_cursor.lnum = lnum; 3388 check_cursor(curwin); 3389 } else { 3390 if (!GETFILE_SUCCESS(getfile(0, files[depth].name, NULL, true, 3391 files[depth].lnum, forceit))) { 3392 break; // failed to jump to file 3393 } 3394 // autocommands may have changed the lnum, we don't 3395 // want that here 3396 curwin->w_cursor.lnum = files[depth].lnum; 3397 } 3398 } 3399 if (action != ACTION_SHOW) { 3400 curwin->w_cursor.col = (colnr_T)(startp - line); 3401 curwin->w_set_curswant = true; 3402 } 3403 3404 if (l_g_do_tagpreview != 0 3405 && curwin != curwin_save && win_valid(curwin_save)) { 3406 // Return cursor to where we were 3407 validate_cursor(curwin); 3408 redraw_later(curwin, UPD_VALID); 3409 win_enter(curwin_save, true); 3410 } 3411 break; 3412 } 3413 exit_matched: 3414 matched = false; 3415 // look for other matches in the rest of the line if we 3416 // are not at the end of it already 3417 if (def_regmatch.regprog == NULL 3418 && action == ACTION_EXPAND 3419 && !compl_status_sol() 3420 && *startp != NUL 3421 && *(startp + utfc_ptr2len(startp)) != NUL) { 3422 goto search_line; 3423 } 3424 } 3425 line_breakcheck(); 3426 if (action == ACTION_EXPAND) { 3427 ins_compl_check_keys(30, false); 3428 } 3429 if (got_int || ins_compl_interrupted()) { 3430 break; 3431 } 3432 3433 // Read the next line. When reading an included file and encountering 3434 // end-of-file, close the file and continue in the file that included 3435 // it. 3436 while (depth >= 0 && !already 3437 && vim_fgets(line = file_line, LSIZE, files[depth].fp)) { 3438 fclose(files[depth].fp); 3439 old_files--; 3440 files[old_files].name = files[depth].name; 3441 files[old_files].matched = files[depth].matched; 3442 depth--; 3443 curr_fname = (depth == -1) ? curbuf->b_fname 3444 : files[depth].name; 3445 depth_displayed = MIN(depth_displayed, depth); 3446 } 3447 if (depth >= 0) { // we could read the line 3448 files[depth].lnum++; 3449 // Remove any CR and LF from the line. 3450 i = (int)strlen(line); 3451 if (i > 0 && line[i - 1] == '\n') { 3452 line[--i] = NUL; 3453 } 3454 if (i > 0 && line[i - 1] == '\r') { 3455 line[--i] = NUL; 3456 } 3457 } else if (!already) { 3458 if (++lnum > end_lnum) { 3459 break; 3460 } 3461 line = get_line_and_copy(lnum, file_line); 3462 } 3463 already = NULL; 3464 } 3465 // End of big while (true) loop. 3466 3467 // Close any files that are still open. 3468 for (i = 0; i <= depth; i++) { 3469 fclose(files[i].fp); 3470 xfree(files[i].name); 3471 } 3472 for (i = old_files; i < max_path_depth; i++) { 3473 xfree(files[i].name); 3474 } 3475 xfree(files); 3476 3477 if (type == CHECK_PATH) { 3478 if (!did_show) { 3479 if (action != ACTION_SHOW_ALL) { 3480 msg(_("All included files were found"), 0); 3481 } else { 3482 msg(_("No included files"), 0); 3483 } 3484 } 3485 } else if (!found && action != ACTION_EXPAND && !silent) { 3486 if (got_int || ins_compl_interrupted()) { 3487 emsg(_(e_interr)); 3488 } else if (type == FIND_DEFINE) { 3489 emsg(_("E388: Couldn't find definition")); 3490 } else { 3491 emsg(_("E389: Couldn't find pattern")); 3492 } 3493 } 3494 if (action == ACTION_SHOW || action == ACTION_SHOW_ALL) { 3495 msg_end(); 3496 } 3497 3498 fpip_end: 3499 xfree(file_line); 3500 vim_regfree(regmatch.regprog); 3501 vim_regfree(incl_regmatch.regprog); 3502 vim_regfree(def_regmatch.regprog); 3503 } 3504 3505 static void show_pat_in_path(char *line, int type, bool did_show, int action, FILE *fp, 3506 linenr_T *lnum, int count) 3507 FUNC_ATTR_NONNULL_ARG(1, 6) 3508 { 3509 if (did_show) { 3510 msg_putchar('\n'); // cursor below last one 3511 } else if (!msg_silent) { 3512 gotocmdline(true); // cursor at status line 3513 } 3514 if (got_int) { // 'q' typed at "--more--" message 3515 return; 3516 } 3517 size_t linelen = strlen(line); 3518 while (true) { 3519 char *p = line + linelen - 1; 3520 if (fp != NULL) { 3521 // We used fgets(), so get rid of newline at end 3522 if (p >= line && *p == '\n') { 3523 p--; 3524 } 3525 if (p >= line && *p == '\r') { 3526 p--; 3527 } 3528 *(p + 1) = NUL; 3529 } 3530 if (action == ACTION_SHOW_ALL) { 3531 snprintf(IObuff, IOSIZE, "%3d: ", count); // Show match nr. 3532 msg_puts(IObuff); 3533 snprintf(IObuff, IOSIZE, "%4" PRIdLINENR, *lnum); // Show line nr. 3534 // Highlight line numbers. 3535 msg_puts_hl(IObuff, HLF_N, false); 3536 msg_puts(" "); 3537 } 3538 msg_prt_line(line, false); 3539 3540 // Definition continues until line that doesn't end with '\' 3541 if (got_int || type != FIND_DEFINE || p < line || *p != '\\') { 3542 break; 3543 } 3544 3545 if (fp != NULL) { 3546 if (vim_fgets(line, LSIZE, fp)) { // end of file 3547 break; 3548 } 3549 linelen = strlen(line); 3550 (*lnum)++; 3551 } else { 3552 if (++*lnum > curbuf->b_ml.ml_line_count) { 3553 break; 3554 } 3555 line = ml_get(*lnum); 3556 linelen = (size_t)ml_get_len(*lnum); 3557 } 3558 msg_putchar('\n'); 3559 } 3560 } 3561 3562 /// Get last search pattern 3563 void get_search_pattern(SearchPattern *const pat) 3564 { 3565 memcpy(pat, &(spats[0]), sizeof(spats[0])); 3566 } 3567 3568 /// Get last substitute pattern 3569 void get_substitute_pattern(SearchPattern *const pat) 3570 { 3571 memcpy(pat, &(spats[1]), sizeof(spats[1])); 3572 CLEAR_FIELD(pat->off); 3573 } 3574 3575 /// Set last search pattern 3576 void set_search_pattern(const SearchPattern pat) 3577 { 3578 free_spat(&spats[0]); 3579 memcpy(&(spats[0]), &pat, sizeof(spats[0])); 3580 set_vv_searchforward(); 3581 } 3582 3583 /// Set last substitute pattern 3584 void set_substitute_pattern(const SearchPattern pat) 3585 { 3586 free_spat(&spats[1]); 3587 memcpy(&(spats[1]), &pat, sizeof(spats[1])); 3588 CLEAR_FIELD(spats[1].off); 3589 } 3590 3591 /// Set last used search pattern 3592 /// 3593 /// @param[in] is_substitute_pattern If true set substitute pattern as last 3594 /// used. Otherwise sets search pattern. 3595 void set_last_used_pattern(const bool is_substitute_pattern) 3596 { 3597 last_idx = (is_substitute_pattern ? 1 : 0); 3598 } 3599 3600 /// Returns true if search pattern was the last used one 3601 bool search_was_last_used(void) 3602 { 3603 return last_idx == 0; 3604 }