mirror of
https://github.com/nsnail/spectre.console.git
synced 2025-04-16 17:02:51 +08:00
restructured MarkupTokenizer a bit.
This commit is contained in:
parent
540bc1307c
commit
00a9ba325e
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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]");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user