mirror of
https://github.com/neovim/neovim
synced 2025-07-15 16:51:49 +00:00
feat(float): add winborder option (#31074)
Problem: There is currently no global option to define the default border style for floating windows. This leads to repetitive code when developers need consistent styling across multiple floating windows. Solution: Introduce a global option winborder to specify the default border style for floating windows. When a floating window is created without explicitly specifying a border style, the value of the winborder option will be used. This simplifies configuration and ensures consistency in floating window appearance. Co-authored-by: Gregory Anders <greg@gpanders.com>
This commit is contained in:
@ -3493,28 +3493,20 @@ nvim_open_win({buffer}, {enter}, {config}) *nvim_open_win()*
|
||||
`eob` flag of 'fillchars' to a space char, and clearing
|
||||
the |hl-EndOfBuffer| region in 'winhighlight'.
|
||||
• border: Style of (optional) window border. This can either
|
||||
be a string or an array. The string values are
|
||||
• "none": No border (default).
|
||||
• "single": A single line box.
|
||||
• "double": A double line box.
|
||||
• "rounded": Like "single", but with rounded corners
|
||||
("╭" etc.).
|
||||
• "solid": Adds padding by a single whitespace cell.
|
||||
• "shadow": A drop shadow effect by blending with the
|
||||
background.
|
||||
• If it is an array, it should have a length of eight or
|
||||
any divisor of eight. The array will specify the eight
|
||||
chars building up the border in a clockwise fashion
|
||||
starting with the top-left corner. As an example, the
|
||||
double box style could be specified as: >
|
||||
[ "╔", "═" ,"╗", "║", "╝", "═", "╚", "║" ].
|
||||
be a string or an array. The string values are the same as
|
||||
those described in 'winborder'. If it is an array, it
|
||||
should have a length of eight or any divisor of eight. The
|
||||
array will specify the eight chars building up the border
|
||||
in a clockwise fashion starting with the top-left corner.
|
||||
As an example, the double box style could be specified as: >
|
||||
[ "╔", "═" ,"╗", "║", "╝", "═", "╚", "║" ].
|
||||
<
|
||||
If the number of chars are less than eight, they will be
|
||||
repeated. Thus an ASCII border could be specified as >
|
||||
[ "/", "-", \"\\\\\", "|" ],
|
||||
If the number of chars are less than eight, they will be
|
||||
repeated. Thus an ASCII border could be specified as >
|
||||
[ "/", "-", \"\\\\\", "|" ],
|
||||
<
|
||||
or all chars the same as >
|
||||
[ "x" ].
|
||||
or all chars the same as >
|
||||
[ "x" ].
|
||||
<
|
||||
An empty string can be used to turn off a specific border,
|
||||
for instance, >
|
||||
|
@ -348,6 +348,7 @@ OPTIONS
|
||||
• 'messagesopt' configures |:messages| and |hit-enter| prompt.
|
||||
• 'tabclose' controls which tab page to focus when closing a tab page.
|
||||
• 'eventignorewin' to persistently ignore events in a window.
|
||||
• 'winborder' sets the default border for |floating-windows|
|
||||
|
||||
PERFORMANCE
|
||||
|
||||
|
@ -7150,6 +7150,18 @@ A jump table for the options with a short description can be found at |Q_op|.
|
||||
|
||||
UI-dependent. Works best with RGB colors. 'termguicolors'
|
||||
|
||||
*'winborder'*
|
||||
'winborder' string (default "")
|
||||
global
|
||||
Defines the default border style of floating windows. The default value
|
||||
is empty, which is equivalent to "none". Valid values include:
|
||||
- "none": No border.
|
||||
- "single": A single line box.
|
||||
- "double": A double line box.
|
||||
- "rounded": Like "single", but with rounded corners ("╭" etc.).
|
||||
- "solid": Adds padding by a single whitespace cell.
|
||||
- "shadow": A drop shadow effect by blending with the background.
|
||||
|
||||
*'window'* *'wi'*
|
||||
'window' 'wi' number (default screen height - 1)
|
||||
global
|
||||
|
16
runtime/lua/vim/_meta/api.lua
generated
16
runtime/lua/vim/_meta/api.lua
generated
@ -1797,17 +1797,11 @@ function vim.api.nvim_open_term(buffer, opts) end
|
||||
--- 'fillchars' to a space char, and clearing the
|
||||
--- `hl-EndOfBuffer` region in 'winhighlight'.
|
||||
--- - border: Style of (optional) window border. This can either be a string
|
||||
--- or an array. The string values are
|
||||
--- - "none": No border (default).
|
||||
--- - "single": A single line box.
|
||||
--- - "double": A double line box.
|
||||
--- - "rounded": Like "single", but with rounded corners ("╭" etc.).
|
||||
--- - "solid": Adds padding by a single whitespace cell.
|
||||
--- - "shadow": A drop shadow effect by blending with the background.
|
||||
--- - If it is an array, it should have a length of eight or any divisor of
|
||||
--- eight. The array will specify the eight chars building up the border
|
||||
--- in a clockwise fashion starting with the top-left corner. As an
|
||||
--- example, the double box style could be specified as:
|
||||
--- or an array. The string values are the same as those described in 'winborder'.
|
||||
--- If it is an array, it should have a length of eight or any divisor of
|
||||
--- eight. The array will specify the eight chars building up the border
|
||||
--- in a clockwise fashion starting with the top-left corner. As an
|
||||
--- example, the double box style could be specified as:
|
||||
--- ```
|
||||
--- [ "╔", "═" ,"╗", "║", "╝", "═", "╚", "║" ].
|
||||
--- ```
|
||||
|
13
runtime/lua/vim/_meta/options.lua
generated
13
runtime/lua/vim/_meta/options.lua
generated
@ -7832,6 +7832,19 @@ vim.o.winbl = vim.o.winblend
|
||||
vim.wo.winblend = vim.o.winblend
|
||||
vim.wo.winbl = vim.wo.winblend
|
||||
|
||||
--- Defines the default border style of floating windows. The default value
|
||||
--- is empty, which is equivalent to "none". Valid values include:
|
||||
--- - "none": No border.
|
||||
--- - "single": A single line box.
|
||||
--- - "double": A double line box.
|
||||
--- - "rounded": Like "single", but with rounded corners ("╭" etc.).
|
||||
--- - "solid": Adds padding by a single whitespace cell.
|
||||
--- - "shadow": A drop shadow effect by blending with the background.
|
||||
---
|
||||
--- @type ''|'double'|'single'|'shadow'|'rounded'|'solid'|'none'
|
||||
vim.o.winborder = ""
|
||||
vim.go.winborder = vim.o.winborder
|
||||
|
||||
--- Window height used for `CTRL-F` and `CTRL-B` when there is only one
|
||||
--- window and the value is smaller than 'lines' minus one. The screen
|
||||
--- will scroll 'window' minus two lines, with a minimum of one.
|
||||
|
@ -6,17 +6,6 @@ local uv = vim.uv
|
||||
|
||||
local M = {}
|
||||
|
||||
local default_border = {
|
||||
{ '', 'NormalFloat' },
|
||||
{ '', 'NormalFloat' },
|
||||
{ '', 'NormalFloat' },
|
||||
{ ' ', 'NormalFloat' },
|
||||
{ '', 'NormalFloat' },
|
||||
{ '', 'NormalFloat' },
|
||||
{ '', 'NormalFloat' },
|
||||
{ ' ', 'NormalFloat' },
|
||||
}
|
||||
|
||||
--- @param border string|(string|[string,string])[]
|
||||
local function border_error(border)
|
||||
error(
|
||||
@ -43,7 +32,11 @@ local border_size = {
|
||||
--- @return integer height
|
||||
--- @return integer width
|
||||
local function get_border_size(opts)
|
||||
local border = opts and opts.border or default_border
|
||||
local border = opts and opts.border or vim.o.winborder
|
||||
|
||||
if border == '' then
|
||||
border = 'none'
|
||||
end
|
||||
|
||||
if type(border) == 'string' then
|
||||
if not border_size[border] then
|
||||
@ -884,7 +877,7 @@ function M.make_floating_popup_options(width, height, opts)
|
||||
or 'cursor',
|
||||
style = 'minimal',
|
||||
width = width,
|
||||
border = opts.border or default_border,
|
||||
border = opts.border,
|
||||
zindex = opts.zindex or (api.nvim_win_get_config(0).zindex or 49) + 1,
|
||||
title = title,
|
||||
title_pos = title_pos,
|
||||
|
@ -159,17 +159,11 @@
|
||||
/// 'fillchars' to a space char, and clearing the
|
||||
/// |hl-EndOfBuffer| region in 'winhighlight'.
|
||||
/// - border: Style of (optional) window border. This can either be a string
|
||||
/// or an array. The string values are
|
||||
/// - "none": No border (default).
|
||||
/// - "single": A single line box.
|
||||
/// - "double": A double line box.
|
||||
/// - "rounded": Like "single", but with rounded corners ("╭" etc.).
|
||||
/// - "solid": Adds padding by a single whitespace cell.
|
||||
/// - "shadow": A drop shadow effect by blending with the background.
|
||||
/// - If it is an array, it should have a length of eight or any divisor of
|
||||
/// eight. The array will specify the eight chars building up the border
|
||||
/// in a clockwise fashion starting with the top-left corner. As an
|
||||
/// example, the double box style could be specified as:
|
||||
/// or an array. The string values are the same as those described in 'winborder'.
|
||||
/// If it is an array, it should have a length of eight or any divisor of
|
||||
/// eight. The array will specify the eight chars building up the border
|
||||
/// in a clockwise fashion starting with the top-left corner. As an
|
||||
/// example, the double box style could be specified as:
|
||||
/// ```
|
||||
/// [ "╔", "═" ,"╗", "║", "╝", "═", "╚", "║" ].
|
||||
/// ```
|
||||
@ -944,11 +938,11 @@ static void parse_border_style(Object style, WinConfig *fconfig, Error *err)
|
||||
char chars[8][MAX_SCHAR_SIZE];
|
||||
bool shadow_color;
|
||||
} defaults[] = {
|
||||
{ "double", { "╔", "═", "╗", "║", "╝", "═", "╚", "║" }, false },
|
||||
{ "single", { "┌", "─", "┐", "│", "┘", "─", "└", "│" }, false },
|
||||
{ "shadow", { "", "", " ", " ", " ", " ", " ", "" }, true },
|
||||
{ "rounded", { "╭", "─", "╮", "│", "╯", "─", "╰", "│" }, false },
|
||||
{ "solid", { " ", " ", " ", " ", " ", " ", " ", " " }, false },
|
||||
{ opt_winborder_values[1], { "╔", "═", "╗", "║", "╝", "═", "╚", "║" }, false },
|
||||
{ opt_winborder_values[2], { "┌", "─", "┐", "│", "┘", "─", "└", "│" }, false },
|
||||
{ opt_winborder_values[3], { "", "", " ", " ", " ", " ", " ", "" }, true },
|
||||
{ opt_winborder_values[4], { "╭", "─", "╮", "│", "╯", "─", "╰", "│" }, false },
|
||||
{ opt_winborder_values[5], { " ", " ", " ", " ", " ", " ", " ", " " }, false },
|
||||
{ NULL, { { NUL } }, false },
|
||||
};
|
||||
|
||||
@ -1279,12 +1273,13 @@ static bool parse_win_config(win_T *wp, Dict(win_config) *config, WinConfig *fco
|
||||
}
|
||||
}
|
||||
|
||||
if (HAS_KEY_X(config, border)) {
|
||||
if (HAS_KEY_X(config, border) || *p_winbd != NUL) {
|
||||
if (is_split) {
|
||||
api_set_error(err, kErrorTypeValidation, "non-float cannot have 'border'");
|
||||
goto fail;
|
||||
}
|
||||
parse_border_style(config->border, fconfig, err);
|
||||
Object style = config->border.type != kObjectTypeNil ? config->border : CSTR_AS_OBJ(p_winbd);
|
||||
parse_border_style(style, fconfig, err);
|
||||
if (ERROR_SET(err)) {
|
||||
goto fail;
|
||||
}
|
||||
|
@ -566,6 +566,7 @@ EXTERN OptInt p_wcm; ///< 'wildcharm'
|
||||
EXTERN int p_wic; ///< 'wildignorecase'
|
||||
EXTERN char *p_wim; ///< 'wildmode'
|
||||
EXTERN int p_wmnu; ///< 'wildmenu'
|
||||
EXTERN char *p_winbd; ///< 'winborder'
|
||||
EXTERN OptInt p_wh; ///< 'winheight'
|
||||
EXTERN OptInt p_wmh; ///< 'winminheight'
|
||||
EXTERN OptInt p_wmw; ///< 'winminwidth'
|
||||
|
@ -10188,6 +10188,26 @@ local options = {
|
||||
short_desc = N_('Controls transparency level for floating windows'),
|
||||
type = 'number',
|
||||
},
|
||||
{
|
||||
defaults = { if_true = '' },
|
||||
cb = 'did_set_winborder',
|
||||
values = { '', 'double', 'single', 'shadow', 'rounded', 'solid', 'none' },
|
||||
desc = [=[
|
||||
Defines the default border style of floating windows. The default value
|
||||
is empty, which is equivalent to "none". Valid values include:
|
||||
- "none": No border.
|
||||
- "single": A single line box.
|
||||
- "double": A double line box.
|
||||
- "rounded": Like "single", but with rounded corners ("╭" etc.).
|
||||
- "solid": Adds padding by a single whitespace cell.
|
||||
- "shadow": A drop shadow effect by blending with the background.
|
||||
]=],
|
||||
full_name = 'winborder',
|
||||
scope = { 'global' },
|
||||
short_desc = N_('border of floating window'),
|
||||
type = 'string',
|
||||
varname = 'p_winbd',
|
||||
},
|
||||
{
|
||||
abbreviation = 'wi',
|
||||
cb = 'did_set_window',
|
||||
|
@ -2005,6 +2005,15 @@ const char *did_set_winhighlight(optset_T *args)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/// The 'winborder' option is changed.
|
||||
const char *did_set_winborder(optset_T *args)
|
||||
{
|
||||
if (opt_strings_flags(p_winbd, opt_winborder_values, NULL, true) != OK) {
|
||||
return e_invarg;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int expand_set_winhighlight(optexpand_T *args, int *numMatches, char ***matches)
|
||||
{
|
||||
return expand_set_opt_generic(args, get_highlight_name, numMatches, matches);
|
||||
|
@ -9909,6 +9909,253 @@ describe('float window', function()
|
||||
]])
|
||||
eq({"%f", ""}, res)
|
||||
end)
|
||||
|
||||
it('winborder option', function()
|
||||
local buf = api.nvim_create_buf(false,false)
|
||||
local config = {relative='editor', width=4, height=4, row=2, col=2}
|
||||
command('set winborder=single')
|
||||
api.nvim_open_win(buf, true, config)
|
||||
if multigrid then
|
||||
screen:expect({
|
||||
grid = [[
|
||||
## grid 1
|
||||
[2:----------------------------------------]|*6
|
||||
[3:----------------------------------------]|
|
||||
## grid 2
|
||||
|
|
||||
{0:~ }|*5
|
||||
## grid 3
|
||||
|
|
||||
## grid 4
|
||||
{5:┌────┐}|
|
||||
{5:│}{1:^ }{5:│}|
|
||||
{5:│}{2:~ }{5:│}|*3
|
||||
{5:└────┘}|
|
||||
]], float_pos={
|
||||
[4] = {1001, "NW", 1, 2, 2, true, 50};
|
||||
}, win_viewport={
|
||||
[2] = {win = 1000, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1, sum_scroll_delta = 0};
|
||||
[4] = {win = 1001, topline = 0, botline = 2, 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 = 1,
|
||||
left = 1,
|
||||
right = 1,
|
||||
top = 1,
|
||||
win = 1001
|
||||
}
|
||||
}
|
||||
})
|
||||
else
|
||||
screen:expect({
|
||||
grid = [[
|
||||
{5:┌────┐} |
|
||||
{0:~ }{5:│}{1:^ }{5:│}{0: }|
|
||||
{0:~ }{5:│}{2:~ }{5:│}{0: }|*3
|
||||
{0:~ }{5:└────┘}{0: }|
|
||||
|
|
||||
]]
|
||||
})
|
||||
end
|
||||
command('fclose')
|
||||
|
||||
command('set winborder=double')
|
||||
api.nvim_open_win(buf, true, config)
|
||||
if multigrid then
|
||||
screen:expect({
|
||||
grid = [[
|
||||
## grid 1
|
||||
[2:----------------------------------------]|*6
|
||||
[3:----------------------------------------]|
|
||||
## grid 2
|
||||
|
|
||||
{0:~ }|*5
|
||||
## grid 3
|
||||
|
|
||||
## grid 5
|
||||
{5:╔════╗}|
|
||||
{5:║}{1:^ }{5:║}|
|
||||
{5:║}{2:~ }{5:║}|*3
|
||||
{5:╚════╝}|
|
||||
]], float_pos={
|
||||
[5] = {1002, "NW", 1, 2, 2, true, 50};
|
||||
}, win_viewport={
|
||||
[2] = {win = 1000, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1, sum_scroll_delta = 0};
|
||||
[5] = {win = 1002, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1, sum_scroll_delta = 0};
|
||||
}, win_viewport_margins={
|
||||
[2] = {
|
||||
bottom = 0,
|
||||
left = 0,
|
||||
right = 0,
|
||||
top = 0,
|
||||
win = 1000
|
||||
},
|
||||
[5] = {
|
||||
bottom = 1,
|
||||
left = 1,
|
||||
right = 1,
|
||||
top = 1,
|
||||
win = 1002
|
||||
}
|
||||
}
|
||||
})
|
||||
else
|
||||
screen:expect({
|
||||
grid = [[
|
||||
{5:╔════╗} |
|
||||
{0:~ }{5:║}{1:^ }{5:║}{0: }|
|
||||
{0:~ }{5:║}{2:~ }{5:║}{0: }|*3
|
||||
{0:~ }{5:╚════╝}{0: }|
|
||||
|
|
||||
]]
|
||||
})
|
||||
end
|
||||
command('fclose!')
|
||||
|
||||
command('set winborder=none')
|
||||
api.nvim_buf_set_lines(buf, 0, -1, false, {'none border'})
|
||||
api.nvim_open_win(buf, true, config)
|
||||
if multigrid then
|
||||
screen:expect({
|
||||
grid = [[
|
||||
## grid 1
|
||||
[2:----------------------------------------]|*6
|
||||
[3:----------------------------------------]|
|
||||
## grid 2
|
||||
|
|
||||
{0:~ }|*5
|
||||
## grid 3
|
||||
|
|
||||
## grid 6
|
||||
{1:^none}|
|
||||
{1: bor}|
|
||||
{1:der }|
|
||||
{2:~ }|
|
||||
]],
|
||||
win_pos = {
|
||||
[2] = {
|
||||
height = 6,
|
||||
startcol = 0,
|
||||
startrow = 0,
|
||||
width = 40,
|
||||
win = 1000
|
||||
}
|
||||
},
|
||||
float_pos = {
|
||||
[6] = {1003, "NW", 1, 2, 2, true, 50};
|
||||
},
|
||||
win_viewport = {
|
||||
[2] = {win = 1000, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1, sum_scroll_delta = 0};
|
||||
[6] = {win = 1003, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1, sum_scroll_delta = 0};
|
||||
},
|
||||
win_viewport_margins = {
|
||||
[2] = {
|
||||
bottom = 0,
|
||||
left = 0,
|
||||
right = 0,
|
||||
top = 0,
|
||||
win = 1000
|
||||
},
|
||||
[6] = {
|
||||
bottom = 0,
|
||||
left = 0,
|
||||
right = 0,
|
||||
top = 0,
|
||||
win = 1003
|
||||
}
|
||||
},
|
||||
})
|
||||
else
|
||||
screen:expect([[
|
||||
|
|
||||
{0:~ }|
|
||||
{0:~ }{1:^none}{0: }|
|
||||
{0:~ }{1: bor}{0: }|
|
||||
{0:~ }{1:der }{0: }|
|
||||
{0:~ }{2:~ }{0: }|
|
||||
|
|
||||
]])
|
||||
end
|
||||
command('fclose!')
|
||||
|
||||
-- respect config.border
|
||||
command('set winborder=rounded')
|
||||
config.border = 'single'
|
||||
api.nvim_open_win(buf, false, config)
|
||||
if multigrid then
|
||||
screen:expect({
|
||||
grid = [[
|
||||
## grid 1
|
||||
[2:----------------------------------------]|*6
|
||||
[3:----------------------------------------]|
|
||||
## grid 2
|
||||
^ |
|
||||
{0:~ }|*5
|
||||
## grid 3
|
||||
|
|
||||
## grid 7
|
||||
{5:┌────┐}|
|
||||
{5:│}{1:none}{5:│}|
|
||||
{5:│}{1: bor}{5:│}|
|
||||
{5:│}{1:der }{5:│}|
|
||||
{5:│}{2:~ }{5:│}|
|
||||
{5:└────┘}|
|
||||
]],
|
||||
win_pos = {
|
||||
[2] = {
|
||||
height = 6,
|
||||
startcol = 0,
|
||||
startrow = 0,
|
||||
width = 40,
|
||||
win = 1000
|
||||
}
|
||||
},
|
||||
float_pos = {
|
||||
[7] = {1004, "NW", 1, 2, 2, true, 50};
|
||||
},
|
||||
win_viewport = {
|
||||
[2] = {win = 1000, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1, sum_scroll_delta = 0};
|
||||
[7] = {win = 1004, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1, sum_scroll_delta = 0};
|
||||
},
|
||||
win_viewport_margins = {
|
||||
[2] = {
|
||||
bottom = 0,
|
||||
left = 0,
|
||||
right = 0,
|
||||
top = 0,
|
||||
win = 1000
|
||||
},
|
||||
[7] = {
|
||||
bottom = 1,
|
||||
left = 1,
|
||||
right = 1,
|
||||
top = 1,
|
||||
win = 1004
|
||||
}
|
||||
},
|
||||
})
|
||||
else
|
||||
screen:expect([[
|
||||
^ {5:┌────┐} |
|
||||
{0:~ }{5:│}{1:none}{5:│}{0: }|
|
||||
{0:~ }{5:│}{1: bor}{5:│}{0: }|
|
||||
{0:~ }{5:│}{1:der }{5:│}{0: }|
|
||||
{0:~ }{5:│}{2:~ }{5:│}{0: }|
|
||||
{0:~ }{5:└────┘}{0: }|
|
||||
|
|
||||
]])
|
||||
end
|
||||
|
||||
-- it is currently not supported.
|
||||
eq('Vim(set):E474: Invalid argument: winborder=custom', pcall_err(command, 'set winborder=custom'))
|
||||
end)
|
||||
end
|
||||
|
||||
describe('with ext_multigrid', function()
|
||||
|
@ -75,6 +75,7 @@ let test_values = {
|
||||
\ 'shada': [['', '''50', '"30'], ['xxx']],
|
||||
\ 'termpastefilter': [['BS', 'HT', 'FF', 'ESC', 'DEL', 'C0', 'C1', 'C0,C1'],
|
||||
\ ['xxx', 'C0,C1,xxx']],
|
||||
\ 'winborder': [['rounded', 'none', 'single', 'solid'], ['xxx']],
|
||||
\ 'winhighlight': [['', 'a:b', 'a:', 'a:b,c:d'],
|
||||
\ ['a', ':', ':b', 'a:b:c', 'a:/', '/:b', ',', 'a:b,,', 'a:b,c']],
|
||||
\
|
||||
|
Reference in New Issue
Block a user