mirror of
				https://github.com/nsnail/spectre.console.git
				synced 2025-10-31 17:15:28 +08:00 
			
		
		
		
	restructured MarkupTokenizer a bit.
This commit is contained in:
		 Nils Andresen
					Nils Andresen
				
			
				
					committed by
					
						 Patrik Svensson
						Patrik Svensson
					
				
			
			
				
	
			
			
			 Patrik Svensson
						Patrik Svensson
					
				
			
						parent
						
							540bc1307c
						
					
				
				
					commit
					00a9ba325e
				
			| @@ -24,134 +24,154 @@ internal sealed class MarkupTokenizer : IDisposable | ||||
|         } | ||||
|  | ||||
|         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; | ||||
|  | ||||
|             _reader.Read(); | ||||
|  | ||||
|             if (_reader.Eof) | ||||
|             { | ||||
|                 throw new InvalidOperationException($"Encountered malformed markup tag at position {_reader.Position}."); | ||||
|             } | ||||
|  | ||||
|             current = _reader.Peek(); | ||||
|             var current = _reader.Peek(); | ||||
|             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(); | ||||
|                 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) | ||||
|         // 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) | ||||
|             { | ||||
|                 current = _reader.Read(); | ||||
|                 var next = '\0'; | ||||
|                 if (!_reader.Eof) | ||||
|                 { | ||||
|                     next = _reader.Peek(); | ||||
|                 } | ||||
|  | ||||
|                 if (current == ']') | ||||
|                 { | ||||
|                     if (next != ']') | ||||
|                     { | ||||
|                         break; | ||||
|                     } | ||||
|  | ||||
|                     _reader.Read(); | ||||
|                 } | ||||
|  | ||||
|                 builder.Append(current); | ||||
|  | ||||
|                 if (current != '[') | ||||
|                 if (encounteredClosing) | ||||
|                 { | ||||
|                     builder.Append(_reader.Read()); | ||||
|                     encounteredClosing = false; | ||||
|                     continue; | ||||
|                 } | ||||
|  | ||||
|                 if (next == '[') | ||||
|                 { | ||||
|                     _reader.Read(); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     throw new InvalidOperationException( | ||||
|                         $"Encountered malformed markup tag at position {_reader.Position - 1}."); | ||||
|                 } | ||||
|                 _reader.Read(); | ||||
|                 encounteredClosing = true; | ||||
|                 continue; | ||||
|             } | ||||
|  | ||||
|             if (_reader.Eof) | ||||
|             if (current == '[' && !encounteredClosing) | ||||
|             { | ||||
|                 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 builder = new StringBuilder(); | ||||
|  | ||||
|             var encounteredClosing = false; | ||||
|             while (!_reader.Eof) | ||||
|             { | ||||
|                 current = _reader.Peek(); | ||||
|                 if (current == '[') | ||||
|                 if (encounteredOpening) | ||||
|                 { | ||||
|                     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()); | ||||
|                     encounteredOpening = false; | ||||
|                     continue; | ||||
|                 } | ||||
|  | ||||
|                 builder.Append(_reader.Read()); | ||||
|                 _reader.Read(); | ||||
|                 encounteredOpening = true; | ||||
|                 continue; | ||||
|             } | ||||
|  | ||||
|             if (encounteredClosing) | ||||
|             { | ||||
|                 throw new InvalidOperationException($"Encountered unescaped ']' token at position {_reader.Position}."); | ||||
|                 break; | ||||
|             } | ||||
|  | ||||
|             Current = new MarkupToken(MarkupTokenKind.Text, builder.ToString(), position); | ||||
|             return true; | ||||
|             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>() | ||||
|                 .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]"); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user