From 3f60767599db61c95333969869287d792d37159a Mon Sep 17 00:00:00 2001 From: Case Duckworth Date: Sun, 28 Feb 2021 23:58:57 -0600 Subject: Restart shit --- init.el | 608 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 565 insertions(+), 43 deletions(-) (limited to 'init.el') diff --git a/init.el b/init.el index 7cf9f7a..9808ae2 100644 --- a/init.el +++ b/init.el @@ -1,51 +1,573 @@ ;;; init.el -*- lexical-binding: t; coding: utf-8 -*- -;; Copyright (C) 2020 Case Duckworth - +;; Copyright (C) 2020-2021 Case Duckworth +;; ;; Author: Case Duckworth -;; Created: Sometime during the Covid-19 lockdown, 2019 +;; Created: Sometime during Covid-19, 2020 ;; Keywords: configuration -;; URL: https://tildegit.org/acdw/emacs +;; URL https://tildegit.org/acdw/emacs +;; +;; 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. +;; +;;; Comentary: +;; +;;; Code: -;; This file is not part of GNU Emacs. +;; User information +(setq 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) -;;; Commentary: -;; This file is automatically tangled from config.org. -;; Hand edits will be overwritten! +;; Load newer files first +(setq-default load-prefer-newer t) -;;; Code: +;; Make C-z more useful +(defvar acdw/leader + (let ((map (make-sparse-keymap)) + (c-z (global-key-binding "\C-z"))) + (global-unset-key "\C-z") + (global-set-key "\C-z" map) + (define-key map "\C-z" c-z) + map) + "A leader key for apps and stuff.") -(setq-default load-prefer-newer t) +(defun when-unfocused (func &rest args) + "Run FUNC with ARGS iff all frames are out of focus." + (when (seq-every-p #'null (mapcar #'frame-focus-state (frame-list))) + (apply func args))) + +;; Dialogs & alerts +(setq-default use-dialog-box nil) ; Don't use a dialog box +(fset 'yes-or-no-p #'y-or-n-p) + +(defun flash-mode-line () + (ding) + (invert-face 'mode-line) + (run-with-timer 0.2 nil #'invert-face 'mode-line)) + +(setq-default visible-bell nil ; Don't use a visible bell + ring-bell-function #'flash-mode-line) + +(defun hook--gc-when-unfocused () + (when-unfocused #'garbage-collect)) + +(add-function :after after-focus-change-function + #'hook--gc-when-unfocused) + +;; Minibuffer +(setq-default + minibuffer-prompt-properties '(read-only t + cursor-intangible t + face minibuffer-prompt) + enable-recursive-minibuffers t + file-name-shadow-properties '(invisible t)) +(file-name-shadow-mode +1) +(minibuffer-depth-indicate-mode +1) + +(use-package savehist + :straight nil + :init + (setq-default + savehist-file (expand-file-name "history" acdw/var-dir) + savehist-additional-variables '(kill-ring search-ring regexp-search-ring) + history-length t + history-delete-duplicates t + savehist-autosave-interval 60) + :config (savehist-mode +1)) + +;; Backups +(setq-default backup-by-copying t + delete-old-versions -1 ; Don't delete old versions + version-control t ; Make numeric backups + vc-make-backup-files t ; Backup version-controlled files + ) + +(let ((dir (expand-file-name "backup" acdw/var-dir))) + (make-directory dir 'parents) + (setq-default backup-directory-alist + `((".*" . ,dir)))) + +;; Lockfiles +(setq-default create-lockfiles nil) ; Are these necessary? + +;; Autosaves +(use-package super-save + :defer 5 ; This package can wait + :init + (setq-default + auto-save-default nil ; Don't use `auto-save' system + super-save-remote-files nil ; Don't save remote files + super-save-exclude '(".gpg") ; Wouldn't work anyway + super-save-auto-save-when-idle t) + :config + (super-save-mode +1)) + +;; Auto-revert +(global-auto-revert-mode +1) ; Automatically revert a file + ; to its on-disk contents + +(use-package saveplace + :straight nil + :init + (setq-default + save-place-file (expand-file-name "places" acdw/var-dir) + save-place-forget-unreadable-files (eq acdw/system :home)) + :config (save-place-mode +1)) + +(use-package recentf + :straight nil + :init + (setq recentf-save-file (expand-file-name "recentf" acdw/var-dir) + recentf-max-menu-items 100 + recentf-max-saved-items nil + recentf-auto-cleanup 'never) + (defun maybe-save-recentf () + "Save `recentf-file' every five minutes, but only when out of focus." + (defvar recentf--last-save (time-convert nil 'integer) + "When we last saved the `recentf-save-list'.") + + (when (> (time-convert (time-since recentf--last-save) 'integer) + (* 60 5)) + (setq-default recentf--last-save (time-convert nil 'integer)) + (when-unfocused #'recentf-save-list))) + :config + (recentf-mode +1) + (add-to-list 'recentf-exclude acdw/var-dir) + (add-to-list 'recentf-exclude acdw/etc-dir) + (add-function :after after-focus-change-function + #'maybe-save-recentf)) + + +;; Uniquify +(use-package uniquify + :straight nil + :init + (setq-default + uniquify-buffer-name-style 'forward ; bubble 'up' the directory tree + uniquify-separator "/" ; separate path elements + uniquify-after-kill-buffer-p t ; hook into buffer kills + uniquify-ignore-buffers-re "^\\*" ; don't worry about special buffers + )) + +;; Scratch +(setq-default + inhibit-startup-screen t ; Don't show the splash screen + initial-buffer-choice t ; Start on *scratch* + initial-scratch-message + (concat ";; Howdy, " + (nth 0 (split-string user-full-name)) "!" + " Welcome to GNU Emacs.\n\n")) + +(defun immortal-scratch () + "Don't kill *scratch* when asked to by `kill-buffer'." + (if (not (eq (current-buffer) (get-buffer "*scratch*"))) + t + (bury-buffer) + nil)) +(add-hook 'kill-buffer-query-functions #'immortal-scratch) + +;; Easier buffer-killing +(defun kill-a-buffer (&optional prefix) + "Kill a buffer and its window, prompting only on unsaved changes. + +`kill-a-buffer' uses the PREFIX argument to determine which buffer(s) to kill: +0 => Kill THIS buffer & window +4 (C-u) => Kill OTHER buffer & window +16 (C-u C-u) => Run the default `kill-buffer'." + (interactive "P") + (pcase (or (car prefix) 0) + (0 (kill-current-buffer) + (unless (one-window-p) (delete-window))) + (4 (other-window 1) + (kill-current-buffer) + (unless (one-window-p) (delete-window))) + (16 (let ((current-prefix-arg nil)) + (kill-buffer))))) + +(bind-key "C-x k" #'kill-a-buffer) + +;; UTF-8 with LF line endings +(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) + +(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 + + org-export-coding-system 'utf-8-unix + org-html-coding-system 'utf-8-unix ; doesn't take from above + + default-process-coding-system '(utf-8-unix . utf-8-unix) + x-select-request-type '(UTF8_STRING COMPOUND_TEXT TEXT STRING)) + +(defun ewiki/no-junk-please-were-unixish () + "Convert line endings to UNIX, dammit." + (let ((coding-str (symbol-name buffer-file-coding-system))) + (when (string-match "-\\(?:dos\\|mac\\)$" coding-str) + (set-buffer-file-coding-system 'unix)))) + +(add-hook 'find-file-hook #'ewiki/no-junk-please-were-unixish) +(add-hook 'before-save-hook #'ewiki/no-junk-please-were-unixish) + +;; Cursor +(setq-default cursor-type 'bar + cursor-in-non-selected-windows nil) +(blink-cursor-mode 0) + +;; Filling text +(setq-default fill-column 80) +(global-display-fill-column-indicator-mode +1) + +(bind-key "C-x f" #'find-file) ; I don't set `fill-column', ever + +(setq-default comment-auto-fill-only-comments t) +;; Enable `auto-fill-mode' everywhere +(add-hook 'text-mode-hook #'auto-fill-mode) +(add-hook 'prog-mode-hook #'auto-fill-mode) +;; Also enable `visual-line-mode' everywhere +(global-visual-line-mode +1) +;; "Fix" `visual-line-mode' in `org-mode' +(defun hook--visual-line-fix-org-keys () + (when (derived-mode-p 'org-mode) + (local-set-key (kbd "C-a") #'org-beginning-of-line) + (local-set-key (kbd "C-e") #'org-end-of-line) + (local-set-key (kbd "C-k") #'org-kill-line))) +(add-hook 'visual-line-mode-hook #'hook--visual-line-fix-org-keys) + +(use-package visual-fill-column + :init (setq-default visual-fill-column-center-text t) + :hook visual-fill-column-mode + :config + (advice-add 'text-scale-adjust :after #'visual-fill-column-adjust)) + +(when (fboundp 'global-so-long-mode) + (global-so-long-mode +1)) + +;; Whitespace +(setq-default whitespace-style '(empty ; remove blank lines at buffer edges + indentation ; clean up indentation + ;; mixed tabs & spaces + space-before-tab + space-after-tab)) +(add-hook 'before-save-hook #'whitespace-cleanup) + +(setq-default indent-tabs-mode t + tab-width 8) + +(use-package smart-tabs-mode + :config + (smart-tabs-insinuate 'c 'c++ 'java 'javascript 'cperl 'python 'ruby 'nxml)) + +;; Window layouts +(setq-default + split-width-threshold 100 ; minimum width for window splits + split-height-threshold 50 ; minimum height for window splits + display-buffer-alist ; how to display buffers + '((".*" . (display-buffer-reuse-window display-buffer-same-window))) + display-buffer-reuse-frames t ; allow reuse of frames + even-window-sizes nil ; avoid resizing windows to even them + help-window-select t ; select *Help* window when opened + ) + +(defun vsplit-other-window () + "Split the window vertically and switch to the new window." + (interactive) + (split-window-vertically) + (other-window 1 nil)) + +(defun hsplit-other-window () + "Split the window horizontally and switch to the new window." + (interactive) + (split-window-horizontally) + (other-window 1 nil)) + +(bind-key "C-x 2" #'vsplit-other-window) +(bind-key "C-x 3" #'hsplit-other-window) + +;; Theming + +(use-package form-feed + :config (global-form-feed-mode +1)) + +(use-package modus-themes + :straight (:host gitlab :repo "protesilaos/modus-themes") + :demand + :init + (setq-default 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) + :custom-face + (modus-theme-heading-1 + ((t (:inherit (modus-theme-heading-1 fixed-pitch bold))))) + (modus-theme-heading-2 + ((t (:inherit (modus-theme-heading-2 fixed-pitch bold))))) + (modus-theme-heading-3 + ((t (:inherit (modus-theme-heading-3 fixed-pitch bold))))) + (modus-theme-heading-4 + ((t (:inherit (modus-theme-heading-4 fixed-pitch bold))))) + (modus-theme-heading-5 + ((t (:inherit (modus-theme-heading-5 fixed-pitch bold))))) + (modus-theme-heading-6 + ((t (:inherit (modus-theme-heading-6 fixed-pitch bold))))) + (modus-theme-heading-7 + ((t (:inherit (modus-theme-heading-7 fixed-pitch bold))))) + (modus-theme-heading-8 + ((t (:inherit (modus-theme-heading-8 fixed-pitch bold)))))) + +;; Change themes based on time of day + +(defun acdw/run-with-sun (sunrise-command sunset-command) + "Run commands at sunrise and sunset." + (let* ((times-regex (rx (* nonl) + (: (any ?s ?S) "unrise") " " + (group (repeat 1 2 digit) ":" + (repeat 1 2 digit) + (: (any ?a ?A ?p ?P) (any ?m ?M))) + (* nonl) + (: (any ?s ?S) "unset") " " + (group (repeat 1 2 digit) ":" + (repeat 1 2 digit) + (: (any ?a ?A ?p ?P) (any ?m ?M))) + (* nonl))) + (ss (sunrise-sunset)) + (_m (string-match times-regex ss)) + (sunrise-time (match-string 1 ss)) + (sunset-time (match-string 2 ss))) + (run-at-time sunrise-time (* 60 60 24) sunrise-command) + (run-at-time sunset-time (* 60 60 24) sunset-command) + (run-at-time "0:00" (* 60 60 24) sunset-command))) + +(acdw/run-with-sun #'modus-themes-load-operandi + #'modus-themes-load-vivendi) + +(use-package minions + :config (minions-mode +1)) + +(which-function-mode +1) + +(use-package which-key + :config (which-key-mode +1)) + +(delete-selection-mode +1) + +(setq-default + save-interprogram-paste-before-kill t ; save existing text before replacing + yank-pop-change-selection t ; update X selection when rotating ring + x-select-enable-clipboard t ; Enable X clipboards + x-select-enable-primary t + mouse-drag-copy-region t ; Copy a region when mouse-selected + kill-do-not-save-duplicates t ; Don't append the same thing twice + ) + +(use-package smartscan + :config + (global-smartscan-mode +1)) + +(when (fboundp 'global-goto-address-mode) + (global-goto-address-mode +1)) + +(use-package flyspell + :init + (setenv "LANG" "en_US") + (setq-default ispell-program-name "hunspell" + ispell-dictionary "en_US" + ispell-personal-dictionary "~/.hunspell_personal") + :hook + (text-mode . flyspell-mode) + (prog-mode . flyspell-prog-mode) + :config + (ispell-set-spellchecker-params) + (unless (file-exists-p ispell-personal-dictionary) + (write-region "" nil ispell-personal-dictionary nil 0))) + +(use-package flyspell-correct + :bind ("C-;" . flyspell-correct-wrapper)) + +(setq-default 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) + +(setq-default prettify-symbols-unprettify-at-point 'right-edge) +(add-hook 'prog-mode-hook #'prettify-symbols-mode) + +(add-hook 'after-save-hook + #'executable-make-buffer-file-executable-if-script-p) + +(setq-default compilation-ask-about-save nil ; just save the buffer + compilation-always-kill t ; kill the processes without asking + compilation-scroll-output 'first-error) + +(use-package reformatter + :demand) + +;; Shell scripts +(setq-default sh-basic-offset 8 + smie-indent-basic 8) + +(use-package flymake-shellcheck + :when (executable-find "shellcheck") + :hook sh-mode) + +(when (executable-find "shfmt") + (reformatter-define sh-format + :program "shfmt" + :lighter "Shfmt") + (add-hook 'sh-mode-hook #'sh-format-on-save-mode)) + +(bind-key "M-/" #'hippie-expand) + +;; Tabs +(setq-default + tab-bar-show 1 ; show the tab bar when more than one + tab-bar-new-tab-choice "*scratch*" ; what to show on a new tab + tab-bar-tab-name-function ; how to name a new tab + #'tab-bar-tab-name-current-with-count + tab-bar-history-limit 25 ; how many tabs to save in history + ) + +(tab-bar-history-mode +1) + +;; Smart hungry delete +(use-package smart-hungry-delete + :defer nil + :bind (("" . smart-hungry-delete-backward-char) + ("C-d" . smart-hungry-delete-forward-char)) + :config (smart-hungry-delete-add-default-hooks)) + +;; Enable all commands +(setq-default disabled-command-function nil) + +;; Magit +(use-package magit + :bind ("C-z g" . magit-status)) + +;; crux +(use-package crux + :straight (:host github :repo "bbatsov/crux") + :bind + ("M-o" . crux-other-window-or-switch-buffer) + :config + (crux-with-region-or-line kill-ring-save) + (crux-with-region-or-line kill-region) + (crux-with-region-or-line comment-or-uncomment-region)) + +;; Completion and... stuff +(setq-default + completion-ignore-case t + read-buffer-completion-ignore-case t + read-file-name-completion-ignore-case t) + +(use-package icomplete-vertical + :demand + :init + (setq-default + icomplete-delay-completions-threshold 0 + icomplete-max-delay-chars 0 + icomplete-compute-delay 0 + icomplete-show-matches-on-no-input t + icomplete-hide-common-prefix nil + icomplete-with-completion-tables t + icomplete-in-buffer t) + :bind (:map icomplete-minibuffer-map + ("" . icomplete-forward-completions) + ("C-n" . icomplete-forward-completions) + ("" . icomplete-backward-completions) + ("C-p" . icomplete-backward-completions) + ("C-v" . icomplete-vertical-toggle)) + :config + (fido-mode -1) + (icomplete-mode +1) + (icomplete-vertical-mode +1)) + +(use-package orderless + :after icomplete + :init (setq-default completion-styles '(orderless))) + +(use-package marginalia + :after icomplete + :init (setq-default marginalia-annotators + '(marginalia-annotators-heavy + marginalia-annotators-light)) + :config (marginalia-mode +1)) -(message "%s..." "Loading init.el") -(let* (;; Speed up init - (gc-cons-threshold most-positive-fixnum) - ;; (gc-cons-percentage 0.6) - (file-name-handler-alist nil) - ;; Config file names - (config (expand-file-name "config" - user-emacs-directory)) - (config.el (concat config ".el")) - (config.org (concat config ".org")) - (straight-org-dir (locate-user-emacs-file "straight/build/org"))) - ;; Okay, let's figure this out. - ;; `and' evaluates each form, and returns nil on the first that - ;; returns nil. `unless' only executes its body if the test - ;; returns nil. So. - ;; 1. Test if config.org is newer than config.el. If it is (t), we - ;; *want* to evaluate the body, so we need to negate that test. - ;; 2. Try to load the config. If it errors (nil), it'll bubble that - ;; to the `and' and the body will be evaluated. - (unless (and (not (file-newer-than-file-p config.org config.el)) - (load config :noerror)) - ;; A plain require here just loads the older `org' - ;; in Emacs' install dir. We need to add the newer - ;; one to the `load-path', hopefully that's all. - (when (file-exists-p straight-org-dir) - (add-to-list 'load-path straight-org-dir)) - ;; Load config.org - (message "%s..." "Loading config.org") - (require 'org) - (org-babel-load-file config.org) - (message "%s... Done" "Loading config.org"))) -(message "%s... Done." "Loading init.el") -;;; init.el ends here +(use-package consult + :after icomplete + :bind (;; 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) ; orig. repeat-complet-command + ("C-x b" . consult-buffer) ; orig. switch-to-buffer + ("C-x 4 b" . consult-buffer-other-window) ; orig. switch-to-buffer-other-window + ("C-x 5 b" . consult-buffer-other-frame) ; orig. switch-to-buffer-other-frame + ;; Custom M-# bindings for fast register access + ("M-#" . consult-register-load) + ("M-'" . consult-register-store) ; orig. abbrev-prefix-mark (unrelated) + ("C-M-#" . consult-register) + ;; Other custom bindings + ("M-y" . consult-yank-pop) ; orig. yank-pop + (" a" . consult-apropos) ; orig. apropos-command + ;; M-g bindings (goto-map) + ("M-g e" . consult-compile-error) + ("M-g g" . consult-goto-line) ; orig. goto-line + ("M-g M-g" . consult-goto-line) ; orig. 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 f" . consult-find) + ("M-s L" . consult-locate) + ("M-s g" . consult-grep) + ("M-s G" . consult-git-grep) + ("M-s r" . consult-ripgrep) + ("M-s l" . consult-line) + ("M-s m" . consult-multi-occur) + ("M-s k" . consult-keep-lines) + ("M-s u" . consult-focus-lines) + ;; Isearch integration + ("M-s e" . consult-isearch) + :map isearch-mode-map + ("M-e" . consult-isearch) ; orig. isearch-edit-string + ("M-s e" . consult-isearch) ; orig. isearch-edit-string + ("M-s l" . consult-line)) ; required by consult-line to detect isearch + :init + (setq register-preview-delay 0 + register-preview-function #'consult-register-format) + (advice-add #'register-preview :override #'consult-register-window) + (setq xref-show-xrefs-function #'consult-xref + xref-show-definitions-function #'consult-xref) + :config + ;; (setq consult-preview-key 'any) + ;; (setq consult-preview-key (kbd "M-p")) + (setq consult-narrow-key "<")) -- cgit 1.4.1-21-gabe81