0ea7241c66 | ||
---|---|---|
etc/eshell | ||
var | ||
.gitattributes | ||
.gitignore | ||
LICENSE | ||
README.org | ||
config.org | ||
early-init.el | ||
init.el |
README.org
Emacs configuration, literate-style
- About me
- Look and feel
- Interactivity
- Persistence
- Responsiveness
- Files
- Editing
- Writing
- Reading
- Programming
- Applications
- Org mode
- Package management
- System integration
- Appendices
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
'("Emacs "
mode-line-client
mode-line-modified
" "
(:eval (if (buffer-file-name)
(abbreviate-file-name (buffer-file-name))
"%b"))
))
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)
(defun hook--setup-fringes-curly-arrows ()
"Set up curly-arrow fringes."
(define-fringe-bitmap 'left-curly-arrow
[#b11000000
#b01100000
#b00110000
#b00011000])
(define-fringe-bitmap 'right-curly-arrow
[#b00011000
#b00110000
#b01100000
#b11000000]))
(add-hook 'after-init-hook #'hook--setup-fringes-curly-arrows)
Arrows (truncation lines)
(defun hook--setup-fringes-arrows ()
"Setup arrow fringe bitmaps."
(define-fringe-bitmap 'left-arrow
[#b00000000
#b01010100
#b01010100
#b00000000])
(define-fringe-bitmap 'right-arrow
[#b00000000
#b00101010
#b00101010
#b00000000]))
(add-hook 'after-init-hook #'hook--setup-fringes-arrows)
Windows
Window Dividers
(setq-default window-divider-default-places 'right-only ; only right
window-divider-default-bottom-width 2
window-divider-default-right-width 2)
(window-divider-mode +1)
Splitting windows sensibly
This is extremely fiddly and I'd love another option.
(setq-default split-width-threshold 100
split-height-threshold 50)
Window layouts
Let's try settings from nex3 on Github.
(setq-default
;; `display-buffer-alist' is the big alist of things
display-buffer-alist
'((".*" (display-buffer-reuse-window display-buffer-same-window)))
;; reuse windows in other frames
display-buffer-reuse-frames t
;; avoid resizing
even-window-sizes nil)
Switch to other window or buffer crux
(acdw/bind "M-o" #'crux-other-window-or-switch-buffer)
The Help window
I want to select the Help window by default, so I can easily quit it.
(setq-default help-window-select t)
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
uniquify-separator "/"
uniquify-after-kill-buffer-p t
uniquify-ignore-buffers-re "^\\*")
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)) "!"
" 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)
An even better scratch buffer package
The aptly-named scratch
pops open a new scratch buffer with the same
mode as the file you're currently editing. I'm pretty chuffed about
it.
I found it from this discussion, which might also come in handy someday.
(straight-use-package 'scratch)
(acdw/bind "C-x" #'scratch :map acdw/leader)
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)))))
(acdw/bind "C-x k" #'kill-a-buffer)
Kill old buffers after a while
Adapted from midnight-mode
, using suggestions from u/ndamee.
(require 'midnight)
What time I run the clean up is a little tricky for me, since I use
Emacs at work and at home, and all at different times. However, I
realized that since I close out of Emacs at work pretty much every
day, I don't need to worry about too many buffers there – so I just
have clean-buffer-list
run at 8:00 PM.
(setq-default acdw/clean-buffer-list-timer
(run-at-time "20:00" 86400 #'clean-buffer-list)
clean-buffer-list-delay-general 5
clean-buffer-list-delay-special (* 7 24 60 60))
(add-to-list 'clean-buffer-list-kill-buffer-names "*Completions*")
(add-to-list 'clean-buffer-list-kill-buffer-names "*Calendar*")
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 bar mode settings
(setq-default tab-bar-show 1 ; show the tab bar when more than 1 tab
tab-bar-new-tab-choice "*scratch*"
tab-bar-tab-name-function
#'tab-bar-tab-name-current-with-count)
Tab bar history
(tab-bar-history-mode +1)
(setq-default tab-bar-history-limit 25)
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)
(set-face-attribute 'variable-pitch nil
:family "DejaVu Serif"
:height 110)
But on Windows, I use Consolas.
(set-face-attribute 'default nil
:family "Consolas"
:height 100)
(set-face-attribute 'fixed-pitch nil
:family "Consolas"
:height 100)
(set-face-attribute 'variable-pitch nil
:family "Cambria"
:height 105)
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
… and persistent-soft
List-utils
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")))
Persistent-soft
(straight-use-package '(persistent-soft
:host github
:repo "rolandwalker/persistent-soft"
:fork (:repo "duckwork/persistent-soft")))
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")))
A function in case it comes up again
I keep googling this Doom Emacs issue, because I keep forgetting what
I need to do to see where Package cl is deprecated
is coming from.
So… function!
;; Make the compiler happy
(autoload 'file-dependents "loadhist")
(autoload 'feature-file "loadhist")
(defun acdw/fucking-cl ()
"Find out where the fuck `cl' is being required from."
(interactive)
(require 'loadhist)
(message "%S" (file-dependents (feature-file 'cl))))
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.
(defun hook--unicode-fonts-setup ()
"Run `unicode-fonts-setup', then remove the hook."
(when (window-system)
(unicode-fonts-setup))
(remove-hook 'after-init-hook #'hook--unicode-fonts-setup))
(add-hook 'after-init-hook #'hook--unicode-fonts-setup)
Draw form-feeds (^L
) properly package
(straight-use-package 'form-feed)
(global-form-feed-mode +1)
(blackout 'form-feed-mode)
variable-pitch
fonts in text-mode
(add-hook 'text-mode-hook #'variable-pitch-mode)
Theme
Modus themes package
I want the git version.
(straight-use-package '(modus-themes
:host gitlab
:repo "protesilaos/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 . section)
(t . no-color))
modus-themes-scale-headings nil
modus-themes-mode-line nil)
Force headings to be fixed-pitch
To enable the proper alignment of Org tags, I want headings to inherit from the
fixed-pitch
family as well as themselves. This paves the way to enable
variable-pitch-mode
in Emacs.
(dolist (face '(modus-theme-heading-1
modus-theme-heading-2
modus-theme-heading-3
modus-theme-heading-4
modus-theme-heading-5
modus-theme-heading-6
modus-theme-heading-7
modus-theme-heading-8))
(doremi-face-set face
'((t (:inherit (face fixed-pitch bold))))))
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
Minions mode package
(straight-use-package 'minions)
(require 'minions)
(minions-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"))
Which-function mode
Shows where we are in the modeline.
(which-function-mode +1)
Mode-line faces
(doremi-face-set 'mode-line
'((t (:family "Terminus"
:height 1.0))))
(doremi-face-set 'mode-line-inactive
'((t (:family "Terminus"
:height 1.0))))
Setting faces
It took me a while to find a function that'll let me customize faces without using customize. Thanks to Drew Adams, I've got it!
(defun doremi-face-set (face spec)
"Tell Customize that FACE has been set to value SPEC.
SPEC is as for `defface'."
(put face 'customized-face spec)
(face-spec-set face spec)
(message "Customized face %s." (symbol-name face)))
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)
Icomplete
(straight-use-package 'icomplete-vertical)
(setq-default
completion-styles '(partial-completion substring flex)
completion-category-overrides '((file
(styles basic substring flex))))
(setq-default
icomplete-delay-completions-threshold 0
icomplete-max-delay-chars 0
icomplete-compute-delay 0
icomplete-show-matches-on-no-input t
icomplete-hide-common-prefix nil
icomplete-with-completion-tables t
icomplete-in-buffer t)
(acdw/bind-after-map 'icomplete icomplete-minibuffer-map
(("<down>" #'icomplete-forward-completions)
("C-n" #'icomplete-forward-completions)
("<up>" #'icomplete-backward-completions)
("C-p" #'icomplete-backward-completions)))
(acdw/bind "C-v" #'icomplete-vertical-toggle
:after 'icomplete-vertical
:map icomplete-minibuffer-map)
(fido-mode -1) ; I take care of this myself
(icomplete-mode +1)
(icomplete-vertical-mode +1)
Orderless package
(straight-use-package 'orderless)
(require 'orderless)
(setq-default
completion-styles '(orderless))
Consult package
(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.
(acdw/bind-after-map 'consult acdw/map
;; C-c bindings (`mode-specific-map')
(("C-c h" #'consult-history)
("C-c m" #'consult-mode-command)
;; C-x bindings (`ctl-x-map')
("C-x M-:" #'consult-complex-command)
("C-x b" #'consult-buffer)
("C-x 4 b" #'consult-buffer-other-window)
("C-x 5 b" #'consult-buffer-other-frame)
("C-x r x" #'consult-register)
("C-x r b" #'consult-bookmark)
;; M-g bindings (`goto-map')
("M-g g" #'consult-line)
("M-g M-g" #'consult-line)
("M-g o" #'consult-outline)
("M-g m" #'consult-mark)
("M-g k" #'consult-global-mark)
("M-g i" #'consult-imenu)
("M-g e" #'consult-error)
;; M-s bindings (`search-map')
("M-s g" #'consult-grep) ; alts:
; consult-git-grep,
; consult-ripgrep
("M-s f" #'consult-find) ; alts:
; consult-locate
("M-s l" #'consult-line)
("M-s m" #'consult-multi-occur)
("M-s k" #'consult-keep-lines)
("M-s u" #'consult-focus-lines)
;; Other bindings
("M-y" #'consult-yank-pop)
("<f1> a" #'consult-apropos)
("C-h 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 package
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))
Imenu
(setq-default imenu-auto-rescan t)
Completion
Hippie Expand
Before I install any completion framework, I want a good default for
completing. hippie-expand
fills that niche.
(acdw/bind "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 " acdw"
:keymap acdw/map)
(define-globalized-minor-mode acdw/global-mode acdw/mode acdw/mode)
(blackout 'acdw/mode)
acdw/bind
macro
Since defining keys can be a chore, I've written this macro to make it just a
little bit easier. It's not as comprehensive as bind-key
, but it's just a
little sugar on top of define-key
et al.
(defmacro acdw/bind (key def &rest args)
"A simple key-binding macro that takes care of the annoying stuff.
If KEY is a vector, it's passed directly to `define-key',
otherwise it wraps it in `kbd'. It does NOT quote any
definitions, because I like to keep those explicit in the
definitions.
The following keywords are recognized:
:after PACKAGE-OR-FEATURE .. wrap key definition in `with-eval-after-load'
:map KEYMAP .. define key in KEYMAP instead of `acdw/map'"
(let* ((after (plist-get args :after))
(map (or (plist-get args :map) 'acdw/map))
(key (if (vectorp key) key `(kbd ,key)))
(def-key `(define-key ,map ,key ,def)))
(if after
`(with-eval-after-load ,after
,def-key)
def-key)))
(defmacro acdw/bind-after-map (feature keymap bindings)
"Wrap multiple calls to `acdw/bind' in a `with-eval-after-load' form.
FEATURE is the argument to `with-eval-after-load'. KEYMAP is
passed to the `:map' argument of `acdw/bind', if it's non-nil."
(declare (indent 2))
(let (bind-list)
(dolist (bind bindings bind-list)
(if keymap
(push `(acdw/bind ,@bind :map ,keymap) bind-list)
(push `(acdw/bind ,@bind) bind-list)))
`(with-eval-after-load ,feature
,@bind-list)))
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
Fast 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)
Scroll margins
(setq-default scroll-margin 0
scroll-conservatively 101 ; if greater than 100 ...
scroll-preserve-screen-position 1)
Enable commands
I think the disabled command feature of Emacs is stupid, especially for a program that values freedom so much.
(setq-default disabled-command-function nil)
CRUX package crux
A collection of generally-useful functions that I don't want to bother including here myself. This is kind of an experiment, to be honest.
(straight-use-package '(crux
:host github
:repo "bbatsov/crux"))
(require 'crux)
A note: I don't do the same with unpackaged (see below, specifically
the Org sections) because it pulls in hydra
and use-package
, et al.
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-autosave-interval 60)
(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-auto-cleanup 'never)
(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
Simple GC munging about
From bling, from … 2016? Maybe this isn't great, but it's one less package so I'm going to try it for now.
(defconst gc-cons-basis (* 800 1000)
"The basis value to which to return after a max jump.
800,000 (800 KB) is Emacs' default.")
(defun hook--gc-cons-maximize ()
"Set `gc-cons-threshold' to the highest possible.
For memory-intensive features."
(setq gc-cons-threshold most-positive-fixnum))
(defun hook--gc-cons-baseline ()
"Return `gc-cons-threshold' to `gc-cons-basis'.
For after memory intensive operations."
(setq gc-cons-threshold gc-cons-basis))
(add-hook 'minibuffer-setup-hook #'hook--gc-cons-maximize)
(add-hook 'minibuffer-exit-hook #'hook--gc-cons-baseline)
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)
Startup time
Just for me to know, and in case I ever want to make it snappier. This function is from Joe Schafer.
As a benchmark, on Windows, emacs -Q
starts up in 0.188585 seconds, and
emacs -q
starts in 0.373297.
(defun hook--message-startup-time ()
"Message Emacs' startup time."
(message "Emacs ready in %s with %d garbage collections."
(format "%.2f seconds"
(float-time (time-subtract after-init-time
before-init-time)))
gcs-done))
(add-hook 'emacs-startup-hook #'hook--message-startup-time)
Files
Encoding
UTF-8
It's 2020. Let's encode files like it is.
(set-charset-priority 'unicode)
(set-language-environment "UTF-8")
(prefer-coding-system 'utf-8-unix)
(set-default-coding-systems 'utf-8-unix)
(set-terminal-coding-system 'utf-8-unix)
(set-keyboard-coding-system 'utf-8-unix)
(set-selection-coding-system 'utf-8-unix)
(setq-default locale-coding-system 'utf-8-unix
coding-system-for-read 'utf-8-unix
coding-system-for-write 'utf-8-unix
buffer-file-coding-system 'utf-8-unix
org-export-coding-system 'utf-8-unix
org-html-coding-system 'utf-8-unix ; doesn't take from above
default-process-coding-system '(utf-8-unix . utf-8-unix)
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)
Lockfiles
I don't think these are really necessary as of now.
(setq-default create-lockfiles nil)
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
Fill-column
(setq-default fill-column 80)
I also want to display the fill-column:
(global-display-fill-column-indicator-mode +1)
By default, Emacs uses C-x f
to set the fill-column
. I think it's
pretty dumb that such an easy-to-reach binding (for Emacs) is set to a function
that I literally never use. So I'm going to bind it to find-file
… since
that's the only time I accidentally call it, anyway.
(acdw/bind "C-x f" #'find-file)
Auto-fill vs. Visual-line
- Enable
auto-fill-mode
with text modes.
(add-hook 'text-mode-hook #'auto-fill-mode)
2a. Enable visual-line-mode
everywhere. I do this because when I'm typing a
long line, I don't want it to accidentally push my viewport out to the right. I
want the text to invisibly wrap, and then wrap "in the real world," as it were.
(global-visual-line-mode +1)
2b. 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.
(blackout 'auto-fill-mode)
Visual fill column mode
First: fix scrolling in margins
This has to be done before loading the package. It's included in
visual-fill-column
, too, but for some reason isn't loaded there.
(dolist (margin '(right-margin left-margin))
(dolist (button '(mouse-1 mouse-2 mouse-3))
(global-set-key (vector margin button)
(global-key-binding (vector button)))))
(mouse-wheel-mode +1)
(when (bound-and-true-p mouse-wheel-mode)
(dolist (margin '(right-margin left-margin))
(dolist (event '(mouse-wheel-down-event
mouse-wheel-up-event
wheel-down
wheel-up
mouse-4
mouse-5))
(global-set-key (vector margin event) #'mwheel-scroll))))
Then: visual-fill-column
package
In reading-intensive views, this mode keeps the text from getting too wide.
(straight-use-package 'visual-fill-column)
(setq-default visual-fill-column-center-text t)
(add-hook 'visual-fill-column-mode-hook #'visual-line-mode)
(with-eval-after-load 'visual-fill-column
(advice-add 'text-scale-adjust :after #'visual-fill-column-adjust))
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)
USE TABs
I love TABs. They're great. I want to use them, dag nab it. Emacs sometimes makes that harder than it should be, but let's start at the basics.
(setq-default indent-tabs-mode t
tab-width 8)
Smart tabs package
(straight-use-package 'smart-tabs-mode)
(smart-tabs-insinuate 'c 'c++ 'java 'javascript 'cperl 'python 'ruby 'nxml)
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)
Kill the line if there is no region crux
(crux-with-region-or-line kill-ring-save)
(crux-with-region-or-line kill-region)
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
see also
- Gnu Emacs' Transient Mark mode, Sean Whitton
Repeat popping the mark without repeating the prefix argument
(setq-default set-mark-repeat-command-pop t)
The Region
Expand region package
(straight-use-package 'expand-region)
(acdw/bind "C-=" #'er/expand-region)
Pulse the modified region with goggles
(straight-use-package 'goggles)
(defun fix--goggles-mode ()
"Fix goggles-mode to blackout the lighter."
(goggles-mode)
(blackout 'goggles-mode))
(add-hook 'text-mode-hook #'fix--goggles-mode)
(add-hook 'prog-mode-hook #'fix--goggles-mode)
Undo package
Undo Fu
(straight-use-package 'undo-fu)
(acdw/bind "C-/" #'undo-fu-only-undo)
(acdw/bind "C-?" #'undo-fu-only-redo)
Undo Fu session
I'm not putting this in /acdw/emacs/src/commit/0ea7241c664644302938a42810cbc8bcc15d8977/*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 package
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.
Isearch
I want to search by regexp by default.
(define-key acdw/map (kbd "C-s") #'isearch-forward-regexp)
(define-key acdw/map (kbd "C-r") #'isearch-backward-regexp)
(define-key acdw/map (kbd "C-M-s") #'isearch-forward)
(define-key acdw/map (kbd "C-M-r") #'isearch-backward)
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))))
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.
(acdw/bind-after-map 'anzu nil
(([remap query-replace] #'anzu-query-replace-regexp)
([remap query-replace-regexp] #'anzu-query-replace)
([remap isearch-query-replace] #'anzu-isearch-query-replace
:map isearch-mode-map)
([remap isearch-query-replace-regexp] #'anzu-isearch-query-replace-regexp
:map isearch-mode-map)))
Commenting crux
I don't think the default M-;
(M-x comment-dwim
) binding makes sense.
I want it to comment out the region or line, or uncomment it if it's
already commented. That's it.
(crux-with-region-or-line comment-or-uncomment-region)
(acdw/bind "M-;" #'comment-or-uncomment-region)
Goto address mode
"Buttonize URLs and Email addresses."
(when (fboundp 'global-goto-address-mode)
(global-goto-address-mode +1))
Writing
Word count
Key binding
I just found out that M-=
counts the words in a region. That's great, but I
often want to count the words in the whole buffer.
(acdw/bind "M-=" #'count-words)
Spell checking
Settings
Let's use hunspell
.
(with-eval-after-load "ispell"
(setenv "LANG" "en_US")
(setq-default ispell-program-name "hunspell"
ispell-dictionary "en_US")
(ispell-set-spellchecker-params))
(setq ispell-personal-dictionary "~/.hunspell_personal")
(unless (file-exists-p ispell-personal-dictionary)
(write-region "" nil ispell-personal-dictionary nil 0))
Flyspell
(add-hook 'text-mode-hook #'flyspell-mode)
(add-hook 'prog-mode-hook #'flyspell-prog-mode)
(blackout 'flyspell-mode)
Flyspell-correct package
Display corrections with completing-read
.
(straight-use-package 'flyspell-correct)
(acdw/bind "C-;" #'flyspell-correct-wrapper
:after 'flyspell
:map flyspell-mode-map)
Reading
Smooth-scrolling of images package
(straight-use-package 'iscroll)
(add-hook 'text-mode-hook #'iscroll-mode)
(add-hook 'iscroll-mode-hook
#'(lambda () (blackout 'iscroll-mode)))
Reading mode
A custom mode to make reading comfy
(define-minor-mode acdw/reading-mode
"Make reading comfier."
:lighter " Read" ; later: book emoji
(if acdw/reading-mode
(progn ;; turn on
(text-scale-increase +1)
(visual-fill-column-mode +1)
(iscroll-mode +1)
(display-fill-column-indicator-mode -1))
(progn ;; turn off
(text-scale-increase 0)
(visual-fill-column-mode -1)
(iscroll-mode -1)
(display-fill-column-indicator-mode +1))))
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
show-paren-when-point-inside-paren t
show-paren-when-point-in-periphery t)
COMMENT Smart parentheses package
(straight-use-package '(smartparens
:host github
:repo "Fuco1/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)
Enable in programming modes
(add-hook 'prog-mode-hook #'smartparens-mode)
(dolist (hook '(lisp-mode-hook
emacs-lisp-mode-hook))
(add-hook hook #'smartparens-strict-mode))
Use paredit
bindings
(setq-default sp-base-keybindings 'paredit)
(with-eval-after-load 'smartparens
(sp-use-paredit-bindings))
Electric pairs
smart-parens
is acting up on me, and Emacs has an included mode to do pretty
much everything I do with smart-parens
– electric-pair-mode
. Let's try it.
(add-hook 'prog-mode-hook #'electric-pair-local-mode)
Formatting
Aggressive indent package
(straight-use-package 'aggressive-indent)
(global-aggressive-indent-mode +1)
(blackout 'aggressive-indent-mode)
Since aggressive indenting takes care of tabs, I can use <TAB>
to complete
things!
(setq-default tab-always-indent nil)
Reformatter package
Steve Purcell's automatic reformatting tool.
(straight-use-package 'reformatter)
(require 'reformatter)
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)
Compilation
(setq-default compilation-ask-about-save nil ; just save
compilation-always-kill t ; kill the old processes
compilation-scroll-output 'first-error)
(acdw/bind "<f5>" #'recompile)
Language-specific
Generic-x
from u/Bodertz, apparently generic-x
just … has syntax highlighting for a ton
of (I suppose) generic files.
(require 'generic-x)
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))
Web
(straight-use-package 'web-mode)
(require 'web-mode)
(add-to-list 'auto-mode-alist '("\\.phtml\\'" . web-mode))
(add-to-list 'auto-mode-alist '("\\.tpl\\.php\\'" . web-mode))
(add-to-list 'auto-mode-alist '("\\.[agj]sp\\'" . web-mode))
(add-to-list 'auto-mode-alist '("\\.as[cp]x\\'" . web-mode))
(add-to-list 'auto-mode-alist '("\\.erb\\'" . web-mode))
(add-to-list 'auto-mode-alist '("\\.mustache\\'" . web-mode))
(add-to-list 'auto-mode-alist '("\\.djhtml\\'" . web-mode))
(add-to-list 'auto-mode-alist '("\\.html?\\'" . web-mode))
(setq-default web-mode-enable-current-element-highlight t)
i3 config
I use i3 … for now. But I only want to load the relevant mode if I have i3 installed.
(when (executable-find "i3")
(straight-use-package 'i3wm-config-mode))
Shell scripts
Shellcheck
(straight-use-package 'flymake-shellcheck)
(autoload 'flymake-shellcheck-load "flymake-shellcheck")
(add-hook 'sh-mode-hook #'flymake-shellcheck-load)
Formatting
(when (executable-find "shfmt")
(reformatter-define sh-format
:program "shfmt"
:lighter "Shfmt")
(add-hook 'sh-mode-hook #'sh-format-on-save-mode))
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")
SHR
(setq-default shr-max-width fill-column
shr-width fill-column)
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 "-AFgho --group-directories-first"
dired-dwim-target t)
(acdw/bind "C-x C-j" #'dired-jump)
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)
(acdw/bind "i" #'dired-subtree-toggle :after 'dired :map dired-mode-map)
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)
(acdw/bind "g" #'magit-status :map acdw/leader)
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 store
Okay, okay, using the store
credential.helper is super insecure. But
here's the thing – the Gits I Git at work (a) aren't my real git, and
(b) they're just tokens! So any time I think somebody got access, I
just revoke the tokens and bingo bongo, good to go. If that's not
true, please feel free to hack this repo and change this paragraph.
(setenv "GIT_ASKPASS" "git-gui--askpass")
Forge package
(straight-use-package 'forge)
(with-eval-after-load 'magit
(require 'forge))
Git file modes package
(dolist (feat '(gitattributes-mode
gitconfig-mode
gitignore-mode))
(straight-use-package feat)
(require feat))
Crosswords! package
I love crosswords. I love Emacs. There ya go.
(straight-use-package '(crossword
:host github
:repo "Boruch-Baum/emacs-crossword"))
(setq-default crossword-empty-position-char "#")
(setq-default crossword-save-path
(no-littering-expand-var-file-name "crosswords/"))
(unless (file-exists-p crossword-save-path)
(make-directory crossword-save-path :parents))
(defun hook--setup-crossword ()
(setq cursor-type 'hbar))
(add-hook 'crossword-mode-hook #'hook--setup-crossword)
The problem with this package is that the default faces are pretty bad, to be honest. Let's change em.
(doremi-face-set 'crossword-current-face
'((((class color)
(background light))
(:inherit 'normal :foreground "black"
:background "lightgreen"))
(((class color)
(background dark))
(:inherit 'normal :foreground "white"
:background "darkgreen"))
(t
(:inherit 'normal :foreground "black"
:background "darkgreen"))))
(doremi-face-set 'crossword-other-dir-face
'((((class color)
(background light))
(:inherit 'normal :foreground "black"
:background "darkgrey"))
(((class color)
(background dark))
(:inherit 'normal :foreground "black"
:background "darkgrey"))
(t
(:inherit 'normal :foreground "black"
:background "darkgrey"))))
TODO Gnus
See this guide and try it out.
RSS Feeds package
Elfeed
;; first, "fix" org-version
(with-eval-after-load 'org
(defun org-version ()
"Fix of `org-version' for `elfeed'.
I don't know what the actual version is, but 9.5 should have us
covered. It's somewhere past 9."
"9.5")
(straight-use-package 'elfeed))
(setq-default elfeed-db-directory
(expand-file-name "elfeed/db"
(or (getenv "XDG_CACHE_HOME")
"~/.cache")))
(add-hook 'elfeed-show-mode-hook #'acdw/reading-mode)
(acdw/bind "f" #'elfeed :map acdw/leader)
Elfeed feeds
(setq elfeed-feeds
`(
("https://computer.rip/rss.xml" tech newsletter)
("https://weather-broker-cdn.api.bbci.co.uk/en/forecast/rss/3day/4315588" weather)
("https://www.realbakingwithrose.com/month?format=rss" food)
("https://xfnw.tilde.institute/sandcats/feed.rss" fwend pix)
("https://www.makeworld.gq/feed.xml" blag)
("https://whyarentyoucoding.com/feed.xml" comix)
("https://xkcd.com/atom.xml" comix)
("https://falseknees.com/rss.xml" comix)
("https://chrisman.github.io/rss.xml" fwend dozens blag)
("https://tilde.team/~dozens/dreams/rss.xml" fwend dozens blag)
("https://society.neocities.org/rss.xml" fwend dozens)
("https://supervegan.neocities.org/feed.xml" fwend dozens food)
("https://blog.astrosnail.pt.eu.org/feed.atom" tech blag)
("https://www.greghendershott.com/feeds/all.atom.xml" tech blag)
("https://hans.gerwitz.com/feeds/writing.rss" fwend)
("http://planet.lisp.org/rss20.xml" tech lisp)
("https://wflewis.com/feed.xml" blag fwend)
("HTTPS://atthis.link/rss.xml" blag tech)
("https://rachelbythebay.com/w/atom.xml" blag tech)
("https://notes.neeasade.net/rss_full.xml" blag tech)
("https://www.uninformativ.de/blog/feeds/en.atom" blag tech)
("http://blog.z3bra.org/rss/feed.xml" blag tech)
("https://blog.sanctum.geek.nz/feed/" blag tech)
("https://drewdevault.com/blog/index.xml" blag tech)
("https://usesthis.com/feed.atom" tech)
("https://occasionallycogent.com/feed.xml" blag)
("https://www.murilopereira.com/index.xml" blag tech)
("https://botanistinthekitchen.blog/feed/" blag food)
("https://www.careercenterbr.com/feed/" work)
("https://blog.ebrpl.com/feed/" work)
(,(concat ; long-ass url
"https://lemmy.ml/feeds/front/"
"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9."
"eyJpZCI6MTY4MjQsImlzcyI6ImxlbW15Lm1sIn0"
".yE2zUGjvlEuTZZi3TiF9HR7L7ITM9f_"
"fQnquyYLgdJ4.xml?sort=Active")
news)
("https://lobste.rs/rss" news tech)
("https://feeds.npr.org/1001/rss.xml" news)
(,(concat ; long-ass url
"https://tilde.news/rss?token="
"FvdFj8rQkhrBy9j1yON1t6RYKDdcuG1MoUlyvRICmbgDGCf2JTWAEObDhdgt")
news tildes tech)
("https://www.acdw.net/atom.xml" fwend)
("https://envs.net/~lucidiot/rsrsss/feed.xml" fwend)
("https://planet.emacslife.com/atom.xml" emacs tech)
("http://tilde.town/~lucidiot/fridaypostcard.xml" tildes art)
("https://quasivoid.net/feed.atom" tildes)
("https://benjaminwil.info/feed.xml" tildes fwend)
("https://benjaminwil.info/antisocial/feed.xml" tildes)
("https://blog.ryliejamesthomas.net/feed/" tildes)
("https://p1k3.com/feed" tildes)
("https://cosmic.voyage/rss.xml" tildes fiction sci-fi)
("https://jackreid.xyz/index.xml" tildes)
("http://lambdacreate.com/static/feed.rss" tildes fwend)
("https://gaffen.co.uk/feed.xml" tildes)
("https://gmb.21x2.net/rss.xml" tildes)
("https://www.insom.me.uk/feed.xml" tildes)
("https://invisibleup.com/atom.xml" tildes)
("https://m455.casa/feed.rss" tildes fwend)
("https://petras.space/index.xml" tildes)
("https://www.benovermyer.com/post/index.xml" tildes)
("https://tilde.town/~trm/blog/atom.xml" tildes)
("https://tilde.team/feed.rss" tildes)
("http://ajroach42.com/feed.xml" tildes)
("http://tilde.town/~mroik/blog/rss.xml" tildes)
("https://hamster.dance/blog/rss/" tildes)
("https://m455.neocities.org/feed.rss" tildes fwend)
("https://eli.li/feed.rss" tildes fwend)
("https://aiweirdness.com/rss" tech)
("http://tilde.town/~m455/javapool.rss" tilde)
("https://spwhitton.name/blog/index.rss" blag)
(,(concat "https://www.theadvocate.com/search/?"
;; Let's Build A URL!!!
"f=rss" ; RSS feed
"&l=10" ; 10 most recent (length)
"&c[]=" ; I'm guessing ... categories?
"baton_rouge/news*,"
"baton_rouge/opinion*"
"?t=article" ; type=article
)
news)
("https://esoteric.codes/rss" tech)
("https://wphicks.github.io/feed.xml" blag)
))
0x0 (null pointer) package
An ease-of-life package that lets me upload my shitty code to share it with others.
(straight-use-package '(0x0
:repo "https://git.sr.ht/~zge/nullpointer-emacs"))
(setq-default 0x0-default-service 'ttm)
Gemini/gopher
Elpher package
(straight-use-package '(elpher
:repo "git://thelambdalab.xyz/elpher.git"))
(setq-default elpher-ipv4-always t)
(doremi-face-set 'elpher-gemini-heading1
'((t (:inherit (modus-theme-heading-1)
:height 1.0))))
(doremi-face-set 'elpher-gemini-heading2
'((t (:inherit (modus-theme-heading-2)
:height 1.0))))
(doremi-face-set 'elpher-gemini-heading3
'((t (:inherit (modus-theme-heading-3)
:height 1.0))))
(setq-default elpher-certificate-directory
(no-littering-expand-var-file-name
"elpher-certificates/"))
(acdw/bind-after-map 'elpher elpher-mode-map
(("n" #'elpher-next-link)
("p" #'elpher-prev-link)
("o" #'elpher-follow-current-link)
("G" #'elpher-go-current)))
(add-hook 'elpher-mode-hook #'acdw/reading-mode)
Gemini-mode package
(straight-use-package '(gemini-mode
:repo "https://git.carcosa.net/jmcbray/gemini.el.git"))
(add-to-list 'auto-mode-alist
'("\\.\\(gemini\\|gmi\\)\\'" . gemini-mode))
(doremi-face-set 'gemini-heading-face-1
'((t (:inherit (elpher-gemini-heading1)))))
(doremi-face-set 'gemini-heading-face2
'((t (:inherit (elpher-gemini-heading2)))))
(doremi-face-set 'gemini-heading-face3
'((t (:inherit (elpher-gemini-heading3)))))
Gemini-write package
(straight-use-package '(gemini-write
:repo "https://alexschroeder.ch/cgit/gemini-write"
:fork (:repo "https://tildegit.org/acdw/gemini-write"
:branch "main")))
(with-eval-after-load 'elpher
(require 'gemini-write))
Eshell
I use eshell
with Emacs, because it works both on Windows and Linux.
Open an eshell or bury its buffer
adapted from zge's setup, which might also be an interesting macro to look into one day.
(defun acdw/eshell-or-bury ()
"Start an `eshell', or bury its buffer if focused."
(interactive)
(if (string= (buffer-name) "*eshell*") ;; XXX: brittle
(bury-buffer)
(eshell)))
(define-key acdw/leader "s" #'acdw/eshell-or-bury)
E-books with nov.el package
I love the name of this package.
(straight-use-package 'nov)
(setq-default nov-text-width t)
(add-to-list 'auto-mode-alist '("\\.epub\\'" . nov-mode))
(add-hook 'nov-mode-hook #'acdw/reading-mode)
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
org-tags-column (- 0 fill-column -3)
;; Source blocks
org-src-tab-acts-natively t
org-src-window-setup 'current-window ; the least stupid option
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
org-imenu-depth 8
;; Exporting
org-export-headline-levels 8
org-export-with-smart-quotes t
org-export-with-sub-superscripts t)
Aesthetics
Prettify some other symbols
(defun acdw/org-mode-prettify ()
"Prettify `org-mode'."
(dolist (cell '(("[ ]" . ?☐) ("[X]" . ?☑) ("[-]" . ?◐)
("#+BEGIN_SRC" . ?✎) ("#+begin_src" . ?✎)
("#+BEGIN_QUOTE" . ?❝) ("#+begin_quote" . ?❝)
("#+END_QUOTE" . ?❞) ("#+end_quote" . ?❞)
("#+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)
Prettify lists and checkboxes using font-lock
from Furkan Karataş.
(with-eval-after-load 'org
(font-lock-add-keywords 'org-mode
'(("^ *\\([-]\\) "
(0 (prog1 ()
(compose-region (match-beginning 1) (match-end 1) "•"))))))
(font-lock-add-keywords 'org-mode
'(("^ *\\([+]\\) "
(0 (prog1 ()
(compose-region (match-beginning 1) (match-end 1) "◦"))))))
(defface org-checkbox-done-text
'((t (:inherit 'font-lock-comment-face :slant normal)))
"Face for the text part of a checked org-mode checkbox."
:group 'org)
(font-lock-add-keywords
'org-mode
`(("^[ \t]*\\(?:[-+*]\\|[0-9]+[).]\\)[ \t]+\\(\\(?:\\[@\\(?:start:\\)?[0-9]+\\][ \t]*\\)?\\[\\(?:X\\|\\([0-9]+\\)/\\2\\)\\][^\n]*\n\\)"
1 'org-checkbox-done-text prepend))
'append))
Org-appear package
(straight-use-package '(org-appear
:host github
:repo "awth13/org-appear"))
(setq-default org-appear-autoemphasis t
org-appear-autolinks nil
org-appear-autosubmarkers t)
(add-hook 'org-mode-hook #'org-appear-mode)
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
(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.
;; FURTHER NOTE: Ideally, I would follow links unless point
;; /appeared/ to be at the end of the line (even if it's still
;; inside the link) -- when it would do `org-return'. That
;; would take some /doing/, however.
;; ((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))))
;; Element in list item, e.g. a link
(unpackaged/org-element-descendant-of 'item context))
;; 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
(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)
Org Agenda
I'm trying to organize my life. Inspo:
Basic Agenda Settings
(setq-default org-agenda-files ; look for files in ~/org
(list org-directory)
;; agenda
org-agenda-span 5
org-agenda-skip-scheduled-if-done t
org-agenda-skip-deadline-if-done t
org-deadline-warning-days 2
;; logging
org-log-into-drawer "LOGBOOK"
org-log-done t
;; archive
org-archive-location (concat (expand-file-name ".archive.org"
org-directory)
"::"))
(define-key acdw/leader (kbd "C-a") #'org-agenda)
Agenda hooks
(defun hook--org-agenda-mode ()
(hl-line-mode +1))
(add-hook 'org-agenda-mode-hook #'hook--org-agenda-mode)
Refile
(setq org-refile-targets '((org-agenda-files . (:maxlevel . 3))))
Calendar settings
I'm not sure where else to put these, to be honest.
(setq-default calendar-date-style 'iso) ; YYYY-mm-dd
Habits
Org can track habits! Great stuff. I need to add it to org-modules
,
though.
(add-to-list 'org-modules 'org-habit)
Now I just add a habit
property to a subtree, and BAM!
Org Todo Keywords
These need some more thinking – e.g., the MEETING sequence should maybe be a type, or maybe I need to include those in something else altogether. Hm.
(setq-default org-todo-keywords
'((sequence
"TODO(t)" "STARTED(s)"
"WAITING(w@/!)" "SOMEDAY(.)"
"|" "DONE(x!)" "CANCELLED(c@/!)")
(sequence "RECUR(r)" "|" "DONE(x!)")
(sequence "MEETING(m)")))
Org Capture
(require 'org-capture)
(with-eval-after-load 'org-capture
(define-key acdw/leader (kbd "C-c") #'org-capture))
I've still got a lot of thinking to do about what kinds of things I want to capture, but I guess for now I can start with the basics: TODO, and Diary.
(defvar acdw/org-inbox-file (expand-file-name "inbox.org" org-directory))
(defvar acdw/org-diary-file (expand-file-name "diary.org" org-directory))
(setq-default
org-capture-templates
`(;; Todo -- these go to the Inbox for further processing
("t" "Quick Task" entry
(file ,acdw/org-inbox-file)
"* TODO %^{Task}\n"
:immediate-finish t)
("T" "Task" entry
(file ,acdw/org-inbox-file)
"* TODO %^{Task}\n")
("." "Today" entry
(file ,acdw/org-inbox-file)
"* TODO %^{Task}\nSCHEDULED: %t\n"
:immediate-finish t)
;; Meetings
("m" "Meeting" entry
(file ,acdw/org-inbox-file)
"* MEETING %^{Meeting}\n%^T\n\n%?")
;; Diary -- for the special Diary file
("j" "Diary entry" entry
(file+olp+datetree ,acdw/org-diary-file)
"* %U\n\n%?"
:empty-lines 1)
;; Books to read
("b" "Book to read" entry
(file+headline "books.org" "Later")
"* TOREAD %^{Title}
:PROPERTIES:
:Author: %^{Author}
:END:\n" :immediate-finish t)))
Org auto tangle package
(straight-use-package 'org-auto-tangle)
(require 'org-auto-tangle)
(add-hook 'org-mode-hook #'org-auto-tangle-mode)
(blackout 'org-auto-tangle-mode)
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 "exe/bin" 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)
(expand-file-name "Z/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))
References
- exec-path and $PATH (StackExchange)
- Difference between Emacs's "(getenv PATH)" and "exec-path" (U&I)
- GUI Emacs sets the exec-path only from Windows environment variable but not from .emacs file (StackExchange)
Disable package.el
(setq package-enable-at-startup nil)
Bootstrap straight.el
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))))
Ignore straight/build/
(add-to-list 'recentf-exclude (expand-file-name "straight/build/"
user-emacs-directory))
System integration
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.
All systems
I'll put generic system-integrating code here.
Edit with Emacs package
Install the Firefox Addon alongside this package to edit web forms in Emacs (or the Chrome one if you… hate freedom :P).
(straight-use-package 'edit-server)
(add-hook 'after-init-hook #'edit-server-start)
git-sync
stuff
This function require git-sync.
(defun acdw/git-sync (directory)
"Run git-sync in DIRECTORY."
(interactive)
(message "Git-Syncing %s..." directory)
(let ((proc (start-process "git-sync"
(get-buffer-create (format "*git-sync:%s*" directory))
"git" "-C" (expand-file-name directory) "sync")))
(add-function :after (process-sentinel proc)
(lambda (proc ev)
(cond
((string-match "finished\n\\'" ev)
(message "Git-Syncing %s...Done." directory)))))))
~/org
(defun acdw/git-sync-org ()
"Run `acdw/git-sync' on `org-directory'."
(interactive)
(acdw/git-sync org-directory))
(define-key acdw/leader (kbd "C-M-o") #'acdw/git-sync-org)
~/.cache/elfeed/db
(defun acdw/git-sync-elfeed-db ()
"Run `acdw/git-sync' on `elfeed-db-directory'."
(interactive)
(save-some-buffers :no-query nil)
(acdw/git-sync elfeed-db-directory))
(define-key acdw/leader (kbd "C-M-f") #'acdw/git-sync-elfeed-db)
Passwords
Password cache
(setq-default password-cache-expiry nil)
Use ~/.authinfo
for passwords
The auth-info
line should look like this:
machine git.example.com user acdw password hahayeahrightyamoroniwouldn'tgiveyouthat
(autoload 'magit-process-password-auth-source "magit")
(add-hook 'magit-process-find-password-functions
#'magit-process-password-auth-source)
TRAMP
It stands for … something kind of stupid, I don't remember. I'm pulling this
from Spartan Emacs. It recommends the following in ~/.ssh/config
:
Host * ForwardAgent yes AddKeysToAgent yes ControlMaster auto ControlPath ~/.ssh/master-%r@%h:%p ControlPersist yes ServerAliveInterval 10 ServerAliveCountMax 10
(setq-default tramp-default-method "ssh"
tramp-copy-size-limit nil
tramp-use-ssh-controlmaster-options nil
tramp-default-remote-shell "/bin/bash")
Linux (home)
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. Many of the ideas and scripts in this section come from termitereform on Github.
Environment variables
DICPATH, for Hunspell
(setenv "DICPATH" (expand-file-name "exe/share/hunspell"
"~/Applications/"))
Settings
See also the GNU FAQ for Windows. At some point I should really dig into the multiple settings available for w32 systems.
See also the Prelude Windows configuration (modifier keys).
(setq-default w32-allow-system-shell t ; enable cmd.exe as shell
;; modifier keys
w32-pass-lwindow-to-system nil
w32-lwindow-modifier 'super
w32-pass-rwindow-to-system nil
w32-rwindow-modifier 'super
w32-pass-apps-to-system nil
w32-apps-modifier 'hyper)
Scripts
Common variables
if not exist %HOME% (set HOME=%~dp0..\..)
set EMACS=%HOME%\Applications\Emacs\bin\runemacs.exe
set EMACSC=%HOME%\Applications\Emacs\bin\emacsclientw.exe
set GIT=%HOME%\Applications\Git\bin\git.exe
Emacs Autostart
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>>
REM git pull the config
start "git" /wait %GIT% -C %HOME%\.emacs.d pull
REM start emacs
chdir %HOME%
start "emacs-daemon" /wait %EMACS% --daemon
start "emacs-client" %EMACSC% -n -c -a "" %*
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>>
start "emacs" "%EMACSC%" -n -c -a "%EMACS%" %*
Emacs Daemon
<<w32-bat-common>>
start "emacs-daemon" %EMACS% --daemon
Emacs Safe Start
This runs Emacs with the factory settings.
<<w32-bat-common>>
start "emacs-safe" "%EMACS%" -Q %*
Emacs Debug
This runs Emacs with the --debug-init
option enabled.
<<w32-bat-common>>
start "emacs-debug" "%EMACS%" --debug-init %*
Other configuration files
Since I kind of have this weird .. portable setup on Windows, I'm going to start putting other config files in here that I need. It's a real pain in the ass to have them all out of sync.
~/.gitconfig
# tangled from ~/.emacs.d/config.org
[github]
user = duckwork
[credential]
helper = store
[user]
name = Case Duckworth
email = acdw@acdw.net
[pull]
rebase = false
[core]
excludesfile = ~/.gitignore
attributesfile = ~/.gitattributes
autocrlf = false
eol = lf
[diff "lisp"]
# basic
#xfuncname = "^(\\(.*)$"
# advanced
xfuncname = "^(((;;;+ )|\\(|([ \t]+\\(((cl-|el-patch-)?def(un|var|macro|method|custom)|gb/))).*)$"
[diff "org"]
xfuncname = "^(\\*+ +.*)$"
[url "https://github.com/"]
insteadOf = gh:
pushInsteadOf = gh:
[url "https://gitlab.com/"]
insteadOf = gl:
pushInsteadOf = gl:
[url "https://git.sr.ht/"]
insteadOf = sr:
pushInsteadOf = sr:
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>>
;;; VARIABLES
<<variables>>
;;; 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
Ease of editing
(defun acdw/find-config ()
"Find `config.org'."
(interactive)
(find-file (locate-user-emacs-file "config.org")))
Bind it to C-z i
because C-z C-c
is taken for capture.
(define-key acdw/leader (kbd "i") #'acdw/find-config)
Ease of reloading
(defun acdw/reload ()
"Tangle and reload Emacs configuration."
(interactive)
(let ((config (locate-user-emacs-file "config.org")))
;; tangle
(with-current-buffer (find-file-noselect config)
(message "Tangling config.org...")
(let ((prog-mode-hook nil)
(inhibit-redisplay t)
(inhibit-message t))
(add-to-list 'load-path (locate-user-emacs-file
"straight/build/org/"))
(require 'org)
(org-babel-tangle)))
(message "Tangling config.org... Done.")
;; load init files
(load (locate-user-emacs-file "early-init.el"))
(load (locate-user-emacs-file "init.el"))
(load (locate-user-emacs-file "config.el"))))
(define-key acdw/leader (kbd "C-M-r") #'acdw/reload)
init.el
The classic Emacs initiation file.
Header
;;; init.el -*- lexical-binding: t; coding: utf-8 -*-
<<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!
(message "%s..." "Loading init.el")
(let* (;; Speed up init
(gc-cons-threshold most-positive-fixnum)
;; (gc-cons-percentage 0.6)
(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 (locate-user-emacs-file "straight/build/org")))
;; 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
(message "%s..." "Loading config.org")
(require 'org)
(org-babel-load-file config.org)
(message "%s... Done" "Loading config.org")))
(message "%s... Done." "Loading init.el")
;;; 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; coding: utf-8 -*-
<<disclaimer>>
;;; Code:
(message "%s..." "Loading early-init.el")
;; BOOTSTRAP PACKAGE MANAGEMENT
<<early-init-package>>
;; SETUP FRAME
<<early-init-frame>>
(message "%s... Done." "Loading early-init.el")
;;; 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!