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