GoPLS Viewer

Home|gopls/godoc/template.go
1// Copyright 2011 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// Template support for writing HTML documents.
6// Documents that include Template: true in their
7// metadata are executed as input to text/template.
8//
9// This file defines functions for those templates to invoke.
10
11// The template uses the function "code" to inject program
12// source into the output by extracting code from files and
13// injecting them as HTML-escaped <pre> blocks.
14//
15// The syntax is simple: 1, 2, or 3 space-separated arguments:
16//
17// Whole file:
18//    {{code "foo.go"}}
19// One line (here the signature of main):
20//    {{code "foo.go" `/^func.main/`}}
21// Block of text, determined by start and end (here the body of main):
22//    {{code "foo.go" `/^func.main/` `/^}/`
23//
24// Patterns can be `/regular expression/`, a decimal number, or "$"
25// to signify the end of the file. In multi-line matches,
26// lines that end with the four characters
27//    OMIT
28// are omitted from the output, making it easy to provide marker
29// lines in the input that will not appear in the output but are easy
30// to identify by pattern.
31
32package godoc
33
34import (
35    "bytes"
36    "fmt"
37    "log"
38    "regexp"
39    "strings"
40
41    "golang.org/x/tools/godoc/vfs"
42)
43
44// Functions in this file panic on error, but the panic is recovered
45// to an error by 'code'.
46
47// contents reads and returns the content of the named file
48// (from the virtual file system, so for example /doc refers to $GOROOT/doc).
49func (c *Corpuscontents(name stringstring {
50    fileerr := vfs.ReadFile(c.fsname)
51    if err != nil {
52        log.Panic(err)
53    }
54    return string(file)
55}
56
57// stringFor returns a textual representation of the arg, formatted according to its nature.
58func stringFor(arg interface{}) string {
59    switch arg := arg.(type) {
60    case int:
61        return fmt.Sprintf("%d"arg)
62    case string:
63        if len(arg) > 2 && arg[0] == '/' && arg[len(arg)-1] == '/' {
64            return fmt.Sprintf("%#q"arg)
65        }
66        return fmt.Sprintf("%q"arg)
67    default:
68        log.Panicf("unrecognized argument: %v type %T"argarg)
69    }
70    return ""
71}
72
73func (p *Presentationcode(file stringarg ...interface{}) (s stringerr error) {
74    defer func() {
75        if r := recover(); r != nil {
76            err = fmt.Errorf("%v"r)
77        }
78    }()
79
80    text := p.Corpus.contents(file)
81    var command string
82    switch len(arg) {
83    case 0:
84        // text is already whole file.
85        command = fmt.Sprintf("code %q"file)
86    case 1:
87        command = fmt.Sprintf("code %q %s"filestringFor(arg[0]))
88        text = p.Corpus.oneLine(filetextarg[0])
89    case 2:
90        command = fmt.Sprintf("code %q %s %s"filestringFor(arg[0]), stringFor(arg[1]))
91        text = p.Corpus.multipleLines(filetextarg[0], arg[1])
92    default:
93        return ""fmt.Errorf("incorrect code invocation: code %q [%v, ...] (%d arguments)"filearg[0], len(arg))
94    }
95    // Trim spaces from output.
96    text = strings.Trim(text"\n")
97    // Replace tabs by spaces, which work better in HTML.
98    text = strings.Replace(text"\t""    ", -1)
99    var buf bytes.Buffer
100    // HTML-escape text and syntax-color comments like elsewhere.
101    FormatText(&buf, []byte(text), -1true""nil)
102    // Include the command as a comment.
103    text = fmt.Sprintf("<pre><!--{{%s}}\n-->%s</pre>"commandbuf.Bytes())
104    return textnil
105}
106
107// parseArg returns the integer or string value of the argument and tells which it is.
108func parseArg(arg interface{}, file stringmax int) (ival intsval stringisInt bool) {
109    switch n := arg.(type) {
110    case int:
111        if n <= 0 || n > max {
112            log.Panicf("%q:%d is out of range"filen)
113        }
114        return n""true
115    case string:
116        return 0nfalse
117    }
118    log.Panicf("unrecognized argument %v type %T"argarg)
119    return
120}
121
122// oneLine returns the single line generated by a two-argument code invocation.
123func (c *CorpusoneLine(filetext stringarg interface{}) string {
124    lines := strings.SplitAfter(c.contents(file), "\n")
125    linepatternisInt := parseArg(argfilelen(lines))
126    if isInt {
127        return lines[line-1]
128    }
129    return lines[match(file0linespattern)-1]
130}
131
132// multipleLines returns the text generated by a three-argument code invocation.
133func (c *CorpusmultipleLines(filetext stringarg1arg2 interface{}) string {
134    lines := strings.SplitAfter(c.contents(file), "\n")
135    line1pattern1isInt1 := parseArg(arg1filelen(lines))
136    line2pattern2isInt2 := parseArg(arg2filelen(lines))
137    if !isInt1 {
138        line1 = match(file0linespattern1)
139    }
140    if !isInt2 {
141        line2 = match(fileline1linespattern2)
142    } else if line2 < line1 {
143        log.Panicf("lines out of order for %q: %d %d"textline1line2)
144    }
145    for k := line1 - 1k < line2k++ {
146        if strings.HasSuffix(lines[k], "OMIT\n") {
147            lines[k] = ""
148        }
149    }
150    return strings.Join(lines[line1-1:line2], "")
151}
152
153// match identifies the input line that matches the pattern in a code invocation.
154// If start>0, match lines starting there rather than at the beginning.
155// The return value is 1-indexed.
156func match(file stringstart intlines []stringpattern stringint {
157    // $ matches the end of the file.
158    if pattern == "$" {
159        if len(lines) == 0 {
160            log.Panicf("%q: empty file"file)
161        }
162        return len(lines)
163    }
164    // /regexp/ matches the line that matches the regexp.
165    if len(pattern) > 2 && pattern[0] == '/' && pattern[len(pattern)-1] == '/' {
166        reerr := regexp.Compile(pattern[1 : len(pattern)-1])
167        if err != nil {
168            log.Panic(err)
169        }
170        for i := starti < len(lines); i++ {
171            if re.MatchString(lines[i]) {
172                return i + 1
173            }
174        }
175        log.Panicf("%s: no match for %#q"filepattern)
176    }
177    log.Panicf("unrecognized pattern: %q"pattern)
178    return 0
179}
180
MembersX
Corpus.contents.name
Presentation.code
Presentation.code.arg
Presentation.code.s
Corpus.multipleLines.text
Corpus.multipleLines.line2
match.file
match.start
parseArg.arg
Corpus.oneLine.arg
Corpus.multipleLines.file
Corpus.multipleLines.isInt2
Corpus.contents.file
Presentation.code.command
Corpus.oneLine.pattern
Corpus.multipleLines.c
parseArg.max
Corpus.oneLine.lines
Corpus.contents.c
Corpus.contents.err
Presentation.code.file
parseArg
Corpus.oneLine.file
Corpus.multipleLines.arg1
match
match.BlockStmt.i
stringFor
Corpus.multipleLines.line1
Corpus.multipleLines.pattern1
match.BlockStmt.err
match.lines
match.pattern
Corpus.oneLine.c
Corpus.oneLine
Corpus.multipleLines.pattern2
Corpus.oneLine.line
Corpus.oneLine.isInt
match.BlockStmt.re
stringFor.arg
Corpus.oneLine.text
Presentation.code.buf
Corpus.multipleLines.arg2
Corpus.multipleLines.isInt1
Corpus.contents
Presentation.code.text
Corpus.multipleLines
Corpus.multipleLines.lines
parseArg.ival
parseArg.sval
parseArg.isInt
Presentation.code.p
Presentation.code.err
Presentation.code.BlockStmt.r
parseArg.file
Members
X