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