8becc146b9479e1aa9048f7a5b0c82a51a7a1365
[~bandali/configs] / init.el
1 ;;; init.el --- Amin Bandali's Emacs config -*- lexical-binding: t -*-
2
3 ;; Copyright (C) 2018 Amin Bandali <bandali@gnu.org>
4
5 ;; This program is free software: you can redistribute it and/or modify
6 ;; it under the terms of the GNU General Public License as published by
7 ;; the Free Software Foundation, either version 3 of the License, or
8 ;; (at your option) any later version.
9
10 ;; This program is distributed in the hope that it will be useful,
11 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ;; GNU General Public License for more details.
14
15 ;; You should have received a copy of the GNU General Public License
16 ;; along with this program. If not, see <https://www.gnu.org/licenses/>.
17
18 ;;; Commentary:
19
20 ;; Emacs configuration of Amin Bandali, computer scientist and functional
21 ;; programmer.
22
23 ;; THIS FILE IS AUTO-GENERATED FROM `init.org'.
24
25 ;; Naming conventions:
26 ;;
27 ;; amin-... public variables or non-interactive functions
28 ;; amin--... private anything (non-interactive), not safe for direct use
29 ;; amin/... an interactive function; safe for M-x or keybinding
30 ;; amin|... a hook function
31 ;; amin*... an advising function
32 ;; amin@... a hydra command
33 ;; ...! a macro
34
35 ;;; Code:
36
37 \f
38 ;; * Initial setup
39
40 (defvar amin--before-user-init-time (current-time)
41 "Value of `current-time' when Emacs begins loading `user-init-file'.")
42 (message "Loading Emacs...done (%.3fs)"
43 (float-time (time-subtract amin--before-user-init-time
44 before-init-time)))
45
46 (defvar amin--gc-cons-threshold gc-cons-threshold)
47 (defvar amin--gc-cons-percentage gc-cons-percentage)
48 (defvar amin--file-name-handler-alist file-name-handler-alist)
49 (setq gc-cons-threshold (* 400 1024 1024) ; 400 MiB
50 gc-cons-percentage 0.6
51 file-name-handler-alist nil
52 ;; sidesteps a bug when profiling with esup
53 esup-child-profile-require-level 0)
54
55 (add-hook
56 'after-init-hook
57 (lambda ()
58 (setq gc-cons-threshold amin--gc-cons-threshold
59 gc-cons-percentage amin--gc-cons-percentage
60 file-name-handler-alist amin--file-name-handler-alist)))
61
62 (setq message-log-max 20000)
63
64 ;; (setq byte-compile-warnings
65 ;; '(not free-vars unresolved noruntime lexical make-local))
66
67 (setq user-full-name "Amin Bandali"
68 user-mail-address "amin@aminb.org")
69
70 (setq package-enable-at-startup nil)
71 ;; (package-initialize)
72
73 (setq user-init-file (or load-file-name buffer-file-name)
74 user-emacs-directory (file-name-directory user-init-file))
75 (add-to-list 'load-path
76 (expand-file-name "lib/borg" user-emacs-directory))
77 (require 'borg)
78 (borg-initialize)
79
80 ;; (require 'borg-nix-shell)
81 ;; (setq borg-build-shell-command 'borg-nix-shell-build-command)
82
83 (with-eval-after-load 'bind-key
84 (bind-keys
85 :package borg
86 ("C-c b A" . borg-activate)
87 ("C-c b a" . borg-assimilate)
88 ("C-c b b" . borg-build)
89 ("C-c b c" . borg-clone)
90 ("C-c b r" . borg-remove)))
91
92 (require 'use-package)
93 (if nil ; set to t when need to debug init
94 (setq use-package-verbose t
95 use-package-expand-minimally nil
96 use-package-compute-statistics t
97 debug-on-error t)
98 (setq use-package-verbose nil
99 use-package-expand-minimally t))
100
101 (use-package epkg
102 :defer t
103 :bind
104 (("C-c b d" . epkg-describe-package)
105 ("C-c b p" . epkg-list-packages)
106 ("C-c b u" . epkg-update)))
107
108 (use-package no-littering
109 :demand t
110 :config
111 (savehist-mode 1)
112 (add-to-list 'savehist-additional-variables 'kill-ring)
113 (save-place-mode 1)
114 (setq auto-save-file-name-transforms
115 `((".*" ,(no-littering-expand-var-file-name "auto-save/") t))))
116
117 (use-package custom
118 :no-require t
119 :config
120 (setq custom-file (no-littering-expand-etc-file-name "custom.el"))
121 (when (file-exists-p custom-file)
122 (load custom-file))
123 (setf custom-safe-themes t))
124
125 (with-demoted-errors
126 (load (no-littering-expand-etc-file-name "secrets")))
127
128 (use-package exec-path-from-shell
129 :defer 1
130 :init
131 (setq exec-path-from-shell-check-startup-files nil)
132 :config
133 (exec-path-from-shell-initialize)
134 ;; while we're at it, let's fix access to our running ssh-agent
135 (exec-path-from-shell-copy-env "SSH_AGENT_PID")
136 (exec-path-from-shell-copy-env "SSH_AUTH_SOCK"))
137
138 ;; only one custom theme at a time
139 ;;
140 ;; (defadvice load-theme (before clear-previous-themes activate)
141 ;; "Clear existing theme settings instead of layering them"
142 ;; (mapc #'disable-theme custom-enabled-themes))
143
144 (use-package server
145 :defer 1
146 :config (or (server-running-p) (server-mode)))
147
148 ;; unicode support
149 ;;
150 ;; (dolist (ft (fontset-list))
151 ;; (set-fontset-font
152 ;; ft
153 ;; 'unicode
154 ;; (font-spec :name "Source Code Pro" :size 14))
155 ;; (set-fontset-font
156 ;; ft
157 ;; 'unicode
158 ;; (font-spec :name "DejaVu Sans Mono")
159 ;; nil
160 ;; 'append)
161 ;; ;; (set-fontset-font
162 ;; ;; ft
163 ;; ;; 'unicode
164 ;; ;; (font-spec
165 ;; ;; :name "Symbola monospacified for DejaVu Sans Mono")
166 ;; ;; nil
167 ;; ;; 'append)
168 ;; ;; (set-fontset-font
169 ;; ;; ft
170 ;; ;; #x2115 ; ℕ
171 ;; ;; (font-spec :name "DejaVu Sans Mono")
172 ;; ;; nil
173 ;; ;; 'append)
174 ;; (set-fontset-font
175 ;; ft
176 ;; (cons ?Α ?ω)
177 ;; (font-spec :name "DejaVu Sans Mono" :size 14)
178 ;; nil
179 ;; 'prepend))
180
181 (setq text-scale-mode-step 1.05)
182
183 (setq mouse-autoselect-window t)
184
185 (defun amin--no-mouse-autoselect-window ()
186 (make-local-variable 'mouse-autoselect-window)
187 (setq mouse-autoselect-window nil))
188
189 (require 'cl-lib)
190 (require 'subr-x)
191
192 (defun amin-enlist (exp)
193 "Return EXP wrapped in a list, or as-is if already a list."
194 (if (listp exp) exp (list exp)))
195
196 ; from https://github.com/hlissner/doom-emacs/commit/589108fdb270f24a98ba6209f6955fe41530b3ef
197 (defmacro after! (features &rest body)
198 "A smart wrapper around `with-eval-after-load'. Supresses warnings during
199 compilation."
200 (declare (indent defun) (debug t))
201 (list (if (or (not (bound-and-true-p byte-compile-current-file))
202 (dolist (next (amin-enlist features))
203 (if (symbolp next)
204 (require next nil :no-error)
205 (load next :no-message :no-error))))
206 #'progn
207 #'with-no-warnings)
208 (cond ((symbolp features)
209 `(eval-after-load ',features '(progn ,@body)))
210 ((and (consp features)
211 (memq (car features) '(:or :any)))
212 `(progn
213 ,@(cl-loop for next in (cdr features)
214 collect `(after! ,next ,@body))))
215 ((and (consp features)
216 (memq (car features) '(:and :all)))
217 (dolist (next (cdr features))
218 (setq body `(after! ,next ,@body)))
219 body)
220 ((listp features)
221 `(after! (:all ,@features) ,@body)))))
222
223 (defmacro setq-every! (value &rest vars)
224 "Set all the variables from VARS to value VALUE."
225 (declare (indent defun) (debug t))
226 `(progn ,@(mapcar (lambda (x) (list 'setq x value)) vars)))
227
228 \f
229 ;; * Core
230
231 ;; (fringe-mode '(3 . 1))
232 (fringe-mode nil)
233
234 (setq disabled-command-function nil)
235
236 (setq save-interprogram-paste-before-kill t)
237
238 (setq enable-recursive-minibuffers t
239 resize-mini-windows t)
240
241 (defalias 'yes-or-no-p #'y-or-n-p)
242
243 (setq initial-buffer-choice t)
244
245 (setq initial-scratch-message nil)
246
247 (setq initial-major-mode 'text-mode)
248
249 (setq inhibit-startup-buffer-menu t)
250
251 (advice-add #'display-startup-echo-area-message :override #'ignore)
252 (setq inhibit-startup-screen t
253 inhibit-startup-echo-area-message user-login-name)
254
255 (setq frame-title-format
256 '("" invocation-name " - "
257 (:eval (if (buffer-file-name)
258 (abbreviate-file-name (buffer-file-name))
259 "%b"))))
260
261 (setq backup-by-copying t
262 version-control t
263 delete-old-versions t)
264
265 (global-auto-revert-mode 1)
266 (setq auto-revert-verbose nil
267 global-auto-revert-non-file-buffers nil)
268
269 (setq-default
270 indent-tabs-mode nil
271 require-final-newline t
272 tab-width 4)
273
274 (winner-mode 1)
275
276 (setq compilation-exit-message-function
277 (lambda (status code msg)
278 "Close the compilation window if successful."
279 ;; if M-x compile exits with 0
280 (when (and (eq status 'exit) (zerop code))
281 (bury-buffer)
282 (delete-window (get-buffer-window (get-buffer "*compilation*"))))
283 ;; return the result of compilation-exit-message-function
284 (cons msg code)))
285
286 (setq search-default-mode #'char-fold-to-regexp)
287
288 ;; uncomment to extend this behaviour to query-replace
289 ;; (setq replace-char-fold t)
290
291 (bind-keys
292 ("C-c a i" . ielm)
293
294 ("C-c e b" . eval-buffer)
295 ("C-c e r" . eval-region)
296
297 ("C-c F m" . make-frame-command)
298 ("C-c F d" . delete-frame)
299 ("C-c F D" . delete-other-frames)
300
301 ("C-c o" . other-window)
302
303 ("C-c Q" . save-buffers-kill-terminal)
304
305 ("C-S-h C" . describe-char)
306 ("C-S-h F" . describe-face)
307
308 ("C-x K" . kill-this-buffer)
309
310 ("s-p" . beginning-of-buffer)
311 ("s-n" . end-of-buffer))
312
313 (use-package auto-compile
314 :demand t
315 :config
316 (auto-compile-on-load-mode)
317 (auto-compile-on-save-mode)
318 (setq auto-compile-display-buffer nil
319 auto-compile-mode-line-counter t
320 auto-compile-source-recreate-deletes-dest t
321 auto-compile-toggle-deletes-nonlib-dest t
322 auto-compile-update-autoloads t)
323 (add-hook 'auto-compile-inhibit-compile-hook
324 'auto-compile-inhibit-compile-detached-git-head))
325
326 (use-package org
327 :defer 1
328 :config
329 (setq org-src-tab-acts-natively t
330 org-src-preserve-indentation nil
331 org-edit-src-content-indentation 0
332 org-email-link-description-format "Email %c: %s" ; %.30s
333 org-highlight-latex-and-related '(entities)
334 org-log-done 'time)
335 (add-to-list 'org-structure-template-alist '("L" . "src emacs-lisp") t)
336 (font-lock-add-keywords
337 'org-mode
338 '(("[ \t]*\\(#\\+\\(BEGIN\\|END\\|begin\\|end\\)_\\(\\S-+\\)\\)[ \t]*\\([^\n:]*\\)"
339 (1 '(:foreground "#5a5b5a" :background "#292b2b") t) ; directive
340 (3 '(:foreground "#81a2be" :background "#292b2b") t) ; kind
341 (4 '(:foreground "#c5c8c6") t))) ; title
342 t)
343 :bind (:map org-mode-map ("M-L" . org-insert-last-stored-link))
344 :hook ((org-mode . org-indent-mode)
345 (org-mode . auto-fill-mode)
346 (org-mode . flyspell-mode))
347 :custom
348 (org-latex-packages-alist '(("" "listings") ("" "color")))
349 :custom-face
350 '(org-block-begin-line ((t (:foreground "#5a5b5a" :background "#1d1f21"))))
351 '(org-block ((t (:background "#1d1f21"))))
352 '(org-latex-and-related ((t (:foreground "#b294bb")))))
353
354 (use-package ox-latex
355 :after ox
356 :config
357 (setq org-latex-listings 'listings
358 ;; org-latex-prefer-user-labels t
359 )
360 (add-to-list 'org-latex-packages-alist '("" "listings"))
361 (add-to-list 'org-latex-packages-alist '("" "color"))
362 (add-to-list 'org-latex-classes
363 '("IEEEtran" "\\documentclass[11pt]{IEEEtran}"
364 ("\\section{%s}" . "\\section*{%s}")
365 ("\\subsection{%s}" . "\\subsection*{%s}")
366 ("\\subsubsection{%s}" . "\\subsubsection*{%s}")
367 ("\\paragraph{%s}" . "\\paragraph*{%s}")
368 ("\\subparagraph{%s}" . "\\subparagraph*{%s}"))
369 t))
370
371 (use-package ox-beamer
372 :after ox)
373
374 (use-package orgalist
375 :after message
376 :hook (message-mode . orgalist-mode))
377
378 (after! org
379 (defvar amin-show-async-tangle-results nil
380 "Keep *emacs* async buffers around for later inspection.")
381
382 (defvar amin-show-async-tangle-time nil
383 "Show the time spent tangling the file.")
384
385 (defvar amin-async-tangle-post-compile "make ti"
386 "If non-nil, pass to `compile' after successful tangle.")
387
388 (defun amin/async-babel-tangle ()
389 "Tangle org file asynchronously."
390 (interactive)
391 (let* ((file-tangle-start-time (current-time))
392 (file (buffer-file-name))
393 (file-nodir (file-name-nondirectory file))
394 (async-quiet-switch "-q"))
395 (async-start
396 `(lambda ()
397 (require 'org)
398 (org-babel-tangle-file ,file))
399 (unless amin-show-async-tangle-results
400 `(lambda (result)
401 (if result
402 (progn
403 (message "Tangled %s%s"
404 ,file-nodir
405 (if amin-show-async-tangle-time
406 (format " (%.3fs)"
407 (float-time (time-subtract (current-time)
408 ',file-tangle-start-time)))
409 ""))
410 (when amin-async-tangle-post-compile
411 (compile amin-async-tangle-post-compile)))
412 (message "Tangling %s failed" ,file-nodir))))))))
413
414 (add-to-list
415 'safe-local-variable-values
416 '(eval add-hook 'after-save-hook #'amin/async-babel-tangle 'append 'local))
417
418 (use-package magit
419 :defer 1
420 :bind (("C-x g" . magit-status)
421 ("s-g s" . magit-status)
422 ("s-g l" . magit-log-buffer-file))
423 :config
424 (magit-add-section-hook 'magit-status-sections-hook
425 'magit-insert-modules
426 'magit-insert-stashes
427 'append)
428 (setq
429 magit-repository-directories '(("~/.emacs.d/" . 0)
430 ("~/src/git/" . 1)))
431 (nconc magit-section-initial-visibility-alist
432 '(([unpulled status] . show)
433 ([unpushed status] . show)))
434 :custom-face (magit-diff-file-heading ((t (:weight normal)))))
435
436 (use-package ivy
437 :defer 1
438 :bind
439 (:map ivy-minibuffer-map
440 ([escape] . keyboard-escape-quit)
441 ([S-up] . ivy-previous-history-element)
442 ([S-down] . ivy-next-history-element)
443 ("DEL" . ivy-backward-delete-char))
444 :config
445 (setq ivy-wrap t)
446 (ivy-mode 1)
447 ;; :custom-face
448 ;; (ivy-minibuffer-match-face-2 ((t (:background "#e99ce8" :weight semi-bold))))
449 ;; (ivy-minibuffer-match-face-3 ((t (:background "#bbbbff" :weight semi-bold))))
450 ;; (ivy-minibuffer-match-face-4 ((t (:background "#ffbbff" :weight semi-bold))))
451 )
452
453 (use-package swiper
454 :bind (("C-s" . swiper)
455 ("C-r" . swiper)))
456
457 (use-package counsel
458 :defer 1
459 :bind (([remap execute-extended-command] . counsel-M-x)
460 ([remap find-file] . counsel-find-file)
461 ("s-r" . counsel-recentf)
462 ("C-c x" . counsel-M-x)
463 ("C-c f ." . counsel-find-file)
464 :map minibuffer-local-map
465 ("C-r" . counsel-minibuffer-history))
466 :config
467 (counsel-mode 1)
468 (defalias 'locate #'counsel-locate))
469
470 (use-package eshell
471 :defer 1
472 :commands eshell
473 :config
474 (eval-when-compile (defvar eshell-prompt-regexp))
475 (defun amin/eshell-quit-or-delete-char (arg)
476 (interactive "p")
477 (if (and (eolp) (looking-back eshell-prompt-regexp nil))
478 (eshell-life-is-too-much)
479 (delete-char arg)))
480
481 (defun amin/eshell-clear ()
482 (interactive)
483 (let ((inhibit-read-only t))
484 (erase-buffer))
485 (eshell-send-input))
486
487 (defun amin|eshell-setup ()
488 (make-local-variable 'company-idle-delay)
489 (setq company-idle-delay nil)
490 (bind-keys :map eshell-mode-map
491 ("C-d" . amin/eshell-quit-or-delete-char)
492 ("C-S-l" . amin/eshell-clear)
493 ("M-r" . counsel-esh-history)
494 ([tab] . company-complete)))
495
496 :hook (eshell-mode . amin|eshell-setup)
497 :custom
498 (eshell-hist-ignoredups t)
499 (eshell-input-filter 'eshell-input-filter-initial-space))
500
501 (use-package ibuffer
502 :defer t
503 :bind
504 (("C-x C-b" . ibuffer-other-window)
505 :map ibuffer-mode-map
506 ("P" . ibuffer-backward-filter-group)
507 ("N" . ibuffer-forward-filter-group)
508 ("M-p" . ibuffer-do-print)
509 ("M-n" . ibuffer-do-shell-command-pipe-replace))
510 :config
511 ;; Use human readable Size column instead of original one
512 (define-ibuffer-column size-h
513 (:name "Size" :inline t)
514 (cond
515 ((> (buffer-size) 1000000) (format "%7.1fM" (/ (buffer-size) 1000000.0)))
516 ((> (buffer-size) 100000) (format "%7.0fk" (/ (buffer-size) 1000.0)))
517 ((> (buffer-size) 1000) (format "%7.1fk" (/ (buffer-size) 1000.0)))
518 (t (format "%8d" (buffer-size)))))
519 :custom
520 (ibuffer-saved-filter-groups
521 '(("default"
522 ("dired" (mode . dired-mode))
523 ("org" (mode . org-mode))
524 ("web"
525 (or
526 (mode . web-mode)
527 (mode . css-mode)
528 (mode . scss-mode)
529 (mode . js2-mode)))
530 ("shell"
531 (or
532 (mode . eshell-mode)
533 (mode . shell-mode)))
534 ("notmuch" (name . "\*notmuch\*"))
535 ("programming"
536 (or
537 (mode . python-mode)
538 (mode . c++-mode)
539 (mode . emacs-lisp-mode)))
540 ("emacs"
541 (or
542 (name . "^\\*scratch\\*$")
543 (name . "^\\*Messages\\*$")))
544 ("slack"
545 (or
546 (name . "^\\*Slack*"))))))
547 (ibuffer-formats
548 '((mark modified read-only locked " "
549 (name 18 18 :left :elide)
550 " "
551 (size-h 9 -1 :right)
552 " "
553 (mode 16 16 :left :elide)
554 " " filename-and-process)
555 (mark " "
556 (name 16 -1)
557 " " filename)))
558 :hook (ibuffer . (lambda () (ibuffer-switch-to-saved-filter-groups "default"))))
559
560 (use-package outline
561 :defer t
562 :hook (prog-mode . outline-minor-mode)
563 :bind
564 (:map
565 outline-minor-mode-map
566 ("<s-tab>" . outline-toggle-children)
567 ("M-p" . outline-previous-visible-heading)
568 ("M-n" . outline-next-visible-heading)
569 :prefix-map amin--outline-prefix-map
570 :prefix "s-o"
571 ("TAB" . outline-toggle-children)
572 ("a" . outline-hide-body)
573 ("H" . outline-hide-body)
574 ("S" . outline-show-all)
575 ("h" . outline-hide-subtree)
576 ("s" . outline-show-subtree)))
577
578 \f
579 ;; * Borg's `layer/essentials'
580
581 (use-package dash
582 :config (dash-enable-font-lock))
583
584 (use-package diff-hl
585 :config
586 (setq diff-hl-draw-borders nil)
587 (global-diff-hl-mode)
588 (add-hook 'magit-post-refresh-hook 'diff-hl-magit-post-refresh t))
589
590 (use-package dired
591 :defer t
592 :config (setq dired-listing-switches "-alh"))
593
594 (use-package eldoc
595 :when (version< "25" emacs-version)
596 :config (global-eldoc-mode))
597
598 (use-package help
599 :defer t
600 :config
601 (temp-buffer-resize-mode)
602 (setq help-window-select t))
603
604 (progn ; `isearch'
605 (setq isearch-allow-scroll t))
606
607 (use-package lisp-mode
608 :config
609 (add-hook 'emacs-lisp-mode-hook 'outline-minor-mode)
610 (add-hook 'emacs-lisp-mode-hook 'reveal-mode)
611 (defun indent-spaces-mode ()
612 (setq indent-tabs-mode nil))
613 (add-hook 'lisp-interaction-mode-hook #'indent-spaces-mode))
614
615 (use-package man
616 :defer t
617 :config (setq Man-width 80))
618
619 (use-package paren
620 :config (show-paren-mode))
621
622 (use-package prog-mode
623 :config (global-prettify-symbols-mode)
624 (defun indicate-buffer-boundaries-left ()
625 (setq indicate-buffer-boundaries 'left))
626 (add-hook 'prog-mode-hook #'indicate-buffer-boundaries-left))
627
628 (use-package recentf
629 :defer 0.5
630 :config
631 (add-to-list 'recentf-exclude "^/\\(?:ssh\\|su\\|sudo\\)?:")
632 (setq recentf-max-saved-items 40))
633
634 (use-package savehist
635 :config (savehist-mode))
636
637 (use-package saveplace
638 :when (version< "25" emacs-version)
639 :config (save-place-mode))
640
641 (use-package simple
642 :config (column-number-mode))
643
644 (progn ; `text-mode'
645 (add-hook 'text-mode-hook #'indicate-buffer-boundaries-left)
646 (add-hook 'text-mode-hook #'abbrev-mode))
647
648 (use-package tramp
649 :defer t
650 :config
651 (add-to-list 'tramp-default-proxies-alist '(nil "\\`root\\'" "/ssh:%h:"))
652 (add-to-list 'tramp-default-proxies-alist '("localhost" nil nil))
653 (add-to-list 'tramp-default-proxies-alist
654 (list (regexp-quote (system-name)) nil nil)))
655
656 \f
657 ;; * Editing
658
659 (use-package company
660 :defer 1
661 :bind
662 (:map company-active-map
663 ([tab] . company-complete-common-or-cycle)
664 ([escape] . company-abort))
665 :custom
666 (company-minimum-prefix-length 1)
667 (company-selection-wrap-around t)
668 (company-dabbrev-char-regexp "\\sw\\|\\s_\\|[-_]")
669 (company-dabbrev-downcase nil)
670 (company-dabbrev-ignore-case nil)
671 :config
672 (global-company-mode t))
673
674 \f
675 ;; * Syntax and spell checking
676
677 (use-package flycheck
678 :defer 3
679 :hook (prog-mode . flycheck-mode)
680 :bind
681 (:map flycheck-mode-map
682 ("M-P" . flycheck-previous-error)
683 ("M-N" . flycheck-next-error))
684 :config
685 ;; Use the load-path from running Emacs when checking elisp files
686 (setq flycheck-emacs-lisp-load-path 'inherit)
687
688 ;; Only flycheck when I actually save the buffer
689 (setq flycheck-check-syntax-automatically '(mode-enabled save)))
690
691 ;; http://endlessparentheses.com/ispell-and-apostrophes.html
692 (use-package ispell
693 :defer 3
694 :config
695 ;; ’ can be part of a word
696 (setq ispell-local-dictionary-alist
697 `((nil "[[:alpha:]]" "[^[:alpha:]]"
698 "['\x2019]" nil ("-B") nil utf-8)))
699 ;; don't send ’ to the subprocess
700 (defun endless/replace-apostrophe (args)
701 (cons (replace-regexp-in-string
702 "’" "'" (car args))
703 (cdr args)))
704 (advice-add #'ispell-send-string :filter-args
705 #'endless/replace-apostrophe)
706
707 ;; convert ' back to ’ from the subprocess
708 (defun endless/replace-quote (args)
709 (if (not (derived-mode-p 'org-mode))
710 args
711 (cons (replace-regexp-in-string
712 "'" "’" (car args))
713 (cdr args))))
714 (advice-add #'ispell-parse-output :filter-args
715 #'endless/replace-quote))
716
717 \f
718 ;; * Programming modes
719
720 (use-package alloy-mode
721 :defer t
722 :config (setq alloy-basic-offset 2))
723
724 (use-package proof-site ; Proof General
725 :defer t
726 :load-path "lib/proof-site/generic/")
727
728 (eval-when-compile (defvar lean-mode-map))
729 (use-package lean-mode
730 :defer 1
731 :bind (:map lean-mode-map
732 ("S-SPC" . company-complete))
733 :config
734 (require 'lean-input)
735 (setq default-input-method "Lean"
736 lean-input-tweak-all '(lean-input-compose
737 (lean-input-prepend "/")
738 (lean-input-nonempty))
739 lean-input-user-translations '(("/" "/")))
740 (lean-input-setup))
741
742 (use-package haskell-mode
743 :defer t
744 :config
745 (setq haskell-indentation-layout-offset 4
746 haskell-indentation-left-offset 4
747 flycheck-checker 'haskell-hlint
748 flycheck-disabled-checkers '(haskell-stack-ghc haskell-ghc)))
749
750 (use-package dante
751 :after haskell-mode
752 :commands dante-mode
753 :hook (haskell-mode . dante-mode))
754
755 (use-package hlint-refactor
756 :after haskell-mode
757 :bind (:map hlint-refactor-mode-map
758 ("C-c l b" . hlint-refactor-refactor-buffer)
759 ("C-c l r" . hlint-refactor-refactor-at-point))
760 :hook (haskell-mode . hlint-refactor-mode))
761
762 (use-package flycheck-haskell
763 :after haskell-mode)
764
765 (use-package sgml-mode
766 :defer t
767 :config
768 (setq sgml-basic-offset 2))
769
770 (use-package css-mode
771 :defer t
772 :config
773 (setq css-indent-offset 2))
774
775 (use-package web-mode
776 :defer t
777 :mode "\\.html\\'"
778 :config
779 (setq-every! 2
780 web-mode-code-indent-offset
781 web-mode-css-indent-offset
782 web-mode-markup-indent-offset))
783
784 (use-package emmet-mode
785 :after (:any web-mode css-mode sgml-mode)
786 :bind* (("C-)" . emmet-next-edit-point)
787 ("C-(" . emmet-prev-edit-point))
788 :config
789 (unbind-key "C-j" emmet-mode-keymap)
790 (setq emmet-move-cursor-between-quotes t)
791 :hook (web-mode css-mode html-mode sgml-mode))
792
793 (use-package nix-mode
794 :defer t
795 :mode "\\.nix\\'")
796
797 \f
798 ;; * Emacs Enhancements
799
800 (use-package which-key
801 :defer 1
802 :config (which-key-mode))
803
804 (add-to-list 'custom-theme-load-path "~/.emacs.d/lisp")
805 (load-theme 'tangomod t)
806
807 (use-package doom-modeline
808 :demand t
809 :config (setq doom-modeline-height 32)
810 :hook (after-init . doom-modeline-init))
811
812 (use-package doom-themes)
813
814 (defun amin/lights-on ()
815 "Enable my favourite light theme."
816 (interactive)
817 (progn
818 (mapc #'disable-theme custom-enabled-themes)
819 (load-theme 'tangomod t)))
820
821 (defun amin/lights-off ()
822 "Go dark."
823 (interactive)
824 (progn
825 (mapc #'disable-theme custom-enabled-themes)
826 (load-theme 'doom-tomorrow-night t)))
827
828 (bind-keys
829 ("s-t d" . amin/lights-off)
830 ("s-t l" . amin/lights-on))
831
832 (use-package crux
833 :defer 1
834 :bind (("C-c b k" . crux-kill-other-buffers)
835 ("C-c d" . crux-duplicate-current-line-or-region)
836 ("C-c D" . crux-duplicate-and-comment-current-line-or-region)
837 ("C-c f c" . crux-copy-file-preserve-attributes)
838 ("C-c f d" . crux-delete-file-and-buffer)
839 ("C-c f r" . crux-rename-file-and-buffer)
840 ("C-c j" . crux-top-join-line)
841 ("C-S-j" . crux-top-join-line)))
842
843 (use-package mwim
844 :bind (("C-a" . mwim-beginning-of-code-or-line)
845 ("C-e" . mwim-end-of-code-or-line)
846 ("<home>" . mwim-beginning-of-line-or-code)
847 ("<end>" . mwim-end-of-line-or-code)))
848
849 (use-package projectile
850 :defer t
851 :bind-keymap ("C-c p" . projectile-command-map)
852 :config
853 (projectile-mode)
854
855 (defun my-projectile-invalidate-cache (&rest _args)
856 ;; ignore the args to `magit-checkout'
857 (projectile-invalidate-cache nil))
858
859 (eval-after-load 'magit-branch
860 '(progn
861 (advice-add 'magit-checkout
862 :after #'my-projectile-invalidate-cache)
863 (advice-add 'magit-branch-and-checkout
864 :after #'my-projectile-invalidate-cache))))
865
866 (use-package helpful
867 :defer 1
868 :bind
869 (("C-S-h c" . helpful-command)
870 ("C-S-h f" . helpful-callable) ; helpful-function
871 ("C-S-h v" . helpful-variable)
872 ("C-S-h k" . helpful-key)
873 ("C-S-h p" . helpful-at-point)))
874
875 (use-package shell-toggle
876 :after eshell
877 :bind ("C-c a s e" . amin/shell-toggle)
878 :config
879 (defun amin/shell-toggle (make-cd)
880 "Toggle between the shell buffer and whatever buffer you are editing.
881 With a prefix argument MAKE-CD also insert a \"cd DIR\" command
882 into the shell, where DIR is the directory of the current buffer.
883
884 When called in the shell buffer returns you to the buffer you were editing
885 before calling this the first time.
886
887 Options: `shell-toggle-goto-eob'"
888 (interactive "P")
889 ;; Try to decide on one of three possibilities:
890 ;; If not in shell-buffer, switch to it.
891 ;; If in shell-buffer, return to state before going to the shell-buffer
892 (if (eq (current-buffer) shell-toggle-shell-buffer)
893 (shell-toggle-buffer-return-from-shell)
894 (progn
895 (shell-toggle-buffer-goto-shell make-cd)
896 (if shell-toggle-full-screen-window-only (delete-other-windows)))))
897
898 ;; override to split horizontally instead
899 (defun shell-toggle-buffer-switch-to-other-window ()
900 "Switch to other window.
901 If the current window is the only window in the current frame,
902 create a new window and switch to it.
903
904 \(This is less intrusive to the current window configuration than
905 `switch-buffer-other-window')"
906 (let ((this-window (selected-window)))
907 (other-window 1)
908 ;; If we did not switch window then we only have one window and need to
909 ;; create a new one.
910 (if (eq this-window (selected-window))
911 (progn
912 (split-window-horizontally)
913 (other-window 1)))))
914
915 :custom
916 (shell-toggle-launch-shell 'shell-toggle-eshell))
917
918 (use-package unkillable-scratch
919 :defer 3
920 :config
921 (unkillable-scratch 1)
922 :custom
923 (unkillable-scratch-behavior 'do-nothing)
924 (unkillable-buffers '("^\\*scratch\\*$" "^\\*Messages\\*$")))
925
926 (use-package boxquote
927 :defer 3
928 :bind
929 (:prefix-map amin--boxquote-prefix-map
930 :prefix "C-c q"
931 ("b" . boxquote-buffer)
932 ("B" . boxquote-insert-buffer)
933 ("d" . boxquote-defun)
934 ("F" . boxquote-insert-file)
935 ("hf" . boxquote-describe-function)
936 ("hk" . boxquote-describe-key)
937 ("hv" . boxquote-describe-variable)
938 ("hw" . boxquote-where-is)
939 ("k" . boxquote-kill)
940 ("p" . boxquote-paragraph)
941 ("q" . boxquote-boxquote)
942 ("r" . boxquote-region)
943 ("s" . boxquote-shell-command)
944 ("t" . boxquote-text)
945 ("T" . boxquote-title)
946 ("u" . boxquote-unbox)
947 ("U" . boxquote-unbox-region)
948 ("y" . boxquote-yank)
949 ("M-q" . boxquote-fill-paragraph)
950 ("M-w" . boxquote-kill-ring-save)))
951
952 (use-package highlight-indent-guides
953 :disabled t
954 :defer 3
955 :hook ((prog-mode . highlight-indent-guides-mode)
956 ;; (org-mode . highlight-indent-guides-mode)
957 )
958 :config
959 (setq highlight-indent-guides-character ?\|)
960 (setq highlight-indent-guides-auto-enabled nil)
961 (setq highlight-indent-guides-method 'character)
962 (setq highlight-indent-guides-responsive 'top)
963 (set-face-foreground 'highlight-indent-guides-character-face "gainsboro")
964 (set-face-foreground 'highlight-indent-guides-top-character-face "grey40")) ; grey13 is nice too
965
966 (use-package pdf-tools
967 :defer t
968 :magic ("%PDF" . pdf-view-mode)
969 :config
970 (setq pdf-view-resize-factor 1.05)
971 (pdf-tools-install)
972 :bind
973 (:map pdf-view-mode-map
974 ("C-s" . isearch-forward)
975 ("C-r" . isearch-backward)
976 ("j" . pdf-view-next-line-or-next-page)
977 ("k" . pdf-view-previous-line-or-previous-page)
978 ("h" . image-backward-hscroll)
979 ("l" . image-forward-hscroll)))
980
981 (use-package anzu)
982
983 (use-package typo
984 :defer 2
985 :config
986 (typo-global-mode 1)
987 :hook (text-mode . typo-mode))
988
989 (use-package hl-todo
990 :defer 4
991 :config
992 (global-hl-todo-mode))
993
994 (use-package shrink-path
995 :after eshell
996 :config
997 (setq eshell-prompt-regexp "\\(.*\n\\)*λ "
998 eshell-prompt-function #'+eshell/prompt)
999
1000 (defun +eshell/prompt ()
1001 (let ((base/dir (shrink-path-prompt default-directory)))
1002 (concat (propertize (car base/dir)
1003 'face 'font-lock-comment-face)
1004 (propertize (cdr base/dir)
1005 'face 'font-lock-constant-face)
1006 (propertize (+eshell--current-git-branch)
1007 'face 'font-lock-function-name-face)
1008 "\n"
1009 (propertize "λ" 'face 'eshell-prompt-face)
1010 ;; needed for the input text to not have prompt face
1011 (propertize " " 'face 'default))))
1012
1013 (defun +eshell--current-git-branch ()
1014 (let ((branch (car (loop for match in (split-string (shell-command-to-string "git branch") "\n")
1015 when (string-match "^\*" match)
1016 collect match))))
1017 (if (not (eq branch nil))
1018 (concat " " (substring branch 2))
1019 ""))))
1020
1021 (use-package slack
1022 :disabled t
1023 :commands (slack-start)
1024 :init
1025 (eval-when-compile ; silence the byte-compiler
1026 (defvar url-http-data nil)
1027 (defvar url-http-extra-headers nil)
1028 (defvar url-http-method nil)
1029 (defvar url-callback-function nil)
1030 (defvar url-callback-arguments nil)
1031 (defvar oauth--token-data nil))
1032 (setq slack-buffer-emojify t
1033 slack-prefer-current-team t)
1034 :config
1035 (slack-register-team
1036 :name "uw-apv"
1037 :default t
1038 :client-id uw-apv-client-id
1039 :client-secret uw-apv-client-secret
1040 :token uw-apv-token
1041 :subscribed-channels '(general)
1042 :full-and-display-names t)
1043 (slack-register-team
1044 :name "watform"
1045 :default nil
1046 :client-id watform-client-id
1047 :client-secret watform-client-secret
1048 :token watform-token
1049 :subscribed-channels '(general)
1050 :full-and-display-names t)
1051 (add-to-list 'swiper-font-lock-exclude 'slack-message-buffer-mode t)
1052 (setq lui-time-stamp-format "[%Y-%m-%d %H:%M:%S]"
1053 lui-time-stamp-only-when-changed-p t
1054 lui-time-stamp-position 'right)
1055 :bind
1056 (("C-c s s" . slack-start)
1057 ("C-c s u" . slack-select-unread-rooms)
1058 ("C-c s b" . slack-select-rooms)
1059 ("C-c s t" . slack-change-current-team)
1060 ("C-c s c" . slack-ws-close)
1061 :map slack-mode-map
1062 ("M-p" . slack-buffer-goto-prev-message)
1063 ("M-n" . slack-buffer-goto-next-message)
1064 ("C-c e" . slack-message-edit)
1065 ("C-c k" . slack-message-delete)
1066 ("C-c C-k" . slack-channel-leave)
1067 ("C-c r a" . slack-message-add-reaction)
1068 ("C-c r r" . slack-message-remove-reaction)
1069 ("C-c r s" . slack-message-show-reaction-users)
1070 ("C-c p l" . slack-room-pins-list)
1071 ("C-c p a" . slack-message-pins-add)
1072 ("C-c p r" . slack-message-pins-remove)
1073 ("@" . slack-message-embed-mention)
1074 ("#" . slack-message-embed-channel)))
1075
1076 (use-package alert
1077 :commands (alert)
1078 :init
1079 (setq alert-default-style 'notifier))
1080
1081 (use-package eshell-up
1082 :after eshell)
1083
1084 (use-package multi-term
1085 :defer 1
1086 :bind (("C-c C-j" . term-line-mode)
1087 ("C-c a s m m" . multi-term)
1088 ("C-c a s m p" . multi-term-dedicated-toggle))
1089 :config
1090 (setq multi-term-program "/bin/screen"
1091 ;; TODO: add separate bindings for connecting to existing
1092 ;; session vs. always creating a new one
1093 multi-term-dedicated-select-after-open-p t
1094 multi-term-dedicated-window-height 20
1095 multi-term-dedicated-max-window-height 30
1096 term-bind-key-alist
1097 '(("C-c C-c" . term-interrupt-subjob)
1098 ("C-c C-e" . term-send-esc)
1099 ("C-k" . kill-line)
1100 ("C-y" . term-paste)
1101 ("M-f" . term-send-forward-word)
1102 ("M-b" . term-send-backward-word)
1103 ("M-p" . term-send-up)
1104 ("M-n" . term-send-down)
1105 ("<C-backspace>" . term-send-backward-kill-word)
1106 ("<M-DEL>" . term-send-backward-kill-word)
1107 ("M-d" . term-send-delete-word)
1108 ("M-," . term-send-raw)
1109 ("M-." . comint-dynamic-complete))
1110 term-unbind-key-alist
1111 '("C-z" "C-x" "C-c" "C-h" "C-y" "<ESC>")))
1112
1113 (use-package page-break-lines
1114 :config
1115 (global-page-break-lines-mode))
1116
1117 \f
1118 ;; * Email
1119
1120 (defvar amin-maildir (expand-file-name "~/mail/"))
1121 (after! recentf
1122 (add-to-list 'recentf-exclude amin-maildir))
1123
1124 (setq
1125 amin-gnus-init-file (no-littering-expand-etc-file-name "gnus")
1126 mail-user-agent 'gnus-user-agent
1127 read-mail-command 'gnus)
1128
1129 (use-package gnus
1130 :bind (("s-m" . gnus)
1131 ("s-M" . gnus-unplugged))
1132 :init
1133 (setq
1134 gnus-select-method '(nnnil "")
1135 gnus-secondary-select-methods
1136 '((nnimap "amin"
1137 (nnimap-stream plain)
1138 (nnimap-address "127.0.0.1")
1139 (nnimap-server-port 143)
1140 (nnimap-authenticator plain)
1141 (nnimap-user "amin@aminb.org"))
1142 (nnimap "uwaterloo"
1143 (nnimap-stream plain)
1144 (nnimap-address "127.0.0.1")
1145 (nnimap-server-port 143)
1146 (nnimap-authenticator plain)
1147 (nnimap-user "abandali@uwaterloo.ca")))
1148 gnus-message-archive-group "nnimap+amin:Sent"
1149 gnus-parameters
1150 '(("gnu.*"
1151 (gcc-self . t)))
1152 gnus-large-newsgroup 50
1153 gnus-home-directory (no-littering-expand-var-file-name "gnus/")
1154 gnus-directory (concat gnus-home-directory "news/")
1155 message-directory (concat gnus-home-directory "mail/")
1156 nndraft-directory (concat gnus-home-directory "drafts/")
1157 gnus-save-newsrc-file nil
1158 gnus-read-newsrc-file nil
1159 gnus-interactive-exit nil
1160 gnus-gcc-mark-as-read t))
1161
1162 (use-package gnus-art
1163 :config
1164 (setq
1165 gnus-visible-headers
1166 (concat gnus-visible-headers "\\|^List-Id:\\|^X-RT-Originator:\\|^User-Agent:")
1167 gnus-sorted-header-list
1168 '("^From:" "^Subject:" "^Summary:" "^Keywords:"
1169 "^Followup-To:" "^To:" "^Cc:" "X-RT-Originator"
1170 "^Newsgroups:" "List-Id:" "^Organization:"
1171 "^User-Agent:" "^Date:")
1172 ;; local-lapsed article dates
1173 ;; from https://www.emacswiki.org/emacs/GnusFormatting#toc11
1174 gnus-article-date-headers '(user-defined)
1175 gnus-article-time-format
1176 (lambda (time)
1177 (let* ((date (format-time-string "%a, %d %b %Y %T %z" time))
1178 (local (article-make-date-line date 'local))
1179 (combined-lapsed (article-make-date-line date
1180 'combined-lapsed))
1181 (lapsed (progn
1182 (string-match " (.+" combined-lapsed)
1183 (match-string 0 combined-lapsed))))
1184 (concat local lapsed))))
1185 (bind-keys
1186 :map gnus-article-mode-map
1187 ("r" . gnus-article-reply-with-original)
1188 ("R" . gnus-article-wide-reply-with-original)
1189 ("M-L" . org-store-link)))
1190
1191 (use-package gnus-sum
1192 :bind (:map gnus-summary-mode-map
1193 :prefix-map amin--gnus-summary-prefix-map
1194 :prefix "v"
1195 ("r" . gnus-summary-reply)
1196 ("w" . gnus-summary-wide-reply)
1197 ("v" . gnus-summary-show-raw-article))
1198 :config
1199 (bind-keys
1200 :map gnus-summary-mode-map
1201 ("r" . gnus-summary-reply-with-original)
1202 ("R" . gnus-summary-wide-reply-with-original)
1203 ("M-L" . org-store-link))
1204 :hook (gnus-summary-mode . amin--no-mouse-autoselect-window))
1205
1206 (use-package gnus-msg
1207 :config
1208 (setq gnus-posting-styles
1209 '((".*"
1210 (address "amin@aminb.org")
1211 (body "\nBest,\namin\n")
1212 (eval (setq amin--message-cite-say-hi t)))
1213 ("gnu.*"
1214 (address "bandali@gnu.org"))
1215 ((header "subject" "ThankCRM")
1216 (to "webmasters-comment@gnu.org")
1217 (body "\nAdded to 2018supporters.html.\n\nMoving to campaigns.\n\n-amin\n")
1218 (eval (setq amin--message-cite-say-hi nil)))
1219 ("nnimap\\+uwaterloo:.*"
1220 (address "abandali@uwaterloo.ca")
1221 (gcc "\"nnimap+uwaterloo:Sent Items\"")))))
1222
1223 (use-package gnus-topic
1224 :hook (gnus-group-mode . gnus-topic-mode))
1225
1226 (use-package gnus-agent
1227 :config
1228 (setq gnus-agent-synchronize-flags 'ask)
1229 :hook (gnus-group-mode . gnus-agent-mode))
1230
1231 (use-package gnus-group
1232 :config
1233 (setq gnus-permanently-visible-groups "\\((INBOX\\|gnu$\\)"))
1234
1235 (use-package mm-decode
1236 :config
1237 (setq mm-discouraged-alternatives '("text/html" "text/richtext")))
1238
1239 (use-package sendmail
1240 :config
1241 (setq sendmail-program "/usr/bin/msmtp"
1242 ;; message-sendmail-extra-arguments '("-v" "-d")
1243 mail-specify-envelope-from t
1244 mail-envelope-from 'header))
1245
1246 (use-package message
1247 :config
1248 (defconst amin--message-cite-style-format "On %Y-%m-%d %l:%M %p, %N wrote:")
1249 (defconst message-cite-style-bandali
1250 '((message-cite-function 'message-cite-original)
1251 (message-citation-line-function 'message-insert-formatted-citation-line)
1252 (message-cite-reply-position 'traditional)
1253 (message-yank-prefix "> ")
1254 (message-yank-cited-prefix ">")
1255 (message-yank-empty-prefix ">")
1256 (message-citation-line-format
1257 (if amin--message-cite-say-hi
1258 (concat "Hi %F,\n\n" amin--message-cite-style-format)
1259 amin--message-cite-style-format)))
1260 "Citation style based on Mozilla Thunderbird's. Use with message-cite-style.")
1261 (setq message-cite-style 'message-cite-style-bandali
1262 message-kill-buffer-on-exit t
1263 message-send-mail-function 'message-send-mail-with-sendmail
1264 message-sendmail-envelope-from 'header
1265 message-dont-reply-to-names
1266 "\\(\\(.*@aminb\\.org\\)\\|\\(amin@bandali\\.me\\)\\|\\(\\(aminb?\\|mab\\|bandali\\)@gnu\\.org\\)\\|\\(\\(m\\|a\\(min\\.\\)?\\)bandali@uwaterloo\\.ca\\)\\)"
1267 message-user-fqdn "aminb.org")
1268 :hook (;; (message-setup . mml-secure-message-sign-pgpmime)
1269 (message-mode . flyspell-mode)
1270 (message-mode . (lambda ()
1271 ;; (setq fill-column 65
1272 ;; message-fill-column 65)
1273 (make-local-variable 'company-idle-delay)
1274 (setq company-idle-delay 0.2))))
1275 ;; :custom-face
1276 ;; (message-header-subject ((t (:foreground "#111" :weight semi-bold))))
1277 ;; (message-header-to ((t (:foreground "#111" :weight normal))))
1278 ;; (message-header-cc ((t (:foreground "#333" :weight normal))))
1279 )
1280
1281 (after! mml-sec
1282 (setq mml-secure-openpgp-encrypt-to-self t
1283 mml-secure-openpgp-sign-with-sender t))
1284
1285 (use-package footnote
1286 :after message
1287 :bind
1288 (:map message-mode-map
1289 :prefix-map amin--footnote-prefix-map
1290 :prefix "C-c f"
1291 ("a" . footnote-add-footnote)
1292 ("b" . footnote-back-to-message)
1293 ("c" . footnote-cycle-style)
1294 ("d" . footnote-delete-footnote)
1295 ("g" . footnote-goto-footnote)
1296 ("r" . footnote-renumber-footnotes)
1297 ("s" . footnote-set-style))
1298 :config
1299 (setq footnote-start-tag ""
1300 footnote-end-tag ""
1301 footnote-style 'unicode))
1302
1303 (use-package bbdb
1304 :load-path "lisp/bbdb"
1305 :init
1306 (load (expand-file-name "lisp/bbdb/bbdb-autoloads.el" user-emacs-directory))
1307 ;; (bbdb-mua-auto-update-init 'message)
1308 (setq bbdb-mua-auto-update-p 'query
1309 bbdb-complete-mail nil)
1310 (bbdb-initialize 'gnus 'message))
1311
1312 (use-package message-x
1313 :disabled t
1314 :custom
1315 (message-x-completion-alist
1316 (quote
1317 (("\\([rR]esent-\\|[rR]eply-\\)?[tT]o:\\|[bB]?[cC][cC]:" . gnus-harvest-find-address)
1318 ((if
1319 (boundp
1320 (quote message-newgroups-header-regexp))
1321 message-newgroups-header-regexp message-newsgroups-header-regexp)
1322 . message-expand-group)))))
1323
1324 (use-package gnus-harvest
1325 :disabled t
1326 :commands gnus-harvest-install
1327 :demand t
1328 :config
1329 (if (featurep 'message-x)
1330 (gnus-harvest-install 'message-x)
1331 (gnus-harvest-install)))
1332
1333 \f
1334 ;; * Blogging
1335
1336 (use-package ox-hugo
1337 :after ox)
1338
1339 (use-package ox-hugo-auto-export
1340 :load-path "lib/ox-hugo")
1341
1342 \f
1343 ;; * Post initialization
1344
1345 (message "Loading %s...done (%.3fs)" user-init-file
1346 (float-time (time-subtract (current-time)
1347 amin--before-user-init-time)))
1348
1349 ;;; init.el ends here