;;; init.el -*- lexical-binding: t; coding: utf-8-unix -*- ;; Author: Case Duckworth <(rot13-string "npqj@npqj.arg")> ;; Created: Sometime during Covid-19, 2020 ;; Keywords: configuration ;; URL: https://tildegit.org/acdw/emacs ;; Bankruptcy: 7 ;; This file is NOT part of GNU Emacs. ;;; License: ;; Everyone is permitted to do whatever with this software, without ;; limitation. This software comes without any warranty whatsoever, ;; but with two pieces of advice: ;; - Be kind to yourself. ;; - Make good choices. ;;; Commentary: ;; Some of the names in these `setup' forms are arbitrary. ;;; Code: (setup (:require auth-source) (:option auth-sources (list (acdw/sync-dir "authinfo") (acdw/sync-dir "authinfo.gpg") "~/.authinfo" "~/.authinfo.gpg"))) (setup (:require goto-addr) (if (fboundp #'global-goto-address-mode) (global-goto-address-mode) (add-hook 'after-change-major-mode-hook #'goto-address-mode))) (setup (:require recentf) (:option recentf-save-file (acdw/dir "recentf.el") recentf-max-menu-items 100 recentf-max-saved-items nil recentf-auto-cleanup 'mode (append recentf-exclude) (acdw/dir)) (:advise dired-rename-file :after #'rjs/recentf-rename-notify) (recentf-mode +1)) (setup (:require savehist) (:option history-length t history-delete-duplicates t savehist-autosave-interval 60 savehist-file (acdw/dir "savehist.el")) (dolist (var '(extended-command-history global-mark-ring kill-ring regexp-search-ring search-ring mark-ring)) (:option (append savehist-additional-variables) var)) (savehist-mode +1)) (setup (:require server) (unless (server-running-p) (server-start))) (setup (:require tramp) ;; thanks Irreal! https://irreal.org/blog/?p=895 (add-to-list 'tramp-default-proxies-alist '(nil "\\`root\\'" "/ssh:%h:")) (add-to-list 'tramp-default-proxies-alist '((regexp-quote (system-name)) nil nil))) (setup Info (:hook #'variable-pitch-mode #'reading-mode)) (setup abbrev (:option abbrev-file-name "~/Sync/abbrev.el" save-abbrevs 'silent) (:hook-into text-mode circe-chat-mode)) (setup acdw (:also-load acdw-compat acdw-lisp acdw-reading) (:option user-full-name "Case Duckworth" user-mail-address (rot13-string "npqj@npqj.arg")) (when-let ((default-directory (expand-file-name-exists-p "pkg/" user-emacs-directory))) (normal-top-level-add-subdirs-to-load-path))) (setup auto-fill (:hook (defun auto-fill@truncate-lines () (setq-local truncate-lines t)))) (setup autoinsert (require 'acdw-autoinsert) (acdw/define-auto-insert '(:replace t) ;; This is my custom auto-insert for elisp files. '("\\.el\\'" . "Emacs Lisp header (acdw)") '("Short description: " ";;; " (file-name-nondirectory (buffer-file-name)) " --- " str (make-string (max 2 ( - fill-column (current-column) 27)) 32) "-*- lexical-binding: t; -*-" '(setq lexical-binding t) "\n\n;; Copyright (C) " (format-time-string "%Y") " " (getenv "ORGANIZATION") | (progn user-full-name) "\n\n;; Author: " (user-full-name) '(if (search-backward "&" (line-beginning-position) t) (replace-match (capitalize (user-login-name)) t t)) '(end-of-line 1) " <" (progn user-mail-address) ">" & -2 "\n\n;;; License:" "\n\n;; Everyone is permitted to do whatever with this software, without" "\n;; limitation. This software comes without any warranty whatsoever," "\n;; but with two pieces of advice:" "\n\n;; - Be kind to yourself." "\n\n;; - Make good choices." "\n\n;;; Commentary:" "\n\n;; " _ "\n\n;;; Code:" "\n\n\n\n(provide '" (file-name-base (buffer-file-name)) ")" "\n;;; " (file-name-nondirectory (buffer-file-name)) " ends here\n")) (auto-insert-mode +1)) (setup autorevert (:option global-auto-revert-non-file-buffers t auto-revert-verbose nil) (global-auto-revert-mode +1)) (setup browse-url (:require acdw-browse-url) (:option browse-url-secondary-browser-function (if (executable-find "firefox") ; prefer Firefox #'browse-url-firefox #'browse-url-default-browser) browse-url-new-window-flag nil ; for eww browse-url-firefox-arguments '("--new-tab") ; for firefox browse-url-firefox-new-window-is-tab t) (acdw/browse-url-set-handlers (list (cons (rx (seq "." (or "jpeg" "jpg" ; images "png") eos)) (lambda (&rest args) (apply (cond ((executable-find "feh") #'browse-url-feh) ((executable-find "mpv") (defun browse-image-url-mpv (url &rest _args) "View an image URL in mpv." (let ((url (browse-url-encode-url url)) (process-environment (browse-url-process-environment))) (message "Viewing %s in mpv..." url) (apply #'start-process (concat "mpv " url) nil "mpv" (append browse-url-mpv-arguments (list "--image-display-duration=inf" url)))))) (t #'eww-browse-url)) args))) (cons (rx (or "youtube.com" ; videos "youtu.be" (seq "." (or "mp4" "gif" "mov" "MOV") eos))) (lambda (&rest args) (apply (if (executable-find "mpv") #'browse-url-mpv browse-url-secondary-browser-function) args))) (cons (rx (or "google.com" ; websites that don't work with eww "reddit.com" "twitter.com" "imgur.com" "pixelfed" "taskiq")) browse-url-secondary-browser-function) (cons "." ; everything else #'eww-browse-url))) ;; Buttonize gemini:// links. (acdw/add-button-url-regexp-protocol "gemini")) (setup buffers (:global "C-x k" #'acdw/kill-a-buffer) ;; Set the right major mode based on buffer name, if not visiting a file. ;; http://ruzkuku.com/emacs.d.html#orgeab93c3 (setq-default major-mode (lambda () (unless buffer-file-name (let ((buffer-file-name (buffer-name))) (set-auto-mode)))))) (setup calendar (:option calendar-week-start-day 1)) (setup completion (:option completion-ignore-case t read-buffer-completion-ignore-case t completion-styles '(substring partial-completion) completion-category-defaults nil completion-category-overrides '((file (styles . (partial-completion))))) (:global "M-/" #'hippie-expand)) (setup css-mode (:bind "C-c C-h" #'css-lookup-symbol)) (setup cursor (:option cursor-type 'bar cursor-in-non-selected-windows 'hollow blink-cursor-blinks 1) (blink-cursor-mode +1)) (setup cus-edit (:also-load acdw-cus-edit) (:option custom-file (acdw/dir "custom.el") custom-magic-show nil custom-magic-show-button t custom-raised-buttons nil custom-unlispify-tag-names nil custom-variable-default-form 'lisp) ;; I need this to save `safe-local-variables' between Emacs invocations. For ;; now, of course .... I would /love/ a better solution. (when (file-exists-p custom-file) ;; Don't load faces, since those are all set in init.el (cl-letf (((symbol-function 'custom-set-faces) #'ignore)) (load custom-file nil nil))) ;; `Custom-mode-hook' fires /before/ the widgets are built, so I have to ;; install advice after the widgets are made. (:advise custom-buffer-create-internal :after #'acdw-cus/expand-widgets) (:with-mode Custom-mode (:local-set imenu-generic-expression acdw-cus/imenu-generic-expression))) (setup debugger (:hook visual-line-mode)) (setup dired (:also-load dired-x) (:straight dired-subtree dired-collapse dired-git-info) (:option dired-recursive-copies 'top dired-recursive-deletes 'top dired-create-destination-dirs 'ask delete-by-moving-to-trash t dired-listing-switches "-Al" ls-lisp-dirs-first t dired-ls-F-marks-symlinks t dired-no-confirm '(byte-compile chgrp chmod chown copy hardlink load move shell touch symlink) dired-dwim-target t) (:bind "TAB" #'dired-subtree-cycle "i" #'dired-subtree-toggle ")" #'dired-git-info-mode) (:hook dired-collapse-mode dired-hide-details-mode hl-line-mode) (:global "C-x C-j" #'dired-jump) (with-eval-after-load 'dired (acdw/system (:work (:straight w32-browser) (autoload #'dired-w32-browser "w32-browser" nil t) (:bind "RET" #'dired-w32-browser)) (:home (:straight dired-open) (autoload #'dired-find-alternate-file "dired-open" nil t) (:bind "RET" #'dired-find-alternate-file))))) (setup disabled ;; While this stuff is defined in novice.el, I'm using 'disabled' as the name ;; for easy finding. ;; Enable all disabled commands. ;; This is an option, but I'm going to try /enabling/ just the ones that I ;; use instead. ;; (mapatoms (lambda (symbol) ;; (when (get symbol 'disabled) ;; (put symbol 'disabled nil)))) ;; Enable /some/ disabled commands (dolist (enable-sym '(narrow-to-region dired-find-alternate-file narrow-to-page)) (put enable-sym 'disabled nil)) ;; Now, disable symbols as I wish. (dolist (disable-sym '(view-hello-file suspend-frame scroll-left scroll-right comment-set-column set-fill-column)) (put disable-sym 'disabled t)) ;; And set the disabled function to something better than the default. ;; Now, I can run any disabled command, but I have to use M-x to do it. (:option disabled-command-function #'acdw/disabled-command-function)) (setup ediff (:option ediff-diff-options "-w" ; ignore whitespace ediff-window-setup-function #'ediff-setup-windows-plain ediff-split-window-function #'split-window-horizontally) ;; https://oremacs.com/2015/01/17/setting-up-ediff/ (add-hook 'ediff-after-quit-hook-internal #'winner-undo)) (setup eldoc (:option eldoc-idle-delay 0.1 eldoc-echo-area-use-multiline-p nil)) (setup elec-pair (electric-pair-mode +1)) (setup elisp-mode (:with-mode emacs-lisp-mode ;; -_- (:option eval-expression-print-length nil eval-expression-print-level nil print-length nil print-level nil lisp-indent-function #'lisp-indent-function) (:local-set (append imenu-generic-expression) `("Setup" ,(rx (seq (group bol (* space) "(setup" (+ space)) (? (group "(:" (+ graph) (* space) (? "("))) (group (+ (any word ?+ ?-))))) 3)) (:hook #'checkdoc-minor-mode #'turn-on-eldoc-mode) ;; Emulate slime's eval binds (:bind "C-c C-c" #'eval-defun "C-c C-k" #'acdw/eval-region-or-buffer "C-c C-z" #'ielm) ;; Add advice to pulse evaluated regions (:advise eval-region :around (defun eval-region@pulse (fn beg end &rest args) (let ((pulse-flag t)) (pulse-momentary-highlight-region beg end)) (apply fn beg end args)))) (:with-mode lisp-interaction-mode ;; -___- (:bind "C-c C-c" #'eval-defun "C-c C-k" #'acdw/eval-region-or-buffer "C-c C-z" #'ielm))) (setup emacs ;; "Et cetera" settings ;; This should stay as /minimal/ as possible. Anything that can go somewhere ;; else /should/ go there. (:option async-shell-command-display-buffer nil async-shell-command-buffer #'new-buffer attempt-orderly-shutdown-on-fatal-signal nil auto-hscroll-mode 'current-line attempt-stack-overflow-recovery nil echo-keystrokes 0.01 find-function-C-source-directory (acdw/find-emacs-source) image-use-external-converter (and (not (version< emacs-version "27")) (or (executable-find "magick") (executable-find "convert"))) kill-read-only-ok t kill-ring-max 500 ; RAM is cheap, right? mark-ring-max 50 kmacro-ring-max 20 search-ring-max 200 global-mark-ring-max 100 regexp-search-ring-max 100 load-prefer-newer t native-comp-async-report-warnings-errors nil password-cache t password-cache-expiry (* 60 5) ; seconds set-mark-command-repeat-pop t hscroll-step 1 scroll-step 1) (when (fboundp 'command-completion-default-include-p) (setq read-extended-command-predicate #'command-completion-default-include-p)) (defvar case-map (make-sparse-keymap) "A keymap for setting case in various ways.") (global-set-key (kbd "C-c c") case-map) (defvar lookup-map (make-sparse-keymap) "A keymap for looking up things.") (global-set-key (kbd "C-c l") lookup-map) (:global "M-=" #'count-words "C-M-;" #'comment-or-uncomment-sexp "C-w" #'kill-region-or-backward-word "C-c d" #'acdw/insert-iso-date "M-`" nil "C-x o" #'acdw/other-window-or-switch-buffer "C-x O" #'acdw/other-window-or-switch-buffer-backward "C-c _" #'add-file-local-variable "C-x C-c" #'acdw/fat-finger-exit) (global-set-key (kbd "M-n") (kbd "C-u 1 C-v")) (global-set-key (kbd "M-p") (kbd "C-u 1 M-v")) ;; inspo: https://github.com/zaeph/.emacs.d/blob/master/init.el#L479 (defvar toggle-map (make-sparse-keymap) "A keymap for toggling!") (global-set-key (kbd "C-c t") toggle-map) (:with-map toggle-map (:bind "c" #'column-number-mode "l" #'display-line-numbers-mode "d" #'toggle-debug-on-error "s" #'so-long-mode "S" #'scroll-bar-mode)) ;; Toggle (:with-map toggle-map (:bind "b" (defun acdw/toggle-lexical-binding () "Toggle `lexical-binding' in the current buffer." (interactive) (setq lexical-binding (not lexical-binding)) (message "Lexical-binding is %sabled." (if lexical-binding "en" "dis")) (force-mode-line-update)))) (:with-map case-map (require 'titlecase) (require 'acdw) (:bind "c" #'capitalize-dwim "t" #'titlecase-dwim "u" #'upcase-dwim "l" #'downcase-dwim)) (column-number-mode +1)) (setup encoding (:option locale-coding-system 'utf-8-unix coding-system-for-read 'utf-8-unix coding-system-for-write 'utf-8-unix buffer-file-coding-system 'utf-8-unix default-process-coding-system '(utf-8-unix . utf-8-unix) x-select-request-type '(UTF8_STRING COMPOUND_TEXT TEXT STRING)) (set-charset-priority 'unicode) (set-language-environment "UTF-8") (prefer-coding-system 'utf-8-unix) (set-default-coding-systems 'utf-8-unix) (set-terminal-coding-system 'utf-8-unix) (set-keyboard-coding-system 'utf-8-unix) (acdw/system (:work (set-clipboard-coding-system 'utf-16-le) (set-selection-coding-system 'utf-16-le)) (_ (set-selection-coding-system 'utf-8) (set-clipboard-coding-system 'utf-8)))) (setup eshell (:also-load acdw-eshell em-smart em-tramp) (:option eshell-aliases-file (acdw/dir "eshell/aliases" t) eshell-destroy-buffer-when-process-dies t eshell-directory-name (acdw/dir "eshell/" t) eshell-error-if-no-glob t eshell-hist-ignore-dups t eshell-kill-on-exit nil eshell-prefer-lisp-functions t ; I want to try using eshell eshell-prefer-lisp-variables t ; as much as possible. eshell-review-quick-commands nil eshell-save-history-on-exit t eshell-scroll-to-bottom-on-input 'all eshell-smart-space-goes-to-end t eshell-where-to-jump 'begin) (:local-set outline-regexp eshell-prompt-regexp page-delimiter eshell-prompt-regexp) (:hook #'eshell-arg-hist-mode (defun eshell-mode@setup () (require 'eshellrc (locate-user-emacs-file "eshell") :noerror) (:bind "C-d" #'eshell-quit-or-delete-char)))) (setup eww (:also-load acdw-eww) (defvar-local eww-readable-p nil "Whether current buffer is in readable-mode.") (:option eww-search-prefix "https://duckduckgo.com/html?q=" url-privacy-level '(email agent cookies lastloc)) (defun eww@is-readable (&rest _) (setq-local eww-readable-p t)) (defun eww@is-not-readable (&rest _) (setq-local eww-readable-p nil)) (advice-add 'eww-readable :after #'eww@is-readable) (advice-add 'eww-render :after #'eww@is-not-readable) (advice-add 'eww-back-url :after #'eww@is-not-readable) (:hook #'reading-mode (defun bookmark-eww--setup () "Setup eww bookmark integration." (setq-local bookmark-make-record-function #'bookmark-eww--make))) (:bind "RET" (defun eww/browse-url (arg) (interactive "P") (if-let ((url (thing-at-point 'url))) (browse-url url) (call-interactively #'acdw/link-hint-open-link))) "b" #'bookmark-set "B" #'bookmark-jump "M-n" nil "M-p" nil)) (setup executable (:option executable-prefix-env t) (add-hook 'after-save-hook #'executable-make-buffer-file-executable-if-script-p)) (setup files (:option auto-save-file-name-transforms `((".*" ,(acdw/dir "auto-save/" t) t)) auto-save-list-file-prefix (acdw/dir "auto-save-list/.saves-" t) auto-save-interval 60 auto-save-timeout 60 auto-save-visited-interval auto-save-timeout backup-by-copying t backup-directory-alist `((".*" . ,(acdw/dir "backup/" t))) delete-old-versions t mode-require-final-newline 'visit-save tramp-backup-directory-alist backup-directory-alist vc-make-backup-files t version-control t) (auto-save-visited-mode +1)) (setup find-func (:global "C-c l f" #'find-function "C-c l l" #'find-library "C-c l v" #'find-variable)) (setup flymake ;; TODO: look at flycheck for ideas around `flycheck-disabled-checkers' and ;; `flycheck-emacs-lisp-load-path'... there must be a way to get flymake to ;; recognize new values in the load path. (defvar-local flymake-inhibit nil "Buffer-local variable to inhibit `flymake'.") (add-to-list 'safe-local-variable-values '(flymake-inhibit . t)) (add-to-list 'safe-local-variable-values '(flymake-inhibit . nil)) (defvar flymake-inhibit-major-modes nil "Which major-modes NOT to enable `flymake' in.") (defvar flymake-inhibit-file-name-regexps '("init\\.el\\'" "early-init\\.el\\'") "List of file regexps NOT to enable `flymake' in.") (defvar flymake-inhibit-buffer-name-regexps (list (rx "*scratch*")) "List of buffer-name regexps NOT to enable `flymake' in.") (defun list-string-match-p (string regexp-list) "Return t if at least one regex in RETGEXP-LIST matches STRING, else nil." ;; FINE alphapapa ;P (seq-some (lambda (regexp) (string-match regexp (or string ""))) regexp-list)) (defun flymake-unless () "Turn on `flymake-mode', UNLESS it's inhibited. There are three methods to inhibit flymake in a file. From most specific to most general, they are these: - `flymake-inhibit': a file-local-variable - `flymake-inhibit-buffer-name-regexps': a list of regexps to match the buffer name against. If one of them matches, inhibit `flymake-mode'. - `flymake-inhibit-file-name-regexps': a list of regexps to match the filename against. If one of them matches, inhibit `flymake-mode'. - `flymake-inhibit-major-modes': a list of major-modes in which to inhibit `flymake-mode'. Really only useful if you want to generally add `flymake-mode' to `prog-mode-hook'." ;; The name of this hook tells you pretty much everything you need to know ;; for this little thing right here. (add-hook 'hack-local-variables-hook (defun flymake-unless@hack-local-variables () (unless (or (cdr (assoc 'flymake-inhibit file-local-variables-alist)) (list-string-match-p (buffer-name) flymake-inhibit-buffer-name-regexps) (list-string-match-p (buffer-file-name) flymake-inhibit-file-name-regexps) (apply #'derived-mode-p flymake-inhibit-major-modes)) (flymake-mode-on))))) (add-hook 'prog-mode-hook #'flymake-unless) (:bind "M-n" #'flymake-goto-next-error "M-p" #'flymake-goto-prev-error)) (setup flyspell (:hook-into text-mode)) (setup frames (:option frame-title-format '("%b@" (:eval (or (file-remote-p default-directory 'host) system-name)) " %+%* GNU Emacs" (:eval (when (frame-parameter nil 'client) " Client"))) window-resize-pixelwise t)) (setup ibuffer (:also-load ibuf-ext) (:option ibuffer-expert t ibuffer-show-empty-filter-groups nil ibuffer-saved-filter-groups '(("default" ("dired" (mode . dired-mode)) ("customize" (mode . Custom-mode)) ("emacs" (or (name . "^\\*scratch\\*$") (name . "^\\*Messages\\*$") (name . "^\\*Warnings\\*$") (name . "^\\*straight-process\\*$") (name . "^\\*Calendar\\*$"))) ("git" (or (name . "^\*magit") (name . "^\magit"))) ("help" (or (mode . help-mode) (mode . Info-mode) (mode . helpful-mode))) ("messaging" (or (mode . message-mode) (mode . bbdb-mode) (mode . mail-mode) (mode . gnus-group-mode) (mode . gnus-summary-mode) (mode . gnus-article-mode) (name . "^\\.bbdb$") (name . "^\\.newsrc-dribble") (mode . erc-mode) (mode . circe-server-mode) (mode . circe-channel-mode))) ("shell" (or (mode . eshell-mode) (mode . shell-mode) (mode . vterm-mode))) ("web" (or (mode . elpher-mode) (mode . gemini-mode) (mode . eww-mode)))))) (:global "C-x C-b" #'ibuffer) (:hook (defun ibuffer@filter-to-default () (ibuffer-switch-to-saved-filter-groups "default")))) (setup ielm (:hook #'turn-on-eldoc-mode)) (setup imenu (:option imenu-auto-rescan t)) (setup isearch (:option search-default-mode t)) (setup lines (:option fill-column 79 word-wrap t truncate-lines nil) (global-display-fill-column-indicator-mode -1) (global-so-long-mode +1) (add-hook 'visual-line-mode-hook (defun acdw/disable-fill-column-indicator () (display-fill-column-indicator-mode (if visual-line-mode -1 +1)))) ;; `acdw/kill-line-and-join-advice' cribs from `crux-kill-and-join-forward'. ;; I can't simply advise `kill-line' with an override from crux because crux ;; itself calls `kill-line', leading to a infinite nesting situation. (advice-add 'kill-line :around (defun kill-line@join (fn &rest args) (if (and (eolp) (not (bolp))) (delete-indentation 1) (apply fn args))))) (setup minibuffer (:option enable-recursive-minibuffers t file-name-shadow-properties '(invisible t intangible t) minibuffer-eldef-shorten-default t minibuffer-prompt-properties '(read-only t cursor-intangible t face minibuffer-prompt) read-answer-short t read-extended-command-predicate ; used on >28 #'command-completion-default-include-p) (add-hook 'minibuffer-setup-hook #'cursor-intangible-mode) (add-hook 'minibuffer-setup-hook #'acdw/gc-disable) (add-hook 'minibuffer-exit-hook #'acdw/gc-enable) (minibuffer-depth-indicate-mode +1) (file-name-shadow-mode +1) (minibuffer-electric-default-mode +1) (if (version< emacs-version "28") (fset 'yes-or-no-p #'y-or-n-p) (setq use-short-answers t))) (setup mouse ;; Unconditionally follow links when clicked. ;; This is because mouse-1 usually sets point. ;; Other options: ;; +[ms] -> perform mouse-2 until held for [ms], then mouse-1 ;; -[ms] -> perform mouse-1 until held for [ms], then mouse-2 ;; 'double -> mouse-2 on double click ;; nil -> mouse-1 never follows link ;; -> mouse-1 /always/ follows link (:option mouse-1-click-follows-link t)) (setup mu4e (:load-from "/usr/share/emacs/site-lisp/mu4e") (:autoload (mu4e :interactive t) make-mu4e-context) (:option message-kill-buffer-on-exit t message-send-mail-function #'smtpmail-send-it mu4e-change-filenames-when-moving t mu4e-completing-read-function 'completing-read mu4e-compose-context-policy 'ask-if-none mu4e-context-policy 'ask-if-none mu4e-contexts (list ;; Work (make-mu4e-context :name "Work" :match-func (lambda (msg) (when msg (string-prefix-p work-mail-dir (mu4e-message-field msg :maildir)))) :vars `((user-mail-address . ,work-email) (smtpmail-smtp-server . ,work-smtp-server) (mu4e-compose-format-flowed . nil) (mu4e-drafts-folder . ,(concat work-mail-dir "/[Gmail]/Drafts")) (mu4e-sent-folder . ,(concat work-mail-dir "/[Gmail]/Sent Mail")) (mu4e-refile-dir . ,(concat work-mail-dir "/[Gmail]/All Mail")) (mu4e-trash-folder . ,(concat work-mail-dir "/[Gmail]/Trash")) (mu4e-maildir-shortcuts . ,(mapcar (lambda (cell) (let ((dir (car cell)) (char (cdr cell))) (cons (concat work-mail-dir dir) char))) '(("/Inbox" . ?i) ("/[Gmail]/All Mail" . ?a) ("/[Gmail]/Sent" . ?s) ("/[Gmail]/Drafts" . ?d) ("/[Gmail]/Trash" . ?t)))))) ;; Home (make-mu4e-context :name "Home" :match-func (lambda (msg) (when msg (string-prefix-p home-mail-dir (mu4e-message-field msg :maildir)))) :vars `((user-mail-address . ,home-email) (smtpmail-smtp-server . ,home-smtp-server) (mu4e-compose-signature . "~ Case") (mu4e-compose-format-flowed . nil) (mu4e-drafts-folder . ,(concat home-mail-dir "/Drafts")) (mu4e-sent-folder . ,(concat home-mail-dir "/Sent")) (mu4e-refile-folder . ,(concat home-mail-dir "/Archive")) (mu4e-trash-folder . ,(concat home-mail-dir "/Trash")) (mu4e-maildir-shortcuts . ,(mapcar (lambda (cell) (let ((dir (car cell)) (char (cdr cell))) (cons (concat home-mail-dir dir) char))) '(("/INBOX" . ?i) ("/Archive" . ?a) ("/Sent" . ?s) ("/Drafts" . ?d) ("/Trash" . ?t))))))) mu4e-get-mail-command "mbsync -a" mu4e-maildir "~/mail" mu4e-update-interval (unless ;; I just realized... there is probably a ;; /much/ better way to do this. (file-exists-p (expand-file-name "systemd/user/mbsync.timer" (getenv "XDG_CONFIG_HOME"))) (* 60 5)) sendmail-program (seq-some #'executable-find '("msmtp" "sendmail")) message-sendmail-f-is-evil t message-sendmail-extra-arguments '("--read-envelope-from") message-send-mail-function #'smtpmail-send-it send-mail-function #'smtpmail-send-it smtpmail-smtp-service 465 smtpmail-stream-type 'ssl) (:with-mode mu4e-view-mode (:hook #'reading-mode))) (setup page (:option page-delimiter (rx bol (or "\f" ";;;") (not (any "#")) (* not-newline) "\n" (* (* blank) (opt ";" (* not-newline)) "\n"))) (defun recenter-to-top (&rest _) "Recenter the cursor to the top of the window." (when (called-interactively-p 'any) (recenter (if (or (null scroll-margin) (zerop scroll-margin)) 3 scroll-margin)))) (:advise forward-page :after #'recenter-to-top backward-page :after #'recenter-to-top) ;; I'm not sure where this is in /my/ version of Emacs ;; (defvar page-navigation-repeat-map ;; (let ((map (make-sparse-keymap))) ;; (define-key map "]" #'forward-page) ;; (define-key map "[" #'backward-page) ;; map) ;; "Keymap to repeat page navigation key sequences. Used in `repeat-mode'.") ;; (put 'forward-page 'repeat-map 'page-navigation-repeat-map) ;; (put 'backward-page 'repeat-map 'page-navigation-repeat-map) ) (setup prog (:option show-paren-delay 0 show-paren-style 'mixed show-paren-when-point-inside-paren t show-paren-when-point-in-periphery t smie-indent-basic tab-width) (:hook #'show-paren-mode #'prettify-symbols-mode ;; #'electric-pair-local-mode #'acdw/setup-fringes #'display-fill-column-indicator-mode (defun prog-mode@auto-fill () (setq-local comment-auto-fill-only-comments t) (turn-on-auto-fill)))) (setup pulse (:option pulse-flag nil pulse-delay 0.5 pulse-iterations 1) ;; XXX: this doesn't work yet. I only want to pulse the line in the active ;; window, so when I have the same buffer viewed in multiple windows I can ;; still see where my cursor is. To see the issue, C-x 2 then C-x o a few ;; times. (defun pulse-line-current-window (&rest _) "Pulse the current line, but only if this window is active." (pulse-momentary-highlight-one-line (window-point (selected-window)))) (dolist (func '(scroll-up-command scroll-down-command recenter-top-bottom other-window switch-to-buffer redraw-frame)) (advice-add func :after #'pulse-line-current-window))) (setup re-builder (require 'acdw-re) (:global "C-M-5" #'re-builder "C-M-%" #'re-builder) (:with-map reb-mode-map (:bind "C-c C-k" #'reb-quit "RET" #'reb-replace-regexp)) (:with-map reb-lisp-mode-map (:bind "RET" #'reb-replace-regexp))) (setup repeat ;; new for Emacs 28! (:only-if (fboundp #'repeat-mode)) (:option repeat-exit-key "g" repeat-exit-timeout 5) (repeat-mode +1)) (setup saveplace (:option save-place-file (acdw/dir "places.el") save-place-forget-unreadable-files (acdw/system :home)) (save-place-mode +1)) (setup scratch (:option inhibit-startup-screen t initial-buffer-choice t initial-major-mode #'lisp-interaction-mode lexical-binding t initial-scratch-message (concat ";; Howdy, " (nth 0 (split-string user-full-name)) "! " "Welcome to GNU Emacs.\n\n")) (add-hook 'kill-buffer-query-functions (defun kill-buffer-query@immortal-scratch () (if (eq (current-buffer) (get-buffer "*scratch*")) (progn (bury-buffer) nil) t)))) (setup scrolling (:option auto-window-vscroll nil fast-but-imprecise-scrolling t scroll-margin 3 scroll-conservatively 101 scroll-preserve-screen-position 1)) (setup selection (:option save-interprogram-paste-before-kill t yank-pop-change-selection t x-select-enable-clipboard t x-select-enable-primary t mouse-drag-copy-region t kill-do-not-save-duplicates t) (delete-selection-mode +1)) (setup sh-mode (:option sh-basic-offset tab-width sh-indent-after-case 0 sh-indent-for-case-alt '+ sh-indent-for-case-label 0) (:local-set indent-tabs-mode t) (when (executable-find "shfmt") (with-eval-after-load 'apheleia (:option (append apheleia-formatters) '(shfmt . ("shfmt")) (append apheleia-mode-alist) '(sh-mode . shfmt)))) (when (executable-find "shellcheck") (:straight flymake-shellcheck) (:hook flymake-mode flymake-shellcheck-load))) (setup shell-command (:option shell-command-switch (acdw/system ;; I should be testing on some variable (:home "-csi") (:work "-c")) shell-command-prompt-show-cwd t shell-command-default-error-buffer "*shell-command-errors*")) (setup shr (:option shr-width fill-column shr-max-image-proportion 0.6 shr-image-animate t shr-discard-aria-hidden t)) (setup text (:hook turn-on-auto-fill tildify-mode acdw/setup-fringes)) (setup uniquify (:option uniquify-buffer-name-style 'forward uniquify-separator path-separator uniquify-after-kill-buffer-p t uniquify-ignore-buffers-re "^\\*")) (setup variable-pitch-mode ;; I might want to change this to `buffer-face-mode-hook'... (:advise variable-pitch-mode :after (defun variable-pitch-mode@setup (&rest _) "Set up `variable-pitch-mode' with my customizations." (display-fill-column-indicator-mode (if buffer-face-mode -1 +1))))) (setup view (:option view-read-only t) (:hook (defun acdw/read-view-mode () (reading-mode (if view-mode +1 -1))))) (setup w32 (:option w32-allow-system-shell t w32-pass-lwindow-to-system nil w32-lwindow-modifier 'super w32-pass-rwindow-to-system nil w32-rwindow-modifier 'super w32-pass-apps-to-system nil w32-apps-modifier 'hyper)) (setup whitespace (:option whitespace-style '(empty indentation space-before-tab space-after-tab) indent-tabs-mode nil tab-width 4 backward-delete-char-untabify-method 'hungry) (:global "M-SPC" #'cycle-spacing) ;; http://ruzkuku.com/emacs.d.html#orgc62eb58 (:advise cycle-spacing :around (defun cycle-spacing@newlines-by-default (old arg &rest _) (funcall old (if (numberp arg) (- arg) arg))))) (setup windmove (:option windmove-wrap-around t) (:global ;; moving "C-x 4 " #'windmove-left "C-x 4 " #'windmove-right "C-x 4 " #'windmove-up "C-x 4 " #'windmove-down ;; swapping "C-x 4 S-" #'windmove-swap-states-left "C-x 4 S-" #'windmove-swap-states-right "C-x 4 S-" #'windmove-swap-states-up "C-x 4 S-" #'windmove-swap-states-down) ;; (when (fboundp 'repeat-mode) ;; (defvar windmove-repeat-map ;; (let ((map (make-sparse-keymap))) ;; ;; moving ;; (define-key map [left] #'windmove-left) ;; (define-key map [right] #'windmove-right) ;; (define-key map [up] #'windmove-up) ;; (define-key map [down] #'windmove-down) ;; ;; swapping ;; (define-key map [S-left] #'windmove-swap-states-left) ;; (define-key map [S-right] #'windmove-swap-states-right) ;; (define-key map [S-up] #'windmove-swap-states-up) ;; (define-key map [S-down] #'windmove-swap-states-down) ;; map) ;; "Keymap to repeat various `windmove' sequences. Used in `repeat-mode'.") ;; (dolist (sym '(windmove-left ;; windmove-right ;; windmove-up ;; windmove-down ;; windmove-swap-states-left ;; windmove-swap-states-right ;; windmove-swap-states-up ;; windmove-swap-states-down)) ;; (put sym 'repeat-map 'windmove-repeat-map))) ) (setup window ;; (require 'acdw-bell) (:option ;; Man-notify-method 'pushy ;; display-buffer-alist ; from FrostyX ;; '(("shell.*" (display-buffer-same-window) ()) ;; (".*" (display-buffer-reuse-window ;; display-buffer-same-window) ;; (reusable-frames . t))) recenter-positions '(top middle bottom) ;; ring-bell-function ;; (lambda () ;; (acdw-bell/flash-mode-line ;; (acdw/system :home))) use-dialog-box nil use-file-dialog nil visible-bell nil) (tooltip-mode -1)) (setup winner ;; see https://lists.gnu.org/archive/html/emacs-devel/2021-08/msg00888.html (:global "C-x 4 C-/" #'winner-undo "C-x 4 /" #'winner-undo "C-x 4 C-?" #'winner-redo "C-x 4 ?" #'winner-redo) ;; add `winner-undo' and `winner-redo' to `repeat-mode' ;; (when (fboundp 'repeat-mode) ;; (defvar winner-mode-repeat-map ;; (let ((map (make-sparse-keymap))) ;; (define-key map "/" #'winner-undo) ;; (define-key map "?" #'winner-redo) ;; map) ;; "Keymap to repeat `winner-mode' sequences. Used in `repeat-mode'.") ;; (put 'winner-undo 'repeat-map 'winner-mode-repeat-map) ;; (put 'winner-redo 'repeat-map 'winner-mode-repeat-map)) (winner-mode +1)) (setup (:straight (0x0 :host gitlab :repo "willvaughn/emacs-0x0")) (:option 0x0-default-server 'ttm)) (setup (:straight (actually-selected-window :host github :repo "duckwork/actually-selected-window.el")) (actually-selected-window-mode +1)) (setup (:straight-when affe (and (or (executable-find "fd") (executable-find "find")) (executable-find "rg"))) ;; Keys are bound in `acdw/sensible-grep' and `acdw/sensible-find' (:option affe-regexp-compiler (defun affe-orderless-regexp-compiler (input _type) (setq input (orderless-pattern-compiler input)) (cons input (lambda (str) (orderless--highlight input str)))))) (setup (:straight-when ahk-mode (acdw/system :work))) (setup (:straight alert) (:option alert-default-style (acdw/system (:home 'libnotify) (_ 'message)))) (setup (:straight (apheleia :host github :repo "raxod502/apheleia")) (require 'acdw-apheleia) (add-hook 'before-save-hook #'apheleia-dumb-auto-format) ;; Aphelia can't find prettier on Windows (though I ;; installed it, I think), and it keeps trying to start ;; new processes until Emacs runs out of subprocess space. ;; So I just enable it at home. (unless (acdw/system :work) (apheleia-global-mode +1))) (setup (:straight async) (dired-async-mode +1)) (setup (:straight avy) (:global "M-j" #'avy-goto-char-timer "C-c C-j" #'avy-resume) (:with-feature isearch (:bind "M-j" #'avy-isearch))) (setup (:straight circe) (require 'circe) (require 'acdw-irc) (:also-load acdw-circe) (defun acdw-circe/format-meta (string) "Return a format string for `lui-format'." (format "{nick:%1$d.%1$ds} *** %s" (- acdw-irc/left-margin 3) string)) (:option acdw-irc/left-margin 20 circe-channel-killed-confirmation nil circe-color-nicks-everywhere t circe-default-nick "acdw" circe-default-part-message "See You, Space Cowpokes . . ." circe-default-user "acdw" circe-format-action (format (format "%%%ds* {nick} {body}" (- acdw-irc/left-margin 2)) " ") circe-format-say (format "{nick:%1$d.%1$ds} | {body}" (- acdw-irc/left-margin 3)) circe-format-self-action circe-format-action circe-format-self-say (format "{nick:%1$d.%1$ds} > {body}" (- acdw-irc/left-margin 3)) circe-format-server-part (acdw-circe/format-meta "PART {channel}: {reason}") circe-format-server-quit (acdw-circe/format-meta "QUIT: {reason}") circe-format-server-quit-channel (acdw-circe/format-meta "QUIT {channel}: {reason}") circe-format-server-join (acdw-circe/format-meta "JOIN: {userinfo}") circe-format-server-rejoin (acdw-circe/format-meta (concat "REJOIN: {userinfo}" " after {departuredelta}")) circe-format-server-topic (acdw-circe/format-meta "TOPIC: {new-topic}") circe-prompt-string (format (format "%%%ds> " (- acdw-irc/left-margin 2)) " ") circe-reduce-lurker-spam t circe-server-auto-join-default-type :after-auth circe-server-buffer-action (lambda (buf) (message "Connected to %s" buf))) (with-eval-after-load 'circe (:face circe-nick-highlight-face ((t (:inherit (modus-themes-hl-line modus-themes-bold)))) ;; circe-my-message-face ;; ((t (:inherit (modus-themes-slant)))) )) (with-eval-after-load 'topsy (:option (append topsy-mode-functions) '(circe-channel-mode . circe-current-topic))) (:bind "C-c C-p" #'circe-command-PART "C-c C-t" #'circe-current-topic ; in acdw-circe.el "C-l" #'lui-track-jump-to-indicator "" #'circe-chat@set-prompt) (:advise circe-command-PART :after #'circe-part@kill-buffer circe-command-QUIT :after #'circe-quit@kill-buffer circe-command-GQUIT :after #'circe-gquit@kill-buffer) (:with-mode circe-chat-mode (:hook #'acdw/stop-paren-annoyances #'enable-circe-color-nicks ;; #'enable-circe-display-images #'enable-circe-new-day-notifier #'circe-chat@set-prompt #'topsy-mode)) (:bind "C-c C-s" #'circe-command-SLAP) (autoload 'circe-nick-color-reset "circe-color-nicks") (add-hook 'modus-themes-after-theme-hook #'circe-nick-color-reset) (:with-mode lui-mode (:option lui-fill-column (+ fill-column acdw-irc/left-margin) lui-fill-type nil ;;(repeat-string acdw-irc/left-margin " ") lui-time-stamp-position 'right-margin lui-time-stamp-format "%H:%M" lui-track-behavior 'before-switch-to-buffer lui-track-indicator 'fringe lui-fill-remove-face-from-newline nil) (:hook #'visual-fill-column-mode #'visual-line-mode #'enable-lui-track) (:face lui-time-stamp-face ((t :inherit font-lock-comment-face))) (:local-set visual-fill-column-extra-text-width (cons acdw-irc/left-margin 0) fringes-outside-margins t right-margin-width 5 scroll-margin 0 word-wrap t wrap-prefix (repeat-string acdw-irc/left-margin " ") nyan-mode nil line-number-mode nil column-number-mode nil file-percentage-mode nil)) (add-hook 'kill-emacs-hook (defun circe-quit-all () (ignore-errors (advice-remove 'circe-command-GQUIT 'circe-gquit@kill-buffer) (circe-command-GQUIT "Quitting Emacs, bye!"))))) (setup (:straight (command-log-mode :host github :repo "positron-solutions/command-log-mode")) ;; I have many ideas as to how to change this. (:option clm-window-text-scale 0 clm-logging-shows-buffer t clm-log-globally t clm-exceptions '(self-insert-command) clm-window-size 0.25) (el-patch-feature command-log-mode) (with-eval-after-load 'command-log-mode (el-patch-defun clm--show-buffer (&optional clear) "Displays the command log buffer in a window. CLEAR will clear the buffer if it exists before returning it." (let ((buffer (clm--setup-buffer clear))) (let ((win (get-buffer-window buffer))) (unless (windowp win) (let ((new-win (el-patch-swap (split-window-horizontally (- 0 clm-window-size)) (if (< (window-pixel-width) (window-pixel-height)) (split-window-vertically (- (if (floatp clm-window-size) (floor (* (window-height) clm-window-size)) clm-window-size))) (split-window-horizontally (- (if (floatp clm-window-size) (floor (* (window-width) clm-window-size)) clm-window-size))))))) (set-window-buffer new-win buffer) (set-window-dedicated-p new-win t) (el-patch-add (with-current-buffer buffer (setq-local mode-line-format nil))))) buffer))))) (setup (:straight (consult :host github :repo "minad/consult")) (:require acdw-consult) (:autoload consult-register-preview) ;; Bindings (:global ;; C-c bindings (`mode-specific-map') ;; I don't use any of these right now. ;; "C-c h" #'consult-history ;; "C-c m" #'consult-mode-command ;; "C-c b" #'consult-bookmark ;; "C-c k" #'consult-kmacro ;; C-x bindings (`ctl-x-map') "C-x M-:" #'consult-complex-command "C-x b" #'consult-buffer "C-x 4 b" #'consult-buffer-other-window "C-x 5 b" #'consult-buffer-other-frame ;; Custom M-# bindings for fast register access "M-#" #'consult-register-load "M-'" #'consult-register-store "C-M-#" #'consult-register ;; M-g bindings (`goto-map') "M-g e" #'consult-compile-error "M-g g" #'consult-goto-line "M-g M-g" #'consult-goto-line "M-g o" #'consult-outline "M-g m" #'consult-mark "M-g k" #'consult-global-mark "M-g i" #'consult-imenu "M-g I" #'consult-project-imenu ;; M-s bindings (`search-map') "M-s g" #'acdw-consult/sensible-grep "M-s f" #'acdw-consult/sensible-find "M-s l" #'consult-line "M-s m" #'consult-multi-occur "M-s k" #'consult-keep-lines "M-s u" #'consult-focus-lines ;; Other bindings "M-y" #'consult-yank-pop " a" #'consult-apropos ;; Isearch integration "M-s e" #'consult-isearch) (:with-map isearch-mode-map (:bind "M-e" #'consult-isearch "M-s e" #'consult-isearch "M-s l" #'consult-line)) (:option (append consult-buffer-sources) #'circe-buffer-source) (consult-history-to-modes ((minibuffer-local-map . nil) (shell-mode-map . shell-mode-hook) (term-mode-map . term-mode-hook) (term-raw-map . term-mode-hook) (comint-mode-map . comint-mode-hook) (sly-mrepl-mode-map . sly-mrepl-hook))) (:option register-preview-delay 0 register-preview-function #'consult-register-format xref-show-xrefs-function #'consult-xref xref-show-definitions-function #'consult-xref consult-project-root-function #'vc-root-dir completion-in-region-function #'acdw-consult/complete-in-region completion-cycle-threshold 3 consult-preview-key (kbd "M-.") tab-always-indent 'complete) (:advise register-preview :override #'consult-register-window) ;; Completing-read-multple (if (fboundp #'consult-completing-read-multiple) (:advise completing-read-multple :override #'consult-completing-read-multiple) (:advise completing-read-multiple :filter-args (defun crm-indicator (args) (cons (concat "[CRM] " (car args)) (cdr args))))) (with-eval-after-load 'orderless (:option consult--regexp-compiler #'consult--orderless-regexp-compiler)) (with-eval-after-loads (vertico consult) (:with-map consult-crm-map (:bind "RET" (defun +vertico-crm-exit () (interactive) (run-at-time 0 nil #'vertico-exit) (funcall #'vertico-exit)) "TAB" #'vertico-exit)))) (setup (:straight consult-dir) (:with-feature project (:autoload project--read-project-list)) (:global "C-x C-d" #'consult-dir) (with-eval-after-load 'vertico (:with-map vertico-map (:bind "C-x C-d" #'consult-dir "C-x C-j" #'consult-dir-jump-file)))) (setup (:straight crux) (:global "C-o" #'crux-smart-open-line "M-o" #'open-paragraph "C-M-\\" #'crux-cleanup-buffer-or-region "C-x 4 t" #'crux-transpose-windows) (el-patch-feature crux) (with-eval-after-load 'crux (el-patch-defun crux-reopen-as-root () "Find file as root if necessary. Meant to be used as `find-file-hook'. See also `crux-reopen-as-root-mode'." (unless (or ;; This helps fix for `nov-mode', and possibly others. (el-patch-add (null buffer-file-name)) (tramp-tramp-file-p buffer-file-name) (equal major-mode 'dired-mode) (not (file-exists-p (file-name-directory buffer-file-name))) (file-writable-p buffer-file-name) (crux-file-owned-by-user-p buffer-file-name)) (crux-find-alternate-file-as-root buffer-file-name)))) (crux-reopen-as-root-mode +1)) ;; (setup (:straight-when ;; (define-repeat-map ;; :host nil ;; :repo "https://tildegit.org/acdw/define-repeat-map.el") ;; (acdw/system :home)) ;; (require 'define-repeat-map ; just for me ;; (acdw/dir ;; "straight/build/define-repeat-map/define-repeat-map.el")) ;; (defun acdw/other-window-or-switch-buffer-backward () ;; (interactive) ;; (setq repeat-map 'other-window-repeat-map) ;; (acdw/other-window-or-switch-buffer -1)) ;; (define-repeat-map other-window ;; ("o" acdw/other-window-or-switch-buffer ;; "O" acdw/other-window-or-switch-buffer-backward)) ;; (define-repeat-map case ;; ("c" capitalize-word ;; "u" upcase-dwim ;; "l" downcase-dwim) ;; (:continue "f" forward-word ;; "b" backward-word) ;; (:enter capitalize-dwim ;; upcase-dwim ;; downcase-dwim)) ;; (define-repeat-map page-navigation ;; ("]" forward-page ;; "[" backward-page)) ;; (define-repeat-map windmove ;; (;; moving ;; "" windmove-left ;; "" windmove-right ;; "" windmove-up ;; "" windmove-down ;; ;; swapping ;; "" windmove-swap-states-left ;; "" windmove-swap-states-right ;; "" windmove-swap-states-up ;; "" windmove-swap-states-down)) ;; (define-repeat-map winner-mode ;; ("/" winner-undo ;; "?" winner-redo))) (setup (:straight dictionary) (:option dictionary-use-single-buffer t) (autoload 'dictionary-search "dictionary" "Ask for a word and search it in all dictionaries" t) (autoload 'dictionary-match-words "dictionary" "Ask for a word and search all matching words in the dictionaries" t) (autoload 'dictionary-lookup-definition "dictionary" "Unconditionally lookup the word at point." t) (autoload 'dictionary "dictionary" "Create a new dictionary buffer" t) (autoload 'dictionary-mouse-popup-matching-words "dictionary" "Display entries matching the word at the cursor" t) (autoload 'dictionary-popup-matching-words "dictionary" "Display entries matching the word at the point" t) (autoload 'dictionary-tooltip-mode "dictionary" "Display tooltips for the current word" t) (autoload 'global-dictionary-tooltip-mode "dictionary" "Enable/disable dictionary-tooltip-mode for all buffers" t) (define-key lookup-map "d" #'dictionary-search) (:hook #'reading-mode)) (setup (:straight (dogears :host github :repo "alphapapa/dogears.el" :files (:defaults (:exclude "helm-dogears.el")))) (:option (append savehist-additional-variables) 'dogears-list) (with-eval-after-load 'dogears (dolist (mode '(magit-status-mode elfeed-show-mode elfeed-search-mode)) (:option (append dogears-ignore-modes) mode))) (:global "M-g d" dogears-go) (:autoload dogears-mode) (dogears-mode +1)) (setup (:straight edit-indirect)) ;; requires extension: ;; https://addons.mozilla.org/en-US/firefox/addon/edit-with-emacs1/ (setup (:straight edit-server) (:require edit-server) (edit-server-start) (:option edit-server-default-major-mode 'text-mode edit-server-url-major-mode-alist (list (cons (rx (| "reddit.com" "tildes.net")) 'markdown-mode) (cons (rx "github.com") 'gfm-mode) (cons "." 'text-mode))) (:advise edit-server-make-frame :before (defun edit-server@set-a-variable (&rest _) (setq-local edit-server-frame-p t)))) (setup (:straight (electric-cursor :host github :repo "duckwork/electric-cursor")) (electric-cursor-mode +1)) (setup (:straight (elfeed :host github :repo "skeeto/elfeed") elfeed-protocol) (:option elfeed-use-curl t elfeed-curl-extra-arguments '("--insecure") elfeed-feeds `(("fever+https://acdw@mf.acdw.net" :api-url "https://mf.acdw.net/fever/" :password ,(acdw/make-password-fetcher :host "mf.acdw.net") :autotags ; do I want to use elfeed-org ? '(("r/emacs" reddit social emacs) ("protesilaos.com/codelog.xml" emacs) ("tildes.net" social) ("catandgirl.com" comics) ("qwantz.com" comics) ("emacsninja.com" emacs) ("falseknees.com" comics) ("emacslife.com" emacs) ("lisp.org" lisp programming) ("scheme.org" scheme programming) ("smbc-comics.com" comics) ("youtube.com" video) ("tilde.news" social) ("xkcd.com" comics)))) elfeed-show-unique-buffers t) (:autoload elfeed-set-timeout) (elfeed-set-timeout 3600) (elfeed-protocol-enable) (:advise elfeed :after (defun elfeed@protocol-update (&rest _) (elfeed-search-fetch nil))) (:with-mode elfeed-search-mode (:bind "G" (defun elfeed-protocol|update-first (arg) (interactive "P") (let ((first-proto (caar elfeed-feeds))) (if arg (call-interactively #'elfeed-protocol-fever-reinit) (with-temp-message (format "Updating %s" first-proto) (elfeed-protocol-fever-reinit first-proto))))))) (:with-mode elfeed-show-mode (:hook #'reading-mode) (:local-set shr-max-image-proportion 0.9 visual-fill-column-width (+ fill-column 5)) ;; see https://irreal.org/blog/?p=8885 (:bind "SPC" (defun elfeed-scroll-up-command (&optional arg) "Scroll up or go to next feed item in Elfeed" (interactive "^P") (let ((scroll-error-top-bottom nil)) (condition-case-unless-debug nil (scroll-up-command arg) (error (elfeed-show-next))))) "S-SPC" (defun elfeed-scroll-down-command (&optional arg) "Scroll up or go to next feed item in Elfeed" (interactive "^P") (let ((scroll-error-top-bottom nil)) (condition-case-unless-debug nil (scroll-down-command arg) (error (elfeed-show-prev)))))))) (setup (:straight elisp-slime-nav) (:hook-into emacs-lisp-mode ielm-mode)) (setup (:straight (elpher :host nil :repo "git://thelambdalab.xyz/elpher.git")) (:option elpher-ipv4-always t elpher-certificate-directory (acdw/dir "elpher/") elpher-gemini-max-fill-width fill-column) (:bind "n" #'elpher-next-link "p" #'elpher-prev-link "o" #'elpher-follow-current-link "G" #'elpher-go-current) (:hook #'reading-mode) (:autoload (elpher-bookmarks :interactive t) (elpher-go :interactive t)) ;; Make `eww' gemini/gopher aware. From Emacswiki. ;; (define-advice eww-browse-url (:around (fn url &rest args) gemini-elpher) ;; (cond ((string-match-p "\\`\\(gemini\\|gopher\\)://" url) ;; (require 'elpher) ;; (elpher-go url)) ;; (t (apply fn url args)))) ) (setup (:straight-when emacs-everywhere (and (executable-find "xclip") (executable-find "xdotool") (executable-find "xprop") (executable-find "xwininfo")))) (setup (:straight (embark ; gotta git that fresh fresh :host github :repo "oantolin/embark")) (:global "C-." #'embark-act) (:option prefix-help-command #'embark-prefix-help-command (append display-buffer-alist) `(,(rx (seq bos "*Embark Collect " (group (| "Live" "Completions")) "*")) nil (window-parameters (mode-line-format . none))) embark-prompter #'embark-keymap-prompter embark-verbose-indicator-display-action '(display-buffer-at-bottom (window-height . fit-window-to-buffer)) embark-action-indicator (lambda (map _target) (which-key--show-keymap "Embark" map nil nil 'no-paging) #'which-key--hide--ignore-command) embark-become-indicator embark-action-indicator) (with-eval-after-loads (embark consult) (:straight embark-consult) (add-hook 'embark-collect-mode-hook #'consult-preview-at-point-mode))) (setup (:straight epithet) (dolist (hook '(Info-selection-hook eww-after-render-hook help-mode-hook occur-mode-hook)) (add-hook hook #'epithet-rename-buffer))) ;; TODO: look into emms or something related for this (setup (:straight-when eradio (executable-find "mpv")) (:option eradio-player '("mpv" "--no-video" "--no-terminal") eradio-channels `(("KLSU" . "http://130.39.238.143:8010/stream.mp3") ("Soma FM Synphaera" . "https://somafm.com/synphaera256.pls") ("SomaFM BAGel Radio" . "https://somafm.com/bagel.pls") ("SomaFM Boot Liquor" . "https://somafm.com/bootliquor320.pls") ("SomaFM Deep Space One" . "https://somafm.com/deepspaceone.pls") ("SomaFM Fluid" . "https://somafm.com/fluid.pls") ("SomaFM Underground 80s" . "https://somafm.com/u80s256.pls") ("WBRH: Jazz & More" . "http://wbrh.streamguys1.com/wbrh-mp3") ("KBRH Blues & Rhythm Hits" . "http://wbrh.streamguys1.com/kbrh-mp3") ("WRKF HD-2" . ,(concat "https://playerservices.streamtheworld.com/" "api/livestream-redirect/WRKFHD2.mp3")) ("WRKF: NPR for the Capital Region" . ,(concat "https://playerservices.streamtheworld.com/" "api/livestream-redirect/WRKFFM.mp3")) ("BadRadio: 24/7 PHONK" . "https://s2.radio.co/s2b2b68744/listen") ("tilderadio" . "https://azuracast.tilderadio.org/radio/8000/radio.ogg") ("vantaradio" . "https://vantaa.black/radio"))) (:global "C-c r r" #'eradio-play ; mnemonic: radio "C-c r s" #'eradio-stop ; mnemonic: stop "C-c r p" #'eradio-toggle ; mnemonic: play/pause )) (setup (:straight eros) (:hook-into emacs-lisp-mode lisp-interaction-mode)) (setup (:straight-when exec-path-from-shell (acdw/system :home)) (when (daemonp) (exec-path-from-shell-initialize)) (exec-path-from-shell-copy-envs '("XDG_CONFIG_HOME" "XDG_CONFIG_DIRS" "XDG_DATA_HOME" "XDG_DATA_DIRS" "XDG_CACHE_HOME"))) (setup (:straight expand-region) (:global "C-=" #'er/expand-region)) (setup (:straight-when fennel-mode (executable-find "fennel")) (:autoload (fennel-repl :interactive t)) (:file-match (rx ".fnl" eos))) (setup (:straight flyspell-correct) (:option flyspell-correct-interface #'flyspell-correct-completing-read flyspell-correct--cr-key ";") (:with-feature flyspell (:bind "C-." #'flyspell-correct-wrapper "" (defun acdw/flyspell-correct-f7 () "Run a full spell correction on the current buffer." (interactive) (save-mark-and-excursion (flyspell-correct-move 0 :forward :rapid)))) (:unbind "C-;" "C-," "C-." "C-M-i"))) (setup (:straight-when forge (acdw/system :home)) ;; make sure to read Info manual with Forge (and Ghub) for setup ;; instructions. (with-eval-after-load 'magit (require 'forge) (add-to-list 'forge-alist ; tildegit is a gitea server '("tildegit.org" "tildegit.org/api/v1" "tildegit.org" forge-gitea-repository)))) (setup (:straight (frowny :host github :repo "duckwork/frowny.el")) (:option frowny-eyes (rx (| ":" ":-" ":'" "=")) frowny-eyes-looking-back-limit 2) (global-frowny-mode +1)) (setup (:straight gcmh) (:option gcmh-idle-delay 'auto) (gcmh-mode +1)) (setup (:straight-when geiser (progn (defvar acdw/schemes (let (schemes) (dolist (scheme '(("scheme" . geiser-chez) ; chez ("petite" . geiser-chez) ; petite ("csi" . geiser-chez) ; chicken ("gsi" . geiser-gambit) ("gosh" . geiser-gauche) ("guile" . geiser-guile) ("kawa" . geiser-kawa) ("mit-scheme" . geiser-mit) ("racket" . geiser-racket) ("stklos" . geiser-stklos))) (when-let (binary (executable-find (car scheme))) (push binary schemes) ;; and install the proper helper package (straight-use-package (cdr scheme)))) (nreverse schemes))) acdw/schemes)) (:file-match (rx ".rkt" eos) (rx ".scm" eos))) (setup (:straight (gemini-mode :host nil :repo "https://git.carcosa.net/jmcbray/gemini.el.git")) (:file-match (rx (seq "." (or "gemini" "gmi") eos))) (:hook turn-off-auto-fill)) (setup (:straight (gemini-write :host nil :repo "https://alexschroeder.ch/cgit/gemini-write" :branch "main")) (with-eval-after-load 'elpher (require 'gemini-write))) (setup (:straight git-modes)) (setup (:straight helpful) (:require-after 3) (:global " f" #'helpful-callable " v" #'helpful-variable " k" #'helpful-key " o" #'helpful-symbol)) (setup (:straight (hippie-completing-read :host github :repo "duckwork/hippie-completing-read")) (:global "M-/" #'hippie-completing-read)) (setup (:straight hungry-delete) (:option hungry-delete-chars-to-skip " \t" hungry-delete-join-reluctantly nil) (global-hungry-delete-mode +1) (:with-feature paredit (:bind [remap paredit-backward-delete] (defun acdw/paredit-hungry-delete-backward (arg) (interactive "P") (if (looking-back "[ \t]" 1) (hungry-delete-backward (or arg 1)) (paredit-backward-delete arg))) [remap paredit-forward-delete] (defun acdw/paredit-hungry-delete-forward (arg) (interactive "P") (if (looking-at "[ \t]") (hungry-delete-forward (or arg 1)) (paredit-forward-delete arg)))))) (setup (:straight iscroll) (define-globalized-minor-mode global-iscroll-mode iscroll-mode (lambda () (iscroll-mode +1))) (global-iscroll-mode +1)) (setup (:straight (kaomoji-insert :host nil :repo "https://tildegit.org/acdw/kaomoji-insert")) (require 'kaomoji-insert) (dolist (km '(("(Ծ‸ Ծ)" "suspicious") ("(¬‿¬)═ɜ ɛ═(⌐‿⌐ )" "pound it" "fist bump") ("▬▬▬▬▬▬▬▋ Ò╭╮Ó" "hammer") ("👁👄👁" "lewk") ("( ͡~ ͜ʖ ͡°)" "wink") (" (づ ̄ ³ ̄)づ " "party") ("⊙﹏⊙" "uhhh" "unsure"))) (add-to-list 'kaomoji-insert-alist km)) (:global "C-x 8 k" #'kaomoji-insert)) ;; (setup (:straight wrap-region) ;; (:hook-into org-mode) ;; (with-eval-after-load 'org ;; (dolist (punc '("=" "*" "/" "_" "+")) ;; (wrap-region-add-wrapper punc punc nil 'org-mode)))) (setup (:straight lacarte) (:global "" #'lacarte-execute-menu-command)) (setup (:straight-when ledger-mode (executable-find "ledger"))) (setup (:straight link-hint) ;; Browse web URLs with a browser with a prefix argument. (dolist (type '(gnus-w3m-image-url gnus-w3m-url markdown-link mu4e-attachment mu4e-url notmuch-hello nov-link org-link shr-url text-url w3m-link w3m-message-link)) (link-hint-define-type type :open-secondary browse-url-secondary-browser-function :open-secondary-multiple t)) (defun acdw/link-hint-open-all-links (prefix) "Open all visible links. When PREFIX is non-nil, open links with `browse-url-secondary-browser-function'." (interactive "P") (avy-with link-hint-open-all-links (link-hint--all (if prefix :open-secondary :open)))) (defun acdw/link-hint-open-multiple-links (prefix) "Use `avy' to open multiple visible links at once. When PREFIX is non-nil, open links with `browse-url-secondary-browser-function'." (interactive "P") (avy-with link-hint-open-multiple-links (link-hint--multiple (if prefix :open-secondary :open)))) (:option link-hint-avy-style 'at-full) (:global "C-j" (defun acdw/link-hint-open-link (arg) "Open a link using `link-hint-open-link', prefix-aware. That is, a prefix argument (\\[universal-argument]) will open the browser defined in `browse-url-secondary-browser-function'." (interactive "P") (avy-with link-hint-open-link (link-hint--one (if arg :open-secondary :open)))))) (setup (:straight lua-mode) (:file-match (rx ".lua" eos))) (setup (:straight macrostep) (define-key emacs-lisp-mode-map (kbd "C-c e") #'macrostep-expand) (define-key lisp-interaction-mode-map (kbd "C-c e") #'macrostep-expand)) (setup (:straight magit) (:global "C-x g" #'magit-status) (:option magit-display-buffer-function (defun magit-display-buffer-same-window (buffer) "Display BUFFER in the selected window like God intended." (display-buffer buffer '(display-buffer-same-window))) magit-popup-display-buffer-action '((display-buffer-same-window)) magit-refresh-status-buffer nil)) (setup (:straight marginalia) (:option marginalia-annotators '(marginalia-annotators-heavy marginalia-annotators-light)) (marginalia-mode +1)) (setup (:straight markdown-mode) (:file-match (rx ".md" eos) (rx ".markdown" eos)) (:hook #'variable-pitch-mode #'visual-fill-column-mode) (:with-mode gfm-mode (:file-match (rx "README.md" eos)) (:hook #'variable-pitch-mode)) (when (executable-find "markdownfmt") (with-eval-after-load 'apheleia (:option (append apheleia-formatters) '(markdownfmt . ("markdownfmt")) (append apheleia-mode-alist) '(markdown-mode . markdownfmt) (append apheleia-mode-alist) '(gfm-mode . markdownfmt))))) (setup (:straight (mastodon :host github :repo "mooseyboots/mastodon.el")) (:option mastodon-instance-url "https://writing.exchange" mastodon-auth-source-file (car auth-sources) mastodon-client--token-file (acdw/dir "mastodon.plstore") mastodon-tl--enable-proportional-fonts t mastodon-tl--enable-relative-timestamps nil) (:hook #'hl-line-mode #'reading-mode) (defun mastodon-goto-toot@recenter () "Recenter the current toot." (recenter -1)) (:advise mastodon-tl--goto-next-toot :after #'mastodon-goto-toot@recenter mastodon-tl--goto-prev-toot :after #'mastodon-goto-toot@recenter)) (setup (:straight mode-line-bell) (:option mode-line-bell-flash-time 0.1) (mode-line-bell-mode +1)) (setup (:straight (modus-themes :host gitlab :repo "protesilaos/modus-themes")) (:option modus-themes-slanted-constructs t modus-themes-bold-constructs t modus-themes-fringes nil modus-themes-mode-line '(borderless) modus-themes-region '(bg-only) modus-themes-org-blocks 'gray-background modus-themes-headings '((t . (background))) modus-themes-lang-checkers '(straight-underline) modus-themes-scale-headings nil) (acdw/sunrise-sunset #'modus-themes-load-operandi #'modus-themes-load-vivendi) (add-hook 'modus-themes-after-load-theme-hook (defun modus-themes@customize-faces () "Customize faces of modus-themes." ;; (dolist (face '(font-lock-builtin-face ;; ;; font-lock-comment-delimiter-face ;; ;; font-lock-coment-face ;; font-lock-constant-face ;; ;; font-lock-doc-face ;; font-lock-function-name-face ;; font-lock-keyword-face ;; font-lock-negation-char-face ;; font-lock-preprocessor-face ;; font-lock-regexp-grouping-backslash ;; font-lock-regexp-goruping-construct ;; font-lock-string-face ;; font-lock-type-face ;; font-lock-variable-name-face ;; font-lock-warning-face)) ;; (modus-themes-with-colors ;; (custom-set-faces ;; `(,face ;; ((,class :foreground ,fg-main ;; :weight normal ;; :slant normal)))))) ;; Other faces (modus-themes-with-colors (custom-set-faces `(org-level-1 ((,class :inherit (modus-themes-heading-1 fixed-pitch) :extend t))) `(org-level-2 ((,class :inherit (modus-themes-heading-2 fixed-pitch) :extend t))) `(org-level-3 ((,class :inherit (modus-themes-heading-3 fixed-pitch) :extend t))) `(org-level-4 ((,class :inherit (modus-themes-heading-4 fixed-pitch) :extend t))) `(org-level-5 ((,class :inherit (modus-themes-heading-5 fixed-pitch) :extend t))) `(org-level-6 ((,class :inherit (modus-themes-heading-6 fixed-pitch) :extend t))) `(org-level-7 ((,class :inherit (modus-themes-heading-7 fixed-pitch) :extend t))) `(org-level-8 ((,class :inherit (modus-themes-heading-8 fixed-pitch) :extend t)))))))) (setup (:straight mwim) (:global "C-a" #'mwim-beginning "C-e" #'mwim-end)) (setup (:straight nov) (:option nov-text-width fill-column) (:file-match (rx ".epub" eos))) (setup (:straight (nyan-mode :host github :repo "TeMPOraL/nyan-mode" :fork (:host github :repo "duckwork/nyan-mode") :files ("nyan-mode.el" "img"))) (:option nyan-animate-nyancat nil nyan-bar-length 20 nyan-minimum-window-width (+ fill-column (/ nyan-bar-length 2))) (nyan-mode +1) (defun disable-nyan-mode () "Disable `nyan-mode' in current buffer." (setq-local nyan-mode -1)) (dolist (mode '(eshell-mode comint-mode)) (add-hook mode #'disable-nyan-mode))) ;; (setup (:straight olivetti) ;; (:option olivetti-body-width (+ fill-column 4) ;; olivetti-minimum-body-width fill-column) ;; (:hook (defun olivetti-mode@setup () ;; (if olivetti-mode ;; (setq-local indicate-empty-lines nil ;; indicate-buffer-boundaries nil) ;; (acdw/setup-fringes))))) (setup (:straight (orderless :host github :repo "oantolin/orderless")) (require 'orderless) (:option (append completion-styles) 'orderless orderless-component-separator #'orderless-escapable-split-on-space orderless-matching-styles '(orderless-literal orderless-regexp ;; orderless-flex ) orderless-style-dispatchers '(acdw/orderless-dispatch)) (:advise orderless-regexp :filter-args (defun fix-dollar (args) (if (string-suffix-p "$" (car args)) (list (concat (substring (car args) 0 -1) "[\x100000-\x10FFFD]*$")) args))) (defun acdw/orderless-dispatch (pattern _index _total) "My custom dispatcher for `orderless'." (cond ;; Ensure that $ works with Consult commands, which add disambiguation ;; suffixes -- see `fix-dollar' ((string-suffix-p "$" pattern) `(orderless-regexp . ,(concat (substring pattern 0 -1) "[\x100000-\x10FFFD]*$"))) ;; File extensions ((string-match-p "\\`\\.." pattern) `(orderless-regexp . ,(concat "\\." (substring pattern 1) "[\x100000-\x10FFFD]*$"))) ;; Ignore single ! ((string= "!" pattern) `(orderless-literal . "")) ;; Character folding ((string-prefix-p "%" pattern) `(char-fold-to-regexp . ,(substring pattern 1))) ((string-suffix-p "%" pattern) `(char-fold-to-regexp . ,(substring pattern 0 -1))) ;; Without literal ((string-prefix-p "!" pattern) `(orderless-without-literal . ,(substring pattern 1))) ((string-suffix-p "!" pattern) `(orderless-without-literal . ,(substring pattern 0 -1))) ;; Initialism matching ((string-prefix-p "`" pattern) `(orderless-initialism . ,(substring pattern 1))) ((string-suffix-p "`" pattern) `(orderless-initialism . ,(substring pattern 0 -1))) ;; Literal matching ((string-prefix-p "=" pattern) `(orderless-literal . ,(substring pattern 1))) ((string-suffix-p "=" pattern) `(orderless-literal . ,(substring pattern 0 -1))) ;; Flex matching ((string-prefix-p "~" pattern) `(orderless-flex . ,(substring pattern 1))) ((string-suffix-p "~" pattern) `(orderless-flex . ,(substring pattern 0 -1)))))) (setup (:straight (org :type git :repo "https://git.savannah.gnu.org/git/emacs/org-mode.git" :local-repo "org" :depth full :pre-build (straight-recipes-org-elpa--build) :build (:not autoloads) :files (:defaults "lisp/*.el" ("etc/styles/" "etc/styles/*"))) (org-contrib :type git :repo "https://git.sr.ht/~bzg/org-contrib")) (:also-load acdw-org) (require 'chd nil 'noerror) (:option org-adapt-indentation nil ;; org-agenda-files nil ; only until I set this up org-catch-invisible-edits 'show-and-error org-clock-clocked-in-display 'mode-line org-clock-frame-title-format (cons '(t org-mode-line-string) (cons " --- " frame-title-format)) org-clock-string-limit 7 ; just the clock bit ;; org-clock-string-limit 25 ; gives enough information org-clock-persist t org-confirm-babel-evaluate nil org-cycle-separator-lines 0 org-directory "~/org" org-ellipsis " …" org-export-coding-system 'utf-8-unix org-export-headline-levels 8 org-export-with-section-numbers nil org-export-with-smart-quotes t org-export-with-sub-superscripts t org-export-with-toc nil org-fontify-done-headline t org-fontify-quote-and-verse-blocks t org-fontify-whole-heading-line t org-hide-emphasis-markers t org-html-coding-system 'utf-8-unix org-image-actual-width '(300) org-imenu-depth 3 org-list-demote-modify-bullet '(("-" . "+") ("+" . "*") ("*" . "-")) org-log-done 'time org-log-into-drawer t org-outline-path-complete-in-steps nil org-pretty-entities t org-refile-use-outline-path 'file org-special-ctrl-a/e t org-special-ctrl-k t org-src-fontify-natively t org-src-tab-acts-natively t org-src-window-setup 'current-window org-startup-truncated nil org-startup-with-inline-images t org-tags-column (- (- fill-column (length org-ellipsis)))) (:bind "RET" #'acdw-org/return-dwim "" #'acdw-org/org-table-copy-down ;; "M-SPC M-SPC" #'insert-zero-width-space "C-c C-l" #'org-insert-link-dwim "C-c w" #'chd/do-the-thing "C-c C-n" #'acdw/org-next-heading-widen "C-c C-p" #'acdw/org-previous-heading-widen "C-x n t" #'org-narrow-to-task) (:unbind "C-j" ; org-return-and-maybe-indent "M-j") (:local-set unfill-fill-function #'org-fill-paragraph wc-count-words-function (lambda (start end) "Count words stupidly with a limit." (acdw-org/count-words-stupidly start end 999))) (with-eval-after-load 'org-export (:option (append org-export-filter-final-output-functions) #'org-export-remove-zero-width-spaces)) (:local-hook before-save-hook (defun org/before-save@prettify-buffer () (save-mark-and-excursion (mark-whole-buffer) (org-fill-paragraph nil t)) (acdw-org/fix-blank-lines t) (org-align-tags :all))) (with-eval-after-load 'org (org-clock-persistence-insinuate)) (with-eval-after-load 'consult (defun consult-clock-in (&optional match scope resolve) "Clock into an Org heading." (interactive (list nil nil current-prefix-arg)) (require 'org-clock) (org-clock-load) (save-window-excursion (consult-org-heading match (or scope (thread-last org-clock-history (mapcar 'marker-buffer) (mapcar 'buffer-file-name) (delete-dups) (delq nil)) (user-error "No recent clocked tasks"))) (org-clock-in nil (when resolve (org-resolve-clocks) (org-read-date t t))))) (consult-customize consult-clock-in :prompt "Clock in: " :preview-key (kbd "M-.") :group (lambda (cand transform) (if transform (substring cand (next-single-property-change 0 'consult-org--buffer cand)) (let ((m (car (get-text-property 0 'consult-org--heading cand)))) (if (member m org-clock-history) "*Recent*" (buffer-name (marker-buffer m)))))))) (:advise org-delete-backward-char :override #'acdw-org/delete-backward-char) (el-patch-feature org) (with-eval-after-load 'org (el-patch-defun org-format-outline-path (path &optional width prefix separator) "Format the outline path PATH for display. WIDTH is the maximum number of characters that is available. PREFIX is a prefix to be included in the returned string, such as the file name. SEPARATOR is inserted between the different parts of the path, the default is \"/\"." (setq width (or width 79)) (setq path (delq nil path)) (unless (> width 0) (user-error "Argument `width' must be positive")) (setq separator (or separator "/")) (let* ((org-odd-levels-only nil) (fpath (concat prefix (and prefix path separator) (mapconcat (lambda (s) (replace-regexp-in-string "[ \t]+\\'" "" s)) (cl-loop for head in path for n from 0 collect (el-patch-swap (org-add-props head nil 'face (nth (% n org-n-level-faces) org-level-faces)) head)) separator)))) (when (> (length fpath) width) (if (< width 7) ;; It's unlikely that `width' will be this small, but don't ;; waste characters by adding ".." if it is. (setq fpath (substring fpath 0 width)) (setf (substring fpath (- width 2)) ".."))) fpath)))) (setup (:straight org-appear) (:option org-appear-autoemphasis t org-appear-autoentities t org-appear-autokeywords t org-appear-autolinks nil org-appear-autosubmarkers t org-appear-delay 0) (:hook-into org-mode)) (setup (:straight org-sticky-header) (:hook-into org-mode)) (setup (:straight package-lint package-lint-flymake)) (setup (:straight page-break-lines) (global-page-break-lines-mode +1)) (setup (:straight paredit) (:bind "DEL" #'paredit-backward-delete "C-" #'paredit-backward-kill-word "M-w" #'paredit-copy-as-kill "RET" #'paredit-newline) (:unbind "C-j" ; paredit-newline ) (:hook-into emacs-lisp-mode lisp-interaction-mode ielm-mode sly-repl-mode lisp-mode scheme-mode) (:also-load eldoc) (eldoc-add-command 'paredit-backward-delete 'paredit-close-round)) (setup (:straight paren-face) (:hook-into emacs-lisp-mode ielm-mode sly-repl-mode lisp-mode lisp-interaction-mode scheme-mode)) (setup (:straight-when (pdf-tools :host github :repo "vedang/pdf-tools") (acdw/system :home)) (:file-match (rx ".pdf" eos)) (pdf-loader-install)) (setup (:straight popper) (:option popper-reference-buffers `(,(rx "*Messages*") ,(rx "Output*" eol) ,(rx "*Async Shell Command*") help-mode helpful-mode compilation-mode) popper-mode-line nil popper-display-control t popper-display-function (defun popper/select-popup-smartly (buffer &optional _alist) (let* ((widep (> (frame-pixel-width) (frame-pixel-height))) (window (display-buffer-in-side-window buffer `((side . ,(if widep 'right 'bottom)) (slot . 1) ,(if widep (cons 'window-width popper-window-height) (cons 'window-height popper-window-height)))))) (select-window window))) popper-window-height (defun popper/figure-window-height (window) (let* ((widep (> (frame-pixel-width) (frame-pixel-height))) (fit-window-to-buffer-horizontally widep)) (fit-window-to-buffer window (floor (frame-pixel-height) 2) (floor (frame-pixel-height) 4) fill-column fill-column)))) (:global "M-`" #'popper-toggle-latest "C-`" #'popper-cycle) (popper-mode +1) (when (fboundp 'popper-echo-mode) (popper-echo-mode +1))) (setup (:straight-when (powershell :host github :repo "jschaf/powershell.el") (acdw/system :work))) (setup (:straight (shell-command+ :host nil :repo "https://git.sr.ht/~pkal/shell-command-plus")) (:option shell-command-prompt "$ ") (:with-feature dired (:bind "M-!" shell-command+)) (:global "M-!" shell-command+)) (setup (:straight sicp)) (setup (:straight simple-modeline minions) (:also-load acdw-modeline) (:option simple-modeline-segments ;; Yeah this is laid out like poo. It's so I can easily change ;; things around if need be. '((;; left acdw-modeline/winum acdw-modeline/modified acdw-modeline/buffer-name acdw-modeline/vc-branch acdw-modeline/wc acdw-modeline/nyan-cat acdw-modeline/position ) (;; right acdw-modeline/track simple-modeline-segment-misc-info acdw-modeline/text-scale simple-modeline-segment-process acdw-modeline/god-mode-indicator acdw-modeline/minions acdw-modeline/reading-mode acdw-modeline/narrowed acdw-modeline/major-mode ))) (:option tab-bar-mode t tab-bar-show 1) ;; I've put in a pull request to add the (- 0 right-margin) bit here. (el-patch-feature simple-modeline) (with-eval-after-load 'simple-modeline (el-patch-defun simple-modeline--format (left-segments right-segments) "Return a string of `window-width' length containing LEFT-SEGMENTS and RIGHT-SEGMENTS, aligned respectively." (let* ((left (simple-modeline--format-segments left-segments)) (right (simple-modeline--format-segments right-segments)) (reserve (length right))) (concat left (propertize " " 'display (el-patch-swap `((space :align-to (- right ,reserve))) `((space :align-to (- right (- 1 right-fringe right-margin) ,reserve)))) 'face '(:inherit simple-modeline-space)) right)))) (simple-modeline-mode +1)) (setup (:straight-when sly (progn (defvar acdw/lisps (let (lisps) (dolist (lisp '("sbcl" ; TODO: add more lisps "clisp")) (when-let (binary (executable-find lisp)) (push binary lisps))) (nreverse lisps))) acdw/lisps)) (:also-load sly-autoloads) (:straight clhs) (:option inferior-lisp-program acdw/lisp-bin sly-kill-without-query-p t) (:with-feature sly-mrepl (defun sly-mrepl-return-at-end () (interactive) (if (<= (point-max) (point)) (sly-mrepl-return) (if (bound-and-true-p paredit-mode) (paredit-newline) (electric-newline-and-maybe-indent)))) (dolist (key '("RET" "")) (:bind key #'sly-mrepl-return-at-end)) (:bind "C-c C-c" #'sly-mrepl-return))) (setup (:straight (spongebob-case :host github :repo "duckwork/spongebob-case.el")) (:global "C-c c s" #'spongebob-case-dwim)) (setup (:straight ssh-config-mode) (:file-match (rx "/.ssh/config" eos) (rx "/ssh" (? "d") "_config" eos)) (:with-mode ssh-known-hosts-mode (:file-match (rx "/knownhosts" eos))) (:with-mode ssh-authorized-keys-mode (:file-match (rx "/authorized_keys" (? "2") eos))) (:hook #'turn-on-font-lock)) (setup (:straight super-save) (:option auto-save-default nil super-save-auto-save-when-idle t super-save-idle-duration 60 super-save-exclude '(".gpg") super-save-remote-files nil) (auto-save-visited-mode -1) (super-save-mode +1)) (setup (:straight-when system-packages (seq-some #'executable-find ;; I can't use `system-packages-supported-package-managers' ;; because, well, the package isn't installed yet. So ;; ... update this list if any package managers are added. '("guix" "nix" "brew" "macports" "pacman" "emerge" "zypper" "dnf" "apt" "aptitude" "xbps")))) (setup (:straight-when systemd (executable-find "systemd"))) (setup (:straight (topsy :host github :repo "alphapapa/topsy.el")) (:hook-into prog-mode) (:when-loaded (:option topsy-header-line-format `(:eval (list (propertize " " 'display `((space :align-to ,(unless (bound-and-true-p visual-fill-column-mode) 0)))) (funcall topsy-fn)))))) (setup (:straight trashed) (:option trashed-action-confirmer #'y-or-n-p)) (setup (:straight typo) ;; Enable C-c 8 map in all buffers (typo-global-mode +1) (add-hook 'text-mode-hook (defun text-mode@typo-unless () "Start `typo-mode' UNLESS the buffer matches a predicate." ;; I implement this instead of using ;; `typo-disable-electricity-functions' because the latter checks ;; on every pertinent keypress. I know I want /no/ typo-ing in ;; these certain buffers, so I won't even turn on the mode. (unless (or ; predicates here (string-match-p "COMMIT_EDITMSG" (or (buffer-name) ""))) (typo-mode +1)))) ;; jlf & cvandusen on #emacs make a great point: ’ (RIGHT SINGLE QUOTATION ;; MARK) is /not/ an apostrophe. Making it curly is a typographical ;; consideration, not an input consideration. (I suppose you could make ;; the argument that all of these are typographical considerations, but ;; .. bleh.) (:bind "'" (define-typo-cycle typo-cycle-apostrophe "Cycle through apostrophe-like graphemes. If used with a numeric prefix argument N, N apostrophes will be inserted." ("'" "′" "″" "’")) "`" (define-typo-cycle typo-cycle-backtick "Cycle through backtick and left single quotation mark. If used with a numeric prefix argument N, N backticks will be inserted." ("`" "‘")))) (setup (:straight undo-fu) (:global "C-/" #'undo-fu-only-undo "C-?" #'undo-fu-only-redo)) (setup (:straight undo-fu-session) (:option undo-fu-session-incompatible-files '("/COMMIT_EDITMSG\\'" "/git-rebase-todo\\'") undo-fu-session-directory (acdw/dir "undo/" t) undo-fu-session-compression (acdw/system :home)) (global-undo-fu-session-mode +1)) (setup (:straight (unfill :host github :repo "purcell/unfill" :fork (:host github :repo "duckwork/unfill"))) (:global "M-q" #'unfill-toggle)) (setup (:straight (unfocused :host github :repo "duckwork/unfocused")) (unfocused-mode +1) (:with-hook unfocused-hook (:hook #'garbage-collect))) (setup (:straight (vertico :host github :repo "minad/vertico" :files ("*" "extensions/*" (:exclude ".git")))) (:option resize-mini-windows 'grow-only vertico-count-format nil vertico-cycle t) (when (boundp 'native-comp-deferred-compilation-deny-list) (add-to-list 'native-comp-deferred-compilation-deny-list "vertico")) (vertico-mode +1) ;; Extensions! (:also-load vertico-mouse vertico-directory) (vertico-mouse-mode +1) (:with-map vertico-map (:bind "RET" #'vertico-directory-enter "DEL" #'vertico-directory-delete-char "M-DEL" #'vertico-directory-delete-word)) (add-hook 'rfn-eshadow-update-overlay-hook #'vertico-directory-tidy)) (setup (:straight visual-fill-column) (:option visual-fill-column-width (1+ fill-column) visual-fill-column-center-text t (append reading-modes) '(visual-fill-column-mode . +1) (append reading-modes) '(visual-line-mode . +1) (append reading-vars) '(fill-column . 0)) (:hook-into org-mode) (:hook (defun visual-fill-column@setup () (if visual-fill-column-mode (setq-local indicate-empty-lines nil indicate-buffer-boundaries nil) (acdw/setup-fringes)))) (:advise text-scale-adjust :after #'visual-fill-column-adjust) ;; Fix bindings (when (bound-and-true-p mouse-wheel-mode) (with-eval-after-load 'visual-fill-column (dolist (margin '(right-margin left-margin)) (dolist (event '(wheel-down wheel-up)) (define-key visual-fill-column-mode-map (vector margin event) #'mwheel-scroll)))))) (setup (:straight visual-regexp) (:global "M-%" #'vr/query-replace)) (setup (:straight-when vterm (acdw/system :home)) (:straight (eshell-vterm :host github :repo "iostapyshyn/eshell-vterm")) (eshell-vterm-mode +1) (defalias 'eshell/v 'eshell-exec-visual)) (setup (:straight wc-mode) (:option wc-modeline-format "[%tww]" wc-idle-wait 2) (:hook-into text-mode) (:unbind "C-c C-w a" "C-c C-w c" "C-c C-w l" "C-c C-w w" "C-c C-w")) (setup (:straight web-mode) (:option css-level-offset 2 js-indent-level 2 sgml-indent-offset 2) (:file-match (rx ".htm" (? "l") eos) (rx "." (? "tpl.") "php" eos) (rx "." (| "asp" "gsp" "jsp") eos) (rx "." (| "ascx" "aspx") eos) (rx ".erb" eos) (rx ".mustache" eos))) (setup (:straight wgrep) (wgrep-setup)) ;; (setup (:straight which-key) ;; (:option which-key-show-early-on-C-h t ;; which-key-idle-delay 1 ;; which-key-idle-secondary-delay 0.5 ;; which-key-sort-order 'which-key-prefix-then-key-order) ;; (:global "C-h m" #'which-key-show-major-mode) ;; (which-key-setup-side-window-right-bottom) ;; (which-key-mode +1)) (setup (:straight whitespace-cleanup-mode) (global-whitespace-cleanup-mode +1)) (setup (:straight winum) (:option winum-scope 'frame-local winum-auto-setup-mode-line nil winum-ignored-buffers '(" *which-key*") winum-format " %s") (winum-mode +1)) (setup (:straight xr)) (setup (:straight-when ytdious (executable-find "mpv")) (:also-load acdw-ytel) ; so named because I used ytel first (:option ytdious-invidious-api-url "https://invidious.snopyta.org") (:hook #'hl-line-mode) (:global "C-c y" #'ytdious) (:bind "v" #'acdw/ytdious-watch "w" #'acdw/ytdious-copy-link "q" #'acdw/ytdious-quit)) (setup (:straight zzz-to-char) (:global "M-z" (defun acdw/zzz-up-to-char (prefix) "Call `zzz-up-to-char' or `zzz-to-char', PREFIX-depending." (interactive "P") (if prefix (call-interactively #'zzz-up-to-char) (call-interactively #'zzz-to-char)))))