diff options
-rw-r--r-- | src/nvim/api/keysets.lua | 2 | ||||
-rw-r--r-- | src/nvim/api/win_config.c | 103 | ||||
-rw-r--r-- | src/nvim/buffer_defs.h | 17 | ||||
-rw-r--r-- | src/nvim/drawscreen.c | 50 | ||||
-rw-r--r-- | src/nvim/eval/funcs.c | 1 | ||||
-rw-r--r-- | src/nvim/window.c | 5 |
6 files changed, 176 insertions, 2 deletions
diff --git a/src/nvim/api/keysets.lua b/src/nvim/api/keysets.lua index ea8949bd2c..d9d9c7ee1d 100644 --- a/src/nvim/api/keysets.lua +++ b/src/nvim/api/keysets.lua @@ -83,6 +83,8 @@ return { "border"; "style"; "noautocmd"; + "title"; + "title_position"; }; runtime = { "is_lua"; diff --git a/src/nvim/api/win_config.c b/src/nvim/api/win_config.c index 636b9566ce..7523ff301d 100644 --- a/src/nvim/api/win_config.c +++ b/src/nvim/api/win_config.c @@ -437,9 +437,73 @@ static void parse_border_style(Object style, FloatConfig *fconfig, Error *err) } } +static bool parse_title(FloatConfig* out, String s) +{ + // The raw title is going to be at most the length of the string. + char* out_title_raw = xcalloc(sizeof(char), s.size + 1); + size_t out_cursor = 0; + + char* data = s.data; + + size_t out_hlrec_nalloc = 4; + stl_hlrec_t* out_hlrec = xcalloc(sizeof(stl_hlrec_t), out_hlrec_nalloc); + out_hlrec[0].start = (char*) out_title_raw; + out_hlrec[0].userhl = 0; + size_t out_hl_cur = 1; + + char hlbuf[128]; + size_t hlbuf_cur = 0; + + int hl; + + for (size_t i = 0; i < s.size; i ++) { + if (data[i] == '\\' && i < s.size - 1) { + i ++; + out_title_raw[out_cursor++] = data[i]; + } else if ( + data[i] == '%' && + i < s.size - 1 && data[i + 1] == '#') { + i += 2; + while (i < s.size && data[i] != '#') { + if (hlbuf_cur < sizeof(hlbuf) - 1) { + hlbuf[hlbuf_cur ++] = data[i]; + } + i ++; + } + hlbuf[hlbuf_cur++] = 0; + hl = syn_check_group(hlbuf, strlen(hlbuf)); + hlbuf_cur = 0; + + if (out_hl_cur >= out_hlrec_nalloc - 1) { // Leave room for last. + out_hlrec = + xrealloc(out_hlrec, sizeof(stl_hlrec_t) * (out_hlrec_nalloc *= 2)); + } + + out_hlrec[out_hl_cur].start = (out_title_raw + out_cursor); + out_hlrec[out_hl_cur++].userhl = -hl; + } else { + out_title_raw[out_cursor++] = data[i]; + } + } + + out->n_title = out_cursor; + out_title_raw[out_cursor++] = 0; + out_hlrec[out_hl_cur] = (stl_hlrec_t) { 0, 0 }; + out->title_hl = out_hlrec; + out->title = out_title_raw; + + return true; +} + static bool parse_float_config(Dict(float_config) *config, FloatConfig *fconfig, bool reconf, bool new_win, Error *err) { + xfree(fconfig->title); + xfree(fconfig->title_hl); + fconfig->title_hl = NULL; + fconfig->n_title = 0; + fconfig->title = NULL; + bool has_relative = false, relative_is_win = false; if (config->relative.type == kObjectTypeString) { // ignore empty string, to match nvim_win_get_config @@ -634,5 +698,44 @@ static bool parse_float_config(Dict(float_config) *config, FloatConfig *fconfig, } } + if (HAS_KEY(config->title)) { + if (!parse_title(fconfig, config->title.data.string)) { + api_set_error(err, kErrorTypeValidation, + "Invalid value of 'title' key."); + goto free_and_fail; + } + } else if (config->title.type == kObjectTypeString) { + api_set_error(err, kErrorTypeValidation, "'title' must be a string"); + return false; + } + + if (HAS_KEY(config->title_position)) { + if (config->title_position.type != kObjectTypeString) { + api_set_error(err, kErrorTypeValidation, + "Invalid value of 'title_position' key"); + goto free_and_fail; + } + + if (striequal(config->title_position.data.string.data, "left")) { + fconfig->title_pos = kTitleLeft; + } else if (striequal(config->title_position.data.string.data, "center")) { + fconfig->title_pos = kTitleCenter; + } else if (striequal(config->title_position.data.string.data, "right")) { + fconfig->title_pos = kTitleRight; + } else { + api_set_error(err, kErrorTypeValidation, + "Invalid value for 'title_position'"); + goto free_and_fail; + } + } + return true; + +free_and_fail: + xfree(fconfig->title); + xfree(fconfig->title_hl); + fconfig->n_title = 0; + fconfig->title_hl = NULL; + fconfig->title = NULL; + return false; } diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 630e1d14a8..829632358f 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -1042,6 +1042,12 @@ typedef enum { kWinStyleMinimal, /// Minimal UI: no number column, eob markers, etc } WinStyle; +typedef enum { + kTitleLeft = 0, + kTitleCenter, + kTitleRight +} TitlePosition; + typedef struct { Window window; lpos_T bufpos; @@ -1059,6 +1065,11 @@ typedef struct { int border_hl_ids[8]; int border_attr[8]; bool noautocmd; + + stl_hlrec_t* title_hl; + char* title; + size_t n_title; + TitlePosition title_pos; } FloatConfig; #define FLOAT_CONFIG_INIT ((FloatConfig){ .height = 0, .width = 0, \ @@ -1068,7 +1079,11 @@ typedef struct { .focusable = true, \ .zindex = kZIndexFloatDefault, \ .style = kWinStyleUnused, \ - .noautocmd = false }) + .noautocmd = false, \ + .title_hl = NULL, \ + .title = NULL, \ + .n_title = 0, \ + .title_pos = kTitleLeft}) // Structure to store last cursor position and topline. Used by check_lnums() // and reset_lnums(). diff --git a/src/nvim/drawscreen.c b/src/nvim/drawscreen.c index 15a7294496..2fbfd7080f 100644 --- a/src/nvim/drawscreen.c +++ b/src/nvim/drawscreen.c @@ -641,13 +641,61 @@ static void win_redr_border(win_T *wp) int *adj = wp->w_border_adj; int irow = wp->w_height_inner + wp->w_winbar_height, icol = wp->w_width_inner; + char* title = wp->w_float_config.title; + size_t n_title = wp->w_float_config.n_title; + stl_hlrec_t* title_hl = wp->w_float_config.title_hl; + + int m8[MAX_MCO + 1]; + int cc; + int len; + int t_attr = title_hl != NULL && title_hl->userhl + ? syn_id2attr(title_hl->userhl) + : 0; + t_attr = hl_combine_attr(attrs[1], t_attr); + + int title_pos = 2; + switch (wp->w_float_config.title_pos) { + case kTitleLeft: + title_pos = 2; + break; + case kTitleRight: + title_pos = icol - 2 - vim_strsize(title); + break; + case kTitleCenter: + title_pos = (icol - vim_strsize(title)) / 2 - 1; + break; + } + title_pos = title_pos < 2 ? 2 : title_pos; + if (adj[0]) { grid_puts_line_start(grid, 0); if (adj[3]) { grid_put_schar(grid, 0, 0, chars[0], attrs[0]); } for (int i = 0; i < icol; i++) { - grid_put_schar(grid, 0, i + adj[3], chars[1], attrs[1]); + schar_T ch; + int attr; + // Draw the title if in the correct position. + if (i > title_pos && n_title > 0 && i < icol - 2) { + cc = utfc_ptr2char((char_u*) title, m8); + len = utfc_ptr2len(title); + n_title -= len; + title += len; + + while (title_hl != NULL && + (title_hl + 1)->start != NULL && + (title_hl + 1)->start < title) { + ++ title_hl; + t_attr = hl_combine_attr(attrs[1], syn_id2attr(-title_hl->userhl)); + } + + schar_from_cc(ch, cc, m8); + attr = t_attr; + } else { + memcpy(ch, chars[1], sizeof(schar_T)); + attr = attrs[1]; + } + grid_put_schar(grid, 0, i + adj[3], ch, attr); } if (adj[1]) { grid_put_schar(grid, 0, icol + adj[3], chars[2], attrs[2]); diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index a326c44371..3fff0798c4 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -3658,6 +3658,7 @@ static void f_has(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) "winaltkeys", "writebackup", "nvim", + "floattitle", }; // XXX: eval_has_provider() may shell out :( diff --git a/src/nvim/window.c b/src/nvim/window.c index d7ca718c68..9be7a91667 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -3053,6 +3053,11 @@ static win_T *win_free_mem(win_T *win, int *dirp, tabpage_T *tp) wp = firstwin; } } + xfree(win->w_float_config.title); + xfree(win->w_float_config.title_hl); + win->w_float_config.title_hl = NULL; + win->w_float_config.title = NULL; + win->w_float_config.n_title = 0; win_free(win, tp); // When deleting the current window of another tab page select a new |