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