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