Статья написана под впечатлением от статьи Emacs Flaw: Simple Changes Emacs Should Adopt, написанной широко известным в узких кругах Emacs-троллем и автором множества пакетов Xah Lee. Кстати, он один из противников тотального внедрения HTTPS, поэтому статья по ссылке доступна только по HTTP.

Итак, как же нам улучшить Emacs?

Терминология

В Emacs полно устаревшей терминологии. Новичкам было бы значительно проще, если бы вместо frames (фреймы) и windows (окна) соответствующие сущности назывались windows (окна) и panels (панели). Эту идею больше 10 лет назад высказал ещё сам RMS в надежде на то, что терминология будет осовременена в ближайшее время:

Conceivably we could rename "window" to "pane" and "frame" to "window". I think the two renamings would have to be done in two different releases, perhaps a year or two apart.

Whether it is worth the trouble, I don't know.

--
Dr Richard Stallman
President, Free Software Foundation
51 Franklin St
Boston MA 02110
USA
www.fsf.org www.gnu.org

Это было написано 6 января 2014 года. С тех пор ничего не изменилось. Просто запомните:

  • frame это окно, созданное ОС;

  • window это панель внутри фрейма.

Основная сущность Emacs называется буфером (buffer). В IT это слово уже давно приобрело собственное значение: область для промежуточного хранения данных. Если по сути, то название buffer в Emacs этому определению не противоречит. Другое дело, что файловый буфер в Emacs это не то же самое что файловый буфер в современных ОС. То, что под этим термином понимают в Emacs -- это просто файл, открытый в редакторе.

Клавиши в Emacs, как и в 1980-х, называются Control, Meta, Super и Hyper. На современных клавиатурах вы не найдёте Meta, Super и Hyper, а Control не тот, что на клавиатурах Lisp-машин.

На современных клавиатурах вместо Meta стоит Alt. Проблема в том, что Meta и Ctrl на клавиатурах Lisp-машин находились там, где сейчас находятся Ctrl и Alt. Это делает команды Emacs неудобными, потому что вместо нажатия Alt большим пальцем мы вынуждены ломать мизинец об Ctrl.

Клавиши местами поменяли, а сочетания переделать забыли, с кем не бывает? Программисты накосячили, скоро исправят. Ведь исправят же?

Изображение с просторов интернета
Изображение с просторов интернета

Scratch и initial-buffer

Буфер *scratch* вряд ли вам понадобится, если только вы не программист на Emacs Lisp. Однако, по какой-то странной причине Emacs создаёт его при каждом запуске. Чтобы предотвратить это, добавьте в init.elкод:

;; Буфер *scratch* не нужен, если вы не программист Emacs Lisp
(defun init-kill-scratch ()
  "Закрыть буфер *scratch* при запуске редактора или подключении клиента."
  (when (get-buffer "*scratch*")
    (kill-buffer "*scratch*")))
(add-hook 'after-init-hook 'init-kill-scratch)
(add-hook 'server-after-make-frame-hook 'init-kill-scratch)

А ещё при запуске Emacs показывает стартовую страницу с информацией о себе. Молодец. Первое, что делает большинство пользователей при запуске любого редактора или IDE -- эту страницу отключают. В Emacs надо делать всё самому:

(setopt inhibit-startup-screen t)

Задам наводящий вопрос: почему бы не сделать это поведением по умолчанию?

Сессии

Emacs умеет сохранять своё состояние между сессиями (запусками), а именно:

  • список открытых файлов;

  • список посещённых файлов;

  • состояние фреймов и окон;

  • позицию курсора в посещённых файлах;

  • историю команд, введённых в minibuffer.

По какой-то непонятной причине по умолчанию всё это выключено.

Иначе как осознанным вредительством это считать нельзя, ведь практически любой современный редактор делает это всё без дополнительных настроек. А вам нужно добавить в init.el код, чтобы получить то же самое:

;; 📦 DESKTOP
;; Сохранение состояния Emacs между сессиями.
;; https://www.gnu.org/software/emacs/manual/html_node/emacs/Saving-Emacs-Sessions.html
(use-package desktop
  :custom
  (desktop-dirname user-emacs-directory "Каталог для хранения файла .desktop.")
  (desktop-load-locked-desktop t "Загрузка файла .desktop даже если он заблокирован.")
  (desktop-restore-frames t "Восстанавливать фреймы.")
  (desktop-save t "Сохранять список открытых буферов, файлов и т. д. без лишних вопросов.")
  :config
  (desktop-save-mode t)
  (add-to-list 'delete-frame-functions 'desktop-save)
  (add-to-list 'desktop-modes-not-to-save 'dired-mode)
  :hook
  (after-init . desktop-read)
  (server-after-make-frame . desktop-read)
  (kill-emacs . (lambda () (desktop-save user-emacs-directory t)))
  (server-done . desktop-save))


;; 📦 RECENTF-MODE
;; Список последних открытых файлов.
(use-package recentf
  :custom
  (recentf-max-saved-items 100 "Помнить последние 100 файлов")
  (recentf-save-file (locate-user-emacs-file "recentf") "Хранить список в файле .emacs.d/recentf")
  :config (recentf-mode t))


