mirror of
https://github.com/neovim/neovim.git
synced 2025-10-16 06:46:07 +00:00
fix(ui): 'pumborder' parsing, shadow #36127
Problem: 1. Setting `pumborder=+,+,+,+,+,+,+,+` failed to render the custom border characters correctly. The issue occurred in `parse_winborder()` where it incorrectly used `p_winborder` instead of the `border_opt` parameter when the option value didn't contain commas. 2. In `pum_redraw()`, calling `parse_border_style()` directly with the option string failed to parse comma-separated border characters. 3. Missing documentation for PmenuShadow and PmenuShadowThrough highlight groups used by the shadow border style. 4. Coverity reports CID 631420: passing WinConfig (480 bytes) by value in `grid_draw_border()`. 5. crash when using `shadow` value on pumborder. Solution: 1. Fix `parse_winborder()` to use `border_opt` parameter consistently, ensuring the correct option value is parsed regardless of which option (winborder/pumborder) is being set. 2. Update `pum_redraw()` to call `parse_winborder()` instead of `parse_border_style()`, properly handling both predefined styles and custom comma-separated border characters. 3. Add documentation for PmenuShadow (blended shadow areas) and PmenuShadowThrough (see-through corners) highlight groups. 4. Change `grid_draw_border()` to accept WinConfig by pointer. 5. When the "shadow" style is used, no additional row and column offset is applied, and the border width is reduced.
This commit is contained in:
@@ -219,6 +219,7 @@ HIGHLIGHTS
|
||||
• |hl-DiffTextAdd| highlights added text within a changed line.
|
||||
• |hl-OkMsg| |hl-StderrMsg| |hl-StdoutMsg|
|
||||
• |hl-SnippetTabstopActive| highlights the currently active tabstop.
|
||||
• |hl-PmenuBorder |hl-PmenuShadow| |hl-PmenuShadowThrough| see 'pumborder'.
|
||||
|
||||
LSP
|
||||
|
||||
@@ -301,6 +302,7 @@ OPTIONS
|
||||
• 'winborder' "bold" style, custom border style.
|
||||
• |g:clipboard| accepts a string name to force any builtin clipboard tool.
|
||||
• 'busy' sets a buffer "busy" status. Indicated in the default statusline.
|
||||
• 'pumborder' add a border to the popup menu.
|
||||
|
||||
PERFORMANCE
|
||||
|
||||
|
@@ -4912,7 +4912,8 @@ A jump table for the options with a short description can be found at |Q_op|.
|
||||
'pumborder' string (default "")
|
||||
global
|
||||
Defines the default border style of popupmenu windows. Same as
|
||||
'winborder'.
|
||||
'winborder'. |hl-PmenuBorder| is used. When style is "shadow", the
|
||||
|hl-PmenuShadow| and |hl-PmenuShadowThrough| are used.
|
||||
|
||||
*'pumheight'* *'ph'*
|
||||
'pumheight' 'ph' number (default 0)
|
||||
|
@@ -5354,6 +5354,11 @@ Normal Normal text.
|
||||
NormalFloat Normal text in floating windows.
|
||||
*hl-FloatBorder*
|
||||
FloatBorder Border of floating windows.
|
||||
*hl-FloatShadow*
|
||||
FloatShadow Blended areas when border is shadow.
|
||||
*hl-FLoatShadowThrough*
|
||||
FloatShadownThrough
|
||||
shadow corners when border is shadow.
|
||||
*hl-FloatTitle*
|
||||
FloatTitle Title of floating windows.
|
||||
*hl-FloatFooter*
|
||||
@@ -5382,6 +5387,13 @@ PmenuMatch Popup menu: Matched text in normal item. Combined with
|
||||
*hl-PmenuMatchSel*
|
||||
PmenuMatchSel Popup menu: Matched text in selected item. Combined with
|
||||
|hl-PmenuMatch| and |hl-PmenuSel|.
|
||||
*hl-PmenuBorder*
|
||||
PmenuBorder Popup menu: border of popup menu.
|
||||
*hl-PmenuShadow*
|
||||
PmenuShadow Popup menu: blended areas when 'pumborder' is shadow.
|
||||
*hl-PmenuShadowThrough*
|
||||
PmenuShadownThrough
|
||||
Popup menu: shadow corners when 'pumborder' is shadow.
|
||||
*hl-ComplMatchIns*
|
||||
ComplMatchIns Matched text of the currently inserted completion.
|
||||
*hl-PreInsert*
|
||||
|
3
runtime/lua/vim/_meta/options.lua
generated
3
runtime/lua/vim/_meta/options.lua
generated
@@ -5130,7 +5130,8 @@ vim.go.pumblend = vim.o.pumblend
|
||||
vim.go.pb = vim.go.pumblend
|
||||
|
||||
--- Defines the default border style of popupmenu windows. Same as
|
||||
--- 'winborder'.
|
||||
--- 'winborder'. `hl-PmenuBorder` is used. When style is "shadow", the
|
||||
--- `hl-PmenuShadow` and `hl-PmenuShadowThrough` are used.
|
||||
---
|
||||
--- @type string
|
||||
vim.o.pumborder = ""
|
||||
|
@@ -1079,7 +1079,7 @@ static void generate_api_error(win_T *wp, const char *attribute, Error *err)
|
||||
}
|
||||
|
||||
/// Parses a border style name or custom (comma-separated) style.
|
||||
bool parse_winborder(WinConfig *fconfig, const char *border_opt, Error *err)
|
||||
bool parse_winborder(WinConfig *fconfig, char *border_opt, Error *err)
|
||||
{
|
||||
if (!fconfig) {
|
||||
return false;
|
||||
@@ -1088,7 +1088,7 @@ bool parse_winborder(WinConfig *fconfig, const char *border_opt, Error *err)
|
||||
|
||||
if (strchr(border_opt, ',')) {
|
||||
Array border_chars = ARRAY_DICT_INIT;
|
||||
char *p = p_winborder;
|
||||
char *p = border_opt;
|
||||
char part[MAX_SCHAR_SIZE] = { 0 };
|
||||
int count = 0;
|
||||
|
||||
@@ -1116,7 +1116,7 @@ bool parse_winborder(WinConfig *fconfig, const char *border_opt, Error *err)
|
||||
|
||||
style = ARRAY_OBJ(border_chars);
|
||||
} else {
|
||||
style = CSTR_TO_OBJ(p_winborder);
|
||||
style = CSTR_TO_OBJ(border_opt);
|
||||
}
|
||||
|
||||
parse_border_style(style, fconfig, err);
|
||||
|
@@ -658,7 +658,7 @@ int update_screen(void)
|
||||
win_grid_alloc(wp);
|
||||
|
||||
if (wp->w_redr_border || wp->w_redr_type >= UPD_NOT_VALID) {
|
||||
grid_draw_border(&wp->w_grid_alloc, wp->w_config, wp->w_border_adj, (int)wp->w_p_winbl,
|
||||
grid_draw_border(&wp->w_grid_alloc, &wp->w_config, wp->w_border_adj, (int)wp->w_p_winbl,
|
||||
wp->w_ns_hl_attr);
|
||||
}
|
||||
|
||||
|
@@ -1115,9 +1115,9 @@ static int get_bordertext_col(int total_col, int text_width, AlignTextPos align)
|
||||
}
|
||||
|
||||
/// draw border on floating window grid
|
||||
void grid_draw_border(ScreenGrid *grid, WinConfig config, int *adj, int winbl, int *hl_attr)
|
||||
void grid_draw_border(ScreenGrid *grid, WinConfig *config, int *adj, int winbl, int *hl_attr)
|
||||
{
|
||||
int *attrs = config.border_attr;
|
||||
int *attrs = config->border_attr;
|
||||
int default_adj[4] = { 1, 1, 1, 1 };
|
||||
if (adj == NULL) {
|
||||
adj = default_adj;
|
||||
@@ -1128,7 +1128,7 @@ void grid_draw_border(ScreenGrid *grid, WinConfig config, int *adj, int winbl, i
|
||||
}
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
chars[i] = schar_from_str(config.border_chars[i]);
|
||||
chars[i] = schar_from_str(config->border_chars[i]);
|
||||
}
|
||||
|
||||
int irow = grid->rows - adj[0] - adj[2];
|
||||
@@ -1144,9 +1144,9 @@ void grid_draw_border(ScreenGrid *grid, WinConfig config, int *adj, int winbl, i
|
||||
grid_line_put_schar(i + adj[3], chars[1], attrs[1]);
|
||||
}
|
||||
|
||||
if (config.title) {
|
||||
int title_col = get_bordertext_col(icol, config.title_width, config.title_pos);
|
||||
grid_draw_bordertext(config.title_chunks, title_col, winbl, hl_attr, kBorderTextTitle);
|
||||
if (config->title) {
|
||||
int title_col = get_bordertext_col(icol, config->title_width, config->title_pos);
|
||||
grid_draw_bordertext(config->title_chunks, title_col, winbl, hl_attr, kBorderTextTitle);
|
||||
}
|
||||
if (adj[1]) {
|
||||
grid_line_put_schar(icol + adj[3], chars[2], attrs[2]);
|
||||
@@ -1179,9 +1179,9 @@ void grid_draw_border(ScreenGrid *grid, WinConfig config, int *adj, int winbl, i
|
||||
grid_line_put_schar(i + adj[3], chars[ic], attrs[ic]);
|
||||
}
|
||||
|
||||
if (config.footer) {
|
||||
int footer_col = get_bordertext_col(icol, config.footer_width, config.footer_pos);
|
||||
grid_draw_bordertext(config.footer_chunks, footer_col, winbl, hl_attr, kBorderTextFooter);
|
||||
if (config->footer) {
|
||||
int footer_col = get_bordertext_col(icol, config->footer_width, config->footer_pos);
|
||||
grid_draw_bordertext(config->footer_chunks, footer_col, winbl, hl_attr, kBorderTextFooter);
|
||||
}
|
||||
if (adj[1]) {
|
||||
grid_line_put_schar(icol + adj[3], chars[4], attrs[4]);
|
||||
|
@@ -6727,7 +6727,8 @@ local options = {
|
||||
values = { '', 'double', 'single', 'shadow', 'rounded', 'solid', 'bold', 'none' },
|
||||
desc = [=[
|
||||
Defines the default border style of popupmenu windows. Same as
|
||||
'winborder'.
|
||||
'winborder'. |hl-PmenuBorder| is used. When style is "shadow", the
|
||||
|hl-PmenuShadow| and |hl-PmenuShadowThrough| are used.
|
||||
]=],
|
||||
short_desc = N_('border of popupmenu'),
|
||||
type = 'string',
|
||||
|
@@ -2125,7 +2125,7 @@ const char *did_set_winbar(optset_T *args)
|
||||
return did_set_statustabline_rulerformat(args, false, false);
|
||||
}
|
||||
|
||||
static bool parse_border_opt(const char *border_opt)
|
||||
static bool parse_border_opt(char *border_opt)
|
||||
{
|
||||
WinConfig fconfig = WIN_CONFIG_INIT;
|
||||
Error err = ERROR_INIT;
|
||||
|
@@ -298,6 +298,9 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i
|
||||
pum_rl = (State & MODE_CMDLINE) == 0 && curwin->w_p_rl;
|
||||
|
||||
int pum_border_size = *p_pumborder != NUL ? 2 : 0;
|
||||
if (strequal(p_pumborder, opt_winborder_values[3])) {
|
||||
pum_border_size -= 1;
|
||||
}
|
||||
|
||||
do {
|
||||
// Mark the pum as visible already here,
|
||||
@@ -605,19 +608,34 @@ void pum_redraw(void)
|
||||
pum_border_width = 2;
|
||||
fconfig.border = true;
|
||||
Error err = ERROR_INIT;
|
||||
parse_border_style(CSTR_AS_OBJ(p_pumborder), &fconfig, &err);
|
||||
// shadow style uses different highlights for different positions
|
||||
if (strcmp(p_pumborder, opt_winborder_values[3]) == 0) {
|
||||
if (!parse_winborder(&fconfig, p_pumborder, &err)) {
|
||||
if (ERROR_SET(&err)) {
|
||||
emsg(err.msg);
|
||||
}
|
||||
api_clear_error(&err);
|
||||
return;
|
||||
}
|
||||
|
||||
// Shadow style: only adds border on right and bottom edges
|
||||
if (strequal(p_pumborder, opt_winborder_values[3])) {
|
||||
fconfig.shadow = true;
|
||||
pum_border_width = 1;
|
||||
int blend = SYN_GROUP_STATIC("PmenuShadow");
|
||||
int through = SYN_GROUP_STATIC("PmenuShadowThrough");
|
||||
int attrs[] = { 0, 0, through, blend, blend, blend, through, 0 };
|
||||
memcpy(fconfig.border_attr, attrs, sizeof(attrs));
|
||||
} else {
|
||||
// Non-shadow styles use PumBorder highlight for all border chars
|
||||
fconfig.border_hl_ids[2] = through;
|
||||
fconfig.border_hl_ids[3] = blend;
|
||||
fconfig.border_hl_ids[4] = blend;
|
||||
fconfig.border_hl_ids[5] = blend;
|
||||
fconfig.border_hl_ids[6] = through;
|
||||
}
|
||||
|
||||
// convert border highlight IDs to attributes, use PmenuBorder as default
|
||||
for (int i = 0; i < 8; i++) {
|
||||
int attr = hl_attr_active[HLF_PBR];
|
||||
for (int i = 0; i < 8; i++) {
|
||||
fconfig.border_attr[i] = attr;
|
||||
if (fconfig.border_hl_ids[i]) {
|
||||
attr = hl_get_ui_attr(-1, HLF_PBR, fconfig.border_hl_ids[i], false);
|
||||
}
|
||||
fconfig.border_attr[i] = attr;
|
||||
}
|
||||
api_clear_error(&err);
|
||||
}
|
||||
@@ -653,9 +671,11 @@ void pum_redraw(void)
|
||||
// avoid set border for mouse menu
|
||||
int mouse_menu = State != MODE_CMDLINE && pum_grid.zindex == kZIndexCmdlinePopupMenu;
|
||||
if (!mouse_menu && fconfig.border) {
|
||||
grid_draw_border(&pum_grid, fconfig, NULL, 0, NULL);
|
||||
row++;
|
||||
col_off++;
|
||||
grid_draw_border(&pum_grid, &fconfig, NULL, 0, NULL);
|
||||
if (!fconfig.shadow) {
|
||||
row++;
|
||||
col_off++;
|
||||
}
|
||||
}
|
||||
|
||||
// Never display more than we have
|
||||
|
@@ -1061,6 +1061,8 @@ describe('builtin popupmenu', function()
|
||||
[111] = { background = Screen.colors.Plum1, foreground = Screen.colors.DarkBlue },
|
||||
[112] = { background = Screen.colors.Plum1, foreground = Screen.colors.DarkGreen },
|
||||
[113] = { background = Screen.colors.Yellow, foreground = Screen.colors.Black },
|
||||
[114] = { background = Screen.colors.Grey0, blend = 100 },
|
||||
[115] = { background = Screen.colors.Grey0, blend = 80 },
|
||||
-- popup non-selected item
|
||||
n = { background = Screen.colors.Plum1 },
|
||||
-- popup scrollbar knob
|
||||
@@ -9130,6 +9132,106 @@ describe('builtin popupmenu', function()
|
||||
]])
|
||||
end
|
||||
end)
|
||||
it('custom pumborder characters', function()
|
||||
command('set pumborder=+,+,=,+,+,-,+,+')
|
||||
feed('S<C-x><C-o>')
|
||||
if not multigrid then
|
||||
screen:expect([[
|
||||
one^ |
|
||||
++++++++++++++++={n:1info}{1: }|
|
||||
+{12:one }+{1: }|
|
||||
+{n:two }+{1: }|
|
||||
+{n:three }+{1: }|
|
||||
+---------------+{1: }|
|
||||
{1:~ }|*4
|
||||
{5:-- }{6:match 1 of 3} |
|
||||
]])
|
||||
end
|
||||
end)
|
||||
it('pumborder with shadow', function()
|
||||
command('set pumborder=shadow')
|
||||
feed('S<C-x><C-o>')
|
||||
if multigrid then
|
||||
screen:expect({
|
||||
grid = [[
|
||||
## grid 1
|
||||
[2:------------------------------]|*10
|
||||
[3:------------------------------]|
|
||||
## grid 2
|
||||
one^ |
|
||||
{1:~ }|*9
|
||||
## grid 3
|
||||
{5:-- }{6:match 1 of 3} |
|
||||
## grid 4
|
||||
{n:1info}|
|
||||
## grid 5
|
||||
{12:one }{114: }|
|
||||
{n:two }{115: }|
|
||||
{n:three }{115: }|
|
||||
{114: }{115: }|
|
||||
]],
|
||||
win_pos = {
|
||||
[2] = {
|
||||
height = 10,
|
||||
startcol = 0,
|
||||
startrow = 0,
|
||||
width = 30,
|
||||
win = 1000,
|
||||
},
|
||||
},
|
||||
float_pos = {
|
||||
[5] = { -1, 'NW', 2, 1, 0, false, 100, 2, 1, 0 },
|
||||
[4] = { 1001, 'NW', 1, 1, 17, false, 50, 1, 1, 17 },
|
||||
},
|
||||
win_viewport = {
|
||||
[2] = {
|
||||
win = 1000,
|
||||
topline = 0,
|
||||
botline = 2,
|
||||
curline = 0,
|
||||
curcol = 3,
|
||||
linecount = 1,
|
||||
sum_scroll_delta = 0,
|
||||
},
|
||||
[4] = {
|
||||
win = 1001,
|
||||
topline = 0,
|
||||
botline = 1,
|
||||
curline = 0,
|
||||
curcol = 0,
|
||||
linecount = 1,
|
||||
sum_scroll_delta = 0,
|
||||
},
|
||||
},
|
||||
win_viewport_margins = {
|
||||
[2] = {
|
||||
bottom = 0,
|
||||
left = 0,
|
||||
right = 0,
|
||||
top = 0,
|
||||
win = 1000,
|
||||
},
|
||||
[4] = {
|
||||
bottom = 0,
|
||||
left = 0,
|
||||
right = 0,
|
||||
top = 0,
|
||||
win = 1001,
|
||||
},
|
||||
},
|
||||
})
|
||||
else
|
||||
screen:expect([[
|
||||
one^ |
|
||||
{12:one }{114: }{1: }{n:1info}{1: }|
|
||||
{n:two }{115: }{1: }|
|
||||
{n:three }{115: }{1: }|
|
||||
{114: }{115: }{1: }|
|
||||
{1:~ }|*5
|
||||
{5:-- }{6:match 1 of 3} |
|
||||
]])
|
||||
end
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
|
@@ -300,6 +300,7 @@ let test_values = {
|
||||
\ 'alpha,hex,bin'],
|
||||
\ ['xxx']],
|
||||
\ 'patchmode': [['', 'xxx', '.x'], [&backupext, '*']],
|
||||
\ 'pumborder': [['rounded', 'none', 'single', 'solid'], ['xxx', 'none,solid']],
|
||||
"\ 'previewpopup': [['', 'height:13', 'width:20', 'highlight:That',
|
||||
"\ " 'align:item', 'align:menu', 'border:on', 'border:off',
|
||||
"\ " 'width:10,height:234,highlight:Mine'],
|
||||
|
Reference in New Issue
Block a user