From c05d1ba2c8e28f2f55f9e11bcf8e42b1ed43e539 Mon Sep 17 00:00:00 2001 From: Case Duckworth Date: Tue, 6 Apr 2021 17:59:45 -0500 Subject: 7 --- .gitignore | 3 +- early-init.el | 75 +++---- init.el | 687 ++++++++++++++++++++++++---------------------------------- lisp/acdw.el | 193 ++++++----------- 4 files changed, 381 insertions(+), 577 deletions(-) diff --git a/.gitignore b/.gitignore index e2bdc81..8de33a1 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,5 @@ var/ etc/ straight/ transient/ -racket-mode/ \ No newline at end of file +racket-mode/ +*~ \ No newline at end of file diff --git a/early-init.el b/early-init.el index fac0705..b8f652e 100644 --- a/early-init.el +++ b/early-init.el @@ -24,37 +24,21 @@ load-path) (require 'acdw) -;;; Refresh Emacs easily -(defun refresh-emacs () - "Reload Emacs's configuration files." - (interactive) - (dolist (file (list (locate-user-emacs-file "early-init.el") - (locate-user-emacs-file "init.el" ".emacs"))) - (when (file-exists-p file) - (load-file file)))) - + ;;; Speed up init ;; see doom-emacs, et al. -(defvar orig-file-name-handler-alist file-name-handler-alist - "The original value of `file-name-handler-alist' will be restored - after init.") - -(setq file-name-handler-alist nil) -(setq gc-cons-threshold (* 800 1024 1024) - gc-cons-percentage 0.6) - -(add-hook 'after-init-hook - (defun hook--post-init-reset () - "Reset `gc-cons-threshold', `gc-cons-percentage', and - `file-name-handler-alist' to their defaults after init." - (acdw/gc-enable) - (dolist (handler file-name-handler-alist) - (add-to-list 'orig-file-name-handler-alist handler)) - (setq file-name-handler-alist orig-file-name-handler-alist))) +(setq orig-file-name-handler-alist file-name-handler-alist + file-name-handler-alist nil) +(acdw/gc-disable) +(hook-defun post-init-reset after-init-hook + (acdw/gc-enable) + (dolist (handler file-name-handler-alist) + (add-to-list 'orig-file-name-handler-alist handler)) + (setq file-name-handler-alist orig-file-name-handler-alist)) + ;;; Frame settings - (setq default-frame-alist ; Remove most UI `((tool-bar-lines . 0) ; No tool bar (menu-bar-lines . 0) ; No menu bar @@ -73,8 +57,7 @@ frame-resize-pixelwise t ; Resize by pixels, not chars ) -(defun hook--disable-ui-modes () - "Disable frame UI using modes, for toggling later." +(hook-defun disable-ui-modes after-init-hook (dolist (mode ;; each mode is of the form (MODE . FRAME-ALIST-VAR) '((tool-bar-mode . tool-bar-lines) (menu-bar-mode . menu-bar-lines) @@ -86,8 +69,25 @@ (= 0 setting)) (funcall (car mode) -1))))) -(add-hook 'after-init-hook #'hook--disable-ui-modes) - +(add-function :after after-focus-change-function + (defun hook--setup-fonts () + (dolist (face '(default fixed-pitch)) + ;; `default' and `fixed-pitch' should be the same. + (set-face-attribute face nil + :font (pcase acdw/system + (:home "DejaVu Sans Mono-10") + (:work "Consolas-10") + (:other "monospace-10")))) + ;; `variable-pitch' is, of course, different. + (set-face-attribute 'variable-pitch nil + :font (pcase acdw/system + (:home "DejaVu Sans") + (:work "Calibri-11") + (:other "sans-serif"))) + (remove-function after-focus-change-function + 'hook--setup-fonts))) + + ;;; Bootstrap package manager (`straight.el') ;; 1. Update `exec-path'. @@ -108,7 +108,7 @@ package-quickstart nil straight-host-usernames '((github . "duckwork") (gitlab . "acdw")) - straight-base-dir acdw/dir) + straight-base-dir (acdw/dir)) ;; 3. Bootstrap `straight'. (defvar bootstrap-version) @@ -126,9 +126,10 @@ (goto-char (point-max)) (eval-print-last-sexp))) (load bootstrap-file nil 'nomessage)) - + ;;; Message startup time for profiling - +;; This just redefines the Emacs function +;; `display-startup-echo-area-message', so no hooks needed. (defun display-startup-echo-area-message () "Show Emacs's startup time in the message buffer. For profiling." (message "Emacs ready in %s with %d garbage collections." @@ -136,11 +137,3 @@ (float-time (time-subtract after-init-time before-init-time))) gcs-done)) - -;;; Install `no-littering', pointing both directories at `acdw/dir'. This will -;;; take care of the packages I don't care about configuring. - -(straight-use-package 'no-littering) -(setq no-littering-etc-directory acdw/dir - no-littering-var-directory acdw/dir) -(require 'no-littering) diff --git a/init.el b/init.el index 09a4fe8..19c0f9a 100644 --- a/init.el +++ b/init.el @@ -15,23 +15,19 @@ ;; - Make good choices. ;;;- Code: + +;; Necessary packages -;;; `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))) +;; `setup' +(progn + (straight-use-package '(setup :host nil + :repo "https://git.sr.ht/~zge/setup")) + (require 'setup)) -;;;; Install packages with `straight-use-package' (setup-define :straight (lambda (recipe) `(straight-use-package ',recipe)) - :documentation "Install RECIPE with `straight-use-package'." + :documenation "Install RECIPE with `straight-use-package'." :repeatable t :shorthand (lambda (sexp) (let ((recipe (cadr sexp))) @@ -39,173 +35,243 @@ (car recipe) recipe)))) -;;;; Set options using `setq-default', instead of `customize-set-variable' -;; From what I can tell, `customize-set-variable' loads "all the dependencies -;; for each SYMBOL it sets (see `custom-load-symbol'). Since I don't want to do -;; that all the time, here's `:set'. DON'T USE THIS HARDLY EVER. Honestly, I -;; might want to do a `:option-after' instead (with `:after-loaded' set to t)... -(setup-define :setq-default - (lambda (variable value) - `(setq-default ,variable ,value)) - :documentation "Set options with `setq-default'. USE SPARINGLY!" - :debug '(sexp form) - :repeatable t) - - -;;;; Bind keys to `acdw/leader' -(setup-define :acdw/leader +(setup-define :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) + #',command)))) -;;;; 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) +;; `no-littering' +(setup (:straight no-littering) + (:option no-littering-etc-directory (acdw/dir) + no-littering-var-directory (acdw/dir)) + (require 'no-littering)) -;;; Good defaults - -;;;; 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) - -;;;; Lines -(setc fill-column 80 - word-wrap t - truncate-lines nil) -(add-hook 'text-mode-hook #'turn-on-auto-fill) -(global-display-fill-column-indicator-mode +1) -(global-so-long-mode +1) - -;; Only fill comments in prog-mode. -(add-hook 'prog-mode-hook - (defun hook--auto-fill-prog-mode () - (setq-local comment-auto-fill-only-comments t) - (turn-on-auto-fill))) - -;; Don't truncate lines in the minibuffer. -(add-hook 'minibuffer-setup-hook - (defun hook--minibuffer-enable-truncate-lines () - (setq-local truncate-lines t))) - -;;;; 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) -(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) +;; Good defaults + +(defmacro setc (&rest args) + "Customize user options using ARGS like `setq'." + (declare (debug setq)) + `(setup (:option ,@args))) + +(setup emacs + ;; Me + (:option 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) + + ;; Lines + (:option fill-column 80 + word-wrap t + truncate-lines nil) + + (global-display-fill-column-indicator-mode +1) + (global-so-long-mode +1) + + ;; Whitespace + (:option whitespace-style + '(empty indentation space-before-tab space-after-tab) + indent-tabs-mode nil + tab-width 4) + + (add-hook 'before-save-hook #'whitespace-cleanup) + + ;; Killing and yanking + (:option 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 + (:option 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) + + ;; Cursor + (:option cursor-type 'bar + cursor-in-non-selected-windows 'hollow) + + ;; Scrolling + (:option auto-window-vscroll nil + fast-but-imprecise-scrolling t + scroll-margin 0 + scroll-conservatively 101 + scroll-preserve-screen-position 1) + + ;; Minibuffer + (:option 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) + + (add-hook 'minibuffer-setup-hook #'acdw/gc-disable) + (add-hook 'minibuffer-exit-hook #'acdw/gc-enable) + + (hook-defun minibuffer-enable-truncate-lines minibuffer-setup-hook + (setq-local truncate-lines t)) + + (minibuffer-depth-indicate-mode +1) + (file-name-shadow-mode +1) + (fset 'yes-or-no-p #'y-or-n-p) + + ;; Completion + (: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) + completion-category-defaults nil + completion-category-overrides + '((file (styles . (partial-completion))))) + + ;; Etc. + (:option custom-file (acdw/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") + disabled-command-function nil + load-prefer-newer t + comp-async-report-warnings-errors nil + frame-title-format '("%b %+%* GNU Emacs" + (:eval (when (frame-parameter + nil 'client) + " Client"))) + tab-bar-show 1 + use-dialog-box nil + use-file-dialog nil + echo-keystrokes 0.25 + recenter-positions '(top middle bottom) + attempt-stack-overflow-recovery nil + attempt-orderly-shutdown-on-fatal-signal nil + window-resize-pixelwise t + find-function-C-source-directory + (pcase acdw/system + (:work (expand-file-name (concat "~/src/emacs-" + emacs-version + "/src"))) + (:home (expand-file-name + "~/src/pkg/emacs/src/emacs-git/src")) + (:other nil)) + 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) + + (when-unfocused garbage-collect + (garbage-collect)) + + (tooltip-mode -1) + (winner-mode +1) + + ;; Bindings + (:global "M-SPC" cycle-spacing + "M-/" hippie-expand + "M-=" count-words + "C-x C-b" ibuffer)) + +;; Regular modes (`text-mode', `prog-mode', etc.) +(defun acdw/setup-regular-modes () + (setq-local indicate-empty-lines t + indicate-buffer-boundaries '((top . right) + (bottom . right)))) + +(setup text + (:hook turn-on-auto-fill + acdw/setup-regular-modes)) + +(setup prog + (:option smie-indent-basic tab-width) + + (hook-defun auto-fill-prog-mode prog-mode-hook + (setq-local comment-auto-fill-only-comments t) + (turn-on-auto-fill)) + + (:option show-paren-delay 0 + show-paren-style 'mixed + show-paren-when-point-inside-paren t + show-paren-when-point-in-periphery t) + + (:hook show-paren-mode + electric-pair-local-mode + acdw/setup-regular-modes) + + (add-hook 'after-save-hook + #'executable-make-buffer-file-executable-if-script-p)) + +(setup 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) - -(add-hook 'after-save-hook #'executable-make-buffer-file-executable-if-script-p) - -;;;;; Auto-save files -(auto-save-visited-mode +1) -;; And /actually/ save all buffers when unfocused -(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))) - -;;;;; Autorevert files -(setup (:require autorevert) +(setup files + (:option backup-directory-alist `((".*" . ,(acdw/dir "backup/" t))) + tramp-backup-directory-alist backup-directory-alist + auto-save-file-name-transforms `((".*" ,(acdw/dir "auto-save/" t) t)) + auto-save-list-file-prefix (acdw/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) + + (when-unfocused save-some-buffers + (save-some-buffers t))) + +(setup autorevert (global-auto-revert-mode +1)) -;;;;; Save place in files -(setup (:require saveplace) - (:option save-place-file (acdw/in-dir "places.el") +(setup saveplace + (:option save-place-file (acdw/dir "places.el") save-place-forget-unreadable-files (eq acdw/system :home)) (save-place-mode +1)) -;;;;; Keep track of recent files (setup (:require recentf) - (:option recentf-save-file (acdw/in-dir "recentf.el") + (:option recentf-save-file (acdw/dir "recentf.el") recentf-max-menu-items 100 recentf-max-saved-items nil recentf-auto-cleanup 60 - (append recentf-exclude) acdw/dir) + (append recentf-exclude) (acdw/dir)) (recentf-mode +1)) -;;;; Windows -(winner-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) - -;;;;; Save minibuffer command history (and others) (setup (:require savehist) (:option (append savehist-additional-variables) 'kill-ring (append savehist-additional-variables) 'search-ring @@ -213,151 +279,36 @@ history-length t history-delete-duplicates t savehist-autosave-interval 6 - savehist-file (acdw/in-dir "savehist.el")) + savehist-file (acdw/dir "savehist.el")) (savehist-mode +1)) -;;;;; Completion framework -(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) - completion-category-defaults nil - completion-category-overrides - '((file (styles . (partial-completion))))) - (fido-mode -1) - (icomplete-mode +1)) - -;;;;; `imenu' (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 -;; Load them /after/ the first frame comes into focus -(add-function :after after-focus-change-function - (defun hook--setup-fonts () - (dolist (face '(default fixed-pitch)) - ;; `default' and `fixed-pitch' should be the same. - (set-face-attribute face nil - :font (pcase acdw/system - (:home "DejaVu Sans Mono-10") - (:work "Consolas-10") - (:other "monospace-10")))) - ;; `variable-pitch' is, of course, different. - (set-face-attribute 'variable-pitch nil - :font (pcase acdw/system - (:home "DejaVu Sans") - (:work "Calibri-11") - (:other "sans-serif"))) - (remove-function after-focus-change-function - 'hook--setup-fonts))) - -;;;; Debugger (setup debugger (:hook visual-line-mode) - (:acdw/leader "d" toggle-debug-on-error)) + (:leader "d" toggle-debug-on-error)) -;;;; 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))) +(setup eldoc + (:option eldoc-idle-delay 0.1 + eldoc-echo-area-use-multiline-p nil)) -;;;; Spelling (setup flyspell - (setenv "LANG" "en_US") (:option ispell-program-name "hunspell" ispell-dictionary "en_US" ispell-personal-dictionary "~/.hunspell_personal") + (:needs ispell-program-name) ; don't proceed if not installed + (ispell-set-spellchecker-params) (unless (file-exists-p ispell-personal-dictionary) (write-region "" nil ispell-personal-dictionary nil 0)) - (:needs ispell-program-name) - ;; add hooks (add-hook 'text-mode-hook #'flyspell-mode) - (add-hook 'prog-mode-hook #'flyspell-prog-mode)) - -;;;; MS Windows -(when (eq acdw/system :work) - (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)) - -;;;; Set up non-special modes -;; Great idea from brause.cc -(defun-with-hooks '(text-mode-hook prog-mode-hook diff-mode-hook) - (defun hook--setup-regular-modes () - (setq indicate-empty-lines t - indicate-buffer-boundaries '((top . right) (bottom . right))) - (goto-address-mode +1) - (show-paren-mode +1))) - -;;;; 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" - (:eval (when (frame-parameter nil 'client) - " Client"))) - tab-bar-show 1 - use-dialog-box nil - use-file-dialog nil - echo-keystrokes 0.25 - recenter-positions '(top middle bottom) - attempt-stack-overflow-recovery nil - attempt-orderly-shutdown-on-fatal-signal nil - window-resize-pixelwise t - find-function-C-source-directory - (pcase acdw/system - (:work (expand-file-name (concat "~/src/emacs-" - emacs-version - "/src"))) - (:home (expand-file-name "~/src/pkg/emacs/src/emacs-git/src")) - (:other nil))) - -;;;; Etc. modes -(tooltip-mode -1) - -;;;; Etc. bindings -(global-set-key [remap just-one-space] #'cycle-spacing) -(global-set-key (kbd "M-/") #'hippie-expand) -(global-set-key (kbd "M-=") #'count-words) -(global-set-key (kbd "C-x C-b") #'ibuffer) + (add-hook 'prog-mode-hook #'flyepell-prog-mode)) -;;; Applications -;; Some of these are built-in, some are packages; all are "extra" functionality -;; in Emacs (i.e., they're not /just/ editing text) -;;;; Org mode +;; Applications + (setup (:straight (org :host nil :repo "https://code.orgmode.org/bzg/org-mode.git")) (require 'acdw-org) @@ -383,38 +334,35 @@ (:bind "RET" unpackaged/org-return-dwim) (add-hook 'before-save-hook #'acdw/hook--org-mode-fix-blank-lines)) -;;;; Eshell (setup eshell + (:option eshell-directory-name (acdw/dir "eshell/" t) + eshell-aliases-file (acdw/dir "eshell/aliases" t)) + (defun eshell-quit-or-delete-char (arg) "Delete the character to the right, or quit eshell on an empty line." (interactive "p") (if (and (eolp) (looking-back eshell-prompt-regexp)) (eshell-life-is-too-much) (delete-forward-char arg))) - (:option eshell-directory-name (acdw/in-dir "eshell/" t) - eshell-aliases-file (acdw/in-dir "eshell/aliases" t)) - (add-hook 'eshell-mode-hook - (defun hook--eshell-setup () - "Stuff to do after eshell is done setting up." - (define-key eshell-mode-map (kbd "C-d") - #'eshell-quit-or-delete-char) - (setq mode-line-format '(:eval simple-modeline--mode-line))))) - + + (hook-defun eshell-setup 'eshell-mode-hook + (define-key eshell-mode-map (kbd "C-d") + #'eshell-quit-or-delete-char) + (when (fboundp simple-modeline--mode-line) + (setq mode-line-format '(:eval simple-modeline--mode-line))))) -;;;; Ediff (setup ediff (:option ediff-window-setup-function 'ediff-setup-windows-plain ediff-split-window-function 'split-window-horizontally)) -;;;; Web browsing (setup browse-url - (:setq-default 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) + (: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) (when (eq acdw/system :work) (add-to-list 'exec-path "C:/Program Files/Mozilla Firefox"))) @@ -428,11 +376,10 @@ (setup eww (:hook acdw/reading-mode)) -;;;; 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/") + elpher-certificate-directory (acdw/dir "elpher/") elpher-gemini-max-fill-width fill-column) (:bind "n" elpher-next-link "p" elpher-prev-link @@ -455,7 +402,6 @@ (:option (append auto-mode-alist) '("\\.\\(gemini\\|gmi\\)\\'" . gemini-mode))) -;;;; File browsing (setup dired (:setq-default dired-recursive-copies 'always dired-recursive-deletes 'always @@ -486,7 +432,6 @@ (setup (:straight trashed) (:option trashed-action-confirmer 'y-or-n-p)))) -;;;; Magit (setup (:straight magit) (:acdw/leader "g" magit-status) (:option magit-display-buffer-function @@ -495,39 +440,30 @@ (display-buffer buffer '(display-buffer-same-window))) magit-popup-display-buffer-action '((display-buffer-same-window)))) -;;;; Read e-books (nov.el) (setup (:straight nov) (:option nov-text-width fill-column (append auto-mode-alist) '("\\.epub\\'" . nov-mode))) -;;;; PDF Tools (when (eq acdw/system :home) (setup (:straight pdf-tools) (pdf-loader-install))) -;;;; VTerm (when (eq acdw/system :home) (setup (:straight vterm))) -;;; Packages - -;;;; Interactivity +;; Extra packages -;;;;; Begin-end (setup (:straight beginend) (beginend-global-mode +1)) -;;;;; MWIM (setup (:straight mwim) (:global "C-a" mwim-beginning "C-e" mwim-end)) -;;;;; Expand-region (setup (:straight expand-region) (:global "C-=" er/expand-region)) -;;;;; CRUX (setup (:straight crux) (:global "M-o" crux-other-window-or-switch-buffer "C-k" crux-kill-and-join-forward @@ -537,7 +473,6 @@ "C-c i" crux-find-user-init-file) (crux-reopen-as-root-mode +1)) -;;;;; AVY ... & friends (setup (:straight avy) (:global "C-:" avy-goto-char "C-'" avy-goto-char-timer @@ -547,7 +482,6 @@ (eval-after-load "isearch" '(define-key isearch-mode-map (kbd "C-'") #'avy-isearch))) -;;;;; zzz-to-char (setup (:straight zzz-to-char) (defun acdw/zzz-up-to-char (prefix) "Call `zzz-up-to-char', unless issued a PREFIX, in which case @@ -556,9 +490,9 @@ call `zzz-to-char'." (if prefix (call-interactively #'zzz-to-char) (call-interactively #'zzz-up-to-char))) + (:global "M-z" acdw/zzz-up-to-char)) -;;;;; anzu (setup (:straight anzu) (:option anzu-replace-to-string-separator " → " anzu-cons-mode-line-p nil) @@ -573,20 +507,10 @@ call `zzz-to-char'." (global-anzu-mode +1)) -;;;;; smart hungry delete -(setup (:straight smart-hungry-delete) - (:global "" smart-hungry-delete-backward-char - "C-d" smart-hungry-delete-forward-char) - (smart-hungry-delete-add-default-hooks)) - -;;;; Functionality - -;;;;; Async (setup (:straight async) (autoload 'dired-async-mode "dired-async.el" nil t) (dired-async-mode +1)) -;;;;; Undo-fu (setup (:straight undo-fu) (:global "C-/" undo-fu-only-undo "C-?" undo-fu-only-redo)) @@ -594,25 +518,24 @@ call `zzz-to-char'." (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) + undo-fu-session-directory (acdw/dir "undo/" t) undo-fu-session-compression (eq acdw/system :home)) + (global-undo-fu-session-mode +1)) -;;;; Minibuffer (setup (:straight (vertico :host github :repo "minad/vertico")) + (advice-add #'completing-read-multiple :filter-args (defun crm-indicator (args) (cons (concat "[CRM] " (car args)) (cdr args)))) - (icomplete-mode -1) + (vertico-mode +1)) -;;;;; Orderless (setup (:straight orderless) (:option (prepend completion-styles) 'orderless)) -;;;;; Consult (setup (:straight consult) ;; "Sensible" functions @@ -625,6 +548,7 @@ if ripgrep is installed, otherwise `consult-grep'." ((executable-find "rg") (call-interactively #'consult-ripgrep)) (t (call-interactively #'consult-grep)))) + (defun consult-sensible-find () "Peform `consult-locate' if locate is installed, otehrwise `consult-find'." (interactive "P") @@ -686,15 +610,11 @@ if ripgrep is installed, otherwise `consult-grep'." ;; Projects (:option consult-project-root-function #'vc-root-dir)) -;;;;; 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")) @@ -708,7 +628,6 @@ if ripgrep is installed, otherwise `consult-grep'." (acdw/sunrise-sunset #'modus-themes-load-operandi #'modus-themes-load-vivendi)) -;;;;; Mode line (setup (:straight simple-modeline) (setup (:straight minions)) (:option simple-modeline-segments @@ -725,22 +644,13 @@ if ripgrep is installed, otherwise `consult-grep'." (require 'acdw-modeline) (simple-modeline-mode +1)) -;;;;; Olivetti -;; also useful for `acdw/reading-mode' (setup (:straight olivetti) (:option olivetti-body-width (+ fill-column 4) olivetti-minimum-body-width fill-column)) -;;;;; Outshine -(setup (:straight outshine) - (:option outline-minor-mode-prefix "") - (:hook-into emacs-lisp-mode)) - -;;;;; Form-feed (setup (:straight form-feed) (global-form-feed-mode +1)) -;;;;; Which-key (setup (:straight which-key) (:option which-key-show-early-on-C-h t which-key-idle-delay 10000 @@ -748,7 +658,6 @@ if ripgrep is installed, otherwise `consult-grep'." (which-key-setup-side-window-bottom) (which-key-mode +1)) -;;;;; Helpful (setup (:straight helpful) (:global " f" helpful-callable " v" helpful-variable @@ -756,73 +665,48 @@ if ripgrep is installed, otherwise `consult-grep'." " o" helpful-symbol "C-c C-d" helpful-at-point)) -;;;; 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)) -;;;;; Flyspell-correct (with-eval-after-load 'flyspell (setup (:straight flyspell-correct) (define-key flyspell-mode-map (kbd "C-;") #'flyspell-correct-wrapper))) -;;;; System tie-ins -;; Insctead of using `setup''s `:needs', I'm going to wrap these in -;; `executable-find' forms. I don't want to waste time with pulling packages -;; that won't work on a machine. - -;;;;; PKGBUILDs (when (eq acdw/system :home) (setup (:straight pkgbuild-mode))) - -;;; Programming languages -;; This section includes packages and other settings, because most languages' -;; packages aren't packaged with Emacs. - -;;;; Formatting (setup (:straight (apheleia :host github :repo "raxod502/apheleia")) (apheleia-global-mode +1) ;; Use a dumb formatter on modes that `apheleia' doesn't work for. - (add-hook 'before-save-hook - (defun dumb-auto-format () - "Run `indent-region' in buffers that don't have an `apheleia' - formatter set." - (unless (and (fboundp 'apheleia--get-formatter-command) - (apheleia--get-formatter-command)) - (indent-region (point-min) (point-max)))))) - -;;;; Eldoc -(setup eldoc - (:option eldoc-idle-delay 0.1 - eldoc-echo-area-use-multiline-p nil)) + (hook-defun dumb-auto-format before-save-hook + (unless (and (fboundp 'apheleia--get-formatter-command) + (apheleia--get-formatter-command)) + (indent-region (point-min) (point-max))))) -;;;; Lisps (setup (:straight paren-face) (global-paren-face-mode +1)) -;;;;; Paredit mode (setup (:straight paredit) (autoload 'enable-paredit-mode "paredit" nil t) - (dolist (hook '(emacs-lisp-mode-hook - eval-expression-minibuffer-setup-hook - ielm-mode-hook - lisp-mode-hook - lisp-interaction-mode-hook - scheme-mode-hook)) - (add-hook hook #'enable-paredit-mode)) - (add-hook 'paredit-mode-hook - (defun hook--paredit-disable-electric-pair () - (electric-pair-local-mode -1))) + + (:hook-into emacs-lisp-mode + eval-expression-minibuffer + ielm-mode + lisp-mode + lisp-interaction-mode + scheme-mode) + + (defun disable-electric-pair-local-mode () + (electric-pair-local-mode -1)) + + (:hook disable-electric-pair-local-mode) + (require 'eldoc) - (eldoc-add-command 'paredit-backward-delete 'paredit-close-round) - (:bind "" paredit-backward-delete)) + (eldoc-add-command 'paredit-backward-delete 'paredit-close-round)) -;;;;; Emacs lisp (setup elisp-mode (:option eval-expression-print-length nil eval-expression-print-level nil @@ -839,34 +723,25 @@ if ripgrep is installed, otherwise `consult-grep'." "C-c C-z" ielm)) (add-hook 'emacs-lisp-mode-hook 'turn-on-eldoc-mode) - (add-hook 'ielm-mode-hook 'turn-on-eldoc-mode)) + (add-hook 'ielm-mode-hook 'turn-on-eldoc-mode) -(setup (:straight macrostep) - (define-key emacs-lisp-mode-map (kbd "C-c e") #'macrostep-expand)) + (setup (:straight macrostep) + (define-key emacs-lisp-mode-map (kbd "C-c e") #'macrostep-expand)) -(setup (:straight eros) - (:hook-into emacs-lisp-mode)) + (setup (:straight eros) + (:hook-into emacs-lisp-mode))) -;;;;; Fennel (when (executable-find "fennel") (setup (:straight fennel-mode) (autoload 'fennel-mode "fennel-mode" nil t) (:option (append auto-mode-alist) '("\\.fnl\\'" . fennel-mode)) (:bind "C-c C-c" ))) -;;;;; Scheme -(when (or (executable-find "guile") - (executable-find "csi") - (executable-find "racket")) - (setup (:straight geiser) - (:with-mode geiser-repl-mode - (:hook enable-paredit-mode)))) +(setup (:straight geiser)) -;;;; Lua (setup (:straight lua-mode) (:option (append auto-mode-alist) '("\\.lua\\'" . lua-mode))) -;;;; Shell scripts (setup sh-mode (:option sh-basic-offset tab-width sh-indent-after-case 0 @@ -884,7 +759,6 @@ if ripgrep is installed, otherwise `consult-grep'." (:hook flymake-mode flymake-shellcheck-load))) -;;;; Web languages (setup (:straight web-mode) (:option css-level-offset 2 js-indent-level 2 @@ -899,11 +773,8 @@ if ripgrep is installed, otherwise `consult-grep'." (add-to-list 'auto-mode-alist `(,(concat "\\." extension "\\'") . web-mode)))) -;;;; FORTH (when (locate-library "gforth") (autoload 'forth-mode "gforth") (add-to-list 'auto-mode-alist '("\\.fs\\'" . forth-mode)) (autoload 'forth-block-mode "gforth") (add-to-list 'auto-mode-alist '("\\.fb\\'" . forth-block-mode))) - -;;;- init.el ends here diff --git a/lisp/acdw.el b/lisp/acdw.el index d412b4b..69f9a7f 100644 --- a/lisp/acdw.el +++ b/lisp/acdw.el @@ -18,139 +18,79 @@ ;; functions for me, acdw. ;;; Code: + +;; Utility constants -;;; Utilities - -;;;; Determine the system I'm on (defconst acdw/system (pcase system-type - ('gnu/linux :home) - ((or 'msdos 'windows-nt) :work) - (_ :other)) - "Which system is currently being used.") - -;;;; Run commands only when unfocused -(defun acdw/when-unfocused (func &rest args) - "Call FUNC, with ARGS, iff all Emacs frames are out of focus. - -Ready for use with `after-focus-change-function'." - (when (seq-every-p #'null (mapcar #'frame-focus-state (frame-list))) - (apply func args))) - -;;;; Run commands at sunrise and sunset -(defun acdw/sunrise-sunset (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 "12:00am" (* 60 60 24) sunset-command))) - -;;;; Define a function and add it to hooks -(defun defun-with-hooks (hooks function-def) - "Add FUNCTION-DEF to HOOKS. + ('gnu/linux :home) + ((or 'msdos 'windows-nt) :work) + (_ :other)) + "Which computer system is currently being used.") + + +;; Utility functions + +(defmacro when-unfocused (name &rest forms) + "Define a function NAME, executing FORMS, that fires when Emacs +is unfocused." + (declare (indent 1)) + (let ((func-name (intern (concat "when-unfocused-" (symbol-name name))))) + `(progn + (defun ,func-name () "Defined by `when-unfocused'." + (when (seq-every-p #'null + (mapcar #'frame-focus-state (frame-list))) + ,@forms)) + (add-function :after after-focus-change-function #',func-name)))) + +(defmacro hook-defun (name hooks &rest forms) + "Define a function NAME that executes FORMS, and add it to +each hook in HOOKS." + (declare (indent 2)) + (let ((func-name (intern (concat "hook-defun-" (symbol-name name)))) + (hook-list (if (consp hooks) hooks (list hooks))) + (hook-defun-add-hook-list)) + `(progn + (defun ,func-name () "Defined by `hook-defun'." ,@forms) + ,@(dolist (hook hook-list hook-defun-add-hook-list) + (push `(add-hook ',hook #',func-name) hook-defun-add-hook-list))))) + +(defun refresh-emacs () + "Reload Emacs's configuration files." + (interactive) + (dolist (file (list (locate-user-emacs-file "early-init.el") + (locate-user-emacs-file "init.el" ".emacs"))) + (when (file-exists-p file) + (load-file file)))) + +(defun acdw/dir (&optional file make-directory) + "Place Emacs files in one place. + +If called without parameters, `acdw/dir' expands to +~/.emacs.d/var or similar. If called with FILE, `acdw/dir' +expands FILE to ~/.emacs.d/var, optionally making its directory +if MAKE-DIRECTORY is non-nil." + (let ((dir (expand-file-name (convert-standard-filename "var/") + user-emacs-directory))) + (if file + (let ((file-name (expand-file-name (convert-standard-filename file) + dir))) + (when make-directory + (make-directory (file-name-directory file-name) 'parents)) + file-name) + dir))) -FUNCTION-DEF should be a `defun' form. This function is just to - put functions that only exist for hooks closer to the hooks - they bind to." - (let ((func function-def)) - (dolist (hook hooks) - (add-hook hook func)))) - -;;; Garbage collection hacks - -(defconst acdw/gc-cons-threshold-basis (* 800 1024) - "Basis value for `gc-cons-threshold' to return to after jumping. -800 KB is Emacs's default.") - -(defconst acdw/gc-cons-percentage-basis 0.1 - "Basis value for `gc-cons-percentage' to return to after jumping. -0.1 is Emacs's default.") +(defun acdw/gc-enable () + "Enable the Garbage collector." + (setq gc-cons-threshold (* 800 1024 1024) + gc-cons-percentage 0.1)) (defun acdw/gc-disable () - "Disable garbage collection by setting relevant variables to their maxima." + "Functionally disable the Garbage collector." (setq gc-cons-threshold most-positive-fixnum - gc-cons-percentage 0.8)) + gc-cons-percentage 0.8)) -(defun acdw/gc-enable () - "Re-enable garbage collection by setting relevant variables back to bases." - (setq gc-cons-threshold acdw/gc-cons-threshold-basis - gc-cons-percentage acdw/gc-cons-percentage-basis)) - -;;; Directories (think `no-littering') - -(defvar acdw/dir (expand-file-name - (convert-standard-filename "var/") - user-emacs-directory) - "A directory to hold extra configuration and emacs data.") - -(defun acdw/in-dir (file &optional make-directory) - "Expand FILE relative to `acdw/dir', optionally creating its -directory." - (let ((f (expand-file-name (convert-standard-filename file) - acdw/dir))) - (when make-directory - (make-directory (file-name-directory f) 'parents)) - f)) - -;;; Reading mode - -(define-minor-mode acdw/reading-mode - "A mode for reading." - :init-value t - :lighter " Read" - (if acdw/reading-mode - (progn ;; turn on - ;; settings - (setq-local mode-line-format - '(:eval - (let* ((fmt " Reading %b (%m) ") - (len (length (format-mode-line fmt)))) - (concat - (propertize " " - 'display `((space :align-to (- right - ,len))) - 'face '(:inherit italic)) - fmt)))) - ;; modes to disable - (dolist (mode '(display-fill-column-indicator-mode)) - (when (fboundp mode) - (funcall mode -1))) - ;; modes to enable - (dolist (mode '(iscroll-mode - olivetti-mode)) - (when (fboundp mode) - (funcall mode +1)))) - ;; turn off - ;; settings - (kill-local-variable 'mode-line-format) - ;; modes to re-enable - (dolist (mode '(display-fill-column-indicator-mode - simple-modeline-mode)) - (when (fboundp mode) - (funcall mode +1))) - ;; modes to re-disable - (dolist (mode '(olivetti-mode - iscroll-mode)) - (when (fboundp mode) - (funcall mode -1))) - (force-mode-line-update))) - -;;; Keymap & Mode - -;; Set up a leader key for `acdw/mode' + +;; Make `C-z' more useful (defvar acdw/leader (let ((map (make-sparse-keymap)) (c-z (global-key-binding "\C-z"))) @@ -159,5 +99,4 @@ directory." map)) (provide 'acdw) - ;;; acdw.el ends here -- cgit 1.4.1-21-gabe81