hugo

Fork of github.com/gohugoio/hugo with reverse pagination support

git clone git://git.shimmy1996.com/hugo.git

attributes.go (3309B)

    1 package attributes
    2 
    3 import (
    4 	"github.com/yuin/goldmark"
    5 	"github.com/yuin/goldmark/ast"
    6 	"github.com/yuin/goldmark/parser"
    7 	"github.com/yuin/goldmark/text"
    8 	"github.com/yuin/goldmark/util"
    9 )
   10 
   11 // This extension is based on/inspired by https://github.com/mdigger/goldmark-attributes
   12 // MIT License
   13 // Copyright (c) 2019 Dmitry Sedykh
   14 
   15 var (
   16 	kindAttributesBlock = ast.NewNodeKind("AttributesBlock")
   17 
   18 	defaultParser                        = new(attrParser)
   19 	defaultTransformer                   = new(transformer)
   20 	attributes         goldmark.Extender = new(attrExtension)
   21 )
   22 
   23 func New() goldmark.Extender {
   24 	return attributes
   25 }
   26 
   27 type attrExtension struct{}
   28 
   29 func (a *attrExtension) Extend(m goldmark.Markdown) {
   30 	m.Parser().AddOptions(
   31 		parser.WithBlockParsers(
   32 			util.Prioritized(defaultParser, 100)),
   33 		parser.WithASTTransformers(
   34 			util.Prioritized(defaultTransformer, 100),
   35 		),
   36 	)
   37 }
   38 
   39 type attrParser struct{}
   40 
   41 func (a *attrParser) CanAcceptIndentedLine() bool {
   42 	return false
   43 }
   44 
   45 func (a *attrParser) CanInterruptParagraph() bool {
   46 	return true
   47 }
   48 
   49 func (a *attrParser) Close(node ast.Node, reader text.Reader, pc parser.Context) {
   50 }
   51 
   52 func (a *attrParser) Continue(node ast.Node, reader text.Reader, pc parser.Context) parser.State {
   53 	return parser.Close
   54 }
   55 
   56 func (a *attrParser) Open(parent ast.Node, reader text.Reader, pc parser.Context) (ast.Node, parser.State) {
   57 	if attrs, ok := parser.ParseAttributes(reader); ok {
   58 		// add attributes
   59 		var node = &attributesBlock{
   60 			BaseBlock: ast.BaseBlock{},
   61 		}
   62 		for _, attr := range attrs {
   63 			node.SetAttribute(attr.Name, attr.Value)
   64 		}
   65 		return node, parser.NoChildren
   66 	}
   67 	return nil, parser.RequireParagraph
   68 }
   69 
   70 func (a *attrParser) Trigger() []byte {
   71 	return []byte{'{'}
   72 }
   73 
   74 type attributesBlock struct {
   75 	ast.BaseBlock
   76 }
   77 
   78 func (a *attributesBlock) Dump(source []byte, level int) {
   79 	attrs := a.Attributes()
   80 	list := make(map[string]string, len(attrs))
   81 	for _, attr := range attrs {
   82 		var (
   83 			name  = util.BytesToReadOnlyString(attr.Name)
   84 			value = util.BytesToReadOnlyString(util.EscapeHTML(attr.Value.([]byte)))
   85 		)
   86 		list[name] = value
   87 	}
   88 	ast.DumpHelper(a, source, level, list, nil)
   89 }
   90 
   91 func (a *attributesBlock) Kind() ast.NodeKind {
   92 	return kindAttributesBlock
   93 }
   94 
   95 type transformer struct{}
   96 
   97 func (a *transformer) Transform(node *ast.Document, reader text.Reader, pc parser.Context) {
   98 	var attributes = make([]ast.Node, 0, 500)
   99 	ast.Walk(node, func(node ast.Node, entering bool) (ast.WalkStatus, error) {
  100 		if entering && node.Kind() == kindAttributesBlock {
  101 			// Attributes for fenced code blocks are handled in their own extension,
  102 			// but note that we currently only support code block attributes when
  103 			// CodeFences=true.
  104 			if node.PreviousSibling() != nil && node.PreviousSibling().Kind() != ast.KindFencedCodeBlock && !node.HasBlankPreviousLines() {
  105 				attributes = append(attributes, node)
  106 				return ast.WalkSkipChildren, nil
  107 			}
  108 		}
  109 
  110 		return ast.WalkContinue, nil
  111 	})
  112 
  113 	for _, attr := range attributes {
  114 		if prev := attr.PreviousSibling(); prev != nil &&
  115 			prev.Type() == ast.TypeBlock {
  116 			for _, attr := range attr.Attributes() {
  117 				if _, found := prev.Attribute(attr.Name); !found {
  118 					prev.SetAttribute(attr.Name, attr.Value)
  119 				}
  120 			}
  121 		}
  122 		// remove attributes node
  123 		attr.Parent().RemoveChild(attr.Parent(), attr)
  124 	}
  125 }