📄 tag.c
字号:
++p;
if (*p == '^')
++p;
}
/* Remove leading whitespace from pattern */
while (p != command_end && vim_isspace(*p))
++p;
while (p != command_end)
{
if (msg_col + (*p == TAB ? 1 : charsize(*p)) > Columns)
msg_putchar('\n');
msg_advance(15);
/* skip backslash used for escaping command char */
if (*p == '\\' && *(p + 1) == *tagp.command)
++p;
if (*p == TAB)
msg_putchar(' ');
else
msg_puts(transchar(*p));
++p;
/* don't display the "$/;\"" and "$?;\"" */
if (p == command_end - 2 && *p == '$'
&& *(p + 1) == *tagp.command)
break;
if (p == command_end - 1 && *p == *tagp.command)
break;
}
if (msg_col)
msg_putchar('\n');
ui_breakcheck();
if (got_int)
{
got_int = FALSE; /* only stop the listing */
break;
}
}
ask_for_selection = TRUE;
}
if (ask_for_selection == TRUE)
{
/*
* Ask to select a tag from the list.
*/
MSG_PUTS("Enter nr of choice (<CR> to abort): ");
i = get_number(TRUE);
if (KeyTyped) /* don't call wait_return() now */
{
msg_putchar('\n');
dont_wait_return = TRUE;
need_wait_return = FALSE;
msg_didany = FALSE;
cmdline_row = msg_row;
msg_scrolled = 0;
redraw_all_later(NOT_VALID);
}
if (i <= 0 || i > num_matches || got_int)
{
/* no valid choice: don't change anything */
tagstack[tagstackidx].fmark = saved_fmark;
++tagstackidx;
#ifdef USE_CSCOPE
cs_free_tags();
jumped_to_tag = TRUE;
#endif
break;
}
cur_match = i - 1;
}
if (cur_match >= num_matches)
cur_match = num_matches - 1;
curwin->w_tagstack[tagstackidx].cur_match = cur_match;
++tagstackidx;
/*
* Only when going to try the next match, report that the previous
* file didn't exist. Otherwise an EMSG() is given below.
*/
if (nofile_fname != NULL && error_cur_match != cur_match)
smsg((char_u *)"File \"%s\" does not exist", nofile_fname);
ic = (matches[cur_match][0] & MT_IC_OFF);
if (type != DT_SELECT && type != DT_JUMP
#ifdef USE_CSCOPE
&& type != DT_CSCOPE
#endif
&& (num_matches > 1 || ic))
{
/* Give an indication of the number of matching tags */
sprintf((char *)msg_buf, "tag %d of %d%s",
cur_match + 1,
num_matches,
max_num_matches != MAXCOL ? " or more" : "");
if (ic)
STRCAT(msg_buf, " Using tag with different case!");
if ((num_matches > prev_num_matches || new_tag)
&& num_matches > 1)
{
if (ic)
msg_attr(msg_buf, hl_attr(HLF_W));
else
msg(msg_buf);
msg_scroll = TRUE; /* don't overwrite this message */
}
else
give_warning(msg_buf, ic);
if (ic && !msg_scrolled)
{
out_flush();
ui_delay(1000L, TRUE);
}
}
/*
* Jump to the desired match.
*/
if (jumpto_tag(matches[cur_match], forceit) == NOTAGFILE)
{
/* File not found: try again with another matching tag */
if ((type == DT_PREV && cur_match > 0)
|| ((type == DT_TAG || type == DT_NEXT
|| type == DT_FIRST)
&& (max_num_matches != MAXCOL
|| cur_match < num_matches - 1)))
{
error_cur_match = cur_match;
--tagstackidx;
if (type == DT_PREV)
--cur_match;
else
{
type = DT_NEXT;
++cur_match;
}
continue;
}
EMSG2("File \"%s\" does not exist", nofile_fname);
}
#ifdef USE_CSCOPE
else
jumped_to_tag = TRUE;
#endif
}
break;
}
end_do_tag:
curwin->w_tagstackidx = tagstackidx;
curwin->w_tagstacklen = tagstacklen;
postponed_split = 0; /* don't split next time */
#ifdef USE_CSCOPE
return jumped_to_tag;
#else
return FALSE;
#endif
}
static void
taglen_advance(l)
int l;
{
if (l == MAXCOL)
{
msg_putchar('\n');
msg_advance(24);
}
else
msg_advance(13 + l);
}
/*
* Print the tag stack
*/
void
do_tags()
{
int i;
char_u *name;
struct taggy *tagstack = curwin->w_tagstack;
int tagstackidx = curwin->w_tagstackidx;
int tagstacklen = curwin->w_tagstacklen;
/* Highlight title */
MSG_PUTS_TITLE("\n # TO tag FROM line in file");
for (i = 0; i < tagstacklen; ++i)
{
if (tagstack[i].tagname != NULL)
{
name = fm_getname(&(tagstack[i].fmark));
if (name == NULL) /* file name not available */
continue;
msg_putchar('\n');
sprintf((char *)IObuff, "%c%2d %2d %-15s %5ld %s",
i == tagstackidx ? '>' : ' ',
i + 1,
tagstack[i].cur_match + 1,
tagstack[i].tagname,
tagstack[i].fmark.mark.lnum,
name);
msg_outtrans(IObuff);
vim_free(name);
}
out_flush(); /* show one line at a time */
}
if (tagstackidx == tagstacklen) /* idx at top of stack */
MSG_PUTS("\n>");
}
/*
* find_tags() - search for tags in tags files
*
* Return FAIL if search completely failed (*num_matches will be 0, *matchesp
* will be NULL), OK otherwise.
*
* There is a priority in which type of tag is recognized.
*
* 6. A static or global tag with a full matching tag for the current file.
* 5. A global tag with a full matching tag for another file.
* 4. A static tag with a full matching tag for another file.
* 3. A static or global tag with an ignore-case matching tag for the
* current file.
* 2. A global tag with an ignore-case matching tag for another file.
* 1. A static tag with an ignore-case matching tag for another file.
*
* Tags in an emacs-style tags file are always global.
*
* flags:
* TAG_HELP only search for help tags
* TAG_NAMES only return name of tag
* TAG_REGEXP use "pat" as a regexp
* TAG_NOIC don't always ignore case
*/
int
find_tags(pat, num_matches, matchesp, flags, mincount)
char_u *pat; /* pattern to search for */
int *num_matches; /* return: number of matches found */
char_u ***matchesp; /* return: array of matches found */
int flags;
int mincount; /* MAXCOL: find all matches
other: minimal number of matches */
{
FILE *fp;
char_u *lbuf; /* line buffer */
char_u *tag_fname; /* name of tag file */
int first_file; /* trying first tag file */
struct tag_pointers tagp;
int did_open = FALSE; /* did open a tag file */
int stop_searching = FALSE; /* stop when match found or error */
int retval = FAIL; /* return value */
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;
vim_regexp *prog = NULL; /* regexp program or NULL */
#ifdef BINARY_TAGS
struct tag_search_info /* Binary search file offsets */
{
long low_offset; /* offset for first char of first line that
could match */
long high_offset; /* offset of char after last line that could
match */
long curr_offset; /* Current file offset in search range */
long match_offset; /* Where the binary search found a tag */
int low_char; /* first char at low_offset */
int high_char; /* first char at high_offset */
} search_info;
off_t filesize;
int tagcmp;
long offset;
#endif
enum
{
TS_START, /* at start of file */
TS_LINEAR, /* linear searching forward, till EOF */
#ifdef BINARY_TAGS
TS_BINARY, /* binary searching */
TS_SKIP_BACK, /* skipping backwards */
TS_STEP_FORWARD /* stepping forwards */
#endif
} state; /* Current search state */
int cmplen;
int match; /* matches */
int match_no_ic = 0;/* matches with reg_ic == FALSE */
int match_re; /* match with regexp */
int matchoff = 0;
#ifdef 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 */
char_u *ebuf; /* aditional buffer for etag fname */
int is_etag; /* current file is emaces style */
#endif
struct growarray ga_match[MT_COUNT];
int match_count = 0; /* number of matches found */
char_u **matches;
int mtt;
int len;
int help_save;
int patlen; /* length of pat[] */
char_u *pathead; /* start of pattern head */
int patheadlen; /* length of pathead[] */
#ifdef BINARY_TAGS
int findall = (mincount == MAXCOL); /* find all matching tags */
int sort_error = FALSE; /* tags file not sorted */
int linear; /* do a linear search */
#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);
int get_it_again = FALSE;
#ifdef USE_CSCOPE
int use_cscope = (flags & TAG_CSCOPE);
#endif
int verbose = (flags & TAG_VERBOSE);
help_save = curbuf->b_help;
/*
* Allocate memory for the buffers that are used
*/
if (has_re)
prog = vim_regcomp(pat, p_magic);
lbuf = alloc(LSIZE);
tag_fname = alloc(LSIZE + 1);
#ifdef EMACS_TAGS
ebuf = alloc(LSIZE);
#endif
for (mtt = 0; mtt < MT_COUNT; ++mtt)
{
ga_init2(&ga_match[mtt], (int)sizeof(char_u *), 100);
}
/* check for out of memory situation */
if (lbuf == NULL || tag_fname == NULL
#ifdef EMACS_TAGS
|| ebuf == NULL
#endif
)
goto findtag_end;
/*
* Initialize a few variables
*/
if (help_only) /* want tags from help file */
curbuf->b_help = TRUE; /* will be restored later */
patlen = STRLEN(pat);
if (p_tl != 0 && patlen > p_tl) /* adjust for 'taglength' */
patlen = p_tl;
pathead = pat;
patheadlen = patlen;
if (has_re)
{
/* When the pattern starts with '^' or "\\<", binary searching can be
* used (much faster). */
if (pat[0] == '^')
pathead = pat + 1;
else if (pat[0] == '\\' && pat[1] == '<')
pathead = pat + 2;
if (pathead == pat)
patheadlen = 0;
else
for (patheadlen = 0; pathead[patheadlen] != NUL; ++patheadlen)
if (vim_strchr((char_u *)(p_magic ? ".[~*\\$" : "\\$"),
pathead[patheadlen]) != NULL)
break;
if (p_tl != 0 && patheadlen > p_tl) /* adjust for 'taglength' */
patheadlen = p_tl;
}
/*
* 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.
* Only ignore case when TAG_NOIC not used or 'ignorecase' set.
*/
#ifdef BINARY_TAGS
reg_ic = ((p_ic || !noic) && (findall || patheadlen == 0 || !p_tbs));
for (;;)
{
linear = (reg_ic || patheadlen == 0 || !p_tbs);
#else
reg_ic = (p_ic || !noic);
#endif
/*
* Try tag file names from tags option one by one.
*/
for (first_file = TRUE;
#ifdef USE_CSCOPE
use_cscope ||
#endif
get_tagfname(first_file, tag_fname) == OK; first_file = FALSE)
{
/*
* 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 USE_CSCOPE
if (use_cscope)
fp = NULL; /* avoid GCC warning */
else
#endif
if ((fp = fopen((char *)tag_fname, "r")) == NULL)
continue;
did_open = TRUE; /* remember that we found at least one file */
state = TS_START; /* we're at the start of the file */
#ifdef EMACS_TAGS
is_etag = 0; /* default is: not emacs style */
#endif
/*
* Read and parse the lines in the file one by one
*/
while (!got_int)
{
line_breakcheck(); /* check for CTRL-C typed */
if (get_it_again)
goto line_read_in;
#ifdef BINARY_TAGS
/*
* For binary search: compute the next offset to use.
*/
if (state == TS_BINARY)
{
offset = search_info.low_offset + ((search_info.high_offset
- search_info.low_offset) / 2);
if (offset == search_info.curr_offset)
break; /* End the binary search without a match. */
else
search_info.curr_offset = offset;
}
/*
* Skipping back (after a match during binary search).
*/
else if (state == TS_SKIP_BACK)
{
search_info.curr_offset -= LSIZE * 2;
if (search_info.curr_offset < 0)
{
search_info.curr_offset = 0;
rewind(fp);
state = TS_STEP_FORWARD;
}
}
/*
* When jumping around in the file, first read a line to find the
* start of the next line.
*/
if (state == TS_BINARY || state == TS_SKIP_BACK)
{
/* Adjust the search file offset to the correct position */
fseek(fp, search_info.curr_offset, SEEK_SET);
eof = vim_fgets(lbuf, LSIZE, fp);
if (!eof && search_info.curr_offset)
{
search_info.curr_offset = ftell(fp);
if (search_info.curr_offset == search_info.high_offset)
{
/* oops, gone a bit too far; try from low offset */
fseek(fp, search_info.low_offset, SEEK_SET);
search_info.curr_offset = search_info.low_offset;
}
eof = vim_fgets(lbuf, LSIZE, fp);
}
/* skip empty and blank lines */
while (!eof && vim_isblankline(lbuf))
{
search_info.curr_offset = ftell(fp);
eof = vim_fgets(lbuf, LSIZE, fp);
}
if (eof)
{
/* Hit end of file. Skip backwards. */
state = TS_SKIP_BACK;
search_info.match_offset = ftell(fp);
continue;
}
}
/*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -