OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [gnu-dev/] [or1k-gcc/] [libgo/] [go/] [go/] [doc/] [comment.go] - Blame information for rev 774

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 747 jeremybenn
// Copyright 2009 The Go Authors. All rights reserved.
2
// Use of this source code is governed by a BSD-style
3
// license that can be found in the LICENSE file.
4
 
5
// Godoc comment extraction and comment -> HTML formatting.
6
 
7
package doc
8
 
9
import (
10
        "io"
11
        "regexp"
12
        "strings"
13
        "text/template" // for HTMLEscape
14
        "unicode"
15
        "unicode/utf8"
16
)
17
 
18
var (
19
        ldquo = []byte("“")
20
        rdquo = []byte("”")
21
)
22
 
23
// Escape comment text for HTML. If nice is set,
24
// also turn `` into “ and '' into ”.
25
func commentEscape(w io.Writer, text string, nice bool) {
26
        last := 0
27
        if nice {
28
                for i := 0; i < len(text)-1; i++ {
29
                        ch := text[i]
30
                        if ch == text[i+1] && (ch == '`' || ch == '\'') {
31
                                template.HTMLEscape(w, []byte(text[last:i]))
32
                                last = i + 2
33
                                switch ch {
34
                                case '`':
35
                                        w.Write(ldquo)
36
                                case '\'':
37
                                        w.Write(rdquo)
38
                                }
39
                                i++ // loop will add one more
40
                        }
41
                }
42
        }
43
        template.HTMLEscape(w, []byte(text[last:]))
44
}
45
 
46
const (
47
        // Regexp for Go identifiers
48
        identRx = `[a-zA-Z_][a-zA-Z_0-9]*` // TODO(gri) ASCII only for now - fix this
49
 
50
        // Regexp for URLs
51
        protocol = `(https?|ftp|file|gopher|mailto|news|nntp|telnet|wais|prospero):`
52
        hostPart = `[a-zA-Z0-9_@\-]+`
53
        filePart = `[a-zA-Z0-9_?%#~&/\-+=]+`
54
        urlRx    = protocol + `//` + // http://
55
                hostPart + `([.:]` + hostPart + `)*/?` + // //www.google.com:8080/
56
                filePart + `([:.,]` + filePart + `)*`
57
)
58
 
59
var matchRx = regexp.MustCompile(`(` + urlRx + `)|(` + identRx + `)`)
60
 
