@@ -26,10 +26,19 @@ type FileInfo struct {
26
26
Code string `json:"code"`
27
27
}
28
28
29
+ // New type to track excluded files and directories
30
+ type ExcludedInfo struct {
31
+ Directories map [string ]int // Directory path -> count of excluded files
32
+ Extensions map [string ]int // File extension -> count of excluded files
33
+ TotalFiles int // Total number of excluded files
34
+ Files []string // List of excluded files (if total ≤ 20)
35
+ }
36
+
29
37
type treeNode struct {
30
38
name string
31
39
children []* treeNode
32
40
isDir bool
41
+ excluded bool
33
42
}
34
43
35
44
func ReadExcludePatterns (patternExclude string , noDefaultExcludes bool ) ([]string , error ) {
@@ -58,9 +67,9 @@ func ReadExcludePatterns(patternExclude string, noDefaultExcludes bool) ([]strin
58
67
// If user has a default.glob, it overrides the default patterns
59
68
if _ , err := os .Stat (userDefaultGlob ); err == nil {
60
69
return readGlobFile (userDefaultGlob )
61
- }
70
+ }
62
71
63
- // Read other user-defined patterns
72
+ // Read other user-defined patterns
64
73
userPatterns , _ := readGlobFilesFromDir (userPatternsDir )
65
74
66
75
// Combine user patterns with default patterns (if not disabled)
@@ -70,6 +79,30 @@ func ReadExcludePatterns(patternExclude string, noDefaultExcludes bool) ([]strin
70
79
return patterns , nil
71
80
}
72
81
82
+ // Helper functions to track exclusions
83
+ func trackExcludedFile (excluded * ExcludedInfo , path string , mu * sync.Mutex ) {
84
+ mu .Lock ()
85
+ defer mu .Unlock ()
86
+
87
+ excluded .TotalFiles ++
88
+
89
+ // Track the directory
90
+ dir := filepath .Dir (path )
91
+ excluded .Directories [dir ]++
92
+
93
+ // Track the extension
94
+ ext := filepath .Ext (path )
95
+ if ext != "" {
96
+ excluded .Extensions [ext ]++
97
+ }
98
+
99
+ // Only store individual files if we haven't exceeded 20
100
+ if excluded .TotalFiles <= 20 {
101
+ excluded .Files = append (excluded .Files , path )
102
+ }
103
+ }
104
+
105
+
73
106
func readGlobFile (filename string ) ([]string , error ) {
74
107
file , err := os .Open (filename )
75
108
if err != nil {
@@ -111,15 +144,27 @@ func readGlobFilesFromDir(dir string) ([]string, error) {
111
144
return patterns , err
112
145
}
113
146
114
- func WalkDirectory (rootPath string , includePatterns , excludePatterns []string , patternExclude string , includePriority , lineNumber , relativePaths , excludeFromTree , noCodeblock , noDefaultExcludes bool ) (string , []FileInfo , error ) {
147
+ func trackExcludedDirectory (excluded * ExcludedInfo , path string , mu * sync.Mutex ) {
148
+ mu .Lock ()
149
+ defer mu .Unlock ()
150
+ excluded .Directories [path ] = 0 // Initialize directory count
151
+ }
152
+
153
+ func WalkDirectory (rootPath string , includePatterns , excludePatterns []string , patternExclude string , includePriority , lineNumber , relativePaths , excludeFromTree , noCodeblock , noDefaultExcludes bool ) (string , []FileInfo , * ExcludedInfo , error ) {
115
154
var files []FileInfo
116
155
var mu sync.Mutex
117
156
var wg sync.WaitGroup
118
157
158
+ excluded := & ExcludedInfo {
159
+ Directories : make (map [string ]int ),
160
+ Extensions : make (map [string ]int ),
161
+ Files : make ([]string , 0 ),
162
+ }
163
+
119
164
// Read exclude patterns
120
165
defaultExcludes , err := ReadExcludePatterns (patternExclude , noDefaultExcludes )
121
166
if err != nil {
122
- return "" , nil , fmt .Errorf ("failed to read exclude patterns: %w" , err )
167
+ return "" , nil , nil , fmt .Errorf ("failed to read exclude patterns: %w" , err )
123
168
}
124
169
125
170
// Combine user-provided exclude patterns with default excludes (if not disabled)
@@ -131,34 +176,34 @@ func WalkDirectory(rootPath string, includePatterns, excludePatterns []string, p
131
176
// Read .gitignore if it exists
132
177
gitignore , err := readGitignore (rootPath )
133
178
if err != nil {
134
- return "" , nil , fmt .Errorf ("failed to read .gitignore: %w" , err )
179
+ return "" , nil , nil , fmt .Errorf ("failed to read .gitignore: %w" , err )
135
180
}
136
181
137
182
// Check if rootPath is a file or directory
138
183
fileInfo , err := os .Stat (rootPath )
139
184
if err != nil {
140
- return "" , nil , fmt .Errorf ("failed to get file info: %w" , err )
185
+ return "" , nil , nil , fmt .Errorf ("failed to get file info: %w" , err )
141
186
}
142
187
143
188
// Check if rootPath is a single PDF file
144
189
if ! fileInfo .IsDir () {
145
190
isPDF , err := pdf .IsPDF (rootPath )
146
191
if err != nil {
147
- return "" , nil , fmt .Errorf ("failed to check if file is PDF: %w" , err )
192
+ return "" , nil , nil , fmt .Errorf ("failed to check if file is PDF: %w" , err )
148
193
}
149
194
150
195
if isPDF {
151
196
// Process single PDF file directly
152
197
content , err := pdf .ConvertPDFToMarkdown (rootPath , false )
153
198
if err != nil {
154
- return "" , nil , fmt .Errorf ("failed to convert PDF: %w" , err )
199
+ return "" , nil , nil , fmt .Errorf ("failed to convert PDF: %w" , err )
155
200
}
156
201
157
202
return fmt .Sprintf ("File: %s" , rootPath ), []FileInfo {{
158
203
Path : rootPath ,
159
204
Extension : ".md" ,
160
205
Code : content ,
161
- }}, nil
206
+ }}, excluded , nil
162
207
}
163
208
}
164
209
@@ -173,13 +218,15 @@ func WalkDirectory(rootPath string, includePatterns, excludePatterns []string, p
173
218
defer wg .Done ()
174
219
processFile (rootPath , relPath , filepath .Dir (rootPath ), lineNumber , relativePaths , noCodeblock , & mu , & files )
175
220
}()
221
+ } else {
222
+ trackExcludedFile (excluded , rootPath , & mu )
176
223
}
177
224
treeString = fmt .Sprintf ("File: %s" , rootPath )
178
225
} else {
179
226
// Generate the tree representation for directory
180
227
treeString , err = generateTreeString (rootPath , allExcludePatterns )
181
228
if err != nil {
182
- return "" , nil , fmt .Errorf ("failed to generate directory tree: %w" , err )
229
+ return "" , nil , nil , fmt .Errorf ("failed to generate directory tree: %w" , err )
183
230
}
184
231
185
232
// Process files in directory
@@ -196,8 +243,15 @@ func WalkDirectory(rootPath string, includePatterns, excludePatterns []string, p
196
243
// Check if the current path (file or directory) should be excluded
197
244
if shouldExcludePath (relPath , allExcludePatterns , gitignore ) {
198
245
if info .IsDir () {
246
+ trackExcludedDirectory (excluded , path , & mu )
199
247
return filepath .SkipDir
200
248
}
249
+ trackExcludedFile (excluded , path , & mu )
250
+ return nil
251
+ }
252
+
253
+ if ! info .IsDir () && ! shouldIncludeFile (relPath , includePatterns , allExcludePatterns , gitignore , includePriority ) {
254
+ trackExcludedFile (excluded , path , & mu )
201
255
return nil
202
256
}
203
257
@@ -216,18 +270,18 @@ func WalkDirectory(rootPath string, includePatterns, excludePatterns []string, p
216
270
wg .Wait ()
217
271
218
272
if err != nil {
219
- return "" , nil , err
273
+ return "" , nil , excluded , err
220
274
}
221
275
222
- return treeString , files , nil
276
+ return treeString , files , excluded , nil
223
277
}
224
278
225
279
// New helper function to check if a path should be excluded
226
280
func shouldExcludePath (path string , excludePatterns []string , gitignore * ignore.GitIgnore ) bool {
227
281
for _ , pattern := range excludePatterns {
228
282
if match , _ := doublestar .Match (pattern , path ); match {
229
283
return true
230
- }
284
+ }
231
285
}
232
286
return gitignore != nil && gitignore .MatchesPath (path )
233
287
}
@@ -299,7 +353,7 @@ func isBinaryFile(filePath string) (bool, error) {
299
353
n , err := file .Read (buffer )
300
354
if err != nil && err != io .EOF {
301
355
return false , err
302
- }
356
+ }
303
357
304
358
// Use http.DetectContentType to determine the content type
305
359
contentType := http .DetectContentType (buffer [:n ])
@@ -397,6 +451,8 @@ func processFile(path, relPath string, rootPath string, lineNumber, relativePath
397
451
398
452
func generateTreeString (rootPath string , excludePatterns []string ) (string , error ) {
399
453
root := & treeNode {name : filepath .Base (rootPath ), isDir : true }
454
+ hasExclusions := false
455
+
400
456
err := filepath .Walk (rootPath , func (path string , info fs.FileInfo , err error ) error {
401
457
if err != nil {
402
458
return err
@@ -413,10 +469,60 @@ func generateTreeString(rootPath string, excludePatterns []string) (string, erro
413
469
}
414
470
415
471
// Check if the path should be excluded
416
- if isExcluded (relPath , excludePatterns ) {
472
+ excluded := isExcluded (relPath , excludePatterns )
473
+ if excluded {
474
+ hasExclusions = true
417
475
if info .IsDir () {
476
+ // Add the excluded directory to the tree with an X marker
477
+ parts := strings .Split (relPath , string (os .PathSeparator ))
478
+ current := root
479
+ for i , part := range parts {
480
+ found := false
481
+ for _ , child := range current .children {
482
+ if child .name == part {
483
+ current = child
484
+ found = true
485
+ break
486
+ }
487
+ }
488
+ if ! found {
489
+ newNode := & treeNode {
490
+ name : part ,
491
+ isDir : true ,
492
+ excluded : true ,
493
+ }
494
+ current .children = append (current .children , newNode )
495
+ current = newNode
496
+ }
497
+ if i == len (parts )- 1 {
498
+ current .isDir = true
499
+ current .excluded = true
500
+ }
501
+ }
418
502
return filepath .SkipDir
419
503
}
504
+ // Add excluded files to the tree with an X marker
505
+ parts := strings .Split (relPath , string (os .PathSeparator ))
506
+ current := root
507
+ for i , part := range parts {
508
+ found := false
509
+ for _ , child := range current .children {
510
+ if child .name == part {
511
+ current = child
512
+ found = true
513
+ break
514
+ }
515
+ }
516
+ if ! found {
517
+ newNode := & treeNode {
518
+ name : part ,
519
+ isDir : i < len (parts )- 1 ,
520
+ excluded : true ,
521
+ }
522
+ current .children = append (current .children , newNode )
523
+ current = newNode
524
+ }
525
+ }
420
526
return nil
421
527
}
422
528
@@ -449,6 +555,9 @@ func generateTreeString(rootPath string, excludePatterns []string) (string, erro
449
555
}
450
556
451
557
var output strings.Builder
558
+ if hasExclusions {
559
+ output .WriteString ("(Files/directories marked with ❌ are excluded or not included here)\n \n " )
560
+ }
452
561
output .WriteString (root .name + "/\n " )
453
562
for i , child := range root .children {
454
563
printTree (child , "" , i == len (root .children )- 1 , & output )
@@ -470,6 +579,9 @@ func printTree(node *treeNode, prefix string, isLast bool, output *strings.Build
470
579
if node .isDir {
471
580
output .WriteString ("/" )
472
581
}
582
+ if node .excluded {
583
+ output .WriteString (" ❌" )
584
+ }
473
585
output .WriteString ("\n " )
474
586
475
587
sort .Slice (node .children , func (i , j int ) bool {
@@ -483,6 +595,7 @@ func printTree(node *treeNode, prefix string, isLast bool, output *strings.Build
483
595
printTree (child , prefix , i == len (node .children )- 1 , output )
484
596
}
485
597
}
598
+
486
599
func isExcluded (path string , patterns []string ) bool {
487
600
for _ , pattern := range patterns {
488
601
if match , _ := doublestar .Match (pattern , path ); match {
0 commit comments