mirror of
https://github.com/neovim/neovim.git
synced 2026-05-25 06:18:32 +00:00
Merge pull request #39248 from zeertzjq/vim-9.2.0356
vim-patch: 'scrolloffpad'
This commit is contained in:
@@ -159,6 +159,7 @@ the same range instances now compare equal.
|
||||
|
||||
OPTIONS
|
||||
|
||||
• 'scrolloffpad' allows vertically centering cursor at the end of file.
|
||||
• 'winpinned' prevents window from closing unless specifically targeted.
|
||||
|
||||
PERFORMANCE
|
||||
|
||||
@@ -5278,14 +5278,32 @@ A jump table for the options with a short description can be found at |Q_op|.
|
||||
Minimal number of screen lines to keep above and below the cursor.
|
||||
This will make some context visible around where you are working. If
|
||||
you set it to a very large value (999) the cursor line will always be
|
||||
in the middle of the window (except at the start or end of the file or
|
||||
when long lines wrap).
|
||||
in the middle of the window (except at the start or end of the file,
|
||||
see 'scrolloffpad', or when long lines wrap).
|
||||
After using the local value, go back the global value with one of
|
||||
these two: >vim
|
||||
setlocal scrolloff<
|
||||
setlocal scrolloff=-1
|
||||
< For scrolling horizontally see 'sidescrolloff'.
|
||||
|
||||
*'scrolloffpad'* *'sop'*
|
||||
'scrolloffpad' 'sop' number (default 0)
|
||||
global or local to window |global-local|
|
||||
When 'scrolloff' and 'scrolloffpad' are greater than zero, allow
|
||||
the cursor to remain centered when at the end of the file.
|
||||
Normally, 'scrolloff' will not keep the cursor centered at the
|
||||
end of the file.
|
||||
|
||||
A value of 0 disables this feature. Any value above 0 enables it.
|
||||
For a window-local value, -1 means to use the global value.
|
||||
Values below -1 are invalid.
|
||||
|
||||
After using the local value, go back the global value with one of
|
||||
these two: >vim
|
||||
setlocal scrolloffpad<
|
||||
setlocal scrolloffpad=-1
|
||||
<
|
||||
|
||||
*'scrollopt'* *'sbo'*
|
||||
'scrollopt' 'sbo' string (default "ver,jump")
|
||||
global
|
||||
|
||||
@@ -841,6 +841,7 @@ Short explanation of each option: *option-list*
|
||||
'scrollbind' 'scb' scroll in window as other windows scroll
|
||||
'scrolljump' 'sj' minimum number of lines to scroll
|
||||
'scrolloff' 'so' minimum nr. of lines above and below cursor
|
||||
'scrolloffpad' 'sop' vertically center cursor at end of file
|
||||
'scrollopt' 'sbo' how 'scrollbind' should behave
|
||||
'sections' 'sect' nroff macros that separate sections
|
||||
'secure' secure mode for reading .vimrc in current dir
|
||||
|
||||
30
runtime/lua/vim/_meta/options.gen.lua
generated
30
runtime/lua/vim/_meta/options.gen.lua
generated
@@ -5488,8 +5488,8 @@ vim.go.sj = vim.go.scrolljump
|
||||
--- Minimal number of screen lines to keep above and below the cursor.
|
||||
--- This will make some context visible around where you are working. If
|
||||
--- you set it to a very large value (999) the cursor line will always be
|
||||
--- in the middle of the window (except at the start or end of the file or
|
||||
--- when long lines wrap).
|
||||
--- in the middle of the window (except at the start or end of the file,
|
||||
--- see 'scrolloffpad', or when long lines wrap).
|
||||
--- After using the local value, go back the global value with one of
|
||||
--- these two:
|
||||
---
|
||||
@@ -5507,6 +5507,32 @@ vim.wo.so = vim.wo.scrolloff
|
||||
vim.go.scrolloff = vim.o.scrolloff
|
||||
vim.go.so = vim.go.scrolloff
|
||||
|
||||
--- When 'scrolloff' and 'scrolloffpad' are greater than zero, allow
|
||||
--- the cursor to remain centered when at the end of the file.
|
||||
--- Normally, 'scrolloff' will not keep the cursor centered at the
|
||||
--- end of the file.
|
||||
---
|
||||
--- A value of 0 disables this feature. Any value above 0 enables it.
|
||||
--- For a window-local value, -1 means to use the global value.
|
||||
--- Values below -1 are invalid.
|
||||
---
|
||||
--- After using the local value, go back the global value with one of
|
||||
--- these two:
|
||||
---
|
||||
--- ```vim
|
||||
--- setlocal scrolloffpad<
|
||||
--- setlocal scrolloffpad=-1
|
||||
--- ```
|
||||
---
|
||||
---
|
||||
--- @type integer
|
||||
vim.o.scrolloffpad = 0
|
||||
vim.o.sop = vim.o.scrolloffpad
|
||||
vim.wo.scrolloffpad = vim.o.scrolloffpad
|
||||
vim.wo.sop = vim.wo.scrolloffpad
|
||||
vim.go.scrolloffpad = vim.o.scrolloffpad
|
||||
vim.go.sop = vim.go.scrolloffpad
|
||||
|
||||
--- This is a comma-separated list of words that specifies how
|
||||
--- 'scrollbind' windows should behave. 'sbo' stands for ScrollBind
|
||||
--- Options.
|
||||
|
||||
@@ -59,6 +59,7 @@ local options_list = {
|
||||
{ 'scroll', N_ 'number of lines to scroll for CTRL-U and CTRL-D' },
|
||||
{ 'smoothscroll', N_ 'scroll by screen line' },
|
||||
{ 'scrolloff', N_ 'number of screen lines to show around the cursor' },
|
||||
{ 'scrolloffpad', N_ 'vertically center cursor even at end of file' },
|
||||
{ 'wrap', N_ 'long lines wrap' },
|
||||
{ 'linebreak', N_ "wrap long lines at a character in 'breakat'" },
|
||||
{ 'breakindent', N_ 'preserve indentation in wrapped text' },
|
||||
|
||||
@@ -204,6 +204,8 @@ typedef struct {
|
||||
#define w_p_siso w_onebuf_opt.wo_siso // 'sidescrolloff' local value
|
||||
OptInt wo_so;
|
||||
#define w_p_so w_onebuf_opt.wo_so // 'scrolloff' local value
|
||||
OptInt wo_sop;
|
||||
#define w_p_sop w_onebuf_opt.wo_sop // 'scrolloffpad' local value
|
||||
char *wo_winhl;
|
||||
#define w_p_winhl w_onebuf_opt.wo_winhl // 'winhighlight'
|
||||
char *wo_lcs;
|
||||
|
||||
@@ -246,6 +246,26 @@ static void reset_skipcol(win_T *wp)
|
||||
redraw_later(wp, UPD_SOME_VALID);
|
||||
}
|
||||
|
||||
/// Return true when 'scrolloffpad' may augment 'scrolloff'.
|
||||
/// This only applies to automatic cursor visibility correction.
|
||||
/// For now 'scrolloffpad' is treated as boolean: 0 disables, > 0 enables.
|
||||
static bool use_scrolloffpad(win_T *wp)
|
||||
{
|
||||
return get_scrolloff_value(wp) > 0 && get_scrolloffpad_value(wp) > 0;
|
||||
}
|
||||
|
||||
/// Return true when there are not enough real buffer lines below "lnum" to
|
||||
/// satisfy the requested "so" context.
|
||||
static bool scrolloffpad_eof_pressure(win_T *wp, linenr_T lnum, OptInt so)
|
||||
{
|
||||
if (!use_scrolloffpad(wp) || so <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Use subtraction to avoid signed overflow in "lnum + so".
|
||||
return lnum > wp->w_buffer->b_ml.ml_line_count - so;
|
||||
}
|
||||
|
||||
// Update wp->w_topline to move the cursor onto the screen.
|
||||
void update_topline(win_T *wp)
|
||||
{
|
||||
@@ -278,6 +298,7 @@ void update_topline(win_T *wp)
|
||||
if (mouse_dragging > 0) {
|
||||
*so_ptr = mouse_dragging - 1;
|
||||
}
|
||||
bool eof_pressure = scrolloffpad_eof_pressure(wp, wp->w_cursor.lnum, *so_ptr);
|
||||
|
||||
linenr_T old_topline = wp->w_topline;
|
||||
int old_topfill = wp->w_topfill;
|
||||
@@ -350,10 +371,18 @@ void update_topline(win_T *wp)
|
||||
// cursor in the middle of the window. Otherwise put the cursor
|
||||
// near the top of the window.
|
||||
if (n >= halfheight) {
|
||||
scroll_cursor_halfway(wp, false, false);
|
||||
if (eof_pressure) {
|
||||
scroll_cursor_halfway(wp, true, true);
|
||||
} else {
|
||||
scroll_cursor_halfway(wp, false, false);
|
||||
}
|
||||
} else {
|
||||
scroll_cursor_top(wp, scrolljump_value(wp), false);
|
||||
check_botline = true;
|
||||
if (eof_pressure) {
|
||||
scroll_cursor_halfway(wp, true, true);
|
||||
} else {
|
||||
scroll_cursor_top(wp, scrolljump_value(wp), false);
|
||||
check_botline = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Make sure topline is the first line of a fold.
|
||||
@@ -374,7 +403,7 @@ void update_topline(win_T *wp)
|
||||
}
|
||||
|
||||
assert(wp->w_buffer != 0);
|
||||
if (wp->w_botline <= wp->w_buffer->b_ml.ml_line_count) {
|
||||
if (wp->w_botline <= wp->w_buffer->b_ml.ml_line_count || use_scrolloffpad(wp)) {
|
||||
if (wp->w_cursor.lnum < wp->w_botline) {
|
||||
if ((wp->w_cursor.lnum >= wp->w_botline - *so_ptr || win_lines_concealed(wp))) {
|
||||
lineoff_T loff;
|
||||
@@ -382,7 +411,7 @@ void update_topline(win_T *wp)
|
||||
// Cursor is (a few lines) above botline, check if there are
|
||||
// 'scrolloff' window lines below the cursor. If not, need to
|
||||
// scroll.
|
||||
int n = wp->w_empty_rows;
|
||||
int n = eof_pressure ? 0 : wp->w_empty_rows;
|
||||
loff.lnum = wp->w_cursor.lnum;
|
||||
// In a fold go to its last line.
|
||||
hasFolding(wp, loff.lnum, NULL, &loff.lnum);
|
||||
@@ -397,7 +426,7 @@ void update_topline(win_T *wp)
|
||||
}
|
||||
botline_forw(wp, &loff);
|
||||
}
|
||||
if (n >= *so_ptr) {
|
||||
if (n >= *so_ptr && !eof_pressure) {
|
||||
// sufficient context, no need to scroll
|
||||
check_botline = false;
|
||||
}
|
||||
@@ -424,9 +453,13 @@ void update_topline(win_T *wp)
|
||||
n = wp->w_cursor.lnum - wp->w_botline + 1 + *so_ptr;
|
||||
}
|
||||
if (n <= wp->w_view_height + 1) {
|
||||
scroll_cursor_bot(wp, scrolljump_value(wp), false);
|
||||
if (eof_pressure) {
|
||||
scroll_cursor_halfway(wp, true, true);
|
||||
} else {
|
||||
scroll_cursor_bot(wp, scrolljump_value(wp), false);
|
||||
}
|
||||
} else {
|
||||
scroll_cursor_halfway(wp, false, false);
|
||||
scroll_cursor_halfway(wp, eof_pressure, eof_pressure);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2111,8 +2144,9 @@ void scroll_cursor_bot(win_T *wp, int min_scroll, bool set_topbot)
|
||||
|
||||
// Scroll up if the cursor is off the bottom of the screen a bit.
|
||||
// Otherwise put it at 1/2 of the screen.
|
||||
bool eof_pressure = scrolloffpad_eof_pressure(wp, cln, so);
|
||||
if (line_count >= wp->w_view_height && line_count > min_scroll) {
|
||||
scroll_cursor_halfway(wp, false, true);
|
||||
scroll_cursor_halfway(wp, eof_pressure, true);
|
||||
} else if (line_count > 0) {
|
||||
if (do_sms) {
|
||||
scrollup(wp, scrolled, true); // TODO(vim):
|
||||
@@ -2289,7 +2323,9 @@ void cursor_correct(win_T *wp)
|
||||
validate_botline_win(wp);
|
||||
if (wp->w_botline == wp->w_buffer->b_ml.ml_line_count + 1
|
||||
&& mouse_dragging == 0) {
|
||||
below_wanted = 0;
|
||||
if (!use_scrolloffpad(wp)) {
|
||||
below_wanted = 0;
|
||||
}
|
||||
int max_off = (wp->w_view_height - 1) / 2;
|
||||
above_wanted = MIN(above_wanted, max_off);
|
||||
}
|
||||
|
||||
@@ -3111,6 +3111,12 @@ static const char *validate_num_option(OptIndex opt_idx, OptInt *newval, char *e
|
||||
return e_positive;
|
||||
}
|
||||
break;
|
||||
case kOptScrolloffpad:
|
||||
// if (value < 0 && full_screen) {
|
||||
if (value < 0) {
|
||||
return e_invarg;
|
||||
}
|
||||
break;
|
||||
case kOptSidescrolloff:
|
||||
if (value < 0 && full_screen) {
|
||||
return e_positive;
|
||||
@@ -3600,6 +3606,7 @@ static OptVal get_option_unset_value(OptIndex opt_idx)
|
||||
case kOptFsync:
|
||||
return BOOLEAN_OPTVAL(kNone);
|
||||
case kOptScrolloff:
|
||||
case kOptScrolloffpad:
|
||||
case kOptSidescrolloff:
|
||||
return NUMBER_OPTVAL(-1);
|
||||
case kOptUndolevels:
|
||||
@@ -4673,6 +4680,8 @@ void *get_varp_scope_from(vimoption_T *p, int opt_flags, buf_T *buf, win_T *win)
|
||||
return &(win->w_p_siso);
|
||||
case kOptScrolloff:
|
||||
return &(win->w_p_so);
|
||||
case kOptScrolloffpad:
|
||||
return &(win->w_p_sop);
|
||||
case kOptDefine:
|
||||
return &(buf->b_p_def);
|
||||
case kOptInclude:
|
||||
@@ -4760,6 +4769,8 @@ void *get_varp_from(vimoption_T *p, buf_T *buf, win_T *win)
|
||||
return win->w_p_siso >= 0 ? &(win->w_p_siso) : p->var;
|
||||
case kOptScrolloff:
|
||||
return win->w_p_so >= 0 ? &(win->w_p_so) : p->var;
|
||||
case kOptScrolloffpad:
|
||||
return win->w_p_sop >= 0 ? &(win->w_p_sop) : p->var;
|
||||
case kOptBackupcopy:
|
||||
return *buf->b_p_bkc != NUL ? &(buf->b_p_bkc) : p->var;
|
||||
case kOptDefine:
|
||||
@@ -5121,6 +5132,7 @@ void copy_winopt(winopt_T *from, winopt_T *to)
|
||||
to->wo_crb_save = from->wo_crb_save;
|
||||
to->wo_siso = from->wo_siso;
|
||||
to->wo_so = from->wo_so;
|
||||
to->wo_sop = from->wo_sop;
|
||||
to->wo_spell = from->wo_spell;
|
||||
to->wo_cuc = from->wo_cuc;
|
||||
to->wo_cul = from->wo_cul;
|
||||
@@ -6560,6 +6572,13 @@ int64_t get_scrolloff_value(win_T *wp)
|
||||
return wp->w_p_so < 0 ? p_so : wp->w_p_so;
|
||||
}
|
||||
|
||||
/// Return the effective 'scrolloffpad' value for the current window, using the
|
||||
/// global value when appropriate.
|
||||
int64_t get_scrolloffpad_value(win_T *wp)
|
||||
{
|
||||
return wp->w_p_sop == -1 ? p_sop : curwin->w_p_sop;
|
||||
}
|
||||
|
||||
/// Return the effective 'sidescrolloff' value for the current window, using the
|
||||
/// global value when appropriate.
|
||||
int64_t get_sidescrolloff_value(win_T *wp)
|
||||
|
||||
@@ -472,6 +472,7 @@ EXTERN char *p_rtp; ///< 'runtimepath'
|
||||
EXTERN OptInt p_scbk; ///< 'scrollback'
|
||||
EXTERN OptInt p_sj; ///< 'scrolljump'
|
||||
EXTERN OptInt p_so; ///< 'scrolloff'
|
||||
EXTERN OptInt p_sop; ///< 'scrolloffpad'
|
||||
EXTERN char *p_sbo; ///< 'scrollopt'
|
||||
EXTERN char *p_sections; ///< 'sections'
|
||||
EXTERN int p_secure; ///< 'secure'
|
||||
|
||||
@@ -7237,8 +7237,8 @@ local options = {
|
||||
Minimal number of screen lines to keep above and below the cursor.
|
||||
This will make some context visible around where you are working. If
|
||||
you set it to a very large value (999) the cursor line will always be
|
||||
in the middle of the window (except at the start or end of the file or
|
||||
when long lines wrap).
|
||||
in the middle of the window (except at the start or end of the file,
|
||||
see 'scrolloffpad', or when long lines wrap).
|
||||
After using the local value, go back the global value with one of
|
||||
these two: >vim
|
||||
setlocal scrolloff<
|
||||
@@ -7251,6 +7251,31 @@ local options = {
|
||||
type = 'number',
|
||||
varname = 'p_so',
|
||||
},
|
||||
{
|
||||
abbreviation = 'sop',
|
||||
defaults = 0,
|
||||
desc = [=[
|
||||
When 'scrolloff' and 'scrolloffpad' are greater than zero, allow
|
||||
the cursor to remain centered when at the end of the file.
|
||||
Normally, 'scrolloff' will not keep the cursor centered at the
|
||||
end of the file.
|
||||
|
||||
A value of 0 disables this feature. Any value above 0 enables it.
|
||||
For a window-local value, -1 means to use the global value.
|
||||
Values below -1 are invalid.
|
||||
|
||||
After using the local value, go back the global value with one of
|
||||
these two: >vim
|
||||
setlocal scrolloffpad<
|
||||
setlocal scrolloffpad=-1
|
||||
<
|
||||
]=],
|
||||
full_name = 'scrolloffpad',
|
||||
scope = { 'global', 'win' },
|
||||
short_desc = N_('vertically center cursor even at end of file'),
|
||||
type = 'number',
|
||||
varname = 'p_sop',
|
||||
},
|
||||
{
|
||||
abbreviation = 'sbo',
|
||||
defaults = 'ver,jump',
|
||||
|
||||
@@ -5532,6 +5532,7 @@ win_T *win_alloc(win_T *after, bool hidden)
|
||||
|
||||
// use global option for global-local options
|
||||
new_wp->w_allbuf_opt.wo_so = new_wp->w_p_so = -1;
|
||||
new_wp->w_allbuf_opt.wo_sop = new_wp->w_p_sop = -1;
|
||||
new_wp->w_allbuf_opt.wo_siso = new_wp->w_p_siso = -1;
|
||||
|
||||
// We won't calculate w_fraction until resizing the window
|
||||
|
||||
@@ -1397,3 +1397,205 @@ describe('smoothscroll', function()
|
||||
]])
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('scrolloffpad', function()
|
||||
local screen
|
||||
|
||||
before_each(function()
|
||||
screen = Screen.new(78, 20)
|
||||
end)
|
||||
|
||||
-- oldtest: Test_scrolloffpad_basic()
|
||||
it('works', function()
|
||||
exec([[
|
||||
set scrolloff=10
|
||||
set scrolloffpad=5
|
||||
enew!
|
||||
call setline(1, map(range(1, 100), 'printf("line %d", v:val)'))
|
||||
normal! gg
|
||||
]])
|
||||
|
||||
-- Enabled: scrolloffpad > 0, expect EOF centering/padding
|
||||
exec('normal! G')
|
||||
screen:expect([[
|
||||
line 91 |
|
||||
line 92 |
|
||||
line 93 |
|
||||
line 94 |
|
||||
line 95 |
|
||||
line 96 |
|
||||
line 97 |
|
||||
line 98 |
|
||||
line 99 |
|
||||
^line 100 |
|
||||
{1:~ }|*9
|
||||
|
|
||||
]])
|
||||
|
||||
-- Beginning-of-file is unchanged (Top)
|
||||
exec('normal! gg')
|
||||
screen:expect([[
|
||||
^line 1 |
|
||||
line 2 |
|
||||
line 3 |
|
||||
line 4 |
|
||||
line 5 |
|
||||
line 6 |
|
||||
line 7 |
|
||||
line 8 |
|
||||
line 9 |
|
||||
line 10 |
|
||||
line 11 |
|
||||
line 12 |
|
||||
line 13 |
|
||||
line 14 |
|
||||
line 15 |
|
||||
line 16 |
|
||||
line 17 |
|
||||
line 18 |
|
||||
line 19 |
|
||||
|
|
||||
]])
|
||||
|
||||
-- Gating: disable scrolloffpad, then go to EOF again
|
||||
-- Expect normal EOF behavior (no extra centering/padding)
|
||||
exec('set scrolloffpad=0')
|
||||
exec('normal! G')
|
||||
screen:expect([[
|
||||
line 82 |
|
||||
line 83 |
|
||||
line 84 |
|
||||
line 85 |
|
||||
line 86 |
|
||||
line 87 |
|
||||
line 88 |
|
||||
line 89 |
|
||||
line 90 |
|
||||
line 91 |
|
||||
line 92 |
|
||||
line 93 |
|
||||
line 94 |
|
||||
line 95 |
|
||||
line 96 |
|
||||
line 97 |
|
||||
line 98 |
|
||||
line 99 |
|
||||
^line 100 |
|
||||
|
|
||||
]])
|
||||
end)
|
||||
|
||||
-- oldtest: Test_scrolloffpad_smoothscroll()
|
||||
it('works with smoothscroll', function()
|
||||
exec([[
|
||||
set smoothscroll scrolloff=10 scrolloffpad=1
|
||||
enew!
|
||||
call setline(1, map(range(1, 100), 'printf("line %d", v:val)'))
|
||||
normal! gg
|
||||
]])
|
||||
|
||||
exec('normal! G')
|
||||
screen:expect([[
|
||||
line 91 |
|
||||
line 92 |
|
||||
line 93 |
|
||||
line 94 |
|
||||
line 95 |
|
||||
line 96 |
|
||||
line 97 |
|
||||
line 98 |
|
||||
line 99 |
|
||||
^line 100 |
|
||||
{1:~ }|*9
|
||||
|
|
||||
]])
|
||||
|
||||
exec([[call setline(line('$'), repeat('LONG ', 30))]])
|
||||
exec('normal! 41|')
|
||||
screen:expect([[
|
||||
line 92 |
|
||||
line 93 |
|
||||
line 94 |
|
||||
line 95 |
|
||||
line 96 |
|
||||
line 97 |
|
||||
line 98 |
|
||||
line 99 |
|
||||
LONG LONG LONG LONG LONG LONG LONG LONG ^LONG LONG LONG LONG LONG LONG LONG LON|
|
||||
G LONG LONG LONG LONG LONG LONG LONG LONG LONG LONG LONG LONG LONG LONG |
|
||||
{1:~ }|*9
|
||||
|
|
||||
]])
|
||||
end)
|
||||
|
||||
-- oldtest: Test_scrolloffpad_with_folds()
|
||||
it('works with folds', function()
|
||||
exec([[
|
||||
set scrolloff=10
|
||||
set scrolloffpad=1
|
||||
|
||||
enew
|
||||
call setline(1, map(range(1, 120), {_, v -> 'line ' . v}))
|
||||
|
||||
" Create a large fold near the end of the file.
|
||||
" Fold lines 60-110, leaving 111-120 visible after the fold.
|
||||
set foldmethod=manual
|
||||
set foldenable
|
||||
normal! gg
|
||||
normal! 60G
|
||||
normal! zf50j
|
||||
normal! gg
|
||||
]])
|
||||
|
||||
-- Case 1: Jump to end-of-file
|
||||
-- With folds present, scrolloffpad should still
|
||||
-- keep the cursor positioned with padding below EOF
|
||||
exec('normal! G')
|
||||
local s1 = [[
|
||||
line 111 |
|
||||
line 112 |
|
||||
line 113 |
|
||||
line 114 |
|
||||
line 115 |
|
||||
line 116 |
|
||||
line 117 |
|
||||
line 118 |
|
||||
line 119 |
|
||||
^line 120 |
|
||||
{1:~ }|*9
|
||||
|
|
||||
]]
|
||||
screen:expect(s1)
|
||||
|
||||
-- Case 2: Move to the folded line to ensure the fold is actually in view
|
||||
exec('normal! 60G')
|
||||
screen:expect([[
|
||||
line 51 |
|
||||
line 52 |
|
||||
line 53 |
|
||||
line 54 |
|
||||
line 55 |
|
||||
line 56 |
|
||||
line 57 |
|
||||
line 58 |
|
||||
line 59 |
|
||||
{13:^+-- 51 lines: line 60·························································}|
|
||||
line 111 |
|
||||
line 112 |
|
||||
line 113 |
|
||||
line 114 |
|
||||
line 115 |
|
||||
line 116 |
|
||||
line 117 |
|
||||
line 118 |
|
||||
line 119 |
|
||||
|
|
||||
]])
|
||||
|
||||
-- Case 3: Close the fold explicitly and go to EOF again
|
||||
-- Behavior should remain stable with closed folds
|
||||
exec('normal! zc')
|
||||
exec('normal! G')
|
||||
screen:expect(s1)
|
||||
end)
|
||||
end)
|
||||
|
||||
@@ -24,6 +24,7 @@ while search("^'[^']*'.*\\n.*|global-local", 'W')
|
||||
endwhile
|
||||
call extend(global_locals, #{
|
||||
\ scrolloff: -1,
|
||||
\ scrolloffpad: -1,
|
||||
\ sidescrolloff: -1,
|
||||
\ undolevels: -123456,
|
||||
\})
|
||||
@@ -127,6 +128,7 @@ let test_values = {
|
||||
\ 'scroll': [[0, 1, 2, 15], [-1, 999]],
|
||||
\ 'scrolljump': [[-100, -1, 0, 1, 2, 15], [-101, 999]],
|
||||
\ 'scrolloff': [[0, 1, 8, 999], [-1]],
|
||||
\ 'scrolloffpad': [[0, 1, 2, 3], [-1]],
|
||||
\ 'shiftwidth': [[0, 1, 8, 999], [-1]],
|
||||
\ 'sidescroll': [[0, 1, 8, 999], [-1]],
|
||||
\ 'sidescrolloff': [[0, 1, 8, 999], [-1]],
|
||||
|
||||
@@ -124,7 +124,8 @@ func Test_screenpos()
|
||||
setlocal nonumber display=lastline so=0
|
||||
exe "normal G\<C-Y>\<C-Y>"
|
||||
redraw
|
||||
call assert_equal({'row': winrow + wininfo.height - 1,
|
||||
let winbar_height = get(wininfo, 'winbar', 0)
|
||||
call assert_equal({'row': winrow + wininfo.height - 1 + winbar_height,
|
||||
\ 'col': wincol + 7,
|
||||
\ 'curscol': wincol + 7,
|
||||
\ 'endcol': wincol + 7}, winid->screenpos(line('$'), 8))
|
||||
|
||||
@@ -1470,6 +1470,46 @@ func Test_local_scrolloff()
|
||||
set siso&
|
||||
endfunc
|
||||
|
||||
func Test_local_scrolloffpad()
|
||||
let save_g_sop = &g:sop
|
||||
let save_l_sop = &l:sop
|
||||
set sop=0
|
||||
call assert_equal(0, &g:sop)
|
||||
call assert_equal(-1, &l:sop)
|
||||
call assert_equal(0, &sop)
|
||||
setglobal sop=1
|
||||
call assert_equal(1, &g:sop)
|
||||
call assert_equal(1, &sop)
|
||||
split
|
||||
call assert_equal(1, &g:sop)
|
||||
call assert_equal(-1, &l:sop)
|
||||
call assert_equal(1, &sop)
|
||||
setlocal sop=0
|
||||
call assert_equal(0, &l:sop)
|
||||
call assert_equal(0, &sop)
|
||||
call assert_equal(1, &g:sop)
|
||||
wincmd p
|
||||
call assert_equal(1, &sop)
|
||||
wincmd p
|
||||
"setlocal sop<
|
||||
set sop<
|
||||
call assert_equal(-1, &l:sop)
|
||||
call assert_equal(1, &sop)
|
||||
setlocal sop=2
|
||||
call assert_equal(2, &l:sop)
|
||||
call assert_equal(2, &sop)
|
||||
setlocal sop=-1
|
||||
call assert_equal(-1, &l:sop)
|
||||
call assert_equal(1, &sop) " Uses global value because local is -1
|
||||
call assert_fails("setlocal sop=-2", 'E474:')
|
||||
call assert_equal(-1, &l:sop)
|
||||
call assert_equal(1, &sop)
|
||||
call assert_fails("setlocal sop=foo", 'E521:')
|
||||
close
|
||||
let &g:sop = save_g_sop
|
||||
let &l:sop = save_l_sop
|
||||
endfunc
|
||||
|
||||
func Test_writedelay()
|
||||
CheckFunction reltimefloat
|
||||
|
||||
|
||||
@@ -1443,6 +1443,657 @@ func Test_smoothscroll_listchars_eol()
|
||||
bwipe!
|
||||
endfunc
|
||||
|
||||
" scrolloffpad contract:
|
||||
" - augment scrolloff only under EOF pressure (insufficient real lines below);
|
||||
" - do not change explicit "z" viewport placement command semantics;
|
||||
" - current scope is EOF-only, so BOF behavior remains unchanged.
|
||||
func Test_scrolloffpad_zb_keeps_bottom_command_semantics()
|
||||
new
|
||||
resize 12
|
||||
setlocal scrolloff=10
|
||||
call setline(1, map(range(1, 300), 'printf("line %d", v:val)'))
|
||||
|
||||
setlocal scrolloffpad=0
|
||||
normal! gg150Gzb
|
||||
let baseline = [line('.'), line('w$'), winline()]
|
||||
|
||||
setlocal scrolloffpad=1
|
||||
normal! gg150Gzb
|
||||
call assert_equal(baseline, [line('.'), line('w$'), winline()])
|
||||
|
||||
bwipe!
|
||||
endfunc
|
||||
|
||||
func Test_scrolloffpad_zminus_keeps_bottom_beginline_semantics()
|
||||
new
|
||||
resize 12
|
||||
setlocal scrolloff=10
|
||||
call setline(1, map(range(1, 300), 'printf(" line %d", v:val)'))
|
||||
|
||||
setlocal scrolloffpad=0
|
||||
normal! gg150Gz-
|
||||
let baseline = [line('.'), line('w$'), winline(), col('.')]
|
||||
call assert_equal(match(getline('.'), '\S') + 1, col('.'))
|
||||
|
||||
setlocal scrolloffpad=1
|
||||
normal! gg150Gz-
|
||||
call assert_equal(baseline, [line('.'), line('w$'), winline(), col('.')])
|
||||
call assert_equal(match(getline('.'), '\S') + 1, col('.'))
|
||||
|
||||
bwipe!
|
||||
endfunc
|
||||
|
||||
func Test_scrolloffpad_zb_is_one_shot_then_scrolloff_reapplies()
|
||||
new
|
||||
resize 12
|
||||
setlocal scrolloff=10
|
||||
call setline(1, map(range(1, 300), 'printf("line %d", v:val)'))
|
||||
|
||||
let after_zb = {}
|
||||
let after_j = {}
|
||||
for sop in [0, 1]
|
||||
let &l:scrolloffpad = sop
|
||||
normal! gg150Gzb
|
||||
let after_zb[sop] = [line('.'), line('w$'), winline(), winsaveview().topline]
|
||||
|
||||
normal! j
|
||||
let after_j[sop] = [line('.'), line('w$'), winline(), winsaveview().topline]
|
||||
call assert_notequal(after_zb[sop][3], after_j[sop][3])
|
||||
call assert_true(line('.') < line('w$'))
|
||||
endfor
|
||||
call assert_equal(after_zb[0], after_zb[1])
|
||||
call assert_equal(after_j[0], after_j[1])
|
||||
|
||||
bwipe!
|
||||
endfunc
|
||||
|
||||
func Test_scrolloffpad_has_no_mid_buffer_effect()
|
||||
new
|
||||
resize 12
|
||||
setlocal scrolloff=10 scrolloffpad=0
|
||||
call setline(1, map(range(1, 500), 'printf("line %d", v:val)'))
|
||||
|
||||
normal! gg150G
|
||||
let topline_without_pad = winsaveview().topline
|
||||
|
||||
setlocal scrolloffpad=1
|
||||
normal! gg150G
|
||||
let topline_with_pad = winsaveview().topline
|
||||
|
||||
call assert_equal(topline_without_pad, topline_with_pad)
|
||||
|
||||
bwipe!
|
||||
endfunc
|
||||
|
||||
func Test_scrolloffpad_changes_eof_pressure_only()
|
||||
new
|
||||
resize 12
|
||||
setlocal scrolloff=10 scrolloffpad=0
|
||||
call setline(1, map(range(1, 200), 'printf("line %d", v:val)'))
|
||||
|
||||
normal! ggG
|
||||
let view_without_pad = winsaveview()
|
||||
let cursor_without_pad = line('.')
|
||||
let row_without_pad = winline()
|
||||
|
||||
setlocal scrolloffpad=1
|
||||
normal! ggG
|
||||
let view_with_pad = winsaveview()
|
||||
let row_with_pad = winline()
|
||||
|
||||
call assert_equal(line('$'), line('.'))
|
||||
call assert_equal(cursor_without_pad, line('.'))
|
||||
call assert_notequal(view_without_pad.topline, view_with_pad.topline)
|
||||
call assert_true(row_with_pad < row_without_pad)
|
||||
|
||||
bwipe!
|
||||
endfunc
|
||||
|
||||
func Test_scrolloffpad_large_scrolloff_no_overflow()
|
||||
new
|
||||
resize 12
|
||||
call setline(1, map(range(1, 200), 'printf("line %d", v:val)'))
|
||||
setlocal scrolloff=2147483647 scrolloffpad=0
|
||||
|
||||
normal! ggG
|
||||
let view_without_pad = winsaveview()
|
||||
let row_without_pad = winline()
|
||||
|
||||
setlocal scrolloffpad=1
|
||||
normal! ggG
|
||||
let view_with_pad = winsaveview()
|
||||
let row_with_pad = winline()
|
||||
|
||||
call assert_equal(line('$'), line('.'))
|
||||
call assert_notequal(view_without_pad.topline, view_with_pad.topline)
|
||||
call assert_true(row_with_pad < row_without_pad)
|
||||
|
||||
bwipe!
|
||||
endfunc
|
||||
|
||||
func Test_scrolloffpad_boolean_gate_values()
|
||||
new
|
||||
resize 12
|
||||
setlocal scrolloff=10
|
||||
call setline(1, map(range(1, 200), 'printf("line %d", v:val)'))
|
||||
|
||||
let views = {}
|
||||
let rows = {}
|
||||
for sop in [0, 1, 2]
|
||||
let &l:scrolloffpad = sop
|
||||
normal! ggG
|
||||
let views[sop] = winsaveview()
|
||||
let rows[sop] = winline()
|
||||
call assert_equal(line('$'), line('.'))
|
||||
endfor
|
||||
|
||||
call assert_equal(views[1].topline, views[2].topline)
|
||||
call assert_equal(rows[1], rows[2])
|
||||
call assert_notequal(views[0].topline, views[1].topline)
|
||||
call assert_true(rows[1] < rows[0])
|
||||
|
||||
bwipe!
|
||||
endfunc
|
||||
|
||||
func Test_scrolloffpad_requires_scrolloff_nonzero()
|
||||
new
|
||||
resize 12
|
||||
call setline(1, map(range(1, 200), 'printf("line %d", v:val)'))
|
||||
|
||||
let states = {}
|
||||
for so in [0, 10]
|
||||
let states[so] = {}
|
||||
for sop in [0, 1]
|
||||
let &l:scrolloff = so
|
||||
let &l:scrolloffpad = sop
|
||||
normal! ggG
|
||||
let states[so][sop] = [line('.'), line('w0'), line('w$'), winline()]
|
||||
call assert_equal(line('$'), line('.'))
|
||||
endfor
|
||||
endfor
|
||||
|
||||
call assert_equal(states[0][0], states[0][1])
|
||||
call assert_notequal(states[10][0], states[10][1])
|
||||
call assert_true(states[10][1][3] < states[10][0][3])
|
||||
|
||||
bwipe!
|
||||
endfunc
|
||||
|
||||
func Test_scrolloffpad_search_to_eof()
|
||||
new
|
||||
resize 12
|
||||
setlocal scrolloff=10
|
||||
call setline(1, map(range(1, 200), 'printf("line %d", v:val)'))
|
||||
call setline(line('$'), 'EOF TARGET')
|
||||
|
||||
let states = {}
|
||||
for sop in [0, 1]
|
||||
let &l:scrolloffpad = sop
|
||||
normal! gg
|
||||
call assert_true(search('EOF TARGET') > 0)
|
||||
let states[sop] = [line('.'), line('w0'), line('w$'), winline()]
|
||||
call assert_equal(line('$'), line('.'))
|
||||
endfor
|
||||
|
||||
call assert_notequal(states[0], states[1])
|
||||
call assert_true(states[1][3] < states[0][3])
|
||||
|
||||
bwipe!
|
||||
endfunc
|
||||
|
||||
func Test_scrolloffpad_paging_to_eof()
|
||||
new
|
||||
resize 12
|
||||
setlocal scrolloff=10
|
||||
call setline(1, map(range(1, 240), 'printf("line %d", v:val)'))
|
||||
|
||||
let states = {}
|
||||
for sop in [0, 1]
|
||||
let &l:scrolloffpad = sop
|
||||
normal! gg
|
||||
|
||||
let prev = -1
|
||||
for _ in range(1, 200)
|
||||
execute "normal! \<C-D>"
|
||||
if line('.') == prev
|
||||
break
|
||||
endif
|
||||
let prev = line('.')
|
||||
endfor
|
||||
|
||||
let states[sop] = [line('.'), line('w0'), line('w$'), winline()]
|
||||
call assert_equal(line('$'), line('w$'))
|
||||
endfor
|
||||
|
||||
call assert_notequal(states[0], states[1])
|
||||
call assert_true(states[1][3] < states[0][3])
|
||||
|
||||
bwipe!
|
||||
endfunc
|
||||
|
||||
func Test_scrolloffpad_autocmd_append_at_eof()
|
||||
let states = {}
|
||||
for sop in [0, 1]
|
||||
new
|
||||
resize 12
|
||||
setlocal scrolloff=10
|
||||
let &l:scrolloffpad = sop
|
||||
call setline(1, map(range(1, 120), 'printf("line %d", v:val)'))
|
||||
|
||||
let b:scrolloffpad_appended = 0
|
||||
augroup ScrolloffpadAppendAtEof
|
||||
autocmd!
|
||||
autocmd CursorMoved <buffer> if b:scrolloffpad_appended == 0 && line('.') == line('$') | call append('$', 'appended') | let b:scrolloffpad_appended = 1 | endif
|
||||
augroup END
|
||||
|
||||
normal! ggG
|
||||
doautocmd <nomodeline> CursorMoved
|
||||
let states[sop] = [
|
||||
\ line('.'),
|
||||
\ line('$'),
|
||||
\ line('w0'),
|
||||
\ line('w$'),
|
||||
\ winline(),
|
||||
\ b:scrolloffpad_appended,
|
||||
\ ]
|
||||
|
||||
call assert_equal(1, b:scrolloffpad_appended)
|
||||
call assert_equal(states[sop][1] - 1, states[sop][0])
|
||||
|
||||
augroup ScrolloffpadAppendAtEof
|
||||
autocmd!
|
||||
augroup END
|
||||
bwipe!
|
||||
endfor
|
||||
|
||||
call assert_notequal(states[0], states[1])
|
||||
call assert_true(states[1][4] < states[0][4])
|
||||
|
||||
endfunc
|
||||
|
||||
func Test_scrolloffpad_eof_no_reverse_scroll_on_j()
|
||||
new
|
||||
resize 20
|
||||
setlocal scrolloff=20 scrolloffpad=1
|
||||
call setline(1, map(range(1, 80), 'printf("line %d", v:val)'))
|
||||
|
||||
normal! gg
|
||||
let prev_topline = winsaveview().topline
|
||||
for lnum in range(2, line('$'))
|
||||
normal! j
|
||||
let cur_topline = winsaveview().topline
|
||||
call assert_true(
|
||||
\ cur_topline >= prev_topline,
|
||||
\ printf('topline moved backwards at line %d: %d -> %d',
|
||||
\ lnum, prev_topline, cur_topline))
|
||||
let prev_topline = cur_topline
|
||||
endfor
|
||||
|
||||
bwipe!
|
||||
endfunc
|
||||
|
||||
func Test_scrolloffpad_bof_unchanged()
|
||||
new
|
||||
resize 12
|
||||
setlocal scrolloff=10 scrolloffpad=0
|
||||
call setline(1, map(range(1, 200), 'printf("line %d", v:val)'))
|
||||
|
||||
normal! Ggg
|
||||
let view_without_pad = winsaveview()
|
||||
let w0_without_pad = line('w0')
|
||||
|
||||
setlocal scrolloffpad=1
|
||||
normal! Ggg
|
||||
let view_with_pad = winsaveview()
|
||||
let w0_with_pad = line('w0')
|
||||
|
||||
call assert_equal(1, w0_without_pad)
|
||||
call assert_equal(1, w0_with_pad)
|
||||
call assert_equal(view_without_pad.topline, view_with_pad.topline)
|
||||
|
||||
bwipe!
|
||||
endfunc
|
||||
|
||||
func Test_scrolloffpad_mouse_drag_uses_drag_scrolloff()
|
||||
CheckFeature mouse
|
||||
|
||||
let save_mouse = &mouse
|
||||
set mouse=a
|
||||
|
||||
new
|
||||
resize 20
|
||||
call setline(1, map(range(1, 240), 'printf("line %d", v:val)'))
|
||||
setlocal scrolloff=50
|
||||
|
||||
let after_drag = {}
|
||||
for sop in [0, 1]
|
||||
let &l:scrolloffpad = sop
|
||||
normal! gg160Gzt
|
||||
normal! v
|
||||
call Ntest_setmouse(2, 1)
|
||||
call feedkeys("\<LeftMouse>", 'xt')
|
||||
call Ntest_setmouse(3, 1)
|
||||
call feedkeys("\<LeftDrag>", 'xt')
|
||||
let after_drag[sop] = [winsaveview().topline, line('.'), winline()]
|
||||
call feedkeys("\<Esc>", 'xt')
|
||||
endfor
|
||||
|
||||
call assert_equal(after_drag[0], after_drag[1])
|
||||
|
||||
bwipe!
|
||||
let &mouse = save_mouse
|
||||
endfunc
|
||||
|
||||
func Test_scrolloffpad_basic()
|
||||
CheckScreendump
|
||||
CheckRunVimInTerminal
|
||||
|
||||
let save_termwinsize = &termwinsize
|
||||
set termwinsize=
|
||||
|
||||
let lines =<< trim END
|
||||
set scrolloff=10
|
||||
set scrolloffpad=5
|
||||
enew!
|
||||
call setline(1, map(range(1, 100), 'printf("line %d", v:val)'))
|
||||
normal! gg
|
||||
END
|
||||
call writefile(lines, 'XScrolloffpadBasic', 'D')
|
||||
|
||||
let buf = RunVimInTerminal('-S XScrolloffpadBasic', {'rows': 20, 'cols': 78})
|
||||
|
||||
" Enabled: scrolloffpad > 0, expect EOF centering/padding
|
||||
call term_sendkeys(buf, "\<Esc>:\<C-U>normal! G\<CR>")
|
||||
call term_sendkeys(buf, "\<C-L>")
|
||||
call TermWait(buf)
|
||||
call VerifyScreenDump(buf, 'Test_scrolloffpad_basic_1', {})
|
||||
|
||||
" Beginning-of-file is unchanged (Top)
|
||||
call term_sendkeys(buf, "\<Esc>:\<C-U>normal! gg\<CR>")
|
||||
call term_sendkeys(buf, "\<C-L>")
|
||||
call TermWait(buf)
|
||||
call VerifyScreenDump(buf, 'Test_scrolloffpad_basic_2', {})
|
||||
|
||||
" Gating: disable scrolloffpad, then go to EOF again
|
||||
" Expect normal EOF behavior (no extra centering/padding)
|
||||
call term_sendkeys(buf, "\<Esc>:\<C-U>set scrolloffpad=0\<CR>")
|
||||
call term_sendkeys(buf, "\<Esc>:\<C-U>normal! G\<CR>")
|
||||
call term_sendkeys(buf, "\<C-L>")
|
||||
call TermWait(buf)
|
||||
call VerifyScreenDump(buf, 'Test_scrolloffpad_basic_3', {})
|
||||
|
||||
call StopVimInTerminal(buf)
|
||||
let &termwinsize = save_termwinsize
|
||||
endfunc
|
||||
|
||||
func Test_scrolloffpad_smoothscroll()
|
||||
CheckScreendump
|
||||
CheckRunVimInTerminal
|
||||
|
||||
let save_termwinsize = &termwinsize
|
||||
set termwinsize=
|
||||
|
||||
let lines =<< trim END
|
||||
set smoothscroll scrolloff=10 scrolloffpad=1
|
||||
enew!
|
||||
call setline(1, map(range(1, 100), 'printf("line %d", v:val)'))
|
||||
normal! gg
|
||||
END
|
||||
call writefile(lines, 'XScrolloffpadSmoothscroll', 'D')
|
||||
|
||||
let buf = RunVimInTerminal('-S XScrolloffpadSmoothscroll', #{rows: 20, cols: 78})
|
||||
|
||||
call term_sendkeys(buf, "\<Esc>:\<C-U>normal! G\<CR>")
|
||||
call term_sendkeys(buf, "\<C-L>")
|
||||
call TermWait(buf)
|
||||
call VerifyScreenDump(buf, 'Test_scrolloffpad_smoothscroll_1', {})
|
||||
|
||||
call term_sendkeys(buf, "\<Esc>:\<C-U>call setline(line('$'), repeat('LONG ', 30))\<CR>")
|
||||
call term_sendkeys(buf, "\<Esc>:\<C-U>normal! 41|\<CR>")
|
||||
call term_sendkeys(buf, "\<C-L>")
|
||||
call TermWait(buf)
|
||||
call VerifyScreenDump(buf, 'Test_scrolloffpad_smoothscroll_2', {})
|
||||
|
||||
call StopVimInTerminal(buf)
|
||||
let &termwinsize = save_termwinsize
|
||||
endfunc
|
||||
|
||||
func Test_scrolloffpad_insert_eof()
|
||||
let save_so = &scrolloff
|
||||
let save_sop = &scrolloffpad
|
||||
|
||||
set scrolloff=10 scrolloffpad=1
|
||||
enew!
|
||||
call setline(1, map(range(1, 200), 'printf("line %d", v:val)'))
|
||||
normal! G
|
||||
|
||||
let topline_before = winsaveview().topline
|
||||
call feedkeys("i\<Esc>", 'xt')
|
||||
call assert_equal(topline_before, winsaveview().topline)
|
||||
|
||||
exe "normal! \<C-E>"
|
||||
let topline_after = winsaveview().topline
|
||||
call feedkeys("i\<Esc>", 'xt')
|
||||
call assert_equal(topline_after, winsaveview().topline)
|
||||
|
||||
let &scrolloff = save_so
|
||||
let &scrolloffpad = save_sop
|
||||
bwipe!
|
||||
endfunc
|
||||
|
||||
func Test_scrolloffpad_in_diff_mode()
|
||||
CheckFeature diff
|
||||
|
||||
let save_so = &scrolloff
|
||||
let save_sop = &scrolloffpad
|
||||
let save_splitright = &splitright
|
||||
|
||||
set nosplitright
|
||||
set scrolloff=10
|
||||
set scrolloffpad=0
|
||||
|
||||
enew
|
||||
call setline(1, map(range(1, 100), {_, v -> 'line ' .. v}))
|
||||
diffthis
|
||||
|
||||
vnew
|
||||
call setline(1, map(range(1, 100), {_, v -> 'line ' .. v}))
|
||||
" Make buffers minimally different to avoid diff folding everything.
|
||||
call setline(50, 'DIFF LINE 50')
|
||||
diffthis
|
||||
|
||||
windo normal! zR
|
||||
windo normal! gg
|
||||
wincmd =
|
||||
|
||||
let rows_without = []
|
||||
let rows_with = []
|
||||
let near_states = []
|
||||
let eof_states = []
|
||||
for sop in [0, 1]
|
||||
let &scrolloffpad = sop
|
||||
|
||||
" Near EOF with real text visible in both windows.
|
||||
windo normal! 99G
|
||||
for w in range(1, winnr('$'))
|
||||
execute w .. 'wincmd w'
|
||||
let state = [line('.'), line('w0'), line('w$'), winline()]
|
||||
call assert_equal(99, state[0])
|
||||
call assert_equal(100, state[2])
|
||||
if sop == 0
|
||||
call add(near_states, state)
|
||||
endif
|
||||
endfor
|
||||
call assert_equal(near_states[0], near_states[1])
|
||||
|
||||
" EOF in both windows: scrolloffpad should raise the cursor row.
|
||||
windo normal! G
|
||||
for w in range(1, winnr('$'))
|
||||
execute w .. 'wincmd w'
|
||||
let state = [line('.'), line('w0'), line('w$'), winline()]
|
||||
call assert_equal(line('$'), state[0])
|
||||
if sop == 0
|
||||
call add(eof_states, state)
|
||||
call add(rows_without, state[3])
|
||||
else
|
||||
call add(rows_with, state[3])
|
||||
endif
|
||||
endfor
|
||||
call assert_equal(eof_states[0], eof_states[1])
|
||||
endfor
|
||||
|
||||
call assert_true(rows_with[0] < rows_without[0])
|
||||
call assert_true(rows_with[1] < rows_without[1])
|
||||
|
||||
windo diffoff
|
||||
%bwipe!
|
||||
let &scrolloff = save_so
|
||||
let &scrolloffpad = save_sop
|
||||
let &splitright = save_splitright
|
||||
endfunc
|
||||
|
||||
func Test_scrolloffpad_diff_eof_filler_behavior()
|
||||
CheckFeature diff
|
||||
|
||||
let save_so = &scrolloff
|
||||
let save_sop = &scrolloffpad
|
||||
let save_diffopt = &diffopt
|
||||
let save_splitright = &splitright
|
||||
|
||||
set diffopt+=filler
|
||||
set scrolloff=10
|
||||
set scrolloffpad=0
|
||||
set nosplitright
|
||||
|
||||
20new
|
||||
call setline(1, map(range(1, 100), {_, v -> 'left ' .. v}))
|
||||
diffthis
|
||||
let short_wid = win_getid()
|
||||
|
||||
vnew
|
||||
call setline(1, map(range(1, 120), {_, v -> 'right ' .. v}))
|
||||
diffthis
|
||||
let long_wid = win_getid()
|
||||
|
||||
call assert_true(win_gotoid(short_wid))
|
||||
let short_height = winheight(0)
|
||||
call assert_true(win_gotoid(long_wid))
|
||||
let long_height = winheight(0)
|
||||
call assert_equal(short_height, long_height)
|
||||
call assert_equal(20, short_height)
|
||||
|
||||
let ordered_diff_wids = [long_wid, short_wid]
|
||||
let states = {}
|
||||
for sop in [0, 1]
|
||||
execute 'set scrolloffpad=' .. sop
|
||||
for wid in ordered_diff_wids
|
||||
call assert_true(win_gotoid(wid))
|
||||
normal! gg
|
||||
endfor
|
||||
for wid in ordered_diff_wids
|
||||
call assert_true(win_gotoid(wid))
|
||||
normal! G
|
||||
endfor
|
||||
|
||||
call assert_true(win_gotoid(short_wid))
|
||||
let short_view = winsaveview()
|
||||
let short_state = [
|
||||
\ line('.'),
|
||||
\ line('$'),
|
||||
\ winline(),
|
||||
\ short_view.topline,
|
||||
\ short_view.topfill,
|
||||
\ diff_filler(line('$') + 1),
|
||||
\ ]
|
||||
call assert_equal(short_state[1], short_state[0])
|
||||
call assert_true(short_state[5] > 0)
|
||||
|
||||
call assert_true(win_gotoid(long_wid))
|
||||
let long_view = winsaveview()
|
||||
let long_state = [
|
||||
\ line('.'),
|
||||
\ line('$'),
|
||||
\ winline(),
|
||||
\ long_view.topline,
|
||||
\ long_view.topfill,
|
||||
\ ]
|
||||
call assert_true(long_state[0] > 0 && long_state[0] <= long_state[1])
|
||||
call assert_equal(short_state[0], long_state[0])
|
||||
|
||||
let states[sop] = [short_state, long_state]
|
||||
endfor
|
||||
|
||||
let short_without = states[0][0]
|
||||
let short_with = states[1][0]
|
||||
" Environment/layout can shift direction of movement; require only that
|
||||
" scrolloffpad changes the short-window viewport state under EOF filler.
|
||||
call assert_true(short_with[2] != short_without[2]
|
||||
\ || short_with[3] != short_without[3]
|
||||
\ || short_with[4] != short_without[4])
|
||||
|
||||
windo diffoff
|
||||
call assert_true(win_gotoid(short_wid))
|
||||
only!
|
||||
%bwipe!
|
||||
let &scrolloff = save_so
|
||||
let &scrolloffpad = save_sop
|
||||
let &diffopt = save_diffopt
|
||||
let &splitright = save_splitright
|
||||
endfunc
|
||||
|
||||
func Test_scrolloffpad_with_folds()
|
||||
CheckScreendump
|
||||
CheckRunVimInTerminal
|
||||
CheckFeature folding
|
||||
|
||||
let save_termwinsize = &termwinsize
|
||||
set termwinsize=
|
||||
|
||||
let lines =<< trim END
|
||||
set scrolloff=10
|
||||
set scrolloffpad=1
|
||||
|
||||
enew
|
||||
call setline(1, map(range(1, 120), {_, v -> 'line ' . v}))
|
||||
|
||||
" Create a large fold near the end of the file.
|
||||
" Fold lines 60-110, leaving 111-120 visible after the fold.
|
||||
set foldmethod=manual
|
||||
set foldenable
|
||||
normal! gg
|
||||
normal! 60G
|
||||
normal! zf50j
|
||||
normal! gg
|
||||
END
|
||||
call writefile(lines, 'XScrolloffpadFolds', 'D')
|
||||
|
||||
let buf = RunVimInTerminal('-S XScrolloffpadFolds', #{rows: 20, cols: 78})
|
||||
|
||||
" Case 1: Jump to end-of-file
|
||||
" With folds present, scrolloffpad should still
|
||||
" keep the cursor positioned with padding below EOF
|
||||
call term_sendkeys(buf, "\<Esc>:\<C-U>normal! G\<CR>")
|
||||
call term_sendkeys(buf, "\<C-L>")
|
||||
call TermWait(buf)
|
||||
call VerifyScreenDump(buf, 'Test_scrolloffpad_folds_1', {})
|
||||
|
||||
" Case 2: Move to the folded line to ensure the fold is actually in view
|
||||
call term_sendkeys(buf, "\<Esc>:\<C-U>normal! 60G\<CR>")
|
||||
call term_sendkeys(buf, "\<C-L>")
|
||||
call TermWait(buf)
|
||||
call VerifyScreenDump(buf, 'Test_scrolloffpad_folds_2', {})
|
||||
|
||||
" Case 3: Close the fold explicitly and go to EOF again
|
||||
" Behavior should remain stable with closed folds
|
||||
call term_sendkeys(buf, "\<Esc>:\<C-U>normal! zc\<CR>")
|
||||
call term_sendkeys(buf, "\<Esc>:\<C-U>normal! G\<CR>")
|
||||
call term_sendkeys(buf, "\<C-L>")
|
||||
call TermWait(buf)
|
||||
call VerifyScreenDump(buf, 'Test_scrolloffpad_folds_3', {})
|
||||
|
||||
call StopVimInTerminal(buf)
|
||||
let &termwinsize = save_termwinsize
|
||||
endfunc
|
||||
|
||||
" Resizing to "textoff" after 'smoothscroll' skips part of a wrapped line must
|
||||
" not crash.
|
||||
|
||||
Reference in New Issue
Block a user