Drop no-littering
[~bandali/configs] / init.el
CommitLineData
33b1a7ea 1;;; init.el --- bandali's emacs configuration -*- lexical-binding: t -*-
41d290a2 2
4c05c418 3;; Copyright (C) 2018-2020 Amin Bandali <bandali@gnu.org>
41d290a2
AB
4
5;; This program is free software: you can redistribute it and/or modify
6;; it under the terms of the GNU General Public License as published by
7;; the Free Software Foundation, either version 3 of the License, or
8;; (at your option) any later version.
9
10;; This program is distributed in the hope that it will be useful,
11;; but WITHOUT ANY WARRANTY; without even the implied warranty of
12;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13;; GNU General Public License for more details.
14
15;; You should have received a copy of the GNU General Public License
16;; along with this program. If not, see <https://www.gnu.org/licenses/>.
17
18;;; Commentary:
19
33b1a7ea 20;; GNU Emacs configuration of Amin Bandali, computer scientist,
92ad9675 21;; Free Software activist, and GNU maintainer & webmaster. Packages
f7910e3d 22;; are installed through using Borg for a fully reproducible setup.
b57457b2
AB
23
24;; Over the years, I've taken inspiration from configurations of many
25;; great people. Some that I can remember off the top of my head are:
26;;
27;; - https://github.com/dieggsy/dotfiles
28;; - https://github.com/dakra/dmacs
29;; - http://pages.sachachua.com/.emacs.d/Sacha.html
30;; - https://github.com/dakrone/eos
31;; - http://doc.rix.si/cce/cce.html
32;; - https://github.com/jwiegley/dot-emacs
33;; - https://github.com/wasamasa/dotemacs
34;; - https://github.com/hlissner/doom-emacs
41d290a2 35
49e9503b
AB
36;;; Code:
37
b57457b2
AB
38;;; Emacs initialization
39
dca50cf5 40(defvar b/before-user-init-time (current-time)
41d290a2 41 "Value of `current-time' when Emacs begins loading `user-init-file'.")
83364e5b
AB
42(defvar b/emacs-initialized nil
43 "Whether Emacs has been initialized.")
44
45(when (not (bound-and-true-p b/emacs-initialized))
46 (message "Loading Emacs...done (%.3fs)"
47 (float-time (time-subtract b/before-user-init-time
48 before-init-time))))
41d290a2 49
b57457b2
AB
50;; temporarily increase `gc-cons-threshhold' and `gc-cons-percentage'
51;; during startup to reduce garbage collection frequency. clearing
52;; `file-name-handler-alist' seems to help reduce startup time too.
dca50cf5
AB
53(defvar b/gc-cons-threshold gc-cons-threshold)
54(defvar b/gc-cons-percentage gc-cons-percentage)
55(defvar b/file-name-handler-alist file-name-handler-alist)
bc58e70a 56(setq gc-cons-threshold (* 30 1024 1024) ; 30 MiB
41d290a2
AB
57 gc-cons-percentage 0.6
58 file-name-handler-alist nil
59 ;; sidesteps a bug when profiling with esup
60 esup-child-profile-require-level 0)
61
b57457b2 62;; set them back to their defaults once we're done initializing
dca50cf5 63(defun b/post-init ()
83364e5b
AB
64 "My post-initialize function, run after loading `user-init-file'."
65 (setq b/emacs-initialized t
66 gc-cons-threshold b/gc-cons-threshold
67 gc-cons-percentage b/gc-cons-percentage
e8a3b8f7 68 file-name-handler-alist b/file-name-handler-alist)
c84be134 69 (when (featurep 'exwm-workspace)
8b1a2f32
AB
70 (with-eval-after-load 'exwm-workspace
71 (setq-default
72 mode-line-format
73 (append
74 mode-line-format
75 '((:eval
76 (format
77 "[%s]" (number-to-string
78 exwm-workspace-current-index)))))))))
dca50cf5 79(add-hook 'after-init-hook #'b/post-init)
41d290a2 80
b57457b2 81;; increase number of lines kept in *Messages* log
41d290a2
AB
82(setq message-log-max 20000)
83
b57457b2
AB
84;; optionally, uncomment to supress some byte-compiler warnings
85;; (see C-h v byte-compile-warnings RET for more info)
41d290a2
AB
86;; (setq byte-compile-warnings
87;; '(not free-vars unresolved noruntime lexical make-local))
88
b57457b2
AB
89\f
90;;; whoami
91
41d290a2 92(setq user-full-name "Amin Bandali"
33b1a7ea 93 user-mail-address "bandali@gnu.org")
41d290a2 94
b57457b2 95\f
33273849
AB
96;;; Package management
97
032726b6
AB
98(progn ; `borg'
99 (add-to-list 'load-path
100 (expand-file-name "lib/borg" user-emacs-directory))
101 (require 'borg)
102 (borg-initialize)
103 (setq borg-rewrite-urls-alist
104 '(("git@github.com:" . "https://github.com/")
105 ("git@gitlab.com:" . "https://gitlab.com/"))))
33273849 106
c84be134
AB
107(defmacro csetq (&rest args)
108 "Set the value of user option VAR to VALUE.
41d290a2 109
c84be134
AB
110More generally, you can use multiple variables and values, as in
111 (csetq VAR VALUE VAR VALUE...)
112This sets each user option VAR's value to the corresponding VALUE.
113
114\(fn [VAR VALUE]...)"
115 (declare (debug setq))
116 `(progn
117 ,@(cl-loop for (var value) on args by 'cddr
118 collect
119 `(funcall (or (get ',var 'custom-set) #'set-default)
120 ',var ,value))))
41d290a2 121
b57457b2
AB
122\f
123;;; Initial setup
124
125;; keep ~/.emacs.d clean
0596e3cf
AB
126(defvar b/etc-dir
127 (expand-file-name
128 (convert-standard-filename "etc/") user-emacs-directory)
129 "The directory where packages place their configuration files.")
130(defvar b/var-dir
131 (expand-file-name
132 (convert-standard-filename "var/") user-emacs-directory)
133 "The directory where packages place their persistent data files.")
134(defun b/etc (file)
135 "Expand filename FILE relative to `b/etc-dir'."
136 (expand-file-name (convert-standard-filename file) b/etc-dir))
137(defun b/var (file)
138 "Expand filename FILE relative to `b/var-dir'."
139 (expand-file-name (convert-standard-filename file) b/var-dir))
140
141(csetq
142 auto-save-list-file-prefix (b/var "auto-save/sessions/")
143 nsm-settings-file (b/var "nsm-settings.el"))
c84be134
AB
144
145(require 'auto-compile)
146(auto-compile-on-load-mode)
147(auto-compile-on-save-mode)
148(setq auto-compile-display-buffer nil)
149(setq auto-compile-mode-line-counter t)
150(setq auto-compile-source-recreate-deletes-dest t)
151(setq auto-compile-toggle-deletes-nonlib-dest t)
152(setq auto-compile-update-autoloads t)
032726b6 153
b57457b2 154;; separate custom file (don't want it mixing with init.el)
c84be134 155(with-eval-after-load 'custom
dca50cf5 156 (setq custom-file (b/etc "custom.el"))
41d290a2
AB
157 (when (file-exists-p custom-file)
158 (load custom-file))
b57457b2 159 ;; while at it, treat themes as safe
60ff805e
AB
160 (setf custom-safe-themes t)
161 ;; only one custom theme at a time
162 (comment
163 (defadvice load-theme (before clear-previous-themes activate)
164 "Clear existing theme settings instead of layering them"
165 (mapc #'disable-theme custom-enabled-themes))))
41d290a2 166
b57457b2 167;; load the secrets file if it exists, otherwise show a warning
dca50cf5
AB
168(comment
169 (with-demoted-errors
170 (load (b/etc "secrets"))))
41d290a2 171
b57457b2
AB
172;; start up emacs server. see
173;; https://www.gnu.org/software/emacs/manual/html_node/emacs/Emacs-Server.html#Emacs-Server
c84be134
AB
174(run-with-idle-timer 0.5 nil #'require 'server)
175(with-eval-after-load 'server
2087ae39 176 (declare-function server-edit "server")
0596e3cf 177 (global-set-key (kbd "C-c F D") #'server-edit)
2087ae39
AB
178 (declare-function server-running-p "server")
179 (or (server-running-p) (server-mode)))
41d290a2 180
60ff805e 181\f
60ff805e
AB
182;;; Defaults
183
184;;;; C-level customizations
185
c84be134 186(csetq
60ff805e
AB
187 ;; minibuffer
188 enable-recursive-minibuffers t
189 resize-mini-windows t
190 ;; more useful frame titles
7c558c9b
AB
191 ;; frame-title-format '("" invocation-name " - "
192 ;; (:eval
193 ;; (if (buffer-file-name)
194 ;; (abbreviate-file-name (buffer-file-name))
195 ;; "%b")))
60ff805e
AB
196 ;; i don't feel like jumping out of my chair every now and again; so
197 ;; don't BEEP! at me, emacs
198 ring-bell-function 'ignore
199 ;; better scrolling
200 ;; scroll-margin 1
201 ;; scroll-conservatively 10000
202 scroll-step 1
bdd3a0bb 203 scroll-conservatively 101
60ff805e
AB
204 scroll-preserve-screen-position 1
205 ;; focus follows mouse
39cc9c96 206 mouse-autoselect-window t)
60ff805e
AB
207
208(setq-default
209 ;; always use space for indentation
210 indent-tabs-mode nil
211 tab-width 4
7682baf8
AB
212 ;; case-sensitive search (and `dabbrev-expand')
213 ;; case-fold-search nil
60ff805e 214 ;; cursor shape
567440fa 215 cursor-type t)
60ff805e 216
7c558c9b
AB
217(set-fontset-font t 'arabic "Vazir")
218
b57457b2
AB
219;; unicode support
220(comment
221 (dolist (ft (fontset-list))
222 (set-fontset-font
223 ft
224 'unicode
225 (font-spec :name "Source Code Pro" :size 14))
226 (set-fontset-font
227 ft
228 'unicode
229 (font-spec :name "DejaVu Sans Mono")
230 nil
231 'append)
232 ;; (set-fontset-font
233 ;; ft
234 ;; 'unicode
235 ;; (font-spec
236 ;; :name "Symbola monospacified for DejaVu Sans Mono")
237 ;; nil
238 ;; 'append)
239 ;; (set-fontset-font
240 ;; ft
241 ;; #x2115 ; ℕ
242 ;; (font-spec :name "DejaVu Sans Mono")
243 ;; nil
244 ;; 'append)
245 (set-fontset-font
246 ft
247 (cons ?Α ?ω)
248 (font-spec :name "DejaVu Sans Mono" :size 14)
249 nil
250 'prepend)))
251
60ff805e 252;;;; Elisp-level customizations
41d290a2 253
c84be134
AB
254;; startup
255;; don't need to see the startup echo area message
256(advice-add #'display-startup-echo-area-message :override #'ignore)
257(csetq
258 ;; i want *scratch* as my startup buffer
259 initial-buffer-choice t
260 ;; i don't need the default hint
261 initial-scratch-message nil
262 ;; use customizable text-mode as major mode for *scratch*
263 ;; (initial-major-mode 'text-mode)
264 ;; inhibit buffer list when more than 2 files are loaded
265 inhibit-startup-buffer-menu t
266 ;; don't need to see the startup screen or echo area message
267 inhibit-startup-screen t
268 inhibit-startup-echo-area-message user-login-name)
269
270;; files
271(csetq
272 ;; backups (C-h v make-backup-files RET)
273 backup-by-copying t
0596e3cf 274 backup-directory-alist (list (cons "." (b/var "backup/")))
c84be134
AB
275 version-control t
276 delete-old-versions t
277 ;; auto-save
278 auto-save-file-name-transforms `((".*" ,(b/var "auto-save/") t))
279 ;; insert newline at the end of files
280 require-final-newline t
281 ;; open read-only file buffers in view-mode
282 ;; (enables niceties like `q' for quit)
283 view-read-only t)
284
285;; novice
60ff805e 286;; disable disabled commands
c84be134 287(csetq disabled-command-function nil)
41d290a2 288
60ff805e
AB
289;; lazy-person-friendly yes/no prompts
290(defalias 'yes-or-no-p #'y-or-n-p)
b57457b2 291
60ff805e 292;; enable automatic reloading of changed buffers and files
c84be134
AB
293(progn ; autorevert
294 (csetq auto-revert-verbose nil
295 global-auto-revert-non-file-buffers nil)
296 (require 'autorevert)
297 (global-auto-revert-mode 1))
b57457b2
AB
298
299;; time and battery in mode-line
c84be134
AB
300(csetq
301 display-time-default-load-average nil
302 display-time-format " %a %b %-e %-l:%M%P"
303 display-time-mail-icon '(image :type xpm
304 :file "gnus/gnus-pointer.xpm"
305 :ascent center)
306 display-time-use-mail-icon t)
307(require 'time)
308(display-time-mode)
309
310(csetq battery-mode-line-format "%p%% %t")
311(require 'battery)
312(display-battery-mode)
313
314(require 'fringe)
315;; smaller fringe
316;; (fringe-mode '(3 . 1))
317(fringe-mode nil)
318
319(require 'winner)
320;; enable winner-mode (C-h f winner-mode RET)
321(winner-mode 1)
322
323(with-eval-after-load 'compile
60ff805e
AB
324 ;; don't display *compilation* buffer on success. based on
325 ;; https://stackoverflow.com/a/17788551, with changes to use `cl-letf'
326 ;; instead of the now obsolete `flet'.
dca50cf5 327 (defun b/compilation-finish-function (buffer outstr)
41d290a2
AB
328 (unless (string-match "finished" outstr)
329 (switch-to-buffer-other-window buffer))
330 t)
331
dca50cf5 332 (setq compilation-finish-functions #'b/compilation-finish-function)
41d290a2
AB
333
334 (require 'cl-macs)
335
336 (defadvice compilation-start
337 (around inhibit-display
338 (command &optional mode name-function highlight-regexp))
339 (if (not (string-match "^\\(find\\|grep\\)" command))
340 (cl-letf (((symbol-function 'display-buffer) #'ignore))
341 (save-window-excursion ad-do-it))
342 ad-do-it))
343 (ad-activate 'compilation-start))
344
c84be134
AB
345;; isearch
346(csetq
347 ;; allow scrolling in Isearch
348 isearch-allow-scroll t
349 ;; search for non-ASCII characters: i’d like non-ASCII characters such
350 ;; as ‘’“”«»‹›áⓐ𝒶 to be selected when i search for their ASCII
351 ;; counterpart. shoutout to
352 ;; http://endlessparentheses.com/new-in-emacs-25-1-easily-search-non-ascii-characters.html
353 search-default-mode #'char-fold-to-regexp)
354
355;; replace
60ff805e 356;; uncomment to extend the above behaviour to query-replace
c84be134
AB
357;; (csetq replace-char-fold t)
358
359;; vc
360(global-set-key (kbd "C-x v C-=") #'vc-ediff)
361
362(with-eval-after-load 'vc-git
363 (csetq vc-git-print-log-follow t))
364
365(csetq ediff-window-setup-function 'ediff-setup-windows-plain
366 ediff-split-window-function 'split-window-horizontally)
367(with-eval-after-load 'ediff
368 (add-hook 'ediff-after-quit-hook-internal #'winner-undo))
369
370;; face-remap
371(csetq
372 ;; gentler font resizing
373 text-scale-mode-step 1.05)
374
375(run-with-idle-timer 0.4 nil #'require 'mwheel)
376(csetq mouse-wheel-scroll-amount '(1 ((shift) . 1)) ; one line at a time
377 mouse-wheel-progressive-speed nil ; don't accelerate scrolling
378 mouse-wheel-follow-mouse t) ; scroll window under mouse
379
380(run-with-idle-timer 0.4 nil #'require 'pixel-scroll)
381(with-eval-after-load 'pixel-scroll
382 (pixel-scroll-mode 1))
383
384;; epg-config
385(csetq
386 epg-gpg-program (executable-find "gpg")
387 ;; ask for GPG passphrase in minibuffer
388 ;; this will fail if gpg>=2.1 is not available
389 epg-pinentry-mode 'loopback)
390
391;; (require 'pinentry)
392;; workaround for systemd-based distros:
393;; (setq pinentry--socket-dir server-socket-dir)
394;; (pinentry-start)
395
396;; auth-source
397(csetq
398 auth-sources '("~/.authinfo.gpg")
399 authinfo-hidden (regexp-opt '("password" "client-secret" "token")))
b98dbb3d 400
b57457b2 401\f
c84be134 402;;; General key bindings
b57457b2 403
c84be134
AB
404(global-set-key (kbd "C-a") #'b/move-indentation-or-beginning-of-line)
405(global-set-key (kbd "C-c a i") #'ielm)
406(global-set-key (kbd "C-c d") #'b/duplicate-line-or-region)
407(global-set-key (kbd "C-S-j") #'b/join-line-top)
408(global-set-key (kbd "C-c x") #'execute-extended-command)
41d290a2 409
c84be134
AB
410;; evaling and macro-expanding
411(global-set-key (kbd "C-c e b") #'eval-buffer)
412(global-set-key (kbd "C-c e e") #'eval-last-sexp)
413(global-set-key (kbd "C-c e p") #'pp-macroexpand-last-sexp)
414(global-set-key (kbd "C-c e r") #'eval-region)
41d290a2 415
c84be134
AB
416;; emacs things
417(global-set-key (kbd "C-c e i") #'emacs-init-time)
418(global-set-key (kbd "C-c e u") #'emacs-uptime)
419(global-set-key (kbd "C-c e v") #'emacs-version)
41d290a2 420
c84be134
AB
421;; finding
422(global-set-key (kbd "C-c f .") #'find-file)
423(global-set-key (kbd "C-c f d") #'find-name-dired)
424(global-set-key (kbd "C-c f l") #'find-library)
567440fa 425
c84be134
AB
426;; frames
427(global-set-key (kbd "C-c F m") #'make-frame-command)
428(global-set-key (kbd "C-c F d") #'delete-frame)
41d290a2 429
c84be134
AB
430;; help/describe
431(global-set-key (kbd "C-S-h C") #'describe-char)
432(global-set-key (kbd "C-S-h F") #'describe-face)
41d290a2 433
c84be134
AB
434;; (global-set-key (kbd "C-x k") #'b/kill-current-buffer)
435;; (global-set-key (kbd "C-x K") #'kill-buffer)
436;; (global-set-key (kbd "C-x s") #'save-buffer)
437;; (global-set-key (kbd "C-x S") #'save-some-buffers)
e5c2d147 438
c84be134 439(define-key emacs-lisp-mode-map (kbd "<C-return>") #'b/add-elisp-section)
41d290a2
AB
440
441(when (display-graphic-p)
c84be134 442 (global-unset-key (kbd "C-z")))
500004f4 443
b57457b2
AB
444\f
445;;; Essential packages
446
c84be134 447;; (require 'bandali-exwm)
33273849 448
f7910e3d 449(require 'bandali-org)
41d290a2 450
9c48decc
AB
451(require 'bandali-theme)
452
0596e3cf
AB
453;; magit, *the* right way to do git
454(csetq transient-history-file (b/var "transient/history.el")
455 transient-levels-file (b/etc "transient/levels.el")
456 transient-values-file (b/etc "transient/values.el"))
c84be134 457(with-eval-after-load 'magit
2087ae39
AB
458 (declare-function magit-add-section-hook "magit-section"
459 (hook function &optional at append local))
41d290a2
AB
460 (magit-add-section-hook 'magit-status-sections-hook
461 'magit-insert-modules
462 'magit-insert-stashes
463 'append)
3b3615f5
AB
464 ;; (magit-add-section-hook 'magit-status-sections-hook
465 ;; 'magit-insert-ignored-files
466 ;; 'magit-insert-untracked-files
467 ;; 'append)
c84be134
AB
468 (declare-function magit-display-buffer-fullframe-status-v1
469 "magit-mode" (buffer))
470 (csetq
471 magit-diff-refine-hunk t
472 magit-repository-directories '(("~/.emacs.d/" . 0)
473 ("~/src/git/" . 2))
474 ;; magit-completing-read-function 'magit-ido-completing-read
475 magit-display-buffer-function
476 #'magit-display-buffer-fullframe-status-v1)
41d290a2
AB
477 (nconc magit-section-initial-visibility-alist
478 '(([unpulled status] . show)
479 ([unpushed status] . show)))
c84be134
AB
480 (custom-set-faces '(magit-diff-file-heading ((t (:weight normal)))))
481
482 (with-eval-after-load 'magit-extras
483 (csetq
484 magit-pop-revision-stack-format
485 (pcase-let ((`(,pt ,_eob ,index-regexp)
486 (default-value 'magit-pop-revision-stack-format)))
487 `(,pt "[%N: %h]: %ci\n %s
7c558c9b 488 https://git.savannah.gnu.org/cgit/emacs.git/commit/?id=%H"
c84be134
AB
489 ,index-regexp)))))
490;; global key bindings
491(global-set-key (kbd "C-x g") #'magit-status)
492(global-set-key (kbd "C-c g b") #'magit-blame-addition)
493(global-set-key (kbd "C-c g l") #'magit-log-buffer-file)
494(global-set-key (kbd "C-c g y") #'magit-pop-revision-stack)
7c558c9b 495
b57457b2 496;; recently opened files
c84be134
AB
497(run-with-idle-timer 0.2 nil #'require 'recentf)
498(with-eval-after-load 'recentf
0596e3cf
AB
499 (csetq recentf-max-saved-items 2000
500 recentf-save-file (b/var "recentf-save.el"))
c84be134
AB
501 (add-to-list 'recentf-keep #'file-remote-p)
502 (recentf-mode))
41d290a2 503
679463c6 504;; needed for history for counsel
0596e3cf 505(csetq amx-save-file (b/var "amx-save.el"))
c84be134
AB
506(run-with-idle-timer 0.3 nil #'require 'amx)
507(with-eval-after-load 'amx
6eb104ff
AB
508 (amx-mode))
509
679463c6
AB
510;; (require 'bandali-ido)
511(require 'bandali-ivy)
41d290a2 512
679463c6 513(require 'bandali-eshell)
1eb20313 514;; (require 'bandali-multi-term)
41d290a2 515
679463c6 516(require 'bandali-ibuffer)
41d290a2 517
c84be134
AB
518;; outline
519;; (with-eval-after-load 'outline
520;; (when (featurep 'which-key)
521;; (which-key-add-key-based-replacements
522;; "C-c @" "outline"
523;; "s-O" "outline"))
524;; (define-key outline-minor-mode-map (kbd "<s-tab>")
525;; #'outline-toggle-children)
526;; (define-key outline-minor-mode-map (kbd "M-p")
527;; #'outline-previous-visible-heading)
528;; (define-key outline-minor-mode-map (kbd "M-n")
529;; #'outline-next-visible-heading)
530;; (defvar b/outline-prefix-map)
531;; (define-prefix-command 'b/outline-prefix-map)
532;; (define-key outline-minor-mode-map (kbd "s-O")
533;; 'b/outline-prefix-map)
534;; (define-key b/outline-prefix-map (kbd "TAB")
535;; #'outline-toggle-children)
536;; (define-key b/outline-prefix-map (kbd "a")
537;; #'outline-hide-body)
538;; (define-key b/outline-prefix-map (kbd "H")
539;; #'outline-hide-body)
540;; (define-key b/outline-prefix-map (kbd "S")
541;; #'outline-show-all)
542;; (define-key b/outline-prefix-map (kbd "h")
543;; #'outline-hide-subtree)
544;; (define-key b/outline-prefix-map (kbd "s")
545;; #'outline-show-subtree))
546;; (add-hook 'prog-mode-hook #'outline-minor-mode)
41d290a2 547
679463c6 548(require 'bandali-dired)
41d290a2 549
c84be134 550(with-eval-after-load 'help
41d290a2 551 (temp-buffer-resize-mode)
c84be134 552 (csetq help-window-select t)
41d290a2 553
c84be134
AB
554 ;; local key bindings
555 (define-key help-mode-map (kbd "p") #'backward-button)
556 (define-key help-mode-map (kbd "n") #'forward-button))
557
558(with-eval-after-load 'tramp
0596e3cf
AB
559 (csetq tramp-auto-save-directory (b/var "tramp/auto-save/")
560 tramp-persistency-file-name (b/var "tramp/persistency.el"))
41d290a2
AB
561 (add-to-list 'tramp-default-proxies-alist '(nil "\\`root\\'" "/ssh:%h:"))
562 (add-to-list 'tramp-default-proxies-alist '("localhost" nil nil))
563 (add-to-list 'tramp-default-proxies-alist
564 (list (regexp-quote (system-name)) nil nil)))
565
c84be134
AB
566(with-eval-after-load 'doc-view
567 (define-key doc-view-mode-map (kbd "M-RET") #'image-previous-line))
41d290a2 568
c84be134 569(csetq shr-max-width 80)
7c558c9b 570
e2178fd3 571;; Email (with Gnus, message, and EBDB)
2087ae39 572(require 'bandali-gnus)
c84be134
AB
573(with-eval-after-load 'sendmail
574 (csetq sendmail-program (executable-find "msmtp")
575 ;; message-sendmail-extra-arguments '("-v" "-d")
576 mail-specify-envelope-from t
577 mail-envelope-from 'header))
2087ae39 578(require 'bandali-message)
e2178fd3 579(require 'bandali-ebdb)
2087ae39 580
39c1c073
AB
581;; IRC (with ERC and ZNC)
582(require 'bandali-erc)
583
c84be134
AB
584(with-eval-after-load 'scpaste
585 (csetq scpaste-http-destination "https://p.bndl.org"
586 scpaste-scp-destination "p:~"))
0596e3cf
AB
587(global-set-key (kbd "C-c a p p") #'scpaste)
588(global-set-key (kbd "C-c a p r") #'scpaste-region)
2331b5a0 589
b57457b2
AB
590\f
591;;; Editing
5750405c 592
b57457b2 593;; display Lisp objects at point in the echo area
c84be134
AB
594(when (version< "25" emacs-version)
595 (with-eval-after-load 'eldoc
596 (global-eldoc-mode)))
41d290a2 597
b57457b2 598;; highlight matching parens
c84be134
AB
599(require 'paren)
600(show-paren-mode)
601
602;; (require 'elec-pair)
603;; (electric-pair-mode)
604
605(csetq
606 ;; Save what I copy into clipboard from other applications into Emacs'
607 ;; kill-ring, which would allow me to still be able to easily access
608 ;; it in case I kill (cut or copy) something else inside Emacs before
609 ;; yanking (pasting) what I'd originally intended to.
610 save-interprogram-paste-before-kill t)
611(with-eval-after-load 'simple
612 (column-number-mode))
41d290a2 613
b57457b2 614;; save minibuffer history
c84be134 615(require 'savehist)
0596e3cf 616(csetq savehist-file (b/var "savehist.el"))
c84be134
AB
617(savehist-mode)
618(add-to-list 'savehist-additional-variables 'kill-ring)
41d290a2 619
b57457b2 620;; automatically save place in files
c84be134 621(when (version< "25" emacs-version)
0596e3cf 622 (csetq save-place-file (b/var "save-place.el"))
c84be134
AB
623 (save-place-mode))
624
625(defun indicate-buffer-boundaries-left ()
626 (csetq indicate-buffer-boundaries 'left))
627(with-eval-after-load 'prog-mode
628 (global-prettify-symbols-mode))
629(add-hook 'prog-mode-hook #'indicate-buffer-boundaries-left)
630
631(define-key text-mode-map (kbd "C-*") #'b/insert-asterism)
632(add-hook 'text-mode-hook #'indicate-buffer-boundaries-left)
633(add-hook 'text-mode-hook #'flyspell-mode)
634
635(add-to-list 'auto-mode-alist '("\\.*rc$" . conf-mode))
636
637(add-to-list 'auto-mode-alist '("\\.bashrc$" . sh-mode))
638
639;; flycheck
640;; (run-with-idle-timer 0.6 nil #'require 'flycheck)
641;; (with-eval-after-load 'flycheck
642;; (csetq
643;; ;; Use the load-path from running Emacs when checking elisp files
644;; flycheck-emacs-lisp-load-path 'inherit
645;; ;; Only flycheck when I actually save the buffer
646;; flycheck-check-syntax-automatically '(mode-enabled save)
647;; flycheck-mode-line-prefix "flyc"))
648;; (define-key flycheck-mode-map (kbd "M-P") #'flycheck-previous-error)
649;; (define-key flycheck-mode-map (kbd "M-N") #'flycheck-next-error)
650;; (add-hook 'prog-mode-hook #'flycheck-mode)
651
652;; ispell
41d290a2 653;; http://endlessparentheses.com/ispell-and-apostrophes.html
c84be134
AB
654;; (run-with-idle-timer 0.6 nil #'require 'ispell)
655;; (with-eval-after-load 'ispell
656;; ;; ’ can be part of a word
657;; (csetq ispell-local-dictionary-alist
658;; `((nil "[[:alpha:]]" "[^[:alpha:]]"
659;; "['\x2019]" nil ("-B") nil utf-8))
660;; ispell-program-name (executable-find "hunspell"))
661;; ;; don't send ’ to the subprocess
662;; (defun endless/replace-apostrophe (args)
663;; (cons (replace-regexp-in-string
664;; "’" "'" (car args))
665;; (cdr args)))
666;; (advice-add #'ispell-send-string :filter-args
667;; #'endless/replace-apostrophe)
668;; ;; convert ' back to ’ from the subprocess
669;; (defun endless/replace-quote (args)
670;; (if (not (derived-mode-p 'org-mode))
671;; args
672;; (cons (replace-regexp-in-string
673;; "'" "’" (car args))
674;; (cdr args))))
675;; (advice-add #'ispell-parse-output :filter-args
676;; #'endless/replace-quote))
677
0596e3cf
AB
678;; abbrev
679(csetq abbrev-file-name (b/var "abbrev.el"))
c84be134 680(add-hook 'text-mode-hook #'abbrev-mode)
54209e74 681
b57457b2
AB
682\f
683;;; Programming modes
684
c84be134 685(with-eval-after-load 'lisp-mode
41d290a2
AB
686 (defun indent-spaces-mode ()
687 (setq indent-tabs-mode nil))
688 (add-hook 'lisp-interaction-mode-hook #'indent-spaces-mode))
689
c84be134
AB
690(with-eval-after-load 'alloy-mode
691 (csetq alloy-basic-offset 2)
fac1032c
AB
692 ;; (defun b/alloy-simple-indent (start end)
693 ;; (interactive "r")
694 ;; ;; (if (region-active-p)
695 ;; ;; (indent-rigidly start end alloy-basic-offset)
696 ;; ;; (if (bolp)
697 ;; ;; (indent-rigidly (line-beginning-position)
698 ;; ;; (line-end-position)
699 ;; ;; alloy-basic-offset)))
700 ;; (indent-to (+ (current-column) alloy-basic-offset)))
c84be134
AB
701 ;; local key bindings
702 (define-key alloy-mode-map (kbd "RET") #'electric-newline-and-maybe-indent)
703 ;; (define-key alloy-mode-map (kbd "TAB") #'b/alloy-simple-indent)
704 (define-key alloy-mode-map (kbd "TAB") #'indent-for-tab-command))
705(add-to-list 'auto-mode-alist '("\\.\\(als\\|dsh\\)\\'" . alloy-mode))
706(add-hook 'alloy-mode-hook (lambda nil (setq-local indent-tabs-mode nil)))
707
708;; lean
709;; (eval-when-compile (defvar lean-mode-map))
710;; (run-with-idle-timer 0.4 nil #'require 'lean-mode)
711;; (with-eval-after-load 'lean-mode
712;; (require 'lean-input)
713;; (csetq default-input-method "Lean"
714;; lean-input-tweak-all '(lean-input-compose
715;; (lean-input-prepend "/")
716;; (lean-input-nonempty))
717;; lean-input-user-translations '(("/" "/")))
718;; (lean-input-setup)
719;; ;; local key bindings
720;; (define-key lean-mode-map (kbd "S-SPC") #'company-complete))
721
722(with-eval-after-load 'sgml-mode
723 (csetq sgml-basic-offset 0))
724
725(with-eval-after-load 'css-mode
726 (csetq css-indent-offset 2))
727
728;; po-mode
729;; (add-hook 'po-mode-hook (lambda nil (run-with-timer 0.1 nil 'View-exit)))
730
731;; auctex
732;; (csetq font-latex-fontify-sectioning 'color)
733
734(with-eval-after-load 'tex-mode
748bd8ac
AB
735 (cl-delete-if
736 (lambda (p) (string-match "^---?" (car p)))
c84be134
AB
737 tex--prettify-symbols-alist))
738(add-hook 'tex-mode-hook #'auto-fill-mode)
739(add-hook 'tex-mode-hook #'flyspell-mode)
1d01c927 740
b57457b2 741\f
b57457b2 742;;; Emacs enhancements & auxiliary packages
1eb20313 743
c84be134
AB
744(with-eval-after-load 'man
745 (csetq Man-width 80))
746
747(run-with-idle-timer 0.4 nil #'require 'which-key)
748(with-eval-after-load 'which-key
749 (csetq
750 which-key-add-column-padding 5
751 which-key-idle-delay 10000
752 which-key-idle-secondary-delay 0.05
753 which-key-max-description-length 32
754 which-key-show-early-on-C-h t)
41d290a2
AB
755 (which-key-add-key-based-replacements
756 ;; prefixes for global prefixes and minor modes
41d290a2 757 "C-c !" "flycheck"
b549b760 758 "C-x RET" "coding system"
41d290a2 759 "C-x 8" "unicode"
b549b760 760 "C-x @" "event modifiers"
41d290a2
AB
761 "C-x a" "abbrev/expand"
762 "C-x r" "rectangle/register/bookmark"
b549b760 763 "C-x t" "tabs"
41d290a2 764 "C-x v" "version control"
b549b760
AB
765 "C-x X" "edebug"
766 "C-x C-a" "edebug"
767 "C-x C-k" "kmacro"
41d290a2 768 ;; prefixes for my personal bindings
b549b760 769 "C-c &" "yasnippet"
41d290a2
AB
770 "C-c a" "applications"
771 "C-c a e" "erc"
772 "C-c a o" "org"
773 "C-c a s" "shells"
2e81c51a 774 "C-c b" "buffers"
41d290a2
AB
775 "C-c c" "compile-and-comments"
776 "C-c e" "eval"
777 "C-c f" "files"
778 "C-c F" "frames"
ef6c487c 779 "C-c g" "magit"
41d290a2 780 "C-S-h" "help(ful)"
41d290a2 781 "C-c q" "boxquote"
1eb20313 782 "C-c t" "themes")
41d290a2 783 ;; prefixes for major modes
41d290a2
AB
784 (which-key-add-major-mode-key-based-replacements 'org-mode
785 "C-c C-v" "org-babel")
c84be134 786 (which-key-mode))
41d290a2 787
1eb20313 788;; (require 'bandali-projectile)
41d290a2 789
c84be134
AB
790(run-with-idle-timer 0.6 nil #'require 'unkillable-scratch)
791(with-eval-after-load 'unkillable-scratch
792 (csetq unkillable-buffers '("^\\*scratch\\*$" "^\\*Messages\\*$"))
793 (unkillable-scratch 1))
41d290a2 794
5b10d879
AB
795;; ,----
796;; | make pretty boxed quotes like this
797;; `----
c84be134
AB
798(run-with-idle-timer 0.6 nil #'require 'boxquote)
799(with-eval-after-load 'boxquote
800 (defvar b/boxquote-prefix-map)
801 (define-prefix-command 'b/boxquote-prefix-map)
802 (global-set-key (kbd "C-c q") 'b/boxquote-prefix-map)
803 (define-key b/boxquote-prefix-map (kbd "b") #'boxquote-buffer)
804 (define-key b/boxquote-prefix-map (kbd "B") #'boxquote-insert-buffer)
805 (define-key b/boxquote-prefix-map (kbd "d") #'boxquote-defun)
806 (define-key b/boxquote-prefix-map (kbd "F") #'boxquote-insert-file)
807 (define-key b/boxquote-prefix-map (kbd "hf") #'boxquote-describe-function)
808 (define-key b/boxquote-prefix-map (kbd "hk") #'boxquote-describe-key)
809 (define-key b/boxquote-prefix-map (kbd "hv") #'boxquote-describe-variable)
810 (define-key b/boxquote-prefix-map (kbd "hw") #'boxquote-where-is)
811 (define-key b/boxquote-prefix-map (kbd "k") #'boxquote-kill)
812 (define-key b/boxquote-prefix-map (kbd "p") #'boxquote-paragraph)
813 (define-key b/boxquote-prefix-map (kbd "q") #'boxquote-boxquote)
814 (define-key b/boxquote-prefix-map (kbd "r") #'boxquote-region)
815 (define-key b/boxquote-prefix-map (kbd "s") #'boxquote-shell-command)
816 (define-key b/boxquote-prefix-map (kbd "t") #'boxquote-text)
817 (define-key b/boxquote-prefix-map (kbd "T") #'boxquote-title)
818 (define-key b/boxquote-prefix-map (kbd "u") #'boxquote-unbox)
819 (define-key b/boxquote-prefix-map (kbd "U") #'boxquote-unbox-region)
820 (define-key b/boxquote-prefix-map (kbd "y") #'boxquote-yank)
821 (define-key b/boxquote-prefix-map (kbd "M-q") #'boxquote-fill-paragraph)
822 (define-key b/boxquote-prefix-map (kbd "M-w") #'boxquote-kill-ring-save))
823
824(run-with-idle-timer 0.5 nil #'require 'hl-todo)
825(with-eval-after-load 'hl-todo
1eb20313 826 ;; highlight TODOs in buffers
41d290a2
AB
827 (global-hl-todo-mode))
828
c84be134
AB
829(run-with-idle-timer 0.5 nil #'require 'page-break-lines)
830(with-eval-after-load 'page-break-lines
831 (csetq page-break-lines-max-width fill-column)
41d290a2
AB
832 (global-page-break-lines-mode))
833
c84be134
AB
834;; expand-region
835(global-set-key (kbd "C-=") #'er/expand-region)
836
837(run-with-idle-timer 0.6 nil #'require 'yasnippet)
838(with-eval-after-load 'yasnippet
839 (declare-function yas-reload-all
840 "yasnippet" (&optional no-jit interactive))
841 (declare-function yas-maybe-expand-abbrev-key-filter
842 "yasnippet" (cmd))
843
844 (defconst yas-verbosity-cur yas-verbosity)
845 (setq yas-verbosity 2)
0596e3cf 846 (csetq yas-snippet-dirs `(,(b/etc "yasnippet/snippets")))
c84be134
AB
847 (add-to-list 'yas-snippet-dirs "~/src/git/guix/etc/snippets" t)
848 (yas-reload-all)
849 (setq yas-verbosity yas-verbosity-cur)
850
851 (defun b/yas-maybe-expand-abbrev-key-filter (cmd)
852 (when (and (yas-maybe-expand-abbrev-key-filter cmd)
853 (not (bound-and-true-p git-commit-mode)))
854 cmd))
855 (defconst b/yas-maybe-expand
856 '(menu-item "" yas-expand
857 :filter b/yas-maybe-expand-abbrev-key-filter))
858 (define-key yas-minor-mode-map (kbd "SPC") b/yas-maybe-expand)
859
860 (yas-global-mode))
861
862;; debbugs
863(global-set-key (kbd "C-c D d") #'debbugs-gnu)
864(global-set-key (kbd "C-c D b") #'debbugs-gnu-bugs)
865(global-set-key (kbd "C-c D e") ; bug-gnu-emacs
866 (lambda ()
867 (interactive)
868 (setq debbugs-gnu-current-suppress t)
869 (debbugs-gnu debbugs-gnu-default-severities
870 '("emacs"))))
871(global-set-key (kbd "C-c D g") ; bug-gnuzilla
872 (lambda ()
873 (interactive)
874 (setq debbugs-gnu-current-suppress t)
875 (debbugs-gnu debbugs-gnu-default-severities
876 '("gnuzilla"))))
877(global-set-key (kbd "C-c D G b") ; bug-guix
878 (lambda ()
879 (interactive)
880 (setq debbugs-gnu-current-suppress t)
881 (debbugs-gnu debbugs-gnu-default-severities
882 '("guix"))))
883(global-set-key (kbd "C-c D G p") ; guix-patches
884 (lambda ()
885 (interactive)
886 (setq debbugs-gnu-current-suppress t)
887 (debbugs-gnu debbugs-gnu-default-severities
888 '("guix-patches"))))
889
0596e3cf
AB
890;; url and url-cache
891(csetq
892 url-configuration-directory (b/var "url/configuration/")
893 url-cache-directory (b/var "url/cache/"))
894
c84be134
AB
895;; eww
896(csetq eww-download-directory (file-name-as-directory
897 (getenv "XDG_DOWNLOAD_DIR")))
898(global-set-key (kbd "C-c a e w") #'eww)
41d290a2 899
f0a2dfe1
AB
900(comment
901
c84be134
AB
902;; org-ref
903(csetq
904 reftex-default-bibliography '("~/usr/org/references.bib")
905 org-ref-default-bibliography '("~/usr/org/references.bib")
906 org-ref-bibliography-notes "~/usr/org/notes.org"
907 org-ref-pdf-directory "~/usr/org/bibtex-pdfs/")
908
909;; fill-column-indicator ?
910
911;; window
912(csetq split-width-threshold 150)
913(global-set-key (kbd "C-c w s l")
914 (lambda ()
915 (interactive)
916 (split-window-right)
917 (other-window 1)))
918(global-set-key (kbd "C-c w s j")
919 (lambda ()
920 (interactive)
921 (split-window-below)
922 (other-window 1)))
923(global-set-key (kbd "C-c w q") #'quit-window)
924
925(run-with-idle-timer 0.6 nil #'require 'windmove)
926(global-set-key (kbd "C-c w h") #'windmove-left)
927(global-set-key (kbd "C-c w j") #'windmove-down)
928(global-set-key (kbd "C-c w k") #'windmove-up)
929(global-set-key (kbd "C-c w l") #'windmove-right)
930(global-set-key (kbd "C-c w H") #'windmove-swap-states-left)
931(global-set-key (kbd "C-c w J") #'windmove-swap-states-down)
932(global-set-key (kbd "C-c w K") #'windmove-swap-states-up)
933(global-set-key (kbd "C-c w L") #'windmove-swap-states-right)
934
935;; pass
936(global-set-key (kbd "C-c a p") #'pass)
937(add-hook 'pass-mode-hook #'View-exit)
938
939;; reftex
940;; uncomment to disable reftex-cite's default choice of previous word
941;; (with-eval-after-load 'reftex
942;; (require 'reftex-cite)
943;; (defun reftex-get-bibkey-default ()
944;; "If the cursor is in a citation macro, return the word before the macro."
945;; (let* ((macro (reftex-what-macro 1)))
946;; (save-excursion
947;; (when (and macro (string-match "cite" (car macro)))
948;; (goto-char (cdr macro)))
949;; (reftex-this-word)))))
950(add-hook 'latex-mode-hook #'reftex-mode)
951
952;; dmenu
953(csetq
954 dmenu-prompt-string "run: "
955 dmenu-save-file (b/var "dmenu-items"))
956
957;; eosd ?
9ed5410e 958
b57457b2 959\f
b57457b2
AB
960;;; Post initialization
961
8b1a2f32 962)
41d290a2
AB
963(message "Loading %s...done (%.3fs)" user-init-file
964 (float-time (time-subtract (current-time)
dca50cf5 965 b/before-user-init-time)))
41d290a2
AB
966
967;;; init.el ends here