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