Skip to content

Commit

Permalink
feat(diffopt): add support for whitespace flags in diffopt
Browse files Browse the repository at this point in the history
Resolves #696
  • Loading branch information
lewis6991 committed Oct 2, 2023
1 parent e32efa6 commit 8aedc8b
Show file tree
Hide file tree
Showing 5 changed files with 148 additions and 62 deletions.
13 changes: 12 additions & 1 deletion doc/gitsigns.txt
Original file line number Diff line number Diff line change
Expand Up @@ -626,7 +626,8 @@ show_deleted *gitsigns-config-show_deleted*
diff_opts *gitsigns-config-diff_opts*
Type: `table[extended]`, Default: derived from 'diffopt'

Diff options.
Diff options. If the default value is used, then changes to 'diffopt' are
automatically applied.

Fields: ~
• algorithm: string
Expand All @@ -646,6 +647,16 @@ diff_opts *gitsigns-config-diff_opts*
• linematch: integer
Enable second-stage diff on hunks to align lines.
Requires `internal=true`.
• ignore_blank_lines: boolean
Ignore changes where lines are blank.
• ignore_whitespace_change: boolean
Ignore changes in amount of white space.
It should ignore adding trailing white space,
but not leading white space.
• ignore_whitespace: boolean
Ignore all white space changes.
• ignore_whitespace_change_at_eol: boolean
Ignore white space changes at end of line.

base *gitsigns-config-base*
Type: `string`, Default: index
Expand Down
129 changes: 92 additions & 37 deletions lua/gitsigns/config.lua
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
--- @class Gitsigns.SchemaElem
--- @class (exact) Gitsigns.SchemaElem
--- @field type string|string[]
--- @field refresh? fun(cb: fun()) Function to refresh the config value
--- @field deep_extend? boolean
--- @field default any
--- @field deprecated? boolean|{new_field:string,message:string,hard:boolean}
--- @field default_help? string
--- @field description string

--- @class Gitsigns.DiffOpts
--- @class (exact) Gitsigns.DiffOpts
--- @field algorithm string
--- @field internal boolean
--- @field indent_heuristic boolean
--- @field vertical boolean
--- @field linematch integer
--- @field linematch? integer
--- @field ignore_whitespace_change? true
--- @field ignore_whitespace? true
--- @field ignore_whitespace_change_at_eol? true
--- @field ignore_blank_lines? true

--- @class Gitsigns.SignConfig
--- @field show_count boolean
Expand Down Expand Up @@ -99,15 +104,59 @@ local function resolve_default(v)
end
end

--- @return Gitsigns.DiffOpts
local function parse_diffopt()
--- @type Gitsigns.DiffOpts
local r = {
algorithm = 'myers',
internal = false,
indent_heuristic = false,
vertical = true,
}

local optmap = {
['indent-heuristic'] = 'indent_heuristic',
internal = 'internal',
iwhite = 'ignore_whitespace_change',
iblank = 'ignore_blank_lines',
iwhiteeol = 'ignore_whitespace_change_at_eol',
iwhiteall = 'ignore_whitespace',
}

local diffopt = vim.opt.diffopt:get() --[[@as string[] ]]
for _, o in ipairs(diffopt) do
if optmap[o] then
r[optmap[o]] = true
elseif o == 'horizontal' then
r.vertical = false
elseif vim.startswith(o, 'algorithm:') then
r.algorithm = string.sub(o, ('algorithm:'):len() + 1)
elseif vim.startswith(o, 'linematch:') then
r.linematch = tonumber(string.sub(o, ('linematch:'):len() + 1))
end
end

return r
end

