comparison src/tag.c @ 28075:a1914b89f0b7 v8.2.4562

patch 8.2.4562: linear tag search is not optimal Commit: https://github.com/vim/vim/commit/b29b96806f1472371fb3cc01d48394e00b95cfc8 Author: Yegappan Lakshmanan <yegappan@yahoo.com> Date: Sun Mar 13 19:23:48 2022 +0000 patch 8.2.4562: linear tag search is not optimal Problem: Linear tag search is not optimal. Solution: Improve linear tag search performance. (Yegappan Lakshmanan, closes #9944)
author Bram Moolenaar <Bram@vim.org>
date Sun, 13 Mar 2022 20:30:03 +0100
parents bfa81ded42e2
children 62cc3b60493b
comparison
equal deleted inserted replaced
28074:876fd6788230 28075:a1914b89f0b7
1640 * State information used during a tag search 1640 * State information used during a tag search
1641 */ 1641 */
1642 typedef struct 1642 typedef struct
1643 { 1643 {
1644 tagsearch_state_T state; // tag search state 1644 tagsearch_state_T state; // tag search state
1645 int stop_searching; // stop when match found or error
1646 pat_T *orgpat; // holds unconverted pattern info
1647 char_u *lbuf; // line buffer
1648 int lbuf_size; // length of lbuf
1645 char_u *tag_fname; // name of the tag file 1649 char_u *tag_fname; // name of the tag file
1646 FILE *fp; // current tags file pointer 1650 FILE *fp; // current tags file pointer
1647 pat_T orgpat; // holds unconverted pattern info
1648 int flags; // flags used for tag search 1651 int flags; // flags used for tag search
1649 int tag_file_sorted; // !_TAG_FILE_SORTED value 1652 int tag_file_sorted; // !_TAG_FILE_SORTED value
1650 int get_searchpat; // used for 'showfulltag' 1653 int get_searchpat; // used for 'showfulltag'
1651 int help_only; // only search for help tags 1654 int help_only; // only search for help tags
1655 int did_open; // did open a tag file
1656 int mincount; // MAXCOL: find all matches
1657 // other: minimal number of matches
1658 int linear; // do a linear search
1652 vimconv_T vimconv; 1659 vimconv_T vimconv;
1660 #ifdef FEAT_EMACS_TAGS
1661 int is_etag; // current file is emacs style
1662 char_u *ebuf; // additional buffer for etag fname
1663 #endif
1653 #ifdef FEAT_MULTI_LANG 1664 #ifdef FEAT_MULTI_LANG
1654 char_u help_lang[3]; // lang of current tags file 1665 char_u help_lang[3]; // lang of current tags file
1655 int help_pri; // help language priority 1666 int help_pri; // help language priority
1656 char_u *help_lang_find; // lang to be found 1667 char_u *help_lang_find; // lang to be found
1657 int is_txt; // flag of file extension 1668 int is_txt; // flag of file extension
1658 #endif 1669 #endif
1659 int did_open; // did open a tag file
1660 int mincount; // MAXCOL: find all matches
1661 // other: minimal number of matches
1662 int linear; // do a linear search
1663 char_u *lbuf; // line buffer
1664 int lbuf_size; // length of lbuf
1665 #ifdef FEAT_EMACS_TAGS
1666 int is_etag; // current file is emacs style
1667 char_u *ebuf; // additional buffer for etag fname
1668 #endif
1669 int match_count; // number of matches found 1670 int match_count; // number of matches found
1670 garray_T ga_match[MT_COUNT]; // stores matches in sequence 1671 garray_T ga_match[MT_COUNT]; // stores matches in sequence
1671 hashtab_T ht_match[MT_COUNT]; // stores matches by key 1672 hashtab_T ht_match[MT_COUNT]; // stores matches by key
1672 int stop_searching; // stop when match found or error
1673 } findtags_state_T; 1673 } findtags_state_T;
1674 1674
1675 /* 1675 /*
1676 * Initialize the state used by find_tags(). 1676 * Initialize the state used by find_tags().
1677 * Returns OK on success and FAIL on memory allocation failure. 1677 * Returns OK on success and FAIL on memory allocation failure.
1685 { 1685 {
1686 int mtt; 1686 int mtt;
1687 1687
1688 st->tag_fname = alloc(MAXPATHL + 1); 1688 st->tag_fname = alloc(MAXPATHL + 1);
1689 st->fp = NULL; 1689 st->fp = NULL;
1690 st->orgpat.pat = pat; 1690 st->orgpat = ALLOC_ONE(pat_T);
1691 st->orgpat.len = (int)STRLEN(pat); 1691 st->orgpat->pat = pat;
1692 st->orgpat.regmatch.regprog = NULL; 1692 st->orgpat->len = (int)STRLEN(pat);
1693 st->orgpat->regmatch.regprog = NULL;
1693 st->flags = flags; 1694 st->flags = flags;
1694 st->tag_file_sorted = NUL; 1695 st->tag_file_sorted = NUL;
1695 st->help_only = (flags & TAG_HELP); 1696 st->help_only = (flags & TAG_HELP);
1696 st->get_searchpat = FALSE; 1697 st->get_searchpat = FALSE;
1697 #ifdef FEAT_MULTI_LANG 1698 #ifdef FEAT_MULTI_LANG
1734 static void 1735 static void
1735 findtags_state_free(findtags_state_T *st) 1736 findtags_state_free(findtags_state_T *st)
1736 { 1737 {
1737 vim_free(st->tag_fname); 1738 vim_free(st->tag_fname);
1738 vim_free(st->lbuf); 1739 vim_free(st->lbuf);
1739 vim_regfree(st->orgpat.regmatch.regprog); 1740 vim_regfree(st->orgpat->regmatch.regprog);
1741 vim_free(st->orgpat);
1740 #ifdef FEAT_EMACS_TAGS 1742 #ifdef FEAT_EMACS_TAGS
1741 vim_free(st->ebuf); 1743 vim_free(st->ebuf);
1742 #endif 1744 #endif
1743 } 1745 }
1744 1746
1938 --incstack_idx; 1940 --incstack_idx;
1939 fclose(st->fp); // end of this file ... 1941 fclose(st->fp); // end of this file ...
1940 st->fp = incstack[incstack_idx].fp; 1942 st->fp = incstack[incstack_idx].fp;
1941 STRCPY(st->tag_fname, incstack[incstack_idx].etag_fname); 1943 STRCPY(st->tag_fname, incstack[incstack_idx].etag_fname);
1942 vim_free(incstack[incstack_idx].etag_fname); 1944 vim_free(incstack[incstack_idx].etag_fname);
1945 st->is_etag = TRUE; // (only etags can include)
1943 1946
1944 return TRUE; 1947 return TRUE;
1945 } 1948 }
1946 1949
1947 /* 1950 /*
2097 2100
2098 if (eof) 2101 if (eof)
2099 { 2102 {
2100 #ifdef FEAT_EMACS_TAGS 2103 #ifdef FEAT_EMACS_TAGS
2101 if (emacs_tags_file_eof(st) == TRUE) 2104 if (emacs_tags_file_eof(st) == TRUE)
2102 {
2103 // an included tags file. Continue processing the parent 2105 // an included tags file. Continue processing the parent
2104 // tags file. 2106 // tags file.
2105 st->is_etag = TRUE; // (only etags can include)
2106 return TAGS_READ_IGNORE; 2107 return TAGS_READ_IGNORE;
2107 }
2108 #endif 2108 #endif
2109 return TAGS_READ_EOF; 2109 return TAGS_READ_EOF;
2110 } 2110 }
2111 } 2111 }
2112 2112
2191 st->state = TS_BINARY; 2191 st->state = TS_BINARY;
2192 else if (st->tag_file_sorted == '2') 2192 else if (st->tag_file_sorted == '2')
2193 { 2193 {
2194 st->state = TS_BINARY; 2194 st->state = TS_BINARY;
2195 *sortic = TRUE; 2195 *sortic = TRUE;
2196 st->orgpat.regmatch.rm_ic = (p_ic || !noic); 2196 st->orgpat->regmatch.rm_ic = (p_ic || !noic);
2197 } 2197 }
2198 else 2198 else
2199 st->state = TS_LINEAR; 2199 st->state = TS_LINEAR;
2200 2200
2201 if (st->state == TS_BINARY && st->orgpat.regmatch.rm_ic && !*sortic) 2201 if (st->state == TS_BINARY && st->orgpat->regmatch.rm_ic && !*sortic)
2202 { 2202 {
2203 // Binary search won't work for ignoring case, use linear 2203 // Binary search won't work for ignoring case, use linear
2204 // search. 2204 // search.
2205 st->linear = TRUE; 2205 st->linear = TRUE;
2206 st->state = TS_LINEAR; 2206 st->state = TS_LINEAR;
2239 * Parse a tag line read from a tags file. 2239 * Parse a tag line read from a tags file.
2240 * Returns OK if a tags line is successfully parsed. 2240 * Returns OK if a tags line is successfully parsed.
2241 * Returns FAIL if a format error is encountered. 2241 * Returns FAIL if a format error is encountered.
2242 */ 2242 */
2243 static int 2243 static int
2244 findtags_parse_line(findtags_state_T *st, tagptrs_T *tagpp) 2244 findtags_parse_line(
2245 findtags_state_T *st,
2246 tagptrs_T *tagpp,
2247 findtags_match_args_T *margs,
2248 tagsearch_info_T *sinfo_p)
2245 { 2249 {
2246 int status; 2250 int status;
2251 int i;
2252 int cmplen;
2253 int tagcmp;
2247 2254
2248 // Figure out where the different strings are in this line. 2255 // Figure out where the different strings are in this line.
2249 // For "normal" tags: Do a quick check if the tag matches. 2256 // For "normal" tags: Do a quick check if the tag matches.
2250 // This speeds up tag searching a lot! 2257 // This speeds up tag searching a lot!
2251 if (st->orgpat.headlen 2258 if (st->orgpat->headlen
2252 #ifdef FEAT_EMACS_TAGS 2259 #ifdef FEAT_EMACS_TAGS
2253 && !st->is_etag 2260 && !st->is_etag
2254 #endif 2261 #endif
2255 ) 2262 )
2256 { 2263 {
2257 CLEAR_FIELD(*tagpp); 2264 CLEAR_FIELD(*tagpp);
2258 tagpp->tagname = st->lbuf; 2265 tagpp->tagname = st->lbuf;
2259 tagpp->tagname_end = vim_strchr(st->lbuf, TAB); 2266 tagpp->tagname_end = vim_strchr(st->lbuf, TAB);
2260 if (tagpp->tagname_end == NULL) 2267 if (tagpp->tagname_end == NULL)
2261 {
2262 // Corrupted tag line. 2268 // Corrupted tag line.
2263 return FAIL; 2269 return TAG_MATCH_FAIL;
2264 } 2270
2265 2271 // Skip this line if the length of the tag is different and
2266 // Can be a matching tag, isolate the file name and command. 2272 // there is no regexp, or the tag is too short.
2267 tagpp->fname = tagpp->tagname_end + 1;
2268 tagpp->fname_end = vim_strchr(tagpp->fname, TAB);
2269 if (tagpp->fname_end == NULL)
2270 status = FAIL;
2271 else
2272 {
2273 tagpp->command = tagpp->fname_end + 1;
2274 status = OK;
2275 }
2276 }
2277 else
2278 status = parse_tag_line(st->lbuf,
2279 #ifdef FEAT_EMACS_TAGS
2280 st->is_etag,
2281 #endif
2282 tagpp);
2283
2284 if (status == FAIL)
2285 return FAIL;
2286
2287 #ifdef FEAT_EMACS_TAGS
2288 if (st->is_etag)
2289 tagpp->fname = st->ebuf;
2290 #endif
2291
2292 return OK;
2293 }
2294
2295 /*
2296 * Initialize the structure used for tag matching.
2297 */
2298 static void
2299 findtags_matchargs_init(findtags_match_args_T *margs, int flags)
2300 {
2301 margs->matchoff = 0; // match offset
2302 margs->match_re = FALSE; // match with regexp
2303 margs->match_no_ic = FALSE; // matches with case
2304 margs->has_re = (flags & TAG_REGEXP); // regexp used
2305 margs->sortic = FALSE; // tag file sorted in nocase
2306 margs->sort_error = FALSE; // tags file not sorted
2307 }
2308
2309 /*
2310 * Compares the tag name in 'tagpp->tagname' with a search pattern in
2311 * 'st->orgpat.head'.
2312 * Returns TAG_MATCH_SUCCESS if the tag matches, TAG_MATCH_FAIL if the tag
2313 * doesn't match, TAG_MATCH_NEXT to look for the next matching tag (used in a
2314 * binary search) and TAG_MATCH_STOP if all the tags are processed without a
2315 * match. Uses the values in 'margs' for doing the comparison.
2316 */
2317 static tagmatch_status_T
2318 findtags_match_tag(
2319 findtags_state_T *st,
2320 tagptrs_T *tagpp,
2321 findtags_match_args_T *margs,
2322 tagsearch_info_T *sinfo_p)
2323 {
2324 int match = FALSE;
2325 int cmplen;
2326 int i;
2327 int tagcmp;
2328
2329 // Skip this line if the length of the tag is different and
2330 // there is no regexp, or the tag is too short.
2331 if (st->orgpat.headlen
2332 #ifdef FEAT_EMACS_TAGS
2333 && !st->is_etag
2334 #endif
2335 )
2336 {
2337 cmplen = (int)(tagpp->tagname_end - tagpp->tagname); 2273 cmplen = (int)(tagpp->tagname_end - tagpp->tagname);
2338 if (p_tl != 0 && cmplen > p_tl) // adjust for 'taglength' 2274 if (p_tl != 0 && cmplen > p_tl) // adjust for 'taglength'
2339 cmplen = p_tl; 2275 cmplen = p_tl;
2340 if (margs->has_re && st->orgpat.headlen < cmplen) 2276 if ((st->flags & TAG_REGEXP) && st->orgpat->headlen < cmplen)
2341 cmplen = st->orgpat.headlen; 2277 cmplen = st->orgpat->headlen;
2342 else if (st->state == TS_LINEAR && st->orgpat.headlen != cmplen) 2278 else if (st->state == TS_LINEAR && st->orgpat->headlen != cmplen)
2343 return TAG_MATCH_FAIL; 2279 return TAG_MATCH_NEXT;
2344 2280
2345 if (st->state == TS_BINARY) 2281 if (st->state == TS_BINARY)
2346 { 2282 {
2347 // Simplistic check for unsorted tags file. 2283 // Simplistic check for unsorted tags file.
2348 i = (int)tagpp->tagname[0]; 2284 i = (int)tagpp->tagname[0];
2351 if (i < sinfo_p->low_char || i > sinfo_p->high_char) 2287 if (i < sinfo_p->low_char || i > sinfo_p->high_char)
2352 margs->sort_error = TRUE; 2288 margs->sort_error = TRUE;
2353 2289
2354 // Compare the current tag with the searched tag. 2290 // Compare the current tag with the searched tag.
2355 if (margs->sortic) 2291 if (margs->sortic)
2356 tagcmp = tag_strnicmp(tagpp->tagname, st->orgpat.head, 2292 tagcmp = tag_strnicmp(tagpp->tagname, st->orgpat->head,
2357 (size_t)cmplen); 2293 (size_t)cmplen);
2358 else 2294 else
2359 tagcmp = STRNCMP(tagpp->tagname, st->orgpat.head, cmplen); 2295 tagcmp = STRNCMP(tagpp->tagname, st->orgpat->head, cmplen);
2360 2296
2361 // A match with a shorter tag means to search forward. 2297 // A match with a shorter tag means to search forward.
2362 // A match with a longer tag means to search backward. 2298 // A match with a longer tag means to search backward.
2363 if (tagcmp == 0) 2299 if (tagcmp == 0)
2364 { 2300 {
2365 if (cmplen < st->orgpat.headlen) 2301 if (cmplen < st->orgpat->headlen)
2366 tagcmp = -1; 2302 tagcmp = -1;
2367 else if (cmplen > st->orgpat.headlen) 2303 else if (cmplen > st->orgpat->headlen)
2368 tagcmp = 1; 2304 tagcmp = 1;
2369 } 2305 }
2370 2306
2371 if (tagcmp == 0) 2307 if (tagcmp == 0)
2372 { 2308 {
2402 // No match yet and are at the end of the binary search. 2338 // No match yet and are at the end of the binary search.
2403 return TAG_MATCH_STOP; 2339 return TAG_MATCH_STOP;
2404 } 2340 }
2405 else if (st->state == TS_SKIP_BACK) 2341 else if (st->state == TS_SKIP_BACK)
2406 { 2342 {
2407 if (MB_STRNICMP(tagpp->tagname, st->orgpat.head, cmplen) != 0) 2343 if (MB_STRNICMP(tagpp->tagname, st->orgpat->head, cmplen) != 0)
2408 st->state = TS_STEP_FORWARD; 2344 st->state = TS_STEP_FORWARD;
2409 else 2345 else
2410 // Have to skip back more. Restore the curr_offset 2346 // Have to skip back more. Restore the curr_offset
2411 // used, otherwise we get stuck at a long line. 2347 // used, otherwise we get stuck at a long line.
2412 sinfo_p->curr_offset = sinfo_p->curr_offset_used; 2348 sinfo_p->curr_offset = sinfo_p->curr_offset_used;
2413 return TAG_MATCH_NEXT; 2349 return TAG_MATCH_NEXT;
2414 } 2350 }
2415 else if (st->state == TS_STEP_FORWARD) 2351 else if (st->state == TS_STEP_FORWARD)
2416 { 2352 {
2417 if (MB_STRNICMP(tagpp->tagname, st->orgpat.head, cmplen) != 0) 2353 if (MB_STRNICMP(tagpp->tagname, st->orgpat->head, cmplen) != 0)
2418 { 2354 {
2419 if ((off_T)vim_ftell(st->fp) > sinfo_p->match_offset) 2355 if ((off_T)vim_ftell(st->fp) > sinfo_p->match_offset)
2420 return TAG_MATCH_STOP; // past last match 2356 return TAG_MATCH_STOP; // past last match
2421 else 2357 else
2422 return TAG_MATCH_NEXT; // before first match 2358 return TAG_MATCH_NEXT; // before first match
2423 } 2359 }
2424 } 2360 }
2425 else 2361 else
2426 // skip this match if it can't match 2362 // skip this match if it can't match
2427 if (MB_STRNICMP(tagpp->tagname, st->orgpat.head, cmplen) != 0) 2363 if (MB_STRNICMP(tagpp->tagname, st->orgpat->head, cmplen) != 0)
2428 return TAG_MATCH_FAIL; 2364 return TAG_MATCH_NEXT;
2429 } 2365
2366 // Can be a matching tag, isolate the file name and command.
2367 tagpp->fname = tagpp->tagname_end + 1;
2368 tagpp->fname_end = vim_strchr(tagpp->fname, TAB);
2369 if (tagpp->fname_end == NULL)
2370 status = FAIL;
2371 else
2372 {
2373 tagpp->command = tagpp->fname_end + 1;
2374 status = OK;
2375 }
2376 }
2377 else
2378 status = parse_tag_line(st->lbuf,
2379 #ifdef FEAT_EMACS_TAGS
2380 st->is_etag,
2381 #endif
2382 tagpp);
2383
2384 if (status == FAIL)
2385 return TAG_MATCH_FAIL;
2386
2387 #ifdef FEAT_EMACS_TAGS
2388 if (st->is_etag)
2389 tagpp->fname = st->ebuf;
2390 #endif
2391
2392 return TAG_MATCH_SUCCESS;
2393 }
2394
2395 /*
2396 * Initialize the structure used for tag matching.
2397 */
2398 static void
2399 findtags_matchargs_init(findtags_match_args_T *margs, int flags)
2400 {
2401 margs->matchoff = 0; // match offset
2402 margs->match_re = FALSE; // match with regexp
2403 margs->match_no_ic = FALSE; // matches with case
2404 margs->has_re = (flags & TAG_REGEXP); // regexp used
2405 margs->sortic = FALSE; // tag file sorted in nocase
2406 margs->sort_error = FALSE; // tags file not sorted
2407 }
2408
2409 /*
2410 * Compares the tag name in 'tagpp->tagname' with a search pattern in
2411 * 'st->orgpat.head'.
2412 * Returns TAG_MATCH_SUCCESS if the tag matches, TAG_MATCH_FAIL if the tag
2413 * doesn't match, TAG_MATCH_NEXT to look for the next matching tag (used in a
2414 * binary search) and TAG_MATCH_STOP if all the tags are processed without a
2415 * match. Uses the values in 'margs' for doing the comparison.
2416 */
2417 static tagmatch_status_T
2418 findtags_match_tag(
2419 findtags_state_T *st,
2420 tagptrs_T *tagpp,
2421 findtags_match_args_T *margs)
2422 {
2423 int match = FALSE;
2424 int cmplen;
2430 2425
2431 // First try matching with the pattern literally (also when it is 2426 // First try matching with the pattern literally (also when it is
2432 // a regexp). 2427 // a regexp).
2433 cmplen = (int)(tagpp->tagname_end - tagpp->tagname); 2428 cmplen = (int)(tagpp->tagname_end - tagpp->tagname);
2434 if (p_tl != 0 && cmplen > p_tl) // adjust for 'taglength' 2429 if (p_tl != 0 && cmplen > p_tl) // adjust for 'taglength'
2435 cmplen = p_tl; 2430 cmplen = p_tl;
2436 // if tag length does not match, don't try comparing 2431 // if tag length does not match, don't try comparing
2437 if (st->orgpat.len != cmplen) 2432 if (st->orgpat->len != cmplen)
2438 match = FALSE; 2433 match = FALSE;
2439 else 2434 else
2440 { 2435 {
2441 if (st->orgpat.regmatch.rm_ic) 2436 if (st->orgpat->regmatch.rm_ic)
2442 { 2437 {
2443 match = 2438 match =
2444 (MB_STRNICMP(tagpp->tagname, st->orgpat.pat, cmplen) == 0); 2439 (MB_STRNICMP(tagpp->tagname, st->orgpat->pat, cmplen) == 0);
2445 if (match) 2440 if (match)
2446 margs->match_no_ic = 2441 margs->match_no_ic =
2447 (STRNCMP(tagpp->tagname, st->orgpat.pat, cmplen) == 0); 2442 (STRNCMP(tagpp->tagname, st->orgpat->pat, cmplen) == 0);
2448 } 2443 }
2449 else 2444 else
2450 match = (STRNCMP(tagpp->tagname, st->orgpat.pat, cmplen) == 0); 2445 match = (STRNCMP(tagpp->tagname, st->orgpat->pat, cmplen) == 0);
2451 } 2446 }
2452 2447
2453 // Has a regexp: Also find tags matching regexp. 2448 // Has a regexp: Also find tags matching regexp.
2454 margs->match_re = FALSE; 2449 margs->match_re = FALSE;
2455 if (!match && st->orgpat.regmatch.regprog != NULL) 2450 if (!match && st->orgpat->regmatch.regprog != NULL)
2456 { 2451 {
2457 int cc; 2452 int cc;
2458 2453
2459 cc = *tagpp->tagname_end; 2454 cc = *tagpp->tagname_end;
2460 *tagpp->tagname_end = NUL; 2455 *tagpp->tagname_end = NUL;
2461 match = vim_regexec(&st->orgpat.regmatch, tagpp->tagname, (colnr_T)0); 2456 match = vim_regexec(&st->orgpat->regmatch, tagpp->tagname, (colnr_T)0);
2462 if (match) 2457 if (match)
2463 { 2458 {
2464 margs->matchoff = (int)(st->orgpat.regmatch.startp[0] - 2459 margs->matchoff = (int)(st->orgpat->regmatch.startp[0] -
2465 tagpp->tagname); 2460 tagpp->tagname);
2466 if (st->orgpat.regmatch.rm_ic) 2461 if (st->orgpat->regmatch.rm_ic)
2467 { 2462 {
2468 st->orgpat.regmatch.rm_ic = FALSE; 2463 st->orgpat->regmatch.rm_ic = FALSE;
2469 margs->match_no_ic = vim_regexec(&st->orgpat.regmatch, 2464 margs->match_no_ic = vim_regexec(&st->orgpat->regmatch,
2470 tagpp->tagname, (colnr_T)0); 2465 tagpp->tagname, (colnr_T)0);
2471 st->orgpat.regmatch.rm_ic = TRUE; 2466 st->orgpat->regmatch.rm_ic = TRUE;
2472 } 2467 }
2473 } 2468 }
2474 *tagpp->tagname_end = cc; 2469 *tagpp->tagname_end = cc;
2475 margs->match_re = TRUE; 2470 margs->match_re = TRUE;
2476 } 2471 }
2569 if (is_current) 2564 if (is_current)
2570 mtt = MT_GL_CUR; 2565 mtt = MT_GL_CUR;
2571 else 2566 else
2572 mtt = MT_GL_OTH; 2567 mtt = MT_GL_OTH;
2573 } 2568 }
2574 if (st->orgpat.regmatch.rm_ic && !margs->match_no_ic) 2569 if (st->orgpat->regmatch.rm_ic && !margs->match_no_ic)
2575 mtt += MT_IC_OFF; 2570 mtt += MT_IC_OFF;
2576 if (margs->match_re) 2571 if (margs->match_re)
2577 mtt += MT_RE_OFF; 2572 mtt += MT_RE_OFF;
2578 } 2573 }
2579 2574
2855 // different 2850 // different
2856 search_info.curr_offset = 0; 2851 search_info.curr_offset = 0;
2857 continue; 2852 continue;
2858 } 2853 }
2859 2854
2860 if (findtags_parse_line(st, &tagp) == FAIL) 2855 retval = findtags_parse_line(st, &tagp, margs, &search_info);
2861 {
2862 semsg(_(e_format_error_in_tags_file_str), st->tag_fname);
2863 #ifdef FEAT_CSCOPE
2864 if (!use_cscope)
2865 #endif
2866 semsg(_("Before byte %ld"), (long)vim_ftell(st->fp));
2867 st->stop_searching = TRUE;
2868 return;
2869 }
2870
2871 retval = findtags_match_tag(st, &tagp, margs, &search_info);
2872 if (retval == TAG_MATCH_NEXT) 2856 if (retval == TAG_MATCH_NEXT)
2873 continue; 2857 continue;
2874 if (retval == TAG_MATCH_STOP) 2858 if (retval == TAG_MATCH_STOP)
2875 break; 2859 break;
2860 if (retval == TAG_MATCH_FAIL)
2861 {
2862 semsg(_(e_format_error_in_tags_file_str), st->tag_fname);
2863 #ifdef FEAT_CSCOPE
2864 if (!use_cscope)
2865 #endif
2866 semsg(_("Before byte %ld"), (long)vim_ftell(st->fp));
2867 st->stop_searching = TRUE;
2868 return;
2869 }
2870
2871 retval = findtags_match_tag(st, &tagp, margs);
2872 if (retval == TAG_MATCH_NEXT)
2873 continue;
2874 if (retval == TAG_MATCH_STOP)
2875 break;
2876 2876
2877 // If a match is found, add it to ht_match[] and ga_match[]. 2877 // If a match is found, add it to ht_match[] and ga_match[].
2878 if (retval == TAG_MATCH_SUCCESS) 2878 if (retval == TAG_MATCH_SUCCESS)
2879 { 2879 {
2880 if (findtags_add_match(st, &tagp, margs, buf_ffname, &hash) 2880 if (findtags_add_match(st, &tagp, margs, buf_ffname, &hash)
2893 static void 2893 static void
2894 findtags_in_file(findtags_state_T *st, char_u *buf_ffname) 2894 findtags_in_file(findtags_state_T *st, char_u *buf_ffname)
2895 { 2895 {
2896 findtags_match_args_T margs; 2896 findtags_match_args_T margs;
2897 #ifdef FEAT_CSCOPE 2897 #ifdef FEAT_CSCOPE
2898 int use_cscope = FALSE; 2898 int use_cscope = (st->flags & TAG_CSCOPE);
2899 #endif 2899 #endif
2900 2900
2901 st->vimconv.vc_type = CONV_NONE; 2901 st->vimconv.vc_type = CONV_NONE;
2902 st->tag_file_sorted = NUL; 2902 st->tag_file_sorted = NUL;
2903 st->fp = NULL; 2903 st->fp = NULL;
2904 findtags_matchargs_init(&margs, st->flags); 2904 findtags_matchargs_init(&margs, st->flags);
2905 2905
2906 // A file that doesn't exist is silently ignored. Only when not a 2906 // A file that doesn't exist is silently ignored. Only when not a
2907 // single file is found, an error message is given (further on). 2907 // single file is found, an error message is given (further on).
2908 #ifdef FEAT_CSCOPE 2908 #ifdef FEAT_CSCOPE
2909 use_cscope = (st->flags & TAG_CSCOPE);
2910 if (use_cscope) 2909 if (use_cscope)
2911 st->fp = NULL; // avoid GCC warning 2910 st->fp = NULL; // avoid GCC warning
2912 else 2911 else
2913 #endif 2912 #endif
2914 { 2913 {
3109 #ifdef FEAT_MULTI_LANG 3108 #ifdef FEAT_MULTI_LANG
3110 if (curbuf->b_help) 3109 if (curbuf->b_help)
3111 { 3110 {
3112 // When "@ab" is specified use only the "ab" language, otherwise 3111 // When "@ab" is specified use only the "ab" language, otherwise
3113 // search all languages. 3112 // search all languages.
3114 if (st.orgpat.len > 3 && pat[st.orgpat.len - 3] == '@' 3113 if (st.orgpat->len > 3 && pat[st.orgpat->len - 3] == '@'
3115 && ASCII_ISALPHA(pat[st.orgpat.len - 2]) 3114 && ASCII_ISALPHA(pat[st.orgpat->len - 2])
3116 && ASCII_ISALPHA(pat[st.orgpat.len - 1])) 3115 && ASCII_ISALPHA(pat[st.orgpat->len - 1]))
3117 { 3116 {
3118 saved_pat = vim_strnsave(pat, st.orgpat.len - 3); 3117 saved_pat = vim_strnsave(pat, st.orgpat->len - 3);
3119 if (saved_pat != NULL) 3118 if (saved_pat != NULL)
3120 { 3119 {
3121 st.help_lang_find = &pat[st.orgpat.len - 2]; 3120 st.help_lang_find = &pat[st.orgpat->len - 2];
3122 st.orgpat.pat = saved_pat; 3121 st.orgpat->pat = saved_pat;
3123 st.orgpat.len -= 3; 3122 st.orgpat->len -= 3;
3124 } 3123 }
3125 } 3124 }
3126 } 3125 }
3127 #endif 3126 #endif
3128 if (p_tl != 0 && st.orgpat.len > p_tl) // adjust for 'taglength' 3127 if (p_tl != 0 && st.orgpat->len > p_tl) // adjust for 'taglength'
3129 st.orgpat.len = p_tl; 3128 st.orgpat->len = p_tl;
3130 3129
3131 save_emsg_off = emsg_off; 3130 save_emsg_off = emsg_off;
3132 emsg_off = TRUE; // don't want error for invalid RE here 3131 emsg_off = TRUE; // don't want error for invalid RE here
3133 prepare_pats(&st.orgpat, has_re); 3132 prepare_pats(st.orgpat, has_re);
3134 emsg_off = save_emsg_off; 3133 emsg_off = save_emsg_off;
3135 if (has_re && st.orgpat.regmatch.regprog == NULL) 3134 if (has_re && st.orgpat->regmatch.regprog == NULL)
3136 goto findtag_end; 3135 goto findtag_end;
3137 3136
3138 #ifdef FEAT_EVAL 3137 #ifdef FEAT_EVAL
3139 retval = findtags_apply_tfu(&st, pat, buf_ffname); 3138 retval = findtags_apply_tfu(&st, pat, buf_ffname);
3140 if (retval != NOTDONE) 3139 if (retval != NOTDONE)
3162 * string to look for, ignore case right away to avoid going though the 3161 * string to look for, ignore case right away to avoid going though the
3163 * tags files twice. 3162 * tags files twice.
3164 * When the tag file is case-fold sorted, it is either one or the other. 3163 * When the tag file is case-fold sorted, it is either one or the other.
3165 * Only ignore case when TAG_NOIC not used or 'ignorecase' set. 3164 * Only ignore case when TAG_NOIC not used or 'ignorecase' set.
3166 */ 3165 */
3167 st.orgpat.regmatch.rm_ic = ((p_ic || !noic) 3166 st.orgpat->regmatch.rm_ic = ((p_ic || !noic)
3168 && (findall || st.orgpat.headlen == 0 || !p_tbs)); 3167 && (findall || st.orgpat->headlen == 0 || !p_tbs));
3169 for (round = 1; round <= 2; ++round) 3168 for (round = 1; round <= 2; ++round)
3170 { 3169 {
3171 st.linear = (st.orgpat.headlen == 0 || !p_tbs || round == 2); 3170 st.linear = (st.orgpat->headlen == 0 || !p_tbs || round == 2);
3172 3171
3173 /* 3172 /*
3174 * Try tag file names from tags option one by one. 3173 * Try tag file names from tags option one by one.
3175 */ 3174 */
3176 for (first_file = TRUE; 3175 for (first_file = TRUE;
3198 tagname_free(&tn); 3197 tagname_free(&tn);
3199 3198
3200 // stop searching when already did a linear search, or when TAG_NOIC 3199 // stop searching when already did a linear search, or when TAG_NOIC
3201 // used, and 'ignorecase' not set or already did case-ignore search 3200 // used, and 'ignorecase' not set or already did case-ignore search
3202 if (st.stop_searching || st.linear || (!p_ic && noic) || 3201 if (st.stop_searching || st.linear || (!p_ic && noic) ||
3203 st.orgpat.regmatch.rm_ic) 3202 st.orgpat->regmatch.rm_ic)
3204 break; 3203 break;
3205 # ifdef FEAT_CSCOPE 3204 # ifdef FEAT_CSCOPE
3206 if (use_cscope) 3205 if (use_cscope)
3207 break; 3206 break;
3208 # endif 3207 # endif
3209 3208
3210 // try another time while ignoring case 3209 // try another time while ignoring case
3211 st.orgpat.regmatch.rm_ic = TRUE; 3210 st.orgpat->regmatch.rm_ic = TRUE;
3212 } 3211 }
3213 3212
3214 if (!st.stop_searching) 3213 if (!st.stop_searching)
3215 { 3214 {
3216 if (!st.did_open && verbose) // never opened any tags file 3215 if (!st.did_open && verbose) // never opened any tags file