📄 syntax.c
字号:
/* 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 + -