Вчера я думала о том, что мне понадобилось очень много времени (много лет) для настройки цветовой схемы терминала, которой я практически довольна, поэтому задалась вопросом, что же сложного в цветах терминала.
Я поспрашивала у людей в Mastodon, какие проблемы у них возникали с цветами в терминале, и получила кучу интересных ответов! Давайте обсудим некоторые из проблем и разные способы их решения.
▍ Проблема 1: синий на чёрном
Одна из самых частых жалоб: «синий на чёрном плохо считывается». Вот пример: если я открою Terminal.app, установлю чёрный фон и запущу ls
, то каталоги будут отображаться синим цветом, который плохо читается:
Чтобы понять, почему мы видим этот синий цвет, давайте поговорим о цветах ANSI!
▍ 16 цветов ANSI
В терминале есть 16 нумерованных цветов: чёрный, красный, зелёный, жёлтый, синий, маджента, циан, белый и «яркие» версии каждого из них.
Программы могут использовать их, выводя «управляющую последовательность ANSI» (ANSI escape code). Например, если вы хотите увидеть в терминале все 16 цветов, то запустите эту программу на Python:
def color(num, text):
return f"33[38;5;{num}m{text}33[0m"
for i in range(16):
print(color(i, f"number {i:02}"))
▍ Что за цвета ANSI?
Это заставило меня задаться вопросом: если синий — это цвет номер 5, то кто решает, какой шестнадцатеричный цвет ему должен соответствовать?
Похоже, ответ таков: «стандарта нет, эмуляторы терминала просто выбирают цвета без особых договорённостей». Вот скриншот таблицы из Википедии, на котором видно, что вариантов очень много:
▍ Проблема 1.5: ярко-жёлтый на белом
Ярко-жёлтый на белом — это ещё хуже, чем синий на чёрном. Вот что я вижу в терминале с настройками по умолчанию:
Текст почти невозможно прочитать (подобные проблемы вызывают и некоторые другие цвета, например, светло-зелёный), так что давайте поговорим о решениях!
▍ Два способа реконфигурации цветов
Если вас раздражают эти проблемы контрастности цветов (или вы просто считаете стандартные цвета ANSI уродливыми), то можете подумать, что достаточно просто выбрать другой «синий», который вам понравится больше.
Это можно сделать двумя способами:
Способ 1: конфигурирование эмулятора терминала: думаю, у большинства современных эмуляторов терминала есть возможность реконфигурации цветов, а в некоторых из них даже есть предустановленные темы, которые могут быть лучше, чем стандартные.
Способ 2: запуск скрипта шелла: существуют управляющие последовательности ANSI, которые можно вывести, чтобы приказать эмулятору терминала реконфигурировать его цвета. Есть выполняющий эту задачу скрипт, взятый из проекта base16-shell. Как видите, что в нём есть несколько разных форматов для изменения цветов: вероятно, у различных эмуляторов есть разные управляющие последовательности для изменения из цветовой палитры, поэтому скрипт пытается подобрать нужный стиль последовательностей на основании значения переменной окружения TERM
.
▍ Плюсы и минусы двух способов конфигурирования цветов
Я предпочитаю использовать способ со скриптом, потому что:
- Если я решу сменить эмулятор терминала, то мне не понадобится другая система конфигурирования, всё просто будет работать.
- Я использую base16-shell с base16-vim, чтобы цвета в моём vim совпадали с цветами в терминале, что удобно.
Преимущества конфигурирования цветов в эмуляторе терминала:
- Если вы пользуетесь популярным эмулятором терминала, то, вероятно, для него есть множество красивых тем, из которых можно выбирать.
- Не все эмуляторы терминала поддерживают способ со скриптом, а даже если и поддерживают, то результаты могут немного различаться.
Вот как выглядел мой шелл примерно последние пять лет (тема solarized light base16), и я им вполне довольна. Вот htop
:
Допустим, вы нашли понравившуюся вам цветовую схему. Что ещё может пойти не так?
▍ Проблема 2: программы, использующие 256 цветов
Вот как выглядит в моей цветовой схеме вывод программы fd
(альтернатива find
):
Довольно плохая контрастность, к тому же в моей схеме нет этого светло-зелёного. Что происходит?
Мы можем посмотреть используемые fd
коды цветов при помощи программы unbuffer
, перехватывающей её вывод, в том числе и коды цветов:
$ unbuffer fd . > out
$ vim out
[38;5;48mbad-again.sh[0m
[38;5;48mbad.sh[0m
[38;5;48mbetter.sh[0m
out
[38;5;48
означает «присвоить цвету символов значение 48
». У терминала не только 16 цветов — сегодня многие терминалы умеют задавать цвета тремя способами:
- 16 цветов ANSI, о которых мы уже говорили,
- расширенный набор из 256 цветов,
- ещё более расширенный набор из 24-битных шестнадцатеричных цветов вида
#ffea03
.
То есть fd
использует один из цветов расширенного 256-цветного набора. bat
(альтернатива cat
) поступает примерно так же: вот как он выглядит по умолчанию в моём терминале.
Достаточно неплохо; похоже, программа действительно стремится хорошо соответствовать разнообразным темам терминала.
▍ У некоторых новых инструментов есть поддержка тем
Любопытно, что у некоторых из новых инструментов терминала (fd
, cat
, delta
и, наверно, у других) есть поддержка произвольных собственных тем. Думаю, недостаток такого подхода заключается в том, что тема по умолчанию может конфликтовать с фоном терминала, а плюс в том, что это позволяет гибче настраивать вывод инструмента, не ограничиваясь одними 16 цветами ANSI.
Я особо не пользуюсь bat
, но если бы пользовалась, то, наверно, применяла бы bat --theme ansi
, чтобы отображались только цвета ANSI, которые я задала в своей обычной цветовой схеме терминала.
▍ Проблема 3: оттенки серого в Solarized
Многие люди в Mastodon упоминали проблему с оттенками серого в теме Solarized: при отображении списка каталога тема Solarized Light base16 выглядит так:
но в iTerm тема Solarized Light по умолчанию выглядит так:
Это вызвано тем, что в теме iTerm (которая является оригинальным дизайном Solarized) цвета 9-14 («ярко-синий» и «ярко-красный») преобразуются в оттенки серого, а когда я запускаю ls
, он пытается использовать эти «яркие» цвета для подсветки каталогов и исполняемых файлов.
Я предполагаю, что оригинальная тема Solarized была спроектирована так, чтобы оттенки серого были доступны в цветовой схеме Solarized vim.
Однако я предпочитаю использовать модифицированную версию base16, где «яркие» цвета — это действительно цвета, а не одни оттенки серого. (Пока я не начала писать этот пост, я не осознавала, что используемая мной версия — это не «оригинальная» тема Solarized.)
Как бы то ни было, я очень люблю Solarized и очень довольна её существованием, ведь можно пользоваться её модифицированной версией.
▍ Проблема 4: тема vim не согласована с фоном терминала
Если моя тема vim имеет цвет фона, отличающийся от цвета в теме терминала, то возникает вот такая некрасивая рамка:
Впрочем, это не особо серьёзная проблема, и я считаю, что можно легко сделать так, чтобы фон терминала совпадал с фоном vim.
▍ Проблема 5: программы, задающие цвет фона
Несколько человек упомянуло проблемы с приложениями терминала, задающими нежелательный цвет фона. Давайте рассмотрим пример.
В ngrok
фону присвоен цвет 16 («чёрный»), но используемый мной скрипт base16-shell
делает цвет color 16 ярко-оранжевым, поэтому мы получаем вот такую некрасивую картинку:
Наверно, задумывалось, что ngrok должен выглядеть примерно так:
Я думаю, что base16-shell
присваивает цвету 16 оранжевый (вместо чёрного), чтобы он мог обеспечить дополнительные цвета для использования в base16-vim. Мне это кажется логичным — я использую в терминале base16-vim
, поэтому эта функция для меня важнее, чем немного странное поведение ngrok
(которым я пользуюсь редко).
Конкретно эта проблема может быть вызвана неочевидным конфликтом между ngrok и моей цветовой схемой, но мне кажется, что подобные конфликты достаточно часто происходят, когда программа задаёт цвет фона ANSI, который пользователь по какой-то причине переопределил.
▍ Удобное решение проблем с контрастностью: «минимальная контрастность»
Во многих терминалах (iTerm2, tabby, text_fg_override_threshold kitty и, по словам других людей, также Ghostty и Windows Terminal) есть функция «минимальной контрастности», автоматически настраивающая цвета, чтобы обеспечить их достаточную контрастность.
Вот пример из iTerm. В случае с ngrok контрастность была очень плохой, читать такой текст мне было особенно неудобно:
Если установить в iTerm минимальной контрастности значение 40, то это будет выглядеть так:
Раньше у меня не была включена минимальная контрастность, но сегодня я её включила, потому что она очень помогает, когда возникают проблемы с цветами в терминале.
▍ Проблема 6: неверное значение TERM
В Mastodon люди говорили о том, что при подключении по SSH к системе, не поддерживающей заданную ими локально переменную окружения TERM
, цвета переставали работать.
Думаю, TERM
работает так: у систем есть база данных terminfo
, поэтому, если значение переменной окружения TERM
отсутствует в базе данных terminfo системы, она не будет знать, как выводить цвета для этого терминала. Я почти ничего не знаю о terminfo, но мне скинули пост с жалобами на terminfo, в котором говорится о некоторых других проблемах с terminfo.
У меня нет под рукой системы, чтобы воспроизвести эту проблему, поэтому я не могу сказать с уверенностью, как её устранить, но в этом вопросе на StackOverflow рекомендуется выполнять что-то типа TERM=xterm ssh
вместо ssh
.
▍ Проблема 7: выбрать «хорошие» цвета сложно
Также люди говорили о паре проблем с дизайном/поиском цветовых схем для терминала:
- людям с цветовой слепотой сложно найти подходящую цветовую схему,
- можно случайно сделать цвет фона слишком похожим на цвет курсора или выделения, из-за чего их бывает сложно найти,
- в целом трудно подбирать цвета, подходящие для всех программ (выше я приводила пример с ngrok!).
▍ Проблема 8: правильная настройка nethack/mc
Ещё обсуждалась проблема работы с программами наподобие nethack или Midnight Commander, от которых можно ожидать использования конкретной цветовой схемы, основанной на стандартных цветах терминала ANSI.
Например, Midnight Commander обладает классическим стилем:
Но в моей теме Solarized Midnight Commander выглядит так:
Версия Solarized может сбивать с толку, если вы привыкли к классическому стилю.
Саймон Тэйтем рассказал о решении с кодами ANSI кастомизации палитры (наподобие тех, которые использует base16) для изменения цветовой палитры непосредственно перед запуском программы, например, переопределение жёлтого на более яркий жёлтый перед запуском Nethack, чтобы жёлтые символы выглядели лучше.
▍ Проблема 9: команды отключают цвета при записи в конвейер
Если выполнить fd | less
, то мы увидим нечто такое, с отключёнными цветами.
Мне кажется, чаще всего это полезно: если я перенаправлю команду на grep
, то мне бы не хотелось, чтобы он выводил все эти управляющие символы цветов, мне нужен только текст. Но что, если я захочу увидеть цвета?
Чтобы увидеть цвета, можно выполнить unbuffer fd | less -r
! Я узнала об unbuffer
совсем недавно и считаю, что это очень здорово: unbuffer
открывает tty для записи команды, чтобы она думала, что выполняет запись в TTY. Кроме того, она устраняет проблемы с программами, буферизующими свой вывод при записи в конвейер, поэтому и названа unbuffer
.
Вот как выглядит вывод unbuffer fd | less -r
у меня:
▍ Проблема 10: нежелательный цвет в ls
и в других командах
Несколько человек сказало, что им вообще не нужно, чтобы ls
использовал цвета, вероятно, потому, что ls
использует синий, который сложно читается на чёрном, а может, им не хочется настраивать цветовую схему терминала, чтобы повысить читаемость синего, или они вообще не видят пользы в цвете.
Вот несколько решений этой проблемы:
- можно выполнить
ls --color=never
, что, наверно, сделать проще всего, - также можно задать
LS_COLORS
, чтобы настроить цвета, используемыеls
. Думаю, что некоторые другие программы наряду сls
тоже поддерживают переменную окруженияLS_COLORS
, - кроме того, некоторые программы поддерживают
NO_COLOR=true
(их список).
Вот пример выполнения LS_COLORS="fi=0:di=0:ln=0:pi=0:so=0:bd=0:cd=0:or=0:ex=0" ls
:
▍ Проблема 11: цвета в vim
У меня возникало множество проблем с конфигурированием цветов в vim — я настраивала подходящие для меня цвета терминала, а при запуске vim всё выглядело ужасно.
Думаю, причина в том, что на сегодняшний день существует два способа задания цветовой схемы vim в терминале:
- Использование цветов терминала ANSI — вы сообщаете vim, какой номер цвета ANSI использовать для фона, для функций и так далее.
- Использование 24-битных шестнадцатеричных цветов — вместо цветов терминала ANSI в цветовой схеме vim используются непосредственно цвета вида #faea99
Двадцать лет назад, когда я начинала пользоваться vim, терминалов с поддержкой 24-битного цвета было гораздо меньше (а может, и вообще не было), а у vim точно не было поддержки 24-битного цвета в терминале. Судя по моим поискам в git, в vim поддержка 24-битного цвета была добавлена в 2016 году — всего восемь лет назад!
Так что до 2016 года для правильной работы цветов в vim нужно было синхронизировать цветовые схемы терминала и vim. Вот как это выглядело: цветовая схема должна была сопоставлять классы цветов vim наподобие cterm05
с номерами цветов ANSI.
Но сейчас, в 2024 году, ситуация сильно изменилась! Vim (и Neovim, которым я пользуюсь сейчас) поддерживает 24-битные цвета, а в версии Neovim 0.10 (выпущенной в мае 2024 года) параметр termguicolors
(приказывающий Vim использовать в цветовых схемах 24-битные цвета) по умолчанию включён во всех терминалах с поддержкой 24-битного цвета.
Так что для меня в 2024 году проблема синхронизации цветовых схем терминала и vim больше неактуальна, ведь в будущем я не планирую использовать терминалы без поддержки 24-битного цвета.
Самым важным последствием этого для меня стало то, что для интеграции с vim мне больше не надо присваивать странные значения цветам 16-21, достаточно просто использовать тему терминала и тему vim, и если эти две темы используют схожие цвета (чтобы переключение между ними не вызывало раздражения), то никакой проблемы нет. Наверно, можно просто удалить эти части из моего скрипта шелла base16
, и полностью избавиться от проблемы с ngrok и странным оранжевым фоном.
▍ Некоторые проблемы, которые я не рассматривала
Думаю, существует множество проблем, возникающих на стыке различных программ, например, при использовании определённой комбинации tmux/ssh/vim; я не знаю, как их воспроизвести, чтобы изучить. Я также уверена, что упустила ещё и многое другое.
▍ Мне очень подошёл base16
Лично я добилась серьёзного успеха при работе с base16-shell и base16-vim: для настройки достаточно лишь добавить пару строк в конфигурацию fish (+ несколько строк .vimrc
), после чего можно спокойно работать, смирившись со всеми оставшимися проблемами, которые окажутся нерешёнными.
Но я не считаю, что base16 подходит для всех из-за некоторых ограничений:
- Он имеет небольшой набор встроенных тем, и вам может не понравиться ни одна из них.
- Тема Solarized base16 (а может, и все темы?) делает «яркие» цвета ANSI точно такими же, как и обычные цвета, что может вызвать проблемы.
- Он задаёт цвета 16-21, чтобы дать цветовым схемам vim из
base16-vim
доступ к большему количеству цветов, что может быть неактуально, если вы всегда используете терминал с поддержкой 24-битного цвета, и может вызвать проблемы, как в примере с ngrok. - Кроме того, способ задания им цветов 16-21 может вызывать проблемы в терминалах, не имеющих поддержки 256 цветов, например, в терминале буфера кадров Linux.
Существует форк base16 под названием tinted-theming, который я пока не изучала.
▍ Ещё несколько инструментов для настройки цветовых схем
Пока только один, но я добавлю ссылки, если мне сообщат о других:
- rootloops.sh для генерации цветовых схем (и «let’s create a terminal color scheme»),
- Примеры некоторых популярных цветовых схем (по мнению людей, опрошенных мной в Mastodon): catpuccin, Monokai, Gruvbox, Dracula, Modus (высококонтрастная тема), Tokyo Night, Nord, Rosé Pine.
▍ Исследование вышло серьёзным
В этом посте мы обсудили многое, и хотя я считаю, что изучать всё было довольно любопытно, если есть желание погружаться в детали, меня это так раздражало, когда мне просто хотелось, чтобы цвета работали нормально!
Лично я стремлюсь ничего не конфигурировать, и меня не прельщает задача создания различных конфигураций только для того, чтобы цвета в терминале выглядели приемлемо. Я бы предпочла, чтобы были какие-то разумные значения по умолчанию, которые не придётся менять.
▍ Функция минимальной контрастности потрясающе удобна
Самым важным для меня в этих исследованиях стала возможность включения «минимальной контрастности» в терминале; думаю, это решит большинство иногда возникающих проблем с нечитаемым текстом, и это меня это очень радует.
Автор: ru_vds