From 35570e4a11bef061777d741929f74fa66ba3f45a Mon Sep 17 00:00:00 2001 From: Evgeni Chasnovski Date: Fri, 25 Aug 2023 10:53:35 +0300 Subject: [PATCH] feat(float): implement footer Problem: Now way to show text at the bottom part of floating window border (a.k.a. "footer"). Solution: Allows `footer` and `footer_pos` config fields similar to `title` and `title_pos`. --- runtime/doc/api.txt | 17 +- runtime/doc/news.txt | 3 + runtime/lua/vim/_meta/api.lua | 17 +- runtime/lua/vim/_meta/api_keysets.lua | 2 + src/nvim/api/keysets.h | 2 + src/nvim/api/win_config.c | 83 +++++- src/nvim/buffer_defs.h | 7 +- src/nvim/drawscreen.c | 7 + src/nvim/window.c | 3 +- test/functional/ui/float_spec.lua | 395 ++++++++++++++++++++++++++ 10 files changed, 514 insertions(+), 22 deletions(-) diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt index 343c63f4b0..53d4282ec5 100644 --- a/runtime/doc/api.txt +++ b/runtime/doc/api.txt @@ -3138,11 +3138,18 @@ nvim_open_win({buffer}, {enter}, {*config}) *nvim_open_win()* specified by character: [ ["+", "MyCorner"], ["x", "MyBorder"] ]. - • title: Title (optional) in window border, String or list. - List is [text, highlight] tuples. if is string the default - highlight group is `FloatTitle`. - • title_pos: Title position must set with title option. - value can be of `left` `center` `right` default is left. + • title: Title (optional) in window border, string or list. + List should consist of `[text, highlight]` tuples. If + string, the default highlight group is `FloatTitle`. + • title_pos: Title position. Must be set with `title` + option. Value can be one of "left", "center", or "right". + Default is `"left"`. + • footer: Footer (optional) in window border, string or + list. List should consist of `[text, highlight]` tuples. + If string, the default highlight group is `FloatTitle`. + • footer_pos: Footer position. Must be set with `footer` + option. Value can be one of "left", "center", or "right". + Default is `"left"`. • noautocmd: If true then no buffer-related autocommand events such as |BufEnter|, |BufLeave| or |BufWinEnter| may fire from calling this function. diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt index 56bdd07171..c28ccb191e 100644 --- a/runtime/doc/news.txt +++ b/runtime/doc/news.txt @@ -155,6 +155,9 @@ The following new APIs and features were added. • New RPC client type `msgpack-rpc` is added for `nvim_set_client_info` to support fully MessagePack-RPC compliant clients. +• Floating windows can now show footer with new `footer` and `footer_pos` + config fields. + ============================================================================== CHANGED FEATURES *news-changed* diff --git a/runtime/lua/vim/_meta/api.lua b/runtime/lua/vim/_meta/api.lua index fdf5016b68..7704a60cf8 100644 --- a/runtime/lua/vim/_meta/api.lua +++ b/runtime/lua/vim/_meta/api.lua @@ -1551,11 +1551,18 @@ function vim.api.nvim_open_term(buffer, opts) end --- specified by character: [ ["+", "MyCorner"], ["x", --- "MyBorder"] ]. --- ---- • title: Title (optional) in window border, String or list. ---- List is [text, highlight] tuples. if is string the default ---- highlight group is `FloatTitle`. ---- • title_pos: Title position must set with title option. ---- value can be of `left` `center` `right` default is left. +--- • title: Title (optional) in window border, string or list. +--- List should consist of `[text, highlight]` tuples. If +--- string, the default highlight group is `FloatTitle`. +--- • title_pos: Title position. Must be set with `title` +--- option. Value can be one of "left", "center", or "right". +--- Default is `"left"`. +--- • footer: Footer (optional) in window border, string or +--- list. List should consist of `[text, highlight]` tuples. +--- If string, the default highlight group is `FloatTitle`. +--- • footer_pos: Footer position. Must be set with `footer` +--- option. Value can be one of "left", "center", or "right". +--- Default is `"left"`. --- • noautocmd: If true then no buffer-related autocommand --- events such as `BufEnter`, `BufLeave` or `BufWinEnter` may --- fire from calling this function. diff --git a/runtime/lua/vim/_meta/api_keysets.lua b/runtime/lua/vim/_meta/api_keysets.lua index 941780202e..08c29ebe7a 100644 --- a/runtime/lua/vim/_meta/api_keysets.lua +++ b/runtime/lua/vim/_meta/api_keysets.lua @@ -108,6 +108,8 @@ error('Cannot require a meta file') --- @field border? any --- @field title? any --- @field title_pos? string +--- @field footer? any +--- @field footer_pos? string --- @field style? string --- @field noautocmd? boolean diff --git a/src/nvim/api/keysets.h b/src/nvim/api/keysets.h index a47e278cad..1f5c7069a9 100644 --- a/src/nvim/api/keysets.h +++ b/src/nvim/api/keysets.h @@ -99,6 +99,8 @@ typedef struct { Object border; Object title; String title_pos; + Object footer; + String footer_pos; String style; Boolean noautocmd; } Dict(float_config); diff --git a/src/nvim/api/win_config.c b/src/nvim/api/win_config.c index 9473803652..325d0cbfa0 100644 --- a/src/nvim/api/win_config.c +++ b/src/nvim/api/win_config.c @@ -145,11 +145,18 @@ /// By default, `FloatBorder` highlight is used, which links to `WinSeparator` /// when not defined. It could also be specified by character: /// [ ["+", "MyCorner"], ["x", "MyBorder"] ]. -/// - title: Title (optional) in window border, String or list. -/// List is [text, highlight] tuples. if is string the default -/// highlight group is `FloatTitle`. -/// - title_pos: Title position must set with title option. -/// value can be of `left` `center` `right` default is left. +/// - title: Title (optional) in window border, string or list. +/// List should consist of `[text, highlight]` tuples. +/// If string, the default highlight group is `FloatTitle`. +/// - title_pos: Title position. Must be set with `title` option. +/// Value can be one of "left", "center", or "right". +/// Default is `"left"`. +/// - footer: Footer (optional) in window border, string or list. +/// List should consist of `[text, highlight]` tuples. +/// If string, the default highlight group is `FloatTitle`. +/// - footer_pos: Footer position. Must be set with `footer` option. +/// Value can be one of "left", "center", or "right". +/// Default is `"left"`. /// - noautocmd: If true then no buffer-related autocommand events such as /// |BufEnter|, |BufLeave| or |BufWinEnter| may fire from /// calling this function. @@ -247,11 +254,19 @@ Dictionary config_put_bordertext(Dictionary config, FloatConfig *fconfig, AlignTextPos align; char *field_name; char *field_pos_name; - if (bordertext_type == kBorderTextTitle) { + switch (bordertext_type) { + case kBorderTextTitle: chunks = fconfig->title_chunks; align = fconfig->title_pos; field_name = "title"; field_pos_name = "title_pos"; + break; + case kBorderTextFooter: + chunks = fconfig->footer_chunks; + align = fconfig->footer_pos; + field_name = "footer"; + field_pos_name = "footer_pos"; + break; } Array bordertext = ARRAY_DICT_INIT; @@ -345,6 +360,9 @@ Dictionary nvim_win_get_config(Window window, Error *err) if (config->title) { rv = config_put_bordertext(rv, config, kBorderTextTitle); } + if (config->footer) { + rv = config_put_bordertext(rv, config, kBorderTextFooter); + } } } @@ -410,10 +428,17 @@ static void parse_bordertext(Object bordertext, BorderTextType bordertext_type, bool *is_present; VirtText *chunks; int *width; - if (bordertext_type == kBorderTextTitle) { + switch (bordertext_type) { + case kBorderTextTitle: is_present = &fconfig->title; chunks = &fconfig->title_chunks; width = &fconfig->title_width; + break; + case kBorderTextFooter: + is_present = &fconfig->footer; + chunks = &fconfig->footer_chunks; + width = &fconfig->footer_width; + break; } if (bordertext.type == kObjectTypeString) { @@ -449,8 +474,13 @@ static bool parse_bordertext_pos(String bordertext_pos, BorderTextType bordertex FloatConfig *fconfig, Error *err) { AlignTextPos *align; - if (bordertext_type == kBorderTextTitle) { + switch (bordertext_type) { + case kBorderTextTitle: align = &fconfig->title_pos; + break; + case kBorderTextFooter: + align = &fconfig->footer_pos; + break; } if (bordertext_pos.size == 0) { @@ -467,8 +497,13 @@ static bool parse_bordertext_pos(String bordertext_pos, BorderTextType bordertex } else if (strequal(pos, "right")) { *align = kAlignRight; } else { - if (bordertext_type == kBorderTextTitle) { + switch (bordertext_type) { + case kBorderTextTitle: api_set_error(err, kErrorTypeValidation, "invalid title_pos value"); + break; + case kBorderTextFooter: + api_set_error(err, kErrorTypeValidation, "invalid footer_pos value"); + break; } return false; } @@ -559,8 +594,9 @@ static void parse_border_style(Object style, FloatConfig *fconfig, Error *err) String str = style.data.string; if (str.size == 0 || strequal(str.data, "none")) { fconfig->border = false; - // title does not work with border equal none + // border text does not work with border equal none fconfig->title = false; + fconfig->footer = false; return; } for (size_t i = 0; defaults[i].name; i++) { @@ -750,6 +786,33 @@ static bool parse_float_config(Dict(float_config) *config, FloatConfig *fconfig, } } + if (HAS_KEY_X(config, footer)) { + // footer only work with border + if (!HAS_KEY_X(config, border) && !fconfig->border) { + api_set_error(err, kErrorTypeException, "footer requires border to be set"); + return false; + } + + if (fconfig->footer) { + clear_virttext(&fconfig->footer_chunks); + } + + parse_bordertext(config->footer, kBorderTextFooter, fconfig, err); + if (ERROR_SET(err)) { + return false; + } + + // handles unset 'footer_pos' same as empty string + if (!parse_bordertext_pos(config->footer_pos, kBorderTextFooter, fconfig, err)) { + return false; + } + } else { + if (HAS_KEY_X(config, footer_pos)) { + api_set_error(err, kErrorTypeException, "footer_pos requires footer to be set"); + return false; + } + } + if (HAS_KEY_X(config, border)) { parse_border_style(config->border, fconfig, err); if (ERROR_SET(err)) { diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index ba6a3a1e27..3f55dbbc00 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -938,6 +938,7 @@ typedef enum { typedef enum { kBorderTextTitle = 0, + kBorderTextFooter = 1, } BorderTextType; typedef struct { @@ -952,14 +953,18 @@ typedef struct { int zindex; WinStyle style; bool border; - bool title; bool shadow; schar_T border_chars[8]; int border_hl_ids[8]; int border_attr[8]; + bool title; AlignTextPos title_pos; VirtText title_chunks; int title_width; + bool footer; + AlignTextPos footer_pos; + VirtText footer_chunks; + int footer_width; bool noautocmd; } FloatConfig; diff --git a/src/nvim/drawscreen.c b/src/nvim/drawscreen.c index 4b23a9f1eb..f71a47a596 100644 --- a/src/nvim/drawscreen.c +++ b/src/nvim/drawscreen.c @@ -782,10 +782,17 @@ static void win_redr_border(win_T *wp) if (adj[3]) { grid_put_schar(grid, irow + adj[0], 0, chars[6], attrs[6]); } + for (int i = 0; i < icol; i++) { int ic = (i == 0 && !adj[3] && chars[6][0]) ? 6 : 5; grid_put_schar(grid, irow + adj[0], i + adj[3], chars[ic], attrs[ic]); } + + if (wp->w_float_config.footer) { + int footer_col = win_get_bordertext_col(icol, wp->w_float_config.footer_width, + wp->w_float_config.footer_pos); + win_redr_bordertext(wp, grid, wp->w_float_config.footer_chunks, grid->rows - 1, footer_col); + } if (adj[1]) { grid_put_schar(grid, irow + adj[0], icol + adj[3], chars[4], attrs[4]); } diff --git a/src/nvim/window.c b/src/nvim/window.c index 970027b2f8..e72c32700d 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -5239,8 +5239,9 @@ static void win_free(win_T *wp, tabpage_T *tp) } } - // free the border title text + // free the border text clear_virttext(&wp->w_float_config.title_chunks); + clear_virttext(&wp->w_float_config.footer_chunks); clear_matches(wp); diff --git a/test/functional/ui/float_spec.lua b/test/functional/ui/float_spec.lua index 85795e7e17..6cdd6e51fa 100644 --- a/test/functional/ui/float_spec.lua +++ b/test/functional/ui/float_spec.lua @@ -1851,6 +1851,39 @@ describe('float window', function() eq('center', title_pos) end) + it('validates footer footer_pos', function() + local buf = meths.create_buf(false,false) + eq("footer requires border to be set", + pcall_err(meths.open_win,buf, false, { + relative='editor', width=9, height=2, row=2, col=5, footer='Footer', + })) + eq("footer_pos requires footer to be set", + pcall_err(meths.open_win,buf, false, { + relative='editor', width=9, height=2, row=2, col=5, + border='single', footer_pos='left', + })) + end) + + it('validate footer_pos in nvim_win_get_config', function() + local footer_pos = exec_lua([[ + local bufnr = vim.api.nvim_create_buf(false, false) + local opts = { + relative = 'editor', + col = 2, + row = 5, + height = 2, + width = 9, + border = 'double', + footer = 'Test', + footer_pos = 'center' + } + + local win_id = vim.api.nvim_open_win(bufnr, true, opts) + return vim.api.nvim_win_get_config(win_id).footer_pos + ]]) + + eq('center', footer_pos) + end) it('border with title', function() local buf = meths.create_buf(false, false) @@ -2033,6 +2066,368 @@ describe('float window', function() end end) + it('border with footer', function() + local buf = meths.create_buf(false, false) + meths.buf_set_lines(buf, 0, -1, true, {' halloj! ', + ' BORDAA '}) + local win = meths.open_win(buf, false, { + relative='editor', width=9, height=2, row=2, col=5, border="double", + footer = "Left",footer_pos = "left", + }) + + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [3:----------------------------------------]| + ## grid 2 + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + | + ## grid 4 + {5:╔═════════╗}| + {5:║}{1: halloj! }{5:║}| + {5:║}{1: BORDAA }{5:║}| + {5:╚}{11:Left}{5:═════╝}| + ]], float_pos={ + [4] = { { id = 1001 }, "NW", 1, 2, 5, true } + }, win_viewport={ + [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1, sum_scroll_delta = 0}; + [4] = {win = {id = 1001}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 2, sum_scroll_delta = 0}; + }} + else + screen:expect{grid=[[ + ^ | + {0:~ }| + {0:~ }{5:╔═════════╗}{0: }| + {0:~ }{5:║}{1: halloj! }{5:║}{0: }| + {0:~ }{5:║}{1: BORDAA }{5:║}{0: }| + {0:~ }{5:╚}{11:Left}{5:═════╝}{0: }| + | + ]]} + end + + meths.win_set_config(win, {footer= "Center",footer_pos="center"}) + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [3:----------------------------------------]| + ## grid 2 + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + | + ## grid 4 + {5:╔═════════╗}| + {5:║}{1: halloj! }{5:║}| + {5:║}{1: BORDAA }{5:║}| + {5:╚═}{11:Center}{5:══╝}| + ]], float_pos={ + [4] = { { id = 1001 }, "NW", 1, 2, 5, true } + }, win_viewport={ + [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1, sum_scroll_delta = 0}; + [4] = {win = {id = 1001}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 2, sum_scroll_delta = 0}; + }} + else + screen:expect{grid=[[ + ^ | + {0:~ }| + {0:~ }{5:╔═════════╗}{0: }| + {0:~ }{5:║}{1: halloj! }{5:║}{0: }| + {0:~ }{5:║}{1: BORDAA }{5:║}{0: }| + {0:~ }{5:╚═}{11:Center}{5:══╝}{0: }| + | + ]]} + end + + meths.win_set_config(win, {footer= "Right",footer_pos="right"}) + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [3:----------------------------------------]| + ## grid 2 + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + | + ## grid 4 + {5:╔═════════╗}| + {5:║}{1: halloj! }{5:║}| + {5:║}{1: BORDAA }{5:║}| + {5:╚════}{11:Right}{5:╝}| + ]], float_pos={ + [4] = { { id = 1001 }, "NW", 1, 2, 5, true } + }, win_viewport={ + [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1, sum_scroll_delta = 0}; + [4] = {win = {id = 1001}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 2, sum_scroll_delta = 0}; + }} + else + screen:expect{grid=[[ + ^ | + {0:~ }| + {0:~ }{5:╔═════════╗}{0: }| + {0:~ }{5:║}{1: halloj! }{5:║}{0: }| + {0:~ }{5:║}{1: BORDAA }{5:║}{0: }| + {0:~ }{5:╚════}{11:Right}{5:╝}{0: }| + | + ]]} + end + + meths.win_set_config(win, {footer= { {"🦄"},{"BB"}},footer_pos="right"}) + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [3:----------------------------------------]| + ## grid 2 + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + | + ## grid 4 + {5:╔═════════╗}| + {5:║}{1: halloj! }{5:║}| + {5:║}{1: BORDAA }{5:║}| + {5:╚═════}🦄BB{5:╝}| + ]], float_pos={ + [4] = { { id = 1001 }, "NW", 1, 2, 5, true } + }, win_viewport={ + [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1, sum_scroll_delta = 0}; + [4] = {win = {id = 1001}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 2, sum_scroll_delta = 0}; + }} + else + screen:expect{grid=[[ + ^ | + {0:~ }| + {0:~ }{5:╔═════════╗}{0: }| + {0:~ }{5:║}{1: halloj! }{5:║}{0: }| + {0:~ }{5:║}{1: BORDAA }{5:║}{0: }| + {0:~ }{5:╚═════}🦄BB{5:╝}{0: }| + | + ]]} + end + end) + + it('border with title and footer', function() + local buf = meths.create_buf(false, false) + meths.buf_set_lines(buf, 0, -1, true, {' halloj! ', + ' BORDAA '}) + local win = meths.open_win(buf, false, { + relative='editor', width=9, height=2, row=2, col=5, border="double", + title = "Left", title_pos = "left", footer = "Right", footer_pos = "right", + }) + + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [3:----------------------------------------]| + ## grid 2 + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + | + ## grid 4 + {5:╔}{11:Left}{5:═════╗}| + {5:║}{1: halloj! }{5:║}| + {5:║}{1: BORDAA }{5:║}| + {5:╚════}{11:Right}{5:╝}| + ]], float_pos={ + [4] = { { id = 1001 }, "NW", 1, 2, 5, true } + }, win_viewport={ + [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1, sum_scroll_delta = 0}; + [4] = {win = {id = 1001}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 2, sum_scroll_delta = 0}; + }} + else + screen:expect{grid=[[ + ^ | + {0:~ }| + {0:~ }{5:╔}{11:Left}{5:═════╗}{0: }| + {0:~ }{5:║}{1: halloj! }{5:║}{0: }| + {0:~ }{5:║}{1: BORDAA }{5:║}{0: }| + {0:~ }{5:╚════}{11:Right}{5:╝}{0: }| + | + ]]} + end + + meths.win_set_config(win, {title= "Center",title_pos="center",footer= "Center",footer_pos="center"}) + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [3:----------------------------------------]| + ## grid 2 + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + | + ## grid 4 + {5:╔═}{11:Center}{5:══╗}| + {5:║}{1: halloj! }{5:║}| + {5:║}{1: BORDAA }{5:║}| + {5:╚═}{11:Center}{5:══╝}| + ]], float_pos={ + [4] = { { id = 1001 }, "NW", 1, 2, 5, true } + }, win_viewport={ + [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1, sum_scroll_delta = 0}; + [4] = {win = {id = 1001}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 2, sum_scroll_delta = 0}; + }} + else + screen:expect{grid=[[ + ^ | + {0:~ }| + {0:~ }{5:╔═}{11:Center}{5:══╗}{0: }| + {0:~ }{5:║}{1: halloj! }{5:║}{0: }| + {0:~ }{5:║}{1: BORDAA }{5:║}{0: }| + {0:~ }{5:╚═}{11:Center}{5:══╝}{0: }| + | + ]]} + end + + meths.win_set_config(win, {title= "Right",title_pos="right",footer= "Left",footer_pos="left"}) + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [3:----------------------------------------]| + ## grid 2 + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + | + ## grid 4 + {5:╔════}{11:Right}{5:╗}| + {5:║}{1: halloj! }{5:║}| + {5:║}{1: BORDAA }{5:║}| + {5:╚}{11:Left}{5:═════╝}| + ]], float_pos={ + [4] = { { id = 1001 }, "NW", 1, 2, 5, true } + }, win_viewport={ + [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1, sum_scroll_delta = 0}; + [4] = {win = {id = 1001}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 2, sum_scroll_delta = 0}; + }} + else + screen:expect{grid=[[ + ^ | + {0:~ }| + {0:~ }{5:╔════}{11:Right}{5:╗}{0: }| + {0:~ }{5:║}{1: halloj! }{5:║}{0: }| + {0:~ }{5:║}{1: BORDAA }{5:║}{0: }| + {0:~ }{5:╚}{11:Left}{5:═════╝}{0: }| + | + ]]} + end + + meths.win_set_config(win, {title= { {"🦄"},{"BB"}},title_pos="right",footer= { {"🦄"},{"BB"}},footer_pos="right"}) + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [3:----------------------------------------]| + ## grid 2 + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + | + ## grid 4 + {5:╔═════}🦄BB{5:╗}| + {5:║}{1: halloj! }{5:║}| + {5:║}{1: BORDAA }{5:║}| + {5:╚═════}🦄BB{5:╝}| + ]], float_pos={ + [4] = { { id = 1001 }, "NW", 1, 2, 5, true } + }, win_viewport={ + [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1, sum_scroll_delta = 0}; + [4] = {win = {id = 1001}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 2, sum_scroll_delta = 0}; + }} + else + screen:expect{grid=[[ + ^ | + {0:~ }| + {0:~ }{5:╔═════}🦄BB{5:╗}{0: }| + {0:~ }{5:║}{1: halloj! }{5:║}{0: }| + {0:~ }{5:║}{1: BORDAA }{5:║}{0: }| + {0:~ }{5:╚═════}🦄BB{5:╝}{0: }| + | + ]]} + end + end) + it('terminates border on edge of viewport when window extends past viewport', function() local buf = meths.create_buf(false, false) meths.open_win(buf, false, {relative='editor', width=40, height=7, row=0, col=0, border="single", zindex=201})