📄 syntax.c
字号:
* Also take cont_list, if there is none.
* Don't do this for the matchgroup of a start or end pattern.
*/
if ((spp->sp_flags & HL_TRANSP) && !(sip->si_flags & HL_MATCH))
{
if (idx == 0)
{
sip->si_attr = 0;
sip->si_trans_id = 0;
if (sip->si_cont_list == NULL)
sip->si_cont_list = ID_LIST_ALL;
}
else
{
sip->si_attr = CUR_STATE(idx - 1).si_attr;
sip->si_trans_id = CUR_STATE(idx - 1).si_trans_id;
if (sip->si_cont_list == NULL)
sip->si_cont_list = CUR_STATE(idx - 1).si_cont_list;
}
}
}
/*
* Check the current stack for patterns with "keepend" flag. Propagate the
* match-end to contained items.
*/
static void
check_keepend()
{
int i;
int maxend = MAXCOL;
struct state_item *sip;
/*
* This check can consume a lot of time; only do it from the level where
* there really is a keepend.
*/
if (keepend_level < 0)
return;
for (i = keepend_level; i < current_state.ga_len; ++i)
{
sip = &CUR_STATE(i);
if (maxend < MAXCOL)
{
if (sip->si_m_endcol > maxend)
sip->si_m_endcol = maxend;
if (sip->si_h_endcol > maxend)
sip->si_h_endcol = maxend;
if (sip->si_eoe_col > maxend)
sip->si_eoe_col = maxend;
sip->si_ends = TRUE;
}
if (sip->si_ends && (sip->si_flags & HL_KEEPEND)
&& maxend > sip->si_m_endcol)
maxend = sip->si_m_endcol;
}
}
/*
* Update an entry in the current_state stack for a start-skip-end pattern.
* This finds the end of the current item, if it's in the current line.
*
* Return the flags for the matched END.
*/
static void
update_si_end(sip, line, startcol)
struct state_item *sip;
char_u *line;
int startcol; /* where to start searching for the end */
{
char_u *endp;
char_u *hl_endp;
char_u *end_endp;
int end_idx;
/*
* We need to find the end of the match. It may continue in the next
* line.
*/
end_idx = 0;
endp = find_endp(sip->si_idx, line + startcol,
startcol == 0, &hl_endp, &(sip->si_flags), &end_endp, &end_idx);
if (endp == NULL)
{
/* continues on next line */
if (SYN_ITEMS(syn_buf)[sip->si_idx].sp_flags & HL_ONELINE)
{
sip->si_ends = TRUE;
sip->si_m_endcol = STRLEN(line) - 1;
}
else
{
sip->si_ends = FALSE;
sip->si_m_endcol = MAXCOL;
}
sip->si_h_endcol = sip->si_m_endcol;
}
else
{
/* match within this line */
sip->si_m_endcol = endp - line - 1;
sip->si_h_endcol = hl_endp - line - 1;
sip->si_ends = TRUE;
sip->si_eoe_col = end_endp - line - 1;
sip->si_end_idx = end_idx;
}
check_keepend();
}
/*
* Add a new state to the current state stack.
* Return FAIL if it's not possible (out of memory).
*/
static int
push_current(idx)
int idx;
{
if (ga_grow(¤t_state, 1) == FAIL)
return FAIL;
vim_memset(&CUR_STATE(current_state.ga_len), 0, sizeof(struct state_item));
CUR_STATE(current_state.ga_len).si_idx = idx;
++current_state.ga_len;
--current_state.ga_room;
return OK;
}
/*
* Remove a state from the current_state stack.
*/
static void
pop_current()
{
if (current_state.ga_len)
{
--current_state.ga_len;
++current_state.ga_room;
}
/* after the end of a pattern, try matching a keyword or pattern */
next_match_idx = -1;
/* if first state with "keepend" is popped, reset keepend_level */
if (keepend_level >= current_state.ga_len)
keepend_level = -1;
}
/*
* Find the end of a start/skip/end pattern match.
*/
static char_u *
find_endp(idx, sstart, at_bol, hl_endp, flagsp, end_endp, end_idx)
int idx; /* index of the pattern */
char_u *sstart; /* where to start looking for an END match */
int at_bol; /* if sstart is at begin-of-line */
char_u **hl_endp; /* end column for highlighting */
int *flagsp; /* flags of matching END */
char_u **end_endp; /* end of end pattern match */
int *end_idx; /* group ID for end pattern match, or 0 */
{
char_u *endp; /* end of highlighting */
struct syn_pattern *spp, *spp_skip;
char_u *p; /* end of match */
int start_idx;
int best_idx;
char_u *best_ptr;
/*
* Check for being called with a START pattern.
* Can happen with a match that continues to the next line, because it
* contained a region.
*/
spp = &(SYN_ITEMS(syn_buf)[idx]);
if (spp->sp_type != SPTYPE_START)
{
*hl_endp = sstart;
return sstart;
}
/*
* Find the SKIP or first END pattern after the last START pattern.
*/
for (;;)
{
spp = &(SYN_ITEMS(syn_buf)[idx]);
if (spp->sp_type != SPTYPE_START)
break;
++idx;
}
/*
* Lookup the SKIP pattern (if present)
*/
if (spp->sp_type == SPTYPE_SKIP)
{
spp_skip = spp;
++idx;
}
else
spp_skip = NULL;
endp = sstart; /* start looking for a match at sstart */
start_idx = idx; /* remember the first END pattern. */
for (;;)
{
best_idx = -1;
best_ptr = NULL;
for (idx = start_idx; idx < syn_buf->b_syn_patterns.ga_len; ++idx)
{
spp = &(SYN_ITEMS(syn_buf)[idx]);
if (spp->sp_type != SPTYPE_END) /* past last END pattern */
break;
reg_ic = spp->sp_ic;
if (syn_regexec(spp->sp_prog, endp, (at_bol && endp == sstart)))
{
if (best_idx == -1 || spp->sp_prog->startp[0] < best_ptr)
{
best_idx = idx;
best_ptr = spp->sp_prog->startp[0];
}
}
}
/*
* If all end patterns have been tried, and there is no match, the
* item continues until end-of-line.
*/
if (best_idx == -1)
break;
/*
* If the skip pattern matches before the end pattern,
* continue searching after the skip pattern.
*/
if ( spp_skip != NULL
&& (reg_ic = spp_skip->sp_ic,
syn_regexec(spp_skip->sp_prog, endp,
(at_bol && endp == sstart)))
&& spp_skip->sp_prog->startp[0] <= best_ptr)
{
/* Add offset to skip pattern match */
p = syn_add_end_off(spp_skip, SPO_ME_OFF, 1);
/* take care of an empty match or negative offset */
if (p <= endp)
++endp;
else if (p <= spp_skip->sp_prog->endp[0])
endp = p;
else
/* Be careful not to jump over the NUL at the end-of-line */
for (endp = spp_skip->sp_prog->endp[0];
*endp != NUL && endp < p; ++endp)
;
/* if skip pattern includes end-of-line, return here */
if (*endp == NUL)
break;
continue; /* start with first end pattern again */
}
/*
* Match from start pattern to end pattern.
* Correct for match and highlight offset of end pattern.
*/
spp = &(SYN_ITEMS(syn_buf)[best_idx]);
p = syn_add_end_off(spp, SPO_ME_OFF, 1);
if (p < sstart)
p = sstart;
endp = syn_add_end_off(spp, SPO_HE_OFF, 1);
if (endp < sstart)
endp = sstart;
if (endp > p)
endp = p;
*end_endp = endp;
/*
* If the end group is highlighted differently, adjust the pointers.
*/
if (spp->sp_syn_match_id != spp->sp_syn_id && spp->sp_syn_match_id != 0)
{
*end_idx = best_idx;
if (spp->sp_off_flags & (1 << (SPO_RE_OFF + SPO_COUNT)))
endp = spp->sp_prog->endp[0] + spp->sp_offsets[SPO_RE_OFF];
else
endp = spp->sp_prog->startp[0] + spp->sp_offsets[SPO_RE_OFF];
if (endp < sstart)
endp = sstart;
if (endp > p)
endp = p;
p = endp;
}
else
*end_idx = 0;
*hl_endp = endp;
*flagsp = spp->sp_flags;
return p;
}
return NULL; /* no match for an END pattern in this line */
}
/*
* Add offset to matched text for end of match or highlight.
*/
static char_u *
syn_add_end_off(spp, idx, extra)
struct syn_pattern *spp;
int idx;
int extra; /* extra chars for offset to start */
{
if (spp->sp_off_flags & (1 << idx))
return spp->sp_prog->startp[0] + spp->sp_offsets[idx] + extra;
return spp->sp_prog->endp[0] + spp->sp_offsets[idx];
}
/*
* Add offset to matched text for start of match or highlight.
*/
static char_u *
syn_add_start_off(spp, idx, extra)
struct syn_pattern *spp;
int idx;
int extra; /* extra chars for offset to end */
{
if (spp->sp_off_flags & (1 << (idx + SPO_COUNT)))
return spp->sp_prog->endp[0] + spp->sp_offsets[idx] + extra;
return spp->sp_prog->startp[0] + spp->sp_offsets[idx];
}
/*
* Check one position in a line for a matching keyword.
* The caller must check if a keyword can start at startcol.
* Return it's ID if found, 0 otherwise.
*/
static int
check_keyword_id(line, startcol, endcol, flags, next_list, cur_si)
char_u *line;
int startcol; /* position in line to check for keyword */
int *endcol; /* last character of found keyword */
int *flags; /* flags of matching keyword */
short **next_list; /* next_list of matching keyword */
struct state_item *cur_si; /* item at the top of the stack */
{
struct keyentry *ktab;
char_u *p;
int round;
int hash;
int len;
char_u keyword[MAXKEYWLEN + 1]; /* assume max. keyword len is 80 */
/* Find first character after the keyword */
p = line + startcol;
for (len = 1; vim_iswordc_buf(p[len], syn_buf); ++len)
;
if (len > MAXKEYWLEN)
return 0;
/*
* Must make a copy of the keyword, so we can add a NUL and make it
* uppercase.
*/
STRNCPY(keyword, p, len);
keyword[len] = NUL;
/*
* Try twice:
* 1. matching case
* 2. ignoring case
*/
for (round = 1; round <= 2; ++round)
{
if ((round == 1 ? syn_buf->b_keywtab : syn_buf->b_keywtab_ic) == NULL)
continue;
p = keyword;
hash = 0;
if (round == 1) /* match case */
{
while (*p)
hash += *p++;
ktab = syn_buf->b_keywtab[hash & KHASH_MASK];
}
else /* round == 2, ignore case */
{
while (*p)
{
hash += (*p = TO_LOWER(*p));
++p;
}
ktab = syn_buf->b_keywtab_ic[hash & KHASH_MASK];
}
/*
* Find keywords that match.
* When current_next_list is non-zero accept only that group, otherwise:
* Accept a not-contained keyword at toplevel.
* Accept a keyword at other levels only if it is in the contains list.
*/
for ( ; ktab != NULL; ktab = ktab->next)
if ( STRCMP(keyword, ktab->keyword) == 0
&& ( (current_next_list != 0
&& in_id_list(current_next_list, ktab->syn_id,
ktab->syn_inc_lvl, 0))
|| (current_next_list == 0
&& ((cur_si == NULL && !(ktab->flags & HL_CONTAINED))
|| (cur_si != NULL
&& in_id_list(cur_si->si_cont_list,
ktab->syn_id,
ktab->syn_inc_lvl,
ktab->flags & HL_CONTAINED))))))
{
*endcol = startcol + len - 1;
*flags = ktab->flags;
*next_list = ktab->next_list;
return ktab->syn_id;
}
}
return 0;
}
/*
* Handle ":syntax case" command.
*/
/* ARGSUSED */
static void
syn_cmd_case(eap, syncing)
EXARG *eap;
int syncing; /* not used */
{
char_u *arg = eap->arg;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -