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