feat: refactor file structure
This commit is contained in:
parent
9761cded88
commit
965e47a1df
17 changed files with 19 additions and 22 deletions
189
lua/cp/diff.lua
189
lua/cp/diff.lua
|
|
@ -1,189 +0,0 @@
|
|||
---@class DiffResult
|
||||
---@field content string[]
|
||||
---@field highlights table[]?
|
||||
---@field raw_diff string?
|
||||
|
||||
---@class DiffBackend
|
||||
---@field name string
|
||||
---@field render fun(expected: string, actual: string): DiffResult
|
||||
|
||||
local M = {}
|
||||
|
||||
---@type DiffBackend
|
||||
local vim_backend = {
|
||||
name = 'vim',
|
||||
render = function(_, actual)
|
||||
local actual_lines = vim.split(actual, '\n', { plain = true, trimempty = true })
|
||||
|
||||
return {
|
||||
content = actual_lines,
|
||||
highlights = nil,
|
||||
}
|
||||
end,
|
||||
}
|
||||
|
||||
---@type DiffBackend
|
||||
local git_backend = {
|
||||
name = 'git',
|
||||
render = function(expected, actual)
|
||||
local tmp_expected = vim.fn.tempname()
|
||||
local tmp_actual = vim.fn.tempname()
|
||||
|
||||
vim.fn.writefile(vim.split(expected, '\n', { plain = true }), tmp_expected)
|
||||
vim.fn.writefile(vim.split(actual, '\n', { plain = true }), tmp_actual)
|
||||
|
||||
local cmd = {
|
||||
'git',
|
||||
'diff',
|
||||
'--no-index',
|
||||
'--word-diff=plain',
|
||||
'--word-diff-regex=.',
|
||||
'--no-prefix',
|
||||
tmp_expected,
|
||||
tmp_actual,
|
||||
}
|
||||
|
||||
local result = vim.system(cmd, { text = true }):wait()
|
||||
|
||||
vim.fn.delete(tmp_expected)
|
||||
vim.fn.delete(tmp_actual)
|
||||
|
||||
if result.code == 0 then
|
||||
return {
|
||||
content = vim.split(actual, '\n', { plain = true, trimempty = true }),
|
||||
highlights = {},
|
||||
}
|
||||
else
|
||||
local diff_content = result.stdout or ''
|
||||
local lines = {}
|
||||
local highlights = {}
|
||||
local line_num = 0
|
||||
|
||||
for line in diff_content:gmatch('[^\n]*') do
|
||||
if
|
||||
line:match('^[%s%+%-]')
|
||||
or (not line:match('^[@%-+]') and not line:match('^index') and not line:match('^diff'))
|
||||
then
|
||||
local clean_line = line
|
||||
if line:match('^[%+%-]') then
|
||||
clean_line = line:sub(2)
|
||||
end
|
||||
|
||||
local col_pos = 0
|
||||
local processed_line = ''
|
||||
local i = 1
|
||||
|
||||
while i <= #clean_line do
|
||||
local removed_start, removed_end = clean_line:find('%[%-[^%-]*%-]', i)
|
||||
local added_start, added_end = clean_line:find('{%+[^%+]*%+}', i)
|
||||
|
||||
local next_marker_start = nil
|
||||
local marker_type = nil
|
||||
|
||||
if removed_start and (not added_start or removed_start < added_start) then
|
||||
next_marker_start = removed_start
|
||||
marker_type = 'removed'
|
||||
elseif added_start then
|
||||
next_marker_start = added_start
|
||||
marker_type = 'added'
|
||||
end
|
||||
|
||||
if next_marker_start then
|
||||
if next_marker_start > i then
|
||||
local before_text = clean_line:sub(i, next_marker_start - 1)
|
||||
processed_line = processed_line .. before_text
|
||||
col_pos = col_pos + #before_text
|
||||
end
|
||||
|
||||
local marker_end = (marker_type == 'removed') and removed_end or added_end
|
||||
local marker_text = clean_line:sub(next_marker_start, marker_end)
|
||||
local content_text
|
||||
|
||||
if marker_type == 'removed' then
|
||||
content_text = marker_text:sub(3, -3)
|
||||
table.insert(highlights, {
|
||||
line = line_num,
|
||||
col_start = col_pos,
|
||||
col_end = col_pos + #content_text,
|
||||
highlight_group = 'DiffDelete',
|
||||
})
|
||||
else
|
||||
content_text = marker_text:sub(3, -3)
|
||||
table.insert(highlights, {
|
||||
line = line_num,
|
||||
col_start = col_pos,
|
||||
col_end = col_pos + #content_text,
|
||||
highlight_group = 'DiffAdd',
|
||||
})
|
||||
end
|
||||
|
||||
processed_line = processed_line .. content_text
|
||||
col_pos = col_pos + #content_text
|
||||
i = marker_end + 1
|
||||
else
|
||||
local rest = clean_line:sub(i)
|
||||
processed_line = processed_line .. rest
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
table.insert(lines, processed_line)
|
||||
line_num = line_num + 1
|
||||
end
|
||||
end
|
||||
|
||||
return {
|
||||
content = lines,
|
||||
highlights = highlights,
|
||||
raw_diff = diff_content,
|
||||
}
|
||||
end
|
||||
end,
|
||||
}
|
||||
|
||||
---@type table<string, DiffBackend>
|
||||
local backends = {
|
||||
vim = vim_backend,
|
||||
git = git_backend,
|
||||
}
|
||||
|
||||
---@return string[]
|
||||
function M.get_available_backends()
|
||||
return vim.tbl_keys(backends)
|
||||
end
|
||||
|
||||
---@param name string
|
||||
---@return DiffBackend?
|
||||
function M.get_backend(name)
|
||||
return backends[name]
|
||||
end
|
||||
|
||||
---@return boolean
|
||||
function M.is_git_available()
|
||||
local result = vim.system({ 'git', '--version' }, { text = true }):wait()
|
||||
return result.code == 0
|
||||
end
|
||||
|
||||
---@param preferred_backend? string
|
||||
---@return DiffBackend
|
||||
function M.get_best_backend(preferred_backend)
|
||||
if preferred_backend and backends[preferred_backend] then
|
||||
if preferred_backend == 'git' and not M.is_git_available() then
|
||||
return backends.vim
|
||||
end
|
||||
return backends[preferred_backend]
|
||||
end
|
||||
|
||||
return backends.vim
|
||||
end
|
||||
|
||||
---@param expected string
|
||||
---@param actual string
|
||||
---@param backend_name? string
|
||||
---@return DiffResult
|
||||
function M.render_diff(expected, actual, backend_name)
|
||||
local backend = M.get_best_backend(backend_name)
|
||||
return backend.render(expected, actual)
|
||||
end
|
||||
|
||||
return M
|
||||
Loading…
Add table
Add a link
Reference in a new issue