fix syntax tree parsing

This commit is contained in:
DismissedLight
2024-02-03 17:26:17 +08:00
parent 446bdb2b49
commit 7efaaae3e1
6 changed files with 47 additions and 89 deletions

View File

@@ -46,7 +46,6 @@ internal sealed partial class DescriptionTextBlock : ContentControl
private static void OnDescriptionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
TextBlock textBlock = (TextBlock)((DescriptionTextBlock)d).Content;
ReadOnlySpan<char> description = SpecialNameHandler.Handle((string)e.NewValue);
try
{
@@ -80,9 +79,6 @@ internal sealed partial class DescriptionTextBlock : ContentControl
AppendNode(textBlock, inlines, child);
}
break;
case MiHoYoSyntaxKind.Line:
AppendLine(textBlock, inlines, (MiHoYoLineSyntax)node);
break;
case MiHoYoSyntaxKind.PlainText:
AppendPlainText(textBlock, inlines, (MiHoYoPlainTextSyntax)node);
@@ -96,19 +92,6 @@ internal sealed partial class DescriptionTextBlock : ContentControl
}
}
private static void AppendLine(TextBlock textBlock, InlineCollection inlines, MiHoYoLineSyntax line)
{
foreach (MiHoYoSyntaxNode node in line.Children)
{
AppendNode(textBlock, inlines, node);
}
if (line.HasTailingNewLine)
{
inlines.Add(new LineBreak());
}
}
private static void AppendPlainText(TextBlock textBlock, InlineCollection inlines, MiHoYoPlainTextSyntax plainText)
{
// PlainText doesn't have children
@@ -131,7 +114,7 @@ internal sealed partial class DescriptionTextBlock : ContentControl
targetColor = Rgba32.FromHsl(hsl);
}
if (colorText.Children.Count > 0)
if (colorText.Children.Count > 1)
{
Span span = new()
{
@@ -155,7 +138,7 @@ internal sealed partial class DescriptionTextBlock : ContentControl
private static void AppendItalicText(TextBlock textBlock, InlineCollection inlines, MiHoYoItalicTextSyntax italicText)
{
if (italicText.Children.Count > 0)
if (italicText.Children.Count > 1)
{
Span span = new()
{

View File

@@ -1,17 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Control.Text.Syntax.MiHoYo;
internal sealed class MiHoYoLineSyntax : MiHoYoSyntaxNode
{
public MiHoYoLineSyntax(bool hasTailingNewLine, string text, int start, int end)
: base(MiHoYoSyntaxKind.Line, text, start, end)
{
HasTailingNewLine = hasTailingNewLine;
}
public bool HasTailingNewLine { get; }
public TextPosition TextPosition { get => HasTailingNewLine ? new(Position.Start, Position.Length - 1) : Position; }
}

View File

@@ -9,4 +9,9 @@ internal sealed class MiHoYoPlainTextSyntax : MiHoYoSyntaxNode
: base(MiHoYoSyntaxKind.PlainText, text, start, end)
{
}
public MiHoYoPlainTextSyntax(string text, TextPosition position)
: base(MiHoYoSyntaxKind.PlainText, text, position)
{
}
}

View File

@@ -7,7 +7,6 @@ internal enum MiHoYoSyntaxKind
{
None,
Root,
Line,
PlainText,
ColorText,
ItalicText,

View File

@@ -12,7 +12,7 @@ internal sealed class MiHoYoSyntaxTree
public static MiHoYoSyntaxTree Parse(string text)
{
MiHoYoRootSyntax root = new(text, 0, text.Length);
ParseLines(text, root);
ParseComponents(text, root);
MiHoYoSyntaxTree tree = new()
{
@@ -23,110 +23,93 @@ internal sealed class MiHoYoSyntaxTree
return tree;
}
private static void ParseLines(string text, MiHoYoRootSyntax syntax)
{
ReadOnlySpan<char> textSpan = text.AsSpan();
int previousProcessedIndexOfText = 0;
while (true)
{
int newLineIndexAtSlicedText = textSpan[previousProcessedIndexOfText..].IndexOf('\n');
if (newLineIndexAtSlicedText < 0)
{
MiHoYoLineSyntax line = new(false, text, previousProcessedIndexOfText, textSpan.Length);
ParseComponents(text, line);
syntax.Children.Add(line);
break;
}
MiHoYoLineSyntax lineWithBreaking = new(true, text, previousProcessedIndexOfText, previousProcessedIndexOfText + newLineIndexAtSlicedText + 1);
ParseComponents(text, lineWithBreaking);
syntax.Children.Add(lineWithBreaking);
previousProcessedIndexOfText = lineWithBreaking.Position.End;
}
}
private static void ParseComponents(string text, MiHoYoSyntaxNode syntax)
{
TextPosition contentPosition = syntax switch
{
MiHoYoXmlElementSyntax xmlSyntax => xmlSyntax.ContentPosition,
MiHoYoLineSyntax lineSyntax => lineSyntax.TextPosition,
_ => syntax.Position,
};
ReadOnlySpan<char> contentSpan = text.AsSpan().Slice(contentPosition.Start, contentPosition.Length);
int previousProcessedIndexOfContent = 0;
int endOfProcessedAtContent = 0;
while (true)
{
int fullXmlOpeningIndexOfContent = contentSpan[previousProcessedIndexOfContent..].IndexOf('<');
if (endOfProcessedAtContent >= contentSpan.Length)
{
break;
}
int indexOfXmlLeftOpeningAtUnprocessedContent = contentSpan[endOfProcessedAtContent..].IndexOf('<');
// End of content
if (fullXmlOpeningIndexOfContent < 0)
if (indexOfXmlLeftOpeningAtUnprocessedContent < 0)
{
MiHoYoPlainTextSyntax plainText = new(text, contentPosition.Start + previousProcessedIndexOfContent, contentPosition.End);
TextPosition position = new(contentPosition.Start + endOfProcessedAtContent, contentPosition.End);
MiHoYoPlainTextSyntax plainText = new(text, position);
syntax.Children.Add(plainText);
break;
}
// We have plain text between xml elements
if (previousProcessedIndexOfContent < fullXmlOpeningIndexOfContent)
if (indexOfXmlLeftOpeningAtUnprocessedContent > 0)
{
MiHoYoPlainTextSyntax plainText = new(text, contentPosition.Start + previousProcessedIndexOfContent, contentPosition.End);
TextPosition position = new(0, indexOfXmlLeftOpeningAtUnprocessedContent);
TextPosition positionAtContent = position.RightShift(endOfProcessedAtContent);
TextPosition positionAtText = positionAtContent.RightShift(contentPosition.Start);
MiHoYoPlainTextSyntax plainText = new(text, positionAtText);
syntax.Children.Add(plainText);
endOfProcessedAtContent = positionAtContent.End;
continue;
}
// Peek the next character after '<'
switch (contentSpan[previousProcessedIndexOfContent + fullXmlOpeningIndexOfContent + 1])
int indexOfXmlLeftOpeningAtContent = endOfProcessedAtContent + indexOfXmlLeftOpeningAtUnprocessedContent;
switch (contentSpan[indexOfXmlLeftOpeningAtContent + 1])
{
case 'c':
{
// <color=#FFFFFFFF></color>
// <color=#FFFFFF></color>
int colorTagClosingEndOfSlicedContent = IndexOfClosingEnd(contentSpan[fullXmlOpeningIndexOfContent..], out int colorTagLeftClosingEndOfSlicedContent);
int endOfXmlColorRightClosingAtUnprocessedContent = EndOfXmlClosing(contentSpan[indexOfXmlLeftOpeningAtContent..], out int endOfXmlColorLeftClosingAtUnprocessedContent);
MiHoYoColorKind colorKind = colorTagLeftClosingEndOfSlicedContent switch
MiHoYoColorKind colorKind = endOfXmlColorLeftClosingAtUnprocessedContent switch
{
17 => MiHoYoColorKind.Rgba,
15 => MiHoYoColorKind.Rgb,
_ => throw Must.NeverHappen(),
};
TextPosition positionOfColorElement = new(0, colorTagClosingEndOfSlicedContent);
TextPosition positionAtContent = positionOfColorElement.Add(fullXmlOpeningIndexOfContent);
TextPosition positionAtText = positionAtContent.Add(contentPosition.Start + previousProcessedIndexOfContent);
TextPosition position = new(0, endOfXmlColorRightClosingAtUnprocessedContent);
TextPosition positionAtContent = position.RightShift(endOfProcessedAtContent);
TextPosition positionAtText = positionAtContent.RightShift(contentPosition.Start);
MiHoYoColorTextSyntax colorText = new(colorKind, text, positionAtText);
ParseComponents(text, colorText);
syntax.Children.Add(colorText);
previousProcessedIndexOfContent = positionAtContent.End;
endOfProcessedAtContent = positionAtContent.End;
break;
}
case 'i':
{
// <i>sometext</i> 14
int italicTagClosingEndOfSlicedContent = IndexOfClosingEnd(contentSpan[fullXmlOpeningIndexOfContent..], out _);
int endOfXmlItalicRightClosingAtUnprocessedContent = EndOfXmlClosing(contentSpan[indexOfXmlLeftOpeningAtContent..], out _);
TextPosition positionOfItalicElement = new(0, italicTagClosingEndOfSlicedContent);
TextPosition positionAtContent = positionOfItalicElement.Add(fullXmlOpeningIndexOfContent);
TextPosition positionAtText = positionAtContent.Add(contentPosition.Start + previousProcessedIndexOfContent);
TextPosition position = new(0, endOfXmlItalicRightClosingAtUnprocessedContent);
TextPosition positionAtContent = position.RightShift(endOfProcessedAtContent);
TextPosition positionAtText = positionAtContent.RightShift(contentPosition.Start);
MiHoYoItalicTextSyntax italicText = new(text, positionAtText);
ParseComponents(text, italicText);
syntax.Children.Add(italicText);
previousProcessedIndexOfContent = positionAtContent.End;
endOfProcessedAtContent = positionAtContent.End;
break;
}
}
}
}
private static int IndexOfClosingEnd(in ReadOnlySpan<char> span, out int leftClosingEnd)
private static int EndOfXmlClosing(in ReadOnlySpan<char> span, out int endOfLeftClosing)
{
leftClosingEnd = 0;
endOfLeftClosing = 0;
int openingCount = 0;
int closingCount = 0;
@@ -151,7 +134,7 @@ internal sealed class MiHoYoSyntaxTree
if (openingCount is 1 && closingCount is 0)
{
leftClosingEnd = current;
endOfLeftClosing = current;
}
if (openingCount == closingCount)

View File

@@ -22,7 +22,12 @@ internal readonly struct TextPosition
get => End - Start;
}
public TextPosition Add(int offset)
public TextPosition LeftShift(int offset)
{
return new(Start - offset, End - offset);
}
public TextPosition RightShift(int offset)
{
return new(Start + offset, End + offset);
}