;;; init.el -*- lexical-binding: t; coding: utf-8-unix -*- ;; Author: Case Duckworth ;; 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)))) ;; 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) ;;; 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) ;;; Good defaults ;; Lines (setc fill-column 80 word-wrap t truncate-lines nil) (add-hook 'text-mode-hook #'turn-on-auto-fill) (add-hook 'prog-mode-hook #'turn-on-auto-fill) (global-display-fill-column-indicator-mode +1) (global-so-long-mode +1) ;; 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) (show-paren-mode +1) (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-visited-mode +1) (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))) (setup (:require autorevert) (global-auto-revert-mode +1)) (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)) (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) (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)) (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)) (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 (set-face-attribute 'default nil :font (pcase acdw/system (:home "DejaVu Sans Mono-10") (:work "Consolas-10") (:other "monospace-10"))) (set-face-attribute 'fixed-pitch nil :inherit 'default) (set-face-attribute 'variable-pitch nil :font (pcase acdw/system (:home "DejaVu Sans") (:work "Calibri-11") (:other "sans-serif"))) ;; MS Windows (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) ;; Dired (setup dired (:option 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) (:acdw/map "C-x C-j" dired-jump)) ;;;; Web browsing (when (eq acdw/system :work) (add-to-list 'exec-path "C:/Program Files/Mozilla Firefox")) (setup browse-url (:option 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)) (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)) (add-hook 'text-mode-hook #'goto-address-mode) (add-hook 'prog-mode-hook #'goto-address-prog-mode) ;; Eshell (setup eshell (:option eshell-directory-name (acdw/in-dir "eshell/" t) eshell-aliases-file (acdw/in-dir "eshell/aliases" t))) ;; 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))) ;; 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" use-dialog-box nil use-file-dialog nil inhibit-startup-echo-area-message (pcase acdw/system (:home "acdw") (:work "aduckworth") (:other (getenv "USER")))) ;; Etc. bindings (autoload 'ehelp-command "ehelp" nil nil 'keymap) (define-key acdw/map (kbd "C-h") 'ehelp-command) (define-key acdw/map [remap just-one-space] #'cycle-spacing) (define-key acdw/map (kbd "M-/") #'hippie-expand) (define-key acdw/map (kbd "M-=") #'count-words) (define-key acdw/map (kbd "C-x C-b") #'ibuffer) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Here ends the package-less configuration. Everything following requires a ;;; package to be downloaded. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Interactivity ;;;; Begin-end (setup (:straight beginend) (beginend-global-mode +1)) ;;;; Expand-region (setup (:straight expand-region) (:acdw/map "C-=" er/expand-region)) ;;;; CRUX (setup (:straight crux) (:with-map acdw/map (:bind "M-o" crux-other-window-or-switch-buffer) (:bind "C-a" crux-move-beginning-of-line) (:bind "C-k" crux-kill-and-join-forward)) (crux-reopen-as-root-mode +1)) ;;; Functionality ;;;; Async (setup (:straight async) (autoload 'dired-async-mode "dired-async.el" nil t) (dired-async-mode +1)) ;;;; Undo-fu (setup (:straight undo-fu) (:with-map acdw/map (:bind "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)) (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 "") command) (define-key map (kbd "C-n") command)) (let ((command #'icomplete-backward-completions)) (define-key map (kbd "") 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) (:with-map acdw/map (:bind-autoload ;; 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 " a" consult-apropos "C-h a" consult-apropos)) (autoload 'consult-register-preview "consult") (:option register-preview-delay 0 register-preview-function #'consult-register-preview)) ;;;; 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)) ;;; 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)) ;;; 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/")) (:bind "n" elpher-next-link "p" elpher-prev-link "o" elpher-follow-current-link "G" elpher-go-current) (:hook acdw/reading-mode) (when (boundp 'god-exempt-major-modes) (:option (append god-exempt-major-modes) 'elpher-mode))) (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)) ;;; Programming languages ;;;; Formatting (setup (:straight (apheleia :host github :repo "raxod502/apheleia")) (apheleia-global-mode +1)) ;;;; Emacs lisp (setup emacs-lisp (:option eval-expression-print-length nil eval-expression-print-level nil lisp-indent-function #'lisp-indent-function)) ;;;; Shell scripts (setup sh (: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)))) ;;; init.el ends here