Иногда возникает необходимость вывести в командную строку Linux сообщение, в котором часть текста или весь текст имеет какой-либо цвет. Согласитесь, очень удобно было бы иметь возможность грамотно и красиво оформить вывод своей программы на D с помощью понятного и почти всем доступного цветового оформления.
В этой статье, я покажу вам содержимое одного самописного модуля, который позволит, не используя сторонних компонентов, добавить в свои консольные программы больше цвета и некоторых других эффектов.
Начнем с того, что терминал Linux — очень умная и хорошо разработанная вещь, которая поддерживает очень многие возможности в помощь программисту. Одной из таких возможностей является обработка специальных кодовых серий, которые называются escape-последовательностями.
Эти последовательности представляют собой ряд символов, которые не видны в ходе печати текста на терминал, но при этом трактуются как некоторые управляющие команды. Данные команды мы будем представлять в виде строковых последовательностей в Unicode, которые имеют общий шаблон построения: указание кодового символа в Unicode и некоторое числовое значение:
"\u001b[Nm"
где N — некоторый управляющий код.
Совокупность escape-последовательностей, помещенных перед текстом, по сути дела, и дают нужный цветовой эффект. Однако, чтобы текст после применения нужного цветового эффекта вернулся в исходное состояние и не оказал действия на последующий текст, то после окрашенного фрагмента текста нужно подать особую команду (добавить особую escape-последовательность) — команду сброса всех последовательностей.
Эта команда выглядит просто:
"\u001b[0m"
А теперь сам текст модуля, который называется colorize:
module colorize;
class ColoredTerminal
{
private
{
enum string reset = "\u001b[0m";
enum string[string] backgroundColors = [
"default" : "\u001b[49m",
"black" : "\u001b[40m",
"red" : "\u001b[41m",
"green" : "\u001b[42m",
"yellow" : "\u001b[43m",
"blue" : "\u001b[44m",
"magenta" : "\u001b[45m",
"cyan" : "\u001b[46m",
"lightGray" : "\u001b[47m",
"darkGrey" : "\u001b[100m",
"lightRed" : "\u001b[101m",
"lightGreen" : "\u001b[102m",
"lightYellow" : "\u001b[103m",
"lightBlue" : "\u001b[104m",
"lightMagenta" : "\u001b[105m",
"lightCyan" : "\u001b[106m",
"white" : "\u001b[107m"
];
enum string[string] foregroundColors = [
"default" : "\u001b[39m",
"black" : "\u001b[30m",
"red" : "\u001b[31m",
"green" : "\u001b[32m",
"yellow" : "\u001b[33m",
"blue" : "\u001b[34m",
"magenta" : "\u001b[35m",
"cyan" : "\u001b[36m",
"lightGray" : "\u001b[37m",
"darkGrey" : "\u001b[90m",
"lightRed" : "\u001b[91m",
"lightGreen" : "\u001b[92m",
"lightYellow" : "\u001b[93m",
"lightBlue" : "\u001b[94m",
"lightMagenta" : "\u001b[95m",
"lightCyan" : "\u001b[96m",
"white" : "\u001b[97m"
];
enum string[string] textStyles = [
"default" : "\u001b[0m",
"bold" : "\u001b[1m",
"bright" : "\u001b[1m",
"dim" : "\u001b[2m",
"underline" : "\u001b[4m",
"blink" : "\u001b[5m",
"reverse" : "\u001b[7m",
"hidden" : "\u001b[8m",
];
}
// colorize your text
static string colorize(
string text,
string foregroundColor = "default",
string backgroundColor = "default",
string textStyle = "default"
)
{
string foreground, background, style;
// set foreground
if (foregroundColor in foregroundColors)
{
foreground = foregroundColors[foregroundColor];
}
else
{
foreground = foregroundColors["default"];
}
// set background
if (backgroundColor in backgroundColors)
{
background = backgroundColors[backgroundColor];
}
else
{
background = backgroundColors["default"];
}
// set style
if ((textStyle in textStyles) && (textStyle != "default"))
{
style = textStyles[textStyle];
}
else
{
style = "";
}
string newText = foreground ~ background ~ style ~ text ~ reset;
return newText;
}
// colorize with some caption style
static string colorize2(string caption, string text, string foregroundColor)
{
return colorize(caption, foregroundColor, "default", "bold") ~ " " ~ colorize(text, "white", "default", "bold");
}
// colorize as info message
static string info(string text)
{
return colorize2("Info:", text, "green");
}
// colorize as log message
static string log(string text)
{
return colorize2("Log:", text, "blue");
}
// colorize as warning message
static string warning(string text)
{
return colorize2("Warning:", text, "yellow");
}
// colorize as error message
static string error(string text)
{
return colorize2("Error:", text, "red");
}
}
Модуль colorize содержит простой класс ColoredTerminal, который реализует вышеупомянутые идеи о раскраске текста в терминале. Данный класс содержит общий метод colorize, принимающий строку текста, строку с описанием его цвета в виде обычного названия цвета на английском (поэтому список цветов довольно скудный), строку с описанием фонового цвета и строку с описанием стиля написания текста (это тоже описание на английском, которое говорит, что текст либо обычный, либо подчеркнутый и т.д.), а возвращает строку с примененными к тексту escape-последовательностями. Также, доступен ряд методов выводящих текст в стиле соответствующем какому-либо из следующих вариантов сообщения: информация (info), отладочное сообщение (log), предупреждение (warning) и ошибка (error).
Испытать класс легко, достаточно подключить модуль colorize к своей программе, разместив его в файле colorize.d, и воспользоваться любым из нужных методов в сочетании, например, с функцией writeln из std.stdio:
void main()
{
import std.stdio;
import colorize;
ColoredTerminal.info("Связь с USB-устройством была установлена").writeln;
ColoredTerminal.log("Был установлен цифровой режим").writeln;
ColoredTerminal.warning("Настройки режима установлены в значения по умолчанию").writeln;
ColoredTerminal.error("Отсутствует подключение к шине USB").writeln;
}
Выглядит это так: