📄 tag.c
字号:
if (vim_memcmp(
((char_u **)(ga_match[mtt].ga_data))[i],
p, (size_t)len) == 0)
break;
if (i < 0)
{
((char_u **)(ga_match[mtt].ga_data))
[ga_match[mtt].ga_len++] = p;
ga_match[mtt].ga_room--;
++match_count;
}
else
vim_free(p);
}
}
else /* Out of memory! Just forget about the rest. */
{
retval = OK;
stop_searching = TRUE;
break;
}
}
#ifdef USE_CSCOPE
if (use_cscope && eof)
break;
#endif
} /* not got_int */
#ifdef USE_CSCOPE
if (!use_cscope)
#endif
fclose(fp);
#ifdef EMACS_TAGS
while (incstack_idx)
{
--incstack_idx;
fclose(incstack[incstack_idx].fp);
vim_free(incstack[incstack_idx].etag_fname);
}
#endif
#ifdef BINARY_TAGS
if (sort_error)
{
EMSG2("Tags file not sorted: %s", tag_fname);
sort_error = FALSE;
}
#endif
/*
* Stop searching if sufficient tags have been found.
*/
if (match_count >= mincount)
{
retval = OK;
stop_searching = TRUE;
}
#ifdef USE_CSCOPE
if (stop_searching || use_cscope)
#else
if (stop_searching)
#endif
break;
} /* end of for-each-file loop */
#ifdef BINARY_TAGS
/* stop searching when already did a linear search, or when
* TAG_NOIC used, and 'ignorecase' not set */
if (stop_searching || linear || (!p_ic && noic))
break;
# ifdef USE_CSCOPE
if (use_cscope)
break;
# endif
reg_ic = TRUE; /* try another time while ignoring case */
}
#endif
if (!stop_searching)
{
if (!did_open && verbose) /* never opened any tags file */
EMSG("No tags file");
retval = OK; /* It's OK even when no tag found */
}
findtag_end:
vim_free(lbuf);
vim_free(prog);
vim_free(tag_fname);
#ifdef EMACS_TAGS
vim_free(ebuf);
#endif
/*
* Move the matches from the ga_match[] arrays into one list of
* matches. When retval == FAIL, free the matches.
*/
if (retval == FAIL)
match_count = 0;
if (match_count > 0)
matches = (char_u **)lalloc((long_u)(match_count * sizeof(char_u *)),
TRUE);
else
matches = NULL;
match_count = 0;
for (mtt = 0; mtt < MT_COUNT; ++mtt)
{
for (i = 0; i < ga_match[mtt].ga_len; ++i)
{
p = ((char_u **)(ga_match[mtt].ga_data))[i];
if (matches == NULL)
vim_free(p);
else
matches[match_count++] = p;
}
ga_clear(&ga_match[mtt]);
}
*matchesp = matches;
*num_matches = match_count;
curbuf->b_help = help_save;
return retval;
}
/*
* Get the next name of a tag file from the tag file list.
* For help files, use "tags" file only.
*
* Return FAIL if no more tag file names, OK otherwise.
*/
static int
get_tagfname(first, buf)
int first; /* TRUE when first file name is wanted */
char_u *buf; /* pointer to buffer of LSIZE chars */
{
static char_u *np = NULL;
char_u *fname;
size_t path_len, fname_len;
/*
* A list is kept of the files that have been visited.
*/
struct visited
{
struct visited *v_next;
#if defined(UNIX)
struct stat v_st;
#else
char_u v_fname[1]; /* actually longer */
#endif
};
static struct visited *first_visited = NULL;
struct visited *vp;
#ifdef UNIX
struct stat st;
#else
char_u *expand_buf;
#endif
if (first)
{
np = p_tags;
while (first_visited != NULL)
{
vp = first_visited->v_next;
vim_free(first_visited);
first_visited = vp;
}
}
if (np == NULL) /* tried allready (or bogus call) */
return FAIL;
/*
* For a help window only try the file 'tags' in the same
* directory as 'helpfile'.
*/
if (curbuf->b_help)
{
path_len = gettail(p_hf) - p_hf;
if (path_len + 9 >= LSIZE)
return FAIL;
mch_memmove(buf, p_hf, path_len);
STRCPY(buf + path_len, "tags");
np = NULL; /* try only once */
}
else
{
#ifndef UNIX
expand_buf = alloc(MAXPATHL);
if (expand_buf == NULL)
return FAIL;
#endif
/*
* Loop until we have found a file name that can be used.
*/
for (;;)
{
if (*np == NUL) /* tried all possibilities */
{
#ifndef UNIX
vim_free(expand_buf);
#endif
return FAIL;
}
/*
* Copy next file name into buf.
*/
(void)copy_option_part(&np, buf, LSIZE, " ,");
/*
* Tag file name starting with "./": Replace '.' with path of
* current file.
* Only do this when 't' flag not included in 'cpo'.
*/
if (buf[0] == '.' && vim_ispathsep(buf[1])
&& vim_strchr(p_cpo, CPO_DOTTAG) == NULL)
{
if (curbuf->b_fname == NULL) /* skip if no file name */
continue;
path_len = gettail(curbuf->b_fname) - curbuf->b_fname;
fname = buf + 1;
while (vim_ispathsep(*fname)) /* skip '/' and the like */
++fname;
fname_len = STRLEN(fname);
if (fname_len + path_len + 1 > LSIZE)
continue;
mch_memmove(buf + path_len, fname, fname_len + 1);
mch_memmove(buf, curbuf->b_fname, path_len);
}
/*
* Check if this tags file has been used already.
* If file doesn't exist, skip it.
*/
#if defined(UNIX)
if (stat((char *)buf, &st) < 0)
#else
if (mch_FullName(buf, expand_buf, MAXPATHL, TRUE) == FAIL)
#endif
continue;
for (vp = first_visited; vp != NULL; vp = vp->v_next)
#if defined(UNIX)
if (vp->v_st.st_dev == st.st_dev &&
vp->v_st.st_ino == st.st_ino)
#else
if (fnamecmp(vp->v_fname, expand_buf) == 0)
#endif
break;
if (vp != NULL) /* already visited, skip it */
continue;
/*
* Found the next name. Add it to the list of visited files.
*/
#ifdef UNIX
vp = (struct visited *)alloc((unsigned)sizeof(struct visited));
#else
vp = (struct visited *)alloc((unsigned)(sizeof(struct visited) +
STRLEN(expand_buf)));
#endif
if (vp != NULL)
{
#ifdef UNIX
vp->v_st = st;
#else
STRCPY(vp->v_fname, expand_buf);
#endif
vp->v_next = first_visited;
first_visited = vp;
}
break;
}
#ifndef UNIX
vim_free(expand_buf);
#endif
}
return OK;
}
/*
* Parse one line from the tags file. Find start/end of tag name, start/end of
* file name and start of search pattern.
*
* If is_etag is TRUE, tagp->fname and tagp->fname_end are not set.
*
* Return FAIL if there is a format error in this line, OK otherwise.
*/
static int
parse_tag_line(lbuf,
#ifdef EMACS_TAGS
is_etag,
#endif
tagp)
char_u *lbuf; /* line to be parsed */
#ifdef EMACS_TAGS
int is_etag;
#endif
struct tag_pointers *tagp;
{
char_u *p;
#ifdef 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)
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 (!isdigit(*p)) /* check for start of line number */
return 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)
return FAIL;
tagp->tagname_end = p + 1;
while (p >= lbuf && vim_iswordc(*p))
--p;
tagp->tagname = p + 1;
}
}
else /* not an Emacs tag */
{
#endif
/* Isolate the tagname, from lbuf up to the first white */
tagp->tagname = lbuf;
#ifdef TAG_ANY_WHITE
p = skiptowhite(lbuf);
#else
p = vim_strchr(lbuf, TAB);
if (p == NULL)
return FAIL;
#endif
tagp->tagname_end = p;
/* Isolate file name, from first to second white space */
#ifdef TAG_ANY_WHITE
p = skipwhite(p);
#else
if (*p != NUL)
++p;
#endif
tagp->fname = p;
#ifdef TAG_ANY_WHITE
p = skiptowhite(p);
#else
p = vim_strchr(p, TAB);
if (p == NULL)
return FAIL;
#endif
tagp->fname_end = p;
/* find start of search command, after second white space */
#ifdef TAG_ANY_WHITE
p = skipwhite(p);
#else
if (*p != NUL)
++p;
#endif
if (*p == NUL)
return FAIL;
tagp->command = p;
#ifdef EMACS_TAGS
}
#endif
return OK;
}
/*
* Check if tagname is a static tag
*
* Static tags produced by the older ctags program have the format:
* 'file:tag file /pattern'.
* This is only recognized when both occurences of 'file' are the same, to
* avoid recognizing "string::string" or ":exit".
*
* Static tags produced by the new ctags program have the format:
* 'tag file /pattern/;"<Tab>file:' "
*
* Return TRUE if it is a static tag and adjust *tagname to the real tag.
* Return FALSE if it is not a static tag.
*/
static int
test_for_static(tagp)
struct tag_pointers *tagp;
{
char_u *p;
#ifdef OLD_STATIC_TAGS
int len;
/*
* Check for old style static tag: "file:tag file .."
*/
len = tagp->fname_end - tagp->fname;
p = tagp->tagname + len;
if ( p < tagp->tagname_end
&& *p == ':'
&& fnamencmp(tagp->tagname, tagp->fname, len) == 0)
{
tagp->tagname = p + 1;
return TRUE;
}
#endif
/*
* Check for new style static tag ":...<Tab>file:[<Tab>...]"
*/
p = tagp->command;
while ((p = vim_strchr(p, '\t')) != NULL)
{
++p;
if (STRNCMP(p, "file:", 5) == 0)
return TRUE;
}
return FALSE;
}
/*
* Parse a line from a matching tag. Does not change the line itself.
*
* The line that we get looks like this:
* Emacs tag: <mtt><tag_fname><NUL><ebuf><NUL><lbuf>
* other tag: <mtt><tag_fname><NUL><NUL><lbuf>
* without Emacs tags: <mtt><tag_fname><NUL><lbuf>
*
* Return OK or FAIL.
*/
static int
parse_match(lbuf, tagp)
char_u *lbuf; /* input: matching line */
struct tag_pointers *tagp; /* output: pointers into the line */
{
int retval;
char_u *p;
char_u *pc, *pt;
tagp->tag_fname = lbuf + 1;
lbuf += STRLEN(tagp->tag_fname) + 2;
#ifdef EMACS_TAGS
if (*lbuf)
{
tagp->is_etag = TRUE;
tagp->fname = lbuf;
lbuf += STRLEN(lbuf);
tagp->fname_end = lbuf++;
}
else
{
tagp->is_etag = FALSE;
++lbuf;
}
#endif
/* Find search pattern and the file name for non-etags. */
retval = parse_tag_line(lbuf,
#ifdef EMACS_TAGS
tagp->is_etag,
#endif
tagp);
tagp->tagkind = NULL;
tagp->command_end = NULL;
if (retval == OK)
{
/* Try to find a kind field: "kind:<kind>" or just "<kind>"*/
p = tagp->command;
if (find_extra(&p) == OK)
{
tagp->command_end = p;
p += 3; /* skip ";\"\t" */
while (isalpha(*p))
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -