Иногда возникает необходимость вывести в командную строку 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; }
Выглядит это так:
