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