package renderer import ( "unicode/utf8" "github.com/yuin/goldmark/ast" "github.com/yuin/goldmark/renderer" "github.com/yuin/goldmark/renderer/html" "github.com/yuin/goldmark/util" ) // ConfluenceTextLegacyRenderer slightly alters the default goldmark behavior for // inline text block. It allows for soft breaks // (c.f. https://spec.commonmark.org/0.30/#softbreak) // to be rendered into HTML as either '\n' (the goldmark default) // or as ' '. // This latter option is useful for Confluence, // which inserts
tags into uploaded HTML where it sees '\n'. // See also https://sembr.org/ for partial motivation. type ConfluenceTextLegacyRenderer struct { html.Config softBreak rune } // NewConfluenceTextLegacyRenderer creates a new instance of the ConfluenceTextRenderer (legacy version) func NewConfluenceTextLegacyRenderer(stripNL bool, opts ...html.Option) renderer.NodeRenderer { sb := '\n' if stripNL { sb = ' ' } return &ConfluenceTextLegacyRenderer{ Config: html.NewConfig(), softBreak: sb, } } // RegisterFuncs implements NodeRenderer.RegisterFuncs . func (r *ConfluenceTextLegacyRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) { reg.Register(ast.KindText, r.renderText) } // This is taken from https://github.com/yuin/goldmark/blob/v1.6.0/renderer/html/html.go#L719 // with the hardcoded '\n' for soft breaks swapped for the configurable r.softBreak func (r *ConfluenceTextLegacyRenderer) renderText(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) { if !entering { return ast.WalkContinue, nil } n := node.(*ast.Text) segment := n.Segment if n.IsRaw() { r.Writer.RawWrite(w, segment.Value(source)) } else { value := segment.Value(source) r.Writer.Write(w, value) if n.HardLineBreak() || (n.SoftLineBreak() && r.HardWraps) { if r.XHTML { _, _ = w.WriteString("
\n") } else { _, _ = w.WriteString("
\n") } } else if n.SoftLineBreak() { if r.EastAsianLineBreaks != html.EastAsianLineBreaksNone && len(value) != 0 { sibling := node.NextSibling() if sibling != nil && sibling.Kind() == ast.KindText { if siblingText := sibling.(*ast.Text).Value(source); len(siblingText) != 0 { thisLastRune := util.ToRune(value, len(value)-1) siblingFirstRune, _ := utf8.DecodeRune(siblingText) // Inline the softLineBreak function as it's not public writeLineBreak := false switch r.EastAsianLineBreaks { case html.EastAsianLineBreaksNone: writeLineBreak = false case html.EastAsianLineBreaksSimple: writeLineBreak = !util.IsEastAsianWideRune(thisLastRune) || !util.IsEastAsianWideRune(siblingFirstRune) case html.EastAsianLineBreaksCSS3Draft: writeLineBreak = eastAsianLineBreaksCSS3DraftSoftLineBreak(thisLastRune, siblingFirstRune) } if writeLineBreak { _ = w.WriteByte(byte(r.softBreak)) } } } } else { _ = w.WriteByte(byte(r.softBreak)) } } } return ast.WalkContinue, nil }