Skip to content

Commit

Permalink
restructured MarkupTokenizer a bit.
Browse files Browse the repository at this point in the history
  • Loading branch information
nils-a authored and patriksvensson committed Aug 29, 2022
1 parent 540bc13 commit 00a9ba3
Show file tree
Hide file tree
Showing 2 changed files with 119 additions and 86 deletions.
192 changes: 106 additions & 86 deletions src/Spectre.Console/Internal/Text/Markup/MarkupTokenizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,134 +24,154 @@ public bool MoveNext()
}

var current = _reader.Peek();
if (current == '[')
{
var position = _reader.Position;
return current == '[' ? ReadMarkup() : ReadText();
}

_reader.Read();
private bool ReadText()
{
var position = _reader.Position;
var builder = new StringBuilder();

if (_reader.Eof)
var encounteredClosing = false;
while (!_reader.Eof)
{
var current = _reader.Peek();
if (current == '[')
{
throw new InvalidOperationException($"Encountered malformed markup tag at position {_reader.Position}.");
// markup encountered. Stop processing.
break;
}

current = _reader.Peek();
if (current == '[')
// 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();
Current = new MarkupToken(MarkupTokenKind.Text, "[", position);
return true;
}

if (current == '/')
{
case '/':
// Markup closed.
_reader.Read();

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();
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();
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();
}
// 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 == ']')
if (current == ']' && !encounteredOpening)
{
if (encounteredClosing)
{
if (next != ']')
{
break;
}

_reader.Read();
builder.Append(_reader.Read());
encounteredClosing = false;
continue;
}

builder.Append(current);
_reader.Read();
encounteredClosing = true;
continue;
}

if (current != '[')
if (current == '[' && !encounteredClosing)
{
if (encounteredOpening)
{
builder.Append(_reader.Read());
encounteredOpening = false;
continue;
}

if (next == '[')
{
_reader.Read();
}
else
{
throw new InvalidOperationException(
$"Encountered malformed markup tag at position {_reader.Position - 1}.");
}
_reader.Read();
encounteredOpening = true;
continue;
}

if (_reader.Eof)
if (encounteredClosing)
{
throw new InvalidOperationException($"Encountered malformed markup tag at position {_reader.Position}.");
break;
}

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)
if (encounteredOpening)
{
current = _reader.Peek();
if (current == '[')
{
break;
}
else 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());
throw new InvalidOperationException(
$"Encountered malformed markup tag at position {_reader.Position - 1}.");
}

if (encounteredClosing)
{
throw new InvalidOperationException($"Encountered unescaped ']' token at position {_reader.Position}.");
}
builder.Append(_reader.Read());
}

Current = new MarkupToken(MarkupTokenKind.Text, builder.ToString(), position);
return true;
if (_reader.Eof)
{
throw new InvalidOperationException($"Encountered malformed markup tag at position {_reader.Position}.");
}

Current = new MarkupToken(MarkupTokenKind.Open, builder.ToString(), position);
return true;
}
}
13 changes: 13 additions & 0 deletions test/Spectre.Console.Tests/Unit/AnsiConsoleTests.Markup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -141,5 +141,18 @@ public void Should_Throw_If_Encounters_Closing_Tag()
result.ShouldBeOfType<InvalidOperationException>()
.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]");
}
}
}

0 comments on commit 00a9ba3

Please sign in to comment.