emacs/init.el

2617 lines
106 KiB
EmacsLisp
Raw Permalink Blame History

This file contains invisible Unicode characters

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

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

;;; init.el --- 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.
;;; Commentary
;; My init.el. There are many like it, but this one is mine.
;; Ideas:
;; [[https://emacs.stackexchange.com/questions/17278/truncate-only-certain-lines-and-use-continuation-lines-elsewhere][Truncate org-mode headings]]
;; [[https://emacs.stackexchange.com/questions/7432/make-visual-line-mode-more-compatible-with-org-mode][another link that might be useful for truncating]]
;;; 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)
(:global "M-u" #'universal-argument)
(+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 ""
ring-bell-function 'ignore
read-file-name-completion-ignore-case t)
;; 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
"C-x C-o" #'+switch-to-last-buffer
"C-x o" #'+switch-to-last-buffer
"C-x C-l" #'+open-paragraph ; original: downcase-region
"C-w" #'+kill-word-backward-or-region
"C-x C-m" #'execute-extended-command ; original: coding systems
"C-<backspace>" #'+backward-kill-word
"C-x TAB" #'+indent-rigidly
"<f7>" #'flyspell-mode
"C-x C-c" #'+save-buffers-quit
"C-\\" nil ; original: toggle-input-method
"C-/" #'undo-only
"C-?" #'undo-redo)
;; Disable bindings
(:global "M-j" nil
"<Scroll_Lock>" nil)
(:+leader "C-t d" #'toggle-debug-on-error
"C-t q" #'toggle-debug-on-quit)
;; 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-?)
;; Faces
(dolist (face '(line-number
line-number-major-tick
line-number-minor-tick
line-number-current-line))
(:face face '((t (:inherit fixed-pitch)))))
;; Hooks
(add-hook 'prog-mode-hook #'turn-on-auto-fill)
(add-hook 'prog-mode-hook #'font-lock-todo-insinuate)
(add-hook 'text-mode-hook #'turn-on-auto-fill) ; XXX: do I want this ??
(add-hook 'special-mode-hook #'turn-off-auto-fill)
;; Advice
(advice-add #'completing-read-multiple :filter-args #'+crm-indicator)
;; 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)
(+with-ensure-after-init
(:hook #'+init-add-setup-to-imenu)))
(setup (:require +window))
(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)
;; (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))
;; Load customizations now, and after init (to capture other possible
;; variables I want to load) XXX: this is dumb
(+with-ensure-after-init
(+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 find-script))
(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-global-mark
Info-history-back Info-history-forward
))
(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)
(add-hook 'user-save-hook (defun user-save@save-some-buffers ()
(save-some-buffers t t)))
(user-save-global-mode +1))
(setup (:require winner)
(winner-mode +1))
(setup +key
(+ensure-after-init #'+key-global-mode))
(setup _work
(with-eval-after-load 'bbdb
(require '_work)))
(setup abbrev
(:option abbrev-file-name (sync/ "abbrev.el")
save-abbrevs 'silent)
(with-eval-after-load 'user-save
(:with-mode edit-abbrevs-mode
(:hook #'user-save-mode-disable)))
(: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 awk-mode
(:apheleia gawk '("gawk" "-f-" "-o-")))
(setup bookmark
(:option bookmark-save-flag 1
bookmark-watch-bookmark-file 'silent
bookmark-set-fringe-mark nil))
(setup browse-url
(:require +browse-url)
(:option
browse-url-browser-function 'browse-url-default-browser
+browse-url-browser-function #'eww-browse-url
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.
;; ... or enable trying multiple servers
;; "yewtu.be"
"youtube.com"
"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.
(:option browse-url-handlers
(list
(cons (rx bos (or "gemini:" "gopher:")) #'elpher-browse-url-elpher)
(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 (+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)))))
(with-eval-after-load 'chd
(add-to-list 'browse-url-handlers
(cons chd/url-regexps #'browse-url-chrome)))
;; 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")
;;TODO: Various paste sites
))
(+browse-url-transform-url-global-mode +1))
(setup c-mode
(:with-hook c-mode-common-hook
(:hook #'indent-tabs-mode)))
(setup calendar
(require '_location)
(:option diary-file (private/ "diary")))
(setup compile
(:require +compile)
(:+key "<f5>" #'+compile-dispatch)
(:option compilation-always-kill t
compilation-ask-about-save nil
compilation-scroll-output t))
(setup dired
(:require dired-x +dired)
(: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
"j" #'+dired-goto-file
"C-j" #'dired-up-directory)
(:hook #'dired-hide-details-mode
#'hl-line-mode
#'lin-mode
#'+dired-dim-git-ignores)
(+with-ensure-after-init ; Necessary because jabber loads later
(:+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 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-mode emacs-lisp-mode
(:hook #'checkdoc-minor-mode))
(:bind-into (emacs-lisp-mode-map lisp-interaction-mode-map)
"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
esh-module)
(+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)
(add-to-list 'eshell-modules-list 'eshell-tramp)
(with-eval-after-load 'mwim
(setf (alist-get 'eshell-mode mwim-beginning-of-line-function)
#'eshell-bol))
(:hook #'eshell-smart-initialize)
(+eshell-eval-after-load
;; Local modes
(dolist (mode '((hungry-delete-mode . -1)))
(funcall (car mode) (cdr mode)))
;; Set local settings
(dolist (setting `((outline-regexp . ,eshell-prompt-regexp)
(page-delimiter . ,eshell-prompt-regexp)
(imenu-generic-expression "Prompt"
,(concat eshell-prompt-regexp
"\\(.*\\)")
1)
(truncate-lines . t)
(scroll-margin . 0)))
(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)
eww-use-browse-url (rx bos (or "mailto:"
"gemini:"
"gopher:")))
(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 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)))
("chat" (or (mode . erc-mode)
(mode . circe-server-mode)
(mode . circe-channel-mode)
(mode . jabber-chat-mode)
(mode . jabber-browse-mode)
(mode . jabber-roster-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)
(dolist (dir (split-string (getenv "INFOPATH") ":" t))
(add-to-list 'Info-additional-directory-list dir))
(:with-mode Info-mode ; -_-
(:hook #'reading-mode)
(:local-set +modeline-buffer-position #'+Info-modeline-breadcrumbs
+modeline-position-function #'ignore)
(:bind "c" #'+Info-copy-current-node-name
"w" #'+Info-copy-current-node-name)))
(setup ispell
(:also-load +ispell)
(:option ispell-program-name (or (executable-find "ispell")
(executable-find "aspell")))
(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 midnight
(midnight-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
(:needs "traceroute")
(:require +finger) ; fixes `finger' to use var below
(:option finger-X.500-host-regexps '(".") ; only send username
)
(with-eval-after-load 'transient
(transient-define-prefix net-utils ()
"Networking utilities"
["Actions"
("p" "Ping" ping)
("i" "Ifconfig" ifconfig)
("w" "Iwconfig" iwconfig)
("n" "Netstat" netstat)
("a" "Arp" arp)
("r" "Route" route)
("h" "Nslookup host" nslookup-host)
("d" "Dig" dig)
("s" "Smb Client" smbclient)
("t" "Traceroute" traceroute)])
(:+key "C-z M-n" #'net-utils)))
(setup notmuch
(:load-from "~/usr/share/emacs/site-lisp/")
(:load-after bbdb)
(: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"))
;; Reading mail
(:option notmuch-show-indent-content nil)
(add-hook 'notmuch-show-mode-hook #'visual-fill-column-mode)
;; 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+unread"
:query (+notmuch-query-concat
"tag:inbox"
"tag:unread"
"NOT tag:Spam")
:key "i")
(list :name "inbox"
:query (+notmuch-query-concat
"tag:inbox"
"NOT tag:Spam")
:key "I")
(list :name "lists+unread"
:query (+notmuch-query-concat
"tag:/List/"
"tag:unread")
:key "l")
(list :name "lists"
:query "tag:/List/"
: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)
;; For `focus'
(put 'notmuch-message 'bounds-of-thing-at-point 'notmuch-show-message-extent))
(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/*"))))
(:straight (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)
(with-eval-after-load '+org (+org-agenda-inhibit-hooks-mode +1))
(:option org-adapt-indentation nil
org-auto-align-tags t
org-archive-mark-done t
org-fold-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 nil
org-confirm-babel-evaluate nil
org-cycle-separator-lines 0
org-directory (sync/ "org/" t)
org-ellipsis (or 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-indent-indentation-per-level 0
org-indent-mode-turns-on-hiding-stars nil
org-insert-heading-respect-content t
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 -77 ;; (- (- fill-column 1 (length org-ellipsis)))
org-todo-keywords '((sequence "TODO(t)" "WAIT(w@/!)" "ONGOING(o@)"
"|" "DONE(d!)" "ASSIGNED(a!)")
(sequence "|" "CANCELED(k@)")
(sequence "MEETING(m)"))
org-use-speed-commands t
org-emphasis-alist '(("*" org-bold)
("/" org-italic)
("_" org-underline)
("=" org-verbatim)
("~" org-code)
("+" org-strikethrough)))
;; (setq org-todo-keywords
;; '((sequence
;; "TODO(t)"
;; "NEXT(n!)" ; next action
;; "DONE(d)" ; done)
;; (sequence
;; "WAIT(w@)" ; waiting to be actionable again
;; "HOLD(h@/!)" ; actinable, but will do later
;; "IDEA(i)" ; maybe someday
;; "KILL(k@/!)" ; cancelled, aborted or is no longer applicable
;; ))))
(:bind "RET" #'+org-return-dwim
"<S-return>" #'+org-table-copy-down
"M-RET" #'+org-meta-return
"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
"C-c C-x l" #'org-toggle-link-display
"C-c C-x m" (lambda () (interactive)
(setq-local org-hide-emphasis-markers
(not org-hide-emphasis-markers))
(font-lock-update))
"C-c C-x r" #'+org-drawer-list-add-resource
"C-M-k" #'kill-paragraph
"C-M-t" #'transpose-paragraphs)
(:global [f8] #'org-clock-in
[f9] #'org-clock-out
"C-c l" #'org-store-link)
(+with-ensure-after-init
(:hook #'variable-pitch-mode
#'visual-fill-column-mode
#'turn-off-auto-fill
#'org-indent-mode ;; Needed for proper hanging indents in lists
#'prettify-symbols-mode
#'+org-wrap-on-hyphens))
(:local-set prettify-symbols-alist '(("DEADLINE:" . ?→)
("SCHEDULED:" . ?↷)
("CLOSED:" . ?✓))
;; electric-pair-pairs
;; (append electric-pair-pairs
;; (mapcar (lambda (emph)
;; (let ((ch (string-to-char (car emph))))
;; (cons ch ch)))
;; org-emphasis-alist))
)
(: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)))
;; (add-to-list '+custom-variable-allowlist 'org-agenda-files)
(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))
(:face 'org-done '((t (:inherit (modus-themes-subtle-green))))
'org-tag '((t (:inherit (secondary-selection))))
'org-todo '((t (:inherit (modus-themes-subtle-red)))))
;; 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.
("^[ \t]*\\([-]\\) "
(0 (progn (compose-region (match-beginning 1) (match-end 1) "") 'fixed-pitch)
;; 'fixed-pitch t
))
("^[ \t]*\\([+]\\) "
(0 (progn (compose-region (match-beginning 1) (match-end 1) "") 'fixed-pitch)
;; 'fixed-pitch t
))
("^[ \t]+\\([*]\\) "
(0 ;; (progn (compose-region (match-beginning 1) (match-end 1) "→") 'fixed-pitch)
'fixed-pitch t))
;; Fancy numbered lists (well, monospaced)
("^[ \t]*\\(\\(?:[0-9]+\\|[A-Za-z]\\)[.)]\\) " 0 'fixed-pitch t)
;; Make leading org-heading stars fixed-pitch
("^\*+ " 0 'fixed-pitch t)
))
(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-block-separator ?─
org-agenda-time-grid
'((daily today require-timed)
(800 1000 1200 1400 1600 1800 2000)
" ┄┄┄┄┄ " "┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄")
org-agenda-current-time-string
"← now ─────────────────────────────────────────────────"
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)
(unless after-init-time
(:option org-agenda-files (list (sync/ "org/"))))
(dolist (var '(org-agenda-files
org-agenda-file-regexp
org-agenda-templates))
(add-to-list '+custom-variable-allowlist var))
(define-advice org-agenda-files (:filter-return (ret))
"Remove SyncThing's sync-conflict files from the org agenda."
(seq-remove (lambda (f) (string-match-p "sync-conflict" f)) ret))
(:+leader "a" #'org-agenda "C-a" #'org-agenda)
(:hook #'hl-line-mode)
(:local-set truncate-lines t)
(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}"
"\n Hostname %\\1"
"\n User %^{User|%(user-login-name)}"
"\n IdentityFile %(read-file-name \"IdentityFile: \" \"~/.ssh/\")"
"\n IdentitiesOnly yes"
"\n PubkeyAuthentication yes"
"\n Port %^{Port|22}")
))
(+org-capture-templates-setf "r"
`("Radio station" plain (file "~/.config/radio/stations")
,(concat "%^{URL} %^{Description} %^{Tags [space delimited]}")
:immediate-finish t))
(+org-capture-sort))
(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 ox ; org-export
(:also-load +ox
ox-md)
(:option org-export-coding-system 'utf-8-unix
org-export-headline-levels 8
org-export-with-drawers nil
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 'ox
(+org-export-pre-hooks-insinuate)))
(setup password-cache
(:option password-cache t
password-cache-expiry (* 60 60)))
(setup prettify-symbols-mode
(:option prettify-symbols-unprettify-at-point t))
(setup prog
(:local-set comment-auto-fill-only-comments t)
(:hook #'prettify-symbols-mode))
(setup scratch
(:require +scratch)
(:option initial-major-mode #'lisp-interaction-mode
initial-scratch-message ";;; What good will you work in the world today?\n\n")
(:+leader "." #'+scratch-switch-to-scratch
"C-." #'+scratch-switch-to-scratch
"," #'+scratch-switch-to-text
"C-," #'+scratch-switch-to-text)
(+with-ensure-after-init
(+scratch-text-scratch))
(add-hook 'kill-buffer-query-functions #'+scratch-immortal))
(setup sh
(:option sh-indentation tab-width)
(:hook #'indent-tabs-mode)
(:apheleia shfmt '("shfmt")))
(setup shell
(:option shell-command-prompt-show-cwd t)
(:local-set +modeline-position-function
(lambda () (string-replace (getenv "HOME")
"~"
default-directory)))
(:hook #'form-feed-mode))
(setup shr
(:also-load +shr)
(:option shr-width (- fill-column 5) ; pad out for wide letters
shr-use-fonts t)
(dolist (mode '(eww-mode
elfeed-show-mode))
(add-hook (intern (format "%s-hook" mode)) #'+shr-heading-setup-imenu)))
(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 t
+tab-bar-menu-bar-icon " ; "
tab-bar-close-button (propertize " × "
'display t
'close-tab nil)
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-timer
+tab-bar-date
+tab-bar-space))
(tab-bar-mode +1)
(display-time-mode +1))
(setup text-mode
(:bind "C-M-k" #'kill-paragraph))
(setup timer-list
(:bind "d" #'timer-list-cancel)
(: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 whitespace
(:option whitespace-line-column nil
whitespace-style '(face trailing tabs tab-mark))
;; I want trailing whitespace to be cleaned up, but I don't need to know about it.
(:face 'whitespace-trailing '((t :inherit nil)))
(:hook-into text-mode prog-mode))
(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
aw-minibuffer-flag t)
(:+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 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 &rest _)
(setq input (orderless-pattern-compiler input))
(cons input (lambda (str) (orderless--highlight input str)))))
(+with-eval-after-loads (affe)
(setq affe-regexp-compiler (defun affe-orderless-regexp-compiler (input &rest _)
(setq input (orderless-pattern-compiler input))
(cons input (lambda (str) (orderless--highlight input str)))))
(:+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 apheleia)
(:require apheleia +apheleia)
(apheleia-global-mode +1))
(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)
(setf (alist-get ?. avy-dispatch-alist) #'avy-action-embark)
(+avy-buffer-face-mode +1))
(setup (:straight bbdb)
(:straight bbdb-vcard)
(add-hook '+custom-after-load-hook
(defun +bbdb-load ()
(:require bbdb-autoloads
bbdb)
(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 browse-kill-ring)
(:+key "C-M-y" #'browse-kill-ring)
(:option browse-kill-ring-highlight-current-entry t
browse-kill-ring-highlight-inserted-item 'pulse
browse-kill-ring-separator " ")
(:hook #'form-feed-mode))
(setup (:straight (cape :host github :repo "minad/cape"))
(let
;; All available cape capfs listed here. Add them to the front since
;; they're reversed with `add-to-list'.
((append-fns '(cape-file
cape-dabbrev
cape-keyword))
(remove-fns '(cap-abbrev
cape-ispell
cape-dict)))
(dolist (fn append-fns)
(add-to-list 'completion-at-point-functions fn :append))
(dolist (fn remove-fns)
(setq completion-at-point-functions
(delete fn completion-at-point-functions)))
;; Fix position of t
(when (memq t completion-at-point-functions)
(setq completion-at-point-functions
(append (delq t completion-at-point-functions)
'(t))))))
(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
+modeline-position-function #'ignore)
(: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-max-buffer-size (+bytes 10 :kb)
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
#'enable-lui-autopaste
(defun turn-off-+nyan-mode () (+nyan-local-mode -1))
(defun turn-off-electric-pair-mode () (electric-pair-mode -1)))
(: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)))
(tracking-mode +1)
(:with-mode tracking-mode
(:option tracking-position 'before-modes)
(:bind "C-c C-SPC" (lambda () (interactive)
(if (and +tracking-hide-when-org-clocking
(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)
(+with-ensure-after-init
(: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
)
(:with-mode minibuffer-mode
(:local-set completion-in-region-function #'consult-completion-in-region))
(advice-add #'register-preview :override #'consult-register-window)
(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)
("<f2>" . consult-buffer)
("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 M-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)
(+with-eval-after-loads (consult +consult)
(:option consult-narrow-key "<"
consult-project-root-function '+consult-project-root)
(add-to-list 'consult-buffer-filter
(rx "*" (or "scratch" "text") "*"))
(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))))
(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)
(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 csv-mode))
(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 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))
("reddit\\.com" . markdown-mode)
("notabug\\.org" . markdown-mode)))
(+with-ensure-after-init
(edit-server-start)))
(setup (:straight editorconfig)
(:with-mode conf-mode
(:file-match (rx ".editorconfig" eos)))
(dolist (m '(emacs-lisp-mode
lisp-mode
scheme-mode))
(add-to-list 'editorconfig-exclude-modes m))
(editorconfig-mode +1))
(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 "@10-days-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 (elfeed-tube :host github :repo "karthink/elfeed-tube")
(or (executable-find "youtube-dl")
(executable-find "yt-dlp")))
(:straight (elfeed-tube-mpv :host github :repo "karthink/elfeed-tube"))
(:load-after elfeed)
(with-eval-after-load 'elfeed
(elfeed-tube-setup)
(:bind-into (elfeed-show-mode-map elfeed-search-mode-map)
"F" #'elfeed-tube-fetch
[remap save-buffer] #'elfeed-tube-save)
(:bind-into elfeed-show-mode-map
"C-c C-f" #'elfeed-tube-mpv-follow-mode
"C-c C-w" #'elfeed-tube-mpv-where)))
(setup (:straight elpher)
(:bind "l" #'elpher-back))
(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)))
(setup (:straight embark-consult)
(:load-after consult embark)
(add-hook 'embark-collect-mode-hook #'consult-preview-at-point-mode))
(setup (:straight embrace)
(dolist (mode '(LaTeX-mode org-mode ruby-mode))
(add-hook (intern (format "%s-hook" mode))
(intern (format "embrace-%s-hook" mode))))
(:face 'embrace-help-pair-face '((t ( :inverse-video nil
:inherit font-lock-keyword-face))))
(:+key "C-," #'embrace-commander))
(setup (:straight (ement :host github
:repo "alphapapa/ement.el")
;; `plz' is a requirement, but isn't on an elpa.
(setup (:straight (plz :host github
:repo "alphapapa/plz.el"))
t)))
(setup (:straight epithet)
(dolist (hook '(Info-selection-hook
;; eww-after-render-hook
help-mode-hook
occur-mode-hook))
(add-hook hook #'epithet-rename-buffer))
(if (boundp 'eww-auto-rename-buffer) ; Emacs 29
(:option eww-auto-rename-buffer 'title)
(add-hook 'eww-after-render-hook #'epithet-rename-buffer)))
(setup (:straight eros)
(:option eros-eval-result-prefix "; "
eros-overlays-use-font-lock nil)
(: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
:quit)
(:load-after eshell)
(defalias 'eshell/v 'eshell-exec-visual)
(eshell-vterm-mode +1))
(setup (:straight 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 (filldent :host nil
:repo "https://codeberg.org/acdw/filldent.el"))
(:+key "M-q" #'filldent-unfill-toggle))
(setup (:straight (flymake-collection :host github
:repo "mohkale/flymake-collection"))
(+ensure-after-init #'flymake-collection-hook-setup))
(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))
(setup (:straight focus)
(:require)
(add-hook 'modus-themes-after-load-theme-hook
(defun focus-update@after-modus-load ()
(modus-themes-with-colors
(:face 'focus-unfocused `((t ( :foreground ,fg-inactive
:background ,bg-inactive
:weight normal
:slant normal
:extend t)))))))
;; XXX: This doesn't work, because notmuch overlays shit on the buffer
(setf (alist-get 'notmuch-show-mode focus-mode-to-thing)
'notmuch-message)
(:hook-into notmuch-show-mode))
(setup (:straight (forge :host github :repo "magit/forge")
(eq system-type 'gnu/linux))
(:quit) ; XXX: Somehow missing compat-26
(add-to-list 'forge-alist
'("tildegit.org" "tildegit.org/api/v1" "tildegit.org"
forge-gitea-repository)))
(setup (:straight form-feed)
;; See also `page-break-lines', further down.
(:face 'form-feed-line '((t (:strike-through t))))
(global-form-feed-mode +1))
(setup (:straight (frowny :host nil
:repo "https://codeberg.org/acdw/frowny.el"))
(:option frowny-eyes (rx (any ":=") (opt "'") (? "-")))
(global-frowny-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"))
(dolist (pkg '( geiser-chicken geiser-guile
macrostep-geiser
scheme-complete))
(straight-use-package pkg))
(:require +chicken)
(:with-mode scheme-mode
(:file-match (rx ".scm" eos)))
(setf (alist-get "\\.scm\\'" auto-insert-alist nil nil #'equal)
'(insert "#!/bin/sh\n#| -*- scheme -*-\nexec csi -s $0 \"$@\"\n|#\n")))
(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)
;; Load faster on first invocation by pre-loading a slow function
;; (see https://github.com/Wilfred/helpful/issues/236)
(run-with-idle-timer 1 nil (lambda ()
(require 'info-look)
(info-lookup-setup-mode 'symbol 'emacs-lisp-mode))))
(setup (:straight (hippie-completing-read :host nil
:repo "https://codeberg.org/acdw/hippie-completing-read.el"))
(:+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 i3wm-config-mode
(executable-find "i3")))
(setup (:straight info+)
(:load-after info)
(:option Info-fontify-isolated-quote-flag nil
Info-breadcrumbs-in-mode-line-mode nil
Info-fontify-emphasis-flag nil
Info-fontify-quotations nil
Info-saved-history-file (.etc "info-history"))
(add-hook 'Info-mode-hook #'Info-variable-pitch-text-mode))
(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 :host nil
:repo "https://codeberg.org/emacs-jabber/emacs-jabber"
: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 ( :host nil
:repo "https://codeberg.org/acdw/emacs-jabber")))
(:also-load +jabber)
(:option +jabber-pre-prompt "~ ~ ~\n")
(:option jabber-account-list '(("acdw@hmm.st"))
jabber-groupchat-buffer-format "%n"
jabber-chat-buffer-format "%n"
jabber-muc-private-buffer-format "%n(%g)"
jabber-activity-show-p #'ignore
jabber-muc-decorate-presence-patterns
'(("\\( enters the room ([^)]+)\\| has left the chatroom\\)$")
("." . jabber-muc-presence-dim))
jabber-muc-colorize-foreign nil ; colorizing doesn't match my color theme
jabber-chat-foreign-prompt-format (concat +jabber-pre-prompt
"%n\n"
(make-string +jabber-ws-prefix
?\ ))
jabber-chat-local-prompt-format (concat +jabber-pre-prompt
"%n\n"
(make-string +jabber-ws-prefix
?\ ))
jabber-groupchat-prompt-format (concat +jabber-pre-prompt
"%n\n"
(make-string +jabber-ws-prefix
?\ ))
jabber-auto-reconnect t)
(add-hook 'modus-themes-after-load-theme-hook
(defun jabber-chat@after-modus-themes-load ()
(modus-themes-with-colors
(:face 'jabber-chat-prompt-foreign `((t (:foreground ,red)))
'jabber-chat-prompt-local `((t (:foreground ,blue)))
'jabber-chat-prompt-system `((t (:foreground ,green)))))
(setq jabber-muc-nick-value (pcase (frame--current-backround-mode (selected-frame))
('light 0.5)
('dark 1.0)))
(+mapc-some-buffers #'+jabber-colors-update
(lambda () (derived-mode-p 'jabber-chat-mode
'jabber-roster-mode
'jabber-activity-mode
'jabber-browse-mode)))))
(dolist (mode '(jabber-chat-mode
jabber-browse-mode
jabber-roster-mode
jabber-console-mode))
(let ((hook (intern (format "%s-hook" mode))))
(add-hook hook #'visual-fill-column-mode)))
(with-eval-after-load 'tracking
(add-to-list 'tracking-ignored-buffers "discuss@conference.soprani.ca"))
(:with-mode jabber-chat-mode
(:local-set +modeline-position-function (lambda ()
(cond
((string-match-p "hmm@" (buffer-name))
"🤔 ")))
file-percentage-mode nil
wrap-prefix (make-string +jabber-ws-prefix ?\ )
comment-start nil))
(:+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)
;;; Alerting hooks --- remove echo messages
(remove-hook 'jabber-alert-muc-hooks 'jabber-muc-echo)
(remove-hook 'jabber-alert-presence-hooks 'jabber-presence-echo))
(setup (:straight (keepassxc-shim :host nil
:repo "https://codeberg.org/acdw/keepassxc-shim.el"))
(keepassxc-shim-activate))
(setup (:straight keychain-environment
(executable-find "keychain"))
(keychain-refresh-environment))
(setup (:straight lacarte)
(:+key "<f10>" #'lacarte-execute-menu-command))
(setup (:straight (lin :host nil
:repo "https://git.sr.ht/~protesilaos/lin"))
(:require)
(lin-global-mode +1))
(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 nil
:repo "https://codeberg.org/acdw/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")))
(dolist (font emoji-fonts)
(when (member font ffl)
(set-fontset-font t 'symbol (font-spec :family font) nil :append))))
(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"
:build (:not compile))
(:straight (transient :host github :repo "magit/transient"
:build (:not compile))))
(autoload 'transient--with-suspended-override "transient"))
(setup (:straight marginalia)
(marginalia-mode +1))
(setup (:straight markdown-mode)
(:option markdown-hide-markup nil)
(:file-match (rx (or ".md" ".markdown" ".mdown") eos))
(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
:fork (:host nil :repo "https://codeberg.org/acdw/mastodon.el")))
(:option mastodon-instance-url "https://tiny.tilde.website"
mastodon-active-user "acdw"
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
#'variable-pitch-mode
#'hl-line-mode
#'lin-mode))
(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 nil
:repo "https://git.sr.ht/~protesilaos/modus-themes"))
(require 'modus-themes (.etc "straight/build/modus-themes/modus-themes"))
(:option modus-themes-mixed-fonts t
modus-themes-bold-constructs t
modus-themes-italic-constructs t
modus-themes-headings '((1 monochrome bold overline)
(2 monochrome bold)
(3 monochrome italic)
(t monochrome)))
(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))))
(define-advice modus-themes--current-theme (:around (fn &rest r))
"Fix a \"nil is not a Modus theme\" error."
(or (apply fn r)
'modus-operandi))
;; This needs to be after the themes are loaded, I think.
(add-hook 'modus-themes-after-load-theme-hook
(defun +modus-themes-mostly-monochrome ()
"Set up mdous-themes to be mostly monochrome."
;; Major mode in the mode-line
(modus-themes-with-colors
(custom-set-faces
`(font-lock-builtin-face
((,class :inherit modus-themes-bold
:foreground unspecified)))
`(font-lock-comment-face
((,class :inherit variable-pitch
:slant italic
:foreground ,fg-comment-yellow)))
`(font-lock-comment-delimiter-face
((,class :inherit fixed-pitch
:foreground ,fg-comment-yellow)))
`(font-lock-constant-face
((,class :inherit underline
:foreground unspecified)))
`(font-lock-doc-face
((,class :inherit modus-themes-slant
:foreground ,fg-docstring)))
`(font-lock-function-name-face
((,class :foreground unspecified
:slant italic)))
`(font-lock-keyword-face
((,class :inherit modus-themes-bold
:foreground unspecified)))
`(font-lock-negation-char-face
((,class :inherit modus-themes-bold
:foreground unspecified)))
`(font-lock-preprocessor-face
((,class :foreground unspecified)))
`(font-lock-regexp-grouping-backslash
((,class :foreground ,fg-escape-char-backslash)))
`(font-lock-regexp-grouping-construct
((,class :foreground ,fg-escape-char-construct)))
`(font-lock-string-face
((,class :foreground ,fg-special-warm)))
`(font-lock-type-face
((,class :inherit modus-themes-bold
:foreground unspecified)))
`(font-lock-variable-name-face
((,class :foreground unspecified)))
`(font-lock-warning-face
((,class :inherit modus-themes-bold
:foreground ,red-nuanced-fg)))
`(font-lock-todo-face
((,class :inherit font-lock-comment-face
:foreground ,fg-header
:background ,yellow-intense-bg)))
;; `(mode-line
;; ((,class :height 100)))
;; `(mode-line-inactive
;; ((,class :height 100)))
;; `(tab-bar
;; ((,class :height 100)))
))))
(require 'dawn)
(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 native-complete)
(with-eval-after-load 'shell
(native-complete-setup-bash))
(:with-hook shell-mode-hook
(:local-set completion-at-point-functions
(cons 'native-complete-at-point
completion-at-point-functions))))
(setup (:straight notmuch-bookmarks)
(:load-after notmuch)
(:when-loaded
(notmuch-bookmarks-mode +1)))
(setup (:straight notmuch-labeler
:quit "Buggy")
(:load-after notmuch))
(setup (:straight nov)
(:hook #'visual-fill-column-mode)
(:file-match (rx ".epub" eos)))
(setup (:straight (nyan-mode
:fork (:repo "duckwork/nyan-mode")))
(:require nyan-mode +nyan-mode)
(with-eval-after-load 'modus-themes
(add-hook 'modus-themes-after-load-theme-hook
(defun +nyan-modus-update-colors ()
(modus-themes-with-colors
(set-face-attribute '+nyan-mode-line nil
:background bg-special-warm))))
(+nyan-modus-update-colors))
(+nyan-mode +1))
(setup (:straight ol-notmuch))
(setup (:straight orderless)
(:require +orderless)
(:option completion-styles '(substring orderless basic)
completion-category-defaults nil
completion-category-overrides
'((file (styles basic 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-drawer-list
:host github
:repo "d12frosted/org-drawer-list"))
(:load-after org)
(:also-load +org-drawer-list))
(setup (:straight org-mime)
(:option org-mime-export-ascii 'utf-8)
(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-taskwise
:host nil
:repo "https://codeberg.org/acdw/org-taskwise.el.git"))
(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-wc)
(:load-after org simple-modeline)
(:also-load +org-wc)
(add-hook 'org-mode-hook #'+org-wc-mode))
(setup (:straight orglink)
(:option orglink-activate-in-modes '(text-mode prog-mode))
(global-orglink-mode +1)
(global-goto-address-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 page-break-lines)
(:option page-break-lines-char ?—)
(:hook-into jabber-chat-mode))
(setup (:straight paredit)
(:also-load +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
(or (executable-find "gcc")
(executable-find "g++")))
(:also-load +pdf-tools)
(:with-mode pdf-view-mode
(:local-set +modeline-position-function #'+pdf-view-position)
(:file-match (rx ".pdf" eos)))
(pdf-tools-install :no-query))
(setup (:straight persistent-scratch)
(:require)
(:option persistent-scratch-save-file (sync/ "emacs/scratch")
persistent-scratch-backup-directory (sync/ "emacs/scratch.d/" t)
persistent-scratch-backup-file-name-format "%Y-%m-%dT%H:%M_%s")
(persistent-scratch-autosave-mode +1)
(+mapc-some-buffers (lambda () (persistent-scratch-mode +1))
persistent-scratch-scratch-buffer-p-function))
(setup (:straight (plancat
:host nil
:repo "https://codeberg.org/acdw/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 rainbow-mode)
(:hook-into prog-mode))
(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 . "-"))
+modeline-minions-icon "&"
+modeline-buffer-name-max-length 0.35)
;; Segments
(:option simple-modeline-segments
`(( ; left
+modeline-ace-window-display
+modeline-modified
+modeline-buffer-name
+modeline-major-mode
(lambda () (+modeline-vc " : "))
+modeline-nyan-on-focused
+modeline-anzu
)
( ; right
simple-modeline-segment-process
(lambda ()
(unless +tab-bar-misc-info-mode
(+modeline-concat
'(+modeline-track
simple-modeline-segment-misc-info))))
,(+modeline-concat
'(+modeline-god-mode
+modeline-kmacro-indicator
+modeline-reading-mode
+modeline-narrowed
+modeline-text-scale
+modeline-input-method)
" ")
+modeline-position
+modeline-spacer
)))
(simple-modeline-mode +1))
(setup (:straight slack)
(:also-load +slack)
(:option slack-prefer-current-team t
slack-buffer-emojify t
slack-thread-also-send-to-room nil
slack-typing-visibility 'buffer
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 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 nil
:repo "https://codeberg.org/acdw/sophomore.el"))
(sophomore-enable #'narrow-to-region)
(sophomore-disable ; These are mostly annoying commands
#'view-hello-file
#'describe-gnu-project
#'suspend-frame)
(sophomore-disable-with 'confirm
#'save-buffers-kill-terminal)
(sophomore-disable-with 'confirm-y
#'+save-buffers-quit)
(sophomore-mode +1))
(setup (:straight (spongebob-case
:host nil
:repo "https://codeberg.org/acdw/spongebob-case.el")))
(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 30
super-save-exclude '(".gpg")
super-save-remote-files nil)
(auto-save-visited-mode -1)
(super-save-mode +1))
(setup (:straight systemd
(executable-find "systemd"))
(:option systemd-man-function 'woman))
(setup (:straight (titlecase
:host nil
:repo "https://codeberg.org/acdw/titlecase.el"
:files ("*")))
(:require titlecase +titlecase)
(add-to-list 'titlecase-skip-words-regexps (rx word-boundary
(+ (any upper digit))
word-boundary))
(:with-map +casing-map
(:bind "t" #'titlecase-dwim
"M-t" #'titlecase-dwim
"s" #'+titlecase-sentence-style-dwim
"M-s" #'+titlecase-sentence-style-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)
(:+leader "t" #'trashed)
(:option trashed-action-confirmer #'y-or-n-p
trashed-use-header-line t
trashed-size-format 'human-readable))
(setup (:straight (twtxt
:fork (:repo "duckwork/twtxt-el")))
(:require)
(:also-load _twtxt)
(:option twtxt-file _twtxt-file
twtxt-following _twtxt-following))
(setup (:straight undo-fu) (:quit "Trying native undo functionality")
(:option undo-fu-allow-undo-in-region t)
(: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 (cond
((executable-find "gzip") 'gz)
((executable-find "bzip2") 'bz2)
((executable-find "xz") 'xz)
(t nil)))
(global-undo-fu-session-mode +1))
(setup (:straight (undo-hl
:host github
:repo "casouri/undo-hl"))
(:require)
(:face 'undo-hl-delete '((t :strikethrough t))
'undo-hl-insert '((t :underline t)))
(:hook-into text-mode prog-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-quick)
(vertico-mouse-mode +1)
(: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 vterm
(and module-file-suffix
(executable-find "cmake"))
:quit)
(: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 (vundo
:host github
:repo "casouri/vundo")))
(setup (:straight web-mode)
(:file-match (rx "." (or "htm" "html" "phtml" "tpl.php"
"asp" "gsp" "jsp" "ascx" "aspx"
"erb" "mustache" "djhtml")
eos))
(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 xr))
(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)
(:+key "M-+" #'zoom-frm-in
"M-_" #'zoom-frm-out))
(setup (:straight zzz-to-char)
(:require +zzz-to-char)
(:option zzz-to-char-reach (+bytes 1 :kib))
(:global "M-z" #'+zzz-to-char))