1
2
3
4
5 package comment
6
7 import (
8 "bytes"
9 "fmt"
10 "strings"
11 )
12
13
14
15
16
17 type Printer struct {
18
19
20
21
22 HeadingLevel int
23
24
25
26
27
28
29 HeadingID func(h *Heading) string
30
31
32
33 DocLinkURL func(link *DocLink) string
34
35
36
37
38 DocLinkBaseURL string
39
40
41
42 TextPrefix string
43
44
45
46
47
48 TextCodePrefix string
49
50
51
52
53
54
55 TextWidth int
56 }
57
58 func (p *Printer) headingLevel() int {
59 if p.HeadingLevel <= 0 {
60 return 3
61 }
62 return p.HeadingLevel
63 }
64
65 func (p *Printer) headingID(h *Heading) string {
66 if p.HeadingID == nil {
67 return h.DefaultID()
68 }
69 return p.HeadingID(h)
70 }
71
72 func (p *Printer) docLinkURL(link *DocLink) string {
73 if p.DocLinkURL != nil {
74 return p.DocLinkURL(link)
75 }
76 return link.DefaultURL(p.DocLinkBaseURL)
77 }
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97 func (l *DocLink) DefaultURL(baseURL string) string {
98 if l.ImportPath != "" {
99 slash := ""
100 if strings.HasSuffix(baseURL, "/") {
101 slash = "/"
102 } else {
103 baseURL += "/"
104 }
105 switch {
106 case l.Name == "":
107 return baseURL + l.ImportPath + slash
108 case l.Recv != "":
109 return baseURL + l.ImportPath + slash + "#" + l.Recv + "." + l.Name
110 default:
111 return baseURL + l.ImportPath + slash + "#" + l.Name
112 }
113 }
114 if l.Recv != "" {
115 return "#" + l.Recv + "." + l.Name
116 }
117 return "#" + l.Name
118 }
119
120
121
122
123
124
125
126
127 func (h *Heading) DefaultID() string {
128
129
130 var out strings.Builder
131 var p textPrinter
132 p.oneLongLine(&out, h.Text)
133 s := strings.TrimSpace(out.String())
134 if s == "" {
135 return ""
136 }
137 out.Reset()
138 out.WriteString("hdr-")
139 for _, r := range s {
140 if r < 0x80 && isIdentASCII(byte(r)) {
141 out.WriteByte(byte(r))
142 } else {
143 out.WriteByte('_')
144 }
145 }
146 return out.String()
147 }
148
149 type commentPrinter struct {
150 *Printer
151 }
152
153
154
155 func (p *Printer) Comment(d *Doc) []byte {
156 cp := &commentPrinter{Printer: p}
157 var out bytes.Buffer
158 for i, x := range d.Content {
159 if i > 0 && blankBefore(x) {
160 out.WriteString("\n")
161 }
162 cp.block(&out, x)
163 }
164
165
166
167
168
169
170 for i := 0; i < 2; i++ {
171 used := i == 0
172 first := true
173 for _, def := range d.Links {
174 if def.Used == used {
175 if first {
176 out.WriteString("\n")
177 first = false
178 }
179 out.WriteString("[")
180 out.WriteString(def.Text)
181 out.WriteString("]: ")
182 out.WriteString(def.URL)
183 out.WriteString("\n")
184 }
185 }
186 }
187
188 return out.Bytes()
189 }
190
191
192
193 func blankBefore(x Block) bool {
194 if x, ok := x.(*List); ok {
195 return x.BlankBefore()
196 }
197 return true
198 }
199
200
201 func (p *commentPrinter) block(out *bytes.Buffer, x Block) {
202 switch x := x.(type) {
203 default:
204 fmt.Fprintf(out, "?%T", x)
205
206 case *Paragraph:
207 p.text(out, "", x.Text)
208 out.WriteString("\n")
209
210 case *Heading:
211 out.WriteString("# ")
212 p.text(out, "", x.Text)
213 out.WriteString("\n")
214
215 case *Code:
216 md := x.Text
217 for md != "" {
218 var line string
219 line, md, _ = strings.Cut(md, "\n")
220 if line != "" {
221 out.WriteString("\t")
222 out.WriteString(line)
223 }
224 out.WriteString("\n")
225 }
226
227 case *List:
228 loose := x.BlankBetween()
229 for i, item := range x.Items {
230 if i > 0 && loose {
231 out.WriteString("\n")
232 }
233 out.WriteString(" ")
234 if item.Number == "" {
235 out.WriteString(" - ")
236 } else {
237 out.WriteString(item.Number)
238 out.WriteString(". ")
239 }
240 for i, blk := range item.Content {
241 const fourSpace = " "
242 if i > 0 {
243 out.WriteString("\n" + fourSpace)
244 }
245 p.text(out, fourSpace, blk.(*Paragraph).Text)
246 out.WriteString("\n")
247 }
248 }
249 }
250 }
251
252
253 func (p *commentPrinter) text(out *bytes.Buffer, indent string, x []Text) {
254 for _, t := range x {
255 switch t := t.(type) {
256 case Plain:
257 p.indent(out, indent, string(t))
258 case Italic:
259 p.indent(out, indent, string(t))
260 case *Link:
261 if t.Auto {
262 p.text(out, indent, t.Text)
263 } else {
264 out.WriteString("[")
265 p.text(out, indent, t.Text)
266 out.WriteString("]")
267 }
268 case *DocLink:
269 out.WriteString("[")
270 p.text(out, indent, t.Text)
271 out.WriteString("]")
272 }
273 }
274 }
275
276
277
278 func (p *commentPrinter) indent(out *bytes.Buffer, indent, s string) {
279 for s != "" {
280 line, rest, ok := strings.Cut(s, "\n")
281 out.WriteString(line)
282 if ok {
283 out.WriteString("\n")
284 out.WriteString(indent)
285 }
286 s = rest
287 }
288 }
289
View as plain text