tabline: Add %[] atom to the tabline, for random commands on click

Currently untested and undocumented.
This commit is contained in:
ZyX 2015-11-23 03:07:32 +03:00
parent 3e3d2d783c
commit ef662498b1
8 changed files with 202 additions and 87 deletions

View File

@ -6011,11 +6011,15 @@ A jump table for the options with a short description can be found at |Q_op|.
( - Start of item group. Can be used for setting the width and ( - Start of item group. Can be used for setting the width and
alignment of a section. Must be followed by %) somewhere. alignment of a section. Must be followed by %) somewhere.
) - End of item group. No width fields allowed. ) - End of item group. No width fields allowed.
T N For 'tabline': start of tab page N label. Use %T after the last T N For 'tabline': start of tab page N label. Use %T or %X to end
label. This information is used for mouse clicks. the label. This information is used for mouse clicks.
X N For 'tabline': start of close tab N label. Use %X after the X N For 'tabline': start of close tab N label. Use %X or %T to end
label, e.g.: %3Xclose%X. Use %999X for a "close current tab" the label, e.g.: %3Xclose%X. Use %999X for a "close current
mark. This information is used for mouse clicks. tab" mark. This information is used for mouse clicks.
[ - For 'tabline': start of execute command label. Use %X or %T to
end the label, e.g.: %[buffer 10]foo.c%X. This information is
used for mouse clicks: in the example when clicking on "foo.c"
"buffer 10" command will be run.
< - Where to truncate line if too long. Default is at the start. < - Where to truncate line if too long. Default is at the start.
No width fields allowed. No width fields allowed.
= - Separation point between left and right aligned items. = - Separation point between left and right aligned items.

View File

@ -22,6 +22,7 @@
#include "nvim/api/private/handle.h" #include "nvim/api/private/handle.h"
#include "nvim/ascii.h" #include "nvim/ascii.h"
#include "nvim/assert.h"
#include "nvim/vim.h" #include "nvim/vim.h"
#include "nvim/buffer.h" #include "nvim/buffer.h"
#include "nvim/charset.h" #include "nvim/charset.h"
@ -2826,7 +2827,7 @@ typedef enum {
/// @param fillchar Character to use when filling empty space in the statusline /// @param fillchar Character to use when filling empty space in the statusline
/// @param maxwidth The maximum width to make the statusline /// @param maxwidth The maximum width to make the statusline
/// @param hltab HL attributes (can be NULL) /// @param hltab HL attributes (can be NULL)
/// @param tabtab tab page nrs (can be NULL) /// @param tabtab Tab clicks definition (can be NULL).
/// ///
/// @return The final width of the statusline /// @return The final width of the statusline
int build_stl_str_hl( int build_stl_str_hl(
@ -2838,13 +2839,15 @@ int build_stl_str_hl(
int fillchar, int fillchar,
int maxwidth, int maxwidth,
struct stl_hlrec *hltab, struct stl_hlrec *hltab,
struct stl_hlrec *tabtab StlClickRecord *tabtab
) )
{ {
int groupitem[STL_MAX_ITEM]; int groupitem[STL_MAX_ITEM];
struct stl_item { struct stl_item {
// Where the item starts in the status line output buffer // Where the item starts in the status line output buffer
char_u *start; char_u *start;
// Command to run for ClickCmd items.
char *cmd;
// The minimum width of the item // The minimum width of the item
int minwid; int minwid;
// The maximum width of the item // The maximum width of the item
@ -2856,10 +2859,10 @@ int build_stl_str_hl(
Middle, Middle,
Highlight, Highlight,
TabPage, TabPage,
ClickCmd,
Trunc Trunc
} type; } type;
} item[STL_MAX_ITEM]; } item[STL_MAX_ITEM];
#define TMPLEN 70 #define TMPLEN 70
char_u tmp[TMPLEN]; char_u tmp[TMPLEN];
char_u *usefmt = fmt; char_u *usefmt = fmt;
@ -3164,6 +3167,22 @@ int build_stl_str_hl(
continue; continue;
} }
if (*fmt_p == STL_CLICK_CMD) {
char *t = (char *) fmt_p;
while (*fmt_p != ']' && *fmt_p) {
fmt_p++;
}
if (*fmt_p != ']') {
break;
}
item[curitem].type = ClickCmd;
item[curitem].start = out_p;
item[curitem].cmd = xmemdupz(t + 1, (size_t) (((char *) fmt_p - t) - 1));
fmt_p++;
curitem++;
continue;
}
// Denotes the end of the minwid // Denotes the end of the minwid
// the maxwid may follow immediately after // the maxwid may follow immediately after
if (*fmt_p == '.') { if (*fmt_p == '.') {
@ -3281,6 +3300,7 @@ int build_stl_str_hl(
} }
break; break;
} }
case STL_LINE: case STL_LINE:
num = (wp->w_buffer->b_ml.ml_flags & ML_EMPTY) num = (wp->w_buffer->b_ml.ml_flags & ML_EMPTY)
? 0L : (long)(wp->w_cursor.lnum); ? 0L : (long)(wp->w_cursor.lnum);
@ -3821,16 +3841,37 @@ int build_stl_str_hl(
// Store the info about tab pages labels. // Store the info about tab pages labels.
if (tabtab != NULL) { if (tabtab != NULL) {
struct stl_hlrec *sp = tabtab; StlClickRecord *cur_tab_rec = tabtab;
for (long l = 0; l < itemcnt; l++) { for (long l = 0; l < itemcnt; l++) {
if (item[l].type == TabPage) { if (item[l].type == TabPage) {
sp->start = item[l].start; cur_tab_rec->start = (char *) item[l].start;
sp->userhl = item[l].minwid; if (item[l].minwid == 0) {
sp++; cur_tab_rec->def.type = kStlClickDisabled;
cur_tab_rec->def.tabnr = 0;
} else {
int tabnr = item[l].minwid;
if (item[l].minwid > 0) {
cur_tab_rec->def.type = kStlClickTabSwitch;
} else {
cur_tab_rec->def.type = kStlClickTabClose;
tabnr = -tabnr;
}
cur_tab_rec->def.tabnr = tabnr;
}
cur_tab_rec->def.cmd = NULL;
cur_tab_rec++;
} else if (item[l].type == ClickCmd) {
cur_tab_rec->start = (char *) item[l].start;
cur_tab_rec->def.type = kStlClickCmd;
cur_tab_rec->def.tabnr = 0;
cur_tab_rec->def.cmd = item[l].cmd;
cur_tab_rec++;
} }
} }
sp->start = NULL; cur_tab_rec->start = NULL;
sp->userhl = 0; cur_tab_rec->def.type = kStlClickDisabled;
cur_tab_rec->def.tabnr = 0;
cur_tab_rec->def.cmd = NULL;
} }
return width; return width;

View File

@ -4,6 +4,7 @@
#include "nvim/window.h" #include "nvim/window.h"
#include "nvim/pos.h" // for linenr_T #include "nvim/pos.h" // for linenr_T
#include "nvim/ex_cmds_defs.h" // for exarg_T #include "nvim/ex_cmds_defs.h" // for exarg_T
#include "nvim/screen.h" // for StlClickRecord
// Values for buflist_getfile() // Values for buflist_getfile()
enum getf_values { enum getf_values {

View File

@ -159,15 +159,6 @@ EXTERN int Screen_mco INIT(= 0); /* value of p_mco used when
* These are single-width. */ * These are single-width. */
EXTERN schar_T *ScreenLines2 INIT(= NULL); EXTERN schar_T *ScreenLines2 INIT(= NULL);
/*
* Indexes for tab page line:
* N > 0 for label of tab page N
* N == 0 for no label
* N < 0 for closing tab page -N
* N == -999 for closing current tab page
*/
EXTERN short *TabPageIdxs INIT(= NULL);
EXTERN int screen_Rows INIT(= 0); /* actual size of ScreenLines[] */ EXTERN int screen_Rows INIT(= 0); /* actual size of ScreenLines[] */
EXTERN int screen_Columns INIT(= 0); /* actual size of ScreenLines[] */ EXTERN int screen_Columns INIT(= 0); /* actual size of ScreenLines[] */

View File

@ -2347,8 +2347,9 @@ do_mouse (
if (mouse_row == 0 && firstwin->w_winrow > 0) { if (mouse_row == 0 && firstwin->w_winrow > 0) {
if (is_drag) { if (is_drag) {
if (in_tab_line) { if (in_tab_line) {
c1 = TabPageIdxs[mouse_col]; tabpage_move(tab_page_click_defs[mouse_col].type == kStlClickTabClose
tabpage_move(c1 <= 0 ? 9999 : c1 - 1); ? 9999
: tab_page_click_defs[mouse_col].tabnr - 1);
} }
return false; return false;
} }
@ -2358,41 +2359,58 @@ do_mouse (
&& cmdwin_type == 0 && cmdwin_type == 0
&& mouse_col < Columns) { && mouse_col < Columns) {
in_tab_line = true; in_tab_line = true;
c1 = TabPageIdxs[mouse_col]; c1 = tab_page_click_defs[mouse_col].tabnr;
if (c1 >= 0) { switch (tab_page_click_defs[mouse_col].type) {
case kStlClickDisabled: {
break;
}
case kStlClickTabClose: {
tabpage_T *tp;
// Close the current or specified tab page.
if (c1 == 999) {
tp = curtab;
} else {
tp = find_tabpage(c1);
}
if (tp == curtab) {
if (first_tabpage->tp_next != NULL) {
tabpage_close(false);
}
} else if (tp != NULL) {
tabpage_close_other(tp, false);
}
break;
}
case kStlClickTabSwitch: {
if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK) { if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK) {
/* double click opens new page */ // double click opens new page
end_visual_mode(); end_visual_mode();
tabpage_new(); tabpage_new();
tabpage_move(c1 == 0 ? 9999 : c1 - 1); tabpage_move(c1 == 0 ? 9999 : c1 - 1);
} else { } else {
/* Go to specified tab page, or next one if not clicking // Go to specified tab page, or next one if not clicking
* on a label. */ // on a label.
goto_tabpage(c1); goto_tabpage(c1);
/* It's like clicking on the status line of a window. */ // It's like clicking on the status line of a window.
if (curwin != old_curwin) if (curwin != old_curwin) {
end_visual_mode(); end_visual_mode();
} }
} else if (c1 < 0) { }
tabpage_T *tp; break;
}
/* Close the current or specified tab page. */ case kStlClickCmd: {
if (c1 == -999) do_cmdline_cmd(tab_page_click_defs[mouse_col].cmd);
tp = curtab; break;
else }
tp = find_tabpage(-c1);
if (tp == curtab) {
if (first_tabpage->tp_next != NULL)
tabpage_close(false);
} else if (tp != NULL)
tabpage_close_other(tp, false);
} }
} }
return true; return true;
} else if (is_drag && in_tab_line) { } else if (is_drag && in_tab_line) {
c1 = TabPageIdxs[mouse_col]; tabpage_move(tab_page_click_defs[mouse_col].type == kStlClickTabClose
tabpage_move(c1 <= 0 ? 9999 : c1 - 1); ? 9999
: tab_page_click_defs[mouse_col].tabnr - 1);
in_tab_line = false; in_tab_line = false;
return false; return false;
} }

View File

@ -247,6 +247,7 @@ enum {
STL_HIGHLIGHT = '#', ///< Highlight name. STL_HIGHLIGHT = '#', ///< Highlight name.
STL_TABPAGENR = 'T', ///< Tab page label nr. STL_TABPAGENR = 'T', ///< Tab page label nr.
STL_TABCLOSENR = 'X', ///< Tab page close nr. STL_TABCLOSENR = 'X', ///< Tab page close nr.
STL_CLICK_CMD = '[', ///< Click region start.
}; };
/// C string containing all 'statusline' option flags /// C string containing all 'statusline' option flags
#define STL_ALL ((char_u[]) { \ #define STL_ALL ((char_u[]) { \
@ -257,7 +258,7 @@ enum {
STL_PREVIEWFLAG, STL_PREVIEWFLAG_ALT, STL_MODIFIED, STL_MODIFIED_ALT, \ STL_PREVIEWFLAG, STL_PREVIEWFLAG_ALT, STL_MODIFIED, STL_MODIFIED_ALT, \
STL_QUICKFIX, STL_PERCENTAGE, STL_ALTPERCENT, STL_ARGLISTSTAT, STL_PAGENUM, \ STL_QUICKFIX, STL_PERCENTAGE, STL_ALTPERCENT, STL_ARGLISTSTAT, STL_PAGENUM, \
STL_VIM_EXPR, STL_MIDDLEMARK, STL_TRUNCMARK, STL_USER_HL, STL_HIGHLIGHT, \ STL_VIM_EXPR, STL_MIDDLEMARK, STL_TRUNCMARK, STL_USER_HL, STL_HIGHLIGHT, \
STL_TABPAGENR, STL_TABCLOSENR, \ STL_TABPAGENR, STL_TABCLOSENR, STL_CLICK_CMD, \
0, \ 0, \
}) })

