/
d
e
v
/
t
o
k
i
o
r
y
Блог <tokiory>
E
m
a
c
s
д
л
я
л
ю
д
е
й
Рассказываю о том, с чего можно начать конфигурировать Emacs

Emacs — отличный редактор, который прошел сквозь огонь и воду через года. К несчастью у данного редактора порог входа еще выше, чем у Vim.

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

  • Люди которые просто хотят делать свою работу используют VSCode, WebStorm и/или Sublime Text;
  • Люди которые хотят писать код настолько быстро, насколько это возможно - используют Vim, Neovim, Zed и/или Helix;
  • Люди которые хотят чтобы их редактор был буквально чем угодно (от браузера и обозревателя почты до полнофункциональной IDE) используют Emacs;
  • Люди которые устали от жизни используют Nano и/или Notepad++;

Вы можете пройти туториал, который предлагает сам Emacs, однако, я предлагаю вам ознакомиться с данной статьей, чтобы понять на что способен Emacs. Если вы готовы, то начнем

C-a
C-e
C-p
C-n
C-f
C-b
C-x C-f
C-s
C-r
M-x
C-x C-b
C-x C-s
C-a
C-e
C-p
C-n
C-f
C-b
C-x C-f
C-s
C-r
M-x
C-x C-b
C-x C-s

Emacs буквально везде

Возможно, вы этого не осознавали, но горячие клавиши из Emacs используются повсеместно по сей день. Если вы когда-нибудь использовали горячие клавиши, которые начинаются с Ctrl, то вы уже знаете часть горячих клавиш, которые используются в Emacs.

Попробуйте сами:

Редактор кода
Вы можете редактировать текст

Ctrl-f

Перейти на один символ вперед

Ctrl-b

Перейти на один символ назад

Ctrl-n

Перейти на одну строку вниз

Ctrl-p

Перейти на одну строку вверх

Ctrl-d

Удалить символ после курсора

Ctrl-k

Удалить все символы после курсора

Ctrl-a

Перейти в начало строки

Ctrl-e

Перейти в конец строки

Вы можете использовать данные горячие клавиши в любом приложении, где есть работа с текстом. Зачастую они пригождаются во всяких мессенджерах (напр. Telegram), при работе с большим текстом.

Также, данные горячие клавиши доступны в терминале, поэтому если у вас когда-нибудь была мысль как перейти в самое начало команды в терминале (не удаляя саму команду), то теперь вы знаете, что можете сделать это с помощью Ctrl-a.

Пока я не изучил документацию Emacs, я и не замечал, насколько может быть удобно редактировать текст горячими клавишами не выходя из режима редактирования (в том же Vim есть несколько режимов для работы с текстом).

Зачем нужен Emacs?

Emacs — это не просто редактор. Это среда, в которой можно организовать почти всё: от разработки и написания технической документации до ведения личного дневника и планирования проектов. Он создавался как инструмент, максимально подстраивающийся под пользователя, и по сей день остаётся одним из самых настраиваемых решений среди всех текстовых редакторов.

Emacs это не просто средство редактирования кода, а полноценное рабочее пространство. В нём можно писать, читать почту, следить за задачами и даже читать RSS-ленты. Всё это без необходимости переключаться между десятками окон и приложений. В Emacs ты действительно управляешь средой целиком — не она управляет тобой.

В отличие от более традиционных IDE, где каждый шаг жёстко регламентирован, здесь всё — под контролем пользователя. Хочешь, чтобы сохранение происходило автоматически при каждом изменении? Не проблема. Хочешь редактировать несколько файлов в одном окне, перемещаясь между ними одной клавишей? Тоже можно сделать. Хочешь LSP между проектами и просмотр MR внутри редактора? И это тут есть. Emacs хорош тогда, когда тебе нужна гибкость и ты не боишься глубины. Он не ограничивает, а предлагает — а ты сам решаешь, как будет выглядеть твоя среда.

Основные встроенные возможности

С первого взгляда Emacs может показаться простым редактором с устаревшим интерфейсом. Он поставляется с десятками встроенных режимов и функций, которые позволяют сразу начать работу. Одной из главных жемчужин является Org Mode — мощнейший инструмент для организации информации.

