787d386e843fecf583931ba39ee84da967ccfef5
[~bandali/configs] / init.org
1 #+title: =aminb='s Literate Emacs Configuration
2 #+author: Amin Bandali
3 #+babel: :cache yes
4 #+property: header-args :tangle yes
5
6 * About
7 :PROPERTIES:
8 :CUSTOM_ID: about
9 :END:
10
11 This org file is my literate configuration for GNU Emacs, and is
12 tangled to [[./init.el][init.el]]. Packages are installed and managed using
13 [[https://github.com/emacscollective/borg][Borg]]. Over the years, I've taken inspiration from configurations of
14 many different people. Some of the configurations that I can remember
15 off the top of my head are:
16
17 - [[https://github.com/dieggsy/dotfiles][dieggsy/dotfiles]]: literate Emacs and dotfiles configuration, uses
18 straight.el for managing packages
19 - [[https://github.com/dakra/dmacs][dakra/dmacs]]: literate Emacs configuration, using Borg for managing
20 packages
21 - [[http://pages.sachachua.com/.emacs.d/Sacha.html][Sacha Chua's literate Emacs configuration]]
22 - [[https://github.com/dakrone/eos][dakrone/eos]]
23 - Ryan Rix's [[http://doc.rix.si/cce/cce.html][Complete Computing Environment]] ([[http://doc.rix.si/projects/fsem.html][about cce]])
24 - [[https://github.com/jwiegley/dot-emacs][jwiegley/dot-emacs]]: nix-based configuration
25 - [[https://github.com/wasamasa/dotemacs][wasamasa/dotemacs]]
26 - [[https://github.com/hlissner/doom-emacs][Doom Emacs]]
27
28 I'd like to have a fully reproducible Emacs setup (part of the reason
29 why I store my configuration in this repository) but unfortunately out
30 of the box, that's not achievable with =package.el=, not currently
31 anyway. So, I've opted to use Borg. For what it's worth, I briefly
32 experimented with [[https://github.com/raxod502/straight.el][straight.el]], but found that it added about 2 seconds
33 to my init time; which is unacceptable for me: I use Emacs as my
34 window manager (via EXWM) and coming from bspwm, I'm too used to
35 having fast startup times.
36
37 ** Installation
38
39 To use this config for your Emacs, first you need to clone this repo,
40 then bootstrap Borg, tell Borg to retrieve package submodules, and
41 byte-compiled the packages. Something along these lines should work:
42
43 #+begin_src sh :tangle no
44 git clone https://github.com/aminb/dotfiles ~/.emacs.d
45 cd ~/.emacs.d
46 make bootstrap-borg
47 make bootstrap
48 make build
49 #+end_src
50
51 * Contents :toc_1:noexport:
52
53 - [[#about][About]]
54 - [[#header][Header]]
55 - [[#initial-setup][Initial setup]]
56 - [[#core][Core]]
57 - [[#post-initialization][Post initialization]]
58 - [[#footer][Footer]]
59
60 * Header
61 :PROPERTIES:
62 :CUSTOM_ID: header
63 :END:
64
65 ** First line
66
67 #+begin_src emacs-lisp :comments none
68 ;;; init.el --- Amin Bandali's Emacs config -*- lexical-binding: t; eval: (view-mode 1) -*-
69 #+end_src
70
71 Enable =view-mode=, which both makes the file read-only (as a reminder
72 that =init.el= is an auto-generated file, not supposed to be edited),
73 and provides some convenient key bindings for browsing through the
74 file.
75
76 ** License
77
78 #+begin_src emacs-lisp :comments none
79 ;; Copyright (C) 2018 Amin Bandali <bandali@gnu.org>
80
81 ;; This program is free software: you can redistribute it and/or modify
82 ;; it under the terms of the GNU General Public License as published by
83 ;; the Free Software Foundation, either version 3 of the License, or
84 ;; (at your option) any later version.
85
86 ;; This program is distributed in the hope that it will be useful,
87 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
88 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
89 ;; GNU General Public License for more details.
90
91 ;; You should have received a copy of the GNU General Public License
92 ;; along with this program. If not, see <https://www.gnu.org/licenses/>.
93 #+end_src
94
95 ** Commentary
96
97 #+begin_src emacs-lisp :comments none
98 ;;; Commentary:
99
100 ;; Emacs configuration of Amin Bandali, computer scientist and functional
101 ;; programmer.
102
103 ;; THIS FILE IS AUTO-GENERATED FROM `init.org'.
104 #+end_src
105
106 ** Naming conventions
107
108 The conventions below were inspired by [[https://github.com/hlissner/doom-emacs][Doom]]'s, found [[https://github.com/hlissner/doom-emacs/blob/5dacbb7cb1c6ac246a9ccd15e6c4290def67757c/core/core.el#L3-L17][here]].
109
110 #+begin_src emacs-lisp :comments none
111 ;; Naming conventions:
112 ;;
113 ;; amin-... public variables or non-interactive functions
114 ;; amin--... private anything (non-interactive), not safe for direct use
115 ;; amin/... an interactive function; safe for M-x or keybinding
116 ;; amin|... a hook function
117 ;; amin*... an advising function
118 ;; amin@... a hydra command
119 ;; ...! a macro
120 #+end_src
121
122 * Initial setup
123 :PROPERTIES:
124 :CUSTOM_ID: initial-setup
125 :END:
126
127 ** Emacs initialization
128
129 I'd like to do a couple of measurements of Emacs' startup time. First,
130 let's see how long Emacs takes to start up, before even loading
131 =init.el=, i.e. =user-init-file=:
132
133 #+begin_src emacs-lisp
134 (defvar amin--before-user-init-time (current-time)
135 "Value of `current-time' when Emacs begins loading `user-init-file'.")
136 (message "Loading Emacs...done (%.3fs)"
137 (float-time (time-subtract amin--before-user-init-time
138 before-init-time)))
139 #+end_src
140
141 Also, temporarily increase ~gc-cons-threshhold~ and
142 ~gc-cons-percentage~ during startup to reduce garbage collection
143 frequency. Clearing the ~file-name-handler-alist~ seems to help reduce
144 startup time as well.
145
146 #+begin_src emacs-lisp
147 (defvar amin--gc-cons-threshold gc-cons-threshold)
148 (defvar amin--gc-cons-percentage gc-cons-percentage)
149 (defvar amin--file-name-handler-alist file-name-handler-alist)
150 (setq gc-cons-threshold (* 400 1024 1024) ; 400 MiB
151 gc-cons-percentage 0.6
152 file-name-handler-alist nil
153 ;; sidesteps a bug when profiling with esup
154 esup-child-profile-require-level 0)
155 #+end_src
156
157 Of course, we'd like to set them back to their defaults once we're
158 done initializing.
159
160 #+begin_src emacs-lisp
161 (add-hook
162 'after-init-hook
163 (lambda ()
164 (setq gc-cons-threshold amin--gc-cons-threshold
165 gc-cons-percentage amin--gc-cons-percentage
166 file-name-handler-alist amin--file-name-handler-alist)))
167 #+end_src
168
169 Increase the number of lines kept in message logs (the =*Messages*=
170 buffer).
171
172 #+begin_src emacs-lisp
173 (setq message-log-max 20000)
174 #+end_src
175
176 Optionally, we could suppress some byte compiler warnings like below,
177 but for now I've decided to keep them enabled. See documentation for
178 ~byte-compile-warnings~ for more details.
179
180 #+begin_src emacs-lisp
181 ;; (setq byte-compile-warnings
182 ;; '(not free-vars unresolved noruntime lexical make-local))
183 #+end_src
184
185 ** whoami
186
187 #+begin_src emacs-lisp
188 (setq user-full-name "Amin Bandali"
189 user-mail-address "amin@aminb.org")
190 #+end_src
191
192 ** Package management
193
194 *** No =package.el=
195
196 I can do all my package management things with Borg, and don't need
197 Emacs' built-in =package.el=. Emacs 27 lets us disable =package.el= in
198 the =early-init-file= (see [[https://git.savannah.gnu.org/cgit/emacs.git/commit/?id=24acb31c04b4048b85311d794e600ecd7ce60d3b][here]]).
199
200 #+begin_src emacs-lisp :tangle early-init.el
201 (setq package-enable-at-startup nil)
202 #+end_src
203
204 But since Emacs 27 isn't out yet (Emacs 26 is just around the corner
205 right now), and even when released it'll be long before most distros
206 ship in their repos, I'll still put the old workaround with the
207 commented call to ~package-initialize~ here anyway.
208
209 #+begin_src emacs-lisp
210 (setq package-enable-at-startup nil)
211 ;; (package-initialize)
212 #+end_src
213
214 *** Borg
215
216 #+begin_quote
217 Assimilate Emacs packages as Git submodules
218 #+end_quote
219
220 [[https://github.com/emacscollective/borg][Borg]] is at the heart of package management of my Emacs setup. In
221 short, it creates a git submodule in =lib/= for each package, which
222 can then be managed with the help of Magit or other tools.
223
224 #+begin_src emacs-lisp
225 (setq user-init-file (or load-file-name buffer-file-name)
226 user-emacs-directory (file-name-directory user-init-file))
227 (add-to-list 'load-path
228 (expand-file-name "lib/borg" user-emacs-directory))
229 (require 'borg)
230 (borg-initialize)
231
232 ;; (require 'borg-nix-shell)
233 ;; (setq borg-build-shell-command 'borg-nix-shell-build-command)
234
235 (with-eval-after-load 'bind-key
236 (bind-keys
237 :package borg
238 ("C-c b A" . borg-activate)
239 ("C-c b a" . borg-assimilate)
240 ("C-c b b" . borg-build)
241 ("C-c b c" . borg-clone)
242 ("C-c b r" . borg-remove)))
243 #+end_src
244
245 *** =use-package=
246
247 #+begin_quote
248 A use-package declaration for simplifying your .emacs
249 #+end_quote
250
251 [[https://github.com/jwiegley/use-package][use-package]] is an awesome utility for managing and configuring
252 packages (in our case especially the latter) in a neatly organized way
253 and without compromising on performance.
254
255 #+begin_src emacs-lisp
256 (require 'use-package)
257 (if nil ; set to t when need to debug init
258 (setq use-package-verbose t
259 use-package-expand-minimally nil
260 use-package-compute-statistics t
261 debug-on-error t)
262 (setq use-package-verbose nil
263 use-package-expand-minimally t))
264 #+end_src
265
266 *** Epkg
267
268 #+begin_quote
269 Browse the Emacsmirror package database
270 #+end_quote
271
272 Epkg provides access to a local copy of the [[https://emacsmirror.net][Emacsmirror]] package
273 database, low-level functions for querying the database, and a
274 =package.el=-like user interface for browsing the available packages.
275
276 #+begin_src emacs-lisp
277 (use-package epkg
278 :defer t
279 :bind
280 (("C-c b d" . epkg-describe-package)
281 ("C-c b p" . epkg-list-packages)
282 ("C-c b u" . epkg-update)))
283 #+end_src
284
285 ** No littering in =~/.emacs.d=
286
287 #+begin_quote
288 Help keeping ~/.emacs.d clean
289 #+end_quote
290
291 By default, even for Emacs' built-in packages, the configuration files
292 and persistent data are all over the place. Use =no-littering= to help
293 contain the mess.
294
295 #+begin_src emacs-lisp
296 (use-package no-littering
297 :demand t
298 :config
299 (savehist-mode 1)
300 (add-to-list 'savehist-additional-variables 'kill-ring)
301 (save-place-mode 1)
302 (setq auto-save-file-name-transforms
303 `((".*" ,(no-littering-expand-var-file-name "auto-save/") t))))
304 #+end_src
305
306 ** Custom file (=custom.el=)
307
308 I'm not planning on using the custom file much, but even so, I
309 definitely don't want it mixing with =init.el=. So, here; let's give
310 it it's own file. While at it, treat themes as safe.
311
312 #+begin_src emacs-lisp
313 (use-package custom
314 :no-require t
315 :config
316 (setq custom-file (no-littering-expand-etc-file-name "custom.el"))
317 (when (file-exists-p custom-file)
318 (load custom-file))
319 (setf custom-safe-themes t))
320 #+end_src
321
322 ** Secrets file
323
324 Load the secrets file if it exists, otherwise show a warning.
325
326 #+begin_src emacs-lisp
327 (with-demoted-errors
328 (load (no-littering-expand-etc-file-name "secrets")))
329 #+end_src
330
331 ** Better =$PATH= handling
332
333 Let's use [[https://github.com/purcell/exec-path-from-shell][exec-path-from-shell]] to make Emacs use the =$PATH= as set up
334 in my shell.
335
336 #+begin_src emacs-lisp
337 (use-package exec-path-from-shell
338 :defer 1
339 :init
340 (setq exec-path-from-shell-check-startup-files nil)
341 :config
342 (exec-path-from-shell-initialize)
343 ;; while we're at it, let's fix access to our running ssh-agent
344 (exec-path-from-shell-copy-env "SSH_AGENT_PID")
345 (exec-path-from-shell-copy-env "SSH_AUTH_SOCK"))
346 #+end_src
347
348 ** Only one custom theme at a time
349
350 #+begin_src emacs-lisp
351 ;; only one custom theme at a time
352 ;;
353 ;; (defadvice load-theme (before clear-previous-themes activate)
354 ;; "Clear existing theme settings instead of layering them"
355 ;; (mapc #'disable-theme custom-enabled-themes))
356 #+end_src
357
358 ** Server
359
360 Start server if not already running. Alternatively, can be done by
361 issuing =emacs --daemon= in the terminal, which can be automated with
362 a systemd service or using =brew services start emacs= on macOS. I use
363 Emacs as my window manager (via EXWM), so I always start Emacs on
364 login; so starting the server from inside Emacs is good enough for me.
365
366 See [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Emacs-Server.html#Emacs-Server][Using Emacs as a Server]].
367
368 #+begin_src emacs-lisp
369 (use-package server
370 :defer 1
371 :config (or (server-running-p) (server-mode)))
372 #+end_src
373
374 ** Unicode support
375
376 Font stack with better unicode support, around =Ubuntu Mono= and
377 =Hack=.
378
379 #+begin_src emacs-lisp
380 ;; unicode support
381 ;;
382 ;; (dolist (ft (fontset-list))
383 ;; (set-fontset-font
384 ;; ft
385 ;; 'unicode
386 ;; (font-spec :name "Source Code Pro" :size 14))
387 ;; (set-fontset-font
388 ;; ft
389 ;; 'unicode
390 ;; (font-spec :name "DejaVu Sans Mono")
391 ;; nil
392 ;; 'append)
393 ;; ;; (set-fontset-font
394 ;; ;; ft
395 ;; ;; 'unicode
396 ;; ;; (font-spec
397 ;; ;; :name "Symbola monospacified for DejaVu Sans Mono")
398 ;; ;; nil
399 ;; ;; 'append)
400 ;; ;; (set-fontset-font
401 ;; ;; ft
402 ;; ;; #x2115 ; ℕ
403 ;; ;; (font-spec :name "DejaVu Sans Mono")
404 ;; ;; nil
405 ;; ;; 'append)
406 ;; (set-fontset-font
407 ;; ft
408 ;; (cons ?Α ?ω)
409 ;; (font-spec :name "DejaVu Sans Mono" :size 14)
410 ;; nil
411 ;; 'prepend))
412 #+end_src
413
414 ** Gentler font resizing
415
416 #+begin_src emacs-lisp
417 (setq text-scale-mode-step 1.05)
418 #+end_src
419
420 ** Focus follows mouse
421
422 I’d like focus to follow the mouse when I move the cursor from one
423 window to the next.
424
425 #+begin_src emacs-lisp
426 (setq mouse-autoselect-window t)
427 #+end_src
428
429 Let’s define a function to conveniently disable this for certain
430 buffers and/or modes.
431
432 #+begin_src emacs-lisp
433 (defun amin--no-mouse-autoselect-window ()
434 (make-local-variable 'mouse-autoselect-window)
435 (setq mouse-autoselect-window nil))
436 #+end_src
437
438 ** Libraries
439
440 #+begin_src emacs-lisp
441 (require 'cl-lib)
442 (require 'subr-x)
443 #+end_src
444
445 ** Useful utilities
446
447 #+begin_src emacs-lisp
448 (defun amin-enlist (exp)
449 "Return EXP wrapped in a list, or as-is if already a list."
450 (if (listp exp) exp (list exp)))
451
452 ; from https://github.com/hlissner/doom-emacs/commit/589108fdb270f24a98ba6209f6955fe41530b3ef
453 (defmacro after! (features &rest body)
454 "A smart wrapper around `with-eval-after-load'. Supresses warnings during
455 compilation."
456 (declare (indent defun) (debug t))
457 (list (if (or (not (bound-and-true-p byte-compile-current-file))
458 (dolist (next (amin-enlist features))
459 (if (symbolp next)
460 (require next nil :no-error)
461 (load next :no-message :no-error))))
462 #'progn
463 #'with-no-warnings)
464 (cond ((symbolp features)
465 `(eval-after-load ',features '(progn ,@body)))
466 ((and (consp features)
467 (memq (car features) '(:or :any)))
468 `(progn
469 ,@(cl-loop for next in (cdr features)
470 collect `(after! ,next ,@body))))
471 ((and (consp features)
472 (memq (car features) '(:and :all)))
473 (dolist (next (cdr features))
474 (setq body `(after! ,next ,@body)))
475 body)
476 ((listp features)
477 `(after! (:all ,@features) ,@body)))))
478 #+end_src
479
480 Convenience macro for =setq='ing multiple variables to the same value:
481
482 #+begin_src emacs-lisp
483 (defmacro setq-every! (value &rest vars)
484 "Set all the variables from VARS to value VALUE."
485 (declare (indent defun) (debug t))
486 `(progn ,@(mapcar (lambda (x) (list 'setq x value)) vars)))
487 #+end_src
488
489 * Core
490 :PROPERTIES:
491 :CUSTOM_ID: core
492 :END:
493
494 ** Defaults
495
496 *** Time and battery in mode-line
497
498 Enable displaying time and battery in the mode-line, since I'm not
499 using the Xfce panel anymore. Also, I don't need to see the load
500 average on a regular basis, so disable that.
501
502 Note: using =i3status= on sway at the moment, so disabling this.
503
504 #+begin_src emacs-lisp :tangle no
505 (use-package time
506 :init
507 (setq display-time-default-load-average nil)
508 :config
509 (display-time-mode))
510
511 (use-package battery
512 :config
513 (display-battery-mode))
514 #+end_src
515
516 *** Smaller fringe
517
518 Might want to set the fringe to a smaller value, especially if using
519 EXWM. I'm fine with the default for now.
520
521 #+begin_src emacs-lisp
522 ;; (fringe-mode '(3 . 1))
523 (fringe-mode nil)
524 #+end_src
525
526 *** Disable disabled commands
527
528 Emacs disables some commands by default that could persumably be
529 confusing for novice users. Let's disable that.
530
531 #+begin_src emacs-lisp
532 (setq disabled-command-function nil)
533 #+end_src
534
535 *** Kill-ring
536
537 Save what I copy into clipboard from other applications into Emacs'
538 kill-ring, which would allow me to still be able to easily access it
539 in case I kill (cut or copy) something else inside Emacs before
540 yanking (pasting) what I'd originally intended to.
541
542 #+begin_src emacs-lisp
543 (setq save-interprogram-paste-before-kill t)
544 #+end_src
545
546 *** Minibuffer
547
548 #+begin_src emacs-lisp
549 (setq enable-recursive-minibuffers t
550 resize-mini-windows t)
551 #+end_src
552
553 *** Lazy-person-friendly yes/no prompts
554
555 Lazy people would prefer to type fewer keystrokes, especially for yes
556 or no questions. I'm lazy.
557
558 #+begin_src emacs-lisp
559 (defalias 'yes-or-no-p #'y-or-n-p)
560 #+end_src
561
562 *** Startup screen and =*scratch*=
563
564 Firstly, let Emacs know that I'd like to have =*scratch*= as my
565 startup buffer.
566
567 #+begin_src emacs-lisp
568 (setq initial-buffer-choice t)
569 #+end_src
570
571 Now let's customize the =*scratch*= buffer a bit. First off, I don't
572 need the default hint.
573
574 #+begin_src emacs-lisp
575 (setq initial-scratch-message nil)
576 #+end_src
577
578 Also, let's use Text mode as the major mode, in case I want to
579 customize it (=*scratch*='s default major mode, Fundamental mode,
580 can't really be customized).
581
582 #+begin_src emacs-lisp
583 (setq initial-major-mode 'text-mode)
584 #+end_src
585
586 Inhibit the buffer list when more than 2 files are loaded.
587
588 #+begin_src emacs-lisp
589 (setq inhibit-startup-buffer-menu t)
590 #+end_src
591
592 I don't really need to see the startup screen or echo area message
593 either.
594
595 #+begin_src emacs-lisp
596 (advice-add #'display-startup-echo-area-message :override #'ignore)
597 (setq inhibit-startup-screen t
598 inhibit-startup-echo-area-message user-login-name)
599 #+end_src
600
601 *** More useful frame titles
602
603 Show either the file name or the buffer name (in case the buffer isn't
604 visiting a file). Borrowed from Emacs Prelude.
605
606 #+begin_src emacs-lisp
607 (setq frame-title-format
608 '("" invocation-name " - "
609 (:eval (if (buffer-file-name)
610 (abbreviate-file-name (buffer-file-name))
611 "%b"))))
612 #+end_src
613
614 *** Backups
615
616 Emacs' default backup settings aren't that great. Let's use more
617 sensible options. See documentation for the ~make-backup-file~
618 variable.
619
620 #+begin_src emacs-lisp
621 (setq backup-by-copying t
622 version-control t
623 delete-old-versions t)
624 #+end_src
625
626 *** Auto revert
627
628 Enable automatic reloading of changed buffers and files.
629
630 #+begin_src emacs-lisp
631 (global-auto-revert-mode 1)
632 (setq auto-revert-verbose nil
633 global-auto-revert-non-file-buffers nil)
634 #+end_src
635
636 *** Always use space for indentation
637
638 #+begin_src emacs-lisp
639 (setq-default
640 indent-tabs-mode nil
641 require-final-newline t
642 tab-width 4)
643 #+end_src
644
645 *** Winner mode
646
647 Enable =winner-mode=.
648
649 #+begin_src emacs-lisp
650 (winner-mode 1)
651 #+end_src
652
653 *** Close =*compilation*= on success
654
655 #+begin_src emacs-lisp
656 (setq compilation-exit-message-function
657 (lambda (status code msg)
658 "Close the compilation window if successful."
659 ;; if M-x compile exits with 0
660 (when (and (eq status 'exit) (zerop code))
661 (bury-buffer)
662 (delete-window (get-buffer-window (get-buffer "*compilation*"))))
663 ;; return the result of compilation-exit-message-function
664 (cons msg code)))
665 #+end_src
666
667 *** Search for non-ASCII characters
668
669 I’d like non-ASCII characters such as ‘’“”«»‹›áⓐ𝒶 to be selected when
670 I search for their ASCII counterpart. Shoutout to [[http://endlessparentheses.com/new-in-emacs-25-1-easily-search-non-ascii-characters.html][endlessparentheses]]
671 for this.
672
673 #+begin_src emacs-lisp
674 (setq search-default-mode #'char-fold-to-regexp)
675
676 ;; uncomment to extend this behaviour to query-replace
677 ;; (setq replace-char-fold t)
678 #+end_src
679
680 ** Bindings
681
682 #+begin_src emacs-lisp
683 (bind-keys
684 ("C-c a i" . ielm)
685
686 ("C-c e b" . eval-buffer)
687 ("C-c e r" . eval-region)
688
689 ("C-c F m" . make-frame-command)
690 ("C-c F d" . delete-frame)
691 ("C-c F D" . delete-other-frames)
692
693 ("C-c o" . other-window)
694
695 ("C-c Q" . save-buffers-kill-terminal)
696
697 ("C-S-h C" . describe-char)
698 ("C-S-h F" . describe-face)
699
700 ("C-x K" . kill-this-buffer)
701
702 ("s-p" . beginning-of-buffer)
703 ("s-n" . end-of-buffer))
704 #+end_src
705
706 ** Packages
707
708 The packages in this section are absolutely essential to my everyday
709 workflow, and they play key roles in how I do my computing. They
710 immensely enhance the Emacs experience for me; both using Emacs, and
711 customizing it.
712
713 *** [[https://github.com/emacscollective/auto-compile][auto-compile]]
714
715 #+begin_src emacs-lisp
716 (use-package auto-compile
717 :demand t
718 :config
719 (auto-compile-on-load-mode)
720 (auto-compile-on-save-mode)
721 (setq auto-compile-display-buffer nil
722 auto-compile-mode-line-counter t
723 auto-compile-source-recreate-deletes-dest t
724 auto-compile-toggle-deletes-nonlib-dest t
725 auto-compile-update-autoloads t)
726 (add-hook 'auto-compile-inhibit-compile-hook
727 'auto-compile-inhibit-compile-detached-git-head))
728 #+end_src
729
730 *** [[https://orgmode.org/][Org mode]]
731
732 #+begin_quote
733 Org mode is for keeping notes, maintaining TODO lists, planning
734 projects, and authoring documents with a fast and effective plain-text
735 system.
736 #+end_quote
737
738 In short, my favourite way of life.
739
740 #+begin_src emacs-lisp
741 (use-package org
742 :defer 1
743 :config
744 (setq org-src-tab-acts-natively t
745 org-src-preserve-indentation nil
746 org-edit-src-content-indentation 0
747 org-email-link-description-format "Email %c: %s" ; %.30s
748 org-highlight-latex-and-related '(entities)
749 org-log-done 'time)
750 (add-to-list 'org-structure-template-alist '("L" . "src emacs-lisp") t)
751 (font-lock-add-keywords
752 'org-mode
753 '(("[ \t]*\\(#\\+\\(BEGIN\\|END\\|begin\\|end\\)_\\(\\S-+\\)\\)[ \t]*\\([^\n:]*\\)"
754 (1 '(:foreground "#5a5b5a" :background "#292b2b") t) ; directive
755 (3 '(:foreground "#81a2be" :background "#292b2b") t) ; kind
756 (4 '(:foreground "#c5c8c6") t))) ; title
757 t)
758 :bind (:map org-mode-map ("M-L" . org-insert-last-stored-link))
759 :hook ((org-mode . org-indent-mode)
760 (org-mode . auto-fill-mode)
761 (org-mode . flyspell-mode))
762 :custom
763 (org-latex-packages-alist '(("" "listings") ("" "color")))
764 :custom-face
765 '(org-block-begin-line ((t (:foreground "#5a5b5a" :background "#1d1f21"))))
766 '(org-block ((t (:background "#1d1f21"))))
767 '(org-latex-and-related ((t (:foreground "#b294bb")))))
768
769 (use-package ox-latex
770 :after ox
771 :config
772 (setq org-latex-listings 'listings
773 ;; org-latex-prefer-user-labels t
774 )
775 (add-to-list 'org-latex-packages-alist '("" "listings"))
776 (add-to-list 'org-latex-packages-alist '("" "color"))
777 (add-to-list 'org-latex-classes
778 '("IEEEtran" "\\documentclass[11pt]{IEEEtran}"
779 ("\\section{%s}" . "\\section*{%s}")
780 ("\\subsection{%s}" . "\\subsection*{%s}")
781 ("\\subsubsection{%s}" . "\\subsubsection*{%s}")
782 ("\\paragraph{%s}" . "\\paragraph*{%s}")
783 ("\\subparagraph{%s}" . "\\subparagraph*{%s}"))
784 t))
785
786 (use-package ox-beamer
787 :after ox)
788
789 (use-package orgalist
790 :after message
791 :hook (message-mode . orgalist-mode))
792 #+end_src
793
794 **** asynchronous tangle
795
796 =amin/async-babel-tangle= is a function closely inspired by [[https://github.com/dieggsy/dotfiles/tree/cc10edf7701958eff1cd94d4081da544d882a28c/emacs.d#dotfiles][dieggsy's
797 d/async-babel-tangle]] which uses [[https://github.com/jwiegley/emacs-async][async]] to asynchronously tangle an org
798 file.
799
800 #+begin_src emacs-lisp
801 (after! org
802 (defvar amin-show-async-tangle-results nil
803 "Keep *emacs* async buffers around for later inspection.")
804
805 (defvar amin-show-async-tangle-time nil
806 "Show the time spent tangling the file.")
807
808 (defvar amin-async-tangle-post-compile "make ti"
809 "If non-nil, pass to `compile' after successful tangle.")
810
811 (defun amin/async-babel-tangle ()
812 "Tangle org file asynchronously."
813 (interactive)
814 (let* ((file-tangle-start-time (current-time))
815 (file (buffer-file-name))
816 (file-nodir (file-name-nondirectory file))
817 (async-quiet-switch "-q"))
818 (async-start
819 `(lambda ()
820 (require 'org)
821 (org-babel-tangle-file ,file))
822 (unless amin-show-async-tangle-results
823 `(lambda (result)
824 (if result
825 (progn
826 (message "Tangled %s%s"
827 ,file-nodir
828 (if amin-show-async-tangle-time
829 (format " (%.3fs)"
830 (float-time (time-subtract (current-time)
831 ',file-tangle-start-time)))
832 ""))
833 (when amin-async-tangle-post-compile
834 (compile amin-async-tangle-post-compile)))
835 (message "Tangling %s failed" ,file-nodir))))))))
836
837 (add-to-list
838 'safe-local-variable-values
839 '(eval add-hook 'after-save-hook #'amin/async-babel-tangle 'append 'local))
840 #+end_src
841
842 *** [[https://magit.vc/][Magit]]
843
844 #+begin_quote
845 It's Magit! A Git porcelain inside Emacs.
846 #+end_quote
847
848 Not just how I do git, but /the/ way to do git.
849
850 #+begin_src emacs-lisp
851 (use-package magit
852 :defer 1
853 :bind (("C-x g" . magit-status)
854 ("s-g s" . magit-status)
855 ("s-g l" . magit-log-buffer-file))
856 :config
857 (magit-add-section-hook 'magit-status-sections-hook
858 'magit-insert-modules
859 'magit-insert-stashes
860 'append)
861 (setq
862 magit-repository-directories '(("~/.emacs.d/" . 0)
863 ("~/src/git/" . 1)))
864 (nconc magit-section-initial-visibility-alist
865 '(([unpulled status] . show)
866 ([unpushed status] . show)))
867 :custom-face (magit-diff-file-heading ((t (:weight normal)))))
868 #+end_src
869
870 *** [[https://github.com/abo-abo/swiper][Ivy]] (and friends)
871
872 #+begin_quote
873 Ivy - a generic completion frontend for Emacs, Swiper - isearch with
874 an overview, and more. Oh, man!
875 #+end_quote
876
877 There's no way I could top that, so I won't attempt to.
878
879 **** Ivy
880
881 #+begin_src emacs-lisp
882 (use-package ivy
883 :defer 1
884 :bind
885 (:map ivy-minibuffer-map
886 ([escape] . keyboard-escape-quit)
887 ([S-up] . ivy-previous-history-element)
888 ([S-down] . ivy-next-history-element)
889 ("DEL" . ivy-backward-delete-char))
890 :config
891 (setq ivy-wrap t)
892 (ivy-mode 1)
893 ;; :custom-face
894 ;; (ivy-minibuffer-match-face-2 ((t (:background "#e99ce8" :weight semi-bold))))
895 ;; (ivy-minibuffer-match-face-3 ((t (:background "#bbbbff" :weight semi-bold))))
896 ;; (ivy-minibuffer-match-face-4 ((t (:background "#ffbbff" :weight semi-bold))))
897 )
898 #+end_src
899
900 **** Swiper
901
902 #+begin_src emacs-lisp
903 (use-package swiper
904 :bind (("C-s" . swiper)
905 ("C-r" . swiper)))
906 #+end_src
907
908 **** Counsel
909
910 #+begin_src emacs-lisp
911 (use-package counsel
912 :defer 1
913 :bind (([remap execute-extended-command] . counsel-M-x)
914 ([remap find-file] . counsel-find-file)
915 ("s-r" . counsel-recentf)
916 ("C-c x" . counsel-M-x)
917 ("C-c f ." . counsel-find-file)
918 :map minibuffer-local-map
919 ("C-r" . counsel-minibuffer-history))
920 :config
921 (counsel-mode 1)
922 (defalias 'locate #'counsel-locate))
923 #+end_src
924
925 *** eshell
926
927 #+begin_src emacs-lisp
928 (use-package eshell
929 :defer 1
930 :commands eshell
931 :config
932 (eval-when-compile (defvar eshell-prompt-regexp))
933 (defun amin/eshell-quit-or-delete-char (arg)
934 (interactive "p")
935 (if (and (eolp) (looking-back eshell-prompt-regexp nil))
936 (eshell-life-is-too-much)
937 (delete-char arg)))
938
939 (defun amin/eshell-clear ()
940 (interactive)
941 (let ((inhibit-read-only t))
942 (erase-buffer))
943 (eshell-send-input))
944
945 (defun amin|eshell-setup ()
946 (make-local-variable 'company-idle-delay)
947 (setq company-idle-delay nil)
948 (bind-keys :map eshell-mode-map
949 ("C-d" . amin/eshell-quit-or-delete-char)
950 ("C-S-l" . amin/eshell-clear)
951 ("M-r" . counsel-esh-history)
952 ([tab] . company-complete)))
953
954 :hook (eshell-mode . amin|eshell-setup)
955 :custom
956 (eshell-hist-ignoredups t)
957 (eshell-input-filter 'eshell-input-filter-initial-space))
958 #+end_src
959
960 *** Ibuffer
961
962 #+begin_src emacs-lisp
963 (use-package ibuffer
964 :defer t
965 :bind
966 (("C-x C-b" . ibuffer-other-window)
967 :map ibuffer-mode-map
968 ("P" . ibuffer-backward-filter-group)
969 ("N" . ibuffer-forward-filter-group)
970 ("M-p" . ibuffer-do-print)
971 ("M-n" . ibuffer-do-shell-command-pipe-replace))
972 :config
973 ;; Use human readable Size column instead of original one
974 (define-ibuffer-column size-h
975 (:name "Size" :inline t)
976 (cond
977 ((> (buffer-size) 1000000) (format "%7.1fM" (/ (buffer-size) 1000000.0)))
978 ((> (buffer-size) 100000) (format "%7.0fk" (/ (buffer-size) 1000.0)))
979 ((> (buffer-size) 1000) (format "%7.1fk" (/ (buffer-size) 1000.0)))
980 (t (format "%8d" (buffer-size)))))
981 :custom
982 (ibuffer-saved-filter-groups
983 '(("default"
984 ("dired" (mode . dired-mode))
985 ("org" (mode . org-mode))
986 ("web"
987 (or
988 (mode . web-mode)
989 (mode . css-mode)
990 (mode . scss-mode)
991 (mode . js2-mode)))
992 ("shell"
993 (or
994 (mode . eshell-mode)
995 (mode . shell-mode)))
996 ("notmuch" (name . "\*notmuch\*"))
997 ("programming"
998 (or
999 (mode . python-mode)
1000 (mode . c++-mode)
1001 (mode . emacs-lisp-mode)))
1002 ("emacs"
1003 (or
1004 (name . "^\\*scratch\\*$")
1005 (name . "^\\*Messages\\*$")))
1006 ("slack"
1007 (or
1008 (name . "^\\*Slack*"))))))
1009 (ibuffer-formats
1010 '((mark modified read-only locked " "
1011 (name 18 18 :left :elide)
1012 " "
1013 (size-h 9 -1 :right)
1014 " "
1015 (mode 16 16 :left :elide)
1016 " " filename-and-process)
1017 (mark " "
1018 (name 16 -1)
1019 " " filename)))
1020 :hook (ibuffer . (lambda () (ibuffer-switch-to-saved-filter-groups "default"))))
1021 #+end_src
1022
1023 *** Outline
1024
1025 #+begin_src emacs-lisp
1026 (use-package outline
1027 :defer t
1028 :hook (prog-mode . outline-minor-mode)
1029 :bind
1030 (:map
1031 outline-minor-mode-map
1032 ("<s-tab>" . outline-toggle-children)
1033 ("M-p" . outline-previous-visible-heading)
1034 ("M-n" . outline-next-visible-heading)
1035 :prefix-map amin--outline-prefix-map
1036 :prefix "s-o"
1037 ("TAB" . outline-toggle-children)
1038 ("a" . outline-hide-body)
1039 ("H" . outline-hide-body)
1040 ("S" . outline-show-all)
1041 ("h" . outline-hide-subtree)
1042 ("s" . outline-show-subtree)))
1043 #+end_src
1044
1045 * Borg's =layer/essentials=
1046
1047 TODO: break this giant source block down into individual org sections.
1048
1049 #+begin_src emacs-lisp
1050 (use-package dash
1051 :config (dash-enable-font-lock))
1052
1053 (use-package diff-hl
1054 :config
1055 (setq diff-hl-draw-borders nil)
1056 (global-diff-hl-mode)
1057 (add-hook 'magit-post-refresh-hook 'diff-hl-magit-post-refresh t))
1058
1059 (use-package dired
1060 :defer t
1061 :config (setq dired-listing-switches "-alh"))
1062
1063 (use-package eldoc
1064 :when (version< "25" emacs-version)
1065 :config (global-eldoc-mode))
1066
1067 (use-package help
1068 :defer t
1069 :config
1070 (temp-buffer-resize-mode)
1071 (setq help-window-select t))
1072
1073 (progn ; `isearch'
1074 (setq isearch-allow-scroll t))
1075
1076 (use-package lisp-mode
1077 :config
1078 (add-hook 'emacs-lisp-mode-hook 'outline-minor-mode)
1079 (add-hook 'emacs-lisp-mode-hook 'reveal-mode)
1080 (defun indent-spaces-mode ()
1081 (setq indent-tabs-mode nil))
1082 (add-hook 'lisp-interaction-mode-hook #'indent-spaces-mode))
1083
1084 (use-package man
1085 :defer t
1086 :config (setq Man-width 80))
1087
1088 (use-package paren
1089 :config (show-paren-mode))
1090
1091 (use-package prog-mode
1092 :config (global-prettify-symbols-mode)
1093 (defun indicate-buffer-boundaries-left ()
1094 (setq indicate-buffer-boundaries 'left))
1095 (add-hook 'prog-mode-hook #'indicate-buffer-boundaries-left))
1096
1097 (use-package recentf
1098 :defer 0.5
1099 :config
1100 (add-to-list 'recentf-exclude "^/\\(?:ssh\\|su\\|sudo\\)?:")
1101 (setq recentf-max-saved-items 40))
1102
1103 (use-package savehist
1104 :config (savehist-mode))
1105
1106 (use-package saveplace
1107 :when (version< "25" emacs-version)
1108 :config (save-place-mode))
1109
1110 (use-package simple
1111 :config (column-number-mode))
1112
1113 (progn ; `text-mode'
1114 (add-hook 'text-mode-hook #'indicate-buffer-boundaries-left)
1115 (add-hook 'text-mode-hook #'abbrev-mode))
1116
1117 (use-package tramp
1118 :defer t
1119 :config
1120 (add-to-list 'tramp-default-proxies-alist '(nil "\\`root\\'" "/ssh:%h:"))
1121 (add-to-list 'tramp-default-proxies-alist '("localhost" nil nil))
1122 (add-to-list 'tramp-default-proxies-alist
1123 (list (regexp-quote (system-name)) nil nil)))
1124
1125 #+end_src
1126
1127 * Editing
1128
1129 ** Company
1130
1131 #+begin_src emacs-lisp
1132 (use-package company
1133 :defer 1
1134 :bind
1135 (:map company-active-map
1136 ([tab] . company-complete-common-or-cycle)
1137 ([escape] . company-abort))
1138 :custom
1139 (company-minimum-prefix-length 1)
1140 (company-selection-wrap-around t)
1141 (company-dabbrev-char-regexp "\\sw\\|\\s_\\|[-_]")
1142 (company-dabbrev-downcase nil)
1143 (company-dabbrev-ignore-case nil)
1144 :config
1145 (global-company-mode t))
1146 #+end_src
1147
1148 * Syntax and spell checking
1149
1150 #+begin_src emacs-lisp
1151 (use-package flycheck
1152 :defer 3
1153 :hook (prog-mode . flycheck-mode)
1154 :bind
1155 (:map flycheck-mode-map
1156 ("M-P" . flycheck-previous-error)
1157 ("M-N" . flycheck-next-error))
1158 :config
1159 ;; Use the load-path from running Emacs when checking elisp files
1160 (setq flycheck-emacs-lisp-load-path 'inherit)
1161
1162 ;; Only flycheck when I actually save the buffer
1163 (setq flycheck-check-syntax-automatically '(mode-enabled save)))
1164
1165 ;; http://endlessparentheses.com/ispell-and-apostrophes.html
1166 (use-package ispell
1167 :defer 3
1168 :config
1169 ;; ’ can be part of a word
1170 (setq ispell-local-dictionary-alist
1171 `((nil "[[:alpha:]]" "[^[:alpha:]]"
1172 "['\x2019]" nil ("-B") nil utf-8)))
1173 ;; don't send ’ to the subprocess
1174 (defun endless/replace-apostrophe (args)
1175 (cons (replace-regexp-in-string
1176 "’" "'" (car args))
1177 (cdr args)))
1178 (advice-add #'ispell-send-string :filter-args
1179 #'endless/replace-apostrophe)
1180
1181 ;; convert ' back to ’ from the subprocess
1182 (defun endless/replace-quote (args)
1183 (if (not (derived-mode-p 'org-mode))
1184 args
1185 (cons (replace-regexp-in-string
1186 "'" "’" (car args))
1187 (cdr args))))
1188 (advice-add #'ispell-parse-output :filter-args
1189 #'endless/replace-quote))
1190 #+end_src
1191 * Programming modes
1192
1193 ** [[http://alloytools.org][Alloy]] (with [[https://github.com/dwwmmn/alloy-mode][alloy-mode]])
1194
1195 #+begin_src emacs-lisp
1196 (use-package alloy-mode
1197 :defer t
1198 :config (setq alloy-basic-offset 2))
1199 #+end_src
1200
1201 ** [[https://coq.inria.fr][Coq]] (with [[https://github.com/ProofGeneral/PG][Proof General]])
1202
1203 #+begin_src emacs-lisp
1204 (use-package proof-site ; Proof General
1205 :defer t
1206 :load-path "lib/proof-site/generic/")
1207 #+end_src
1208
1209 ** [[https://leanprover.github.io][Lean]] (with [[https://github.com/leanprover/lean-mode][lean-mode]])
1210
1211 #+begin_src emacs-lisp
1212 (eval-when-compile (defvar lean-mode-map))
1213 (use-package lean-mode
1214 :defer 1
1215 :bind (:map lean-mode-map
1216 ("S-SPC" . company-complete))
1217 :config
1218 (require 'lean-input)
1219 (setq default-input-method "Lean"
1220 lean-input-tweak-all '(lean-input-compose
1221 (lean-input-prepend "/")
1222 (lean-input-nonempty))
1223 lean-input-user-translations '(("/" "/")))
1224 (lean-input-setup))
1225 #+end_src
1226
1227 ** Haskell
1228
1229 *** [[https://github.com/haskell/haskell-mode][haskell-mode]]
1230
1231 #+begin_src emacs-lisp
1232 (use-package haskell-mode
1233 :defer t
1234 :config
1235 (setq haskell-indentation-layout-offset 4
1236 haskell-indentation-left-offset 4
1237 flycheck-checker 'haskell-hlint
1238 flycheck-disabled-checkers '(haskell-stack-ghc haskell-ghc)))
1239 #+end_src
1240
1241 *** [[https://github.com/jyp/dante][dante]]
1242
1243 #+begin_src emacs-lisp
1244 (use-package dante
1245 :after haskell-mode
1246 :commands dante-mode
1247 :hook (haskell-mode . dante-mode))
1248 #+end_src
1249
1250 *** [[https://github.com/mpickering/hlint-refactor-mode][hlint-refactor]]
1251
1252 Emacs bindings for [[https://github.com/ndmitchell/hlint][hlint]]'s refactor option. This requires the refact
1253 executable from [[https://github.com/mpickering/apply-refact][apply-refact]].
1254
1255 #+begin_src emacs-lisp
1256 (use-package hlint-refactor
1257 :after haskell-mode
1258 :bind (:map hlint-refactor-mode-map
1259 ("C-c l b" . hlint-refactor-refactor-buffer)
1260 ("C-c l r" . hlint-refactor-refactor-at-point))
1261 :hook (haskell-mode . hlint-refactor-mode))
1262 #+end_src
1263
1264 *** [[https://github.com/flycheck/flycheck-haskell][flycheck-haskell]]
1265
1266 #+begin_src emacs-lisp
1267 (use-package flycheck-haskell
1268 :after haskell-mode)
1269 #+end_src
1270
1271 *** [[https://github.com/ndmitchell/hlint/blob/20e116a043f2073c57b17b24ae6364b5e433ba7e/data/hs-lint.el][hs-lint.el]]
1272 :PROPERTIES:
1273 :header-args+: :tangle lisp/hs-lint.el :mkdirp yes
1274 :END:
1275
1276 Currently using =flycheck-haskell= with the =haskell-hlint= checker
1277 instead.
1278
1279 #+begin_src emacs-lisp :tangle no
1280 ;;; hs-lint.el --- minor mode for HLint code checking
1281
1282 ;; Copyright 2009 (C) Alex Ott
1283 ;;
1284 ;; Author: Alex Ott <alexott@gmail.com>
1285 ;; Keywords: haskell, lint, HLint
1286 ;; Requirements:
1287 ;; Status: distributed under terms of GPL2 or above
1288
1289 ;; Typical message from HLint looks like:
1290 ;;
1291 ;; /Users/ott/projects/lang-exp/haskell/test.hs:52:1: Eta reduce
1292 ;; Found:
1293 ;; count1 p l = length (filter p l)
1294 ;; Why not:
1295 ;; count1 p = length . filter p
1296
1297
1298 (require 'compile)
1299
1300 (defgroup hs-lint nil
1301 "Run HLint as inferior of Emacs, parse error messages."
1302 :group 'tools
1303 :group 'haskell)
1304
1305 (defcustom hs-lint-command "hlint"
1306 "The default hs-lint command for \\[hlint]."
1307 :type 'string
1308 :group 'hs-lint)
1309
1310 (defcustom hs-lint-save-files t
1311 "Save modified files when run HLint or no (ask user)"
1312 :type 'boolean
1313 :group 'hs-lint)
1314
1315 (defcustom hs-lint-replace-with-suggestions nil
1316 "Replace user's code with suggested replacements"
1317 :type 'boolean
1318 :group 'hs-lint)
1319
1320 (defcustom hs-lint-replace-without-ask nil
1321 "Replace user's code with suggested replacements automatically"
1322 :type 'boolean
1323 :group 'hs-lint)
1324
1325 (defun hs-lint-process-setup ()
1326 "Setup compilation variables and buffer for `hlint'."
1327 (run-hooks 'hs-lint-setup-hook))
1328
1329 ;; regex for replace suggestions
1330 ;;
1331 ;; ^\(.*?\):\([0-9]+\):\([0-9]+\): .*
1332 ;; Found:
1333 ;; \s +\(.*\)
1334 ;; Why not:
1335 ;; \s +\(.*\)
1336
1337 (defvar hs-lint-regex
1338 "^\\(.*?\\):\\([0-9]+\\):\\([0-9]+\\): .*[\n\C-m]Found:[\n\C-m]\\s +\\(.*\\)[\n\C-m]Why not:[\n\C-m]\\s +\\(.*\\)[\n\C-m]"
1339 "Regex for HLint messages")
1340
1341 (defun make-short-string (str maxlen)
1342 (if (< (length str) maxlen)
1343 str
1344 (concat (substring str 0 (- maxlen 3)) "...")))
1345
1346 (defun hs-lint-replace-suggestions ()
1347 "Perform actual replacement of suggestions"
1348 (goto-char (point-min))
1349 (while (re-search-forward hs-lint-regex nil t)
1350 (let* ((fname (match-string 1))
1351 (fline (string-to-number (match-string 2)))
1352 (old-code (match-string 4))
1353 (new-code (match-string 5))
1354 (msg (concat "Replace '" (make-short-string old-code 30)
1355 "' with '" (make-short-string new-code 30) "'"))
1356 (bline 0)
1357 (eline 0)
1358 (spos 0)
1359 (new-old-code ""))
1360 (save-excursion
1361 (switch-to-buffer (get-file-buffer fname))
1362 (goto-char (point-min))
1363 (forward-line (1- fline))
1364 (beginning-of-line)
1365 (setf bline (point))
1366 (when (or hs-lint-replace-without-ask
1367 (yes-or-no-p msg))
1368 (end-of-line)
1369 (setf eline (point))
1370 (beginning-of-line)
1371 (setf old-code (regexp-quote old-code))
1372 (while (string-match "\\\\ " old-code spos)
1373 (setf new-old-code (concat new-old-code
1374 (substring old-code spos (match-beginning 0))
1375 "\\ *"))
1376 (setf spos (match-end 0)))
1377 (setf new-old-code (concat new-old-code (substring old-code spos)))
1378 (remove-text-properties bline eline '(composition nil))
1379 (when (re-search-forward new-old-code eline t)
1380 (replace-match new-code nil t)))))))
1381
1382 (defun hs-lint-finish-hook (buf msg)
1383 "Function, that is executed at the end of HLint execution"
1384 (if hs-lint-replace-with-suggestions
1385 (hs-lint-replace-suggestions)
1386 (next-error 1 t)))
1387
1388 (define-compilation-mode hs-lint-mode "HLint"
1389 "Mode for check Haskell source code."
1390 (set (make-local-variable 'compilation-process-setup-function)
1391 'hs-lint-process-setup)
1392 (set (make-local-variable 'compilation-disable-input) t)
1393 (set (make-local-variable 'compilation-scroll-output) nil)
1394 (set (make-local-variable 'compilation-finish-functions)
1395 (list 'hs-lint-finish-hook))
1396 )
1397
1398 (defun hs-lint ()
1399 "Run HLint for current buffer with haskell source"
1400 (interactive)
1401 (save-some-buffers hs-lint-save-files)
1402 (compilation-start (concat hs-lint-command " \"" buffer-file-name "\"")
1403 'hs-lint-mode))
1404
1405 (provide 'hs-lint)
1406 ;;; hs-lint.el ends here
1407 #+end_src
1408
1409 #+begin_src emacs-lisp :tangle no
1410 (use-package hs-lint
1411 :load-path "lisp/"
1412 :bind (:map haskell-mode-map
1413 ("C-c l l" . hs-lint)))
1414 #+end_src
1415
1416 ** Web dev
1417
1418 *** SGML and HTML
1419
1420 #+begin_src emacs-lisp
1421 (use-package sgml-mode
1422 :defer t
1423 :config
1424 (setq sgml-basic-offset 2))
1425 #+end_src
1426
1427 *** CSS and SCSS
1428
1429 #+begin_src emacs-lisp
1430 (use-package css-mode
1431 :defer t
1432 :config
1433 (setq css-indent-offset 2))
1434 #+end_src
1435
1436 *** Web mode
1437
1438 #+begin_src emacs-lisp
1439 (use-package web-mode
1440 :defer t
1441 :mode "\\.html\\'"
1442 :config
1443 (setq-every! 2
1444 web-mode-code-indent-offset
1445 web-mode-css-indent-offset
1446 web-mode-markup-indent-offset))
1447 #+end_src
1448
1449 *** Emmet mode
1450
1451 #+begin_src emacs-lisp
1452 (use-package emmet-mode
1453 :after (:any web-mode css-mode sgml-mode)
1454 :bind* (("C-)" . emmet-next-edit-point)
1455 ("C-(" . emmet-prev-edit-point))
1456 :config
1457 (unbind-key "C-j" emmet-mode-keymap)
1458 (setq emmet-move-cursor-between-quotes t)
1459 :hook (web-mode css-mode html-mode sgml-mode))
1460 #+end_src
1461
1462 ** Nix
1463
1464 #+begin_src emacs-lisp
1465 (use-package nix-mode
1466 :defer t
1467 :mode "\\.nix\\'")
1468 #+end_src
1469
1470 ** Java
1471
1472 *** meghanada
1473
1474 #+begin_src emacs-lisp :tangle no
1475 (use-package meghanada
1476 :bind
1477 (:map meghanada-mode-map
1478 (("C-M-o" . meghanada-optimize-import)
1479 ("C-M-t" . meghanada-import-all)))
1480 :hook (java-mode . meghanada-mode))
1481 #+end_src
1482
1483 *** lsp-java
1484
1485 #+begin_comment
1486 dependencies:
1487
1488 ace-window
1489 avy
1490 bui
1491 company-lsp
1492 dap-mode
1493 lsp-java
1494 lsp-mode
1495 lsp-ui
1496 pfuture
1497 tree-mode
1498 treemacs
1499 #+end_comment
1500
1501 #+begin_src emacs-lisp :tangle no
1502 (use-package treemacs
1503 :config (setq treemacs-never-persist t))
1504
1505 (use-package yasnippet
1506 :config
1507 ;; (yas-global-mode)
1508 )
1509
1510 (use-package lsp-mode
1511 :init (setq lsp-eldoc-render-all nil
1512 lsp-highlight-symbol-at-point nil)
1513 )
1514
1515 (use-package hydra)
1516
1517 (use-package company-lsp
1518 :after company
1519 :config
1520 (setq company-lsp-cache-candidates t
1521 company-lsp-async t))
1522
1523 (use-package lsp-ui
1524 :config
1525 (setq lsp-ui-sideline-update-mode 'point))
1526
1527 (use-package lsp-java
1528 :config
1529 (add-hook 'java-mode-hook
1530 (lambda ()
1531 (setq-local company-backends (list 'company-lsp))))
1532
1533 (add-hook 'java-mode-hook 'lsp-java-enable)
1534 (add-hook 'java-mode-hook 'flycheck-mode)
1535 (add-hook 'java-mode-hook 'company-mode)
1536 (add-hook 'java-mode-hook 'lsp-ui-mode))
1537
1538 (use-package dap-mode
1539 :after lsp-mode
1540 :config
1541 (dap-mode t)
1542 (dap-ui-mode t))
1543
1544 (use-package dap-java
1545 :after (lsp-java))
1546
1547 (use-package lsp-java-treemacs
1548 :after (treemacs))
1549 #+end_src
1550
1551 * Emacs Enhancements
1552
1553 ** [[https://github.com/justbur/emacs-which-key][which-key]]
1554
1555 #+begin_quote
1556 Emacs package that displays available keybindings in popup
1557 #+end_quote
1558
1559 #+begin_src emacs-lisp
1560 (use-package which-key
1561 :defer 1
1562 :config (which-key-mode))
1563 #+end_src
1564
1565 ** theme
1566
1567 #+begin_src emacs-lisp
1568 (add-to-list 'custom-theme-load-path "~/.emacs.d/lisp")
1569 (load-theme 'tangomod t)
1570 #+end_src
1571
1572 ** doom-modeline
1573
1574 #+begin_src emacs-lisp
1575 (use-package doom-modeline
1576 :demand t
1577 :config (setq doom-modeline-height 32)
1578 :hook (after-init . doom-modeline-init))
1579 #+end_src
1580
1581 ** doom-themes
1582
1583 #+begin_src emacs-lisp
1584 (use-package doom-themes)
1585 #+end_src
1586
1587 ** theme helper functions
1588
1589 #+begin_src emacs-lisp
1590 (defun amin/lights-on ()
1591 "Enable my favourite light theme."
1592 (interactive)
1593 (progn
1594 (mapc #'disable-theme custom-enabled-themes)
1595 (load-theme 'tangomod t)))
1596
1597 (defun amin/lights-off ()
1598 "Go dark."
1599 (interactive)
1600 (progn
1601 (mapc #'disable-theme custom-enabled-themes)
1602 (load-theme 'doom-tomorrow-night t)))
1603
1604 (bind-keys
1605 ("s-t d" . amin/lights-off)
1606 ("s-t l" . amin/lights-on))
1607 #+end_src
1608
1609 ** [[https://github.com/bbatsov/crux][crux]]
1610
1611 #+begin_src emacs-lisp
1612 (use-package crux
1613 :defer 1
1614 :bind (("C-c b k" . crux-kill-other-buffers)
1615 ("C-c d" . crux-duplicate-current-line-or-region)
1616 ("C-c D" . crux-duplicate-and-comment-current-line-or-region)
1617 ("C-c f c" . crux-copy-file-preserve-attributes)
1618 ("C-c f d" . crux-delete-file-and-buffer)
1619 ("C-c f r" . crux-rename-file-and-buffer)
1620 ("C-c j" . crux-top-join-line)
1621 ("C-S-j" . crux-top-join-line)))
1622 #+end_src
1623
1624 ** [[https://github.com/alezost/mwim.el][mwim]]
1625
1626 #+begin_src emacs-lisp
1627 (use-package mwim
1628 :bind (("C-a" . mwim-beginning-of-code-or-line)
1629 ("C-e" . mwim-end-of-code-or-line)
1630 ("<home>" . mwim-beginning-of-line-or-code)
1631 ("<end>" . mwim-end-of-line-or-code)))
1632 #+end_src
1633
1634 ** projectile
1635
1636 #+begin_src emacs-lisp
1637 (use-package projectile
1638 :defer t
1639 :bind-keymap ("C-c p" . projectile-command-map)
1640 :config
1641 (projectile-mode)
1642
1643 (defun my-projectile-invalidate-cache (&rest _args)
1644 ;; ignore the args to `magit-checkout'
1645 (projectile-invalidate-cache nil))
1646
1647 (eval-after-load 'magit-branch
1648 '(progn
1649 (advice-add 'magit-checkout
1650 :after #'my-projectile-invalidate-cache)
1651 (advice-add 'magit-branch-and-checkout
1652 :after #'my-projectile-invalidate-cache))))
1653 #+end_src
1654
1655 ** [[https://github.com/Wilfred/helpful][helpful]]
1656
1657 #+begin_src emacs-lisp
1658 (use-package helpful
1659 :defer 1
1660 :bind
1661 (("C-S-h c" . helpful-command)
1662 ("C-S-h f" . helpful-callable) ; helpful-function
1663 ("C-S-h v" . helpful-variable)
1664 ("C-S-h k" . helpful-key)
1665 ("C-S-h p" . helpful-at-point)))
1666 #+end_src
1667
1668 ** [[https://github.com/knu/shell-toggle.el][shell-toggle]]
1669
1670 #+begin_src emacs-lisp
1671 (use-package shell-toggle
1672 :after eshell
1673 :bind ("C-c a s e" . amin/shell-toggle)
1674 :config
1675 (defun amin/shell-toggle (make-cd)
1676 "Toggle between the shell buffer and whatever buffer you are editing.
1677 With a prefix argument MAKE-CD also insert a \"cd DIR\" command
1678 into the shell, where DIR is the directory of the current buffer.
1679
1680 When called in the shell buffer returns you to the buffer you were editing
1681 before calling this the first time.
1682
1683 Options: `shell-toggle-goto-eob'"
1684 (interactive "P")
1685 ;; Try to decide on one of three possibilities:
1686 ;; If not in shell-buffer, switch to it.
1687 ;; If in shell-buffer, return to state before going to the shell-buffer
1688 (if (eq (current-buffer) shell-toggle-shell-buffer)
1689 (shell-toggle-buffer-return-from-shell)
1690 (progn
1691 (shell-toggle-buffer-goto-shell make-cd)
1692 (if shell-toggle-full-screen-window-only (delete-other-windows)))))
1693
1694 ;; override to split horizontally instead
1695 (defun shell-toggle-buffer-switch-to-other-window ()
1696 "Switch to other window.
1697 If the current window is the only window in the current frame,
1698 create a new window and switch to it.
1699
1700 \(This is less intrusive to the current window configuration than
1701 `switch-buffer-other-window')"
1702 (let ((this-window (selected-window)))
1703 (other-window 1)
1704 ;; If we did not switch window then we only have one window and need to
1705 ;; create a new one.
1706 (if (eq this-window (selected-window))
1707 (progn
1708 (split-window-horizontally)
1709 (other-window 1)))))
1710
1711 :custom
1712 (shell-toggle-launch-shell 'shell-toggle-eshell))
1713 #+end_src
1714
1715 ** [[https://github.com/EricCrosson/unkillable-scratch][unkillable-scratch]]
1716
1717 Make =*scratch*= and =*Messages*= unkillable.
1718
1719 #+begin_src emacs-lisp
1720 (use-package unkillable-scratch
1721 :defer 3
1722 :config
1723 (unkillable-scratch 1)
1724 :custom
1725 (unkillable-scratch-behavior 'do-nothing)
1726 (unkillable-buffers '("^\\*scratch\\*$" "^\\*Messages\\*$")))
1727 #+end_src
1728
1729 ** [[https://github.com/davep/boxquote.el][boxquote.el]]
1730
1731 #+begin_example
1732 ,----
1733 | make pretty boxed quotes like this
1734 `----
1735 #+end_example
1736
1737 #+begin_src emacs-lisp
1738 (use-package boxquote
1739 :defer 3
1740 :bind
1741 (:prefix-map amin--boxquote-prefix-map
1742 :prefix "C-c q"
1743 ("b" . boxquote-buffer)
1744 ("B" . boxquote-insert-buffer)
1745 ("d" . boxquote-defun)
1746 ("F" . boxquote-insert-file)
1747 ("hf" . boxquote-describe-function)
1748 ("hk" . boxquote-describe-key)
1749 ("hv" . boxquote-describe-variable)
1750 ("hw" . boxquote-where-is)
1751 ("k" . boxquote-kill)
1752 ("p" . boxquote-paragraph)
1753 ("q" . boxquote-boxquote)
1754 ("r" . boxquote-region)
1755 ("s" . boxquote-shell-command)
1756 ("t" . boxquote-text)
1757 ("T" . boxquote-title)
1758 ("u" . boxquote-unbox)
1759 ("U" . boxquote-unbox-region)
1760 ("y" . boxquote-yank)
1761 ("M-q" . boxquote-fill-paragraph)
1762 ("M-w" . boxquote-kill-ring-save)))
1763 #+end_src
1764
1765 Also see [[https://www.emacswiki.org/emacs/rebox2][rebox2]].
1766
1767 ** [[https://github.com/DarthFennec/highlight-indent-guides][highlight-indent-guides]]
1768
1769 #+begin_src emacs-lisp
1770 (use-package highlight-indent-guides
1771 :disabled t
1772 :defer 3
1773 :hook ((prog-mode . highlight-indent-guides-mode)
1774 ;; (org-mode . highlight-indent-guides-mode)
1775 )
1776 :config
1777 (setq highlight-indent-guides-character ?\|)
1778 (setq highlight-indent-guides-auto-enabled nil)
1779 (setq highlight-indent-guides-method 'character)
1780 (setq highlight-indent-guides-responsive 'top)
1781 (set-face-foreground 'highlight-indent-guides-character-face "gainsboro")
1782 (set-face-foreground 'highlight-indent-guides-top-character-face "grey40")) ; grey13 is nice too
1783 #+end_src
1784
1785 ** pdf-tools
1786
1787 #+begin_src emacs-lisp
1788 (use-package pdf-tools
1789 :defer t
1790 :magic ("%PDF" . pdf-view-mode)
1791 :config
1792 (setq pdf-view-resize-factor 1.05)
1793 (pdf-tools-install)
1794 :bind
1795 (:map pdf-view-mode-map
1796 ("C-s" . isearch-forward)
1797 ("C-r" . isearch-backward)
1798 ("j" . pdf-view-next-line-or-next-page)
1799 ("k" . pdf-view-previous-line-or-previous-page)
1800 ("h" . image-backward-hscroll)
1801 ("l" . image-forward-hscroll)))
1802 #+end_src
1803
1804 ** anzu
1805
1806 #+begin_src emacs-lisp
1807 (use-package anzu)
1808 #+end_src
1809
1810 ** typo.el
1811
1812 #+begin_src emacs-lisp
1813 (use-package typo
1814 :defer 2
1815 :config
1816 (typo-global-mode 1)
1817 :hook (text-mode . typo-mode))
1818 #+end_src
1819
1820 ** hl-todo
1821
1822 #+begin_src emacs-lisp
1823 (use-package hl-todo
1824 :defer 4
1825 :config
1826 (global-hl-todo-mode))
1827 #+end_src
1828
1829 ** shrink-path
1830
1831 #+begin_src emacs-lisp
1832 (use-package shrink-path
1833 :after eshell
1834 :config
1835 (setq eshell-prompt-regexp "\\(.*\n\\)*λ "
1836 eshell-prompt-function #'+eshell/prompt)
1837
1838 (defun +eshell/prompt ()
1839 (let ((base/dir (shrink-path-prompt default-directory)))
1840 (concat (propertize (car base/dir)
1841 'face 'font-lock-comment-face)
1842 (propertize (cdr base/dir)
1843 'face 'font-lock-constant-face)
1844 (propertize (+eshell--current-git-branch)
1845 'face 'font-lock-function-name-face)
1846 "\n"
1847 (propertize "λ" 'face 'eshell-prompt-face)
1848 ;; needed for the input text to not have prompt face
1849 (propertize " " 'face 'default))))
1850
1851 (defun +eshell--current-git-branch ()
1852 (let ((branch (car (loop for match in (split-string (shell-command-to-string "git branch") "\n")
1853 when (string-match "^\*" match)
1854 collect match))))
1855 (if (not (eq branch nil))
1856 (concat " " (substring branch 2))
1857 ""))))
1858 #+end_src
1859
1860 ** slack
1861
1862 Hopefully temporary.
1863
1864 #+begin_src emacs-lisp
1865 (use-package slack
1866 :disabled t
1867 :commands (slack-start)
1868 :init
1869 (eval-when-compile ; silence the byte-compiler
1870 (defvar url-http-data nil)
1871 (defvar url-http-extra-headers nil)
1872 (defvar url-http-method nil)
1873 (defvar url-callback-function nil)
1874 (defvar url-callback-arguments nil)
1875 (defvar oauth--token-data nil))
1876 (setq slack-buffer-emojify t
1877 slack-prefer-current-team t)
1878 :config
1879 (slack-register-team
1880 :name "uw-apv"
1881 :default t
1882 :client-id uw-apv-client-id
1883 :client-secret uw-apv-client-secret
1884 :token uw-apv-token
1885 :subscribed-channels '(general)
1886 :full-and-display-names t)
1887 (slack-register-team
1888 :name "watform"
1889 :default nil
1890 :client-id watform-client-id
1891 :client-secret watform-client-secret
1892 :token watform-token
1893 :subscribed-channels '(general)
1894 :full-and-display-names t)
1895 (add-to-list 'swiper-font-lock-exclude 'slack-message-buffer-mode t)
1896 (setq lui-time-stamp-format "[%Y-%m-%d %H:%M:%S]"
1897 lui-time-stamp-only-when-changed-p t
1898 lui-time-stamp-position 'right)
1899 :bind
1900 (("C-c s s" . slack-start)
1901 ("C-c s u" . slack-select-unread-rooms)
1902 ("C-c s b" . slack-select-rooms)
1903 ("C-c s t" . slack-change-current-team)
1904 ("C-c s c" . slack-ws-close)
1905 :map slack-mode-map
1906 ("M-p" . slack-buffer-goto-prev-message)
1907 ("M-n" . slack-buffer-goto-next-message)
1908 ("C-c e" . slack-message-edit)
1909 ("C-c k" . slack-message-delete)
1910 ("C-c C-k" . slack-channel-leave)
1911 ("C-c r a" . slack-message-add-reaction)
1912 ("C-c r r" . slack-message-remove-reaction)
1913 ("C-c r s" . slack-message-show-reaction-users)
1914 ("C-c p l" . slack-room-pins-list)
1915 ("C-c p a" . slack-message-pins-add)
1916 ("C-c p r" . slack-message-pins-remove)
1917 ("@" . slack-message-embed-mention)
1918 ("#" . slack-message-embed-channel)))
1919
1920 (use-package alert
1921 :commands (alert)
1922 :init
1923 (setq alert-default-style 'notifier))
1924 #+end_src
1925
1926 ** [[https://github.com/peterwvj/eshell-up][eshell-up]]
1927
1928 #+begin_src emacs-lisp
1929 (use-package eshell-up
1930 :after eshell)
1931 #+end_src
1932
1933 ** multi-term
1934
1935 #+begin_src emacs-lisp
1936 (use-package multi-term
1937 :defer 1
1938 :bind (("C-c C-j" . term-line-mode)
1939 ("C-c a s m m" . multi-term)
1940 ("C-c a s m p" . multi-term-dedicated-toggle))
1941 :config
1942 (setq multi-term-program "/bin/screen"
1943 ;; TODO: add separate bindings for connecting to existing
1944 ;; session vs. always creating a new one
1945 multi-term-dedicated-select-after-open-p t
1946 multi-term-dedicated-window-height 20
1947 multi-term-dedicated-max-window-height 30
1948 term-bind-key-alist
1949 '(("C-c C-c" . term-interrupt-subjob)
1950 ("C-c C-e" . term-send-esc)
1951 ("C-k" . kill-line)
1952 ("C-y" . term-paste)
1953 ("M-f" . term-send-forward-word)
1954 ("M-b" . term-send-backward-word)
1955 ("M-p" . term-send-up)
1956 ("M-n" . term-send-down)
1957 ("<C-backspace>" . term-send-backward-kill-word)
1958 ("<M-DEL>" . term-send-backward-kill-word)
1959 ("M-d" . term-send-delete-word)
1960 ("M-," . term-send-raw)
1961 ("M-." . comint-dynamic-complete))
1962 term-unbind-key-alist
1963 '("C-z" "C-x" "C-c" "C-h" "C-y" "<ESC>")))
1964 #+end_src
1965
1966 ** page-break-lines
1967
1968 #+begin_src emacs-lisp
1969 (use-package page-break-lines
1970 :config
1971 (global-page-break-lines-mode))
1972 #+end_src
1973
1974 * Email
1975
1976 #+begin_src emacs-lisp
1977 (defvar amin-maildir (expand-file-name "~/mail/"))
1978 (after! recentf
1979 (add-to-list 'recentf-exclude amin-maildir))
1980 #+end_src
1981
1982 ** Gnus
1983
1984 #+begin_src emacs-lisp
1985 (setq
1986 amin-gnus-init-file (no-littering-expand-etc-file-name "gnus")
1987 mail-user-agent 'gnus-user-agent
1988 read-mail-command 'gnus)
1989
1990 (use-package gnus
1991 :bind (("s-m" . gnus)
1992 ("s-M" . gnus-unplugged))
1993 :init
1994 (setq
1995 gnus-select-method '(nnnil "")
1996 gnus-secondary-select-methods
1997 '((nnimap "amin"
1998 (nnimap-stream plain)
1999 (nnimap-address "127.0.0.1")
2000 (nnimap-server-port 143)
2001 (nnimap-authenticator plain)
2002 (nnimap-user "amin@aminb.org"))
2003 (nnimap "uwaterloo"
2004 (nnimap-stream plain)
2005 (nnimap-address "127.0.0.1")
2006 (nnimap-server-port 143)
2007 (nnimap-authenticator plain)
2008 (nnimap-user "abandali@uwaterloo.ca")))
2009 gnus-message-archive-group "nnimap+amin:Sent"
2010 gnus-parameters
2011 '(("gnu.*"
2012 (gcc-self . t)))
2013 gnus-large-newsgroup 50
2014 gnus-home-directory (no-littering-expand-var-file-name "gnus/")
2015 gnus-directory (concat gnus-home-directory "news/")
2016 message-directory (concat gnus-home-directory "mail/")
2017 nndraft-directory (concat gnus-home-directory "drafts/")
2018 gnus-save-newsrc-file nil
2019 gnus-read-newsrc-file nil
2020 gnus-interactive-exit nil
2021 gnus-gcc-mark-as-read t))
2022
2023 (use-package gnus-art
2024 :config
2025 (setq
2026 gnus-visible-headers
2027 (concat gnus-visible-headers "\\|^List-Id:\\|^X-RT-Originator:\\|^User-Agent:")
2028 gnus-sorted-header-list
2029 '("^From:" "^Subject:" "^Summary:" "^Keywords:"
2030 "^Followup-To:" "^To:" "^Cc:" "X-RT-Originator"
2031 "^Newsgroups:" "List-Id:" "^Organization:"
2032 "^User-Agent:" "^Date:")
2033 ;; local-lapsed article dates
2034 ;; from https://www.emacswiki.org/emacs/GnusFormatting#toc11
2035 gnus-article-date-headers '(user-defined)
2036 gnus-article-time-format
2037 (lambda (time)
2038 (let* ((date (format-time-string "%a, %d %b %Y %T %z" time))
2039 (local (article-make-date-line date 'local))
2040 (combined-lapsed (article-make-date-line date
2041 'combined-lapsed))
2042 (lapsed (progn
2043 (string-match " (.+" combined-lapsed)
2044 (match-string 0 combined-lapsed))))
2045 (concat local lapsed))))
2046 (bind-keys
2047 :map gnus-article-mode-map
2048 ("r" . gnus-article-reply-with-original)
2049 ("R" . gnus-article-wide-reply-with-original)
2050 ("M-L" . org-store-link)))
2051
2052 (use-package gnus-sum
2053 :bind (:map gnus-summary-mode-map
2054 :prefix-map amin--gnus-summary-prefix-map
2055 :prefix "v"
2056 ("r" . gnus-summary-reply)
2057 ("w" . gnus-summary-wide-reply)
2058 ("v" . gnus-summary-show-raw-article))
2059 :config
2060 (bind-keys
2061 :map gnus-summary-mode-map
2062 ("r" . gnus-summary-reply-with-original)
2063 ("R" . gnus-summary-wide-reply-with-original)
2064 ("M-L" . org-store-link))
2065 :hook (gnus-summary-mode . amin--no-mouse-autoselect-window))
2066
2067 (use-package gnus-msg
2068 :config
2069 (setq gnus-posting-styles
2070 '((".*"
2071 (address "amin@aminb.org")
2072 (body "\nBest,\namin\n")
2073 (eval (setq amin--message-cite-say-hi t)))
2074 ("gnu.*"
2075 (address "bandali@gnu.org"))
2076 ((header "subject" "ThankCRM")
2077 (to "webmasters-comment@gnu.org")
2078 (body "\nAdded to 2018supporters.html.\n\nMoving to campaigns.\n\n-amin\n")
2079 (eval (setq amin--message-cite-say-hi nil)))
2080 ("nnimap\\+uwaterloo:.*"
2081 (address "abandali@uwaterloo.ca")
2082 (gcc "\"nnimap+uwaterloo:Sent Items\"")))))
2083
2084 (use-package gnus-topic
2085 :hook (gnus-group-mode . gnus-topic-mode))
2086
2087 (use-package gnus-agent
2088 :config
2089 (setq gnus-agent-synchronize-flags 'ask)
2090 :hook (gnus-group-mode . gnus-agent-mode))
2091
2092 (use-package gnus-group
2093 :config
2094 (setq gnus-permanently-visible-groups "\\((INBOX\\|gnu$\\)"))
2095
2096 (use-package mm-decode
2097 :config
2098 (setq mm-discouraged-alternatives '("text/html" "text/richtext")))
2099 #+end_src
2100
2101 ** sendmail
2102
2103 #+begin_src emacs-lisp
2104 (use-package sendmail
2105 :config
2106 (setq sendmail-program "/usr/bin/msmtp"
2107 ;; message-sendmail-extra-arguments '("-v" "-d")
2108 mail-specify-envelope-from t
2109 mail-envelope-from 'header))
2110 #+end_src
2111
2112 ** message
2113
2114 #+begin_src emacs-lisp
2115 (use-package message
2116 :config
2117 (defconst amin--message-cite-style-format "On %Y-%m-%d %l:%M %p, %N wrote:")
2118 (defconst message-cite-style-bandali
2119 '((message-cite-function 'message-cite-original)
2120 (message-citation-line-function 'message-insert-formatted-citation-line)
2121 (message-cite-reply-position 'traditional)
2122 (message-yank-prefix "> ")
2123 (message-yank-cited-prefix ">")
2124 (message-yank-empty-prefix ">")
2125 (message-citation-line-format
2126 (if amin--message-cite-say-hi
2127 (concat "Hi %F,\n\n" amin--message-cite-style-format)
2128 amin--message-cite-style-format)))
2129 "Citation style based on Mozilla Thunderbird's. Use with message-cite-style.")
2130 (setq message-cite-style 'message-cite-style-bandali
2131 message-kill-buffer-on-exit t
2132 message-send-mail-function 'message-send-mail-with-sendmail
2133 message-sendmail-envelope-from 'header
2134 message-dont-reply-to-names
2135 "\\(\\(.*@aminb\\.org\\)\\|\\(amin@bandali\\.me\\)\\|\\(\\(aminb?\\|mab\\|bandali\\)@gnu\\.org\\)\\|\\(\\(m\\|a\\(min\\.\\)?\\)bandali@uwaterloo\\.ca\\)\\)"
2136 message-user-fqdn "aminb.org")
2137 :hook (;; (message-setup . mml-secure-message-sign-pgpmime)
2138 (message-mode . flyspell-mode)
2139 (message-mode . (lambda ()
2140 ;; (setq fill-column 65
2141 ;; message-fill-column 65)
2142 (make-local-variable 'company-idle-delay)
2143 (setq company-idle-delay 0.2))))
2144 ;; :custom-face
2145 ;; (message-header-subject ((t (:foreground "#111" :weight semi-bold))))
2146 ;; (message-header-to ((t (:foreground "#111" :weight normal))))
2147 ;; (message-header-cc ((t (:foreground "#333" :weight normal))))
2148 )
2149
2150 (after! mml-sec
2151 (setq mml-secure-openpgp-encrypt-to-self t
2152 mml-secure-openpgp-sign-with-sender t))
2153 #+end_src
2154
2155 ** footnote
2156
2157 Convenient footnotes in =message-mode=.
2158
2159 #+begin_src emacs-lisp
2160 (use-package footnote
2161 :after message
2162 :bind
2163 (:map message-mode-map
2164 :prefix-map amin--footnote-prefix-map
2165 :prefix "C-c f"
2166 ("a" . footnote-add-footnote)
2167 ("b" . footnote-back-to-message)
2168 ("c" . footnote-cycle-style)
2169 ("d" . footnote-delete-footnote)
2170 ("g" . footnote-goto-footnote)
2171 ("r" . footnote-renumber-footnotes)
2172 ("s" . footnote-set-style))
2173 :config
2174 (setq footnote-start-tag ""
2175 footnote-end-tag ""
2176 footnote-style 'unicode))
2177 #+end_src
2178
2179 ** bbdb
2180
2181 Manually install bbdb (=lisp/bbdb= copied from an ELPA-based setup),
2182 because installing it from source on Emacs 27 using the following
2183 submodule configuration for some reason doesn’t work and results in
2184 very strange errors when using any of the functions.
2185
2186 #+begin_src conf :tangle no
2187 [submodule "bbdb"]
2188 path = lib/bbdb
2189 url = https://git.savannah.nongnu.org/git/bbdb.git
2190 load-path = lisp
2191 info-path = doc
2192 build-step = ./autogen.sh
2193 build-step = ./configure
2194 build-step = make
2195 build-step = make install
2196 #+end_src
2197
2198 I tried using =borg-elpa= instead of doing it like this, but it added
2199 2 seconds to my startup time, which is unacceptable to me.
2200
2201 #+begin_src emacs-lisp
2202 (use-package bbdb
2203 :load-path "lisp/bbdb"
2204 :init
2205 (load (expand-file-name "lisp/bbdb/bbdb-autoloads.el" user-emacs-directory))
2206 ;; (bbdb-mua-auto-update-init 'message)
2207 (setq bbdb-mua-auto-update-p 'query
2208 bbdb-complete-mail nil)
2209 (bbdb-initialize 'gnus 'message))
2210 #+end_src
2211
2212 ** message-x
2213
2214 #+begin_src emacs-lisp
2215 (use-package message-x
2216 :disabled t
2217 :custom
2218 (message-x-completion-alist
2219 (quote
2220 (("\\([rR]esent-\\|[rR]eply-\\)?[tT]o:\\|[bB]?[cC][cC]:" . gnus-harvest-find-address)
2221 ((if
2222 (boundp
2223 (quote message-newgroups-header-regexp))
2224 message-newgroups-header-regexp message-newsgroups-header-regexp)
2225 . message-expand-group)))))
2226 #+end_src
2227
2228 ** gnus-harvest
2229
2230 #+begin_src emacs-lisp
2231 (use-package gnus-harvest
2232 :disabled t
2233 :commands gnus-harvest-install
2234 :demand t
2235 :config
2236 (if (featurep 'message-x)
2237 (gnus-harvest-install 'message-x)
2238 (gnus-harvest-install)))
2239 #+end_src
2240
2241 * Blogging
2242
2243 ** [[https://ox-hugo.scripter.co][ox-hugo]]
2244
2245 #+begin_src emacs-lisp
2246 (use-package ox-hugo
2247 :after ox)
2248
2249 (use-package ox-hugo-auto-export
2250 :load-path "lib/ox-hugo")
2251 #+end_src
2252
2253 * Post initialization
2254 :PROPERTIES:
2255 :CUSTOM_ID: post-initialization
2256 :END:
2257
2258 Display how long it took to load the init file.
2259
2260 #+begin_src emacs-lisp
2261 (message "Loading %s...done (%.3fs)" user-init-file
2262 (float-time (time-subtract (current-time)
2263 amin--before-user-init-time)))
2264 #+end_src
2265
2266 * Footer
2267 :PROPERTIES:
2268 :CUSTOM_ID: footer
2269 :END:
2270
2271 #+begin_src emacs-lisp :comments none
2272 ;;; init.el ends here
2273 #+end_src
2274
2275 * COMMENT Local Variables :ARCHIVE:
2276 # Local Variables:
2277 # eval: (add-hook 'after-save-hook #'amin/async-babel-tangle 'append 'local)
2278 # End: