diff --git a/README.md b/README.md
index 64dc545..c25b673 100644
--- a/README.md
+++ b/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
diff --git a/http_readers.go b/http_readers.go
index f00673d..fab83ee 100644
--- a/http_readers.go
+++ b/http_readers.go
@@ -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
-
+
+
+
-`, 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)))
diff --git a/main.go b/main.go
index 7d08e0b..e6b1ede 100644
--- a/main.go
+++ b/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)
diff --git a/tree/tree.go b/tree/tree.go
new file mode 100644
index 0000000..d7e8c45
--- /dev/null
+++ b/tree/tree.go
@@ -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 ``
+}
+
+// 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(`
+ %s
+
+`, class, name, beautifulName(name))
+}