mirror of
https://github.com/nsnail/spectre.console.git
synced 2025-06-19 13:28:16 +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