4.4 KB
files.go
package sharing
import (
"fmt"
"path/filepath"
"strings"
)
// PathPart represents a breadcrumb segment for navigation.
type PathPart struct {
Name string
Path string
}
// ParsePathParts returns breadcrumb parts for a path.
func ParsePathParts(path string) []PathPart {
if path == "" {
return nil
}
parts := strings.Split(path, "/")
var result []PathPart
for i, part := range parts {
if part == "" {
continue
}
result = append(result, PathPart{
Name: part,
Path: strings.Join(parts[:i+1], "/"),
})
}
return result
}
// ParentPath returns the parent directory path.
func ParentPath(path string) string {
if path == "" {
return ""
}
parent := filepath.Dir(path)
if parent == "." {
return ""
}
return parent
}
// FileName returns the base name of a path.
func FileName(path string) string {
return filepath.Base(path)
}
// FileExtension returns the file extension without the leading dot.
func FileExtension(path string) string {
return strings.TrimPrefix(filepath.Ext(path), ".")
}
// FormatSize formats a file size in human readable format.
func FormatSize(size int64) string {
if size >= 1048576 {
return fmt.Sprintf("%.1f MB", float64(size)/1048576.0)
}
if size >= 1024 {
return fmt.Sprintf("%.1f KB", float64(size)/1024.0)
}
return fmt.Sprintf("%d B", size)
}
// IsTextFile returns true if the file appears to be a text file.
func IsTextFile(path string) bool {
name := FileName(path)
ext := FileExtension(path)
// Check specific filenames first
textFiles := map[string]bool{
"Dockerfile": true,
"Makefile": true,
"Rakefile": true,
"Gemfile": true,
"Procfile": true,
".gitignore": true,
".gitattributes": true,
".dockerignore": true,
".editorconfig": true,
".env.example": true,
".prettierrc": true,
".eslintrc": true,
".babelrc": true,
"LICENSE": true,
"CHANGELOG": true,
"AUTHORS": true,
"CONTRIBUTORS": true,
}
if textFiles[name] {
return true
}
// Check extensions
textExtensions := map[string]bool{
"": true, // no extension
"go": true,
"js": true,
"ts": true,
"tsx": true,
"jsx": true,
"html": true,
"css": true,
"scss": true,
"less": true,
"json": true,
"yaml": true,
"yml": true,
"md": true,
"txt": true,
"sh": true,
"bash": true,
"zsh": true,
"fish": true,
"sql": true,
"xml": true,
"toml": true,
"ini": true,
"cfg": true,
"conf": true,
"mod": true,
"sum": true,
"lock": true,
"py": true,
"rb": true,
"rs": true,
"c": true,
"h": true,
"cpp": true,
"hpp": true,
"java": true,
"kt": true,
"swift": true,
"php": true,
"vue": true,
"svelte": true,
"env": true,
"gitignore": true,
}
return textExtensions[ext]
}
// IsMarkdown returns true if the file is a markdown file.
func IsMarkdown(path string) bool {
ext := FileExtension(path)
return ext == "md" || ext == "markdown"
}
// PrismLanguage returns the Prism.js language class for syntax highlighting.
func PrismLanguage(path string) string {
name := FileName(path)
ext := FileExtension(path)
// Special filenames
fileLanguages := map[string]string{
"Dockerfile": "docker",
"Makefile": "makefile",
".gitignore": "gitignore",
".dockerignore": "gitignore",
"Gemfile": "ruby",
"Rakefile": "ruby",
}
if lang, ok := fileLanguages[name]; ok {
return lang
}
// Extension mapping
extLanguages := map[string]string{
"go": "go",
"js": "javascript",
"ts": "typescript",
"tsx": "tsx",
"jsx": "jsx",
"html": "html",
"css": "css",
"scss": "scss",
"less": "less",
"json": "json",
"yaml": "yaml",
"yml": "yaml",
"md": "markdown",
"sh": "bash",
"bash": "bash",
"zsh": "bash",
"sql": "sql",
"xml": "xml",
"toml": "toml",
"py": "python",
"rb": "ruby",
"rs": "rust",
"c": "c",
"h": "c",
"cpp": "cpp",
"hpp": "cpp",
"java": "java",
"kt": "kotlin",
"swift": "swift",
"php": "php",
"vue": "markup",
"svelte": "markup",
"ini": "ini",
"env": "bash",
"mod": "go-mod",
}
if lang, ok := extLanguages[ext]; ok {
return lang
}
return "plaintext"
}