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

3fa0d3514b

Co-authored-by: Gary Johnson <garyjohn@spocom.com>
This commit is contained in:
zeertzjq
2025-06-09 06:57:54 +08:00
committed by GitHub
parent 2b79d9ba1a
commit d2dad30898
4 changed files with 59 additions and 98 deletions

View File

@ -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;

View File

@ -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()

View File

@ -303,19 +303,8 @@ something
feed('2<c-w>w')
feed(':4,17diffget<cr>')
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('1<c-w>w')
feed(':4,12diffget<cr>')
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('2<c-w>w')
feed(':1,12diffget<cr>')
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('1<c-w>w')
feed(':1,19diffget<cr>')
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 |
]])

View File

@ -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()