463 lines
14 KiB
EmacsLisp
463 lines
14 KiB
EmacsLisp
;;; 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
|