1764 lines
		
	
	
		
			68 KiB
		
	
	
	
		
			EmacsLisp
		
	
	
	
	
	
			
		
		
	
	
			1764 lines
		
	
	
		
			68 KiB
		
	
	
	
		
			EmacsLisp
		
	
	
	
	
	
| ;;; init.el --- Emacs configuration file -*- lexical-binding: t; -*-
 | |
| ;;
 | |
| ;; Author: Jessie Hildebrandt <jessieh.net>
 | |
| ;; Homepage: https://gitlab.com/jessieh/dot-emacs
 | |
| ;; Package-Requires: ((emacs "29.1"))
 | |
| ;;
 | |
| ;; This file is not part of GNU Emacs.
 | |
| 
 | |
| ;;; Commentary:
 | |
| ;;
 | |
| ;; jessieh's (Mostly) Portable Emacs Config
 | |
| ;;
 | |
| ;; This configuration file was designed to work with Emacs 29.1, and will not
 | |
| ;; work with earlier versions.  You should probably have native-comp enabled.
 | |
| ;;
 | |
| ;; This file will automatically generate an early-init.el file upon first run,
 | |
| ;; or if early-init.el is missing.
 | |
| ;;
 | |
| ;; All configuration efforts here are organized around the use of wrapped
 | |
| ;; `use-package' macros.  The keyword order for any config entry should be:
 | |
| ;;  - preface
 | |
| ;;  - if/when/unless
 | |
| ;;  - demand
 | |
| ;;  - after
 | |
| ;;  - requires
 | |
| ;;  - defines/functions
 | |
| ;;  - init/config
 | |
| ;;  - custom/custom-faces
 | |
| ;;  - commands
 | |
| ;;  - hook/interpreter/mode
 | |
| ;;  - bind/bind*/bind-keymap/bind-keymap*
 | |
| ;;
 | |
| ;; Custom functions and variables should generally be prefixed with "user/".
 | |
| ;; This prefix is purposely nonidiomatic so as to avoid collisions with any
 | |
| ;; package prefixes, inbuilt or otherwise.
 | |
| 
 | |
| ;;; License:
 | |
| ;;
 | |
| ;; This program is free software; you can redistribute it and/or
 | |
| ;; modify it under the terms of the GNU General Public License as
 | |
| ;; published by the Free Software Foundation; either version 2, or
 | |
| ;; (at your option) any later version.
 | |
| ;;
 | |
| ;; This program is distributed in the hope that it will be useful,
 | |
| ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
| ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | |
| ;; General Public License for more details.
 | |
| ;;
 | |
| ;; You should have received a copy of the GNU General Public License
 | |
| ;; along with this program; see the file COPYING.  If not, write to
 | |
| ;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth
 | |
| ;; Floor, Boston, MA 02110-1301, USA.
 | |
| 
 | |
| ;;; Code:
 | |
| 
 | |
| ;; -------------------------------------------------------------------------- ;;
 | |
| ;;
 | |
| ;; Startup
 | |
| ;;
 | |
| ;; -------------------------------------------------------------------------- ;;
 | |
| 
 | |
| (defconst user/init-file (locate-user-emacs-file "init.el") "Location of user init file.")
 | |
| (defconst user/early-init-file (locate-user-emacs-file "early-init.el") "Location of user early-init file.")
 | |
| 
 | |
| ;; ---------------------------------- ;;
 | |
| ;; early-init.el file creation
 | |
| ;; ---------------------------------- ;;
 | |
| 
 | |
| (defvar user/early-init-forms
 | |
|   `(";;; early-init.el --- Early initialization -*- lexical-binding: t; -*-"
 | |
| 
 | |
|     ";; This file is not part of GNU Emacs."
 | |
| 
 | |
|     ";;; Commentary:"
 | |
| 
 | |
|     ";; This file has been automatically generated by init.el."
 | |
|     ";; More relevant commentary is likely in init.el."
 | |
| 
 | |
|     ";;; Code:"
 | |
| 
 | |
|     (defconst user/default-gc-cons-threshold gc-cons-threshold)
 | |
|     (defconst user/default-gc-cons-percentage gc-cons-percentage)
 | |
| 
 | |
|     (defun user/defer-garbage-collection ()
 | |
|       "Defer garbage collection by maximizing the collection threshold."
 | |
|       (setq gc-cons-threshold most-positive-fixnum
 | |
|             gc-cons-percentage 1.0))
 | |
|     (defun user/restore-garbage-collection ()
 | |
|       "Restore the garbage collection threshold parameters to their default values."
 | |
|       (setq gc-cons-threshold user/default-gc-cons-threshold
 | |
|             gc-cons-percentage user/default-gc-cons-percentage))
 | |
| 
 | |
|     ";; Defer garbage collection until after initialization"
 | |
|     (user/defer-garbage-collection)
 | |
|     (add-hook 'emacs-startup-hook #'user/restore-garbage-collection)
 | |
| 
 | |
|     ";; Clear `file-name-handler-alist' until after initialization"
 | |
|     (let ((user/file-name-handler-alist file-name-handler-alist))
 | |
|       (setq file-name-handler-alist nil)
 | |
|       (add-hook 'emacs-startup-hook (lambda () (setq file-name-handler-alist user/file-name-handler-alist))))
 | |
| 
 | |
|     ";; Configure GUI components before initial frame creation"
 | |
|     (setq font-use-system-font t
 | |
|           pgtk-wait-for-event-timeout 0
 | |
|           frame-inhibit-implied-resize t
 | |
|           default-frame-alist '((width . 100)
 | |
|                                 (height . 40)
 | |
|                                 (menu-bar-lines . 0)
 | |
|                                 (tool-bar-lines . 0)
 | |
|                                 (vertical-scroll-bars . nil)
 | |
|                                 (background-color . "gray15")
 | |
|                                 (foreground-color . "gray85")))
 | |
| 
 | |
|     ";; Prevent system-provided configuration files from loading"
 | |
|     (setq site-run-file nil
 | |
|           inhibit-default-init t)
 | |
| 
 | |
|     ";; package.el initialization is handled manually in init.el"
 | |
|     (setq package-enable-at-startup nil)
 | |
| 
 | |
|     ";;; init.el ends here")
 | |
|   "List of forms that are written to the user early-init file.")
 | |
| 
 | |
| (defun user/write-forms-to-file (forms file)
 | |
|   "Format and pretty-print list FORMS to FILE."
 | |
|   (with-temp-file file
 | |
|     (mapcar (lambda (form)
 | |
|               (insert (if (stringp form)
 | |
|                           (prin1-to-string form :no-escape)
 | |
|                         (pp-to-string form))
 | |
|                       "\n"))
 | |
|             forms)))
 | |
| 
 | |
| ;; Create (and load) early-init file if it does not yet exist, or if this file
 | |
| ;; is currently being recompiled. We recreate early-init.el during compilation
 | |
| ;; in case `user/early-init-forms' has been changed
 | |
| (unless (file-exists-p user/early-init-file)
 | |
|   (user/write-forms-to-file user/early-init-forms user/early-init-file)
 | |
|   (load user/early-init-file nil nil :no-suffix))
 | |
| 
 | |
| ;; Make sure that the user init files are byte-compiled.
 | |
| (when (file-newer-than-file-p user/early-init-file (concat user/early-init-file "c"))
 | |
|   (byte-compile-file user/early-init-file))
 | |
| (when (file-newer-than-file-p user/init-file (concat user/init-file "c"))
 | |
|   (byte-compile-file user/init-file))
 | |
| 
 | |
| ;; ---------------------------------- ;;
 | |
| ;; Package manager initialization
 | |
| ;; ---------------------------------- ;;
 | |
| 
 | |
| ;; To be evaluated at both compile time and run time
 | |
| (eval-and-compile
 | |
|   (setq package-user-dir (locate-user-emacs-file "packages/")
 | |
|         package-native-compile t
 | |
|         package-check-signature nil
 | |
|         use-package-hook-name-suffix nil
 | |
|         use-package-always-demand (daemonp)
 | |
|         native-comp-compiler-options "-march=native -flto"))
 | |
| 
 | |
| ;; To be evaluated only at compile time
 | |
| (eval-when-compile
 | |
| 
 | |
|   ;; Initialize package.el
 | |
|   (require 'package)
 | |
|   (require 'use-package)
 | |
|   (add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t)
 | |
|   (unless (bound-and-true-p package--initialized)
 | |
|     (package-initialize))
 | |
|   (package-refresh-contents)
 | |
| 
 | |
|   ;; After initialization, build quickstart file containing package autoload defs
 | |
|   ;; and compile it alongside any other uncompiled Elisp files in installed packages
 | |
|   (add-hook 'emacs-startup-hook (lambda ()
 | |
|                                   (let ((byte-compile-warnings nil))
 | |
|                                     (byte-recompile-directory package-user-dir 0)
 | |
|                                     (package-quickstart-refresh)))))
 | |
| 
 | |
| ;; Load "package-quickstart.el" file containing package autoload defs with a
 | |
| ;; call to `package-activate-all'. This is must be done to load any installed
 | |
| ;; packages during initialization outside of compile time because we won't have
 | |
| ;; called `package-initialize'. We also require `bind-key' here, which the
 | |
| ;; compiled `use-package' macros still rely on for handling keybinds
 | |
| (unless (bound-and-true-p package--initialized)
 | |
|   (package-activate-all)
 | |
|   (require 'bind-key))
 | |
| 
 | |
| ;; -------------------------------------------------------------------------- ;;
 | |
| ;;
 | |
| ;; User commands
 | |
| ;;
 | |
| ;; -------------------------------------------------------------------------- ;;
 | |
| 
 | |
| (defun user/open-init-file ()
 | |
|   "Open the user init file in a new buffer."
 | |
|   (interactive)
 | |
|   (find-file user/init-file))
 | |
| 
 | |
| (defun user/byte-compile-init-files ()
 | |
|   "Byte-compile the user init and early-init files."
 | |
|   (interactive)
 | |
|   (message "Byte-compiling init and early-init file...")
 | |
|   (byte-compile-file user/init-file)
 | |
|   (byte-compile-file user/early-init-file))
 | |
| 
 | |
| (defun user/refresh-packages ()
 | |
|   "Refresh packages that have been configured for use in the user init file.
 | |
| This is accomplished by deleting `package-user-dir' and recompiling the user
 | |
| init file, which initializes the package manager during compile time."
 | |
|   (interactive)
 | |
|   (when (yes-or-no-p "Redownload and refresh packages? ")
 | |
|     (message "Refreshing packages...")
 | |
|     (delete-directory package-user-dir :recursive)
 | |
|     (user/byte-compile-init-files)))
 | |
| 
 | |
| ;; -------------------------------------------------------------------------- ;;
 | |
| ;;
 | |
| ;; Internal/built-in packages
 | |
| ;;
 | |
| ;; -------------------------------------------------------------------------- ;;
 | |
| 
 | |
| (defmacro editor-feature (name docstring &rest args)
 | |
|   "Apply NAME and ARGS to `use-package' with `:ensure' defaulted to nil.
 | |
| DOCSTRING is an optional form that is discarded upon expansion."
 | |
|   (declare (doc-string 2)
 | |
|            (indent defun))
 | |
|   (ignore docstring)
 | |
|   `(use-package ,name :ensure nil ,@args))
 | |
| 
 | |
| ;; ---------------------------------- ;;
 | |
| ;; emacs
 | |
| ;; ---------------------------------- ;;
 | |
| 
 | |
| (editor-feature emacs
 | |
|   "Provides an extensible, customizable, self-documenting real-time display editor."
 | |
| 
 | |
|   :preface
 | |
|   (defconst user/load-directory (locate-user-emacs-file "load/") "Location of extra user Lisp files.")
 | |
|   (defun user/indent-buffer ()
 | |
|     "Call `indent-region' on the contents of the active buffer."
 | |
|     (interactive)
 | |
|     (indent-region (point-min) (point-max)))
 | |
