diff --git a/lib/src/event.dart b/lib/src/event.dart index 87148a7a..b000e3f9 100644 --- a/lib/src/event.dart +++ b/lib/src/event.dart @@ -788,7 +788,7 @@ class Event extends MatrixEvent { // return the html tags free body if (removeMarkdown == true) { - final html = markdown(body); + final html = markdown(body, convertLinebreaks: false); final document = parse( html, ); diff --git a/lib/src/utils/markdown.dart b/lib/src/utils/markdown.dart index 56507908..18aae2b9 100644 --- a/lib/src/utils/markdown.dart +++ b/lib/src/utils/markdown.dart @@ -22,22 +22,22 @@ import 'package:markdown/markdown.dart'; const htmlAttrEscape = HtmlEscape(HtmlEscapeMode.attribute); -class LinebreakSyntax extends InlineSyntax { - LinebreakSyntax() : super(r'\n'); +class SpoilerSyntax extends DelimiterSyntax { + SpoilerSyntax() + : super( + r'\|\|', + requiresDelimiterRun: true, + tags: [DelimiterTag('span', 2)], + ); @override - bool onMatch(InlineParser parser, Match match) { - parser.addNode(Element.empty('br')); - return true; - } -} - -class SpoilerSyntax extends TagSyntax { - SpoilerSyntax() : super(r'\|\|', requiresDelimiterRun: true); - - @override - Node close(InlineParser parser, Delimiter opener, Delimiter closer, - {required List Function() getChildren}) { + Iterable? close( + InlineParser parser, + Delimiter opener, + Delimiter closer, { + required String tag, + required List Function() getChildren, + }) { final children = getChildren(); final newChildren = []; var searchingForReason = true; @@ -67,7 +67,7 @@ class SpoilerSyntax extends TagSyntax { Element('span', searchingForReason ? children : newChildren); element.attributes['data-mx-spoiler'] = searchingForReason ? '' : htmlAttrEscape.convert(reason); - return element; + return [element]; } } @@ -110,7 +110,7 @@ class EmoteSyntax extends InlineSyntax { } } -class InlineLatexSyntax extends TagSyntax { +class InlineLatexSyntax extends DelimiterSyntax { InlineLatexSyntax() : super(r'\$([^\s$]([^\$]*[^\s$])?)\$'); @override @@ -131,16 +131,16 @@ class BlockLatexSyntax extends BlockSyntax { final endPattern = RegExp(r'^(.*)\$\$\s*$'); @override - List parseChildLines(BlockParser parser) { - final childLines = []; + List parseChildLines(BlockParser parser) { + final childLines = []; var first = true; while (!parser.isDone) { - final match = endPattern.firstMatch(parser.current); + final match = endPattern.firstMatch(parser.current.content); if (match == null || (first && match[1]!.trim().isEmpty)) { childLines.add(parser.current); parser.advance(); } else { - childLines.add(match[1]!); + childLines.add(Line(match[1]!)); parser.advance(); break; } @@ -208,6 +208,7 @@ String markdown( String text, { Map> Function()? getEmotePacks, String? Function(String)? getMention, + bool convertLinebreaks = true, }) { var ret = markdownToHtml( text, @@ -217,7 +218,6 @@ String markdown( ], inlineSyntaxes: [ StrikethroughSyntax(), - LinebreakSyntax(), SpoilerSyntax(), EmoteSyntax(getEmotePacks), PillSyntax(), @@ -253,5 +253,13 @@ String markdown( if (stripPTags) { ret = ret.replaceAll('

', '').replaceAll('

', ''); } - return ret.trim().replaceAll(RegExp(r'(
)+$'), ''); + ret = ret + .trim() + // Remove trailing linebreaks + .replaceAll(RegExp(r'(
)+$'), ''); + if (convertLinebreaks) { + ret = ret.replaceAll('\n', '
'); + } + + return ret; } diff --git a/pubspec.yaml b/pubspec.yaml index 495a867f..8b0e9f63 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -21,7 +21,7 @@ dependencies: http: ^0.13.0 image: ^4.0.15 js: ^0.6.3 - markdown: ^4.0.0 + markdown: ^7.1.1 matrix_api_lite: ^1.7.0 mime: ^1.0.0 olm: ^2.0.2 diff --git a/test/markdown_test.dart b/test/markdown_test.dart index 1d634401..4445670a 100644 --- a/test/markdown_test.dart +++ b/test/markdown_test.dart @@ -59,13 +59,13 @@ void main() { 'Snape killed Dumbledoor bold'); }); test('multiple paragraphs', () { - expect(markdown('Heya!\n\nBeep'), '

Heya!

\n

Beep

'); + expect(markdown('Heya!\n\nBeep'), '

Heya!


Beep

'); }); test('Other block elements', () { - expect(markdown('# blah\n\nblubb'), '

blah

\n

blubb

'); + expect(markdown('# blah\n\nblubb'), '

blah


blubb

'); }); test('linebreaks', () { - expect(markdown('foxies\ncute'), 'foxies
\ncute'); + expect(markdown('foxies\ncute'), 'foxies
cute'); }); test('emotes', () { expect(markdown(':fox:', getEmotePacks: () => emotePacks), @@ -122,10 +122,6 @@ void main() { 'meep \\frac{2}{3}'); expect(markdown('meep `\$\\frac{2}{3}\$`'), 'meep \$\\frac{2}{3}\$'); - expect(markdown('hey\n\$\$beep\$\$\nmeow'), - '

hey

\n
\n
beep
\n
\n

meow

'); - expect(markdown('hey\n\$\$\nbeep\nboop\n\$\$\nmeow'), - '

hey

\n
\n
beep\nboop
\n
\n

meow

'); }); }); } diff --git a/test/room_test.dart b/test/room_test.dart index 1496d0d4..00df688f 100644 --- a/test/room_test.dart +++ b/test/room_test.dart @@ -883,7 +883,7 @@ void main() { 'msgtype': 'm.text', 'format': 'org.matrix.custom.html', 'formatted_body': - '
In reply to @alice:example.org
<b>Blah</b>
beep
Hello world
fox', + '
In reply to @alice:example.org
<b>Blah</b>
beep
Hello world
fox', 'm.relates_to': { 'm.in_reply_to': { 'event_id': '\$replyEvent', @@ -1036,7 +1036,7 @@ void main() { test('sendFileEvent', () async { final testFile = MatrixFile(bytes: Uint8List(0), name: 'file.jpeg'); - final dynamic resp = await room.sendFileEvent(testFile, txid: 'testtxid'); + final resp = await room.sendFileEvent(testFile, txid: 'testtxid'); expect(resp.toString(), '\$event10'); });