patch 9.2.0407: tabpanel: A few issues with the tabpanel

Problem:  Several issues around the tabpanel scrollbar:
          1. :set tabpanelopt= completion did not offer "scroll" and
              "scrollbar".
          2. gt/gT and other tab switches did not update the scrollbar
              thumb; the current tab could move outside the visible
              panel range without the view following.
          3. When tpl_scroll_offset was at its maximum, the thumb's
              bottom did not reach the last screen row due to integer
              truncation in thumb_top (e.g. 31 tabs on 24 rows + :tablast
              left a one-row gap).
          4. For align:right the scrollbar was drawn on the panel's
              left edge (adjacent to the buffer area), which breaks the
              common convention that a vertical scrollbar sits on the
              right.
Solution: - Add "scroll" and "scrollbar" to the 'tabpanelopt' expansion
            list.  Cover the completion in test_options.vim and extend
            util/gen_opt_test.vim with the new valid/invalid values;
            drop the now-redundant acceptance test from
            test_tabpanel.vim.
          - In draw_tabpanel(), remember the last-drawn curtab and,
            when it changes, adjust tpl_scroll_offset so curtab_row
            falls inside [offset, offset + Rows).  Mouse wheel and
            drag leave curtab unchanged, so the user's chosen offset
            is preserved.
          - In draw_tabpanel_scrollbar(), compute thumb_top as
            (Rows - thumb_height) * tpl_scroll_offset
            / (tpl_total_rows - Rows), mirroring the mapping already
            used by tabpanel_drag_scrollbar().  This guarantees the
            thumb's bottom reaches the last row at the maximum offset.
          - In draw_tabpanel(), place the scrollbar at the tabpanel's
            right edge for both align:left and align:right (previously
            align:right put it on the panel's left edge next to the
            vertical separator).  For align:right this means the
            scrollbar now sits at the screen's right edge.
          - Update :h tabpanel-scroll to describe the new, align-
            independent placement.
          - Add Test_tabpanel_scrollbar_follows_curtab() and
            Test_tabpanel_scrollbar_reaches_bottom() to exercise the
            regressions fixed by items 2 and 3.

