github.com/kovansky/hugo@v0.92.3-0.20220224232819-63076e4ff19f/markup/goldmark/codeblocks/render.go (about) 1 // Copyright 2022 The Hugo Authors. All rights reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // http://www.apache.org/licenses/LICENSE-2.0 7 // 8 // Unless required by applicable law or agreed to in writing, software 9 // distributed under the License is distributed on an "AS IS" BASIS, 10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package codeblocks 15 16 import ( 17 "bytes" 18 "fmt" 19 20 "github.com/gohugoio/hugo/markup/converter/hooks" 21 "github.com/gohugoio/hugo/markup/goldmark/internal/render" 22 "github.com/gohugoio/hugo/markup/internal/attributes" 23 "github.com/yuin/goldmark" 24 "github.com/yuin/goldmark/ast" 25 "github.com/yuin/goldmark/parser" 26 "github.com/yuin/goldmark/renderer" 27 "github.com/yuin/goldmark/text" 28 "github.com/yuin/goldmark/util" 29 ) 30 31 type ( 32 diagrams struct{} 33 htmlRenderer struct{} 34 ) 35 36 func New() goldmark.Extender { 37 return &diagrams{} 38 } 39 40 func (e *diagrams) Extend(m goldmark.Markdown) { 41 m.Parser().AddOptions( 42 parser.WithASTTransformers( 43 util.Prioritized(&Transformer{}, 100), 44 ), 45 ) 46 m.Renderer().AddOptions(renderer.WithNodeRenderers( 47 util.Prioritized(newHTMLRenderer(), 100), 48 )) 49 } 50 51 func newHTMLRenderer() renderer.NodeRenderer { 52 r := &htmlRenderer{} 53 return r 54 } 55 56 func (r *htmlRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) { 57 reg.Register(KindCodeBlock, r.renderCodeBlock) 58 } 59 60 func (r *htmlRenderer) renderCodeBlock(w util.BufWriter, src []byte, node ast.Node, entering bool) (ast.WalkStatus, error) { 61 ctx := w.(*render.Context) 62 63 if entering { 64 return ast.WalkContinue, nil 65 } 66 67 n := node.(*codeBlock) 68 lang := string(n.b.Language(src)) 69 ordinal := n.ordinal 70 71 var buff bytes.Buffer 72 73 l := n.b.Lines().Len() 74 for i := 0; i < l; i++ { 75 line := n.b.Lines().At(i) 76 buff.Write(line.Value(src)) 77 } 78 text := buff.String() 79 80 var info []byte 81 if n.b.Info != nil { 82 info = n.b.Info.Segment.Value(src) 83 } 84 attrs := getAttributes(n.b, info) 85 86 v := ctx.RenderContext().GetRenderer(hooks.CodeBlockRendererType, lang) 87 if v == nil { 88 return ast.WalkStop, fmt.Errorf("no code renderer found for %q", lang) 89 } 90 91 cr := v.(hooks.CodeBlockRenderer) 92 93 err := cr.RenderCodeblock( 94 w, 95 codeBlockContext{ 96 page: ctx.DocumentContext().Document, 97 lang: lang, 98 code: text, 99 ordinal: ordinal, 100 AttributesHolder: attributes.New(attrs, attributes.AttributesOwnerCodeBlock), 101 }, 102 ) 103 104 ctx.AddIdentity(cr) 105 106 return ast.WalkContinue, err 107 } 108 109 type codeBlockContext struct { 110 page interface{} 111 lang string 112 code string 113 ordinal int 114 *attributes.AttributesHolder 115 } 116 117 func (c codeBlockContext) Page() interface{} { 118 return c.page 119 } 120 121 func (c codeBlockContext) Lang() string { 122 return c.lang 123 } 124 125 func (c codeBlockContext) Code() string { 126 return c.code 127 } 128 129 func (c codeBlockContext) Ordinal() int { 130 return c.ordinal 131 } 132 133 func getAttributes(node *ast.FencedCodeBlock, infostr []byte) []ast.Attribute { 134 if node.Attributes() != nil { 135 return node.Attributes() 136 } 137 if infostr != nil { 138 attrStartIdx := -1 139 140 for idx, char := range infostr { 141 if char == '{' { 142 attrStartIdx = idx 143 break 144 } 145 } 146 147 if attrStartIdx > 0 { 148 n := ast.NewTextBlock() // dummy node for storing attributes 149 attrStr := infostr[attrStartIdx:] 150 if attrs, hasAttr := parser.ParseAttributes(text.NewReader(attrStr)); hasAttr { 151 for _, attr := range attrs { 152 n.SetAttribute(attr.Name, attr.Value) 153 } 154 return n.Attributes() 155 } 156 } 157 } 158 return nil 159 }