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