Commit c757113

Eric Bower  ·  2026-05-12 00:06:58 -0400 EDT
parent cce1481
chore: better text file detection
1 files changed,  +64, -6
+64, -6
......@@ -9,7 +9,6 @@ import (
99 "html/template"
1010 "io"
1111 "log/slog"
12- "math"
1312 "os"
1413 "path/filepath"
1514 "sort"
......@@ -284,13 +283,72 @@ func isText(s string) bool {
284283 return true
285284 }
286285
286+// newSet builds a string set from a list of values for O(1) lookup.
287+func newSet(vals ...string) map[string]struct{} {
288+ m := make(map[string]struct{}, len(vals))
289+ for _, v := range vals {
290+ m[v] = struct{}{}
291+ }
292+ return m
293+}
294+
295+// knownBinaryExts lists extensions that are always binary regardless of content.
296+var knownBinaryExts = newSet(
297+ // images
298+ ".png", ".jpg", ".jpeg", ".gif", ".bmp", ".ico", ".webp", ".svg", ".tiff", ".tif", ".psd",
299+ // archives / compressed
300+ ".zip", ".tar", ".gz", ".bz2", ".xz", ".7z", ".rar", ".tgz", ".war", ".jar",
301+ // documents
302+ ".pdf", ".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx",
303+ // executables / libraries
304+ ".exe", ".dll", ".so", ".dylib", ".a", ".o",
305+ // fonts
306+ ".ttf", ".otf", ".woff", ".woff2", ".eot",
307+ // audio / video
308+ ".mp3", ".mp4", ".avi", ".mov", ".wav", ".flac", ".ogg", ".webm",
309+ // data / serialized
310+ ".pb", ".msgpack", ".parquet", ".avro",
311+ // other
312+ ".class", ".pyc", ".pyo", ".wasm", ".db", ".sqlite", ".sqlite3",
313+)
314+
315+// knownTextExts lists extensions that are always text regardless of content.
316+var knownTextExts = newSet(
317+ // code
318+ ".go", ".py", ".js", ".ts", ".tsx", ".jsx", ".java", ".c", ".h", ".cpp", ".hpp", ".rs", ".rb", ".php", ".pl", ".sh", ".bash", ".zsh", ".fish", ".ps1",
319+ // markup / data
320+ ".html", ".htm", ".css", ".scss", ".less", ".xml", ".json", ".yaml", ".yml", ".toml", ".ini", ".cfg", ".conf",
321+ // docs
322+ ".md", ".markdown", ".txt", ".rst", ".tex", ".bib", ".csv", ".tsv",
323+ // config / build
324+ ".Dockerfile", ".dockerignore", ".gitignore", ".gitattributes", ".editorconfig",
325+ // other text
326+ ".diff", ".patch", ".log", ".sql", ".graphql", ".proto", ".makefile", ".cmake",
327+)
328+
287329 // isTextFile reports whether the file has a known extension indicating
288330 // a text file, or if a significant chunk of the specified file looks like
289331 // correct UTF-8; that is, if it is likely that the file contains human-
290-// readable text.
291-func isTextFile(text string) bool {
292- num := math.Min(float64(len(text)), 1024)
293- return isText(text[0:int(num)])
332+// readable text. Extension check takes priority as a fast path.
333+func isTextFile(filename, text string) bool {
334+ ext := strings.ToLower(filepath.Ext(filename))
335+
336+ // fast path: known binary extension
337+ if _, ok := knownBinaryExts[ext]; ok {
338+ return false
339+ }
340+ // fast path: known text extension
341+ if _, ok := knownTextExts[ext]; ok {
342+ return true
343+ }
344+ // also check the full filename for extensionless known text files
345+ nameLower := strings.ToLower(filename)
346+ if _, ok := knownTextExts["."+nameLower]; ok {
347+ return true
348+ }
349+
350+ // fallback: inspect bytes
351+ return isText(text)
294352 }
295353
296354 func toPretty(b int64) string {
......@@ -454,7 +512,7 @@ func (c *Config) writeHTMLTreeFile(pageData *PageData, treeItem *TreeItem) strin
454512 bail(err)
455513 str := string(b)
456514
457- treeItem.IsTextFile = isTextFile(str)
515+ treeItem.IsTextFile = isTextFile(treeItem.Entry.Name(), str)
458516
459517 contents := "binary file, cannot display"
460518 if treeItem.IsTextFile {