[emacs][wip] straight.el: don’t check for package changes on startup
[~bandali/configs] / init.org
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
11 This org file is my literate configuration for GNU Emacs, and is
12 tangled 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
14 configurations of many different people. Some of the configurations
15 that 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
28 I'd like to have a fully reproducible Emacs setup (part of the reason
29 why I store my configuration in this repository) but unfortunately out
30 of the box, that's not achievable with =package.el=, not currently
31 anyway. So, I've opted to use =straight.el=. I also used Borg for a
32 few months, but decided to try =straight.el= which allows direct use
33 of 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
62 Enable =view-mode=, which both makes the file read-only (as a reminder
63 that =init.el= is an auto-generated file, not supposed to be edited),
64 and provides some convenient key bindings for browsing through the
65 file.
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
104 I'd like to do a couple of measurements of Emacs' startup time. First,
105 let'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
116 Also, temporarily increase ~gc-cons-threshhold~ and
117 ~gc-cons-percentage~ during startup to reduce garbage collection
118 frequency. Clearing the ~file-name-handler-alist~ seems to help reduce
119 startup 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
132 Of course, we'd like to set them back to their defaults once we're
133 done 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
144 Increase the number of lines kept in message logs (the =*Messages*=
145 buffer).
146
147 #+begin_src emacs-lisp
148 (setq message-log-max 20000)
149 #+end_src
150
151 Optionally, we could suppress some byte compiler warnings like below,
152 but 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
171 I can do all my package management things with =straight.el=, and
172 don'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
179 But since Emacs 27 isn't out yet (Emacs 26 is just around the corner
180 right now), and even when released it'll be long before most distros
181 ship in their repos, I'll still put the old workaround with the
182 commented 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
189 Update: the above is not necessary, since =straight.el= automatically
190 does that (and more). See =straight-package-neutering-mode=.
191
192 *** =straight.el=
193
194 #+begin_quote
195 Next-generation, purely functional package manager for the Emacs
196 hacker.
197 #+end_quote
198
199 =straight.el= allows me to have a fully reproducible Emacs setup.
200
201 #+begin_src emacs-lisp
202 ;; Main engine start...
203
204 (setq straight-repository-branch "develop"
205 straight-check-for-modifications '(check-on-save find-when-checking))
206
207 (defun a/bootstrap-straight ()
208 (defvar bootstrap-version)
209 (let ((bootstrap-file
210 (expand-file-name "straight/repos/straight.el/bootstrap.el" user-emacs-directory))
211 (bootstrap-version 5))
212 (unless (file-exists-p bootstrap-file)
213 (with-current-buffer
214 (url-retrieve-synchronously
215 "https://raw.githubusercontent.com/raxod502/straight.el/develop/install.el"
216 'silent 'inhibit-cookies)
217 (goto-char (point-max))
218 (eval-print-last-sexp)))
219 (load bootstrap-file nil 'nomessage)))
220
221 ;; Solid rocket booster ignition...
222
223 (defun a/build-init ()
224 (a/bootstrap-straight)
225 (byte-compile-file "init.el"))
226
227 (a/bootstrap-straight)
228
229 ;; We have lift off!
230
231 (setq straight-use-package-by-default t)
232 #+end_src
233
234 Since we enable =straight.el='s =straight-use-package-by-default=
235 integration, we will define a =use-feature= for plain ole
236 =use-package= without any of the =straight.el= stuff.
237
238 #+begin_src emacs-lisp
239 (defmacro use-feature (name &rest args)
240 "Like `use-package', but with `straight-use-package-by-default' disabled."
241 (declare (indent defun))
242 `(use-package ,name
243 :straight nil
244 ,@args))
245 #+end_src
246
247 *** =use-package=
248
249 #+begin_quote
250 A use-package declaration for simplifying your .emacs
251 #+end_quote
252
253 [[https://github.com/jwiegley/use-package][use-package]] is an awesome utility for managing and configuring
254 packages (in our case especially the latter) in a neatly organized way
255 and without compromising on performance.
256
257 #+begin_src emacs-lisp
258 (straight-use-package 'use-package)
259 (if nil ; set to t when need to debug init
260 (setq use-package-verbose t
261 use-package-expand-minimally nil
262 use-package-compute-statistics t
263 debug-on-error t)
264 (setq use-package-verbose nil
265 use-package-expand-minimally t))
266
267 (setq use-package-always-defer t)
268 #+end_src
269
270 *** Epkg
271
272 #+begin_quote
273 Browse the Emacsmirror package database
274 #+end_quote
275
276 Epkg provides access to a local copy of the [[https://emacsmirror.net][Emacsmirror]] package
277 database, low-level functions for querying the database, and a
278 =package.el=-like user interface for browsing the available packages.
279
280 #+begin_src emacs-lisp
281 (require 'bind-key)
282 (use-package epkg
283 :commands (epkg-list-packages epkg-describe-package)
284 :bind
285 (("C-c P e d" . epkg-describe-package)
286 ("C-c P e p" . epkg-list-packages))
287 :config
288 (setq epkg-repository "~/.emacs.d/straight/repos/epkgs/")
289 (eval-when-compile (defvar ivy-initial-inputs-alist))
290 (with-eval-after-load 'ivy
291 (add-to-list
292 'ivy-initial-inputs-alist '(epkg-describe-package . "^") t)))
293 #+end_src
294
295 ** No littering in =~/.emacs.d=
296
297 #+begin_quote
298 Help keeping ~/.emacs.d clean
299 #+end_quote
300
301 By default, even for Emacs' built-in packages, the configuration files
302 and persistent data are all over the place. Use =no-littering= to help
303 contain the mess.
304
305 #+begin_src emacs-lisp
306 (use-package no-littering
307 :demand t
308 :config
309 (savehist-mode 1)
310 (add-to-list 'savehist-additional-variables 'kill-ring)
311 (save-place-mode 1)
312 (setq auto-save-file-name-transforms
313 `((".*" ,(no-littering-expand-var-file-name "auto-save/") t))))
314 #+end_src
315
316 ** Custom file (=custom.el=)
317
318 I'm not planning on using the custom file much, but even so, I
319 definitely don't want it mixing with =init.el=. So, here; let's give
320 it it's own file. While at it, treat themes as safe.
321
322 #+begin_src emacs-lisp
323 (use-feature custom
324 :no-require t
325 :config
326 (setq custom-file (no-littering-expand-etc-file-name "custom.el"))
327 (when (file-exists-p custom-file)
328 (load custom-file))
329 (setf custom-safe-themes t))
330 #+end_src
331
332 ** Secrets file
333
334 Load the secrets file if it exists, otherwise show a warning.
335
336 #+begin_src emacs-lisp
337 (with-demoted-errors
338 (load (no-littering-expand-etc-file-name "secrets")))
339 #+end_src
340
341 ** Better =$PATH= handling
342
343 Let's use [[https://github.com/purcell/exec-path-from-shell][exec-path-from-shell]] to make Emacs use the =$PATH= as set up
344 in my shell.
345
346 #+begin_src emacs-lisp
347 (use-package exec-path-from-shell
348 :defer 1
349 :init
350 (setq exec-path-from-shell-check-startup-files nil)
351 :config
352 (exec-path-from-shell-initialize)
353 ;; while we're at it, let's fix access to our running ssh-agent
354 (exec-path-from-shell-copy-env "SSH_AGENT_PID")
355 (exec-path-from-shell-copy-env "SSH_AUTH_SOCK"))
356 #+end_src
357
358 ** COMMENT Only one custom theme at a time
359
360 #+begin_src emacs-lisp
361 (defadvice load-theme (before clear-previous-themes activate)
362 "Clear existing theme settings instead of layering them"
363 (mapc #'disable-theme custom-enabled-themes))
364 #+end_src
365
366 ** Server
367
368 Start server if not already running. Alternatively, can be done by
369 issuing =emacs --daemon= in the terminal, which can be automated with
370 a systemd service or using =brew services start emacs= on macOS. I use
371 Emacs as my window manager (via EXWM), so I always start Emacs on
372 login; so starting the server from inside Emacs is good enough for me.
373
374 See [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Emacs-Server.html#Emacs-Server][Using Emacs as a Server]].
375
376 #+begin_src emacs-lisp
377 (use-feature server
378 :defer 1
379 :config (or (server-running-p) (server-mode)))
380 #+end_src
381
382 ** COMMENT Unicode support
383
384 Font stack with better unicode support, around =Ubuntu Mono= and
385 =Hack=.
386
387 #+begin_src emacs-lisp
388 (dolist (ft (fontset-list))
389 (set-fontset-font
390 ft
391 'unicode
392 (font-spec :name "Source Code Pro" :size 14))
393 (set-fontset-font
394 ft
395 'unicode
396 (font-spec :name "DejaVu Sans Mono")
397 nil
398 'append)
399 ;; (set-fontset-font
400 ;; ft
401 ;; 'unicode
402 ;; (font-spec
403 ;; :name "Symbola monospacified for DejaVu Sans Mono")
404 ;; nil
405 ;; 'append)
406 ;; (set-fontset-font
407 ;; ft
408 ;; #x2115 ; ℕ
409 ;; (font-spec :name "DejaVu Sans Mono")
410 ;; nil
411 ;; 'append)
412 (set-fontset-font
413 ft
414 (cons ?Α ?ω)
415 (font-spec :name "DejaVu Sans Mono" :size 14)
416 nil
417 'prepend))
418 #+end_src
419
420 ** Gentler font resizing
421
422 #+begin_src emacs-lisp
423 (setq text-scale-mode-step 1.05)
424 #+end_src
425
426 ** Focus follows mouse
427
428 I’d like focus to follow the mouse when I move the cursor from one
429 window to the next.
430
431 #+begin_src emacs-lisp
432 (setq mouse-autoselect-window t)
433 #+end_src
434
435 Let’s define a function to conveniently disable this for certain
436 buffers and/or modes.
437
438 #+begin_src emacs-lisp
439 (defun a/no-mouse-autoselect-window ()
440 (make-local-variable 'mouse-autoselect-window)
441 (setq mouse-autoselect-window nil))
442 #+end_src
443
444 ** Libraries
445
446 #+begin_src emacs-lisp
447 (require 'cl-lib)
448 (require 'subr-x)
449 #+end_src
450
451 ** Useful utilities
452
453 Convenience macro for =setq='ing multiple variables to the same value:
454
455 #+begin_src emacs-lisp
456 (defmacro a/setq-every (value &rest vars)
457 "Set all the variables from VARS to value VALUE."
458 (declare (indent defun) (debug t))
459 `(progn ,@(mapcar (lambda (x) (list 'setq x value)) vars)))
460 #+end_src
461
462 The following process-related stuff from [[https://github.com/alezost/emacs-config][alezost's emacs-config]].
463
464 #+begin_src emacs-lisp
465 (defun a/start-process (program &rest args)
466 "Same as `start-process', but doesn't bother about name and buffer."
467 (let ((process-name (concat program "_process"))
468 (buffer-name (generate-new-buffer-name
469 (concat program "_output"))))
470 (apply #'start-process
471 process-name buffer-name program args)))
472
473 (defun a/dired-start-process (program &optional args)
474 "Open current file with a PROGRAM."
475 ;; Shell command looks like this: "program [ARGS]... FILE" (ARGS can
476 ;; be nil, so remove it).
477 (apply #'a/start-process
478 program
479 (remove nil (list args (dired-get-file-for-visit)))))
480 #+end_src
481
482 * Core
483 :PROPERTIES:
484 :CUSTOM_ID: core
485 :END:
486
487 ** Defaults
488
489 *** Time and battery in mode-line
490
491 Enable displaying time and battery in the mode-line, since I'm not
492 using the Xfce panel anymore. Also, I don't need to see the load
493 average on a regular basis, so disable that.
494
495 Note: using =i3status= on sway at the moment, so disabling this.
496
497 #+begin_src emacs-lisp :tangle no
498 (use-package time
499 :init
500 (setq display-time-default-load-average nil)
501 :config
502 (display-time-mode))
503
504 (use-package battery
505 :config
506 (display-battery-mode))
507 #+end_src
508
509 *** Smaller fringe
510
511 Might want to set the fringe to a smaller value, especially if using
512 EXWM. I'm fine with the default for now.
513
514 #+begin_src emacs-lisp
515 ;; (fringe-mode '(3 . 1))
516 (fringe-mode nil)
517 #+end_src
518
519 *** Disable disabled commands
520
521 Emacs disables some commands by default that could persumably be
522 confusing for novice users. Let's disable that.
523
524 #+begin_src emacs-lisp
525 (setq disabled-command-function nil)
526 #+end_src
527
528 *** Kill-ring
529
530 Save what I copy into clipboard from other applications into Emacs'
531 kill-ring, which would allow me to still be able to easily access it
532 in case I kill (cut or copy) something else inside Emacs before
533 yanking (pasting) what I'd originally intended to.
534
535 #+begin_src emacs-lisp
536 (setq save-interprogram-paste-before-kill t)
537 #+end_src
538
539 *** Minibuffer
540
541 #+begin_src emacs-lisp
542 (setq enable-recursive-minibuffers t
543 resize-mini-windows t)
544 #+end_src
545
546 *** Lazy-person-friendly yes/no prompts
547
548 Lazy people would prefer to type fewer keystrokes, especially for yes
549 or no questions. I'm lazy.
550
551 #+begin_src emacs-lisp
552 (defalias 'yes-or-no-p #'y-or-n-p)
553 #+end_src
554
555 *** Startup screen and =*scratch*=
556
557 Firstly, let Emacs know that I'd like to have =*scratch*= as my
558 startup buffer.
559
560 #+begin_src emacs-lisp
561 (setq initial-buffer-choice t)
562 #+end_src
563
564 Now let's customize the =*scratch*= buffer a bit. First off, I don't
565 need the default hint.
566
567 #+begin_src emacs-lisp
568 (setq initial-scratch-message nil)
569 #+end_src
570
571 Also, let's use Text mode as the major mode, in case I want to
572 customize it (=*scratch*='s default major mode, Fundamental mode,
573 can't really be customized).
574
575 #+begin_src emacs-lisp
576 (setq initial-major-mode 'text-mode)
577 #+end_src
578
579 Inhibit the buffer list when more than 2 files are loaded.
580
581 #+begin_src emacs-lisp
582 (setq inhibit-startup-buffer-menu t)
583 #+end_src
584
585 I don't really need to see the startup screen or echo area message
586 either.
587
588 #+begin_src emacs-lisp
589 (advice-add #'display-startup-echo-area-message :override #'ignore)
590 (setq inhibit-startup-screen t
591 inhibit-startup-echo-area-message user-login-name)
592 #+end_src
593
594 *** More useful frame titles
595
596 Show either the file name or the buffer name (in case the buffer isn't
597 visiting a file). Borrowed from Emacs Prelude.
598
599 #+begin_src emacs-lisp
600 (setq frame-title-format
601 '("" invocation-name " - "
602 (:eval (if (buffer-file-name)
603 (abbreviate-file-name (buffer-file-name))
604 "%b"))))
605 #+end_src
606
607 *** Backups
608
609 Emacs' default backup settings aren't that great. Let's use more
610 sensible options. See documentation for the ~make-backup-file~
611 variable.
612
613 #+begin_src emacs-lisp
614 (setq backup-by-copying t
615 version-control t
616 delete-old-versions t)
617 #+end_src
618
619 *** Auto revert
620
621 Enable automatic reloading of changed buffers and files.
622
623 #+begin_src emacs-lisp
624 (global-auto-revert-mode 1)
625 (setq auto-revert-verbose nil
626 global-auto-revert-non-file-buffers nil)
627 #+end_src
628
629 *** Always use space for indentation
630
631 #+begin_src emacs-lisp
632 (setq-default
633 indent-tabs-mode nil
634 require-final-newline t
635 tab-width 4)
636 #+end_src
637
638 *** Winner mode
639
640 Enable =winner-mode=.
641
642 #+begin_src emacs-lisp
643 (winner-mode 1)
644 #+end_src
645
646 *** Don’t display =*compilation*= on success
647
648 Based on https://stackoverflow.com/a/17788551, with changes to use
649 =cl-letf= instead of the now obsolete =flet=.
650
651 #+begin_src emacs-lisp
652 (with-eval-after-load 'compile
653 (defun a/compilation-finish-function (buffer outstr)
654 (unless (string-match "finished" outstr)
655 (switch-to-buffer-other-window buffer))
656 t)
657
658 (setq compilation-finish-functions #'a/compilation-finish-function)
659
660 (require 'cl-macs)
661
662 (defadvice compilation-start
663 (around inhibit-display
664 (command &optional mode name-function highlight-regexp))
665 (if (not (string-match "^\\(find\\|grep\\)" command))
666 (cl-letf (((symbol-function 'display-buffer) #'ignore))
667 (save-window-excursion ad-do-it))
668 ad-do-it))
669 (ad-activate 'compilation-start))
670 #+end_src
671
672 *** Search for non-ASCII characters
673
674 I’d like non-ASCII characters such as ‘’“”«»‹›áⓐ𝒶 to be selected when
675 I search for their ASCII counterpart. Shoutout to [[http://endlessparentheses.com/new-in-emacs-25-1-easily-search-non-ascii-characters.html][endlessparentheses]]
676 for this.
677
678 #+begin_src emacs-lisp
679 (setq search-default-mode #'char-fold-to-regexp)
680
681 ;; uncomment to extend this behaviour to query-replace
682 ;; (setq replace-char-fold t)
683 #+end_src
684
685 *** Cursor shape
686
687 #+begin_src emacs-lisp
688 (setq-default cursor-type 'bar)
689 #+end_src
690
691 *** Allow scrolling in Isearch
692
693 #+begin_src emacs-lisp
694 (setq isearch-allow-scroll t)
695 #+end_src
696
697 ** Bindings
698
699 #+begin_src emacs-lisp
700 (bind-keys
701 ("C-c a i" . ielm)
702
703 ("C-c e b" . eval-buffer)
704 ("C-c e r" . eval-region)
705
706 ("C-c F m" . make-frame-command)
707 ("C-c F d" . delete-frame)
708 ("C-c F D" . delete-other-frames)
709
710 ("C-c o" . other-window)
711
712 ("C-c Q" . save-buffers-kill-terminal)
713
714 ("C-S-h C" . describe-char)
715 ("C-S-h F" . describe-face)
716
717 ("C-x K" . kill-this-buffer)
718
719 ("s-p" . beginning-of-buffer)
720 ("s-n" . end-of-buffer))
721 #+end_src
722
723 ** Packages
724
725 The packages in this section are absolutely essential to my everyday
726 workflow, and they play key roles in how I do my computing. They
727 immensely enhance the Emacs experience for me; both using Emacs, and
728 customizing it.
729
730 *** [[https://github.com/emacscollective/auto-compile][auto-compile]]
731
732 #+begin_src emacs-lisp
733 (use-package auto-compile
734 :demand t
735 :config
736 (auto-compile-on-load-mode)
737 (auto-compile-on-save-mode)
738 (setq auto-compile-display-buffer nil
739 auto-compile-mode-line-counter t
740 auto-compile-source-recreate-deletes-dest t
741 auto-compile-toggle-deletes-nonlib-dest t
742 auto-compile-update-autoloads t)
743 (add-hook 'auto-compile-inhibit-compile-hook
744 'auto-compile-inhibit-compile-detached-git-head))
745 #+end_src
746
747 *** [[https://orgmode.org/][Org]]
748
749 #+begin_quote
750 Org mode is for keeping notes, maintaining TODO lists, planning
751 projects, and authoring documents with a fast and effective plain-text
752 system.
753 #+end_quote
754
755 In short, my favourite way of life.
756
757 First, we have to resort to a [[https://github.com/raxod502/straight.el#installing-org-with-straightel][hack]] to be able to use the correct
758 latest version of Org from upstream.
759
760 #+begin_src emacs-lisp
761 (use-package git)
762
763 (defun org-git-version ()
764 "The Git version of org-mode.
765 Inserted by installing org-mode or when a release is made."
766 (require 'git)
767 (let ((git-repo (expand-file-name
768 "straight/repos/org/" user-emacs-directory)))
769 (string-trim
770 (git-run "describe"
771 "--match=release\*"
772 "--abbrev=6"
773 "HEAD"))))
774
775 (defun org-release ()
776 "The release version of org-mode.
777 Inserted by installing org-mode or when a release is made."
778 (require 'git)
779 (let ((git-repo (expand-file-name
780 "straight/repos/org/" user-emacs-directory)))
781 (string-trim
782 (string-remove-prefix
783 "release_"
784 (git-run "describe"
785 "--match=release\*"
786 "--abbrev=0"
787 "HEAD")))))
788
789 (provide 'org-version)
790 #+end_src
791
792 We will use the =org-plus-contrib= package to get the whole deal:
793
794 #+begin_src emacs-lisp
795 (straight-use-package 'org-plus-contrib)
796 #+end_src
797
798 And here's where my actual Org configurations begin:
799
800 #+begin_src emacs-lisp
801 (use-feature org
802 :defer 0.5
803 :config
804 (setq org-src-tab-acts-natively t
805 org-src-preserve-indentation nil
806 org-edit-src-content-indentation 0
807 org-email-link-description-format "Email %c: %s" ; %.30s
808 org-highlight-latex-and-related '(entities)
809 org-use-speed-commands t
810 org-startup-folded 'content
811 org-catch-invisible-edits 'show-and-error
812 org-log-done 'time)
813 (add-to-list 'org-structure-template-alist '("L" . "src emacs-lisp") t)
814 (font-lock-add-keywords
815 'org-mode
816 '(("[ \t]*\\(#\\+\\(BEGIN\\|END\\|begin\\|end\\)_\\(\\S-+\\)\\)[ \t]*\\([^\n:]*\\)"
817 (1 '(:foreground "#5a5b5a" :background "#292b2b") t) ; directive
818 (3 '(:foreground "#81a2be" :background "#292b2b") t) ; kind
819 (4 '(:foreground "#c5c8c6") t))) ; title
820 t)
821 :bind (:map org-mode-map ("M-L" . org-insert-last-stored-link))
822 :hook ((org-mode . org-indent-mode)
823 (org-mode . auto-fill-mode)
824 (org-mode . flyspell-mode))
825 :custom
826 (org-latex-packages-alist '(("" "listings") ("" "color")))
827 :custom-face
828 '(org-block-begin-line ((t (:foreground "#5a5b5a" :background "#1d1f21"))))
829 '(org-block ((t (:background "#1d1f21"))))
830 '(org-latex-and-related ((t (:foreground "#b294bb")))))
831
832 (use-feature ox-latex
833 :after ox
834 :config
835 (setq org-latex-listings 'listings
836 ;; org-latex-prefer-user-labels t
837 )
838 (add-to-list 'org-latex-packages-alist '("" "listings"))
839 (add-to-list 'org-latex-packages-alist '("" "color"))
840 (add-to-list 'org-latex-classes
841 '("IEEEtran" "\\documentclass[11pt]{IEEEtran}"
842 ("\\section{%s}" . "\\section*{%s}")
843 ("\\subsection{%s}" . "\\subsection*{%s}")
844 ("\\subsubsection{%s}" . "\\subsubsection*{%s}")
845 ("\\paragraph{%s}" . "\\paragraph*{%s}")
846 ("\\subparagraph{%s}" . "\\subparagraph*{%s}"))
847 t))
848
849 (use-feature ox-beamer
850 :demand
851 :after ox)
852 #+end_src
853
854 **** asynchronous tangle
855
856 =a/async-babel-tangle= is a function closely inspired by [[https://github.com/dieggsy/dotfiles/tree/cc10edf7701958eff1cd94d4081da544d882a28c/emacs.d#dotfiles][dieggsy's
857 d/async-babel-tangle]] which uses [[https://github.com/jwiegley/emacs-async][async]] to asynchronously tangle an org
858 file.
859
860 #+begin_src emacs-lisp
861 (with-eval-after-load 'org
862 (defvar a/show-async-tangle-results nil
863 "Keep *emacs* async buffers around for later inspection.")
864
865 (defvar a/show-async-tangle-time nil
866 "Show the time spent tangling the file.")
867
868 (defvar a/async-tangle-post-compile "make bi"
869 "If non-nil, pass to `compile' after successful tangle.")
870
871 (defvar a/async-tangle-byte-recompile nil
872 "If non-nil, byte-recompile the file on successful tangle.")
873
874 (defun a/async-babel-tangle ()
875 "Tangle org file asynchronously."
876 (interactive)
877 (let* ((file-tangle-start-time (current-time))
878 (file (buffer-file-name))
879 (file-nodir (file-name-nondirectory file))
880 ;; (async-quiet-switch "-q")
881 (file-noext (file-name-sans-extension file)))
882 (async-start
883 `(lambda ()
884 (require 'org)
885 (org-babel-tangle-file ,file))
886 (unless a/show-async-tangle-results
887 `(lambda (result)
888 (if result
889 (progn
890 (setq byte-compile-warnings '(not noruntime unresolved))
891 (message "Tangled %s%s"
892 ,file-nodir
893 (if a/show-async-tangle-time
894 (format " (%.3fs)"
895 (float-time (time-subtract (current-time)
896 ',file-tangle-start-time)))
897 ""))
898 (when a/async-tangle-post-compile
899 (compile a/async-tangle-post-compile))
900 (when a/async-tangle-byte-recompile
901 (byte-recompile-file (concat ,file-noext ".el"))))
902 (message "Tangling %s failed" ,file-nodir))))))))
903
904 (add-to-list
905 'safe-local-variable-values
906 '(eval add-hook 'after-save-hook #'a/async-babel-tangle 'append 'local))
907 #+end_src
908
909 *** [[https://magit.vc/][Magit]]
910
911 #+begin_quote
912 It's Magit! A Git porcelain inside Emacs.
913 #+end_quote
914
915 Not just how I do git, but /the/ way to do git.
916
917 #+begin_src emacs-lisp
918 (use-package magit
919 :defer 1
920 :bind (("C-x g" . magit-status)
921 ("s-g s" . magit-status)
922 ("s-g l" . magit-log-buffer-file))
923 :config
924 (magit-add-section-hook 'magit-status-sections-hook
925 'magit-insert-modules
926 'magit-insert-stashes
927 'append)
928 (setq
929 magit-repository-directories '(("~/.emacs.d/" . 0)
930 ("~/src/git/" . 1)))
931 (nconc magit-section-initial-visibility-alist
932 '(([unpulled status] . show)
933 ([unpushed status] . show)))
934 :custom-face (magit-diff-file-heading ((t (:weight normal)))))
935 #+end_src
936
937 *** recentf
938
939 Recently opened files.
940
941 #+begin_src emacs-lisp
942 (use-feature recentf
943 :defer 0.5
944 :config
945 (add-to-list 'recentf-exclude "^/\\(?:ssh\\|su\\|sudo\\)?:")
946 (setq recentf-max-saved-items 40))
947 #+end_src
948
949 *** smex
950
951 #+begin_quote
952 A smart M-x enhancement for Emacs.
953 #+end_quote
954
955 Mostly because =counsel= needs it to remember history.
956
957 #+begin_src emacs-lisp
958 (use-package smex)
959 #+end_src
960
961 *** [[https://github.com/abo-abo/swiper][Ivy]] (and friends)
962
963 #+begin_quote
964 Ivy - a generic completion frontend for Emacs, Swiper - isearch with
965 an overview, and more. Oh, man!
966 #+end_quote
967
968 There's no way I could top that, so I won't attempt to.
969
970 **** Ivy
971
972 #+begin_src emacs-lisp
973 (use-package ivy
974 :defer 0.6
975 :bind
976 (:map ivy-minibuffer-map
977 ([escape] . keyboard-escape-quit)
978 ([S-up] . ivy-previous-history-element)
979 ([S-down] . ivy-next-history-element)
980 ("DEL" . ivy-backward-delete-char))
981 :config
982 (setq ivy-wrap t)
983 (ivy-mode 1)
984 ;; :custom-face
985 ;; (ivy-minibuffer-match-face-2 ((t (:background "#e99ce8" :weight semi-bold))))
986 ;; (ivy-minibuffer-match-face-3 ((t (:background "#bbbbff" :weight semi-bold))))
987 ;; (ivy-minibuffer-match-face-4 ((t (:background "#ffbbff" :weight semi-bold))))
988 )
989 #+end_src
990
991 **** Swiper
992
993 #+begin_src emacs-lisp
994 (use-package swiper
995 :after ivy
996 :bind (("C-s" . swiper)
997 ("C-r" . swiper)
998 ("C-S-s" . isearch-forward)))
999 #+end_src
1000
1001 **** Counsel
1002
1003 #+begin_src emacs-lisp
1004 (use-package counsel
1005 :after ivy
1006 :bind (([remap execute-extended-command] . counsel-M-x)
1007 ([remap find-file] . counsel-find-file)
1008 ("s-r" . counsel-recentf)
1009 ("C-c x" . counsel-M-x)
1010 ("C-c f ." . counsel-find-file)
1011 :map minibuffer-local-map
1012 ("C-r" . counsel-minibuffer-history))
1013 :config
1014 (counsel-mode 1)
1015 (defalias 'locate #'counsel-locate))
1016 #+end_src
1017
1018 *** eshell
1019
1020 #+begin_src emacs-lisp
1021 (use-feature eshell
1022 :defer 1
1023 :commands eshell
1024 :bind ("C-c a s e" . eshell)
1025 :config
1026 (eval-when-compile (defvar eshell-prompt-regexp))
1027 (defun a/eshell-quit-or-delete-char (arg)
1028 (interactive "p")
1029 (if (and (eolp) (looking-back eshell-prompt-regexp nil))
1030 (eshell-life-is-too-much)
1031 (delete-char arg)))
1032
1033 (defun a/eshell-clear ()
1034 (interactive)
1035 (let ((inhibit-read-only t))
1036 (erase-buffer))
1037 (eshell-send-input))
1038
1039 (defun a/eshell-setup ()
1040 (make-local-variable 'company-idle-delay)
1041 (defvar company-idle-delay)
1042 (setq company-idle-delay nil)
1043 (bind-keys :map eshell-mode-map
1044 ("C-d" . a/eshell-quit-or-delete-char)
1045 ("C-S-l" . a/eshell-clear)
1046 ("M-r" . counsel-esh-history)
1047 ([tab] . company-complete)))
1048
1049 :hook (eshell-mode . a/eshell-setup)
1050 :custom
1051 (eshell-hist-ignoredups t)
1052 (eshell-input-filter 'eshell-input-filter-initial-space))
1053 #+end_src
1054
1055 *** Ibuffer
1056
1057 #+begin_src emacs-lisp
1058 (use-feature ibuffer
1059 :bind
1060 (("C-x C-b" . ibuffer-other-window)
1061 :map ibuffer-mode-map
1062 ("P" . ibuffer-backward-filter-group)
1063 ("N" . ibuffer-forward-filter-group)
1064 ("M-p" . ibuffer-do-print)
1065 ("M-n" . ibuffer-do-shell-command-pipe-replace))
1066 :config
1067 ;; Use human readable Size column instead of original one
1068 (define-ibuffer-column size-h
1069 (:name "Size" :inline t)
1070 (cond
1071 ((> (buffer-size) 1000000) (format "%7.1fM" (/ (buffer-size) 1000000.0)))
1072 ((> (buffer-size) 100000) (format "%7.0fk" (/ (buffer-size) 1000.0)))
1073 ((> (buffer-size) 1000) (format "%7.1fk" (/ (buffer-size) 1000.0)))
1074 (t (format "%8d" (buffer-size)))))
1075 :custom
1076 (ibuffer-saved-filter-groups
1077 '(("default"
1078 ("dired" (mode . dired-mode))
1079 ("org" (mode . org-mode))
1080 ("gnus"
1081 (or
1082 (mode . gnus-group-mode)
1083 (mode . gnus-summary-mode)
1084 (mode . gnus-article-mode)
1085 ;; not really, but...
1086 (mode . message-mode)))
1087 ("web"
1088 (or
1089 (mode . web-mode)
1090 (mode . css-mode)
1091 (mode . scss-mode)
1092 (mode . js2-mode)))
1093 ("shell"
1094 (or
1095 (mode . eshell-mode)
1096 (mode . shell-mode)
1097 (mode . term-mode)))
1098 ("programming"
1099 (or
1100 (mode . python-mode)
1101 (mode . c-mode)
1102 (mode . c++-mode)
1103 (mode . emacs-lisp-mode)
1104 (mode . scheme-mode)
1105 (mode . haskell-mode)
1106 (mode . lean-mode)))
1107 ("emacs"
1108 (or
1109 (name . "^\\*scratch\\*$")
1110 (name . "^\\*Messages\\*$"))))))
1111 (ibuffer-formats
1112 '((mark modified read-only locked " "
1113 (name 18 18 :left :elide)
1114 " "
1115 (size-h 9 -1 :right)
1116 " "
1117 (mode 16 16 :left :elide)
1118 " " filename-and-process)
1119 (mark " "
1120 (name 16 -1)
1121 " " filename)))
1122 :hook (ibuffer . (lambda () (ibuffer-switch-to-saved-filter-groups "default"))))
1123 #+end_src
1124
1125 *** Outline
1126
1127 #+begin_src emacs-lisp
1128 (use-feature outline
1129 :hook (prog-mode . outline-minor-mode)
1130 :bind
1131 (:map
1132 outline-minor-mode-map
1133 ("<s-tab>" . outline-toggle-children)
1134 ("M-p" . outline-previous-visible-heading)
1135 ("M-n" . outline-next-visible-heading)
1136 :prefix-map a/outline-prefix-map
1137 :prefix "s-o"
1138 ("TAB" . outline-toggle-children)
1139 ("a" . outline-hide-body)
1140 ("H" . outline-hide-body)
1141 ("S" . outline-show-all)
1142 ("h" . outline-hide-subtree)
1143 ("s" . outline-show-subtree)))
1144 #+end_src
1145
1146 *** Dired
1147
1148 #+begin_src emacs-lisp
1149 (use-feature ls-lisp
1150 :custom (ls-lisp-dirs-first t))
1151
1152 (use-feature dired
1153 :config
1154 (setq dired-listing-switches "-alh"
1155 ls-lisp-use-insert-directory-program nil)
1156
1157 ;; easily diff 2 marked files
1158 ;; https://oremacs.com/2017/03/18/dired-ediff/
1159 (defun dired-ediff-files ()
1160 (interactive)
1161 (require 'dired-aux)
1162 (defvar ediff-after-quit-hook-internal)
1163 (let ((files (dired-get-marked-files))
1164 (wnd (current-window-configuration)))
1165 (if (<= (length files) 2)
1166 (let ((file1 (car files))
1167 (file2 (if (cdr files)
1168 (cadr files)
1169 (read-file-name
1170 "file: "
1171 (dired-dwim-target-directory)))))
1172 (if (file-newer-than-file-p file1 file2)
1173 (ediff-files file2 file1)
1174 (ediff-files file1 file2))
1175 (add-hook 'ediff-after-quit-hook-internal
1176 (lambda ()
1177 (setq ediff-after-quit-hook-internal nil)
1178 (set-window-configuration wnd))))
1179 (error "no more than 2 files should be marked"))))
1180 :bind (:map dired-mode-map
1181 ("b" . dired-up-directory)
1182 ("e" . dired-ediff-files)
1183 ("E" . dired-toggle-read-only)
1184 ("\\" . dired-hide-details-mode)
1185 ("z" . (lambda ()
1186 (interactive)
1187 (a/dired-start-process "zathura"))))
1188 :hook (dired-mode . dired-hide-details-mode))
1189 #+end_src
1190
1191 *** Help
1192
1193 #+begin_src emacs-lisp
1194 (use-feature help
1195 :config
1196 (temp-buffer-resize-mode)
1197 (setq help-window-select t))
1198 #+end_src
1199
1200 *** Tramp
1201
1202 #+begin_src emacs-lisp
1203 (use-feature tramp
1204 :config
1205 (add-to-list 'tramp-default-proxies-alist '(nil "\\`root\\'" "/ssh:%h:"))
1206 (add-to-list 'tramp-default-proxies-alist '("localhost" nil nil))
1207 (add-to-list 'tramp-default-proxies-alist
1208 (list (regexp-quote (system-name)) nil nil)))
1209 #+end_src
1210
1211 *** Dash
1212
1213 #+begin_src emacs-lisp
1214 (use-package dash
1215 :config (dash-enable-font-lock))
1216 #+end_src
1217
1218 * Editing
1219 :PROPERTIES:
1220 :CUSTOM_ID: editing
1221 :END:
1222
1223 ** =diff-hl=
1224
1225 Highlight uncommitted changes in the left fringe.
1226
1227 #+begin_src emacs-lisp
1228 (use-package diff-hl
1229 :config
1230 (setq diff-hl-draw-borders nil)
1231 (global-diff-hl-mode)
1232 :hook (magit-post-refresh . diff-hl-magit-post-refresh))
1233 #+end_src
1234
1235 ** ElDoc
1236
1237 Display Lisp objects at point in the echo area.
1238
1239 #+begin_src emacs-lisp
1240 (use-feature eldoc
1241 :when (version< "25" emacs-version)
1242 :config (global-eldoc-mode))
1243 #+end_src
1244
1245 ** paren
1246
1247 Highlight matching parens.
1248
1249 #+begin_src emacs-lisp
1250 (use-feature paren
1251 :config (show-paren-mode))
1252 #+end_src
1253
1254 ** simple (for column numbers)
1255
1256 #+begin_src emacs-lisp
1257 (use-feature simple
1258 :config (column-number-mode))
1259 #+end_src
1260
1261 ** =savehist=
1262
1263 Save minibuffer history.
1264
1265 #+begin_src emacs-lisp
1266 (use-feature savehist
1267 :config (savehist-mode))
1268 #+end_src
1269
1270 ** =saveplace=
1271
1272 Automatically save place in each file.
1273
1274 #+begin_src emacs-lisp
1275 (use-feature saveplace
1276 :when (version< "25" emacs-version)
1277 :config (save-place-mode))
1278 #+end_src
1279
1280 ** =prog-mode=
1281
1282 #+begin_src emacs-lisp
1283 (use-feature prog-mode
1284 :config (global-prettify-symbols-mode)
1285 (defun indicate-buffer-boundaries-left ()
1286 (setq indicate-buffer-boundaries 'left))
1287 (add-hook 'prog-mode-hook #'indicate-buffer-boundaries-left))
1288 #+end_src
1289
1290 ** =text-mode=
1291
1292 #+begin_src emacs-lisp
1293 (use-feature text-mode
1294 :hook ((text-mode . indicate-buffer-boundaries-left)
1295 (text-mode . abbrev-mode)))
1296 #+end_src
1297
1298 ** Company
1299
1300 #+begin_src emacs-lisp
1301 (use-package company
1302 :defer 1
1303 :bind
1304 (:map company-active-map
1305 ([tab] . company-complete-common-or-cycle)
1306 ([escape] . company-abort))
1307 :custom
1308 (company-minimum-prefix-length 1)
1309 (company-selection-wrap-around t)
1310 (company-dabbrev-char-regexp "\\sw\\|\\s_\\|[-_]")
1311 (company-dabbrev-downcase nil)
1312 (company-dabbrev-ignore-case nil)
1313 :config
1314 (global-company-mode t))
1315 #+end_src
1316
1317 ** Flycheck
1318
1319 #+begin_src emacs-lisp
1320 (use-package flycheck
1321 :defer 3
1322 :hook (prog-mode . flycheck-mode)
1323 :bind
1324 (:map flycheck-mode-map
1325 ("M-P" . flycheck-previous-error)
1326 ("M-N" . flycheck-next-error))
1327 :config
1328 ;; Use the load-path from running Emacs when checking elisp files
1329 (setq flycheck-emacs-lisp-load-path 'inherit)
1330
1331 ;; Only flycheck when I actually save the buffer
1332 (setq flycheck-check-syntax-automatically '(mode-enabled save)))
1333
1334 ;; http://endlessparentheses.com/ispell-and-apostrophes.html
1335 (use-package ispell
1336 :defer 3
1337 :config
1338 ;; ’ can be part of a word
1339 (setq ispell-local-dictionary-alist
1340 `((nil "[[:alpha:]]" "[^[:alpha:]]"
1341 "['\x2019]" nil ("-B") nil utf-8)))
1342 ;; don't send ’ to the subprocess
1343 (defun endless/replace-apostrophe (args)
1344 (cons (replace-regexp-in-string
1345 "’" "'" (car args))
1346 (cdr args)))
1347 (advice-add #'ispell-send-string :filter-args
1348 #'endless/replace-apostrophe)
1349
1350 ;; convert ' back to ’ from the subprocess
1351 (defun endless/replace-quote (args)
1352 (if (not (derived-mode-p 'org-mode))
1353 args
1354 (cons (replace-regexp-in-string
1355 "'" "’" (car args))
1356 (cdr args))))
1357 (advice-add #'ispell-parse-output :filter-args
1358 #'endless/replace-quote))
1359 #+end_src
1360
1361 * Programming modes
1362 :PROPERTIES:
1363 :CUSTOM_ID: programming-modes
1364 :END:
1365
1366 ** Lisp
1367
1368 #+begin_src emacs-lisp
1369 (use-feature lisp-mode
1370 :config
1371 (add-hook 'emacs-lisp-mode-hook 'outline-minor-mode)
1372 (add-hook 'emacs-lisp-mode-hook 'reveal-mode)
1373 (defun indent-spaces-mode ()
1374 (setq indent-tabs-mode nil))
1375 (add-hook 'lisp-interaction-mode-hook #'indent-spaces-mode))
1376 #+end_src
1377
1378 ** [[http://alloytools.org][Alloy]] (with [[https://github.com/dwwmmn/alloy-mode][alloy-mode]])
1379
1380 #+begin_src emacs-lisp
1381 (use-package alloy-mode
1382 :straight (:host github :repo "dwwmmn/alloy-mode")
1383 :config (setq alloy-basic-offset 2))
1384 #+end_src
1385
1386 ** [[https://coq.inria.fr][Coq]] (with [[https://github.com/ProofGeneral/PG][Proof General]])
1387
1388 #+begin_src emacs-lisp
1389 (use-package proof-site
1390 :straight proof-general)
1391 #+end_src
1392
1393 ** [[https://leanprover.github.io][Lean]] (with [[https://github.com/leanprover/lean-mode][lean-mode]])
1394
1395 #+begin_src emacs-lisp
1396 (eval-when-compile (defvar lean-mode-map))
1397 (use-package lean-mode
1398 :defer 1
1399 :bind (:map lean-mode-map
1400 ("S-SPC" . company-complete))
1401 :config
1402 (require 'lean-input)
1403 (setq default-input-method "Lean"
1404 lean-input-tweak-all '(lean-input-compose
1405 (lean-input-prepend "/")
1406 (lean-input-nonempty))
1407 lean-input-user-translations '(("/" "/")))
1408 (lean-input-setup))
1409 #+end_src
1410
1411 ** Haskell
1412
1413 *** [[https://github.com/haskell/haskell-mode][haskell-mode]]
1414
1415 #+begin_src emacs-lisp
1416 (use-package haskell-mode
1417 :config
1418 (setq haskell-indentation-layout-offset 4
1419 haskell-indentation-left-offset 4
1420 flycheck-checker 'haskell-hlint
1421 flycheck-disabled-checkers '(haskell-stack-ghc haskell-ghc)))
1422 #+end_src
1423
1424 *** [[https://github.com/jyp/dante][dante]]
1425
1426 #+begin_src emacs-lisp
1427 (use-package dante
1428 :after haskell-mode
1429 :commands dante-mode
1430 :hook (haskell-mode . dante-mode))
1431 #+end_src
1432
1433 *** [[https://github.com/mpickering/hlint-refactor-mode][hlint-refactor]]
1434
1435 Emacs bindings for [[https://github.com/ndmitchell/hlint][hlint]]'s refactor option. This requires the refact
1436 executable from [[https://github.com/mpickering/apply-refact][apply-refact]].
1437
1438 #+begin_src emacs-lisp
1439 (use-package hlint-refactor
1440 :after haskell-mode
1441 :bind (:map hlint-refactor-mode-map
1442 ("C-c l b" . hlint-refactor-refactor-buffer)
1443 ("C-c l r" . hlint-refactor-refactor-at-point))
1444 :hook (haskell-mode . hlint-refactor-mode))
1445 #+end_src
1446
1447 *** [[https://github.com/flycheck/flycheck-haskell][flycheck-haskell]]
1448
1449 #+begin_src emacs-lisp
1450 (use-package flycheck-haskell
1451 :after haskell-mode)
1452 #+end_src
1453
1454 *** [[https://github.com/ndmitchell/hlint/blob/20e116a043f2073c57b17b24ae6364b5e433ba7e/data/hs-lint.el][hs-lint.el]]
1455 :PROPERTIES:
1456 :header-args+: :tangle lisp/hs-lint.el :mkdirp yes
1457 :END:
1458
1459 Currently using =flycheck-haskell= with the =haskell-hlint= checker
1460 instead.
1461
1462 #+begin_src emacs-lisp :tangle no
1463 ;;; hs-lint.el --- minor mode for HLint code checking
1464
1465 ;; Copyright 2009 (C) Alex Ott
1466 ;;
1467 ;; Author: Alex Ott <alexott@gmail.com>
1468 ;; Keywords: haskell, lint, HLint
1469 ;; Requirements:
1470 ;; Status: distributed under terms of GPL2 or above
1471
1472 ;; Typical message from HLint looks like:
1473 ;;
1474 ;; /Users/ott/projects/lang-exp/haskell/test.hs:52:1: Eta reduce
1475 ;; Found:
1476 ;; count1 p l = length (filter p l)
1477 ;; Why not:
1478 ;; count1 p = length . filter p
1479
1480
1481 (require 'compile)
1482
1483 (defgroup hs-lint nil
1484 "Run HLint as inferior of Emacs, parse error messages."
1485 :group 'tools
1486 :group 'haskell)
1487
1488 (defcustom hs-lint-command "hlint"
1489 "The default hs-lint command for \\[hlint]."
1490 :type 'string
1491 :group 'hs-lint)
1492
1493 (defcustom hs-lint-save-files t
1494 "Save modified files when run HLint or no (ask user)"
1495 :type 'boolean
1496 :group 'hs-lint)
1497
1498 (defcustom hs-lint-replace-with-suggestions nil
1499 "Replace user's code with suggested replacements"
1500 :type 'boolean
1501 :group 'hs-lint)
1502
1503 (defcustom hs-lint-replace-without-ask nil
1504 "Replace user's code with suggested replacements automatically"
1505 :type 'boolean
1506 :group 'hs-lint)
1507
1508 (defun hs-lint-process-setup ()
1509 "Setup compilation variables and buffer for `hlint'."
1510 (run-hooks 'hs-lint-setup-hook))
1511
1512 ;; regex for replace suggestions
1513 ;;
1514 ;; ^\(.*?\):\([0-9]+\):\([0-9]+\): .*
1515 ;; Found:
1516 ;; \s +\(.*\)
1517 ;; Why not:
1518 ;; \s +\(.*\)
1519
1520 (defvar hs-lint-regex
1521 "^\\(.*?\\):\\([0-9]+\\):\\([0-9]+\\): .*[\n\C-m]Found:[\n\C-m]\\s +\\(.*\\)[\n\C-m]Why not:[\n\C-m]\\s +\\(.*\\)[\n\C-m]"
1522 "Regex for HLint messages")
1523
1524 (defun make-short-string (str maxlen)
1525 (if (< (length str) maxlen)
1526 str
1527 (concat (substring str 0 (- maxlen 3)) "...")))
1528
1529 (defun hs-lint-replace-suggestions ()
1530 "Perform actual replacement of suggestions"
1531 (goto-char (point-min))
1532 (while (re-search-forward hs-lint-regex nil t)
1533 (let* ((fname (match-string 1))
1534 (fline (string-to-number (match-string 2)))
1535 (old-code (match-string 4))
1536 (new-code (match-string 5))
1537 (msg (concat "Replace '" (make-short-string old-code 30)
1538 "' with '" (make-short-string new-code 30) "'"))
1539 (bline 0)
1540 (eline 0)
1541 (spos 0)
1542 (new-old-code ""))
1543 (save-excursion
1544 (switch-to-buffer (get-file-buffer fname))
1545 (goto-char (point-min))
1546 (forward-line (1- fline))
1547 (beginning-of-line)
1548 (setf bline (point))
1549 (when (or hs-lint-replace-without-ask
1550 (yes-or-no-p msg))
1551 (end-of-line)
1552 (setf eline (point))
1553 (beginning-of-line)
1554 (setf old-code (regexp-quote old-code))
1555 (while (string-match "\\\\ " old-code spos)
1556 (setf new-old-code (concat new-old-code
1557 (substring old-code spos (match-beginning 0))
1558 "\\ *"))
1559 (setf spos (match-end 0)))
1560 (setf new-old-code (concat new-old-code (substring old-code spos)))
1561 (remove-text-properties bline eline '(composition nil))
1562 (when (re-search-forward new-old-code eline t)
1563 (replace-match new-code nil t)))))))
1564
1565 (defun hs-lint-finish-hook (buf msg)
1566 "Function, that is executed at the end of HLint execution"
1567 (if hs-lint-replace-with-suggestions
1568 (hs-lint-replace-suggestions)
1569 (next-error 1 t)))
1570
1571 (define-compilation-mode hs-lint-mode "HLint"
1572 "Mode for check Haskell source code."
1573 (set (make-local-variable 'compilation-process-setup-function)
1574 'hs-lint-process-setup)
1575 (set (make-local-variable 'compilation-disable-input) t)
1576 (set (make-local-variable 'compilation-scroll-output) nil)
1577 (set (make-local-variable 'compilation-finish-functions)
1578 (list 'hs-lint-finish-hook))
1579 )
1580
1581 (defun hs-lint ()
1582 "Run HLint for current buffer with haskell source"
1583 (interactive)
1584 (save-some-buffers hs-lint-save-files)
1585 (compilation-start (concat hs-lint-command " \"" buffer-file-name "\"")
1586 'hs-lint-mode))
1587
1588 (provide 'hs-lint)
1589 ;;; hs-lint.el ends here
1590 #+end_src
1591
1592 #+begin_src emacs-lisp :tangle no
1593 (use-package hs-lint
1594 :load-path "lisp/"
1595 :bind (:map haskell-mode-map
1596 ("C-c l l" . hs-lint)))
1597 #+end_src
1598
1599 ** Web
1600
1601 *** SGML and HTML
1602
1603 #+begin_src emacs-lisp
1604 (use-package sgml-mode
1605 :config
1606 (setq sgml-basic-offset 2))
1607 #+end_src
1608
1609 *** CSS and SCSS
1610
1611 #+begin_src emacs-lisp
1612 (use-package css-mode
1613 :config
1614 (setq css-indent-offset 2))
1615 #+end_src
1616
1617 *** Web mode
1618
1619 #+begin_src emacs-lisp
1620 (use-package web-mode
1621 :mode "\\.html\\'"
1622 :config
1623 (a/setq-every 2
1624 web-mode-code-indent-offset
1625 web-mode-css-indent-offset
1626 web-mode-markup-indent-offset))
1627 #+end_src
1628
1629 *** Emmet mode
1630
1631 #+begin_src emacs-lisp
1632 (use-package emmet-mode
1633 :after (:any web-mode css-mode sgml-mode)
1634 :bind* (("C-)" . emmet-next-edit-point)
1635 ("C-(" . emmet-prev-edit-point))
1636 :config
1637 (unbind-key "C-j" emmet-mode-keymap)
1638 (setq emmet-move-cursor-between-quotes t)
1639 :hook (web-mode css-mode html-mode sgml-mode))
1640 #+end_src
1641
1642 ** COMMENT Java
1643
1644 *** meghanada
1645
1646 #+begin_src emacs-lisp
1647 (use-package meghanada
1648 :bind
1649 (:map meghanada-mode-map
1650 (("C-M-o" . meghanada-optimize-import)
1651 ("C-M-t" . meghanada-import-all)))
1652 :hook (java-mode . meghanada-mode))
1653 #+end_src
1654
1655 *** lsp-java
1656
1657 #+begin_comment
1658 dependencies:
1659
1660 ace-window
1661 avy
1662 bui
1663 company-lsp
1664 dap-mode
1665 lsp-java
1666 lsp-mode
1667 lsp-ui
1668 pfuture
1669 tree-mode
1670 treemacs
1671 #+end_comment
1672
1673 #+begin_src emacs-lisp
1674 (use-package treemacs
1675 :config (setq treemacs-never-persist t))
1676
1677 (use-package yasnippet
1678 :config
1679 ;; (yas-global-mode)
1680 )
1681
1682 (use-package lsp-mode
1683 :init (setq lsp-eldoc-render-all nil
1684 lsp-highlight-symbol-at-point nil)
1685 )
1686
1687 (use-package hydra)
1688
1689 (use-package company-lsp
1690 :after company
1691 :config
1692 (setq company-lsp-cache-candidates t
1693 company-lsp-async t))
1694
1695 (use-package lsp-ui
1696 :config
1697 (setq lsp-ui-sideline-update-mode 'point))
1698
1699 (use-package lsp-java
1700 :config
1701 (add-hook 'java-mode-hook
1702 (lambda ()
1703 (setq-local company-backends (list 'company-lsp))))
1704
1705 (add-hook 'java-mode-hook 'lsp-java-enable)
1706 (add-hook 'java-mode-hook 'flycheck-mode)
1707 (add-hook 'java-mode-hook 'company-mode)
1708 (add-hook 'java-mode-hook 'lsp-ui-mode))
1709
1710 (use-package dap-mode
1711 :after lsp-mode
1712 :config
1713 (dap-mode t)
1714 (dap-ui-mode t))
1715
1716 (use-package dap-java
1717 :after (lsp-java))
1718
1719 (use-package lsp-java-treemacs
1720 :after (treemacs))
1721 #+end_src
1722
1723 ** geiser
1724
1725 #+begin_src emacs-lisp
1726 (use-package geiser)
1727
1728 (use-feature geiser-guile
1729 :config
1730 (setq geiser-guile-load-path "~/src/git/guix"))
1731 #+end_src
1732
1733 ** guix
1734
1735 #+begin_src emacs-lisp
1736 (use-package guix)
1737 #+end_src
1738
1739 * Emacs enhancements
1740 :PROPERTIES:
1741 :CUSTOM_ID: emacs-enhancements
1742 :END:
1743
1744 ** man
1745
1746 #+begin_src emacs-lisp
1747 (use-feature man
1748 :config (setq Man-width 80))
1749 #+end_src
1750
1751 ** [[https://github.com/justbur/emacs-which-key][which-key]]
1752
1753 #+begin_quote
1754 Emacs package that displays available keybindings in popup
1755 #+end_quote
1756
1757 #+begin_src emacs-lisp
1758 (use-package which-key
1759 :defer 1
1760 :config
1761 (which-key-add-key-based-replacements
1762 ;; prefixes for global prefixes and minor modes
1763 "C-c @" "outline"
1764 "C-c !" "flycheck"
1765 "C-c 8" "typo"
1766 "C-c 8 -" "typo/dashes"
1767 "C-c 8 <" "typo/left-brackets"
1768 "C-c 8 >" "typo/right-brackets"
1769 "C-x 8" "unicode"
1770 "C-x a" "abbrev/expand"
1771 "C-x r" "rectangle/register/bookmark"
1772 "C-x v" "version control"
1773 ;; prefixes for my personal bindings
1774 "C-c a" "applications"
1775 "C-c a s" "shells"
1776 "C-c P" "package-management"
1777 "C-c P e" "package-management/epkg"
1778 "C-c P s" "package-management/straight.el"
1779 "C-c c" "compile-and-comments"
1780 "C-c e" "eval"
1781 "C-c f" "files"
1782 "C-c F" "frames"
1783 "C-S-h" "help(ful)"
1784 "C-c m" "multiple-cursors"
1785 "C-c p" "projectile"
1786 "C-c p s" "projectile/search"
1787 "C-c p x" "projectile/execute"
1788 "C-c p 4" "projectile/other-window"
1789 "C-c q" "boxquote"
1790 "s-g" "magit"
1791 "s-o" "outline"
1792 "s-t" "themes")
1793
1794 ;; prefixes for major modes
1795 (which-key-add-major-mode-key-based-replacements 'message-mode
1796 "C-c f" "footnote")
1797 (which-key-add-major-mode-key-based-replacements 'org-mode
1798 "C-c C-v" "org-babel")
1799 (which-key-add-major-mode-key-based-replacements 'web-mode
1800 "C-c C-a" "web/attributes"
1801 "C-c C-b" "web/blocks"
1802 "C-c C-d" "web/dom"
1803 "C-c C-e" "web/element"
1804 "C-c C-t" "web/tags")
1805
1806 (which-key-mode))
1807 #+end_src
1808
1809 ** theme
1810
1811 #+begin_src emacs-lisp
1812 (add-to-list 'custom-theme-load-path "~/.emacs.d/lisp")
1813 (load-theme 'tangomod t)
1814 #+end_src
1815
1816 ** smart-mode-line
1817
1818 #+begin_src emacs-lisp
1819 (use-package smart-mode-line
1820 :commands (sml/apply-theme)
1821 :demand
1822 :config
1823 (sml/setup))
1824 #+end_src
1825
1826 ** doom-themes
1827
1828 #+begin_src emacs-lisp
1829 (use-package doom-themes)
1830 #+end_src
1831
1832 ** theme helper functions
1833
1834 #+begin_src emacs-lisp
1835 (defun a/lights-on ()
1836 "Enable my favourite light theme."
1837 (interactive)
1838 (mapc #'disable-theme custom-enabled-themes)
1839 (load-theme 'tangomod t)
1840 (sml/apply-theme 'automatic))
1841
1842 (defun a/lights-off ()
1843 "Go dark."
1844 (interactive)
1845 (mapc #'disable-theme custom-enabled-themes)
1846 (load-theme 'doom-tomorrow-night t)
1847 (sml/apply-theme 'automatic))
1848
1849 (bind-keys
1850 ("s-t d" . a/lights-off)
1851 ("s-t l" . a/lights-on))
1852 #+end_src
1853
1854 ** [[https://github.com/bbatsov/crux][crux]]
1855
1856 #+begin_src emacs-lisp
1857 (use-package crux ; results in Waiting for git... [2 times]
1858 :defer 1
1859 :bind (("C-c b k" . crux-kill-other-buffers)
1860 ("C-c d" . crux-duplicate-current-line-or-region)
1861 ("C-c D" . crux-duplicate-and-comment-current-line-or-region)
1862 ("C-c f c" . crux-copy-file-preserve-attributes)
1863 ("C-c f d" . crux-delete-file-and-buffer)
1864 ("C-c f r" . crux-rename-file-and-buffer)
1865 ("C-c j" . crux-top-join-line)
1866 ("C-S-j" . crux-top-join-line)))
1867 #+end_src
1868
1869 ** [[https://github.com/alezost/mwim.el][mwim]]
1870
1871 #+begin_src emacs-lisp
1872 (use-package mwim
1873 :bind (("C-a" . mwim-beginning-of-code-or-line)
1874 ("C-e" . mwim-end-of-code-or-line)
1875 ("<home>" . mwim-beginning-of-line-or-code)
1876 ("<end>" . mwim-end-of-line-or-code)))
1877 #+end_src
1878
1879 ** projectile
1880
1881 #+begin_src emacs-lisp
1882 (use-package projectile
1883 :bind-keymap ("C-c p" . projectile-command-map)
1884 :config
1885 (projectile-mode)
1886
1887 (defun my-projectile-invalidate-cache (&rest _args)
1888 ;; ignore the args to `magit-checkout'
1889 (projectile-invalidate-cache nil))
1890
1891 (eval-after-load 'magit-branch
1892 '(progn
1893 (advice-add 'magit-checkout
1894 :after #'my-projectile-invalidate-cache)
1895 (advice-add 'magit-branch-and-checkout
1896 :after #'my-projectile-invalidate-cache)))
1897 :custom (projectile-completion-system 'ivy))
1898 #+end_src
1899
1900 ** [[https://github.com/Wilfred/helpful][helpful]]
1901
1902 #+begin_src emacs-lisp
1903 (use-package helpful
1904 :defer 1
1905 :bind
1906 (("C-S-h c" . helpful-command)
1907 ("C-S-h f" . helpful-callable) ; helpful-function
1908 ("C-S-h v" . helpful-variable)
1909 ("C-S-h k" . helpful-key)
1910 ("C-S-h p" . helpful-at-point)))
1911 #+end_src
1912
1913 ** [[https://github.com/EricCrosson/unkillable-scratch][unkillable-scratch]]
1914
1915 Make =*scratch*= and =*Messages*= unkillable.
1916
1917 #+begin_src emacs-lisp
1918 (use-package unkillable-scratch
1919 :defer 3
1920 :config
1921 (unkillable-scratch 1)
1922 :custom
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
1962 Also 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
2280 Convenient 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
2404 Display 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: