Mercurial > vim
comparison src/tag.c @ 11008:0ecd07cd2e43 v8.0.0393
patch 8.0.0393: order of duplicate tags is not preserved
commit https://github.com/vim/vim/commit/98e83b295628bc29bc67bcc1adb8ae75d01b8e07
Author: Bram Moolenaar <Bram@vim.org>
Date: Wed Mar 1 15:45:05 2017 +0100
patch 8.0.0393: order of duplicate tags is not preserved
Problem: When the same tag appears more than once, the order is
unpredictable. (Charles Campbell)
Solution: Besides using a dict for finding duplicates, use a grow array for
keeping the tags in sequence.
author | Christian Brabandt <cb@256bit.org> |
---|---|
date | Wed, 01 Mar 2017 16:00:05 +0100 |
parents | ed6f03535745 |
children | 778c10516955 |
comparison
equal
deleted
inserted
replaced
11007:14a0d274348d | 11008:0ecd07cd2e43 |
---|---|
33 char_u *tagkind; /* "kind:" value */ | 33 char_u *tagkind; /* "kind:" value */ |
34 char_u *tagkind_end; /* end of tagkind */ | 34 char_u *tagkind_end; /* end of tagkind */ |
35 } tagptrs_T; | 35 } tagptrs_T; |
36 | 36 |
37 /* | 37 /* |
38 * The matching tags are first stored in one of the ht_match[] hash tables. In | 38 * The matching tags are first stored in one of the hash tables. In |
39 * which one depends on the priority of the match. | 39 * which one depends on the priority of the match. |
40 * At the end, all the matches from ht_match[] are concatenated, to make a list | 40 * ht_match[] is used to find duplicates, ga_match[] to keep them in sequence. |
41 * At the end, all the matches from ga_match[] are concatenated, to make a list | |
41 * sorted on priority. | 42 * sorted on priority. |
42 */ | 43 */ |
43 #define MT_ST_CUR 0 /* static match in current file */ | 44 #define MT_ST_CUR 0 /* static match in current file */ |
44 #define MT_GL_CUR 1 /* global match in current file */ | 45 #define MT_GL_CUR 1 /* global match in current file */ |
45 #define MT_GL_OTH 2 /* global match in other file */ | 46 #define MT_GL_OTH 2 /* global match in other file */ |
1337 char_u *ebuf; /* additional buffer for etag fname */ | 1338 char_u *ebuf; /* additional buffer for etag fname */ |
1338 int is_etag; /* current file is emaces style */ | 1339 int is_etag; /* current file is emaces style */ |
1339 #endif | 1340 #endif |
1340 | 1341 |
1341 char_u *mfp; | 1342 char_u *mfp; |
1342 hashtab_T ht_match[MT_COUNT]; | 1343 garray_T ga_match[MT_COUNT]; /* stores matches in sequence */ |
1344 hashtab_T ht_match[MT_COUNT]; /* stores matches by key */ | |
1343 hash_T hash = 0; | 1345 hash_T hash = 0; |
1344 int match_count = 0; /* number of matches found */ | 1346 int match_count = 0; /* number of matches found */ |
1345 char_u **matches; | 1347 char_u **matches; |
1346 int mtt; | 1348 int mtt; |
1347 int help_save; | 1349 int help_save; |
1403 tag_fname = alloc(MAXPATHL + 1); | 1405 tag_fname = alloc(MAXPATHL + 1); |
1404 #ifdef FEAT_EMACS_TAGS | 1406 #ifdef FEAT_EMACS_TAGS |
1405 ebuf = alloc(LSIZE); | 1407 ebuf = alloc(LSIZE); |
1406 #endif | 1408 #endif |
1407 for (mtt = 0; mtt < MT_COUNT; ++mtt) | 1409 for (mtt = 0; mtt < MT_COUNT; ++mtt) |
1410 { | |
1411 ga_init2(&ga_match[mtt], (int)sizeof(char_u *), 100); | |
1408 hash_init(&ht_match[mtt]); | 1412 hash_init(&ht_match[mtt]); |
1413 } | |
1409 | 1414 |
1410 /* check for out of memory situation */ | 1415 /* check for out of memory situation */ |
1411 if (lbuf == NULL || tag_fname == NULL | 1416 if (lbuf == NULL || tag_fname == NULL |
1412 #ifdef FEAT_EMACS_TAGS | 1417 #ifdef FEAT_EMACS_TAGS |
1413 || ebuf == NULL | 1418 || ebuf == NULL |
2211 *tagp.tagname_end = cc; | 2216 *tagp.tagname_end = cc; |
2212 match_re = TRUE; | 2217 match_re = TRUE; |
2213 } | 2218 } |
2214 | 2219 |
2215 /* | 2220 /* |
2216 * If a match is found, add it to ht_match[]. | 2221 * If a match is found, add it to ht_match[] and ga_match[]. |
2217 */ | 2222 */ |
2218 if (match) | 2223 if (match) |
2219 { | 2224 { |
2220 int len = 0; | 2225 int len = 0; |
2221 | 2226 |
2269 if (match_re) | 2274 if (match_re) |
2270 mtt += MT_RE_OFF; | 2275 mtt += MT_RE_OFF; |
2271 } | 2276 } |
2272 | 2277 |
2273 /* | 2278 /* |
2274 * Add the found match in ht_match[mtt]. | 2279 * Add the found match in ht_match[mtt] and ga_match[mtt]. |
2275 * Store the info we need later, which depends on the kind of | 2280 * Store the info we need later, which depends on the kind of |
2276 * tags we are dealing with. | 2281 * tags we are dealing with. |
2277 */ | 2282 */ |
2278 if (help_only) | 2283 if (help_only) |
2279 { | 2284 { |
2421 hash = hash_hash(mfp); | 2426 hash = hash_hash(mfp); |
2422 hi = hash_lookup(&ht_match[mtt], mfp, hash); | 2427 hi = hash_lookup(&ht_match[mtt], mfp, hash); |
2423 if (HASHITEM_EMPTY(hi)) | 2428 if (HASHITEM_EMPTY(hi)) |
2424 { | 2429 { |
2425 if (hash_add_item(&ht_match[mtt], hi, mfp, hash) | 2430 if (hash_add_item(&ht_match[mtt], hi, mfp, hash) |
2426 == FAIL) | 2431 == FAIL |
2432 || ga_grow(&ga_match[mtt], 1) != OK) | |
2427 { | 2433 { |
2428 /* Out of memory! Just forget about the rest. */ | 2434 /* Out of memory! Just forget about the rest. */ |
2429 retval = OK; | 2435 retval = OK; |
2430 stop_searching = TRUE; | 2436 stop_searching = TRUE; |
2431 break; | 2437 break; |
2432 } | 2438 } |
2433 else | 2439 else |
2440 { | |
2441 ((char_u **)(ga_match[mtt].ga_data)) | |
2442 [ga_match[mtt].ga_len++] = mfp; | |
2434 ++match_count; | 2443 ++match_count; |
2444 } | |
2435 } | 2445 } |
2436 else | 2446 else |
2437 /* duplicate tag, drop it */ | 2447 /* duplicate tag, drop it */ |
2438 vim_free(mfp); | 2448 vim_free(mfp); |
2439 } | 2449 } |
2531 #ifdef FEAT_EMACS_TAGS | 2541 #ifdef FEAT_EMACS_TAGS |
2532 vim_free(ebuf); | 2542 vim_free(ebuf); |
2533 #endif | 2543 #endif |
2534 | 2544 |
2535 /* | 2545 /* |
2536 * Move the matches from the ht_match[] arrays into one list of | 2546 * Move the matches from the ga_match[] arrays into one list of |
2537 * matches. When retval == FAIL, free the matches. | 2547 * matches. When retval == FAIL, free the matches. |
2538 */ | 2548 */ |
2539 if (retval == FAIL) | 2549 if (retval == FAIL) |
2540 match_count = 0; | 2550 match_count = 0; |
2541 | 2551 |
2545 else | 2555 else |
2546 matches = NULL; | 2556 matches = NULL; |
2547 match_count = 0; | 2557 match_count = 0; |
2548 for (mtt = 0; mtt < MT_COUNT; ++mtt) | 2558 for (mtt = 0; mtt < MT_COUNT; ++mtt) |
2549 { | 2559 { |
2550 hashitem_T *hi; | 2560 for (i = 0; i < ga_match[mtt].ga_len; ++i) |
2551 long_u todo; | 2561 { |
2552 | 2562 mfp = ((char_u **)(ga_match[mtt].ga_data))[i]; |
2553 todo = (long)ht_match[mtt].ht_used; | 2563 if (matches == NULL) |
2554 for (hi = ht_match[mtt].ht_array; todo > 0; ++hi) | 2564 vim_free(mfp); |
2555 { | 2565 else |
2556 if (!HASHITEM_EMPTY(hi)) | 2566 { |
2557 { | 2567 if (!name_only) |
2558 mfp = hi->hi_key; | 2568 { |
2559 if (matches == NULL) | 2569 /* Change mtt back to zero-based. */ |
2560 vim_free(mfp); | 2570 *mfp = *mfp - 1; |
2561 else | 2571 |
2562 { | 2572 /* change the TAG_SEP back to NUL */ |
2563 if (!name_only) | 2573 for (p = mfp + 1; *p != NUL; ++p) |
2564 { | 2574 if (*p == TAG_SEP) |
2565 /* Change mtt back to zero-based. */ | 2575 *p = NUL; |
2566 *mfp = *mfp - 1; | 2576 } |
2567 | 2577 matches[match_count++] = (char_u *)mfp; |
2568 /* change the TAG_SEP back to NUL */ | 2578 } |
2569 for (p = mfp + 1; *p != NUL; ++p) | 2579 } |
2570 if (*p == TAG_SEP) | 2580 |
2571 *p = NUL; | 2581 ga_clear(&ga_match[mtt]); |
2572 } | |
2573 matches[match_count++] = (char_u *)mfp; | |
2574 } | |
2575 todo--; | |
2576 } | |
2577 } | |
2578 hash_clear(&ht_match[mtt]); | 2582 hash_clear(&ht_match[mtt]); |
2579 } | 2583 } |
2580 | 2584 |
2581 *matchesp = matches; | 2585 *matchesp = matches; |
2582 *num_matches = match_count; | 2586 *num_matches = match_count; |