mirror of
				https://github.com/nsnail/spectre.console.git
				synced 2025-11-04 10:35:27 +08:00 
			
		
		
		
	Use file scoped namespace declarations
This commit is contained in:
		
				
					committed by
					
						
						Phil Scott
					
				
			
			
				
	
			
			
			
						parent
						
							1dbaf50935
						
					
				
				
					commit
					ec1188b837
				
			@@ -4,108 +4,107 @@ using System.Text;
 | 
			
		||||
using Spectre.Console.Rendering;
 | 
			
		||||
using static Spectre.Console.AnsiSequences;
 | 
			
		||||
 | 
			
		||||
namespace Spectre.Console
 | 
			
		||||
namespace Spectre.Console;
 | 
			
		||||
 | 
			
		||||
internal static class AnsiBuilder
 | 
			
		||||
{
 | 
			
		||||
    internal static class AnsiBuilder
 | 
			
		||||
    private static readonly AnsiLinkHasher _linkHasher;
 | 
			
		||||
 | 
			
		||||
    static AnsiBuilder()
 | 
			
		||||
    {
 | 
			
		||||
        private static readonly AnsiLinkHasher _linkHasher;
 | 
			
		||||
 | 
			
		||||
        static AnsiBuilder()
 | 
			
		||||
        {
 | 
			
		||||
            _linkHasher = new AnsiLinkHasher();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static string Build(IAnsiConsole console, IRenderable renderable)
 | 
			
		||||
        {
 | 
			
		||||
            var builder = new StringBuilder();
 | 
			
		||||
            foreach (var segment in renderable.GetSegments(console))
 | 
			
		||||
            {
 | 
			
		||||
                if (segment.IsControlCode)
 | 
			
		||||
                {
 | 
			
		||||
                    builder.Append(segment.Text);
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                var parts = segment.Text.NormalizeNewLines().Split(new[] { '\n' });
 | 
			
		||||
                foreach (var (_, _, last, part) in parts.Enumerate())
 | 
			
		||||
                {
 | 
			
		||||
                    if (!string.IsNullOrEmpty(part))
 | 
			
		||||
                    {
 | 
			
		||||
                        builder.Append(Build(console.Profile, part, segment.Style));
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    if (!last)
 | 
			
		||||
                    {
 | 
			
		||||
                        builder.Append(Environment.NewLine);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return builder.ToString();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private static string Build(Profile profile, string text, Style style)
 | 
			
		||||
        {
 | 
			
		||||
            if (profile is null)
 | 
			
		||||
            {
 | 
			
		||||
                throw new ArgumentNullException(nameof(profile));
 | 
			
		||||
            }
 | 
			
		||||
            else if (text is null)
 | 
			
		||||
            {
 | 
			
		||||
                throw new ArgumentNullException(nameof(text));
 | 
			
		||||
            }
 | 
			
		||||
            else if (style is null)
 | 
			
		||||
            {
 | 
			
		||||
                throw new ArgumentNullException(nameof(style));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            var codes = AnsiDecorationBuilder.GetAnsiCodes(style.Decoration);
 | 
			
		||||
 | 
			
		||||
            // Got foreground?
 | 
			
		||||
            if (style.Foreground != Color.Default)
 | 
			
		||||
            {
 | 
			
		||||
                codes = codes.Concat(
 | 
			
		||||
                    AnsiColorBuilder.GetAnsiCodes(
 | 
			
		||||
                        profile.Capabilities.ColorSystem,
 | 
			
		||||
                        style.Foreground,
 | 
			
		||||
                        true));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Got background?
 | 
			
		||||
            if (style.Background != Color.Default)
 | 
			
		||||
            {
 | 
			
		||||
                codes = codes.Concat(
 | 
			
		||||
                    AnsiColorBuilder.GetAnsiCodes(
 | 
			
		||||
                        profile.Capabilities.ColorSystem,
 | 
			
		||||
                        style.Background,
 | 
			
		||||
                        false));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            var result = codes.ToArray();
 | 
			
		||||
            if (result.Length == 0 && style.Link == null)
 | 
			
		||||
            {
 | 
			
		||||
                return text;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            var ansi = result.Length > 0
 | 
			
		||||
                ? $"{SGR(result)}{text}{SGR(0)}"
 | 
			
		||||
                : text;
 | 
			
		||||
 | 
			
		||||
            if (style.Link != null && !profile.Capabilities.Legacy)
 | 
			
		||||
            {
 | 
			
		||||
                var link = style.Link;
 | 
			
		||||
 | 
			
		||||
                // Empty links means we should take the URL from the text.
 | 
			
		||||
                if (link.Equals(Constants.EmptyLink, StringComparison.Ordinal))
 | 
			
		||||
                {
 | 
			
		||||
                    link = text;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                var linkId = _linkHasher.GenerateId(link, text);
 | 
			
		||||
                ansi = $"{ESC}]8;id={linkId};{link}{ESC}\\{ansi}{ESC}]8;;{ESC}\\";
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return ansi;
 | 
			
		||||
        }
 | 
			
		||||
        _linkHasher = new AnsiLinkHasher();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    public static string Build(IAnsiConsole console, IRenderable renderable)
 | 
			
		||||
    {
 | 
			
		||||
        var builder = new StringBuilder();
 | 
			
		||||
        foreach (var segment in renderable.GetSegments(console))
 | 
			
		||||
        {
 | 
			
		||||
            if (segment.IsControlCode)
 | 
			
		||||
            {
 | 
			
		||||
                builder.Append(segment.Text);
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            var parts = segment.Text.NormalizeNewLines().Split(new[] { '\n' });
 | 
			
		||||
            foreach (var (_, _, last, part) in parts.Enumerate())
 | 
			
		||||
            {
 | 
			
		||||
                if (!string.IsNullOrEmpty(part))
 | 
			
		||||
                {
 | 
			
		||||
                    builder.Append(Build(console.Profile, part, segment.Style));
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (!last)
 | 
			
		||||
                {
 | 
			
		||||
                    builder.Append(Environment.NewLine);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return builder.ToString();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static string Build(Profile profile, string text, Style style)
 | 
			
		||||
    {
 | 
			
		||||
        if (profile is null)
 | 
			
		||||
        {
 | 
			
		||||
            throw new ArgumentNullException(nameof(profile));
 | 
			
		||||
        }
 | 
			
		||||
        else if (text is null)
 | 
			
		||||
        {
 | 
			
		||||
            throw new ArgumentNullException(nameof(text));
 | 
			
		||||
        }
 | 
			
		||||
        else if (style is null)
 | 
			
		||||
        {
 | 
			
		||||
            throw new ArgumentNullException(nameof(style));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var codes = AnsiDecorationBuilder.GetAnsiCodes(style.Decoration);
 | 
			
		||||
 | 
			
		||||
        // Got foreground?
 | 
			
		||||
        if (style.Foreground != Color.Default)
 | 
			
		||||
        {
 | 
			
		||||
            codes = codes.Concat(
 | 
			
		||||
                AnsiColorBuilder.GetAnsiCodes(
 | 
			
		||||
                    profile.Capabilities.ColorSystem,
 | 
			
		||||
                    style.Foreground,
 | 
			
		||||
                    true));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Got background?
 | 
			
		||||
        if (style.Background != Color.Default)
 | 
			
		||||
        {
 | 
			
		||||
            codes = codes.Concat(
 | 
			
		||||
                AnsiColorBuilder.GetAnsiCodes(
 | 
			
		||||
                    profile.Capabilities.ColorSystem,
 | 
			
		||||
                    style.Background,
 | 
			
		||||
                    false));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var result = codes.ToArray();
 | 
			
		||||
        if (result.Length == 0 && style.Link == null)
 | 
			
		||||
        {
 | 
			
		||||
            return text;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var ansi = result.Length > 0
 | 
			
		||||
            ? $"{SGR(result)}{text}{SGR(0)}"
 | 
			
		||||
            : text;
 | 
			
		||||
 | 
			
		||||
        if (style.Link != null && !profile.Capabilities.Legacy)
 | 
			
		||||
        {
 | 
			
		||||
            var link = style.Link;
 | 
			
		||||
 | 
			
		||||
            // Empty links means we should take the URL from the text.
 | 
			
		||||
            if (link.Equals(Constants.EmptyLink, StringComparison.Ordinal))
 | 
			
		||||
            {
 | 
			
		||||
                link = text;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            var linkId = _linkHasher.GenerateId(link, text);
 | 
			
		||||
            ansi = $"{ESC}]8;id={linkId};{link}{ESC}\\{ansi}{ESC}]8;;{ESC}\\";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return ansi;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -2,69 +2,68 @@ using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Diagnostics;
 | 
			
		||||
 | 
			
		||||
namespace Spectre.Console
 | 
			
		||||
namespace Spectre.Console;
 | 
			
		||||
 | 
			
		||||
internal static class AnsiColorBuilder
 | 
			
		||||
{
 | 
			
		||||
    internal static class AnsiColorBuilder
 | 
			
		||||
    public static IEnumerable<byte> GetAnsiCodes(ColorSystem system, Color color, bool foreground)
 | 
			
		||||
    {
 | 
			
		||||
        public static IEnumerable<byte> GetAnsiCodes(ColorSystem system, Color color, bool foreground)
 | 
			
		||||
        return system switch
 | 
			
		||||
        {
 | 
			
		||||
            return system switch
 | 
			
		||||
            {
 | 
			
		||||
                ColorSystem.NoColors => Array.Empty<byte>(), // No colors
 | 
			
		||||
                ColorSystem.TrueColor => GetTrueColor(color, foreground), // 24-bit
 | 
			
		||||
                ColorSystem.EightBit => GetEightBit(color, foreground), // 8-bit
 | 
			
		||||
                ColorSystem.Standard => GetFourBit(color, foreground), // 4-bit
 | 
			
		||||
                ColorSystem.Legacy => GetThreeBit(color, foreground), // 3-bit
 | 
			
		||||
                _ => throw new InvalidOperationException("Could not determine ANSI color."),
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private static IEnumerable<byte> GetThreeBit(Color color, bool foreground)
 | 
			
		||||
        {
 | 
			
		||||
            var number = color.Number;
 | 
			
		||||
            if (number == null || color.Number >= 8)
 | 
			
		||||
            {
 | 
			
		||||
                number = ColorPalette.ExactOrClosest(ColorSystem.Legacy, color).Number;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            Debug.Assert(number >= 0 && number < 8, "Invalid range for 4-bit color");
 | 
			
		||||
 | 
			
		||||
            var mod = foreground ? 30 : 40;
 | 
			
		||||
            return new byte[] { (byte)(number.Value + mod) };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private static IEnumerable<byte> GetFourBit(Color color, bool foreground)
 | 
			
		||||
        {
 | 
			
		||||
            var number = color.Number;
 | 
			
		||||
            if (number == null || color.Number >= 16)
 | 
			
		||||
            {
 | 
			
		||||
                number = ColorPalette.ExactOrClosest(ColorSystem.Standard, color).Number;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            Debug.Assert(number >= 0 && number < 16, "Invalid range for 4-bit color");
 | 
			
		||||
 | 
			
		||||
            var mod = number < 8 ? (foreground ? 30 : 40) : (foreground ? 82 : 92);
 | 
			
		||||
            return new byte[] { (byte)(number.Value + mod) };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private static IEnumerable<byte> GetEightBit(Color color, bool foreground)
 | 
			
		||||
        {
 | 
			
		||||
            var number = color.Number ?? ColorPalette.ExactOrClosest(ColorSystem.EightBit, color).Number;
 | 
			
		||||
            Debug.Assert(number >= 0 && number <= 255, "Invalid range for 8-bit color");
 | 
			
		||||
 | 
			
		||||
            var mod = foreground ? (byte)38 : (byte)48;
 | 
			
		||||
            return new byte[] { mod, 5, (byte)number };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private static IEnumerable<byte> GetTrueColor(Color color, bool foreground)
 | 
			
		||||
        {
 | 
			
		||||
            if (color.Number != null)
 | 
			
		||||
            {
 | 
			
		||||
                return GetEightBit(color, foreground);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            var mod = foreground ? (byte)38 : (byte)48;
 | 
			
		||||
            return new byte[] { mod, 2, color.R, color.G, color.B };
 | 
			
		||||
        }
 | 
			
		||||
            ColorSystem.NoColors => Array.Empty<byte>(), // No colors
 | 
			
		||||
            ColorSystem.TrueColor => GetTrueColor(color, foreground), // 24-bit
 | 
			
		||||
            ColorSystem.EightBit => GetEightBit(color, foreground), // 8-bit
 | 
			
		||||
            ColorSystem.Standard => GetFourBit(color, foreground), // 4-bit
 | 
			
		||||
            ColorSystem.Legacy => GetThreeBit(color, foreground), // 3-bit
 | 
			
		||||
            _ => throw new InvalidOperationException("Could not determine ANSI color."),
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    private static IEnumerable<byte> GetThreeBit(Color color, bool foreground)
 | 
			
		||||
    {
 | 
			
		||||
        var number = color.Number;
 | 
			
		||||
        if (number == null || color.Number >= 8)
 | 
			
		||||
        {
 | 
			
		||||
            number = ColorPalette.ExactOrClosest(ColorSystem.Legacy, color).Number;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Debug.Assert(number >= 0 && number < 8, "Invalid range for 4-bit color");
 | 
			
		||||
 | 
			
		||||
        var mod = foreground ? 30 : 40;
 | 
			
		||||
        return new byte[] { (byte)(number.Value + mod) };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static IEnumerable<byte> GetFourBit(Color color, bool foreground)
 | 
			
		||||
    {
 | 
			
		||||
        var number = color.Number;
 | 
			
		||||
        if (number == null || color.Number >= 16)
 | 
			
		||||
        {
 | 
			
		||||
            number = ColorPalette.ExactOrClosest(ColorSystem.Standard, color).Number;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Debug.Assert(number >= 0 && number < 16, "Invalid range for 4-bit color");
 | 
			
		||||
 | 
			
		||||
        var mod = number < 8 ? (foreground ? 30 : 40) : (foreground ? 82 : 92);
 | 
			
		||||
        return new byte[] { (byte)(number.Value + mod) };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static IEnumerable<byte> GetEightBit(Color color, bool foreground)
 | 
			
		||||
    {
 | 
			
		||||
        var number = color.Number ?? ColorPalette.ExactOrClosest(ColorSystem.EightBit, color).Number;
 | 
			
		||||
        Debug.Assert(number >= 0 && number <= 255, "Invalid range for 8-bit color");
 | 
			
		||||
 | 
			
		||||
        var mod = foreground ? (byte)38 : (byte)48;
 | 
			
		||||
        return new byte[] { mod, 5, (byte)number };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static IEnumerable<byte> GetTrueColor(Color color, bool foreground)
 | 
			
		||||
    {
 | 
			
		||||
        if (color.Number != null)
 | 
			
		||||
        {
 | 
			
		||||
            return GetEightBit(color, foreground);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var mod = foreground ? (byte)38 : (byte)48;
 | 
			
		||||
        return new byte[] { mod, 2, color.R, color.G, color.B };
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -2,39 +2,38 @@ using System;
 | 
			
		||||
using Spectre.Console.Rendering;
 | 
			
		||||
using static Spectre.Console.AnsiSequences;
 | 
			
		||||
 | 
			
		||||
namespace Spectre.Console
 | 
			
		||||
namespace Spectre.Console;
 | 
			
		||||
 | 
			
		||||
internal sealed class AnsiConsoleBackend : IAnsiConsoleBackend
 | 
			
		||||
{
 | 
			
		||||
    internal sealed class AnsiConsoleBackend : IAnsiConsoleBackend
 | 
			
		||||
    private readonly IAnsiConsole _console;
 | 
			
		||||
 | 
			
		||||
    public IAnsiConsoleCursor Cursor { get; }
 | 
			
		||||
 | 
			
		||||
    public AnsiConsoleBackend(IAnsiConsole console)
 | 
			
		||||
    {
 | 
			
		||||
        private readonly IAnsiConsole _console;
 | 
			
		||||
        _console = console ?? throw new ArgumentNullException(nameof(console));
 | 
			
		||||
        Cursor = new AnsiConsoleCursor(this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        public IAnsiConsoleCursor Cursor { get; }
 | 
			
		||||
    public void Clear(bool home)
 | 
			
		||||
    {
 | 
			
		||||
        Write(new ControlCode(ED(2)));
 | 
			
		||||
        Write(new ControlCode(ED(3)));
 | 
			
		||||
 | 
			
		||||
        public AnsiConsoleBackend(IAnsiConsole console)
 | 
			
		||||
        if (home)
 | 
			
		||||
        {
 | 
			
		||||
            _console = console ?? throw new ArgumentNullException(nameof(console));
 | 
			
		||||
            Cursor = new AnsiConsoleCursor(this);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Clear(bool home)
 | 
			
		||||
        {
 | 
			
		||||
            Write(new ControlCode(ED(2)));
 | 
			
		||||
            Write(new ControlCode(ED(3)));
 | 
			
		||||
 | 
			
		||||
            if (home)
 | 
			
		||||
            {
 | 
			
		||||
                Write(new ControlCode(CUP(1, 1)));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Write(IRenderable renderable)
 | 
			
		||||
        {
 | 
			
		||||
            var result = AnsiBuilder.Build(_console, renderable);
 | 
			
		||||
            if (result?.Length > 0)
 | 
			
		||||
            {
 | 
			
		||||
                _console.Profile.Out.Writer.Write(result);
 | 
			
		||||
                _console.Profile.Out.Writer.Flush();
 | 
			
		||||
            }
 | 
			
		||||
            Write(new ControlCode(CUP(1, 1)));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    public void Write(IRenderable renderable)
 | 
			
		||||
    {
 | 
			
		||||
        var result = AnsiBuilder.Build(_console, renderable);
 | 
			
		||||
        if (result?.Length > 0)
 | 
			
		||||
        {
 | 
			
		||||
            _console.Profile.Out.Writer.Write(result);
 | 
			
		||||
            _console.Profile.Out.Writer.Flush();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,56 +1,55 @@
 | 
			
		||||
using System;
 | 
			
		||||
using static Spectre.Console.AnsiSequences;
 | 
			
		||||
 | 
			
		||||
namespace Spectre.Console
 | 
			
		||||
namespace Spectre.Console;
 | 
			
		||||
 | 
			
		||||
internal sealed class AnsiConsoleCursor : IAnsiConsoleCursor
 | 
			
		||||
{
 | 
			
		||||
    internal sealed class AnsiConsoleCursor : IAnsiConsoleCursor
 | 
			
		||||
    private readonly AnsiConsoleBackend _backend;
 | 
			
		||||
 | 
			
		||||
    public AnsiConsoleCursor(AnsiConsoleBackend backend)
 | 
			
		||||
    {
 | 
			
		||||
        private readonly AnsiConsoleBackend _backend;
 | 
			
		||||
        _backend = backend ?? throw new ArgumentNullException(nameof(backend));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        public AnsiConsoleCursor(AnsiConsoleBackend backend)
 | 
			
		||||
    public void Show(bool show)
 | 
			
		||||
    {
 | 
			
		||||
        if (show)
 | 
			
		||||
        {
 | 
			
		||||
            _backend = backend ?? throw new ArgumentNullException(nameof(backend));
 | 
			
		||||
            _backend.Write(new ControlCode(SM(DECTCEM)));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Show(bool show)
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            if (show)
 | 
			
		||||
            {
 | 
			
		||||
                _backend.Write(new ControlCode(SM(DECTCEM)));
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                _backend.Write(new ControlCode(RM(DECTCEM)));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Move(CursorDirection direction, int steps)
 | 
			
		||||
        {
 | 
			
		||||
            if (steps == 0)
 | 
			
		||||
            {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            switch (direction)
 | 
			
		||||
            {
 | 
			
		||||
                case CursorDirection.Up:
 | 
			
		||||
                    _backend.Write(new ControlCode(CUU(steps)));
 | 
			
		||||
                    break;
 | 
			
		||||
                case CursorDirection.Down:
 | 
			
		||||
                    _backend.Write(new ControlCode(CUD(steps)));
 | 
			
		||||
                    break;
 | 
			
		||||
                case CursorDirection.Right:
 | 
			
		||||
                    _backend.Write(new ControlCode(CUF(steps)));
 | 
			
		||||
                    break;
 | 
			
		||||
                case CursorDirection.Left:
 | 
			
		||||
                    _backend.Write(new ControlCode(CUB(steps)));
 | 
			
		||||
                    break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void SetPosition(int column, int line)
 | 
			
		||||
        {
 | 
			
		||||
            _backend.Write(new ControlCode(CUP(line, column)));
 | 
			
		||||
            _backend.Write(new ControlCode(RM(DECTCEM)));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    public void Move(CursorDirection direction, int steps)
 | 
			
		||||
    {
 | 
			
		||||
        if (steps == 0)
 | 
			
		||||
        {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        switch (direction)
 | 
			
		||||
        {
 | 
			
		||||
            case CursorDirection.Up:
 | 
			
		||||
                _backend.Write(new ControlCode(CUU(steps)));
 | 
			
		||||
                break;
 | 
			
		||||
            case CursorDirection.Down:
 | 
			
		||||
                _backend.Write(new ControlCode(CUD(steps)));
 | 
			
		||||
                break;
 | 
			
		||||
            case CursorDirection.Right:
 | 
			
		||||
                _backend.Write(new ControlCode(CUF(steps)));
 | 
			
		||||
                break;
 | 
			
		||||
            case CursorDirection.Left:
 | 
			
		||||
                _backend.Write(new ControlCode(CUB(steps)));
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void SetPosition(int column, int line)
 | 
			
		||||
    {
 | 
			
		||||
        _backend.Write(new ControlCode(CUP(line, column)));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,56 +1,55 @@
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
 | 
			
		||||
namespace Spectre.Console
 | 
			
		||||
namespace Spectre.Console;
 | 
			
		||||
 | 
			
		||||
internal static class AnsiDecorationBuilder
 | 
			
		||||
{
 | 
			
		||||
    internal static class AnsiDecorationBuilder
 | 
			
		||||
    // TODO: Rewrite this to not yield
 | 
			
		||||
    public static IEnumerable<byte> GetAnsiCodes(Decoration decoration)
 | 
			
		||||
    {
 | 
			
		||||
        // TODO: Rewrite this to not yield
 | 
			
		||||
        public static IEnumerable<byte> GetAnsiCodes(Decoration decoration)
 | 
			
		||||
        if ((decoration & Decoration.Bold) != 0)
 | 
			
		||||
        {
 | 
			
		||||
            if ((decoration & Decoration.Bold) != 0)
 | 
			
		||||
            {
 | 
			
		||||
                yield return 1;
 | 
			
		||||
            }
 | 
			
		||||
            yield return 1;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
            if ((decoration & Decoration.Dim) != 0)
 | 
			
		||||
            {
 | 
			
		||||
                yield return 2;
 | 
			
		||||
            }
 | 
			
		||||
        if ((decoration & Decoration.Dim) != 0)
 | 
			
		||||
        {
 | 
			
		||||
            yield return 2;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
            if ((decoration & Decoration.Italic) != 0)
 | 
			
		||||
            {
 | 
			
		||||
                yield return 3;
 | 
			
		||||
            }
 | 
			
		||||
        if ((decoration & Decoration.Italic) != 0)
 | 
			
		||||
        {
 | 
			
		||||
            yield return 3;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
            if ((decoration & Decoration.Underline) != 0)
 | 
			
		||||
            {
 | 
			
		||||
                yield return 4;
 | 
			
		||||
            }
 | 
			
		||||
        if ((decoration & Decoration.Underline) != 0)
 | 
			
		||||
        {
 | 
			
		||||
            yield return 4;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
            if ((decoration & Decoration.SlowBlink) != 0)
 | 
			
		||||
            {
 | 
			
		||||
                yield return 5;
 | 
			
		||||
            }
 | 
			
		||||
        if ((decoration & Decoration.SlowBlink) != 0)
 | 
			
		||||
        {
 | 
			
		||||
            yield return 5;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
            if ((decoration & Decoration.RapidBlink) != 0)
 | 
			
		||||
            {
 | 
			
		||||
                yield return 6;
 | 
			
		||||
            }
 | 
			
		||||
        if ((decoration & Decoration.RapidBlink) != 0)
 | 
			
		||||
        {
 | 
			
		||||
            yield return 6;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
            if ((decoration & Decoration.Invert) != 0)
 | 
			
		||||
            {
 | 
			
		||||
                yield return 7;
 | 
			
		||||
            }
 | 
			
		||||
        if ((decoration & Decoration.Invert) != 0)
 | 
			
		||||
        {
 | 
			
		||||
            yield return 7;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
            if ((decoration & Decoration.Conceal) != 0)
 | 
			
		||||
            {
 | 
			
		||||
                yield return 8;
 | 
			
		||||
            }
 | 
			
		||||
        if ((decoration & Decoration.Conceal) != 0)
 | 
			
		||||
        {
 | 
			
		||||
            yield return 8;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
            if ((decoration & Decoration.Strikethrough) != 0)
 | 
			
		||||
            {
 | 
			
		||||
                yield return 9;
 | 
			
		||||
            }
 | 
			
		||||
        if ((decoration & Decoration.Strikethrough) != 0)
 | 
			
		||||
        {
 | 
			
		||||
            yield return 9;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
@@ -9,133 +9,132 @@ using System.Text.RegularExpressions;
 | 
			
		||||
// https://github.com/keqingrong/supports-ansi/blob/master/index.js
 | 
			
		||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
namespace Spectre.Console
 | 
			
		||||
namespace Spectre.Console;
 | 
			
		||||
 | 
			
		||||
internal static class AnsiDetector
 | 
			
		||||
{
 | 
			
		||||
    internal static class AnsiDetector
 | 
			
		||||
    private static readonly Regex[] _regexes = new[]
 | 
			
		||||
    {
 | 
			
		||||
        private static readonly Regex[] _regexes = new[]
 | 
			
		||||
        {
 | 
			
		||||
            new Regex("^xterm"), // xterm, PuTTY, Mintty
 | 
			
		||||
            new Regex("^rxvt"), // RXVT
 | 
			
		||||
            new Regex("^eterm"), // Eterm
 | 
			
		||||
            new Regex("^screen"), // GNU screen, tmux
 | 
			
		||||
            new Regex("tmux"), // tmux
 | 
			
		||||
            new Regex("^vt100"), // DEC VT series
 | 
			
		||||
            new Regex("^vt102"), // DEC VT series
 | 
			
		||||
            new Regex("^vt220"), // DEC VT series
 | 
			
		||||
            new Regex("^vt320"), // DEC VT series
 | 
			
		||||
            new Regex("ansi"), // ANSI
 | 
			
		||||
            new Regex("scoansi"), // SCO ANSI
 | 
			
		||||
            new Regex("cygwin"), // Cygwin, MinGW
 | 
			
		||||
            new Regex("linux"), // Linux console
 | 
			
		||||
            new Regex("konsole"), // Konsole
 | 
			
		||||
            new Regex("bvterm"), // Bitvise SSH Client
 | 
			
		||||
        };
 | 
			
		||||
        new Regex("^xterm"), // xterm, PuTTY, Mintty
 | 
			
		||||
        new Regex("^rxvt"), // RXVT
 | 
			
		||||
        new Regex("^eterm"), // Eterm
 | 
			
		||||
        new Regex("^screen"), // GNU screen, tmux
 | 
			
		||||
        new Regex("tmux"), // tmux
 | 
			
		||||
        new Regex("^vt100"), // DEC VT series
 | 
			
		||||
        new Regex("^vt102"), // DEC VT series
 | 
			
		||||
        new Regex("^vt220"), // DEC VT series
 | 
			
		||||
        new Regex("^vt320"), // DEC VT series
 | 
			
		||||
        new Regex("ansi"), // ANSI
 | 
			
		||||
        new Regex("scoansi"), // SCO ANSI
 | 
			
		||||
        new Regex("cygwin"), // Cygwin, MinGW
 | 
			
		||||
        new Regex("linux"), // Linux console
 | 
			
		||||
        new Regex("konsole"), // Konsole
 | 
			
		||||
        new Regex("bvterm"), // Bitvise SSH Client
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
        public static (bool SupportsAnsi, bool LegacyConsole) Detect(bool stdError, bool upgrade)
 | 
			
		||||
    public static (bool SupportsAnsi, bool LegacyConsole) Detect(bool stdError, bool upgrade)
 | 
			
		||||
    {
 | 
			
		||||
        // Running on Windows?
 | 
			
		||||
        if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
 | 
			
		||||
        {
 | 
			
		||||
            // Running on Windows?
 | 
			
		||||
            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
 | 
			
		||||
            // Running under ConEmu?
 | 
			
		||||
            var conEmu = Environment.GetEnvironmentVariable("ConEmuANSI");
 | 
			
		||||
            if (!string.IsNullOrEmpty(conEmu) && conEmu.Equals("On", StringComparison.OrdinalIgnoreCase))
 | 
			
		||||
            {
 | 
			
		||||
                // Running under ConEmu?
 | 
			
		||||
                var conEmu = Environment.GetEnvironmentVariable("ConEmuANSI");
 | 
			
		||||
                if (!string.IsNullOrEmpty(conEmu) && conEmu.Equals("On", StringComparison.OrdinalIgnoreCase))
 | 
			
		||||
                {
 | 
			
		||||
                    return (true, false);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                var supportsAnsi = Windows.SupportsAnsi(upgrade, stdError, out var legacyConsole);
 | 
			
		||||
                return (supportsAnsi, legacyConsole);
 | 
			
		||||
                return (true, false);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return DetectFromTerm();
 | 
			
		||||
            var supportsAnsi = Windows.SupportsAnsi(upgrade, stdError, out var legacyConsole);
 | 
			
		||||
            return (supportsAnsi, legacyConsole);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private static (bool SupportsAnsi, bool LegacyConsole) DetectFromTerm()
 | 
			
		||||
        return DetectFromTerm();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static (bool SupportsAnsi, bool LegacyConsole) DetectFromTerm()
 | 
			
		||||
    {
 | 
			
		||||
        // Check if the terminal is of type ANSI/VT100/xterm compatible.
 | 
			
		||||
        var term = Environment.GetEnvironmentVariable("TERM");
 | 
			
		||||
        if (!string.IsNullOrWhiteSpace(term))
 | 
			
		||||
        {
 | 
			
		||||
            // Check if the terminal is of type ANSI/VT100/xterm compatible.
 | 
			
		||||
            var term = Environment.GetEnvironmentVariable("TERM");
 | 
			
		||||
            if (!string.IsNullOrWhiteSpace(term))
 | 
			
		||||
            if (_regexes.Any(regex => regex.IsMatch(term)))
 | 
			
		||||
            {
 | 
			
		||||
                if (_regexes.Any(regex => regex.IsMatch(term)))
 | 
			
		||||
                {
 | 
			
		||||
                    return (true, false);
 | 
			
		||||
                }
 | 
			
		||||
                return (true, false);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return (false, true);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        internal static class Windows
 | 
			
		||||
        return (false, true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    internal static class Windows
 | 
			
		||||
    {
 | 
			
		||||
        [SuppressMessage("StyleCop.CSharp.NamingRules", "SA1310:Field names should not contain underscore")]
 | 
			
		||||
        private const int STD_OUTPUT_HANDLE = -11;
 | 
			
		||||
 | 
			
		||||
        [SuppressMessage("StyleCop.CSharp.NamingRules", "SA1310:Field names should not contain underscore")]
 | 
			
		||||
        private const int STD_ERROR_HANDLE = -12;
 | 
			
		||||
 | 
			
		||||
        [SuppressMessage("StyleCop.CSharp.NamingRules", "SA1310:Field names should not contain underscore")]
 | 
			
		||||
        private const uint ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004;
 | 
			
		||||
 | 
			
		||||
        [SuppressMessage("StyleCop.CSharp.NamingRules", "SA1310:Field names should not contain underscore")]
 | 
			
		||||
        private const uint DISABLE_NEWLINE_AUTO_RETURN = 0x0008;
 | 
			
		||||
 | 
			
		||||
        [DllImport("kernel32.dll")]
 | 
			
		||||
        private static extern bool GetConsoleMode(IntPtr hConsoleHandle, out uint lpMode);
 | 
			
		||||
 | 
			
		||||
        [DllImport("kernel32.dll")]
 | 
			
		||||
        private static extern bool SetConsoleMode(IntPtr hConsoleHandle, uint dwMode);
 | 
			
		||||
 | 
			
		||||
        [DllImport("kernel32.dll", SetLastError = true)]
 | 
			
		||||
        private static extern IntPtr GetStdHandle(int nStdHandle);
 | 
			
		||||
 | 
			
		||||
        [DllImport("kernel32.dll")]
 | 
			
		||||
        public static extern uint GetLastError();
 | 
			
		||||
 | 
			
		||||
        public static bool SupportsAnsi(bool upgrade, bool stdError, out bool isLegacy)
 | 
			
		||||
        {
 | 
			
		||||
            [SuppressMessage("StyleCop.CSharp.NamingRules", "SA1310:Field names should not contain underscore")]
 | 
			
		||||
            private const int STD_OUTPUT_HANDLE = -11;
 | 
			
		||||
            isLegacy = false;
 | 
			
		||||
 | 
			
		||||
            [SuppressMessage("StyleCop.CSharp.NamingRules", "SA1310:Field names should not contain underscore")]
 | 
			
		||||
            private const int STD_ERROR_HANDLE = -12;
 | 
			
		||||
 | 
			
		||||
            [SuppressMessage("StyleCop.CSharp.NamingRules", "SA1310:Field names should not contain underscore")]
 | 
			
		||||
            private const uint ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004;
 | 
			
		||||
 | 
			
		||||
            [SuppressMessage("StyleCop.CSharp.NamingRules", "SA1310:Field names should not contain underscore")]
 | 
			
		||||
            private const uint DISABLE_NEWLINE_AUTO_RETURN = 0x0008;
 | 
			
		||||
 | 
			
		||||
            [DllImport("kernel32.dll")]
 | 
			
		||||
            private static extern bool GetConsoleMode(IntPtr hConsoleHandle, out uint lpMode);
 | 
			
		||||
 | 
			
		||||
            [DllImport("kernel32.dll")]
 | 
			
		||||
            private static extern bool SetConsoleMode(IntPtr hConsoleHandle, uint dwMode);
 | 
			
		||||
 | 
			
		||||
            [DllImport("kernel32.dll", SetLastError = true)]
 | 
			
		||||
            private static extern IntPtr GetStdHandle(int nStdHandle);
 | 
			
		||||
 | 
			
		||||
            [DllImport("kernel32.dll")]
 | 
			
		||||
            public static extern uint GetLastError();
 | 
			
		||||
 | 
			
		||||
            public static bool SupportsAnsi(bool upgrade, bool stdError, out bool isLegacy)
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                isLegacy = false;
 | 
			
		||||
 | 
			
		||||
                try
 | 
			
		||||
                var @out = GetStdHandle(stdError ? STD_ERROR_HANDLE : STD_OUTPUT_HANDLE);
 | 
			
		||||
                if (!GetConsoleMode(@out, out var mode))
 | 
			
		||||
                {
 | 
			
		||||
                    var @out = GetStdHandle(stdError ? STD_ERROR_HANDLE : STD_OUTPUT_HANDLE);
 | 
			
		||||
                    if (!GetConsoleMode(@out, out var mode))
 | 
			
		||||
                    {
 | 
			
		||||
                        // Could not get console mode, try TERM (set in cygwin, WSL-Shell).
 | 
			
		||||
                        var (ansiFromTerm, legacyFromTerm) = DetectFromTerm();
 | 
			
		||||
                    // Could not get console mode, try TERM (set in cygwin, WSL-Shell).
 | 
			
		||||
                    var (ansiFromTerm, legacyFromTerm) = DetectFromTerm();
 | 
			
		||||
 | 
			
		||||
                        isLegacy = ansiFromTerm ? legacyFromTerm : isLegacy;
 | 
			
		||||
                        return ansiFromTerm;
 | 
			
		||||
                    isLegacy = ansiFromTerm ? legacyFromTerm : isLegacy;
 | 
			
		||||
                    return ansiFromTerm;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if ((mode & ENABLE_VIRTUAL_TERMINAL_PROCESSING) == 0)
 | 
			
		||||
                {
 | 
			
		||||
                    isLegacy = true;
 | 
			
		||||
 | 
			
		||||
                    if (!upgrade)
 | 
			
		||||
                    {
 | 
			
		||||
                        return false;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    if ((mode & ENABLE_VIRTUAL_TERMINAL_PROCESSING) == 0)
 | 
			
		||||
                    // Try enable ANSI support.
 | 
			
		||||
                    mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING | DISABLE_NEWLINE_AUTO_RETURN;
 | 
			
		||||
                    if (!SetConsoleMode(@out, mode))
 | 
			
		||||
                    {
 | 
			
		||||
                        isLegacy = true;
 | 
			
		||||
 | 
			
		||||
                        if (!upgrade)
 | 
			
		||||
                        {
 | 
			
		||||
                            return false;
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        // Try enable ANSI support.
 | 
			
		||||
                        mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING | DISABLE_NEWLINE_AUTO_RETURN;
 | 
			
		||||
                        if (!SetConsoleMode(@out, mode))
 | 
			
		||||
                        {
 | 
			
		||||
                            // Enabling failed.
 | 
			
		||||
                            return false;
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        isLegacy = false;
 | 
			
		||||
                        // Enabling failed.
 | 
			
		||||
                        return false;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    return true;
 | 
			
		||||
                }
 | 
			
		||||
                catch
 | 
			
		||||
                {
 | 
			
		||||
                    // All we know here is that we don't support ANSI.
 | 
			
		||||
                    return false;
 | 
			
		||||
                    isLegacy = false;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
            catch
 | 
			
		||||
            {
 | 
			
		||||
                // All we know here is that we don't support ANSI.
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
@@ -1,42 +1,41 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Runtime.CompilerServices;
 | 
			
		||||
 | 
			
		||||
namespace Spectre.Console
 | 
			
		||||
namespace Spectre.Console;
 | 
			
		||||
 | 
			
		||||
internal sealed class AnsiLinkHasher
 | 
			
		||||
{
 | 
			
		||||
    internal sealed class AnsiLinkHasher
 | 
			
		||||
    private readonly Random _random;
 | 
			
		||||
 | 
			
		||||
    public AnsiLinkHasher()
 | 
			
		||||
    {
 | 
			
		||||
        private readonly Random _random;
 | 
			
		||||
        _random = new Random(Environment.TickCount);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        public AnsiLinkHasher()
 | 
			
		||||
    public int GenerateId(string link, string text)
 | 
			
		||||
    {
 | 
			
		||||
        if (link is null)
 | 
			
		||||
        {
 | 
			
		||||
            _random = new Random(Environment.TickCount);
 | 
			
		||||
            throw new ArgumentNullException(nameof(link));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public int GenerateId(string link, string text)
 | 
			
		||||
        link += text ?? string.Empty;
 | 
			
		||||
 | 
			
		||||
        unchecked
 | 
			
		||||
        {
 | 
			
		||||
            if (link is null)
 | 
			
		||||
            {
 | 
			
		||||
                throw new ArgumentNullException(nameof(link));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            link += text ?? string.Empty;
 | 
			
		||||
 | 
			
		||||
            unchecked
 | 
			
		||||
            {
 | 
			
		||||
                return Math.Abs(
 | 
			
		||||
                    GetLinkHashCode(link) +
 | 
			
		||||
                    _random.Next(0, int.MaxValue));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 | 
			
		||||
        private static int GetLinkHashCode(string link)
 | 
			
		||||
        {
 | 
			
		||||
#if NETSTANDARD2_0
 | 
			
		||||
            return link.GetHashCode();
 | 
			
		||||
#else
 | 
			
		||||
            return link.GetHashCode(StringComparison.Ordinal);
 | 
			
		||||
#endif
 | 
			
		||||
            return Math.Abs(
 | 
			
		||||
                GetLinkHashCode(link) +
 | 
			
		||||
                _random.Next(0, int.MaxValue));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 | 
			
		||||
    private static int GetLinkHashCode(string link)
 | 
			
		||||
    {
 | 
			
		||||
#if NETSTANDARD2_0
 | 
			
		||||
        return link.GetHashCode();
 | 
			
		||||
#else
 | 
			
		||||
        return link.GetHashCode(StringComparison.Ordinal);
 | 
			
		||||
#endif
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,163 +1,162 @@
 | 
			
		||||
using System.Linq;
 | 
			
		||||
 | 
			
		||||
namespace Spectre.Console
 | 
			
		||||
namespace Spectre.Console;
 | 
			
		||||
 | 
			
		||||
internal static class AnsiSequences
 | 
			
		||||
{
 | 
			
		||||
    internal static class AnsiSequences
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// The ASCII escape character (decimal 27).
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public const string ESC = "\u001b";
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Introduces a control sequence that uses 8-bit characters.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public const string CSI = ESC + "[";
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Text cursor enable.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <remarks>
 | 
			
		||||
    /// See <see href="https://vt100.net/docs/vt510-rm/DECRQM.html#T5-8"/>.
 | 
			
		||||
    /// </remarks>
 | 
			
		||||
    public const int DECTCEM = 25;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// This control function selects one or more character attributes at the same time.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <remarks>
 | 
			
		||||
    /// See <see href="https://vt100.net/docs/vt510-rm/SGR.html"/>.
 | 
			
		||||
    /// </remarks>
 | 
			
		||||
    /// <returns>The ANSI escape code.</returns>
 | 
			
		||||
    public static string SGR(params byte[] codes)
 | 
			
		||||
    {
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// The ASCII escape character (decimal 27).
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public const string ESC = "\u001b";
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Introduces a control sequence that uses 8-bit characters.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public const string CSI = ESC + "[";
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Text cursor enable.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <remarks>
 | 
			
		||||
        /// See <see href="https://vt100.net/docs/vt510-rm/DECRQM.html#T5-8"/>.
 | 
			
		||||
        /// </remarks>
 | 
			
		||||
        public const int DECTCEM = 25;
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// This control function selects one or more character attributes at the same time.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <remarks>
 | 
			
		||||
        /// See <see href="https://vt100.net/docs/vt510-rm/SGR.html"/>.
 | 
			
		||||
        /// </remarks>
 | 
			
		||||
        /// <returns>The ANSI escape code.</returns>
 | 
			
		||||
        public static string SGR(params byte[] codes)
 | 
			
		||||
        {
 | 
			
		||||
            var joinedCodes = string.Join(";", codes.Select(c => c.ToString()));
 | 
			
		||||
            return $"{CSI}{joinedCodes}m";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// This control function erases characters from part or all of the display.
 | 
			
		||||
        /// When you erase complete lines, they become single-height, single-width lines,
 | 
			
		||||
        /// with all visual character attributes cleared.
 | 
			
		||||
        /// ED works inside or outside the scrolling margins.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <remarks>
 | 
			
		||||
        /// See <see href="https://vt100.net/docs/vt510-rm/ED.html"/>.
 | 
			
		||||
        /// </remarks>
 | 
			
		||||
        /// <returns>The ANSI escape code.</returns>
 | 
			
		||||
        public static string ED(int code)
 | 
			
		||||
        {
 | 
			
		||||
            return $"{CSI}{code}J";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Moves the cursor up a specified number of lines in the same column.
 | 
			
		||||
        /// The cursor stops at the top margin.
 | 
			
		||||
        /// If the cursor is already above the top margin, then the cursor stops at the top line.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <remarks>
 | 
			
		||||
        /// See <see href="https://vt100.net/docs/vt510-rm/CUU.html"/>.
 | 
			
		||||
        /// </remarks>
 | 
			
		||||
        /// <param name="steps">The number of steps to move up.</param>
 | 
			
		||||
        /// <returns>The ANSI escape code.</returns>
 | 
			
		||||
        public static string CUU(int steps)
 | 
			
		||||
        {
 | 
			
		||||
            return $"{CSI}{steps}A";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// This control function moves the cursor down a specified number of lines in the same column.
 | 
			
		||||
        /// The cursor stops at the bottom margin.
 | 
			
		||||
        /// If the cursor is already below the bottom margin, then the cursor stops at the bottom line.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <remarks>
 | 
			
		||||
        /// See <see href="https://vt100.net/docs/vt510-rm/CUD.html"/>.
 | 
			
		||||
        /// </remarks>
 | 
			
		||||
        /// <param name="steps">The number of steps to move down.</param>
 | 
			
		||||
        /// <returns>The ANSI escape code.</returns>
 | 
			
		||||
        public static string CUD(int steps)
 | 
			
		||||
        {
 | 
			
		||||
            return $"{CSI}{steps}B";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// This control function moves the cursor to the right by a specified number of columns.
 | 
			
		||||
        /// The cursor stops at the right border of the page.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <remarks>
 | 
			
		||||
        /// See <see href="https://vt100.net/docs/vt510-rm/CUF.html"/>.
 | 
			
		||||
        /// </remarks>
 | 
			
		||||
        /// <param name="steps">The number of steps to move forward.</param>
 | 
			
		||||
        /// <returns>The ANSI escape code.</returns>
 | 
			
		||||
        public static string CUF(int steps)
 | 
			
		||||
        {
 | 
			
		||||
            return $"{CSI}{steps}C";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// This control function moves the cursor to the left by a specified number of columns.
 | 
			
		||||
        /// The cursor stops at the left border of the page.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <remarks>
 | 
			
		||||
        /// See <see href="https://vt100.net/docs/vt510-rm/CUB.html"/>.
 | 
			
		||||
        /// </remarks>
 | 
			
		||||
        /// <param name="steps">The number of steps to move backward.</param>
 | 
			
		||||
        /// <returns>The ANSI escape code.</returns>
 | 
			
		||||
        public static string CUB(int steps)
 | 
			
		||||
        {
 | 
			
		||||
            return $"{CSI}{steps}D";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Moves the cursor to the specified position.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="line">The line to move to.</param>
 | 
			
		||||
        /// <param name="column">The column to move to.</param>
 | 
			
		||||
        /// <remarks>
 | 
			
		||||
        /// See <see href="https://vt100.net/docs/vt510-rm/CUP.html"/>.
 | 
			
		||||
        /// </remarks>
 | 
			
		||||
        /// <returns>The ANSI escape code.</returns>
 | 
			
		||||
        public static string CUP(int line, int column)
 | 
			
		||||
        {
 | 
			
		||||
            return $"{CSI}{line};{column}H";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Hides the cursor.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <remarks>
 | 
			
		||||
        /// See <see href="https://vt100.net/docs/vt510-rm/RM.html"/>.
 | 
			
		||||
        /// </remarks>
 | 
			
		||||
        /// <returns>The ANSI escape code.</returns>
 | 
			
		||||
        public static string RM(int code)
 | 
			
		||||
        {
 | 
			
		||||
            return $"{CSI}?{code}l";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Shows the cursor.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <remarks>
 | 
			
		||||
        /// See <see href="https://vt100.net/docs/vt510-rm/SM.html"/>.
 | 
			
		||||
        /// </remarks>
 | 
			
		||||
        /// <returns>The ANSI escape code.</returns>
 | 
			
		||||
        public static string SM(int code)
 | 
			
		||||
        {
 | 
			
		||||
            return $"{CSI}?{code}h";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// This control function erases characters on the line that has the cursor.
 | 
			
		||||
        /// EL clears all character attributes from erased character positions.
 | 
			
		||||
        /// EL works inside or outside the scrolling margins.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <remarks>
 | 
			
		||||
        /// See <see href="https://vt100.net/docs/vt510-rm/EL.html"/>.
 | 
			
		||||
        /// </remarks>
 | 
			
		||||
        /// <returns>The ANSI escape code.</returns>
 | 
			
		||||
        public static string EL(int code)
 | 
			
		||||
        {
 | 
			
		||||
            return $"{CSI}{code}K";
 | 
			
		||||
        }
 | 
			
		||||
        var joinedCodes = string.Join(";", codes.Select(c => c.ToString()));
 | 
			
		||||
        return $"{CSI}{joinedCodes}m";
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// This control function erases characters from part or all of the display.
 | 
			
		||||
    /// When you erase complete lines, they become single-height, single-width lines,
 | 
			
		||||
    /// with all visual character attributes cleared.
 | 
			
		||||
    /// ED works inside or outside the scrolling margins.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <remarks>
 | 
			
		||||
    /// See <see href="https://vt100.net/docs/vt510-rm/ED.html"/>.
 | 
			
		||||
    /// </remarks>
 | 
			
		||||
    /// <returns>The ANSI escape code.</returns>
 | 
			
		||||
    public static string ED(int code)
 | 
			
		||||
    {
 | 
			
		||||
        return $"{CSI}{code}J";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Moves the cursor up a specified number of lines in the same column.
 | 
			
		||||
    /// The cursor stops at the top margin.
 | 
			
		||||
    /// If the cursor is already above the top margin, then the cursor stops at the top line.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <remarks>
 | 
			
		||||
    /// See <see href="https://vt100.net/docs/vt510-rm/CUU.html"/>.
 | 
			
		||||
    /// </remarks>
 | 
			
		||||
    /// <param name="steps">The number of steps to move up.</param>
 | 
			
		||||
    /// <returns>The ANSI escape code.</returns>
 | 
			
		||||
    public static string CUU(int steps)
 | 
			
		||||
    {
 | 
			
		||||
        return $"{CSI}{steps}A";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// This control function moves the cursor down a specified number of lines in the same column.
 | 
			
		||||
    /// The cursor stops at the bottom margin.
 | 
			
		||||
    /// If the cursor is already below the bottom margin, then the cursor stops at the bottom line.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <remarks>
 | 
			
		||||
    /// See <see href="https://vt100.net/docs/vt510-rm/CUD.html"/>.
 | 
			
		||||
    /// </remarks>
 | 
			
		||||
    /// <param name="steps">The number of steps to move down.</param>
 | 
			
		||||
    /// <returns>The ANSI escape code.</returns>
 | 
			
		||||
    public static string CUD(int steps)
 | 
			
		||||
    {
 | 
			
		||||
        return $"{CSI}{steps}B";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// This control function moves the cursor to the right by a specified number of columns.
 | 
			
		||||
    /// The cursor stops at the right border of the page.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <remarks>
 | 
			
		||||
    /// See <see href="https://vt100.net/docs/vt510-rm/CUF.html"/>.
 | 
			
		||||
    /// </remarks>
 | 
			
		||||
    /// <param name="steps">The number of steps to move forward.</param>
 | 
			
		||||
    /// <returns>The ANSI escape code.</returns>
 | 
			
		||||
    public static string CUF(int steps)
 | 
			
		||||
    {
 | 
			
		||||
        return $"{CSI}{steps}C";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// This control function moves the cursor to the left by a specified number of columns.
 | 
			
		||||
    /// The cursor stops at the left border of the page.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <remarks>
 | 
			
		||||
    /// See <see href="https://vt100.net/docs/vt510-rm/CUB.html"/>.
 | 
			
		||||
    /// </remarks>
 | 
			
		||||
    /// <param name="steps">The number of steps to move backward.</param>
 | 
			
		||||
    /// <returns>The ANSI escape code.</returns>
 | 
			
		||||
    public static string CUB(int steps)
 | 
			
		||||
    {
 | 
			
		||||
        return $"{CSI}{steps}D";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Moves the cursor to the specified position.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="line">The line to move to.</param>
 | 
			
		||||
    /// <param name="column">The column to move to.</param>
 | 
			
		||||
    /// <remarks>
 | 
			
		||||
    /// See <see href="https://vt100.net/docs/vt510-rm/CUP.html"/>.
 | 
			
		||||
    /// </remarks>
 | 
			
		||||
    /// <returns>The ANSI escape code.</returns>
 | 
			
		||||
    public static string CUP(int line, int column)
 | 
			
		||||
    {
 | 
			
		||||
        return $"{CSI}{line};{column}H";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Hides the cursor.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <remarks>
 | 
			
		||||
    /// See <see href="https://vt100.net/docs/vt510-rm/RM.html"/>.
 | 
			
		||||
    /// </remarks>
 | 
			
		||||
    /// <returns>The ANSI escape code.</returns>
 | 
			
		||||
    public static string RM(int code)
 | 
			
		||||
    {
 | 
			
		||||
        return $"{CSI}?{code}l";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Shows the cursor.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <remarks>
 | 
			
		||||
    /// See <see href="https://vt100.net/docs/vt510-rm/SM.html"/>.
 | 
			
		||||
    /// </remarks>
 | 
			
		||||
    /// <returns>The ANSI escape code.</returns>
 | 
			
		||||
    public static string SM(int code)
 | 
			
		||||
    {
 | 
			
		||||
        return $"{CSI}?{code}h";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// This control function erases characters on the line that has the cursor.
 | 
			
		||||
    /// EL clears all character attributes from erased character positions.
 | 
			
		||||
    /// EL works inside or outside the scrolling margins.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <remarks>
 | 
			
		||||
    /// See <see href="https://vt100.net/docs/vt510-rm/EL.html"/>.
 | 
			
		||||
    /// </remarks>
 | 
			
		||||
    /// <returns>The ANSI escape code.</returns>
 | 
			
		||||
    public static string EL(int code)
 | 
			
		||||
    {
 | 
			
		||||
        return $"{CSI}{code}K";
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,57 +1,56 @@
 | 
			
		||||
using System;
 | 
			
		||||
using Spectre.Console.Rendering;
 | 
			
		||||
 | 
			
		||||
namespace Spectre.Console
 | 
			
		||||
namespace Spectre.Console;
 | 
			
		||||
 | 
			
		||||
internal sealed class AnsiConsoleFacade : IAnsiConsole
 | 
			
		||||
{
 | 
			
		||||
    internal sealed class AnsiConsoleFacade : IAnsiConsole
 | 
			
		||||
    private readonly object _renderLock;
 | 
			
		||||
    private readonly AnsiConsoleBackend _ansiBackend;
 | 
			
		||||
    private readonly LegacyConsoleBackend _legacyBackend;
 | 
			
		||||
 | 
			
		||||
    public Profile Profile { get; }
 | 
			
		||||
    public IAnsiConsoleCursor Cursor => GetBackend().Cursor;
 | 
			
		||||
    public IAnsiConsoleInput Input { get; }
 | 
			
		||||
    public IExclusivityMode ExclusivityMode { get; }
 | 
			
		||||
    public RenderPipeline Pipeline { get; }
 | 
			
		||||
 | 
			
		||||
    public AnsiConsoleFacade(Profile profile, IExclusivityMode exclusivityMode)
 | 
			
		||||
    {
 | 
			
		||||
        private readonly object _renderLock;
 | 
			
		||||
        private readonly AnsiConsoleBackend _ansiBackend;
 | 
			
		||||
        private readonly LegacyConsoleBackend _legacyBackend;
 | 
			
		||||
        _renderLock = new object();
 | 
			
		||||
 | 
			
		||||
        public Profile Profile { get; }
 | 
			
		||||
        public IAnsiConsoleCursor Cursor => GetBackend().Cursor;
 | 
			
		||||
        public IAnsiConsoleInput Input { get; }
 | 
			
		||||
        public IExclusivityMode ExclusivityMode { get; }
 | 
			
		||||
        public RenderPipeline Pipeline { get; }
 | 
			
		||||
        Profile = profile ?? throw new ArgumentNullException(nameof(profile));
 | 
			
		||||
        Input = new DefaultInput(Profile);
 | 
			
		||||
        ExclusivityMode = exclusivityMode ?? throw new ArgumentNullException(nameof(exclusivityMode));
 | 
			
		||||
        Pipeline = new RenderPipeline();
 | 
			
		||||
 | 
			
		||||
        public AnsiConsoleFacade(Profile profile, IExclusivityMode exclusivityMode)
 | 
			
		||||
        _ansiBackend = new AnsiConsoleBackend(this);
 | 
			
		||||
        _legacyBackend = new LegacyConsoleBackend(this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void Clear(bool home)
 | 
			
		||||
    {
 | 
			
		||||
        lock (_renderLock)
 | 
			
		||||
        {
 | 
			
		||||
            _renderLock = new object();
 | 
			
		||||
 | 
			
		||||
            Profile = profile ?? throw new ArgumentNullException(nameof(profile));
 | 
			
		||||
            Input = new DefaultInput(Profile);
 | 
			
		||||
            ExclusivityMode = exclusivityMode ?? throw new ArgumentNullException(nameof(exclusivityMode));
 | 
			
		||||
            Pipeline = new RenderPipeline();
 | 
			
		||||
 | 
			
		||||
            _ansiBackend = new AnsiConsoleBackend(this);
 | 
			
		||||
            _legacyBackend = new LegacyConsoleBackend(this);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Clear(bool home)
 | 
			
		||||
        {
 | 
			
		||||
            lock (_renderLock)
 | 
			
		||||
            {
 | 
			
		||||
                GetBackend().Clear(home);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Write(IRenderable renderable)
 | 
			
		||||
        {
 | 
			
		||||
            lock (_renderLock)
 | 
			
		||||
            {
 | 
			
		||||
                GetBackend().Write(renderable);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private IAnsiConsoleBackend GetBackend()
 | 
			
		||||
        {
 | 
			
		||||
            if (Profile.Capabilities.Ansi)
 | 
			
		||||
            {
 | 
			
		||||
                return _ansiBackend;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return _legacyBackend;
 | 
			
		||||
            GetBackend().Clear(home);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    public void Write(IRenderable renderable)
 | 
			
		||||
    {
 | 
			
		||||
        lock (_renderLock)
 | 
			
		||||
        {
 | 
			
		||||
            GetBackend().Write(renderable);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private IAnsiConsoleBackend GetBackend()
 | 
			
		||||
    {
 | 
			
		||||
        if (Profile.Capabilities.Ansi)
 | 
			
		||||
        {
 | 
			
		||||
            return _ansiBackend;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return _legacyBackend;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,27 +1,26 @@
 | 
			
		||||
using Spectre.Console.Rendering;
 | 
			
		||||
 | 
			
		||||
namespace Spectre.Console
 | 
			
		||||
namespace Spectre.Console;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// Represents a console backend.
 | 
			
		||||
/// </summary>
 | 
			
		||||
internal interface IAnsiConsoleBackend
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Represents a console backend.
 | 
			
		||||
    /// Gets the console cursor for the backend.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    internal interface IAnsiConsoleBackend
 | 
			
		||||
    {
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets the console cursor for the backend.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        IAnsiConsoleCursor Cursor { get; }
 | 
			
		||||
    IAnsiConsoleCursor Cursor { get; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Clears the console.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="home">If the cursor should be moved to the home position.</param>
 | 
			
		||||
        void Clear(bool home);
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Clears the console.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="home">If the cursor should be moved to the home position.</param>
 | 
			
		||||
    void Clear(bool home);
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Writes a <see cref="IRenderable"/> to the console backend.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="renderable">The <see cref="IRenderable"/> to write.</param>
 | 
			
		||||
        void Write(IRenderable renderable);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Writes a <see cref="IRenderable"/> to the console backend.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="renderable">The <see cref="IRenderable"/> to write.</param>
 | 
			
		||||
    void Write(IRenderable renderable);
 | 
			
		||||
}
 | 
			
		||||
@@ -1,70 +1,69 @@
 | 
			
		||||
using Spectre.Console.Rendering;
 | 
			
		||||
 | 
			
		||||
namespace Spectre.Console
 | 
			
		||||
namespace Spectre.Console;
 | 
			
		||||
 | 
			
		||||
internal sealed class LegacyConsoleBackend : IAnsiConsoleBackend
 | 
			
		||||
{
 | 
			
		||||
    internal sealed class LegacyConsoleBackend : IAnsiConsoleBackend
 | 
			
		||||
    private readonly IAnsiConsole _console;
 | 
			
		||||
    private Style _lastStyle;
 | 
			
		||||
 | 
			
		||||
    public IAnsiConsoleCursor Cursor { get; }
 | 
			
		||||
 | 
			
		||||
    public LegacyConsoleBackend(IAnsiConsole console)
 | 
			
		||||
    {
 | 
			
		||||
        private readonly IAnsiConsole _console;
 | 
			
		||||
        private Style _lastStyle;
 | 
			
		||||
        _console = console ?? throw new System.ArgumentNullException(nameof(console));
 | 
			
		||||
        _lastStyle = Style.Plain;
 | 
			
		||||
 | 
			
		||||
        public IAnsiConsoleCursor Cursor { get; }
 | 
			
		||||
        Cursor = new LegacyConsoleCursor();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        public LegacyConsoleBackend(IAnsiConsole console)
 | 
			
		||||
    public void Clear(bool home)
 | 
			
		||||
    {
 | 
			
		||||
        var (x, y) = (System.Console.CursorLeft, System.Console.CursorTop);
 | 
			
		||||
 | 
			
		||||
        System.Console.Clear();
 | 
			
		||||
 | 
			
		||||
        if (!home)
 | 
			
		||||
        {
 | 
			
		||||
            _console = console ?? throw new System.ArgumentNullException(nameof(console));
 | 
			
		||||
            _lastStyle = Style.Plain;
 | 
			
		||||
 | 
			
		||||
            Cursor = new LegacyConsoleCursor();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Clear(bool home)
 | 
			
		||||
        {
 | 
			
		||||
            var (x, y) = (System.Console.CursorLeft, System.Console.CursorTop);
 | 
			
		||||
 | 
			
		||||
            System.Console.Clear();
 | 
			
		||||
 | 
			
		||||
            if (!home)
 | 
			
		||||
            {
 | 
			
		||||
                // Set the cursor position
 | 
			
		||||
                System.Console.SetCursorPosition(x, y);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Write(IRenderable renderable)
 | 
			
		||||
        {
 | 
			
		||||
            foreach (var segment in renderable.GetSegments(_console))
 | 
			
		||||
            {
 | 
			
		||||
                if (segment.IsControlCode)
 | 
			
		||||
                {
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (_lastStyle?.Equals(segment.Style) != true)
 | 
			
		||||
                {
 | 
			
		||||
                    SetStyle(segment.Style);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                _console.Profile.Out.Writer.Write(segment.Text.NormalizeNewLines(native: true));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void SetStyle(Style style)
 | 
			
		||||
        {
 | 
			
		||||
            _lastStyle = style;
 | 
			
		||||
 | 
			
		||||
            System.Console.ResetColor();
 | 
			
		||||
 | 
			
		||||
            var background = Color.ToConsoleColor(style.Background);
 | 
			
		||||
            if (_console.Profile.Capabilities.ColorSystem != ColorSystem.NoColors && (int)background != -1)
 | 
			
		||||
            {
 | 
			
		||||
                System.Console.BackgroundColor = background;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            var foreground = Color.ToConsoleColor(style.Foreground);
 | 
			
		||||
            if (_console.Profile.Capabilities.ColorSystem != ColorSystem.NoColors && (int)foreground != -1)
 | 
			
		||||
            {
 | 
			
		||||
                System.Console.ForegroundColor = foreground;
 | 
			
		||||
            }
 | 
			
		||||
            // Set the cursor position
 | 
			
		||||
            System.Console.SetCursorPosition(x, y);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    public void Write(IRenderable renderable)
 | 
			
		||||
    {
 | 
			
		||||
        foreach (var segment in renderable.GetSegments(_console))
 | 
			
		||||
        {
 | 
			
		||||
            if (segment.IsControlCode)
 | 
			
		||||
            {
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (_lastStyle?.Equals(segment.Style) != true)
 | 
			
		||||
            {
 | 
			
		||||
                SetStyle(segment.Style);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            _console.Profile.Out.Writer.Write(segment.Text.NormalizeNewLines(native: true));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void SetStyle(Style style)
 | 
			
		||||
    {
 | 
			
		||||
        _lastStyle = style;
 | 
			
		||||
 | 
			
		||||
        System.Console.ResetColor();
 | 
			
		||||
 | 
			
		||||
        var background = Color.ToConsoleColor(style.Background);
 | 
			
		||||
        if (_console.Profile.Capabilities.ColorSystem != ColorSystem.NoColors && (int)background != -1)
 | 
			
		||||
        {
 | 
			
		||||
            System.Console.BackgroundColor = background;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var foreground = Color.ToConsoleColor(style.Foreground);
 | 
			
		||||
        if (_console.Profile.Capabilities.ColorSystem != ColorSystem.NoColors && (int)foreground != -1)
 | 
			
		||||
        {
 | 
			
		||||
            System.Console.ForegroundColor = foreground;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,40 +1,39 @@
 | 
			
		||||
namespace Spectre.Console
 | 
			
		||||
namespace Spectre.Console;
 | 
			
		||||
 | 
			
		||||
internal sealed class LegacyConsoleCursor : IAnsiConsoleCursor
 | 
			
		||||
{
 | 
			
		||||
    internal sealed class LegacyConsoleCursor : IAnsiConsoleCursor
 | 
			
		||||
    public void Show(bool show)
 | 
			
		||||
    {
 | 
			
		||||
        public void Show(bool show)
 | 
			
		||||
        System.Console.CursorVisible = show;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void Move(CursorDirection direction, int steps)
 | 
			
		||||
    {
 | 
			
		||||
        if (steps == 0)
 | 
			
		||||
        {
 | 
			
		||||
            System.Console.CursorVisible = show;
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Move(CursorDirection direction, int steps)
 | 
			
		||||
        switch (direction)
 | 
			
		||||
        {
 | 
			
		||||
            if (steps == 0)
 | 
			
		||||
            {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            switch (direction)
 | 
			
		||||
            {
 | 
			
		||||
                case CursorDirection.Up:
 | 
			
		||||
                    System.Console.CursorTop -= steps;
 | 
			
		||||
                    break;
 | 
			
		||||
                case CursorDirection.Down:
 | 
			
		||||
                    System.Console.CursorTop += steps;
 | 
			
		||||
                    break;
 | 
			
		||||
                case CursorDirection.Left:
 | 
			
		||||
                    System.Console.CursorLeft -= steps;
 | 
			
		||||
                    break;
 | 
			
		||||
                case CursorDirection.Right:
 | 
			
		||||
                    System.Console.CursorLeft += steps;
 | 
			
		||||
                    break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void SetPosition(int x, int y)
 | 
			
		||||
        {
 | 
			
		||||
            System.Console.CursorLeft = x;
 | 
			
		||||
            System.Console.CursorTop = y;
 | 
			
		||||
            case CursorDirection.Up:
 | 
			
		||||
                System.Console.CursorTop -= steps;
 | 
			
		||||
                break;
 | 
			
		||||
            case CursorDirection.Down:
 | 
			
		||||
                System.Console.CursorTop += steps;
 | 
			
		||||
                break;
 | 
			
		||||
            case CursorDirection.Left:
 | 
			
		||||
                System.Console.CursorLeft -= steps;
 | 
			
		||||
                break;
 | 
			
		||||
            case CursorDirection.Right:
 | 
			
		||||
                System.Console.CursorLeft += steps;
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    public void SetPosition(int x, int y)
 | 
			
		||||
    {
 | 
			
		||||
        System.Console.CursorLeft = x;
 | 
			
		||||
        System.Console.CursorTop = y;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user