1
0
mirror of https://github.com/osmarks/mycorrhiza.git synced 2024-10-30 19:56:16 +00:00
mycorrhiza/tree/tree.go

199 lines
5.5 KiB
Go
Raw Normal View History

2020-08-05 20:19:14 +00:00
package tree
import (
"fmt"
"path"
"sort"
"strings"
"sync"
2021-01-10 13:01:38 +00:00
2021-02-17 18:41:35 +00:00
"github.com/bouncepaw/mycorrhiza/hyphae"
2021-01-10 13:01:38 +00:00
"github.com/bouncepaw/mycorrhiza/util"
2020-08-05 20:19:14 +00:00
)
func findSiblings(hyphaName string) []*sibling {
parentHyphaName := ""
2021-10-01 08:57:00 +00:00
if hyphaRawDir := path.Dir(hyphaName); hyphaRawDir != "." {
parentHyphaName = hyphaRawDir
2021-10-01 08:57:00 +00:00
}
var (
2021-10-01 08:57:00 +00:00
siblingsMap = make(map[string]bool)
siblingCheck = func(h hyphae.Hypher) hyphae.CheckResult {
switch {
case h.CanonicalName() == hyphaName, // Hypha is no sibling of itself
h.CanonicalName() == parentHyphaName: // Parent hypha is no sibling of its child
return hyphae.CheckContinue
}
if (parentHyphaName != "" && strings.HasPrefix(h.CanonicalName(), parentHyphaName+"/")) ||
(parentHyphaName == "") {
2021-10-01 08:57:00 +00:00
var (
rawSubPath = strings.TrimPrefix(h.CanonicalName(), parentHyphaName)[1:]
2021-10-01 08:57:00 +00:00
slashIdx = strings.IndexRune(rawSubPath, '/')
)
if slashIdx > -1 {
var sibPath = h.CanonicalName()[:slashIdx+len(parentHyphaName)+1]
2021-10-01 08:57:00 +00:00
if _, exists := siblingsMap[sibPath]; !exists {
siblingsMap[sibPath] = false
}
} else { // it is a straight sibling
siblingsMap[h.CanonicalName()] = true
2021-10-01 08:57:00 +00:00
}
}
return hyphae.CheckContinue
}
i7n = hyphae.NewIteration()
)
2021-10-01 08:57:00 +00:00
siblingsMap[hyphaName] = true
i7n.AddCheck(siblingCheck)
i7n.Ignite()
2021-10-01 08:57:00 +00:00
siblings := make([]*sibling, len(siblingsMap))
sibIdx := 0
for sibName, exists := range siblingsMap {
siblings[sibIdx] = &sibling{sibName, 0, 0, exists}
sibIdx++
}
sort.Slice(siblings, func(i, j int) bool {
return siblings[i].name < siblings[j].name
})
return siblings
2020-08-05 20:19:14 +00:00
}
func countSubhyphae(siblings []*sibling) {
var (
subhyphaCheck = func(h hyphae.Hypher) hyphae.CheckResult {
for _, s := range siblings {
if path.Dir(h.CanonicalName()) == s.name {
s.directSubhyphaeCount++
return hyphae.CheckContinue
} else if strings.HasPrefix(h.CanonicalName(), s.name+"/") {
s.indirectSubhyphaeCount++
return hyphae.CheckContinue
}
}
return hyphae.CheckContinue
}
i7n = hyphae.NewIteration()
)
i7n.AddCheck(subhyphaCheck)
i7n.Ignite()
}
// Tree generates a tree for `hyphaName` as html and returns next and previous hyphae if any.
func Tree(hyphaName string) (siblingsHTML, childrenHTML, prev, next string) {
children := make([]child, 0)
I := 0
// The tree is generated in two iterations of hyphae storage:
// 1. Find all siblings (sorted)
// 2. Count how many subhyphae siblings have
//
// We also have to figure out what is going on with the descendants: who is a child of whom. We do that in parallel with (2) because we can.
// One of the siblings is the hypha with name `hyphaName`
var siblings []*sibling
wg := sync.WaitGroup{}
wg.Add(2)
go func() {
siblings = findSiblings(hyphaName)
countSubhyphae(siblings)
wg.Done()
}()
go func() {
children = figureOutChildren(hyphaName).children
wg.Done()
}()
wg.Wait()
for i, s := range siblings {
if s.name == hyphaName {
I = i
2021-07-12 15:23:25 +00:00
siblingsHTML += fmt.Sprintf(`<li class="sibling-hyphae__entry sibling-hyphae__entry_this"><span>%s</span></li>`, util.BeautifulName(path.Base(hyphaName)))
} else {
siblingsHTML += siblingHTML(s)
}
2020-08-05 20:19:14 +00:00
}
2021-04-07 16:49:00 +00:00
if I != 0 && len(siblings) > 1 {
prev = siblings[I-1].name
}
2021-04-07 16:49:00 +00:00
if I != len(siblings)-1 && len(siblings) > 1 {
next = siblings[I+1].name
}
2021-07-12 15:23:25 +00:00
return fmt.Sprintf(`<ul class="sibling-hyphae__list">%s</ul>`, siblingsHTML), subhyphaeMatrix(children), prev, next
}
type child struct {
name string
2021-10-01 09:18:11 +00:00
exists bool
children []child
}
func figureOutChildren(hyphaName string) child {
var (
descPrefix = hyphaName + "/"
child = child{hyphaName, true, make([]child, 0)}
)
for desc := range hyphae.YieldExistingHyphae() {
var descName = desc.CanonicalName()
if strings.HasPrefix(descName, descPrefix) {
var subPath = strings.TrimPrefix(descName, descPrefix)
addHyphaToChild(descName, subPath, &child)
}
}
return child
}
func addHyphaToChild(hyphaName, subPath string, child *child) {
// when hyphaName = "root/a/b", subPath = "a/b", and child.name = "root"
// addHyphaToChild("root/a/b", "b", child{"root/a"})
// when hyphaName = "root/a/b", subPath = "b", and child.name = "root/a"
// set .exists=true for "root/a/b", and create it if it isn't there already
var exists = !strings.Contains(subPath, "/")
if exists {
var subchild = findOrCreateSubchild(subPath, child)
subchild.exists = true
} else {
var (
firstSlash = strings.IndexRune(subPath, '/')
firstDir = subPath[:firstSlash]
restOfPath = subPath[firstSlash+1:]
subchild = findOrCreateSubchild(firstDir, child)
)
addHyphaToChild(hyphaName, restOfPath, subchild)
2021-10-01 09:18:11 +00:00
}
}
2021-10-01 09:18:11 +00:00
func findOrCreateSubchild(name string, baseChild *child) *child {
// when name = "a", and baseChild.name = "root"
// if baseChild.children contains "root/a", return it
// else create it and return that
var fullName = baseChild.name + "/" + name
for i := range baseChild.children {
if baseChild.children[i].name == fullName {
return &baseChild.children[i]
}
}
baseChild.children = append(baseChild.children, child{fullName, false, make([]child, 0)})
return &baseChild.children[len(baseChild.children)-1]
}
type sibling struct {
name string
directSubhyphaeCount int
indirectSubhyphaeCount int
2021-10-01 08:57:00 +00:00
exists bool
2020-08-05 20:19:14 +00:00
}
func subhyphaeMatrix(children []child) (html string) {
2021-02-19 18:12:36 +00:00
sort.Slice(children, func(i, j int) bool {
return children[i].name < children[j].name
})
for _, child := range children {
html += childHTML(&child)
2021-02-19 18:12:36 +00:00
}
return html
}