333211d4d0
Signed-off-by: Tobias Erbshäußer <tobias@tesoft.dev>
111 lines
2.6 KiB
Go
111 lines
2.6 KiB
Go
package md
|
|
|
|
import (
|
|
"bytes"
|
|
"strings"
|
|
|
|
"github.com/yuin/goldmark"
|
|
"github.com/yuin/goldmark/ast"
|
|
"github.com/yuin/goldmark/renderer"
|
|
"github.com/yuin/goldmark/renderer/html"
|
|
"github.com/yuin/goldmark/util"
|
|
)
|
|
|
|
type UrlTransformer = func(string) (string, error)
|
|
|
|
type urlTransformerExtension struct {
|
|
transformer UrlTransformer
|
|
}
|
|
|
|
func (e *urlTransformerExtension) Extend(m goldmark.Markdown) {
|
|
m.Renderer().AddOptions(renderer.WithNodeRenderers(
|
|
util.Prioritized(&urlRenderer{e.transformer}, 500),
|
|
))
|
|
}
|
|
|
|
type urlRenderer struct {
|
|
transformer UrlTransformer
|
|
}
|
|
|
|
func (r *urlRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) {
|
|
reg.Register(ast.KindImage, r.renderImage)
|
|
reg.Register(ast.KindLink, r.renderLink)
|
|
}
|
|
|
|
func (r *urlRenderer) renderImage(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
|
|
if !entering {
|
|
return ast.WalkContinue, nil
|
|
}
|
|
|
|
n := node.(*ast.Image)
|
|
|
|
url, err := r.resolveUrl(string(n.Destination))
|
|
if err != nil {
|
|
return ast.WalkStop, err
|
|
}
|
|
|
|
_, _ = w.WriteString(`<img src="`)
|
|
_, _ = w.WriteString(url)
|
|
_, _ = w.WriteString(`" alt="`)
|
|
_, _ = w.Write(nodeToHTMLText(n, source))
|
|
_ = w.WriteByte('"')
|
|
if n.Title != nil {
|
|
_, _ = w.WriteString(` title="`)
|
|
_, _ = w.Write(n.Title)
|
|
_ = w.WriteByte('"')
|
|
}
|
|
if n.Attributes() != nil {
|
|
html.RenderAttributes(w, n, html.ImageAttributeFilter)
|
|
}
|
|
_, _ = w.WriteString(">")
|
|
return ast.WalkSkipChildren, nil
|
|
}
|
|
|
|
func (r *urlRenderer) renderLink(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
|
|
if !entering {
|
|
return ast.WalkContinue, nil
|
|
}
|
|
|
|
n := node.(*ast.Link)
|
|
|
|
url, err := r.resolveUrl(string(n.Destination))
|
|
if err != nil {
|
|
return ast.WalkStop, err
|
|
}
|
|
|
|
_, _ = w.WriteString(`<a href="`)
|
|
_, _ = w.WriteString(url)
|
|
_, _ = w.WriteString(`"`)
|
|
if n.Attributes() != nil {
|
|
html.RenderAttributes(w, n, html.ImageAttributeFilter)
|
|
}
|
|
_, _ = w.WriteString(`>`)
|
|
if n.Title != nil {
|
|
_, _ = w.Write(n.Title)
|
|
}
|
|
_, _ = w.WriteString("</a>")
|
|
return ast.WalkSkipChildren, nil
|
|
}
|
|
|
|
func (r *urlRenderer) resolveUrl(url string) (string, error) {
|
|
if !strings.HasPrefix(url, "http://") && !strings.HasPrefix(url, "https://") {
|
|
return r.transformer(url)
|
|
}
|
|
|
|
return url, nil
|
|
}
|
|
|
|
func nodeToHTMLText(n ast.Node, source []byte) []byte {
|
|
var buf bytes.Buffer
|
|
for c := n.FirstChild(); c != nil; c = c.NextSibling() {
|
|
if s, ok := c.(*ast.String); ok && s.IsCode() {
|
|
buf.Write(s.Text(source))
|
|
} else if !c.HasChildren() {
|
|
buf.Write(util.EscapeHTML(c.Text(source)))
|
|
} else {
|
|
buf.Write(nodeToHTMLText(c, source))
|
|
}
|
|
}
|
|
return buf.Bytes()
|
|
}
|