commit cf9b7a37cc3cf0ba934865626e1f392e956a59b1
parent 4f10e56cc5f2cb91337790ddc93e5fdeaefd5c2d
Author: zeertzjq <zeertzjq@outlook.com>
Date: Fri, 12 Sep 2025 11:57:05 +0800
vim-patch:9.1.1753: defaults: 'diffopt' option value can be improved (#35727)
Problem: defaults: 'diffopt' option value can be improved
Solution: Update diffopt defaults to include "indent-heuristic" and
"inline:char" (Yee Cheng Chin)
The default diff options have not been updated much despite new
functionality having been added to Vim.
- indent-heurstic: This has been enabled by default in Git since
33de716387 in 2017. Given that Vim uses xdiff from Git, it makes sense
to track the default configuration from Git.
- inline:char: This turns on character-wise inline highlighting which is
generally much better than the default inline:simple. It has been
implemented since vim/vim#16881 and we have not seen reports of any issues
with it, and it has received good feedbacks.
closes: vim/vim#18255
https://github.com/vim/vim/commit/976b365305c2e9025813ff3c7fe52f8d927fafc3
Co-authored-by: Yee Cheng Chin <ychin.git@gmail.com>
Diffstat:
11 files changed, 73 insertions(+), 36 deletions(-)
diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt
@@ -160,6 +160,7 @@ BUILD
DEFAULTS
+• 'diffopt' default value now includes "indent-heuristic" and "inline:char".
• 'statusline' default is exposed as a statusline expression (previously it
was implemented as an internal C routine).
• 'statusline' includes |vim.diagnostic.status()|
diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt
@@ -2182,7 +2182,7 @@ A jump table for the options with a short description can be found at |Q_op|.
security reasons.
*'diffopt'* *'dip'*
-'diffopt' 'dip' string (default "internal,filler,closeoff,inline:simple,linematch:40")
+'diffopt' 'dip' string (default "internal,filler,closeoff,indent-heuristic,inline:char,linematch:40")
global
Option settings for diff mode. It can consist of the following items.
All are optional. Items must be separated by a comma.
diff --git a/runtime/lua/vim/_meta/options.lua b/runtime/lua/vim/_meta/options.lua
@@ -1936,7 +1936,7 @@ vim.go.dex = vim.go.diffexpr
---
---
--- @type string
-vim.o.diffopt = "internal,filler,closeoff,inline:simple,linematch:40"
+vim.o.diffopt = "internal,filler,closeoff,indent-heuristic,inline:char,linematch:40"
vim.o.dip = vim.o.diffopt
vim.go.diffopt = vim.o.diffopt
vim.go.dip = vim.go.diffopt
diff --git a/src/nvim/diff.c b/src/nvim/diff.c
@@ -95,9 +95,10 @@ static bool diff_need_update = false; // ex_diffupdate needs to be called
#define ALL_WHITE_DIFF (DIFF_IWHITE | DIFF_IWHITEALL | DIFF_IWHITEEOL)
#define ALL_INLINE (DIFF_INLINE_NONE | DIFF_INLINE_SIMPLE | DIFF_INLINE_CHAR | DIFF_INLINE_WORD)
#define ALL_INLINE_DIFF (DIFF_INLINE_CHAR | DIFF_INLINE_WORD)
-static int diff_flags = DIFF_INTERNAL | DIFF_FILLER | DIFF_CLOSE_OFF | DIFF_LINEMATCH;
+static int diff_flags = DIFF_INTERNAL | DIFF_FILLER | DIFF_CLOSE_OFF
+ | DIFF_LINEMATCH | DIFF_INLINE_CHAR;
-static int diff_algorithm = 0;
+static int diff_algorithm = XDF_INDENT_HEURISTIC;
static int linematch_lines = 40;
#define LBUFLEN 50 // length of line in diff file
diff --git a/src/nvim/options.lua b/src/nvim/options.lua
@@ -2374,7 +2374,7 @@ local options = {
{
abbreviation = 'dip',
cb = 'did_set_diffopt',
- defaults = 'internal,filler,closeoff,inline:simple,linematch:40',
+ defaults = 'internal,filler,closeoff,indent-heuristic,inline:char,linematch:40',
-- Keep this in sync with diffopt_changed().
values = {
'filler',
diff --git a/test/functional/ui/diff_spec.lua b/test/functional/ui/diff_spec.lua
@@ -1249,7 +1249,7 @@ end)
it('diff updates line numbers below filler lines', function()
local screen = Screen.new(40, 14)
exec([[
- set diffopt=internal,filler,closeoff
+ 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'])
diff --git a/test/functional/ui/highlight_spec.lua b/test/functional/ui/highlight_spec.lua
@@ -1220,7 +1220,7 @@ describe('CursorLine and CursorLineNr highlights', function()
command('windo diffthis')
screen:expect([[
{7: }{9:line 1 some text }│{7: }{9:^line 1 some text }|
- {7: }{4:line 2 mo}{27:Re text!}{4: }│{7: }{4:line 2 mo}{27:re text}{4: }|
+ {7: }{4:line 2 mo}{27:R}{4:e text}{27:!}{4: }│{7: }{4:line 2 mo}{27:r}{4:e text }|
{7: }{22:extra line! }│{7: }{23:----------------------}|
{7: }extra line! │{7: }extra line! |*2
{7: }last line ... │{7: }last line ... |
@@ -1232,7 +1232,7 @@ describe('CursorLine and CursorLineNr highlights', function()
feed('jjjjj')
screen:expect([[
{7: }line 1 some text │{7: }line 1 some text |
- {7: }{4:line 2 mo}{27:Re text!}{4: }│{7: }{4:line 2 mo}{27:re text}{4: }|
+ {7: }{4:line 2 mo}{27:R}{4:e text}{27:!}{4: }│{7: }{4:line 2 mo}{27:r}{4:e text }|
{7: }{22:extra line! }│{7: }{23:----------------------}|
{7: }extra line! │{7: }extra line! |*2
{7: }last line ... │{7: }last line ... |
@@ -1248,7 +1248,7 @@ describe('CursorLine and CursorLineNr highlights', function()
feed('kkkk')
screen:expect([[
{7: }line 1 some text │{7: }line 1 some text |
- {7: }{100:line 2 mo}{101:Re text!}{100: }│{7: }{100:^line 2 mo}{101:re text}{100: }|
+ {7: }{100:line 2 mo}{101:R}{100:e text}{101:!}{100: }│{7: }{100:^line 2 mo}{101:r}{100:e text }|
{7: }{22:extra line! }│{7: }{23:----------------------}|
{7: }extra line! │{7: }extra line! |*2
{7: }last line ... │{7: }last line ... |
diff --git a/test/functional/ui/linematch_spec.lua b/test/functional/ui/linematch_spec.lua
@@ -31,7 +31,7 @@ describe('Diff mode screen with 3 diffs open', function()
before_each(function()
clear()
- feed(':set diffopt+=linematch:30<cr>')
+ feed(':set diffopt=internal,filler,linematch:30<cr>')
feed(':e ' .. fname .. '<cr>')
feed(':vnew ' .. fname_2 .. '<cr>')
feed(':vnew ' .. fname_3 .. '<cr>')
@@ -218,7 +218,7 @@ describe('Diff mode screen with 2 diffs open', function()
describe('setup a diff with 2 files and set linematch:30', function()
before_each(function()
- feed(':set diffopt+=linematch:30<cr>')
+ feed(':set diffopt=internal,filler,linematch:30<cr>')
local f1 = [[
common line
@@ -701,7 +701,7 @@ something
end)
describe('setup a diff with 2 files and set linematch:30', function()
before_each(function()
- feed(':set diffopt+=linematch:30<cr>')
+ feed(':set diffopt=internal,filler,linematch:30<cr>')
local f1 = [[
// abc d
// d
@@ -731,7 +731,7 @@ d
end)
describe('setup a diff with 2 files and set linematch:30, with ignore white', function()
before_each(function()
- feed(':set diffopt+=linematch:30<cr>:set diffopt+=iwhiteall<cr>')
+ feed(':set diffopt=internal,filler,linematch:30,iwhiteall<cr>')
local f1 = [[
void testFunction () {
for (int i = 0; i < 10; i++) {
@@ -767,7 +767,7 @@ void testFunction () {
end)
describe('a diff that would result in multiple groups before grouping optimization', function()
before_each(function()
- feed(':set diffopt+=linematch:30<cr>')
+ feed(':set diffopt=internal,filler,linematch:30<cr>')
local f1 = [[
!A
!B
@@ -806,7 +806,7 @@ void testFunction () {
end)
describe('a diff that would result in multiple groups before grouping optimization', function()
before_each(function()
- feed(':set diffopt+=linematch:30<cr>')
+ feed(':set diffopt=internal,filler,linematch:30<cr>')
local f1 = [[
!A
!B
@@ -845,7 +845,7 @@ void testFunction () {
end)
describe('setup a diff with 2 files and set linematch:10', function()
before_each(function()
- feed(':set diffopt+=linematch:10<cr>')
+ feed(':set diffopt=internal,filler,linematch:10<cr>')
local f1 = [[
common line
HIL
@@ -1024,7 +1024,7 @@ describe('regressions', function()
it("doesn't crash with long lines", function()
clear()
- feed(':set diffopt+=linematch:30<cr>')
+ feed(':set diffopt=internal.filler,linematch:30<cr>')
screen = Screen.new(100, 20)
-- line must be greater than MATCH_CHAR_MAX_LEN
n.api.nvim_buf_set_lines(0, 0, -1, false, { string.rep('a', 1000) .. 'hello' })
@@ -1035,7 +1035,7 @@ describe('regressions', function()
it('properly computes filler lines for hunks bigger than linematch limit', function()
clear()
- feed(':set diffopt+=linematch:10<cr>')
+ feed(':set diffopt=internal,filler,linematch:10<cr>')
screen = Screen.new(100, 20)
local lines = {}
for i = 0, 29 do
@@ -1093,7 +1093,7 @@ describe('regressions', function()
n.exec('rightbelow vnew')
n.api.nvim_buf_set_lines(0, 0, -1, false, { 'hijklm', 'nopqr', 'stuv' })
n.exec([[
- set diffopt+=linematch:60
+ set diffopt=internal,filler,linematch:60
windo diffthis | wincmd t
call feedkeys("Aq\<esc>")
call feedkeys("GAklm\<esc>")
diff --git a/test/old/testdir/setup.vim b/test/old/testdir/setup.vim
@@ -6,7 +6,7 @@ if exists('s:did_load')
set completeopt=menu,preview
endif
set define=^\\s*#\\s*define
- set diffopt=internal,filler,closeoff,inline:simple
+ set diffopt=internal,filler,closeoff,indent-heuristic,inline:char
set directory^=.
set display=
set fillchars=vert:\|,foldsep:\|,fold:-
diff --git a/test/old/testdir/test_diffmode.vim b/test/old/testdir/test_diffmode.vim
@@ -850,6 +850,7 @@ func Test_diff_nomodifiable()
endfunc
func Test_diff_hlID()
+ set diffopt=internal,filler
new
call setline(1, [1, 2, 3, 'Yz', 'a dxxg',])
diffthis
@@ -892,6 +893,7 @@ func Test_diff_hlID()
call assert_equal(synIDattr(diff_hlID(3, 1), "name"), "")
%bwipe!
+ set diffopt&
endfunc
func Test_diff_filler()
@@ -1060,18 +1062,18 @@ func Test_diff_screen()
call term_sendkeys(buf, ":set diffopt+=algorithm:histogram\<cr>")
call VerifyScreenDump(buf, 'Test_diff_09', {})
- " Test 10-11: normal/indent-heuristic
+ " Test 10-11: with/without indent-heuristic
call term_sendkeys(buf, ":set diffopt&vim\<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>")
- call VerifyScreenDump(buf, 'Test_diff_10', {})
+ 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_11', {}, 'one')
+ 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>")
@@ -1312,6 +1314,7 @@ func Test_diff_with_syntax()
call writefile(lines, 'Xprogram2.c', 'D')
let lines =<< trim END
+ set diffopt=internal,filler
edit Xprogram1.c
diffsplit Xprogram2.c
END
@@ -1454,9 +1457,7 @@ func Test_diff_rnu()
CheckScreendump
let content =<< trim END
- 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'])
+ 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'])
@@ -1544,6 +1545,7 @@ 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'])
@@ -1623,6 +1625,7 @@ func Test_diff_modify_chunks()
call assert_equal(['', '', '', '', '', '', '', '', ''], hl)
%bw!
+ set diffopt&
endfunc
func Test_diff_binary()
@@ -2616,7 +2619,7 @@ func Test_linematch_diff()
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 term_sendkeys(buf, ":set diffopt=internal,filler,linematch:30\<CR>")
call WriteDiffFiles(buf, ['// abc d?',
\ '// d?',
\ '// d?' ],
@@ -2644,7 +2647,7 @@ func Test_linematch_diff_iwhite()
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+=linematch:30\<CR>")
+ 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++) {',
@@ -2671,7 +2674,7 @@ func Test_linematch_diff_grouping()
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+=linematch:30\<CR>")
+ call term_sendkeys(buf, ":set diffopt=internal,filler,linematch:30\<CR>")
call WriteDiffFiles(buf, ['!A',
\ '!B',
\ '!C' ],
@@ -2709,7 +2712,7 @@ func Test_linematch_diff_scroll()
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+=linematch:30\<CR>")
+ call term_sendkeys(buf, ":set diffopt=internal,filler,linematch:30\<CR>")
call WriteDiffFiles(buf, ['!A',
\ '!B',
\ '!C' ],
@@ -2740,7 +2743,7 @@ func Test_linematch_line_limit_exceeded()
let buf = RunVimInTerminal('-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+=linematch:10\<CR>")
+ 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,
@@ -2792,7 +2795,7 @@ func Test_linematch_3diffs()
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+=linematch:30\<CR>")
+ call term_sendkeys(buf, ":set diffopt=internal,filler,linematch:30\<CR>")
call WriteDiffFiles3(buf,
\ ["",
\ " common line",
@@ -2827,7 +2830,7 @@ func Test_linematch_3diffs_sanity_check()
call delete('.Xfile_linematch2.swp')
call delete('.Xfile_linematch3.swp')
let lines =<< trim END
- set diffopt+=linematch:60
+ set diffopt=internal,filler,linematch:60
call feedkeys("Aq\<esc>")
call feedkeys("GAklm\<esc>")
call feedkeys("o")
diff --git a/test/old/testdir/test_scroll_opt.vim b/test/old/testdir/test_scroll_opt.vim
@@ -302,11 +302,43 @@ func Test_smoothscroll_diff_mode()
call StopVimInTerminal(buf)
endfunc
+func Test_smoothscroll_diff_change_line_default()
+ CheckScreendump
+
+ " Uses the new diffopt default with indent-heuristic and inline:char
+ let lines =<< trim END
+ set diffopt=internal,filler,closeoff,indent-heuristic,inline:char,followwrap smoothscroll
+ call setline(1, repeat(' abc', &columns))
+ call setline(2, 'bar')
+ call setline(3, repeat(' abc', &columns))
+ vnew
+ call setline(1, repeat(' abc', &columns))
+ call setline(2, 'foo')
+ call setline(3, 'bar')
+ call setline(4, repeat(' abc', &columns))
+ windo exe "normal! 2gg5\<C-E>"
+ windo diffthis
+ END
+ call writefile(lines, 'XSmoothDiffChangeLine', 'D')
+ let buf = RunVimInTerminal('-S XSmoothDiffChangeLine', #{rows: 20, columns: 55})
+
+ call VerifyScreenDump(buf, 'Test_smooth_diff_change_line_1', {})
+ call term_sendkeys(buf, "Abar")
+ call VerifyScreenDump(buf, 'Test_smooth_diff_change_line_2', {})
+ call term_sendkeys(buf, "\<Esc>")
+ call VerifyScreenDump(buf, 'Test_smooth_diff_change_line_3a', {})
+ call term_sendkeys(buf, "yyp")
+ call VerifyScreenDump(buf, 'Test_smooth_diff_change_line_4', {})
+
+ call StopVimInTerminal(buf)
+endfunc
+
func Test_smoothscroll_diff_change_line()
CheckScreendump
+ " Uses the old diffopt default
let lines =<< trim END
- set diffopt+=followwrap smoothscroll
+ set diffopt=internal,filler,closeoff,followwrap,inline:simple smoothscroll
call setline(1, repeat(' abc', &columns))
call setline(2, 'bar')
call setline(3, repeat(' abc', &columns))