61
var (
62
        html_a      = []byte(`
         
63
        html_aq     = []byte(`">`)
64
        html_enda   = []byte("")
65
        html_i      = []byte("")
66
        html_endi   = []byte("")
67
        html_p      = []byte("

\n")

68
        html_endp   = []byte("

\n")
69
        html_pre    = []byte("
")
70
        html_endpre = []byte("
\n")
71
        html_h      = []byte(`

72
        html_hq     = []byte(`">`)
73
        html_endh   = []byte("\n")
74
)
75
 
76
// Emphasize and escape a line of text for HTML. URLs are converted into links;
77
// if the URL also appears in the words map, the link is taken from the map (if
78
// the corresponding map value is the empty string, the URL is not converted
79
// into a link). Go identifiers that appear in the words map are italicized; if
80
// the corresponding map value is not the empty string, it is considered a URL
81
// and the word is converted into a link. If nice is set, the remaining text's
82
// appearance is improved where it makes sense (e.g., `` is turned into “
83
// and '' into ”).
84
func emphasize(w io.Writer, line string, words map[string]string, nice bool) {
85
        for {
86
                m := matchRx.FindStringSubmatchIndex(line)
87
                if m == nil {
88
                        break
89
                }
90
                // m >= 6 (two parenthesized sub-regexps in matchRx, 1st one is urlRx)
91
 
92
                // write text before match
93
                commentEscape(w, line[0:m[0]], nice)
94
 
95
                // analyze match
96
                match := line[m[0]:m[1]]
97
                url := ""
98
                italics := false
99
                if words != nil {
100
                        url, italics = words[string(match)]
101
                }
102
                if m[2] >= 0 {
103
                        // match against first parenthesized sub-regexp; must be match against urlRx
104
                        if !italics {
105
                                // no alternative URL in words list, use match instead
106
                                url = string(match)
107
                        }
108
                        italics = false // don't italicize URLs
109
                }
110
 
111
                // write match
112
                if len(url) > 0 {
113
                        w.Write(html_a)
114
                        template.HTMLEscape(w, []byte(url))
115
                        w.Write(html_aq)
116
                }
117
                if italics {
118
                        w.Write(html_i)
119
                }
120
                commentEscape(w, match, nice)
121
                if italics {
122
                        w.Write(html_endi)
123
                }
124
                if len(url) > 0 {
125
                        w.Write(html_enda)
126
                }
127
 
128
                // advance
129
                line = line[m[1]:]
130
        }
131
        commentEscape(w, line, nice)
132
}
133
 
134
func indentLen(s string) int {
135
        i := 0
136
        for i < len(s) && (s[i] == ' ' || s[i] == '\t') {
137
                i++
138
        }
139
        return i
140
}
141
 
142
func isBlank(s string) bool {
143
        return len(s) == 0 || (len(s) == 1 && s[0] == '\n')
144
}
145
 
146
func commonPrefix(a, b string) string {
147
        i := 0
148
        for i < len(a) && i < len(b) && a[i] == b[i] {
149
                i++
150
        }
151
        return a[0:i]
152
}
153
 
154
func unindent(block []string) {
155
        if len(block) == 0 {
156
                return
157
        }
158
 
159
        // compute maximum common white prefix
160
        prefix := block[0][0:indentLen(block[0])]
161
        for _, line := range block {
162
                if !isBlank(line) {
163
                        prefix = commonPrefix(prefix, line[0:indentLen(line)])
164
                }
165
        }
166
        n := len(prefix)
167
 
168
        // remove
169
        for i, line := range block {
170
                if !isBlank(line) {
171
                        block[i] = line[n:]
172
                }
173
        }
174
}
175
 
176
// heading returns the trimmed line if it passes as a section heading;
177
// otherwise it returns the empty string.
178
func heading(line string) string {
179
        line = strings.TrimSpace(line)
180
        if len(line) == 0 {
181
                return ""
182
        }
183
 
184
        // a heading must start with an uppercase letter
185
        r, _ := utf8.DecodeRuneInString(line)
186
        if !unicode.IsLetter(r) || !unicode.IsUpper(r) {
187
                return ""
188
        }
189
 
190
        // it must end in a letter or digit:
191
        r, _ = utf8.DecodeLastRuneInString(line)
192
        if !unicode.IsLetter(r) && !unicode.IsDigit(r) {
193
                return ""
194
        }
195
 
196
        // exclude lines with illegal characters
197
        if strings.IndexAny(line, ",.;:!?+*/=()[]{}_^°&§~%#@<\">\\") >= 0 {
198
                return ""
199
        }
200
 
201
        // allow "'" for possessive "'s" only
202
        for b := line; ; {
203
                i := strings.IndexRune(b, '\'')
204
                if i < 0 {
205
                        break
206
                }
207
                if i+1 >= len(b) || b[i+1] != 's' || (i+2 < len(b) && b[i+2] != ' ') {
208
                        return "" // not followed by "s "
209
                }
210
                b = b[i+2:]
211
        }
212
 
213
        return line
214
}
215
 
216
type op int
217
 
218
const (
219
        opPara op = iota
220
        opHead
221
        opPre
222
)
223
 
224
type block struct {
225
        op    op
226
        lines []string
227
}
228
 
229
var nonAlphaNumRx = regexp.MustCompile(`[^a-zA-Z0-9]`)
230
 
231
func anchorID(line string) string {
232
        return nonAlphaNumRx.ReplaceAllString(line, "_")
233
}
234
 
235
// ToHTML converts comment text to formatted HTML.
236
// The comment was prepared by DocReader,
237
// so it is known not to have leading, trailing blank lines
238
// nor to have trailing spaces at the end of lines.
239
// The comment markers have already been removed.
240
//
241
// Turn each run of multiple \n into 

.

242
// Turn each run of indented lines into a 
 block without indent.
243
// Enclose headings with header tags.
244
//
245
// URLs in the comment text are converted into links; if the URL also appears
246
// in the words map, the link is taken from the map (if the corresponding map
247
// value is the empty string, the URL is not converted into a link).
248
//
249
// Go identifiers that appear in the words map are italicized; if the corresponding
250
// map value is not the empty string, it is considered a URL and the word is converted
251
// into a link.
252
func ToHTML(w io.Writer, text string, words map[string]string) {
253
        for _, b := range blocks(text) {
254
                switch b.op {
255
                case opPara:
256
                        w.Write(html_p)
257
                        for _, line := range b.lines {
258
                                emphasize(w, line, words, true)
259
                        }
260
                        w.Write(html_endp)
261
                case opHead:
262
                        w.Write(html_h)
263
                        id := ""
264
                        for _, line := range b.lines {
265
                                if id == "" {
266
                                        id = anchorID(line)
267
                                        w.Write([]byte(id))
268
                                        w.Write(html_hq)
269
                                }
270
                                commentEscape(w, line, true)
271
                        }
272
                        if id == "" {
273
                                w.Write(html_hq)
274
                        }
275
                        w.Write(html_endh)
276
                case opPre:
277
                        w.Write(html_pre)
278
                        for _, line := range b.lines {
279
                                emphasize(w, line, nil, false)
280
                        }
281
                        w.Write(html_endpre)
282
                }
283
        }
284
}
285
 
286
func blocks(text string) []block {
287
        var (
288
                out  []block
289
                para []string
290
 
291
                lastWasBlank   = false
292
                lastWasHeading = false
293
        )
294
 
295
        close := func() {
296
                if para != nil {
297
                        out = append(out, block{opPara, para})
298
                        para = nil
299
                }
300
        }
301
 
302
        lines := strings.SplitAfter(text, "\n")
303
        unindent(lines)
304
        for i := 0; i < len(lines); {
305
                line := lines[i]
306
                if isBlank(line) {
307
                        // close paragraph
308
                        close()
309
                        i++
310
                        lastWasBlank = true
311
                        continue
312
                }
313
                if indentLen(line) > 0 {
314
                        // close paragraph
315
                        close()
316
 
317
                        // count indented or blank lines
318
                        j := i + 1
319
                        for j < len(lines) && (isBlank(lines[j]) || indentLen(lines[j]) > 0) {
320
                                j++
321
                        }
322
                        // but not trailing blank lines
323
                        for j > i && isBlank(lines[j-1]) {
324
                                j--
325
                        }
326
                        pre := lines[i:j]
327
                        i = j
328
 
329
                        unindent(pre)
330
 
331
                        // put those lines in a pre block
332
                        out = append(out, block{opPre, pre})
333
                        lastWasHeading = false
334
                        continue
335
                }
336
 
337
                if lastWasBlank && !lastWasHeading && i+2 < len(lines) &&
338
                        isBlank(lines[i+1]) && !isBlank(lines[i+2]) && indentLen(lines[i+2]) == 0 {
339
                        // current line is non-blank, surrounded by blank lines
340
                        // and the next non-blank line is not indented: this
341
                        // might be a heading.
342
                        if head := heading(line); head != "" {
343
                                close()
344
                                out = append(out, block{opHead, []string{head}})
345
                                i += 2
346
                                lastWasHeading = true
347
                                continue
348
                        }
349
                }
350
 
351
                // open paragraph
352
                lastWasBlank = false
353
                lastWasHeading = false
354
                para = append(para, lines[i])
355
                i++
356
        }
357
        close()
358
 
359
        return out
360
}
361
 
362
// ToText prepares comment text for presentation in textual output.
363
// It wraps paragraphs of text to width or fewer Unicode code points
364
// and then prefixes each line with the indent.  In preformatted sections
365
// (such as program text), it prefixes each non-blank line with preIndent.
366
func ToText(w io.Writer, text string, indent, preIndent string, width int) {
367
        l := lineWrapper{
368
                out:    w,
369
                width:  width,
370
                indent: indent,
371
        }
372
        for _, b := range blocks(text) {
373
                switch b.op {
374
                case opPara:
375
                        // l.write will add leading newline if required
376
                        for _, line := range b.lines {
377
                                l.write(line)
378
                        }
379
                        l.flush()
380
                case opHead:
381
                        w.Write(nl)
382
                        for _, line := range b.lines {
383
                                l.write(line + "\n")
384
                        }
385
                        l.flush()
386
                case opPre:
387
                        w.Write(nl)
388
                        for _, line := range b.lines {
389
                                if !isBlank(line) {
390
                                        w.Write([]byte(preIndent))
391
                                        w.Write([]byte(line))
392
                                }
393
                        }
394
                }
395
        }
396
}
397
 
398
type lineWrapper struct {
399
        out       io.Writer
400
        printed   bool
401
        width     int
402
        indent    string
403
        n         int
404
        pendSpace int
405
}
406
 
407
var nl = []byte("\n")
408
var space = []byte(" ")
409
 
410
func (l *lineWrapper) write(text string) {
411
        if l.n == 0 && l.printed {
412
                l.out.Write(nl) // blank line before new paragraph
413
        }
414
        l.printed = true
415
 
416
        for _, f := range strings.Fields(text) {
417
                w := utf8.RuneCountInString(f)
418
                // wrap if line is too long
419
                if l.n > 0 && l.n+l.pendSpace+w > l.width {
420
                        l.out.Write(nl)
421
                        l.n = 0
422
                        l.pendSpace = 0
423
                }
424
                if l.n == 0 {
425
                        l.out.Write([]byte(l.indent))
426
                }
427
                l.out.Write(space[:l.pendSpace])
428
                l.out.Write([]byte(f))
429
                l.n += l.pendSpace + w
430
                l.pendSpace = 1
431
        }
432
}
433
 
434
func (l *lineWrapper) flush() {
435
        if l.n == 0 {
436
                return
437
        }
438
        l.out.Write(nl)
439
        l.pendSpace = 0
440
        l.n = 0
441
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.