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