emacs/init.el

863 lines
28 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: 6
;; 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' -- configuration macro
(straight-use-package '(setup :host nil
:repo "https://git.sr.ht/~zge/setup"))
(require 'setup)
;;;; shorthand for `customize-set-variable' (via setup)
(defmacro setc (&rest args)
"Customize user options using ARGS like `setq'."
(declare (debug setq))
`(setup (:option ,@args)))
;;;; Install packages with `straight-use-package'
(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))))
;;;; Set options using `setq-default', instead of `customize-set-variable'
;; From what I can tell, `customize-set-variable' loads "all the dependencies
;; for each SYMBOL it sets (see `custom-load-symbol'). Since I don't want to do
;; that all the time, here's `:set'. DON'T USE THIS HARDLY EVER. Honestly, I
;; might want to do a `:option-after' instead (with `:after-loaded' set to t)...
(setup-define :setq-default
(lambda (variable value)
`(setq-default ,variable ,value))
:documentation "Set options with `setq-default'. USE SPARINGLY!"
:debug '(sexp form)
:repeatable t)
;;;; Bind keys to `acdw/map'
(setup-define :acdw/map
(lambda (key command)
`(progn
(autoload #',command (symbol-name setup-name))
(define-key acdw/map
,(if (stringp key) (kbd key) key)
#',command)))
:documentation "Bind KEY to COMMAND in `acdw/map'."
:debug '(form sexp)
:repeatable t)
;;;; Bind keys to `acdw/leader'
(setup-define :acdw/leader
(lambda (key command)
`(progn
(autoload #',command (symbol-name setup-name))
(define-key acdw/leader
,(if (stringp key) (kbd key) key)
#',command)))
:documentation "Bind KEY to COMMAND in `acdw/leader' map."
:debug '(form sexp)
:repeatable t)
;;;; Bind keys, and autoload the functions they're bound to.
(setup-define :bind-autoload
(lambda (key command)
`(progn
(autoload #',command (symbol-name setup-name))
(define-key (symbol-value setup-map)
,(if (stringp key) (kbd key) key)
#',command)))
:documentation "Bind KEY to COMMAND, and autload COMMAND from FEATURE."
:debug '(form sexp)
:repeatable t)
;;; Good defaults
;;;; About me
(setc user-full-name "Case Duckworth"
user-mail-address "acdw@acdw.net"
calendar-location-name "Baton Rouge, LA"
calendar-latitude 30.4
calendar-longitude -91.1)
;;;; Lines
(setc fill-column 80
word-wrap t
truncate-lines nil)
(add-hook 'text-mode-hook #'turn-on-auto-fill)
(global-display-fill-column-indicator-mode +1)
(global-so-long-mode +1)
;; Only fill comments in prog-mode.
(defun-with-hooks '(prog-mode)
(defun hook--auto-fill-prog-mode ()
(setq-local comment-auto-fill-only-comments t)
(turn-on-auto-fill)))
;;;; Whitespace
(setc whitespace-style
'(empty indentation space-before-tab space-after-tab)
indent-tabs-mode nil
tab-width 4
smie-indent-basic tab-width)
(add-hook 'before-save-hook #'whitespace-cleanup)
;;;; Pairs
(setc show-paren-delay 0
show-paren-style 'mixed
show-paren-when-point-inside-paren t
show-paren-when-point-in-periphery t)
(add-hook 'prog-mode-hook #'electric-pair-local-mode)
;;;; Killing and yanking
(setc 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)
;;;; Encoding
(setc local-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
org-export-coding-system 'utf-8-unix
org-html-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)
(set-selection-coding-system 'utf-8-unix)
;;;; Uniquify
(setup (:require uniquify)
(:option uniquify-buffer-name-style 'forward
uniquify-separator path-separator
uniquify-after-kill-buffer-p t
uniquify-ignore-buffers-re "^\\*"))
;;;; Files
(setc backup-directory-alist `((".*" . ,(acdw/in-dir "backup/" t)))
tramp-backup-directory-alist backup-directory-alist
auto-save-file-name-transforms `((".*" ,(acdw/in-dir "auto-save/" t) t))
auto-save-list-file-prefix (acdw/in-dir "auto-save-list/.saves-" t)
backup-by-copying t
delete-old-versions t
version-control t
vc-make-backup-files t)
;;;;; Auto-save files
(auto-save-visited-mode +1)
;; And /actually/ save all buffers when unfocused
(add-function :after after-focus-change-function
(defun hook--auto-save-when-unfocused ()
"Save all buffers when out of focus."
(acdw/when-unfocused #'save-some-buffers t)))
;;;;; Autorevert files
(setup (:require autorevert)
(global-auto-revert-mode +1))
;;;;; Save place in files
(setup (:require saveplace)
(:option save-place-file (acdw/in-dir "places.el")
save-place-forget-unreadable-files (eq acdw/system :home))
(save-place-mode +1))
;;;;; Keep track of recent files
(setup (:require recentf)
(:option recentf-save-file (acdw/in-dir "recentf.el")
recentf-max-menu-items 100
recentf-max-saved-items nil
recentf-auto-cleanup 60
(append recentf-exclude) acdw/dir)
(recentf-mode +1))
;;;; Minibuffer
(setc 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)
(minibuffer-depth-indicate-mode +1)
(file-name-shadow-mode +1)
(fset 'yes-or-no-p #'y-or-n-p)
;;;;; Save minibuffer command history (and others)
(setup (:require savehist)
(:option (append savehist-additional-variables) 'kill-ring
(append savehist-additional-variables) 'search-ring
(append savehist-additional-variables) 'regexp-search-ring
history-length t
history-delete-duplicates t
savehist-autosave-interval 6
savehist-file (acdw/in-dir "savehist.el"))
(savehist-mode +1))
;;;;; Completion framework
(setup (:require icomplete)
(:option completion-ignore-case t
read-buffer-completion-ignore-case t
icomplete-delay-completions-threshold 0
icomplete-max-delay-chars 0
icomplete-compute-delay 0
icomplete-show-matches-on-no-input t
icomplete-with-buffer-completion-tables t
icomplete-in-buffer t
completion-styles '(partial-completion substring flex))
(fido-mode -1)
(icomplete-mode +1))
;;;;; `imenu'
(setup imenu
(:option imenu-auto-rescan t))
;;;; Cursor
(setc cursor-type 'bar
cursor-in-non-selected-windows 'hollow)
;;;; Scrolling
(setc auto-window-vscroll nil
fast-but-imprecise-scrolling t
scroll-margin 0
scroll-conservatively 101
scroll-preserve-screen-position 1)
;;;; Fonts
;; Load them /after/ the first frame comes into focus
(add-function :after after-focus-change-function
(defun hook--setup-fonts ()
(dolist (face '(default fixed-pitch))
;; `default' and `fixed-pitch' should be the same.
(set-face-attribute face nil
:font (pcase acdw/system
(:home "DejaVu Sans Mono-10")
(:work "Consolas-10")
(:other "monospace-10"))))
;; `variable-pitch' is, of course, different.
(set-face-attribute 'variable-pitch nil
:font (pcase acdw/system
(:home "DejaVu Sans")
(:work "Calibri-11")
(:other "sans-serif")))
(remove-function after-focus-change-function
'hook--setup-fonts)))
;;;; Dired
(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)
(:bind "RET" dired-find-alternate-file))
;;;; Web browsing
(setup browse-url
(:setq-default browse-url-browser-function 'eww-browse-url
browse-url-secondary-browser-function
(if (executable-find "firefox")
'browse-url-firefox
'browse-url-default-browser)
browse-url-new-window-flag t
browse-url-firefox-new-window-is-tab t)
(when (eq acdw/system :work)
(add-to-list 'exec-path "C:/Program Files/Mozilla Firefox")))
(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))
(setup eww
(:hook acdw/reading-mode))
(add-hook 'text-mode-hook #'goto-address-mode)
(add-hook 'prog-mode-hook #'goto-address-prog-mode)
;;;; Eshell
(setup eshell
(defun eshell-quit-or-delete-char (arg)
"Delete the character to the right, or quit eshell on an empty line."
(interactive "p")
(if (and (eolp) (looking-back eshell-prompt-regexp))
(eshell-life-is-too-much)
(delete-forward-char arg)))
(:option eshell-directory-name (acdw/in-dir "eshell/" t)
eshell-aliases-file (acdw/in-dir "eshell/aliases" t))
(add-hook 'eshell-mode-hook
(defun hook--eshell-setup ()
"Stuff to do after eshell is done setting up."
(define-key eshell-mode-map (kbd "C-d")
#'eshell-quit-or-delete-char)
(setq mode-line-format '(:eval simple-modeline--mode-line)))))
;;;; Ediff
(setup ediff
(:option ediff-window-setup-function 'ediff-setup-windows-plain
ediff-split-window-function 'split-window-horizontally))
;;;; Debugger
(setup debugger
(:hook visual-line-mode)
(:acdw/leader "d" toggle-debug-on-error))
;;;; Garbage collection
(add-hook 'minibuffer-setup-hook #'acdw/gc-disable)
(add-hook 'minibuffer-exit-hook #'acdw/gc-enable)
(add-function :after after-focus-change-function
(defun hook--gc-when-unfocused ()
(acdw/when-unfocused #'garbage-collect)))
;;;; Help
;; (define-key acdw/map (kbd "C-h") 'ehelp-command)
;; ;; for some reason, `ehelp.el' doesn't include a version for `describe-symbol'.
;; (defun electric-describe-symbol ()
;; (interactive)
;; (electric-helpify 'describe-symbol))
;; (define-key ehelp-map "o" #'electric-describe-symbol)
;;;; Spelling
(setup flyspell
(setenv "LANG" "en_US")
(:option ispell-program-name "hunspell"
ispell-dictionary "en_US"
ispell-personal-dictionary "~/.hunspell_personal")
(ispell-set-spellchecker-params)
(unless (file-exists-p ispell-personal-dictionary)
(write-region "" nil ispell-personal-dictionary nil 0))
;; add hooks
(add-hook 'text-mode-hook #'flyspell-mode)
(add-hook 'prog-mode-hook #'flyspell-prog-mode))
;;;; MS Windows
(when (eq acdw/system :work)
(setc 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))
;;;; Set up non-special modes
;; Great idea from brause.cc
(defun-with-hooks '(text-mode-hook prog-mode-hook diff-mode-hook)
(defun hook--setup-regular-modes ()
(setq indicate-empty-lines t
indicate-buffer-boundaries '((top . right) (bottom . right)))
(goto-address-mode +1)
(show-paren-mode +1)))
;;;; Etc. good defaults
(setc custom-file (acdw/in-dir "custom.el")
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")
default-directory (expand-file-name "~/")
disabled-command-function nil
load-prefer-newer t
comp-async-report-warnings-errors nil
frame-title-format '("%b %+%* GNU Emacs"
(:eval (when (frame-parameter nil 'client)
" Client")))
tab-bar-show 1
use-dialog-box nil
use-file-dialog nil
echo-keystrokes 0.25
recenter-positions '(top middle bottom)
attempt-stack-overflow-recovery nil
attempt-orderly-shutdown-on-fatal-signal nil
find-function-C-source-directory
(pcase acdw/system
(:work (expand-file-name (concat "~/src/emacs-"
emacs-version
"/src")))
(:home (expand-file-name "~/src/pkg/emacs/src/emacs-git/src"))
(:other nil)))
;;;; Etc. bindings
(global-set-key [remap just-one-space] #'cycle-spacing)
(global-set-key (kbd "M-/") #'hippie-expand)
(global-set-key (kbd "M-=") #'count-words)
(global-set-key (kbd "C-x C-b") #'ibuffer)
;;; Packages
;;;; Interactivity
;;;;; Begin-end
(setup (:straight beginend)
(beginend-global-mode +1))
;;;;; MWIM
(setup (:straight mwim)
(:global "C-a" mwim-beginning
"C-e" mwim-end))
;;;;; Expand-region
(setup (:straight expand-region)
(:global "C-=" er/expand-region))
;;;;; CRUX
(setup (:straight crux)
(:global "M-o" crux-other-window-or-switch-buffer
"C-k" crux-kill-and-join-forward
"C-o" crux-smart-open-line-above
"C-S-o" crux-smart-open-line
"C-M-\\" crux-cleanup-buffer-or-region
"C-c i" crux-find-user-init-file)
(crux-reopen-as-root-mode +1))
;;;;; AVY ... & friends
(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)
(eval-after-load "isearch"
'(define-key isearch-mode-map (kbd "C-'") #'avy-isearch)))
;;;;; zzz-to-char
(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))
;;;;; anzu
(setup (:straight anzu)
(:option anzu-replace-to-string-separator ""
anzu-cons-mode-line-p nil)
(add-to-list 'mode-line-misc-info '(:eval (anzu--update-mode-line)))
(:global [remap query-replace] anzu-query-replace
[remap query-replace-regexp] anzu-query-replace-regexp)
(:with-map isearch-mode-map
(:bind [remap isearch-query-replace] anzu-isearch-query-replace
[remap isearch-query-replace-regexp]
anzu-isearch-query-replace-regexp))
(global-anzu-mode +1))
;;;;; smart hungry delete
(setup (:straight smart-hungry-delete)
(:global "<backspace>" smart-hungry-delete-backward-char
"C-d" smart-hungry-delete-forward-char)
(smart-hungry-delete-add-default-hooks))
;;;; Functionality
;;;;; Async
(setup (:straight async)
(autoload 'dired-async-mode "dired-async.el" nil t)
(dired-async-mode +1))
;;;;; Undo-fu
(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/in-dir "undo/" t)
undo-fu-session-compression (eq acdw/system :home))
(global-undo-fu-session-mode +1))
;;;; Minibuffer
;;;;; Icomplete-vertical
(setup (:straight icomplete-vertical)
(let ((map icomplete-minibuffer-map))
(let ((command #'icomplete-forward-completions))
(define-key map (kbd "<down>") command)
(define-key map (kbd "C-n") command))
(let ((command #'icomplete-backward-completions))
(define-key map (kbd "<up>") command)
(define-key map (kbd "C-p") command))
(define-key map (kbd "RET") #'icomplete-force-complete-and-exit)
(define-key map (kbd "C-RET") #'minibuffer-complete-and-exit))
(icomplete-vertical-mode +1))
;;;;; Orderless
(setup (:straight orderless)
(:option (prepend completion-styles) 'orderless))
;;;;; Consult
(setup (:straight consult)
;; "Sensible" functions
(defun consult-sensible-grep ()
"Perform `consult-git-grep' if in a git project, otherwise `consult-ripgrep'
if ripgrep is installed, otherwise `consult-grep'."
(interactive "P")
(cond ((= (vc-backend buffer-file-name) "Git")
(call-interactively #'consult-git-grep))
((exectuable-find "rg")
(call-interactively #'consult-ripgrep))
(t (call-interactively #'consult-grep))))
(defun consult-sensible-find ()
"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))))
;; Bindings
(:global
;; C-c bindings (`mode-specific-map')
"C-c h" consult-history
"C-c m" consult-mode-command
"C-c b" consult-bookmark
"C-c k" consult-kmacro
;; C-x bindings (`ctl-x-map')
"C-x M-:" consult-complex-command
"C-x b" consult-buffer
"C-x 4 b" consult-buffer-other-window
"C-x 5 b" consult-buffer-other-frame
;; Custom M-# bindings for fast register access
"M-#" consult-register-load
"M-'" consult-register-store
"C-M-#" consult-register
;; 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))
;;;;; Marginalia
(setup (:straight marginalia)
(:option marginalia-annotators '(marginalia-annotators-heavy
marginalia-annotators-light))
(marginalia-mode +1))
;;;; UI
;;;;; Modus themes
(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))
;;;;; Mode line
(setup (:straight simple-modeline)
(setup (:straight minions))
(:option simple-modeline-segments
'((acdw-modeline/modified
acdw-modeline/buffer-name
acdw-modeline/vc-branch
simple-modeline-segment-position
simple-modeline-segment-word-count)
(simple-modeline-segment-misc-info
simple-modeline-segment-process
acdw-modeline/god-mode-indicator
acdw-modeline/minions
simple-modeline-segment-major-mode)))
(require 'acdw-modeline)
(simple-modeline-mode +1))
;;;;; Olivetti
;; also useful for `acdw/reading-mode'
(setup (:straight olivetti)
(:option olivetti-body-width (+ fill-column 4)
olivetti-minimum-body-width fill-column))
;;;;; Outshine
(setup (:straight outshine)
(:option outline-minor-mode-prefix "")
(:hook-into emacs-lisp-mode))
;;;;; Which-key
(setup (:straight which-key)
(:option which-key-show-early-on-C-h t
which-key-idle-delay 10000
which-key-idle-secondary-delay 0.05)
(which-key-setup-side-window-bottom)
(which-key-mode +1))
;;;;; Helpful
(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))
;;;; Utilities
;;;;; 0x0 -- upload files to a nullpointer
(setup (:straight (0x0 :host nil
:repo "https://git.sr.ht/~zge/nullpointer-emacs"))
(:option 0x0-default-host 'ttm))
;;;;; Flyspell-correct
(when (executable-find ispell-program-name)
(with-eval-after-load 'flyspell
(setup (:straight flyspell-correct)
(define-key flyspell-mode-map (kbd "C-;") #'flyspell-correct-wrapper))))
;;;; System tie-ins
;; Insctead of using `setup''s `:needs', I'm going to wrap these in
;; `exectuable-find' forms. I don't want to waste time with pulling packages
;; that won't work on a machine.
;;;;; PKGBUILDs
(when (eq acdw/system :home)
(setup (:straight pkgbuild-mode)))
;;;; Applications
;;;;; Magit
(setup (:straight magit)
(:acdw/leader "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))))
;;;;; File browsing
(setup (:straight dired-subtree)
(define-key dired-mode-map "i" #'dired-subtree-toggle))
(setup (:straight dired-collapse)
(:hook-into dired-mode))
(setup (:straight trashed)
(:option trashed-action-confirmer 'y-or-n-p))
;;;;; Gemini/gopher browsing
(setup (:straight (elpher :host nil
:repo "git://thelambdalab.xyz/elpher.git"))
(:option elpher-ipv4-always t
elpher-certificate-directory (acdw/in-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)
;; Make `eww' gemini/gopher aware.
;; From Emacswiki.
(defun elpher:eww-browse-url (original url &optional new-window)
"Handle gemini and gopher links."
(cond ((string-match-p "\\`\\(gemini\\|gopher\\)://" url)
(require 'elpher)
(elpher-go url))
(t (funcall original url new-window))))
(advice-add 'eww-browse-url :around #'elpher:eww-browse-url))
(setup (:straight (gemini-mode
:host nil
:repo "https://git.carcosa.net/jmcbray/gemini.el.git"))
(:option (append auto-mode-alist)
'("\\.\\(gemini\\|gmi\\)\\'" . gemini-mode)))
;;;;; Read e-books (nov.el)
(setup (:straight nov)
(:option nov-text-width fill-column
(append auto-mode-alist) '("\\.epub\\'" . nov-mode)))
;;;;; Org mode
(setup (:straight (org :host nil
:repo "https://code.orgmode.org/bzg/org-mode.git"))
(require 'acdw-org)
(:option org-directory "~/org"
org-hide-emphasis-markers t
org-fontify-whole-heading-line t
org-fontify-done-headline t
org-fontify-quote-and-verse-blocks t
org-src-fontify-natively t
org-pretty-entities t
org-tags-column (- 0 fill-column -3)
org-src-tab-acts-natively t
org-src-window-setup 'current-window
org-confirm-babel-evaluate nil
org-adapt-indentation nil
org-catch-invisible-edits 'smart
org-special-ctrl-a/e t
org-special-ctrl-k t
org-imenu-depth 3
org-export-headline-levels 8
org-export-with-smart-quotes t
org-export-with-sub-superscripts t)
(:bind "RET" unpackaged/org-return-dwim)
(add-hook 'before-save-hook #'acdw/hook--org-mode-fix-blank-lines))
;;;;; PDF Tools
(when (eq acdw/system :home)
(setup (:straight pdf-tools)
(pdf-loader-install)))
;;; Programming languages
;; This section includes packages and other settings, because most languages'
;; packages aren't packaged with Emacs.
;;;;; Formatting
(setup (:straight (apheleia :host github
:repo "raxod502/apheleia"))
(apheleia-global-mode +1)
;; Use a dumb formatter on modes that `apheleia' doesn't work for.
(defun dumb-auto-format ()
"Run `indent-region' in buffers that don't have an
`apheleia' formatter set."
(unless (apheleia--get-formatter-command)
(indent-region (point-min) (point-max))))
(add-hook 'before-save-hook #'dumb-auto-format))
;;;;; Eldoc
(setup eldoc
(:option eldoc-idle-delay 0.1
eldoc-echo-area-use-multiline-p nil))
;;;;; Emacs lisp
(setup elisp-mode
(:option eval-expression-print-length nil
eval-expression-print-level nil
lisp-indent-function #'lisp-indent-function)
(defun acdw/eval-region-or-buffer ()
(interactive)
(if (region-active-p)
(eval-region (region-beginning) (region-end))
(eval-buffer)))
(:with-map emacs-lisp-mode-map
(:bind "C-c C-c" 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))
;;;;; Shell scripts
(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-use-package 'flymake-shellcheck)
(:hook flymake-mode
flymake-shellcheck-load)))
;;;;; Web languages
(setup (:straight web-mode)
(:option css-level-offset 2
js-indent-level 2
sgml-indent-offset 2)
(dolist (extension '("\\(p\\|dj\\)?html"
"html?"
"\\(tpl\\.\\)?php"
"[agj]sp"
"as[cp]x"
"erb"
"mustache"))
(add-to-list 'auto-mode-alist
`(,(concat "\\." extension "\\'") . web-mode))))
;;;;; FORTH
(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)))
;;;- init.el ends here