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

Emacs – текстовый редактор от сообщества GNU. Он расширяется при помощи языка Emacs Lisp и является одним из старейших текстовых редакторов, который используется многими и по сей день. Далее идёт моя конфигурация этого редактора.


Исходный код

Вклады

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

Лицензия

Я выступаю против копирайта и интеллектуальной собственности в целом, поэтому стараюсь копирайт избегать. По этой причине свой код распространяю на правах общественного достояния вашей страны. Для этого используется лицензия CC0. Если кратко, то можете использовать без ограничений вплоть до полной приватизации всего контента. К сожалению за чужие сниппеты не могу ручаться, они распространяются по своим лицензиям и я их не указывал, впрочем всё равно никто это не проверяет и я бы не стал напрягаться из-за авторских прав на конфигурацию текстового редактора.

О конфигурации

Минимальная конфигурация Emacs. Пакеты ставлю через Guix или package.el (elpa и melpa), для разделения ответственности использую грамотное программирование при помощи org-mode.

Версия Emacs 28+.

  • В Guix пакет emacs-next
  • В FreeBSD пакет emacs-devel
  • В Ubuntu PPA emacs28 и пакет emacs28-native

Из программирования поддерживаются C, Java, Python, Common Lisp / Scheme, Haskell, Idris, Ocaml, Ruby, PHP, SML, SQL, Erlang, редактирование веб-шаблонов и различных форматов конфигураций. Какие-то языки в лучшей мере настроены, какие-то хуже, но есть почти все плюс-минус популярные.

Для систем контроля версий использую встроенный VC и в некоторых случаях Magit. Для входа в виртуальные среды используется Direnv.

Для ведения заметок использую методологию Zettelkasten при помощи org-roam. Для доступа к оффлайн википедии и другим ресурсам Kiwix клиент, который стучится на сервер, который стоит дома.

Для коммуникаций клиент для Telegram telega, для IRC erc и электронной почты gnus, для Mastodon mastodon.el.

Для системного администрирования имеют пакеты для работы с Debian, NixOS, Guix. Также есть поддержка Docker, Docker-compose: можно запускать/стопать контейнеры, а также залетать в них через TRAMP.

Для установки необходимо установить пакеты Emacs из ../../guix/user.scm и выполнить tangle файла при помощи M-x org-babel-tangle-file. В дальнейшем для развёртывания конфигурации можно использовать M-x config-tangle или M-x config-load.

Данная конфигурация доступна также в сети интернет по адресу, но так как сайт обновляется нечасто, то там может лежать старая версия: https://w96k.dev/emacs.html

Исходники конфигурации лежат на Sourcehut: https://git.sr.ht/~w96k/dotfiles/tree/master/item/emacs

Распространяется на условиях свободной лицензии GNU GPL v3, реиспользование одобряется пока распространяется под той же лицензией.

Пакеты

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

Название Описание
Avy Прыжки по тексту
Anaconda Поддержка Python
Auctex Поддержка Tex и Latex
Browse Kill Ring Обзор буфера обмена
Haskell Mode Поддержка Haskell
Idris Mode Поддержка Idris
Magit Интерфейс к Git
Gitpatch Создание патчей
Goto Cgh Переход к последнему изменению в буфере
Debbugs Интерфейс к Debbugs
Deft Полнотекстовый поиск (использую для org-mode)
Direnv Изменение окружений при входе в директорию
Dumb Jump Прыжки по определениям при помощи grep и ему подобных
Docker Интерфейс к Docker
Exec Path From Shell Синхронизация путей чтения из .bashrc в Emacs
Expand Region Семантическое расширение выделения на курсоре
Robe Прыжки по определениям в Ruby
Geiser REPL для Scheme
Git Gutters Отметки слева с изменением кода по гиту
Guix Интерфейс к GNU Guix
Org Roam Ведение связанных друг с другом заметок
Gnuplot Поддержка Gnuplot (графики)
Nix Mode Поддержка Nix
Pdf Tools Рендер PDF файлов в Emacs
PHP Mode Поддержка PHP
Kiwix Чтение скачанных архивов веб-страниц
SML Mode Поддержка Standard ML
Sly/Slime REPL Common Lisp
Simple HTTPD Простой сервер HTTP
Telega Интерфейс к Telegram
Treemacs Сайдбар для навигации по проекту
Undo Tree Визуализация дерева отмен
Yasnippet и Yasnippet Snippets Сниппеты для текста и языков программирования
xref  
Web Mode Поддержка веб-шаблонов

Следующий блок нужен для тех, кто хочет использовать встроенный пакетный менеджер. Я использую Guix для большей части взаимодействий со сторонним кодом.

(require 'package)

(setq package-archives '(("gnu" . "http://elpa.gnu.org/packages/")
                   ("melpa" . "http://melpa.org/packages/")))

(when (< emacs-major-version 27)
  (package-initialize))

