about summary refs log tree commit diff
path: root/modules/workstation/emacs/init.el
blob: 6ff273bdc43ee9b76d2bf5546e271756a127cb96 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
;; -*- lexical-binding: t -*-

;;; UI customization ;;;
(column-number-mode)
(electric-pair-mode)
(global-so-long-mode)
(menu-bar-mode -1)
(scroll-bar-mode -1)
(show-paren-mode)
(tool-bar-mode -1)

(setq frame-title-format "%b — Emacs")
(setq uniquify-buffer-name-style 'forward)
(setq-default truncate-lines t)

(load-theme 'monokai t)

;; <102 or >109 will make Ioveska look squished rather than tall,
;; narrow, and beautiful.
(set-face-attribute 'default nil :height 104)

;; Disabling bidirectional text will make Emacs less slow with very
;; long lines.
(setq bidi-paragraph-direction 'left-to-right)
(setq bidi-inhibit-bpa t)

;;; Utility functions ;;;

;; Set an environment variable value for a single buffer.
(defun setenv-local (variable &optional value substitute-env-vars)
  ;; If process-environment is the global one, make a copy of it.
  (when (eq process-environment (default-value 'process-environment))
    (make-local-variable 'process-environment)
    (setq process-environment (mapcar 'concat process-environment)))
  (setenv variable value substitute-env-vars))

(defun enable-ansi-color ()
  (setenv-local "TERM" "dumb-emacs-ansi"))

;;; Major modes ;;;
(add-to-list 'auto-mode-alist '("\\.adoc\\'" . adoc-mode))
(add-to-list 'auto-mode-alist '("\\.quirks\\'" . conf-unix-mode))
(add-to-list 'auto-mode-alist '("\\.tmac\\'" . nroff-mode))
(add-to-list 'auto-mode-alist '("/Cargo.lock\\'" . toml-mode))

;;; Backups ;;;
(let ((backup-dir
       (concat (file-name-as-directory user-emacs-directory) "backups")))
  (make-directory backup-dir t)
  (setq backup-directory-alist `(("." . ,backup-dir)))
  (setq message-auto-save-directory backup-dir))

;;; CC Mode ;;;
(with-eval-after-load 'cc-styles
  ;; Default to kernel style.
  (add-to-list 'c-default-style '(other . "linux")))

;;; Comint ;;;
;; This relies on an Emacs patch to apply to async-shell-command.
(setq comint-terminfo-terminal "dumb-emacs-ansi")

;;; Diff Mode ;;;
;; Unbind M-DEL from scroll-down-command, so it does the same thing as
;; in the rest of Emacs (backward-kill-word).
(with-eval-after-load 'diff-mode
  (define-key diff-mode-map (kbd "M-DEL") nil))

;;; Dired ;;;
(with-eval-after-load 'dired
  ;; Use human-readable sizes.
  (setq dired-listing-switches
	(combine-and-quote-strings `(,dired-listing-switches "-h")))

  ;; Don't open new windows when clicking directory entries.
  (define-key dired-mode-map [mouse-2] 'dired-find-file))

;;; Direnv ;;;
(direnv-mode)
(global-set-key (kbd "C-c d") 'direnv-update-environment)

;;; Ediff ;;;
;; Don't make a new frame for Ediff controls.
(setq ediff-window-setup-function 'ediff-setup-windows-plain)

;;; EditorConfig ;;;
(editorconfig-mode)

;;; Eglot ;;;
(with-eval-after-load 'eglot
  ;; Override default LSPs for languages.
  (add-to-list 'eglot-server-programs '(rust-mode "rust-analyzer"))
  (add-to-list 'eglot-server-programs '((c++-mode c-mode) "clangd"))

  ;; Underline all occurrences of the symbol at point in the current
  ;; buffer (default is to bold instead).
  (set-face-attribute 'eglot-highlight-symbol-face nil :inherit 'underline))

;;; Environment ;;;

;; Provide a way to turn paging back on for modes like terminal emulators.
(let ((pager (getenv "PAGER")))
  (defun enable-pager ()
    (setenv-local "PAGER" pager)))

;; Set PAGER to the empty string, which Git and journalctl will
;; interpret as an explicit opt-out of paging.
(setenv "PAGER" "")

;;; Eshell ;;;
(add-hook 'eshell-mode-hook #'enable-ansi-color)

;;; Ibuffer ;;;
;; Open ibuffer in other window, to match behaviour of the default
;; list-buffers.
(global-set-key (kbd "C-x C-b") (lambda () (interactive) (ibuffer t)))

;;; ispell ;;;
;; System locale is Esperanto, but I write in English much more, and
;; spelling in Esperanto is easy anyway. ;)
(setq ispell-dictionary "english")

;;; Magit ;;;
(setq magit-delete-by-moving-to-trash delete-by-moving-to-trash)
(setq magit-repository-directories
      `((,(expand-file-name "~/src") . 1)))
(global-set-key (kbd "C-x g") 'magit-status)
(global-set-key (kbd "C-x M-g") 'magit-dispatch)

(with-eval-after-load "magit"
  ;; Add a --no-gpg-sign option to Magit.  This is only useful with
  ;; commit.gpgsign=true.  It would be nice if commit.gpgsign=true just
  ;; meant that Magit showed the --gpg-sign option as enabled by
  ;; default, and disabling it would make Magit pass --no-gpg-sign to
  ;; git, but that's not currently the case, so we need a seperate
  ;; option for --no-gpg-sign.  See
  ;; <https://github.com/magit/magit/issues/3832>.
  (dolist (command '(magit-commit magit-merge magit-cherry-pick
		     magit-revert magit-am magit-rebase))
    (transient-append-suffix command ["-S"]
      '("=S" "Don't sign using gpg" "--no-gpg-sign"))))

;;; Man ;;;
;; Open man pages in the current window.  I don't really care what
;; happens when I do M-x man, but I don't want my whole frame to be
;; taken over with man buffers if I'm following a bunch of
;; cross-references.
(setq Man-notify-method 'pushy)

;;; MML ;;;
(setq mml-secure-openpgp-signers '("757356D779BBB888773E415E736CCDF9EF51BD97"))

;;; Nix support ;;;
(defun browse-url-nixpkgs (attr)
  "Open a browser to the homepage for the Nixpkgs attribute ATTR."
  (interactive "sAttribute: ")
  (let* ((full-attr-quoted (shell-quote-argument (concat attr ".meta.homepage")))
	 (command (concat "nix eval --raw -f '<nixpkgs>' " full-attr-quoted))
	 (homepage (shell-command-to-string command)))
    (browse-url homepage)))

;;; Rainbow Delimiters ;;;
(add-hook 'prog-mode-hook #'rainbow-delimiters-mode)

;;; Revert buffers ;;;
(global-set-key (kbd "C-c r") 'revert-buffer)

;;; Rust ;;;
;; Indent with spaces in Rust code.
(add-hook 'rust-mode-hook (lambda () (setq indent-tabs-mode nil)))

(with-eval-after-load 'rust-mode
  ;; Cargo keybindings
  (define-key rust-mode-map (kbd "C-c c b") 'rust-compile)
  (define-key rust-mode-map (kbd "C-c c c") 'rust-check)
  (define-key rust-mode-map (kbd "C-c c r") 'rust-run)
  (define-key rust-mode-map (kbd "C-c c t") 'rust-test))

;;; save-some-buffers ;;;
;; Allow reverting buffers directly from the "Save file ...?" message.
(add-to-list 'save-some-buffers-action-alist
  `(?r ,(lambda (buf) (with-current-buffer buf (revert-buffer t t)))
       ,(purecopy "revert this buffer")))

;;; sendmail ;;;
;; Use the system "sendmail" program to send mail.
(setq send-mail-function 'sendmail-send-it)

;;; Term ;;;
(add-hook 'term-mode-hook #'enable-pager)

;;; Transient ;;;
;; Make all Magit options available, even those that are disabled by
;; default because they're too obscure.
(setq transient-default-level 7)

;;; with-editor ;;;
(add-hook 'eshell-mode-hook 'with-editor-export-editor)
(add-hook 'shell-mode-hook 'with-editor-export-editor)
(add-hook 'term-exec-hook 'with-editor-export-editor)