Создание собственной темы для GNU Emacs

от автора

В августе прошлого, 2025 года, я рассказал о создании собственной чёрно-белой темы для GNU Emacs. Почти год безсменно я её использую и не испытываю никаких проблем (скриншот позволяет в этом убедиться). Но рассказ будет не про это, а про то, как выглядит техника создания собственной темы для Emacs. Для тех, кто подумал, что будет также скушно и сложно, как и в рассказах про создание собственного пакета и режима для Emacs, да, это так, только не сложно.

Вот так выглядит эта тема в процессе работы на статьёй

Вот так выглядит эта тема в процессе работы на статьёй

Начну с того, друзья мои, что вам всё-таки придётся почитать мою статью по ссылке выше про создание пакета, так как в целом тема в GNU Emacs оформляется по правилам обычного пакета, но конечно со своими особенностями. И про эти правила я здесь умалчиваю, полагая, что это вы уже знаете.

Как и в предыдущие разы объяснять буду на примере кода темы, исходные тексты которой вы найдёте по ссылке.

Обратите внимание на то, что этот исходный текст распространяется под лицензией GNU GPL версии 3 или новее.

Сначала общая картина. Во-первых, если пакет включает в себя две или более темы, то рекомендуемая структура будет такой: главный модуль пакета, который содержит процедуры для работы с темами (загрузка тем, переключение), и по одному модулю на каждую тему. Совпадающие настройки тем, в принципе, можно поместить в одном месте, но у меня светлая и тёмная темы полностью независимы. Во-вторых, настройки темы включают в себя в первую очередь базовые настройки оформления текста и настройки font-lock, а также cursor, error, warning, highlight, link, match, mode-line, success и некоторые другие. И лишь затем идут настройки для отдельно взятых режимов. В третьих, вам не будет достаточно базовых настроек, так как многие режимы, как встроенные, так и загружаемые из ваших любимых пакетов, например, org, magit делают свои настройки, которые для единообразия желательно привести к общему знаменателю. В-четвёртых, сами настройки весьма просты: указывается название настройки, цвет фона, цвет переднего плана, наличие подчёркивания и его настройки, насышенность (жирность). В-пятых, для указания цвета можно использовать как числовые коды RGB, так и название цвета в палитре. Наконец, для создания темы в Emacs Lisp предназначен макрос deftheme, а регистрация модуля с темой производится provide-theme. Загружается тема вызовом load-theme.

Теперь пройдёмся по исходнику book-like-light-theme.el, в котором описан светлый вариант моей чёрно-белой темы. В book-like-dark-theme.el всё то же самое, только цвета инвертированы с отдельными коррекциями. (Вы ведь уже скачали и установили её себе? Я не стал её регистрировать в MELPA, но её можно установить из исходников с помощью package-vc-install.)

Итак, все настройки темы происходят после вызова макроса deftheme:

(deftheme book-like-light  "Book like grayscale light theme.")(custom-theme-set-faces 'book-like-light '(default ((t (:width normal :weight regular :slant normal :underline nil :overline nil :extend nil :strike-through nil :box nil    :inverse-video nil :foreground "black" :background "white" :stipple nil :inherit nil)))))

Вызов custom-theme-set-faces начинается с самой базовой настройки внешнего вида текста. А затем идут уточнения для различных режимов. Я не буду их все перечислять и объяснять, там довольно всё однообразно. Разберу только несколько примеров и перечислю самые основные из них в алфавитном порядке.

'(cursor ((((background light)) (:background "dim gray")) (((background dark)) (:background "white smoke"))))

Настройка вида курсора: задаём цвет курсора в зависимости от цвета фона, на светлом потемнее, на тёмном посветлее.

'(error ((t (:foreground "black" :weight bold))))

Настройки вида сообщения об ошибке в минибуфере: черным цветом, жирным шрифтом.

'(font-lock-comment-face ((t (:extend t :background "white smoke" :foreground "dim gray"))))

