summaryrefslogtreecommitdiffhomepage
path: root/internal/llm/tools
diff options
context:
space:
mode:
authorisaac-scarrott <[email protected]>2025-04-27 15:38:40 +0100
committerKujtim Hoxha <[email protected]>2025-04-27 18:01:31 +0200
commit3c2b0f4dd03f4b9d366a4667608390923618bb0c (patch)
tree3d4f5927791d7215ab6316d9ab229abbf3ca3629 /internal/llm/tools
parent9738886620f3b3bbc77cea6faadbf21b6f864119 (diff)
downloadopencode-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.go69
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 += "/"