changeset 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 14a0d274348d
children 1a8893be0bea
files src/tag.c src/testdir/test_tagjump.vim src/version.c
diffstat 3 files changed, 59 insertions(+), 29 deletions(-) [+]
line wrap: on
line diff
--- a/src/tag.c
+++ b/src/tag.c
@@ -35,9 +35,10 @@ typedef struct tag_pointers
 } tagptrs_T;
 
 /*
- * The matching tags are first stored in one of the ht_match[] hash tables.  In
+ * The matching tags are first stored in one of the hash tables.  In
  * which one depends on the priority of the match.
- * At the end, all the matches from ht_match[] are concatenated, to make a list
+ * ht_match[] is used to find duplicates, ga_match[] to keep them in sequence.
+ * At the end, all the matches from ga_match[] are concatenated, to make a list
  * sorted on priority.
  */
 #define MT_ST_CUR	0		/* static match in current file */
@@ -1339,7 +1340,8 @@ find_tags(
 #endif
 
     char_u	*mfp;
-    hashtab_T	ht_match[MT_COUNT];
+    garray_T	ga_match[MT_COUNT];	/* stores matches in sequence */
+    hashtab_T	ht_match[MT_COUNT];	/* stores matches by key */
     hash_T	hash = 0;
     int		match_count = 0;		/* number of matches found */
     char_u	**matches;
@@ -1405,7 +1407,10 @@ find_tags(
     ebuf = alloc(LSIZE);
 #endif
     for (mtt = 0; mtt < MT_COUNT; ++mtt)
+    {
+	ga_init2(&ga_match[mtt], (int)sizeof(char_u *), 100);
 	hash_init(&ht_match[mtt]);
+    }
 
     /* check for out of memory situation */
     if (lbuf == NULL || tag_fname == NULL
@@ -2213,7 +2218,7 @@ parse_line:
 	    }
 
 	    /*
-	     * If a match is found, add it to ht_match[].
+	     * If a match is found, add it to ht_match[] and ga_match[].
 	     */
 	    if (match)
 	    {
@@ -2271,7 +2276,7 @@ parse_line:
 		}
 
 		/*
-		 * Add the found match in ht_match[mtt].
+		 * Add the found match in ht_match[mtt] and ga_match[mtt].
 		 * Store the info we need later, which depends on the kind of
 		 * tags we are dealing with.
 		 */
@@ -2423,7 +2428,8 @@ parse_line:
 		    if (HASHITEM_EMPTY(hi))
 		    {
 			if (hash_add_item(&ht_match[mtt], hi, mfp, hash)
-								       == FAIL)
+								       == FAIL
+				       || ga_grow(&ga_match[mtt], 1) != OK)
 			{
 			    /* Out of memory! Just forget about the rest. */
 			    retval = OK;
@@ -2431,7 +2437,11 @@ parse_line:
 			    break;
 			}
 			else
+			{
+			    ((char_u **)(ga_match[mtt].ga_data))
+						[ga_match[mtt].ga_len++] = mfp;
 			    ++match_count;
+			}
 		    }
 		    else
 			/* duplicate tag, drop it */
@@ -2533,7 +2543,7 @@ findtag_end:
 #endif
 
     /*
-     * Move the matches from the ht_match[] arrays into one list of
+     * Move the matches from the ga_match[] arrays into one list of
      * matches.  When retval == FAIL, free the matches.
      */
     if (retval == FAIL)
@@ -2547,34 +2557,28 @@ findtag_end:
     match_count = 0;
     for (mtt = 0; mtt < MT_COUNT; ++mtt)
     {
-	hashitem_T	*hi;
-	long_u		todo;
-
-	todo = (long)ht_match[mtt].ht_used;
-	for (hi = ht_match[mtt].ht_array; todo > 0; ++hi)
+	for (i = 0; i < ga_match[mtt].ga_len; ++i)
 	{
-	    if (!HASHITEM_EMPTY(hi))
+	    mfp = ((char_u **)(ga_match[mtt].ga_data))[i];
+	    if (matches == NULL)
+		vim_free(mfp);
+	    else
 	    {
-		mfp = hi->hi_key;
-		if (matches == NULL)
-		    vim_free(mfp);
-		else
+		if (!name_only)
 		{
-		    if (!name_only)
-		    {
-			/* Change mtt back to zero-based. */
-			*mfp = *mfp - 1;
-
-			/* change the TAG_SEP back to NUL */
-			for (p = mfp + 1; *p != NUL; ++p)
-			    if (*p == TAG_SEP)
-				*p = NUL;
-		    }
-		    matches[match_count++] = (char_u *)mfp;
+		    /* Change mtt back to zero-based. */
+		    *mfp = *mfp - 1;
+
+		    /* change the TAG_SEP back to NUL */
+		    for (p = mfp + 1; *p != NUL; ++p)
+			if (*p == TAG_SEP)
+			    *p = NUL;
 		}
-		todo--;
+		matches[match_count++] = (char_u *)mfp;
 	    }
 	}
+
+	ga_clear(&ga_match[mtt]);
 	hash_clear(&ht_match[mtt]);
     }
 
--- a/src/testdir/test_tagjump.vim
+++ b/src/testdir/test_tagjump.vim
@@ -35,10 +35,34 @@ func Test_static_tagjump()
   tag one
   call assert_equal(2, line('.'))
 
+  bwipe!
   set tags&
   call delete('Xtags')
   call delete('Xfile1')
+endfunc
+
+func Test_duplicate_tagjump()
+  set tags=Xtags
+  call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
+        \ "thesame\tXfile1\t1;\"\td\tfile:",
+        \ "thesame\tXfile1\t2;\"\td\tfile:",
+        \ "thesame\tXfile1\t3;\"\td\tfile:",
+        \ ],
+        \ 'Xtags')
+  new Xfile1
+  call setline(1, ['thesame one', 'thesame two', 'thesame three'])
+  write
+  tag thesame
+  call assert_equal(1, line('.'))
+  tnext
+  call assert_equal(2, line('.'))
+  tnext
+  call assert_equal(3, line('.'))
+
   bwipe!
+  set tags&
+  call delete('Xtags')
+  call delete('Xfile1')
 endfunc
 
 " Tests for [ CTRL-I and CTRL-W CTRL-I commands
--- a/src/version.c
+++ b/src/version.c
@@ -765,6 +765,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    393,
+/**/
     392,
 /**/
     391,