С его помощью можно вести заметки, планировать задачи, создавать структуры проектов и даже генерировать отчёты в различных форматах. Всё это реализовано так, что текст становится одновременно и содержанием, и интерфейсом.

Другой важной возможностью является M-x-интерфейс команд. Через него можно запустить любую функцию Emacs, даже если вы не знаете, на какую клавишу она повешена.

Всё это доступно без установки ни одного стороннего пакета. Уже из коробки Emacs позволяет писать код на большинстве популярных языков, использовать встроенный терминал, редактировать удалённые файлы по SSH и работать с Git. Да, интерфейс может быть непривычным. Но если дать себе время — Emacs постепенно становится тем самым инструментом, без которого сложно представить работу.

Конфигурация

Начать конфигурацию Emacs, пожалуй, нужно с создания файла конфигурации. По умолчанию Emacs будет искать файл конфигурации в следующих директориях:

  • ~/.emacs
  • ~/.emacs.d/init.el
  • ~/.config/emacs/init.el

Вся конфигурация Emacs пишется с помощью специального языка ELisp.

Давайте начнем писать конфигурацию с чего-то простого:

;; Подключаем специальный пакет, через который будем устанавливать другие пакеты
(require 'package)

;; Отключаем начальный экран
(setq inhibit-startup-message t)

;; Стираем все из "чернового" буфера
(setq initial-scratch-message "")

;; Устанавливаем нужный нам шрифт и размер
(set-face-attribute 'default nil :font "ZedMono Nerd Font" :height 150)

;; Включаем подсветку синтаксиса
(global-font-lock-mode 1)

Каждое выражение в ELisp оборачивается в круглые скобки, они являются областью видимости для всех выражений внутри них (скоупом).

Например, выражение (setq inhibit-startup-message t) устанавливает значение переменной inhibit-startup-message в t (оно же true), а выражение (setq initial-scratch-message "") устанавливает значение переменной initial-scratch-message в пустую строку.

set-face-attribute является функцией, которая позволяет изменять атрибуты шрифта. global-font-lock-mode является функцией, которая включает или отключает подсветку синтаксиса (в данном случае мы передаем 1, которая преобразуется в t).

Разделение конфигурации на файлы

Хранить всю конфигурацию в одном файле – не лучшая идея. Лучше разделить конфигурацию на несколько файлов и подключать их в зависимости от нужды.

Для того чтобы подключать файлы, нам нужно объявить функцию, которая будет загружать файлы. Давайте назовем ее load-config, вот как она будет выглядеть:

;; Функция для загрузки конфигураций
(defun load-config (file)
  (load (expand-file-name file "~/.emacs.d/config/")))

После того как мы объявили данную функцию – самое время поговорить о структуре конфигурации.

Наша конфигурация будет разделена на несколько частей для удобства поддержки.

Файлы:

  • Файл packages.el содержит конфигурацию для установки пакетов.
  • Файл appearance.el содержит настройки окружения, здесь располагаются настройки UX;
  • Файл keybindings.el содержит конфигурацию для настройки хоткеев.

Директории:

  • Директория ui содержит настройки пользовательского интерфейса (цветовые схемы и настройки интерфейса окна);
  • Директория utils содержит пакеты, которые улучшают пользовательский опыт, а также вспомогательные функции;
  • Директория langs содержит настройки для поддержки различных языков.

Мы можем дополнить конфигурацию следующим кодом:

;; Функция для загрузки конфигураций
(defun load-config (file)
  (load (expand-file-name file "~/.emacs.d/config/")))

(load-config "packages.el")
(load-config "appearance.el")
(load-config "keybindings.el")

(load-config "ui/theme.el")
(load-config "ui/window.el")

;; Утилитки
(load-config "utils/backup.el")
(load-config "utils/fuzzy.el")
(load-config "utils/treesitter.el")
(load-config "utils/lsp.el")
(load-config "utils/shell.el")


;; ЯПы
(load-config "langs/web.el")
(load-config "langs/go.el")
(load-config "langs/typescript.el")
(load-config "langs/markdown.el")
(load-config "langs/vue.el")
(load-config "langs/svelte.el")

Подробнее 

Подробнее 

Подробнее 

Подробнее 

Подробнее 

Подробнее 

Подробнее 

Подробнее 

Подробнее 

Подробнее 

Подробнее 

Подробнее 

packages.el

Для начала нам нужно настроить установку пакетов. Мы будем делать это в packages.el.

Для того чтобы перейти в другой файл мы можем использовать команду find-file, она доступна по комбинации клавиш C-x C-f, или мы можем использовать M-x (он же Option+x), чтобы вызвать меню с доступными командами, а затем ввести find-file.

Сам файл packages.el будет выглядеть следующим образом:

;; Добавляем репозитории
(setq package-archives
      '(("gnu" . "https://elpa.gnu.org/packages/")
        ("melpa" . "https://melpa.org/packages/")))

;; Установка use-package, если его нет
(unless (package-installed-p 'use-package)
  (package-refresh-contents)
  (package-install 'use-package))

;; Устанавливаем use-package для удобной установки других пакетов
(require 'use-package)
(setq use-package-always-ensure t)

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

  • GNU ELPA — официальный репозиторий пакетов Emacs.
  • MELPA — крупнейший неофициальный репозиторий, содержащий множество современных и активно поддерживаемых пакетов.

Во второй части кода проверяется, установлен ли пакет use-package. Если данный пакет не установлен, то выполняется package-refresh-contents, чтобы обновить список доступных пакетов с указанных репозиториев, а затем устанавливается сам use-package.

use-package

use-package — это пакет, который позволяет устанавливать другие пакеты и настраивать их не отходя от кассы прямо в теле функции use-package.

Ну, и наконец, c помощью require мы подключаем установленный пакет use-package. Переменная use-package-always-ensure устанавливается в t, что означает, что все последующие объявления use-package будут автоматически устанавливать нужные пакеты, если они ещё не установлены. Это удобно для автоматической и декларативной настройки окружения.

appearance.el

В данный файл мы добавим мелкие улучшения по UX/DX. Обычно я пишу туда следующее:

;; Закрыть текущий буфер Dired, если открывается еще один (дочерний) буфер
(setq dired-kill-when-opening-new-dired-buffer t)

;; Фокусироваться на новых окнах помощи
(setq help-window-select t)

keybindings.el

В данном файле мы добавим настройки для комбинаций клавиш, а также пакеты, которые упростят работу с горячими клавишами:

(use-package which-key
  :ensure nil
  :config
  (which-key-mode))

;; Менеджмент окон
(global-set-key (kbd "C-S-m") 'toggle-frame-maximized)
(global-set-key (kbd "C-s-f") 'toggle-frame-fullscreen)

Для начала мы используем use-package, который мы установили в package.el, с помощью него мы устанавливаем новый пакет – which-key.

which-key

which-key — это пакет, который позволяет отображать подсказки для горячих клавиш в Emacs. Он позволяет быстро и удобно находить нужные комбинации клавиш по мере их нажатия.

После установки данного пакета, мы также задаем две глобальные комбинации клавиш для управления окнами:

  • (global-set-key (kbd "C-S-m") 'toggle-frame-maximized)
    При нажатии клавиш C-S-m окно будет переключено между максимизированным и обычным режимом.
  • (global-set-key (kbd "C-s-f") 'toggle-frame-fullscreen)
    При нажатии клавиш C-s-f окно будет переключено между полноэкранным и обычным режимом.

utils/fuzzy.el (ivy+counsel)

Пришло время настраивать утилитарные пакеты. Первым и (наверное) одним из самых важных пакетов является ivy.

ivy

ivy — это пакет, который предоставляет систему автодополнения и фильтрации для Emacs. Он позволяет быстро и удобно находить нужные файлы, буферы, команды и другие элементы в Emacs.

counsel

counsel — это пакет, который обычно устанавливают на пару с ivy, он нужен для того чтобы предоставить нам удобные интерфейсы для взаимодействия с ivy и заменить часть стандартных интерфейсов Emacs, где не используется (но, может использоваться) ivy.

С помощью ivy и counsel мы улучшим команды, по типу find-file, M-x, bookmark-jump и другие. Вот как будет выглядеть конфигурация:

(use-package ivy
  :ensure t
  :config
  (ivy-mode 1))

(use-package ivy-rich
  :ensure t
  :init (ivy-rich-mode 1))

(use-package recentf
  :config
  (setq recentf-max-menu-items 15          ;; Number of items in the menu
        recentf-max-saved-items 100)       ;; Number of saved recent files
  (recentf-mode 1))

(use-package counsel
  :ensure t
  :bind ("C-c f r" . counsel-recentf))

(counsel-mode)

В данном файле мы скачали пакет ivy и сразу же включили его с помощью ivy-mode, затем мы объявили конфигурацию для recentf (встроенный пакет для просмотра недавних файлов) и также как и с ivy включили специальный режим recentf-mode.

В конце файла мы также включили counsel и забиндили новый хоткей, для того чтобы с помощью counsel открывать список недавних файлов.

В середине конфигурации также располагается ivy-rich, который позволяет упростить взаимодействие c ivy, с помощью него мы сможем создавать новые интерфейсы c ivy.

utils/treesitter.el

Treesitter - это библиотека для синтаксического анализа кода, которая позволяет анализировать код на основе синтаксических деревьев. Нам она нужна для того чтобы мы могли предоставить улучшенную подсветку кода для различных языков.

В Emacs начиная с версии 29 есть встроенная поддержка Treesitter, пакет который отвечает за данную поддержку называется treesit.

(use-package treesit
  :ensure nil
  :when (and (fboundp 'treesit-available-p)
             (treesit-available-p))
  :config
  (setq treesit-extra-load-path '("~/.emacs.d/tree-sitter/")))

(setq treesit-auto-langs '(python svelte go typescript
				  tsx css html))

(use-package treesit-auto
  :custom
  (treesit-auto-install 'prompt)
  :config
  (treesit-auto-add-to-auto-mode-alist 'all)
  (global-treesit-auto-mode))

В данной конфигурации мы использовали встроенный пакет treesit, и настроили пути для скачивания словарей, которые необходимы для поддержки различных языков. Также, мы указали правило :when, для того чтобы пакет treesit включался только в том случае, если он доступен (для совместимости с более старыми версиями Emacs).

treesit-auto

treesit-auto — это пакет, который нужен для автоматической настройки альтернативных режимов для языков программирования. Он позволяет легко добавлять словари, а также упрощает работу со встроенным treesit.

Еще один пакет, который мы используем – treesit-auto.

Мы включили его режим (global-treesit-auto-mode) и указали что нужно переключать все доступные режимы для языков программирования на альтернативные (с TreeSitter).

utils/lsp.el

В современных редакторах кода нам важна не только подсветка синтаксиса, но и интеграция с языковыми серверами (LSP). У Emacs есть встроенный пакет eglot, однако он не поддерживает все фичи LSP, которые доступны, поэтому мы будем использовать lsp-mode.

lsp-mode

lsp-mode — это пакет, который нужен для интеграции с LSP. Он отличается простой конфигурацией, а также наличием поддержки еще одного протокола — DAP (Debug Adapter Protocol).

;; Задаем префикс для всех действий из lsp-mode
(setq lsp-keymap-prefix "C-l")

;; Устанавливаем lsp-mode
(use-package lsp-mode
  ;; Ассоциируем каждый TreeSitter режим с режимом lsp-mode
  :hook (
	 (vue-ts-mode . lsp)
	 (javascript-ts-mode . lsp)
	 (tsx-ts-mode . lsp)
	 (web-mode . lsp)
	 (typescript-ts-mode . lsp)
	 (svelte-ts-mode . lsp)
	 (lsp-mode . lsp-enable-which-key-integration))
  :commands lsp)

;; Используем lsp-ivy для поиска символов в рабочем пространстве
(use-package lsp-ivy :commands lsp-ivy-workspace-symbol)

;; Убираем строку с навигацией (хлебные крошки)
(setq lsp-headerline-breadcrumb-enable nil)

;; Интегрируем lsp-mode с which-key
(with-eval-after-load 'lsp-mode
  (add-hook 'lsp-mode-hook #'lsp-enable-which-key-integration))

Теперь, для того чтобы выполнить какое-либо действие связанное с LSP, достаточно нажать C-l и выбрать действие из меню.

Если мы откроем файл, который поддерживается Treesitter, (а соответственно и lsp-mode, если есть хук), то lsp-mode запросит установить необходимый LS (Language Server), для того чтобы предоставить функциональность LSP.

utils/shell.el

Далее, нам нужно интегрировать терминал из Emacs с системным окружением (как минимум, нам нужно подменить переменную окружения PATH).

eshell

eshell — это встроенный пакет, который предоставляет интерфейс терминала в Emacs.

Вызвать терминал из Emacs можно с помощью команды eshell, данная команда открывает новое окно терминала с системным окружением. Для того чтобы выйти из терминала, достаточно нажать Ctrl + d или ввести exit.

Для того чтобы интегрировать переменные окружения в шелл мы будем использовать следующую конфигурацию:

(use-package exec-path-from-shell
  :ensure t
  :config
  (when (memq window-system '(mac ns x))
    (exec-path-from-shell-initialize)))
exec-path-from-shell

exec-path-from-shell — это пакет, который подменяет переменные окружения в Emacs с системными переменными окружения.

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

utils/backup.el

Ну и наконец, подходим к завершению настраивания утилитарных конфигураций — настраиваем автосохранение.

В Emacs уже есть встроенный функционал для автосохранения, мы можем использовать его следующим образом:

(auto-save-visited-mode 1)
(setq auto-save-visited-interval 60) ;; Save every 60 seconds

;; Отключаем бэкапы
(setq make-backup-files nil)

;; Отключаем отдельные файлы для автосохранения
(setq auto-save-default nil)

;; Отключаем лок-файлы
(setq create-lockfiles nil)

;; Отключаем версионированные бэкапы
(setq version-control nil)
auto-save-visited-mode

auto-save-visited-mode — встроенный в Emacs режим для автосохранения файлов. Он сохраняет все нами посещенные буферы.

Если же мы не хотим использовать встроенный функционал для автосохранения и сохранять все буферы разом — мы можем использовать пакет real-auto-save.

(use-package real-auto-save
  :ensure t
  :hook (
    ;; Добавляем режим автосохранения для всех программных режимов
    (prog-mode-hook . real-auto-save-mode))
  :config
  ;; Устанавливаем интервал автосохранения в 10 секунд
  (setq real-auto-save-interval 10))

;; Отключаем бэкапы
(setq make-backup-files nil)

;; Отключаем отдельные файлы для автосохранения
(setq auto-save-default nil)

;; Отключаем лок-файлы
(setq create-lockfiles nil)

;; Отключаем версионированные бэкапы
(setq version-control nil)
real-auto-save

real-auto-save — это пакет, который добавляет в Emacs функциональность автосохранения текущего буфера.

Конфигурация режимов для языков программирования

Финальным этапом конфигурации Emacs является настройка режимов для языков программирования.

Для примера мы возьмем два языка: Go и Vue.

Для этих языков программирования существует множество пакетов, которые упрощают работу с ними в Emacs. Например, для Go есть пакет go-mode, а для Vue есть пакет vue-mode, однако, не у всех режимов есть поддержка TreeSitter, поэтому для Go мы будем использовать go-mode, а для Vue — соберем режим сами.

Начнем с Go:

;; langs/go.el
(use-package go-mode
  :ensure t)

На этом наша настройка Go закончена😅

Теперь разберемся с Vue, тут все будет немного сложнее:

;; Добавляем поддержку TreeSitter для Vue с помощью `treesit-auto`
(setq vue-treesit-config
      (make-treesit-auto-recipe
       :lang 'vue
       :ts-mode 'vue-ts-mode
       :url  "https://github.com/ikatyang/tree-sitter-vue"
       :revision "master"
       :source-dir "src"
       :ext "\.vue\'"))

;; Добавляем язык в список словарей
(add-to-list 'treesit-auto-recipe-list vue-treesit-config)

;; Используем специальный режим vue-ts-mode
;; Подсказка: ts означает TreeSitter
(use-package vue-ts-mode
   :vc (:url "https://github.com/8uff3r/vue-ts-mode")
   :ensure t)

Другие языки программирования настраиваются схожим образом.

У режимов, которые поддерживают TreeSitter – мы не используем дополнительных конфигураций, а у режимов языков, которые не поддерживают TreeSitter, мы используем дополнительные конфигурации с указанием словарей в treesit-auto.