markup/highlight: Replace the temp for with a dependency
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Sun, 24 Nov 2019 12:56:37 +0000 (13:56 +0100)
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Sun, 24 Nov 2019 12:59:16 +0000 (13:59 +0100)
go.mod
go.sum
markup/goldmark/convert.go
markup/highlight/highlight.go
markup/highlight/temphighlighting/highlighting.go [deleted file]
markup/highlight/temphighlighting/highlighting_test.go [deleted file]

diff --git a/go.mod b/go.mod
index 2bfed030243c4720075e2009b6e3c335ee769fc6..eaa6892bd1c61f61518cbda4e72fd6284fb6f2bf 100644 (file)
--- a/go.mod
+++ b/go.mod
@@ -55,6 +55,7 @@ require (
        github.com/tdewolff/minify/v2 v2.5.2
        github.com/yosssi/ace v0.0.5
        github.com/yuin/goldmark v1.1.5
+       github.com/yuin/goldmark-highlighting v0.0.0-20191124122839-ede94e40cc3a
        go.opencensus.io v0.22.0 // indirect
        gocloud.dev v0.15.0
        golang.org/x/image v0.0.0-20190523035834-f03afa92d3ff
diff --git a/go.sum b/go.sum
index 866148569eae225508e9e28d9ab507f11f1ffb53..33f8f353f7948e5b137e055e592d400387ec6c23 100644 (file)
--- a/go.sum
+++ b/go.sum
@@ -353,6 +353,8 @@ github.com/yuin/goldmark v1.1.4 h1:Fj9vOhXMWRBITkIfa8OG/5j6PTKPkyPHxZbT1bvmjV8=
 github.com/yuin/goldmark v1.1.4/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.1.5 h1:JJy3EDke+PMI2WcFIU6SdaeiP6FgRGK5NKAiPZHiOoE=
 github.com/yuin/goldmark v1.1.5/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark-highlighting v0.0.0-20191124122839-ede94e40cc3a h1:L7FTUnbc0WEBqGWgjbx4sPNAOX1/q5W/3KCD6g8XkKo=
+github.com/yuin/goldmark-highlighting v0.0.0-20191124122839-ede94e40cc3a/go.mod h1:1gshkGdH4gcrIH5MGSScGH42rOOCO+4Ks6acjAkA9C0=
 go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
 go.mongodb.org/mongo-driver v1.0.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
 go.opencensus.io v0.15.0/go.mod h1:UffZAU+4sDEINUGP/B7UfBBkq4fqLu9zXAX7ke6CHW0=
index 6c9a3771a88f37781d1f02db79be9eab70794830..9ce0b0b56f8b53c7aa0d1ca4d8c4daf629333528 100644 (file)
@@ -28,10 +28,10 @@ import (
        "github.com/alecthomas/chroma/styles"
        "github.com/gohugoio/hugo/markup/converter"
        "github.com/gohugoio/hugo/markup/highlight"
-       hl "github.com/gohugoio/hugo/markup/highlight/temphighlighting"
        "github.com/gohugoio/hugo/markup/markup_config"
        "github.com/gohugoio/hugo/markup/tableofcontents"
        "github.com/yuin/goldmark"
+       hl "github.com/yuin/goldmark-highlighting"
        "github.com/yuin/goldmark/extension"
        "github.com/yuin/goldmark/parser"
        "github.com/yuin/goldmark/renderer"
index cec637c72ecb33398243c16a5d6e171a4a45d9e9..322bde1ef52d9f90c058af03ce34edd1efe35f19 100644 (file)
@@ -22,7 +22,7 @@ import (
        "github.com/alecthomas/chroma/formatters/html"
        "github.com/alecthomas/chroma/lexers"
        "github.com/alecthomas/chroma/styles"
-       hl "github.com/gohugoio/hugo/markup/highlight/temphighlighting"
+       hl "github.com/yuin/goldmark-highlighting"
 )
 
 func New(cfg Config) Highlighter {
diff --git a/markup/highlight/temphighlighting/highlighting.go b/markup/highlight/temphighlighting/highlighting.go
deleted file mode 100644 (file)
index d2f16c5..0000000
+++ /dev/null
@@ -1,512 +0,0 @@
-// package highlighting is a extension for the goldmark(http://github.com/yuin/goldmark).
-//
-// This extension adds syntax-highlighting to the fenced code blocks using
-// chroma(https://github.com/alecthomas/chroma).
-//
-// TODO(bep) this is a very temporary fork based on https://github.com/yuin/goldmark-highlighting/pull/10
-// MIT Licensed, Copyright Yusuke Inuzuka
-package temphighlighting
-
-import (
-       "bytes"
-       "io"
-       "strconv"
-       "strings"
-
-       "github.com/yuin/goldmark"
-       "github.com/yuin/goldmark/ast"
-       "github.com/yuin/goldmark/parser"
-       "github.com/yuin/goldmark/renderer"
-       "github.com/yuin/goldmark/renderer/html"
-       "github.com/yuin/goldmark/text"
-       "github.com/yuin/goldmark/util"
-
-       "github.com/alecthomas/chroma"
-       chromahtml "github.com/alecthomas/chroma/formatters/html"
-       "github.com/alecthomas/chroma/lexers"
-       "github.com/alecthomas/chroma/styles"
-)
-
-// ImmutableAttributes is a read-only interface for ast.Attributes.
-type ImmutableAttributes interface {
-       // Get returns (value, true) if an attribute associated with given
-       // name exists, otherwise (nil, false)
-       Get(name []byte) (interface{}, bool)
-
-       // GetString returns (value, true) if an attribute associated with given
-       // name exists, otherwise (nil, false)
-       GetString(name string) (interface{}, bool)
-
-       // All returns all attributes.
-       All() []ast.Attribute
-}
-
-type immutableAttributes struct {
-       n ast.Node
-}
-
-func (a *immutableAttributes) Get(name []byte) (interface{}, bool) {
-       return a.n.Attribute(name)
-}
-
-func (a *immutableAttributes) GetString(name string) (interface{}, bool) {
-       return a.n.AttributeString(name)
-}
-
-func (a *immutableAttributes) All() []ast.Attribute {
-       if a.n.Attributes() == nil {
-               return []ast.Attribute{}
-       }
-       return a.n.Attributes()
-}
-
-// CodeBlockContext holds contextual information of code highlighting.
-type CodeBlockContext interface {
-       // Language returns (language, true) if specified, otherwise (nil, false).
-       Language() ([]byte, bool)
-
-       // Highlighted returns true if this code block can be highlighted, otherwise false.
-       Highlighted() bool
-
-       // Attributes return attributes of the code block.
-       Attributes() ImmutableAttributes
-}
-
-type codeBlockContext struct {
-       language    []byte
-       highlighted bool
-       attributes  ImmutableAttributes
-}
-
-func newCodeBlockContext(language []byte, highlighted bool, attrs ImmutableAttributes) CodeBlockContext {
-       return &codeBlockContext{
-               language:    language,
-               highlighted: highlighted,
-               attributes:  attrs,
-       }
-}
-
-func (c *codeBlockContext) Language() ([]byte, bool) {
-       if c.language != nil {
-               return c.language, true
-       }
-       return nil, false
-}
-
-func (c *codeBlockContext) Highlighted() bool {
-       return c.highlighted
-}
-
-func (c *codeBlockContext) Attributes() ImmutableAttributes {
-       return c.attributes
-}
-
-// WrapperRenderer renders wrapper elements like div, pre, etc.
-type WrapperRenderer func(w util.BufWriter, context CodeBlockContext, entering bool)
-
-// CodeBlockOptions creates Chroma options per code block.
-type CodeBlockOptions func(ctx CodeBlockContext) []chromahtml.Option
-
-// Config struct holds options for the extension.
-type Config struct {
-       html.Config
-
-       // Style is a highlighting style.
-       // Supported styles are defined under https://github.com/alecthomas/chroma/tree/master/formatters.
-       Style string
-
-       // FormatOptions is a option related to output formats.
-       // See https://github.com/alecthomas/chroma#the-html-formatter for details.
-       FormatOptions []chromahtml.Option
-
-       // CSSWriter is an io.Writer that will be used as CSS data output buffer.
-       // If WithClasses() is enabled, you can get CSS data corresponds to the style.
-       CSSWriter io.Writer
-
-       // CodeBlockOptions allows set Chroma options per code block.
-       CodeBlockOptions CodeBlockOptions
-
-       // WrapperRendererCodeBlockOptions allows you to change wrapper elements.
-       WrapperRenderer WrapperRenderer
-}
-
-// NewConfig returns a new Config with defaults.
-func NewConfig() Config {
-       return Config{
-               Config:           html.NewConfig(),
-               Style:            "github",
-               FormatOptions:    []chromahtml.Option{},
-               CSSWriter:        nil,
-               WrapperRenderer:  nil,
-               CodeBlockOptions: nil,
-       }
-}
-
-// SetOption implements renderer.SetOptioner.
-func (c *Config) SetOption(name renderer.OptionName, value interface{}) {
-       switch name {
-       case optStyle:
-               c.Style = value.(string)
-       case optFormatOptions:
-               if value != nil {
-                       c.FormatOptions = value.([]chromahtml.Option)
-               }
-       case optCSSWriter:
-               c.CSSWriter = value.(io.Writer)
-       case optWrapperRenderer:
-               c.WrapperRenderer = value.(WrapperRenderer)
-       case optCodeBlockOptions:
-               c.CodeBlockOptions = value.(CodeBlockOptions)
-       default:
-               c.Config.SetOption(name, value)
-       }
-}
-
-// Option interface is a functional option interface for the extension.
-type Option interface {
-       renderer.Option
-       // SetHighlightingOption sets given option to the extension.
-       SetHighlightingOption(*Config)
-}
-
-type withHTMLOptions struct {
-       value []html.Option
-}
-
-func (o *withHTMLOptions) SetConfig(c *renderer.Config) {
-       if o.value != nil {
-               for _, v := range o.value {
-                       v.(renderer.Option).SetConfig(c)
-               }
-       }
-}
-
-func (o *withHTMLOptions) SetHighlightingOption(c *Config) {
-       if o.value != nil {
-               for _, v := range o.value {
-                       v.SetHTMLOption(&c.Config)
-               }
-       }
-}
-
-// WithHTMLOptions is functional option that wraps goldmark HTMLRenderer options.
-func WithHTMLOptions(opts ...html.Option) Option {
-       return &withHTMLOptions{opts}
-}
-
-const optStyle renderer.OptionName = "HighlightingStyle"
-
-var highlightLinesAttrName = []byte("hl_lines")
-
-var styleAttrName = []byte("hl_style")
-var nohlAttrName = []byte("nohl")
-var linenosAttrName = []byte("linenos")
-var linenosTableAttrValue = []byte("table")
-var linenosInlineAttrValue = []byte("inline")
-var linenostartAttrName = []byte("linenostart")
-
-type withStyle struct {
-       value string
-}
-
-func (o *withStyle) SetConfig(c *renderer.Config) {
-       c.Options[optStyle] = o.value
-}
-
-func (o *withStyle) SetHighlightingOption(c *Config) {
-       c.Style = o.value
-}
-
-// WithStyle is a functional option that changes highlighting style.
-func WithStyle(style string) Option {
-       return &withStyle{style}
-}
-
-const optCSSWriter renderer.OptionName = "HighlightingCSSWriter"
-
-type withCSSWriter struct {
-       value io.Writer
-}
-
-func (o *withCSSWriter) SetConfig(c *renderer.Config) {
-       c.Options[optCSSWriter] = o.value
-}
-
-func (o *withCSSWriter) SetHighlightingOption(c *Config) {
-       c.CSSWriter = o.value
-}
-
-// WithCSSWriter is a functional option that sets io.Writer for CSS data.
-func WithCSSWriter(w io.Writer) Option {
-       return &withCSSWriter{w}
-}
-
-const optWrapperRenderer renderer.OptionName = "HighlightingWrapperRenderer"
-
-type withWrapperRenderer struct {
-       value WrapperRenderer
-}
-
-func (o *withWrapperRenderer) SetConfig(c *renderer.Config) {
-       c.Options[optWrapperRenderer] = o.value
-}
-
-func (o *withWrapperRenderer) SetHighlightingOption(c *Config) {
-       c.WrapperRenderer = o.value
-}
-
-// WithWrapperRenderer is a functional option that sets WrapperRenderer that
-// renders wrapper elements like div, pre, etc.
-func WithWrapperRenderer(w WrapperRenderer) Option {
-       return &withWrapperRenderer{w}
-}
-
-const optCodeBlockOptions renderer.OptionName = "HighlightingCodeBlockOptions"
-
-type withCodeBlockOptions struct {
-       value CodeBlockOptions
-}
-
-func (o *withCodeBlockOptions) SetConfig(c *renderer.Config) {
-       c.Options[optWrapperRenderer] = o.value
-}
-
-func (o *withCodeBlockOptions) SetHighlightingOption(c *Config) {
-       c.CodeBlockOptions = o.value
-}
-
-// WithCodeBlockOptions is a functional option that sets CodeBlockOptions that
-// allows setting Chroma options per code block.
-func WithCodeBlockOptions(c CodeBlockOptions) Option {
-       return &withCodeBlockOptions{value: c}
-}
-
-const optFormatOptions renderer.OptionName = "HighlightingFormatOptions"
-
-type withFormatOptions struct {
-       value []chromahtml.Option
-}
-
-func (o *withFormatOptions) SetConfig(c *renderer.Config) {
-       if _, ok := c.Options[optFormatOptions]; !ok {
-               c.Options[optFormatOptions] = []chromahtml.Option{}
-       }
-       c.Options[optStyle] = append(c.Options[optFormatOptions].([]chromahtml.Option), o.value...)
-}
-
-func (o *withFormatOptions) SetHighlightingOption(c *Config) {
-       c.FormatOptions = append(c.FormatOptions, o.value...)
-}
-
-// WithFormatOptions is a functional option that wraps chroma HTML formatter options.
-func WithFormatOptions(opts ...chromahtml.Option) Option {
-       return &withFormatOptions{opts}
-}
-
-// HTMLRenderer struct is a renderer.NodeRenderer implementation for the extension.
-type HTMLRenderer struct {
-       Config
-}
-
-// NewHTMLRenderer builds a new HTMLRenderer with given options and returns it.
-func NewHTMLRenderer(opts ...Option) renderer.NodeRenderer {
-       r := &HTMLRenderer{
-               Config: NewConfig(),
-       }
-       for _, opt := range opts {
-               opt.SetHighlightingOption(&r.Config)
-       }
-       return r
-}
-
-// RegisterFuncs implements NodeRenderer.RegisterFuncs.
-func (r *HTMLRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) {
-       reg.Register(ast.KindFencedCodeBlock, r.renderFencedCodeBlock)
-}
-
-func getAttributes(node *ast.FencedCodeBlock, infostr []byte) ImmutableAttributes {
-       if node.Attributes() != nil {
-               return &immutableAttributes{node}
-       }
-       if infostr != nil {
-               attrStartIdx := -1
-
-               for idx, char := range infostr {
-                       if char == '{' {
-                               attrStartIdx = idx
-                               break
-                       }
-               }
-               if attrStartIdx > 0 {
-                       n := ast.NewTextBlock() // dummy node for storing attributes
-                       attrStr := infostr[attrStartIdx:]
-                       if attrs, hasAttr := parser.ParseAttributes(text.NewReader(attrStr)); hasAttr {
-                               for _, attr := range attrs {
-                                       n.SetAttribute(attr.Name, attr.Value)
-                               }
-                               return &immutableAttributes{n}
-                       }
-               }
-       }
-       return nil
-}
-
-func (r *HTMLRenderer) renderFencedCodeBlock(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
-       n := node.(*ast.FencedCodeBlock)
-       if !entering {
-               return ast.WalkContinue, nil
-       }
-       language := n.Language(source)
-
-       chromaFormatterOptions := make([]chromahtml.Option, len(r.FormatOptions))
-       copy(chromaFormatterOptions, r.FormatOptions)
-       style := styles.Get(r.Style)
-       nohl := false
-
-       var info []byte
-       if n.Info != nil {
-               info = n.Info.Segment.Value(source)
-       }
-       attrs := getAttributes(n, info)
-       if attrs != nil {
-               baseLineNumber := 1
-               if linenostartAttr, ok := attrs.Get(linenostartAttrName); ok {
-                       baseLineNumber = int(linenostartAttr.(float64))
-                       chromaFormatterOptions = append(chromaFormatterOptions, chromahtml.BaseLineNumber(baseLineNumber))
-               }
-               if linesAttr, hasLinesAttr := attrs.Get(highlightLinesAttrName); hasLinesAttr {
-                       if lines, ok := linesAttr.([]interface{}); ok {
-                               var hlRanges [][2]int
-                               for _, l := range lines {
-                                       if ln, ok := l.(float64); ok {
-                                               hlRanges = append(hlRanges, [2]int{int(ln) + baseLineNumber - 1, int(ln) + baseLineNumber - 1})
-                                       }
-                                       if rng, ok := l.([]uint8); ok {
-                                               slices := strings.Split(string([]byte(rng)), "-")
-                                               lhs, err := strconv.Atoi(slices[0])
-                                               if err != nil {
-                                                       continue
-                                               }
-                                               rhs := lhs
-                                               if len(slices) > 1 {
-                                                       rhs, err = strconv.Atoi(slices[1])
-                                                       if err != nil {
-                                                               continue
-                                                       }
-                                               }
-                                               hlRanges = append(hlRanges, [2]int{lhs + baseLineNumber - 1, rhs + baseLineNumber - 1})
-                                       }
-                               }
-                               chromaFormatterOptions = append(chromaFormatterOptions, chromahtml.HighlightLines(hlRanges))
-                       }
-               }
-               if styleAttr, hasStyleAttr := attrs.Get(styleAttrName); hasStyleAttr {
-                       styleStr := string([]byte(styleAttr.([]uint8)))
-                       style = styles.Get(styleStr)
-               }
-               if _, hasNohlAttr := attrs.Get(nohlAttrName); hasNohlAttr {
-                       nohl = true
-               }
-
-               if linenosAttr, ok := attrs.Get(linenosAttrName); ok {
-                       switch v := linenosAttr.(type) {
-                       case bool:
-                               chromaFormatterOptions = append(chromaFormatterOptions, chromahtml.WithLineNumbers(v))
-                       case []uint8:
-                               if v != nil {
-                                       chromaFormatterOptions = append(chromaFormatterOptions, chromahtml.WithLineNumbers(true))
-                               }
-                               if bytes.Equal(v, linenosTableAttrValue) {
-                                       chromaFormatterOptions = append(chromaFormatterOptions, chromahtml.LineNumbersInTable(true))
-                               } else if bytes.Equal(v, linenosInlineAttrValue) {
-                                       chromaFormatterOptions = append(chromaFormatterOptions, chromahtml.LineNumbersInTable(false))
-                               }
-                       }
-               }
-       }
-
-       var lexer chroma.Lexer
-       if language != nil {
-               lexer = lexers.Get(string(language))
-       }
-       if !nohl && lexer != nil {
-               if style == nil {
-                       style = styles.Fallback
-               }
-               var buffer bytes.Buffer
-               l := n.Lines().Len()
-               for i := 0; i < l; i++ {
-                       line := n.Lines().At(i)
-                       buffer.Write(line.Value(source))
-               }
-               iterator, err := lexer.Tokenise(nil, buffer.String())
-               if err == nil {
-                       c := newCodeBlockContext(language, true, attrs)
-
-                       if r.CodeBlockOptions != nil {
-                               chromaFormatterOptions = append(chromaFormatterOptions, r.CodeBlockOptions(c)...)
-                       }
-                       formatter := chromahtml.New(chromaFormatterOptions...)
-                       if r.WrapperRenderer != nil {
-                               r.WrapperRenderer(w, c, true)
-                       }
-                       _ = formatter.Format(w, style, iterator) == nil
-                       if r.WrapperRenderer != nil {
-                               r.WrapperRenderer(w, c, false)
-                       }
-                       if r.CSSWriter != nil {
-                               _ = formatter.WriteCSS(r.CSSWriter, style)
-                       }
-                       return ast.WalkContinue, nil
-               }
-       }
-
-       var c CodeBlockContext
-       if r.WrapperRenderer != nil {
-               c = newCodeBlockContext(language, false, attrs)
-               r.WrapperRenderer(w, c, true)
-       } else {
-               _, _ = w.WriteString("<pre><code")
-               language := n.Language(source)
-               if language != nil {
-                       _, _ = w.WriteString(" class=\"language-")
-                       r.Writer.Write(w, language)
-                       _, _ = w.WriteString("\"")
-               }
-               _ = w.WriteByte('>')
-       }
-       l := n.Lines().Len()
-       for i := 0; i < l; i++ {
-               line := n.Lines().At(i)
-               r.Writer.RawWrite(w, line.Value(source))
-       }
-       if r.WrapperRenderer != nil {
-               r.WrapperRenderer(w, c, false)
-       } else {
-               _, _ = w.WriteString("</code></pre>\n")
-       }
-       return ast.WalkContinue, nil
-}
-
-type highlighting struct {
-       options []Option
-}
-
-// Highlighting is a goldmark.Extender implementation.
-var Highlighting = &highlighting{
-       options: []Option{},
-}
-
-// NewHighlighting returns a new extension with given options.
-func NewHighlighting(opts ...Option) goldmark.Extender {
-       return &highlighting{
-               options: opts,
-       }
-}
-
-// Extend implements goldmark.Extender.
-func (e *highlighting) Extend(m goldmark.Markdown) {
-       m.Renderer().AddOptions(renderer.WithNodeRenderers(
-               util.Prioritized(NewHTMLRenderer(e.options...), 200),
-       ))
-}
diff --git a/markup/highlight/temphighlighting/highlighting_test.go b/markup/highlight/temphighlighting/highlighting_test.go
deleted file mode 100644 (file)
index a2d6058..0000000
+++ /dev/null
@@ -1,308 +0,0 @@
-// TODO(bep) this is a very temporary fork based on https://github.com/yuin/goldmark-highlighting/pull/10
-// MIT Licensed, Copyright Yusuke Inuzuka
-package temphighlighting
-
-import (
-       "bytes"
-       "fmt"
-       "strings"
-       "testing"
-
-       chromahtml "github.com/alecthomas/chroma/formatters/html"
-       "github.com/yuin/goldmark"
-       "github.com/yuin/goldmark/util"
-)
-
-func TestHighlighting(t *testing.T) {
-       var css bytes.Buffer
-       markdown := goldmark.New(
-               goldmark.WithExtensions(
-                       NewHighlighting(
-                               WithStyle("monokai"),
-                               WithCSSWriter(&css),
-                               WithFormatOptions(
-                                       chromahtml.WithClasses(true),
-                                       chromahtml.WithLineNumbers(false),
-                               ),
-                               WithWrapperRenderer(func(w util.BufWriter, c CodeBlockContext, entering bool) {
-                                       _, ok := c.Language()
-                                       if entering {
-                                               if !ok {
-                                                       w.WriteString("<pre><code>")
-                                                       return
-                                               }
-                                               w.WriteString(`<div class="highlight">`)
-                                       } else {
-                                               if !ok {
-                                                       w.WriteString("</pre></code>")
-                                                       return
-                                               }
-                                               w.WriteString(`</div>`)
-                                       }
-                               }),
-                               WithCodeBlockOptions(func(c CodeBlockContext) []chromahtml.Option {
-                                       if language, ok := c.Language(); ok {
-                                               // Turn on line numbers for Go only.
-                                               if string(language) == "go" {
-                                                       return []chromahtml.Option{
-                                                               chromahtml.WithLineNumbers(true),
-                                                       }
-                                               }
-                                       }
-                                       return nil
-                               }),
-                       ),
-               ),
-       )
-       var buffer bytes.Buffer
-       if err := markdown.Convert([]byte(`
-Title
-=======
-`+"``` go\n"+`func main() {
-    fmt.Println("ok")
-}
-`+"```"+`
-`), &buffer); err != nil {
-               t.Fatal(err)
-       }
-
-       if strings.TrimSpace(buffer.String()) != strings.TrimSpace(`
-<h1>Title</h1>
-<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> <span class="p">{</span>
-<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">&#34;ok&#34;</span><span class="p">)</span>
-<span class="ln">3</span><span class="p">}</span>
-</pre></div>
-`) {
-               t.Error("failed to render HTML")
-       }
-
-       if strings.TrimSpace(css.String()) != strings.TrimSpace(`/* Background */ .chroma { color: #f8f8f2; background-color: #272822 }
-/* Error */ .chroma .err { color: #960050; background-color: #1e0010 }
-/* LineTableTD */ .chroma .lntd { vertical-align: top; padding: 0; margin: 0; border: 0; }
-/* LineTable */ .chroma .lntable { border-spacing: 0; padding: 0; margin: 0; border: 0; width: auto; overflow: auto; display: block; }
-/* LineHighlight */ .chroma .hl { display: block; width: 100%;background-color: #3c3d38 }
-/* LineNumbersTable */ .chroma .lnt { margin-right: 0.4em; padding: 0 0.4em 0 0.4em;color: #7f7f7f }
-/* LineNumbers */ .chroma .ln { margin-right: 0.4em; padding: 0 0.4em 0 0.4em;color: #7f7f7f }
-/* Keyword */ .chroma .k { color: #66d9ef }
-/* KeywordConstant */ .chroma .kc { color: #66d9ef }
-/* KeywordDeclaration */ .chroma .kd { color: #66d9ef }
-/* KeywordNamespace */ .chroma .kn { color: #f92672 }
-/* KeywordPseudo */ .chroma .kp { color: #66d9ef }
-/* KeywordReserved */ .chroma .kr { color: #66d9ef }
-/* KeywordType */ .chroma .kt { color: #66d9ef }
-/* NameAttribute */ .chroma .na { color: #a6e22e }
-/* NameClass */ .chroma .nc { color: #a6e22e }
-/* NameConstant */ .chroma .no { color: #66d9ef }
-/* NameDecorator */ .chroma .nd { color: #a6e22e }
-/* NameException */ .chroma .ne { color: #a6e22e }
-/* NameFunction */ .chroma .nf { color: #a6e22e }
-/* NameOther */ .chroma .nx { color: #a6e22e }
-/* NameTag */ .chroma .nt { color: #f92672 }
-/* Literal */ .chroma .l { color: #ae81ff }
-/* LiteralDate */ .chroma .ld { color: #e6db74 }
-/* LiteralString */ .chroma .s { color: #e6db74 }
-/* LiteralStringAffix */ .chroma .sa { color: #e6db74 }
-/* LiteralStringBacktick */ .chroma .sb { color: #e6db74 }
-/* LiteralStringChar */ .chroma .sc { color: #e6db74 }
-/* LiteralStringDelimiter */ .chroma .dl { color: #e6db74 }
-/* LiteralStringDoc */ .chroma .sd { color: #e6db74 }
-/* LiteralStringDouble */ .chroma .s2 { color: #e6db74 }
-/* LiteralStringEscape */ .chroma .se { color: #ae81ff }
-/* LiteralStringHeredoc */ .chroma .sh { color: #e6db74 }
-/* LiteralStringInterpol */ .chroma .si { color: #e6db74 }
-/* LiteralStringOther */ .chroma .sx { color: #e6db74 }
-/* LiteralStringRegex */ .chroma .sr { color: #e6db74 }
-/* LiteralStringSingle */ .chroma .s1 { color: #e6db74 }
-/* LiteralStringSymbol */ .chroma .ss { color: #e6db74 }
-/* LiteralNumber */ .chroma .m { color: #ae81ff }
-/* LiteralNumberBin */ .chroma .mb { color: #ae81ff }
-/* LiteralNumberFloat */ .chroma .mf { color: #ae81ff }
-/* LiteralNumberHex */ .chroma .mh { color: #ae81ff }
-/* LiteralNumberInteger */ .chroma .mi { color: #ae81ff }
-/* LiteralNumberIntegerLong */ .chroma .il { color: #ae81ff }
-/* LiteralNumberOct */ .chroma .mo { color: #ae81ff }
-/* Operator */ .chroma .o { color: #f92672 }
-/* OperatorWord */ .chroma .ow { color: #f92672 }
-/* Comment */ .chroma .c { color: #75715e }
-/* CommentHashbang */ .chroma .ch { color: #75715e }
-/* CommentMultiline */ .chroma .cm { color: #75715e }
-/* CommentSingle */ .chroma .c1 { color: #75715e }
-/* CommentSpecial */ .chroma .cs { color: #75715e }
-/* CommentPreproc */ .chroma .cp { color: #75715e }
-/* CommentPreprocFile */ .chroma .cpf { color: #75715e }
-/* GenericDeleted */ .chroma .gd { color: #f92672 }
-/* GenericEmph */ .chroma .ge { font-style: italic }
-/* GenericInserted */ .chroma .gi { color: #a6e22e }
-/* GenericStrong */ .chroma .gs { font-weight: bold }
-/* GenericSubheading */ .chroma .gu { color: #75715e }`) {
-               t.Error("failed to render CSS")
-       }
-}
-
-func TestHighlighting2(t *testing.T) {
-       markdown := goldmark.New(
-               goldmark.WithExtensions(
-                       Highlighting,
-               ),
-       )
-       var buffer bytes.Buffer
-       if err := markdown.Convert([]byte(`
-Title
-=======
-`+"```"+`
-func main() {
-    fmt.Println("ok")
-}
-`+"```"+`
-`), &buffer); err != nil {
-               t.Fatal(err)
-       }
-
-       if strings.TrimSpace(buffer.String()) != strings.TrimSpace(`
-<h1>Title</h1>
-<pre><code>func main() {
-    fmt.Println(&quot;ok&quot;)
-}
-</code></pre>
-`) {
-               t.Error("failed to render HTML")
-       }
-}
-
-func TestHighlighting3(t *testing.T) {
-       markdown := goldmark.New(
-               goldmark.WithExtensions(
-                       Highlighting,
-               ),
-       )
-       var buffer bytes.Buffer
-       if err := markdown.Convert([]byte(`
-Title
-=======
-
-`+"```"+`cpp {hl_lines=[1,2]}
-#include <iostream>
-int main() {
-    std::cout<< "hello" << std::endl;
-}
-`+"```"+`
-`), &buffer); err != nil {
-               t.Fatal(err)
-       }
-       if strings.TrimSpace(buffer.String()) != strings.TrimSpace(`
-<h1>Title</h1>
-<pre style="background-color:#fff"><span style="display:block;width:100%;background-color:#e5e5e5"><span style="color:#999;font-weight:bold;font-style:italic">#</span><span style="color:#999;font-weight:bold;font-style:italic">include</span> <span style="color:#999;font-weight:bold;font-style:italic">&lt;iostream&gt;</span><span style="color:#999;font-weight:bold;font-style:italic">
-</span></span><span style="display:block;width:100%;background-color:#e5e5e5"><span style="color:#999;font-weight:bold;font-style:italic"></span><span style="color:#458;font-weight:bold">int</span> <span style="color:#900;font-weight:bold">main</span>() {
-</span>    std<span style="color:#000;font-weight:bold">:</span><span style="color:#000;font-weight:bold">:</span>cout<span style="color:#000;font-weight:bold">&lt;</span><span style="color:#000;font-weight:bold">&lt;</span> <span style="color:#d14"></span><span style="color:#d14">&#34;</span><span style="color:#d14">hello</span><span style="color:#d14">&#34;</span> <span style="color:#000;font-weight:bold">&lt;</span><span style="color:#000;font-weight:bold">&lt;</span> std<span style="color:#000;font-weight:bold">:</span><span style="color:#000;font-weight:bold">:</span>endl;
-}
-</pre>
-`) {
-               t.Error("failed to render HTML")
-       }
-}
-
-func TestHighlightingHlLines(t *testing.T) {
-       markdown := goldmark.New(
-               goldmark.WithExtensions(
-                       NewHighlighting(
-                               WithFormatOptions(
-                                       chromahtml.WithClasses(true),
-                               ),
-                       ),
-               ),
-       )
-
-       for i, test := range []struct {
-               attributes string
-               expect     []int
-       }{
-               {`hl_lines=["2"]`, []int{2}},
-               {`hl_lines=["2-3",5],linenostart=5`, []int{2, 3, 5}},
-               {`hl_lines=["2-3"]`, []int{2, 3}},
-       } {
-               t.Run(fmt.Sprint(i), func(t *testing.T) {
-                       var buffer bytes.Buffer
-                       codeBlock := fmt.Sprintf(`bash {%s}
-LINE1
-LINE2
-LINE3
-LINE4
-LINE5
-LINE6
-LINE7
-LINE8
-`, test.attributes)
-
-                       if err := markdown.Convert([]byte(`
-`+"```"+codeBlock+"```"+`
-`), &buffer); err != nil {
-                               t.Fatal(err)
-                       }
-
-                       for _, line := range test.expect {
-                               expectStr := fmt.Sprintf("<span class=\"hl\">LINE%d\n</span>", line)
-                               if !strings.Contains(buffer.String(), expectStr) {
-                                       t.Fatal("got\n", buffer.String(), "\nexpected\n", expectStr)
-                               }
-                       }
-               })
-       }
-}
-
-func TestHighlightingLinenos(t *testing.T) {
-       outputLineNumbersInTable := `<div class="chroma">
-<table class="lntable"><tr><td class="lntd">
-<span class="lnt">1
-</span></td>
-<td class="lntd">
-LINE1
-</td></tr></table>
-</div>`
-
-       for i, test := range []struct {
-               attributes         string
-               lineNumbers        bool
-               lineNumbersInTable bool
-               expect             string
-       }{
-               {`linenos=true`, false, false, `<span class="ln">1</span>LINE1`},
-               {`linenos=false`, false, false, `LINE1`},
-               {``, true, false, `<span class="ln">1</span>LINE1`},
-               {``, true, true, outputLineNumbersInTable},
-               {`linenos=inline`, true, true, `<span class="ln">1</span>LINE1`},
-               {`linenos=foo`, false, false, `<span class="ln">1</span>LINE1`},
-               {`linenos=table`, false, false, outputLineNumbersInTable},
-       } {
-               t.Run(fmt.Sprint(i), func(t *testing.T) {
-                       markdown := goldmark.New(
-                               goldmark.WithExtensions(
-                                       NewHighlighting(
-                                               WithFormatOptions(
-                                                       chromahtml.WithLineNumbers(test.lineNumbers),
-                                                       chromahtml.LineNumbersInTable(test.lineNumbersInTable),
-                                                       chromahtml.PreventSurroundingPre(true),
-                                                       chromahtml.WithClasses(true),
-                                               ),
-                                       ),
-                               ),
-                       )
-
-                       var buffer bytes.Buffer
-                       codeBlock := fmt.Sprintf(`bash {%s}
-LINE1
-`, test.attributes)
-
-                       content := "```" + codeBlock + "```"
-
-                       if err := markdown.Convert([]byte(content), &buffer); err != nil {
-                               t.Fatal(err)
-                       }
-
-                       s := strings.TrimSpace(buffer.String())
-
-                       if s != test.expect {
-                               t.Fatal("got\n", s, "\nexpected\n", test.expect)
-                       }
-               })
-       }
-}