diff options
author | bfredl <bjorn.linse@gmail.com> | 2023-08-26 19:41:52 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-08-26 19:41:52 +0200 |
commit | a6d0535c15374599e0dd9ca891d9fbc7b5774403 (patch) | |
tree | d0915f863f81409785b2f51ac91741b3e14258e9 /src | |
parent | afd0c648a89ff88c9bff1b24c37e139813ec13c9 (diff) | |
parent | 986bf7e78d09286e198b696630254eb097ad0875 (diff) | |
download | rneovim-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.h | 2 | ||||
-rw-r--r-- | src/nvim/api/win_config.c | 206 | ||||
-rw-r--r-- | src/nvim/api/win_config.h | 1 | ||||
-rw-r--r-- | src/nvim/buffer_defs.h | 11 | ||||
-rw-r--r-- | src/nvim/drawscreen.c | 47 | ||||
-rw-r--r-- | src/nvim/highlight_group.c | 1 | ||||
-rw-r--r-- | src/nvim/window.c | 3 |
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); |