@@ -5,7 +5,9 @@ import 'package:html/parser.dart';
55
66import '../api/model/model.dart' ;
77import '../api/model/submessage.dart' ;
8+ import '../log.dart' ;
89import 'code_block.dart' ;
10+ import 'katex.dart' ;
911
1012/// A node in a parse tree for Zulip message-style content.
1113///
@@ -341,22 +343,52 @@ class CodeBlockSpanNode extends ContentNode {
341343}
342344
343345class MathBlockNode extends BlockContentNode {
344- const MathBlockNode ({super .debugHtmlNode, required this .texSource});
346+ const MathBlockNode ({
347+ super .debugHtmlNode,
348+ required this .texSource,
349+ required this .nodes,
350+ });
345351
346352 final String texSource;
353+ final List <KatexNode >? nodes;
347354
348355 @override
349- bool operator == (Object other) {
350- return other is MathBlockNode && other.texSource == texSource;
356+ void debugFillProperties (DiagnosticPropertiesBuilder properties) {
357+ super .debugFillProperties (properties);
358+ properties.add (StringProperty ('texSource' , texSource));
351359 }
352360
353361 @override
354- int get hashCode => Object .hash ('MathBlockNode' , texSource);
362+ List <DiagnosticsNode > debugDescribeChildren () {
363+ return nodes? .map ((node) => node.toDiagnosticsNode ()).toList () ?? const [];
364+ }
365+ }
366+
367+ class KatexNode extends ContentNode {
368+ const KatexNode ({
369+ required this .text,
370+ required this .nodes,
371+ super .debugHtmlNode,
372+ }) : assert ((text != null ) ^ (nodes != null ));
373+
374+ /// The text or a single character this KaTeX span contains, generally
375+ /// observed to be the leaf node in the KaTeX HTML tree.
376+ /// It will be null if this span has child nodes.
377+ final String ? text;
378+
379+ /// The child nodes of this span in the KaTeX HTML tree.
380+ /// It will be null if this span is a text node.
381+ final List <KatexNode >? nodes;
355382
356383 @override
357384 void debugFillProperties (DiagnosticPropertiesBuilder properties) {
358385 super .debugFillProperties (properties);
359- properties.add (StringProperty ('texSource' , texSource));
386+ properties.add (StringProperty ('text' , text));
387+ }
388+
389+ @override
390+ List <DiagnosticsNode > debugDescribeChildren () {
391+ return nodes? .map ((node) => node.toDiagnosticsNode ()).toList () ?? const [];
360392 }
361393}
362394
@@ -822,23 +854,25 @@ class ImageEmojiNode extends EmojiNode {
822854}
823855
824856class MathInlineNode extends InlineContentNode {
825- const MathInlineNode ({super .debugHtmlNode, required this .texSource});
857+ const MathInlineNode ({
858+ super .debugHtmlNode,
859+ required this .texSource,
860+ required this .nodes,
861+ });
826862
827863 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);
864+ final List <KatexNode >? nodes;
836865
837866 @override
838867 void debugFillProperties (DiagnosticPropertiesBuilder properties) {
839868 super .debugFillProperties (properties);
840869 properties.add (StringProperty ('texSource' , texSource));
841870 }
871+
872+ @override
873+ List <DiagnosticsNode > debugDescribeChildren () {
874+ return nodes? .map ((node) => node.toDiagnosticsNode ()).toList () ?? const [];
875+ }
842876}
843877
844878class GlobalTimeNode extends InlineContentNode {
@@ -864,7 +898,10 @@ class GlobalTimeNode extends InlineContentNode {
864898
865899////////////////////////////////////////////////////////////////
866900
867- String ? _parseMath (dom.Element element, {required bool block}) {
901+ ({List <KatexNode >? spans, String texSource})? _parseMath (
902+ dom.Element element, {
903+ required bool block,
904+ }) {
868905 final dom.Element katexElement;
869906 if (! block) {
870907 assert (element.localName == 'span' && element.className == 'katex' );
@@ -882,16 +919,15 @@ String? _parseMath(dom.Element element, {required bool block}) {
882919 }
883920 }
884921
885- // Expect two children span.katex-mathml, span.katex-html .
886- // For now we only care about the .katex-mathml .
887922 if (katexElement.nodes case [
888923 dom.Element (localName: 'span' , className: 'katex-mathml' , nodes: [
889924 dom.Element (
890925 localName: 'math' ,
891926 namespaceUri: 'http://www.w3.org/1998/Math/MathML' )
892927 && final mathElement,
893928 ]),
894- ...
929+ dom.Element (localName: 'span' , className: 'katex-html' , nodes: [...])
930+ && final katexHtmlElement,
895931 ]) {
896932 if (mathElement.attributes['display' ] != (block ? 'block' : null )) {
897933 return null ;
@@ -911,7 +947,15 @@ String? _parseMath(dom.Element element, {required bool block}) {
911947 } else {
912948 return null ;
913949 }
914- return texSource;
950+
951+ List <KatexNode >? spans;
952+ try {
953+ spans = KatexParser ().parseKatexHTML (katexHtmlElement);
954+ } on KatexHtmlParseError catch (e, st) {
955+ assert (debugLog ('$e \n $st ' ));
956+ }
957+
958+ return (spans: spans, texSource: texSource);
915959 } else {
916960 return null ;
917961 }
@@ -927,9 +971,12 @@ String? _parseMath(dom.Element element, {required bool block}) {
927971class _ZulipInlineContentParser {
928972 InlineContentNode ? parseInlineMath (dom.Element element) {
929973 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);
974+ final parsed = _parseMath (element, block: false );
975+ if (parsed == null ) return null ;
976+ return MathInlineNode (
977+ texSource: parsed.texSource,
978+ nodes: parsed.spans,
979+ debugHtmlNode: debugHtmlNode);
933980 }
934981
935982 UserMentionNode ? parseUserMention (dom.Element element) {
@@ -1631,10 +1678,11 @@ class _ZulipContentParser {
16311678 })());
16321679
16331680 final firstChild = nodes.first as dom.Element ;
1634- final texSource = _parseMath (firstChild, block: true );
1635- if (texSource != null ) {
1681+ final parsed = _parseMath (firstChild, block: true );
1682+ if (parsed != null ) {
16361683 result.add (MathBlockNode (
1637- texSource: texSource,
1684+ texSource: parsed.texSource,
1685+ nodes: parsed.spans,
16381686 debugHtmlNode: kDebugMode ? firstChild : null ));
16391687 } else {
16401688 result.add (UnimplementedBlockContentNode (htmlNode: firstChild));
@@ -1666,10 +1714,11 @@ class _ZulipContentParser {
16661714 if (child case dom.Text (text: '\n\n ' )) continue ;
16671715
16681716 if (child case dom.Element (localName: 'span' , className: 'katex-display' )) {
1669- final texSource = _parseMath (child, block: true );
1670- if (texSource != null ) {
1717+ final parsed = _parseMath (child, block: true );
1718+ if (parsed != null ) {
16711719 result.add (MathBlockNode (
1672- texSource: texSource,
1720+ texSource: parsed.texSource,
1721+ nodes: parsed.spans,
16731722 debugHtmlNode: debugHtmlNode));
16741723 continue ;
16751724 }
0 commit comments