@@ -6,6 +6,7 @@ import 'package:html/parser.dart';
6
6
import '../api/model/model.dart' ;
7
7
import '../api/model/submessage.dart' ;
8
8
import 'code_block.dart' ;
9
+ import 'katex.dart' ;
9
10
10
11
/// A node in a parse tree for Zulip message-style content.
11
12
///
@@ -341,22 +342,52 @@ class CodeBlockSpanNode extends ContentNode {
341
342
}
342
343
343
344
class MathBlockNode extends BlockContentNode {
344
- const MathBlockNode ({super .debugHtmlNode, required this .texSource});
345
+ const MathBlockNode ({
346
+ super .debugHtmlNode,
347
+ required this .texSource,
348
+ required this .nodes,
349
+ });
345
350
346
351
final String texSource;
352
+ final List <KatexNode >? nodes;
347
353
348
354
@override
349
- bool operator == (Object other) {
350
- return other is MathBlockNode && other.texSource == texSource;
355
+ void debugFillProperties (DiagnosticPropertiesBuilder properties) {
356
+ super .debugFillProperties (properties);
357
+ properties.add (StringProperty ('texSource' , texSource));
351
358
}
352
359
353
360
@override
354
- int get hashCode => Object .hash ('MathBlockNode' , texSource);
361
+ List <DiagnosticsNode > debugDescribeChildren () {
362
+ return nodes? .map ((node) => node.toDiagnosticsNode ()).toList () ?? const [];
363
+ }
364
+ }
365
+
366
+ class KatexNode extends ContentNode {
367
+ const KatexNode ({
368
+ required this .text,
369
+ required this .nodes,
370
+ super .debugHtmlNode,
371
+ }) : assert ((text != null ) ^ (nodes != null ));
372
+
373
+ /// The text or a single character this KaTeX node contains, generally
374
+ /// observed to be the leaf node in the KaTeX HTML tree.
375
+ /// It will be null if [nodes] is non-null.
376
+ final String ? text;
377
+
378
+ /// The child nodes of this node in the KaTeX HTML tree.
379
+ /// It will be null if [text] is non-null.
380
+ final List <KatexNode >? nodes;
355
381
356
382
@override
357
383
void debugFillProperties (DiagnosticPropertiesBuilder properties) {
358
384
super .debugFillProperties (properties);
359
- properties.add (StringProperty ('texSource' , texSource));
385
+ properties.add (StringProperty ('text' , text));
386
+ }
387
+
388
+ @override
389
+ List <DiagnosticsNode > debugDescribeChildren () {
390
+ return nodes? .map ((node) => node.toDiagnosticsNode ()).toList () ?? const [];
360
391
}
361
392
}
362
393
@@ -822,23 +853,25 @@ class ImageEmojiNode extends EmojiNode {
822
853
}
823
854
824
855
class MathInlineNode extends InlineContentNode {
825
- const MathInlineNode ({super .debugHtmlNode, required this .texSource});
856
+ const MathInlineNode ({
857
+ super .debugHtmlNode,
858
+ required this .texSource,
859
+ required this .nodes,
860
+ });
826
861
827
862
final String texSource;
828
-
829
- @override
830
- bool operator == (Object other) {
831
- return other is MathInlineNode && other.texSource == texSource;
832
- }
833
-
834
- @override
835
- int get hashCode => Object .hash ('MathInlineNode' , texSource);
863
+ final List <KatexNode >? nodes;
836
864
837
865
@override
838
866
void debugFillProperties (DiagnosticPropertiesBuilder properties) {
839
867
super .debugFillProperties (properties);
840
868
properties.add (StringProperty ('texSource' , texSource));
841
869
}
870
+
871
+ @override
872
+ List <DiagnosticsNode > debugDescribeChildren () {
873
+ return nodes? .map ((node) => node.toDiagnosticsNode ()).toList () ?? const [];
874
+ }
842
875
}
843
876
844
877
class GlobalTimeNode extends InlineContentNode {
@@ -864,59 +897,6 @@ class GlobalTimeNode extends InlineContentNode {
864
897
865
898
////////////////////////////////////////////////////////////////
866
899
867
- String ? _parseMath (dom.Element element, {required bool block}) {
868
- final dom.Element katexElement;
869
- if (! block) {
870
- assert (element.localName == 'span' && element.className == 'katex' );
871
-
872
- katexElement = element;
873
- } else {
874
- assert (element.localName == 'span' && element.className == 'katex-display' );
875
-
876
- if (element.nodes case [
877
- dom.Element (localName: 'span' , className: 'katex' ) && final child,
878
- ]) {
879
- katexElement = child;
880
- } else {
881
- return null ;
882
- }
883
- }
884
-
885
- // Expect two children span.katex-mathml, span.katex-html .
886
- // For now we only care about the .katex-mathml .
887
- if (katexElement.nodes case [
888
- dom.Element (localName: 'span' , className: 'katex-mathml' , nodes: [
889
- dom.Element (
890
- localName: 'math' ,
891
- namespaceUri: 'http://www.w3.org/1998/Math/MathML' )
892
- && final mathElement,
893
- ]),
894
- ...
895
- ]) {
896
- if (mathElement.attributes['display' ] != (block ? 'block' : null )) {
897
- return null ;
898
- }
899
-
900
- final String texSource;
901
- if (mathElement.nodes case [
902
- dom.Element (localName: 'semantics' , nodes: [
903
- ...,
904
- dom.Element (
905
- localName: 'annotation' ,
906
- attributes: {'encoding' : 'application/x-tex' },
907
- : final text),
908
- ]),
909
- ]) {
910
- texSource = text.trim ();
911
- } else {
912
- return null ;
913
- }
914
- return texSource;
915
- } else {
916
- return null ;
917
- }
918
- }
919
-
920
900
/// Parser for the inline-content subtrees within Zulip content HTML.
921
901
///
922
902
/// The only entry point to this class is [parseBlockInline] .
@@ -927,9 +907,12 @@ String? _parseMath(dom.Element element, {required bool block}) {
927
907
class _ZulipInlineContentParser {
928
908
InlineContentNode ? parseInlineMath (dom.Element element) {
929
909
final debugHtmlNode = kDebugMode ? element : null ;
930
- final texSource = _parseMath (element, block: false );
931
- if (texSource == null ) return null ;
932
- return MathInlineNode (texSource: texSource, debugHtmlNode: debugHtmlNode);
910
+ final parsed = parseMath (element, block: false );
911
+ if (parsed == null ) return null ;
912
+ return MathInlineNode (
913
+ texSource: parsed.texSource,
914
+ nodes: parsed.nodes,
915
+ debugHtmlNode: debugHtmlNode);
933
916
}
934
917
935
918
UserMentionNode ? parseUserMention (dom.Element element) {
@@ -1631,10 +1614,11 @@ class _ZulipContentParser {
1631
1614
})());
1632
1615
1633
1616
final firstChild = nodes.first as dom.Element ;
1634
- final texSource = _parseMath (firstChild, block: true );
1635
- if (texSource != null ) {
1617
+ final parsed = parseMath (firstChild, block: true );
1618
+ if (parsed != null ) {
1636
1619
result.add (MathBlockNode (
1637
- texSource: texSource,
1620
+ texSource: parsed.texSource,
1621
+ nodes: parsed.nodes,
1638
1622
debugHtmlNode: kDebugMode ? firstChild : null ));
1639
1623
} else {
1640
1624
result.add (UnimplementedBlockContentNode (htmlNode: firstChild));
@@ -1666,10 +1650,11 @@ class _ZulipContentParser {
1666
1650
if (child case dom.Text (text: '\n\n ' )) continue ;
1667
1651
1668
1652
if (child case dom.Element (localName: 'span' , className: 'katex-display' )) {
1669
- final texSource = _parseMath (child, block: true );
1670
- if (texSource != null ) {
1653
+ final parsed = parseMath (child, block: true );
1654
+ if (parsed != null ) {
1671
1655
result.add (MathBlockNode (
1672
- texSource: texSource,
1656
+ texSource: parsed.texSource,
1657
+ nodes: parsed.nodes,
1673
1658
debugHtmlNode: debugHtmlNode));
1674
1659
continue ;
1675
1660
}
0 commit comments