 86e2789960
			
		
	
	
	
	
	86e2789960* update github.com/PuerkitoBio/goquery * update github.com/alecthomas/chroma * update github.com/blevesearch/bleve/v2 * update github.com/caddyserver/certmagic * update github.com/go-enry/go-enry/v2 * update github.com/go-git/go-billy/v5 * update github.com/go-git/go-git/v5 * update github.com/go-redis/redis/v8 * update github.com/go-testfixtures/testfixtures/v3 * update github.com/jaytaylor/html2text * update github.com/json-iterator/go * update github.com/klauspost/compress * update github.com/markbates/goth * update github.com/mattn/go-isatty * update github.com/mholt/archiver/v3 * update github.com/microcosm-cc/bluemonday * update github.com/minio/minio-go/v7 * update github.com/prometheus/client_golang * update github.com/unrolled/render * update github.com/xanzy/go-gitlab * update github.com/yuin/goldmark * update github.com/yuin/goldmark-highlighting Co-authored-by: techknowlogick <techknowlogick@gitea.io>
		
			
				
	
	
		
			574 lines
		
	
	
	
		
			16 KiB
		
	
	
	
		
			Go
		
	
	
	
		
			Vendored
		
	
	
	
			
		
		
	
	
			574 lines
		
	
	
	
		
			16 KiB
		
	
	
	
		
			Go
		
	
	
	
		
			Vendored
		
	
	
	
| // 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).
 | |
| package highlighting
 | |
| 
 | |
| 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
 | |
| 
 | |
| 	// Pass in a custom Chroma style. If this is not nil, the Style string will be ignored
 | |
| 	CustomStyle *chroma.Style
 | |
| 
 | |
| 	// If set, will try to guess language if none provided.
 | |
| 	// If the guessing fails, we will fall back to a text lexer.
 | |
| 	// Note that while Chroma's API supports language guessing, the implementation
 | |
| 	// is not there yet, so you will currently always get the basic text lexer.
 | |
| 	GuessLanguage bool
 | |
| 
 | |
| 	// 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
 | |
| 
 | |
| 	// WrapperRenderer 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 optCustomStyle:
 | |
| 		c.CustomStyle = value.(*chroma.Style)
 | |
| 	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)
 | |
| 	case optGuessLanguage:
 | |
| 		c.GuessLanguage = value.(bool)
 | |
| 	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"
 | |
| const optCustomStyle renderer.OptionName = "HighlightingCustomStyle"
 | |
| 
 | |
| 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}
 | |
| }
 | |
| 
 | |
| type withCustomStyle struct {
 | |
| 	value *chroma.Style
 | |
| }
 | |
| 
 | |
| func (o *withCustomStyle) SetConfig(c *renderer.Config) {
 | |
| 	c.Options[optCustomStyle] = o.value
 | |
| }
 | |
| 
 | |
| func (o *withCustomStyle) SetHighlightingOption(c *Config) {
 | |
| 	c.CustomStyle = o.value
 | |
| }
 | |
| 
 | |
| // WithStyle is a functional option that changes highlighting style.
 | |
| func WithCustomStyle(style *chroma.Style) Option {
 | |
| 	return &withCustomStyle{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 optGuessLanguage renderer.OptionName = "HighlightingGuessLanguage"
 | |
| 
 | |
| type withGuessLanguage struct {
 | |
| 	value bool
 | |
| }
 | |
| 
 | |
| func (o *withGuessLanguage) SetConfig(c *renderer.Config) {
 | |
| 	c.Options[optGuessLanguage] = o.value
 | |
| }
 | |
| 
 | |
| func (o *withGuessLanguage) SetHighlightingOption(c *Config) {
 | |
| 	c.GuessLanguage = o.value
 | |
| }
 | |
| 
 | |
| // WithGuessLanguage is a functional option that toggles language guessing
 | |
| // if none provided.
 | |
| func WithGuessLanguage(b bool) Option {
 | |
| 	return &withGuessLanguage{value: b}
 | |
| }
 | |
| 
 | |
| 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[optFormatOptions] = 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 := r.CustomStyle
 | |
| 	if style == nil {
 | |
| 		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 || r.GuessLanguage) {
 | |
| 		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))
 | |
| 		}
 | |
| 
 | |
| 		if lexer == nil {
 | |
| 			lexer = lexers.Analyse(buffer.String())
 | |
| 			if lexer == nil {
 | |
| 				lexer = lexers.Fallback
 | |
| 			}
 | |
| 			language = []byte(strings.ToLower(lexer.Config().Name))
 | |
| 		}
 | |
| 		lexer = chroma.Coalesce(lexer)
 | |
| 
 | |
| 		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),
 | |
| 	))
 | |
| }
 |