diff --git a/doc/gitsigns.txt b/doc/gitsigns.txt index d481a0c0..9044e75e 100644 --- a/doc/gitsigns.txt +++ b/doc/gitsigns.txt @@ -646,6 +646,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 diff --git a/lua/gitsigns/config.lua b/lua/gitsigns/config.lua index ba03e911..64c681ba 100644 --- a/lua/gitsigns/config.lua +++ b/lua/gitsigns/config.lua @@ -6,12 +6,16 @@ --- @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 @@ -99,6 +103,41 @@ 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) @@ -378,30 +417,15 @@ M.schema = { 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 + vim.api.nvim_create_autocmd('OptionSet', { + group = vim.api.nvim_create_augroup('gitsigns.config.diff_opts', {}), + pattern = 'diffopt', + callback = function() + M.config.diff_opts = parse_diffopt() + end, + }) + + return parse_diffopt() end, default_help = "derived from 'diffopt'", description = [[ @@ -425,6 +449,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. ]], }, diff --git a/lua/gitsigns/diff.lua b/lua/gitsigns/diff.lua index d4f3c207..eb823de2 100644 --- a/lua/gitsigns/diff.lua +++ b/lua/gitsigns/diff.lua @@ -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[] @@ -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 diff --git a/lua/gitsigns/diff_ext.lua b/lua/gitsigns/diff_ext.lua index e7dff623..794a0683 100644 --- a/lua/gitsigns/diff_ext.lua +++ b/lua/gitsigns/diff_ext.lua @@ -1,3 +1,4 @@ +local config = require('gitsigns.config').config local git_diff = require('gitsigns.git').diff local gs_hunks = require('gitsigns.hunks') @@ -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 @@ -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 diff --git a/lua/gitsigns/diff_int.lua b/lua/gitsigns/diff_int.lua index 2aae8622..96c391ff 100644 --- a/lua/gitsigns/diff_int.lua +++ b/lua/gitsigns/diff_int.lua @@ -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 @@ -23,26 +28,43 @@ 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 @@ -50,18 +72,16 @@ local run_diff_xdl_async = async.wrap( 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 @@ -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