emacs/init.el

2244 lines
88 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 --- Emacs initiation file -*- lexical-binding: t -*-
;; Author: Case Duckworth <acdw@acdw.net>
;; Created: Sometime during Covid-19, 2020
;; Keywords: configuration
;; URL: https://tildegit.org/acdw/emacs
;; Bankruptcy: 8
;;; License:
;; Everyone is permitted to do whatever they like 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.
;;; Code:
(let ((early-features `((early-init . ,(locate-user-emacs-file "early-init"))
acdw private +key)))
(dolist (feature early-features)
(require (or (car-safe feature) feature) (cdr-safe feature) :noerror)))
(setup (:require +casing)
(+casing-mode +1))
(setup (:require +emacs)
;; +emacs.el contains super-basic defaults that are basically necessary for
;; good functioning. In this block, I add extra things or more "experimental"
;; ones that might not belong in a separate file.
(:also-load +lisp)
(:option truncate-string-ellipsis "")
;; Bindings
(:global "C-x C-k" #'kill-current-buffer
"C-x 4 n" #'clone-buffer
"C-c v" #'visible-mode
"C-M-;" #'+lisp-comment-or-uncomment-sexp
"M-j" nil
"C-x o" (lambda () (interactive) (switch-to-buffer nil))
"C-x C-o" #'+open-paragraph
"C-w" #'+kill-word-backward-or-region
;; "C-x C-1" #'delete-other-windows
;; "C-x 2" #'+split-window-below-then
;; "C-x C-2" #'+split-window-below-then
;; "C-x 3" #'+split-window-right-then
;; "C-x C-3" #'+split-window-right-then
)
;; Font-lock keywords
(add-hook 'prog-mode-hook #'font-lock-todo-insinuate)
;; C-h deletes backward - see https://idiomdrottning.org/bad-emacs-defaults
(global-set-key (kbd "C-h") 'delete-backward-char)
(keyboard-translate ?\C-h ?\C-?)
;; Hooks
;; Advice
;; https://old.reddit.com/r/emacs/comments/rlli0u/whats_your_favorite_defadvice/hph14un/
(define-advice keyboard-escape-quit (:around (fn &rest r))
"Don't close splits on `keyboard-escape-quit'."
(let ((buffer-quit-function #'ignore))
(apply fn r))))
(setup (:require +init)
(:local-hook user-save-hook #'+init-sort)
(:hook #'+init-add-setup-to-imenu))
(setup (:require auth-source)
(:option auth-sources (list 'default
"secrets:passwords"
(private/ "authinfo")))
(:with-mode authinfo-mode
(:local-set truncate-lines t)))
(setup (:require autoinsert)
(setf (alist-get "\\.scm" auto-insert-alist nil nil #'equal)
'(insert "#!/bin/sh\n#| -*- scheme -*-\nexec csi -s $0 \"$@\"\n|#\n"))
;; (auto-insert-mode +1)
)
(setup (:require cus-edit)
;; I don't use Custom to actually /make/ any customizations, but it's handy to
;; (A) see what options are available and (B) persist some changes across
;; restarts, for example, `safe-local-variables'.
(:require +cus-edit)
(:option custom-file (private/ "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)
(dolist (var '(safe-local-variable-values
warning-suppress-types))
(add-to-list '+custom-variable-allowlist var))
(+custom-load-ignoring-most-customizations)
(advice-add #'custom-buffer-create-internal :after #'+cus-edit-expand-widgets)
(:with-mode Custom-mode
(:local-set imenu-generic-expression +cus-edit-imenu-generic-expression)))
(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 pulse)
(:also-load +pulse)
(:option pulse-flag nil
pulse-delay 0.5
pulse-iterations 1)
(dolist (command '(+ace-window-or-switch-buffer
pop-mark pop-globl-mark))
(add-to-list '+pulse-location-commands command))
(+ensure-after-init #'+pulse-location-mode))
(setup (:require reading)
;;(:hook-into view-mode) ; XXX doesn't go back
)
(setup (:require user-save)
(add-hook 'user-save-hook #'+clean-empty-lines)
(user-save-global-mode +1))
(setup (:require winner)
(winner-mode +1))
(setup +key
(+ensure-after-init #'+key-global-mode))
(setup abbrev
(:option abbrev-file-name (sync/ "abbrev.el")
save-abbrevs 'silent)
(with-eval-after-load 'user-save
(:with-mode edit-abbrevs-mode
(:hook #'turn-off-user-save-mode)))
(:hook-into text-mode
circe-chat-mode))
(setup autorevert
(:option global-auto-revert-non-file-buffers t
auto-revert-verbose nil)
(global-auto-revert-mode +1))
(setup bookmark
(:option bookmark-save-flag 1
bookmark-watch-bookmark-file 'silent))
(setup browse-url
(:require +browse-url)
(:option
browse-url-browser-function #'eww-browse-url
+browse-url-browser-function browse-url-browser-function
browse-url-generic-program (seq-some #'executable-find
'("firefox"
"chromium"
"chrome"))
browse-url-chrome-program (seq-some #'executable-find
'("chromium"
"chrome"
"google-chrome-stable"))
browse-url-generic-args (seq-some (lambda (e)
(when (equal (executable-find (car e))
browse-url-generic-program)
(cdr e)))
'(("firefox" "--new-tab")))
browse-url-secondary-browser-function (if (executable-find "firefox")
#'browse-url-firefox
#'browse-url-default-browser)
browse-url-new-window-flag nil
browse-url-firefox-arguments '("--new-tab")
browse-url-firefox-new-window-is-tab t)
(defvar +invidious-host
;; TODO: Add variables for other transformations and what-not.
"invidious.snopyta.org"
"Host for invidious instance.")
;; Set up external browsing URLs.
(add-to-list '+custom-variable-allowlist
'+browse-url-secondary-browser-regexps)
(dolist (domain '("github.com" "gitlab.com" "google.com"
"imgur.com" "twitch.tv"
"pixelfed" "instagram.com" "bibliogram.art"
"reddit.com" "teddit.net"
"twitter.com" "nitter.net" "t.co"
"streamable.com" "spotify.com"
"hetzner.cloud"
"melpa.org"))
(add-to-list '+browse-url-secondary-browser-regexps
(replace-regexp-in-string "\\." "\\\\." domain)))
;; Set up URL handlers.
(require 'chd)
(+browse-url-set-handlers
(list
(cons (rx ; images
"." (or "jpeg" "jpg" "png" "bmp") eos)
(lambda (&rest args)
(apply
(cond ((executable-find "mpv") #'+browse-image-with-mpv)
(t #'eww-browse-url))
args)))
(cons (rx (or ;; videos
"youtube.com" "youtu.be" "invidious" "yewtu.be"
(seq "." (or "mp4" "gif" "mov" "MOV" "webm") eos)
;; music
"soundcloud.com" "bandcamp.com"
(seq "." (or "ogg" "mp3" "opus" "m4a") eos)))
(lambda (&rest args)
(apply (if (executable-find "mpv")
#'+browse-url-with-mpv
browse-url-secondary-browser-function)
args)))
(cons chd/url-regexps #'browse-url-chrome)
(cons (+browse-url-secondary-browser-regexps-combine) ; non-text websites
(lambda (&rest args)
(apply browse-url-secondary-browser-function args)))
(cons "xkcd\\.com"
(lambda (&rest args)
(apply (if (fboundp #'xkcd-get)
(progn (require '+xkcd)
#'+xkcd-get-from-url)
+browse-url-browser-function)
args)))
(cons "." ; everything else
(lambda (&rest args)
(apply +browse-url-browser-function args)))))
;; Transform URLs before passing to `browse-url'
(:option +browse-url-transformations `((,(rx (or "youtube.com"
"youtu.be"))
. ,+invidious-host)
("twitter\\.com"
. "nitter.net")
("instagram\\.com"
. "bibilogram.art")
(,(rx (or "reddit.com"
"old.reddit.com"))
. "teddit.net")
("medium\\.com"
. "scribe.rip")
("www\\.npr\\.org"
. "text.npr.org")))
(+browse-url-transform-url-global-mode +1))
(setup calendar
(require '_location)
(:option diary-file (private/ "diary")))
(setup compile
(:option compilation-always-kill t
compilation-ask-about-save nil
compilation-scroll-output t))
(setup dired
(:also-load dired-x)
(:also-straight dired+)
(:option dired-recursive-copies 'always
dired-recursive-deletes 'always
dired-create-destination-dirs 'always
dired-do-revert-buffer t
dired-hide-details-hide-symlink-targets nil
dired-isearch-filenames 'dwim
delete-by-moving-to-trash t
dired-auto-revert-buffer t
dired-listing-switches "-AlF"
ls-lisp-dirs-first t
dired-ls-F-marks-symlinks t
dired-clean-confirm-killing-deleted-buffers nil
dired-no-confirm '(byte-compile
load chgrp chmod chown
copy move hardlink symlink
shell touch)
dired-dwim-target t)
(:local-set truncate-lines t)
(:bind "<backspace>" #'dired-up-directory)
(:hook #'dired-hide-details-mode
#'hl-line-mode
#'lin-mode)
(:+key "C-x C-j" #'dired-jump)
(dolist (refresh-after-func '(dired-do-flagged-delete))
(advice-add refresh-after-func :after #'revert-buffer))
(with-eval-after-load 'frowny
(add-to-list 'frowny-inhibit-modes #'dired-mode)))
(setup ecomplete (:quit)
(:load-after org-contacts)
(:also-load +ecomplete)
(:option message-mail-alias-type 'ecomplete
message-self-insert-commands nil
message-expand-name-standard-ui t)
(with-eval-after-load 'ecomplete
(:option completion-category-defaults nil)
(with-eval-after-load 'embark
(:bind-into embark-email-map
"+" #'+ecomplete-add-email
"\\" #'+ecomplete-remove-email)))
(add-hook 'message-sent-hook #'message-put-addresses-in-ecomplete))
;; (setup ehelp
;; ;; Trying this instead of `helpful'
;; (:global [help] 'ehelp-command
;; [f1] 'ehelp-command)
;; (with-eval-after-load 'vertico-multiform
;; (dolist (cmd '(electric-describe-key
;; electric-describe-mode
;; electric-describe-syntax
;; electric-describe-bindings
;; electric-describe-function
;; electric-describe-variable))
;; (setf (alist-get cmd vertico-multiform-commands) nil))))
(setup eldoc
(:hook-into elisp-mode
lisp-interaction-mode))
(setup elisp-mode
(:also-load +elisp)
(:option eval-expression-print-length nil
eval-expression-print-level nil)
(:with-map (emacs-lisp-mode-map lisp-interaction-mode-map)
(:bind "C-c C-c" #'eval-defun
"C-c C-k" #'+elisp-eval-region-or-buffer
"C-c C-z" #'ielm))
(advice-add #'eval-region :around #'+eval-region@pulse))
(setup eshell
(:also-load em-smart
em-tramp)
(:require +eshell)
(+define-dir eshell/ (locate-user-emacs-file "eshell")
"Where to place Eshell-specific files.")
(:option eshell-aliases-file (eshell/ "aliases")
;; What are these for???
eshell-rc-script (eshell/ "profile")
eshell-login-script (eshell/ "login")
eshell-destroy-buffer-when-process-dies t
eshell-directory-name eshell/
eshell-error-if-no-glob t
eshell-hist-ignore-dups t
eshell-kill-on-exit nil
eshell-prefer-lisp-functions t
eshell-prefer-lisp-variables t
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
eshell-banner-message ""
eshell-prompt-regexp (rx bol (* (not (any ?# ?$ ?\n)))
" " (any ?# ?$)
(* " ")))
(:+leader "s" #'+eshell-here
"C-s" #'+eshell-here)
(with-eval-after-load 'mwim
(setf (alist-get 'eshell-mode mwim-beginning-of-line-function)
#'eshell-bol))
(+eshell-eval-after-load
;; Local modes
(dolist (mode '((hungry-delete-mode . -1)))
(funcall (car mode) (cdr mode)))
;; Set local settings
(dolist (setting (list (cons 'outline-regexp eshell-prompt-regexp)
(cons 'page-delimiter eshell-prompt-regexp)
(cons 'imenu-generic-expression
(list "Prompt"
(concat eshell-prompt-regexp
"\\(.*\\)")
1))
(cons 'truncate-lines t)))
(set (make-local-variable (car setting)) (cdr setting)))
;; Bind keys
(dolist (binding '(("C-d" . +eshell-quit-or-delete-char)))
(define-key eshell-mode-map
(kbd (car binding)) (cdr binding)))
;; Environment variables
(dolist (environment '(("PAGER" . "cat")))
(setenv (car environment) (cdr environment)))))
(setup eww
(:also-load +eww)
(:option eww-search-prefix "https://duckduckgo.com/html?q="
url-privacy-level '(email agent cookies lastloc))
(add-hook 'eww-after-render-hook #'reading-mode)
(:hook #'+eww-bookmark-setup
#'+eww-track-readable-mode)
(:bind "b" #'bookmark-set
"B" #'bookmark-jump
"M-n" nil
"M-p" nil))
(setup flyspell
(:hook-into org-mode))
(setup hideshow
(:also-load +hideshow)
(:with-mode hs-minor-mode
(:hook-into prog-mode)
(:bind "C-<tab>" #'+hs-cycle
"C-S-<tab>" #'+hs-global-cycle
;; but y tho
"C-S-<iso-lefttab>" #'+hs-global-cycle)))
(setup ibuffer
(:also-load ibuf-ext)
(:option ibuffer-expert t
ibuffer-show-empty-filter-groups nil
ibuffer-saved-filter-groups
'(("default"
("Org" (mode . org-mode))
("emacs" (or (name . "^\\*scratch\\*$")
(name . "^\\*Messages\\*$")
(name . "^\\*Warnings\\*$")
(name . "^\\*straight-process\\*$")
(name . "^\\*Calendar\\*$")))
("customize" (mode . Custom-mode))
("emacs-config" (or (filename . ".emacs.d")
(mode . +init-mode)))
("git" (or (name . "^\*magit")
(name . "^\magit")))
("help" (or (mode . help-mode)
(mode . Info-mode)
(mode . helpful-mode)))
("irc" (or (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 . eww-mode))))))
(:hook (defun ibuffer@filter-to-default ()
(ibuffer-auto-mode +1)
(ibuffer-switch-to-saved-filter-groups "default"))))
(setup info
(:also-load +Info)
(:with-mode Info-mode ; -_-
(:hook #'reading-mode)
(:bind "c" #'+Info-copy-current-node-name
"w" #'+Info-copy-current-node-name)))
(setup ispell
(:also-load +ispell)
(put 'ispell-buffer-session-localwords
'safe-local-variable #'+ispell-safe-local-p)
(add-hook 'user-save-hook #'+ispell-move-buffer-words-to-dir-locals-hook))
(setup kmacro
(:also-load +kmacro)
(with-eval-after-load '+kmacro
;; (+kmacro-recording-indicator-mode +1)
(+kmacro-block-undo-mode +1)))
(setup minibuffer
(:require +minibuffer)
(:with-map minibuffer-local-map
(:bind "M-/" #'+minibuffer-complete-history)))
(setup mouse
;; Brand new for Emacs 28: see https://ruzkuku.com/texts/emacs-mouse.html
;; Actually, look at this as well: https://www.emacswiki.org/emacs/Mouse3
(when (fboundp 'context-menu-mode)
(:option context-menu-functions
'(context-menu-ffap
context-menu-region
context-menu-undo
context-menu-dictionary))
(context-menu-mode +1))
(dolist (click '(;; Fix scrolling in the margin
wheel-down double-wheel-down triple-wheel-down
wheel-up double-wheel-up triple-wheel-up))
(global-set-key (vector 'right-margin click) 'mwheel-scroll)
(global-set-key (vector 'left-margin click) 'mwheel-scroll)))
(setup net-utils
(:require +finger) ; fixes `finger' to use var below
(:option finger-X.500-host-regexps '(".") ; only send username
))
(setup notmuch
(:load-from "~/usr/share/emacs/site-lisp/")
(:load-after org-contacts)
(:also-load +notmuch +message)
(+define-dir notmuch/ (sync/ "emacs/notmuch")
"Notmuch configuration and data.")
(:option notmuch-init-file (notmuch/ "notmuch-init.el" t)
notmuch-address-save-filename (notmuch/ "addresses" t)
notmuch-address-use-company (featurep 'company)
notmuch-search-oldest-first nil
notmuch-archive-tags '("-inbox" "-unread"))
;; Composing mail
(:option message-kill-buffer-on-exit t
message-auto-save-directory "~/var/mail/drafts")
;; Sending mail
(:option send-mail-function #'sendmail-send-it
mail-specify-envelope-from t
message-sendmail-envelope-from 'header
mail-envelope-from 'header)
;; Extras and fixes
(with-eval-after-load 'notmuch
(load notmuch-init-file :noerror)
(add-hook 'message-setup-hook #'+message-signature-setup)
(add-hook 'message-send-hook #'+send-mail-dispatch)
(advice-add 'notmuch-tag :filter-args #'+notmuch-correct-tags)
(:option notmuch-saved-searches (list
(list :name "inbox"
:query (+notmuch-query-concat "tag:inbox"
"NOT tag:Spam")
:key "i")
(list :name "lists"
:query (+notmuch-query-concat "tag:/List/"
"tag:unread")
:key "l")
(list :name "unread"
:query (+notmuch-query-concat "tag:unread"
"NOT tag:Spam")
:key "u")
(list :name "flagged" :query "tag:flagged" :key "f")
(list :name "sent" :query "tag:sent" :key "t")
(list :name "drafts" :query "tag:draft" :key "d")
(list :name "all mail" :query "*" :key "a"))))
(:+leader "m" #'+notmuch-goto "C-m" #'+notmuch-goto
"n" #'notmuch "C-n" #'notmuch))
(setup org
;; Plain org with the `setup' form for sorting, but I install with straight.
(:straight (org
:type git :host nil
: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 :host nil
:repo "https://git.sr.ht/~bzg/org-contrib"))
;; DO NOT load system-installed org !!!
(setq load-path (cl-remove-if (lambda (path)
(string-match-p "lisp/org\\'" path))
load-path))
(:also-load +org
_work)
(:option org-adapt-indentation nil
org-archive-mark-done t
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 (sync/ "org/" t)
org-ellipsis truncate-string-ellipsis
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 (list (* (window-font-width)
(- fill-column 8)))
org-imenu-depth 3
org-list-demote-modify-bullet '(("-" . "+")
("+" . "-"))
org-log-done 'time
org-log-into-drawer t
org-num-skip-commented t
org-num-skip-unnumbered t
org-num-skip-footnotes t
org-outline-path-complete-in-steps nil
org-pretty-entities t
org-pretty-entities-include-sub-superscripts nil
org-refile-targets '((nil . (:maxlevel . 2))
(org-agenda-files . (:maxlevel . 1)))
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)))
org-todo-keywords '((sequence "TODO(t)" "WAIT(w@/!)" "ONGOING(o@)"
"|" "DONE(d!)")
(sequence "|" "CANCELED(k@)")
(sequence "MEETING(m)")
(sequence "ASSIGNED(a@/!)" "REVIEW(r)" "|" "DONE(d!)"))
org-use-speed-commands t
org-emphasis-alist '(("*" org-bold)
("/" org-italic)
("_" org-underline)
("=" org-verbatim)
("~" org-code)
("+" org-strikethrough)))
(:bind "RET" #'+org-return-dwim
"<S-return>" #'+org-table-copy-down
"C-c C-l" #'+org-insert-link-dwim
"C-c C-n" #'+org-next-heading-widen
"C-c C-p" #'+org-previous-heading-widen
"C-c C-o" #'+org-open-at-point-dwim
"`" #'+org-insert-tilde
"~" #'+org-insert-backtick)
(:global [f8] #'org-clock-in
[f9] #'org-clock-out)
(:hook #'variable-pitch-mode)
(:local-hook user-save-hook #'+org-before-save@prettify-buffer)
(advice-add #'org-delete-backward-char :override #'+org-delete-backward-char)
;; (define-advice org-open-at-point (:around (fn &rest r) open-external)
;; "Open links from org externally."
;; (let ((browse-url-browser-function browse-url-secondary-browser-function))
;; (apply fn r)))
(with-eval-after-load 'org
(setf (alist-get "\\.x?html?\\'" org-file-apps nil nil #'equal)
#'+org-open-html)
(org-clock-persistence-insinuate)
(org-link-set-parameters "tel" :follow #'+org-tel-open)
(org-link-set-parameters "sms" :follow #'+org-sms-open)
(setf (alist-get "\\.x?html?\\'" org-file-apps nil nil #'equal)
#'+org-open-html))
;; Extra keywords
(font-lock-add-keywords
'org-mode
'(;; Fancy list bullets
;; NOTE: these `progn' and `default's are necessary; otherwise Emacs
;; complains about "Invalid face reference: t" in org-mode buffers, because
;; `compose-region' returns t.
("^ *\\([-]\\) "
(0 (progn (compose-region (match-beginning 1) (match-end 1) "") 'default)))
("^ *\\([+]\\) "
(0 (progn (compose-region (match-beginning 1) (match-end 1) "»") 'default)))))
(with-eval-after-load 'form-feed
;; Horizontal lines
(font-lock-add-keywords
'org-mode
'(("^-----+" . form-feed--font-lock-face))))
(put 'browse-url-browser-function 'safe-local-variable
(lambda (val)
(eq (function-get val 'browse-url-browser-kind :autoload)
'external))))
(setup org-agenda
(:option org-agenda-skip-deadline-if-done t
org-agenda-skip-scheduled-if-done t
org-agenda-span 10
org-agenda-include-diary nil ; I use the org-diary features
org-agenda-todo-ignore-deadlines 'near
org-agenda-todo-ignore-scheduled 'future
org-agenda-include-deadlines t
org-deadline-warning-days 0
org-agenda-show-future-repeats 'next
org-agenda-window-setup 'current-window)
(dolist (var '(org-agenda-files
org-agenda-file-regexp
org-agenda-templates))
(add-to-list '+custom-variable-allowlist var))
(with-eval-after-load 'org
(add-to-list 'org-agenda-files (sync/ "org/" t)))
(:+leader "a" #'org-agenda "C-a" #'org-agenda)
(:hook #'hl-line-mode)
(add-hook 'org-agenda-after-show-hook 'org-narrow-to-subtree))
(setup org-attach
(:also-load +org-attach)
(:option org-attach-method 'lns)
(with-eval-after-load '+org-attach
(+org-attach-fix-args-mode +1)))
(setup org-capture
(:require +org-capture)
(:+leader "c" #'org-capture "C-c" #'org-capture)
(+org-capture-templates-setf "t" "Todo")
(+org-capture-templates-setf "tt"
`("Today!" entry (file "todo.org")
,(concat "* TODO %^{Title}\n"
"DEADLINE: %t\n"
"\n%?")))
(+org-capture-templates-setf "ts"
`("Someday..." entry (file "todo.org")
,(concat "* TODO %^{Title}\n"
":PROPERTIES:\n"
":CREATED: [%<%F %T>]\n"
":END:\n"
"\n%?")))
(+org-capture-templates-setf "tm"
`("Media" entry (file "todo.org")
,(concat "* TODO %^{TITLE}\n"
":PROPERTIES:\n"
":TITLE: %\\1\n"
":AUTHOR: %^{AUTHOR}\n"
":END:\n"
"\n%?")))
(+org-capture-templates-setf "l"
`("Link" entry (file "links.org")
"* %(+org-insert-link-dwim) %^g\n\n"))
(+org-capture-templates-setf "w" "Work")
(+org-capture-templates-setf "j"
'("Journal entry" plain
(file+olp+datetree "journal.org")
"**** %U\n%i\n%?"))
;; TODO: Prompt for identity file from ~/.ssh and try to guess the hostname
;; from there.
(+org-capture-templates-setf "s"
`("SSH Config" plain (file "~/.ssh/config")
,(concat "\n\nHost %^{Host: }"
"\nHostname %\\1"
"\nUser %^{User:|" (user-login-name) "}"
"\nIdentityFile %?"
"\nIdentitiesOnly yes"
"\nPubkeyAuthentication yes"
"\nPort %^{Port: |22}")
:unnarrowed t))
(+org-capture-sort))
(setup org-contacts (:quit)
(:require)
(:also-straight org-vcard) ; for importing Vcard files
(:option org-contacts-matcher "contact") ; Contacts are tagged "contact"
)
(setup org-export
(:also-load ox-md)
(:option 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)
(with-eval-after-load 'user-save
(advice-add 'org-export-dispatch :before 'user-save-run-hooks)))
(setup org-id
(:load-after org)
;; https://helpdeskheadesk.net/2022-03-13/
(:option org-id-method 'ts
org-attach-id-to-path-function-list '(org-attach-id-ts-folder-format
org-attach-id-uuid-folder-format)))
(setup password-cache
(:option password-cache t
password-cache-expiry (* 60 60)))
(setup prog
(:local-set comment-auto-fill-only-comments t)
(:hook #'prettify-symbols-mode
#'turn-on-auto-fill))
(setup scratch
(:require +scratch)
(:option initial-major-mode #'lisp-interaction-mode
initial-scratch-message
(concat (replace-regexp-in-string "^" ";; "
(string-trim (if (executable-find "fortune")
(shell-command-to-string "fortune -s")
"ABANDON ALL HOPE YE WHO ENTER HERE")))
"\n\n"))
(add-hook 'kill-buffer-query-functions #'+scratch-immortal))
(setup shr
(:option shr-width (- fill-column 5) ; pad out for wide letters
shr-use-fonts t))
(setup tab-bar
(:require +tab-bar)
(:option tab-bar-tab-name-function '+tab-bar-basename
tab-bar-tab-name-truncated-max 20
tab-bar-tab-name-ellipsis truncate-string-ellipsis
tab-bar-show t
tab-bar-close-button-show t
tab-bar-new-button-show nil
+tab-bar-menu-bar-icon " ; "
tab-bar-close-button (propertize "(x)"
'display t
'close-tab t)
tab-bar-new-button (propertize "+ " 'display t))
;; I need to set these here so that they take effect /before/ `display-time-mode'
(:option display-time-format "%H:%M"
display-time-mail-file :disable
display-time-load-average-threshold 50)
(:option tab-bar-format '(;;+tab-bar-format-menu-bar
tab-bar-format-history
tab-bar-format-tabs
tab-bar-separator
tab-bar-format-add-tab
tab-bar-format-align-right
;;+tab-bar-misc-info
+tab-bar-org-clock
+tab-bar-bongo
;;+tab-bar-emms
+tab-bar-tracking-mode
+tab-bar-notmuch-count
+tab-bar-date))
(tab-bar-mode +1)
(display-time-mode +1))
(setup text
(:hook #'turn-on-auto-fill))
(setup timer-list
(:hook #'hl-line-mode
#'lin-mode))
(setup tramp
(el-patch-feature tramp)
(with-eval-after-load 'tramp
(el-patch-defun tramp-debug-buffer-command-completion-p (_symbol buffer)
"A predicate for Tramp interactive commands.
They are completed by \"M-x TAB\" only in Tramp debug buffers."
(with-current-buffer buffer
(el-patch-wrap 2
(save-restriction
(widen)
(string-equal (buffer-substring 1 10) ";; Emacs:")))))))
(setup (:straight 0x0)
(:option 0x0-default-server 'ttm)
(with-eval-after-load 'embark
(define-key embark-region-map (kbd "U") #'0x0-dwim)))
(setup (:straight ace-window)
(:require +ace-window)
(:option aw-keys '(?a ?s ?d ?f ?g ?h ?j ?k ?l)
aw-display-mode-overlay nil
aw-scope 'frame)
(:+key "M-o" #'+ace-window-or-switch-buffer)
(:face aw-mode-line-face ((t (:foreground "red"))))
(+ace-window-display-mode +1))
(setup (:straight (actually-selected-window
:host github
:repo "duckwork/actually-selected-window.el"))
(actually-selected-window-mode +1))
(setup (:straight adaptive-wrap)
(:with-mode adaptive-wrap-prefix-mode
(:hook-into visual-column-mode)))
(setup (:straight-when affe
(or (executable-find "rg")
(and (executable-find "find")
(executable-find "grep"))))
(:load-after consult orderless vertico)
(setq affe-regexp-compiler (defun affe-orderless-regexp-compiler (input _type)
(setq input (orderless-pattern-compiler input))
(cons input (lambda (str) (orderless--highlight input str)))))
(+with-eval-after-loads (affe vertico-multiform)
(setq affe-regexp-compiler (defun affe-orderless-regexp-compiler (input _type)
(setq input (orderless-pattern-compiler input))
(cons input (lambda (str) (orderless--highlight input str)))))
(setf (alist-get 'affe-grep vertico-multiform-commands) '(buffer)
(alist-get 'affe-find vertico-multiform-commands) '(buffer))
(:+key "M-s g" #'affe-grep
"M-s f" #'affe-find)))
(setup (:straight alert)
(:option alert-default-style 'libnotify))
(setup (:straight anzu)
(:option anzu-cons-mode-line-p nil)
(:+key [remap query-replace] #'anzu-query-replace-regexp
[remap query-replace-regexp] #'anzu-query-replace-regexp)
(global-anzu-mode +1)
(:bind-into isearch
[remap isearch-query-replace] #'anzu-isearch-query-replace
[remap isearch-query-replace-regexp] #'anzu-isearch-query-replace-regexp))
(setup (:straight avy)
(:require avy +avy)
(:option avy-background t
avy-lead-faces
'(avy-lead-face
avy-lead-face-1 avy-lead-face-1 avy-lead-face-1
avy-lead-face-1 avy-lead-face-1 avy-lead-face-1))
(:face avy-background-face
((t (:foreground "#888888"))))
(:+key "M-j" #'avy-goto-char-timer)
(:bind-into isearch
"M-j" #'avy-isearch)
(:when-loaded
(setf (alist-get ?. avy-dispatch-alist) #'avy-action-embark)))
(setup (:straight bbdb)
(:require bbdb-autoloads
bbdb)
(:also-straight bbdb-vcard)
(bbdb-initialize 'gnus 'message))
(setup (:straight (bongo :type git
:flavor melpa
:files ("*.el" "*.texi" "images" "*.rb" "bongo-pkg.el" "*.info")
:pre-build ("makeinfo" "--no-split" "bongo.texi")
:host github
:repo "dbrock/bongo"))
(:also-load +bongo)
(:option bongo-default-directory "~/var/music"
bongo-custom-backend-matchers '((mpv . (("https:") . t)))
+bongo-radio-stations ; use `+bongo-radio' for these
`(;; Local radio
("KLSU"
. "http://130.39.238.143:8010/stream.mp3")
("WRKF: NPR for the Capital Region"
. ,(concat "https://playerservices.streamtheworld.com/api/"
"livestream-redirect/WRKFFM.mp3"))
("WRKF HD-2"
. ,(concat "https://playerservices.streamtheworld.com/api/"
"livestream-redirect/WRKFHD2.mp3"))
("WBRH: Jazz & More"
. "http://wbrh.streamguys1.com/wbrh-mp3")
("KBRH Blues & Rhythm Hits"
. "http://wbrh.streamguys1.com/kbrh-mp3")
;; Soma FM
("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")
;; Tildeverse & Friends
("tilderadio"
. "https://azuracast.tilderadio.org/radio/8000/radio.ogg")
("vantaradio"
. "https://vantaa.black/radio")
;; Other online radio
("BadRadio: 24/7 PHONK"
. "https://s2.radio.co/s2b2b68744/listen")
("Cafe - lainon.life"
. "https://lainon.life/radio/cafe.ogg.m3u")
("Everything - lainon.life"
. "https://lainon.life/radio/everything.ogg.m3u")
("Swing - lainon.life"
. "https://lainon.life/radio/swing.ogg.m3u")
("Cyberia - lainon.life"
. "https://lainon.life/radio/cyberia.ogg.m3u")
("Nightwave Plaza - Online Vaporwave Radio"
. "http://radio.plaza.one/opus")))
(advice-add 'bongo-play :before #'+bongo-stop-all)
(with-eval-after-load 'notifications
(add-hook 'bongo-player-metadata-changed-hook #'+bongo-notify)))
(setup (:straight (cape
:host github :repo "minad/cape"))
(dolist (fn
;; All available cape capfs listed here. Add them to the front since
;; they're reversed with `add-to-list'.
'(cape-file
cape-dabbrev
cape-keyword
;;cape-abbrev
cape-ispell
;;cape-dict
))
(add-to-list 'completion-at-point-functions fn :append)))
(setup (:straight circe)
(:require _circe
+circe)
(:also-load circe-chanop)
(+ensure-after-init (lambda () (defalias 'irc '+irc "Start IRC.")))
;; Formatting options
(:option
;; Messages between users
circe-format-action (format (format "%%%ds* {nick} {body}"
(- +circe-left-margin 2))
" ")
circe-format-say (format "{nick:%1$d.%1$ds} | {body}"
(- +circe-left-margin 3))
circe-format-self-action circe-format-action
circe-format-self-say (replace-regexp-in-string "|" ">" circe-format-say)
circe-format-notice (format "-{nick:%1$d.%1$ds}---{body}"
(- +circe-left-margin 4))
circe-format-message (format (format "%%%ds@ *{nick}* {body}"
(- +circe-left-margin 2))
" ")
circe-format-message-action (replace-regexp-in-string "@" "*"
circe-format-message)
circe-format-self-message (format (format "%%%ds> *{chattarget}* {body}"
(- +circe-left-margin 2))
" ")
;; Meta messages
circe-format-server-channel-creation-time (+circe-format-meta
(concat "Channel {channel}"
" created on {date}") t)
circe-format-server-ctcp (+circe-format-meta
(concat "CTCP PING request to {target} from"
" {userhost}: {body}"))
circe-format-server-ctcp-ping-reply (+circe-format-meta
(concat
"CTCP PING reply to {target} from"
" {userhost}: {body}"))
circe-format-server-part (+circe-format-meta "PART {channel}: {reason}")
circe-format-server-quit (+circe-format-meta "QUIT: {reason}")
circe-format-server-quit-channel (+circe-format-meta
"QUIT {channel}: {reason}")
circe-format-server-join (+circe-format-meta "JOIN: {userinfo}")
circe-format-server-join-in-channel (+circe-format-meta
"JOIN {channel}: {userinfo}")
circe-format-server-lurker-activity (+circe-format-meta
"(JOINED {joindelta} ago)")
circe-format-server-message (+circe-format-meta "{body}" t)
circe-fromat-server-mode-change (+circe-format-meta
(concat "MODE: {target} {change}"
" by {setter} ({userhost})") t)
circe-format-server-netmerge (+circe-format-meta
(concat "NETMERGE: {split} at {date}"
" (/WL to see who's still missing)") t)
circe-format-server-netsplit (+circe-format-meta
(concat "NETSPLIT: {split}"
" (/WL to see who left)") t)
circe-format-server-nick-change (+circe-format-meta
"NICK WAS {old-nick} ({userhost})"
"new-nick")
circe-format-server-nick-regain (+circe-format-meta
"NICK REGAINED: {old-nick} ({userhost})"
"new-nick")
circe-format-server-notice (+circe-format-meta "-SERVER NOTICE- {body}" t)
circe-format-server-topic-time (+circe-format-meta
"TOPIC SET BY {setter} on {topic-date}")
circe-format-server-topic-time-for-channel (+circe-format-meta
(concat
"TOPIC ({channel}) SET BY"
" {setter} on {topic-date}"))
circe-format-server-whois-idle (+circe-format-meta "IDLE FOR {idle-duration}"
"whois-nick")
circe-format-server-whois-idle-with-signon (+circe-format-meta
(concat
"IDLE FOR {idle-duration}"
" (signon: {signon-date})")
"whois-nick")
circe-format-server-rejoin (+circe-format-meta
(concat "REJOIN: {userinfo} "
"after {departuredelta}"))
circe-format-server-topic (+circe-format-meta "TOPIC: {new-topic}")
circe-prompt-string (format (format "%%%ds> "
(- +circe-left-margin 2))
" "))
(:option +circe-server-buffer-action (lambda (buf)
(message "Connected to %s" buf))
+circe-network-inhibit-autoconnect _circe-network-inhibit-autoconnect
circe-network-options _circe-network-options
circe-color-nicks-everywhere t
circe-default-part-message "See You, Space Cowpokes . . ."
circe-default-user user-real-login-name
circe-reduce-lurker-spam t
circe-server-auto-join-default-type :after-auth)
(:bind "C-c C-p" #'circe-command-PART
"C-c C-t" #'+circe-current-topic
"C-l" #'lui-track-jump-to-indicator
"C-<return>" #'+circe-chat@set-prompt)
;; XXX: this doesn't quite work right.
(advice-add #'circe-command-PART :after #'+circe-kill-buffer)
(advice-add #'circe-command-QUIT :after #'+circe-quit@kill-buffer)
(advice-add #'circe-command-GQUIT :after #'+circe-gquit@kill-buffer)
(:with-mode circe-chat-mode
(:local-set lui-input-function #'+lui-filter)
(:hook #'enable-circe-color-nicks
#'enable-circe-new-day-notifier
#'+circe-chat@set-prompt
;; Filters
;;#'+circe-F/C-mode
;; For some reason `+circe-shorten-url-mode' won't work right out of
;; the gate.
;;(lambda () (run-at-time 0.25 nil #'+circe-shorten-url-mode))
)
(:bind "C-c C-s" #'circe-command-SLAP))
(:with-mode lui-mode
(:option lui-fill-column (+ fill-column +circe-left-margin)
lui-fill-type nil
lui-time-stamp-position 'right-margin
lui-time-stamp-format "| %H:%M"
lui-track-behavior 'before-switch-to-buffer
lui-track-indicator 'bar
lui-fill-remove-face-from-newline nil
lui-formatting-list `((,(+lui-make-formatting-list-rx "*")
1 lui-strong-face)
(,(+lui-make-formatting-list-rx "_")
1 lui-emphasis-face)
(,(+lui-make-formatting-list-rx "/")
1 lui-emphasis-face))
lui-autopaste-function
(defun +0x0-upload-string (string)
"Upload a string using 0x0."
(with-temp-buffer
(insert string)
(0x0-upload-text (0x0--choose-server)))
(current-kill 0)))
(add-to-list '+pulse-location-commands #'lui-track-jump-to-indicator)
(:face lui-track-bar ((t (:height 10
:underline (:color foreground-color
:style line
:position line)
:extend t :inhert (default)))))
(:hook #'visual-line-mode
#'enable-lui-track
#'visual-fill-column-mode
(defun +disable-electric-pair-mode ()
"Disable `electric-pair-mode' in the current buffer."
(interactive)
(electric-pair-local-mode -1))
#'enable-lui-autopaste)
(:local-set fringes-outside-margins t
right-margin-width (length lui-time-stamp-format)
scroll-margin 0
scroll-step 1
word-wrap t
wrap-prefix (+string-repeat +circe-left-margin " ")
line-number-mode nil
column-number-mode nil
file-percentage-mode nil
visual-fill-column-extra-text-width
(cons +circe-left-margin 0))
(with-eval-after-load 'vertico-multiform
(setf (alist-get 'lui-next-button-or-complete vertico-multiform-commands)
'(flat))))
(:with-mode tracking-mode
(:option tracking-position 'before-modes)
(:bind "C-c C-SPC" (lambda () (interactive)
(if (and (fboundp 'org-clocking-p)
(org-clocking-p))
(message "Bro, get back to work!")
(call-interactively #'tracking-next-buffer))))
(add-to-list 'mode-line-misc-info
'(tracking-mode
tracking-mode-line-buffers)))
(with-eval-after-load 'topsy
(:option (append topsy-mode-functions)
'(circe-channel-mode . +circe-current-topic)))
(with-eval-after-load 'circe-color-nicks
(add-hook 'modus-themes-after-load-theme-hook #'circe-nick-color-reset))
(add-hook 'kill-emacs-hook #'+circe-quit-all@kill-emacs))
(setup (:straight (clean-kill-ring
:host github
:repo "NicholasBHubbard/clean-kill-ring.el"))
(:require)
(:option clean-kill-ring-prevent-duplicates t)
(clean-kill-ring-mode +1))
(setup (:straight clhs))
(setup (:straight consult)
(:require consult +consult)
;; from Consult wiki
(:option register-preview-delay 0
register-preview-function #'consult-register-format
xref-show-xrefs-function #'consult-xref
xref-show-definitions-function #'consult-xref
tab-always-indent 'complete
completion-in-region-function #'consult-completion-in-region)
(advice-add #'register-preview :override #'consult-register-window)
(advice-add #'completing-read-multiple :override
#'consult-completing-read-multiple)
(dolist (binding '(;; C-c bindings (mode-specific-map)
("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)
;; Other custom bindings
("M-y" . consult-yank-pop)
;;("<f1> a" . consult-apropos)
;; M-g bindings (goto-map)
("M-g e" . consult-compile-error)
("M-g f" . consult-flymake) ; or consult-flycheck
("M-g g" . consult-goto-line)
("M-g M-g" . consult-goto-line)
("M-g o" . consult-outline) ; or consult-org-heading
("M-g m" . consult-mark)
("M-g k" . consult-global-mark)
("M-g i" . consult-imenu)
("M-g I" . consult-imenu-multi)
;; M-s bindings (search-map)
("M-s f" . consult-find)
("M-s F" . consult-locate)
("M-s g" . consult-grep)
("M-s G" . consult-git-grep)
("M-s r" . consult-ripgrep)
("M-s l" . consult-line)
("M-s L" . consult-line-multi)
("M-s m" . consult-multi-occur)
("M-s k" . consult-keep-lines)
("M-s u" . consult-focus-lines)
;; Isearch integration
("M-s e" . consult-isearch-history)))
(global-set-key (kbd (car binding)) (cdr binding)))
(with-eval-after-load 'isearch-mode
(dolist (binding '(("M-e" . consult-isearch-history)
("M-s e" . consult-isearch-history)
("M-s l" . consult-line)
("M-s L" . consult-line-multi)))
(define-key isearch-mode-map (car binding) (cdr binding))))
(:+menu "b" #'consult-buffer
"f" #'find-file)
(:bind-into org
"M-g o" #'consult-org-heading)
(advice-add 'consult-yank-pop :after #'+yank@indent)
(:when-loaded
(:option consult-narrow-key "<"
consult-project-root-function '+consult-project-root)
(consult-customize consult-theme
:preview-key '(:debounce 0.2 any))
(consult-customize consult-ripgrep consult-git-grep consult-grep
consult-bookmark consult-recent-file consult-xref
consult--source-recent-file
consult--source-project-recent-file
consult--source-bookmark consult-buffer
:preview-key (kbd "M-,"))
(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)))
(with-eval-after-load 'orderless
(:option consult--regexp-compiler #'consult--orderless-regexp-compiler))
(with-eval-after-load 'vertico-multiform
(setf (alist-get 'consult-buffer vertico-multiform-commands) '(flat))
(dolist (buf-cmd '(consult-find
consult-yank-pop
consult-locate
consult-grep
consult-git-grep
consult-ripgrep
consult-line
consult-line-multi
consult-multi-occur
consult-keep-lines
consult-focus-lines
consult-imenu
consult-imenu-multi
consult-outline))
(setf (alist-get buf-cmd vertico-multiform-commands) '(buffer))))))
(setup (:straight consult-dir)
(:+key "C-x C-d" #'consult-dir)
(:with-map vertico-map
(:bind "C-x C-d" #'consult-dir
"C-x C-j" #'consult-dir-jump-file)))
(setup (:straight consult-notmuch)
(:load-after consult notmuch)
(with-eval-after-load 'vertico-multiform
(setf (alist-get 'consult-notmuch vertico-multiform-commands) '(buffer)
(alist-get 'consult-notmuch-tree vertico-multiform-commands) '(buffer))))
(setup (:straight corfu) (:quit "Turns out, I actually like minibuffer completion better.")
(+with-ensure-after-init
(corfu-global-mode +1)))
(setup (:straight crossword)
;; This isn't the perfect Emacs crossword puzzle, but it's the only one I
;; know.
(:hook #'turn-off-+key-mode)
(:option crossword-save-path (sync/ "emacs/crosswords/" t)
crossword-empty-position-char "=")
(:face crossword-grid-face ((t :inherit 'font-lock-string-face))
crossword-current-face ((t :inherit 'highlight))
crossword-other-dir-face ((t :inherit 'font-lock-keyword-face))))
(setup (:straight crux)
;; yes it's silly I have an addon to this addon.
(:require crux +crux)
(:option crux-shell-func #'crux-eshell
crux-shell-buffer-name "eshell"
+crux-default-date-format "%F")
(:global "C-o" #'crux-smart-open-line
"C-x 4 t" #'crux-transpose-windows
"M-w" #'+crux-kill-ring-save
"C-k" #'crux-kill-and-join-forward
"C-c d" #'+crux-insert-date-or-time)
(:+leader "s" #'crux-visit-shell-buffer)
(crux-with-region-or-buffer indent-region)
(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 dictionary)
(:option dictionary-use-single-buffer t)
(autoload 'dictionary-search "dictionary"
"Ask for a word and search it in all dictionaries" t)
(:hook #'reading-mode))
(setup (:straight diff-hl)
(global-diff-hl-mode +1))
(setup (:straight dired-git-info)
(:bind-into dired
")" #'dired-git-info-mode))
(setup (:straight dired-open)
(:load-after dired))
(setup (:straight dired-subtree)
(:load-after dired)
(:bind-into dired
"TAB" #'dired-subtree-cycle
"i" #'dired-subtree-toggle))
(setup (:straight (discord
:host github
:repo "davep/discord.el"
:fork (:repo "duckwork/discord.el"))))
(setup (:straight dumb-jump)
(add-hook 'xref-backend-functions #'dumb-jump-xref-activate))
(setup (:straight-when ebuku
(executable-find "buku"))
(:option ebuku-display-on-startup 'recent
ebuku-recent-count 100))
(setup (:straight edit-server)
(:option edit-server-url-major-mode-alist `(("github\\.com" . ,(if (fboundp 'gfm-mode)
#'gfm-mode
#'markdown-mode))))
(add-hook 'edit-server-done-hook (lambda () (unfill-region (point-min) (point-max))))
(+with-ensure-after-init
(edit-server-start)))
(setup (:straight electric-cursor)
(:option electric-cursor-alist '((overwrite-mode . hbar)
(god-local-mode . box)
(t . bar)))
(electric-cursor-mode +1))
(setup (:straight elfeed)
(:require +elfeed)
(+define-dir elfeed/ (sync/ "emacs/elfeed/" t))
(:option
elfeed-curl-program-name (executable-find "curl")
elfeed-use-curl elfeed-curl-program-name
elfeed-curl-extra-arguments '("--insecure")
elfeed-enclosure-default-dir (cl-loop for dir in '("~/var/download/"
"~/Downloads/")
if (file-exists-p dir)
return dir)
elfeed-search-filter "@1-month-ago +unread"
elfeed-search-trailing-width 24
elfeed-search-title-min-width 24
elfeed-search-title-max-width 78
elfeed-search-remain-on-entry t
elfeed-show-unique-buffers t
elfeed-db-directory (elfeed/ "db/" t))
(:+leader "f" #'elfeed "C-f" #'elfeed)
(advice-add #'elfeed-search-fetch :after #'beginning-of-buffer)
(:with-mode elfeed-search-mode
(:bind "&" #'+elfeed-search-browse-generic
"w" #'elfeed-search-yank
"y" nil
"a" #'+elfeed-show-mark-read-and-advance)
(:hook #'hl-line-mode)
;; https://old.reddit.com/r/emacs/comments/rlli0u/whats_your_favorite_defadvice/hphfh4e/
(advice-add #'elfeed-search-update--force :after #'elfeed-db-save)
(advice-add #'elfeed :before #'elfeed-db-load))
(:with-mode elfeed-show-mode
(:bind "SPC" #'+elfeed-scroll-up-command
"S-SPC" #'+elfeed-scroll-down-command
"&" #'+elfeed-show-browse-generic
"RET" #'shr-browse-url
"w" #'elfeed-show-yank
"y" nil)
(:hook #'reading-mode)
(:option +elfeed--update-repeat (* 60 30) ; 1/2 hour
+elfeed--update-first-time 60))
(+elfeed-update-async-mode +1)
(add-hook '+elfeed-update-proceed-hook (defun non-work-hours? ()
"Return nil if during work hours, t otherwise."
(let* ((now (current-time))
(now* (decode-time now))
(work-start* (append '(0 0 8) (cdddr now*))) ; 8:00 AM
(work-end* (append '(0 0 18) (cdddr now*))) ; 6:00 PM
(work-start (encode-time work-start*))
(work-end (encode-time work-end*)))
(or (time-less-p now work-start)
(time-less-p work-end now))))))
(setup (:straight elfeed-org)
(:also-load +org-capture)
(:option rmh-elfeed-org-files (list (elfeed/ "elfeed.org" t)))
(elfeed-org)
(+org-capture-templates-setf "f"
`("Feed" entry
(file+olp ,(car rmh-elfeed-org-files) "Feeds")
"* %? %^g")))
(setup (:straight elpher))
(setup (:straight embark)
(:require embark
+embark)
(:option prefix-help-command 'embark-prefix-help-command
embark-keymap-prompter-key ";")
(:+key "C-." #'embark-act
"M-." #'embark-dwim
"<f1> B" #'embark-bindings)
(:with-map minibuffer-local-map
(:bind "C-." #'embark-act
"M-." #'embark-dwim))
(:with-map embark-file-map
(:bind "l" #'vlf))
;; Integrations
(with-eval-after-load 'vertico-multiform
(setf (alist-get 'embark-prefix-help-command vertico-multiform-commands)
nil)))
(setup (:straight embark-consult)
(:load-after consult embark)
(add-hook 'embark-collect-mode-hook #'consult-preview-at-point-mode))
(setup (:straight embrace)
(:+key "C-," #'embrace-commander))
(setup (:straight epithet)
(add-hook 'epithet-suggesters #'epithet-for-eww-url)
(dolist (hook '(Info-selection-hook
eww-after-render-hook
help-mode-hook
occur-mode-hook))
(add-hook hook #'epithet-rename-buffer)))
(setup (:straight eros)
(:hook-into emacs-lisp-mode
lisp-interaction-mode))
(setup (:straight eshell-bookmark)
(add-hook 'eshell-mode-hook #'eshell-bookmark-setup))
(setup (:straight eshell-syntax-highlighting)
(:hook-into eshell-mode))
;; (setup (:straight eshell-vterm)
;; (:load-after eshell)
;; (defalias 'eshell/v 'eshell-exec-visual)
;; (eshell-vterm-mode +1))
(setup (:straight-when exec-path-from-shell
(eq system-type 'gnu/linux))
(require 'exec-path-from-shell)
(dolist (var '("SSH_AUTH_SOCK"
"SSH_AGENT_PID"
"GPG_AGENT_INFO"
"LANG"
"LC_CTYPE"
"XDG_CONFIG_HOME"
"XDG_CONFIG_DIRS"
"XDG_DATA_HOME"
"XDG_DATA_DIRS"
"XDG_CACHE_HOME"))
(add-to-list 'exec-path-from-shell-variables var))
(exec-path-from-shell-initialize))
(setup (:straight expand-region)
(:require expand-region +expand-region)
(:option expand-region-fast-keys-enabled nil)
(:+key "C-=" #'er/expand-region
"C--" #'+er/contract-or-negative-argument))
(setup (:straight (fill-sentences-correctly
:host github
:repo "duckwork/fill-sentences-correctly.el"))
(fill-sentences-correctly-mode +1))
(setup (:straight (filldent
:host github
:repo "duckwork/filldent.el"))
(:+key "M-q" #'filldent-dwim))
(setup (:straight (flyspell-correct
:fork (:host github :repo "duckwork/flyspell-correct"
:branch "metadata-category")))
(:load-after flyspell)
(:also-load +flyspell-correct)
(:option flyspell-correct--cr-key ";")
(:bind-into flyspell
"C-;" #'flyspell-correct-wrapper
"<f7>" #'+flyspell-correct-buffer)
(with-eval-after-load 'vertico-multiform
(setf (alist-get 'flyspell vertico-multiform-categories) nil)))
(setup (:straight-when (forge
:host github :repo "magit/forge")
(eq system-type 'gnu/linux))
(require 'forge)
(add-to-list 'forge-alist
'("tildegit.org" "tildegit.org/api/v1" "tildegit.org"
forge-gitea-repository)))
(setup (:straight form-feed)
(global-form-feed-mode +1))
(setup (:straight (frowny
:host github
:repo "duckwork/frowny.el"))
(global-frowny-mode +1))
(setup (:straight gcmh)
(:option gcmh-idle-delay 'auto)
(gcmh-mode +1))
(setup (:straight (geiser
:type git
:flavor melpa
:files ("elisp/*.el" "doc/*" "geiser-pkg.el")
:pre-build ("make" "-Cdoc" "geiser.info")
:host gitlab
:repo "emacs-geiser/geiser")
geiser-chicken
macrostep-geiser
scheme-complete)
(:also-load +chicken)
(setf (alist-get "\\.scm\\'" auto-mode-alist nil nil #'string=)
'scheme-mode))
(setup (:straight (git-modes
:host github :repo "magit/git-modes"))
(:require git-modes))
(setup (:straight god-mode) (:quit "I could never get the hang of this.")
(setq god-mode-enable-function-key-translation nil)
(:require god-mode
+god-mode)
(:+key "C-M-g" #'god-mode-all)
(:with-mode god-local-mode
(:bind "i" #'+god-mode-insert
"a" nil)))
(setup (:straight helpful)
(:+key "<f1> f" #'helpful-callable
"<f1> v" #'helpful-variable
"<f1> k" #'helpful-key
"<f1> ." #'helpful-at-point)
(with-eval-after-load 'vertico-multiform
(dolist (cmd '(describe-symbol ; describe-* included here for completeness
describe-function describe-variable
helpful-function helpful-macro helpful-callable
helpful-variable))
(setf (alist-get cmd vertico-multiform-commands) nil))))
(setup (:straight (hippie-completing-read
:host github
:repo "duckwork/hippie-completing-read"))
(:+key "M-/" #'hippie-completing-read))
(setup (:straight hungry-delete)
(:option hungry-delete-chars-to-skip " \t"
hungry-delete-join-reluctantly nil)
(+with-ensure-after-init
(add-to-list 'hungry-delete-except-modes 'eshell-mode))
(:bind-into paredit
;; I define these functions here because they really require both packages
;; to make any sense. So, would I put them in `+hungry-delete' or
;; `+paredit' ? There's no satisfactory answer.
[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))))
(global-hungry-delete-mode +1))
(setup (:straight info+)
(:load-after info))
(setup (:straight isearch-mb)
;; This complicatedness is an attempt to make it easier to add and
;; subtract `isearch-mb' bindings using the suggestions in the
;; project's README.
(:load-after consult anzu)
(:when-loaded
(dolist (spec '((isearch-mb--with-buffer
("M-e" . consult-isearch)
("C-o" . loccur-isearch))
(isearch-mb--after-exit
("M-%" . anzu-isearch-query-replace)
("M-s l" . consult-line))))
(let ((isearch-mb-list (car spec))
(isearch-mb-binds (cdr spec)))
(dolist (cell isearch-mb-binds)
(let ((key (car cell))
(command (cdr cell)))
(when (fboundp command)
(add-to-list isearch-mb-list command)
(define-key isearch-mb-minibuffer-map (kbd key) command)))))))
(isearch-mb-mode +1))
(setup (:straight (jabber :repo "https://tildegit.org/wgreenhouse/emacs-jabber"
:host nil
:files ("*.el" "*.texi"
("jabber-fallback-lib"
"jabber-fallback-lib/hexrgb.el"
"jabber-fallback-lib/srv.el"
"jabber-fallback-lib/fsm.el")
"jabber-pkg.el")
:fork (:repo "https://tildegit.org/acdw/emacs-jabber"
:host nil)))
(:also-load +jabber)
(:option jabber-account-list '(("acdw@hmm.st"))
jabber-groupchat-buffer-format "xmpp:%n"
jabber-chat-buffer-format "xmpp:%n"
jabber-muc-private-buffer-format "xmpp:%n(%g)"
jabber-activity-show-p #'ignore
jabber-muc-decorate-presence-patterns
'(("\\( enters the room ([^)]+)\\| has left the chatroom\\)$")
("." . jabber-muc-presence-dim)))
(dolist (mode '(jabber-chat-mode
jabber-browse-mode
jabber-roster-mode
jabber-console-mode))
(add-hook (intern (format "%s-hook" mode)) #'visual-fill-column-mode))
(add-hook 'jabber-activity-mode-hook 'tracking-mode)
(:+leader "C-j" jabber-global-keymap)
(advice-add 'jabber-activity-add :after #'+jabber-tracking-add)
(advice-add 'jabber-activity-add-muc :after #'+jabber-tracking-add-muc))
(setup (:straight (keepassxc-shim
:host github :repo "duckwork/keepassxc-shim.el"))
(keepassxc-shim-activate))
(setup (:straight-when keychain-environment
(executable-find "keychain"))
(keychain-refresh-environment))
(setup (:straight lacarte)
(:+key "<f10>" #'lacarte-execute-menu-command)
(with-eval-after-load 'vertico-multiform
(setf (alist-get 'lacarte-execute-menu-command vertico-multiform-commands)
'(buffer grid (vertico-sort-function . vertico-sort-length-alpha)))))
(setup (:straight (lin :host gitlab :repo "protesilaos/lin"))
(require 'lin)
(+with-ensure-after-init
(dolist (hook lin-foreign-hooks)
(add-hook hook #'hl-line-mode)
(add-hook hook #'lin-mode))))
(setup (:straight link-hint)
(:require +link-hint)
(+link-hint-open-secondary-setup)
(+link-hint-open-chrome-setup)
(:option link-hint-avy-style 'at-full)
(:+key "M-l" +link-hint-map)
(:with-map +link-hint-map
(:bind "M-l" #'+link-hint-open-link "l" #'+link-hint-open-link
"M-o" #'+link-hint-open-secondary "o" #'+link-hint-open-secondary
"M-m" #'link-hint-open-multiple-links "m" #'link-hint-open-multiple-links
"M-w" #'link-hint-copy-link "w" #'link-hint-copy-link
"M-c" #'+link-hint-open-chrome "c" #'+link-hint-open-chrome)))
(setup (:straight (machine
:host github
:repo "duckwork/machine.el"))
(+with-ensure-after-init ; So that they override anything here.
;; Emoji fonts
(let ((ffl (font-family-list))
(emoji-fonts '("Noto Color Emoji"
"Noto Emoji"
"Segoe UI Emoji"
"Apple Color Emoji"
"FreeSans"
"FreeMono"
"FreeSerif"
"Unifont"
"Symbola"))
found)
(dolist (font emoji-fonts)
(when (member font ffl)
(push font found)
(set-fontset-font t 'symbol (font-spec :family font) nil :append)))
(nreverse found))
(machine-settings-load)))
(setup (:straight macrostep)
(:require macrostep)
(dolist (m '(emacs-lisp-mode-map
lisp-interaction-mode-map))
(define-key (symbol-value m) (kbd "C-c e") #'macrostep-expand)))
(setup (:straight (magit :host github :repo "magit/magit")))
(setup (:straight marginalia)
(marginalia-mode +1))
(setup (:straight markdown-mode)
(:option markdown-hide-markup nil)
(add-to-list 'auto-mode-alist (cons (rx (or ".md" ".markdown" ".mdown")
eos)
'markdown-mode))
(with-eval-after-load 'visual-fill-column
(:hook #'visual-fill-column-mode))
(with-eval-after-load 'apheleia
(when-let ((mdfmt-exe (executable-find "markdownfmt")))
(setf (alist-get 'markdownfmt apheleia-formatters) mdfmt-exe)
(setf (alist-get 'markdown-mode apheleia-mode-alist) 'markdownfmt)
(setf (alist-get 'gfm-mode apheleia-mode-alist) 'markdownfmt))))
(setup (:straight mastodon)
(:option mastodon-instance-url "https://tiny.tilde.website"
mastodon-client--token-file (.etc "mastodon.plstore")
mastodon-auth-source-file (seq-some (lambda (i)
(when (and (stringp i)
(file-exists-p i))
i))
auth-sources)
mastodon-tl--show-avatars t
mastodon-tl--enable-proportional-fonts nil)
(:hook #'mastodon-async-mode
#'visual-fill-column-mode
#'variable-pitch-mode
#'hl-line-mode
#'lin-mode))
(setup (:straight md4rd) (:quit "Janky a.f.")
;; `md4rd' is ... a bit janky, tbh. But I'm including this here so I have it.
;; TODO: enable opening Reddit links in md4rd
(:also-load _md4rd)
(defalias 'reddit 'md4rd "Browse Reddit.")
(with-eval-after-load 'md4rd
(run-with-timer 0 (* 60 59) 'md4rd-refresh-login)))
(setup (:straight minions)
(minions-mode +1))
(setup (:straight (mode-line-bell
:host github :repo "purcell/mode-line-bell"
:fork (:host github :repo "duckwork/mode-line-bell"
:branch "remap-face")))
;; This is still, annoyingly, not quite working right.
(:face mode-line-bell ((t (:inherit mode-line-highlight))))
(:option mode-line-bell-flash-time 0.1)
(mode-line-bell-mode +1))
(setup (:straight (modus-themes
:host gitlab
:repo "protesilaos/modus-themes"))
(require 'modus-themes (.etc "straight/build/modus-themes/modus-themes"))
(:also-load dawn)
(:option modus-themes-mixed-fonts t
modus-themes-bold-constructs nil
modus-themes-italic-constructs t
modus-themes-headings '((t . (background regular rainbow))))
(dotimes (facen-1 8)
(let ((facen (1+ facen-1)))
(custom-set-faces
`(,(intern (format "org-level-%s" facen))
((t :inherit
(,(intern (format "modus-themes-heading-%s" facen))
fixed-pitch))
:now)))))
(:face modus-themes-tab-active ((t :bold nil))
modus-themes-tab-inactive ((t :italic t)))
(dawn-schedule #'modus-themes-load-operandi
#'modus-themes-load-vivendi))
(setup (:straight mwim)
(:require +mwim)
(:option +mwim-passthrough-modes '(comint-mode
eshell-mode
vterm-mode
crossword-mode
geiser-repl-mode))
(:global "C-a" #'mwim-beginning
"C-e" #'mwim-end))
(setup (:straight notmuch-bookmarks)
(:load-after notmuch)
(:when-loaded
(notmuch-bookmarks-mode +1)))
(setup (:straight notmuch-labeler) (:quit "This is buggy")
(:load-after notmuch))
(setup (:straight ol-notmuch))
(setup (:straight orderless)
(:require +orderless)
(:option completion-styles '(substring orderless basic)
completion-category-defaults nil
completion-category-overrides
'((file (styles partial-completion))
(command (styles +orderless-with-initialism))
(variable (styles +orderless-with-initialism))
(symbol (styles +orderless-with-initialism)))
orderless-component-separator #'orderless-escapable-split-on-space
orderless-style-dispatchers '(+orderless-dispatch)))
(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-download)
(:require)
(:option org-download-method 'attach
org-download-backend (cond ((executable-find "curl") 'curl)
((executable-find "wget") 'wget)
(:else 'url-retrieve)))
(add-hook 'dired-mode-hook 'org-download-enable))
(setup (:straight org-mime)
(:option org-mime-export-ascii 'utf-8))
(setup (:straight org-mime)
(add-hook 'message-mode-hook
(defun org-mime-setup@message-mode ()
(local-set-key (kbd "C-c M-o") 'org-mime-htmlize)))
(add-hook 'org-mode-hook
(defun org-mime-setup@org-mode ()
(local-set-key (kbd "C-c M-o") 'org-mime-org-buffer-htmlize))))
(setup (:straight org-sticky-header)
;; (:hook-into org-mode)
)
(setup (:straight (org-taskwise
:host github
:repo "duckwork/org-taskwise.el"))
(with-eval-after-load 'org
(require 'org-taskwise)
(define-key org-mode-map (kbd "C-x n t") #'org-taskwise-narrow-to-task)))
(setup (:straight org-visibility)
(:require org-visibility)
(:option org-visibility-state-file (.etc "org-visibility")
org-visibility-include-regexps '("\\.org\\'"))
(org-visibility-enable-hooks))
(setup (:straight orglink)
(global-orglink-mode +1))
(setup (:straight package-lint))
(setup (:straight package-lint-flymake)
(add-hook 'emacs-mode-hook #'package-lint-flymake-setup)
;; Remove it from init.el files
(add-hook '+init-mode-hook #'flymake-mode-off))
(setup (:straight paredit)
(:bind "DEL" #'paredit-backward-delete
"C-<backspace>" #'paredit-backward-kill-word
"C-w" (lambda (arg) (interactive "P")
(+kill-word-backward-or-region arg
#'paredit-backward-kill-word))
"M-s" nil)
(dolist (hook '(emacs-lisp-mode-hook
eval-expression-minibuffer-setup-hook
ielm-mode-hook
lisp-interaction-mode-hook
lisp-mode-hook
scheme-mode-hook
geiser-mode-hook
geiser-repl-mode-hook))
(add-hook hook #'enable-paredit-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 pdf-tools)
(pdf-tools-install))
;; (setup (:straight-when pdf-tools
;; ;; Ensure we can build `pdf-tools'
;; (or (executable-find "gcc")
;; (executable-find "g++")))
;; (setf (alist-get "\\.pdf\\'" auto-mode-alist nil nil #'equal)
;; #'pdf-view-mode)
;; (pdf-tools-install t))
(setup (:straight (plancat
:host github
:repo "duckwork/plancat.el"
:local-repo "~/src/emacs-packages/plancat.el/"))
(:option plancat-user "acdw"))
(setup (:straight pocket-reader)
(:option pocket-reader-open-url-default-function #'browse-url)
(:+leader "p" #'pocket-reader
"C-p" #'pocket-reader)
(dolist (mode '((eww-mode-map . eww)
(w3m-mode-map . w3m)
(elfeed-search-mode-map . elfeed-search)
(elfeed-show-mode-map . elfeed-show)))
(with-eval-after-load (cdr mode)
(define-key (symbol-value (car mode)) "\"" #'pocket-reader-add-link))
(with-eval-after-load '+link-hint
(+link-hint-pocket-add-setup)
(define-key +link-hint-map "M-\"" #'+link-hint-pocket-add)
(define-key +link-hint-map "\"" #'+link-hint-pocket-add))))
(setup (:straight (shell-command+
:host nil
:repo "https://git.sr.ht/~pkal/shell-command-plus"))
(:option shell-command-prompt "$ ")
(:bind-into dired
"M-!" 'shell-command+)
(:+key "M-!" #'shell-command+))
(setup (:straight sicp))
(setup (:straight (simple-modeline
:host github :repo "gexplorer/simple-modeline"
:fork (:host github :repo "duckwork/simple-modeline")))
(:require +modeline)
(:option +modeline-modified-icon-alist '((ephemeral . "~")
(special . "*")
(readonly . "=")
(modified . "+")
(t . "-"))
;; '((ephemeral . "🥞")
;; (special . "🥐")
;; (readonly . "🦞")
;; (modified . "🥪")
;; (t . "🍞"))
+modeline-minions-icon ";"
simple-modeline-segments
`(( ; left
+modeline-ace-window-display
+modeline-modified
+modeline-buffer-name
(lambda () (+modeline-vc " : "))
,(+modeline-concat
'(+modeline-minions
+modeline-major-mode))
+modeline-anzu
)
( ; right
(lambda ()
(unless +tab-bar-misc-info-mode
(+modeline-concat
'(+modeline-track
simple-modeline-segment-misc-info))))
simple-modeline-segment-process
+modeline-text-scale
,(+modeline-concat
'(+modeline-god-mode
+modeline-kmacro-indicator
+modeline-reading-mode
+modeline-narrowed)
",")
+modeline-input-method
,(+modeline-concat
'(+modeline-region
+modeline-line-column
+modeline-file-percentage))
)))
(simple-modeline-mode +1))
(setup (:straight slack)
(:also-load +slack)
(:option slack-prefer-current-team t
slack-buffer-emojify t
slack-buffer-create-on-notify t
slack-enable-wysiwyg t
slack-file-dir (xdg-user-dir "DOWNLOAD")
slack-display-team-name nil)
(with-eval-after-load '+slack
(+slack-register-teams))
(with-eval-after-load 'alert
;; Don't notify for Slack messages
(alert-add-rule :category "slack"
:style 'ignore)))
(setup (:straight-when sly
(defvar +lisp-bin (executable-find "sbcl")))
(:also-load sly-autoloads
+sly)
(:option inferior-lisp-program +lisp-bin
sly-kill-without-query-p t)
(:with-feature sly-mrepl
(dolist (key '("RET" "<return>"))
(:bind key #'sly-mrepl-return-at-end))
(:bind "C-c C-c" #'sly-mrepl-return)))
(setup (:straight smartscan)
(:with-map smartscan-map
(:bind "M-'" nil))
(:hook-into prog-mode))
(setup (:straight (sophomore
:host github
:repo "duckwork/sophomore.el"))
(sophomore-enable #'narrow-to-region)
(sophomore-disable ; These are mostly annoying commands
#'view-hello-file
#'describe-gnu-project)
(sophomore-mode +1))
(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))))
(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 systemd
(executable-find "systemd"))
(:option systemd-man-function 'woman))
(setup (:straight (titlecase
:host github
:repo "duckwork/titlecase.el"
:files ("*")))
(:require titlecase)
(:with-map +casing-map
(:bind "t" #'titlecase-dwim
"M-t" #'titlecase-dwim)))
(setup (:straight topsy)
(:hook-into ;;prog-mode
circe-chat-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 transpose-frame)
(defvar +transpose-frame-map
(let ((map (make-sparse-keymap)))
(dolist (bind '(("t" . transpose-frame)
("v" . flip-frame)
("h" . flop-frame)
("r" . rotate-frame-clockwise)
("R" . rotate-frame-anticlockwise)))
(define-key map (car bind) (cdr bind)))
map)
"Map for transposing frames.")
(define-key +key-mode-map (kbd "C-x 5 t") +transpose-frame-map))
(setup (:straight trashed)
(:option trashed-action-confirmer #'y-or-n-p))
(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 (.etc "undo/" t)
undo-fu-session-compression (executable-find "gzip"))
(global-undo-fu-session-mode +1))
(setup (:straight unfill))
(setup (:straight valign)
(:hook-into org-mode))
(setup (:straight valign) (:quit "Doesn't work with narrowed tables.")
(:option valign-fancy-bar t)
(:hook-into org-mode
markdown-mode))
(setup (:straight (vertico
:host github
:repo "minad/vertico"
:files ("*" "extensions/*"
(:exclude ".git"))))
(:require vertico +vertico)
(:option resize-mini-windows 'grow-only
vertico-count-format nil
vertico-cycle t)
(advice-add #'vertico-next :around #'+vertico-ding-wrap)
(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-directory
vertico-mouse
vertico-unobtrusive
vertico-multiform
vertico-quick)
(vertico-mouse-mode +1)
(vertico-multiform-mode +1)
;; I `setf' these so they don't override the other setfs elsewhere in init.el.
(setf (alist-get 'execute-extended-command vertico-multiform-commands) '(flat))
(setf (alist-get 'completion-at-point vertico-multiform-commands) '(flat))
(setf (alist-get 'indent-for-tab-command vertico-multiform-commands) '(flat))
(setf (alist-get 'insert-char vertico-multiform-commands) nil)
(setf (alist-get 'file vertico-multiform-categories) '(buffer))
(setf (alist-get 'bookmark vertico-multiform-categories) nil)
;; Default. Needs to be `add-to-list' so that it appears at the end.
(add-to-list 'vertico-multiform-categories '(t flat) :append)
(:with-map vertico-map
(:bind "RET" #'vertico-directory-enter
"DEL" #'vertico-directory-delete-char
"M-DEL" #'vertico-directory-delete-word
"TAB" #'+vertico-widen-or-complete
"M-j" #'vertico-quick-insert))
(add-hook 'rfn-eshadow-update-overlay-hook #'vertico-directory-tidy))
(setup (:straight visual-fill-column)
(:option visual-fill-column-center-text t
(append reading-modes) '(visual-fill-column-mode . +1))
(:hook #'visual-line-mode)
(:hook-into org-mode)
(advice-add #'text-scale-adjust :after #'visual-fill-column-adjust)
(:global [f12] #'visual-fill-column-mode))
(setup (:straight vlf)
(:require vlf-setup))
;; (setup (:straight-when vterm
;; (and module-file-suffix
;; (executable-find "cmake")))
;; (:also-load +vterm)
;; (:option vterm-always-compile-module t
;; vterm-buffer-name-string "vterm: %s"
;; vterm-max-scrollback 100000 ; max allowed by vterm-module.h
;; )
;; (advice-add 'counsel-yank-pop-action :around
;; #'+vterm-counsel-yank-pop-action))
(setup (:straight-when w3m
(executable-find "w3m"))
;; (+with-ensure-after-init
;; (:option browse-url-browser-function #'w3m-browse-url
;; +browse-url-browser-function browse-url-browser-function))
)
(setup (:straight web-mode)
(setf (alist-get (rx "." (or "htm" "html" "phtml" "tpl.php"
"asp" "gsp" "jsp" "ascx" "aspx"
"erb" "mustache" "djhtml")
eos)
auto-mode-alist)
'web-mode)
(with-eval-after-load 'apheleia
(setf (alist-get 'web-mode apheleia-mode-alist)
'prettier)))
(setup (:straight whitespace-cleanup-mode)
(:option whitespace-cleanup-mode-preserve-point t
whitespace-cleanup-mode-only-if-initially-clean nil)
(global-whitespace-cleanup-mode +1))
(setup (:straight wrap-region)
(:require wrap-region)
(wrap-region-add-wrappers
'(("*" "*" nil org-mode)
("~" "~" nil org-mode)
("/" "/" nil org-mode)
("=" "=" nil org-mode)
("+" "+" nil org-mode)
("_" "_" nil org-mode)
("$" "$" nil (org-mode latex-mode))))
(:hook-into org-mode
latex-mode))
(setup (:straight xkcd)
(:also-load +xkcd)
(:hook #'visual-fill-column-mode))
(setup (:straight yaoddmuse))
(setup (:straight yasnippet)
(:option yas-snippet-dirs (list
(expand-file-name "snippets" user-emacs-directory)
(sync/ "emacs/snippets" t)))
(yas-global-mode +1))
(setup (:straight (ytdious
:host github :repo "spiderbit/ytdious"
:fork (:host github :repo "duckwork/ytdious")))
(:also-load +ytdious)
(:option ytdious-invidious-api-url (if +invidious-host
(concat "https://" +invidious-host)
"https://invidious.snopyta.org"))
(:bind "y" #'+ytdious-watch))
(setup (:straight zoom-frm))
(setup (:straight zzz-to-char)
(:require +zzz-to-char)
(:option zzz-to-char-reach (+bytes 1 :kib))
(:global "M-z" #'+zzz-to-char))