From: Nigel Tao
Date: Mon, 10 Oct 2011 03:44:37 +0000 (+1100)
Subject: html: add a Render function.
X-Git-Tag: weekly.2011-10-18~135
X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=be8b4d943ff5fc9c169b91410ce11a377c8aa6b9;p=gostls13.git
html: add a Render function.
R=mikesamuel, andybalholm
CC=golang-dev
https://golang.org/cl/5218041
---
diff --git a/src/pkg/html/Makefile b/src/pkg/html/Makefile
index 28dc1a3f52..2d664720d3 100644
--- a/src/pkg/html/Makefile
+++ b/src/pkg/html/Makefile
@@ -12,6 +12,7 @@ GOFILES=\
escape.go\
node.go\
parse.go\
+ render.go\
token.go\
include ../../Make.pkg
diff --git a/src/pkg/html/escape.go b/src/pkg/html/escape.go
index 0de97c5ac1..4d0661ff36 100644
--- a/src/pkg/html/escape.go
+++ b/src/pkg/html/escape.go
@@ -6,6 +6,7 @@ package html
import (
"bytes"
+ "os"
"strings"
"utf8"
)
@@ -184,10 +185,12 @@ func unescape(b []byte) []byte {
const escapedChars = `&'<>"`
-func escape(buf *bytes.Buffer, s string) {
+func escape(w writer, s string) os.Error {
i := strings.IndexAny(s, escapedChars)
for i != -1 {
- buf.WriteString(s[0:i])
+ if _, err := w.WriteString(s[:i]); err != nil {
+ return err
+ }
var esc string
switch s[i] {
case '&':
@@ -204,10 +207,13 @@ func escape(buf *bytes.Buffer, s string) {
panic("unrecognized escape character")
}
s = s[i+1:]
- buf.WriteString(esc)
+ if _, err := w.WriteString(esc); err != nil {
+ return err
+ }
i = strings.IndexAny(s, escapedChars)
}
- buf.WriteString(s)
+ _, err := w.WriteString(s)
+ return err
}
// EscapeString escapes special characters like "<" to become "<". It
diff --git a/src/pkg/html/parse_test.go b/src/pkg/html/parse_test.go
index 7d918d2508..5a473694b3 100644
--- a/src/pkg/html/parse_test.go
+++ b/src/pkg/html/parse_test.go
@@ -134,7 +134,7 @@ func TestParser(t *testing.T) {
if err != nil {
t.Fatal(err)
}
- actual, err := dump(doc)
+ got, err := dump(doc)
if err != nil {
t.Fatal(err)
}
@@ -147,9 +147,24 @@ func TestParser(t *testing.T) {
if err != nil {
t.Fatal(err)
}
- expected := string(b)
- if actual != expected {
- t.Errorf("%s test #%d %q, actual vs expected:\n----\n%s----\n%s----", filename, i, text, actual, expected)
+ if want := string(b); got != want {
+ t.Errorf("%s test #%d %q, got vs want:\n----\n%s----\n%s----", filename, i, text, got, want)
+ }
+ // Check that rendering and re-parsing results in an identical tree.
+ pr, pw := io.Pipe()
+ go func() {
+ pw.CloseWithError(Render(pw, doc))
+ }()
+ doc1, err := Parse(pr)
+ if err != nil {
+ t.Fatal(err)
+ }
+ got1, err := dump(doc1)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if got != got1 {
+ t.Errorf("%s test #%d %q, got vs got1:\n----\n%s----\n%s----", filename, i, text, got, got1)
}
}
}
diff --git a/src/pkg/html/render.go b/src/pkg/html/render.go
new file mode 100644
index 0000000000..bf7b5995a1
--- /dev/null
+++ b/src/pkg/html/render.go
@@ -0,0 +1,159 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package html
+
+import (
+ "bufio"
+ "fmt"
+ "io"
+ "os"
+)
+
+type writer interface {
+ io.Writer
+ WriteByte(byte) os.Error
+ WriteString(string) (int, os.Error)
+}
+
+// Render renders the parse tree n to the given writer.
+//
+// For 'well-formed' parse trees, calling Parse on the output of Render will
+// result in a clone of the original tree.
+//
+// 'Well-formed' is not formally specified, but calling Parse on arbitrary
+// input results in a 'well-formed' parse tree if Parse does not return an
+// error. Programmatically constructed trees are typically also 'well-formed',
+// but it is possible to construct a tree that, when rendered and re-parsed,
+// results in a different tree. A simple example is that a solitary text node
+// would become a tree containing , and elements. Another
+// example is that the programmatic equivalent of "abc" becomes
+// "
abc".
+//
+// Comment nodes are elided from the output, analogous to Parse skipping over
+// any input.
+func Render(w io.Writer, n *Node) os.Error {
+ if x, ok := w.(writer); ok {
+ return render(x, n)
+ }
+ buf := bufio.NewWriter(w)
+ if err := render(buf, n); err != nil {
+ return err
+ }
+ return buf.Flush()
+}
+
+func render(w writer, n *Node) os.Error {
+ // Render non-element nodes; these are the easy cases.
+ switch n.Type {
+ case ErrorNode:
+ return os.NewError("html: cannot render an ErrorNode node")
+ case TextNode:
+ return escape(w, n.Data)
+ case DocumentNode:
+ for _, c := range n.Child {
+ if err := render(w, c); err != nil {
+ return err
+ }
+ }
+ return nil
+ case ElementNode:
+ // No-op.
+ case CommentNode:
+ return nil
+ case DoctypeNode:
+ if _, err := w.WriteString("')
+ default:
+ return os.NewError("html: unknown node type")
+ }
+
+ // TODO: figure out what to do with