diff --git a/markdown/markdown.go b/markdown/markdown.go
index 2ae7edc5..821d19ad 100644
--- a/markdown/markdown.go
+++ b/markdown/markdown.go
@@ -56,6 +56,7 @@ func (c *ConfluenceExtension) Extend(m goldmark.Markdown) {
util.Prioritized(crenderer.NewConfluenceImageRenderer(c.Stdlib, c, c.Path), 100),
util.Prioritized(crenderer.NewConfluenceParagraphRenderer(), 100),
util.Prioritized(crenderer.NewConfluenceLinkRenderer(), 100),
+ util.Prioritized(crenderer.NewConfluenceMathLatexRenderer(), 100),
))
if slices.Contains(c.MarkConfig.Features, "mkdocsadmonitions") {
@@ -74,6 +75,7 @@ func (c *ConfluenceExtension) Extend(m goldmark.Markdown) {
// Must be registered with a higher priority than goldmark's linkParser to make sure goldmark doesn't parse
// the tags.
util.Prioritized(cparser.NewConfluenceTagParser(), 199),
+ util.Prioritized(cparser.NewMathLatexParser(), 200),
))
}
diff --git a/parser/mathlatex.go b/parser/mathlatex.go
new file mode 100644
index 00000000..2c96d010
--- /dev/null
+++ b/parser/mathlatex.go
@@ -0,0 +1,160 @@
+package parser
+
+import (
+ "github.com/yuin/goldmark/ast"
+ "github.com/yuin/goldmark/parser"
+ "github.com/yuin/goldmark/text"
+ "github.com/yuin/goldmark/util"
+)
+
+type MathLatexInline struct {
+ ast.BaseInline
+
+ Equation []byte
+}
+
+func (n *MathLatexInline) Inline() {}
+
+func (n *MathLatexInline) IsBlank(source []byte) bool {
+ for c := n.FirstChild(); c != nil; c = c.NextSibling() {
+ text := c.(*ast.Text).Segment
+ if !util.IsBlank(text.Value(source)) {
+ return false
+ }
+ }
+ return true
+}
+
+func (n *MathLatexInline) Dump(source []byte, level int) {
+ ast.DumpHelper(n, source, level, nil, nil)
+}
+
+var KindMathLatexInline = ast.NewNodeKind("MathLatexInline")
+
+func (n *MathLatexInline) Kind() ast.NodeKind {
+ return KindMathLatexInline
+}
+
+type MathLatexBlock struct {
+ ast.BaseInline
+
+ Equation []byte
+}
+
+func (n *MathLatexBlock) IsBlank(source []byte) bool {
+ for c := n.FirstChild(); c != nil; c = c.NextSibling() {
+ text := c.(*ast.Text).Segment
+ if !util.IsBlank(text.Value(source)) {
+ return false
+ }
+ }
+ return true
+}
+
+func (n *MathLatexBlock) Dump(source []byte, level int) {
+ ast.DumpHelper(n, source, level, nil, nil)
+}
+
+var KindMathLatexBlock = ast.NewNodeKind("MathLatexBlock")
+
+func (n *MathLatexBlock) Kind() ast.NodeKind {
+ return KindMathLatexBlock
+}
+
+type MathLatexParser struct {
+}
+
+func NewMathLatexParser() parser.InlineParser {
+ return &MathLatexParser{}
+}
+
+func (s *MathLatexParser) Trigger() []byte {
+ return []byte{'$'}
+}
+
+func (s *MathLatexParser) Parse(parent ast.Node, block text.Reader, pc parser.Context) ast.Node {
+ buf := block.Source()
+ ln, pos := block.Position()
+
+ lstart := pos.Start
+ lend := pos.Stop
+ line := buf[lstart:lend]
+
+ var start, end, advance int
+
+ trigger := line[0]
+
+ display := len(line) > 1 && line[1] == trigger
+
+ if display { // Display
+ start = lstart + 2
+
+ offset := 2
+
+ L:
+ for x := 0; x < 20; x++ {
+ for j := offset; j < len(line); j++ {
+ if len(line) > j+1 && line[j] == trigger && line[j+1] == trigger {
+ end = lstart + j
+ advance = 2
+ break L
+ }
+ }
+ if lend == len(buf) {
+ break
+ }
+ if end == 0 {
+ rest := buf[lend:]
+ j := 1
+ for j < len(rest) && rest[j] != '\n' {
+ j++
+ }
+ lstart = lend
+ lend += j
+ line = buf[lstart:lend]
+ ln++
+ offset = 0
+ }
+ }
+
+ } else { // Inline
+ start = lstart + 1
+
+ for i := 1; i < len(line); i++ {
+ c := line[i]
+ if c == '\\' {
+ i++
+ continue
+ }
+ if c == trigger {
+ end = lstart + i
+ advance = 1
+ break
+ }
+ }
+ if end >= len(buf) || buf[end] != trigger {
+ return nil
+ }
+ }
+
+ if start >= end {
+ return nil
+ }
+
+ newpos := end + advance
+ if newpos < lend {
+ block.SetPosition(ln, text.NewSegment(newpos, lend))
+ } else {
+ block.Advance(newpos)
+ }
+
+ if display {
+ return &MathLatexBlock{
+ Equation: buf[start:end],
+ }
+ } else {
+ return &MathLatexInline{
+ Equation: buf[start:end],
+ }
+ }
+}
diff --git a/renderer/mathlatex.go b/renderer/mathlatex.go
new file mode 100644
index 00000000..ce5e2cb9
--- /dev/null
+++ b/renderer/mathlatex.go
@@ -0,0 +1,58 @@
+package renderer
+
+import (
+ "fmt"
+
+ "github.com/yuin/goldmark/ast"
+ "github.com/yuin/goldmark/renderer"
+ "github.com/yuin/goldmark/renderer/html"
+ "github.com/yuin/goldmark/util"
+
+ cparser "github.com/kovetskiy/mark/parser"
+)
+
+type ConfluenceMathLatexRenderer struct {
+ html.Config
+}
+
+// NewConfluenceRenderer creates a new instance of the ConfluenceRenderer
+func NewConfluenceMathLatexRenderer(opts ...html.Option) renderer.NodeRenderer {
+ return &ConfluenceMathLatexRenderer{
+ Config: html.NewConfig(),
+ }
+}
+
+// RegisterFuncs implements NodeRenderer.RegisterFuncs .
+func (r *ConfluenceMathLatexRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) {
+ reg.Register(cparser.KindMathLatexInline, r.renderInline)
+ reg.Register(cparser.KindMathLatexBlock, r.renderBlock)
+}
+
+func (r *ConfluenceMathLatexRenderer) renderInline(w util.BufWriter, source []byte, n ast.Node, entering bool) (ast.WalkStatus, error) {
+ if entering {
+ node := n.(*cparser.MathLatexInline)
+ quoteType := "ppl mathjax inline macro"
+
+ w.WriteString(fmt.Sprintf("", quoteType))
+ w.WriteString("")
+ w.Write(node.Equation)
+ w.WriteString("")
+ w.WriteString("")
+ }
+ return ast.WalkContinue, nil
+}
+
+func (r *ConfluenceMathLatexRenderer) renderBlock(w util.BufWriter, source []byte, n ast.Node, entering bool) (ast.WalkStatus, error) {
+ if entering {
+ node := n.(*cparser.MathLatexBlock)
+ quoteType := "ppl mathjax block macro"
+
+ w.WriteString(fmt.Sprintf("", quoteType))
+ w.WriteString("")
+ w.WriteString("")
+ }
+
+ return ast.WalkContinue, nil
+}