--- @type Gitsigns.Config
M.config = setmetatable({}, {
__index = function(t, k)
if rawget(t, k) == nil then
local field = M.schema[k]
if field then
rawset(t, k, resolve_default(field))
if not field then
return
end

rawset(t, k, resolve_default(field))

if field.refresh then
field.refresh(function()
rawset(t, k, resolve_default(field))
end)
end
end

return rawget(t, k)
end,
})
Expand Down Expand Up @@ -377,35 +426,18 @@ M.schema = {
diff_opts = {
type = 'table',
deep_extend = true,
default = function()
local r = {
algorithm = 'myers',
internal = false,
indent_heuristic = false,
vertical = true,
linematch = nil,
}
local diffopt = vim.opt.diffopt:get() --[[@as string[] ]]
for _, o in ipairs(diffopt) do
if o == 'indent-heuristic' then
r.indent_heuristic = true
elseif o == 'internal' then
if vim.diff then
r.internal = true
end
elseif o == 'horizontal' then
r.vertical = false
elseif vim.startswith(o, 'algorithm:') then
r.algorithm = string.sub(o, ('algorithm:'):len() + 1)
elseif vim.startswith(o, 'linematch:') then
r.linematch = tonumber(string.sub(o, ('linematch:'):len() + 1))
end
end
return r
refresh = function(callback)
vim.api.nvim_create_autocmd('OptionSet', {
group = vim.api.nvim_create_augroup('gitsigns.config.diff_opts', {}),
pattern = 'diffopt',
callback = callback,
})
end,
default = parse_diffopt,
default_help = "derived from 'diffopt'",
description = [[
Diff options.
Diff options. If the default value is used, then changes to 'diffopt' are
automatically applied.
Fields: ~
• algorithm: string
Expand All @@ -425,6 +457,16 @@ M.schema = {
• linematch: integer
Enable second-stage diff on hunks to align lines.
Requires `internal=true`.
• ignore_blank_lines: boolean
Ignore changes where lines are blank.
• ignore_whitespace_change: boolean
Ignore changes in amount of white space.
It should ignore adding trailing white space,
but not leading white space.
• ignore_whitespace: boolean
Ignore all white space changes.
• ignore_whitespace_change_at_eol: boolean
Ignore white space changes at end of line.
]],
},

Expand Down Expand Up @@ -847,6 +889,20 @@ local function handle_deprecated(cfg)
end
end

--- @param k string
--- @param v Gitsigns.SchemaElem
--- @param user_val any
local function build_field(k, v, user_val)
local config = M.config --[[@as table<string,any>]]

if v.deep_extend then
local d = resolve_default(v)
config[k] = vim.tbl_deep_extend('force', d, user_val)
else
config[k] = user_val
end
end

--- @param user_config Gitsigns.Config|nil
function M.build(user_config)
user_config = user_config or {}
Expand All @@ -855,14 +911,13 @@ function M.build(user_config)

validate_config(user_config)

local config = M.config --[[@as table<string,any>]]
for k, v in pairs(M.schema) do
if user_config[k] ~= nil then
if v.deep_extend then
local d = resolve_default(v)
config[k] = vim.tbl_deep_extend('force', d, user_config[k])
else
config[k] = user_config[k]
build_field(k, v, user_config[k])
if v.refresh then
v.refresh(function()
build_field(k, v, user_config[k])
end)
end
end
end
Expand Down
4 changes: 2 additions & 2 deletions lua/gitsigns/diff.lua
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
local config = require('gitsigns.config').config

--- @alias Gitsigns.Difffn fun(fa: string[], fb: string[], algorithm?: string, indent_heuristic?: boolean, linematch?: integer): Gitsigns.Hunk.Hunk[]
--- @alias Gitsigns.Difffn fun(fa: string[], fb: string[], linematch?: integer): Gitsigns.Hunk.Hunk[]

--- @param a string[]
--- @param b string[]
Expand Down Expand Up @@ -28,5 +28,5 @@ return function(a, b, linematch)
linematch0 = diff_opts.linematch
end

return f(a, b, diff_opts.algorithm, diff_opts.indent_heuristic, linematch0)
return f(a, b, linematch0)
end
8 changes: 4 additions & 4 deletions lua/gitsigns/diff_ext.lua
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
local config = require('gitsigns.config').config
local git_diff = require('gitsigns.git').diff

local gs_hunks = require('gitsigns.hunks')
Expand All @@ -24,10 +25,8 @@ end
--- @async
--- @param text_cmp string[]
--- @param text_buf string[]
--- @param diff_algo string
--- @param indent_heuristic? boolean
--- @return Gitsigns.Hunk.Hunk[]
function M.run_diff(text_cmp, text_buf, diff_algo, indent_heuristic)
function M.run_diff(text_cmp, text_buf)
local results = {} --- @type Gitsigns.Hunk.Hunk[]

-- tmpname must not be called in a callback
Expand Down Expand Up @@ -57,7 +56,8 @@ function M.run_diff(text_cmp, text_buf, diff_algo, indent_heuristic)
-- We can safely ignore the warning, we turn it off by passing the '-c
-- "core.safecrlf=false"' argument to git-diff.

local out = git_diff(file_cmp, file_buf, indent_heuristic, diff_algo)
local opts = config.diff_opts
local out = git_diff(file_cmp, file_buf, opts.indent_heuristic, opts.algorithm)

for _, line in ipairs(out) do
if vim.startswith(line, '@@') then
Expand Down
56 changes: 38 additions & 18 deletions lua/gitsigns/diff_int.lua
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,19 @@ local M = {}
--- @alias Gitsigns.Region {[1]:integer, [2]:string, [3]:integer, [4]:integer}

--- @alias Gitsigns.RawHunk {[1]:integer, [2]:integer, [3]:integer, [4]:integer}
--- @alias Gitsigns.RawDifffn fun(a: string, b: string, algorithm?: string, indent_heuristic?: boolean, linematch?: integer): Gitsigns.RawHunk[]
--- @alias Gitsigns.RawDifffn fun(a: string, b: string, linematch?: integer): Gitsigns.RawHunk[]

--- @type Gitsigns.RawDifffn
local run_diff_xdl = function(a, b, algorithm, indent_heuristic, linematch)
local run_diff_xdl = function(a, b, linematch)
local opts = config.diff_opts
return vim.diff(a, b, {
result_type = 'indices',
algorithm = algorithm,
indent_heuristic = indent_heuristic,
algorithm = opts.algorithm,
indent_heuristic = opts.indent_heuristic,
ignore_whitespace = opts.ignore_whitespace,
ignore_whitespace_change = opts.ignore_whitespace_change,
ignore_whitespace_change_at_eol = opts.ignore_whitespace_change_at_eol,
ignore_blank_lines = opts.ignore_blank_lines,
linematch = linematch,
}) --[[@as Gitsigns.RawHunk[] ]]
end
Expand All @@ -23,45 +28,60 @@ end
local run_diff_xdl_async = async.wrap(
--- @param a string
--- @param b string
--- @param algorithm? string
--- @param indent_heuristic? boolean
--- @param linematch? integer
--- @param callback fun(hunks: Gitsigns.RawHunk[])
function(a, b, algorithm, indent_heuristic, linematch, callback)
function(a, b, linematch, callback)
local opts = config.diff_opts
local function toflag(f, pos)
return f and bit.lshift(1, pos) or 0
end

local flags = toflag(opts.indent_heuristic, 0)
+ toflag(opts.ignore_whitespace, 1)
+ toflag(opts.ignore_whitespace_change, 2)
+ toflag(opts.ignore_whitespace_change_at_eol, 3)
+ toflag(opts.ignore_blank_lines, 4)

vim.loop
.new_work(
--- @param a0 string
--- @param b0 string
--- @param algorithm0 string
--- @param indent_heuristic0 integer
--- @param algorithm string
--- @param flags0 integer
--- @param linematch0 integer
--- @return string
function(a0, b0, algorithm0, indent_heuristic0, linematch0)
function(a0, b0, algorithm, flags0, linematch0)
local function flagval(pos)
return bit.band(flags0, bit.lshift(1, pos)) ~= 0
end

--- @diagnostic disable-next-line:return-type-mismatch
return vim.mpack.encode(vim.diff(a0, b0, {
result_type = 'indices',
algorithm = algorithm0,
indent_heuristic = indent_heuristic0,
algorithm = algorithm,
linematch = linematch0,
indent_heuristic = flagval(0),
ignore_whitespace = flagval(1),
ignore_whitespace_change = flagval(2),
ignore_whitespace_change_at_eol = flagval(3),
ignore_blank_lines = flagval(4),
}))
end,
--- @param r string
function(r)
callback(vim.mpack.decode(r) --[[@as Gitsigns.RawHunk[] ]])
end
)
:queue(a, b, algorithm, indent_heuristic, linematch)
:queue(a, b, opts.algorithm, flags, linematch)
end,
6
4
)

--- @param fa string[]
--- @param fb string[]
--- @param diff_algo? string
--- @param indent_heuristic? boolean
--- @param linematch? integer
--- @return Gitsigns.Hunk.Hunk[]
function M.run_diff(fa, fb, diff_algo, indent_heuristic, linematch)
function M.run_diff(fa, fb, linematch)
local run_diff0 --- @type Gitsigns.RawDifffn
if config._threaded_diff and vim.is_thread then
run_diff0 = run_diff_xdl_async
Expand All @@ -72,7 +92,7 @@ function M.run_diff(fa, fb, diff_algo, indent_heuristic, linematch)
local a = table.concat(fa, '\n')
local b = table.concat(fb, '\n')

local results = run_diff0(a, b, diff_algo, indent_heuristic, linematch)
local results = run_diff0(a, b, linematch)

local hunks = {} --- @type Gitsigns.Hunk.Hunk[]
for _, r in ipairs(results) do
Expand Down

0 comments on commit 8aedc8b

Please sign in to comment.