mirror of
https://github.com/osmarks/mycorrhiza.git
synced 2025-12-06 16:38:08 +00:00
Make the relative hyphae block look nicer a little
This commit is contained in:
198
tree/tree.go
198
tree/tree.go
@@ -9,100 +9,122 @@ import (
|
||||
"github.com/bouncepaw/mycorrhiza/util"
|
||||
)
|
||||
|
||||
// If Name == "", the tree is empty.
|
||||
type tree struct {
|
||||
name string
|
||||
exists bool
|
||||
prevSibling string
|
||||
nextSibling string
|
||||
siblings []string
|
||||
descendants []*tree
|
||||
root bool
|
||||
hyphaIterator func(func(string))
|
||||
type sibling struct {
|
||||
name string
|
||||
hasChildren bool
|
||||
}
|
||||
|
||||
func (s *sibling) checkThisChild(hyphaName string) {
|
||||
if !s.hasChildren && path.Dir(hyphaName) == s.name {
|
||||
s.hasChildren = true
|
||||
}
|
||||
}
|
||||
|
||||
func (s *sibling) asHTML() string {
|
||||
class := "navitree__entry navitree__sibling"
|
||||
if s.hasChildren {
|
||||
class += " navitree__sibling_fertile navitree__entry_fertile"
|
||||
} else {
|
||||
class += " navitree__sibling_infertile navitree__entry_infertile"
|
||||
}
|
||||
return fmt.Sprintf(
|
||||
`<li class="%s"><a class="navitree__link" href="/hypha/%s">%s</a></li>`,
|
||||
class,
|
||||
s.name,
|
||||
util.BeautifulName(path.Base(s.name)),
|
||||
)
|
||||
}
|
||||
|
||||
type mainFamilyMember struct {
|
||||
name string
|
||||
children []*mainFamilyMember
|
||||
}
|
||||
|
||||
func (m *mainFamilyMember) checkThisChild(hyphaName string) (adopted bool) {
|
||||
if path.Dir(hyphaName) == m.name {
|
||||
m.children = append(m.children, &mainFamilyMember{
|
||||
name: hyphaName,
|
||||
children: make([]*mainFamilyMember, 0),
|
||||
})
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (m *mainFamilyMember) asHTML() string {
|
||||
if len(m.children) == 0 {
|
||||
return fmt.Sprintf(`<li class="navitree__entry navitree__entry_infertile navitree__trunk navitree__trunk_infertile"><a class="navitree__link" href="/hypha/%s">%s</a></li>`, m.name, util.BeautifulName(path.Base(m.name)))
|
||||
}
|
||||
sort.Slice(m.children, func(i, j int) bool {
|
||||
return m.children[i].name < m.children[j].name
|
||||
})
|
||||
html := fmt.Sprintf(`<li class="navitree__entry navitree__entry_fertile navitree__trunk navitree__trunk_fertile"><a class="navitree__link" href="/hypha/%s">%s</a><ul>`, m.name, util.BeautifulName(path.Base(m.name)))
|
||||
for _, child := range m.children {
|
||||
html += child.asHTML()
|
||||
}
|
||||
return html + `</li></ul></li>`
|
||||
}
|
||||
|
||||
func mainFamilyFromPool(hyphaName string, subhyphaePool map[string]bool) *mainFamilyMember {
|
||||
var (
|
||||
nestLevel = strings.Count(hyphaName, "/")
|
||||
adopted = make([]*mainFamilyMember, 0)
|
||||
)
|
||||
for subhyphaName, _ := range subhyphaePool {
|
||||
subnestLevel := strings.Count(subhyphaName, "/")
|
||||
if subnestLevel-1 == nestLevel && path.Dir(subhyphaName) == hyphaName {
|
||||
delete(subhyphaePool, subhyphaName)
|
||||
adopted = append(adopted, mainFamilyFromPool(subhyphaName, subhyphaePool))
|
||||
}
|
||||
}
|
||||
return &mainFamilyMember{name: hyphaName, children: adopted}
|
||||
}
|
||||
|
||||
// Tree generates a tree for `hyphaName` as html and returns next and previous hyphae if any.
|
||||
func Tree(hyphaName string, hyphaIterator func(func(string))) (html, prev, next string) {
|
||||
t := &tree{name: hyphaName, root: true, hyphaIterator: hyphaIterator}
|
||||
t.fill()
|
||||
return t.asHtml(), util.BeautifulName(t.prevSibling), util.BeautifulName(t.nextSibling)
|
||||
}
|
||||
|
||||
// subtree adds a descendant tree to `t` and returns that tree.
|
||||
func (t *tree) fork(descendantName string) *tree {
|
||||
subt := &tree{
|
||||
name: descendantName,
|
||||
root: false,
|
||||
hyphaIterator: t.hyphaIterator,
|
||||
}
|
||||
t.descendants = append(t.descendants, subt)
|
||||
return subt
|
||||
}
|
||||
|
||||
// Compare current prev next hyphae and decide if any of them should be set to `name2`.
|
||||
func (t *tree) prevNextDetermine(name2 string) {
|
||||
if name2 < t.name && (name2 > t.prevSibling || t.prevSibling == "") {
|
||||
t.prevSibling = name2
|
||||
} else if name2 > t.name && (name2 < t.nextSibling || t.nextSibling == "") {
|
||||
t.nextSibling = name2
|
||||
}
|
||||
}
|
||||
|
||||
// Compares names and does something with them, may generate a subtree.
|
||||
func (t *tree) compareNamesAndAppend(name2 string) {
|
||||
switch {
|
||||
case t.name == name2:
|
||||
t.exists = true
|
||||
case t.root && path.Dir(t.name) == path.Dir(name2):
|
||||
t.prevNextDetermine(name2)
|
||||
t.siblings = append(t.siblings, name2)
|
||||
case t.name == path.Dir(name2):
|
||||
t.fork(name2).fill()
|
||||
}
|
||||
}
|
||||
|
||||
// Fills t.siblings and t.descendants, sorts them and does the same to the descendants.
|
||||
func (t *tree) fill() {
|
||||
t.hyphaIterator(func(hyphaName string) {
|
||||
t.compareNamesAndAppend(hyphaName)
|
||||
})
|
||||
sort.Strings(t.siblings)
|
||||
sort.Slice(t.descendants, func(i, j int) bool {
|
||||
return t.descendants[i].name < t.descendants[j].name
|
||||
})
|
||||
}
|
||||
|
||||
// asHtml returns HTML representation of a tree.
|
||||
// It applies itself recursively on the tree's children.
|
||||
func (t *tree) asHtml() (html string) {
|
||||
if t.root {
|
||||
html += navitreeEntry(t.name, "navitree__pagename")
|
||||
} else {
|
||||
html += navitreeEntry(t.name, "navitree__name")
|
||||
}
|
||||
|
||||
for _, subtree := range t.descendants {
|
||||
html += subtree.asHtml()
|
||||
}
|
||||
|
||||
if t.root {
|
||||
for _, siblingName := range t.siblings {
|
||||
html += navitreeEntry(siblingName, "navitree__sibling")
|
||||
var (
|
||||
// One of the siblings is the hypha with name `hyphaName`
|
||||
siblings = findSiblings(hyphaName, hyphaIterator)
|
||||
subhyphaePool = make(map[string]bool)
|
||||
I int
|
||||
)
|
||||
hyphaIterator(func(otherHyphaName string) {
|
||||
for _, s := range siblings {
|
||||
s.checkThisChild(otherHyphaName)
|
||||
}
|
||||
if strings.HasPrefix(otherHyphaName, hyphaName+"/") {
|
||||
subhyphaePool[otherHyphaName] = true
|
||||
}
|
||||
})
|
||||
for i, s := range siblings {
|
||||
if s.name == hyphaName {
|
||||
I = i
|
||||
break
|
||||
}
|
||||
html += s.asHTML()
|
||||
}
|
||||
|
||||
return `<ul class="navitree__node">` + html + `</ul>`
|
||||
html += mainFamilyFromPool(hyphaName, subhyphaePool).asHTML()
|
||||
for _, s := range siblings[I+1:] {
|
||||
html += s.asHTML()
|
||||
}
|
||||
if I != 0 {
|
||||
prev = siblings[I-1].name
|
||||
}
|
||||
if I != len(siblings)-1 {
|
||||
next = siblings[I+1].name
|
||||
}
|
||||
return fmt.Sprintf(`<ul class="navitree">%s</ul>`, html), prev, next
|
||||
}
|
||||
|
||||
// Strip hypha name from all ancestor names, replace _ with spaces, title case
|
||||
func beautifulName(uglyName string) string {
|
||||
return strings.Title(strings.ReplaceAll(path.Base(uglyName), "_", " "))
|
||||
}
|
||||
|
||||
// navitreeEntry is a small utility function that makes generating html easier.
|
||||
func navitreeEntry(name, class string) string {
|
||||
return fmt.Sprintf(`<li class="navitree__entry %s">
|
||||
<a class="navitree__link" href="/page/%s">%s</a>
|
||||
</li>
|
||||
`, class, name, beautifulName(name))
|
||||
func findSiblings(hyphaName string, hyphaIterator func(func(string))) []*sibling {
|
||||
siblings := []*sibling{&sibling{name: hyphaName, hasChildren: true}}
|
||||
hyphaIterator(func(otherHyphaName string) {
|
||||
if path.Dir(hyphaName) == path.Dir(otherHyphaName) && hyphaName != otherHyphaName {
|
||||
siblings = append(siblings, &sibling{name: otherHyphaName, hasChildren: false})
|
||||
}
|
||||
})
|
||||
sort.Slice(siblings, func(i, j int) bool {
|
||||
return siblings[i].name < siblings[j].name
|
||||
})
|
||||
return siblings
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user