@@ -786,7 +786,7 @@ class MathInlineNode extends InlineContentNode {
786
786
class GlobalTimeNode extends InlineContentNode {
787
787
const GlobalTimeNode ({super .debugHtmlNode, required this .datetime});
788
788
789
- /// Always in UTC, enforced in [_ZulipContentParser .parseInlineContent] .
789
+ /// Always in UTC, enforced in [_ZulipInlineContentParser .parseInlineContent] .
790
790
final DateTime datetime;
791
791
792
792
@override
@@ -852,32 +852,19 @@ String? _parseMath(dom.Element element, {required bool block}) {
852
852
return descendant4.text.trim ();
853
853
}
854
854
855
- /// What sort of nodes a [_ZulipContentParser] is currently expecting to find.
856
- enum _ParserContext {
857
- /// The parser is currently looking for block nodes.
858
- block,
859
-
860
- /// The parser is currently looking for inline nodes.
861
- inline,
862
- }
863
-
864
- /// Parser for a complete piece of Zulip HTML content, a [ZulipContent] .
855
+ /// Parser for the inline-content subtrees within Zulip content HTML.
865
856
///
866
- /// The only entry point to this class is [parse] .
867
- class _ZulipContentParser {
868
- /// The current state of what sort of nodes the parser is looking for.
869
- ///
870
- /// This exists for the sake of debug-mode checks,
871
- /// and should be read or updated only inside an assertion.
872
- _ParserContext _debugParserContext = _ParserContext .block;
873
-
857
+ /// The only entry point to this class is [parseBlockInline] .
858
+ ///
859
+ /// After a call to [parseBlockInline] returns, the [_ZulipInlineContentParser]
860
+ /// instance has been reset to its starting state, and can be re-used for
861
+ /// parsing other subtrees.
862
+ class _ZulipInlineContentParser {
874
863
String ? parseInlineMath (dom.Element element) {
875
- assert (_debugParserContext == _ParserContext .inline);
876
864
return _parseMath (element, block: false );
877
865
}
878
866
879
867
UserMentionNode ? parseUserMention (dom.Element element) {
880
- assert (_debugParserContext == _ParserContext .inline);
881
868
assert (element.localName == 'span' );
882
869
final debugHtmlNode = kDebugMode ? element : null ;
883
870
@@ -951,7 +938,6 @@ class _ZulipContentParser {
951
938
static final _emojiCodeFromClassNameRegexp = RegExp (r"emoji-([^ ]+)" );
952
939
953
940
InlineContentNode parseInlineContent (dom.Node node) {
954
- assert (_debugParserContext == _ParserContext .inline);
955
941
final debugHtmlNode = kDebugMode ? node : null ;
956
942
InlineContentNode unimplemented () => UnimplementedInlineContentNode (htmlNode: node);
957
943
@@ -1041,33 +1027,38 @@ class _ZulipContentParser {
1041
1027
}
1042
1028
1043
1029
List <InlineContentNode > parseInlineContentList (List <dom.Node > nodes) {
1044
- assert (_debugParserContext == _ParserContext .inline);
1045
1030
return nodes.map (parseInlineContent).toList (growable: false );
1046
1031
}
1047
1032
1048
1033
/// Parse the children of a [BlockInlineContainerNode] , making up a
1049
1034
/// complete subtree of inline content with no further inline ancestors.
1050
1035
({List <InlineContentNode > nodes, List <LinkNode >? links}) parseBlockInline (List <dom.Node > nodes) {
1051
- assert (_debugParserContext == _ParserContext .block);
1052
- assert (() {
1053
- _debugParserContext = _ParserContext .inline;
1054
- return true ;
1055
- }());
1056
1036
final resultNodes = parseInlineContentList (nodes);
1057
- assert (() {
1058
- _debugParserContext = _ParserContext .block;
1059
- return true ;
1060
- }());
1061
1037
return (nodes: resultNodes, links: _takeLinkNodes ());
1062
1038
}
1039
+ }
1040
+
1041
+ /// Parser for a complete piece of Zulip HTML content, a [ZulipContent] .
1042
+ ///
1043
+ /// The only entry point to this class is [parse] .
1044
+ class _ZulipContentParser {
1045
+ /// The single inline-content parser used and re-used throughout parsing of
1046
+ /// a complete piece of Zulip HTML content.
1047
+ ///
1048
+ /// Because block content can never appear nested inside inline content,
1049
+ /// there's never a need for more than one of these at a time,
1050
+ /// so we can allocate just one up front.
1051
+ final inlineParser = _ZulipInlineContentParser ();
1052
+
1053
+ ({List <InlineContentNode > nodes, List <LinkNode >? links}) parseBlockInline (List <dom.Node > nodes) {
1054
+ return inlineParser.parseBlockInline (nodes);
1055
+ }
1063
1056
1064
1057
String ? parseMathBlock (dom.Element element) {
1065
- assert (_debugParserContext == _ParserContext .block);
1066
1058
return _parseMath (element, block: true );
1067
1059
}
1068
1060
1069
1061
BlockContentNode parseListNode (dom.Element element) {
1070
- assert (_debugParserContext == _ParserContext .block);
1071
1062
ListStyle ? listStyle;
1072
1063
switch (element.localName) {
1073
1064
case 'ol' : listStyle = ListStyle .ordered; break ;
@@ -1090,7 +1081,6 @@ class _ZulipContentParser {
1090
1081
}
1091
1082
1092
1083
BlockContentNode parseSpoilerNode (dom.Element divElement) {
1093
- assert (_debugParserContext == _ParserContext .block);
1094
1084
assert (divElement.localName == 'div'
1095
1085
&& divElement.className == 'spoiler-block' );
1096
1086
@@ -1110,7 +1100,6 @@ class _ZulipContentParser {
1110
1100
}
1111
1101
1112
1102
BlockContentNode parseCodeBlock (dom.Element divElement) {
1113
- assert (_debugParserContext == _ParserContext .block);
1114
1103
final mainElement = () {
1115
1104
assert (divElement.localName == 'div'
1116
1105
&& divElement.className == "codehilite" );
@@ -1193,7 +1182,6 @@ class _ZulipContentParser {
1193
1182
static final _imageDimensionsRegExp = RegExp (r'^(\d+)x(\d+)$' );
1194
1183
1195
1184
BlockContentNode parseImageNode (dom.Element divElement) {
1196
- assert (_debugParserContext == _ParserContext .block);
1197
1185
final elements = () {
1198
1186
assert (divElement.localName == 'div'
1199
1187
&& divElement.className == 'message_inline_image' );
@@ -1285,7 +1273,6 @@ class _ZulipContentParser {
1285
1273
}();
1286
1274
1287
1275
BlockContentNode parseInlineVideoNode (dom.Element divElement) {
1288
- assert (_debugParserContext == _ParserContext .block);
1289
1276
assert (divElement.localName == 'div'
1290
1277
&& _videoClassNameRegexp.hasMatch (divElement.className));
1291
1278
@@ -1318,7 +1305,6 @@ class _ZulipContentParser {
1318
1305
}
1319
1306
1320
1307
BlockContentNode parseEmbedVideoNode (dom.Element divElement) {
1321
- assert (_debugParserContext == _ParserContext .block);
1322
1308
assert (divElement.localName == 'div'
1323
1309
&& _videoClassNameRegexp.hasMatch (divElement.className));
1324
1310
@@ -1357,7 +1343,6 @@ class _ZulipContentParser {
1357
1343
}
1358
1344
1359
1345
BlockContentNode parseTableContent (dom.Element tableElement) {
1360
- assert (_debugParserContext == _ParserContext .block);
1361
1346
assert (tableElement.localName == 'table'
1362
1347
&& tableElement.className.isEmpty);
1363
1348
@@ -1465,7 +1450,6 @@ class _ZulipContentParser {
1465
1450
}
1466
1451
1467
1452
BlockContentNode parseBlockContent (dom.Node node) {
1468
- assert (_debugParserContext == _ParserContext .block);
1469
1453
final debugHtmlNode = kDebugMode ? node : null ;
1470
1454
if (node is ! dom.Element ) {
1471
1455
return UnimplementedBlockContentNode (htmlNode: node);
@@ -1592,7 +1576,6 @@ class _ZulipContentParser {
1592
1576
///
1593
1577
/// See [ParagraphNode] .
1594
1578
List <BlockContentNode > parseImplicitParagraphBlockContentList (dom.NodeList nodes) {
1595
- assert (_debugParserContext == _ParserContext .block);
1596
1579
final List <BlockContentNode > result = [];
1597
1580
final List <dom.Node > currentParagraph = [];
1598
1581
List <ImageNode > imageNodes = [];
@@ -1641,7 +1624,6 @@ class _ZulipContentParser {
1641
1624
static final _redundantLineBreaksRegexp = RegExp (r'^\n+$' );
1642
1625
1643
1626
List <BlockContentNode > parseBlockContentList (dom.NodeList nodes) {
1644
- assert (_debugParserContext == _ParserContext .block);
1645
1627
final List <BlockContentNode > result = [];
1646
1628
List <ImageNode > imageNodes = [];
1647
1629
for (final node in nodes) {
0 commit comments