⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 syntax.c

📁 VIM文本编辑器
💻 C
📖 第 1 页 / 共 5 页
字号:
/* 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.
 */

/*
 * syntax.c: code for syntax highlighting
 */

#include "vim.h"

/*
 * Structure that stores information about a highlight group.
 * The ID of a highlight group is also called group ID.  It is the index in
 * the highlight_ga array PLUS ONE.
 */
struct hl_group
{
    char_u	*sg_name;	/* highlight group name */
    char_u	*sg_name_u;	/* uppercase of sg_name */
/* for normal terminals */
    int		sg_term;	/* "term=" highlighting attributes */
    char_u	*sg_start;	/* terminal string for start highl */
    char_u	*sg_stop;	/* terminal string for stop highl */
    int		sg_term_attr;	/* NextScreen attr for term mode */
/* for color terminals */
    int		sg_cterm;	/* "cterm=" highlighting attr */
    int		sg_cterm_bold;	/* bold attr was set for light color */
    int		sg_cterm_fg;	/* terminal fg color number + 1 */
    int		sg_cterm_bg;	/* terminal bg color number + 1 */
    int		sg_cterm_attr;	/* NextScreen attr for color term mode */
#ifdef USE_GUI
/* for when using the GUI */
    int		sg_gui;		/* "gui=" highlighting attributes */
    GuiColor	sg_gui_fg;	/* GUI foreground color handle + 1 */
    char_u	*sg_gui_fg_name;/* GUI foreground color name */
    GuiColor	sg_gui_bg;	/* GUI background color handle + 1 */
    char_u	*sg_gui_bg_name;/* GUI background color name */
    GuiFont	sg_font;	/* GUI font handle */
    char_u	*sg_font_name;  /* GUI font name */
    int		sg_gui_attr;    /* NextScreen attr for GUI mode */
#endif
    int		sg_link;	/* link to this highlight group ID */
    int		sg_set;		/* combination of SG_* */
};

#define SG_TERM		1	/* term has been set */
#define SG_CTERM	2	/* cterm has been set */
#define SG_GUI		4	/* gui has been set */
#define SG_LINK		8	/* link has been set */

static struct growarray highlight_ga;	    /* highlight groups for
					       'highlight' option */

#define HL_TABLE() ((struct hl_group *)((highlight_ga.ga_data)))

static int include_link = FALSE;	/* include "link" for expansion */

/*
 * The "term", "cterm" and "gui" arguments can be any combination of the
 * following names, separated by commas (but no spaces!).
 */
static char *(hl_name_table[]) =
    {"bold", "standout", "underline", "italic", "reverse", "inverse", "NONE"};
static int hl_attr_table[] =
    {HL_BOLD, HL_STANDOUT, HL_UNDERLINE, HL_ITALIC, HL_INVERSE, HL_INVERSE, 0};

static int get_attr_entry  __ARGS((struct growarray *table, struct attr_entry *aep));
static int syn_namen2id __ARGS((char_u *linep, int len));
static void syn_unadd_group __ARGS((void));
static void set_hl_attr __ARGS((int idx));
static void highlight_list_one __ARGS((int id));
static void highlight_list_two __ARGS((int cnt, int attr));
static int highlight_list_arg __ARGS((int id, int didh, int type, int iarg, char_u *sarg, char *name));
static int syn_add_group __ARGS((char_u *name));
static int syn_list_header __ARGS((int did_header, int outlen, int id));
static void highlight_list __ARGS((void));
static void highlight_clear __ARGS((int idx));

#ifdef USE_GUI
static void gui_do_one_color __ARGS((int idx));
static int  set_group_colors __ARGS((char_u *name, GuiColor *fgp, GuiColor *bgp));
static GuiColor color_name2handle __ARGS((char_u *name));
static GuiFont font_name2handle __ARGS((char_u *name));
#endif

/*
 * An attribute number is the index in attr_table plus ATTR_OFF.
 */
#define ATTR_OFF (HL_ALL + 1)

#ifdef SYNTAX_HL

#define SYN_NAMELEN	50		/* maximum length of a syntax name */

