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