Spectre.Console для C# (.NET): Полное руководство
Spectre.Console — это мощная библиотека для .NET, которая превращает обычный консольный вывод в нечто визуально привлекательное и интерактивное. Она позволяет разработчикам C# создавать красивые, структурированные и информативные интерфейсы командной строки (CLI) с минимальными усилиями. Вместо скучного монохромного текста вы получаете поддержку цветов, таблиц, деревьев, прогресс-баров, диаграмм и даже интерактивных подсказок.
Зачем нужна Spectre.Console? В современной разработке консольные приложения часто используются для DevOps-инструментов, скриптов автоматизации, анализа данных и утилит. Пользователи ожидают от них не только функциональности, но и удобства. Spectre.Console решает проблему «голого» вывода, делая логи понятными, а взаимодействие с приложением — приятным. Она особенно полезна для создания профессиональных CLI-инструментов, где важна читаемость и визуальная иерархия данных.
Библиотека построена на принципах композиции и предоставляет богатый набор «виджетов» (widgets), которые можно комбинировать. Она активно поддерживается сообществом, имеет подробную документацию и используется в таких крупных проектах, как Cake Build, NuGet Client и .NET SDK. Если вы пишете на C# и хотите, чтобы ваша консольная утилита выглядела как современный инструмент, а не как relic из 80-х — Spectre.Console — ваш выбор.
Установка
Spectre.Console распространяется через NuGet. Для установки выполните одну из следующих команд в терминале или консоли диспетчера пакетов:
# Через .NET CLI
dotnet add package Spectre.Console
# Через Package Manager Console (Visual Studio)
Install-Package Spectre.Console
# Или через NuGet Package Manager в вашей IDE
После установки добавьте using Spectre.Console; в начало вашего файла с кодом.
Быстрый старт
Минимальный рабочий пример, демонстрирующий основные возможности:
using Spectre.Console;
// Простое цветное сообщение
AnsiConsole.MarkupLine("[bold green]Привет, Spectre.Console![/]");
// Таблица
var table = new Table();
table.AddColumn("Имя");
table.AddColumn("Возраст");
table.AddRow("Алиса", "30");
table.AddRow("Боб", "25");
AnsiConsole.Write(table);
// Прогресс-бар
AnsiConsole.Progress()
.Start(ctx =>
{
var task = ctx.AddTask("[yellow]Обработка...[/]");
while (!task.IsFinished)
{
task.Increment(15);
Thread.Sleep(200);
}
});
Этот код выведет цветное приветствие, таблицу с данными и анимированный прогресс-бар.
Основные методы, функции и классы
1. AnsiConsole.MarkupLine
Сигнатура: public static void MarkupLine(string markup)
Что делает: Выводит строку с поддержкой разметки (markup) для стилизации текста (цвета, стили, ссылки).
AnsiConsole.MarkupLine("[bold yellow]Внимание![/] [red]Ошибка[/] в строке [underline]42[/].");
2. AnsiConsole.Progress
Сигнатура: public static Progress Display(ProgressContext context) (через fluent API)
Что делает: Создает и отображает интерактивный прогресс-бар с возможностью добавления нескольких задач.
AnsiConsole.Progress()
.Columns(new TaskDescriptionColumn(), new ProgressBarColumn(), new PercentageColumn())
.Start(ctx =>
{
var task1 = ctx.AddTask("[green]Загрузка файла 1[/]");
var task2 = ctx.AddTask("[blue]Загрузка файла 2[/]");
while (!task1.IsFinished || !task2.IsFinished)
{
task1.Increment(5);
task2.Increment(3);
Thread.Sleep(100);
}
});
3. Table
Сигнатура: public class Table : Renderable, IHasTableBorder
Что делает: Создает форматированную таблицу с заголовками, строками и настраиваемыми границами.
var table = new Table();
table.Border(TableBorder.Rounded);
table.AddColumn("[bold]ID[/]");
table.AddColumn("[bold]Имя[/]");
table.AddColumn("[bold]Статус[/]");
table.AddRow("1", "Алиса", "[green]Активен[/]");
table.AddRow("2", "Боб", "[red]Заблокирован[/]");
AnsiConsole.Write(table);
4. Tree
Сигнатура: public class Tree : Renderable
Что делает: Отображает иерархические данные в виде дерева с раскрывающимися узлами.
var tree = new Tree("Корень");
var node1 = tree.AddNode("Ветвь 1");
node1.AddNode("Лист 1.1");
node1.AddNode("Лист 1.2");
var node2 = tree.AddNode("Ветвь 2");
node2.AddNode("Лист 2.1");
AnsiConsole.Write(tree);
5. AnsiConsole.Prompt
Сигнатура: public static T Prompt
Что делает: Запрашивает ввод от пользователя с валидацией и автодополнением.
var name = AnsiConsole.Prompt(
new TextPrompt("Введите ваше имя:")
.PromptStyle("green")
.ValidationErrorMessage("[red]Имя не может быть пустым[/]")
.Validate(name => !string.IsNullOrWhiteSpace(name)));
var color = AnsiConsole.Prompt(
new SelectionPrompt()
.Title("Выберите цвет:")
.AddChoices(new[] { "Красный", "Зеленый", "Синий" }));
AnsiConsole.MarkupLine($"Привет, [bold]{name}[/]! Вы выбрали [underline]{color}[/].");
6. Calendar
Сигнатура: public class Calendar : Renderable
Что делает: Отображает календарь на указанный месяц/год с подсветкой дат.
var calendar = new Calendar(2024, 12);
calendar.HighlightStyle(Style.Plain);
calendar.AddCalendarEvent(2024, 12, 25); // Рождество
AnsiConsole.Write(calendar);
7. AnsiConsole.Status
Сигнатура: public static Status Display(StatusContext context) (через fluent API)
Что делает: Отображает спиннер с текстом статуса во время выполнения длительной операции.
AnsiConsole.Status()
.Start("Обработка...", ctx =>
{
ctx.Spinner(Spinner.Known.Dots);
Thread.Sleep(3000);
ctx.Status("Почти готово...");
Thread.Sleep(2000);
});
AnsiConsole.MarkupLine("[green]Готово![/]");
8. Rule
Сигнатура: public class Rule : Renderable
Что делает: Рисует горизонтальную разделительную линию (rule) с опциональным заголовком.
AnsiConsole.Write(new Rule("[yellow]Раздел 1[/]") { Style = Style.Parse("grey") });
AnsiConsole.MarkupLine("Текст раздела...");
AnsiConsole.Write(new Rule("[blue]Раздел 2[/]") { Style = Style.Parse("grey") });
9. AnsiConsole.Markup
Сигнатура: public static void Markup(string markup)
Что делает: Выводит строку с разметкой без перевода строки в конце.
AnsiConsole.Markup("[bold]Загрузка:[/] ");
for (int i = 0; i < 10; i++)
{
AnsiConsole.Markup("[green]#[/]");
Thread.Sleep(100);
}
AnsiConsole.MarkupLine(" [green]100%[/]");
10. Panel
Сигнатура: public class Panel : Renderable
Что делает: Оборачивает содержимое в рамку (панель) с заголовком.
var panel = new Panel("Это текст внутри панели.");
panel.Header("Информация");
panel.Border(BoxBorder.Rounded);
AnsiConsole.Write(panel);
Реальный практический пример
Создадим консольное приложение для анализа логов. Оно будет читать файл логов, отображать статистику в таблице, показывать прогресс обработки и выводить ошибки в виде дерева.
using Spectre.Console;
using System.Text.RegularExpressions;
class LogAnalyzer
{
static async Task Main(string[] args)
{
AnsiConsole.Write(new Rule("[bold yellow]Анализатор логов[/]"));
// Запрос пути к файлу
var filePath = AnsiConsole.Prompt(
new TextPrompt("Введите путь к файлу лога:")
.PromptStyle("cyan")
.Validate(path => File.Exists(path) ? ValidationResult.Success() : ValidationResult.Error("[red]Файл не найден[/]")));
// Чтение и парсинг логов с прогрессом
var logs = new List();
var errors = new List();
await AnsiConsole.Progress()
.Columns(new TaskDescriptionColumn(), new ProgressBarColumn(), new PercentageColumn())
.StartAsync(async ctx =>
{
var task = ctx.AddTask("[green]Чтение и обработка логов[/]");
var lines = await File.ReadAllLinesAsync(filePath);
task.MaxValue = lines.Length;
foreach (var line in lines)
{
var match = Regex.Match(line, @"\[(?\w+)\]\s+(?.+)");
if (match.Success)
{
logs.Add(new LogEntry
{
Level = match.Groups["level"].Value,
Message = match.Groups["message"].Value
});
if (match.Groups["level"].Value == "ERROR")
errors.Add(match.Groups["message"].Value);
}
task.Increment(1);
await Task.Delay(5); // Имитация обработки
}
});
// Статистика в таблице
var table = new Table();
table.Border(TableBorder.Rounded);
table.AddColumn("[bold]Уровень[/]");
table.AddColumn("[bold]Количество[/]");
var groups = logs.GroupBy(l => l.Level);
foreach (var group in groups)
{
var color = group.Key switch
{
"ERROR" => "red",
"WARN" => "yellow",
"INFO" => "green",
_ => "white"
};
table.AddRow($"[{color}]{group.Key}[/]", group.Count().ToString());
}
AnsiConsole.Write(new Rule("[bold]Статистика[/]"));
AnsiConsole.Write(table);
// Дерево ошибок
if (errors.Any())
{
AnsiConsole.Write(new Rule("[bold red]Ошибки[/]"));
var tree = new Tree($"[red]Найдено {errors.Count} ошибок[/]");
foreach (var error in errors.Take(10)) // Показываем первые 10
{
tree.AddNode($"[red]{error}[/]");
}
if (errors.Count > 10)
tree.AddNode($"[grey]... и еще {errors.Count - 10} ошибок[/]");
AnsiConsole.Write(tree);
}
// Интерактивный запрос на сохранение отчета
if (AnsiConsole.Confirm("Сохранить отчет?"))
{
var reportPath = "report.txt";
await File.WriteAllTextAsync(reportPath, $"Всего записей: {logs.Count}\nОшибок: {errors.Count}");
AnsiConsole.MarkupLine($"[green]Отчет сохранен в {reportPath}[/]");
}
}
class LogEntry
{
public string Level { get; set; }
public string Message { get; set; }
}
}
Советы по производительности и типичные ошибки
Производительность
- Избегайте частых вызовов AnsiConsole.Write внутри циклов. Вместо этого собирайте виджеты в список и выводите их одним вызовом.
- Используйте MarkupLine вместо форматирования строк. Разметка обрабатывается быстрее, чем ручное создание Style объектов.
- Для прогресс-баров с большим количеством итераций уменьшайте частоту обновления (например, Increment(10) вместо Increment(1)).
- Отключайте анимацию в CI/CD средах с помощью
AnsiConsole.Profile.Capabilities.Interactive = false;.
Типичные ошибки
- Забыли using Spectre.Console; — самая частая ошибка новичков.
- Неправильный синтаксис разметки. Всегда закрывайте теги:
[bold]текст[/], а не[bold]текст. - Использование AnsiConsole.MarkupLine с обычным текстом. Если текст содержит квадратные скобки, их нужно экранировать:
[[]и[]]. - Попытка вывести слишком много строк в таблицу. Таблицы Spectre.Console рендерятся целиком в памяти, что может вызвать проблемы с производительностью при тысячах строк. Используйте пагинацию.
- Игнорирование асинхронных методов. Методы типа
StartAsyncдолжны вызываться сawait, иначе прогресс-бар не будет обновляться.
Ссылки
- Официальная документация: https://spectreconsole.net