/* different types of offsets that are possible */
#define SPO_MS_OFF	0	/* match  start offset */
#define SPO_ME_OFF	1	/* match  end	offset */
#define SPO_HS_OFF	2	/* highl. start offset */
#define SPO_HE_OFF	3	/* highl. end	offset */
#define SPO_RS_OFF	4	/* region start offset */
#define SPO_RE_OFF	5	/* region end	offset */
#define SPO_LC_OFF	6	/* leading context offset */
#define SPO_COUNT	7

static char *(spo_name_tab[SPO_COUNT]) =
	    {"ms=", "me=", "hs=", "he=", "rs=", "re=", "lc="};

/*
 * The patterns that are being searched for are stored in a syn_pattern.
 * A match item consists of one pattern.
 * A start/end item consists of n start patterns and m end patterns.
 * A start/skip/end item consists of n start patterns, one skip pattern and m
 * end patterns.
 * For the latter two, the patterns are always consecutive: start-skip-end.
 *
 * A character offset can be given for the matched text (_m_start and _m_end)
 * and for the actually highlighted text (_h_start and _h_end).
 */
struct syn_pattern
{
    char		 sp_type;	    /* see SPTYPE_ defines below */
    char		 sp_syncing;	    /* this item used for syncing */
    short		 sp_flags;	    /* see HL_ defines below */
    int			 sp_syn_inc_lvl;    /* ":syn include" level of item */
    short		 sp_syn_id;	    /* highlight group ID of item */
    short		 sp_syn_match_id;   /* highlight group ID of pattern */
    char_u		*sp_pattern;	    /* regexp to match, pattern */
    vim_regexp		*sp_prog;	    /* regexp to match, program */
    int			 sp_ic;		    /* ignore-case flag for sp_prog */
    short		 sp_off_flags;	    /* see below */
    int			 sp_offsets[SPO_COUNT];	/* offsets */
    short		*sp_cont_list;	    /* cont. group IDs, if non-zero */
    short		*sp_next_list;	    /* next group IDs, if non-zero */
    int			 sp_sync_idx;	    /* sync item index (syncing only) */
    int			 sp_line_id;	    /* ID of last line where tried */
    int			 sp_startcol;	    /* next match in sp_line_id line */
};

/* The sp_off_flags are computed like this:
 * offset from the start of the matched text: (1 << SPO_XX_OFF)
 * offset from the end	 of the matched text: (1 << (SPO_XX_OFF + SPO_COUNT))
 * When both are present, only one is used.
 */

#define SPTYPE_MATCH	1	/* match keyword with this group ID */
#define SPTYPE_START	2	/* match a regexp, start of item */
#define SPTYPE_END	3	/* match a regexp, end of item */
#define SPTYPE_SKIP	4	/* match a regexp, skip within item */

#define HL_CONTAINED	0x01	/* not used on toplevel */
#define HL_TRANSP	0x02	/* has no highlighting	*/
#define HL_ONELINE	0x04	/* match within one line only */
#define HL_HAS_EOL	0x08	/* end pattern that matches with $ */
#define HL_SYNC_HERE	0x10	/* sync point after this item (syncing only) */
#define HL_SYNC_THERE	0x20	/* sync point at current line (syncing only) */
#define HL_MATCH	0x40	/* use match ID instead of item ID */
#define HL_SKIPNL	0x80	/* nextgroup can skip newlines */
#define HL_SKIPWHITE	0x100	/* nextgroup can skip white space */
#define HL_SKIPEMPTY	0x200	/* nextgroup can skip empty lines */
#define HL_KEEPEND	0x400	/* end match always kept */

#define SYN_ITEMS(buf)	((struct syn_pattern *)((buf)->b_syn_patterns.ga_data))

#define NONE_IDX	-2	/* value of sp_sync_idx for "NONE" */

/*
 * Flags for b_syn_sync_flags:
 */
#define SF_CCOMMENT	0x01	/* sync on a C-style comment */
#define SF_MATCH	0x02	/* sync by matching a pattern */

/*
 * Struct used to store states for the start of some lines for a buffer.
 */
