Мой конфиг Emacs в формате org-mode

Опи­са­ние

Этот ко­нфиг ис­поль­зую на всех де­скто­пах и те­ле­фо­нах. Посте­пенно он по­пол­ня­ет­ся. Акт­уаль­ную вер­сию вс­ег­да мож­но най­ти в ра­бо­чем ре­по­зи­то­рии: https://github.com/johnlepikhin/emacs-public/blob/master/settings.org.

Из не­го ди­на­ми­чес­ки под­гру­жа­ют­ся фай­лы:

  • ~/.emacs.d/local/user-info.org : на­ст­рой­ки email, име­ни поль­зо­ва­те­ля и т.д.
  • ~/.emacs.d/local/org-agenda.org : лич­ные на­ст­рой­ки ша­бло­нов agenda
  • ~/.emacs.d/local/org-capture.org : лич­ные ша­бло­ны org-за­пи­сей
  • ~/.emacs.d/local/gnus-accounts.org : по­что­вые ак­ка­ун­ты
  • ~/.emacs.d/local/gnus-templates.org : по­что­вые ша­бло­ны

Как под­кл­ючать

В ~/.emacs на­до на­пи­сать:

(package-initialize)

(defun my-load-org-config (subpath)
  (let ((ini-file (expand-file-name subpath user-emacs-directory)))
	(condition-case errinfo (org-babel-load-file ini-file)
	  (error (message "Cannot load settings for file %s: %s" ini-file errinfo)))))

(my-load-org-config "public/settings.org")

Здесь public/settings.org бу­дет тран­сли­ро­ва­но в ~/.emacs.d/public/settings.org, со­глас­но зна­че­нию user-emacs-directory.

Базо­вая ко­нфиг­ура­ция

Все пе­ре­мен­ные здесь лек­си­чес­ки ло­каль­ны:

(setq-local lexical-binding t)

Соб­рать ин­фор­ма­цию по use-package. После за­груз­ки мож­но по­звать функ­ци use-package-report и по­смот­реть.

(setq use-package-compute-statistics t)

Вне­шний вид

Неко­то­рые оп­ти­ми­за­ции:

(setq auto-revert-interval 1            ; Refresh buffers fast
	  custom-file (make-temp-file "")   ; Discard customization's
	  default-input-method "TeX"        ; Use TeX when toggling input method
	  echo-keystrokes 0.1               ; Show keystrokes asap
	  inhibit-startup-message t         ; No splash screen please
	  initial-scratch-message nil       ; Clean scratch buffer
	  sentence-end-double-space nil)    ; No double space

Выклю­чить кноп­ку Insert (вклю­че­ние overwrite-mode):

(define-key global-map [(insert)] nil)

Выклю­чить ту­лбар:

(tool-bar-mode -1)

Шири­на за­пол­не­ния по умо­лча­нию боль­шая, се­йчас ши­ро­кие мо­ни­то­ры:

(setq-default fill-column 140)

Не на­до мне со­об­щать, что у стро­ки есть про­дол­же­ние. Прос­то по­ка­зы­вай про­дол­же­ние на сле­ду­ющей стро­ке:

(setq-default truncate-lines t)

Наст­рой­ка те­мы

