diff options
| author | Mehmet Samet Duman <yongdohyun@projecttick.org> | 2026-04-04 12:41:27 +0300 |
|---|---|---|
| committer | Mehmet Samet Duman <yongdohyun@projecttick.org> | 2026-04-04 12:41:27 +0300 |
| commit | 4f2d36194b4f299aa7509d815c07121039ea833b (patch) | |
| tree | f3ded014bad3a4c76ff6a22b8726ebaab68c3d13 /mnv/src/testdir/test_diffmode.mnv | |
| parent | 5b578e70c314723a3cde5c9bfc2be0bf1dadc93b (diff) | |
| download | Project-Tick-4f2d36194b4f299aa7509d815c07121039ea833b.tar.gz Project-Tick-4f2d36194b4f299aa7509d815c07121039ea833b.zip | |
NOISSUE change uvim folder name to mnv
Signed-off-by: Mehmet Samet Duman <yongdohyun@projecttick.org>
Diffstat (limited to 'mnv/src/testdir/test_diffmode.mnv')
| -rw-r--r-- | mnv/src/testdir/test_diffmode.mnv | 3631 |
1 files changed, 3631 insertions, 0 deletions
diff --git a/mnv/src/testdir/test_diffmode.mnv b/mnv/src/testdir/test_diffmode.mnv new file mode 100644 index 0000000000..72366e6928 --- /dev/null +++ b/mnv/src/testdir/test_diffmode.mnv @@ -0,0 +1,3631 @@ +" Tests for diff mode + +source util/screendump.mnv + +func Test_diff_fold_sync() + enew! + let g:update_count = 0 + au DiffUpdated * let g:update_count += 1 + + let l = range(50) + call setline(1, l) + diffthis + let winone = win_getid() + new + let l[25] = 'diff' + call setline(1, l) + diffthis + let wintwo = win_getid() + " line 15 is inside the closed fold + call assert_equal(19, foldclosedend(10)) + call win_gotoid(winone) + call assert_equal(19, foldclosedend(10)) + " open the fold + normal zv + call assert_equal(-1, foldclosedend(10)) + " fold in other window must have opened too + call win_gotoid(wintwo) + call assert_equal(-1, foldclosedend(10)) + + " cursor position is in sync + normal 23G + call win_gotoid(winone) + call assert_equal(23, getcurpos()[1]) + + " depending on how redraw is done DiffUpdated may be triggered once or twice + call assert_inrange(1, 2, g:update_count) + au! DiffUpdated + + windo diffoff + close! + set nomodified +endfunc + +func Test_vert_split() + set diffopt=filler + call Common_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 a minimal case. + call writefile(['Paragraph 1', '', 'Paragraph 2', '', 'Paragraph 3'], 'Xfile1') + call writefile(['Paragraph 1', '', 'Paragraph 3'], 'Xfile2') + + " Open in diff mode. + edit Xfile1 + vert diffsplit Xfile2 + + " Go to the diff and apply :diffput to copy Paragraph 2 to Xfile2. + wincmd l + 3 + diffput + + " Check that the folds in both windows are closed and extend from the first + " line of the buffer to the last line of the buffer. + call assert_equal(1, foldclosed(line("$"))) + wincmd h + call assert_equal(1, foldclosed(line("$"))) + + " Clean up. + bwipe! + bwipe! + call delete('Xfile1') + call delete('Xfile2') +endfunc + +func Test_vert_split_internal() + set diffopt=internal,filler + call Common_vert_split() + set diffopt& +endfunc + +func Common_vert_split() + " Disable the title to avoid xterm keeping the wrong one. + set notitle noicon + new + let l = ['1 aa', '2 bb', '3 cc', '4 dd', '5 ee'] + call setline(1, l) + w! Xtest + normal dd + $ + put + normal kkrXoxxx + w! Xtest2 + file Nop + normal ggoyyyjjjozzzz + set foldmethod=marker foldcolumn=4 + call assert_equal(0, &diff) + call assert_equal('marker', &foldmethod) + call assert_equal(4, &foldcolumn) + call assert_equal(0, &scrollbind) + call assert_equal(0, &cursorbind) + call assert_equal(1, &wrap) + + vert diffsplit Xtest + vert diffsplit Xtest2 + call assert_equal(1, &diff) + call assert_equal('diff', &foldmethod) + call assert_equal(2, &foldcolumn) + call assert_equal(1, &scrollbind) + call assert_equal(1, &cursorbind) + call assert_equal(0, &wrap) + + let diff_fdm = &fdm + let diff_fdc = &fdc + " repeat entering diff mode here to see if this saves the wrong settings + diffthis + " jump to second window for a moment to have filler line appear at start of + " first window + wincmd w + normal gg + wincmd p + normal gg + call assert_equal(2, winline()) + normal j + call assert_equal(4, winline()) + normal j + call assert_equal(5, winline()) + normal j + call assert_equal(6, winline()) + normal j + call assert_equal(8, winline()) + normal j + call assert_equal(9, winline()) + + wincmd w + normal gg + call assert_equal(1, winline()) + normal j + call assert_equal(2, winline()) + normal j + call assert_equal(4, winline()) + normal j + call assert_equal(5, winline()) + normal j + call assert_equal(8, winline()) + + wincmd w + normal gg + call assert_equal(2, winline()) + normal j + call assert_equal(3, winline()) + normal j + call assert_equal(4, winline()) + normal j + call assert_equal(5, winline()) + normal j + call assert_equal(6, winline()) + normal j + call assert_equal(7, winline()) + normal j + call assert_equal(8, winline()) + + " Test diffoff + diffoff! + 1wincmd w + let &diff = 1 + let &fdm = diff_fdm + let &fdc = diff_fdc + 4wincmd w + diffoff! + 1wincmd w + call assert_equal(0, &diff) + call assert_equal('marker', &foldmethod) + call assert_equal(4, &foldcolumn) + call assert_equal(0, &scrollbind) + call assert_equal(0, &cursorbind) + call assert_equal(1, &wrap) + + wincmd w + call assert_equal(0, &diff) + call assert_equal('marker', &foldmethod) + call assert_equal(4, &foldcolumn) + call assert_equal(0, &scrollbind) + call assert_equal(0, &cursorbind) + call assert_equal(1, &wrap) + + wincmd w + call assert_equal(0, &diff) + call assert_equal('marker', &foldmethod) + call assert_equal(4, &foldcolumn) + call assert_equal(0, &scrollbind) + call assert_equal(0, &cursorbind) + call assert_equal(1, &wrap) + + call delete('Xtest') + call delete('Xtest2') + windo bw! +endfunc + +func Test_filler_lines() + " Test that diffing shows correct filler lines + enew! + put =range(4,10) + 1d _ + vnew + put =range(1,10) + 1d _ + windo diffthis + wincmd h + call assert_equal(1, line('w0')) + unlet! diff_fdm diff_fdc + windo diffoff + bwipe! + enew! +endfunc + +func Test_diffget_diffput() + enew! + let l = range(50) + call setline(1, l) + call assert_fails('diffget', 'E99:') + diffthis + call assert_fails('diffget', 'E100:') + new + let l[10] = 'one' + let l[20] = 'two' + let l[30] = 'three' + let l[40] = 'four' + call setline(1, l) + diffthis + call assert_equal('one', getline(11)) + 11diffget + call assert_equal('10', getline(11)) + 21diffput + wincmd w + call assert_equal('two', getline(21)) + normal 31Gdo + call assert_equal('three', getline(31)) + call assert_equal('40', getline(41)) + normal 41Gdp + wincmd w + call assert_equal('40', getline(41)) + new + diffthis + call assert_fails('diffget', 'E101:') + + windo diffoff + %bwipe! +endfunc + +" Test putting two changes from one buffer to another +func Test_diffput_two() + new a + let win_a = win_getid() + call setline(1, range(1, 10)) + diffthis + new b + let win_b = win_getid() + call setline(1, range(1, 10)) + 8del + 5del + diffthis + call win_gotoid(win_a) + %diffput + call win_gotoid(win_b) + call assert_equal(map(range(1, 10), 'string(v:val)'), getline(1, '$')) + bwipe! a + bwipe! b +endfunc + +" Test for :diffget/:diffput with a range that is inside a diff chunk +func Test_diffget_diffput_range() + call setline(1, range(1, 10)) + new + call setline(1, range(11, 20)) + windo diffthis + 3,5diffget + call assert_equal(['13', '14', '15'], getline(3, 5)) + call setline(1, range(1, 10)) + 4,8diffput + wincmd p + call assert_equal(['13', '4', '5', '6', '7', '8', '19'], getline(3, 9)) + %bw! +endfunc + +" Test :diffget/:diffput handling of added/deleted lines +func Test_diffget_diffput_deleted_lines() + call setline(1, ['2','4','6']) + diffthis + new + call setline(1, range(1,7)) + diffthis + wincmd w + + 3,3diffget " get nothing + call assert_equal(['2', '4', '6'], getline(1, '$')) + 3,4diffget " get the last insertion past the end of file + call assert_equal(['2', '4', '6', '7'], getline(1, '$')) + 0,1diffget " get the first insertion above first line + call assert_equal(['1', '2', '4', '6', '7'], getline(1, '$')) + + " When using non-range diffget on the last line, it should get the + " change above or at the line as usual, but if the only change is below the + " last line, diffget should get that instead. + 1,$delete + call setline(1, ['2','4','6']) + diffupdate + norm Gdo + call assert_equal(['2', '4', '5', '6'], getline(1, '$')) + norm Gdo + call assert_equal(['2', '4', '5', '6', '7'], getline(1, '$')) + + " Test non-range diffput on last line with the same logic + 1,$delete + call setline(1, ['2','4','6']) + diffupdate + norm Gdp + wincmd w + call assert_equal(['1', '2', '3', '4', '6', '7'], getline(1, '$')) + wincmd w + norm Gdp + wincmd w + call assert_equal(['1', '2', '3', '4', '6'], getline(1, '$')) + call setline(1, range(1,7)) + diffupdate + wincmd w + + " Test that 0,$+1 will get/put all changes from/to the other buffer + 1,$delete + call setline(1, ['2','4','6']) + diffupdate + 0,$+1diffget + call assert_equal(['1', '2', '3', '4', '5', '6', '7'], getline(1, '$')) + 1,$delete + call setline(1, ['2','4','6']) + diffupdate + 0,$+1diffput + wincmd w + call assert_equal(['2', '4', '6'], getline(1, '$')) + %bw! +endfunc + +" Test for :diffget/:diffput with an empty buffer and a non-empty buffer +func Test_diffget_diffput_empty_buffer() + %d _ + new + call setline(1, 'one') + windo diffthis + diffget + call assert_equal(['one'], getline(1, '$')) + %d _ + diffput + wincmd p + call assert_equal([''], getline(1, '$')) + %bw! +endfunc + +" :diffput and :diffget completes names of buffers which +" are in diff mode and which are different than current buffer. +" No completion when the current window is not in diff mode. +func Test_diffget_diffput_completion() + e Xdiff1 | diffthis + botright new Xdiff2 + botright new Xdiff3 | split | diffthis + botright new Xdiff4 | diffthis + + wincmd t + call assert_equal('Xdiff1', bufname('%')) + call feedkeys(":diffput \<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal('"diffput Xdiff3 Xdiff4', @:) + call feedkeys(":diffget \<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal('"diffget Xdiff3 Xdiff4', @:) + call assert_equal(['Xdiff3', 'Xdiff4'], getcompletion('', 'diff_buffer')) + + " Xdiff2 is not in diff mode, so no completion for :diffput, :diffget + wincmd j + call assert_equal('Xdiff2', bufname('%')) + call feedkeys(":diffput \<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal('"diffput ', @:) + call feedkeys(":diffget \<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal('"diffget ', @:) + call assert_equal([], getcompletion('', 'diff_buffer')) + + " Xdiff3 is split in 2 windows, only the top one is in diff mode. + " So completion of :diffput :diffget only happens in the top window. + wincmd j + call assert_equal('Xdiff3', bufname('%')) + call assert_equal(1, &diff) + call feedkeys(":diffput \<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal('"diffput Xdiff1 Xdiff4', @:) + call feedkeys(":diffget \<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal('"diffget Xdiff1 Xdiff4', @:) + call assert_equal(['Xdiff1', 'Xdiff4'], getcompletion('', 'diff_buffer')) + + wincmd j + call assert_equal('Xdiff3', bufname('%')) + call assert_equal(0, &diff) + call feedkeys(":diffput \<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal('"diffput ', @:) + call feedkeys(":diffget \<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal('"diffget ', @:) + call assert_equal([], getcompletion('', 'diff_buffer')) + + wincmd j + call assert_equal('Xdiff4', bufname('%')) + call feedkeys(":diffput \<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal('"diffput Xdiff1 Xdiff3', @:) + call feedkeys(":diffget \<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal('"diffget Xdiff1 Xdiff3', @:) + call assert_equal(['Xdiff1', 'Xdiff3'], getcompletion('', 'diff_buffer')) + + %bwipe +endfunc + +func Test_dp_do_buffer() + e! one + let bn1=bufnr('%') + let l = range(60) + call setline(1, l) + diffthis + + new two + let l[10] = 'one' + let l[20] = 'two' + let l[30] = 'three' + let l[40] = 'four' + let l[50] = 'five' + call setline(1, l) + diffthis + + " dp and do with invalid buffer number. + 11 + call assert_fails('norm 99999dp', 'E102:') + call assert_fails('norm 99999do', 'E102:') + call assert_fails('diffput non_existing_buffer', 'E94:') + call assert_fails('diffget non_existing_buffer', 'E94:') + + " dp and do with valid buffer number. + call assert_equal('one', getline('.')) + exe 'norm ' . bn1 . 'do' + call assert_equal('10', getline('.')) + 21 + call assert_equal('two', getline('.')) + diffget one + call assert_equal('20', getline('.')) + + 31 + exe 'norm ' . bn1 . 'dp' + 41 + diffput one + wincmd w + 31 + call assert_equal('three', getline('.')) + 41 + call assert_equal('four', getline('.')) + + " dp and do with buffer number which is not in diff mode. + new not_in_diff_mode + let bn3=bufnr('%') + wincmd w + 51 + call assert_fails('exe "norm" . bn3 . "dp"', 'E103:') + call assert_fails('exe "norm" . bn3 . "do"', 'E103:') + call assert_fails('diffput not_in_diff_mode', 'E94:') + call assert_fails('diffget not_in_diff_mode', 'E94:') + + windo diffoff + %bwipe! +endfunc + +func Test_do_lastline() + e! one + call setline(1, ['1','2','3','4','5','6']) + diffthis + + new two + call setline(1, ['2','4','5']) + diffthis + + 1 + norm dp]c + norm dp]c + wincmd w + call assert_equal(4, line('$')) + norm G + norm do + call assert_equal(3, line('$')) + + windo diffoff + %bwipe! +endfunc + +func Test_diffoff() + enew! + call setline(1, ['Two', 'Three']) + redraw + let normattr = screenattr(1, 1) + diffthis + botright vert new + call setline(1, ['One', '', 'Two', 'Three']) + diffthis + redraw + call assert_notequal(normattr, 1->screenattr(1)) + diffoff! + redraw + call assert_equal(normattr, screenattr(1, 1)) + bwipe! + bwipe! +endfunc + +func Common_icase_test() + edit one + call setline(1, ['One', 'Two', 'Three', 'Four', 'Fi#vϵ', 'Si⃗x', 'Se⃗ve⃗n']) + redraw + let normattr = screenattr(1, 1) + diffthis + + botright vert new two + call setline(1, ['one', 'TWO', 'Three ', 'Four', 'fI=VΕ', 'SI⃗x', 'SEvE⃗n']) + diffthis + + redraw + call assert_equal(normattr, screenattr(1, 1)) + call assert_equal(normattr, screenattr(2, 1)) + call assert_notequal(normattr, screenattr(3, 1)) + call assert_equal(normattr, screenattr(4, 1)) + call assert_equal(normattr, screenattr(6, 2)) + call assert_notequal(normattr, screenattr(7, 2)) + + let dtextattr = screenattr(5, 3) + call assert_notequal(dtextattr, screenattr(5, 1)) + call assert_notequal(dtextattr, screenattr(5, 5)) + call assert_notequal(dtextattr, screenattr(7, 4)) + + diffoff! + %bwipe! +endfunc + +func Test_diffopt_icase() + set diffopt=icase,foldcolumn:0 + call Common_icase_test() + set diffopt& +endfunc + +func Test_diffopt_icase_internal() + set diffopt=icase,foldcolumn:0,internal + call Common_icase_test() + set diffopt& +endfunc + +func Common_iwhite_test() + edit one + " Difference in trailing spaces and amount of spaces should be ignored, + " but not other space differences. + call setline(1, ["One \t", 'Two', 'Three', 'one two', 'one two', 'Four']) + redraw + let normattr = screenattr(1, 1) + diffthis + + botright vert new two + call setline(1, ["One\t ", "Two\t ", 'Three', 'one two', 'onetwo', ' Four']) + diffthis + + redraw + call assert_equal(normattr, screenattr(1, 1)) + call assert_equal(normattr, screenattr(2, 1)) + call assert_equal(normattr, screenattr(3, 1)) + call assert_equal(normattr, screenattr(4, 1)) + call assert_notequal(normattr, screenattr(5, 1)) + call assert_notequal(normattr, screenattr(6, 1)) + + diffoff! + %bwipe! +endfunc + +func Test_diffopt_iwhite() + set diffopt=iwhite,foldcolumn:0 + call Common_iwhite_test() + set diffopt& +endfunc + +func Test_diffopt_iwhite_internal() + set diffopt=internal,iwhite,foldcolumn:0 + call Common_iwhite_test() + set diffopt& +endfunc + +func Test_diffopt_context() + enew! + call setline(1, ['1', '2', '3', '4', '5', '6', '7']) + diffthis + new + call setline(1, ['1', '2', '3', '4', '5x', '6', '7']) + diffthis + + set diffopt=context:2 + call assert_equal('+-- 2 lines: 1', foldtextresult(1)) + set diffopt=internal,context:2 + call assert_equal('+-- 2 lines: 1', foldtextresult(1)) + + set diffopt=context:1 + call assert_equal('+-- 3 lines: 1', foldtextresult(1)) + set diffopt=internal,context:1 + call assert_equal('+-- 3 lines: 1', foldtextresult(1)) + + diffoff! + %bwipe! + set diffopt& +endfunc + +func Test_diffopt_horizontal() + set diffopt=internal,horizontal + diffsplit + + call assert_equal(&columns, winwidth(1)) + call assert_equal(&columns, winwidth(2)) + call assert_equal(&lines, winheight(1) + winheight(2) + 3) + call assert_inrange(0, 1, winheight(1) - winheight(2)) + + set diffopt& + diffoff! + %bwipe +endfunc + +func Test_diffopt_vertical() + set diffopt=internal,vertical + diffsplit + + call assert_equal(&lines - 2, winheight(1)) + call assert_equal(&lines - 2, winheight(2)) + call assert_equal(&columns, winwidth(1) + winwidth(2) + 1) + call assert_inrange(0, 1, winwidth(1) - winwidth(2)) + + set diffopt& + diffoff! + %bwipe +endfunc + +func Test_diffopt_hiddenoff() + set diffopt=internal,filler,foldcolumn:0,hiddenoff + e! one + call setline(1, ['Two', 'Three']) + redraw + let normattr = screenattr(1, 1) + diffthis + botright vert new two + call setline(1, ['One', 'Four']) + diffthis + redraw + call assert_notequal(normattr, screenattr(1, 1)) + set hidden + close + redraw + " should not diffing with hidden buffer two while 'hiddenoff' is enabled + call assert_equal(normattr, screenattr(1, 1)) + + bwipe! + bwipe! + set hidden& diffopt& +endfunc + +func Test_diffoff_hidden() + set diffopt=internal,filler,foldcolumn:0 + e! one + call setline(1, ['Two', 'Three']) + redraw + let normattr = screenattr(1, 1) + diffthis + botright vert new two + call setline(1, ['One', 'Four']) + diffthis + redraw + call assert_notequal(normattr, screenattr(1, 1)) + set hidden + close + redraw + " diffing with hidden buffer two + call assert_notequal(normattr, screenattr(1, 1)) + diffoff + redraw + call assert_equal(normattr, screenattr(1, 1)) + diffthis + redraw + " still diffing with hidden buffer two + call assert_notequal(normattr, screenattr(1, 1)) + diffoff! + redraw + call assert_equal(normattr, screenattr(1, 1)) + diffthis + redraw + " no longer diffing with hidden buffer two + call assert_equal(normattr, screenattr(1, 1)) + + bwipe! + bwipe! + set hidden& diffopt& +endfunc + +func Test_setting_cursor() + new Xtest1 + put =range(1,90) + wq + new Xtest2 + put =range(1,100) + wq + + tabe Xtest2 + $ + diffsp Xtest1 + tabclose + + call delete('Xtest1') + call delete('Xtest2') +endfunc + +func Test_diff_move_to() + new + call setline(1, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) + diffthis + vnew + call setline(1, [1, '2x', 3, 4, 4, 5, '6x', 7, '8x', 9, '10x']) + diffthis + norm ]c + call assert_equal(2, line('.')) + norm 3]c + call assert_equal(9, line('.')) + norm 10]c + call assert_equal(11, line('.')) + norm [c + call assert_equal(9, line('.')) + norm 2[c + call assert_equal(5, line('.')) + norm 10[c + call assert_equal(2, line('.')) + %bwipe! +endfunc + +func Test_diffexpr() + CheckExecutable diff + + func DiffExpr() + " Prepend some text to check diff type detection + call writefile(['warning', ' message'], v:fname_out) + silent exe '!diff ' . v:fname_in . ' ' . v:fname_new . '>>' . v:fname_out + endfunc + set diffexpr=DiffExpr() + set diffopt=foldcolumn:0 + + enew! + call setline(1, ['one', 'two', 'three']) + redraw + let normattr = screenattr(1, 1) + diffthis + + botright vert new + call setline(1, ['one', 'two', 'three.']) + diffthis + + redraw + call assert_equal(normattr, screenattr(1, 1)) + call assert_equal(normattr, screenattr(2, 1)) + call assert_notequal(normattr, screenattr(3, 1)) + diffoff! + + " Try using a non-existing function for 'diffexpr'. + set diffexpr=NewDiffFunc() + call assert_fails('windo diffthis', ['E117:', 'E97:']) + diffoff! + + " Using a script-local function + func s:NewDiffExpr() + endfunc + set diffexpr=s:NewDiffExpr() + call assert_equal(expand('<SID>') .. 'NewDiffExpr()', &diffexpr) + set diffexpr=<SID>NewDiffExpr() + call assert_equal(expand('<SID>') .. 'NewDiffExpr()', &diffexpr) + + %bwipe! + set diffexpr& diffopt& + delfunc DiffExpr + delfunc s:NewDiffExpr +endfunc + +func Test_diffpatch() + " The patch program on MS-Windows may fail or hang. + CheckExecutable patch + CheckUnix + new + insert +*************** +*** 1,3 **** + 1 +! 2 + 3 +--- 1,4 ---- + 1 +! 2x + 3 ++ 4 +. + saveas! Xpatch + bwipe! + new + call assert_fails('diffpatch Xpatch', 'E816:') + + for name in ['Xpatch', 'Xpatch$HOME', 'Xpa''tch'] + call setline(1, ['1', '2', '3']) + if name != 'Xpatch' + call rename('Xpatch', name) + endif + exe 'diffpatch ' . escape(name, '$') + call assert_equal(['1', '2x', '3', '4'], getline(1, '$')) + if name != 'Xpatch' + call rename(name, 'Xpatch') + endif + bwipe! + endfor + + call delete('Xpatch') + bwipe! +endfunc + +" FIXME: test fails, the Xresult file can't be read +func No_Test_diffpatch_restricted() + let lines =<< trim END + call assert_fails('diffpatch NoSuchDiff', 'E145:') + + call writefile(v:errors, 'Xresult') + qa! + END + call writefile(lines, 'Xrestricted', 'D') + if RunMNV([], [], '-Z --clean -S Xrestricted') + call assert_equal([], readfile('Xresult')) + endif + call delete('Xresult') +endfunc + +func Test_diff_too_many_buffers() + for i in range(1, 8) + exe "new Xtest" . i + diffthis + endfor + new Xtest9 + call assert_fails('diffthis', 'E96:') + %bwipe! +endfunc + +func Test_diff_nomodifiable() + new + call setline(1, [1, 2, 3, 4]) + setl nomodifiable + diffthis + vnew + call setline(1, ['1x', 2, 3, 3, 4]) + diffthis + call assert_fails('norm dp', 'E793:') + setl nomodifiable + call assert_fails('norm do', 'E21:') + %bwipe! +endfunc + +func Test_diff_hlID() + set diffopt=internal,filler + new + call setline(1, [1, 2, 3, 'Yz', 'a dxxg',]) + diffthis + vnew + call setline(1, ['1x', 2, 'x', 3, 'yx', 'abc defg']) + diffthis + redraw + + call diff_hlID(-1, 1)->synIDattr("name")->assert_equal("") + + call diff_hlID(1, 1)->synIDattr("name")->assert_equal("DiffChange") + call diff_hlID(1, 2)->synIDattr("name")->assert_equal("DiffText") + call diff_hlID(2, 1)->synIDattr("name")->assert_equal("") + call diff_hlID(3, 1)->synIDattr("name")->assert_equal("DiffAdd") + eval 4->diff_hlID(1)->synIDattr("name")->assert_equal("") + call diff_hlID(5, 1)->synIDattr("name")->assert_equal("DiffText") + call diff_hlID(5, 2)->synIDattr("name")->assert_equal("DiffText") + + set diffopt+=icase " test that caching is invalidated by diffopt change + call diff_hlID(5, 1)->synIDattr("name")->assert_equal("DiffChange") + set diffopt-=icase + call diff_hlID(5, 1)->synIDattr("name")->assert_equal("DiffText") + + call diff_hlID(6, 1)->synIDattr("name")->assert_equal("DiffChange") + call diff_hlID(6, 2)->synIDattr("name")->assert_equal("DiffText") + call diff_hlID(6, 4)->synIDattr("name")->assert_equal("DiffText") + call diff_hlID(6, 7)->synIDattr("name")->assert_equal("DiffText") + call diff_hlID(6, 8)->synIDattr("name")->assert_equal("DiffChange") + set diffopt+=inline:char + call diff_hlID(6, 1)->synIDattr("name")->assert_equal("DiffChange") + call diff_hlID(6, 2)->synIDattr("name")->assert_equal("DiffTextAdd") + call diff_hlID(6, 4)->synIDattr("name")->assert_equal("DiffChange") + call diff_hlID(6, 7)->synIDattr("name")->assert_equal("DiffText") + call diff_hlID(6, 8)->synIDattr("name")->assert_equal("DiffChange") + set diffopt-=inline:char + + wincmd w + call assert_equal(synIDattr(diff_hlID(1, 1), "name"), "DiffChange") + call assert_equal(synIDattr(diff_hlID(2, 1), "name"), "") + call assert_equal(synIDattr(diff_hlID(3, 1), "name"), "") + + %bwipe! + set diffopt& +endfunc + +func Test_diff_filler() + new + call setline(1, [1, 2, 3, 'x', 4]) + diffthis + vnew + call setline(1, [1, 2, 'y', 'y', 3, 4]) + diffthis + redraw + + call assert_equal([0, 0, 0, 0, 0, 0, 0, 1, 0], map(range(-1, 7), 'v:val->diff_filler()')) + wincmd w + call assert_equal([0, 0, 0, 0, 2, 0, 0, 0], map(range(-1, 6), 'diff_filler(v:val)')) + + %bwipe! +endfunc + +func Test_diff_lastline() + enew! + only! + call setline(1, ['This is a ', 'line with five ', 'rows']) + diffthis + botright vert new + call setline(1, ['This is', 'a line with ', 'four rows']) + diffthis + 1 + call feedkeys("Je a\<CR>", 'tx') + call feedkeys("Je a\<CR>", 'tx') + let w1lines = winline() + wincmd w + $ + let w2lines = winline() + call assert_equal(w2lines, w1lines) + bwipe! + bwipe! +endfunc + +func WriteDiffFiles(buf, list1, list2) + call writefile(a:list1, 'Xdifile1') + call writefile(a:list2, 'Xdifile2') + if a:buf + call term_sendkeys(a:buf, ":checktime\<CR>") + endif +endfunc + +func WriteDiffFiles3(buf, list1, list2, list3) + call writefile(a:list1, 'Xdifile1') + call writefile(a:list2, 'Xdifile2') + call writefile(a:list3, 'Xdifile3') + if a:buf + call term_sendkeys(a:buf, ":checktime\<CR>") + endif +endfunc + +" Verify a screendump with both the internal and external diff. +func VerifyBoth(buf, dumpfile, extra) + CheckScreendump + + " trailing : for leaving the cursor on the command line + for cmd in [":set diffopt=filler" . a:extra . "\<CR>:", ":set diffopt+=internal\<CR>:"] + call term_sendkeys(a:buf, cmd) + if VerifyScreenDump(a:buf, a:dumpfile, {}, cmd =~ 'internal' ? 'internal' : 'external') + " don't let the next iteration overwrite the "failed" file. + return + endif + endfor + + " also test unified diff + call term_sendkeys(a:buf, ":call SetupUnified()\<CR>:") + call term_sendkeys(a:buf, ":redraw!\<CR>:") + call VerifyScreenDump(a:buf, a:dumpfile, {}, 'unified') + call term_sendkeys(a:buf, ":call StopUnified()\<CR>:") +endfunc + +" Verify a screendump with the internal diff only. +func VerifyInternal(buf, dumpfile, extra) + CheckScreendump + + call term_sendkeys(a:buf, ":diffupdate!\<CR>") + " trailing : for leaving the cursor on the command line + call term_sendkeys(a:buf, ":set diffopt=internal,filler" . a:extra . "\<CR>:") + call VerifyScreenDump(a:buf, a:dumpfile, {}) +endfunc + +func Test_diff_screen() + if has('bsd') + CheckExecutable gdiff + endif + if has('osxdarwin') && system('diff --version') =~ '^Apple diff' + throw 'Skipped: unified diff does not work properly on this macOS version' + endif + + let g:test_is_flaky = 1 + CheckScreendump + CheckFeature menu + + let lines =<< trim END + func UnifiedDiffExpr() + " Prepend some text to check diff type detection + call writefile(['warning', ' message'], v:fname_out) + let diff = has('bsd') ? 'gdiff' : 'diff' + silent exe $'!{diff} -U0 {v:fname_in} {v:fname_new}>>{v:fname_out}' + endfunc + func SetupUnified() + set diffexpr=UnifiedDiffExpr() + diffupdate + endfunc + func StopUnified() + set diffexpr= + endfunc + END + call writefile(lines, 'XdiffSetup', 'D') + + " clean up already existing swap files, just in case + call delete('.Xdifile1.swp') + call delete('.Xdifile2.swp') + + " Test 1: Add a line in beginning of file 2 + call WriteDiffFiles(0, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) + let buf = RunMNVInTerminal('-d -S XdiffSetup Xdifile1 Xdifile2', {}) + " Set autoread mode, so that MNV won't complain once we re-write the test + " files + call term_sendkeys(buf, ":set autoread\<CR>\<c-w>w:set autoread\<CR>\<c-w>w") + + call VerifyBoth(buf, 'Test_diff_01', '') + + " Test 2: Add a line in beginning of file 1 + call WriteDiffFiles(buf, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) + call VerifyBoth(buf, 'Test_diff_02', '') + + " Test 3: Add a line at the end of file 2 + call WriteDiffFiles(buf, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]) + call VerifyBoth(buf, 'Test_diff_03', '') + + " Test 4: Add a line at the end of file 1 + call WriteDiffFiles(buf, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) + call VerifyBoth(buf, 'Test_diff_04', '') + + " Test 5: Add a line in the middle of file 2, remove on at the end of file 1 + call WriteDiffFiles(buf, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], [1, 2, 3, 4, 4, 5, 6, 7, 8, 9, 10]) + call VerifyBoth(buf, 'Test_diff_05', '') + + " Test 6: Add a line in the middle of file 1, remove on at the end of file 2 + call WriteDiffFiles(buf, [1, 2, 3, 4, 4, 5, 6, 7, 8, 9, 10], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]) + call VerifyBoth(buf, 'Test_diff_06', '') + + " Variants on test 6 with different context settings + call term_sendkeys(buf, ":set diffopt+=context:2\<cr>") + call VerifyScreenDump(buf, 'Test_diff_06.2', {}) + call term_sendkeys(buf, ":set diffopt-=context:2\<cr>") + call term_sendkeys(buf, ":set diffopt+=context:1\<cr>") + call VerifyScreenDump(buf, 'Test_diff_06.1', {}) + call term_sendkeys(buf, ":set diffopt-=context:1\<cr>") + call term_sendkeys(buf, ":set diffopt+=context:0\<cr>") + call VerifyScreenDump(buf, 'Test_diff_06.0', {}) + call term_sendkeys(buf, ":set diffopt-=context:0\<cr>") + + " Test 7 - 9: Test normal/patience/histogram diff algorithm + call WriteDiffFiles(buf, ['#include <stdio.h>', '', '// Frobs foo heartily', 'int frobnitz(int foo)', '{', + \ ' int i;', ' for(i = 0; i < 10; i++)', ' {', ' printf("Your answer is: ");', + \ ' printf("%d\n", foo);', ' }', '}', '', 'int fact(int n)', '{', ' if(n > 1)', ' {', + \ ' return fact(n-1) * n;', ' }', ' return 1;', '}', '', 'int main(int argc, char **argv)', + \ '{', ' frobnitz(fact(10));', '}'], + \ ['#include <stdio.h>', '', 'int fib(int n)', '{', ' if(n > 2)', ' {', + \ ' return fib(n-1) + fib(n-2);', ' }', ' return 1;', '}', '', '// Frobs foo heartily', + \ 'int frobnitz(int foo)', '{', ' int i;', ' for(i = 0; i < 10; i++)', ' {', + \ ' printf("%d\n", foo);', ' }', '}', '', + \ 'int main(int argc, char **argv)', '{', ' frobnitz(fib(10));', '}']) + call term_sendkeys(buf, ":diffupdate!\<cr>") + call term_sendkeys(buf, ":set diffopt+=internal\<cr>") + call VerifyScreenDump(buf, 'Test_diff_07', {}) + + call term_sendkeys(buf, ":set diffopt+=algorithm:patience\<cr>") + call VerifyScreenDump(buf, 'Test_diff_08', {}) + + call term_sendkeys(buf, ":set diffopt+=algorithm:histogram\<cr>") + call VerifyScreenDump(buf, 'Test_diff_09', {}) + + " Test 10-11: with/without indent-heuristic + call term_sendkeys(buf, ":set diffopt&mnv\<cr>") + call WriteDiffFiles(buf, ['', ' def finalize(values)', '', ' values.each do |v|', ' v.finalize', ' end'], + \ ['', ' def finalize(values)', '', ' values.each do |v|', ' v.prepare', ' end', '', + \ ' values.each do |v|', ' v.finalize', ' end']) + call term_sendkeys(buf, ":diffupdate!\<cr>") + call term_sendkeys(buf, ":set diffopt+=internal\<cr>:\<cr>") + call VerifyScreenDump(buf, 'Test_diff_11', {}) + + " Leave trailing : at commandline! + call term_sendkeys(buf, ":set diffopt-=indent-heuristic\<cr>:\<cr>") + call VerifyScreenDump(buf, 'Test_diff_10', {}, 'one') + " shouldn't matter, if indent-algorithm comes before or after the algorithm + call term_sendkeys(buf, ":set diffopt&\<cr>") + call term_sendkeys(buf, ":set diffopt+=indent-heuristic,algorithm:patience\<cr>:\<cr>") + call VerifyScreenDump(buf, 'Test_diff_11', {}, 'two') + call term_sendkeys(buf, ":set diffopt&\<cr>") + call term_sendkeys(buf, ":set diffopt+=algorithm:patience,indent-heuristic\<cr>:\<cr>") + call VerifyScreenDump(buf, 'Test_diff_11', {}, 'three') + + " Test 12: diff the same file + call WriteDiffFiles(buf, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) + call VerifyBoth(buf, 'Test_diff_12', '') + + " Test 13: diff an empty file + call WriteDiffFiles(buf, [], []) + call VerifyBoth(buf, 'Test_diff_13', '') + + " Test 14: test diffopt+=icase + call WriteDiffFiles(buf, ['a', 'b', 'cd'], ['A', 'b', 'cDe']) + call VerifyBoth(buf, 'Test_diff_14', " diffopt+=filler diffopt+=icase") + + " Test 15-16: test diffopt+=iwhite + call WriteDiffFiles(buf, ['int main()', '{', ' printf("Hello, World!");', ' return 0;', '}'], + \ ['int main()', '{', ' if (0)', ' {', ' printf("Hello, World!");', ' return 0;', ' }', '}']) + call term_sendkeys(buf, ":diffupdate!\<cr>") + call term_sendkeys(buf, ":set diffopt&mnv diffopt+=filler diffopt+=iwhite\<cr>") + call VerifyScreenDump(buf, 'Test_diff_15', {}) + call term_sendkeys(buf, ":set diffopt+=internal\<cr>") + call VerifyScreenDump(buf, 'Test_diff_16', {}) + + " Test 17: test diffopt+=iblank + call WriteDiffFiles(buf, ['a', ' ', 'cd', 'ef', 'xxx'], ['a', 'cd', '', 'ef', 'yyy']) + call VerifyInternal(buf, 'Test_diff_17', " diffopt+=iblank") + + " Test 18: test diffopt+=iblank,iwhite / iwhiteall / iwhiteeol + call VerifyInternal(buf, 'Test_diff_18', " diffopt+=iblank,iwhite") + call VerifyInternal(buf, 'Test_diff_18', " diffopt+=iblank,iwhiteall") + call VerifyInternal(buf, 'Test_diff_18', " diffopt+=iblank,iwhiteeol") + + " Test 19: test diffopt+=iwhiteeol + call WriteDiffFiles(buf, ['a ', 'x', 'cd', 'ef', 'xx xx', 'foo', 'bar'], ['a', 'x', 'c d', ' ef', 'xx xx', 'foo', '', 'bar']) + call VerifyInternal(buf, 'Test_diff_19', " diffopt+=iwhiteeol") + + " Test 20: test diffopt+=iwhiteall + call VerifyInternal(buf, 'Test_diff_20', " diffopt+=iwhiteall") + + " Test 21: Delete all lines + call WriteDiffFiles(buf, [0], []) + call VerifyBoth(buf, "Test_diff_21", "") + + " Test 22: Add line to empty file + call WriteDiffFiles(buf, [], [0]) + call VerifyBoth(buf, "Test_diff_22", "") + + call WriteDiffFiles(buf, ['?a', '?b', '?c'], ['!b']) + call VerifyInternal(buf, 'Test_diff_23', " diffopt+=linematch:30") + + call WriteDiffFiles(buf, ['', + \ 'common line', + \ 'common line', + \ '', + \ 'DEFabc', + \ 'xyz', + \ 'xyz', + \ 'xyz', + \ 'DEFabc', + \ 'DEFabc', + \ 'DEFabc', + \ 'common line', + \ 'common line', + \ 'DEF', + \ 'common line', + \ 'DEF', + \ 'something' ], + \ ['', + \ 'common line', + \ 'common line', + \ '', + \ 'ABCabc', + \ 'ABCabc', + \ 'ABCabc', + \ 'ABCabc', + \ 'common line', + \ 'common line', + \ 'common line', + \ 'something']) + call VerifyInternal(buf, 'Test_diff_24', " diffopt+=linematch:30") + + + " clean up + call StopMNVInTerminal(buf) + call delete('Xdifile1') + call delete('Xdifile2') +endfunc + +func Test_diff_with_scroll_and_change() + CheckScreendump + + let lines =<< trim END + call setline(1, range(1, 15)) + vnew + call setline(1, range(9, 15)) + windo diffthis + wincmd h + exe "normal Gl5\<C-E>" + END + call writefile(lines, 'Xtest_scroll_change', 'D') + let buf = RunMNVInTerminal('-S Xtest_scroll_change', {}) + + call VerifyScreenDump(buf, 'Test_diff_scroll_change_01', {}) + + call term_sendkeys(buf, "ax\<Esc>") + call VerifyScreenDump(buf, 'Test_diff_scroll_change_02', {}) + + call term_sendkeys(buf, "\<C-W>lay\<Esc>") + call VerifyScreenDump(buf, 'Test_diff_scroll_change_03', {}) + + " clean up + call StopMNVInTerminal(buf) +endfunc + +func Test_diff_with_cursorline() + CheckScreendump + + call writefile([ + \ 'hi CursorLine ctermbg=red ctermfg=white', + \ 'set cursorline', + \ 'call setline(1, ["foo","foo","foo","bar"])', + \ 'vnew', + \ 'call setline(1, ["bee","foo","foo","baz"])', + \ 'windo diffthis', + \ '2wincmd w', + \ ], 'Xtest_diff_cursorline', 'D') + let buf = RunMNVInTerminal('-S Xtest_diff_cursorline', {}) + + call VerifyScreenDump(buf, 'Test_diff_with_cursorline_01', {}) + call term_sendkeys(buf, "j") + call VerifyScreenDump(buf, 'Test_diff_with_cursorline_02', {}) + call term_sendkeys(buf, "j") + call VerifyScreenDump(buf, 'Test_diff_with_cursorline_03', {}) + + " clean up + call StopMNVInTerminal(buf) +endfunc + +func Test_diff_with_cursorline_number() + CheckScreendump + + let lines =<< trim END + hi CursorLine ctermbg=red ctermfg=white + hi CursorLineNr ctermbg=white ctermfg=black cterm=underline + set cursorline number + call setline(1, ["baz", "foo", "foo", "bar"]) + 2 + vnew + call setline(1, ["foo", "foo", "bar"]) + windo diffthis + 1wincmd w + END + call writefile(lines, 'Xtest_diff_cursorline_number', 'D') + let buf = RunMNVInTerminal('-S Xtest_diff_cursorline_number', {}) + + call VerifyScreenDump(buf, 'Test_diff_with_cursorline_number_01', {}) + call term_sendkeys(buf, ":set cursorlineopt=number\r") + call VerifyScreenDump(buf, 'Test_diff_with_cursorline_number_02', {}) + + " clean up + call StopMNVInTerminal(buf) +endfunc + +func Test_diff_with_cursorline_breakindent() + CheckScreendump + + let lines =<< trim END + hi CursorLine ctermbg=red ctermfg=white + set noequalalways wrap diffopt=followwrap cursorline breakindent + 50vnew + call setline(1, [' ', ' ', ' ', ' ']) + exe "norm! 20Afoo\<Esc>j20Afoo\<Esc>j20Afoo\<Esc>j20Abar\<Esc>" + vnew + call setline(1, [' ', ' ', ' ', ' ']) + exe "norm! 20Abee\<Esc>j20Afoo\<Esc>j20Afoo\<Esc>j20Abaz\<Esc>" + windo diffthis + 2wincmd w + END + call writefile(lines, 'Xtest_diff_cursorline_breakindent', 'D') + let buf = RunMNVInTerminal('-S Xtest_diff_cursorline_breakindent', {}) + + call term_sendkeys(buf, "gg0") + call VerifyScreenDump(buf, 'Test_diff_with_cul_bri_01', {}) + call term_sendkeys(buf, "j") + call VerifyScreenDump(buf, 'Test_diff_with_cul_bri_02', {}) + call term_sendkeys(buf, "j") + call VerifyScreenDump(buf, 'Test_diff_with_cul_bri_03', {}) + call term_sendkeys(buf, "j") + call VerifyScreenDump(buf, 'Test_diff_with_cul_bri_04', {}) + + " clean up + call StopMNVInTerminal(buf) +endfunc + +func Test_diff_breakindent_after_filler() + CheckScreendump + + let lines =<< trim END + set laststatus=0 diffopt+=followwrap breakindent breakindentopt=min:0 + call setline(1, ['a', ' ' .. repeat('c', 50)]) + vnew + call setline(1, ['a', 'b', ' ' .. repeat('c', 50)]) + windo diffthis + norm! G$ + END + call writefile(lines, 'Xtest_diff_breakindent_after_filler', 'D') + let buf = RunMNVInTerminal('-S Xtest_diff_breakindent_after_filler', #{rows: 8, cols: 45}) + call VerifyScreenDump(buf, 'Test_diff_breakindent_after_filler', {}) + + " clean up + call StopMNVInTerminal(buf) +endfunc + +func Test_diff_with_syntax() + CheckScreendump + + let lines =<< trim END + void doNothing() { + int x = 0; + char *s = "hello"; + return 5; + } + END + call writefile(lines, 'Xprogram1.c', 'D') + let lines =<< trim END + void doSomething() { + int x = 0; + char *s = "there"; + return 5; + } + END + call writefile(lines, 'Xprogram2.c', 'D') + + let lines =<< trim END + set diffopt=internal,filler + edit Xprogram1.c + diffsplit Xprogram2.c + END + call writefile(lines, 'Xtest_diff_syntax', 'D') + let buf = RunMNVInTerminal('-S Xtest_diff_syntax', {}) + + call VerifyScreenDump(buf, 'Test_diff_syntax_1', {}) + + " clean up + call StopMNVInTerminal(buf) +endfunc + +func Test_diff_of_diff() + CheckScreendump + CheckFeature rightleft + + call writefile([ + \ 'call setline(1, ["aa","bb","cc","@@ -3,2 +5,7 @@","dd","ee","ff"])', + \ 'vnew', + \ 'call setline(1, ["aa","bb","cc"])', + \ 'windo diffthis', + \ '1wincmd w', + \ 'setlocal number', + \ ], 'Xtest_diff_diff', 'D') + let buf = RunMNVInTerminal('-S Xtest_diff_diff', {}) + + call VerifyScreenDump(buf, 'Test_diff_of_diff_01', {}) + + call term_sendkeys(buf, ":set rightleft\<cr>") + call VerifyScreenDump(buf, 'Test_diff_of_diff_02', {}) + + " clean up + call StopMNVInTerminal(buf) +endfunc + +func CloseoffSetup() + enew + call setline(1, ['one', 'two', 'three']) + diffthis + new + call setline(1, ['one', 'tow', 'three']) + diffthis + call assert_equal(1, &diff) + bw! +endfunc + +func Test_diff_closeoff() + " "closeoff" included by default: last diff win gets 'diff' reset' + call CloseoffSetup() + call assert_equal(0, &diff) + enew! + + " "closeoff" excluded: last diff win keeps 'diff' set' + set diffopt-=closeoff + call CloseoffSetup() + call assert_equal(1, &diff) + diffoff! + enew! +endfunc + +func Test_diff_followwrap() + new + set diffopt+=followwrap + set wrap + diffthis + call assert_equal(1, &wrap) + diffoff + set nowrap + diffthis + call assert_equal(0, &wrap) + diffoff + set diffopt& + bwipe! +endfunc + +func Test_diff_maintains_change_mark() + func DiffMaintainsChangeMark() + enew! + call setline(1, ['a', 'b', 'c', 'd']) + diffthis + new + call setline(1, ['a', 'b', 'c', 'e']) + " Set '[ and '] marks + 2,3yank + call assert_equal([2, 3], [line("'["), line("']")]) + " Verify they aren't affected by the implicit diff + diffthis + call assert_equal([2, 3], [line("'["), line("']")]) + " Verify they aren't affected by an explicit diff + diffupdate + call assert_equal([2, 3], [line("'["), line("']")]) + bwipe! + bwipe! + endfunc + + set diffopt-=internal + call DiffMaintainsChangeMark() + set diffopt+=internal + call DiffMaintainsChangeMark() + + set diffopt& + delfunc DiffMaintainsChangeMark +endfunc + +" Test for 'patchexpr' +func Test_patchexpr() + let g:patch_args = [] + func TPatch() + call add(g:patch_args, readfile(v:fname_in)) + call add(g:patch_args, readfile(v:fname_diff)) + call writefile(['output file'], v:fname_out) + endfunc + set patchexpr=TPatch() + + call writefile(['input file'], 'Xinput', 'D') + call writefile(['diff file'], 'Xdiff', 'D') + %bwipe! + edit Xinput + diffpatch Xdiff + call assert_equal('output file', getline(1)) + call assert_equal('Xinput.new', bufname()) + call assert_equal(2, winnr('$')) + call assert_true(&diff) + + " Using a script-local function + func s:NewPatchExpr() + endfunc + set patchexpr=s:NewPatchExpr() + call assert_equal(expand('<SID>') .. 'NewPatchExpr()', &patchexpr) + set patchexpr=<SID>NewPatchExpr() + call assert_equal(expand('<SID>') .. 'NewPatchExpr()', &patchexpr) + + set patchexpr& + delfunc TPatch + delfunc s:NewPatchExpr + %bwipe! +endfunc + +func Test_diff_rnu() + CheckScreendump + + let content =<< trim END + set diffopt=internal,filler + call setline(1, ['a', 'a', 'a', 'y', 'b', 'b', 'b', 'b', 'b']) + vnew + call setline(1, ['a', 'a', 'a', 'x', 'x', 'x', 'b', 'b', 'b', 'b', 'b']) + windo diffthis + setlocal number rnu foldcolumn=0 + END + call writefile(content, 'Xtest_diff_rnu', 'D') + let buf = RunMNVInTerminal('-S Xtest_diff_rnu', {}) + + call VerifyScreenDump(buf, 'Test_diff_rnu_01', {}) + + call term_sendkeys(buf, "j") + call VerifyScreenDump(buf, 'Test_diff_rnu_02', {}) + call term_sendkeys(buf, "j") + call VerifyScreenDump(buf, 'Test_diff_rnu_03', {}) + + " clean up + call StopMNVInTerminal(buf) +endfunc + +func Test_diff_multilineconceal() + new + diffthis + + new + call matchadd('Conceal', 'a\nb', 9, -1, {'conceal': 'Y'}) + set cole=2 cocu=n + call setline(1, ["a", "b"]) + diffthis + redraw +endfunc + +func Test_diff_and_scroll() + " this was causing an ml_get error + set ls=2 + for i in range(winheight(0) * 2) + call setline(i, i < winheight(0) - 10 ? i : i + 10) + endfor + vnew + for i in range(winheight(0)*2 + 10) + call setline(i, i < winheight(0) - 10 ? 0 : i) + endfor + diffthis + wincmd p + diffthis + execute 'normal ' . winheight(0) . "\<C-d>" + + bwipe! + bwipe! + set ls& +endfunc + +func Test_diff_filler_cursorcolumn() + CheckScreendump + + let content =<< trim END + call setline(1, ['aa', 'bb', 'cc']) + vnew + call setline(1, ['aa', 'cc']) + windo diffthis + wincmd p + setlocal cursorcolumn foldcolumn=0 + norm! gg0 + redraw! + END + call writefile(content, 'Xtest_diff_cuc', 'D') + let buf = RunMNVInTerminal('-S Xtest_diff_cuc', {}) + + call VerifyScreenDump(buf, 'Test_diff_cuc_01', {}) + + call term_sendkeys(buf, "l") + call term_sendkeys(buf, "\<C-l>") + call VerifyScreenDump(buf, 'Test_diff_cuc_02', {}) + call term_sendkeys(buf, "0j") + call term_sendkeys(buf, "\<C-l>") + call VerifyScreenDump(buf, 'Test_diff_cuc_03', {}) + call term_sendkeys(buf, "l") + call term_sendkeys(buf, "\<C-l>") + call VerifyScreenDump(buf, 'Test_diff_cuc_04', {}) + + " clean up + call StopMNVInTerminal(buf) +endfunc + +" Test for adding/removing lines inside diff chunks, between diff chunks +" and before diff chunks +func Test_diff_modify_chunks() + set diffopt=internal,filler + enew! + let w2_id = win_getid() + call setline(1, ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']) + new + let w1_id = win_getid() + call setline(1, ['a', '2', '3', 'd', 'e', 'f', '7', '8', 'i']) + windo diffthis + + " remove a line between two diff chunks and create a new diff chunk + call win_gotoid(w2_id) + 5d + call win_gotoid(w1_id) + call diff_hlID(5, 1)->synIDattr('name')->assert_equal('DiffAdd') + + " add a line between two diff chunks + call win_gotoid(w2_id) + normal! 4Goe + call win_gotoid(w1_id) + call diff_hlID(4, 1)->synIDattr('name')->assert_equal('') + call diff_hlID(5, 1)->synIDattr('name')->assert_equal('') + + " remove all the lines in a diff chunk. + call win_gotoid(w2_id) + 7,8d + call win_gotoid(w1_id) + let hl = range(1, 9)->map({_, lnum -> diff_hlID(lnum, 1)->synIDattr('name')}) + call assert_equal(['', 'DiffText', 'DiffText', '', '', '', 'DiffAdd', + \ 'DiffAdd', ''], hl) + + " remove lines from one diff chunk to just before the next diff chunk + call win_gotoid(w2_id) + call setline(1, ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']) + 2,6d + call win_gotoid(w1_id) + let hl = range(1, 9)->map({_, lnum -> diff_hlID(lnum, 1)->synIDattr('name')}) + call assert_equal(['', 'DiffText', 'DiffText', 'DiffAdd', 'DiffAdd', + \ 'DiffAdd', 'DiffAdd', 'DiffAdd', ''], hl) + + " remove lines just before the top of a diff chunk + call win_gotoid(w2_id) + call setline(1, ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']) + 5,6d + call win_gotoid(w1_id) + let hl = range(1, 9)->map({_, lnum -> diff_hlID(lnum, 1)->synIDattr('name')}) + call assert_equal(['', 'DiffText', 'DiffText', '', 'DiffText', 'DiffText', + \ 'DiffAdd', 'DiffAdd', ''], hl) + + " remove line after the end of a diff chunk + call win_gotoid(w2_id) + call setline(1, ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']) + 4d + call win_gotoid(w1_id) + let hl = range(1, 9)->map({_, lnum -> diff_hlID(lnum, 1)->synIDattr('name')}) + call assert_equal(['', 'DiffText', 'DiffText', 'DiffAdd', '', '', 'DiffText', + \ 'DiffText', ''], hl) + + " remove lines starting from the end of one diff chunk and ending inside + " another diff chunk + call win_gotoid(w2_id) + call setline(1, ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']) + 4,7d + call win_gotoid(w1_id) + let hl = range(1, 9)->map({_, lnum -> diff_hlID(lnum, 1)->synIDattr('name')}) + call assert_equal(['', 'DiffText', 'DiffText', 'DiffText', 'DiffAdd', + \ 'DiffAdd', 'DiffAdd', 'DiffAdd', ''], hl) + + " removing the only remaining diff chunk should make the files equal + call win_gotoid(w2_id) + call setline(1, ['a', '2', '3', 'x', 'd', 'e', 'f', 'x', '7', '8', 'i']) + 8d + let hl = range(1, 10)->map({_, lnum -> diff_hlID(lnum, 1)->synIDattr('name')}) + call assert_equal(['', '', '', 'DiffAdd', '', '', '', '', '', ''], hl) + call win_gotoid(w2_id) + 4d + call win_gotoid(w1_id) + let hl = range(1, 9)->map({_, lnum -> diff_hlID(lnum, 1)->synIDattr('name')}) + call assert_equal(['', '', '', '', '', '', '', '', ''], hl) + + %bw! + set diffopt& +endfunc + +func Test_diff_binary() + CheckScreendump + + let content =<< trim END + call setline(1, ['a', 'b', "c\n", 'd', 'e', 'f', 'g']) + vnew + call setline(1, ['A', 'b', 'c', 'd', 'E', 'f', 'g']) + windo diffthis + wincmd p + norm! gg0 + redraw! + END + call writefile(content, 'Xtest_diff_bin', 'D') + let buf = RunMNVInTerminal('-S Xtest_diff_bin', {}) + + " Test using internal diff + call VerifyScreenDump(buf, 'Test_diff_bin_01', {}) + + " Test using internal diff and case folding + call term_sendkeys(buf, ":set diffopt+=icase\<cr>") + call term_sendkeys(buf, "\<C-l>") + call VerifyScreenDump(buf, 'Test_diff_bin_02', {}) + " Test using external diff + call term_sendkeys(buf, ":set diffopt=filler\<cr>") + call term_sendkeys(buf, "\<C-l>") + call VerifyScreenDump(buf, 'Test_diff_bin_03', {}) + " Test using external diff and case folding + call term_sendkeys(buf, ":set diffopt=filler,icase\<cr>") + call term_sendkeys(buf, "\<C-l>") + call VerifyScreenDump(buf, 'Test_diff_bin_04', {}) + + " clean up + call StopMNVInTerminal(buf) + set diffopt&mnv +endfunc + +" Test for using the 'zi' command to invert 'foldenable' in diff windows (test +" for the issue fixed by patch 6.2.317) +func Test_diff_foldinvert() + %bw! + edit Xdoffile1 + new Xdoffile2 + new Xdoffile3 + windo diffthis + " open a non-diff window + botright new + 1wincmd w + call assert_true(getwinvar(1, '&foldenable')) + call assert_true(getwinvar(2, '&foldenable')) + call assert_true(getwinvar(3, '&foldenable')) + normal zi + call assert_false(getwinvar(1, '&foldenable')) + call assert_false(getwinvar(2, '&foldenable')) + call assert_false(getwinvar(3, '&foldenable')) + normal zi + call assert_true(getwinvar(1, '&foldenable')) + call assert_true(getwinvar(2, '&foldenable')) + call assert_true(getwinvar(3, '&foldenable')) + + " If the current window has 'noscrollbind', then 'zi' should not change + " 'foldenable' in other windows. + 1wincmd w + set noscrollbind + normal zi + call assert_false(getwinvar(1, '&foldenable')) + call assert_true(getwinvar(2, '&foldenable')) + call assert_true(getwinvar(3, '&foldenable')) + + " 'zi' should not change the 'foldenable' for windows with 'noscrollbind' + 1wincmd w + set scrollbind + normal zi + call setwinvar(2, '&scrollbind', v:false) + normal zi + call assert_false(getwinvar(1, '&foldenable')) + call assert_true(getwinvar(2, '&foldenable')) + call assert_false(getwinvar(3, '&foldenable')) + + %bw! + set scrollbind& +endfunc + +" This was scrolling for 'cursorbind' but 'scrollbind' is more important +func Test_diff_scroll() + CheckScreendump + + let left =<< trim END + line 1 + line 2 + line 3 + line 4 + + // Common block + // one + // containing + // four lines + + // Common block + // two + // containing + // four lines + END + call writefile(left, 'Xleft', 'D') + let right =<< trim END + line 1 + line 2 + line 3 + line 4 + + Lorem + ipsum + dolor + sit + amet, + consectetur + adipiscing + elit. + Etiam + luctus + lectus + sodales, + dictum + + // Common block + // one + // containing + // four lines + + Vestibulum + tincidunt + aliquet + nulla. + + // Common block + // two + // containing + // four lines + END + call writefile(right, 'Xright', 'D') + let buf = RunMNVInTerminal('-d Xleft Xright', {'rows': 12}) + call term_sendkeys(buf, "\<C-W>\<C-W>jjjj") + call VerifyScreenDump(buf, 'Test_diff_scroll_1', {}) + call term_sendkeys(buf, "j") + call VerifyScreenDump(buf, 'Test_diff_scroll_2', {}) + + call StopMNVInTerminal(buf) +endfunc + +" This was scrolling too many lines. +func Test_diff_scroll_wrap_on() + 20new + 40vsplit + call setline(1, map(range(1, 9), 'repeat(v:val, 200)')) + setlocal number diff so=0 + redraw + normal! jj + call assert_equal(1, winsaveview().topline) + normal! j + call assert_equal(2, winsaveview().topline) + + bwipe! + bwipe! +endfunc + +func Test_diff_scroll_many_filler() + 20new + vnew + call setline(1, range(1, 40)) + diffthis + setlocal scrolloff=0 + wincmd p + 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. + for _ in range(2) + normal! G + redraw + call assert_equal(40, winsaveview().topline) + call assert_equal(19, winsaveview().topfill) + exe "normal! \<C-B>" + redraw + call assert_equal(22, winsaveview().topline) + call assert_equal(0, winsaveview().topfill) + exe "normal! \<C-B>" + redraw + call assert_equal(4, winsaveview().topline) + call assert_equal(0, winsaveview().topfill) + exe "normal! \<C-B>" + redraw + call assert_equal(1, winsaveview().topline) + call assert_equal(0, winsaveview().topfill) + set smoothscroll + endfor + + set smoothscroll& + %bwipe! +endfunc + +" This was trying to update diffs for a buffer being closed +func Test_diff_only() + silent! lfile + set diff + lopen + norm o + silent! norm o + + set nodiff + %bwipe! +endfunc + +" This was causing invalid diff block values +" FIXME: somehow this causes a valgrind error when run directly but not when +" run as a test. +func Test_diff_manipulations() + set diff + split 0 + sil! norm R
doobdeuR
doobdeuR
doobdeu + + set nodiff + %bwipe! +endfunc + +" This was causing the line number in the diff block to go below one. +" FIXME: somehow this causes a valgrind error when run directly but not when +" run as a test. +func Test_diff_put_and_undo() + set diff + next 0 + split 00 + sil! norm o0gguudpo0ggJuudp + + bwipe! + bwipe! + set nodiff +endfunc + +" Test for the diff() function +def Test_diff_func() + # string is added/removed/modified at the beginning + assert_equal("@@ -0,0 +1 @@\n+abc\n", + diff(['def'], ['abc', 'def'], {output: 'unified'})) + assert_equal([{from_idx: 0, from_count: 0, to_idx: 0, to_count: 1}], + diff(['def'], ['abc', 'def'], {output: 'indices'})) + assert_equal("@@ -1 +0,0 @@\n-abc\n", + diff(['abc', 'def'], ['def'], {output: 'unified'})) + assert_equal([{from_idx: 0, from_count: 1, to_idx: 0, to_count: 0}], + diff(['abc', 'def'], ['def'], {output: 'indices'})) + assert_equal("@@ -1 +1 @@\n-abc\n+abx\n", + diff(['abc', 'def'], ['abx', 'def'], {output: 'unified'})) + assert_equal([{from_idx: 0, from_count: 1, to_idx: 0, to_count: 1}], + diff(['abc', 'def'], ['abx', 'def'], {output: 'indices'})) + + # string is added/removed/modified at the end + assert_equal("@@ -1,0 +2 @@\n+def\n", + diff(['abc'], ['abc', 'def'], {output: 'unified'})) + assert_equal([{from_idx: 1, from_count: 0, to_idx: 1, to_count: 1}], + diff(['abc'], ['abc', 'def'], {output: 'indices'})) + assert_equal("@@ -2 +1,0 @@\n-def\n", + diff(['abc', 'def'], ['abc'], {output: 'unified'})) + assert_equal([{from_idx: 1, from_count: 1, to_idx: 1, to_count: 0}], + diff(['abc', 'def'], ['abc'], {output: 'indices'})) + assert_equal("@@ -2 +2 @@\n-def\n+xef\n", + diff(['abc', 'def'], ['abc', 'xef'], {output: 'unified'})) + assert_equal([{from_idx: 1, from_count: 1, to_idx: 1, to_count: 1}], + diff(['abc', 'def'], ['abc', 'xef'], {output: 'indices'})) + + # string is added/removed/modified in the middle + assert_equal("@@ -2,0 +3 @@\n+xxx\n", + diff(['111', '222', '333'], ['111', '222', 'xxx', '333'], {output: 'unified'})) + assert_equal([{from_idx: 2, from_count: 0, to_idx: 2, to_count: 1}], + diff(['111', '222', '333'], ['111', '222', 'xxx', '333'], {output: 'indices'})) + assert_equal("@@ -3 +2,0 @@\n-333\n", + diff(['111', '222', '333', '444'], ['111', '222', '444'], {output: 'unified'})) + assert_equal([{from_idx: 2, from_count: 1, to_idx: 2, to_count: 0}], + diff(['111', '222', '333', '444'], ['111', '222', '444'], {output: 'indices'})) + assert_equal("@@ -3 +3 @@\n-333\n+xxx\n", + diff(['111', '222', '333', '444'], ['111', '222', 'xxx', '444'], {output: 'unified'})) + assert_equal([{from_idx: 2, from_count: 1, to_idx: 2, to_count: 1}], + diff(['111', '222', '333', '444'], ['111', '222', 'xxx', '444'], {output: 'indices'})) + + # new strings are added to an empty List + assert_equal("@@ -0,0 +1,2 @@\n+abc\n+def\n", + diff([], ['abc', 'def'], {output: 'unified'})) + assert_equal([{from_idx: 0, from_count: 0, to_idx: 0, to_count: 2}], + diff([], ['abc', 'def'], {output: 'indices'})) + + # all the strings are removed from a List + assert_equal("@@ -1,2 +0,0 @@\n-abc\n-def\n", + diff(['abc', 'def'], [], {output: 'unified'})) + assert_equal([{from_idx: 0, from_count: 2, to_idx: 0, to_count: 0}], + diff(['abc', 'def'], [], {output: 'indices'})) + + # First character is added/removed/different + assert_equal("@@ -1 +1 @@\n-abc\n+bc\n", + diff(['abc'], ['bc'], {output: 'unified'})) + assert_equal([{from_idx: 0, from_count: 1, to_idx: 0, to_count: 1}], + diff(['abc'], ['bc'], {output: 'indices'})) + assert_equal("@@ -1 +1 @@\n-bc\n+abc\n", + diff(['bc'], ['abc'], {output: 'unified'})) + assert_equal([{from_idx: 0, from_count: 1, to_idx: 0, to_count: 1}], + diff(['bc'], ['abc'], {output: 'indices'})) + assert_equal("@@ -1 +1 @@\n-abc\n+xbc\n", + diff(['abc'], ['xbc'], {output: 'unified'})) + assert_equal([{from_idx: 0, from_count: 1, to_idx: 0, to_count: 1}], + diff(['abc'], ['xbc'], {output: 'indices'})) + + # Last character is added/removed/different + assert_equal("@@ -1 +1 @@\n-abc\n+abcd\n", + diff(['abc'], ['abcd'], {output: 'unified'})) + assert_equal([{from_idx: 0, from_count: 1, to_idx: 0, to_count: 1}], + diff(['abc'], ['abcd'], {output: 'indices'})) + assert_equal("@@ -1 +1 @@\n-abcd\n+abc\n", + diff(['abcd'], ['abc'], {output: 'unified'})) + assert_equal([{from_idx: 0, from_count: 1, to_idx: 0, to_count: 1}], + diff(['abcd'], ['abc'], {output: 'indices'})) + var diff_unified: string = diff(['abc'], ['abx'], {output: 'unified'}) + assert_equal("@@ -1 +1 @@\n-abc\n+abx\n", diff_unified) + var diff_indices: list<dict<number>> = + diff(['abc'], ['abx'], {output: 'indices'}) + assert_equal([{from_idx: 0, from_count: 1, to_idx: 0, to_count: 1}], + diff_indices) + + # partial string modification at the start and at the end. + var fromlist =<< trim END + one two + three four + five six + END + var tolist =<< trim END + one + six + END + assert_equal("@@ -1,3 +1,2 @@\n-one two\n-three four\n-five six\n+one\n+six\n", diff(fromlist, tolist, {output: 'unified'})) + assert_equal([{from_idx: 0, from_count: 3, to_idx: 0, to_count: 2}], + diff(fromlist, tolist, {output: 'indices'})) + + # non-contiguous modifications + fromlist =<< trim END + one two + three four + five abc six + END + tolist =<< trim END + one abc two + three four + five six + END + assert_equal("@@ -1 +1 @@\n-one two\n+one abc two\n@@ -3 +3 @@\n-five abc six\n+five six\n", + diff(fromlist, tolist, {output: 'unified'})) + assert_equal([{'from_count': 1, 'to_idx': 0, 'to_count': 1, 'from_idx': 0}, + {'from_count': 1, 'to_idx': 2, 'to_count': 1, 'from_idx': 2}], + diff(fromlist, tolist, {output: 'indices'})) + + # add/remove blank lines + assert_equal("@@ -2,2 +1,0 @@\n-\n-\n", + diff(['one', '', '', 'two'], ['one', 'two'], {output: 'unified'})) + assert_equal([{from_idx: 1, from_count: 2, to_idx: 1, to_count: 0}], + diff(['one', '', '', 'two'], ['one', 'two'], {output: 'indices'})) + assert_equal("@@ -1,0 +2,2 @@\n+\n+\n", + diff(['one', 'two'], ['one', '', '', 'two'], {output: 'unified'})) + assert_equal([{'from_idx': 1, 'from_count': 0, 'to_idx': 1, 'to_count': 2}], + diff(['one', 'two'], ['one', '', '', 'two'], {output: 'indices'})) + + # diff ignoring case + assert_equal('', diff(['abc', 'def'], ['ABC', 'DEF'], {icase: true, output: 'unified'})) + assert_equal([], diff(['abc', 'def'], ['ABC', 'DEF'], {icase: true, output: 'indices'})) + + # diff ignoring all whitespace changes except leading whitespace changes + assert_equal('', diff(['abc def'], ['abc def '], {iwhite: true})) + assert_equal("@@ -1 +1 @@\n- abc\n+ xxx\n", diff([' abc'], [' xxx'], {iwhite: v:true})) + assert_equal("@@ -1 +1 @@\n- abc\n+ xxx\n", diff([' abc'], [' xxx'], {iwhite: v:false})) + assert_equal("@@ -1 +1 @@\n-abc \n+xxx \n", diff(['abc '], ['xxx '], {iwhite: v:true})) + assert_equal("@@ -1 +1 @@\n-abc \n+xxx \n", diff(['abc '], ['xxx '], {iwhite: v:false})) + + # diff ignoring all whitespace changes + assert_equal('', diff(['abc def'], [' abc def '], {iwhiteall: true})) + assert_equal("@@ -1 +1 @@\n- abc \n+ xxx \n", diff([' abc '], [' xxx '], {iwhiteall: v:true})) + assert_equal("@@ -1 +1 @@\n- abc \n+ xxx \n", diff([' abc '], [' xxx '], {iwhiteall: v:false})) + + # diff ignoring trailing whitespace changes + assert_equal('', diff(['abc'], ['abc '], {iwhiteeol: true})) + + # diff ignoring blank lines + assert_equal('', diff(['one', '', '', 'two'], ['one', 'two'], {iblank: true})) + assert_equal('', diff(['one', 'two'], ['one', '', '', 'two'], {iblank: true})) + + # same string + assert_equal('', diff(['abc', 'def', 'ghi'], ['abc', 'def', 'ghi'])) + assert_equal('', diff([''], [''])) + assert_equal('', diff([], [])) + + # different xdiff algorithms + for algo in ['myers', 'minimal', 'patience', 'histogram'] + assert_equal("@@ -1 +1 @@\n- abc \n+ xxx \n", + diff([' abc '], [' xxx '], {algorithm: algo, output: 'unified'})) + assert_equal([{from_idx: 0, from_count: 1, to_idx: 0, to_count: 1}], + diff([' abc '], [' xxx '], {algorithm: algo, output: 'indices'})) + endfor + assert_equal("@@ -1 +1 @@\n- abc \n+ xxx \n", + diff([' abc '], [' xxx '], {indent-heuristic: true, output: 'unified'})) + assert_equal([{from_idx: 0, from_count: 1, to_idx: 0, to_count: 1}], + diff([' abc '], [' xxx '], {indent-heuristic: true, output: 'indices'})) + + # identical strings + assert_equal('', diff(['111', '222'], ['111', '222'], {output: 'unified'})) + assert_equal([], diff(['111', '222'], ['111', '222'], {output: 'indices'})) + assert_equal('', diff([], [], {output: 'unified'})) + assert_equal([], diff([], [], {output: 'indices'})) + + # If 'diffexpr' is set, it should not be used for diff() + def MyDiffExpr() + enddef + var save_diffexpr = &diffexpr + :set diffexpr=MyDiffExpr() + assert_equal("@@ -1 +1 @@\n-abc\n+\n", + diff(['abc'], [''], {output: 'unified'})) + assert_equal([{'from_idx': 0, 'from_count': 1, 'to_idx': 0, 'to_count': 1}], + diff(['abc'], [''], {output: 'indices'})) + assert_equal('MyDiffExpr()', &diffexpr) + &diffexpr = save_diffexpr + + # try different values for unified diff 'context' + assert_equal("@@ -0,0 +1 @@\n+x\n", + diff(['a', 'b', 'c'], ['x', 'a', 'b', 'c'])) + assert_equal("@@ -0,0 +1 @@\n+x\n", + diff(['a', 'b', 'c'], ['x', 'a', 'b', 'c'], {context: 0})) + assert_equal("@@ -1 +1,2 @@\n+x\n a\n", + diff(['a', 'b', 'c'], ['x', 'a', 'b', 'c'], {context: 1})) + assert_equal("@@ -1,2 +1,3 @@\n+x\n a\n b\n", + diff(['a', 'b', 'c'], ['x', 'a', 'b', 'c'], {context: 2})) + assert_equal("@@ -1,3 +1,4 @@\n+x\n a\n b\n c\n", + diff(['a', 'b', 'c'], ['x', 'a', 'b', 'c'], {context: 3})) + assert_equal("@@ -1,3 +1,4 @@\n+x\n a\n b\n c\n", + diff(['a', 'b', 'c'], ['x', 'a', 'b', 'c'], {context: 4})) + assert_equal("@@ -0,0 +1 @@\n+x\n", + diff(['a', 'b', 'c'], ['x', 'a', 'b', 'c'], {context: -1})) + + # Error cases + assert_fails('call diff({}, ["a"])', 'E1211:') + assert_fails('call diff(["a"], {})', 'E1211:') + assert_fails('call diff(["a"], ["a"], [])', 'E1206:') + assert_fails('call diff(["a"], ["a"], {output: "xyz"})', 'E106: Unsupported diff output format: xyz') + assert_fails('call diff(["a"], ["a"], {context: []})', 'E745: Using a List as a Number') +enddef + +" Test for using the diff() function with 'diffexpr' +func Test_diffexpr_with_diff_func() + CheckScreendump + + let lines =<< trim END + def DiffFuncExpr() + var in: list<string> = readfile(v:fname_in) + var new: list<string> = readfile(v:fname_new) + var out: string = diff(in, new) + writefile(split(out, "\n"), v:fname_out) + enddef + set diffexpr=DiffFuncExpr() + + edit Xdifffunc1.txt + diffthis + vert split Xdifffunc2.txt + diffthis + END + call writefile(lines, 'XsetupDiffFunc.mnv', 'D') + + call writefile(['zero', 'one', 'two', 'three'], 'Xdifffunc1.txt', 'D') + call writefile(['one', 'twox', 'three', 'four'], 'Xdifffunc2.txt', 'D') + + let buf = RunMNVInTerminal('-S XsetupDiffFunc.mnv', {'rows': 12}) + call VerifyScreenDump(buf, 'Test_difffunc_diffexpr_1', {}) + call StopMNVInTerminal(buf) +endfunc + +func Test_diff_toggle_wrap_skipcol_leftcol() + 61vnew + call setline(1, 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.') + 30vnew + call setline(1, 'ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.') + let win1 = win_getid() + setlocal smoothscroll + exe "normal! $\<C-E>" + wincmd l + let win2 = win_getid() + setlocal smoothscroll + exe "normal! $\<C-E>" + call assert_equal([ + \ '<<<sadipscing elitr, sed diam |<<<tetur sadipscing elitr, sed|', + \ 'nonumy eirmod tempor invidunt | diam nonumy eirmod tempor inv|', + \ 'ut labore et dolore magna aliq|idunt ut labore et dolore magn|', + \ 'uyam erat, sed diam voluptua. |a aliquyam erat, sed diam volu|', + \ '~ |ptua. |', + \ ], ScreenLines([1, 5], 62)) + call assert_equal({'col': 29, 'row': 4, 'endcol': 29, 'curscol': 29}, + \ screenpos(win1, line('.', win1), col('.', win1))) + call assert_equal({'col': 36, 'row': 5, 'endcol': 36, 'curscol': 36}, + \ screenpos(win2, line('.', win2), col('.', win2))) + + wincmd h + diffthis + wincmd l + diffthis + normal! 0 + call assert_equal([ + \ ' ipsum dolor sit amet, conset| Lorem ipsum dolor sit amet, |', + \ '~ |~ |', + \ ], ScreenLines([1, 2], 62)) + call assert_equal({'col': 3, 'row': 1, 'endcol': 3, 'curscol': 3}, + \ screenpos(win1, line('.', win1), col('.', win1))) + call assert_equal({'col': 34, 'row': 1, 'endcol': 34, 'curscol': 34}, + \ screenpos(win2, line('.', win2), col('.', win2))) + + normal! $ + call assert_equal([ + \ ' voluptua. | diam voluptua. |', + \ '~ |~ |', + \ ], ScreenLines([1, 2], 62)) + call assert_equal({'col': 11, 'row': 1, 'endcol': 11, 'curscol': 11}, + \ screenpos(win1, line('.', win1), col('.', win1))) + call assert_equal({'col': 48, 'row': 1, 'endcol': 48, 'curscol': 48}, + \ screenpos(win2, line('.', win2), col('.', win2))) + + diffoff! + call assert_equal([ + \ 'ipsum dolor sit amet, consetet|Lorem ipsum dolor sit amet, co|', + \ 'ur sadipscing elitr, sed diam |nsetetur sadipscing elitr, sed|', + \ 'nonumy eirmod tempor invidunt | diam nonumy eirmod tempor inv|', + \ 'ut labore et dolore magna aliq|idunt ut labore et dolore magn|', + \ 'uyam erat, sed diam voluptua. |a aliquyam erat, sed diam volu|', + \ '~ |ptua. |', + \ ], ScreenLines([1, 6], 62)) + call assert_equal({'col': 29, 'row': 5, 'endcol': 29, 'curscol': 29}, + \ screenpos(win1, line('.', win1), col('.', win1))) + call assert_equal({'col': 36, 'row': 6, 'endcol': 36, 'curscol': 36}, + \ screenpos(win2, line('.', win2), col('.', win2))) + + bwipe! + bwipe! +endfunc + +" Ctrl-D reveals filler lines below the last line in the buffer. +func Test_diff_eob_halfpage() + new + call setline(1, ['']->repeat(10) + ['a']) + diffthis + new + call setline(1, ['']->repeat(3) + ['a', 'b']) + diffthis + resize 5 + wincmd j + resize 5 + norm G + call assert_equal(7, line('w0')) + exe "norm! \<C-D>" + call assert_equal(8, line('w0')) + + %bwipe! +endfunc + +func Test_diff_overlapped_diff_blocks_will_be_merged() + CheckScreendump + + let lines =<< trim END + func DiffExprStub() + let txt_in = readfile(v:fname_in) + let txt_new = readfile(v:fname_new) + if txt_in == ["line1"] && txt_new == ["line2"] + call writefile(["1c1"], v:fname_out) + elseif txt_in == readfile("Xdiin1") && txt_new == readfile("Xdinew1") + call writefile(readfile("Xdiout1"), v:fname_out) + elseif txt_in == readfile("Xdiin2") && txt_new == readfile("Xdinew2") + call writefile(readfile("Xdiout2"), v:fname_out) + endif + endfunc + END + call writefile(lines, 'XdiffSetup', 'D') + + call WriteDiffFiles(0, [], []) + let buf = RunMNVInTerminal('-d -S XdiffSetup Xdifile1 Xdifile2', {}) + call term_sendkeys(buf, ":set autoread\<CR>\<c-w>w:set autoread\<CR>\<c-w>w") + + call WriteDiffFiles(buf, ["a", "b"], ["x", "x"]) + call writefile(["a", "b"], "Xdiin1") + call writefile(["x", "x"], "Xdinew1") + call writefile(["1c1", "2c2"], "Xdiout1") + call term_sendkeys(buf, ":set diffexpr=DiffExprStub()\<CR>:") + call VerifyBoth(buf, "Test_diff_overlapped_2.01", "") + call term_sendkeys(buf, ":set diffexpr&\<CR>:") + + call WriteDiffFiles(buf, ["a", "b", "c"], ["x", "c"]) + call writefile(["a", "b", "c"], "Xdiin1") + call writefile(["x", "c"], "Xdinew1") + call writefile(["1c1", "2d1"], "Xdiout1") + call term_sendkeys(buf, ":set diffexpr=DiffExprStub()\<CR>:") + call VerifyBoth(buf, "Test_diff_overlapped_2.02", "") + call term_sendkeys(buf, ":set diffexpr&\<CR>:") + + call WriteDiffFiles(buf, ["a", "c"], ["x", "x", "c"]) + call writefile(["a", "c"], "Xdiin1") + call writefile(["x", "x", "c"], "Xdinew1") + call writefile(["1c1", "1a2"], "Xdiout1") + call term_sendkeys(buf, ":set diffexpr=DiffExprStub()\<CR>:") + call VerifyBoth(buf, "Test_diff_overlapped_2.03", "") + call term_sendkeys(buf, ":set diffexpr&\<CR>:") + + call StopMNVInTerminal(buf) + wincmd c + + call WriteDiffFiles3(0, [], [], []) + let buf = RunMNVInTerminal('-d -S XdiffSetup Xdifile1 Xdifile2 Xdifile3', {}) + call term_sendkeys(buf, ":set autoread\<CR>\<c-w>w:set autoread\<CR>\<c-w>w:set autoread\<CR>\<c-w>w") + + call WriteDiffFiles3(buf, ["a", "b", "c"], ["a", "x", "c"], ["y", "b", "c"]) + call VerifyBoth(buf, "Test_diff_overlapped_3.01", "") + + call WriteDiffFiles3(buf, ["a", "b", "c"], ["a", "x", "c"], ["a", "y", "c"]) + call VerifyBoth(buf, "Test_diff_overlapped_3.02", "") + + call WriteDiffFiles3(buf, ["a", "b", "c"], ["a", "x", "c"], ["a", "b", "y"]) + call VerifyBoth(buf, "Test_diff_overlapped_3.03", "") + + call WriteDiffFiles3(buf, ["a", "b", "c"], ["a", "x", "c"], ["y", "y", "c"]) + call VerifyBoth(buf, "Test_diff_overlapped_3.04", "") + + call WriteDiffFiles3(buf, ["a", "b", "c"], ["a", "x", "c"], ["a", "y", "y"]) + call VerifyBoth(buf, "Test_diff_overlapped_3.05", "") + + call WriteDiffFiles3(buf, ["a", "b", "c"], ["a", "x", "c"], ["y", "y", "y"]) + call VerifyBoth(buf, "Test_diff_overlapped_3.06", "") + + call WriteDiffFiles3(buf, ["a", "b", "c"], ["a", "x", "x"], ["y", "y", "c"]) + call VerifyBoth(buf, "Test_diff_overlapped_3.07", "") + + call WriteDiffFiles3(buf, ["a", "b", "c"], ["x", "x", "c"], ["a", "y", "y"]) + call VerifyBoth(buf, "Test_diff_overlapped_3.08", "") + + call WriteDiffFiles3(buf, ["a", "b", "c", "d", "e"], ["a", "x", "c", "x", "e"], ["y", "y", "y", "d", "e"]) + call VerifyBoth(buf, "Test_diff_overlapped_3.09", "") + + call WriteDiffFiles3(buf, ["a", "b", "c", "d", "e"], ["a", "x", "c", "x", "e"], ["y", "y", "y", "y", "e"]) + call VerifyBoth(buf, "Test_diff_overlapped_3.10", "") + + call WriteDiffFiles3(buf, ["a", "b", "c", "d", "e"], ["a", "x", "c", "x", "e"], ["y", "y", "y", "y", "y"]) + call VerifyBoth(buf, "Test_diff_overlapped_3.11", "") + + call WriteDiffFiles3(buf, ["a", "b", "c", "d", "e"], ["a", "x", "c", "x", "e"], ["a", "y", "y", "d", "e"]) + call VerifyBoth(buf, "Test_diff_overlapped_3.12", "") + + call WriteDiffFiles3(buf, ["a", "b", "c", "d", "e"], ["a", "x", "c", "x", "e"], ["a", "y", "y", "y", "e"]) + call VerifyBoth(buf, "Test_diff_overlapped_3.13", "") + + call WriteDiffFiles3(buf, ["a", "b", "c", "d", "e"], ["a", "x", "c", "x", "e"], ["a", "y", "y", "y", "y"]) + call VerifyBoth(buf, "Test_diff_overlapped_3.14", "") + + call WriteDiffFiles3(buf, ["a", "b", "c", "d", "e"], ["a", "x", "c", "x", "e"], ["a", "b", "y", "d", "e"]) + call VerifyBoth(buf, "Test_diff_overlapped_3.15", "") + + call WriteDiffFiles3(buf, ["a", "b", "c", "d", "e"], ["a", "x", "c", "x", "e"], ["a", "b", "y", "y", "e"]) + call VerifyBoth(buf, "Test_diff_overlapped_3.16", "") + + call WriteDiffFiles3(buf, ["a", "b", "c", "d", "e"], ["a", "x", "c", "x", "e"], ["a", "b", "y", "y", "y"]) + call VerifyBoth(buf, "Test_diff_overlapped_3.17", "") + + call WriteDiffFiles3(buf, ["a", "b"], ["x", "b"], ["y", "y"]) + call writefile(["a", "b"], "Xdiin1") + call writefile(["x", "b"], "Xdinew1") + call writefile(["1c1"], "Xdiout1") + call writefile(["a", "b"], "Xdiin2") + call writefile(["y", "y"], "Xdinew2") + call writefile(["1c1", "2c2"], "Xdiout2") + call term_sendkeys(buf, ":set diffexpr=DiffExprStub()\<CR>:") + call VerifyInternal(buf, "Test_diff_overlapped_3.18", "") + call term_sendkeys(buf, ":set diffexpr&\<CR>:") + + call WriteDiffFiles3(buf, ["a", "b", "c", "d"], ["x", "b", "x", "d"], ["y", "y", "c", "d"]) + call writefile(["a", "b", "c", "d"], "Xdiin1") + call writefile(["x", "b", "x", "d"], "Xdinew1") + call writefile(["1c1", "3c3"], "Xdiout1") + call writefile(["a", "b", "c", "d"], "Xdiin2") + call writefile(["y", "y", "c", "d"], "Xdinew2") + call writefile(["1c1", "2c2"], "Xdiout2") + call term_sendkeys(buf, ":set diffexpr=DiffExprStub()\<CR>:") + call VerifyInternal(buf, "Test_diff_overlapped_3.19", "") + call term_sendkeys(buf, ":set diffexpr&\<CR>:") + + call WriteDiffFiles3(buf, ["a", "b", "c", "d"], ["x", "b", "x", "d"], ["y", "y", "y", "d"]) + call writefile(["a", "b", "c", "d"], "Xdiin1") + call writefile(["x", "b", "x", "d"], "Xdinew1") + call writefile(["1c1", "3c3"], "Xdiout1") + call writefile(["a", "b", "c", "d"], "Xdiin2") + call writefile(["y", "y", "y", "d"], "Xdinew2") + call writefile(["1c1", "2,3c2,3"], "Xdiout2") + call term_sendkeys(buf, ":set diffexpr=DiffExprStub()\<CR>:") + call VerifyInternal(buf, "Test_diff_overlapped_3.20", "") + call term_sendkeys(buf, ":set diffexpr&\<CR>:") + + call WriteDiffFiles3(buf, ["a", "b", "c", "d"], ["x", "b", "x", "d"], ["y", "y", "y", "y"]) + call writefile(["a", "b", "c", "d"], "Xdiin1") + call writefile(["x", "b", "x", "d"], "Xdinew1") + call writefile(["1c1", "3c3"], "Xdiout1") + call writefile(["a", "b", "c", "d"], "Xdiin2") + call writefile(["y", "y", "y", "y"], "Xdinew2") + call writefile(["1c1", "2,4c2,4"], "Xdiout2") + call term_sendkeys(buf, ":set diffexpr=DiffExprStub()\<CR>:") + call VerifyInternal(buf, "Test_diff_overlapped_3.21", "") + call term_sendkeys(buf, ":set diffexpr&\<CR>:") + + call WriteDiffFiles3(buf, ["a", "b", "c"], ["a", "x", "c"], ["b", "c"]) + call VerifyBoth(buf, "Test_diff_overlapped_3.22", "") + + call WriteDiffFiles3(buf, ["a", "b", "c"], ["a", "x", "c"], ["c"]) + call VerifyBoth(buf, "Test_diff_overlapped_3.23", "") + + call WriteDiffFiles3(buf, ["a", "b", "c"], ["a", "x", "c"], []) + call VerifyBoth(buf, "Test_diff_overlapped_3.24", "") + + call WriteDiffFiles3(buf, ["a", "b", "c"], ["a", "x", "c"], ["a", "c"]) + call VerifyBoth(buf, "Test_diff_overlapped_3.25", "") + + call WriteDiffFiles3(buf, ["a", "b", "c"], ["a", "x", "c"], ["a"]) + call VerifyBoth(buf, "Test_diff_overlapped_3.26", "") + + call WriteDiffFiles3(buf, ["a", "b", "c"], ["a", "x", "c"], ["b"]) + call VerifyBoth(buf, "Test_diff_overlapped_3.27", "") + + call WriteDiffFiles3(buf, ["a", "b", "c", "d", "e"], ["a", "x", "c", "x", "e"], ["d", "e"]) + call VerifyBoth(buf, "Test_diff_overlapped_3.28", "") + + call WriteDiffFiles3(buf, ["a", "b", "c", "d", "e"], ["a", "x", "c", "x", "e"], ["e"]) + call VerifyBoth(buf, "Test_diff_overlapped_3.29", "") + + call WriteDiffFiles3(buf, ["a", "b", "c", "d", "e"], ["a", "x", "c", "x", "e"], ["a", "d", "e"]) + call VerifyBoth(buf, "Test_diff_overlapped_3.30", "") + + call WriteDiffFiles3(buf, ["a", "b", "c", "d", "e"], ["a", "x", "c", "x", "e"], ["a", "e"]) + call VerifyBoth(buf, "Test_diff_overlapped_3.31", "") + + call WriteDiffFiles3(buf, ["a", "b", "c", "d", "e"], ["a", "x", "c", "x", "e"], ["a"]) + call VerifyBoth(buf, "Test_diff_overlapped_3.32", "") + + call WriteDiffFiles3(buf, ["a", "b", "c", "d", "e"], ["a", "x", "c", "x", "e"], ["a", "b", "d", "e"]) + call VerifyBoth(buf, "Test_diff_overlapped_3.33", "") + + call WriteDiffFiles3(buf, ["a", "b", "c", "d", "e"], ["a", "x", "c", "x", "e"], ["a", "b", "e"]) + call VerifyBoth(buf, "Test_diff_overlapped_3.34", "") + + call WriteDiffFiles3(buf, ["a", "b", "c", "d", "e"], ["a", "x", "c", "x", "e"], ["a", "b"]) + call VerifyBoth(buf, "Test_diff_overlapped_3.35", "") + + call WriteDiffFiles3(buf, ["a", "b", "c"], ["a", "x", "c"], ["a", "y", "b", "c"]) + call VerifyBoth(buf, "Test_diff_overlapped_3.36", "") + + call WriteDiffFiles3(buf, ["a", "b", "c"], ["a", "x", "c"], ["a", "b", "y", "c"]) + call VerifyBoth(buf, "Test_diff_overlapped_3.37", "") + + call WriteDiffFiles3(buf, ["a", "b", "c"], ["d", "e"], ["b", "f"]) + call VerifyBoth(buf, "Test_diff_overlapped_3.38", "") + + 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 StopMNVInTerminal(buf) +endfunc + +" switching windows in diff mode caused an unnecessary scroll +func Test_diff_topline_noscroll() + CheckScreendump + + let content =<< trim END + call setline(1, range(1,60)) + vnew + call setline(1, range(1,10) + range(50,60)) + windo diffthis + norm! G + exe "norm! 30\<C-y>" + END + call writefile(content, 'Xcontent', 'D') + let buf = RunMNVInTerminal('-S Xcontent', {'rows': 20}) + call VerifyScreenDump(buf, 'Test_diff_topline_1', {}) + call term_sendkeys(buf, ":echo line('w0', 1001)\<cr>") + call term_wait(buf) + call VerifyScreenDump(buf, 'Test_diff_topline_2', {}) + call term_sendkeys(buf, "\<C-W>p") + call term_wait(buf) + call VerifyScreenDump(buf, 'Test_diff_topline_3', {}) + call term_sendkeys(buf, "\<C-W>p") + call term_wait(buf) + call VerifyScreenDump(buf, 'Test_diff_topline_4', {}) + call StopMNVInTerminal(buf) +endfunc + +" Test inline highlighting which shows what's different within each diff block +func Test_diff_inline() + CheckScreendump + + call WriteDiffFiles(0, [], []) + let buf = RunMNVInTerminal('-d Xdifile1 Xdifile2', {}) + call term_sendkeys(buf, ":set autoread\<CR>\<c-w>w:set autoread\<CR>\<c-w>w") + + call WriteDiffFiles(buf, ["abcdef ghi jk n", "x", "y"], ["aBcef gHi lm n", "y", "z"]) + call VerifyInternal(buf, "Test_diff_inline_01", "") + call VerifyInternal(buf, "Test_diff_inline_02", " diffopt+=inline:none") + + " inline:simple is the same as default + call VerifyInternal(buf, "Test_diff_inline_01", " diffopt+=inline:simple") + + call VerifyInternal(buf, "Test_diff_inline_03", " diffopt+=inline:char") + call VerifyInternal(buf, "Test_diff_inline_04", " diffopt+=inline:word") + + " multiple inline values will the last one + call VerifyInternal(buf, "Test_diff_inline_01", " diffopt+=inline:none,inline:char,inline:simple") + call VerifyInternal(buf, "Test_diff_inline_02", " diffopt+=inline:simple,inline:word,inline:none") + call VerifyInternal(buf, "Test_diff_inline_03", " diffopt+=inline:simple,inline:word,inline:char") + + " DiffTextAdd highlight + call term_sendkeys(buf, ":hi DiffTextAdd ctermbg=blue\<CR>") + call VerifyInternal(buf, "Test_diff_inline_05", " diffopt+=inline:char") + + " Live update in insert mode + call term_sendkeys(buf, "\<Esc>isometext") + call VerifyScreenDump(buf, "Test_diff_inline_06", {}) + call term_sendkeys(buf, "\<Esc>u") + + " icase simple scenarios + call VerifyInternal(buf, "Test_diff_inline_07", " diffopt+=inline:simple,icase") + call VerifyInternal(buf, "Test_diff_inline_08", " diffopt+=inline:char,icase") + call VerifyInternal(buf, "Test_diff_inline_09", " diffopt+=inline:word,icase") + + " diff algorithms should affect highlight + call WriteDiffFiles(buf, ["apples and oranges"], ["oranges and apples"]) + call VerifyInternal(buf, "Test_diff_inline_10", " diffopt+=inline:char") + call VerifyInternal(buf, "Test_diff_inline_11", " diffopt+=inline:char,algorithm:patience") + + " icase: composing chars and Unicode fold case edge cases + call WriteDiffFiles(buf, + \ ["1 - sigma in 6σ and Ὀδυσσεύς", "1 - angstrom in åå", "1 - composing: ii⃗I⃗"], + \ ["2 - Sigma in 6Σ and ὈΔΥΣΣΕΎΣ", "2 - Angstrom in ÅÅ", "2 - Composing: i⃗I⃗I⃗"]) + call VerifyInternal(buf, "Test_diff_inline_12", " diffopt+=inline:char") + call VerifyInternal(buf, "Test_diff_inline_13", " diffopt+=inline:char,icase") + + " wide chars + call WriteDiffFiles(buf, ["abc😅xde一", "f🚀g"], ["abcy😢de", "二f🚀g"]) + call VerifyInternal(buf, "Test_diff_inline_14", " diffopt+=inline:char,icase") + + " NUL char (\n below is internally substituted as NUL) + call WriteDiffFiles(buf, ["1\n34\n5\n6"], ["1234\n5", "6"]) + call VerifyInternal(buf, "Test_diff_inline_15", " diffopt+=inline:char") + + " word diff: always use first buffer's iskeyword and ignore others' for consistency + call WriteDiffFiles(buf, ["foo+bar test"], ["foo+baz test"]) + call VerifyInternal(buf, "Test_diff_inline_word_01", " diffopt+=inline:word") + + call term_sendkeys(buf, ":set iskeyword+=+\<CR>:diffupdate\<CR>") + call VerifyInternal(buf, "Test_diff_inline_word_02", " diffopt+=inline:word") + + call term_sendkeys(buf, ":set iskeyword&\<CR>:wincmd w\<CR>") + call term_sendkeys(buf, ":set iskeyword+=+\<CR>:wincmd w\<CR>:diffupdate\<CR>") + " Use the previous screen dump as 2nd buffer's iskeyword does not matter + call VerifyInternal(buf, "Test_diff_inline_word_01", " diffopt+=inline:word") + + call term_sendkeys(buf, ":windo set iskeyword&\<CR>:1wincmd w\<CR>") + + " word diff: test handling of multi-byte characters. Only alphanumeric chars + " (e.g. Greek alphabet, but not CJK/emoji) count as words. + call WriteDiffFiles(buf, ["🚀⛵️一二三ひらがなΔέλτα Δelta foobar"], ["🚀🛸一二四ひらなδέλτα δelta foobar"]) + call VerifyInternal(buf, "Test_diff_inline_word_03", " diffopt+=inline:word") + + " char diff: should slide highlight to whitespace boundary if possible for + " better readability (by using forced indent-heuristics). A wrong result + " would be if the highlight is "Bar, prefix". It should be "prefixBar, " + " instead. + call WriteDiffFiles(buf, ["prefixFoo, prefixEnd"], ["prefixFoo, prefixBar, prefixEnd"]) + call VerifyInternal(buf, "Test_diff_inline_char_01", " diffopt+=inline:char") + + " char diff: small gaps between inline diff blocks will be merged during refine step + " - first segment: test that we iteratively merge small gaps after we merged + " adjacent blocks, but only with limited number (set to 4) of iterations. + " - second and third segments: show that we need a large enough adjacent block to + " trigger a merge. + " - fourth segment: small gaps are not merged when adjacent large block is + " on a different line. + call WriteDiffFiles(buf, + \ ["abcdefghijklmno", "anchor1", + \ "abcdefghijklmno", "anchor2", + \ "abcdefghijklmno", "anchor3", + \ "test", "multiline"], + \ ["a?c?e?g?i?k???o", "anchor1", + \ "a??de?????klmno", "anchor2", + \ "a??de??????lmno", "anchor3", + \ "t?s?", "??????i?e"]) + call VerifyInternal(buf, "Test_diff_inline_char_02", " diffopt+=inline:char") + + " Test multi-line blocks and whitespace + call WriteDiffFiles(buf, + \ ["this is ", "sometest text foo", "baz abc def ", "one", "word another word", "additional line"], + \ ["this is some test", "texts", "foo bar abX Yef ", "oneword another word"]) + call VerifyInternal(buf, "Test_diff_inline_multiline_01", " diffopt+=inline:char,iwhite") + call VerifyInternal(buf, "Test_diff_inline_multiline_02", " diffopt+=inline:word,iwhite") + call VerifyInternal(buf, "Test_diff_inline_multiline_03", " diffopt+=inline:char,iwhiteeol") + call VerifyInternal(buf, "Test_diff_inline_multiline_04", " diffopt+=inline:word,iwhiteeol") + call VerifyInternal(buf, "Test_diff_inline_multiline_05", " diffopt+=inline:char,iwhiteall") + call VerifyInternal(buf, "Test_diff_inline_multiline_06", " diffopt+=inline:word,iwhiteall") + + " newline should be highlighted too when 'list' is set + call term_sendkeys(buf, ":windo set list\<CR>") + call VerifyInternal(buf, "Test_diff_inline_multiline_07", " diffopt+=inline:char") + call VerifyInternal(buf, "Test_diff_inline_multiline_08", " diffopt+=inline:char,iwhite") + call VerifyInternal(buf, "Test_diff_inline_multiline_09", " diffopt+=inline:char,iwhiteeol") + call VerifyInternal(buf, "Test_diff_inline_multiline_10", " diffopt+=inline:char,iwhiteall") + call term_sendkeys(buf, ":windo set nolist\<CR>") + + call StopMNVInTerminal(buf) +endfunc + +func Test_diff_inline_multibuffer() + CheckScreendump + + call WriteDiffFiles3(0, [], [], []) + let buf = RunMNVInTerminal('-d Xdifile1 Xdifile2 Xdifile3', {}) + call term_sendkeys(buf, ":windo set autoread\<CR>:1wincmd w\<CR>") + call term_sendkeys(buf, ":hi DiffTextAdd ctermbg=blue\<CR>") + + call WriteDiffFiles3(buf, + \ ["That is buffer1.", "anchor", "Some random text", "anchor"], + \ ["This is buffer2.", "anchor", "Some text", "anchor", "buffer2/3"], + \ ["This is buffer3. Last.", "anchor", "Some more", "text here.", "anchor", "only in buffer2/3", "not in buffer1"]) + call VerifyInternal(buf, "Test_diff_inline_multibuffer_01", " diffopt+=inline:char") + + " Close one of the buffers and make sure it updates correctly + call term_sendkeys(buf, ":diffoff\<CR>") + call VerifyInternal(buf, "Test_diff_inline_multibuffer_02", " diffopt+=inline:char") + + " Update text in the non-diff buffer and nothing should be changed + call term_sendkeys(buf, "\<Esc>isometext") + call VerifyScreenDump(buf, "Test_diff_inline_multibuffer_03", {}) + call term_sendkeys(buf, "\<Esc>u") + + call term_sendkeys(buf, ":diffthis\<CR>") + call VerifyInternal(buf, "Test_diff_inline_multibuffer_01", " diffopt+=inline:char") + + " Test that removing first buffer from diff will in turn use the next + " earliest buffer's iskeyword during word diff. + call WriteDiffFiles3(buf, + \ ["This+is=a-setence"], + \ ["This+is=another-setence"], + \ ["That+is=a-setence"]) + call term_sendkeys(buf, ":set iskeyword+=+\<CR>:2wincmd w\<CR>:set iskeyword+=-\<CR>:1wincmd w\<CR>") + call VerifyInternal(buf, "Test_diff_inline_multibuffer_04", " diffopt+=inline:word") + call term_sendkeys(buf, ":diffoff\<CR>") + call VerifyInternal(buf, "Test_diff_inline_multibuffer_05", " diffopt+=inline:word") + call term_sendkeys(buf, ":diffthis\<CR>") + call VerifyInternal(buf, "Test_diff_inline_multibuffer_04", " diffopt+=inline:word") + + " Test multi-buffer char diff refinement, and that removing a buffer from + " diff will update the others properly. + call WriteDiffFiles3(buf, + \ ["abcdefghijkYmYYY"], + \ ["aXXdXXghijklmnop"], + \ ["abcdefghijkYmYop"]) + call VerifyInternal(buf, "Test_diff_inline_multibuffer_06", " diffopt+=inline:char") + call term_sendkeys(buf, ":diffoff\<CR>") + call VerifyInternal(buf, "Test_diff_inline_multibuffer_07", " diffopt+=inline:char") + call term_sendkeys(buf, ":diffthis\<CR>") + call VerifyInternal(buf, "Test_diff_inline_multibuffer_06", " diffopt+=inline:char") + + call StopMNVInTerminal(buf) +endfunc + +" Test that when using multi-buffer diff, an empty block would be correctly +" skipped in the result, without resulting in invalid states or crashes. +func Test_diff_inline_multibuffer_empty_block() + CheckScreendump + + call writefile(['anchor1', '1234567890abcde', 'anchor2'], 'Xdifile1') + call writefile(['anchor1', '1234567--0abc-e', 'anchor2'], 'Xdifile2') + call writefile(['anchor1', 'anchor2'], 'Xdifile3') + call writefile(['anchor1', '1???????90abcd?', 'anchor2'], 'Xdifile4') + + let buf = RunMNVInTerminal('-d Xdifile1 Xdifile2 Xdifile3 Xdifile4', {}) + call VerifyInternal(buf, "Test_diff_inline_multibuffer_empty_block_01", " diffopt+=inline:char") + + call StopMNVInTerminal(buf) +endfunc + +func Test_diffget_diffput_linematch() + CheckScreendump + call delete('.Xdifile1.swp') + call delete('.Xdifile2.swp') + call WriteDiffFiles(0, [], []) + let buf = RunMNVInTerminal('-d Xdifile1 Xdifile2', {}) + call term_sendkeys(buf, ":set autoread\<CR>\<c-w>w:set autoread\<CR>\<c-w>w") + + " enable linematch + call term_sendkeys(buf, ":set diffopt+=linematch:30\<CR>") + call WriteDiffFiles(buf, ['', + \ 'common line', + \ 'common line', + \ '', + \ 'ABCabc', + \ 'ABCabc', + \ 'ABCabc', + \ 'ABCabc', + \ 'common line', + \ 'common line', + \ 'common line', + \ 'something' ], + \ ['', + \ 'common line', + \ 'common line', + \ '', + \ 'DEFabc', + \ 'xyz', + \ 'xyz', + \ 'xyz', + \ 'DEFabc', + \ 'DEFabc', + \ 'DEFabc', + \ 'common line', + \ 'common line', + \ 'DEF', + \ 'common line', + \ 'DEF', + \ 'something']) + call VerifyScreenDump(buf, 'Test_diff_get_put_linematch_1', {}) + + " get from window 1 from line 5 to 9 + call term_sendkeys(buf, "1\<c-w>w") + call term_sendkeys(buf, ":5,9diffget\<CR>") + call VerifyScreenDump(buf, 'Test_diff_get_put_linematch_2', {}) + + " undo the last diffget + call term_sendkeys(buf, "u") + + " get from window 2 from line 5 to 10 + call term_sendkeys(buf, "2\<c-w>w") + call term_sendkeys(buf, ":5,10diffget\<CR>") + call VerifyScreenDump(buf, 'Test_diff_get_put_linematch_3', {}) + + " undo the last diffget + call term_sendkeys(buf, "u") + + " get all from window 2 + call term_sendkeys(buf, "2\<c-w>w") + call term_sendkeys(buf, ":4,17diffget\<CR>") + call VerifyScreenDump(buf, 'Test_diff_get_put_linematch_4', {}) + + " undo the last diffget + call term_sendkeys(buf, "u") + + " get all from window 1 + call term_sendkeys(buf, "1\<c-w>w") + call term_sendkeys(buf, ":4,12diffget\<CR>") + call VerifyScreenDump(buf, 'Test_diff_get_put_linematch_5', {}) + + " undo the last diffget + call term_sendkeys(buf, "u") + + " get from window 1 using do 1 line 5 + call term_sendkeys(buf, "1\<c-w>w") + call term_sendkeys(buf, "5gg") + call term_sendkeys(buf, ":diffget\<CR>") + call VerifyScreenDump(buf, 'Test_diff_get_put_linematch_6', {}) + + " undo the last diffget + call term_sendkeys(buf, "u") + + " get from window 1 using do 2 line 6 + call term_sendkeys(buf, "1\<c-w>w") + call term_sendkeys(buf, "6gg") + call term_sendkeys(buf, ":diffget\<CR>") + call VerifyScreenDump(buf, 'Test_diff_get_put_linematch_7', {}) + + " undo the last diffget + call term_sendkeys(buf, "u") + + " get from window 1 using do 2 line 7 + call term_sendkeys(buf, "1\<c-w>w") + call term_sendkeys(buf, "7gg") + call term_sendkeys(buf, ":diffget\<CR>") + call VerifyScreenDump(buf, 'Test_diff_get_put_linematch_8', {}) + + " undo the last diffget + call term_sendkeys(buf, "u") + + " get from window 1 using do 2 line 11 + call term_sendkeys(buf, "1\<c-w>w") + call term_sendkeys(buf, "11gg") + call term_sendkeys(buf, ":diffget\<CR>") + call VerifyScreenDump(buf, 'Test_diff_get_put_linematch_9', {}) + + " undo the last diffget + call term_sendkeys(buf, "u") + + " get from window 1 using do 2 line 12 + call term_sendkeys(buf, "1\<c-w>w") + call term_sendkeys(buf, "12gg") + call term_sendkeys(buf, ":diffget\<CR>") + call VerifyScreenDump(buf, 'Test_diff_get_put_linematch_10', {}) + + " undo the last diffget + call term_sendkeys(buf, "u") + + " put from window 1 using dp 1 line 5 + call term_sendkeys(buf, "1\<c-w>w") + call term_sendkeys(buf, "5gg") + call term_sendkeys(buf, ":diffput\<CR>") + call VerifyScreenDump(buf, 'Test_diff_get_put_linematch_11', {}) + + " undo the last diffput + call term_sendkeys(buf, "2\<c-w>w") + call term_sendkeys(buf, "u") + + " put from window 1 using dp 2 line 6 + call term_sendkeys(buf, "1\<c-w>w") + call term_sendkeys(buf, "6gg") + call term_sendkeys(buf, ":diffput\<CR>") + call VerifyScreenDump(buf, 'Test_diff_get_put_linematch_12', {}) + + " undo the last diffput + call term_sendkeys(buf, "2\<c-w>w") + call term_sendkeys(buf, "u") + + " put from window 1 using dp 2 line 7 + call term_sendkeys(buf, "1\<c-w>w") + call term_sendkeys(buf, "7gg") + call term_sendkeys(buf, ":diffput\<CR>") + call VerifyScreenDump(buf, 'Test_diff_get_put_linematch_13', {}) + + " undo the last diffput + call term_sendkeys(buf, "2\<c-w>w") + call term_sendkeys(buf, "u") + + " put from window 1 using dp 2 line 11 + call term_sendkeys(buf, "1\<c-w>w") + call term_sendkeys(buf, "11gg") + call term_sendkeys(buf, ":diffput\<CR>") + call VerifyScreenDump(buf, 'Test_diff_get_put_linematch_14', {}) + + " undo the last diffput + call term_sendkeys(buf, "2\<c-w>w") + call term_sendkeys(buf, "u") + + " put from window 1 using dp 2 line 12 + call term_sendkeys(buf, "1\<c-w>w") + call term_sendkeys(buf, "12gg") + call term_sendkeys(buf, ":diffput\<CR>") + call VerifyScreenDump(buf, 'Test_diff_get_put_linematch_15', {}) + + " undo the last diffput + call term_sendkeys(buf, "2\<c-w>w") + call term_sendkeys(buf, "u") + + " put from window 2 using dp line 6 + call term_sendkeys(buf, "2\<c-w>w") + call term_sendkeys(buf, "6gg") + call term_sendkeys(buf, ":diffput\<CR>") + call VerifyScreenDump(buf, 'Test_diff_get_put_linematch_16', {}) + + " undo the last diffput + call term_sendkeys(buf, "1\<c-w>w") + call term_sendkeys(buf, "u") + + " put from window 2 using dp line 8 + call term_sendkeys(buf, "2\<c-w>w") + call term_sendkeys(buf, "8gg") + call term_sendkeys(buf, ":diffput\<CR>") + call VerifyScreenDump(buf, 'Test_diff_get_put_linematch_17', {}) + + " undo the last diffput + call term_sendkeys(buf, "1\<c-w>w") + call term_sendkeys(buf, "u") + + " put from window 2 using dp line 9 + call term_sendkeys(buf, "2\<c-w>w") + call term_sendkeys(buf, "9gg") + call term_sendkeys(buf, ":diffput\<CR>") + call VerifyScreenDump(buf, 'Test_diff_get_put_linematch_18', {}) + + " undo the last diffput + call term_sendkeys(buf, "1\<c-w>w") + call term_sendkeys(buf, "u") + + " put from window 2 using dp line 17 + call term_sendkeys(buf, "2\<c-w>w") + call term_sendkeys(buf, "17gg") + call term_sendkeys(buf, ":diffput\<CR>") + call VerifyScreenDump(buf, 'Test_diff_get_put_linematch_19', {}) + " clean up + call StopMNVInTerminal(buf) +endfunc + +func Test_linematch_diff() + CheckScreendump + call delete('.Xdifile1.swp') + call delete('.Xdifile2.swp') + call WriteDiffFiles(0, [], []) + let buf = RunMNVInTerminal('-d Xdifile1 Xdifile2', {}) + call term_sendkeys(buf, ":set autoread\<CR>\<c-w>w:set autoread\<CR>\<c-w>w") + + " enable linematch + call term_sendkeys(buf, ":set diffopt=internal,filler,linematch:30\<CR>") + call WriteDiffFiles(buf, ['// abc d?', + \ '// d?', + \ '// d?' ], + \ ['!', + \ 'abc d!', + \ 'd!']) + call term_sendkeys(buf, ":\<CR>") " clear cmdline + call VerifyScreenDump(buf, 'Test_linematch_diff1', {}) + + " test that filler is always implicitly set by linematch + call term_sendkeys(buf, ":set diffopt-=filler\<CR>") + call term_sendkeys(buf, ":\<CR>") " clear cmdline + call VerifyScreenDump(buf, 'Test_linematch_diff1', {}) + + " clean up + call StopMNVInTerminal(buf) +endfunc + +func Test_linematch_diff_iwhite() + CheckScreendump + call delete('.Xdifile1.swp') + call delete('.Xdifile2.swp') + call WriteDiffFiles(0, [], []) + let buf = RunMNVInTerminal('-d Xdifile1 Xdifile2', {}) + call term_sendkeys(buf, ":set autoread\<CR>\<c-w>w:set autoread\<CR>\<c-w>w") + + " setup a diff with 2 files and set linematch:30, with ignore white + call term_sendkeys(buf, ":set diffopt=internal,filler,linematch:30\<CR>") + call WriteDiffFiles(buf, ['void testFunction () {', + \ ' for (int i = 0; i < 10; i++) {', + \ ' for (int j = 0; j < 10; j++) {', + \ ' }', + \ ' }', + \ '}' ], + \ ['void testFunction () {', + \ ' // for (int j = 0; j < 10; i++) {', + \ ' // }', + \ '}']) + call VerifyScreenDump(buf, 'Test_linematch_diff_iwhite1', {}) + call term_sendkeys(buf, ":set diffopt+=iwhiteall\<CR>") + call VerifyScreenDump(buf, 'Test_linematch_diff_iwhite2', {}) + " clean up + call StopMNVInTerminal(buf) +endfunc + +func Test_linematch_diff_grouping() + CheckScreendump + call delete('.Xdifile1.swp') + call delete('.Xdifile2.swp') + call WriteDiffFiles(0, [], []) + let buf = RunMNVInTerminal('-d Xdifile1 Xdifile2', {}) + call term_sendkeys(buf, ":set autoread\<CR>\<c-w>w:set autoread\<CR>\<c-w>w") + + " a diff that would result in multiple groups before grouping optimization + call term_sendkeys(buf, ":set diffopt=internal,filler,linematch:30\<CR>") + call WriteDiffFiles(buf, ['!A', + \ '!B', + \ '!C' ], + \ ['?Z', + \ '?A', + \ '?B', + \ '?C', + \ '?A', + \ '?B', + \ '?B', + \ '?C']) + call VerifyScreenDump(buf, 'Test_linematch_diff_grouping1', {}) + call WriteDiffFiles(buf, ['!A', + \ '!B', + \ '!C' ], + \ ['?A', + \ '?Z', + \ '?B', + \ '?C', + \ '?A', + \ '?B', + \ '?C', + \ '?C']) + call VerifyScreenDump(buf, 'Test_linematch_diff_grouping2', {}) + " clean up + call StopMNVInTerminal(buf) +endfunc + +func Test_linematch_diff_scroll() + CheckScreendump + call delete('.Xdifile1.swp') + call delete('.Xdifile2.swp') + call WriteDiffFiles(0, [], []) + let buf = RunMNVInTerminal('-d Xdifile1 Xdifile2', {}) + call term_sendkeys(buf, ":set autoread\<CR>\<c-w>w:set autoread\<CR>\<c-w>w") + + " a diff that would result in multiple groups before grouping optimization + call term_sendkeys(buf, ":set diffopt=internal,filler,linematch:30\<CR>") + call WriteDiffFiles(buf, ['!A', + \ '!B', + \ '!C' ], + \ ['?A', + \ '?Z', + \ '?B', + \ '?C', + \ '?A', + \ '?B', + \ '?C', + \ '?C']) + " scroll down to show calculation of top fill and scroll to correct line in + " both windows + call VerifyScreenDump(buf, 'Test_linematch_diff_grouping_scroll0', {}) + call term_sendkeys(buf, "3\<c-e>") + call VerifyScreenDump(buf, 'Test_linematch_diff_grouping_scroll1', {}) + call term_sendkeys(buf, "3\<c-e>") + call VerifyScreenDump(buf, 'Test_linematch_diff_grouping_scroll2', {}) + " clean up + call StopMNVInTerminal(buf) +endfunc + +func Test_linematch_line_limit_exceeded() + CheckScreendump + call delete('.Xdifile1.swp') + call delete('.Xdifile2.swp') + call WriteDiffFiles(0, [], []) + let buf = RunMNVInTerminal('-d Xdifile1 Xdifile2', {}) + call term_sendkeys(buf, ":set autoread\<CR>\<c-w>w:set autoread\<CR>\<c-w>w") + + call term_sendkeys(buf, ":set diffopt=internal,filler,linematch:10\<CR>") + " a diff block will not be aligned with linematch because it's contents + " exceed 10 lines + call WriteDiffFiles(buf, + \ ['common line', + \ 'HIL', + \ '', + \ 'aABCabc', + \ 'aABCabc', + \ 'aABCabc', + \ 'aABCabc', + \ 'common line', + \ 'HIL', + \ 'common line', + \ 'something'], + \ ['common line', + \ 'DEF', + \ 'GHI', + \ 'something', + \ '', + \ 'aDEFabc', + \ 'xyz', + \ 'xyz', + \ 'xyz', + \ 'aDEFabc', + \ 'aDEFabc', + \ 'aDEFabc', + \ 'common line', + \ 'DEF', + \ 'GHI', + \ 'something else', + \ 'common line', + \ 'something']) + call VerifyScreenDump(buf, 'Test_linematch_line_limit_exceeded1', {}) + " after increasing the count to 30, the limit is not exceeded, and the + " alignment algorithm will run on the largest diff block here + call term_sendkeys(buf, ":set diffopt+=linematch:30\<CR>") + call VerifyScreenDump(buf, 'Test_linematch_line_limit_exceeded2', {}) + " clean up + call StopMNVInTerminal(buf) +endfunc + +func Test_linematch_3diffs() + CheckScreendump + call delete('.Xdifile1.swp') + call delete('.Xdifile2.swp') + call delete('.Xdifile3.swp') + call WriteDiffFiles3(0, [], [], []) + let buf = RunMNVInTerminal('-d Xdifile1 Xdifile2 Xdifile3', {}) + call term_sendkeys(buf, "1\<c-w>w:set autoread\<CR>") + call term_sendkeys(buf, "2\<c-w>w:set autoread\<CR>") + call term_sendkeys(buf, "3\<c-w>w:set autoread\<CR>") + call term_sendkeys(buf, ":set diffopt=internal,filler,linematch:30\<CR>") + call WriteDiffFiles3(buf, + \ ["", + \ " common line", + \ " AAA", + \ " AAA", + \ " AAA"], + \ ["", + \ " common line", + \ " <<<<<<< HEAD", + \ " AAA", + \ " AAA", + \ " AAA", + \ " =======", + \ " BBB", + \ " BBB", + \ " BBB", + \ " >>>>>>> branch1"], + \ ["", + \ " common line", + \ " BBB", + \ " BBB", + \ " BBB"]) + call VerifyScreenDump(buf, 'Test_linematch_3diffs1', {}) + " clean up + call StopMNVInTerminal(buf) +endfunc + +" this used to access invalid memory +func Test_linematch_3diffs_sanity_check() + CheckScreendump + call delete('.Xfile_linematch1.swp') + call delete('.Xfile_linematch2.swp') + call delete('.Xfile_linematch3.swp') + let lines =<< trim END + set diffopt=internal,filler,linematch:60 + call feedkeys("Aq\<esc>") + call feedkeys("GAklm\<esc>") + call feedkeys("o") + END + call writefile(lines, 'Xlinematch_3diffs.mnv', 'D') + call writefile(['abcd', 'def', 'hij'], 'Xfile_linematch1', 'D') + call writefile(['defq', 'hijk', 'nopq'], 'Xfile_linematch2', 'D') + call writefile(['hijklm', 'nopqr', 'stuv'], 'Xfile_linematch3', 'D') + call WriteDiffFiles3(0, [], [], []) + let buf = RunMNVInTerminal('-d -S Xlinematch_3diffs.mnv Xfile_linematch1 Xfile_linematch2 Xfile_linematch3', {}) + call VerifyScreenDump(buf, 'Test_linematch_3diffs2', {}) + + " clean up + call StopMNVInTerminal(buf) +endfunc + +func Test_diffanchors() + CheckScreendump + call WriteDiffFiles3(0, + \ ["anchorA1", "1", "2", "3", + \ "100", "101", "102", "anchorB", "103", "104", "105"], + \ ["100", "101", "102", "anchorB", "103", "104", "105", + \ "anchorA2", "1", "2", "3"], + \ ["100", "anchorB", "103", + \ "anchorA3", "1", "2", "3"]) + let buf = RunMNVInTerminal('-d Xdifile1 Xdifile2 Xdifile3', {}) + + " Simple diff without any anchors + call VerifyInternal(buf, "Test_diff_anchors_00", "") + + " Setting diffopt+=anchor or diffanchors without the other won't do anything + call VerifyInternal(buf, "Test_diff_anchors_00", " diffopt+=anchor") + call VerifyInternal(buf, "Test_diff_anchors_00", " dia=1/anchorA/") + + " Use a single anchor by specifying a pattern. Test both internal and + " external diff to make sure both paths work. + call VerifyBoth(buf, "Test_diff_anchors_01", " dia=1/anchorA/ diffopt+=anchor") + + " Use 2 anchors. They should be sorted by line number, so in file 2/3 + " anchorB is used before anchorA. + call VerifyBoth(buf, "Test_diff_anchors_02", " dia=1/anchorA/,1/anchorB/ diffopt+=anchor") + + " Set marks and specify addresses using marks and repeat the test + call term_sendkeys(buf, ":2wincmd w\<CR>:1/anchorA/mark a\<CR>") + call term_sendkeys(buf, ":1/anchorB/mark b\<CR>") + call term_sendkeys(buf, ":3wincmd w\<CR>:1/anchorA/mark a\<CR>") + call term_sendkeys(buf, ":1/anchorB/mark b\<CR>") + call term_sendkeys(buf, ":1wincmd w\<CR>:1/anchorA/mark a\<CR>") + call term_sendkeys(buf, ":1/anchorB/mark b\<CR>") + + call VerifyInternal(buf, "Test_diff_anchors_01", " dia='a diffopt+=anchor") + call VerifyInternal(buf, "Test_diff_anchors_02", " dia='a,'b diffopt+=anchor") + + " Update marks to point somewhere else. When we first set the mark the diff + " won't be updated until we manually invoke :diffupdate. + call VerifyInternal(buf, "Test_diff_anchors_01", " dia='a diffopt+=anchor") + call term_sendkeys(buf, ":1wincmd w\<CR>:1/anchorB/mark a\<CR>:") + call term_wait(buf) + call VerifyScreenDump(buf, "Test_diff_anchors_01", {}) + call term_sendkeys(buf, ":diffupdate\<CR>:") + call term_wait(buf) + call VerifyScreenDump(buf, "Test_diff_anchors_03", {}) + + " Use local diff anchors with line numbers, and repeat the same test + call term_sendkeys(buf, ":2wincmd w\<CR>:setlocal dia=8\<CR>") + call term_sendkeys(buf, ":3wincmd w\<CR>:setlocal dia=4\<CR>") + call term_sendkeys(buf, ":1wincmd w\<CR>:setlocal dia=1\<CR>") + call VerifyInternal(buf, "Test_diff_anchors_01", " diffopt+=anchor") + call term_sendkeys(buf, ":2wincmd w\<CR>:setlocal dia=8,4\<CR>") + call term_sendkeys(buf, ":3wincmd w\<CR>:setlocal dia=4,2\<CR>") + call term_sendkeys(buf, ":1wincmd w\<CR>:setlocal dia=1,8\<CR>") + call VerifyInternal(buf, "Test_diff_anchors_02", " diffopt+=anchor") + + " Test multiple diff anchors on the same line in file 1. + call term_sendkeys(buf, ":1wincmd w\<CR>:setlocal dia=1,1\<CR>") + call VerifyInternal(buf, "Test_diff_anchors_04", " diffopt+=anchor") + + " Test that if one file has fewer diff anchors than others. MNV should only + " use the minimum in this case. + call term_sendkeys(buf, ":1wincmd w\<CR>:setlocal dia=8\<CR>") + call VerifyInternal(buf, "Test_diff_anchors_05", " diffopt+=anchor") + + " $+1 should anchor everything past the last line + call term_sendkeys(buf, ":1wincmd w\<CR>:setlocal dia=$+1\<CR>") + call VerifyInternal(buf, "Test_diff_anchors_06", " diffopt+=anchor") + + " Sorting of diff anchors should work with multiple anchors + call term_sendkeys(buf, ":1wincmd w\<CR>:setlocal dia=1,10,8,2\<CR>") + call term_sendkeys(buf, ":2wincmd w\<CR>:setlocal dia=1,2,3,4\<CR>") + call term_sendkeys(buf, ":3wincmd w\<CR>:setlocal dia=4,3,2,1\<CR>") + call VerifyInternal(buf, "Test_diff_anchors_07", " diffopt+=anchor") + + " Intentionally set an invalid anchor with wrong line number. Should fall + " back to treat it as if no anchors are used at all. + call term_sendkeys(buf, ":1wincmd w\<CR>:setlocal dia=1,10,8,2,1000 | silent! diffupdate\<CR>:") + call VerifyScreenDump(buf, "Test_diff_anchors_00", {}) + + call StopMNVInTerminal(buf) +endfunc + +" Test that scrollbind and topline calculations work correctly, even when diff +" anchors create adjacent diff blocks which complicates the calculations. +func Test_diffanchors_scrollbind_topline() + CheckScreendump + + " Simple overlapped line anchored to be adjacent to each other + call WriteDiffFiles(0, + \ ["anchor1", "diff1a", "anchor2"], + \ ["anchor1", "diff2a", "anchor2"]) + let buf = RunMNVInTerminal('-d Xdifile1 Xdifile2', {}) + + call term_sendkeys(buf, ":1wincmd w\<CR>:setlocal dia=2\<CR>") + call term_sendkeys(buf, ":2wincmd w\<CR>:setlocal dia=3\<CR>") + + call VerifyInternal(buf, "Test_diff_anchors_scrollbind_topline_01", " diffopt+=anchor") + call term_sendkeys(buf, "\<Esc>\<C-E>") + call VerifyScreenDump(buf, "Test_diff_anchors_scrollbind_topline_02", {}) + call term_sendkeys(buf, "\<C-E>") + call VerifyScreenDump(buf, "Test_diff_anchors_scrollbind_topline_03", {}) + call term_sendkeys(buf, "\<C-E>") + call VerifyScreenDump(buf, "Test_diff_anchors_scrollbind_topline_04", {}) + + " Also test no-filler + call term_sendkeys(buf, "gg") + call VerifyInternal(buf, "Test_diff_anchors_scrollbind_topline_05", " diffopt+=anchor diffopt-=filler") + call term_sendkeys(buf, "\<Esc>\<C-E>") + call VerifyScreenDump(buf, "Test_diff_anchors_scrollbind_topline_06", {}) + call term_sendkeys(buf, "\<C-E>") + call VerifyScreenDump(buf, "Test_diff_anchors_scrollbind_topline_07", {}) + + call StopMNVInTerminal(buf) +endfunc + +func Test_diffanchors_scrollbind_topline2() + CheckScreendump + + " More-complicated case with 3 files and multiple overlapping diff blocks + call WriteDiffFiles3(0, + \ ["anchor1"], + \ ["diff2a", "diff2b", "diff2c", "diff2d", "anchor2"], + \ ["diff3a", "diff3c", "diff3d", "anchor3", "diff3e"]) + let buf = RunMNVInTerminal('-d Xdifile1 Xdifile2 Xdifile3', {}) + + call term_sendkeys(buf, ":1wincmd w\<CR>:setlocal dia=1,1,2\<CR>") + call term_sendkeys(buf, ":2wincmd w\<CR>:setlocal dia=3,5,6\<CR>") + call term_sendkeys(buf, ":3wincmd w\<CR>:setlocal dia=2,4,5\<CR>") + + call VerifyInternal(buf, "Test_diff_anchors_scrollbind_topline_08", " diffopt+=anchor") + call term_sendkeys(buf, ":1wincmd w\<CR>") + call term_sendkeys(buf, "\<C-E>") + call VerifyScreenDump(buf, "Test_diff_anchors_scrollbind_topline_09", {}) + call term_sendkeys(buf, "\<C-E>") + call VerifyScreenDump(buf, "Test_diff_anchors_scrollbind_topline_10", {}) + call term_sendkeys(buf, "\<C-E>") + call VerifyScreenDump(buf, "Test_diff_anchors_scrollbind_topline_11", {}) + call term_sendkeys(buf, "\<C-E>") + call VerifyScreenDump(buf, "Test_diff_anchors_scrollbind_topline_12", {}) + + " Also test no-filler + call term_sendkeys(buf, ":3wincmd w\<CR>gg") + call VerifyInternal(buf, "Test_diff_anchors_scrollbind_topline_13", " diffopt+=anchor diffopt-=filler") + call term_sendkeys(buf, "\<Esc>\<C-E>") + call VerifyScreenDump(buf, "Test_diff_anchors_scrollbind_topline_14", {}) + call term_sendkeys(buf, "\<C-E>") + call VerifyScreenDump(buf, "Test_diff_anchors_scrollbind_topline_15", {}) + call term_sendkeys(buf, "\<C-E>") + call VerifyScreenDump(buf, "Test_diff_anchors_scrollbind_topline_16", {}) + call term_sendkeys(buf, "\<C-E>") + call VerifyScreenDump(buf, "Test_diff_anchors_scrollbind_topline_17", {}) + + call StopMNVInTerminal(buf) +endfunc + +" Test that setting 'diffanchors' will update the diff. +func Test_diffanchors_option_set_update() + set diffanchors='a diffopt=internal,filler,anchor + + " Set up 3 tabs that share some buffers, and set up marks on each of them. + " We want to make sure only relevant tabs are updated if buffer-local diff + " anchors are updated, but all tabs should refresh if global diff anchors + " are updated (see diffanchors_changed() in code). + + " Tab 1. A buffer here will be reused. + call setline(1, range(1, 10)) + 3mark a + 4mark b + diffthis + new + call setline(1, range(21, 25)) + let buf = bufnr() + 1mark a + 2mark b + diffthis + call assert_equal(2, diff_filler(1)) + call assert_equal(0, diff_filler(2)) + + " Tab 2. "buf" is here but intentionally not participating in diff. + tabnew + exec 'buf ' .. buf + diffoff + new + call setline(1, range(31, 40)) + 8mark a + 9mark b + diffthis + new + call setline(1, range(41, 50)) + 5mark a + 6mark b + diffthis + + call assert_equal(3, diff_filler(5)) + call assert_equal(0, diff_filler(6)) + call assert_equal(0, diff_filler(7)) + + " Update mark a location, and check that the diff has *not* updated. When + " updating marks diff's won't automatically update. + 7mark a + call assert_equal(3, diff_filler(5)) + call assert_equal(0, diff_filler(6)) + call assert_equal(0, diff_filler(7)) + + " Tab 3. "buf" is used here and also in a diff. + tabnew + call setline(1, range(51, 65)) + 10mark a + 11mark b + diffthis + exec 'sbuffer ' .. buf + diffthis + + " Change local diff anchor of "buf" to mark b + setlocal diffanchors='b + + " Tab 1 should immediately update the diff to use mark b because the buf + " local diff anchor has been changed in "buf". + 1tabnext + call assert_equal(0, diff_filler(1)) + call assert_equal(1, diff_filler(2)) + + " Tab 2 should not immediately update because "buf" is not a diff buffer + " here. + 2tabnext + call assert_equal(3, diff_filler(5)) + call assert_equal(0, diff_filler(6)) + call assert_equal(0, diff_filler(7)) + + " Manual diff update would refresh the diff since we previously changed mark + " a's location above. + diffupdate + call assert_equal(0, diff_filler(5)) + call assert_equal(0, diff_filler(6)) + call assert_equal(1, diff_filler(7)) + + " Go back to tab 1. Reset diff anchor to global value and make sure it uses + " mark a again. + 1tabnext + set diffanchors< + call assert_equal(2, diff_filler(1)) + call assert_equal(0, diff_filler(2)) + + " Now, change the global diff anchor to mark b. This should affect all tabs + " including tab 2 which should update automatically. + set diffanchors='b + call assert_equal(0, diff_filler(1)) + call assert_equal(2, diff_filler(2)) + + 2tabnext + call assert_equal(0, diff_filler(5)) + call assert_equal(3, diff_filler(6)) + call assert_equal(0, diff_filler(7)) + + %bw! + set diffopt& + set diffanchors& +endfunc + +" Test that using diff anchors with window/buffer-local addresses will work as +" expected and use the relevant window/buffer instead of curbuf/curwin. +func Test_diffanchors_buf_win_local_addresses() + " Win 1-3 point to buffer 1. Set up different window-specific jump history + " Win 2 is the one we activate diff mode on. + call setline(1, range(1, 15)) + norm 2gg + norm 3gg + + split + norm 4gg + norm 5gg + + split + norm 11gg + norm 12gg + call setline(10, 'new text 1') " update the '. mark to line 10 + + " Win 4 points to buffer 2 + botright vert new + call setline(1, range(101, 110)) + norm 8gg + norm 9gg + call setline(3, 'new text 2') " update the '. mark to line 3 + + 2wincmd w + diffthis + 4wincmd w + diffthis + + " Test buffer-local marks using '. Should be anchored to lines 10 / 3. + set diffopt=internal,filler,anchor + set diffanchors='. + 4wincmd w + call assert_equal(7, diff_filler(3)) + + " Test window-local marks using '' Should be anchored to lines 4 / 8. + " Note that windows 1 & 3 point to the buffer being diff'ed but are not used + " for diffing themselves and therefore should not be used. Windows 2 & 4 + " should be used. + set diffanchors='' + 2wincmd w + call assert_equal(4, diff_filler(4)) + + " Also test "." for the current cursor position, which is also + " window-specific. Make sure the cursor position at the longer file doesn't + " result in the other file using out of bounds line number. + 4wincmd w + norm G + 2wincmd w + norm G + set diffanchors=. + diffupdate + 4wincmd w + call assert_equal(5, diff_filler(10)) + + %bw! + set diffopt& + set diffanchors& +endfunc + +" Test diff anchors error handling for anchors that fail to resolve to a line. +" These are not handled during option parsing because they depend on the +" specifics of the buffer at diff time. +func Test_diffanchors_invalid() + call setline(1, range(1, 5)) + new + call setline(1, range(11, 20)) + set diffopt=internal,filler,anchor + windo diffthis + 1wincmd w + + " Line numbers that are out of bounds should be an error + set diffanchors=0 + call assert_fails('diffupdate', 'E16:') + set diffanchors=1 + diffupdate + set diffanchors=$ + diffupdate + set diffanchors=$+1 + diffupdate + set diffanchors=$+2 + call assert_fails('diffupdate', 'E16:') + + " Test that non-existent marks in any one buffer will be detected + set diffanchors='a + call assert_fails('diffupdate', 'E20:') + 2mark a + call assert_fails('diffupdate', 'E20:') + + set diffanchors=1 + setlocal diffanchors='a + diffupdate + + set diffanchors< + windo 2mark a + set diffanchors='b + call assert_fails('diffupdate', 'E20:') + set diffanchors='a + diffupdate + + " File marks are ok to use for anchors only if it is in the same file + 1wincmd w + 3mark C + setlocal diffanchors='C + diffupdate + set diffanchors='C + call assert_fails('diffupdate', 'E20:') + + " Buffer-local marks also can only be used in buffers that have them. + set diffanchors=1 + exec "norm 1ggVj\<Esc>" + setlocal diffanchors='< + diffupdate + set diffanchors='< + call assert_fails('diffupdate', 'E20:') + + " Pattern search that failed will be an error too + let @/='orig_search_pat' + set diffanchors=1/5/ + diffupdate + call assert_equal('orig_search_pat', @/) " also check we don't pollute the search register + set diffanchors=1/does_not_exist/ + call assert_fails('diffupdate', 'E1550:') + call assert_equal('orig_search_pat', @/) + + " Hidden buffers are not supported right now + hide + call assert_fails('diffupdate', 'E1562:') + + %bw! + set diffopt& + set diffanchors& +endfunc + +" Test diffget/diffput behaviors when using diff anchors which could create +" adjacent diff blocks. +func Test_diffget_diffput_diffanchors() + set diffanchors=1/anchor/ + set diffopt=internal,filler,anchor + + call setline(1, ['1', 'anchor1', '4']) + diffthis + new + call setline(1, ['2', '3', 'anchor2', '4', '5']) + diffthis + wincmd w + + " Test using no-range diffget. It should grab the closest diff block only, + " even if there are multiple adjacent blocks. + 2 + diffget + call assert_equal(['1', 'anchor2', '4'], getline(1, '$')) + diffget + call assert_equal(['2', '3', 'anchor2', '4'], getline(1, '$')) + + " Test using a range to get/put all the adjacent diff blocks. + 1,$delete + call setline(1, ['anchor1', '4']) + 0,1 diffget + call assert_equal(['2', '3', 'anchor2', '4'], getline(1, '$')) + + 1,$delete + call setline(1, ['anchor1', '4']) + 0,$+1 diffget + call assert_equal(['2', '3', 'anchor2', '4', '5'], getline(1, '$')) + + 1,$delete + call setline(1, ['anchor1', '4']) + 0,1 diffput + wincmd w + call assert_equal(['anchor1', '4', '5'], getline(1,'$')) + + %bw! + set diffopt& + set diffanchors& +endfunc + +func Test_diff_add_prop_in_autocmd() + CheckScreendump + + let lines =<< trim END + func MyDiff() abort + let f1 = readfile(v:fname_in) + let f2 = readfile(v:fname_new) + let f0 = diff(f1, f2) + call writefile(split(f0, "\n"), v:fname_out) + endfunc + + call prop_type_add('myprop', #{highlight: 'Search', override: 1}) + autocmd OptionSet diff call prop_add(1, 1, #{type: 'myprop', length: 100}) + set diffexpr=MyDiff() + END + call writefile(lines, 'Xtest_diff_add_prop_in_autocmd', 'D') + call writefile(['foo', 'bar', 'baz'], 'Xdiffsplit_file', 'D') + + let buf = RunMNVInTerminal('-S Xtest_diff_add_prop_in_autocmd', {}) + call term_sendkeys(buf, ":diffsplit Xdiffsplit_file\<CR>") + call VerifyScreenDump(buf, 'Test_diff_add_prop_in_autocmd_01', {}) + + call StopMNVInTerminal(buf) +endfunc + +" this was causing a use-after-free by calling winframe_remove() recursively +func Test_diffexpr_wipe_buffers() + CheckRunMNVInTerminal + + let lines =<< trim END + def DiffFuncExpr() + var in: list<string> = readfile(v:fname_in) + var new = readfile(v:fname_new) + var out: string = diff(in, new) + writefile(split(out, "n"), v:fname_out) + enddef + + new + vnew + set diffexpr=DiffFuncExpr() + wincmd l + new + cal setline(1,range(20)) + wind difft + wincm w + hid + %bw! + END + call writefile(lines, 'Xtest_diffexpr_wipe', 'D') + + let buf = RunMNVInTerminal('Xtest_diffexpr_wipe', {}) + call term_sendkeys(buf, ":so\<CR>") + call WaitForAssert({-> assert_match('4 buffers wiped out', term_getline(buf, 20))}) + + call StopMNVInTerminal(buf) +endfunc + +func Test_diffput_to_empty_buf() + CheckScreendump + + let lines =<< trim END + call setline(1, ['foo', 'bar', 'baz']) + rightbelow vnew + windo diffthis + windo set cursorline nofoldenable + wincmd t + END + call writefile(lines, 'Xtest_diffput_to_empty_buf', 'D') + + let buf = RunMNVInTerminal('-S Xtest_diffput_to_empty_buf', {}) + call VerifyScreenDump(buf, 'Test_diffput_to_empty_buf_01', {}) + call term_sendkeys(buf, '0') " Trigger an initial 'cursorbind' check. + call VerifyScreenDump(buf, 'Test_diffput_to_empty_buf_01', {}) + call term_sendkeys(buf, ":diffput | echo\<CR>") + call VerifyScreenDump(buf, 'Test_diffput_to_empty_buf_02', {}) + call term_sendkeys(buf, ":redraw!\<CR>") + call VerifyScreenDump(buf, 'Test_diffput_to_empty_buf_02', {}) + call term_sendkeys(buf, 'j') + call VerifyScreenDump(buf, 'Test_diffput_to_empty_buf_03', {}) + + call StopMNVInTerminal(buf) +endfunc + +" mnv: shiftwidth=2 sts=2 expandtab |
