summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorYegappan Lakshmanan <yegappan@yahoo.com>2022-03-05 14:35:12 +0000
committerBram Moolenaar <Bram@vim.org>2022-03-05 14:35:12 +0000
commitdf1bbea436636ac227d33dd79f77e07f4fffb028 (patch)
tree1fe4acce1bba5825b1c33b5c7a022d6db9dc6a51 /src
parentc12dc47b8f80c02e4fdcdb38b8217e1c06a11368 (diff)
downloadvim-git-df1bbea436636ac227d33dd79f77e07f4fffb028.tar.gz
patch 8.2.4512: the find_tags_in_file() function is much too longv8.2.4512
Problem: The find_tags_in_file() function is much too long. Solution: Refactor into multiple smaller functions. (Yegappan Lakshmanan, closes #9892)
Diffstat (limited to 'src')
-rw-r--r--src/Makefile1
-rw-r--r--src/quickfix.c3
-rw-r--r--src/tag.c1211
-rw-r--r--src/testdir/test83-tags22
-rw-r--r--src/testdir/test83-tags3102
-rw-r--r--src/testdir/test_tagjump.vim45
-rw-r--r--src/version.c2
7 files changed, 720 insertions, 646 deletions
diff --git a/src/Makefile b/src/Makefile
index 24d39e8d0..5eae1a47f 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -3010,7 +3010,6 @@ shadow: runtime pixmaps
../../testdir/sautest \
../../testdir/samples \
../../testdir/dumps \
- ../../testdir/test83-tags? \
../../testdir/*.ok \
../../testdir/testluaplugin \
.
diff --git a/src/quickfix.c b/src/quickfix.c
index db607171a..0b6a5a3a6 100644
--- a/src/quickfix.c
+++ b/src/quickfix.c
@@ -8106,8 +8106,7 @@ hgr_search_file(
// Convert a line if 'encoding' is not utf-8 and
// the line contains a non-ASCII character.
- if (p_vc->vc_type != CONV_NONE
- && has_non_ascii(IObuff))
+ if (p_vc->vc_type != CONV_NONE && has_non_ascii(IObuff))
{
line = string_convert(p_vc, IObuff, NULL);
if (line == NULL)
diff --git a/src/tag.c b/src/tag.c
index a7ffa498f..76558cb9e 100644
--- a/src/tag.c
+++ b/src/tag.c
@@ -1580,8 +1580,11 @@ find_tagfunc_tags(
* State information used during a tag search
*/
typedef struct {
- char_u *tag_fname; // name of tag file
+ char_u *tag_fname; // name of the tag file
pat_T orgpat; // holds unconverted pattern info
+ int name_only; // get only tag names
+ int get_searchpat; // used for 'showfulltag'
+ int help_only; // only search for help tags
#ifdef FEAT_MULTI_LANG
char_u *help_lang_find; // lang to be found
int is_txt; // flag of file extension
@@ -1607,7 +1610,11 @@ typedef struct {
* Initialize the state used by find_tags()
*/
static int
-findtags_state_init(findtags_state_T *st, char_u *pat, int mincount)
+findtags_state_init(
+ findtags_state_T *st,
+ char_u *pat,
+ int flags,
+ int mincount)
{
int mtt;
@@ -1615,6 +1622,9 @@ findtags_state_init(findtags_state_T *st, char_u *pat, int mincount)
st->orgpat.pat = pat;
st->orgpat.len = (int)STRLEN(pat);
st->orgpat.regmatch.regprog = NULL;
+ st->help_only = (flags & TAG_HELP);
+ st->name_only = (flags & TAG_NAMES);
+ st->get_searchpat = FALSE;
#ifdef FEAT_MULTI_LANG
st->help_lang_find = NULL;
st->is_txt = FALSE;
@@ -1648,6 +1658,592 @@ findtags_state_init(findtags_state_T *st, char_u *pat, int mincount)
}
/*
+ * Free the state used by find_tags()
+ */
+ static void
+findtags_state_free(findtags_state_T *st)
+{
+ vim_free(st->tag_fname);
+ vim_free(st->lbuf);
+ vim_regfree(st->orgpat.regmatch.regprog);
+#ifdef FEAT_EMACS_TAGS
+ vim_free(st->ebuf);
+#endif
+}
+
+#ifdef FEAT_MULTI_LANG
+/*
+ * Initialize the state for searching tags in a Vim help file.
+ * Returns TRUE to process the help file and FALSE to skip the file.
+ */
+ static int
+findtags_in_help_init(
+ findtags_state_T *st,
+ int flags,
+ char_u *help_lang,
+ int *help_pri)
+{
+ int i;
+ char_u *s;
+
+ // Keep en if the file extension is .txt
+ if (st->is_txt)
+ STRCPY(help_lang, "en");
+ else
+ {
+ // Prefer help tags according to 'helplang'. Put the
+ // two-letter language name in help_lang[].
+ i = (int)STRLEN(st->tag_fname);
+ if (i > 3 && st->tag_fname[i - 3] == '-')
+ STRCPY(help_lang, st->tag_fname + i - 2);
+ else
+ STRCPY(help_lang, "en");
+ }
+ // When searching for a specific language skip tags files
+ // for other languages.
+ if (st->help_lang_find != NULL
+ && STRICMP(help_lang, st->help_lang_find) != 0)
+ return FALSE;
+
+ // For CTRL-] in a help file prefer a match with the same
+ // language.
+ if ((flags & TAG_KEEP_LANG)
+ && st->help_lang_find == NULL
+ && curbuf->b_fname != NULL
+ && (i = (int)STRLEN(curbuf->b_fname)) > 4
+ && curbuf->b_fname[i - 1] == 'x'
+ && curbuf->b_fname[i - 4] == '.'
+ && STRNICMP(curbuf->b_fname + i - 3, help_lang, 2) == 0)
+ *help_pri = 0;
+ else
+ {
+ *help_pri = 1;
+ for (s = p_hlg; *s != NUL; ++s)
+ {
+ if (STRNICMP(s, help_lang, 2) == 0)
+ break;
+ ++*help_pri;
+ if ((s = vim_strchr(s, ',')) == NULL)
+ break;
+ }
+ if (s == NULL || *s == NUL)
+ {
+ // Language not in 'helplang': use last, prefer English,
+ // unless found already.
+ ++*help_pri;
+ if (STRICMP(help_lang, "en") != 0)
+ ++*help_pri;
+ }
+ }
+
+ return TRUE;
+}
+#endif
+
+#ifdef FEAT_EVAL
+/*
+ * Use the 'tagfunc' (if configured and enabled) to get the tags.
+ * Return OK if at least 1 tag has been successfully found, NOTDONE if the
+ * 'tagfunc' is not used or the 'tagfunc' returns v:null and FAIL otherwise.
+ */
+ static int
+findtags_apply_tfu(
+ char_u *pat,
+ findtags_state_T *st,
+ int flags,
+ char_u *buf_ffname)
+{
+ int use_tfu = ((flags & TAG_NO_TAGFUNC) == 0);
+ int retval;
+
+ if (!use_tfu || tfu_in_use || *curbuf->b_p_tfu == NUL)
+ return NOTDONE;
+
+ tfu_in_use = TRUE;
+ retval = find_tagfunc_tags(pat, st->ga_match, &st->match_count,
+ flags, buf_ffname);
+ tfu_in_use = FALSE;
+
+ return retval;
+}
+#endif
+
+#ifdef FEAT_EMACS_TAGS
+/*
+ * Stack for included emacs-tags file.
+ * It has a fixed size, to truncate cyclic includes. jw
+ */
+# define INCSTACK_SIZE 42
+static struct
+{
+ FILE *fp;
+ char_u *etag_fname;
+} incstack[INCSTACK_SIZE];
+static int incstack_idx = 0; // index in incstack
+
+/*
+ * Free the include tags file stack.
+ */
+ static void
+emacs_tags_incstack_free(void)
+{
+ while (incstack_idx)
+ {
+ --incstack_idx;
+ fclose(incstack[incstack_idx].fp);
+ incstack[incstack_idx].fp = NULL;
+ VIM_CLEAR(incstack[incstack_idx].etag_fname);
+ }
+}
+
+/*
+ * Emacs tags line with CTRL-L: New file name on next line.
+ * The file name is followed by a ','. Remember etag file name in ebuf.
+ * Returns a FILE pointer to the tags file. If another tags file is included,
+ * then returns a pointer to the new tags file. The old file pointer is saved
+ * in incstack.
+ */
+ static FILE *
+emacs_tags_new_filename(findtags_state_T *st, FILE *fp, int *is_etag)
+{
+ char_u *p;
+ char_u *fullpath_ebuf;
+
+ if (vim_fgets(st->ebuf, LSIZE, fp))
+ return fp;
+
+ for (p = st->ebuf; *p && *p != ','; p++)
+ ;
+ *p = NUL;
+
+ // check for an included tags file.
+ // atoi(p+1) is the number of bytes before the next ^L unless it is an
+ // include statement. Skip the included tags file if it exceeds the
+ // maximum.
+ if (STRNCMP(p + 1, "include", 7) != 0 || incstack_idx >= INCSTACK_SIZE)
+ return fp;
+
+ // Save current "fp" and "tag_fname" in the stack.
+ incstack[incstack_idx].etag_fname = vim_strsave(st->tag_fname);
+ if (incstack[incstack_idx].etag_fname == NULL)
+ return fp;
+
+ incstack[incstack_idx].fp = fp;
+ fp = NULL;
+
+ // Figure out "tag_fname" and "fp" to use for
+ // included file.
+ fullpath_ebuf = expand_tag_fname(st->ebuf, st->tag_fname, FALSE);
+ if (fullpath_ebuf != NULL)
+ {
+ fp = mch_fopen((char *)fullpath_ebuf, "r");
+ if (fp != NULL)
+ {
+ if (STRLEN(fullpath_ebuf) > LSIZE)
+ semsg(_(e_tag_file_path_truncated_for_str), st->ebuf);
+ vim_strncpy(st->tag_fname, fullpath_ebuf, MAXPATHL);
+ ++incstack_idx;
+ *is_etag = 0; // we can include anything
+ }
+ vim_free(fullpath_ebuf);
+ }
+ if (fp == NULL)
+ {
+ // Can't open the included file, skip it and
+ // restore old value of "fp".
+ fp = incstack[incstack_idx].fp;
+ vim_free(incstack[incstack_idx].etag_fname);
+ }
+
+ return fp;
+}
+
+/*
+ * Reached the end of an emacs-style tags file. If this is an included tags
+ * file, then pop it from the incstack and continue processing the parent tags
+ * file. Otherwise, processed all the tags.
+ * Returns TRUE if an included tags file is popped and processing should
+ * continue with the parent tags file. Otherwise returns FALSE.
+ */
+ static int
+emacs_tags_file_eof(findtags_state_T *st, FILE **fp)
+{
+ if (!incstack_idx) // reached end of file. stop processing.
+ return FALSE;
+
+ // reached the end of an included tags file. pop it.
+ --incstack_idx;
+ fclose(*fp); // end of this file ...
+ *fp = incstack[incstack_idx].fp;
+ STRCPY(st->tag_fname, incstack[incstack_idx].etag_fname);
+ vim_free(incstack[incstack_idx].etag_fname);
+
+ return TRUE;
+}
+
+/*
+ * Parse a line from an emacs-style tags file.
+ * Returns OK is the line is parsed successfully, otherwise FALSE.
+ */
+ static int
+emacs_tags_parse_line(char_u *lbuf, tagptrs_T *tagp)
+{
+ char_u *p_7f;
+ char_u *p;
+
+ // There are two formats for an emacs tag line:
+ // 1: struct EnvBase ^?EnvBase^A139,4627
+ // 2: #define ARPB_WILD_WORLD ^?153,5194
+ p_7f = vim_strchr(lbuf, 0x7f);
+ if (p_7f == NULL)
+ {
+etag_fail:
+ if (vim_strchr(lbuf, '\n') != NULL)
+ return FAIL;
+
+ // Truncated line. Ignore it.
+ if (p_verbose >= 5)
+ {
+ verbose_enter();
+ msg(_("Ignoring long line in tags file"));
+ verbose_leave();
+ }
+ tagp->command = lbuf;
+ tagp->tagname = lbuf;
+ tagp->tagname_end = lbuf;
+ return OK;
+ }
+
+ // Find ^A. If not found the line number is after the 0x7f
+ p = vim_strchr(p_7f, Ctrl_A);
+ if (p == NULL)
+ p = p_7f + 1;
+ else
+ ++p;
+
+ if (!VIM_ISDIGIT(*p)) // check for start of line number
+ goto etag_fail;
+ tagp->command = p;
+
+ if (p[-1] == Ctrl_A) // first format: explicit tagname given
+ {
+ tagp->tagname = p_7f + 1;
+ tagp->tagname_end = p - 1;
+ }
+ else // second format: isolate tagname
+ {
+ // find end of tagname
+ for (p = p_7f - 1; !vim_iswordc(*p); --p)
+ if (p == lbuf)
+ goto etag_fail;
+ tagp->tagname_end = p + 1;
+ while (p >= lbuf && vim_iswordc(*p))
+ --p;
+ tagp->tagname = p + 1;
+ }
+
+ return OK;
+}
+#endif
+
+/*
+ * Parse a tags file header line in 'st->lbuf'.
+ * Returns TRUE to read the next header line and FALSE to process the line.
+ */
+ static int
+tags_file_hdr_parse(findtags_state_T *st, vimconv_T *vcp, int *sorted_file)
+{
+ char_u *p;
+
+ if (STRNCMP(st->lbuf, "!_TAG_", 6) != 0)
+ // Non-header item before the header, e.g. "!" itself.
+ return FALSE;
+
+ // Read header line.
+#ifdef FEAT_TAG_BINS
+ if (STRNCMP(st->lbuf, "!_TAG_FILE_SORTED\t", 18) == 0)
+ *sorted_file = st->lbuf[18];
+#endif
+ if (STRNCMP(st->lbuf, "!_TAG_FILE_ENCODING\t", 20) == 0)
+ {
+ // Prepare to convert every line from the specified
+ // encoding to 'encoding'.
+ for (p = st->lbuf + 20; *p > ' ' && *p < 127; ++p)
+ ;
+ *p = NUL;
+ convert_setup(vcp, st->lbuf + 20, p_enc);
+ }
+
+ // Read the next line. Unrecognized flags are ignored.
+ return TRUE;
+}
+
+/*
+ * Convert the encoding of a line read from a tags file in 'st->lbuf'.
+ * Converting the pattern from 'enc' to the tags file encoding doesn't work,
+ * because characters are not recognized.
+ */
+ static void
+findtags_string_convert(findtags_state_T *st, vimconv_T *vcp)
+{
+ char_u *conv_line;
+ int len;
+
+ conv_line = string_convert(vcp, st->lbuf, NULL);
+ if (conv_line == NULL)
+ return;
+
+ // Copy or swap lbuf and conv_line.
+ len = (int)STRLEN(conv_line) + 1;
+ if (len > st->lbuf_size)
+ {
+ vim_free(st->lbuf);
+ st->lbuf = conv_line;
+ st->lbuf_size = len;
+ }
+ else
+ {
+ STRCPY(st->lbuf, conv_line);
+ vim_free(conv_line);
+ }
+}
+
+ static int
+findtags_add_match(
+ findtags_state_T *st,
+ tagptrs_T *tagp,
+ char_u *buf_ffname,
+ int flags UNUSED,
+ hash_T *hash,
+ int match_re,
+ int match_no_ic,
+ int matchoff,
+ int is_etag UNUSED,
+ char_u *help_lang UNUSED,
+ int help_pri UNUSED)
+{
+#ifdef FEAT_CSCOPE
+ int use_cscope = (flags & TAG_CSCOPE);
+#endif
+ int mtt;
+ int len = 0;
+ int is_current; // file name matches
+ int is_static; // current tag line is static
+ char_u *mfp;
+ char_u *p;
+ char_u *s;
+
+#ifdef FEAT_CSCOPE
+ if (use_cscope)
+ {
+ // Don't change the ordering, always use the same table.
+ mtt = MT_GL_OTH;
+ }
+ else
+#endif
+ {
+ // Decide in which array to store this match.
+ is_current = test_for_current(
+#ifdef FEAT_EMACS_TAGS
+ is_etag,
+#endif
+ tagp->fname, tagp->fname_end, st->tag_fname,
+ buf_ffname);
+#ifdef FEAT_EMACS_TAGS
+ is_static = FALSE;
+ if (!is_etag) // emacs tags are never static
+#endif
+ is_static = test_for_static(tagp);
+
+ // decide in which of the sixteen tables to store this
+ // match
+ if (is_static)
+ {
+ if (is_current)
+ mtt = MT_ST_CUR;
+ else
+ mtt = MT_ST_OTH;
+ }
+ else
+ {
+ if (is_current)
+ mtt = MT_GL_CUR;
+ else
+ mtt = MT_GL_OTH;
+ }
+ if (st->orgpat.regmatch.rm_ic && !match_no_ic)
+ mtt += MT_IC_OFF;
+ if (match_re)
+ mtt += MT_RE_OFF;
+ }
+
+ // 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.
+ if (st->help_only)
+ {
+#ifdef FEAT_MULTI_LANG
+# define ML_EXTRA 3
+#else
+# define ML_EXTRA 0
+#endif
+ // Append the help-heuristic number after the tagname, for
+ // sorting it later. The heuristic is ignored for
+ // detecting duplicates.
+ // The format is {tagname}@{lang}NUL{heuristic}NUL
+ *tagp->tagname_end = NUL;
+ len = (int)(tagp->tagname_end - tagp->tagname);
+ mfp = alloc(sizeof(char_u) + len + 10 + ML_EXTRA + 1);
+ if (mfp != NULL)
+ {
+ int heuristic;
+
+ p = mfp;
+ STRCPY(p, tagp->tagname);
+#ifdef FEAT_MULTI_LANG
+ p[len] = '@';
+ STRCPY(p + len + 1, help_lang);
+#endif
+
+ heuristic = help_heuristic(tagp->tagname,
+ match_re ? matchoff : 0, !match_no_ic);
+#ifdef FEAT_MULTI_LANG
+ heuristic += help_pri;
+#endif
+ sprintf((char *)p + len + 1 + ML_EXTRA, "%06d",
+ heuristic);
+ }
+ *tagp->tagname_end = TAB;
+ }
+ else if (st->name_only)
+ {
+ if (st->get_searchpat)
+ {
+ char_u *temp_end = tagp->command;
+
+ if (*temp_end == '/')
+ while (*temp_end && *temp_end != '\r'
+ && *temp_end != '\n'
+ && *temp_end != '$')
+ temp_end++;
+
+ if (tagp->command + 2 < temp_end)
+ {
+ len = (int)(temp_end - tagp->command - 2);
+ mfp = alloc(len + 2);
+ if (mfp != NULL)
+ vim_strncpy(mfp, tagp->command + 2, len);
+ }
+ else
+ mfp = NULL;
+ st->get_searchpat = FALSE;
+ }
+ else
+ {
+ len = (int)(tagp->tagname_end - tagp->tagname);
+ mfp = alloc(sizeof(char_u) + len + 1);
+ if (mfp != NULL)
+ vim_strncpy(mfp, tagp->tagname, len);
+
+ // if wanted, re-read line to get long form too
+ if (State & INSERT)
+ st->get_searchpat = p_sft;
+ }
+ }
+ else
+ {
+ size_t tag_fname_len = STRLEN(st->tag_fname);
+#ifdef FEAT_EMACS_TAGS
+ size_t ebuf_len = 0;
+#endif
+
+ // Save the tag in a buffer.
+ // Use 0x02 to separate fields (Can't use NUL because the
+ // hash key is terminated by NUL, or Ctrl_A because that is
+ // part of some Emacs tag files -- see parse_tag_line).
+ // Emacs tag: <mtt><tag_fname><0x02><ebuf><0x02><lbuf><NUL>
+ // other tag: <mtt><tag_fname><0x02><0x02><lbuf><NUL>
+ // without Emacs tags: <mtt><tag_fname><0x02><lbuf><NUL>
+ // Here <mtt> is the "mtt" value plus 1 to avoid NUL.
+ len = (int)tag_fname_len + (int)STRLEN(st->lbuf) + 3;
+#ifdef FEAT_EMACS_TAGS
+ if (is_etag)
+ {
+ ebuf_len = STRLEN(st->ebuf);
+ len += (int)ebuf_len + 1;
+ }
+ else
+ ++len;
+#endif
+ mfp = alloc(sizeof(char_u) + len + 1);
+ if (mfp != NULL)
+ {
+ p = mfp;
+ p[0] = mtt + 1;
+ STRCPY(p + 1, st->tag_fname);
+#ifdef BACKSLASH_IN_FILENAME
+ // Ignore differences in slashes, avoid adding
+ // both path/file and path\file.
+ slash_adjust(p + 1);
+#endif
+ p[tag_fname_len + 1] = TAG_SEP;
+ s = p + 1 + tag_fname_len + 1;
+#ifdef FEAT_EMACS_TAGS
+ if (is_etag)
+ {
+ STRCPY(s, st->ebuf);
+ s[ebuf_len] = TAG_SEP;
+ s += ebuf_len + 1;
+ }
+ else
+ *s++ = TAG_SEP;
+#endif
+ STRCPY(s, st->lbuf);
+ }
+ }
+
+ if (mfp != NULL)
+ {
+ hashitem_T *hi;
+
+ // Don't add identical matches.
+ // Add all cscope tags, because they are all listed.
+ // "mfp" is used as a hash key, there is a NUL byte to end
+ // the part that matters for comparing, more bytes may
+ // follow after it. E.g. help tags store the priority
+ // after the NUL.
+#ifdef FEAT_CSCOPE
+ if (use_cscope)
+ ++*hash;
+ else
+#endif
+ *hash = hash_hash(mfp);
+ hi = hash_lookup(&st->ht_match[mtt], mfp, *hash);
+ if (HASHITEM_EMPTY(hi))
+ {
+ if (hash_add_item(&st->ht_match[mtt], hi, mfp, *hash) == FAIL
+ || ga_grow(&st->ga_match[mtt], 1) != OK)
+ {
+ // Out of memory! Just forget about the rest.
+ st->stop_searching = TRUE;
+ return FAIL;
+ }
+ else
+ {
+ ((char_u **)(st->ga_match[mtt].ga_data))
+ [st->ga_match[mtt].ga_len++] = mfp;
+ st->match_count++;
+ }
+ }
+ else
+ // duplicate tag, drop it
+ vim_free(mfp);
+ }
+
+ return OK;
+}
+
+/*
* Search for tags matching 'st->orgpat.pat' in the 'st->tag_fname' tags file.
* Information needed to search for the tags is in the 'st' state structure.
* The matching tags are returned in 'st'.
@@ -1662,11 +2258,7 @@ find_tags_in_file(
{
FILE *fp = NULL;
tagptrs_T tagp;
- int is_static; // current tag line is static
- int is_current; // file name matches
int eof = FALSE; // found end-of-file
- char_u *p;
- char_u *s;
int i;
#ifdef FEAT_MULTI_LANG
int help_pri = 0;
@@ -1709,24 +2301,8 @@ find_tags_in_file(
int match_re; // match with regexp
int matchoff = 0;
-#ifdef FEAT_EMACS_TAGS
- /*
- * Stack for included emacs-tags file.
- * It has a fixed size, to truncate cyclic includes. jw
- */
-# define INCSTACK_SIZE 42
- struct
- {
- FILE *fp;
- char_u *etag_fname;
- } incstack[INCSTACK_SIZE];
-
- int incstack_idx = 0; // index in incstack
int is_etag; // current file is emaces style
-#endif
- char_u *mfp;
- int mtt;
hash_T hash = 0;
#ifdef FEAT_TAG_BINS
@@ -1736,12 +2312,9 @@ find_tags_in_file(
#endif
int line_error = FALSE; // syntax error
int has_re = (flags & TAG_REGEXP); // regexp used
- int help_only = (flags & TAG_HELP);
- int name_only = (flags & TAG_NAMES);
#ifdef FEAT_CSCOPE
int use_cscope = (flags & TAG_CSCOPE);
#endif
- int get_it_again = FALSE;
vimconv_T vimconv;
vimconv.vc_type = CONV_NONE;
@@ -1752,10 +2325,8 @@ find_tags_in_file(
CLEAR_FIELD(search_info);
#endif
- /*
- * A file that doesn't exist is silently ignored. Only when not a
- * single file is found, an error message is given (further on).
- */
+ // A file that doesn't exist is silently ignored. Only when not a
+ // single file is found, an error message is given (further on).
#ifdef FEAT_CSCOPE
if (use_cscope)
fp = NULL; // avoid GCC warning
@@ -1765,55 +2336,8 @@ find_tags_in_file(
#ifdef FEAT_MULTI_LANG
if (curbuf->b_help)
{
- // Keep en if the file extension is .txt
- if (st->is_txt)
- STRCPY(help_lang, "en");
- else
- {
- // Prefer help tags according to 'helplang'. Put the
- // two-letter language name in help_lang[].
- i = (int)STRLEN(st->tag_fname);
- if (i > 3 && st->tag_fname[i - 3] == '-')
- STRCPY(help_lang, st->tag_fname + i - 2);
- else
- STRCPY(help_lang, "en");
- }
- // When searching for a specific language skip tags files
- // for other languages.
- if (st->help_lang_find != NULL
- && STRICMP(help_lang, st->help_lang_find) != 0)
+ if (!findtags_in_help_init(st, flags, help_lang, &help_pri))
return OK;
-
- // For CTRL-] in a help file prefer a match with the same
- // language.
- if ((flags & TAG_KEEP_LANG)
- && st->help_lang_find == NULL
- && curbuf->b_fname != NULL
- && (i = (int)STRLEN(curbuf->b_fname)) > 4
- && curbuf->b_fname[i - 1] == 'x'
- && curbuf->b_fname[i - 4] == '.'
- && STRNICMP(curbuf->b_fname + i - 3, help_lang, 2) == 0)
- help_pri = 0;
- else
- {
- help_pri = 1;
- for (s = p_hlg; *s != NUL; ++s)
- {
- if (STRNICMP(s, help_lang, 2) == 0)
- break;
- ++help_pri;
- if ((s = vim_strchr(s, ',')) == NULL)
- break;
- }
- if (s == NULL || *s == NUL)
- {
- // Language not in 'helplang': use last, prefer English,
- // unless found already.
- ++help_pri;
- if (STRICMP(help_lang, "en") != 0)
- ++help_pri;
- }
- }
}
#endif
@@ -1827,16 +2351,12 @@ find_tags_in_file(
verbose_leave();
}
}
- st->did_open = TRUE; // remember that we found at least one file
+ st->did_open = TRUE; // remember that we found at least one file
- state = TS_START; // we're at the start of the file
-#ifdef FEAT_EMACS_TAGS
- is_etag = 0; // default is: not emacs style
-#endif
+ state = TS_START; // we're at the start of the file
+ is_etag = 0; // default is: not emacs style
- /*
- * Read and parse the lines in the file one by one
- */
+ // Read and parse the lines in the file one by one
for (;;)
{
#ifdef FEAT_TAG_BINS
@@ -1860,12 +2380,10 @@ find_tags_in_file(
st->stop_searching = TRUE;
break;
}
- if (get_it_again)
+ if (st->get_searchpat)
goto line_read_in;
#ifdef FEAT_TAG_BINS
- /*
- * For binary search: compute the next offset to use.
- */
+ // For binary search: compute the next offset to use.
if (state == TS_BINARY)
{
offset = search_info.low_offset + ((search_info.high_offset
@@ -1952,50 +2470,21 @@ find_tags_in_file(
if (eof)
{
#ifdef FEAT_EMACS_TAGS
- if (incstack_idx) // this was an included file
+ if (emacs_tags_file_eof(st, &fp) == TRUE)
{
- --incstack_idx;
- fclose(fp); // end of this file ...
- fp = incstack[incstack_idx].fp;
- STRCPY(st->tag_fname, incstack[incstack_idx].etag_fname);
- vim_free(incstack[incstack_idx].etag_fname);
+ // an included tags file. Continue processing the parent
+ // tags file.
is_etag = 1; // (only etags can include)
- continue; // ... continue with parent file
+ continue;
}
- else
#endif
- break; // end of file
+ break; // end of file
}
}
line_read_in:
if (vimconv.vc_type != CONV_NONE)
- {
- char_u *conv_line;
- int len;
-
- // Convert every line. Converting the pattern from 'enc' to
- // the tags file encoding doesn't work, because characters are
- // not recognized.
- conv_line = string_convert(&vimconv, st->lbuf, NULL);
- if (conv_line != NULL)
- {
- // Copy or swap lbuf and conv_line.
- len = (int)STRLEN(conv_line) + 1;
- if (len > st->lbuf_size)
- {
- vim_free(st->lbuf);
- st->lbuf = conv_line;
- st->lbuf_size = len;
- }
- else
- {
- STRCPY(st->lbuf, conv_line);
- vim_free(conv_line);
- }
- }
- }
-
+ findtags_string_convert(st, &vimconv);
#ifdef FEAT_EMACS_TAGS
/*
@@ -2011,56 +2500,7 @@ line_read_in:
{
is_etag = 1; // in case at the start
state = TS_LINEAR;
- if (!vim_fgets(st->ebuf, LSIZE, fp))
- {
- for (p = st->ebuf; *p && *p != ','; p++)
- ;
- *p = NUL;
-
- /*
- * atoi(p+1) is the number of bytes before the next ^L
- * unless it is an include statement.
- */
- if (STRNCMP(p + 1, "include", 7) == 0
- && incstack_idx < INCSTACK_SIZE)
- {
- // Save current "fp" and "tag_fname" in the stack.
- if ((incstack[incstack_idx].etag_fname =
- vim_strsave(st->tag_fname)) != NULL)
- {
- char_u *fullpath_ebuf;
-
- incstack[incstack_idx].fp = fp;
- fp = NULL;
-
- // Figure out "tag_fname" and "fp" to use for
- // included file.
- fullpath_ebuf = expand_tag_fname(st->ebuf,
- st->tag_fname, FALSE);
- if (fullpath_ebuf != NULL)
- {
- fp = mch_fopen((char *)fullpath_ebuf, "r");
- if (fp != NULL)
- {
- if (STRLEN(fullpath_ebuf) > LSIZE)
- semsg(_(e_tag_file_path_truncated_for_str), st->ebuf);
- vim_strncpy(st->tag_fname, fullpath_ebuf,
- MAXPATHL);
- ++incstack_idx;
- is_etag = 0; // we can include anything
- }
- vim_free(fullpath_ebuf);
- }
- if (fp == NULL)
- {
- // Can't open the included file, skip it and
- // restore old value of "fp".
- fp = incstack[incstack_idx].fp;
- vim_free(incstack[incstack_idx].etag_fname);
- }
- }
- }
- }
+ fp = emacs_tags_new_filename(st, fp, &is_etag);
continue;
}
#endif
@@ -2076,29 +2516,11 @@ line_read_in:
if (STRNCMP(st->lbuf, "!_TAG_", 6) <= 0
|| (st->lbuf[0] == '!' && ASCII_ISLOWER(st->lbuf[1])))
{
- if (STRNCMP(st->lbuf, "!_TAG_", 6) != 0)
- // Non-header item before the header, e.g. "!" itself.
- goto parse_line;
-
- /*
- * Read header line.
- */
-#ifdef FEAT_TAG_BINS
- if (STRNCMP(st->lbuf, "!_TAG_FILE_SORTED\t", 18) == 0)
- tag_file_sorted = st->lbuf[18];
-#endif
- if (STRNCMP(st->lbuf, "!_TAG_FILE_ENCODING\t", 20) == 0)
- {
- // Prepare to convert every line from the specified
- // encoding to 'encoding'.
- for (p = st->lbuf + 20; *p > ' ' && *p < 127; ++p)
- ;
- *p = NUL;
- convert_setup(&vimconv, st->lbuf + 20, p_enc);
- }
+ if (tags_file_hdr_parse(st, &vimconv, &tag_file_sorted))
+ // Read the next line. Unrecognized flags are ignored.
+ continue;
- // Read the next line. Unrecognized flags are ignored.
- continue;
+ goto parse_line;
}
// Headers ends.
@@ -2338,11 +2760,13 @@ parse_line:
*/
tagp.fname = tagp.tagname_end + 1;
tagp.fname_end = vim_strchr(tagp.fname, TAB);
- tagp.command = tagp.fname_end + 1;
if (tagp.fname_end == NULL)
i = FAIL;
else
+ {
+ tagp.command = tagp.fname_end + 1;
i = OK;
+ }
}
else
i = parse_tag_line(st->lbuf,
@@ -2409,226 +2833,19 @@ parse_line:
match_re = TRUE;
}
- /*
- * If a match is found, add it to ht_match[] and ga_match[].
- */
+ // If a match is found, add it to ht_match[] and ga_match[].
if (match)
{
- int len = 0;
-
-#ifdef FEAT_CSCOPE
- if (use_cscope)
- {
- // Don't change the ordering, always use the same table.
- mtt = MT_GL_OTH;
- }
- else
-#endif
- {
- // Decide in which array to store this match.
- is_current = test_for_current(
-#ifdef FEAT_EMACS_TAGS
+ if (findtags_add_match(st, &tagp, buf_ffname, flags, &hash,
+ match_re, match_no_ic, matchoff,
is_etag,
-#endif
- tagp.fname, tagp.fname_end, st->tag_fname,
- buf_ffname);
-#ifdef FEAT_EMACS_TAGS
- is_static = FALSE;
- if (!is_etag) // emacs tags are never static
-#endif
- is_static = test_for_static(&tagp);
-
- // decide in which of the sixteen tables to store this
- // match
- if (is_static)
- {
- if (is_current)
- mtt = MT_ST_CUR;
- else
- mtt = MT_ST_OTH;
- }
- else
- {
- if (is_current)
- mtt = MT_GL_CUR;
- else
- mtt = MT_GL_OTH;
- }
- if (st->orgpat.regmatch.rm_ic && !match_no_ic)
- mtt += MT_IC_OFF;
- if (match_re)
- mtt += MT_RE_OFF;
- }
-
- /*
- * 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.
- */
- if (help_only)
- {
#ifdef FEAT_MULTI_LANG
-# define ML_EXTRA 3
+ help_lang, help_pri
#else
-# define ML_EXTRA 0
-#endif
- /*
- * Append the help-heuristic number after the tagname, for
- * sorting it later. The heuristic is ignored for
- * detecting duplicates.
- * The format is {tagname}@{lang}NUL{heuristic}NUL
- */
- *tagp.tagname_end = NUL;
- len = (int)(tagp.tagname_end - tagp.tagname);
- mfp = alloc(sizeof(char_u) + len + 10 + ML_EXTRA + 1);
- if (mfp != NULL)
- {
- int heuristic;
-
- p = mfp;
- STRCPY(p, tagp.tagname);
-#ifdef FEAT_MULTI_LANG
- p[len] = '@';
- STRCPY(p + len + 1, help_lang);
-#endif
-
- heuristic = help_heuristic(tagp.tagname,
- match_re ? matchoff : 0, !match_no_ic);
-#ifdef FEAT_MULTI_LANG
- heuristic += help_pri;
+ (char_u *)"", 0
#endif
- sprintf((char *)p + len + 1 + ML_EXTRA, "%06d",
- heuristic);
- }
- *tagp.tagname_end = TAB;
- }
- else if (name_only)
- {
- if (get_it_again)
- {
- char_u *temp_end = tagp.command;
-
- if (*temp_end == '/')
- while (*temp_end && *temp_end != '\r'
- && *temp_end != '\n'
- && *temp_end != '$')
- temp_end++;
-
- if (tagp.command + 2 < temp_end)
- {
- len = (int)(temp_end - tagp.command - 2);
- mfp = alloc(len + 2);
- if (mfp != NULL)
- vim_strncpy(mfp, tagp.command + 2, len);
- }
- else
- mfp = NULL;
- get_it_again = FALSE;
- }
- else
- {
- len = (int)(tagp.tagname_end - tagp.tagname);
- mfp = alloc(sizeof(char_u) + len + 1);
- if (mfp != NULL)
- vim_strncpy(mfp, tagp.tagname, len);
-
- // if wanted, re-read line to get long form too
- if (State & INSERT)
- get_it_again = p_sft;
- }
- }
- else
- {
- size_t tag_fname_len = STRLEN(st->tag_fname);
-#ifdef FEAT_EMACS_TAGS
- size_t ebuf_len = 0;
-#endif
-
- // Save the tag in a buffer.
- // Use 0x02 to separate fields (Can't use NUL because the
- // hash key is terminated by NUL, or Ctrl_A because that is
- // part of some Emacs tag files -- see parse_tag_line).
- // Emacs tag: <mtt><tag_fname><0x02><ebuf><0x02><lbuf><NUL>
- // other tag: <mtt><tag_fname><0x02><0x02><lbuf><NUL>
- // without Emacs tags: <mtt><tag_fname><0x02><lbuf><NUL>
- // Here <mtt> is the "mtt" value plus 1 to avoid NUL.
- len = (int)tag_fname_len + (int)STRLEN(st->lbuf) + 3;
-#ifdef FEAT_EMACS_TAGS
- if (is_etag)
- {
- ebuf_len = STRLEN(st->ebuf);
- len += (int)ebuf_len + 1;
- }
- else
- ++len;
-#endif
- mfp = alloc(sizeof(char_u) + len + 1);
- if (mfp != NULL)
- {
- p = mfp;
- p[0] = mtt + 1;
- STRCPY(p + 1, st->tag_fname);
-#ifdef BACKSLASH_IN_FILENAME
- // Ignore differences in slashes, avoid adding
- // both path/file and path\file.
- slash_adjust(p + 1);
-#endif
- p[tag_fname_len + 1] = TAG_SEP;
- s = p + 1 + tag_fname_len + 1;
-#ifdef FEAT_EMACS_TAGS
- if (is_etag)
- {
- STRCPY(s, st->ebuf);
- s[ebuf_len] = TAG_SEP;
- s += ebuf_len + 1;
- }
- else
- *s++ = TAG_SEP;
-#endif
- STRCPY(s, st->lbuf);
- }
- }
-
- if (mfp != NULL)
- {
- hashitem_T *hi;
-
- /*
- * Don't add identical matches.
- * Add all cscope tags, because they are all listed.
- * "mfp" is used as a hash key, there is a NUL byte to end
- * the part that matters for comparing, more bytes may
- * follow after it. E.g. help tags store the priority
- * after the NUL.
- */
-#ifdef FEAT_CSCOPE
- if (use_cscope)
- hash++;
- else
-#endif
- hash = hash_hash(mfp);
- hi = hash_lookup(&st->ht_match[mtt], mfp, hash);
- if (HASHITEM_EMPTY(hi))
- {
- if (hash_add_item(&st->ht_match[mtt], hi, mfp, hash)
- == FAIL
- || ga_grow(&st->ga_match[mtt], 1) != OK)
- {
- // Out of memory! Just forget about the rest.
- st->stop_searching = TRUE;
- break;
- }
- else
- {
- ((char_u **)(st->ga_match[mtt].ga_data))
- [st->ga_match[mtt].ga_len++] = mfp;
- st->match_count++;
- }
- }
- else
- // duplicate tag, drop it
- vim_free(mfp);
- }
+ ) == FAIL)
+ break;
}
#ifdef FEAT_CSCOPE
if (use_cscope && eof)
@@ -2652,12 +2869,7 @@ parse_line:
#endif
fclose(fp);
#ifdef FEAT_EMACS_TAGS
- while (incstack_idx)
- {
- --incstack_idx;
- fclose(incstack[incstack_idx].fp);
- vim_free(incstack[incstack_idx].etag_fname);
- }
+ emacs_tags_incstack_free();
#endif
if (vimconv.vc_type != CONV_NONE)
convert_setup(&vimconv, NULL, NULL);
@@ -2687,8 +2899,7 @@ parse_line:
findtags_copy_matches(
findtags_state_T *st,
char_u ***matchesp,
- int *num_matches,
- int name_only)
+ int *num_matches)
{
char_u **matches;
int mtt;
@@ -2710,7 +2921,7 @@ findtags_copy_matches(
vim_free(mfp);
else
{
- if (!name_only)
+ if (!st->name_only)
{
// Change mtt back to zero-based.
*mfp = *mfp - 1;
@@ -2789,16 +3000,11 @@ find_tags(
// find all matching tags
#endif
int has_re = (flags & TAG_REGEXP); // regexp used
- int help_only = (flags & TAG_HELP);
- int name_only = (flags & TAG_NAMES);
int noic = (flags & TAG_NOIC);
#ifdef FEAT_CSCOPE
int use_cscope = (flags & TAG_CSCOPE);
#endif
int verbose = (flags & TAG_VERBOSE);
-#ifdef FEAT_EVAL
- int use_tfu = ((flags & TAG_NO_TAGFUNC) == 0);
-#endif
int save_p_ic = p_ic;
/*
@@ -2816,7 +3022,7 @@ find_tags(
help_save = curbuf->b_help;
- if (findtags_state_init(&st, pat, mincount) == FAIL)
+ if (findtags_state_init(&st, pat, flags, mincount) == FAIL)
goto findtag_end;
#ifdef FEAT_CSCOPE
@@ -2826,13 +3032,13 @@ find_tags(
/*
* Initialize a few variables
*/
- if (help_only) // want tags from help file
+ if (st.help_only) // want tags from help file
curbuf->b_help = TRUE; // will be restored later
#ifdef FEAT_CSCOPE
else if (use_cscope)
{
// Make sure we don't mix help and cscope, confuses Coverity.
- help_only = FALSE;
+ st.help_only = FALSE;
curbuf->b_help = FALSE;
}
#endif
@@ -2867,15 +3073,22 @@ find_tags(
goto findtag_end;
#ifdef FEAT_EVAL
- if (*curbuf->b_p_tfu != NUL && use_tfu && !tfu_in_use)
- {
- tfu_in_use = TRUE;
- retval = find_tagfunc_tags(pat, &st.ga_match[0], &st.match_count,
- flags, buf_ffname);
- tfu_in_use = FALSE;
- if (retval != NOTDONE)
- goto findtag_end;
- }
+ retval = findtags_apply_tfu(pat, &st, flags, buf_ffname);
+ if (retval != NOTDONE)
+ goto findtag_end;
+
+ // re-initialize the default return value
+ retval = FAIL;
+#endif
+
+#ifdef FEAT_MULTI_LANG
+ // Set a flag if the file extension is .txt
+ if ((flags & TAG_KEEP_LANG)
+ && st.help_lang_find == NULL
+ && curbuf->b_fname != NULL
+ && (i = (int)STRLEN(curbuf->b_fname)) > 4
+ && STRICMP(curbuf->b_fname + i - 4, ".txt") == 0)
+ st.is_txt = TRUE;
#endif
/*
@@ -2888,15 +3101,6 @@ find_tags(
* When the tag file is case-fold sorted, it is either one or the other.
* Only ignore case when TAG_NOIC not used or 'ignorecase' set.
*/
-#ifdef FEAT_MULTI_LANG
- // Set a flag if the file extension is .txt
- if ((flags & TAG_KEEP_LANG)
- && st.help_lang_find == NULL
- && curbuf->b_fname != NULL
- && (i = (int)STRLEN(curbuf->b_fname)) > 4
- && STRICMP(curbuf->b_fname + i - 4, ".txt") == 0)
- st.is_txt = TRUE;
-#endif
#ifdef FEAT_TAG_BINS
st.orgpat.regmatch.rm_ic = ((p_ic || !noic)
&& (findall || st.orgpat.headlen == 0 || !p_tbs));
@@ -2959,12 +3163,7 @@ find_tags(
}
findtag_end:
- vim_free(st.tag_fname);
- vim_free(st.lbuf);
- vim_regfree(st.orgpat.regmatch.regprog);
-#ifdef FEAT_EMACS_TAGS
- vim_free(st.ebuf);
-#endif
+ findtags_state_free(&st);
/*
* Move the matches from the ga_match[] arrays into one list of
@@ -2973,7 +3172,7 @@ findtag_end:
if (retval == FAIL)
st.match_count = 0;
- findtags_copy_matches(&st, matchesp, num_matches, name_only);
+ findtags_copy_matches(&st, matchesp, num_matches);
curbuf->b_help = help_save;
#ifdef FEAT_MULTI_LANG
@@ -3194,93 +3393,33 @@ parse_tag_line(
char_u *p;
#ifdef FEAT_EMACS_TAGS
- char_u *p_7f;
-
if (is_etag)
- {
- /*
- * There are two formats for an emacs tag line:
- * 1: struct EnvBase ^?EnvBase^A139,4627
- * 2: #define ARPB_WILD_WORLD ^?153,5194
- */
- p_7f = vim_strchr(lbuf, 0x7f);
- if (p_7f == NULL)
- {
-etag_fail:
- if (vim_strchr(lbuf, '\n') == NULL)
- {
- // Truncated line. Ignore it.
- if (p_verbose >= 5)
- {
- verbose_enter();
- msg(_("Ignoring long line in tags file"));
- verbose_leave();
- }
- tagp->command = lbuf;
- tagp->tagname = lbuf;
- tagp->tagname_end = lbuf;
- return OK;
- }
- return FAIL;
- }
-
- // Find ^A. If not found the line number is after the 0x7f
- p = vim_strchr(p_7f, Ctrl_A);
- if (p == NULL)
- p = p_7f + 1;
- else
- ++p;
-
- if (!VIM_ISDIGIT(*p)) // check for start of line number
- goto etag_fail;
- tagp->command = p;
-
-
- if (p[-1] == Ctrl_A) // first format: explicit tagname given
- {
- tagp->tagname = p_7f + 1;
- tagp->tagname_end = p - 1;
- }
- else // second format: isolate tagname
- {
- // find end of tagname
- for (p = p_7f - 1; !vim_iswordc(*p); --p)
- if (p == lbuf)
- goto etag_fail;
- tagp->tagname_end = p + 1;
- while (p >= lbuf && vim_iswordc(*p))
- --p;
- tagp->tagname = p + 1;
- }
- }
- else // not an Emacs tag
- {
+ // emacs-style tag file
+ return emacs_tags_parse_line(lbuf, tagp);
#endif
- // Isolate the tagname, from lbuf up to the first white
- tagp->tagname = lbuf;
- p = vim_strchr(lbuf, TAB);
- if (p == NULL)
- return FAIL;
- tagp->tagname_end = p;
- // Isolate file name, from first to second white space
- if (*p != NUL)
- ++p;
- tagp->fname = p;
- p = vim_strchr(p, TAB);
- if (p == NULL)
- return FAIL;
- tagp->fname_end = p;
+ // Isolate the tagname, from lbuf up to the first white
+ tagp->tagname = lbuf;
+ p = vim_strchr(lbuf, TAB);
+ if (p == NULL)
+ return FAIL;
+ tagp->tagname_end = p;
- // find start of search command, after second white space
- if (*p != NUL)
- ++p;
- if (*p == NUL)
- return FAIL;
- tagp->command = p;
-#ifdef FEAT_EMACS_TAGS
- }
-#endif
+ // Isolate file name, from first to second white space
+ if (*p != NUL)
+ ++p;
+ tagp->fname = p;
+ p = vim_strchr(p, TAB);
+ if (p == NULL)
+ return FAIL;
+ tagp->fname_end = p;
+
+ // find start of search command, after second white space
+ if (*p != NUL)
+ ++p;
+ if (*p == NUL)
+ return FAIL;
+ tagp->command = p;
return OK;
}
diff --git a/src/testdir/test83-tags2 b/src/testdir/test83-tags2
deleted file mode 100644
index 7f9f21b0e..000000000
--- a/src/testdir/test83-tags2
+++ /dev/null
@@ -1,2 +0,0 @@
-!_TAG_FILE_ENCODING cp932 //
-‚`‚a‚b Xtags2.txt /‚`‚a‚b
diff --git a/src/testdir/test83-tags3 b/src/testdir/test83-tags3
deleted file mode 100644
index 0cb659156..000000000
--- a/src/testdir/test83-tags3
+++ /dev/null
@@ -1,102 +0,0 @@
-!_TAG_FILE_SORTED 1 //
-!_TAG_FILE_ENCODING cp932 //
-abc1 Xtags3.txt /‚`‚a‚b
-abc2 Xtags3.txt /‚`‚a‚b
-abc3 Xtags3.txt /‚`‚a‚b
-abc4 Xtags3.txt /‚`‚a‚b
-abc5 Xtags3.txt /‚`‚a‚b
-abc6 Xtags3.txt /‚`‚a‚b
-abc7 Xtags3.txt /‚`‚a‚b
-abc8 Xtags3.txt /‚`‚a‚b
-abc9 Xtags3.txt /‚`‚a‚b
-abc10 Xtags3.txt /‚`‚a‚b
-abc11 Xtags3.txt /‚`‚a‚b
-abc12 Xtags3.txt /‚`‚a‚b
-abc13 Xtags3.txt /‚`‚a‚b
-abc14 Xtags3.txt /‚`‚a‚b
-abc15 Xtags3.txt /‚`‚a‚b
-abc16 Xtags3.txt /‚`‚a‚b
-abc17 Xtags3.txt /‚`‚a‚b
-abc18 Xtags3.txt /‚`‚a‚b
-abc19 Xtags3.txt /‚`‚a‚b
-abc20 Xtags3.txt /‚`‚a‚b
-abc21 Xtags3.txt /‚`‚a‚b
-abc22 Xtags3.txt /‚`‚a‚b
-abc23 Xtags3.txt /‚`‚a‚b
-abc24 Xtags3.txt /‚`‚a‚b
-abc25 Xtags3.txt /‚`‚a‚b
-abc26 Xtags3.txt /‚`‚a‚b
-abc27 Xtags3.txt /‚`‚a‚b
-abc28 Xtags3.txt /‚`‚a‚b
-abc29 Xtags3.txt /‚`‚a‚b
-abc30 Xtags3.txt /‚`‚a‚b
-abc31 Xtags3.txt /‚`‚a‚b
-abc32 Xtags3.txt /‚`‚a‚b
-abc33 Xtags3.txt /‚`‚a‚b
-abc34 Xtags3.txt /‚`‚a‚b
-abc35 Xtags3.txt /‚`‚a‚b
-abc36 Xtags3.txt /‚`‚a‚b
-abc37 Xtags3.txt /‚`‚a‚b
-abc38 Xtags3.txt /‚`‚a‚b
-abc39 Xtags3.txt /‚`‚a‚b
-abc40 Xtags3.txt /‚`‚a‚b
-abc41 Xtags3.txt /‚`‚a‚b
-abc42 Xtags3.txt /‚`‚a‚b
-abc43 Xtags3.txt /‚`‚a‚b
-abc44 Xtags3.txt /‚`‚a‚b
-abc45 Xtags3.txt /‚`‚a‚b
-abc46 Xtags3.txt /‚`‚a‚b
-abc47 Xtags3.txt /‚`‚a‚b
-abc48 Xtags3.txt /‚`‚a‚b
-abc49 Xtags3.txt /‚`‚a‚b
-abc50 Xtags3.txt /‚`‚a‚b
-abc51 Xtags3.txt /‚`‚a‚b
-abc52 Xtags3.txt /‚`‚a‚b
-abc53 Xtags3.txt /‚`‚a‚b
-abc54 Xtags3.txt /‚`‚a‚b
-abc55 Xtags3.txt /‚`‚a‚b
-abc56 Xtags3.txt /‚`‚a‚b
-abc57 Xtags3.txt /‚`‚a‚b
-abc58 Xtags3.txt /‚`‚a‚b
-abc59 Xtags3.txt /‚`‚a‚b
-abc60 Xtags3.txt /‚`‚a‚b
-abc61 Xtags3.txt /‚`‚a‚b
-abc62 Xtags3.txt /‚`‚a‚b
-abc63 Xtags3.txt /‚`‚a‚b
-abc64 Xtags3.txt /‚`‚a‚b
-abc65 Xtags3.txt /‚`‚a‚b
-abc66 Xtags3.txt /‚`‚a‚b
-abc67 Xtags3.txt /‚`‚a‚b
-abc68 Xtags3.txt /‚`‚a‚b
-abc69 Xtags3.txt /‚`‚a‚b
-abc70 Xtags3.txt /‚`‚a‚b
-abc71 Xtags3.txt /‚`‚a‚b
-abc72 Xtags3.txt /‚`‚a‚b
-abc73 Xtags3.txt /‚`‚a‚b
-abc74 Xtags3.txt /‚`‚a‚b
-abc75 Xtags3.txt /‚`‚a‚b
-abc76 Xtags3.txt /‚`‚a‚b
-abc77 Xtags3.txt /‚`‚a‚b
-abc78 Xtags3.txt /‚`‚a‚b
-abc79 Xtags3.txt /‚`‚a‚b
-abc80 Xtags3.txt /‚`‚a‚b
-abc81 Xtags3.txt /‚`‚a‚b
-abc82 Xtags3.txt /‚`‚a‚b
-abc83 Xtags3.txt /‚`‚a‚b
-abc84 Xtags3.txt /‚`‚a‚b
-abc85 Xtags3.txt /‚`‚a‚b
-abc86 Xtags3.txt /‚`‚a‚b
-abc87 Xtags3.txt /‚`‚a‚b
-abc88 Xtags3.txt /‚`‚a‚b
-abc89 Xtags3.txt /‚`‚a‚b
-abc90 Xtags3.txt /‚`‚a‚b
-abc91 Xtags3.txt /‚`‚a‚b
-abc92 Xtags3.txt /‚`‚a‚b
-abc93 Xtags3.txt /‚`‚a‚b
-abc94 Xtags3.txt /‚`‚a‚b
-abc95 Xtags3.txt /‚`‚a‚b
-abc96 Xtags3.txt /‚`‚a‚b
-abc97 Xtags3.txt /‚`‚a‚b
-abc98 Xtags3.txt /‚`‚a‚b
-abc99 Xtags3.txt /‚`‚a‚b
-abc100 Xtags3.txt /‚`‚a‚b
diff --git a/src/testdir/test_tagjump.vim b/src/testdir/test_tagjump.vim
index 4b479be9b..95a3c3113 100644
--- a/src/testdir/test_tagjump.vim
+++ b/src/testdir/test_tagjump.vim
@@ -230,7 +230,6 @@ func Test_tag_symbolic()
endfunc
" Tests for tag search with !_TAG_FILE_ENCODING.
-" Depends on the test83-tags2 and test83-tags3 files.
func Test_tag_file_encoding()
if has('vms')
throw 'Skipped: does not work on VMS'
@@ -262,18 +261,31 @@ func Test_tag_file_encoding()
" case2:
new
- set tags=test83-tags2
+ let content = ['!_TAG_FILE_ENCODING cp932 //',
+ \ "\x82`\x82a\x82b Xtags2.txt /\x82`\x82a\x82b"]
+ call writefile(content, 'Xtags')
+ set tags=Xtags
tag /.BC
call assert_equal('Xtags2.txt', expand('%:t'))
call assert_equal('ABC', getline('.'))
+ call delete('Xtags')
close
" case3:
new
- set tags=test83-tags3
+ let contents = [
+ \ "!_TAG_FILE_SORTED 1 //",
+ \ "!_TAG_FILE_ENCODING cp932 //"]
+ for i in range(1, 100)
+ call add(contents, 'abc' .. i
+ \ .. " Xtags3.txt /\x82`\x82a\x82b")
+ endfor
+ call writefile(contents, 'Xtags')
+ set tags=Xtags
tag abc50
call assert_equal('Xtags3.txt', expand('%:t'))
call assert_equal('ABC', getline('.'))
+ call delete('Xtags')
close
set tags&
@@ -324,6 +336,7 @@ func Test_tagjump_etags()
\ ], 'Xtags2')
tag main
call assert_equal(2, line('.'))
+ call assert_fails('tag bar', 'E426:')
" corrupted tag line
call writefile([
@@ -349,6 +362,27 @@ func Test_tagjump_etags()
\ ], 'Xtags')
call assert_fails('tag foo', 'E431:')
+ " end of file after a CTRL-L line
+ call writefile([
+ \ "\x0c",
+ \ "Xmain.c,64",
+ \ "void foo() {}\x7ffoo\x011,0",
+ \ "\x0c",
+ \ ], 'Xtags')
+ call assert_fails('tag main', 'E426:')
+
+ " error in an included tags file
+ call writefile([
+ \ "\x0c",
+ \ "Xtags2,include"
+ \ ], 'Xtags')
+ call writefile([
+ \ "\x0c",
+ \ "Xmain.c,64",
+ \ "void foo() {}",
+ \ ], 'Xtags2')
+ call assert_fails('tag foo', 'E431:')
+
call delete('Xtags')
call delete('Xtags2')
call delete('Xmain.c')
@@ -1432,6 +1466,11 @@ func Test_tagfile_errors()
\ "foo Xfile 1"], 'Xtags')
call assert_fails('tag foo', 'E431:')
+ " file name and search pattern are not separated by a tab
+ call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
+ \ "foo\tXfile 1;"], 'Xtags')
+ call assert_fails('tag foo', 'E431:')
+
call delete('Xtags')
call delete('Xfile')
set tags&
diff --git a/src/version.c b/src/version.c
index 6e337b4f1..e80801035 100644
--- a/src/version.c
+++ b/src/version.c
@@ -755,6 +755,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 4512,
+/**/
4511,
/**/
4510,