[emacs] port over many changes/enhancements from straight-exp
[~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
794 (use-package ox-beamer
795 :demand
796 :after ox)
797 #+end_src
798
799 **** asynchronous tangle
800
801 =a/async-babel-tangle= is a function closely inspired by [[https://github.com/dieggsy/dotfiles/tree/cc10edf7701958eff1cd94d4081da544d882a28c/emacs.d#dotfiles][dieggsy's
802 d/async-babel-tangle]] which uses [[https://github.com/jwiegley/emacs-async][async]] to asynchronously tangle an org
803 file.
804
805 #+begin_src emacs-lisp
806 (with-eval-after-load 'org
807 (defvar a/show-async-tangle-results nil
808 "Keep *emacs* async buffers around for later inspection.")
809
810 (defvar a/show-async-tangle-time nil
811 "Show the time spent tangling the file.")
812
813 (defvar a/async-tangle-post-compile "make ti"
814 "If non-nil, pass to `compile' after successful tangle.")
815
816 (defvar a/async-tangle-byte-recompile nil
817 "If non-nil, byte-recompile the file on successful tangle.")
818
819 (defun a/async-babel-tangle ()
820 "Tangle org file asynchronously."
821 (interactive)
822 (let* ((file-tangle-start-time (current-time))
823 (file (buffer-file-name))
824 (file-nodir (file-name-nondirectory file))
825 ;; (async-quiet-switch "-q")
826 (file-noext (file-name-sans-extension file)))
827 (async-start
828 `(lambda ()
829 (require 'org)
830 (org-babel-tangle-file ,file))
831 (unless a/show-async-tangle-results
832 `(lambda (result)
833 (if result
834 (progn
835 ;; (setq byte-compile-warnings '(not noruntime unresolved))
836 (message "Tangled %s%s"
837 ,file-nodir
838 (if a/show-async-tangle-time
839 (format " (%.3fs)"
840 (float-time (time-subtract (current-time)
841 ',file-tangle-start-time)))
842 ""))
843 (when a/async-tangle-post-compile
844 (compile a/async-tangle-post-compile))
845 (when a/async-tangle-byte-recompile
846 (byte-recompile-file (concat ,file-noext ".el"))))
847 (message "Tangling %s failed" ,file-nodir))))))))
848
849 (add-to-list
850 'safe-local-variable-values
851 '(eval add-hook 'after-save-hook #'a/async-babel-tangle 'append 'local))
852 #+end_src
853
854 *** [[https://magit.vc/][Magit]]
855
856 #+begin_quote
857 It's Magit! A Git porcelain inside Emacs.
858 #+end_quote
859
860 Not just how I do git, but /the/ way to do git.
861
862 #+begin_src emacs-lisp
863 (use-package magit
864 :defer 1
865 :bind (("C-x g" . magit-status)
866 ("s-g s" . magit-status)
867 ("s-g l" . magit-log-buffer-file))
868 :config
869 (magit-add-section-hook 'magit-status-sections-hook
870 'magit-insert-modules
871 'magit-insert-stashes
872 'append)
873 (setq
874 magit-repository-directories '(("~/.emacs.d/" . 0)
875 ("~/src/git/" . 1)))
876 (nconc magit-section-initial-visibility-alist
877 '(([unpulled status] . show)
878 ([unpushed status] . show)))
879 :custom-face (magit-diff-file-heading ((t (:weight normal)))))
880 #+end_src
881
882 *** recentf
883
884 Recently opened files.
885
886 #+begin_src emacs-lisp
887 (use-package recentf
888 :defer 0.5
889 :config
890 (add-to-list 'recentf-exclude "^/\\(?:ssh\\|su\\|sudo\\)?:")
891 (setq recentf-max-saved-items 40))
892 #+end_src
893
894 *** smex
895
896 #+begin_quote
897 A smart M-x enhancement for Emacs.
898 #+end_quote
899
900 Mostly because =counsel= needs it to remember history.
901
902 #+begin_src emacs-lisp
903 (use-package smex)
904 #+end_src
905
906 *** [[https://github.com/abo-abo/swiper][Ivy]] (and friends)
907
908 #+begin_quote
909 Ivy - a generic completion frontend for Emacs, Swiper - isearch with
910 an overview, and more. Oh, man!
911 #+end_quote
912
913 There's no way I could top that, so I won't attempt to.
914
915 **** Ivy
916
917 #+begin_src emacs-lisp
918 (use-package ivy
919 :defer 1
920 :bind
921 (:map ivy-minibuffer-map
922 ([escape] . keyboard-escape-quit)
923 ([S-up] . ivy-previous-history-element)
924 ([S-down] . ivy-next-history-element)
925 ("DEL" . ivy-backward-delete-char))
926 :config
927 (setq ivy-wrap t)
928 (ivy-mode 1)
929 ;; :custom-face
930 ;; (ivy-minibuffer-match-face-2 ((t (:background "#e99ce8" :weight semi-bold))))
931 ;; (ivy-minibuffer-match-face-3 ((t (:background "#bbbbff" :weight semi-bold))))
932 ;; (ivy-minibuffer-match-face-4 ((t (:background "#ffbbff" :weight semi-bold))))
933 )
934 #+end_src
935
936 **** Swiper
937
938 #+begin_src emacs-lisp
939 (use-package swiper
940 :after ivy
941 :bind (("C-s" . swiper)
942 ("C-r" . swiper)
943 ("C-S-s" . isearch-forward)))
944 #+end_src
945
946 **** Counsel
947
948 #+begin_src emacs-lisp
949 (use-package counsel
950 :defer 1
951 :after ivy
952 :bind (([remap execute-extended-command] . counsel-M-x)
953 ([remap find-file] . counsel-find-file)
954 ("s-r" . counsel-recentf)
955 ("C-c x" . counsel-M-x)
956 ("C-c f ." . counsel-find-file)
957 :map minibuffer-local-map
958 ("C-r" . counsel-minibuffer-history))
959 :config
960 (counsel-mode 1)
961 (defalias 'locate #'counsel-locate))
962 #+end_src
963
964 *** eshell
965
966 #+begin_src emacs-lisp
967 (use-package eshell
968 :defer 1
969 :commands eshell
970 :bind ("C-c a s e" . eshell)
971 :config
972 (eval-when-compile (defvar eshell-prompt-regexp))
973 (defun a/eshell-quit-or-delete-char (arg)
974 (interactive "p")
975 (if (and (eolp) (looking-back eshell-prompt-regexp nil))
976 (eshell-life-is-too-much)
977 (delete-char arg)))
978
979 (defun a/eshell-clear ()
980 (interactive)
981 (let ((inhibit-read-only t))
982 (erase-buffer))
983 (eshell-send-input))
984
985 (defun a/eshell-setup ()
986 (make-local-variable 'company-idle-delay)
987 (defvar company-idle-delay)
988 (setq company-idle-delay nil)
989 (bind-keys :map eshell-mode-map
990 ("C-d" . a/eshell-quit-or-delete-char)
991 ("C-S-l" . a/eshell-clear)
992 ("M-r" . counsel-esh-history)
993 ([tab] . company-complete)))
994
995 :hook (eshell-mode . a/eshell-setup)
996 :custom
997 (eshell-hist-ignoredups t)
998 (eshell-input-filter 'eshell-input-filter-initial-space))
999 #+end_src
1000
1001 *** Ibuffer
1002
1003 #+begin_src emacs-lisp
1004 (use-package ibuffer
1005 :bind
1006 (("C-x C-b" . ibuffer-other-window)
1007 :map ibuffer-mode-map
1008 ("P" . ibuffer-backward-filter-group)
1009 ("N" . ibuffer-forward-filter-group)
1010 ("M-p" . ibuffer-do-print)
1011 ("M-n" . ibuffer-do-shell-command-pipe-replace))
1012 :config
1013 ;; Use human readable Size column instead of original one
1014 (define-ibuffer-column size-h
1015 (:name "Size" :inline t)
1016 (cond
1017 ((> (buffer-size) 1000000) (format "%7.1fM" (/ (buffer-size) 1000000.0)))
1018 ((> (buffer-size) 100000) (format "%7.0fk" (/ (buffer-size) 1000.0)))
1019 ((> (buffer-size) 1000) (format "%7.1fk" (/ (buffer-size) 1000.0)))
1020 (t (format "%8d" (buffer-size)))))
1021 :custom
1022 (ibuffer-saved-filter-groups
1023 '(("default"
1024 ("dired" (mode . dired-mode))
1025 ("org" (mode . org-mode))
1026 ("gnus"
1027 (or
1028 (mode . gnus-group-mode)
1029 (mode . gnus-summary-mode)
1030 (mode . gnus-article-mode)
1031 ;; not really, but...
1032 (mode . message-mode)))
1033 ("web"
1034 (or
1035 (mode . web-mode)
1036 (mode . css-mode)
1037 (mode . scss-mode)
1038 (mode . js2-mode)))
1039 ("shell"
1040 (or
1041 (mode . eshell-mode)
1042 (mode . shell-mode)
1043 (mode . term-mode)))
1044 ("programming"
1045 (or
1046 (mode . python-mode)
1047 (mode . c-mode)
1048 (mode . c++-mode)
1049 (mode . emacs-lisp-mode)
1050 (mode . scheme-mode)
1051 (mode . haskell-mode)
1052 (mode . lean-mode)))
1053 ("emacs"
1054 (or
1055 (name . "^\\*scratch\\*$")
1056 (name . "^\\*Messages\\*$"))))))
1057 (ibuffer-formats
1058 '((mark modified read-only locked " "
1059 (name 18 18 :left :elide)
1060 " "
1061 (size-h 9 -1 :right)
1062 " "
1063 (mode 16 16 :left :elide)
1064 " " filename-and-process)
1065 (mark " "
1066 (name 16 -1)
1067 " " filename)))
1068 :hook (ibuffer . (lambda () (ibuffer-switch-to-saved-filter-groups "default"))))
1069 #+end_src
1070
1071 *** Outline
1072
1073 #+begin_src emacs-lisp
1074 (use-package outline
1075 :hook (prog-mode . outline-minor-mode)
1076 :bind
1077 (:map
1078 outline-minor-mode-map
1079 ("<s-tab>" . outline-toggle-children)
1080 ("M-p" . outline-previous-visible-heading)
1081 ("M-n" . outline-next-visible-heading)
1082 :prefix-map a/outline-prefix-map
1083 :prefix "s-o"
1084 ("TAB" . outline-toggle-children)
1085 ("a" . outline-hide-body)
1086 ("H" . outline-hide-body)
1087 ("S" . outline-show-all)
1088 ("h" . outline-hide-subtree)
1089 ("s" . outline-show-subtree)))
1090 #+end_src
1091
1092 *** Dired
1093
1094 #+begin_src emacs-lisp
1095 (use-package ls-lisp
1096 :custom (ls-lisp-dirs-first t))
1097
1098 (use-package dired
1099 :config
1100 (setq dired-listing-switches "-alh"
1101 ls-lisp-use-insert-directory-program nil)
1102
1103 ;; easily diff 2 marked files
1104 ;; https://oremacs.com/2017/03/18/dired-ediff/
1105 (defun dired-ediff-files ()
1106 (interactive)
1107 (require 'dired-aux)
1108 (defvar ediff-after-quit-hook-internal)
1109 (let ((files (dired-get-marked-files))
1110 (wnd (current-window-configuration)))
1111 (if (<= (length files) 2)
1112 (let ((file1 (car files))
1113 (file2 (if (cdr files)
1114 (cadr files)
1115 (read-file-name
1116 "file: "
1117 (dired-dwim-target-directory)))))
1118 (if (file-newer-than-file-p file1 file2)
1119 (ediff-files file2 file1)
1120 (ediff-files file1 file2))
1121 (add-hook 'ediff-after-quit-hook-internal
1122 (lambda ()
1123 (setq ediff-after-quit-hook-internal nil)
1124 (set-window-configuration wnd))))
1125 (error "no more than 2 files should be marked"))))
1126 :bind (:map dired-mode-map
1127 ("b" . dired-up-directory)
1128 ("e" . dired-ediff-files)
1129 ("E" . dired-toggle-read-only)
1130 ("\\" . dired-hide-details-mode)
1131 ("z" . (lambda ()
1132 (interactive)
1133 (a/dired-start-process "zathura"))))
1134 :hook (dired-mode . dired-hide-details-mode))
1135 #+end_src
1136
1137 *** Help
1138
1139 #+begin_src emacs-lisp
1140 (use-package help
1141 :config
1142 (temp-buffer-resize-mode)
1143 (setq help-window-select t))
1144 #+end_src
1145
1146 *** Tramp
1147
1148 #+begin_src emacs-lisp
1149 (use-package tramp
1150 :config
1151 (add-to-list 'tramp-default-proxies-alist '(nil "\\`root\\'" "/ssh:%h:"))
1152 (add-to-list 'tramp-default-proxies-alist '("localhost" nil nil))
1153 (add-to-list 'tramp-default-proxies-alist
1154 (list (regexp-quote (system-name)) nil nil)))
1155 #+end_src
1156
1157 *** Dash
1158
1159 #+begin_src emacs-lisp
1160 (use-package dash
1161 :config (dash-enable-font-lock))
1162 #+end_src
1163
1164 * Editing
1165 :PROPERTIES:
1166 :CUSTOM_ID: editing
1167 :END:
1168
1169 ** =diff-hl=
1170
1171 Highlight uncommitted changes in the left fringe.
1172
1173 #+begin_src emacs-lisp
1174 (use-package diff-hl
1175 :config
1176 (setq diff-hl-draw-borders nil)
1177 (global-diff-hl-mode)
1178 :hook (magit-post-refresh . diff-hl-magit-post-refresh))
1179 #+end_src
1180
1181 ** ElDoc
1182
1183 Display Lisp objects at point in the echo area.
1184
1185 #+begin_src emacs-lisp
1186 (use-package eldoc
1187 :when (version< "25" emacs-version)
1188 :config (global-eldoc-mode))
1189 #+end_src
1190
1191 ** paren
1192
1193 Highlight matching parens.
1194
1195 #+begin_src emacs-lisp
1196 (use-package paren
1197 :demand
1198 :config (show-paren-mode))
1199 #+end_src
1200
1201 ** simple (for column numbers)
1202
1203 #+begin_src emacs-lisp
1204 (use-package simple
1205 :config (column-number-mode))
1206 #+end_src
1207
1208 ** =savehist=
1209
1210 Save minibuffer history.
1211
1212 #+begin_src emacs-lisp
1213 (use-package savehist
1214 :config (savehist-mode))
1215 #+end_src
1216
1217 ** =saveplace=
1218
1219 Automatically save place in each file.
1220
1221 #+begin_src emacs-lisp
1222 (use-package saveplace
1223 :when (version< "25" emacs-version)
1224 :config (save-place-mode))
1225 #+end_src
1226
1227 ** =prog-mode=
1228
1229 #+begin_src emacs-lisp
1230 (use-package prog-mode
1231 :config (global-prettify-symbols-mode)
1232 (defun indicate-buffer-boundaries-left ()
1233 (setq indicate-buffer-boundaries 'left))
1234 (add-hook 'prog-mode-hook #'indicate-buffer-boundaries-left))
1235 #+end_src
1236
1237 ** =text-mode=
1238
1239 #+begin_src emacs-lisp
1240 (use-package text-mode
1241 :hook ((text-mode . indicate-buffer-boundaries-left)
1242 (text-mode . abbrev-mode)))
1243 #+end_src
1244
1245 ** Company
1246
1247 #+begin_src emacs-lisp
1248 (use-package company
1249 :defer 1
1250 :bind
1251 (:map company-active-map
1252 ([tab] . company-complete-common-or-cycle)
1253 ([escape] . company-abort))
1254 :custom
1255 (company-minimum-prefix-length 1)
1256 (company-selection-wrap-around t)
1257 (company-dabbrev-char-regexp "\\sw\\|\\s_\\|[-_]")
1258 (company-dabbrev-downcase nil)
1259 (company-dabbrev-ignore-case nil)
1260 :config
1261 (global-company-mode t))
1262 #+end_src
1263
1264 ** Flycheck
1265
1266 #+begin_src emacs-lisp
1267 (use-package flycheck
1268 :defer 3
1269 :hook (prog-mode . flycheck-mode)
1270 :bind
1271 (:map flycheck-mode-map
1272 ("M-P" . flycheck-previous-error)
1273 ("M-N" . flycheck-next-error))
1274 :config
1275 ;; Use the load-path from running Emacs when checking elisp files
1276 (setq flycheck-emacs-lisp-load-path 'inherit)
1277
1278 ;; Only flycheck when I actually save the buffer
1279 (setq flycheck-check-syntax-automatically '(mode-enabled save)))
1280
1281 ;; http://endlessparentheses.com/ispell-and-apostrophes.html
1282 (use-package ispell
1283 :defer 3
1284 :config
1285 ;; ’ can be part of a word
1286 (setq ispell-local-dictionary-alist
1287 `((nil "[[:alpha:]]" "[^[:alpha:]]"
1288 "['\x2019]" nil ("-B") nil utf-8)))
1289 ;; don't send ’ to the subprocess
1290 (defun endless/replace-apostrophe (args)
1291 (cons (replace-regexp-in-string
1292 "’" "'" (car args))
1293 (cdr args)))
1294 (advice-add #'ispell-send-string :filter-args
1295 #'endless/replace-apostrophe)
1296
1297 ;; convert ' back to ’ from the subprocess
1298 (defun endless/replace-quote (args)
1299 (if (not (derived-mode-p 'org-mode))
1300 args
1301 (cons (replace-regexp-in-string
1302 "'" "’" (car args))
1303 (cdr args))))
1304 (advice-add #'ispell-parse-output :filter-args
1305 #'endless/replace-quote))
1306 #+end_src
1307
1308 * Programming modes
1309 :PROPERTIES:
1310 :CUSTOM_ID: programming-modes
1311 :END:
1312
1313 ** Lisp
1314
1315 #+begin_src emacs-lisp
1316 (use-package lisp-mode
1317 :config
1318 (add-hook 'emacs-lisp-mode-hook 'outline-minor-mode)
1319 (add-hook 'emacs-lisp-mode-hook 'reveal-mode)
1320 (defun indent-spaces-mode ()
1321 (setq indent-tabs-mode nil))
1322 (add-hook 'lisp-interaction-mode-hook #'indent-spaces-mode))
1323 #+end_src
1324
1325 ** [[http://alloytools.org][Alloy]] (with [[https://github.com/dwwmmn/alloy-mode][alloy-mode]])
1326
1327 #+begin_src emacs-lisp
1328 (use-package alloy-mode
1329 :config (setq alloy-basic-offset 2))
1330 #+end_src
1331
1332 ** [[https://coq.inria.fr][Coq]] (with [[https://github.com/ProofGeneral/PG][Proof General]])
1333
1334 #+begin_src emacs-lisp
1335 (use-package proof-site ; Proof General
1336 :load-path "lib/proof-site/generic/")
1337 #+end_src
1338
1339 ** [[https://leanprover.github.io][Lean]] (with [[https://github.com/leanprover/lean-mode][lean-mode]])
1340
1341 #+begin_src emacs-lisp
1342 (eval-when-compile (defvar lean-mode-map))
1343 (use-package lean-mode
1344 :defer 1
1345 :bind (:map lean-mode-map
1346 ("S-SPC" . company-complete))
1347 :config
1348 (require 'lean-input)
1349 (setq default-input-method "Lean"
1350 lean-input-tweak-all '(lean-input-compose
1351 (lean-input-prepend "/")
1352 (lean-input-nonempty))
1353 lean-input-user-translations '(("/" "/")))
1354 (lean-input-setup))
1355 #+end_src
1356
1357 ** Haskell
1358
1359 *** [[https://github.com/haskell/haskell-mode][haskell-mode]]
1360
1361 #+begin_src emacs-lisp
1362 (use-package haskell-mode
1363 :config
1364 (setq haskell-indentation-layout-offset 4
1365 haskell-indentation-left-offset 4
1366 flycheck-checker 'haskell-hlint
1367 flycheck-disabled-checkers '(haskell-stack-ghc haskell-ghc)))
1368 #+end_src
1369
1370 *** [[https://github.com/jyp/dante][dante]]
1371
1372 #+begin_src emacs-lisp
1373 (use-package dante
1374 :after haskell-mode
1375 :commands dante-mode
1376 :hook (haskell-mode . dante-mode))
1377 #+end_src
1378
1379 *** [[https://github.com/mpickering/hlint-refactor-mode][hlint-refactor]]
1380
1381 Emacs bindings for [[https://github.com/ndmitchell/hlint][hlint]]'s refactor option. This requires the refact
1382 executable from [[https://github.com/mpickering/apply-refact][apply-refact]].
1383
1384 #+begin_src emacs-lisp
1385 (use-package hlint-refactor
1386 :after haskell-mode
1387 :bind (:map hlint-refactor-mode-map
1388 ("C-c l b" . hlint-refactor-refactor-buffer)
1389 ("C-c l r" . hlint-refactor-refactor-at-point))
1390 :hook (haskell-mode . hlint-refactor-mode))
1391 #+end_src
1392
1393 *** [[https://github.com/flycheck/flycheck-haskell][flycheck-haskell]]
1394
1395 #+begin_src emacs-lisp
1396 (use-package flycheck-haskell
1397 :after haskell-mode)
1398 #+end_src
1399
1400 *** [[https://github.com/ndmitchell/hlint/blob/20e116a043f2073c57b17b24ae6364b5e433ba7e/data/hs-lint.el][hs-lint.el]]
1401 :PROPERTIES:
1402 :header-args+: :tangle lisp/hs-lint.el :mkdirp yes
1403 :END:
1404
1405 Currently using =flycheck-haskell= with the =haskell-hlint= checker
1406 instead.
1407
1408 #+begin_src emacs-lisp :tangle no
1409 ;;; hs-lint.el --- minor mode for HLint code checking
1410
1411 ;; Copyright 2009 (C) Alex Ott
1412 ;;
1413 ;; Author: Alex Ott <alexott@gmail.com>
1414 ;; Keywords: haskell, lint, HLint
1415 ;; Requirements:
1416 ;; Status: distributed under terms of GPL2 or above
1417
1418 ;; Typical message from HLint looks like:
1419 ;;
1420 ;; /Users/ott/projects/lang-exp/haskell/test.hs:52:1: Eta reduce
1421 ;; Found:
1422 ;; count1 p l = length (filter p l)
1423 ;; Why not:
1424 ;; count1 p = length . filter p
1425
1426
1427 (require 'compile)
1428
1429 (defgroup hs-lint nil
1430 "Run HLint as inferior of Emacs, parse error messages."
1431 :group 'tools
1432 :group 'haskell)
1433
1434 (defcustom hs-lint-command "hlint"
1435 "The default hs-lint command for \\[hlint]."
1436 :type 'string
1437 :group 'hs-lint)
1438
1439 (defcustom hs-lint-save-files t
1440 "Save modified files when run HLint or no (ask user)"
1441 :type 'boolean
1442 :group 'hs-lint)
1443
1444 (defcustom hs-lint-replace-with-suggestions nil
1445 "Replace user's code with suggested replacements"
1446 :type 'boolean
1447 :group 'hs-lint)
1448
1449 (defcustom hs-lint-replace-without-ask nil
1450 "Replace user's code with suggested replacements automatically"
1451 :type 'boolean
1452 :group 'hs-lint)
1453
1454 (defun hs-lint-process-setup ()
1455 "Setup compilation variables and buffer for `hlint'."
1456 (run-hooks 'hs-lint-setup-hook))
1457
1458 ;; regex for replace suggestions
1459 ;;
1460 ;; ^\(.*?\):\([0-9]+\):\([0-9]+\): .*
1461 ;; Found:
1462 ;; \s +\(.*\)
1463 ;; Why not:
1464 ;; \s +\(.*\)
1465
1466 (defvar hs-lint-regex
1467 "^\\(.*?\\):\\([0-9]+\\):\\([0-9]+\\): .*[\n\C-m]Found:[\n\C-m]\\s +\\(.*\\)[\n\C-m]Why not:[\n\C-m]\\s +\\(.*\\)[\n\C-m]"
1468 "Regex for HLint messages")
1469
1470 (defun make-short-string (str maxlen)
1471 (if (< (length str) maxlen)
1472 str
1473 (concat (substring str 0 (- maxlen 3)) "...")))
1474
1475 (defun hs-lint-replace-suggestions ()
1476 "Perform actual replacement of suggestions"
1477 (goto-char (point-min))
1478 (while (re-search-forward hs-lint-regex nil t)
1479 (let* ((fname (match-string 1))
1480 (fline (string-to-number (match-string 2)))
1481 (old-code (match-string 4))
1482 (new-code (match-string 5))
1483 (msg (concat "Replace '" (make-short-string old-code 30)
1484 "' with '" (make-short-string new-code 30) "'"))
1485 (bline 0)
1486 (eline 0)
1487 (spos 0)
1488 (new-old-code ""))
1489 (save-excursion
1490 (switch-to-buffer (get-file-buffer fname))
1491 (goto-char (point-min))
1492 (forward-line (1- fline))
1493 (beginning-of-line)
1494 (setf bline (point))
1495 (when (or hs-lint-replace-without-ask
1496 (yes-or-no-p msg))
1497 (end-of-line)
1498 (setf eline (point))
1499 (beginning-of-line)
1500 (setf old-code (regexp-quote old-code))
1501 (while (string-match "\\\\ " old-code spos)
1502 (setf new-old-code (concat new-old-code
1503 (substring old-code spos (match-beginning 0))
1504 "\\ *"))
1505 (setf spos (match-end 0)))
1506 (setf new-old-code (concat new-old-code (substring old-code spos)))
1507 (remove-text-properties bline eline '(composition nil))
1508 (when (re-search-forward new-old-code eline t)
1509 (replace-match new-code nil t)))))))
1510
1511 (defun hs-lint-finish-hook (buf msg)
1512 "Function, that is executed at the end of HLint execution"
1513 (if hs-lint-replace-with-suggestions
1514 (hs-lint-replace-suggestions)
1515 (next-error 1 t)))
1516
1517 (define-compilation-mode hs-lint-mode "HLint"
1518 "Mode for check Haskell source code."
1519 (set (make-local-variable 'compilation-process-setup-function)
1520 'hs-lint-process-setup)
1521 (set (make-local-variable 'compilation-disable-input) t)
1522 (set (make-local-variable 'compilation-scroll-output) nil)
1523 (set (make-local-variable 'compilation-finish-functions)
1524 (list 'hs-lint-finish-hook))
1525 )
1526
1527 (defun hs-lint ()
1528 "Run HLint for current buffer with haskell source"
1529 (interactive)
1530 (save-some-buffers hs-lint-save-files)
1531 (compilation-start (concat hs-lint-command " \"" buffer-file-name "\"")
1532 'hs-lint-mode))
1533
1534 (provide 'hs-lint)
1535 ;;; hs-lint.el ends here
1536 #+end_src
1537
1538 #+begin_src emacs-lisp :tangle no
1539 (use-package hs-lint
1540 :load-path "lisp/"
1541 :bind (:map haskell-mode-map
1542 ("C-c l l" . hs-lint)))
1543 #+end_src
1544
1545 ** Web
1546
1547 *** SGML and HTML
1548
1549 #+begin_src emacs-lisp
1550 (use-package sgml-mode
1551 :config
1552 (setq sgml-basic-offset 2))
1553 #+end_src
1554
1555 *** CSS and SCSS
1556
1557 #+begin_src emacs-lisp
1558 (use-package css-mode
1559 :config
1560 (setq css-indent-offset 2))
1561 #+end_src
1562
1563 *** Web mode
1564
1565 #+begin_src emacs-lisp
1566 (use-package web-mode
1567 :mode "\\.html\\'"
1568 :config
1569 (a/setq-every 2
1570 web-mode-code-indent-offset
1571 web-mode-css-indent-offset
1572 web-mode-markup-indent-offset))
1573 #+end_src
1574
1575 *** Emmet mode
1576
1577 #+begin_src emacs-lisp
1578 (use-package emmet-mode
1579 :after (:any web-mode css-mode sgml-mode)
1580 :bind* (("C-)" . emmet-next-edit-point)
1581 ("C-(" . emmet-prev-edit-point))
1582 :config
1583 (unbind-key "C-j" emmet-mode-keymap)
1584 (setq emmet-move-cursor-between-quotes t)
1585 :hook (web-mode css-mode html-mode sgml-mode))
1586 #+end_src
1587
1588 ** COMMENT Java
1589
1590 *** meghanada
1591
1592 #+begin_src emacs-lisp
1593 (use-package meghanada
1594 :bind
1595 (:map meghanada-mode-map
1596 (("C-M-o" . meghanada-optimize-import)
1597 ("C-M-t" . meghanada-import-all)))
1598 :hook (java-mode . meghanada-mode))
1599 #+end_src
1600
1601 *** lsp-java
1602
1603 #+begin_comment
1604 dependencies:
1605
1606 ace-window
1607 avy
1608 bui
1609 company-lsp
1610 dap-mode
1611 lsp-java
1612 lsp-mode
1613 lsp-ui
1614 pfuture
1615 tree-mode
1616 treemacs
1617 #+end_comment
1618
1619 #+begin_src emacs-lisp
1620 (use-package treemacs
1621 :config (setq treemacs-never-persist t))
1622
1623 (use-package yasnippet
1624 :config
1625 ;; (yas-global-mode)
1626 )
1627
1628 (use-package lsp-mode
1629 :init (setq lsp-eldoc-render-all nil
1630 lsp-highlight-symbol-at-point nil)
1631 )
1632
1633 (use-package hydra)
1634
1635 (use-package company-lsp
1636 :after company
1637 :config
1638 (setq company-lsp-cache-candidates t
1639 company-lsp-async t))
1640
1641 (use-package lsp-ui
1642 :config
1643 (setq lsp-ui-sideline-update-mode 'point))
1644
1645 (use-package lsp-java
1646 :config
1647 (add-hook 'java-mode-hook
1648 (lambda ()
1649 (setq-local company-backends (list 'company-lsp))))
1650
1651 (add-hook 'java-mode-hook 'lsp-java-enable)
1652 (add-hook 'java-mode-hook 'flycheck-mode)
1653 (add-hook 'java-mode-hook 'company-mode)
1654 (add-hook 'java-mode-hook 'lsp-ui-mode))
1655
1656 (use-package dap-mode
1657 :after lsp-mode
1658 :config
1659 (dap-mode t)
1660 (dap-ui-mode t))
1661
1662 (use-package dap-java
1663 :after (lsp-java))
1664
1665 (use-package lsp-java-treemacs
1666 :after (treemacs))
1667 #+end_src
1668
1669 ** geiser
1670
1671 #+begin_src emacs-lisp
1672 (use-package geiser)
1673
1674 (use-package geiser-guile
1675 :config
1676 (setq geiser-guile-load-path "~/src/git/guix"))
1677 #+end_src
1678
1679 ** guix
1680
1681 #+begin_src emacs-lisp
1682 (use-package guix
1683 :load-path "lib/guix/elisp")
1684 #+end_src
1685
1686 * Emacs enhancements
1687 :PROPERTIES:
1688 :CUSTOM_ID: emacs-enhancements
1689 :END:
1690
1691 ** man
1692
1693 #+begin_src emacs-lisp
1694 (use-package man
1695 :config (setq Man-width 80))
1696 #+end_src
1697
1698 ** [[https://github.com/justbur/emacs-which-key][which-key]]
1699
1700 #+begin_quote
1701 Emacs package that displays available keybindings in popup
1702 #+end_quote
1703
1704 #+begin_src emacs-lisp
1705 (use-package which-key
1706 :defer 1
1707 :config
1708 (which-key-add-key-based-replacements
1709 ;; prefixes for global prefixes and minor modes
1710 "C-c @" "outline"
1711 "C-c !" "flycheck"
1712 "C-c 8" "typo"
1713 "C-c 8 -" "typo/dashes"
1714 "C-c 8 <" "typo/left-brackets"
1715 "C-c 8 >" "typo/right-brackets"
1716 "C-x 8" "unicode"
1717 "C-x a" "abbrev/expand"
1718 "C-x r" "rectangle/register/bookmark"
1719 "C-x v" "version control"
1720 ;; prefixes for my personal bindings
1721 "C-c a" "applications"
1722 "C-c a s" "shells"
1723 "C-c b" "borg"
1724 "C-c c" "compile-and-comments"
1725 "C-c e" "eval"
1726 "C-c f" "files"
1727 "C-c F" "frames"
1728 "C-S-h" "help(ful)"
1729 "C-c m" "multiple-cursors"
1730 "C-c p" "projectile"
1731 "C-c p s" "projectile/search"
1732 "C-c p x" "projectile/execute"
1733 "C-c p 4" "projectile/other-window"
1734 "C-c q" "boxquote"
1735 "s-g" "magit"
1736 "s-o" "outline"
1737 "s-t" "themes")
1738
1739 ;; prefixes for major modes
1740 (which-key-add-major-mode-key-based-replacements 'message-mode
1741 "C-c f" "footnote")
1742 (which-key-add-major-mode-key-based-replacements 'org-mode
1743 "C-c C-v" "org-babel")
1744 (which-key-add-major-mode-key-based-replacements 'web-mode
1745 "C-c C-a" "web/attributes"
1746 "C-c C-b" "web/blocks"
1747 "C-c C-d" "web/dom"
1748 "C-c C-e" "web/element"
1749 "C-c C-t" "web/tags")
1750
1751 (which-key-mode))
1752 #+end_src
1753
1754 ** theme
1755
1756 #+begin_src emacs-lisp
1757 (add-to-list 'custom-theme-load-path "~/.emacs.d/lisp")
1758 (load-theme 'tangomod t)
1759 #+end_src
1760
1761 ** smart-mode-line
1762
1763 #+begin_src emacs-lisp
1764 (use-package smart-mode-line
1765 :demand
1766 :config
1767 (sml/setup))
1768 #+end_src
1769
1770 ** doom-themes
1771
1772 #+begin_src emacs-lisp
1773 (use-package doom-themes)
1774 #+end_src
1775
1776 ** theme helper functions
1777
1778 #+begin_src emacs-lisp
1779 (defun a/lights-on ()
1780 "Enable my favourite light theme."
1781 (interactive)
1782 (mapc #'disable-theme custom-enabled-themes)
1783 (load-theme 'tangomod t)
1784 (sml/apply-theme 'automatic))
1785
1786 (defun a/lights-off ()
1787 "Go dark."
1788 (interactive)
1789 (mapc #'disable-theme custom-enabled-themes)
1790 (load-theme 'doom-tomorrow-night t)
1791 (sml/apply-theme 'automatic))
1792
1793 (bind-keys
1794 ("s-t d" . a/lights-off)
1795 ("s-t l" . a/lights-on))
1796 #+end_src
1797
1798 ** [[https://github.com/bbatsov/crux][crux]]
1799
1800 #+begin_src emacs-lisp
1801 (use-package crux
1802 :defer 1
1803 :bind (("C-c b k" . crux-kill-other-buffers)
1804 ("C-c d" . crux-duplicate-current-line-or-region)
1805 ("C-c D" . crux-duplicate-and-comment-current-line-or-region)
1806 ("C-c f c" . crux-copy-file-preserve-attributes)
1807 ("C-c f d" . crux-delete-file-and-buffer)
1808 ("C-c f r" . crux-rename-file-and-buffer)
1809 ("C-c j" . crux-top-join-line)
1810 ("C-S-j" . crux-top-join-line)))
1811 #+end_src
1812
1813 ** [[https://github.com/alezost/mwim.el][mwim]]
1814
1815 #+begin_src emacs-lisp
1816 (use-package mwim
1817 :bind (("C-a" . mwim-beginning-of-code-or-line)
1818 ("C-e" . mwim-end-of-code-or-line)
1819 ("<home>" . mwim-beginning-of-line-or-code)
1820 ("<end>" . mwim-end-of-line-or-code)))
1821 #+end_src
1822
1823 ** projectile
1824
1825 #+begin_src emacs-lisp
1826 (use-package projectile
1827 :bind-keymap ("C-c p" . projectile-command-map)
1828 :config
1829 (projectile-mode)
1830
1831 (defun my-projectile-invalidate-cache (&rest _args)
1832 ;; ignore the args to `magit-checkout'
1833 (projectile-invalidate-cache nil))
1834
1835 (eval-after-load 'magit-branch
1836 '(progn
1837 (advice-add 'magit-checkout
1838 :after #'my-projectile-invalidate-cache)
1839 (advice-add 'magit-branch-and-checkout
1840 :after #'my-projectile-invalidate-cache)))
1841 :custom (projectile-completion-system 'ivy))
1842 #+end_src
1843
1844 ** [[https://github.com/Wilfred/helpful][helpful]]
1845
1846 #+begin_src emacs-lisp
1847 (use-package helpful
1848 :defer 1
1849 :bind
1850 (("C-S-h c" . helpful-command)
1851 ("C-S-h f" . helpful-callable) ; helpful-function
1852 ("C-S-h v" . helpful-variable)
1853 ("C-S-h k" . helpful-key)
1854 ("C-S-h p" . helpful-at-point)))
1855 #+end_src
1856
1857 ** [[https://github.com/EricCrosson/unkillable-scratch][unkillable-scratch]]
1858
1859 Make =*scratch*= and =*Messages*= unkillable.
1860
1861 #+begin_src emacs-lisp
1862 (use-package unkillable-scratch
1863 :defer 3
1864 :config
1865 (unkillable-scratch 1)
1866 :custom
1867 (unkillable-scratch-behavior 'do-nothing)
1868 (unkillable-buffers '("^\\*scratch\\*$" "^\\*Messages\\*$")))
1869 #+end_src
1870
1871 ** [[https://github.com/davep/boxquote.el][boxquote.el]]
1872
1873 #+begin_example
1874 ,----
1875 | make pretty boxed quotes like this
1876 `----
1877 #+end_example
1878
1879 #+begin_src emacs-lisp
1880 (use-package boxquote
1881 :defer 3
1882 :bind
1883 (:prefix-map a/boxquote-prefix-map
1884 :prefix "C-c q"
1885 ("b" . boxquote-buffer)
1886 ("B" . boxquote-insert-buffer)
1887 ("d" . boxquote-defun)
1888 ("F" . boxquote-insert-file)
1889 ("hf" . boxquote-describe-function)
1890 ("hk" . boxquote-describe-key)
1891 ("hv" . boxquote-describe-variable)
1892 ("hw" . boxquote-where-is)
1893 ("k" . boxquote-kill)
1894 ("p" . boxquote-paragraph)
1895 ("q" . boxquote-boxquote)
1896 ("r" . boxquote-region)
1897 ("s" . boxquote-shell-command)
1898 ("t" . boxquote-text)
1899 ("T" . boxquote-title)
1900 ("u" . boxquote-unbox)
1901 ("U" . boxquote-unbox-region)
1902 ("y" . boxquote-yank)
1903 ("M-q" . boxquote-fill-paragraph)
1904 ("M-w" . boxquote-kill-ring-save)))
1905 #+end_src
1906
1907 Also see [[https://www.emacswiki.org/emacs/rebox2][rebox2]].
1908
1909 ** orgalist
1910
1911 #+begin_src emacs-lisp
1912 (use-package orgalist
1913 :after message
1914 :hook (message-mode . orgalist-mode))
1915 #+end_src
1916
1917 ** typo.el
1918
1919 #+begin_src emacs-lisp
1920 (use-package typo
1921 :defer 2
1922 :config
1923 (typo-global-mode 1)
1924 :hook (text-mode . typo-mode))
1925 #+end_src
1926
1927 ** hl-todo
1928
1929 #+begin_src emacs-lisp
1930 (use-package hl-todo
1931 :defer 4
1932 :config
1933 (global-hl-todo-mode))
1934 #+end_src
1935
1936 ** shrink-path
1937
1938 #+begin_src emacs-lisp
1939 (use-package shrink-path
1940 :defer 2
1941 :after eshell
1942 :config
1943 (defun +eshell/prompt ()
1944 (let ((base/dir (shrink-path-prompt default-directory)))
1945 (concat (propertize (car base/dir)
1946 'face 'font-lock-comment-face)
1947 (propertize (cdr base/dir)
1948 'face 'font-lock-constant-face)
1949 (propertize (+eshell--current-git-branch)
1950 'face 'font-lock-function-name-face)
1951 "\n"
1952 (propertize "λ" 'face 'eshell-prompt-face)
1953 ;; needed for the input text to not have prompt face
1954 (propertize " " 'face 'default))))
1955
1956 (defun +eshell--current-git-branch ()
1957 (let ((branch (car (loop for match in (split-string (shell-command-to-string "git branch") "\n")
1958 when (string-match "^\*" match)
1959 collect match))))
1960 (if (not (eq branch nil))
1961 (concat " " (substring branch 2))
1962 "")))
1963 (setq eshell-prompt-regexp "\\(.*\n\\)*λ "
1964 eshell-prompt-function #'+eshell/prompt))
1965 #+end_src
1966
1967 ** [[https://github.com/peterwvj/eshell-up][eshell-up]]
1968
1969 #+begin_src emacs-lisp
1970 (use-package eshell-up
1971 :after eshell
1972 :commands eshell-up)
1973 #+end_src
1974
1975 ** multi-term
1976
1977 #+begin_src emacs-lisp
1978 (use-package multi-term
1979 :defer 1
1980 :bind (("C-c a s m" . multi-term-dedicated-toggle)
1981 :map term-mode-map
1982 ("C-c C-j" . term-char-mode)
1983 :map term-raw-map
1984 ("C-c C-j" . term-line-mode))
1985 :config
1986 (setq multi-term-program "/bin/screen"
1987 ;; TODO: add separate bindings for connecting to existing
1988 ;; session vs. always creating a new one
1989 multi-term-dedicated-select-after-open-p t
1990 multi-term-dedicated-window-height 20
1991 multi-term-dedicated-max-window-height 30
1992 term-bind-key-alist
1993 '(("C-c C-c" . term-interrupt-subjob)
1994 ("C-c C-e" . term-send-esc)
1995 ("C-k" . kill-line)
1996 ("C-y" . term-paste)
1997 ("M-f" . term-send-forward-word)
1998 ("M-b" . term-send-backward-word)
1999 ("M-p" . term-send-up)
2000 ("M-n" . term-send-down)
2001 ("<C-backspace>" . term-send-backward-kill-word)
2002 ("<M-DEL>" . term-send-backward-kill-word)
2003 ("M-d" . term-send-delete-word)
2004 ("M-," . term-send-raw)
2005 ("M-." . comint-dynamic-complete))
2006 term-unbind-key-alist
2007 '("C-z" "C-x" "C-c" "C-h" "C-y" "<ESC>")))
2008 #+end_src
2009
2010 ** page-break-lines
2011
2012 #+begin_src emacs-lisp
2013 (use-package page-break-lines
2014 :config
2015 (global-page-break-lines-mode))
2016 #+end_src
2017
2018 ** expand-region
2019
2020 #+begin_src emacs-lisp
2021 (use-package expand-region
2022 :bind ("C-=" . er/expand-region))
2023 #+end_src
2024
2025 ** multiple-cursors
2026
2027 #+begin_src emacs-lisp
2028 (use-package multiple-cursors
2029 :bind
2030 (("C-S-<mouse-1>" . mc/add-cursor-on-click)
2031 (:prefix-map a/mc-prefix-map
2032 :prefix "C-c m"
2033 ("c" . mc/edit-lines)
2034 ("n" . mc/mark-next-like-this)
2035 ("p" . mc/mark-previous-like-this)
2036 ("a" . mc/mark-all-like-this))))
2037 #+end_src
2038
2039 * Email
2040 :PROPERTIES:
2041 :CUSTOM_ID: email
2042 :END:
2043
2044 #+begin_src emacs-lisp
2045 (defvar a/maildir (expand-file-name "~/mail/"))
2046 (with-eval-after-load 'recentf
2047 (add-to-list 'recentf-exclude a/maildir))
2048 #+end_src
2049
2050 ** Gnus
2051
2052 #+begin_src emacs-lisp
2053 (setq
2054 a/gnus-init-file (no-littering-expand-etc-file-name "gnus")
2055 mail-user-agent 'gnus-user-agent
2056 read-mail-command 'gnus)
2057
2058 (use-package gnus
2059 :bind (("s-m" . gnus)
2060 ("s-M" . gnus-unplugged))
2061 :init
2062 (setq
2063 gnus-select-method '(nnnil "")
2064 gnus-secondary-select-methods
2065 '((nnimap "amin"
2066 (nnimap-stream plain)
2067 (nnimap-address "127.0.0.1")
2068 (nnimap-server-port 143)
2069 (nnimap-authenticator plain)
2070 (nnimap-user "amin@aminb.org"))
2071 (nnimap "uwaterloo"
2072 (nnimap-stream plain)
2073 (nnimap-address "127.0.0.1")
2074 (nnimap-server-port 143)
2075 (nnimap-authenticator plain)
2076 (nnimap-user "abandali@uwaterloo.ca")))
2077 gnus-message-archive-group "nnimap+amin:Sent"
2078 gnus-parameters
2079 '(("gnu.*"
2080 (gcc-self . t)))
2081 gnus-large-newsgroup 50
2082 gnus-home-directory (no-littering-expand-var-file-name "gnus/")
2083 gnus-directory (concat gnus-home-directory "news/")
2084 message-directory (concat gnus-home-directory "mail/")
2085 nndraft-directory (concat gnus-home-directory "drafts/")
2086 gnus-save-newsrc-file nil
2087 gnus-read-newsrc-file nil
2088 gnus-interactive-exit nil
2089 gnus-gcc-mark-as-read t))
2090
2091 (use-package gnus-art
2092 :config
2093 (setq
2094 gnus-visible-headers
2095 (concat gnus-visible-headers "\\|^List-Id:\\|^X-RT-Originator:\\|^User-Agent:")
2096 gnus-sorted-header-list
2097 '("^From:" "^Subject:" "^Summary:" "^Keywords:"
2098 "^Followup-To:" "^To:" "^Cc:" "X-RT-Originator"
2099 "^Newsgroups:" "List-Id:" "^Organization:"
2100 "^User-Agent:" "^Date:")
2101 ;; local-lapsed article dates
2102 ;; from https://www.emacswiki.org/emacs/GnusFormatting#toc11
2103 gnus-article-date-headers '(user-defined)
2104 gnus-article-time-format
2105 (lambda (time)
2106 (let* ((date (format-time-string "%a, %d %b %Y %T %z" time))
2107 (local (article-make-date-line date 'local))
2108 (combined-lapsed (article-make-date-line date
2109 'combined-lapsed))
2110 (lapsed (progn
2111 (string-match " (.+" combined-lapsed)
2112 (match-string 0 combined-lapsed))))
2113 (concat local lapsed))))
2114 (bind-keys
2115 :map gnus-article-mode-map
2116 ("r" . gnus-article-reply-with-original)
2117 ("R" . gnus-article-wide-reply-with-original)
2118 ("M-L" . org-store-link)))
2119
2120 (use-package gnus-sum
2121 :bind (:map gnus-summary-mode-map
2122 :prefix-map a/gnus-summary-prefix-map
2123 :prefix "v"
2124 ("r" . gnus-summary-reply)
2125 ("w" . gnus-summary-wide-reply)
2126 ("v" . gnus-summary-show-raw-article))
2127 :config
2128 (bind-keys
2129 :map gnus-summary-mode-map
2130 ("r" . gnus-summary-reply-with-original)
2131 ("R" . gnus-summary-wide-reply-with-original)
2132 ("M-L" . org-store-link))
2133 :hook (gnus-summary-mode . a/no-mouse-autoselect-window))
2134
2135 (use-package gnus-msg
2136 :config
2137 (setq gnus-posting-styles
2138 '((".*"
2139 (address "amin@aminb.org")
2140 (body "\nBest,\namin\n")
2141 (eval (setq a/message-cite-say-hi t)))
2142 ("gnu.*"
2143 (address "bandali@gnu.org"))
2144 ((header "subject" "ThankCRM")
2145 (to "webmasters-comment@gnu.org")
2146 (body "\nAdded to 2018supporters.html.\n\nMoving to campaigns.\n\n-amin\n")
2147 (eval (setq a/message-cite-say-hi nil)))
2148 ("nnimap\\+uwaterloo:.*"
2149 (address "abandali@uwaterloo.ca")
2150 (gcc "\"nnimap+uwaterloo:Sent Items\"")))))
2151
2152 (use-package gnus-topic
2153 :hook (gnus-group-mode . gnus-topic-mode))
2154
2155 (use-package gnus-agent
2156 :config
2157 (setq gnus-agent-synchronize-flags 'ask)
2158 :hook (gnus-group-mode . gnus-agent-mode))
2159
2160 (use-package gnus-group
2161 :config
2162 (setq gnus-permanently-visible-groups "\\((INBOX\\|gnu$\\)"))
2163
2164 (use-package mm-decode
2165 :config
2166 (setq mm-discouraged-alternatives '("text/html" "text/richtext")))
2167 #+end_src
2168
2169 ** sendmail
2170
2171 #+begin_src emacs-lisp
2172 (use-package sendmail
2173 :config
2174 (setq sendmail-program "/usr/bin/msmtp"
2175 ;; message-sendmail-extra-arguments '("-v" "-d")
2176 mail-specify-envelope-from t
2177 mail-envelope-from 'header))
2178 #+end_src
2179
2180 ** message
2181
2182 #+begin_src emacs-lisp
2183 (use-package message
2184 :config
2185 (defconst a/message-cite-style-format "On %Y-%m-%d %l:%M %p, %N wrote:")
2186 (defconst message-cite-style-bandali
2187 '((message-cite-function 'message-cite-original)
2188 (message-citation-line-function 'message-insert-formatted-citation-line)
2189 (message-cite-reply-position 'traditional)
2190 (message-yank-prefix "> ")
2191 (message-yank-cited-prefix ">")
2192 (message-yank-empty-prefix ">")
2193 (message-citation-line-format
2194 (if a/message-cite-say-hi
2195 (concat "Hi %F,\n\n" a/message-cite-style-format)
2196 a/message-cite-style-format)))
2197 "Citation style based on Mozilla Thunderbird's. Use with message-cite-style.")
2198 (setq message-cite-style 'message-cite-style-bandali
2199 message-kill-buffer-on-exit t
2200 message-send-mail-function 'message-send-mail-with-sendmail
2201 message-sendmail-envelope-from 'header
2202 message-dont-reply-to-names
2203 "\\(\\(.*@aminb\\.org\\)\\|\\(amin@bandali\\.me\\)\\|\\(\\(aminb?\\|mab\\|bandali\\)@gnu\\.org\\)\\|\\(\\(m\\|a\\(min\\.\\)?\\)bandali@uwaterloo\\.ca\\)\\)"
2204 message-user-fqdn "aminb.org")
2205 :hook (;; (message-setup . mml-secure-message-sign-pgpmime)
2206 (message-mode . flyspell-mode)
2207 (message-mode . (lambda ()
2208 ;; (setq fill-column 65
2209 ;; message-fill-column 65)
2210 (make-local-variable 'company-idle-delay)
2211 (setq company-idle-delay 0.2))))
2212 ;; :custom-face
2213 ;; (message-header-subject ((t (:foreground "#111" :weight semi-bold))))
2214 ;; (message-header-to ((t (:foreground "#111" :weight normal))))
2215 ;; (message-header-cc ((t (:foreground "#333" :weight normal))))
2216 )
2217
2218 (with-eval-after-load 'mml-sec
2219 (setq mml-secure-openpgp-encrypt-to-self t
2220 mml-secure-openpgp-sign-with-sender t))
2221 #+end_src
2222
2223 ** footnote
2224
2225 Convenient footnotes in =message-mode=.
2226
2227 #+begin_src emacs-lisp
2228 (use-package footnote
2229 :after message
2230 :bind
2231 (:map message-mode-map
2232 :prefix-map a/footnote-prefix-map
2233 :prefix "C-c f"
2234 ("a" . footnote-add-footnote)
2235 ("b" . footnote-back-to-message)
2236 ("c" . footnote-cycle-style)
2237 ("d" . footnote-delete-footnote)
2238 ("g" . footnote-goto-footnote)
2239 ("r" . footnote-renumber-footnotes)
2240 ("s" . footnote-set-style))
2241 :config
2242 (setq footnote-start-tag ""
2243 footnote-end-tag ""
2244 footnote-style 'unicode))
2245 #+end_src
2246
2247 ** ebdb
2248
2249 #+begin_src emacs-lisp
2250 (use-package ebdb
2251 :after gnus
2252 :bind (:map gnus-group-mode-map ("e" . ebdb))
2253 :config
2254 (setq ebdb-sources (no-littering-expand-var-file-name "ebdb"))
2255 (with-eval-after-load 'swiper
2256 (add-to-list 'swiper-font-lock-exclude 'ebdb-mode t)))
2257
2258 (use-package ebdb-com
2259 :after ebdb)
2260
2261 ;; (use-package ebdb-complete
2262 ;; :after ebdb
2263 ;; :config
2264 ;; (ebdb-complete-enable))
2265
2266 (use-package company-ebdb
2267 :after (:all company message)
2268 :config
2269 (defun company-ebdb--post-complete (_) nil)
2270 :hook
2271 (message-mode . (lambda ()
2272 (add-to-list (make-local-variable 'company-backends)
2273 'company-ebdb))))
2274
2275 (use-package ebdb-gnus
2276 :demand
2277 :after ebdb
2278 :custom
2279 (ebdb-gnus-window-configuration
2280 '(article
2281 (vertical 1.0
2282 (summary 0.25 point)
2283 (horizontal 1.0
2284 (article 1.0)
2285 (ebdb-gnus 0.3))))))
2286
2287 (use-package ebdb-mua
2288 :demand
2289 :after ebdb
2290 ;; :custom (ebdb-mua-pop-up nil)
2291 )
2292
2293 ;; (use-package ebdb-message
2294 ;; :after ebdb)
2295
2296
2297 ;; (use-package ebdb-vcard
2298 ;; :after ebdb)
2299 #+end_src
2300
2301 ** COMMENT message-x
2302
2303 #+begin_src emacs-lisp
2304 (use-package message-x
2305 :custom
2306 (message-x-completion-alist
2307 (quote
2308 (("\\([rR]esent-\\|[rR]eply-\\)?[tT]o:\\|[bB]?[cC][cC]:" . gnus-harvest-find-address)
2309 ((if
2310 (boundp
2311 (quote message-newgroups-header-regexp))
2312 message-newgroups-header-regexp message-newsgroups-header-regexp)
2313 . message-expand-group)))))
2314 #+end_src
2315
2316 ** COMMENT gnus-harvest
2317
2318 #+begin_src emacs-lisp
2319 (use-package gnus-harvest
2320 :commands gnus-harvest-install
2321 :demand t
2322 :config
2323 (if (featurep 'message-x)
2324 (gnus-harvest-install 'message-x)
2325 (gnus-harvest-install)))
2326 #+end_src
2327
2328 * Blogging
2329 :PROPERTIES:
2330 :CUSTOM_ID: blogging
2331 :END:
2332
2333 ** [[https://ox-hugo.scripter.co][ox-hugo]]
2334
2335 #+begin_src emacs-lisp
2336 (use-package ox-hugo
2337 :after ox)
2338
2339 (use-package ox-hugo-auto-export
2340 :load-path "lib/ox-hugo")
2341 #+end_src
2342
2343 * Post initialization
2344 :PROPERTIES:
2345 :CUSTOM_ID: post-initialization
2346 :END:
2347
2348 Display how long it took to load the init file.
2349
2350 #+begin_src emacs-lisp
2351 (message "Loading %s...done (%.3fs)" user-init-file
2352 (float-time (time-subtract (current-time)
2353 a/before-user-init-time)))
2354 #+end_src
2355
2356 * Footer
2357 :PROPERTIES:
2358 :CUSTOM_ID: footer
2359 :END:
2360
2361 #+begin_src emacs-lisp :comments none
2362 ;;; init.el ends here
2363 #+end_src
2364
2365 * COMMENT Local Variables :ARCHIVE:
2366 # Local Variables:
2367 # eval: (add-hook 'after-save-hook #'a/async-babel-tangle 'append 'local)
2368 # eval: (when (featurep 'typo (typo-mode -1)))
2369 # End: