diff options
author | luukvbaal <31730729+luukvbaal@users.noreply.github.com> | 2022-11-10 12:05:16 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-11-10 19:05:16 +0800 |
commit | 69507c0204cfe284e42865c9c89baec0f351b2c1 (patch) | |
tree | abe2c01c88913f2d619a649a9a464ef7f230f5a5 | |
parent | befae73044fa367c6d0d82bf4b61501010e7545d (diff) | |
download | rneovim-69507c0204cfe284e42865c9c89baec0f351b2c1.tar.gz rneovim-69507c0204cfe284e42865c9c89baec0f351b2c1.tar.bz2 rneovim-69507c0204cfe284e42865c9c89baec0f351b2c1.zip |
refactor: move tabline code to statusline.c (#21008)
* refactor: move tabline code to statusline.c
Problem: Tabline code is closely related to statusline, but still left over in drawscreen.c and screen.c.
Solution: Move it to statusline.c.
* refactor: add statusline_defs.h
-rw-r--r-- | src/nvim/buffer.h | 1 | ||||
-rw-r--r-- | src/nvim/buffer_defs.h | 4 | ||||
-rw-r--r-- | src/nvim/drawscreen.c | 8 | ||||
-rw-r--r-- | src/nvim/ex_docmd.c | 1 | ||||
-rw-r--r-- | src/nvim/grid_defs.h | 18 | ||||
-rw-r--r-- | src/nvim/main.c | 1 | ||||
-rw-r--r-- | src/nvim/mouse.c | 1 | ||||
-rw-r--r-- | src/nvim/screen.c | 270 | ||||
-rw-r--r-- | src/nvim/screen.h | 6 | ||||
-rw-r--r-- | src/nvim/statusline.c | 564 | ||||
-rw-r--r-- | src/nvim/statusline.h | 5 | ||||
-rw-r--r-- | src/nvim/statusline_defs.h | 26 | ||||
-rw-r--r-- | src/nvim/window.c | 9 |
13 files changed, 458 insertions, 456 deletions
diff --git a/src/nvim/buffer.h b/src/nvim/buffer.h index 7380bb045a..84c402d782 100644 --- a/src/nvim/buffer.h +++ b/src/nvim/buffer.h @@ -4,7 +4,6 @@ #include "nvim/eval/typval.h" #include "nvim/ex_cmds_defs.h" #include "nvim/func_attr.h" -#include "nvim/grid_defs.h" // for StlClickRecord #include "nvim/macros.h" #include "nvim/memline.h" #include "nvim/pos.h" // for linenr_T diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 6448c6b6f6..96cf352067 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -6,8 +6,6 @@ // for FILE #include <stdio.h> -#include "grid_defs.h" - typedef struct file_buffer buf_T; // Forward declaration // Reference to a buffer that stores the value of buf_free_count. @@ -46,6 +44,8 @@ typedef struct { #include "nvim/marktree.h" // for float window title #include "nvim/extmark_defs.h" +// for click definitions +#include "nvim/statusline_defs.h" #define GETFILE_SUCCESS(x) ((x) <= 0) #define MODIFIABLE(buf) (buf->b_p_ma) diff --git a/src/nvim/drawscreen.c b/src/nvim/drawscreen.c index b674f8c235..fe2c9c3801 100644 --- a/src/nvim/drawscreen.c +++ b/src/nvim/drawscreen.c @@ -165,14 +165,10 @@ bool default_grid_alloc(void) // size is wrong. grid_alloc(&default_grid, Rows, Columns, true, true); - StlClickDefinition *new_tab_page_click_defs = - xcalloc((size_t)Columns, sizeof(*new_tab_page_click_defs)); stl_clear_click_defs(tab_page_click_defs, tab_page_click_defs_size); - xfree(tab_page_click_defs); - - tab_page_click_defs = new_tab_page_click_defs; - tab_page_click_defs_size = Columns; + tab_page_click_defs = stl_alloc_click_defs(tab_page_click_defs, Columns, + &tab_page_click_defs_size); default_grid.comp_height = Rows; default_grid.comp_width = Columns; diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index e19c5f27d7..31e9401125 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -78,6 +78,7 @@ #include "nvim/spell.h" #include "nvim/spellfile.h" #include "nvim/state.h" +#include "nvim/statusline.h" #include "nvim/strings.h" #include "nvim/syntax.h" #include "nvim/tag.h" diff --git a/src/nvim/grid_defs.h b/src/nvim/grid_defs.h index 57b3817bc6..db31f7b984 100644 --- a/src/nvim/grid_defs.h +++ b/src/nvim/grid_defs.h @@ -110,24 +110,6 @@ struct ScreenGrid { false, 0, 0, NULL, false, true, 0, \ 0, 0, 0, 0, 0, false } -/// Status line click definition -typedef struct { - enum { - kStlClickDisabled = 0, ///< Clicks to this area are ignored. - kStlClickTabSwitch, ///< Switch to the given tab. - kStlClickTabClose, ///< Close given tab. - kStlClickFuncRun, ///< Run user function. - } type; ///< Type of the click. - int tabnr; ///< Tab page number. - char *func; ///< Function to run. -} StlClickDefinition; - -/// Used for tabline clicks -typedef struct { - StlClickDefinition def; ///< Click definition. - const char *start; ///< Location where region starts. -} StlClickRecord; - typedef struct { int args[3]; int icell; diff --git a/src/nvim/main.c b/src/nvim/main.c index fc88739738..e395f8dc78 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -61,6 +61,7 @@ #include "nvim/shada.h" #include "nvim/sign.h" #include "nvim/state.h" +#include "nvim/statusline.h" #include "nvim/strings.h" #include "nvim/syntax.h" #include "nvim/ui.h" diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c index ed69197cde..fd1eec692a 100644 --- a/src/nvim/mouse.c +++ b/src/nvim/mouse.c @@ -25,6 +25,7 @@ #include "nvim/plines.h" #include "nvim/search.h" #include "nvim/state.h" +#include "nvim/statusline.h" #include "nvim/strings.h" #include "nvim/syntax.h" #include "nvim/ui.h" diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 39b3291404..2470fd326b 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -582,65 +582,6 @@ void check_for_delay(bool check_msg_scroll) } } -/// Clear status line, window bar or tab page line click definition table -/// -/// @param[out] tpcd Table to clear. -/// @param[in] tpcd_size Size of the table. -void stl_clear_click_defs(StlClickDefinition *const click_defs, const long click_defs_size) -{ - if (click_defs != NULL) { - for (long i = 0; i < click_defs_size; i++) { - if (i == 0 || click_defs[i].func != click_defs[i - 1].func) { - xfree(click_defs[i].func); - } - } - memset(click_defs, 0, (size_t)click_defs_size * sizeof(click_defs[0])); - } -} - -/// Allocate or resize the click definitions array if needed. -StlClickDefinition *stl_alloc_click_defs(StlClickDefinition *cdp, long width, size_t *size) -{ - if (*size < (size_t)width) { - xfree(cdp); - *size = (size_t)width; - cdp = xcalloc(*size, sizeof(StlClickDefinition)); - } - return cdp; -} - -/// Fill the click definitions array if needed. -void stl_fill_click_defs(StlClickDefinition *click_defs, StlClickRecord *click_recs, char *buf, - int width, bool tabline) -{ - if (click_defs == NULL) { - return; - } - - int col = 0; - int len = 0; - - StlClickDefinition cur_click_def = { - .type = kStlClickDisabled, - }; - for (int i = 0; click_recs[i].start != NULL; i++) { - len += vim_strnsize(buf, (int)(click_recs[i].start - buf)); - while (col < len) { - click_defs[col++] = cur_click_def; - } - buf = (char *)click_recs[i].start; - cur_click_def = click_recs[i].def; - if (!tabline && !(cur_click_def.type == kStlClickDisabled - || cur_click_def.type == kStlClickFuncRun)) { - // window bar and status line only support click functions - cur_click_def.type = kStlClickDisabled; - } - } - while (col < width) { - click_defs[col++] = cur_click_def; - } -} - /// Set cursor to its position in the current window. void setcursor(void) { @@ -959,217 +900,6 @@ static void recording_mode(int attr) } } -/// Draw the tab pages line at the top of the Vim window. -void draw_tabline(void) -{ - int tabcount = 0; - int tabwidth = 0; - int col = 0; - int scol = 0; - int attr; - win_T *wp; - win_T *cwp; - int wincount; - int modified; - int c; - int len; - int attr_nosel = HL_ATTR(HLF_TP); - int attr_fill = HL_ATTR(HLF_TPF); - char_u *p; - int room; - int use_sep_chars = (t_colors < 8); - - if (default_grid.chars == NULL) { - return; - } - redraw_tabline = false; - - if (ui_has(kUITabline)) { - ui_ext_tabline_update(); - return; - } - - if (tabline_height() < 1) { - return; - } - - // Init TabPageIdxs[] to zero: Clicking outside of tabs has no effect. - assert(Columns == tab_page_click_defs_size); - stl_clear_click_defs(tab_page_click_defs, tab_page_click_defs_size); - - // Use the 'tabline' option if it's set. - if (*p_tal != NUL) { - win_redr_custom(NULL, false, false); - } else { - FOR_ALL_TABS(tp) { - tabcount++; - } - - if (tabcount > 0) { - tabwidth = (Columns - 1 + tabcount / 2) / tabcount; - } - - if (tabwidth < 6) { - tabwidth = 6; - } - - attr = attr_nosel; - tabcount = 0; - - FOR_ALL_TABS(tp) { - if (col >= Columns - 4) { - break; - } - - scol = col; - - if (tp == curtab) { - cwp = curwin; - wp = firstwin; - } else { - cwp = tp->tp_curwin; - wp = tp->tp_firstwin; - } - - if (tp->tp_topframe == topframe) { - attr = win_hl_attr(cwp, HLF_TPS); - } - if (use_sep_chars && col > 0) { - grid_putchar(&default_grid, '|', 0, col++, attr); - } - - if (tp->tp_topframe != topframe) { - attr = win_hl_attr(cwp, HLF_TP); - } - - grid_putchar(&default_grid, ' ', 0, col++, attr); - - modified = false; - - for (wincount = 0; wp != NULL; wp = wp->w_next, wincount++) { - if (bufIsChanged(wp->w_buffer)) { - modified = true; - } - } - - if (modified || wincount > 1) { - if (wincount > 1) { - vim_snprintf((char *)NameBuff, MAXPATHL, "%d", wincount); - len = (int)strlen(NameBuff); - if (col + len >= Columns - 3) { - break; - } - grid_puts_len(&default_grid, NameBuff, len, 0, col, - hl_combine_attr(attr, win_hl_attr(cwp, HLF_T))); - col += len; - } - if (modified) { - grid_puts_len(&default_grid, "+", 1, 0, col++, attr); - } - grid_putchar(&default_grid, ' ', 0, col++, attr); - } - - room = scol - col + tabwidth - 1; - if (room > 0) { - // Get buffer name in NameBuff[] - get_trans_bufname(cwp->w_buffer); - shorten_dir(NameBuff); - len = vim_strsize((char *)NameBuff); - p = (char_u *)NameBuff; - while (len > room) { - len -= ptr2cells((char *)p); - MB_PTR_ADV(p); - } - if (len > Columns - col - 1) { - len = Columns - col - 1; - } - - grid_puts_len(&default_grid, (char *)p, (int)STRLEN(p), 0, col, attr); - col += len; - } - grid_putchar(&default_grid, ' ', 0, col++, attr); - - // Store the tab page number in tab_page_click_defs[], so that - // jump_to_mouse() knows where each one is. - tabcount++; - while (scol < col) { - tab_page_click_defs[scol++] = (StlClickDefinition) { - .type = kStlClickTabSwitch, - .tabnr = tabcount, - .func = NULL, - }; - } - } - - if (use_sep_chars) { - c = '_'; - } else { - c = ' '; - } - grid_fill(&default_grid, 0, 1, col, Columns, c, c, attr_fill); - - // Put an "X" for closing the current tab if there are several. - if (first_tabpage->tp_next != NULL) { - grid_putchar(&default_grid, 'X', 0, Columns - 1, attr_nosel); - tab_page_click_defs[Columns - 1] = (StlClickDefinition) { - .type = kStlClickTabClose, - .tabnr = 999, - .func = NULL, - }; - } - } - - // Reset the flag here again, in case evaluating 'tabline' causes it to be - // set. - redraw_tabline = false; -} - -static void ui_ext_tabline_update(void) -{ - Arena arena = ARENA_EMPTY; - - size_t n_tabs = 0; - FOR_ALL_TABS(tp) { - n_tabs++; - } - - Array tabs = arena_array(&arena, n_tabs); - FOR_ALL_TABS(tp) { - Dictionary tab_info = arena_dict(&arena, 2); - PUT_C(tab_info, "tab", TABPAGE_OBJ(tp->handle)); - - win_T *cwp = (tp == curtab) ? curwin : tp->tp_curwin; - get_trans_bufname(cwp->w_buffer); - PUT_C(tab_info, "name", STRING_OBJ(arena_string(&arena, cstr_as_string((char *)NameBuff)))); - - ADD_C(tabs, DICTIONARY_OBJ(tab_info)); - } - - size_t n_buffers = 0; - FOR_ALL_BUFFERS(buf) { - n_buffers += buf->b_p_bl ? 1 : 0; - } - - Array buffers = arena_array(&arena, n_buffers); - FOR_ALL_BUFFERS(buf) { - // Do not include unlisted buffers - if (!buf->b_p_bl) { - continue; - } - - Dictionary buffer_info = arena_dict(&arena, 2); - PUT_C(buffer_info, "buffer", BUFFER_OBJ(buf->handle)); - - get_trans_bufname(buf); - PUT_C(buffer_info, "name", STRING_OBJ(arena_string(&arena, cstr_as_string((char *)NameBuff)))); - - ADD_C(buffers, DICTIONARY_OBJ(buffer_info)); - } - - ui_call_tabline_update(curtab->handle, tabs, curbuf->handle, buffers); - arena_mem_free(arena_finish(&arena)); -} - void get_trans_bufname(buf_T *buf) { if (buf_spname(buf) != NULL) { diff --git a/src/nvim/screen.h b/src/nvim/screen.h index ea1c58cd80..5cee708bd1 100644 --- a/src/nvim/screen.h +++ b/src/nvim/screen.h @@ -9,12 +9,6 @@ EXTERN match_T screen_search_hl; // used for 'hlsearch' highlight matching -/// Array defining what should be done when tabline is clicked -EXTERN StlClickDefinition *tab_page_click_defs INIT(= NULL); - -/// Size of the tab_page_click_defs array -EXTERN long tab_page_click_defs_size INIT(= 0); - #define W_ENDCOL(wp) ((wp)->w_wincol + (wp)->w_width) #define W_ENDROW(wp) ((wp)->w_winrow + (wp)->w_height) diff --git a/src/nvim/statusline.c b/src/nvim/statusline.c index 039fe5d586..93334a1d75 100644 --- a/src/nvim/statusline.c +++ b/src/nvim/statusline.c @@ -158,6 +158,243 @@ void win_redr_status(win_T *wp) busy = false; } +/// Clear status line, window bar or tab page line click definition table +/// +/// @param[out] tpcd Table to clear. +/// @param[in] tpcd_size Size of the table. +void stl_clear_click_defs(StlClickDefinition *const click_defs, const size_t click_defs_size) +{ + if (click_defs != NULL) { + for (size_t i = 0; i < click_defs_size; i++) { + if (i == 0 || click_defs[i].func != click_defs[i - 1].func) { + xfree(click_defs[i].func); + } + } + memset(click_defs, 0, click_defs_size * sizeof(click_defs[0])); + } +} + +/// Allocate or resize the click definitions array if needed. +StlClickDefinition *stl_alloc_click_defs(StlClickDefinition *cdp, long width, size_t *size) +{ + if (*size < (size_t)width) { + xfree(cdp); + *size = (size_t)width; + cdp = xcalloc(*size, sizeof(StlClickDefinition)); + } + return cdp; +} + +/// Fill the click definitions array if needed. +void stl_fill_click_defs(StlClickDefinition *click_defs, StlClickRecord *click_recs, char *buf, + int width, bool tabline) +{ + if (click_defs == NULL) { + return; + } + + int col = 0; + int len = 0; + + StlClickDefinition cur_click_def = { + .type = kStlClickDisabled, + }; + for (int i = 0; click_recs[i].start != NULL; i++) { + len += vim_strnsize(buf, (int)(click_recs[i].start - buf)); + while (col < len) { + click_defs[col++] = cur_click_def; + } + buf = (char *)click_recs[i].start; + cur_click_def = click_recs[i].def; + if (!tabline && !(cur_click_def.type == kStlClickDisabled + || cur_click_def.type == kStlClickFuncRun)) { + // window bar and status line only support click functions + cur_click_def.type = kStlClickDisabled; + } + } + while (col < width) { + click_defs[col++] = cur_click_def; + } +} + +/// Redraw the status line, window bar or ruler of window "wp". +/// When "wp" is NULL redraw the tab pages line from 'tabline'. +static void win_redr_custom(win_T *wp, bool draw_winbar, bool draw_ruler) +{ + static bool entered = false; + int attr; + int curattr; + int row; + int col = 0; + int maxwidth; + int width; + int n; + int len; + int fillchar; + char buf[MAXPATHL]; + char *stl; + char *p; + char *opt_name; + int opt_scope = 0; + stl_hlrec_t *hltab; + StlClickRecord *tabtab; + win_T *ewp; + int p_crb_save; + bool is_stl_global = global_stl_height() > 0; + + ScreenGrid *grid = &default_grid; + + // There is a tiny chance that this gets called recursively: When + // redrawing a status line triggers redrawing the ruler or tabline. + // Avoid trouble by not allowing recursion. + if (entered) { + return; + } + entered = true; + + // setup environment for the task at hand + if (wp == NULL) { + // Use 'tabline'. Always at the first line of the screen. + stl = p_tal; + row = 0; + fillchar = ' '; + attr = HL_ATTR(HLF_TPF); + maxwidth = Columns; + opt_name = "tabline"; + } else if (draw_winbar) { + opt_name = "winbar"; + stl = ((*wp->w_p_wbr != NUL) ? wp->w_p_wbr : p_wbr); + opt_scope = ((*wp->w_p_wbr != NUL) ? OPT_LOCAL : 0); + row = -1; // row zero is first row of text + col = 0; + grid = &wp->w_grid; + grid_adjust(&grid, &row, &col); + + if (row < 0) { + return; + } + + fillchar = wp->w_p_fcs_chars.wbr; + attr = (wp == curwin) ? win_hl_attr(wp, HLF_WBR) : win_hl_attr(wp, HLF_WBRNC); + maxwidth = wp->w_width_inner; + stl_clear_click_defs(wp->w_winbar_click_defs, wp->w_winbar_click_defs_size); + wp->w_winbar_click_defs = stl_alloc_click_defs(wp->w_winbar_click_defs, maxwidth, + &wp->w_winbar_click_defs_size); + } else { + row = is_stl_global ? (Rows - (int)p_ch - 1) : W_ENDROW(wp); + fillchar = fillchar_status(&attr, wp); + maxwidth = is_stl_global ? Columns : wp->w_width; + stl_clear_click_defs(wp->w_status_click_defs, wp->w_status_click_defs_size); + wp->w_status_click_defs = stl_alloc_click_defs(wp->w_status_click_defs, maxwidth, + &wp->w_status_click_defs_size); + + if (draw_ruler) { + stl = p_ruf; + opt_name = "rulerformat"; + // advance past any leading group spec - implicit in ru_col + if (*stl == '%') { + if (*++stl == '-') { + stl++; + } + if (atoi(stl)) { + while (ascii_isdigit(*stl)) { + stl++; + } + } + if (*stl++ != '(') { + stl = p_ruf; + } + } + col = ru_col - (Columns - maxwidth); + if (col < (maxwidth + 1) / 2) { + col = (maxwidth + 1) / 2; + } + maxwidth = maxwidth - col; + if (!wp->w_status_height && !is_stl_global) { + grid = &msg_grid_adj; + row = Rows - 1; + maxwidth--; // writing in last column may cause scrolling + fillchar = ' '; + attr = HL_ATTR(HLF_MSG); + } + } else { + opt_name = "statusline"; + stl = ((*wp->w_p_stl != NUL) ? wp->w_p_stl : p_stl); + opt_scope = ((*wp->w_p_stl != NUL) ? OPT_LOCAL : 0); + } + + col += is_stl_global ? 0 : wp->w_wincol; + } + + if (maxwidth <= 0) { + goto theend; + } + + // Temporarily reset 'cursorbind', we don't want a side effect from moving + // the cursor away and back. + ewp = wp == NULL ? curwin : wp; + p_crb_save = ewp->w_p_crb; + ewp->w_p_crb = false; + + // Make a copy, because the statusline may include a function call that + // might change the option value and free the memory. + stl = xstrdup(stl); + width = build_stl_str_hl(ewp, buf, sizeof(buf), stl, opt_name, + opt_scope, fillchar, maxwidth, &hltab, &tabtab); + xfree(stl); + ewp->w_p_crb = p_crb_save; + + // Make all characters printable. + p = transstr(buf, true); + len = (int)STRLCPY(buf, p, sizeof(buf)); + len = (size_t)len < sizeof(buf) ? len : (int)sizeof(buf) - 1; + xfree(p); + + // fill up with "fillchar" + while (width < maxwidth && len < (int)sizeof(buf) - 1) { + len += utf_char2bytes(fillchar, buf + len); + width++; + } + buf[len] = NUL; + + // Draw each snippet with the specified highlighting. + grid_puts_line_start(grid, row); + + curattr = attr; + p = buf; + for (n = 0; hltab[n].start != NULL; n++) { + int textlen = (int)(hltab[n].start - p); + grid_puts_len(grid, p, textlen, row, col, curattr); + col += vim_strnsize(p, textlen); + p = hltab[n].start; + + if (hltab[n].userhl == 0) { + curattr = attr; + } else if (hltab[n].userhl < 0) { + curattr = syn_id2attr(-hltab[n].userhl); + } else if (wp != NULL && wp != curwin && wp->w_status_height != 0) { + curattr = highlight_stlnc[hltab[n].userhl - 1]; + } else { + curattr = highlight_user[hltab[n].userhl - 1]; + } + } + // Make sure to use an empty string instead of p, if p is beyond buf + len. + grid_puts(grid, p >= buf + len ? "" : p, row, col, curattr); + + grid_puts_line_flush(false); + + // Fill the tab_page_click_defs, w_status_click_defs or w_winbar_click_defs array for clicking + // in the tab page line, status line or window bar + StlClickDefinition *click_defs = (wp == NULL) ? tab_page_click_defs + : draw_winbar ? wp->w_winbar_click_defs + : wp->w_status_click_defs; + + stl_fill_click_defs(click_defs, tabtab, buf, maxwidth, wp == NULL); + +theend: + entered = false; +} + void win_redr_winbar(win_T *wp) { static bool entered = false; @@ -385,182 +622,211 @@ void redraw_custom_statusline(win_T *wp) entered = false; } -/// Redraw the status line, window bar or ruler of window "wp". -/// When "wp" is NULL redraw the tab pages line from 'tabline'. -void win_redr_custom(win_T *wp, bool draw_winbar, bool draw_ruler) +static void ui_ext_tabline_update(void) { - static bool entered = false; - int attr; - int curattr; - int row; + Arena arena = ARENA_EMPTY; + + size_t n_tabs = 0; + FOR_ALL_TABS(tp) { + n_tabs++; + } + + Array tabs = arena_array(&arena, n_tabs); + FOR_ALL_TABS(tp) { + Dictionary tab_info = arena_dict(&arena, 2); + PUT_C(tab_info, "tab", TABPAGE_OBJ(tp->handle)); + + win_T *cwp = (tp == curtab) ? curwin : tp->tp_curwin; + get_trans_bufname(cwp->w_buffer); + PUT_C(tab_info, "name", STRING_OBJ(arena_string(&arena, cstr_as_string((char *)NameBuff)))); + + ADD_C(tabs, DICTIONARY_OBJ(tab_info)); + } + + size_t n_buffers = 0; + FOR_ALL_BUFFERS(buf) { + n_buffers += buf->b_p_bl ? 1 : 0; + } + + Array buffers = arena_array(&arena, n_buffers); + FOR_ALL_BUFFERS(buf) { + // Do not include unlisted buffers + if (!buf->b_p_bl) { + continue; + } + + Dictionary buffer_info = arena_dict(&arena, 2); + PUT_C(buffer_info, "buffer", BUFFER_OBJ(buf->handle)); + + get_trans_bufname(buf); + PUT_C(buffer_info, "name", STRING_OBJ(arena_string(&arena, cstr_as_string((char *)NameBuff)))); + + ADD_C(buffers, DICTIONARY_OBJ(buffer_info)); + } + + ui_call_tabline_update(curtab->handle, tabs, curbuf->handle, buffers); + arena_mem_free(arena_finish(&arena)); +} + +/// Draw the tab pages line at the top of the Vim window. +void draw_tabline(void) +{ + int tabcount = 0; + int tabwidth = 0; int col = 0; - int maxwidth; - int width; - int n; + int scol = 0; + int attr; + win_T *wp; + win_T *cwp; + int wincount; + int modified; + int c; int len; - int fillchar; - char buf[MAXPATHL]; - char *stl; - char *p; - char *opt_name; - int opt_scope = 0; - stl_hlrec_t *hltab; - StlClickRecord *tabtab; - win_T *ewp; - int p_crb_save; - bool is_stl_global = global_stl_height() > 0; + int attr_nosel = HL_ATTR(HLF_TP); + int attr_fill = HL_ATTR(HLF_TPF); + char_u *p; + int room; + int use_sep_chars = (t_colors < 8); - ScreenGrid *grid = &default_grid; + if (default_grid.chars == NULL) { + return; + } + redraw_tabline = false; - // There is a tiny chance that this gets called recursively: When - // redrawing a status line triggers redrawing the ruler or tabline. - // Avoid trouble by not allowing recursion. - if (entered) { + if (ui_has(kUITabline)) { + ui_ext_tabline_update(); return; } - entered = true; - // setup environment for the task at hand - if (wp == NULL) { - // Use 'tabline'. Always at the first line of the screen. - stl = p_tal; - row = 0; - fillchar = ' '; - attr = HL_ATTR(HLF_TPF); - maxwidth = Columns; - opt_name = "tabline"; - } else if (draw_winbar) { - opt_name = "winbar"; - stl = ((*wp->w_p_wbr != NUL) ? wp->w_p_wbr : p_wbr); - opt_scope = ((*wp->w_p_wbr != NUL) ? OPT_LOCAL : 0); - row = -1; // row zero is first row of text - col = 0; - grid = &wp->w_grid; - grid_adjust(&grid, &row, &col); + if (tabline_height() < 1) { + return; + } - if (row < 0) { - return; + // Use the 'tabline' option if it's set. + if (*p_tal != NUL) { + win_redr_custom(NULL, false, false); + } else { + FOR_ALL_TABS(tp) { + tabcount++; } - fillchar = wp->w_p_fcs_chars.wbr; - attr = (wp == curwin) ? win_hl_attr(wp, HLF_WBR) : win_hl_attr(wp, HLF_WBRNC); - maxwidth = wp->w_width_inner; - stl_clear_click_defs(wp->w_winbar_click_defs, (long)wp->w_winbar_click_defs_size); - wp->w_winbar_click_defs = stl_alloc_click_defs(wp->w_winbar_click_defs, maxwidth, - &wp->w_winbar_click_defs_size); - } else { - row = is_stl_global ? (Rows - (int)p_ch - 1) : W_ENDROW(wp); - fillchar = fillchar_status(&attr, wp); - maxwidth = is_stl_global ? Columns : wp->w_width; - stl_clear_click_defs(wp->w_status_click_defs, (long)wp->w_status_click_defs_size); - wp->w_status_click_defs = stl_alloc_click_defs(wp->w_status_click_defs, maxwidth, - &wp->w_status_click_defs_size); + if (tabcount > 0) { + tabwidth = (Columns - 1 + tabcount / 2) / tabcount; + } - if (draw_ruler) { - stl = p_ruf; - opt_name = "rulerformat"; - // advance past any leading group spec - implicit in ru_col - if (*stl == '%') { - if (*++stl == '-') { - stl++; - } - if (atoi(stl)) { - while (ascii_isdigit(*stl)) { - stl++; - } - } - if (*stl++ != '(') { - stl = p_ruf; - } - } - col = ru_col - (Columns - maxwidth); - if (col < (maxwidth + 1) / 2) { - col = (maxwidth + 1) / 2; + if (tabwidth < 6) { + tabwidth = 6; + } + + attr = attr_nosel; + tabcount = 0; + + FOR_ALL_TABS(tp) { + if (col >= Columns - 4) { + break; } - maxwidth = maxwidth - col; - if (!wp->w_status_height && !is_stl_global) { - grid = &msg_grid_adj; - row = Rows - 1; - maxwidth--; // writing in last column may cause scrolling - fillchar = ' '; - attr = HL_ATTR(HLF_MSG); + + scol = col; + + if (tp == curtab) { + cwp = curwin; + wp = firstwin; + } else { + cwp = tp->tp_curwin; + wp = tp->tp_firstwin; } - } else { - opt_name = "statusline"; - stl = ((*wp->w_p_stl != NUL) ? wp->w_p_stl : p_stl); - opt_scope = ((*wp->w_p_stl != NUL) ? OPT_LOCAL : 0); - } - col += is_stl_global ? 0 : wp->w_wincol; - } + if (tp->tp_topframe == topframe) { + attr = win_hl_attr(cwp, HLF_TPS); + } + if (use_sep_chars && col > 0) { + grid_putchar(&default_grid, '|', 0, col++, attr); + } - if (maxwidth <= 0) { - goto theend; - } + if (tp->tp_topframe != topframe) { + attr = win_hl_attr(cwp, HLF_TP); + } - // Temporarily reset 'cursorbind', we don't want a side effect from moving - // the cursor away and back. - ewp = wp == NULL ? curwin : wp; - p_crb_save = ewp->w_p_crb; - ewp->w_p_crb = false; + grid_putchar(&default_grid, ' ', 0, col++, attr); - // Make a copy, because the statusline may include a function call that - // might change the option value and free the memory. - stl = xstrdup(stl); - width = build_stl_str_hl(ewp, buf, sizeof(buf), stl, opt_name, - opt_scope, fillchar, maxwidth, &hltab, &tabtab); - xfree(stl); - ewp->w_p_crb = p_crb_save; + modified = false; - // Make all characters printable. - p = transstr(buf, true); - len = (int)STRLCPY(buf, p, sizeof(buf)); - len = (size_t)len < sizeof(buf) ? len : (int)sizeof(buf) - 1; - xfree(p); + for (wincount = 0; wp != NULL; wp = wp->w_next, wincount++) { + if (bufIsChanged(wp->w_buffer)) { + modified = true; + } + } - // fill up with "fillchar" - while (width < maxwidth && len < (int)sizeof(buf) - 1) { - len += utf_char2bytes(fillchar, buf + len); - width++; - } - buf[len] = NUL; + if (modified || wincount > 1) { + if (wincount > 1) { + vim_snprintf((char *)NameBuff, MAXPATHL, "%d", wincount); + len = (int)strlen(NameBuff); + if (col + len >= Columns - 3) { + break; + } + grid_puts_len(&default_grid, NameBuff, len, 0, col, + hl_combine_attr(attr, win_hl_attr(cwp, HLF_T))); + col += len; + } + if (modified) { + grid_puts_len(&default_grid, "+", 1, 0, col++, attr); + } + grid_putchar(&default_grid, ' ', 0, col++, attr); + } - // Draw each snippet with the specified highlighting. - grid_puts_line_start(grid, row); + room = scol - col + tabwidth - 1; + if (room > 0) { + // Get buffer name in NameBuff[] + get_trans_bufname(cwp->w_buffer); + shorten_dir(NameBuff); + len = vim_strsize((char *)NameBuff); + p = (char_u *)NameBuff; + while (len > room) { + len -= ptr2cells((char *)p); + MB_PTR_ADV(p); + } + if (len > Columns - col - 1) { + len = Columns - col - 1; + } - curattr = attr; - p = buf; - for (n = 0; hltab[n].start != NULL; n++) { - int textlen = (int)(hltab[n].start - p); - grid_puts_len(grid, p, textlen, row, col, curattr); - col += vim_strnsize(p, textlen); - p = hltab[n].start; + grid_puts_len(&default_grid, (char *)p, (int)STRLEN(p), 0, col, attr); + col += len; + } + grid_putchar(&default_grid, ' ', 0, col++, attr); + + // Store the tab page number in tab_page_click_defs[], so that + // jump_to_mouse() knows where each one is. + tabcount++; + while (scol < col) { + tab_page_click_defs[scol++] = (StlClickDefinition) { + .type = kStlClickTabSwitch, + .tabnr = tabcount, + .func = NULL, + }; + } + } - if (hltab[n].userhl == 0) { - curattr = attr; - } else if (hltab[n].userhl < 0) { - curattr = syn_id2attr(-hltab[n].userhl); - } else if (wp != NULL && wp != curwin && wp->w_status_height != 0) { - curattr = highlight_stlnc[hltab[n].userhl - 1]; + if (use_sep_chars) { + c = '_'; } else { - curattr = highlight_user[hltab[n].userhl - 1]; + c = ' '; + } + grid_fill(&default_grid, 0, 1, col, Columns, c, c, attr_fill); + + // Put an "X" for closing the current tab if there are several. + if (first_tabpage->tp_next != NULL) { + grid_putchar(&default_grid, 'X', 0, Columns - 1, attr_nosel); + tab_page_click_defs[Columns - 1] = (StlClickDefinition) { + .type = kStlClickTabClose, + .tabnr = 999, + .func = NULL, + }; } } - // Make sure to use an empty string instead of p, if p is beyond buf + len. - grid_puts(grid, p >= buf + len ? "" : p, row, col, curattr); - grid_puts_line_flush(false); - - // Fill the tab_page_click_defs, w_status_click_defs or w_winbar_click_defs array for clicking - // in the tab page line, status line or window bar - StlClickDefinition *click_defs = (wp == NULL) ? tab_page_click_defs - : draw_winbar ? wp->w_winbar_click_defs - : wp->w_status_click_defs; - - stl_fill_click_defs(click_defs, tabtab, buf, maxwidth, wp == NULL); - -theend: - entered = false; + // Reset the flag here again, in case evaluating 'tabline' causes it to be + // set. + redraw_tabline = false; } /// Build a string from the status line items in "fmt". diff --git a/src/nvim/statusline.h b/src/nvim/statusline.h index 357a9a821f..dc25dd5e67 100644 --- a/src/nvim/statusline.h +++ b/src/nvim/statusline.h @@ -3,6 +3,11 @@ #include "nvim/buffer_defs.h" +/// Array defining what should be done when tabline is clicked +EXTERN StlClickDefinition *tab_page_click_defs INIT(= NULL); +/// Size of the tab_page_click_defs array +EXTERN size_t tab_page_click_defs_size INIT(= 0); + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "statusline.h.generated.h" #endif diff --git a/src/nvim/statusline_defs.h b/src/nvim/statusline_defs.h new file mode 100644 index 0000000000..eac9dfd690 --- /dev/null +++ b/src/nvim/statusline_defs.h @@ -0,0 +1,26 @@ +#ifndef NVIM_STATUSLINE_DEFS_H +#define NVIM_STATUSLINE_DEFS_H + +#include <stddef.h> + +#include "nvim/macros.h" + +/// Status line click definition +typedef struct { + enum { + kStlClickDisabled = 0, ///< Clicks to this area are ignored. + kStlClickTabSwitch, ///< Switch to the given tab. + kStlClickTabClose, ///< Close given tab. + kStlClickFuncRun, ///< Run user function. + } type; ///< Type of the click. + int tabnr; ///< Tab page number. + char *func; ///< Function to run. +} StlClickDefinition; + +/// Used for tabline clicks +typedef struct { + StlClickDefinition def; ///< Click definition. + const char *start; ///< Location where region starts. +} StlClickRecord; + +#endif // NVIM_STATUSLINE_DEFS_H diff --git a/src/nvim/window.c b/src/nvim/window.c index 3649e6bc16..de5bcb40ea 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -52,6 +52,7 @@ #include "nvim/regexp.h" #include "nvim/search.h" #include "nvim/state.h" +#include "nvim/statusline.h" #include "nvim/strings.h" #include "nvim/syntax.h" #include "nvim/terminal.h" @@ -5027,10 +5028,10 @@ static void win_free(win_T *wp, tabpage_T *tp) xfree(wp->w_localdir); xfree(wp->w_prevdir); - stl_clear_click_defs(wp->w_status_click_defs, (long)wp->w_status_click_defs_size); + stl_clear_click_defs(wp->w_status_click_defs, wp->w_status_click_defs_size); xfree(wp->w_status_click_defs); - stl_clear_click_defs(wp->w_winbar_click_defs, (long)wp->w_winbar_click_defs_size); + stl_clear_click_defs(wp->w_winbar_click_defs, wp->w_winbar_click_defs_size); xfree(wp->w_winbar_click_defs); // Remove the window from the b_wininfo lists, it may happen that the @@ -6584,7 +6585,7 @@ static void win_remove_status_line(win_T *wp, bool add_hsep) } comp_col(); - stl_clear_click_defs(wp->w_status_click_defs, (long)wp->w_status_click_defs_size); + stl_clear_click_defs(wp->w_status_click_defs, wp->w_status_click_defs_size); xfree(wp->w_status_click_defs); wp->w_status_click_defs_size = 0; wp->w_status_click_defs = NULL; @@ -6720,7 +6721,7 @@ int set_winbar_win(win_T *wp, bool make_room, bool valid_cursor) if (winbar_height == 0) { // When removing winbar, deallocate the w_winbar_click_defs array - stl_clear_click_defs(wp->w_winbar_click_defs, (long)wp->w_winbar_click_defs_size); + stl_clear_click_defs(wp->w_winbar_click_defs, wp->w_winbar_click_defs_size); xfree(wp->w_winbar_click_defs); wp->w_winbar_click_defs_size = 0; wp->w_winbar_click_defs = NULL; |