Skip to content

Commit

Permalink
[#133] Add initial math support to GFM
Browse files Browse the repository at this point in the history
  • Loading branch information
Ekaterina Berezhko authored and FirstTimeInForever committed Apr 3, 2024
1 parent 31f49df commit 9253d03
Show file tree
Hide file tree
Showing 9 changed files with 167 additions and 46 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ object GFMTokenTypes {

@JvmField
val CELL: IElementType = MarkdownElementType("CELL", true)

@JvmField
val DOLLAR: IElementType = MarkdownElementType("DOLLAR", true)
}

object GFMElementTypes {
Expand All @@ -33,4 +36,10 @@ object GFMElementTypes {

@JvmField
val ROW: IElementType = MarkdownElementType("ROW")

@JvmField
val INLINE_MATH: IElementType = MarkdownElementType("INLINE_MATH")

@JvmField
val BLOCK_MATH: IElementType = MarkdownElementType("BLOCK_MATH")
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ open class GFMFlavourDescriptor(
override val sequentialParserManager = object : SequentialParserManager() {
override fun getParserSequence(): List<SequentialParser> {
return listOf(AutolinkParser(listOf(MarkdownTokenTypes.AUTOLINK, GFMTokenTypes.GFM_AUTOLINK)),
MathParser(),
BacktickParser(),
ImageParser(),
InlineLinkParser(),
Expand Down Expand Up @@ -105,7 +106,10 @@ open class GFMFlavourDescriptor(
}
},

MarkdownElementTypes.LIST_ITEM to CheckedListItemGeneratingProvider()
)
MarkdownElementTypes.LIST_ITEM to CheckedListItemGeneratingProvider(),

GFMElementTypes.INLINE_MATH to MathGeneratingProvider(inline = true),
GFMElementTypes.BLOCK_MATH to MathGeneratingProvider()
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import org.intellij.markdown.html.InlineHolderGeneratingProvider
import org.intellij.markdown.html.SimpleTagProvider
import org.intellij.markdown.html.entities.EntityConverter
import org.intellij.markdown.lexer.Compat.assert
import kotlin.text.Regex

internal class CheckedListItemGeneratingProvider : SimpleTagProvider("li") {
override fun openTag(visitor: HtmlGenerator.HtmlGeneratingVisitor, text: String, node: ASTNode) {
Expand Down Expand Up @@ -152,6 +151,16 @@ open class TableAwareCodeSpanGeneratingProvider : GeneratingProvider {
}
}

internal class MathGeneratingProvider(private val inline: Boolean = false): GeneratingProvider {
override fun processNode(visitor: HtmlGenerator.HtmlGeneratingVisitor, text: String, node: ASTNode) {
val nodes = node.children.subList(1, node.children.size - 1)
val output = nodes.joinToString(separator = "") { HtmlGenerator.leafText(text, it, false) }.trim()
visitor.consumeTagOpen(node, "span", "class=\"math\"", "inline = \"$inline\"")
visitor.consumeHtml(output)
visitor.consumeTagClose("span")
}
}

internal class TablesGeneratingProvider : GeneratingProvider {
override fun processNode(visitor: HtmlGenerator.HtmlGeneratingVisitor, text: String, node: ASTNode) {
assert(node.type == GFMElementTypes.TABLE)
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,14 @@ GFM_AUTOLINK = (("http" "s"? | "ftp" | "file")"://" | "www.") {HOST_PART} ("." {
return parseDelimited.returnType;
}

// Math
"$"+ {
if (canInline()) {
return GFMTokenTypes.DOLLAR;
}
return parseDelimited.returnType;
}

// Emphasis
{WHITE_SPACE}+ ("*" | "_") {WHITE_SPACE}+ {
return getReturnGeneralized(MarkdownTokenTypes.TEXT);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package org.intellij.markdown.parser.sequentialparsers.impl

import org.intellij.markdown.flavours.gfm.GFMElementTypes
import org.intellij.markdown.flavours.gfm.GFMTokenTypes
import org.intellij.markdown.parser.sequentialparsers.RangesListBuilder
import org.intellij.markdown.parser.sequentialparsers.SequentialParser
import org.intellij.markdown.parser.sequentialparsers.TokensCache

class MathParser : SequentialParser {
override fun parse(tokens: TokensCache, rangesToGlue: List<IntRange>): SequentialParser.ParsingResult {
val result = SequentialParser.ParsingResultBuilder()
val delegateIndices = RangesListBuilder()
var iterator: TokensCache.Iterator = tokens.RangesListIterator(rangesToGlue)

while (iterator.type != null) {
if (iterator.type == GFMTokenTypes.DOLLAR) {

val endIterator = findOfSize(iterator.advance(), iterator.length)

if (endIterator != null) {
if (iterator.length == 1) {
result.withNode(SequentialParser.Node(iterator.index..endIterator.index + 1, GFMElementTypes.INLINE_MATH))
} else {
result.withNode(SequentialParser.Node(iterator.index..endIterator.index + 1, GFMElementTypes.BLOCK_MATH))
}
iterator = endIterator.advance()
continue
}
}
delegateIndices.put(iterator.index)
iterator = iterator.advance()
}

return result.withFurtherProcessing(delegateIndices.get())
}

private fun findOfSize(it: TokensCache.Iterator, length: Int): TokensCache.Iterator? {
var iterator = it
while (iterator.type != null) {
if (iterator.type == GFMTokenTypes.DOLLAR) {
if (iterator.length == length) {
return iterator
}
}

iterator = iterator.advance()
}
return null
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,11 @@ Markdown:MARKDOWN_FILE
defaultTest(GFMFlavourDescriptor())
}

@Test
fun testMath() {
defaultTest(GFMFlavourDescriptor())
}

@Test
fun testBrokenTable() {
defaultTest(GFMFlavourDescriptor())
Expand Down
3 changes: 3 additions & 0 deletions src/fileBasedTest/resources/data/parser/math.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
This expression uses `\$` to display a dollar sign: $\sqrt{\$4}$

$$\sum_{k=1}^n a_k^2$$
26 changes: 26 additions & 0 deletions src/fileBasedTest/resources/data/parser/math.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
Markdown:MARKDOWN_FILE
Markdown:PARAGRAPH
Markdown:TEXT('This expression uses')
WHITE_SPACE(' ')
Markdown:CODE_SPAN
Markdown:BACKTICK('`')
Markdown:TEXT('\$')
Markdown:BACKTICK('`')
WHITE_SPACE(' ')
Markdown:TEXT('to display a dollar sign')
Markdown::(':')
WHITE_SPACE(' ')
Markdown:INLINE_MATH
Markdown:DOLLAR('$')
Markdown:TEXT('\sqrt{\$4}')
Markdown:DOLLAR('$')
Markdown:EOL('\n')
Markdown:EOL('\n')
Markdown:PARAGRAPH
Markdown:BLOCK_MATH
Markdown:DOLLAR('$$')
Markdown:TEXT('\sum')
Markdown:EMPH('_')
Markdown:TEXT('{k=1}^n a_k^2')
Markdown:DOLLAR('$$')
Markdown:EOL('\n')

0 comments on commit 9253d03

Please sign in to comment.