feat: Add LaTeX markdown rendering as per MSC2191

This commit is contained in:
Sorunome 2020-10-21 11:20:19 +02:00
parent 090f0c326c
commit be6824b746
No known key found for this signature in database
GPG Key ID: B19471D07FC9BE9C
2 changed files with 60 additions and 4 deletions

View File

@ -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<String> parseChildLines(BlockParser parser) {
var childLines = <String>[];
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<String, Map<String, String>> emotePacks]) {
var ret = markdownToHtml(
text,
extensionSet: ExtensionSet.commonMark,
blockSyntaxes: [
BlockLatexSyntax(),
],
inlineSyntaxes: [
StrikethroughSyntax(),
LinebreakSyntax(),
SpoilerSyntax(),
EmoteSyntax(emotePacks),
PillSyntax()
PillSyntax(),
InlineLatexSyntax(),
],
);
var stripPTags = '<p>'.allMatches(ret).length <= 1;
if (stripPTags) {
final otherBlockTags = [
const otherBlockTags = {
'table',
'pre',
'ol',
@ -116,8 +165,9 @@ String markdown(String text, [Map<String, Map<String, String>> 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('</${tag}>')) {

View File

@ -70,5 +70,11 @@ void main() {
expect(markdown('!blah:example.org'),
'<a href="https://matrix.to/#/!blah:example.org">!blah:example.org</a>');
});
test('latex', () {
expect(markdown('meep \$\\frac{2}{3}\$'),
'meep <span data-mx-maths="\\frac{2}{3}"><code>\\frac{2}{3}</code></span>');
expect(markdown('hey\n\$\$\nbeep\nboop\n\$\$\nmeow'),
'<p>hey</p>\n<div data-mx-maths="beep\nboop">\n<pre><code>beep\nboop</code></pre>\n</div>\n<p>meow</p>');
});
});
}