Commit 57d671f

Eric Bower  ·  2026-09-15 07:17:53 -0400 EDT
parent b93c126
fix: race when determining if file is text file
1 files changed,  +57, -26
+57, -26
......@@ -277,12 +277,14 @@ func (c *Config) parseText(filename string, text string) (string, error) {
277277 // that is, if it is likely that s is human-readable text.
278278 func isText(s string) bool {
279279 const max = 1024 // at least utf8.UTFMax
280+ truncated := false
280281 if len(s) > max {
281282 s = s[0:max]
283+ truncated = true
282284 }
283285 for i, c := range s {
284- if i+utf8.UTFMax > len(s) {
285- // last char may be incomplete - ignore
286+ if truncated && i+utf8.UTFMax > len(s) {
287+ // last char may be incomplete due to truncation - ignore
286288 break
287289 }
288290 if c == 0xFFFD || c < ' ' && c != '\n' && c != '\t' && c != '\f' && c != '\r' {
......@@ -326,12 +328,15 @@ var knownBinaryExts = newSet(
326328 var knownTextExts = newSet(
327329 // code
328330 ".go", ".py", ".js", ".ts", ".tsx", ".jsx", ".java", ".c", ".h", ".cpp", ".hpp", ".rs", ".rb", ".php", ".pl", ".sh", ".bash", ".zsh", ".fish", ".ps1",
331+ ".lua", ".swift", ".kt", ".kts", ".dart", ".zig", ".scala", ".ex", ".exs", ".erl", ".hs", ".clj", ".cljs", ".el", ".lisp", ".r", ".jl",
329332 // markup / data
330- ".html", ".htm", ".css", ".scss", ".less", ".xml", ".json", ".yaml", ".yml", ".toml", ".ini", ".cfg", ".conf",
333+ ".html", ".htm", ".css", ".scss", ".less", ".xml", ".json", ".jsonl", ".yaml", ".yml", ".toml", ".ini", ".cfg", ".conf",
331334 // docs
332335 ".md", ".markdown", ".txt", ".rst", ".tex", ".bib", ".csv", ".tsv",
333336 // config / build
334- ".Dockerfile", ".dockerignore", ".gitignore", ".gitattributes", ".editorconfig",
337+ ".dockerfile", ".dockerignore", ".gitignore", ".gitattributes", ".gitmodules", ".editorconfig",
338+ // templates / meta
339+ ".tmpl", ".template", ".mod", ".sum", ".env", ".lock", ".license", ".licence", ".copying",
335340 // other text
336341 ".diff", ".patch", ".log", ".sql", ".graphql", ".proto", ".makefile", ".cmake",
337342 )
......@@ -541,7 +546,11 @@ func (c *Config) writeHTMLTreeFile(pageData PageData, treeItem *TreeItem) string
541546
542547 contents := "binary file, cannot display"
543548 if treeItem.IsTextFile {
544- treeItem.NumLines = len(strings.Split(str, "\n"))
549+ if len(str) == 0 {
550+ treeItem.NumLines = 0
551+ } else {
552+ treeItem.NumLines = len(strings.Split(strings.TrimSuffix(str, "\n"), "\n"))
553+ }
545554 if isMarkdown(treeItem.Entry.Name()) {
546555 html, err := ParseMarkdown(str)
547556 bail(err)
......@@ -1141,6 +1150,7 @@ func (c *Config) writeRevision(repo *git.Repository, pageData *PageData, refs []
11411150 bail(err)
11421151
11431152 readme := ""
1153+ var readmeMu sync.Mutex
11441154 entries := make(chan *TreeItem)
11451155 subtrees := make(chan *TreeRoot)
11461156 tw := &TreeWalker{
......@@ -1150,42 +1160,63 @@ func (c *Config) writeRevision(repo *git.Repository, pageData *PageData, refs []
11501160 treeItem: entries,
11511161 tree: subtrees,
11521162 }
1153- wg.Add(1)
1163+
1164+ var twWg sync.WaitGroup
1165+ twWg.Add(1)
11541166 go func() {
1155- defer wg.Done()
1167+ defer twWg.Done()
11561168 tw.walk(tree, "")
11571169 }()
11581170
1159- wg.Add(1)
1171+ var roots []*TreeRoot
1172+ var rootsMu sync.Mutex
1173+ var subtreeWg sync.WaitGroup
1174+ subtreeWg.Add(1)
11601175 go func() {
1161- defer wg.Done()
1176+ defer subtreeWg.Done()
1177+ for t := range subtrees {
1178+ rootsMu.Lock()
1179+ roots = append(roots, t)
1180+ rootsMu.Unlock()
1181+ }
1182+ }()
1183+
1184+ var fileWg sync.WaitGroup
1185+ var entryWg sync.WaitGroup
1186+ entryWg.Add(1)
1187+ go func() {
1188+ defer entryWg.Done()
11621189 for e := range entries {
1163- wg.Add(1)
1190+ if e.IsDir {
1191+ continue
1192+ }
1193+ fileWg.Add(1)
11641194 go func(entry *TreeItem) {
1165- defer wg.Done()
1166- if entry.IsDir {
1167- return
1168- }
1169-
1195+ defer fileWg.Done()
11701196 readmeStr := c.writeHTMLTreeFile(*pageData, entry)
11711197 if readmeStr != "" {
1198+ readmeMu.Lock()
11721199 readme = readmeStr
1200+ readmeMu.Unlock()
11731201 }
11741202 }(e)
11751203 }
11761204 }()
11771205
1178- wg.Add(1)
1179- go func() {
1180- defer wg.Done()
1181- for t := range subtrees {
1182- wg.Add(1)
1183- go func(tree *TreeRoot) {
1184- defer wg.Done()
1185- c.writeTree(*pageData, tree)
1186- }(t)
1187- }
1188- }()
1206+ twWg.Wait()
1207+ subtreeWg.Wait()
1208+ entryWg.Wait()
1209+ fileWg.Wait()
1210+
1211+ var treeWg sync.WaitGroup
1212+ for _, t := range roots {
1213+ treeWg.Add(1)
1214+ go func(tree *TreeRoot) {
1215+ defer treeWg.Done()
1216+ c.writeTree(*pageData, tree)
1217+ }(t)
1218+ }
1219+ treeWg.Wait()
11891220
11901221 wg.Wait()
11911222