[emacs][wip] hack together a bootstrap path
[~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
f8e11b0e
AB
1253** =savehist=
1254
1255Save minibuffer history.
1256
1257#+begin_src emacs-lisp
1258(use-feature savehist
1259 :config (savehist-mode))
1260#+end_src
1261
1262** =saveplace=
1263
1264Automatically save place in each file.
1265
1266#+begin_src emacs-lisp
1267(use-feature saveplace
1268 :when (version< "25" emacs-version)
1269 :config (save-place-mode))
1270#+end_src
1271
5097c774
AB
1272** =prog-mode=
1273
1274#+begin_src emacs-lisp
1275(use-feature prog-mode
1276 :config (global-prettify-symbols-mode)
1277 (defun indicate-buffer-boundaries-left ()
1278 (setq indicate-buffer-boundaries 'left))
1279 (add-hook 'prog-mode-hook #'indicate-buffer-boundaries-left))
1280#+end_src
1281
1282** =text-mode=
1283
1284#+begin_src emacs-lisp
1285(use-feature text-mode
1286 :hook ((text-mode . indicate-buffer-boundaries-left)
1287 (text-mode . abbrev-mode)))
1288#+end_src
1289
ab0674a2 1290** Company
5fece105
AB
1291
1292#+begin_src emacs-lisp
1293(use-package company
1294 :defer 1
1295 :bind
1296 (:map company-active-map
1297 ([tab] . company-complete-common-or-cycle)
1298 ([escape] . company-abort))
1299 :custom
1300 (company-minimum-prefix-length 1)
1301 (company-selection-wrap-around t)
1302 (company-dabbrev-char-regexp "\\sw\\|\\s_\\|[-_]")
1303 (company-dabbrev-downcase nil)
1304 (company-dabbrev-ignore-case nil)
1305 :config
1306 (global-company-mode t))
1307#+end_src
1308
de000e57 1309** Flycheck
5fece105 1310
5fece105
AB
1311#+begin_src emacs-lisp
1312(use-package flycheck
1313 :defer 3
1314 :hook (prog-mode . flycheck-mode)
1315 :bind
1316 (:map flycheck-mode-map
1317 ("M-P" . flycheck-previous-error)
1318 ("M-N" . flycheck-next-error))
1319 :config
1320 ;; Use the load-path from running Emacs when checking elisp files
1321 (setq flycheck-emacs-lisp-load-path 'inherit)
1322
1323 ;; Only flycheck when I actually save the buffer
1324 (setq flycheck-check-syntax-automatically '(mode-enabled save)))
1325
1326;; http://endlessparentheses.com/ispell-and-apostrophes.html
1327(use-package ispell
1328 :defer 3
1329 :config
1330 ;; ’ can be part of a word
1331 (setq ispell-local-dictionary-alist
1332 `((nil "[[:alpha:]]" "[^[:alpha:]]"
1333 "['\x2019]" nil ("-B") nil utf-8)))
1334 ;; don't send ’ to the subprocess
1335 (defun endless/replace-apostrophe (args)
1336 (cons (replace-regexp-in-string
1337 "’" "'" (car args))
1338 (cdr args)))
1339 (advice-add #'ispell-send-string :filter-args
1340 #'endless/replace-apostrophe)
1341
1342 ;; convert ' back to ’ from the subprocess
1343 (defun endless/replace-quote (args)
1344 (if (not (derived-mode-p 'org-mode))
1345 args
1346 (cons (replace-regexp-in-string
1347 "'" "’" (car args))
1348 (cdr args))))
1349 (advice-add #'ispell-parse-output :filter-args
1350 #'endless/replace-quote))
1351#+end_src
9678e6da 1352
520aebd5 1353* Programming modes
673d5faa
AB
1354:PROPERTIES:
1355:CUSTOM_ID: programming-modes
1356:END:
5fece105 1357
520aebd5
AB
1358** Lisp
1359
1360#+begin_src emacs-lisp
1361(use-feature lisp-mode
1362 :config
1363 (add-hook 'emacs-lisp-mode-hook 'outline-minor-mode)
1364 (add-hook 'emacs-lisp-mode-hook 'reveal-mode)
1365 (defun indent-spaces-mode ()
1366 (setq indent-tabs-mode nil))
1367 (add-hook 'lisp-interaction-mode-hook #'indent-spaces-mode))
1368#+end_src
1369
5fece105
AB
1370** [[http://alloytools.org][Alloy]] (with [[https://github.com/dwwmmn/alloy-mode][alloy-mode]])
1371
1372#+begin_src emacs-lisp
1373(use-package alloy-mode
520aebd5 1374 :straight (:host github :repo "dwwmmn/alloy-mode")
5fece105
AB
1375 :config (setq alloy-basic-offset 2))
1376#+end_src
1377
1378** [[https://coq.inria.fr][Coq]] (with [[https://github.com/ProofGeneral/PG][Proof General]])
1379
1380#+begin_src emacs-lisp
426cec7a
AB
1381(use-package proof-site
1382 :straight proof-general)
5fece105
AB
1383#+end_src
1384
1385** [[https://leanprover.github.io][Lean]] (with [[https://github.com/leanprover/lean-mode][lean-mode]])
1386
1387#+begin_src emacs-lisp
1388(eval-when-compile (defvar lean-mode-map))
1389(use-package lean-mode
1390 :defer 1
1391 :bind (:map lean-mode-map
1392 ("S-SPC" . company-complete))
1393 :config
1394 (require 'lean-input)
1395 (setq default-input-method "Lean"
1396 lean-input-tweak-all '(lean-input-compose
1397 (lean-input-prepend "/")
1398 (lean-input-nonempty))
1399 lean-input-user-translations '(("/" "/")))
1400 (lean-input-setup))
1401 #+end_src
1402
1403** Haskell
1404
1405*** [[https://github.com/haskell/haskell-mode][haskell-mode]]
1406
1407#+begin_src emacs-lisp
1408(use-package haskell-mode
5fece105
AB
1409 :config
1410 (setq haskell-indentation-layout-offset 4
1411 haskell-indentation-left-offset 4
1412 flycheck-checker 'haskell-hlint
1413 flycheck-disabled-checkers '(haskell-stack-ghc haskell-ghc)))
1414#+end_src
1415
1416*** [[https://github.com/jyp/dante][dante]]
1417
1418#+begin_src emacs-lisp
1419(use-package dante
1420 :after haskell-mode
1421 :commands dante-mode
1422 :hook (haskell-mode . dante-mode))
1423#+end_src
1424
1425*** [[https://github.com/mpickering/hlint-refactor-mode][hlint-refactor]]
1426
1427Emacs bindings for [[https://github.com/ndmitchell/hlint][hlint]]'s refactor option. This requires the refact
1428executable from [[https://github.com/mpickering/apply-refact][apply-refact]].
1429
1430#+begin_src emacs-lisp
1431(use-package hlint-refactor
1432 :after haskell-mode
1433 :bind (:map hlint-refactor-mode-map
1434 ("C-c l b" . hlint-refactor-refactor-buffer)
1435 ("C-c l r" . hlint-refactor-refactor-at-point))
1436 :hook (haskell-mode . hlint-refactor-mode))
1437#+end_src
1438
1439*** [[https://github.com/flycheck/flycheck-haskell][flycheck-haskell]]
1440
1441#+begin_src emacs-lisp
1442(use-package flycheck-haskell
1443 :after haskell-mode)
1444#+end_src
1445
1446*** [[https://github.com/ndmitchell/hlint/blob/20e116a043f2073c57b17b24ae6364b5e433ba7e/data/hs-lint.el][hs-lint.el]]
1447:PROPERTIES:
1448:header-args+: :tangle lisp/hs-lint.el :mkdirp yes
1449:END:
1450
1451Currently using =flycheck-haskell= with the =haskell-hlint= checker
1452instead.
1453
1454#+begin_src emacs-lisp :tangle no
1455;;; hs-lint.el --- minor mode for HLint code checking
1456
1457;; Copyright 2009 (C) Alex Ott
1458;;
1459;; Author: Alex Ott <alexott@gmail.com>
1460;; Keywords: haskell, lint, HLint
1461;; Requirements:
1462;; Status: distributed under terms of GPL2 or above
1463
1464;; Typical message from HLint looks like:
1465;;
1466;; /Users/ott/projects/lang-exp/haskell/test.hs:52:1: Eta reduce
1467;; Found:
1468;; count1 p l = length (filter p l)
1469;; Why not:
1470;; count1 p = length . filter p
1471
1472
1473(require 'compile)
1474
1475(defgroup hs-lint nil
1476 "Run HLint as inferior of Emacs, parse error messages."
1477 :group 'tools
1478 :group 'haskell)
1479
1480(defcustom hs-lint-command "hlint"
1481 "The default hs-lint command for \\[hlint]."
1482 :type 'string
1483 :group 'hs-lint)
1484
1485(defcustom hs-lint-save-files t
1486 "Save modified files when run HLint or no (ask user)"
1487 :type 'boolean
1488 :group 'hs-lint)
1489
1490(defcustom hs-lint-replace-with-suggestions nil
1491 "Replace user's code with suggested replacements"
1492 :type 'boolean
1493 :group 'hs-lint)
1494
1495(defcustom hs-lint-replace-without-ask nil
1496 "Replace user's code with suggested replacements automatically"
1497 :type 'boolean
1498 :group 'hs-lint)
1499
1500(defun hs-lint-process-setup ()
1501 "Setup compilation variables and buffer for `hlint'."
1502 (run-hooks 'hs-lint-setup-hook))
1503
1504;; regex for replace suggestions
1505;;
1506;; ^\(.*?\):\([0-9]+\):\([0-9]+\): .*
1507;; Found:
1508;; \s +\(.*\)
1509;; Why not:
1510;; \s +\(.*\)
1511
1512(defvar hs-lint-regex
1513 "^\\(.*?\\):\\([0-9]+\\):\\([0-9]+\\): .*[\n\C-m]Found:[\n\C-m]\\s +\\(.*\\)[\n\C-m]Why not:[\n\C-m]\\s +\\(.*\\)[\n\C-m]"
1514 "Regex for HLint messages")
1515
1516(defun make-short-string (str maxlen)
1517 (if (< (length str) maxlen)
1518 str
1519 (concat (substring str 0 (- maxlen 3)) "...")))
1520
1521(defun hs-lint-replace-suggestions ()
1522 "Perform actual replacement of suggestions"
1523 (goto-char (point-min))
1524 (while (re-search-forward hs-lint-regex nil t)
1525 (let* ((fname (match-string 1))
1526 (fline (string-to-number (match-string 2)))
1527 (old-code (match-string 4))
1528 (new-code (match-string 5))
1529 (msg (concat "Replace '" (make-short-string old-code 30)
1530 "' with '" (make-short-string new-code 30) "'"))
1531 (bline 0)
1532 (eline 0)
1533 (spos 0)
1534 (new-old-code ""))
1535 (save-excursion
1536 (switch-to-buffer (get-file-buffer fname))
1537 (goto-char (point-min))
1538 (forward-line (1- fline))
1539 (beginning-of-line)
1540 (setf bline (point))
1541 (when (or hs-lint-replace-without-ask
1542 (yes-or-no-p msg))
1543 (end-of-line)
1544 (setf eline (point))
1545 (beginning-of-line)
1546 (setf old-code (regexp-quote old-code))
1547 (while (string-match "\\\\ " old-code spos)
1548 (setf new-old-code (concat new-old-code
1549 (substring old-code spos (match-beginning 0))
1550 "\\ *"))
1551 (setf spos (match-end 0)))
1552 (setf new-old-code (concat new-old-code (substring old-code spos)))
1553 (remove-text-properties bline eline '(composition nil))
1554 (when (re-search-forward new-old-code eline t)
1555 (replace-match new-code nil t)))))))
1556
1557(defun hs-lint-finish-hook (buf msg)
1558 "Function, that is executed at the end of HLint execution"
1559 (if hs-lint-replace-with-suggestions
1560 (hs-lint-replace-suggestions)
1561 (next-error 1 t)))
1562
1563(define-compilation-mode hs-lint-mode "HLint"
1564 "Mode for check Haskell source code."
1565 (set (make-local-variable 'compilation-process-setup-function)
1566 'hs-lint-process-setup)
1567 (set (make-local-variable 'compilation-disable-input) t)
1568 (set (make-local-variable 'compilation-scroll-output) nil)
1569 (set (make-local-variable 'compilation-finish-functions)
1570 (list 'hs-lint-finish-hook))
1571 )
1572
1573(defun hs-lint ()
1574 "Run HLint for current buffer with haskell source"
1575 (interactive)
1576 (save-some-buffers hs-lint-save-files)
1577 (compilation-start (concat hs-lint-command " \"" buffer-file-name "\"")
1578 'hs-lint-mode))
1579
1580(provide 'hs-lint)
1581;;; hs-lint.el ends here
1582#+end_src
1583
1584#+begin_src emacs-lisp :tangle no
1585(use-package hs-lint
1586 :load-path "lisp/"
1587 :bind (:map haskell-mode-map
1588 ("C-c l l" . hs-lint)))
1589#+end_src
1590
42b1fa9b 1591** Web
5fece105
AB
1592
1593*** SGML and HTML
1594
1595#+begin_src emacs-lisp
1596(use-package sgml-mode
5fece105
AB
1597 :config
1598 (setq sgml-basic-offset 2))
1599#+end_src
1600
1601*** CSS and SCSS
1602
1603#+begin_src emacs-lisp
1604(use-package css-mode
5fece105
AB
1605 :config
1606 (setq css-indent-offset 2))
1607#+end_src
1608
1609*** Web mode
1610
1611#+begin_src emacs-lisp
1612(use-package web-mode
5fece105
AB
1613 :mode "\\.html\\'"
1614 :config
c5d8bb25 1615 (a/setq-every 2
5fece105
AB
1616 web-mode-code-indent-offset
1617 web-mode-css-indent-offset
1618 web-mode-markup-indent-offset))
1619#+end_src
1620
1621*** Emmet mode
1622
1623#+begin_src emacs-lisp
1624(use-package emmet-mode
1625 :after (:any web-mode css-mode sgml-mode)
1626 :bind* (("C-)" . emmet-next-edit-point)
1627 ("C-(" . emmet-prev-edit-point))
1628 :config
1629 (unbind-key "C-j" emmet-mode-keymap)
1630 (setq emmet-move-cursor-between-quotes t)
1631 :hook (web-mode css-mode html-mode sgml-mode))
1632#+end_src
1633
9cebbd53 1634** COMMENT Java
5fece105
AB
1635
1636*** meghanada
1637
9cebbd53 1638#+begin_src emacs-lisp
5fece105
AB
1639(use-package meghanada
1640 :bind
1641 (:map meghanada-mode-map
1642 (("C-M-o" . meghanada-optimize-import)
1643 ("C-M-t" . meghanada-import-all)))
1644 :hook (java-mode . meghanada-mode))
1645#+end_src
1646
1647*** lsp-java
1648
1649#+begin_comment
1650dependencies:
1651
1652ace-window
1653avy
1654bui
1655company-lsp
1656dap-mode
1657lsp-java
1658lsp-mode
1659lsp-ui
1660pfuture
1661tree-mode
1662treemacs
1663#+end_comment
1664
9cebbd53 1665#+begin_src emacs-lisp
5fece105
AB
1666(use-package treemacs
1667 :config (setq treemacs-never-persist t))
1668
1669(use-package yasnippet
1670 :config
1671 ;; (yas-global-mode)
1672 )
1673
1674(use-package lsp-mode
1675 :init (setq lsp-eldoc-render-all nil
1676 lsp-highlight-symbol-at-point nil)
1677 )
1678
1679(use-package hydra)
1680
1681(use-package company-lsp
1682 :after company
1683 :config
1684 (setq company-lsp-cache-candidates t
1685 company-lsp-async t))
1686
1687(use-package lsp-ui
1688 :config
1689 (setq lsp-ui-sideline-update-mode 'point))
1690
1691(use-package lsp-java
1692 :config
1693 (add-hook 'java-mode-hook
1694 (lambda ()
1695 (setq-local company-backends (list 'company-lsp))))
1696
1697 (add-hook 'java-mode-hook 'lsp-java-enable)
1698 (add-hook 'java-mode-hook 'flycheck-mode)
1699 (add-hook 'java-mode-hook 'company-mode)
1700 (add-hook 'java-mode-hook 'lsp-ui-mode))
1701
1702(use-package dap-mode
1703 :after lsp-mode
1704 :config
1705 (dap-mode t)
1706 (dap-ui-mode t))
1707
1708(use-package dap-java
1709 :after (lsp-java))
1710
1711(use-package lsp-java-treemacs
1712 :after (treemacs))
1713#+end_src
1714
520aebd5 1715** COMMENT geiser
5492a3dc
AB
1716
1717#+begin_src emacs-lisp
1718(use-package geiser)
1719
42b1fa9b 1720(use-feature geiser-guile
5492a3dc
AB
1721 :config
1722 (setq geiser-guile-load-path "~/src/git/guix"))
1723#+end_src
1724
520aebd5 1725** COMMENT guix
5492a3dc
AB
1726
1727#+begin_src emacs-lisp
42b1fa9b 1728(use-package guix)
5492a3dc
AB
1729#+end_src
1730
e738f03b 1731* Emacs enhancements
673d5faa
AB
1732:PROPERTIES:
1733:CUSTOM_ID: emacs-enhancements
1734:END:
5fece105 1735
9f120b4f
AB
1736** man
1737
1738#+begin_src emacs-lisp
1739(use-feature man
9f120b4f
AB
1740 :config (setq Man-width 80))
1741#+end_src
1742
5fece105
AB
1743** [[https://github.com/justbur/emacs-which-key][which-key]]
1744
1745#+begin_quote
1746Emacs package that displays available keybindings in popup
1747#+end_quote
1748
1749#+begin_src emacs-lisp
1750(use-package which-key
1751 :defer 1
169947ac
AB
1752 :config
1753 (which-key-add-key-based-replacements
1754 ;; prefixes for global prefixes and minor modes
1755 "C-c @" "outline"
1756 "C-c !" "flycheck"
1757 "C-c 8" "typo"
1758 "C-c 8 -" "typo/dashes"
1759 "C-c 8 <" "typo/left-brackets"
1760 "C-c 8 >" "typo/right-brackets"
1761 "C-x 8" "unicode"
1762 "C-x a" "abbrev/expand"
1763 "C-x r" "rectangle/register/bookmark"
1764 "C-x v" "version control"
1765 ;; prefixes for my personal bindings
1766 "C-c a" "applications"
1767 "C-c a s" "shells"
e724cc40
AB
1768 "C-c P" "package-management"
1769 "C-c P e" "package-management/epkg"
1770 "C-c P s" "package-management/straight.el"
169947ac
AB
1771 "C-c c" "compile-and-comments"
1772 "C-c e" "eval"
1773 "C-c f" "files"
1774 "C-c F" "frames"
1775 "C-S-h" "help(ful)"
1776 "C-c m" "multiple-cursors"
1777 "C-c p" "projectile"
1778 "C-c p s" "projectile/search"
1779 "C-c p x" "projectile/execute"
1780 "C-c p 4" "projectile/other-window"
1781 "C-c q" "boxquote"
1782 "s-g" "magit"
1783 "s-o" "outline"
1784 "s-t" "themes")
1785
1786 ;; prefixes for major modes
1787 (which-key-add-major-mode-key-based-replacements 'message-mode
1788 "C-c f" "footnote")
1789 (which-key-add-major-mode-key-based-replacements 'org-mode
1790 "C-c C-v" "org-babel")
1791 (which-key-add-major-mode-key-based-replacements 'web-mode
1792 "C-c C-a" "web/attributes"
1793 "C-c C-b" "web/blocks"
1794 "C-c C-d" "web/dom"
1795 "C-c C-e" "web/element"
1796 "C-c C-t" "web/tags")
1797
1798 (which-key-mode))
5fece105
AB
1799#+end_src
1800
1801** theme
1802
1803#+begin_src emacs-lisp
1804(add-to-list 'custom-theme-load-path "~/.emacs.d/lisp")
1805(load-theme 'tangomod t)
1806#+end_src
1807
21ad71fa 1808** smart-mode-line
5fece105
AB
1809
1810#+begin_src emacs-lisp
21ad71fa 1811(use-package smart-mode-line
19740fc0 1812 :commands (sml/apply-theme)
21ad71fa
AB
1813 :config
1814 (sml/setup))
5fece105
AB
1815#+end_src
1816
1817** doom-themes
1818
1819#+begin_src emacs-lisp
1820(use-package doom-themes)
1821#+end_src
1822
1823** theme helper functions
1824
1825#+begin_src emacs-lisp
c5d8bb25 1826(defun a/lights-on ()
5fece105
AB
1827 "Enable my favourite light theme."
1828 (interactive)
21ad71fa
AB
1829 (mapc #'disable-theme custom-enabled-themes)
1830 (load-theme 'tangomod t)
1831 (sml/apply-theme 'automatic))
5fece105 1832
c5d8bb25 1833(defun a/lights-off ()
5fece105
AB
1834 "Go dark."
1835 (interactive)
21ad71fa
AB
1836 (mapc #'disable-theme custom-enabled-themes)
1837 (load-theme 'doom-tomorrow-night t)
1838 (sml/apply-theme 'automatic))
5fece105
AB
1839
1840(bind-keys
c5d8bb25
AB
1841 ("s-t d" . a/lights-off)
1842 ("s-t l" . a/lights-on))
5fece105
AB
1843#+end_src
1844
1845** [[https://github.com/bbatsov/crux][crux]]
1846
1847#+begin_src emacs-lisp
e738f03b 1848(use-package crux ; results in Waiting for git... [2 times]
5fece105
AB
1849 :defer 1
1850 :bind (("C-c b k" . crux-kill-other-buffers)
1851 ("C-c d" . crux-duplicate-current-line-or-region)
1852 ("C-c D" . crux-duplicate-and-comment-current-line-or-region)
1853 ("C-c f c" . crux-copy-file-preserve-attributes)
1854 ("C-c f d" . crux-delete-file-and-buffer)
1855 ("C-c f r" . crux-rename-file-and-buffer)
1856 ("C-c j" . crux-top-join-line)
1857 ("C-S-j" . crux-top-join-line)))
1858#+end_src
1859
1860** [[https://github.com/alezost/mwim.el][mwim]]
1861
1862#+begin_src emacs-lisp
1863(use-package mwim
1864 :bind (("C-a" . mwim-beginning-of-code-or-line)
1865 ("C-e" . mwim-end-of-code-or-line)
1866 ("<home>" . mwim-beginning-of-line-or-code)
1867 ("<end>" . mwim-end-of-line-or-code)))
1868#+end_src
1869
1870** projectile
1871
1872#+begin_src emacs-lisp
1873(use-package projectile
5fece105
AB
1874 :bind-keymap ("C-c p" . projectile-command-map)
1875 :config
1876 (projectile-mode)
1877
1878 (defun my-projectile-invalidate-cache (&rest _args)
1879 ;; ignore the args to `magit-checkout'
1880 (projectile-invalidate-cache nil))
1881
1882 (eval-after-load 'magit-branch
1883 '(progn
1884 (advice-add 'magit-checkout
1885 :after #'my-projectile-invalidate-cache)
1886 (advice-add 'magit-branch-and-checkout
c371adda
AB
1887 :after #'my-projectile-invalidate-cache)))
1888 :custom (projectile-completion-system 'ivy))
5fece105
AB
1889#+end_src
1890
1891** [[https://github.com/Wilfred/helpful][helpful]]
1892
1893#+begin_src emacs-lisp
1894(use-package helpful
1895 :defer 1
1896 :bind
1897 (("C-S-h c" . helpful-command)
1898 ("C-S-h f" . helpful-callable) ; helpful-function
1899 ("C-S-h v" . helpful-variable)
1900 ("C-S-h k" . helpful-key)
1901 ("C-S-h p" . helpful-at-point)))
1902#+end_src
1903
5fece105
AB
1904** [[https://github.com/EricCrosson/unkillable-scratch][unkillable-scratch]]
1905
1906Make =*scratch*= and =*Messages*= unkillable.
1907
1908#+begin_src emacs-lisp
1909(use-package unkillable-scratch
1910 :defer 3
1911 :config
1912 (unkillable-scratch 1)
1913 :custom
1914 (unkillable-scratch-behavior 'do-nothing)
1915 (unkillable-buffers '("^\\*scratch\\*$" "^\\*Messages\\*$")))
1916#+end_src
1917
1918** [[https://github.com/davep/boxquote.el][boxquote.el]]
1919
1920#+begin_example
1921,----
1922| make pretty boxed quotes like this
1923`----
1924#+end_example
1925
1926#+begin_src emacs-lisp
1927(use-package boxquote
1928 :defer 3
1929 :bind
c5d8bb25 1930 (:prefix-map a/boxquote-prefix-map
5fece105
AB
1931 :prefix "C-c q"
1932 ("b" . boxquote-buffer)
1933 ("B" . boxquote-insert-buffer)
1934 ("d" . boxquote-defun)
1935 ("F" . boxquote-insert-file)
1936 ("hf" . boxquote-describe-function)
1937 ("hk" . boxquote-describe-key)
1938 ("hv" . boxquote-describe-variable)
1939 ("hw" . boxquote-where-is)
1940 ("k" . boxquote-kill)
1941 ("p" . boxquote-paragraph)
1942 ("q" . boxquote-boxquote)
1943 ("r" . boxquote-region)
1944 ("s" . boxquote-shell-command)
1945 ("t" . boxquote-text)
1946 ("T" . boxquote-title)
1947 ("u" . boxquote-unbox)
1948 ("U" . boxquote-unbox-region)
1949 ("y" . boxquote-yank)
1950 ("M-q" . boxquote-fill-paragraph)
1951 ("M-w" . boxquote-kill-ring-save)))
1952#+end_src
1953
1954Also see [[https://www.emacswiki.org/emacs/rebox2][rebox2]].
1955
cca52a26
AB
1956** orgalist
1957
1958#+begin_src emacs-lisp
1959(use-package orgalist
1960 :after message
1961 :hook (message-mode . orgalist-mode))
1962#+end_src
1963
5fece105
AB
1964** typo.el
1965
1966#+begin_src emacs-lisp
1967(use-package typo
1968 :defer 2
1969 :config
1970 (typo-global-mode 1)
1971 :hook (text-mode . typo-mode))
1972#+end_src
1973
1974** hl-todo
1975
1976#+begin_src emacs-lisp
1977(use-package hl-todo
1978 :defer 4
1979 :config
1980 (global-hl-todo-mode))
1981#+end_src
1982
1983** shrink-path
1984
1985#+begin_src emacs-lisp
1986(use-package shrink-path
e931bbdd 1987 :defer 2
5fece105
AB
1988 :config
1989 (setq eshell-prompt-regexp "\\(.*\n\\)*λ "
1990 eshell-prompt-function #'+eshell/prompt)
1991
1992 (defun +eshell/prompt ()
1993 (let ((base/dir (shrink-path-prompt default-directory)))
1994 (concat (propertize (car base/dir)
1995 'face 'font-lock-comment-face)
1996 (propertize (cdr base/dir)
1997 'face 'font-lock-constant-face)
1998 (propertize (+eshell--current-git-branch)
1999 'face 'font-lock-function-name-face)
2000 "\n"
2001 (propertize "λ" 'face 'eshell-prompt-face)
2002 ;; needed for the input text to not have prompt face
2003 (propertize " " 'face 'default))))
2004
2005 (defun +eshell--current-git-branch ()
2006 (let ((branch (car (loop for match in (split-string (shell-command-to-string "git branch") "\n")
2007 when (string-match "^\*" match)
2008 collect match))))
2009 (if (not (eq branch nil))
2010 (concat " " (substring branch 2))
2011 ""))))
2012#+end_src
2013
5fece105
AB
2014** [[https://github.com/peterwvj/eshell-up][eshell-up]]
2015
2016#+begin_src emacs-lisp
2017(use-package eshell-up
e931bbdd 2018 :commands eshell-up
5fece105
AB
2019 :after eshell)
2020#+end_src
2021
2022** multi-term
2023
2024#+begin_src emacs-lisp
2025(use-package multi-term
2026 :defer 1
169947ac 2027 :bind (("C-c a s m" . multi-term-dedicated-toggle)
a219024e
AB
2028 :map term-mode-map
2029 ("C-c C-j" . term-char-mode)
2030 :map term-raw-map
2031 ("C-c C-j" . term-line-mode))
5fece105
AB
2032 :config
2033 (setq multi-term-program "/bin/screen"
2034 ;; TODO: add separate bindings for connecting to existing
2035 ;; session vs. always creating a new one
2036 multi-term-dedicated-select-after-open-p t
2037 multi-term-dedicated-window-height 20
2038 multi-term-dedicated-max-window-height 30
2039 term-bind-key-alist
2040 '(("C-c C-c" . term-interrupt-subjob)
2041 ("C-c C-e" . term-send-esc)
2042 ("C-k" . kill-line)
2043 ("C-y" . term-paste)
2044 ("M-f" . term-send-forward-word)
2045 ("M-b" . term-send-backward-word)
2046 ("M-p" . term-send-up)
2047 ("M-n" . term-send-down)
2048 ("<C-backspace>" . term-send-backward-kill-word)
2049 ("<M-DEL>" . term-send-backward-kill-word)
2050 ("M-d" . term-send-delete-word)
2051 ("M-," . term-send-raw)
2052 ("M-." . comint-dynamic-complete))
2053 term-unbind-key-alist
2054 '("C-z" "C-x" "C-c" "C-h" "C-y" "<ESC>")))
2055#+end_src
2056
2057** page-break-lines
2058
2059#+begin_src emacs-lisp
2060(use-package page-break-lines
2061 :config
2062 (global-page-break-lines-mode))
2063#+end_src
2064
f2a57944
AB
2065** expand-region
2066
2067#+begin_src emacs-lisp
2068(use-package expand-region
2069 :bind ("C-=" . er/expand-region))
2070#+end_src
2071
64e429b5
AB
2072** multiple-cursors
2073
2074#+begin_src emacs-lisp
2075(use-package multiple-cursors
1bbc615c
AB
2076 :bind
2077 (("C-S-<mouse-1>" . mc/add-cursor-on-click)
2078 (:prefix-map a/mc-prefix-map
2079 :prefix "C-c m"
2080 ("c" . mc/edit-lines)
2081 ("n" . mc/mark-next-like-this)
2082 ("p" . mc/mark-previous-like-this)
2083 ("a" . mc/mark-all-like-this))))
64e429b5 2084#+end_src
0884b63b 2085
d4b2a7ef 2086* Email
673d5faa
AB
2087:PROPERTIES:
2088:CUSTOM_ID: email
2089:END:
5fece105 2090
5fece105 2091#+begin_src emacs-lisp
c5d8bb25 2092(defvar a/maildir (expand-file-name "~/mail/"))
ab6781dd 2093(with-eval-after-load 'recentf
c5d8bb25 2094 (add-to-list 'recentf-exclude a/maildir))
5fece105
AB
2095#+end_src
2096
2097** Gnus
2098
2099#+begin_src emacs-lisp
2100(setq
c5d8bb25
AB
2101 a/gnus-init-file (no-littering-expand-etc-file-name "gnus")
2102 mail-user-agent 'gnus-user-agent
2103 read-mail-command 'gnus)
5fece105 2104
d4b2a7ef 2105(use-feature gnus
1bbc615c 2106 :bind (("s-m" . gnus)
a0801748 2107 ("s-M" . gnus-unplugged))
5fece105
AB
2108 :init
2109 (setq
2110 gnus-select-method '(nnnil "")
2111 gnus-secondary-select-methods
2112 '((nnimap "amin"
2113 (nnimap-stream plain)
2114 (nnimap-address "127.0.0.1")
2115 (nnimap-server-port 143)
2116 (nnimap-authenticator plain)
2117 (nnimap-user "amin@aminb.org"))
2118 (nnimap "uwaterloo"
2119 (nnimap-stream plain)
2120 (nnimap-address "127.0.0.1")
2121 (nnimap-server-port 143)
2122 (nnimap-authenticator plain)
2123 (nnimap-user "abandali@uwaterloo.ca")))
2124 gnus-message-archive-group "nnimap+amin:Sent"
2125 gnus-parameters
2126 '(("gnu.*"
2127 (gcc-self . t)))
2128 gnus-large-newsgroup 50
2129 gnus-home-directory (no-littering-expand-var-file-name "gnus/")
2130 gnus-directory (concat gnus-home-directory "news/")
2131 message-directory (concat gnus-home-directory "mail/")
2132 nndraft-directory (concat gnus-home-directory "drafts/")
2133 gnus-save-newsrc-file nil
2134 gnus-read-newsrc-file nil
2135 gnus-interactive-exit nil
2136 gnus-gcc-mark-as-read t))
2137
d4b2a7ef 2138(use-feature gnus-art
5fece105
AB
2139 :config
2140 (setq
2141 gnus-visible-headers
2142 (concat gnus-visible-headers "\\|^List-Id:\\|^X-RT-Originator:\\|^User-Agent:")
2143 gnus-sorted-header-list
2144 '("^From:" "^Subject:" "^Summary:" "^Keywords:"
2145 "^Followup-To:" "^To:" "^Cc:" "X-RT-Originator"
2146 "^Newsgroups:" "List-Id:" "^Organization:"
2147 "^User-Agent:" "^Date:")
2148 ;; local-lapsed article dates
2149 ;; from https://www.emacswiki.org/emacs/GnusFormatting#toc11
2150 gnus-article-date-headers '(user-defined)
2151 gnus-article-time-format
2152 (lambda (time)
2153 (let* ((date (format-time-string "%a, %d %b %Y %T %z" time))
2154 (local (article-make-date-line date 'local))
2155 (combined-lapsed (article-make-date-line date
2156 'combined-lapsed))
2157 (lapsed (progn
2158 (string-match " (.+" combined-lapsed)
2159 (match-string 0 combined-lapsed))))
2160 (concat local lapsed))))
2161 (bind-keys
2162 :map gnus-article-mode-map
2163 ("r" . gnus-article-reply-with-original)
2164 ("R" . gnus-article-wide-reply-with-original)
2165 ("M-L" . org-store-link)))
2166
d4b2a7ef 2167(use-feature gnus-sum
5fece105 2168 :bind (:map gnus-summary-mode-map
c5d8bb25 2169 :prefix-map a/gnus-summary-prefix-map
5fece105
AB
2170 :prefix "v"
2171 ("r" . gnus-summary-reply)
2172 ("w" . gnus-summary-wide-reply)
2173 ("v" . gnus-summary-show-raw-article))
2174 :config
2175 (bind-keys
2176 :map gnus-summary-mode-map
2177 ("r" . gnus-summary-reply-with-original)
2178 ("R" . gnus-summary-wide-reply-with-original)
2179 ("M-L" . org-store-link))
c5d8bb25 2180 :hook (gnus-summary-mode . a/no-mouse-autoselect-window))
5fece105 2181
d4b2a7ef 2182(use-feature gnus-msg
5fece105
AB
2183 :config
2184 (setq gnus-posting-styles
2185 '((".*"
2186 (address "amin@aminb.org")
2187 (body "\nBest,\namin\n")
c5d8bb25 2188 (eval (setq a/message-cite-say-hi t)))
5fece105
AB
2189 ("gnu.*"
2190 (address "bandali@gnu.org"))
2191 ((header "subject" "ThankCRM")
2192 (to "webmasters-comment@gnu.org")
2193 (body "\nAdded to 2018supporters.html.\n\nMoving to campaigns.\n\n-amin\n")
c5d8bb25 2194 (eval (setq a/message-cite-say-hi nil)))
5fece105
AB
2195 ("nnimap\\+uwaterloo:.*"
2196 (address "abandali@uwaterloo.ca")
2197 (gcc "\"nnimap+uwaterloo:Sent Items\"")))))
2198
d4b2a7ef 2199(use-feature gnus-topic
5fece105
AB
2200 :hook (gnus-group-mode . gnus-topic-mode))
2201
d4b2a7ef 2202(use-feature gnus-agent
5fece105
AB
2203 :config
2204 (setq gnus-agent-synchronize-flags 'ask)
2205 :hook (gnus-group-mode . gnus-agent-mode))
2206
d4b2a7ef 2207(use-feature gnus-group
5fece105
AB
2208 :config
2209 (setq gnus-permanently-visible-groups "\\((INBOX\\|gnu$\\)"))
2210
d4b2a7ef 2211(use-feature mm-decode
5fece105
AB
2212 :config
2213 (setq mm-discouraged-alternatives '("text/html" "text/richtext")))
2214#+end_src
2215
2216** sendmail
2217
2218#+begin_src emacs-lisp
d4b2a7ef 2219(use-feature sendmail
5fece105
AB
2220 :config
2221 (setq sendmail-program "/usr/bin/msmtp"
2222 ;; message-sendmail-extra-arguments '("-v" "-d")
2223 mail-specify-envelope-from t
2224 mail-envelope-from 'header))
2225#+end_src
2226
2227** message
2228
2229#+begin_src emacs-lisp
d4b2a7ef 2230(use-feature message
5fece105 2231 :config
c5d8bb25 2232 (defconst a/message-cite-style-format "On %Y-%m-%d %l:%M %p, %N wrote:")
5fece105
AB
2233 (defconst message-cite-style-bandali
2234 '((message-cite-function 'message-cite-original)
2235 (message-citation-line-function 'message-insert-formatted-citation-line)
2236 (message-cite-reply-position 'traditional)
2237 (message-yank-prefix "> ")
2238 (message-yank-cited-prefix ">")
2239 (message-yank-empty-prefix ">")
2240 (message-citation-line-format
c5d8bb25
AB
2241 (if a/message-cite-say-hi
2242 (concat "Hi %F,\n\n" a/message-cite-style-format)
2243 a/message-cite-style-format)))
5fece105
AB
2244 "Citation style based on Mozilla Thunderbird's. Use with message-cite-style.")
2245 (setq message-cite-style 'message-cite-style-bandali
2246 message-kill-buffer-on-exit t
2247 message-send-mail-function 'message-send-mail-with-sendmail
2248 message-sendmail-envelope-from 'header
2249 message-dont-reply-to-names
2250 "\\(\\(.*@aminb\\.org\\)\\|\\(amin@bandali\\.me\\)\\|\\(\\(aminb?\\|mab\\|bandali\\)@gnu\\.org\\)\\|\\(\\(m\\|a\\(min\\.\\)?\\)bandali@uwaterloo\\.ca\\)\\)"
2251 message-user-fqdn "aminb.org")
2252 :hook (;; (message-setup . mml-secure-message-sign-pgpmime)
2253 (message-mode . flyspell-mode)
2254 (message-mode . (lambda ()
2255 ;; (setq fill-column 65
2256 ;; message-fill-column 65)
2257 (make-local-variable 'company-idle-delay)
2258 (setq company-idle-delay 0.2))))
2259 ;; :custom-face
2260 ;; (message-header-subject ((t (:foreground "#111" :weight semi-bold))))
2261 ;; (message-header-to ((t (:foreground "#111" :weight normal))))
2262 ;; (message-header-cc ((t (:foreground "#333" :weight normal))))
2263 )
2264
ab6781dd 2265(with-eval-after-load 'mml-sec
5fece105
AB
2266 (setq mml-secure-openpgp-encrypt-to-self t
2267 mml-secure-openpgp-sign-with-sender t))
2268#+end_src
2269
2270** footnote
2271
2272Convenient footnotes in =message-mode=.
2273
2274#+begin_src emacs-lisp
d4b2a7ef 2275(use-feature footnote
5fece105
AB
2276 :after message
2277 :bind
2278 (:map message-mode-map
c5d8bb25 2279 :prefix-map a/footnote-prefix-map
5fece105
AB
2280 :prefix "C-c f"
2281 ("a" . footnote-add-footnote)
2282 ("b" . footnote-back-to-message)
2283 ("c" . footnote-cycle-style)
2284 ("d" . footnote-delete-footnote)
2285 ("g" . footnote-goto-footnote)
2286 ("r" . footnote-renumber-footnotes)
2287 ("s" . footnote-set-style))
2288 :config
2289 (setq footnote-start-tag ""
2290 footnote-end-tag ""
2291 footnote-style 'unicode))
2292#+end_src
2293
490554d3
AB
2294** ebdb
2295
2296#+begin_src emacs-lisp
2297(use-package ebdb
86efa0e3 2298 :straight (:host github :repo "girzel/ebdb")
04a268e3 2299 :after gnus
490554d3
AB
2300 :bind (:map gnus-group-mode-map ("e" . ebdb))
2301 :config
2302 (setq ebdb-sources (no-littering-expand-var-file-name "ebdb"))
ab6781dd 2303 (with-eval-after-load 'swiper
490554d3
AB
2304 (add-to-list 'swiper-font-lock-exclude 'ebdb-mode t)))
2305
d4b2a7ef 2306(use-feature ebdb-com
490554d3
AB
2307 :after ebdb)
2308
2309;; (use-package ebdb-complete
2310;; :after ebdb
2311;; :config
2312;; (ebdb-complete-enable))
2313
2314(use-package company-ebdb
2315 :after (:all company message)
2316 :config
2317 (defun company-ebdb--post-complete (_) nil)
2318 :hook
2319 (message-mode . (lambda ()
2320 (add-to-list (make-local-variable 'company-backends)
2321 'company-ebdb))))
2322
d4b2a7ef 2323(use-feature ebdb-gnus
490554d3 2324 :after ebdb
bf233291 2325 :demand
490554d3
AB
2326 :custom
2327 (ebdb-gnus-window-configuration
2328 '(article
2329 (vertical 1.0
2330 (summary 0.25 point)
2331 (horizontal 1.0
2332 (article 1.0)
2333 (ebdb-gnus 0.3))))))
2334
d4b2a7ef 2335(use-feature ebdb-mua
490554d3 2336 :after ebdb
bf233291 2337 :demand
490554d3
AB
2338 ;; :custom (ebdb-mua-pop-up nil)
2339 )
2340
2341;; (use-package ebdb-message
2342;; :after ebdb)
2343
2344
2345;; (use-package ebdb-vcard
2346;; :after ebdb)
2347#+end_src
2348
9cebbd53 2349** COMMENT message-x
5fece105
AB
2350
2351#+begin_src emacs-lisp
2352(use-package message-x
5fece105
AB
2353 :custom
2354 (message-x-completion-alist
2355 (quote
2356 (("\\([rR]esent-\\|[rR]eply-\\)?[tT]o:\\|[bB]?[cC][cC]:" . gnus-harvest-find-address)
2357 ((if
2358 (boundp
2359 (quote message-newgroups-header-regexp))
2360 message-newgroups-header-regexp message-newsgroups-header-regexp)
2361 . message-expand-group)))))
2362#+end_src
2363
9cebbd53 2364** COMMENT gnus-harvest
5fece105
AB
2365
2366#+begin_src emacs-lisp
2367(use-package gnus-harvest
5fece105
AB
2368 :commands gnus-harvest-install
2369 :demand t
2370 :config
2371 (if (featurep 'message-x)
2372 (gnus-harvest-install 'message-x)
2373 (gnus-harvest-install)))
2374#+end_src
2375
c27a9ad6 2376* Blogging
673d5faa
AB
2377:PROPERTIES:
2378:CUSTOM_ID: blogging
2379:END:
5fece105 2380
5fece105
AB
2381** [[https://ox-hugo.scripter.co][ox-hugo]]
2382
2383#+begin_src emacs-lisp
2384(use-package ox-hugo
2385 :after ox)
2386
c27a9ad6
AB
2387(use-feature ox-hugo-auto-export
2388 :after ox-hugo)
5fece105
AB
2389#+end_src
2390
2391* Post initialization
2392:PROPERTIES:
2393:CUSTOM_ID: post-initialization
2394:END:
2395
5fece105
AB
2396Display how long it took to load the init file.
2397
2398#+begin_src emacs-lisp
2399(message "Loading %s...done (%.3fs)" user-init-file
2400 (float-time (time-subtract (current-time)
c5d8bb25 2401 a/before-user-init-time)))
5fece105
AB
2402#+end_src
2403
2404* Footer
2405:PROPERTIES:
2406:CUSTOM_ID: footer
2407:END:
2408
2409#+begin_src emacs-lisp :comments none
2410;;; init.el ends here
2411#+end_src
2412
2413* COMMENT Local Variables :ARCHIVE:
2414# Local Variables:
c31b1ac0 2415# eval: (add-hook 'after-save-hook #'a/async-babel-tangle 'append 'local)
6b569be8 2416# eval: (typo-mode -1)
5fece105 2417# End: