Add support for moving the cursor

This commit is contained in:
Patrik Svensson 2020-10-28 10:51:25 +01:00 committed by Patrik Svensson
parent 93d1971f48
commit a1d11e9d0c
29 changed files with 543 additions and 116 deletions

View File

@ -15,7 +15,7 @@
]
},
"dotnet-example": {
"version": "0.8.0",
"version": "0.10.0",
"commands": [
"dotnet-example"
]

View File

@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<IsPackable>false</IsPackable>
<Title>Cursor</Title>
<Description>Demonstrates how to move the cursor.</Description>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,21 @@
using System;
using Spectre.Console;
namespace Cursor
{
public static class Program
{
public static void Main(string[] args)
{
AnsiConsole.Write("Hello");
// Move the cursor 3 cells to the right
AnsiConsole.Cursor.Move(CursorDirection.Right, 3);
AnsiConsole.Write("World");
// Move the cursor 5 cells to the left.
AnsiConsole.Cursor.Move(CursorDirection.Left, 5);
AnsiConsole.WriteLine("Universe");
}
}
}

View File

@ -17,6 +17,7 @@ namespace Spectre.Console.Tests
public Encoding Encoding => _console.Encoding;
public int Width { get; }
public int Height => _console.Height;
public IAnsiConsoleCursor Cursor => _console.Cursor;
public TestableAnsiConsole(ColorSystem system, AnsiSupport ansi = AnsiSupport.Yes, int width = 80)
{
@ -37,6 +38,11 @@ namespace Spectre.Console.Tests
_writer?.Dispose();
}
public void Clear(bool home)
{
_console.Clear(home);
}
public void Write(Segment segment)
{
_console.Write(segment);

View File

@ -14,6 +14,7 @@ namespace Spectre.Console.Tests.Tools
public Capabilities Capabilities => _console.Capabilities;
public Encoding Encoding => _console.Encoding;
public IAnsiConsoleCursor Cursor => _console.Cursor;
public int Width { get; }
public int Height => _console.Height;
@ -36,6 +37,11 @@ namespace Spectre.Console.Tests.Tools
_writer?.Dispose();
}
public void Clear(bool home)
{
_console.Clear(home);
}
public void Write(Segment segment)
{
_console.Write(segment);

View File

@ -11,6 +11,7 @@ namespace Spectre.Console.Tests
{
public Capabilities Capabilities { get; }
public Encoding Encoding { get; }
public IAnsiConsoleCursor Cursor => throw new NotSupportedException();
public int Width { get; }
public int Height { get; }
@ -42,6 +43,10 @@ namespace Spectre.Console.Tests
Writer.Dispose();
}
public void Clear(bool home)
{
}
public void Write(Segment segment)
{
if (segment is null)

View File

@ -48,6 +48,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Calendars", "..\examples\Ca
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Rules", "..\examples\Rules\Rules.csproj", "{8622A261-02C6-40CA-9797-E3F01ED87D6B}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cursor", "..\examples\Cursor\Cursor.csproj", "{75C608C3-ABB4-4168-A229-7F8250B946D1}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -226,6 +228,18 @@ Global
{8622A261-02C6-40CA-9797-E3F01ED87D6B}.Release|x64.Build.0 = Release|Any CPU
{8622A261-02C6-40CA-9797-E3F01ED87D6B}.Release|x86.ActiveCfg = Release|Any CPU
{8622A261-02C6-40CA-9797-E3F01ED87D6B}.Release|x86.Build.0 = Release|Any CPU
{75C608C3-ABB4-4168-A229-7F8250B946D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{75C608C3-ABB4-4168-A229-7F8250B946D1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{75C608C3-ABB4-4168-A229-7F8250B946D1}.Debug|x64.ActiveCfg = Debug|Any CPU
{75C608C3-ABB4-4168-A229-7F8250B946D1}.Debug|x64.Build.0 = Debug|Any CPU
{75C608C3-ABB4-4168-A229-7F8250B946D1}.Debug|x86.ActiveCfg = Debug|Any CPU
{75C608C3-ABB4-4168-A229-7F8250B946D1}.Debug|x86.Build.0 = Debug|Any CPU
{75C608C3-ABB4-4168-A229-7F8250B946D1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{75C608C3-ABB4-4168-A229-7F8250B946D1}.Release|Any CPU.Build.0 = Release|Any CPU
{75C608C3-ABB4-4168-A229-7F8250B946D1}.Release|x64.ActiveCfg = Release|Any CPU
{75C608C3-ABB4-4168-A229-7F8250B946D1}.Release|x64.Build.0 = Release|Any CPU
{75C608C3-ABB4-4168-A229-7F8250B946D1}.Release|x86.ActiveCfg = Release|Any CPU
{75C608C3-ABB4-4168-A229-7F8250B946D1}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -244,6 +258,7 @@ Global
{C3E2CB5C-1517-4C75-B59A-93D4E22BEC8D} = {20595AD4-8D75-4AF8-B6BC-9C38C160423F}
{57691C7D-683D-46E6-AA4F-57A8C5F65D25} = {F0575243-121F-4DEE-9F6B-246E26DC0844}
{8622A261-02C6-40CA-9797-E3F01ED87D6B} = {F0575243-121F-4DEE-9F6B-246E26DC0844}
{75C608C3-ABB4-4168-A229-7F8250B946D1} = {F0575243-121F-4DEE-9F6B-246E26DC0844}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {5729B071-67A0-48FB-8B1B-275E6822086C}

View File

@ -27,6 +27,11 @@ namespace Spectre.Console
/// </summary>
public static IAnsiConsole Console => _recorder ?? _console.Value;
/// <summary>
/// Gets the <see cref="IAnsiConsoleCursor"/>.
/// </summary>
public static IAnsiConsoleCursor Cursor => _recorder?.Cursor ?? _console.Value.Cursor;
/// <summary>
/// Gets the console's capabilities.
/// </summary>
@ -56,7 +61,7 @@ namespace Spectre.Console
/// <returns>An <see cref="IAnsiConsole"/> instance.</returns>
public static IAnsiConsole Create(AnsiConsoleSettings settings)
{
return AnsiConsoleBuilder.Build(settings);
return BackendBuilder.Build(settings);
}
}
}

View File

@ -0,0 +1,28 @@
namespace Spectre.Console
{
/// <summary>
/// Represents cursor direction.
/// </summary>
public enum CursorDirection
{
/// <summary>
/// Up
/// </summary>
Up,
/// <summary>
/// Down
/// </summary>
Down,
/// <summary>
/// Left
/// </summary>
Left,
/// <summary>
/// Right
/// </summary>
Right,
}
}

View File

@ -0,0 +1,152 @@
namespace Spectre.Console
{
/// <summary>
/// Contains extension methods for <see cref="IAnsiConsoleCursor"/>.
/// </summary>
public static class CursorExtensions
{
/// <summary>
/// Shows the cursor.
/// </summary>
/// <param name="cursor">The cursor.</param>
public static void Show(this IAnsiConsoleCursor cursor)
{
if (cursor is null)
{
throw new System.ArgumentNullException(nameof(cursor));
}
cursor.Show(true);
}
/// <summary>
/// Hides the cursor.
/// </summary>
/// <param name="cursor">The cursor.</param>
public static void Hide(this IAnsiConsoleCursor cursor)
{
if (cursor is null)
{
throw new System.ArgumentNullException(nameof(cursor));
}
cursor.Show(false);
}
/// <summary>
/// Moves the cursor up.
/// </summary>
/// <param name="cursor">The cursor.</param>
public static void MoveUp(this IAnsiConsoleCursor cursor)
{
if (cursor is null)
{
throw new System.ArgumentNullException(nameof(cursor));
}
cursor.Move(CursorDirection.Up, 1);
}
/// <summary>
/// Moves the cursor up.
/// </summary>
/// <param name="cursor">The cursor.</param>
/// <param name="steps">The number of steps to move the cursor.</param>
public static void MoveUp(this IAnsiConsoleCursor cursor, int steps)
{
if (cursor is null)
{
throw new System.ArgumentNullException(nameof(cursor));
}
cursor.Move(CursorDirection.Up, steps);
}
/// <summary>
/// Moves the cursor down.
/// </summary>
/// <param name="cursor">The cursor.</param>
public static void MoveDown(this IAnsiConsoleCursor cursor)
{
if (cursor is null)
{
throw new System.ArgumentNullException(nameof(cursor));
}
cursor.Move(CursorDirection.Down, 1);
}
/// <summary>
/// Moves the cursor down.
/// </summary>
/// <param name="cursor">The cursor.</param>
/// <param name="steps">The number of steps to move the cursor.</param>
public static void MoveDown(this IAnsiConsoleCursor cursor, int steps)
{
if (cursor is null)
{
throw new System.ArgumentNullException(nameof(cursor));
}
cursor.Move(CursorDirection.Down, steps);
}
/// <summary>
/// Moves the cursor to the left.
/// </summary>
/// <param name="cursor">The cursor.</param>
public static void MoveLeft(this IAnsiConsoleCursor cursor)
{
if (cursor is null)
{
throw new System.ArgumentNullException(nameof(cursor));
}
cursor.Move(CursorDirection.Left, 1);
}
/// <summary>
/// Moves the cursor to the left.
/// </summary>
/// <param name="cursor">The cursor.</param>
/// <param name="steps">The number of steps to move the cursor.</param>
public static void MoveLeft(this IAnsiConsoleCursor cursor, int steps)
{
if (cursor is null)
{
throw new System.ArgumentNullException(nameof(cursor));
}
cursor.Move(CursorDirection.Left, steps);
}
/// <summary>
/// Moves the cursor to the right.
/// </summary>
/// <param name="cursor">The cursor.</param>
public static void MoveRight(this IAnsiConsoleCursor cursor)
{
if (cursor is null)
{
throw new System.ArgumentNullException(nameof(cursor));
}
cursor.Move(CursorDirection.Right, 1);
}
/// <summary>
/// Moves the cursor to the right.
/// </summary>
/// <param name="cursor">The cursor.</param>
/// <param name="steps">The number of steps to move the cursor.</param>
public static void MoveRight(this IAnsiConsoleCursor cursor, int steps)
{
if (cursor is null)
{
throw new System.ArgumentNullException(nameof(cursor));
}
cursor.Move(CursorDirection.Right, steps);
}
}
}

View File

@ -1,4 +1,5 @@
using System;
using Spectre.Console.Internal;
using Spectre.Console.Rendering;
namespace Spectre.Console

View File

@ -18,6 +18,11 @@ namespace Spectre.Console
/// </summary>
Encoding Encoding { get; }
/// <summary>
/// Gets the console cursor.
/// </summary>
IAnsiConsoleCursor Cursor { get; }
/// <summary>
/// Gets the buffer width of the console.
/// </summary>
@ -28,6 +33,12 @@ namespace Spectre.Console
/// </summary>
int Height { 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>
/// Writes a string followed by a line terminator to the console.
/// </summary>

View File

@ -0,0 +1,28 @@
namespace Spectre.Console
{
/// <summary>
/// Represents the console's cursor.
/// </summary>
public interface IAnsiConsoleCursor
{
/// <summary>
/// Shows or hides the cursor.
/// </summary>
/// <param name="show"><c>true</c> to show the cursor, <c>false</c> to hide it.</param>
void Show(bool show);
/// <summary>
/// Sets the cursor position.
/// </summary>
/// <param name="column">The column to move the cursor to.</param>
/// <param name="line">The line to move the cursor to.</param>
void SetPosition(int column, int line);
/// <summary>
/// Moves the cursor relative to the current position.
/// </summary>
/// <param name="direction">The direction to move the cursor.</param>
/// <param name="steps">The number of steps to move the cursor.</param>
void Move(CursorDirection direction, int steps);
}
}

View File

@ -5,13 +5,15 @@ using Spectre.Console.Rendering;
namespace Spectre.Console.Internal
{
internal sealed class AnsiConsoleRenderer : IAnsiConsole
internal sealed class AnsiBackend : IAnsiConsole
{
private readonly TextWriter _out;
private readonly AnsiBuilder _ansiBuilder;
private readonly AnsiCursor _cursor;
public Capabilities Capabilities { get; }
public Encoding Encoding { get; }
public IAnsiConsoleCursor Cursor => _cursor;
public int Width
{
@ -39,7 +41,7 @@ namespace Spectre.Console.Internal
}
}
public AnsiConsoleRenderer(TextWriter @out, Capabilities capabilities, ILinkIdentityGenerator? linkHasher)
public AnsiBackend(TextWriter @out, Capabilities capabilities, ILinkIdentityGenerator? linkHasher)
{
_out = @out ?? throw new ArgumentNullException(nameof(@out));
@ -47,6 +49,17 @@ namespace Spectre.Console.Internal
Encoding = _out.IsStandardOut() ? System.Console.OutputEncoding : Encoding.UTF8;
_ansiBuilder = new AnsiBuilder(Capabilities, linkHasher);
_cursor = new AnsiCursor(this);
}
public void Clear(bool home)
{
Write(Segment.Control("\u001b[2J"));
if (home)
{
Cursor.SetPosition(0, 0);
}
}
public void Write(Segment segment)

View File

@ -0,0 +1,56 @@
using System;
using Spectre.Console.Rendering;
namespace Spectre.Console.Internal
{
internal sealed class AnsiCursor : IAnsiConsoleCursor
{
private readonly AnsiBackend _renderer;
public AnsiCursor(AnsiBackend renderer)
{
_renderer = renderer ?? throw new ArgumentNullException(nameof(renderer));
}
public void Show(bool show)
{
if (show)
{
_renderer.Write(Segment.Control("\u001b[?25h"));
}
else
{
_renderer.Write(Segment.Control("\u001b[?25l"));
}
}
public void Move(CursorDirection direction, int steps)
{
if (steps == 0)
{
return;
}
switch (direction)
{
case CursorDirection.Up:
_renderer.Write(Segment.Control($"\u001b[{steps}A"));
break;
case CursorDirection.Down:
_renderer.Write(Segment.Control($"\u001b[{steps}B"));
break;
case CursorDirection.Right:
_renderer.Write(Segment.Control($"\u001b[{steps}C"));
break;
case CursorDirection.Left:
_renderer.Write(Segment.Control($"\u001b[{steps}D"));
break;
}
}
public void SetPosition(int column, int line)
{
_renderer.Write(Segment.Control($"\u001b[{line};{column}H"));
}
}
}

View File

@ -3,7 +3,7 @@ using System.Runtime.InteropServices;
namespace Spectre.Console.Internal
{
internal static class AnsiConsoleBuilder
internal static class BackendBuilder
{
public static IAnsiConsole Build(AnsiConsoleSettings settings)
{
@ -60,11 +60,11 @@ namespace Spectre.Console.Internal
// Create the renderer
if (supportsAnsi)
{
return new AnsiConsoleRenderer(buffer, capabilities, settings.LinkIdentityGenerator);
return new AnsiBackend(buffer, capabilities, settings.LinkIdentityGenerator);
}
else
{
return new FallbackConsoleRenderer(buffer, capabilities);
return new FallbackBackend(buffer, capabilities);
}
}
}

View File

@ -0,0 +1,89 @@
using System;
using System.IO;
using System.Text;
using Spectre.Console.Rendering;
namespace Spectre.Console.Internal
{
internal sealed class FallbackBackend : IAnsiConsole
{
private readonly ColorSystem _system;
private readonly FallbackCursor _cursor;
private Style? _lastStyle;
public Capabilities Capabilities { get; }
public Encoding Encoding { get; }
public IAnsiConsoleCursor Cursor => _cursor;
public int Width
{
get { return ConsoleHelper.GetSafeBufferWidth(Constants.DefaultBufferWidth); }
}
public int Height
{
get { return ConsoleHelper.GetSafeBufferHeight(Constants.DefaultBufferHeight); }
}
public FallbackBackend(TextWriter @out, Capabilities capabilities)
{
if (capabilities == null)
{
throw new ArgumentNullException(nameof(capabilities));
}
_system = capabilities.ColorSystem;
_cursor = new FallbackCursor();
if (@out != System.Console.Out)
{
System.Console.SetOut(@out ?? throw new ArgumentNullException(nameof(@out)));
}
Encoding = System.Console.OutputEncoding;
Capabilities = capabilities;
}
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(Segment segment)
{
if (_lastStyle?.Equals(segment.Style) != true)
{
SetStyle(segment.Style);
}
System.Console.Write(segment.Text.NormalizeLineEndings(native: true));
}
private void SetStyle(Style style)
{
_lastStyle = style;
System.Console.ResetColor();
var background = Color.ToConsoleColor(style.Background);
if (_system != ColorSystem.NoColors && (int)background != -1)
{
System.Console.BackgroundColor = background;
}
var foreground = Color.ToConsoleColor(style.Foreground);
if (_system != ColorSystem.NoColors && (int)foreground != -1)
{
System.Console.ForegroundColor = foreground;
}
}
}
}

View File

@ -0,0 +1,40 @@
namespace Spectre.Console.Internal
{
internal sealed class FallbackCursor : IAnsiConsoleCursor
{
public void Show(bool show)
{
System.Console.CursorVisible = show;
}
public void Move(CursorDirection direction, int steps)
{
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;
}
}
}

View File

@ -2,7 +2,7 @@ using System;
using System.Collections;
using System.Collections.Generic;
namespace Spectre.Console.Internal.Collections
namespace Spectre.Console.Internal
{
internal sealed class ListWithCallback<T> : IList<T>, IReadOnlyList<T>
{

View File

@ -1,10 +1,9 @@
using System;
using System.Linq;
using System.Text;
using Spectre.Console.Internal;
using Spectre.Console.Rendering;
namespace Spectre.Console
namespace Spectre.Console.Internal
{
internal static class ExceptionFormatter
{

View File

@ -1,97 +0,0 @@
using System;
using System.IO;
using System.Text;
using Spectre.Console.Rendering;
namespace Spectre.Console.Internal
{
internal sealed class FallbackConsoleRenderer : IAnsiConsole
{
private readonly TextWriter _out;
private readonly ColorSystem _system;
private Style? _lastStyle;
public Capabilities Capabilities { get; }
public Encoding Encoding { get; }
public int Width
{
get
{
if (_out.IsStandardOut())
{
return ConsoleHelper.GetSafeBufferWidth(Constants.DefaultBufferWidth);
}
return Constants.DefaultBufferWidth;
}
}
public int Height
{
get
{
if (_out.IsStandardOut())
{
return ConsoleHelper.GetSafeBufferHeight(Constants.DefaultBufferHeight);
}
return Constants.DefaultBufferHeight;
}
}
public FallbackConsoleRenderer(TextWriter @out, Capabilities capabilities)
{
if (capabilities == null)
{
throw new ArgumentNullException(nameof(capabilities));
}
_out = @out ?? throw new ArgumentNullException(nameof(@out));
_system = capabilities.ColorSystem;
if (_out.IsStandardOut())
{
Encoding = System.Console.OutputEncoding;
}
else
{
Encoding = Encoding.UTF8;
}
Capabilities = capabilities;
}
public void Write(Segment segment)
{
if (_lastStyle?.Equals(segment.Style) != true)
{
SetStyle(segment.Style);
}
_out.Write(segment.Text.NormalizeLineEndings(native: true));
}
private void SetStyle(Style style)
{
_lastStyle = style;
if (_out.IsStandardOut())
{
System.Console.ResetColor();
var background = Color.ToConsoleColor(style.Background);
if (_system != ColorSystem.NoColors && _out.IsStandardOut() && (int)background != -1)
{
System.Console.BackgroundColor = background;
}
var foreground = Color.ToConsoleColor(style.Foreground);
if (_system != ColorSystem.NoColors && _out.IsStandardOut() && (int)foreground != -1)
{
System.Console.ForegroundColor = foreground;
}
}
}
}
}

View File

@ -19,6 +19,9 @@ namespace Spectre.Console
/// <inheritdoc/>
public Encoding Encoding => _console.Encoding;
/// <inheritdoc/>
public IAnsiConsoleCursor Cursor => _console.Cursor;
/// <inheritdoc/>
public int Width => _console.Width;
@ -41,10 +44,26 @@ namespace Spectre.Console
// Only used for scoping.
}
/// <inheritdoc/>
public void Clear(bool home)
{
_console.Clear(home);
}
/// <inheritdoc/>
public void Write(Segment segment)
{
_recorded.Add(segment);
if (segment is null)
{
throw new ArgumentNullException(nameof(segment));
}
// Don't record control codes.
if (!segment.IsControlCode)
{
_recorded.Add(segment);
}
_console.Write(segment);
}

View File

@ -31,6 +31,12 @@ namespace Spectre.Console.Rendering
/// </summary>
public bool IsWhiteSpace { get; }
/// <summary>
/// Gets a value indicating whether or not his is a
/// control code such as cursor movement.
/// </summary>
public bool IsControlCode { get; }
/// <summary>
/// Gets the segment style.
/// </summary>
@ -39,12 +45,12 @@ namespace Spectre.Console.Rendering
/// <summary>
/// Gets a segment representing a line break.
/// </summary>
public static Segment LineBreak { get; } = new Segment(Environment.NewLine, Style.Plain, true);
public static Segment LineBreak { get; } = new Segment(Environment.NewLine, Style.Plain, true, false);
/// <summary>
/// Gets an empty segment.
/// </summary>
public static Segment Empty { get; } = new Segment(string.Empty, Style.Plain, false);
public static Segment Empty { get; } = new Segment(string.Empty, Style.Plain, false, false);
/// <summary>
/// Initializes a new instance of the <see cref="Segment"/> class.
@ -61,16 +67,27 @@ namespace Spectre.Console.Rendering
/// <param name="text">The segment text.</param>
/// <param name="style">The segment style.</param>
public Segment(string text, Style style)
: this(text, style, false)
: this(text, style, false, false)
{
}
private Segment(string text, Style style, bool lineBreak)
private Segment(string text, Style style, bool lineBreak, bool control)
{
Text = text?.NormalizeLineEndings() ?? throw new ArgumentNullException(nameof(text));
Style = style ?? throw new ArgumentNullException(nameof(style));
IsLineBreak = lineBreak;
IsWhiteSpace = string.IsNullOrWhiteSpace(text);
IsControlCode = control;
}
/// <summary>
/// Creates a control segment.
/// </summary>
/// <param name="control">The control code.</param>
/// <returns>A segment representing a control code.</returns>
public static Segment Control(string control)
{
return new Segment(control, Style.Plain, false, true);
}
/// <summary>

View File

@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Spectre.Console.Internal;
using Spectre.Console.Internal.Collections;
using Spectre.Console.Rendering;
namespace Spectre.Console

View File

@ -1,7 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Spectre.Console.Internal.Collections;
using Spectre.Console.Internal;
using Spectre.Console.Rendering;
namespace Spectre.Console

View File

@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.Linq;
using Spectre.Console.Internal;
using Spectre.Console.Rendering;
using Spectre.Console.Widgets;
namespace Spectre.Console
{

View File

@ -3,7 +3,7 @@ using System.Collections;
using System.Collections.Generic;
using Spectre.Console.Rendering;
namespace Spectre.Console.Widgets
namespace Spectre.Console
{
/// <summary>
/// Represents a table row.