struct buf_state
{
    int	    bs_idx;		/* index of pattern */
    int	    bs_flags;		/* flags for pattern */
};

#define SYN_STATE_P(ssp)    ((struct buf_state *)((ssp)->ga_data))

/*
 * Settings for keyword hash table.  It uses a simplistic hash function: add
 * all characters together, modulo KHASH_SIZE.
 */
#define KHASH_SIZE	512
#define KHASH_MASK	(KHASH_SIZE - 1)
#define MAXKEYWLEN	80	    /* maximum length of a keyword */

/*
 * The attributes of the syntax item that has been recognized.
 */
static int current_attr = 0;	    /* attr of current syntax word */
static int current_id = 0;	    /* ID of current char for syn_get_id() */
static int current_trans_id = 0;    /* idem, transparancy removed */

struct syn_cluster
{
    char_u	    *scl_name;	    /* syntax cluster name */
    char_u	    *scl_name_u;    /* uppercase of scl_name */
    short	    *scl_list;	    /* IDs in this syntax cluster */
};

/*
 * Syntax group IDs greater than or equal to this are actually cluster IDs.  I
 * was gonna use SHRT_MAX/2, but apparently not everyone has <limits.h>...
 */
#define CLUSTER_ID_MIN	15000

/*
 * Methods of combining two clusters
 */
#define CLUSTER_REPLACE	    1	/* replace first list with second */
#define CLUSTER_ADD	    2	/* add second list to first */
#define CLUSTER_SUBTRACT    3	/* subtract second list from first */

#define SYN_CLSTR(buf)	((struct syn_cluster *)((buf)->b_syn_clusters.ga_data))

/*
 * Annoying Hack(TM):  ":syn include" needs this pointer to pass to
 * expand_filename().  Most of the other syntax commands don't need it, so
 * instead of passing it to them, we stow it here.
 */
static char_u **syn_cmdlinep;

/*
 * Another Annoying Hack(TM):  To prevent rules from higher or lower in the
 * ":syn include" stack from from leaking into ALLBUT lists, we track the
 * current stack "level".
 */
static int current_syn_inc_lvl = 0;

/*
 * To reduce the time spent in keepend(), remember at which level in the state
 * stack the first item with "keepend" is present.  When "-1", there is no
 * "keepend" on the stack.
 */
static int keepend_level = -1;

/*
 * For the current state we need to remember more than just the idx.
 * When si_m_endcol is 0, the items other than si_idx are unknown.
 */
struct state_item
{
    int	    si_idx;		    /* index of syntax pattern */
    int	    si_id;		    /* highlight group ID for keywords */
    int	    si_trans_id;	    /* idem, transparancy removed */
    int	    si_m_lnum;		    /* lnum of the match */
    int	    si_m_startcol;	    /* starting column of the match */
    int	    si_m_endcol;	    /* ending column of the match */
    int	    si_h_startcol;	    /* starting column of the highlighting */
    int	    si_h_endcol;	    /* ending column of the highlighting */
    int	    si_eoe_col;		    /* ending column of end pattern */
    int	    si_end_idx;		    /* group ID for end pattern or zero */
    int	    si_ends;		    /* if match ends after si_m_endcol */
    int	    si_attr;		    /* attributes in this state */
    int	    si_flags;		    /* HL_HAS_EOL flag in this state, and
				       HL_SKIP* for si_next_list */
    short   *si_cont_list;	    /* list of contained groups */
    short   *si_next_list;	    /* nextgroup IDs after this item ends */
};

#define KEYWORD_IDX	-1	    /* value of si_idx for keywords */
#define CONTAINS_ALLBUT	9999	    /* value of id for contains ALLBUT */
#define ID_LIST_ALL	(short *)-1 /* valid of si_cont_list for containing all
				       but contained groups */

/*
 * The next possible match for any pattern is remembered, to avoid having to
 * try for a match in each column.
 * If next_match_idx == -1, not tried (in this line) yet.
 * If next_match_col == MAXCOL, no match found in this line.
 */
