[emacs] re-add installation instructions and explanations
[~bandali/configs] / init.org
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
11 This org file is my literate configuration for GNU Emacs, and is
12 tangled 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
14 configurations of many different people. Some of the configurations
15 that I can remember off 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]]
27
28 I'd like to have a fully reproducible Emacs setup (part of the reason
29 why I store my configuration in this repository) but unfortunately out
30 of the box, that's not achievable with =package.el=, not currently
31 anyway. So, I've opted to use =straight.el=. I also used Borg for a
32 few months, but decided to try =straight.el= which allows direct use
33 of the various package archives.
34
35 ** Installation
36
37 To use this config for your Emacs, first you need to clone this repo,
38 then tangle =init.org= into =init.el=, and optionally byte-compile
39 =init.el=.
40
41 First, clone the repository and =cd= into it:
42
43 #+begin_src sh :tangle no
44 git clone https://git.sr.ht/~bandali/dotfiles ~/.emacs.d
45 cd ~/.emacs.d
46 #+end_src
47
48 Then, decide if you would like to use a byte-compiled init file, and
49 set 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
56 Now, first tangle =init.org=, and only if you chose to have a
57 byte-compiled init, build init as well:
58
59 #+begin_src sh :tangle no
60 make tangle-init
61 make build-init
62 #+end_src
63
64 If you'd like to use a byte-compiled init, it's important that it be
65 recompiled whenever =init.el= is generated from an updated =init.org=.
66 Not only does my setup automatically and asynchronously tangle
67 =init.org= to =init.el= every time you edit and save =init.org= in GNU
68 Emacs, 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
70 about manually tangling and compiling your init file whenever you
71 change it. Results of byte-compilation are kept in a =*compilation*=
72 buffer, which will be automatically displayed when compilation fails.
73
74 * Contents :toc_1:noexport:
75
76 - [[#about][About]]
77 - [[#header][Header]]
78 - [[#initial-setup][Initial setup]]
79 - [[#core][Core]]
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]]
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
98 ;;; init.el --- Amin Bandali's Emacs config -*- lexical-binding: t; eval: (view-mode 1) -*-
99 #+end_src
100
101 Enable =view-mode=, which both makes the file read-only (as a reminder
102 that =init.el= is an auto-generated file, not supposed to be edited),
103 and provides some convenient key bindings for browsing through the
104 file.
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
130 ;; Emacs configuration of Amin Bandali, computer scientist, functional
131 ;; programmer, and free software advocate.
132
133 ;; THIS FILE IS AUTO-GENERATED FROM `init.org'.
134 #+end_src
135
136 * Initial setup
137 :PROPERTIES:
138 :CUSTOM_ID: initial-setup
139 :END:
140
141 ** Emacs initialization
142
143 I'd like to do a couple of measurements of Emacs' startup time. First,
144 let'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
148 (defvar a/before-user-init-time (current-time)
149 "Value of `current-time' when Emacs begins loading `user-init-file'.")
150 (message "Loading Emacs...done (%.3fs)"
151 (float-time (time-subtract a/before-user-init-time
152 before-init-time)))
153 #+end_src
154
155 Also, temporarily increase ~gc-cons-threshhold~ and
156 ~gc-cons-percentage~ during startup to reduce garbage collection
157 frequency. Clearing the ~file-name-handler-alist~ seems to help reduce
158 startup time as well.
159
160 #+begin_src emacs-lisp
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)
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
171 Of course, we'd like to set them back to their defaults once we're
172 done initializing.
173
174 #+begin_src emacs-lisp
175 (add-hook
176 'after-init-hook
177 (lambda ()
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)))
181 #+end_src
182
183 Increase the number of lines kept in message logs (the =*Messages*=
184 buffer).
185
186 #+begin_src emacs-lisp
187 (setq message-log-max 20000)
188 #+end_src
189
190 Optionally, we could suppress some byte compiler warnings like below,
191 but 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
210 I can do all my package management things with =straight.el=, and
211 don'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]]).
213
214 #+begin_src emacs-lisp :tangle early-init.el
215 (setq package-enable-at-startup nil)
216 #+end_src
217
218 But since Emacs 27 isn't out yet (Emacs 26 is just around the corner
219 right now), and even when released it'll be long before most distros
220 ship in their repos, I'll still put the old workaround with the
221 commented call to ~package-initialize~ here anyway.
222
223 #+begin_src emacs-lisp :tangle no
224 (setq package-enable-at-startup nil)
225 ;; (package-initialize)
226 #+end_src
227
228 Update: the above is not necessary, since =straight.el= automatically
229 does that (and more). See =straight-package-neutering-mode=.
230
231 *** =straight.el=
232
233 #+begin_quote
234 Next-generation, purely functional package manager for the Emacs
235 hacker.
236 #+end_quote
237
238 =straight.el= allows me to have a fully reproducible Emacs setup.
239
240 #+begin_src emacs-lisp
241 ;; Main engine start...
242
243 (setq straight-repository-branch "develop"
244 straight-check-for-modifications '(check-on-save find-when-checking))
245
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
260 ;; Solid rocket booster ignition...
261
262 (defun a/build-init ()
263 (a/bootstrap-straight)
264 (byte-compile-file "init.el"))
265
266 (a/bootstrap-straight)
267
268 ;; We have lift off!
269
270 (setq straight-use-package-by-default t)
271 #+end_src
272
273 Since we enable =straight.el='s =straight-use-package-by-default=
274 integration, we will define a =use-feature= for plain ole
275 =use-package= without any of the =straight.el= stuff.
276
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
283 ,@args))
284 #+end_src
285
286 *** =use-package=
287
288 #+begin_quote
289 A 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
293 packages (in our case especially the latter) in a neatly organized way
294 and without compromising on performance.
295
296 #+begin_src emacs-lisp
297 (straight-use-package 'use-package)
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))
305 (setq use-package-verbose nil
306 use-package-expand-minimally t))
307
308 (setq use-package-always-defer t)
309 #+end_src
310
311 *** Epkg
312
313 #+begin_quote
314 Browse the Emacsmirror package database
315 #+end_quote
316
317 Epkg provides access to a local copy of the [[https://emacsmirror.net][Emacsmirror]] package
318 database, 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
322 (require 'bind-key)
323 (use-package epkg
324 :commands (epkg-list-packages epkg-describe-package)
325 :bind
326 (("C-c P e d" . epkg-describe-package)
327 ("C-c P e p" . epkg-list-packages))
328 :config
329 (setq epkg-repository "~/.emacs.d/straight/repos/epkgs/")
330 (eval-when-compile (defvar ivy-initial-inputs-alist))
331 (with-eval-after-load 'ivy
332 (add-to-list
333 'ivy-initial-inputs-alist '(epkg-describe-package . "^") t)))
334 #+end_src
335
336 ** No littering in =~/.emacs.d=
337
338 #+begin_quote
339 Help keeping ~/.emacs.d clean
340 #+end_quote
341
342 By default, even for Emacs' built-in packages, the configuration files
343 and persistent data are all over the place. Use =no-littering= to help
344 contain 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
359 I'm not planning on using the custom file much, but even so, I
360 definitely don't want it mixing with =init.el=. So, here; let's give
361 it it's own file. While at it, treat themes as safe.
362
363 #+begin_src emacs-lisp
364 (use-feature custom
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
375 Load 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
384 Let's use [[https://github.com/purcell/exec-path-from-shell][exec-path-from-shell]] to make Emacs use the =$PATH= as set up
385 in my shell.
386
387 #+begin_src emacs-lisp
388 (use-package exec-path-from-shell
389 :defer 1
390 :init
391 (setq exec-path-from-shell-arguments nil
392 exec-path-from-shell-check-startup-files nil)
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
400 ** COMMENT Only one custom theme at a time
401
402 #+begin_src emacs-lisp
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))
406 #+end_src
407
408 ** Server
409
410 Start server if not already running. Alternatively, can be done by
411 issuing =emacs --daemon= in the terminal, which can be automated with
412 a systemd service or using =brew services start emacs= on macOS. I use
413 Emacs as my window manager (via EXWM), so I always start Emacs on
414 login; so starting the server from inside Emacs is good enough for me.
415
416 See [[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
419 (use-feature server
420 :defer 1
421 :config (or (server-running-p) (server-mode)))
422 #+end_src
423
424 ** COMMENT Unicode support
425
426 Font stack with better unicode support, around =Ubuntu Mono= and
427 =Hack=.
428
429 #+begin_src emacs-lisp
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))
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
470 I’d like focus to follow the mouse when I move the cursor from one
471 window to the next.
472
473 #+begin_src emacs-lisp
474 (setq mouse-autoselect-window t)
475 #+end_src
476
477 Let’s define a function to conveniently disable this for certain
478 buffers and/or modes.
479
480 #+begin_src emacs-lisp
481 (defun a/no-mouse-autoselect-window ()
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
495 Convenience macro for =setq='ing multiple variables to the same value:
496
497 #+begin_src emacs-lisp
498 (defmacro a/setq-every (value &rest vars)
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
504 The 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
524 * Core
525 :PROPERTIES:
526 :CUSTOM_ID: core
527 :END:
528
529 ** Defaults
530
531 *** Time and battery in mode-line
532
533 Enable displaying time and battery in the mode-line, since I'm not
534 using the Xfce panel anymore. Also, I don't need to see the load
535 average on a regular basis, so disable that.
536
537 Note: 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
553 Might want to set the fringe to a smaller value, especially if using
554 EXWM. 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
563 Emacs disables some commands by default that could persumably be
564 confusing 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
572 Save what I copy into clipboard from other applications into Emacs'
573 kill-ring, which would allow me to still be able to easily access it
574 in case I kill (cut or copy) something else inside Emacs before
575 yanking (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
590 Lazy people would prefer to type fewer keystrokes, especially for yes
591 or 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
599 Firstly, let Emacs know that I'd like to have =*scratch*= as my
600 startup buffer.
601
602 #+begin_src emacs-lisp
603 (setq initial-buffer-choice t)
604 #+end_src
605
606 Now let's customize the =*scratch*= buffer a bit. First off, I don't
607 need the default hint.
608
609 #+begin_src emacs-lisp
610 (setq initial-scratch-message nil)
611 #+end_src
612
613 Also, let's use Text mode as the major mode, in case I want to
614 customize it (=*scratch*='s default major mode, Fundamental mode,
615 can't really be customized).
616
617 #+begin_src emacs-lisp
618 (setq initial-major-mode 'text-mode)
619 #+end_src
620
621 Inhibit 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
627 I don't really need to see the startup screen or echo area message
628 either.
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
638 Show either the file name or the buffer name (in case the buffer isn't
639 visiting 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
651 Emacs' default backup settings aren't that great. Let's use more
652 sensible options. See documentation for the ~make-backup-file~
653 variable.
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
663 Enable 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
682 Enable =winner-mode=.
683
684 #+begin_src emacs-lisp
685 (winner-mode 1)
686 #+end_src
687
688 *** Don’t display =*compilation*= on success
689
690 Based on https://stackoverflow.com/a/17788551, with changes to use
691 =cl-letf= instead of the now obsolete =flet=.
692
693 #+begin_src emacs-lisp
694 (with-eval-after-load 'compile
695 (defun a/compilation-finish-function (buffer outstr)
696 (unless (string-match "finished" outstr)
697 (switch-to-buffer-other-window buffer))
698 t)
699
700 (setq compilation-finish-functions #'a/compilation-finish-function)
701
702 (require 'cl-macs)
703
704 (defadvice compilation-start
705 (around inhibit-display
706 (command &optional mode name-function highlight-regexp))
707 (if (not (string-match "^\\(find\\|grep\\)" command))
708 (cl-letf (((symbol-function 'display-buffer) #'ignore))
709 (save-window-excursion ad-do-it))
710 ad-do-it))
711 (ad-activate 'compilation-start))
712 #+end_src
713
714 *** Search for non-ASCII characters
715
716 I’d like non-ASCII characters such as ‘’“”«»‹›áⓐ𝒶 to be selected when
717 I search for their ASCII counterpart. Shoutout to [[http://endlessparentheses.com/new-in-emacs-25-1-easily-search-non-ascii-characters.html][endlessparentheses]]
718 for 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
727 *** Cursor shape
728
729 #+begin_src emacs-lisp
730 (setq-default cursor-type 'bar)
731 #+end_src
732
733 *** Allow scrolling in Isearch
734
735 #+begin_src emacs-lisp
736 (setq isearch-allow-scroll t)
737 #+end_src
738
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
767 The packages in this section are absolutely essential to my everyday
768 workflow, and they play key roles in how I do my computing. They
769 immensely enhance the Emacs experience for me; both using Emacs, and
770 customizing 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
789 *** [[https://orgmode.org/][Org]]
790
791 #+begin_quote
792 Org mode is for keeping notes, maintaining TODO lists, planning
793 projects, and authoring documents with a fast and effective plain-text
794 system.
795 #+end_quote
796
797 In short, my favourite way of life.
798
799 First, we have to resort to a [[https://github.com/raxod502/straight.el#installing-org-with-straightel][hack]] to be able to use the correct
800 latest version of Org from upstream.
801
802 #+begin_src emacs-lisp
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
831 (provide 'org-version)
832 #+end_src
833
834 We 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
840 And here's where my actual Org configurations begin:
841
842 #+begin_src emacs-lisp
843 (use-feature org
844 :defer 2
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)
851 org-use-speed-commands t
852 org-startup-folded 'content
853 org-catch-invisible-edits 'show-and-error
854 org-log-done 'time)
855 (add-to-list 'org-structure-template-alist '("L" . "src emacs-lisp") t)
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
867 (use-feature ox-latex
868 :after ox
869 :config
870 (setq org-latex-listings 'listings
871 ;; org-latex-prefer-user-labels t
872 )
873 (add-to-list 'org-latex-classes
874 '("IEEEtran" "\\documentclass[11pt]{IEEEtran}"
875 ("\\section{%s}" . "\\section*{%s}")
876 ("\\subsection{%s}" . "\\subsection*{%s}")
877 ("\\subsubsection{%s}" . "\\subsubsection*{%s}")
878 ("\\paragraph{%s}" . "\\paragraph*{%s}")
879 ("\\subparagraph{%s}" . "\\subparagraph*{%s}"))
880 t)
881 (require 'ox-beamer))
882 #+end_src
883
884 **** asynchronous tangle
885
886 =a/async-babel-tangle= is a function closely inspired by [[https://github.com/dieggsy/dotfiles/tree/cc10edf7701958eff1cd94d4081da544d882a28c/emacs.d#dotfiles][dieggsy's
887 d/async-babel-tangle]] which uses [[https://github.com/jwiegley/emacs-async][async]] to asynchronously tangle an org
888 file.
889
890 #+begin_src emacs-lisp
891 (with-eval-after-load 'org
892 (defvar a/show-async-tangle-results nil
893 "Keep *emacs* async buffers around for later inspection.")
894
895 (defvar a/show-async-tangle-time nil
896 "Show the time spent tangling the file.")
897
898 (defvar a/async-tangle-post-compile
899 (when a/byte-compiled-init "make build-init")
900 "If non-nil, pass to `compile' after successful tangle.")
901
902 ;; TODO: look into why directly byte-compiling init.el causes a
903 ;; number of problems, including magit-status not loading (busy
904 ;; waiting).
905 (defvar a/async-tangle-byte-recompile nil
906 "If non-nil, byte-recompile the file on successful tangle.")
907
908 (defun a/async-babel-tangle ()
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))
914 ;; (async-quiet-switch "-q")
915 (file-noext (file-name-sans-extension file)))
916 (async-start
917 `(lambda ()
918 (require 'org)
919 (org-babel-tangle-file ,file))
920 (unless a/show-async-tangle-results
921 `(lambda (result)
922 (if result
923 (progn
924 ;; (setq byte-compile-warnings '(not noruntime unresolved))
925 (message "Tangled %s%s"
926 ,file-nodir
927 (if a/show-async-tangle-time
928 (format " (%.3fs)"
929 (float-time (time-subtract (current-time)
930 ',file-tangle-start-time)))
931 ""))
932 (when a/async-tangle-post-compile
933 (compile a/async-tangle-post-compile))
934 (when a/async-tangle-byte-recompile
935 (byte-recompile-file (concat ,file-noext ".el"))))
936 (message "Tangling %s failed" ,file-nodir))))))))
937
938 (add-to-list
939 'safe-local-variable-values
940 '(eval add-hook 'after-save-hook #'a/async-babel-tangle 'append 'local))
941 #+end_src
942
943 *** [[https://magit.vc/][Magit]]
944
945 #+begin_quote
946 It's Magit! A Git porcelain inside Emacs.
947 #+end_quote
948
949 Not just how I do git, but /the/ way to do git.
950
951 #+begin_src emacs-lisp
952 (use-package magit
953 :defer 2
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
971 *** recentf
972
973 Recently opened files.
974
975 #+begin_src emacs-lisp
976 (use-feature recentf
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
986 A smart M-x enhancement for Emacs.
987 #+end_quote
988
989 Mostly because =counsel= needs it to remember history.
990
991 #+begin_src emacs-lisp
992 (use-package smex)
993 #+end_src
994
995 *** [[https://github.com/abo-abo/swiper][Ivy]] (and friends)
996
997 #+begin_quote
998 Ivy - a generic completion frontend for Emacs, Swiper - isearch with
999 an overview, and more. Oh, man!
1000 #+end_quote
1001
1002 There'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
1008 :defer 0.6
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
1029 :after ivy
1030 :bind (("C-s" . swiper)
1031 ("C-r" . swiper)
1032 ("C-S-s" . isearch-forward)))
1033 #+end_src
1034
1035 **** Counsel
1036
1037 #+begin_src emacs-lisp
1038 (use-package counsel
1039 :after ivy
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
1055 (use-feature eshell
1056 :defer 2
1057 :commands eshell
1058 :bind ("C-c a s e" . eshell)
1059 :config
1060 (eval-when-compile (defvar eshell-prompt-regexp))
1061 (defun a/eshell-quit-or-delete-char (arg)
1062 (interactive "p")
1063 (if (and (eolp) (looking-back eshell-prompt-regexp nil))
1064 (eshell-life-is-too-much)
1065 (delete-char arg)))
1066
1067 (defun a/eshell-clear ()
1068 (interactive)
1069 (let ((inhibit-read-only t))
1070 (erase-buffer))
1071 (eshell-send-input))
1072
1073 (defun a/eshell-setup ()
1074 (make-local-variable 'company-idle-delay)
1075 (defvar company-idle-delay)
1076 (setq company-idle-delay nil)
1077 (bind-keys :map eshell-mode-map
1078 ("C-d" . a/eshell-quit-or-delete-char)
1079 ("C-S-l" . a/eshell-clear)
1080 ("M-r" . counsel-esh-history)
1081 ([tab] . company-complete)))
1082
1083 :hook (eshell-mode . a/eshell-setup)
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
1092 (use-feature ibuffer
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))
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)))
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)
1130 (mode . shell-mode)
1131 (mode . term-mode)))
1132 ("programming"
1133 (or
1134 (mode . python-mode)
1135 (mode . c-mode)
1136 (mode . c++-mode)
1137 (mode . emacs-lisp-mode)
1138 (mode . scheme-mode)
1139 (mode . haskell-mode)
1140 (mode . lean-mode)))
1141 ("emacs"
1142 (or
1143 (name . "^\\*scratch\\*$")
1144 (name . "^\\*Messages\\*$"))))))
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
1162 (use-feature outline
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)
1170 :prefix-map a/outline-prefix-map
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
1180 *** Dired
1181
1182 #+begin_src emacs-lisp
1183 (use-feature ls-lisp
1184 :custom (ls-lisp-dirs-first t))
1185
1186 (use-feature dired
1187 :config
1188 (setq dired-listing-switches "-alh"
1189 ls-lisp-use-insert-directory-program nil)
1190
1191 ;; easily diff 2 marked files
1192 ;; https://oremacs.com/2017/03/18/dired-ediff/
1193 (defun dired-ediff-files ()
1194 (interactive)
1195 (require 'dired-aux)
1196 (defvar ediff-after-quit-hook-internal)
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
1215 ("b" . dired-up-directory)
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))
1223 #+end_src
1224
1225 *** Help
1226
1227 #+begin_src emacs-lisp
1228 (use-feature help
1229 :config
1230 (temp-buffer-resize-mode)
1231 (setq help-window-select t))
1232 #+end_src
1233
1234 *** Tramp
1235
1236 #+begin_src emacs-lisp
1237 (use-feature tramp
1238 :config
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
1244
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
1259 Highlight 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
1271 Display Lisp objects at point in the echo area.
1272
1273 #+begin_src emacs-lisp
1274 (use-feature eldoc
1275 :when (version< "25" emacs-version)
1276 :config (global-eldoc-mode))
1277 #+end_src
1278
1279 ** paren
1280
1281 Highlight matching parens.
1282
1283 #+begin_src emacs-lisp
1284 (use-feature paren
1285 :demand
1286 :config (show-paren-mode))
1287 #+end_src
1288
1289 ** simple (for column numbers)
1290
1291 #+begin_src emacs-lisp
1292 (use-feature simple
1293 :config (column-number-mode))
1294 #+end_src
1295
1296 ** =savehist=
1297
1298 Save minibuffer history.
1299
1300 #+begin_src emacs-lisp
1301 (use-feature savehist
1302 :config (savehist-mode))
1303 #+end_src
1304
1305 ** =saveplace=
1306
1307 Automatically save place in each file.
1308
1309 #+begin_src emacs-lisp
1310 (use-feature saveplace
1311 :when (version< "25" emacs-version)
1312 :config (save-place-mode))
1313 #+end_src
1314
1315 ** =prog-mode=
1316
1317 #+begin_src emacs-lisp
1318 (use-feature prog-mode
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
1324
1325 ** =text-mode=
1326
1327 #+begin_src emacs-lisp
1328 (use-feature text-mode
1329 :hook ((text-mode . indicate-buffer-boundaries-left)
1330 (text-mode . abbrev-mode)))
1331 #+end_src
1332
1333 ** Company
1334
1335 #+begin_src emacs-lisp
1336 (use-package company
1337 :defer 3
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
1352 ** Flycheck
1353
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
1395
1396 * Programming modes
1397 :PROPERTIES:
1398 :CUSTOM_ID: programming-modes
1399 :END:
1400
1401 ** Lisp
1402
1403 #+begin_src emacs-lisp
1404 (use-feature lisp-mode
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
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
1417 :straight (:host github :repo "dwwmmn/alloy-mode")
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
1425 :straight proof-general)
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
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
1470 Emacs bindings for [[https://github.com/ndmitchell/hlint][hlint]]'s refactor option. This requires the refact
1471 executable 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
1494 Currently using =flycheck-haskell= with the =haskell-hlint= checker
1495 instead.
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
1634 ** Web
1635
1636 *** SGML and HTML
1637
1638 #+begin_src emacs-lisp
1639 (use-package sgml-mode
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
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
1656 :mode "\\.html\\'"
1657 :config
1658 (a/setq-every 2
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
1677 ** COMMENT Java
1678
1679 *** meghanada
1680
1681 #+begin_src emacs-lisp
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
1693 dependencies:
1694
1695 ace-window
1696 avy
1697 bui
1698 company-lsp
1699 dap-mode
1700 lsp-java
1701 lsp-mode
1702 lsp-ui
1703 pfuture
1704 tree-mode
1705 treemacs
1706 #+end_comment
1707
1708 #+begin_src emacs-lisp
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
1758 ** geiser
1759
1760 #+begin_src emacs-lisp
1761 (use-package geiser)
1762
1763 (use-feature geiser-guile
1764 :config
1765 (setq geiser-guile-load-path "~/src/git/guix"))
1766 #+end_src
1767
1768 ** guix
1769
1770 #+begin_src emacs-lisp
1771 (use-package guix)
1772 #+end_src
1773
1774 * Emacs enhancements
1775 :PROPERTIES:
1776 :CUSTOM_ID: emacs-enhancements
1777 :END:
1778
1779 ** man
1780
1781 #+begin_src emacs-lisp
1782 (use-feature man
1783 :config (setq Man-width 80))
1784 #+end_src
1785
1786 ** [[https://github.com/justbur/emacs-which-key][which-key]]
1787
1788 #+begin_quote
1789 Emacs package that displays available keybindings in popup
1790 #+end_quote
1791
1792 #+begin_src emacs-lisp
1793 (use-package which-key
1794 :defer 1
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"
1811 "C-c P" "package-management"
1812 "C-c P e" "package-management/epkg"
1813 "C-c P s" "package-management/straight.el"
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))
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
1851 ** smart-mode-line
1852
1853 #+begin_src emacs-lisp
1854 (use-package smart-mode-line
1855 :commands (sml/apply-theme)
1856 :demand
1857 :config
1858 (sml/setup))
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
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
1876 (defun a/lights-on ()
1877 "Enable my favourite light theme."
1878 (interactive)
1879 (mapc #'disable-theme custom-enabled-themes)
1880 (load-theme 'tangomod t)
1881 (sml/apply-theme 'automatic)
1882 (font-lock-remove-keywords
1883 'org-mode a/org-mode-font-lock-keywords))
1884
1885 (defun a/lights-off ()
1886 "Go dark."
1887 (interactive)
1888 (mapc #'disable-theme custom-enabled-themes)
1889 (load-theme 'doom-tomorrow-night t)
1890 (sml/apply-theme 'automatic)
1891 (font-lock-add-keywords
1892 'org-mode a/org-mode-font-lock-keywords t))
1893
1894 (bind-keys
1895 ("s-t d" . a/lights-off)
1896 ("s-t l" . a/lights-on))
1897 #+end_src
1898
1899 ** [[https://github.com/bbatsov/crux][crux]]
1900
1901 #+begin_src emacs-lisp
1902 (use-package crux ; results in Waiting for git... [2 times]
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
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
1941 :after #'my-projectile-invalidate-cache)))
1942 :custom (projectile-completion-system 'ivy))
1943 #+end_src
1944
1945 ** [[https://github.com/Wilfred/helpful][helpful]]
1946
1947 #+begin_src emacs-lisp
1948 (use-package helpful
1949 :defer 3
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
1958 ** [[https://github.com/EricCrosson/unkillable-scratch][unkillable-scratch]]
1959
1960 Make =*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
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
1983 (:prefix-map a/boxquote-prefix-map
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
2007 Also see [[https://www.emacswiki.org/emacs/rebox2][rebox2]].
2008
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
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
2040 :defer 2
2041 :after eshell
2042 :config
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))
2062 "")))
2063 (setq eshell-prompt-regexp "\\(.*\n\\)*λ "
2064 eshell-prompt-function #'+eshell/prompt))
2065 #+end_src
2066
2067 ** [[https://github.com/peterwvj/eshell-up][eshell-up]]
2068
2069 #+begin_src emacs-lisp
2070 (use-package eshell-up
2071 :after eshell
2072 :commands eshell-up)
2073 #+end_src
2074
2075 ** multi-term
2076
2077 #+begin_src emacs-lisp
2078 (use-package multi-term
2079 :defer 3
2080 :bind (("C-c a s m" . multi-term-dedicated-toggle)
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))
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
2118 ** expand-region
2119
2120 #+begin_src emacs-lisp
2121 (use-package expand-region
2122 :bind ("C-=" . er/expand-region))
2123 #+end_src
2124
2125 ** multiple-cursors
2126
2127 #+begin_src emacs-lisp
2128 (use-package multiple-cursors
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))))
2137 #+end_src
2138
2139 * Email
2140 :PROPERTIES:
2141 :CUSTOM_ID: email
2142 :END:
2143
2144 #+begin_src emacs-lisp
2145 (defvar a/maildir (expand-file-name "~/mail/"))
2146 (with-eval-after-load 'recentf
2147 (add-to-list 'recentf-exclude a/maildir))
2148 #+end_src
2149
2150 ** Gnus
2151
2152 #+begin_src emacs-lisp
2153 (setq
2154 a/gnus-init-file (no-littering-expand-etc-file-name "gnus")
2155 mail-user-agent 'gnus-user-agent
2156 read-mail-command 'gnus)
2157
2158 (use-feature gnus
2159 :bind (("s-m" . gnus)
2160 ("s-M" . gnus-unplugged))
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
2191 (use-feature gnus-art
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
2220 (use-feature gnus-sum
2221 :bind (:map gnus-summary-mode-map
2222 :prefix-map a/gnus-summary-prefix-map
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))
2233 :hook (gnus-summary-mode . a/no-mouse-autoselect-window))
2234
2235 (use-feature gnus-msg
2236 :config
2237 (setq gnus-posting-styles
2238 '((".*"
2239 (address "amin@aminb.org")
2240 (body "\nBest,\namin\n")
2241 (eval (setq a/message-cite-say-hi t)))
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")
2247 (eval (setq a/message-cite-say-hi nil)))
2248 ("nnimap\\+uwaterloo:.*"
2249 (address "abandali@uwaterloo.ca")
2250 (gcc "\"nnimap+uwaterloo:Sent Items\"")))))
2251
2252 (use-feature gnus-topic
2253 :hook (gnus-group-mode . gnus-topic-mode))
2254
2255 (use-feature gnus-agent
2256 :config
2257 (setq gnus-agent-synchronize-flags 'ask)
2258 :hook (gnus-group-mode . gnus-agent-mode))
2259
2260 (use-feature gnus-group
2261 :config
2262 (setq gnus-permanently-visible-groups "\\((INBOX\\|gnu$\\)"))
2263
2264 (use-feature mm-decode
2265 :config
2266 (setq mm-discouraged-alternatives '("text/html" "text/richtext")))
2267 #+end_src
2268
2269 ** sendmail
2270
2271 #+begin_src emacs-lisp
2272 (use-feature sendmail
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
2283 (use-feature message
2284 :config
2285 (defconst a/message-cite-style-format "On %Y-%m-%d %l:%M %p, %N wrote:")
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
2294 (if a/message-cite-say-hi
2295 (concat "Hi %F,\n\n" a/message-cite-style-format)
2296 a/message-cite-style-format)))
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
2318 (with-eval-after-load 'mml-sec
2319 (setq mml-secure-openpgp-encrypt-to-self t
2320 mml-secure-openpgp-sign-with-sender t))
2321 #+end_src
2322
2323 ** footnote
2324
2325 Convenient footnotes in =message-mode=.
2326
2327 #+begin_src emacs-lisp
2328 (use-feature footnote
2329 :after message
2330 :bind
2331 (:map message-mode-map
2332 :prefix-map a/footnote-prefix-map
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
2347 ** ebdb
2348
2349 #+begin_src emacs-lisp
2350 (use-package ebdb
2351 :straight (:host github :repo "girzel/ebdb")
2352 :defer 2
2353 :after gnus
2354 :bind (:map gnus-group-mode-map ("e" . ebdb))
2355 :config
2356 (setq ebdb-sources (no-littering-expand-var-file-name "ebdb"))
2357 (with-eval-after-load 'swiper
2358 (add-to-list 'swiper-font-lock-exclude 'ebdb-mode t)))
2359
2360 (use-feature ebdb-com
2361 :after ebdb)
2362
2363 ;; (use-package ebdb-complete
2364 ;; :after ebdb
2365 ;; :config
2366 ;; (ebdb-complete-enable))
2367
2368 (use-package company-ebdb
2369 :defer 2
2370 :config
2371 (defun company-ebdb--post-complete (_) nil))
2372
2373 (use-feature ebdb-gnus
2374 :defer 3
2375 :after ebdb
2376 :demand
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
2386 (use-feature ebdb-mua
2387 :defer 3
2388 :after ebdb
2389 :demand
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
2401 ** COMMENT message-x
2402
2403 #+begin_src emacs-lisp
2404 (use-package message-x
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
2416 ** COMMENT gnus-harvest
2417
2418 #+begin_src emacs-lisp
2419 (use-package gnus-harvest
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
2429 :PROPERTIES:
2430 :CUSTOM_ID: blogging
2431 :END:
2432
2433 ** [[https://ox-hugo.scripter.co][ox-hugo]]
2434
2435 #+begin_src emacs-lisp
2436 (use-package ox-hugo
2437 :after ox)
2438
2439 (use-feature ox-hugo-auto-export
2440 :after ox-hugo)
2441 #+end_src
2442
2443 * Post initialization
2444 :PROPERTIES:
2445 :CUSTOM_ID: post-initialization
2446 :END:
2447
2448 Display 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)
2453 a/before-user-init-time)))
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:
2467 # eval: (add-hook 'after-save-hook #'a/async-babel-tangle 'append 'local)
2468 # eval: (when (featurep 'typo (typo-mode -1)))
2469 # End: