Upon reconsideration, synIDtrans should return a list of IDs that are being
used in the current highlight group. This includes all links that also have
attributes & the top level group if any, skipping vanilla links.
This is consistent with the behavior that syntax-combine should exhibit.
Here's the patch updated with the synIDtrans fix.
We might want to consider adding a "link" {what} to synIDattr, as it's now a
bit harder to detect links.
--
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/src/eval.c b/src/eval.c
index aab9b0b..a373458 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -17651,17 +17651,44 @@ f_synIDtrans(argvars, rettv)
typval_T *rettv;
{
int id;
+ int count;
+ listitem_T *item;
#ifdef FEAT_SYN_HL
id = get_tv_number(&argvars[0]);
if (id > 0)
+ {
id = syn_get_final_id(id);
+ if (syn_extends_link(id))
+ {
+ if (rettv_list_alloc(rettv) == OK)
+ {
+ for (;;)
+ {
+ list_append_number(rettv->vval.v_list, (varnumber_T)id);
+ id = syn_get_final_id(syn_parent(id));
+ if (id <= 0)
+ break;
+ /*
+ * Linear-time loop detection. Not ideal, but we expect
+ * these chains to be fairly short.
+ */
+ for (item = rettv->vval.v_list->lv_first; item != NULL;
+ item = item->li_next)
+ if (item->li_tv.vval.v_number == (varnumber_T)id)
+ return;
+ }
+ }
+ else
+ rettv->vval.v_number = id;
+ }
+ else
+ rettv->vval.v_number = id;
+ }
else
#endif
- id = 0;
-
- rettv->vval.v_number = id;
+ rettv->vval.v_number = 0;
}
/*
diff --git a/src/syntax.c b/src/syntax.c
index abaa025..ef1cf1a 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_INHERIT 0x10 /* link has been set */
static garray_T highlight_ga; /* highlight groups for 'highlight' option */
@@ -4015,7 +4016,10 @@ syn_list_one(id, syncing, link_only)
if (HL_TABLE()[id - 1].sg_link && (did_header || link_only) && !got_int)
{
(void)syn_list_header(did_header, 999, id);
- msg_puts_attr((char_u *)"links to", attr);
+ if (HL_TABLE()[id - 1].sg_set & SG_INHERIT)
+ msg_puts_attr((char_u *)"inherits from", attr);
+ else
+ msg_puts_attr((char_u *)"links to", attr);
msg_putchar(' ');
msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
}
@@ -6931,71 +6935,6 @@ do_highlight(line, forceit, init)
return;
}
- /*
- * Handle ":highlight link {from} {to}" command.
- */
- if (dolink)
- {
- char_u *from_start = linep;
- char_u *from_end;
- char_u *to_start;
- char_u *to_end;
- int from_id;
- int to_id;
-
- from_end = skiptowhite(from_start);
- to_start = skipwhite(from_end);
- to_end = skiptowhite(to_start);
-
- if (ends_excmd(*from_start) || ends_excmd(*to_start))
- {
- EMSG2(_("E412: Not enough arguments: \":highlight link %s\""),
- from_start);
- return;
- }
-
- if (!ends_excmd(*skipwhite(to_end)))
- {
- EMSG2(_("E413: Too many arguments: \":highlight link %s\""), from_start);
- return;
- }
-
- from_id = syn_check_group(from_start, (int)(from_end - from_start));
- if (STRNCMP(to_start, "NONE", 4) == 0)
- to_id = 0;
- else
- to_id = syn_check_group(to_start, (int)(to_end - to_start));
-
- if (from_id > 0 && (!init || HL_TABLE()[from_id - 1].sg_set == 0))
- {
- /*
- * Don't allow a link when there already is some highlighting
- * for the group, unless '!' is used
- */
- if (to_id > 0 && !forceit && !init
- && hl_has_settings(from_id - 1, dodefault))
- {
- if (sourcing_name == NULL && !dodefault)
- EMSG(_("E414: group has settings, highlight link ignored"));
- }
- else
- {
- if (!init)
- HL_TABLE()[from_id - 1].sg_set |= SG_LINK;
- HL_TABLE()[from_id - 1].sg_link = to_id;
-#ifdef FEAT_EVAL
- HL_TABLE()[from_id - 1].sg_scriptID = current_SID;
-#endif
- redraw_all_later(SOME_VALID);
- }
- }
-
- /* Only call highlight_changed() once, after sourcing a syntax file */
- need_highlight_changed = TRUE;
-
- return;
- }
-
if (doclear)
{
/*
@@ -7074,7 +7013,18 @@ do_highlight(line, forceit, init)
/*
* Find the group name in the table. If it does not exist yet, add it.
*/
- id = syn_check_group(line, (int)(name_end - line));
+ if (dolink)
+ {
+ /*
+ * If we're linking the current name is 'link', not the highlight group.
+ * Isolate the actual highlight group.
+ */
+ name_end = skiptowhite(linep);
+ id = syn_check_group(linep, (int)(name_end - linep));
+ linep = skipwhite(name_end);
+ }
+ else
+ id = syn_check_group(line, (int)(name_end - line));
if (id == 0) /* failed (out of memory) */
return;
idx = id - 1; /* index is ID minus one */
@@ -7083,6 +7033,50 @@ do_highlight(line, forceit, init)
if (dodefault && hl_has_settings(idx, TRUE))
return;
+ /*
+ * Handle the 'highlight link {child} {parent}' command.
+ */
+ if (dolink)
+ {
+ char_u *parent_end;
+ int parent_id;
+
+ parent_end = skiptowhite(linep);
+
+ if (ends_excmd(*linep))
+ {
+ EMSG2(_("E412: Not enough arguments: \":highlight %s\""), line);
+ return;
+ }
+
+ if (STRNCMP(linep, "NONE", 4) == 0)
+ parent_id = 0;
+ else
+ parent_id = syn_check_group(linep, (int)(parent_end - linep));
+
+ if (!init || HL_TABLE()[idx].sg_set == 0)
+ {
+ /*
+ * Don't allow a link when there already is some highlighting
+ * for the group, unless '!' is used
+ */
+ if (parent_id > 0 && !forceit && !init
+ && hl_has_settings(idx, dodefault))
+ {
+ if (sourcing_name == NULL && !dodefault)
+ EMSG(_("E414: group has settings, highlight inherit ignored"));
+ }
+ else
+ {
+ if (!init)
+ HL_TABLE()[idx].sg_set |= SG_LINK;
+ HL_TABLE()[idx].sg_link = parent_id;
+ }
+ }
+
+ linep = skipwhite(parent_end);
+ }
+
if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
is_normal_group = TRUE;
#ifdef FEAT_GUI_X11
@@ -7698,10 +7692,13 @@ do_highlight(line, forceit, init)
}
/*
- * When highlighting has been given for a group, don't link it.
+ * When highlighting has been given for a group mark it as an inheritor.
*/
if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
- HL_TABLE()[idx].sg_link = 0;
+ if (dolink)
+ HL_TABLE()[idx].sg_set |= SG_INHERIT;
+ else
+ HL_TABLE()[idx].sg_link = 0;
/*
* Continue with next argument.
@@ -8569,7 +8566,10 @@ highlight_list_one(id)
{
(void)syn_list_header(didh, 9999, id);
didh = TRUE;
- msg_puts_attr((char_u *)"links to", hl_attr(HLF_D));
+ if (sgp->sg_set & SG_INHERIT)
+ msg_puts_attr((char_u *)"inherits from", hl_attr(HLF_D));
+ else
+ msg_puts_attr((char_u *)"links to", hl_attr(HLF_D));
msg_putchar(' ');
msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
}
@@ -9074,6 +9074,62 @@ syn_unadd_group()
}
/*
+ * Helper function to get the appropriate attr from an hl_group.
+ */
+ int
+sg_getattr(sgp)
+ struct hl_group* sgp;
+{
+#ifdef FEAT_GUI
+ /*
+ * Only use GUI attr when the GUI is being used.
+ */
+ if (gui.in_use)
+ return sgp->sg_gui_attr;
+ else
+#endif
+ if (t_colors > 1)
+ return sgp->sg_cterm_attr;
+ else
+ return sgp->sg_term_attr;
+}
+
+
+/*
+ * Find the parent of a group ID, e.g. the immediate link.
+ * Returns 0 if there is no parent.
+ */
+ int
+syn_parent(hl_id)
+ int hl_id;
+{
+ struct hl_group *sgp;
+ if (hl_id == 0)
+ return 0;
+ sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
+ if (sgp->sg_link > highlight_ga.ga_len)
+ return 0;
+ return sgp->sg_link;
+}
+
+
+/*
+ * TRUE if the highlight group links to another link and also extends it.
+ * FALSE otherwise.
+ */
+ int
+syn_extends_link(hl_id)
+ int hl_id;
+{
+ struct hl_group *sgp;
+ if (hl_id == 0)
+ return 0;
+ sgp = &HL_TABLE()[hl_id - 1];
+ return sgp->sg_set & SG_INHERIT+SG_LINK;
+}
+
+
+/*
* Translate a group ID to highlight attributes.
*/
int
@@ -9082,23 +9138,28 @@ syn_id2attr(hl_id)
{
int attr;
struct hl_group *sgp;
+ int count;
hl_id = syn_get_final_id(hl_id);
sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
-#ifdef FEAT_GUI
/*
- * Only use GUI attr when the GUI is being used.
+ * Accumulate attributes from all inheriting links up to the final link.
+ * Look out for loops! Break after 100 links.
*/
- if (gui.in_use)
- attr = sgp->sg_gui_attr;
+ if (sgp->sg_set & SG_INHERIT)
+ {
+ attr = 0;
+ for (count = 100; --count >= 0; )
+ {
+ attr = hl_combine_attr(sg_getattr(sgp), attr);
+ if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
+ break;
+ sgp = &HL_TABLE()[sgp->sg_link - 1];
+ }
+ }
else
-#endif
- if (t_colors > 1)
- attr = sgp->sg_cterm_attr;
- else
- attr = sgp->sg_term_attr;
-
+ attr = sg_getattr(sgp);
return attr;
}
@@ -9114,13 +9175,39 @@ syn_id2colors(hl_id, fgp, bgp)
guicolor_T *bgp;
{
struct hl_group *sgp;
+ int attr;
+ int count;
hl_id = syn_get_final_id(hl_id);
sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
- *fgp = sgp->sg_gui_fg;
- *bgp = sgp->sg_gui_bg;
- return sgp->sg_gui;
+ /*
+ * If it's an inheritor group and some colors aren't set, keep looking up
+ * the chain for the colors.
+ * Look out for loops! Break after 100 links.
+ */
+ if (sgp->sg_set & SG_INHERIT)
+ {
+ attr = 0;
+ for (count = 100; --count >= 0; )
+ {
+ attr = hl_combine_attr(sgp->sg_gui, attr);
+ if (*fgp == INVALCOLOR)
+ *fgp = sgp->sg_gui_fg;
+ if (*bgp == INVALCOLOR)
+ *bgp = sgp->sg_gui_fg;
+ if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
+ break;
+ sgp = &HL_TABLE()[sgp->sg_link - 1];
+ }
+ }
+ else
+ {
+ *fgp = sgp->sg_gui_fg;
+ *bgp = sgp->sg_gui_bg;
+ attr = sgp->sg_gui;
+ }
+ return attr;
}
#endif
@@ -9144,7 +9231,8 @@ syn_get_final_id(hl_id)
for (count = 100; --count >= 0; )
{
sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
- if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
+ if (sgp->sg_set & SG_INHERIT
+ || sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
break;
hl_id = sgp->sg_link;
}