···11+local M = {}
22+33+local run = require "vclib.run"
44+55+---@diagnostic disable-next-line: undefined-field
66+local stat = vim.uv.fs_stat
77+88+-- This is probably way overkill.
99+local vcs_root_cache = {}
1010+1111+---@param path string
1212+---@return {type: string, root: string}|nil
1313+local function _get_vcs_root(path)
1414+ if vcs_root_cache[path] then
1515+ return vcs_root_cache[path]
1616+ end
1717+ if stat(path .. "/.git") then
1818+ vcs_root_cache[path] = {
1919+ type = "git",
2020+ root = path,
2121+ }
2222+ return vcs_root_cache[path]
2323+ end
2424+ if stat(path .. "/.jj") then
2525+ vcs_root_cache[path] = {
2626+ type = "jj",
2727+ root = path,
2828+ }
2929+ return vcs_root_cache[path]
3030+ end
3131+ -- Could add more, but this is a good start.
3232+ if vim.fs.dirname(path) == path then
3333+ -- Reached the root directory without finding a VCS directory.
3434+ return nil
3535+ end
3636+ -- Recursively check the parent directory.
3737+ return _get_vcs_root(vim.fs.dirname(path))
3838+end
3939+4040+local is_ignored_cache = {}
4141+4242+-- Check if file should be ignored as per gitignore rules.
4343+function M.is_ignored(path)
4444+ if is_ignored_cache[path] ~= nil then
4545+ return is_ignored_cache[path]
4646+ end
4747+4848+ local absolute_path = vim.fs.normalize(vim.fs.abspath(path))
4949+5050+ local vcs_root = _get_vcs_root(vim.fs.dirname(absolute_path))
5151+ if vcs_root then
5252+ local gitdir
5353+ if vcs_root.type == "git" then
5454+ gitdir = vcs_root.root .. "/.git"
5555+ elseif vcs_root.type == "jj" then
5656+ -- I should really read `git_target`, but let's keep this simple...
5757+ gitdir = vcs_root.root .. "/.jj/repo/store/git"
5858+ else
5959+ error("Unknown VCS type: " .. vim.inspect(vcs_root.type))
6060+ end
6161+6262+ local out = run
6363+ .run_with_timeout({
6464+ "git",
6565+ "--git-dir",
6666+ gitdir,
6767+ "--work-tree",
6868+ vcs_root.root,
6969+ "check-ignore",
7070+ "--no-index",
7171+ absolute_path,
7272+ }, {})
7373+ :wait()
7474+ if out.code == 0 then
7575+ is_ignored_cache[path] = true
7676+ return true
7777+ end
7878+ end
7979+8080+ return false
8181+end
8282+8383+function M.clear_ignored_cache()
8484+ is_ignored_cache = {}
8585+end
8686+8787+return M