diff --git a/term/.config/nvim/init.lua b/term/.config/nvim/init.lua index 3638c14..25ed79c 100644 --- a/term/.config/nvim/init.lua +++ b/term/.config/nvim/init.lua @@ -44,6 +44,8 @@ require("basic-settings") -- ============================================================================ require("lazy").setup("plugins") +require("diff-base") + -- ============================================================================ -- STEP 4: LSP KEYMAPS -- ============================================================================ diff --git a/term/.config/nvim/lua/diff-base.lua b/term/.config/nvim/lua/diff-base.lua new file mode 100644 index 0000000..4ecb65e --- /dev/null +++ b/term/.config/nvim/lua/diff-base.lua @@ -0,0 +1,112 @@ +local git = require("neo-tree.git") +local parser = require("neo-tree.git.parser") +local git_cmd = require("neo-tree.git.cmd") +local utils = require("neo-tree.utils") + +local diff_base = nil +local status_cache = {} +local orig_find_status + +local function find_worktree_root() + local root = git.find_worktree_info(vim.fn.getcwd()) + if not root then + local buf = vim.api.nvim_buf_get_name(0) + if buf ~= "" then + root = git.find_worktree_info(buf) + end + end + return root +end + +local function run_git(args) + local result = vim.system(args):wait() + if result.code ~= 0 then + return nil + end + return result.stdout +end + +local function compute_status(wr, base) + local args = git_cmd.with_args({ "-C", wr, "diff", base, "--name-status", "-z" }) + local out = run_git(args) + if not out then + vim.notify("DiffBase: invalid ref " .. base, vim.log.levels.WARN) + return nil + end + local status = parser.parse_diff_name_status_output(wr, false, utils.gsplit_plain(out, "\000")) + local ut = run_git(git_cmd.with_args({ "-C", wr, "ls-files", "--others", "--exclude-standard", "-z" })) + if ut then + for _, p in ipairs(parser.parse_ls_files_output(wr, utils.gsplit_plain(ut, "\000"))) do + status[p] = "?" + end + end + return status +end + +local function patch() + if not orig_find_status then + orig_find_status = git.find_existing_status_code + end + git.find_existing_status_code = function(path, base_lookup) + if diff_base then + local wr, _ = git.find_existing_worktree(path) + local cached = wr and status_cache[wr] + local hit = cached and cached[path] + if hit then + return hit, true + end + end + return orig_find_status(path, base_lookup) + end +end + +local function set(base) + base = base or "" + if base == "" or base == "HEAD" then + diff_base = nil + status_cache = {} + pcall(function() + require("gitsigns").change_base(nil, true) + end) + local e = require("neo-tree.events") + vim.schedule(function() + e.fire_event(e.GIT_EVENT, {}) + end) + vim.notify("DiffBase: reset to HEAD") + return + end + local wr = find_worktree_root() + if not wr then + vim.notify("DiffBase: not in a git repo", vim.log.levels.WARN) + return + end + local s = compute_status(wr, base) + if not s then + return + end + diff_base = base + status_cache[wr] = s + pcall(function() + require("gitsigns").change_base(base, true) + end) + patch() + local events = require("neo-tree.events") + if git.worktrees[wr] then + git.worktrees[wr].status = s + end + vim.schedule(function() + events.fire_event(events.GIT_STATUS_CHANGED, { git_root = wr, git_status = s, base = base }) + end) + vim.notify("DiffBase: set to " .. base) +end + +vim.api.nvim_create_user_command("DiffBase", function(opts) + set(opts.args) +end, { + nargs = "?", + complete = function(l) + return vim.tbl_filter(function(s) + return s:find(l, 1, true) == 1 + end, { "HEAD", "HEAD~1", "HEAD~2", "HEAD~3", "main", "master", "origin/main", "origin/master" }) + end, +})