// tree.go — the repository tree, one fragment shared by every page.
package web
import (
"fmt"
"html"
"path/filepath"
"strings"
)
type treeNode struct {
Name string
Dirs []*treeNode
Files []*File
Mask uint8 // union of traced masks underneath
HasNotes bool
HasTraced bool
}
func aggregate(n *treeNode) {
for _, f := range n.Files {
if f.Traced {
n.HasTraced = true
n.Mask |= f.Mask
if len(f.Notes) > 0 {
n.HasNotes = true
}
}
}
for _, d := range n.Dirs {
aggregate(d)
n.Mask |= d.Mask
n.HasNotes = n.HasNotes || d.HasNotes
n.HasTraced = n.HasTraced || d.HasTraced
}
}
func buildTree(files []*File) *treeNode {
root := &treeNode{}
for _, f := range files {
parts := strings.Split(f.Path, "/")
node := root
for _, d := range parts[:len(parts)-1] {
var next *treeNode
for _, c := range node.Dirs {
if c.Name == d {
next = c
}
}
if next == nil {
next = &treeNode{Name: d}
node.Dirs = append(node.Dirs, next)
}
node = next
}
node.Files = append(node.Files, f)
}
return root
}
// treeHTML renders one unified tree: traced files carry coverage bars, dirs
// with traced content underneath get a mixed-color dot (and ◆ for notes) and
// default open. prefix is the dir path so far, used for state persistence;
// link maps a repo path to the href for that file's page.
func treeHTML(b *strings.Builder, n *treeNode, link func(path string) string, prefix string) {
for _, d := range n.Dirs {
dp := d.Name
if prefix != "" {
dp = prefix + "/" + d.Name
}
openAttr, cls := "", ""
if d.HasTraced {
openAttr, cls = " open", ` class="t"`
}
fmt.Fprintf(b, `%s`, cls, openAttr, html.EscapeString(dp), html.EscapeString(d.Name))
if d.HasTraced {
fmt.Fprintf(b, ``, d.Mask)
}
if d.HasNotes {
b.WriteString(`◆`)
}
b.WriteString(`
`)
treeHTML(b, d, link, dp)
b.WriteString(`