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