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