local create_hunk = require("gitsigns.hunks").create_hunk local Hunk = require('gitsigns.hunks').Hunk local config = require('gitsigns.config').config local async = require('gitsigns.async') local M = {} local run_diff_xdl = function( fa, fb, algorithm, indent_heuristic, linematch) local a = vim.tbl_isempty(fa) and '' or table.concat(fa, '\n') .. '\n' local b = vim.tbl_isempty(fb) and '' or table.concat(fb, '\n') .. '\n' return vim.diff(a, b, { result_type = 'indices', algorithm = algorithm, indent_heuristic = indent_heuristic, linematch = linematch, }) end local run_diff_xdl_async = async.wrap(function( fa, fb, algorithm, indent_heuristic, linematch, callback) local a = vim.tbl_isempty(fa) and '' or table.concat(fa, '\n') .. '\n' local b = vim.tbl_isempty(fb) and '' or table.concat(fb, '\n') .. '\n' vim.loop.new_work(function( a0, b0, algorithm0, indent_heuristic0, linematch0) return vim.mpack.encode(vim.diff(a0, b0, { result_type = 'indices', algorithm = algorithm0, indent_heuristic = indent_heuristic0, linematch = linematch0, })) end, function(r) callback(vim.mpack.decode(r)) end):queue(a, b, algorithm, indent_heuristic, linematch) end, 6) M.run_diff = async.void(function( fa, fb, diff_algo, indent_heuristic, linematch) local run_diff0 if config._threaded_diff and vim.is_thread then run_diff0 = run_diff_xdl_async else run_diff0 = run_diff_xdl end local results = run_diff0(fa, fb, diff_algo, indent_heuristic, linematch) local hunks = {} for _, r in ipairs(results) do local rs, rc, as, ac = unpack(r) local hunk = create_hunk(rs, rc, as, ac) if rc > 0 then for i = rs, rs + rc - 1 do hunk.removed.lines[#hunk.removed.lines + 1] = fa[i] or '' end end if ac > 0 then for i = as, as + ac - 1 do hunk.added.lines[#hunk.added.lines + 1] = fb[i] or '' end end hunks[#hunks + 1] = hunk end return hunks end) local gaps_between_regions = 5 local function denoise_hunks(hunks) -- Denoise the hunks local ret = { hunks[1] } for j = 2, #hunks do local h, n = ret[#ret], hunks[j] if not h or not n then break end if n.added.start - h.added.start - h.added.count < gaps_between_regions then h.added.count = n.added.start + n.added.count - h.added.start h.removed.count = n.removed.start + n.removed.count - h.removed.start if h.added.count > 0 or h.removed.count > 0 then h.type = 'change' end else ret[#ret + 1] = n end end return ret end function M.run_word_diff(removed, added) local adds = {} local rems = {} if #removed ~= #added then return rems, adds end for i = 1, #removed do -- pair lines by position local a, b = vim.split(removed[i], ''), vim.split(added[i], '') local hunks = {} for _, r in ipairs(run_diff_xdl(a, b)) do local rs, rc, as, ac = unpack(r) -- Balance of the unknown offset done in hunk_func if rc == 0 then rs = rs + 1 end if ac == 0 then as = as + 1 end hunks[#hunks + 1] = create_hunk(rs, rc, as, ac) end hunks = denoise_hunks(hunks) for _, h in ipairs(hunks) do adds[#adds + 1] = { i, h.type, h.added.start, h.added.start + h.added.count } rems[#rems + 1] = { i, h.type, h.removed.start, h.removed.start + h.removed.count } end end return rems, adds end return M