synID now returns a list.
synIDattr now takes a list.
They both still handle the single-id case, but the code's a bit messy and the
name is stale. I think we should add synActive and synActiveAttr, which
take/return lists instead of single numbers, and then deprecate synID*.
--
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt
index 2bcd21e..a8e481b 100644
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -5721,21 +5721,22 @@ substitute({expr}, {pat}, {sub}, {flags}) *substitute()*
\ '\=nr2char("0x" . submatch(1))', 'g')
synID({lnum}, {col}, {trans}) *synID()*
- The result is a Number, which is the syntax ID at the position
- {lnum} and {col} in the current window.
- The syntax ID can be used with |synIDattr()| and
+ The result is a Number or a list, which is/are the syntax
+ ID(s) at the position {lnum} and {col} in the current window.
+ The syntax ID(s) can be used with |synIDattr()| and
|synIDtrans()| to obtain syntax information about text.
{col} is 1 for the leftmost column, {lnum} is 1 for the first
line. 'synmaxcol' applies, in a longer line zero is returned.
When {trans} is non-zero, transparent items are reduced to the
- item that they reveal. This is useful when wanting to know
- the effective color. When {trans} is zero, the transparent
- item is returned. This is useful when wanting to know which
- syntax item is effective (e.g. inside parens).
- Warning: This function can be very slow. Best speed is
- obtained by going through the file in forward direction.
+ item that they reveal. Non-transparent groups may use
+ |syn-combine|, you'll need to use |synIDattr| in combination
+ with |synstack| to find the effective color. When {trans} is
+ zero, the transparent item is returned. This is useful when
+ wanting to know which syntax item is effective (e.g. inside
+ parens). Warning: This function can be very slow. Best speed
+ is obtained by going through the file in forward direction.
Example (echoes the name of the syntax item under the cursor): >
:echo synIDattr(synID(line("."), col("."), 1), "name")
@@ -5752,6 +5753,8 @@ synIDattr({synID}, {what} [, {mode}]) *synIDattr()*
Use synIDtrans() to follow linked highlight groups.
{what} result
"name" the name of the syntax item
+ "combine" "1" if the syntax group combines with others
+ in the stack. See |syn-combine|
"fg" foreground color (GUI: color name used to set
the color, cterm: color number as a string,
term: empty string)
diff --git a/runtime/doc/syntax.txt b/runtime/doc/syntax.txt
index 5b56585..a8a66a4 100644
--- a/runtime/doc/syntax.txt
+++ b/runtime/doc/syntax.txt
@@ -3459,6 +3459,7 @@ can not be used for all commands:
:syntax region yes yes yes yes yes yes
These arguments can be used for all three commands:
+ combine
conceal
cchar
contained
@@ -3469,6 +3470,24 @@ These arguments can be used for all three commands:
skipnl
skipempty
+
+combine *:syn-combine*
+
+When the "combine" argument is given, the highlight attributes of the group
+will be combined with the attributes of other matching syntax groups. The
+"combine" group takes precedence.
+
+Multiple groups may be combined, with the innermost groups taking precedence.
+Example: >
+
+ syntax region Italic start="_" end="_" combine contains=Bold
+ syntax region Bold start="\*" end="\*" combine contains=Italic
+ highlight Italic term=italic
+ highlight Bold term=bold
+
+ _The inner text is italic *and bold*_.
+
+
conceal *conceal* *:syn-conceal*
When the "conceal" argument is given, the item is marked as concealable.
@@ -3750,6 +3769,7 @@ Note that this example doesn't work for nested "if"s. You need to add
"contains" arguments to make that work (omitted for simplicity of the
example).
+
IMPLICIT CONCEAL *:syn-conceal-implicit*
:sy[ntax] conceal [on|off]
diff --git a/runtime/syntax/vim.vim b/runtime/syntax/vim.vim
index 391e650..aafb1ea 100644
--- a/runtime/syntax/vim.vim
+++ b/runtime/syntax/vim.vim
@@ -421,14 +421,14 @@ syn cluster vimFuncBodyList add=vimSynType
syn cluster vimSynKeyGroup contains=vimSynNextgroup,vimSynKeyOpt,vimSynKeyContainedin
syn keyword vimSynType contained keyword skipwhite nextgroup=vimSynKeyRegion
syn region vimSynKeyRegion contained oneline keepend matchgroup=vimGroupName start="\k\+" skip="\\\\\|\\|" matchgroup=vimSep end="|\|$" contains=@vimSynKeyGroup
-syn match vimSynKeyOpt contained "\<\(conceal\|contained\|transparent\|skipempty\|skipwhite\|skipnl\)\>"
+syn match vimSynKeyOpt contained "\<\(conceal\|contained\|transparent\|skipempty\|skipwhite\|skipnl\|combine\)\>"
syn cluster vimFuncBodyList add=vimSynType
" Syntax: match {{{2
syn cluster vimSynMtchGroup contains=vimMtchComment,vimSynContains,vimSynError,vimSynMtchOpt,vimSynNextgroup,vimSynRegPat,vimNotation
syn keyword vimSynType contained match skipwhite nextgroup=vimSynMatchRegion
syn region vimSynMatchRegion contained keepend matchgroup=vimGroupName start="\k\+" matchgroup=vimSep end="|\|$" contains=@vimSynMtchGroup
-syn match vimSynMtchOpt contained "\<\(conceal\|transparent\|contained\|excludenl\|skipempty\|skipwhite\|display\|extend\|skipnl\|fold\)\>"
+syn match vimSynMtchOpt contained "\<\(conceal\|transparent\|contained\|excludenl\|skipempty\|skipwhite\|display\|extend\|skipnl\|fold\|combine\)\>"
if has("conceal")
syn match vimSynMtchOpt contained "\<cchar=" nextgroup=vimSynMtchCchar
syn match vimSynMtchCchar contained "\S"
@@ -443,7 +443,7 @@ syn cluster vimSynRegPatGroup contains=vimPatSep,vimNotPatSep,vimSynPatRange,vim
syn cluster vimSynRegGroup contains=vimSynContains,vimSynNextgroup,vimSynRegOpt,vimSynReg,vimSynMtchGrp
syn keyword vimSynType contained region skipwhite nextgroup=vimSynRegion
syn region vimSynRegion contained keepend matchgroup=vimGroupName start="\k\+" skip="\\\\\|\\|" end="|\|$" contains=@vimSynRegGroup
-syn match vimSynRegOpt contained "\<\(conceal\(ends\)\=\|transparent\|contained\|excludenl\|skipempty\|skipwhite\|display\|keepend\|oneline\|extend\|skipnl\|fold\)\>"
+syn match vimSynRegOpt contained "\<\(conceal\(ends\)\=\|transparent\|contained\|excludenl\|skipempty\|skipwhite\|display\|keepend\|oneline\|extend\|skipnl\|fold\|combine\)\>"
syn match vimSynReg contained "\(start\|skip\|end\)="he=e-1 nextgroup=vimSynRegPat
syn match vimSynMtchGrp contained "matchgroup=" nextgroup=vimGroup,vimHLGroup
syn region vimSynRegPat contained extend start="\z([-`~!@#$%^&*_=+;:'",./?]\)" skip="\\\\\|\\\z1" end="\z1" contains=@vimSynRegPatGroup skipwhite nextgroup=vimSynPatMod,vimSynReg
diff --git a/src/eval.c b/src/eval.c
index 7c57557..1124184 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -17565,6 +17565,7 @@ f_synID(argvars, rettv)
#ifdef FEAT_SYN_HL
long lnum;
long col;
+ int i;
int trans;
int transerr = FALSE;
@@ -17574,9 +17575,30 @@ f_synID(argvars, rettv)
if (!transerr && lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count
&& col >= 0 && col < (long)STRLEN(ml_get(lnum)))
+ {
id = syn_get_id(curwin, lnum, (colnr_T)col, trans, NULL, FALSE);
+ if (highlight_combines(id))
+ {
+ rettv->v_type = VAR_LIST;
+ rettv->vval.v_list = NULL;
+ if (rettv_list_alloc(rettv) == FAIL)
+ return;
+ (void)syn_get_id(curwin, lnum, (colnr_T)col, FALSE, NULL, TRUE);
+ for (i = syn_get_stack_length() - 1; i >= 0; --i)
+ {
+ id = syn_get_stack_item(i);
+ if (trans)
+ id = syn_get_final_id(id);
+ if (list_append_number(rettv->vval.v_list, id) == FAIL)
+ break;
+ if (!highlight_combines(id))
+ break;
+ }
+ return;
+ }
+ }
#endif
-
+ rettv->v_type = VAR_NUMBER;
rettv->vval.v_number = id;
}
@@ -17590,13 +17612,26 @@ f_synIDattr(argvars, rettv)
{
char_u *p = NULL;
#ifdef FEAT_SYN_HL
- int id;
+ char_u c; /* The first char of {what}. */
+ int id; /* The current highlight id */
+ int attr; /* The accumulated hl attrs */
+ int flag; /* The HL_* flag {what} requests */
+ list_T *l = NULL; /* The list of highlight ids */
+ listitem_T *item; /* The current list item */
char_u *what;
char_u *mode;
char_u modebuf[NUMBUFLEN];
int modec;
- id = get_tv_number(&argvars[0]);
+ if (argvars[0].v_type == VAR_NUMBER)
+ id = get_tv_number(&argvars[0]);
+ else if (argvars[0].v_type == VAR_LIST)
+ {
+ l = argvars[0].vval.v_list;
+ if (l == NULL || l->lv_len < 1)
+ return;
+ id = get_tv_number(&(l->lv_first->li_tv));
+ }
what = get_tv_string(&argvars[1]);
if (argvars[2].v_type != VAR_UNKNOWN)
{
@@ -17612,56 +17647,53 @@ f_synIDattr(argvars, rettv)
modec = 'g';
else
#endif
- if (t_colors > 1)
+ if (t_colors > 1)
modec = 'c';
else
modec = 't';
}
-
- switch (TOLOWER_ASC(what[0]))
+ c = TOLOWER_ASC(what[0]);
+ /* Cases that do not require looking up the stack. */
+ if (c == 'c')
+ p = highlight_combines(id) ? (char_u *)"1" : NULL;
+ else if (c == 'n')
+ p = get_highlight_name(NULL, id - 1);
+ else if ( (c == 'b' && TOLOWER_ASC(what[1]) == 'o' && (flag = HL_BOLD))
+ || (c == 'i' && TOLOWER_ASC(what[1]) == 't' && (flag = HL_ITALIC))
+ || (c == 'i' && (flag = HL_INVERSE))
+ || (c == 'r' && (flag = HL_INVERSE))
+ || (c == 's' && TOLOWER_ASC(what[1]) == 't' && (flag = HL_STANDOUT))
+ || (c == 'u' && STRLEN(what) > 5 && TOLOWER_ASC(what[5]) == 'c'
+ && (flag = HL_UNDERCURL))
+ || (c == 'u' && (flag = HL_UNDERLINE)))
{
- case 'b':
- if (TOLOWER_ASC(what[1]) == 'g') /* bg[#] */
- p = highlight_color(id, what, modec);
- else /* bold */
- p = highlight_has_attr(id, HL_BOLD, modec);
- break;
-
- case 'f': /* fg[#] or font */
+ if (l == NULL)
+ attr = highlight_get_attr(id);
+ else
+ {
+ attr = 0;
+ for (item = l->lv_first; item != NULL; item = item->li_next)
+ {
+ id = get_tv_number(&item->li_tv);
+ attr |= highlight_get_attr(id, modec);
+ }
+ }
+ p = attr & flag ? ((char_u *)"1") : NULL;
+ }
+ else if (c == 'b' || c == 'f' || c == 's')
+ {
+ if (l == NULL)
+ p = highlight_color(id, what, modec);
+ else
+ for (item = l->lv_first; item != NULL; item = item->li_next)
+ {
+ id = get_tv_number(&item->li_tv);
p = highlight_color(id, what, modec);
- break;
-
- case 'i':
- if (TOLOWER_ASC(what[1]) == 'n') /* inverse */
- p = highlight_has_attr(id, HL_INVERSE, modec);
- else /* italic */
- p = highlight_has_attr(id, HL_ITALIC, modec);
- break;
-
- case 'n': /* name */
- p = get_highlight_name(NULL, id - 1);
- break;
-
- case 'r': /* reverse */
- p = highlight_has_attr(id, HL_INVERSE, modec);
- break;
-
- case 's':
- if (TOLOWER_ASC(what[1]) == 'p') /* sp[#] */
- p = highlight_color(id, what, modec);
- else /* standout */
- p = highlight_has_attr(id, HL_STANDOUT, modec);
- break;
-
- case 'u':
- if (STRLEN(what) <= 5 || TOLOWER_ASC(what[5]) != 'c')
- /* underline */
- p = highlight_has_attr(id, HL_UNDERLINE, modec);
- else
- /* undercurl */
- p = highlight_has_attr(id, HL_UNDERCURL, modec);
- break;
+ /* -1 is an invalid color. */
+ if (p != NULL && p[0] != '-')
+ break;
+ }
}
if (p != NULL)
diff --git a/src/syntax.c b/src/syntax.c
index 4adbaf0..d8db30f 100644
--- a/src/syntax.c
+++ b/src/syntax.c
@@ -59,10 +59,11 @@ struct hl_group
#endif
};
-#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 */
+#define SG_TERM 0x01 /* term has been set */
+#define SG_CTERM 0x02 /* cterm has been set */
+#define SG_GUI 0x04 /* gui has been set */
+#define SG_LINK 0x08 /* link has been set */
+#define SG_COMBINE 0x10 /* group can combine with other groups. */
static garray_T highlight_ga; /* highlight groups for 'highlight' option */
@@ -289,8 +290,9 @@ typedef struct state_item
int si_end_idx; /* group ID for end pattern or zero */
int si_ends; /* if match ends before si_m_endpos */
int si_attr; /* attributes in this state */
- long si_flags; /* HL_HAS_EOL flag in this state, and
- * HL_SKIP* for si_next_list */
+ long si_flags; /* HL_HAS_EOL flag in this state,
+ * HL_SKIP* for si_next_list, and
+ * HL_COMBINE for combining. */
#ifdef FEAT_CONCEAL
int si_seqnr; /* sequence number */
int si_cchar; /* substitution character for conceal */
@@ -315,7 +317,8 @@ static int next_seqnr = 0; /* value to use for si_seqnr */
*/
typedef struct
{
- int flags; /* flags for contained and transparent */
+ int flags; /* flags for contained, transparent, and
+ combine. */
int keyword; /* TRUE for ":syn keyword" */
int *sync_idx; /* syntax item for "grouphere" argument, NULL
if not allowed */
@@ -1861,6 +1864,7 @@ syn_current_attr(syncing, displaying, can_spell, keep_state)
int cchar;
short *next_list;
int found_match; /* found usable match */
+ int applied_combiner; /* Applied a combining syntax */
static int try_next_column = FALSE; /* must try in next col */
int do_keywords;
regmmatch_T regmatch;
@@ -2284,6 +2288,8 @@ syn_current_attr(syncing, displaying, can_spell, keep_state)
* If not, use attributes from the current-but-one state, etc.
*/
current_attr = 0;
+ applied_combiner = FALSE;
+
#ifdef FEAT_EVAL
current_id = 0;
current_trans_id = 0;
@@ -2307,17 +2313,26 @@ syn_current_attr(syncing, displaying, can_spell, keep_state)
|| (current_lnum == sip->si_h_endpos.lnum
&& current_col < sip->si_h_endpos.col)))
{
- current_attr = sip->si_attr;
+ if (applied_combiner)
+ current_attr = hl_combine_attr(current_attr, sip->si_attr);
+ else
+ {
+ current_attr = sip->si_attr;
#ifdef FEAT_EVAL
- current_id = sip->si_id;
+ current_id = sip->si_id;
#endif
- current_trans_id = sip->si_trans_id;
+ current_trans_id = sip->si_trans_id;
#ifdef FEAT_CONCEAL
- current_flags = sip->si_flags;
- current_seqnr = sip->si_seqnr;
- current_sub_char = sip->si_cchar;
+ current_flags = sip->si_flags;
+ current_seqnr = sip->si_seqnr;
+ current_sub_char = sip->si_cchar;
#endif
- break;
+ }
+
+ if (sip->si_flags & HL_COMBINE)
+ applied_combiner = TRUE;
+ else
+ break;
}
}
@@ -3914,6 +3929,7 @@ syn_list_one(id, syncing, link_only)
{HL_EXCLUDENL, "excludenl"},
{HL_TRANSP, "transparent"},
{HL_FOLD, "fold"},
+ {HL_COMBINE, "combine"},
#ifdef FEAT_CONCEAL
{HL_CONCEAL, "conceal"},
{HL_CONCEALENDS, "concealends"},
@@ -4467,6 +4483,7 @@ get_syn_options(arg, opt, conceal_char)
{"fFoOlLdD", 0, HL_FOLD},
{"cCoOnNcCeEaAlL", 0, HL_CONCEAL},
{"cCoOnNcCeEaAlLeEnNdDsS", 0, HL_CONCEALENDS},
+ {"cCoOmMbBiInNeE", 0, HL_COMBINE},
{"cCcChHaArR", 11, 0},
{"cCoOnNtTaAiInNsS", 1, 0},
{"cCoOnNtTaAiInNeEdDiInN", 2, 0},
@@ -4789,6 +4806,11 @@ syn_cmd_keyword(eap, syncing)
/* Adjust flags for use of ":syn include". */
syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
+ /* Mark whether this syntax group combines with other syntax
+ * groups on the syntax stack. */
+ if (syn_opt_arg.flags & HL_COMBINE)
+ HL_TABLE()[syn_id - 1].sg_set |= SG_COMBINE;
+
/*
* 2: Add an entry for each keyword.
*/
@@ -4932,6 +4954,11 @@ syn_cmd_match(eap, syncing)
if (syn_opt_arg.flags & HL_FOLD)
++curwin->w_s->b_syn_folditems;
#endif
+ /* Mark whether this syntax group combines with other syntax groups
+ * on the syntax stack. */
+ if (syn_opt_arg.flags & HL_COMBINE)
+ HL_TABLE()[syn_id - 1].sg_set |= SG_COMBINE;
+
redraw_curbuf_later(SOME_VALID);
syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
@@ -5182,6 +5209,10 @@ syn_cmd_region(eap, syncing)
if (syn_opt_arg.flags & HL_FOLD)
++curwin->w_s->b_syn_folditems;
#endif
+ /* Mark whether this syntax group combines with other syntax
+ * groups on the syntax stack. */
+ if (syn_opt_arg.flags & HL_COMBINE)
+ HL_TABLE()[syn_id - 1].sg_set |= SG_COMBINE;
}
}
@@ -6438,6 +6469,20 @@ syn_get_stack_item(i)
}
return CUR_STATE(i).si_id;
}
+
+/*
+ * Return the length of the current stack.
+ * Used for functions that need to go through the stack 'backwards', i.e.
+ * innermost-to-outermost.
+ * The caller must have called syn_get_id() before to fill the stack.
+ * Returns -1 when "i" is out of range.
+ */
+ int
+syn_get_stack_length()
+{
+ return current_state.ga_len;
+}
+
#endif
#if defined(FEAT_FOLDING) || defined(PROTO)
@@ -8620,33 +8665,53 @@ highlight_list_arg(id, didh, type, iarg, sarg, name)
#if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
/*
- * Return "1" if highlight group "id" has attribute "flag".
- * Return NULL otherwise.
+ * Whether or not the highlight group combines with other highlight groups on
+ * the syntax stack.
*/
- char_u *
-highlight_has_attr(id, flag, modec)
+ int
+highlight_combines(id)
int id;
- int flag;
- int modec; /* 'g' for GUI, 'c' for cterm, 't' for term */
{
- int attr;
+ if (id <= 0 || id > highlight_ga.ga_len)
+ return 0;
+ /* Index is id - 1. */
+ return HL_TABLE()[id - 1].sg_set & SG_COMBINE;
+}
+
+/*
+ * Return the highlight attributes.
+ */
+ int
+highlight_get_attr(id, modec)
+ int id;
+ int modec; /* 'g' for GUI, 'c' for cterm, 't' for term */
+{
if (id <= 0 || id > highlight_ga.ga_len)
- return NULL;
+ return 0;
#if defined(FEAT_GUI) || defined(FEAT_EVAL)
if (modec == 'g')
- attr = HL_TABLE()[id - 1].sg_gui;
+ return HL_TABLE()[id - 1].sg_gui;
else
#endif
if (modec == 'c')
- attr = HL_TABLE()[id - 1].sg_cterm;
+ return HL_TABLE()[id - 1].sg_cterm;
else
- attr = HL_TABLE()[id - 1].sg_term;
+ return HL_TABLE()[id - 1].sg_term;
+}
- if (attr & flag)
- return (char_u *)"1";
- return NULL;
+/*
+ * Check if highlight group specified by "id" has attr "flag".
+ * "1" if it does, NULL otherwise.
+ */
+ char_u*
+highlight_has_attr(id, flag, modec)
+ int id;
+ int flag;
+ int modec; /* 'g' for GUI, 'c' for cterm, 't' for term */
+{
+ return highlight_get_attr(id, modec) & flag ? (char_u *)"1" : NULL;
}
#endif
@@ -9056,6 +9121,7 @@ syn_unadd_group()
vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
}
+
/*
* Translate a group ID to highlight attributes.
*/
diff --git a/src/vim.h b/src/vim.h
index 35b2310..de42322 100644
--- a/src/vim.h
+++ b/src/vim.h
@@ -894,6 +894,7 @@ extern char *(*dyn_libintl_textdomain)(const char *domainname);
# define HL_TRANS_CONT 0x10000 /* transparent item without contains arg */
# define HL_CONCEAL 0x20000 /* can be concealed */
# define HL_CONCEALENDS 0x40000 /* can be concealed */
+# define HL_COMBINE 0x80000 /* combine attributes with other matches */
#endif
/* Values for 'options' argument in do_search() and searchit() */