LEFT | RIGHT |
1 // Copyright 2012 The Go Authors. All rights reserved. | 1 // Copyright 2012 The Go Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style | 2 // Use of this source code is governed by a BSD-style |
3 // license that can be found in the LICENSE file. | 3 // license that can be found in the LICENSE file. |
4 | 4 |
5 package present | 5 package present |
6 | 6 |
7 import ( | 7 import ( |
8 "bufio" | 8 "bufio" |
9 "bytes" | 9 "bytes" |
10 "fmt" | 10 "fmt" |
11 "html/template" | 11 "html/template" |
12 "path/filepath" | 12 "path/filepath" |
13 "regexp" | 13 "regexp" |
14 "strconv" | 14 "strconv" |
15 "strings" | 15 "strings" |
16 "unicode" | |
17 ) | 16 ) |
18 | 17 |
19 // Is the playground available? | 18 // Is the playground available? |
20 var PlayEnabled = false | 19 var PlayEnabled = false |
21 | 20 |
22 // TOOD(adg): replace the PlayEnabled flag with something less spaghetti-like. | 21 // TOOD(adg): replace the PlayEnabled flag with something less spaghetti-like. |
23 // Instead this will probably be determined by a template execution Context | 22 // Instead this will probably be determined by a template execution Context |
24 // value that contains various global metadata required when rendering | 23 // value that contains various global metadata required when rendering |
25 // templates. | 24 // templates. |
26 | 25 |
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
100 // Highlight lines that end with "// HL[highlight]" | 99 // Highlight lines that end with "// HL[highlight]" |
101 // and strip the magic comment. | 100 // and strip the magic comment. |
102 if m := hlCommentRE.FindStringSubmatch(line.L); m != nil { | 101 if m := hlCommentRE.FindStringSubmatch(line.L); m != nil { |
103 line.L = m[1] | 102 line.L = m[1] |
104 line.HL = m[2] == highlight | 103 line.HL = m[2] == highlight |
105 } | 104 } |
106 | 105 |
107 lines[i] = line | 106 lines[i] = line |
108 } | 107 } |
109 | 108 |
110 » data := &codeTemplateData{Lines: lines, Command: cmd} | 109 » data := &codeTemplateData{Lines: lines} |
111 | 110 |
112 // Include before and after in a hidden span for playground code. | 111 // Include before and after in a hidden span for playground code. |
113 if play { | 112 if play { |
114 data.Prefix = textBytes[:lo] | 113 data.Prefix = textBytes[:lo] |
115 data.Suffix = textBytes[hi:] | 114 data.Suffix = textBytes[hi:] |
116 } | 115 } |
117 | 116 |
118 var buf bytes.Buffer | 117 var buf bytes.Buffer |
119 if err := codeTemplate.Execute(&buf, data); err != nil { | 118 if err := codeTemplate.Execute(&buf, data); err != nil { |
120 return nil, err | 119 return nil, err |
121 } | 120 } |
122 return Code{Text: template.HTML(buf.String()), Play: play}, nil | 121 return Code{Text: template.HTML(buf.String()), Play: play}, nil |
123 } | 122 } |
124 | 123 |
125 type codeTemplateData struct { | 124 type codeTemplateData struct { |
126 Lines []codeLine | 125 Lines []codeLine |
127 Prefix, Suffix []byte | 126 Prefix, Suffix []byte |
128 » Command string | 127 } |
129 } | 128 |
| 129 var leadingSpaceRE = regexp.MustCompile(`^[ \t]*`) |
130 | 130 |
131 var codeTemplate = template.Must(template.New("code").Funcs(template.FuncMap{ | 131 var codeTemplate = template.Must(template.New("code").Funcs(template.FuncMap{ |
132 » "trimSpace": strings.TrimSpace, | 132 » "trimSpace": strings.TrimSpace, |
133 » "leadingSpace": func(s string) string { | 133 » "leadingSpace": leadingSpaceRE.FindString, |
134 » » // return the leading space of s. | 134 }).Parse(codeTemplateHTML)) |
135 » » if j := strings.IndexFunc(s, func(r rune) bool { | 135 |
136 » » » return !unicode.IsSpace(r) | 136 const codeTemplateHTML = ` |
137 » » }); j > -1 { | 137 {{with .Prefix}}<pre style="display: none"><span>{{printf "%s" .}}</span></pre>{
{end}} |
138 » » » return s[:j] | 138 |
139 » » } | |
140 » » return "" | |
141 » }, | |
142 }).Parse(` | |
143 {{with .Prefix}}<pre style="display: none">{{printf "%s" .}}</pre>{{end}} | |
144 <pre>{{range .Lines}}<span num="{{.N}}">{{/* | 139 <pre>{{range .Lines}}<span num="{{.N}}">{{/* |
145 */}}{{if .HL}}{{leadingSpace .L}}<b>{{trimSpace .L}}</b>{{/* | 140 */}}{{if .HL}}{{leadingSpace .L}}<b>{{trimSpace .L}}</b>{{/* |
146 */}}{{else}}{{.L}}{{end}}{{/* | 141 */}}{{else}}{{.L}}{{end}}{{/* |
147 */}}</span> | 142 */}}</span> |
148 {{end}}</pre> | 143 {{end}}</pre> |
149 {{with .Suffix}}<pre style="display: none">{{printf "%s" .}}</pre>{{end}} | 144 |
150 `)) | 145 {{with .Suffix}}<pre style="display: none"><span>{{printf "%s" .}}</span></pre>{
{end}} |
| 146 ` |
151 | 147 |
152 // codeLine represents a line of code extracted from a source file. | 148 // codeLine represents a line of code extracted from a source file. |
153 type codeLine struct { | 149 type codeLine struct { |
154 L string // The line of code. | 150 L string // The line of code. |
155 N int // The line number from the source file. | 151 N int // The line number from the source file. |
156 HL bool // Whether the line should be highlighted. | 152 HL bool // Whether the line should be highlighted. |
157 } | 153 } |
158 | 154 |
159 // codeLines takes a source file and returns the lines that | 155 // codeLines takes a source file and returns the lines that |
160 // span the byte range specified by start and end. | 156 // span the byte range specified by start and end. |
(...skipping 143 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
304 } | 300 } |
305 for i := start; i < len(lines); i++ { | 301 for i := start; i < len(lines); i++ { |
306 if re.MatchString(lines[i]) { | 302 if re.MatchString(lines[i]) { |
307 return i + 1, nil | 303 return i + 1, nil |
308 } | 304 } |
309 } | 305 } |
310 return 0, fmt.Errorf("%s: no match for %#q", file, pattern) | 306 return 0, fmt.Errorf("%s: no match for %#q", file, pattern) |
311 } | 307 } |
312 return 0, fmt.Errorf("unrecognized pattern: %q", pattern) | 308 return 0, fmt.Errorf("unrecognized pattern: %q", pattern) |
313 } | 309 } |
LEFT | RIGHT |