Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions internal/filetree/filetree.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
package filetree

import (
"path/filepath"
"sort"
"strings"
)
Expand All @@ -28,9 +29,18 @@ func New(paths []string) *FileTree {
tree := &FileTree{
root: make(Node),
}

for _, path := range paths {
tree.addPath(strings.Split(path, "/"))
// Normalize path separators for cross-platform compatibility
normalizedPath := filepath.ToSlash(path)

// Split path into components, automatically filtering empty parts
parts := strings.FieldsFunc(normalizedPath, func(r rune) bool {
return r == '/'
})

if len(parts) > 0 {
tree.addPath(parts)
}
}

return tree
Expand Down
52 changes: 52 additions & 0 deletions internal/filetree/filetree_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,56 @@ func TestFileTree(t *testing.T) {
t.Errorf("Expected:\n%s\n\nGot:\n%s", expected, result)
}
})

t.Run("mixed path separators", func(t *testing.T) {
// Test that FileTree can handle mixed Windows and Unix paths
paths := []string{
"file1.txt",
"dir\\subdir\\file2.txt", // Windows-style
"dir/file3.txt", // Unix-style
}
result := Build(paths, "")
expected := strings.Join([]string{
"/",
"├── dir/",
"│ ├── subdir/",
"│ │ └── file2.txt",
"│ └── file3.txt",
"└── file1.txt",
}, "\n")

if result != expected {
t.Errorf("Expected:\n%s\n\nGot:\n%s", expected, result)
}
})

t.Run("edge cases with slashes", func(t *testing.T) {
// Test edge cases: double slashes, leading/trailing slashes
paths := []string{
"normal/path.txt",
"double//slash.txt", // double slash
"/leading/slash.txt", // leading slash
"trailing/slash/.txt", // trailing slash
"multiple///slashes.txt", // multiple slashes
}
result := Build(paths, "")
expected := strings.Join([]string{
"/",
"├── double/",
"│ └── slash.txt",
"├── leading/",
"│ └── slash.txt",
"├── multiple/",
"│ └── slashes.txt",
"├── normal/",
"│ └── path.txt",
"└── trailing/",
" └── slash/",
" └── .txt",
}, "\n")

if result != expected {
t.Errorf("Expected:\n%s\n\nGot:\n%s", expected, result)
}
})
}
16 changes: 9 additions & 7 deletions internal/processor/processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -212,19 +212,22 @@ func (p *Processor) collectFiles() ([]string, error) {
if info.IsDir() {
return nil
}

// Get relative path for ignore checking
// Get relative path and normalize separators for cross-platform consistency
relPath, err := filepath.Rel(p.rootDir, path)
if err != nil {
return fmt.Errorf("failed to get relative path: %w", err)
}

// Skip if file matches ignore pattern
if p.matcher != nil && p.matcher.Match(strings.Split(relPath, "/"), false) {

// Normalize to forward slashes for consistent processing
// This ensures gitignore patterns work and output is uniform across platforms
normalizedPath := filepath.ToSlash(relPath)
// Check gitignore patterns using normalized path
if p.matcher != nil && p.matcher.Match(strings.Split(normalizedPath, "/"), false) {
return nil
}

files = append(files, relPath)
// Store normalized path for consistent cross-platform output
files = append(files, normalizedPath)
return nil
})

Expand Down Expand Up @@ -274,6 +277,5 @@ func (p *Processor) writeContents(w *bufio.Writer, files []string) error {
return err
}
}

return nil
}
Loading