aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorluukvbaal <31730729+luukvbaal@users.noreply.github.com>2022-11-10 12:05:16 +0100
committerGitHub <noreply@github.com>2022-11-10 19:05:16 +0800
commit69507c0204cfe284e42865c9c89baec0f351b2c1 (patch)
treeabe2c01c88913f2d619a649a9a464ef7f230f5a5
parentbefae73044fa367c6d0d82bf4b61501010e7545d (diff)
downloadrneovim-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.h1
-rw-r--r--src/nvim/buffer_defs.h4
-rw-r--r--src/nvim/drawscreen.c8
-rw-r--r--src/nvim/ex_docmd.c1
-rw-r--r--src/nvim/grid_defs.h18
-rw-r--r--src/nvim/main.c1
-rw-r--r--src/nvim/mouse.c1
-rw-r--r--src/nvim/screen.c270
-rw-r--r--src/nvim/screen.h6
-rw-r--r--src/nvim/statusline.c564
-rw-r--r--src/nvim/statusline.h5
-rw-r--r--src/nvim/statusline_defs.h26
-rw-r--r--src/nvim/window.c9
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;