Various config updates in rc.org
[~bandali/configs] / init.el
CommitLineData
33b1a7ea 1;;; init.el --- bandali's emacs configuration -*- lexical-binding: t -*-
41d290a2 2
4c05c418 3;; Copyright (C) 2018-2020 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
33b1a7ea 20;; GNU Emacs configuration of Amin Bandali, computer scientist,
92ad9675 21;; Free Software activist, and GNU maintainer & webmaster. Packages
f7910e3d 22;; are installed through using Borg for a fully reproducible setup.
b57457b2
AB
23
24;; Over the years, I've taken inspiration from configurations of many
25;; great people. Some that I can remember off the top of my head are:
26;;
27;; - https://github.com/dieggsy/dotfiles
28;; - https://github.com/dakra/dmacs
29;; - http://pages.sachachua.com/.emacs.d/Sacha.html
30;; - https://github.com/dakrone/eos
31;; - http://doc.rix.si/cce/cce.html
32;; - https://github.com/jwiegley/dot-emacs
33;; - https://github.com/wasamasa/dotemacs
34;; - https://github.com/hlissner/doom-emacs
41d290a2 35
49e9503b
AB
36;;; Code:
37
b57457b2
AB
38;;; Emacs initialization
39
dca50cf5 40(defvar b/before-user-init-time (current-time)
41d290a2 41 "Value of `current-time' when Emacs begins loading `user-init-file'.")
83364e5b
AB
42(defvar b/emacs-initialized nil
43 "Whether Emacs has been initialized.")
44
45(when (not (bound-and-true-p b/emacs-initialized))
46 (message "Loading Emacs...done (%.3fs)"
47 (float-time (time-subtract b/before-user-init-time
48 before-init-time))))
41d290a2 49
b57457b2
AB
50;; temporarily increase `gc-cons-threshhold' and `gc-cons-percentage'
51;; during startup to reduce garbage collection frequency. clearing
52;; `file-name-handler-alist' seems to help reduce startup time too.
dca50cf5
AB
53(defvar b/gc-cons-threshold gc-cons-threshold)
54(defvar b/gc-cons-percentage gc-cons-percentage)
55(defvar b/file-name-handler-alist file-name-handler-alist)
bc58e70a 56(setq gc-cons-threshold (* 30 1024 1024) ; 30 MiB
41d290a2
AB
57 gc-cons-percentage 0.6
58 file-name-handler-alist nil
59 ;; sidesteps a bug when profiling with esup
60 esup-child-profile-require-level 0)
61
b57457b2 62;; set them back to their defaults once we're done initializing
dca50cf5 63(defun b/post-init ()
83364e5b
AB
64 "My post-initialize function, run after loading `user-init-file'."
65 (setq b/emacs-initialized t
66 gc-cons-threshold b/gc-cons-threshold
67 gc-cons-percentage b/gc-cons-percentage
e8a3b8f7 68 file-name-handler-alist b/file-name-handler-alist)
c84be134 69 (when (featurep 'exwm-workspace)
8b1a2f32
AB
70 (with-eval-after-load 'exwm-workspace
71 (setq-default
72 mode-line-format
73 (append
74 mode-line-format
75 '((:eval
76 (format
77 "[%s]" (number-to-string
78 exwm-workspace-current-index)))))))))
dca50cf5 79(add-hook 'after-init-hook #'b/post-init)
41d290a2 80
b57457b2 81;; increase number of lines kept in *Messages* log
41d290a2
AB
82(setq message-log-max 20000)
83
b57457b2
AB
84;; optionally, uncomment to supress some byte-compiler warnings
85;; (see C-h v byte-compile-warnings RET for more info)
41d290a2
AB
86;; (setq byte-compile-warnings
87;; '(not free-vars unresolved noruntime lexical make-local))
88
b57457b2
AB
89\f
90;;; whoami
91
41d290a2 92(setq user-full-name "Amin Bandali"
33b1a7ea 93 user-mail-address "bandali@gnu.org")
41d290a2 94
b57457b2 95\f
33273849
AB
96;;; Package management
97
032726b6
AB
98(progn ; `borg'
99 (add-to-list 'load-path
100 (expand-file-name "lib/borg" user-emacs-directory))
101 (require 'borg)
102 (borg-initialize)
103 (setq borg-rewrite-urls-alist
104 '(("git@github.com:" . "https://github.com/")
105 ("git@gitlab.com:" . "https://gitlab.com/"))))
33273849 106
c84be134
AB
107(defmacro csetq (&rest args)
108 "Set the value of user option VAR to VALUE.
41d290a2 109
c84be134
AB
110More generally, you can use multiple variables and values, as in
111 (csetq VAR VALUE VAR VALUE...)
112This sets each user option VAR's value to the corresponding VALUE.
113
114\(fn [VAR VALUE]...)"
115 (declare (debug setq))
116 `(progn
117 ,@(cl-loop for (var value) on args by 'cddr
118 collect
119 `(funcall (or (get ',var 'custom-set) #'set-default)
120 ',var ,value))))
41d290a2 121
b57457b2
AB
122\f
123;;; Initial setup
124
125;; keep ~/.emacs.d clean
c84be134
AB
126(require 'no-littering)
127(defalias 'b/etc 'no-littering-expand-etc-file-name)
128(defalias 'b/var 'no-littering-expand-var-file-name)
129
130(require 'auto-compile)
131(auto-compile-on-load-mode)
132(auto-compile-on-save-mode)
133(setq auto-compile-display-buffer nil)
134(setq auto-compile-mode-line-counter t)
135(setq auto-compile-source-recreate-deletes-dest t)
136(setq auto-compile-toggle-deletes-nonlib-dest t)
137(setq auto-compile-update-autoloads t)
032726b6 138
b57457b2 139;; separate custom file (don't want it mixing with init.el)
c84be134 140(with-eval-after-load 'custom
dca50cf5 141 (setq custom-file (b/etc "custom.el"))
41d290a2
AB
142 (when (file-exists-p custom-file)
143 (load custom-file))
b57457b2 144 ;; while at it, treat themes as safe
60ff805e
AB
145 (setf custom-safe-themes t)
146 ;; only one custom theme at a time
147 (comment
148 (defadvice load-theme (before clear-previous-themes activate)
149 "Clear existing theme settings instead of layering them"
150 (mapc #'disable-theme custom-enabled-themes))))
41d290a2 151
b57457b2 152;; load the secrets file if it exists, otherwise show a warning
dca50cf5
AB
153(comment
154 (with-demoted-errors
155 (load (b/etc "secrets"))))
41d290a2 156
b57457b2
AB
157;; start up emacs server. see
158;; https://www.gnu.org/software/emacs/manual/html_node/emacs/Emacs-Server.html#Emacs-Server
c84be134
AB
159(run-with-idle-timer 0.5 nil #'require 'server)
160(with-eval-after-load 'server
2087ae39
AB
161 (declare-function server-edit "server")
162 (bind-key "C-c F D" 'server-edit)
163 (declare-function server-running-p "server")
164 (or (server-running-p) (server-mode)))
41d290a2 165
60ff805e 166\f
60ff805e
AB
167;;; Defaults
168
169;;;; C-level customizations
170
c84be134 171(csetq
60ff805e
AB
172 ;; minibuffer
173 enable-recursive-minibuffers t
174 resize-mini-windows t
175 ;; more useful frame titles
7c558c9b
AB
176 ;; frame-title-format '("" invocation-name " - "
177 ;; (:eval
178 ;; (if (buffer-file-name)
179 ;; (abbreviate-file-name (buffer-file-name))
180 ;; "%b")))
60ff805e
AB
181 ;; i don't feel like jumping out of my chair every now and again; so
182 ;; don't BEEP! at me, emacs
183 ring-bell-function 'ignore
184 ;; better scrolling
185 ;; scroll-margin 1
186 ;; scroll-conservatively 10000
187 scroll-step 1
bdd3a0bb 188 scroll-conservatively 101
60ff805e
AB
189 scroll-preserve-screen-position 1
190 ;; focus follows mouse
39cc9c96 191 mouse-autoselect-window t)
60ff805e
AB
192
193(setq-default
194 ;; always use space for indentation
195 indent-tabs-mode nil
196 tab-width 4
7682baf8
AB
197 ;; case-sensitive search (and `dabbrev-expand')
198 ;; case-fold-search nil
60ff805e 199 ;; cursor shape
567440fa 200 cursor-type t)
60ff805e 201
7c558c9b
AB
202(set-fontset-font t 'arabic "Vazir")
203
b57457b2
AB
204;; unicode support
205(comment
206 (dolist (ft (fontset-list))
207 (set-fontset-font
208 ft
209 'unicode
210 (font-spec :name "Source Code Pro" :size 14))
211 (set-fontset-font
212 ft
213 'unicode
214 (font-spec :name "DejaVu Sans Mono")
215 nil
216 'append)
217 ;; (set-fontset-font
218 ;; ft
219 ;; 'unicode
220 ;; (font-spec
221 ;; :name "Symbola monospacified for DejaVu Sans Mono")
222 ;; nil
223 ;; 'append)
224 ;; (set-fontset-font
225 ;; ft
226 ;; #x2115 ; ℕ
227 ;; (font-spec :name "DejaVu Sans Mono")
228 ;; nil
229 ;; 'append)
230 (set-fontset-font
231 ft
232 (cons ?Α ?ω)
233 (font-spec :name "DejaVu Sans Mono" :size 14)
234 nil
235 'prepend)))
236
60ff805e 237;;;; Elisp-level customizations
41d290a2 238
c84be134
AB
239;; startup
240;; don't need to see the startup echo area message
241(advice-add #'display-startup-echo-area-message :override #'ignore)
242(csetq
243 ;; i want *scratch* as my startup buffer
244 initial-buffer-choice t
245 ;; i don't need the default hint
246 initial-scratch-message nil
247 ;; use customizable text-mode as major mode for *scratch*
248 ;; (initial-major-mode 'text-mode)
249 ;; inhibit buffer list when more than 2 files are loaded
250 inhibit-startup-buffer-menu t
251 ;; don't need to see the startup screen or echo area message
252 inhibit-startup-screen t
253 inhibit-startup-echo-area-message user-login-name)
254
255;; files
256(csetq
257 ;; backups (C-h v make-backup-files RET)
258 backup-by-copying t
259 version-control t
260 delete-old-versions t
261 ;; auto-save
262 auto-save-file-name-transforms `((".*" ,(b/var "auto-save/") t))
263 ;; insert newline at the end of files
264 require-final-newline t
265 ;; open read-only file buffers in view-mode
266 ;; (enables niceties like `q' for quit)
267 view-read-only t)
268
269;; novice
60ff805e 270;; disable disabled commands
c84be134 271(csetq disabled-command-function nil)
41d290a2 272
60ff805e
AB
273;; lazy-person-friendly yes/no prompts
274(defalias 'yes-or-no-p #'y-or-n-p)
b57457b2 275
60ff805e 276;; enable automatic reloading of changed buffers and files
c84be134
AB
277(progn ; autorevert
278 (csetq auto-revert-verbose nil
279 global-auto-revert-non-file-buffers nil)
280 (require 'autorevert)
281 (global-auto-revert-mode 1))
b57457b2
AB
282
283;; time and battery in mode-line
c84be134
AB
284(csetq
285 display-time-default-load-average nil
286 display-time-format " %a %b %-e %-l:%M%P"
287 display-time-mail-icon '(image :type xpm
288 :file "gnus/gnus-pointer.xpm"
289 :ascent center)
290 display-time-use-mail-icon t)
291(require 'time)
292(display-time-mode)
293
294(csetq battery-mode-line-format "%p%% %t")
295(require 'battery)
296(display-battery-mode)
297
298(require 'fringe)
299;; smaller fringe
300;; (fringe-mode '(3 . 1))
301(fringe-mode nil)
302
303(require 'winner)
304;; enable winner-mode (C-h f winner-mode RET)
305(winner-mode 1)
306
307(with-eval-after-load 'compile
60ff805e
AB
308 ;; don't display *compilation* buffer on success. based on
309 ;; https://stackoverflow.com/a/17788551, with changes to use `cl-letf'
310 ;; instead of the now obsolete `flet'.
dca50cf5 311 (defun b/compilation-finish-function (buffer outstr)
41d290a2
AB
312 (unless (string-match "finished" outstr)
313 (switch-to-buffer-other-window buffer))
314 t)
315
dca50cf5 316 (setq compilation-finish-functions #'b/compilation-finish-function)
41d290a2
AB
317
318 (require 'cl-macs)
319
320 (defadvice compilation-start
321 (around inhibit-display
322 (command &optional mode name-function highlight-regexp))
323 (if (not (string-match "^\\(find\\|grep\\)" command))
324 (cl-letf (((symbol-function 'display-buffer) #'ignore))
325 (save-window-excursion ad-do-it))
326 ad-do-it))
327 (ad-activate 'compilation-start))
328
c84be134
AB
329;; isearch
330(csetq
331 ;; allow scrolling in Isearch
332 isearch-allow-scroll t
333 ;; search for non-ASCII characters: i’d like non-ASCII characters such
334 ;; as ‘’“”«»‹›áⓐ𝒶 to be selected when i search for their ASCII
335 ;; counterpart. shoutout to
336 ;; http://endlessparentheses.com/new-in-emacs-25-1-easily-search-non-ascii-characters.html
337 search-default-mode #'char-fold-to-regexp)
338
339;; replace
60ff805e 340;; uncomment to extend the above behaviour to query-replace
c84be134
AB
341;; (csetq replace-char-fold t)
342
343;; vc
344(global-set-key (kbd "C-x v C-=") #'vc-ediff)
345
346(with-eval-after-load 'vc-git
347 (csetq vc-git-print-log-follow t))
348
349(csetq ediff-window-setup-function 'ediff-setup-windows-plain
350 ediff-split-window-function 'split-window-horizontally)
351(with-eval-after-load 'ediff
352 (add-hook 'ediff-after-quit-hook-internal #'winner-undo))
353
354;; face-remap
355(csetq
356 ;; gentler font resizing
357 text-scale-mode-step 1.05)
358
359(run-with-idle-timer 0.4 nil #'require 'mwheel)
360(csetq mouse-wheel-scroll-amount '(1 ((shift) . 1)) ; one line at a time
361 mouse-wheel-progressive-speed nil ; don't accelerate scrolling
362 mouse-wheel-follow-mouse t) ; scroll window under mouse
363
364(run-with-idle-timer 0.4 nil #'require 'pixel-scroll)
365(with-eval-after-load 'pixel-scroll
366 (pixel-scroll-mode 1))
367
368;; epg-config
369(csetq
370 epg-gpg-program (executable-find "gpg")
371 ;; ask for GPG passphrase in minibuffer
372 ;; this will fail if gpg>=2.1 is not available
373 epg-pinentry-mode 'loopback)
374
375;; (require 'pinentry)
376;; workaround for systemd-based distros:
377;; (setq pinentry--socket-dir server-socket-dir)
378;; (pinentry-start)
379
380;; auth-source
381(csetq
382 auth-sources '("~/.authinfo.gpg")
383 authinfo-hidden (regexp-opt '("password" "client-secret" "token")))
b98dbb3d 384
b57457b2 385\f
c84be134 386;;; General key bindings
b57457b2 387
c84be134
AB
388(global-set-key (kbd "C-a") #'b/move-indentation-or-beginning-of-line)
389(global-set-key (kbd "C-c a i") #'ielm)
390(global-set-key (kbd "C-c d") #'b/duplicate-line-or-region)
391(global-set-key (kbd "C-S-j") #'b/join-line-top)
392(global-set-key (kbd "C-c x") #'execute-extended-command)
41d290a2 393
c84be134
AB
394;; evaling and macro-expanding
395(global-set-key (kbd "C-c e b") #'eval-buffer)
396(global-set-key (kbd "C-c e e") #'eval-last-sexp)
397(global-set-key (kbd "C-c e p") #'pp-macroexpand-last-sexp)
398(global-set-key (kbd "C-c e r") #'eval-region)
41d290a2 399
c84be134
AB
400;; emacs things
401(global-set-key (kbd "C-c e i") #'emacs-init-time)
402(global-set-key (kbd "C-c e u") #'emacs-uptime)
403(global-set-key (kbd "C-c e v") #'emacs-version)
41d290a2 404
c84be134
AB
405;; finding
406(global-set-key (kbd "C-c f .") #'find-file)
407(global-set-key (kbd "C-c f d") #'find-name-dired)
408(global-set-key (kbd "C-c f l") #'find-library)
567440fa 409
c84be134
AB
410;; frames
411(global-set-key (kbd "C-c F m") #'make-frame-command)
412(global-set-key (kbd "C-c F d") #'delete-frame)
41d290a2 413
c84be134
AB
414;; help/describe
415(global-set-key (kbd "C-S-h C") #'describe-char)
416(global-set-key (kbd "C-S-h F") #'describe-face)
41d290a2 417
c84be134
AB
418;; (global-set-key (kbd "C-x k") #'b/kill-current-buffer)
419;; (global-set-key (kbd "C-x K") #'kill-buffer)
420;; (global-set-key (kbd "C-x s") #'save-buffer)
421;; (global-set-key (kbd "C-x S") #'save-some-buffers)
e5c2d147 422
c84be134 423(define-key emacs-lisp-mode-map (kbd "<C-return>") #'b/add-elisp-section)
41d290a2
AB
424
425(when (display-graphic-p)
c84be134 426 (global-unset-key (kbd "C-z")))
500004f4 427
b57457b2
AB
428\f
429;;; Essential packages
430
c84be134 431;; (require 'bandali-exwm)
33273849 432
f7910e3d 433(require 'bandali-org)
41d290a2 434
9c48decc
AB
435(require 'bandali-theme)
436
b57457b2 437;; *the* right way to do git
c84be134 438(with-eval-after-load 'magit
2087ae39
AB
439 (declare-function magit-add-section-hook "magit-section"
440 (hook function &optional at append local))
41d290a2
AB
441 (magit-add-section-hook 'magit-status-sections-hook
442 'magit-insert-modules
443 'magit-insert-stashes
444 'append)
3b3615f5
AB
445 ;; (magit-add-section-hook 'magit-status-sections-hook
446 ;; 'magit-insert-ignored-files
447 ;; 'magit-insert-untracked-files
448 ;; 'append)
c84be134
AB
449 (declare-function magit-display-buffer-fullframe-status-v1
450 "magit-mode" (buffer))
451 (csetq
452 magit-diff-refine-hunk t
453 magit-repository-directories '(("~/.emacs.d/" . 0)
454 ("~/src/git/" . 2))
455 ;; magit-completing-read-function 'magit-ido-completing-read
456 magit-display-buffer-function
457 #'magit-display-buffer-fullframe-status-v1)
41d290a2
AB
458 (nconc magit-section-initial-visibility-alist
459 '(([unpulled status] . show)
460 ([unpushed status] . show)))
c84be134
AB
461 (custom-set-faces '(magit-diff-file-heading ((t (:weight normal)))))
462
463 (with-eval-after-load 'magit-extras
464 (csetq
465 magit-pop-revision-stack-format
466 (pcase-let ((`(,pt ,_eob ,index-regexp)
467 (default-value 'magit-pop-revision-stack-format)))
468 `(,pt "[%N: %h]: %ci\n %s
7c558c9b 469 https://git.savannah.gnu.org/cgit/emacs.git/commit/?id=%H"
c84be134
AB
470 ,index-regexp)))))
471;; global key bindings
472(global-set-key (kbd "C-x g") #'magit-status)
473(global-set-key (kbd "C-c g b") #'magit-blame-addition)
474(global-set-key (kbd "C-c g l") #'magit-log-buffer-file)
475(global-set-key (kbd "C-c g y") #'magit-pop-revision-stack)
7c558c9b 476
b57457b2 477;; recently opened files
c84be134
AB
478(run-with-idle-timer 0.2 nil #'require 'recentf)
479(with-eval-after-load 'recentf
480 (csetq recentf-max-saved-items 2000)
481 (add-to-list 'recentf-keep #'file-remote-p)
482 (recentf-mode))
41d290a2 483
679463c6 484;; needed for history for counsel
c84be134
AB
485(run-with-idle-timer 0.3 nil #'require 'amx)
486(with-eval-after-load 'amx
6eb104ff
AB
487 (amx-mode))
488
679463c6
AB
489;; (require 'bandali-ido)
490(require 'bandali-ivy)
41d290a2 491
679463c6 492(require 'bandali-eshell)
1eb20313 493;; (require 'bandali-multi-term)
41d290a2 494
679463c6 495(require 'bandali-ibuffer)
41d290a2 496
c84be134
AB
497;; outline
498;; (with-eval-after-load 'outline
499;; (when (featurep 'which-key)
500;; (which-key-add-key-based-replacements
501;; "C-c @" "outline"
502;; "s-O" "outline"))
503;; (define-key outline-minor-mode-map (kbd "<s-tab>")
504;; #'outline-toggle-children)
505;; (define-key outline-minor-mode-map (kbd "M-p")
506;; #'outline-previous-visible-heading)
507;; (define-key outline-minor-mode-map (kbd "M-n")
508;; #'outline-next-visible-heading)
509;; (defvar b/outline-prefix-map)
510;; (define-prefix-command 'b/outline-prefix-map)
511;; (define-key outline-minor-mode-map (kbd "s-O")
512;; 'b/outline-prefix-map)
513;; (define-key b/outline-prefix-map (kbd "TAB")
514;; #'outline-toggle-children)
515;; (define-key b/outline-prefix-map (kbd "a")
516;; #'outline-hide-body)
517;; (define-key b/outline-prefix-map (kbd "H")
518;; #'outline-hide-body)
519;; (define-key b/outline-prefix-map (kbd "S")
520;; #'outline-show-all)
521;; (define-key b/outline-prefix-map (kbd "h")
522;; #'outline-hide-subtree)
523;; (define-key b/outline-prefix-map (kbd "s")
524;; #'outline-show-subtree))
525;; (add-hook 'prog-mode-hook #'outline-minor-mode)
41d290a2 526
679463c6 527(require 'bandali-dired)
41d290a2 528
c84be134 529(with-eval-after-load 'help
41d290a2 530 (temp-buffer-resize-mode)
c84be134 531 (csetq help-window-select t)
41d290a2 532
c84be134
AB
533 ;; local key bindings
534 (define-key help-mode-map (kbd "p") #'backward-button)
535 (define-key help-mode-map (kbd "n") #'forward-button))
536
537(with-eval-after-load 'tramp
41d290a2
AB
538 (add-to-list 'tramp-default-proxies-alist '(nil "\\`root\\'" "/ssh:%h:"))
539 (add-to-list 'tramp-default-proxies-alist '("localhost" nil nil))
540 (add-to-list 'tramp-default-proxies-alist
541 (list (regexp-quote (system-name)) nil nil)))
542
c84be134
AB
543(with-eval-after-load 'doc-view
544 (define-key doc-view-mode-map (kbd "M-RET") #'image-previous-line))
41d290a2 545
c84be134 546(csetq shr-max-width 80)
7c558c9b 547
e2178fd3 548;; Email (with Gnus, message, and EBDB)
2087ae39 549(require 'bandali-gnus)
c84be134
AB
550(with-eval-after-load 'sendmail
551 (csetq sendmail-program (executable-find "msmtp")
552 ;; message-sendmail-extra-arguments '("-v" "-d")
553 mail-specify-envelope-from t
554 mail-envelope-from 'header))
2087ae39 555(require 'bandali-message)
e2178fd3 556(require 'bandali-ebdb)
2087ae39 557
39c1c073
AB
558;; IRC (with ERC and ZNC)
559(require 'bandali-erc)
560
c84be134
AB
561(with-eval-after-load 'scpaste
562 (csetq scpaste-http-destination "https://p.bndl.org"
563 scpaste-scp-destination "p:~"))
2331b5a0 564
b57457b2
AB
565\f
566;;; Editing
5750405c 567
b57457b2 568;; display Lisp objects at point in the echo area
c84be134
AB
569(when (version< "25" emacs-version)
570 (with-eval-after-load 'eldoc
571 (global-eldoc-mode)))
41d290a2 572
b57457b2 573;; highlight matching parens
c84be134
AB
574(require 'paren)
575(show-paren-mode)
576
577;; (require 'elec-pair)
578;; (electric-pair-mode)
579
580(csetq
581 ;; Save what I copy into clipboard from other applications into Emacs'
582 ;; kill-ring, which would allow me to still be able to easily access
583 ;; it in case I kill (cut or copy) something else inside Emacs before
584 ;; yanking (pasting) what I'd originally intended to.
585 save-interprogram-paste-before-kill t)
586(with-eval-after-load 'simple
587 (column-number-mode))
41d290a2 588
b57457b2 589;; save minibuffer history
c84be134
AB
590(require 'savehist)
591(savehist-mode)
592(add-to-list 'savehist-additional-variables 'kill-ring)
41d290a2 593
b57457b2 594;; automatically save place in files
c84be134
AB
595(when (version< "25" emacs-version)
596 (save-place-mode))
597
598(defun indicate-buffer-boundaries-left ()
599 (csetq indicate-buffer-boundaries 'left))
600(with-eval-after-load 'prog-mode
601 (global-prettify-symbols-mode))
602(add-hook 'prog-mode-hook #'indicate-buffer-boundaries-left)
603
604(define-key text-mode-map (kbd "C-*") #'b/insert-asterism)
605(add-hook 'text-mode-hook #'indicate-buffer-boundaries-left)
606(add-hook 'text-mode-hook #'flyspell-mode)
607
608(add-to-list 'auto-mode-alist '("\\.*rc$" . conf-mode))
609
610(add-to-list 'auto-mode-alist '("\\.bashrc$" . sh-mode))
611
612;; flycheck
613;; (run-with-idle-timer 0.6 nil #'require 'flycheck)
614;; (with-eval-after-load 'flycheck
615;; (csetq
616;; ;; Use the load-path from running Emacs when checking elisp files
617;; flycheck-emacs-lisp-load-path 'inherit
618;; ;; Only flycheck when I actually save the buffer
619;; flycheck-check-syntax-automatically '(mode-enabled save)
620;; flycheck-mode-line-prefix "flyc"))
621;; (define-key flycheck-mode-map (kbd "M-P") #'flycheck-previous-error)
622;; (define-key flycheck-mode-map (kbd "M-N") #'flycheck-next-error)
623;; (add-hook 'prog-mode-hook #'flycheck-mode)
624
625;; ispell
41d290a2 626;; http://endlessparentheses.com/ispell-and-apostrophes.html
c84be134
AB
627;; (run-with-idle-timer 0.6 nil #'require 'ispell)
628;; (with-eval-after-load 'ispell
629;; ;; ’ can be part of a word
630;; (csetq ispell-local-dictionary-alist
631;; `((nil "[[:alpha:]]" "[^[:alpha:]]"
632;; "['\x2019]" nil ("-B") nil utf-8))
633;; ispell-program-name (executable-find "hunspell"))
634;; ;; don't send ’ to the subprocess
635;; (defun endless/replace-apostrophe (args)
636;; (cons (replace-regexp-in-string
637;; "’" "'" (car args))
638;; (cdr args)))
639;; (advice-add #'ispell-send-string :filter-args
640;; #'endless/replace-apostrophe)
641;; ;; convert ' back to ’ from the subprocess
642;; (defun endless/replace-quote (args)
643;; (if (not (derived-mode-p 'org-mode))
644;; args
645;; (cons (replace-regexp-in-string
646;; "'" "’" (car args))
647;; (cdr args))))
648;; (advice-add #'ispell-parse-output :filter-args
649;; #'endless/replace-quote))
650
651(add-hook 'text-mode-hook #'abbrev-mode)
54209e74 652
b57457b2
AB
653\f
654;;; Programming modes
655
c84be134 656(with-eval-after-load 'lisp-mode
41d290a2
AB
657 (defun indent-spaces-mode ()
658 (setq indent-tabs-mode nil))
659 (add-hook 'lisp-interaction-mode-hook #'indent-spaces-mode))
660
c84be134
AB
661(with-eval-after-load 'alloy-mode
662 (csetq alloy-basic-offset 2)
fac1032c
AB
663 ;; (defun b/alloy-simple-indent (start end)
664 ;; (interactive "r")
665 ;; ;; (if (region-active-p)
666 ;; ;; (indent-rigidly start end alloy-basic-offset)
667 ;; ;; (if (bolp)
668 ;; ;; (indent-rigidly (line-beginning-position)
669 ;; ;; (line-end-position)
670 ;; ;; alloy-basic-offset)))
671 ;; (indent-to (+ (current-column) alloy-basic-offset)))
c84be134
AB
672 ;; local key bindings
673 (define-key alloy-mode-map (kbd "RET") #'electric-newline-and-maybe-indent)
674 ;; (define-key alloy-mode-map (kbd "TAB") #'b/alloy-simple-indent)
675 (define-key alloy-mode-map (kbd "TAB") #'indent-for-tab-command))
676(add-to-list 'auto-mode-alist '("\\.\\(als\\|dsh\\)\\'" . alloy-mode))
677(add-hook 'alloy-mode-hook (lambda nil (setq-local indent-tabs-mode nil)))
678
679;; lean
680;; (eval-when-compile (defvar lean-mode-map))
681;; (run-with-idle-timer 0.4 nil #'require 'lean-mode)
682;; (with-eval-after-load 'lean-mode
683;; (require 'lean-input)
684;; (csetq default-input-method "Lean"
685;; lean-input-tweak-all '(lean-input-compose
686;; (lean-input-prepend "/")
687;; (lean-input-nonempty))
688;; lean-input-user-translations '(("/" "/")))
689;; (lean-input-setup)
690;; ;; local key bindings
691;; (define-key lean-mode-map (kbd "S-SPC") #'company-complete))
692
693(with-eval-after-load 'sgml-mode
694 (csetq sgml-basic-offset 0))
695
696(with-eval-after-load 'css-mode
697 (csetq css-indent-offset 2))
698
699;; po-mode
700;; (add-hook 'po-mode-hook (lambda nil (run-with-timer 0.1 nil 'View-exit)))
701
702;; auctex
703;; (csetq font-latex-fontify-sectioning 'color)
704
705(with-eval-after-load 'tex-mode
748bd8ac
AB
706 (cl-delete-if
707 (lambda (p) (string-match "^---?" (car p)))
c84be134
AB
708 tex--prettify-symbols-alist))
709(add-hook 'tex-mode-hook #'auto-fill-mode)
710(add-hook 'tex-mode-hook #'flyspell-mode)
1d01c927 711
b57457b2 712\f
b57457b2 713;;; Emacs enhancements & auxiliary packages
1eb20313 714
c84be134
AB
715(with-eval-after-load 'man
716 (csetq Man-width 80))
717
718(run-with-idle-timer 0.4 nil #'require 'which-key)
719(with-eval-after-load 'which-key
720 (csetq
721 which-key-add-column-padding 5
722 which-key-idle-delay 10000
723 which-key-idle-secondary-delay 0.05
724 which-key-max-description-length 32
725 which-key-show-early-on-C-h t)
41d290a2
AB
726 (which-key-add-key-based-replacements
727 ;; prefixes for global prefixes and minor modes
41d290a2 728 "C-c !" "flycheck"
b549b760 729 "C-x RET" "coding system"
41d290a2 730 "C-x 8" "unicode"
b549b760 731 "C-x @" "event modifiers"
41d290a2
AB
732 "C-x a" "abbrev/expand"
733 "C-x r" "rectangle/register/bookmark"
b549b760 734 "C-x t" "tabs"
41d290a2 735 "C-x v" "version control"
b549b760
AB
736 "C-x X" "edebug"
737 "C-x C-a" "edebug"
738 "C-x C-k" "kmacro"
41d290a2 739 ;; prefixes for my personal bindings
b549b760 740 "C-c &" "yasnippet"
41d290a2
AB
741 "C-c a" "applications"
742 "C-c a e" "erc"
743 "C-c a o" "org"
744 "C-c a s" "shells"
2e81c51a 745 "C-c b" "buffers"
41d290a2
AB
746 "C-c c" "compile-and-comments"
747 "C-c e" "eval"
748 "C-c f" "files"
749 "C-c F" "frames"
ef6c487c 750 "C-c g" "magit"
41d290a2 751 "C-S-h" "help(ful)"
41d290a2 752 "C-c q" "boxquote"
1eb20313 753 "C-c t" "themes")
41d290a2 754 ;; prefixes for major modes
41d290a2
AB
755 (which-key-add-major-mode-key-based-replacements 'org-mode
756 "C-c C-v" "org-babel")
c84be134 757 (which-key-mode))
41d290a2 758
1eb20313 759;; (require 'bandali-projectile)
41d290a2 760
c84be134
AB
761(run-with-idle-timer 0.6 nil #'require 'unkillable-scratch)
762(with-eval-after-load 'unkillable-scratch
763 (csetq unkillable-buffers '("^\\*scratch\\*$" "^\\*Messages\\*$"))
764 (unkillable-scratch 1))
41d290a2 765
5b10d879
AB
766;; ,----
767;; | make pretty boxed quotes like this
768;; `----
c84be134
AB
769(run-with-idle-timer 0.6 nil #'require 'boxquote)
770(with-eval-after-load 'boxquote
771 (defvar b/boxquote-prefix-map)
772 (define-prefix-command 'b/boxquote-prefix-map)
773 (global-set-key (kbd "C-c q") 'b/boxquote-prefix-map)
774 (define-key b/boxquote-prefix-map (kbd "b") #'boxquote-buffer)
775 (define-key b/boxquote-prefix-map (kbd "B") #'boxquote-insert-buffer)
776 (define-key b/boxquote-prefix-map (kbd "d") #'boxquote-defun)
777 (define-key b/boxquote-prefix-map (kbd "F") #'boxquote-insert-file)
778 (define-key b/boxquote-prefix-map (kbd "hf") #'boxquote-describe-function)
779 (define-key b/boxquote-prefix-map (kbd "hk") #'boxquote-describe-key)
780 (define-key b/boxquote-prefix-map (kbd "hv") #'boxquote-describe-variable)
781 (define-key b/boxquote-prefix-map (kbd "hw") #'boxquote-where-is)
782 (define-key b/boxquote-prefix-map (kbd "k") #'boxquote-kill)
783 (define-key b/boxquote-prefix-map (kbd "p") #'boxquote-paragraph)
784 (define-key b/boxquote-prefix-map (kbd "q") #'boxquote-boxquote)
785 (define-key b/boxquote-prefix-map (kbd "r") #'boxquote-region)
786 (define-key b/boxquote-prefix-map (kbd "s") #'boxquote-shell-command)
787 (define-key b/boxquote-prefix-map (kbd "t") #'boxquote-text)
788 (define-key b/boxquote-prefix-map (kbd "T") #'boxquote-title)
789 (define-key b/boxquote-prefix-map (kbd "u") #'boxquote-unbox)
790 (define-key b/boxquote-prefix-map (kbd "U") #'boxquote-unbox-region)
791 (define-key b/boxquote-prefix-map (kbd "y") #'boxquote-yank)
792 (define-key b/boxquote-prefix-map (kbd "M-q") #'boxquote-fill-paragraph)
793 (define-key b/boxquote-prefix-map (kbd "M-w") #'boxquote-kill-ring-save))
794
795(run-with-idle-timer 0.5 nil #'require 'hl-todo)
796(with-eval-after-load 'hl-todo
1eb20313 797 ;; highlight TODOs in buffers
41d290a2
AB
798 (global-hl-todo-mode))
799
c84be134
AB
800(run-with-idle-timer 0.5 nil #'require 'page-break-lines)
801(with-eval-after-load 'page-break-lines
802 (csetq page-break-lines-max-width fill-column)
41d290a2
AB
803 (global-page-break-lines-mode))
804
c84be134
AB
805;; expand-region
806(global-set-key (kbd "C-=") #'er/expand-region)
807
808(run-with-idle-timer 0.6 nil #'require 'yasnippet)
809(with-eval-after-load 'yasnippet
810 (declare-function yas-reload-all
811 "yasnippet" (&optional no-jit interactive))
812 (declare-function yas-maybe-expand-abbrev-key-filter
813 "yasnippet" (cmd))
814
815 (defconst yas-verbosity-cur yas-verbosity)
816 (setq yas-verbosity 2)
817 (add-to-list 'yas-snippet-dirs "~/src/git/guix/etc/snippets" t)
818 (yas-reload-all)
819 (setq yas-verbosity yas-verbosity-cur)
820
821 (defun b/yas-maybe-expand-abbrev-key-filter (cmd)
822 (when (and (yas-maybe-expand-abbrev-key-filter cmd)
823 (not (bound-and-true-p git-commit-mode)))
824 cmd))
825 (defconst b/yas-maybe-expand
826 '(menu-item "" yas-expand
827 :filter b/yas-maybe-expand-abbrev-key-filter))
828 (define-key yas-minor-mode-map (kbd "SPC") b/yas-maybe-expand)
829
830 (yas-global-mode))
831
832;; debbugs
833(global-set-key (kbd "C-c D d") #'debbugs-gnu)
834(global-set-key (kbd "C-c D b") #'debbugs-gnu-bugs)
835(global-set-key (kbd "C-c D e") ; bug-gnu-emacs
836 (lambda ()
837 (interactive)
838 (setq debbugs-gnu-current-suppress t)
839 (debbugs-gnu debbugs-gnu-default-severities
840 '("emacs"))))
841(global-set-key (kbd "C-c D g") ; bug-gnuzilla
842 (lambda ()
843 (interactive)
844 (setq debbugs-gnu-current-suppress t)
845 (debbugs-gnu debbugs-gnu-default-severities
846 '("gnuzilla"))))
847(global-set-key (kbd "C-c D G b") ; bug-guix
848 (lambda ()
849 (interactive)
850 (setq debbugs-gnu-current-suppress t)
851 (debbugs-gnu debbugs-gnu-default-severities
852 '("guix"))))
853(global-set-key (kbd "C-c D G p") ; guix-patches
854 (lambda ()
855 (interactive)
856 (setq debbugs-gnu-current-suppress t)
857 (debbugs-gnu debbugs-gnu-default-severities
858 '("guix-patches"))))
859
860;; eww
861(csetq eww-download-directory (file-name-as-directory
862 (getenv "XDG_DOWNLOAD_DIR")))
863(global-set-key (kbd "C-c a e w") #'eww)
41d290a2 864
f0a2dfe1
AB
865(comment
866
c84be134
AB
867;; org-ref
868(csetq
869 reftex-default-bibliography '("~/usr/org/references.bib")
870 org-ref-default-bibliography '("~/usr/org/references.bib")
871 org-ref-bibliography-notes "~/usr/org/notes.org"
872 org-ref-pdf-directory "~/usr/org/bibtex-pdfs/")
873
874;; fill-column-indicator ?
875
876;; window
877(csetq split-width-threshold 150)
878(global-set-key (kbd "C-c w s l")
879 (lambda ()
880 (interactive)
881 (split-window-right)
882 (other-window 1)))
883(global-set-key (kbd "C-c w s j")
884 (lambda ()
885 (interactive)
886 (split-window-below)
887 (other-window 1)))
888(global-set-key (kbd "C-c w q") #'quit-window)
889
890(run-with-idle-timer 0.6 nil #'require 'windmove)
891(global-set-key (kbd "C-c w h") #'windmove-left)
892(global-set-key (kbd "C-c w j") #'windmove-down)
893(global-set-key (kbd "C-c w k") #'windmove-up)
894(global-set-key (kbd "C-c w l") #'windmove-right)
895(global-set-key (kbd "C-c w H") #'windmove-swap-states-left)
896(global-set-key (kbd "C-c w J") #'windmove-swap-states-down)
897(global-set-key (kbd "C-c w K") #'windmove-swap-states-up)
898(global-set-key (kbd "C-c w L") #'windmove-swap-states-right)
899
900;; pass
901(global-set-key (kbd "C-c a p") #'pass)
902(add-hook 'pass-mode-hook #'View-exit)
903
904;; reftex
905;; uncomment to disable reftex-cite's default choice of previous word
906;; (with-eval-after-load 'reftex
907;; (require 'reftex-cite)
908;; (defun reftex-get-bibkey-default ()
909;; "If the cursor is in a citation macro, return the word before the macro."
910;; (let* ((macro (reftex-what-macro 1)))
911;; (save-excursion
912;; (when (and macro (string-match "cite" (car macro)))
913;; (goto-char (cdr macro)))
914;; (reftex-this-word)))))
915(add-hook 'latex-mode-hook #'reftex-mode)
916
917;; dmenu
918(csetq
919 dmenu-prompt-string "run: "
920 dmenu-save-file (b/var "dmenu-items"))
921
922;; eosd ?
9ed5410e 923
b57457b2 924\f
b57457b2
AB
925;;; Post initialization
926
8b1a2f32 927)
41d290a2
AB
928(message "Loading %s...done (%.3fs)" user-init-file
929 (float-time (time-subtract (current-time)
dca50cf5 930 b/before-user-init-time)))
41d290a2
AB
931
932;;; init.el ends here