aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorglepnir <glephunter@gmail.com>2025-03-19 05:05:35 +0800
committerGitHub <noreply@github.com>2025-03-18 16:05:35 -0500
commit62d9fab9af21323e42828748e6761c02020a7aa5 (patch)
treec92c1fc3e5fd4e25850b5b7f7e0b617c0b322421
parenteefd72fff753e923abf88ac85b1de0859cf24635 (diff)
downloadrneovim-62d9fab9af21323e42828748e6761c02020a7aa5.tar.gz
rneovim-62d9fab9af21323e42828748e6761c02020a7aa5.tar.bz2
rneovim-62d9fab9af21323e42828748e6761c02020a7aa5.zip
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>
-rw-r--r--runtime/doc/api.txt32
-rw-r--r--runtime/doc/news.txt1
-rw-r--r--runtime/doc/options.txt12
-rw-r--r--runtime/lua/vim/_meta/api.lua16
-rw-r--r--runtime/lua/vim/_meta/options.lua13
-rw-r--r--runtime/lua/vim/lsp/util.lua19
-rw-r--r--src/nvim/api/win_config.c31
-rw-r--r--src/nvim/option_vars.h1
-rw-r--r--src/nvim/options.lua20
-rw-r--r--src/nvim/optionstr.c9
-rw-r--r--test/functional/ui/float_spec.lua247
-rw-r--r--test/old/testdir/gen_opt_test.vim1
12 files changed, 340 insertions, 62 deletions
diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt
index dae4df00af..eba4bc9562 100644
--- a/runtime/doc/api.txt
+++ b/runtime/doc/api.txt
@@ -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, >
diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt
index 8c7e400042..a5305e5903 100644
--- a/runtime/doc/news.txt
+++ b/runtime/doc/news.txt
@@ -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
diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt
index 01d5efee4a..ba1d353f40 100644
--- a/runtime/doc/options.txt
+++ b/runtime/doc/options.txt
@@ -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
diff --git a/runtime/lua/vim/_meta/api.lua b/runtime/lua/vim/_meta/api.lua
index 2c1fb260b2..e7ad91132d 100644
--- a/runtime/lua/vim/_meta/api.lua
+++ b/runtime/lua/vim/_meta/api.lua
@@ -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:
--- ```
--- [ "╔", "═" ,"╗", "║", "╝", "═", "╚", "║" ].
--- ```
diff --git a/runtime/lua/vim/_meta/options.lua b/runtime/lua/vim/_meta/options.lua
index 5e138c1a4a..46366d585b 100644
--- a/runtime/lua/vim/_meta/options.lua
+++ b/runtime/lua/vim/_meta/options.lua
@@ -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.
diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua
index 056cb0c73c..d8a6a70ccd 100644
--- a/runtime/lua/vim/lsp/util.lua
+++ b/runtime/lua/vim/lsp/util.lua
@@ -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,
diff --git a/src/nvim/api/win_config.c b/src/nvim/api/win_config.c
index 12bbd79dbd..31de5ca1eb 100644
--- a/src/nvim/api/win_config.c
+++ b/src/nvim/api/win_config.c
@@ -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;
}
diff --git a/src/nvim/option_vars.h b/src/nvim/option_vars.h
index 340a12a32f..a484eb8447 100644
--- a/src/nvim/option_vars.h
+++ b/src/nvim/option_vars.h
@@ -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'
diff --git a/src/nvim/options.lua b/src/nvim/options.lua
index d150303033..3cf60513c4 100644
--- a/src/nvim/options.lua
+++ b/src/nvim/options.lua
@@ -10189,6 +10189,26 @@ local options = {
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',
defaults = {
diff --git a/src/nvim/optionstr.c b/src/nvim/optionstr.c
index 3a6b4c9936..c3cdd6e804 100644
--- a/src/nvim/optionstr.c
+++ b/src/nvim/optionstr.c
@@ -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);
diff --git a/test/functional/ui/float_spec.lua b/test/functional/ui/float_spec.lua
index 6610d25afb..fa4937cec3 100644
--- a/test/functional/ui/float_spec.lua
+++ b/test/functional/ui/float_spec.lua
@@ -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()
diff --git a/test/old/testdir/gen_opt_test.vim b/test/old/testdir/gen_opt_test.vim
index d1d27cf578..84a352a158 100644
--- a/test/old/testdir/gen_opt_test.vim
+++ b/test/old/testdir/gen_opt_test.vim
@@ -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']],
\