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,134 +24,154 @@ internal sealed class MarkupTokenizer : IDisposable
} }
var current = _reader.Peek(); var current = _reader.Peek();
if (current == '[') return current == '[' ? ReadMarkup() : ReadText();
}
private bool ReadText()
{
var position = _reader.Position;
var builder = new StringBuilder();
var encounteredClosing = false;
while (!_reader.Eof)
{ {
var position = _reader.Position; var current = _reader.Peek();
_reader.Read();
if (_reader.Eof)
{
throw new InvalidOperationException($"Encountered malformed markup tag at position {_reader.Position}.");
}
current = _reader.Peek();
if (current == '[') if (current == '[')
{ {
// markup encountered. Stop processing.
break;
}
// If we find a closing tag (']') there must be two of them.
if (current == ']')
{
if (encounteredClosing)
{
_reader.Read();
encounteredClosing = false;
continue;
}
encounteredClosing = true;
}
else
{
if (encounteredClosing)
{
throw new InvalidOperationException(
$"Encountered unescaped ']' token at position {_reader.Position}.");
}
}
builder.Append(_reader.Read());
}
if (encounteredClosing)
{
throw new InvalidOperationException($"Encountered unescaped ']' token at position {_reader.Position}.");
}
Current = new MarkupToken(MarkupTokenKind.Text, builder.ToString(), position);
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(); _reader.Read();
Current = new MarkupToken(MarkupTokenKind.Text, "[", position); Current = new MarkupToken(MarkupTokenKind.Text, "[", position);
return true; return true;
} case '/':
// Markup closed.
if (current == '/')
{
_reader.Read(); _reader.Read();
if (_reader.Eof) if (_reader.Eof)
{ {
throw new InvalidOperationException($"Encountered malformed markup tag at position {_reader.Position}."); throw new InvalidOperationException(
$"Encountered malformed markup tag at position {_reader.Position}.");
} }
current = _reader.Peek(); current = _reader.Peek();
if (current != ']') if (current != ']')
{ {
throw new InvalidOperationException($"Encountered malformed markup tag at position {_reader.Position}."); throw new InvalidOperationException(
$"Encountered malformed markup tag at position {_reader.Position}.");
} }
_reader.Read(); _reader.Read();
Current = new MarkupToken(MarkupTokenKind.Close, string.Empty, position); Current = new MarkupToken(MarkupTokenKind.Close, string.Empty, position);
return true; return true;
} }
var builder = new StringBuilder(); // Read the "content" of the markup until we find the end-of-markup
while (!_reader.Eof) var builder = new StringBuilder();
var encounteredOpening = false;
var encounteredClosing = false;
while (!_reader.Eof)
{
current = _reader.Peek();
if (current == ']' && !encounteredOpening)
{ {
current = _reader.Read(); if (encounteredClosing)
var next = '\0';
if (!_reader.Eof)
{
next = _reader.Peek();
}
if (current == ']')
{
if (next != ']')
{
break;
}
_reader.Read();
}
builder.Append(current);
if (current != '[')
{ {
builder.Append(_reader.Read());
encounteredClosing = false;
continue; continue;
} }
if (next == '[') _reader.Read();
{ encounteredClosing = true;
_reader.Read(); continue;
}
else
{
throw new InvalidOperationException(
$"Encountered malformed markup tag at position {_reader.Position - 1}.");
}
} }
if (_reader.Eof) if (current == '[' && !encounteredClosing)
{ {
throw new InvalidOperationException($"Encountered malformed markup tag at position {_reader.Position}."); if (encounteredOpening)
}
Current = new MarkupToken(MarkupTokenKind.Open, builder.ToString(), position);
return true;
}
else
{
var position = _reader.Position;
var builder = new StringBuilder();
var encounteredClosing = false;
while (!_reader.Eof)
{
current = _reader.Peek();
if (current == '[')
{ {
break; builder.Append(_reader.Read());
} encounteredOpening = false;
else if (current == ']') continue;
{
if (encounteredClosing)
{
_reader.Read();
encounteredClosing = false;
continue;
}
encounteredClosing = true;
}
else
{
if (encounteredClosing)
{
throw new InvalidOperationException(
$"Encountered unescaped ']' token at position {_reader.Position}.");
}
} }
builder.Append(_reader.Read()); _reader.Read();
encounteredOpening = true;
continue;
} }
if (encounteredClosing) if (encounteredClosing)
{ {
throw new InvalidOperationException($"Encountered unescaped ']' token at position {_reader.Position}."); break;
} }
Current = new MarkupToken(MarkupTokenKind.Text, builder.ToString(), position); if (encounteredOpening)
return true; {
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]");
}
} }
} }