ls.go 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. package fsext
  2. import (
  3. "os"
  4. "path/filepath"
  5. "github.com/charlievieth/fastwalk"
  6. ignore "github.com/sabhiram/go-gitignore"
  7. )
  8. // CommonIgnorePatterns contains commonly ignored files and directories
  9. var CommonIgnorePatterns = []string{
  10. // Version control
  11. ".git",
  12. ".svn",
  13. ".hg",
  14. ".bzr",
  15. // IDE and editor files
  16. ".vscode",
  17. ".idea",
  18. "*.swp",
  19. "*.swo",
  20. "*~",
  21. ".DS_Store",
  22. "Thumbs.db",
  23. // Build artifacts and dependencies
  24. "node_modules",
  25. "target",
  26. "build",
  27. "dist",
  28. "out",
  29. "bin",
  30. "obj",
  31. "*.o",
  32. "*.so",
  33. "*.dylib",
  34. "*.dll",
  35. "*.exe",
  36. // Logs and temporary files
  37. "*.log",
  38. "*.tmp",
  39. "*.temp",
  40. ".cache",
  41. ".tmp",
  42. // Language-specific
  43. "__pycache__",
  44. "*.pyc",
  45. "*.pyo",
  46. ".pytest_cache",
  47. "vendor",
  48. "Cargo.lock",
  49. "package-lock.json",
  50. "yarn.lock",
  51. "pnpm-lock.yaml",
  52. // OS generated files
  53. ".Trash",
  54. ".Spotlight-V100",
  55. ".fseventsd",
  56. // Crush
  57. ".crush",
  58. }
  59. type DirectoryLister struct {
  60. gitignore *ignore.GitIgnore
  61. commonIgnore *ignore.GitIgnore
  62. rootPath string
  63. }
  64. func NewDirectoryLister(rootPath string) *DirectoryLister {
  65. dl := &DirectoryLister{
  66. rootPath: rootPath,
  67. }
  68. // Load gitignore if it exists
  69. gitignorePath := filepath.Join(rootPath, ".gitignore")
  70. if _, err := os.Stat(gitignorePath); err == nil {
  71. if gi, err := ignore.CompileIgnoreFile(gitignorePath); err == nil {
  72. dl.gitignore = gi
  73. }
  74. }
  75. // Create common ignore patterns
  76. dl.commonIgnore = ignore.CompileIgnoreLines(CommonIgnorePatterns...)
  77. return dl
  78. }
  79. func (dl *DirectoryLister) shouldIgnore(path string, ignorePatterns []string) bool {
  80. relPath, err := filepath.Rel(dl.rootPath, path)
  81. if err != nil {
  82. relPath = path
  83. }
  84. // Check common ignore patterns
  85. if dl.commonIgnore.MatchesPath(relPath) {
  86. return true
  87. }
  88. // Check gitignore patterns if available
  89. if dl.gitignore != nil && dl.gitignore.MatchesPath(relPath) {
  90. return true
  91. }
  92. base := filepath.Base(path)
  93. for _, pattern := range ignorePatterns {
  94. matched, err := filepath.Match(pattern, base)
  95. if err == nil && matched {
  96. return true
  97. }
  98. }
  99. return false
  100. }
  101. // ListDirectory lists files and directories in the specified path,
  102. func ListDirectory(initialPath string, ignorePatterns []string, limit int) ([]string, bool, error) {
  103. var results []string
  104. truncated := false
  105. dl := NewDirectoryLister(initialPath)
  106. conf := fastwalk.Config{
  107. Follow: true,
  108. // Use forward slashes when running a Windows binary under WSL or MSYS
  109. ToSlash: fastwalk.DefaultToSlash(),
  110. Sort: fastwalk.SortDirsFirst,
  111. }
  112. err := fastwalk.Walk(&conf, initialPath, func(path string, d os.DirEntry, err error) error {
  113. if err != nil {
  114. return nil // Skip files we don't have permission to access
  115. }
  116. if dl.shouldIgnore(path, ignorePatterns) {
  117. if d.IsDir() {
  118. return filepath.SkipDir
  119. }
  120. return nil
  121. }
  122. if path != initialPath {
  123. if d.IsDir() {
  124. path = path + string(filepath.Separator)
  125. }
  126. results = append(results, path)
  127. }
  128. if limit > 0 && len(results) >= limit {
  129. truncated = true
  130. return filepath.SkipAll
  131. }
  132. return nil
  133. })
  134. if err != nil {
  135. return nil, truncated, err
  136. }
  137. return results, truncated, nil
  138. }