Patch 9.0.1599
Problem: Cursor not adjusted when near top or bottom of window and
'splitkeep' is not "cursor".
Solution: Move boundary checks to outer cursor move functions, inner
functions should only return valid cursor positions. (Luuk van
Baal, closes #12480)
Files: src/edit.c, src/proto/edit.pro, src/normal.c, src/window.c,
src/testdir/test_window_cmd.vim
*** ../vim-9.0.1598/src/edit.c 2023-05-20 14:06:56.669542805 +0100
--- src/edit.c 2023-06-02 13:50:44.181628850 +0100
***************
*** 2755,2771 ****
/*
* Move the cursor up "n" lines in window "wp".
* Takes care of closed folds.
- * Returns the new cursor line or zero for failure.
*/
! linenr_T
cursor_up_inner(win_T *wp, long n)
{
linenr_T lnum = wp->w_cursor.lnum;
- // This fails if the cursor is already in the first line or the count is
- // larger than the line number and '-' is in 'cpoptions'
- if (lnum <= 1 || (n >= lnum && vim_strchr(p_cpo, CPO_MINUS) != NULL))
- return 0;
if (n >= lnum)
lnum = 1;
else
--- 2755,2766 ----
/*
* Move the cursor up "n" lines in window "wp".
* Takes care of closed folds.
*/
! void
cursor_up_inner(win_T *wp, long n)
{
linenr_T lnum = wp->w_cursor.lnum;
if (n >= lnum)
lnum = 1;
else
***************
*** 2798,2804 ****
lnum -= n;
wp->w_cursor.lnum = lnum;
- return lnum;
}
int
--- 2793,2798 ----
***************
*** 2806,2813 ****
long n,
int upd_topline) // When TRUE: update topline
{
! if (n > 0 && cursor_up_inner(curwin, n) == 0)
return FAIL;
// try to advance to the column we want to be at
coladvance(curwin->w_curswant);
--- 2800,2812 ----
long n,
int upd_topline) // When TRUE: update topline
{
! // This fails if the cursor is already in the first line or the count is
! // larger than the line number and '-' is in 'cpoptions'
! linenr_T lnum = curwin->w_cursor.lnum;
! if (n > 0 && (lnum <= 1
! || (n >= lnum && vim_strchr(p_cpo, CPO_MINUS) != NULL)))
return FAIL;
+ cursor_up_inner(curwin, n);
// try to advance to the column we want to be at
coladvance(curwin->w_curswant);
***************
*** 2821,2843 ****
/*
* Move the cursor down "n" lines in window "wp".
* Takes care of closed folds.
- * Returns the new cursor line or zero for failure.
*/
! linenr_T
cursor_down_inner(win_T *wp, long n)
{
linenr_T lnum = wp->w_cursor.lnum;
linenr_T line_count = wp->w_buffer->b_ml.ml_line_count;
- #ifdef FEAT_FOLDING
- // Move to last line of fold, will fail if it's the end-of-file.
- (void)hasFoldingWin(wp, lnum, NULL, &lnum, TRUE, NULL);
- #endif
- // This fails if the cursor is already in the last line or would move
- // beyond the last line and '-' is in 'cpoptions'
- if (lnum >= line_count
- || (lnum + n > line_count && vim_strchr(p_cpo, CPO_MINUS) != NULL))
- return FAIL;
if (lnum + n >= line_count)
lnum = line_count;
else
--- 2820,2832 ----
/*
* Move the cursor down "n" lines in window "wp".
* Takes care of closed folds.
*/
! void
cursor_down_inner(win_T *wp, long n)
{
linenr_T lnum = wp->w_cursor.lnum;
linenr_T line_count = wp->w_buffer->b_ml.ml_line_count;
if (lnum + n >= line_count)
lnum = line_count;
else
***************
*** 2849,2854 ****
--- 2838,2844 ----
// count each sequence of folded lines as one logical line
while (n--)
{
+ // Move to last line of fold, will fail if it's the end-of-file.
if (hasFoldingWin(wp, lnum, NULL, &last, TRUE, NULL))
lnum = last + 1;
else
***************
*** 2864,2870 ****
lnum += n;
wp->w_cursor.lnum = lnum;
- return lnum;
}
/*
--- 2854,2859 ----
***************
*** 2875,2882 ****
long n,
int upd_topline) // When TRUE: update topline
{
! if (n > 0 && cursor_down_inner(curwin, n) == 0)
return FAIL;
// try to advance to the column we want to be at
coladvance(curwin->w_curswant);
--- 2864,2879 ----
long n,
int upd_topline) // When TRUE: update topline
{
! linenr_T lnum = curwin->w_cursor.lnum;
! linenr_T line_count = curwin->w_buffer->b_ml.ml_line_count;
! // This fails if the cursor is already in the last line or would move
! // beyond the last line and '-' is in 'cpoptions'
! if (n > 0
! && (lnum >= line_count
! || (lnum + n > line_count
! && vim_strchr(p_cpo, CPO_MINUS) != NULL)))
return FAIL;
+ cursor_down_inner(curwin, n);
// try to advance to the column we want to be at
coladvance(curwin->w_curswant);
*** ../vim-9.0.1598/src/proto/edit.pro 2022-09-27 12:30:53.222180148 +0100
--- src/proto/edit.pro 2023-06-02 13:48:13.629873068 +0100
***************
*** 19,27 ****
void beginline(int flags);
int oneright(void);
int oneleft(void);
! linenr_T cursor_up_inner(win_T *wp, long n);
int cursor_up(long n, int upd_topline);
! linenr_T cursor_down_inner(win_T *wp, long n);
int cursor_down(long n, int upd_topline);
int stuff_inserted(int c, long count, int no_esc);
char_u *get_last_insert(void);
--- 19,27 ----
void beginline(int flags);
int oneright(void);
int oneleft(void);
! void cursor_up_inner(win_T *wp, long n);
int cursor_up(long n, int upd_topline);
! void cursor_down_inner(win_T *wp, long n);
int cursor_down(long n, int upd_topline);
int stuff_inserted(int c, long count, int no_esc);
char_u *get_last_insert(void);
*** ../vim-9.0.1598/src/normal.c 2023-05-12 15:47:21.856773279 +0100
--- src/normal.c 2023-06-02 13:48:43.721823978 +0100
***************
*** 2359,2369 ****
else
{
// to previous line
! if (!cursor_up_inner(curwin, 1))
{
retval = FAIL;
break;
}
linelen = linetabsize_str(ml_get_curline());
if (linelen > width1)
curwin->w_curswant += (((linelen - width1 - 1) / width2)
--- 2359,2371 ----
else
{
// to previous line
! if (curwin->w_cursor.lnum <= 1)
{
retval = FAIL;
break;
}
+ cursor_up_inner(curwin, 1);
+
linelen = linetabsize_str(ml_get_curline());
if (linelen > width1)
curwin->w_curswant += (((linelen - width1 - 1) / width2)
***************
*** 2386,2397 ****
else
{
// to next line
! if (!cursor_down_inner(curwin, 1))
{
retval = FAIL;
break;
}
curwin->w_curswant %= width2;
// Check if the cursor has moved below the number display
// when width1 < width2 (with cpoptions+=n). Subtract width2
// to get a negative value for w_curswant, which will get
--- 2388,2402 ----
else
{
// to next line
! if (curwin->w_cursor.lnum
! >= curwin->w_buffer->b_ml.ml_line_count)
{
retval = FAIL;
break;
}
+ cursor_down_inner(curwin, 1);
curwin->w_curswant %= width2;
+
// Check if the cursor has moved below the number display
// when width1 < width2 (with cpoptions+=n). Subtract width2
// to get a negative value for w_curswant, which will get
*** ../vim-9.0.1598/src/window.c 2023-05-24 21:02:20.489162125 +0100
--- src/window.c 2023-06-02 14:07:16.016018650 +0100
***************
*** 1406,1412 ****
win_equal(wp, TRUE,
(flags & WSP_VERT) ? (dir == 'v' ? 'b' : 'h')
: dir == 'h' ? 'b' : 'v');
! else if (*p_spk != 'c' && !is_aucmd_win(wp))
win_fix_scroll(FALSE);
// Don't change the window height/width to 'winheight' / 'winwidth' if a
--- 1406,1412 ----
win_equal(wp, TRUE,
(flags & WSP_VERT) ? (dir == 'v' ? 'b' : 'h')
: dir == 'h' ? 'b' : 'v');
! else if (!is_aucmd_win(wp))
win_fix_scroll(FALSE);
// Don't change the window height/width to 'winheight' / 'winwidth' if a
***************
*** 2012,2018 ****
win_equal_rec(next_curwin == NULL ? curwin : next_curwin, current,
topframe, dir, 0, tabline_height(),
(int)Columns, topframe->fr_height);
! if (*p_spk != 'c' && !is_aucmd_win(next_curwin))
win_fix_scroll(TRUE);
}
--- 2012,2018 ----
win_equal_rec(next_curwin == NULL ? curwin : next_curwin, current,
topframe, dir, 0, tabline_height(),
(int)Columns, topframe->fr_height);
! if (!is_aucmd_win(next_curwin))
win_fix_scroll(TRUE);
}
***************
*** 2822,2829 ****
else
{
win_comp_pos();
! if (*p_spk != 'c')
! win_fix_scroll(FALSE);
}
if (close_curwin)
{
--- 2822,2828 ----
else
{
win_comp_pos();
! win_fix_scroll(FALSE);
}
if (close_curwin)
{
***************
*** 5906,5912 ****
compute_cmdrow();
curtab->tp_ch_used = p_ch;
! if (*p_spk != 'c' && !skip_win_fix_scroll)
win_fix_scroll(TRUE);
#if 0
--- 5905,5911 ----
compute_cmdrow();
curtab->tp_ch_used = p_ch;
! if (!skip_win_fix_scroll)
win_fix_scroll(TRUE);
#if 0
***************
*** 6111,6118 ****
msg_row = row;
msg_col = 0;
! if (*p_spk != 'c')
! win_fix_scroll(TRUE);
redraw_all_later(UPD_NOT_VALID);
}
--- 6110,6116 ----
msg_row = row;
msg_col = 0;
! win_fix_scroll(TRUE);
redraw_all_later(UPD_NOT_VALID);
}
***************
*** 6642,6649 ****
p_ch = MAX(Rows - cmdline_row, 1);
curtab->tp_ch_used = p_ch;
! if (*p_spk != 'c')
! win_fix_scroll(TRUE);
redraw_all_later(UPD_SOME_VALID);
showmode();
--- 6640,6646 ----
p_ch = MAX(Rows - cmdline_row, 1);
curtab->tp_ch_used = p_ch;
! win_fix_scroll(TRUE);
redraw_all_later(UPD_SOME_VALID);
showmode();
***************
*** 6772,6792 ****
}
/*
! * Handle scroll position for 'splitkeep'. Replaces scroll_to_fraction()
! * call from win_new_height(). Instead we iterate over all windows in a
! * tabpage and calculate the new scroll 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)
{
! int diff;
! win_T *wp;
! linenr_T lnum;
skip_update_topline = TRUE;
FOR_ALL_WINDOWS(wp)
{
// Skip when window height has not changed.
--- 6769,6790 ----
}
/*
! * Handle scroll position, depending on 'splitkeep'. Replaces the
! * scroll_to_fraction() call from win_new_height() if 'splitkeep' is "screen"
! * or "topline". Instead we iterate over all windows in a tabpage and
! * calculate the new scroll position.
* TODO: Ensure this also works with wrapped lines.
! * Requires a not fully visible cursor line to be allowed at the bottom of
! * a window("zb"), probably only when 'smoothscroll' is also set.
*/
static void
win_fix_scroll(int resize)
{
! if (*p_spk == 'c')
! return; // 'splitkeep' is "cursor"
skip_update_topline = TRUE;
+ win_T *wp;
FOR_ALL_WINDOWS(wp)
{
// Skip when window height has not changed.
***************
*** 6796,6813 ****
if (*p_spk == 's' && wp->w_winrow != wp->w_prev_winrow
&& wp->w_botline - 1 <= wp->w_buffer->b_ml.ml_line_count)
{
! lnum = wp->w_cursor.lnum;
! diff = (wp->w_winrow - wp->w_prev_winrow)
! + (wp->w_height - wp->w_prev_height);
wp->w_cursor.lnum = wp->w_botline - 1;
// Add difference in height and row to botline.
if (diff > 0)
cursor_down_inner(wp, diff);
else
cursor_up_inner(wp, -diff);
! // 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;
}
else if (wp == curwin)
--- 6794,6815 ----
if (*p_spk == 's' && wp->w_winrow != wp->w_prev_winrow
&& wp->w_botline - 1 <= wp->w_buffer->b_ml.ml_line_count)
{
! int diff = (wp->w_winrow - wp->w_prev_winrow)
! + (wp->w_height - wp->w_prev_height);
! linenr_T lnum = wp->w_cursor.lnum;
wp->w_cursor.lnum = wp->w_botline - 1;
+
// Add difference in height and row to botline.
if (diff > 0)
cursor_down_inner(wp, diff);
else
cursor_up_inner(wp, -diff);
!
! // Scroll to put the new cursor position at the bottom of the
! // screen.
wp->w_fraction = FRACTION_MULT;
scroll_to_fraction(wp, wp->w_prev_height);
+
wp->w_cursor.lnum = lnum;
}
else if (wp == curwin)
***************
*** 6835,6866 ****
static void
win_fix_cursor(int normal)
{
- long so = get_scrolloff_value();
win_T *wp = curwin;
- linenr_T nlnum = 0;
- linenr_T lnum = wp->w_cursor.lnum;
- linenr_T bot;
- linenr_T top;
! if (wp->w_buffer->b_ml.ml_line_count < wp->w_height)
! return;
! if (skip_win_fix_cursor)
return;
// Determine valid cursor range.
! so = MIN(wp->w_height / 2, so);
wp->w_cursor.lnum = wp->w_topline;
! top = cursor_down_inner(wp, so);
wp->w_cursor.lnum = wp->w_botline - 1;
! bot = cursor_up_inner(wp, so);
wp->w_cursor.lnum = lnum;
// Check if cursor position is above or below valid cursor range.
if (lnum > bot && (wp->w_botline - wp->w_buffer->b_ml.ml_line_count) != 1)
nlnum = bot;
else if (lnum < top && wp->w_topline != 1)
nlnum = (so == wp->w_height / 2) ? bot : top;
! if (nlnum) // Cursor is invalid for current scroll position.
{
if (normal) // Save to jumplist and set cursor to avoid scrolling.
{
--- 6837,6869 ----
static void
win_fix_cursor(int normal)
{
win_T *wp = curwin;
! if (skip_win_fix_cursor || wp->w_buffer->b_ml.ml_line_count <
wp->w_height)
return;
// Determine valid cursor range.
! long so = MIN(wp->w_height / 2, get_scrolloff_value());
! linenr_T lnum = wp->w_cursor.lnum;
!
wp->w_cursor.lnum = wp->w_topline;
! cursor_down_inner(wp, so);
! linenr_T top = wp->w_cursor.lnum;
!
wp->w_cursor.lnum = wp->w_botline - 1;
! cursor_up_inner(wp, so);
! linenr_T bot = wp->w_cursor.lnum;
!
wp->w_cursor.lnum = lnum;
+
// Check if cursor position is above or below valid cursor range.
+ linenr_T nlnum = 0;
if (lnum > bot && (wp->w_botline - wp->w_buffer->b_ml.ml_line_count) != 1)
nlnum = bot;
else if (lnum < top && wp->w_topline != 1)
nlnum = (so == wp->w_height / 2) ? bot : top;
! if (nlnum != 0) // Cursor is invalid for current scroll position.
{
if (normal) // Save to jumplist and set cursor to avoid scrolling.
{
*** ../vim-9.0.1598/src/testdir/test_window_cmd.vim 2023-05-06
12:53:33.711345001 +0100
--- src/testdir/test_window_cmd.vim 2023-06-02 14:16:08.579164110 +0100
***************
*** 1819,1827 ****
func Test_splitkeep_misc()
set splitkeep=screen
- set splitbelow
call setline(1, range(1, &lines))
norm Gzz
let top = line('w0')
" No scroll when aucmd_win is opened
--- 1819,1838 ----
func Test_splitkeep_misc()
set splitkeep=screen
call setline(1, range(1, &lines))
+ " Cursor is adjusted to start and end of buffer
+ norm M
+ wincmd s
+ resize 1
+ call assert_equal(1, line('.'))
+ wincmd j
+ norm GM
+ resize 1
+ call assert_equal(&lines, line('.'))
+ only!
+
+ set splitbelow
norm Gzz
let top = line('w0')
" No scroll when aucmd_win is opened
*** ../vim-9.0.1598/src/version.c 2023-06-01 20:26:52.064180242 +0100
--- src/version.c 2023-06-02 14:14:29.471321762 +0100
***************
*** 697,698 ****
--- 697,700 ----
{ /* Add new patch number below this line */
+ /**/
+ 1599,
/**/
--
hundred-and-one symptoms of being an internet addict:
93. New mail alarm on your palmtop annoys other churchgoers.
/// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net \\\
/// \\\
\\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ ///
\\\ help me help AIDS victims -- http://ICCF-Holland.org ///
--
--
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php
---
You received this message because you are subscribed to the Google Groups
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/vim_dev/20230602131704.58A891C0916%40moolenaar.net.