Skip to content

Commit

Permalink
Functional parser
Browse files Browse the repository at this point in the history
  • Loading branch information
thomiceli committed Oct 13, 2024
1 parent 89d2d5e commit 6c4526e
Show file tree
Hide file tree
Showing 4 changed files with 141 additions and 74 deletions.
2 changes: 1 addition & 1 deletion internal/render/markdown.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ func newMarkdown() goldmark.Markdown {
highlighting.WithFormatOptions(html.WithClasses(true))),
emoji.Emoji,
&mermaid.Extender{},
&svgToImg{},
&svgToImgBase64{},
),
goldmark.WithParserOptions(
parser.WithASTTransformers(
Expand Down
File renamed without changes.
140 changes: 140 additions & 0 deletions internal/render/markdown_svg.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
package render

import (
"bytes"
"encoding/base64"
"github.com/yuin/goldmark"
"github.com/yuin/goldmark/ast"
"github.com/yuin/goldmark/parser"
"github.com/yuin/goldmark/renderer"
"github.com/yuin/goldmark/text"
"github.com/yuin/goldmark/util"
"regexp"
)

type svgToImgBase64 struct{}

func (e *svgToImgBase64) Extend(m goldmark.Markdown) {
m.Parser().AddOptions(parser.WithBlockParsers(
util.Prioritized(newSvgParser(), 1),
))
m.Renderer().AddOptions(renderer.WithNodeRenderers(
util.Prioritized(newSvgRenderer(), 1),
))
}

// -- SVG Block -- //

type svgBlock struct {
ast.BaseBlock
}

func (n *svgBlock) IsRaw() bool {
return true
}

func (n *svgBlock) Dump(source []byte, level int) {
ast.DumpHelper(n, source, level, nil, nil)
}

var svgBlockKind = ast.NewNodeKind("SVG")

func (n *svgBlock) Kind() ast.NodeKind {
return svgBlockKind
}

func newSvgBlock() *svgBlock {
return &svgBlock{
BaseBlock: ast.BaseBlock{},
}
}

// -- SVG Parser -- //

type svgParser struct {
}

var defaultSvgParser = &svgParser{}

func newSvgParser() parser.BlockParser {
return defaultSvgParser
}

func (b *svgParser) Trigger() []byte {
return []byte{'<'}
}

func (b *svgParser) Open(parent ast.Node, reader text.Reader, _ parser.Context) (ast.Node, parser.State) {
var node *svgBlock
line, segment := reader.PeekLine()

if !bytes.HasPrefix(line, []byte("<svg")) {
return nil, parser.None
}

a := regexp.MustCompile("(?i)^[ ]{0,3}<(svg)(?:\\s.*|>.*|/>.*|)(?:\\r\\n|\\n)?$")

Check failure on line 75 in internal/render/markdown_svg.go

View workflow job for this annotation

GitHub Actions / Lint

S1007: should use raw string (`...`) with regexp.MustCompile to avoid having to escape twice (gosimple)

Check failure on line 75 in internal/render/markdown_svg.go

View workflow job for this annotation

GitHub Actions / Lint

S1007: should use raw string (`...`) with regexp.MustCompile to avoid having to escape twice (gosimple)
if a.Match(line) {
node = newSvgBlock()
}

if node != nil {
reader.Advance(segment.Len() - util.TrimRightSpaceLength(line))
node.Lines().Append(segment)
return node, parser.NoChildren
}
return nil, parser.None
}

func (b *svgParser) Continue(node ast.Node, reader text.Reader, _ parser.Context) parser.State {
line, segment := reader.PeekLine()
if util.IsBlank(line) {
return parser.Close
}

if !bytes.HasSuffix(util.TrimRightSpace(line), []byte("</svg>")) {
node.Lines().Append(segment)
return parser.Continue | parser.NoChildren
}

node.Lines().Append(segment)
reader.Advance(segment.Len())
return parser.Close
}

func (b *svgParser) Close(_ ast.Node, _ text.Reader, _ parser.Context) {}

func (b *svgParser) CanInterruptParagraph() bool {
return true
}

func (b *svgParser) CanAcceptIndentedLine() bool {
return false
}

// -- SVG Renderer -- //

type svgRenderer struct{}

func newSvgRenderer() renderer.NodeRenderer {
return &svgRenderer{}
}

func (r *svgRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) {
reg.Register(svgBlockKind, r.renderSVG)
}

func (r *svgRenderer) renderSVG(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
if !entering {
return ast.WalkContinue, nil
}
rawHTML := node.(*svgBlock)
var svgContent []byte
for i := 0; i < rawHTML.Lines().Len(); i++ {
segment := rawHTML.Lines().At(i)
svgContent = append(svgContent, segment.Value(source)...)
}
encoded := base64.StdEncoding.EncodeToString(svgContent)
imgTag := `<img src="data:image/svg+xml;base64,` + encoded + `" />`
_, _ = w.Write([]byte(imgTag))
return ast.WalkContinue, nil
}
73 changes: 0 additions & 73 deletions internal/render/svgtoimg.go

This file was deleted.

0 comments on commit 6c4526e

Please sign in to comment.