[emacs][wip] begin migration to straight.el
[~bandali/configs] / init.org
CommitLineData
5fece105
AB
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
11This org file is my literate configuration for GNU Emacs, and is
12tangled 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
14many different people. Some of the configurations that I can remember
15off 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
28I'd like to have a fully reproducible Emacs setup (part of the reason
29why I store my configuration in this repository) but unfortunately out
30of the box, that's not achievable with =package.el=, not currently
31anyway. So, I've opted to use Borg. For what it's worth, I briefly
32experimented with [[https://github.com/raxod502/straight.el][straight.el]], but found that it added about 2 seconds
33to my init time; which is unacceptable for me: I use Emacs as my
34window manager (via EXWM) and coming from bspwm, I'm too used to
35having fast startup times.
36
37** Installation
38
39To use this config for your Emacs, first you need to clone this repo,
40then bootstrap Borg, tell Borg to retrieve package submodules, and
41byte-compiled the packages. Something along these lines should work:
42
43#+begin_src sh :tangle no
44git clone https://github.com/aminb/dotfiles ~/.emacs.d
45cd ~/.emacs.d
46make bootstrap-borg
47make bootstrap
48make 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]]
673d5faa
AB
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]]
5fece105
AB
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
f1149591 75;;; init.el --- Amin Bandali's Emacs config -*- lexical-binding: t; eval: (view-mode 1) -*-
5fece105
AB
76#+end_src
77
78Enable =view-mode=, which both makes the file read-only (as a reminder
79that =init.el= is an auto-generated file, not supposed to be edited),
80and provides some convenient key bindings for browsing through the
81file.
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
c5d8bb25
AB
107;; Emacs configuration of Amin Bandali, computer scientist, functional
108;; programmer, and free software advocate.
5fece105
AB
109
110;; THIS FILE IS AUTO-GENERATED FROM `init.org'.
111#+end_src
112
5fece105
AB
113* Initial setup
114:PROPERTIES:
115:CUSTOM_ID: initial-setup
116:END:
117
5fece105
AB
118** Emacs initialization
119
120I'd like to do a couple of measurements of Emacs' startup time. First,
121let'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
c5d8bb25 125(defvar a/before-user-init-time (current-time)
5fece105
AB
126 "Value of `current-time' when Emacs begins loading `user-init-file'.")
127(message "Loading Emacs...done (%.3fs)"
c5d8bb25 128 (float-time (time-subtract a/before-user-init-time
5fece105
AB
129 before-init-time)))
130#+end_src
131
132Also, temporarily increase ~gc-cons-threshhold~ and
133~gc-cons-percentage~ during startup to reduce garbage collection
134frequency. Clearing the ~file-name-handler-alist~ seems to help reduce
135startup time as well.
136
137#+begin_src emacs-lisp
c5d8bb25
AB
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)
5fece105
AB
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
148Of course, we'd like to set them back to their defaults once we're
149done initializing.
150
151#+begin_src emacs-lisp
152(add-hook
153 'after-init-hook
154 (lambda ()
c5d8bb25
AB
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)))
5fece105
AB
158#+end_src
159
160Increase the number of lines kept in message logs (the =*Messages*=
161buffer).
162
163#+begin_src emacs-lisp
164(setq message-log-max 20000)
165#+end_src
166
167Optionally, we could suppress some byte compiler warnings like below,
168but 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
12652893
AB
187I can do all my package management things with =straight.el=, and
188don'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]]).
5fece105
AB
190
191#+begin_src emacs-lisp :tangle early-init.el
192(setq package-enable-at-startup nil)
193#+end_src
194
195But since Emacs 27 isn't out yet (Emacs 26 is just around the corner
196right now), and even when released it'll be long before most distros
197ship in their repos, I'll still put the old workaround with the
198commented call to ~package-initialize~ here anyway.
199
12652893 200#+begin_src emacs-lisp :tangle no
5fece105
AB
201(setq package-enable-at-startup nil)
202;; (package-initialize)
203#+end_src
204
12652893
AB
205Update: the above is not necessary, since =straight.el= automatically
206does 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
5fece105
AB
230
231#+begin_quote
232Assimilate 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
236short, it creates a git submodule in =lib/= for each package, which
237can 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
263A 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
267packages (in our case especially the latter) in a neatly organized way
268and without compromising on performance.
269
270#+begin_src emacs-lisp
12652893 271(straight-use-package 'use-package)
5fece105
AB
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
12652893 281*** COMMENT Epkg
5fece105
AB
282
283#+begin_quote
284Browse the Emacsmirror package database
285#+end_quote
286
287Epkg provides access to a local copy of the [[https://emacsmirror.net][Emacsmirror]] package
288database, 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)
d568ebac
AB
297 ("C-c b u" . epkg-update))
298 :config
2b34d6dd 299 (eval-when-compile (defvar ivy-initial-inputs-alist))
d568ebac
AB
300 (with-eval-after-load 'ivy
301 (add-to-list
302 'ivy-initial-inputs-alist '(epkg-describe-package . "^") t)))
5fece105
AB
303#+end_src
304
305** No littering in =~/.emacs.d=
306
307#+begin_quote
308Help keeping ~/.emacs.d clean
309#+end_quote
310
311By default, even for Emacs' built-in packages, the configuration files
312and persistent data are all over the place. Use =no-littering= to help
313contain 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
328I'm not planning on using the custom file much, but even so, I
329definitely don't want it mixing with =init.el=. So, here; let's give
330it it's own file. While at it, treat themes as safe.
331
332#+begin_src emacs-lisp
333(use-package custom
12652893 334 :straight nil
5fece105
AB
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
345Load 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
354Let's use [[https://github.com/purcell/exec-path-from-shell][exec-path-from-shell]] to make Emacs use the =$PATH= as set up
355in 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
21ad71fa 369** COMMENT Only one custom theme at a time
5fece105
AB
370
371#+begin_src emacs-lisp
21ad71fa
AB
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))
5fece105
AB
375#+end_src
376
377** Server
378
379Start server if not already running. Alternatively, can be done by
380issuing =emacs --daemon= in the terminal, which can be automated with
381a systemd service or using =brew services start emacs= on macOS. I use
382Emacs as my window manager (via EXWM), so I always start Emacs on
383login; so starting the server from inside Emacs is good enough for me.
384
385See [[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
12652893 389 :straight nil
5fece105
AB
390 :defer 1
391 :config (or (server-running-p) (server-mode)))
392#+end_src
393
21ad71fa 394** COMMENT Unicode support
5fece105
AB
395
396Font stack with better unicode support, around =Ubuntu Mono= and
397=Hack=.
398
399#+begin_src emacs-lisp
21ad71fa
AB
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))
5fece105
AB
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
440I’d like focus to follow the mouse when I move the cursor from one
441window to the next.
442
443#+begin_src emacs-lisp
444(setq mouse-autoselect-window t)
445#+end_src
446
447Let’s define a function to conveniently disable this for certain
448buffers and/or modes.
449
450#+begin_src emacs-lisp
c5d8bb25 451(defun a/no-mouse-autoselect-window ()
5fece105
AB
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
5fece105
AB
465Convenience macro for =setq='ing multiple variables to the same value:
466
467#+begin_src emacs-lisp
c5d8bb25 468(defmacro a/setq-every (value &rest vars)
5fece105
AB
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
cb0c13d0
AB
474The 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
5fece105
AB
494* Core
495:PROPERTIES:
496:CUSTOM_ID: core
497:END:
498
5fece105
AB
499** Defaults
500
501*** Time and battery in mode-line
502
503Enable displaying time and battery in the mode-line, since I'm not
504using the Xfce panel anymore. Also, I don't need to see the load
505average on a regular basis, so disable that.
506
507Note: 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
523Might want to set the fringe to a smaller value, especially if using
524EXWM. 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
533Emacs disables some commands by default that could persumably be
534confusing 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
542Save what I copy into clipboard from other applications into Emacs'
543kill-ring, which would allow me to still be able to easily access it
544in case I kill (cut or copy) something else inside Emacs before
545yanking (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
560Lazy people would prefer to type fewer keystrokes, especially for yes
561or 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
569Firstly, let Emacs know that I'd like to have =*scratch*= as my
570startup buffer.
571
572#+begin_src emacs-lisp
573(setq initial-buffer-choice t)
574#+end_src
575
576Now let's customize the =*scratch*= buffer a bit. First off, I don't
577need the default hint.
578
579#+begin_src emacs-lisp
580(setq initial-scratch-message nil)
581#+end_src
582
583Also, let's use Text mode as the major mode, in case I want to
584customize it (=*scratch*='s default major mode, Fundamental mode,
585can't really be customized).
586
587#+begin_src emacs-lisp
588(setq initial-major-mode 'text-mode)
589#+end_src
590
591Inhibit 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
597I don't really need to see the startup screen or echo area message
598either.
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
608Show either the file name or the buffer name (in case the buffer isn't
609visiting 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
621Emacs' default backup settings aren't that great. Let's use more
622sensible options. See documentation for the ~make-backup-file~
623variable.
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
633Enable 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
652Enable =winner-mode=.
653
654#+begin_src emacs-lisp
655(winner-mode 1)
656#+end_src
657
f79ed7c6
AB
658*** Don’t display =*compilation*= on success
659
660Based on https://stackoverflow.com/a/17788551, with changes to use
661=cl-letf= instead of the now obsolete =flet=.
c44ace12
AB
662
663#+begin_src emacs-lisp
f1516d51 664(with-eval-after-load 'compile
c5d8bb25 665 (defun a/compilation-finish-function (buffer outstr)
5e07a091
AB
666 (unless (string-match "finished" outstr)
667 (switch-to-buffer-other-window buffer))
668 t)
f79ed7c6 669
c5d8bb25 670 (setq compilation-finish-functions #'a/compilation-finish-function)
f79ed7c6 671
5e07a091 672 (require 'cl-macs)
f79ed7c6 673
5e07a091
AB
674 (defadvice compilation-start
675 (around inhibit-display
676 (command &optional mode name-function highlight-regexp))
677 (if (not (string-match "^\\(find\\|grep\\)" command))
0571b240 678 (cl-letf (((symbol-function 'display-buffer) #'ignore))
5e07a091
AB
679 (save-window-excursion ad-do-it))
680 ad-do-it))
681 (ad-activate 'compilation-start))
5fece105
AB
682#+end_src
683
684*** Search for non-ASCII characters
685
686I’d like non-ASCII characters such as ‘’“”«»‹›áⓐ𝒶 to be selected when
687I search for their ASCII counterpart. Shoutout to [[http://endlessparentheses.com/new-in-emacs-25-1-easily-search-non-ascii-characters.html][endlessparentheses]]
688for 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
96b3e55d
AB
697*** Cursor shape
698
699#+begin_src emacs-lisp
700(setq-default cursor-type 'bar)
701#+end_src
702
5fece105
AB
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
731The packages in this section are absolutely essential to my everyday
732workflow, and they play key roles in how I do my computing. They
733immensely enhance the Emacs experience for me; both using Emacs, and
734customizing 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
12652893
AB
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]]
5fece105
AB
791
792#+begin_quote
793Org mode is for keeping notes, maintaining TODO lists, planning
794projects, and authoring documents with a fast and effective plain-text
795system.
796#+end_quote
797
798In 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)
815c338b
AB
809 org-use-speed-commands t
810 org-startup-folded 'content
811 org-catch-invisible-edits 'show-and-error
5fece105
AB
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
c5d8bb25 859=a/async-babel-tangle= is a function closely inspired by [[https://github.com/dieggsy/dotfiles/tree/cc10edf7701958eff1cd94d4081da544d882a28c/emacs.d#dotfiles][dieggsy's
5fece105
AB
860d/async-babel-tangle]] which uses [[https://github.com/jwiegley/emacs-async][async]] to asynchronously tangle an org
861file.
862
863#+begin_src emacs-lisp
ab6781dd 864(with-eval-after-load 'org
c5d8bb25 865 (defvar a/show-async-tangle-results nil
5fece105
AB
866 "Keep *emacs* async buffers around for later inspection.")
867
c5d8bb25 868 (defvar a/show-async-tangle-time nil
5fece105
AB
869 "Show the time spent tangling the file.")
870
c5d8bb25 871 (defvar a/async-tangle-post-compile "make ti"
5fece105
AB
872 "If non-nil, pass to `compile' after successful tangle.")
873
c5d8bb25 874 (defun a/async-babel-tangle ()
5fece105
AB
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))
0571b240
AB
880 ;; (async-quiet-switch "-q")
881 )
5fece105
AB
882 (async-start
883 `(lambda ()
884 (require 'org)
885 (org-babel-tangle-file ,file))
c5d8bb25 886 (unless a/show-async-tangle-results
5fece105
AB
887 `(lambda (result)
888 (if result
889 (progn
890 (message "Tangled %s%s"
891 ,file-nodir
c5d8bb25 892 (if a/show-async-tangle-time
5fece105
AB
893 (format " (%.3fs)"
894 (float-time (time-subtract (current-time)
895 ',file-tangle-start-time)))
896 ""))
c5d8bb25
AB
897 (when a/async-tangle-post-compile
898 (compile a/async-tangle-post-compile)))
5fece105
AB
899 (message "Tangling %s failed" ,file-nodir))))))))
900
901(add-to-list
902 'safe-local-variable-values
c5d8bb25 903 '(eval add-hook 'after-save-hook #'a/async-babel-tangle 'append 'local))
5fece105
AB
904#+end_src
905
906*** [[https://magit.vc/][Magit]]
907
908#+begin_quote
909It's Magit! A Git porcelain inside Emacs.
910#+end_quote
911
912Not 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
12652893 934*** COMMENT [[https://github.com/abo-abo/swiper][Ivy]] (and friends)
5fece105
AB
935
936#+begin_quote
937Ivy - a generic completion frontend for Emacs, Swiper - isearch with
938an overview, and more. Oh, man!
939#+end_quote
940
941There'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
f2a57944
AB
968 :bind (("C-s" . swiper)
969 ("C-r" . swiper)
970 ("C-S-s" . isearch-forward)))
5fece105
AB
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
12652893 990*** COMMENT eshell
5fece105
AB
991
992#+begin_src emacs-lisp
993(use-package eshell
994 :defer 1
995 :commands eshell
23cd3185 996 :bind ("C-c a s e" . eshell)
5fece105
AB
997 :config
998 (eval-when-compile (defvar eshell-prompt-regexp))
c5d8bb25 999 (defun a/eshell-quit-or-delete-char (arg)
5fece105
AB
1000 (interactive "p")
1001 (if (and (eolp) (looking-back eshell-prompt-regexp nil))
1002 (eshell-life-is-too-much)
1003 (delete-char arg)))
1004
c5d8bb25 1005 (defun a/eshell-clear ()
5fece105
AB
1006 (interactive)
1007 (let ((inhibit-read-only t))
1008 (erase-buffer))
1009 (eshell-send-input))
1010
c5d8bb25 1011 (defun a/eshell-setup ()
5fece105 1012 (make-local-variable 'company-idle-delay)
59ff41b6
AB
1013 (defvar company-idle-delay)
1014 (setq company-idle-delay nil)
5fece105 1015 (bind-keys :map eshell-mode-map
c5d8bb25
AB
1016 ("C-d" . a/eshell-quit-or-delete-char)
1017 ("C-S-l" . a/eshell-clear)
5fece105
AB
1018 ("M-r" . counsel-esh-history)
1019 ([tab] . company-complete)))
1020
c5d8bb25 1021 :hook (eshell-mode . a/eshell-setup)
5fece105
AB
1022 :custom
1023 (eshell-hist-ignoredups t)
1024 (eshell-input-filter 'eshell-input-filter-initial-space))
1025#+end_src
1026
12652893 1027*** COMMENT Ibuffer
5fece105
AB
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))
4e0afb99
AB
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)))
5fece105
AB
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)
4e0afb99
AB
1069 (mode . shell-mode)
1070 (mode . term-mode)))
5fece105
AB
1071 ("programming"
1072 (or
1073 (mode . python-mode)
4e0afb99 1074 (mode . c-mode)
5fece105 1075 (mode . c++-mode)
4e0afb99
AB
1076 (mode . emacs-lisp-mode)
1077 (mode . scheme-mode)
1078 (mode . haskell-mode)
1079 (mode . lean-mode)))
5fece105
AB
1080 ("emacs"
1081 (or
1082 (name . "^\\*scratch\\*$")
4e0afb99 1083 (name . "^\\*Messages\\*$"))))))
5fece105
AB
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
12652893 1098*** COMMENT Outline
5fece105
AB
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)
c5d8bb25 1110 :prefix-map a/outline-prefix-map
5fece105
AB
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
12652893 1120* COMMENT Borg's =layer/essentials=
673d5faa
AB
1121:PROPERTIES:
1122:CUSTOM_ID: borg-essentials
1123:END:
5fece105 1124
5fece105
AB
1125TODO: 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
3b8e0d03
AB
1137(use-package ls-lisp
1138 :custom (ls-lisp-dirs-first t))
1139
5fece105
AB
1140(use-package dired
1141 :defer t
cb0c13d0 1142 :config
3b8e0d03
AB
1143 (setq dired-listing-switches "-alh"
1144 ls-lisp-use-insert-directory-program nil)
cb0c13d0
AB
1145
1146 ;; easily diff 2 marked files
1147 ;; https://oremacs.com/2017/03/18/dired-ediff/
1148 (defun dired-ediff-files ()
1149 (interactive)
7bd8b3a2 1150 (defvar ediff-after-quit-hook-internal)
cb0c13d0
AB
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
0884b63b
AB
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))
5fece105
AB
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
5fece105
AB
1239#+end_src
1240
12652893 1241* COMMENT Editing
673d5faa
AB
1242:PROPERTIES:
1243:CUSTOM_ID: editing
1244:END:
5fece105 1245
5fece105
AB
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
12652893 1265* COMMENT Syntax and spell checking
673d5faa
AB
1266:PROPERTIES:
1267:CUSTOM_ID: syntax-spell-checking
1268:END:
5fece105 1269
5fece105
AB
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
9678e6da 1311
12652893 1312* COMMENT Programming modes
673d5faa
AB
1313:PROPERTIES:
1314:CUSTOM_ID: programming-modes
1315:END:
5fece105 1316
5fece105
AB
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
1376Emacs bindings for [[https://github.com/ndmitchell/hlint][hlint]]'s refactor option. This requires the refact
1377executable 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
1400Currently using =flycheck-haskell= with the =haskell-hlint= checker
1401instead.
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
c5d8bb25 1567 (a/setq-every 2
5fece105
AB
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
9cebbd53 1586** COMMENT Java
5fece105
AB
1587
1588*** meghanada
1589
9cebbd53 1590#+begin_src emacs-lisp
5fece105
AB
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
1602dependencies:
1603
1604ace-window
1605avy
1606bui
1607company-lsp
1608dap-mode
1609lsp-java
1610lsp-mode
1611lsp-ui
1612pfuture
1613tree-mode
1614treemacs
1615#+end_comment
1616
9cebbd53 1617#+begin_src emacs-lisp
5fece105
AB
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
5492a3dc
AB
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
12652893 1684* COMMENT Emacs enhancements
673d5faa
AB
1685:PROPERTIES:
1686:CUSTOM_ID: emacs-enhancements
1687:END:
5fece105 1688
5fece105
AB
1689** [[https://github.com/justbur/emacs-which-key][which-key]]
1690
1691#+begin_quote
1692Emacs package that displays available keybindings in popup
1693#+end_quote
1694
1695#+begin_src emacs-lisp
1696(use-package which-key
1697 :defer 1
169947ac
AB
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))
5fece105
AB
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
21ad71fa 1752** smart-mode-line
5fece105
AB
1753
1754#+begin_src emacs-lisp
21ad71fa
AB
1755(use-package smart-mode-line
1756 :config
1757 (sml/setup))
5fece105
AB
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
c5d8bb25 1769(defun a/lights-on ()
5fece105
AB
1770 "Enable my favourite light theme."
1771 (interactive)
21ad71fa
AB
1772 (mapc #'disable-theme custom-enabled-themes)
1773 (load-theme 'tangomod t)
1774 (sml/apply-theme 'automatic))
5fece105 1775
c5d8bb25 1776(defun a/lights-off ()
5fece105
AB
1777 "Go dark."
1778 (interactive)
21ad71fa
AB
1779 (mapc #'disable-theme custom-enabled-themes)
1780 (load-theme 'doom-tomorrow-night t)
1781 (sml/apply-theme 'automatic))
5fece105
AB
1782
1783(bind-keys
c5d8bb25
AB
1784 ("s-t d" . a/lights-off)
1785 ("s-t l" . a/lights-on))
5fece105
AB
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
c371adda
AB
1831 :after #'my-projectile-invalidate-cache)))
1832 :custom (projectile-completion-system 'ivy))
5fece105
AB
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
5fece105
AB
1848** [[https://github.com/EricCrosson/unkillable-scratch][unkillable-scratch]]
1849
1850Make =*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
c5d8bb25 1874 (:prefix-map a/boxquote-prefix-map
5fece105
AB
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
1898Also see [[https://www.emacswiki.org/emacs/rebox2][rebox2]].
1899
5fece105
AB
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
5fece105
AB
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
169947ac 1962 :bind (("C-c a s m" . multi-term-dedicated-toggle)
a219024e
AB
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))
5fece105
AB
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
f2a57944
AB
2000** expand-region
2001
2002#+begin_src emacs-lisp
2003(use-package expand-region
2004 :bind ("C-=" . er/expand-region))
2005#+end_src
2006
64e429b5
AB
2007** multiple-cursors
2008
2009#+begin_src emacs-lisp
2010(use-package multiple-cursors
1bbc615c
AB
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))))
64e429b5 2019#+end_src
0884b63b 2020
12652893 2021* COMMENT Email
673d5faa
AB
2022:PROPERTIES:
2023:CUSTOM_ID: email
2024:END:
5fece105 2025
5fece105 2026#+begin_src emacs-lisp
c5d8bb25 2027(defvar a/maildir (expand-file-name "~/mail/"))
ab6781dd 2028(with-eval-after-load 'recentf
c5d8bb25 2029 (add-to-list 'recentf-exclude a/maildir))
5fece105
AB
2030#+end_src
2031
2032** Gnus
2033
2034#+begin_src emacs-lisp
2035(setq
c5d8bb25
AB
2036 a/gnus-init-file (no-littering-expand-etc-file-name "gnus")
2037 mail-user-agent 'gnus-user-agent
2038 read-mail-command 'gnus)
5fece105
AB
2039
2040(use-package gnus
1bbc615c 2041 :bind (("s-m" . gnus)
a0801748 2042 ("s-M" . gnus-unplugged))
5fece105
AB
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
c5d8bb25 2104 :prefix-map a/gnus-summary-prefix-map
5fece105
AB
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))
c5d8bb25 2115 :hook (gnus-summary-mode . a/no-mouse-autoselect-window))
5fece105
AB
2116
2117(use-package gnus-msg
2118 :config
2119 (setq gnus-posting-styles
2120 '((".*"
2121 (address "amin@aminb.org")
2122 (body "\nBest,\namin\n")
c5d8bb25 2123 (eval (setq a/message-cite-say-hi t)))
5fece105
AB
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")
c5d8bb25 2129 (eval (setq a/message-cite-say-hi nil)))
5fece105
AB
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
c5d8bb25 2167 (defconst a/message-cite-style-format "On %Y-%m-%d %l:%M %p, %N wrote:")
5fece105
AB
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
c5d8bb25
AB
2176 (if a/message-cite-say-hi
2177 (concat "Hi %F,\n\n" a/message-cite-style-format)
2178 a/message-cite-style-format)))
5fece105
AB
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
ab6781dd 2200(with-eval-after-load 'mml-sec
5fece105
AB
2201 (setq mml-secure-openpgp-encrypt-to-self t
2202 mml-secure-openpgp-sign-with-sender t))
2203#+end_src
2204
2205** footnote
2206
2207Convenient footnotes in =message-mode=.
2208
2209#+begin_src emacs-lisp
2210(use-package footnote
2211 :after message
2212 :bind
2213 (:map message-mode-map
c5d8bb25 2214 :prefix-map a/footnote-prefix-map
5fece105
AB
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
490554d3
AB
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"))
ab6781dd 2237 (with-eval-after-load 'swiper
490554d3
AB
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
9cebbd53 2281** COMMENT message-x
5fece105
AB
2282
2283#+begin_src emacs-lisp
2284(use-package message-x
5fece105
AB
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
9cebbd53 2296** COMMENT gnus-harvest
5fece105
AB
2297
2298#+begin_src emacs-lisp
2299(use-package gnus-harvest
5fece105
AB
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
12652893 2308* COMMENT Blogging
673d5faa
AB
2309:PROPERTIES:
2310:CUSTOM_ID: blogging
2311:END:
5fece105 2312
5fece105
AB
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
5fece105
AB
2328Display 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)
c5d8bb25 2333 a/before-user-init-time)))
5fece105
AB
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:
12652893 2347# eval: ;; (add-hook 'after-save-hook #'a/async-babel-tangle 'append 'local)
6b569be8 2348# eval: (typo-mode -1)
5fece105 2349# End: