📄 tag.c
字号:
if (STRNCMP(p, "kind:", 5) == 0)
{
tagp->tagkind = p + 5;
break;
}
pc = vim_strchr(p, ':');
pt = vim_strchr(p, '\t');
if (pc == NULL || (pt != NULL && pc > pt))
{
tagp->tagkind = p;
break;
}
if (pt == NULL)
break;
p = pt + 1;
}
}
if (tagp->tagkind != NULL)
{
for (p = tagp->tagkind;
*p && *p != '\t' && *p != '\r' && *p != '\n'; ++p)
;
tagp->tagkind_end = p;
}
}
return retval;
}
/*
* Jump to a tag that has been found in one of the tag files
*
* returns OK for success, NOTAGFILE when file not found, FAIL otherwise.
*/
static int
jumpto_tag(lbuf, forceit)
char_u *lbuf; /* line from the tags file for this tag */
int forceit; /* :ta with ! */
{
int save_secure;
int save_magic;
int save_p_ws, save_p_scs, save_p_ic;
int csave = 0;
char_u *str;
char_u *pbuf; /* search pattern buffer */
char_u *pbuf_end;
char_u *expanded_fname = NULL;
char_u *tofree_fname = NULL;
char_u *fname;
struct tag_pointers tagp;
int retval = FAIL;
int getfile_result;
int search_options;
pbuf = alloc(LSIZE);
/* parse the match line into the tagp structure */
if (pbuf == NULL || parse_match(lbuf, &tagp) == FAIL)
{
tagp.fname_end = NULL;
goto erret;
}
/* truncate the file name, so it can be used as a string */
csave = *tagp.fname_end;
*tagp.fname_end = NUL;
fname = tagp.fname;
/* copy the command to pbuf[], remove trailing CR/NL */
str = tagp.command;
for (pbuf_end = pbuf; *str && *str != '\n' && *str != '\r'; )
{
#ifdef EMACS_TAGS
if (tagp.is_etag && *str == ',')/* stop at ',' after line number */
break;
#endif
*pbuf_end++ = *str++;
}
*pbuf_end = NUL;
#ifdef EMACS_TAGS
if (!tagp.is_etag)
#endif
{
/*
* Remove the "<Tab>fieldname:value" stuff; we don't need it here.
*/
str = pbuf;
if (find_extra(&str) == OK)
{
pbuf_end = str;
*pbuf_end = NUL;
}
}
/*
* expand file name (for environment variables)
*/
expanded_fname = ExpandOne((char_u *)fname, NULL, WILD_LIST_NOTFOUND,
WILD_EXPAND_FREE);
if (expanded_fname != NULL)
fname = expanded_fname;
/*
* if 'tagrelative' option set, may change file name
*/
fname = expand_rel_name(fname, tagp.tag_fname);
if (fname == NULL)
goto erret;
tofree_fname = fname; /* free() it later */
/*
* check if file for tag exists before abandoning current file
*/
if (mch_getperm(fname) < 0)
{
retval = NOTAGFILE;
vim_free(nofile_fname);
nofile_fname = vim_strsave(fname);
if (nofile_fname == NULL)
nofile_fname = (char_u *)"";
goto erret;
}
++RedrawingDisabled;
#ifdef USE_GUI
need_mouse_correct = TRUE;
#endif
/* if it was a CTRL-W CTRL-] command split window now */
if (postponed_split)
win_split(postponed_split > 0 ? postponed_split : 0, FALSE, FALSE);
/* A :ta from a help file will keep the b_help flag set. */
keep_help_flag = curbuf->b_help;
getfile_result = getfile(0, fname, NULL, TRUE, (linenr_t)0, forceit);
keep_help_flag = FALSE;
if (getfile_result <= 0) /* got to the right file */
{
curwin->w_set_curswant = TRUE;
postponed_split = 0;
save_secure = secure;
secure = 1;
save_magic = p_magic;
p_magic = FALSE; /* always execute with 'nomagic' */
tag_modified = FALSE;
/*
* If 'cpoptions' contains 't', store the search pattern for the "n"
* command. If 'cpoptions' does not contain 't', the search pattern
* is not stored.
*/
if (vim_strchr(p_cpo, CPO_TAGPAT) != NULL)
search_options = 0;
else
search_options = SEARCH_KEEP;
/*
* If the command is a search, try here.
*
* Reset 'smartcase' for the search, since the search pattern was not
* typed by the user.
* Only use do_search() when there is a full search command, without
* anything following.
*/
str = pbuf;
if (pbuf[0] == '/' || pbuf[0] == '?')
str = skip_regexp(pbuf + 1, pbuf[0], FALSE) + 1;
if (str > pbuf_end - 1) /* search command with nothing following */
{
save_p_ws = p_ws;
save_p_ic = p_ic;
save_p_scs = p_scs;
p_ws = TRUE; /* Switch wrap-scan on temporarily */
p_ic = FALSE; /* don't ignore case now */
p_scs = FALSE;
/* put pattern in search history */
add_to_history(HIST_SEARCH, pbuf + 1);
curwin->w_cursor.lnum = 0; /* start search before first line */
if (do_search(NULL, pbuf[0], pbuf + 1, (long)1, search_options))
retval = OK;
else
{
int found = 1;
int cc;
/*
* try again, ignore case now
*/
p_ic = TRUE;
if (!do_search(NULL, pbuf[0], pbuf + 1, (long)1,
search_options))
{
/*
* Failed to find pattern, take a guess: "^func ("
*/
found = 2;
(void)test_for_static(&tagp);
cc = *tagp.tagname_end;
*tagp.tagname_end = NUL;
sprintf((char *)pbuf, "^%s\\s\\*(", tagp.tagname);
if (!do_search(NULL, '/', pbuf, (long)1, search_options))
{
/* Guess again: "^char * \<func (" */
sprintf((char *)pbuf, "^\\[#a-zA-Z_]\\.\\*\\<%s\\s\\*(",
tagp.tagname);
if (!do_search(NULL, '/', pbuf, (long)1,
search_options))
found = 0;
}
*tagp.tagname_end = cc;
}
if (found == 0)
EMSG("Can't find tag pattern");
else
{
/*
* Only give a message when really guessed, not when 'ic'
* is set and match found while ignoring case.
*/
if (found == 2 || !save_p_ic)
{
MSG("Couldn't find tag, just guessing!");
if (!msg_scrolled)
{
out_flush();
ui_delay(1000L, TRUE);
}
}
retval = OK;
}
}
p_ws = save_p_ws;
p_ic = save_p_ic;
p_scs = save_p_scs;
/* A search command may have positioned the cursor beyond the end
* of the line. May need to correct that here. */
adjust_cursor();
}
else
{
curwin->w_cursor.lnum = 1; /* start command in line 1 */
do_cmdline(pbuf, NULL, NULL, DOCMD_NOWAIT|DOCMD_VERBOSE);
retval = OK;
}
/*
* When the command has set the b_changed flag, give a warning to the
* user about this.
*/
if (tag_modified)
{
secure = 2;
EMSG("WARNING: tag command changed a buffer!!!");
}
if (secure == 2) /* done something that is not allowed */
wait_return(TRUE);
secure = save_secure;
p_magic = save_magic;
/* Return OK if jumped to another file (at least we found the file!). */
if (getfile_result == -1)
retval = OK;
/*
* For a help buffer: Put the cursor line at the top of the window,
* the help subject will be below it.
*/
if (curbuf->b_help)
{
set_topline(curwin, curwin->w_cursor.lnum);
update_topline(); /* correct for 'so' */
update_screen(NOT_VALID);
}
--RedrawingDisabled;
}
else
{
--RedrawingDisabled;
if (postponed_split) /* close the window */
{
close_window(curwin, FALSE);
postponed_split = 0;
}
}
erret:
if (tagp.fname_end != NULL)
*tagp.fname_end = csave;
vim_free(pbuf);
vim_free(tofree_fname);
vim_free(expanded_fname);
return retval;
}
/*
* If 'tagrelative' option set, change fname (name of file containing tag)
* according to tag_fname (name of tag file containing fname).
* Returns a pointer to allocated memory (or NULL when out of memory).
*/
static char_u *
expand_rel_name(fname, tag_fname)
char_u *fname;
char_u *tag_fname;
{
char_u *p;
char_u *retval;
if ((p_tr || curbuf->b_help) && !mch_isFullName(fname) &&
(p = gettail(tag_fname)) != tag_fname)
{
retval = alloc(MAXPATHL);
if (retval == NULL)
return NULL;
STRCPY(retval, tag_fname);
STRNCPY(retval + (p - tag_fname), fname, MAXPATHL - (p - tag_fname));
/*
* Translate names like "src/a/../b/file.c" into "src/b/file.c".
*/
simplify_filename(retval);
}
else
retval = vim_strsave(fname);
return retval;
}
/*
* Moves the tail part of the path (including the terminating NUL) pointed to
* by "tail" to the new location pointed to by "here". This should accomodate
* an overlapping move.
*/
#define movetail(here, tail) mch_memmove(here, tail, STRLEN(tail) + (size_t)1)
/*
* Converts a file name into a canonical form. It simplifies a file name into
* its simplest form by stripping out unneeded components, if any. The
* resulting file name is simplified in place and will either be the same
* length as that supplied, or shorter.
*/
void
simplify_filename(filename)
char_u *filename;
{
#ifndef AMIGA /* Amiga doesn't have "..", it uses "/" */
int components = 0;
char_u *p, *tail, *start;
#ifdef UNIX
char_u *orig = vim_strsave(filename);
if (orig == NULL)
return;
#endif
p = filename;
#ifdef BACKSLASH_IN_FILENAME
if (p[1] == ':') /* skip "x:" */
p += 2;
#endif
while (vim_ispathsep(*p))
++p;
start = p; /* remember start after "c:/" or "/" or "//" */
do
{
/* At this point "p" is pointing to the char following a "/". */
if (vim_ispathsep(*p))
movetail(p, p + 1); /* remove duplicate "/" */
else if (p[0] == '.' && vim_ispathsep(p[1]))
movetail(p, p + 2); /* strip "./" */
else if (p[0] == '.' && p[1] == '.' && vim_ispathsep(p[2]))
{
if (components > 0) /* strip one preceding component */
{
tail = p + 3; /* skip to after "../" or "..///" */
while (vim_ispathsep(*tail))
++tail;
--p;
/* skip back to after previous '/' */
while (p > start && !vim_ispathsep(p[-1]))
--p;
/* skip back to after first '/' in a row */
while (p - 1 > start && vim_ispathsep(p[-2]))
--p;
movetail(p, tail); /* strip previous component */
--components;
}
else /* leading "../" */
p += 3; /* skip to char after "/" */
}
else
{
++components; /* simple path component */
p = getnextcomp(p);
}
} while (p != NULL && *p != NUL);
#ifdef UNIX
/* Check that the new file name is really the same file. This will not be
* the case when using symbolic links: "dir/link/../name" != "dir/name". */
{
struct stat orig_st, new_st;
if ( stat((char *)orig, &orig_st) < 0
|| stat((char *)filename, &new_st) < 0
|| orig_st.st_ino != new_st.st_ino
|| orig_st.st_dev != new_st.st_dev)
STRCPY(filename, orig);
vim_free(orig);
}
#endif
#endif /* !AMIGA */
}
/*
* Check if we have a tag for the current file.
* This is a bit slow, because of the full path compare in fullpathcmp().
* Return TRUE if tag for file "fname" if tag file "tag_fname" is for current
* file.
*/
static int
#ifdef EMACS_TAGS
test_for_current(is_etag, fname, fname_end, tag_fname)
int is_etag;
#else
test_for_current(fname, fname_end, tag_fname)
#endif
char_u *fname;
char_u *fname_end;
char_u *tag_fname;
{
int c;
int retval = FALSE;
char_u *relname;
if (curbuf->b_ffname != NULL) /* if the current buffer has a name */
{
#ifdef EMACS_TAGS
if (is_etag)
c = 0; /* to shut up GCC */
else
#endif
{
c = *fname_end;
*fname_end = NUL;
}
relname = expand_rel_name(fname, tag_fname);
if (relname != NULL)
{
retval = (fullpathcmp(relname, curbuf->b_ffname, TRUE) & FPC_SAME);
vim_free(relname);
}
#ifdef EMACS_TAGS
if (!is_etag)
#endif
*fname_end = c;
}
return retval;
}
/*
* Find the end of the tagaddress.
* Return OK if ";\"\t" is following, FAIL otherwise.
*/
static int
find_extra(pp)
char_u **pp;
{
char_u *str = *pp;
/* Repeat for addresses separated with ';' */
for (;;)
{
if (isdigit(*str))
str = skipdigits(str);
else if (*str == '/' || *str == '?')
{
str = skip_regexp(str + 1, *str, FALSE);
if (*str != **pp)
str = NULL;
else
++str;
}
else
str = NULL;
if (str == NULL || *str != ';'
|| !(isdigit(str[1]) || str[1] == '/' || str[1] == '?'))
break;
++str; /* skip ';' */
}
if (str != NULL && STRNCMP(str, ";\"\t", 3) == 0)
{
*pp = str;
return OK;
}
return FAIL;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -