diff options
author | Famiu Haque <famiuhaque@protonmail.com> | 2022-05-23 19:11:24 +0600 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-05-23 06:11:24 -0700 |
commit | 4c6626f03dc645a426c1e63ca372b96f1073581b (patch) | |
tree | e0ab7136f9b7c32a827ad59bbe1d0cc3f6b7dd65 | |
parent | 9e1ee9fb1d747facb6fa97a32dc5e22c7dfcb346 (diff) | |
download | rneovim-4c6626f03dc645a426c1e63ca372b96f1073581b.tar.gz rneovim-4c6626f03dc645a426c1e63ca372b96f1073581b.tar.bz2 rneovim-4c6626f03dc645a426c1e63ca372b96f1073581b.zip |
feat: click support for 'statusline', 'winbar' #18650
The mouseclick item "%@" is now supported by 'statusline' and 'winbar'.
Previously it was only supported by 'tabline'.
-rw-r--r-- | runtime/doc/options.txt | 4 | ||||
-rw-r--r-- | src/nvim/buffer_defs.h | 12 | ||||
-rw-r--r-- | src/nvim/grid_defs.h | 18 | ||||
-rw-r--r-- | src/nvim/mouse.c | 4 | ||||
-rw-r--r-- | src/nvim/normal.c | 153 | ||||
-rw-r--r-- | src/nvim/screen.c | 82 | ||||
-rw-r--r-- | src/nvim/screen.h | 18 | ||||
-rw-r--r-- | src/nvim/window.c | 47 | ||||
-rw-r--r-- | test/functional/ui/global_statusline_spec.lua | 260 | ||||
-rw-r--r-- | test/functional/ui/statusline_spec.lua | 317 |
10 files changed, 529 insertions, 386 deletions
diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 9015f0a97e..77aa294027 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -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 diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 9efa540210..22eef020a1 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -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) diff --git a/src/nvim/grid_defs.h b/src/nvim/grid_defs.h index a97a1c2c39..f2427f8aef 100644 --- a/src/nvim/grid_defs.h +++ b/src/nvim/grid_defs.h @@ -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 diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c index 8736c73080..347adc589f 100644 --- a/src/nvim/mouse.c +++ b/src/nvim/mouse.c @@ -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; diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 0c55c64b5c..7c7a042eac 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -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. diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 770099ab65..dbe6cf2077 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -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) { diff --git a/src/nvim/screen.h b/src/nvim/screen.h index f3beeff8a9..0afbc24538 100644 --- a/src/nvim/screen.h +++ b/src/nvim/screen.h @@ -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); diff --git a/src/nvim/window.c b/src/nvim/window.c index 4076bb2531..835aba204d 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -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; + } } } } diff --git a/test/functional/ui/global_statusline_spec.lua b/test/functional/ui/global_statusline_spec.lua deleted file mode 100644 index 369c4a31f1..0000000000 --- a/test/functional/ui/global_statusline_spec.lua +++ /dev/null @@ -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) diff --git a/test/functional/ui/statusline_spec.lua b/test/functional/ui/statusline_spec.lua new file mode 100644 index 0000000000..3e1b284856 --- /dev/null +++ b/test/functional/ui/statusline_spec.lua @@ -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) |