mirror of
				https://github.com/nsnail/spectre.console.git
				synced 2025-10-31 09:09:25 +08:00 
			
		
		
		
	Add method to write VT/ANSI control codes
Adds a new extension method IAnsiConsole.WriteAnsi that writes VT/ANSI control codes to the console. If VT/ANSI control codes are not supported, nothing will be written.
This commit is contained in:
		 Patrik Svensson
					Patrik Svensson
				
			
				
					committed by
					
						 Phil Scott
						Phil Scott
					
				
			
			
				
	
			
			
			 Phil Scott
						Phil Scott
					
				
			
						parent
						
							91f910326c
						
					
				
				
					commit
					3463dde543
				
			| @@ -17,6 +17,18 @@ namespace Spectre.Console.Testing | ||||
|             return console; | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Sets whether or not ANSI is supported. | ||||
|         /// </summary> | ||||
|         /// <param name="console">The console.</param> | ||||
|         /// <param name="enable">Whether or not VT/ANSI control codes are supported.</param> | ||||
|         /// <returns>The same instance so that multiple calls can be chained.</returns> | ||||
|         public static TestConsole SupportsAnsi(this TestConsole console, bool enable) | ||||
|         { | ||||
|             console.Profile.Capabilities.Ansi = enable; | ||||
|             return console; | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Makes the console interactive. | ||||
|         /// </summary> | ||||
|   | ||||
							
								
								
									
										47
									
								
								src/Spectre.Console.Tests/Unit/AnsiConsoleTests.Advanced.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								src/Spectre.Console.Tests/Unit/AnsiConsoleTests.Advanced.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,47 @@ | ||||
| using Shouldly; | ||||
| using Spectre.Console.Advanced; | ||||
| using Spectre.Console.Testing; | ||||
| using Xunit; | ||||
|  | ||||
| namespace Spectre.Console.Tests.Unit | ||||
| { | ||||
|     public partial class AnsiConsoleTests | ||||
|     { | ||||
|         public sealed class Advanced | ||||
|         { | ||||
|             [Fact] | ||||
|             public void Should_Write_Ansi_Codes_To_Console_If_Supported() | ||||
|             { | ||||
|                 // Given | ||||
|                 var console = new TestConsole() | ||||
|                     .SupportsAnsi(true) | ||||
|                     .Colors(ColorSystem.Standard) | ||||
|                     .EmitAnsiSequences(); | ||||
|  | ||||
|                 // When | ||||
|                 console.WriteAnsi("[101mHello[0m"); | ||||
|  | ||||
|                 // Then | ||||
|                 console.Output.NormalizeLineEndings() | ||||
|                     .ShouldBe("[101mHello[0m"); | ||||
|             } | ||||
|  | ||||
|             [Fact] | ||||
|             public void Should_Not_Write_Ansi_Codes_To_Console_If_Not_Supported() | ||||
|             { | ||||
|                 // Given | ||||
|                 var console = new TestConsole() | ||||
|                     .SupportsAnsi(false) | ||||
|                     .Colors(ColorSystem.Standard) | ||||
|                     .EmitAnsiSequences(); | ||||
|  | ||||
|                 // When | ||||
|                 console.WriteAnsi("[101mHello[0m"); | ||||
|  | ||||
|                 // Then | ||||
|                 console.Output.NormalizeLineEndings() | ||||
|                     .ShouldBeEmpty(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -7,103 +7,109 @@ namespace Spectre.Console.Tests.Unit | ||||
| { | ||||
|     public partial class AnsiConsoleTests | ||||
|     { | ||||
|         [Theory] | ||||
|         [InlineData(false, "Hello[2J[3JWorld")] | ||||
|         [InlineData(true, "Hello[2J[3J[1;1HWorld")] | ||||
|         public void Should_Clear_Screen(bool home, string expected) | ||||
|         public sealed class Clear | ||||
|         { | ||||
|             // Given | ||||
|             var console = new TestConsole() | ||||
|                 .Colors(ColorSystem.Standard) | ||||
|                 .EmitAnsiSequences(); | ||||
|             [Theory] | ||||
|             [InlineData(false, "Hello[2J[3JWorld")] | ||||
|             [InlineData(true, "Hello[2J[3J[1;1HWorld")] | ||||
|             public void Should_Clear_Screen(bool home, string expected) | ||||
|             { | ||||
|                 // Given | ||||
|                 var console = new TestConsole() | ||||
|                     .Colors(ColorSystem.Standard) | ||||
|                     .EmitAnsiSequences(); | ||||
|  | ||||
|             // When | ||||
|             console.Write("Hello"); | ||||
|             console.Clear(home); | ||||
|             console.Write("World"); | ||||
|                 // When | ||||
|                 console.Write("Hello"); | ||||
|                 console.Clear(home); | ||||
|                 console.Write("World"); | ||||
|  | ||||
|             // Then | ||||
|             console.Output.ShouldBe(expected); | ||||
|                 // Then | ||||
|                 console.Output.ShouldBe(expected); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         [Fact] | ||||
|         public void Should_Combine_Decoration_And_Colors() | ||||
|         public sealed class Write | ||||
|         { | ||||
|             // Given | ||||
|             var console = new TestConsole() | ||||
|                 .Colors(ColorSystem.Standard) | ||||
|                 .EmitAnsiSequences(); | ||||
|             [Fact] | ||||
|             public void Should_Combine_Decoration_And_Colors() | ||||
|             { | ||||
|                 // Given | ||||
|                 var console = new TestConsole() | ||||
|                     .Colors(ColorSystem.Standard) | ||||
|                     .EmitAnsiSequences(); | ||||
|  | ||||
|             // When | ||||
|             console.Write( | ||||
|                 "Hello", | ||||
|                 new Style() | ||||
|                     .Foreground(Color.RoyalBlue1) | ||||
|                     .Background(Color.NavajoWhite1) | ||||
|                     .Decoration(Decoration.Italic)); | ||||
|                 // When | ||||
|                 console.Write( | ||||
|                     "Hello", | ||||
|                     new Style() | ||||
|                         .Foreground(Color.RoyalBlue1) | ||||
|                         .Background(Color.NavajoWhite1) | ||||
|                         .Decoration(Decoration.Italic)); | ||||
|  | ||||
|             // Then | ||||
|             console.Output.ShouldBe("\u001b[3;90;47mHello\u001b[0m"); | ||||
|         } | ||||
|                 // Then | ||||
|                 console.Output.ShouldBe("\u001b[3;90;47mHello\u001b[0m"); | ||||
|             } | ||||
|  | ||||
|         [Fact] | ||||
|         public void Should_Not_Include_Foreground_If_Set_To_Default_Color() | ||||
|         { | ||||
|             // Given | ||||
|             var console = new TestConsole() | ||||
|                 .Colors(ColorSystem.Standard) | ||||
|                 .EmitAnsiSequences(); | ||||
|             [Fact] | ||||
|             public void Should_Not_Include_Foreground_If_Set_To_Default_Color() | ||||
|             { | ||||
|                 // Given | ||||
|                 var console = new TestConsole() | ||||
|                     .Colors(ColorSystem.Standard) | ||||
|                     .EmitAnsiSequences(); | ||||
|  | ||||
|             // When | ||||
|             console.Write( | ||||
|                 "Hello", | ||||
|                 new Style() | ||||
|                     .Foreground(Color.Default) | ||||
|                     .Background(Color.NavajoWhite1) | ||||
|                     .Decoration(Decoration.Italic)); | ||||
|                 // When | ||||
|                 console.Write( | ||||
|                     "Hello", | ||||
|                     new Style() | ||||
|                         .Foreground(Color.Default) | ||||
|                         .Background(Color.NavajoWhite1) | ||||
|                         .Decoration(Decoration.Italic)); | ||||
|  | ||||
|             // Then | ||||
|             console.Output.ShouldBe("\u001b[3;47mHello\u001b[0m"); | ||||
|         } | ||||
|                 // Then | ||||
|                 console.Output.ShouldBe("\u001b[3;47mHello\u001b[0m"); | ||||
|             } | ||||
|  | ||||
|         [Fact] | ||||
|         public void Should_Not_Include_Background_If_Set_To_Default_Color() | ||||
|         { | ||||
|             // Given | ||||
|             var console = new TestConsole() | ||||
|                 .Colors(ColorSystem.Standard) | ||||
|                 .EmitAnsiSequences(); | ||||
|             [Fact] | ||||
|             public void Should_Not_Include_Background_If_Set_To_Default_Color() | ||||
|             { | ||||
|                 // Given | ||||
|                 var console = new TestConsole() | ||||
|                     .Colors(ColorSystem.Standard) | ||||
|                     .EmitAnsiSequences(); | ||||
|  | ||||
|             // When | ||||
|             console.Write( | ||||
|                 "Hello", | ||||
|                 new Style() | ||||
|                     .Foreground(Color.RoyalBlue1) | ||||
|                     .Background(Color.Default) | ||||
|                     .Decoration(Decoration.Italic)); | ||||
|                 // When | ||||
|                 console.Write( | ||||
|                     "Hello", | ||||
|                     new Style() | ||||
|                         .Foreground(Color.RoyalBlue1) | ||||
|                         .Background(Color.Default) | ||||
|                         .Decoration(Decoration.Italic)); | ||||
|  | ||||
|             // Then | ||||
|             console.Output.ShouldBe("\u001b[3;90mHello\u001b[0m"); | ||||
|         } | ||||
|                 // Then | ||||
|                 console.Output.ShouldBe("\u001b[3;90mHello\u001b[0m"); | ||||
|             } | ||||
|  | ||||
|         [Fact] | ||||
|         public void Should_Not_Include_Decoration_If_Set_To_None() | ||||
|         { | ||||
|             // Given | ||||
|             var console = new TestConsole() | ||||
|                 .Colors(ColorSystem.Standard) | ||||
|                 .EmitAnsiSequences(); | ||||
|             [Fact] | ||||
|             public void Should_Not_Include_Decoration_If_Set_To_None() | ||||
|             { | ||||
|                 // Given | ||||
|                 var console = new TestConsole() | ||||
|                     .Colors(ColorSystem.Standard) | ||||
|                     .EmitAnsiSequences(); | ||||
|  | ||||
|             // When | ||||
|             console.Write( | ||||
|                 "Hello", | ||||
|                 new Style() | ||||
|                     .Foreground(Color.RoyalBlue1) | ||||
|                     .Background(Color.NavajoWhite1) | ||||
|                     .Decoration(Decoration.None)); | ||||
|                 // When | ||||
|                 console.Write( | ||||
|                     "Hello", | ||||
|                     new Style() | ||||
|                         .Foreground(Color.RoyalBlue1) | ||||
|                         .Background(Color.NavajoWhite1) | ||||
|                         .Decoration(Decoration.None)); | ||||
|  | ||||
|             // Then | ||||
|             console.Output.ShouldBe("\u001b[90;47mHello\u001b[0m"); | ||||
|                 // Then | ||||
|                 console.Output.ShouldBe("\u001b[90;47mHello\u001b[0m"); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         public sealed class WriteLine | ||||
|   | ||||
| @@ -16,7 +16,7 @@ namespace Spectre.Console | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Gets or sets a value indicating whether or not | ||||
|         /// the console supports Ansi. | ||||
|         /// the console supports VT/ANSI control codes. | ||||
|         /// </summary> | ||||
|         public bool Ansi { get; set; } | ||||
|  | ||||
|   | ||||
| @@ -0,0 +1,28 @@ | ||||
| using System; | ||||
|  | ||||
| namespace Spectre.Console.Advanced | ||||
| { | ||||
|     /// <summary> | ||||
|     /// Contains extension methods for <see cref="IAnsiConsole"/>. | ||||
|     /// </summary> | ||||
|     public static class AnsiConsoleExtensions | ||||
|     { | ||||
|         /// <summary> | ||||
|         /// Writes a VT/Ansi control code sequence to the console (if supported). | ||||
|         /// </summary> | ||||
|         /// <param name="console">The console to write to.</param> | ||||
|         /// <param name="sequence">The VT/Ansi control code sequence to write.</param> | ||||
|         public static void WriteAnsi(this IAnsiConsole console, string sequence) | ||||
|         { | ||||
|             if (console is null) | ||||
|             { | ||||
|                 throw new ArgumentNullException(nameof(console)); | ||||
|             } | ||||
|  | ||||
|             if (console.Profile.Capabilities.Ansi) | ||||
|             { | ||||
|                 console.Write(new ControlCode(sequence)); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -38,6 +38,11 @@ namespace Spectre.Console | ||||
|         /// <param name="text">The text to write.</param> | ||||
|         public static void Write(this IAnsiConsole console, string text) | ||||
|         { | ||||
|             if (console is null) | ||||
|             { | ||||
|                 throw new ArgumentNullException(nameof(console)); | ||||
|             } | ||||
|  | ||||
|             console.Write(new Text(text, Style.Plain)); | ||||
|         } | ||||
|  | ||||
| @@ -49,6 +54,11 @@ namespace Spectre.Console | ||||
|         /// <param name="style">The text style.</param> | ||||
|         public static void Write(this IAnsiConsole console, string text, Style style) | ||||
|         { | ||||
|             if (console is null) | ||||
|             { | ||||
|                 throw new ArgumentNullException(nameof(console)); | ||||
|             } | ||||
|  | ||||
|             console.Write(new Text(text, style)); | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -14,7 +14,7 @@ namespace Spectre.Console | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Gets a value indicating whether or not | ||||
|         /// the console supports Ansi. | ||||
|         /// the console supports VT/ANSI control codes. | ||||
|         /// </summary> | ||||
|         bool Ansi { get; } | ||||
|  | ||||
|   | ||||
| @@ -22,12 +22,12 @@ namespace Spectre.Console | ||||
|  | ||||
|         public void Clear(bool home) | ||||
|         { | ||||
|             Write(new ControlSequence(ED(2))); | ||||
|             Write(new ControlSequence(ED(3))); | ||||
|             Write(new ControlCode(ED(2))); | ||||
|             Write(new ControlCode(ED(3))); | ||||
|  | ||||
|             if (home) | ||||
|             { | ||||
|                 Write(new ControlSequence(CUP(1, 1))); | ||||
|                 Write(new ControlCode(CUP(1, 1))); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -16,11 +16,11 @@ namespace Spectre.Console | ||||
|         { | ||||
|             if (show) | ||||
|             { | ||||
|                 _backend.Write(new ControlSequence(SM(DECTCEM))); | ||||
|                 _backend.Write(new ControlCode(SM(DECTCEM))); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 _backend.Write(new ControlSequence(RM(DECTCEM))); | ||||
|                 _backend.Write(new ControlCode(RM(DECTCEM))); | ||||
|             } | ||||
|         } | ||||
|  | ||||
| @@ -34,23 +34,23 @@ namespace Spectre.Console | ||||
|             switch (direction) | ||||
|             { | ||||
|                 case CursorDirection.Up: | ||||
|                     _backend.Write(new ControlSequence(CUU(steps))); | ||||
|                     _backend.Write(new ControlCode(CUU(steps))); | ||||
|                     break; | ||||
|                 case CursorDirection.Down: | ||||
|                     _backend.Write(new ControlSequence(CUD(steps))); | ||||
|                     _backend.Write(new ControlCode(CUD(steps))); | ||||
|                     break; | ||||
|                 case CursorDirection.Right: | ||||
|                     _backend.Write(new ControlSequence(CUF(steps))); | ||||
|                     _backend.Write(new ControlCode(CUF(steps))); | ||||
|                     break; | ||||
|                 case CursorDirection.Left: | ||||
|                     _backend.Write(new ControlSequence(CUB(steps))); | ||||
|                     _backend.Write(new ControlCode(CUB(steps))); | ||||
|                     break; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         public void SetPosition(int column, int line) | ||||
|         { | ||||
|             _backend.Write(new ControlSequence(CUP(line, column))); | ||||
|             _backend.Write(new ControlCode(CUP(line, column))); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -25,11 +25,11 @@ namespace Spectre.Console.Rendering | ||||
|             { | ||||
|                 if (_shape == null) | ||||
|                 { | ||||
|                     return new ControlSequence(string.Empty); | ||||
|                     return new ControlCode(string.Empty); | ||||
|                 } | ||||
|  | ||||
|                 var linesToMoveUp = _shape.Value.Height - 1; | ||||
|                 return new ControlSequence("\r" + CUU(linesToMoveUp)); | ||||
|                 return new ControlCode("\r" + CUU(linesToMoveUp)); | ||||
|             } | ||||
|         } | ||||
|  | ||||
| @@ -39,11 +39,11 @@ namespace Spectre.Console.Rendering | ||||
|             { | ||||
|                 if (_shape == null) | ||||
|                 { | ||||
|                     return new ControlSequence(string.Empty); | ||||
|                     return new ControlCode(string.Empty); | ||||
|                 } | ||||
|  | ||||
|                 var linesToClear = _shape.Value.Height - 1; | ||||
|                 return new ControlSequence("\r" + EL(2) + (CUU(1) + EL(2)).Repeat(linesToClear)); | ||||
|                 return new ControlCode("\r" + EL(2) + (CUU(1) + EL(2)).Repeat(linesToClear)); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -14,6 +14,11 @@ namespace Spectre.Console.Rendering | ||||
|         /// </summary> | ||||
|         public ColorSystem ColorSystem => _capabilities.ColorSystem; | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Gets a value indicating whether or not VT/Ansi codes are supported. | ||||
|         /// </summary> | ||||
|         public bool Ansi => _capabilities.Ansi; | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Gets a value indicating whether or not unicode is supported. | ||||
|         /// </summary> | ||||
|   | ||||
| @@ -3,11 +3,11 @@ using Spectre.Console.Rendering; | ||||
| 
 | ||||
| namespace Spectre.Console | ||||
| { | ||||
|     internal sealed class ControlSequence : Renderable | ||||
|     internal sealed class ControlCode : Renderable | ||||
|     { | ||||
|         private readonly Segment _segment; | ||||
| 
 | ||||
|         public ControlSequence(string control) | ||||
|         public ControlCode(string control) | ||||
|         { | ||||
|             _segment = Segment.Control(control); | ||||
|         } | ||||
| @@ -19,7 +19,10 @@ namespace Spectre.Console | ||||
| 
 | ||||
|         protected override IEnumerable<Segment> Render(RenderContext context, int maxWidth) | ||||
|         { | ||||
|             yield return _segment; | ||||
|             if (context.Ansi) | ||||
|             { | ||||
|                 yield return _segment; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -73,7 +73,7 @@ namespace Spectre.Console | ||||
|         public void Refresh() | ||||
|         { | ||||
|             _renderer.Update(this); | ||||
|             _console.Write(new ControlSequence(string.Empty)); | ||||
|             _console.Write(new ControlCode(string.Empty)); | ||||
|         } | ||||
|  | ||||
|         internal IReadOnlyList<ProgressTask> GetTasks() | ||||
|   | ||||
| @@ -38,7 +38,7 @@ namespace Spectre.Console | ||||
|  | ||||
|         public void Redraw() | ||||
|         { | ||||
|             _console.Write(new ControlSequence(string.Empty)); | ||||
|             _console.Write(new ControlCode(string.Empty)); | ||||
|         } | ||||
|  | ||||
|         public bool Update(ConsoleKey key) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user