From d5cf88f7e72d1847719c9f0bd81d6c5017950a28 Mon Sep 17 00:00:00 2001 From: Mark McGranaghan Date: Sat, 29 Sep 2012 13:49:52 -0700 Subject: [PATCH] special case for heading --- style/generate.html | 194 ++++++++++++++++++++++++++++++++++++++++++++ tool/generate.go | 8 +- 2 files changed, 199 insertions(+), 3 deletions(-) create mode 100644 style/generate.html diff --git a/style/generate.html b/style/generate.html new file mode 100644 index 0000000..b27626c --- /dev/null +++ b/style/generate.html @@ -0,0 +1,194 @@ + + + + + + Page Title + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + +

Line Filters

+
  
+
+

Generate literate-programming style HTML +documentation form Go source files.

+
package main
+
+import (
+    "fmt"
+    "io/ioutil"
+    "os"
+    "os/exec"
+    "regexp"
+    "strings"
+)
+  
+
+

Recognize doc lines, extract their comment prefixes.

+
var docsPat = regexp.MustCompile("^\\s*\\/\\/\\s")
+  
+
+

Abort on non-nil errors.

+
func check(err error) {
+    if err != nil {
+        panic(err)
+    }
+}
+  
+
+

For docs and code rendering: pipe source data +through binary at path with given argv, return +the output.

+
func pipedCmd(path string, argv []string, source string) string {
+    cmd := exec.Command(path, argv...)
+    in, err := cmd.StdinPipe()
+    check(err)
+    out, err := cmd.StdoutPipe()
+    check(err)
+    err = cmd.Start()
+    check(err)
+    in.Write([]byte(source))
+    check(err)
+    err = in.Close()
+    check(err)
+    bytes, err := ioutil.ReadAll(out)
+    check(err)
+    err = cmd.Wait()
+    check(err)
+    return string(bytes)
+}
+  
+
+

We'll break the code into {docs, code} pairs, +and then render those text segments before +including them in the HTML doc.

+
type segment struct {
+    docs, code, docsRendered, codeRendered string
+}
+
+func main() {  
+
+

Accept exactly 1 argument - the input filename.

+
    if len(os.Args) != 2 {
+        fmt.Fprintln(os.Stderr, "Usage: tool/generate input.go > output.html")
+        os.Exit(1)
+    }
+  
+
+

Ensure that we have markdown and pygmentize, +binaries, remember their paths.

+
    markdownPath, err := exec.LookPath("markdown")
+    check(err)
+    pygmentizePath, err := exec.LookPath("pygmentize")
+    check(err)
+  
+
+

Read the source file in, split into lines.

+
    sourceBytes, err := ioutil.ReadFile(os.Args[1])
+    check(err)
+    lines := strings.Split(string(sourceBytes), "\n")
+  
+
+

Group lines into docs/code segments. +Special case the header to go in its own segment.

+
    segments := []*segment{}
+    segments = append(segments, &segment{code: "", docs: docsPat.ReplaceAllString(lines[0], "")})
+    segments = append(segments, &segment{code: "", docs: ""})
+    lastLine := ""
+    for _, line := range lines[2:] {
+        head := segments[len(segments)-1]  
+
+

Doc line - trim off the comment markers.

+
        if (line == "" && lastLine == "docs") || docsPat.MatchString(line) {
+            trimLine := docsPat.ReplaceAllString(line, "")
+            if !(lastLine == "code" && head.docs != "") {
+                head.docs = head.docs + "\n" + trimLine
+            } else {
+                segments = append(segments, &segment{docs: trimLine, code: ""})
+            }
+            lastLine = "docs"  
+
+

Code line - preserve all whitespace.

+
        } else {
+            if !(lastLine == "docs" && head.code != "") {
+                head.code = head.code + "\n" + line
+            } else {
+                segments = append(segments, &segment{docs: "", code: line})
+            }
+            lastLine = "code"
+        }
+    }
+  
+
+

Render docs via markdown and code via +pygmentize in each segment.

+
    for _, seg := range segments {
+        seg.docsRendered = pipedCmd(markdownPath, []string{}, seg.docs)
+        seg.codeRendered = pipedCmd(pygmentizePath, []string{"-l", "go", "-f", "html"}, seg.code+"  ")
+    }
+  
+
+

Print HTML header.

+
    fmt.Print(`
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta http-eqiv="content-type" content="text/html;charset=utf-8">
+    <title>Page Title</title>
+    <link rel=stylesheet href="book.css">
+  </head>
+  <body>
+    <div id="container">
+      <div id="background"></div>
+      <table cellspacing="0" cellpadding="0">
+        <thead>
+          <tr><td class=docs></td><td class=code></td></tr>
+        </thead>
+        <tbody>`)
+  
+
+

Print HTML docs/code segments.

+
    for _, seg := range segments {
+        fmt.Printf("<tr><td class=docs>%s</td><td class=code>%s</td></tr>\n", seg.docsRendered, seg.codeRendered)
+    }
+  
+
+

Print HTML footer.

+
    fmt.Print(`
+        </tbody>
+      </table>
+    </div>
+  </body>
+</html>
+`)
+}
+  
+
+
+
+ + diff --git a/tool/generate.go b/tool/generate.go index 4cd11ac..ee29a63 100644 --- a/tool/generate.go +++ b/tool/generate.go @@ -1,5 +1,5 @@ // ## Line Filters - + // Generate literate-programming style HTML // documentation form Go source files. @@ -73,10 +73,12 @@ func main() { lines := strings.Split(string(sourceBytes), "\n") // Group lines into docs/code segments. + // Special case the header to go in its own segment. segments := []*segment{} + segments = append(segments, &segment{code: "", docs: docsPat.ReplaceAllString(lines[0], "")}) segments = append(segments, &segment{code: "", docs: ""}) lastLine := "" - for _, line := range lines { + for _, line := range lines[2:] { head := segments[len(segments)-1] // Doc line - trim off the comment markers. if (line == "" && lastLine == "docs") || docsPat.MatchString(line) { @@ -102,7 +104,7 @@ func main() { // `pygmentize` in each segment. for _, seg := range segments { seg.docsRendered = pipedCmd(markdownPath, []string{}, seg.docs) - seg.codeRendered = pipedCmd(pygmentizePath, []string{"-l", "go", "-f", "html"}, seg.code + " ") + seg.codeRendered = pipedCmd(pygmentizePath, []string{"-l", "go", "-f", "html"}, seg.code+" ") } // Print HTML header.