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