closes: #20052

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Hirohito Higashi <h.east.727@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
Hirohito Higashi
2026-04-27 21:14:46 +00:00
committed by Christian Brabandt
parent 7e76514896
commit 2d43240659
12 changed files with 228 additions and 120 deletions
+10 -18
View File
@@ -1,4 +1,4 @@
*options.txt* For Vim version 9.2. Last change: 2026 Apr 21
*options.txt* For Vim version 9.2. Last change: 2026 Apr 27
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -9062,25 +9062,17 @@ A jump table for the options with a short description can be found at |Q_op|.
columns:{n} Number of columns of the tabpanel.
If this value is 0 or less than 'columns', the
tab panel will not be displayed.
tabpanel will not be displayed.
(default 20)
scroll Enable mouse wheel scrolling over the tabpanel
area when the tab list exceeds the visible
screen height. The scroll step is controlled
by 'mousescroll'. When disabled (the default),
the tabpanel shows the page containing the
current tab, with no way to view tabs outside
that page.
scrollbar Reserve a one-column scrollbar in the tabpanel
showing the current scroll position. The
scrollbar uses the |hl-PmenuSbar| and
|hl-PmenuThumb| highlight groups for the track
and thumb respectively. Clicking on the
scrollbar column jumps the thumb to that
position; the thumb can also be dragged.
Implies "scroll".
scrollbar Reserve a one-column scrollbar at the right
edge of the tabpanel showing the current
scroll position. The scrollbar uses the
|hl-PmenuSbar| and |hl-PmenuThumb| highlight
groups for the track and thumb respectively.
Clicking on the scrollbar column jumps the
thumb to that position; the thumb can also be
dragged. See |tabpanel-scroll|.
vert Use a vertical separator for tabpanel.
The vertical separator character is taken from
+18 -18
View File
@@ -1,4 +1,4 @@
*tabpage.txt* For Vim version 9.2. Last change: 2026 Apr 26
*tabpage.txt* For Vim version 9.2. Last change: 2026 Apr 27
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -485,33 +485,33 @@ groups: |hl-TabPanel| |hl-TabPanelSel| |hl-TabPanelFill|
SCROLLING IN THE TABPANEL: *tabpanel-scroll*
When the total height of the tab page list exceeds the visible screen height,
the tabpanel by default displays the "page" that contains the current tab page
and offers no way to view tab pages outside that page.
mouse wheel events over the tabpanel area scroll the tab page list up or
down. The scroll step follows the 'mousescroll' setting. Wheel events
inside the tabpanel area are consumed by the tabpanel and do not trigger
|<ScrollWheelUp>| or |<ScrollWheelDown>| mappings.
To make the tabpanel scrollable, add "scroll" to 'tabpanelopt': >
:set tabpanelopt+=scroll
The current tab page is always brought into view: when the selected tab
page changes (by |gt|, |gT|, |:tabnext| etc.), the panel scrolls so the
current entry is visible.
With "scroll" enabled, mouse wheel events over the tabpanel area scroll the
tab page list up or down. The scroll step follows the 'mousescroll' setting.
Wheel events inside the tabpanel area are consumed by the tabpanel and do not
trigger |<ScrollWheelUp>| or |<ScrollWheelDown>| mappings.
To additionally show a vertical scrollbar indicating the current scroll
position, use "scrollbar": >
To show a vertical scrollbar indicating the current scroll position, add
"scrollbar" to 'tabpanelopt': >
:set tabpanelopt+=scrollbar
The "scrollbar" value implies "scroll". A one-column scrollbar is reserved at
the edge of the tabpanel; clicking on the scrollbar column moves the thumb to
A one-column scrollbar is always reserved at the right edge of the
tabpanel, regardless of 'align'. For |'tabpanelopt'|=align:left this is
the edge adjacent to the buffer windows; for align:right it is the right
edge of the screen. Clicking on the scrollbar column moves the thumb to
the click position, and the thumb can be dragged to scroll continuously.
When "vert" is combined with "scrollbar", the scrollbar is drawn next to the
vertical separator, on the panel side.
When "vert" is combined with "scrollbar", the vertical separator is drawn
at the tabpanel's boundary with the buffer area and the scrollbar stays at
the tabpanel's right edge.
The scrollbar uses the |hl-PmenuSbar| highlight group for the track and
|hl-PmenuThumb| for the thumb.
The scroll offset is remembered across redraws but is reset when "scroll" or
"scrollbar" is toggled off and back on.
The scroll offset is remembered across redraws.
MOUSE CLICKS IN THE TABPANEL: *tabpanel-mouse*
+3 -3
View File
@@ -1,4 +1,4 @@
*version9.txt* For Vim version 9.2. Last change: 2026 Apr 26
*version9.txt* For Vim version 9.2. Last change: 2026 Apr 27
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -52616,8 +52616,8 @@ Other ~
- Allow mouse clickable regions in the 'statusline', 'tabline' and the
'tabpanel' using the |stl-%[FuncName]| atom.
- Enable reflow support in the |:terminal|.
- Added "scroll" and "scrollbar" sub-options to 'tabpanelopt' so the tabpanel
can scroll when the tab page list exceeds the visible screen height.
- Added "scrollbar" sub-option to 'tabpanelopt' so the tabpanel can scroll
when the tab page list exceeds the visible screen height.
Platform specific ~
-----------------
+1 -1
View File
@@ -30,7 +30,7 @@ static char *(p_briopt_values[]) = {"shift:", "min:", "sbr", "list:", "column:",
#endif
#if defined(FEAT_TABPANEL)
// Note: Keep this in sync with tabpanelopt_changed()
static char *(p_tplo_values[]) = {"align:", "columns:", "vert", NULL};
static char *(p_tplo_values[]) = {"align:", "columns:", "scrollbar", "vert", NULL};
static char *(p_tplo_align_values[]) = {"left", "right", NULL};
#endif
#if defined(FEAT_DIFF)
+1
View File
@@ -1,5 +1,6 @@
/* tabpanel.c */
int tabpanelopt_changed(void);
void tabpanel_forget_tabpage(const tabpage_T *tp);
int tabpanel_width(void);
int tabpanel_leftcol(void);
void draw_tabpanel(void);
+80 -43
View File
@@ -43,11 +43,11 @@ static int opt_scope = OPT_LOCAL;
static int tpl_align = ALIGN_LEFT;
static int tpl_columns = 20;
static bool tpl_is_vert = false;
static bool tpl_scroll = false;
static bool tpl_scrollbar = false;
static int tpl_scroll_offset = 0;
static int tpl_total_rows = 0;
static int tpl_scrollbar_col = -1; // screen column of scrollbar, -1 if none
static int tpl_scrollbar_col = -1; // screen column of scrollbar, -1 if none
static tabpage_T *tpl_last_curtab = NULL; // last curtab seen by draw_tabpanel
typedef struct {
win_T *wp;
@@ -69,7 +69,6 @@ tabpanelopt_changed(void)
int new_align = ALIGN_LEFT;
long new_columns = 20;
bool new_is_vert = false;
bool new_scroll = false;
bool new_scrollbar = false;
p = p_tplo;
@@ -107,12 +106,6 @@ tabpanelopt_changed(void)
{
p += 9;
new_scrollbar = true;
new_scroll = true;
}
else if (STRNCMP(p, "scroll", 6) == 0)
{
p += 6;
new_scroll = true;
}
if (*p != ',' && *p != NUL)
@@ -124,15 +117,26 @@ tabpanelopt_changed(void)
tpl_align = new_align;
tpl_columns = new_columns;
tpl_is_vert = new_is_vert;
if (tpl_scroll != new_scroll)
tpl_scroll_offset = 0;
tpl_scroll = new_scroll;
tpl_scrollbar = new_scrollbar;
// Re-center the current tab on the next redraw.
tpl_last_curtab = NULL;
shell_new_columns();
return OK;
}
/*
* Drop any internal reference to "tp", so draw_tabpanel() never compares
* against a dangling pointer after the tabpage has been freed.
*/
void
tabpanel_forget_tabpage(const tabpage_T *tp)
{
if (tpl_last_curtab == tp)
tpl_last_curtab = NULL;
}
/*
* Return the width of tabpanel.
*/
@@ -264,6 +268,31 @@ tabpanel_append_click_regions(
}
}
/*
* Ensure the current tab is visible by adjusting tpl_scroll_offset when
* the selected tab has changed since the previous redraw. Mouse wheel or
* scrollbar drag operations leave curtab unchanged, so the user's chosen
* offset is preserved in those cases.
*/
static void
follow_curtab_if_needed(int curtab_row)
{
if (Rows <= 0 || curtab == tpl_last_curtab)
return;
if (curtab_row < tpl_scroll_offset)
tpl_scroll_offset = curtab_row;
else if (curtab_row >= tpl_scroll_offset + Rows)
tpl_scroll_offset = curtab_row - Rows + 1;
int max_offset = tpl_total_rows > Rows ? tpl_total_rows - Rows : 0;
if (tpl_scroll_offset < 0)
tpl_scroll_offset = 0;
else if (tpl_scroll_offset > max_offset)
tpl_scroll_offset = max_offset;
}
/*
* draw the tabpanel.
*/
@@ -293,30 +322,36 @@ draw_tabpanel(void)
int sb_len = tpl_scrollbar ? SCROLL_LEN : 0;
int sb_screen_col = -1;
// The scrollbar is always placed at the right edge of the tabpanel,
// regardless of 'align'. The vertical separator sits at the panel's
// boundary with the buffer area (left edge for align:right, right edge
// for align:left).
if (tpl_is_vert)
{
if (is_right)
{
// draw main contents in tabpanel
do_by_tplmode(TPLMODE_GET_CURTAB_ROW, VERT_LEN + sb_len,
maxwidth - VERT_LEN, &curtab_row, NULL);
do_by_tplmode(TPLMODE_REDRAW, VERT_LEN + sb_len, maxwidth,
// Panel on the right: vert at panel's left edge, scrollbar at
// panel's right edge (= screen's right edge).
do_by_tplmode(TPLMODE_GET_CURTAB_ROW, VERT_LEN,
maxwidth - sb_len, &curtab_row, NULL);
follow_curtab_if_needed(curtab_row);
do_by_tplmode(TPLMODE_REDRAW, VERT_LEN, maxwidth - sb_len,
&curtab_row, NULL);
// draw vert separator in tabpanel
for (vsrow = 0; vsrow < Rows; vsrow++)
screen_putchar(curwin->w_fill_chars.tpl_vert, vsrow,
topframe->fr_width, vs_attr);
if (tpl_scrollbar)
sb_screen_col = topframe->fr_width + VERT_LEN;
sb_screen_col = topframe->fr_width + maxwidth - SCROLL_LEN;
}
else
{
// draw main contents in tabpanel
// Panel on the left: scrollbar just left of vert, vert at
// panel's right edge (boundary with buffer).
do_by_tplmode(TPLMODE_GET_CURTAB_ROW, 0,
maxwidth - VERT_LEN - sb_len, &curtab_row, NULL);
follow_curtab_if_needed(curtab_row);
do_by_tplmode(TPLMODE_REDRAW, 0, maxwidth - VERT_LEN - sb_len,
&curtab_row, NULL);
// draw vert separator in tabpanel
for (vsrow = 0; vsrow < Rows; vsrow++)
screen_putchar(curwin->w_fill_chars.tpl_vert, vsrow,
maxwidth - VERT_LEN, vs_attr);
@@ -328,16 +363,20 @@ draw_tabpanel(void)
{
if (is_right)
{
do_by_tplmode(TPLMODE_GET_CURTAB_ROW, sb_len, maxwidth,
// Panel on the right, no vert: scrollbar at screen's right edge.
do_by_tplmode(TPLMODE_GET_CURTAB_ROW, 0, maxwidth - sb_len,
&curtab_row, NULL);
follow_curtab_if_needed(curtab_row);
do_by_tplmode(TPLMODE_REDRAW, 0, maxwidth - sb_len,
&curtab_row, NULL);
do_by_tplmode(TPLMODE_REDRAW, sb_len, maxwidth, &curtab_row, NULL);
if (tpl_scrollbar)
sb_screen_col = topframe->fr_width;
sb_screen_col = topframe->fr_width + maxwidth - SCROLL_LEN;
}
else
{
do_by_tplmode(TPLMODE_GET_CURTAB_ROW, 0, maxwidth - sb_len,
&curtab_row, NULL);
follow_curtab_if_needed(curtab_row);
do_by_tplmode(TPLMODE_REDRAW, 0, maxwidth - sb_len,
&curtab_row, NULL);
if (tpl_scrollbar)
@@ -354,6 +393,7 @@ draw_tabpanel(void)
// A user function may reset KeyTyped, restore it.
KeyTyped = saved_KeyTyped;
tpl_last_curtab = curtab;
redraw_tabpanel = FALSE;
}
@@ -606,13 +646,7 @@ do_by_tplmode(
args.col_end = col_end;
if (tplmode != TPLMODE_GET_CURTAB_ROW && args.maxrow > 0)
{
if (tpl_scroll)
args.offsetrow = tpl_scroll_offset;
else
while (args.offsetrow + args.maxrow <= *pcurtab_row)
args.offsetrow += args.maxrow;
}
args.offsetrow = tpl_scroll_offset;
tp = first_tabpage;
@@ -632,16 +666,9 @@ do_by_tplmode(
{
args.attr = attr_tpls;
if (tplmode == TPLMODE_GET_CURTAB_ROW)
{
// Capture the row of the current tab and keep iterating so
// tpl_total_rows receives the true content height below.
*pcurtab_row = row;
// When scroll mode is active keep iterating so tpl_total_rows
// receives the true content height; otherwise bail out early.
if (!tpl_scroll)
{
do_unlet((char_u *)"g:actual_curtabpage", TRUE);
break;
}
}
}
else
args.attr = attr_tpl;
@@ -742,7 +769,7 @@ do_by_tplmode(
// Capture the true content height during the GET_CURTAB_ROW pass, which
// ignores maxrow and therefore walks every tab. REDRAW stops at the
// visible edge so its "row" is clamped and unusable here.
if (tplmode == TPLMODE_GET_CURTAB_ROW && tpl_scroll)
if (tplmode == TPLMODE_GET_CURTAB_ROW)
tpl_total_rows = row;
}
@@ -761,10 +788,21 @@ draw_tabpanel_scrollbar(int screen_col)
if (tpl_total_rows > Rows && Rows > 0)
{
int max_offset = tpl_total_rows - Rows;
int track_range;
thumb_height = Rows * Rows / tpl_total_rows;
if (thumb_height < 1)
thumb_height = 1;
thumb_top = Rows * tpl_scroll_offset / tpl_total_rows;
// Map tpl_scroll_offset onto the track: at offset 0 the thumb's top
// is at row 0, at the maximum offset its bottom reaches the last
// row. This is the exact inverse of tabpanel_drag_scrollbar().
track_range = Rows - thumb_height;
if (track_range > 0 && max_offset > 0)
thumb_top = track_range * tpl_scroll_offset / max_offset;
else
thumb_top = 0;
if (thumb_top + thumb_height > Rows)
thumb_top = Rows - thumb_height;
if (thumb_top < 0)
@@ -848,7 +886,6 @@ tabpanel_drag_scrollbar(int screen_row)
/*
* Scroll the tabpanel by 'count' rows in direction 'dir' (1 = down, -1 = up).
* Returns true if the offset changed and a redraw was scheduled.
* Has no effect unless 'tabpanelopt' contains "scroll".
*/
bool
tabpanel_scroll(int dir, int count)
@@ -856,7 +893,7 @@ tabpanel_scroll(int dir, int count)
int max_offset;
int new_offset;
if (!tpl_scroll || tabpanel_width() == 0)
if (tabpanel_width() == 0)
return false;
max_offset = tpl_total_rows - Rows;
+8 -8
View File
@@ -1,10 +1,10 @@
|1+2&#ffffff0@1|:|t|a|b| @3> +0&&@34
|5+8#0000001#e0e0e08|:|t|a|b| @4> +0#0000000#ffffff0@34
|6+8#0000001#e0e0e08|:|t|a|b| @4|~+0#4040ff13#ffffff0| @33
|7+8#0000001#e0e0e08|:|t|a|b| @4|~+0#4040ff13#ffffff0| @33
|8+8#0000001#e0e0e08|:|t|a|b| @4|~+0#4040ff13#ffffff0| @33
|9+8#0000001#e0e0e08|:|t|a|b| @4|~+0#4040ff13#ffffff0| @33
|1+8#0000001#e0e0e08|0|:|t|a|b| @3|~+0#4040ff13#ffffff0| @33
|1+2#0000000&@1|:|t|a|b| @3|~+0#4040ff13&| @33
|1+8#0000001#e0e0e08|2|:|t|a|b| @3|~+0#4040ff13#ffffff0| @33
|1+8#0000001#e0e0e08|3|:|t|a|b| @3|~+0#4040ff13#ffffff0| @33
|1+8#0000001#e0e0e08|4|:|t|a|b| @3|~+0#4040ff13#ffffff0| @33
|1+8#0000001#e0e0e08|5|:|t|a|b| @3|~+0#4040ff13#ffffff0| @33
|1+8#0000001#e0e0e08|6|:|t|a|b| @3|~+0#4040ff13#ffffff0| @33
|1+8#0000001#e0e0e08|7|:|t|a|b| @3|~+0#4040ff13#ffffff0| @33
|1+8#0000001#e0e0e08|8|:|t|a|b| @3|~+0#4040ff13#ffffff0| @33
|1+8#0000001#e0e0e08|9|:|t|a|b| @3|~+0#4040ff13#ffffff0| @33
|2+8#0000001#e0e0e08|0|:|t|a|b| @3|:+0#0000000#ffffff0|t|a|b|n|e|x|t| |-|3| @6|0|,|0|-|1| @7|A|l@1|
|1+8#0000001#e0e0e08|4|:|t|a|b| @3|:+0#0000000#ffffff0|t|a|b|n|e|x|t| |-|3| @6|0|,|0|-|1| @7|A|l@1|
+6
View File
@@ -593,6 +593,12 @@ func Test_set_completion_string_values()
if exists('+tabclose')
call assert_equal('left uselast', join(sort(getcompletion('set tabclose=', 'cmdline'))), ' ')
endif
if has('tabpanel')
call assert_equal(['align:', 'columns:', 'scrollbar', 'vert'],
\ getcompletion('set tabpanelopt=', 'cmdline'))
call assert_equal(['left', 'right'],
\ getcompletion('set tabpanelopt=align:', 'cmdline'))
endif
if exists('+termwintype')
call assert_equal('conpty', getcompletion('set termwintype=', 'cmdline')[1])
endif
+92 -27
View File
@@ -963,49 +963,26 @@ func Test_tabpanel_large_columns()
call assert_fails(':set tabpanelopt=columns:-1', 'E474:')
endfunc
func Test_tabpanel_scrollopt_accepted()
" 'scroll' / 'scrollbar' must be accepted in 'tabpanelopt'.
set tabpanelopt=scroll
call assert_equal('scroll', &tabpanelopt)
set tabpanelopt=scrollbar
call assert_equal('scrollbar', &tabpanelopt)
" Combination with other values.
set tabpanelopt=align:right,scroll
call assert_equal('align:right,scroll', &tabpanelopt)
set tabpanelopt=columns:15,vert,scrollbar
call assert_equal('columns:15,vert,scrollbar', &tabpanelopt)
set tabpanelopt=align:right,columns:12,vert,scrollbar
call assert_equal('align:right,columns:12,vert,scrollbar', &tabpanelopt)
" Unknown values must still fail.
call assert_fails(':set tabpanelopt=scrol', 'E474:')
call assert_fails(':set tabpanelopt=scrollbarx', 'E474:')
call s:reset()
endfunc
func Test_tabpanel_scroll_many_tabs()
let save_lines = &lines
let save_showtabpanel = &showtabpanel
let save_tabpanelopt = &tabpanelopt
" Make the screen short so the tab list exceeds the visible height.
" Make the screen short so the tab page list exceeds the visible height.
set lines=8
set showtabpanel=2
set tabpanelopt=scroll
set tabpanelopt=
for i in range(20)
tabnew
endfor
" Should not crash with many tabs and scroll enabled.
" Should not crash with many tabs (scroll behaviour is always on).
redraw!
" Switching to scrollbar resets the offset but must also not crash.
" Toggling scrollbar must also not crash.
set tabpanelopt=scrollbar
redraw!
" Disabling scroll returns to normal behavior.
set tabpanelopt=
redraw!
@@ -1024,6 +1001,94 @@ func Test_tabpanel_scroll_many_tabs()
let &lines = save_lines
endfunc
" The scrollbar thumb must follow the current tab when it is moved by
" gt/gT/:tabnext/:tablast, so that the selected tab is always visible.
func Test_tabpanel_scrollbar_follows_curtab()
let save_lines = &lines
let save_columns = &columns
let save_showtabpanel = &showtabpanel
let save_tabpanelopt = &tabpanelopt
set lines=10 columns=40
set showtabpanel=2 tabpanelopt=scrollbar,columns:8
for i in range(49)
tabnew
endfor
let sb_col = 8
" With curtab at the top of the list, row 1 shows the thumb and the
" last row shows the track. Record the two attrs for comparison.
tabfirst
redraw
let attr_thumb = screenattr(1, sb_col)
let attr_track = screenattr(&lines, sb_col)
call assert_notequal(attr_thumb, attr_track)
" Jump to a tab far outside the visible range: thumb must leave the top.
30tabnext
redraw
call assert_equal(attr_track, screenattr(1, sb_col))
" Back to the first tab: thumb returns to the top.
tabfirst
redraw
call assert_equal(attr_thumb, screenattr(1, sb_col))
call assert_equal(attr_track, screenattr(&lines, sb_col))
" gT from the first tab wraps to the last: thumb moves to the bottom.
normal! gT
redraw
call assert_equal(attr_track, screenattr(1, sb_col))
call assert_equal(attr_thumb, screenattr(&lines, sb_col))
" gt from the last tab wraps to the first: thumb returns to the top.
normal! gt
redraw
call assert_equal(attr_thumb, screenattr(1, sb_col))
call assert_equal(attr_track, screenattr(&lines, sb_col))
%bwipeout!
let &tabpanelopt = save_tabpanelopt
let &showtabpanel = save_showtabpanel
let &lines = save_lines
let &columns = save_columns
endfunc
" With 31 tabs on 24 rows, :tablast must place the scrollbar thumb's
" bottom at the last screen row. Before the fix, integer truncation in
" thumb_top left a one-row gap at the bottom.
func Test_tabpanel_scrollbar_reaches_bottom()
let save_lines = &lines
let save_columns = &columns
let save_showtabpanel = &showtabpanel
let save_tabpanelopt = &tabpanelopt
set lines=24 columns=40
set showtabpanel=2 tabpanelopt=scrollbar,columns:8
for i in range(30)
tabnew
endfor
let sb_col = 8
" Identify the thumb attr while the thumb is at the top.
tabfirst
redraw
let attr_thumb = screenattr(1, sb_col)
let attr_track = screenattr(&lines, sb_col)
call assert_notequal(attr_thumb, attr_track)
" :tablast must push the thumb all the way to the bottom.
tablast
redraw
call assert_equal(attr_thumb, screenattr(&lines, sb_col))
%bwipeout!
let &tabpanelopt = save_tabpanelopt
let &showtabpanel = save_showtabpanel
let &lines = save_lines
let &columns = save_columns
endfunc
func Test_tabpanel_variable_height()
let save_lines = &lines
+4 -2
View File
@@ -330,9 +330,11 @@ let test_values = {
\ 'tabline': [['', 'xxx'], ['%$', '%{', '%{%', '%{%}', '%(', '%)']],
\ 'tabpanel': [['', 'aaa', 'bbb'], []],
\ 'tabpanelopt': [['', 'align:left', 'align:right', 'vert', 'columns:0',
\ 'columns:20', 'columns:999'],
\ 'columns:20', 'columns:999', 'scrollbar',
\ 'columns:15,vert,scrollbar',
\ 'align:right,columns:12,vert,scrollbar'],
\ ['xxx', 'align:', 'align:middle', 'colomns:', 'cols:10',
\ 'cols:-1']],
\ 'cols:-1', 'scroll', 'scrol', 'scrollbarx']],
\ 'tagcase': [['followic', 'followscs', 'ignore', 'match', 'smart'],
\ ['', 'xxx', 'smart,match']],
\ 'termencoding': [has('gui_gtk') ? [] : ['', 'utf-8'], ['xxx']],
+2
View File
@@ -729,6 +729,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
407,
/**/
406,
/**/
+3
View File
@@ -4784,6 +4784,9 @@ free_tabpage(tabpage_T *tp)
if (tp == lastused_tabpage)
lastused_tabpage = NULL;
#ifdef FEAT_TABPANEL
tabpanel_forget_tabpage(tp);
#endif
vim_free(tp->tp_localdir);
vim_free(tp->tp_prevdir);