aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/nvim/diff.c29
-rw-r--r--test/functional/ui/diff_spec.lua82
-rw-r--r--test/old/testdir/test_diffmode.vim24
3 files changed, 132 insertions, 3 deletions
diff --git a/src/nvim/diff.c b/src/nvim/diff.c
index 4a4d2baf94..c9ca58c816 100644
--- a/src/nvim/diff.c
+++ b/src/nvim/diff.c
@@ -1622,7 +1622,23 @@ static void process_hunk(diff_T **dpp, diff_T **dprevp, int idx_orig, int idx_ne
dp->df_count[idx_new] = (linenr_T)hunk->count_new - off;
} else {
// second overlap of new block with existing block
- dp->df_count[idx_new] += (linenr_T)hunk->count_new;
+
+ // if this hunk has different orig/new counts, adjust
+ // the diff block size first. When we handled the first hunk we
+ // would have expanded it to fit, without knowing that this
+ // hunk exists
+ int orig_size_in_dp = MIN(hunk->count_orig,
+ dp->df_lnum[idx_orig] +
+ dp->df_count[idx_orig] - hunk->lnum_orig);
+ int size_diff = hunk->count_new - orig_size_in_dp;
+ dp->df_count[idx_new] += size_diff;
+
+ // grow existing block to include the overlap completely
+ off = hunk->lnum_new + hunk->count_new
+ - (dp->df_lnum[idx_new] + dp->df_count[idx_new]);
+ if (off > 0) {
+ dp->df_count[idx_new] += off;
+ }
if ((dp->df_lnum[idx_new] + dp->df_count[idx_new] - 1)
> curtab->tp_diffbuf[idx_new]->b_ml.ml_line_count) {
dp->df_count[idx_new] = curtab->tp_diffbuf[idx_new]->b_ml.ml_line_count
@@ -1636,8 +1652,15 @@ static void process_hunk(diff_T **dpp, diff_T **dprevp, int idx_orig, int idx_ne
- (dpl->df_lnum[idx_orig] + dpl->df_count[idx_orig]);
if (off < 0) {
- // new change ends in existing block, adjust the end
- dp->df_count[idx_new] += -off;
+ // new change ends in existing block, adjust the end. We only
+ // need to do this once per block or we will over-adjust.
+ if (*notsetp || dp != dpl) {
+ // adjusting by 'off' here is only correct if
+ // there is not another hunk in this block. we
+ // adjust for this when we encounter a second
+ // overlap later.
+ dp->df_count[idx_new] += -off;
+ }
if ((dp->df_lnum[idx_new] + dp->df_count[idx_new] - 1)
> curtab->tp_diffbuf[idx_new]->b_ml.ml_line_count) {
dp->df_count[idx_new] = curtab->tp_diffbuf[idx_new]->b_ml.ml_line_count
diff --git a/test/functional/ui/diff_spec.lua b/test/functional/ui/diff_spec.lua
index dae373297a..e0d88771d3 100644
--- a/test/functional/ui/diff_spec.lua
+++ b/test/functional/ui/diff_spec.lua
@@ -2064,6 +2064,88 @@ it('diff mode overlapped diff blocks will be merged', function()
{2:Xdifile1 Xdifile2 }{3:Xdifile3 }|
|
]])
+
+ -- File 3 overlaps twice, 2nd overlap completely within the existing block.
+ WriteDiffFiles3('foo\na\nb\nc\nbar', 'foo\nw\nx\ny\nz\nbar', 'foo\n1\na\nb\n2\nbar')
+ screen:expect([[
+ {7: }foo │{7: }foo │{7: }^foo |
+ {7: }{27:a}{4: }│{7: }{27:w}{4: }│{7: }{27:1}{4: }|
+ {7: }{27:b}{4: }│{7: }{27:x}{4: }│{7: }{27:a}{4: }|
+ {7: }{27:c}{4: }│{7: }{27:y}{4: }│{7: }{27:b}{4: }|
+ {7: }{23:---------}│{7: }{27:z}{4: }│{7: }{27:2}{4: }|
+ {7: }bar │{7: }bar │{7: }bar |
+ {1:~ }│{1:~ }│{1:~ }|*12
+ {2:Xdifile1 Xdifile2 }{3:Xdifile3 }|
+ |
+ ]])
+
+ -- File 3 overlaps twice, 2nd overlap extends beyond existing block on new
+ -- side. Make sure we don't over-extend the range and hit 'bar'.
+ WriteDiffFiles3('foo\na\nb\nc\nd\nbar', 'foo\nw\nx\ny\nz\nu\nbar', 'foo\n1\na\nb\n2\nd\nbar')
+ screen:expect([[
+ {7: }foo │{7: }foo │{7: }^foo |
+ {7: }{27:a}{4: }│{7: }{27:w}{4: }│{7: }{27:1}{4: }|
+ {7: }{27:b}{4: }│{7: }{27:x}{4: }│{7: }{27:a}{4: }|
+ {7: }{27:c}{4: }│{7: }{27:y}{4: }│{7: }{27:b}{4: }|
+ {7: }{27:d}{4: }│{7: }{27:z}{4: }│{7: }{27:2}{4: }|
+ {7: }{23:---------}│{7: }{27:u}{4: }│{7: }{27:d}{4: }|
+ {7: }bar │{7: }bar │{7: }bar |
+ {1:~ }│{1:~ }│{1:~ }|*11
+ {2:Xdifile1 Xdifile2 }{3:Xdifile3 }|
+ |
+ ]])
+
+ -- Chained overlaps. File 3's 2nd overlap spans two diff blocks and is longer
+ -- than the 2nd one.
+ WriteDiffFiles3(
+ 'foo\na\nb\nc\nd\ne\nf\nbar',
+ 'foo\nw\nx\ny\nz\ne\nu\nbar',
+ 'foo\n1\nb\n2\n3\nd\n4\nf\nbar'
+ )
+ screen:expect([[
+ {7: }foo │{7: }foo │{7: }^foo |
+ {7: }{27:a}{4: }│{7: }{27:w}{4: }│{7: }{27:1}{4: }|
+ {7: }{27:b}{4: }│{7: }{27:x}{4: }│{7: }{27:b}{4: }|
+ {7: }{27:c}{4: }│{7: }{27:y}{4: }│{7: }{27:2}{4: }|
+ {7: }{27:d}{4: }│{7: }{27:z}{4: }│{7: }{27:3}{4: }|
+ {7: }{27:e}{4: }│{7: }{27:e}{4: }│{7: }{27:d}{4: }|
+ {7: }{27:f}{4: }│{7: }{27:u}{4: }│{7: }{27:4}{4: }|
+ {7: }{23:---------}│{7: }{23:---------}│{7: }{22:f }|
+ {7: }bar │{7: }bar │{7: }bar |
+ {1:~ }│{1:~ }│{1:~ }|*9
+ {2:Xdifile1 Xdifile2 }{3:Xdifile3 }|
+ |
+ ]])
+
+ -- File 3 has 2 overlaps. An add and a delete. First overlap's expansion hits
+ -- the 2nd one. Make sure we adjust the diff block to have fewer lines.
+ WriteDiffFiles3('foo\na\nb\nbar', 'foo\nx\ny\nbar', 'foo\n1\na\nbar')
+ screen:expect([[
+ {7: }foo │{7: }foo │{7: }^foo |
+ {7: }{27:a}{4: }│{7: }{27:x}{4: }│{7: }{27:1}{4: }|
+ {7: }{27:b}{4: }│{7: }{27:y}{4: }│{7: }{27:a}{4: }|
+ {7: }bar │{7: }bar │{7: }bar |
+ {1:~ }│{1:~ }│{1:~ }|*14
+ {2:Xdifile1 Xdifile2 }{3:Xdifile3 }|
+ |
+ ]])
+
+ -- File 3 has 2 overlaps. An add and another add. First overlap's expansion hits
+ -- the 2nd one. Make sure we adjust the diff block to have more lines.
+ WriteDiffFiles3('foo\na\nb\nc\nd\nbar', 'foo\nw\nx\ny\nz\nu\nbar', 'foo\n1\na\nb\n3\n4\nd\nbar')
+ screen:expect([[
+ {7: }foo │{7: }foo │{7: }^foo |
+ {7: }{27:a}{4: }│{7: }{27:w}{4: }│{7: }{27:1}{4: }|
+ {7: }{27:b}{4: }│{7: }{27:x}{4: }│{7: }{27:a}{4: }|
+ {7: }{27:c}{4: }│{7: }{27:y}{4: }│{7: }{27:b}{4: }|
+ {7: }{27:d}{4: }│{7: }{27:z}{4: }│{7: }{27:3}{4: }|
+ {7: }{23:---------}│{7: }{27:u}{4: }│{7: }{27:4}{4: }|
+ {7: }{23:---------}│{7: }{23:---------}│{7: }{22:d }|
+ {7: }bar │{7: }bar │{7: }bar |
+ {1:~ }│{1:~ }│{1:~ }|*10
+ {2:Xdifile1 Xdifile2 }{3:Xdifile3 }|
+ |
+ ]])
end)
-- oldtest: Test_diff_topline_noscroll()
diff --git a/test/old/testdir/test_diffmode.vim b/test/old/testdir/test_diffmode.vim
index ab64658bd0..0d20085360 100644
--- a/test/old/testdir/test_diffmode.vim
+++ b/test/old/testdir/test_diffmode.vim
@@ -2045,6 +2045,30 @@ func Test_diff_overlapped_diff_blocks_will_be_merged()
call WriteDiffFiles3(buf, ["a", "b", "c"], ["d", "e"], ["b"])
call VerifyBoth(buf, "Test_diff_overlapped_3.39", "")
+ " File 3 overlaps twice, 2nd overlap completely within the existing block.
+ call WriteDiffFiles3(buf, ["foo", "a", "b", "c", "bar"], ["foo", "w", "x", "y", "z", "bar"], ["foo", "1", "a", "b", "2", "bar"])
+ call VerifyBoth(buf, "Test_diff_overlapped_3.40", "")
+
+ " File 3 overlaps twice, 2nd overlap extends beyond existing block on new
+ " side. Make sure we don't over-extend the range and hit 'bar'.
+ call WriteDiffFiles3(buf, ["foo", "a", "b", "c", "d", "bar"], ["foo", "w", "x", "y", "z", "u", "bar"], ["foo", "1", "a", "b", "2", "d", "bar"])
+ call VerifyBoth(buf, "Test_diff_overlapped_3.41", "")
+
+ " Chained overlaps. File 3's 2nd overlap spans two diff blocks and is longer
+ " than the 2nd one.
+ call WriteDiffFiles3(buf, ["foo", "a", "b", "c", "d", "e", "f", "bar"], ["foo", "w", "x", "y", "z", "e", "u", "bar"], ["foo", "1", "b", "2", "3", "d", "4", "f", "bar"])
+ call VerifyBoth(buf, "Test_diff_overlapped_3.42", "")
+
+ " File 3 has 2 overlaps. An add and a delete. First overlap's expansion hits
+ " the 2nd one. Make sure we adjust the diff block to have fewer lines.
+ call WriteDiffFiles3(buf, ["foo", "a", "b", "bar"], ["foo", "x", "y", "bar"], ["foo", "1", "a", "bar"])
+ call VerifyBoth(buf, "Test_diff_overlapped_3.43", "")
+
+ " File 3 has 2 overlaps. An add and another add. First overlap's expansion hits
+ " the 2nd one. Make sure we adjust the diff block to have more lines.
+ call WriteDiffFiles3(buf, ["foo", "a", "b", "c", "d", "bar"], ["foo", "w", "x", "y", "z", "u", "bar"], ["foo", "1", "a", "b", "3", "4", "d", "bar"])
+ call VerifyBoth(buf, "Test_diff_overlapped_3.44", "")
+
call StopVimInTerminal(buf)
endfunc