local util = require("jjannotate.util") local M = {} ---@class jjannotate.Range ---@field start integer 0-indexed inclusive ---@field _end integer 0-indexed inclusive ---@class jjannotate.Line ---@field range jjannotate.Range ---@field change_id string ---@field author string ---@field timestamp string ---@class jjannotate.AnnotateResult ---@field lines jjannotate.Line[] ---@field longest_timestamp integer ---@field row_index jjannotate.Line[] ---@field change_id_index table --- A version control backend. Every backend annotation resolves a `revision` --- to a canonical revision ID and annotates/reads files at it. ---@class jjannotate.Backend ---@field name string backend name, e.g. "jj" or "git" ---@field marker string root marker used to detect the backend, e.g. ".jj" or ".git" ---@field annotate fun(repo: string, path: string, revision: string, cb: fun(result: jjannotate.AnnotateResult?)) ---@field show fun(repo: string, revision: string) ---@field file_contents fun(repo: string, path: string, revision: string, cb: fun(contents: string?)) ---@field resolve_revision fun(repo: string, revision: string?, cb?: fun(id: string?)): string? ---@class jjannotate.RepoInfo ---@field backend jjannotate.Backend ---@field repo string repo root ---@field path string path within `repo` --- Backends in detection priority order. ---@type jjannotate.Backend[] M.backends = { require("jjannotate.backend.jj"), require("jjannotate.backend.git") } --- Resolves the given `path`'s backend, repo root, and relative path within the repo. --- Returns `nil` if `path` is not in any supported repo. ---@param path string ---@return jjannotate.RepoInfo? function M.path_info(path) if path == "" then return nil end path = vim.fs.normalize(vim.fs.abspath(path)) for _, backend in ipairs(M.backends) do local repo = vim.fs.root(path, backend.marker) if repo then local relpath = vim.fs.relpath(repo, path) if relpath then return { backend = backend, repo = repo, path = relpath } end end end end --- Resolves the given `buf`'s backend, repo root, and relative path within the repo. --- Handles annotate URI buffers, falling back to filesystem detection. --- Returns `nil` if the buffer is not in any supported repo. ---@param buf integer ---@return jjannotate.RepoInfo? function M.buf_info(buf) local ref = util.parse_uri("annotate", vim.api.nvim_buf_get_name(buf)) if ref then local info = M.path_info(ref.repo) if info then return { backend = info.backend, repo = ref.repo, path = ref.path } end end return M.path_info(vim.api.nvim_buf_get_name(buf)) end --- Detects the backend for the given path (or repo root). ---@param path string ---@return jjannotate.Backend? function M.detect(path) local info = M.path_info(path) if not info then util.notify(string.format("`%s` is not in a jj or git repo", path), vim.log.levels.ERROR) return end return info.backend end return M