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