@@ -786,7 +786,7 @@ class MathInlineNode extends InlineContentNode {
786786class GlobalTimeNode extends InlineContentNode {
787787 const GlobalTimeNode ({super .debugHtmlNode, required this .datetime});
788788
789- /// Always in UTC, enforced in [_ZulipContentParser .parseInlineContent] .
789+ /// Always in UTC, enforced in [_ZulipInlineContentParser .parseInlineContent] .
790790 final DateTime datetime;
791791
792792 @override
@@ -852,32 +852,19 @@ String? _parseMath(dom.Element element, {required bool block}) {
852852 return descendant4.text.trim ();
853853}
854854
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.
865856///
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 {
874863 String ? parseInlineMath (dom.Element element) {
875- assert (_debugParserContext == _ParserContext .inline);
876864 return _parseMath (element, block: false );
877865 }
878866
879867 UserMentionNode ? parseUserMention (dom.Element element) {
880- assert (_debugParserContext == _ParserContext .inline);
881868 assert (element.localName == 'span' );
882869 final debugHtmlNode = kDebugMode ? element : null ;
883870
@@ -951,7 +938,6 @@ class _ZulipContentParser {
951938 static final _emojiCodeFromClassNameRegexp = RegExp (r"emoji-([^ ]+)" );
952939
953940 InlineContentNode parseInlineContent (dom.Node node) {
954- assert (_debugParserContext == _ParserContext .inline);
955941 final debugHtmlNode = kDebugMode ? node : null ;
956942 InlineContentNode unimplemented () => UnimplementedInlineContentNode (htmlNode: node);
957943
@@ -1041,33 +1027,38 @@ class _ZulipContentParser {
10411027 }
10421028
10431029 List <InlineContentNode > parseInlineContentList (List <dom.Node > nodes) {
1044- assert (_debugParserContext == _ParserContext .inline);
10451030 return nodes.map (parseInlineContent).toList (growable: false );
10461031 }
10471032
10481033 /// Parse the children of a [BlockInlineContainerNode] , making up a
10491034 /// complete subtree of inline content with no further inline ancestors.
10501035 ({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- }());
10561036 final resultNodes = parseInlineContentList (nodes);
1057- assert (() {
1058- _debugParserContext = _ParserContext .block;
1059- return true ;
1060- }());
10611037 return (nodes: resultNodes, links: _takeLinkNodes ());
10621038 }
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+ }
10631056
10641057 String ? parseMathBlock (dom.Element element) {
1065- assert (_debugParserContext == _ParserContext .block);
10661058 return _parseMath (element, block: true );
10671059 }
10681060
10691061 BlockContentNode parseListNode (dom.Element element) {
1070- assert (_debugParserContext == _ParserContext .block);
10711062 ListStyle ? listStyle;
10721063 switch (element.localName) {
10731064 case 'ol' : listStyle = ListStyle .ordered; break ;
@@ -1090,7 +1081,6 @@ class _ZulipContentParser {
10901081 }
10911082
10921083 BlockContentNode parseSpoilerNode (dom.Element divElement) {
1093- assert (_debugParserContext == _ParserContext .block);
10941084 assert (divElement.localName == 'div'
10951085 && divElement.className == 'spoiler-block' );
10961086
@@ -1110,7 +1100,6 @@ class _ZulipContentParser {
11101100 }
11111101
11121102 BlockContentNode parseCodeBlock (dom.Element divElement) {
1113- assert (_debugParserContext == _ParserContext .block);
11141103 final mainElement = () {
11151104 assert (divElement.localName == 'div'
11161105 && divElement.className == "codehilite" );
@@ -1193,7 +1182,6 @@ class _ZulipContentParser {
11931182 static final _imageDimensionsRegExp = RegExp (r'^(\d+)x(\d+)$' );
11941183
11951184 BlockContentNode parseImageNode (dom.Element divElement) {
1196- assert (_debugParserContext == _ParserContext .block);
11971185 final elements = () {
11981186 assert (divElement.localName == 'div'
11991187 && divElement.className == 'message_inline_image' );
@@ -1285,7 +1273,6 @@ class _ZulipContentParser {
12851273 }();
12861274
12871275 BlockContentNode parseInlineVideoNode (dom.Element divElement) {
1288- assert (_debugParserContext == _ParserContext .block);
12891276 assert (divElement.localName == 'div'
12901277 && _videoClassNameRegexp.hasMatch (divElement.className));
12911278
@@ -1318,7 +1305,6 @@ class _ZulipContentParser {
13181305 }
13191306
13201307 BlockContentNode parseEmbedVideoNode (dom.Element divElement) {
1321- assert (_debugParserContext == _ParserContext .block);
13221308 assert (divElement.localName == 'div'
13231309 && _videoClassNameRegexp.hasMatch (divElement.className));
13241310
@@ -1357,7 +1343,6 @@ class _ZulipContentParser {
13571343 }
13581344
13591345 BlockContentNode parseTableContent (dom.Element tableElement) {
1360- assert (_debugParserContext == _ParserContext .block);
13611346 assert (tableElement.localName == 'table'
13621347 && tableElement.className.isEmpty);
13631348
@@ -1465,7 +1450,6 @@ class _ZulipContentParser {
14651450 }
14661451
14671452 BlockContentNode parseBlockContent (dom.Node node) {
1468- assert (_debugParserContext == _ParserContext .block);
14691453 final debugHtmlNode = kDebugMode ? node : null ;
14701454 if (node is ! dom.Element ) {
14711455 return UnimplementedBlockContentNode (htmlNode: node);
@@ -1592,7 +1576,6 @@ class _ZulipContentParser {
15921576 ///
15931577 /// See [ParagraphNode] .
15941578 List <BlockContentNode > parseImplicitParagraphBlockContentList (dom.NodeList nodes) {
1595- assert (_debugParserContext == _ParserContext .block);
15961579 final List <BlockContentNode > result = [];
15971580 final List <dom.Node > currentParagraph = [];
15981581 List <ImageNode > imageNodes = [];
@@ -1641,7 +1624,6 @@ class _ZulipContentParser {
16411624 static final _redundantLineBreaksRegexp = RegExp (r'^\n+$' );
16421625
16431626 List <BlockContentNode > parseBlockContentList (dom.NodeList nodes) {
1644- assert (_debugParserContext == _ParserContext .block);
16451627 final List <BlockContentNode > result = [];
16461628 List <ImageNode > imageNodes = [];
16471629 for (final node in nodes) {
0 commit comments