From c76d0204448e11a403c230cd0f883b5ee3447b72 Mon Sep 17 00:00:00 2001 From: Case Duckworth Date: Tue, 8 Dec 2020 23:51:40 -0600 Subject: I DECLARE … BANKRUPTCY!!! … again --- README.md | 2030 +++++++++++++++++++------------------------------------------ 1 file changed, 635 insertions(+), 1395 deletions(-) (limited to 'README.md') diff --git a/README.md b/README.md index 3f1adfa..fd7ed85 100644 --- a/README.md +++ b/README.md @@ -1,1656 +1,896 @@ -This is my Emacs configuration. It's also a literate `org-mode` file. Yeah, I'm a cool guy. - -# About me - - ;; init.el -*- lexical-binding: t -*- - (setq user-full-name "Case Duckworth" - user-mail-address "acdw@acdw.net") - - -# License - -Copyright © 2020 Case Duckworth - -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 - - 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!** - - -# Bootstrap - - -## Original init.el - -This file replaces itself with the actual configuration when it's first run. For easy installation, *this* is the `init.el` file in git – and you probably want to keep it that way. To keep git from trying to update `init.el` when it's re-tangled, type this in the repo: - - git update-index --assume-unchanged init.el - -If, for some reason, you want to change this original file to be re-tracked, run this command: - - git update-index --no-assume-unchanged init.el - -Otherwise, here's the actual, original `init.el` that tangles this Org file and gets us going. - - (require 'org) - (find-file (concat user-emacs-directory "config.org")) - (org-babel-tangle) - (load-file (concat user-emacs-directory "early-init.el")) - (load-file (concat user-emacs-directory "init.el")) - (byte-compile-file (concat user-emacs-directory "init.el")) - - -### TODO What I should do instead - -Honestly, I should just change this "Original init.el" thing to a Makefile I can tangle in `config.org`, and track – since it won't be overwritten or need any special `git` invocations to stop tracking it, I can edit it as I think about what would work best. I could also maybe give it more of a "cross-platform" vibe by installing, say, `straight.el` in the Makefile on Windows. One day … - - -## Tangling - -After our first tangle, each time we edit `config.org` we want to go ahead and re-tangle our config. To that end, I've written `acdw/tangle-init`, which automatically tangles `config.org`. - - (defun acdw/tangle-init () - "If the current buffer is `config.org', tangle it, then compile - and load the resulting files." - (when (equal (buffer-file-name) - (expand-file-name - (concat user-emacs-directory "config.org"))) - ;; Tangle and load init.el and early-init.el - (require 'async) - (async-start - (lambda () - (let ((prog-mode-hook nil)) - (require 'org) - (org-babel-tangle-file - (expand-file-name - (concat user-emacs-directory "config.org"))))) - (lambda (response) - (acdw/load-init) - (message "Tangled and loaded: %s" response))))) - -Since I want to tangle every time I save `config.org`, I've added `acdw/tangle-init` to a hook. - - (add-hook 'after-save-hook #'acdw/tangle-init) - -Finally, I want an easier way to load the generated init files than the old `M-x load-file RET ~/.config/emacs/init.el RET`. So I've written `acdw/load-init` – which also gets called at the end of the async part of `acdw/tangle-init`. - - (defun acdw/load-init () - (interactive) - (load-file (expand-file-name - (concat user-emacs-directory "early-init.el"))) - (load-file (expand-file-name - (concat user-emacs-directory "init.el")))) - - -## Miscellaneous bootstrappy stuff - - -### Add directories to `load-path` - -I also put lispy stuff in the `lisp/` subdirectory of my Emacs config, and under my SyncThing directory (for easy syncing ;P). - - (dolist (dir `(,(concat user-emacs-directory - (convert-standard-filename "lisp/")) - ,(expand-file-name "~/Sync/elisp/"))) - (add-to-list 'load-path dir)) - - -### TODO Require my secrets - -While this is like, the *dumbest* way to do this, it's what I'm doing right now. I'm going to slap a TODO on here because I really should make it better – like, `auth-sources` hooked into KeePassXC somehow… ? Maybe follow [Bill Dietrich's setup](https://www.billdietrich.me/Authentication.html?expandall=1#KeePassXCandSecretService). - - (require 'acdw-secrets) - - -# Early initiation - -Starting with version 27.1, Emacs loads `early-init.el` *before* `init.el`, setting up early stuff like package management, etc. Since I use an alternative package manager, I have to bootstrap it here. - -Of course, I also want to set some really early-on settings here too, like `load-prefer-newer` – why not? - - ;; early-init.el -*- lexical-binding: t; no-byte-compile: t -*- - (setq load-prefer-newer t) - - -## Increase the garbage collector - -Let's try to speed startup times by increasing the garbage collector's threshold while running init. Note the hook afterwards that restores it to a reasonable default. - - (setq gc-cons-threshold (* 100 100 1000)) - - (add-hook 'after-init-hook - (lambda () - (setq gc-cons-threshold (* 100 100 100)) - (message "gc-cons-threshold restored to %S" - gc-cons-threshold))) - - -## Add more paths to the `exec-path` - -When using Windows (at work), I need to use the PortableGit installation I've downloaded, since I don't have Admin privileges. - - (when (eq system-type 'windows-nt) - (dolist (path '("c:/Users/aduckworth/Downloads/emacs/bin" - "C:/Users/aduckworth/Downloads/PortableGit/bin" - "C:/Users/aduckworth/Downloads/PortableGit/usr/bin")) - (add-to-list 'exec-path path))) - -Elsewhere, I want to add a few more paths to the `exec-path` as well, since I store scripts in a couple of places at ~. - - (dolist (path `(,(expand-file-name "bin" - user-emacs-directory) - ,(expand-file-name "~/bin") - ,(expand-file-name "~/.local/bin") - ,(expand-file-name "~/Scripts"))) - (add-to-list 'exec-path path)) - - -## Bootstrap [straight.el](https://github.com/raxod502/straight.el) - -So far, this is the best package manager I've used. It allows for *truly* declarative package management (if I don't specify a package here, it doesn't get loaded), easy installation from pretty much any source (as long as it's got a git repo), *and* it hooks into `use-package`! - -The one annoying thing is that this bootstrap code doesn't work on Windows for some reason. I'm too lazy to really try and figure out why, so when I need to bootstrap on Windows (pretty rare, TBH), I just [download the master-branch zip file](https://github.com/raxod502/straight.el/archive/master.zip) and extract it to `~/.emacs.d/straight/repos/`. - - (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 - "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)) - - -## … but first: override the definition of straight.el to use the `develop` branch - -For the KeePassXC bits later, I need the `develop` branch of straight, so I can pass a `:build` keyword. I can do this, but I have to override the recipe for `straight.el` itself. I do that here. For more information, see [the README](https://github.com/raxod502/straight.el#overriding-recipes). - - (setq straight-repository-branch "develop") - - (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 +# Table of Contents + +1. [Pave the way](#org24d31f9) + 1. [Correct `exec-path`](#org82dd805) + 2. [Package management](#org947e1de) + 1. [Straight.el](#orgd711f6b) + 2. [Use-package](#org9392b5d) + 3. [Extra use-package keywords](#orgc93ae09) + 3. [Customize variables](#org7cae7fe) + 1. [Put customizations in a separate file](#org126d855) + 2. [A macro for ease of customization](#org2cf1d1a) + 4. [Keep a tidy `~/.emacs`](#orga6c0096) +2. [Look and Feel](#org1ecbcc5) + 1. [Simplify the UI](#org23fb19e) + 1. [Tool bars and menu bars](#orgad64258) + 2. [Scroll bars](#org9b2f49e) + 3. [Dialog boxen](#orgf1c5f65) + 4. [Shorten confirmations](#orgedf9e78) + 5. [Remove the bell](#org1643ce2) + 6. [Tell Ediff to setup windows better](#org3996a6f) + 2. [Tweak the remaining UI](#orgcdf874b) + 1. [Window dividers](#org187505c) + 2. [Fringes](#org3fd2bc6) + 3. [Minibuffer](#org8ff32ea) + 4. [Tabs](#orgef2a000) + 5. [Cursor](#orge57d1b2) + 6. [Buffer names](#orgb3f29a9) + 7. [Buffer boundaries](#org2627b1e) + 3. [Startup](#org1fc3c6d) + 4. [Theme](#org207a1bd) + 1. [Fonts](#org52f6c8c) +3. [Interactivity](#org6cbcfe5) + 1. [Selectrum](#org7f26398) + 2. [Prescient](#orgea8df9e) + 3. [Consult](#org8818eb9) + 4. [Marginalia](#orgd31a964) + 5. [Ignore case](#org6e4913f) + 6. [Search](#org416dd18) +4. [Persistence](#orgb20768d) + 1. [Save history](#org7dfee32) + 2. [Save places in files](#org0f20005) + 3. [Recent files](#org6d1a477) + 1. [Easily navigate recent files](#org9368a6b) + 4. [Undo](#orgbb4f91a) +5. [Editing](#org52b008a) + 1. [Operate visually on lines](#orgce838ba) + 2. [Require a final newline](#org6f67996) + 3. [Killing & Yanking](#orga2bdb3e) + 1. [Replace selection when typing](#org16c1a6b) + 2. [Save existing clipboard text into kill ring before replacing it](#orgea7fd73) + 4. [So long mode](#org27f430f) +6. [Files](#org8cc8ee8) + 1. [Encoding](#org8ca2e9b) + 1. [UTF-8](#org54363a7) + 2. [Convert all files to UNIX-style line endings](#orgeaed3bd) + 2. [Backups](#org7239c47) + 3. [Auto-saves](#org32fc658) + 4. [Revert files](#org94456e2) + 5. [Add a timestamp to files](#orgb586b3b) +7. [Programming](#org738fbd9) + 1. [Which function are we in?](#org080eb2f) +8. [Writing](#org6bf5097) + 1. [Visual Fill Column](#org6f01971) + 2. [Type nice-looking quote-type marks](#org03e747d) +9. [Applications](#org9528516) + 1. [Magit](#orgd2a60aa) +10. [Appendices](#org7339cf2) + 1. [Emacs' files](#org6070b2c) + 1. [init.el](#org0d720f6) + 2. [early-init.el](#orgd6bffd2) + 2. [Ease tangling and loading of Emacs' init](#org9c5b437) + 3. [License](#org1a4bb4d) + 1. [Note on the license](#orga6047ee) + + + + + +# Pave the way + + + + +## Correct `exec-path` + + (let ((win-downloads "c:/Users/aduckworth/Downloads")) + (dolist (path `(;; Linux + ,(expand-file-name "bin" + user-emacs-directory) + ,(expand-file-name "~/bin") + ,(expand-file-name "~/.local/bin") + ,(expand-file-name "~/Scripts") + ;; Windows + ,(expand-file-name "emacs/bin" + win-downloads) + ,(expand-file-name "PortableGit/bin" + win-downloads) + ,(expand-file-name "PortableGit/usr/bin" + win-downloads))) + (when (file-exists-p path) + (add-to-list 'exec-path path)))) + + + + +## Package management + + + + +### 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 "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)) - - -## Use [use-package](https://jwiegley.github.io/use-package/) - -Like I said, `straight.el` hooks into `use-package` easily. These two lines get the latter to use the former by default. - - (setq straight-use-package-by-default t) - (straight-use-package 'use-package) - - -## Keep `~/.emacs.d` tidy with [no-littering](https://github.com/emacscollective/no-littering) - -I'll be honest – I don't really notice this package. But I think that's the point. It keeps Emacs (and packages) from throwing files all over the place, so I have a clean `ls -l`. Since I want to run this code as early as possible, I use the `straight-use-package` form instead of `use-package`. - - (straight-use-package 'no-littering) - (require 'no-littering) + (goto-char (point-max)) + (eval-print-last-sexp))) + (load bootstrap-file nil 'nomessage)) -## Additional `use-package` keywords + +### Use-package -### [:custom-update](https://github.com/a13/use-package-custom-update) + (setq straight-use-package-by-default t) + (straight-use-package 'use-package) -The `:custom-update` keyword lets me do this: - (use-package package - :custom-update - (package-list '(1 2 3))) + -instead of this: +### Extra use-package keywords - (use-package package - :config - (add-to-list 'package-list '(1 2 3))) +1. :custom-update -It's not … perfect, but it's kind of nice. + (straight-use-package + '(use-package-custom-update + :host "github" + :repo "a13/use-package-custom-update")) + + (require 'use-package-custom-update) - (use-package use-package-custom-update - :straight (use-package-custom-update - :host github - :repo "a13/use-package-custom-update")) - - -## Setup [async](https://github.com/jwiegley/emacs-async) - -I thought this was included in Emacs at first, but it's not – so we need to install and require it. - - (straight-use-package 'async) - (require 'async) - - -# Macros - - -## Customizing variables -I like `use-package` a lot, but I don't like using those shims you see in a lot of other Emacs configs where they use `(use-package emacs)` forms and stuff like that – it just feels dirty. Plus, `straight` gets confused about those packages sometimes. So, since I'm actually *configuring* Emacs in this Org file, which is nicely organized anyway, I can just set settings the old-school way. + -Except. Using `setq` is actually *not* recommended any more, because `customize-set-variable` is more expressive and can include side-effects. However, not all settings are customizable, *and* `customize-set-variable` is like, way longer to type. So I've decided to write a little macro (my first!) to copy `use-package`'s `:custom` keyword, except … *outside* `use-package`. I've called it `cuss`, because I have a terrible sense of humor. +## Customize variables - (defmacro cuss (var val) - "Basically `use-package''s `:custom', but without using either." - `(progn - (funcall (or (get ',var 'custom-set) #'set-default) - ',var ,val))) + -# Theme: [Modus](https://protesilaos.com/modus-themes/) +### Put customizations in a separate file -Protesilaos Stavrou's *excellent* theme pair. At some point I'll probably write my own that's really minimal and does some funky stuff with faces, but until then, these really are the best I've used. + (setq custom-file + (expand-file-name "custom.el" user-emacs-directory)) -The big `dolist` form is from [his documentation](https://protesilaos.com/modus-themes/#h:a897b302-8e10-4a26-beab-3caaee1e1193); it basically allows me to configure both themes before loading them. I've tweaked his code a little to use `use-package`. - (defmacro modus-themes-format-sexp (sexp &rest objects) - `(eval (read (format ,(format "%S" sexp) ,@objects)))) + - (dolist (theme '("operandi" "vivendi")) - (modus-themes-format-sexp - (use-package modus-%1$s-theme - :custom - (modus-%1$s-theme-slanted-constructs t) - (modus-%1$s-theme-bold-constructs t) - (modus-%1$s-theme-fringes nil) - (modus-%1$s-theme-mode-line '3d) - (modus-%1$s-theme-syntax 'yellow-comments) - (modus-%1$s-theme-intense-hl-line nil) - (modus-%1$s-theme-intense-paren-match t) - (modus-%1$s-theme-links nil) - (modus-%1$s-theme-no-mixed-fonts nil) - (modus-%1$s-theme-prompts nil) - (modus-%1$s-theme-completions nil) - (modus-%1$s-theme-diffs nil) - (modus-%1$s-theme-org-blocks 'grayscale) - (modus-%1$s-theme-headings - '((1 . section) - (2 . line) - (t . rainbow-line))) - (modus-%1$s-theme-variable-pitch-headings t) - (modus-%1$s-theme-scale-headings t) - (modus-%1$s-theme-scale-1 1.1) - (modus-%1$s-theme-scale-2 1.15) - (modus-%1$s-theme-scale-3 1.21) - (modus-%1$s-theme-scale-4 1.27) - (modus-%1$s-theme-scale-5 1.33) - :custom-face - (font-lock-comment-face - ((t (:inherit (custom-comment italic variable-pitch)))))) - theme)) +### A macro for ease of customization + (defmacro cuss (var val &optional docstring) + "Basically `:custom' from `use-package', broken out." + `(funcall (or (get ',var 'custom-set) #'set-default) + ',var ,val)) -## Theme changer -I also want to switch themes between night and day. + - (use-package theme-changer - :custom - (calendar-latitude 30.39) - (calendar-longitude -91.83) - :config - (change-theme 'modus-operandi 'modus-vivendi)) +## Keep a tidy `~/.emacs` + (use-package no-littering + :custom + (backup-directory-alist + `((".*" . ,(no-littering-expand-var-file-name "backup/")))) + (auto-save-file-name-transforms + `((".*" ,(no-littering-expand-var-file-name "autosaves/") t))) + (save-place-file + (no-littering-expand-var-file-name "places")) + (undo-fu-session-directory + (no-littering-expand-var-file-name "undos/")) + (undohist-directory + (no-littering-expand-var-file-name "undos/")) + (elpher-certificate-directory + (no-littering-expand-var-file-name "elpher-certificates/"))) + + (dolist (dir '("backup" + "autosaves" + "undos" + "elpher-certificates")) + (make-directory (no-littering-expand-var-file-name dir) t)) -# Simplify GUI + - +# Look and Feel -## Frame defaults -I want no toolbar, menubar, or scrollbars (ideally I'd have a vertical scrollbar if necessary, but apparently that's too much to ask the Emacs devs); and fringes and window dividers 2 pixels wide. + - (cuss default-frame-alist - '((tool-bar-lines . 0) - (menu-bar-lines . 0) - (vertical-scroll-bars . nil) - (horizontal-scroll-bars . nil) - (right-divider-width . 2) - (bottom-divider-width . 2) - (left-fringe-width . 2) - (right-fringe-width . 2))) +## Simplify the UI -## Minibuffer window/frame defaults + -Of course, on the minibuffer, I want to make sure there's no scrollbar – even if I change my mind on `vertical-scroll-bars`, above. +### Tool bars and menu bars - (cuss minibuffer-frame-alist - '((width . 80) - (height . 2) - (vertical-scrollbars . nil))) + (cuss default-frame-alist + '((tool-bar-lines . 0) + (menu-bar-lines . 0))) + + (menu-bar-mode -1) + (tool-bar-mode -1) - (set-window-scroll-bars (minibuffer-window) nil nil) + -## Remove unneeded GUI elements +### Scroll bars -The [Frame Defaults](#org1f12d35) section sets up the frame to be free of visual clutter, but *this* section allows us to toggle that clutter's visibility easily, with one call to each of these functions. + (add-to-list 'default-frame-alist '(vertical-scroll-bars . nil)) + (scroll-bar-mode -1) + + (add-to-list 'default-frame-alist '(horizontal-scroll-bars . nil)) + (horizontal-scroll-bar-mode -1) - (menu-bar-mode -1) - (tool-bar-mode -1) - (scroll-bar-mode -1) - (horizontal-scroll-bar-mode -1) + -## Tabs +### Dialog boxen -I'm kind of getting into Emacs tabs – but I like not showing the `tab-bar` when there's only one. + (cuss use-dialog-box nil) - (cuss tab-bar-show 1) - (cuss tab-bar-tab-name-function 'tab-bar-tab-name-current-with-count) + +### Shorten confirmations -## Word wrap and operate visually + (fset 'yes-or-no-p #'y-or-n-p) -`global-visual-line-mode` is one of those which, in my opinion, should be a default. There's only one place I don't want to wrap words, and that's in `dired`, which I can set individually in its config. - (global-visual-line-mode 1) + +### Remove the bell -## Modeline + (cuss visible-bell (not (string= (system-name) "larry"))) -### [smart-mode-line](https://github.com/Malabarba/smart-mode-line) + - (use-package smart-mode-line - :custom - (sml/no-confirm-load-theme t) - :config - (sml/setup)) +### Tell Ediff to setup windows better + (declare-function ediff-setup-windows-plain "ediff-wind.el") + (cuss ediff-window-setup-function #'ediff-setup-windows-plain) -### [rich-minority](https://github.com/Malabarba/rich-minority) -`smart-mode-line` comes with `rich-minority` for taking care of minor modes in the modeline, so I'm not going to *also* use `diminish` or anything. However, `rich-minority` has kind of a hinky way of adding modes to the whitelist, so I had to write my own function to do so. + -This confuration means that, by default, no minor modes are shown; if you want a minor mode to be shown (like `word-count-mode` for me), call `(rm/whitelist-add "REGEXP")`. +## Tweak the remaining UI - (defun rm/whitelist-add (regexp) - "Add a REGEXP to the whitelist for `rich-minority'." - (if (listp 'rm--whitelist-regexps) - (add-to-list 'rm--whitelist-regexps regexp) - (setq rm--whitelist-regexps `(,regexp))) - (setq rm-whitelist - (mapconcat 'identity rm--whitelist-regexps "\\|"))) - (use-package rich-minority - :config - (rm/whitelist-add "^$")) + +### Window dividers -## Minibuffer + (add-to-list 'default-frame-alist '(right-divider-width . 2)) + (add-to-list 'default-frame-alist '(bottom-divider-width . 2)) -### Keep cursor from going into the prompt + -from [Ergo Emacs](http://ergoemacs.org/emacs/emacs_stop_cursor_enter_prompt.html). +### Fringes - (cuss minibuffer-prompt-properties - '(read-only t cursor-intangible t face minibuffer-prompt)) + (add-to-list 'default-frame-alist '(left-fringe-width . 2)) + (add-to-list 'default-frame-alist '(right-fringe-width . 2)) -## Show `^L` as a line + -I like using the form-feed character to separate pages, it turns out. 'Tis nice. This package turns that character into a nice long line. +### Minibuffer - (use-package form-feed - :hook - ((text-mode prog-mode) . form-feed-mode)) +1. Setup the minibuffer frame + (cuss minibuffer-frame-alist + '((width . 80) + (height . 2) + (vertical-scrollbars . nil))) + + (set-window-scroll-bars (minibuffer-window) nil nil) -## Cursor +2. Keep the cursor from going into the prompt -I want my cursor to be a bar in focused windows, but a hollow box in non-focused windows. + (cuss minibuffer-prompt-properties + '(read-only t cursor-intangible t face minibuffer-prompt)) - (cuss cursor-type 'bar) - (cuss cursor-in-non-selected-windows 'hollow) + -# Typesetting +### Tabs +1. Show the tabs as current buffer, plus window count -## Fonts + (cuss tab-bar-tab-name-function #'tab-bar-tab-name-current-with-count) -This is the best way I've come up with to specify a number of different fonts that apply depending on what's applied. To be honest, I didn't really come up with the `font-candidate` function, though – I got it from the ["Testing if fonts are available?"](https://www.emacswiki.org/emacs/SetFonts#toc11) section of the SetFonts page on EmacsWiki. +2. Only show the tab bar when there's more than one tab -See [this StackExchange question and answer](https://emacs.stackexchange.com/questions/12351/when-to-call-find-font-if-launching-emacs-in-daemon-mode) for more information on why I have these font settings applied in a hook. + (cuss tab-bar-show 1) - (require 'cl) - (defun font-candidate (&rest fonts) - (loop for font in fonts - when (find-font (font-spec :name font)) - return font)) - (defun acdw/setup-fonts () - "Setup fonts. This has to happen after the frame is set up for - the first time, so add it to `focus-in-hook'. It removes - itself." - (interactive) - (set-face-attribute 'default nil - :font - (font-candidate - "Libertinus Mono-11" - "Linux Libertine Mono O-11" - "Go Mono-11" - "Consolas-11")) + - (set-face-attribute 'fixed-pitch nil - :font - (font-candidate - "Libertinus Mono-11" - "Linux Libertine Mono O-11" - "Go Mono-11" - "Consolas-11")) +### Cursor - (set-face-attribute 'variable-pitch nil - :font - (font-candidate - "Libertinus Serif-13" - "Linux Libertine O-12" - "Georgia-11")) + (cuss cursor-type 'bar) + (cuss cursor-in-non-selected-windows 'hollow) - (remove-hook 'focus-in-hook #'acdw/setup-fonts)) - (add-hook 'focus-in-hook #'acdw/setup-fonts) + +### Buffer names -## [unicode-fonts](https://github.com/rolandwalker/unicode-fonts) + (require 'uniquify) + (cuss uniquify-buffer-name-style 'forward) -This does something similar to the above code, but for the entirety of the Unicode field (I think). - (use-package unicode-fonts - :config - (unicode-fonts-setup)) + +### Buffer boundaries -## Variable pitch faces - -One reason I like the Modus themes so much is that they have *excellent* support for variable-pitch faces, and mixing them with fixed-pitch faces in, say, Org Mode. That means I can enable `variable-pitch-mode` in all my `text-mode`-derived buffers. - - (add-hook 'text-mode-hook #'variable-pitch-mode) - - -## Padding - -This has been taken from ["Ricing Org Mode"](https://lepisma.xyz/2017/10/28/ricing-org-mode/) – of course, I want the typographic niceties everywhere. - - (cuss line-spacing 0.1) - + (cuss indicate-buffer-boundaries + '((top . right) + (bottom . right) + (t . nil))) + + (cuss indicate-empty-lines t) -# Ease of use + ## Startup -I want a minimal screen when I start Emacs. Based on the beauty of configs like [Nicolas Rougier's](https://github.com/rougier/elegant-emacs) [splash screen](https://github.com/rougier/emacs-splash) [experiments](https://github.com/rougier/nano-emacs), I might try my hand at some kind of splash screen or dashboard – but until then, a simple "Hi there!" will suffice 😎 - - (cuss inhibit-startup-buffer-menu t) - (cuss inhibit-startup-screen t) - (cuss initial-buffer-choice t) - (cuss initial-scratch-message ";; Hi there!\n") - - -## Completing-read niceties - -`completing-read` is Emacs's selection-narrowing-slash-completion framework thing. There's a bunch of packages for it, including `ido`, `icomplete`, `ivy`, and `helm`. I use raxod52's `selectrum` and others, which *extend* without *clobbering* existing Emacs functionality. Plus they seem to run faster, at least on Windows. - - -### [selectrum](https://github.com/raxod502/selectrum) - -`selectrum` is the basic *sorting and selecting items from a list* functionality. It's a drop-in replacement for `ido` or the really basic tab-completion Emacs has for, say, `find-file`. - - (use-package selectrum - :config - (selectrum-mode 1)) - - -### [prescient](https://github.com/raxod502/prescient.el) - -`prescient` helps `selectrum` be more intelligent about sorting the candidates in a list – it's in charge of the *filtering and sorting* bit of `completing-read` and friends. It has an algorithm that works well enough for me, though I keep hearing about [orderless](https://github.com/oantolin/orderless), enough to maybe try it as well sometime. - - (use-package prescient - :config - (prescient-persist-mode 1)) - - (use-package selectrum-prescient - :after (selectrum prescient) - :config - (selectrum-prescient-mode 1)) - - -### [consult](https://github.com/minad/cconsult) - -`consult` is the newest package I have with this setup, and it kind of brings the `selectrum` experience up to par with `ivy`'s – it provides functions that list, say, recently used files *alongside* buffers, allow you to search lines and go to them, etc. It seems pretty nice so far. + (cuss inhibit-startup-buffer-menu t) + (cuss inhibit-start-screen t) + (cuss initial-buffer-choice t) + (cuss initial-scratch-message ";; Hi there!\n") + + + + +## Theme + + (use-package modus-themes + :straight (modus-themes + :host gitlab + :repo "protesilaos/modus-themes" + :branch "main") + :custom + (modus-themes-slanted-constructs t) + (modus-themes-bold-constructs t) + (modus-themes-fringes nil) + (modus-themes-mode-line '3d) + (modus-themes-syntax 'yellow-comments) + (modus-themes-intense-hl-line nil) + (modus-themes-paren-match 'intense-bold) + (modus-themes-links nil) + (modus-themes-no-mixed-fonts nil) + (modus-themes-prompts nil) + (modus-themes-completions nil) + (modus-themes-diffs nil) + (modus-themes-org-blocks 'grayscale) + (modus-themes-headings + '()) + (modus-themes-variable-pitch-headings t) + (modus-themes-scale-headings t) + (modus-themes-scale-1 1.1) + (modus-themes-scale-2 1.15) + (modus-themes-scale-3 1.21) + (modus-themes-scale-4 1.27) + (modus-themes-scale-5 1.33) + :custom-face + (font-lock-comment-face + ((t (:inherit (custom-comment italic variable-pitch))))) + :init + (load-theme 'modus-operandi t)) + + + + +### Fonts + +1. Define fonts + + (defun font-candidate (&rest fonts) + (catch :font + (dolist (font fonts) + (if (find-font (font-spec :name font)) + (throw :font font))))) + + (defun acdw/setup-fonts () + "Setup fonts. This has to happen after the frame is setup for + the first time, so it should be added to `window-setup-hook'. It + removes itself from that hook." + (interactive) + (set-face-attribute 'default nil + :font + (font-candidate + "Libertinus Mono-11" + "Linux Libertine Mono O-11" + "Go Mono-10" + "Consolas-10")) + + (set-face-attribute 'fixed-pitch nil + :font + (font-candidate + "Libertinus Mono-11" + "Linux Libertine Mono O-11" + "Go Mono-10" + "Consolas-10")) + + (set-face-attribute 'variable-pitch nil + :font + (font-candidate + "Libertinus Serif-13" + "Linux Libertine O-12" + "Georgia-11")) + + (remove-hook 'window-setup-hook #'acdw/setup-fonts)) + + (add-hook 'window-setup-hook #'acdw/setup-fonts) + +2. Variable-pitch in text modes + + (add-hook 'text-mode-hook #'variable-pitch-mode) + +3. Line spacing + + (cuss line-spacing 0.1) + +4. Unicode fonts + + (use-package unicode-fonts + :config + (unicode-fonts-setup)) + + + + +# Interactivity + + + + +## Selectrum + + (use-package selectrum + :config + (selectrum-mode +1)) + + + + +## Prescient + + (use-package prescient + :config + (prescient-persist-mode +1)) + + (use-package selectrum-prescient + :after (selectrum prescient) + :config + (selectrum-prescient-mode +1)) + + + -By the way, the [Reddit announcement thread for consult](https://www.reddit.com/r/emacs/comments/k3c0u7) has a great comment by the author detailing [the differences between different completing-read implementations](https://www.reddit.com/r/emacs/comments/k3c0u7/consult_counselswiper_alternative_for/ge460z3/) that actually is what convinced me to try `consult`. +## Consult - (use-package consult - :after (selectrum) - :straight (consult + (use-package consult + :after (selectrum) + :straight (consult :host github :repo "minad/consult") - :bind (("C-x b" . consult-buffer) - ("C-x 4 b" . consult-buffer-other-window) - ("C-x 5 b" . consult-buffer-other-frame) - ("M-g o" . consult-outline) - ("M-g l" . consult-line) - ("M-y" . consult-yank-pop) - (" a" . consult-apropos)) - :init - (fset 'multi-occur #'consult-multi-occur) - (consult-annotate-mode) - :config - (setf (alist-get 'execute-extended-command consult-annotate-alist) - #'consult-annotate-command-full)) - - -### Ignore case - -I don't like holding the Shift key if I can help it. - - (cuss completion-ignore-case t) - (cuss read-buffer-completion-ignore-case t) - (cuss read-file-name-completion-ignore-case t) - - -## [ctrlf](https://github.com/raxod502/ctrlf) - -The biggest reason I use this over the default functionality of `C-s` is that `ctrlf-forward-*` wraps the search around by default. - - (use-package ctrlf - :custom - (ctrlf-show-match-count-at-eol nil) - :bind - ("C-s" . ctrlf-forward-regexp) - ("C-r" . ctrlf-backward-regexp) - ("C-M-s" . ctrlf-forward-literal) - ("C-M-r" . ctrlf-backward-literal) - :config - (ctrlf-mode 1)) - - -## [which-key](https://github.com/justbur/emacs-which-key) - -This package is really helpful for discovering functionality. When I get more adept in my Emacs-fu, I might remove this. - - (use-package which-key - :custom - (which-key-popup-type 'minibuffer) - :config - (which-key-mode)) - - -## Miscellaneous settings - -Maybe a better title for this section is **Other settings** – or maybe I should put them somewhere else entirely. - - -### Set `view-mode` when in a read-only file + :bind + (("C-x b" . consult-buffer) + ("C-x 4 b" . consult-buffer-other-window) + ("C-x 5 b" . consult-buffer-other-frame) + ("M-g o" . consult-outline) + ("M-g l" . consult-line) + ("M-y" . consult-yank-pop) + (" a" . consult-apropos)) + :init + (fset 'multi-occur #'consult-multi-occur)) -`view-mode` gives easy-to-use keybindings, like Space for page-down, etc., which are nice to have when you can't edit the file anyway. - (cuss view-read-only t) + +## Marginalia -### Don't use dialog boxen - - (cuss use-dialog-box nil) - - -### Enable all functions - -By default, Emacs disables some commands, because NeWbIeS wOuLd GeT cOnFuSeD or some ish. I just want to use the dang editor! - - (cuss disabled-command-function nil) - - -### Shorter confirmations - -Instead of making me type *yes* or *no*, just let me hit the *y* or *n* key. - - (fset 'yes-or-no-p #'y-or-n-p) - - -### Uniquify buffer names - -This names buffers with the same basename (e.g., `~/.config/emacs/config.org` and `~/.emacs.d/config.org`) in a better way than the default (`config.org<1>`, etc). - - (require 'uniquify) - (cuss uniquify-buffer-name-style 'forward) - - -### Show buffer boundaries - -These little L-shaped graphics at the top and bottom of buffers don't do anything, but I like 'em. - - (cuss indicate-buffer-boundaries - '((top . right) - (bottom . right) - (t . nil))) - - -### Hippie expand - -At some point, will probably replace with [company](https://company-mode.github.io/). - - (global-set-key (kbd "M-/") 'hippie-expand) - - -### "[better defaults](https://git.sr.ht/~technomancy/better-defaults/tree/master/better-defaults.el)" - -Most of these come from technomancy's repo, linked above, just copy-pasted into here. - - (cuss save-interprogram-paste-before-kill t) - (cuss apropos-do-all t) - (cuss mouse-yank-at-point t) - (cuss require-final-newline t) - (cuss visible-bell (not (string= (system-name) "larry"))) - (cuss ediff-window-setup-function #'ediff-setup-windows-plain) - -1. Zap-up-to-char, not zap-to-char - - Similarly to `ibuffer`, this is a Better default™. - - (autoload 'zap-up-to-char "misc" - "Kill up to, but not including, ARGth occurrence of CHAR." t) - - (global-set-key (kbd "M-z") 'zap-up-to-char) - -2. iBuffer - - A Better Default™ for `C-x C-b`. I don't really use this, but everyone says it's worth it, so it's there. - - (global-set-key (kbd "C-x C-b") 'ibuffer) + (use-package marginalia + :straight (marginalia + :host github + :repo "minad/marginalia" + :branch "main") + :custom + (marginalia-annotators + '((command . marginalia-annotate-command-full) + (customize-group . marginalia-annotate-customize-group) + (variable . marginalia-annotate-variable) + (face . marginalia-annotate-face) + (symbol . marginalia-annotate-symbol) + (variable . marginalia-annotate-variable) + (package . marginalia-annotate-package))) + :init + (marginalia-mode +1)) -### So-long-mode + -I figure, why not go ahead and make Emacs deal with really long lines better? Can't hurt, right? +## Ignore case - (if (boundp 'global-so-long-mode) - (global-so-long-mode)) + (cuss completion-ignore-case t) + (cuss read-buffer-completion-ignore-case t) + (cuss read-file-name-completion-ignore-case t) -### Change `just-one-space` to `cycle-space` + -I keep forgetting to actually *use* this keybind (I think it's `M-SPC`?), but cycling spacing seems *way* more useful than the default `just-one-space` function. +## Search - (defun acdw/cycle-spacing-1 () - (interactive) - (cycle-spacing -1)) + (use-package ctrlf + :custom + (ctrlf-show-match-count-at-eol nil) + :bind + ("C-s" . ctrlf-forward-regexp) + ("C-r" . ctrlf-backward-regexp) + ("C-M-s" . ctrlf-forward-literal) + ("C-M-r" . ctrlf-backward-literal) + :config + (ctrlf-mode +1)) - (bind-key [remap just-one-space] #'acdw/cycle-spacing-1) + # Persistence -Honestly, persistence across sessions was one of the best things about my well-tuned Vim setup. Here's where I try to repeat that with Emacs. - - -## Auto-saves with [super-save](https://github.com/bbatsov/super-save) - -The default `auto-save` functionality isn't … *enough* for me. I want to *actually* save the files, and I don't care about `#file#` stuff. So … I use this package. - - (use-package super-save - :custom - (auto-save-default nil) - (super-save-exclue '(".gpg")) - :config - (super-save-mode 1)) - - -## Backup files - -To be honest, I probably don't need backup files at all. At some point, I will probably delete this. - - (cuss backup-directory-alist - `((".*" . ,(no-littering-expand-var-file-name "backup/")))) - - (cuss backup-by-copying 1) - (cuss delete-old-versions -1) - (cuss version-control t) - (cuss vc-make-backup-files t) - - -## Recent files - -Since I apparently *only* edit my `config.org`, this is also probably not necessary – I'd be better off just adding a `(find-file (concat (user-emacs-directory "config.org")))` at the end 😎 - -But until then, it's really nice to have a `recentf` list. - - (require 'recentf) - - (add-to-list 'recentf-exclude - '(no-littering-var-directory - no-littering-etc-directory)) - - (cuss recentf-max-menu-items 100) - (cuss recentf-max-saved-items 100) - - (recentf-mode 1) - - -### Easily navigate recent files - -Now I'm going through this, I might not need this function any more. I'll have to see how `consult` goes. - - (defun recentf-find-file () - "Find a recent file using `completing-read'." - (interactive) - (let ((file (completing-read "Recent file: " recentf-list nil t))) - (when file - (find-file file)))) - - (bind-key "C-x C-r" #'recentf-find-file) - - -## Save places in visited files - - (require 'saveplace) - - (cuss save-place-file (no-littering-expand-var-file-name "places")) - - (cuss save-place-forget-unreadable-files - (not (eq system-type 'windows-nt))) - - (save-place-mode 1) + ## Save history - (require 'savehist) - - (cuss savehist-additional-variables - '(kill-ring + (require 'savehist) + + (cuss savehist-additional-variables + '(kill-ring search-ring regexp-search-ring)) + + (cuss savehist-save-minibuffer-history t) + + (cuss history-length t) + + (cuss history-delete-duplicates t) + + (savehist-mode +1) - (cuss savehist-save-minibuffer-history t) - - (cuss history-length t) - - (cuss history-delete-duplicates t) - (savehist-mode 1) + +## Save places in files -## Undo: [undo-fu-session](https://gitlab.com/ideasman42/emacs-undo-fu-session) + (require 'saveplace) + + (cuss save-place-forget-unreadable-files + (not (eq system-type 'windows-nt))) + + (save-place-mode 1) -The other Killer Feature of Neovim when I used it was the perisistent undo. I *think* this works the same. Honestly, undo is giving me a little grief recently; I need to look into it. -Note to self: if I *do* switch away from `undo-fu`, look at [undohist](https://github.com/emacsorphanage/undohist). + - (use-package undo-fu-session - :after (no-littering undo-fu) - :custom - (undo-fu-session-incompatible-files - '("COMMIT_EDITMSG\\'" - "/git-rebase-todo\\'")) - (undo-fu-session-directory - (no-littering-expand-var-file-name "undos/")) - :config - (global-undo-fu-session-mode 1)) - - -# General editing - - -## File encoding - -I just want to use UTF-8 everywhere, and end all files with UNIX line endings (`^J`, or `LF`). Hell, even Windows Notepad correctly reads UNIX files nowadays (though of course you can't configure it to *save* the files in UNIX-mode). However, since Emacs is ~40 years old, it has a billion different ways to set encodings. This is my best attempt at setting everything up how I want it. - -I'm going to be honest – most of this is a stab in the dark. - - (set-language-environment 'utf-8) - (set-terminal-coding-system 'utf-8) - (cuss locale-coding-system 'utf-8) - (set-default-coding-systems 'utf-8) - (set-selection-coding-system 'utf-8) - (prefer-coding-system 'utf-8) - - ;; from https://www.emacswiki.org/emacs/EndOfLineTips - - (defun acdw/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)))) - - (add-hook 'find-file-hooks #'acdw/no-junk-please-were-unixish) - - -## [undo-fu](https://gitlab.com/ideasman42/emacs-undo-fu) - -I've heard that Emacs' undo is weird, so here I am, trying to make it …. *less* weird. I keep forgetting I've installed this though, so I might uninstall it at some point. - - (use-package undo-fu - :bind - ("C-/" . undo-fu-only-undo) - ("C-?" . undo-fu-only-redo)) - - -## Find/replace: [visual-regexp](https://github.com/benma/visual-regexp.el) - -Another replacement for a Killer Feature in Neovim – the ease of regexp find/replace was so wonderful, because I could easily see *what* I'd be changing with a `%s` command, as well as *how* it'd change. This works… pretty similarly. It could be a little better. - - (use-package visual-regexp - :bind - ("C-c r" . 'vr/replace) - ("C-c q" . 'vr/query-replace)) - - -## Visual editing - - -### [volatile-highlights](https://github.com/k-talo/volatile-highlights.el) +## Recent files -Highlights text changed by certain operations. + (require 'recentf) + + (cuss recentf-max-menu-items 100) + (cuss recentf-max-saved-items 100) + + (with-eval-after-load 'no-littering + (add-to-list 'recentf-exclude no-littering-var-directory) + (add-to-list 'recentf-exclude no-littering-etc-directory)) + + (recentf-mode 1) - (use-package volatile-highlights - :config - (volatile-highlights-mode 1)) + -### [expand-region](https://github.com/magnars/expand-region.el) +### Easily navigate recent files -I don't use this a *ton*, but not because it's not useful – I just forget it's there sometimes. + (defun recentf-find-file () + "Find a recent file using `completing-read'." + (interactive) + (let ((file (completing-read "Recent file: " recentf-list nil t))) + (when file + (find-file file)))) + + (global-set-key (kbd "C-x C-r") #'recentf-find-file) -Basically, it allows you to do like a Kakoune-style incremental widening of the selection by semantic units. - (use-package expand-region - :bind - ("C-=" . er/expand-region) - ("C-+" . er/contract-region)) + +## Undo -## Clean up white space on save + (use-package undohist + :config + (undohist-initialize)) -I'm not sure if I'll *keep* this forever, because in combination with `super-save` I lose the final "thinking spaces" when I shift contexts to another window. - (add-hook 'before-save-hook #'whitespace-cleanup) - (add-hook 'before-save-hook #'delete-trailing-whitespace) + +# Editing -## Automatically revert a file to what it is on disk -Revert a buffer to reflect what's on disk if it's changed outside of Emacs. + - (global-auto-revert-mode 1) +## Operate visually on lines + (global-visual-line-mode +1) -# Writing -Configurations related to writing prose or verse. + +## Require a final newline -## Word count: [wc-mode](https://github.com/bnbeckwith/wc-mode) + (cuss require-final-newline t) - (use-package wc-mode - :config - (rm/whitelist-add "WC") - :hook text-mode) + -## [visual-fill-column-mode](https://github.com/joostkremers/visual-fill-column) +## Killing & Yanking -Center the text part of the frame within a `fill-column`-sized area in the frame as a whole. - (use-package visual-fill-column - :custom - (split-window-preferred-function - 'visual-fill-column-split-window-sensibly) - (visual-fill-column-center-text t) - (fill-column 80) - :config - (advice-add 'text-scale-adjust - :after #'visual-fill-column-adjust) - :hook - (text-mode . visual-fill-column-mode)) - - -### Fix mouse bindings - -In `visual-fill-column-mode`, mouse bindings on the margins don't work. In fact, they don't work when *not* in `visual-fill-column-mode`. Let's bind those bindings. - - (dolist (vec '([left-margin wheel-down] - [left-margin mouse-5] - [right-margin wheel-down] - [right-margin mouse-5])) - (bind-key vec 'scroll-down-command)) - - (dolist (vec '([left-margin wheel-up] - [left-margin mouse-4] - [right-margin wheel-up] - [right-margin mouse-4])) - (bind-key vec 'scroll-up-command)) - - -## [org-mode](https://orgmode.org/) - -Pretty self-explanatory, I think… - -I need to break this config up and like, comment it better. - - (use-package org - :custom - (org-startup-indented t) - (org-src-tab-acts-natively t) - (org-hide-emphasis-markers t) - (org-fontify-done-headline t) - (org-fontify-whole-heading-line t) - (org-fontify-quote-and-verse-blocks t) - (org-hide-leading-stars t) - (org-hidden-keywords '(author date title)) - (org-src-window-setup 'current-window) - (org-pretty-entities t) - (org-ellipsis " ⋯ ")) - - -### Make bullets look like centered dots - -from [zzamboni.org](https://zzamboni.org/post/beautifying-org-mode-in-emacs/) - - (font-lock-add-keywords - 'org-mode - '(("^ *\\([-+]\\) " - (0 (prog1 () - (compose-region (match-beginning 1) - (match-end 1) - "•")))))) - - -### [org-superstar](https://github.com/integral-dw/org-superstar-mode) - - (use-package org-superstar - :custom - (org-superstar-headline-bullets-list - '(?❧ ?✪ ?③ ?④ ?⑤ ?⑥ ?⑦ ?⑧ ?⑨ ?●)) - (org-superstar-cycle-headline-bullets nil) - (org-superstar-item-bullet-alist - '((?* . ?★) - (?+ . ?‣) - (?- . ?•))) - :custom-face - (org-superstar-header-bullet - ((t (:weight normal)))) - :hook - (org-mode . org-superstar-mode)) - - -### Enable markdown export - - (require 'ox-md) - - -### Ensure blank lines between headings and before contents - -from [unpackaged.el](https://github.com/alphapapa/unpackaged.el#ensure-blank-lines-between-headings-and-before-contents) - - ;;;###autoload - (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))) - - -### `org-return-dwim` - -from [unpackaged.el](https://github.com/alphapapa/unpackaged.el#org-return-dwim) - - (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)))) - - ;;;###autoload - (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"))))) - (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))))) + - (bind-key "RET" #'unpackaged/org-return-dwim 'org-mode-map) +### Replace selection when typing + (delete-selection-mode +1) -# Coding -The Other Thing Emacs is Good For. + +### Save existing clipboard text into kill ring before replacing it -## Formatting + (cuss save-interprogram-paste-before-kill t) -### Indenting: [aggressive-indent-mode](https://github.com/Malabarba/aggressive-indent-mode) + -This automagically indents code on every change, as opposed to `electric-indent-mode`, which only does when I like, hit `RET` or whatever. As such, I can turn `electric-indent-mode` off. +## So long mode - (use-package aggressive-indent - :init - (electric-indent-mode -1) - :config - (global-aggressive-indent-mode 1)) + (when (fboundp 'global-so-long-mode) + (global-so-long-mode)) -### [Smart tabs](https://github.com/jcsalomon/smarttabs) + -I really want to like, use tabs all the time. But I thought the `smart-tabs` package author made some good points about using tabs for semantic indentation, and spaces for the rest. So. +# Files - (use-package smart-tabs-mode - :custom - (whitespace-style - '(face trailing tabs spaces lines newline - empty indentation space-before-tab - space-mark tab-mark newline-mark)) - :config - (smart-tabs-insinuate 'c 'c++ 'javascript 'java 'ruby)) + -## Display +## Encoding -### Prettify symbols mode + -By default, I think `prettify-symbols-mode` only changes `lambda` to `λ`. I should, at some point, add some prettified symbols. +### UTF-8 - (add-hook 'prog-mode-hook #'prettify-symbols-mode) + (set-language-environment 'utf-8) + (set-terminal-coding-system 'utf-8) + (cuss locale-coding-system 'utf-8) + (set-default-coding-systems 'utf-8) + (set-selection-coding-system 'utf-8) + (prefer-coding-system 'utf-8) -### Parentheses and frens + -1. `show-paren-style` +### Convert all files to UNIX-style line endings - A `mixed` `show-paren-style` means that, when both parentheses are visible, it just highlights them. If one is *not*, though, it highlights the entire block. +from [Emacs Wiki](https://www.emacswiki.org/emacs/EndOfLineTips). - (cuss show-paren-style 'mixed) - (show-paren-mode 1) + (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)))) -2. [smartparens](https://github.com/Fuco1/smartparens) +I add it to the `find-file-hook` *and* `before-save-hook` because 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. - Automagically close pairs and stuff. See also [ParEdit](https://www.emacswiki.org/emacs/ParEdit) – maybe test that one? + (add-hook 'find-file-hook #'ewiki/no-junk-please-were-unixish) + (add-hook 'before-save-hook #'ewiki/no-junk-please-were-unixish) - (use-package smartparens - :init - (defun acdw/setup-smartparens () - (require 'smartparens-config) - (smartparens-mode 1)) - :hook - (prog-mode . acdw/setup-smartparens)) -3. [rainbow-delimiters](https://github.com/Fanael/rainbow-delimiters) + - Show different pairs of delimiters in diffferent colors. Pretty! Useful! +## Backups - (use-package rainbow-delimiters - :hook (prog-mode . rainbow-delimiters-mode)) + (cuss backup-by-copying 1) + (cuss delete-old-versions -1) + (cuss version-control t) + (cuss vc-make-backup-files t) -### [rainbow-mode](https://elpa.gnu.org/packages/rainbow-mode.html) + -Show different colors *in that color*. Useful! Pretty! +## Auto-saves - (use-package rainbow-mode - :custom - (rainbow-x-colors nil) - :hook prog-mode) + (auto-save-visited-mode 1) -### Line numbers + -I only want line numbers in `prog-mode`-derived modes. In addition, apparently `linum-mode` works better in TUI, but is slower that `display-line-numbers`. So I want to do some logic to see what to use. +## Revert files - (defun acdw/enable-line-numbers () - "Enable line numbers, either through `display-line-numbers-mode' - or through `linum-mode'." - (if (and (fboundp 'display-line-numbers-mode) - (display-graphic-p)) - (progn - (display-line-numbers-mode 1) - (cuss display-line-numbers-width 2)) - (linum-mode 1))) + (cuss auto-revert-verbose nil) + (global-auto-revert-mode +1) - (add-hook 'prog-mode-hook #'acdw/enable-line-numbers) + -## Programming languages +## Add a timestamp to files -These are the programming languages I (don't really) use. + (add-hook 'before-save-hook #'time-stamp) -### Fish shell + - (use-package fish-mode) +# Programming -### Lisps + -1. Common Lisp (SLIME) +## Which function are we in? - (use-package slime - :when (executable-find "sbcl") - :custom - (inferior-lisp-program "sbcl") - (slime-contribs '(slime-fancy))) + (which-function-mode +1) -2. Fennel - (use-package fennel-mode - :mode "\\.fnl\\'") + - -### Lua - - (use-package lua-mode - :mode "\\.lua\\'" - :interpreter "lua") +# Writing -### Web (HTML/CSS/JS) + - (use-package web-mode - :mode (("\\.ts\\'" . web-mode) - ("\\.html?\\'" . web-mode) - ("\\.css?\\'" . web-mode) - ("\\.js\\'" . web-mode))) +## Visual Fill Column + (use-package visual-fill-column + :custom + (split-window-preferred-function + 'visual-fill-column-split-window-sensibly) + (visual-fill-column-center-text t) + (fill-column 80) + :config + (advice-add 'text-scale-adjust + :after #'visual-fill-column-adjust) + :hook + (text-mode . visual-fill-column-mode)) -### `~/.ssh/config` - (use-package ssh-config-mode) + +## Type nice-looking quote-type marks -### Go + (use-package typo + :hook + (text-mode . typo-mode)) - (use-package go-mode - :mode "\\.go\\'" - :hook - (before-save . gofmt-before-save)) + # Applications -Of course, the real reason we love emacs is for the application layer. What is it they say? - -> Emacs is a great operating system, lacking only a decent editor. - -Yeah, that's it 😎 - - -## Git: [magit](https://magit.vc/) -The magical porcelain. + - (use-package magit - :bind - ("C-x g" . magit-status) - :custom-update - (magit-no-confirm '(stage-all-changes)) - :config - (add-hook 'magit-process-find-password-functions - #'magit-process-password-auth-source)) +## Magit + (use-package magit + :bind + ("C-x g" . magit-status)) -### Hook into `prescient` - (define-advice magit-list-refs - (:around (orig &optional namespaces format sortby) - prescient-sort) - "Apply prescient sorting when listing refs." - (let ((res (funcall orig namespaces format sortby))) - (if (or sortby - magit-list-refs-sortby - (not selectrum-should-sort-p)) - res - (prescient-sort res)))) + +# Appendices -### Use `libgit` when I can build it (requires `cmake`) - (when (executable-find "cmake") - (use-package libgit) - (use-package magit-libgit)) + +## Emacs' files -### Git "forge" capabilities - (use-package forge - :after magit - :unless (eq system-type 'windows-nt) - :custom - (forge-owned-accounts - '(("duckwork")))) + +### init.el -## Dired + ;; init.el -*- lexical-binding: t -*- -I'm still figuring out what all I can do with `dired`. +1. Load config - (with-eval-after-load 'dired - (cuss dired-dwim-target t) - (cuss dired-listing-switches "-alDh") + from [Protesilaos Stavrou](https://protesilaos.com/dotemacs/#h:584c3604-55a1-49d0-9c31-abe46cb1f028). + + (let* ((conf (expand-file-name "config" + user-emacs-directory)) + (elc (concat conf ".elc")) + (el (concat conf ".el")) + (org (concat conf ".org"))) + (cond ((file-exists-p elc) (load-file elc)) + ((file-exists-p el) (load-file el)) + (t (require 'org) + (org-babel-load-file org)))) - (cuss wdired-allow-to-change-permissions t) - (bind-key "C-c w" #'wdired-change-to-wdired-mode 'dired-mode-map)) + -### dired-subtree +### early-init.el -Part of the [dired-hacks](https://github.com/Fuco1/dired-hacks) package. + ;; early-init.el -*- lexical-binding: t; no-byte-compile: t; -*- - (use-package dired-subtree - :bind (:map dired-mode-map - (("i" . dired-subtree-insert) - (";" . dired-subtree-remove)))) + (setq load-prefer-newer t) + (setq frame-inhibit-implied-resize t) -## Proced + -The process editor. +## Ease tangling and loading of Emacs' init - (defun acdw/setup-proced () - (variable-pitch-mode -1) - (toggle-truncate-lines 1) - (proced-toggle-auto-update 1)) + (defun acdw/tangle-and-load-init () + (interactive) + "If the current buffer is `config.org', tangle it, then byte-compile." + (let ((config (expand-file-name "config.org" user-emacs-directory))) + (when (string= (buffer-file-name) config) + (let ((prog-mode-hook nil)) + (require 'org) + (org-babel-tangle-file config) + (org-md-export-to-markdown) + + (dolist (file `(,(expand-file-name "init.el" + user-emacs-directory) + ,(expand-file-name "config.el" + user-emacs-directory))) + (byte-compile-file file t)))))) + + (add-hook 'after-save-hook #'acdw/tangle-and-load-init) - (add-hook 'proced-mode-hook #'acdw/setup-proced) + -## Gemini (and gopher) +## License +Copyright 2020 Case Duckworth -### [elpher](https://thelambdalab.xyz/elpher/) +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. -Actually, `elpher` is the reason I started using Emacs. So thanks, smol web denizens! + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + + Version 2, December 2004 + + Copyright (C) 2004 Sam Hocevar + + 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. -Fun fact: these packages are *also* why I use `straight.el`, since they're none of them on GitHub. - (use-package elpher - :straight (elpher - :repo "git://thelambdalab.xyz/elpher.git") - :custom - (elpher-certificate-directory - (no-littering-expand-var-file-name "elpher-certificates/")) - (elpher-ipv4-always t) - :custom-face - (elpher-gemini-heading1 - ((t (:inherit (modus-theme-heading-1))))) - (elpher-gemini-heading2 - ((t (:inherit (modus-theme-heading-2))))) - (elpher-gemini-heading3 - ((t (:inherit (modus-theme-heading-3))))) - :config - (defun elpher:eww-browse-url (original url &optional new-window) - "Handle gemini/gopher links with eww." - (cond ((string-match-p "\\`\\(gemini\\|gopher\\)://" url) - (require 'elpher) - (elpher-go url)) - (t (funcall original url new-window)))) - (advice-add 'eww-browse-url :around 'elpher:eww-browse-url) - :bind (:map elpher-mode-map - ("n" . elpher-next-link) - ("p" . elpher-prev-link) - ("o" . elpher-follow-current-link) - ("G" . elpher-go-current)) - :hook - (elpher-mode . visual-fill-column-mode)) + +### Note on the license -### [gemini-mode](https://git.carcosa.net/jmcbray/gemini.el) +It's highly likely that the WTFPL is completely incompatible with the +GPL, for what should be fairly obvious reasons. To that, I say: -A major mode for `text/gemini` files. I've changed the headings to match Elpher's. - - (use-package gemini-mode - :straight (gemini-mode - :repo "https://git.carcosa.net/jmcbray/gemini.el.git") - :mode "\\.\\(gemini|gmi\\)\\'" - :custom-face - (gemini-heading-face-1 - ((t (:inherit (elpher-gemini-heading1))))) - (gemini-heading-face2 - ((t (:inherit (elpher-gemini-heading2))))) - (gemini-heading-face3 - ((t (:inherit (elpher-gemini-heading3))))) - :init - (defun acdw/setup-gemini-mode () - (visual-fill-column-mode 1) - (variable-pitch-mode -1)) - :hook - (gemini-mode . acdw/setup-gemini-mode)) - - -### [gemini-write](https://alexschroeder.ch/cgit/gemini-write/about/) - -Alex Schroeder's Emacs implementation of the Titan protocol. This is why I use his Gemini server, [Phoebe](https://alexschroeder.ch/cgit/phoebe/)! - - (use-package gemini-write - :straight (gemini-write - :repo "https://alexschroeder.ch/cgit/gemini-write") - :config - (when (boundp 'acdw-secrets/elpher-gemini-tokens) - (dolist (token acdw-secrets/elpher-gemini-tokens) - (add-to-list 'elpher-gemini-tokens token)))) - - -### [post-to-gemlog-blue](https://git.sr.ht/~acdw/post-to-gemlog-blue.el) - -My first (!) Emacs package, to allow posting to [gemlog.blue's web interface](https://gemlog.blue). I don't use gemlog.blue any more, but if I didn't have this package, no one would 😎 - - (use-package post-to-gemlog-blue - :straight (post-to-gemlog-blue - :repo "https://git.sr.ht/~acdw/post-to-gemlog-blue.el")) - - -## Pastebin: [0x0](https://git.sr.ht/~zge/nullpointer-emacs) - -Pastebins are so useful. Now I can use them from Emacs. - - (use-package 0x0 - :custom - (0x0-default-service 'ttm)) - - -## [mu4e](https://www.djcbsoftware.nl/code/mu/mu4e.html) - -I've just recently started (again) using mu4e. We'll see how it goes. - - (when (executable-find "mu") - (add-to-list 'load-path - "/usr/share/emacs/site-lisp/mu4e") - (require 'mu4e) - - (cuss mail-user-agent 'mu4e-user-agent) - - (cuss mu4e-headers-skip-duplicates t) - (cuss mu4e-view-show-images t) - (cuss mu4e-view-show-addresses t) - (cuss mu4e-compose-format-flowed t) - (cuss mu4e-change-filenames-when-moving t) - (cuss mu4e-attachments-dir "~/Downloads") - - (cuss mu4e-maildir "~/.mail/fastmail") - (cuss mu4e-refile-folder "/Archive") - (cuss mu4e-sent-folder "/Sent") - (cuss mu4e-drafts-folder "/Drafts") - (cuss mu4e-trash-folder "/Trash") - - (fset 'my-move-to-trash "mTrash") - (define-key mu4e-headers-mode-map (kbd "d") 'my-move-to-trash) - (define-key mu4e-view-mode-map (kbd "d") 'my-move-to-trash) - - (cuss message-send-mail-function 'smtpmail-send-it) - (cuss smtpmail-default-smtp-server "smtp.fastmail.com") - (cuss smtpmail-smtp-server "smtp.fastmail.com") - (cuss smtpmail-stream-type 'ssl) - (cuss smtpmail-smtp-service 465) - (cuss smtpmail-local-domain "acdw.net") - (cuss mu4e-compose-signature - "Best,\nCase\n") - - (cuss mu4e-get-mail-command "mbsync -a") - (cuss mu4e-update-interval 300) - - (cuss mu4e-completing-read-function 'completing-read) - (cuss message-kill-buffer-on-exit t) - (cuss mu4e-confirm-quit nil) - - (cuss mu4e-bookmarks - '(( - :name "Unread" - :query - "flag:unread AND NOT flag:trashed AND NOT maildir:/Spam" - :key ?u) - ( - :name "Today" - :query "date:today..now and not maildir:/Spam" - :key ?t) - ( - :name "This week" - :query "date:7d..now and not maildir:/Spam" - :hide-unread t - :key ?w))) - - (cuss mu4e-headers-fields - '((:human-date . 12) - (:flags . 6) - (:mailing-list . 10) - (:from-or-to . 22) - (:subject))) - - (defun acdw/setup-mu4e-view-mode () - (visual-fill-column-mode)) - - (add-hook 'mu4e-view-mode-hook #'acdw/setup-mu4e-view-mode)) - - ;; not sure about this... - (use-package mu4e-dashboard - :straight (mu4e-dashboard - :host github - :repo "rougier/mu4e-dashboard" - :branch "main")) - - - - -## KeePassXC integration - -I use KeePassXC, and I'd *like* to use KeePassXC to get passwords and stuff at home. So I'm trying this library out, since the secret-tool integration isn't built into the KeePassXC on Void. If this doesn't work, looks like I'll have to become a packager 😜 - - (when (eq system-type 'gnu/linux) - (use-package sodium - :straight (sodium - :host github - :repo "dakra/sodium.el" - :build ("make")) - :init - (add-to-list 'load-path - (expand-file-name "straight/repos/sodium.el" - user-emacs-directory))) - (use-package keepassxc - :straight (keepassxc - :host github - :repo "dakra/keepassxc.el") - :after (sodium))) - - -### libsodium integration - -I had to manually run `make` in the repo this time around, for some reason. Should probably look into that … when it's not 1:00 AM. - - (use-package sodium - :straight (sodium - :host github - :repo "dakra/sodium.el" - :build ("make")) - :init - (add-to-list 'load-path - (expand-file-name "straight/repos/sodium.el" - user-emacs-directory))) - - -# Appendix A: Scripts - - -## `emacsdc` - -Here's a wrapper script that'll start `emacs --daemon` if there isn't one, and then launche `emacsclient` on the arguments. I'd recommend installing with `ln -s emacsdc ~/.local/bin/` or something. Then you can set it as your `$EDITOR`! - -if ! emacsclient -nc "$@" 2>/dev/null; then - emacs --daemon - emacsclient -nc "$@" - fi - - -# Appendix B: areas for further research - -- [ebuku](https://github.com/flexibeast/ebuku) (of course, I'd need [buku](https://github.com/jarun/buku) as well) – bookmarks -- [KeePassXC as Secret Service](https://www.billdietrich.me/Authentication.html?expandall=1#KeePassXCandSecretService) – see [14.7](#orgb5347d4) -- [Ignoramus](https://github.com/rolandwalker/ignoramus) – this might not e necessary -- [Dynamic fonts](https://github.com/rolandwalker/dynamic-fonts) – take a look @ this and compare with my fonts section -- [Simple clipboard integration](https://github.com/rolandwalker/simpleclip) – test with Windows, maybe -- [visible mark](https://git.sr.ht/~iank/visible-mark) – show where the marks are … -- consider this Reddit thread: [speeding up magit](https://www.reddit.com/r/emacs/comments/k3xfa1/speeding_up_magit/) - - -## export org to ODT on Windows - -Windows doesn't have `zip` natively (though it *does* have `curl` – go figure!), so the ODT export for Org-mode won't work. There *is* a [StackOverflow discussion](https://stackoverflow.com/questions/8625306/org-mode-zip-needed-how-to-over-come) that mentions `p7zip` and a possible batch file, but I couldn't get that to work with a little testing. It might need more work. - -Something that *did* work was downloading `zip.exe` from [Info-ZIP](http://infozip.sourceforge.net/), and placing it somewhere in `exec-path` – though of course, *then* you need `unzip.exe`, apparently in the same folder. Git Portable ships with `unzip.exe`, for example, but even though it's in `exec-path`, org-export threw an error unless `zip` and `unzip` were in the *same* folder. +**SUE ME, RMS!** -So I might either (a) have to set up computers in this way when I use new ones, or (b) include both `zip.exe` and `unzip.exe` in *this* Git repo, or … something else. A true quandry. -- cgit 1.4.1-21-gabe81