emacs/init.el

2020 lines
70 KiB
EmacsLisp
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

;;; init.el -*- lexical-binding: t; coding: utf-8-unix -*-
;; Author: Case Duckworth <acdw@acdw.net>
;; Created: Sometime during Covid-19, 2020
;; Keywords: configuration
;; URL: https://tildegit.org/acdw/emacs
;; Bankruptcy: 7
;; This file is NOT part of GNU Emacs.
;;; License:
;; Everyone is permitted to do whatever with this software, without
;; limitation. This software comes without any warranty whatsoever,
;; but with two pieces of advice:
;; - Don't hurt yourself.
;; - Make good choices.
;;; Code:
;; NOTE that some of the names in `setup' forms are arbitrary.
(setup (:straight (0x0
:host gitlab
:repo "willvaughn/emacs-0x0"))
(:option 0x0-default-server 'ttm))
(setup Info
(:hook variable-pitch-mode
acdw/reading-mode))
(setup abbrev
(:option abbrev-file-name "~/Sync/abbrev.el"
save-abbrevs 'silent)
(:hook-into text-mode
circe-chat-mode))
(setup acdw
(:also-load acdw-compat
acdw-lisp)
(acdw/require-private)
(:option user-full-name "Case Duckworth"
user-mail-address "acdw@acdw.net")
(when-let ((default-directory
(expand-file-name-exists-p "pkg/" user-emacs-directory)))
(normal-top-level-add-subdirs-to-load-path))
(defun acdw/sort-setups ()
"Sort `setup' forms in the current buffer.
Actually sorts all forms, but based on the logic of `setup'.
AKA, DO NOT USE THIS FUNCTION!!!"
(save-excursion
(sort-sexps (point-min) (point-max)
(lambda (sexp)
(let ((name (cadr sexp)))
(symbol-name (if (listp name) ; this is /terrible/!
(if (keywordp (car name))
(let ((feature (cadr name)))
(if (listp feature)
(car feature)
feature))
(car name))
name))))))))
(setup (:straight-if affe
(and (or (executable-find "fd")
(executable-find "find"))
(executable-find "rg")))
;; Keys are bound in `acdw/sensible-grep' and `acdw/sensible-find'
(:option affe-regexp-compiler
(defun affe-orderless-regexp-compiler (input _type)
(setq input (orderless-pattern-compiler input))
(cons input (lambda (str) (orderless--highlight input str))))))
(setup (:straight-if ahk-mode
(acdw/system :work)))
(setup (:straight alert)
(:option alert-default-style (acdw/system
(:home 'libnotify)
(_ 'message))))
(setup (:straight (apheleia
:host github
:repo "raxod502/apheleia"))
(apheleia-global-mode +1)
;; Use a dumb formatter on modes that `apheleia' doesn't work for.
(add-hook 'before-save-hook
(defun before-save@dumb-auto-format ()
(setq stupid-modes '(makefile-mode
org-mode))
;; If there's no apheleia formatter for the mode, just indent the
;; buffer.
(unless (or (apply #'derived-mode-p stupid-modes)
(and (fboundp 'apheleia--get-formatter-command)
(apheleia--get-formatter-command)))
(indent-region (point-min) (point-max))))))
(setup (:straight async)
(dired-async-mode +1)
(:with-feature dired
(:hook (defun dired@disable-dired-async-mode-line ()
(autoload 'dired-async--modeline-mode "dired-async" nil t)
(dired-async--modeline-mode -1)))))
(setup (:require auth-source)
(:option auth-sources '("~/.authinfo" "~/.authinfo.gpg")))
(setup autorevert
(:option global-auto-revert-non-file-buffers t)
(global-auto-revert-mode +1))
(setup (:straight avy)
(:global "C-'" #'avy-goto-char-timer
"C-c C-j" #'avy-resume)
(:with-feature isearch
(:bind "C-'" #'avy-isearch)))
(setup browse-url
(:require acdw-browse-url)
(:option browse-url-secondary-browser-function
(if (executable-find "firefox") ; prefer Firefox
#'browse-url-firefox
#'browse-url-default-browser)
browse-url-new-window-flag nil ; for eww
browse-url-firefox-arguments '("--new-tab") ; for firefox
browse-url-firefox-new-window-is-tab t)
(acdw/browse-url-set-handlers
(list
(cons (rx (seq "." (or "jpeg" "jpg" ; images
"png")
eos))
(lambda (&rest args)
(apply (if (executable-find "feh")
#'browse-url-feh
#'eww-browse-url)
args)))
(cons (rx (or "youtube.com" ; videos
"youtu.be"
(seq "." (or "mp4"
"gif")
eos)))
(lambda (&rest args)
(apply (if (executable-find "mpv")
#'browse-url-mpv
browse-url-secondary-browser-function)
args)))
(cons (rx (or "google.com" ; websites that don't work with eww
"reddit.com"
"twitter.com"
"imgur.com"))
browse-url-secondary-browser-function)
(cons "." ; everything else
#'eww-browse-url)))
;; Buttonize gemini:// links.
(acdw/add-button-url-regexp-protocol "gemini"))
(setup buffers
(:global "C-x k" #'acdw/kill-a-buffer))
(setup calendar
(:option calendar-week-start-day 1))
(setup (:straight circe)
(require 'circe)
(require 'acdw-irc)
(:option acdw-irc/left-margin 12
acdw-irc/post-my-nick "-> "
circe-channel-killed-confirmation nil
circe-color-nicks-everywhere t
circe-default-nick "acdw"
circe-default-part-message "See You, Space Cowpokes . . ."
circe-default-user "acdw"
circe-format-action
(lambda (&rest plist)
(concat
(acdw-irc/margin-format "" "*" "*" t)
" " (plist-get plist :nick) " " (plist-get plist :body)))
circe-format-say
(lambda (&rest plist)
(concat
(acdw-irc/margin-format (plist-get plist :nick) "" " |" t)
" " (plist-get plist :body)))
circe-format-self-action
(lambda (&rest plist)
(concat
(acdw-irc/margin-format "" "-*" " *" t)
" " (plist-get plist :nick) " " (plist-get plist :body)))
circe-format-self-say
(lambda (&rest plist)
(concat
(acdw-irc/margin-format (plist-get plist :nick) "-" " >" t)
" " (plist-get plist :body)))
circe-highlight-nick-type 'sender
circe-network-options
`(("Libera Chat"
:channels ("#emacs" "#systemcrafters" "##webpals")
:sasl-username ,circe-default-nick
:sasl-password ,(acdw/make-password-fetcher
:host "libera.chat"))
("Tilde Chat" :host "irc.tilde.chat" :port 6697 :use-tls t
:channels ("#meta" "#bread" "#dadjokes" "#team"
"#emacs")
:sasl-username ,circe-default-nick
:sasl-password ,(acdw/make-password-fetcher
:host "tilde.chat"))
("Casa" :host "m455.casa" :port 6697 :use-tls t
:channels ("#basement")
:sasl-username ,circe-default-nick
:sasl-password ,(acdw/make-password-fetcher
:host "m455.casa")))
circe-reduce-lurker-spam t
circe-server-auto-join-default-type :after-auth)
(:face circe-nick-highlight-face
((t (:inherit (modus-themes-hl-line)))))
(:bind "C-c C-p" #'circe-command-PART)
(:advise circe-command-PART :after
(defun circe-part@kill-buffer (&rest _)
(let ((circe-channel-killed-confirmation nil))
(kill-buffer)))
circe-command-QUIT :after
(defun circe-quit@kill-buffer (&rest _)
(let ((circe-server-killed-confirmation nil))
(with-circe-server-buffer
(kill-buffer))))
circe-command-GQUIT :after
(defun circe-gquit@kill-buffer (&rest _)
(let ((circe-server-killed-confirmation nil))
(dolist (buf (circe-server-buffers))
(with-current-buffer buf
(kill-buffer))))))
(:with-mode circe-chat-mode
(:hook #'acdw/stop-paren-annoyances
#'enable-circe-color-nicks
#'enable-circe-display-images
#'enable-circe-new-day-notifier
(defun circe-chat@set-prompt ()
(lui-set-prompt
(concat
(propertize
(acdw-irc/margin-format (buffer-name) "" ">")
'face 'circe-prompt-face
'read-only t
'intangible t
'cursor-intangible t)
" ")))))
(autoload 'circe-nick-color-reset "circe-color-nicks")
(add-hook 'modus-themes-after-load-theme-hook
#'circe-nick-color-reset)
(:with-mode lui-mode
(:option lui-fill-column fill-column
lui-fill-type (repeat-string acdw-irc/left-margin " ")
lui-time-stamp-position 'right-margin
lui-time-stamp-format "%H:%M"
lui-track-behavior 'before-switch-to-buffer
lui-track-indicator 'fringe)
(:local-set fringes-outside-margins t
right-margin-width 5
scroll-margin 0
word-wrap t
wrap-prefix (repeat-string acdw-irc/left-margin " ")
line-number-mode nil)
(:hook #'enable-lui-track)))
(setup completion
(:option completion-ignore-case t
read-buffer-completion-ignore-case t
completion-styles '(substring partial-completion)
completion-category-defaults nil
completion-category-overrides
'((file (styles . (partial-completion)))))
(:global "M-/" #'hippie-expand))
(setup (:straight (consult
:host github
:repo "minad/consult"))
(:require acdw-consult)
(:autoload consult-register-preview)
;; Bindings
(:global
;; C-c bindings (`mode-specific-map')
;; I don't use any of these right now.
;; "C-c h" #'consult-history
;; "C-c m" #'consult-mode-command
;; "C-c b" #'consult-bookmark
;; "C-c k" #'consult-kmacro
;; 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
;; Custom M-# bindings for fast register access
"M-#" #'consult-register-load
"M-'" #'consult-register-store
"C-M-#" #'consult-register
;; M-g bindings (`goto-map')
"M-g e" #'consult-compile-error
"M-g g" #'consult-goto-line
"M-g M-g" #'consult-goto-line
"M-g o" #'consult-outline
"M-g m" #'consult-mark
"M-g k" #'consult-global-mark
"M-g i" #'consult-imenu
"M-g I" #'consult-project-imenu
;; M-s bindings (`search-map')
"M-s g" #'acdw-consult/sensible-grep
"M-s f" #'acdw-consult/sensible-find
"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
"<help> a" #'consult-apropos
;; Isearch integration
"M-s e" #'consult-isearch)
(:with-map isearch-mode-map
(:bind "M-e" #'consult-isearch
"M-s e" #'consult-isearch
"M-s l" #'consult-line))
(consult-history-to-modes ((minibuffer-local-map . nil)
(shell-mode-map . shell-mode-hook)
(term-mode-map . term-mode-hook)
(term-raw-map . term-mode-hook)
(comint-mode-map . comint-mode-hook)
(sly-mrepl-mode-map . sly-mrepl-hook)))
(:option register-preview-delay 0
register-preview-function #'consult-register-format
xref-show-xrefs-function #'consult-xref
xref-show-definitions-function #'consult-xref
consult-project-root-function #'vc-root-dir
completion-in-region-function #'acdw-consult/complete-in-region
completion-cycle-threshold 3
tab-always-indent 'complete)
(:advise register-preview :override #'consult-register-window)
;; Completing-read-multple
(if (fboundp #'consult-completing-read-multiple)
(:advise completing-read-multple :override
#'consult-completing-read-multiple)
(:advise completing-read-multiple :filter-args
(defun crm-indicator (args)
(cons (concat "[CRM] " (car args)) (cdr args)))))
(with-eval-after-load 'orderless
(:option consult--regexp-compiler
#'consult--orderless-regexp-compiler))
(with-eval-after-loads (vertico consult)
(:with-map consult-crm-map
(:bind "RET" (defun +vertico-crm-exit ()
(interactive)
(run-at-time 0 nil #'vertico-exit)
(funcall #'vertico-exit))
"TAB" #'vertico-exit))))
(setup (:straight crux)
(:global "C-x o" #'acdw/other-window-or-switch-buffer
"C-o" #'crux-smart-open-line
"M-o" #'crux-smart-open-line-above
"C-M-\\" #'crux-cleanup-buffer-or-region
"C-x 4 t" #'crux-transpose-windows)
(when (fboundp 'repeat-mode)
(defvar other-window-repeat-map (make-sparse-keymap)
"A map for repeating `other-window' keys.")
(define-key other-window-repeat-map "o"
#'acdw/other-window-or-switch-buffer)
(define-key other-window-repeat-map "O"
(defun acdw/other-window-or-switch-buffer-backward ()
(interactive)
(setq repeat-map 'other-window-repeat-map)
(acdw/other-window-or-switch-buffer -1)))
(put 'acdw/other-window-or-switch-buffer
'repeat-map 'other-window-repeat-map))
(crux-reopen-as-root-mode +1))
(setup cursor
(:option cursor-type 'bar
cursor-in-non-selected-windows 'hollow
blink-cursor-blinks 1)
(blink-cursor-mode +1))
(setup cus-edit
(:option custom-file null-device ; don't store customizations
custom-magic-show nil
custom-magic-show-button t
custom-raised-buttons nil
custom-unlispify-tag-names nil
custom-variable-default-form 'lisp)
;; `Custom-mode-hook' fires /before/ the widgets are built, so I have to
;; install advice after the widgets are made.
(:advise custom-buffer-create :after
(defun custom-buffer@expand-widgets (&rest _)
"Expand descriptions in `Custom-mode' buffers."
(interactive)
;; "More/Hide" widgets (thanks alphapapa!)
(widget-map-buttons (lambda (widget _)
(pcase (widget-get widget :off)
("More" (widget-apply-action widget)))
nil))
;; "Show Value" widgets (the little triangles)
(widget-map-buttons (lambda (widget _)
(pcase (widget-get widget :off)
("Show Value"
(widget-apply-action widget)))
nil))))
(:with-mode Custom-mode
(:local-set imenu-generic-expression ; thanks u/oantolin!
'(("Faces" (rx (seq bol
(or "Show" "Hide") " "
(group (zero-or-more nonl))
" face: [sample]"))
1)
("Variables" (rx (seq bol
(or "Show Value" "Hide") " "
(group (zero-or-more
(not (any "\n:"))))))
1)))))
(setup debugger
(:hook visual-line-mode))
(setup dired
(:also-load dired-x)
(:straight dired-subtree
dired-collapse
dired-git-info)
(:option dired-recursive-copies 'always
dired-recursive-deletes 'always
delete-by-moving-to-trash t
dired-listing-switches "-Al"
ls-lisp-dirs-first t
dired-ls-F-marks-symlinks t
dired-no-confirm '(byte-compile
chgrp chmod chown copy
hardlink load move
shell touch symlink)
dired-dwim-target t)
(:bind "TAB" #'dired-subtree-cycle
"i" #'dired-subtree-toggle
")" #'dired-git-info-mode)
(:hook dired-collapse-mode
dired-hide-details-mode
hl-line-mode)
(:global "C-x C-j" #'dired-jump)
(with-eval-after-load 'dired
(acdw/system
(:work (:straight w32-browser)
(autoload #'dired-w32-browser "w32-browser" nil t)
(:bind "RET" #'dired-w32-browser))
(:home (:straight dired-open)
(autoload #'dired-find-alternate-file "dired-open" nil t)
(:bind "RET" #'dired-find-alternate-file)))))
(setup disabled
;; While this stuff is defined in novice.el, I'm using 'disabled' as the name
;; for easy finding.
;; Enable all disabled commands.
;; This is an option, but I'm going to try /enabling/ just the ones that I
;; use instead.
;; (mapatoms (lambda (symbol)
;; (when (get symbol 'disabled)
;; (put symbol 'disabled nil))))
;; Enable /some/ disabled commands
(dolist (enable-sym '(narrow-to-region
dired-find-alternate-file
narrow-to-page))
(put enable-sym 'disabled nil))
;; Now, disable symbols as I wish.
(dolist (disable-sym '(view-hello-file
suspend-frame
scroll-left
scroll-right
comment-set-column
set-fill-column))
(put disable-sym 'disabled t))
;; And set the disabled function to something better than the default.
;; Now, I can run any disabled command, but I have to use M-x to do it.
(setq disabled-command-function
(defun acdw/disabled-command-function (&optional cmd keys)
(let ((cmd (or cmd this-command))
(keys (or keys (this-command-keys))))
;; this logic stolen from original `disabled-command-function'
(if (or (eq (aref keys 0) (if (stringp keys)
(aref "\M-x" 0)
?\M-x))
(and (>= (length keys) 2)
(eq (aref keys 0) meta-prefix-char)
(eq (aref keys 1) ?x)))
;; it's been run as an M-x command, we want to do it
(call-interactively cmd)
;; else, tell the user it's disabled.
(message (substitute-command-keys
(concat "Command `%s' has been disabled. "
"Run with \\[execute-extended-command]."))
cmd))))))
(setup ediff
(:option ediff-window-setup-function #'ediff-setup-windows-plain
ediff-split-window-function #'split-window-horizontally))
;; requires extension:
;; https://addons.mozilla.org/en-US/firefox/addon/edit-with-emacs1/
(setup (:straight edit-server)
(when (and (daemonp)
(require 'edit-server nil :noerror))
(edit-server-start)
(:advise edit-server-make-frame :before
(defun edit-server@set-a-variable (&rest _)
(setq-local edit-server-frame-p t)))))
(setup eldoc
(:option eldoc-idle-delay 0.1
eldoc-echo-area-use-multiline-p nil))
(setup (:straight (electric-cursor
:host github
:repo "duckwork/electric-cursor"))
(electric-cursor-mode +1))
(setup (:straight elfeed
elfeed-protocol)
(:option elfeed-use-curl t
elfeed-feeds `(("fever+https://acdw@mf.acdw.net"
:api-url "https://mf.acdw.net/fever/"
:password ,(acdw/make-password-fetcher
:host "mf.acdw.net"))))
(elfeed-protocol-enable)
(:advise elfeed :after
(defun elfeed@protocol-update (&rest _)
(elfeed-search-fetch nil)))
(:face message-header-subject
((t (:height 1.5))))
(:with-mode elfeed-show-mode
(:hook (defun elfeed-show@setup ()
(acdw/reading-mode)))
;; see https://irreal.org/blog/?p=8885
(:bind "SPC" (defun elfeed-scroll-up-command (&optional arg)
"Scroll up or go to next feed item in Elfeed"
(interactive "^P")
(let ((scroll-error-top-bottom nil))
(condition-case-unless-debug nil
(scroll-up-command arg)
(error (elfeed-show-next)))))
"S-SPC" (defun elfeed-scroll-down-command (&optional arg)
"Scroll up or go to next feed item in Elfeed"
(interactive "^P")
(let ((scroll-error-top-bottom nil))
(condition-case-unless-debug nil
(scroll-down-command arg)
(error (elfeed-show-prev))))))))
(setup elisp-mode
(:with-mode emacs-lisp-mode ;; -_-
(:option eval-expression-print-length nil
eval-expression-print-level nil
lisp-indent-function #'lisp-indent-function)
(:local-set (append imenu-generic-expression)
`("Setup"
,(rx (seq
(group bol (* space) "(setup" (+ space))
(? (group "(:" (+ graph) (* space) (? "(")))
(group (+ (any word ?-)))))
3))
(:hook #'checkdoc-minor-mode
#'turn-on-eldoc-mode)
;; Emulate slime's eval binds
(:bind "C-c C-c" #'eval-defun
"C-c C-k" #'acdw/eval-region-or-buffer
"C-c C-z" #'ielm)
;; Add advice to pulse evaluated regions
(:advise eval-region :around
(defun eval-region@pulse (fn beg end &rest args)
(pulse-momentary-highlight-region beg end)
(apply fn beg end args))))
(:with-mode lisp-interaction-mode ;; -___-
(:bind "C-c C-c" #'eval-defun
"C-c C-k" #'acdw/eval-region-or-buffer
"C-c C-z" #'ielm)))
(setup (:straight elisp-slime-nav)
(:hook-into emacs-lisp-mode
ielm-mode))
(setup (:straight (elpher
:host nil
:repo "git://thelambdalab.xyz/elpher.git"))
(:option elpher-ipv4-always t
elpher-certificate-directory (acdw/dir "elpher/")
elpher-gemini-max-fill-width fill-column)
(:bind "n" #'elpher-next-link
"p" #'elpher-prev-link
"o" #'elpher-follow-current-link
"G" #'elpher-go-current)
(:hook #'acdw/reading-mode)
(:autoload (elpher-bookmarks :interactive t)
(elpher-go :interactive t))
;; Make `eww' gemini/gopher aware. From Emacswiki.
;; (define-advice eww-browse-url (:around (fn url &rest args) gemini-elpher)
;; (cond ((string-match-p "\\`\\(gemini\\|gopher\\)://" url)
;; (require 'elpher)
;; (elpher-go url))
;; (t (apply fn url args))))
)
(setup emacs
;; "Et cetera" settings
;; This should stay as /minimal/ as possible. Anything that can go somewhere
;; else /should/ go there.
(:option
attempt-orderly-shutdown-on-fatal-signal nil
attempt-stack-overflow-recovery nil
echo-keystrokes 0.01
find-function-C-source-directory (acdw/find-emacs-source)
kill-read-only-ok t
load-prefer-newer t
native-comp-async-report-warnings-errors nil
password-cache t
password-cache-expiry 3600 ; 5 minutes
read-extended-command-predicate ; emacs 28
set-mark-command-repeat-pop t)
(when (fboundp 'command-completion-default-include-p)
(setq read-extended-command-predicate
#'command-completion-default-include-p))
(defvar case-map (make-sparse-keymap)
"A keymap for setting case in various ways.")
(global-set-key (kbd "C-c c") case-map)
(defvar lookup-map (make-sparse-keymap)
"A keymap for looking up things.")
(global-set-key (kbd "C-c l") lookup-map)
(:global "M-=" #'count-words
"C-w" #'kill-region-or-backward-word
"C-c c c" #'capitalize-dwim
"C-c c t" #'titlecase-dwim
"C-c c u" #'upcase-dwim
"C-c c l" #'downcase-dwim
"C-c d" #'acdw/insert-iso-date
"M-`" nil)
;; toggle bindings
(defvar toggle-map (make-sparse-keymap)
"A keymap for toggling!")
(global-set-key (kbd "C-c t") toggle-map)
(:with-map toggle-map
(:bind "c" #'column-number-mode
"l" #'display-line-numbers-mode
"d" #'toggle-debug-on-error))
(defalias 'forward-word-with-case 'forward-word
"Alias for `forward-word' for use in `case-repeat-map'.")
(defalias 'backward-word-with-case 'backward-word
"Alias for `backward-word for use in `case-repeat-map'.")
(defvar case-repeat-map
(let ((map (make-sparse-keymap)))
(define-key map "c" #'capitalize-word)
(define-key map "u" #'upcase-word)
(define-key map "l" #'downcase-word)
;; movement
(define-key map "f" #'forward-word-with-case)
(define-key map "b" #'backward-word-with-case)
map)
"A map to repeat word-casing commands. For use with `repeat-mode'.")
(dolist (command '(capitalize-word
capitalize-dwim
upcase-word
upcase-dwim
downcase-word
downcase-dwim
forward-word-with-case
backward-word-with-case))
(put command 'repeat-map 'case-repeat-map))
(add-hook 'after-make-frame-functions
(defun after-make-frame@maximize (frame)
(unless (bound-and-true-p edit-server-frame-p)
(toggle-frame-maximized frame)))))
(setup (:straight embark)
(:global "C-." #'embark-act)
(:option prefix-help-command #'embark-prefix-help-command
(append display-buffer-alist)
`(,(rx (seq bos "*Embark Collect "
(group (| "Live" "Completions"))
"*"))
nil
(window-parameters (mode-line-format . none)))
embark-prompter #'embark-keymap-prompter
embark-verbose-indicator-display-action
'(display-buffer-at-bottom (window-height . fit-window-to-buffer))
embark-action-indicator
(lambda (map _target)
(which-key--show-keymap "Embark" map nil nil 'no-paging)
#'which-key--hide--ignore-command)
embark-become-indicator embark-action-indicator)
(with-eval-after-loads (embark consult)
(:straight embark-consult)
(add-hook 'embark-collect-mode-hook
#'consult-preview-at-point-mode)))
(setup encoding
(:option 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
default-process-coding-system '(utf-8-unix . utf-8-unix)
x-select-request-type '(UTF8_STRING
COMPOUND_TEXT
TEXT
STRING))
(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)
(acdw/system
(:work (set-clipboard-coding-system 'utf-16-le)
(set-selection-coding-system 'utf-16-le))
(_ (set-selection-coding-system 'utf-8)
(set-clipboard-coding-system 'utf-8))))
(setup (:straight epithet)
(dolist (hook '(Info-selection-hook
eww-after-render-hook
help-mode-hook
occur-mode-hook))
(add-hook hook #'epithet-rename-buffer)))
;; TODO: look into emms or something related for this
(setup (:straight-if eradio
(executable-find "mpv"))
(:option
eradio-player '("mpv" "--no-video" "--no-terminal")
eradio-channels `(("KLSU" .
"http://130.39.238.143:8010/stream.mp3")
("Soma FM Synphaera" .
"https://somafm.com/synphaera256.pls")
("SomaFM BAGel Radio" .
"https://somafm.com/bagel.pls")
("SomaFM Boot Liquor" .
"https://somafm.com/bootliquor320.pls")
("SomaFM Deep Space One" .
"https://somafm.com/deepspaceone.pls")
("SomaFM Fluid" .
"https://somafm.com/fluid.pls")
("SomaFM Underground 80s" .
"https://somafm.com/u80s256.pls")
("WBRH: Jazz & More" .
"http://wbrh.streamguys1.com/wbrh-mp3")
("KBRH Blues & Rhythm Hits" .
"http://wbrh.streamguys1.com/kbrh-mp3")
("WRKF HD-2" .
,(concat "https://playerservices.streamtheworld.com/"
"api/livestream-redirect/WRKFHD2.mp3"))
("WRKF: NPR for the Capital Region" .
,(concat "https://playerservices.streamtheworld.com/"
"api/livestream-redirect/WRKFFM.mp3"))
("BadRadio: 24/7 PHONK" .
"https://s2.radio.co/s2b2b68744/listen")
("tilderadio" .
"https://radio.tildeverse.org/radio/8000/radio.ogg")
("vantaradio" .
"https://vantaa.black/radio")))
(:global "C-c r r" #'eradio-play ; mnemonic: radio
"C-c r s" #'eradio-stop ; mnemonic: stop
"C-c r p" #'eradio-toggle ; mnemonic: play/pause
))
(setup (:straight eros)
(:hook-into emacs-lisp-mode))
(setup eshell
(:also-load acdw-eshell
em-smart
em-tramp)
(:option eshell-aliases-file (acdw/dir "eshell/aliases" t)
eshell-destroy-buffer-when-process-dies t
eshell-directory-name (acdw/dir "eshell/" t)
eshell-error-if-no-glob t
eshell-hist-ignore-dups t
eshell-kill-on-exit nil
eshell-prefer-lisp-functions t ; I want to try using eshell
eshell-prefer-lisp-variables t ; as much as possible.
eshell-review-quick-commands nil
eshell-save-history-on-exit t
eshell-scroll-to-bottom-on-input 'all
eshell-smart-space-goes-to-end t
eshell-where-to-jump 'begin)
(:local-set outline-regexp eshell-prompt-regexp
page-delimiter eshell-prompt-regexp)
(:bind "C-d" #'eshell-quit-or-delete-char)
(:hook #'eshell-arg-hist-mode
(defun eshell-mode@setup ()
(unless (bound-and-true-p eshell-customizations-loaded)
(load (expand-file-name "eshell" user-emacs-directory))))))
(setup eww
(:option eww-search-prefix "https://duckduckgo.com/html?q="
url-privacy-level '(email agent cookies lastloc))
(:hook #'acdw/reading-mode))
(setup (:straight-if exec-path-from-shell
(acdw/system :home))
(when (daemonp)
(exec-path-from-shell-initialize)))
(setup (:straight expand-region)
(:global "C-=" #'er/expand-region
"C-SPC"
(defun acdw/set-mark-or-expand-region (arg)
"Set mark at point and activate, jump to mark, or expand region.
See `set-mark-command' and `expand-region'.
With no prefix argument, either run `set-mark-command' on first
invocation and `er/expand-region' on each successive invocation.
With any prefix argument
(e.g., \\[universal-argument] \\[set-mark-command]), act as with
`set-mark-command' (i.e., pop the mark). Don't care about
successive invocations."
(interactive "P")
(cond
((or arg
(and set-mark-command-repeat-pop
(eq last-command 'pop-to-mark-command)))
(setq this-command 'set-mark-command)
(set-mark-command arg))
((eq last-command 'acdw/set-mark-or-expand-region)
(er/expand-region 1))
(t (set-mark-command arg))))))
(setup (:straight-if fennel-mode
(executable-find "fennel"))
(:autoload (fennel-repl :interactive t))
(:file-match (rx ".fnl" eos)))
(setup files
(:option
auto-save-file-name-transforms `((".*" ,(acdw/dir "auto-save/" t) t))
auto-save-list-file-prefix (acdw/dir "auto-save-list/.saves-" t)
auto-save-interval 60
auto-save-timeout 60
auto-save-visited-interval auto-save-timeout
backup-by-copying t
backup-directory-alist `((".*" . ,(acdw/dir "backup/" t)))
delete-old-versions t
mode-require-final-newline 'visit-save
tramp-backup-directory-alist backup-directory-alist
vc-make-backup-files t
version-control t)
(auto-save-visited-mode +1))
(setup find-func
(:global "C-c l f" #'find-function
"C-c l l" #'find-library
"C-c l v" #'find-variable))
(setup flyspell
(add-hook 'text-mode-hook #'flyspell-mode))
(setup (:straight flyspell-correct)
(:with-mode flyspell-mode
(:hook (defun flyspell@correct ()
(:bind "C-;" #'flyspell-correct-wrapper)
(:unbind "C-," "C-." "C-M-i")))))
(setup frames
(:option frame-title-format '("%b@"
(:eval
(or (file-remote-p default-directory 'host)
system-name))
" %+%* GNU Emacs"
(:eval (when (frame-parameter nil 'client)
" Client")))
window-resize-pixelwise t))
(setup (:straight gcmh)
(:option gcmh-idle-delay 'auto)
(gcmh-mode +1))
(setup (:straight-if geiser
(progn
(defvar acdw/schemes
(let (schemes)
(dolist (scheme '(("scheme" . geiser-chez) ; chez
("petite" . geiser-chez) ; petite
("csi" . geiser-chez) ; chicken
("gsi" . geiser-gambit)
("gosh" . geiser-gauche)
("guile" . geiser-guile)
("kawa" . geiser-kawa)
("mit-scheme" . geiser-mit)
("racket" . geiser-racket)
("stklos" . geiser-stklos)))
(when-let (binary (executable-find (car scheme)))
(push binary schemes)
;; and install the proper helper package
(straight-use-package (cdr scheme))))
(nreverse schemes)))
acdw/schemes))
(:file-match (rx ".rkt" eos)
(rx ".scm" eos)))
(setup (:straight (gemini-mode
:host nil
:repo "https://git.carcosa.net/jmcbray/gemini.el.git"))
(:file-match (rx (seq "." (or "gemini" "gmi") eos)))
(:hook turn-off-auto-fill))
(setup (:straight (gemini-write
:host nil
:repo "https://alexschroeder.ch/cgit/gemini-write"
:branch "main"))
(with-eval-after-load 'elpher
(require 'gemini-write)))
(setup (:require gforth)
(:autoload forth-mode
forth-block-mode)
(add-to-list 'auto-mode-alist '("\\.fs\\'" . forth-mode))
(add-to-list 'auto-mode-alist '("\\.fb\\'" . forth-block-mode)))
(setup goto-addr
(if (fboundp #'global-goto-address-mode)
(global-goto-address-mode)
(add-hook 'after-change-major-mode-hook #'goto-address-mode)))
;; TODO: figure out a popper.el / shackle.el ... thing to fix this
(setup (:straight helpful)
(:option helpful-switch-buffer-function
(defun helpful-pop-to-buffer (&rest args)
(let ((foo
(if (eq major-mode 'helpful-mode)
#'pop-to-buffer-same-window
#'pop-to-buffer)))
(apply foo args))))
(:global "<help> f" #'helpful-callable
"<help> v" #'helpful-variable
"<help> k" #'helpful-key
"<help> o" #'helpful-symbol)
(:bind "q" (defun helpful-quit ()
(interactive)
(when (eq major-mode 'helpful-mode)
(quit-window)
(unless (or (= 1 (count-windows))
(eq major-mode 'helpful-mode))
(delete-window))))))
(setup (:straight hungry-delete)
(:option hungry-delete-chars-to-skip " \t"
hungry-delete-join-reluctantly nil)
(global-hungry-delete-mode +1)
(:with-feature paredit
(:bind [remap paredit-backward-delete]
(defun acdw/paredit-hungry-delete-backward (arg)
(interactive "P")
(if (looking-back "[ \t]" 1)
(hungry-delete-backward (or arg 1))
(paredit-backward-delete arg)))
[remap paredit-forward-delete]
(defun acdw/paredit-hungry-delete-forward (arg)
(interactive "P")
(if (looking-at "[ \t]")
(hungry-delete-forward (or arg 1))
(paredit-forward-delete arg))))))
(setup ibuffer
(:also-load ibuf-ext)
(:option ibuffer-expert t
ibuffer-show-empty-filter-groups nil
ibuffer-saved-filter-groups
'(("default"
("dired" (mode . dired-mode))
("customize" (mode . Custom-mode))
("emacs" (or (name . "^\\*scratch\\*$")
(name . "^\\*Messages\\*$")
(name . "^\\*Warnings\\*$")
(name . "^\\*straight-process\\*$")
(name . "^\\*Calendar\\*$")))
("git" (or (name . "^\*magit")
(name . "^\magit")))
("help" (or (mode . help-mode)
(mode . Info-mode)
(mode . helpful-mode)))
("messaging" (or (mode . message-mode)
(mode . bbdb-mode)
(mode . mail-mode)
(mode . gnus-group-mode)
(mode . gnus-summary-mode)
(mode . gnus-article-mode)
(name . "^\\.bbdb$")
(name . "^\\.newsrc-dribble")
(mode . erc-mode)
(mode . circe-server-mode)
(mode . circe-channel-mode)))
("shell" (or (mode . eshell-mode)
(mode . shell-mode)
(mode . vterm-mode)))
("web" (or (mode . elpher-mode)
(mode . gemini-mode)
(mode . eww-mode))))))
(:global "C-x C-b" #'ibuffer)
(:hook (defun ibuffer@filter-to-default ()
(ibuffer-switch-to-saved-filter-groups "default"))))
(setup ielm
(:hook #'turn-on-eldoc-mode))
(setup imenu
(:option imenu-auto-rescan t))
(setup (:straight iscroll)
(define-globalized-minor-mode global-iscroll-mode iscroll-mode
(lambda () (iscroll-mode +1)))
(global-iscroll-mode +1))
(setup isearch
(:option search-default-mode t))
(setup (:straight lacarte)
(:global "<f10>" #'lacarte-execute-menu-command))
(setup (:straight-if ledger-mode
(executable-find "ledger")))
(setup lines
(:option fill-column 79
word-wrap t
truncate-lines nil)
(global-display-fill-column-indicator-mode +1)
(global-so-long-mode +1)
(add-hook 'visual-line-mode-hook
(defun acdw/disable-fill-column-indicator ()
(display-fill-column-indicator-mode
(if visual-line-mode -1 +1))))
;; `acdw/kill-line-and-join-advice' cribs from `crux-kill-and-join-forward'.
;; I can't simply advise `kill-line' with an override from crux because crux
;; itself calls `kill-line', leading to a infinite nesting situation.
(advice-add 'kill-line :around
(defun kill-line@join (fn &rest args)
(if (and (eolp)
(not (bolp)))
(delete-indentation 1)
(apply fn args)))))
(setup (:straight link-hint)
;; Browse web URLs with a browser with a prefix argument.
(dolist (type '(gnus-w3m-image-url
gnus-w3m-url
markdown-link
mu4e-attachment
mu4e-url
notmuch-hello
nov-link
org-link
shr-url
text-url
w3m-link
w3m-message-link))
(link-hint-define-type type
:open-secondary browse-url-secondary-browser-function))
(:option link-hint-avy-style 'at)
(:global "C-;"
(defun acdw/link-hint-open-link (arg)
"Open a link using `link-hint-open-link', prefix-aware.
That is, a prefix argument (\\[universal-argument]) will open the
browser defined in `browse-url-secondary-browser-function'."
(interactive "P")
(avy-with link-hint-open-link
(link-hint--one (if arg :open-secondary :open))))))
(setup (:straight lua-mode)
(:file-match (rx ".lua" eos)))
(setup (:straight macrostep)
(define-key emacs-lisp-mode-map (kbd "C-c e") #'macrostep-expand))
(setup (:straight magit)
(:global "C-c g" #'magit-status)
(:option magit-display-buffer-function
(defun magit-display-buffer-same-window (buffer)
"Display BUFFER in the selected window like God intended."
(display-buffer buffer '(display-buffer-same-window)))
magit-popup-display-buffer-action '((display-buffer-same-window))
magit-refresh-status-buffer nil))
(setup (:straight marginalia)
(:option marginalia-annotators '(marginalia-annotators-heavy
marginalia-annotators-light))
(marginalia-mode +1))
(setup (:straight markdown-mode)
(:file-match (rx ".md" eos)
(rx ".markdown" eos))
(:with-mode gfm-mode
(:file-match (rx "README.md" eos)))
(when (executable-find "markdownfmt")
(with-eval-after-load 'apheleia
(:option (append apheleia-formatters) '(markdownfmt . ("markdownfmt"))
(append apheleia-mode-alist) '(markdown-mode . markdownfmt)
(append apheleia-mode-alist) '(gfm-mode . markdownfmt)))))
(setup (:straight (mastodon
:host github
:repo "mooseyboots/mastodon.el")
request)
(:option mastodon-instance-url "https://writing.exchange"
mastodon-auth-source-file (car auth-sources)
mastodon-client--token-file (acdw/dir "mastodon.plstore"))
(:hook #'hl-line-mode
#'acdw/reading-mode))
(setup minibuffer
(:option enable-recursive-minibuffers t
file-name-shadow-properties '(invisible t intangible t)
minibuffer-eldef-shorten-default t
minibuffer-prompt-properties
'(read-only t cursor-intangible t face minibuffer-prompt)
read-answer-short t
read-extended-command-predicate ; used on >28
#'command-completion-default-include-p)
(add-hook 'minibuffer-setup-hook #'cursor-intangible-mode)
(add-hook 'minibuffer-setup-hook #'acdw/gc-disable)
(add-hook 'minibuffer-exit-hook #'acdw/gc-enable)
(minibuffer-depth-indicate-mode +1)
(file-name-shadow-mode +1)
(minibuffer-electric-default-mode +1)
(if (version< emacs-version "28")
(fset 'yes-or-no-p #'y-or-n-p)
(setq use-short-answers t)))
(setup (:straight (modus-themes
:host gitlab
:repo "protesilaos/modus-themes"))
(:option 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-mode-line nil)
(acdw/sunrise-sunset #'modus-themes-load-operandi
#'modus-themes-load-vivendi))
(setup (:straight mwim)
(:global "C-a" #'mwim-beginning
"C-e" #'mwim-end))
(setup (:straight nov)
(:option nov-text-width fill-column)
(:file-match (rx ".epub" eos)))
(setup (:straight olivetti)
(:option olivetti-body-width (+ fill-column 4)
olivetti-minimum-body-width fill-column)
(:hook (defun olivetti-mode@setup ()
(if olivetti-mode
(setq-local indicate-empty-lines nil
indicate-buffer-boundaries nil)
(acdw/setup-fringes)))))
(setup (:straight (orderless
:host github
:repo "oantolin/orderless"))
(require 'orderless)
(:option (append completion-styles) 'orderless
orderless-component-separator #'orderless-escapable-split-on-space
orderless-style-dispatchers '(acdw/orderless-dispatch))
(:advise orderless-regexp :filter-args
(defun fix-dollar (args)
(if (string-suffix-p "$" (car args))
(list (concat (substring (car args) 0 -1)
"[\x100000-\x10FFFD]*$"))
args)))
(defun acdw/orderless-dispatch (pattern _index _total)
"My custom dispatcher for `orderless'."
(cond
;; Ensure that $ works with Consult commands, which add disambiguation
;; suffixes -- see `fix-dollar'
((string-suffix-p "$" pattern)
`(orderless-regexp . ,(concat (substring pattern 0 -1)
"[\x100000-\x10FFFD]*$")))
;; File extensions
((string-match-p "\\`\\.." pattern)
`(orderless-regexp . ,(concat "\\." (substring pattern 1)
"[\x100000-\x10FFFD]*$")))
;; Ignore single !
((string= "!" pattern)
`(orderless-literal . ""))
;; Character folding
((string-prefix-p "%" pattern)
`(char-fold-to-regexp . ,(substring pattern 1)))
((string-suffix-p "%" pattern)
`(char-fold-to-regexp . ,(substring pattern 0 -1)))
;; Without literal
((string-prefix-p "!" pattern)
`(orderless-without-literal . ,(substring pattern 1)))
((string-suffix-p "!" pattern)
`(orderless-without-literal . ,(substring pattern 0 -1)))
;; Initialism matching
((string-prefix-p "`" pattern)
`(orderless-initialism . ,(substring pattern 1)))
((string-suffix-p "`" pattern)
`(orderless-initialism . ,(substring pattern 0 -1)))
;; Literal matching
((string-prefix-p "=" pattern)
`(orderless-literal . ,(substring pattern 1)))
((string-suffix-p "=" pattern)
`(orderless-literal . ,(substring pattern 0 -1)))
;; Flex matching
((string-prefix-p "~" pattern)
`(orderless-flex . ,(substring pattern 1)))
((string-suffix-p "~" pattern)
`(orderless-flex . ,(substring pattern 0 -1))))))
(setup (:straight org
org-contrib)
(:also-load 'acdw-org) ; so I don't clutter up init.el
(:option
org-adapt-indentation nil
org-agenda-files nil ; only until I set this up
org-catch-invisible-edits 'smart
org-clock-clocked-in-display 'mode-line
org-clock-string-limit 7 ; gives time and not title
org-confirm-babel-evaluate nil
org-directory "~/org"
org-ellipsis ""
org-export-coding-system 'utf-8-unix
org-export-headline-levels 8
org-export-with-section-numbers nil
org-export-with-smart-quotes t
org-export-with-sub-superscripts t
org-export-with-toc nil
org-fontify-done-headline t
org-fontify-quote-and-verse-blocks t
org-fontify-whole-heading-line t
org-hide-emphasis-markers t
org-html-coding-system 'utf-8-unix
org-image-actual-width '(300)
org-imenu-depth 3
org-outline-path-complete-in-steps nil
org-pretty-entities t
org-refile-use-outline-path 'file
org-special-ctrl-a/e t
org-special-ctrl-k t
org-src-fontify-natively t
org-src-tab-acts-natively t
org-src-window-setup 'current-window
org-startup-truncated nil
org-startup-with-inline-images t
org-tags-column 0 ; (- 0 fill-column -3)
)
(:bind "RET" #'acdw-org/return-dwim
"<S-return>" #'acdw-org/org-table-copy-down
"M-SPC M-SPC" #'insert-zero-width-space
"C-c C-l" #'org-insert-link-dwim
"M-w" #'acdw/copy-region-plain
"C-c C-n" #'acdw/org-next-heading-widen
"C-c C-p" #'acdw/org-previous-heading-widen)
(with-eval-after-load 'org-export
(:option (append org-export-filter-final-output-functions)
#'org-export-remove-zero-width-spaces))
(:hook variable-pitch-mode
olivetti-mode
(defun acdw/org-fix-lines-before-save ()
(add-hook 'before-save-hook
#'acdw-org/fix-blank-lines-in-buffer 0 :local))
(defun org-mode@wc-stupid ()
(unless (and wc-mode
(> 0 (+ (or wc-orig-words 0)
(or wc-words-delta 0)))))
(setq-local
wc-count-words-function
(lambda (start end) "Count words stupidly with a limit."
(acdw-org/count-words-stupidly start
end
999)))))
(:advise org-delete-backward-char :override #'acdw-org/delete-backward-char))
(setup (:straight org-appear)
(:hook-into org-mode))
(setup (:straight package-lint))
(setup (:straight package-lint-flymake))
(setup page
(:option page-delimiter
(rx bol (or "\f" ";;;")
(not (any "#")) (* not-newline) "\n"
(* (* blank) (opt ";" (* not-newline)) "\n")))
(defun recenter-to-top (&rest _)
"Recenter the cursor to the top of the window."
(when (called-interactively-p 'any)
(recenter (if (or (null scroll-margin)
(zerop scroll-margin))
3
scroll-margin))))
(:advise forward-page :after #'recenter-to-top
backward-page :after #'recenter-to-top)
;; I'm not sure where this is in /my/ version of Emacs
(defvar page-navigation-repeat-map
(let ((map (make-sparse-keymap)))
(define-key map "]" #'forward-page)
(define-key map "[" #'backward-page)
map)
"Keymap to repeat page navigation key sequences. Used in `repeat-mode'.")
(put 'forward-page 'repeat-map 'page-navigation-repeat-map)
(put 'backward-page 'repeat-map 'page-navigation-repeat-map))
(setup (:straight page-break-lines)
(global-page-break-lines-mode +1))
(setup (:straight paredit)
;; I don't use paredit-splice-sexp much, and it stomps on isearch.
(:unbind "M-s")
(defun paredit@setup ()
"Correct weirdnesses and set up paredit mode."
(:with-map lisp-mode-shared-map
(:bind "DEL" #'paredit-backward-delete
"C-M-;" #'comment-or-uncomment-sexp
"C-<backspace>" #'paredit-backward-kill-word))
(paredit-mode +1))
(dolist (mode lispy-modes)
(add-hook (intern (concat (symbol-name mode) "-hook"))
#'paredit@setup))
(:also-load eldoc)
(eldoc-add-command 'paredit-backward-delete 'paredit-close-round))
(setup (:straight paren-face)
(dolist (mode lispy-modes)
(add-hook (intern (concat (symbol-name mode) "-hook")) #'paren-face-mode)))
(setup (:straight-if (pdf-tools
:host github
:repo "vedang/pdf-tools")
(acdw/system :home))
(:file-match (rx ".pdf" eos))
(pdf-loader-install))
(setup (:straight persistent-scratch)
(:option persistent-scratch-backup-directory (acdw/dir "scratch" t)
persistent-scratch-keep-n-newest-backups 12)
(persistent-scratch-setup-default)
(mapc (lambda (buf)
(with-current-buffer buf
(when (funcall persistent-scratch-scratch-buffer-p-function)
(setq lexical-binding t)
(persistent-scratch-mode +1))))
(buffer-list)))
(setup (:straight-if pkgbuild-mode
(executable-find "makepkg"))
(:file-match "PKGBUILD"))
(setup (:straight powerthesaurus)
(:global "C-c l t" #'powerthesaurus-lookup-word-dwim))
(setup prog
(:option show-paren-delay 0
show-paren-style 'mixed
show-paren-when-point-inside-paren t
show-paren-when-point-in-periphery t
smie-indent-basic tab-width)
(:hook show-paren-mode
electric-pair-local-mode
acdw/setup-fringes
(defun flymake-mode-except ()
"Turn on flymake mode, except in some modes."
(let ((no-flymake-modes '(emacs-lisp-mode)))
(unless (or (member major-mode no-flymake-modes)
(apply #'derived-mode-p no-flymake-modes))
(flymake-mode-on))))
(defun prog-mode@auto-fill ()
(setq-local comment-auto-fill-only-comments t)
(turn-on-auto-fill)))
(add-hook 'after-save-hook
#'executable-make-buffer-file-executable-if-script-p))
(setup (:require recentf)
(:option recentf-save-file (acdw/dir "recentf.el")
recentf-max-menu-items 100
recentf-max-saved-items nil
recentf-auto-cleanup 'mode
(append recentf-exclude) (acdw/dir))
(:advise dired-rename-file :after #'rjs/recentf-rename-notify)
(recentf-mode +1))
(setup repeat
;; new for Emacs 28!
(:only-if (fboundp #'repeat-mode))
(:option repeat-exit-key "g"
repeat-exit-timeout 5)
(repeat-mode +1))
(setup (:straight restart-emacs)
(defun emacs-upgrade (&optional update-packages)
"Pull config, upgrade packages, restart Emacs."
(interactive "P")
(emacs-git-pull-config)
(when update-packages
(require 'straight-x)
(straight-x-pull-all))
(restart-emacs)))
(setup (:require savehist)
(:option history-length t
history-delete-duplicates t
savehist-autosave-interval 60
savehist-file (acdw/dir "savehist.el"))
(dolist (var '(extended-command-history
global-mark-ring
kill-ring
regexp-search-ring
search-ring
mark-ring))
(:option (append savehist-additional-variables) var))
(savehist-mode +1))
(setup saveplace
(:option save-place-file (acdw/dir "places.el")
save-place-forget-unreadable-files (acdw/system :home))
(save-place-mode +1))
(setup scratch
(:option inhibit-startup-screen t
initial-buffer-choice t
initial-scratch-message ""
;; (concat ";; Howdy, "
;; (nth 0 (split-string
;; user-full-name))
;; "! "
;; "Welcome to GNU Emacs.\n\n")
initial-major-mode 'emacs-lisp-mode)
(add-hook 'kill-buffer-query-functions
(defun kill-buffer-query@immortal-scratch ()
(if (eq (current-buffer) (get-buffer "*scratch*"))
(progn (bury-buffer)
nil)
t))))
(setup scrolling
(:option auto-window-vscroll nil
fast-but-imprecise-scrolling t
scroll-margin 3
scroll-conservatively 101
scroll-preserve-screen-position 1))
(setup selection
(:option save-interprogram-paste-before-kill t
yank-pop-change-selection t
x-select-enable-clipboard t
x-select-enable-primary t
mouse-drag-copy-region t
kill-do-not-save-duplicates t)
(delete-selection-mode +1))
(setup (:require server)
(unless (server-running-p)
(server-start)))
(setup sh-mode
(:option sh-basic-offset tab-width
sh-indent-after-case 0
sh-indent-for-case-alt '+
sh-indent-for-case-label 0)
(:local-set indent-tabs-mode t)
(when (executable-find "shfmt")
(with-eval-after-load 'apheleia
(:option (append apheleia-formatters) '(shfmt . ("shfmt"))
(append apheleia-mode-alist) '(sh-mode . shfmt))))
(when (executable-find "shellcheck")
(:straight flymake-shellcheck)
(:hook flymake-mode
flymake-shellcheck-load)))
(setup shell-command
(:option shell-command-switch (acdw/system
;; I should be testing on some variable
(:home "-csi")
(:work "-c"))
shell-command-prompt-show-cwd t
shell-command-default-error-buffer "*shell-command-errors*"))
(setup (:straight (shell-command+
:host nil
:repo "https://git.sr.ht/~pkal/shell-command-plus"))
(:option shell-command-prompt "$ ")
(:with-feature dired
(:bind "M-!" shell-command+))
(:global "M-!" shell-command+))
(setup shr
(:option shr-width fill-column
shr-max-width fill-column
shr-max-image-proportion 0.6
shr-image-animate t
shr-discard-aria-hidden t
shr-folding-mode t))
(setup (:straight simple-modeline
minions)
(:also-load acdw-modeline)
(:option simple-modeline-segments
'(;; left
(acdw-modeline/modified
acdw-modeline/buffer-name
acdw-modeline/vc-branch
acdw-modeline/winum
acdw-modeline/position)
;; right
(acdw-modeline/track
simple-modeline-segment-misc-info
acdw-modeline/wc
acdw-modeline/text-scale
simple-modeline-segment-process
acdw-modeline/god-mode-indicator
acdw-modeline/minions
acdw-modeline/narrowed
acdw-modeline/major-mode)))
(:option tab-bar-mode t
tab-bar-show 1)
;; I've put in a pull request to add the (- 0 right-margin) bit here.
(:advise simple-modeline--format :override
(defun simple-modeline@format (lefts rights)
(let* ((left (simple-modeline--format-segments lefts))
(right (simple-modeline--format-segments rights))
(reserve (length right)))
(concat
left
(propertize " "
'display `((space :align-to
(- right
(- 0 right-margin)
,reserve)))
'face '(:inherit simple-modeline-space))
right))))
(simple-modeline-mode +1))
(setup (:straight-if sly
(progn
(defvar acdw/lisps
(let (lisps)
(dolist (lisp '("sbcl" ; TODO: add more lisps
"clisp"))
(when-let (binary (executable-find lisp))
(push binary lisps)))
(nreverse lisps)))
acdw/lisps))
(:also-load sly-autoloads)
(:straight clhs)
(:option inferior-lisp-program acdw/lisp-bin
sly-kill-without-query-p t)
(:with-feature sly-mrepl
(defun sly-mrepl-return-at-end ()
(interactive)
(if (<= (point-max) (point))
(sly-mrepl-return)
(if (bound-and-true-p paredit-mode)
(paredit-newline)
(electric-newline-and-maybe-indent))))
(dolist (key '("RET" "<return>"))
(:bind key #'sly-mrepl-return-at-end))
(:bind "C-c C-c" #'sly-mrepl-return)))
(setup (:straight ssh-config-mode)
(:file-match (rx "/.ssh/config" eos)
(rx "/ssh" (? "d") "_config" eos))
(:with-mode ssh-known-hosts-mode
(:file-match (rx "/knownhosts" eos)))
(:with-mode ssh-authorized-keys-mode
(:file-match (rx "/authorized_keys" (? "2") eos)))
(:hook #'turn-on-font-lock))
(setup (:straight-if systemd
(executable-find "systemd")))
(setup text
(:hook turn-on-auto-fill
acdw/setup-fringes))
(setup (:straight (topsy
:host github
:repo "alphapapa/topsy.el"))
(:hook-into prog-mode))
(setup (:straight typo)
;; Enable C-c 8 map in all buffers
(typo-global-mode +1)
(add-hook 'text-mode-hook
(defun text-mode@typo-unless ()
"Start `typo-mode' UNLESS the buffer matches a predicate."
;; I implement this instead of using
;; `typo-disable-electricity-functions' because the latter checks
;; on every pertinent keypress. I know I want /no/ typo-ing in
;; these certain buffers, so I won't even turn on the mode.
(unless (or ; predicates here
(string-match-p "COMMIT_EDITMSG"
(or (buffer-name) "")))
(typo-mode +1))))
;; jlf & cvandusen on #emacs make a great point: (RIGHT SINGLE QUOTATION
;; MARK) is /not/ an apostrophe. Making it curly is a typographical
;; consideration, not an input consideration. (I suppose you could make
;; the argument that all of these are typographical considerations, but
;; .. bleh.)
(:bind "'" (define-typo-cycle typo-cycle-apostrophe
"Cycle through apostrophe-like graphemes.
If used with a numeric prefix argument N, N apostrophes will be inserted."
("'" "" "" ""))
"`" (define-typo-cycle typo-cycle-backtick
"Cycle through backtick and left single quotation mark.
If used with a numeric prefix argument N, N backticks will be inserted."
("`" ""))))
(setup (:straight undo-fu)
(:global "C-/" #'undo-fu-only-undo
"C-?" #'undo-fu-only-redo))
(setup (:straight undo-fu-session)
(:option undo-fu-session-incompatible-files '("/COMMIT_EDITMSG\\'"
"/git-rebase-todo\\'")
undo-fu-session-directory (acdw/dir "undo/" t)
undo-fu-session-compression (acdw/system :home))
(global-undo-fu-session-mode +1))
(setup (:straight unfill))
(setup (:straight (unfocused
:host github
:repo "duckwork/unfocused"))
(unfocused-mode +1)
(:with-hook unfocused-hook
(:hook (defun unfocused@save-buffers ()
(save-some-buffers t))
#'garbage-collect)))
(setup uniquify
(:option uniquify-buffer-name-style 'forward
uniquify-separator path-separator
uniquify-after-kill-buffer-p t
uniquify-ignore-buffers-re "^\\*"))
(setup variable-pitch-mode
;; I might want to change this to `buffer-face-mode-hook'...
(:advise variable-pitch-mode :after
(defun variable-pitch-mode@setup (&rest _)
"Set up `variable-pitch-mode' with my customizations."
(display-fill-column-indicator-mode
(if buffer-face-mode -1 +1)))))
(setup (:straight (vertico
:host github
:repo "minad/vertico"
:files ("*" "extensions/*"
(:exclude ".git"))))
(:option resize-mini-windows 'grow-only
vertico-count-format nil
vertico-cycle t)
(:with-map vertico-map
(:bind "<C-backspace>"
(defun up-directory (arg)
"Move up a directory (delete backwards to /)."
(interactive "p")
(if (string-match-p "/." (minibuffer-contents))
(zap-up-to-char (- arg) ?/)
(backward-kill-word arg)))))
(when (boundp 'native-comp-deferred-compilation-deny-list)
(add-to-list 'native-comp-deferred-compilation-deny-list "vertico"))
(vertico-mode +1)
;; Extensions!
(:also-load vertico-mouse)
(vertico-mouse-mode +1)
;; Prefix the current candidate with "> ". From Vertico wiki.
(:advise vertico--format-candidate :around
(defun vertico-format@add-arrow (orig cand pref suf index start)
(setq cand (funcall orig cand pref suf index start))
(concat (if (= vertico--index index)
(propertize "> " 'face 'vertico-current)
" ")
cand))))
(setup view
(:option view-read-only t)
(:hook (defun acdw/read-view-mode ()
(acdw/reading-mode (if view-mode +1 -1)))))
(setup (:straight visual-regexp)
(:global "M-%" #'vr/query-replace))
(setup (:straight-if vterm
(acdw/system :home))
(:straight (eshell-vterm
:host github
:repo "iostapyshyn/eshell-vterm"))
(eshell-vterm-mode +1)
(defalias 'eshell/v 'eshell-exec-visual))
(setup w32
(:option w32-allow-system-shell t
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))
(setup (:straight wc-mode)
(:option wc-modeline-format "[%tww]"
wc-idle-wait 2)
(:hook-into text-mode))
(setup (:straight web-mode)
(:option css-level-offset 2
js-indent-level 2
sgml-indent-offset 2)
(:file-match (rx ".htm" (? "l") eos)
(rx "." (? "tpl.") "php" eos)
(rx "." (| "asp" "gsp" "jsp") eos)
(rx "." (| "ascx" "aspx") eos)
(rx ".erb" eos)
(rx ".mustache" eos)))
(setup (:straight wgrep))
(setup (:straight which-key)
(:option which-key-show-early-on-C-h t
which-key-idle-delay 1
which-key-idle-secondary-delay 0.5
which-key-sort-order 'which-key-prefix-then-key-order)
(:global "C-h m" #'which-key-show-major-mode)
(which-key-setup-side-window-right-bottom)
(which-key-mode +1))
(setup whitespace
(:option whitespace-style '(empty
indentation
space-before-tab
space-after-tab)
indent-tabs-mode nil
tab-width 4
backward-delete-char-untabify-method 'hungry)
(:global "M-SPC" #'cycle-spacing))
(setup (:straight whitespace-cleanup-mode)
(global-whitespace-cleanup-mode +1))
(setup windmove
(:option windmove-wrap-around t)
(:global
;; moving
"C-x 4 <left>" #'windmove-left
"C-x 4 <right>" #'windmove-right
"C-x 4 <up>" #'windmove-up
"C-x 4 <down>" #'windmove-down
;; swapping
"C-x 4 S-<left>" #'windmove-swap-states-left
"C-x 4 S-<right>" #'windmove-swap-states-right
"C-x 4 S-<up>" #'windmove-swap-states-up
"C-x 4 S-<down>" #'windmove-swap-states-down)
(when (fboundp 'repeat-mode)
(defvar windmove-repeat-map
(let ((map (make-sparse-keymap)))
;; moving
(define-key map [left] #'windmove-left)
(define-key map [right] #'windmove-right)
(define-key map [up] #'windmove-up)
(define-key map [down] #'windmove-down)
;; swapping
(define-key map [S-left] #'windmove-swap-states-left)
(define-key map [S-right] #'windmove-swap-states-right)
(define-key map [S-up] #'windmove-swap-states-up)
(define-key map [S-down] #'windmove-swap-states-down)
map)
"Keymap to repeat various `windmove' sequences. Used in `repeat-mode'.")
(dolist (sym '(windmove-left
windmove-right
windmove-up
windmove-down
windmove-swap-states-left
windmove-swap-states-right
windmove-swap-states-up
windmove-swap-states-down))
(put sym 'repeat-map 'windmove-repeat-map))))
(setup window
(require 'acdw-bell)
(:option
;; Man-notify-method 'pushy
;; display-buffer-alist ; from FrostyX
;; '(("shell.*" (display-buffer-same-window) ())
;; (".*" (display-buffer-reuse-window
;; display-buffer-same-window)
;; (reusable-frames . t)))
recenter-positions '(top middle bottom)
ring-bell-function (lambda ()
(acdw-bell/flash-mode-line
(acdw/system :home)))
use-dialog-box nil
use-file-dialog nil
visible-bell nil)
(tooltip-mode -1))
(setup winner
;; see https://lists.gnu.org/archive/html/emacs-devel/2021-08/msg00888.html
(:global "C-x 4 C-/" #'winner-undo
"C-x 4 /" #'winner-undo
"C-x 4 C-?" #'winner-redo
"C-x 4 ?" #'winner-redo)
;; add `winner-undo' and `winner-redo' to `repeat-mode'
(when (fboundp 'repeat-mode)
(defvar winner-mode-repeat-map
(let ((map (make-sparse-keymap)))
(define-key map "/" #'winner-undo)
(define-key map "?" #'winner-redo)
map)
"Keymap to repeat `winner-mode' sequences. Used in `repeat-mode'.")
(put 'winner-undo 'repeat-map 'winner-mode-repeat-map)
(put 'winner-redo 'repeat-map 'winner-mode-repeat-map))
(winner-mode +1))
(setup (:straight winum)
(:option winum-scope 'frame-local
winum-auto-setup-mode-line nil
winum-ignored-buffers '(" *which-key*")
winum-format " %s")
(when-unfocused winum-map-keys
(defvar winum--keys-mapped nil
"Whether `winum' keys have been mapped already.")
(when (and (not winum--keys-mapped)
(display-graphic-p))
(:with-map winum-keymap
(:bind "M-0" #'winum-select-window-0-or-10
"M-1" #'winum-select-window-1
"M-2" #'winum-select-window-2
"M-3" #'winum-select-window-3
"M-4" #'winum-select-window-4
"M-5" #'winum-select-window-5
"M-6" #'winum-select-window-6
"M-7" #'winum-select-window-7
"M-8" #'winum-select-window-8
"M-9" #'winum-select-window-9))
(setq winum--keys-mapped t)))
(winum-mode +1))
(setup (:straight xr))
(setup (:straight zzz-to-char)
(:global "M-z"
(defun acdw/zzz-up-to-char (prefix)
"Call `zzz-up-to-char' or `zzz-to-char', PREFIX-depending."
(interactive "P")
(if prefix
(call-interactively #'zzz-to-char)
(call-interactively #'zzz-up-to-char)))))
;;; init.el ends here