This repository has been archived on 2026-10-25. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
simonfrankenandClaude Sonnet 5.5 f5fabfd093 Add / filter for the tree
A text input above the footer filters submodules by path (case-insensitive).
Matches keep their ancestors. A filtered-out selection moves to the next
visible submodule; esc closes the input and removes the filter.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-21 21:15:24 +02:00

69 lines
1.5 KiB
Go

package tree
import (
"path"
"submarine/internal/submodule"
)
type rowKind int
const (
rowDir rowKind = iota // non-selectable directory header, e.g. "modules/"
rowNode // a submodule
)
type row struct {
kind rowKind
depth int
label string // directory label for rowDir
node *submodule.Submodule
}
// rows flattens the visible part of the tree.
func (m *Model) rows() []row {
var out []row
m.appendRows(&out, m.roots, 0)
return out
}
// appendRows groups mods by the directory of their RelPath. Each group with a
// directory gets a header row; its submodules are indented one level deeper.
func (m *Model) appendRows(out *[]row, mods []*submodule.Submodule, depth int) {
for _, g := range groupByDir(m.filtered(mods)) {
d := depth
if g.dir != "." {
*out = append(*out, row{kind: rowDir, depth: depth, label: g.dir + "/"})
d++
}
for _, s := range g.mods {
*out = append(*out, row{kind: rowNode, depth: d, node: s})
if m.isExpanded(s) && len(s.Children) > 0 {
m.appendRows(out, s.Children, d+1)
}
}
}
}
type dirGroup struct {
dir string
mods []*submodule.Submodule
}
// groupByDir keeps the order in which directories first appear.
func groupByDir(mods []*submodule.Submodule) []dirGroup {
var groups []dirGroup
idx := map[string]int{}
for _, s := range mods {
dir := path.Dir(s.RelPath)
i, ok := idx[dir]
if !ok {
i = len(groups)
idx[dir] = i
groups = append(groups, dirGroup{dir: dir})
}
groups[i].mods = append(groups[i].mods, s)
}
return groups
}