pull/700/merge
Patricio Whittingslow 2022-08-28 18:49:10 +03:00 committed by GitHub
commit 628760900b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 73 additions and 21 deletions

View File

@ -465,6 +465,8 @@ var (
h5CloseTag = []byte("</h5>")
h6Tag = []byte("<h6")
h6CloseTag = []byte("</h6>")
mathTag = []byte("<math>")
mathCloseTag = []byte("</math>")
footnotesDivBytes = []byte("\n<div class=\"footnotes\">\n\n")
footnotesCloseDivBytes = []byte("\n</div>\n")
@ -827,6 +829,10 @@ func (r *HTMLRenderer) RenderNode(w io.Writer, node *Node, entering bool) WalkSt
r.out(w, trCloseTag)
r.cr(w)
}
case Math:
r.out(w, mathTag)
escapeAllHTML(w, node.Literal)
r.out(w, mathCloseTag)
default:
panic("Unknown node type " + node.Type.String())
}

View File

@ -735,7 +735,9 @@ func linkEndsWithEntity(data []byte, linkEnd int) bool {
}
// hasPrefixCaseInsensitive is a custom implementation of
// strings.HasPrefix(strings.ToLower(s), prefix)
//
// strings.HasPrefix(strings.ToLower(s), prefix)
//
// we rolled our own because ToLower pulls in a huge machinery of lowercasing
// anything from Unicode and that's very slow. Since this func will only be
// used on ASCII protocol prefixes, we can take shortcuts.
@ -1226,3 +1228,25 @@ func text(s []byte) *Node {
func normalizeURI(s []byte) []byte {
return s // TODO: implement
}
func math(p *Markdown, data []byte, offset int) (int, *Node) {
if offset > 0 && data[offset-1] == '\\' {
return 0, nil // Dollar sign has been escaped.
}
data = data[offset:]
isInline := len(data) > 2 && data[1] != '$'
if isInline {
data = data[1:]
nl := bytes.IndexByte(data, '\n')
dolla := bytes.IndexByte(data, '$')
if dolla < 0 || (nl >= 0 && nl < dolla) {
return 0, nil // No end to math node or newline before dollar.
}
math := NewNode(Math)
math.Literal = data[:dolla]
return dolla + 2, math
}
// Block math unsupported.
// isBlock := len(data) > 4 && data[1] == '$' && data[2] != '$'
return 0, nil
}

View File

@ -1214,3 +1214,15 @@ func BenchmarkSmartDoubleQuotes(b *testing.B) {
runMarkdown("this should be normal \"quoted\" text.\n", params)
}
}
func TestMath(t *testing.T) {
doTestsParam(t, []string{
"In class we saw $a$ is a length.",
"<p>In class we saw <math>a</math> is a length.</p>\n",
"What we see is that following $a+b <three> four$. Right?",
"<p>What we see is that following <math>a+b &lt;three&gt; four</math>. Right?</p>\n",
}, TestParams{
extensions: DollarMath,
})
}

View File

@ -47,6 +47,7 @@ const (
AutoHeadingIDs // Create the heading ID from the text
BackslashLineBreak // Translate trailing backslashes into line breaks
DefinitionLists // Render definition lists
DollarMath // Parse inline and block math inside '$' as Math nodes.
CommonHTMLFlags HTMLFlags = UseXHTML | Smartypants |
SmartypantsFractions | SmartypantsDashes | SmartypantsLatexDashes
@ -308,6 +309,9 @@ func New(opts ...Option) *Markdown {
if p.extensions&Footnotes != 0 {
p.notes = make([]*reference, 0)
}
if p.extensions&DollarMath != 0 {
p.inlineCallback['$'] = math
}
return &p
}
@ -345,8 +349,8 @@ func WithNoExtensions() Option {
// In Markdown, the link reference syntax can be made to resolve a link to
// a reference instead of an inline URL, in one of the following ways:
//
// * [link text][refid]
// * [refid][]
// - [link text][refid]
// - [refid][]
//
// Usually, the refid is defined at the bottom of the Markdown document. If
// this override function is provided, the refid is passed to the override
@ -363,7 +367,9 @@ func WithRefOverride(o ReferenceOverrideFunc) Option {
// block of markdown-encoded text.
//
// The simplest invocation of Run takes one argument, input:
// output := Run(input)
//
// output := Run(input)
//
// This will parse the input with CommonExtensions enabled and render it with
// the default HTMLRenderer (with CommonHTMLFlags).
//
@ -371,13 +377,15 @@ func WithRefOverride(o ReferenceOverrideFunc) Option {
// type does not contain exported fields, you can not use it directly. Instead,
// use the With* functions. For example, this will call the most basic
// functionality, with no extensions:
// output := Run(input, WithNoExtensions())
//
// output := Run(input, WithNoExtensions())
//
// You can use any number of With* arguments, even contradicting ones. They
// will be applied in order of appearance and the latter will override the
// former:
// output := Run(input, WithNoExtensions(), WithExtensions(exts),
// WithRenderer(yourRenderer))
//
// output := Run(input, WithNoExtensions(), WithExtensions(exts),
// WithRenderer(yourRenderer))
func Run(input []byte, opts ...Option) []byte {
r := NewHTMLRenderer(HTMLRendererParameters{
Flags: CommonHTMLFlags,
@ -491,35 +499,35 @@ func (p *Markdown) parseRefsToAST() {
//
// Consider this markdown with reference-style links:
//
// [link][ref]
// [link][ref]
//
// [ref]: /url/ "tooltip title"
// [ref]: /url/ "tooltip title"
//
// It will be ultimately converted to this HTML:
//
// <p><a href=\"/url/\" title=\"title\">link</a></p>
// <p><a href=\"/url/\" title=\"title\">link</a></p>
//
// And a reference structure will be populated as follows:
//
// p.refs["ref"] = &reference{
// link: "/url/",
// title: "tooltip title",
// }
// p.refs["ref"] = &reference{
// link: "/url/",
// title: "tooltip title",
// }
//
// Alternatively, reference can contain information about a footnote. Consider
// this markdown:
//
// Text needing a footnote.[^a]
// Text needing a footnote.[^a]
//
// [^a]: This is the note
// [^a]: This is the note
//
// A reference structure will be populated as follows:
//
// p.refs["a"] = &reference{
// link: "a",
// title: "This is the note",
// noteID: <some positive int>,
// }
// p.refs["a"] = &reference{
// link: "a",
// title: "This is the note",
// noteID: <some positive int>,
// }
//
// TODO: As you can see, it begs for splitting into two dedicated structures
// for refs and for footnotes.

View File

@ -36,6 +36,7 @@ const (
TableHead
TableBody
TableRow
Math
)
var nodeTypeNames = []string{
@ -63,6 +64,7 @@ var nodeTypeNames = []string{
TableHead: "TableHead",
TableBody: "TableBody",
TableRow: "TableRow",
Math: "Math",
}
func (t NodeType) String() string {