static int next_match_col;	    /* column for start of next match */
static int next_match_m_endcol;	    /* column for end of next match */
static int next_match_h_startcol;   /* column for highl. start of next match */
static int next_match_h_endcol;	    /* column for highl. end of next match */
static int next_match_idx;	    /* index of matched item */
static int next_match_flags;	    /* flags for next match */
static int next_match_eos_col;	    /* column for end of start pattern */
static int next_match_eoe_col;	    /* column for end of end pattern */
static int next_match_end_idx;	    /* ID of group for end pattern or zero */

/*
 * A state stack is an array of integers or struct state_item, stored in a
 * struct growarray.  A state stack is invalid if it's itemsize entry is zero.
 */
#define INVALID_STATE(ssp)  ((ssp)->ga_itemsize == 0)
#define VALID_STATE(ssp)    ((ssp)->ga_itemsize != 0)

/*
 * The current state (within the line) of the recognition engine.
 */
static BUF	*syn_buf;		/* current buffer for highlighting */
static linenr_t current_lnum = 0;	/* lnum of current state */
static int	current_state_stored = 0; /* TRUE if stored current state
					   * after setting current_finished */
static colnr_t	current_col = 0;	/* column of current state */
static int	current_finished = 0;	/* current line has been finished */
static struct growarray current_state	/* current stack of state_items */
		= {0, 0, 0, 0, NULL};
static short	*current_next_list = NULL; /* when non-zero, nextgroup list */
static int	current_next_flags = 0; /* flags for current_next_list */
static int	current_line_id = 0;	/* unique number for current line */

#define CUR_STATE(idx)	((struct state_item *)(current_state.ga_data))[idx]

static void syn_sync __ARGS((WIN *wp, linenr_t lnum));
static int syn_match_linecont __ARGS((linenr_t lnum));
static void syn_start_line __ARGS((void));
static void syn_free_all_states __ARGS((BUF *buf));
static void syn_clear_states __ARGS((int start, int end));
static void store_current_state __ARGS((void));
static void invalidate_current_state __ARGS((void));
static void validate_current_state __ARGS((void));
static void copy_state_to_current __ARGS((struct syn_state *from));
static void move_state __ARGS((int from, int to));
static int syn_finish_line __ARGS((int syncing));
static int syn_current_attr __ARGS((int syncing, char_u *line));
static int did_match_already __ARGS((int idx));
static struct state_item *push_next_match __ARGS((struct state_item *cur_si, char_u *line));
static void check_state_ends __ARGS((char_u *line));
static void update_si_attr __ARGS((int idx));
static void check_keepend __ARGS((void));
static void update_si_end __ARGS((struct state_item *sip, char_u *line, int startcol));
static short *copy_id_list __ARGS((short *list));
static int in_id_list __ARGS((short *cont_list, int id, int inclvl, int contained));
static int syn_regexec __ARGS((vim_regexp *prog, char_u *string, int at_bol));
static int push_current __ARGS((int idx));
static void pop_current __ARGS((void));
static char_u *find_endp __ARGS((int idx, char_u *sstart, int at_bol, char_u **hl_endp, int *flagsp, char_u **end_endp, int *end_idx));
static char_u *syn_add_end_off __ARGS((struct syn_pattern *spp, int idx, int extra));
static char_u *syn_add_start_off __ARGS((struct syn_pattern *spp, int idx, int extra));
static int check_keyword_id __ARGS((char_u *line, int startcol, int *endcol, int *flags, short **next_list, struct state_item *cur_si));
static void syn_cmd_case __ARGS((EXARG *eap, int syncing));
static void syntax_sync_clear __ARGS((void));
static void syn_remove_pattern __ARGS((BUF *buf, int idx));
static void syn_clear_pattern __ARGS((BUF *buf, int i));
static void syn_clear_cluster __ARGS((BUF *buf, int i));
static void syn_cmd_clear __ARGS((EXARG *eap, int syncing));
static void syn_clear_one __ARGS((int id, int syncing));
static void syn_cmd_on __ARGS((EXARG *eap, int syncing));
static void syn_cmd_off __ARGS((EXARG *eap, int syncing));
static void syn_cmd_list __ARGS((EXARG *eap, int syncing));
static void syn_lines_msg __ARGS((void));
static void syn_list_one __ARGS((int id, int syncing, int link_only));
static void syn_list_cluster __ARGS((int id));
static void put_id_list __ARGS((char_u *name, short *list, int attr));
static void put_pattern __ARGS((char *s, int c, struct syn_pattern *spp, int attr));
static int syn_list_keywords __ARGS((int id, struct keyentry **ktabp, int did_header, int attr));
static void syn_clear_keyword __ARGS((int id, struct keyentry **ktabp));
static void free_keywtab __ARGS((struct keyentry **ktabp));
static void add_keyword __ARGS((char_u *name, int id, int flags, short *next_list));
static char_u *get_group_name __ARGS((char_u *arg, char_u **name_end));
static char_u *get_syn_options __ARGS((char_u *arg, int *flagsp, int *sync_idx,
				    short **cont_list, short **next_list));
