emacs/init.el

2759 lines
101 KiB
EmacsLisp
Raw Blame History

This file contains ambiguous Unicode characters

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 <(rot13-string "npqj@npqj.arg")>
;; 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:
;; - Be kind to yourself.
;; - Make good choices.
;;; Commentary:
;; Some of the names in these `setup' forms are arbitrary.
;;; Code:
(setup (:require auth-source)
(:option auth-sources (list (acdw/sync-dir "authinfo")
(acdw/sync-dir "authinfo.gpg")
"~/.authinfo"
"~/.authinfo.gpg")))
(setup (:require goto-addr)
(if (fboundp #'global-goto-address-mode)
(global-goto-address-mode)
(add-hook 'after-change-major-mode-hook #'goto-address-mode)))
(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 (: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 (:require server)
(unless (server-running-p)
(server-start)))
(setup (:require tramp)
;; thanks Irreal! https://irreal.org/blog/?p=895
(add-to-list 'tramp-default-proxies-alist
'(nil "\\`root\\'" "/ssh:%h:"))
(add-to-list 'tramp-default-proxies-alist
'((regexp-quote (system-name)) nil nil)))
(setup Info
(:hook #'variable-pitch-mode
#'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-reading)
(:option user-full-name "Case Duckworth"
user-mail-address (rot13-string "npqj@npqj.arg"))
(when-let ((default-directory
(expand-file-name-exists-p "pkg/" user-emacs-directory)))
(normal-top-level-add-subdirs-to-load-path)))
(setup auto-fill
(:hook (defun auto-fill@truncate-lines ()
(setq-local truncate-lines t))))
(setup autoinsert
(require 'acdw-autoinsert)
(acdw/define-auto-insert '(:replace t)
;; This is my custom auto-insert for elisp files.
'("\\.el\\'" . "Emacs Lisp header (acdw)")
'("Short description: " ";;; "
(file-name-nondirectory (buffer-file-name))
" --- " str
(make-string (max 2 ( - fill-column (current-column) 27)) 32)
"-*- lexical-binding: t; -*-"
'(setq lexical-binding t)
"\n\n;; Copyright (C) " (format-time-string "%Y")
" " (getenv "ORGANIZATION") | (progn user-full-name)
"\n\n;; Author: " (user-full-name)
'(if (search-backward "&" (line-beginning-position) t)
(replace-match (capitalize (user-login-name)) t t))
'(end-of-line 1)
" <" (progn user-mail-address) ">"
& -2
"\n\n;;; License:"
"\n\n;; Everyone is permitted to do whatever with this software, without"
"\n;; limitation. This software comes without any warranty whatsoever,"
"\n;; but with two pieces of advice:"
"\n\n;; - Be kind to yourself."
"\n\n;; - Make good choices."
"\n\n;;; Commentary:"
"\n\n;; " _
"\n\n;;; Code:"
"\n\n\n\n(provide '" (file-name-base (buffer-file-name)) ")"
"\n;;; " (file-name-nondirectory (buffer-file-name)) " ends here\n"))
(auto-insert-mode +1))
(setup autorevert
(:option global-auto-revert-non-file-buffers t
auto-revert-verbose nil)
(global-auto-revert-mode +1))
(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
(cond ((executable-find "feh") #'browse-url-feh)
((executable-find "mpv")
(defun browse-image-url-mpv (url &rest _args)
"View an image URL in mpv."
(let ((url (browse-url-encode-url url))
(process-environment
(browse-url-process-environment)))
(message "Viewing %s in mpv..." url)
(apply #'start-process
(concat "mpv " url) nil
"mpv"
(append browse-url-mpv-arguments
(list "--image-display-duration=inf"
url))))))
(t #'eww-browse-url))
args)))
(cons (rx (or "youtube.com" ; videos
"youtu.be"
(seq "." (or "mp4"
"gif"
"mov" "MOV")
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"
"pixelfed"
"taskiq"))
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)
;; Set the right major mode based on buffer name, if not visiting a file.
;; http://ruzkuku.com/emacs.d.html#orgeab93c3
(setq-default major-mode (lambda ()
(unless buffer-file-name
(let ((buffer-file-name (buffer-name)))
(set-auto-mode))))))
(setup calendar
(:option calendar-week-start-day 1))
(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 css-mode
(:bind "C-c C-h" #'css-lookup-symbol))
(setup cursor
(:option cursor-type 'bar
cursor-in-non-selected-windows 'hollow
blink-cursor-blinks 1)
(blink-cursor-mode +1))
(setup cus-edit
(:also-load acdw-cus-edit)
(:option custom-file (acdw/dir "custom.el")
custom-magic-show nil
custom-magic-show-button t
custom-raised-buttons nil
custom-unlispify-tag-names nil
custom-variable-default-form 'lisp)
;; I need this to save `safe-local-variables' between Emacs invocations. For
;; now, of course .... I would /love/ a better solution.
(when (file-exists-p custom-file)
;; Don't load faces, since those are all set in init.el
(cl-letf (((symbol-function 'custom-set-faces) #'ignore))
(load custom-file nil nil)))
;; `Custom-mode-hook' fires /before/ the widgets are built, so I have to
;; install advice after the widgets are made.
(:advise custom-buffer-create-internal :after #'acdw-cus/expand-widgets)
(:with-mode Custom-mode
(:local-set imenu-generic-expression acdw-cus/imenu-generic-expression)))
(setup debugger
(:hook visual-line-mode))
(setup dired
(:also-load dired-x)
(:straight dired-subtree
dired-collapse
dired-git-info)
(:option dired-recursive-copies 'top
dired-recursive-deletes 'top
dired-create-destination-dirs 'ask
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.
(:option disabled-command-function #'acdw/disabled-command-function))
(setup ediff
(:option ediff-diff-options "-w" ; ignore whitespace
ediff-window-setup-function #'ediff-setup-windows-plain
ediff-split-window-function #'split-window-horizontally)
;; https://oremacs.com/2015/01/17/setting-up-ediff/
(add-hook 'ediff-after-quit-hook-internal #'winner-undo))
(setup eldoc
(:option eldoc-idle-delay 0.1
eldoc-echo-area-use-multiline-p nil))
(setup elec-pair
(electric-pair-mode +1))
(setup elisp-mode
(:with-mode emacs-lisp-mode ;; -_-
(:option eval-expression-print-length nil
eval-expression-print-level nil
print-length nil
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)
(let ((pulse-flag t))
(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 emacs
;; "Et cetera" settings
;; This should stay as /minimal/ as possible. Anything that can go somewhere
;; else /should/ go there.
(:option
async-shell-command-display-buffer nil
async-shell-command-buffer #'new-buffer
attempt-orderly-shutdown-on-fatal-signal nil
auto-hscroll-mode 'current-line
attempt-stack-overflow-recovery nil
echo-keystrokes 0.01
find-function-C-source-directory (acdw/find-emacs-source)
image-use-external-converter (and (not (version< emacs-version "27"))
(or (executable-find "magick")
(executable-find "convert")))
kill-read-only-ok t
kill-ring-max 500 ; RAM is cheap, right?
mark-ring-max 50
kmacro-ring-max 20
search-ring-max 200
global-mark-ring-max 100
regexp-search-ring-max 100
load-prefer-newer t
native-comp-async-report-warnings-errors nil
password-cache t
password-cache-expiry (* 60 5) ; seconds
set-mark-command-repeat-pop t
hscroll-step 1
scroll-step 1)
(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-M-;" #'comment-or-uncomment-sexp
"C-w" #'kill-region-or-backward-word
"C-c d" #'acdw/insert-iso-date
"M-`" nil
"C-x o" #'acdw/other-window-or-switch-buffer
"C-x O" #'acdw/other-window-or-switch-buffer-backward
"C-c _" #'add-file-local-variable
"C-x C-c" #'acdw/fat-finger-exit)
(global-set-key (kbd "M-n") (kbd "C-u 1 C-v"))
(global-set-key (kbd "M-p") (kbd "C-u 1 M-v"))
;; inspo: https://github.com/zaeph/.emacs.d/blob/master/init.el#L479
(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
"s" #'so-long-mode
"S" #'scroll-bar-mode))
;; Toggle
(:with-map toggle-map
(:bind "b" (defun acdw/toggle-lexical-binding ()
"Toggle `lexical-binding' in the current buffer."
(interactive)
(setq lexical-binding (not lexical-binding))
(message "Lexical-binding is %sabled."
(if lexical-binding "en" "dis"))
(force-mode-line-update))))
(:with-map case-map
(require 'titlecase)
(require 'acdw)
(:bind "c" #'capitalize-dwim
"t" #'titlecase-dwim
"u" #'upcase-dwim
"l" #'downcase-dwim))
(column-number-mode +1))
(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 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)
(:hook #'eshell-arg-hist-mode
(defun eshell-mode@setup ()
(require 'eshellrc (locate-user-emacs-file "eshell") :noerror)
(:bind "C-d" #'eshell-quit-or-delete-char))))
(setup eww
(:also-load acdw-eww)
(defvar-local eww-readable-p nil
"Whether current buffer is in readable-mode.")
(:option eww-search-prefix "https://duckduckgo.com/html?q="
url-privacy-level '(email agent cookies lastloc))
(defun eww@is-readable (&rest _)
(setq-local eww-readable-p t))
(defun eww@is-not-readable (&rest _)
(setq-local eww-readable-p nil))
(advice-add 'eww-readable :after #'eww@is-readable)
(advice-add 'eww-render :after #'eww@is-not-readable)
(advice-add 'eww-back-url :after #'eww@is-not-readable)
(:hook #'reading-mode
(defun bookmark-eww--setup ()
"Setup eww bookmark integration."
(setq-local bookmark-make-record-function #'bookmark-eww--make)))
(:bind "RET" (defun eww/browse-url (arg)
(interactive "P")
(if-let ((url (thing-at-point 'url)))
(browse-url url)
(call-interactively #'acdw/link-hint-open-link)))
"b" #'bookmark-set
"B" #'bookmark-jump
"M-n" nil
"M-p" nil))
(setup executable
(:option executable-prefix-env t)
(add-hook 'after-save-hook
#'executable-make-buffer-file-executable-if-script-p))
(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 flymake
;; TODO: look at flycheck for ideas around `flycheck-disabled-checkers' and
;; `flycheck-emacs-lisp-load-path'... there must be a way to get flymake to
;; recognize new values in the load path.
(defvar-local flymake-inhibit nil
"Buffer-local variable to inhibit `flymake'.")
(add-to-list 'safe-local-variable-values '(flymake-inhibit . t))
(add-to-list 'safe-local-variable-values '(flymake-inhibit . nil))
(defvar flymake-inhibit-major-modes nil
"Which major-modes NOT to enable `flymake' in.")
(defvar flymake-inhibit-file-name-regexps '("init\\.el\\'"
"early-init\\.el\\'")
"List of file regexps NOT to enable `flymake' in.")
(defvar flymake-inhibit-buffer-name-regexps (list (rx "*scratch*"))
"List of buffer-name regexps NOT to enable `flymake' in.")
(defun list-string-match-p (string regexp-list)
"Return t if at least one regex in RETGEXP-LIST matches STRING, else nil."
;; FINE alphapapa ;P
(seq-some (lambda (regexp)
(string-match regexp (or string "")))
regexp-list))
(defun flymake-unless ()
"Turn on `flymake-mode', UNLESS it's inhibited.
There are three methods to inhibit flymake in a file. From most
specific to most general, they are these:
- `flymake-inhibit': a file-local-variable
- `flymake-inhibit-buffer-name-regexps': a list of regexps to
match the buffer name against. If one of them matches, inhibit
`flymake-mode'.
- `flymake-inhibit-file-name-regexps': a list of regexps to match
the filename against. If one of them matches, inhibit
`flymake-mode'.
- `flymake-inhibit-major-modes': a list of major-modes in which
to inhibit `flymake-mode'. Really only useful if you want to
generally add `flymake-mode' to `prog-mode-hook'."
;; The name of this hook tells you pretty much everything you need to know
;; for this little thing right here.
(add-hook 'hack-local-variables-hook
(defun flymake-unless@hack-local-variables ()
(unless (or (cdr (assoc 'flymake-inhibit
file-local-variables-alist))
(list-string-match-p
(buffer-name)
flymake-inhibit-buffer-name-regexps)
(list-string-match-p
(buffer-file-name)
flymake-inhibit-file-name-regexps)
(apply #'derived-mode-p
flymake-inhibit-major-modes))
(flymake-mode-on)))))
(add-hook 'prog-mode-hook #'flymake-unless)
(:bind "M-n" #'flymake-goto-next-error
"M-p" #'flymake-goto-prev-error))
(setup flyspell
(:hook-into text-mode))
(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 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 isearch
(:option search-default-mode t))
(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 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 mouse
;; Unconditionally follow links when clicked.
;; This is because mouse-1 usually sets point.
;; Other options:
;; +[ms] -> perform mouse-2 until held for [ms], then mouse-1
;; -[ms] -> perform mouse-1 until held for [ms], then mouse-2
;; 'double -> mouse-2 on double click
;; nil -> mouse-1 never follows link
;; <anything> -> mouse-1 /always/ follows link
(:option mouse-1-click-follows-link t))
(setup mu4e
(:load-from "/usr/share/emacs/site-lisp/mu4e")
(:autoload (mu4e :interactive t)
make-mu4e-context)
(:option message-kill-buffer-on-exit t
message-send-mail-function #'smtpmail-send-it
mu4e-change-filenames-when-moving t
mu4e-completing-read-function 'completing-read
mu4e-compose-context-policy 'ask-if-none
mu4e-context-policy 'ask-if-none
mu4e-contexts
(list
;; Work
(make-mu4e-context
:name "Work"
:match-func (lambda (msg)
(when msg
(string-prefix-p
work-mail-dir
(mu4e-message-field msg :maildir))))
:vars `((user-mail-address . ,work-email)
(smtpmail-smtp-server . ,work-smtp-server)
(mu4e-compose-format-flowed . nil)
(mu4e-drafts-folder
. ,(concat work-mail-dir "/[Gmail]/Drafts"))
(mu4e-sent-folder
. ,(concat work-mail-dir "/[Gmail]/Sent Mail"))
(mu4e-refile-dir
. ,(concat work-mail-dir "/[Gmail]/All Mail"))
(mu4e-trash-folder
. ,(concat work-mail-dir "/[Gmail]/Trash"))
(mu4e-maildir-shortcuts
. ,(mapcar (lambda (cell)
(let ((dir (car cell))
(char (cdr cell)))
(cons (concat work-mail-dir dir) char)))
'(("/Inbox" . ?i)
("/[Gmail]/All Mail" . ?a)
("/[Gmail]/Sent" . ?s)
("/[Gmail]/Drafts" . ?d)
("/[Gmail]/Trash" . ?t))))))
;; Home
(make-mu4e-context
:name "Home"
:match-func (lambda (msg)
(when msg
(string-prefix-p
home-mail-dir
(mu4e-message-field msg :maildir))))
:vars `((user-mail-address . ,home-email)
(smtpmail-smtp-server . ,home-smtp-server)
(mu4e-compose-signature . "~ Case")
(mu4e-compose-format-flowed . nil)
(mu4e-drafts-folder
. ,(concat home-mail-dir "/Drafts"))
(mu4e-sent-folder
. ,(concat home-mail-dir "/Sent"))
(mu4e-refile-folder
. ,(concat home-mail-dir "/Archive"))
(mu4e-trash-folder
. ,(concat home-mail-dir "/Trash"))
(mu4e-maildir-shortcuts
. ,(mapcar (lambda (cell)
(let ((dir (car cell))
(char (cdr cell)))
(cons (concat home-mail-dir dir) char)))
'(("/INBOX" . ?i)
("/Archive" . ?a)
("/Sent" . ?s)
("/Drafts" . ?d)
("/Trash" . ?t)))))))
mu4e-get-mail-command "mbsync -a"
mu4e-maildir "~/mail"
mu4e-update-interval (unless
;; I just realized... there is probably a
;; /much/ better way to do this.
(file-exists-p
(expand-file-name
"systemd/user/mbsync.timer"
(getenv "XDG_CONFIG_HOME")))
(* 60 5))
sendmail-program (seq-some #'executable-find
'("msmtp"
"sendmail"))
message-sendmail-f-is-evil t
message-sendmail-extra-arguments '("--read-envelope-from")
message-send-mail-function #'smtpmail-send-it
send-mail-function #'smtpmail-send-it
smtpmail-smtp-service 465
smtpmail-stream-type 'ssl)
(:with-mode mu4e-view-mode
(:hook #'reading-mode)))
(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 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
#'prettify-symbols-mode
;; #'electric-pair-local-mode
#'acdw/setup-fringes
#'display-fill-column-indicator-mode
(defun prog-mode@auto-fill ()
(setq-local comment-auto-fill-only-comments t)
(turn-on-auto-fill))))
(setup pulse
(:option pulse-flag nil
pulse-delay 0.5
pulse-iterations 1)
;; XXX: this doesn't work yet. I only want to pulse the line in the active
;; window, so when I have the same buffer viewed in multiple windows I can
;; still see where my cursor is. To see the issue, C-x 2 then C-x o a few
;; times.
(defun pulse-line-current-window (&rest _)
"Pulse the current line, but only if this window is active."
(pulse-momentary-highlight-one-line (window-point (selected-window))))
(dolist (func '(scroll-up-command
scroll-down-command
recenter-top-bottom
other-window
switch-to-buffer
redraw-frame))
(advice-add func :after #'pulse-line-current-window)))
(setup re-builder
(require 'acdw-re)
(:global "C-M-5" #'re-builder
"C-M-%" #'re-builder)
(:with-map reb-mode-map
(:bind "C-c C-k" #'reb-quit
"RET" #'reb-replace-regexp))
(:with-map reb-lisp-mode-map
(:bind "RET" #'reb-replace-regexp)))
(setup repeat
;; new for Emacs 28!
(:only-if (fboundp #'repeat-mode))
(:option repeat-exit-key "g"
repeat-exit-timeout 5)
(repeat-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-major-mode #'lisp-interaction-mode
lexical-binding t
initial-scratch-message
(concat ";; Howdy, "
(nth 0 (split-string
user-full-name))
"! "
"Welcome to GNU Emacs.\n\n"))
(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 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 shr
(:option shr-width fill-column
shr-max-image-proportion 0.6
shr-image-animate t
shr-discard-aria-hidden t))
(setup text
(:hook turn-on-auto-fill
tildify-mode
acdw/setup-fringes))
(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 view
(:option view-read-only t)
(:hook (defun acdw/read-view-mode ()
(reading-mode (if view-mode +1 -1)))))
(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 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)
;; http://ruzkuku.com/emacs.d.html#orgc62eb58
(:advise cycle-spacing :around
(defun cycle-spacing@newlines-by-default (old arg &rest _)
(funcall old (if (numberp arg) (- arg) arg)))))
(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 (0x0
:host gitlab
:repo "willvaughn/emacs-0x0"))
(:option 0x0-default-server 'ttm))
(setup (:straight (actually-selected-window
:host github
:repo "duckwork/actually-selected-window.el"))
(actually-selected-window-mode +1))
(setup (:straight-when 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-when 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"))
(require 'acdw-apheleia)
(add-hook 'before-save-hook #'apheleia-dumb-auto-format)
;; Aphelia can't find prettier on Windows (though I
;; installed it, I think), and it keeps trying to start
;; new processes until Emacs runs out of subprocess space.
;; So I just enable it at home.
(unless (acdw/system :work)
(apheleia-global-mode +1)))
(setup (:straight async)
(dired-async-mode +1))
(setup (:straight avy)
(:global "M-j" #'avy-goto-char-timer
"C-c C-j" #'avy-resume)
(:with-feature isearch
(:bind "M-j" #'avy-isearch)))
(setup (:straight circe)
(require 'circe)
(require 'acdw-irc)
(:also-load acdw-circe)
(defun acdw-circe/format-meta (string)
"Return a format string for `lui-format'."
(format "{nick:%1$d.%1$ds} *** %s" (- acdw-irc/left-margin 3) string))
(:option acdw-irc/left-margin 20
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 (format
(format "%%%ds* {nick} {body}"
(- acdw-irc/left-margin 2)) " ")
circe-format-say (format
"{nick:%1$d.%1$ds} | {body}"
(- acdw-irc/left-margin 3))
circe-format-self-action circe-format-action
circe-format-self-say (format
"{nick:%1$d.%1$ds} > {body}"
(- acdw-irc/left-margin 3))
circe-format-server-part (acdw-circe/format-meta
"PART {channel}: {reason}")
circe-format-server-quit (acdw-circe/format-meta "QUIT: {reason}")
circe-format-server-quit-channel (acdw-circe/format-meta
"QUIT {channel}: {reason}")
circe-format-server-join (acdw-circe/format-meta "JOIN: {userinfo}")
circe-format-server-rejoin (acdw-circe/format-meta
(concat "REJOIN: {userinfo}"
" after {departuredelta}"))
circe-format-server-topic (acdw-circe/format-meta
"TOPIC: {new-topic}")
circe-prompt-string (format (format "%%%ds> "
(- acdw-irc/left-margin 2))
" ")
circe-reduce-lurker-spam t
circe-server-auto-join-default-type :after-auth
circe-server-buffer-action (lambda (buf)
(message "Connected to %s" buf)))
(with-eval-after-load 'circe
(:face circe-nick-highlight-face
((t (:inherit (modus-themes-hl-line modus-themes-bold))))
;; circe-my-message-face
;; ((t (:inherit (modus-themes-slant))))
))
(with-eval-after-load 'topsy
(:option (append topsy-mode-functions)
'(circe-channel-mode . circe-current-topic)))
(:bind "C-c C-p" #'circe-command-PART
"C-c C-t" #'circe-current-topic ; in acdw-circe.el
"C-l" #'lui-track-jump-to-indicator
"<C-return>" #'circe-chat@set-prompt)
(:advise circe-command-PART :after #'circe-part@kill-buffer
circe-command-QUIT :after #'circe-quit@kill-buffer
circe-command-GQUIT :after #'circe-gquit@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
#'circe-chat@set-prompt
#'topsy-mode))
(:bind "C-c C-s" #'circe-command-SLAP)
(autoload 'circe-nick-color-reset "circe-color-nicks")
(add-hook 'modus-themes-after-theme-hook
#'circe-nick-color-reset)
(:with-mode lui-mode
(:option lui-fill-column (+ fill-column acdw-irc/left-margin)
lui-fill-type nil ;;(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
lui-fill-remove-face-from-newline nil)
(:hook #'visual-fill-column-mode
#'visual-line-mode
#'enable-lui-track)
(:face lui-time-stamp-face
((t :inherit font-lock-comment-face)))
(:local-set visual-fill-column-extra-text-width
(cons acdw-irc/left-margin 0)
fringes-outside-margins t
right-margin-width 5
scroll-margin 0
word-wrap t
wrap-prefix (repeat-string acdw-irc/left-margin " ")
nyan-mode nil
line-number-mode nil
column-number-mode nil
file-percentage-mode nil))
(add-hook 'kill-emacs-hook
(defun circe-quit-all ()
(ignore-errors
(advice-remove 'circe-command-GQUIT 'circe-gquit@kill-buffer)
(circe-command-GQUIT "Quitting Emacs, bye!")))))
(setup (:straight (command-log-mode
:host github
:repo "positron-solutions/command-log-mode"))
;; I have many ideas as to how to change this.
(:option clm-window-text-scale 0
clm-logging-shows-buffer t
clm-log-globally t
clm-exceptions '(self-insert-command)
clm-window-size 0.25)
(el-patch-feature command-log-mode)
(with-eval-after-load 'command-log-mode
(el-patch-defun clm--show-buffer (&optional clear)
"Displays the command log buffer in a window.
CLEAR will clear the buffer if it exists before returning it."
(let ((buffer (clm--setup-buffer clear)))
(let ((win (get-buffer-window buffer)))
(unless (windowp win)
(let ((new-win (el-patch-swap
(split-window-horizontally
(- 0 clm-window-size))
(if (< (window-pixel-width) (window-pixel-height))
(split-window-vertically
(- (if (floatp clm-window-size)
(floor (* (window-height) clm-window-size))
clm-window-size)))
(split-window-horizontally
(- (if (floatp clm-window-size)
(floor (* (window-width) clm-window-size))
clm-window-size)))))))
(set-window-buffer new-win buffer)
(set-window-dedicated-p new-win t)
(el-patch-add
(with-current-buffer buffer
(setq-local mode-line-format nil)))))
buffer)))))
(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))
(:option (append consult-buffer-sources) #'circe-buffer-source)
(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
consult-preview-key (kbd "M-.")
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 consult-dir)
(:with-feature project
(:autoload project--read-project-list))
(:global "C-x C-d" #'consult-dir)
(with-eval-after-load 'vertico
(:with-map vertico-map
(:bind "C-x C-d" #'consult-dir
"C-x C-j" #'consult-dir-jump-file))))
(setup (:straight crux)
(:global "C-o" #'crux-smart-open-line
"M-o" #'open-paragraph
"C-M-\\" #'crux-cleanup-buffer-or-region
"C-x 4 t" #'crux-transpose-windows)
(el-patch-feature crux)
(with-eval-after-load 'crux
(el-patch-defun crux-reopen-as-root ()
"Find file as root if necessary.
Meant to be used as `find-file-hook'.
See also `crux-reopen-as-root-mode'."
(unless (or
;; This helps fix for `nov-mode', and possibly others.
(el-patch-add (null buffer-file-name))
(tramp-tramp-file-p buffer-file-name)
(equal major-mode 'dired-mode)
(not (file-exists-p (file-name-directory buffer-file-name)))
(file-writable-p buffer-file-name)
(crux-file-owned-by-user-p buffer-file-name))
(crux-find-alternate-file-as-root buffer-file-name))))
(crux-reopen-as-root-mode +1))
;; (setup (:straight-when
;; (define-repeat-map
;; :host nil
;; :repo "https://tildegit.org/acdw/define-repeat-map.el")
;; (acdw/system :home))
;; (require 'define-repeat-map ; just for me
;; (acdw/dir
;; "straight/build/define-repeat-map/define-repeat-map.el"))
;; (defun acdw/other-window-or-switch-buffer-backward ()
;; (interactive)
;; (setq repeat-map 'other-window-repeat-map)
;; (acdw/other-window-or-switch-buffer -1))
;; (define-repeat-map other-window
;; ("o" acdw/other-window-or-switch-buffer
;; "O" acdw/other-window-or-switch-buffer-backward))
;; (define-repeat-map case
;; ("c" capitalize-word
;; "u" upcase-dwim
;; "l" downcase-dwim)
;; (:continue "f" forward-word
;; "b" backward-word)
;; (:enter capitalize-dwim
;; upcase-dwim
;; downcase-dwim))
;; (define-repeat-map page-navigation
;; ("]" forward-page
;; "[" backward-page))
;; (define-repeat-map windmove
;; (;; moving
;; "<left>" windmove-left
;; "<right>" windmove-right
;; "<up>" windmove-up
;; "<down>" windmove-down
;; ;; swapping
;; "<S-left>" windmove-swap-states-left
;; "<S-right>" windmove-swap-states-right
;; "<S-up>" windmove-swap-states-up
;; "<S-down>" windmove-swap-states-down))
;; (define-repeat-map winner-mode
;; ("/" winner-undo
;; "?" winner-redo)))
(setup (:straight dictionary)
(:option dictionary-use-single-buffer t)
(autoload 'dictionary-search "dictionary"
"Ask for a word and search it in all dictionaries" t)
(autoload 'dictionary-match-words "dictionary"
"Ask for a word and search all matching words in the dictionaries" t)
(autoload 'dictionary-lookup-definition "dictionary"
"Unconditionally lookup the word at point." t)
(autoload 'dictionary "dictionary"
"Create a new dictionary buffer" t)
(autoload 'dictionary-mouse-popup-matching-words "dictionary"
"Display entries matching the word at the cursor" t)
(autoload 'dictionary-popup-matching-words "dictionary"
"Display entries matching the word at the point" t)
(autoload 'dictionary-tooltip-mode "dictionary"
"Display tooltips for the current word" t)
(autoload 'global-dictionary-tooltip-mode "dictionary"
"Enable/disable dictionary-tooltip-mode for all buffers" t)
(define-key lookup-map "d" #'dictionary-search)
(:hook #'reading-mode))
(setup (:straight (dogears
:host github
:repo "alphapapa/dogears.el"
:files (:defaults
(:exclude "helm-dogears.el"))))
(:option (append savehist-additional-variables) 'dogears-list)
(with-eval-after-load 'dogears
(dolist (mode '(magit-status-mode
elfeed-show-mode
elfeed-search-mode))
(:option (append dogears-ignore-modes) mode)))
(:global "M-g d" dogears-go)
(:autoload dogears-mode)
(dogears-mode +1))
(setup (:straight edit-indirect))
;; requires extension:
;; https://addons.mozilla.org/en-US/firefox/addon/edit-with-emacs1/
(setup (:straight edit-server)
(:require edit-server)
(edit-server-start)
(:option edit-server-default-major-mode 'text-mode
edit-server-url-major-mode-alist
(list (cons (rx (| "reddit.com"
"tildes.net"))
'markdown-mode)
(cons (rx "github.com")
'gfm-mode)
(cons "." 'text-mode)))
(:advise edit-server-make-frame :before
(defun edit-server@set-a-variable (&rest _)
(setq-local edit-server-frame-p t))))
(setup (:straight (electric-cursor
:host github
:repo "duckwork/electric-cursor"))
(electric-cursor-mode +1))
(setup (:straight (elfeed
:host github
:repo "skeeto/elfeed")
elfeed-protocol)
(:option elfeed-use-curl t
elfeed-curl-extra-arguments '("--insecure")
elfeed-feeds `(("fever+https://acdw@mf.acdw.net"
:api-url "https://mf.acdw.net/fever/"
:password ,(acdw/make-password-fetcher
:host "mf.acdw.net")
:autotags ; do I want to use elfeed-org ?
'(("r/emacs" reddit social emacs)
("protesilaos.com/codelog.xml" emacs)
("tildes.net" social)
("catandgirl.com" comics)
("qwantz.com" comics)
("emacsninja.com" emacs)
("falseknees.com" comics)
("emacslife.com" emacs)
("lisp.org" lisp programming)
("scheme.org" scheme programming)
("smbc-comics.com" comics)
("youtube.com" video)
("tilde.news" social)
("xkcd.com" comics))))
elfeed-show-unique-buffers t)
(:autoload elfeed-set-timeout)
(elfeed-set-timeout 3600)
(elfeed-protocol-enable)
(:advise elfeed :after
(defun elfeed@protocol-update (&rest _)
(elfeed-search-fetch nil)))
(:with-mode elfeed-search-mode
(:bind "G" (defun elfeed-protocol|update-first (arg)
(interactive "P")
(let ((first-proto (caar elfeed-feeds)))
(if arg
(call-interactively #'elfeed-protocol-fever-reinit)
(with-temp-message (format "Updating %s" first-proto)
(elfeed-protocol-fever-reinit first-proto)))))))
(:with-mode elfeed-show-mode
(:hook #'reading-mode)
(:local-set shr-max-image-proportion 0.9
visual-fill-column-width (+ fill-column 5))
;; 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 (: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 #'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 (:straight-when emacs-everywhere
(and (executable-find "xclip")
(executable-find "xdotool")
(executable-find "xprop")
(executable-find "xwininfo"))))
(setup (:straight (embark ; gotta git that fresh fresh
:host github
:repo "oantolin/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 (: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-when 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://azuracast.tilderadio.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
lisp-interaction-mode))
(setup (:straight-when exec-path-from-shell
(acdw/system :home))
(when (daemonp)
(exec-path-from-shell-initialize))
(exec-path-from-shell-copy-envs '("XDG_CONFIG_HOME"
"XDG_CONFIG_DIRS"
"XDG_DATA_HOME"
"XDG_DATA_DIRS"
"XDG_CACHE_HOME")))
(setup (:straight expand-region)
(:global "C-=" #'er/expand-region))
(setup (:straight-when fennel-mode
(executable-find "fennel"))
(:autoload (fennel-repl :interactive t))
(:file-match (rx ".fnl" eos)))
(setup (:straight flyspell-correct)
(:option flyspell-correct-interface #'flyspell-correct-completing-read
flyspell-correct--cr-key ";")
(:with-feature flyspell
(:bind "C-." #'flyspell-correct-wrapper
"<f7>" (defun acdw/flyspell-correct-f7 ()
"Run a full spell correction on the current buffer."
(interactive)
(save-mark-and-excursion
(flyspell-correct-move 0 :forward :rapid))))
(:unbind "C-;" "C-," "C-." "C-M-i")))
(setup (:straight-when forge
(acdw/system :home))
;; make sure to read Info manual with Forge (and Ghub) for setup
;; instructions.
(with-eval-after-load 'magit
(require 'forge)
(add-to-list 'forge-alist ; tildegit is a gitea server
'("tildegit.org" "tildegit.org/api/v1" "tildegit.org"
forge-gitea-repository))))
(setup (:straight (frowny
:host github
:repo "duckwork/frowny.el"))
(:option frowny-eyes (rx (| ":" ":-" ":'" "="))
frowny-eyes-looking-back-limit 2)
(global-frowny-mode +1))
(setup (:straight gcmh)
(:option gcmh-idle-delay 'auto)
(gcmh-mode +1))
(setup (:straight-when 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 (:straight git-modes))
(setup (:straight helpful)
(:require-after 3)
(:global "<help> f" #'helpful-callable
"<help> v" #'helpful-variable
"<help> k" #'helpful-key
"<help> o" #'helpful-symbol))
(setup (:straight (hippie-completing-read
:host github
:repo "duckwork/hippie-completing-read"))
(:global "M-/" #'hippie-completing-read))
(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 (:straight iscroll)
(define-globalized-minor-mode global-iscroll-mode iscroll-mode
(lambda () (iscroll-mode +1)))
(global-iscroll-mode +1))
(setup (:straight (kaomoji-insert
:host nil
:repo "https://tildegit.org/acdw/kaomoji-insert"))
(require 'kaomoji-insert)
(dolist (km '(("(Ծ‸ Ծ)" "suspicious")
("(¬‿¬)═ɜ ɛ═(⌐‿⌐ )" "pound it" "fist bump")
("▬▬▬▬▬▬▬▋ Ò╭╮Ó" "hammer")
("👁👄👁" "lewk")
("( ͡~ ͜ʖ ͡°)" "wink")
(" (づ ̄ ³ ̄)づ " "party")
("⊙﹏⊙" "uhhh" "unsure")))
(add-to-list 'kaomoji-insert-alist km))
(:global "C-x 8 k" #'kaomoji-insert))
;; (setup (:straight wrap-region)
;; (:hook-into org-mode)
;; (with-eval-after-load 'org
;; (dolist (punc '("=" "*" "/" "_" "+"))
;; (wrap-region-add-wrapper punc punc nil 'org-mode))))
(setup (:straight lacarte)
(:global "<f10>" #'lacarte-execute-menu-command))
(setup (:straight-when ledger-mode
(executable-find "ledger")))
(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
:open-secondary-multiple t))
(defun acdw/link-hint-open-all-links (prefix)
"Open all visible links.
When PREFIX is non-nil, open links with
`browse-url-secondary-browser-function'."
(interactive "P")
(avy-with link-hint-open-all-links
(link-hint--all (if prefix :open-secondary :open))))
(defun acdw/link-hint-open-multiple-links (prefix)
"Use `avy' to open multiple visible links at once.
When PREFIX is non-nil, open links with
`browse-url-secondary-browser-function'."
(interactive "P")
(avy-with link-hint-open-multiple-links
(link-hint--multiple (if prefix :open-secondary :open))))
(:option link-hint-avy-style 'at-full)
(:global "C-j"
(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)
(define-key lisp-interaction-mode-map (kbd "C-c e") #'macrostep-expand))
(setup (:straight magit)
(:global "C-x 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))
(:hook #'variable-pitch-mode
#'visual-fill-column-mode)
(:with-mode gfm-mode
(:file-match (rx "README.md" eos))
(:hook #'variable-pitch-mode))
(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"))
(:option mastodon-instance-url "https://writing.exchange"
mastodon-auth-source-file (car auth-sources)
mastodon-client--token-file (acdw/dir "mastodon.plstore")
mastodon-tl--enable-proportional-fonts t
mastodon-tl--enable-relative-timestamps nil)
(:hook #'hl-line-mode
#'reading-mode)
(defun mastodon-goto-toot@recenter ()
"Recenter the current toot."
(recenter -1))
(:advise mastodon-tl--goto-next-toot :after #'mastodon-goto-toot@recenter
mastodon-tl--goto-prev-toot :after #'mastodon-goto-toot@recenter))
(setup (:straight mode-line-bell)
(:option mode-line-bell-flash-time 0.1)
(mode-line-bell-mode +1))
(setup (:straight (modus-themes
:host gitlab
:repo "protesilaos/modus-themes"))
(:option modus-themes-slanted-constructs t
modus-themes-bold-constructs t
modus-themes-fringes nil
modus-themes-mode-line '(borderless)
modus-themes-region '(bg-only)
modus-themes-org-blocks 'gray-background
modus-themes-headings '((t . (background)))
modus-themes-lang-checkers '(straight-underline)
modus-themes-scale-headings nil)
(acdw/sunrise-sunset #'modus-themes-load-operandi
#'modus-themes-load-vivendi)
(add-hook 'modus-themes-after-load-theme-hook
(defun modus-themes@customize-faces ()
"Customize faces of modus-themes."
;; (dolist (face '(font-lock-builtin-face
;; ;; font-lock-comment-delimiter-face
;; ;; font-lock-coment-face
;; font-lock-constant-face
;; ;; font-lock-doc-face
;; font-lock-function-name-face
;; font-lock-keyword-face
;; font-lock-negation-char-face
;; font-lock-preprocessor-face
;; font-lock-regexp-grouping-backslash
;; font-lock-regexp-goruping-construct
;; font-lock-string-face
;; font-lock-type-face
;; font-lock-variable-name-face
;; font-lock-warning-face))
;; (modus-themes-with-colors
;; (custom-set-faces
;; `(,face
;; ((,class :foreground ,fg-main
;; :weight normal
;; :slant normal))))))
;; Other faces
(modus-themes-with-colors
(custom-set-faces
`(org-level-1
((,class :inherit (modus-themes-heading-1 fixed-pitch)
:extend t)))
`(org-level-2
((,class :inherit (modus-themes-heading-2 fixed-pitch)
:extend t)))
`(org-level-3
((,class :inherit (modus-themes-heading-3 fixed-pitch)
:extend t)))
`(org-level-4
((,class :inherit (modus-themes-heading-4 fixed-pitch)
:extend t)))
`(org-level-5
((,class :inherit (modus-themes-heading-5 fixed-pitch)
:extend t)))
`(org-level-6
((,class :inherit (modus-themes-heading-6 fixed-pitch)
:extend t)))
`(org-level-7
((,class :inherit (modus-themes-heading-7 fixed-pitch)
:extend t)))
`(org-level-8
((,class :inherit (modus-themes-heading-8 fixed-pitch)
:extend t))))))))
(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 (nyan-mode
:host github :repo "TeMPOraL/nyan-mode"
:fork (:host github :repo "duckwork/nyan-mode")
:files ("nyan-mode.el" "img")))
(:option nyan-animate-nyancat nil
nyan-bar-length 20
nyan-minimum-window-width (+ fill-column (/ nyan-bar-length 2)))
(nyan-mode +1)
(defun disable-nyan-mode ()
"Disable `nyan-mode' in current buffer."
(setq-local nyan-mode -1))
(dolist (mode '(eshell-mode
comint-mode))
(add-hook mode #'disable-nyan-mode)))
;; (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-matching-styles '(orderless-literal
orderless-regexp
;; orderless-flex
)
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
:type git
:repo "https://git.savannah.gnu.org/git/emacs/org-mode.git"
:local-repo "org"
:depth full
:pre-build (straight-recipes-org-elpa--build)
:build (:not autoloads)
:files (:defaults
"lisp/*.el"
("etc/styles/" "etc/styles/*")))
(org-contrib
:type git
:repo "https://git.sr.ht/~bzg/org-contrib"))
(:also-load acdw-org)
(require 'chd nil 'noerror)
(:option org-adapt-indentation nil
;; org-agenda-files nil ; only until I set this up
org-catch-invisible-edits 'show-and-error
org-clock-clocked-in-display 'mode-line
org-clock-frame-title-format (cons
'(t org-mode-line-string)
(cons " --- " frame-title-format))
org-clock-string-limit 7 ; just the clock bit
;; org-clock-string-limit 25 ; gives enough information
org-clock-persist t
org-confirm-babel-evaluate nil
org-cycle-separator-lines 0
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-list-demote-modify-bullet '(("-" . "+")
("+" . "*")
("*" . "-"))
org-log-done 'time
org-log-into-drawer t
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 (- (- fill-column (length org-ellipsis))))
(: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
"C-c w" #'chd/do-the-thing
"C-c C-n" #'acdw/org-next-heading-widen
"C-c C-p" #'acdw/org-previous-heading-widen
"C-x n t" #'org-narrow-to-task)
(:unbind "C-j" ; org-return-and-maybe-indent
"M-j")
(:local-set unfill-fill-function #'org-fill-paragraph
wc-count-words-function
(lambda (start end) "Count words stupidly with a limit."
(acdw-org/count-words-stupidly start
end
999)))
(with-eval-after-load 'org-export
(:option (append org-export-filter-final-output-functions)
#'org-export-remove-zero-width-spaces))
(:local-hook before-save-hook
(defun org/before-save@prettify-buffer ()
(save-mark-and-excursion
(mark-whole-buffer)
(org-fill-paragraph nil t))
(acdw-org/fix-blank-lines t)
(org-align-tags :all)))
(with-eval-after-load 'org
(org-clock-persistence-insinuate))
(with-eval-after-load 'consult
(defun consult-clock-in (&optional match scope resolve)
"Clock into an Org heading."
(interactive (list nil nil current-prefix-arg))
(require 'org-clock)
(org-clock-load)
(save-window-excursion
(consult-org-heading
match
(or scope
(thread-last org-clock-history
(mapcar 'marker-buffer)
(mapcar 'buffer-file-name)
(delete-dups)
(delq nil))
(user-error "No recent clocked tasks")))
(org-clock-in nil (when resolve
(org-resolve-clocks)
(org-read-date t t)))))
(consult-customize consult-clock-in
:prompt "Clock in: "
:preview-key (kbd "M-.")
:group
(lambda (cand transform)
(if transform
(substring
cand
(next-single-property-change
0 'consult-org--buffer cand))
(let ((m (car (get-text-property
0 'consult-org--heading cand))))
(if (member m org-clock-history)
"*Recent*"
(buffer-name (marker-buffer m))))))))
(:advise org-delete-backward-char :override #'acdw-org/delete-backward-char)
(el-patch-feature org)
(with-eval-after-load 'org
(el-patch-defun org-format-outline-path (path &optional
width prefix separator)
"Format the outline path PATH for display.
WIDTH is the maximum number of characters that is available.
PREFIX is a prefix to be included in the returned string,
such as the file name.
SEPARATOR is inserted between the different parts of the path,
the default is \"/\"."
(setq width (or width 79))
(setq path (delq nil path))
(unless (> width 0)
(user-error "Argument `width' must be positive"))
(setq separator (or separator "/"))
(let* ((org-odd-levels-only nil)
(fpath (concat
prefix (and prefix path separator)
(mapconcat
(lambda (s) (replace-regexp-in-string "[ \t]+\\'" "" s))
(cl-loop for head in path
for n from 0
collect
(el-patch-swap
(org-add-props
head nil 'face
(nth (% n org-n-level-faces)
org-level-faces))
head))
separator))))
(when (> (length fpath) width)
(if (< width 7)
;; It's unlikely that `width' will be this small, but don't
;; waste characters by adding ".." if it is.
(setq fpath (substring fpath 0 width))
(setf (substring fpath (- width 2)) "..")))
fpath))))
(setup (:straight org-appear)
(:option org-appear-autoemphasis t
org-appear-autoentities t
org-appear-autokeywords t
org-appear-autolinks nil
org-appear-autosubmarkers t
org-appear-delay 0)
(:hook-into org-mode))
(setup (:straight org-sticky-header)
(:hook-into org-mode))
(setup (:straight package-lint
package-lint-flymake))
(setup (:straight page-break-lines)
(global-page-break-lines-mode +1))
(setup (:straight paredit)
(:bind "DEL" #'paredit-backward-delete
"C-<backspace>" #'paredit-backward-kill-word
"M-w" #'paredit-copy-as-kill
"RET" #'paredit-newline)
(:unbind "C-j" ; paredit-newline
)
(:hook-into emacs-lisp-mode lisp-interaction-mode
ielm-mode sly-repl-mode
lisp-mode scheme-mode)
(:also-load eldoc)
(eldoc-add-command 'paredit-backward-delete 'paredit-close-round))
(setup (:straight paren-face)
(:hook-into emacs-lisp-mode
ielm-mode sly-repl-mode
lisp-mode
lisp-interaction-mode
scheme-mode))
(setup (:straight-when (pdf-tools
:host github
:repo "vedang/pdf-tools")
(acdw/system :home))
(:file-match (rx ".pdf" eos))
(pdf-loader-install))
(setup (:straight popper)
(:option popper-reference-buffers
`(,(rx "*Messages*")
,(rx "Output*" eol)
,(rx "*Async Shell Command*")
help-mode helpful-mode
compilation-mode)
popper-mode-line nil
popper-display-control t
popper-display-function
(defun popper/select-popup-smartly (buffer &optional _alist)
(let* ((widep (> (frame-pixel-width) (frame-pixel-height)))
(window (display-buffer-in-side-window
buffer
`((side . ,(if widep 'right 'bottom))
(slot . 1)
,(if widep
(cons 'window-width
popper-window-height)
(cons 'window-height
popper-window-height))))))
(select-window window)))
popper-window-height
(defun popper/figure-window-height (window)
(let* ((widep (> (frame-pixel-width) (frame-pixel-height)))
(fit-window-to-buffer-horizontally widep))
(fit-window-to-buffer
window
(floor (frame-pixel-height) 2)
(floor (frame-pixel-height) 4)
fill-column
fill-column))))
(:global "M-`" #'popper-toggle-latest
"C-`" #'popper-cycle)
(popper-mode +1)
(when (fboundp 'popper-echo-mode)
(popper-echo-mode +1)))
(setup (:straight-when (powershell
:host github
:repo "jschaf/powershell.el")
(acdw/system :work)))
(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 (:straight sicp))
(setup (:straight simple-modeline
minions)
(:also-load acdw-modeline)
(:option simple-modeline-segments
;; Yeah this is laid out like poo. It's so I can easily change
;; things around if need be.
'((;; left
acdw-modeline/winum
acdw-modeline/modified
acdw-modeline/buffer-name
acdw-modeline/vc-branch
acdw-modeline/wc
acdw-modeline/nyan-cat
acdw-modeline/position
) (;; right
acdw-modeline/track
simple-modeline-segment-misc-info
acdw-modeline/text-scale
simple-modeline-segment-process
acdw-modeline/god-mode-indicator
acdw-modeline/minions
acdw-modeline/reading-mode
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.
(el-patch-feature simple-modeline)
(with-eval-after-load 'simple-modeline
(el-patch-defun simple-modeline--format (left-segments right-segments)
"Return a string of `window-width' length containing LEFT-SEGMENTS and RIGHT-SEGMENTS, aligned respectively."
(let* ((left (simple-modeline--format-segments left-segments))
(right (simple-modeline--format-segments right-segments))
(reserve (length right)))
(concat
left
(propertize " "
'display (el-patch-swap
`((space :align-to (- right ,reserve)))
`((space :align-to
(- right
(- 1 right-fringe right-margin)
,reserve))))
'face '(:inherit simple-modeline-space))
right))))
(simple-modeline-mode +1))
(setup (:straight-when 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 (spongebob-case
:host github
:repo "duckwork/spongebob-case.el"))
(:global "C-c c s" #'spongebob-case-dwim))
(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 super-save)
(:option auto-save-default nil
super-save-auto-save-when-idle t
super-save-idle-duration 60
super-save-exclude '(".gpg")
super-save-remote-files nil)
(auto-save-visited-mode -1)
(super-save-mode +1))
(setup (:straight-when system-packages
(seq-some #'executable-find
;; I can't use `system-packages-supported-package-managers'
;; because, well, the package isn't installed yet. So
;; ... update this list if any package managers are added.
'("guix" "nix"
"brew" "macports"
"pacman" "emerge"
"zypper" "dnf"
"apt" "aptitude"
"xbps"))))
(setup (:straight-when systemd
(executable-find "systemd")))
(setup (:straight (topsy
:host github
:repo "alphapapa/topsy.el"))
(:hook-into prog-mode)
(:when-loaded
(:option topsy-header-line-format
`(:eval
(list
(propertize " "
'display
`((space
:align-to
,(unless
(bound-and-true-p visual-fill-column-mode)
0))))
(funcall topsy-fn))))))
(setup (:straight trashed)
(:option trashed-action-confirmer #'y-or-n-p))
(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 :host github :repo "purcell/unfill"
:fork (:host github :repo "duckwork/unfill")))
(:global "M-q" #'unfill-toggle))
(setup (:straight (unfocused
:host github
:repo "duckwork/unfocused"))
(unfocused-mode +1)
(:with-hook unfocused-hook
(:hook #'garbage-collect)))
(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)
(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-directory)
(vertico-mouse-mode +1)
(:with-map vertico-map
(:bind "RET" #'vertico-directory-enter
"DEL" #'vertico-directory-delete-char
"M-DEL" #'vertico-directory-delete-word))
(add-hook 'rfn-eshadow-update-overlay-hook #'vertico-directory-tidy))
(setup (:straight visual-fill-column)
(:option visual-fill-column-width (1+ fill-column)
visual-fill-column-center-text t
(append reading-modes) '(visual-fill-column-mode . +1)
(append reading-modes) '(visual-line-mode . +1)
(append reading-vars) '(fill-column . 0))
(:hook-into org-mode)
(:hook (defun visual-fill-column@setup ()
(if visual-fill-column-mode
(setq-local indicate-empty-lines nil
indicate-buffer-boundaries nil)
(acdw/setup-fringes))))
(:advise text-scale-adjust :after #'visual-fill-column-adjust)
;; Fix bindings
(when (bound-and-true-p mouse-wheel-mode)
(with-eval-after-load 'visual-fill-column
(dolist (margin '(right-margin left-margin))
(dolist (event '(wheel-down wheel-up))
(define-key visual-fill-column-mode-map
(vector margin event)
#'mwheel-scroll))))))
(setup (:straight visual-regexp)
(:global "M-%" #'vr/query-replace))
(setup (:straight-when vterm
(acdw/system :home))
(:straight (eshell-vterm
:host github
:repo "iostapyshyn/eshell-vterm"))
(eshell-vterm-mode +1)
(defalias 'eshell/v 'eshell-exec-visual))
(setup (:straight wc-mode)
(:option wc-modeline-format "[%tww]"
wc-idle-wait 2)
(:hook-into text-mode)
(:unbind "C-c C-w a"
"C-c C-w c"
"C-c C-w l"
"C-c C-w w"
"C-c C-w"))
(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)
(wgrep-setup))
;; (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 (:straight whitespace-cleanup-mode)
(global-whitespace-cleanup-mode +1))
(setup (:straight winum)
(:option winum-scope 'frame-local
winum-auto-setup-mode-line nil
winum-ignored-buffers '(" *which-key*")
winum-format " %s")
(winum-mode +1))
(setup (:straight xr))
(setup (:straight-when ytdious
(executable-find "mpv"))
(:also-load acdw-ytel) ; so named because I used ytel first
(:option ytdious-invidious-api-url "https://invidious.snopyta.org")
(:hook #'hl-line-mode)
(:global "C-c y" #'ytdious)
(:bind "v" #'acdw/ytdious-watch
"w" #'acdw/ytdious-copy-link
"q" #'acdw/ytdious-quit))
(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-up-to-char)
(call-interactively #'zzz-to-char)))))