msg_puts("\n>");
}
-#ifdef FEAT_TAG_BINS
/*
* Compare two strings, for length "len", ignoring case the ASCII way.
* return 0 for match, < 0 for smaller, > 0 for bigger
}
return 0; // strings match
}
-#endif
/*
* Structure to hold info about the tag pattern being used.
int did_open; // did open a tag file
int mincount; // MAXCOL: find all matches
// other: minimal number of matches
-#ifdef FEAT_TAG_BINS
int linear; // do a linear search
-#endif
char_u *lbuf; // line buffer
int lbuf_size; // length of lbuf
#ifdef FEAT_EMACS_TAGS
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
int help_pri = 0;
char_u help_lang[3] = ""; // lang of current tags file
#endif
-#ifdef FEAT_TAG_BINS
int tag_file_sorted = NUL; // !_TAG_FILE_SORTED value
off_T filesize;
int tagcmp;
off_T offset;
-#endif
enum
{
TS_START, // at start of file
- TS_LINEAR // linear searching forward, till EOF
-#ifdef FEAT_TAG_BINS
- , TS_BINARY, // binary searching
+ TS_LINEAR, // linear searching forward, till EOF
+ TS_BINARY, // binary searching
TS_SKIP_BACK, // skipping backwards
TS_STEP_FORWARD // stepping forwards
-#endif
} state; // Current search state
-#ifdef FEAT_TAG_BINS
struct tag_search_info // Binary search file offsets
{
off_T low_offset; // offset for first char of first line that
int low_char; // first char at low_offset
int high_char; // first char at high_offset
} search_info;
-#endif
int cmplen;
int match; // matches
hash_T hash = 0;
-#ifdef FEAT_TAG_BINS
int sort_error = FALSE; // tags file not sorted
int sortic = FALSE; // tag file sorted in nocase
int noic = (flags & TAG_NOIC);
-#endif
int line_error = FALSE; // syntax error
int has_re = (flags & TAG_REGEXP); // regexp used
#ifdef FEAT_CSCOPE
vimconv.vc_type = CONV_NONE;
-#ifdef FEAT_TAG_BINS
// This is only to avoid a compiler warning for using search_info
// uninitialised.
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).
// Read and parse the lines in the file one by one
for (;;)
{
-#ifdef FEAT_TAG_BINS
// check for CTRL-C typed, more often when jumping around
if (state == TS_BINARY || state == TS_SKIP_BACK)
line_breakcheck();
else
-#endif
fast_breakcheck();
if ((flags & TAG_INS_COMP)) // Double brackets for gcc
ins_compl_check_keys(30, FALSE);
}
if (st->get_searchpat)
goto line_read_in;
-#ifdef FEAT_TAG_BINS
// For binary search: compute the next offset to use.
if (state == TS_BINARY)
{
* Not jumping around in the file: Read the next line.
*/
else
-#endif
{
// skip empty and blank lines
do
else
#endif
{
-#ifdef FEAT_TAG_BINS
search_info.curr_offset = vim_ftell(fp);
-#endif
eof = vim_fgets(st->lbuf, st->lbuf_size, fp);
}
} while (!eof && vim_isblankline(st->lbuf));
// Headers ends.
-#ifdef FEAT_TAG_BINS
/*
* When there is no tag head, or ignoring case, need to do a
* linear search.
st->linear = TRUE;
state = TS_LINEAR;
}
-#else
- state = TS_LINEAR;
-#endif
-#ifdef FEAT_TAG_BINS
// When starting a binary search, get the size of the file and
// compute the first offset.
if (state == TS_BINARY)
}
continue;
}
-#endif
}
parse_line:
return FAIL;
}
-#ifdef FEAT_TAG_BINS
if (state == TS_STEP_FORWARD)
// Seek to the same position to read the same line again
vim_fseek(fp, search_info.curr_offset, SEEK_SET);
// this will try the same thing again, make sure the offset is
// different
search_info.curr_offset = 0;
-#endif
continue;
}
else if (state == TS_LINEAR && st->orgpat.headlen != cmplen)
continue;
-#ifdef FEAT_TAG_BINS
if (state == TS_BINARY)
{
/*
}
}
else
-#endif
// skip this match if it can't match
if (MB_STRNICMP(tagp.tagname, st->orgpat.head, cmplen) != 0)
continue;
if (vimconv.vc_type != CONV_NONE)
convert_setup(&vimconv, NULL, NULL);
-#ifdef FEAT_TAG_BINS
tag_file_sorted = NUL;
if (sort_error)
{
semsg(_(e_tags_file_not_sorted_str), st->tag_fname);
sort_error = FALSE;
}
-#endif
/*
* Stop searching if sufficient tags have been found.
tagname_T tn; // info for get_tagfname()
int first_file; // trying first tag file
int retval = FAIL; // return value
-#ifdef FEAT_TAG_BINS
int round;
-#endif
int save_emsg_off;
char_u *saved_pat = NULL; // copy of pat[]
#endif
-#ifdef FEAT_TAG_BINS
int findall = (mincount == MAXCOL || mincount == TAG_MANY);
// find all matching tags
-#endif
int has_re = (flags & TAG_REGEXP); // regexp used
int noic = (flags & TAG_NOIC);
#ifdef FEAT_CSCOPE
* 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_TAG_BINS
st.orgpat.regmatch.rm_ic = ((p_ic || !noic)
&& (findall || st.orgpat.headlen == 0 || !p_tbs));
for (round = 1; round <= 2; ++round)
{
st.linear = (st.orgpat.headlen == 0 || !p_tbs || round == 2);
-#else
- st.orgpat.regmatch.rm_ic = (p_ic || !noic);
-#endif
/*
* Try tag file names from tags option one by one.
#endif
tagname_free(&tn);
-#ifdef FEAT_TAG_BINS
// stop searching when already did a linear search, or when TAG_NOIC
// used, and 'ignorecase' not set or already did case-ignore search
if (st.stop_searching || st.linear || (!p_ic && noic) ||
// try another time while ignoring case
st.orgpat.regmatch.rm_ic = TRUE;
}
-#endif
if (!st.stop_searching)
{
set tags&
endfunc
+" Test for 'tagbsearch' (binary search)
+func Test_tagbsearch()
+ " If a tags file header says the tags are sorted, but the tags are actually
+ " unsorted, then binary search should fail and linear search should work.
+ call writefile([
+ \ "!_TAG_FILE_ENCODING\tutf-8\t//",
+ \ "!_TAG_FILE_SORTED\t1\t/0=unsorted, 1=sorted, 2=foldcase/",
+ \ "third\tXfoo\t3",
+ \ "second\tXfoo\t2",
+ \ "first\tXfoo\t1"],
+ \ 'Xtags')
+ set tags=Xtags
+ let code =<< trim [CODE]
+ int first() {}
+ int second() {}
+ int third() {}
+ [CODE]
+ call writefile(code, 'Xfoo')
+
+ enew
+ set tagbsearch
+ call assert_fails('tag first', 'E426:')
+ call assert_equal('', bufname())
+ call assert_fails('tag second', 'E426:')
+ call assert_equal('', bufname())
+ tag third
+ call assert_equal('Xfoo', bufname())
+ call assert_equal(3, line('.'))
+ %bw!
+
+ set notagbsearch
+ tag first
+ call assert_equal('Xfoo', bufname())
+ call assert_equal(1, line('.'))
+ enew
+ tag second
+ call assert_equal('Xfoo', bufname())
+ call assert_equal(2, line('.'))
+ enew
+ tag third
+ call assert_equal('Xfoo', bufname())
+ call assert_equal(3, line('.'))
+ %bw!
+
+ " If a tags file header says the tags are unsorted, but the tags are
+ " actually sorted, then binary search should work.
+ call writefile([
+ \ "!_TAG_FILE_ENCODING\tutf-8\t//",
+ \ "!_TAG_FILE_SORTED\t0\t/0=unsorted, 1=sorted, 2=foldcase/",
+ \ "first\tXfoo\t1",
+ \ "second\tXfoo\t2",
+ \ "third\tXfoo\t3"],
+ \ 'Xtags')
+
+ set tagbsearch
+ tag first
+ call assert_equal('Xfoo', bufname())
+ call assert_equal(1, line('.'))
+ enew
+ tag second
+ call assert_equal('Xfoo', bufname())
+ call assert_equal(2, line('.'))
+ enew
+ tag third
+ call assert_equal('Xfoo', bufname())
+ call assert_equal(3, line('.'))
+ %bw!
+
+ call delete('Xtags')
+ call delete('Xfoo')
+ set tags& tagbsearch&
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab