Mercurial > vim
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 |