hackdoc: render TOC inline

Change-Id: Ib91e4d3b73354e7e19095ea62eed70a23ef96512
diff --git a/devtools/hackdoc/README.md b/devtools/hackdoc/README.md
index 0ee3f7b..7bf556a 100644
--- a/devtools/hackdoc/README.md
+++ b/devtools/hackdoc/README.md
@@ -13,6 +13,16 @@
 - https://hackdoc.hackerspace.pl/?ref=master for the `master` branch
 - https://hackdoc.hackerspace.pl/?ref=change/249 for the the source code at change '249'
 
+Special Markdown
+----------------
+
+We should be accepting a Somewhat Standard Subset Of Markdown. For reference, we're using the [blackfriday](https://godoc.org/gopkg.in/russross/blackfriday.v2) library with [CommonExtensions](https://godoc.org/gopkg.in/russross/blackfriday.v2#CommonExtensions) enabled.
+
+In addition, we also support Table of Contents autorendering, just place the following anywhere in your document to render a TOC:
+
+    [TOC]
+
+
 Local Rendering
 ---------------
 
diff --git a/devtools/hackdoc/markdown.go b/devtools/hackdoc/markdown.go
index cfd594d..5004642 100644
--- a/devtools/hackdoc/markdown.go
+++ b/devtools/hackdoc/markdown.go
@@ -28,11 +28,18 @@
 	parser := blackfriday.New(blackfriday.WithRenderer(r), blackfriday.WithExtensions(blackfriday.CommonExtensions))
 	ast := parser.Parse(input)
 
+	// Render table of contents (raw HTML) into bytes.
+	var tocB bytes.Buffer
+	tocB.Write([]byte(`<div class="toc">`))
+	r.RenderHeader(&tocB, ast)
+	tocB.Write([]byte(`</div>`))
+	toc := tocB.Bytes()
+
 	var buf bytes.Buffer
-	buf.Write([]byte(`<div class="toc"><h1>Page Contents</h1>`))
-	r.RenderHeader(&buf, ast)
-	buf.Write([]byte(`</div><div class="content">`))
+	buf.Write([]byte(`<div class="content">`))
+	// Render Markdown with some custom behaviour.
 	ast.Walk(func(node *blackfriday.Node, entering bool) blackfriday.WalkStatus {
+		// Fix intra-hackdoc links to contain ?ref=
 		if ref != "" && entering && node.Type == blackfriday.Link || node.Type == blackfriday.Image {
 			dest := string(node.Destination)
 			u, err := url.Parse(dest)
@@ -41,9 +48,14 @@
 				q["ref"] = []string{ref}
 				u.RawQuery = q.Encode()
 				node.Destination = []byte(u.String())
-				glog.Infof("link fix %q -> %q", dest, u.String())
+				glog.V(10).Infof("link fix %q -> %q", dest, u.String())
 			}
 		}
+		// Replace [TOC] anchor with a rendered TOC.
+		if entering && node.Type == blackfriday.Text && string(node.Literal) == "[TOC]" {
+			buf.Write(toc)
+			return blackfriday.GoToNext
+		}
 		return r.RenderNode(&buf, node, entering)
 	})
 	buf.Write([]byte(`</div>`))
diff --git a/devtools/hackdoc/tpl/default.html b/devtools/hackdoc/tpl/default.html
index e6280db..6211e38 100644
--- a/devtools/hackdoc/tpl/default.html
+++ b/devtools/hackdoc/tpl/default.html
@@ -169,7 +169,7 @@
     line-height: 1.5em;
 }
 
-.content ul {
+.content :not(li) > ul {
     padding-top: 0.5em;
     line-height: 1.5em;
 }
@@ -178,12 +178,20 @@
     padding-left: 1em;
 }
 
-.content ul li::before {
+.content :not(li) > ul > li::before {
     content: "•";
     color: #333;;
     display: inline-block;
     width: 1em;
-    margin-left: -1em;
+    margin-left: -0.5em;
+}
+
+.content li > ul > li::before {
+    content: "◦";
+    color: #333;;
+    display: inline-block;
+    width: 1em;
+    margin-left: -0.5em;
 }
 
 .content img {
@@ -193,37 +201,19 @@
 }
 
 .toc {
-    float: right;
-    padding: 1em 1em 1em 1em;
+    padding: .5em;
     border: 1px solid #ddd;
     background-color: #f8f8f8;
     margin: 2em;
     max-width: 30%;
-}
-
-.toc h1 {
-    font-size: 1.2em;
-    padding-bottom: 0.5em;
+    font-size: 1em;
+    font-family: sans-serif;
 }
 
 .toc a {
     text-decoration: none;
 }
 
-.toc li {
-    padding-left: 0.5em;
-}
-
-.toc ul {
-    list-style-type: disc;
-    padding-left: 1em;
-}
-
-.toc ul ul {
-    list-style-type: circle;
-}
-
-
 </style>
 {{ end }}
 {{ define "body" }}