1
0
mirror of https://github.com/osmarks/mycorrhiza.git synced 2024-12-04 18:19:54 +00:00

Implement tree generation

This commit is contained in:
Timur Ismagilov 2020-08-06 01:19:14 +05:00
parent 673c2b1836
commit 6992312c46
4 changed files with 116 additions and 3 deletions

View File

@ -1,7 +1,16 @@
# mycorrhiza wiki
A wiki engine inspired by fungi. Not production-ready.
Current version: 0.7 (or more?)
This branch is devoted to version 0.8.
* [ ] Tree generation
* [x] Basic generation
* [ ] Generation that takes non-existent hyphae into account¹
* [ ] History
* [ ] Saving all changes to git
* [ ] Make it possible to see any previous version
* [ ] A nice UI for that
¹ Current algorithm won't detect `a/b/c` as a child of `a` if `a/b` does not exist.
## Current features
* Edit pages through html forms

View File

@ -8,6 +8,7 @@ import (
"os"
"github.com/bouncepaw/mycorrhiza/gemtext"
"github.com/bouncepaw/mycorrhiza/tree"
)
func init() {
@ -70,7 +71,7 @@ func handlerPage(w http.ResponseWriter, rq *http.Request) {
%[2]s
%[3]s
</article>
<hr>
<hr/>
<form action="/upload-binary/%[1]s"
method="post" enctype="multipart/form-data">
<label for="upload-binary__input">Upload new binary part</label>
@ -78,8 +79,15 @@ func handlerPage(w http.ResponseWriter, rq *http.Request) {
<input type="file" id="upload-binary__input" name="binary"/>
<input type="submit"/>
</form>
<hr/>
<aside>
%[4]s
</aside>
</main>
`, hyphaName, naviTitle(hyphaName), contents)
`, hyphaName,
naviTitle(hyphaName),
contents,
tree.TreeAsHtml(hyphaName, IterateHyphaNamesWith))
w.Header().Set("Content-Type", "text/html;charset=utf-8")
w.WriteHeader(http.StatusOK)
w.Write([]byte(base(hyphaName, form)))

View File

@ -19,6 +19,13 @@ var HyphaPattern = regexp.MustCompile(`[^?!:#@><*|"\'&%]+`)
// HyphaStorage is a mapping between canonical hypha names and their meta information.
var HyphaStorage = make(map[string]*HyphaData)
// IterateHyphaNamesWith is a closure to be passed to subpackages to let them iterate all hypha names read-only.
func IterateHyphaNamesWith(f func(string)) {
for hyphaName, _ := range HyphaStorage {
f(hyphaName)
}
}
// HttpErr is used by many handlers to signal errors in a compact way.
func HttpErr(w http.ResponseWriter, status int, name, title, errMsg string) {
log.Println(errMsg, "for", name)

89
tree/tree.go Normal file
View File

@ -0,0 +1,89 @@
package tree
import (
"fmt"
"path"
"sort"
"strings"
)
// If Name == "", the tree is empty.
type tree struct {
name string
siblings []string
descendants []*tree
root bool
hyphaIterator func(func(string))
}
// TreeAsHtml generates a tree for `hyphaName`. `hyphaStorage` has this type because package `tree` has no access to `HyphaData` data type. One day it shall have it, I guess.
func TreeAsHtml(hyphaName string, hyphaIterator func(func(string))) string {
t := &tree{name: hyphaName, root: true, hyphaIterator: hyphaIterator}
t.fill()
return t.asHtml()
}
// 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
}
// Compares names and does something with them, may generate a subtree.
func (t *tree) compareNamesAndAppend(name2 string) {
switch {
case t.name == name2:
case t.root && path.Dir(t.name) == path.Dir(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 {
for _, siblingName := range t.siblings {
html += navitreeEntry(siblingName, "navitree__sibling")
}
html += navitreeEntry(t.name, "navitree__pagename")
} else {
html += navitreeEntry(t.name, "navitree__name")
}
for _, subtree := range t.descendants {
html += subtree.asHtml()
}
return `<ul class="navitree__node">` + html + `</ul>`
}
// 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))
}