# HG changeset patch # User Christian Brabandt # Date 1711476004 -3600 # Node ID 8079960136dbf34a471d81a530aa063540864b90 # Parent 86aff5d0f82afe69c712c2d95321f268a1e92ca1 patch 9.1.0211: page-wise scrolling does not support smooth-scrolling Commit: https://github.com/vim/vim/commit/b9f5b95b7bec2414a5a96010514702d99afea18e Author: Luuk van Baal Date: Tue Mar 26 18:46:45 2024 +0100 patch 9.1.0211: page-wise scrolling does not support smooth-scrolling Problem: Page-wise scrolling with Ctrl-F/Ctrl-B implements it's own logic to change the topline and cursor. In doing so, skipcol is not handled properly for 'smoothscroll', and virtual lines. Solution: Re-use the logic from Ctrl-E/Ctrl-Y while staying backward compatible as much as possible. closes: #14268 Signed-off-by: Luuk van Baal Signed-off-by: Christian Brabandt diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -1,4 +1,4 @@ -*options.txt* For Vim version 9.1. Last change: 2024 Mar 25 +*options.txt* For Vim version 9.1. Last change: 2024 Mar 26 VIM REFERENCE MANUAL by Bram Moolenaar @@ -7502,8 +7502,8 @@ A jump table for the options with a shor highlighted with |hl-NonText|. You may also want to add "lastline" to the 'display' option to show as much of the last line as possible. - NOTE: only partly implemented, currently works with CTRL-E, CTRL-Y - and scrolling with the mouse. + NOTE: only partly implemented, currently works with CTRL-E, CTRL-Y, + CTRL-B, CTRL-F and scrolling with the mouse. *'softtabstop'* *'sts'* 'softtabstop' 'sts' number (default 0) diff --git a/runtime/doc/version9.txt b/runtime/doc/version9.txt --- a/runtime/doc/version9.txt +++ b/runtime/doc/version9.txt @@ -41554,6 +41554,8 @@ Other improvements *new-other-9.2* Changed *changed-9.2* ------- +- use 'smoothscroll' logic for CTRL-F and CTRL-B for pagewise scrolling + Added *added-9.2* ----- diff --git a/src/move.c b/src/move.c --- a/src/move.c +++ b/src/move.c @@ -2047,26 +2047,6 @@ check_topfill( } } } - -/* - * Use as many filler lines as possible for w_topline. Make sure w_topline - * is still visible. - */ - static void -max_topfill(void) -{ - int n; - - n = plines_nofill(curwin->w_topline); - if (n >= curwin->w_height) - curwin->w_topfill = 0; - else - { - curwin->w_topfill = diff_check_fill(curwin, curwin->w_topline); - if (curwin->w_topfill + n > curwin->w_height) - curwin->w_topfill = curwin->w_height - n; - } -} #endif /* @@ -2269,38 +2249,6 @@ botline_forw(lineoff_T *lp) } } -#ifdef FEAT_DIFF -/* - * Switch from including filler lines below lp->lnum to including filler - * lines above loff.lnum + 1. This keeps pointing to the same line. - * When there are no filler lines nothing changes. - */ - static void -botline_topline(lineoff_T *lp) -{ - if (lp->fill > 0) - { - ++lp->lnum; - lp->fill = diff_check_fill(curwin, lp->lnum) - lp->fill + 1; - } -} - -/* - * Switch from including filler lines above lp->lnum to including filler - * lines below loff.lnum - 1. This keeps pointing to the same line. - * When there are no filler lines nothing changes. - */ - static void -topline_botline(lineoff_T *lp) -{ - if (lp->fill > 0) - { - lp->fill = diff_check_fill(curwin, lp->lnum) - lp->fill + 1; - --lp->lnum; - } -} -#endif - /* * Recompute topline to put the cursor at the top of the window. * Scroll at least "min_scroll" lines. @@ -3077,8 +3025,6 @@ cursor_correct(void) curwin->w_valid |= VALID_TOPLINE; } -static void get_scroll_overlap(lineoff_T *lp, int dir); - /* * Move screen "count" pages up ("dir" is BACKWARD) or down ("dir" is FORWARD) * and update the screen. @@ -3088,313 +3034,48 @@ static void get_scroll_overlap(lineoff_T int onepage(int dir, long count) { - long n; - int retval = OK; - lineoff_T loff; - linenr_T old_topline = curwin->w_topline; - long so = get_scrolloff_value(); +#ifdef FEAT_DIFF + int prev_topfill = curwin->w_topfill; +#endif + linenr_T prev_topline = curwin->w_topline; + colnr_T prev_skipcol = curwin->w_skipcol; + + // Scroll 'window' or current window height lines. + count *= ((ONE_WINDOW && p_window > 0 && p_window < Rows - 1) ? + p_window : curwin->w_height) - 2; - if (curbuf->b_ml.ml_line_count == 1) // nothing to do + if (curwin->w_p_sms) + scroll_redraw(dir == FORWARD, count); + else { - beep_flush(); - return FAIL; + // Scroll at least one full line without 'smoothscroll'. +#ifdef FEAT_DIFF + count -= plines_nofill(curwin->w_topline); +#else + count -= plines(curwin->w_topline); +#endif + scroll_redraw(dir == FORWARD, 1); + + // Temporarily set 'smoothscroll' so that scrolling count lines + // does not skip over parts of the buffer with wrapped lines. + curwin->w_p_sms = TRUE; + if (count > 0) + scroll_redraw(dir == FORWARD, count); + curwin->w_p_sms = FALSE; } - for ( ; count > 0; --count) - { - validate_botline(); - /* - * It's an error to move a page up when the first line is already on - * the screen. It's an error to move a page down when the last line - * is on the screen and the topline is 'scrolloff' lines from the - * last line. - */ - if (dir == FORWARD - ? ((curwin->w_topline >= curbuf->b_ml.ml_line_count - so) - && curwin->w_botline > curbuf->b_ml.ml_line_count) - : (curwin->w_topline == 1 -#ifdef FEAT_DIFF - && curwin->w_topfill == - diff_check_fill(curwin, curwin->w_topline) -#endif - )) - { - beep_flush(); - retval = FAIL; - break; - } - -#ifdef FEAT_DIFF - loff.fill = 0; -#endif - if (dir == FORWARD) - { - if (ONE_WINDOW && p_window > 0 && p_window < Rows - 1) - { - // Vi compatible scrolling - if (p_window <= 2) - ++curwin->w_topline; - else - curwin->w_topline += p_window - 2; - if (curwin->w_topline > curbuf->b_ml.ml_line_count) - curwin->w_topline = curbuf->b_ml.ml_line_count; - curwin->w_cursor.lnum = curwin->w_topline; - } - else if (curwin->w_botline > curbuf->b_ml.ml_line_count) - { - // at end of file - curwin->w_topline = curbuf->b_ml.ml_line_count; -#ifdef FEAT_DIFF - curwin->w_topfill = 0; -#endif - curwin->w_valid &= ~(VALID_WROW|VALID_CROW); - } - else - { - // For the overlap, start with the line just below the window - // and go upwards. - loff.lnum = curwin->w_botline; -#ifdef FEAT_DIFF - loff.fill = diff_check_fill(curwin, loff.lnum) - - curwin->w_filler_rows; -#endif - get_scroll_overlap(&loff, -1); - curwin->w_topline = loff.lnum; -#ifdef FEAT_DIFF - curwin->w_topfill = loff.fill; - check_topfill(curwin, FALSE); -#endif - curwin->w_cursor.lnum = curwin->w_topline; - curwin->w_valid &= ~(VALID_WCOL|VALID_CHEIGHT|VALID_WROW| - VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP); - } - } - else // dir == BACKWARDS - { + int nochange = curwin->w_topline == prev_topline #ifdef FEAT_DIFF - if (curwin->w_topline == 1) - { - // Include max number of filler lines - max_topfill(); - continue; - } -#endif - if (ONE_WINDOW && p_window > 0 && p_window < Rows - 1) - { - // Vi compatible scrolling (sort of) - if (p_window <= 2) - --curwin->w_topline; - else - curwin->w_topline -= p_window - 2; - if (curwin->w_topline < 1) - curwin->w_topline = 1; - curwin->w_cursor.lnum = curwin->w_topline + p_window - 1; - if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count) - curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; - continue; - } - - // Find the line at the top of the window that is going to be the - // line at the bottom of the window. Make sure this results in - // the same line as before doing CTRL-F. - loff.lnum = curwin->w_topline - 1; -#ifdef FEAT_DIFF - loff.fill = diff_check_fill(curwin, loff.lnum + 1) - - curwin->w_topfill; -#endif - get_scroll_overlap(&loff, 1); - - if (loff.lnum >= curbuf->b_ml.ml_line_count) - { - loff.lnum = curbuf->b_ml.ml_line_count; -#ifdef FEAT_DIFF - loff.fill = 0; - } - else - { - botline_topline(&loff); -#endif - } - curwin->w_cursor.lnum = loff.lnum; - - // Find the line just above the new topline to get the right line - // at the bottom of the window. - n = 0; - while (n <= curwin->w_height && loff.lnum >= 1) - { - topline_back(&loff); - if (loff.height == MAXCOL) - n = MAXCOL; - else - n += loff.height; - } - if (loff.lnum < 1) // at begin of file - { - curwin->w_topline = 1; -#ifdef FEAT_DIFF - max_topfill(); -#endif - curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE); - } - else - { - // Go two lines forward again. -#ifdef FEAT_DIFF - topline_botline(&loff); -#endif - botline_forw(&loff); - botline_forw(&loff); -#ifdef FEAT_DIFF - botline_topline(&loff); + && curwin->w_topfill == prev_topfill #endif -#ifdef FEAT_FOLDING - // We're at the wrong end of a fold now. - (void)hasFolding(loff.lnum, &loff.lnum, NULL); -#endif - - // Always scroll at least one line. Avoid getting stuck on - // very long lines. - if (loff.lnum >= curwin->w_topline -#ifdef FEAT_DIFF - && (loff.lnum > curwin->w_topline - || loff.fill >= curwin->w_topfill) -#endif - ) - { -#ifdef FEAT_DIFF - // First try using the maximum number of filler lines. If - // that's not enough, backup one line. - loff.fill = curwin->w_topfill; - if (curwin->w_topfill < diff_check_fill(curwin, - curwin->w_topline)) - max_topfill(); - if (curwin->w_topfill == loff.fill) -#endif - { - --curwin->w_topline; -#ifdef FEAT_DIFF - curwin->w_topfill = 0; -#endif - curwin->w_valid &= ~(VALID_WROW|VALID_CROW); - } - comp_botline(curwin); - curwin->w_cursor.lnum = curwin->w_botline - 1; - curwin->w_valid &= - ~(VALID_WCOL|VALID_CHEIGHT|VALID_WROW|VALID_CROW); - } - else - { - curwin->w_topline = loff.lnum; -#ifdef FEAT_DIFF - curwin->w_topfill = loff.fill; - check_topfill(curwin, FALSE); -#endif - curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE); - } - } - } - } -#ifdef FEAT_FOLDING - foldAdjustCursor(); -#endif - cursor_correct(); - check_cursor_col(); - if (retval == OK) - beginline(BL_SOL | BL_FIX); - curwin->w_valid &= ~(VALID_WCOL|VALID_WROW|VALID_VIRTCOL); + && curwin->w_skipcol == prev_skipcol; - if (retval == OK && dir == FORWARD) - { - // Avoid the screen jumping up and down when 'scrolloff' is non-zero. - // But make sure we scroll at least one line (happens with mix of long - // wrapping lines and non-wrapping line). - if (check_top_offset()) - { - scroll_cursor_top(1, FALSE); - if (curwin->w_topline <= old_topline - && old_topline < curbuf->b_ml.ml_line_count) - { - curwin->w_topline = old_topline + 1; -#ifdef FEAT_FOLDING - (void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL); -#endif - } - } -#ifdef FEAT_FOLDING - else if (curwin->w_botline > curbuf->b_ml.ml_line_count) - (void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL); -#endif - } - - redraw_later(UPD_VALID); - return retval; -} - -/* - * Decide how much overlap to use for page-up or page-down scrolling. - * This is symmetric, so that doing both keeps the same lines displayed. - * Three lines are examined: - * - * before CTRL-F after CTRL-F / before CTRL-B - * etc. l1 - * l1 last but one line ------------ - * l2 last text line l2 top text line - * ------------- l3 second text line - * l3 etc. - */ - static void -get_scroll_overlap(lineoff_T *lp, int dir) -{ - int h1, h2, h3, h4; - int min_height = curwin->w_height - 2; - lineoff_T loff0, loff1, loff2; + if (nochange) + beep_flush(); + else if (!curwin->w_p_sms || curwin->w_skipcol == prev_skipcol) + beginline(BL_SOL | BL_FIX); -#ifdef FEAT_DIFF - if (lp->fill > 0) - lp->height = 1; - else - lp->height = plines_nofill(lp->lnum); -#else - lp->height = plines(lp->lnum); -#endif - h1 = lp->height; - if (h1 > min_height) - return; // no overlap - - loff0 = *lp; - if (dir > 0) - botline_forw(lp); - else - topline_back(lp); - h2 = lp->height; - if (h2 == MAXCOL || h2 + h1 > min_height) - { - *lp = loff0; // no overlap - return; - } - - loff1 = *lp; - if (dir > 0) - botline_forw(lp); - else - topline_back(lp); - h3 = lp->height; - if (h3 == MAXCOL || h3 + h2 > min_height) - { - *lp = loff0; // no overlap - return; - } - - loff2 = *lp; - if (dir > 0) - botline_forw(lp); - else - topline_back(lp); - h4 = lp->height; - if (h4 == MAXCOL || h4 + h3 + h2 > min_height || h3 + h2 + h1 > min_height) - *lp = loff1; // 1 line overlap - else - *lp = loff2; // 2 lines overlap + return nochange; } /* diff --git a/src/testdir/test_diffmode.vim b/src/testdir/test_diffmode.vim --- a/src/testdir/test_diffmode.vim +++ b/src/testdir/test_diffmode.vim @@ -1647,34 +1647,38 @@ endfunc func Test_diff_scroll_many_filler() 20new vnew - call setline(1, ['^^^', '^^^', '$$$', '$$$']) + call setline(1, range(1, 40)) diffthis setlocal scrolloff=0 wincmd p - call setline(1, ['^^^', '^^^'] + repeat(['###'], 41) + ['$$$', '$$$']) + call setline(1, range(1, 20)->reverse() + ['###']->repeat(41) + range(21, 40)->reverse()) diffthis setlocal scrolloff=0 wincmd p redraw " Note: need a redraw after each scroll, otherwise the test always passes. - normal! G - redraw - call assert_equal(3, winsaveview().topline) - call assert_equal(18, winsaveview().topfill) - exe "normal! \" - redraw - call assert_equal(3, winsaveview().topline) - call assert_equal(19, winsaveview().topfill) - exe "normal! \" - redraw - call assert_equal(2, winsaveview().topline) - call assert_equal(0, winsaveview().topfill) - exe "normal! \" - redraw - call assert_equal(1, winsaveview().topline) - call assert_equal(0, winsaveview().topfill) + for _ in range(2) + normal! G + redraw + call assert_equal(40, winsaveview().topline) + call assert_equal(19, winsaveview().topfill) + exe "normal! \" + redraw + call assert_equal(22, winsaveview().topline) + call assert_equal(0, winsaveview().topfill) + exe "normal! \" + redraw + call assert_equal(4, winsaveview().topline) + call assert_equal(0, winsaveview().topfill) + exe "normal! \" + redraw + call assert_equal(1, winsaveview().topline) + call assert_equal(0, winsaveview().topfill) + set smoothscroll + endfor + set smoothscroll& bwipe! bwipe! endfunc diff --git a/src/testdir/test_edit.vim b/src/testdir/test_edit.vim --- a/src/testdir/test_edit.vim +++ b/src/testdir/test_edit.vim @@ -1294,15 +1294,15 @@ func Test_edit_PAGEUP_PAGEDOWN() call feedkeys("i\\", 'tnix') call assert_equal([0, 30, 1, 0], getpos('.')) call feedkeys("A\\", 'tnix') - call assert_equal([0, 29, 1, 0], getpos('.')) + call assert_equal([0, 30, 1, 0], getpos('.')) call feedkeys("A\\", 'tnix') - call assert_equal([0, 21, 1, 0], getpos('.')) + call assert_equal([0, 23, 1, 0], getpos('.')) call feedkeys("A\\", 'tnix') - call assert_equal([0, 13, 1, 0], getpos('.')) + call assert_equal([0, 15, 1, 0], getpos('.')) call feedkeys("A\\", 'tnix') - call assert_equal([0, 5, 1, 0], getpos('.')) + call assert_equal([0, 10, 1, 0], getpos('.')) call feedkeys("A\\", 'tnix') - call assert_equal([0, 5, 11, 0], getpos('.')) + call assert_equal([0, 10, 11, 0], getpos('.')) " is the same as " is the same as call cursor(1, 1) @@ -1317,28 +1317,28 @@ func Test_edit_PAGEUP_PAGEDOWN() call feedkeys("i\\", 'tnix') call assert_equal([0, 30, 1, 0], getpos('.')) call feedkeys("A\\", 'tnix') - call assert_equal([0, 29, 1, 0], getpos('.')) + call assert_equal([0, 30, 1, 0], getpos('.')) call feedkeys("A\\", 'tnix') - call assert_equal([0, 21, 1, 0], getpos('.')) + call assert_equal([0, 23, 1, 0], getpos('.')) call feedkeys("A\\", 'tnix') - call assert_equal([0, 13, 1, 0], getpos('.')) + call assert_equal([0, 15, 1, 0], getpos('.')) call feedkeys("A\\", 'tnix') - call assert_equal([0, 5, 1, 0], getpos('.')) + call assert_equal([0, 10, 1, 0], getpos('.')) call feedkeys("A\\", 'tnix') - call assert_equal([0, 5, 11, 0], getpos('.')) + call assert_equal([0, 10, 11, 0], getpos('.')) set nostartofline call cursor(30, 11) norm! zt call feedkeys("A\\", 'tnix') - call assert_equal([0, 29, 11, 0], getpos('.')) + call assert_equal([0, 30, 11, 0], getpos('.')) call feedkeys("A\\", 'tnix') - call assert_equal([0, 21, 11, 0], getpos('.')) + call assert_equal([0, 23, 11, 0], getpos('.')) call feedkeys("A\\", 'tnix') - call assert_equal([0, 13, 11, 0], getpos('.')) + call assert_equal([0, 15, 11, 0], getpos('.')) call feedkeys("A\\", 'tnix') - call assert_equal([0, 5, 11, 0], getpos('.')) + call assert_equal([0, 10, 11, 0], getpos('.')) call feedkeys("A\\", 'tnix') - call assert_equal([0, 5, 11, 0], getpos('.')) + call assert_equal([0, 10, 11, 0], getpos('.')) call cursor(1, 1) call feedkeys("A\\", 'tnix') call assert_equal([0, 9, 11, 0], getpos('.')) @@ -1355,15 +1355,15 @@ func Test_edit_PAGEUP_PAGEDOWN() call cursor(30, 11) norm! zt call feedkeys("A\\", 'tnix') - call assert_equal([0, 29, 11, 0], getpos('.')) + call assert_equal([0, 30, 11, 0], getpos('.')) call feedkeys("A\\", 'tnix') - call assert_equal([0, 21, 11, 0], getpos('.')) + call assert_equal([0, 23, 11, 0], getpos('.')) call feedkeys("A\\", 'tnix') - call assert_equal([0, 13, 11, 0], getpos('.')) + call assert_equal([0, 15, 11, 0], getpos('.')) call feedkeys("A\\", 'tnix') - call assert_equal([0, 5, 11, 0], getpos('.')) + call assert_equal([0, 10, 11, 0], getpos('.')) call feedkeys("A\\", 'tnix') - call assert_equal([0, 5, 11, 0], getpos('.')) + call assert_equal([0, 10, 11, 0], getpos('.')) call cursor(1, 1) call feedkeys("A\\", 'tnix') call assert_equal([0, 9, 11, 0], getpos('.')) diff --git a/src/testdir/test_normal.vim b/src/testdir/test_normal.vim --- a/src/testdir/test_normal.vim +++ b/src/testdir/test_normal.vim @@ -130,7 +130,7 @@ func Test_normal01_keymodel() call assert_equal([0, 1, 1, 0], getpos("'<")) call assert_equal([0, 3, 1, 0], getpos("'>")) call feedkeys("Gz\8|\y", 'xt') - call assert_equal([0, 2, 1, 0], getpos("'<")) + call assert_equal([0, 3, 1, 0], getpos("'<")) call assert_equal([0, 3, 8, 0], getpos("'>")) " Test for and call cursor(2, 12) @@ -912,12 +912,10 @@ func Test_normal14_page() set scrolloff=0 100 exe "norm! $\" - call assert_equal('92', getline('.')) call assert_equal([0, 92, 1, 0, 1], getcurpos()) 100 set nostartofline exe "norm! $\" - call assert_equal('92', getline('.')) call assert_equal([0, 92, 2, 0, v:maxcol], getcurpos()) " cleanup set startofline @@ -3815,11 +3813,11 @@ func Test_normal_vert_scroll_longline() call assert_equal(11, line('.')) call assert_equal(1, winline()) exe "normal \" - call assert_equal(10, line('.')) - call assert_equal(3, winline()) + call assert_equal(11, line('.')) + call assert_equal(9, winline()) exe "normal \\" call assert_equal(5, line('.')) - call assert_equal(5, winline()) + call assert_equal(1, winline()) bwipe! endfunc @@ -4172,20 +4170,30 @@ func Test_normal34_zet_large() norm! z9765405999999999999 endfunc -" Test for { and } paragraph movements in a single line -func Test_brace_single_line() - let text =<< trim [DATA] - foobar one two three - [DATA] +" Test for { and } paragraph movements and Ctrl-B in buffer with a single line +func Test_single_line_scroll() + CheckFeature textprop new - call setline(1, text) + call setline(1, ['foobar one two three']) + let vt = 'virt_above' + call prop_type_add(vt, {'highlight': 'IncSearch'}) + call prop_add(1, 0, {'type': vt, 'text': '---', 'text_align': 'above'}) 1 norm! 0} call assert_equal([0, 1, 20, 0], getpos('.')) norm! { call assert_equal([0, 1, 1, 0], getpos('.')) + + " Ctrl-B scrolls up with hidden "above" virtual text. + set smoothscroll + exe "normal \" + call assert_notequal(0, winsaveview().skipcol) + exe "normal \" + call assert_equal(0, winsaveview().skipcol) + + set smoothscroll& bw! endfunc diff --git a/src/testdir/test_scroll_opt.vim b/src/testdir/test_scroll_opt.vim --- a/src/testdir/test_scroll_opt.vim +++ b/src/testdir/test_scroll_opt.vim @@ -829,7 +829,7 @@ func Test_smoothscroll_eob() call VerifyScreenDump(buf, 'Test_smooth_eob_1', {}) " cursor is not placed below window - call term_sendkeys(buf, ":call setline(92, 'a'->repeat(100))\\G") + call term_sendkeys(buf, ":call setline(92, 'a'->repeat(100))\\\G") call VerifyScreenDump(buf, 'Test_smooth_eob_2', {}) call StopVimInTerminal(buf) @@ -998,4 +998,26 @@ func Test_smoothscroll_textoff_small_win set smoothscroll& number& endfunc +func Test_smoothscroll_page() + set smoothscroll + + 10split | 40vsplit + call setline(1, 'abcde '->repeat(150)) + + exe "norm! \" + call assert_equal(320, winsaveview().skipcol) + exe "norm! \" + call assert_equal(640, winsaveview().skipcol) + exe "norm! \" + call assert_equal(880, winsaveview().skipcol) + exe "norm! \" + call assert_equal(560, winsaveview().skipcol) + exe "norm! \" + call assert_equal(240, winsaveview().skipcol) + exe "norm! \" + call assert_equal(0, winsaveview().skipcol) + + set smoothscroll& +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -705,6 +705,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 211, +/**/ 210, /**/ 209,