diff options
| author | isaac-scarrott <[email protected]> | 2025-04-27 15:38:40 +0100 |
|---|---|---|
| committer | Kujtim Hoxha <[email protected]> | 2025-04-27 18:01:31 +0200 |
| commit | 3c2b0f4dd03f4b9d366a4667608390923618bb0c (patch) | |
| tree | 3d4f5927791d7215ab6316d9ab229abbf3ca3629 /internal/llm/tools | |
| parent | 9738886620f3b3bbc77cea6faadbf21b6f864119 (diff) | |
| download | opencode-3c2b0f4dd03f4b9d366a4667608390923618bb0c.tar.gz opencode-3c2b0f4dd03f4b9d366a4667608390923618bb0c.zip | |
[feature/ripgrep-glob] Add ripgrep-based file globbing to improve performance
- Introduced `globWithRipgrep` function to perform file globbing using the `rg` (ripgrep) command.
- Updated `globFiles` to prioritize ripgrep-based globbing and fall back to doublestar-based globbing if ripgrep fails.
- Added logic to handle ripgrep command execution, output parsing, and filtering of hidden files.
- Ensured results are sorted by path length and limited to the specified maximum number of matches.
- Modified imports to include `os/exec` and `bytes` for ripgrep integration.
Diffstat (limited to 'internal/llm/tools')
| -rw-r--r-- | internal/llm/tools/glob.go | 69 |
1 files changed, 69 insertions, 0 deletions
diff --git a/internal/llm/tools/glob.go b/internal/llm/tools/glob.go index 737b3ea27..dd224267c 100644 --- a/internal/llm/tools/glob.go +++ b/internal/llm/tools/glob.go @@ -1,11 +1,13 @@ package tools import ( + "bytes" "context" "encoding/json" "fmt" "io/fs" "os" + "os/exec" "path/filepath" "sort" "strings" @@ -132,6 +134,73 @@ func (g *globTool) Run(ctx context.Context, call ToolCall) (ToolResponse, error) } func globFiles(pattern, searchPath string, limit int) ([]string, bool, error) { + matches, err := globWithRipgrep(pattern, searchPath, limit) + if err == nil { + return matches, len(matches) >= limit, nil + } + + return globWithDoublestar(pattern, searchPath, limit) +} + +func globWithRipgrep( + pattern, searchRoot string, + limit int, +) ([]string, error) { + + if searchRoot == "" { + searchRoot = "." + } + + rgBin, err := exec.LookPath("rg") + if err != nil { + return nil, fmt.Errorf("ripgrep not found in $PATH: %w", err) + } + + if !filepath.IsAbs(pattern) && !strings.HasPrefix(pattern, "/") { + pattern = "/" + pattern + } + + args := []string{ + "--files", + "--null", + "--glob", pattern, + "-L", + } + + cmd := exec.Command(rgBin, args...) + cmd.Dir = searchRoot + + out, err := cmd.CombinedOutput() + if err != nil { + if ee, ok := err.(*exec.ExitError); ok && ee.ExitCode() == 1 { + return nil, nil + } + return nil, fmt.Errorf("ripgrep: %w\n%s", err, out) + } + + var matches []string + for _, p := range bytes.Split(out, []byte{0}) { + if len(p) == 0 { + continue + } + abs := filepath.Join(searchRoot, string(p)) + if skipHidden(abs) { + continue + } + matches = append(matches, abs) + } + + sort.SliceStable(matches, func(i, j int) bool { + return len(matches[i]) < len(matches[j]) + }) + + if len(matches) > limit { + matches = matches[:limit] + } + return matches, nil +} + +func globWithDoublestar(pattern, searchPath string, limit int) ([]string, bool, error) { if !strings.HasPrefix(pattern, "/") && !strings.HasPrefix(pattern, searchPath) { if !strings.HasSuffix(searchPath, "/") { searchPath += "/" |
