feat: add :Gdiff, :Gvdiff, :Ghdiff commands for unified diff view
Compares current buffer against any git revision (default HEAD), opens result with full diffs.nvim syntax highlighting. Follows fugitive convention: :Gdiff/:Gvdiff open vertical split, :Ghdiff opens horizontal split.
This commit is contained in:
parent
2ce76e7683
commit
045a9044b5
10 changed files with 404 additions and 7 deletions
114
lua/diffs/commands.lua
Normal file
114
lua/diffs/commands.lua
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
local M = {}
|
||||
|
||||
local git = require('diffs.git')
|
||||
local dbg = require('diffs.log').dbg
|
||||
|
||||
---@param old_lines string[]
|
||||
---@param new_lines string[]
|
||||
---@param old_name string
|
||||
---@param new_name string
|
||||
---@return string[]
|
||||
local function generate_unified_diff(old_lines, new_lines, old_name, new_name)
|
||||
local old_content = table.concat(old_lines, '\n')
|
||||
local new_content = table.concat(new_lines, '\n')
|
||||
|
||||
local diff_fn = vim.text and vim.text.diff or vim.diff
|
||||
local diff_output = diff_fn(old_content, new_content, {
|
||||
result_type = 'unified',
|
||||
ctxlen = 3,
|
||||
})
|
||||
|
||||
if not diff_output or diff_output == '' then
|
||||
return {}
|
||||
end
|
||||
|
||||
local diff_lines = vim.split(diff_output, '\n', { plain = true })
|
||||
|
||||
local result = {
|
||||
'diff --git a/' .. old_name .. ' b/' .. new_name,
|
||||
'--- a/' .. old_name,
|
||||
'+++ b/' .. new_name,
|
||||
}
|
||||
for _, line in ipairs(diff_lines) do
|
||||
table.insert(result, line)
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
---@param revision? string
|
||||
---@param vertical? boolean
|
||||
function M.gdiff(revision, vertical)
|
||||
revision = revision or 'HEAD'
|
||||
|
||||
local bufnr = vim.api.nvim_get_current_buf()
|
||||
local filepath = vim.api.nvim_buf_get_name(bufnr)
|
||||
|
||||
if filepath == '' then
|
||||
vim.notify('[diffs.nvim]: cannot diff unnamed buffer', vim.log.levels.ERROR)
|
||||
return
|
||||
end
|
||||
|
||||
local rel_path = git.get_relative_path(filepath)
|
||||
if not rel_path then
|
||||
vim.notify('[diffs.nvim]: not in a git repository', vim.log.levels.ERROR)
|
||||
return
|
||||
end
|
||||
|
||||
local old_lines, err = git.get_file_content(revision, filepath)
|
||||
if not old_lines then
|
||||
vim.notify('[diffs.nvim]: ' .. (err or 'unknown error'), vim.log.levels.ERROR)
|
||||
return
|
||||
end
|
||||
|
||||
local new_lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false)
|
||||
|
||||
local diff_lines = generate_unified_diff(old_lines, new_lines, rel_path, rel_path)
|
||||
|
||||
if #diff_lines == 0 then
|
||||
vim.notify('[diffs.nvim]: no diff against ' .. revision, vim.log.levels.INFO)
|
||||
return
|
||||
end
|
||||
|
||||
local diff_buf = vim.api.nvim_create_buf(false, true)
|
||||
vim.api.nvim_buf_set_lines(diff_buf, 0, -1, false, diff_lines)
|
||||
vim.api.nvim_set_option_value('buftype', 'nofile', { buf = diff_buf })
|
||||
vim.api.nvim_set_option_value('bufhidden', 'wipe', { buf = diff_buf })
|
||||
vim.api.nvim_set_option_value('modifiable', false, { buf = diff_buf })
|
||||
vim.api.nvim_set_option_value('filetype', 'diff', { buf = diff_buf })
|
||||
vim.api.nvim_buf_set_name(diff_buf, 'diffs://' .. revision .. ':' .. rel_path)
|
||||
|
||||
vim.cmd(vertical and 'vsplit' or 'split')
|
||||
vim.api.nvim_win_set_buf(0, diff_buf)
|
||||
|
||||
dbg('opened diff buffer %d for %s against %s', diff_buf, rel_path, revision)
|
||||
|
||||
vim.schedule(function()
|
||||
require('diffs').attach(diff_buf)
|
||||
end)
|
||||
end
|
||||
|
||||
function M.setup()
|
||||
vim.api.nvim_create_user_command('Gdiff', function(opts)
|
||||
M.gdiff(opts.args ~= '' and opts.args or nil, false)
|
||||
end, {
|
||||
nargs = '?',
|
||||
desc = 'Show unified diff against git revision (default: HEAD)',
|
||||
})
|
||||
|
||||
vim.api.nvim_create_user_command('Gvdiff', function(opts)
|
||||
M.gdiff(opts.args ~= '' and opts.args or nil, true)
|
||||
end, {
|
||||
nargs = '?',
|
||||
desc = 'Show unified diff against git revision in vertical split',
|
||||
})
|
||||
|
||||
vim.api.nvim_create_user_command('Ghdiff', function(opts)
|
||||
M.gdiff(opts.args ~= '' and opts.args or nil, false)
|
||||
end, {
|
||||
nargs = '?',
|
||||
desc = 'Show unified diff against git revision in horizontal split',
|
||||
})
|
||||
end
|
||||
|
||||
return M
|
||||
Loading…
Add table
Add a link
Reference in a new issue