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