|   (defun user/select-minibuffer-window ()
 | |
|     "Select the minibuffer window if it is active."
 | |
|     (interactive)
 | |
|     (when (active-minibuffer-window)
 | |
|       (select-window (active-minibuffer-window))))
 | |
|   (defun user/scratch-buffer ()
 | |
|     "Open the scratch buffer, (re)creating it if not present."
 | |
|     (interactive)
 | |
|     (pop-to-buffer (get-scratch-buffer-create)))
 | |
|   (defun user/ensure-region-active (func &rest args)
 | |
|     "Apply ARGS to FUNC only if `region-active-p' is non-nil."
 | |
|     (when (region-active-p)
 | |
|       (apply func args)))
 | |
| 
 | |
|   :init
 | |
|   ;; Add user dir to `load-path'
 | |
|   (add-to-list 'load-path user/load-directory)
 | |
| 
 | |
|   ;; Use default system monospace and proportional fonts
 | |
|   (set-face-font 'fixed-pitch (font-get-system-font))
 | |
|   (set-face-font 'variable-pitch (font-get-system-normal-font))
 | |
| 
 | |
|   :config
 | |
|   ;; Override echo area greeting function
 | |
|   (defun display-startup-echo-area-message () (message ""))
 | |
| 
 | |
|   :custom
 | |
|   ;; Default working directory
 | |
|   (default-directory (if (eq system-type 'windows-nt)
 | |
|                          (setq default-directory (getenv "USERPROFILE"))
 | |
|                        (setq default-directory "~/")))
 | |
| 
 | |
|   ;; General configuration
 | |
|   (frame-title-format '("%b — Emacs") "Set frame title to buffer name")
 | |
|   (truncate-lines t "Truncate lines instead of wrapping")
 | |
|   (context-menu-mode t "Enable global context menu support")
 | |
|   (message-truncate-lines t "Truncate messages in the echo area")
 | |
|   (cursor-in-non-selected-windows nil "Hide cursor in inactive windows")
 | |
|   (ring-bell-function 'ignore "Disable terminal bell")
 | |
|   (line-spacing 0.20 "Add 20% extra space between lines")
 | |
|   (fill-column 80 "Set default line-wrap column to column 80")
 | |
|   (help-window-select t "Focus help windows after summoning them")
 | |
|   (max-mini-window-height 10 "Limit minibuffer height to 10 lines")
 | |
|   (tab-always-indent 'complete "Allow Tab key to indent current line or complete a symbol")
 | |
|   (enable-recursive-minibuffers t "Allow minibuffer commands to be called in the minibuffer")
 | |
|   (disabled-command-function nil "Enable invocation of all disabled interactive commands")
 | |
|   (use-system-tooltips t "Enable use of system tooltips in favor of Emacs tooltips")
 | |
| 
 | |
|   ;; Startup
 | |
|   (initial-scratch-message "" "Leave scratch buffer empty on startup")
 | |
|   (initial-major-mode 'fundamental-mode "Set initial mode to fundamental-mode on startup")
 | |
|   (inhibit-startup-screen t "Do not create or show the initial splash screen")
 | |
|   (inhibit-default-init t "Do not attempt to load any OS-provided init files")
 | |
| 
 | |
|   ;; Default style rules
 | |
|   (sentence-end-double-space nil "Do not use double spacing between sentences in paragraphs")
 | |
|   (require-final-newline t "Require a terminating newline at the end of every file")
 | |
|   (indent-tabs-mode nil "Use spaces for indentation")
 | |
|   (tab-width 4 "Use 4 spaces for indentation")
 | |
| 
 | |
|   ;; Scrolling
 | |
|   (mouse-wheel-progressive-speed nil "Disable mouse wheel acceleration during scrolling")
 | |
|   (scroll-preserve-screen-position 'always "Prevent the cursor from moving during scrolling")
 | |
|   (scroll-conservatively 101 "Scroll only one line at a time when cursor leaves view")
 | |
|   (scroll-margin 5 "Maintain margin of 5 lines around cursor during scrolling")
 | |
| 
 | |
|   ;; Performance tweaks
 | |
|   (redisplay-skip-fontification-on-input t "Improve redisplay performance while scrolling")
 | |
|   (fast-but-imprecise-scrolling t "Improve redisplay performance while scrolling")
 | |
|   (jit-lock-defer-time 0 "Defer fontification while input is pending")
 | |
|   (auto-window-vscroll nil "Prevent calcuation of arbitrary line heights while scrolling")
 | |
|   (auto-mode-case-fold nil "Disable case-insensitive second pass over `auto-mode-alist'")
 | |
| 
 | |
|   ;; Bidirectional text
 | |
|   (bidi-inhibit-bpa t "Disable parentheses matching for bidirectional text")
 | |