Это настройка внешнего вида для комментариев: :extend задаёт распространение фоновых настроек на всю ширину окна буфера в области комментария. :background "white smoke" задаёт фоновый цвет, а :foreground "dim gray" для переднего плана (символов).

'(font-lock-warning-face ((t (:underline (:color "gray" :style wave :position nil)))))

А это настройка предупреждений в тексте: :underline задаёт подчёркивание для предупреждения. Далее идут настройки для линии подчёркивания: :color "gray" цвет, :style wave волнистый стиль, :position nil положение линии (по-умолчанию под текстом).

 '(highlight ((t (:weight bold :inverse-video t))))

Это настройка для подсвеченного текста: :weight bold жирность шрифта и :inverse-video t инвертирование настроек для фона и символов. Обратите внимание, что я не задаю цвет фона и символа, он вычисляется самим редактором.

'(outline-1 ((t (:inherit font-lock-function-name-face :weight bold))))

Универсальная настройка для заголовков первого уровня: здесь интересно, что оформление наследуется :inherit от другой настройки.

Перечислю основные настройки, которые потребуется сделать для темы, разрабатываемой с нуля:

  • cursor

  • error, success и warning

  • font-lock-*

  • highlight и lazy-highlight

  • link и link-visited

  • match

  • minibuffer-prompt

  • mode-line

  • outline-*

  • region

  • shadow

  • show-paren-*

  • trailing-whitespace

Возможно потребуется ещё что-то подправить, ну и произвести настроку для ваших любимых режимов. Например, я настроил тему для agda, company, flymake, isearch, sh-*.

Остаётся добавить путь к теме в список путей:

(and load-file-name     (boundp 'custom-theme-load-path)     (add-to-list 'custom-theme-load-path                  (file-name-as-directory (file-name-directory load-file-name))))

И подсказать Emacs, что этот модуль содержит описание темы:

(provide-theme 'book-like-light)

Теперь заглянем в модуль book-like-themes.el, который отвечает за удобство использования тем. В нём определены процедуры для загрузки тем book-like-light-load и book-like-dark-load, и переключения тем book-like-themes-toggle. Первые две работают очень просто, а последняя в случае, если ни одна, ни другая ещё не были загружены, запрашивает у пользователя, какую из них загрузить.

Вот в общем-то и всё! Но прежде чем бросаться писать код своей темы, рекомендую сначала взять чужую тему и поэкспериментировать с её перенастройкой. Для этого нужно воспользоваться командой customize-faces, выбрать какую-нибудь настройку внешнего вида и подправить под себя. Сохраните эти настройки, они могут вам пригодиться при написании кода своей темы. А если передумаете, то можете их удалить из custom-set-faces в init.el или custom.el в зависимости от ваших настроек.

Если статья вам понравилась, то можете поблагодарить меня как автора на Boosty. Полученные средства будут направлены не на покупку очередного понтового гаджета, а на написание следующей статьи про GNU Emacs, Lisp или что-нибудь ещё такое же красивое.

Ссылки

© Симоненко Е.А., 2026

Буквальное копирование и распространение этого произведения разрешается на любом носителе при условии сохранения вышеуказанного уведомления об авторских правах, этого лицензионного уведомления и нижеуказанного уведомления об отказе от ответственности. Авторские права на все изображения в этом произведении принадлежат автору этого произведения и являются его неотъемлемой частью. Фрагменты программного кода из этого произведения, если не оговорено иное, разрешается распространять и использовать без ограничений с или без изменений и без указания авторских прав.

Информация в этом произведении, включая также изображения и фрагменты программного кода, предоставляется как есть, без какой либо гарантии и обещания пригодности для чего либо. Автор не несёт ответственность за любой ущерб, возникший в результате использования данного произведения.

Разрешается использование текста данной лицензии иными авторами в своих произведениях как в буквальном виде, так и в изменённом или адаптированном под нужды автора виде без указания оригинального авторства текста данной лицензии.

ссылка на оригинал статьи https://habr.com/ru/articles/1043092/