aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorbfredl <bjorn.linse@gmail.com>2023-08-26 19:41:52 +0200
committerGitHub <noreply@github.com>2023-08-26 19:41:52 +0200
commita6d0535c15374599e0dd9ca891d9fbc7b5774403 (patch)
treed0915f863f81409785b2f51ac91741b3e14258e9 /src
parentafd0c648a89ff88c9bff1b24c37e139813ec13c9 (diff)
parent986bf7e78d09286e198b696630254eb097ad0875 (diff)
downloadrneovim-a6d0535c15374599e0dd9ca891d9fbc7b5774403.tar.gz
rneovim-a6d0535c15374599e0dd9ca891d9fbc7b5774403.tar.bz2
rneovim-a6d0535c15374599e0dd9ca891d9fbc7b5774403.zip
Merge pull request #24739 from echasnovski/win-footer
feature: add floating window footer text
Diffstat (limited to 'src')
-rw-r--r--src/nvim/api/keysets.h2
-rw-r--r--src/nvim/api/win_config.c206
-rw-r--r--src/nvim/api/win_config.h1
-rw-r--r--src/nvim/buffer_defs.h11
-rw-r--r--src/nvim/drawscreen.c47
-rw-r--r--src/nvim/highlight_group.c1
-rw-r--r--src/nvim/window.c3
7 files changed, 201 insertions, 70 deletions
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 b666d1efa2..ac27554172 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 `FloatFooter`.
+/// - 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.
@@ -240,6 +247,56 @@ void nvim_win_set_config(Window window, Dict(float_config) *config, Error *err)
}
}
+Dictionary config_put_bordertext(Dictionary config, FloatConfig *fconfig,
+ BorderTextType bordertext_type)
+{
+ VirtText chunks;
+ AlignTextPos align;
+ char *field_name;
+ char *field_pos_name;
+ 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;
+ for (size_t i = 0; i < chunks.size; i++) {
+ Array tuple = ARRAY_DICT_INIT;
+ ADD(tuple, CSTR_TO_OBJ(chunks.items[i].text));
+ if (chunks.items[i].hl_id > 0) {
+ ADD(tuple, CSTR_TO_OBJ(syn_id2name(chunks.items[i].hl_id)));
+ }
+ ADD(bordertext, ARRAY_OBJ(tuple));
+ }
+ PUT(config, field_name, ARRAY_OBJ(bordertext));
+
+ char *pos;
+ switch (align) {
+ case kAlignLeft:
+ pos = "left";
+ break;
+ case kAlignCenter:
+ pos = "center";
+ break;
+ case kAlignRight:
+ pos = "right";
+ break;
+ }
+ PUT(config, field_pos_name, CSTR_TO_OBJ(pos));
+
+ return config;
+}
+
/// Gets window configuration.
///
/// The returned value may be given to |nvim_open_win()|.
@@ -301,26 +358,10 @@ Dictionary nvim_win_get_config(Window window, Error *err)
}
PUT(rv, "border", ARRAY_OBJ(border));
if (config->title) {
- Array titles = ARRAY_DICT_INIT;
- VirtText title_datas = config->title_chunks;
- for (size_t i = 0; i < title_datas.size; i++) {
- Array tuple = ARRAY_DICT_INIT;
- ADD(tuple, CSTR_TO_OBJ(title_datas.items[i].text));
- if (title_datas.items[i].hl_id > 0) {
- ADD(tuple, CSTR_TO_OBJ(syn_id2name(title_datas.items[i].hl_id)));
- }
- ADD(titles, ARRAY_OBJ(tuple));
- }
- PUT(rv, "title", ARRAY_OBJ(titles));
- char *title_pos;
- if (config->title_pos == kAlignLeft) {
- title_pos = "left";
- } else if (config->title_pos == kAlignCenter) {
- title_pos = "center";
- } else {
- title_pos = "right";
- }
- PUT(rv, "title_pos", CSTR_TO_OBJ(title_pos));
+ rv = config_put_bordertext(rv, config, kBorderTextTitle);
+ }
+ if (config->footer) {
+ rv = config_put_bordertext(rv, config, kBorderTextFooter);
}
}
}
@@ -381,54 +422,91 @@ static bool parse_float_bufpos(Array bufpos, lpos_T *out)
return true;
}
-static void parse_border_title(Object title, FloatConfig *fconfig, Error *err)
+static void parse_bordertext(Object bordertext, BorderTextType bordertext_type,
+ FloatConfig *fconfig, Error *err)
{
- if (title.type == kObjectTypeString) {
- if (title.data.string.size == 0) {
- fconfig->title = false;
+ bool *is_present;
+ VirtText *chunks;
+ int *width;
+ int default_hl_id;
+ switch (bordertext_type) {
+ case kBorderTextTitle:
+ is_present = &fconfig->title;
+ chunks = &fconfig->title_chunks;
+ width = &fconfig->title_width;
+ default_hl_id = syn_check_group(S_LEN("FloatTitle"));
+ break;
+ case kBorderTextFooter:
+ is_present = &fconfig->footer;
+ chunks = &fconfig->footer_chunks;
+ width = &fconfig->footer_width;
+ default_hl_id = syn_check_group(S_LEN("FloatFooter"));
+ break;
+ }
+
+ if (bordertext.type == kObjectTypeString) {
+ if (bordertext.data.string.size == 0) {
+ *is_present = false;
return;
}
- int hl_id = syn_check_group(S_LEN("FloatTitle"));
- kv_push(fconfig->title_chunks, ((VirtTextChunk){ .text = xstrdup(title.data.string.data),
- .hl_id = hl_id }));
- fconfig->title_width = (int)mb_string2cells(title.data.string.data);
- fconfig->title = true;
+ kv_push(*chunks, ((VirtTextChunk){ .text = xstrdup(bordertext.data.string.data),
+ .hl_id = default_hl_id }));
+ *width = (int)mb_string2cells(bordertext.data.string.data);
+ *is_present = true;
return;
}
- if (title.type != kObjectTypeArray) {
+ if (bordertext.type != kObjectTypeArray) {
api_set_error(err, kErrorTypeValidation, "title must be string or array");
return;
}
- if (title.data.array.size == 0) {
+ if (bordertext.data.array.size == 0) {
api_set_error(err, kErrorTypeValidation, "title cannot be an empty array");
return;
}
- fconfig->title_width = 0;
- fconfig->title_chunks = parse_virt_text(title.data.array, err, &fconfig->title_width);
+ *width = 0;
+ *chunks = parse_virt_text(bordertext.data.array, err, width);
- fconfig->title = true;
+ *is_present = true;
}
-static bool parse_title_pos(String title_pos, FloatConfig *fconfig, Error *err)
+static bool parse_bordertext_pos(String bordertext_pos, BorderTextType bordertext_type,
+ FloatConfig *fconfig, Error *err)
{
- if (title_pos.size == 0) {
- fconfig->title_pos = kAlignLeft;
+ AlignTextPos *align;
+ switch (bordertext_type) {
+ case kBorderTextTitle:
+ align = &fconfig->title_pos;
+ break;
+ case kBorderTextFooter:
+ align = &fconfig->footer_pos;
+ break;
+ }
+
+ if (bordertext_pos.size == 0) {
+ *align = kAlignLeft;
return true;
}
- char *pos = title_pos.data;
+ char *pos = bordertext_pos.data;
if (strequal(pos, "left")) {
- fconfig->title_pos = kAlignLeft;
+ *align = kAlignLeft;
} else if (strequal(pos, "center")) {
- fconfig->title_pos = kAlignCenter;
+ *align = kAlignCenter;
} else if (strequal(pos, "right")) {
- fconfig->title_pos = kAlignRight;
+ *align = kAlignRight;
} else {
- api_set_error(err, kErrorTypeValidation, "invalid title_pos value");
+ 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;
}
return true;
@@ -518,8 +596,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++) {
@@ -693,13 +772,13 @@ static bool parse_float_config(Dict(float_config) *config, FloatConfig *fconfig,
clear_virttext(&fconfig->title_chunks);
}
- parse_border_title(config->title, fconfig, err);
+ parse_bordertext(config->title, kBorderTextTitle, fconfig, err);
if (ERROR_SET(err)) {
return false;
}
// handles unset 'title_pos' same as empty string
- if (!parse_title_pos(config->title_pos, fconfig, err)) {
+ if (!parse_bordertext_pos(config->title_pos, kBorderTextTitle, fconfig, err)) {
return false;
}
} else {
@@ -709,6 +788,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/api/win_config.h b/src/nvim/api/win_config.h
index a4614f02ce..426a74fb3e 100644
--- a/src/nvim/api/win_config.h
+++ b/src/nvim/api/win_config.h
@@ -3,6 +3,7 @@
#include "nvim/api/keysets.h"
#include "nvim/api/private/defs.h"
+#include "nvim/buffer_defs.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "api/win_config.h.generated.h"
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index 2d83242275..3f55dbbc00 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -936,6 +936,11 @@ typedef enum {
kAlignRight = 2,
} AlignTextPos;
+typedef enum {
+ kBorderTextTitle = 0,
+ kBorderTextFooter = 1,
+} BorderTextType;
+
typedef struct {
Window window;
lpos_T bufpos;
@@ -948,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 bd177884fb..f71a47a596 100644
--- a/src/nvim/drawscreen.c
+++ b/src/nvim/drawscreen.c
@@ -701,20 +701,32 @@ void end_search_hl(void)
screen_search_hl.rm.regprog = NULL;
}
-static void win_border_redr_title(win_T *wp, ScreenGrid *grid, int col)
+static void win_redr_bordertext(win_T *wp, ScreenGrid *grid, VirtText text_chunks, int row, int col)
{
- VirtText title_chunks = wp->w_float_config.title_chunks;
-
- for (size_t i = 0; i < title_chunks.size; i++) {
- char *text = title_chunks.items[i].text;
+ for (size_t i = 0; i < text_chunks.size; i++) {
+ char *text = text_chunks.items[i].text;
int cell = (int)mb_string2cells(text);
- int hl_id = title_chunks.items[i].hl_id;
+ int hl_id = text_chunks.items[i].hl_id;
int attr = hl_id ? syn_id2attr(hl_id) : 0;
- grid_puts(grid, text, 0, col, attr);
+ grid_puts(grid, text, row, col, attr);
col += cell;
}
}
+int win_get_bordertext_col(int total_col, int text_width, AlignTextPos align)
+{
+ switch (align) {
+ case kAlignLeft:
+ return 1;
+ case kAlignCenter:
+ return (total_col - text_width) / 2 + 1;
+ case kAlignRight:
+ return total_col - text_width + 1;
+ default:
+ abort();
+ }
+}
+
static void win_redr_border(win_T *wp)
{
wp->w_redr_border = false;
@@ -741,17 +753,9 @@ static void win_redr_border(win_T *wp)
}
if (wp->w_float_config.title) {
- int title_col = 0;
- int title_width = wp->w_float_config.title_width;
- AlignTextPos title_pos = wp->w_float_config.title_pos;
-
- if (title_pos == kAlignCenter) {
- title_col = (icol - title_width) / 2 + 1;
- } else {
- title_col = title_pos == kAlignLeft ? 1 : icol - title_width + 1;
- }
-
- win_border_redr_title(wp, grid, title_col);
+ int title_col = win_get_bordertext_col(icol, wp->w_float_config.title_width,
+ wp->w_float_config.title_pos);
+ win_redr_bordertext(wp, grid, wp->w_float_config.title_chunks, 0, title_col);
}
if (adj[1]) {
grid_put_schar(grid, 0, icol + adj[3], chars[2], attrs[2]);
@@ -778,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/highlight_group.c b/src/nvim/highlight_group.c
index 2b268ff2e1..c4d140d1e1 100644
--- a/src/nvim/highlight_group.c
+++ b/src/nvim/highlight_group.c
@@ -173,6 +173,7 @@ static const char *highlight_init_both[] = {
"default link NormalFloat Pmenu",
"default link FloatBorder WinSeparator",
"default link FloatTitle Title",
+ "default link FloatFooter Title",
"default FloatShadow blend=80 guibg=Black",
"default FloatShadowThrough blend=100 guibg=Black",
"RedrawDebugNormal cterm=reverse gui=reverse",
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);