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:
parent
673c2b1836
commit
6992312c46
11
README.md
11
README.md
@ -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
|
||||
|
@ -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)))
|
||||
|
7
main.go
7
main.go
@ -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
89
tree/tree.go
Normal 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))
|
||||
}
|
Loading…
Reference in New Issue
Block a user