emacs/init.el

1610 lines
54 KiB
EmacsLisp
Raw Blame History

This file contains invisible Unicode characters

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

;;; init.el -*- lexical-binding: t; coding: utf-8-unix -*-
;; Author: Case Duckworth <acdw@acdw.net>
;; Created: Sometime during Covid-19, 2020
;; Keywords: configuration
;; URL: https://tildegit.org/acdw/emacs
;; Bankruptcy: 7
;; This file is NOT part of GNU Emacs.
;;; License:
;; Everyone is permitted to do whatever with this software, without
;; limitation. This software comes without any warranty whatsoever,
;; but with two pieces of advice:
;; - Don't hurt yourself.
;; - Make good choices.
;;; Code:
;;; Setup
;;;; `setup'
(straight-use-package '(setup :host nil :repo "https://git.sr.ht/~zge/setup"))
(require 'setup)
(setup setup
(setup-define :straight
(lambda (recipe)
`(straight-use-package ',recipe))
:documentation "Install RECIPE with `straight-use-package'."
:repeatable t
:shorthand (lambda (sexp)
(let ((recipe (cadr sexp)))
(if (consp recipe)
(car recipe)
recipe)))))
;;;; `no-littering'
(setup (:straight no-littering)
(:option no-littering-etc-directory (acdw/dir)
no-littering-var-directory (acdw/dir))
(require 'no-littering))
;;;; My packages
(when-let ((default-directory
(expand-file-name-exists-p "pkg/" user-emacs-directory)))
(normal-top-level-add-subdirs-to-load-path))
;;;; Private stuff
(acdw/require-private)
;;;; Compatibility with older versions
(require 'acdw-compat)
;;;; Utility functions and variables
;; see also: `acdw' and friends. Functions here aren't big enough, or they're
;; too tightly bound to stuff here, to be placed in `acdw'.
(defvar lispy-modes '(emacs-lisp-mode
eval-expression-minibuffer
ielm-mode
lisp-mode
lisp-interaction-mode
scheme-mode
slime-repl-mode
sly-mrepl-mode)
"List of modes that are lisp-like enough to hook packages into.")
;;; Basics
;; NOTE that some of the names in `setup' forms are arbitrary.
(setup acdw
(:option user-full-name "Case Duckworth"
user-mail-address "acdw@acdw.net"))
(setup (:require auth-source)
(:option auth-sources '("~/.authinfo" "~/.authinfo.gpg")))
(setup autorevert
(global-auto-revert-mode +1))
(setup browse-url
(setq-default 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)
(defvar browse-url-mpv-arguments nil
"Arguments to pass to mpv in `browse-url-mpv'.")
(defun browse-url-mpv (url &optional new-window)
"Play `URL' in mpv."
(interactive (browse-url-interactive-arg "Video URL: "))
(ignore new-window) ;; mpv always opens a new window
(let* ((url (browse-url-encode-url url))
(process-environment (browse-url-process-environment)))
(message "Playing %s in mpv..." url)
(apply #'start-process
(concat "mpv " url) nil
"mpv"
(append
browse-url-mpv-arguments
(list url)))))
(defvar browse-url-feh-arguments '("--auto-zoom"
"--geometry" "800x600")
"Arguments to pass to feh in `browse-url-feh'.")
(defun browse-url-feh (url &optional new-window)
"Open `URL' in feh."
(interactive (browse-url-interactive-arg "Video URL: "))
(ignore new-window) ;; mpv always opens a new window
(let* ((url (browse-url-encode-url url))
(process-environment (browse-url-process-environment)))
(message "Opening %s in feh..." url)
(apply #'start-process
(concat "feh " url) nil
"feh"
(append
browse-url-feh-arguments
(list url)))))
;; `browse-url-browser-function' as an alist is deprecated in Emacs 28 for
;; `browse-url-handlers'.
(set-default (if (version<= emacs-version "28")
'browse-url-browser-function
'browse-url-handlers)
`(("\\.jpe?g\\'" . ,(if (executable-find "feh")
'browse-url-feh
'eww-browse-url))
("youtube\\.com\\|youtu\\.be" . ,(if (executable-find "mpv")
'browse-url-mpv
'eww-browse-url))
("google\\.com" . browse-url-default-browser)
("\\(twitter\\.com\\|t\\.co\\)" . acdw/eww-browse-twitter-url)
("." . eww-browse-url)))
;; Buttonize gemini:// links.
(acdw/add-button-url-regexp-protocol "gemini"))
(setup buffers
(:global "C-x k" acdw/kill-a-buffer))
(setup calendar
(:option calendar-week-start-day 1 ; Monday
))
(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 cursor
(:option cursor-type 'bar
cursor-in-non-selected-windows 'hollow
blink-cursor-blinks 1)
(blink-cursor-mode +1))
(setup cus-edit
(:option custom-file (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)
;; `Custom-mode-hook' fires /before/ the widgets are built, so I have to
;; install advice after the widgets are made.
(advice-add
'custom-buffer-create :after
(defun custom-buffer@expand-widgets (&rest _)
"Expand descriptions and values of variables in `Custom-mode' buffers."
(interactive)
;; "More/Hide" widgets (thanks alphapapa!)
(widget-map-buttons (lambda (widget _)
(pcase (widget-get widget :off)
("More" (widget-apply-action widget)))
nil))
;; "Show Value" widgets (the little triangles)
(widget-map-buttons (lambda (widget _)
(pcase (widget-get widget :off)
("Show Value" (widget-apply-action widget)))
nil))))
(add-hook ; thanks u/oantolin!
'Custom-mode-hook
(defun custom-mode@imenu ()
"Build `imenu' for `Custom-mode'."
(setq imenu-generic-expression
'(("Faces" "^\\(?:Show\\|Hide\\) \\(.*\\) face: \\[sample\\]" 1)
("Variables" "^\\(?:Show Value\\|Hide\\) \\([^:\n]*\\)" 1))))))
(setup debugger
(:hook visual-line-mode)
(:global "C-c d" toggle-debug-on-error))
(setup dired
(setq-default dired-recursive-copies 'always
dired-recursive-deletes 'always
delete-by-moving-to-trash t
dired-listing-switches "-Al"
ls-lisp-dirs-first t
dired-ls-F-marks-symlinks t
dired-no-confirm '(byte-compile
chgrp chmod chown copy
hardlink load move
shell touch symlink)
dired-dwim-target t)
(:also-load dired-x)
(:hook dired-hide-details-mode
hl-line-mode)
(:global "C-x C-j" dired-jump)
(acdw/system
(:work (:straight w32-browser)
(autoload 'dired-w32-browser "w32-browser")
(:bind "RET" dired-w32-browser))
(:home (:straight dired-open)
(require 'dired-open)
(:bind "RET" dired-find-alternate-file)
(:option (prepend dired-open-functions) #'dired-open-xdg)))
(:when-loaded
(:straight dired-subtree)
(:bind "i" dired-subtree-toggle
"TAB" dired-subtree-cycle)
(:straight dired-collapse)
(:hook dired-collapse-mode)
(:straight dired-git-info)
(:bind ")" dired-git-info-mode)
(:straight trashed)
(:option trashed-action-confirmer #'y-or-n-p)))
(setup ediff
(:option ediff-window-setup-function 'ediff-setup-windows-plain
ediff-split-window-function 'split-window-horizontally))
(setup eldoc
(:option eldoc-idle-delay 0.1
eldoc-echo-area-use-multiline-p nil))
(setup elisp-mode
(:option eval-expression-print-length nil
eval-expression-print-level nil
lisp-indent-function #'lisp-indent-function)
(add-hook 'emacs-lisp-mode-hook
(defun emacs-lisp@enforce-lexical-binding ()
(setq lexical-binding t)))
(add-hook 'emacs-lisp-mode-hook
(defun emacs-lisp@imenu-add-setup ()
(add-to-list 'imenu-generic-expression
'("Setup"
"\\(^\\s-*(setup +(?\\)\\(\\_<.+\\_>\\)" 2))))
(defun acdw/eval-region-or-buffer ()
(interactive)
(if (region-active-p)
(let ((begin (region-beginning))
(end (region-end)))
(with-message (format "Evaluating %S -> %S" begin end)
(eval-region begin end)))
(with-message "Evaluating buffer"
(eval-buffer))))
;; Emulate slime's eval binds
(:with-map emacs-lisp-mode-map
(:bind "C-c C-c" eval-defun
"C-c C-k" acdw/eval-region-or-buffer
"C-c C-z" ielm))
(add-hook 'emacs-lisp-mode-hook 'turn-on-eldoc-mode)
(add-hook 'ielm-mode-hook 'turn-on-eldoc-mode)
(setup (:straight macrostep)
(define-key emacs-lisp-mode-map (kbd "C-c e") #'macrostep-expand))
(setup (:straight eros)
(:hook-into emacs-lisp-mode))
;; Add advice to pulse evaluated regions
(define-advice eval-region (:around (fn start end &rest args) pulse-region)
(pulse-momentary-highlight-region start end)
(apply fn start end args)))
(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 erc
(:require acdw-erc)
(:also-load erc-autoaway
erc-track
erc-goodies)
(:option
erc-auto-discard-away t
erc-autoaway-idle-seconds 600
erc-autoaway-message "BRB (autoaway: %i seconds)"
erc-button-url-regexp browse-url-button-regexp
erc-common-server-suffixes '(("tilde.chat\\'" . "~")
("libera.chat\\'" . "LC"))
erc-default-server "irc.tilde.chat" ; fuck freenode. all my friends hate
; freenode.
erc-fill-function #'erc-fill-static
erc-fill-static-center 14
erc-format-nick-function #'erc-format-truncate-@nick
erc-header-line-face-method #'erc/update-header-line-show-disconnected
erc-hide-list '("NICK" "MODE" "JOIN" "NICK" "PART" "QUIT"
"324" "329" "332" "333" "353" "477")
erc-interpret-controls-p t
erc-interpret-mirc-color t
erc-join-buffer 'bury
erc-kill-buffer-on-part t
erc-kill-queries-on-quit t
erc-kill-server-buffer-on-quit t
erc-nick "acdw"
erc-nick-truncate (- erc-fill-static-center 1)
erc-prompt (lambda () (acdw-erc/prompt))
erc-prompt-for-password nil ; use ~/.authinfo
erc-rename-buffers t
erc-server erc-default-server
erc/servers
(when (boundp 'erc-autojoin-channels-alist)
(mapcar #'car erc-autojoin-channels-alist))
erc-server-coding-system '(utf-8 . utf-8)
erc-track-exclude-types (append erc-hide-list
'("AWAY"))
erc-track-exclude-server-buffer t
erc-track-position-in-mode-line 'before-modes
erc-track-visibility nil ; only the selected frame
)
;; Thanks bpalmer!
(advice-add 'show-paren-function :around
(defun show-paren@skip-in-erc (f &rest r)
"Skip `show-paren-mode' in `erc-mode'."
(unless (derived-mode-p 'erc-mode)
(apply f r))))
(:hook erc-autoaway-mode
erc-track-mode
erc-truncate-mode)
(:bind "C-c C-b" acdw-erc/erc-switch-to-buffer
"C-c C-c" nil ; def: `erc-toggle-interpret-controls'
)
(with-eval-after-load 'erc
(when (acdw/system :work)
;; IDK why, but on 28 this bit doesn't work -- which is at home.
(setup (:straight erc-hl-nicks)
(:option (append erc-modules) 'hl-nicks
erc-hl-nicks-minimum-contrast-ratio 4.5
erc-hl-nicks-color-contrast-strategy '(invert contrast))
(:hook-into erc-mode)
;; Refresh nick colors after changing modus theme
;; Ideally, there'd be a hook for /any/ time we changed theme... but
;; whatever.
(add-hook 'modus-themes-after-load-theme-hook
#'erc-hl-nicks-refresh-colors)))
;; (setup (:straight erc-image)
;; (:option (append erc-modules) 'image
;; erc-image-inline-rescale 300))
;; Rewrite `erc-quit/part-reason-default'
(defun erc-quit/part-reason-default ()
"Default quit/part message."
(format "\C-iSee You, Space Cowpokes. . .\C-i"))
(erc-update-modules)))
(setup eshell
(:also-load acdw-eshell
em-smart)
(:option eshell-aliases-file (acdw/dir "eshell/aliases" t)
eshell-directory-name (acdw/dir "eshell/" t)
eshell-kill-on-exit nil
eshell-review-quick-commands nil
eshell-smart-space-goes-to-end t
eshell-where-to-jump 'begin)
(:global "C-c s" eshell-pop-or-quit)
(defun eshell-mode@setup ()
"Set up `eshell' for use.
Most customizations must go in this function since `eshell' loads
like a dumbass."
;; Define keys
(dolist (spec '(("C-d" . eshell-quit-or-delete-char)))
(define-key eshell-mode-map (kbd (car spec)) (function (cdr spec))))
;; Fix modeline
(when (boundp 'simple-modeline--mode-line)
(setq mode-line-format '(:eval simple-modeline--mode-line)))
;; Set outline-regexp for consult-outline
(setq outline-regexp eshell-prompt-regexp))
(defun eshell-buffer-name ()
(rename-buffer (concat "*eshell*<" (eshell/pwd) ">") t))
(add-hook 'eshell-directory-change-hook #'eshell-buffer-name)
(add-hook 'eshell-prompt-load-hook #'eshell-buffer-name)
(:hook eshell-mode@setup
eshell-arg-hist-mode))
(setup eww
(:option eww-search-prefix "https://duckduckgo.com/html?q="
url-privacy-level '(email agent cookies lastloc))
(:hook acdw/reading-mode))
(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
tramp-backup-directory-alist backup-directory-alist
vc-make-backup-files t
version-control t)
(:global "C-c i" acdw/find-emacs-dotfiles)
(auto-save-visited-mode +1)
(add-hook 'unfocused-hook
(defun unfocused@save-buffers ()
(save-some-buffers t))))
(setup flyspell
(setq-default ispell-program-name "hunspell"
ispell-dictionary "en_US"
ispell-personal-dictionary "~/.hunspell_personal"
ispell-local-dictionary-alist '(("en_US"
"[[:alpha:]]" "[^[:alpha:]]"
"[']" nil
("-d" "en_US") nil utf-8)))
(when (boundp 'ispell-hunspell-dictionary-alist)
(setq-default
ispell-hunspell-dictionary-alist ispell-local-dictionary-alist))
(:needs ispell-program-name) ; don't proceed if not installed
(unless (file-exists-p ispell-personal-dictionary)
(write-region "" nil ispell-personal-dictionary nil 0))
(defun flyspell-start ()
"Start `flyspell-mode' or `flyspell-prog-mode', depending on current mode."
(interactive)
(cond ((derived-mode-p 'text-mode)
(flyspell-mode))
((derived-mode-p 'prog-mode)
(flyspell-prog-mode))
((called-interactively-p)
(message "Non-text or -prog mode. Run `flyspell-mode'."))))
(when (executable-find ispell-program-name)
(add-hook 'change-major-mode-hook #'flyspell-start))
(:when-loaded
(setup (:straight flyspell-correct)
(define-key flyspell-mode-map (kbd "C-;") #'flyspell-correct-wrapper))))
(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)
(add-hook 'unfocused-hook #'garbage-collect))
(setup gnus
(:option gnus-home-directory (acdw/dir "gnus" t)
gnus-directory (acdw/dir "News" t)
gnus-init-file (expand-file-name "gnus.el" user-emacs-directory))
(:global "C-c n" gnus))
(setup goto-addr
(add-hook 'text-mode-hook #'goto-address-mode)
(add-hook 'prog-mode-hook #'goto-address-prog-mode))
(setup ibuffer
(:option 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)))
("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)
(defun ibuffer-filter-to-default ()
(ibuffer-switch-to-saved-filter-groups "default"))
(:hook ibuffer-filter-to-default)
(:also-load ibuf-ext)
(:option ibuffer-show-empty-filter-groups nil
ibuffer-expert t))
(setup imenu
(:option imenu-auto-rescan t))
(setup Info
(:hook variable-pitch-mode
acdw/reading-mode))
(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.
(define-advice kill-line (:around (fn &rest args) join-killed-line)
(if (and (eolp)
(not (bolp)))
(delete-indentation 1)
(apply fn args))))
(setup minibuffer
(:option minibuffer-prompt-properties
'(read-only t cursor-intangible t face minibuffer-prompt)
enable-recursive-minibuffers t
file-name-shadow-properties '(invisible t intangible t)
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)
(fset 'yes-or-no-p #'y-or-n-p))
(setup page
(defun recenter-to-top (&rest _args)
"Recenter the cursor to the top of the window."
(recenter 0))
(:advise forward-page :after #'recenter-to-top
backward-page :after #'recenter-to-top))
(setup prog
(:option smie-indent-basic tab-width)
(add-hook 'prog-mode-hook
(defun prog-mode@auto-fill ()
(setq-local comment-auto-fill-only-comments t)
(turn-on-auto-fill)))
(:option show-paren-delay 0
show-paren-style 'mixed
show-paren-when-point-inside-paren t
show-paren-when-point-in-periphery t)
(defun flymake-mode-except ()
"Turn on flymake mode, except in some modes."
(let ((no-flymake-modes '(emacs-lisp-mode)))
(unless (or (member major-mode no-flymake-modes)
(apply #'derived-mode-p no-flymake-modes))
(flymake-mode-on))))
(:hook show-paren-mode
electric-pair-local-mode
flymake-mode-except
acdw/setup-fringes)
(add-hook 'after-save-hook
#'executable-make-buffer-file-executable-if-script-p))
(setup re-builder
(require 'acdw-re)
(advice-add 're-builder :before #'acdw/re-builder-save-state)
(add-hook 'reb-mode-hook #'paredit-mode)
(:global "<f2>" re-builder)
(dolist (map '(reb-mode-map reb-lisp-mode-map))
(let ((setup-map map))
(:bind "RET" reb-replace-regexp
"M-n" reb-next-match
"M-p" reb-prev-match
"C-g" reb-quit
"C-c C-k" reb-quit))))
(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))
(advice-add 'dired-rename-file :after #'rjs/recentf-rename-notify)
(recentf-mode +1))
(setup repeat
(:only-if (fboundp #'repeat-mode))
(repeat-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 saveplace
(:option save-place-file (acdw/dir "places.el")
save-place-forget-unreadable-files (acdw/system :home))
(save-place-mode +1))
(setup scratch
(:option inhibit-startup-screen t
initial-buffer-choice t
initial-scratch-message (concat ";; Howdy, "
(nth 0 (split-string
user-full-name))
"! "
"Welcome to GNU Emacs.\n\n")
initial-major-mode 'emacs-lisp-mode)
(add-hook 'kill-buffer-query-functions
(defun kill-buffer-query@immortal-scratch ()
(if (eq (current-buffer) (get-buffer "*scratch*"))
(progn (bury-buffer)
nil)
t))))
(setup scrolling
(:option auto-window-vscroll nil
fast-but-imprecise-scrolling t
scroll-margin 0
scroll-conservatively 101
scroll-preserve-screen-position 1))
(setup selection
(:option save-interprogram-paste-before-kill t
yank-pop-change-selection t
x-select-enable-clipboard t
x-select-enable-primary t
mouse-drag-copy-region t
kill-do-not-save-duplicates t)
(delete-selection-mode +1))
(setup (:require server)
(unless (server-running-p)
(server-start)))
(setup sh-mode
(:option sh-basic-offset tab-width
sh-indent-after-case 0
sh-indent-for-case-alt '+
sh-indent-for-case-label 0)
(:local-set indent-tabs-mode t)
(when (executable-find "shfmt")
(with-eval-after-load 'apheleia
(:option (append apheleia-formatters) '(shfmt . ("shfmt"))
(append apheleia-mode-alist) '(sh-mode . shfmt))))
(when (executable-find "shellcheck")
(:straight flymake-shellcheck)
(:hook flymake-mode
flymake-shellcheck-load)))
(setup shell-command
(:option shell-command-switch (acdw/system
;; I should be testing on some variable
(:home "-csi")
(:work "-c"))
shell-command-prompt-show-cwd t
shell-command-default-error-buffer "*shell-command-errors*"))
(setup shr
(:option shr-width fill-column
shr-max-width fill-column
shr-max-image-proportion 0.6
shr-image-animate t
shr-discard-aria-hidden t
shr-folding-mode t))
(setup text
(:hook turn-on-auto-fill
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
(:hook acdw-fonts/adapt-variable-pitch))
(setup view
(:option view-read-only t)
(defun acdw/read-view-mode ()
(acdw/reading-mode (if view-mode +1 -1)))
(:hook acdw/read-view-mode))
(setup whitespace
(:option whitespace-style
'(empty indentation space-before-tab space-after-tab)
indent-tabs-mode nil
tab-width 4
backward-delete-char-untabify-method 'hungry)
(:global "M-SPC" cycle-spacing))
(setup windows
(require 'acdw-bell)
(:option use-dialog-box nil
use-file-dialog nil
tab-bar-show 1
visible-bell nil
ring-bell-function (lambda ()
(acdw-bell/flash-mode-line
(acdw/system :home)))
recenter-positions '(top middle bottom))
(tooltip-mode -1)
(winner-mode +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))
;;; "Et cetera" settings
;; This should stay as /minimal/ as possible. Anything that can go somewhere
;; else /should/ go there.
(setup emacs
(:option disabled-command-function nil
kill-read-only-ok t
load-prefer-newer t
native-comp-async-report-warnings-errors nil
echo-keystrokes 0.01
attempt-stack-overflow-recovery nil
attempt-orderly-shutdown-on-fatal-signal nil
find-function-C-source-directory (acdw/find-emacs-source))
(:global "M-=" count-words
"C-w" kill-region-or-backward-word
"<help> h" nil ; HELLO takes a long time to load on Windows
"M-c" capitalize-dwim
"M-u" upcase-dwim
"M-l" downcase-dwim)
;; (when (display-graphic-p)
;; (:global "<escape>" keyboard-escape-quit))
;; Remap C-h to DEL -- <f1> can be the "help" key
;; (define-key key-translation-map [?\C-h] [?\C-?])
(:global "C-c t" acdw/insert-iso-date
"C-z" nil))
;;; Packages
(setup (:straight (0x0 :host gitlab
:repo "willvaughn/emacs-0x0"))
(:option 0x0-default-host 'ttm))
(setup (:straight (apheleia :host github
:repo "raxod502/apheleia"))
(apheleia-global-mode +1)
;; Use a dumb formatter on modes that `apheleia' doesn't work for.
(add-hook 'before-save-hook
(defun before-save@dumb-auto-format ()
(setq stupid-modes '(makefile-mode
org-mode))
;; If there's no apheleia formatter for the mode, just indent the
;; buffer.
(unless (or (apply #'derived-mode-p stupid-modes)
(and (fboundp 'apheleia--get-formatter-command)
(apheleia--get-formatter-command)))
(indent-region (point-min) (point-max))))))
(setup (:straight async)
(autoload 'dired-async-mode "dired-async.el" nil t)
(dired-async-mode +1))
(setup (:straight ace-link)
(ace-link-setup-default))
(setup (:straight avy)
(:global "C-:" avy-goto-char
"C-'" avy-goto-char-timer
"M-g f" avy-goto-line
"M-g w" avy-goto-word-1
"C-c C-j" avy-resume)
(with-eval-after-load "isearch"
(define-key isearch-mode-map (kbd "C-'") #'avy-isearch)))
(setup (:straight (beginend))
(beginend-global-mode +1))
(setup (:straight (consult
:host github
:repo "minad/consult"))
;; "Sensible" functions
(defun consult-sensible-grep (&optional arg)
"Perform `consult-git-grep' if in a git project, otherwise `consult-ripgrep'
if ripgrep is installed, otherwise `consult-grep'."
(interactive "P")
(cond ((executable-find "rg")
(call-interactively #'consult-ripgrep))
((string-equal (vc-backend buffer-file-name) "Git")
(call-interactively #'consult-git-grep))
(t (call-interactively #'consult-grep))))
(defun consult-sensible-find (&optional arg)
"Peform `consult-locate' if locate is installed, otehrwise `consult-find'."
(interactive "P")
(cond ((executable-find "locate") (call-interactively #'consult-locate))
(t (call-interactively #'consult-find))))
;; Orderless Regexp Compiler! -- from Consult Wiki
(defun consult--orderless-regexp-compiler (input type)
(setq input (orderless-pattern-compiler input))
(cons
(mapcar (lambda (r) (consult--convert-regexp r type)) input)
(lambda (str) (orderless--highlight input str))))
(setq consult--regexp-compiler #'consult--orderless-regexp-compiler)
;; 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" consult-sensible-grep
"M-s f" consult-sensible-find
"M-s l" consult-line
"M-s m" consult-multi-occur
"M-s k" consult-keep-lines
"M-s u" consult-focus-lines
;; Other bindings
"M-y" consult-yank-pop
"<help> a" consult-apropos
;; Isearch integration
"M-s e" consult-isearch)
(:with-map isearch-mode-map
(:bind "M-e" consult-isearch
"M-s e" consult-isearch
"M-s l" consult-line))
;; Registers
(autoload 'consult-register-preview "consult")
(:option register-preview-delay 0
register-preview-function #'consult-register-format)
(:advise register-preview :override #'consult-register-window)
;; Xref
(:option xref-show-xrefs-function #'consult-xref
xref-show-definitions-function #'consult-xref)
;; Projects
(:option consult-project-root-function #'vc-root-dir)
;; Competion-at-point (complete-region)
(:global "M-/" completion-at-point)
(:option completion-in-region-function
(lambda (&rest args)
(apply (if vertico-mode
#'consult-completion-in-region
#'completion--in-region)
args))
completion-cycle-threshold 3
tab-always-indent 'complete)
;; Completing-read-multple
(if (fboundp #'consult-completing-read-multiple)
(:advise completing-read-multple
:override #'consult-completing-read-multiple)
;; else
(defun crm-indicator (args)
(cons (concat "[CRM] " (car args)) (cdr args)))
(:advise completing-read-multiple
:filter-args #'crm-indicator))
(with-eval-after-load 'vertico
(with-eval-after-load 'consult
(when (boundp 'consult-crm-map)
(define-key consult-crm-map "\r" #'+vertico-crm-exit)
(define-key consult-crm-map "\t" #'vertico-exit)
(defun +vertico-crm-exit ()
(interactive)
(run-at-time 0 nil #'vertico-exit)
(funcall #'vertico-exit))))))
(setup (:straight crux)
(:global "M-`" crux-other-window-or-switch-buffer
"C-o" crux-smart-open-line
"M-o" crux-smart-open-line-above
"C-M-\\" crux-cleanup-buffer-or-region
"C-x 4 t" crux-transpose-windows)
(crux-reopen-as-root-mode +1))
;; requires extension:
;; https://addons.mozilla.org/en-US/firefox/addon/edit-with-emacs1/
(setup (:straight edit-server)
(when (and (daemonp)
(require 'edit-server nil :noerror))
(edit-server-start)
(add-hook 'edit-server-done-hook #'unfill-buffer)))
(setup (:straight (electric-cursor
:host github
:repo "duckwork/electric-cursor"))
(electric-cursor-mode +1))
(setup (:straight (elpher :host nil
:repo "git://thelambdalab.xyz/elpher.git"))
(:option elpher-ipv4-always t
elpher-certificate-directory (acdw/dir "elpher/")
elpher-gemini-max-fill-width fill-column)
(:bind "n" elpher-next-link
"p" elpher-prev-link
"o" elpher-follow-current-link
"G" elpher-go-current)
(:hook acdw/reading-mode)
(autoload 'elpher-bookmarks "elpher" nil t)
(autoload 'elpher-go "elpher" nil 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))))
(:when-loaded
(setup (:straight (gemini-write
:host nil
:repo "https://alexschroeder.ch/cgit/gemini-write"
:branch "main"))
(require 'gemini-write))))
(setup (:straight embark)
(:global "C-." embark-act)
(:option prefix-help-command #'embark-prefix-help-command
(append display-buffer-alist)
'("\\`\\*Embark Collect \\(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)))
(setq embark-action-indicator
(lambda (map _target)
(which-key--show-keymap "Embark" map nil nil 'no-paging)
#'which-key--hide-popup-ignore-command)
embark-become-indicator embark-action-indicator)
(with-eval-after-load 'embark
(with-eval-after-load 'consult
(setup (:straight embark-consult)
(add-hook 'embark-collect-mode-hook
#'consult-preview-at-point-mode)))))
(setup (:straight epithet)
(add-hook 'Info-selection-hook #'epithet-rename-buffer)
(add-hook 'eww-after-render-hook #'epithet-rename-buffer)
(add-hook 'help-mode-hook #'epithet-rename-buffer)
(add-hook 'occur-mode-hook #'epithet-rename-buffer))
(setup (:straight eradio)
(:needs "mpv")
(:option
eradio-player '("mpv" "--no-video" "--no-terminal")
eradio-channels `(("KLSU" .
"http://130.39.238.143:8010/stream.mp3")
("Soma FM Synphaera" .
"https://somafm.com/synphaera256.pls")
("SomaFM BAGel Radio" .
"https://somafm.com/bagel.pls")
("SomaFM Boot Liquor" .
"https://somafm.com/bootliquor320.pls")
("SomaFM Deep Space One" .
"https://somafm.com/deepspaceone.pls")
("SomaFM Fluid" .
"https://somafm.com/fluid.pls")
("SomaFM Underground 80s" .
"https://somafm.com/u80s256.pls")
("WBRH: Jazz & More" .
"http://wbrh.streamguys1.com/wbrh-mp3")
("KBRH Blues & Rhythm Hits" .
"http://wbrh.streamguys1.com/kbrh-mp3")
("WRKF HD-2" .
,(concat "https://playerservices.streamtheworld.com/"
"api/livestream-redirect/WRKFHD2.mp3"))
("WRKF: NPR for the Capital Region" .
,(concat "https://playerservices.streamtheworld.com/"
"api/livestream-redirect/WRKFFM.mp3"))
("BadRadio: 24/7 PHONK" .
"https://s2.radio.co/s2b2b68744/listen")
("tilderadio" .
"https://radio.tildeverse.org/radio/8000/radio.ogg")
("vantaradio" .
"https://vantaa.black/radio")))
(:global "C-c r r" eradio-play ; mnemonic: radio
"C-c r s" eradio-stop ; mnemonic: stop
"C-c r p" eradio-toggle ; mnemonic: play/pause
))
(setup (:straight expand-region)
(:global "C-=" er/expand-region))
(setup (:straight fennel-mode)
(:needs "fennel")
(autoload 'fennel-repl "fennel-mode" nil t)
(:file-match "\\.fnl\\'"))
(setup (:straight form-feed)
(global-form-feed-mode +1))
(setup (:straight geiser))
(setup (:straight (gemini-mode
:host nil
:repo "https://git.carcosa.net/jmcbray/gemini.el.git"))
(:file-match "\\.\\(gemini\\|gmi\\)\\'")
(:hook turn-off-auto-fill))
(setup gforth
(when (locate-library "gforth")
(autoload 'forth-mode "gforth")
(add-to-list 'auto-mode-alist '("\\.fs\\'" . forth-mode))
(autoload 'forth-block-mode "gforth")
(add-to-list 'auto-mode-alist '("\\.fb\\'" . forth-block-mode))))
(setup (:straight helpful)
(:global "<help> f" helpful-callable
"<help> v" helpful-variable
"<help> k" helpful-key
"<help> o" helpful-symbol
"C-c C-d" helpful-at-point))
(setup (:straight iscroll)
(:hook-into text-mode))
(setup (:straight lacarte)
(:global "<f10>" lacarte-execute-menu-command))
(setup (:straight ledger-mode)
(:needs "ledger"))
(setup (:straight lua-mode)
(:file-match "\\.lua\\'"))
(setup (:straight magit)
(:global "C-c g" magit-status)
(defun magit-display-buffer-same-window (buffer)
"Display BUFFER in the selected window like God intended."
(display-buffer buffer '(display-buffer-same-window)))
(:option magit-display-buffer-function #'magit-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 (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")))
(setup (:straight (modus-themes
:host gitlab
:repo "protesilaos/modus-themes"))
(:option modus-themes-slanted-constructs t
modus-themes-bold-constructs t
modus-themes-region 'bg-only
modus-themes-org-blocks 'grayscale
modus-themes-headings '((1 . section)
(t . no-color))
modus-themes-mode-line nil)
(acdw/sunrise-sunset #'modus-themes-load-operandi
#'modus-themes-load-vivendi))
(setup (:straight mwim)
(:global "C-a" mwim-beginning
"C-e" mwim-end))
(setup (:straight nov)
(:option nov-text-width fill-column)
(:file-match "\\.epub\\'"))
(setup (:straight package-lint))
(setup (:straight package-lint-flymake))
(setup (:straight olivetti)
(:option olivetti-body-width (+ fill-column 4)
olivetti-minimum-body-width fill-column)
(add-hook 'olivetti-mode-hook
(defun acdw/olivetti-mode-hook ()
(if olivetti-mode
(setq-local indicate-empty-lines nil
indicate-buffer-boundaries nil)
(acdw/setup-fringes)))))
(setup (:straight (orderless
:host github
:repo "oantolin/orderless"))
(require 'orderless)
(:option (append completion-styles) 'orderless
orderless-component-separator #'orderless-escapable-split-on-space
orderless-style-dispatchers '(acdw/orderless-dispatch))
(defun fix-dollar (args)
(if (string-suffix-p "$" (car args))
(list (concat (substring (car args) 0 -1) "[\x100000-\x10FFFD]*$"))
args))
(advice-add #'orderless-regexp :filter-args #'fix-dollar)
(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)
(:straight org-contrib)
(require 'acdw-org) ; so I don't clutter up init.el
(:option org-adapt-indentation nil
org-catch-invisible-edits 'smart
org-clock-clocked-in-display 'mode-line
org-clock-string-limit 7 ; gives time and not title
org-confirm-babel-evaluate nil
org-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-imenu-depth 3
org-outline-path-complete-in-steps nil
org-pretty-entities t
org-refile-use-outline-path 'file
org-special-ctrl-a/e t
org-special-ctrl-k t
org-src-fontify-natively t
org-src-tab-acts-natively t
org-src-window-setup 'current-window
org-startup-truncated nil
org-tags-column 0 ; (- 0 fill-column -3)
org-directory "~/org")
(:bind "RET" acdw-org/return-dwim
"<S-return>" acdw-org/org-table-copy-down
"M-SPC M-SPC" insert-zero-width-space
"C-c C-l" org-insert-link-dwim
"M-w" acdw/copy-region-plain)
(with-eval-after-load 'org-export
(add-to-list 'org-export-filter-final-output-functions
#'org-export-remove-zero-width-spaces))
(defun acdw/org-fix-lines-before-save ()
(add-hook 'before-save-hook #'acdw-org/fix-blank-lines-in-buffer 0 :local))
(:hook variable-pitch-mode
acdw/org-fix-lines-before-save)
(advice-add 'org-delete-backward-char
:override #'acdw-org/delete-backward-char))
(setup (:straight paredit)
;; I don't use paredit-splice-sexp much, and it stomps on isearch.
(:unbind "M-s")
(defun setup-paredit-mode ()
"Correct weirdnesses and set up paredit mode."
(paredit-mode +1)
(let ((map lisp-mode-shared-map))
(define-key map (kbd "DEL") #'paredit-backward-delete)
(define-key map (kbd "C-M-;") #'comment-or-uncomment-sexp)))
(dolist (mode lispy-modes)
(add-hook (intern (concat (symbol-name mode) "-hook"))
#'setup-paredit-mode))
(require 'eldoc)
(eldoc-add-command 'paredit-backward-delete 'paredit-close-round))
(setup (:straight paren-face)
(dolist (mode lispy-modes)
(add-hook (intern (concat (symbol-name mode) "-hook")) #'paren-face-mode)))
(setup (:straight restart-emacs)
(defun emacs-upgrade (&optional update-packages)
"Pull config, upgrade packages, restart Emacs."
(interactive "P")
(when update-packages
(straight-pull-all))
(emacs-git-pull-config)
(restart-emacs)))
(setup (:straight simple-modeline)
(setup (:straight minions))
(require 'acdw-modeline)
(:option
simple-modeline-segments '((acdw-modeline/modified
acdw-modeline/buffer-name
acdw-modeline/vc-branch
acdw-modeline/position)
(simple-modeline-segment-misc-info
acdw-modeline/erc
acdw-modeline/wc
acdw-modeline/text-scale
simple-modeline-segment-process
acdw-modeline/god-mode-indicator
acdw-modeline/winum
acdw-modeline/minions
acdw-modeline/narrowed
simple-modeline-segment-major-mode)))
;; I've put in a pull request to add the (- 0 right-margin) bit here.
(advice-add 'simple-modeline--format :override
(defun simple-modeline@format (lefts rights)
(let* ((left (simple-modeline--format-segments lefts))
(right (simple-modeline--format-segments rights))
(reserve (length right)))
(concat
left
(propertize " "
'display `((space :align-to
(- right
(- 0 right-margin)
,reserve)))
'face '(:inherit simple-modeline-space))
right))))
(simple-modeline-mode +1))
(setup (:straight sly)
(defvar acdw/lisp-bin (or (executable-find "sbcl")
(executable-find "clisp")
""))
(:needs acdw/lisp-bin)
(:option inferior-lisp-program acdw/lisp-bin
sly-kill-without-query-p t)
(:also-load sly-autoloads)
(setup (:straight clhs))
(:with-feature sly-mrepl
(dolist (key '("RET" "<return>"))
(:bind key sly-mrepl-return-at-end))
(:bind "C-c C-c" sly-mrepl-return))
(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)))))
(setup (:straight ssh-config-mode)
(dolist (spec '(("/\\.ssh/config\\'" . ssh-config-mode)
("/sshd?_config\\'" . ssh-config-mode)
("/knownhosts\\'" . ssh-known-hosts-mode)
("/authorized_keys2?\\'" . ssh-authorized-keys-mode)))
(add-to-list 'auto-mode-alist spec))
(add-hook 'ssh-config-mode-hook #'turn-on-font-lock))
(setup (:straight undo-fu)
(:global "C-/" undo-fu-only-undo
"C-?" undo-fu-only-redo))
(setup (:straight undo-fu-session)
(:option undo-fu-session-incompatible-files '("/COMMIT_EDITMSG\\'"
"/git-rebase-todo\\'")
undo-fu-session-directory (acdw/dir "undo/" t)
undo-fu-session-compression (acdw/system :home))
(global-undo-fu-session-mode +1))
(setup (:straight unfill))
(setup (:straight (unfocused
:host github
:repo "duckwork/unfocused"))
(unfocused-mode +1))
(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)
(if (boundp 'comp-deferred-compilation-deny-list)
(add-to-list 'comp-deferred-compilation-deny-list "vertico"))
(vertico-mode +1)
;; Extensions!
(:also-load vertico-mouse)
(vertico-mouse-mode +1)
;; Prefix the current candidate with "> ". From Vertico wiki.
(defun vertico-format@add-arrow (orig cand prefix suffix index _start)
(setq cand (funcall orig cand prefix suffix index _start))
(concat
(if (= vertico--index index)
(propertize "> " 'face 'vertico-current)
" ")
cand))
(advice-add #'vertico--format-candidate :around #'vertico-format@add-arrow))
(setup (:straight wc-mode) ; TODO: move some of this stuff around
(:option wc-modeline-format "[%tww]"
wc-idle-wait 0)
(:hook-into text-mode)
(add-hook 'org-mode-hook
(defun org-mode@wc-stupid ()
(setq-local
wc-count-words-function
(lambda (start end) "Count words stupidly with a limit."
(acdw-org/count-words-stupidly start
end
999)))))
(defun acdw-modeline/wc ()
"Display current `wc-buffer-stats'."
(when (bound-and-true-p wc-mode)
(or wc-buffer-stats "[w]"))))
(setup (:straight web-mode)
(:option css-level-offset 2
js-indent-level 2
sgml-indent-offset 2)
(:file-match "\\.\\(p\\|dj\\)?html\\'"
"\\.html?\\'"
"\\.\\(tpl\\.\\)?php\\'"
"\\.[agj]sp\\'"
"\\.as[cp]x\\'"
"\\.erb\\'"
"\\.mustache\\'"))
(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-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")
(when-unfocused winum-map-keys
(defvar winum--keys-mapped nil
"Whether `winum' keys have been mapped already.")
(when (and (not winum--keys-mapped)
(display-graphic-p))
(:with-map winum-keymap
(:bind "M-0" winum-select-window-0-or-10
"M-1" winum-select-window-1
"M-2" winum-select-window-2
"M-3" winum-select-window-3
"M-4" winum-select-window-4
"M-5" winum-select-window-5
"M-6" winum-select-window-6
"M-7" winum-select-window-7
"M-8" winum-select-window-8
"M-9" winum-select-window-9))
(setq winum--keys-mapped t)))
(winum-mode +1))
(setup (:straight xr))
(setup (:straight zzz-to-char)
(defun acdw/zzz-up-to-char (prefix)
"Call `zzz-up-to-char', unless issued a PREFIX, in which case
call `zzz-to-char'."
(interactive "P")
(if prefix
(call-interactively #'zzz-to-char)
(call-interactively #'zzz-up-to-char)))
(:global "M-z" acdw/zzz-up-to-char))
;;; System-dependent
;;;; Home
(when (acdw/system :home)
(setup (:straight pkgbuild-mode))
(setup (:straight (pdf-tools
:host github
:repo "vedang/pdf-tools"))
(add-to-list 'auto-mode-alist '("\\.pdf\\'" . pdf-view-mode))
(pdf-loader-install))
(setup (:straight vterm)))
;;;; Work
(when (acdw/system :work)
(setup (:straight ahk-mode)))
;;; init.el ends here