diff options
| author | Adictya <[email protected]> | 2025-05-15 08:42:45 +0530 |
|---|---|---|
| committer | Adam <[email protected]> | 2025-05-15 08:29:54 -0500 |
| commit | 15bf40bc102ed5426fa2148b9e8f39acef1174a0 (patch) | |
| tree | 4b005eba92703864a4bc2884b3cdc1fb5ab21a3c /internal/fileutil | |
| parent | a33e3e25b6646b6128975836d7f182e89f5eb891 (diff) | |
| download | opencode-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.go | 162 |
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 +} |