View File

@ -147,6 +147,9 @@ static foldinfo_T win_foldinfo; /* info for 'foldcolumn' */
*/ */
static schar_T *current_ScreenLine; static schar_T *current_ScreenLine;
StlClickDefinition *tab_page_click_defs = NULL;
long tab_page_click_defs_size = 0;
# define SCREEN_LINE(r, o, e, c, rl) screen_line((r), (o), (e), (c), (rl)) # define SCREEN_LINE(r, o, e, c, rl) screen_line((r), (o), (e), (c), (rl))
#ifdef INCLUDE_GENERATED_DECLARATIONS #ifdef INCLUDE_GENERATED_DECLARATIONS
# include "screen.c.generated.h" # include "screen.c.generated.h"
@ -5009,8 +5012,8 @@ win_redr_custom (
char_u *stl; char_u *stl;
char_u *p; char_u *p;
struct stl_hlrec hltab[STL_MAX_ITEM]; struct stl_hlrec hltab[STL_MAX_ITEM];
struct stl_hlrec tabtab[STL_MAX_ITEM]; StlClickRecord tabtab[STL_MAX_ITEM];
int use_sandbox = FALSE; int use_sandbox = false;
win_T *ewp; win_T *ewp;
int p_crb_save; int p_crb_save;
@ -5126,20 +5129,24 @@ win_redr_custom (
screen_puts(p >= buf + len ? (char_u *)"" : p, row, col, curattr); screen_puts(p >= buf + len ? (char_u *)"" : p, row, col, curattr);
if (wp == NULL) { if (wp == NULL) {
/* Fill the TabPageIdxs[] array for clicking in the tab pagesline. */ // Fill the tab_page_click_defs array for clicking in the tab pages line.
col = 0; col = 0;
len = 0; len = 0;
p = buf; p = buf;
fillchar = 0; StlClickDefinition cur_click_def = {
.type = kStlClickDisabled,
};
for (n = 0; tabtab[n].start != NULL; n++) { for (n = 0; tabtab[n].start != NULL; n++) {
len += vim_strnsize(p, (int)(tabtab[n].start - p)); len += vim_strnsize(p, (int)(tabtab[n].start - (char *) p));
while (col < len) while (col < len) {
TabPageIdxs[col++] = fillchar; tab_page_click_defs[col++] = cur_click_def;
p = tabtab[n].start; }
fillchar = tabtab[n].userhl; p = (char_u *) tabtab[n].start;
cur_click_def = tabtab[n].def;
}
while (col < Columns) {
tab_page_click_defs[col++] = cur_click_def;
} }
while (col < Columns)
TabPageIdxs[col++] = fillchar;
} }
theend: theend:
@ -5958,9 +5965,9 @@ void screenalloc(bool doclear)
sattr_T *new_ScreenAttrs; sattr_T *new_ScreenAttrs;
unsigned *new_LineOffset; unsigned *new_LineOffset;
char_u *new_LineWraps; char_u *new_LineWraps;
short *new_TabPageIdxs; StlClickDefinition *new_tab_page_click_defs;
static int entered = FALSE; /* avoid recursiveness */ static bool entered = false; // avoid recursiveness
static int done_outofmem_msg = FALSE; /* did outofmem message */ static bool done_outofmem_msg = false;
int retry_count = 0; int retry_count = 0;
const bool l_enc_utf8 = enc_utf8; const bool l_enc_utf8 = enc_utf8;
const int l_enc_dbcs = enc_dbcs; const int l_enc_dbcs = enc_dbcs;
@ -6033,7 +6040,8 @@ retry:
new_ScreenAttrs = xmalloc((size_t)((Rows + 1) * Columns * sizeof(sattr_T))); new_ScreenAttrs = xmalloc((size_t)((Rows + 1) * Columns * sizeof(sattr_T)));
new_LineOffset = xmalloc((size_t)(Rows * sizeof(unsigned))); new_LineOffset = xmalloc((size_t)(Rows * sizeof(unsigned)));
new_LineWraps = xmalloc((size_t)(Rows * sizeof(char_u))); new_LineWraps = xmalloc((size_t)(Rows * sizeof(char_u)));
new_TabPageIdxs = xmalloc((size_t)(Columns * sizeof(short))); new_tab_page_click_defs = xcalloc(
(size_t) Columns, sizeof(*new_tab_page_click_defs));
FOR_ALL_TAB_WINDOWS(tp, wp) { FOR_ALL_TAB_WINDOWS(tp, wp) {
win_alloc_lines(wp); win_alloc_lines(wp);
@ -6051,7 +6059,7 @@ retry:
|| new_ScreenAttrs == NULL || new_ScreenAttrs == NULL
|| new_LineOffset == NULL || new_LineOffset == NULL
|| new_LineWraps == NULL || new_LineWraps == NULL
|| new_TabPageIdxs == NULL || new_tab_page_click_defs == NULL
|| outofmem) { || outofmem) {
if (ScreenLines != NULL || !done_outofmem_msg) { if (ScreenLines != NULL || !done_outofmem_msg) {
/* guess the size */ /* guess the size */
@ -6077,8 +6085,8 @@ retry:
new_LineOffset = NULL; new_LineOffset = NULL;
xfree(new_LineWraps); xfree(new_LineWraps);
new_LineWraps = NULL; new_LineWraps = NULL;
xfree(new_TabPageIdxs); xfree(new_tab_page_click_defs);
new_TabPageIdxs = NULL; new_tab_page_click_defs = NULL;
} else { } else {
done_outofmem_msg = FALSE; done_outofmem_msg = FALSE;
@ -6157,7 +6165,8 @@ retry:
ScreenAttrs = new_ScreenAttrs; ScreenAttrs = new_ScreenAttrs;
LineOffset = new_LineOffset; LineOffset = new_LineOffset;
LineWraps = new_LineWraps; LineWraps = new_LineWraps;
TabPageIdxs = new_TabPageIdxs; tab_page_click_defs = new_tab_page_click_defs;
tab_page_click_defs_size = Columns;
/* It's important that screen_Rows and screen_Columns reflect the actual /* It's important that screen_Rows and screen_Columns reflect the actual
* size of ScreenLines[]. Set them before calling anything. */ * size of ScreenLines[]. Set them before calling anything. */
@ -6196,7 +6205,25 @@ void free_screenlines(void)
xfree(ScreenAttrs); xfree(ScreenAttrs);
xfree(LineOffset); xfree(LineOffset);
xfree(LineWraps); xfree(LineWraps);
xfree(TabPageIdxs); clear_tab_page_click_defs(tab_page_click_defs, tab_page_click_defs_size);
xfree(tab_page_click_defs);
}
/// Clear tab_page_click_defs table
///
/// @param[out] tpcd Table to clear.
/// @param[in] tpcd_size Size of the table.
void clear_tab_page_click_defs(StlClickDefinition *const tpcd,
const long tpcd_size)
{
if (tpcd != NULL) {
for (long i = 0; i < tpcd_size; i++) {
if (i == 0 || tpcd[i].cmd != tpcd[i - 1].cmd) {
xfree(tpcd[i].cmd);
}
}
}
memset(tpcd, 0, (size_t) tpcd_size * sizeof(tpcd[0]));
} }
void screenclear(void) void screenclear(void)
@ -6804,9 +6831,9 @@ static void draw_tabline(void)
return; return;
/* Init TabPageIdxs[] to zero: Clicking outside of tabs has no effect. */ // Init TabPageIdxs[] to zero: Clicking outside of tabs has no effect.
for (scol = 0; scol < Columns; ++scol) assert(Columns == tab_page_click_defs_size);
TabPageIdxs[scol] = 0; clear_tab_page_click_defs(tab_page_click_defs, tab_page_click_defs_size);
/* Use the 'tabline' option if it's set. */ /* Use the 'tabline' option if it's set. */
if (*p_tal != NUL) { if (*p_tal != NUL) {
@ -6904,11 +6931,16 @@ static void draw_tabline(void)
} }
screen_putchar(' ', 0, col++, attr); screen_putchar(' ', 0, col++, attr);
/* Store the tab page number in TabPageIdxs[], so that // Store the tab page number in tab_page_click_defs[], so that
* jump_to_mouse() knows where each one is. */ // jump_to_mouse() knows where each one is.
++tabcount; tabcount++;
while (scol < col) while (scol < col) {
TabPageIdxs[scol++] = tabcount; tab_page_click_defs[scol++] = (StlClickDefinition) {
.type = kStlClickTabSwitch,
.tabnr = tabcount,
.cmd = NULL,
};
}
} }
if (use_sep_chars) if (use_sep_chars)
@ -6920,7 +6952,11 @@ static void draw_tabline(void)
/* Put an "X" for closing the current tab if there are several. */ /* Put an "X" for closing the current tab if there are several. */
if (first_tabpage->tp_next != NULL) { if (first_tabpage->tp_next != NULL) {
screen_putchar('X', 0, (int)Columns - 1, attr_nosel); screen_putchar('X', 0, (int)Columns - 1, attr_nosel);
TabPageIdxs[Columns - 1] = -999; tab_page_click_defs[Columns - 1] = (StlClickDefinition) {
.type = kStlClickTabClose,
.tabnr = 999,
.cmd = NULL,
};
} }
} }

View File

@ -16,6 +16,29 @@
#define NOT_VALID 40 /* buffer needs complete redraw */ #define NOT_VALID 40 /* buffer needs complete redraw */
#define CLEAR 50 /* screen messed up, clear it */ #define CLEAR 50 /* screen messed up, clear it */
/// 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.
kStlClickCmd, ///< Run VimL command.
} type; ///< Type of the click.
int tabnr; ///< Tab page number.
char *cmd; ///< Command to execute.
} StlClickDefinition;
/// Used for tabline clicks
typedef struct {
StlClickDefinition def; ///< Click definition.
const char *start; ///< Location where region starts.
} StlClickRecord;
/// Array defining what should be done when tabline is clicked
extern StlClickDefinition *tab_page_click_defs;
/// Size of the tab_page_click_defs array
extern long tab_page_click_defs_size;
#ifdef INCLUDE_GENERATED_DECLARATIONS #ifdef INCLUDE_GENERATED_DECLARATIONS
# include "screen.h.generated.h" # include "screen.h.generated.h"