Skip to content

Commit 98e83b2

Browse files
committed
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.
1 parent e0c31f6 commit 98e83b2

File tree

3 files changed

+58
-28
lines changed

3 files changed

+58
-28
lines changed

src/tag.c

Lines changed: 32 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,10 @@ typedef struct tag_pointers
3535
} tagptrs_T;
3636

3737
/*
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
3939
* 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
4142
* sorted on priority.
4243
*/
4344
#define MT_ST_CUR 0 /* static match in current file */
@@ -1339,7 +1340,8 @@ find_tags(
13391340
#endif
13401341

13411342
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 */
13431345
hash_T hash = 0;
13441346
int match_count = 0; /* number of matches found */
13451347
char_u **matches;
@@ -1405,7 +1407,10 @@ find_tags(
14051407
ebuf = alloc(LSIZE);
14061408
#endif
14071409
for (mtt = 0; mtt < MT_COUNT; ++mtt)
1410+
{
1411+
ga_init2(&ga_match[mtt], (int)sizeof(char_u *), 100);
14081412
hash_init(&ht_match[mtt]);
1413+
}
14091414

14101415
/* check for out of memory situation */
14111416
if (lbuf == NULL || tag_fname == NULL
@@ -2213,7 +2218,7 @@ find_tags(
22132218
}
22142219

22152220
/*
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[].
22172222
*/
22182223
if (match)
22192224
{
@@ -2271,7 +2276,7 @@ find_tags(
22712276
}
22722277

22732278
/*
2274-
* Add the found match in ht_match[mtt].
2279+
* Add the found match in ht_match[mtt] and ga_match[mtt].
22752280
* Store the info we need later, which depends on the kind of
22762281
* tags we are dealing with.
22772282
*/
@@ -2423,15 +2428,20 @@ find_tags(
24232428
if (HASHITEM_EMPTY(hi))
24242429
{
24252430
if (hash_add_item(&ht_match[mtt], hi, mfp, hash)
2426-
== FAIL)
2431+
== FAIL
2432+
|| ga_grow(&ga_match[mtt], 1) != OK)
24272433
{
24282434
/* Out of memory! Just forget about the rest. */
24292435
retval = OK;
24302436
stop_searching = TRUE;
24312437
break;
24322438
}
24332439
else
2440+
{
2441+
((char_u **)(ga_match[mtt].ga_data))
2442+
[ga_match[mtt].ga_len++] = mfp;
24342443
++match_count;
2444+
}
24352445
}
24362446
else
24372447
/* duplicate tag, drop it */
@@ -2533,7 +2543,7 @@ find_tags(
25332543
#endif
25342544

25352545
/*
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
25372547
* matches. When retval == FAIL, free the matches.
25382548
*/
25392549
if (retval == FAIL)
@@ -2547,34 +2557,28 @@ find_tags(
25472557
match_count = 0;
25482558
for (mtt = 0; mtt < MT_COUNT; ++mtt)
25492559
{
2550-
hashitem_T *hi;
2551-
long_u todo;
2552-
2553-
todo = (long)ht_match[mtt].ht_used;
2554-
for (hi = ht_match[mtt].ht_array; todo > 0; ++hi)
2560+
for (i = 0; i < ga_match[mtt].ga_len; ++i)
25552561
{
2556-
if (!HASHITEM_EMPTY(hi))
2562+
mfp = ((char_u **)(ga_match[mtt].ga_data))[i];
2563+
if (matches == NULL)
2564+
vim_free(mfp);
2565+
else
25572566
{
2558-
mfp = hi->hi_key;
2559-
if (matches == NULL)
2560-
vim_free(mfp);
2561-
else
2567+
if (!name_only)
25622568
{
2563-
if (!name_only)
2564-
{
2565-
/* Change mtt back to zero-based. */
2566-
*mfp = *mfp - 1;
2569+
/* Change mtt back to zero-based. */
2570+
*mfp = *mfp - 1;
25672571

2568-
/* change the TAG_SEP back to NUL */
2569-
for (p = mfp + 1; *p != NUL; ++p)
2570-
if (*p == TAG_SEP)
2571-
*p = NUL;
2572-
}
2573-
matches[match_count++] = (char_u *)mfp;
2572+
/* change the TAG_SEP back to NUL */
2573+
for (p = mfp + 1; *p != NUL; ++p)
2574+
if (*p == TAG_SEP)
2575+
*p = NUL;
25742576
}
2575-
todo--;
2577+
matches[match_count++] = (char_u *)mfp;
25762578
}
25772579
}
2580+
2581+
ga_clear(&ga_match[mtt]);
25782582
hash_clear(&ht_match[mtt]);
25792583
}
25802584

src/testdir/test_tagjump.vim

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,34 @@ func Test_static_tagjump()
3535
tag one
3636
call assert_equal(2, line('.'))
3737

38+
bwipe!
3839
set tags&
3940
call delete('Xtags')
4041
call delete('Xfile1')
42+
endfunc
43+
44+
func Test_duplicate_tagjump()
45+
set tags=Xtags
46+
call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
47+
\ "thesame\tXfile1\t1;\"\td\tfile:",
48+
\ "thesame\tXfile1\t2;\"\td\tfile:",
49+
\ "thesame\tXfile1\t3;\"\td\tfile:",
50+
\ ],
51+
\ 'Xtags')
52+
new Xfile1
53+
call setline(1, ['thesame one', 'thesame two', 'thesame three'])
54+
write
55+
tag thesame
56+
call assert_equal(1, line('.'))
57+
tnext
58+
call assert_equal(2, line('.'))
59+
tnext
60+
call assert_equal(3, line('.'))
61+
4162
bwipe!
63+
set tags&
64+
call delete('Xtags')
65+
call delete('Xfile1')
4266
endfunc
4367

4468
" Tests for [ CTRL-I and CTRL-W CTRL-I commands

src/version.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -764,6 +764,8 @@ static char *(features[]) =
764764

765765
static int included_patches[] =
766766
{ /* Add new patch number below this line */
767+
/**/
768+
393,
767769
/**/
768770
392,
769771
/**/

0 commit comments

Comments
 (0)