# HG changeset patch # User Bram Moolenaar # Date 1664279104 -7200 # Node ID 04df44c52d6562f13534400a817c39791fdff7de # Parent e2cfe68129e696fe7903e10547bf0165b59a169e patch 9.0.0603: with 'nosplitscroll' folds are not handled correctly Commit: https://github.com/vim/vim/commit/7c1cbb6cd437c6e0c3ccc05840cc931108b4a60a Author: Luuk van Baal Date: Tue Sep 27 12:31:15 2022 +0100 patch 9.0.0603: with 'nosplitscroll' folds are not handled correctly Problem: With 'nosplitscroll' folds are not handled correctly. Solution: Take care of closed folds when moving the cursor. (Luuk van Baal, closes #11234) diff --git a/src/edit.c b/src/edit.c --- a/src/edit.c +++ b/src/edit.c @@ -2749,52 +2749,62 @@ oneleft(void) return OK; } +/* + * 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 +#ifdef FEAT_FOLDING + if (hasAnyFolding(wp)) + { + /* + * Count each sequence of folded lines as one logical line. + */ + // go to the start of the current fold + (void)hasFoldingWin(wp, lnum, &lnum, NULL, TRUE, NULL); + + while (n--) + { + // move up one line + --lnum; + if (lnum <= 1) + break; + // If we entered a fold, move to the beginning, unless in + // Insert mode or when 'foldopen' contains "all": it will open + // in a moment. + if (n > 0 || !((State & MODE_INSERT) || (fdo_flags & FDO_ALL))) + (void)hasFoldingWin(wp, lnum, &lnum, NULL, TRUE, NULL); + } + if (lnum < 1) + lnum = 1; + } + else +#endif + lnum -= n; + + wp->w_cursor.lnum = lnum; + return lnum; +} + int cursor_up( long n, int upd_topline) // When TRUE: update topline { - linenr_T lnum; - - if (n > 0) - { - lnum = curwin->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 FAIL; - if (n >= lnum) - lnum = 1; - else -#ifdef FEAT_FOLDING - if (hasAnyFolding(curwin)) - { - /* - * Count each sequence of folded lines as one logical line. - */ - // go to the start of the current fold - (void)hasFolding(lnum, &lnum, NULL); - - while (n--) - { - // move up one line - --lnum; - if (lnum <= 1) - break; - // If we entered a fold, move to the beginning, unless in - // Insert mode or when 'foldopen' contains "all": it will open - // in a moment. - if (n > 0 || !((State & MODE_INSERT) || (fdo_flags & FDO_ALL))) - (void)hasFolding(lnum, &lnum, NULL); - } - if (lnum < 1) - lnum = 1; - } - else -#endif - lnum -= n; - curwin->w_cursor.lnum = lnum; - } + 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); @@ -2806,6 +2816,55 @@ cursor_up( } /* + * 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 +#ifdef FEAT_FOLDING + if (hasAnyFolding(wp)) + { + linenr_T last; + + // count each sequence of folded lines as one logical line + while (n--) + { + if (hasFoldingWin(wp, lnum, NULL, &last, TRUE, NULL)) + lnum = last + 1; + else + ++lnum; + if (lnum >= line_count) + break; + } + if (lnum > line_count) + lnum = line_count; + } + else +#endif + lnum += n; + + wp->w_cursor.lnum = lnum; + return lnum; +} + +/* * Cursor down a number of logical lines. */ int @@ -2813,47 +2872,8 @@ cursor_down( long n, int upd_topline) // When TRUE: update topline { - linenr_T lnum; - - if (n > 0) - { - lnum = curwin->w_cursor.lnum; -#ifdef FEAT_FOLDING - // Move to last line of fold, will fail if it's the end-of-file. - (void)hasFolding(lnum, NULL, &lnum); -#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 >= curbuf->b_ml.ml_line_count - || (lnum + n > curbuf->b_ml.ml_line_count - && vim_strchr(p_cpo, CPO_MINUS) != NULL)) - return FAIL; - if (lnum + n >= curbuf->b_ml.ml_line_count) - lnum = curbuf->b_ml.ml_line_count; - else -#ifdef FEAT_FOLDING - if (hasAnyFolding(curwin)) - { - linenr_T last; - - // count each sequence of folded lines as one logical line - while (n--) - { - if (hasFolding(lnum, NULL, &last)) - lnum = last + 1; - else - ++lnum; - if (lnum >= curbuf->b_ml.ml_line_count) - break; - } - if (lnum > curbuf->b_ml.ml_line_count) - lnum = curbuf->b_ml.ml_line_count; - } - else -#endif - lnum += n; - curwin->w_cursor.lnum = lnum; - } + 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); diff --git a/src/proto/edit.pro b/src/proto/edit.pro --- a/src/proto/edit.pro +++ b/src/proto/edit.pro @@ -19,7 +19,9 @@ char_u *add_char2buf(int c, char_u *s); 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); diff --git a/src/testdir/dumps/Test_nosplitscroll_fold_1.dump b/src/testdir/dumps/Test_nosplitscroll_fold_1.dump new file mode 100644 --- /dev/null +++ b/src/testdir/dumps/Test_nosplitscroll_fold_1.dump @@ -0,0 +1,10 @@ +| +0#0000e05#a8a8a8255@1|1| |+|-@1| @1|7| |l|i|n|e|s|:| |i|n|t| |F|u|n|c|N|a|m|e|(|)| |{|-@40 +| +0#af5f00255#ffffff0@1|8| >a+0#0000000&|f|t|e|r| |f|o|l|d| @60 +| +0#0000e05#a8a8a8255@1|9| |+|-@1| @1|7| |l|i|n|e|s|:| |i|n|t| |F|u|n|c|N|a|m|e|(|)| |{|-@40 +| +0#af5f00255#ffffff0|1|6| |a+0#0000000&|f|t|e|r| |f|o|l|d| @60 +|[+3&&|N|o| |N|a|m|e|]| |[|+|]| @43|8|,|1| @11|T|o|p +| +0#af5f00255&|2|4| |a+0#0000000&|f|t|e|r| |f|o|l|d| @60 +| +0#0000e05#a8a8a8255|2|5| |+|-@1| @1|7| |l|i|n|e|s|:| |i|n|t| |F|u|n|c|N|a|m|e|(|)| |{|-@40 +| +0#af5f00255#ffffff0|3|2| |a+0#0000000&|f|t|e|r| |f|o|l|d| @60 +|[+1&&|N|o| |N|a|m|e|]| |[|+|]| @43|2|3|,|1| @10|3|2|% +|:+0&&|w|i|n|c|m|d| |s| @65 diff --git a/src/testdir/dumps/Test_nosplitscroll_fold_2.dump b/src/testdir/dumps/Test_nosplitscroll_fold_2.dump new file mode 100644 --- /dev/null +++ b/src/testdir/dumps/Test_nosplitscroll_fold_2.dump @@ -0,0 +1,10 @@ +| +0#0000e05#a8a8a8255@1|1| |+|-@1| @1|7| |l|i|n|e|s|:| |i|n|t| |F|u|n|c|N|a|m|e|(|)| |{|-@40 +| +0#af5f00255#ffffff0@1|8| |a+0#0000000&|f|t|e|r| |f|o|l|d| @60 +| +0#0000e05#a8a8a8255@1|9| |+|-@1| @1|7| |l|i|n|e|s|:| |i|n|t| |F|u|n|c|N|a|m|e|(|)| |{|-@40 +| +0#af5f00255#ffffff0|1|6| |a+0#0000000&|f|t|e|r| |f|o|l|d| @60 +| +0#0000e05#a8a8a8255|1|7| >+|-@1| @1|7| |l|i|n|e|s|:| |i|n|t| |F|u|n|c|N|a|m|e|(|)| |{|-@40 +| +0#af5f00255#ffffff0|2|4| |a+0#0000000&|f|t|e|r| |f|o|l|d| @60 +| +0#0000e05#a8a8a8255|2|5| |+|-@1| @1|7| |l|i|n|e|s|:| |i|n|t| |F|u|n|c|N|a|m|e|(|)| |{|-@40 +| +0#af5f00255#ffffff0|3|2| |a+0#0000000&|f|t|e|r| |f|o|l|d| @60 +| +0#0000e05#a8a8a8255|3@1| |+|-@1| @1|7| |l|i|n|e|s|:| |i|n|t| |F|u|n|c|N|a|m|e|(|)| |{|-@40 +|:+0#0000000#ffffff0|q|u|i|t| @51|1|7|,|1| @9|T|o|p| diff --git a/src/testdir/dumps/Test_nosplitscroll_fold_3.dump b/src/testdir/dumps/Test_nosplitscroll_fold_3.dump new file mode 100644 --- /dev/null +++ b/src/testdir/dumps/Test_nosplitscroll_fold_3.dump @@ -0,0 +1,10 @@ +| +0#0000e05#a8a8a8255@1|1| |+|-@1| @1|7| |l|i|n|e|s|:| |i|n|t| |F|u|n|c|N|a|m|e|(|)| |{|-@40 +| +0#af5f00255#ffffff0@1|8| |a+0#0000000&|f|t|e|r| |f|o|l|d| @60 +| +0#0000e05#a8a8a8255@1|9| |+|-@1| @1|7| |l|i|n|e|s|:| |i|n|t| |F|u|n|c|N|a|m|e|(|)| |{|-@40 +|[+1#0000000#ffffff0|N|o| |N|a|m|e|]| |[|+|]| @43|1|,|1| @11|T|o|p +| +0#0000e05#a8a8a8255|1|7| |+|-@1| @1|7| |l|i|n|e|s|:| |i|n|t| |F|u|n|c|N|a|m|e|(|)| |{|-@40 +| +0#af5f00255#ffffff0|2|4| >a+0#0000000&|f|t|e|r| |f|o|l|d| @60 +| +0#0000e05#a8a8a8255|2|5| |+|-@1| @1|7| |l|i|n|e|s|:| |i|n|t| |F|u|n|c|N|a|m|e|(|)| |{|-@40 +| +0#af5f00255#ffffff0|3|2| |a+0#0000000&|f|t|e|r| |f|o|l|d| @60 +|[+3&&|N|o| |N|a|m|e|]| |[|+|]| @43|2|4|,|1| @10|2|5|% +|:+0&&|b|e|l|o|w| |s|p|l|i|t| @62 diff --git a/src/testdir/test_window_cmd.vim b/src/testdir/test_window_cmd.vim --- a/src/testdir/test_window_cmd.vim +++ b/src/testdir/test_window_cmd.vim @@ -1848,4 +1848,34 @@ function Test_nosplitscroll_callback() call VerifyScreenDump(buf, 'Test_nosplitscroll_callback_4', {}) endfunc +function Test_nosplitscroll_fold() +CheckScreendump + +let lines =<< trim END + set nosplitscroll + set foldmethod=marker + set number + let line = 1 + for n in range(1, &lines) + call setline(line, ['int FuncName() {/*{{{*/', 1, 2, 3, 4, 5, '}/*}}}*/', + \ 'after fold']) + let line += 8 + endfor +END + call writefile(lines, 'XTestNosplitscrollFold', 'D') + let buf = RunVimInTerminal('-S XTestNosplitscrollFold', #{rows: 10}) + + call term_sendkeys(buf, "L:wincmd s\") + call VerifyScreenDump(buf, 'Test_nosplitscroll_fold_1', {}) + + call term_sendkeys(buf, ":quit\") + call VerifyScreenDump(buf, 'Test_nosplitscroll_fold_2', {}) + + call term_sendkeys(buf, "H:below split\") + call VerifyScreenDump(buf, 'Test_nosplitscroll_fold_3', {}) + + call term_sendkeys(buf, ":wincmd k\:quit\") + call VerifyScreenDump(buf, 'Test_nosplitscroll_fold_4', {}) +endfunction + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -700,6 +700,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 603, +/**/ 602, /**/ 601, diff --git a/src/window.c b/src/window.c --- a/src/window.c +++ b/src/window.c @@ -6351,7 +6351,7 @@ set_fraction(win_T *wp) /* * 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. + * 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). @@ -6359,8 +6359,9 @@ set_fraction(win_T *wp) static void win_fix_scroll(int resize) { - win_T *wp; - linenr_T lnum; + int diff; + win_T *wp; + linenr_T lnum; skip_update_topline = TRUE; // avoid scrolling in curs_columns() FOR_ALL_WINDOWS(wp) @@ -6368,13 +6369,18 @@ win_fix_scroll(int resize) // Skip when window height has not changed. if (wp->w_height != wp->w_prev_height) { - // Determine botline needed to avoid scrolling and set cursor. + // If window has moved update botline to keep the same screenlines. 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_winrow - wp->w_prev_winrow) - + (wp->w_height - wp->w_prev_height)); + 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); @@ -6405,9 +6411,12 @@ win_fix_scroll(int resize) static void win_fix_cursor(int normal) { - win_T *wp = curwin; - long so = get_scrolloff_value(); - linenr_T nlnum = 0; + 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; @@ -6415,28 +6424,30 @@ win_fix_cursor(int normal) if (skip_win_fix_cursor) return; #endif - + // Determine valid cursor range. 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) - 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 + 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); + // 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; + + wp->w_cursor.lnum = lnum; + + if (nlnum) // Cursor is invalid for current scroll position. + { + if (normal) // Save to jumplist and set cursor to avoid scrolling. + { + setmark('\''); + wp->w_cursor.lnum = nlnum; curs_columns(TRUE); // validate w_wrow } - else - { // Ensure cursor stays visible if we are not in normal mode. + else // Scroll instead when not in normal mode. + { wp->w_fraction = 0.5 * FRACTION_MULT; scroll_to_fraction(wp, wp->w_prev_height); validate_botline_win(curwin);