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