Skip to content

Commit e3d5471

Browse files
rajveermalviyagnprice
authored andcommitted
content: Handle code block spans with multiple CSS classes
Fixes: #933
1 parent 276209d commit e3d5471

File tree

3 files changed

+31
-4
lines changed

3 files changed

+31
-4
lines changed

lib/model/content.dart

+12-4
Original file line numberDiff line numberDiff line change
@@ -1023,17 +1023,25 @@ class _ZulipContentParser {
10231023
span = CodeBlockSpanNode(text: text, type: CodeBlockSpanType.text);
10241024

10251025
case dom.Element(localName: 'span', :final text, :final className):
1026-
final CodeBlockSpanType type = codeBlockSpanTypeFromClassName(className);
1027-
switch (type) {
1028-
case CodeBlockSpanType.unknown:
1026+
// Empirically, when a Pygments node has multiple classes, the first
1027+
// class names a standard token type and the rest are for non-standard
1028+
// token types specific to the language. Zulip web only styles the
1029+
// standard token classes and ignores the others, so we do the same.
1030+
// See: https://github.com/zulip/zulip-flutter/issues/933
1031+
final spanType = className.split(' ')
1032+
.map(codeBlockSpanTypeFromClassName)
1033+
.firstWhereOrNull((e) => e != CodeBlockSpanType.unknown);
1034+
1035+
switch (spanType) {
1036+
case null:
10291037
// TODO(#194): Show these as un-syntax-highlighted code, in production.
10301038
return UnimplementedBlockContentNode(htmlNode: divElement);
10311039
case CodeBlockSpanType.highlightedLines:
10321040
// TODO: Implement nesting in CodeBlockSpanNode to support hierarchically
10331041
// inherited styles for `span.hll` nodes.
10341042
return UnimplementedBlockContentNode(htmlNode: divElement);
10351043
default:
1036-
span = CodeBlockSpanNode(text: text, type: type);
1044+
span = CodeBlockSpanNode(text: text, type: spanType);
10371045
}
10381046

10391047
default:

test/model/content_test.dart

+18
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,23 @@ class ContentExample {
344344
]),
345345
]);
346346

347+
static const codeBlockSpansWithMultipleClasses = ContentExample(
348+
'code block spans with multiple CSS classes',
349+
'```yaml\n- item\n```',
350+
expectedText: '- item',
351+
// https://chat.zulip.org/#narrow/channel/7-test-here/topic/Greg/near/1949014
352+
'<div class="codehilite" data-code-language="YAML">'
353+
'<pre><span></span><code><span class="p p-Indicator">-</span>'
354+
'<span class="w"> </span>'
355+
'<span class="l l-Scalar l-Scalar-Plain">item</span>\n'
356+
'</code></pre></div>', [
357+
CodeBlockNode([
358+
CodeBlockSpanNode(text: "-", type: CodeBlockSpanType.punctuation),
359+
CodeBlockSpanNode(text: " ", type: CodeBlockSpanType.whitespace),
360+
CodeBlockSpanNode(text: "item", type: CodeBlockSpanType.literal)
361+
]),
362+
]);
363+
347364
// Current servers no longer produce this, but it can be found in ancient
348365
// messages. For example:
349366
// https://chat.zulip.org/#narrow/stream/2-general/topic/Error.20in.20dev.20server/near/18765
@@ -1160,6 +1177,7 @@ void main() {
11601177
testParseExample(ContentExample.codeBlockPlain);
11611178
testParseExample(ContentExample.codeBlockHighlightedShort);
11621179
testParseExample(ContentExample.codeBlockHighlightedMultiline);
1180+
testParseExample(ContentExample.codeBlockSpansWithMultipleClasses);
11631181
testParseExample(ContentExample.codeBlockWithEmptyBody);
11641182
testParseExample(ContentExample.codeBlockWithHighlightedLines);
11651183
testParseExample(ContentExample.codeBlockWithUnknownSpanType);

test/widgets/content_test.dart

+1
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,7 @@ void main() {
480480
testContentSmoke(ContentExample.codeBlockPlain);
481481
testContentSmoke(ContentExample.codeBlockHighlightedShort);
482482
testContentSmoke(ContentExample.codeBlockHighlightedMultiline);
483+
testContentSmoke(ContentExample.codeBlockSpansWithMultipleClasses);
483484

484485
testFontWeight('syntax highlighting: non-bold span',
485486
expectedWght: 400,

0 commit comments

Comments
 (0)