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 -
+
@@ -78,8 +79,15 @@ func handlerPage(w http.ResponseWriter, rq *http.Request) {
+
+ -`, 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)) +}