📄 syntax.c
字号:
}
next_match_idx = -1;
++current_line_id;
}
/*
* Free b_syn_states[] for buffer "buf".
*/
static void
syn_free_all_states(buf)
BUF *buf;
{
int idx;
if (buf->b_syn_states != NULL)
{
for (idx = 0; idx < buf->b_syn_states_len; ++idx)
ga_clear(&(buf->b_syn_states[idx].sst_ga));
vim_free(buf->b_syn_states);
buf->b_syn_states = NULL;
buf->b_syn_states_len = 0;
}
}
/*
* clear the entries in b_syn_states[] from "start" to (not including) "end"
*/
static void
syn_clear_states(start, end)
int start, end;
{
int idx;
struct growarray *sp;
for (idx = start; idx < end; ++idx)
{
sp = &(syn_buf->b_syn_states[idx].sst_ga);
ga_clear(sp);
sp->ga_itemsize = 0;
}
}
/*
* Try saving the current state in b_syn_states[].
* The current state must be at the start of the current_lnum line!
*/
static void
store_current_state()
{
long idx;
int i;
struct growarray *to;
idx = current_lnum - syn_buf->b_syn_states_lnum;
if (idx >= 0 && idx < syn_buf->b_syn_states_len)
{
to = &(syn_buf->b_syn_states[idx].sst_ga);
if (to->ga_data != NULL)
ga_clear(to);
else if (to->ga_itemsize == 0)
{
to->ga_itemsize = sizeof(struct buf_state);
to->ga_growsize = 3;
}
if (current_state.ga_len && ga_grow(to, current_state.ga_len) != FAIL)
{
for (i = 0; i < current_state.ga_len; ++i)
{
SYN_STATE_P(to)[i].bs_idx = CUR_STATE(i).si_idx;
SYN_STATE_P(to)[i].bs_flags = CUR_STATE(i).si_flags;
}
to->ga_len = current_state.ga_len;
to->ga_room -= to->ga_len;
}
syn_buf->b_syn_states[idx].sst_next_list = current_next_list;
syn_buf->b_syn_states[idx].sst_next_flags = current_next_flags;
}
current_state_stored = TRUE;
}
/*
* Copy a state stack from "from" in b_syn_states[] to current_state;
*/
static void
copy_state_to_current(from)
struct syn_state *from;
{
int i;
struct growarray *ga = &(from->sst_ga);
ga_clear(¤t_state);
validate_current_state();
keepend_level = -1;
if (ga->ga_len && ga_grow(¤t_state, ga->ga_len) != FAIL)
{
for (i = 0; i < ga->ga_len; ++i)
{
CUR_STATE(i).si_idx = SYN_STATE_P(ga)[i].bs_idx;
CUR_STATE(i).si_flags = SYN_STATE_P(ga)[i].bs_flags;
if (keepend_level < 0 && (CUR_STATE(i).si_flags & HL_KEEPEND))
keepend_level = i;
CUR_STATE(i).si_m_endcol = 0;
CUR_STATE(i).si_m_startcol = 0;
CUR_STATE(i).si_m_lnum = 0;
update_si_attr(i);
}
current_state.ga_len = ga->ga_len;
current_state.ga_room -= current_state.ga_len;
}
current_next_list = from->sst_next_list;
current_next_flags = from->sst_next_flags;
}
static void
invalidate_current_state()
{
ga_clear(¤t_state);
current_state.ga_itemsize = 0;
current_next_list = NULL;
keepend_level = -1;
}
static void
validate_current_state()
{
current_state.ga_itemsize = sizeof(struct state_item);
current_state.ga_growsize = 3;
}
/*
* Move a state stack from b_syn_states[from] to b_syn_states[to].
*/
static void
move_state(from, to)
int from, to;
{
ga_clear(&(syn_buf->b_syn_states[to].sst_ga));
syn_buf->b_syn_states[to] = syn_buf->b_syn_states[from];
ga_init(&(syn_buf->b_syn_states[from].sst_ga));
syn_buf->b_syn_states[from].sst_ga.ga_itemsize = 0; /* invalid entry */
}
/*
* Mark like "lnum" and following ones as changed: Need to recompute its
* highlighting.
* This must be called whenever something is changed. ml_delete() and
* ml_append() take care of this when deleting/appending lines.
* When changing a single line, and calling update_screenline(), for it, no
* need to call this (syntax_check_changed() will be used then).
*/
void
syn_changed(lnum)
linenr_t lnum;
{
if (curbuf->b_syn_change_lnum > lnum)
curbuf->b_syn_change_lnum = lnum;
}
/*
* Return TRUE if the syntax at start of lnum changed since last time.
* This will only be called just after get_syntax_attr for the previous line,
* to check if the next line needs to be redrawn too.
*/
int
syntax_check_changed(lnum)
linenr_t lnum;
{
struct growarray *ssp;
int i;
int retval = TRUE;
long idx;
reg_syn = TRUE; /* let vim_regexec() know we're using syntax */
/*
* Check the state stack when:
* - lnum is just below the previously syntaxed line.
* - lnum is not before the lines with saved states.
* - lnum is not past the lines with saved states.
* - lnum is at or before the last changed line.
*/
idx = lnum - syn_buf->b_syn_states_lnum;
if (VALID_STATE(¤t_state) && lnum == current_lnum + 1 &&
idx >= 0 && idx < syn_buf->b_syn_states_len &&
lnum < syn_buf->b_syn_change_lnum)
{
/*
* finish the previous line (needed when not all of the line was drawn)
*/
(void)syn_finish_line(FALSE);
ssp = &(syn_buf->b_syn_states[idx].sst_ga);
if (VALID_STATE(ssp)) /* entry is valid */
{
/*
* Compare the current state with the previously saved state of
* the line.
*/
if (ssp->ga_len == current_state.ga_len
&& syn_buf->b_syn_states[idx].sst_next_list
== current_next_list)
{
for (i = current_state.ga_len; --i >= 0; )
if (SYN_STATE_P(ssp)[i].bs_idx != CUR_STATE(i).si_idx)
break;
/*
* If still the same state, return FALSE, syntax didn't change.
*/
if (i < 0)
retval = FALSE;
}
}
/*
* Store the current state in b_syn_states[] for later use.
*/
++current_lnum;
store_current_state();
}
reg_syn = FALSE;
/* If state has changed, the saved states are invalid now */
if (retval)
syn_changed(lnum);
return retval;
}
/*
* Finish the current line.
* This doesn't return any attributes, it only gets the state at the end of
* the line. It can start anywhere in the line, as long as the current state
* is valid.
*/
static int
syn_finish_line(syncing)
int syncing; /* called for syncing */
{
char_u *line;
struct state_item *cur_si;
if (!current_finished)
{
line = ml_get_buf(syn_buf, current_lnum, FALSE);
while (!current_finished)
{
(void)syn_current_attr(syncing, line);
/*
* When syncing, and found some item, need to check the item.
*/
if (syncing && current_state.ga_len)
{
/*
* Check for match with sync item.
*/
cur_si = &CUR_STATE(current_state.ga_len - 1);
if (SYN_ITEMS(syn_buf)[cur_si->si_idx].sp_flags
& (HL_SYNC_HERE|HL_SYNC_THERE))
return TRUE;
/* syn_current_attr() will have skipped the check for an item
* that ends here, need to do that now. */
++current_col;
check_state_ends(line);
--current_col;
}
++current_col;
}
}
return FALSE;
}
/*
* Return highlight attributes for next character.
* This function is alwyas called from the screen updating, for each
* consecutive character. And only after syntax_start() has been called for
* the current line.
* Note that "col" doesn't start at 0, when win->w_leftcol is non-zero, and
* doesn't continue until the last col when 'nowrap' is set.
*/
int
get_syntax_attr(col, line)
colnr_t col;
char_u *line;
{
int attr = 0;
reg_syn = TRUE; /* let vim_regexec() know we're using syntax */
/* check for out of memory situation */
if (syn_buf->b_syn_states_len == 0)
return 0;
/* Make sure current_state is valid */
if (INVALID_STATE(¤t_state))
validate_current_state();
/*
* Skip from the current column to "col", get the attributes for "col".
*/
while (current_col <= col)
{
attr = syn_current_attr(FALSE, line);
++current_col;
}
reg_syn = FALSE;
return attr;
}
/*
* Get syntax attributes for current_lnum, current_col.
*/
static int
syn_current_attr(syncing, line)
int syncing; /* When 1: called for syncing */
char_u *line;
{
int syn_id;
char_u *endp;
char_u *hl_endp = NULL;
char_u *eoep; /* end-of-end pattern */
int end_idx; /* group ID for end pattern */
int idx;
struct syn_pattern *spp;
struct state_item *cur_si, *sip;
int startcol;
int hl_startcol;
int eos_col; /* end-of-start column */
int endcol;
int flags;
short *next_list;
int found_match; /* found usable match */
static int try_next_column = FALSE; /* must try in next col */
/*
* No character, no attributes! Past end of line?
* Do try matching with an empty line (could be the start of a region).
*/
if (*(line + current_col) == NUL && current_col != 0)
{
/*
* If we found a match after the last column, use it.
*/
if (next_match_idx >= 0 && next_match_col >= (int)current_col
&& next_match_col != MAXCOL)
(void)push_next_match(NULL, line);
current_finished = TRUE;
current_state_stored = FALSE;
return 0;
}
/* if the next character is NUL, we will finish the line now */
if (*(line + current_col) == NUL || *(line + current_col + 1) == NUL)
{
current_finished = TRUE;
current_state_stored = FALSE;
}
/*
* When in the previous column there was a match but it could not be used
* (empty match or already matched in this column) need to try again in
* the next column.
*/
if (try_next_column)
{
next_match_idx = -1;
try_next_column = FALSE;
}
/*
* Repeat matching keywords and patterns, to find contained items at the
* same column. This stops when there are no extra matches at the current
* column.
*/
do
{
found_match = FALSE;
syn_id = 0;
/*
* 1. Check for a current state.
* Only when there is no current state, or if the current state may
* contain other things, we need to check for keywords and patterns.
*/
if (current_state.ga_len)
cur_si = &CUR_STATE(current_state.ga_len - 1);
else
cur_si = NULL;
if (cur_si == NULL || cur_si->si_cont_list != NULL)
{
/*
* 2. Check for keywords, if on a keyword char after a non-keyword
* char. Don't do this when syncing.
*/
if ( !syncing
&& (syn_buf->b_keywtab != NULL
|| syn_buf->b_keywtab_ic != NULL)
&& vim_iswordc_buf(line[current_col], syn_buf)
&& (current_col == 0
|| !vim_iswordc_buf(line[current_col - 1], syn_buf)))
{
syn_id = check_keyword_id(line, (int)current_col,
&endcol, &flags, &next_list, cur_si);
if (syn_id)
{
if (push_current(KEYWORD_IDX) == OK)
{
cur_si = &CUR_STATE(current_state.ga_len - 1);
cur_si->si_m_startcol = current_col;
cur_si->si_h_startcol = 0; /* starts right away */
cur_si->si_m_endcol = endcol;
cur_si->si_h_endcol = endcol;
cur_si->si_ends = TRUE;
cur_si->si_end_idx = 0;
cur_si->si_flags = flags;
cur_si->si_id = syn_id;
cur_si->si_trans_id = syn_id;
if (flags & HL_TRANSP)
{
if (current_state.ga_len < 2)
{
cur_si->si_attr = 0;
cur_si->si_trans_id = 0;
}
else
{
cur_si->si_attr = CUR_STATE(
current_state.ga_len - 2).si_attr;
cur_si->si_trans_id = CUR_STATE(
current_state.ga_len - 2).si_trans_id;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -