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