(load-theme 'leuven t)

Менеджер па­ке­тов и за­ви­си­мос­тей

Подклю­чить ме­неджер па­ке­тов:

(require 'package)

(add-to-list 'package-archives '("melpa" . "http://melpa.milkbox.net/packages/"))
(add-to-list 'package-archives '("melpa-stable" . "https://stable.melpa.org/packages/"))
(add-to-list 'package-archives '("org" . "http://orgmode.org/elpa/") t)

При­бить гвоз­дя­ми ука­зан­ные па­ке­ты к ре­по­зи­то­ри­ям:

(add-to-list 'package-pinned-packages '(cider . "melpa-stable") t)

Поста­вить пе­речис­лен­ные па­ке­ты, ес­ли не уста­нов­ле­ны:

(dolist (package
		 '(use-package))
  (if (not (package-installed-p package))
	(progn
	  (message "Installing package %s" package)
	(package-refresh-contents)
	  (package-install package))))

Настра­ива­ем use-package:

(require 'use-package)
(setq use-package-always-ensure t)

Боль­ше де­ба­га для use-package:

(setq use-package-verbose t)

Я хо­чу ука­зы­вать за­ви­си­мос­ти к сис­тем­ным па­ке­там при ис­поль­зо­ва­нии use-package:

(use-package use-package-ensure-system-package
  :ensure t)

Про­дуб­ли­ру­ем опре­де­ле­ние, под­гру­жа­ющее функ­ции из ~/.emacs:

(defun my-load-org-config (subpath)
  (let ((ini-file (expand-file-name subpath user-emacs-directory)))
	(condition-case errinfo
		(progn
		  (org-babel-load-file ini-file)
		  (message "Loaded config: %s" subpath))
	  (error (message "Cannot load settings for file %s: %s" ini-file errinfo)))))

Инфор­ма­ция о поль­зо­ва­те­ле

(my-load-org-config "local/user-info.org")

Наст­рой­ки вво­да

У ме­ня пе­ре­клю­че­ние рас­клад­ки внут­ри Emacs ин­тег­ри­ро­ва­но с XMonad. Нижес­ле­ду­ющие функ­ции слу­жат этой ин­тег­ра­ции.

(defun my-update-cursor ()
  (set-cursor-color
   (if (string= current-input-method "russian-computer") "red" "black")))

(add-hook 'buffer-list-update-hook 'my-update-cursor)

(defun my-update-isearch-input-method ()
  (if isearch-mode
	  (progn
		(setq isearch-input-method-function input-method-function
			  isearch-input-method-local-p t)
		(isearch-update))))

(defun my-update-input-method (is-ru)
  (if is-ru
	  (set-input-method 'russian-computer)
	(inactivate-input-method))
  (my-update-isearch-input-method)
  (my-update-cursor))

(defun my-select-input-eng ()
  (interactive)
  (my-update-input-method nil))

(defun my-select-input-rus ()
  (interactive)
  (my-update-input-method t))

(global-set-key (kbd "<M-f11>") 'my-select-input-eng)
(global-set-key (kbd "<M-f12>") 'my-select-input-rus)
(define-key isearch-mode-map (kbd "<M-f11>") 'my-select-input-eng)
(define-key isearch-mode-map (kbd "<M-f12>") 'my-select-input-rus)

Стран­ный спо­соб до­ба­вить ещё од­ну рас­клад­ку:

(set-input-method 'russian-computer)
(toggle-input-method)

Соз­да­ем лич­ную кар­ту ак­кор­дов:

(defvar my-bindings-map (make-keymap)
  "Мои личные горячие клавиги.")

(define-minor-mode my-bindings-mode
  "Режим для подключения моих кнопок."
  t nil my-bindings-map)

(define-key my-bindings-map (kbd "C-c <up>")    'windmove-up)
(define-key my-bindings-map (kbd "C-c <down>")  'windmove-down)
(define-key my-bindings-map (kbd "C-c <left>")  'windmove-left)
(define-key my-bindings-map (kbd "C-c <right>") 'windmove-right)
(define-key my-bindings-map [C-return] (lambda () (interactive) (point-to-register 'r)))
(define-key my-bindings-map [M-return] (lambda () (interactive) (jump-to-register 'r)))

Зачем мне за­мо­ра­жи­вать фрей­мы?

(global-unset-key (kbd "C-z"))

Expand region

По на­жа­тию C-= вы­бран­ный ре­ги­он се­ман­ти­чес­ки/син­так­си­чес­ки рас­ши­ря­ет­ся:

(use-package
  expand-region
  :bind (:map my-bindings-map
			  ("C-=" . er/expand-region)))

Вне­шний вид

Золо­тое се­че­ние для рас­по­ло­же­ния окон:

(use-package
  golden-ratio
  :config (golden-ratio-mode 1))

Подсказ­ка по вво­ди­мым кноп­кам. Ото­бра­жать с за­держ­кой в 1 се­кун­ду:

(use-package
  which-key
  :config
  (setq which-key-idle-delay 1)
  (which-key-mode))

Пока­зы­вать в ми­ни­бу­фе­ре по­зи­цию в стро­ке:

(column-number-mode)

Пове­де­ние

Бэка­пы не нуж­ны:

(setq backup-inhibited t)

Шаг от­сту­па по умо­лча­нию 4 сим­во­ла:

(setq-default tab-width 4)
(setq-default standart-indent 4)

Я люблю огра­ни­чи­вать об­ласть ви­ди­мос­ти до вы­бран­но­го ре­ги­она, не на­до ме­ня вор­нить:

(put 'narrow-to-region 'disabled nil)

Исто­рия от­кры­тых фай­лов:

(use-package
  recentf
  :config
  (setq recentf-max-saved-items 100)
  (setq recentf-max-menu-items 25)
  (recentf-mode 1))

Пом­нить по­зи­ции в фа­йлах:

(save-place-mode 1)

Поми­нить ис­то­рию ко­ма­нд ми­ни­бу­фе­ра:

(savehist-mode 1)

Бра­узер по умо­лча­нию — chromium:

(setq browse-url-browser-function 'browse-url-chromium)

Наст­рой­ки isearch

У ме­ня пло­хо при­жи­лись стан­дарт­ные кноп­ки, слег­ка пе­ре­на­зна­чил:

(define-key isearch-mode-map (kbd "<up>") 'isearch-ring-retreat )
(define-key isearch-mode-map (kbd "<down>") 'isearch-ring-advance )

(define-key isearch-mode-map (kbd "<left>") 'isearch-repeat-backward)
(define-key isearch-mode-map (kbd "<right>") 'isearch-repeat-forward)

(define-key minibuffer-local-isearch-map (kbd "<left>") 'isearch-reverse-exit-minibuffer)
(define-key minibuffer-local-isearch-map (kbd "<right>") 'isearch-forward-exit-minibuffer)

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

C-; ком­мен­ти­ру­ет стро­ку или ре­ги­он:

(defun comment-dwim-line-or-region ()
  "[Un]comment line or region"
  (interactive)
  (if mark-active
	  (comment-dwim t)
	(progn
	  (comment-line 1)
	  (forward-line -1))))

(define-key my-bindings-map (kbd "C-;") 'comment-dwim-line-or-region)

Мно­гост­роч­ный ре­жим ком­мен­та­ри­ев по умо­лча­нию

(setq comment-style 'multi-line)

У нас не при­ня­то раз­де­лять пред­ло­же­ния двой­ным про­бе­лом:

(setq sentence-end-double-space nil)

Мы пре­зи­ра­ем та­бу­лял­цию в от­сту­пах:

(setq-default indent-tabs-mode nil)

Yasnippet: раз­во­ра­чи­ва­ние сниппе­тов

(use-package yasnippet
  :bind (:map my-bindings-map
			  ("C-<tab>" . yas-expand))
  :hook (cperl-mode . yas-minor-mode)
  :commands (yas-minor-mode)
  :after (yasnippet-classic-snippets)
  :config
  ;; подключить мой публичный репозиторий сниппетов
  (add-to-list 'yas-snippet-dirs (expand-file-name "~/.emacs.d/public/yasnippets"))
  ;; теперь надо всё перечитать
  (yas-reload-all))

(use-package yasnippet-classic-snippets)

Уда­ле­ние без по­пол­не­ния kill-ring

Не люблю, ког­да уда­ля­емые кус­ки тек­ста ав­то­ма­том ко­пи­ру­ют­ся в бу­фер об­ме­на. Кусоч­ки ко­да от­ку­да-то спёр.

(defun my-delete-word (arg)
  "Delete characters forward until encountering the end of a word.
With argument, do this that many times.
This command does not push text to `kill-ring'."
  (interactive "p")
  (delete-region
   (point)
   (progn
	 (forward-word arg)
	 (point))))

(defun my-backward-delete-word (arg)
  "Delete characters backward until encountering the beginning of a word.
With argument, do this that many times.
This command does not push text to `kill-ring'."
  (interactive "p")
  (my-delete-word (- arg)))

(define-key my-bindings-map (kbd "C-S-k") 'my-delete-line-backward)
(define-key my-bindings-map (kbd "M-d") 'my-delete-word)
(define-key my-bindings-map (kbd "<M-backspace>") 'my-backward-delete-word)
(define-key my-bindings-map (kbd "<C-backspace>") 'my-backward-delete-word)

Пока­зы­вать от­сту­пы

(use-package
  indent-guide
  :hook (prog-mode . indent-guide-mode)
  :config
  (setq indent-guide-char "|")
  (set-face-foreground 'indent-guide-face "darkgray"))

Умные ско­боч­ки

(use-package
  smartparens
  :config
  ;; Есть баг с electric-parens-mode с cperl, заплатка из https://github.com/syl20bnr/spacemacs/issues/480
  (with-eval-after-load 'cperl-mode
	(add-hook 'smartparens-enabled-hook  (lambda () (define-key cperl-mode-map "{" nil)))
	(add-hook 'smartparens-disabled-hook  (lambda () (define-key cperl-mode-map "{" 'cperl-electric-lbrace))))
  ;; Включаем глобально
  (smartparens-global-mode 1))

Подсве­чи­вать пар­ную скоб­ку, пе­ре­хо­ды по ним:

(show-paren-mode 1)
(setq show-paren-delay 0)

(defun match-paren (arg)
  "Go to the matching paren."
  (interactive "p")
  (cond ((looking-at "\\s\(") (forward-list 1) (backward-char 1))
		((looking-at "\\s\)") (forward-char 1) (backward-list 1))
		(t (self-insert-command (or arg 1)))))

(define-key my-bindings-map (kbd "C-`") 'match-paren)

Дере­во от­ме­ны

(use-package
  undo-tree
  :bind (:map my-bindings-map
			  ("C-x u" . undo-tree-visualize))
  :config (global-undo-tree-mode 1))

Автодо­пол­не­ние

(use-package
  company
  :hook (prog-mode . company-mode)
  :config
  (setq company-idle-delay 0
		company-echo-delay 0
		company-dabbrev-downcase nil
		company-minimum-prefix-length 2
		company-selection-wrap-around t
		company-transformers '(company-sort-by-occurrence
							   company-sort-by-backend-importance)))

Автодо­пол­не­ние с не­точ­ным со­впа­де­ни­ем

(use-package company-flx
  :after company
  :config
  (company-flx-mode +1))

Tramp

(use-package
  tramp
  :config (setq tramp-use-ssh-controlmaster-options nil))