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