Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,10 @@ I make the assumption you are using Lazy
-- max_files = 5000, -- cap on total discovered files
-- exclude = { ".env", ".env.*", "node_modules", ".git", ... },
},
--- File Discovery:
--- - In git repos: Uses `git ls-files` which automatically respects .gitignore
--- - Non-git repos: Falls back to filesystem scanning with manual excludes
--- - Both methods apply the configured `exclude` list on top of gitignore

--- What autocomplete you use.
source = "cmp" | "blink",
Expand Down Expand Up @@ -139,7 +143,7 @@ I make the assumption you are using Lazy
When prompting, you can reference rules and files to add context to your request.

- `#` references rules — type `#` in the prompt to autocomplete rule files from your configured rule directories
- `@` references files — type `@` to fuzzy-search project files
- `@` references files — type `@` to fuzzy-search project files. This will exclude files that are in .gitignore.

Referenced content is automatically resolved and injected into the AI context. Requires cmp (`source = "cmp"` in your completion config).

Expand Down
68 changes: 67 additions & 1 deletion lua/99/extensions/files/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,59 @@ function M.set_project_root(root)
cache.files = {}
end

--- @param root string
--- @return boolean
local function is_git_repo(root)
local git_dir = vim.fs.joinpath(root, ".git")
local stat = vim.uv.fs_stat(git_dir)
-- Check if .git exists (can be directory OR file for worktrees/submodules)
return stat ~= nil
end
Comment thread
sentry[bot] marked this conversation as resolved.
Comment thread
codegirl-007 marked this conversation as resolved.

--- @param root string
--- @return _99.Files.File[]
local function scan_with_git_sync(root)
local cmd = string.format(
"git -C %s ls-files --cached --others --exclude-standard --deduplicate",
vim.fn.shellescape(root)
)
local output = vim.fn.system(cmd)

if vim.v.shell_error ~= 0 then
return nil
Comment thread
codegirl-007 marked this conversation as resolved.
end

if output == "" then
return {}
end

local files = {}
local count = 0

for line in output:gmatch("[^\n]+") do
if count >= config.max_files then
break
end

line = vim.trim(line)
if line ~= "" then
local name = line:match("([^/]+)$") or line
if
not matches_exclude_pattern(line) and not matches_exclude_pattern(name)
then
table.insert(files, {
path = line,
name = name,
absolute_path = vim.fs.joinpath(root, line),
})
count = count + 1
end
end
end

return files
end

--- @return string
function M.get_project_root()
return cache.root
Expand All @@ -83,6 +136,19 @@ function M.discover_files()
return {}
end

-- Try git-based discovery first if in a git repo
if is_git_repo(root) then
local git_files = scan_with_git_sync(root)
if git_files then
table.sort(git_files, function(a, b)
return a.path < b.path
end)
cache.files = git_files
return git_files
end
end

-- Fallback to filesystem scanning
local files = {}
local count = 0

Expand Down Expand Up @@ -140,7 +206,7 @@ end

--- @return _99.Files.File[]
function M.get_files()
if #cache.files == 0 and cache.root ~= "" then
if #cache.files == 0 then
return M.discover_files()
end
return cache.files
Expand Down
Loading