aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/nvim/tag.c566
-rw-r--r--src/nvim/testdir/test_tagjump.vim50
2 files changed, 357 insertions, 259 deletions
diff --git a/src/nvim/tag.c b/src/nvim/tag.c
index 6dce3c500b..38e87e17e3 100644
--- a/src/nvim/tag.c
+++ b/src/nvim/tag.c
@@ -112,8 +112,11 @@ static char *nofile_fname = NULL; // fname for NOTAGFILE error
/// State information used during a tag search
typedef struct {
- char *tag_fname; ///< name of tag file
+ char *tag_fname; ///< name of the tag file
pat_T orgpat; ///< holds unconverted pattern info
+ bool name_only; ///< get only tag names
+ bool get_searchpat; ///< used for 'showfulltag'
+ bool help_only; ///< only search for help tags
char *help_lang_find; ///< lang to be found
bool is_txt; ///< flag of file extension
bool did_open; ///< did open a tag file
@@ -1374,7 +1377,7 @@ static int find_tagfunc_tags(char_u *pat, garray_T *ga, int *match_count, int fl
}
/// Initialize the state used by find_tags()
-static void findtags_state_init(findtags_state_T *st, char *pat, int mincount)
+static void findtags_state_init(findtags_state_T *st, char *pat, int flags, int mincount)
{
st->tag_fname = xmalloc(MAXPATHL + 1);
st->orgpat.pat = (char_u *)pat;
@@ -1383,6 +1386,9 @@ static void findtags_state_init(findtags_state_T *st, char *pat, int mincount)
st->help_lang_find = NULL;
st->is_txt = false;
st->did_open = false;
+ st->help_only = (flags & TAG_HELP);
+ st->name_only = (flags & TAG_NAMES);
+ st->get_searchpat = false;
st->mincount = mincount;
st->lbuf_size = LSIZE;
st->lbuf = xmalloc((size_t)st->lbuf_size);
@@ -1395,6 +1401,278 @@ static void findtags_state_init(findtags_state_T *st, char *pat, int mincount)
}
}
+/// Free the state used by find_tags()
+static void findtags_state_free(findtags_state_T *st)
+{
+ xfree(st->tag_fname);
+ xfree(st->lbuf);
+ vim_regfree(st->orgpat.regmatch.regprog);
+}
+
+/// 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 bool findtags_in_help_init(findtags_state_T *st, int flags, char *help_lang, int *help_pri)
+{
+ int i;
+
+ // 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;
+ char *s;
+ for (s = (char *)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;
+}
+
+/// 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 *pat, findtags_state_T *st, int flags, char *buf_ffname)
+{
+ const bool use_tfu = ((flags & TAG_NO_TAGFUNC) == 0);
+
+ if (!use_tfu || tfu_in_use || *curbuf->b_p_tfu == NUL) {
+ return NOTDONE;
+ }
+
+ tfu_in_use = true;
+ int retval = find_tagfunc_tags((char_u *)pat, st->ga_match, &st->match_count,
+ flags, (char_u *)buf_ffname);
+ tfu_in_use = false;
+
+ return retval;
+}
+
+/// Parse a tags file header line in "st->lbuf".
+/// Returns true to read the next header line and false to process the line.
+static bool tags_file_hdr_parse(findtags_state_T *st, vimconv_T *vcp, int *sorted_file)
+{
+ if (strncmp(st->lbuf, "!_TAG_", 6) != 0) {
+ // Non-header item before the header, e.g. "!" itself.
+ return false;
+ }
+
+ // Read header line.
+ if (strncmp(st->lbuf, "!_TAG_FILE_SORTED\t", 18) == 0) {
+ *sorted_file = (uint8_t)st->lbuf[18];
+ }
+ if (strncmp(st->lbuf, "!_TAG_FILE_ENCODING\t", 20) == 0) {
+ // Prepare to convert every line from the specified
+ // encoding to 'encoding'.
+ char *p;
+ 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 *conv_line = string_convert(vcp, st->lbuf, NULL);
+ if (conv_line == NULL) {
+ return;
+ }
+
+ // Copy or swap lbuf and conv_line.
+ int len = (int)strlen(conv_line) + 1;
+ if (len > st->lbuf_size) {
+ xfree(st->lbuf);
+ st->lbuf = conv_line;
+ st->lbuf_size = len;
+ } else {
+ STRCPY(st->lbuf, conv_line);
+ xfree(conv_line);
+ }
+}
+
+static void findtags_add_match(findtags_state_T *st, tagptrs_T *tagp, char *buf_ffname, int flags,
+ hash_T *hash, bool match_re, bool match_no_ic, bool matchoff,
+ char *help_lang, int help_pri)
+{
+ int mtt;
+ size_t len = 0;
+ bool is_current; // file name matches
+ bool is_static; // current tag line is static
+ char *mfp;
+ char *p;
+ char_u *s;
+
+ // Decide in which array to store this match.
+ is_current = test_for_current((char *)tagp->fname, (char *)tagp->fname_end,
+ st->tag_fname,
+ buf_ffname);
+ 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) {
+#define ML_EXTRA 3
+ // 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 = (size_t)(tagp->tagname_end - (char_u *)tagp->tagname);
+ mfp = xmalloc(sizeof(char) + len + 10 + ML_EXTRA + 1);
+
+ p = mfp;
+ STRCPY(p, tagp->tagname);
+ p[len] = '@';
+ STRCPY(p + len + 1, help_lang);
+ snprintf(p + len + 1 + ML_EXTRA, strlen(p) + len + 1 + ML_EXTRA, "%06d",
+ help_heuristic(tagp->tagname,
+ match_re ? matchoff : 0, !match_no_ic) + help_pri);
+
+ *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 = (size_t)(temp_end - tagp->command - 2);
+ mfp = xmalloc(len + 2);
+ STRLCPY(mfp, tagp->command + 2, len + 1);
+ } else {
+ mfp = NULL;
+ }
+ st->get_searchpat = false;
+ } else {
+ len = (size_t)((char *)tagp->tagname_end - tagp->tagname);
+ mfp = xmalloc(sizeof(char) + len + 1);
+ STRLCPY(mfp, tagp->tagname, len + 1);
+
+ // if wanted, re-read line to get long form too
+ if (State & MODE_INSERT) {
+ st->get_searchpat = p_sft;
+ }
+ }
+ } else {
+ size_t tag_fname_len = strlen(st->tag_fname);
+ // Save the tag in a buffer.
+ // Use 0x02 to separate fields (Can't use NUL, because the
+ // hash key is terminated by NUL).
+ // 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 = tag_fname_len + strlen(st->lbuf) + 3;
+ mfp = xmalloc(sizeof(char) + len + 1);
+ p = mfp;
+ p[0] = (char)(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 = (char_u *)p + 1 + tag_fname_len + 1;
+ STRCPY(s, st->lbuf);
+ }
+
+ if (mfp != NULL) {
+ hashitem_T *hi;
+
+ // Don't add identical matches.
+ // "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.
+ *hash = hash_hash((char_u *)mfp);
+ hi = hash_lookup(&st->ht_match[mtt], (const char *)mfp, strlen(mfp), *hash);
+ if (HASHITEM_EMPTY(hi)) {
+ hash_add_item(&st->ht_match[mtt], hi, (char_u *)mfp, *hash);
+ GA_APPEND(char *, &st->ga_match[mtt], mfp);
+ st->match_count++;
+ } else {
+ // duplicate tag, drop it
+ xfree(mfp);
+ }
+ }
+}
+
/// 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".
@@ -1402,14 +1680,10 @@ static void find_tags_in_file(findtags_state_T *st, int flags, char *buf_ffname)
{
FILE *fp = NULL;
tagptrs_T tagp;
- int is_static; // current tag line is static
- int is_current; // file name matches
bool eof = false; // found end-of-file
- char *p;
- char_u *s;
int i;
int help_pri = 0;
- char_u help_lang[3] = ""; // lang of current tags file
+ char help_lang[3] = ""; // lang of current tags file
int tag_file_sorted = NUL; // !_TAG_FILE_SORTED value
int tagcmp;
off_T offset;
@@ -1438,8 +1712,6 @@ static void find_tags_in_file(findtags_state_T *st, int flags, char *buf_ffname)
int match_re; // match with regexp
int matchoff = 0;
- char *mfp;
- int mtt;
hash_T hash = 0;
bool sort_error = false; // tags file not sorted
@@ -1447,9 +1719,6 @@ static void find_tags_in_file(findtags_state_T *st, int flags, char *buf_ffname)
int noic = (flags & TAG_NOIC);
bool 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);
- int get_it_again = false;
vimconv_T vimconv;
vimconv.vc_type = CONV_NONE;
@@ -1461,57 +1730,9 @@ static void find_tags_in_file(findtags_state_T *st, int flags, char *buf_ffname)
// A file that doesn't exist is silently ignored. Only when not a
// single file is found, an error message is given (further on).
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;
}
-
- // 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 = (char_u *)vim_strchr((char *)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++;
- }
- }
- }
}
if ((fp = os_fopen(st->tag_fname, "r")) == NULL) {
@@ -1547,9 +1768,9 @@ static void find_tags_in_file(findtags_state_T *st, int flags, char *buf_ffname)
// found (for completion).
if (st->mincount == TAG_MANY && st->match_count >= TAG_MANY) {
st->stop_searching = true;
- return;
+ break;
}
- if (get_it_again) {
+ if (st->get_searchpat) {
goto line_read_in;
}
// For binary search: compute the next offset to use.
@@ -1608,31 +1829,13 @@ static void find_tags_in_file(findtags_state_T *st, int flags, char *buf_ffname)
} while (!eof && vim_isblankline(st->lbuf));
if (eof) {
- break; // end of file
+ break; // end of file
}
}
line_read_in:
if (vimconv.vc_type != CONV_NONE) {
- char *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) {
- xfree(st->lbuf);
- st->lbuf = conv_line;
- st->lbuf_size = len;
- } else {
- STRCPY(st->lbuf, conv_line);
- xfree(conv_line);
- }
- }
+ findtags_string_convert(st, &vimconv);
}
// When still at the start of the file, check for Emacs tags file
@@ -1642,25 +1845,12 @@ line_read_in:
// case is folded lower case letters sort before "_".
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.
- if (strncmp(st->lbuf, "!_TAG_FILE_SORTED\t", 18) == 0) {
- tag_file_sorted = (uint8_t)st->lbuf[18];
- }
- 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.
@@ -1854,10 +2044,10 @@ parse_line:
// Can be a matching tag, isolate the file name and command.
tagp.fname = tagp.tagname_end + 1;
tagp.fname_end = (char_u *)vim_strchr((char *)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 {
@@ -1912,130 +2102,8 @@ parse_line:
// If a match is found, add it to ht_match[] and ga_match[].
if (match) {
- size_t len = 0;
-
- // Decide in which array to store this match.
- is_current = test_for_current((char *)tagp.fname, (char *)tagp.fname_end,
- st->tag_fname,
- buf_ffname);
- 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) {
-#define ML_EXTRA 3
- // 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 = (size_t)(tagp.tagname_end - (char_u *)tagp.tagname);
- mfp = xmalloc(sizeof(char) + len + 10 + ML_EXTRA + 1);
-
- p = mfp;
- STRCPY(p, tagp.tagname);
- p[len] = '@';
- STRCPY(p + len + 1, help_lang);
- snprintf(p + len + 1 + ML_EXTRA, strlen(p) + len + 1 + ML_EXTRA, "%06d",
- help_heuristic(tagp.tagname,
- match_re ? matchoff : 0, !match_no_ic) + help_pri);
-
- *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 = (size_t)(temp_end - tagp.command - 2);
- mfp = xmalloc(len + 2);
- STRLCPY(mfp, tagp.command + 2, len + 1);
- } else {
- mfp = NULL;
- }
- get_it_again = false;
- } else {
- len = (size_t)(tagp.tagname_end - (char_u *)tagp.tagname);
- mfp = xmalloc(sizeof(char) + len + 1);
- STRLCPY(mfp, tagp.tagname, len + 1);
-
- // if wanted, re-read line to get long form too
- if (State & MODE_INSERT) {
- get_it_again = p_sft;
- }
- }
- } else {
- size_t tag_fname_len = strlen(st->tag_fname);
- // Save the tag in a buffer.
- // Use 0x02 to separate fields (Can't use NUL, because the
- // hash key is terminated by NUL).
- // 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 = tag_fname_len + strlen(st->lbuf) + 3;
- mfp = xmalloc(sizeof(char) + len + 1);
- p = mfp;
- p[0] = (char)(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 = (char_u *)p + 1 + tag_fname_len + 1;
- STRCPY(s, st->lbuf);
- }
-
- if (mfp != NULL) {
- hashitem_T *hi;
-
- // Don't add identical matches.
- // "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.
- hash = hash_hash((char_u *)mfp);
- hi = hash_lookup(&st->ht_match[mtt], (const char *)mfp, strlen(mfp), hash);
- if (HASHITEM_EMPTY(hi)) {
- hash_add_item(&st->ht_match[mtt], hi, (char_u *)mfp, hash);
- GA_APPEND(char *, &st->ga_match[mtt], mfp);
- st->match_count++;
- } else {
- // duplicate tag, drop it
- xfree(mfp);
- }
- }
+ findtags_add_match(st, &tagp, buf_ffname, flags, &hash,
+ match_re, match_no_ic, matchoff, help_lang, help_pri);
}
} // forever
@@ -2064,8 +2132,7 @@ parse_line:
}
/// Copy the tags found by find_tags() to "matchesp".
-static void findtags_copy_matches(findtags_state_T *st, char ***matchesp, int *num_matches,
- int name_only)
+static void findtags_copy_matches(findtags_state_T *st, char ***matchesp, int *num_matches)
{
char **matches;
int mtt;
@@ -2085,7 +2152,7 @@ static void findtags_copy_matches(findtags_state_T *st, char ***matchesp, int *n
if (matches == NULL) {
xfree(mfp);
} else {
- if (!name_only) {
+ if (!st->name_only) {
// Change mtt back to zero-based.
*mfp = (char)(*mfp - 1);
@@ -2156,11 +2223,8 @@ int find_tags(char *pat, int *num_matches, char ***matchesp, int flags, int minc
int findall = (mincount == MAXCOL || mincount == TAG_MANY); // find all matching tags
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);
int verbose = (flags & TAG_VERBOSE);
- int use_tfu = ((flags & TAG_NO_TAGFUNC) == 0);
int save_p_ic = p_ic;
// Change the value of 'ignorecase' according to 'tagcase' for the
@@ -2186,10 +2250,10 @@ int find_tags(char *pat, int *num_matches, char ***matchesp, int flags, int minc
help_save = curbuf->b_help;
- findtags_state_init(&st, pat, mincount);
+ findtags_state_init(&st, pat, flags, mincount);
// 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
}
@@ -2217,24 +2281,13 @@ int find_tags(char *pat, int *num_matches, char ***matchesp, int flags, int minc
goto findtag_end;
}
- if (*curbuf->b_p_tfu != NUL && use_tfu && !tfu_in_use) {
- tfu_in_use = true;
- retval = find_tagfunc_tags((char_u *)pat, &st.ga_match[0], &st.match_count, flags,
- (char_u *)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;
}
- // When finding a specified number of matches, first try with matching
- // case, so binary search can be used, and try ignore-case matches in a
- // second loop.
- // When finding all matches, 'tagbsearch' is off, or there is no fixed
- // string to look for, ignore case right away to avoid going though the
- // tags files twice.
- // 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.
+ // re-initialize the default return value
+ retval = FAIL;
// Set a flag if the file extension is .txt
if ((flags & TAG_KEEP_LANG)
@@ -2244,6 +2297,15 @@ int find_tags(char *pat, int *num_matches, char ***matchesp, int flags, int minc
&& STRICMP(curbuf->b_fname + i - 4, ".txt") == 0) {
st.is_txt = true;
}
+
+ // When finding a specified number of matches, first try with matching
+ // case, so binary search can be used, and try ignore-case matches in a
+ // second loop.
+ // When finding all matches, 'tagbsearch' is off, or there is no fixed
+ // string to look for, ignore case right away to avoid going though the
+ // tags files twice.
+ // 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.
st.orgpat.regmatch.rm_ic = ((p_ic || !noic)
&& (findall || st.orgpat.headlen == 0 || !p_tbs));
for (round = 1; round <= 2; round++) {
@@ -2281,9 +2343,7 @@ int find_tags(char *pat, int *num_matches, char ***matchesp, int flags, int minc
}
findtag_end:
- xfree(st.tag_fname);
- xfree(st.lbuf);
- vim_regfree(st.orgpat.regmatch.regprog);
+ findtags_state_free(&st);
// Move the matches from the ga_match[] arrays into one list of
// matches. When retval == FAIL, free the matches.
@@ -2291,7 +2351,7 @@ findtag_end:
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;
xfree(saved_pat);
diff --git a/src/nvim/testdir/test_tagjump.vim b/src/nvim/testdir/test_tagjump.vim
index 361aa23291..231d006da7 100644
--- a/src/nvim/testdir/test_tagjump.vim
+++ b/src/nvim/testdir/test_tagjump.vim
@@ -231,15 +231,13 @@ 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()
- throw 'skipped: Nvim removed test83-tags2, test83-tags3'
if has('vms')
- return
+ throw 'Skipped: does not work on VMS'
endif
if !has('iconv') || iconv("\x82\x60", "cp932", "utf-8") != "\uff21"
- return
+ throw 'Skipped: iconv does not work'
endif
let save_enc = &encoding
@@ -264,18 +262,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&
@@ -327,6 +338,7 @@ func Test_tagjump_etags()
\ ], 'Xtags2')
tag main
call assert_equal(2, line('.'))
+ call assert_fails('tag bar', 'E426:')
" corrupted tag line
call writefile([
@@ -352,6 +364,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')
@@ -1453,6 +1486,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&