From d2dad3089841c761ef57cb200629c50af8269c23 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 9 Jun 2025 06:57:54 +0800 Subject: [PATCH] vim-patch:9.1.1439: Last diff folds not merged (#34380) Problem: Last diff folds not merged (after v8.1.1922) Solution: loop over all windows in the current tabpage and update all folds (Gary Johnson) This commit fixes a bug where the last two folds of a diff are not merged when the last difference between the two diff'd buffers is resolved. Normally, when two buffers are diff'd, folding is used to show only the text that differs and to hide the text that is the same between the two buffers. When a difference is resolved by making a block of text the same in both buffers, the folds are updated to merge that block with the folds above and below it into one closed fold. That updating of the folds did not occur when the block of text was the last diff block in the buffers. The bug was introduced by this patch on August 24, 2019: patch 8.1.1922: in diff mode global operations can be very slow Problem: In diff mode global operations can be very slow. Solution: Do not call diff_redraw() many times, call it once when redrawing. And also don't update folds multiple times. Unfortunately, folds were then not updated often enough. The problem was fixed by adding a short loop to the ex_diffgetput() function in diff.c to update all the folds in the current tab when the last difference is removed. A test for this was added to test_diffmode.vim. Two of the reference screen dumps for another test in that file, Test_diffget_diffput_linematch(), had to be changed to have all the folds closed rather than to have the last diff block remain open. closes: vim/vim#17457 https://github.com/vim/vim/commit/3fa0d3514b928e0b21ef24594785f9104a3ffafd Co-authored-by: Gary Johnson --- src/nvim/diff.c | 9 ++++ test/functional/treesitter/fold_spec.lua | 45 +++------------- test/functional/ui/linematch_spec.lua | 69 +++--------------------- test/old/testdir/test_diffmode.vim | 34 ++++++++++++ 4 files changed, 59 insertions(+), 98 deletions(-) diff --git a/src/nvim/diff.c b/src/nvim/diff.c index 2728efd8ca..71d2dc687a 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -3552,6 +3552,15 @@ theend: check_cursor(curwin); changed_line_abv_curs(); + // If all diffs are gone, update folds in all diff windows. + if (curtab->tp_first_diff == NULL) { + FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { + if (wp->w_p_diff && wp->w_p_fdm[0] == 'd' && wp->w_p_fen) { + foldUpdateAll(wp); + } + } + } + if (diff_need_update) { // redraw already done by ex_diffupdate() diff_need_update = false; diff --git a/test/functional/treesitter/fold_spec.lua b/test/functional/treesitter/fold_spec.lua index cb3e8d10e8..f462d27675 100644 --- a/test/functional/treesitter/fold_spec.lua +++ b/test/functional/treesitter/fold_spec.lua @@ -617,44 +617,15 @@ t3]]) command('windo diffthis') feed('do') - screen:expect { - grid = [[ - {1:+ }{2:+-- 9 lines: void ui_refresh(void)·······················}| - {1: } for (size_t i = 0; i < ui_count; i++) { | - {1: } UI *ui = uis[i]; | - {1: } width = MIN(ui->width, width); | - {1: } height = MIN(ui->height, height); | - {1: } foo = BAR(ui->bazaar, bazaar); | - {1: } for (UIExtension j = 0; (int)j < kUIExtCount; j++) { | - {1: } ext_widgets[j] &= (ui->ui_ext[j] || inclusive); | - {1: } } | - {1: } } | - {1: }} | - {3:~ }|*6 - {4:[No Name] [+] }| - {1:+ }{2:+-- 9 lines: void ui_refresh(void)·······················}| - {1: } for (size_t i = 0; i < ui_count; i++) { | - {1: } UI *ui = uis[i]; | - {1: } width = MIN(ui->width, width); | - {1: } height = MIN(ui->height, height); | - {1: } foo = BAR(ui->bazaar, bazaar); | - {1: } for (UIExtension j = 0; (int)j < kUIExtCount; j++) { | - {1: } ext_widgets[j] &= (ui->ui_ext[j] || inclusive); | - {1: } ^} | - {1: } } | - {1: }} | - {3:~ }|*5 - {5:[No Name] [+] }| + screen:expect([[ + {7:+ }{13:+-- 19 lines: void ui_refresh(void)·······················}| + {1:~ }|*16 + {2:[No Name] [+] }| + {7:+ }{13:^+-- 19 lines: void ui_refresh(void)·······················}| + {1:~ }|*15 + {3:[No Name] [+] }| | - ]], - attr_ids = { - [1] = { background = Screen.colors.Grey, foreground = Screen.colors.Blue4 }, - [2] = { background = Screen.colors.LightGrey, foreground = Screen.colors.Blue4 }, - [3] = { foreground = Screen.colors.Blue, bold = true }, - [4] = { reverse = true }, - [5] = { reverse = true, bold = true }, - }, - } + ]]) end) it('does not extend closed fold with `o`/`O`', function() diff --git a/test/functional/ui/linematch_spec.lua b/test/functional/ui/linematch_spec.lua index 1b93bf23cc..b52a072523 100644 --- a/test/functional/ui/linematch_spec.lua +++ b/test/functional/ui/linematch_spec.lua @@ -303,19 +303,8 @@ something feed('2w') feed(':4,17diffget') screen:expect([[ - {7: }{8: 1 } │{7: }{8: 1 }^ | - {7: }{8: 2 }common line │{7: }{8: 2 }common line | - {7: }{8: 3 }common line │{7: }{8: 3 }common line | - {7: }{8: 4 } │{7: }{8: 4 } | - {7: }{8: 5 }ABCabc │{7: }{8: 5 }ABCabc | - {7: }{8: 6 }ABCabc │{7: }{8: 6 }ABCabc | - {7: }{8: 7 }ABCabc │{7: }{8: 7 }ABCabc | - {7: }{8: 8 }ABCabc │{7: }{8: 8 }ABCabc | - {7: }{8: 9 }common line │{7: }{8: 9 }common line | - {7: }{8: 10 }common line │{7: }{8: 10 }common line | - {7: }{8: 11 }common line │{7: }{8: 11 }common line | - {7: }{8: 12 }something │{7: }{8: 12 }something | - {1:~ }│{1:~ }|*6 + {7:+ }{8: 1 }{13:+-- 12 lines: common line··················}│{7:+ }{8: 1 }{13:^+-- 12 lines: common line···················}| + {1:~ }│{1:~ }|*17 {2:Xtest-functional-diff-screen-1.2 }{3:Xtest-functional-diff-screen-1 [+] }| :4,17diffget | ]]) @@ -324,24 +313,8 @@ something feed('1w') feed(':4,12diffget') screen:expect([[ - {7: }{8: 1 }^ │{7: }{8: 1 } | - {7: }{8: 2 }common line │{7: }{8: 2 }common line | - {7: }{8: 3 }common line │{7: }{8: 3 }common line | - {7: }{8: 4 } │{7: }{8: 4 } | - {7: }{8: 5 }DEFabc │{7: }{8: 5 }DEFabc | - {7: }{8: 6 }xyz │{7: }{8: 6 }xyz | - {7: }{8: 7 }xyz │{7: }{8: 7 }xyz | - {7: }{8: 8 }xyz │{7: }{8: 8 }xyz | - {7: }{8: 9 }DEFabc │{7: }{8: 9 }DEFabc | - {7: }{8: 10 }DEFabc │{7: }{8: 10 }DEFabc | - {7: }{8: 11 }DEFabc │{7: }{8: 11 }DEFabc | - {7: }{8: 12 }common line │{7: }{8: 12 }common line | - {7: }{8: 13 }common line │{7: }{8: 13 }common line | - {7: }{8: 14 }DEF │{7: }{8: 14 }DEF | - {7: }{8: 15 }common line │{7: }{8: 15 }common line | - {7: }{8: 16 }DEF │{7: }{8: 16 }DEF | - {7: }{8: 17 }something │{7: }{8: 17 }something | - {1:~ }│{1:~ }| + {7:+ }{8: 1 }{13:^+-- 17 lines: common line··················}│{7:+ }{8: 1 }{13:+-- 17 lines: common line···················}| + {1:~ }│{1:~ }|*17 {3:Xtest-functional-diff-screen-1.2 [+] }{2:Xtest-functional-diff-screen-1 }| :4,12diffget | ]]) @@ -968,24 +941,8 @@ something feed('2w') feed(':1,12diffget') screen:expect([[ - {7: }{8: 1 }common line │{7: }{8: 1 }^common line | - {7: }{8: 2 }DEF │{7: }{8: 2 }DEF | - {7: }{8: 3 }GHI │{7: }{8: 3 }GHI | - {7: }{8: 4 }something │{7: }{8: 4 }something | - {7: }{8: 5 } │{7: }{8: 5 } | - {7: }{8: 6 }aDEFabc │{7: }{8: 6 }aDEFabc | - {7: }{8: 7 }xyz │{7: }{8: 7 }xyz | - {7: }{8: 8 }xyz │{7: }{8: 8 }xyz | - {7: }{8: 9 }xyz │{7: }{8: 9 }xyz | - {7: }{8: 10 }aDEFabc │{7: }{8: 10 }aDEFabc | - {7: }{8: 11 }aDEFabc │{7: }{8: 11 }aDEFabc | - {7: }{8: 12 }aDEFabc │{7: }{8: 12 }aDEFabc | - {7: }{8: 13 }common line │{7: }{8: 13 }common line | - {7: }{8: 14 }DEF │{7: }{8: 14 }DEF | - {7: }{8: 15 }GHI │{7: }{8: 15 }GHI | - {7: }{8: 16 }something else │{7: }{8: 16 }something else | - {7: }{8: 17 }common line │{7: }{8: 17 }common line | - {7: }{8: 18 }something │{7: }{8: 18 }something | + {7:+ }{8: 1 }{13:+-- 18 lines: common line··················}│{7:+ }{8: 1 }{13:^+-- 18 lines: common line···················}| + {1:~ }│{1:~ }|*17 {2:Xtest-functional-diff-screen-1.2 }{3:Xtest-functional-diff-screen-1 [+] }| :1,12diffget | ]]) @@ -994,18 +951,8 @@ something feed('1w') feed(':1,19diffget') screen:expect([[ - {7: }{8: 1 }^common line │{7: }{8: 1 }common line | - {7: }{8: 2 }HIL │{7: }{8: 2 }HIL | - {7: }{8: 3 } │{7: }{8: 3 } | - {7: }{8: 4 }aABCabc │{7: }{8: 4 }aABCabc | - {7: }{8: 5 }aABCabc │{7: }{8: 5 }aABCabc | - {7: }{8: 6 }aABCabc │{7: }{8: 6 }aABCabc | - {7: }{8: 7 }aABCabc │{7: }{8: 7 }aABCabc | - {7: }{8: 8 }common line │{7: }{8: 8 }common line | - {7: }{8: 9 }HIL │{7: }{8: 9 }HIL | - {7: }{8: 10 }common line │{7: }{8: 10 }common line | - {7: }{8: 11 }something │{7: }{8: 11 }something | - {1:~ }│{1:~ }|*7 + {7:+ }{8: 1 }{13:^+-- 11 lines: common line··················}│{7:+ }{8: 1 }{13:+-- 11 lines: common line···················}| + {1:~ }│{1:~ }|*17 {3:Xtest-functional-diff-screen-1.2 [+] }{2:Xtest-functional-diff-screen-1 }| :1,19diffget | ]]) diff --git a/test/old/testdir/test_diffmode.vim b/test/old/testdir/test_diffmode.vim index 2d3bcb8234..26501f2ca2 100644 --- a/test/old/testdir/test_diffmode.vim +++ b/test/old/testdir/test_diffmode.vim @@ -50,6 +50,40 @@ func Test_vert_split() set diffopt& endfunc +" Test for diff folding redraw after last diff is resolved +func Test_diff_fold_redraw() + " Set up two files with the minimal case + call writefile(['Paragraph 1', '', 'Paragraph 2', '', 'Paragraph 3'], 'Xfile1') + call writefile(['Paragraph 1', '', 'Paragraph 3'], 'Xfile2') + + " Open in diff mode + exe 'edit Xfile1' + exe 'vert diffsplit Xfile2' + + " Ensure both windows are in diff mode and folds are enabled + set foldmethod=diff + set foldenable + + " Go to the diff and apply dp to copy Paragraph 2 to Xfile2 + normal! ]c + normal! dp + + " Redraw and check folds + redraw! + let win1_fold = foldclosed(1) + let win2_fold = foldclosed(1) + + " Both windows should be fully folded (foldclosed returns 1 for top fold) + call assert_equal(1, win1_fold) + call assert_equal(1, win2_fold) + + " Clean up + bwipe! + bwipe! + call delete('Xfile1') + call delete('Xfile2') +endfunc + func Test_vert_split_internal() set diffopt=internal,filler call Common_vert_split()