Improved math block parsing with added fence types

This commit is contained in:
Thorsten Sommer 2026-03-21 19:45:57 +01:00
parent af2642e0d8
commit 7d9ad7bdd3
Signed by untrusted user who does not match committer: tsommer
GPG Key ID: 371BBA77A02C0108

View File

@ -15,7 +15,9 @@ public partial class ContentBlockComponent : MSGComponentBase
private const string HTML_SELF_CLOSING_TAG = "/>"; private const string HTML_SELF_CLOSING_TAG = "/>";
private const string CODE_FENCE_MARKER_BACKTICK = "```"; private const string CODE_FENCE_MARKER_BACKTICK = "```";
private const string CODE_FENCE_MARKER_TILDE = "~~~"; private const string CODE_FENCE_MARKER_TILDE = "~~~";
private const string MATH_BLOCK_MARKER = "$$"; private const string MATH_BLOCK_MARKER_DOLLAR = "$$";
private const string MATH_BLOCK_MARKER_BRACKET_OPEN = """\[""";
private const string MATH_BLOCK_MARKER_BRACKET_CLOSE = """\]""";
private const string HTML_CODE_FENCE_PREFIX = "```html"; private const string HTML_CODE_FENCE_PREFIX = "```html";
private static readonly string[] HTML_TAG_MARKERS = private static readonly string[] HTML_TAG_MARKERS =
@ -223,7 +225,7 @@ public partial class ContentBlockComponent : MSGComponentBase
var normalizedSpan = normalized.AsSpan(); var normalizedSpan = normalized.AsSpan();
var segments = new List<MarkdownRenderSegment>(); var segments = new List<MarkdownRenderSegment>();
var activeCodeFenceMarker = '\0'; var activeCodeFenceMarker = '\0';
var inMathBlock = false; var activeMathBlockFenceType = MathBlockFenceType.NONE;
var markdownSegmentStart = 0; var markdownSegmentStart = 0;
var mathContentStart = 0; var mathContentStart = 0;
@ -244,7 +246,7 @@ public partial class ContentBlockComponent : MSGComponentBase
} }
var trimmedLine = TrimWhitespace(normalizedSpan[lineStart..lineEnd]); var trimmedLine = TrimWhitespace(normalizedSpan[lineStart..lineEnd]);
if (!inMathBlock && TryUpdateCodeFenceState(trimmedLine, ref activeCodeFenceMarker)) if (activeMathBlockFenceType is MathBlockFenceType.NONE && TryUpdateCodeFenceState(trimmedLine, ref activeCodeFenceMarker))
{ {
lineStart = nextLineStart; lineStart = nextLineStart;
continue; continue;
@ -256,28 +258,51 @@ public partial class ContentBlockComponent : MSGComponentBase
continue; continue;
} }
if (trimmedLine.SequenceEqual(MATH_BLOCK_MARKER.AsSpan())) if (activeMathBlockFenceType is MathBlockFenceType.NONE)
{ {
if (inMathBlock) if (trimmedLine.SequenceEqual(MATH_BLOCK_MARKER_DOLLAR.AsSpan()))
{
AddMarkdownSegment(markdownSegmentStart, lineStart);
mathContentStart = nextLineStart;
activeMathBlockFenceType = MathBlockFenceType.DOLLAR;
lineStart = nextLineStart;
continue;
}
if (trimmedLine.SequenceEqual(MATH_BLOCK_MARKER_BRACKET_OPEN.AsSpan()))
{
AddMarkdownSegment(markdownSegmentStart, lineStart);
mathContentStart = nextLineStart;
activeMathBlockFenceType = MathBlockFenceType.BRACKET;
lineStart = nextLineStart;
continue;
}
}
else if (activeMathBlockFenceType is MathBlockFenceType.DOLLAR && trimmedLine.SequenceEqual(MATH_BLOCK_MARKER_DOLLAR.AsSpan()))
{ {
var (start, end) = TrimLineBreaks(normalizedSpan, mathContentStart, lineStart); var (start, end) = TrimLineBreaks(normalizedSpan, mathContentStart, lineStart);
segments.Add(new(MarkdownRenderSegmentType.MATH_BLOCK, start, end - start)); segments.Add(new(MarkdownRenderSegmentType.MATH_BLOCK, start, end - start));
markdownSegmentStart = nextLineStart; markdownSegmentStart = nextLineStart;
inMathBlock = false; activeMathBlockFenceType = MathBlockFenceType.NONE;
lineStart = nextLineStart;
continue;
} }
else else if (activeMathBlockFenceType is MathBlockFenceType.BRACKET && trimmedLine.SequenceEqual(MATH_BLOCK_MARKER_BRACKET_CLOSE.AsSpan()))
{ {
AddMarkdownSegment(markdownSegmentStart, lineStart); var (start, end) = TrimLineBreaks(normalizedSpan, mathContentStart, lineStart);
mathContentStart = nextLineStart; segments.Add(new(MarkdownRenderSegmentType.MATH_BLOCK, start, end - start));
inMathBlock = true;
} markdownSegmentStart = nextLineStart;
activeMathBlockFenceType = MathBlockFenceType.NONE;
lineStart = nextLineStart;
continue;
} }
lineStart = nextLineStart; lineStart = nextLineStart;
} }
if (inMathBlock) if (activeMathBlockFenceType is not MathBlockFenceType.NONE)
return new(normalized, [new(MarkdownRenderSegmentType.MARKDOWN, 0, normalized.Length)]); return new(normalized, [new(MarkdownRenderSegmentType.MARKDOWN, 0, normalized.Length)]);
AddMarkdownSegment(markdownSegmentStart, normalized.Length); AddMarkdownSegment(markdownSegmentStart, normalized.Length);
@ -385,6 +410,13 @@ public partial class ContentBlockComponent : MSGComponentBase
MATH_BLOCK, MATH_BLOCK,
} }
private enum MathBlockFenceType
{
NONE,
DOLLAR,
BRACKET,
}
private sealed record MarkdownRenderPlan(string Source, IReadOnlyList<MarkdownRenderSegment> Segments) private sealed record MarkdownRenderPlan(string Source, IReadOnlyList<MarkdownRenderSegment> Segments)
{ {
public static readonly MarkdownRenderPlan EMPTY = new(string.Empty, []); public static readonly MarkdownRenderPlan EMPTY = new(string.Empty, []);
@ -396,9 +428,9 @@ public partial class ContentBlockComponent : MSGComponentBase
public MarkdownRenderSegmentType Type { get; } = type; public MarkdownRenderSegmentType Type { get; } = type;
public int Start { get; } = start; private int Start { get; } = start;
public int Length { get; } = length; private int Length { get; } = length;
public string GetContent(string source) public string GetContent(string source)
{ {