[emacs][wip] straight.el: don’t check for package changes on startup
[~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=
193
74c5f353
AB
194#+begin_quote
195Next-generation, purely functional package manager for the Emacs
196hacker.
197#+end_quote
198
199=straight.el= allows me to have a fully reproducible Emacs setup.
200
12652893 201#+begin_src emacs-lisp
c145ffd9
AB
202;; Main engine start...
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
221;; Solid rocket booster ignition...
222
223(defun a/build-init ()
224 (a/bootstrap-straight)
225 (byte-compile-file "init.el"))
226
227(a/bootstrap-straight)
228
229;; We have lift off!
12652893
AB
230
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.
237
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))
04a268e3
AB
266
267(setq use-package-always-defer t)
5fece105
AB
268#+end_src
269
e724cc40 270*** Epkg
5fece105
AB
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
6540f2c4
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
f59c2780
AB
747*** [[https://orgmode.org/][Org]]
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
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
AB
759
760#+begin_src emacs-lisp
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
2ec331fc 802 :defer 0.5
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)
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
74c5f353 832(use-feature ox-latex
5fece105
AB
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
74c5f353 849(use-feature ox-beamer
bf233291 850 :demand
5fece105 851 :after ox)
5fece105
AB
852#+end_src
853
854**** asynchronous tangle
855
c5d8bb25 856=a/async-babel-tangle= is a function closely inspired by [[https://github.com/dieggsy/dotfiles/tree/cc10edf7701958eff1cd94d4081da544d882a28c/emacs.d#dotfiles][dieggsy's
5fece105
AB
857d/async-babel-tangle]] which uses [[https://github.com/jwiegley/emacs-async][async]] to asynchronously tangle an org
858file.
859
860#+begin_src emacs-lisp
ab6781dd 861(with-eval-after-load 'org
c5d8bb25 862 (defvar a/show-async-tangle-results nil
5fece105
AB
863 "Keep *emacs* async buffers around for later inspection.")
864
c5d8bb25 865 (defvar a/show-async-tangle-time nil
5fece105
AB
866 "Show the time spent tangling the file.")
867
c145ffd9 868 (defvar a/async-tangle-post-compile "make bi"
5fece105
AB
869 "If non-nil, pass to `compile' after successful tangle.")
870
c145ffd9 871 (defvar a/async-tangle-byte-recompile nil
c31b1ac0
AB
872 "If non-nil, byte-recompile the file on 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 880 ;; (async-quiet-switch "-q")
c31b1ac0 881 (file-noext (file-name-sans-extension file)))
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
c31b1ac0 890 (setq byte-compile-warnings '(not noruntime unresolved))
5fece105
AB
891 (message "Tangled %s%s"
892 ,file-nodir
c5d8bb25 893 (if a/show-async-tangle-time
5fece105
AB
894 (format " (%.3fs)"
895 (float-time (time-subtract (current-time)
896 ',file-tangle-start-time)))
897 ""))
c5d8bb25 898 (when a/async-tangle-post-compile
c31b1ac0
AB
899 (compile a/async-tangle-post-compile))
900 (when a/async-tangle-byte-recompile
901 (byte-recompile-file (concat ,file-noext ".el"))))
5fece105
AB
902 (message "Tangling %s failed" ,file-nodir))))))))
903
904(add-to-list
905 'safe-local-variable-values
c5d8bb25 906 '(eval add-hook 'after-save-hook #'a/async-babel-tangle 'append 'local))
5fece105
AB
907#+end_src
908
909*** [[https://magit.vc/][Magit]]
910
911#+begin_quote
912It's Magit! A Git porcelain inside Emacs.
913#+end_quote
914
915Not just how I do git, but /the/ way to do git.
916
917#+begin_src emacs-lisp
918(use-package magit
919 :defer 1
920 :bind (("C-x g" . magit-status)
921 ("s-g s" . magit-status)
922 ("s-g l" . magit-log-buffer-file))
923 :config
924 (magit-add-section-hook 'magit-status-sections-hook
925 'magit-insert-modules
926 'magit-insert-stashes
927 'append)
928 (setq
929 magit-repository-directories '(("~/.emacs.d/" . 0)
930 ("~/src/git/" . 1)))
931 (nconc magit-section-initial-visibility-alist
932 '(([unpulled status] . show)
933 ([unpushed status] . show)))
934 :custom-face (magit-diff-file-heading ((t (:weight normal)))))
935#+end_src
936
d2cb5b82
AB
937*** recentf
938
939Recently opened files.
940
941#+begin_src emacs-lisp
942(use-feature recentf
943 :defer 0.5
944 :config
945 (add-to-list 'recentf-exclude "^/\\(?:ssh\\|su\\|sudo\\)?:")
946 (setq recentf-max-saved-items 40))
947#+end_src
948
cc7b1957
AB
949*** smex
950
951#+begin_quote
952A smart M-x enhancement for Emacs.
953#+end_quote
954
955Mostly because =counsel= needs it to remember history.
956
957#+begin_src emacs-lisp
04a268e3 958(use-package smex)
cc7b1957
AB
959#+end_src
960
69b24a1e 961*** [[https://github.com/abo-abo/swiper][Ivy]] (and friends)
5fece105
AB
962
963#+begin_quote
964Ivy - a generic completion frontend for Emacs, Swiper - isearch with
965an overview, and more. Oh, man!
966#+end_quote
967
968There's no way I could top that, so I won't attempt to.
969
970**** Ivy
971
972#+begin_src emacs-lisp
973(use-package ivy
69b24a1e 974 :defer 0.6
5fece105
AB
975 :bind
976 (:map ivy-minibuffer-map
977 ([escape] . keyboard-escape-quit)
978 ([S-up] . ivy-previous-history-element)
979 ([S-down] . ivy-next-history-element)
980 ("DEL" . ivy-backward-delete-char))
981 :config
982 (setq ivy-wrap t)
983 (ivy-mode 1)
984 ;; :custom-face
985 ;; (ivy-minibuffer-match-face-2 ((t (:background "#e99ce8" :weight semi-bold))))
986 ;; (ivy-minibuffer-match-face-3 ((t (:background "#bbbbff" :weight semi-bold))))
987 ;; (ivy-minibuffer-match-face-4 ((t (:background "#ffbbff" :weight semi-bold))))
988)
989#+end_src
990
991**** Swiper
992
993#+begin_src emacs-lisp
994(use-package swiper
69b24a1e 995 :after ivy
f2a57944
AB
996 :bind (("C-s" . swiper)
997 ("C-r" . swiper)
998 ("C-S-s" . isearch-forward)))
5fece105
AB
999#+end_src
1000
1001**** Counsel
1002
1003#+begin_src emacs-lisp
1004(use-package counsel
69b24a1e 1005 :after ivy
5fece105
AB
1006 :bind (([remap execute-extended-command] . counsel-M-x)
1007 ([remap find-file] . counsel-find-file)
1008 ("s-r" . counsel-recentf)
1009 ("C-c x" . counsel-M-x)
1010 ("C-c f ." . counsel-find-file)
1011 :map minibuffer-local-map
1012 ("C-r" . counsel-minibuffer-history))
1013 :config
1014 (counsel-mode 1)
1015 (defalias 'locate #'counsel-locate))
1016#+end_src
1017
69b24a1e 1018*** eshell
5fece105
AB
1019
1020#+begin_src emacs-lisp
69b24a1e 1021(use-feature eshell
5fece105
AB
1022 :defer 1
1023 :commands eshell
23cd3185 1024 :bind ("C-c a s e" . eshell)
5fece105
AB
1025 :config
1026 (eval-when-compile (defvar eshell-prompt-regexp))
c5d8bb25 1027 (defun a/eshell-quit-or-delete-char (arg)
5fece105
AB
1028 (interactive "p")
1029 (if (and (eolp) (looking-back eshell-prompt-regexp nil))
1030 (eshell-life-is-too-much)
1031 (delete-char arg)))
1032
c5d8bb25 1033 (defun a/eshell-clear ()
5fece105
AB
1034 (interactive)
1035 (let ((inhibit-read-only t))
1036 (erase-buffer))
1037 (eshell-send-input))
1038
c5d8bb25 1039 (defun a/eshell-setup ()
5fece105 1040 (make-local-variable 'company-idle-delay)
59ff41b6
AB
1041 (defvar company-idle-delay)
1042 (setq company-idle-delay nil)
5fece105 1043 (bind-keys :map eshell-mode-map
c5d8bb25
AB
1044 ("C-d" . a/eshell-quit-or-delete-char)
1045 ("C-S-l" . a/eshell-clear)
5fece105
AB
1046 ("M-r" . counsel-esh-history)
1047 ([tab] . company-complete)))
1048
c5d8bb25 1049 :hook (eshell-mode . a/eshell-setup)
5fece105
AB
1050 :custom
1051 (eshell-hist-ignoredups t)
1052 (eshell-input-filter 'eshell-input-filter-initial-space))
1053#+end_src
1054
69b24a1e 1055*** Ibuffer
5fece105
AB
1056
1057#+begin_src emacs-lisp
69b24a1e 1058(use-feature ibuffer
5fece105
AB
1059 :bind
1060 (("C-x C-b" . ibuffer-other-window)
1061 :map ibuffer-mode-map
1062 ("P" . ibuffer-backward-filter-group)
1063 ("N" . ibuffer-forward-filter-group)
1064 ("M-p" . ibuffer-do-print)
1065 ("M-n" . ibuffer-do-shell-command-pipe-replace))
1066 :config
1067 ;; Use human readable Size column instead of original one
1068 (define-ibuffer-column size-h
1069 (:name "Size" :inline t)
1070 (cond
1071 ((> (buffer-size) 1000000) (format "%7.1fM" (/ (buffer-size) 1000000.0)))
1072 ((> (buffer-size) 100000) (format "%7.0fk" (/ (buffer-size) 1000.0)))
1073 ((> (buffer-size) 1000) (format "%7.1fk" (/ (buffer-size) 1000.0)))
1074 (t (format "%8d" (buffer-size)))))
1075 :custom
1076 (ibuffer-saved-filter-groups
1077 '(("default"
1078 ("dired" (mode . dired-mode))
1079 ("org" (mode . org-mode))
4e0afb99
AB
1080 ("gnus"
1081 (or
1082 (mode . gnus-group-mode)
1083 (mode . gnus-summary-mode)
1084 (mode . gnus-article-mode)
1085 ;; not really, but...
1086 (mode . message-mode)))
5fece105
AB
1087 ("web"
1088 (or
1089 (mode . web-mode)
1090 (mode . css-mode)
1091 (mode . scss-mode)
1092 (mode . js2-mode)))
1093 ("shell"
1094 (or
1095 (mode . eshell-mode)
4e0afb99
AB
1096 (mode . shell-mode)
1097 (mode . term-mode)))
5fece105
AB
1098 ("programming"
1099 (or
1100 (mode . python-mode)
4e0afb99 1101 (mode . c-mode)
5fece105 1102 (mode . c++-mode)
4e0afb99
AB
1103 (mode . emacs-lisp-mode)
1104 (mode . scheme-mode)
1105 (mode . haskell-mode)
1106 (mode . lean-mode)))
5fece105
AB
1107 ("emacs"
1108 (or
1109 (name . "^\\*scratch\\*$")
4e0afb99 1110 (name . "^\\*Messages\\*$"))))))
5fece105
AB
1111 (ibuffer-formats
1112 '((mark modified read-only locked " "
1113 (name 18 18 :left :elide)
1114 " "
1115 (size-h 9 -1 :right)
1116 " "
1117 (mode 16 16 :left :elide)
1118 " " filename-and-process)
1119 (mark " "
1120 (name 16 -1)
1121 " " filename)))
1122 :hook (ibuffer . (lambda () (ibuffer-switch-to-saved-filter-groups "default"))))
1123#+end_src
1124
69b24a1e 1125*** Outline
5fece105
AB
1126
1127#+begin_src emacs-lisp
69b24a1e 1128(use-feature outline
5fece105
AB
1129 :hook (prog-mode . outline-minor-mode)
1130 :bind
1131 (:map
1132 outline-minor-mode-map
1133 ("<s-tab>" . outline-toggle-children)
1134 ("M-p" . outline-previous-visible-heading)
1135 ("M-n" . outline-next-visible-heading)
c5d8bb25 1136 :prefix-map a/outline-prefix-map
5fece105
AB
1137 :prefix "s-o"
1138 ("TAB" . outline-toggle-children)
1139 ("a" . outline-hide-body)
1140 ("H" . outline-hide-body)
1141 ("S" . outline-show-all)
1142 ("h" . outline-hide-subtree)
1143 ("s" . outline-show-subtree)))
1144#+end_src
1145
41d73018 1146*** Dired
5fece105
AB
1147
1148#+begin_src emacs-lisp
633dee4d 1149(use-feature ls-lisp
3b8e0d03
AB
1150 :custom (ls-lisp-dirs-first t))
1151
633dee4d 1152(use-feature dired
cb0c13d0 1153 :config
3b8e0d03
AB
1154 (setq dired-listing-switches "-alh"
1155 ls-lisp-use-insert-directory-program nil)
cb0c13d0
AB
1156
1157 ;; easily diff 2 marked files
1158 ;; https://oremacs.com/2017/03/18/dired-ediff/
1159 (defun dired-ediff-files ()
1160 (interactive)
19733078 1161 (require 'dired-aux)
7bd8b3a2 1162 (defvar ediff-after-quit-hook-internal)
cb0c13d0
AB
1163 (let ((files (dired-get-marked-files))
1164 (wnd (current-window-configuration)))
1165 (if (<= (length files) 2)
1166 (let ((file1 (car files))
1167 (file2 (if (cdr files)
1168 (cadr files)
1169 (read-file-name
1170 "file: "
1171 (dired-dwim-target-directory)))))
1172 (if (file-newer-than-file-p file1 file2)
1173 (ediff-files file2 file1)
1174 (ediff-files file1 file2))
1175 (add-hook 'ediff-after-quit-hook-internal
1176 (lambda ()
1177 (setq ediff-after-quit-hook-internal nil)
1178 (set-window-configuration wnd))))
1179 (error "no more than 2 files should be marked"))))
1180 :bind (:map dired-mode-map
19733078 1181 ("b" . dired-up-directory)
0884b63b
AB
1182 ("e" . dired-ediff-files)
1183 ("E" . dired-toggle-read-only)
1184 ("\\" . dired-hide-details-mode)
1185 ("z" . (lambda ()
1186 (interactive)
1187 (a/dired-start-process "zathura"))))
1188 :hook (dired-mode . dired-hide-details-mode))
41d73018
AB
1189#+end_src
1190
a8d0c7ad
AB
1191*** Help
1192
1193#+begin_src emacs-lisp
1194(use-feature help
a8d0c7ad
AB
1195 :config
1196 (temp-buffer-resize-mode)
1197 (setq help-window-select t))
1198#+end_src
1199
9f120b4f 1200*** Tramp
41d73018
AB
1201
1202#+begin_src emacs-lisp
633dee4d 1203(use-feature tramp
5fece105
AB
1204 :config
1205 (add-to-list 'tramp-default-proxies-alist '(nil "\\`root\\'" "/ssh:%h:"))
1206 (add-to-list 'tramp-default-proxies-alist '("localhost" nil nil))
1207 (add-to-list 'tramp-default-proxies-alist
1208 (list (regexp-quote (system-name)) nil nil)))
9f120b4f 1209#+end_src
5fece105 1210
9f120b4f
AB
1211*** Dash
1212
1213#+begin_src emacs-lisp
1214(use-package dash
9f120b4f 1215 :config (dash-enable-font-lock))
5fece105
AB
1216#+end_src
1217
f8e11b0e 1218* Editing
673d5faa
AB
1219:PROPERTIES:
1220:CUSTOM_ID: editing
1221:END:
5fece105 1222
5023c310
AB
1223** =diff-hl=
1224
1225Highlight uncommitted changes in the left fringe.
1226
1227#+begin_src emacs-lisp
1228(use-package diff-hl
1229 :config
1230 (setq diff-hl-draw-borders nil)
1231 (global-diff-hl-mode)
1232 :hook (magit-post-refresh . diff-hl-magit-post-refresh))
1233#+end_src
1234
a8d0c7ad
AB
1235** ElDoc
1236
1237Display Lisp objects at point in the echo area.
1238
1239#+begin_src emacs-lisp
1240(use-feature eldoc
1241 :when (version< "25" emacs-version)
1242 :config (global-eldoc-mode))
1243#+end_src
1244
6540f2c4
AB
1245** paren
1246
1247Highlight matching parens.
1248
1249#+begin_src emacs-lisp
1250(use-feature paren
1251 :config (show-paren-mode))
1252#+end_src
1253
5c1cce80
AB
1254** simple (for column numbers)
1255
1256#+begin_src emacs-lisp
1257(use-feature simple
1258 :config (column-number-mode))
1259#+end_src
1260
f8e11b0e
AB
1261** =savehist=
1262
1263Save minibuffer history.
1264
1265#+begin_src emacs-lisp
1266(use-feature savehist
1267 :config (savehist-mode))
1268#+end_src
1269
1270** =saveplace=
1271
1272Automatically save place in each file.
1273
1274#+begin_src emacs-lisp
1275(use-feature saveplace
1276 :when (version< "25" emacs-version)
1277 :config (save-place-mode))
1278#+end_src
1279
5097c774
AB
1280** =prog-mode=
1281
1282#+begin_src emacs-lisp
1283(use-feature prog-mode
1284 :config (global-prettify-symbols-mode)
1285 (defun indicate-buffer-boundaries-left ()
1286 (setq indicate-buffer-boundaries 'left))
1287 (add-hook 'prog-mode-hook #'indicate-buffer-boundaries-left))
1288#+end_src
1289
1290** =text-mode=
1291
1292#+begin_src emacs-lisp
1293(use-feature text-mode
1294 :hook ((text-mode . indicate-buffer-boundaries-left)
1295 (text-mode . abbrev-mode)))
1296#+end_src
1297
ab0674a2 1298** Company
5fece105
AB
1299
1300#+begin_src emacs-lisp
1301(use-package company
1302 :defer 1
1303 :bind
1304 (:map company-active-map
1305 ([tab] . company-complete-common-or-cycle)
1306 ([escape] . company-abort))
1307 :custom
1308 (company-minimum-prefix-length 1)
1309 (company-selection-wrap-around t)
1310 (company-dabbrev-char-regexp "\\sw\\|\\s_\\|[-_]")
1311 (company-dabbrev-downcase nil)
1312 (company-dabbrev-ignore-case nil)
1313 :config
1314 (global-company-mode t))
1315#+end_src
1316
de000e57 1317** Flycheck
5fece105 1318
5fece105
AB
1319#+begin_src emacs-lisp
1320(use-package flycheck
1321 :defer 3
1322 :hook (prog-mode . flycheck-mode)
1323 :bind
1324 (:map flycheck-mode-map
1325 ("M-P" . flycheck-previous-error)
1326 ("M-N" . flycheck-next-error))
1327 :config
1328 ;; Use the load-path from running Emacs when checking elisp files
1329 (setq flycheck-emacs-lisp-load-path 'inherit)
1330
1331 ;; Only flycheck when I actually save the buffer
1332 (setq flycheck-check-syntax-automatically '(mode-enabled save)))
1333
1334;; http://endlessparentheses.com/ispell-and-apostrophes.html
1335(use-package ispell
1336 :defer 3
1337 :config
1338 ;; ’ can be part of a word
1339 (setq ispell-local-dictionary-alist
1340 `((nil "[[:alpha:]]" "[^[:alpha:]]"
1341 "['\x2019]" nil ("-B") nil utf-8)))
1342 ;; don't send ’ to the subprocess
1343 (defun endless/replace-apostrophe (args)
1344 (cons (replace-regexp-in-string
1345 "’" "'" (car args))
1346 (cdr args)))
1347 (advice-add #'ispell-send-string :filter-args
1348 #'endless/replace-apostrophe)
1349
1350 ;; convert ' back to ’ from the subprocess
1351 (defun endless/replace-quote (args)
1352 (if (not (derived-mode-p 'org-mode))
1353 args
1354 (cons (replace-regexp-in-string
1355 "'" "’" (car args))
1356 (cdr args))))
1357 (advice-add #'ispell-parse-output :filter-args
1358 #'endless/replace-quote))
1359#+end_src
9678e6da 1360
520aebd5 1361* Programming modes
673d5faa
AB
1362:PROPERTIES:
1363:CUSTOM_ID: programming-modes
1364:END:
5fece105 1365
520aebd5
AB
1366** Lisp
1367
1368#+begin_src emacs-lisp
1369(use-feature lisp-mode
1370 :config
1371 (add-hook 'emacs-lisp-mode-hook 'outline-minor-mode)
1372 (add-hook 'emacs-lisp-mode-hook 'reveal-mode)
1373 (defun indent-spaces-mode ()
1374 (setq indent-tabs-mode nil))
1375 (add-hook 'lisp-interaction-mode-hook #'indent-spaces-mode))
1376#+end_src
1377
5fece105
AB
1378** [[http://alloytools.org][Alloy]] (with [[https://github.com/dwwmmn/alloy-mode][alloy-mode]])
1379
1380#+begin_src emacs-lisp
1381(use-package alloy-mode
520aebd5 1382 :straight (:host github :repo "dwwmmn/alloy-mode")
5fece105
AB
1383 :config (setq alloy-basic-offset 2))
1384#+end_src
1385
1386** [[https://coq.inria.fr][Coq]] (with [[https://github.com/ProofGeneral/PG][Proof General]])
1387
1388#+begin_src emacs-lisp
426cec7a
AB
1389(use-package proof-site
1390 :straight proof-general)
5fece105
AB
1391#+end_src
1392
1393** [[https://leanprover.github.io][Lean]] (with [[https://github.com/leanprover/lean-mode][lean-mode]])
1394
1395#+begin_src emacs-lisp
1396(eval-when-compile (defvar lean-mode-map))
1397(use-package lean-mode
1398 :defer 1
1399 :bind (:map lean-mode-map
1400 ("S-SPC" . company-complete))
1401 :config
1402 (require 'lean-input)
1403 (setq default-input-method "Lean"
1404 lean-input-tweak-all '(lean-input-compose
1405 (lean-input-prepend "/")
1406 (lean-input-nonempty))
1407 lean-input-user-translations '(("/" "/")))
1408 (lean-input-setup))
1409 #+end_src
1410
1411** Haskell
1412
1413*** [[https://github.com/haskell/haskell-mode][haskell-mode]]
1414
1415#+begin_src emacs-lisp
1416(use-package haskell-mode
5fece105
AB
1417 :config
1418 (setq haskell-indentation-layout-offset 4
1419 haskell-indentation-left-offset 4
1420 flycheck-checker 'haskell-hlint
1421 flycheck-disabled-checkers '(haskell-stack-ghc haskell-ghc)))
1422#+end_src
1423
1424*** [[https://github.com/jyp/dante][dante]]
1425
1426#+begin_src emacs-lisp
1427(use-package dante
1428 :after haskell-mode
1429 :commands dante-mode
1430 :hook (haskell-mode . dante-mode))
1431#+end_src
1432
1433*** [[https://github.com/mpickering/hlint-refactor-mode][hlint-refactor]]
1434
1435Emacs bindings for [[https://github.com/ndmitchell/hlint][hlint]]'s refactor option. This requires the refact
1436executable from [[https://github.com/mpickering/apply-refact][apply-refact]].
1437
1438#+begin_src emacs-lisp
1439(use-package hlint-refactor
1440 :after haskell-mode
1441 :bind (:map hlint-refactor-mode-map
1442 ("C-c l b" . hlint-refactor-refactor-buffer)
1443 ("C-c l r" . hlint-refactor-refactor-at-point))
1444 :hook (haskell-mode . hlint-refactor-mode))
1445#+end_src
1446
1447*** [[https://github.com/flycheck/flycheck-haskell][flycheck-haskell]]
1448
1449#+begin_src emacs-lisp
1450(use-package flycheck-haskell
1451 :after haskell-mode)
1452#+end_src
1453
1454*** [[https://github.com/ndmitchell/hlint/blob/20e116a043f2073c57b17b24ae6364b5e433ba7e/data/hs-lint.el][hs-lint.el]]
1455:PROPERTIES:
1456:header-args+: :tangle lisp/hs-lint.el :mkdirp yes
1457:END:
1458
1459Currently using =flycheck-haskell= with the =haskell-hlint= checker
1460instead.
1461
1462#+begin_src emacs-lisp :tangle no
1463;;; hs-lint.el --- minor mode for HLint code checking
1464
1465;; Copyright 2009 (C) Alex Ott
1466;;
1467;; Author: Alex Ott <alexott@gmail.com>
1468;; Keywords: haskell, lint, HLint
1469;; Requirements:
1470;; Status: distributed under terms of GPL2 or above
1471
1472;; Typical message from HLint looks like:
1473;;
1474;; /Users/ott/projects/lang-exp/haskell/test.hs:52:1: Eta reduce
1475;; Found:
1476;; count1 p l = length (filter p l)
1477;; Why not:
1478;; count1 p = length . filter p
1479
1480
1481(require 'compile)
1482
1483(defgroup hs-lint nil
1484 "Run HLint as inferior of Emacs, parse error messages."
1485 :group 'tools
1486 :group 'haskell)
1487
1488(defcustom hs-lint-command "hlint"
1489 "The default hs-lint command for \\[hlint]."
1490 :type 'string
1491 :group 'hs-lint)
1492
1493(defcustom hs-lint-save-files t
1494 "Save modified files when run HLint or no (ask user)"
1495 :type 'boolean
1496 :group 'hs-lint)
1497
1498(defcustom hs-lint-replace-with-suggestions nil
1499 "Replace user's code with suggested replacements"
1500 :type 'boolean
1501 :group 'hs-lint)
1502
1503(defcustom hs-lint-replace-without-ask nil
1504 "Replace user's code with suggested replacements automatically"
1505 :type 'boolean
1506 :group 'hs-lint)
1507
1508(defun hs-lint-process-setup ()
1509 "Setup compilation variables and buffer for `hlint'."
1510 (run-hooks 'hs-lint-setup-hook))
1511
1512;; regex for replace suggestions
1513;;
1514;; ^\(.*?\):\([0-9]+\):\([0-9]+\): .*
1515;; Found:
1516;; \s +\(.*\)
1517;; Why not:
1518;; \s +\(.*\)
1519
1520(defvar hs-lint-regex
1521 "^\\(.*?\\):\\([0-9]+\\):\\([0-9]+\\): .*[\n\C-m]Found:[\n\C-m]\\s +\\(.*\\)[\n\C-m]Why not:[\n\C-m]\\s +\\(.*\\)[\n\C-m]"
1522 "Regex for HLint messages")
1523
1524(defun make-short-string (str maxlen)
1525 (if (< (length str) maxlen)
1526 str
1527 (concat (substring str 0 (- maxlen 3)) "...")))
1528
1529(defun hs-lint-replace-suggestions ()
1530 "Perform actual replacement of suggestions"
1531 (goto-char (point-min))
1532 (while (re-search-forward hs-lint-regex nil t)
1533 (let* ((fname (match-string 1))
1534 (fline (string-to-number (match-string 2)))
1535 (old-code (match-string 4))
1536 (new-code (match-string 5))
1537 (msg (concat "Replace '" (make-short-string old-code 30)
1538 "' with '" (make-short-string new-code 30) "'"))
1539 (bline 0)
1540 (eline 0)
1541 (spos 0)
1542 (new-old-code ""))
1543 (save-excursion
1544 (switch-to-buffer (get-file-buffer fname))
1545 (goto-char (point-min))
1546 (forward-line (1- fline))
1547 (beginning-of-line)
1548 (setf bline (point))
1549 (when (or hs-lint-replace-without-ask
1550 (yes-or-no-p msg))
1551 (end-of-line)
1552 (setf eline (point))
1553 (beginning-of-line)
1554 (setf old-code (regexp-quote old-code))
1555 (while (string-match "\\\\ " old-code spos)
1556 (setf new-old-code (concat new-old-code
1557 (substring old-code spos (match-beginning 0))
1558 "\\ *"))
1559 (setf spos (match-end 0)))
1560 (setf new-old-code (concat new-old-code (substring old-code spos)))
1561 (remove-text-properties bline eline '(composition nil))
1562 (when (re-search-forward new-old-code eline t)
1563 (replace-match new-code nil t)))))))
1564
1565(defun hs-lint-finish-hook (buf msg)
1566 "Function, that is executed at the end of HLint execution"
1567 (if hs-lint-replace-with-suggestions
1568 (hs-lint-replace-suggestions)
1569 (next-error 1 t)))
1570
1571(define-compilation-mode hs-lint-mode "HLint"
1572 "Mode for check Haskell source code."
1573 (set (make-local-variable 'compilation-process-setup-function)
1574 'hs-lint-process-setup)
1575 (set (make-local-variable 'compilation-disable-input) t)
1576 (set (make-local-variable 'compilation-scroll-output) nil)
1577 (set (make-local-variable 'compilation-finish-functions)
1578 (list 'hs-lint-finish-hook))
1579 )
1580
1581(defun hs-lint ()
1582 "Run HLint for current buffer with haskell source"
1583 (interactive)
1584 (save-some-buffers hs-lint-save-files)
1585 (compilation-start (concat hs-lint-command " \"" buffer-file-name "\"")
1586 'hs-lint-mode))
1587
1588(provide 'hs-lint)
1589;;; hs-lint.el ends here
1590#+end_src
1591
1592#+begin_src emacs-lisp :tangle no
1593(use-package hs-lint
1594 :load-path "lisp/"
1595 :bind (:map haskell-mode-map
1596 ("C-c l l" . hs-lint)))
1597#+end_src
1598
42b1fa9b 1599** Web
5fece105
AB
1600
1601*** SGML and HTML
1602
1603#+begin_src emacs-lisp
1604(use-package sgml-mode
5fece105
AB
1605 :config
1606 (setq sgml-basic-offset 2))
1607#+end_src
1608
1609*** CSS and SCSS
1610
1611#+begin_src emacs-lisp
1612(use-package css-mode
5fece105
AB
1613 :config
1614 (setq css-indent-offset 2))
1615#+end_src
1616
1617*** Web mode
1618
1619#+begin_src emacs-lisp
1620(use-package web-mode
5fece105
AB
1621 :mode "\\.html\\'"
1622 :config
c5d8bb25 1623 (a/setq-every 2
5fece105
AB
1624 web-mode-code-indent-offset
1625 web-mode-css-indent-offset
1626 web-mode-markup-indent-offset))
1627#+end_src
1628
1629*** Emmet mode
1630
1631#+begin_src emacs-lisp
1632(use-package emmet-mode
1633 :after (:any web-mode css-mode sgml-mode)
1634 :bind* (("C-)" . emmet-next-edit-point)
1635 ("C-(" . emmet-prev-edit-point))
1636 :config
1637 (unbind-key "C-j" emmet-mode-keymap)
1638 (setq emmet-move-cursor-between-quotes t)
1639 :hook (web-mode css-mode html-mode sgml-mode))
1640#+end_src
1641
9cebbd53 1642** COMMENT Java
5fece105
AB
1643
1644*** meghanada
1645
9cebbd53 1646#+begin_src emacs-lisp
5fece105
AB
1647(use-package meghanada
1648 :bind
1649 (:map meghanada-mode-map
1650 (("C-M-o" . meghanada-optimize-import)
1651 ("C-M-t" . meghanada-import-all)))
1652 :hook (java-mode . meghanada-mode))
1653#+end_src
1654
1655*** lsp-java
1656
1657#+begin_comment
1658dependencies:
1659
1660ace-window
1661avy
1662bui
1663company-lsp
1664dap-mode
1665lsp-java
1666lsp-mode
1667lsp-ui
1668pfuture
1669tree-mode
1670treemacs
1671#+end_comment
1672
9cebbd53 1673#+begin_src emacs-lisp
5fece105
AB
1674(use-package treemacs
1675 :config (setq treemacs-never-persist t))
1676
1677(use-package yasnippet
1678 :config
1679 ;; (yas-global-mode)
1680 )
1681
1682(use-package lsp-mode
1683 :init (setq lsp-eldoc-render-all nil
1684 lsp-highlight-symbol-at-point nil)
1685 )
1686
1687(use-package hydra)
1688
1689(use-package company-lsp
1690 :after company
1691 :config
1692 (setq company-lsp-cache-candidates t
1693 company-lsp-async t))
1694
1695(use-package lsp-ui
1696 :config
1697 (setq lsp-ui-sideline-update-mode 'point))
1698
1699(use-package lsp-java
1700 :config
1701 (add-hook 'java-mode-hook
1702 (lambda ()
1703 (setq-local company-backends (list 'company-lsp))))
1704
1705 (add-hook 'java-mode-hook 'lsp-java-enable)
1706 (add-hook 'java-mode-hook 'flycheck-mode)
1707 (add-hook 'java-mode-hook 'company-mode)
1708 (add-hook 'java-mode-hook 'lsp-ui-mode))
1709
1710(use-package dap-mode
1711 :after lsp-mode
1712 :config
1713 (dap-mode t)
1714 (dap-ui-mode t))
1715
1716(use-package dap-java
1717 :after (lsp-java))
1718
1719(use-package lsp-java-treemacs
1720 :after (treemacs))
1721#+end_src
1722
ff374d22 1723** geiser
5492a3dc
AB
1724
1725#+begin_src emacs-lisp
1726(use-package geiser)
1727
42b1fa9b 1728(use-feature geiser-guile
5492a3dc
AB
1729 :config
1730 (setq geiser-guile-load-path "~/src/git/guix"))
1731#+end_src
1732
ff374d22 1733** guix
5492a3dc
AB
1734
1735#+begin_src emacs-lisp
42b1fa9b 1736(use-package guix)
5492a3dc
AB
1737#+end_src
1738
e738f03b 1739* Emacs enhancements
673d5faa
AB
1740:PROPERTIES:
1741:CUSTOM_ID: emacs-enhancements
1742:END:
5fece105 1743
9f120b4f
AB
1744** man
1745
1746#+begin_src emacs-lisp
1747(use-feature man
9f120b4f
AB
1748 :config (setq Man-width 80))
1749#+end_src
1750
5fece105
AB
1751** [[https://github.com/justbur/emacs-which-key][which-key]]
1752
1753#+begin_quote
1754Emacs package that displays available keybindings in popup
1755#+end_quote
1756
1757#+begin_src emacs-lisp
1758(use-package which-key
1759 :defer 1
169947ac
AB
1760 :config
1761 (which-key-add-key-based-replacements
1762 ;; prefixes for global prefixes and minor modes
1763 "C-c @" "outline"
1764 "C-c !" "flycheck"
1765 "C-c 8" "typo"
1766 "C-c 8 -" "typo/dashes"
1767 "C-c 8 <" "typo/left-brackets"
1768 "C-c 8 >" "typo/right-brackets"
1769 "C-x 8" "unicode"
1770 "C-x a" "abbrev/expand"
1771 "C-x r" "rectangle/register/bookmark"
1772 "C-x v" "version control"
1773 ;; prefixes for my personal bindings
1774 "C-c a" "applications"
1775 "C-c a s" "shells"
e724cc40
AB
1776 "C-c P" "package-management"
1777 "C-c P e" "package-management/epkg"
1778 "C-c P s" "package-management/straight.el"
169947ac
AB
1779 "C-c c" "compile-and-comments"
1780 "C-c e" "eval"
1781 "C-c f" "files"
1782 "C-c F" "frames"
1783 "C-S-h" "help(ful)"
1784 "C-c m" "multiple-cursors"
1785 "C-c p" "projectile"
1786 "C-c p s" "projectile/search"
1787 "C-c p x" "projectile/execute"
1788 "C-c p 4" "projectile/other-window"
1789 "C-c q" "boxquote"
1790 "s-g" "magit"
1791 "s-o" "outline"
1792 "s-t" "themes")
1793
1794 ;; prefixes for major modes
1795 (which-key-add-major-mode-key-based-replacements 'message-mode
1796 "C-c f" "footnote")
1797 (which-key-add-major-mode-key-based-replacements 'org-mode
1798 "C-c C-v" "org-babel")
1799 (which-key-add-major-mode-key-based-replacements 'web-mode
1800 "C-c C-a" "web/attributes"
1801 "C-c C-b" "web/blocks"
1802 "C-c C-d" "web/dom"
1803 "C-c C-e" "web/element"
1804 "C-c C-t" "web/tags")
1805
1806 (which-key-mode))
5fece105
AB
1807#+end_src
1808
1809** theme
1810
1811#+begin_src emacs-lisp
1812(add-to-list 'custom-theme-load-path "~/.emacs.d/lisp")
1813(load-theme 'tangomod t)
1814#+end_src
1815
21ad71fa 1816** smart-mode-line
5fece105
AB
1817
1818#+begin_src emacs-lisp
21ad71fa 1819(use-package smart-mode-line
19740fc0 1820 :commands (sml/apply-theme)
5c1cce80 1821 :demand
21ad71fa
AB
1822 :config
1823 (sml/setup))
5fece105
AB
1824#+end_src
1825
1826** doom-themes
1827
1828#+begin_src emacs-lisp
1829(use-package doom-themes)
1830#+end_src
1831
1832** theme helper functions
1833
1834#+begin_src emacs-lisp
c5d8bb25 1835(defun a/lights-on ()
5fece105
AB
1836 "Enable my favourite light theme."
1837 (interactive)
21ad71fa
AB
1838 (mapc #'disable-theme custom-enabled-themes)
1839 (load-theme 'tangomod t)
1840 (sml/apply-theme 'automatic))
5fece105 1841
c5d8bb25 1842(defun a/lights-off ()
5fece105
AB
1843 "Go dark."
1844 (interactive)
21ad71fa
AB
1845 (mapc #'disable-theme custom-enabled-themes)
1846 (load-theme 'doom-tomorrow-night t)
1847 (sml/apply-theme 'automatic))
5fece105
AB
1848
1849(bind-keys
c5d8bb25
AB
1850 ("s-t d" . a/lights-off)
1851 ("s-t l" . a/lights-on))
5fece105
AB
1852#+end_src
1853
1854** [[https://github.com/bbatsov/crux][crux]]
1855
1856#+begin_src emacs-lisp
e738f03b 1857(use-package crux ; results in Waiting for git... [2 times]
5fece105
AB
1858 :defer 1
1859 :bind (("C-c b k" . crux-kill-other-buffers)
1860 ("C-c d" . crux-duplicate-current-line-or-region)
1861 ("C-c D" . crux-duplicate-and-comment-current-line-or-region)
1862 ("C-c f c" . crux-copy-file-preserve-attributes)
1863 ("C-c f d" . crux-delete-file-and-buffer)
1864 ("C-c f r" . crux-rename-file-and-buffer)
1865 ("C-c j" . crux-top-join-line)
1866 ("C-S-j" . crux-top-join-line)))
1867#+end_src
1868
1869** [[https://github.com/alezost/mwim.el][mwim]]
1870
1871#+begin_src emacs-lisp
1872(use-package mwim
1873 :bind (("C-a" . mwim-beginning-of-code-or-line)
1874 ("C-e" . mwim-end-of-code-or-line)
1875 ("<home>" . mwim-beginning-of-line-or-code)
1876 ("<end>" . mwim-end-of-line-or-code)))
1877#+end_src
1878
1879** projectile
1880
1881#+begin_src emacs-lisp
1882(use-package projectile
5fece105
AB
1883 :bind-keymap ("C-c p" . projectile-command-map)
1884 :config
1885 (projectile-mode)
1886
1887 (defun my-projectile-invalidate-cache (&rest _args)
1888 ;; ignore the args to `magit-checkout'
1889 (projectile-invalidate-cache nil))
1890
1891 (eval-after-load 'magit-branch
1892 '(progn
1893 (advice-add 'magit-checkout
1894 :after #'my-projectile-invalidate-cache)
1895 (advice-add 'magit-branch-and-checkout
c371adda
AB
1896 :after #'my-projectile-invalidate-cache)))
1897 :custom (projectile-completion-system 'ivy))
5fece105
AB
1898#+end_src
1899
1900** [[https://github.com/Wilfred/helpful][helpful]]
1901
1902#+begin_src emacs-lisp
1903(use-package helpful
1904 :defer 1
1905 :bind
1906 (("C-S-h c" . helpful-command)
1907 ("C-S-h f" . helpful-callable) ; helpful-function
1908 ("C-S-h v" . helpful-variable)
1909 ("C-S-h k" . helpful-key)
1910 ("C-S-h p" . helpful-at-point)))
1911#+end_src
1912
5fece105
AB
1913** [[https://github.com/EricCrosson/unkillable-scratch][unkillable-scratch]]
1914
1915Make =*scratch*= and =*Messages*= unkillable.
1916
1917#+begin_src emacs-lisp
1918(use-package unkillable-scratch
1919 :defer 3
1920 :config
1921 (unkillable-scratch 1)
1922 :custom
5fece105
AB
1923 (unkillable-buffers '("^\\*scratch\\*$" "^\\*Messages\\*$")))
1924#+end_src
1925
1926** [[https://github.com/davep/boxquote.el][boxquote.el]]
1927
1928#+begin_example
1929,----
1930| make pretty boxed quotes like this
1931`----
1932#+end_example
1933
1934#+begin_src emacs-lisp
1935(use-package boxquote
1936 :defer 3
1937 :bind
c5d8bb25 1938 (:prefix-map a/boxquote-prefix-map
5fece105
AB
1939 :prefix "C-c q"
1940 ("b" . boxquote-buffer)
1941 ("B" . boxquote-insert-buffer)
1942 ("d" . boxquote-defun)
1943 ("F" . boxquote-insert-file)
1944 ("hf" . boxquote-describe-function)
1945 ("hk" . boxquote-describe-key)
1946 ("hv" . boxquote-describe-variable)
1947 ("hw" . boxquote-where-is)
1948 ("k" . boxquote-kill)
1949 ("p" . boxquote-paragraph)
1950 ("q" . boxquote-boxquote)
1951 ("r" . boxquote-region)
1952 ("s" . boxquote-shell-command)
1953 ("t" . boxquote-text)
1954 ("T" . boxquote-title)
1955 ("u" . boxquote-unbox)
1956 ("U" . boxquote-unbox-region)
1957 ("y" . boxquote-yank)
1958 ("M-q" . boxquote-fill-paragraph)
1959 ("M-w" . boxquote-kill-ring-save)))
1960#+end_src
1961
1962Also see [[https://www.emacswiki.org/emacs/rebox2][rebox2]].
1963
cca52a26
AB
1964** orgalist
1965
1966#+begin_src emacs-lisp
1967(use-package orgalist
1968 :after message
1969 :hook (message-mode . orgalist-mode))
1970#+end_src
1971
5fece105
AB
1972** typo.el
1973
1974#+begin_src emacs-lisp
1975(use-package typo
1976 :defer 2
1977 :config
1978 (typo-global-mode 1)
1979 :hook (text-mode . typo-mode))
1980#+end_src
1981
1982** hl-todo
1983
1984#+begin_src emacs-lisp
1985(use-package hl-todo
1986 :defer 4
1987 :config
1988 (global-hl-todo-mode))
1989#+end_src
1990
1991** shrink-path
1992
1993#+begin_src emacs-lisp
1994(use-package shrink-path
e931bbdd 1995 :defer 2
5fece105
AB
1996 :config
1997 (setq eshell-prompt-regexp "\\(.*\n\\)*λ "
1998 eshell-prompt-function #'+eshell/prompt)
1999
2000 (defun +eshell/prompt ()
2001 (let ((base/dir (shrink-path-prompt default-directory)))
2002 (concat (propertize (car base/dir)
2003 'face 'font-lock-comment-face)
2004 (propertize (cdr base/dir)
2005 'face 'font-lock-constant-face)
2006 (propertize (+eshell--current-git-branch)
2007 'face 'font-lock-function-name-face)
2008 "\n"
2009 (propertize "λ" 'face 'eshell-prompt-face)
2010 ;; needed for the input text to not have prompt face
2011 (propertize " " 'face 'default))))
2012
2013 (defun +eshell--current-git-branch ()
2014 (let ((branch (car (loop for match in (split-string (shell-command-to-string "git branch") "\n")
2015 when (string-match "^\*" match)
2016 collect match))))
2017 (if (not (eq branch nil))
2018 (concat " " (substring branch 2))
2019 ""))))
2020#+end_src
2021
5fece105
AB
2022** [[https://github.com/peterwvj/eshell-up][eshell-up]]
2023
2024#+begin_src emacs-lisp
2025(use-package eshell-up
e931bbdd 2026 :commands eshell-up
5fece105
AB
2027 :after eshell)
2028#+end_src
2029
2030** multi-term
2031
2032#+begin_src emacs-lisp
2033(use-package multi-term
2034 :defer 1
169947ac 2035 :bind (("C-c a s m" . multi-term-dedicated-toggle)
a219024e
AB
2036 :map term-mode-map
2037 ("C-c C-j" . term-char-mode)
2038 :map term-raw-map
2039 ("C-c C-j" . term-line-mode))
5fece105
AB
2040 :config
2041 (setq multi-term-program "/bin/screen"
2042 ;; TODO: add separate bindings for connecting to existing
2043 ;; session vs. always creating a new one
2044 multi-term-dedicated-select-after-open-p t
2045 multi-term-dedicated-window-height 20
2046 multi-term-dedicated-max-window-height 30
2047 term-bind-key-alist
2048 '(("C-c C-c" . term-interrupt-subjob)
2049 ("C-c C-e" . term-send-esc)
2050 ("C-k" . kill-line)
2051 ("C-y" . term-paste)
2052 ("M-f" . term-send-forward-word)
2053 ("M-b" . term-send-backward-word)
2054 ("M-p" . term-send-up)
2055 ("M-n" . term-send-down)
2056 ("<C-backspace>" . term-send-backward-kill-word)
2057 ("<M-DEL>" . term-send-backward-kill-word)
2058 ("M-d" . term-send-delete-word)
2059 ("M-," . term-send-raw)
2060 ("M-." . comint-dynamic-complete))
2061 term-unbind-key-alist
2062 '("C-z" "C-x" "C-c" "C-h" "C-y" "<ESC>")))
2063#+end_src
2064
2065** page-break-lines
2066
2067#+begin_src emacs-lisp
2068(use-package page-break-lines
2069 :config
2070 (global-page-break-lines-mode))
2071#+end_src
2072
f2a57944
AB
2073** expand-region
2074
2075#+begin_src emacs-lisp
2076(use-package expand-region
2077 :bind ("C-=" . er/expand-region))
2078#+end_src
2079
64e429b5
AB
2080** multiple-cursors
2081
2082#+begin_src emacs-lisp
2083(use-package multiple-cursors
1bbc615c
AB
2084 :bind
2085 (("C-S-<mouse-1>" . mc/add-cursor-on-click)
2086 (:prefix-map a/mc-prefix-map
2087 :prefix "C-c m"
2088 ("c" . mc/edit-lines)
2089 ("n" . mc/mark-next-like-this)
2090 ("p" . mc/mark-previous-like-this)
2091 ("a" . mc/mark-all-like-this))))
64e429b5 2092#+end_src
0884b63b 2093
d4b2a7ef 2094* Email
673d5faa
AB
2095:PROPERTIES:
2096:CUSTOM_ID: email
2097:END:
5fece105 2098
5fece105 2099#+begin_src emacs-lisp
c5d8bb25 2100(defvar a/maildir (expand-file-name "~/mail/"))
ab6781dd 2101(with-eval-after-load 'recentf
c5d8bb25 2102 (add-to-list 'recentf-exclude a/maildir))
5fece105
AB
2103#+end_src
2104
2105** Gnus
2106
2107#+begin_src emacs-lisp
2108(setq
c5d8bb25
AB
2109 a/gnus-init-file (no-littering-expand-etc-file-name "gnus")
2110 mail-user-agent 'gnus-user-agent
2111 read-mail-command 'gnus)
5fece105 2112
d4b2a7ef 2113(use-feature gnus
1bbc615c 2114 :bind (("s-m" . gnus)
a0801748 2115 ("s-M" . gnus-unplugged))
5fece105
AB
2116 :init
2117 (setq
2118 gnus-select-method '(nnnil "")
2119 gnus-secondary-select-methods
2120 '((nnimap "amin"
2121 (nnimap-stream plain)
2122 (nnimap-address "127.0.0.1")
2123 (nnimap-server-port 143)
2124 (nnimap-authenticator plain)
2125 (nnimap-user "amin@aminb.org"))
2126 (nnimap "uwaterloo"
2127 (nnimap-stream plain)
2128 (nnimap-address "127.0.0.1")
2129 (nnimap-server-port 143)
2130 (nnimap-authenticator plain)
2131 (nnimap-user "abandali@uwaterloo.ca")))
2132 gnus-message-archive-group "nnimap+amin:Sent"
2133 gnus-parameters
2134 '(("gnu.*"
2135 (gcc-self . t)))
2136 gnus-large-newsgroup 50
2137 gnus-home-directory (no-littering-expand-var-file-name "gnus/")
2138 gnus-directory (concat gnus-home-directory "news/")
2139 message-directory (concat gnus-home-directory "mail/")
2140 nndraft-directory (concat gnus-home-directory "drafts/")
2141 gnus-save-newsrc-file nil
2142 gnus-read-newsrc-file nil
2143 gnus-interactive-exit nil
2144 gnus-gcc-mark-as-read t))
2145
d4b2a7ef 2146(use-feature gnus-art
5fece105
AB
2147 :config
2148 (setq
2149 gnus-visible-headers
2150 (concat gnus-visible-headers "\\|^List-Id:\\|^X-RT-Originator:\\|^User-Agent:")
2151 gnus-sorted-header-list
2152 '("^From:" "^Subject:" "^Summary:" "^Keywords:"
2153 "^Followup-To:" "^To:" "^Cc:" "X-RT-Originator"
2154 "^Newsgroups:" "List-Id:" "^Organization:"
2155 "^User-Agent:" "^Date:")
2156 ;; local-lapsed article dates
2157 ;; from https://www.emacswiki.org/emacs/GnusFormatting#toc11
2158 gnus-article-date-headers '(user-defined)
2159 gnus-article-time-format
2160 (lambda (time)
2161 (let* ((date (format-time-string "%a, %d %b %Y %T %z" time))
2162 (local (article-make-date-line date 'local))
2163 (combined-lapsed (article-make-date-line date
2164 'combined-lapsed))
2165 (lapsed (progn
2166 (string-match " (.+" combined-lapsed)
2167 (match-string 0 combined-lapsed))))
2168 (concat local lapsed))))
2169 (bind-keys
2170 :map gnus-article-mode-map
2171 ("r" . gnus-article-reply-with-original)
2172 ("R" . gnus-article-wide-reply-with-original)
2173 ("M-L" . org-store-link)))
2174
d4b2a7ef 2175(use-feature gnus-sum
5fece105 2176 :bind (:map gnus-summary-mode-map
c5d8bb25 2177 :prefix-map a/gnus-summary-prefix-map
5fece105
AB
2178 :prefix "v"
2179 ("r" . gnus-summary-reply)
2180 ("w" . gnus-summary-wide-reply)
2181 ("v" . gnus-summary-show-raw-article))
2182 :config
2183 (bind-keys
2184 :map gnus-summary-mode-map
2185 ("r" . gnus-summary-reply-with-original)
2186 ("R" . gnus-summary-wide-reply-with-original)
2187 ("M-L" . org-store-link))
c5d8bb25 2188 :hook (gnus-summary-mode . a/no-mouse-autoselect-window))
5fece105 2189
d4b2a7ef 2190(use-feature gnus-msg
5fece105
AB
2191 :config
2192 (setq gnus-posting-styles
2193 '((".*"
2194 (address "amin@aminb.org")
2195 (body "\nBest,\namin\n")
c5d8bb25 2196 (eval (setq a/message-cite-say-hi t)))
5fece105
AB
2197 ("gnu.*"
2198 (address "bandali@gnu.org"))
2199 ((header "subject" "ThankCRM")
2200 (to "webmasters-comment@gnu.org")
2201 (body "\nAdded to 2018supporters.html.\n\nMoving to campaigns.\n\n-amin\n")
c5d8bb25 2202 (eval (setq a/message-cite-say-hi nil)))
5fece105
AB
2203 ("nnimap\\+uwaterloo:.*"
2204 (address "abandali@uwaterloo.ca")
2205 (gcc "\"nnimap+uwaterloo:Sent Items\"")))))
2206
d4b2a7ef 2207(use-feature gnus-topic
5fece105
AB
2208 :hook (gnus-group-mode . gnus-topic-mode))
2209
d4b2a7ef 2210(use-feature gnus-agent
5fece105
AB
2211 :config
2212 (setq gnus-agent-synchronize-flags 'ask)
2213 :hook (gnus-group-mode . gnus-agent-mode))
2214
d4b2a7ef 2215(use-feature gnus-group
5fece105
AB
2216 :config
2217 (setq gnus-permanently-visible-groups "\\((INBOX\\|gnu$\\)"))
2218
d4b2a7ef 2219(use-feature mm-decode
5fece105
AB
2220 :config
2221 (setq mm-discouraged-alternatives '("text/html" "text/richtext")))
2222#+end_src
2223
2224** sendmail
2225
2226#+begin_src emacs-lisp
d4b2a7ef 2227(use-feature sendmail
5fece105
AB
2228 :config
2229 (setq sendmail-program "/usr/bin/msmtp"
2230 ;; message-sendmail-extra-arguments '("-v" "-d")
2231 mail-specify-envelope-from t
2232 mail-envelope-from 'header))
2233#+end_src
2234
2235** message
2236
2237#+begin_src emacs-lisp
d4b2a7ef 2238(use-feature message
5fece105 2239 :config
c5d8bb25 2240 (defconst a/message-cite-style-format "On %Y-%m-%d %l:%M %p, %N wrote:")
5fece105
AB
2241 (defconst message-cite-style-bandali
2242 '((message-cite-function 'message-cite-original)
2243 (message-citation-line-function 'message-insert-formatted-citation-line)
2244 (message-cite-reply-position 'traditional)
2245 (message-yank-prefix "> ")
2246 (message-yank-cited-prefix ">")
2247 (message-yank-empty-prefix ">")
2248 (message-citation-line-format
c5d8bb25
AB
2249 (if a/message-cite-say-hi
2250 (concat "Hi %F,\n\n" a/message-cite-style-format)
2251 a/message-cite-style-format)))
5fece105
AB
2252 "Citation style based on Mozilla Thunderbird's. Use with message-cite-style.")
2253 (setq message-cite-style 'message-cite-style-bandali
2254 message-kill-buffer-on-exit t
2255 message-send-mail-function 'message-send-mail-with-sendmail
2256 message-sendmail-envelope-from 'header
2257 message-dont-reply-to-names
2258 "\\(\\(.*@aminb\\.org\\)\\|\\(amin@bandali\\.me\\)\\|\\(\\(aminb?\\|mab\\|bandali\\)@gnu\\.org\\)\\|\\(\\(m\\|a\\(min\\.\\)?\\)bandali@uwaterloo\\.ca\\)\\)"
2259 message-user-fqdn "aminb.org")
2260 :hook (;; (message-setup . mml-secure-message-sign-pgpmime)
2261 (message-mode . flyspell-mode)
2262 (message-mode . (lambda ()
2263 ;; (setq fill-column 65
2264 ;; message-fill-column 65)
2265 (make-local-variable 'company-idle-delay)
2266 (setq company-idle-delay 0.2))))
2267 ;; :custom-face
2268 ;; (message-header-subject ((t (:foreground "#111" :weight semi-bold))))
2269 ;; (message-header-to ((t (:foreground "#111" :weight normal))))
2270 ;; (message-header-cc ((t (:foreground "#333" :weight normal))))
2271 )
2272
ab6781dd 2273(with-eval-after-load 'mml-sec
5fece105
AB
2274 (setq mml-secure-openpgp-encrypt-to-self t
2275 mml-secure-openpgp-sign-with-sender t))
2276#+end_src
2277
2278** footnote
2279
2280Convenient footnotes in =message-mode=.
2281
2282#+begin_src emacs-lisp
d4b2a7ef 2283(use-feature footnote
5fece105
AB
2284 :after message
2285 :bind
2286 (:map message-mode-map
c5d8bb25 2287 :prefix-map a/footnote-prefix-map
5fece105
AB
2288 :prefix "C-c f"
2289 ("a" . footnote-add-footnote)
2290 ("b" . footnote-back-to-message)
2291 ("c" . footnote-cycle-style)
2292 ("d" . footnote-delete-footnote)
2293 ("g" . footnote-goto-footnote)
2294 ("r" . footnote-renumber-footnotes)
2295 ("s" . footnote-set-style))
2296 :config
2297 (setq footnote-start-tag ""
2298 footnote-end-tag ""
2299 footnote-style 'unicode))
2300#+end_src
2301
490554d3
AB
2302** ebdb
2303
2304#+begin_src emacs-lisp
2305(use-package ebdb
86efa0e3 2306 :straight (:host github :repo "girzel/ebdb")
04a268e3 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
2323 :after (:all company message)
2324 :config
2325 (defun company-ebdb--post-complete (_) nil)
2326 :hook
2327 (message-mode . (lambda ()
2328 (add-to-list (make-local-variable 'company-backends)
2329 'company-ebdb))))
2330
d4b2a7ef 2331(use-feature ebdb-gnus
490554d3 2332 :after ebdb
bf233291 2333 :demand
490554d3
AB
2334 :custom
2335 (ebdb-gnus-window-configuration
2336 '(article
2337 (vertical 1.0
2338 (summary 0.25 point)
2339 (horizontal 1.0
2340 (article 1.0)
2341 (ebdb-gnus 0.3))))))
2342
d4b2a7ef 2343(use-feature ebdb-mua
490554d3 2344 :after ebdb
bf233291 2345 :demand
490554d3
AB
2346 ;; :custom (ebdb-mua-pop-up nil)
2347 )
2348
2349;; (use-package ebdb-message
2350;; :after ebdb)
2351
2352
2353;; (use-package ebdb-vcard
2354;; :after ebdb)
2355#+end_src
2356
9cebbd53 2357** COMMENT message-x
5fece105
AB
2358
2359#+begin_src emacs-lisp
2360(use-package message-x
5fece105
AB
2361 :custom
2362 (message-x-completion-alist
2363 (quote
2364 (("\\([rR]esent-\\|[rR]eply-\\)?[tT]o:\\|[bB]?[cC][cC]:" . gnus-harvest-find-address)
2365 ((if
2366 (boundp
2367 (quote message-newgroups-header-regexp))
2368 message-newgroups-header-regexp message-newsgroups-header-regexp)
2369 . message-expand-group)))))
2370#+end_src
2371
9cebbd53 2372** COMMENT gnus-harvest
5fece105
AB
2373
2374#+begin_src emacs-lisp
2375(use-package gnus-harvest
5fece105
AB
2376 :commands gnus-harvest-install
2377 :demand t
2378 :config
2379 (if (featurep 'message-x)
2380 (gnus-harvest-install 'message-x)
2381 (gnus-harvest-install)))
2382#+end_src
2383
c27a9ad6 2384* Blogging
673d5faa
AB
2385:PROPERTIES:
2386:CUSTOM_ID: blogging
2387:END:
5fece105 2388
5fece105
AB
2389** [[https://ox-hugo.scripter.co][ox-hugo]]
2390
2391#+begin_src emacs-lisp
2392(use-package ox-hugo
2393 :after ox)
2394
c27a9ad6
AB
2395(use-feature ox-hugo-auto-export
2396 :after ox-hugo)
5fece105
AB
2397#+end_src
2398
2399* Post initialization
2400:PROPERTIES:
2401:CUSTOM_ID: post-initialization
2402:END:
2403
5fece105
AB
2404Display how long it took to load the init file.
2405
2406#+begin_src emacs-lisp
2407(message "Loading %s...done (%.3fs)" user-init-file
2408 (float-time (time-subtract (current-time)
c5d8bb25 2409 a/before-user-init-time)))
5fece105
AB
2410#+end_src
2411
2412* Footer
2413:PROPERTIES:
2414:CUSTOM_ID: footer
2415:END:
2416
2417#+begin_src emacs-lisp :comments none
2418;;; init.el ends here
2419#+end_src
2420
2421* COMMENT Local Variables :ARCHIVE:
2422# Local Variables:
c31b1ac0 2423# eval: (add-hook 'after-save-hook #'a/async-babel-tangle 'append 'local)
5c1cce80 2424# eval: (when (featurep 'typo (typo-mode -1)))
5fece105 2425# End: