diff --git a/lib/src/utils/markdown.dart b/lib/src/utils/markdown.dart index 745601b..81a882c 100644 --- a/lib/src/utils/markdown.dart +++ b/lib/src/utils/markdown.dart @@ -76,6 +76,51 @@ class EmoteSyntax extends InlineSyntax { } } +class InlineLatexSyntax extends InlineSyntax { + InlineLatexSyntax() : super(r'(?<=\s|^)\$(?=\S)([^\n$]+)(?<=\S)\$(?=\s|$)'); + + @override + bool onMatch(InlineParser parser, Match match) { + final latex = htmlEscape.convert(match[1]); + final element = Element('span', [Element.text('code', latex)]); + element.attributes['data-mx-maths'] = latex; + parser.addNode(element); + return true; + } +} + +class BlockLatexSyntax extends BlockSyntax { + @override + RegExp get pattern => RegExp(r'^[ ]{0,3}\${2}\s*$'); + + @override + List parseChildLines(BlockParser parser) { + var childLines = []; + parser.advance(); + while (!parser.isDone) { + if (!pattern.hasMatch(parser.current)) { + childLines.add(parser.current); + parser.advance(); + } else { + parser.advance(); + break; + } + } + return childLines; + } + + @override + Node parse(BlockParser parser) { + final childLines = parseChildLines(parser); + final latex = htmlEscape.convert(childLines.join('\n')); + final element = Element('div', [ + Element('pre', [Element.text('code', latex)]) + ]); + element.attributes['data-mx-maths'] = latex; + return element; + } +} + class PillSyntax extends InlineSyntax { PillSyntax() : super(r'([@#!][^\s:]*:[^\s]+\.\w+)'); @@ -94,18 +139,22 @@ String markdown(String text, [Map> emotePacks]) { var ret = markdownToHtml( text, extensionSet: ExtensionSet.commonMark, + blockSyntaxes: [ + BlockLatexSyntax(), + ], inlineSyntaxes: [ StrikethroughSyntax(), LinebreakSyntax(), SpoilerSyntax(), EmoteSyntax(emotePacks), - PillSyntax() + PillSyntax(), + InlineLatexSyntax(), ], ); var stripPTags = '

'.allMatches(ret).length <= 1; if (stripPTags) { - final otherBlockTags = [ + const otherBlockTags = { 'table', 'pre', 'ol', @@ -116,8 +165,9 @@ String markdown(String text, [Map> emotePacks]) { 'h4', 'h5', 'h6', - 'blockquote' - ]; + 'blockquote', + 'div', + }; for (final tag in otherBlockTags) { // we check for the close tag as the opening one might have attributes if (ret.contains('')) { diff --git a/test/markdown_test.dart b/test/markdown_test.dart index 7690d4b..0b172d6 100644 --- a/test/markdown_test.dart +++ b/test/markdown_test.dart @@ -70,5 +70,11 @@ void main() { expect(markdown('!blah:example.org'), '!blah:example.org'); }); + test('latex', () { + expect(markdown('meep \$\\frac{2}{3}\$'), + 'meep \\frac{2}{3}'); + expect(markdown('hey\n\$\$\nbeep\nboop\n\$\$\nmeow'), + '

hey

\n
\n
beep\nboop
\n
\n

meow

'); + }); }); }