mirror of
https://github.com/neovim/neovim.git
synced 2026-03-31 04:42:03 +00:00
feat(statusline): option to specify stacking highlight groups #37153
**Problem:** No easy way to stack highlight groups #35806. **Solution:** Add a way to specify a new statusline chunk with a highlight group that inherits from previous highlight attributes. Also applies to tabline, etc.
This commit is contained in:
@@ -353,6 +353,8 @@ OPTIONS
|
||||
• 'busy' sets a buffer "busy" status. Indicated in the default statusline.
|
||||
• 'pumborder' adds a border to the popup menu.
|
||||
• |g:clipboard| autodetection only selects tmux when running inside tmux
|
||||
• 'statusline' allows "stacking" highlight groups (groups inherit from
|
||||
previous highlight attributes)
|
||||
|
||||
PERFORMANCE
|
||||
|
||||
|
||||
@@ -6409,6 +6409,8 @@ A jump table for the options with a short description can be found at |Q_op|.
|
||||
Thus use %#HLname# for highlight group HLname. The same
|
||||
highlighting is used, also for the statusline of non-current
|
||||
windows.
|
||||
$ - Same as `#`, except the `%$HLname$` group will inherit from
|
||||
preceding highlight attributes.
|
||||
* - Set highlight group to User{N}, where {N} is taken from the
|
||||
minwid field, e.g. %1*. Restore normal highlight with %* or
|
||||
%0*. The difference between User{N} and StatusLine will be
|
||||
|
||||
2
runtime/lua/vim/_meta/options.lua
generated
2
runtime/lua/vim/_meta/options.lua
generated
@@ -6857,6 +6857,8 @@ vim.wo.stc = vim.wo.statuscolumn
|
||||
--- Thus use %#HLname# for highlight group HLname. The same
|
||||
--- highlighting is used, also for the statusline of non-current
|
||||
--- windows.
|
||||
--- $ - Same as `#`, except the `%$HLname$` group will inherit from
|
||||
--- preceding highlight attributes.
|
||||
--- * - Set highlight group to User{N}, where {N} is taken from the
|
||||
--- minwid field, e.g. %1*. Restore normal highlight with %* or
|
||||
--- %0*. The difference between User{N} and StatusLine will be
|
||||
|
||||
@@ -222,8 +222,8 @@ enum {
|
||||
STL_PREVIEWFLAG, STL_PREVIEWFLAG_ALT, STL_MODIFIED, STL_MODIFIED_ALT, \
|
||||
STL_QUICKFIX, STL_PERCENTAGE, STL_ALTPERCENT, STL_ARGLISTSTAT, STL_PAGENUM, \
|
||||
STL_SHOWCMD, STL_FOLDCOL, STL_SIGNCOL, STL_VIM_EXPR, STL_SEPARATE, \
|
||||
STL_TRUNCMARK, STL_USER_HL, STL_HIGHLIGHT, STL_TABPAGENR, STL_TABCLOSENR, \
|
||||
STL_CLICK_FUNC, STL_TABPAGENR, STL_TABCLOSENR, STL_CLICK_FUNC, \
|
||||
STL_TRUNCMARK, STL_USER_HL, STL_HIGHLIGHT, STL_HIGHLIGHT_COMB, STL_TABPAGENR, \
|
||||
STL_TABCLOSENR, STL_CLICK_FUNC, STL_TABPAGENR, STL_TABCLOSENR, STL_CLICK_FUNC, \
|
||||
0, })
|
||||
|
||||
// arguments for can_bs()
|
||||
|
||||
@@ -8914,6 +8914,8 @@ local options = {
|
||||
Thus use %#HLname# for highlight group HLname. The same
|
||||
highlighting is used, also for the statusline of non-current
|
||||
windows.
|
||||
$ - Same as `#`, except the `%$HLname$` group will inherit from
|
||||
preceding highlight attributes.
|
||||
* - Set highlight group to User{N}, where {N} is taken from the
|
||||
minwid field, e.g. %1*. Restore normal highlight with %* or
|
||||
%0*. The difference between User{N} and StatusLine will be
|
||||
|
||||
@@ -374,7 +374,12 @@ static void win_redr_custom(win_T *wp, bool draw_winbar, bool draw_ruler, bool u
|
||||
curattr = attr;
|
||||
curgroup = (int)group;
|
||||
} else if (sp->userhl < 0) {
|
||||
curattr = syn_id2attr(-sp->userhl);
|
||||
int new_attr = syn_id2attr(-sp->userhl);
|
||||
if (sp->item == STL_HIGHLIGHT_COMB) {
|
||||
curattr = hl_combine_attr(curattr, new_attr);
|
||||
} else {
|
||||
curattr = new_attr;
|
||||
}
|
||||
curgroup = -sp->userhl;
|
||||
} else {
|
||||
int *userhl = (wp != NULL && wp != curwin && wp->w_status_height != 0)
|
||||
@@ -1081,7 +1086,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, OptIndex op
|
||||
// remove group if all items are empty and highlight group
|
||||
// doesn't change
|
||||
for (n = stl_groupitems[groupdepth] - 1; n >= 0; n--) {
|
||||
if (stl_items[n].type == Highlight) {
|
||||
if (stl_items[n].type == Highlight || stl_items[n].type == HighlightCombining) {
|
||||
group_start_userhl = group_end_userhl = stl_items[n].minwid;
|
||||
break;
|
||||
}
|
||||
@@ -1090,7 +1095,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, OptIndex op
|
||||
if (stl_items[n].type == Normal) {
|
||||
break;
|
||||
}
|
||||
if (stl_items[n].type == Highlight) {
|
||||
if (stl_items[n].type == Highlight || stl_items[n].type == HighlightCombining) {
|
||||
group_end_userhl = stl_items[n].minwid;
|
||||
}
|
||||
}
|
||||
@@ -1100,7 +1105,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, OptIndex op
|
||||
group_len = 0;
|
||||
for (n = stl_groupitems[groupdepth] + 1; n < curitem; n++) {
|
||||
// do not use the highlighting from the removed group
|
||||
if (stl_items[n].type == Highlight) {
|
||||
if (stl_items[n].type == Highlight || stl_items[n].type == HighlightCombining) {
|
||||
stl_items[n].type = Empty;
|
||||
}
|
||||
// adjust the start position of TabPage to the next
|
||||
@@ -1682,17 +1687,18 @@ stcsign:
|
||||
}
|
||||
break;
|
||||
|
||||
case STL_HIGHLIGHT_COMB:
|
||||
case STL_HIGHLIGHT: {
|
||||
// { The name of the highlight is surrounded by `#`
|
||||
// { The name of the highlight is surrounded by `#` or `$`
|
||||
char *t = fmt_p;
|
||||
while (*fmt_p != '#' && *fmt_p != NUL) {
|
||||
while (*fmt_p != opt && *fmt_p != NUL) {
|
||||
fmt_p++;
|
||||
}
|
||||
// }
|
||||
|
||||
// Create a highlight item based on the name
|
||||
if (*fmt_p == '#') {
|
||||
stl_items[curitem].type = Highlight;
|
||||
if (*fmt_p == opt) {
|
||||
stl_items[curitem].type = opt == STL_HIGHLIGHT_COMB ? HighlightCombining : Highlight;
|
||||
stl_items[curitem].start = out_p;
|
||||
stl_items[curitem].minwid = -syn_name2id_len(t, (size_t)(fmt_p - t));
|
||||
curitem++;
|
||||
@@ -2073,12 +2079,14 @@ stcsign:
|
||||
*hltab = stl_hltab;
|
||||
stl_hlrec_t *sp = stl_hltab;
|
||||
for (int l = evalstart; l < itemcnt + evalstart; l++) {
|
||||
if (stl_items[l].type == Highlight
|
||||
if (stl_items[l].type == Highlight || stl_items[l].type == HighlightCombining
|
||||
|| stl_items[l].type == HighlightFold || stl_items[l].type == HighlightSign) {
|
||||
sp->start = stl_items[l].start;
|
||||
sp->userhl = stl_items[l].minwid;
|
||||
unsigned type = stl_items[l].type;
|
||||
sp->item = type == HighlightSign ? STL_SIGNCOL : type == HighlightFold ? STL_FOLDCOL : 0;
|
||||
sp->item = type == HighlightSign ? STL_SIGNCOL : type ==
|
||||
HighlightFold ? STL_FOLDCOL : type ==
|
||||
HighlightCombining ? STL_HIGHLIGHT_COMB : 0;
|
||||
sp++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ typedef enum {
|
||||
STL_TRUNCMARK = '<', ///< Truncation mark if line is too long.
|
||||
STL_USER_HL = '*', ///< Highlight from (User)1..9 or 0.
|
||||
STL_HIGHLIGHT = '#', ///< Highlight name.
|
||||
STL_HIGHLIGHT_COMB = '$', ///< Highlight name (combining previous attrs).
|
||||
STL_TABPAGENR = 'T', ///< Tab page label nr.
|
||||
STL_TABCLOSENR = 'X', ///< Tab page close nr.
|
||||
STL_CLICK_FUNC = '@', ///< Click region start.
|
||||
@@ -88,6 +89,7 @@ struct stl_item {
|
||||
Group,
|
||||
Separate,
|
||||
Highlight,
|
||||
HighlightCombining,
|
||||
HighlightSign,
|
||||
HighlightFold,
|
||||
TabPage,
|
||||
|
||||
@@ -99,6 +99,47 @@ for _, model in ipairs(mousemodels) do
|
||||
eq('0 1 l', eval('g:testvar'))
|
||||
end)
|
||||
|
||||
it('works with combined highlight attributes', function()
|
||||
screen:add_extra_attr_ids({
|
||||
[131] = { reverse = true, bold = true, background = Screen.colors.LightMagenta },
|
||||
[132] = {
|
||||
reverse = true,
|
||||
foreground = Screen.colors.Magenta,
|
||||
bold = true,
|
||||
background = Screen.colors.LightMagenta,
|
||||
},
|
||||
[133] = { reverse = true, bold = true, foreground = Screen.colors.Magenta1 },
|
||||
[134] = {
|
||||
bold = true,
|
||||
background = Screen.colors.LightMagenta,
|
||||
reverse = true,
|
||||
undercurl = true,
|
||||
special = Screen.colors.Red,
|
||||
},
|
||||
[135] = {
|
||||
bold = true,
|
||||
background = Screen.colors.LightMagenta,
|
||||
reverse = true,
|
||||
undercurl = true,
|
||||
foreground = Screen.colors.Fuchsia,
|
||||
special = Screen.colors.Red,
|
||||
},
|
||||
})
|
||||
|
||||
api.nvim_set_option_value(
|
||||
'statusline',
|
||||
'\t%#Pmenu#foo%$SpellBad$bar%$String$baz%#Constant#qux',
|
||||
{}
|
||||
)
|
||||
|
||||
screen:expect([[
|
||||
^ |
|
||||
{1:~ }|*5
|
||||
{3:^I}{131:foo}{134:bar}{135:baz}{133:qux }|
|
||||
|
|
||||
]])
|
||||
end)
|
||||
|
||||
it('works for winbar', function()
|
||||
api.nvim_set_option_value('winbar', 'Not clicky stuff %0@MyClickFunc@Clicky stuff%T', {})
|
||||
api.nvim_input_mouse('left', 'press', '', 0, 0, 17)
|
||||
|
||||
@@ -336,13 +336,13 @@ let test_values = {
|
||||
\ 'timeout:-1', 'file:/tmp/file', 'expr:Func()', 'double,33'],
|
||||
\ ['xxx', '-1', 'timeout:', 'best,double', 'double,fast']],
|
||||
\ 'splitkeep': [['cursor', 'screen', 'topline'], ['xxx']],
|
||||
\ 'statusline': [['', 'xxx'], ['%$', '%{', '%{%', '%{%}', '%(', '%)']],
|
||||
\ 'statusline': [['', 'xxx'], ['%{', '%{%', '%{%}', '%(', '%)']],
|
||||
"\ 'swapsync': [['', 'sync', 'fsync'], ['xxx']],
|
||||
\ 'switchbuf': [['', 'useopen', 'usetab', 'split', 'vsplit', 'newtab',
|
||||
\ 'uselast', 'split,newtab'],
|
||||
\ ['xxx']],
|
||||
\ 'tabclose': [['', 'left', 'uselast', 'left,uselast'], ['xxx']],
|
||||
\ 'tabline': [['', 'xxx'], ['%$', '%{', '%{%', '%{%}', '%(', '%)']],
|
||||
\ 'tabline': [['', 'xxx'], ['%{', '%{%', '%{%}', '%(', '%)']],
|
||||
\ 'tagcase': [['followic', 'followscs', 'ignore', 'match', 'smart'],
|
||||
\ ['', 'xxx', 'smart,match']],
|
||||
\ 'termencoding': [has('gui_gtk') ? [] : ['', 'utf-8'], ['xxx']],
|
||||
|
||||
@@ -866,7 +866,6 @@ func Test_set_option_errors()
|
||||
call assert_fails('set rulerformat=%15(%%', 'E542:')
|
||||
|
||||
" Test for 'statusline' errors
|
||||
call assert_fails('set statusline=%$', 'E539:')
|
||||
call assert_fails('set statusline=%{', 'E540:')
|
||||
call assert_fails('set statusline=%{%', 'E540:')
|
||||
call assert_fails('set statusline=%{%}', 'E539:')
|
||||
@@ -874,7 +873,6 @@ func Test_set_option_errors()
|
||||
call assert_fails('set statusline=%)', 'E542:')
|
||||
|
||||
" Test for 'tabline' errors
|
||||
call assert_fails('set tabline=%$', 'E539:')
|
||||
call assert_fails('set tabline=%{', 'E540:')
|
||||
call assert_fails('set tabline=%{%', 'E540:')
|
||||
call assert_fails('set tabline=%{%}', 'E539:')
|
||||
|
||||
Reference in New Issue
Block a user