3875 lines · 94.7 KB · C
RawDownload
/* vi:set ts=8 sts=4 sw=4: * * VIM - Vi IMproved	by Bram Moolenaar * * Do ":help uganda"  in Vim to read copying and usage conditions. * Do ":help credits" in Vim to see a list of people who contributed. * See README.txt for an overview of the Vim source code. */ /* * Code to handle tags and the tag stack */ #if defined MSDOS || defined WIN32 || defined(_WIN64)# include "vimio.h"	/* for lseek(), must be before vim.h */#endif #include "vim.h" #ifdef HAVE_FCNTL_H# include <fcntl.h>	/* for lseek() */#endif /* * Structure to hold pointers to various items in a tag line. */typedef struct tag_pointers{    /* filled in by parse_tag_line(): */    char_u	*tagname;	/* start of tag name (skip "file:") */    char_u	*tagname_end;	/* char after tag name */    char_u	*fname;		/* first char of file name */    char_u	*fname_end;	/* char after file name */    char_u	*command;	/* first char of command */    /* filled in by parse_match(): */    char_u	*command_end;	/* first char after command */    char_u	*tag_fname;	/* file name of the tags file */#ifdef FEAT_EMACS_TAGS    int		is_etag;	/* TRUE for emacs tag */#endif    char_u	*tagkind;	/* "kind:" value */    char_u	*tagkind_end;	/* end of tagkind */} tagptrs_T; /* * The matching tags are first stored in ga_match[].  In which one depends on * the priority of the match. * At the end, the matches from ga_match[] are concatenated, to make a list * sorted on priority. */#define MT_ST_CUR	0		/* static match in current file */#define MT_GL_CUR	1		/* global match in current file */#define MT_GL_OTH	2		/* global match in other file */#define MT_ST_OTH	3		/* static match in other file */#define MT_IC_ST_CUR	4		/* icase static match in current file */#define MT_IC_GL_CUR	5		/* icase global match in current file */#define MT_IC_GL_OTH	6		/* icase global match in other file */#define MT_IC_ST_OTH	7		/* icase static match in other file */#define MT_IC_OFF	4		/* add for icase match */#define MT_RE_OFF	8		/* add for regexp match */#define MT_MASK		7		/* mask for printing priority */#define MT_COUNT	16 static char	*mt_names[MT_COUNT/2] =		{"FSC", "F C", "F  ", "FS ", " SC", "  C", "   ", " S "}; #define NOTAGFILE	99		/* return value for jumpto_tag */static char_u	*nofile_fname = NULL;	/* fname for NOTAGFILE error */ static void taglen_advance __ARGS((int l)); static int jumpto_tag __ARGS((char_u *lbuf, int forceit, int keep_help));#ifdef FEAT_EMACS_TAGSstatic int parse_tag_line __ARGS((char_u *lbuf, int is_etag, tagptrs_T *tagp));#elsestatic int parse_tag_line __ARGS((char_u *lbuf, tagptrs_T *tagp));#endifstatic int test_for_static __ARGS((tagptrs_T *));static int parse_match __ARGS((char_u *lbuf, tagptrs_T *tagp));static char_u *tag_full_fname __ARGS((tagptrs_T *tagp));static char_u *expand_tag_fname __ARGS((char_u *fname, char_u *tag_fname, int expand));#ifdef FEAT_EMACS_TAGSstatic int test_for_current __ARGS((int, char_u *, char_u *, char_u *, char_u *));#elsestatic int test_for_current __ARGS((char_u *, char_u *, char_u *, char_u *));#endifstatic int find_extra __ARGS((char_u **pp)); static char_u *bottommsg = (char_u *)N_("E555: at bottom of tag stack");static char_u *topmsg = (char_u *)N_("E556: at top of tag stack"); static char_u	*tagmatchname = NULL;	/* name of last used tag */ /* * We use ftello() here, if available.  It returns off_t instead of long, * which helps if long is 32 bit and off_t is 64 bit. */#ifdef HAVE_FTELLO# define ftell ftello#endif #if defined(FEAT_WINDOWS) && defined(FEAT_QUICKFIX)/* * Tag for preview window is remembered separately, to avoid messing up the * normal tagstack. */static taggy_T ptag_entry = {NULL};#endif /* * Jump to tag; handling of tag commands and tag stack * * *tag != NUL: ":tag {tag}", jump to new tag, add to tag stack * * type == DT_TAG:	":tag [tag]", jump to newer position or same tag again * type == DT_HELP:	like DT_TAG, but don't use regexp. * type == DT_POP:	":pop" or CTRL-T, jump to old position * type == DT_NEXT:	jump to next match of same tag * type == DT_PREV:	jump to previous match of same tag * type == DT_FIRST:	jump to first match of same tag * type == DT_LAST:	jump to last match of same tag * type == DT_SELECT:	":tselect [tag]", select tag from a list of all matches * type == DT_JUMP:	":tjump [tag]", jump to tag or select tag from a list * type == DT_CSCOPE:	use cscope to find the tag * type == DT_LTAG:	use location list for displaying tag matches * type == DT_FREE:	free cached matches * * for cscope, returns TRUE if we jumped to tag or aborted, FALSE otherwise */    intdo_tag(tag, type, count, forceit, verbose)    char_u	*tag;		/* tag (pattern) to jump to */    int		type;    int		count;    int		forceit;	/* :ta with ! */    int		verbose;	/* print "tag not found" message */{    taggy_T	*tagstack = curwin->w_tagstack;    int		tagstackidx = curwin->w_tagstackidx;    int		tagstacklen = curwin->w_tagstacklen;    int		cur_match = 0;    int		cur_fnum = curbuf->b_fnum;    int		oldtagstackidx = tagstackidx;    int		prevtagstackidx = tagstackidx;    int		prev_num_matches;    int		new_tag = FALSE;    int		other_name;    int		i, j, k;    int		idx;    int		ic;    char_u	*p;    char_u	*name;    int		no_regexp = FALSE;    int		error_cur_match = 0;    char_u	*command_end;    int		save_pos = FALSE;    fmark_T	saved_fmark;    int		taglen;#ifdef FEAT_CSCOPE    int		jumped_to_tag = FALSE;#endif    tagptrs_T	tagp, tagp2;    int		new_num_matches;    char_u	**new_matches;    int		attr;    int		use_tagstack;    int		skip_msg = FALSE;    char_u	*buf_ffname = curbuf->b_ffname;	    /* name to use for						       priority computation */     /* remember the matches for the last used tag */    static int		num_matches = 0;    static int		max_num_matches = 0;  /* limit used for match search */    static char_u	**matches = NULL;    static int		flags; #ifdef EXITFREE    if (type == DT_FREE)    {	/* remove the list of matches */	FreeWild(num_matches, matches);# ifdef FEAT_CSCOPE	cs_free_tags();# endif	num_matches = 0;	return FALSE;    }#endif     if (type == DT_HELP)    {	type = DT_TAG;	no_regexp = TRUE;    }     prev_num_matches = num_matches;    free_string_option(nofile_fname);    nofile_fname = NULL;     clearpos(&saved_fmark.mark);	/* shutup gcc 4.0 */    saved_fmark.fnum = 0;     /*     * Don't add a tag to the tagstack if 'tagstack' has been reset.     */    if ((!p_tgst && *tag != NUL))    {	use_tagstack = FALSE;	new_tag = TRUE;    }    else    {#if defined(FEAT_WINDOWS) && defined(FEAT_QUICKFIX)	if (g_do_tagpreview)	    use_tagstack = FALSE;	else#endif	    use_tagstack = TRUE; 	/* new pattern, add to the tag stack */	if (*tag != NUL		&& (type == DT_TAG || type == DT_SELECT || type == DT_JUMP#ifdef FEAT_QUICKFIX		    || type == DT_LTAG#endif#ifdef FEAT_CSCOPE		    || type == DT_CSCOPE#endif		    ))	{#if defined(FEAT_WINDOWS) && defined(FEAT_QUICKFIX)	    if (g_do_tagpreview)	    {		if (ptag_entry.tagname != NULL			&& STRCMP(ptag_entry.tagname, tag) == 0)		{		    /* Jumping to same tag: keep the current match, so that		     * the CursorHold autocommand example works. */		    cur_match = ptag_entry.cur_match;		    cur_fnum = ptag_entry.cur_fnum;		}		else		{		    vim_free(ptag_entry.tagname);		    if ((ptag_entry.tagname = vim_strsave(tag)) == NULL)			goto end_do_tag;		}	    }	    else#endif	    {		/*		 * If the last used entry is not at the top, delete all tag		 * stack entries above it.		 */		while (tagstackidx < tagstacklen)		    vim_free(tagstack[--tagstacklen].tagname); 		/* if the tagstack is full: remove oldest entry */		if (++tagstacklen > TAGSTACKSIZE)		{		    tagstacklen = TAGSTACKSIZE;		    vim_free(tagstack[0].tagname);		    for (i = 1; i < tagstacklen; ++i)			tagstack[i - 1] = tagstack[i];		    --tagstackidx;		} 		/*		 * put the tag name in the tag stack		 */		if ((tagstack[tagstackidx].tagname = vim_strsave(tag)) == NULL)		{		    curwin->w_tagstacklen = tagstacklen - 1;		    goto end_do_tag;		}		curwin->w_tagstacklen = tagstacklen; 		save_pos = TRUE;	/* save the cursor position below */	    } 	    new_tag = TRUE;	}	else	{	    if (#if defined(FEAT_WINDOWS) && defined(FEAT_QUICKFIX)		    g_do_tagpreview ? ptag_entry.tagname == NULL :#endif		    tagstacklen == 0)	    {		/* empty stack */		EMSG(_(e_tagstack));		goto end_do_tag;	    } 	    if (type == DT_POP)		/* go to older position */	    {#ifdef FEAT_FOLDING		int	old_KeyTyped = KeyTyped;#endif		if ((tagstackidx -= count) < 0)		{		    EMSG(_(bottommsg));		    if (tagstackidx + count == 0)		    {			/* We did [num]^T from the bottom of the stack */			tagstackidx = 0;			goto end_do_tag;		    }		    /* We weren't at the bottom of the stack, so jump all the		     * way to the bottom now.		     */		    tagstackidx = 0;		}		else if (tagstackidx >= tagstacklen)    /* count == 0? */		{		    EMSG(_(topmsg));		    goto end_do_tag;		} 		/* Make a copy of the fmark, autocommands may invalidate the		 * tagstack before it's used. */		saved_fmark = tagstack[tagstackidx].fmark;		if (saved_fmark.fnum != curbuf->b_fnum)		{		    /*		     * Jump to other file. If this fails (e.g. because the		     * file was changed) keep original position in tag stack.		     */		    if (buflist_getfile(saved_fmark.fnum, saved_fmark.mark.lnum,					       GETF_SETMARK, forceit) == FAIL)		    {			tagstackidx = oldtagstackidx;  /* back to old posn */			goto end_do_tag;		    }		    /* An BufReadPost autocommand may jump to the '" mark, but		     * we don't what that here. */		    curwin->w_cursor.lnum = saved_fmark.mark.lnum;		}		else		{		    setpcmark();		    curwin->w_cursor.lnum = saved_fmark.mark.lnum;		}		curwin->w_cursor.col = saved_fmark.mark.col;		curwin->w_set_curswant = TRUE;		check_cursor();#ifdef FEAT_FOLDING		if ((fdo_flags & FDO_TAG) && old_KeyTyped)		    foldOpenCursor();#endif 		/* remove the old list of matches */		FreeWild(num_matches, matches);#ifdef FEAT_CSCOPE		cs_free_tags();#endif		num_matches = 0;		tag_freematch();		goto end_do_tag;	    } 	    if (type == DT_TAG#if defined(FEAT_QUICKFIX)		    || type == DT_LTAG#endif	       )	    {#if defined(FEAT_WINDOWS) && defined(FEAT_QUICKFIX)		if (g_do_tagpreview)		{		    cur_match = ptag_entry.cur_match;		    cur_fnum = ptag_entry.cur_fnum;		}		else#endif		{		    /* ":tag" (no argument): go to newer pattern */		    save_pos = TRUE;	/* save the cursor position below */		    if ((tagstackidx += count - 1) >= tagstacklen)		    {			/*			 * Beyond the last one, just give an error message and			 * go to the last one.  Don't store the cursor			 * position.			 */			tagstackidx = tagstacklen - 1;			EMSG(_(topmsg));			save_pos = FALSE;		    }		    else if (tagstackidx < 0)	/* must have been count == 0 */		    {			EMSG(_(bottommsg));			tagstackidx = 0;			goto end_do_tag;		    }		    cur_match = tagstack[tagstackidx].cur_match;		    cur_fnum = tagstack[tagstackidx].cur_fnum;		}		new_tag = TRUE;	    }	    else				/* go to other matching tag */	    {		/* Save index for when selection is cancelled. */		prevtagstackidx = tagstackidx; #if defined(FEAT_WINDOWS) && defined(FEAT_QUICKFIX)		if (g_do_tagpreview)		{		    cur_match = ptag_entry.cur_match;		    cur_fnum = ptag_entry.cur_fnum;		}		else#endif		{		    if (--tagstackidx < 0)			tagstackidx = 0;		    cur_match = tagstack[tagstackidx].cur_match;		    cur_fnum = tagstack[tagstackidx].cur_fnum;		}		switch (type)		{		    case DT_FIRST: cur_match = count - 1; break;		    case DT_SELECT:		    case DT_JUMP:#ifdef FEAT_CSCOPE		    case DT_CSCOPE:#endif		    case DT_LAST:  cur_match = MAXCOL - 1; break;		    case DT_NEXT:  cur_match += count; break;		    case DT_PREV:  cur_match -= count; break;		}		if (cur_match >= MAXCOL)		    cur_match = MAXCOL - 1;		else if (cur_match < 0)		{		    EMSG(_("E425: Cannot go before first matching tag"));		    skip_msg = TRUE;		    cur_match = 0;		    cur_fnum = curbuf->b_fnum;		}	    }	} #if defined(FEAT_WINDOWS) && defined(FEAT_QUICKFIX)	if (g_do_tagpreview)	{	    if (type != DT_SELECT && type != DT_JUMP)	    {		ptag_entry.cur_match = cur_match;		ptag_entry.cur_fnum = cur_fnum;	    }	}	else#endif	{	    /*	     * For ":tag [arg]" or ":tselect" remember position before the jump.	     */	    saved_fmark = tagstack[tagstackidx].fmark;	    if (save_pos)	    {		tagstack[tagstackidx].fmark.mark = curwin->w_cursor;		tagstack[tagstackidx].fmark.fnum = curbuf->b_fnum;	    } 	    /* Curwin will change in the call to jumpto_tag() if ":stag" was	     * used or an autocommand jumps to another window; store value of	     * tagstackidx now. */	    curwin->w_tagstackidx = tagstackidx;	    if (type != DT_SELECT && type != DT_JUMP)	    {		curwin->w_tagstack[tagstackidx].cur_match = cur_match;		curwin->w_tagstack[tagstackidx].cur_fnum = cur_fnum;	    }	}    }     /* When not using the current buffer get the name of buffer "cur_fnum".     * Makes sure that the tag order doesn't change when using a remembered     * position for "cur_match". */    if (cur_fnum != curbuf->b_fnum)    {	buf_T *buf = buflist_findnr(cur_fnum); 	if (buf != NULL)	    buf_ffname = buf->b_ffname;    }     /*     * Repeat searching for tags, when a file has not been found.     */    for (;;)    {	/*	 * When desired match not found yet, try to find it (and others).	 */	if (use_tagstack)	    name = tagstack[tagstackidx].tagname;

Lines 1–500 of 3875.