Emacs: философия, org-mode, elisp и почему он всё ещё жив
Есть программа, которой в 2026 году исполняется пятьдесят лет, и она по-прежнему получает мажорные релизы, по-прежнему собирает несколько процентов профессиональных разработчиков и по-прежнему вызывает у них странную привязанность, которую сложно объяснить человеку со стороны. Речь про Emacs.
Объяснение, которое обычно дают («там классные горячие клавиши», «там org-mode»), неверно. Горячие клавиши у Emacs, честно говоря, спорные. Org-mode впечатляет, но это следствие, а не причина. Настоящая причина одна, и она архитектурная: Emacs — это не редактор с плагинами, а работающая Lisp-машина, у которой редактор написан на Lisp и который вы можете переписать, не выходя из него.
Дальше — что из этого следует, сколько это стоит и в каких случаях оно того не стоит.
Откуда всё взялось
Emacs родился в 1976 году в MIT AI Lab как набор макросов для редактора TECO — отсюда и имя, Editing MACroS. Ричард Столлман и Гай Стил собрали воедино чужие наработки, и получившаяся штука сразу оказалась не редактором, а средой, где редактор конфигурируется кодом. В 1985 году Столлман написал GNU Emacs с нуля — уже с собственным диалектом Lisp, — и это стало первой большой программой проекта GNU. Каноническое изложение замысла — его статья EMACS: The Extensible, Customizable Display Editor (1981). Три слова из заголовка — extensible, customizable, self-documenting — до сих пор работают как техническое задание.
Из этой таблицы важен не список версий, а форма кривой. Emacs десять лет выглядел законсервированным, а с 27-й версии втянул внутрь себя ровно то, что делало современные редакторы современными: быстрый JSON (без него LSP тормозил), tree-sitter, LSP-клиент, нативную компиляцию. Проект не «дожил» — он догнал.
Архитектура: 5% на C, всё остальное на Lisp
Ключ ко всему — пропорция.
На C написан минимум: работа с текстовым буфером (классический gap buffer — массив с «дыркой» в позиции курсора, дешёвая вставка там, где вы печатаете, дорогая — при прыжках по файлу), перерисовка экрана, сборщик мусора, ввод-вывод, процессы и примитивы языка. Всё остальное — открытие файла, поиск, дired, почтовый клиент, git-интерфейс, режим для Python — это Emacs Lisp, который загружается при старте и который можно переопределить сейчас, прямо в запущенном процессе.
Разница с моделью «плагин через API» принципиальная. В VS Code расширение обращается к
vscode.* — к тому, что команда редактора решила вынести наружу; остальное закрыто и живёт
в другом процессе. В Emacs нет «наружу»: функция find-file, которой вы открываете файлы, —
обычная лисповая функция, её исходник открывается по C-h f find-file двумя нажатиями, и её
поведение можно изменить, не форкая проект.
;; Штатное поведение: C-x C-f открывает файл.
;; Хотим, чтобы при открытии файла в /etc он молча открывался под sudo.
(defun my/etc-as-root (orig-fn filename &rest args)
"Обёртка вокруг find-file: подменяем путь на TRAMP-путь с sudo."
(if (and (string-prefix-p "/etc/" filename)
(not (file-writable-p filename)))
(apply orig-fn (concat "/sudo::" filename) args)
(apply orig-fn filename args)))
(advice-add 'find-file :around #'my/etc-as-root)
;; Отменить: (advice-remove 'find-file #'my/etc-as-root)
Десять строк меняют поведение базовой команды редактора. Ни перекомпиляции, ни перезапуска, ни ожидания, пока мейнтейнер примет патч. Это и есть тот самый «Emacs-эффект», который сложно объяснить, пока не почувствуешь.
Цена у этого честная и заметная: раз всё редактируется, то всё и ломается. Кривая advice в чужом пакете кладёт редактор так же надёжно, как кривой патч в ядре.
Всё есть буфер
Второй столп философии — единая абстракция. В Emacs любое содержимое — это буфер: открытый файл, вывод команды, интерфейс git, почтовый ящик, список директории, окно дополнения. Буфер существует в памяти независимо от того, показан ли он на экране.
Терминология Emacs старше принятой и потому путает:
| Термин Emacs | Что это на самом деле | Аналог в других редакторах |
|---|---|---|
| buffer | содержимое в памяти | буфер / открытая вкладка (данные) |
| window | прямоугольник, показывающий буфер | сплит / панель |
| frame | окно операционной системы | окно приложения |
| point | позиция курсора | caret |
| mark | вторая точка, задаёт регион | якорь выделения |
| minibuffer | строка внизу для ввода команд | command palette |
Практический смысл единой абстракции: все навыки переносятся. Умеете искать по буферу —
умеете искать в выводе компилятора, в письме и в списке коммитов. Умеете применять
макрос — примените его к результату grep. Умеете редактировать текст — значит, умеете
редактировать список файлов в dired и одним C-c C-c переименовать сто файлов (режим
wdired). Ни в одном редакторе, где каждая панель — отдельный виджет с собственными
правилами, такое невозможно в принципе.
Командный цикл: что происходит при нажатии клавиши
Понимание этого цикла снимает 80% вопросов «почему у меня не работает биндинг».
Три следствия, которые стоит запомнить:
- Порядок keymap’ов объясняет конфликты. Ваш биндинг «не работает», потому что минорный
мод перехватил клавишу раньше мажорного. Диагностика —
C-h k(какая команда реально привязана) иC-h c. Лечение — биндить в keymap нужного уровня или использовать minor-mode с высоким приоритетом. pre-command-hookиpost-command-hookвызываются после каждой команды — на них держатся подсветка скобок, авто-сохранение, индикаторы. И на них же легко посадить редактор: тяжёлая функция вpost-command-hookдаёт лаг при каждом движении курсора.- Всё это — один поток. Lisp в Emacs выполняется однопоточно;
make-threadиз Emacs 26 даёт кооперативные, а не вытесняющие потоки. Значит, любая долгая синхронная операция замораживает интерфейс целиком. Это главная архитектурная слабость Emacs, и её не чинят конфигом — только асинхронными процессами (make-process,url-retrieveс колбэком).
Emacs Lisp за десять минут
Elisp — диалект Lisp 1970-х с современными заплатками. Он не Scheme и не Common Lisp: свой
объектный слой (cl-defstruct, defclass из EIEIO), свой набор странностей. Но выучить
рабочий минимум занимает вечер.
;;; my-utils.el --- личные утилиты -*- lexical-binding: t; -*-
;; Переменные. defvar — глобальная с документацией, setq — присваивание.
(defvar my/project-root "~/work" "Корень рабочих проектов.")
;; Функция. Интерактивная = вызывается через M-x и биндится на клавишу.
(defun my/insert-iso-date (&optional with-time)
"Вставить текущую дату в формате ISO. С префиксом — вместе со временем."
(interactive "P") ; "P" = сырой префиксный аргумент C-u
(insert (format-time-string (if with-time "%Y-%m-%dT%H:%M:%S%z" "%Y-%m-%d"))))
;; Буфер-локальные переменные — сердце модальности Emacs.
;; Одна и та же переменная имеет разные значения в разных буферах.
(add-hook 'python-ts-mode-hook
(lambda ()
(setq-local fill-column 88) ; black-совместимая ширина
(setq-local tab-width 4)))
;; Хуки — списки функций, вызываемых в определённый момент.
(add-hook 'before-save-hook #'delete-trailing-whitespace)
;; Замыкания работают только при lexical-binding: t (строка -*- выше).
;; Без неё Emacs использует динамическое связывание, и counter сломается.
(defun my/make-counter ()
(let ((n 0))
(lambda () (setq n (1+ n)) n)))
(provide 'my-utils)
;;; my-utils.el ends here
Пункт про lexical-binding — не педантизм. Динамическое связывание по умолчанию — самое
неприятное наследие Elisp: функция видит переменные вызывающего кода, замыкания не
захватывают окружение, отладка превращается в гадание. Emacs 30 уже выдаёт предупреждение,
если файл открыт без магической строки. Всегда ставьте её первой строкой в каждом .el.
Обязательные привычки при работе с Elisp:
C-x C-e— выполнить выражение перед курсором. Изменили функцию — выполнили — она уже новая. Это REPL-driven разработка внутри редактора.M-x ielm— полноценный REPL.C-h f/C-h v/C-h k/C-h o— что делает функция, переменная, клавиша, символ. Это и есть «self-documenting»: документация не сайт, а часть образа в памяти.M-x debug-on-entry,M-x edebug-defun— пошаговая отладка Lisp прямо в исходнике.
Конфигурация: как это должно выглядеть в 2026 году
Самая частая ошибка новичка — копипастить чужой init.el на 2000 строк. Второй по частоте —
начинать с Doom или Spacemacs, не понимая базы, и через полгода не уметь починить сломавшееся.
Рабочая схема — маленький осмысленный конфиг на use-package (встроен с Emacs 29).
early-init.el — выполняется до инициализации GUI и пакетной системы. Здесь живёт то, что
влияет на старт:
;;; early-init.el --- до GUI и package.el -*- lexical-binding: t; -*-
;; На время старта отключаем GC: сборка мусора при загрузке пакетов
;; съедает больше времени, чем экономит памяти. Вернём разумное значение потом.
(setq gc-cons-threshold most-positive-fixnum
gc-cons-percentage 0.6)
;; Не рисовать элементы, которые мы всё равно выключим — экономит перерисовку.
(push '(menu-bar-lines . 0) default-frame-alist)
(push '(tool-bar-lines . 0) default-frame-alist)
(push '(vertical-scroll-bars) default-frame-alist)
;; Не подставлять .el, если рядом лежит свежий .elc/.eln.
(setq load-prefer-newer t
package-enable-at-startup t
frame-inhibit-implied-resize t)
init.el — основное. Ниже конфиг, который реально можно взять как стартовый: он про
редактирование, а не про красоту.
;;; init.el --- рабочая конфигурация -*- lexical-binding: t; -*-
(require 'package)
(setq package-archives
'(("gnu" . "https://elpa.gnu.org/packages/")
("nongnu" . "https://elpa.nongnu.org/nongnu/")
("melpa" . "https://melpa.org/packages/")))
(unless package-archive-contents (package-refresh-contents))
(require 'use-package)
(setq use-package-always-ensure t ; ставить пакет, если его нет
use-package-always-defer t) ; грузить лениво — это главный рычаг старта
;; ---------- базовое поведение ----------
(setq-default indent-tabs-mode nil tab-width 4 fill-column 100)
(setq make-backup-files nil ; бэкапы рядом с файлом мешают git
auto-save-default t
create-lockfiles nil
require-final-newline t
sentence-end-double-space nil
use-short-answers t) ; y/n вместо yes/no, Emacs 28+
(save-place-mode 1) ; вернуться на ту же строку при открытии
(recentf-mode 1) ; список недавних файлов
(global-auto-revert-mode 1) ; подхватывать изменения с диска (git switch)
(delete-selection-mode 1)
(electric-pair-mode 1)
(global-so-long-mode 1) ; не умирать на файлах с километровыми строками
(when (fboundp 'which-key-mode) (which-key-mode 1))
;; ---------- минибуфер: vertico + orderless + marginalia + consult ----------
;; Этот стек заменил тяжёлые helm/ivy: он опирается на штатный completing-read,
;; поэтому работает со ВСЕМИ командами Emacs, включая ещё не написанные.
(use-package vertico :demand t :init (vertico-mode 1))
(use-package marginalia :demand t :init (marginalia-mode 1))
(use-package orderless
:demand t
:custom (completion-styles '(orderless basic))
(completion-category-overrides '((file (styles basic partial-completion)))))
(use-package consult
:bind (("C-s" . consult-line) ; поиск по буферу со списком совпадений
("C-x b" . consult-buffer) ; буферы, файлы, закладки в одном списке
("M-g g" . consult-goto-line)
("M-s r" . consult-ripgrep))) ; grep по проекту, потоковый
(use-package embark
:bind (("C-." . embark-act))) ; «что я могу сделать с этим объектом?»
;; ---------- дополнение в буфере ----------
(use-package corfu
:demand t
:init (global-corfu-mode 1)
:custom (corfu-auto t) (corfu-auto-delay 0.15) (corfu-cycle t))
;; ---------- проекты и git ----------
(use-package project :ensure nil ; штатный, не тянем из MELPA
:bind ("C-x p g" . project-find-regexp))
(use-package magit
:bind ("C-x g" . magit-status))
;; ---------- синтаксис: tree-sitter вместо регулярок ----------
(setq treesit-language-source-alist
'((python "https://github.com/tree-sitter/tree-sitter-python")
(typescript "https://github.com/tree-sitter/tree-sitter-typescript"
"master" "typescript/src")
(go "https://github.com/tree-sitter/tree-sitter-go")))
;; Один раз: M-x treesit-install-language-grammar
(setq major-mode-remap-alist
'((python-mode . python-ts-mode)
(go-mode . go-ts-mode)
(typescript-mode . typescript-ts-mode)))
;; ---------- LSP: eglot, встроен с Emacs 29 ----------
(use-package eglot :ensure nil
:hook ((python-ts-mode go-ts-mode typescript-ts-mode) . eglot-ensure)
:bind (:map eglot-mode-map
("C-c l r" . eglot-rename)
("C-c l a" . eglot-code-actions)
("C-c l f" . eglot-format-buffer))
:config
(setq eglot-autoshutdown t
eglot-events-buffer-size 0) ; логи протокола едят память и время
(add-hook 'before-save-hook
(lambda () (when (eglot-managed-p) (eglot-format-buffer)))))
;; ---------- GC обратно в разумные рамки ----------
(add-hook 'emacs-startup-hook
(lambda ()
(setq gc-cons-threshold (* 64 1024 1024) ; 64 МБ вместо 800 КБ
gc-cons-percentage 0.2)
(message "Старт за %.2f с, сборок мусора: %d"
(float-time (time-subtract after-init-time before-init-time))
gcs-done)))
Комментарий про gc-cons-threshold: значение по умолчанию — 800 000 байт, оно из эпохи, когда
у машин было 32 МБ памяти. На современном железе редактор с таким порогом собирает мусор
десятки раз в минуту, и это ощущается как микрозаикания при наборе. 32–64 МБ — здравый
компромисс; ставить most-positive-fixnum навсегда не нужно, иначе редкая, но многосекундная
пауза заменит частые короткие.
Eglot или lsp-mode
Оба живы, выбор реальный.
| eglot | lsp-mode | |
|---|---|---|
| Поставка | встроен с Emacs 29 | MELPA |
| Философия | минимализм, переиспользует штатные xref, flymake, eldoc |
собственный богатый UI |
| Зависимости | нулевые | десятки |
| Конфигурация | почти не нужна | много ручек |
| UI-плюшки | нет, берёт из ядра | lsp-ui, доктор, sideline, breadcrumbs |
| Скорость на больших проектах | обычно легче | тяжелее, но настраивается |
Практика: начинайте с eglot. Если чего-то критически не хватает (например, нужны специфичные
расширения протокола для Java через lsp-java) — переезжайте на lsp-mode. Обратный переезд
случается чаще прямого.
Org-mode: почему это отдельная технология
Org-mode — plain-text формат и режим редактирования, написанный Карстеном Домиником в 2003 году и с тех пор входящий в поставку. Со стороны выглядит как Markdown с чекбоксами. На деле это три разные системы, соединённые общим синтаксисом: структурный редактор, система планирования и среда исполняемых документов.
#+TITLE: Рабочий план
#+STARTUP: overview
* Проект «Оплата» :work:payments:
** TODO Починить идемпотентность вебхука :bug:
DEADLINE: <2026-07-24 Пт>
:PROPERTIES:
:Effort: 4h
:JIRA: PAY-1841
:END:
Дубли приходят, если провайдер ретраит быстрее, чем мы коммитим транзакцию.
- [X] воспроизвести на стенде
- [ ] добавить уникальный индекс по (provider, event_id)
- [ ] бэкофилл дублей
** WAIT Согласовать лимиты с рисками
:LOGBOOK:
- State "WAIT" from "TODO" [2026-07-17 Пт 12:03] \\
Ждём ответа команды рисков.
:END:
** DONE Метрики по отказам
CLOSED: [2026-07-15 Ср 18:20]
* Заметки
** Как считать SLA вебхуков
Ссылка на тикет: [[https://jira.example.com/PAY-1841][PAY-1841]]
Файл читается глазами, кладётся в git, диффится по-человечески и при этом является базой данных: по нему строятся представления.
Рабочие процессы как конечный автомат
org-todo-keywords задаёт не просто метки, а состояния со смыслом: переход в некоторые из них
логируется, некоторые считаются «завершёнными».
(use-package org
:ensure nil
:custom
(org-directory "~/org")
(org-agenda-files '("~/org/work.org" "~/org/personal.org"))
(org-todo-keywords
'((sequence "TODO(t)" "NEXT(n)" "DOING(o!)" "WAIT(w@/!)"
"|" "DONE(d!)" "CANCELLED(c@)")))
;; (o!) — логировать время входа; (w@/!) — спросить причину и записать переход
(org-log-into-drawer t) ; журнал в :LOGBOOK:, чтобы не засорять текст
(org-agenda-start-on-weekday nil)
(org-startup-indented t)
:bind (("C-c a" . org-agenda)
("C-c c" . org-capture))
:config
(setq org-capture-templates
'(("t" "Задача в инбокс" entry
(file+headline "~/org/work.org" "Инбокс")
"* TODO %?\n :PROPERTIES:\n :CREATED: %U\n :END:\n %a\n")
("b" "Баг из кода" entry
(file+headline "~/org/work.org" "Баги")
;; %a — ссылка на текущую позицию в файле, работает и для TRAMP
"* TODO %? :bug:\n %a\n %i\n")
("j" "Журнал дня" entry
(file+olp+datetree "~/org/journal.org")
"* %U %?\n"))))
Ценность здесь не в TODO-списке — их пишут все. Ценность в org-capture: нажали C-c c b,
находясь в строке кода, — задача создана со ссылкой на файл и строку, и вы вернулись туда, где
были, через полторы секунды. Мысль не потеряна, контекст не переключён. Это тот случай, когда
инструмент реально экономит внимание, а не время.
org-agenda собирает из всех файлов единый вид по датам, тегам и состояниям — по сути SQL-запрос
к вашим текстовым файлам. Плюс org-clock-in/org-clock-out даёт учёт времени и отчёты
(org-clock-report), который не требует ни одного стороннего сервиса.
Babel: исполняемые документы
org-babel позволяет вставлять блоки кода на трёх десятках языков, выполнять их прямо в
документе и подставлять результат. Это literate programming в рабочем виде — подход описан в
рецензируемой статье
A Multi-Language Computing Environment for Literate Programming and Reproducible Research
(Journal of Statistical Software, 2012).
#+NAME: fetch-orders
#+BEGIN_SRC sql :engine postgresql :dbhost replica.internal :database shop :results table
SELECT status, count(*) FROM orders WHERE created_at > now() - interval '1 day'
GROUP BY status ORDER BY 2 DESC;
#+END_SRC
#+RESULTS: fetch-orders
| status | count |
|---------+-------|
| paid | 8421 |
| pending | 312 |
| failed | 47 |
#+BEGIN_SRC python :var data=fetch-orders :results output
# Результат SQL-блока приходит сюда как список списков — язык не важен.
total = sum(int(row[1]) for row in data)
for status, n in data:
print(f"{status:>8}: {int(n)/total:6.2%}")
#+END_SRC
Три вещи, которые из этого получаются:
- Runbook, который выполняется. Инструкция по разбору инцидента с живыми запросами вместо «скопируйте вот этот SQL куда-нибудь».
- Литературная конфигурация.
org-babel-tangle(C-c C-v t) вытаскивает блоки в реальные файлы: пишетеconfig.orgс объяснениями, получаетеinit.el,.zshrc,Dockerfile. Блоки помечаются:tangle ~/.config/foo/bar.conf. - Экспорт.
ox-*рендерит тот же документ в HTML, PDF через LaTeX, Markdown, reveal.js.
Честная оговорка: Jupyter выигрывает у Babel в интерактивности графики и в экосистеме, Quarto — в оформлении публикаций. Babel выигрывает в том, что документ остаётся plain text (нормальный diff в git), полиглотен по умолчанию и не требует ничего, кроме уже открытого редактора.
Практика продакшена: за что Emacs держат на работе
Есть набор вещей, которые в Emacs объективно сильнее, чем у конкурентов, и обычно именно они удерживают людей.
Magit. Интерфейс к git, который вместо «кнопок для частых операций» даёт прямое отображение
модели git на буфер: staging отдельных строк одной клавишей s, интерактивный rebase, где
строки переставляются как текст, разрешение конфликтов, reflog, bisect. Люди ставят Emacs
исключительно ради Magit и работают в другом редакторе — это не шутка, это распространённый
сценарий.
TRAMP. Прозрачная работа с удалёнными файлами: путь /ssh:deploy@prod-3:/etc/nginx/nginx.conf
открывается как локальный, со всеми режимами, LSP и git. Многошаговые прыжки через бастион
пишутся одной строкой:
;; Через jump-host на приватный хост, затем sudo — всё в одном пути:
;; C-x C-f /ssh:jump.corp|ssh:app-7.internal|sudo:app-7.internal:/var/log/app.log
(setq tramp-default-method "ssh"
remote-file-name-inhibit-cache 60
tramp-verbose 1) ; выше 3 — заметно медленнее
;; Ускорение: не искать проектные файлы и не запускать vc на удалённых путях
(setq vc-handled-backends '(Git))
(connection-local-set-profile-variables
'remote-fast '((vc-handled-backends . nil)))
(connection-local-set-profiles
'(:application tramp) 'remote-fast)
VS Code Remote SSH делает похожее, но иначе: он ставит серверную часть на удалённую машину. Это мощнее для полноценной разработки и невозможно там, где на хост нельзя ничего класть. TRAMP работает поверх обычного ssh и не оставляет следов.
Daemon и мгновенный старт. Долгий запуск Emacs — известная претензия. Ответ архитектурный: запускать один раз.
# systemd user unit идёт в поставке Emacs 26+
systemctl --user enable --now emacs
# В ~/.zshrc или ~/.bashrc:
export EDITOR="emacsclient -nw -a ''" # -a '' поднимет демон, если он не запущен
export VISUAL="emacsclient -c -a ''"
alias e='emacsclient -n' # -n = не ждать закрытия буфера
Замер старта, который стоит сделать самому:
# Чистый Emacs без конфига — базовая линия
emacs -Q --eval '(message "%s" (emacs-init-time))' --batch
# Ваш конфиг
emacs --eval '(progn (message "%s" (emacs-init-time)) (kill-emacs))'
# Профилирование: какой пакет съел время
emacs -l ~/.emacs.d/init.el --eval '(profiler-start (quote cpu))'
Нативная компиляция. С Emacs 28 Lisp компилируется в машинный код через libgccjit
(.eln-файлы). Проверить, что сборка её поддерживает: M-: (native-comp-available-p).
Реальный выигрыш на нагруженном Lisp — обычно в 2–4 раза на конкретных операциях (сложные
шрифт-локи, org-agenda на тысячах записей); на «печатании в буфер» разницы не почувствуете,
потому что там узкое место — redisplay на C. Первый запуск после установки пакетов будет
медленнее: идёт фоновая компиляция.
Evil: Vim внутри Emacs
Отдельно про то, что примиряет два лагеря. evil-mode — не эмуляция «на глазок», а честная
реализация модального редактирования Vim, включая операторы, текстовые объекты, регистры и
ex-команды. Про сам модальный подход подробно — в
статье про основы Vim и
продвинутом Vim.
(use-package evil
:demand t
:init
(setq evil-want-keybinding nil ; обязательно ДО загрузки, иначе конфликты
evil-want-C-u-scroll t
evil-undo-system 'undo-redo ; штатный undo-redo из Emacs 28
evil-respect-visual-line-mode t)
:config
(evil-mode 1))
(use-package evil-collection ; evil-биндинги для magit, dired, ibuffer...
:after evil
:config (evil-collection-init))
(use-package general ; удобный SPC-лидер в стиле Doom/Spacemacs
:after evil
:config
(general-create-definer my/leader
:states '(normal visual) :prefix "SPC")
(my/leader
"f f" #'find-file
"b b" #'consult-buffer
"g g" #'magit-status
"p r" #'consult-ripgrep))
Комбинация «модальное редактирование Vim + расширяемость Emacs» — вполне рациональный выбор:
вы берёте лучшую известную грамматику команд и лучшую известную систему расширения. Именно на
этом построены Doom Emacs и Spacemacs. Цена — дополнительный слой, который иногда конфликтует
с пакетами, не знающими про evil; evil-collection закрывает большую часть, но не всё.
Честное сравнение
| Критерий | Emacs | Neovim | VS Code | JetBrains |
|---|---|---|---|---|
| Язык расширения | Emacs Lisp | Lua | TypeScript | Kotlin/Java |
| Модель расширения | переопределение изнутри | плагины + встроенный Lua | API + процесс-изолятор | плагины через SDK |
| Правка поведения на лету | да, полностью | частично | нет, требует reload | нет |
| Порог входа | очень высокий | высокий | низкий | средний |
| Старт «из коробки» | почти пустой | почти пустой | рабочий сразу | рабочий сразу |
| LSP | eglot / lsp-mode | встроенный | встроенный | не нужен, свой анализ |
| Глубина рефакторингов | средняя, зависит от LSP | средняя | средняя | лучшая на рынке |
| Работа с git | Magit, лучший в классе | плагины | хорошо | хорошо |
| Удалённое редактирование | TRAMP, без агента | плагины | Remote SSH с агентом | Gateway с агентом |
| Отзывчивость UI | однопоточный, может замирать | быстрый | обычно ровный | тяжёлый, но параллельный |
| Заметки/планирование | org-mode, вне конкуренции | нет | расширения | нет |
| Сколько живёт конфиг | десятилетия | годы | годы | почти не нужен |
| Кто поддерживает | GNU + сообщество | сообщество | Microsoft | коммерческая компания |
По распространённости честная картина такая: в опросах Stack Overflow последних лет Emacs стабильно держится в районе нескольких процентов — примерно на порядок ниже VS Code (см. Stack Overflow Developer Survey). Это не «умирание», а стабильная ниша; абсолютное число пользователей за десять лет скорее выросло вместе с отраслью.
Сколько это стоит
Числа ниже — оценка по типичному опыту, а не измерение, но порядок правильный:
| Этап | Реалистичные затраты |
|---|---|
Пройти встроенный туториал C-h t |
30–40 минут |
| Перестать думать о навигации и правках | 2–4 недели ежедневной работы |
| Собрать свой конфиг, который не стыдно | 10–20 часов, размазанных по месяцам |
| Начать писать elisp под свои задачи | ещё 10–15 часов |
| Обслуживание после этого | 1–2 часа в месяц, плюс всплеск при мажорном апдейте |
Просадка производительности в первые 2–3 недели реальна и составляет десятки процентов. Если у вас идёт горящий релиз — это не то время, чтобы менять редактор. Общая логика оценки «окупится / не окупится» разобрана в обзоре трека.
Типичные ошибки
- Начать с чужого мегаконфига. Doom Emacs даёт отличный результат за час, но когда через полгода что-то сломается, вы будете отлаживать чужие 20 000 строк Lisp без базы. Разумный путь: месяц на голом Emacs с конфигом на 100 строк, потом решайте.
- Не поставить
lexical-binding: t. Приводит к загадочным багам в замыканиях, которые вы будете искать часами. - Тяжёлые функции в
post-command-hookилиafter-change-functions. Мгновенный результат — лаг при наборе. Проверять черезM-x profiler-startв режиме cpu. - Синхронные вызовы внешних процессов.
shell-command-to-stringна медленной команде вешает весь редактор. Используйтеmake-processс фильтром илиasync-shell-command. use-packageбез:defer. Двадцать пакетов, загруженных жадно, превращают старт в пять секунд. Правило::hook,:bind,:commandsдают отложенную загрузку автоматически;:demand tставится осознанно и редко.tramp-verboseбольше 3 «для отладки» и забыть. Удалённые операции замедляются в разы.- Игнорировать RSI. Аккорды с Control реально нагружают мизинец. Минимум — переназначить
Caps Lock на Control на уровне ОС; дальше — evil-mode или
god-mode. - Хранить конфиг только локально. Emacs-конфиг — это ваш накопленный инструмент за годы; он должен лежать в git. Про организацию dotfiles и синхронизацию окружения — отдельная статья трека.
Так почему он всё ещё жив
Не из-за ностальгии. Три конкретные причины.
Стабильность интерфейса на масштабе десятилетий. Конфиг 2010 года по большей части работает сегодня. Ни один коммерческий редактор такого не обещает и обещать не может: у Atom, который был «будущим редакторов», архив на GitHub с 2022 года. Emacs пережил TECO, XEmacs, Atom и переживёт что-нибудь ещё — потому что у него нет владельца, которому он может надоесть.
Единая среда вместо набора приложений. Код, git, заметки, планирование, почта, shell, удалённые серверы — одни и те же клавиши, одни и те же способы поиска, одни и те же макросы. Экономия здесь не в скорости операций, а в отсутствии переключений контекста между несовместимыми интерфейсами.
Расширяемость, которая не упирается в API. Когда нужна нестандартная вещь — не «есть ли плагин», а «напишу за двадцать минут». Для человека, который годами решает узкие задачи в конкретной предметной области, разница накапливается.
И честная другая сторона: Emacs требует, чтобы вы стали его соавтором. Кто не хочет — проиграет VS Code по любому критерию первых месяцев. Это не универсальный инструмент, а инструмент для конкретного склада: для тех, кому приятно затачивать среду, и для тех, у кого задачи достаточно долгоживущие, чтобы заточка окупилась.
Мини-итог
- Emacs — Lisp-машина: ~5% на C, остальное на Elisp, переопределяемом во время работы.
- Единая абстракция «всё есть буфер» делает навыки переносимыми между кодом, git, почтой и shell.
- Командный цикл и приоритет keymap’ов объясняют почти все проблемы с биндингами; однопоточность объясняет почти все проблемы с отзывчивостью.
- Современная база 2026 года:
use-package+ vertico/corfu + eglot + treesit + magit; всё, кроме первых двух групп, уже в поставке. - Org-mode — три системы в одной: структурный редактор, планировщик и среда исполняемых документов.
- Демон плюс
emacsclientснимают вопрос времени старта; native-comp снимает часть вопросов скорости. - Стоимость входа реальна: недели до продуктивности и часы на конфиг. Окупается на долгом горизонте и при желании программировать свой инструмент.
Источники
- GNU Emacs Manual — основное руководство.
- Emacs Lisp Reference Manual — справочник по языку и внутренностям.
- An Introduction to Programming in Emacs Lisp — вводный курс Роберта Чассела.
- Org Mode Manual и Org Guide.
- Magit User Manual — документация Magit.
- Eglot Manual и TRAMP Manual.
- Richard Stallman, EMACS: The Extensible, Customizable Self-Documenting Display Editor, 1981.
- Schulte et al., A Multi-Language Computing Environment for Literate Programming and Reproducible Research, JSS 46(3), 2012.
- Emacs NEWS — что менялось от версии к версии.
- MELPA и GNU ELPA — репозитории пакетов.
- Doom Emacs — готовая конфигурация, полезна как источник идей.
Что дальше
Мы разобрали инструмент, который требует от вас максимума и отдаёт максимум. Следующая статья — про противоположный полюс: редактор, который работает сразу, ставится в два клика и именно поэтому занял рынок.
VS Code: архитектура, расширения, отладка, remote и devcontainers