Patch 9.0.0445

4 views
Skip to first unread message

Bram Moolenaar

unread,
Sep 11, 2022, 12:01:47 PM9/11/22
to vim...@googlegroups.com

Patch 9.0.0445
Problem: When opening/closing window text moves up/down.
Solution: Add the 'splitscroll' option. When off text will keep its
position as much as possible.
Files: runtime/doc/options.txt, runtime/doc/quickref.txt,
runtime/optwin.vim, src/move.c, src/option.h, src/optiondefs.h,
src/structs.h, src/window.c, src/testdir/test_window_cmd.vim


*** ../vim-9.0.0444/runtime/doc/options.txt 2022-08-31 14:46:07.903016994 +0100
--- runtime/doc/options.txt 2022-09-11 16:43:40.824700636 +0100
***************
*** 7472,7477 ****
--- 7499,7516 ----
When on, splitting a window will put the new window right of the
current one. |:vsplit|

+ *'splitscroll'* *'spsc'* *'nosplitscroll'* *'nospsc'*
+ 'splitscroll' 'spsc' boolean (default on)
+ global
+ The value of this option determines the scroll behavior when opening,
+ closing or resizing horizontal splits. When "on", splitting a window
+ horizontally will keep the same relative cursor position in the old and
+ new window, as well windows that are resized. When "off", scrolling
+ will be avoided to stabilize the window content. Instead, the cursor
+ position will be changed when necessary. In this case, the jumplist
+ will be populated with the previous cursor position. Scrolling cannot
+ be guaranteed to be avoided when 'wrap' is enabled.
+
*'startofline'* *'sol'* *'nostartofline'* *'nosol'*
'startofline' 'sol' boolean (default on)
global
*** ../vim-9.0.0444/runtime/doc/quickref.txt 2022-06-28 11:21:06.000000000 +0100
--- runtime/doc/quickref.txt 2022-09-11 16:49:42.148013392 +0100
***************
*** 919,924 ****
--- 919,925 ----
'spellsuggest' 'sps' method(s) used to suggest spelling corrections
'splitbelow' 'sb' new window from split is below the current one
'splitright' 'spr' new window is put right of the current one
+ 'splitscroll' 'spsc' determines scroll behavior when splitting windows
'startofline' 'sol' commands move cursor to first non-blank in line
'statusline' 'stl' custom format for the status line
'suffixes' 'su' suffixes that are ignored with multiple match
*** ../vim-9.0.0444/runtime/optwin.vim 2022-04-07 12:17:51.000000000 +0100
--- runtime/optwin.vim 2022-09-11 16:50:06.551966954 +0100
***************
*** 515,520 ****
--- 515,522 ----
call <SID>BinOptionG("sb", &sb)
call <SID>AddOption("splitright", gettext("a new window is put right of the current one"))
call <SID>BinOptionG("spr", &spr)
+ call <SID>AddOption("splitscroll", gettext("determines scroll behavior when spliting windows"))
+ call <SID>BinOptionG("spsc", &spsc)
call <SID>AddOption("scrollbind", gettext("this window scrolls together with other bound windows"))
call append("$", "\t" .. s:local_to_window)
call <SID>BinOptionL("scb")
*** ../vim-9.0.0444/src/move.c 2022-09-10 20:00:31.121468657 +0100
--- src/move.c 2022-09-11 16:43:40.824700636 +0100
***************
*** 981,987 ****
/*
* First make sure that w_topline is valid (after moving the cursor).
*/
! update_topline();

/*
* Next make sure that w_cline_row is valid.
--- 981,988 ----
/*
* First make sure that w_topline is valid (after moving the cursor).
*/
! if (p_spsc)
! update_topline();

/*
* Next make sure that w_cline_row is valid.
*** ../vim-9.0.0444/src/option.h 2022-08-26 16:58:46.139489368 +0100
--- src/option.h 2022-09-11 16:43:40.824700636 +0100
***************
*** 924,929 ****
--- 924,930 ----
EXTERN char_u *p_sps; // 'spellsuggest'
#endif
EXTERN int p_spr; // 'splitright'
+ EXTERN int p_spsc; // 'splitscroll'
EXTERN int p_sol; // 'startofline'
EXTERN char_u *p_su; // 'suffixes'
EXTERN char_u *p_sws; // 'swapsync'
*** ../vim-9.0.0444/src/optiondefs.h 2022-08-27 21:24:22.709002324 +0100
--- src/optiondefs.h 2022-09-11 16:43:40.824700636 +0100
***************
*** 2349,2354 ****
--- 2349,2357 ----
{"splitright", "spr", P_BOOL|P_VI_DEF,
(char_u *)&p_spr, PV_NONE,
{(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
+ {"splitscroll", "spsc", P_BOOL,
+ (char_u *)&p_spsc, PV_NONE,
+ {(char_u *)TRUE, (char_u *)TRUE} SCTX_INIT},
{"startofline", "sol", P_BOOL|P_VI_DEF|P_VIM,
(char_u *)&p_sol, PV_NONE,
{(char_u *)TRUE, (char_u *)0L} SCTX_INIT},
*** ../vim-9.0.0444/src/structs.h 2022-09-10 20:00:31.117468669 +0100
--- src/structs.h 2022-09-11 16:51:07.075851730 +0100
***************
*** 3570,3575 ****
--- 3570,3577 ----
int w_winrow; // first row of window in screen
int w_height; // number of rows in window, excluding
// status/command/winbar line(s)
+ int w_prev_winrow; // previous winrow used for 'splitscroll'
+ int w_prev_height; // previous height used for 'splitscroll'

int w_status_height; // number of status lines (0 or 1)
int w_wincol; // Leftmost column of window in screen.
*** ../vim-9.0.0444/src/window.c 2022-09-07 14:42:46.099802275 +0100
--- src/window.c 2022-09-11 16:57:26.366536524 +0100
***************
*** 25,30 ****
--- 25,32 ----
static tabpage_T *alt_tabpage(void);
static win_T *frame2win(frame_T *frp);
static int frame_has_win(frame_T *frp, win_T *wp);
+ static void win_fix_scroll(int resize);
+ static void win_fix_cursor(int normal);
static void frame_new_height(frame_T *topfrp, int height, int topfirst, int wfh);
static int frame_fixed_height(frame_T *frp);
static int frame_fixed_width(frame_T *frp);
***************
*** 1323,1328 ****
--- 1325,1332 ----
win_equal(wp, TRUE,
(flags & WSP_VERT) ? (dir == 'v' ? 'b' : 'h')
: dir == 'h' ? 'b' : 'v');
+ else if (!p_spsc)
+ win_fix_scroll(FALSE);

// Don't change the window height/width to 'winheight' / 'winwidth' if a
// size was given.
***************
*** 1407,1412 ****
--- 1411,1423 ----
newp->w_prevdir = (oldp->w_prevdir == NULL)
? NULL : vim_strsave(oldp->w_prevdir);

+ if (!p_spsc)
+ {
+ newp->w_botline = oldp->w_botline;
+ newp->w_prev_height = oldp->w_height - WINBAR_HEIGHT(oldp);
+ newp->w_prev_winrow = oldp->w_winrow + 2 * WINBAR_HEIGHT(oldp);
+ }
+
// copy tagstack and folds
for (i = 0; i < oldp->w_tagstacklen; i++)
{
***************
*** 1914,1919 ****
--- 1925,1932 ----
win_equal_rec(next_curwin == NULL ? curwin : next_curwin, current,
topframe, dir, 0, tabline_height(),
(int)Columns, topframe->fr_height);
+ if (!p_spsc)
+ win_fix_scroll(TRUE);
}

/*
***************
*** 2725,2731 ****
--- 2738,2748 ----
// only resize that frame. Otherwise resize all windows.
win_equal(curwin, curwin->w_frame->fr_parent == win_frame, dir);
else
+ {
win_comp_pos();
+ if (!p_spsc)
+ win_fix_scroll(FALSE);
+ }
if (close_curwin)
{
// Pass WEE_ALLOW_PARSE_MESSAGES to decrement dont_parse_messages
***************
*** 4912,4918 ****

// Might need to scroll the old window before switching, e.g., when the
// cursor was moved.
! update_topline();

// may have to copy the buffer options when 'cpo' contains 'S'
if (wp->w_buffer != curbuf)
--- 4929,4936 ----

// Might need to scroll the old window before switching, e.g., when the
// cursor was moved.
! if (p_spsc)
! update_topline();

// may have to copy the buffer options when 'cpo' contains 'S'
if (wp->w_buffer != curbuf)
***************
*** 4927,4933 ****
check_cursor();
if (!virtual_active())
curwin->w_cursor.coladd = 0;
! changed_line_abv_curs(); // assume cursor position needs updating

// Now it is OK to parse messages again, which may be needed in
// autocommands.
--- 4945,4954 ----
check_cursor();
if (!virtual_active())
curwin->w_cursor.coladd = 0;
! if (p_spsc) // assume cursor position needs updating.
! changed_line_abv_curs();
! else
! win_fix_cursor(TRUE);

// Now it is OK to parse messages again, which may be needed in
// autocommands.
***************
*** 5458,5463 ****
--- 5479,5487 ----
compute_cmdrow();
curtab->tp_ch_used = p_ch;

+ if (!p_spsc)
+ win_fix_scroll(TRUE);
+
#if 0
// Disabled: don't want making the screen smaller make a window larger.
if (p_ea)
***************
*** 5662,5667 ****
--- 5686,5694 ----
msg_row = row;
msg_col = 0;

+ if (!p_spsc)
+ win_fix_scroll(TRUE);
+
redraw_all_later(UPD_NOT_VALID);
}

***************
*** 6190,6195 ****
--- 6217,6225 ----
p_ch = MAX(Rows - cmdline_row, 1);
curtab->tp_ch_used = p_ch;

+ if (!p_spsc)
+ win_fix_scroll(TRUE);
+
redraw_all_later(UPD_SOME_VALID);
showmode();
}
***************
*** 6317,6322 ****
--- 6347,6443 ----
}

/*
+ * Handle scroll position for 'nosplitscroll'. Replaces scroll_to_fraction()
+ * call from win_new_height(). Instead we iterate over all windows in a
+ * tabpage and calculate the new scroll/cursor position.
+ * TODO: Ensure this also works with wrapped lines.
+ * Requires topline to be able to be set to a bufferline with some
+ * offset(row-wise scrolling/smoothscroll).
+ */
+ static void
+ win_fix_scroll(int resize)
+ {
+ win_T *wp;
+ linenr_T lnum;
+
+ FOR_ALL_WINDOWS(wp)
+ {
+ // Skip when window height has not changed or when
+ // buffer has less lines than the window height.
+ if (wp->w_height != wp->w_prev_height
+ && wp->w_height < wp->w_buffer->b_ml.ml_line_count)
+ {
+ // Determine botline needed to avoid scrolling and set cursor.
+ if (wp->w_winrow != wp->w_prev_winrow)
+ {
+ lnum = wp->w_cursor.lnum;
+ wp->w_cursor.lnum = MIN(wp->w_buffer->b_ml.ml_line_count,
+ wp->w_botline - 1 + (wp->w_prev_height
+ ? (wp->w_winrow - wp->w_prev_winrow)
+ + (wp->w_height - wp->w_prev_height)
+ : -WINBAR_HEIGHT(wp)));
+ // Bring the new cursor position to the bottom of the screen.
+ wp->w_fraction = FRACTION_MULT;
+ scroll_to_fraction(wp, wp->w_prev_height);
+ wp->w_cursor.lnum = lnum;
+ }
+ invalidate_botline_win(wp);
+ validate_botline_win(wp);
+ }
+ wp->w_prev_height = wp->w_height;
+ wp->w_prev_winrow = wp->w_winrow;
+ }
+ // Ensure cursor is valid when not in normal mode or when resized.
+ if (!(get_real_state() & (MODE_NORMAL|MODE_CMDLINE)))
+ win_fix_cursor(FALSE);
+ else if (resize)
+ win_fix_cursor(TRUE);
+ }
+
+ /*
+ * Make sure the cursor position is valid for 'nosplitscroll'.
+ * If it is not, put the cursor position in the jumplist and move it.
+ * If we are not in normal mode, scroll to make valid instead.
+ */
+ static void
+ win_fix_cursor(int normal)
+ {
+ int top = FALSE;
+ win_T *wp = curwin;
+ long so = get_scrolloff_value();
+ linenr_T nlnum = 0;
+
+ if (wp->w_buffer->b_ml.ml_line_count < wp->w_height)
+ return;
+
+ so = MIN(wp->w_height / 2, so);
+ // Check if cursor position is above topline or below botline.
+ if (wp->w_cursor.lnum < (wp->w_topline + so) && wp->w_topline != 1)
+ top = nlnum = MIN(wp->w_topline + so, wp->w_buffer->b_ml.ml_line_count);
+ else if (wp->w_cursor.lnum > (wp->w_botline - so - 1)
+ && (wp->w_botline - wp->w_buffer->b_ml.ml_line_count) != 1)
+ nlnum = MAX(wp->w_botline - so - 1, 1);
+ // If cursor was invalid scroll or change cursor.
+ if (nlnum)
+ {
+ if (normal)
+ { // Make sure cursor is closer to topline than botline.
+ if (so == wp->w_height / 2
+ && nlnum - wp->w_topline > wp->w_botline - 1 - nlnum)
+ nlnum--;
+ setmark('\''); // save cursor position
+ wp->w_cursor.lnum = nlnum; // change to avoid scrolling
+ curs_columns(TRUE); // validate w_wrow
+ }
+ else
+ { // Ensure cursor stays visible if we are not in normal mode.
+ wp->w_fraction = top ? 0 : FRACTION_MULT;
+ scroll_to_fraction(wp, wp->w_prev_height);
+ }
+ }
+ }
+
+ /*
* Set the height of a window.
* "height" excludes any window toolbar.
* This takes care of the things inside the window, not what happens to the
***************
*** 6336,6342 ****

if (wp->w_height > 0)
{
! if (wp == curwin)
// w_wrow needs to be valid. When setting 'laststatus' this may
// call win_new_height() recursively.
validate_cursor();
--- 6457,6463 ----

if (wp->w_height > 0)
{
! if (wp == curwin && p_spsc)
// w_wrow needs to be valid. When setting 'laststatus' this may
// call win_new_height() recursively.
validate_cursor();
***************
*** 6352,6358 ****

// There is no point in adjusting the scroll position when exiting. Some
// values might be invalid.
! if (!exiting)
scroll_to_fraction(wp, prev_height);
}

--- 6473,6479 ----

// There is no point in adjusting the scroll position when exiting. Some
// values might be invalid.
! if (!exiting && p_spsc)
scroll_to_fraction(wp, prev_height);
}

***************
*** 6466,6472 ****

if (wp == curwin)
{
! if (get_scrolloff_value())
update_topline();
curs_columns(FALSE); // validate w_wrow
}
--- 6587,6593 ----

if (wp == curwin)
{
! if (p_spsc && get_scrolloff_value())
update_topline();
curs_columns(FALSE); // validate w_wrow
}
***************
*** 6488,6498 ****
wp->w_width = width;
wp->w_lines_valid = 0;
changed_line_abv_curs_win(wp);
! invalidate_botline_win(wp);
! if (wp == curwin)
{
! update_topline();
! curs_columns(TRUE); // validate w_wrow
}
redraw_win_later(wp, UPD_NOT_VALID);
wp->w_redr_status = TRUE;
--- 6609,6623 ----
wp->w_width = width;
wp->w_lines_valid = 0;
changed_line_abv_curs_win(wp);
! // Handled in win_fix_scroll()
! if (p_spsc)
{
! invalidate_botline_win(wp);
! if (wp == curwin)
! {
! update_topline();
! curs_columns(TRUE); // validate w_wrow
! }
}
redraw_win_later(wp, UPD_NOT_VALID);
wp->w_redr_status = TRUE;
*** ../vim-9.0.0444/src/testdir/test_window_cmd.vim 2022-09-07 14:42:46.099802275 +0100
--- src/testdir/test_window_cmd.vim 2022-09-11 16:43:40.828700627 +0100
***************
*** 1631,1635 ****
--- 1631,1763 ----
set laststatus&
endfunc

+ " Ensure no scrolling happens with 'nosplitscroll' with and without a
+ " winbar, tabline, for each possible value of 'laststatus', 'scrolloff',
+ " 'equalalways', and regardless of the cursor position.
+ func Test_splitscroll_with_splits()
+ set nowrap
+ set nosplitscroll
+ let gui = has("gui_running")
+ inoremap c <cmd>:copen<CR>
+ for winbar in [0, 1]
+ for sb in [0, 1]
+ for ea in [0, 1]
+ for tab in [0, 1]
+ for so in [0, 5]
+ for ls in range(0, 2)
+ for pos in ["H", "M", "L"]
+ let tabline = (gui ? 0 : (tab ? 1 : 0))
+ let winbar_sb = (sb ? winbar : 0)
+ execute 'set scrolloff=' . so
+ execute 'set laststatus=' . ls
+ execute 'set ' . (ea ? 'equalalways' : 'noequalalways')
+ execute 'set ' . (sb ? 'splitbelow' : 'nosplitbelow')
+ execute tab ? 'tabnew' : ''
+ execute winbar ? 'nnoremenu 1.10 WinBar.Test :echo' : ''
+ call setline(1, range(1, 256))
+ execute 'norm gg' . pos
+ " No scroll for vertical split and quit
+ vsplit | quit
+ call assert_equal(1, line("w0"))
+
+ " No scroll for horizontal split
+ split | redraw! | wincmd k
+ call assert_equal(1, line("w0"))
+
+ " No scroll when resizing windows
+ resize +2
+ call assert_equal(1, line("w0"))
+ wincmd j
+ call assert_equal(win_screenpos(0)[0] - tabline - winbar_sb, line("w0"))
+
+ " No scroll when dragging statusline
+ call win_move_statusline(1, -3)
+ call assert_equal(win_screenpos(0)[0] - tabline - winbar_sb, line("w0"))
+ wincmd k
+ call assert_equal(1, line("w0"))
+
+ " No scroll when changing shellsize
+ set lines+=2
+ call assert_equal(1, line("w0"))
+ wincmd j
+ call assert_equal(win_screenpos(0)[0] - tabline - winbar_sb, line("w0"))
+ set lines-=2
+ call assert_equal(win_screenpos(0)[0] - tabline - winbar_sb, line("w0"))
+ wincmd k
+ call assert_equal(1, line("w0"))
+
+ " No scroll when equalizing windows
+ wincmd =
+ call assert_equal(1, line("w0"))
+ wincmd j
+ call assert_equal(win_screenpos(0)[0] - tabline - winbar_sb, line("w0"))
+ wincmd k
+ call assert_equal(1, line("w0"))
+
+ " No scroll in windows split multiple times
+ vsplit | split | 4wincmd w
+ call assert_equal(win_screenpos(0)[0] - tabline - winbar_sb, line("w0"))
+ 1wincmd w | quit | wincmd l | split
+ call assert_equal(win_screenpos(0)[0] - tabline - winbar_sb, line("w0"))
+ wincmd j
+ call assert_equal(win_screenpos(0)[0] - tabline - winbar_sb, line("w0"))
+
+ " No scroll in small window
+ 2wincmd w | only | 5split | wincmd k
+ call assert_equal(1, line("w0"))
+ wincmd j
+ call assert_equal(win_screenpos(0)[0] - tabline - winbar_sb, line("w0"))
+
+ " No scroll for vertical split
+ quit | vsplit | wincmd l
+ call assert_equal(1, line("w0"))
+ wincmd h
+ call assert_equal(1, line("w0"))
+
+ " No scroll in windows split and quit multiple times
+ quit | split | split | quit
+ call assert_equal(win_screenpos(0)[0] - tabline - winbar_sb, line("w0"))
+
+ " No scroll for new buffer
+ 1wincmd w | only | copen | wincmd k
+ call assert_equal(1, line("w0"))
+ only
+ call assert_equal(1, line("w0"))
+ above copen | wincmd j
+ call assert_equal(win_screenpos(0)[0] - tabline, line("w0"))
+
+ " No scroll when opening cmdwin
+ only | norm ggLq:
+ call assert_equal(1, line("w0"))
+
+ " Scroll when cursor becomes invalid in insert mode
+ norm Lic
+ wincmd k | only
+ call assert_notequal(1, line("w0"))
+
+ " No scroll when topline not equal to 1
+ execute "norm gg5\<C-e>" | split | wincmd k
+ call assert_equal(6, line("w0"))
+ wincmd j
+ call assert_equal(5 + win_screenpos(0)[0] - tabline - winbar_sb, line("w0"))
+ only
+ endfor
+ endfor
+ endfor
+ tabonly!
+ endfor
+ endfor
+ endfor
+ endfor
+
+ tabnew | tabonly! | %bwipeout!
+ iunmap c
+ set wrap&
+ set scrolloff&
+ set splitbelow&
+ set laststatus&
+ set equalalways&
+ set splitscroll&
+ endfunc

" vim: shiftwidth=2 sts=2 expandtab
*** ../vim-9.0.0444/src/version.c 2022-09-11 15:14:00.551020049 +0100
--- src/version.c 2022-09-11 16:45:28.040496825 +0100
***************
*** 705,706 ****
--- 705,708 ----
{ /* Add new patch number below this line */
+ /**/
+ 445,
/**/

--
Light travels faster than sound. This is why some people
appear bright until you hear them speak

/// Bram Moolenaar -- Br...@Moolenaar.net -- http://www.Moolenaar.net \\\
/// \\\
\\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ ///
\\\ help me help AIDS victims -- http://ICCF-Holland.org ///
Reply all
Reply to author
Forward
0 new messages