summaryrefslogtreecommitdiffhomepage
path: root/internal/fileutil
diff options
context:
space:
mode:
authorAdictya <[email protected]>2025-05-15 08:42:45 +0530
committerAdam <[email protected]>2025-05-15 08:29:54 -0500
commit15bf40bc102ed5426fa2148b9e8f39acef1174a0 (patch)
tree4b005eba92703864a4bc2884b3cdc1fb5ab21a3c /internal/fileutil
parenta33e3e25b6646b6128975836d7f182e89f5eb891 (diff)
downloadopencode-15bf40bc102ed5426fa2148b9e8f39acef1174a0.tar.gz
opencode-15bf40bc102ed5426fa2148b9e8f39acef1174a0.zip
feat(complete-module): add completions logic, dialog and providers
Diffstat (limited to 'internal/fileutil')
-rw-r--r--internal/fileutil/fileutil.go162
1 files changed, 162 insertions, 0 deletions
diff --git a/internal/fileutil/fileutil.go b/internal/fileutil/fileutil.go
new file mode 100644
index 000000000..4f2957835
--- /dev/null
+++ b/internal/fileutil/fileutil.go
@@ -0,0 +1,162 @@
+package fileutil
+
+import (
+ "fmt"
+ "io/fs"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "sort"
+ "strings"
+ "time"
+
+ "github.com/bmatcuk/doublestar/v4"
+)
+
+var (
+ rgPath string
+ fzfPath string
+)
+
+func init() {
+ var err error
+ rgPath, err = exec.LookPath("rg")
+ if err != nil {
+ // logging.("Ripgrep (rg) not found in $PATH. Some features might be limited or slower.")
+ rgPath = ""
+ }
+ fzfPath, err = exec.LookPath("fzf")
+ if err != nil {
+ // logging.Warn("FZF not found in $PATH. Some features might be limited or slower.")
+ fzfPath = ""
+ }
+}
+
+func GetRgCmd(globPattern string) *exec.Cmd {
+ if rgPath == "" {
+ return nil
+ }
+ rgArgs := []string{
+ "--files",
+ "-L",
+ "--null",
+ }
+ if globPattern != "" {
+ if !filepath.IsAbs(globPattern) && !strings.HasPrefix(globPattern, "/") {
+ globPattern = "/" + globPattern
+ }
+ rgArgs = append(rgArgs, "--glob", globPattern)
+ }
+ cmd := exec.Command(rgPath, rgArgs...)
+ cmd.Dir = "."
+ return cmd
+}
+
+func GetFzfCmd(query string) *exec.Cmd {
+ if fzfPath == "" {
+ return nil
+ }
+ fzfArgs := []string{
+ "--filter",
+ query,
+ "--read0",
+ "--print0",
+ }
+ cmd := exec.Command(fzfPath, fzfArgs...)
+ cmd.Dir = "."
+ return cmd
+}
+
+type FileInfo struct {
+ Path string
+ ModTime time.Time
+}
+
+func SkipHidden(path string) bool {
+ // Check for hidden files (starting with a dot)
+ base := filepath.Base(path)
+ if base != "." && strings.HasPrefix(base, ".") {
+ return true
+ }
+
+ commonIgnoredDirs := map[string]bool{
+ ".opencode": true,
+ "node_modules": true,
+ "vendor": true,
+ "dist": true,
+ "build": true,
+ "target": true,
+ ".git": true,
+ ".idea": true,
+ ".vscode": true,
+ "__pycache__": true,
+ "bin": true,
+ "obj": true,
+ "out": true,
+ "coverage": true,
+ "tmp": true,
+ "temp": true,
+ "logs": true,
+ "generated": true,
+ "bower_components": true,
+ "jspm_packages": true,
+ }
+
+ parts := strings.Split(path, string(os.PathSeparator))
+ for _, part := range parts {
+ if commonIgnoredDirs[part] {
+ return true
+ }
+ }
+ return false
+}
+
+func GlobWithDoublestar(pattern, searchPath string, limit int) ([]string, bool, error) {
+ fsys := os.DirFS(searchPath)
+ relPattern := strings.TrimPrefix(pattern, "/")
+ var matches []FileInfo
+
+ err := doublestar.GlobWalk(fsys, relPattern, func(path string, d fs.DirEntry) error {
+ if d.IsDir() {
+ return nil
+ }
+ if SkipHidden(path) {
+ return nil
+ }
+ info, err := d.Info()
+ if err != nil {
+ return nil
+ }
+ absPath := path
+ if !strings.HasPrefix(absPath, searchPath) && searchPath != "." {
+ absPath = filepath.Join(searchPath, absPath)
+ } else if !strings.HasPrefix(absPath, "/") && searchPath == "." {
+ absPath = filepath.Join(searchPath, absPath) // Ensure relative paths are joined correctly
+ }
+
+ matches = append(matches, FileInfo{Path: absPath, ModTime: info.ModTime()})
+ if limit > 0 && len(matches) >= limit*2 {
+ return fs.SkipAll
+ }
+ return nil
+ })
+ if err != nil {
+ return nil, false, fmt.Errorf("glob walk error: %w", err)
+ }
+
+ sort.Slice(matches, func(i, j int) bool {
+ return matches[i].ModTime.After(matches[j].ModTime)
+ })
+
+ truncated := false
+ if limit > 0 && len(matches) > limit {
+ matches = matches[:limit]
+ truncated = true
+ }
+
+ results := make([]string, len(matches))
+ for i, m := range matches {
+ results[i] = m.Path
+ }
+ return results, truncated, nil
+}