1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32 package godoc
33
34 import (
35 "bytes"
36 "fmt"
37 "log"
38 "regexp"
39 "strings"
40
41 "golang.org/x/tools/godoc/vfs"
42 )
43
44
45
46
47
48
49 func (c *Corpus) contents(name string) string {
50 file, err := vfs.ReadFile(c.fs, name)
51 if err != nil {
52 log.Panic(err)
53 }
54 return string(file)
55 }
56
57
58 func 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", arg, arg)
69 }
70 return ""
71 }
72
73 func (p *Presentation) code(file string, arg ...interface{}) (s string, err 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
85 command = fmt.Sprintf("code %q", file)
86 case 1:
87 command = fmt.Sprintf("code %q %s", file, stringFor(arg[0]))
88 text = p.Corpus.oneLine(file, text, arg[0])
89 case 2:
90 command = fmt.Sprintf("code %q %s %s", file, stringFor(arg[0]), stringFor(arg[1]))
91 text = p.Corpus.multipleLines(file, text, arg[0], arg[1])
92 default:
93 return "", fmt.Errorf("incorrect code invocation: code %q [%v, ...] (%d arguments)", file, arg[0], len(arg))
94 }
95
96 text = strings.Trim(text, "\n")
97
98 text = strings.Replace(text, "\t", " ", -1)
99 var buf bytes.Buffer
100
101 FormatText(&buf, []byte(text), -1, true, "", nil)
102
103 text = fmt.Sprintf("<pre><!--{{%s}}\n-->%s</pre>", command, buf.Bytes())
104 return text, nil
105 }
106
107
108 func parseArg(arg interface{}, file string, max int) (ival int, sval string, isInt bool) {
109 switch n := arg.(type) {
110 case int:
111 if n <= 0 || n > max {
112 log.Panicf("%q:%d is out of range", file, n)
113 }
114 return n, "", true
115 case string:
116 return 0, n, false
117 }
118 log.Panicf("unrecognized argument %v type %T", arg, arg)
119 return
120 }
121
122
123 func (c *Corpus) oneLine(file, text string, arg interface{}) string {
124 lines := strings.SplitAfter(c.contents(file), "\n")
125 line, pattern, isInt := parseArg(arg, file, len(lines))
126 if isInt {
127 return lines[line-1]
128 }
129 return lines[match(file, 0, lines, pattern)-1]
130 }
131
132
133 func (c *Corpus) multipleLines(file, text string, arg1, arg2 interface{}) string {
134 lines := strings.SplitAfter(c.contents(file), "\n")
135 line1, pattern1, isInt1 := parseArg(arg1, file, len(lines))
136 line2, pattern2, isInt2 := parseArg(arg2, file, len(lines))
137 if !isInt1 {
138 line1 = match(file, 0, lines, pattern1)
139 }
140 if !isInt2 {
141 line2 = match(file, line1, lines, pattern2)
142 } else if line2 < line1 {
143 log.Panicf("lines out of order for %q: %d %d", text, line1, line2)
144 }
145 for k := line1 - 1; k < line2; k++ {
146 if strings.HasSuffix(lines[k], "OMIT\n") {
147 lines[k] = ""
148 }
149 }
150 return strings.Join(lines[line1-1:line2], "")
151 }
152
153
154
155
156 func match(file string, start int, lines []string, pattern string) int {
157
158 if pattern == "$" {
159 if len(lines) == 0 {
160 log.Panicf("%q: empty file", file)
161 }
162 return len(lines)
163 }
164
165 if len(pattern) > 2 && pattern[0] == '/' && pattern[len(pattern)-1] == '/' {
166 re, err := regexp.Compile(pattern[1 : len(pattern)-1])
167 if err != nil {
168 log.Panic(err)
169 }
170 for i := start; i < len(lines); i++ {
171 if re.MatchString(lines[i]) {
172 return i + 1
173 }
174 }
175 log.Panicf("%s: no match for %#q", file, pattern)
176 }
177 log.Panicf("unrecognized pattern: %q", pattern)
178 return 0
179 }
180
View as plain text