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