Add support for vim.xdl_diff
Requires PR neovim/neovim/#14536
This commit is contained in:
parent
368c7f65aa
commit
1ddb1f64f5
|
@ -17,10 +17,14 @@ on:
|
|||
jobs:
|
||||
# This workflow contains a single job called "build"
|
||||
build:
|
||||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
neovim_branch: ['v0.5.0', 'nightly']
|
||||
# The type of runner that the job will run on
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
NEOVIM_BRANCH: v0.5.0
|
||||
NEOVIM_BRANCH: ${{ matrix.neovim_branch }}
|
||||
|
||||
# Steps represent a sequence of tasks that will be executed as part of the job
|
||||
steps:
|
||||
|
|
|
@ -153,7 +153,7 @@ Feature | gitsigns
|
|||
---------------------------------------------------------|----------------------|-----------------------------------------------|--------
|
||||
Shows signs for added, modified, and removed lines | :white_check_mark: | :white_check_mark: |
|
||||
Asynchronous | :white_check_mark: | :white_check_mark: |
|
||||
Runs diffs in-process (no IO or pipes) | :white_check_mark: * | | * Via FFI and soon via [lua](https://github.com/neovim/neovim/pull/14536)
|
||||
Runs diffs in-process (no IO or pipes) | :white_check_mark: * | | * Via [lua](https://github.com/neovim/neovim/pull/14536) or FFI.
|
||||
Only adds signs for drawn lines | :white_check_mark: * | | * Via Neovims decoration API
|
||||
Updates immediately | :white_check_mark: | * | * Triggered on CursorHold
|
||||
Ensures signs are always up to date | :white_check_mark: * | | * Watches the git index to do so
|
||||
|
|
|
@ -14,13 +14,13 @@ feature set which includes (but not limited to):
|
|||
• Provides signs in the |signcolumn| to show changed/added/removed lines.
|
||||
• Mappings to operate on hunks to stage, undo or reset against Git's index.
|
||||
|
||||
Gitsigns is implemented entirely in Lua which is built into Neovim and because
|
||||
of this requires no external dependencies. This is unlike other plugins that
|
||||
require python, node, etc, which need to communicate with Neovim using |RPC|.
|
||||
By default, Gitsigns also uses Neovim's built-in diff library and runs
|
||||
in-process via LuaJIT's FFI module. This is unlike other similar plugins that
|
||||
need to run `git-diff` as an external process which is less efficient, has
|
||||
tighter bottlenecks and requires file IO.
|
||||
Gitsigns is implemented entirely in Lua which is built into Neovim and
|
||||
requires no external dependencies other than git. This is unlike other plugins
|
||||
that require python, node, etc, which need to communicate with Neovim using
|
||||
|RPC|. By default, Gitsigns also uses Neovim's built-in diff library
|
||||
(`vim.diff`) unlike other similar plugins that need to run `git-diff` as an
|
||||
external process which is less efficient, has tighter bottlenecks and requires
|
||||
file IO.
|
||||
|
||||
==============================================================================
|
||||
USAGE *gitsigns-usage*
|
||||
|
@ -519,11 +519,12 @@ update_debounce *gitsigns-config-update_debounce*
|
|||
Debounce time for updates (in milliseconds).
|
||||
|
||||
use_internal_diff *gitsigns-config-use_internal_diff*
|
||||
Type: `boolean`, Default: `true` if luajit is present (windows unsupported)
|
||||
Type: `boolean`, Default: `true` if `vim.diff` or luajit is present. Windows unsupported on v0.5
|
||||
|
||||
Use Neovim's built in xdiff library for running diffs.
|
||||
|
||||
This uses LuaJIT's FFI interface.
|
||||
Note Neovim v0.5 uses LuaJIT's FFI interface, whereas v0.5+ uses
|
||||
`vim.diff`.
|
||||
|
||||
current_line_blame *gitsigns-config-current_line_blame*
|
||||
Type: `boolean`, Default: `false`
|
||||
|
|
|
@ -361,7 +361,7 @@ M.preview_hunk = function()
|
|||
vim.cmd([[autocmd CursorMoved,CursorMovedI <buffer> ++once silent! unlet b:_gitsigns_preview_open]])
|
||||
|
||||
if config.use_internal_diff then
|
||||
local regions = require('gitsigns.diff_ffi').run_word_diff(hunk.lines)
|
||||
local regions = require('gitsigns.diff_int').run_word_diff(hunk.lines)
|
||||
local offset = #lines - #hunk.lines
|
||||
for _, region in ipairs(regions) do
|
||||
local line, scol, ecol = region[1], region[3], region[4]
|
||||
|
@ -537,7 +537,7 @@ end
|
|||
|
||||
local function run_diff(a, b)
|
||||
if config.use_internal_diff then
|
||||
return require('gitsigns.diff_ffi').run_diff(a, b, config.diff_algorithm)
|
||||
return require('gitsigns.diff_int').run_diff(a, b, config.diff_algorithm)
|
||||
else
|
||||
return require('gitsigns.diff_ext').run_diff(a, b, config.diff_algorithm)
|
||||
end
|
||||
|
|
|
@ -353,17 +353,20 @@ M.schema = {
|
|||
use_internal_diff = {
|
||||
type = 'boolean',
|
||||
default = function()
|
||||
if not jit or jit.os == "Windows" then
|
||||
if vim.diff then
|
||||
return true
|
||||
elseif not jit or jit.os == "Windows" then
|
||||
return false
|
||||
else
|
||||
return true
|
||||
end
|
||||
end,
|
||||
default_help = "`true` if luajit is present (windows unsupported)",
|
||||
default_help = "`true` if `vim.diff` or luajit is present. Windows unsupported on v0.5",
|
||||
description = [[
|
||||
Use Neovim's built in xdiff library for running diffs.
|
||||
|
||||
This uses LuaJIT's FFI interface.
|
||||
Note Neovim v0.5 uses LuaJIT's FFI interface, whereas v0.5+ uses
|
||||
`vim.diff`.
|
||||
]],
|
||||
},
|
||||
|
||||
|
|
|
@ -1,152 +1,22 @@
|
|||
local create_hunk = require("gitsigns.hunks").create_hunk
|
||||
local Hunk = require('gitsigns.hunks').Hunk
|
||||
|
||||
local ffi = require("ffi")
|
||||
|
||||
ffi.cdef([[
|
||||
typedef struct s_mmbuffer { const char *ptr; long size; } mmbuffer_t;
|
||||
|
||||
typedef struct s_xpparam {
|
||||
unsigned long flags;
|
||||
|
||||
// See Documentation/diff-options.txt.
|
||||
char **anchors;
|
||||
size_t anchors_nr;
|
||||
} xpparam_t;
|
||||
|
||||
typedef long (__stdcall *find_func_t)(
|
||||
const char *line,
|
||||
long line_len,
|
||||
char *buffer,
|
||||
long buffer_size,
|
||||
void *priv
|
||||
);
|
||||
|
||||
typedef int (__stdcall *xdl_emit_hunk_consume_func_t)(
|
||||
long start_a, long count_a, long start_b, long count_b,
|
||||
void *cb_data
|
||||
);
|
||||
|
||||
typedef struct s_xdemitconf {
|
||||
long ctxlen;
|
||||
long interhunkctxlen;
|
||||
unsigned long flags;
|
||||
find_func_t find_func;
|
||||
void *find_func_priv;
|
||||
xdl_emit_hunk_consume_func_t hunk_func;
|
||||
} xdemitconf_t;
|
||||
|
||||
typedef struct s_xdemitcb {
|
||||
void *priv;
|
||||
int (__stdcall *outf)(void *, mmbuffer_t *, int);
|
||||
} xdemitcb_t;
|
||||
|
||||
int xdl_diff(
|
||||
mmbuffer_t *mf1,
|
||||
mmbuffer_t *mf2,
|
||||
xpparam_t const *xpp,
|
||||
xdemitconf_t const *xecfg,
|
||||
xdemitcb_t *ecb
|
||||
);
|
||||
]])
|
||||
|
||||
local MMBuffer = {}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
local function setup_mmbuffer(lines)
|
||||
local text = vim.tbl_isempty(lines) and '' or table.concat(lines, '\n') .. '\n'
|
||||
return text, #text
|
||||
end
|
||||
|
||||
local XPParam = {}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
local function get_xpparam_flag(diff_algo)
|
||||
local daflag = 0
|
||||
|
||||
if diff_algo == 'minimal' then daflag = 1
|
||||
elseif diff_algo == 'patience' then daflag = math.floor(2 ^ 14)
|
||||
elseif diff_algo == 'histogram' then daflag = math.floor(2 ^ 15)
|
||||
end
|
||||
|
||||
return daflag
|
||||
end
|
||||
|
||||
local Long = {}
|
||||
|
||||
|
||||
|
||||
local XDEmitConf = {}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
local M = {}
|
||||
|
||||
local DiffResult = {}
|
||||
|
||||
local mmba = ffi.new('mmbuffer_t')
|
||||
local mmbb = ffi.new('mmbuffer_t')
|
||||
local xpparam = ffi.new('xpparam_t')
|
||||
local emitcb = ffi.new('xdemitcb_t')
|
||||
local run_diff_xdl
|
||||
|
||||
local function run_diff_xdl(fa, fb, diff_algo)
|
||||
mmba.ptr, mmba.size = setup_mmbuffer(fa)
|
||||
mmbb.ptr, mmbb.size = setup_mmbuffer(fb)
|
||||
xpparam.flags = get_xpparam_flag(diff_algo)
|
||||
|
||||
local results = {}
|
||||
|
||||
local hunk_func = ffi.cast('xdl_emit_hunk_consume_func_t', function(
|
||||
start_a, count_a, start_b, count_b)
|
||||
|
||||
local ca = tonumber(count_a)
|
||||
local cb = tonumber(count_b)
|
||||
local sa = tonumber(start_a)
|
||||
local sb = tonumber(start_b)
|
||||
|
||||
|
||||
|
||||
if ca > 0 then sa = sa + 1 end
|
||||
if cb > 0 then sb = sb + 1 end
|
||||
|
||||
results[#results + 1] = { sa, ca, sb, cb }
|
||||
return 0
|
||||
end)
|
||||
|
||||
local emitconf = ffi.new('xdemitconf_t')
|
||||
emitconf.hunk_func = hunk_func
|
||||
|
||||
local ok = ffi.C.xdl_diff(mmba, mmbb, xpparam, emitconf, emitcb)
|
||||
|
||||
hunk_func:free()
|
||||
|
||||
return ok == 0 and results
|
||||
if vim.diff then
|
||||
run_diff_xdl = function(fa, fb, algorithm)
|
||||
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 })
|
||||
end
|
||||
else
|
||||
run_diff_xdl = require('gitsigns.diff_int.xdl_diff_ffi')
|
||||
end
|
||||
|
||||
jit.off(run_diff_xdl)
|
||||
|
||||
function M.run_diff(fa, fb, diff_algo)
|
||||
local results = run_diff_xdl(fa, fb, diff_algo)
|
||||
|
|
@ -0,0 +1,145 @@
|
|||
local ffi = require("ffi")
|
||||
|
||||
ffi.cdef([[
|
||||
typedef struct s_mmbuffer { const char *ptr; long size; } mmbuffer_t;
|
||||
|
||||
typedef struct s_xpparam {
|
||||
unsigned long flags;
|
||||
|
||||
// See Documentation/diff-options.txt.
|
||||
char **anchors;
|
||||
size_t anchors_nr;
|
||||
} xpparam_t;
|
||||
|
||||
typedef long (__stdcall *find_func_t)(
|
||||
const char *line,
|
||||
long line_len,
|
||||
char *buffer,
|
||||
long buffer_size,
|
||||
void *priv
|
||||
);
|
||||
|
||||
typedef int (__stdcall *xdl_emit_hunk_consume_func_t)(
|
||||
long start_a, long count_a, long start_b, long count_b,
|
||||
void *cb_data
|
||||
);
|
||||
|
||||
typedef struct s_xdemitconf {
|
||||
long ctxlen;
|
||||
long interhunkctxlen;
|
||||
unsigned long flags;
|
||||
find_func_t find_func;
|
||||
void *find_func_priv;
|
||||
xdl_emit_hunk_consume_func_t hunk_func;
|
||||
} xdemitconf_t;
|
||||
|
||||
typedef struct s_xdemitcb {
|
||||
void *priv;
|
||||
int (__stdcall *outf)(void *, mmbuffer_t *, int);
|
||||
} xdemitcb_t;
|
||||
|
||||
int xdl_diff(
|
||||
mmbuffer_t *mf1,
|
||||
mmbuffer_t *mf2,
|
||||
xpparam_t const *xpp,
|
||||
xdemitconf_t const *xecfg,
|
||||
xdemitcb_t *ecb
|
||||
);
|
||||
]])
|
||||
|
||||
local MMBuffer = {}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
local function setup_mmbuffer(lines)
|
||||
local text = vim.tbl_isempty(lines) and '' or table.concat(lines, '\n') .. '\n'
|
||||
return text, #text
|
||||
end
|
||||
|
||||
local XPParam = {}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
local function get_xpparam_flag(diff_algo)
|
||||
local daflag = 0
|
||||
|
||||
if diff_algo == 'minimal' then daflag = 1
|
||||
elseif diff_algo == 'patience' then daflag = math.floor(2 ^ 14)
|
||||
elseif diff_algo == 'histogram' then daflag = math.floor(2 ^ 15)
|
||||
end
|
||||
|
||||
return daflag
|
||||
end
|
||||
|
||||
local Long = {}
|
||||
|
||||
|
||||
|
||||
local XDEmitConf = {}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
local DiffResult = {}
|
||||
|
||||
local mmba = ffi.new('mmbuffer_t')
|
||||
local mmbb = ffi.new('mmbuffer_t')
|
||||
local xpparam = ffi.new('xpparam_t')
|
||||
local emitcb = ffi.new('xdemitcb_t')
|
||||
|
||||
local function run_diff_xdl(fa, fb, diff_algo)
|
||||
mmba.ptr, mmba.size = setup_mmbuffer(fa)
|
||||
mmbb.ptr, mmbb.size = setup_mmbuffer(fb)
|
||||
xpparam.flags = get_xpparam_flag(diff_algo)
|
||||
|
||||
local results = {}
|
||||
|
||||
local hunk_func = ffi.cast('xdl_emit_hunk_consume_func_t', function(
|
||||
start_a, count_a, start_b, count_b)
|
||||
|
||||
local ca = tonumber(count_a)
|
||||
local cb = tonumber(count_b)
|
||||
local sa = tonumber(start_a)
|
||||
local sb = tonumber(start_b)
|
||||
|
||||
|
||||
|
||||
if ca > 0 then sa = sa + 1 end
|
||||
if cb > 0 then sb = sb + 1 end
|
||||
|
||||
results[#results + 1] = { sa, ca, sb, cb }
|
||||
return 0
|
||||
end)
|
||||
|
||||
local emitconf = ffi.new('xdemitconf_t')
|
||||
emitconf.hunk_func = hunk_func
|
||||
|
||||
local ok = ffi.C.xdl_diff(mmba, mmbb, xpparam, emitconf, emitcb)
|
||||
|
||||
hunk_func:free()
|
||||
|
||||
return ok == 0 and results
|
||||
end
|
||||
|
||||
jit.off(run_diff_xdl)
|
||||
|
||||
return run_diff_xdl
|
|
@ -143,7 +143,7 @@ M.apply_word_diff = function(bufnr, row)
|
|||
for _, hunk in ipairs(cache[bufnr].hunks) do
|
||||
if lnum >= hunk.start and lnum <= hunk.vend then
|
||||
local size = #hunk.lines / 2
|
||||
local regions = require('gitsigns.diff_ffi').run_word_diff(hunk.lines)
|
||||
local regions = require('gitsigns.diff_int').run_word_diff(hunk.lines)
|
||||
for _, region in ipairs(regions) do
|
||||
local line = region[1]
|
||||
if lnum == hunk.start + line - size - 1 and
|
||||
|
@ -192,7 +192,7 @@ local update0 = function(bufnr, bcache)
|
|||
|
||||
local run_diff
|
||||
if config.use_internal_diff then
|
||||
run_diff = require('gitsigns.diff_ffi').run_diff
|
||||
run_diff = require('gitsigns.diff_int').run_diff
|
||||
else
|
||||
run_diff = require('gitsigns.diff_ext').run_diff
|
||||
end
|
||||
|
|
|
@ -361,7 +361,7 @@ M.preview_hunk = function()
|
|||
vim.cmd[[autocmd CursorMoved,CursorMovedI <buffer> ++once silent! unlet b:_gitsigns_preview_open]]
|
||||
|
||||
if config.use_internal_diff then
|
||||
local regions = require('gitsigns.diff_ffi').run_word_diff(hunk.lines)
|
||||
local regions = require('gitsigns.diff_int').run_word_diff(hunk.lines)
|
||||
local offset = #lines - #hunk.lines
|
||||
for _, region in ipairs(regions) do
|
||||
local line, scol, ecol = region[1], region[3], region[4]
|
||||
|
@ -537,7 +537,7 @@ end
|
|||
|
||||
local function run_diff(a: {string}, b: {string}): {Hunk}
|
||||
if config.use_internal_diff then
|
||||
return require('gitsigns.diff_ffi').run_diff(a, b, config.diff_algorithm)
|
||||
return require('gitsigns.diff_int').run_diff(a, b, config.diff_algorithm)
|
||||
else
|
||||
return require('gitsigns.diff_ext').run_diff(a, b, config.diff_algorithm)
|
||||
end
|
||||
|
|
|
@ -353,17 +353,20 @@ M.schema = {
|
|||
use_internal_diff = {
|
||||
type = 'boolean',
|
||||
default = function(): boolean
|
||||
if not jit or jit.os == "Windows" then
|
||||
if vim.diff then
|
||||
return true
|
||||
elseif not jit or jit.os == "Windows" then
|
||||
return false
|
||||
else
|
||||
return true
|
||||
end
|
||||
end,
|
||||
default_help = "`true` if luajit is present (windows unsupported)",
|
||||
default_help = "`true` if `vim.diff` or luajit is present. Windows unsupported on v0.5",
|
||||
description = [[
|
||||
Use Neovim's built in xdiff library for running diffs.
|
||||
|
||||
This uses LuaJIT's FFI interface.
|
||||
Note Neovim v0.5 uses LuaJIT's FFI interface, whereas v0.5+ uses
|
||||
`vim.diff`.
|
||||
]]
|
||||
},
|
||||
|
||||
|
|
|
@ -0,0 +1,115 @@
|
|||
local create_hunk = require("gitsigns.hunks").create_hunk
|
||||
local Hunk = require('gitsigns.hunks').Hunk
|
||||
|
||||
local M = {}
|
||||
|
||||
local type DiffResult = {integer, integer, integer, integer}
|
||||
|
||||
local run_diff_xdl: function({string}, {string}, diff_algo: string): {DiffResult}
|
||||
|
||||
if vim.diff then
|
||||
run_diff_xdl = function(fa: {string}, fb: {string}, algorithm: string): {DiffResult}
|
||||
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 })
|
||||
end
|
||||
else
|
||||
run_diff_xdl = require('gitsigns.diff_int.xdl_diff_ffi')
|
||||
end
|
||||
|
||||
function M.run_diff(fa: {string}, fb: {string}, diff_algo: string): {Hunk}
|
||||
local results = run_diff_xdl(fa, fb, diff_algo)
|
||||
|
||||
local hunks: {Hunk} = {}
|
||||
|
||||
for _, r in ipairs(results) do
|
||||
local rs, rc, as, ac = unpack(r)
|
||||
local hunk = create_hunk(rs, rc, as, ac)
|
||||
hunk.head = ('@@ -%d%s +%d%s @@'):format(
|
||||
rs, rc > 0 and ','..rc or '',
|
||||
as, ac > 0 and ','..ac or ''
|
||||
)
|
||||
if rc > 0 then
|
||||
for i = rs, rs+rc-1 do
|
||||
table.insert(hunk.lines, '-'..(fa[i] or ''))
|
||||
end
|
||||
end
|
||||
if ac > 0 then
|
||||
for i = as, as+ac-1 do
|
||||
table.insert(hunk.lines, '+'..(fb[i] or ''))
|
||||
end
|
||||
end
|
||||
table.insert(hunks, hunk)
|
||||
end
|
||||
|
||||
return hunks
|
||||
end
|
||||
|
||||
local type Region = {integer, string, integer, integer}
|
||||
|
||||
local gaps_between_regions = 5
|
||||
|
||||
function M.run_word_diff(hunk_body: {string}): {Region}
|
||||
local removed, added = 0, 0
|
||||
for _, line in ipairs(hunk_body) do
|
||||
if line:sub(1, 1) == '-' then
|
||||
removed = removed + 1
|
||||
elseif line:sub(1, 1) == '+' then
|
||||
added = added + 1
|
||||
end
|
||||
end
|
||||
|
||||
if removed ~= added then
|
||||
return {}
|
||||
end
|
||||
|
||||
local ret: {Region} = {}
|
||||
|
||||
for i = 1, removed do
|
||||
-- pair lines by position
|
||||
local rline = hunk_body[i]:sub(2)
|
||||
local aline = hunk_body[i + removed]:sub(2)
|
||||
|
||||
local a, b = vim.split(rline, ''), vim.split(aline, '')
|
||||
|
||||
local hunks0: {Hunk} = {}
|
||||
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
|
||||
|
||||
-- print(string.format('-%d,%d +%d,%d', rs, rc, as, ac))
|
||||
hunks0[#hunks0+1] = create_hunk(rs, rc, as, ac)
|
||||
end
|
||||
|
||||
-- Denoise the hunks
|
||||
local hunks = {hunks0[1]}
|
||||
for j = 2, #hunks0 do
|
||||
local h, n = hunks[#hunks], hunks0[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
|
||||
hunks[#hunks+1] = n
|
||||
end
|
||||
end
|
||||
|
||||
for _, h in ipairs(hunks) do
|
||||
local rem = {i , h.type, h.removed.start, h.removed.start + h.removed.count}
|
||||
local add = {i+removed, h.type, h.added.start , h.added.start + h.added.count}
|
||||
|
||||
ret[#ret+1] = rem
|
||||
ret[#ret+1] = add
|
||||
end
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
return M
|
|
@ -1,6 +1,3 @@
|
|||
local create_hunk = require("gitsigns.hunks").create_hunk
|
||||
local Hunk = require('gitsigns.hunks').Hunk
|
||||
|
||||
local ffi = require("ffi")
|
||||
|
||||
ffi.cdef[[
|
||||
|
@ -102,8 +99,6 @@ end
|
|||
-- local DIFF_CLOSE_OFF = 0x400 -- diffoff when closing window
|
||||
-- local DIFF_FOLLOWWRAP = 0x800 -- follow the wrap option
|
||||
|
||||
local M = {}
|
||||
|
||||
local type DiffResult = {integer, integer, integer, integer}
|
||||
|
||||
local mmba = ffi.new('mmbuffer_t') as MMBuffer
|
||||
|
@ -147,99 +142,4 @@ end
|
|||
|
||||
jit.off(run_diff_xdl)
|
||||
|
||||
function M.run_diff(fa: {string}, fb: {string}, diff_algo: string): {Hunk}
|
||||
local results = run_diff_xdl(fa, fb, diff_algo)
|
||||
|
||||
local hunks: {Hunk} = {}
|
||||
|
||||
for _, r in ipairs(results) do
|
||||
local rs, rc, as, ac = unpack(r)
|
||||
local hunk = create_hunk(rs, rc, as, ac)
|
||||
hunk.head = ('@@ -%d%s +%d%s @@'):format(
|
||||
rs, rc > 0 and ','..rc or '',
|
||||
as, ac > 0 and ','..ac or ''
|
||||
)
|
||||
if rc > 0 then
|
||||
for i = rs, rs+rc-1 do
|
||||
table.insert(hunk.lines, '-'..(fa[i] or ''))
|
||||
end
|
||||
end
|
||||
if ac > 0 then
|
||||
for i = as, as+ac-1 do
|
||||
table.insert(hunk.lines, '+'..(fb[i] or ''))
|
||||
end
|
||||
end
|
||||
table.insert(hunks, hunk)
|
||||
end
|
||||
|
||||
return hunks
|
||||
end
|
||||
|
||||
local type Region = {integer, string, integer, integer}
|
||||
|
||||
local gaps_between_regions = 5
|
||||
|
||||
function M.run_word_diff(hunk_body: {string}): {Region}
|
||||
local removed, added = 0, 0
|
||||
for _, line in ipairs(hunk_body) do
|
||||
if line:sub(1, 1) == '-' then
|
||||
removed = removed + 1
|
||||
elseif line:sub(1, 1) == '+' then
|
||||
added = added + 1
|
||||
end
|
||||
end
|
||||
|
||||
if removed ~= added then
|
||||
return {}
|
||||
end
|
||||
|
||||
local ret: {Region} = {}
|
||||
|
||||
for i = 1, removed do
|
||||
-- pair lines by position
|
||||
local rline = hunk_body[i]:sub(2)
|
||||
local aline = hunk_body[i + removed]:sub(2)
|
||||
|
||||
local a, b = vim.split(rline, ''), vim.split(aline, '')
|
||||
|
||||
local hunks0: {Hunk} = {}
|
||||
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
|
||||
|
||||
-- print(string.format('-%d,%d +%d,%d', rs, rc, as, ac))
|
||||
hunks0[#hunks0+1] = create_hunk(rs, rc, as, ac)
|
||||
end
|
||||
|
||||
-- Denoise the hunks
|
||||
local hunks = {hunks0[1]}
|
||||
for j = 2, #hunks0 do
|
||||
local h, n = hunks[#hunks], hunks0[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
|
||||
hunks[#hunks+1] = n
|
||||
end
|
||||
end
|
||||
|
||||
for _, h in ipairs(hunks) do
|
||||
local rem = {i , h.type, h.removed.start, h.removed.start + h.removed.count}
|
||||
local add = {i+removed, h.type, h.added.start , h.added.start + h.added.count}
|
||||
|
||||
ret[#ret+1] = rem
|
||||
ret[#ret+1] = add
|
||||
end
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
return M
|
||||
return run_diff_xdl
|
|
@ -143,7 +143,7 @@ M.apply_word_diff = function(bufnr: integer, row: integer)
|
|||
for _, hunk in ipairs(cache[bufnr].hunks) do
|
||||
if lnum >= hunk.start and lnum <= hunk.vend then
|
||||
local size = #hunk.lines / 2
|
||||
local regions = require('gitsigns.diff_ffi').run_word_diff(hunk.lines)
|
||||
local regions = require('gitsigns.diff_int').run_word_diff(hunk.lines)
|
||||
for _, region in ipairs(regions) do
|
||||
local line = region[1]
|
||||
if lnum == hunk.start + line - size - 1
|
||||
|
@ -192,7 +192,7 @@ local update0 = function(bufnr: integer, bcache: CacheEntry)
|
|||
-- See https://github.com/neovim/neovim/issues/15147
|
||||
local run_diff: function({string}, {string}, string): {Hunk}
|
||||
if config.use_internal_diff then
|
||||
run_diff = require('gitsigns.diff_ffi').run_diff
|
||||
run_diff = require('gitsigns.diff_int').run_diff
|
||||
else
|
||||
run_diff = require('gitsigns.diff_ext').run_diff
|
||||
end
|
||||
|
|
|
@ -227,6 +227,8 @@ global record vim
|
|||
|
||||
deepcopy: function<T>(T): T
|
||||
|
||||
diff: function(string|{string}, string|{string}, table)
|
||||
|
||||
record o
|
||||
diffopt: string
|
||||
wrapscan: boolean
|
||||
|
|
Loading…
Reference in New Issue