[emacs][wip] enable geiser and guix, and update packages
[~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** simple (for column numbers)
1254
1255#+begin_src emacs-lisp
1256(use-feature simple
1257 :config (column-number-mode))
1258#+end_src
1259
1260** =savehist=
1261
1262Save minibuffer history.
1263
1264#+begin_src emacs-lisp
1265(use-feature savehist
1266 :config (savehist-mode))
1267#+end_src
1268
1269** =saveplace=
1270
1271Automatically save place in each file.
1272
1273#+begin_src emacs-lisp
1274(use-feature saveplace
1275 :when (version< "25" emacs-version)
1276 :config (save-place-mode))
1277#+end_src
1278
1279** =prog-mode=
1280
1281#+begin_src emacs-lisp
1282(use-feature prog-mode
1283 :config (global-prettify-symbols-mode)
1284 (defun indicate-buffer-boundaries-left ()
1285 (setq indicate-buffer-boundaries 'left))
1286 (add-hook 'prog-mode-hook #'indicate-buffer-boundaries-left))
1287#+end_src
1288
1289** =text-mode=
1290
1291#+begin_src emacs-lisp
1292(use-feature text-mode
1293 :hook ((text-mode . indicate-buffer-boundaries-left)
1294 (text-mode . abbrev-mode)))
1295#+end_src
1296
1297** Company
1298
1299#+begin_src emacs-lisp
1300(use-package company
1301 :defer 1
1302 :bind
1303 (:map company-active-map
1304 ([tab] . company-complete-common-or-cycle)
1305 ([escape] . company-abort))
1306 :custom
1307 (company-minimum-prefix-length 1)
1308 (company-selection-wrap-around t)
1309 (company-dabbrev-char-regexp "\\sw\\|\\s_\\|[-_]")
1310 (company-dabbrev-downcase nil)
1311 (company-dabbrev-ignore-case nil)
1312 :config
1313 (global-company-mode t))
1314#+end_src
1315
1316** Flycheck
1317
1318#+begin_src emacs-lisp
1319(use-package flycheck
1320 :defer 3
1321 :hook (prog-mode . flycheck-mode)
1322 :bind
1323 (:map flycheck-mode-map
1324 ("M-P" . flycheck-previous-error)
1325 ("M-N" . flycheck-next-error))
1326 :config
1327 ;; Use the load-path from running Emacs when checking elisp files
1328 (setq flycheck-emacs-lisp-load-path 'inherit)
1329
1330 ;; Only flycheck when I actually save the buffer
1331 (setq flycheck-check-syntax-automatically '(mode-enabled save)))
1332
1333;; http://endlessparentheses.com/ispell-and-apostrophes.html
1334(use-package ispell
1335 :defer 3
1336 :config
1337 ;; ’ can be part of a word
1338 (setq ispell-local-dictionary-alist
1339 `((nil "[[:alpha:]]" "[^[:alpha:]]"
1340 "['\x2019]" nil ("-B") nil utf-8)))
1341 ;; don't send ’ to the subprocess
1342 (defun endless/replace-apostrophe (args)
1343 (cons (replace-regexp-in-string
1344 "’" "'" (car args))
1345 (cdr args)))
1346 (advice-add #'ispell-send-string :filter-args
1347 #'endless/replace-apostrophe)
1348
1349 ;; convert ' back to ’ from the subprocess
1350 (defun endless/replace-quote (args)
1351 (if (not (derived-mode-p 'org-mode))
1352 args
1353 (cons (replace-regexp-in-string
1354 "'" "’" (car args))
1355 (cdr args))))
1356 (advice-add #'ispell-parse-output :filter-args
1357 #'endless/replace-quote))
1358#+end_src
1359
1360* Programming modes
1361:PROPERTIES:
1362:CUSTOM_ID: programming-modes
1363:END:
1364
1365** Lisp
1366
1367#+begin_src emacs-lisp
1368(use-feature lisp-mode
1369 :config
1370 (add-hook 'emacs-lisp-mode-hook 'outline-minor-mode)
1371 (add-hook 'emacs-lisp-mode-hook 'reveal-mode)
1372 (defun indent-spaces-mode ()
1373 (setq indent-tabs-mode nil))
1374 (add-hook 'lisp-interaction-mode-hook #'indent-spaces-mode))
1375#+end_src
1376
1377** [[http://alloytools.org][Alloy]] (with [[https://github.com/dwwmmn/alloy-mode][alloy-mode]])
1378
1379#+begin_src emacs-lisp
1380(use-package alloy-mode
1381 :straight (:host github :repo "dwwmmn/alloy-mode")
1382 :config (setq alloy-basic-offset 2))
1383#+end_src
1384
1385** [[https://coq.inria.fr][Coq]] (with [[https://github.com/ProofGeneral/PG][Proof General]])
1386
1387#+begin_src emacs-lisp
1388(use-package proof-site
1389 :straight proof-general)
1390#+end_src
1391
1392** [[https://leanprover.github.io][Lean]] (with [[https://github.com/leanprover/lean-mode][lean-mode]])
1393
1394#+begin_src emacs-lisp
1395(eval-when-compile (defvar lean-mode-map))
1396(use-package lean-mode
1397 :defer 1
1398 :bind (:map lean-mode-map
1399 ("S-SPC" . company-complete))
1400 :config
1401 (require 'lean-input)
1402 (setq default-input-method "Lean"
1403 lean-input-tweak-all '(lean-input-compose
1404 (lean-input-prepend "/")
1405 (lean-input-nonempty))
1406 lean-input-user-translations '(("/" "/")))
1407 (lean-input-setup))
1408 #+end_src
1409
1410** Haskell
1411
1412*** [[https://github.com/haskell/haskell-mode][haskell-mode]]
1413
1414#+begin_src emacs-lisp
1415(use-package haskell-mode
1416 :config
1417 (setq haskell-indentation-layout-offset 4
1418 haskell-indentation-left-offset 4
1419 flycheck-checker 'haskell-hlint
1420 flycheck-disabled-checkers '(haskell-stack-ghc haskell-ghc)))
1421#+end_src
1422
1423*** [[https://github.com/jyp/dante][dante]]
1424
1425#+begin_src emacs-lisp
1426(use-package dante
1427 :after haskell-mode
1428 :commands dante-mode
1429 :hook (haskell-mode . dante-mode))
1430#+end_src
1431
1432*** [[https://github.com/mpickering/hlint-refactor-mode][hlint-refactor]]
1433
1434Emacs bindings for [[https://github.com/ndmitchell/hlint][hlint]]'s refactor option. This requires the refact
1435executable from [[https://github.com/mpickering/apply-refact][apply-refact]].
1436
1437#+begin_src emacs-lisp
1438(use-package hlint-refactor
1439 :after haskell-mode
1440 :bind (:map hlint-refactor-mode-map
1441 ("C-c l b" . hlint-refactor-refactor-buffer)
1442 ("C-c l r" . hlint-refactor-refactor-at-point))
1443 :hook (haskell-mode . hlint-refactor-mode))
1444#+end_src
1445
1446*** [[https://github.com/flycheck/flycheck-haskell][flycheck-haskell]]
1447
1448#+begin_src emacs-lisp
1449(use-package flycheck-haskell
1450 :after haskell-mode)
1451#+end_src
1452
1453*** [[https://github.com/ndmitchell/hlint/blob/20e116a043f2073c57b17b24ae6364b5e433ba7e/data/hs-lint.el][hs-lint.el]]
1454:PROPERTIES:
1455:header-args+: :tangle lisp/hs-lint.el :mkdirp yes
1456:END:
1457
1458Currently using =flycheck-haskell= with the =haskell-hlint= checker
1459instead.
1460
1461#+begin_src emacs-lisp :tangle no
1462;;; hs-lint.el --- minor mode for HLint code checking
1463
1464;; Copyright 2009 (C) Alex Ott
1465;;
1466;; Author: Alex Ott <alexott@gmail.com>
1467;; Keywords: haskell, lint, HLint
1468;; Requirements:
1469;; Status: distributed under terms of GPL2 or above
1470
1471;; Typical message from HLint looks like:
1472;;
1473;; /Users/ott/projects/lang-exp/haskell/test.hs:52:1: Eta reduce
1474;; Found:
1475;; count1 p l = length (filter p l)
1476;; Why not:
1477;; count1 p = length . filter p
1478
1479
1480(require 'compile)
1481
1482(defgroup hs-lint nil
1483 "Run HLint as inferior of Emacs, parse error messages."
1484 :group 'tools
1485 :group 'haskell)
1486
1487(defcustom hs-lint-command "hlint"
1488 "The default hs-lint command for \\[hlint]."
1489 :type 'string
1490 :group 'hs-lint)
1491
1492(defcustom hs-lint-save-files t
1493 "Save modified files when run HLint or no (ask user)"
1494 :type 'boolean
1495 :group 'hs-lint)
1496
1497(defcustom hs-lint-replace-with-suggestions nil
1498 "Replace user's code with suggested replacements"
1499 :type 'boolean
1500 :group 'hs-lint)
1501
1502(defcustom hs-lint-replace-without-ask nil
1503 "Replace user's code with suggested replacements automatically"
1504 :type 'boolean
1505 :group 'hs-lint)
1506
1507(defun hs-lint-process-setup ()
1508 "Setup compilation variables and buffer for `hlint'."
1509 (run-hooks 'hs-lint-setup-hook))
1510
1511;; regex for replace suggestions
1512;;
1513;; ^\(.*?\):\([0-9]+\):\([0-9]+\): .*
1514;; Found:
1515;; \s +\(.*\)
1516;; Why not:
1517;; \s +\(.*\)
1518
1519(defvar hs-lint-regex
1520 "^\\(.*?\\):\\([0-9]+\\):\\([0-9]+\\): .*[\n\C-m]Found:[\n\C-m]\\s +\\(.*\\)[\n\C-m]Why not:[\n\C-m]\\s +\\(.*\\)[\n\C-m]"
1521 "Regex for HLint messages")
1522
1523(defun make-short-string (str maxlen)
1524 (if (< (length str) maxlen)
1525 str
1526 (concat (substring str 0 (- maxlen 3)) "...")))
1527
1528(defun hs-lint-replace-suggestions ()
1529 "Perform actual replacement of suggestions"
1530 (goto-char (point-min))
1531 (while (re-search-forward hs-lint-regex nil t)
1532 (let* ((fname (match-string 1))
1533 (fline (string-to-number (match-string 2)))
1534 (old-code (match-string 4))
1535 (new-code (match-string 5))
1536 (msg (concat "Replace '" (make-short-string old-code 30)
1537 "' with '" (make-short-string new-code 30) "'"))
1538 (bline 0)
1539 (eline 0)
1540 (spos 0)
1541 (new-old-code ""))
1542 (save-excursion
1543 (switch-to-buffer (get-file-buffer fname))
1544 (goto-char (point-min))
1545 (forward-line (1- fline))
1546 (beginning-of-line)
1547 (setf bline (point))
1548 (when (or hs-lint-replace-without-ask
1549 (yes-or-no-p msg))
1550 (end-of-line)
1551 (setf eline (point))
1552 (beginning-of-line)
1553 (setf old-code (regexp-quote old-code))
1554 (while (string-match "\\\\ " old-code spos)
1555 (setf new-old-code (concat new-old-code
1556 (substring old-code spos (match-beginning 0))
1557 "\\ *"))
1558 (setf spos (match-end 0)))
1559 (setf new-old-code (concat new-old-code (substring old-code spos)))
1560 (remove-text-properties bline eline '(composition nil))
1561 (when (re-search-forward new-old-code eline t)
1562 (replace-match new-code nil t)))))))
1563
1564(defun hs-lint-finish-hook (buf msg)
1565 "Function, that is executed at the end of HLint execution"
1566 (if hs-lint-replace-with-suggestions
1567 (hs-lint-replace-suggestions)
1568 (next-error 1 t)))
1569
1570(define-compilation-mode hs-lint-mode "HLint"
1571 "Mode for check Haskell source code."
1572 (set (make-local-variable 'compilation-process-setup-function)
1573 'hs-lint-process-setup)
1574 (set (make-local-variable 'compilation-disable-input) t)
1575 (set (make-local-variable 'compilation-scroll-output) nil)
1576 (set (make-local-variable 'compilation-finish-functions)
1577 (list 'hs-lint-finish-hook))
1578 )
1579
1580(defun hs-lint ()
1581 "Run HLint for current buffer with haskell source"
1582 (interactive)
1583 (save-some-buffers hs-lint-save-files)
1584 (compilation-start (concat hs-lint-command " \"" buffer-file-name "\"")
1585 'hs-lint-mode))
1586
1587(provide 'hs-lint)
1588;;; hs-lint.el ends here
1589#+end_src
1590
1591#+begin_src emacs-lisp :tangle no
1592(use-package hs-lint
1593 :load-path "lisp/"
1594 :bind (:map haskell-mode-map
1595 ("C-c l l" . hs-lint)))
1596#+end_src
1597
1598** Web
1599
1600*** SGML and HTML
1601
1602#+begin_src emacs-lisp
1603(use-package sgml-mode
1604 :config
1605 (setq sgml-basic-offset 2))
1606#+end_src
1607
1608*** CSS and SCSS
1609
1610#+begin_src emacs-lisp
1611(use-package css-mode
1612 :config
1613 (setq css-indent-offset 2))
1614#+end_src
1615
1616*** Web mode
1617
1618#+begin_src emacs-lisp
1619(use-package web-mode
1620 :mode "\\.html\\'"
1621 :config
1622 (a/setq-every 2
1623 web-mode-code-indent-offset
1624 web-mode-css-indent-offset
1625 web-mode-markup-indent-offset))
1626#+end_src
1627
1628*** Emmet mode
1629
1630#+begin_src emacs-lisp
1631(use-package emmet-mode
1632 :after (:any web-mode css-mode sgml-mode)
1633 :bind* (("C-)" . emmet-next-edit-point)
1634 ("C-(" . emmet-prev-edit-point))
1635 :config
1636 (unbind-key "C-j" emmet-mode-keymap)
1637 (setq emmet-move-cursor-between-quotes t)
1638 :hook (web-mode css-mode html-mode sgml-mode))
1639#+end_src
1640
1641** COMMENT Java
1642
1643*** meghanada
1644
1645#+begin_src emacs-lisp
1646(use-package meghanada
1647 :bind
1648 (:map meghanada-mode-map
1649 (("C-M-o" . meghanada-optimize-import)
1650 ("C-M-t" . meghanada-import-all)))
1651 :hook (java-mode . meghanada-mode))
1652#+end_src
1653
1654*** lsp-java
1655
1656#+begin_comment
1657dependencies:
1658
1659ace-window
1660avy
1661bui
1662company-lsp
1663dap-mode
1664lsp-java
1665lsp-mode
1666lsp-ui
1667pfuture
1668tree-mode
1669treemacs
1670#+end_comment
1671
1672#+begin_src emacs-lisp
1673(use-package treemacs
1674 :config (setq treemacs-never-persist t))
1675
1676(use-package yasnippet
1677 :config
1678 ;; (yas-global-mode)
1679 )
1680
1681(use-package lsp-mode
1682 :init (setq lsp-eldoc-render-all nil
1683 lsp-highlight-symbol-at-point nil)
1684 )
1685
1686(use-package hydra)
1687
1688(use-package company-lsp
1689 :after company
1690 :config
1691 (setq company-lsp-cache-candidates t
1692 company-lsp-async t))
1693
1694(use-package lsp-ui
1695 :config
1696 (setq lsp-ui-sideline-update-mode 'point))
1697
1698(use-package lsp-java
1699 :config
1700 (add-hook 'java-mode-hook
1701 (lambda ()
1702 (setq-local company-backends (list 'company-lsp))))
1703
1704 (add-hook 'java-mode-hook 'lsp-java-enable)
1705 (add-hook 'java-mode-hook 'flycheck-mode)
1706 (add-hook 'java-mode-hook 'company-mode)
1707 (add-hook 'java-mode-hook 'lsp-ui-mode))
1708
1709(use-package dap-mode
1710 :after lsp-mode
1711 :config
1712 (dap-mode t)
1713 (dap-ui-mode t))
1714
1715(use-package dap-java
1716 :after (lsp-java))
1717
1718(use-package lsp-java-treemacs
1719 :after (treemacs))
1720#+end_src
1721
1722** geiser
1723
1724#+begin_src emacs-lisp
1725(use-package geiser)
1726
1727(use-feature geiser-guile
1728 :config
1729 (setq geiser-guile-load-path "~/src/git/guix"))
1730#+end_src
1731
1732** guix
1733
1734#+begin_src emacs-lisp
1735(use-package guix)
1736#+end_src
1737
1738* Emacs enhancements
1739:PROPERTIES:
1740:CUSTOM_ID: emacs-enhancements
1741:END:
1742
1743** man
1744
1745#+begin_src emacs-lisp
1746(use-feature man
1747 :config (setq Man-width 80))
1748#+end_src
1749
1750** [[https://github.com/justbur/emacs-which-key][which-key]]
1751
1752#+begin_quote
1753Emacs package that displays available keybindings in popup
1754#+end_quote
1755
1756#+begin_src emacs-lisp
1757(use-package which-key
1758 :defer 1
1759 :config
1760 (which-key-add-key-based-replacements
1761 ;; prefixes for global prefixes and minor modes
1762 "C-c @" "outline"
1763 "C-c !" "flycheck"
1764 "C-c 8" "typo"
1765 "C-c 8 -" "typo/dashes"
1766 "C-c 8 <" "typo/left-brackets"
1767 "C-c 8 >" "typo/right-brackets"
1768 "C-x 8" "unicode"
1769 "C-x a" "abbrev/expand"
1770 "C-x r" "rectangle/register/bookmark"
1771 "C-x v" "version control"
1772 ;; prefixes for my personal bindings
1773 "C-c a" "applications"
1774 "C-c a s" "shells"
1775 "C-c P" "package-management"
1776 "C-c P e" "package-management/epkg"
1777 "C-c P s" "package-management/straight.el"
1778 "C-c c" "compile-and-comments"
1779 "C-c e" "eval"
1780 "C-c f" "files"
1781 "C-c F" "frames"
1782 "C-S-h" "help(ful)"
1783 "C-c m" "multiple-cursors"
1784 "C-c p" "projectile"
1785 "C-c p s" "projectile/search"
1786 "C-c p x" "projectile/execute"
1787 "C-c p 4" "projectile/other-window"
1788 "C-c q" "boxquote"
1789 "s-g" "magit"
1790 "s-o" "outline"
1791 "s-t" "themes")
1792
1793 ;; prefixes for major modes
1794 (which-key-add-major-mode-key-based-replacements 'message-mode
1795 "C-c f" "footnote")
1796 (which-key-add-major-mode-key-based-replacements 'org-mode
1797 "C-c C-v" "org-babel")
1798 (which-key-add-major-mode-key-based-replacements 'web-mode
1799 "C-c C-a" "web/attributes"
1800 "C-c C-b" "web/blocks"
1801 "C-c C-d" "web/dom"
1802 "C-c C-e" "web/element"
1803 "C-c C-t" "web/tags")
1804
1805 (which-key-mode))
1806#+end_src
1807
1808** theme
1809
1810#+begin_src emacs-lisp
1811(add-to-list 'custom-theme-load-path "~/.emacs.d/lisp")
1812(load-theme 'tangomod t)
1813#+end_src
1814
1815** smart-mode-line
1816
1817#+begin_src emacs-lisp
1818(use-package smart-mode-line
1819 :commands (sml/apply-theme)
1820 :demand
1821 :config
1822 (sml/setup))
1823#+end_src
1824
1825** doom-themes
1826
1827#+begin_src emacs-lisp
1828(use-package doom-themes)
1829#+end_src
1830
1831** theme helper functions
1832
1833#+begin_src emacs-lisp
1834(defun a/lights-on ()
1835 "Enable my favourite light theme."
1836 (interactive)
1837 (mapc #'disable-theme custom-enabled-themes)
1838 (load-theme 'tangomod t)
1839 (sml/apply-theme 'automatic))
1840
1841(defun a/lights-off ()
1842 "Go dark."
1843 (interactive)
1844 (mapc #'disable-theme custom-enabled-themes)
1845 (load-theme 'doom-tomorrow-night t)
1846 (sml/apply-theme 'automatic))
1847
1848(bind-keys
1849 ("s-t d" . a/lights-off)
1850 ("s-t l" . a/lights-on))
1851#+end_src
1852
1853** [[https://github.com/bbatsov/crux][crux]]
1854
1855#+begin_src emacs-lisp
1856(use-package crux ; results in Waiting for git... [2 times]
1857 :defer 1
1858 :bind (("C-c b k" . crux-kill-other-buffers)
1859 ("C-c d" . crux-duplicate-current-line-or-region)
1860 ("C-c D" . crux-duplicate-and-comment-current-line-or-region)
1861 ("C-c f c" . crux-copy-file-preserve-attributes)
1862 ("C-c f d" . crux-delete-file-and-buffer)
1863 ("C-c f r" . crux-rename-file-and-buffer)
1864 ("C-c j" . crux-top-join-line)
1865 ("C-S-j" . crux-top-join-line)))
1866#+end_src
1867
1868** [[https://github.com/alezost/mwim.el][mwim]]
1869
1870#+begin_src emacs-lisp
1871(use-package mwim
1872 :bind (("C-a" . mwim-beginning-of-code-or-line)
1873 ("C-e" . mwim-end-of-code-or-line)
1874 ("<home>" . mwim-beginning-of-line-or-code)
1875 ("<end>" . mwim-end-of-line-or-code)))
1876#+end_src
1877
1878** projectile
1879
1880#+begin_src emacs-lisp
1881(use-package projectile
1882 :bind-keymap ("C-c p" . projectile-command-map)
1883 :config
1884 (projectile-mode)
1885
1886 (defun my-projectile-invalidate-cache (&rest _args)
1887 ;; ignore the args to `magit-checkout'
1888 (projectile-invalidate-cache nil))
1889
1890 (eval-after-load 'magit-branch
1891 '(progn
1892 (advice-add 'magit-checkout
1893 :after #'my-projectile-invalidate-cache)
1894 (advice-add 'magit-branch-and-checkout
1895 :after #'my-projectile-invalidate-cache)))
1896 :custom (projectile-completion-system 'ivy))
1897#+end_src
1898
1899** [[https://github.com/Wilfred/helpful][helpful]]
1900
1901#+begin_src emacs-lisp
1902(use-package helpful
1903 :defer 1
1904 :bind
1905 (("C-S-h c" . helpful-command)
1906 ("C-S-h f" . helpful-callable) ; helpful-function
1907 ("C-S-h v" . helpful-variable)
1908 ("C-S-h k" . helpful-key)
1909 ("C-S-h p" . helpful-at-point)))
1910#+end_src
1911
1912** [[https://github.com/EricCrosson/unkillable-scratch][unkillable-scratch]]
1913
1914Make =*scratch*= and =*Messages*= unkillable.
1915
1916#+begin_src emacs-lisp
1917(use-package unkillable-scratch
1918 :defer 3
1919 :config
1920 (unkillable-scratch 1)
1921 :custom
1922 (unkillable-scratch-behavior 'do-nothing)
1923 (unkillable-buffers '("^\\*scratch\\*$" "^\\*Messages\\*$")))
1924#+end_src
1925
1926** [[https://github.com/davep/boxquote.el][boxquote.el]]
1927
1928#+begin_example
1929,----
1930| make pretty boxed quotes like this
1931`----
1932#+end_example
1933
1934#+begin_src emacs-lisp
1935(use-package boxquote
1936 :defer 3
1937 :bind
1938 (:prefix-map a/boxquote-prefix-map
1939 :prefix "C-c q"
1940 ("b" . boxquote-buffer)
1941 ("B" . boxquote-insert-buffer)
1942 ("d" . boxquote-defun)
1943 ("F" . boxquote-insert-file)
1944 ("hf" . boxquote-describe-function)
1945 ("hk" . boxquote-describe-key)
1946 ("hv" . boxquote-describe-variable)
1947 ("hw" . boxquote-where-is)
1948 ("k" . boxquote-kill)
1949 ("p" . boxquote-paragraph)
1950 ("q" . boxquote-boxquote)
1951 ("r" . boxquote-region)
1952 ("s" . boxquote-shell-command)
1953 ("t" . boxquote-text)
1954 ("T" . boxquote-title)
1955 ("u" . boxquote-unbox)
1956 ("U" . boxquote-unbox-region)
1957 ("y" . boxquote-yank)
1958 ("M-q" . boxquote-fill-paragraph)
1959 ("M-w" . boxquote-kill-ring-save)))
1960#+end_src
1961
1962Also see [[https://www.emacswiki.org/emacs/rebox2][rebox2]].
1963
1964** orgalist
1965
1966#+begin_src emacs-lisp
1967(use-package orgalist
1968 :after message
1969 :hook (message-mode . orgalist-mode))
1970#+end_src
1971
1972** typo.el
1973
1974#+begin_src emacs-lisp
1975(use-package typo
1976 :defer 2
1977 :config
1978 (typo-global-mode 1)
1979 :hook (text-mode . typo-mode))
1980#+end_src
1981
1982** hl-todo
1983
1984#+begin_src emacs-lisp
1985(use-package hl-todo
1986 :defer 4
1987 :config
1988 (global-hl-todo-mode))
1989#+end_src
1990
1991** shrink-path
1992
1993#+begin_src emacs-lisp
1994(use-package shrink-path
1995 :defer 2
1996 :config
1997 (setq eshell-prompt-regexp "\\(.*\n\\)*λ "
1998 eshell-prompt-function #'+eshell/prompt)
1999
2000 (defun +eshell/prompt ()
2001 (let ((base/dir (shrink-path-prompt default-directory)))
2002 (concat (propertize (car base/dir)
2003 'face 'font-lock-comment-face)
2004 (propertize (cdr base/dir)
2005 'face 'font-lock-constant-face)
2006 (propertize (+eshell--current-git-branch)
2007 'face 'font-lock-function-name-face)
2008 "\n"
2009 (propertize "λ" 'face 'eshell-prompt-face)
2010 ;; needed for the input text to not have prompt face
2011 (propertize " " 'face 'default))))
2012
2013 (defun +eshell--current-git-branch ()
2014 (let ((branch (car (loop for match in (split-string (shell-command-to-string "git branch") "\n")
2015 when (string-match "^\*" match)
2016 collect match))))
2017 (if (not (eq branch nil))
2018 (concat " " (substring branch 2))
2019 ""))))
2020#+end_src
2021
2022** [[https://github.com/peterwvj/eshell-up][eshell-up]]
2023
2024#+begin_src emacs-lisp
2025(use-package eshell-up
2026 :commands eshell-up
2027 :after eshell)
2028#+end_src
2029
2030** multi-term
2031
2032#+begin_src emacs-lisp
2033(use-package multi-term
2034 :defer 1
2035 :bind (("C-c a s m" . multi-term-dedicated-toggle)
2036 :map term-mode-map
2037 ("C-c C-j" . term-char-mode)
2038 :map term-raw-map
2039 ("C-c C-j" . term-line-mode))
2040 :config
2041 (setq multi-term-program "/bin/screen"
2042 ;; TODO: add separate bindings for connecting to existing
2043 ;; session vs. always creating a new one
2044 multi-term-dedicated-select-after-open-p t
2045 multi-term-dedicated-window-height 20
2046 multi-term-dedicated-max-window-height 30
2047 term-bind-key-alist
2048 '(("C-c C-c" . term-interrupt-subjob)
2049 ("C-c C-e" . term-send-esc)
2050 ("C-k" . kill-line)
2051 ("C-y" . term-paste)
2052 ("M-f" . term-send-forward-word)
2053 ("M-b" . term-send-backward-word)
2054 ("M-p" . term-send-up)
2055 ("M-n" . term-send-down)
2056 ("<C-backspace>" . term-send-backward-kill-word)
2057 ("<M-DEL>" . term-send-backward-kill-word)
2058 ("M-d" . term-send-delete-word)
2059 ("M-," . term-send-raw)
2060 ("M-." . comint-dynamic-complete))
2061 term-unbind-key-alist
2062 '("C-z" "C-x" "C-c" "C-h" "C-y" "<ESC>")))
2063#+end_src
2064
2065** page-break-lines
2066
2067#+begin_src emacs-lisp
2068(use-package page-break-lines
2069 :config
2070 (global-page-break-lines-mode))
2071#+end_src
2072
2073** expand-region
2074
2075#+begin_src emacs-lisp
2076(use-package expand-region
2077 :bind ("C-=" . er/expand-region))
2078#+end_src
2079
2080** multiple-cursors
2081
2082#+begin_src emacs-lisp
2083(use-package multiple-cursors
2084 :bind
2085 (("C-S-<mouse-1>" . mc/add-cursor-on-click)
2086 (:prefix-map a/mc-prefix-map
2087 :prefix "C-c m"
2088 ("c" . mc/edit-lines)
2089 ("n" . mc/mark-next-like-this)
2090 ("p" . mc/mark-previous-like-this)
2091 ("a" . mc/mark-all-like-this))))
2092#+end_src
2093
2094* Email
2095:PROPERTIES:
2096:CUSTOM_ID: email
2097:END:
2098
2099#+begin_src emacs-lisp
2100(defvar a/maildir (expand-file-name "~/mail/"))
2101(with-eval-after-load 'recentf
2102 (add-to-list 'recentf-exclude a/maildir))
2103#+end_src
2104
2105** Gnus
2106
2107#+begin_src emacs-lisp
2108(setq
2109 a/gnus-init-file (no-littering-expand-etc-file-name "gnus")
2110 mail-user-agent 'gnus-user-agent
2111 read-mail-command 'gnus)
2112
2113(use-feature gnus
2114 :bind (("s-m" . gnus)
2115 ("s-M" . gnus-unplugged))
2116 :init
2117 (setq
2118 gnus-select-method '(nnnil "")
2119 gnus-secondary-select-methods
2120 '((nnimap "amin"
2121 (nnimap-stream plain)
2122 (nnimap-address "127.0.0.1")
2123 (nnimap-server-port 143)
2124 (nnimap-authenticator plain)
2125 (nnimap-user "amin@aminb.org"))
2126 (nnimap "uwaterloo"
2127 (nnimap-stream plain)
2128 (nnimap-address "127.0.0.1")
2129 (nnimap-server-port 143)
2130 (nnimap-authenticator plain)
2131 (nnimap-user "abandali@uwaterloo.ca")))
2132 gnus-message-archive-group "nnimap+amin:Sent"
2133 gnus-parameters
2134 '(("gnu.*"
2135 (gcc-self . t)))
2136 gnus-large-newsgroup 50
2137 gnus-home-directory (no-littering-expand-var-file-name "gnus/")
2138 gnus-directory (concat gnus-home-directory "news/")
2139 message-directory (concat gnus-home-directory "mail/")
2140 nndraft-directory (concat gnus-home-directory "drafts/")
2141 gnus-save-newsrc-file nil
2142 gnus-read-newsrc-file nil
2143 gnus-interactive-exit nil
2144 gnus-gcc-mark-as-read t))
2145
2146(use-feature gnus-art
2147 :config
2148 (setq
2149 gnus-visible-headers
2150 (concat gnus-visible-headers "\\|^List-Id:\\|^X-RT-Originator:\\|^User-Agent:")
2151 gnus-sorted-header-list
2152 '("^From:" "^Subject:" "^Summary:" "^Keywords:"
2153 "^Followup-To:" "^To:" "^Cc:" "X-RT-Originator"
2154 "^Newsgroups:" "List-Id:" "^Organization:"
2155 "^User-Agent:" "^Date:")
2156 ;; local-lapsed article dates
2157 ;; from https://www.emacswiki.org/emacs/GnusFormatting#toc11
2158 gnus-article-date-headers '(user-defined)
2159 gnus-article-time-format
2160 (lambda (time)
2161 (let* ((date (format-time-string "%a, %d %b %Y %T %z" time))
2162 (local (article-make-date-line date 'local))
2163 (combined-lapsed (article-make-date-line date
2164 'combined-lapsed))
2165 (lapsed (progn
2166 (string-match " (.+" combined-lapsed)
2167 (match-string 0 combined-lapsed))))
2168 (concat local lapsed))))
2169 (bind-keys
2170 :map gnus-article-mode-map
2171 ("r" . gnus-article-reply-with-original)
2172 ("R" . gnus-article-wide-reply-with-original)
2173 ("M-L" . org-store-link)))
2174
2175(use-feature gnus-sum
2176 :bind (:map gnus-summary-mode-map
2177 :prefix-map a/gnus-summary-prefix-map
2178 :prefix "v"
2179 ("r" . gnus-summary-reply)
2180 ("w" . gnus-summary-wide-reply)
2181 ("v" . gnus-summary-show-raw-article))
2182 :config
2183 (bind-keys
2184 :map gnus-summary-mode-map
2185 ("r" . gnus-summary-reply-with-original)
2186 ("R" . gnus-summary-wide-reply-with-original)
2187 ("M-L" . org-store-link))
2188 :hook (gnus-summary-mode . a/no-mouse-autoselect-window))
2189
2190(use-feature gnus-msg
2191 :config
2192 (setq gnus-posting-styles
2193 '((".*"
2194 (address "amin@aminb.org")
2195 (body "\nBest,\namin\n")
2196 (eval (setq a/message-cite-say-hi t)))
2197 ("gnu.*"
2198 (address "bandali@gnu.org"))
2199 ((header "subject" "ThankCRM")
2200 (to "webmasters-comment@gnu.org")
2201 (body "\nAdded to 2018supporters.html.\n\nMoving to campaigns.\n\n-amin\n")
2202 (eval (setq a/message-cite-say-hi nil)))
2203 ("nnimap\\+uwaterloo:.*"
2204 (address "abandali@uwaterloo.ca")
2205 (gcc "\"nnimap+uwaterloo:Sent Items\"")))))
2206
2207(use-feature gnus-topic
2208 :hook (gnus-group-mode . gnus-topic-mode))
2209
2210(use-feature gnus-agent
2211 :config
2212 (setq gnus-agent-synchronize-flags 'ask)
2213 :hook (gnus-group-mode . gnus-agent-mode))
2214
2215(use-feature gnus-group
2216 :config
2217 (setq gnus-permanently-visible-groups "\\((INBOX\\|gnu$\\)"))
2218
2219(use-feature mm-decode
2220 :config
2221 (setq mm-discouraged-alternatives '("text/html" "text/richtext")))
2222#+end_src
2223
2224** sendmail
2225
2226#+begin_src emacs-lisp
2227(use-feature sendmail
2228 :config
2229 (setq sendmail-program "/usr/bin/msmtp"
2230 ;; message-sendmail-extra-arguments '("-v" "-d")
2231 mail-specify-envelope-from t
2232 mail-envelope-from 'header))
2233#+end_src
2234
2235** message
2236
2237#+begin_src emacs-lisp
2238(use-feature message
2239 :config
2240 (defconst a/message-cite-style-format "On %Y-%m-%d %l:%M %p, %N wrote:")
2241 (defconst message-cite-style-bandali
2242 '((message-cite-function 'message-cite-original)
2243 (message-citation-line-function 'message-insert-formatted-citation-line)
2244 (message-cite-reply-position 'traditional)
2245 (message-yank-prefix "> ")
2246 (message-yank-cited-prefix ">")
2247 (message-yank-empty-prefix ">")
2248 (message-citation-line-format
2249 (if a/message-cite-say-hi
2250 (concat "Hi %F,\n\n" a/message-cite-style-format)
2251 a/message-cite-style-format)))
2252 "Citation style based on Mozilla Thunderbird's. Use with message-cite-style.")
2253 (setq message-cite-style 'message-cite-style-bandali
2254 message-kill-buffer-on-exit t
2255 message-send-mail-function 'message-send-mail-with-sendmail
2256 message-sendmail-envelope-from 'header
2257 message-dont-reply-to-names
2258 "\\(\\(.*@aminb\\.org\\)\\|\\(amin@bandali\\.me\\)\\|\\(\\(aminb?\\|mab\\|bandali\\)@gnu\\.org\\)\\|\\(\\(m\\|a\\(min\\.\\)?\\)bandali@uwaterloo\\.ca\\)\\)"
2259 message-user-fqdn "aminb.org")
2260 :hook (;; (message-setup . mml-secure-message-sign-pgpmime)
2261 (message-mode . flyspell-mode)
2262 (message-mode . (lambda ()
2263 ;; (setq fill-column 65
2264 ;; message-fill-column 65)
2265 (make-local-variable 'company-idle-delay)
2266 (setq company-idle-delay 0.2))))
2267 ;; :custom-face
2268 ;; (message-header-subject ((t (:foreground "#111" :weight semi-bold))))
2269 ;; (message-header-to ((t (:foreground "#111" :weight normal))))
2270 ;; (message-header-cc ((t (:foreground "#333" :weight normal))))
2271 )
2272
2273(with-eval-after-load 'mml-sec
2274 (setq mml-secure-openpgp-encrypt-to-self t
2275 mml-secure-openpgp-sign-with-sender t))
2276#+end_src
2277
2278** footnote
2279
2280Convenient footnotes in =message-mode=.
2281
2282#+begin_src emacs-lisp
2283(use-feature footnote
2284 :after message
2285 :bind
2286 (:map message-mode-map
2287 :prefix-map a/footnote-prefix-map
2288 :prefix "C-c f"
2289 ("a" . footnote-add-footnote)
2290 ("b" . footnote-back-to-message)
2291 ("c" . footnote-cycle-style)
2292 ("d" . footnote-delete-footnote)
2293 ("g" . footnote-goto-footnote)
2294 ("r" . footnote-renumber-footnotes)
2295 ("s" . footnote-set-style))
2296 :config
2297 (setq footnote-start-tag ""
2298 footnote-end-tag ""
2299 footnote-style 'unicode))
2300#+end_src
2301
2302** ebdb
2303
2304#+begin_src emacs-lisp
2305(use-package ebdb
2306 :straight (:host github :repo "girzel/ebdb")
2307 :after gnus
2308 :bind (:map gnus-group-mode-map ("e" . ebdb))
2309 :config
2310 (setq ebdb-sources (no-littering-expand-var-file-name "ebdb"))
2311 (with-eval-after-load 'swiper
2312 (add-to-list 'swiper-font-lock-exclude 'ebdb-mode t)))
2313
2314(use-feature ebdb-com
2315 :after ebdb)
2316
2317;; (use-package ebdb-complete
2318;; :after ebdb
2319;; :config
2320;; (ebdb-complete-enable))
2321
2322(use-package company-ebdb
2323 :after (:all company message)
2324 :config
2325 (defun company-ebdb--post-complete (_) nil)
2326 :hook
2327 (message-mode . (lambda ()
2328 (add-to-list (make-local-variable 'company-backends)
2329 'company-ebdb))))
2330
2331(use-feature ebdb-gnus
2332 :after ebdb
2333 :demand
2334 :custom
2335 (ebdb-gnus-window-configuration
2336 '(article
2337 (vertical 1.0
2338 (summary 0.25 point)
2339 (horizontal 1.0
2340 (article 1.0)
2341 (ebdb-gnus 0.3))))))
2342
2343(use-feature ebdb-mua
2344 :after ebdb
2345 :demand
2346 ;; :custom (ebdb-mua-pop-up nil)
2347 )
2348
2349;; (use-package ebdb-message
2350;; :after ebdb)
2351
2352
2353;; (use-package ebdb-vcard
2354;; :after ebdb)
2355#+end_src
2356
2357** COMMENT message-x
2358
2359#+begin_src emacs-lisp
2360(use-package message-x
2361 :custom
2362 (message-x-completion-alist
2363 (quote
2364 (("\\([rR]esent-\\|[rR]eply-\\)?[tT]o:\\|[bB]?[cC][cC]:" . gnus-harvest-find-address)
2365 ((if
2366 (boundp
2367 (quote message-newgroups-header-regexp))
2368 message-newgroups-header-regexp message-newsgroups-header-regexp)
2369 . message-expand-group)))))
2370#+end_src
2371
2372** COMMENT gnus-harvest
2373
2374#+begin_src emacs-lisp
2375(use-package gnus-harvest
2376 :commands gnus-harvest-install
2377 :demand t
2378 :config
2379 (if (featurep 'message-x)
2380 (gnus-harvest-install 'message-x)
2381 (gnus-harvest-install)))
2382#+end_src
2383
2384* Blogging
2385:PROPERTIES:
2386:CUSTOM_ID: blogging
2387:END:
2388
2389** [[https://ox-hugo.scripter.co][ox-hugo]]
2390
2391#+begin_src emacs-lisp
2392(use-package ox-hugo
2393 :after ox)
2394
2395(use-feature ox-hugo-auto-export
2396 :after ox-hugo)
2397#+end_src
2398
2399* Post initialization
2400:PROPERTIES:
2401:CUSTOM_ID: post-initialization
2402:END:
2403
2404Display how long it took to load the init file.
2405
2406#+begin_src emacs-lisp
2407(message "Loading %s...done (%.3fs)" user-init-file
2408 (float-time (time-subtract (current-time)
2409 a/before-user-init-time)))
2410#+end_src
2411
2412* Footer
2413:PROPERTIES:
2414:CUSTOM_ID: footer
2415:END:
2416
2417#+begin_src emacs-lisp :comments none
2418;;; init.el ends here
2419#+end_src
2420
2421* COMMENT Local Variables :ARCHIVE:
2422# Local Variables:
2423# eval: (add-hook 'after-save-hook #'a/async-babel-tangle 'append 'local)
2424# eval: (when (featurep 'typo (typo-mode -1)))
2425# End: