feat: click support for 'statusline', 'winbar' #18650

The mouseclick item "%@" is now supported by 'statusline' and 'winbar'.
Previously it was only supported by 'tabline'.
This commit is contained in:
Famiu Haque 2022-05-23 19:11:24 +06:00 committed by GitHub
parent 9e1ee9fb1d
commit 4c6626f03d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 529 additions and 386 deletions

View File

@ -6009,7 +6009,7 @@ A jump table for the options with a short description can be found at |Q_op|.
the label, e.g.: %3Xclose%X. Use %999X for a "close current
tab" label. Clicking this label with left mouse button closes
specified tab page.
@ N For 'tabline': start of execute function label. Use %X or %T to
@ N Start of execute function label. Use %X or %T to
end the label, e.g.: %10@SwitchBuffer@foo.c%X. Clicking this
label runs specified function: in the example when clicking once
using left mouse button on "foo.c" "SwitchBuffer(10, 1, 'l',
@ -6033,8 +6033,6 @@ A jump table for the options with a short description can be found at |Q_op|.
is a bug that denotes that new mouse button recognition was
added without modifying code that reacts on mouse clicks on
this label.
Note: to test whether your version of Neovim contains this
feature use `has('tablineat')`.
< - Where to truncate line if too long. Default is at the start.
No width fields allowed.
= - Separation point between alignment sections. Each section will

View File

@ -6,6 +6,8 @@
// 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.
@ -1492,6 +1494,16 @@ struct window_S {
// Location list reference used in the location list window.
// In a non-location list window, w_llist_ref is NULL.
qf_info_T *w_llist_ref;
// Status line click definitions
StlClickDefinition *w_status_click_defs;
// Size of the w_status_click_defs array
size_t w_status_click_defs_size;
// Window bar click definitions
StlClickDefinition *w_winbar_click_defs;
// Size of the w_winbar_click_defs array
size_t w_winbar_click_defs_size;
};
static inline int win_hl_attr(win_T *wp, int hlf)

View File

@ -111,4 +111,22 @@ 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;
#endif // NVIM_GRID_DEFS_H

View File

@ -241,8 +241,8 @@ retnomove:
}
curwin->w_cursor.lnum = curwin->w_topline;
} else if (on_status_line && which_button == MOUSE_LEFT) {
if (dragwin != NULL) {
} else if (on_status_line) {
if (which_button == MOUSE_LEFT && dragwin != NULL) {
// Drag the status line
count = row - dragwin->w_winrow - dragwin->w_height + 1
- on_status_line;

View File

@ -32,6 +32,7 @@
#include "nvim/fold.h"
#include "nvim/getchar.h"
#include "nvim/globals.h"
#include "nvim/grid_defs.h"
#include "nvim/indent.h"
#include "nvim/keycodes.h"
#include "nvim/log.h"
@ -1432,6 +1433,63 @@ static void move_tab_to_mouse(void)
}
}
/// Call click definition function for column "col" in the "click_defs" array for button
/// "which_button".
static void call_click_def_func(StlClickDefinition *click_defs, int col, int which_button)
{
typval_T argv[] = {
{
.v_lock = VAR_FIXED,
.v_type = VAR_NUMBER,
.vval = {
.v_number = (varnumber_T)click_defs[col].tabnr
},
},
{
.v_lock = VAR_FIXED,
.v_type = VAR_NUMBER,
.vval = {
.v_number = ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_4CLICK
? 4
: ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_3CLICK
? 3
: ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK
? 2
: 1)))
},
},
{
.v_lock = VAR_FIXED,
.v_type = VAR_STRING,
.vval = {
.v_string = (which_button == MOUSE_LEFT
? "l"
: (which_button == MOUSE_RIGHT
? "r"
: (which_button == MOUSE_MIDDLE
? "m"
: "?")))
},
},
{
.v_lock = VAR_FIXED,
.v_type = VAR_STRING,
.vval = {
.v_string = (char[]) {
(char)(mod_mask & MOD_MASK_SHIFT ? 's' : ' '),
(char)(mod_mask & MOD_MASK_CTRL ? 'c' : ' '),
(char)(mod_mask & MOD_MASK_ALT ? 'a' : ' '),
(char)(mod_mask & MOD_MASK_META ? 'm' : ' '),
NUL
}
},
}
};
typval_T rettv;
(void)call_vim_function(click_defs[col].func, ARRAY_SIZE(argv), argv, &rettv);
tv_clear(&rettv);
}
/// Do the appropriate action for the current mouse click in the current mode.
/// Not used for Command-line mode.
///
@ -1481,6 +1539,7 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent)
int jump_flags = 0; // flags for jump_to_mouse()
pos_T start_visual;
bool moved; // Has cursor moved?
bool in_winbar; // mouse in window bar
bool in_status_line; // mouse in status line
static bool in_tab_line = false; // mouse clicked in tab line
bool in_sep_line; // mouse in vertical separator line
@ -1711,66 +1770,10 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent)
}
}
break;
case kStlClickFuncRun: {
typval_T argv[] = {
{
.v_lock = VAR_FIXED,
.v_type = VAR_NUMBER,
.vval = {
.v_number = (varnumber_T)tab_page_click_defs[mouse_col].tabnr
},
},
{
.v_lock = VAR_FIXED,
.v_type = VAR_NUMBER,
.vval = {
.v_number = ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_4CLICK
? 4
: ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_3CLICK
? 3
: ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK
? 2
: 1)))
},
},
{
.v_lock = VAR_FIXED,
.v_type = VAR_STRING,
.vval = {
.v_string = (which_button == MOUSE_LEFT
? "l"
: (which_button == MOUSE_RIGHT
? "r"
: (which_button == MOUSE_MIDDLE
? "m"
: "?")))
},
},
{
.v_lock = VAR_FIXED,
.v_type = VAR_STRING,
.vval = {
.v_string = (char[]) {
(char)(mod_mask & MOD_MASK_SHIFT ? 's' : ' '),
(char)(mod_mask & MOD_MASK_CTRL ? 'c' : ' '),
(char)(mod_mask & MOD_MASK_ALT ? 'a' : ' '),
(char)(mod_mask & MOD_MASK_META ? 'm' : ' '),
NUL
}
},
}
};
typval_T rettv;
funcexe_T funcexe = FUNCEXE_INIT;
funcexe.firstline = curwin->w_cursor.lnum;
funcexe.lastline = curwin->w_cursor.lnum;
funcexe.evaluate = true;
(void)call_func(tab_page_click_defs[mouse_col].func, -1,
&rettv, ARRAY_SIZE(argv), argv, &funcexe);
tv_clear(&rettv);
case kStlClickFuncRun:
call_click_def_func(tab_page_click_defs, mouse_col, which_button);
break;
}
}
}
return true;
} else if (is_drag && in_tab_line) {
@ -1840,15 +1843,39 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent)
oap == NULL ? NULL : &(oap->inclusive),
which_button);
// A click in the window bar has no side effects.
if (jump_flags & MOUSE_WINBAR) {
return false;
}
moved = (jump_flags & CURSOR_MOVED);
in_winbar = (jump_flags & MOUSE_WINBAR);
in_status_line = (jump_flags & IN_STATUS_LINE);
in_sep_line = (jump_flags & IN_SEP_LINE);
if ((in_winbar || in_status_line) && is_click) {
// Handle click event on window bar or status lin
int click_grid = mouse_grid;
int click_row = mouse_row;
int click_col = mouse_col;
win_T *wp = mouse_find_win(&click_grid, &click_row, &click_col);
StlClickDefinition *click_defs = in_status_line ? wp->w_status_click_defs
: wp->w_winbar_click_defs;
if (click_defs != NULL) {
switch (click_defs[click_col].type) {
case kStlClickDisabled:
break;
case kStlClickFuncRun:
call_click_def_func(click_defs, click_col, which_button);
break;
default:
assert(false && "winbar and statusline only support %@ for clicks");
break;
}
}
return false;
} else if (in_winbar) {
// A drag or release event in the window bar has no side effects.
return false;
}
// When jumping to another window, clear a pending operator. That's a bit
// friendlier than beeping and not jumping to that window.

View File

@ -88,6 +88,7 @@
#include "nvim/fold.h"
#include "nvim/garray.h"
#include "nvim/getchar.h"
#include "nvim/grid_defs.h"
#include "nvim/highlight.h"
#include "nvim/highlight_group.h"
#include "nvim/indent.h"
@ -5337,11 +5338,27 @@ static void win_redr_custom(win_T *wp, bool draw_winbar, bool draw_ruler)
attr = (wp == curwin) ? HL_ATTR(HLF_WBR) : HL_ATTR(HLF_WBRNC);
maxwidth = wp->w_width_inner;
use_sandbox = was_set_insecurely(wp, "winbar", 0);
stl_clear_click_defs(wp->w_winbar_click_defs, wp->w_winbar_click_defs_size);
// Allocate / resize the click definitions array for winbar if needed.
if (wp->w_winbar_height && wp->w_winbar_click_defs_size < (size_t)maxwidth) {
xfree(wp->w_winbar_click_defs);
wp->w_winbar_click_defs_size = (size_t)maxwidth;
wp->w_winbar_click_defs = xcalloc(wp->w_winbar_click_defs_size, sizeof(StlClickRecord));
}
} else {
row = is_stl_global ? (Rows - 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);
// Allocate / resize the click definitions array for statusline if needed.
if (wp->w_status_click_defs_size < (size_t)maxwidth) {
xfree(wp->w_status_click_defs);
wp->w_status_click_defs_size = maxwidth;
wp->w_status_click_defs = xcalloc(wp->w_status_click_defs_size, sizeof(StlClickRecord));
}
if (draw_ruler) {
stl = p_ruf;
// advance past any leading group spec - implicit in ru_col
@ -5445,26 +5462,38 @@ static void win_redr_custom(win_T *wp, bool draw_winbar, bool draw_ruler)
grid_puts_line_flush(false);
if (wp == NULL) {
// Fill the tab_page_click_defs array for clicking in the tab pages line.
col = 0;
len = 0;
p = buf;
StlClickDefinition cur_click_def = {
.type = kStlClickDisabled,
};
for (n = 0; tabtab[n].start != NULL; n++) {
len += vim_strnsize(p, (int)(tabtab[n].start - (char *)p));
while (col < len) {
tab_page_click_defs[col++] = cur_click_def;
}
p = (char_u *)tabtab[n].start;
cur_click_def = tabtab[n].def;
// 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;
if (click_defs == NULL) {
goto theend;
}
col = 0;
len = 0;
p = buf;
StlClickDefinition cur_click_def = {
.type = kStlClickDisabled,
};
for (n = 0; tabtab[n].start != NULL; n++) {
len += vim_strnsize(p, (int)(tabtab[n].start - (char *)p));
while (col < len) {
click_defs[col++] = cur_click_def;
}
while (col < Columns) {
tab_page_click_defs[col++] = cur_click_def;
p = (char_u *)tabtab[n].start;
cur_click_def = tabtab[n].def;
if ((wp != NULL) && !(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 < maxwidth) {
click_defs[col++] = cur_click_def;
}
theend:
entered = false;
@ -5572,7 +5601,6 @@ void check_for_delay(bool check_msg_scroll)
}
}
/// Resize the screen to Rows and Columns.
///
/// Allocate default_grid.chars[] and other grid arrays.
@ -5641,7 +5669,7 @@ retry:
StlClickDefinition *new_tab_page_click_defs =
xcalloc((size_t)Columns, sizeof(*new_tab_page_click_defs));
clear_tab_page_click_defs(tab_page_click_defs, tab_page_click_defs_size);
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;
@ -5672,19 +5700,19 @@ retry:
resizing = false;
}
/// Clear tab_page_click_defs table
/// 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 clear_tab_page_click_defs(StlClickDefinition *const tpcd, const long tpcd_size)
void stl_clear_click_defs(StlClickDefinition *const click_defs, const long click_defs_size)
{
if (tpcd != NULL) {
for (long i = 0; i < tpcd_size; i++) {
if (i == 0 || tpcd[i].func != tpcd[i - 1].func) {
xfree(tpcd[i].func);
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(tpcd, 0, (size_t)tpcd_size * sizeof(tpcd[0]));
memset(click_defs, 0, (size_t)click_defs_size * sizeof(click_defs[0]));
}
}
@ -6200,7 +6228,7 @@ void draw_tabline(void)
// Init TabPageIdxs[] to zero: Clicking outside of tabs has no effect.
assert(Columns == tab_page_click_defs_size);
clear_tab_page_click_defs(tab_page_click_defs, 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) {

View File

@ -30,24 +30,6 @@ typedef enum {
// Maximum columns for terminal highlight attributes
#define TERM_ATTRS_MAX 1024
/// 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;
/// Array defining what should be done when tabline is clicked
EXTERN StlClickDefinition *tab_page_click_defs INIT(= NULL);

View File

@ -5119,6 +5119,12 @@ 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, wp->w_status_click_defs_size);
xfree(wp->w_status_click_defs);
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
// freed memory is re-used for another window.
FOR_ALL_BUFFERS(buf) {
@ -6612,6 +6618,23 @@ void last_status(bool morewin)
global_stl_height() > 0);
}
// Remove status line from window, replacing it with a horizontal separator if needed.
static void win_remove_status_line(win_T *wp, bool add_hsep)
{
wp->w_status_height = 0;
if (add_hsep) {
wp->w_hsep_height = 1;
} else {
win_new_height(wp, wp->w_height + STATUS_HEIGHT);
}
comp_col();
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;
}
// Look for resizable frames and take lines from them to make room for the statusline
static void resize_frame_for_status(frame_T *fr)
{
@ -6652,10 +6675,7 @@ static void last_status_rec(frame_T *fr, bool statusline, bool is_stl_global)
if (is_last) {
if (wp->w_status_height != 0 && (!statusline || is_stl_global)) {
// Remove status line
wp->w_status_height = 0;
win_new_height(wp, wp->w_height + STATUS_HEIGHT);
comp_col();
win_remove_status_line(wp, false);
} else if (wp->w_status_height == 0 && !is_stl_global && statusline) {
// Add statusline to window if needed
wp->w_status_height = STATUS_HEIGHT;
@ -6665,9 +6685,7 @@ static void last_status_rec(frame_T *fr, bool statusline, bool is_stl_global)
} else if (wp->w_status_height != 0 && is_stl_global) {
// If statusline is global and the window has a statusline, replace it with a horizontal
// separator
wp->w_status_height = 0;
wp->w_hsep_height = 1;
comp_col();
win_remove_status_line(wp, true);
} else if (wp->w_status_height == 0 && !is_stl_global) {
// If statusline isn't global and the window doesn't have a statusline, re-add it
wp->w_status_height = STATUS_HEIGHT;
@ -6675,13 +6693,8 @@ static void last_status_rec(frame_T *fr, bool statusline, bool is_stl_global)
comp_col();
}
redraw_all_later(SOME_VALID);
} else if (fr->fr_layout == FR_COL) {
// For a column frame, recursively call this function for all child frames
FOR_ALL_FRAMES(fp, fr->fr_child) {
last_status_rec(fp, statusline, is_stl_global);
}
} else {
// For a row frame, recursively call this function for all child frames
// For a column or row frame, recursively call this function for all child frames
FOR_ALL_FRAMES(fp, fr->fr_child) {
last_status_rec(fp, statusline, is_stl_global);
}
@ -6697,6 +6710,14 @@ void set_winbar(void)
wp->w_winbar_height = winbar_height;
win_set_inner_size(wp);
wp->w_redr_winbar = winbar_height;
if (winbar_height == 0) {
// When removing winbar, deallocate the w_winbar_click_defs array
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;
}
}
}
}

View File

@ -1,260 +0,0 @@
local helpers = require('test.functional.helpers')(after_each)
local Screen = require('test.functional.ui.screen')
local clear, command, feed = helpers.clear, helpers.command, helpers.feed
local eq, funcs, meths = helpers.eq, helpers.funcs, helpers.meths
describe('global statusline', function()
local screen
before_each(function()
clear()
screen = Screen.new(60, 16)
screen:attach()
command('set laststatus=3')
command('set ruler')
end)
it('works', function()
screen:expect{grid=[[
^ |
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{2:[No Name] 0,0-1 All}|
|
]], attr_ids={
[1] = {bold = true, foreground = Screen.colors.Blue1};
[2] = {bold = true, reverse = true};
}}
feed('i<CR><CR>')
screen:expect{grid=[[
|
|
^ |
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{2:[No Name] [+] 3,1 All}|
{3:-- INSERT --} |
]], attr_ids={
[1] = {bold = true, foreground = Screen.colors.Blue};
[2] = {bold = true, reverse = true};
[3] = {bold = true};
}}
end)
it('works with splits', function()
command('vsplit | split | vsplit | vsplit | wincmd l | split | 2wincmd l | split')
screen:expect{grid=[[
^ |
{2:~ }{2:~ }{2:~}{2:~ }|
{2:~ }{2:~ }{2:~}{2:~ }|
{2:~ }{2:~ }{2:~}{2:~ }|
{2:~ }{2:~}{2:~ }|
{2:~ } {2:~}{2:~ }|
{2:~ }{2:~ }{2:~}{2:~ }|
{2:~ }{2:~ }{2:~}|
{2:~ }{2:~ }{2:~} |
{2:~ }|
{2:~ }|
{2:~ }{2:~ }|
{2:~ }{2:~ }|
{2:~ }{2:~ }|
{3:[No Name] 0,0-1 All}|
|
]], attr_ids={
[1] = {reverse = true};
[2] = {bold = true, foreground = Screen.colors.Blue1};
[3] = {bold = true, reverse = true};
}}
end)
it('works when switching between values of laststatus', function()
command('set laststatus=1')
screen:expect{grid=[[
^ |
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
0,0-1 All |
]], attr_ids={
[1] = {foreground = Screen.colors.Blue, bold = true};
}}
command('set laststatus=3')
screen:expect{grid=[[
^ |
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{2:[No Name] 0,0-1 All}|
|
]], attr_ids={
[1] = {foreground = Screen.colors.Blue, bold = true};
[2] = {reverse = true, bold = true};
}}
command('vsplit | split | vsplit | vsplit | wincmd l | split | 2wincmd l | split')
command('set laststatus=2')
screen:expect{grid=[[
^ |
{2:~ }{2:~ }{2:~}{2:~ }|
{2:~ }{2:~ }{2:~}{2:~ }|
{2:~ }{2:~ }{2:~}{2:~ }|
{2:~ }{1:< Name] 0,0-1 }{2:~}{2:~ }|
{2:~ } {2:~}{2:~ }|
{2:~ }{2:~ }{2:~}{2:~ }|
{2:~ }{2:~ }{2:~}{3:<No Name] 0,0-1 All}|
{2:~ }{2:~ }{2:~} |
{1:<No Name] 0,0-1 All < Name] 0,0-1 <}{2:~ }|
{2:~ }|
{2:~ }{2:~ }|
{2:~ }{2:~ }|
{2:~ }{2:~ }|
{1:[No Name] 0,0-1 All <No Name] 0,0-1 All}|
|
]], attr_ids={
[1] = {reverse = true};
[2] = {foreground = Screen.colors.Blue, bold = true};
[3] = {reverse = true, bold = true};
}}
command('set laststatus=3')
screen:expect{grid=[[
^ |
{2:~ }{2:~ }{2:~}{2:~ }|
{2:~ }{2:~ }{2:~}{2:~ }|
{2:~ }{2:~ }{2:~}{2:~ }|
{2:~ }{2:~}{2:~ }|
{2:~ } {2:~}{2:~ }|
{2:~ }{2:~ }{2:~}{2:~ }|
{2:~ }{2:~ }{2:~}|
{2:~ }{2:~ }{2:~} |
{2:~ }|
{2:~ }|
{2:~ }{2:~ }|
{2:~ }{2:~ }|
{2:~ }{2:~ }|
{3:[No Name] 0,0-1 All}|
|
]], attr_ids={
[1] = {reverse = true};
[2] = {foreground = Screen.colors.Blue, bold = true};
[3] = {reverse = true, bold = true};
}}
command('set laststatus=0')
screen:expect{grid=[[
^ |
{2:~ }{2:~ }{2:~}{2:~ }|
{2:~ }{2:~ }{2:~}{2:~ }|
{2:~ }{2:~ }{2:~}{2:~ }|
{2:~ }{1:< Name] 0,0-1 }{2:~}{2:~ }|
{2:~ } {2:~}{2:~ }|
{2:~ }{2:~ }{2:~}{2:~ }|
{2:~ }{2:~ }{2:~}{3:<No Name] 0,0-1 All}|
{2:~ }{2:~ }{2:~} |
{1:<No Name] 0,0-1 All < Name] 0,0-1 <}{2:~ }|
{2:~ }|
{2:~ }{2:~ }|
{2:~ }{2:~ }|
{2:~ }{2:~ }|
{2:~ }{2:~ }|
0,0-1 All |
]], attr_ids={
[1] = {reverse = true};
[2] = {foreground = Screen.colors.Blue, bold = true};
[3] = {reverse = true, bold = true};
}}
command('set laststatus=3')
screen:expect{grid=[[
^ |
{2:~ }{2:~ }{2:~}{2:~ }|
{2:~ }{2:~ }{2:~}{2:~ }|
{2:~ }{2:~ }{2:~}{2:~ }|
{2:~ }{2:~}{2:~ }|
{2:~ } {2:~}{2:~ }|
{2:~ }{2:~ }{2:~}{2:~ }|
{2:~ }{2:~ }{2:~}|
{2:~ }{2:~ }{2:~} |
{2:~ }|
{2:~ }|
{2:~ }{2:~ }|
{2:~ }{2:~ }|
{2:~ }{2:~ }|
{3:[No Name] 0,0-1 All}|
|
]], attr_ids={
[1] = {reverse = true};
[2] = {foreground = Screen.colors.Blue, bold = true};
[3] = {reverse = true, bold = true};
}}
end)
it('win_move_statusline() can reduce cmdheight to 1', function()
eq(1, meths.get_option('cmdheight'))
funcs.win_move_statusline(0, -1)
eq(2, meths.get_option('cmdheight'))
funcs.win_move_statusline(0, -1)
eq(3, meths.get_option('cmdheight'))
funcs.win_move_statusline(0, 1)
eq(2, meths.get_option('cmdheight'))
funcs.win_move_statusline(0, 1)
eq(1, meths.get_option('cmdheight'))
end)
it('mouse dragging can reduce cmdheight to 1', function()
command('set mouse=a')
meths.input_mouse('left', 'press', '', 0, 14, 10)
eq(1, meths.get_option('cmdheight'))
meths.input_mouse('left', 'drag', '', 0, 13, 10)
eq(2, meths.get_option('cmdheight'))
meths.input_mouse('left', 'drag', '', 0, 12, 10)
eq(3, meths.get_option('cmdheight'))
meths.input_mouse('left', 'drag', '', 0, 13, 10)
eq(2, meths.get_option('cmdheight'))
meths.input_mouse('left', 'drag', '', 0, 14, 10)
eq(1, meths.get_option('cmdheight'))
end)
end)

View File

@ -0,0 +1,317 @@
local helpers = require('test.functional.helpers')(after_each)
local Screen = require('test.functional.ui.screen')
local clear = helpers.clear
local command = helpers.command
local feed = helpers.feed
local eq = helpers.eq
local funcs = helpers.funcs
local meths = helpers.meths
local exec = helpers.exec
local exec_lua = helpers.exec_lua
local eval = helpers.eval
describe('statusline clicks', function()
local screen
before_each(function()
clear()
screen = Screen.new(40, 8)
screen:attach()
command('set laststatus=2')
exec([=[
function! MyClickFunc(minwid, clicks, button, mods)
let g:testvar = printf("%d %d %s", a:minwid, a:clicks, a:button)
endfunction
]=])
end)
it('works', function()
meths.set_option('statusline', 'Not clicky stuff %0@MyClickFunc@Clicky stuff%T')
meths.input_mouse('left', 'press', '', 0, 6, 17)
eq('0 1 l', eval("g:testvar"))
meths.input_mouse('right', 'press', '', 0, 6, 17)
eq('0 1 r', eval("g:testvar"))
end)
it('works for winbar', function()
meths.set_option('winbar', 'Not clicky stuff %0@MyClickFunc@Clicky stuff%T')
meths.input_mouse('left', 'press', '', 0, 0, 17)
eq('0 1 l', eval("g:testvar"))
meths.input_mouse('right', 'press', '', 0, 6, 17)
eq('0 1 r', eval("g:testvar"))
end)
it('works for winbar in floating window', function()
meths.open_win(0, true, { width=30, height=4, relative='editor', row=1, col=5,
border = "single" })
meths.set_option_value('winbar', 'Not clicky stuff %0@MyClickFunc@Clicky stuff%T',
{ scope = 'local' })
meths.input_mouse('left', 'press', '', 0, 2, 23)
eq('0 1 l', eval("g:testvar"))
end)
it('works when there are multiple windows', function()
command('split')
meths.set_option('statusline', 'Not clicky stuff %0@MyClickFunc@Clicky stuff%T')
meths.set_option('winbar', 'Not clicky stuff %0@MyClickFunc@Clicky stuff%T')
meths.input_mouse('left', 'press', '', 0, 0, 17)
eq('0 1 l', eval("g:testvar"))
meths.input_mouse('right', 'press', '', 0, 4, 17)
eq('0 1 r', eval("g:testvar"))
meths.input_mouse('middle', 'press', '', 0, 3, 17)
eq('0 1 m', eval("g:testvar"))
meths.input_mouse('left', 'press', '', 0, 6, 17)
eq('0 1 l', eval("g:testvar"))
end)
it('works with Lua function', function()
exec_lua([[
function clicky_func(minwid, clicks, button, mods)
vim.g.testvar = string.format("%d %d %s", minwid, clicks, button)
end
]])
meths.set_option('statusline', 'Not clicky stuff %0@v:lua.clicky_func@Clicky stuff%T')
meths.input_mouse('left', 'press', '', 0, 6, 17)
eq('0 1 l', eval("g:testvar"))
end)
it('ignores unsupported click items', function()
command('tabnew | tabprevious')
meths.set_option('statusline', '%2TNot clicky stuff%T')
meths.input_mouse('left', 'press', '', 0, 6, 0)
eq(1, meths.get_current_tabpage().id)
meths.set_option('statusline', '%2XNot clicky stuff%X')
meths.input_mouse('left', 'press', '', 0, 6, 0)
eq(2, #meths.list_tabpages())
end)
end)
describe('global statusline', function()
local screen
before_each(function()
clear()
screen = Screen.new(60, 16)
screen:attach()
screen:set_default_attr_ids({
[1] = {bold = true, foreground = Screen.colors.Blue};
[2] = {bold = true, reverse = true};
[3] = {bold = true};
[4] = {reverse = true};
})
command('set laststatus=3')
command('set ruler')
end)
it('works', function()
screen:expect([[
^ |
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{2:[No Name] 0,0-1 All}|
|
]])
feed('i<CR><CR>')
screen:expect([[
|
|
^ |
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{2:[No Name] [+] 3,1 All}|
{3:-- INSERT --} |
]])
end)
it('works with splits', function()
command('vsplit | split | vsplit | vsplit | wincmd l | split | 2wincmd l | split')
screen:expect([[
^ |
{1:~ }{1:~ }{1:~}{1:~ }|
{1:~ }{1:~ }{1:~}{1:~ }|
{1:~ }{1:~ }{1:~}{1:~ }|
{1:~ }{1:~}{1:~ }|
{1:~ } {1:~}{1:~ }|
{1:~ }{1:~ }{1:~}{1:~ }|
{1:~ }{1:~ }{1:~}|
{1:~ }{1:~ }{1:~} |
{1:~ }|
{1:~ }|
{1:~ }{1:~ }|
{1:~ }{1:~ }|
{1:~ }{1:~ }|
{2:[No Name] 0,0-1 All}|
|
]])
end)
it('works when switching between values of laststatus', function()
command('set laststatus=1')
screen:expect([[
^ |
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
0,0-1 All |
]])
command('set laststatus=3')
screen:expect([[
^ |
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{2:[No Name] 0,0-1 All}|
|
]])
command('vsplit | split | vsplit | vsplit | wincmd l | split | 2wincmd l | split')
command('set laststatus=2')
screen:expect([[
^ |
{1:~ }{1:~ }{1:~}{1:~ }|
{1:~ }{1:~ }{1:~}{1:~ }|
{1:~ }{1:~ }{1:~}{1:~ }|
{1:~ }{4:< Name] 0,0-1 }{1:~}{1:~ }|
{1:~ } {1:~}{1:~ }|
{1:~ }{1:~ }{1:~}{1:~ }|
{1:~ }{1:~ }{1:~}{2:<No Name] 0,0-1 All}|
{1:~ }{1:~ }{1:~} |
{4:<No Name] 0,0-1 All < Name] 0,0-1 <}{1:~ }|
{1:~ }|
{1:~ }{1:~ }|
{1:~ }{1:~ }|
{1:~ }{1:~ }|
{4:[No Name] 0,0-1 All <No Name] 0,0-1 All}|
|
]])
command('set laststatus=3')
screen:expect([[
^ |
{1:~ }{1:~ }{1:~}{1:~ }|
{1:~ }{1:~ }{1:~}{1:~ }|
{1:~ }{1:~ }{1:~}{1:~ }|
{1:~ }{1:~}{1:~ }|
{1:~ } {1:~}{1:~ }|
{1:~ }{1:~ }{1:~}{1:~ }|
{1:~ }{1:~ }{1:~}|
{1:~ }{1:~ }{1:~} |
{1:~ }|
{1:~ }|
{1:~ }{1:~ }|
{1:~ }{1:~ }|
{1:~ }{1:~ }|
{2:[No Name] 0,0-1 All}|
|
]])
command('set laststatus=0')
screen:expect([[
^ |
{1:~ }{1:~ }{1:~}{1:~ }|
{1:~ }{1:~ }{1:~}{1:~ }|
{1:~ }{1:~ }{1:~}{1:~ }|
{1:~ }{4:< Name] 0,0-1 }{1:~}{1:~ }|
{1:~ } {1:~}{1:~ }|
{1:~ }{1:~ }{1:~}{1:~ }|
{1:~ }{1:~ }{1:~}{2:<No Name] 0,0-1 All}|
{1:~ }{1:~ }{1:~} |
{4:<No Name] 0,0-1 All < Name] 0,0-1 <}{1:~ }|
{1:~ }|
{1:~ }{1:~ }|
{1:~ }{1:~ }|
{1:~ }{1:~ }|
{1:~ }{1:~ }|
0,0-1 All |
]])
command('set laststatus=3')
screen:expect([[
^ |
{1:~ }{1:~ }{1:~}{1:~ }|
{1:~ }{1:~ }{1:~}{1:~ }|
{1:~ }{1:~ }{1:~}{1:~ }|
{1:~ }{1:~}{1:~ }|
{1:~ } {1:~}{1:~ }|
{1:~ }{1:~ }{1:~}{1:~ }|
{1:~ }{1:~ }{1:~}|
{1:~ }{1:~ }{1:~} |
{1:~ }|
{1:~ }|
{1:~ }{1:~ }|
{1:~ }{1:~ }|
{1:~ }{1:~ }|
{2:[No Name] 0,0-1 All}|
|
]])
end)
it('win_move_statusline() can reduce cmdheight to 1', function()
eq(1, meths.get_option('cmdheight'))
funcs.win_move_statusline(0, -1)
eq(2, meths.get_option('cmdheight'))
funcs.win_move_statusline(0, -1)
eq(3, meths.get_option('cmdheight'))
funcs.win_move_statusline(0, 1)
eq(2, meths.get_option('cmdheight'))
funcs.win_move_statusline(0, 1)
eq(1, meths.get_option('cmdheight'))
end)
it('mouse dragging can reduce cmdheight to 1', function()
command('set mouse=a')
meths.input_mouse('left', 'press', '', 0, 14, 10)
eq(1, meths.get_option('cmdheight'))
meths.input_mouse('left', 'drag', '', 0, 13, 10)
eq(2, meths.get_option('cmdheight'))
meths.input_mouse('left', 'drag', '', 0, 12, 10)
eq(3, meths.get_option('cmdheight'))
meths.input_mouse('left', 'drag', '', 0, 13, 10)
eq(2, meths.get_option('cmdheight'))
meths.input_mouse('left', 'drag', '', 0, 14, 10)
eq(1, meths.get_option('cmdheight'))
end)
end)