emacs/init.el

463 lines
14 KiB
EmacsLisp
Raw Permalink Blame History

This file contains ambiguous Unicode characters

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

;;; init.el -*- 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: 5c
;;
;; 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:
;;; Add `acdw.el'
(push (expand-file-name "lisp/" user-emacs-directory)
load-path)
(require 'acdw)
;;; About me
(acdw/set
'((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)
(calendar-date-style iso)))
;;; Good defaults
;; Lines
(acdw/set '((fill-column 80)))
(global-display-fill-column-indicator-mode +1)
(add-hook 'text-mode-hook #'turn-on-auto-fill)
(add-hook 'prog-mode-hook #'turn-on-auto-fill)
(global-so-long-mode +1)
;; I don't want `visual-line-mode', because I really only want to wrap and
;; truncate lines. Believe me, I know what I'm doing.
(acdw/set '((word-wrap t)
(truncate-lines t)))
;; Whitespace
(acdw/set '((whitespace-style
(empty indentation space-before-tab space-after-tab))
(indent-tabs-mode t)
(tab-width 8)))
(add-hook 'before-save-hook #'whitespace-cleanup)
;; Pairs
(add-hook 'prog-mode-hook #'electric-pair-local-mode)
(acdw/set '((show-paren-delay 0)
(show-paren-style mixed)
(show-paren-when-point-inside-paren t)
(show-paren-when-point-in-periphery t)))
(show-paren-mode +1)
;; Killing & Yanking
(delete-selection-mode +1)
(acdw/set '((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)))
;; Encoding
(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)
(acdw/set '((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)
(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))))
;; Unique buffer names
(when (require 'uniquify)
(acdw/set '((uniquify-buffer-name-style forward)
(uniquify-separator "/")
(uniquify-after-kill-buffer-p t)
(uniquify-ignore-buffers-re "^\\*"))))
;; Backups
(acdw/set `((backup-by-copying t)
(delete-old-versions -1)
(version-control t)
(vc-make-backup-files t)
(backup-directory-alist ((".*" . ,(acdw/in-dir "backup/" t))))))
;; Autosaves
(acdw/set `((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))))
(auto-save-visited-mode +1)
(defun hook--auto-save-when-unfocused ()
"Save all buffers when out of focus."
(acdw/when-unfocused #'save-some-buffers t))
(add-function :after after-focus-change-function
#'hook--auto-save-when-unfocused)
;; Auto-revert
(when (require 'autorevert)
(global-auto-revert-mode +1))
;; Save place
(when (require 'saveplace)
(acdw/set
`((save-place-file ,(acdw/in-dir "places.el"))
(save-place-forget-unreadable-files ,(eq acdw/system :home))))
(save-place-mode +1))
;; Recent files
(when (require 'recentf)
(acdw/set
`((recentf-save-file ,(acdw/in-dir "recentf.el"))
(recentf-max-menu-items 100)
(recentf-max-saved-items nil)
(recentf-auto-cleanup 60 "Cleanup the recentf list when idle for 60s.")))
(add-to-list 'recentf-exclude acdw/dir)
(recentf-mode +1))
;; Move the custom file
(acdw/set `((custom-file ,(acdw/in-dir "custom.el"))))
;; Cursor
(acdw/set '((cursor-type bar)
(cursor-in-non-selected-windows hollow)))
(defun hook--overwrite-mode-change-cursor ()
(setq cursor-type (if overwrite-mode 'hbar 'bar)))
(add-hook 'overwrite-mode-hook #'hook--overwrite-mode-change-cursor)
;; Scrolling
(acdw/set '((auto-window-vscroll nil)
(fast-but-imprecise-scrolling t)
(scroll-margin 0)
(scroll-conservatively 101)
(scroll-preserve-screen-position 1)))
;; Bindings
(acdw/binds (("C-h" ehelp-command :after ("ehelp" nil nil 'keymap))
([remap just-one-space] cycle-spacing)
("M-/" hippie-expand)
("M-=" count-words)
("C-x C-b" ibuffer)))
;;; Startup
(acdw/set `((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"))))
;;; Minibuffer
(acdw/set '((minibuffer-prompt-properties (read-only t
cursor-intangible t
face minibuffer-prompt))
(enable-recursive-minibuffers t)
(file-name-shadow-properties (invisible t))))
(minibuffer-depth-indicate-mode +1)
(file-name-shadow-mode +1)
;; Save history
(when (require 'savehist)
(acdw/set `((savehist-additional-variables
(kill-ring search-ring regexp-search-ring))
(history-length t)
(history-delete-duplicates t)
(savehist-autosave-interval 6)
(savehist-file ,(acdw/in-dir "savehist.el"))))
(savehist-mode +1))
;; Icomplete (-vertical)
(when (require 'icomplete)
(acdw/set '((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)))
(acdw/pkg orderless
:set '((completion-styles (orderless))))
(acdw/pkg icomplete-vertical
:binds (("<down>" icomplete-forward-completions
:map icomplete-minibuffer-map)
("C-n" icomplete-forward-completions
:map icomplete-minibuffer-map)
("<up>" icomplete-backward-completions
:map icomplete-minibuffer-map)
("C-p" icomplete-backward-completions
:map icomplete-minibuffer-map)
("C-v" icomplete-vertical-toggle
:map icomplete-minibuffer-map))
:now ((fido-mode -1)
(icomplete-mode +1)
(icomplete-vertical-mode +1))))
;; Consult
(acdw/pkg consult
:binds (;; C-c bindings (`mode-specific-map')
("C-c h" consult-history)
("C-c m" consult-mode-command)
;; 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)
("C-x r x" consult-register)
("C-x r b" consult-bookmark)
;; M-g bindings (`goto-map')
("M-g o" consult-outline)
("M-g m" consult-mark)
("M-g k" consult-global-mark)
("M-g i" consult-imenu)
("M-g e" consult-error)
;; M-s bindings (`search-map')
("M-s g" consult-grep) ; alts:
; consult-git-grep,
; consult-ripgrep
("M-s f" consult-find) ; alts:
; consult-locate
("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)
("<f1> a" consult-apropos)
("C-h a" consult-apropos))
:now ((autoload 'consult-register-preview "consult"))
:set '((register-preview-delay 0)
(register-preview-function #'consult-register-preview)))
;; Marginalia
(acdw/pkg marginalia
:set '((marginalia-annotators (marginalia-annotators-heavy
marginalia-annotators-light)))
:now ((marginalia-mode +1)))
;; Imenu
(when (require 'imenu)
(acdw/set '((imenu-auto-rescan t))))
;;; Packages
;; Undo-fu
(acdw/pkg undo-fu
:binds (("C-/" undo-fu-only-undo)
("C-?" undo-fu-only-redo)))
(acdw/pkg undo-fu-session
:set `((undo-fu-session-incompatible-files ("/COMMIT_EDITMSG\\'"
"/git-rebase-todo\\'"))
(undo-fu-session-directory ,(acdw/in-dir "undo/" t)))
:then ((global-undo-fu-session-mode +1)))
;; Modus themes
(acdw/pkg (modus-themes
:host gitlab
:repo "protesilaos/modus-themes")
:set `((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-scale-headings nil)
(modus-themes-mode-line nil))
:now ((acdw/sunrise-sunset #'modus-themes-load-operandi
#'modus-themes-load-vivendi)))
;;; Mode line
;; Minions
(acdw/pkg minions
:now ((minions-mode +1)))
;; Simple mode line
(acdw/pkg simple-modeline
:set '((simple-modeline-segments
((;; left
acdw/modeline-modified
simple-modeline-segment-buffer-name
simple-modeline-segment-position)
(;; right
simple-modeline-segment-vc
simple-modeline-segment-misc-info
simple-modeline-segment-process
acdw/modeline-minions
simple-modeline-segment-major-mode))))
:now ((defun acdw/modeline-modified ()
"Displays a color-coded buffer modification/read-only
indicator in the mode-line."
(if (not (string-match-p "\\*.*\\*" (buffer-name)))
(let* ((read-only (and buffer-read-only (buffer-file-name)))
(modified (buffer-modified-p)))
(propertize
(if read-only " ×" (if modified "" ""))
'face `(:inherit
,(if modified 'simple-modeline-status-modified
(if read-only 'simple-modeline-status-error
'simple-modeline-unimportant)))
'help-echo (format
(concat "Buffer is %s and %smodified\n"
"mouse-1: Toggle read-only status.")
(if read-only "read-only" "writable")
(if modified "" "not "))
'local-map (purecopy (simple-modeline-make-mouse-map
'mouse-1
(lambda (event)
(interactive "e")
(with-selected-window
(posn-window (event-start event))
(read-only-mode 'toggle)))))
'mouse-face 'mode-line-highlight))))
(defun acdw/modeline-minions ()
"Display a button for `minions-minor-modes-menu'."
(concat
" "
(propertize
""
'help-echo (format
"Minor modes menu\nmouse-1: show menu.")
'local-map (purecopy (simple-modeline-make-mouse-map
'mouse-1
(lambda (event)
(interactive "e")
(with-selected-window (posn-window
(event-start event))
(minions-minor-modes-menu)))))
'mouse-face 'mode-line-highlight)))
(simple-modeline-mode +1)))
;;; Magit
(acdw/pkg magit
:binds (("g" magit-status :map acdw/leader)))
;;; Web browsing
(acdw/set '((browse-url-browser-function browse-url-firefox)
(browse-url-new-window-flag t)
(browse-url-firefox-new-window-is-tab t)
(shr-max-width fill-column)
(shr-width fill-column)))
(when (eq acdw/system :work)
(add-to-list 'exec-path "C:/Program Files/Mozilla Firefox"))
(acdw/hooks ((text-mode-hook goto-address-mode)
(prog-mode-hook goto-address-prog-mode)))
;;; Dired
(acdw/set `((dired-recursive-copies always)
(dired-recursive-deletes always)
(delete-by-moving-to-trash t)
(dired-listing-switches "-Al")
(ls-lisp-dirs-first t)
(dired-dwim-target t)))
(defun hook--dired-mode ()
(hl-line-mode +1)
(dired-hide-details-mode +1))
(add-hook 'dired-mode-hook #'hook--dired-mode)
(acdw/bind "C-x C-j" dired-jump :after "dired-x")
(with-eval-after-load 'dired
;; I tried `autoload'ing the keymap, and it just wouldn't work. I have no
;; idea what the issue was.
(acdw/pkg dired-subtree
:binds (("i" dired-subtree-toggle :map dired-mode-map))))
(acdw/pkg dired-collapse
:hooks ((dired-mode-hook dired-collapse-mode)))
;;; Eshell
(acdw/set `((eshell-directory-name ,(acdw/in-dir "eshell/" t))
(eshell-aliases-file ,(acdw/in-dir "eshell/aliases" t))))
;;; Org-mode
(acdw/pkg (org
:repo "https://code.orgmode.org/bzg/org-mode.git")
:now ((require 'acdw-org))
:set `((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))
:hooks ((before-save-hook acdw/hook--org-mode-fix-blank-lines)))
;;; Programming languages
;; Emacs lisp
(acdw/set '((eval-expression-print-length nil)
(eval-expression-print-level nil)))
(when (require 'cl-lib)
(setq-default lisp-indent-function #'common-lisp-indent-function)
(put 'cl-flet 'common-lisp-indent-function
(get 'flet 'common-lisp-indent-function))
(put 'cl-labels 'common-lisp-indent-function
(get 'labels 'common-lisp-indent-function))
(put 'if 'common-lisp-indent-function 2)
(put 'dotimes-protect 'common-lisp-indent-function
(get 'when 'common-lisp-indent-function)))
;;; Miscellaneous
(acdw/set '((disabled-command-function nil)
(load-prefer-newer t)
(comp-async-report-warnings-errors nil)))
(fset 'yes-or-no-p #'y-or-n-p)
;; Garbage collection
(defun hook--gc-cons-maximize ()
(setq gc-cons-threshold most-positive-fixnum))
(add-hook 'minibuffer-setup-hook #'hook--gc-cons-maximize)
(defun hook--gc-cons-baseline ()
(setq gc-cons-threshold gc-cons-threshold-basis))
(add-hook 'minibuffer-exit-hook #'hook--gc-cons-baseline)
(defun hook--gc-when-unfocused ()
(acdw/when-unfocused #'garbage-collect))
(add-function :after after-focus-change-function
#'hook--gc-when-unfocused)
;;; init.el ends here