Skip to content

Instantly share code, notes, and snippets.

@benhoyt
Created October 7, 2020 19:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save benhoyt/ef85c0bd52bfbdf6d92fd34a80a755b7 to your computer and use it in GitHub Desktop.
Save benhoyt/ef85c0bd52bfbdf6d92fd34a80a755b7 to your computer and use it in GitHub Desktop.
Diff to override goldmark's code block output
diff --git a/internal/markdown/markdown.go b/internal/markdown/markdown.go
index a729b9f..94d29c1 100644
--- a/internal/markdown/markdown.go
+++ b/internal/markdown/markdown.go
@@ -13,8 +13,11 @@ import (
"bytes"
"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/util"
)
// Render converts a limited and opinionated flavor of Markdown (compliant with
@@ -25,14 +28,65 @@ import (
// untrusted content is not performed: the caller is responsible for ensuring
// that only trusted content is provided.
func Render(src []byte) ([]byte, error) {
+ hr := html.NewRenderer(html.WithUnsafe())
+ nr := &CustomNodeRenderer{hr, html.DefaultWriter}
+ r := renderer.NewRenderer(renderer.WithNodeRenderers(util.Prioritized(nr, 1000)))
+
// parser.WithHeadingAttribute allows custom ids on headings.
// html.WithUnsafe allows use of raw HTML, which we need for tables.
md := goldmark.New(
goldmark.WithParserOptions(parser.WithHeadingAttribute()),
- goldmark.WithRendererOptions(html.WithUnsafe()))
+ goldmark.WithRenderer(r))
var buf bytes.Buffer
if err := md.Convert(src, &buf); err != nil {
return nil, err
}
return buf.Bytes(), nil
}
+
+type CustomNodeRenderer struct {
+ nr renderer.NodeRenderer
+ Writer html.Writer
+}
+
+func (r *CustomNodeRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) {
+ r.nr.RegisterFuncs(reg)
+ reg.Register(ast.KindCodeBlock, r.renderCodeBlock)
+ reg.Register(ast.KindFencedCodeBlock, r.renderFencedCodeBlock)
+}
+
+func (r *CustomNodeRenderer) renderCodeBlock(w util.BufWriter, source []byte, n ast.Node, entering bool) (ast.WalkStatus, error) {
+ if entering {
+ _, _ = w.WriteString("<pre>")
+ r.writeLines(w, source, n)
+ } else {
+ _, _ = w.WriteString("</pre>\n")
+ }
+ return ast.WalkContinue, nil
+}
+
+func (r *CustomNodeRenderer) renderFencedCodeBlock(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
+ n := node.(*ast.FencedCodeBlock)
+ if entering {
+ _, _ = w.WriteString("<pre")
+ language := n.Language(source)
+ if language != nil {
+ _, _ = w.WriteString(" class=\"language-")
+ r.Writer.Write(w, language)
+ _, _ = w.WriteString("\"")
+ }
+ _ = w.WriteByte('>')
+ r.writeLines(w, source, n)
+ } else {
+ _, _ = w.WriteString("</pre>\n")
+ }
+ return ast.WalkContinue, nil
+}
+
+func (r *CustomNodeRenderer) writeLines(w util.BufWriter, source []byte, n ast.Node) {
+ l := n.Lines().Len()
+ for i := 0; i < l; i++ {
+ line := n.Lines().At(i)
+ r.Writer.RawWrite(w, line.Value(source))
+ }
+}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment