67 lines
1.6 KiB
Go
67 lines
1.6 KiB
Go
package cli
|
|
|
|
import (
|
|
"bytes"
|
|
|
|
"github.com/yuin/goldmark/ast"
|
|
"github.com/yuin/goldmark/parser"
|
|
"github.com/yuin/goldmark/text"
|
|
)
|
|
|
|
var kindMath = ast.NewNodeKind("Math")
|
|
|
|
type mathNode struct {
|
|
ast.BaseInline
|
|
value string
|
|
display bool
|
|
}
|
|
|
|
func (n *mathNode) Kind() ast.NodeKind { return kindMath }
|
|
func (n *mathNode) Dump(src []byte, level int) { ast.DumpHelper(n, src, level, nil, nil) }
|
|
|
|
type mathParser struct{}
|
|
|
|
func (p *mathParser) Trigger() []byte { return []byte{'$'} }
|
|
|
|
func (p *mathParser) Parse(parent ast.Node, block text.Reader, pc parser.Context) ast.Node {
|
|
line, _ := block.PeekLine()
|
|
if len(line) == 0 || line[0] != '$' {
|
|
return nil
|
|
}
|
|
display := len(line) >= 2 && line[1] == '$'
|
|
delim := 1
|
|
if display {
|
|
delim = 2
|
|
}
|
|
|
|
rest := line[delim:]
|
|
var closeAt int
|
|
if display {
|
|
closeAt = bytes.Index(rest, []byte("$$"))
|
|
} else {
|
|
closeAt = bytes.IndexByte(rest, '$')
|
|
}
|
|
if closeAt < 0 {
|
|
return nil
|
|
}
|
|
inner := rest[:closeAt]
|
|
if len(bytes.TrimSpace(inner)) == 0 {
|
|
return nil
|
|
}
|
|
|
|
// Currency guard (markdown-it-texmath rule): a single-$ span only counts as
|
|
// math when the open isn't followed by space, the close isn't preceded by
|
|
// space, and the char after the close isn't a digit — so "$5 and $10" stays
|
|
// prose. Display $$ is unambiguous and skips the check.
|
|
if !display {
|
|
after := closeAt + 1
|
|
if inner[0] == ' ' || inner[len(inner)-1] == ' ' ||
|
|
(after < len(rest) && rest[after] >= '0' && rest[after] <= '9') {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
block.Advance(delim + closeAt + delim)
|
|
return &mathNode{value: latexToUnicode(string(bytes.TrimSpace(inner))), display: display}
|
|
}
|