5ba4c72278ae0d5edaf7a1751f7477e70316fc17
[~bandali/configs] / init.el
1 ;;; init.el --- bandali's emacs configuration -*- lexical-binding: t -*-
2
3 ;; Copyright (C) 2018-2020 Amin Bandali <bandali@gnu.org>
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
20 ;; GNU Emacs configuration of Amin Bandali, computer scientist,
21 ;; Free Software activist, and GNU maintainer & webmaster. Packages
22 ;; are installed through using Borg for a fully reproducible setup.
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
35
36 ;;; Code:
37
38 ;;; Emacs initialization
39
40 (defvar b/before-user-init-time (current-time)
41 "Value of `current-time' when Emacs begins loading `user-init-file'.")
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))))
49
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.
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)
56 (setq gc-cons-threshold (* 30 1024 1024) ; 30 MiB
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
62 ;; set them back to their defaults once we're done initializing
63 (defun b/post-init ()
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
68 file-name-handler-alist b/file-name-handler-alist)
69 (when (featurep 'exwm-workspace)
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)))))))))
79 (add-hook 'after-init-hook #'b/post-init)
80
81 ;; increase number of lines kept in *Messages* log
82 (setq message-log-max 20000)
83
84 ;; optionally, uncomment to supress some byte-compiler warnings
85 ;; (see C-h v byte-compile-warnings RET for more info)
86 ;; (setq byte-compile-warnings
87 ;; '(not free-vars unresolved noruntime lexical make-local))
88
89 \f
90 ;;; whoami
91
92 (setq user-full-name "Amin Bandali"
93 user-mail-address "bandali@gnu.org")
94
95 \f
96 ;;; Package management
97
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/"))))
106
107 (defmacro csetq (&rest args)
108 "Set the value of user option VAR to VALUE.
109
110 More generally, you can use multiple variables and values, as in
111 (csetq VAR VALUE VAR VALUE...)
112 This 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))))
121
122 \f
123 ;;; Initial setup
124
125 ;; keep ~/.emacs.d clean
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)
138
139 ;; separate custom file (don't want it mixing with init.el)
140 (with-eval-after-load 'custom
141 (setq custom-file (b/etc "custom.el"))
142 (when (file-exists-p custom-file)
143 (load custom-file))
144 ;; while at it, treat themes as safe
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))))
151
152 ;; load the secrets file if it exists, otherwise show a warning
153 (comment
154 (with-demoted-errors
155 (load (b/etc "secrets"))))
156
157 ;; start up emacs server. see
158 ;; https://www.gnu.org/software/emacs/manual/html_node/emacs/Emacs-Server.html#Emacs-Server
159 (run-with-idle-timer 0.5 nil #'require 'server)
160 (with-eval-after-load 'server
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)))
165
166 \f
167 ;;; Defaults
168
169 ;;;; C-level customizations
170
171 (csetq
172 ;; minibuffer
173 enable-recursive-minibuffers t
174 resize-mini-windows t
175 ;; more useful frame titles
176 ;; frame-title-format '("" invocation-name " - "
177 ;; (:eval
178 ;; (if (buffer-file-name)
179 ;; (abbreviate-file-name (buffer-file-name))
180 ;; "%b")))
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
188 scroll-conservatively 101
189 scroll-preserve-screen-position 1
190 ;; focus follows mouse
191 mouse-autoselect-window t)
192
193 (setq-default
194 ;; always use space for indentation
195 indent-tabs-mode nil
196 tab-width 4
197 ;; case-sensitive search (and `dabbrev-expand')
198 ;; case-fold-search nil
199 ;; cursor shape
200 cursor-type t)
201
202 (set-fontset-font t 'arabic "Vazir")
203
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
237 ;;;; Elisp-level customizations
238
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
270 ;; disable disabled commands
271 (csetq disabled-command-function nil)
272
273 ;; lazy-person-friendly yes/no prompts
274 (defalias 'yes-or-no-p #'y-or-n-p)
275
276 ;; enable automatic reloading of changed buffers and files
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))
282
283 ;; time and battery in mode-line
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
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'.
311 (defun b/compilation-finish-function (buffer outstr)
312 (unless (string-match "finished" outstr)
313 (switch-to-buffer-other-window buffer))
314 t)
315
316 (setq compilation-finish-functions #'b/compilation-finish-function)
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
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
340 ;; uncomment to extend the above behaviour to query-replace
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")))
384
385 \f
386 ;;; General key bindings
387
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)
393
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)
399
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)
404
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)
409
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)
413
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)
417
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)
422
423 (define-key emacs-lisp-mode-map (kbd "<C-return>") #'b/add-elisp-section)
424
425 (when (display-graphic-p)
426 (global-unset-key (kbd "C-z")))
427
428 \f
429 ;;; Essential packages
430
431 ;; (require 'bandali-exwm)
432
433 (require 'bandali-org)
434
435 (require 'bandali-theme)
436
437 ;; *the* right way to do git
438 (with-eval-after-load 'magit
439 (declare-function magit-add-section-hook "magit-section"
440 (hook function &optional at append local))
441 (magit-add-section-hook 'magit-status-sections-hook
442 'magit-insert-modules
443 'magit-insert-stashes
444 'append)
445 ;; (magit-add-section-hook 'magit-status-sections-hook
446 ;; 'magit-insert-ignored-files
447 ;; 'magit-insert-untracked-files
448 ;; 'append)
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)
458 (nconc magit-section-initial-visibility-alist
459 '(([unpulled status] . show)
460 ([unpushed status] . show)))
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
469 https://git.savannah.gnu.org/cgit/emacs.git/commit/?id=%H"
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)
476
477 ;; recently opened files
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))
483
484 ;; needed for history for counsel
485 (run-with-idle-timer 0.3 nil #'require 'amx)
486 (with-eval-after-load 'amx
487 (amx-mode))
488
489 ;; (require 'bandali-ido)
490 (require 'bandali-ivy)
491
492 (require 'bandali-eshell)
493 ;; (require 'bandali-multi-term)
494
495 (require 'bandali-ibuffer)
496
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)
526
527 (require 'bandali-dired)
528
529 (with-eval-after-load 'help
530 (temp-buffer-resize-mode)
531 (csetq help-window-select t)
532
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
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
543 (with-eval-after-load 'doc-view
544 (define-key doc-view-mode-map (kbd "M-RET") #'image-previous-line))
545
546 (csetq shr-max-width 80)
547
548 ;; Email (with Gnus, message, and EBDB)
549 (require 'bandali-gnus)
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))
555 (require 'bandali-message)
556 (require 'bandali-ebdb)
557
558 ;; IRC (with ERC and ZNC)
559 (require 'bandali-erc)
560
561 (with-eval-after-load 'scpaste
562 (csetq scpaste-http-destination "https://p.bndl.org"
563 scpaste-scp-destination "p:~"))
564
565 \f
566 ;;; Editing
567
568 ;; display Lisp objects at point in the echo area
569 (when (version< "25" emacs-version)
570 (with-eval-after-load 'eldoc
571 (global-eldoc-mode)))
572
573 ;; highlight matching parens
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))
588
589 ;; save minibuffer history
590 (require 'savehist)
591 (savehist-mode)
592 (add-to-list 'savehist-additional-variables 'kill-ring)
593
594 ;; automatically save place in files
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
626 ;; http://endlessparentheses.com/ispell-and-apostrophes.html
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)
652
653 \f
654 ;;; Programming modes
655
656 (with-eval-after-load 'lisp-mode
657 (defun indent-spaces-mode ()
658 (setq indent-tabs-mode nil))
659 (add-hook 'lisp-interaction-mode-hook #'indent-spaces-mode))
660
661 (with-eval-after-load 'alloy-mode
662 (csetq alloy-basic-offset 2)
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)))
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
706 (cl-delete-if
707 (lambda (p) (string-match "^---?" (car p)))
708 tex--prettify-symbols-alist))
709 (add-hook 'tex-mode-hook #'auto-fill-mode)
710 (add-hook 'tex-mode-hook #'flyspell-mode)
711
712 \f
713 ;;; Emacs enhancements & auxiliary packages
714
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)
726 (which-key-add-key-based-replacements
727 ;; prefixes for global prefixes and minor modes
728 "C-c !" "flycheck"
729 "C-x RET" "coding system"
730 "C-x 8" "unicode"
731 "C-x @" "event modifiers"
732 "C-x a" "abbrev/expand"
733 "C-x r" "rectangle/register/bookmark"
734 "C-x t" "tabs"
735 "C-x v" "version control"
736 "C-x X" "edebug"
737 "C-x C-a" "edebug"
738 "C-x C-k" "kmacro"
739 ;; prefixes for my personal bindings
740 "C-c &" "yasnippet"
741 "C-c a" "applications"
742 "C-c a e" "erc"
743 "C-c a o" "org"
744 "C-c a s" "shells"
745 "C-c b" "buffers"
746 "C-c c" "compile-and-comments"
747 "C-c e" "eval"
748 "C-c f" "files"
749 "C-c F" "frames"
750 "C-c g" "magit"
751 "C-S-h" "help(ful)"
752 "C-c q" "boxquote"
753 "C-c t" "themes")
754 ;; prefixes for major modes
755 (which-key-add-major-mode-key-based-replacements 'org-mode
756 "C-c C-v" "org-babel")
757 (which-key-mode))
758
759 ;; (require 'bandali-projectile)
760
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))
765
766 ;; ,----
767 ;; | make pretty boxed quotes like this
768 ;; `----
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
797 ;; highlight TODOs in buffers
798 (global-hl-todo-mode))
799
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)
803 (global-page-break-lines-mode))
804
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)
864
865 (comment
866
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 ?
923
924 \f
925 ;;; Post initialization
926
927 )
928 (message "Loading %s...done (%.3fs)" user-init-file
929 (float-time (time-subtract (current-time)
930 b/before-user-init-time)))
931
932 ;;; init.el ends here