mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
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
This commit is contained in:
parent
befae73044
commit
69507c0204
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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"
|
||||
|
@ -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;
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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) {
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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;
|
||||
|
||||
// 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 (default_grid.chars == NULL) {
|
||||
return;
|
||||
}
|
||||
entered = true;
|
||||
redraw_tabline = false;
|
||||
|
||||
// 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 (ui_has(kUITabline)) {
|
||||
ui_ext_tabline_update();
|
||||
return;
|
||||
}
|
||||
|
||||
if (row < 0) {
|
||||
return;
|
||||
}
|
||||
if (tabline_height() < 1) {
|
||||
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, (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);
|
||||
// Use the 'tabline' option if it's set.
|
||||
if (*p_tal != NUL) {
|
||||
win_redr_custom(NULL, false, false);
|
||||
} 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);
|
||||
FOR_ALL_TABS(tp) {
|
||||
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 (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 (atoi(stl)) {
|
||||
while (ascii_isdigit(*stl)) {
|
||||
stl++;
|
||||
}
|
||||
|
||||
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 (*stl++ != '(') {
|
||||
stl = p_ruf;
|
||||
if (modified) {
|
||||
grid_puts_len(&default_grid, "+", 1, 0, col++, attr);
|
||||
}
|
||||
grid_putchar(&default_grid, ' ', 0, col++, attr);
|
||||
}
|
||||
col = ru_col - (Columns - maxwidth);
|
||||
if (col < (maxwidth + 1) / 2) {
|
||||
col = (maxwidth + 1) / 2;
|
||||
|
||||
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;
|
||||
}
|
||||
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);
|
||||
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,
|
||||
};
|
||||
}
|
||||
} 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];
|
||||
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".
|
||||
|
@ -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
|
||||
|
26
src/nvim/statusline_defs.h
Normal file
26
src/nvim/statusline_defs.h
Normal file
@ -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
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user