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