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