To: vim_dev@googlegroups.com Subject: Patch 9.0.0603 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 9.0.0603 Problem: With 'nosplitscroll' folds are not handled correctly. Solution: Take care of closed folds when moving the cursor. (Luuk van Baal, closes #11234) Files: src/edit.c, src/proto/edit.pro, src/window.c, src/testdir/test_window_cmd.vim, src/testdir/dumps/Test_nosplitscroll_fold_1.dump, src/testdir/dumps/Test_nosplitscroll_fold_2.dump, src/testdir/dumps/Test_nosplitscroll_fold_3.dump, src/testdir/dumps/Test_nosplitscroll_fold_4.dump *** ../vim-9.0.0602/src/edit.c 2022-09-25 20:58:08.801019377 +0100 --- src/edit.c 2022-09-27 12:28:59.802429780 +0100 *************** *** 2749,2800 **** return OK; } ! 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; } // try to advance to the column we want to be at coladvance(curwin->w_curswant); --- 2749,2810 ---- 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 + { + 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,2859 **** } /* ! * Cursor down a number of logical lines. */ ! int ! 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; } // try to advance to the column we want to be at coladvance(curwin->w_curswant); --- 2816,2879 ---- } /* ! * 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 + cursor_down( + 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); *** ../vim-9.0.0602/src/proto/edit.pro 2022-08-16 14:51:49.442515641 +0100 --- src/proto/edit.pro 2022-09-27 12:22:20.579274093 +0100 *************** *** 19,25 **** --- 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); *** ../vim-9.0.0602/src/window.c 2022-09-26 23:08:17.444945122 +0100 --- src/window.c 2022-09-27 12:28:07.622543490 +0100 *************** *** 6351,6357 **** /* * 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). --- 6351,6357 ---- /* * 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 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,6366 **** static void win_fix_scroll(int resize) { ! win_T *wp; ! linenr_T lnum; skip_update_topline = TRUE; // avoid scrolling in curs_columns() FOR_ALL_WINDOWS(wp) --- 6359,6367 ---- static void win_fix_scroll(int resize) { ! int diff; ! win_T *wp; ! linenr_T lnum; skip_update_topline = TRUE; // avoid scrolling in curs_columns() FOR_ALL_WINDOWS(wp) *************** *** 6368,6380 **** // 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 (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)); // Bring the new cursor position to the bottom of the screen. wp->w_fraction = FRACTION_MULT; scroll_to_fraction(wp, wp->w_prev_height); --- 6369,6386 ---- // Skip when window height has not changed. if (wp->w_height != wp->w_prev_height) { ! // If window has moved update botline to keep the same screenlines. if (wp->w_winrow != wp->w_prev_winrow) { 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); *************** *** 6405,6413 **** static void win_fix_cursor(int normal) { ! 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; --- 6411,6422 ---- 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; *************** *** 6415,6442 **** if (skip_win_fix_cursor) return; #endif ! 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 curs_columns(TRUE); // validate w_wrow } ! else ! { // Ensure cursor stays visible if we are not in normal mode. wp->w_fraction = 0.5 * FRACTION_MULT; scroll_to_fraction(wp, wp->w_prev_height); validate_botline_win(curwin); --- 6424,6453 ---- if (skip_win_fix_cursor) return; #endif ! // 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); ! // 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 // 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); *** ../vim-9.0.0602/src/testdir/test_window_cmd.vim 2022-09-23 12:56:49.305714732 +0100 --- src/testdir/test_window_cmd.vim 2022-09-27 12:25:33.110875003 +0100 *************** *** 1848,1851 **** --- 1848,1881 ---- 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 *** ../vim-9.0.0602/src/testdir/dumps/Test_nosplitscroll_fold_1.dump 2022-09-27 12:30:28.514234792 +0100 --- src/testdir/dumps/Test_nosplitscroll_fold_1.dump 2022-09-27 12:25:33.110875003 +0100 *************** *** 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 *** ../vim-9.0.0602/src/testdir/dumps/Test_nosplitscroll_fold_2.dump 2022-09-27 12:30:28.518234783 +0100 --- src/testdir/dumps/Test_nosplitscroll_fold_2.dump 2022-09-27 12:25:33.110875003 +0100 *************** *** 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| *** ../vim-9.0.0602/src/testdir/dumps/Test_nosplitscroll_fold_3.dump 2022-09-27 12:30:28.522234775 +0100 --- src/testdir/dumps/Test_nosplitscroll_fold_3.dump 2022-09-27 12:25:33.110875003 +0100 *************** *** 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 *** ../vim-9.0.0602/src/version.c 2022-09-27 11:57:09.785256754 +0100 --- src/version.c 2022-09-27 12:30:04.742287235 +0100 *************** *** 701,702 **** --- 701,704 ---- { /* Add new patch number below this line */ + /**/ + 603, /**/ -- hundred-and-one symptoms of being an internet addict: 192. Your boss asks you to "go fer" coffee and you come up with 235 FTP sites. /// Bram Moolenaar -- Bram@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 ///