tag.c (110101B)
1 // Code to handle tags and the tag stack 2 3 #include <assert.h> 4 #include <ctype.h> 5 #include <inttypes.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/charset.h" 17 #include "nvim/cmdexpand.h" 18 #include "nvim/cmdexpand_defs.h" 19 #include "nvim/cursor.h" 20 #include "nvim/drawscreen.h" 21 #include "nvim/errors.h" 22 #include "nvim/eval.h" 23 #include "nvim/eval/typval.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/file_search.h" 29 #include "nvim/fileio.h" 30 #include "nvim/fold.h" 31 #include "nvim/garray.h" 32 #include "nvim/garray_defs.h" 33 #include "nvim/gettext_defs.h" 34 #include "nvim/globals.h" 35 #include "nvim/hashtab.h" 36 #include "nvim/hashtab_defs.h" 37 #include "nvim/help.h" 38 #include "nvim/highlight_defs.h" 39 #include "nvim/input.h" 40 #include "nvim/insexpand.h" 41 #include "nvim/macros_defs.h" 42 #include "nvim/mark.h" 43 #include "nvim/mark_defs.h" 44 #include "nvim/mbyte.h" 45 #include "nvim/mbyte_defs.h" 46 #include "nvim/memory.h" 47 #include "nvim/message.h" 48 #include "nvim/move.h" 49 #include "nvim/option.h" 50 #include "nvim/option_defs.h" 51 #include "nvim/option_vars.h" 52 #include "nvim/optionstr.h" 53 #include "nvim/os/fs.h" 54 #include "nvim/os/input.h" 55 #include "nvim/os/os_defs.h" 56 #include "nvim/os/time.h" 57 #include "nvim/path.h" 58 #include "nvim/pos_defs.h" 59 #include "nvim/quickfix.h" 60 #include "nvim/regexp.h" 61 #include "nvim/regexp_defs.h" 62 #include "nvim/runtime.h" 63 #include "nvim/search.h" 64 #include "nvim/state_defs.h" 65 #include "nvim/strings.h" 66 #include "nvim/tag.h" 67 #include "nvim/types_defs.h" 68 #include "nvim/ui.h" 69 #include "nvim/vim_defs.h" 70 #include "nvim/window.h" 71 72 // Structure to hold pointers to various items in a tag line. 73 typedef struct { 74 // filled in by parse_tag_line(): 75 char *tagname; // start of tag name (skip "file:") 76 char *tagname_end; // char after tag name 77 char *fname; // first char of file name 78 char *fname_end; // char after file name 79 char *command; // first char of command 80 // filled in by parse_match(): 81 char *command_end; // first char after command 82 char *tag_fname; // file name of the tags file. This is used 83 // when 'tr' is set. 84 char *tagkind; // "kind:" value 85 char *tagkind_end; // end of tagkind 86 char *user_data; // user_data string 87 char *user_data_end; // end of user_data 88 linenr_T tagline; // "line:" value 89 } tagptrs_T; 90 91 // Structure to hold info about the tag pattern being used. 92 typedef struct { 93 char *pat; // the pattern 94 int len; // length of pat[] 95 char *head; // start of pattern head 96 int headlen; // length of head[] 97 regmatch_T regmatch; // regexp program, may be NULL 98 } pat_T; 99 100 // The matching tags are first stored in one of the hash tables. In 101 // which one depends on the priority of the match. 102 // ht_match[] is used to find duplicates, ga_match[] to keep them in sequence. 103 // At the end, the matches from ga_match[] are concatenated, to make a list 104 // sorted on priority. 105 enum { 106 MT_ST_CUR = 0, // static match in current file 107 MT_GL_CUR = 1, // global match in current file 108 MT_GL_OTH = 2, // global match in other file 109 MT_ST_OTH = 3, // static match in other file 110 MT_IC_OFF = 4, // add for icase match 111 MT_RE_OFF = 8, // add for regexp match 112 MT_MASK = 7, // mask for printing priority 113 MT_COUNT = 16, 114 }; 115 116 static char *mt_names[MT_COUNT/2] = 117 { "FSC", "F C", "F ", "FS ", " SC", " C", " ", " S " }; 118 119 #define NOTAGFILE 99 // return value for jumpto_tag 120 static char *nofile_fname = NULL; // fname for NOTAGFILE error 121 122 /// Return values used when reading lines from a tags file. 123 typedef enum { 124 TAGS_READ_SUCCESS = 1, 125 TAGS_READ_EOF, 126 TAGS_READ_IGNORE, 127 } tags_read_status_T; 128 129 /// States used during a tags search 130 typedef enum { 131 TS_START, ///< at start of file 132 TS_LINEAR, ///< linear searching forward, till EOF 133 TS_BINARY, ///< binary searching 134 TS_SKIP_BACK, ///< skipping backwards 135 TS_STEP_FORWARD, ///< stepping forwards 136 } tagsearch_state_T; 137 138 /// Binary search file offsets in a tags file 139 typedef struct { 140 off_T low_offset; ///< offset for first char of first line that 141 ///< could match 142 off_T high_offset; ///< offset of char after last line that could 143 ///< match 144 off_T curr_offset; ///< Current file offset in search range 145 off_T curr_offset_used; ///< curr_offset used when skipping back 146 off_T match_offset; ///< Where the binary search found a tag 147 int low_char; ///< first char at low_offset 148 int high_char; ///< first char at high_offset 149 } tagsearch_info_T; 150 151 /// Return values used when matching tags against a pattern. 152 typedef enum { 153 TAG_MATCH_SUCCESS = 1, 154 TAG_MATCH_FAIL, 155 TAG_MATCH_STOP, 156 TAG_MATCH_NEXT, 157 } tagmatch_status_T; 158 159 /// Arguments used for matching tags read from a tags file against a pattern. 160 typedef struct { 161 int matchoff; ///< tag match offset 162 bool match_re; ///< true if the tag matches a regexp 163 bool match_no_ic; ///< true if the tag matches with case 164 bool has_re; ///< regular expression used 165 bool sortic; ///< tags file sorted ignoring case (foldcase) 166 bool sort_error; ///< tags file not sorted 167 } findtags_match_args_T; 168 169 /// State information used during a tag search 170 typedef struct { 171 tagsearch_state_T state; ///< tag search state 172 bool stop_searching; ///< stop when match found or error 173 pat_T *orgpat; ///< holds unconverted pattern info 174 char *lbuf; ///< line buffer 175 int lbuf_size; ///< length of lbuf 176 char *tag_fname; ///< name of the tag file 177 FILE *fp; ///< current tags file pointer 178 int flags; ///< flags used for tag search 179 int tag_file_sorted; ///< !_TAG_FILE_SORTED value 180 bool get_searchpat; ///< used for 'showfulltag' 181 bool help_only; ///< only search for help tags 182 bool did_open; ///< did open a tag file 183 int mincount; ///< MAXCOL: find all matches 184 ///< other: minimal number of matches 185 bool linear; ///< do a linear search 186 vimconv_T vimconv; 187 char help_lang[3]; ///< lang of current tags file 188 int help_pri; ///< help language priority 189 char *help_lang_find; ///< lang to be found 190 bool is_txt; ///< flag of file extension 191 int match_count; ///< number of matches found 192 garray_T ga_match[MT_COUNT]; ///< stores matches in sequence 193 hashtab_T ht_match[MT_COUNT]; ///< stores matches by key 194 } findtags_state_T; 195 196 #include "tag.c.generated.h" 197 198 static const char e_tag_stack_empty[] 199 = N_("E73: Tag stack empty"); 200 static const char e_tag_not_found_str[] 201 = N_("E426: Tag not found: %s"); 202 static const char e_at_bottom_of_tag_stack[] 203 = N_("E555: At bottom of tag stack"); 204 static const char e_at_top_of_tag_stack[] 205 = N_("E556: At top of tag stack"); 206 static const char e_cannot_modify_tag_stack_within_tagfunc[] 207 = N_("E986: Cannot modify the tag stack within tagfunc"); 208 static const char e_invalid_return_value_from_tagfunc[] 209 = N_("E987: Invalid return value from tagfunc"); 210 static const char e_window_unexpectedly_close_while_searching_for_tags[] 211 = N_("E1299: Window unexpectedly closed while searching for tags"); 212 213 static char *tagmatchname = NULL; // name of last used tag 214 215 // Tag for preview window is remembered separately, to avoid messing up the 216 // normal tagstack. 217 static taggy_T ptag_entry = { NULL, INIT_FMARK, 0, 0, NULL }; 218 219 static bool tfu_in_use = false; // disallow recursive call of tagfunc 220 static Callback tfu_cb; // 'tagfunc' callback function 221 222 // Used instead of NUL to separate tag fields in the growarrays. 223 #define TAG_SEP 0x02 224 225 /// Reads the 'tagfunc' option value and convert that to a callback value. 226 /// Invoked when the 'tagfunc' option is set. The option value can be a name of 227 /// a function (string), or function(<name>) or funcref(<name>) or a lambda. 228 const char *did_set_tagfunc(optset_T *args) 229 { 230 buf_T *buf = (buf_T *)args->os_buf; 231 int retval; 232 233 if (args->os_flags & OPT_LOCAL) { 234 retval = option_set_callback_func(args->os_newval.string.data, &buf->b_tfu_cb); 235 } else { 236 retval = option_set_callback_func(args->os_newval.string.data, &tfu_cb); 237 if (retval == OK && !(args->os_flags & OPT_GLOBAL)) { 238 set_buflocal_tfu_callback(buf); 239 } 240 } 241 242 return retval == FAIL ? e_invarg : NULL; 243 } 244 245 #if defined(EXITFREE) 246 void free_tagfunc_option(void) 247 { 248 callback_free(&tfu_cb); 249 } 250 #endif 251 252 /// Mark the global 'tagfunc' callback with "copyID" so that it is not garbage 253 /// collected. 254 bool set_ref_in_tagfunc(int copyID) 255 { 256 return set_ref_in_callback(&tfu_cb, copyID, NULL, NULL); 257 } 258 259 /// Copy the global 'tagfunc' callback function to the buffer-local 'tagfunc' 260 /// callback for 'buf'. 261 void set_buflocal_tfu_callback(buf_T *buf) 262 { 263 callback_free(&buf->b_tfu_cb); 264 if (tfu_cb.type != kCallbackNone) { 265 callback_copy(&buf->b_tfu_cb, &tfu_cb); 266 } 267 } 268 269 /// Jump to tag; handling of tag commands and tag stack 270 /// 271 /// *tag != NUL: ":tag {tag}", jump to new tag, add to tag stack 272 /// 273 /// type == DT_TAG: ":tag [tag]", jump to newer position or same tag again 274 /// type == DT_HELP: like DT_TAG, but don't use regexp. 275 /// type == DT_POP: ":pop" or CTRL-T, jump to old position 276 /// type == DT_NEXT: jump to next match of same tag 277 /// type == DT_PREV: jump to previous match of same tag 278 /// type == DT_FIRST: jump to first match of same tag 279 /// type == DT_LAST: jump to last match of same tag 280 /// type == DT_SELECT: ":tselect [tag]", select tag from a list of all matches 281 /// type == DT_JUMP: ":tjump [tag]", jump to tag or select tag from a list 282 /// type == DT_LTAG: use location list for displaying tag matches 283 /// type == DT_FREE: free cached matches 284 /// 285 /// @param tag tag (pattern) to jump to 286 /// @param forceit :ta with ! 287 /// @param verbose print "tag not found" message 288 void do_tag(char *tag, int type, int count, int forceit, bool verbose) 289 { 290 taggy_T *tagstack = curwin->w_tagstack; 291 int tagstackidx = curwin->w_tagstackidx; 292 int tagstacklen = curwin->w_tagstacklen; 293 int cur_match = 0; 294 int cur_fnum = curbuf->b_fnum; 295 int oldtagstackidx = tagstackidx; 296 int prevtagstackidx = tagstackidx; 297 bool new_tag = false; 298 bool no_regexp = false; 299 int error_cur_match = 0; 300 bool save_pos = false; 301 fmark_T saved_fmark; 302 int new_num_matches; 303 char **new_matches; 304 bool use_tagstack; 305 bool skip_msg = false; 306 char *buf_ffname = curbuf->b_ffname; // name for priority computation 307 bool use_tfu = true; 308 char *tofree = NULL; 309 310 // remember the matches for the last used tag 311 static int num_matches = 0; 312 static int max_num_matches = 0; // limit used for match search 313 static char **matches = NULL; 314 static int flags; 315 316 #ifdef EXITFREE 317 if (type == DT_FREE) { 318 // remove the list of matches 319 FreeWild(num_matches, matches); 320 num_matches = 0; 321 return; 322 } 323 #endif 324 325 if (tfu_in_use) { 326 emsg(_(e_cannot_modify_tag_stack_within_tagfunc)); 327 return; 328 } 329 330 if (postponed_split == 0 && !check_can_set_curbuf_forceit(forceit)) { 331 return; 332 } 333 334 if (type == DT_HELP) { 335 type = DT_TAG; 336 no_regexp = true; 337 use_tfu = false; 338 } 339 340 int prev_num_matches = num_matches; 341 free_string_option(nofile_fname); 342 nofile_fname = NULL; 343 344 clearpos(&saved_fmark.mark); // shutup gcc 4.0 345 saved_fmark.fnum = 0; 346 saved_fmark.view = (fmarkv_T)INIT_FMARKV; 347 348 // Don't add a tag to the tagstack if 'tagstack' has been reset. 349 assert(tag != NULL); 350 if (!p_tgst && *tag != NUL) { 351 use_tagstack = false; 352 new_tag = true; 353 if (g_do_tagpreview != 0) { 354 tagstack_clear_entry(&ptag_entry); 355 ptag_entry.tagname = xstrdup(tag); 356 } 357 } else { 358 if (g_do_tagpreview != 0) { 359 use_tagstack = false; 360 } else { 361 use_tagstack = true; 362 } 363 364 // new pattern, add to the tag stack 365 if (*tag != NUL 366 && (type == DT_TAG || type == DT_SELECT || type == DT_JUMP 367 || type == DT_LTAG)) { 368 if (g_do_tagpreview != 0) { 369 if (ptag_entry.tagname != NULL 370 && strcmp(ptag_entry.tagname, tag) == 0) { 371 // Jumping to same tag: keep the current match, so that 372 // the CursorHold autocommand example works. 373 cur_match = ptag_entry.cur_match; 374 cur_fnum = ptag_entry.cur_fnum; 375 } else { 376 tagstack_clear_entry(&ptag_entry); 377 ptag_entry.tagname = xstrdup(tag); 378 } 379 } else { 380 // If the last used entry is not at the top, delete all tag 381 // stack entries above it. 382 while (tagstackidx < tagstacklen) { 383 tagstack_clear_entry(&tagstack[--tagstacklen]); 384 } 385 386 // if the tagstack is full: remove oldest entry 387 if (++tagstacklen > TAGSTACKSIZE) { 388 tagstacklen = TAGSTACKSIZE; 389 tagstack_clear_entry(&tagstack[0]); 390 for (int i = 1; i < tagstacklen; i++) { 391 tagstack[i - 1] = tagstack[i]; 392 } 393 tagstack[--tagstackidx].user_data = NULL; 394 } 395 396 // put the tag name in the tag stack 397 tagstack[tagstackidx].tagname = xstrdup(tag); 398 399 curwin->w_tagstacklen = tagstacklen; 400 401 save_pos = true; // save the cursor position below 402 } 403 404 new_tag = true; 405 } else { 406 if (g_do_tagpreview != 0 ? ptag_entry.tagname == NULL 407 : tagstacklen == 0) { 408 // empty stack 409 emsg(_(e_tag_stack_empty)); 410 goto end_do_tag; 411 } 412 413 if (type == DT_POP) { // go to older position 414 const bool old_KeyTyped = KeyTyped; 415 if ((tagstackidx -= count) < 0) { 416 emsg(_(e_at_bottom_of_tag_stack)); 417 if (tagstackidx + count == 0) { 418 // We did [num]^T from the bottom of the stack 419 tagstackidx = 0; 420 goto end_do_tag; 421 } 422 // We weren't at the bottom of the stack, so jump all the 423 // way to the bottom now. 424 tagstackidx = 0; 425 } else if (tagstackidx >= tagstacklen) { // count == 0? 426 emsg(_(e_at_top_of_tag_stack)); 427 goto end_do_tag; 428 } 429 430 // Make a copy of the fmark, autocommands may invalidate the 431 // tagstack before it's used. 432 saved_fmark = tagstack[tagstackidx].fmark; 433 if (saved_fmark.fnum != curbuf->b_fnum) { 434 // Jump to other file. If this fails (e.g. because the 435 // file was changed) keep original position in tag stack. 436 if (buflist_getfile(saved_fmark.fnum, saved_fmark.mark.lnum, 437 GETF_SETMARK, forceit) == FAIL) { 438 tagstackidx = oldtagstackidx; // back to old posn 439 goto end_do_tag; 440 } 441 // A BufReadPost autocommand may jump to the '" mark, but 442 // we don't what that here. 443 curwin->w_cursor.lnum = saved_fmark.mark.lnum; 444 } else { 445 setpcmark(); 446 curwin->w_cursor.lnum = saved_fmark.mark.lnum; 447 } 448 curwin->w_cursor.col = saved_fmark.mark.col; 449 curwin->w_set_curswant = true; 450 if (jop_flags & kOptJopFlagView) { 451 mark_view_restore(&saved_fmark); 452 } 453 check_cursor(curwin); 454 if ((fdo_flags & kOptFdoFlagTag) && old_KeyTyped) { 455 foldOpenCursor(); 456 } 457 458 // remove the old list of matches 459 FreeWild(num_matches, matches); 460 num_matches = 0; 461 tag_freematch(); 462 goto end_do_tag; 463 } 464 465 if (type == DT_TAG 466 || type == DT_LTAG) { 467 if (g_do_tagpreview != 0) { 468 cur_match = ptag_entry.cur_match; 469 cur_fnum = ptag_entry.cur_fnum; 470 } else { 471 // ":tag" (no argument): go to newer pattern 472 save_pos = true; // save the cursor position below 473 if ((tagstackidx += count - 1) >= tagstacklen) { 474 // Beyond the last one, just give an error message and 475 // go to the last one. Don't store the cursor 476 // position. 477 tagstackidx = tagstacklen - 1; 478 emsg(_(e_at_top_of_tag_stack)); 479 save_pos = false; 480 } else if (tagstackidx < 0) { // must have been count == 0 481 emsg(_(e_at_bottom_of_tag_stack)); 482 tagstackidx = 0; 483 goto end_do_tag; 484 } 485 cur_match = tagstack[tagstackidx].cur_match; 486 cur_fnum = tagstack[tagstackidx].cur_fnum; 487 } 488 new_tag = true; 489 } else { // go to other matching tag 490 // Save index for when selection is cancelled. 491 prevtagstackidx = tagstackidx; 492 493 if (g_do_tagpreview != 0) { 494 cur_match = ptag_entry.cur_match; 495 cur_fnum = ptag_entry.cur_fnum; 496 } else { 497 if (--tagstackidx < 0) { 498 tagstackidx = 0; 499 } 500 cur_match = tagstack[tagstackidx].cur_match; 501 cur_fnum = tagstack[tagstackidx].cur_fnum; 502 } 503 switch (type) { 504 case DT_FIRST: 505 cur_match = count - 1; break; 506 case DT_SELECT: 507 case DT_JUMP: 508 case DT_LAST: 509 cur_match = MAXCOL - 1; break; 510 case DT_NEXT: 511 cur_match += count; break; 512 case DT_PREV: 513 cur_match -= count; break; 514 } 515 if (cur_match >= MAXCOL) { 516 cur_match = MAXCOL - 1; 517 } else if (cur_match < 0) { 518 emsg(_("E425: Cannot go before first matching tag")); 519 skip_msg = true; 520 cur_match = 0; 521 cur_fnum = curbuf->b_fnum; 522 } 523 } 524 } 525 526 if (g_do_tagpreview != 0) { 527 if (type != DT_SELECT && type != DT_JUMP) { 528 ptag_entry.cur_match = cur_match; 529 ptag_entry.cur_fnum = cur_fnum; 530 } 531 } else { 532 // For ":tag [arg]" or ":tselect" remember position before the jump. 533 saved_fmark = tagstack[tagstackidx].fmark; 534 if (save_pos) { 535 tagstack[tagstackidx].fmark.mark = curwin->w_cursor; 536 tagstack[tagstackidx].fmark.fnum = curbuf->b_fnum; 537 tagstack[tagstackidx].fmark.view = mark_view_make(curwin->w_topline, curwin->w_cursor); 538 } 539 540 // Curwin will change in the call to jumpto_tag() if ":stag" was 541 // used or an autocommand jumps to another window; store value of 542 // tagstackidx now. 543 curwin->w_tagstackidx = tagstackidx; 544 if (type != DT_SELECT && type != DT_JUMP) { 545 curwin->w_tagstack[tagstackidx].cur_match = cur_match; 546 curwin->w_tagstack[tagstackidx].cur_fnum = cur_fnum; 547 } 548 } 549 } 550 551 // When not using the current buffer get the name of buffer "cur_fnum". 552 // Makes sure that the tag order doesn't change when using a remembered 553 // position for "cur_match". 554 if (cur_fnum != curbuf->b_fnum) { 555 buf_T *buf = buflist_findnr(cur_fnum); 556 557 if (buf != NULL) { 558 buf_ffname = buf->b_ffname; 559 } 560 } 561 562 // Repeat searching for tags, when a file has not been found. 563 while (true) { 564 char *name; 565 566 // When desired match not found yet, try to find it (and others). 567 if (use_tagstack) { 568 // make a copy, the tagstack may change in 'tagfunc' 569 name = xstrdup(tagstack[tagstackidx].tagname); 570 xfree(tofree); 571 tofree = name; 572 } else if (g_do_tagpreview != 0) { 573 name = ptag_entry.tagname; 574 } else { 575 name = tag; 576 } 577 bool other_name = (tagmatchname == NULL || strcmp(tagmatchname, name) != 0); 578 if (new_tag 579 || (cur_match >= num_matches && max_num_matches != MAXCOL) 580 || other_name) { 581 if (other_name) { 582 xfree(tagmatchname); 583 tagmatchname = xstrdup(name); 584 } 585 586 if (type == DT_SELECT || type == DT_JUMP 587 || type == DT_LTAG) { 588 cur_match = MAXCOL - 1; 589 } 590 max_num_matches = type == DT_TAG ? MAXCOL : cur_match + 1; 591 592 // when the argument starts with '/', use it as a regexp 593 if (!no_regexp && *name == '/') { 594 flags = TAG_REGEXP; 595 name++; 596 } else { 597 flags = TAG_NOIC; 598 } 599 600 flags |= verbose ? TAG_VERBOSE : 0; 601 flags |= !use_tfu ? TAG_NO_TAGFUNC : 0; 602 603 if (find_tags(name, &new_num_matches, &new_matches, flags, 604 max_num_matches, buf_ffname) == OK 605 && new_num_matches < max_num_matches) { 606 max_num_matches = MAXCOL; // If less than max_num_matches 607 // found: all matches found. 608 } 609 610 // A tag function may do anything, which may cause various 611 // information to become invalid. At least check for the tagstack 612 // to still be the same. 613 if (tagstack != curwin->w_tagstack) { 614 emsg(_(e_window_unexpectedly_close_while_searching_for_tags)); 615 FreeWild(new_num_matches, new_matches); 616 break; 617 } 618 619 // If there already were some matches for the same name, move them 620 // to the start. Avoids that the order changes when using 621 // ":tnext" and jumping to another file. 622 if (!new_tag && !other_name) { 623 int idx = 0; 624 tagptrs_T tagp, tagp2; 625 626 // Find the position of each old match in the new list. Need 627 // to use parse_match() to find the tag line. 628 for (int j = 0; j < num_matches; j++) { 629 parse_match(matches[j], &tagp); 630 for (int i = idx; i < new_num_matches; i++) { 631 parse_match(new_matches[i], &tagp2); 632 if (strcmp(tagp.tagname, tagp2.tagname) == 0) { 633 char *p = new_matches[i]; 634 for (int k = i; k > idx; k--) { 635 new_matches[k] = new_matches[k - 1]; 636 } 637 new_matches[idx++] = p; 638 break; 639 } 640 } 641 } 642 } 643 FreeWild(num_matches, matches); 644 num_matches = new_num_matches; 645 matches = new_matches; 646 } 647 648 if (num_matches <= 0) { 649 if (verbose) { 650 semsg(_(e_tag_not_found_str), name); 651 } 652 g_do_tagpreview = 0; 653 } else { 654 bool ask_for_selection = false; 655 656 if (type == DT_TAG && *tag != NUL) { 657 // If a count is supplied to the ":tag <name>" command, then 658 // jump to count'th matching tag. 659 cur_match = count > 0 ? count - 1 : 0; 660 } else if (type == DT_SELECT || (type == DT_JUMP && num_matches > 1)) { 661 print_tag_list(new_tag, use_tagstack, num_matches, matches); 662 ask_for_selection = true; 663 } else if (type == DT_LTAG) { 664 if (add_llist_tags(tag, num_matches, matches) == FAIL) { 665 goto end_do_tag; 666 } 667 668 cur_match = 0; // Jump to the first tag 669 } 670 671 if (ask_for_selection) { 672 // Ask to select a tag from the list. 673 int i = prompt_for_input(NULL, 0, false, NULL); 674 if (i <= 0 || i > num_matches || got_int) { 675 // no valid choice: don't change anything 676 if (use_tagstack) { 677 tagstack[tagstackidx].fmark = saved_fmark; 678 tagstackidx = prevtagstackidx; 679 } 680 break; 681 } 682 cur_match = i - 1; 683 } 684 685 if (cur_match >= num_matches) { 686 // Avoid giving this error when a file wasn't found and we're 687 // looking for a match in another file, which wasn't found. 688 // There will be an emsg("file doesn't exist") below then. 689 if ((type == DT_NEXT || type == DT_FIRST) 690 && nofile_fname == NULL) { 691 if (num_matches == 1) { 692 emsg(_("E427: There is only one matching tag")); 693 } else { 694 emsg(_("E428: Cannot go beyond last matching tag")); 695 } 696 skip_msg = true; 697 } 698 cur_match = num_matches - 1; 699 } 700 if (use_tagstack) { 701 tagptrs_T tagp2; 702 703 tagstack[tagstackidx].cur_match = cur_match; 704 tagstack[tagstackidx].cur_fnum = cur_fnum; 705 706 // store user-provided data originating from tagfunc 707 if (use_tfu && parse_match(matches[cur_match], &tagp2) == OK 708 && tagp2.user_data) { 709 XFREE_CLEAR(tagstack[tagstackidx].user_data); 710 tagstack[tagstackidx].user_data = 711 xmemdupz(tagp2.user_data, (size_t)(tagp2.user_data_end - tagp2.user_data)); 712 } 713 714 tagstackidx++; 715 } else if (g_do_tagpreview != 0) { 716 ptag_entry.cur_match = cur_match; 717 ptag_entry.cur_fnum = cur_fnum; 718 } 719 720 // Only when going to try the next match, report that the previous 721 // file didn't exist. Otherwise an emsg() is given below. 722 if (nofile_fname != NULL && error_cur_match != cur_match) { 723 smsg(0, _("File \"%s\" does not exist"), nofile_fname); 724 } 725 726 bool ic = (matches[cur_match][0] & MT_IC_OFF); 727 if (type != DT_TAG && type != DT_SELECT && type != DT_JUMP 728 && (num_matches > 1 || ic) 729 && !skip_msg) { 730 // Give an indication of the number of matching tags 731 snprintf(IObuff, sizeof(IObuff), _("tag %d of %d%s"), 732 cur_match + 1, 733 num_matches, 734 max_num_matches != MAXCOL ? _(" or more") : ""); 735 if (ic) { 736 xstrlcat(IObuff, _(" Using tag with different case!"), IOSIZE); 737 } 738 if ((num_matches > prev_num_matches || new_tag) 739 && num_matches > 1) { 740 msg(IObuff, ic ? HLF_W : 0); 741 msg_scroll = true; // Don't overwrite this message. 742 } else { 743 give_warning(IObuff, ic, true); 744 } 745 if (ic && !msg_scrolled && msg_silent == 0) { 746 msg_delay(1007, true); 747 } 748 } 749 750 // Let the SwapExists event know what tag we are jumping to. 751 vim_snprintf(IObuff, IOSIZE, ":ta %s\r", name); 752 set_vim_var_string(VV_SWAPCOMMAND, IObuff, -1); 753 754 // Jump to the desired match. 755 int i = jumpto_tag(matches[cur_match], forceit, true); 756 757 set_vim_var_string(VV_SWAPCOMMAND, NULL, -1); 758 759 if (i == NOTAGFILE) { 760 // File not found: try again with another matching tag 761 if ((type == DT_PREV && cur_match > 0) 762 || ((type == DT_TAG || type == DT_NEXT 763 || type == DT_FIRST) 764 && (max_num_matches != MAXCOL 765 || cur_match < num_matches - 1))) { 766 error_cur_match = cur_match; 767 if (use_tagstack) { 768 tagstackidx--; 769 } 770 if (type == DT_PREV) { 771 cur_match--; 772 } else { 773 type = DT_NEXT; 774 cur_match++; 775 } 776 continue; 777 } 778 semsg(_("E429: File \"%s\" does not exist"), nofile_fname); 779 } else { 780 // We may have jumped to another window, check that 781 // tagstackidx is still valid. 782 if (use_tagstack && tagstackidx > curwin->w_tagstacklen) { 783 tagstackidx = curwin->w_tagstackidx; 784 } 785 } 786 } 787 break; 788 } 789 790 end_do_tag: 791 // Only store the new index when using the tagstack and it's valid. 792 if (use_tagstack && tagstackidx <= curwin->w_tagstacklen) { 793 curwin->w_tagstackidx = tagstackidx; 794 } 795 postponed_split = 0; // don't split next time 796 g_do_tagpreview = 0; // don't do tag preview next time 797 xfree(tofree); 798 } 799 800 // List all the matching tags. 801 static void print_tag_list(bool new_tag, bool use_tagstack, int num_matches, char **matches) 802 { 803 taggy_T *tagstack = curwin->w_tagstack; 804 int tagstackidx = curwin->w_tagstackidx; 805 tagptrs_T tagp; 806 807 // Assume that the first match indicates how long the tags can 808 // be, and align the file names to that. 809 parse_match(matches[0], &tagp); 810 int taglen = MAX((int)(tagp.tagname_end - tagp.tagname + 2), 18); 811 if (taglen > Columns - 25) { 812 taglen = MAXCOL; 813 } 814 if (msg_col == 0) { 815 msg_didout = false; // overwrite previous message 816 } 817 msg_ext_set_kind("confirm"); 818 msg_start(); 819 msg_puts_hl(_(" # pri kind tag"), HLF_T, false); 820 msg_clr_eos(); 821 taglen_advance(taglen); 822 msg_puts_hl(_("file\n"), HLF_T, false); 823 824 for (int i = 0; i < num_matches && !got_int; i++) { 825 parse_match(matches[i], &tagp); 826 if (!new_tag && ( 827 (g_do_tagpreview != 0 828 && i == ptag_entry.cur_match) 829 || (use_tagstack 830 && i == tagstack[tagstackidx].cur_match))) { 831 *IObuff = '>'; 832 } else { 833 *IObuff = ' '; 834 } 835 vim_snprintf(IObuff + 1, IOSIZE - 1, 836 "%2d %s ", i + 1, 837 mt_names[matches[i][0] & MT_MASK]); 838 msg_puts(IObuff); 839 if (tagp.tagkind != NULL) { 840 msg_outtrans_len(tagp.tagkind, (int)(tagp.tagkind_end - tagp.tagkind), 0, false); 841 } 842 msg_advance(13); 843 msg_outtrans_len(tagp.tagname, (int)(tagp.tagname_end - tagp.tagname), HLF_T, false); 844 msg_putchar(' '); 845 taglen_advance(taglen); 846 847 // Find out the actual file name. If it is long, truncate 848 // it and put "..." in the middle 849 const char *p = tag_full_fname(&tagp); 850 if (p != NULL) { 851 msg_outtrans(p, HLF_D, false); 852 XFREE_CLEAR(p); 853 } 854 if (msg_col > 0) { 855 msg_putchar('\n'); 856 } 857 if (got_int) { 858 break; 859 } 860 msg_advance(15); 861 862 // print any extra fields 863 const char *command_end = tagp.command_end; 864 if (command_end != NULL) { 865 p = command_end + 3; 866 while (*p && *p != '\r' && *p != '\n') { 867 while (*p == TAB) { 868 p++; 869 } 870 871 // skip "file:" without a value (static tag) 872 if (strncmp(p, "file:", 5) == 0 && ascii_isspace(p[5])) { 873 p += 5; 874 continue; 875 } 876 // skip "kind:<kind>" and "<kind>" 877 if (p == tagp.tagkind 878 || (p + 5 == tagp.tagkind 879 && strncmp(p, "kind:", 5) == 0)) { 880 p = tagp.tagkind_end; 881 continue; 882 } 883 // print all other extra fields 884 int hl_id = HLF_CM; 885 while (*p && *p != '\r' && *p != '\n') { 886 if (msg_col + ptr2cells(p) >= Columns) { 887 msg_putchar('\n'); 888 if (got_int) { 889 break; 890 } 891 msg_advance(15); 892 } 893 p = msg_outtrans_one(p, hl_id, false); 894 if (*p == TAB) { 895 msg_puts_hl(" ", hl_id, false); 896 break; 897 } 898 if (*p == ':') { 899 hl_id = 0; 900 } 901 } 902 } 903 if (msg_col > 15) { 904 msg_putchar('\n'); 905 if (got_int) { 906 break; 907 } 908 msg_advance(15); 909 } 910 } else { 911 for (p = tagp.command; 912 *p && *p != '\r' && *p != '\n'; 913 p++) {} 914 command_end = p; 915 } 916 917 // Put the info (in several lines) at column 15. 918 // Don't display "/^" and "?^". 919 p = tagp.command; 920 if (*p == '/' || *p == '?') { 921 p++; 922 if (*p == '^') { 923 p++; 924 } 925 } 926 // Remove leading whitespace from pattern 927 while (p != command_end && ascii_isspace(*p)) { 928 p++; 929 } 930 931 while (p != command_end) { 932 if (msg_col + (*p == TAB ? 1 : ptr2cells(p)) > Columns) { 933 msg_putchar('\n'); 934 } 935 if (got_int) { 936 break; 937 } 938 msg_advance(15); 939 940 // skip backslash used for escaping a command char or 941 // a backslash 942 if (*p == '\\' && (*(p + 1) == *tagp.command 943 || *(p + 1) == '\\')) { 944 p++; 945 } 946 947 if (*p == TAB) { 948 msg_putchar(' '); 949 p++; 950 } else { 951 p = msg_outtrans_one(p, 0, false); 952 } 953 954 // don't display the "$/;\"" and "$?;\"" 955 if (p == command_end - 2 && *p == '$' 956 && *(p + 1) == *tagp.command) { 957 break; 958 } 959 // don't display matching '/' or '?' 960 if (p == command_end - 1 && *p == *tagp.command 961 && (*p == '/' || *p == '?')) { 962 break; 963 } 964 } 965 if (msg_col && (!ui_has(kUIMessages) || i < num_matches - 1)) { 966 msg_putchar('\n'); 967 } 968 os_breakcheck(); 969 } 970 if (got_int) { 971 got_int = false; // only stop the listing 972 } 973 } 974 975 /// Add the matching tags to the location list for the current 976 /// window. 977 static int add_llist_tags(char *tag, int num_matches, char **matches) 978 { 979 char tag_name[128 + 1]; 980 tagptrs_T tagp; 981 982 char *fname = xmalloc(MAXPATHL + 1); 983 char *cmd = xmalloc(CMDBUFFSIZE + 1); 984 list_T *list = tv_list_alloc(0); 985 986 for (int i = 0; i < num_matches; i++) { 987 dict_T *dict; 988 989 parse_match(matches[i], &tagp); 990 991 // Save the tag name 992 int len = MIN((int)(tagp.tagname_end - tagp.tagname), 128); 993 xmemcpyz(tag_name, tagp.tagname, (size_t)len); 994 tag_name[len] = NUL; 995 996 // Save the tag file name 997 char *p = tag_full_fname(&tagp); 998 if (p == NULL) { 999 continue; 1000 } 1001 xstrlcpy(fname, p, MAXPATHL); 1002 XFREE_CLEAR(p); 1003 1004 // Get the line number or the search pattern used to locate 1005 // the tag. 1006 linenr_T lnum = 0; 1007 if (isdigit((uint8_t)(*tagp.command))) { 1008 // Line number is used to locate the tag 1009 lnum = atoi(tagp.command); 1010 } else { 1011 // Search pattern is used to locate the tag 1012 1013 // Locate the end of the command 1014 char *cmd_start = tagp.command; 1015 char *cmd_end = tagp.command_end; 1016 if (cmd_end == NULL) { 1017 for (p = tagp.command; 1018 *p && *p != '\r' && *p != '\n'; p++) {} 1019 cmd_end = p; 1020 } 1021 1022 // Now, cmd_end points to the character after the 1023 // command. Adjust it to point to the last 1024 // character of the command. 1025 cmd_end--; 1026 1027 // Skip the '/' and '?' characters at the 1028 // beginning and end of the search pattern. 1029 if (*cmd_start == '/' || *cmd_start == '?') { 1030 cmd_start++; 1031 } 1032 1033 if (*cmd_end == '/' || *cmd_end == '?') { 1034 cmd_end--; 1035 } 1036 1037 len = 0; 1038 cmd[0] = NUL; 1039 1040 // If "^" is present in the tag search pattern, then 1041 // copy it first. 1042 if (*cmd_start == '^') { 1043 STRCPY(cmd, "^"); 1044 cmd_start++; 1045 len++; 1046 } 1047 1048 // Precede the tag pattern with \V to make it very 1049 // nomagic. 1050 strcat(cmd, "\\V"); 1051 len += 2; 1052 1053 int cmd_len = MIN((int)(cmd_end - cmd_start + 1), CMDBUFFSIZE - 5); 1054 snprintf(cmd + len, (size_t)(CMDBUFFSIZE + 1 - len), 1055 "%.*s", cmd_len, cmd_start); 1056 len += cmd_len; 1057 1058 if (cmd[len - 1] == '$') { 1059 // Replace '$' at the end of the search pattern 1060 // with '\$' 1061 cmd[len - 1] = '\\'; 1062 cmd[len] = '$'; 1063 len++; 1064 } 1065 1066 cmd[len] = NUL; 1067 } 1068 1069 dict = tv_dict_alloc(); 1070 tv_list_append_dict(list, dict); 1071 1072 tv_dict_add_str(dict, S_LEN("text"), tag_name); 1073 tv_dict_add_str(dict, S_LEN("filename"), fname); 1074 tv_dict_add_nr(dict, S_LEN("lnum"), lnum); 1075 if (lnum == 0) { 1076 tv_dict_add_str(dict, S_LEN("pattern"), cmd); 1077 } 1078 } 1079 1080 vim_snprintf(IObuff, IOSIZE, "ltag %s", tag); 1081 set_errorlist(curwin, list, ' ', IObuff, NULL); 1082 1083 tv_list_free(list); 1084 XFREE_CLEAR(fname); 1085 XFREE_CLEAR(cmd); 1086 1087 return OK; 1088 } 1089 1090 // Free cached tags. 1091 void tag_freematch(void) 1092 { 1093 XFREE_CLEAR(tagmatchname); 1094 } 1095 1096 static void taglen_advance(int l) 1097 { 1098 if (l == MAXCOL) { 1099 msg_putchar('\n'); 1100 msg_advance(24); 1101 } else { 1102 msg_advance(13 + l); 1103 } 1104 } 1105 1106 // Print the tag stack 1107 void do_tags(exarg_T *eap) 1108 { 1109 taggy_T *tagstack = curwin->w_tagstack; 1110 int tagstackidx = curwin->w_tagstackidx; 1111 int tagstacklen = curwin->w_tagstacklen; 1112 1113 // Highlight title 1114 msg_puts_title(_("\n # TO tag FROM line in file/text")); 1115 for (int i = 0; i < tagstacklen; i++) { 1116 if (tagstack[i].tagname != NULL) { 1117 char *name = fm_getname(&(tagstack[i].fmark), 30); 1118 if (name == NULL) { // file name not available 1119 continue; 1120 } 1121 1122 msg_putchar('\n'); 1123 vim_snprintf(IObuff, IOSIZE, "%c%2d %2d %-15s %5" PRIdLINENR " ", 1124 i == tagstackidx ? '>' : ' ', 1125 i + 1, 1126 tagstack[i].cur_match + 1, 1127 tagstack[i].tagname, 1128 tagstack[i].fmark.mark.lnum); 1129 msg_outtrans(IObuff, 0, false); 1130 msg_outtrans(name, tagstack[i].fmark.fnum == curbuf->b_fnum ? HLF_D : 0, false); 1131 xfree(name); 1132 } 1133 } 1134 if (tagstackidx == tagstacklen) { // idx at top of stack 1135 msg_puts("\n>"); 1136 } 1137 } 1138 1139 // Compare two strings, for length "len", ignoring case the ASCII way. 1140 // return 0 for match, < 0 for smaller, > 0 for bigger 1141 // Make sure case is folded to uppercase in comparison (like for 'sort -f') 1142 static int tag_strnicmp(char *s1, char *s2, size_t len) 1143 { 1144 while (len > 0) { 1145 int i = TOUPPER_ASC((uint8_t)(*s1)) - TOUPPER_ASC((uint8_t)(*s2)); 1146 if (i != 0) { 1147 return i; // this character different 1148 } 1149 if (*s1 == NUL) { 1150 break; // strings match until NUL 1151 } 1152 s1++; 1153 s2++; 1154 len--; 1155 } 1156 return 0; // strings match 1157 } 1158 1159 // Extract info from the tag search pattern "pats->pat". 1160 static void prepare_pats(pat_T *pats, bool has_re) 1161 { 1162 pats->head = pats->pat; 1163 pats->headlen = pats->len; 1164 if (has_re) { 1165 // When the pattern starts with '^' or "\\<", binary searching can be 1166 // used (much faster). 1167 if (pats->pat[0] == '^') { 1168 pats->head = pats->pat + 1; 1169 } else if (pats->pat[0] == '\\' && pats->pat[1] == '<') { 1170 pats->head = pats->pat + 2; 1171 } 1172 if (pats->head == pats->pat) { 1173 pats->headlen = 0; 1174 } else { 1175 for (pats->headlen = 0; pats->head[pats->headlen] != NUL; pats->headlen++) { 1176 if (vim_strchr(magic_isset() ? ".[~*\\$" : "\\$", 1177 (uint8_t)pats->head[pats->headlen]) != NULL) { 1178 break; 1179 } 1180 } 1181 } 1182 if (p_tl != 0 && pats->headlen > p_tl) { // adjust for 'taglength' 1183 pats->headlen = (int)p_tl; 1184 } 1185 } 1186 1187 if (has_re) { 1188 pats->regmatch.regprog = vim_regcomp(pats->pat, magic_isset() ? RE_MAGIC : 0); 1189 } else { 1190 pats->regmatch.regprog = NULL; 1191 } 1192 } 1193 1194 /// Call the user-defined function to generate a list of tags used by 1195 /// find_tags(). 1196 /// 1197 /// Return OK if at least 1 tag has been successfully found, 1198 /// NOTDONE if the function returns v:null, and FAIL otherwise. 1199 /// 1200 /// @param pat pattern supplied to the user-defined function 1201 /// @param ga the tags will be placed here 1202 /// @param match_count here the number of tags found will be placed 1203 /// @param flags flags from find_tags (TAG_*) 1204 /// @param buf_ffname name of buffer for priority 1205 static int find_tagfunc_tags(char *pat, garray_T *ga, int *match_count, int flags, char *buf_ffname) 1206 { 1207 int ntags = 0; 1208 typval_T args[4]; 1209 typval_T rettv; 1210 char flagString[4]; 1211 taggy_T *tag = NULL; 1212 1213 if (curwin->w_tagstacklen > 0) { 1214 if (curwin->w_tagstackidx == curwin->w_tagstacklen) { 1215 tag = &curwin->w_tagstack[curwin->w_tagstackidx - 1]; 1216 } else { 1217 tag = &curwin->w_tagstack[curwin->w_tagstackidx]; 1218 } 1219 } 1220 1221 if (*curbuf->b_p_tfu == NUL || curbuf->b_tfu_cb.type == kCallbackNone) { 1222 return FAIL; 1223 } 1224 1225 args[0].v_type = VAR_STRING; 1226 args[0].vval.v_string = pat; 1227 args[1].v_type = VAR_STRING; 1228 args[1].vval.v_string = flagString; 1229 1230 // create 'info' dict argument 1231 dict_T *const d = tv_dict_alloc_lock(VAR_FIXED); 1232 if (!(flags & TAG_INS_COMP) && tag != NULL && tag->user_data != NULL) { 1233 tv_dict_add_str(d, S_LEN("user_data"), tag->user_data); 1234 } 1235 if (buf_ffname != NULL) { 1236 tv_dict_add_str(d, S_LEN("buf_ffname"), buf_ffname); 1237 } 1238 1239 d->dv_refcount++; 1240 args[2].v_type = VAR_DICT; 1241 args[2].vval.v_dict = d; 1242 1243 args[3].v_type = VAR_UNKNOWN; 1244 1245 vim_snprintf(flagString, sizeof(flagString), 1246 "%s%s%s", 1247 g_tag_at_cursor ? "c" : "", 1248 flags & TAG_INS_COMP ? "i" : "", 1249 flags & TAG_REGEXP ? "r" : ""); 1250 1251 pos_T save_pos = curwin->w_cursor; 1252 int result = callback_call(&curbuf->b_tfu_cb, 3, args, &rettv); 1253 curwin->w_cursor = save_pos; // restore the cursor position 1254 check_cursor(curwin); // make sure cursor position is valid 1255 d->dv_refcount--; 1256 1257 if (result == FAIL) { 1258 return FAIL; 1259 } 1260 if (rettv.v_type == VAR_SPECIAL && rettv.vval.v_special == kSpecialVarNull) { 1261 tv_clear(&rettv); 1262 return NOTDONE; 1263 } 1264 if (rettv.v_type != VAR_LIST || !rettv.vval.v_list) { 1265 tv_clear(&rettv); 1266 emsg(_(e_invalid_return_value_from_tagfunc)); 1267 return FAIL; 1268 } 1269 list_T *taglist = rettv.vval.v_list; 1270 1271 TV_LIST_ITER_CONST(taglist, li, { 1272 char *res_name; 1273 char *res_fname; 1274 char *res_cmd; 1275 char *res_kind; 1276 bool has_extra = false; 1277 int name_only = flags & TAG_NAMES; 1278 1279 if (TV_LIST_ITEM_TV(li)->v_type != VAR_DICT) { 1280 emsg(_(e_invalid_return_value_from_tagfunc)); 1281 break; 1282 } 1283 1284 size_t len = 2; 1285 res_name = NULL; 1286 res_fname = NULL; 1287 res_cmd = NULL; 1288 res_kind = NULL; 1289 1290 TV_DICT_ITER(TV_LIST_ITEM_TV(li)->vval.v_dict, di, { 1291 const char *dict_key = di->di_key; 1292 typval_T *tv = &di->di_tv; 1293 1294 if (tv->v_type != VAR_STRING || tv->vval.v_string == NULL) { 1295 continue; 1296 } 1297 1298 len += strlen(tv->vval.v_string) + 1; // Space for "\tVALUE" 1299 if (!strcmp(dict_key, "name")) { 1300 res_name = tv->vval.v_string; 1301 continue; 1302 } 1303 if (!strcmp(dict_key, "filename")) { 1304 res_fname = tv->vval.v_string; 1305 continue; 1306 } 1307 if (!strcmp(dict_key, "cmd")) { 1308 res_cmd = tv->vval.v_string; 1309 continue; 1310 } 1311 has_extra = true; 1312 if (!strcmp(dict_key, "kind")) { 1313 res_kind = tv->vval.v_string; 1314 continue; 1315 } 1316 // Other elements will be stored as "\tKEY:VALUE" 1317 // Allocate space for the key and the colon 1318 len += strlen(dict_key) + 1; 1319 }); 1320 1321 if (has_extra) { 1322 len += 2; // need space for ;" 1323 } 1324 1325 if (!res_name || !res_fname || !res_cmd) { 1326 emsg(_(e_invalid_return_value_from_tagfunc)); 1327 break; 1328 } 1329 1330 char *const mfp = name_only ? xstrdup(res_name) : xmalloc(len + 2); 1331 1332 if (!name_only) { 1333 char *p = mfp; 1334 1335 *p++ = MT_GL_OTH + 1; // mtt 1336 *p++ = TAG_SEP; // no tag file name 1337 1338 STRCPY(p, res_name); 1339 p += strlen(p); 1340 1341 *p++ = TAB; 1342 STRCPY(p, res_fname); 1343 p += strlen(p); 1344 1345 *p++ = TAB; 1346 STRCPY(p, res_cmd); 1347 p += strlen(p); 1348 1349 if (has_extra) { 1350 STRCPY(p, ";\""); 1351 p += strlen(p); 1352 1353 if (res_kind) { 1354 *p++ = TAB; 1355 STRCPY(p, res_kind); 1356 p += strlen(p); 1357 } 1358 1359 TV_DICT_ITER(TV_LIST_ITEM_TV(li)->vval.v_dict, di, { 1360 const char *dict_key = di->di_key; 1361 typval_T *tv = &di->di_tv; 1362 if (tv->v_type != VAR_STRING || tv->vval.v_string == NULL) { 1363 continue; 1364 } 1365 1366 if (!strcmp(dict_key, "name")) { 1367 continue; 1368 } 1369 if (!strcmp(dict_key, "filename")) { 1370 continue; 1371 } 1372 if (!strcmp(dict_key, "cmd")) { 1373 continue; 1374 } 1375 if (!strcmp(dict_key, "kind")) { 1376 continue; 1377 } 1378 1379 *p++ = TAB; 1380 STRCPY(p, dict_key); 1381 p += strlen(p); 1382 STRCPY(p, ":"); 1383 p += strlen(p); 1384 STRCPY(p, tv->vval.v_string); 1385 p += strlen(p); 1386 }); 1387 } 1388 } 1389 1390 // Add all matches because tagfunc should do filtering. 1391 ga_grow(ga, 1); 1392 ((char **)(ga->ga_data))[ga->ga_len++] = (char *)mfp; 1393 ntags++; 1394 result = OK; 1395 }); 1396 1397 tv_clear(&rettv); 1398 1399 *match_count = ntags; 1400 return result; 1401 } 1402 1403 /// Initialize the state used by find_tags() 1404 static void findtags_state_init(findtags_state_T *st, char *pat, int flags, int mincount) 1405 { 1406 st->tag_fname = xmalloc(MAXPATHL + 1); 1407 st->fp = NULL; 1408 st->orgpat = xmalloc(sizeof(pat_T)); 1409 st->orgpat->pat = pat; 1410 st->orgpat->len = (int)strlen(pat); 1411 st->orgpat->regmatch.regprog = NULL; 1412 st->flags = flags; 1413 st->tag_file_sorted = NUL; 1414 st->help_lang_find = NULL; 1415 st->is_txt = false; 1416 st->did_open = false; 1417 st->help_only = (flags & TAG_HELP); 1418 st->get_searchpat = false; 1419 st->help_lang[0] = NUL; 1420 st->help_pri = 0; 1421 st->mincount = mincount; 1422 st->lbuf_size = LSIZE; 1423 st->lbuf = xmalloc((size_t)st->lbuf_size); 1424 st->match_count = 0; 1425 st->stop_searching = false; 1426 1427 for (int mtt = 0; mtt < MT_COUNT; mtt++) { 1428 ga_init(&st->ga_match[mtt], sizeof(char *), 100); 1429 hash_init(&st->ht_match[mtt]); 1430 } 1431 } 1432 1433 /// Free the state used by find_tags() 1434 static void findtags_state_free(findtags_state_T *st) 1435 { 1436 xfree(st->tag_fname); 1437 xfree(st->lbuf); 1438 vim_regfree(st->orgpat->regmatch.regprog); 1439 xfree(st->orgpat); 1440 } 1441 1442 /// Initialize the language and priority used for searching tags in a Vim help 1443 /// file. 1444 /// Returns true to process the help file for tags and false to skip the file. 1445 static bool findtags_in_help_init(findtags_state_T *st) 1446 { 1447 int i; 1448 1449 // Keep "en" as the language if the file extension is ".txt" 1450 if (st->is_txt) { 1451 STRCPY(st->help_lang, "en"); 1452 } else { 1453 // Prefer help tags according to 'helplang'. Put the two-letter 1454 // language name in help_lang[]. 1455 i = (int)strlen(st->tag_fname); 1456 if (i > 3 && st->tag_fname[i - 3] == '-') { 1457 xmemcpyz(st->help_lang, st->tag_fname + i - 2, 2); 1458 } else { 1459 STRCPY(st->help_lang, "en"); 1460 } 1461 } 1462 // When searching for a specific language skip tags files for other 1463 // languages. 1464 if (st->help_lang_find != NULL 1465 && STRICMP(st->help_lang, st->help_lang_find) != 0) { 1466 return false; 1467 } 1468 1469 // For CTRL-] in a help file prefer a match with the same language. 1470 if ((st->flags & TAG_KEEP_LANG) 1471 && st->help_lang_find == NULL 1472 && curbuf->b_fname != NULL 1473 && (i = (int)strlen(curbuf->b_fname)) > 4 1474 && curbuf->b_fname[i - 1] == 'x' 1475 && curbuf->b_fname[i - 4] == '.' 1476 && STRNICMP(curbuf->b_fname + i - 3, st->help_lang, 2) == 0) { 1477 st->help_pri = 0; 1478 } else { 1479 st->help_pri = 1; 1480 char *s; 1481 for (s = p_hlg; *s != NUL; s++) { 1482 if (STRNICMP(s, st->help_lang, 2) == 0) { 1483 break; 1484 } 1485 st->help_pri++; 1486 if ((s = vim_strchr(s, ',')) == NULL) { 1487 break; 1488 } 1489 } 1490 if (s == NULL || *s == NUL) { 1491 // Language not in 'helplang': use last, prefer English, unless 1492 // found already. 1493 st->help_pri++; 1494 if (STRICMP(st->help_lang, "en") != 0) { 1495 st->help_pri++; 1496 } 1497 } 1498 } 1499 1500 return true; 1501 } 1502 1503 /// Use the function set in 'tagfunc' (if configured and enabled) to get the 1504 /// tags. 1505 /// Return OK if at least 1 tag has been successfully found, NOTDONE if the 1506 /// 'tagfunc' is not used, still executing or the 'tagfunc' returned v:null and 1507 /// FAIL otherwise. 1508 static int findtags_apply_tfu(findtags_state_T *st, char *pat, char *buf_ffname) 1509 { 1510 const bool use_tfu = ((st->flags & TAG_NO_TAGFUNC) == 0); 1511 1512 if (!use_tfu || tfu_in_use || *curbuf->b_p_tfu == NUL) { 1513 return NOTDONE; 1514 } 1515 1516 tfu_in_use = true; 1517 int retval = find_tagfunc_tags(pat, st->ga_match, &st->match_count, 1518 st->flags, buf_ffname); 1519 tfu_in_use = false; 1520 1521 return retval; 1522 } 1523 1524 /// Read the next line from a tags file. 1525 /// Returns TAGS_READ_SUCCESS if a tags line is successfully read and should be 1526 /// processed. 1527 /// Returns TAGS_READ_EOF if the end of file is reached. 1528 /// Returns TAGS_READ_IGNORE if the current line should be ignored (used when 1529 /// reached end of a emacs included tags file) 1530 static tags_read_status_T findtags_get_next_line(findtags_state_T *st, tagsearch_info_T *sinfo_p) 1531 { 1532 bool eof; 1533 1534 // For binary search: compute the next offset to use. 1535 if (st->state == TS_BINARY) { 1536 off_T offset = sinfo_p->low_offset + ((sinfo_p->high_offset - sinfo_p->low_offset) / 2); 1537 if (offset == sinfo_p->curr_offset) { 1538 return TAGS_READ_EOF; // End the binary search without a match. 1539 } else { 1540 sinfo_p->curr_offset = offset; 1541 } 1542 } else if (st->state == TS_SKIP_BACK) { 1543 // Skipping back (after a match during binary search). 1544 sinfo_p->curr_offset -= st->lbuf_size * 2; 1545 if (sinfo_p->curr_offset < 0) { 1546 sinfo_p->curr_offset = 0; 1547 fseek(st->fp, 0, SEEK_SET); 1548 st->state = TS_STEP_FORWARD; 1549 } 1550 } 1551 1552 // When jumping around in the file, first read a line to find the 1553 // start of the next line. 1554 if (st->state == TS_BINARY || st->state == TS_SKIP_BACK) { 1555 // Adjust the search file offset to the correct position 1556 sinfo_p->curr_offset_used = sinfo_p->curr_offset; 1557 vim_ignored = vim_fseek(st->fp, sinfo_p->curr_offset, SEEK_SET); 1558 eof = vim_fgets(st->lbuf, st->lbuf_size, st->fp); 1559 if (!eof && sinfo_p->curr_offset != 0) { 1560 sinfo_p->curr_offset = vim_ftell(st->fp); 1561 if (sinfo_p->curr_offset == sinfo_p->high_offset) { 1562 // oops, gone a bit too far; try from low offset 1563 vim_ignored = vim_fseek(st->fp, sinfo_p->low_offset, SEEK_SET); 1564 sinfo_p->curr_offset = sinfo_p->low_offset; 1565 } 1566 eof = vim_fgets(st->lbuf, st->lbuf_size, st->fp); 1567 } 1568 // skip empty and blank lines 1569 while (!eof && vim_isblankline(st->lbuf)) { 1570 sinfo_p->curr_offset = vim_ftell(st->fp); 1571 eof = vim_fgets(st->lbuf, st->lbuf_size, st->fp); 1572 } 1573 if (eof) { 1574 // Hit end of file. Skip backwards. 1575 st->state = TS_SKIP_BACK; 1576 sinfo_p->match_offset = vim_ftell(st->fp); 1577 sinfo_p->curr_offset = sinfo_p->curr_offset_used; 1578 return TAGS_READ_IGNORE; 1579 } 1580 } else { 1581 // Not jumping around in the file: Read the next line. 1582 1583 // skip empty and blank lines 1584 do { 1585 eof = vim_fgets(st->lbuf, st->lbuf_size, st->fp); 1586 } while (!eof && vim_isblankline(st->lbuf)); 1587 1588 if (eof) { 1589 return TAGS_READ_EOF; 1590 } 1591 } 1592 1593 return TAGS_READ_SUCCESS; 1594 } 1595 1596 /// Parse a tags file header line in "st->lbuf". 1597 /// Returns true if the current line in st->lbuf is not a tags header line and 1598 /// should be parsed as a regular tag line. Returns false if the line is a 1599 /// header line and the next header line should be read. 1600 static bool findtags_hdr_parse(findtags_state_T *st) 1601 { 1602 // Header lines in a tags file start with "!_TAG_" 1603 if (strncmp(st->lbuf, "!_TAG_", 6) != 0) { 1604 // Non-header item before the header, e.g. "!" itself. 1605 return true; 1606 } 1607 1608 // Process the header line. 1609 if (strncmp(st->lbuf, "!_TAG_FILE_SORTED\t", 18) == 0) { 1610 st->tag_file_sorted = (uint8_t)st->lbuf[18]; 1611 } 1612 if (strncmp(st->lbuf, "!_TAG_FILE_ENCODING\t", 20) == 0) { 1613 // Prepare to convert every line from the specified encoding to 1614 // 'encoding'. 1615 char *p; 1616 for (p = st->lbuf + 20; *p > ' ' && *p < 127; p++) {} 1617 *p = NUL; 1618 convert_setup(&st->vimconv, st->lbuf + 20, p_enc); 1619 } 1620 1621 // Read the next line. Unrecognized flags are ignored. 1622 return false; 1623 } 1624 1625 /// Handler to initialize the state when starting to process a new tags file. 1626 /// Called in the TS_START state when finding tags from a tags file. 1627 /// Returns true if the line read from the tags file should be parsed and 1628 /// false if the line should be ignored. 1629 static bool findtags_start_state_handler(findtags_state_T *st, bool *sortic, 1630 tagsearch_info_T *sinfo_p) 1631 { 1632 const bool noic = (st->flags & TAG_NOIC); 1633 1634 // The header ends when the line sorts below "!_TAG_". When case is 1635 // folded lower case letters sort before "_". 1636 if (strncmp(st->lbuf, "!_TAG_", 6) <= 0 1637 || (st->lbuf[0] == '!' && ASCII_ISLOWER(st->lbuf[1]))) { 1638 return findtags_hdr_parse(st); 1639 } 1640 1641 // Headers ends. 1642 1643 // When there is no tag head, or ignoring case, need to do a 1644 // linear search. 1645 // When no "!_TAG_" is found, default to binary search. If 1646 // the tag file isn't sorted, the second loop will find it. 1647 // When "!_TAG_FILE_SORTED" found: start binary search if 1648 // flag set. 1649 if (st->linear) { 1650 st->state = TS_LINEAR; 1651 } else if (st->tag_file_sorted == NUL) { 1652 st->state = TS_BINARY; 1653 } else if (st->tag_file_sorted == '1') { 1654 st->state = TS_BINARY; 1655 } else if (st->tag_file_sorted == '2') { 1656 st->state = TS_BINARY; 1657 *sortic = true; 1658 st->orgpat->regmatch.rm_ic = (p_ic || !noic); 1659 } else { 1660 st->state = TS_LINEAR; 1661 } 1662 1663 if (st->state == TS_BINARY && st->orgpat->regmatch.rm_ic && !*sortic) { 1664 // Binary search won't work for ignoring case, use linear 1665 // search. 1666 st->linear = true; 1667 st->state = TS_LINEAR; 1668 } 1669 1670 // When starting a binary search, get the size of the file and 1671 // compute the first offset. 1672 if (st->state == TS_BINARY) { 1673 if (vim_fseek(st->fp, 0, SEEK_END) != 0) { 1674 // can't seek, don't use binary search 1675 st->state = TS_LINEAR; 1676 } else { 1677 // Get the tag file size. 1678 // Don't use lseek(), it doesn't work 1679 // properly on MacOS Catalina. 1680 const off_T filesize = vim_ftell(st->fp); 1681 vim_ignored = vim_fseek(st->fp, 0, SEEK_SET); 1682 1683 // Calculate the first read offset in the file. Start 1684 // the search in the middle of the file. 1685 sinfo_p->low_offset = 0; 1686 sinfo_p->low_char = 0; 1687 sinfo_p->high_offset = filesize; 1688 sinfo_p->curr_offset = 0; 1689 sinfo_p->high_char = 0xff; 1690 } 1691 return false; 1692 } 1693 1694 return true; 1695 } 1696 1697 /// Parse a tag line read from a tags file. 1698 /// Also compares the tag name in "tagpp->tagname" with a search pattern in 1699 /// "st->orgpat->head" as a quick check if the tag may match. 1700 /// Returns: 1701 /// - TAG_MATCH_SUCCESS if the tag may match 1702 /// - TAG_MATCH_FAIL if the tag doesn't match 1703 /// - TAG_MATCH_NEXT to look for the next matching tag (used in a binary search) 1704 /// - TAG_MATCH_STOP if all the tags are processed without a match. 1705 /// Uses the values in "margs" for doing the comparison. 1706 static tagmatch_status_T findtags_parse_line(findtags_state_T *st, tagptrs_T *tagpp, 1707 findtags_match_args_T *margs, 1708 tagsearch_info_T *sinfo_p) 1709 { 1710 int status; 1711 1712 // Figure out where the different strings are in this line. 1713 // For "normal" tags: Do a quick check if the tag matches. 1714 // This speeds up tag searching a lot! 1715 if (st->orgpat->headlen) { 1716 CLEAR_FIELD(*tagpp); 1717 tagpp->tagname = st->lbuf; 1718 tagpp->tagname_end = vim_strchr(st->lbuf, TAB); 1719 if (tagpp->tagname_end == NULL) { 1720 // Corrupted tag line. 1721 return TAG_MATCH_FAIL; 1722 } 1723 1724 // Skip this line if the length of the tag is different and 1725 // there is no regexp, or the tag is too short. 1726 int cmplen = (int)(tagpp->tagname_end - tagpp->tagname); 1727 if (p_tl != 0 && cmplen > p_tl) { // adjust for 'taglength' 1728 cmplen = (int)p_tl; 1729 } 1730 if ((st->flags & TAG_REGEXP) && st->orgpat->headlen < cmplen) { 1731 cmplen = st->orgpat->headlen; 1732 } else if (st->state == TS_LINEAR && st->orgpat->headlen != cmplen) { 1733 return TAG_MATCH_NEXT; 1734 } 1735 1736 if (st->state == TS_BINARY) { 1737 int tagcmp; 1738 // Simplistic check for unsorted tags file. 1739 int i = (uint8_t)tagpp->tagname[0]; 1740 if (margs->sortic) { 1741 i = TOUPPER_ASC(tagpp->tagname[0]); 1742 } 1743 if (i < sinfo_p->low_char || i > sinfo_p->high_char) { 1744 margs->sort_error = true; 1745 } 1746 1747 // Compare the current tag with the searched tag. 1748 if (margs->sortic) { 1749 tagcmp = tag_strnicmp(tagpp->tagname, st->orgpat->head, 1750 (size_t)cmplen); 1751 } else { 1752 tagcmp = strncmp(tagpp->tagname, st->orgpat->head, (size_t)cmplen); 1753 } 1754 1755 // A match with a shorter tag means to search forward. 1756 // A match with a longer tag means to search backward. 1757 if (tagcmp == 0) { 1758 if (cmplen < st->orgpat->headlen) { 1759 tagcmp = -1; 1760 } else if (cmplen > st->orgpat->headlen) { 1761 tagcmp = 1; 1762 } 1763 } 1764 1765 if (tagcmp == 0) { 1766 // We've located the tag, now skip back and search 1767 // forward until the first matching tag is found. 1768 st->state = TS_SKIP_BACK; 1769 sinfo_p->match_offset = sinfo_p->curr_offset; 1770 return TAG_MATCH_NEXT; 1771 } 1772 if (tagcmp < 0) { 1773 sinfo_p->curr_offset = vim_ftell(st->fp); 1774 if (sinfo_p->curr_offset < sinfo_p->high_offset) { 1775 sinfo_p->low_offset = sinfo_p->curr_offset; 1776 if (margs->sortic) { 1777 sinfo_p->low_char = TOUPPER_ASC(tagpp->tagname[0]); 1778 } else { 1779 sinfo_p->low_char = (uint8_t)tagpp->tagname[0]; 1780 } 1781 return TAG_MATCH_NEXT; 1782 } 1783 } 1784 if (tagcmp > 0 && sinfo_p->curr_offset != sinfo_p->high_offset) { 1785 sinfo_p->high_offset = sinfo_p->curr_offset; 1786 if (margs->sortic) { 1787 sinfo_p->high_char = TOUPPER_ASC(tagpp->tagname[0]); 1788 } else { 1789 sinfo_p->high_char = (uint8_t)tagpp->tagname[0]; 1790 } 1791 return TAG_MATCH_NEXT; 1792 } 1793 1794 // No match yet and are at the end of the binary search. 1795 return TAG_MATCH_STOP; 1796 } else if (st->state == TS_SKIP_BACK) { 1797 assert(cmplen >= 0); 1798 if (mb_strnicmp(tagpp->tagname, st->orgpat->head, (size_t)cmplen) != 0) { 1799 st->state = TS_STEP_FORWARD; 1800 } else { 1801 // Have to skip back more. Restore the curr_offset 1802 // used, otherwise we get stuck at a long line. 1803 sinfo_p->curr_offset = sinfo_p->curr_offset_used; 1804 } 1805 return TAG_MATCH_NEXT; 1806 } else if (st->state == TS_STEP_FORWARD) { 1807 assert(cmplen >= 0); 1808 if (mb_strnicmp(tagpp->tagname, st->orgpat->head, (size_t)cmplen) != 0) { 1809 return ((off_T)vim_ftell(st->fp) > sinfo_p->match_offset) 1810 ? TAG_MATCH_STOP // past last match 1811 : TAG_MATCH_NEXT; // before first match 1812 } 1813 } else { 1814 // skip this match if it can't match 1815 assert(cmplen >= 0); 1816 if (mb_strnicmp(tagpp->tagname, st->orgpat->head, (size_t)cmplen) != 0) { 1817 return TAG_MATCH_NEXT; 1818 } 1819 } 1820 1821 // Can be a matching tag, isolate the file name and command. 1822 tagpp->fname = tagpp->tagname_end + 1; 1823 tagpp->fname_end = vim_strchr(tagpp->fname, TAB); 1824 if (tagpp->fname_end == NULL) { 1825 status = FAIL; 1826 } else { 1827 tagpp->command = tagpp->fname_end + 1; 1828 status = OK; 1829 } 1830 } else { 1831 status = parse_tag_line(st->lbuf, tagpp); 1832 } 1833 1834 return status == FAIL 1835 ? TAG_MATCH_FAIL 1836 : TAG_MATCH_SUCCESS; 1837 } 1838 1839 /// Initialize the structure used for tag matching. 1840 static void findtags_matchargs_init(findtags_match_args_T *margs, int flags) 1841 { 1842 margs->matchoff = 0; // match offset 1843 margs->match_re = false; // match with regexp 1844 margs->match_no_ic = false; // matches with case 1845 margs->has_re = (flags & TAG_REGEXP); // regexp used 1846 margs->sortic = false; // tag file sorted in nocase 1847 margs->sort_error = false; // tags file not sorted 1848 } 1849 1850 /// Compares the tag name in "tagpp->tagname" with a search pattern in 1851 /// "st->orgpat->pat". 1852 /// Returns true if the tag matches, false if the tag doesn't match. 1853 /// Uses the values in "margs" for doing the comparison. 1854 static bool findtags_match_tag(findtags_state_T *st, tagptrs_T *tagpp, findtags_match_args_T *margs) 1855 { 1856 bool match = false; 1857 1858 // First try matching with the pattern literally (also when it is 1859 // a regexp). 1860 int cmplen = (int)(tagpp->tagname_end - tagpp->tagname); 1861 if (p_tl != 0 && cmplen > p_tl) { // adjust for 'taglength' 1862 cmplen = (int)p_tl; 1863 } 1864 // if tag length does not match, don't try comparing 1865 if (st->orgpat->len != cmplen) { 1866 match = false; 1867 } else { 1868 if (st->orgpat->regmatch.rm_ic) { 1869 assert(cmplen >= 0); 1870 match = mb_strnicmp(tagpp->tagname, st->orgpat->pat, (size_t)cmplen) == 0; 1871 if (match) { 1872 margs->match_no_ic = strncmp(tagpp->tagname, st->orgpat->pat, (size_t)cmplen) == 0; 1873 } 1874 } else { 1875 match = strncmp(tagpp->tagname, st->orgpat->pat, (size_t)cmplen) == 0; 1876 } 1877 } 1878 1879 // Has a regexp: Also find tags matching regexp. 1880 margs->match_re = false; 1881 if (!match && st->orgpat->regmatch.regprog != NULL) { 1882 char cc = *tagpp->tagname_end; 1883 *tagpp->tagname_end = NUL; 1884 match = vim_regexec(&st->orgpat->regmatch, tagpp->tagname, 0); 1885 if (match) { 1886 margs->matchoff = (int)(st->orgpat->regmatch.startp[0] - tagpp->tagname); 1887 if (st->orgpat->regmatch.rm_ic) { 1888 st->orgpat->regmatch.rm_ic = false; 1889 margs->match_no_ic = vim_regexec(&st->orgpat->regmatch, 1890 tagpp->tagname, 0); 1891 st->orgpat->regmatch.rm_ic = true; 1892 } 1893 } 1894 *tagpp->tagname_end = cc; 1895 margs->match_re = true; 1896 } 1897 1898 return match; 1899 } 1900 1901 /// Convert the encoding of a line read from a tags file in "st->lbuf". 1902 /// Converting the pattern from 'enc' to the tags file encoding doesn't work, 1903 /// because characters are not recognized. The converted line is saved in 1904 /// st->lbuf. 1905 static void findtags_string_convert(findtags_state_T *st) 1906 { 1907 char *conv_line = string_convert(&st->vimconv, st->lbuf, NULL); 1908 if (conv_line == NULL) { 1909 return; 1910 } 1911 1912 // Copy or swap lbuf and conv_line. 1913 int len = (int)strlen(conv_line) + 1; 1914 if (len > st->lbuf_size) { 1915 xfree(st->lbuf); 1916 st->lbuf = conv_line; 1917 st->lbuf_size = len; 1918 } else { 1919 STRCPY(st->lbuf, conv_line); 1920 xfree(conv_line); 1921 } 1922 } 1923 1924 /// Add a matching tag found in a tags file to st->ht_match and st->ga_match. 1925 static void findtags_add_match(findtags_state_T *st, tagptrs_T *tagpp, findtags_match_args_T *margs, 1926 char *buf_ffname, hash_T *hash) 1927 { 1928 const bool name_only = (st->flags & TAG_NAMES); 1929 size_t len = 0; 1930 size_t mfp_size = 0; 1931 char *mfp; 1932 1933 // Decide in which array to store this match. 1934 bool is_current = test_for_current(tagpp->fname, tagpp->fname_end, 1935 st->tag_fname, buf_ffname); 1936 1937 // current tag line is static 1938 bool is_static = test_for_static(tagpp); 1939 1940 // Decide in which of the sixteen tables to store this match. 1941 int mtt = is_static 1942 ? is_current ? MT_ST_CUR : MT_ST_OTH 1943 : is_current ? MT_GL_CUR : MT_GL_OTH; 1944 1945 if (st->orgpat->regmatch.rm_ic && !margs->match_no_ic) { 1946 mtt += MT_IC_OFF; 1947 } 1948 if (margs->match_re) { 1949 mtt += MT_RE_OFF; 1950 } 1951 1952 // Add the found match in ht_match[mtt] and ga_match[mtt]. 1953 // Store the info we need later, which depends on the kind of 1954 // tags we are dealing with. 1955 if (st->help_only) { 1956 #define ML_EXTRA 3 1957 // Append the help-heuristic number after the tagname, for 1958 // sorting it later. The heuristic is ignored for 1959 // detecting duplicates. 1960 // The format is {tagname}@{lang}NUL{heuristic}NUL 1961 *tagpp->tagname_end = NUL; 1962 len = (size_t)(tagpp->tagname_end - tagpp->tagname); 1963 mfp_size = sizeof(char) + len + 10 + ML_EXTRA + 1; 1964 mfp = xmalloc(mfp_size); 1965 1966 char *p = mfp; 1967 STRCPY(p, tagpp->tagname); 1968 p[len] = '@'; 1969 STRCPY(p + len + 1, st->help_lang); 1970 snprintf(p + len + 1 + ML_EXTRA, mfp_size - (len + 1 + ML_EXTRA), "%06d", 1971 help_heuristic(tagpp->tagname, 1972 margs->match_re ? margs->matchoff : 0, 1973 !margs->match_no_ic) + st->help_pri); 1974 1975 *tagpp->tagname_end = TAB; 1976 } else if (name_only) { 1977 if (st->get_searchpat) { 1978 char *temp_end = tagpp->command; 1979 1980 if (*temp_end == '/') { 1981 while (*temp_end && *temp_end != '\r' 1982 && *temp_end != '\n' 1983 && *temp_end != '$') { 1984 temp_end++; 1985 } 1986 } 1987 1988 if (tagpp->command + 2 < temp_end) { 1989 len = (size_t)(temp_end - tagpp->command - 2); 1990 mfp = xmalloc(len + 2); 1991 xmemcpyz(mfp, tagpp->command + 2, len); 1992 } else { 1993 mfp = NULL; 1994 } 1995 st->get_searchpat = false; 1996 } else { 1997 len = (size_t)(tagpp->tagname_end - tagpp->tagname); 1998 mfp = xmalloc(sizeof(char) + len + 1); 1999 xmemcpyz(mfp, tagpp->tagname, len); 2000 2001 // if wanted, re-read line to get long form too 2002 if (State & MODE_INSERT) { 2003 st->get_searchpat = p_sft; 2004 } 2005 } 2006 } else { 2007 size_t tag_fname_len = strlen(st->tag_fname); 2008 // Save the tag in a buffer. 2009 // Use 0x02 to separate fields (Can't use NUL, because the 2010 // hash key is terminated by NUL). 2011 // Emacs tag: <mtt><tag_fname><0x02><ebuf><0x02><lbuf><NUL> 2012 // other tag: <mtt><tag_fname><0x02><0x02><lbuf><NUL> 2013 // without Emacs tags: <mtt><tag_fname><0x02><lbuf><NUL> 2014 // Here <mtt> is the "mtt" value plus 1 to avoid NUL. 2015 len = tag_fname_len + strlen(st->lbuf) + 3; 2016 mfp = xmalloc(sizeof(char) + len + 1); 2017 char *p = mfp; 2018 p[0] = (char)(mtt + 1); 2019 STRCPY(p + 1, st->tag_fname); 2020 #ifdef BACKSLASH_IN_FILENAME 2021 // Ignore differences in slashes, avoid adding 2022 // both path/file and path\file. 2023 slash_adjust(p + 1); 2024 #endif 2025 p[tag_fname_len + 1] = TAG_SEP; 2026 char *s = p + 1 + tag_fname_len + 1; 2027 STRCPY(s, st->lbuf); 2028 } 2029 2030 if (mfp != NULL) { 2031 hashitem_T *hi; 2032 2033 // Don't add identical matches. 2034 // "mfp" is used as a hash key, there is a NUL byte to end 2035 // the part that matters for comparing, more bytes may 2036 // follow after it. E.g. help tags store the priority 2037 // after the NUL. 2038 *hash = hash_hash(mfp); 2039 hi = hash_lookup(&st->ht_match[mtt], mfp, strlen(mfp), *hash); 2040 if (HASHITEM_EMPTY(hi)) { 2041 hash_add_item(&st->ht_match[mtt], hi, mfp, *hash); 2042 GA_APPEND(char *, &st->ga_match[mtt], mfp); 2043 st->match_count++; 2044 } else { 2045 // duplicate tag, drop it 2046 xfree(mfp); 2047 } 2048 } 2049 } 2050 2051 /// Read and get all the tags from file st->tag_fname. 2052 /// Sets "st->stop_searching" to true to stop searching for additional tags. 2053 static void findtags_get_all_tags(findtags_state_T *st, findtags_match_args_T *margs, 2054 char *buf_ffname) 2055 { 2056 tagptrs_T tagp; 2057 tagsearch_info_T search_info; 2058 hash_T hash = 0; 2059 2060 // This is only to avoid a compiler warning for using search_info 2061 // uninitialised. 2062 CLEAR_FIELD(search_info); 2063 2064 // Read and parse the lines in the file one by one 2065 while (true) { 2066 // check for CTRL-C typed, more often when jumping around 2067 if (st->state == TS_BINARY || st->state == TS_SKIP_BACK) { 2068 line_breakcheck(); 2069 } else { 2070 fast_breakcheck(); 2071 } 2072 if ((st->flags & TAG_INS_COMP)) { // Double brackets for gcc 2073 ins_compl_check_keys(30, false); 2074 } 2075 if (got_int || ins_compl_interrupted()) { 2076 st->stop_searching = true; 2077 break; 2078 } 2079 // When mincount is TAG_MANY, stop when enough matches have been 2080 // found (for completion). 2081 if (st->mincount == TAG_MANY && st->match_count >= TAG_MANY) { 2082 st->stop_searching = true; 2083 break; 2084 } 2085 if (st->get_searchpat) { 2086 goto line_read_in; 2087 } 2088 2089 int retval = (int)findtags_get_next_line(st, &search_info); 2090 if (retval == TAGS_READ_IGNORE) { 2091 continue; 2092 } 2093 if (retval == TAGS_READ_EOF) { 2094 break; 2095 } 2096 2097 line_read_in: 2098 2099 if (st->vimconv.vc_type != CONV_NONE) { 2100 findtags_string_convert(st); 2101 } 2102 2103 // When still at the start of the file, check for Emacs tags file 2104 // format, and for "not sorted" flag. 2105 if (st->state == TS_START) { 2106 if (!findtags_start_state_handler(st, &margs->sortic, &search_info)) { 2107 continue; 2108 } 2109 } 2110 2111 // When the line is too long the NUL will not be in the 2112 // last-but-one byte (see vim_fgets()). 2113 // Has been reported for Mozilla JS with extremely long names. 2114 // In that case we need to increase lbuf_size. 2115 if (st->lbuf[st->lbuf_size - 2] != NUL) { 2116 st->lbuf_size *= 2; 2117 xfree(st->lbuf); 2118 st->lbuf = xmalloc((size_t)st->lbuf_size); 2119 2120 if (st->state == TS_STEP_FORWARD || st->state == TS_LINEAR) { 2121 // Seek to the same position to read the same line again 2122 vim_ignored = vim_fseek(st->fp, search_info.curr_offset, SEEK_SET); 2123 } 2124 // this will try the same thing again, make sure the offset is 2125 // different 2126 search_info.curr_offset = 0; 2127 continue; 2128 } 2129 2130 retval = (int)findtags_parse_line(st, &tagp, margs, &search_info); 2131 if (retval == TAG_MATCH_NEXT) { 2132 continue; 2133 } 2134 if (retval == TAG_MATCH_STOP) { 2135 break; 2136 } 2137 if (retval == TAG_MATCH_FAIL) { 2138 semsg(_("E431: Format error in tags file \"%s\""), st->tag_fname); 2139 semsg(_("Before byte %" PRId64), (int64_t)vim_ftell(st->fp)); 2140 st->stop_searching = true; 2141 return; 2142 } 2143 2144 // If a match is found, add it to ht_match[] and ga_match[]. 2145 if (findtags_match_tag(st, &tagp, margs)) { 2146 findtags_add_match(st, &tagp, margs, buf_ffname, &hash); 2147 } 2148 } // forever 2149 } 2150 2151 /// Search for tags matching "st->orgpat.pat" in the "st->tag_fname" tags file. 2152 /// Information needed to search for the tags is in the "st" state structure. 2153 /// The matching tags are returned in "st". If an error is encountered, then 2154 /// "st->stop_searching" is set to true. 2155 static void findtags_in_file(findtags_state_T *st, int flags, char *buf_ffname) 2156 { 2157 findtags_match_args_T margs; 2158 2159 st->vimconv.vc_type = CONV_NONE; 2160 st->tag_file_sorted = NUL; 2161 st->fp = NULL; 2162 findtags_matchargs_init(&margs, st->flags); 2163 2164 // A file that doesn't exist is silently ignored. Only when not a 2165 // single file is found, an error message is given (further on). 2166 if (curbuf->b_help) { 2167 if (!findtags_in_help_init(st)) { 2168 return; 2169 } 2170 } 2171 2172 st->fp = os_fopen(st->tag_fname, "r"); 2173 if (st->fp == NULL) { 2174 return; 2175 } 2176 2177 if (p_verbose >= 5) { 2178 verbose_enter(); 2179 smsg(0, _("Searching tags file %s"), st->tag_fname); 2180 verbose_leave(); 2181 } 2182 st->did_open = true; // remember that we found at least one file 2183 2184 st->state = TS_START; // we're at the start of the file 2185 2186 // Read and parse the lines in the file one by one 2187 findtags_get_all_tags(st, &margs, buf_ffname); 2188 2189 if (st->fp != NULL) { 2190 fclose(st->fp); 2191 st->fp = NULL; 2192 } 2193 if (st->vimconv.vc_type != CONV_NONE) { 2194 convert_setup(&st->vimconv, NULL, NULL); 2195 } 2196 2197 if (margs.sort_error) { 2198 semsg(_("E432: Tags file not sorted: %s"), st->tag_fname); 2199 } 2200 2201 // Stop searching if sufficient tags have been found. 2202 if (st->match_count >= st->mincount) { 2203 st->stop_searching = true; 2204 } 2205 } 2206 2207 /// Copy the tags found by find_tags() to "matchesp". 2208 /// Returns the number of matches copied. 2209 static int findtags_copy_matches(findtags_state_T *st, char ***matchesp) 2210 { 2211 const bool name_only = (st->flags & TAG_NAMES); 2212 2213 char **matches = st->match_count > 0 2214 ? xmalloc((size_t)st->match_count * sizeof(char *)) 2215 : NULL; 2216 2217 st->match_count = 0; 2218 for (int mtt = 0; mtt < MT_COUNT; mtt++) { 2219 for (int i = 0; i < st->ga_match[mtt].ga_len; i++) { 2220 char *mfp = ((char **)(st->ga_match[mtt].ga_data))[i]; 2221 if (matches == NULL) { 2222 xfree(mfp); 2223 } else { 2224 if (!name_only) { 2225 // Change mtt back to zero-based. 2226 *mfp = (char)(*mfp - 1); 2227 2228 // change the TAG_SEP back to NUL 2229 for (char *p = mfp + 1; *p != NUL; p++) { 2230 if (*p == TAG_SEP) { 2231 *p = NUL; 2232 } 2233 } 2234 } 2235 matches[st->match_count++] = mfp; 2236 } 2237 } 2238 2239 ga_clear(&st->ga_match[mtt]); 2240 hash_clear(&st->ht_match[mtt]); 2241 } 2242 2243 *matchesp = matches; 2244 return st->match_count; 2245 } 2246 2247 /// find_tags() - search for tags in tags files 2248 /// 2249 /// Return FAIL if search completely failed (*num_matches will be 0, *matchesp 2250 /// will be NULL), OK otherwise. 2251 /// 2252 /// There is a priority in which type of tag is recognized. 2253 /// 2254 /// 6. A static or global tag with a full matching tag for the current file. 2255 /// 5. A global tag with a full matching tag for another file. 2256 /// 4. A static tag with a full matching tag for another file. 2257 /// 3. A static or global tag with an ignore-case matching tag for the 2258 /// current file. 2259 /// 2. A global tag with an ignore-case matching tag for another file. 2260 /// 1. A static tag with an ignore-case matching tag for another file. 2261 /// 2262 /// Tags in an emacs-style tags file are always global. 2263 /// 2264 /// flags: 2265 /// TAG_HELP only search for help tags 2266 /// TAG_NAMES only return name of tag 2267 /// TAG_REGEXP use "pat" as a regexp 2268 /// TAG_NOIC don't always ignore case 2269 /// TAG_KEEP_LANG keep language 2270 /// TAG_NO_TAGFUNC do not call the 'tagfunc' function 2271 /// 2272 /// @param pat pattern to search for 2273 /// @param num_matches return: number of matches found 2274 /// @param matchesp return: array of matches found 2275 /// @param mincount MAXCOL: find all matches 2276 /// other: minimal number of matches 2277 /// @param buf_ffname name of buffer for priority 2278 int find_tags(char *pat, int *num_matches, char ***matchesp, int flags, int mincount, 2279 char *buf_ffname) 2280 { 2281 findtags_state_T st; 2282 tagname_T tn; // info for get_tagfname() 2283 int first_file; // trying first tag file 2284 int retval = FAIL; // return value 2285 2286 int i; 2287 char *saved_pat = NULL; // copy of pat[] 2288 2289 int findall = (mincount == MAXCOL || mincount == TAG_MANY); // find all matching tags 2290 bool has_re = (flags & TAG_REGEXP); // regexp used 2291 int noic = (flags & TAG_NOIC); 2292 int verbose = (flags & TAG_VERBOSE); 2293 int save_p_ic = p_ic; 2294 2295 // uncrustify:off 2296 2297 // Change the value of 'ignorecase' according to 'tagcase' for the 2298 // duration of this function. 2299 switch (curbuf->b_tc_flags ? curbuf->b_tc_flags : tc_flags) { 2300 case kOptTcFlagFollowic: break; 2301 case kOptTcFlagIgnore: 2302 p_ic = true; 2303 break; 2304 case kOptTcFlagMatch: 2305 p_ic = false; 2306 break; 2307 case kOptTcFlagFollowscs: 2308 p_ic = ignorecase(pat); 2309 break; 2310 case kOptTcFlagSmart: 2311 p_ic = ignorecase_opt(pat, true, true); 2312 break; 2313 default: 2314 abort(); 2315 } 2316 2317 // uncrustify:on 2318 2319 int help_save = curbuf->b_help; 2320 2321 findtags_state_init(&st, pat, flags, mincount); 2322 2323 // Initialize a few variables 2324 if (st.help_only) { // want tags from help file 2325 curbuf->b_help = true; // will be restored later 2326 } 2327 2328 if (curbuf->b_help) { 2329 // When "@ab" is specified use only the "ab" language, otherwise 2330 // search all languages. 2331 if (st.orgpat->len > 3 && pat[st.orgpat->len - 3] == '@' 2332 && ASCII_ISALPHA(pat[st.orgpat->len - 2]) 2333 && ASCII_ISALPHA(pat[st.orgpat->len - 1])) { 2334 saved_pat = xstrnsave(pat, (size_t)st.orgpat->len - 3); 2335 st.help_lang_find = &pat[st.orgpat->len - 2]; 2336 st.orgpat->pat = saved_pat; 2337 st.orgpat->len -= 3; 2338 } 2339 } 2340 if (p_tl != 0 && st.orgpat->len > p_tl) { // adjust for 'taglength' 2341 st.orgpat->len = (int)p_tl; 2342 } 2343 2344 int save_emsg_off = emsg_off; 2345 emsg_off = true; // don't want error for invalid RE here 2346 prepare_pats(st.orgpat, has_re); 2347 emsg_off = save_emsg_off; 2348 if (has_re && st.orgpat->regmatch.regprog == NULL) { 2349 goto findtag_end; 2350 } 2351 2352 retval = findtags_apply_tfu(&st, pat, buf_ffname); 2353 if (retval != NOTDONE) { 2354 goto findtag_end; 2355 } 2356 2357 // re-initialize the default return value 2358 retval = FAIL; 2359 2360 // Set a flag if the file extension is .txt 2361 if ((flags & TAG_KEEP_LANG) 2362 && st.help_lang_find == NULL 2363 && curbuf->b_fname != NULL 2364 && (i = (int)strlen(curbuf->b_fname)) > 4 2365 && STRICMP(curbuf->b_fname + i - 4, ".txt") == 0) { 2366 st.is_txt = true; 2367 } 2368 2369 // When finding a specified number of matches, first try with matching 2370 // case, so binary search can be used, and try ignore-case matches in a 2371 // second loop. 2372 // When finding all matches, 'tagbsearch' is off, or there is no fixed 2373 // string to look for, ignore case right away to avoid going though the 2374 // tags files twice. 2375 // When the tag file is case-fold sorted, it is either one or the other. 2376 // Only ignore case when TAG_NOIC not used or 'ignorecase' set. 2377 st.orgpat->regmatch.rm_ic = ((p_ic || !noic) 2378 && (findall || st.orgpat->headlen == 0 || !p_tbs)); 2379 for (int round = 1; round <= 2; round++) { 2380 st.linear = (st.orgpat->headlen == 0 || !p_tbs || round == 2); 2381 2382 // Try tag file names from tags option one by one. 2383 for (first_file = true; 2384 get_tagfname(&tn, first_file, st.tag_fname) == OK; 2385 first_file = false) { 2386 findtags_in_file(&st, flags, buf_ffname); 2387 if (st.stop_searching) { 2388 retval = OK; 2389 break; 2390 } 2391 } // end of for-each-file loop 2392 2393 tagname_free(&tn); 2394 2395 // stop searching when already did a linear search, or when TAG_NOIC 2396 // used, and 'ignorecase' not set or already did case-ignore search 2397 if (st.stop_searching || st.linear || (!p_ic && noic) 2398 || st.orgpat->regmatch.rm_ic) { 2399 break; 2400 } 2401 2402 // try another time while ignoring case 2403 st.orgpat->regmatch.rm_ic = true; 2404 } 2405 2406 if (!st.stop_searching) { 2407 if (!st.did_open && verbose) { // never opened any tags file 2408 emsg(_("E433: No tags file")); 2409 } 2410 retval = OK; // It's OK even when no tag found 2411 } 2412 2413 findtag_end: 2414 findtags_state_free(&st); 2415 2416 // Move the matches from the ga_match[] arrays into one list of 2417 // matches. When retval == FAIL, free the matches. 2418 if (retval == FAIL) { 2419 st.match_count = 0; 2420 } 2421 2422 *num_matches = findtags_copy_matches(&st, matchesp); 2423 2424 curbuf->b_help = help_save; 2425 xfree(saved_pat); 2426 2427 p_ic = save_p_ic; 2428 2429 return retval; 2430 } 2431 2432 static garray_T tag_fnames = GA_EMPTY_INIT_VALUE; 2433 2434 // Callback function for finding all "tags" and "tags-??" files in 2435 // 'runtimepath' doc directories. 2436 static bool found_tagfile_cb(int num_fnames, char **fnames, bool all, void *cookie) 2437 { 2438 for (int i = 0; i < num_fnames; i++) { 2439 char *const tag_fname = xstrdup(fnames[i]); 2440 2441 #ifdef BACKSLASH_IN_FILENAME 2442 slash_adjust(tag_fname); 2443 #endif 2444 simplify_filename(tag_fname); 2445 GA_APPEND(char *, &tag_fnames, tag_fname); 2446 2447 if (!all) { 2448 break; 2449 } 2450 } 2451 2452 return num_fnames > 0; 2453 } 2454 2455 #if defined(EXITFREE) 2456 void free_tag_stuff(void) 2457 { 2458 ga_clear_strings(&tag_fnames); 2459 if (curwin != NULL) { 2460 do_tag(NULL, DT_FREE, 0, 0, 0); 2461 } 2462 tag_freematch(); 2463 2464 tagstack_clear_entry(&ptag_entry); 2465 } 2466 2467 #endif 2468 2469 /// Get the next name of a tag file from the tag file list. 2470 /// For help files, use "tags" file only. 2471 /// 2472 /// @param tnp holds status info 2473 /// @param first true when first file name is wanted 2474 /// @param buf pointer to buffer of MAXPATHL chars 2475 /// 2476 /// @return FAIL if no more tag file names, OK otherwise. 2477 int get_tagfname(tagname_T *tnp, int first, char *buf) 2478 { 2479 char *fname = NULL; 2480 2481 if (first) { 2482 CLEAR_POINTER(tnp); 2483 } 2484 2485 if (curbuf->b_help) { 2486 // For help files it's done in a completely different way: 2487 // Find "doc/tags" and "doc/tags-??" in all directories in 2488 // 'runtimepath'. 2489 if (first) { 2490 ga_clear_strings(&tag_fnames); 2491 ga_init(&tag_fnames, (int)sizeof(char *), 10); 2492 do_in_runtimepath("doc/tags doc/tags-??", DIP_ALL, 2493 found_tagfile_cb, NULL); 2494 } 2495 2496 if (tnp->tn_hf_idx >= tag_fnames.ga_len) { 2497 // Not found in 'runtimepath', use 'helpfile', if it exists and 2498 // wasn't used yet, replacing "help.txt" with "tags". 2499 if (tnp->tn_hf_idx > tag_fnames.ga_len || *p_hf == NUL) { 2500 return FAIL; 2501 } 2502 tnp->tn_hf_idx++; 2503 xstrlcpy(buf, p_hf, MAXPATHL - STRLEN_LITERAL("tags")); 2504 STRCPY(path_tail(buf), "tags"); 2505 #ifdef BACKSLASH_IN_FILENAME 2506 slash_adjust(buf); 2507 #endif 2508 simplify_filename(buf); 2509 2510 for (int i = 0; i < tag_fnames.ga_len; i++) { 2511 if (strcmp(buf, ((char **)(tag_fnames.ga_data))[i]) == 0) { 2512 return FAIL; // avoid duplicate file names 2513 } 2514 } 2515 } else { 2516 xstrlcpy(buf, ((char **)(tag_fnames.ga_data))[tnp->tn_hf_idx++], MAXPATHL); 2517 } 2518 return OK; 2519 } 2520 2521 if (first) { 2522 // Init. We make a copy of 'tags', because autocommands may change 2523 // the value without notifying us. 2524 tnp->tn_tags = xstrdup((*curbuf->b_p_tags != NUL) ? curbuf->b_p_tags : p_tags); 2525 tnp->tn_np = tnp->tn_tags; 2526 } 2527 2528 // Loop until we have found a file name that can be used. 2529 // There are two states: 2530 // tnp->tn_did_filefind_init == false: setup for next part in 'tags'. 2531 // tnp->tn_did_filefind_init == true: find next file in this part. 2532 while (true) { 2533 if (tnp->tn_did_filefind_init) { 2534 fname = vim_findfile(tnp->tn_search_ctx); 2535 if (fname != NULL) { 2536 break; 2537 } 2538 2539 tnp->tn_did_filefind_init = false; 2540 } else { 2541 char *filename = NULL; 2542 2543 // Stop when used all parts of 'tags'. 2544 if (*tnp->tn_np == NUL) { 2545 vim_findfile_cleanup(tnp->tn_search_ctx); 2546 tnp->tn_search_ctx = NULL; 2547 return FAIL; 2548 } 2549 2550 // Copy next file name into buf. 2551 buf[0] = NUL; 2552 copy_option_part(&tnp->tn_np, buf, MAXPATHL - 1, " ,"); 2553 2554 char *r_ptr = vim_findfile_stopdir(buf); 2555 // move the filename one char forward and truncate the 2556 // filepath with a NUL 2557 filename = path_tail(buf); 2558 if (r_ptr != NULL) { 2559 STRMOVE(r_ptr + 1, r_ptr); 2560 r_ptr++; 2561 } 2562 STRMOVE(filename + 1, filename); 2563 *filename++ = NUL; 2564 2565 tnp->tn_search_ctx = vim_findfile_init(buf, filename, strlen(filename), 2566 r_ptr, 100, 2567 false, // don't free visited list 2568 FINDFILE_FILE, // we search for a file 2569 tnp->tn_search_ctx, true, curbuf->b_ffname); 2570 if (tnp->tn_search_ctx != NULL) { 2571 tnp->tn_did_filefind_init = true; 2572 } 2573 } 2574 } 2575 2576 STRCPY(buf, fname); 2577 xfree(fname); 2578 return OK; 2579 } 2580 2581 // Free the contents of a tagname_T that was filled by get_tagfname(). 2582 void tagname_free(tagname_T *tnp) 2583 { 2584 xfree(tnp->tn_tags); 2585 vim_findfile_cleanup(tnp->tn_search_ctx); 2586 tnp->tn_search_ctx = NULL; 2587 ga_clear_strings(&tag_fnames); 2588 } 2589 2590 /// Parse one line from the tags file. Find start/end of tag name, start/end of 2591 /// file name and start of search pattern. 2592 /// 2593 /// If is_etag is true, tagp->fname and tagp->fname_end are not set. 2594 /// 2595 /// @param lbuf line to be parsed 2596 /// 2597 /// @return FAIL if there is a format error in this line, OK otherwise. 2598 static int parse_tag_line(char *lbuf, tagptrs_T *tagp) 2599 { 2600 // Isolate the tagname, from lbuf up to the first white 2601 tagp->tagname = lbuf; 2602 char *p = vim_strchr(lbuf, TAB); 2603 if (p == NULL) { 2604 return FAIL; 2605 } 2606 tagp->tagname_end = p; 2607 2608 // Isolate file name, from first to second white space 2609 if (*p != NUL) { 2610 p++; 2611 } 2612 tagp->fname = p; 2613 p = vim_strchr(p, TAB); 2614 if (p == NULL) { 2615 return FAIL; 2616 } 2617 tagp->fname_end = p; 2618 2619 // find start of search command, after second white space 2620 if (*p != NUL) { 2621 p++; 2622 } 2623 if (*p == NUL) { 2624 return FAIL; 2625 } 2626 tagp->command = p; 2627 2628 return OK; 2629 } 2630 2631 // Check if tagname is a static tag 2632 // 2633 // Static tags produced by the older ctags program have the format: 2634 // 'file:tag file /pattern'. 2635 // This is only recognized when both occurrence of 'file' are the same, to 2636 // avoid recognizing "string::string" or ":exit". 2637 // 2638 // Static tags produced by the new ctags program have the format: 2639 // 'tag file /pattern/;"<Tab>file:' " 2640 // 2641 // Return true if it is a static tag and adjust *tagname to the real tag. 2642 // Return false if it is not a static tag. 2643 static bool test_for_static(tagptrs_T *tagp) 2644 { 2645 // Check for new style static tag ":...<Tab>file:[<Tab>...]" 2646 char *p = tagp->command; 2647 while ((p = vim_strchr(p, '\t')) != NULL) { 2648 p++; 2649 if (strncmp(p, "file:", 5) == 0) { 2650 return true; 2651 } 2652 } 2653 2654 return false; 2655 } 2656 2657 /// @return the length of a matching tag line. 2658 static size_t matching_line_len(const char *const lbuf) 2659 { 2660 const char *p = lbuf + 1; 2661 2662 // does the same thing as parse_match() 2663 p += strlen(p) + 1; 2664 return (size_t)(p - lbuf) + strlen(p); 2665 } 2666 2667 /// Parse a line from a matching tag. Does not change the line itself. 2668 /// 2669 /// The line that we get looks like this: 2670 /// Emacs tag: <mtt><tag_fname><NUL><ebuf><NUL><lbuf> 2671 /// other tag: <mtt><tag_fname><NUL><NUL><lbuf> 2672 /// without Emacs tags: <mtt><tag_fname><NUL><lbuf> 2673 /// 2674 /// @param lbuf input: matching line 2675 /// @param tagp output: pointers into the line 2676 /// 2677 /// @return OK or FAIL. 2678 static int parse_match(char *lbuf, tagptrs_T *tagp) 2679 { 2680 tagp->tag_fname = lbuf + 1; 2681 lbuf += strlen(tagp->tag_fname) + 2; 2682 2683 // Find search pattern and the file name for non-etags. 2684 int retval = parse_tag_line(lbuf, tagp); 2685 2686 tagp->tagkind = NULL; 2687 tagp->user_data = NULL; 2688 tagp->tagline = 0; 2689 tagp->command_end = NULL; 2690 2691 if (retval != OK) { 2692 return retval; 2693 } 2694 2695 // Try to find a kind field: "kind:<kind>" or just "<kind>" 2696 char *p = tagp->command; 2697 if (find_extra(&p) == OK) { 2698 tagp->command_end = p; 2699 if (p > tagp->command && p[-1] == '|') { 2700 tagp->command_end = p - 1; // drop trailing bar 2701 } 2702 p += 2; // skip ";\"" 2703 if (*p++ == TAB) { 2704 // Accept ASCII alphabetic kind characters and any multi-byte 2705 // character. 2706 while (ASCII_ISALPHA(*p) || utfc_ptr2len(p) > 1) { 2707 if (strncmp(p, "kind:", 5) == 0) { 2708 tagp->tagkind = p + 5; 2709 } else if (strncmp(p, "user_data:", 10) == 0) { 2710 tagp->user_data = p + 10; 2711 } else if (strncmp(p, "line:", 5) == 0) { 2712 tagp->tagline = atoi(p + 5); 2713 } 2714 if (tagp->tagkind != NULL && tagp->user_data != NULL) { 2715 break; 2716 } 2717 2718 char *pc = vim_strchr(p, ':'); 2719 char *pt = vim_strchr(p, '\t'); 2720 if (pc == NULL || (pt != NULL && pc > pt)) { 2721 tagp->tagkind = p; 2722 } 2723 if (pt == NULL) { 2724 break; 2725 } 2726 p = pt; 2727 MB_PTR_ADV(p); 2728 } 2729 } 2730 } 2731 if (tagp->tagkind != NULL) { 2732 for (p = tagp->tagkind; 2733 *p && *p != '\t' && *p != '\r' && *p != '\n'; 2734 MB_PTR_ADV(p)) {} 2735 tagp->tagkind_end = p; 2736 } 2737 if (tagp->user_data != NULL) { 2738 for (p = tagp->user_data; 2739 *p && *p != '\t' && *p != '\r' && *p != '\n'; 2740 MB_PTR_ADV(p)) {} 2741 tagp->user_data_end = p; 2742 } 2743 return retval; 2744 } 2745 2746 // Find out the actual file name of a tag. Concatenate the tags file name 2747 // with the matching tag file name. 2748 // Returns an allocated string. 2749 static char *tag_full_fname(tagptrs_T *tagp) 2750 { 2751 char c = *tagp->fname_end; 2752 *tagp->fname_end = NUL; 2753 char *fullname = expand_tag_fname(tagp->fname, tagp->tag_fname, false); 2754 *tagp->fname_end = c; 2755 2756 return fullname; 2757 } 2758 2759 /// Jump to a tag that has been found in one of the tag files 2760 /// 2761 /// @param lbuf_arg line from the tags file for this tag 2762 /// @param forceit :ta with ! 2763 /// @param keep_help keep help flag 2764 /// 2765 /// @return OK for success, NOTAGFILE when file not found, FAIL otherwise. 2766 static int jumpto_tag(const char *lbuf_arg, int forceit, bool keep_help) 2767 { 2768 if (postponed_split == 0 && !check_can_set_curbuf_forceit(forceit)) { 2769 return FAIL; 2770 } 2771 2772 char *pbuf_end; 2773 char *tofree_fname = NULL; 2774 tagptrs_T tagp; 2775 int retval = FAIL; 2776 int getfile_result = GETFILE_UNUSED; 2777 int search_options; 2778 win_T *curwin_save = NULL; 2779 char *full_fname = NULL; 2780 const bool old_KeyTyped = KeyTyped; // getting the file may reset it 2781 const int l_g_do_tagpreview = g_do_tagpreview; 2782 const size_t len = matching_line_len(lbuf_arg) + 1; 2783 char *lbuf = xmalloc(len); 2784 memmove(lbuf, lbuf_arg, len); 2785 2786 char *pbuf = xmalloc(LSIZE); // search pattern buffer 2787 2788 // parse the match line into the tagp structure 2789 if (parse_match(lbuf, &tagp) == FAIL) { 2790 tagp.fname_end = NULL; 2791 goto erret; 2792 } 2793 2794 // truncate the file name, so it can be used as a string 2795 *tagp.fname_end = NUL; 2796 char *fname = tagp.fname; 2797 2798 // copy the command to pbuf[], remove trailing CR/NL 2799 char *str = tagp.command; 2800 for (pbuf_end = pbuf; *str && *str != '\n' && *str != '\r';) { 2801 *pbuf_end++ = *str++; 2802 if (pbuf_end - pbuf + 1 >= LSIZE) { 2803 break; 2804 } 2805 } 2806 *pbuf_end = NUL; 2807 2808 { 2809 // Remove the "<Tab>fieldname:value" stuff; we don't need it here. 2810 str = pbuf; 2811 if (find_extra(&str) == OK) { 2812 pbuf_end = str; 2813 *pbuf_end = NUL; 2814 } 2815 } 2816 2817 // Expand file name, when needed (for environment variables). 2818 // If 'tagrelative' option set, may change file name. 2819 fname = expand_tag_fname(fname, tagp.tag_fname, true); 2820 tofree_fname = fname; // free() it later 2821 2822 // Check if the file with the tag exists before abandoning the current 2823 // file. Also accept a file name for which there is a matching BufReadCmd 2824 // autocommand event (e.g., http://sys/file). 2825 if (!os_path_exists(fname) 2826 && !has_autocmd(EVENT_BUFREADCMD, fname, NULL)) { 2827 retval = NOTAGFILE; 2828 xfree(nofile_fname); 2829 nofile_fname = xstrdup(fname); 2830 goto erret; 2831 } 2832 2833 RedrawingDisabled++; 2834 2835 if (l_g_do_tagpreview != 0) { 2836 postponed_split = 0; // don't split again below 2837 curwin_save = curwin; // Save current window 2838 2839 // If we are reusing a window, we may change dir when 2840 // entering it (autocommands) so turn the tag filename 2841 // into a fullpath 2842 if (!curwin->w_p_pvw) { 2843 full_fname = FullName_save(fname, false); 2844 fname = full_fname; 2845 2846 // Make the preview window the current window. 2847 // Open a preview window when needed. 2848 prepare_tagpreview(true); 2849 } 2850 } 2851 2852 // If it was a CTRL-W CTRL-] command split window now. For ":tab tag" 2853 // open a new tab page. 2854 if (postponed_split && (swb_flags & (kOptSwbFlagUseopen | kOptSwbFlagUsetab))) { 2855 buf_T *const existing_buf = buflist_findname_exp(fname); 2856 2857 if (existing_buf != NULL) { 2858 // If 'switchbuf' is set jump to the window containing "buf". 2859 if (swbuf_goto_win_with_buf(existing_buf) != NULL) { 2860 // We've switched to the buffer, the usual loading of the file 2861 // must be skipped. 2862 getfile_result = GETFILE_SAME_FILE; 2863 } 2864 } 2865 } 2866 if (getfile_result == GETFILE_UNUSED 2867 && (postponed_split || cmdmod.cmod_tab != 0)) { 2868 if (swb_flags & kOptSwbFlagVsplit) { 2869 // If 'switchbuf' contains 'vsplit', then use a new vertically 2870 // split window. 2871 cmdmod.cmod_split |= WSP_VERT; 2872 } 2873 2874 if ((swb_flags & kOptSwbFlagNewtab) && cmdmod.cmod_tab == 0) { 2875 // If 'switchbuf' contains 'newtab', then use a new tabpage 2876 cmdmod.cmod_tab = tabpage_index(curtab) + 1; 2877 } 2878 2879 if (win_split(postponed_split > 0 ? postponed_split : 0, 2880 postponed_split_flags) == FAIL) { 2881 RedrawingDisabled--; 2882 goto erret; 2883 } 2884 RESET_BINDING(curwin); 2885 } 2886 2887 if (keep_help) { 2888 // A :ta from a help file will keep the b_help flag set. For ":ptag" 2889 // we need to use the flag from the window where we came from. 2890 if (l_g_do_tagpreview != 0) { 2891 keep_help_flag = bt_help(curwin_save->w_buffer); 2892 } else { 2893 keep_help_flag = curbuf->b_help; 2894 } 2895 } 2896 2897 if (getfile_result == GETFILE_UNUSED) { 2898 // Careful: getfile() may trigger autocommands and call jumpto_tag() 2899 // recursively. 2900 getfile_result = getfile(0, fname, NULL, true, 0, forceit); 2901 } 2902 keep_help_flag = false; 2903 2904 if (GETFILE_SUCCESS(getfile_result)) { // got to the right file 2905 curwin->w_set_curswant = true; 2906 postponed_split = 0; 2907 2908 const optmagic_T save_magic_overruled = magic_overruled; 2909 magic_overruled = OPTION_MAGIC_OFF; // always execute with 'nomagic' 2910 // Save value of no_hlsearch, jumping to a tag is not a real search 2911 const bool save_no_hlsearch = no_hlsearch; 2912 2913 // If 'cpoptions' contains 't', store the search pattern for the "n" 2914 // command. If 'cpoptions' does not contain 't', the search pattern 2915 // is not stored. 2916 if (vim_strchr(p_cpo, CPO_TAGPAT) != NULL) { 2917 search_options = 0; 2918 } else { 2919 search_options = SEARCH_KEEP; 2920 } 2921 2922 // If the command is a search, try here. 2923 // 2924 // Reset 'smartcase' for the search, since the search pattern was not 2925 // typed by the user. 2926 // Only use do_search() when there is a full search command, without 2927 // anything following. 2928 str = pbuf; 2929 if (pbuf[0] == '/' || pbuf[0] == '?') { 2930 str = skip_regexp(pbuf + 1, pbuf[0], false) + 1; 2931 } 2932 if (str > pbuf_end - 1) { // search command with nothing following 2933 size_t pbuflen = (size_t)(pbuf_end - pbuf); 2934 2935 bool save_p_ws = p_ws; 2936 int save_p_ic = p_ic; 2937 int save_p_scs = p_scs; 2938 p_ws = true; // need 'wrapscan' for backward searches 2939 p_ic = false; // don't ignore case now 2940 p_scs = false; 2941 linenr_T save_lnum = curwin->w_cursor.lnum; 2942 2943 curwin->w_cursor.lnum = tagp.tagline > 0 2944 // start search before line from "line:" field 2945 ? tagp.tagline - 1 2946 // start search before first line 2947 : 0; 2948 2949 if (do_search(NULL, pbuf[0], pbuf[0], pbuf + 1, pbuflen - 1, 1, 2950 search_options, NULL)) { 2951 retval = OK; 2952 } else { 2953 int found = 1; 2954 2955 // try again, ignore case now 2956 p_ic = true; 2957 if (!do_search(NULL, pbuf[0], pbuf[0], pbuf + 1, pbuflen - 1, 1, 2958 search_options, NULL)) { 2959 // Failed to find pattern, take a guess: "^func (" 2960 found = 2; 2961 test_for_static(&tagp); 2962 char cc = *tagp.tagname_end; 2963 *tagp.tagname_end = NUL; 2964 pbuflen = (size_t)snprintf(pbuf, LSIZE, "^%s\\s\\*(", tagp.tagname); 2965 if (!do_search(NULL, '/', '/', pbuf, pbuflen, 1, search_options, NULL)) { 2966 // Guess again: "^char * \<func (" 2967 pbuflen = (size_t)snprintf(pbuf, LSIZE, "^\\[#a-zA-Z_]\\.\\*\\<%s\\s\\*(", 2968 tagp.tagname); 2969 if (!do_search(NULL, '/', '/', pbuf, pbuflen, 1, search_options, NULL)) { 2970 found = 0; 2971 } 2972 } 2973 *tagp.tagname_end = cc; 2974 } 2975 if (found == 0) { 2976 emsg(_("E434: Can't find tag pattern")); 2977 curwin->w_cursor.lnum = save_lnum; 2978 } else { 2979 // Only give a message when really guessed, not when 'ic' 2980 // is set and match found while ignoring case. 2981 if (found == 2 || !save_p_ic) { 2982 msg(_("E435: Couldn't find tag, just guessing!"), 0); 2983 if (!msg_scrolled && msg_silent == 0) { 2984 msg_delay(1010, true); 2985 } 2986 } 2987 retval = OK; 2988 } 2989 } 2990 p_ws = save_p_ws; 2991 p_ic = save_p_ic; 2992 p_scs = save_p_scs; 2993 2994 // A search command may have positioned the cursor beyond the end 2995 // of the line. May need to correct that here. 2996 check_cursor(curwin); 2997 } else { 2998 const int save_secure = secure; 2999 3000 // Setup the sandbox for executing the command from the tags file. 3001 secure = 1; 3002 sandbox++; 3003 curwin->w_cursor.lnum = 1; // start command in line 1 3004 curwin->w_cursor.col = 0; 3005 curwin->w_cursor.coladd = 0; 3006 do_cmdline_cmd(pbuf); 3007 retval = OK; 3008 3009 // When the command has done something that is not allowed make sure 3010 // the error message can be seen. 3011 if (secure == 2) { 3012 wait_return(true); 3013 } 3014 secure = save_secure; 3015 sandbox--; 3016 } 3017 3018 magic_overruled = save_magic_overruled; 3019 // restore no_hlsearch when keeping the old search pattern 3020 if (search_options) { 3021 set_no_hlsearch(save_no_hlsearch); 3022 } 3023 3024 // Return OK if jumped to another file (at least we found the file!). 3025 if (getfile_result == GETFILE_OPEN_OTHER) { 3026 retval = OK; 3027 } 3028 3029 if (retval == OK) { 3030 // For a help buffer: Put the cursor line at the top of the window, 3031 // the help subject will be below it. 3032 if (curbuf->b_help) { 3033 set_topline(curwin, curwin->w_cursor.lnum); 3034 } 3035 if ((fdo_flags & kOptFdoFlagTag) && old_KeyTyped) { 3036 foldOpenCursor(); 3037 } 3038 } 3039 3040 if (l_g_do_tagpreview != 0 3041 && curwin != curwin_save && win_valid(curwin_save)) { 3042 // Return cursor to where we were 3043 validate_cursor(curwin); 3044 redraw_later(curwin, UPD_VALID); 3045 win_enter(curwin_save, true); 3046 } 3047 3048 RedrawingDisabled--; 3049 } else { 3050 RedrawingDisabled--; 3051 if (postponed_split) { // close the window 3052 win_close(curwin, false, false); 3053 postponed_split = 0; 3054 } 3055 } 3056 3057 erret: 3058 g_do_tagpreview = 0; // For next time 3059 xfree(lbuf); 3060 xfree(pbuf); 3061 xfree(tofree_fname); 3062 xfree(full_fname); 3063 3064 return retval; 3065 } 3066 3067 /// If "expand" is true, expand wildcards in fname. 3068 /// If 'tagrelative' option set, change fname (name of file containing tag) 3069 /// according to tag_fname (name of tag file containing fname). 3070 /// 3071 /// @return a pointer to allocated memory. 3072 static char *expand_tag_fname(char *fname, char *const tag_fname, const bool expand) 3073 { 3074 char *p; 3075 char *expanded_fname = NULL; 3076 expand_T xpc; 3077 3078 // Expand file name (for environment variables) when needed. 3079 if (expand && path_has_wildcard(fname)) { 3080 ExpandInit(&xpc); 3081 xpc.xp_context = EXPAND_FILES; 3082 expanded_fname = ExpandOne(&xpc, fname, NULL, 3083 WILD_LIST_NOTFOUND|WILD_SILENT, WILD_EXPAND_FREE); 3084 if (expanded_fname != NULL) { 3085 fname = expanded_fname; 3086 } 3087 } 3088 3089 char *retval; 3090 if ((p_tr || curbuf->b_help) 3091 && !vim_isAbsName(fname) 3092 && (p = path_tail(tag_fname)) != tag_fname) { 3093 retval = xmalloc(MAXPATHL); 3094 STRCPY(retval, tag_fname); 3095 xstrlcpy(retval + (p - tag_fname), fname, (size_t)(MAXPATHL - (p - tag_fname))); 3096 // Translate names like "src/a/../b/file.c" into "src/b/file.c". 3097 simplify_filename(retval); 3098 } else { 3099 retval = xstrdup(fname); 3100 } 3101 3102 xfree(expanded_fname); 3103 3104 return retval; 3105 } 3106 3107 /// Check if we have a tag for the buffer with name "buf_ffname". 3108 /// This is a bit slow, because of the full path compare in path_full_compare(). 3109 /// 3110 /// @return true if tag for file "fname" if tag file "tag_fname" is for current 3111 /// file. 3112 static int test_for_current(char *fname, char *fname_end, char *tag_fname, char *buf_ffname) 3113 { 3114 int retval = false; 3115 3116 if (buf_ffname != NULL) { // if the buffer has a name 3117 char c; 3118 { 3119 c = *fname_end; 3120 *fname_end = NUL; 3121 } 3122 char *fullname = expand_tag_fname(fname, tag_fname, true); 3123 retval = (path_full_compare(fullname, buf_ffname, true, true) & kEqualFiles); 3124 xfree(fullname); 3125 *fname_end = c; 3126 } 3127 3128 return retval; 3129 } 3130 3131 // Find the end of the tagaddress. 3132 // Return OK if ";\"" is following, FAIL otherwise. 3133 static int find_extra(char **pp) 3134 { 3135 char *str = *pp; 3136 char first_char = **pp; 3137 3138 // Repeat for addresses separated with ';' 3139 while (true) { 3140 if (ascii_isdigit(*str)) { 3141 str = skipdigits(str + 1); 3142 } else if (*str == '/' || *str == '?') { 3143 str = skip_regexp(str + 1, *str, false); 3144 if (*str != first_char) { 3145 str = NULL; 3146 } else { 3147 str++; 3148 } 3149 } else { 3150 // not a line number or search string, look for terminator. 3151 str = strstr(str, "|;\""); 3152 if (str != NULL) { 3153 str++; 3154 break; 3155 } 3156 } 3157 if (str == NULL || *str != ';' 3158 || !(ascii_isdigit(str[1]) || str[1] == '/' || str[1] == '?')) { 3159 break; 3160 } 3161 str++; // skip ';' 3162 first_char = *str; 3163 } 3164 3165 if (str != NULL && strncmp(str, ";\"", 2) == 0) { 3166 *pp = str; 3167 return OK; 3168 } 3169 return FAIL; 3170 } 3171 3172 /// Free a single entry in a tag stack 3173 void tagstack_clear_entry(taggy_T *item) 3174 { 3175 XFREE_CLEAR(item->tagname); 3176 XFREE_CLEAR(item->user_data); 3177 } 3178 3179 /// @param tagnames expand tag names 3180 int expand_tags(bool tagnames, char *pat, int *num_file, char ***file) 3181 { 3182 size_t name_buf_size = 100; 3183 int ret; 3184 3185 char *name_buf = xmalloc(name_buf_size); 3186 3187 int extra_flag = tagnames ? TAG_NAMES : 0; 3188 if (pat[0] == '/') { 3189 ret = find_tags(pat + 1, num_file, file, 3190 TAG_REGEXP | extra_flag | TAG_VERBOSE | TAG_NO_TAGFUNC, 3191 TAG_MANY, curbuf->b_ffname); 3192 } else { 3193 ret = find_tags(pat, num_file, file, 3194 TAG_REGEXP | extra_flag | TAG_VERBOSE | TAG_NO_TAGFUNC | TAG_NOIC, 3195 TAG_MANY, curbuf->b_ffname); 3196 } 3197 if (ret == OK && !tagnames) { 3198 // Reorganize the tags for display and matching as strings of: 3199 // "<tagname>\0<kind>\0<filename>\0" 3200 for (int i = 0; i < *num_file; i++) { 3201 tagptrs_T t_p; 3202 parse_match((*file)[i], &t_p); 3203 size_t len = (size_t)(t_p.tagname_end - t_p.tagname); 3204 if (len > name_buf_size - 3) { 3205 name_buf_size = len + 3; 3206 char *buf = xrealloc(name_buf, name_buf_size); 3207 name_buf = buf; 3208 } 3209 3210 memmove(name_buf, t_p.tagname, len); 3211 name_buf[len++] = 0; 3212 name_buf[len++] = (t_p.tagkind != NULL && *t_p.tagkind) 3213 ? *t_p.tagkind : 'f'; 3214 name_buf[len++] = 0; 3215 memmove((*file)[i] + len, t_p.fname, (size_t)(t_p.fname_end - t_p.fname)); 3216 (*file)[i][len + (size_t)(t_p.fname_end - t_p.fname)] = 0; 3217 memmove((*file)[i], name_buf, len); 3218 } 3219 } 3220 xfree(name_buf); 3221 return ret; 3222 } 3223 3224 /// Add a tag field to the dictionary "dict". 3225 /// Return OK or FAIL. 3226 /// 3227 /// @param start start of the value 3228 /// @param end after the value; can be NULL 3229 static int add_tag_field(dict_T *dict, const char *field_name, const char *start, const char *end) 3230 FUNC_ATTR_NONNULL_ARG(1, 2) 3231 { 3232 // Check that the field name doesn't exist yet. 3233 if (tv_dict_find(dict, field_name, -1) != NULL) { 3234 if (p_verbose > 0) { 3235 verbose_enter(); 3236 smsg(0, _("Duplicate field name: %s"), field_name); 3237 verbose_leave(); 3238 } 3239 return FAIL; 3240 } 3241 int len = 0; 3242 char *buf = xmalloc(MAXPATHL); 3243 if (start != NULL) { 3244 if (end == NULL) { 3245 end = start + strlen(start); 3246 while (end > start && (end[-1] == '\r' || end[-1] == '\n')) { 3247 end--; 3248 } 3249 } 3250 len = MIN((int)(end - start), MAXPATHL - 1); 3251 xmemcpyz(buf, start, (size_t)len); 3252 } 3253 buf[len] = NUL; 3254 int retval = tv_dict_add_str(dict, field_name, strlen(field_name), buf); 3255 xfree(buf); 3256 return retval; 3257 } 3258 3259 /// Add the tags matching the specified pattern "pat" to the list "list" 3260 /// as a dictionary. Use "buf_fname" for priority, unless NULL. 3261 int get_tags(list_T *list, char *pat, char *buf_fname) 3262 { 3263 int num_matches; 3264 char **matches; 3265 tagptrs_T tp; 3266 3267 int ret = find_tags(pat, &num_matches, &matches, TAG_REGEXP | TAG_NOIC, MAXCOL, buf_fname); 3268 if (ret != OK || num_matches <= 0) { 3269 return ret; 3270 } 3271 3272 for (int i = 0; i < num_matches; i++) { 3273 if (parse_match(matches[i], &tp) == FAIL) { 3274 xfree(matches[i]); 3275 continue; 3276 } 3277 3278 bool is_static = test_for_static(&tp); 3279 3280 // Skip pseudo-tag lines. 3281 if (strncmp(tp.tagname, "!_TAG_", 6) == 0) { 3282 xfree(matches[i]); 3283 continue; 3284 } 3285 3286 dict_T *dict = tv_dict_alloc(); 3287 tv_list_append_dict(list, dict); 3288 3289 char *full_fname = tag_full_fname(&tp); 3290 if (add_tag_field(dict, "name", tp.tagname, tp.tagname_end) == FAIL 3291 || add_tag_field(dict, "filename", full_fname, NULL) == FAIL 3292 || add_tag_field(dict, "cmd", tp.command, tp.command_end) == FAIL 3293 || add_tag_field(dict, "kind", tp.tagkind, 3294 tp.tagkind ? tp.tagkind_end : NULL) == FAIL 3295 || tv_dict_add_nr(dict, S_LEN("static"), is_static) == FAIL) { 3296 ret = FAIL; 3297 } 3298 3299 xfree(full_fname); 3300 3301 if (tp.command_end != NULL) { 3302 for (char *p = tp.command_end + 3; 3303 *p != NUL && *p != '\n' && *p != '\r'; 3304 MB_PTR_ADV(p)) { 3305 if (p == tp.tagkind 3306 || (p + 5 == tp.tagkind && strncmp(p, "kind:", 5) == 0)) { 3307 // skip "kind:<kind>" and "<kind>" 3308 p = tp.tagkind_end - 1; 3309 } else if (strncmp(p, "file:", 5) == 0) { 3310 // skip "file:" (static tag) 3311 p += 4; 3312 } else if (!ascii_iswhite(*p)) { 3313 int len; 3314 3315 // Add extra field as a dict entry. Fields are 3316 // separated by Tabs. 3317 char *n = p; 3318 while (*p != NUL && *p >= ' ' && *p < 127 && *p != ':') { 3319 p++; 3320 } 3321 len = (int)(p - n); 3322 if (*p == ':' && len > 0) { 3323 char *s = ++p; 3324 while (*p != NUL && (uint8_t)(*p) >= ' ') { 3325 p++; 3326 } 3327 n[len] = NUL; 3328 if (add_tag_field(dict, n, s, p) == FAIL) { 3329 ret = FAIL; 3330 } 3331 n[len] = ':'; 3332 } else { 3333 // Skip field without colon. 3334 while (*p != NUL && (uint8_t)(*p) >= ' ') { 3335 p++; 3336 } 3337 } 3338 if (*p == NUL) { 3339 break; 3340 } 3341 } 3342 } 3343 } 3344 3345 xfree(matches[i]); 3346 } 3347 xfree(matches); 3348 return ret; 3349 } 3350 3351 // Return information about 'tag' in dict 'retdict'. 3352 static void get_tag_details(taggy_T *tag, dict_T *retdict) 3353 { 3354 tv_dict_add_str(retdict, S_LEN("tagname"), tag->tagname); 3355 tv_dict_add_nr(retdict, S_LEN("matchnr"), tag->cur_match + 1); 3356 tv_dict_add_nr(retdict, S_LEN("bufnr"), tag->cur_fnum); 3357 if (tag->user_data) { 3358 tv_dict_add_str(retdict, S_LEN("user_data"), tag->user_data); 3359 } 3360 3361 list_T *pos = tv_list_alloc(4); 3362 tv_dict_add_list(retdict, S_LEN("from"), pos); 3363 3364 fmark_T *fmark = &tag->fmark; 3365 tv_list_append_number(pos, 3366 (varnumber_T)(fmark->fnum != -1 ? fmark->fnum : 0)); 3367 tv_list_append_number(pos, (varnumber_T)fmark->mark.lnum); 3368 tv_list_append_number(pos, (varnumber_T)(fmark->mark.col == MAXCOL 3369 ? MAXCOL : fmark->mark.col + 1)); 3370 tv_list_append_number(pos, (varnumber_T)fmark->mark.coladd); 3371 } 3372 3373 // Return the tag stack entries of the specified window 'wp' in dictionary 3374 // 'retdict'. 3375 void get_tagstack(win_T *wp, dict_T *retdict) 3376 { 3377 tv_dict_add_nr(retdict, S_LEN("length"), wp->w_tagstacklen); 3378 tv_dict_add_nr(retdict, S_LEN("curidx"), wp->w_tagstackidx + 1); 3379 list_T *l = tv_list_alloc(2); 3380 tv_dict_add_list(retdict, S_LEN("items"), l); 3381 3382 for (int i = 0; i < wp->w_tagstacklen; i++) { 3383 dict_T *d = tv_dict_alloc(); 3384 tv_list_append_dict(l, d); 3385 get_tag_details(&wp->w_tagstack[i], d); 3386 } 3387 } 3388 3389 // Free all the entries in the tag stack of the specified window 3390 static void tagstack_clear(win_T *wp) 3391 { 3392 // Free the current tag stack 3393 for (int i = 0; i < wp->w_tagstacklen; i++) { 3394 tagstack_clear_entry(&wp->w_tagstack[i]); 3395 } 3396 wp->w_tagstacklen = 0; 3397 wp->w_tagstackidx = 0; 3398 } 3399 3400 // Remove the oldest entry from the tag stack and shift the rest of 3401 // the entries to free up the top of the stack. 3402 static void tagstack_shift(win_T *wp) 3403 { 3404 taggy_T *tagstack = wp->w_tagstack; 3405 tagstack_clear_entry(&tagstack[0]); 3406 for (int i = 1; i < wp->w_tagstacklen; i++) { 3407 tagstack[i - 1] = tagstack[i]; 3408 } 3409 wp->w_tagstacklen--; 3410 } 3411 3412 /// Push a new item to the tag stack 3413 static void tagstack_push_item(win_T *wp, char *tagname, int cur_fnum, int cur_match, pos_T mark, 3414 int fnum, char *user_data) 3415 { 3416 taggy_T *tagstack = wp->w_tagstack; 3417 int idx = wp->w_tagstacklen; // top of the stack 3418 3419 // if the tagstack is full: remove the oldest entry 3420 if (idx >= TAGSTACKSIZE) { 3421 tagstack_shift(wp); 3422 idx = TAGSTACKSIZE - 1; 3423 } 3424 3425 wp->w_tagstacklen++; 3426 tagstack[idx].tagname = tagname; 3427 tagstack[idx].cur_fnum = cur_fnum; 3428 tagstack[idx].cur_match = cur_match; 3429 tagstack[idx].cur_match = MAX(tagstack[idx].cur_match, 0); 3430 tagstack[idx].fmark.mark = mark; 3431 tagstack[idx].fmark.fnum = fnum; 3432 tagstack[idx].fmark.view = (fmarkv_T)INIT_FMARKV; 3433 tagstack[idx].user_data = user_data; 3434 } 3435 3436 /// Add a list of items to the tag stack in the specified window 3437 static void tagstack_push_items(win_T *wp, list_T *l) 3438 { 3439 dictitem_T *di; 3440 char *tagname; 3441 pos_T mark; 3442 int fnum; 3443 3444 // Add one entry at a time to the tag stack 3445 for (listitem_T *li = tv_list_first(l); li != NULL; li = TV_LIST_ITEM_NEXT(l, li)) { 3446 if (TV_LIST_ITEM_TV(li)->v_type != VAR_DICT 3447 || TV_LIST_ITEM_TV(li)->vval.v_dict == NULL) { 3448 continue; // Skip non-dict items 3449 } 3450 dict_T *itemdict = TV_LIST_ITEM_TV(li)->vval.v_dict; 3451 3452 // parse 'from' for the cursor position before the tag jump 3453 if ((di = tv_dict_find(itemdict, "from", -1)) == NULL) { 3454 continue; 3455 } 3456 if (list2fpos(&di->di_tv, &mark, &fnum, NULL, false) != OK) { 3457 continue; 3458 } 3459 if ((tagname = tv_dict_get_string(itemdict, "tagname", true)) == NULL) { 3460 continue; 3461 } 3462 3463 if (mark.col > 0) { 3464 mark.col--; 3465 } 3466 tagstack_push_item(wp, 3467 tagname, 3468 (int)tv_dict_get_number(itemdict, "bufnr"), 3469 (int)tv_dict_get_number(itemdict, "matchnr") - 1, 3470 mark, fnum, 3471 tv_dict_get_string(itemdict, "user_data", true)); 3472 } 3473 } 3474 3475 // Set the current index in the tag stack. Valid values are between 0 3476 // and the stack length (inclusive). 3477 static void tagstack_set_curidx(win_T *wp, int curidx) 3478 { 3479 wp->w_tagstackidx = curidx; 3480 // sanity check 3481 wp->w_tagstackidx = MIN(MAX(wp->w_tagstackidx, 0), wp->w_tagstacklen); 3482 } 3483 3484 // Set the tag stack entries of the specified window. 3485 // 'action' is set to one of: 3486 // 'a' for append 3487 // 'r' for replace 3488 // 't' for truncate 3489 int set_tagstack(win_T *wp, const dict_T *d, int action) 3490 FUNC_ATTR_NONNULL_ARG(1) 3491 { 3492 // not allowed to alter the tag stack entries from inside tagfunc 3493 if (tfu_in_use) { 3494 emsg(_(e_cannot_modify_tag_stack_within_tagfunc)); 3495 return FAIL; 3496 } 3497 3498 dictitem_T *di; 3499 list_T *l = NULL; 3500 3501 if ((di = tv_dict_find(d, "items", -1)) != NULL) { 3502 if (di->di_tv.v_type != VAR_LIST) { 3503 emsg(_(e_listreq)); 3504 return FAIL; 3505 } 3506 l = di->di_tv.vval.v_list; 3507 } 3508 3509 if ((di = tv_dict_find(d, "curidx", -1)) != NULL) { 3510 tagstack_set_curidx(wp, (int)tv_get_number(&di->di_tv) - 1); 3511 } 3512 3513 if (action == 't') { // truncate the stack 3514 taggy_T *const tagstack = wp->w_tagstack; 3515 const int tagstackidx = wp->w_tagstackidx; 3516 int tagstacklen = wp->w_tagstacklen; 3517 3518 // delete all the tag stack entries above the current entry 3519 while (tagstackidx < tagstacklen) { 3520 tagstack_clear_entry(&tagstack[--tagstacklen]); 3521 } 3522 wp->w_tagstacklen = tagstacklen; 3523 } 3524 3525 if (l != NULL) { 3526 if (action == 'r') { // replace the stack 3527 tagstack_clear(wp); 3528 } 3529 3530 tagstack_push_items(wp, l); 3531 // set the current index after the last entry 3532 wp->w_tagstackidx = wp->w_tagstacklen; 3533 } 3534 3535 return OK; 3536 }