mirror of
https://github.com/zoriya/jj.nvim.git
synced 2026-08-05 10:46:08 +00:00
Feat: Rewrite diff module to allow for external dependencies as jj.nvim's diff tool (#74)
- Refactor diff into modular architecture (native, diffview, codediff) - Add the possibility for users to define their own diff backend - Support external diff tools like diffview.nvim codediff.nvim - Enable visual mode selection to diff two changes in log buffer - Add summary tooltip integration with terminal - Update documentation with new diff workflows
This commit is contained in:
@@ -83,13 +83,13 @@ function M.describe(description, revset, opts, on_close)
|
||||
-- Use buffer editor mode (defaults to "buffer" if not configured)
|
||||
local editor_mode = merged_opts.type or cmd.config.describe.editor.type or "buffer"
|
||||
if editor_mode == "buffer" then
|
||||
local jj_cmd = "jj log -r " .. revset .. " --no-graph -T 'coalesce(description, \"\n\")'"
|
||||
local jj_cmd = "jj log -r " .. revset .. " --quiet --no-graph -T 'coalesce(description, \"\n\")'"
|
||||
local old_description_raw, success = runner.execute_command(jj_cmd, "Failed to get old description")
|
||||
if not old_description_raw or not success then
|
||||
return
|
||||
end
|
||||
|
||||
local log_cmd = "jj log -r " .. revset .. " --no-graph -T 'self.diff().summary()'"
|
||||
local log_cmd = "jj log -r " .. revset .. " --quiet --no-graph -T 'self.diff().summary()'"
|
||||
local status_result, success2 = runner.execute_command(log_cmd, "Error getting status")
|
||||
if not success2 then
|
||||
return
|
||||
|
||||
+4
-5
@@ -378,19 +378,18 @@ function M.diff(opts)
|
||||
return
|
||||
end
|
||||
|
||||
local cmd = "jj diff"
|
||||
local diff_module = require("jj.diff")
|
||||
|
||||
if opts and opts.current then
|
||||
local file = vim.fn.expand("%:p")
|
||||
if file and file ~= "" then
|
||||
cmd = string.format("%s %s", cmd, vim.fn.fnameescape(file))
|
||||
diff_module.diff_current({ path = file })
|
||||
else
|
||||
utils.notify("Current buffer is not a file", vim.log.levels.ERROR)
|
||||
return
|
||||
end
|
||||
else
|
||||
diff_module.show_revision({ rev = "@" })
|
||||
end
|
||||
|
||||
terminal.run(cmd, M.terminal_keymaps())
|
||||
end
|
||||
|
||||
-- Jujutsu rebase
|
||||
|
||||
+23
-41
@@ -6,6 +6,7 @@ local runner = require("jj.core.runner")
|
||||
local parser = require("jj.core.parser")
|
||||
local terminal = require("jj.ui.terminal")
|
||||
local buffer = require("jj.core.buffer")
|
||||
local diff = require("jj.diff")
|
||||
|
||||
local log_selected_hl_group = "JJLogSelectedHlGroup"
|
||||
local log_selected_ns_id = vim.api.nvim_create_namespace(log_selected_hl_group)
|
||||
@@ -332,13 +333,23 @@ end
|
||||
|
||||
--- Handle diffing a log line
|
||||
function M.handle_log_diff()
|
||||
local revset = get_revset()
|
||||
|
||||
if revset then
|
||||
local cmd = string.format("jj show --no-pager %s", revset)
|
||||
terminal.run_floating(cmd, require("jj.cmd").floating_keymaps())
|
||||
else
|
||||
local revsets = extract_revsets_from_terminal_buffer()
|
||||
if not revsets then
|
||||
utils.notify("No valid revision found in the log line", vim.log.levels.ERROR)
|
||||
return
|
||||
end
|
||||
|
||||
-- Delete the pipes from the revsets string since jj new expects space separated revsets
|
||||
revsets = revsets:gsub("| ", "")
|
||||
|
||||
local is_multiple = revsets:find(" ") ~= nil
|
||||
|
||||
if not is_multiple then
|
||||
diff.show_revision({ rev = revsets })
|
||||
else
|
||||
-- Get the first and the last revset to diff
|
||||
local list = vim.split(revsets, "%s+", { trimempty = true })
|
||||
diff.diff_revisions({ left = list[1], right = list[#list] })
|
||||
end
|
||||
end
|
||||
|
||||
@@ -731,40 +742,11 @@ function M.handle_summary_diff(revset)
|
||||
terminal.keep_tooltip_open(true)
|
||||
end
|
||||
|
||||
-- Add custom close behavior that returns to tooltip
|
||||
local function close_and_return()
|
||||
-- Close the floating diff
|
||||
terminal.close_floating_buffer()
|
||||
-- Return focus to tooltip if still valid
|
||||
if terminal.state.tooltip_win and vim.api.nvim_win_is_valid(terminal.state.tooltip_win) then
|
||||
vim.api.nvim_set_current_win(terminal.state.tooltip_win)
|
||||
end
|
||||
-- Clear suppress flag
|
||||
if terminal.state.tooltip_buf and vim.api.nvim_buf_is_valid(terminal.state.tooltip_buf) then
|
||||
terminal.keep_tooltip_open(false)
|
||||
end
|
||||
end
|
||||
|
||||
local cmd = require("jj.cmd")
|
||||
local cfg = cmd.config.keymaps.floating or {}
|
||||
-- In this specific case we override the behaviour of the default terminal close and return to the old buffer
|
||||
local specs = {
|
||||
close = {
|
||||
modes = { "n", "v" },
|
||||
handler = close_and_return,
|
||||
desc = "Close diff and return to tooltip",
|
||||
},
|
||||
hide = {
|
||||
modes = { "n", "v" },
|
||||
handler = close_and_return,
|
||||
desc = "Close diff and return to tooltip",
|
||||
},
|
||||
}
|
||||
|
||||
terminal.run_floating(
|
||||
string.format("jj diff -r %s %s", revset, filepath.new_path),
|
||||
cmd.resolve_keymaps_from_specs(cfg, specs)
|
||||
)
|
||||
-- Use the diff module to show what changed in this revision for this file
|
||||
diff.show_revision({
|
||||
rev = revset,
|
||||
path = filepath.new_path,
|
||||
})
|
||||
end
|
||||
|
||||
--- Handle edit action in summary tooltip (opens file after jj edit)
|
||||
@@ -936,7 +918,7 @@ function M.log_keymaps()
|
||||
diff = {
|
||||
desc = "Diff revision under cursor",
|
||||
handler = M.handle_log_diff,
|
||||
modes = { "n" },
|
||||
modes = { "n", "v" },
|
||||
},
|
||||
new = {
|
||||
desc = "Create new change branching off revision under cursor",
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
---@class jj.diff
|
||||
local M = {}
|
||||
|
||||
local utils = require("jj.utils")
|
||||
local buffer = require("jj.core.buffer")
|
||||
|
||||
--- Get the content of a file at a specific revision
|
||||
--- @param rev string The revision
|
||||
--- @param path string The file path
|
||||
--- @return table lines The file content
|
||||
local function get_file_content(rev, path)
|
||||
local cmd = string.format("jj file show -r %s %s", vim.fn.shellescape(rev), vim.fn.shellescape(path))
|
||||
local content = vim.fn.system(cmd)
|
||||
local success = vim.v.shell_error == 0
|
||||
if success then
|
||||
return vim.split(content, "\n", { trimempty = true })
|
||||
else
|
||||
-- File does not exist at revision
|
||||
return {}
|
||||
end
|
||||
end
|
||||
|
||||
--- Open a read-only buffer for a specific revision of a file
|
||||
--- @param rev string The revision
|
||||
--- @param path string The file path
|
||||
function M.open_revision(rev, path)
|
||||
local lines = get_file_content(rev, path)
|
||||
|
||||
local buf = vim.api.nvim_create_buf(false, true)
|
||||
|
||||
local buf_name = string.format("jj://%s/%s", rev, path)
|
||||
vim.api.nvim_buf_set_name(buf, buf_name)
|
||||
vim.api.nvim_buf_set_lines(buf, 0, -1, false, lines)
|
||||
|
||||
local ft = vim.filetype.match({ filename = path })
|
||||
if ft then
|
||||
vim.bo[buf].filetype = ft
|
||||
end
|
||||
|
||||
vim.bo[buf].buftype = "nofile"
|
||||
vim.bo[buf].bufhidden = "wipe"
|
||||
vim.bo[buf].readonly = true
|
||||
vim.bo[buf].swapfile = false
|
||||
vim.bo[buf].modifiable = true
|
||||
|
||||
vim.api.nvim_win_set_buf(0, buf)
|
||||
end
|
||||
|
||||
---@class jj.diff.diff_opts
|
||||
---@field rev string the revision to diff against
|
||||
|
||||
--- Open a diff split for a specific revision of the current file
|
||||
--- @param split_fun function Split function for the diff
|
||||
--- @param opts? jj.diff.diff_opts Any passed arguments
|
||||
function M.open_diff(split_fun, opts)
|
||||
if not utils.ensure_jj() then
|
||||
return
|
||||
end
|
||||
|
||||
-- Get previus cursor position
|
||||
local prev_buf = vim.api.nvim_get_current_buf()
|
||||
local prev_cur_pos = buffer.get_cursor(prev_buf)
|
||||
-- If no previous cursor position, set to start of file
|
||||
if prev_cur_pos == nil then
|
||||
prev_cur_pos = { 1, 0 }
|
||||
end
|
||||
|
||||
-- Ensure opts is a table to avoid indexing nil
|
||||
opts = opts or {}
|
||||
local rev = opts.rev or "@-"
|
||||
local path = vim.api.nvim_buf_get_name(0)
|
||||
|
||||
vim.cmd.diffthis()
|
||||
split_fun({ mods = { split = "aboveleft" } })
|
||||
M.open_revision(rev, path)
|
||||
vim.cmd.diffthis()
|
||||
|
||||
-- Add an autocomnd to restore the buffer position to the previous one when exiting diff
|
||||
vim.api.nvim_create_autocmd("BufWipeout", {
|
||||
buffer = vim.api.nvim_get_current_buf(),
|
||||
callback = function()
|
||||
buffer.set_cursor(prev_buf, prev_cur_pos)
|
||||
end,
|
||||
})
|
||||
end
|
||||
|
||||
-- Open a vertical diff split for a specific revision of the current file
|
||||
--- @param opts? jj.diff.diff_opts Any passed arguments
|
||||
function M.open_vdiff(opts)
|
||||
M.open_diff(vim.cmd.vsplit, opts)
|
||||
end
|
||||
|
||||
-- Open a horizontal diff split for a specific revision of the current file
|
||||
--- @param opts? jj.diff.diff_opts Any passed arguments
|
||||
function M.open_hdiff(opts)
|
||||
M.open_diff(vim.cmd.split, opts)
|
||||
end
|
||||
|
||||
return M
|
||||
@@ -0,0 +1,64 @@
|
||||
local utils = require("jj.utils")
|
||||
|
||||
---@type jj.diff
|
||||
local diff = require("jj.diff")
|
||||
|
||||
--- Givewn two changes, show their diff using codediff
|
||||
--- @param left string
|
||||
--- @param right string
|
||||
local function diff_two_changes(left, right)
|
||||
local commit_id_left = utils.get_commit_id(left)
|
||||
if commit_id_left == nil then
|
||||
return
|
||||
end
|
||||
|
||||
local commit_id_right = utils.get_commit_id(right)
|
||||
if commit_id_right == nil then
|
||||
return
|
||||
end
|
||||
|
||||
vim.cmd(string.format("CodeDiff %s %s", commit_id_right, commit_id_left))
|
||||
end
|
||||
|
||||
-----------------------------------------------------------------------
|
||||
-- Codediff Backend
|
||||
-----------------------------------------------------------------------
|
||||
|
||||
diff.register_backend("codediff", {
|
||||
diff_current = function(opts)
|
||||
if not utils.has_dependency("codediff") then
|
||||
return
|
||||
end
|
||||
|
||||
-- Extract the commit id from opts.rev
|
||||
local commit_id = "HEAD~1"
|
||||
if opts.rev then
|
||||
local t_commit_id = utils.get_commit_id(opts.rev)
|
||||
if t_commit_id == nil then
|
||||
return
|
||||
end
|
||||
commit_id = t_commit_id
|
||||
end
|
||||
|
||||
vim.cmd(string.format("CodeDiff file %s", commit_id))
|
||||
end,
|
||||
|
||||
show_revision = function(opts)
|
||||
if not utils.has_dependency("codediff") then
|
||||
return
|
||||
end
|
||||
|
||||
-- When comparing a revision we always compare it to it's parent to get the diff
|
||||
local right = string.format("%s-", opts.rev)
|
||||
|
||||
diff_two_changes(opts.rev, right)
|
||||
end,
|
||||
|
||||
diff_revisions = function(opts)
|
||||
if not utils.has_dependency("codediff") then
|
||||
return
|
||||
end
|
||||
|
||||
diff_two_changes(opts.left, opts.right)
|
||||
end,
|
||||
})
|
||||
@@ -0,0 +1,64 @@
|
||||
local utils = require("jj.utils")
|
||||
|
||||
---@type jj.diff
|
||||
local diff = require("jj.diff")
|
||||
|
||||
-----------------------------------------------------------------------
|
||||
-- Diffvew Backend
|
||||
-----------------------------------------------------------------------
|
||||
|
||||
--- Givewn two changes, show their diff using diffview.nvim
|
||||
--- @param left string
|
||||
--- @param right string
|
||||
local function diff_two_changes(left, right)
|
||||
-- Extract the commit id from opts.rev
|
||||
local commit_id_left = utils.get_commit_id(left)
|
||||
if commit_id_left == nil then
|
||||
return
|
||||
end
|
||||
local commit_id_right = utils.get_commit_id(right)
|
||||
if commit_id_right == nil then
|
||||
return
|
||||
end
|
||||
|
||||
vim.cmd(string.format("DiffviewOpen %s..%s", commit_id_right, commit_id_left))
|
||||
end
|
||||
|
||||
-- Register the diffview backend
|
||||
diff.register_backend("diffview", {
|
||||
diff_current = function(opts)
|
||||
if not utils.has_dependency("diffview") then
|
||||
return
|
||||
end
|
||||
|
||||
-- Extract the commit id from opts.rev
|
||||
local commit_id = "HEAD~1"
|
||||
if opts.rev then
|
||||
local t_commit_id = utils.get_commit_id(opts.rev)
|
||||
if t_commit_id == nil then
|
||||
return
|
||||
end
|
||||
commit_id = t_commit_id
|
||||
end
|
||||
|
||||
vim.cmd(string.format("DiffviewOpen %s -- %%", commit_id))
|
||||
vim.cmd("DiffviewToggleFiles")
|
||||
end,
|
||||
show_revision = function(opts)
|
||||
if not utils.has_dependency("diffview") then
|
||||
return
|
||||
end
|
||||
|
||||
-- When comparing a revision we always compare it to it's parent to get the diff
|
||||
local right = string.format("%s-", opts.rev)
|
||||
|
||||
diff_two_changes(opts.rev, right)
|
||||
end,
|
||||
diff_revisions = function(opts)
|
||||
if not utils.has_dependency("diffview") then
|
||||
return
|
||||
end
|
||||
|
||||
diff_two_changes(opts.left, opts.right)
|
||||
end,
|
||||
})
|
||||
@@ -0,0 +1,164 @@
|
||||
---@class jj.diff
|
||||
local M = {}
|
||||
|
||||
local utils = require("jj.utils")
|
||||
|
||||
---@alias jj.diff.backend "native"|"diffview"|"codediff"|string
|
||||
|
||||
---@class jj.diff.current_opts
|
||||
---@field rev? string -- revision to diff against (default: "@-")
|
||||
---@field path? string -- path to diff (default: current buffer path)
|
||||
---@field backend? jj.diff.backend
|
||||
---@field layout? "vertical"|"horizontal" -- only used by native backend
|
||||
|
||||
---@class jj.diff.revision_opts
|
||||
---@field rev string -- revision to show
|
||||
---@field path? string -- optional single-file filter
|
||||
---@field backend? jj.diff.backend
|
||||
---@field display? "floating"|"tab"|"split" -- hint to backend
|
||||
|
||||
---@class jj.diff.revisions_opts
|
||||
---@field left string -- left/base revision
|
||||
---@field right string -- right/target revision
|
||||
---@field path? string -- optional single-file filter
|
||||
---@field backend? jj.diff.backend
|
||||
---@field display? "floating"|"tab"|"split"
|
||||
|
||||
---@class jj.diff.BackendImpl
|
||||
---@field diff_current? fun(opts: jj.diff.current_opts)
|
||||
---@field show_revision? fun(opts: jj.diff.revision_opts)
|
||||
---@field diff_revisions? fun(opts: jj.diff.revisions_opts)
|
||||
|
||||
---@class jj.diff.config
|
||||
---@field backend? jj.diff.backend
|
||||
---@field backends? table<string, table>
|
||||
|
||||
---@class jj.diff.diff_opts
|
||||
---@field rev string the revision to diff against
|
||||
|
||||
---@type jj.diff.config
|
||||
M.config = {
|
||||
backend = "native",
|
||||
backends = {},
|
||||
}
|
||||
|
||||
---@type table<string, jj.diff.BackendImpl>
|
||||
local backends = {}
|
||||
|
||||
-----------------------------------------------------------------------
|
||||
-- Backend Registry
|
||||
-----------------------------------------------------------------------
|
||||
|
||||
--- Register or override a backend implementation
|
||||
---@param name string
|
||||
---@param impl jj.diff.BackendImpl
|
||||
function M.register_backend(name, impl)
|
||||
backends[name] = impl
|
||||
end
|
||||
|
||||
--- Get the configured default backend name
|
||||
---@return string
|
||||
local function get_config_backend()
|
||||
local ok, cfg = pcall(function()
|
||||
return require("jj").config.diff
|
||||
end)
|
||||
if ok and cfg and cfg.backend then
|
||||
return cfg.backend
|
||||
end
|
||||
return M.config.backend or "native"
|
||||
end
|
||||
|
||||
--- Get a backend implementation by name, falling back to native
|
||||
---@param name? string
|
||||
---@return jj.diff.BackendImpl
|
||||
local function get_backend(name)
|
||||
name = name or get_config_backend()
|
||||
local impl = backends[name]
|
||||
if not impl then
|
||||
utils.notify(
|
||||
string.format("[Diff] backend '%s' not available, falling back to 'native'", name),
|
||||
vim.log.levels.WARN
|
||||
)
|
||||
impl = backends.native
|
||||
end
|
||||
return impl
|
||||
end
|
||||
|
||||
--- Setup the diff module
|
||||
---@param cfg? jj.diff.config
|
||||
function M.setup(cfg)
|
||||
M.config = vim.tbl_deep_extend("force", M.config, cfg or {})
|
||||
-- Laod default backends
|
||||
pcall(require, "jj.diff.diffview")
|
||||
pcall(require, "jj.diff.codediff")
|
||||
pcall(require, "jj.diff.native")
|
||||
end
|
||||
|
||||
-----------------------------------------------------------------------
|
||||
-- Unified Public API
|
||||
-----------------------------------------------------------------------
|
||||
|
||||
--- Single dispatcher (canonical entry point)
|
||||
---@param kind "current"|"revision"|"revisions"
|
||||
---@param opts table
|
||||
function M.open(kind, opts)
|
||||
opts = opts or {}
|
||||
local backend = get_backend(opts.backend)
|
||||
|
||||
if kind == "current" then
|
||||
if backend.diff_current then
|
||||
return backend.diff_current(opts)
|
||||
end
|
||||
return backends.native.diff_current(opts)
|
||||
elseif kind == "revision" then
|
||||
if backend.show_revision then
|
||||
return backend.show_revision(opts)
|
||||
end
|
||||
return backends.native.show_revision(opts)
|
||||
elseif kind == "revisions" then
|
||||
if backend.diff_revisions then
|
||||
return backend.diff_revisions(opts)
|
||||
end
|
||||
return backends.native.diff_revisions(opts)
|
||||
else
|
||||
utils.notify("[Diff] unknown diff kind: " .. tostring(kind), vim.log.levels.ERROR)
|
||||
end
|
||||
end
|
||||
|
||||
--- Diff current buffer against a revision
|
||||
---@param opts? jj.diff.current_opts
|
||||
function M.diff_current(opts)
|
||||
return M.open("current", opts or {})
|
||||
end
|
||||
|
||||
--- Show what changed in a single revision
|
||||
---@param opts jj.diff.revision_opts
|
||||
function M.show_revision(opts)
|
||||
return M.open("revision", opts)
|
||||
end
|
||||
|
||||
--- Diff between two revisions
|
||||
---@param opts jj.diff.revisions_opts
|
||||
function M.diff_revisions(opts)
|
||||
return M.open("revisions", opts)
|
||||
end
|
||||
|
||||
---
|
||||
-----------------------------------------------------------------------
|
||||
-- BACKWARDS COMPATIBLE API
|
||||
-----------------------------------------------------------------------
|
||||
|
||||
-- Open a vertical diff split for a specific revision of the current file
|
||||
--- @param opts? jj.diff.diff_opts Any passed arguments
|
||||
function M.open_vdiff(opts)
|
||||
M.diff_current(vim.tbl_extend("force", { layout = "vertical" }, { rev = opts and opts.rev }))
|
||||
end
|
||||
|
||||
-- Open a horizontal diff split for a specific revision of the current file
|
||||
--- @param opts? jj.diff.diff_opts Any passed arguments
|
||||
function M.open_hdiff(opts)
|
||||
M.diff_current(vim.tbl_extend("force", { layout = "horizontal" }, { rev = opts and opts.rev }))
|
||||
end
|
||||
|
||||
---@return jj.diff
|
||||
return M
|
||||
@@ -0,0 +1,127 @@
|
||||
local utils = require("jj.utils")
|
||||
local buffer = require("jj.core.buffer")
|
||||
|
||||
local diff = require("jj.diff")
|
||||
|
||||
--- Get the content of a file at a specific revision
|
||||
--- @param rev string The revision
|
||||
--- @param path string The file path
|
||||
--- @return table lines The file content
|
||||
local function get_file_content(rev, path)
|
||||
local cmd = string.format("jj file show -r %s %s", vim.fn.shellescape(rev), vim.fn.shellescape(path))
|
||||
local content = vim.fn.system(cmd)
|
||||
local success = vim.v.shell_error == 0
|
||||
if success then
|
||||
return vim.split(content, "\n", { trimempty = true })
|
||||
else
|
||||
return {}
|
||||
end
|
||||
end
|
||||
|
||||
--- Open a read-only buffer for a specific revision of a file
|
||||
--- @param rev string The revision
|
||||
--- @param path string The file path
|
||||
local function open_revision(rev, path)
|
||||
local lines = get_file_content(rev, path)
|
||||
|
||||
local buf = vim.api.nvim_create_buf(false, true)
|
||||
|
||||
local buf_name = string.format("jj://%s/%s", rev, path)
|
||||
vim.api.nvim_buf_set_name(buf, buf_name)
|
||||
vim.api.nvim_buf_set_lines(buf, 0, -1, false, lines)
|
||||
|
||||
local ft = vim.filetype.match({ filename = path })
|
||||
if ft then
|
||||
vim.bo[buf].filetype = ft
|
||||
end
|
||||
|
||||
vim.bo[buf].buftype = "nofile"
|
||||
vim.bo[buf].bufhidden = "wipe"
|
||||
vim.bo[buf].readonly = true
|
||||
vim.bo[buf].swapfile = false
|
||||
vim.bo[buf].modifiable = true
|
||||
|
||||
vim.api.nvim_win_set_buf(0, buf)
|
||||
end
|
||||
|
||||
-----------------------------------------------------------------------
|
||||
-- Native Backend
|
||||
-----------------------------------------------------------------------
|
||||
|
||||
diff.register_backend("native", {
|
||||
--- Opens a side-by-side diff of the current buffer against a revision.
|
||||
--- Creates a split with the revision content on the left and the current buffer on the right.
|
||||
--- Closing either side will clean up both and restore the original cursor position.
|
||||
diff_current = function(opts)
|
||||
if not utils.ensure_jj() then
|
||||
return
|
||||
end
|
||||
|
||||
-- Save current state to restore after diff is closed
|
||||
local prev_buf = vim.api.nvim_get_current_buf()
|
||||
local prev_cur_pos = buffer.get_cursor(prev_buf) or { 1, 0 }
|
||||
|
||||
local rev = opts.rev or "@-"
|
||||
local path = opts.path or vim.api.nvim_buf_get_name(0)
|
||||
local layout = opts.layout or "vertical"
|
||||
|
||||
local split_fun = layout == "horizontal" and vim.cmd.split or vim.cmd.vsplit
|
||||
local orig_win = vim.api.nvim_get_current_win()
|
||||
|
||||
-- Use better diff algorithm for code moves and indentation
|
||||
local saved_diffopt = vim.o.diffopt
|
||||
vim.opt.diffopt:append("algorithm:patience,indent-heuristic")
|
||||
|
||||
-- Set up diff: current buffer on right, revision on left
|
||||
vim.cmd.diffthis()
|
||||
split_fun({ mods = { split = "aboveleft" } })
|
||||
open_revision(rev, path)
|
||||
vim.cmd.diffthis()
|
||||
|
||||
local rev_buf = vim.api.nvim_get_current_buf()
|
||||
local augroup = vim.api.nvim_create_augroup("JJDiffCleanup" .. rev_buf, { clear = true })
|
||||
|
||||
-- Cleanup closes both sides, exits diff mode, and restores cursor.
|
||||
local function cleanup()
|
||||
vim.api.nvim_del_augroup_by_id(augroup)
|
||||
vim.schedule(function()
|
||||
if vim.api.nvim_buf_is_valid(rev_buf) then
|
||||
vim.api.nvim_buf_delete(rev_buf, { force = true })
|
||||
end
|
||||
if vim.api.nvim_win_is_valid(orig_win) then
|
||||
vim.api.nvim_set_current_win(orig_win)
|
||||
vim.cmd.diffoff()
|
||||
end
|
||||
buffer.set_cursor(prev_buf, prev_cur_pos)
|
||||
vim.o.diffopt = saved_diffopt
|
||||
end)
|
||||
end
|
||||
|
||||
-- Trigger cleanup when either the revision buffer or original window is closed
|
||||
vim.api.nvim_create_autocmd({ "BufWipeout", "BufHidden" }, {
|
||||
group = augroup,
|
||||
buffer = rev_buf,
|
||||
once = true,
|
||||
callback = cleanup,
|
||||
})
|
||||
|
||||
vim.api.nvim_create_autocmd("WinClosed", {
|
||||
group = augroup,
|
||||
pattern = tostring(orig_win),
|
||||
once = true,
|
||||
callback = cleanup,
|
||||
})
|
||||
end,
|
||||
show_revision = function(opts)
|
||||
local terminal = require("jj.ui.terminal")
|
||||
|
||||
local cmd = string.format("jj show -r %s --quiet --no-pager", opts.rev)
|
||||
terminal.run_floating(cmd, require("jj.cmd").floating_keymaps())
|
||||
end,
|
||||
diff_revisions = function(opts)
|
||||
local terminal = require("jj.ui.terminal")
|
||||
|
||||
local cmd = string.format("jj diff -f %s -t %s --quiet --no-pager", opts.left, opts.right)
|
||||
terminal.run_floating(cmd, require("jj.cmd").floating_keymaps())
|
||||
end,
|
||||
})
|
||||
@@ -3,6 +3,7 @@ local cmd = require("jj.cmd")
|
||||
local picker = require("jj.picker")
|
||||
local editor = require("jj.ui.editor")
|
||||
local terminal = require("jj.ui.terminal")
|
||||
local diff = require("jj.diff")
|
||||
|
||||
--- Jujutsu plugin configuration
|
||||
--- @class jj.Config
|
||||
@@ -10,6 +11,7 @@ local terminal = require("jj.ui.terminal")
|
||||
--- @field picker? jj.picker.config Options for picker module
|
||||
--- @field terminal? jj.ui.terminal.opts Options for the terminal
|
||||
--- @field highlights? jj.highlights Options for the highlights
|
||||
--- @field diff? jj.diff.config Options for the diff module
|
||||
|
||||
--- @class jj.highlights
|
||||
--- @field editor? jj.ui.editor.highlights Highlight configuration for describe buffer
|
||||
@@ -29,6 +31,10 @@ M.config = {
|
||||
targeted = { fg = "#5a9e6f", ctermfg = "Green" },
|
||||
},
|
||||
},
|
||||
diff = {
|
||||
backend = "native",
|
||||
backends = {},
|
||||
},
|
||||
}
|
||||
|
||||
--- Setup the plugin
|
||||
@@ -41,6 +47,7 @@ function M.setup(opts)
|
||||
editor.setup({ highlights = M.config.highlights.editor })
|
||||
cmd.setup(opts and opts.cmd or {})
|
||||
terminal.setup(opts and opts.terminal or {})
|
||||
diff.setup(M.config.diff)
|
||||
|
||||
cmd.register_command()
|
||||
end
|
||||
|
||||
@@ -163,8 +163,13 @@ end
|
||||
|
||||
--- Close the current tooltip buffer if it exists
|
||||
function M.close_tooltip()
|
||||
if not state.tooltip_buf then
|
||||
return
|
||||
end
|
||||
buffer.close(state.tooltip_buf)
|
||||
vim.api.nvim_del_autocmd(state.tooltip_close_autocmd)
|
||||
if state.tooltip_close_autocmd then
|
||||
pcall(vim.api.nvim_del_autocmd, state.tooltip_close_autocmd)
|
||||
end
|
||||
state.tooltip_chan = nil
|
||||
state.tooltip_job_id = nil
|
||||
state.tooltip_buf = nil
|
||||
@@ -668,7 +673,8 @@ function M.run_tooltip(cmd, tool_opts)
|
||||
state.tooltip_close_autocmd = vim.api.nvim_create_autocmd("CursorMoved", {
|
||||
callback = function()
|
||||
if
|
||||
vim.api.nvim_buf_is_valid(state.tooltip_buf)
|
||||
state.tooltip_buf
|
||||
and vim.api.nvim_buf_is_valid(state.tooltip_buf)
|
||||
and vim.api.nvim_win_is_valid(state.tooltip_win)
|
||||
and vim.api.nvim_get_current_win() ~= state.tooltip_win
|
||||
then
|
||||
@@ -676,8 +682,7 @@ function M.run_tooltip(cmd, tool_opts)
|
||||
if vim.b[state.tooltip_buf].jj_keep_open then
|
||||
return
|
||||
end
|
||||
buffer.close(state.tooltip_buf, true)
|
||||
return true
|
||||
M.close_tooltip()
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
+20
-1
@@ -261,7 +261,7 @@ end
|
||||
--- @return boolean True if the change is immutable, false otherwise
|
||||
function M.is_change_immutable(revset)
|
||||
local output, success = runner.execute_command(
|
||||
string.format("jj log --no-graph -r '%s' -T 'immutable'", revset),
|
||||
string.format("jj log --no-graph -r '%s' -T 'immutable' --quiet", revset),
|
||||
"Error checking change immutability",
|
||||
nil,
|
||||
true
|
||||
@@ -273,5 +273,24 @@ function M.is_change_immutable(revset)
|
||||
|
||||
return vim.trim(output) == "true"
|
||||
end
|
||||
---
|
||||
--- Get the commit id from a given revision
|
||||
--- @param revset string The revset to extract the commit id from
|
||||
--- @return string|nil
|
||||
function M.get_commit_id(revset)
|
||||
local output, success = runner.execute_command(
|
||||
string.format("jj log --no-graph -r '%s' -T 'commit_id' --quiet", revset),
|
||||
"Error extracting commit id",
|
||||
nil,
|
||||
true
|
||||
)
|
||||
-- Test quie
|
||||
|
||||
if not success or not output then
|
||||
return nil
|
||||
end
|
||||
|
||||
return vim.trim(output)
|
||||
end
|
||||
|
||||
return M
|
||||
|
||||
Reference in New Issue
Block a user