---@class mini.diff.jj.CacheEntry ---@field fs_event uv.uv_fs_event_t? ---@field timer uv.uv_timer_t? local MiniDiff = require("mini.diff") ---@overload fun(args: string[], cwd: string): vim.SystemCompleted ---@overload fun(args: string[], cwd: string, on_exit: fun(out: vim.SystemCompleted)): vim.SystemObj ---@param args string[] ---@param cwd string ---@param on_exit? fun(out: vim.SystemCompleted) local function jj(args, cwd, on_exit) local obj = vim.system( vim.list_extend({ "jj", "--ignore-working-copy" }, args), { text = true, cwd = cwd }, on_exit ) if on_exit then return obj else return obj:wait() end end ---@type table local cache = {} ---@param buf_id integer ---@return string local function buf_path(buf_id) return vim.uv.fs_realpath(vim.api.nvim_buf_get_name(buf_id)) or "" end ---@param cwd string ---@return string? local function repo_root(cwd) local result = jj({ "root" }, cwd) if result.code == 0 and result.stdout ~= nil then return vim.trim(result.stdout) end end local function invalidate(buf_id) local entry = cache[buf_id] if entry then if entry.fs_event then entry.fs_event:stop() end if entry.timer then entry.timer:stop() end cache[buf_id] = nil end end ---@param buf_id integer ---@param file_path string ---@param cwd string local function set_ref_text(buf_id, file_path, cwd) local stat = vim.uv.fs_stat(cwd) if not stat or stat.type ~= "directory" then return end ---@param text string | string[] | nil local function set_text(text) vim.schedule(function() MiniDiff.set_ref_text(buf_id, text) end) end jj({ "file", "show", "-r", "@-", file_path }, cwd, function(res) if res.code == 0 then return set_text(res.stdout) end -- Ignore if file is untracked in `@`. jj({ "file", "list", "-r", "@", file_path }, cwd, function(list) local tracked = list.code == 0 and list.stdout ~= nil and vim.trim(list.stdout) ~= "" set_text(tracked and "" or nil) end) end) end ---@param buf_id integer ---@param file_path string ---@return boolean? local function watch(buf_id, file_path) local cwd = vim.fs.dirname(file_path) local repo = repo_root(cwd) if not repo then return false end local watch_path = vim.fs.joinpath(repo, ".jj/working_copy") local fs_event = vim.uv.new_fs_event() local timer = vim.uv.new_timer() local function tick() if not timer then return end timer:stop() timer:start(50, 0, function() set_ref_text(buf_id, file_path, cwd) end) end local function on_change(_, filename) if filename == "checkout" and timer then tick() end end if fs_event then fs_event:start(watch_path, {}, on_change) end invalidate(buf_id) cache[buf_id] = { fs_event = fs_event, timer = timer } set_ref_text(buf_id, file_path, cwd) return true end ---@param buf_id integer ---@return boolean? local function attach(buf_id) if cache[buf_id] then return false end local path = buf_path(buf_id) if path == "" then return false end return watch(buf_id, path) end ---@param buf_id integer local function detach(buf_id) invalidate(buf_id) end return { name = "jj", attach = attach, detach = detach }