restructured MarkupTokenizer a bit.

This commit is contained in:
Nils Andresen 2022-08-03 22:24:49 +02:00 committed by Patrik Svensson
parent 540bc1307c
commit 00a9ba325e
2 changed files with 123 additions and 90 deletions

View File

@ -24,92 +24,10 @@ internal sealed class MarkupTokenizer : IDisposable
} }
var current = _reader.Peek(); var current = _reader.Peek();
if (current == '[') return current == '[' ? ReadMarkup() : ReadText();
{
var position = _reader.Position;
_reader.Read();
if (_reader.Eof)
{
throw new InvalidOperationException($"Encountered malformed markup tag at position {_reader.Position}.");
} }
current = _reader.Peek(); private bool ReadText()
if (current == '[')
{
_reader.Read();
Current = new MarkupToken(MarkupTokenKind.Text, "[", position);
return true;
}
if (current == '/')
{
_reader.Read();
if (_reader.Eof)
{
throw new InvalidOperationException($"Encountered malformed markup tag at position {_reader.Position}.");
}
current = _reader.Peek();
if (current != ']')
{
throw new InvalidOperationException($"Encountered malformed markup tag at position {_reader.Position}.");
}
_reader.Read();
Current = new MarkupToken(MarkupTokenKind.Close, string.Empty, position);
return true;
}
var builder = new StringBuilder();
while (!_reader.Eof)
{
current = _reader.Read();
var next = '\0';
if (!_reader.Eof)
{
next = _reader.Peek();
}
if (current == ']')
{
if (next != ']')
{
break;
}
_reader.Read();
}
builder.Append(current);
if (current != '[')
{
continue;
}
if (next == '[')
{
_reader.Read();
}
else
{
throw new InvalidOperationException(
$"Encountered malformed markup tag at position {_reader.Position - 1}.");
}
}
if (_reader.Eof)
{
throw new InvalidOperationException($"Encountered malformed markup tag at position {_reader.Position}.");
}
Current = new MarkupToken(MarkupTokenKind.Open, builder.ToString(), position);
return true;
}
else
{ {
var position = _reader.Position; var position = _reader.Position;
var builder = new StringBuilder(); var builder = new StringBuilder();
@ -117,12 +35,15 @@ internal sealed class MarkupTokenizer : IDisposable
var encounteredClosing = false; var encounteredClosing = false;
while (!_reader.Eof) while (!_reader.Eof)
{ {
current = _reader.Peek(); var current = _reader.Peek();
if (current == '[') if (current == '[')
{ {
// markup encountered. Stop processing.
break; break;
} }
else if (current == ']')
// If we find a closing tag (']') there must be two of them.
if (current == ']')
{ {
if (encounteredClosing) if (encounteredClosing)
{ {
@ -153,5 +74,104 @@ internal sealed class MarkupTokenizer : IDisposable
Current = new MarkupToken(MarkupTokenKind.Text, builder.ToString(), position); Current = new MarkupToken(MarkupTokenKind.Text, builder.ToString(), position);
return true; return true;
} }
private bool ReadMarkup()
{
var position = _reader.Position;
_reader.Read();
if (_reader.Eof)
{
throw new InvalidOperationException($"Encountered malformed markup tag at position {_reader.Position}.");
}
var current = _reader.Peek();
switch (current)
{
case '[':
// No markup but instead escaped markup in text.
_reader.Read();
Current = new MarkupToken(MarkupTokenKind.Text, "[", position);
return true;
case '/':
// Markup closed.
_reader.Read();
if (_reader.Eof)
{
throw new InvalidOperationException(
$"Encountered malformed markup tag at position {_reader.Position}.");
}
current = _reader.Peek();
if (current != ']')
{
throw new InvalidOperationException(
$"Encountered malformed markup tag at position {_reader.Position}.");
}
_reader.Read();
Current = new MarkupToken(MarkupTokenKind.Close, string.Empty, position);
return true;
}
// Read the "content" of the markup until we find the end-of-markup
var builder = new StringBuilder();
var encounteredOpening = false;
var encounteredClosing = false;
while (!_reader.Eof)
{
current = _reader.Peek();
if (current == ']' && !encounteredOpening)
{
if (encounteredClosing)
{
builder.Append(_reader.Read());
encounteredClosing = false;
continue;
}
_reader.Read();
encounteredClosing = true;
continue;
}
if (current == '[' && !encounteredClosing)
{
if (encounteredOpening)
{
builder.Append(_reader.Read());
encounteredOpening = false;
continue;
}
_reader.Read();
encounteredOpening = true;
continue;
}
if (encounteredClosing)
{
break;
}
if (encounteredOpening)
{
throw new InvalidOperationException(
$"Encountered malformed markup tag at position {_reader.Position - 1}.");
}
builder.Append(_reader.Read());
}
if (_reader.Eof)
{
throw new InvalidOperationException($"Encountered malformed markup tag at position {_reader.Position}.");
}
Current = new MarkupToken(MarkupTokenKind.Open, builder.ToString(), position);
return true;
} }
} }

View File

@ -141,5 +141,18 @@ public partial class AnsiConsoleTests
result.ShouldBeOfType<InvalidOperationException>() result.ShouldBeOfType<InvalidOperationException>()
.Message.ShouldBe("Encountered closing tag when none was expected near position 5."); .Message.ShouldBe("Encountered closing tag when none was expected near position 5.");
} }
[Fact]
public void Should_Not_Get_Confused_When_Mixing_Escaped_And_Unescaped()
{
// Given
var console = new TestConsole();
// When
console.Markup("[grey][[grey]][/][white][[white]][/]");
// Then
console.Output.ShouldBe("[grey][white]");
}
} }
} }