0044 — setq VS custom-set-variables

Наткнулся на пост про отличия между setq, setq-default, set-default и custom-set-variables.

TL; DR: автор не обнаружил значимых различий между setq и custom-set-variables, однако, упомянул, что разработчики пакетов могут перехватывать вызов custom-set-variables для своих переменных и тем самым делать какие-то дополнительные действия, связанные с переменной.

Я использую в своём init.el следующую схему:

  • если переменная относится к базовой функциональности Emacs, я смотрю, куда ведёт меня xref-dind-definitions ([M-.]):

    • если по ссылке открывается код Emacs, то это явно setq;
    • если по ссылке переменная, объявленная с помощью defcustom, то это custom-set-variables;
  • если переменная определена в каком-то пакете, я использую макрос :custom из состава use-package.

    В документации пакета на сайте GNU написано следующее (выделил самое важное):

    In Emacs, you normally set customizable variables (user options) using the M-x customize interface (see Easy Customization in GNU Emacs Manual). We recommend this method for most users. However, it is also possible to set them in your use-package declarations by using the :custom keyword. This is better than using setq in a :config block, as customizable variables might have some code associated with it that Emacs will execute when you assign values to them. (In Emacs 29 and later, there is also the new setopt macro that does this for you.)

    Т. е. когда вы используете custom-set-variables, разработчик пакета может перехватить присваивание значения переменной, объявленной с помощью defcustom, и выполнить какой-то дополнительный код. При вызове setq такой «магии» (никакой магии, обычный паттерн «Наблюдатель») нет.

В текущем виде мои настройки задаются так:

;; Если используется старая версия EMACS, нужно указать параметры протокола TLS.
;; В противном случае будут проблемы при загрузке архива пакетов.
(when (< emacs-major-version 27)
  (require 'gnutls)
  (custom-set-variables '(gnutls-algorithm-priority "NORMAL:-VERS-TLS1.3")))

(setq
 create-lockfiles nil               ;; Не надо создавать lock-файлы
 cursor-type 'bar                   ;; Курсор в виде вертикальной черты
 delete-by-moving-to-trash t        ;; При удалении файла помещать его в Корзину
 gc-cons-threshold (* 50 1000 1000) ;; "Увеличим лимит для сборщика мусора с 800 000 до 50 000 000
 load-prefer-newer t                ;; Если есть файл elc, но el новее, загрузить el-файл
 locale-coding-system 'utf-8        ;; UTF-8 по умолчанию
 menu-bar-mode nil                  ;; Отключить показ главного меню
 ring-bell-function #'ignore        ;; Заблокировать пищание
 scroll-conservatively 100000       ;; TODO: проверить, что это такое
 scroll-margin 4                    ;; При прокрутке помещать курсор на 5 строк выше / ниже верхней / нижней границы окна
 scroll-preserve-screen-position 1  ;; TODO: проверить, что это такое
 show-trailing-whitespace t         ;; Показывать висячие пробелы
 source-directory (format           ;; Путь к исходному коду EMACS
                   "/usr/share/emacs/%s.%s/src/"
                   emacs-major-version
                   emacs-minor-version)

 tool-bar-mode nil                  ;; Отключить показ панели инструментов
 truncate-lines 1                   ;; Обрезать длинные строки
 use-dialog-box nil                 ;; Диалоговые окна не нужны, будем использовать текстовый интерфейс
 user-full-name "Dunaevsky Maxim"   ;; Имя пользователя

 visible-bell t)                    ;; Эффект мигания при переходе в буфер

(custom-set-variables
 '(inhibit-startup-screen t "Не надо показывать загрузочный экран")
 '(initial-scratch-message nil "В новых буферах не нужно ничего писать")
 '(safe-local-variable-values
   '((buffer-env-script-name . ".venv/bin/activate")
     (electric-pair-preserve-balance . t)
     (fill-column . 70)
     (frozen_string_literal . true)) nil nil "Безопасные значения локальных переменных")
 '(save-place-file (expand-file-name ".emacs-places" init-el-config-dir) "Хранить данные о позициях в открытых файлах в .emacs-places")
 '(save-place-forget-unreadable-files t "Если файл нельзя открыть, то и помнить о нём ничего не надо")
 '(tab-always-indent 'complete "Если можно — выровнять текст, иначе — автодополнение")
 '(user-mail-address "dunmaksim@yandex.ru"))

Есть ещё небольшой фрагмент с загрузкой custom.el, этот код у меня в самом конце init.el:

(defconst init-el-config-dir
  (file-name-directory user-init-file)
  "Корневой каталог для размещения настроек.") ;; ~/.emacs.d/

;; -> CUS-EDIT
;; Встроенный пакет.
;; Управление custom-файлами
(use-package cus-edit
  :custom
  (custom-file
   (expand-file-name
    (convert-standard-filename "custom.el")
    init-el-config-dir)
   "Файл для сохранения пользовательских настроек, сделанных в customize."))

;; -> CUSTOM FILE
;; Пользовательские настройки, сделанные через CUSTOMIZE
(when (file-exists-p custom-file)
  (load custom-file))

Вроде бы это работает, и работает хорошо.