|   (bidi-display-reordering 'left-to-right "Force global left-to-right text direction")
 | |
|   (bidi-paragraph-direction 'left-to-right "Disable paragraph directionality detection")
 | |
| 
 | |
|   ;; Elisp compilation warnings
 | |
|   (native-comp-async-report-warnings-errors nil "Don't report errors from async native compilation")
 | |
|   (byte-compile-warnings '(not lexical
 | |
|                                free-vars
 | |
|                                noruntime
 | |
|                                unresolved
 | |
|                                docstrings))
 | |
| 
 | |
|   ;; Filter out buffer-incompatible interactive commands by default
 | |
|   (read-extended-command-predicate #'command-completion-default-include-p)
 | |
| 
 | |
|   :bind
 | |
|   ;; General binds
 | |
|   ("C-z" . undo)
 | |
|   ("C-x C-z" . nil)
 | |
|   ("C-c \\" . user/indent-buffer)
 | |
| 
 | |
|   ;; Cursor navigation
 | |
|   ("M-n" . scroll-up-line)
 | |
|   ("M-p" . scroll-down-line)
 | |
|   ("C-M-n" . forward-paragraph)
 | |
|   ("C-M-p" . backward-paragraph)
 | |
| 
 | |
|   :bind*
 | |
|   ;; General binds
 | |
|   ("C-c DEL" . fixup-whitespace)
 | |
|   ("C-c d" . delete-pair)
 | |
|   ("C-c ." . emoji-search)
 | |
|   ("C-c i" . overwrite-mode)
 | |
|   ("C-c v" . visual-line-mode)
 | |
|   ("C-c r" . revert-buffer-quick)
 | |
|   ("C-c f" . display-fill-column-indicator-mode)
 | |
|   ("C-c o" . user/select-minibuffer-window)
 | |
|   ("C-c s" . user/scratch-buffer)
 | |
|   ("C-c m" . (lambda () (interactive) (message "%s is in %s" (buffer-name) major-mode)))
 | |
| 
 | |
|   ;; Window navigation
 | |
|   ("C-c C-i" . windmove-up)
 | |
|   ("C-c C-k" . windmove-down)
 | |
|   ("C-c C-j" . windmove-left)
 | |
|   ("C-c C-l" . windmove-right)
 | |
| 
 | |
|   :hook
 | |
|   ;; Defer garbage collection while the minibuffer is active
 | |
|   ;; (Many interactive commands are memory-intensive and trigger many GC cycles)
 | |
|   (minibuffer-setup-hook . user/defer-garbage-collection)
 | |
|   (minibuffer-exit-hook . (lambda () (run-at-time 1 nil #'user/restore-garbage-collection))))
 | |
| 
 | |
| ;; ---------------------------------- ;;
 | |
| ;; autorevert
 | |
| ;; ---------------------------------- ;;
 | |
| 
 | |
| (editor-feature autorevert
 | |
|   "Displays on-disk changes to files in unmodified buffers automatically."
 | |
|   :config
 | |
|   (global-auto-revert-mode))
 | |
| 
 | |
| ;; ---------------------------------- ;;
 | |
| ;; c-ts-mode
 | |
| ;; ---------------------------------- ;;
 | |
| 
 | |
| (editor-feature c-ts-mode
 | |
|   "Major mode(s) for C and C++, using the Tree-sitter parsing library."
 | |
|   :preface
 | |
|   (defun user/c-ts-indent-style()
 | |
|     "Provide custom `c-ts-mode' indentation style."
 | |
|     `(;; Align function arguments to the start of the first one, offset if standalone
 | |
|       ((match nil "argument_list" nil 1 1) parent-bol c-ts-mode-indent-offset)
 | |
|       ((parent-is "argument_list") (nth-sibling 1) 0)
 | |
|       ;; Ditto for parameters
 | |
|       ((match nil "parameter_list" nil 1 1) parent-bol c-ts-mode-indent-offset)
 | |
|       ((parent-is "parameter_list") (nth-sibling 1) 0)
 | |
|       ;; Indent inside case blocks
 | |
|       ((parent-is "case_statement") standalone-parent c-ts-mode-indent-offset)
 | |
|       ;; Do not indent preprocessor statements or within namespaces
 | |
|       ((node-is "preproc") column-0 0)
 | |
|       ((n-p-gp nil nil "namespace_definition") grand-parent 0)
 | |
|       ;; Append to BSD style
 | |
|       ,@(alist-get 'bsd (c-ts-mode--indent-styles 'cpp))))
 | |
|   :custom
 | |
|   (c-ts-mode-indent-offset 2 "Use 2 spaces for indenting C and C++ code")
 | |
|   (c-ts-mode-indent-style #'user/c-ts-indent-style "Use custom indentation rules for C and C++")
 | |
|   :mode
 | |
|   ("\\.cppm\\'" . c++-ts-mode))
 | |
| 
 | |
| ;; ---------------------------------- ;;
 | |
| ;; cmake-ts-mode
 | |
| ;; ---------------------------------- ;;
 | |
| 
 | |
| (editor-feature cmake-ts-mode
 | |
|   "Major mode for CMake files, using the Tree-sitter parsing library."
 | |
|   :mode
 | |
|   ("\\CMakeLists.txt\\'" . cmake-ts-mode))
 | |
| 
 | |
| ;; ---------------------------------- ;;
 | |
| ;; compile
 | |
| ;; ---------------------------------- ;;
 | |
| 
 | |
| (editor-feature compile
 | |
|   "Provides shortcuts for running and parsing compiler output."
 | |
|   :hook
 | |
|   (compilation-filter-hook . ansi-color-compilation-filter))
 | |
| 
 | |
| ;; ---------------------------------- ;;
 | |
| ;; display-line-numbers
 | |
| ;; ---------------------------------- ;;
 | |
| 
 | |
| (editor-feature display-line-numbers
 | |
|   "Displays the absolute number of each line in a buffer."
 | |
|   :custom
 | |
|   (display-line-numbers-grow-only t "Do not decrease the width of the line number column after it has grown")
 | |
|   (display-line-numbers-width-start 100 "Count number of lines (+100) in buffer for initial line number width")
 | |
|   :hook
 | |
|   (prog-mode-hook . (lambda ()
 | |
|                       (unless (derived-mode-p 'lisp-interaction-mode)
 | |
|                         (display-line-numbers-mode))))
 | |
|   :bind
 | |
|   ("C-c n" . display-line-numbers-mode))
 | |
| 
 | |
| ;; ---------------------------------- ;;
 | |
| ;; eglot
 | |
| ;; ---------------------------------- ;;
 | |
| 
 | |
| (editor-feature eglot
 | |
|   "Integrates LSP client features into Emacs."
 | |
|   :preface
 | |
|   (defun user/set-up-eglot ()
 | |
|     "Set up buffer-local variables for current eglot session."
 | |
|     (setq eldoc-documentation-functions '(flymake-eldoc-function
 | |
|                                           eglot-signature-eldoc-function
 | |
|                                           eglot-hover-eldoc-function))
 | |
|     (eglot-inlay-hints-mode -1))
 | |
|   :config
 | |
|   (add-to-list 'eglot-server-programs `(svelte-mode . ("svelteserver" "--stdio")))
 | |
|   ;; Remove the mode-line segment that Eglot adds
 | |
|   (setq mode-line-misc-info (assoc-delete-all 'eglot--managed-mode mode-line-misc-info))
 | |
|   :hook
 | |
|   (c-ts-base-mode-hook . eglot-ensure)
 | |
|   (gdscript-mode-hook . eglot-ensure)
 | |
|   (js-ts-mode-hook . eglot-ensure)
 | |
|   (rust-ts-mode-hook . eglot-ensure)
 | |
|   (svelte-mode-hook . eglot-ensure)
 | |
|   (typescript-mode-hook . eglot-ensure)
 | |
|   (eglot-managed-mode-hook . user/set-up-eglot)
 | |
|   :bind
 | |
|   (:map eglot-mode-map
 | |
|         ("C-c \\" . eglot-format-buffer)
 | |
|         ("C-c q" . eglot-code-action-quickfix)
 | |
|         ("C-c C-q" . eglot-code-actions)
 | |
|         ("M-s r ." . eglot-rename)))
 | |
| 
 | |
| ;; ---------------------------------- ;;
 | |
| ;; elec-pair
 | |
| ;; ---------------------------------- ;;
 | |
| 
 | |
| (editor-feature elec-pair
 | |
|   "Enables automatic pairing of most paired delimiters."
 | |
|   :hook
 | |
|   (prog-mode-hook . electric-pair-local-mode))
 | |
| 
 | |
| ;; ---------------------------------- ;;
 | |
| ;; eshell
 | |
| ;; ---------------------------------- ;;
 | |
| 
 | |
| (editor-feature eshell
 | |
|   "Provides a shell-like interpreter that can process shell or Lisp commands."
 | |
|   :preface
 | |
|   (defun user/open-eshell (&optional force-new-session)
 | |
|     "Focus the last active `eshell' session, or start a new one if none available.
 | |
| When FORCE-NEW-SESSION is non-nil, a new session will be started even if there
 | |
| is already an active session to bring into focus."
 | |
|     (interactive)
 | |
|     (let ((eshell-buffer (cl-find-if (lambda (buffer)
 | |
|                                        (eq (buffer-local-value 'major-mode buffer)
 | |
|                                            'eshell-mode))
 | |
|                                      (buffer-list))))
 | |
|       (if eshell-buffer
 | |
|           (if force-new-session
 | |
|               (let ((current-buffer-directory default-directory))
 | |
|                 (pop-to-buffer eshell-buffer)
 | |
|                 (let ((default-directory current-buffer-directory))
 | |
|                   (eshell :new-session)))
 | |
|             (pop-to-buffer eshell-buffer))
 | |
|         (eshell))))
 | |
|   (defun user/auto-toggle-eshell-buffer-tabs ()
 | |
|     "Enable `tab-line-mode' in all `eshell-mode' buffers if multiple are open.
 | |
| Also automatically disables `tab-line-mode' if all but one `eshell-mode' buffer
 | |
| have been closed."
 | |
|     (let ((eshell-buffers (cl-remove-if-not (lambda (buffer)
 | |
|                                               (eq (buffer-local-value 'major-mode buffer)
 | |
|                                                   'eshell-mode))
 | |
|                                             (buffer-list))))
 | |
|       (mapc (lambda (buffer)
 | |
|               (with-current-buffer buffer
 | |
|                 (tab-line-mode (and (length< eshell-buffers 2) -1))))
 | |
|             eshell-buffers)))
 | |
|   (defun user/prettify-eshell-pwd (path)
 | |
|     "Return PATH formatted and with all but the last directory name abbreviated."
 | |
|     (let* ((components (split-string (abbreviate-file-name path) "/"))
 | |
|            (str ""))
 | |
|       (while (cdr components)
 | |
|         (setq str (concat str
 | |
|                           (cond ((= 0 (length (car components)))
 | |
|                                  "/")
 | |
|                                 ((= 1 (length (car components)))
 | |
|                                  (concat (car components) "/"))
 | |
|                                 (t
 | |
|                                  (if (string= "." (string (elt (car components) 0)))
 | |
|                                      (concat (substring (car components) 0 2) "/")
 | |
|                                    (string (elt (car components) 0) ?/)))))
 | |
|               components (cdr components)))
 | |
|       (concat (propertize str 'face 'font-lock-comment-face)
 | |
|               (propertize (cl-reduce (lambda (a b) (concat a "/" b))
 | |
|                                      components)
 | |
|                           'face 'font-lock-doc-face))))
 | |
|   :custom
 | |
|   (eshell-prompt-regexp "^[^#❱\n]* [#❱] ")
 | |
|   (eshell-visual-commands '("fish" "bash"
 | |
|                             "ssh" "mosh"
 | |
|                             "fzf" "top" "htop" "less"))
 | |
|   (eshell-destroy-buffer-when-process-dies t "Destroys child buffers after their process returns")
 | |
|   (eshell-error-if-no-glob t "Produces an error if a glob pattern fails to match, like zsh")
 | |
|   (eshell-hist-ignoredups t "Treat multiple repeating history entries as a single entry")
 | |
|   (eshell-banner-message '(if (executable-find "fortune")
 | |
|                               (shell-command-to-string "fortune -s computers")
 | |
|                             "Hello, commander."))
 | |
|   (eshell-prompt-function (lambda () (concat "\n" (user/prettify-eshell-pwd (eshell/pwd))
 | |
|                                              (if (= (user-uid) 0) " # " " ❱ "))))
 | |
|   :hook
 | |
|   (eshell-post-command-hook . (lambda () (rename-buffer (concat eshell-buffer-name " "
 | |
|                                                                 (user/prettify-eshell-pwd (eshell/pwd)))
 | |
|                                                         :unique)))
 | |
|   (eshell-mode-hook . (lambda () (setq-local global-hl-line-mode nil
 | |
|                                              tab-line-tabs-function #'tab-line-tabs-mode-buffers)))
 | |
|   (buffer-list-update-hook . user/auto-toggle-eshell-buffer-tabs)
 | |
|   :bind*
 | |
|   ("C-c RET" . user/open-eshell)
 | |
|   ("C-c C-<return>" . (lambda () (interactive) (user/open-eshell :force-new-session))))
 | |
| 
 | |
| ;; `eshell-mode-map' is not available until the `esh-mode' module is loaded, so
 | |
| ;; the local keymap bindings are set up here
 | |
| 
 | |
| (editor-feature esh-mode
 | |
|   "Handles input from the user and provides `eshell-mode' for eshell."
 | |
|   :bind
 | |
|   (:map eshell-mode-map
 | |
|         ("C-<return>" . eshell-copy-old-input)
 | |
|         ("C-c j" . tab-line-switch-to-prev-tab)
 | |
|         ("C-c l" . tab-line-switch-to-next-tab)))
 | |
| 
 | |
| ;; `eshell-command-aliases-list' is not available until the `em-alias' module is loaded,
 | |
| ;; so the alias additions are set up here
 | |
| 
 | |
| (editor-feature em-alias
 | |
|   "Handles creation and management of command aliases for eshell."
 | |
|   :config
 | |
|   (eval-after-load 'em-alias (lambda () (add-to-list 'eshell-command-aliases-list '("lsl" "ls -lh $1")))))
 | |
| 
 | |
| ;; ---------------------------------- ;;
 | |
| ;; files
 | |
| ;; ---------------------------------- ;;
 | |
| 
 | |
| (editor-feature files
 | |
|   "Defines most of Emacs' file-handling functionality."
 | |
|   :preface
 | |
|   (defconst user/custom-file (locate-user-emacs-file "custom.el") "Location of user customizations file.")
 | |
|   (defconst user/backup-directory (locate-user-emacs-file "backups/") "Location of user backup directory.")
 | |
|   (defconst user/auto-save-directory (locate-user-emacs-file "auto-saves/") "Location of user auto save directory.")
 | |
|   (defconst user/lock-file-directory (locate-user-emacs-file "lock-files/") "Location of user lock file directory.")
 | |
|   (make-directory user/backup-directory :parents)
 | |
|   (make-directory user/auto-save-directory :parents)
 | |
|   (make-directory user/lock-file-directory :parents)
 | |
|   :custom
 | |
|   (version-control t "Use version numbers on backup files")
 | |
|   (kept-new-versions 5 "Keep 5 recent backup files")
 | |
|   (kept-old-versions 3 "Keep 3 old backup files")
 | |
|   (delete-old-versions t "Clean up old backup files")
 | |
|   (backup-by-copying t "Use copying unconditionally when creating backups")
 | |
|   (custom-file user/custom-file "Store customization info in a separate file")
 | |
|   (backup-directory-alist `((".*" . ,user/backup-directory)) "Set backup directory location")
 | |
|   (auto-save-file-name-transforms `((".*" ,user/auto-save-directory t)) "Set auto save directory location")
 | |
|   (lock-file-name-transforms `((".*" ,user/lock-file-directory t)) "Set lock file directory location")
 | |
|   :hook
 | |
|   (before-save-hook . delete-trailing-whitespace))
 | |
| 
 | |
| ;; ---------------------------------- ;;
 | |
| ;; flymake
 | |
| ;; ---------------------------------- ;;
 | |
| 
 | |
| (editor-feature flymake
 | |
|   "Provides universal on-the-fly syntax checking."
 | |
|   :config
 | |
|   (advice-add 'flymake-show-buffer-diagnostics :after (lambda () (pop-to-buffer (flymake--diagnostics-buffer-name))))
 | |
|   :hook
 | |
|   (prog-mode-hook . flymake-mode)
 | |
|   :bind
 | |
|   (:map flymake-mode-map
 | |
|         ("C-c C-e" . flymake-show-buffer-diagnostics)))
 | |
| 
 | |
| ;; ---------------------------------- ;;
 | |
| ;; frame
 | |
| ;; ---------------------------------- ;;
 | |
| 
 | |
| (editor-feature frame
 | |
|   "Graphical frame configuration."
 | |
|   :preface
 | |
|   (defvar user/initial-frame-created nil "Whether or not the first frame has been initialized by the server.")
 | |
|   (defvar user/after-daemon-make-initial-frame-hook nil "Run when the first frame is produced by the server in daemon mode.")
 | |
|   (defun user/set-up-frame (frame)
 | |
|     "Set up newly-created frame FRAME."
 | |
|     (when (and (daemonp)
 | |
|                (display-graphic-p frame)
 | |
|                (not user/initial-frame-created))
 | |
|       (with-selected-frame frame
 | |
|         (run-hooks 'user/after-daemon-make-initial-frame-hook)
 | |
|         (setq user/initial-frame-created t))))
 | |
|   :config
 | |
|   (mapc #'user/set-up-frame (frame-list))
 | |
|   :hook
 | |
|   (after-make-frame-functions . user/set-up-frame))
 | |
| 
 | |
| ;; ---------------------------------- ;;
 | |
| ;; hl-line
 | |
| ;; ---------------------------------- ;;
 | |
| 
 | |
| (editor-feature hl-line
 | |
|   "Highlights the current line in a buffer."
 | |
|   :config
 | |
|   (global-hl-line-mode))
 | |
| 
 | |
| ;; ---------------------------------- ;;
 | |
| ;; ispell
 | |
| ;; ---------------------------------- ;;
 | |
| 
 | |
| (editor-feature ispell
 | |
|   "Checks for spelling errors and suggests corrections from a dictionary."
 | |
|   :bind
 | |
|   ("C-c ' '" . ispell)
 | |
|   ("C-c ' w" . ispell-word)
 | |
|   ("C-c ' r" . ispell-region)
 | |
|   ("C-c ' b" . ispell-buffer)
 | |
|   ("C-c ' ;" . ispell-comment-or-string-at-point)
 | |
|   ("C-c ' C-;" . ispell-comments-and-strings))
 | |
| 
 | |
| ;; ---------------------------------- ;;
 | |
| ;; paren
 | |
| ;; ---------------------------------- ;;
 | |
| 
 | |
| (editor-feature paren
 | |
|   "Highlights matching delimiter pairs under the cursor."
 | |
|   :custom
 | |
|   (show-paren-delay 0.0 "Highlight matching delimiters instantly")
 | |
|   (show-paren-context-when-offscreen t "Show context of matching off-screen delimiters")
 | |
|   :hook
 | |
|   (text-mode-hook . show-paren-mode)
 | |
|   (prog-mode-hook . show-paren-mode))
 | |
| 
 | |
| ;; ---------------------------------- ;;
 | |
| ;; pixel-scroll
 | |
| ;; ---------------------------------- ;;
 | |
| 
 | |
| (editor-feature pixel-scroll
 | |
|   "Enables pixel-level animation of scrolling events."
 | |
|   :when
 | |
|   (or (daemonp)
 | |
|       (display-graphic-p))
 | |
|   :custom
 | |
|   (pixel-scroll-precision-interpolate-page t "Enable pixel-level scrolling for page-wise scroll events")
 | |
|   (pixel-scroll-precision-large-scroll-height 40.0 "Animate any scroll moving the view more than 40 pixels")
 | |
|   :hook
 | |
|   (server-after-make-frame-hook . pixel-scroll-precision-mode)
 | |
|   (emacs-startup-hook . pixel-scroll-precision-mode)
 | |
|   :bind
 | |
|   (:map pixel-scroll-precision-mode-map
 | |
|         ("M-v" . pixel-scroll-interpolate-up)
 | |
|         ("C-v" . pixel-scroll-interpolate-down)))
 | |
| 
 | |
| ;; ---------------------------------- ;;
 | |
| ;; project
 | |
| ;; ---------------------------------- ;;
 | |
| 
 | |
| (editor-feature project
 | |
|   "Provides functionality for dealing with projects."
 | |
|   :preface
 | |
|   (defun user/index-projects (dir)
 | |
|     "Recursively scan for and index any project roots within DIR."
 | |
|     (interactive
 | |
|      (list (read-directory-name "Look for projects in: ")))
 | |
|     (when-let (project-roots (directory-files-recursively dir "\\.git$" #'project--find-in-directory t))
 | |
|       (message "%d directories indexed as projects."
 | |
|                (seq-reduce '+ (mapcar #'project-remember-projects-under project-roots) 0))))
 | |
|   :bind
 | |
|   (:map project-prefix-map
 | |
|         ("e" . flymake-show-project-diagnostics)
 | |
|         ("RET" . project-eshell)))
 | |
| 
 | |
| ;; ---------------------------------- ;;
 | |
| ;; rust-ts-mode
 | |
| ;; ---------------------------------- ;;
 | |
| 
 | |
| (editor-feature rust-ts-mode
 | |
|   "Major mode for Rust, using the Tree-sitter parsing library."
 | |
|   :preface
 | |
|   (defconst user/rust-cargo-directory (expand-file-name "~/.cargo/bin") "Location of installed Rust binaries.")
 | |
|   :config
 | |
|   ;; Ensure Emacs knows where to look for Rust tooling
 | |
|   (when (file-directory-p user/rust-cargo-directory)
 | |
|     (add-to-list 'exec-path user/rust-cargo-directory))
 | |
|   :hook
 | |
|   ;; Ignore disruptive delimiter auto-matching feature of rust-analyzer LS
 | |
|   (eglot-managed-mode-hook . (lambda ()
 | |
|                                (setq-local eglot-ignored-server-capabilities '(:documentOnTypeFormattingProvider))))
 | |
|   :mode
 | |
|   ("\\.rs\\'" . rust-ts-mode))
 | |
| 
 | |
| ;; ---------------------------------- ;;
 | |
| ;; savehist
 | |
| ;; ---------------------------------- ;;
 | |
| 
 | |
| (editor-feature savehist
 | |
|   "Persists minibuffer history between sessions."
 | |
|   :config
 | |
|   ;; Save last 30 items from the kill ring between sessions
 | |
|   (add-to-list 'savehist-additional-variables '(kill-ring . 30))
 | |
|   (savehist-mode)
 | |
|   :hook
 | |
|   ;; Remove text properties from `kill-ring' before saving it
 | |
|   (kill-emacs-hook . (lambda () (setq kill-ring (mapcar 'substring-no-properties kill-ring)))))
 | |
| 
 | |
| ;; ---------------------------------- ;;
 | |
| ;; save-place
 | |
| ;; ---------------------------------- ;;
 | |
| 
 | |
| (editor-feature saveplace
 | |
|   "Saves the last editing location for files between sessions."
 | |
|   :config
 | |
|   (save-place-mode))
 | |
| 
 | |
| ;; ---------------------------------- ;;
 | |
| ;; server
 | |
| ;; ---------------------------------- ;;
 | |
| 
 | |
| (editor-feature server
 | |
|   "Allows Emacs to operate as a server for other Emacs processes."
 | |
|   :config
 | |
|   ;; When running in a server/client configuration, we don't want the
 | |
|   ;; foreground/background colors in `default-frame-alist' to overwrite the
 | |
|   ;; colors set by the active theme every time we open a new client frame
 | |
|   (when (daemonp)
 | |
|     (assoc-delete-all 'foreground-color default-frame-alist)
 | |
|     (assoc-delete-all 'background-color default-frame-alist))
 | |
|   :custom
 | |
|   (server-client-instructions nil "Suppress help messages from the server for new frames"))
 | |
| 
 | |
| ;; ---------------------------------- ;;
 | |
| ;; so-long
 | |
| ;; ---------------------------------- ;;
 | |
| 
 | |
| (editor-feature so-long
 | |
|   "Deactivates certain editor features when opening files with very long lines."
 | |
|   :config
 | |
|   (global-so-long-mode))
 | |
| 
 | |
| ;; ---------------------------------- ;;
 | |
| ;; subword
 | |
| ;; ---------------------------------- ;;
 | |
| 
 | |
| (editor-feature subword
 | |
|   "Enables detection of subwords as words in camel case or pascal case names."
 | |
|   :config
 | |
|   (global-subword-mode))
 | |
| 
 | |
| ;; ---------------------------------- ;;
 | |
| ;; tab-bar
 | |
| ;; ---------------------------------- ;;
 | |
| 
 | |
| (editor-feature tab-bar
 | |
|   "Provides a frame-wide tab bar that allows for tabbed workspace switching."
 | |
|   :custom
 | |
|   (tab-bar-format '(tab-bar-format-tabs tab-bar-separator))
 | |
|   (tab-bar-close-button-show nil "Disable close button on tabs"))
 | |
| 
 | |
| ;; ---------------------------------- ;;
 | |
| ;; tab-line
 | |
| ;; ---------------------------------- ;;
 | |
| 
 | |
| (editor-feature tab-line
 | |
|   "Provides a buffer-local tab line that facilitates quick buffer switching."
 | |
|   :custom
 | |
|   (tab-line-close-button-show nil "Disable close button on tabs")
 | |
|   (tab-line-new-button-show nil "Disable tab creation button")
 | |
|   (tab-line-left-button nil "Disable the left scroll button")
 | |
|   (tab-line-right-button nil "Disable the right scroll button")
 | |
|   (tab-line-switch-cycling t "Enable wrap-around tab cycling"))
 | |
| 
 | |
| ;; ---------------------------------- ;;
 | |
| ;; treesit
 | |
| ;; ---------------------------------- ;;
 | |
| 
 | |
| (editor-feature treesit
 | |
|   "Integrates the Tree-sitter parsing library into Emacs."
 | |
|   :config
 | |
|   (add-to-list 'treesit-language-source-alist '(cpp "https://github.com/alfaix/tree-sitter-cpp" "alfaix/cpp-20-modules")))
 | |
| 
 | |
| ;; ---------------------------------- ;;
 | |
| ;; uniquify
 | |
| ;; ---------------------------------- ;;
 | |
| 
 | |
| (editor-feature uniquify
 | |
|   "Provides better unique names when there are name conflicts between buffers."
 | |
|   :custom
 | |
|   (uniquify-buffer-name-style 'forward "Show file path before buffer name")
 | |
|   (uniquify-after-kill-buffer-p t "Update buffer names after killing")
 | |
|   (uniquify-ignore-buffers-re "^\\*" "Avoid renaming special buffers"))
 | |
| 
 | |
| ;; ---------------------------------- ;;
 | |
| ;; window
 | |
| ;; ---------------------------------- ;;
 | |
| 
 | |
| (editor-feature window
 | |
|   "Provides commands for interacting with Emacs windows."
 | |
|   :preface
 | |
|   (defun user/buffer-special-p (buffer)
 | |
|     "Return non-nil if BUFFER is not visiting a file."
 | |
|     (and (not (buffer-file-name (get-buffer buffer)))
 | |
|          (not (string= (buffer-name (get-buffer buffer)) "*scratch*"))
 | |
|          (not (string-prefix-p " *" (buffer-name (get-buffer buffer))))))
 | |
|   (defun user/window-special-p (window)
 | |
|     "Return non-nil if the current buffer of WINDOW is not visiting a file."
 | |
|     (and (not (window-dedicated-p window))
 | |
|          (user/buffer-special-p (window-buffer window))))
 | |
|   (defun user/display-buffer-reuse-special-window (buffer _alist)
 | |
|     "Return a window based on whether its displayed buffer is not visiting a file.
 | |
| Display BUFFER in the returned window. Return nil if no usable window is found."
 | |
|     (let ((windows (seq-filter 'user/window-special-p (window-list nil :no-minibuf))))
 | |
|       (cl-letf (((symbol-function 'window-list-1) (lambda (&rest _args) windows)))
 | |
|         (when-let ((window (get-lru-window)))
 | |
|           (set-window-buffer window buffer)
 | |
|           window))))
 | |
|   :custom
 | |
|   ;; Set default buffer display action priority:
 | |
|   ;; focus window already displaying buffer, else display buffer in current window
 | |
|   (display-buffer-base-action '((display-buffer-reuse-window display-buffer-same-window)))
 | |
|   ;; Set special (non-file) buffer display action priority:
 | |
|   ;; focus window already displaying buffer, else reuse another special buffer's window,
 | |
|   ;; else attempt pop up new window with buffer
 | |
|   (display-buffer-alist `((user/buffer-special-p (display-buffer-reuse-window
 | |
|                                                   user/display-buffer-reuse-special-window
 | |
|                                                   display-buffer-pop-up-window)))))
 | |
| 
 | |
| ;; ---------------------------------- ;;
 | |
| ;; whitespace
 | |
| ;; ---------------------------------- ;;
 | |
| 
 | |
| (editor-feature whitespace
 | |
|   "Visualizes blank characters."
 | |
|   :custom
 | |
|   (whitespace-line-column nil "Use the value of `fill-column' instead of a set value")
 | |
|   (whitespace-display-mappings '((tab-mark 9 [8677 9])          ; Tab
 | |
|                                  (space-mark 32 [8729])         ; Space
 | |
|                                  (space-mark 160 [8999])        ; Non-breaking space
 | |
|                                  (newline-mark 10 [8626 10])))) ; Newline
 | |
| 
 | |
| ;; -------------------------------------------------------------------------- ;;
 | |
| ;;
 | |
| ;; External packages
 | |
| ;;
 | |
| ;; -------------------------------------------------------------------------- ;;
 | |
| 
 | |
| (defmacro external-package (name docstring &rest args)
 | |
|   "Apply NAME and ARGS to `use-package' with `:ensure' defaulted to t.
 | |
| DOCSTRING is an optional form that is discarded upon expansion."
 | |
|   (declare (doc-string 2)
 | |
|            (indent defun))
 | |
|   (ignore docstring)
 | |
|   `(use-package ,name :ensure t ,@args))
 | |
| 
 | |
| ;; ---------------------------------- ;;
 | |
| ;; Theme
 | |
| ;; ---------------------------------- ;;
 | |
| 
 | |
| (external-package adwaita-dark-theme
 | |
|   "Provides the `adwaita-dark' theme and custom fringe bitmaps."
 | |
|   :config
 | |
|   (load-theme 'adwaita-dark :no-confirm)
 | |
|   (adwaita-dark-theme-arrow-fringe-bmp-enable)
 | |
|   (eval-after-load 'diff-hl #'adwaita-dark-theme-diff-hl-fringe-bmp-enable)
 | |
|   (eval-after-load 'flymake #'adwaita-dark-theme-flymake-fringe-bmp-enable)
 | |
|   (eval-after-load 'neotree #'adwaita-dark-theme-neotree-configuration-enable)
 | |
|   (eval-after-load 'eldoc-frame #'adwaita-dark-theme-eldoc-frame-configuration-enable)
 | |
|   :custom
 | |
|   (adwaita-dark-theme-pad-tab-bar t "Enable visual padding for tab bars")
 | |
|   (adwaita-dark-theme-pad-tab-line t "Enable visual padding for tab lines")
 | |
|   (adwaita-dark-theme-pad-mode-line t "Enable visual padding for mode lines")
 | |
|   (adwaita-dark-theme-gray-outlines t "Use gray colors for outline-mode faces")
 | |
|   (adwaita-dark-theme-gray-rainbow-delimiters t "Use a gray color for rainbow-delimiters faces")
 | |
|   (adwaita-dark-theme-bold-vertico-current t "Embolden the currently-selected candidate in vertico")
 | |
|   (adwaita-dark-theme-no-completions-first-difference t "Disable first difference highlight in completions")
 | |
|   :hook
 | |
|   (user/after-daemon-make-initial-frame-hook . (lambda () (load-theme 'adwaita-dark :no-confirm))))
 | |
| 
 | |
| ;; ---------------------------------- ;;
 | |
| ;; Simple language modes
 | |
| ;; ---------------------------------- ;;
 | |
| 
 | |
| (external-package clojure-mode
 | |
|   "Major mode for Clojure."
 | |
|   :mode
 | |
|   ("\\.clj\\'" . clojure-mode)
 | |
|   ("\\.cljs\\'" . clojurescript-mode)
 | |
|   ("\\.cljc\\'" . clojurec-mode))
 | |
| 
 | |
| (external-package dart-mode
 | |
|   "Major mode for Dart."
 | |
|   :mode
 | |
|   ("\\.dart\\'" . dart-mode))
 | |
| 
 | |
| (external-package docker-compose-mode
 | |
|   "Major mdoe for docker-compose files."
 | |
|   :mode
 | |
|   ("\\docker-compose.yml\\'" . docker-compose-mode))
 | |
| 
 | |
| (external-package dockerfile-mode
 | |
|   "Major mode for Docker files."
 | |
|   :mode
 | |
|   ("\\Dockerfile\\'" . dockerfile-mode))
 | |
| 
 | |
| (external-package fennel-mode
 | |
|   "Major mode for Fennel."
 | |
|   :mode
 | |
|   ("\\.fnl\\'" . fennel-mode))
 | |
| 
 | |
| (external-package fish-mode
 | |
|   "Major mode for fish files."
 | |
|   :mode
 | |
|   ("\\.fish\\'" . fish-mode))
 | |
| 
 | |
| (external-package gdscript-mode
 | |
|   "Major mode for GDScript."
 | |
|   :mode
 | |
|   ("\\.gd\\'" . gdscript-mode)
 | |
|   ("\\.tscn\\'" . gdscript-mode))
 | |
| 
 | |
| (external-package git-modes
 | |
|   "Major modes for git files."
 | |
|   :mode
 | |
|   ("\\.gitignore\\'" . gitignore-mode)
 | |
|   ("\\.gitconfig\\'" . gitconfig-mode)
 | |
|   ("\\.gitmodules\\'" . gitconfig-mode)
 | |
|   ("\\.gitattributes\\'" . gitattributes-mode))
 | |
| 
 | |
| (external-package glsl-mode
 | |
|   "Major mode for GLSL."
 | |
|   :mode
 | |
|   ("\\.frag\\'" . glsl-mode)
 | |
|   ("\\.vert\\'" . glsl-mode))
 | |
| 
 | |
| (external-package kotlin-mode
 | |
|   "Major mode for Kotlin."
 | |
|   :mode
 | |
|   ("\\.kt\\'" . kotlin-mode))
 | |
| 
 | |
| (external-package lua-mode
 | |
|   "Major mode for Lua."
 | |
|   :mode
 | |
|   (("\\.lua\\'" . lua-mode)
 | |
|    ("\\.rockspec\\'" . lua-mode)))
 | |
| 
 | |
| (external-package markdown-mode
 | |
|   "Major mode for Markdown files."
 | |
|   :mode
 | |
|   ("\\.md\\'" . markdown-mode))
 | |
| 
 | |
| (external-package svelte-mode
 | |
|   "Major mode for Svelte files."
 | |
|   :mode
 | |
|   ("\\.svelte\\'" . svelte-mode))
 | |
| 
 | |
| ;; (external-package web-mode
 | |
| ;;   "Major mode for web templates."
 | |
| ;;   :custom
 | |
| ;;   (web-mode-markup-indent-offset 2 "Use 2 spaces instead of 4 for indenting HTML elements")
 | |
| ;;   (web-mode-enable-auto-quoting nil "Do not automatically insert quotes after HTML attributes")
 | |
| ;;   :mode
 | |
| ;;   ("\\.php\\'" . web-mode)
 | |
| ;;   ("\\.html\\'" . web-mode)
 | |
| ;;   ("\\.svelte\\'" . web-mode))
 | |
| 
 | |
| ;; ---------------------------------- ;;
 | |
| ;; anzu
 | |
| ;; ---------------------------------- ;;
 | |
| 
 | |
| (external-package anzu
 | |
|   "Displays matching and replacement information in the mode line."
 | |
|   :config
 | |
|   (global-anzu-mode)
 | |
|   :bind
 | |
|   ("M-s r ." . anzu-query-replace-at-cursor)
 | |
|   ("<remap> <query-replace>" . anzu-query-replace)
 | |
|   ("<remap> <query-replace-regexp>" . anzu-query-replace-regexp))
 | |
| 
 | |
| ;; ---------------------------------- ;;
 | |
| ;; bufler
 | |
| ;; ---------------------------------- ;;
 | |
| 
 | |
| (external-package bufler
 | |
|   "Presents open buffers in a grouped and organized menu."
 | |
|   :custom
 | |
|   (bufler-columns '("Name" "Path"))
 | |
|   (bufler-list-group-separators '((0 . "\n")))
 | |
|   (bufler-column-name-modified-buffer-sigil " ●")
 | |
|   (bufler-list-display-buffer-action '((display-buffer-same-window)))
 | |
|   (bufler-groups
 | |
|    (bufler-defgroups
 | |
|      (group
 | |
|       ;; Subgroup collecting all named workspaces.
 | |
|       (auto-workspace))
 | |
|      (group
 | |
|       ;; Subgroup collecting all `help-mode' and `info-mode' buffers.
 | |
|       (group-or "Help/Info"
 | |
|                 (mode-match "Help" (rx bos "help-"))
 | |
|                 (mode-match "Info" (rx bos "info-"))))
 | |
|      (group
 | |
|       ;; Subgroup collecting all special buffers (i.e. ones that are not file-backed),
 | |
|       ;; except certain ones like Dired, Forge, or Magit buffers (which are allowed to
 | |
|       ;; fall through to other groups, so they end up grouped with their project buffers).
 | |
|       (group-not "Special"
 | |
|                  (group-or "Special"
 | |
|                            (mode-match "Eshell" (rx bos "eshell-"))
 | |
|                            (mode-match "Magit" (rx bos "magit-"))
 | |
|                            (mode-match "Forge" (rx bos "forge-"))
 | |
|                            (mode-match "Dired" (rx bos "dired"))
 | |
|                            (mode-match "Grep" (rx bos "grep-"))
 | |
|                            (mode-match "Compilation" (rx bos "compilation-"))
 | |
|                            (auto-file)))
 | |
|       (group
 | |
|        ;; Subgroup collecting these "special special" buffers
 | |
|        ;; separately for convenience.
 | |
|        (name-match "Emacs"
 | |
|                    (rx bos "*" (or "Messages" "Warnings" "scratch" "Backtrace") "*")))
 | |
|       (group
 | |
|        ;; Subgroup collecting all other Magit buffers, grouped by directory.
 | |
|        (mode-match "Magit" (rx bos "magit-"))
 | |
|        (auto-directory))
 | |
|       ;; Remaining special buffers are grouped automatically by mode.
 | |
|       (auto-mode))
 | |
|      (group
 | |
|       ;; Subgroup collecting buffers in a version-control project,
 | |
|       ;; grouping them by directory (using the parent project keeps,
 | |
|       ;; e.g. git worktrees with their parent repos).
 | |
|       (auto-parent-project)
 | |
|       (group-or "Files"
 | |
|                 (auto-file))
 | |
|       ;; Subgroup collecting special buffers so they are easily distinguished from file buffers.
 | |
|       (group-not "Special"
 | |
|                  (auto-file)))
 | |
|      ;; Group remaining buffers by directory, then major mode.
 | |
|      (auto-directory)
 | |
|      (auto-mode)))
 | |
|   :bind
 | |
|   ("C-x C-b" . bufler-list))
 | |
| 
 | |
| ;; ---------------------------------- ;;
 | |
| ;; cape
 | |
| ;; ---------------------------------- ;;
 | |
| 
 | |
| (external-package cape
 | |
|   "Provides extensions for default Emacs completion facilities."
 | |
|   :config
 | |
|   ;; Wrap eglot's capf as noninterruptible to avoid desyncing with the language
 | |
|   ;; server during completion operations
 | |
|   (advice-add #'eglot-completion-at-point :around #'cape-wrap-noninterruptible))
 | |
| 
 | |
| ;; ---------------------------------- ;;
 | |
| ;; cider
 | |
| ;; ---------------------------------- ;;
 | |
| 
 | |
| (external-package cider
 | |
|   "Provides an interactive programming environment for Clojure."
 | |
|   :preface
 | |
|   (defun user/cider-error-buffer ()
 | |
|     "Switch to the CIDER error buffer, if it exists."
 | |
|     (interactive)
 | |
|     (if (get-buffer cider-error-buffer)
 | |
|         (pop-to-buffer cider-error-buffer)
 | |
|       (message "No active CIDER stacktrace buffer.")))
 | |
|   (defun user/prettify-cider-namespace (namespace)
 | |
|     "Return NAMESPACE formatted and with all but the last name abbreviated."
 | |
|     (let* ((abbreviated-ns (cider-abbreviate-ns namespace))
 | |
|            (names (reverse (split-string abbreviated-ns "\\.")))
 | |
|            (last-name (car names)))
 | |
|       (concat (mapconcat (lambda (name) (propertize (concat name ".") 'face 'font-lock-comment-face))
 | |
|                          (reverse (cdr names))
 | |
|                          "")
 | |
|               (propertize last-name
 | |
|                           'face 'font-lock-doc-face))))
 | |
|   :custom
 | |
|   (nrepl-hide-special-buffers t "Hide REPL communications buffers from buffer lists")
 | |
|   (cider-show-error-buffer nil "Disable automatic display of the CIDER stacktrace info buffer")
 | |
|   (cider-use-fringe-indicators nil "Disable evaluation indicators in the fringe of CIDER buffers")
 | |
|   (cider-session-name-template "%j" "Label CIDER sessions with the short name of the current project")
 | |
|   (cider-eval-spinner-type ["" "" "" "" "" ""] "Use an activity spinner that utilizes Fira Code glyphs")
 | |
|   (cider-eval-spinner-delay 0.5 "Show the evaluation activity spinner after 0.5 seconds of activity")
 | |
|   (cider-repl-display-help-banner nil "Disable initial infodump in CIDER REPL buffers")
 | |
|   (cider-repl-prompt-function (lambda (ns) (concat (user/prettify-cider-namespace ns) " ❱ ")))
 | |
|   :hook
 | |
|   (clojure-mode-hook . cider-mode)
 | |
|   (cider-repl-mode-hook . electric-pair-local-mode)
 | |
|   :bind
 | |
|   (:map cider-mode-map
 | |
|         ("M-." . cider-find-var)
 | |
|         ("M-," . cider-pop-back)
 | |
|         ("C-c m" . cider-macroexpand-1)
 | |
|         ("C-c \\" . cider-format-buffer)
 | |
|         ("C-c C-f" . cider-load-file)
 | |
|         ("C-c C-b" . cider-load-buffer)
 | |
|         ("C-c M-e" . user/cider-error-buffer))
 | |
|   (:map cider-repl-mode-map
 | |
|         ("C-c C-e" . end-of-buffer)
 | |
|         ("C-<return>" . (lambda ()
 | |
|                           (interactive)
 | |
|                           (when (and (get-text-property (point) 'cider-old-input)
 | |
|                                      (< (point) cider-repl-input-start-mark))
 | |
|                             (cider-repl--grab-old-input nil))))))
 | |
| 
 | |
| ;; ---------------------------------- ;;
 | |
| ;; clj-refactor
 | |
| ;; ---------------------------------- ;;
 | |
| 
 | |
| (external-package clj-refactor
 | |
|   "Provides a suite of powerful refactoring functions for Clojure projects."
 | |
|   :custom
 | |
|   (cljr-warn-on-eval nil "Disables warning prompts for refactor commands that build ASTs")
 | |
|   :hook
 | |
|   (cider-mode-hook . (lambda ()
 | |
|                        (clj-refactor-mode)
 | |
|                        (advice-remove 'cider-format-buffer #'cljr-clean-ns)
 | |
|                        (advice-add 'cider-format-buffer :before #'cljr-clean-ns)))
 | |
|   :bind
 | |
|   (:map clj-refactor-map
 | |
|         ("C-c ." . cljr-find-usages)
 | |
|         ("M-s r ." . cljr-rename-symbol)))
 | |
| 
 | |
| ;; ---------------------------------- ;;
 | |
| ;; consult
 | |
| ;; ---------------------------------- ;;
 | |
| 
 | |
| (external-package consult
 | |
|   "Provides a number of autocompletion-enhanced replacements for default commands."
 | |
|   :config
 | |
|   ;; `eshell-mode-map' is not available until the `esh-mode' module is loaded
 | |
|   (eval-after-load 'esh-mode (lambda () (bind-key "C-r" #'consult-history 'eshell-mode-map)))
 | |
|   ;; Override the default character range used for candidate line number annotations
 | |
|   ;; to use the Unicode PUA-B block (in favor of a character range outside of Unicode)
 | |
|   (setq consult--tofu-char #x100000
 | |
|         consult--tofu-range #xFFFD)
 | |
|   ;; Have `xref' use consult for displaying definitions and references
 | |
|   (setq xref-show-xrefs-function #'consult-xref)
 | |
|   (setq xref-show-definitions-function #'consult-xref)
 | |
|   :bind
 | |
|   ("M-C-s" . (lambda () (interactive) (consult-line (thing-at-point 'symbol))))
 | |
|   ("M-s l" . consult-line)
 | |
|   ("M-s L" . consult-line-multi)
 | |
|   ("C-S-s" . consult-line-multi)
 | |
|   ("C-c e" . consult-flymake)
 | |
|   ("C-x M-b" . consult-buffer-other-window)
 | |
|   ("<remap> <goto-line>" . consult-goto-line)
 | |
|   ("<remap> <isearch-forward>" . consult-line)
 | |
|   ("<remap> <switch-to-buffer>" . consult-buffer)
 | |
|   ("<remap> <project-switch-to-buffer>" . consult-project-buffer)
 | |
|   ("<remap> <yank-pop>" . consult-yank-from-kill-ring)
 | |
|   ("<remap> <project-find-regexp>" . consult-ripgrep))
 | |
| 
 | |
| ;; ---------------------------------- ;;
 | |
| ;; corfu
 | |
| ;; ---------------------------------- ;;
 | |
| 
 | |
| (external-package corfu
 | |
|   "Enhances completion suggestions with a visible popup."
 | |
|   :init
 | |
|   (global-corfu-mode)
 | |
|   :config
 | |
|   (add-to-list 'savehist-additional-variables 'corfu-history)
 | |
|   (corfu-popupinfo-mode)
 | |
|   (corfu-history-mode)
 | |
|   :custom
 | |
|   (corfu-auto t "Automatically display completion popups when available")
 | |
|   (corfu-auto-delay 0.0 "Display completion popups immediately when available")
 | |
|   (corfu-min-width 20 "Ensure completion popups are at least 20 columns wide")
 | |
|   (corfu-max-width 50 "Limit completion popup width to 50 columns")
 | |
|   (corfu-preview-current nil "Disable inline preview of currently selected candidate")
 | |
|   (corfu-popupinfo-delay '(0.4 . 0.2) "Wait 0.4 seconds (0.2 subsequently) before showing a doc popup")
 | |
|   (corfu-popupinfo-max-width 70 "Limit doc popup width to 70 columns")
 | |
|   (corfu-popupinfo-min-height 4 "Ensure doc popups are at least 4 lines tall")
 | |
|   (corfu-echo-documentation nil "Disable displaying documentation strings in the echo area")
 | |
|   :hook
 | |
|   ;; Displaying completion popups automatically (i.e. without summoning them explicitly)
 | |
|   ;; can cause performance issues in `eshell-mode' due to the high volume of completion
 | |
|   ;; candidates for certain commands
 | |
|   (eshell-mode-hook . (lambda () (setq-local corfu-auto nil)))
 | |
|   :bind
 | |
|   (:map corfu-popupinfo-map
 | |
|         ("M-n" . corfu-popupinfo-scroll-up)
 | |
|         ("M-p" . corfu-popupinfo-scroll-down)))
 | |
| 
 | |
| ;; ---------------------------------- ;;
 | |
| ;; corfu-terminal
 | |
| ;; ---------------------------------- ;;
 | |
| 
 | |
| (external-package corfu-terminal
 | |
|   "Allows Corfu to function when not running in a graphical frame."
 | |
|   :when
 | |
|   (not (or (daemonp)
 | |
|            (display-graphic-p)))
 | |
|   :config
 | |
|   (corfu-terminal-mode))
 | |
| 
 | |
| ;; ---------------------------------- ;;
 | |
| ;; diff-hl
 | |
| ;; ---------------------------------- ;;
 | |
| 
 | |
| (external-package diff-hl
 | |
|   "Highlights uncommited changes to version controlled files in the gutter."
 | |
|   :config
 | |
|   (global-diff-hl-mode)
 | |
|   (diff-hl-flydiff-mode))
 | |
| 
 | |
| ;; ---------------------------------- ;;
 | |
| ;; editorconfig-mode
 | |
| ;; ---------------------------------- ;;
 | |
| 
 | |
| (external-package editorconfig
 | |
|   "Reads EditorConfig files and applies appropriate formatting settings for projects."
 | |
|   :config
 | |
|   (editorconfig-mode))
 | |
| 
 | |
| ;; ---------------------------------- ;;
 | |
| ;; eldoc-frame
 | |
| ;; ---------------------------------- ;;
 | |
| 
 | |
| (editor-feature eldoc-frame
 | |
|   "Displays ElDoc documentation in floating child frames."
 | |
|   :custom
 | |
|   (eldoc-documentation-strategy 'eldoc-documentation-compose "Combine results from all documentation sources")
 | |
|   :hook
 | |
|   (eldoc-mode-hook . eldoc-frame-mode)
 | |
|   :bind
 | |
|   (:map eldoc-frame-mode-map
 | |
|         ("M-<up>" . eldoc-frame-scroll-down-line)
 | |
|         ("M-<down>" . eldoc-frame-scroll-up-line)))
 | |
| 
 | |
| ;; ---------------------------------- ;;
 | |
| ;; eshell-up
 | |
| ;; ---------------------------------- ;;
 | |
| 
 | |
| (external-package eshell-up
 | |
|   "Provides a shortcut for quickly navigating to parent directories in eshell."
 | |
|   :config
 | |
|   ;; `eshell-command-aliases-list' is not available until the `em-alias' module is loaded
 | |
|   (eval-after-load 'em-alias (lambda ()
 | |
|                                (add-to-list 'eshell-command-aliases-list '("up" "eshell-up $1"))
 | |
|                                (add-to-list 'eshell-command-aliases-list '("pk" "eshell-up-peek $1")))))
 | |
| 
 | |
| ;; ---------------------------------- ;;
 | |
| ;; fish-completion
 | |
| ;; ---------------------------------- ;;
 | |
| 
 | |
| (external-package fish-completion
 | |
|   "Sources shell completion candidates from the fish shell."
 | |
|   :when
 | |
|   (executable-find "fish")
 | |
|   :hook
 | |
|   (eshell-mode-hook . fish-completion-mode))
 | |
| 
 | |
| ;; ---------------------------------- ;;
 | |
| ;; flymake-kondor
 | |
| ;; ---------------------------------- ;;
 | |
| 
 | |
| (external-package flymake-kondor
 | |
|   "Integrates clj-kondo as a flymake checker for Clojure buffers."
 | |
|   :when
 | |
|   (executable-find "clj-kondo")
 | |
|   :hook
 | |
|   (clojure-mode-hook . flymake-kondor-setup))
 | |
| 
 | |
| ;; ---------------------------------- ;;
 | |
| ;; hl-todo
 | |
| ;; ---------------------------------- ;;
 | |
| 
 | |
| (external-package hl-todo
 | |
|   "Highlights certain keywords in comments."
 | |
|   :custom
 | |
|   (hl-todo-keyword-faces '(("TODO" . hl-todo)
 | |
|                            ("TEMP" . hl-todo)
 | |
|                            ("HACK" . hl-todo)
 | |
|                            ("DEBUG" . hl-todo)
 | |
|                            ("FIXME" . hl-todo)))
 | |
|   :hook
 | |
|   (prog-mode-hook . hl-todo-mode))
 | |
| 
 | |
| ;; ---------------------------------- ;;
 | |
| ;; hotfuzz
 | |
| ;; ---------------------------------- ;;
 | |
| 
 | |
| (external-package hotfuzz
 | |
|   "Provides a native fuzzy completion style that scores and sorts candidates."
 | |
|   :preface
 | |
|   (defconst user/hotfuzz-directory (locate-user-emacs-file "hotfuzz/") "Location of dynamic Emacs module for hotfuzz")
 | |
|   (make-directory user/hotfuzz-directory :parents)
 | |
|   (add-to-list 'load-path user/hotfuzz-directory)
 | |
|   (eval-when-compile
 | |
|     (when (executable-find "clang")
 | |
|       (let ((source-url "https://raw.githubusercontent.com/axelf4/hotfuzz/master/hotfuzz-module.c")
 | |
|             (source-file (locate-user-emacs-file (concat user/hotfuzz-directory "hotfuzz-module.c")))
 | |
|             (output-file (locate-user-emacs-file (concat user/hotfuzz-directory "hotfuzz-module.so"))))
 | |
|         (url-copy-file source-url source-file :ok-if-already-exists)
 | |
|         (shell-command (concat "clang -fPIC -O2 -march=native -shared " source-file " -o " output-file)))))
 | |
|   :custom
 | |
|   (hotfuzz-max-highlighted-completions most-positive-fixnum "Disable match candidate highlighting limits"))
 | |
| 
 | |
| ;; ---------------------------------- ;;
 | |
| ;; kind-icon
 | |
| ;; ---------------------------------- ;;
 | |
| 
 | |
| (external-package kind-icon
 | |
|   "Adds contextual icons in front of Corfu completion candidates."
 | |
|   :when
 | |
|   (or (daemonp)
 | |
|       (display-graphic-p))
 | |
|   :after
 | |
|   (corfu)
 | |
|   :config
 | |
|   (add-to-list 'corfu-margin-formatters #'kind-icon-margin-formatter)
 | |
|   :custom
 | |
|   (kind-icon-extra-space t "Insert extra blank space after the icon"))
 | |
| 
 | |
| ;; ---------------------------------- ;;
 | |
| ;; ligature
 | |
| ;; ---------------------------------- ;;
 | |
| 
 | |
| (external-package ligature
 | |
|   "Enables support for mapping characters to ligatures."
 | |
|   :config
 | |
|   (ligature-set-ligatures
 | |
|    '(sgml-mode prog-mode)
 | |
|    '(;; This set of ligatures is for Fira Code, but
 | |
|      ;; should work for most any font with ligatures:
 | |
|      ;; && &&&
 | |
|      ;; ;; ;;;
 | |
|      ;; %% %%%
 | |
|      ;; ?? ??? ?:  ?=  ?.
 | |
|      ;; !! !!! !. !: !!. != !== !~
 | |
|      (";" (rx (+ ";")))
 | |
|      ("&" (rx (+ "&")))
 | |
|      ("%" (rx (+ "%")))
 | |
|      ("?" (rx (or ":" "=" "\." (+ "?"))))
 | |
|      ("!" (rx (+ (or "=" "!" "\." ":" "~"))))
 | |
|      ;; \\ \\\ \/
 | |
|      ;; ++ +++ ++++ +>
 | |
|      ;; :: ::: :::: :> :< := :// ::=
 | |
|      ;; // /// //// /\ /* /> /===:===!=//===>>==>==/
 | |
|      ;; == === ==== => =| =>>=>=|=>==>> ==< =/=//=// =~ =:= =!=
 | |
|      ("\\" (rx (or "/" (+ "\\"))))
 | |
|      ("+" (rx (or ">" (+ "+"))))
 | |
|      (":" (rx (or ">" "<" "=" "//" ":=" (+ ":"))))
 | |
|      ("/" (rx (+ (or ">"  "<" "|" "/" "\\" "\*" ":" "!" "="))))
 | |
|      ("=" (rx (+ (or ">" "<" "|" "/" "~" ":" "!" "="))))
 | |
|      ;; |> ||> |||> ||||> |] |} || ||| |-> ||-||
 | |
|      ;; |->>-||-<<-| |- |== ||=|| |==>>==<<==<=>==//==/=!==:===>
 | |
|      ("|" (rx (+ (or ">" "<" "|" "/" ":" "!" "}" "\]" "-" "=" ))))
 | |
|      ;; *> */ *)  ** *** ****
 | |
|      ;; .. ... .... .= .- .? ..= ..<
 | |
|      ;; -- --- ---- -~ -> ->> -| -|->-->>->--<<-|
 | |
|      ;; #: #= #! #( #? #[ #{ #_ #_( ## ### #####
 | |
|      ;; >: >- >>- >--|-> >>-|-> >= >== >>== >=|=:=>> >> >>> >>>>
 | |
|      ("*" (rx (or ">" "/" ")" (+ "*"))))
 | |
|      ("\." (rx (or "=" "-" "\?" "\.=" "\.<" (+ "\."))))
 | |
|      ("-" (rx (+ (or ">" "<" "|" "~" "-"))))
 | |
|      ("#" (rx (or ":" "=" "!" "(" "\?" "\[" "{" "_(" "_" (+ "#"))))
 | |
|      (">" (rx (+ (or ">" "<" "|" "/" ":" "=" "-"))))
 | |
| 
 | |
|      ;; <> <!-- <|> <: <~ <~> <~~ <+ <* <$ </  <+> <*>
 | |
|      ;; <$> </> <|  <||  <||| <|||| <- <-| <-<<-|-> <->>
 | |
|      ;; <<-> <= <=> <<==<<==>=|=>==/==//=!==:=> << <<< <<<<
 | |
|      ("<" (rx (+ (or "\+" "\*" "\$" "<" ">" ":" "~"  "!" "-"  "/" "|" "="))))
 | |
|      ;; __ ___ ____ _|_ __|____|_
 | |
|      ;; ~~ ~~~ ~=  ~-  ~@ ~> ~~>
 | |
|      ("_" (rx (+ (or "_" "|"))))
 | |
|      ("~" (rx (or ">" "=" "-" "@" "~>" (+ "~"))))
 | |
|      ;; {| [\ ]# (* }# $> ^=
 | |
|      "{|"  "[|"  "]#"  "(*"  "}#"  "$>"  "^="
 | |
|      ;; www wwww
 | |
|      ;; 0xFF 0x12
 | |
|      ;; Fl Tl fi fj fl ft
 | |
|      ("w" (rx (+ "w")))
 | |
|      ("0" (rx (and "x" (+ (in "A-F" "a-f" "0-9")))))
 | |
|      "Fl"  "Tl"  "fi"  "fj"  "fl"  "ft"))
 | |
|   :hook
 | |
|   (sgml-mode-hook . ligature-mode)
 | |
|   (prog-mode-hook . ligature-mode))
 | |
| 
 | |
| ;; ---------------------------------- ;;
 | |
| ;; magit
 | |
| ;; ---------------------------------- ;;
 | |
| 
 | |
| (external-package magit
 | |
|   "Provides a visual interface to git."
 | |
|   :custom
 | |
|   (magit-display-buffer-function 'display-buffer "Use standard display-buffer function to display Magit buffers")
 | |
|   (magit-commit-diff-inhibit-same-window t "Never display the commit diff over the commit message window")
 | |
|   (git-commit-summary-max-length 50 "Highlight characters in commit summaries past column 50")
 | |
|   :bind
 | |
|   ("C-c g" . magit-status))
 | |
| 
 | |
| ;; ---------------------------------- ;;
 | |
| ;; magit-todos
 | |
| ;; ---------------------------------- ;;
 | |
| 
 | |
| (external-package magit-todos
 | |
|   "Shows a list of keyword-containing comments in the Magit status buffer."
 | |
|   :hook
 | |
|   (magit-mode-hook . magit-todos-mode))
 | |
| 
 | |
| ;; ---------------------------------- ;;
 | |
| ;; marginalia
 | |
| ;; ---------------------------------- ;;
 | |
| 
 | |
| (external-package marginalia
 | |
|   "Adds informative annotations to completion candidates in the minibuffer."
 | |
|   :config
 | |
|   (defun marginalia--file-owner (_) "")
 | |
|   (defun marginalia--file-modes (_) "")
 | |
|   (defun marginalia--symbol-class (_) "")
 | |
|   (defun marginalia--buffer-status (_) "")
 | |
|   (marginalia-mode))
 | |
| 
 | |
| ;; ---------------------------------- ;;
 | |
| ;; mood-line
 | |
| ;; ---------------------------------- ;;
 | |
| 
 | |
| (external-package mood-line
 | |
|   "Gives the mode-line a cleaner appearance."
 | |
|   :config
 | |
|   (mood-line-mode)
 | |
|   :custom
 | |
|   (mood-line-glyph-alist mood-line-glyphs-fira-code "Use Fira Code-compatible decorative glyphs")
 | |
|   (mood-line-format
 | |
|    (mood-line-defformat
 | |
|     :left
 | |
|     (((or (mood-line-segment-buffer-status) " ") . " ")
 | |
|      ((mood-line-segment-buffer-name) . "  ")
 | |
|      ((mood-line-segment-anzu) . "  ")
 | |
|      ((mood-line-segment-multiple-cursors) . "  ")
 | |
|      ((mood-line-segment-cursor-position) . " ")
 | |
|      (mood-line-segment-scroll))
 | |
|     :right
 | |
|     (((mood-line-segment-vc) . "  ")
 | |
|      ((mood-line-segment-misc-info) . "  ")
 | |
|      ((mood-line-segment-checker) . "  ")
 | |
|      ((mood-line-segment-process) . "  ")))))
 | |
| 
 | |
| ;; ---------------------------------- ;;
 | |
| ;; multiple-cursors
 | |
| ;; ---------------------------------- ;;
 | |
| 
 | |
| (external-package multiple-cursors
 | |
|   "Provides the ability to summon additional cursors."
 | |
|   :bind
 | |
|   ("C->" . mc/mark-next-like-this)
 | |
|   ("C-<" . mc/mark-previous-like-this)
 | |
|   ("C-c C->" . mc/mark-all-like-this)
 | |
|   ("C-c C-<" . mc/mark-all-like-this))
 | |
| 
 | |
| ;; ---------------------------------- ;;
 | |
| ;; neotree
 | |
| ;; ---------------------------------- ;;
 | |
| 
 | |
| (external-package neotree
 | |
|   "Displays an interactive directory/file tree in a side window."
 | |
|   :config
 | |
|   (advice-add 'neotree-change-root :after (lambda () (goto-char (point-min))))
 | |
|   :custom
 | |
|   (neo-smart-open t "Jump to current file when focusing the neotree window")
 | |
|   (neo-window-width 30 "Limit neotree window width to 30 columns")
 | |
|   (neo-show-updir-line nil "Disable dipspying updir (..) line in the neotree window")
 | |
|   (neo-confirm-create-file #'off-p "Skip confirmation when creating a new file from neotree")
 | |
|   (neo-confirm-create-directory #'off-p "Skip confirmation when creating a new directory from neotree")
 | |
|   :bind
 | |
|   ("C-c t" . neotree)
 | |
|   (:map neotree-mode-map
 | |
|         ("C-c w" . (lambda () (interactive) nil))
 | |
|         ("C-c q" . (lambda () (interactive) nil))
 | |
|         ("C-c n" . (lambda () (interactive) nil))))
 | |
| 
 | |
| ;; ---------------------------------- ;;
 | |
| ;; orderless
 | |
| ;; ---------------------------------- ;;
 | |
| 
 | |
| (external-package orderless
 | |
|   "Provides a flexible component-based completion style."
 | |
|   :preface
 | |
|   (defun user/orderless-dispatch (word index total)
 | |
|     (and (= index 0) (= total 1) (length< word 4)
 | |
|          `(orderless-regexp . ,(concat "^" (regexp-quote word)))))
 | |
|   :after
 | |
|   (hotfuzz)
 | |
|   :custom
 | |
|   (orderless-component-separator " " "Use a space to separate orderless matching components")
 | |
|   (orderless-style-dispatchers '(user/orderless-dispatch) "Use custom fast dispatcher for orderless")
 | |
|   (orderless-matching-styles '(orderless-literal) "Use only literal matching for orderless completions")
 | |
|   (completion-ignore-case t "Ignore case in completion candidates")
 | |
|   (completion-category-defaults nil "Disable category-specific completion styles")
 | |
|   (completion-category-overrides nil "Disable category-specific completion styles")
 | |
|   (completion-styles '(orderless hotfuzz basic) "Set desired completion style precedence"))
 | |
| 
 | |
| ;; ---------------------------------- ;;
 | |
| ;; package-lint
 | |
| ;; ---------------------------------- ;;
 | |
| 
 | |
| ;; Load Package-Lint
 | |
| (external-package package-lint
 | |
|   "Provides a command for linting Emacs Lisp packages."
 | |
|   :commands
 | |
|   (package-lint-current-buffer))
 | |
| 
 | |
| ;; ---------------------------------- ;;
 | |
| ;; page-break-lines
 | |
| ;; ---------------------------------- ;;
 | |
| 
 | |
| (external-package page-break-lines
 | |
|   "Displays form feed characters as horizontal rules."
 | |
|   :config
 | |
|   (global-page-break-lines-mode))
 | |
| 
 | |
| ;; ---------------------------------- ;;
 | |
| ;; rainbow-delimiters
 | |
| ;; ---------------------------------- ;;
 | |
| 
 | |
| (external-package rainbow-delimiters
 | |
|   "Highlights matching delimiters with colors according to depth."
 | |
|   :hook
 | |
|   (prog-mode-hook . rainbow-delimiters-mode))
 | |
| 
 | |
| ;; ---------------------------------- ;;
 | |
| ;; resize-window
 | |
| ;; ---------------------------------- ;;
 | |
| 
 | |
| (external-package resize-window
 | |
|   "Provides an overlay that allows for quick window management inputs."
 | |
|   :bind
 | |
|   ("C-c w" . resize-window))
 | |
| 
 | |
| ;; ---------------------------------- ;;
 | |
| ;; restclient
 | |
| ;; ---------------------------------- ;;
 | |
| 
 | |
| (external-package restclient
 | |
|   "Allows for the execution of HTTP queries from plain-text query sheets."
 | |
|   :config
 | |
|   (defun restclient ()
 | |
|     "Open the restclient buffer, (re)creating it if not present."
 | |
|     (interactive)
 | |
|     (pop-to-buffer (get-buffer-create "*restclient*"))
 | |
|     (unless (derived-mode-p 'restclient-mode)
 | |
|       (restclient-mode)))
 | |
|   :commands
 | |
|   (restclient))
 | |
| 
 | |
| ;; ---------------------------------- ;;
 | |
| ;; string-inflection
 | |
| ;; ---------------------------------- ;;
 | |
| 
 | |
| (external-package string-inflection
 | |
|   "Provides case conversion functions for symbols and strings."
 | |
|   :commands
 | |
|   (string-inflection-underscore-function
 | |
|    string-inflection-pascal-case-function
 | |
|    string-inflection-camelcase-function
 | |
|    string-inflection-upcase-function
 | |
|    string-inflection-kebab-case-function
 | |
|    string-inflection-capital-underscore-function))
 | |
| 
 | |
| ;; ---------------------------------- ;;
 | |
| ;; solaire-mode
 | |
| ;; ---------------------------------- ;;
 | |
| 
 | |
| (external-package solaire-mode
 | |
|   "Recolors special buffers to distinguish them from regular buffers."
 | |
|   :custom
 | |
|   (solaire-mode-real-buffer-fn (lambda ()
 | |
|                                  (and (buffer-name (buffer-base-buffer))
 | |
|                                       (not (derived-mode-p 'neotree-mode))
 | |
|                                       (not (string-match "\*Echo Area" (buffer-name (buffer-base-buffer)))))))
 | |
|   :hook
 | |
|   (server-after-make-frame-hook . solaire-global-mode)
 | |
|   (emacs-startup-hook . solaire-global-mode))
 | |
| 
 | |
| ;; ---------------------------------- ;;
 | |
| ;; tempel
 | |
| ;; ---------------------------------- ;;
 | |
| 
 | |
| (external-package tempel
 | |
|   "Provides inline expansion for Emacs Tempo templates."
 | |
|   :preface
 | |
|   (defconst user/template-directory (locate-user-emacs-file "templates/") "Location of template file directory.")
 | |
|   (defun user/open-template-file (file-choice)
 | |
|     "Prompt the user to open a template definition file in a new buffer."
 | |
|     (interactive
 | |
|      (list (completing-read "Open template file: "
 | |
|                             (directory-files user/template-directory nil "\\.eld$"))))
 | |
|     (find-file (concat user/template-directory file-choice)))
 | |
|   (defun user/tempel-field-name (elem)
 | |
|     "Return the name of field ELEM, or nil if it has none."
 | |
|     (or (when (symbolp elem)
 | |
|           elem)
 | |
|         (when (eq (car-safe elem) 's)
 | |
|           (cadr elem))
 | |
|         (when (member (car-safe elem) '(p r r>))
 | |
|           (caddr elem))))
 | |
|   (defun user/tempel-subst-names (template args)
 | |
|     "Substitute named fields in TEMPLATE according to name/value pairs in ARGS."
 | |
|     (when-let (elem (car-safe template))
 | |
|       (cons (or (plist-get args (user/tempel-field-name elem))
 | |
|                 elem)
 | |
|             (user/tempel-subst-names (cdr template) args))))
 | |
|   (defun user/tempel-include (elem)
 | |
|     "If ELEM is (i ...), process it as an include element."
 | |
|     (when (eq (car-safe elem) 'i)
 | |
|       (if-let (template (alist-get (cadr elem) (tempel--templates)))
 | |
|           (cons 'l (user/tempel-subst-names template (caddr elem)))
 | |
|         (message "user/tempel-include: Template not found: %s" (cadr elem)))))
 | |
|   (defun user/tempel-setup-capf ()
 | |
|     "Add `tempel-complete' to `completion-at-point-functions' (buffer-local).
 | |
| If `tempel-complete' is already a member of `completion-at-point-functions', it
 | |
| is promoted to the beginning of the list of hooked functions."
 | |
|     (setq-local completion-at-point-functions (cons #'tempel-complete (remove #'tempel-complete completion-at-point-functions))))
 | |
|   :config
 | |
|   (add-to-list 'tempel-user-elements #'user/tempel-include)
 | |
|   ;; Advise `tempel-done' to jump to the end of the template before ending expansion,
 | |
|   ;; and advice `tempel-next' to call `tempel-done' if there are no remaining fields
 | |
|   (advice-add 'tempel-done :before (lambda () (goto-char (tempel--end))))
 | |
|   (advice-add 'tempel-next :after (lambda (_arg) (when tempel--active (unless (tempel--find 1) (tempel-done)))))
 | |
|   :custom
 | |
|   (tempel-mark "⇥" "Mark empty field positions with an arrow during template expansion")
 | |
|   (tempel-trigger-prefix "@" "Require an '@' before template names when triggering expansion")
 | |
|   (tempel-path (concat user/template-directory "*.eld") "Look for templates in the user template directory")
 | |
|   :hook
 | |
|   (prog-mode-hook . user/tempel-setup-capf)
 | |
|   (text-mode-hook . user/tempel-setup-capf)
 | |
|   (conf-mode-hook . user/tempel-setup-capf)
 | |
|   (eglot-managed-mode-hook . user/tempel-setup-capf)
 | |
|   :bind
 | |
|   (:map tempel-map
 | |
|         ("RET" . tempel-done)
 | |
|         ("<tab>" . tempel-next)
 | |
|         ("<backtab>" . tempel-previous)
 | |
|         ("C-g" . tempel-abort)
 | |
|         ("C-k" . tempel-abort)))
 | |
| 
 | |
| ;; ---------------------------------- ;;
 | |
| ;; treesit-auto
 | |
| ;; ---------------------------------- ;;
 | |
| 
 | |
| (external-package treesit-auto
 | |
|   "Automatically switches to Tree-sitter versions of major editing modes when available."
 | |
|   :config
 | |
|   (global-treesit-auto-mode))
 | |
| 
 | |
| ;; ---------------------------------- ;;
 | |
| ;; undo-fu-session
 | |
| ;; ---------------------------------- ;;
 | |
| 
 | |
| (external-package undo-fu-session
 | |
|   "Saves and recovers undo history of files between editing sessions."
 | |
|   :preface
 | |
|   (defconst user/undo-history-directory (locate-user-emacs-file "undo-history/") "Location of undo-fu-session history backups.")
 | |
|   :config
 | |
|   (undo-fu-session-global-mode)
 | |
|   :custom
 | |
|   (undo-fu-session-directory user/undo-history-directory "Set custom undo history storage location"))
 | |
| 
 | |
| ;; ---------------------------------- ;;
 | |
| ;; vertico
 | |
| ;; ---------------------------------- ;;
 | |
| 
 | |
| (external-package vertico
 | |
|   "Provides a vertical autocompletion UI in the minibuffer."
 | |
|   :custom
 | |
|   (vertico-cycle t "Enable wrap-around candidate cycling")
 | |
|   (vertico-count 9 "Display a maximum of 9 candidates in the minibuffer")
 | |
|   (vertico-resize nil "Keep the minibuffer at a static size")
 | |
|   (vertico-group-format nil "Disable candidate group titles")
 | |
|   (vertico-count-format nil "Disable candidate count display")
 | |
|   :hook
 | |
|   (emacs-startup-hook . vertico-mode)
 | |
|   (vertico-mode-hook . vertico-mouse-mode)
 | |
|   (rfn-eshadow-update-overlay-hook . vertico-directory-tidy)
 | |
|   :bind
 | |
|   (:map vertico-map
 | |
|         ("\r" . vertico-directory-enter)
 | |
|         ("\d" . vertico-directory-delete-char)
 | |
|         ("\M-\d" . vertico-directory-delete-word)))
 | |
| 
 | |
| ;; ---------------------------------- ;;
 | |
| ;; vundo
 | |
| ;; ---------------------------------- ;;
 | |
| 
 | |
| (external-package vundo
 | |
|   "Visualizes undo history as a tree in an interactive buffer."
 | |
|   :custom
 | |
|   (vundo-glyph-alist vundo-unicode-symbols "Visualize undo history with pretty unicode symbols")
 | |
|   :hook
 | |
|   (vundo-mode-hook . (lambda () (setq-local global-hl-line-mode nil)))
 | |
|   :bind
 | |
|   ("C-x u" . vundo))
 | |
| 
 | |
| ;; ---------------------------------- ;;
 | |
| ;; writeroom-mode
 | |
| ;; ---------------------------------- ;;
 | |
| 
 | |
| (external-package writeroom-mode
 | |
|   "Centers the working text area in the active buffer."
 | |
|   :custom
 | |
|   (writeroom-width 140 "Set the working area width to 140 columns")
 | |
|   (writeroom-maximize-window nil "Do not maximize the active window")
 | |
|   (writeroom-mode-line t "Show the mode line while writeroom-mode is active")
 | |
|   (writeroom-header-line t "Show the header line while writeroom-mode is active")
 | |
|   (writeroom-global-effects nil "Disable all frame-wide writeroom effects")
 | |
|   (writeroom-fringes-outside-margins nil "Keep the fringes close to the text")
 | |
|   :bind
 | |
|   ("C-c c" . writeroom-mode)
 | |
|   (:map writeroom-mode-map
 | |
|         ("C-<next>" . writeroom-decrease-width)
 | |
|         ("C-<prior>" . writeroom-increase-width)))
 | |
| 
 | |
| ;; --------------------------------------------------------------------------
 | |
| ;;
 | |
| ;; End of init file
 | |
| ;;
 | |
| ;; --------------------------------------------------------------------------
 | |
| 
 | |
| (provide 'init)
 | |
| 
 | |
| ;;; init.el ends here
 |