1
2
3
4
5 package comment
6
7 import (
8 "bytes"
9 "fmt"
10 "strconv"
11 )
12
13
14 type htmlPrinter struct {
15 *Printer
16 tight bool
17 }
18
19
20
21 func (p *Printer) HTML(d *Doc) []byte {
22 hp := &htmlPrinter{Printer: p}
23 var out bytes.Buffer
24 for _, x := range d.Content {
25 hp.block(&out, x)
26 }
27 return out.Bytes()
28 }
29
30
31 func (p *htmlPrinter) block(out *bytes.Buffer, x Block) {
32 switch x := x.(type) {
33 default:
34 fmt.Fprintf(out, "?%T", x)
35
36 case *Paragraph:
37 if !p.tight {
38 out.WriteString("<p>")
39 }
40 p.text(out, x.Text)
41 out.WriteString("\n")
42
43 case *Heading:
44 out.WriteString("<h")
45 h := strconv.Itoa(p.headingLevel())
46 out.WriteString(h)
47 if id := p.headingID(x); id != "" {
48 out.WriteString(` id="`)
49 p.escape(out, id)
50 out.WriteString(`"`)
51 }
52 out.WriteString(">")
53 p.text(out, x.Text)
54 out.WriteString("</h")
55 out.WriteString(h)
56 out.WriteString(">\n")
57
58 case *Code:
59 out.WriteString("<pre>")
60 p.escape(out, x.Text)
61 out.WriteString("</pre>\n")
62
63 case *List:
64 kind := "ol>\n"
65 if x.Items[0].Number == "" {
66 kind = "ul>\n"
67 }
68 out.WriteString("<")
69 out.WriteString(kind)
70 next := "1"
71 for _, item := range x.Items {
72 out.WriteString("<li")
73 if n := item.Number; n != "" {
74 if n != next {
75 out.WriteString(` value="`)
76 out.WriteString(n)
77 out.WriteString(`"`)
78 next = n
79 }
80 next = inc(next)
81 }
82 out.WriteString(">")
83 p.tight = !x.BlankBetween()
84 for _, blk := range item.Content {
85 p.block(out, blk)
86 }
87 p.tight = false
88 }
89 out.WriteString("</")
90 out.WriteString(kind)
91 }
92 }
93
94
95
96 func inc(s string) string {
97 b := []byte(s)
98 for i := len(b) - 1; i >= 0; i-- {
99 if b[i] < '9' {
100 b[i]++
101 return string(b)
102 }
103 b[i] = '0'
104 }
105 return "1" + string(b)
106 }
107
108
109 func (p *htmlPrinter) text(out *bytes.Buffer, x []Text) {
110 for _, t := range x {
111 switch t := t.(type) {
112 case Plain:
113 p.escape(out, string(t))
114 case Italic:
115 out.WriteString("<i>")
116 p.escape(out, string(t))
117 out.WriteString("</i>")
118 case *Link:
119 out.WriteString(`<a href="`)
120 p.escape(out, t.URL)
121 out.WriteString(`">`)
122 p.text(out, t.Text)
123 out.WriteString("</a>")
124 case *DocLink:
125 url := p.docLinkURL(t)
126 if url != "" {
127 out.WriteString(`<a href="`)
128 p.escape(out, url)
129 out.WriteString(`">`)
130 }
131 p.text(out, t.Text)
132 if url != "" {
133 out.WriteString("</a>")
134 }
135 }
136 }
137 }
138
139
140
141
142 func (p *htmlPrinter) escape(out *bytes.Buffer, s string) {
143 start := 0
144 for i := 0; i < len(s); i++ {
145 switch s[i] {
146 case '<':
147 out.WriteString(s[start:i])
148 out.WriteString("<")
149 start = i + 1
150 case '&':
151 out.WriteString(s[start:i])
152 out.WriteString("&")
153 start = i + 1
154 case '"':
155 out.WriteString(s[start:i])
156 out.WriteString(""")
157 start = i + 1
158 case '\'':
159 out.WriteString(s[start:i])
160 out.WriteString("'")
161 start = i + 1
162 case '>':
163 out.WriteString(s[start:i])
164 out.WriteString(">")
165 start = i + 1
166 }
167 }
168 out.WriteString(s[start:])
169 }
170
View as plain text