diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt index 1fe6594958..56d0b72026 100644 --- a/runtime/doc/news.txt +++ b/runtime/doc/news.txt @@ -229,7 +229,7 @@ OPTIONS • 'diffopt' `inline:` configures diff highlighting for changes within a line. • 'grepformat' is now a |global-local| option. • 'pummaxwidth' sets maximum width for the completion popup menu. -• 'winborder' "bold" style. +• '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. diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 1fd793f436..1871b33e99 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -7410,6 +7410,10 @@ A jump table for the options with a short description can be found at |Q_op|. - "shadow": Drop shadow effect, by blending with the background. - "single": Single-line box. - "solid": Adds padding by a single whitespace cell. + - custom: comma-separated list of exactly 8 characters in clockwise + order starting from topleft. Example: >lua + vim.o.winborder='+,-,+,|,+,-,+,|' +< *'window'* *'wi'* 'window' 'wi' number (default screen height - 1) diff --git a/runtime/lua/vim/_meta/options.lua b/runtime/lua/vim/_meta/options.lua index c529a87811..d5db159855 100644 --- a/runtime/lua/vim/_meta/options.lua +++ b/runtime/lua/vim/_meta/options.lua @@ -8117,8 +8117,15 @@ vim.wo.winbl = vim.wo.winblend --- - "shadow": Drop shadow effect, by blending with the background. --- - "single": Single-line box. --- - "solid": Adds padding by a single whitespace cell. +--- - custom: comma-separated list of exactly 8 characters in clockwise +--- order starting from topleft. Example: --- ---- @type ''|'double'|'single'|'shadow'|'rounded'|'solid'|'bold'|'none' +--- ```lua +--- vim.o.winborder='+,-,+,`,+,-,+,`' +--- ``` +--- +--- +--- @type string vim.o.winborder = "" vim.go.winborder = vim.o.winborder diff --git a/src/nvim/api/win_config.c b/src/nvim/api/win_config.c index 0924e5dd21..f5a2770cc8 100644 --- a/src/nvim/api/win_config.c +++ b/src/nvim/api/win_config.c @@ -1064,6 +1064,52 @@ 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, Error *err) +{ + if (!fconfig) { + return false; + } + Object style = OBJECT_INIT; + + if (strchr(p_winborder, ',')) { + Array border_chars = ARRAY_DICT_INIT; + char *p = p_winborder; + char part[MAX_SCHAR_SIZE] = { 0 }; + int count = 0; + + while (*p != NUL) { + if (count >= 8) { + api_free_array(border_chars); + return false; + } + + size_t part_len = copy_option_part(&p, part, sizeof(part), ","); + if (part_len == 0 || part[0] == NUL) { + api_free_array(border_chars); + return false; + } + + String str = cstr_to_string(part); + ADD(border_chars, STRING_OBJ(str)); + count++; + } + + if (count != 8) { + api_free_array(border_chars); + return false; + } + + style = ARRAY_OBJ(border_chars); + } else { + style = CSTR_TO_OBJ(p_winborder); + } + + parse_border_style(style, fconfig, err); + api_free_object(style); + return !ERROR_SET(err); +} + static bool parse_win_config(win_T *wp, Dict(win_config) *config, WinConfig *fconfig, bool reconf, Error *err) { @@ -1297,14 +1343,15 @@ static bool parse_win_config(win_T *wp, Dict(win_config) *config, WinConfig *fco goto fail; } border_style = config->border; - } else if (*p_winborder != NUL && (wp == NULL || !wp->w_floating)) { - border_style = CSTR_AS_OBJ(p_winborder); - } - if (border_style.type != kObjectTypeNil) { - parse_border_style(border_style, fconfig, err); - if (ERROR_SET(err)) { - goto fail; + if (border_style.type != kObjectTypeNil) { + parse_border_style(border_style, fconfig, err); + if (ERROR_SET(err)) { + goto fail; + } } + } else if (*p_winborder != NUL && (wp == NULL || !wp->w_floating) + && !parse_winborder(fconfig, err)) { + goto fail; } if (HAS_KEY_X(config, style)) { diff --git a/src/nvim/api/win_config.h b/src/nvim/api/win_config.h index 6df8ed13fa..3b18501276 100644 --- a/src/nvim/api/win_config.h +++ b/src/nvim/api/win_config.h @@ -4,6 +4,7 @@ #include "nvim/api/keysets_defs.h" // IWYU pragma: keep #include "nvim/api/private/defs.h" // IWYU pragma: keep +#include "nvim/buffer_defs.h" // IWYU pragma: keep #ifdef INCLUDE_GENERATED_DECLARATIONS # include "api/win_config.h.generated.h" diff --git a/src/nvim/options.lua b/src/nvim/options.lua index 570d0a6d35..0816a2578f 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -10431,6 +10431,9 @@ local options = { type = 'number', }, { + full_name = 'winborder', + scope = { 'global' }, + cb = 'did_set_winborder', defaults = { if_true = '' }, values = { '', 'double', 'single', 'shadow', 'rounded', 'solid', 'bold', 'none' }, desc = [=[ @@ -10443,11 +10446,14 @@ local options = { - "shadow": Drop shadow effect, by blending with the background. - "single": Single-line box. - "solid": Adds padding by a single whitespace cell. + - custom: comma-separated list of exactly 8 characters in clockwise + order starting from topleft. Example: >lua + vim.o.winborder='+,-,+,|,+,-,+,|' + < ]=], - full_name = 'winborder', - scope = { 'global' }, short_desc = N_('border of floating window'), type = 'string', + list = 'onecomma', varname = 'p_winborder', }, { diff --git a/src/nvim/optionstr.c b/src/nvim/optionstr.c index 9d36418693..67505777c8 100644 --- a/src/nvim/optionstr.c +++ b/src/nvim/optionstr.c @@ -4,6 +4,7 @@ #include #include "nvim/api/private/defs.h" +#include "nvim/api/win_config.h" #include "nvim/ascii_defs.h" #include "nvim/autocmd.h" #include "nvim/buffer_defs.h" @@ -2101,6 +2102,19 @@ const char *did_set_winbar(optset_T *args) return did_set_statustabline_rulerformat(args, false, false); } +/// The 'winborder' option is changed. +const char *did_set_winborder(optset_T *args) +{ + WinConfig fconfig = WIN_CONFIG_INIT; + Error err = ERROR_INIT; + if (!parse_winborder(&fconfig, &err)) { + api_clear_error(&err); + return e_invarg; + } + api_clear_error(&err); + return NULL; +} + /// The 'winhighlight' option is changed. const char *did_set_winhighlight(optset_T *args) { diff --git a/test/functional/ui/float_spec.lua b/test/functional/ui/float_spec.lua index 32f6b6dccd..1e6fbf9b9e 100644 --- a/test/functional/ui/float_spec.lua +++ b/test/functional/ui/float_spec.lua @@ -10938,7 +10938,16 @@ describe('float window', function() winid = api.nvim_open_win(buf, false, config) eq('┏', api.nvim_win_get_config(winid).border[1]) - -- it is currently not supported. + command([[set winborder=+,-,+,\|,+,-,+,\|]]) + winid = api.nvim_open_win(buf, false, config) + eq('+', api.nvim_win_get_config(winid).border[1]) + + command([[set winborder=●,○,●,○,●,○,●,○]]) + winid = api.nvim_open_win(buf, false, config) + eq('●', api.nvim_win_get_config(winid).border[1]) + + eq('Vim(set):E474: Invalid argument: winborder=,,', pcall_err(command, 'set winborder=,,')) + eq('Vim(set):E474: Invalid argument: winborder=+,-,+,|,+,-,+,', pcall_err(command, [[set winborder=+,-,+,\|,+,-,+,]])) eq('Vim(set):E474: Invalid argument: winborder=custom', pcall_err(command, 'set winborder=custom')) end) end