5 | 5 |
"strings"
|
6 | 6 |
"testing"
|
7 | 7 |
|
|
8 |
"github.com/alecthomas/chroma"
|
8 | 9 |
chromahtml "github.com/alecthomas/chroma/formatters/html"
|
|
10 |
"github.com/google/go-cmp/cmp"
|
9 | 11 |
"github.com/yuin/goldmark"
|
10 | 12 |
"github.com/yuin/goldmark/util"
|
11 | 13 |
)
|
|
73 | 75 |
t.Error("failed to render HTML")
|
74 | 76 |
}
|
75 | 77 |
|
76 | |
println(css.String())
|
77 | |
|
78 | |
if strings.TrimSpace(css.String()) != strings.TrimSpace(`/* Background */ .chroma { color: #f8f8f2; background-color: #272822 }
|
|
78 |
expected := strings.TrimSpace(`/* Background */ .chroma { color: #f8f8f2; background-color: #272822 }
|
79 | 79 |
/* LineNumbers targeted by URL anchor */ .chroma .ln:target { color: #f8f8f2; background-color: #3c3d38 }
|
80 | 80 |
/* LineNumbersTable targeted by URL anchor */ .chroma .lnt:target { color: #f8f8f2; background-color: #3c3d38 }
|
81 | 81 |
/* Error */ .chroma .err { color: #960050; background-color: #1e0010 }
|
|
135 | 135 |
/* GenericEmph */ .chroma .ge { font-style: italic }
|
136 | 136 |
/* GenericInserted */ .chroma .gi { color: #a6e22e }
|
137 | 137 |
/* GenericStrong */ .chroma .gs { font-weight: bold }
|
138 | |
/* GenericSubheading */ .chroma .gu { color: #75715e }`) {
|
139 | |
t.Error("failed to render CSS")
|
|
138 |
/* GenericSubheading */ .chroma .gu { color: #75715e }`)
|
|
139 |
|
|
140 |
gotten := strings.TrimSpace(css.String())
|
|
141 |
|
|
142 |
if diff := cmp.Diff(expected, gotten); diff != "" {
|
|
143 |
t.Errorf("failed to render CSS.\n%s", diff)
|
140 | 144 |
}
|
141 | 145 |
}
|
142 | 146 |
|
|
199 | 203 |
</pre>
|
200 | 204 |
`) {
|
201 | 205 |
t.Error("failed to render HTML")
|
|
206 |
}
|
|
207 |
}
|
|
208 |
|
|
209 |
func TestHighlightingCustom(t *testing.T) {
|
|
210 |
custom := chroma.MustNewStyle("custom", chroma.StyleEntries{
|
|
211 |
chroma.Background: "#cccccc bg:#1d1d1d",
|
|
212 |
chroma.Comment: "#999999",
|
|
213 |
chroma.CommentSpecial: "#cd0000",
|
|
214 |
chroma.Keyword: "#cc99cd",
|
|
215 |
chroma.KeywordDeclaration: "#cc99cd",
|
|
216 |
chroma.KeywordNamespace: "#cc99cd",
|
|
217 |
chroma.KeywordType: "#cc99cd",
|
|
218 |
chroma.Operator: "#67cdcc",
|
|
219 |
chroma.OperatorWord: "#cdcd00",
|
|
220 |
chroma.NameClass: "#f08d49",
|
|
221 |
chroma.NameBuiltin: "#f08d49",
|
|
222 |
chroma.NameFunction: "#f08d49",
|
|
223 |
chroma.NameException: "bold #666699",
|
|
224 |
chroma.NameVariable: "#00cdcd",
|
|
225 |
chroma.LiteralString: "#7ec699",
|
|
226 |
chroma.LiteralNumber: "#f08d49",
|
|
227 |
chroma.LiteralStringBoolean: "#f08d49",
|
|
228 |
chroma.GenericHeading: "bold #000080",
|
|
229 |
chroma.GenericSubheading: "bold #800080",
|
|
230 |
chroma.GenericDeleted: "#e2777a",
|
|
231 |
chroma.GenericInserted: "#cc99cd",
|
|
232 |
chroma.GenericError: "#e2777a",
|
|
233 |
chroma.GenericEmph: "italic",
|
|
234 |
chroma.GenericStrong: "bold",
|
|
235 |
chroma.GenericPrompt: "bold #000080",
|
|
236 |
chroma.GenericOutput: "#888",
|
|
237 |
chroma.GenericTraceback: "#04D",
|
|
238 |
chroma.GenericUnderline: "underline",
|
|
239 |
chroma.Error: "border:#e2777a",
|
|
240 |
})
|
|
241 |
|
|
242 |
var css bytes.Buffer
|
|
243 |
markdown := goldmark.New(
|
|
244 |
goldmark.WithExtensions(
|
|
245 |
NewHighlighting(
|
|
246 |
WithStyle("monokai"), // to make sure it is overrided even if present
|
|
247 |
WithCustomStyle(custom),
|
|
248 |
WithCSSWriter(&css),
|
|
249 |
WithFormatOptions(
|
|
250 |
chromahtml.WithClasses(true),
|
|
251 |
chromahtml.WithLineNumbers(false),
|
|
252 |
),
|
|
253 |
WithWrapperRenderer(func(w util.BufWriter, c CodeBlockContext, entering bool) {
|
|
254 |
_, ok := c.Language()
|
|
255 |
if entering {
|
|
256 |
if !ok {
|
|
257 |
w.WriteString("<pre><code>")
|
|
258 |
return
|
|
259 |
}
|
|
260 |
w.WriteString(`<div class="highlight">`)
|
|
261 |
} else {
|
|
262 |
if !ok {
|
|
263 |
w.WriteString("</pre></code>")
|
|
264 |
return
|
|
265 |
}
|
|
266 |
w.WriteString(`</div>`)
|
|
267 |
}
|
|
268 |
}),
|
|
269 |
WithCodeBlockOptions(func(c CodeBlockContext) []chromahtml.Option {
|
|
270 |
if language, ok := c.Language(); ok {
|
|
271 |
// Turn on line numbers for Go only.
|
|
272 |
if string(language) == "go" {
|
|
273 |
return []chromahtml.Option{
|
|
274 |
chromahtml.WithLineNumbers(true),
|
|
275 |
}
|
|
276 |
}
|
|
277 |
}
|
|
278 |
return nil
|
|
279 |
}),
|
|
280 |
),
|
|
281 |
),
|
|
282 |
)
|
|
283 |
var buffer bytes.Buffer
|
|
284 |
if err := markdown.Convert([]byte(`
|
|
285 |
Title
|
|
286 |
=======
|
|
287 |
`+"``` go\n"+`func main() {
|
|
288 |
fmt.Println("ok")
|
|
289 |
}
|
|
290 |
`+"```"+`
|
|
291 |
`), &buffer); err != nil {
|
|
292 |
t.Fatal(err)
|
|
293 |
}
|
|
294 |
|
|
295 |
if strings.TrimSpace(buffer.String()) != strings.TrimSpace(`
|
|
296 |
<h1>Title</h1>
|
|
297 |
<div class="highlight"><pre class="chroma"><span class="ln">1</span><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
|
|
298 |
<span class="ln">2</span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">"ok"</span><span class="p">)</span>
|
|
299 |
<span class="ln">3</span><span class="p">}</span>
|
|
300 |
</pre></div>
|
|
301 |
`) {
|
|
302 |
t.Error("failed to render HTML")
|
|
303 |
}
|
|
304 |
|
|
305 |
expected := strings.TrimSpace(`/* Background */ .chroma { color: #cccccc; background-color: #1d1d1d }
|
|
306 |
/* LineNumbers targeted by URL anchor */ .chroma .ln:target { color: #cccccc; background-color: #333333 }
|
|
307 |
/* LineNumbersTable targeted by URL anchor */ .chroma .lnt:target { color: #cccccc; background-color: #333333 }
|
|
308 |
/* Error */ .chroma .err { }
|
|
309 |
/* LineTableTD */ .chroma .lntd { vertical-align: top; padding: 0; margin: 0; border: 0; }
|
|
310 |
/* LineTable */ .chroma .lntable { border-spacing: 0; padding: 0; margin: 0; border: 0; width: auto; overflow: auto; display: block; }
|
|
311 |
/* LineHighlight */ .chroma .hl { display: block; width: 100%;background-color: #333333 }
|
|
312 |
/* LineNumbersTable */ .chroma .lnt { margin-right: 0.4em; padding: 0 0.4em 0 0.4em;color: #666666 }
|
|
313 |
/* LineNumbers */ .chroma .ln { margin-right: 0.4em; padding: 0 0.4em 0 0.4em;color: #666666 }
|
|
314 |
/* Keyword */ .chroma .k { color: #cc99cd }
|
|
315 |
/* KeywordConstant */ .chroma .kc { color: #cc99cd }
|
|
316 |
/* KeywordDeclaration */ .chroma .kd { color: #cc99cd }
|
|
317 |
/* KeywordNamespace */ .chroma .kn { color: #cc99cd }
|
|
318 |
/* KeywordPseudo */ .chroma .kp { color: #cc99cd }
|
|
319 |
/* KeywordReserved */ .chroma .kr { color: #cc99cd }
|
|
320 |
/* KeywordType */ .chroma .kt { color: #cc99cd }
|
|
321 |
/* NameBuiltin */ .chroma .nb { color: #f08d49 }
|
|
322 |
/* NameClass */ .chroma .nc { color: #f08d49 }
|
|
323 |
/* NameException */ .chroma .ne { color: #666699; font-weight: bold }
|
|
324 |
/* NameFunction */ .chroma .nf { color: #f08d49 }
|
|
325 |
/* NameVariable */ .chroma .nv { color: #00cdcd }
|
|
326 |
/* LiteralString */ .chroma .s { color: #7ec699 }
|
|
327 |
/* LiteralStringAffix */ .chroma .sa { color: #7ec699 }
|
|
328 |
/* LiteralStringBacktick */ .chroma .sb { color: #7ec699 }
|
|
329 |
/* LiteralStringChar */ .chroma .sc { color: #7ec699 }
|
|
330 |
/* LiteralStringDelimiter */ .chroma .dl { color: #7ec699 }
|
|
331 |
/* LiteralStringDoc */ .chroma .sd { color: #7ec699 }
|
|
332 |
/* LiteralStringDouble */ .chroma .s2 { color: #7ec699 }
|
|
333 |
/* LiteralStringEscape */ .chroma .se { color: #7ec699 }
|
|
334 |
/* LiteralStringHeredoc */ .chroma .sh { color: #7ec699 }
|
|
335 |
/* LiteralStringInterpol */ .chroma .si { color: #7ec699 }
|
|
336 |
/* LiteralStringOther */ .chroma .sx { color: #7ec699 }
|
|
337 |
/* LiteralStringRegex */ .chroma .sr { color: #7ec699 }
|
|
338 |
/* LiteralStringSingle */ .chroma .s1 { color: #7ec699 }
|
|
339 |
/* LiteralStringSymbol */ .chroma .ss { color: #7ec699 }
|
|
340 |
/* LiteralNumber */ .chroma .m { color: #f08d49 }
|
|
341 |
/* LiteralNumberBin */ .chroma .mb { color: #f08d49 }
|
|
342 |
/* LiteralNumberFloat */ .chroma .mf { color: #f08d49 }
|
|
343 |
/* LiteralNumberHex */ .chroma .mh { color: #f08d49 }
|
|
344 |
/* LiteralNumberInteger */ .chroma .mi { color: #f08d49 }
|
|
345 |
/* LiteralNumberIntegerLong */ .chroma .il { color: #f08d49 }
|
|
346 |
/* LiteralNumberOct */ .chroma .mo { color: #f08d49 }
|
|
347 |
/* Operator */ .chroma .o { color: #67cdcc }
|
|
348 |
/* OperatorWord */ .chroma .ow { color: #cdcd00 }
|
|
349 |
/* Comment */ .chroma .c { color: #999999 }
|
|
350 |
/* CommentHashbang */ .chroma .ch { color: #999999 }
|
|
351 |
/* CommentMultiline */ .chroma .cm { color: #999999 }
|
|
352 |
/* CommentSingle */ .chroma .c1 { color: #999999 }
|
|
353 |
/* CommentSpecial */ .chroma .cs { color: #cd0000 }
|
|
354 |
/* CommentPreproc */ .chroma .cp { color: #999999 }
|
|
355 |
/* CommentPreprocFile */ .chroma .cpf { color: #999999 }
|
|
356 |
/* GenericDeleted */ .chroma .gd { color: #e2777a }
|
|
357 |
/* GenericEmph */ .chroma .ge { font-style: italic }
|
|
358 |
/* GenericError */ .chroma .gr { color: #e2777a }
|
|
359 |
/* GenericHeading */ .chroma .gh { color: #000080; font-weight: bold }
|
|
360 |
/* GenericInserted */ .chroma .gi { color: #cc99cd }
|
|
361 |
/* GenericOutput */ .chroma .go { color: #888888 }
|
|
362 |
/* GenericPrompt */ .chroma .gp { color: #000080; font-weight: bold }
|
|
363 |
/* GenericStrong */ .chroma .gs { font-weight: bold }
|
|
364 |
/* GenericSubheading */ .chroma .gu { color: #800080; font-weight: bold }
|
|
365 |
/* GenericTraceback */ .chroma .gt { color: #0044dd }
|
|
366 |
/* GenericUnderline */ .chroma .gl { text-decoration: underline }`)
|
|
367 |
|
|
368 |
gotten := strings.TrimSpace(css.String())
|
|
369 |
|
|
370 |
if diff := cmp.Diff(expected, gotten); diff != "" {
|
|
371 |
t.Errorf("failed to render CSS.\n%s", diff)
|
202 | 372 |
}
|
203 | 373 |
}
|
204 | 374 |
|