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