;; 📦 SAVEPLACE
;; Запоминание позиции курсора в посещённых файлах.
(use-package saveplace
  :custom
  (save-place-forget-unreadable-files t "Не запоминать положение в нечитаемых файлах.")
  :config
  (save-place-mode t))


;; 📦 SAVEHIST
;; Запоминание истории команд.
(use-package savehist
  :hook
  (server-done . savehist-save)
  (kill-emacs . savehist-save)
  :config
  (add-to-list 'delete-frame-functions 'savehist-save)
  (savehist-mode t))

Удаление выделенного текста

Emacs не модальный редактор, в отличие от VIM, NeoVim и Helix. Т. е. для ввода текста не нужно менять режим, достаточно начать нажимать на клавиши. Однако, поведение Emacs при работе с выделенным текстом по умолчанию отличается от того, к чему нас всех приучили современные ОС.

Допустим, мы выделили текст любым удобным способом. В любом нормальном редакторе, IDE или поле ввода, созданном ОС, поведение стандартизировано:

Клавиша

Действие

Del

Удалить выделенный текст

BackSpace

Удалить выделенный текст

Алфавитно-цифровые клавиши

Удалить выделенный текст, вставить новый знак в позиции курсора

Но Emacs не такой как все:

Клавиша

Действие

Del

Удалить знак справа от курсора

BackSpace

Удалить знак слева от курсора

Алфавитно-цифровые клавиши

Вставить новый знак в позиции курсора

Почему бы не привести настройки Emacs к современным стандартам? Добавим три строки кода и одну строку комментария в init.el:

;; 📦 DELSEL
(use-package delsel
  :config
  (delete-selection-mode t)) ;; Удалять выделенный фрагмент при вводе текста

Нужный режим в Emacs есть, просто по непонятной причине выключен!

Внешний вид курсора

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

Но в Emacs по умолчанию используется прямоугольник, даже если дело происходит в GUI. Неужели этот код такой сложный?

(setopt cursor-type 'bar)

Ну, окей, допустим, надо определить, где мы находимся, а то вдруг в терминале работаем:

(when (display-graphics-p (selected-frame))
  (setopt cursor-type 'bar))

Не выглядит как задача на 34 Story Point, правда?

Ответы на вопросы

По умолчанию для ответов на вопросы Emacs требует ввода слов yes и no. Уточню, что мы сейчас рассматриваем случай, когда использование UI выключено или невозможно (например, в терминалах). Удивительно, но в Emacs по умолчанию НЕ сделаны эти настройки:

(setopt use-short-answers t)      ;; Новые версии
(defalias 'yes-or-no-p 'y-or-n-p) ;; Старые версии

Пищание

Знаете, что меня бесит в настройках по умолчанию? Пищание! Emacs пищит всякий раз, когда вы пытаетесь сделать что-то не так:

  • прокрутить содержимое вниз, находясь в конце файла;

  • перейти в другое окно, когда активен минибуфер;

  • и многое, многое другое!

Тут Emacs вам не помогает, а пугает своим писком. Можно ли его отключить, не используя сторонние пакеты? Вы знаете ответ.

(setopt ring-bell-function 'ignore ;; Заменим писк ничегонеделанием
        visible-bell t)            ;; Лучше мигать чем пищать

Современные экраны

Emacs умеет плавно прокручивать текст. Режим pixel-scroll-precision-mode добавили ещё в версии 29.1. Однако он по умолчанию выключен.

;; 📦 PIXEL-SCROLL
;; Плавная прокрутка текста
(when (package-installed-p 'pixel-scroll)
  (use-package pixel-scroll
    :config
    (pixel-scroll-mode t)
    (pixel-scroll-precision-mode)))


;; 📦 FRAME
;; Управление фреймами.
(use-package frame
  :custom
  (frame-resize-pixelwise t "Размер фреймов считать по пикселям а не по символам"))

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

Удаление файлов

Зато с удаленим файлов всё в порядке! "Корзина не нужна" -- решили авторы Emacs, поэтому вызов операций удаления файлов приводит к их стиранию с диска, а не перемещению в корзину.

Не вижу ни одной причины, по которой не следовало бы сделать настройку delete-by-moving-to-trash включённой по умолчанию:

(setopt delete-by-moving-to-trash t)

C-z

Напоследок расскажу о поведении Emacs, от которого у меня отвалилась жопа. По умолчании при нажатии [Ctrl+Z] он не отменяет последнюю операцию, а сворачивается на панель задач в графическом режиме и уходит в фон в TUI. Уж казалось бы, сколько лет прошло! Можно ведь повесить undo на [Ctrl+Z]? Можно:

;; Изменим некоторые привязки клавиш по умолчанию
(require 'keymap)
(keymap-global-unset "C-z") ;; Такой Ctrl+Z нам не нужен
(use-package simple
  :bind (:map global-map
              ("C-z" . undo)))

Итоги

Я не настолько хорош, чтобы коммитить в master Emacs'а. Однако, я достаточно хорош, чтобы создать репозиторий "Emacs From Scratch": шаблон init.el, с которого можно начать писать свой собственный конфиг.