Merge pull request #74 from barrettruth/feat/vscode-diff
feat(config): replace algorithm 'auto'/'native' with 'default'/'vscode'
This commit is contained in:
commit
b6f1c5b749
6 changed files with 90 additions and 47 deletions
|
|
@ -17,6 +17,7 @@ syntax highlighting.
|
|||
- Background-only diff colors for any `&diff` buffer (`:diffthis`, `vimdiff`)
|
||||
- Vim syntax fallback for languages without a treesitter parser
|
||||
- Hunk header context highlighting (`@@ ... @@ function foo()`)
|
||||
- Character-level (intra-line) diff highlighting for changed characters
|
||||
- Configurable debouncing, max lines, and diff prefix concealment
|
||||
|
||||
## Requirements
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ Configuration is done via `vim.g.diffs`. Set this before the plugin loads:
|
|||
},
|
||||
intra = {
|
||||
enabled = true,
|
||||
algorithm = 'auto',
|
||||
algorithm = 'default',
|
||||
max_lines = 500,
|
||||
},
|
||||
},
|
||||
|
|
@ -158,12 +158,12 @@ Configuration is done via `vim.g.diffs`. Set this before the plugin loads:
|
|||
an intense background overlay while the rest of the
|
||||
line keeps the softer line-level background.
|
||||
|
||||
{algorithm} (string, default: 'auto')
|
||||
{algorithm} (string, default: 'default')
|
||||
Diff algorithm for character-level analysis.
|
||||
`'auto'`: use libvscodediff if available, else
|
||||
native `vim.diff()`. `'native'`: always use
|
||||
`vim.diff()`. `'vscode'`: require libvscodediff
|
||||
(falls back to native if not available).
|
||||
`'default'`: use |vim.diff()| with settings
|
||||
inherited from |'diffopt'| (`algorithm` and
|
||||
`linematch`). `'vscode'`: use libvscodediff FFI
|
||||
(falls back to default if not available).
|
||||
|
||||
{max_lines} (integer, default: 500)
|
||||
Skip character-level highlighting for hunks larger
|
||||
|
|
@ -285,8 +285,8 @@ Summary / commit detail views: ~
|
|||
- Code is parsed with |vim.treesitter.get_string_parser()|
|
||||
- If no treesitter parser and `vim.enabled`: vim syntax fallback via
|
||||
scratch buffer and |synID()|
|
||||
- Background extmarks (`DiffsAdd`/`DiffsDelete`) at priority 198
|
||||
- `Normal` extmarks at priority 199 clear underlying diff foreground
|
||||
- `Normal` extmarks at priority 198 clear underlying diff foreground
|
||||
- Background extmarks (`DiffsAdd`/`DiffsDelete`) at priority 199
|
||||
- Syntax highlights are applied as extmarks at priority 200
|
||||
- Character-level diff extmarks (`DiffsAddText`/`DiffsDeleteText`) at
|
||||
priority 201 overlay changed characters with an intense background
|
||||
|
|
@ -383,15 +383,14 @@ Fugitive unified diff highlights: ~
|
|||
*DiffsAddText*
|
||||
DiffsAddText Character-level background for changed characters
|
||||
within `+` lines. Derived by blending `diffAdded`
|
||||
foreground with `Normal` background at 40% alpha.
|
||||
Uses the same base color as `DiffsAddNr` foreground,
|
||||
making changed characters clearly visible. Only sets
|
||||
`bg`, so treesitter foreground colors show through.
|
||||
foreground with `Normal` background at 70% alpha.
|
||||
Only sets `bg`, so treesitter foreground colors show
|
||||
through.
|
||||
|
||||
*DiffsDeleteText*
|
||||
DiffsDeleteText Character-level background for changed characters
|
||||
within `-` lines. Derived by blending `diffRemoved`
|
||||
foreground with `Normal` background at 40% alpha.
|
||||
foreground with `Normal` background at 70% alpha.
|
||||
|
||||
Diff mode window highlights: ~
|
||||
These are used for |winhighlight| remapping in `&diff` windows.
|
||||
|
|
|
|||
|
|
@ -11,6 +11,10 @@
|
|||
---@field del_lines {idx: integer, text: string}[]
|
||||
---@field add_lines {idx: integer, text: string}[]
|
||||
|
||||
---@class diffs.DiffOpts
|
||||
---@field algorithm? string
|
||||
---@field linematch? integer
|
||||
|
||||
local M = {}
|
||||
|
||||
local dbg = require('diffs.log').dbg
|
||||
|
|
@ -60,11 +64,35 @@ function M.extract_change_groups(hunk_lines)
|
|||
return groups
|
||||
end
|
||||
|
||||
---@return diffs.DiffOpts
|
||||
local function parse_diffopt()
|
||||
local opts = {}
|
||||
for _, item in ipairs(vim.split(vim.o.diffopt, ',')) do
|
||||
local key, val = item:match('^(%w+):(.+)$')
|
||||
if key == 'algorithm' then
|
||||
opts.algorithm = val
|
||||
elseif key == 'linematch' then
|
||||
opts.linematch = tonumber(val)
|
||||
end
|
||||
end
|
||||
return opts
|
||||
end
|
||||
|
||||
---@param old_text string
|
||||
---@param new_text string
|
||||
---@param diff_opts? diffs.DiffOpts
|
||||
---@return {old_start: integer, old_count: integer, new_start: integer, new_count: integer}[]
|
||||
local function byte_diff(old_text, new_text)
|
||||
local ok, result = pcall(vim.diff, old_text, new_text, { result_type = 'indices' })
|
||||
local function byte_diff(old_text, new_text, diff_opts)
|
||||
local vim_opts = { result_type = 'indices' }
|
||||
if diff_opts then
|
||||
if diff_opts.algorithm then
|
||||
vim_opts.algorithm = diff_opts.algorithm
|
||||
end
|
||||
if diff_opts.linematch then
|
||||
vim_opts.linematch = diff_opts.linematch
|
||||
end
|
||||
end
|
||||
local ok, result = pcall(vim.diff, old_text, new_text, vim_opts)
|
||||
if not ok or not result then
|
||||
return {}
|
||||
end
|
||||
|
|
@ -95,8 +123,9 @@ end
|
|||
---@param new_line string
|
||||
---@param del_idx integer
|
||||
---@param add_idx integer
|
||||
---@param diff_opts? diffs.DiffOpts
|
||||
---@return diffs.CharSpan[], diffs.CharSpan[]
|
||||
local function char_diff_pair(old_line, new_line, del_idx, add_idx)
|
||||
local function char_diff_pair(old_line, new_line, del_idx, add_idx, diff_opts)
|
||||
---@type diffs.CharSpan[]
|
||||
local del_spans = {}
|
||||
---@type diffs.CharSpan[]
|
||||
|
|
@ -108,7 +137,7 @@ local function char_diff_pair(old_line, new_line, del_idx, add_idx)
|
|||
local old_text = table.concat(old_bytes, '\n') .. '\n'
|
||||
local new_text = table.concat(new_bytes, '\n') .. '\n'
|
||||
|
||||
local char_hunks = byte_diff(old_text, new_text)
|
||||
local char_hunks = byte_diff(old_text, new_text, diff_opts)
|
||||
|
||||
for _, ch in ipairs(char_hunks) do
|
||||
if ch.old_count > 0 then
|
||||
|
|
@ -132,8 +161,9 @@ local function char_diff_pair(old_line, new_line, del_idx, add_idx)
|
|||
end
|
||||
|
||||
---@param group diffs.ChangeGroup
|
||||
---@param diff_opts? diffs.DiffOpts
|
||||
---@return diffs.CharSpan[], diffs.CharSpan[]
|
||||
local function diff_group_native(group)
|
||||
local function diff_group_native(group, diff_opts)
|
||||
---@type diffs.CharSpan[]
|
||||
local all_del = {}
|
||||
---@type diffs.CharSpan[]
|
||||
|
|
@ -147,7 +177,8 @@ local function diff_group_native(group)
|
|||
group.del_lines[1].text,
|
||||
group.add_lines[1].text,
|
||||
group.del_lines[1].idx,
|
||||
group.add_lines[1].idx
|
||||
group.add_lines[1].idx,
|
||||
diff_opts
|
||||
)
|
||||
vim.list_extend(all_del, ds)
|
||||
vim.list_extend(all_add, as)
|
||||
|
|
@ -166,7 +197,7 @@ local function diff_group_native(group)
|
|||
local old_block = table.concat(old_texts, '\n') .. '\n'
|
||||
local new_block = table.concat(new_texts, '\n') .. '\n'
|
||||
|
||||
local line_hunks = byte_diff(old_block, new_block)
|
||||
local line_hunks = byte_diff(old_block, new_block, diff_opts)
|
||||
|
||||
---@type table<integer, integer>
|
||||
local old_to_new = {}
|
||||
|
|
@ -184,7 +215,8 @@ local function diff_group_native(group)
|
|||
group.del_lines[old_i].text,
|
||||
group.add_lines[new_i].text,
|
||||
group.del_lines[old_i].idx,
|
||||
group.add_lines[new_i].idx
|
||||
group.add_lines[new_i].idx,
|
||||
diff_opts
|
||||
)
|
||||
vim.list_extend(all_del, ds)
|
||||
vim.list_extend(all_add, as)
|
||||
|
|
@ -202,7 +234,8 @@ local function diff_group_native(group)
|
|||
group.del_lines[oi].text,
|
||||
group.add_lines[ni].text,
|
||||
group.del_lines[oi].idx,
|
||||
group.add_lines[ni].idx
|
||||
group.add_lines[ni].idx,
|
||||
diff_opts
|
||||
)
|
||||
vim.list_extend(all_del, ds)
|
||||
vim.list_extend(all_add, as)
|
||||
|
|
@ -295,16 +328,26 @@ function M.compute_intra_hunks(hunk_lines, algorithm)
|
|||
return nil
|
||||
end
|
||||
|
||||
algorithm = algorithm or 'auto'
|
||||
algorithm = algorithm or 'default'
|
||||
|
||||
local lib = require('diffs.lib')
|
||||
local vscode_handle = nil
|
||||
if algorithm ~= 'native' then
|
||||
vscode_handle = lib.load()
|
||||
if algorithm == 'vscode' then
|
||||
vscode_handle = require('diffs.lib').load()
|
||||
if not vscode_handle then
|
||||
dbg('vscode algorithm requested but library not available, falling back to default')
|
||||
end
|
||||
end
|
||||
|
||||
if algorithm == 'vscode' and not vscode_handle then
|
||||
dbg('vscode algorithm requested but library not available, falling back to native')
|
||||
---@type diffs.DiffOpts?
|
||||
local diff_opts = nil
|
||||
if not vscode_handle then
|
||||
diff_opts = parse_diffopt()
|
||||
if diff_opts.algorithm then
|
||||
dbg('diffopt algorithm: %s', diff_opts.algorithm)
|
||||
end
|
||||
if diff_opts.linematch then
|
||||
dbg('diffopt linematch: %d', diff_opts.linematch)
|
||||
end
|
||||
end
|
||||
|
||||
---@type diffs.CharSpan[]
|
||||
|
|
@ -325,7 +368,7 @@ function M.compute_intra_hunks(hunk_lines, algorithm)
|
|||
if vscode_handle then
|
||||
ds, as = diff_group_vscode(group, vscode_handle)
|
||||
else
|
||||
ds, as = diff_group_native(group)
|
||||
ds, as = diff_group_native(group, diff_opts)
|
||||
end
|
||||
dbg('group %d result: %d del spans, %d add spans', gi, #ds, #as)
|
||||
for _, s in ipairs(ds) do
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ local default_config = {
|
|||
},
|
||||
intra = {
|
||||
enabled = true,
|
||||
algorithm = 'auto',
|
||||
algorithm = 'default',
|
||||
max_lines = 500,
|
||||
},
|
||||
},
|
||||
|
|
@ -259,9 +259,9 @@ local function init()
|
|||
['highlights.intra.algorithm'] = {
|
||||
opts.highlights.intra.algorithm,
|
||||
function(v)
|
||||
return v == nil or v == 'auto' or v == 'native' or v == 'vscode'
|
||||
return v == nil or v == 'default' or v == 'vscode'
|
||||
end,
|
||||
"'auto', 'native', or 'vscode'",
|
||||
"'default' or 'vscode'",
|
||||
},
|
||||
['highlights.intra.max_lines'] = { opts.highlights.intra.max_lines, 'number', true },
|
||||
})
|
||||
|
|
|
|||
|
|
@ -73,17 +73,17 @@ describe('diff', function()
|
|||
|
||||
describe('compute_intra_hunks', function()
|
||||
it('returns nil for all-addition hunks', function()
|
||||
local result = diff.compute_intra_hunks({ '+line1', '+line2' }, 'native')
|
||||
local result = diff.compute_intra_hunks({ '+line1', '+line2' }, 'default')
|
||||
assert.is_nil(result)
|
||||
end)
|
||||
|
||||
it('returns nil for all-deletion hunks', function()
|
||||
local result = diff.compute_intra_hunks({ '-line1', '-line2' }, 'native')
|
||||
local result = diff.compute_intra_hunks({ '-line1', '-line2' }, 'default')
|
||||
assert.is_nil(result)
|
||||
end)
|
||||
|
||||
it('returns nil for context-only hunks', function()
|
||||
local result = diff.compute_intra_hunks({ ' line1', ' line2' }, 'native')
|
||||
local result = diff.compute_intra_hunks({ ' line1', ' line2' }, 'default')
|
||||
assert.is_nil(result)
|
||||
end)
|
||||
|
||||
|
|
@ -91,7 +91,7 @@ describe('diff', function()
|
|||
local result = diff.compute_intra_hunks({
|
||||
'-local x = 1',
|
||||
'+local x = 2',
|
||||
}, 'native')
|
||||
}, 'default')
|
||||
assert.is_not_nil(result)
|
||||
assert.is_true(#result.del_spans > 0)
|
||||
assert.is_true(#result.add_spans > 0)
|
||||
|
|
@ -101,7 +101,7 @@ describe('diff', function()
|
|||
local result = diff.compute_intra_hunks({
|
||||
'-local x = 1',
|
||||
'+local x = 2',
|
||||
}, 'native')
|
||||
}, 'default')
|
||||
assert.is_not_nil(result)
|
||||
|
||||
assert.are.equal(1, #result.del_spans)
|
||||
|
|
@ -121,7 +121,7 @@ describe('diff', function()
|
|||
' local b = 3',
|
||||
'-local c = 4',
|
||||
'+local c = 5',
|
||||
}, 'native')
|
||||
}, 'default')
|
||||
assert.is_not_nil(result)
|
||||
assert.is_true(#result.del_spans >= 2)
|
||||
assert.is_true(#result.add_spans >= 2)
|
||||
|
|
@ -132,7 +132,7 @@ describe('diff', function()
|
|||
'-line one',
|
||||
'-line two',
|
||||
'+line combined',
|
||||
}, 'native')
|
||||
}, 'default')
|
||||
assert.is_not_nil(result)
|
||||
end)
|
||||
|
||||
|
|
@ -140,7 +140,7 @@ describe('diff', function()
|
|||
local result = diff.compute_intra_hunks({
|
||||
'-local x = "héllo"',
|
||||
'+local x = "wörld"',
|
||||
}, 'native')
|
||||
}, 'default')
|
||||
assert.is_not_nil(result)
|
||||
assert.is_true(#result.del_spans > 0)
|
||||
assert.is_true(#result.add_spans > 0)
|
||||
|
|
@ -150,7 +150,7 @@ describe('diff', function()
|
|||
local result = diff.compute_intra_hunks({
|
||||
'-local x = 1',
|
||||
'+local x = 1',
|
||||
}, 'native')
|
||||
}, 'default')
|
||||
assert.is_nil(result)
|
||||
end)
|
||||
end)
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ describe('highlight', function()
|
|||
},
|
||||
intra = {
|
||||
enabled = false,
|
||||
algorithm = 'native',
|
||||
algorithm = 'default',
|
||||
max_lines = 500,
|
||||
},
|
||||
},
|
||||
|
|
@ -828,7 +828,7 @@ describe('highlight', function()
|
|||
default_opts({
|
||||
highlights = {
|
||||
background = true,
|
||||
intra = { enabled = true, algorithm = 'native', max_lines = 500 },
|
||||
intra = { enabled = true, algorithm = 'default', max_lines = 500 },
|
||||
},
|
||||
})
|
||||
)
|
||||
|
|
@ -873,7 +873,7 @@ describe('highlight', function()
|
|||
ns,
|
||||
hunk,
|
||||
default_opts({
|
||||
highlights = { intra = { enabled = true, algorithm = 'native', max_lines = 500 } },
|
||||
highlights = { intra = { enabled = true, algorithm = 'default', max_lines = 500 } },
|
||||
})
|
||||
)
|
||||
|
||||
|
|
@ -913,7 +913,7 @@ describe('highlight', function()
|
|||
ns,
|
||||
hunk,
|
||||
default_opts({
|
||||
highlights = { intra = { enabled = false, algorithm = 'native', max_lines = 500 } },
|
||||
highlights = { intra = { enabled = false, algorithm = 'default', max_lines = 500 } },
|
||||
})
|
||||
)
|
||||
|
||||
|
|
@ -947,7 +947,7 @@ describe('highlight', function()
|
|||
ns,
|
||||
hunk,
|
||||
default_opts({
|
||||
highlights = { intra = { enabled = true, algorithm = 'native', max_lines = 500 } },
|
||||
highlights = { intra = { enabled = true, algorithm = 'default', max_lines = 500 } },
|
||||
})
|
||||
)
|
||||
|
||||
|
|
@ -984,7 +984,7 @@ describe('highlight', function()
|
|||
default_opts({
|
||||
highlights = {
|
||||
background = true,
|
||||
intra = { enabled = true, algorithm = 'native', max_lines = 500 },
|
||||
intra = { enabled = true, algorithm = 'default', max_lines = 500 },
|
||||
},
|
||||
})
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue