emacs/lisp/+emacs.el

385 lines
13 KiB
EmacsLisp
Raw Permalink Blame History

This file contains invisible Unicode characters

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

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

;;; +emacs.el --- measured defaults for Emacs -*- lexical-binding: t -*-
;;; Commentary:
;; I find myself copy-pasting a lot of "boilerplate" type code when
;; bankrupting my Emacs config and starting afresh. Instead of doing
;; that, I'm putting it here, where it'll be easier to include in my
;; config.
;; Of course, some might say I could just ... stop bankrupting my
;; Emacs. But like, why would I want to?
;; Other notable packages include
;; - https://git.sr.ht/~technomancy/better-defaults/
;; - https://github.com/susam/emfy
;;; Code:
(require 'early-init (locate-user-emacs-file "early-init.el"))
(defun +set-major-mode-from-buffer-name (&optional buf)
"Set the major mode for BUF from the buffer's name.
Do this only if the buffer is not visiting a file."
(unless buffer-file-name
(let ((buffer-file-name (buffer-name buf)))
(set-auto-mode))))
;;; General settings
(setq-default
apropos-do-all t
async-shell-command-buffer 'new-buffer
async-shell-command-display-buffer nil
auto-hscroll-mode 'current-line
auto-revert-verbose nil
auto-save-file-name-transforms `((".*" ,(.etc "auto-save/" t) t))
auto-save-interval 60
auto-save-list-file-prefix (.etc "auto-save/.saves-" t)
auto-save-timeout 60
auto-save-visited-interval 60
auto-window-vscroll nil
backup-by-copying t
backup-directory-alist `((".*" . ,(.etc "backup/" t)))
blink-cursor-blinks 1
comp-deferred-compilation nil
completion-category-defaults nil
completion-category-overrides '((file (styles . (partial-completion))))
completion-ignore-case t
completion-styles '(substring partial-completion)
create-lockfiles nil
cursor-in-non-selected-windows 'hollow
cursor-type 'bar
custom-file (.etc "custom.el")
delete-old-versions t
echo-keystrokes 0.1
ediff-window-setup-function 'ediff-setup-windows-plain
eldoc-echo-area-use-multiline-p nil
eldoc-idle-delay 0.1
enable-recursive-minibuffers t
executable-prefix-env t
fast-but-imprecise-scrolling t
file-name-shadow-properties '(invisible t intangible t)
fill-column 80
find-file-visit-truename t
frame-resize-pixelwise t
global-auto-revert-non-file-buffers t
global-mark-ring-max 100
hscroll-margin 1
hscroll-step 1
imenu-auto-rescan t
image-use-external-converter (or (executable-find "convert")
(executable-find "gm")
(executable-find "ffmpeg"))
indent-tabs-mode nil
inhibit-startup-screen t
initial-buffer-choice t
kept-new-versions 6
kept-old-versions 2
kill-do-not-save-duplicates t
kill-read-only-ok t
kill-ring-max 500
kmacro-ring-max 20
load-prefer-newer noninteractive
major-mode '+set-major-mode-from-buffer-name
mark-ring-max 50
minibuffer-eldef-shorten-default t
minibuffer-prompt-properties (list 'read-only t
'cursor-intangible t
'face 'minibuffer-prompt)
mode-require-final-newline 'visit-save
mouse-drag-copy-region t
mouse-wheel-progressive-speed nil
mouse-yank-at-point t
native-comp-async-report-warnings-errors 'silent
native-comp-deferred-compilation nil
read-answer-short t
read-buffer-completion-ignore-case t
;; read-extended-command-predicate
;; (when (fboundp
;; 'command-completion-default-include-p)
;; 'command-completion-default-include-p)
read-process-output-max (+bytes 1 :mib) ; Were in the future man. Set that to at least a megabyte
recenter-positions '(top middle bottom)
regexp-search-ring-max 100
regexp-search-ring-max 200
save-interprogram-paste-before-kill t
scroll-conservatively 101
scroll-down-aggressively 0.01
scroll-margin 2
scroll-preserve-screen-position 1
scroll-step 1
scroll-up-aggressively 0.01
search-ring-max 200
search-ring-max 200
sentence-end-double-space t
set-mark-command-repeat-pop t
show-paren-delay 0
show-paren-style 'mixed
show-paren-when-point-in-periphery t
show-paren-when-point-inside-paren t
;;show-trailing-whitespace t
tab-bar-show 1
tab-width 8 ; so alignment expecting the default looks right
tramp-backup-directory-alist backup-directory-alist
undo-limit 100000000 ; 10 MB
use-dialog-box nil
use-file-dialog nil
use-short-answers t
vc-follow-symlinks t
vc-make-backup-files t
version-control t
view-read-only t
visible-bell nil
window-resize-pixelwise t
x-select-enable-clipboard t
x-select-enable-primary t
yank-pop-change-selection t
)
;; Programming language offsets.
;; Set these after the initial block so I can use `tab-width'
(setq-default
c-basic-offset tab-width)
;; Emacs 28 ships with an option, `use-short-answers', that makes this form
;; obsolete, but I still use 27 at work.
(when (version< emacs-version "28")
(fset 'yes-or-no-p 'y-or-n-p))
;;; Encodings
;; Allegedly, this is the only one you need...
(set-language-environment "UTF-8")
;; But I still set all of these, for fun.
(setq-default 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)
(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)
(pcase system-type
((or 'ms-dos 'windows-nt)
(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)))
;;; Modes
(dolist (enable-mode '(global-auto-revert-mode
blink-cursor-mode
electric-pair-mode
show-paren-mode
global-so-long-mode
minibuffer-depth-indicate-mode
file-name-shadow-mode
minibuffer-electric-default-mode
delete-selection-mode
;; column-number-mode
))
(when (fboundp enable-mode)
(funcall enable-mode +1)))
(dolist (disable-mode '(tooltip-mode
tool-bar-mode
menu-bar-mode
scroll-bar-mode
horizontal-scroll-bar-mode))
(when (fboundp disable-mode)
(funcall disable-mode -1)))
;;; Hooks
(add-hook 'after-save-hook #'executable-make-buffer-file-executable-if-script-p)
(add-hook 'minibuffer-setup-hook #'cursor-intangible-mode)
(defun +auto-create-missing-dirs ()
"Automatically create missing directories when finding a file."
;; https://emacsredux.com/blog/2022/06/12/auto-create-missing-directories/
(let ((target-dir (file-name-directory buffer-file-name)))
(unless (file-exists-p target-dir)
(make-directory target-dir t))))
(add-hook 'find-file-not-found-functions #'+auto-create-missing-dirs)
;;; Better-default functions ...
(defun +cycle-spacing (&optional n preserve-nl-back mode)
"Negate N argument on `cycle-spacing'.
That is, with a positive N, deletes newlines as well, leaving -N
spaces. If N is negative, it will not delete newlines and leave
N spaces. See docstring of `cycle-spacing' for the meaning of
PRESERVE-NL-BACK and MODE."
(interactive "*p")
(cycle-spacing (- n) preserve-nl-back mode))
(defun +save-buffers-quit (&optional arg)
"Silently save each buffer, then kill the current connection.
If the current frame has no client, kill Emacs itself using
`save-buffers-kill-emacs' after confirming with the user.
With prefix ARG, silently save all file-visiting buffers, then
kill without asking."
(interactive "P")
(save-some-buffers t)
(if (and (not (frame-parameter nil 'client))
(and (not arg)))
(when (yes-or-no-p "Sure you want to quit? ")
(save-buffers-kill-emacs))
(delete-frame nil :force)))
(defun +kill-word-backward-or-region (&optional arg backward-kill-word-fn)
"Kill active region or ARG words backward.
BACKWARD-KILL-WORD-FN is the function to call to kill a word
backward. It defaults to `backward-kill-word'."
(interactive "P")
(call-interactively (if (region-active-p)
#'kill-region
(or backward-kill-word-fn #'backward-kill-word))))
(defun +backward-kill-word-wrapper (fn &optional arg)
"Kill backward using FN until the beginning of a word, smartly.
If point is on at the beginning of a line, kill the previous new
line. If the only thing before point on the current line is
whitespace, kill that whitespace.
With argument ARG: if ARG is a number, just call FN
ARG times. Otherwise, just call FN."
;; I want this to be a wrapper so that I can call other word-killing functions
;; with it. It's *NOT* advice because those functions probably use
;; `backward-kill-word' under the hood (looking at you, paredit), so advice
;; will make things weird.
(if (null arg)
(cond
((looking-back "^" 1)
(let ((delete-active-region nil))
(delete-backward-char 1)))
((looking-back "^[ ]*")
(delete-horizontal-space :backward-only))
(t (call-interactively fn)))
(funcall fn (if (listp arg) 1 arg))))
(defun +backward-kill-word (&optional arg)
"Kill word backward using `backward-kill-word'.
ARG is passed to `backward-kill-word'."
(interactive "P")
(+backward-kill-word-wrapper #'backward-kill-word arg))
;; ... and advice
;; Indent the region after a yank.
(defun +yank@indent (&rest _)
"Indent the current region."
(indent-region (min (point) (mark)) (max (point) (mark))))
(advice-add #'yank :after #'+yank@indent)
(advice-add #'yank-pop :after #'+yank@indent)
;;; Bindings
;; I need to place these bindings under `+key-mode-map' so that they aren't
;; shadowed by other maps. There might be a better way to do this.
(require '+key)
(dolist (binding '(("C-x C-c" . +save-buffers-quit)
("M-SPC" . +cycle-spacing)
("M-/" . hippie-expand)
("M-=" . count-words)
("C-x C-b" . ibuffer)
("C-s" . isearch-forward-regexp)
("C-r" . isearch-backward-regexp)
("C-M-s" . isearch-forward)
("C-M-r" . isearch-backward)))
(define-key (current-global-map) (kbd (car binding)) (cdr binding)))
;;; Required libraries
(when (require 'uniquify nil :noerror)
(setq-default uniquify-buffer-name-style 'forward
uniquify-separator path-separator
uniquify-after-kill-buffer-p t
uniquify-ignore-buffers-re "^\\*"))
(when (require 'goto-addr)
(if (fboundp 'global-goto-address-mode)
(global-goto-address-mode +1)
(add-hook 'after-change-major-mode-hook 'goto-address-mode)))
(when (require 'recentf nil :noerror)
(setq-default recentf-save-file (.etc "recentf.el")
recentf-max-menu-items 100
recentf-max-saved-items nil
recentf-auto-cleanup 'mode)
(add-to-list 'recentf-exclude .etc)
(recentf-mode +1))
(when (require 'savehist nil :noerror)
(setq-default history-length t
history-delete-duplicates t
history-autosave-interval 60
savehist-file (.etc "savehist.el")
;; Other variables --- don't truncate any of these.
;; `add-to-history' uses the values of these variables unless
;; they're nil, in which case it falls back to `history-length'.
kill-ring-max 100
mark-ring-max 100
global-mark-ring-max 100
regexp-search-ring-max 100
search-ring-max 100
kmacro-ring-max 100
eww-history-limit 100)
(dolist (var '(extended-command-history
global-mark-ring
mark-ring
kill-ring
kmacro-ring
regexp-search-ring
search-ring))
(add-to-list 'savehist-additional-variables var))
(savehist-mode +1))
(when (require 'saveplace nil :noerror)
(setq-default save-place-file (.etc "places.el")
save-place-forget-unreadable-files (eq system-type 'gnu/linux))
(save-place-mode +1))
;; (when (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)))
;;; Newer features
;; These aren't in older version of Emacs, but they're so nice.
(when (fboundp 'repeat-mode)
(setq-default repeat-exit-key "g"
repeat-exit-timeout 5)
(repeat-mode +1))
(when (fboundp 'pixel-scroll-precision-mode)
(pixel-scroll-precision-mode +1))
(provide '+emacs)
;;; +emacs.el ends here