mark/renderer/mkDocsAdmonition.go
Manuel Rüger cf0699c088 fix: remove dead level-map infrastructure in mkDocsAdmonitionRenderer
GenerateMkDocsAdmonitionLevel walked the AST looking for
ast.KindBlockquote nodes to build a nesting-level map, but the
renderer is registered for parser.KindAdmonition nodes. Because
admonition nodes were never added to the map, LevelMap.Level()
always returned 0 for every admonition, making the level check
in renderMkDocsAdmonition a no-op.

The intended behaviour (all admonitions rendered as Confluence
structured macros regardless of nesting) was accidentally working
because of this bug. Remove the dead MkDocsAdmonitionLevelMap type,
GenerateMkDocsAdmonitionLevel function, and LevelMap field, and
simplify renderMkDocsAdmonition to directly render the Confluence
macro for all known admonition types.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-16 19:18:29 +01:00

117 lines
3.3 KiB
Go

package renderer
import (
"fmt"
"strconv"
parser "github.com/stefanfritsch/goldmark-admonitions"
"github.com/yuin/goldmark/ast"
"github.com/yuin/goldmark/renderer"
"github.com/yuin/goldmark/renderer/html"
"github.com/yuin/goldmark/util"
)
// HeadingAttributeFilter defines attribute names which heading elements can have
var MkDocsAdmonitionAttributeFilter = html.GlobalAttributeFilter
// A Renderer struct is an implementation of renderer.NodeRenderer that renders
// nodes as (X)HTML.
type ConfluenceMkDocsAdmonitionRenderer struct {
html.Config
}
// NewConfluenceMkDocsAdmonitionRenderer creates a new instance of the ConfluenceRenderer
func NewConfluenceMkDocsAdmonitionRenderer(opts ...html.Option) renderer.NodeRenderer {
return &ConfluenceMkDocsAdmonitionRenderer{
Config: html.NewConfig(),
}
}
// RegisterFuncs implements NodeRenderer.RegisterFuncs.
func (r *ConfluenceMkDocsAdmonitionRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) {
reg.Register(parser.KindAdmonition, r.renderMkDocsAdmonition)
}
// Define MkDocsAdmonitionType enum
type MkDocsAdmonitionType int
const (
AInfo MkDocsAdmonitionType = iota
ANote
AWarn
ATip
ANone
)
func (t MkDocsAdmonitionType) String() string {
return []string{"info", "note", "warning", "tip", "none"}[t]
}
func ParseMkDocsAdmonitionType(node ast.Node) MkDocsAdmonitionType {
n, ok := node.(*parser.Admonition)
if !ok {
return ANone
}
switch string(n.AdmonitionClass) {
case "info":
return AInfo
case "note":
return ANote
case "warning":
return AWarn
case "tip":
return ATip
default:
return ANone
}
}
// renderMkDocsAdmonition renders an admonition node as a Confluence structured macro.
// All admonitions (including nested ones) are rendered as Confluence macros.
func (r *ConfluenceMkDocsAdmonitionRenderer) renderMkDocsAdmonition(writer util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
n := node.(*parser.Admonition)
admonitionType := ParseMkDocsAdmonitionType(node)
if entering && admonitionType != ANone {
prefix := fmt.Sprintf("<ac:structured-macro ac:name=\"%s\"><ac:parameter ac:name=\"icon\">true</ac:parameter><ac:rich-text-body>\n", admonitionType)
if _, err := writer.Write([]byte(prefix)); err != nil {
return ast.WalkStop, err
}
title, _ := strconv.Unquote(string(n.Title))
if title != "" {
titleHTML := fmt.Sprintf("<p><strong>%s</strong></p>\n", title)
if _, err := writer.Write([]byte(titleHTML)); err != nil {
return ast.WalkStop, err
}
}
return ast.WalkContinue, nil
}
if !entering && admonitionType != ANone {
suffix := "</ac:rich-text-body></ac:structured-macro>\n"
if _, err := writer.Write([]byte(suffix)); err != nil {
return ast.WalkStop, err
}
return ast.WalkContinue, nil
}
return r.renderMkDocsAdmon(writer, source, node, entering)
}
func (r *ConfluenceMkDocsAdmonitionRenderer) renderMkDocsAdmon(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
n := node.(*parser.Admonition)
if entering {
if n.Attributes() != nil {
_, _ = w.WriteString("<blockquote")
html.RenderAttributes(w, n, MkDocsAdmonitionAttributeFilter)
_ = w.WriteByte('>')
} else {
_, _ = w.WriteString("<blockquote>\n")
}
} else {
_, _ = w.WriteString("</blockquote>\n")
}
return ast.WalkContinue, nil
}