(require 'gnutls)

(when (not package-archive-contents)
  (progn
    (package-refresh-contents)
    (package-install 'avy)
    ;; (package-install 'anzu)
    (package-install 'anaconda-mode)
    (package-install 'auctex)
    (package-install 'rmsbolt)
    (package-install 'beginend)
    ;; (package-install 'composer)
    (package-install 'cinspect)
    (package-install 'debian-el)
    (package-install 'dpkg-dev-el)
    (package-install 'edebug-inline-result)
    ;;(package-install 'haskell-mode)
    ;;(package-install 'idris-mode)
    (package-install 'magit)
    (package-install 'git-timemachine)
    (package-install 'git-dwim)
    (package-install 'gitpatch)
    (package-install 'goto-chg)
    (package-install 'debbugs)
    ;;(package-install 'deft)
    (package-install 'direnv)
    (package-install 'dumb-jump)
    (package-install 'docker)
    (package-install 'docker-cli)
    (package-install 'docker-tramp)
    (package-install 'docker-compose-mode)
    (package-install 'org-sql)
    ;;(package-install 'eglot)
    (package-install 'eglot-java)
    (package-install 'mastodon)
    (package-install 'exec-path-from-shell)
    (package-install 'expand-region)
    (package-install 'flymake-php)
    (package-install 'flycheck)
    (package-install 'phpactor)
    ;;(package-install 'robe)
    (package-install 'geiser)
    (package-install 'geiser-guile)
    (package-install 'guix)
    (package-install 'git-gutter)
    (package-install 'gnuplot)
    ;;(package-install 'swiper)
    ;;(package-install 'sqlite3)
    (package-install 'org-roam)
    ;; (package-install 'org-roam-ui)
    (package-install 'org-download)
    (package-install 'org-babel-eval-in-repl)
    ;;(package-install 'ob-php)
    (package-install 'on-screen)
    ;;(package-install 'nix-mode)
    (package-install 'pdf-tools)
    ;;(package-install 'phpactor)
    ;; (package-install 'company-quickhelp)
    ;;(package-install 'ac-php)
    (package-install 'php-mode)
    (package-install 'php-quickhelp)
    (package-install 'phan)
    ;;(package-install 'php-cs-fixer)
    ;; (package-install 'company-php)
    ;;(package-install 'php-eldoc)
    ;;(package-install 'phps-mode)
    (package-install 'realgud)
    (package-install 'kiwix)
    ;;(package-install 'sml-mode)
    (package-install 'sly)
    (package-install 'simple-httpd)
    (package-install 'sudo-edit)
    ;;(package-install 'treemacs)
    (package-install 'undo-tree)
    (package-install 'yasnippet)
    (package-install 'yasnippet-snippets)
    (package-install 'vimrc-mode)
    (package-install 'flymake-phpcs)
    (package-install 'flymake-phpstan)
    (package-install 'no-littering)
    (package-install 'web-mode)
    (package-install 'which-key)
    (package-install 'reverse-im)
    (package-install 'imenu-list)
    (package-install 'isearch-mb)
    (package-install 'visual-fill-column)
    (package-install 'browse-kill-ring)
    (package-install 'corfu)
    (package-install 'inf-ruby)
    (package-install 'yaml-mode)
    (package-install 'geben)
    (package-install 'link-hint)
    (package-install 'whole-line-or-region)
    (package-install 'psysh)
    (package-install 'restclient)))

Инициализация

Следующие блоки кода выводят в файл early-init.el

Компиляция

JIT компиляция elisp кода в машинный

(when (eq window-system 'pgtk)
  (pgtk-use-im-context t))

(when (fboundp 'native-compile-async)
    (setq comp-async-compilation t
      package-native-compile t

      native-comp-speed 2
      native-comp-async-jobs-number 1
      comp-num-cpus 2
      ;; JIT Compilation
      native-comp-deferred-compilation t
      ;; AOT Compilation
      package-native-compile t
      comp-async-report-warnings-errors nil))
  • Ручная компиляция
    ;; (native-compile-async "~/.emacs.d/elpa/" 'recursively)
    (native-compile-async "~/.guix-profile/share/emacs/site-lisp" 'recursively)
    
    ;; block until native compilation has finished
      (while (or comp-files-queue
                 (> (comp-async-runnings) 0))
    

Отключаем ненужные загрузки

;; Disable guix autoloading and x resources loading
(setq site-run-file nil)

Редакируем GUI

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

;; Do not resize the frame at this early stage.
(setq frame-inhibit-implied-resize t)
(setq use-dialog-box nil)

;;; Disable some gui
(scroll-bar-mode -1)
(tool-bar-mode -1)
(menu-bar-mode -1)
;;(tooltip-mode -1)

;; Change mode-line-modes to show only major mode
(defvar mode-line-major-mode
  (let ((recursive-edit-help-echo "Recursive edit, type C-M-c to get out"))
    (list (propertize "%[" 'help-echo recursive-edit-help-echo)
          `(:propertize ("" mode-name)
                        help-echo "Major mode\n\
    mouse-1: Display major mode menu\n\
    mouse-2: Show help for major mode\n\
    mouse-3: Toggle minor modes"
                        mouse-face mode-line-highlight
                        local-map ,mode-line-major-mode-keymap)
          '("" mode-line-process)
          (propertize "%n" 'help-echo "mouse-2: Remove narrowing from buffer"
                      'mouse-face 'mode-line-highlight
                      'local-map (make-mode-line-mouse-map
                                  'mouse-2 #'mode-line-widen))
          (propertize "%]" 'help-echo recursive-edit-help-echo)
          " "))
  "Mode line construct for displaying major and minor modes.")

(put 'mode-line-major-mode 'risky-local-variable t)

;; Change mode-line-format
(setq-default mode-line-format
              '("%e"
                mode-line-front-space
                mode-line-mule-info

                mode-line-client
                mode-line-modified
                mode-line-remote
                mode-line-frame-identification
                ;; long-path
                mode-line-buffer-identification
                mode-line-misc-info
                "   "
                mode-line-major-mode
                " "
                vc-mode
                "  "

                mode-line-position
                mode-line-end-spaces
                ))

;;; Changing emacs default setting through customize
(custom-set-variables
 '(fill-column 72)
 '(git-gutter:added-sign " ")
 '(git-gutter:deleted-sign " ")
 '(git-gutter:modified-sign " ")
 '(git-gutter:unchanged-sign " ")
 '(lsp-headerline-breadcrumb-enable nil)
 '(scroll-bar-mode 'nil)
 '(scroll-bar-adjust-thumb-portion nil)
 '(tool-bar-position 'bottom)
 '(tool-bar-style 'both-horiz))

(custom-set-faces
 '(default ((t (:height 140 :family "Jetbrains Mono" :embolden t))))
 '(region ((t (:background "gray85"))))
 '(mode-line ((t (:background "grey70" :foreground "grey10"))))
 '(mode-line-inactive ((t (:inherit mode-line :background "grey90" :foreground "grey20" :box (:line-width (-2 . -2) :color "grey85") :weight light))))
 '(mode-line-buffer-id ((t (:weight bold))))
 '(mode-line-emphasis ((t (:weight bold))))
 '(lsp-modeline-code-actions-face ((t :inherit mode-line :height 100)))
 '(tool-bar ((t (:background "grey80" :foreground "grey10"))))
 '(fill-column-indicator ((t (:foreground "grey95"))))
 '(hl-line ((t (:background "gray95"))))
 '(fringe ((t (:background "grey87"))))
 '(header-line ((t (:inherit mode-line :background "grey90"))))
 '(vertical-border ((t (:foreground "grey90"))))
 '(window-divider ((t (:foreground "gray90")))))

(set-fringe-style (cons 7 7))

(defun switch-gui ()
  "Disable/enable menu-bar and tool-bar."
  (interactive)
  (if menu-bar-mode
      (progn
        (menu-bar-mode -1)
        (tool-bar-mode -1)
        (scroll-bar-mode -1))
    (progn
      (menu-bar-mode 1)
      (tool-bar-mode 1)
      (scroll-bar-mode 1))))

(defun switch-scroll-bar ()
  "Disable/enable scroll-bar."
  (interactive)
  (if scroll-bar-mode
      (scroll-bar-mode -1)
    (scroll-bar-mode)))

(define-key global-map (kbd "<f5>") 'switch-gui)
(define-key global-map (kbd "<f8>") 'switch-scroll-bar)

(provide 'early-init)
    ;;; early-init.el ends here

Инициализируем остальной конфиг

Следующие блоки кода выводят в файл init.el.

;; -*- lexical-binding: t -*-

;; Show/Hide errors
;; (setq debug-on-error nil)
;; (setq debug-on-quit nil)

(require 'package)

(defun package-loaded? (string)
  (if (or (cl-member string package-activated-list :test #'string=)
          (ignore-errors (load (concat string "-autoloads"))))
      t
      (progn
        (message (concat "Package " string " is not loaded"))
        nil)))

;; Timer
(add-hook 'emacs-startup-hook
          (lambda ()
            (message
             "Emacs ready in %s with %d garbage collections."
             (format "%.2f seconds"
                     (float-time
                      (time-subtract after-init-time before-init-time)))
             gcs-done)))

;; Dont ask when following symlinks
(setq vc-follow-symlinks t)

;; Load your custom settings
(setq custom-file "~/.emacs.d/custom-settings.el")
(load custom-file t)

Meta

Обо мне

;; Information about me
(setq user-full-name "Mikhail Kirillov"
      user-mail-address "w96k.ru@gmail.com")

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

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

(setq config-dotfiles-path "~/projects/dotfiles/emacs/.emacs.d/"
      config-path "~/.emacs.d/"
      config-name ".emacs-config.org")

(defun config-visit ()
  (interactive)
  (find-file (concat config-path config-name)))

(defun config-tangle ()
  (interactive)
  (org-babel-tangle-file (concat config-dotfiles-path config-name))
  ;; Configuration stored in another directory,
  ;; so I need to move files .emacs.d manually
  (rename-file (concat config-dotfiles-path "early-init.el") config-path t)
  (rename-file (concat config-dotfiles-path "init.el") config-path t))

(defun config-load ()
  (interactive)
  (org-babel-load-file (concat config-dotfiles-path config-name) t))

Внешний вид

Я использую по большей части стандартную тему имакса и стараюсь не менять стандартное поведение.

Назначение шрифтов

А также отступа между строк

;;; Set font
(setq-default line-spacing 0)

(set-language-environment "UTF-8")
(set-default-coding-systems 'utf-8-unix)

(add-to-list 'default-frame-alist
           '(font . "Jetbrains Mono-14"))

(setq mouse-wheel-progressive-speed nil)

Отображение номера строк и пробелов

Изначально они отключены, но можно вызвать по клавише F7.

(define-key global-map
  (kbd "<f7>") 'global-display-line-numbers-mode)

(define-key global-map
  (kbd "<f6>") 'whitespace-mode)

Скроллинг

Помечает на время часть старого буфера при скроллинге.

(when (package-loaded? "on-screen")
  (on-screen-global-mode t))

(customize-set-variable 'fast-but-imprecise-scrolling t)
(customize-set-variable 'scroll-conservatively 101)
(customize-set-variable 'scroll-margin 0)
(customize-set-variable 'scroll-preserve-screen-position t)

Объединение эхо зоны и модлайна

(when (package-loaded? "mini-modeline")
  (setq mini-modeline-r-format
        '("%e"
          mode-line-front-space

          mode-line-mule-info
          mode-line-client
          mode-line-modified
          mode-line-remote

          mode-line-frame-identification
          ;;long-path
          ;; mode-line-buffer-identification
          mode-line-misc-info

          mode-line-frame-identification
          mode-line-major-mode

          mode-line-frame-identification
          vc-mode
          mode-line-frame-identification

          mode-line-position))

  (setq mini-modeline-display-gui-line nil)
  (mini-modeline-mode))

Отображать полный путь в header line

(defun with-face (str &rest face-plist)
  (propertize str 'face face-plist))

(defun sl/make-header ()
  ""
  (let* ((sl/full-header (abbreviate-file-name buffer-file-name))
         (sl/header (file-name-directory sl/full-header))
         (sl/drop-str "[...]"))
    (if (> (length sl/full-header)
           (window-body-width))
        (if (> (length sl/header)
               (window-body-width))
            (progn
              (concat (with-face sl/drop-str
                                 :background "blue"
                                 :weight 'bold
                                 )
                      (with-face (substring sl/header
                                            (+ (- (length sl/header)
                                                  (window-body-width))
                                               (length sl/drop-str))
                                            (length sl/header))
                                 ;; :background "red"
                                 :weight 'bold
                                 )))
          (concat (with-face sl/header
                             ;; :background "red"
                             :foreground "#8fb28f"
                             :weight 'bold
                             )))
      (concat (with-face sl/header
                         ;; :background "green"
                         ;; :foreground "black"
                         :weight 'bold
                         :foreground "#8fb28f"
                         )
              (with-face (file-name-nondirectory buffer-file-name)
                         :weight 'bold
                         ;; :background "red"
                         )))))

(defun sl/display-header ()
  (setq header-line-format
        '("" ;; invocation-name
          (:eval (if (buffer-file-name)
                     (sl/make-header)
                   "%b")))))

(add-hook 'buffer-list-update-hook
          'sl/display-header)

Редактирование

Файловый менеджер

(setq dired-async-mode t)
;; Show files in KiB
(setq dired-listing-switches "-hlap"
      dired-kill-when-opening-new-dired-buffer t)

(customize-set-variable 'global-auto-revert-non-file-buffers t)
(global-auto-revert-mode 1)

Линтер

Использую встроенный Flymake и Flycheck

  • Flymake
    ;;(add-hook 'prog-mode-hook 'flymake-mode)
    
    (require 'psalm)
    
    (define-prefix-command 'flymake-map)
    (global-set-key (kbd "C-q") 'flymake-map)
    (define-key flymake-map (kbd "n") 'flymake-goto-next-error)
    (define-key flymake-map (kbd "p") 'flymake-goto-prev-error)
    (define-key flymake-map (kbd "l") 'flymake-show-diagnostics-buffer)
    (define-key flymake-map (kbd "e") 'flymake-show-diagnostic)
    
  • Flycheck
    ;; (require 'psalm)
    
    (when (package-loaded? "flycheck")
    
      (defun flycheck-phanclient-start-daemon ()
        "Start the phan daemon"
        (interactive)
        (let* ((default-directory (php-project-get-root-dir))
               (phan-executable (or flycheck-phanclient--phan-executable
                                    (if (file-exists-p "vendor/bin/phan")
                                        (concat default-directory "vendor/bin/phan")
                                      (executable-find "phan"))))
               (cmd (list phan-executable "--daemonize-tcp-port" "4846" "--quick")))
          (apply #'start-process "PhanDaemon" "*phan daemon*" cmd)))
    
      (flycheck-define-checker php-phanclient
        "Phan"
        :command ("phan_client" "-l" source-original "-f" source)
        :error-patterns
        ((warning line-start (or "Parse" "Fatal" "syntax" "Phan") " error" (any ":" ",") " " (message) " in " (file-name) " on line " line line-end))
        :modes (php-mode php+-mode))
    
      (add-to-list 'flycheck-checkers 'php-phanclient)
    
      (flycheck-add-next-checker 'php '(warning . php-phanclient))
    
      (add-hook 'prog-mode-hook 'flycheck-mode))
    

Дерево проекта

Возможно в дальнейшем откажусь от этого пакета, так как по факту им пользуюсь нечасто. Он предоставляет дерево проектов, как в IDE.

(when (package-loaded? "treemacs")
  (progn
    (setq treemacs-width       50
          treemacs-show-cursor t
          treemacs-position    'right
          treemacs-indentation 1
          treemacs-tag-follow-mode t
          treemacs-fringe-indicator-mode nil)

    (define-key global-map
      (kbd "C-x C-d") 'treemacs)))

Дерево imenu

(when (package-loaded? "imenu-list")
  (setq imenu-list-focus-after-activation nil
        imenu-list-auto-resize nil
        imenu-list-mode-line-format '()
        imenu-list-size 0.4)
  (global-set-key (kbd "C-x C-d") #'imenu-list-smart-toggle))

Better Isearch

(when (package-loaded? "isearch-mb")
  (progn
    (isearch-mb-mode t)
    (global-set-key (kbd "C-s") 'isearch-forward-regexp)
    (global-set-key (kbd "C-r") 'isearch-backward-regexp)))

Система контроля версий

Модуль VC + Magit.

Operation VC Magit
Project status project-vc-dir (C-x p v) magit-status (C-x g)
Pull vc-update (F, in my case) magit-pull (F p)
New branch vc-retrieve-tag (C-u B s) magit-branch (b c)
Commit vc-next-action (C-x v v) magit-commit (c c)
Rebase shell-command (M-!) + git rebase master magit-rebase (r p)
Push vc-push (P or C-u P) magit-push (P p)
Stash mu-vc-git-stash (z) magit-stash (z)
Log vc-print-root-log (L) magit-log (l l)

https://www.manueluberti.eu//emacs/2021/11/27/vc/

  (setq vc-command-messages t)

  ;;(load "git-dwim-autoloads")

  (global-set-key "\C-xvB" 'git-branch-next-action)

  ;; Fix for transient to behave like a normal buffer
;; (with-eval-after-load 'transient
;;   (setq
;;    transient--buffer-name "*transient*"
;;    ;; transient-detect-key-conflicts t
;;    ;; transient-highlight-mismatched-keys t
;;    ;; transient--debug t
;;    transient-enable-popup-navigation t
;;    transient-mode-line-format mode-line-format
;;    transient-display-buffer-action '(display-buffer-below-selected))

;;   (let ((map transient-base-map))
;;     (define-key map (kbd "C-g") 'transient-quit-all)
;;     (define-key map (kbd "C-q") 'transient-quit-one)
;;     (define-key map (kbd "DEL") 'transient-quit-one))

;;   (define-key transient-map (kbd "C-h") nil)

;;   (let ((map transient-popup-navigation-map))
;;     (define-key map (kbd "<tab>") 'transient-forward-button)
;;     (define-key map (kbd "<backtab>") 'transient-backward-button ))

;;   (transient-suffix-put 'transient-common-commands
;; 			"C-g" :command 'transient-quit-all)
;;   (transient-suffix-put 'transient-common-commands
;; 			"C-q" :command 'transient-quit-one)

;;   (defun al/transient-fix-window ()
;;     "Return `transient--window' to a 'normal' state."
;;     (set-window-dedicated-p transient--window nil)
;;     (set-window-parameter transient--window 'no-other-window nil)
;;     (with-selected-window transient--window
;;       (setq
;;        window-size-fixed nil
;;        cursor-in-non-selected-windows t
;;        cursor-type (default-value 'cursor-type)
;;        mode-line-buffer-identification
;;        (list ""
;; 	     (symbol-name (oref transient--prefix command))
;; 	     " " (default-value 'mode-line-buffer-identification)))))

;;   (define-derived-mode al/transient-mode special-mode "al/transient"
;;     (setq buffer-read-only nil)
;;     (al/transient-fix-window))

;;   (defun al/transient-push-keymap (map)
;;     (with-demoted-errors "al/transient-push-keymap: %S"
;;       (internal-push-keymap (symbol-value map) 'al/transient-mode-map)))

;;   (defun al/transient-pop-keymap (map)
;;     (with-demoted-errors "al/transient-pop-keymap: %S"
;;       (internal-pop-keymap (symbol-value map) 'al/transient-mode-map)))

;;   (defun al/transient-fix-show (&rest _)
;;     (transient--debug 'al/transient-fix-show)
;;     (al/transient-fix-window)
;;     (select-window transient--window))

;;   (defun al/transient-fix-init (&rest _)
;;     (transient--debug 'al/transient-fix-init)
;;     (with-current-buffer transient--buffer-name
;;       (al/transient-mode)))

;;   (defun al/transient-fix-pre/post-command (fun &rest args)
;;     (transient--debug 'al/transient-fix-pre/post-command)
;;     ;; Do anything only for transient commands.
;;     (when (or (get this-command 'transient--prefix)
;; 	      (string-match-p "\\`transient"
;; 			      (symbol-name this-command))
;; 	      (and transient--transient-map
;; 		   (string= (buffer-name) transient--buffer-name)
;; 		   (lookup-key transient--transient-map
;; 			       (this-single-command-raw-keys))))
;;       (apply fun args)))

;;   (defun al/transient-fix-delete-window (fun &rest args)
;;     (unless (eq transient--exitp 'suspend)
;;       (apply fun args)))

;;   (advice-add 'transient--minibuffer-setup :override #'ignore)
;;   (advice-add 'transient--minibuffer-exit :override #'ignore)
;;   (advice-add 'transient--push-keymap :override #'al/transient-push-keymap)
;;   (advice-add 'transient--pop-keymap :override #'al/transient-pop-keymap)
;;   (advice-add 'transient--pre-command :around #'al/transient-fix-pre/post-command)
;;   (advice-add 'transient--post-command :around #'al/transient-fix-pre/post-command)
;;   (advice-add 'transient--show :after #'al/transient-fix-show)
;;   (advice-add 'transient--init-transient :after #'al/transient-fix-init)
;;   (advice-add 'transient--delete-window :around #'al/transient-fix-delete-window))

  ;; Use magit only when built-in VC fails
  (when (package-loaded? "magit")
    (progn
      (setq magit-refresh-status-buffer nil)
      (global-set-key (kbd "C-x g") 'magit-status)))

(package-loaded? "git-timemachine")
  • Пометки о модифицированных строках

    Если строка добавлена, удалена или отредактирована относительно текущего git проекта, то строка помечается зеленым, красным или желтым цветом соответственно.

    ;;; Show added & removed git lines
    (when (package-loaded? "git-gutter")
      (progn
        (global-set-key (kbd "<f9>") 'git-gutter-mode)
    
        (setq git-gutter-window-width 1)
    
        (with-eval-after-load "git-gutter"
          '(set-face-background 'git-gutter:added "#99cc99")
          '(set-face-background 'git-gutter:deleted "#f2777a")
          '(set-face-background 'git-gutter:modified "#ffcc66")
          '(set-face-background 'git-gutter:unchanged "#f6f5f4"))
    
        (custom-set-variables
         '(git-gutter:modified-sign " ")
         '(git-gutter:unchanged-sign " ")
         '(git-gutter:added-sign " ")
         '(git-gutter:deleted-sign " "))))
    

Прыжки

(when (package-loaded? "avy")
  (progn
    (define-key global-map (kbd "M-s M-s") 'avy-goto-char)
    (define-key global-map (kbd "M-s s") 'avy-goto-char)
    (define-key global-map (kbd "M-s g") 'avy-goto-line)
    (define-key global-map (kbd "M-s l") 'avy-goto-char-in-line)
    (define-key global-map (kbd "M-s M-l") 'avy-goto-char-in-line)

    ;; Rewrite default bind to avy
    (define-key global-map (kbd "M-g g") 'avy-goto-line)
    (define-key global-map (kbd "M-g M-g") 'avy-goto-line)))

(when (package-loaded? "link-hint")
  (progn
    (define-key global-map (kbd "M-s j") 'link-hint-open-link)))

;; Прыжок на последнее изменение
(when (package-loaded? "goto-chg")
  (progn
    (setq glc-default-span 2)
    (define-key global-map (kbd "C-z") 'goto-last-change)
    (define-key global-map (kbd "M-z") 'goto-last-change-reverse)))

;; Dumb Jump
(when (package-loaded? "dumb-jump")
  (progn
    (define-key global-map (kbd "C-.") 'dumb-jump-go)))

Проекты

Использую встроенный project.el I use built-in project.el

Ограничение ширины строки

(when (package-loaded? "visual-fill-column")
  (progn
    ;;; Column width limit highlighter
    (add-hook 'prog-mode-hook 'display-fill-column-indicator-mode)

    ;;; Set column width to 79 according to pep8 for python
    (add-hook 'python-mode-hook
              (lambda () (set-fill-column 79)))))

Подсвечивание парных скобок

;;; Show pair for a parenthesis
(show-paren-mode)

Ввод парных скобок и кавычек (electric)

;;; Input of pair delimiters
(electric-pair-mode)
(add-hook 'prog-mode-hook 'electric-pair-mode)
;; (add-hook 'prog-mode-hook 'rainbow-identifiers-mode)

Kill-ring

(when (package-loaded? "browse-kill-ring")  
  (define-key global-map (kbd "C-M-y") 'browse-kill-ring))

Tags

Для прыжков и поиска функций/классов и т.д.

(setq path-to-ctags "~/.guix-profile/bin/ctags")

(defun tags-create (dir-name)
  "Create tags file."
  (interactive "DDirectory: ")
  (shell-command
   (format "%s -f TAGS -e -R %s" path-to-ctags
           (directory-file-name dir-name))))

(defun tags-create-python (dir-name)
  "Create tags with python interpreter"
  (interactive "DDirectory: ")
  (shell-command
   (format "%s -f TAGS -e -R --fields=+l --languages=python --python-kinds=-iv $(python -c \"import os, sys; print(' '.join('{}'.format(d) for d in sys.path if os.path.isdir(d)))\") %s" path-to-ctags
           (directory-file-name dir-name))))  

Дополнение

Дебаггер

(when (package-loaded? "realgud")
  (load "~/.emacs.d/site-lisp/realgud-xdebug/realgud-xdebug.el"))
  • Автодополнение кода и документация

    По большей части я использую дефолтный Completion Buffer и Corfu

    (when (package-loaded? "corfu")
      (progn
        (setq corfu-preview-current 'nil
              corfu-popupinfo-delay t)
        (corfu-mode 1)
        (corfu-popupinfo-mode 1)
        (defun show-default-completion-buffer ()
          (interactive)
          (corfu-quit)
          (corfu-mode -1)
          (completion-at-point)
          (corfu-mode 1)
          (corfu-popupinfo-mode 1))
        (define-key corfu-map (kbd "M-TAB") 'show-default-completion-buffer)
        (define-key corfu-map (kbd "TAB") 'show-default-completion-buffer)
        (define-key corfu-map (kbd "C-M-i") 'show-default-completion-buffer)      
        (corfu-mode -1)
        (add-hook 'prog-mode-hook 'corfu-mode)
    
        (defun corfu-send-shell (&rest _)
          "Send completion candidate when inside comint/eshell."
          (cond
           ((and (derived-mode-p 'eshell-mode) (fboundp 'eshell-send-input))
            (eshell-send-input))
           ((and (derived-mode-p 'comint-mode)  (fboundp 'comint-send-input))
            (comint-send-input))))
    
        (advice-add #'corfu-insert :after #'corfu-send-shell)
    
        (add-hook 'eshell-mode-hook 'corfu-mode)))
    
    
  • Модификация дефолта
    (setq completion-styles '(basic partial-completion substring flex emacs22)
          completion-ignore-case t
          read-buffer-completion-ignore-case t
          read-file-name-completion-ignore-case t)
    
  • Агрессивный дефолтный комплит
    (setq aggressive-completion-delay 0.5)
    (aggressive-completion-mode t)
    
  • HELM (не используется)
    (load "helm-autoloads")
    
    (setq helm-completion-style 'emacs
          helm-no-header t)
    
    (global-set-key (kbd "M-x")     #'helm-M-x)
    (global-set-key (kbd "C-x r b") #'helm-filtered-bookmarks)
    (global-set-key (kbd "C-x C-f") #'helm-find-files)
    (global-set-key (kbd "C-x C-b") #'helm-buffers-list)
    (global-set-key (kbd "C-s")     #'helm-occur)
    (global-set-key (kbd "M-y")     #'helm-show-kill-ring)
    (global-set-key (kbd "C-x b")   #'helm-mini)
    
    (define-key helm-map (kbd "<tab>") 'helm-execute-persistent-action)
    (define-key helm-map (kbd "C-i")   'helm-execute-persistent-action)
    (define-key helm-map (kbd "C-z")   'helm-select-action)
    (define-key minibuffer-local-map (kbd "C-c C-l") 'helm-minibuffer-history)
    
    (setq helm-display-header-line          nil
          helm-split-window-in-side-p       t
          helm-echo-input-in-header-line    t
          helm-display-source-at-screen-top nil
          helm-autoresize-max-height        50
          helm-autoresize-min-height        5)
    
    (helm-adaptive-mode)
    (helm-autoresize-mode 1)
    
    (add-hook 'eshell-mode-hook
              (lambda ()
                (eshell-cmpl-initialize)
                (define-key eshell-mode-map (kbd "C-c C-l")  'helm-eshell-history)
                (define-key eshell-mode-map [remap eshell-pcomplete] 'helm-esh-pcomplete)
                (define-key eshell-mode-map (kbd "M-p") 'helm-eshell-history)))
    
    (add-hook 'eshell-mode-hook
              (lambda ()
                (define-key eshell-mode-map
                  (kbd "M-p")
                  'helm-eshell-history)))
    
    (defun pcomplete/sudo ()
      (let ((prec (pcomplete-arg 'last -1)))
        (cond ((string= "sudo" prec)
               (while (pcomplete-here*
                       (funcall pcomplete-command-completion-function)
                       (pcomplete-arg 'last) t))))))
    
    (helm-mode 1)
    
    

Полнотекстовый поиск

Для выхода из поиска – C-c C-q

(load "deft-autoloads")

(define-key global-map
  (kbd "C-c n s") 'deft)

(setq deft-recursive t
      deft-use-filter-string-for-filename t
      deft-default-extension "org md"
      deft-directory "~/projects/at-w96k/content/digarden")

Визуализирование откатов

При помощи пакета undo-tree

(when (package-loaded? "undo-tree")
  (progn
    (add-hook 'prog-mode-hook #'undo-tree-mode)
    (add-hook 'org-mode-hook #'undo-tree-mode)
    (setq undo-tree-auto-save-history nil)))

Сниппеты

(when (package-loaded? "yasnippet")
  (progn
    (add-hook 'prog-mode-hook #'yas-minor-mode)))

Клиент LSP

(with-eval-after-load 'eglot
  (add-to-list 'eglot-server-programs '((php-mode phps-mode) . ("~/projects/phpactor/bin/phpactor" "language-server" "-vvv")))
  (add-to-list 'eglot-server-programs '((php-mode phps-mode) . ("intelephense" "--stdio")))

  ;; No event buffers, disable providers cause a lot of hover traffic. Shutdown unused servers.
  (setq eglot-events-buffer-size 0
        eglot-ignored-server-capabilities '(:hoverProvider
                                            :documentHighlightProvider)
        eglot-autoshutdown t))

;; Show all of the available eldoc information when we want it. This way Flymake errors
;; don't just get clobbered by docstrings.        
(add-hook 'eglot-managed-mode-hook
          (lambda ()
            "Make sure Eldoc will show us all of the feedback at point."
            (setq-local eldoc-documentation-strategy
                        #'eldoc-documentation-compose)))

Линтеры

;; (add-hook 'php-mode-hook 'flymake-php-load)
 ;; (add-hook 'php-mode-hook 'flymake-phpstan-turn-on)

  ;; (require 'flycheck-phpstan)


;;(add-to-list 'auto-mode-alist '("\\.\\(php\\|phtml\\)\\'" . phps-mode))

;; (phps-mode-flycheck-setup)

;; (setq phps-mode-async-process t)
;; (setq phps-mode-async-process-using-async-el t)

Выделение

(when (package-loaded? "expand-region")
  (global-set-key (kbd "C-=") 'er/expand-region))

Сессия

(desktop-save-mode 0)

Скроллинг

(setq scroll-margin 0)

Поиск

  • Isearch
    (with-eval-after-load 'isearch
      (define-key isearch-mode-map "\C-h" 'isearch-delete-char)
      (define-key isearch-mode-map "\C-ch" 'isearch-help-for-help))
    
  • Подсчёт кандидатов
    (global-anzu-mode t)
    
  • Swiper (не используется)
    (load "swiper-autoloads")
    (global-set-key (kbd "C-s") 'swiper)
    
    (setq swiper-include-line-number-in-search t
          swiper-use-visual-line t
          swiper-stay-on-quit t)
    

Подсказка биндов

Пакет Which-key

(load "which-key-autoloads")
(which-key-setup-side-window-right)
(which-key-mode)

(setq which-key-side-window-max-width 0.5
      which-key-show-remaining-keys t
      which-key-max-display-columns 50
      which-key-max-description-length 35
      which-key-sort-order 'which-key-local-then-key-order
      which-key-idle-delay 0.25)

Права суперпользователя

Sudo-edit

(package-loaded? "sudo-edit")

Промежуточный код

Показывает собранное состояние будь то собранный куски на ассемблере или байт-код при помощи пакета RMSbolt.

(add-hook 'prog-mode-hook 'rmsbolt-mode)

Языки программирования

Common Lisp

  • REPL
    (load "sly-autoloads")
    
    (setq sly-lisp-implementations
          '((clisp ("clisp"))
            (cmucl ("cmucl" "-quiet"))
            (sbcl ("/opt/sbcl/bin/sbcl") :coding-system utf-8-unix)))
    
    

Erlang

(load "erlang-autoloads")

Ruby

(when (package-loaded? "inf-ruby")
  (add-hook 'ruby-mode-hook 'inf-ruby-minor-mode))

(when (package-loaded? "inf-ruby")
  (add-hook 'ruby-mode-hook 'robe-mode))

Scheme

(setq geiser-active-implementations '("guile"))

Python

  • Автодополнение и линт
    (when (package-loaded? "anaconda-mode")
      (progn
        (add-hook 'python-mode-hook 'anaconda-mode)
        (add-hook 'python-mode-hook 'anaconda-eldoc-mode)))
    
    ;; (when (load "flymake" t)
    ;;   (defun flymake-pylint-init ()
    ;;     (let* ((temp-file (flymake-init-create-temp-buffer-copy
    ;; 		       'flymake-create-temp-inplace))
    ;; 	   (local-file (file-relative-name
    ;; 			temp-file
    ;; 			(file-name-directory buffer-file-name))))
    ;;       (list "epylint" (list local-file))))
    
    ;;   (add-to-list 'flymake-allowed-file-name-masks
    ;; 	       '("\\.py\\'" flymake-pylint-init)))
    
    ;; (add-hook 'python-mode-hook 'flymake-mode)
    
  • Прыжки в функции стандартной библиотеки на си

SML

(add-hook 'sml-mode-hook 'sml-mode)

PHP

  • PHP-Mode

    Необходимо скачать и распаковать мануал PHP (в формате html) в директорию ~/.emacs.d/php-manual/.

    ;; (add-to-list 'load-path "~/.emacs.d/site-lisp/realgud-xdebug/")
    ;; (require 'realgud-xdebug)
    
    (defun init-php-mode ()
      (eglot-ensure))
    
    (with-eval-after-load 'php-mode
      (custom-set-variables '(lsp-phpactor-path "/home/w96k/projects/phpactor"))
      ;; (add-hook 'php-mode-hook #'init-php-mode)
      )
    
    (when (package-loaded? "transient")
      (progn
        (transient-define-prefix
          php ()
          "Php"
          [["Class"
            ("cc" "Copy" phpactor-copy-class)
            ("cn" "New" phpactor-create-new-class)
            ("cr" "Move" phpactor-move-class)
            ("ci" "Inflect" phpactor-inflect-class)
            ("n"  "Namespace" phpactor-fix-namespace)]
           ["Properties"
            ("a"  "Accessor" phpactor-generate-accessors)
            ("pc" "Constructor" phpactor-complete-constructor)
            ("pm" "Add missing props" phpactor-complete-properties)
            ("r" "Rename var locally" phpactor-rename-variable-local)
            ("R" "Rename var in file" phpactor-rename-variable-file)]
           ["Extract"
            ("ec" "constant" phpactor-extract-constant)
            ("ee" "expression" phpactor-extract-expression)
            ("em"  "method" phpactor-extract-method)]
           ["Methods"
            ("i" "Implement Contracts" phpactor-implement-contracts)
            ("m"  "Generate method" phpactor-generate-method)]
           ["Navigate"
            ("x" "List refs" phpactor-list-references)
            ("X" "Replace refs" phpactor-replace-references)
            ("."  "Goto def" phpactor-goto-definition)]
           ["Phpactor"
            ("s" "Status" phpactor-status)
            ("u" "Install" phpactor-install-or-update)]])))
    
    (when (package-loaded? "php-mode")
      (progn
        (add-hook 'php-mode-hook 'php-enable-symfony2-coding-style)
        (setq lsp-intelephense-php-version "8.1.6")
        (defvar phpactor-executable "~/.local/bin/phpactor")
        (custom-set-variables '(lsp-phpactor-path "~/.local/bin/phpactor"))
    
        (add-hook 'php-mode-hook 
                  '(lambda ()
                     (require 'yasnippet)
                     (require 'yasnippet-snippets)
    
                     (set-fill-column 120)
    
                     (make-local-variable 'eldoc-documentation-function)
                     (setq eldoc-documentation-function
                           'phpactor-hover)
                     (yas-minor-mode t)
                     (define-key php-mode-map (kbd "C-c h") 'php-quickhelp-at-point)))
    
        (setq php-manual-path
              "~/.emacs.d/php-manual/"
              php-quickhelp-dir "~/.emacs.d/php-manual/"
              php-quickhelp--dest "~/.emacs.d/php-manual/php_manual_en.json")
    
    
        ;; (add-hook 'php-mode-hook 
        ;; 		'(lambda ()
        ;; 		   ;; (auto-complete-mode t)
    
        ;; 		   ;; (require 'ac-php)
        ;; 		   (require 'php-quickhelp)
        ;; 		   (require 'company)
        ;; 		   (company-mode t)
        ;; 		   (require 'company-php)
        ;; 		   (require 'company-quickhelp)
    
        ;; 		   (require 'yasnippet)
        ;; 		   (require 'yasnippet-snippets)
    
        ;; 		   (set (make-local-variable 'company-backends)
        ;; 			'((company-ac-php-backend company-dabbrev-code)
        ;; 			  php-quickhelp-company-php
        ;; 			  company-capf company-files))
    
        ;; 		   (company-quickhelp-mode t)
    
        ;; 		   (define-key php-mode-map (kbd "C-M-i") 'company-complete)
        ;; 		   (define-key company-mode-map (kbd "M-TAB") 'company-complete)
    
        ;; 		   ;; (setq ac-sources '(ac-source-php php-quickhelp-company-php))
        ;; 		   ;; (setq eldoc-documentation-function
        ;; 		   ;;       'php-quickhelp-eldoc-func)
    
        ;; 		   (yas-minor-mode t)
    
        ;; 		   ;; (define-key php-mode-map (kbd "C-M-i") 'auto-complete)
        ;; 		   ;; (define-key ac-mode-map (kbd "M-TAB") 'auto-complete)
    
        ;; 		   (define-key php-mode-map (kbd "C-c H")
        ;; 			       'php-local-manual-search)
    
        ;; 		   (define-key php-mode-map (kbd "C-c h") 'php-quickhelp-at-point)
        ;; 		   (define-key company-mode-map (kbd "C-c h") 'php-quickhelp-at-point)
    
        ;; 		   ;; (define-key php-mode-map (kbd "C-c t") 'ac-php-show-tip)
    
        ;; 		   ;; Jump to definition (optional)
        ;; 		   (define-key php-mode-map
        ;; 			       (kbd "M-.") 'ac-php-find-symbol-at-point)
    
        ;; 		   ;; Return back (optional)
        ;; 		   (define-key php-mode-map
        ;; 			       (kbd "M-,") 'ac-php-location-stack-back)))
        ))
    
  • Composer
  • Flymake PHP
  • REPL
  • LSP сервер

    PHPactor

    (with-eval-after-load 'php-mode
      (define-key php-mode-map (kbd "M-.") #'phpactor-goto-definition)
      (define-key php-mode-map (kbd "M-?") #'phpactor-find-references))
    
  • Transient меню
    (require 'transient)
    (define-transient-command php-menu ()
      "Php"
      [["Class"
        ("cc" "Copy" phpactor-copy-class)
        ("cn" "New" phpactor-create-new-class)
        ("cr" "Move" phpactor-move-class)
        ("ci" "Inflect" phpactor-inflect-class)
        ("n"  "Namespace" phpactor-fix-namespace)]
       ["Properties"
        ("a"  "Accessor" phpactor-generate-accessors)
        ("pc" "Constructor" phpactor-complete-constructor)
        ("pm" "Add missing props" phpactor-complete-properties)
        ("r" "Rename var locally" phpactor-rename-variable-local)
        ("R" "Rename var in file" phpactor-rename-variable-file)]
      ["Extract"
        ("ec" "constant" phpactor-extract-constant)
        ("ee" "expression" phpactor-extract-expression)
        ("em"  "method" phpactor-extract-method)]
      ["Methods"
        ("i" "Implement Contracts" phpactor-implement-contracts)
        ("m"  "Generate method" phpactor-generate-method)]
      ["Navigate"
        ("x" "List refs" phpactor-list-references)
        ("X" "Replace refs" phpactor-replace-references)
        ("."  "Goto def" phpactor-goto-definition)]
      ["Phpactor"
        ("s" "Status" phpactor-status)
        ("u" "Install" phpactor-install-or-update)]])
    

Языки декларирования

SQL

Need to install lsp-server called sqls https://emacs-lsp.github.io/lsp-mode/page/lsp-sqls/

;; Empty for now (was using emacsql)
(setq lsp-sqls-server "~/go/bin/sqls")

;; (setq lsp-sqls-workspace-config-path nil)

(setq lsp-sqls-connections
      '(((driver . "mysql") (dataSourceName . "dbuser:mangoworms@tcp(localhost:3306)/profile24"))))

The main way to interact with SQL is using org-mode

(when (package-loaded? "org-sql")
  (setq org-sql-files "~/projects/profile24/org"))

(add-hook 'sql-interactive-mode-hook
        (lambda ()
          (sql-connect "profile24")
          (toggle-truncate-lines t)))

(setq sql-connection-alist
    '((profile24
       (sql-product 'mysql)
       (sql-server "localhost")
       (sql-user "dbuser")
       (sql-password "mangoworms")
       (sql-database "profile24")
       (sql-port 3306))))

Веб шаблоны

  • Web-mode
    (when (package-loaded? "web-mode")
      (progn
    
        (add-to-list 'auto-mode-alist '("\\.html?\\'" . web-mode))
        (add-to-list 'auto-mode-alist '("\\.twig.html\\'" . web-mode))
    
        (setq web-mode-markup-indent-offset 2)
        (setq web-mode-enable-auto-pairing t)
        (setq web-mode-enable-css-colorization t)
        (setq web-mode-enable-block-face t)
        (setq web-mode-enable-current-element-highlight t)
        (setq web-mode-enable-current-column-highlight t)))
    

Org

  • Org-mode
    (org-babel-do-load-languages
     'org-babel-load-languages
     '((R . t)
       (ditaa . t)
       (dot . t)
       ;; (php . t)
       (emacs-lisp . t)
       (gnuplot . t)
       (haskell . nil)
       (latex . t)
       ;;(ledger . t)
       (ocaml . nil)
       (octave . t)
       (python . t)
       (ruby . t)
       (screen . nil)
       (shell . t)
       (sql . t)
       (js . t)))
    
    (defun org-babel-edit-prep:sql (babel-info)
      (setq-local buffer-file-name (->> babel-info caddr (alist-get :tangle)))
      (setq-local lsp-buffer-uri (->> babel-info caddr (alist-get :tangle) lsp--path-to-uri))
      (setq-local lsp-headerline-breadcrumb-enable nil)
      (lsp))
    
    (global-set-key (kbd "C-c l") 'org-store-link)
    (global-set-key (kbd "C-c a") 'org-agenda)
    (global-set-key (kbd "C-c c") 'org-capture)
    ;; (global-set-key (kbd "M-f") 'org-metaright)
    ;; (global-set-key (kbd "M-b") 'org-metaleft)
    ;; (global-set-key (kbd "M-p") 'org-metaup)
    ;; (global-set-key (kbd "M-n") 'org-metadown)
    
    (setq org-default-notes-file "~/Documents/todo.org"
          system-time-locale "C"
          org-use-speed-commands t
          org-adapt-indentation nil
          org-return-follows-link t
          org-display-remote-inline-images 'download
          org-image-actual-width (list 400)
          org-hide-emphasis-markers t
          org-outline-path-complete-in-steps nil
          org-src-tab-acts-natively t
          org-id-track-globally t
          org-confirm-babel-evaluate nil)
    
    (setq org-todo-keywords
          (quote ((sequence "TODO(t)"
                            "MIGRATE(m)" "|"
                            "IN PROGRESS(p)" 
                            "DONE(d)")
                  (sequence "WAITING(w@/!)"
                            "HOLD(h@/!)" "|"
                            "CANCELLED(c@/!)"
                            "PHONE"
                            "MEETING"))))
    (setq org-todo-keyword-faces
          (quote (("TODO" :foreground "red" :weight bold)
                  ("NEXT" :foreground "blue" :weight bold)
                  ("DONE" :foreground "forest green" :weight bold)
                  ("WAITING" :foreground "orange" :weight bold)
                  ("HOLD" :foreground "magenta" :weight bold)
                  ("CANCELLED" :foreground "forest green" :weight bold)
                  ("MEETING" :foreground "forest cyan" :weight bold)
                  ("PHONE" :foreground "blue" :weight bold))))
    
  • Org-ref (не используется)
    (load "org-ref-autoloads")
    
    (setq reftex-default-bibliography '("~/Documents/bibliography/references.bib"))
    
    ;; see org-ref for use of these variables
    (setq org-ref-bibliography-notes "~/Documents/bibliography/notes.org"
          org-ref-default-bibliography '("~/Documents/Bibliography/references.bib")
          org-ref-pdf-directory "~/Documents/bibliography/bibtex-pdfs/")
    
  • Org-roam
    (when (package-loaded? "org-roam")
      (progn
        (setq org-roam-directory (file-truename "~/projects/at-w96k/content/digarden/")
              org-roam-v2-ack t
              org-roam-completion-everywhere t
              org-roam-index-file (concat org-roam-directory "/20210409054712-жизнь.org")
              org-roam-dailies-directory (concat org-roam-directory "journals/")
              org-roam-capture-templates
              '(("d" "default" plain
                 "%?" :target
                 (file+head "pages/${slug}.org" "#+title: ${title}\n")
                 :unnarrowed t)))
    
        ;; (load "org-roam-ui-autoloads")
        ;; (org-roam-db-sync)
    
        (org-roam-db-autosync-mode t)
    
        (defun org-roam-jump-to-index ()
          "Stub of recreating the function from V1"
          (interactive)
          (let
              ((org-roam-index org-roam-index-file))
            (find-file org-roam-index)))
    
        (define-key global-map
                    (kbd "C-c n l") 'org-roam-node-insert)
        (define-key global-map
                    (kbd "C-c n f") 'org-roam-node-find)
        (define-key global-map
                    (kbd "C-c n b") 'org-roam-buffer-toggle)
        (define-key global-map
                    (kbd "C-c n t t") 'org-roam-tag-add)
        (define-key global-map
                    (kbd "C-c n t r") 'org-roam-tag-remove)
        (define-key global-map
                    (kbd "C-c n i") 'org-roam-jump-to-index)
        (define-key global-map
                    (kbd "C-c n g") 'org-roam-graph)
        (define-key global-map
                    (kbd "C-c n d") 'org-roam-db-build-cache)
        (define-key global-map
                    (kbd "C-c n r") 'org-roam-node-random)
        (define-key global-map  
                    (kbd "C-c n j") 'org-roam-dailies-find-date)))
    
    (customize-set-variable 'org-link-descriptive t)
    
  • Агенда и Capture
    (add-to-list 'org-agenda-files
                 "~/Documents/todo.org")
    
    (setq org-directory "~/Documents"
          org-default-notes-file (concat org-directory "/todo.org"))
    

YAML

(package-loaded? "yaml-mode")

Коммуникации

Telega

(when (package-loaded? "telega")
  (setq telega-filter-custom-show-folders t
        telega-chat-fill-column 40
        telega-root-fill-column 60
        telega-url-shorten-use-images t)

  (define-key global-map (kbd "C-c t") telega-prefix-map))

Mastodon

(when (package-loaded? "mastodon")
  (setq mastodon-active-user "w96k"
        mastodon-instance-url "https://fosstodon.org/"))

Наука

Разное

Минорные твики дефолтного имакса

  • Короткие ответы на вопросы
    (if (boundp 'use-short-answers)
      (setq use-short-answers t)
    (advice-add 'yes-or-no-p :override #'y-or-n-p))
    
  • Не сохранять дубликаты в killring
  • Удалять выделенный регион при вводе текста
    (delete-selection-mode 1)
    
  • Подсвечивать текущую строку
    (global-hl-line-mode 1)
    
  • Открывать список буферов в отдельном фрейме
    (add-to-list 'special-display-buffer-names "*Buffer List*")
    (setq Buffer-menu-files-only t)
    
  • Автодополнение в echo при M-x и других командах
    (icomplete-mode 1)
    
  • Проверять орфографию
    (flyspell-mode 1)
    
  • Не спрашивать о несуществующих буферах
    (setq-default confirm-nonexistent-file-or-buffer t)
    
  • Переключение буферов
    (global-set-key (kbd "M-o")  'mode-line-other-buffer)
    
  • Минорные твики
    (setq redisplay-dont-pause t)
    
    (setq select-enable-clipboard t
          select-enable-primary t)
    
    (setq completions-detailed nil)
    
    (setq kill-buffer-delete-auto-save-files t)
    (setq next-error-message-highlight t)
    
    (setq mode-line-compact 'long)
    
    (setq completions-group t)
    
    ;;(set-frame-parameter nil 'internal-border-width 0)
    
    ;; (set-window-buffer nil (current-buffer))
    
      (setq default-directory "~/"
          delete-seleciton-mode t
          inhibit-startup-message t
          initial-scratch-message nil
          custom-safe-themes t
          delete-old-versions t
          confirm-kill-processes nil
          enable-local-variables t)
    
  • Shell
    (setq ansi-color-for-comint-mode t)
    (setq shell-command-prompt-show-cwd t)
    
  • Переменная PATH в eshell
    (setq exec-path-from-shell-variables
          '("PATH" "MANPATH"))
    
    (when (and (memq window-system '(mac ns x))
               (not (eq system-type 'berkeley-unix)))
      (exec-path-from-shell-initialize))
    
  • Отображение номера колонки
    (column-number-mode)
    
  • nobreak символы
    (setq nobreak-char-display nil)
    
  • Меню

    По неизвестным причинам навигация в меню при помощи биндов Emacs не работает

    ;; GTK menu doesn't allow emacs-style navigation
    (define-key global-map (kbd "<f10>") 'tmm-menubar)
    
  • Сохранять временные файлы не в той же директории
    (defvar backup-dir "~/.emacs.d/backups/")
    
    (setq
     backup-by-copying t
     backup-directory-alist
     '(("." . "~/.emacs.d/backups/"))
     version-control nil)
    
  • Календарь

    Делаем начало недели в понедельник.

    (setq calendar-week-start-day 1)
    
  • Вернуться в предыдущий буфер
    (define-key global-map (kbd "C-q C-q") 'previous-buffer)
    (define-key global-map (kbd "C-S-q C-S-q") 'next-buffer)
    
  • Смена раскладки (EN / RU) и поддержка биндов на других языках

    Работает на C-\

    (set-input-method "russian-computer")
    (toggle-input-method)
    
  • Требовать создания последней пустой строки
    (setq require-final-newline t)
    
  • Стирать текст на C-h как в Bash

    И переназначаем старые бинды

    (define-key global-map (kbd "C-h") 'delete-backward-char)
    (define-key global-map (kbd "C-c h") 'help-command)
    
  • Поддержка CamelCase в навигации
    (global-subword-mode 1)
    
  • Kills
    (define-key global-map (kbd "C-k") 'kill-region)
    
    (when (package-loaded? "whole-line-or-region")
      (whole-line-or-region-global-mode))
    
    (define-key global-map (kbd "C-w") 'backward-kill-word)
    

Браузер

(add-hook 'eww-mode-hook
          (lambda ()
            (set-fill-column 80)
            (display-fill-column-indicator-mode)
            (visual-fill-column-mode)))

Tramp

(add-to-list 'tramp-remote-path 'tramp-own-remote-path)

Docker

(package-loaded? "docker")
(package-loaded? "docker-compose-mode")

Debian

Инструменты для работы с пакетным менеджером Debian'а apt'ом и смежными инструментами.

(load "debian-el-autoloads")
(load "dpkg-dev-el-autoloads")

Guix

(package-loaded? "geiser-guile")

(package-loaded? "guix")

(setq geiser-guile-binary "guile")

(with-eval-after-load 'geiser-guile
  (progn
    (add-to-list 'geiser-guile-load-path "~/projects/guix/")))

(let ((guix-copyright "~/projects/guix/etc/copyright.el"))
  (if (file-exists-p guix-copyright)
      (load-file "~/projects/guix/etc/copyright.el")))

(setq copyright-names-regexp
      (format "%s <%s>" user-full-name user-mail-address))

Nix

(package-loaded? "nix")

Vim

(add-to-list 'auto-mode-alist '("\\.vim\\(rc\\)?\\'" . vimrc-mode))

Direnv

(when (package-loaded? "direnv")
    (direnv-mode))

Баг-трекеры

  • Debbugs

PDF

Увеличение/уменьшение шрифта

(defun zoom-in ()
  (interactive)
  (let ((x (+ (face-attribute 'default :height)
              10)))
    (set-face-attribute 'default nil :height x)
    (set-face-attribute 'mode-line nil :height x)
    (set-face-attribute 'mode-line-inactive nil :height x)
    (set-face-attribute 'mode-line-position-face nil :height x)))

(defun zoom-out ()
  (interactive)
  (let ((x (- (face-attribute 'default :height)
              10)))
    (set-face-attribute 'default nil :height x)
    (set-face-attribute 'mode-line nil :height x)
    (set-face-attribute 'mode-line-inactive nil :height x)
    (set-face-attribute 'mode-line-position-face nil :height x)))

(define-key global-map (kbd "C-=") 'zoom-in)
(define-key global-map (kbd "C-+") 'zoom-out)

Автокомплит у yes-or-no

Полный экран

Открывать Emacs на полный экран

(add-to-list 'default-frame-alist '(fullscreen . maximized))

Фуллскрин

Отображать ровно столько строчек, сколько вмещает экран.

Не работает с native-comp.

(toggle-frame-fullscreen)

(defun fullscreen ()
"Fullscreen."
(interactive)
(x-send-client-message nil 0 nil "_NET_WM_STATE" 32
                       ;; if first parameter is '1', can't toggle fullscreen status
                       '(1 "_NET_WM_STATE_FULLSCREEN" 0)))

Удаление буфера и файла

(defun delete-file-and-buffer ()
  "Kill the current buffer and deletes the file it is visiting."
  (interactive)
  (let ((filename (buffer-file-name)))
    (if filename
        (if (y-or-n-p (concat "Do you really want to delete file " filename " ?"))
            (progn
              (delete-file filename)
              (message "Deleted file %s." filename)
              (kill-buffer)))
      (message "Not a file visiting buffer!"))))

Длинные строки

;; Better support for files with long lines
(setq-default bidi-paragraph-direction 'left-to-right)
(setq-default bidi-inhibit-bpa t)
(global-so-long-mode 1)

Make shebang (#!) file executable when saved

(add-hook 'after-save-hook #'executable-make-buffer-file-executable-if-script-p)

Enable savehist-mode for command history

(savehist-mode 1)

w96k Ⓐ 2019-2022

2022-11-27 Sun 23:34