static void syn_cmd_include __ARGS((EXARG *eap, int syncing));
static void syn_cmd_keyword __ARGS((EXARG *eap, int syncing));
static void syn_cmd_match __ARGS((EXARG *eap, int syncing));
static void syn_cmd_region __ARGS((EXARG *eap, int syncing));
#ifdef __BORLANDC__
static int _RTLENTRYF syn_compare_stub __ARGS((const void *v1, const void *v2));
#else
static int syn_compare_stub __ARGS((const void *v1, const void *v2));
#endif
static void syn_cmd_cluster __ARGS((EXARG *eap, int syncing));
static int syn_scl_name2id __ARGS((char_u *name));
static int syn_scl_namen2id __ARGS((char_u *linep, int len));
static int syn_check_cluster __ARGS((char_u *pp, int len));
static int syn_add_cluster __ARGS((char_u *name));
static void init_syn_patterns __ARGS((void));
static char_u *get_syn_pattern __ARGS((char_u *arg, struct syn_pattern	*ci));
static void syn_cmd_sync __ARGS((EXARG *eap, int syncing));
static int get_id_list __ARGS((char_u **arg, int keylen, short **list));
static void syn_combine_list __ARGS((short **clstr1, short **clstr2, int list_op));
static void syn_incl_toplevel __ARGS((int id, int *flagsp));

/*
 * Start the syntax recognition for a line.  This function is normally called
 * from the screen updating, once for each consecutive line.
 * The buffer is remembered in syn_buf, because get_syntax_attr() doesn't get
 * it.	Careful: curbuf and curwin are likely to point to another buffer and
 * window.
 */
    void
syntax_start(wp, lnum)
    WIN		*wp;
    linenr_t	lnum;
{
    long	to, from, first;
    long	diff;
    int		idx;

    reg_syn = TRUE;	/* let vim_regexec() know we're using syntax */

    /*
     * After switching buffers, invalidate current_state.
     */
    if (syn_buf != wp->w_buffer)
    {
	invalidate_current_state();
	syn_buf = wp->w_buffer;
    }

    /*
     * Keep syncing info for ten lines above the window.  This is a compromise
     * between computing extra lines (jumping around) and reducing computions
     * (mostly when scrolling up).
     */
#define SYNC_LINES 10

    /*
     * If the screen height has changed, re-allocate b_syn_states[].
     * Use the screen height plus SYNC_LINES, so some lines above and one
     * line below the window can always be stored too.
     */
    if (syn_buf->b_syn_states_len != Rows + SYNC_LINES)
    {
	syn_free_all_states(syn_buf);
	syn_buf->b_syn_states = (struct syn_state *)alloc_clear(
		  (int)((Rows + SYNC_LINES) * sizeof(struct syn_state)));
	if (syn_buf->b_syn_states == NULL)	    /* out of memory */
	{
	    syn_buf->b_syn_states_len = 0;
	    goto theend;
	}
	syn_buf->b_syn_states_len = Rows + SYNC_LINES;
	syn_buf->b_syn_states_lnum = 0;
	syn_buf->b_syn_change_lnum = MAXLNUM;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -