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