Skip to content

Instantly share code, notes, and snippets.

@nf
Created January 23, 2013 23:47
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nf/4615889 to your computer and use it in GitHub Desktop.
Save nf/4615889 to your computer and use it in GitHub Desktop.
// Before pulling the latest changes from the go repository, first
// run this tool:
// cd go-tour
// go build -o conv conv.go
// ./conv < static/index.html
// It will create tour.article and a prog directory full of .go files.
// Read over tour.article and check that it makes sense.
// You will probably want to:
// hg pull -u
// go get -u code.google.com/p/go.talks/pkg/present
// and then resolve merge conflicts (most likely static/index.html,
// which can just be removed.)
// You will then probably want to go through tour.article and rename
// the prog/*.go files to the same names as the English tour,
// preserving any translations.
package main
import (
"bytes"
"exp/html"
"flag"
"fmt"
"io/ioutil"
"log"
"os"
"regexp"
"strings"
)
var excludeClass = flag.String("exclude_class", "appengineMode", "name of class to exclude from text")
func main() {
flag.Parse()
root, err := html.Parse(os.Stdin)
if err != nil {
log.Fatal(err)
}
f, err := os.Create("tour.article")
if err != nil {
log.Fatal(err)
}
defer f.Close()
fmt.Fprintln(f, header)
for n, s := range findAll(root, hasClass("slide")) {
title := text(find(s, isTag("h2")))
fmt.Fprintf(f, "* %v\n\n", title)
fmt.Fprint(f, slideText(s))
source := text(find(s, hasClass("source")))
if source != "" {
fn := "prog/" + filename(n, title)
ioutil.WriteFile(fn, []byte(source), 0666)
fmt.Fprintf(f, ".play %s\n\n", fn)
}
}
}
const header = `
A Tour of Go
The Go Authors
http://golang.org
`
var nonSafe = regexp.MustCompile(`[^a-z ]`)
func filename(n int, title string) string {
title = strings.ToLower(title)
title = nonSafe.ReplaceAllString(title, "")
title = strings.TrimSpace(title)
title = strings.Replace(title, " ", "-", -1)
return fmt.Sprintf("%s.go", title)
}
func slideText(s *html.Node) string {
var buf bytes.Buffer
walk(s, func(n *html.Node) bool {
switch {
case isTag("li")(n):
buf.WriteString("- ")
buf.WriteString(unwrap(text(n)))
buf.WriteByte('\n')
case isTag("p")(n):
buf.WriteString(unwrap(text(n)))
case isTag("pre")(n) && !hasClass("source")(n):
buf.WriteString(indent(text(n)))
buf.WriteByte('\n')
default:
return true
}
return false
})
return buf.String()
}
func indent(s string) string {
var buf bytes.Buffer
for _, l := range strings.Split(s, "\n") {
buf.WriteByte('\t')
buf.WriteString(l)
buf.WriteByte('\n')
}
return buf.String()
}
func unwrap(s string) string {
var buf bytes.Buffer
var cont bool
for _, l := range strings.Split(s, "\n") {
l = strings.TrimSpace(l)
if len(l) == 0 {
if cont {
buf.WriteByte('\n')
buf.WriteByte('\n')
}
cont = false
} else {
if cont {
buf.WriteByte(' ')
}
buf.WriteString(l)
cont = true
}
}
return buf.String()
}
func text(n *html.Node) string {
var buf bytes.Buffer
walk(n, func(n *html.Node) bool {
if hasClass(*excludeClass)(n) {
return false
}
if isTag("a")(n) {
t := childText(n)
fmt.Fprintf(&buf, "[[%s][%s]]", attr(n, "href"), t)
return false
}
if isTag("code")(n) {
fmt.Fprint(&buf, highlight(n, "`"))
return false
}
if isTag("b")(n) {
fmt.Fprint(&buf, highlight(n, "*"))
return false
}
if isTag("i")(n) {
fmt.Fprint(&buf, highlight(n, "_"))
return false
}
if n.Type == html.TextNode {
buf.WriteString(n.Data)
}
return true
})
return buf.String()
}
func childText(node *html.Node) string {
var buf bytes.Buffer
for n := node.FirstChild; n != nil; n = n.NextSibling {
fmt.Fprint(&buf, text(n))
}
return buf.String()
}
func highlight(node *html.Node, char string) string {
t := strings.Replace(childText(node), " ", char, -1)
return fmt.Sprintf("%s%s%s", char, t, char)
}
type selector func(*html.Node) bool
func isTag(name string) selector {
return func(n *html.Node) bool {
return n.DataAtom.String() == name
}
}
func hasClass(name string) selector {
return func(n *html.Node) bool {
for _, a := range n.Attr {
if a.Key == "class" {
for _, c := range strings.Fields(a.Val) {
if c == name {
return true
}
}
}
}
return false
}
}
func attr(node *html.Node, key string) (value string) {
for _, attr := range node.Attr {
if attr.Key == key {
return attr.Val
}
}
return ""
}
func findAll(node *html.Node, fn selector) (nodes []*html.Node) {
walk(node, func(n *html.Node) bool {
if fn(n) {
nodes = append(nodes, n)
}
return true
})
return
}
func find(n *html.Node, fn selector) *html.Node {
var result *html.Node
walk(n, func(n *html.Node) bool {
if result != nil {
return false
}
if fn(n) {
result = n
return false
}
return true
})
return result
}
func walk(n *html.Node, fn selector) {
var w func(*html.Node, bool)
w = func(n *html.Node, first bool) {
if n == nil {
return
}
if fn(n) {
w(n.FirstChild, false)
}
if !first {
w(n.NextSibling, false)
}
return
}
w(n, true)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment