Now on git.acdw.net https://git.acdw.net/emacs
Go to file
Ashley Duckworth 91b56c70a0 Fork and correct cl dependency in font-utils and list-utils
See config.org for details.
2021-01-23 12:51:22 -06:00
etc/eshell Unignore etc/eshell and add alias 2020-11-27 15:28:10 -06:00
var Add bookmark: SMOG 2020-12-21 22:50:10 -06:00
.gitignore Change README.md to README.org 2021-01-19 23:43:19 -06:00
LICENSE Change indentation 2021-01-12 18:34:17 -06:00
README.org Change README.md to README.org 2021-01-19 23:43:19 -06:00
config.org Fork and correct cl dependency in font-utils and list-utils 2021-01-23 12:51:22 -06:00
early-init.el Add Forge 2021-01-23 12:48:44 -06:00
init.el Finally sort out the config.org/el loading thing 2021-01-22 13:07:59 -06:00

README.org

Emacs configuration, literate-style

About me

  (setq user-full-name "Case Duckworth"
        user-mail-address "acdw@acdw.net")

Where I am

  (setq calendar-location-name "Baton Rouge, LA"
        calendar-latitude 30.4
        calendar-longitude -91.1)

Auth-sources

Here feels like as good a place as any to setup auth-sources. Yes, I need to use GPG. I'll get around to it. Until then, please don't break into my workplace and steal my secrets.

  (setq-default auth-sources '("~/.authinfo"))

Look and feel

Frames

Initial frame setup

I tangle this section to early-init.el, since that's evaluated before GUI set-up. Which, in turn, means Emacs will skip the "flash of unstyled content" thing.

Tool bar
  (add-to-list 'default-frame-alist
               '(tool-bar-lines . 0))

  (tool-bar-mode -1)
Menu bar
  (add-to-list 'default-frame-alist
               '(menu-bar-lines . 0))

  (menu-bar-mode -1)
Scroll bars
  (add-to-list 'default-frame-alist
               '(vertical-scroll-bars . nil)
               '(horizontal-scroll-bars . nil))

  (scroll-bar-mode -1)
  (horizontal-scroll-bar-mode -1)
Resizing

I don't want the frame to resize when I change fonts and stuff, and I want it to resize by pixels we are using a GUI, after all.

  (setq-default frame-inhibit-implied-resize t
                frame-resize-pixelwise t)

Frame titles

  (setq-default frame-title-format
                (concat invocation-name "@" (system-name)
                        ": %b %+%+ %f"))

Fringes

I have grown to love Emacs's little fringes on the side of the windows. In fact, I love them so much that I really went overboard and have made a custom fringe bitmap.

Indicate empty lines after the end of the buffer
  (setq-default indicate-empty-lines t)
Indicate the boundaries of the buffer
  (setq-default indicate-buffer-boundaries 'right)
Indicate continuation lines, but only on the left fringe
  (setq-default visual-line-fringe-indicators '(left-curly-arrow nil))
Customize fringe bitmaps
Curly arrows (continuation lines)
  (define-fringe-bitmap 'left-curly-arrow
    [#b11000000
     #b01100000
     #b00110000
     #b00011000])

  (define-fringe-bitmap 'right-curly-arrow
    [#b00011000
     #b00110000
     #b01100000
     #b11000000])
Arrows (truncation lines)
  (define-fringe-bitmap 'left-arrow
    [#b00000000
     #b01010100
     #b01010100
     #b00000000])

  (define-fringe-bitmap 'right-arrow
    [#b00000000
     #b00101010
     #b00101010
     #b00000000])

Windows

Switch to other window or buffer

I grabbed this from u/astoff1: it extends the other-window command to switch to the other buffer if there's only one window in the frame.

  (defun other-window-or-buffer ()
    "Switch to other window, or the previous buffer."
    (interactive)
    (if (eq (count-windows) 1)
        (switch-to-buffer nil)
      (other-window 1)))

And I'll bind it to M-o, since that's easier to reach than C-x o.

  (define-key acdw/map (kbd "M-o") #'other-window-or-buffer)

Buffers

Uniquify buffers

The default way Emacs makes buffer names unique is really ugly and, dare I say it, stupid. Instead, I want them to be uniquified by their filesystem paths.

  (require 'uniquify)
  (setq-default uniquify-buffer-name-style 'forward)

Startup buffers

When Emacs starts up, I want a blank slate: the scratch buffer. I also want it to show a cute little message to myself.

  (setq-default inhibit-startup-screen t ; Don't show that splash screen thing.
                initial-buffer-choice t  ; Start on *scratch*
                initial-scratch-message
                (concat ";; Howdy, "
                        (nth 0 (split-string user-full-name)) "!\n"
                        ";; Welcome to Emacs."
                        "\n\n"))

Immortal *scratch* buffer

I don't want to accidentally kill the scratch buffer. So, I add a function to the kill-buffer-query-functions hook that will return nil if the buffer is scratch.

  (defun immortal-scratch ()
    (if (not (eq (current-buffer) (get-buffer "*scratch*")))
        t
      (bury-buffer)
      nil))
  (add-hook 'kill-buffer-query-functions #'immortal-scratch)

Kill buffers better

  (defun kill-a-buffer (&optional prefix)
    "Kill a buffer and its window, prompting only on unsaved changes.

  `kill-a-buffer' uses the PREFIX argument to determine which buffer(s) to kill:
  0            => Kill current buffer & window
  4 (C-u)      => Kill OTHER buffer & window
  16 (C-u C-u) => Run `kill-buffer' without a prefix arg."
    (interactive "P")
    (pcase (or (car prefix) 0)
      (0 (kill-current-buffer)
         (unless (one-window-p) (delete-window)))
      (4 (other-window 1)
         (kill-current-buffer)
         (unless (one-window-p) (delete-window)))
      (16 (let ((current-prefix-arg nil))
            (kill-buffer)))))
  (define-key acdw/map (kbd "C-x k") #'kill-a-buffer)

Cursor

Cursor shape

I like a vertical bar, but only in the selected window.

  (setq-default cursor-type 'bar
                cursor-in-non-selected-windows nil)

Don't blink the cursor

  (blink-cursor-mode -1)

Tabs

Tab names

  (setq-default tab-bar-tab-name-function
                #'tab-bar-tab-name-current-with-count)

When to show the tab bar

Only when there's more than one tab.

  (setq-default tab-bar-show 1)

Fonts

On Linux, I have a custom build of Iosevka that I like.

  (set-face-attribute 'default nil
                      :family "Iosevka Acdw"
                      :height 105)

  (set-face-attribute 'fixed-pitch nil
                      :family "Iosevka Acdw"
                      :height 105)

But on Windows, I use Consolas.

  (set-face-attribute 'default nil
                      :family "Consolas"
                      :height 110)

  (set-face-attribute 'fixed-pitch nil
                      :family "Consolas"
                      :height 110)

Underlines

I like the fancy underlines in newer browsers that skip all the descenders. Emacs doesn't quite have that, but it can put the underline below all the text.

  (setq-default x-underline-at-descent-line t)

Unicode fonts   package

unicode-fonts pulls in some other packages that still require the deprecated cl library. So, I've forked those libraries to require cl-lib instead.

First: un-fuck font-utils and list-utils

List-utils first

Since font-utils depends on list-utils, if I load the former first, it pulls in the unpatched latter. So I need to do list-utils first. (*straight-process* is your friend, y'all!)

Since list-utils requires cl in line 259 (see this issue, apparently just changing it breaks many tests, but I'll run with it until Emacs complains), I need to fork and change that to a cl-lib.

  (straight-use-package '(list-utils
                          :host github
                          :repo "rolandwalker/list-utils"
                          :fork (:repo "duckwork/list-utils")))
Font-utils

I was able to actually create a PR for this one, so fingers crossed. Since the last update on font-utils was in 2015, I'm not super hopeful that my fix will get merged upstream, but I'm using a :fork argument to stay hopeful.

  (straight-use-package '(font-utils
                          :host github
                          :repo "rolandwalker/font-utils"
                          :fork (:repo "duckwork/font-utils")))
Unicode-fonts

Okay … pull requests in, time to load unicode-fonts.

  (straight-use-package '(unicode-fonts
                          :host github
                          :repo "rolandwalker/unicode-fonts"))
  (require 'unicode-fonts)

According to Issue #3, there can be problems with unicode-fonts-setup when using a daemon. Instead of forking this repo and merging PR #4 into my personal fork, I'll use the workaround described in the issue. Trying to un-fuck these packages by Roland Walker have been enough work today.

  (defun hook--unicode-fonts-setup (frame)
    "Run `unicode-fonts-setup', then remove the hook."
    (select-frame frame)
    (unicode-fonts-setup)
    (remove-hook 'after-make-frame-functions #'hook--unicode-fonts-setup))

  (add-hook 'after-make-frame-functions #'hook--unicode-fonts-setup)

Theming

Modus themes   package

  (straight-use-package 'modus-themes)
  (setq-default modus-themes-slanted-constructs t
                modus-themes-bold-constructs t
                modus-themes-region 'bg-only
                modus-themes-org-blocks 'grayscale
                modus-themes-headings '((1 . line)
                                        (t . t))
                modus-themes-scale-headings nil)

Change themes based on time of day

  (defun acdw/run-with-sun (sunrise-command sunset-command)
    "Run commands at sunrise and sunset."
    (let* ((times-regex (rx (* nonl)
                            (: (any ?s ?S) "unrise") " "
                            (group (repeat 1 2 digit) ":"
                                   (repeat 1 2 digit)
                                   (: (any ?a ?A ?p ?P) (any ?m ?M)))
                            (* nonl)
                            (: (any ?s ?S) "unset") " "
                            (group (repeat 1 2 digit) ":"
                                   (repeat 1 2 digit)
                                   (: (any ?a ?A ?p ?P) (any ?m ?M)))
                            (* nonl)))
           (ss (sunrise-sunset))
           (_m (string-match times-regex ss))
           (sunrise-time (match-string 1 ss))
           (sunset-time (match-string 2 ss)))
      (run-at-time sunrise-time (* 60 60 24) sunrise-command)
      (run-at-time sunset-time (* 60 60 24) sunset-command)))
  (acdw/run-with-sun #'modus-themes-load-operandi
                     #'modus-themes-load-vivendi)

Mode line

Simple modeline   package

After trying doom-mode-line and smart-mode-line, I think I've finally landed on a good one: simple-modeline.

  (straight-use-package 'simple-modeline)
  (setq-default simple-modeline-segments
                '((simple-modeline-segment-modified
                   simple-modeline-segment-buffer-name
                   simple-modeline-segment-position)
                  (simple-modeline-segment-minor-modes
                   simple-modeline-segment-input-method
                   simple-modeline-segment-vc
                   simple-modeline-segment-misc-info
                   simple-modeline-segment-process
                   simple-modeline-segment-major-mode)))
  (simple-modeline-mode +1)
Blackout some modes   package

Like diminish or delight, blackout allows me to remove some minor-modes from the modeline.

  (straight-use-package '(blackout
                          :host github
                          :repo "raxod502/blackout"))

Interactivity

Dialogs and alerts

Don't use a dialog box

Ask in the modeline instead.

  (setq-default use-dialog-box nil)

Yes or no questions

I just want to type y or n, okay?

  (fset 'yes-or-no-p #'y-or-n-p)

The Bell

The only system I sort of like the bell on is my Thinkpad, which does a little on-board speaker beep. Until I can figure out how to let it do its thing, though, I'll just change the bell on all my systems.

  (setq-default visible-bell nil
                ring-bell-function #'flash-mode-line)
Flash the mode-line
  (defun flash-mode-line ()
    (invert-face 'mode-line)
    (run-with-timer 0.2 nil #'invert-face 'mode-line))

Minibuffer

Keep the cursor away from the minibuffer prompt

  (setq-default minibuffer-prompt-properties
                '(read-only t
                  cursor-intangible t
                  face minibuffer-prompt))

Enable a recursive minibuffer

  (setq-default enable-recursive-minibuffers t)

Show the recursivity of the minibuffer in the mode-line

  (minibuffer-depth-indicate-mode +1)

Completing-read

Shadow file names

When typing ~ or / in the file-selection dialog, Emacs "pretends" that you've typed them at the beginning of the line. By default, however, it only fades out the previous contents of the line. I want to hide those contents.

  (setq-default file-name-shadow-properties '(invisible t))
  (file-name-shadow-mode +1)

Ignore case

  (setq-default completion-ignore-case t
                read-buffer-completion-ignore-case t
                read-file-name-completion-ignore-case t)

Selectrum   package

My minibuffer completion system uses selectrum, prescient, company, and marginalia. At some point, I'd like to take a deep dive in embark (possibly switching out selectrum), ido, orderless, or others, for now …. I just want to see my completions.

  (straight-use-package 'selectrum)
  (selectrum-mode +1)

Prescient

  (straight-use-package 'prescient)
  (require 'prescient)

Prescient can persist itself too.

  (prescient-persist-mode +1)

Let's have prescient and selectrum work together.

  (straight-use-package 'selectrum-prescient)
  (with-eval-after-load 'selectrum
    (selectrum-prescient-mode +1))

Consult

  (straight-use-package 'consult)
  (require 'consult)

Consult has a lot of great bindings that work well with Emacs's default completion system. These all come from the example configuration.

  (with-eval-after-load 'consult
    ;; C-c bindings (`mode-specific-map')
    (define-key acdw/map (kbd "C-c h") #'consult-history)
    (define-key acdw/map (kbd "C-c m") #'consult-mode-command)
    ;; C-x bindings (`ctl-x-map')
    (define-key acdw/map (kbd "C-x M-:") #'consult-complex-command)
    (define-key acdw/map (kbd "C-x b") #'consult-buffer)
    (define-key acdw/map (kbd "C-x 4 b") #'consult-buffer-other-window)
    (define-key acdw/map (kbd "C-x 5 b") #'consult-buffer-other-frame)
    (define-key acdw/map (kbd "C-x r x") #'consult-register)
    (define-key acdw/map (kbd "C-x r b") #'consult-bookmark)
    ;; M-g bindings (`goto-map')
    (define-key acdw/map (kbd "M-g g") #'consult-line)
    (define-key acdw/map (kbd "M-g M-g") #'consult-line)
    (define-key acdw/map (kbd "M-g o") #'consult-outline)
    (define-key acdw/map (kbd "M-g m") #'consult-mark)
    (define-key acdw/map (kbd "M-g k") #'consult-global-mark)
    (define-key acdw/map (kbd "M-g i") #'consult-imenu)
    (define-key acdw/map (kbd "M-g e") #'consult-error)
    ;; M-s bindings (`search-map')
    (define-key acdw/map (kbd "M-s g") #'consult-grep) ; alts:
                                          ; consult-git-grep,
                                          ; consult-ripgrep
    (define-key acdw/map (kbd "M-s f") #'consult-find) ; alts:
                                          ; consult-locate
    (define-key acdw/map (kbd "M-s l") #'consult-line)
    (define-key acdw/map (kbd "M-s m") #'consult-multi-occur)
    (define-key acdw/map (kbd "M-s k") #'consult-keep-lines)
    (define-key acdw/map (kbd "M-s u") #'consult-focus-lines)
    ;; Other bindings
    (define-key acdw/map (kbd "M-y") #'consult-yank-pop)
    (define-key acdw/map (kbd "<help> a") #'consult-apropos))
  (autoload 'consult-register-preview "consult") ; make the compiler happy
  (setq-default register-preview-delay 0
                register-preview-function #'consult-register-preview)

Marginalia

Finally, marginalia provides extra information about completion candidates.

  (straight-use-package 'marginalia)
  (require 'marginalia)
  (marginalia-mode +1)

I like the rich annotations provided by marginalia.

  (setq-default marginalia-annotators
                '(marginalia-annotators-heavy
                  marginalia-annotators-light
                  nil))
Integration with Selectrum
  (advice-add #'marginalia-cycle :after
              (lambda ()
                (when (bound-and-true-p selectrum-mode)
                  (selectrum-exhibit))))

Completion

Hippie Expand

Before I install any completion framework, I want a good default for completing. hippie-expand fills that niche.

  (define-key acdw/map (kbd "M-/") #'hippie-expand)

Bindings

Acdw Mode

I've decided to set up a custom minor mode for my keybindings, as suggested in Lars Tvei's config, so that I can override all other modes with my own keybindings. Plus I can easily turn it off and back on as I please.

  (defvar acdw/map (make-sparse-keymap)
    "A keymap for my custom bindings.")

  (define-minor-mode acdw/mode
      "A mode for `acdw/map'."
    :init-value t
    :lighter " ⱷ"
    :keymap acdw/map)

  (define-globalized-minor-mode acdw/global-mode acdw/mode acdw/mode)
Turn off acdw/mode in the minibuffer
  (defun acdw/mode--disable ()
    "Turn off acdw/mode."
    (acdw/mode -1))

  (add-hook 'minibuffer-setup-hook #'acdw/mode--disable)
Custom leader

Since C-z is generally pretty useless in Emacs (minimize the window? really?), I rebind it to be a sort of personal leader key. I generally use it as a leader for entering applications.

  (defvar acdw/leader
    (let ((map (make-sparse-keymap))
          (c-z (global-key-binding "\C-z")))
      ;(global-unset-key "\C-z")
      (define-key acdw/map "\C-z" map)
      (define-key map "\C-z" c-z)
      map))

  ;; Just in case I want to run hooks after defining the leader map
  (run-hooks 'acdw/leader-defined-hook)

Show keybindings with which-key   package

  (straight-use-package 'which-key)
  (which-key-mode +1)
  (blackout 'which-key-mode)

Scrolling

According to Murilo Pereira, these settings will make Emacs scrolling "an order of magnitude faster."

  (setq-default auto-window-vscroll nil
                fast-but-imprecise-scrolling t)

Persistence

Minibuffer history

The savehist package saves minibuffer history between sessions, as well as the option for some other variables. Since storage is cheap, I keep all of it.

  (require 'savehist)
  (setq-default savehist-additional-variables
                '(kill-ring
                  search-ring
                  regexp-search-ring)
                history-length t ; Don't truncate
                history-delete-duplicates t)
  (savehist-mode +1)

File places

The saveplace package saves where I've been in my visited files.

  (require 'saveplace)

Since storage is cheap, but I'm impatient especially on Windows I'm not going to check whether the files save-place saves the places of are readable or not when I'm not at home.

  (setq-default save-place-forget-unreadable-files
                (memq system-type '(gnu gnu/linux gnu/kfreebsd)))
  (save-place-mode +1)

Recent files

I also like to keep track of recently-opened files. recentf helps with that.

  (require 'recentf)
  (setq-default recentf-max-menu-items 100
                recentf-max-saved-items nil)
  (recentf-mode +1)

I also want to ignore the no-littering-var-directory and no-littering-etc-directory, since those aren't useful.

  (add-to-list 'recentf-exclude no-littering-var-directory)
  (add-to-list 'recentf-exclude no-littering-etc-directory)

Save the recentf list periodically

  (defun maybe-save-recentf ()
    "Save `recentf-file' every five minutes, but only when out of focus."
    (defvar recentf--last-save (time-convert nil 'integer)
      "When we last saved the `recentf-save-list'.")

    (when (> (time-convert (time-since recentf--last-save) 'integer)
             (* 60 5))
      (setq-default recentf--last-save (time-convert nil 'integer))
      (when-unfocused #'recentf-save-list)))
  (add-function :after after-focus-change-function
                #'maybe-save-recentf)

Responsiveness

Emacs has a slew of well-documented problems with snappiness. Luckily, there are a number of solutions.

Only do things when unfocused

Sometimes, we can fake responsiveness by only performing commands when the user is looking at something else.

  (defun when-unfocused (func &rest args)
    "Run FUNC, with ARGS, iff all frames are out of focus."
    (when (seq-every-p #'null (mapcar #'frame-focus-state (frame-list)))
      (apply func args)))

Garbage collection

Garbage Collection Magic Hack   package

Look, I'm not going to look too deeply into this. It's magic afer all.

  (straight-use-package 'gcmh)
  (gcmh-mode +1)
  (blackout 'gcmh-mode)

Garbage Collect when out of focus

  (defun hook--gc-when-unfocused ()
    (when-unfocused #'garbage-collect))

  (add-function :after after-focus-change-function
                #'hook--gc-when-unfocused)

Files

Encoding

UTF-8

It's 2020. Let's encode files like it is.

  (prefer-coding-system 'utf-8)
  (set-default-coding-systems 'utf-8)
  (set-terminal-coding-system 'utf-8)
  (set-keyboard-coding-system 'utf-8)
  (set-language-environment "UTF-8")

  (setq-default locale-coding-system 'utf-8
                buffer-file-coding-system 'utf-8
                x-select-request-type '(UTF8_STRING COMPOUND_TEXT TEXT STRING))

UNIX-style line endings

This function is from the Emacs Wiki.

  (defun ewiki/no-junk-please-were-unixish ()
    "Convert line endings to UNIX, dammit."
    (let ((coding-str (symbol-name buffer-file-coding-system)))
      (when (string-match "-\\(?:dos\\|mac\\)$" coding-str)
        (set-buffer-file-coding-system 'unix))))

I add it to both file-find-hook and before-save-hook because I'm that over it. I don't want to ever work with anything other than UNIX line endings ever again. I just don't care. Even Microsoft Notepad can handle UNIX line endings, so I don't want to hear it.

  (add-hook 'find-file-hook #'ewiki/no-junk-please-were-unixish)
  (add-hook 'before-save-hook #'ewiki/no-junk-please-were-unixish)

Keep ~/.emacs.d clean   package

  (straight-use-package 'no-littering)
  (require 'no-littering)
  (with-eval-after-load 'no-littering
    <<no-littering>>
    ) ; end of no-littering

Backups

  (setq-default backup-by-copying t
                ;; Don't delete old versions
                delete-old-versions -1
                ;; Make numeric backups unconditionally
                version-control t
                ;; Also backup files covered by version control
                vc-make-backup-files t)
  (let ((dir (no-littering-expand-var-file-name "backup")))
    (make-directory dir :parents)
    (setq-default backup-directory-alist
                  `((".*" . ,dir))))

Autosaves   package

I don't use the auto-save system, preferring instead to use Bozhidar Batsov's super-save package.

  (setq-default auto-save-default nil)

  (setq-default super-save-remote-files nil
                super-save-exclude '(".gpg")
                super-save-auto-save-when-idle t)
  (straight-use-package 'super-save)
  (super-save-mode +1)
  (blackout 'super-save-mode)

Auto-revert files

I like to keep the buffers Emacs has in-memory in sync with the actual contents of the files the represent on-disk. Thus, we have auto-revert-mode.

  (setq-default auto-revert-verbose nil)
  (global-auto-revert-mode +1)

Editing

Lines

Auto-fill vs. Visual-line

I've mostly been using visual-line mode, and it's been pretty good. There are some times, however, when lines are just … really long, and they wrap weird or whatever. Not to mention, in Org mode, visual-line-mode screws up the bindings for line movement. So here's what I'm going to do.

  1. Enable visual-line-mode with text-mode, but not with org-mode.

      (defun hook--visual-line-mode ()
        (unless (eq major-mode 'org-mode)
          (visual-line-mode +1)))
    
      (add-hook 'text-mode-hook #'hook--visual-line-mode)
  2. Enable auto-fill-mode with org-mode.

      (add-hook 'org-mode-hook #'auto-fill-mode)
  3. Just in case … let's "fix" visual-line-mode if we're in org-mode.

      (defun hook--visual-line-fix-org-keys ()
        (when (derived-mode-p 'org-mode)
          (local-set-key (kbd "C-a") #'org-beginning-of-line)
          (local-set-key (kbd "C-e") #'org-end-of-line)
          (local-set-key (kbd "C-k") #'org-kill-line)))
    
      (add-hook 'visual-line-mode-hook #'hook--visual-line-fix-org-keys)

I think that'll work I only care about line aesthetics with text. Programming modes should be allowed to have long lines, regardless of how terrible it is to have them.

Stay snappy with long-lined files

  (when (fboundp 'global-so-long-mode)
    (global-so-long-mode +1))

Whitespace

Whitespace style

The whitespace-style defines what kinds of whitespace to clean up on whitespace-cleanup, as well as what to highlight (if that option is enabled).

  (setq-default whitespace-style '(empty ; remove blank lines at buffer edges
                                   indentation ; clean up indentation
                                   ;; fix mixed spaces and tabs
                                   space-before-tab
                                   space-after-tab))

Clean up whitespace on save

  (add-hook 'before-save-hook #'whitespace-cleanup)

Don't use TABs

I was team TAB for a while, but I find them easier to avoid in Emacs. It manages my whitespace for me, anyway.

  (setq-default indent-tabs-mode nil)

Killing & Yanking

Replace the selection when typing

  (delete-selection-mode +1)

Work better with the system clipboard

  (setq-default
   ;; Save existing clipboard text to the kill ring before replacing it.
   save-interprogram-paste-before-kill t
   ;; Update the X selection when rotating the kill ring.
   yank-pop-change-selection t
   ;; Enable clipboards
   x-select-enable-clipboard t
   x-select-enable-primary t
   ;; Copy a region when it's selected with the mouse
   mouse-drag-copy-region t)

Don't append the same thing twice to the kill ring

  (setq-default kill-do-not-save-duplicates t)

Overwrite mode

Change the cursor

  (defun hook--overwrite-mode-change-cursor ()
    (setq cursor-type (if overwrite-mode t 'bar)))

  (add-hook 'overwrite-mode-hook #'hook--overwrite-mode-change-cursor)

The Mark

Repeat popping the mark without repeating the prefix argument

  (setq-default set-mark-repeat-command-pop t)

Undo   package

Undo Fu

  (straight-use-package 'undo-fu)
  (define-key acdw/map (kbd "C-/") #'undo-fu-only-undo)
  (define-key acdw/map (kbd "C-?") #'undo-fu-only-redo)

Undo Fu session

I'm not putting this in /acdw/emacs/src/branch/again/*Persistence because it'd be confusing away from undo-fu.

  (straight-use-package 'undo-fu-session)
  (setq-default undo-fu-session-incompatible-files
                '("/COMMIT_EDITMSG\\'"
                  "/git-rebase-todo\\'"))
  (let ((dir (no-littering-expand-var-file-name "undos")))
    (make-directory dir :parents)
    (setq-default undo-fu-session-directory dir))
  (global-undo-fu-session-mode +1)

Search/Replace

The biggest thing I miss about my Neovim days is its ease of search/replace. It didn't matter where the point was in the buffer; it could wrap around. It had a little highlight to show you all the matching strings, and it could show you what the replacement would look like. anzu does most of this, except the wrapping around part ctrlf does the wrapping around okay, but I haven't really tried to get the two packages to play nice together. Until then, I'll just use anzu and isearch, which is honestly a pretty good search package.

Regex

I search with regex by default.

  (setq-default
   ;; Search Regex by default
   search-default-mode t)

I've switched query-replace and query-replace-regexp with their anzu versions, because of the regex thing.

  (define-key acdw/map [remap query-replace] #'anzu-query-replace-regexp)
  (define-key acdw/map [remap query-replace-regexp] #'anzu-query-replace)

  (define-key isearch-mode-map [remap isearch-query-replace]
    #'anzu-isearch-query-replace)
  (define-key isearch-mode-map [remap isearch-query-replace-regexp]
    #'anzu-isearch-query-replace-regexp)

Anzu setup   package

  (straight-use-package 'anzu)
  (setq-default anzu-mode-lighter "" ; hide anzu-mode in the modeline
                anzu-replace-to-string-separator " → ")

  ;; Set up anzu in the modeline
  (setq-default anzu-cons-mode-line-p nil)
  (setcar (cdr (assq 'isearch-mode minor-mode-alist))
          '(:eval (concat " " (anzu--update-mode-line))))

Writing

Word count   package

  (straight-use-package 'wc-mode)
  (add-hook 'text-mode-hook #'wc-mode)

Programming

Comments

Auto fill comments in programming modes

Okay, so I lied in the Auto-fill vs. Visual-line section. I do want to auto-fill in programming modes, but only the comments.

  (defun hook--comment-auto-fill ()
    (setq-local comment-auto-fill-only-comments t)
    (auto-fill-mode +1))

  (add-hook 'prog-mode-hook #'hook--comment-auto-fill)

Parentheses

Show parentheses

  (show-paren-mode +1)
  (setq-default show-paren-delay 0
                ;; Show the matching paren if visible, else the whole expression
                show-paren-style 'mixed)

Smart parens   package

  (straight-use-package 'smartparens)
  (require 'smartparens-config)
Show parens
  (setq-default sp-show-pair-delay 0
                sp-show-pair-from-inside t)
  (add-hook 'prog-mode-hook #'show-smartparens-mode)
Hide the smartparens lighter
  (blackout 'smartparens-mode)
Be strict in prog-mode
  (add-hook 'prog-mode-hook #'smartparens-strict-mode)

Formatting

Aggressive indent   package

  (straight-use-package 'aggressive-indent)
  (global-aggressive-indent-mode +1)
  (blackout 'aggressive-indent-mode)

Typesetting

Prettify-mode

I like my pretty lambda's and maybe one day, I'll add more symbols, but only in prog-mode. I want to see what I'm actually typing in text.

  (add-hook 'prog-mode-hook #'prettify-symbols-mode)

Of course, I want to be able to see the actual text in the buffer if I'm in the symbols.

  (setq-default prettify-symbols-unprettify-at-point 'right-edge)

Executable scripts

This poorly-named function will make a file executable if it looks like a script (looking at the function definition, it looks like it checks for a shebang).

  (add-hook 'after-save-hook
            #'executable-make-buffer-file-executable-if-script-p)

Language-specific

Emacs Lisp

Don't limit the length of evaluated expressions
  (setq-default eval-expression-print-length nil
                eval-expression-print-level nil)
Indent Elisp like Common Lisp
  (require 'cl-lib)
  (setq-default lisp-indent-function #'common-lisp-indent-function)
  (put 'cl-flet 'common-lisp-indent-function
       (get 'flet 'common-lisp-indent-function))
  (put 'cl-labels 'common-lisp-indent-function
       (get 'labels 'common-lisp-indent-function))
  (put 'if 'common-lisp-indent-function 2)
  (put 'dotimes-protect 'common-lisp-indent-function
       (get 'when 'common-lisp-indent-function))

Applications

Emacs is well-known for its ability to subsume one's entire computing life. There are a few killer apps that make Emacs really shine. Here, I configure them and a few others.

My rubric for what makes a package an application, versus just a package, is mostly based on the way I feel about it. Don't expect to agree with all of my choices.

Web browsing

Browse-url

I like using Firefox.

  (setq-default browse-url-browser-function 'browse-url-firefox
                browse-url-new-window-flag t
                browse-url-firefox-new-window-is-tab t)

At work, I need to tell Emacs where Firefox is.

  (add-to-list 'exec-path "C:/Program Files/Mozilla Firefox")

Dired

  (defun hook--dired-mode ()
    (hl-line-mode +1)
    (dired-hide-details-mode +1))

  (add-hook 'dired-mode-hook #'hook--dired-mode)

A note on dired-listing-switches: when I'm able to figure out how to move up a directory with a keybinding, I'll change -a to -A.

  (setq-default dired-recursive-copies 'always
                dired-recursive-deletes 'always
                delete-by-moving-to-trash t
                dired-listing-switches "-alh")

Expand subtrees   package

Instead of opening each folder in its own buffer, dired-subtree enables me to open them in the same buffer, fancily indented.

  (straight-use-package 'dired-subtree)
  (with-eval-after-load 'dired
    (define-key dired-mode-map "i" #'dired-subtree-toggle))

Collapse singleton directories   package

If a directory only has one item in it, dired-collapse shows what that one item is.

  (straight-use-package 'dired-collapse)
  (add-hook 'dired-mode-hook #'dired-collapse-mode)

Git   package

Magit

  (straight-use-package 'magit)
  (define-key acdw/leader "g" #'magit-status)
Windows setup

Following the wiki page located here. Also make sure to run the following in cmd.exe to set $HOME correctly:

  setx HOME C:\Users\aduckworth\Downloads\acdw

and run this command to setup a git credential helper:

  git config --global credential.helper wincred
  (setenv "GIT_ASKPASS" "git-gui--askpass")
Forge
  (straight-use-package 'forge)
  (with-eval-after-load 'magit
    (require 'forge))

Git file modes

  (dolist (feat '(gitattributes-mode
                  gitconfig-mode
                  gitignore-mode))
    (straight-use-package feat)
    (require feat))

Org mode   package

Install it with straight.el

I want to use the newest version of Org that I can.

  (straight-use-package 'org)

  (with-eval-after-load 'org
    (require 'org-tempo)
    (require 'ox-md))

Basic settings

  (setq-default
   ;; Where to look for Org files
   org-directory "~/org" ; this is the default
   ;; Fontify stuff
   org-hide-emphasis-markers t
   org-fontify-whole-heading-line t
   org-fontify-done-headline t
   org-fontify-quote-and-verse-blocks t
   org-src-fontify-natively t
   org-ellipsis "…"
   org-pretty-entities t
   ;; Source blocks
   org-src-tab-acts-natively t
   org-src-window-setup 'split-window-below ; could change this based on geom
   org-confirm-babel-evaluate nil
   ;; Behavior
   org-adapt-indentation nil ; don't indent things
   org-catch-invisible-edits 'smart ; let's try this
   org-special-ctrl-a/e t
   org-special-ctrl-k t
   ;; Exporting
   org-export-headline-levels 8
   org-export-with-smart-quotes t)

Aesthetics

Prettify some other symbols

  (defun acdw/org-mode-prettify ()
    "Prettify `org-mode'."
    (dolist (cell '(("[ ]" . ?☐) ("[X]" . ?☑) ("[-]" . ?◐)
                    ("#+BEGIN_SRC" . ?✎) ("#+begin_src" . ?✎)
                    ("#+END_SRC" . ?■) ("#+end_src" . ?■)))
      (add-to-list 'prettify-symbols-alist cell :append))
    (prettify-symbols-mode +1))
  (add-hook 'org-mode-hook #'acdw/org-mode-prettify)

Org templates

  (with-eval-after-load 'org-tempo
    (dolist (cell '(("el" . "src emacs-lisp")
                    ("cr" . "src emacs-lisp :noweb-ref requires")
                    ("cf" . "src emacs-lisp :noweb-ref functions")
                    ("cs" . "src emacs-lisp :noweb-ref settings")
                    ("cm" . "src emacs-lisp :noweb-ref modes")
                    ("cl" . "src emacs-lisp :noweb-ref linux-specific")
                    ("cw" . "src emacs-lisp :noweb-ref windows-specific")
                    ("cp" . "src emacs-lisp :noweb-ref packages")
                    ("ch" . "src emacs-lisp :noweb-ref hooks")
                    ("cb" . "src emacs-lisp :noweb-ref bindings")
                    ("cnl" . "src emacs-lisp :noweb-ref no-littering")))
      (add-to-list 'org-structure-template-alist cell)))

Org Return: DWIM   unpackaged

  (defun unpackaged/org-element-descendant-of (type element)
    "Return non-nil if ELEMENT is a descendant of TYPE.
  TYPE should be an element type, like `item' or `paragraph'.
  ELEMENT should be a list like that returned by `org-element-context'."
    ;; MAYBE: Use `org-element-lineage'.
    (when-let* ((parent (org-element-property :parent element)))
      (or (eq type (car parent))
          (unpackaged/org-element-descendant-of type parent))))

  (defun unpackaged/org-return-dwim (&optional default)
    "A helpful replacement for `org-return'.  With prefix, call `org-return'.

  On headings, move point to position after entry content.  In
  lists, insert a new item or end the list, with checkbox if
  appropriate.  In tables, insert a new row or end the table."
    ;; Inspired by John Kitchin: http://kitchingroup.cheme.cmu.edu/blog/2017/04/09/A-better-return-in-org-mode/
    (interactive "P")
    (if default
        (org-return)
      (cond
        ;; Act depending on context around point.

        ;; NOTE: I prefer RET to not follow links, but by uncommenting this block, links will be
        ;; followed.

        ;; ((eq 'link (car (org-element-context)))
        ;;  ;; Link: Open it.
        ;;  (org-open-at-point-global))

        ((org-at-heading-p)
         ;; Heading: Move to position after entry content.
         ;; NOTE: This is probably the most interesting feature of this function.
         (let ((heading-start (org-entry-beginning-position)))
           (goto-char (org-entry-end-position))
           (cond ((and (org-at-heading-p)
                       (= heading-start (org-entry-beginning-position)))
                  ;; Entry ends on its heading; add newline after
                  (end-of-line)
                  (insert "\n\n"))
                 (t
                  ;; Entry ends after its heading; back up
                  (forward-line -1)
                  (end-of-line)
                  (when (org-at-heading-p)
                    ;; At the same heading
                    (forward-line)
                    (insert "\n")
                    (forward-line -1))
                  ;; FIXME: looking-back is supposed to be called with more arguments.
                  (while (not (looking-back (rx (repeat 3 (seq (optional blank) "\n"))) nil))
                    (insert "\n"))
                  (forward-line -1)))))

        ((org-at-item-checkbox-p)
         ;; Checkbox: Insert new item with checkbox.
         (org-insert-todo-heading nil))

        ((org-in-item-p)
         ;; Plain list.  Yes, this gets a little complicated...
         (let ((context (org-element-context)))
           (if (or (eq 'plain-list (car context))  ; First item in list
                   (and (eq 'item (car context))
                        (not (eq (org-element-property :contents-begin context)
                                 (org-element-property :contents-end context))))
                   (unpackaged/org-element-descendant-of 'item context))  ; Element in list item, e.g. a link
               ;; Non-empty item: Add new item.
               (org-insert-item)
             ;; Empty item: Close the list.
             ;; TODO: Do this with org functions rather than operating on the text. Can't seem to find the right function.
             (delete-region (line-beginning-position) (line-end-position))
             (insert "\n"))))

        ((when (fboundp 'org-inlinetask-in-task-p)
           (org-inlinetask-in-task-p))
         ;; Inline task: Don't insert a new heading.
         (org-return))

        ((org-at-table-p)
         (cond ((save-excursion
                  (beginning-of-line)
                  ;; See `org-table-next-field'.
                  (cl-loop with end = (line-end-position)
                     for cell = (org-element-table-cell-parser)
                     always (equal (org-element-property :contents-begin cell)
                                   (org-element-property :contents-end cell))
                     while (re-search-forward "|" end t)))
                ;; Empty row: end the table.
                (delete-region (line-beginning-position) (line-end-position))
                (org-return))
               (t
                ;; Non-empty row: call `org-return'.
                (org-return))))
        (t
         ;; All other cases: call `org-return'.
         (org-return)))))
  (with-eval-after-load 'org
    (define-key org-mode-map (kbd "RET") #'unpackaged/org-return-dwim))

Insert blank lines around headers   unpackaged

  (defun unpackaged/org-fix-blank-lines (&optional prefix)
    "Ensure that blank lines exist between headings and between headings and their contents.
  With prefix, operate on whole buffer. Ensures that blank lines
  exist after each headings's drawers."
    (interactive "P")
    (org-map-entries (lambda ()
                       (org-with-wide-buffer
                        ;; `org-map-entries' narrows the buffer, which prevents us
                        ;; from seeing newlines before the current heading, so we
                        ;; do this part widened.
                        (while (not (looking-back "\n\n" nil))
                          ;; Insert blank lines before heading.
                          (insert "\n")))
                       (let ((end (org-entry-end-position)))
                         ;; Insert blank lines before entry content
                         (forward-line)
                         (while (and (org-at-planning-p)
                                     (< (point) (point-max)))
                           ;; Skip planning lines
                           (forward-line))
                         (while (re-search-forward org-drawer-regexp end t)
                           ;; Skip drawers. You might think that `org-at-drawer-p'
                           ;; would suffice, but for some reason it doesn't work
                           ;; correctly when operating on hidden text.  This
                           ;; works, taken from `org-agenda-get-some-entry-text'.
                           (re-search-forward "^[ \t]*:END:.*\n?" end t)
                           (goto-char (match-end 0)))
                         (unless (or (= (point) (point-max))
                                     (org-at-heading-p)
                                     (looking-at-p "\n"))
                           (insert "\n"))))
                     t (if prefix
                           nil
                         'tree)))

I fix the headline spacing every time I save.

  (defun hook--org-mode-fix-blank-lines ()
    (when (eq major-mode 'org-mode)
      (let ((current-prefix-arg 4)) ; Emulate C-u
        (call-interactively 'unpackaged/org-fix-blank-lines))))

  (add-hook 'before-save-hook #'hook--org-mode-fix-blank-lines)

Package management   package

Emacs is the extensible editor, and that means I want to use third-party packages. Of course, first I have to manage those packages. I use the excellent straight.el.

Update the PATH

PATH handling on Emacs is a little complicated. There's the regular environment variable $PATH, which we all know and love, and then Emacs has its own special exec-path on top of that. From my research, it looks like Emacs uses exec-path for itself, and $PATH for any shells or other processes it spawns. They don't have to be the same, but luckily for us, Emacs sets exec-path from $PATH on initialization, so when I add stuff to exec-path to, say, run git, I can just change $PATH right back to the expanded exec-path without any data loss. Here's what all that looks like.

  (let ((win-app-dir "~/Applications"))
    (dolist (path (list
                   ;; Windows
                   (expand-file-name "exe" win-app-dir)
                   (expand-file-name "Git/bin" win-app-dir)
                   (expand-file-name "Git/usr/bin" win-app-dir)
                   (expand-file-name "Git/mingw64/bin" win-app-dir)
                   (expand-file-name "Everything" win-app-dir)
                   (expand-file-name "Win-builds/bin" win-app-dir)
                   ;; Linux
                   (expand-file-name "bin" user-emacs-directory)
                   (expand-file-name "~/bin")
                   (expand-file-name "~/.local/bin")
                   (expand-file-name "~/Scripts")
                   ))
      (when (file-exists-p path)
        (add-to-list 'exec-path path :append))))

  ;; Set $PATH
  (setenv "PATH" (mapconcat #'identity exec-path path-separator))

Disable package.el

  (setq package-enable-at-startup nil)

Bootstrap

The following is straight (heh) from the straight repo, wrapped in a function so I can call it in another wrapper.

  (defun acdw/bootstrap-straight ()
    "Bootstrap straight.el."
    (defvar bootstrap-version)
    (let ((bootstrap-file
           (expand-file-name
            "straight/repos/straight.el/bootstrap.el"
            user-emacs-directory))
          (bootstrap-version 5))
      (unless (file-exists-p bootstrap-file)
        (with-current-buffer
            (url-retrieve-synchronously
             (concat
              "https://raw.githubusercontent.com/"
              "raxod502/straight.el/develop/install.el")
             'silent 'inhibit-cookies)
          (goto-char (point-max))
          (eval-print-last-sexp)))
      (load bootstrap-file nil 'nomessage)))

To actually bootstrap straight, I'll first try running the above directly. If it errors (it tends to on Windows), I'll directly clone the repo using git, then run the bootstrap code.

  (when (executable-find "git")
    (unless (ignore-errors (acdw/bootstrap-straight))
      (let ((msg "Straight.el didn't bootstrap correctly.  Cloning directly"))
        (message "%s..." msg)
        (call-process "git" nil
                      (get-buffer-create "*bootstrap-straight-messages*") nil
                      "clone"
                      "https://github.com/raxod502/straight.el"
                      (expand-file-name "straight/repos/straight.el"
                                        user-emacs-directory))
        (message "%s...Done." msg)
        (acdw/bootstrap-straight))))

System-specific

I use both Linux (at home) and Windows (at work). To make Emacs easier to use in both systems, I've included various system-specific settings and written some ancillary scripts.

Linux (home)

Settings

Scripts

em

Here's a wrapper script that'll start emacs --daemon if there isn't one, and then launch emacsclient with the arguments. Install it to your $PATH somewhere.

  if ! emacsclient -nc "$@"; then
      emacs --daemon
      emacsclient -nc "$@"
  fi
emacsclient.desktop

I haven't really tested this yet, but it should allow me to open other files and things in Emacs. From taingram.

  [Desktop Entry]
  Name=Emacs Client
  GenericName=Text Editor
  Comment=Edit text
  MimeType=text/english;text/plain;text/x-makefile;text/x-c++hdr;text/x-c++src;text/x-chdr;text/x-csrc;text/x-java;text/x-moc;text/x-pascal;text/x-tcl;text/x-tex;application/x-shellscript;text/x-c;text/x-c++;
  Exec=emacsclient -c %f
  Icon=emacs
  Type=Application
  Terminal=false
  Categories=Utility;TextEditor;

Windows (work)

I use Windows at work, where I also don't have Admin rights. So I kind of fly-by-night there. Much of the ideas and scripts in this section come from termitereform on Github.

Settings

See also the GNU FAQ for Windows. At some point I should really dig into the multiple settings available for w32 systems.

    (setq-default w32-allow-system-shell t) ; enable cmd.exe as shell

Scripts

Common variables
set HOME=%~dp0..\..
set EMACS=%HOME%\Applications\Emacs\bin\runemacs.exe
chdir %HOME%
Emacs Daemon

Either run this once at startup, or put a shortcut of it in the Startup folder: %USERPROFILE%\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup.

<<w32-bat-common>>
%EMACS% --daemon
Emacs Client

This will try to connect to the daemon above. If that fails, it'll run runemacs.exe.

This is the main shortcut for running Emacs.

<<w32-bat-common>>
set EMACSC=%HOME%\Applications\Emacs\bin\emacsclientw.exe
"%EMACSC%" -n -c -a "%EMACS%" %*
Emacs Safe Start

This runs Emacs with the factory settings.

<<w32-bat-common>>
"%EMACS%" -Q %*
Emacs Debug

This runs Emacs with the --debug-init option enabled.

<<w32-bat-common>>
"%EMACS%" --debug-init %*

Appendices

config.el

While config.el is written above, I use Noweb references to tangle them all together in the following block, which enables me to organize my config here logically, while keeping the generated file organized programmatically.

Enable lexical binding

  ;;; config.el --- personal configuration -*- lexical-binding: t -*-

Header & disclaimer

  ;; Copyright (C) 2020 Case Duckworth

  ;; Author: Case Duckworth <acdw@acdw.net>
  ;; Created: Sometime during the Covid-19 lockdown, 2019
  ;; Keywords: configuration
  ;; URL: https://tildegit.org/acdw/emacs

  ;; This file is not part of GNU Emacs.

  ;;; Commentary:
  ;; This file is automatically tangled from config.org.
  ;; Hand edits will be overwritten!

The rest

  <<disclaimer>>
  ;;; Code:

  ;;; REQUIRES
  <<requires>>

  ;;; ACDW MODE
  <<acdw-mode>>

  ;;; PACKAGES
  <<packages>>

  ;;; FUNCTIONS
  <<functions>>

  ;;; SETTINGS
  <<settings>>

  ;;; SYSTEM-DEPENDENT SETTINGS
  ;; at home
  (eval-and-compile
    (when (memq system-type '(gnu gnu/linux gnu/kfreebsd))
      <<linux-specific>>
      ))

  ;; at work
  (eval-and-compile
    (when (memq system-type '(ms-dos windows-nt))
      <<windows-specific>>
      ))

  ;;; MODES
  <<modes>>

  ;;; HOOKS
  <<hooks>>

  ;;; BINDINGS
  <<bindings>>
  ;;; config.el ends here

init.el

The classic Emacs initiation file.

Header

  ;;; init.el -*- lexical-binding: t -*-
  <<disclaimer>>
  ;;; Code:

Prefer newer files to older files

  (setq-default load-prefer-newer t)

Load the config

I keep most of my config in config.el, which is tangled directly from this file. This init just loads that file, either from lisp or directly from Org if it's newer. Note the longish comment before the unless form it was pretty tough for me to wrap my head around the needed boolean expression to tangle config.org. Booleans, yall!

  (let* (;; Speed up init
         (gc-cons-threshold most-positive-fixnum)
         (file-name-handler-alist nil)
         ;; Config file names
         (config (expand-file-name "config"
                                   user-emacs-directory))
         (config.el (concat config ".el"))
         (config.org (concat config ".org"))
         (straight-org-dir (expand-file-name "straight/build/org"
                                             user-emacs-directory)))
    ;; Okay, let's figure this out.
    ;; `and' evaluates each form, and returns nil on the first that
    ;; returns nil.  `unless' only executes its body if the test
    ;; returns nil.  So.
    ;; 1. Test if config.org is newer than config.el.  If it is (t), we
    ;;    *want* to evaluate the body, so we need to negate that test.
    ;; 2. Try to load the config.  If it errors (nil), it'll bubble that
    ;;    to the `and' and the body will be evaluated.
    (unless (and (not (file-newer-than-file-p config.org config.el))
                 (load config :noerror))
      ;; A plain require here just loads the older `org'
      ;; in Emacs' install dir.  We need to add the newer
      ;; one to the `load-path', hopefully that's all.
      (when (file-exists-p straight-org-dir)
        (add-to-list 'load-path straight-org-dir))
      ;; Load config.org
      (require 'org)
      (org-babel-load-file config.org)))

  ;;; init.el ends here

early-init.el

Beginning with 27.1, Emacs also loads an early-init.el file, before the package manager or the UI code. The Info says we should put as little as possible in this file, so I only have what I need.

  ;;; early-init.el -*- no-byte-compile: t; -*-
  <<disclaimer>>
  ;;; Code:

  ;; BOOTSTRAP PACKAGE MANAGEMENT
  <<early-init-package>>
  ;; SETUP FRAME
  <<early-init-frame>>

  ;;; early-init.el ends here

License

Copyright © 2020 Case Duckworth <acdw@acdw.net>

This work is free. You can redistribute it and/or modify it under the terms of the Do What the Fuck You Want To Public License, Version 2, as published by Sam Hocevar. See the LICENSE file, tangled from the following source block, for details.

  DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE

  Version 2, December 2004

  Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>

  Everyone is permitted to copy and distribute verbatim or modified copies of
  this license document, and changing it is allowed as long as the name is changed.

  DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE

  TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. You just DO WHAT THE FUCK YOU WANT TO.

Note on the license

It's highly likely that the WTFPL is completely incompatible with the GPL, for what should be fairly obvious reasons. To that, I say:

SUE ME, RMS!