From a21c42eb27a1b130bdba5a553152e101db961ec6 Mon Sep 17 00:00:00 2001 From: Jonathan Leech-Pepin Date: Wed, 4 Jun 2025 10:37:01 -0400 Subject: [PATCH] Add emacs configs --- .gitattributes | 4 +- emacs/.config/emacs/config/config-upgrades.el | 4 + emacs/.config/emacs/post-early-init.el | 38 + emacs/.config/emacs/post-init.el | 1757 +++++++++++++++++ emacs/.config/emacs/pre-early-init.el | 23 + emacs/.config/emacs/pre-init.el | 45 + .../emacs/secure-config/config-mail.el | 245 +++ 7 files changed, 2114 insertions(+), 2 deletions(-) create mode 100644 emacs/.config/emacs/config/config-upgrades.el create mode 100644 emacs/.config/emacs/post-early-init.el create mode 100644 emacs/.config/emacs/post-init.el create mode 100644 emacs/.config/emacs/pre-early-init.el create mode 100644 emacs/.config/emacs/pre-init.el create mode 100644 emacs/.config/emacs/secure-config/config-mail.el diff --git a/.gitattributes b/.gitattributes index 7c53c7a..0524ee4 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,10 +1,10 @@ ## Encrypted .wakatime.cfg filter=ageEncrypt diff=ageEncrypt **/.kube/config filter=ageEncrypt diff=ageEncrypt -**/.tabby-client/agent/config.toml filter=ageEncrypt diff=ageEncrypt +config.toml filter=ageEncrypt diff=ageEncrypt gpg/.gnupg/private-keys-v1.d/* filter=ageEncrypt diff=ageEncrypt msmtp/.config/msmtp/config filter=ageEncrypt diff=ageEncrypt -**/emacs/config/config-mail.el filter=ageEncrypt diff=ageEncrypt +emacs/.config/emacs/secure-config/* filter=ageEncrypt diff=ageEncrypt ## Hostname interpolation emborg/.config/emborg/* filter=setHostname diff --git a/emacs/.config/emacs/config/config-upgrades.el b/emacs/.config/emacs/config/config-upgrades.el new file mode 100644 index 0000000..4b61304 --- /dev/null +++ b/emacs/.config/emacs/config/config-upgrades.el @@ -0,0 +1,4 @@ +;;; config-upgrades.el --- Extra packages purely for dependencies -*- no-byte-compile: t; lexical-binding: t; -*- + +;; * Provide +(provide 'config-upgrades) diff --git a/emacs/.config/emacs/post-early-init.el b/emacs/.config/emacs/post-early-init.el new file mode 100644 index 0000000..b3cb801 --- /dev/null +++ b/emacs/.config/emacs/post-early-init.el @@ -0,0 +1,38 @@ +;;; post-early-init.el --- DESCRIPTION -*- no-byte-compile: t; lexical-binding: t; -*- + + +;; * Custom Variables +;; ** Path Variables +;; Define the vars +(defgroup config-dirs + nil "My Configuration directories") +(defvar user-lisp-dir (expand-file-name "single-lisp" minimal-emacs-user-directory) + "Directory for standalone lisp files that are manually installed.") +(defvar user-config-dir (expand-file-name "config" minimal-emacs-user-directory) + "Directory for all configurations to go under.") +(defvar user-secure-config-dir (expand-file-name "secure-config" minimal-emacs-user-directory) + "Directory for all secure configurations to go under") +(defcustom user-repo-dir (expand-file-name "sources" (getenv "HOME")) + "User directory that repos are stored in." + :type 'directory + :group 'config-dirs) +(defcustom user-org-dir (expand-file-name "org" (getenv "HOME")) + "User's default directory for org files" + :type 'directory + :group 'config-dirs) + +;; Add them to load path for use +(add-to-list 'load-path user-config-dir) +(add-to-list 'load-path user-lisp-dir) +(add-to-list 'load-path user-secure-config-dir) + +;; ** UI Variables +(defcustom emacs-font "Fira Code Nerd Font" "Font to use" + :type 'string + :group 'config-ui) +(defcustom emoji-font "Noto Color Emoji" "Font for emojis/icons" + :type 'string + :group 'config-ui) +(defcustom emacs-theme 'doom-spacegrey "Theme to use" + :type 'string + :group 'config-ui) diff --git a/emacs/.config/emacs/post-init.el b/emacs/.config/emacs/post-init.el new file mode 100644 index 0000000..1855896 --- /dev/null +++ b/emacs/.config/emacs/post-init.el @@ -0,0 +1,1757 @@ +;;; post-init.el --- DESCRIPTION -*- no-byte-compile: t; lexical-binding: t; -*- + +;; * Fix frame right away +;; This prevents it from resizing based on Window Manager settings while being +;; created. +(setq frame-resize-pixelwise nil) + +;; * Early packages that make everything else work + +;; ** Compile Angel +;; Ensure everything is compiled when loaded except the init files +(use-package compile-angel + :ensure (:wait t) + :demand t + :custom + (compile-angel-verbose t) + :config + ;; Exclude default init files + (push "/init.el" compile-angel-excluded-files) + (push "/early-init.el" compile-angel-excluded-files) + (push "/pre-init.el" compile-angel-excluded-files) + (push "/post-init.el" compile-angel-excluded-files) + (push "/pre-early-init.el" compile-angel-excluded-files) + (push "/post-early-init.el" compile-angel-excluded-files) + ;; Exclude no-littering folders + (push "/var/.*\\.el" compile-angel-excluded-files-regexps) + (push "/etc/.*\\.el" compile-angel-excluded-files-regexps) + ;; Exclude my config files + (push "/config/.*\\.el" compile-angel-excluded-files-regexps) + (push "/secure-config/.*\\.el" compile-angel-excluded-files-regexps) + (push "/single-lisp/.*\\.el" compile-angel-excluded-files-regexps) + ;; Compile on load + (compile-angel-on-load-mode)) + +;; ** Benchmark-Init +;; Benchmark init/package loading times +(use-package benchmark-init + :ensure (:wait t) + :demand t + :hook + (after-init . benchmark-init/deactivate)) + +;; ** General +;; Use-Package keybinding enhancements +(use-package general + :ensure (:wait t) + :demand t) + +;; ** No-Littering +;; Move extra files into subfolders rather than root emacs user dir +(use-package no-littering + :ensure (:wait t) + :demand t) + +;; ** Built-in upgrades +;; Upgrade built-ins that are also developed on melpa and needed +(require 'config-upgrades) +;; * Built-in overrides +;; ** Smarter keyboard-quit (C-g) +;; From: https://emacsredux.com/blog/2025/06/01/let-s-make-keyboard-quit-smarter/ + +(define-advice keyboard-quit + (:around (quit) quit-current-context) + "Quit the current context. + +When there is an active minibuffer and we are not inside it close +it. When we are inside the minibuffer use the regular +`minibuffer-keyboard-quit' which quits any active region before +exiting. When there is no minibuffer `keyboard-quit' unless we +are defining or executing a macro." + (if (active-minibuffer-window) + (if (minibufferp) + (minibuffer-keyboard-quit) + (abort-recursive-edit)) + (unless (or defining-kbd-macro + executing-kbd-macro) + (funcall-interactively quit)))) + +;; ** Recentf +(use-package recentf + :ensure nil + :hook + (kill-emacs . recentf-cleanup) + (after-init . recentf-mode)) + +;; ** Savehist +(use-package savehist + :ensure nil + :hook + (after-init . savehist-mode)) + +;; ** Auto Save +;; Part of simple.el +(setopt auto-save-default t + auto-save-interval 300 + auto-save-timeout 30) +(add-to-list 'auto-save-file-name-transforms + `(".*" ,(expand-file-name "auto-save/" no-littering-var-directory) t)) + +;; ** Backups +(setq backup-directory-alist + `(("." . ,(expand-file-name "backups" no-littering-var-directory)))) + +;; ** Save-Place +(use-package save-place + :ensure nil + :init + (setopt save-place-forget-unreadable-files t) + :hook + (after-init . save-place-mode)) + +;; ** CUA +(use-package cua-base + :ensure nil + :init + ;; Don't turn on cua keys + (setopt cua-enable-cua-keys nil + cua-rectangle-mark-key (kbd "C-M-")) + (cua-selection-mode 1)) + +;; ** Stop minimizing Emacs on C-z +;; TWM can manage my frame +(global-unset-key (kbd "C-z")) + +;; ** Server +;; Start the server just in case +(use-package server + :ensure nil + :config + (unless (server-running-p) + (server-start))) +;; * UI Settings +;; ** Defaults +(use-package hl-line + :ensure nil + :config + (global-hl-line-mode 1)) + +(use-package help + :ensure nil + :init + (setopt help-window-select t)) + +(use-package goto-addr + :ensure nil + :hook + (find-file-hook . goto-address-prog-mode)) + +(use-package display-line-numbers + :ensure nil + :hook ((prog-mode text-mode) . display-line-numbers-mode) + :init + (defun my/display-line-numbers-relative-toggle () + (interactive) + (if display-line-numbers-mode + (if (eq display-line-numbers 'relative) + (setq display-line-numbers t) + (setq display-line-numbers 'relative))))) + +(use-package paren + :ensure nil + :init + (setopt show-paren-context-when-offscreen 'overlay) + :config + (show-paren-mode 1)) + +(use-package whitespace + :ensure nil + :init + (setq whitespace-line-column nil ; Leave at nil to follow `fill-column' + whitespace-global-modes '(not circe-mode) + whitespace-style '(tabs newline tab-mark space-mark + newline-mark face lines-tail) + whitespace-display-mappings '( + (space-mark nil) + (newline-mark 10 [172 10]) + (tab-mark 9 [183 9] [92 9]))) + :config (global-whitespace-mode 1)) + +;; ** Font +(set-fontset-font t nil emoji-font) +(set-face-attribute 'default nil + :family emacs-font + :height 110 + :weight 'normal + :width 'normal) + +;; ** Theme +;; Rainbow mode colors text when a color is recognized +(use-package rainbow-mode + :hook (prog-mode . rainbow-mode)) + +;; Solaire makes non-file buffers slightly different background to +;; catch attention +(use-package solaire-mode + :config + (solaire-global-mode 1)) + +(use-package doom-themes + :demand t + ;; Wait for theme so that doom-color works + :ensure (:wait t) + :init + (setq doom-spacegrey-comment-bg nil + doom-spacegrey-brighter-comments t + doom-spacegrey-brighter-modeline nil) + :config + (load-theme emacs-theme t) + (doom-themes-org-config)) + +;; ** Tabs +(use-package tab-bar + :ensure nil + :after doom-themes + :config + (setopt tab-bar-show 1) + (set-face-attribute 'tab-bar nil + :background (doom-color 'base2)) + (set-face-attribute 'tab-bar-tab nil + :background (doom-color 'base4) + :foreground (doom-color 'cyan)) + (set-face-attribute 'tab-bar-tab-inactive nil + :background (doom-color 'base2) + :foreground (doom-color 'base4))) + +;; ** Modeline +(use-package minions + :config (minions-mode 1)) + +(use-package doom-modeline + :config (doom-modeline-mode 1) + :init + (setq doom-modeline-minor-modes t + doom-modeline-checker-simple-format nil + doom-modeline-buffer-file-name-style 'relative-to-project + doom-modeline-enable-word-count t + doom-modeline-hud t + doom-modeline-icon t + doom-modeline-continuous-word-count-modes '(markdown-mode org-mode) + doom-modeline-indent-info nil + doom-modeline-buffer-encoding nil + doom-modeline-env-version t + doom-modeline-env-load-string "?env?")) + +;; ** Delimiters +(use-package rainbow-delimiters + :hook (prog-mode . rainbow-delimiters-mode)) + +;; ** Icons +(use-package all-the-icons) + +(use-package all-the-icons-completion + :config (all-the-icons-completion-mode +1) + :hook (marginalia-mode . all-the-icons-completion-marginalia-setup)) + +;; * UX +;; ** Keybindings + +(use-package which-key + :config (which-key-mode 1)) + +;; Generalized keybindings +(general-def + "C-M-i" #'delete-indentation + "C-M-=" #'align-regexp) + +;; ** Transient +(use-package transient + :defer t) + +;; ** Search/Replace +;; *** isearch +(use-package isearch + :ensure nil + :config + (setopt isearch-lazy-count t)) + +;; *** visual replace +(use-package visual-replace + :defer nil + :config (visual-replace-global-mode 1) + :general + (:keymaps 'isearch-mode-map + "C-c r" #'visual-replace-from-isearch) + ("C-c r" #'visual-replace) + ("C-c R" #'visual-replace-thing-at-point)) + +;; ** Undo +;; *** vundo +(use-package vundo + :init + (setopt vundo-glyph-alist vundo-unicode-symbols) + :general + ("C-z" #'vundo)) + +;; *** undu-fu-session +(use-package undo-fu-session + :init + (setopt undo-fu-session-linear t + undo-fu-session-command 'zst) + :config + (undo-fu-session-global-mode +1)) + +;; ** Casual +(use-package casual-suite + :ensure t + :defer t) + +(use-package casual-calc + :ensure nil + :general (:keymaps 'calc-mode-map "C-o" #'casual-calc-tmenu) + :after calc) + +(use-package casual-info + :ensure nil + :general (:keymaps 'Info-mode-map "C-o" #'casual-info-tmenu)) + +(use-package casual-dired + :ensure nil + :general (:keymaps 'dired-mode-map "C-o" #'casual-dired-tmenu)) + +(use-package casual-isearch + :ensure nil + :general (:keymaps 'isearch-mode-map "C-o" #'casual-isearch-tmenu)) + +(use-package casual-re-builder + :ensure nil + :general + (:keymaps 'reb-mode-map + "C-o" #'casual-re-builder-tmenu) + (:keymaps 'reb-lisp-mode-map + "C-o" #'casual-re-builder-tmenu) + :after (re-builder)) + +(use-package casual-bookmarks + :ensure nil + :after (bookmark) + :general (:keymaps 'bookmark-bmenu-mode-map + "C-o" #'casual-bookmarks-tmenu + "S" #'casual-bookmarks-sortby-tmenu + "J" #'bookmark-jump)) + +(use-package casual-ibuffer + :ensure nil + :after (ibuffer) + :general + (:keymaps 'ibuffer-mode-map + "C-o" #'casual-ibuffer-tmenu + "F" #'casual-ibuffer-filter-tmenu + "s" #'casual-ibuffer-sortby-tmenu + "[" #'ibuffer-backwards-next-marked + "]" #'ibuffer-forward-next-marked + "{" #'ibuffer-backward-filter-group + "}" #'ibuffer-forward-filter-group + "$" #'ibuffer-toggle-filter-group)) + +(use-package casual-agenda + :ensure nil + :after (org) + :general + (:keymaps 'org-agenda-mode-map + "C-o" #'casual-agenda-tmenu)) + +;; ** TODO SmartParens +;; I really need to actually start using this properly + +(defun configure-smartparens () + (require 'smartparens-config)) + +(use-package smartparens + :disabled t + :hook ((smartparens-mode . configure-smartparens) + (prog-mode . smartparens-mode))) + +;; ** TODO combobulate +;; https://github.com/mickeynp/combobulate +(use-package combobulate + :ensure (combobulate :host github :repo "mickeynp/combobulate") + :config + (setopt combobulate-key-prefix "C-c o") + :hook (prog-mode . combobulate-mode)) + +;; ** TODO puni +(use-package puni + :hook + ((prog-mode eval-expression-minibuffer-setup) . puni-mode)) + +;; ** electric-pair-mode +(use-package elec-pair + :ensure nil + :init + (electric-pair-mode)) + +;; ** aggressive-indent +(use-package aggressive-indent + :ensure t + :commands aggressive-indent-mode + :hook ((lisp-data-mode scheme-mode) . aggressive-indent-mode)) +;; ** Multiple Cursors +(use-package multiple-cursors + :general + ("C-<" #'mc/mark-previous-like-this) + ("C->" #'mc/mark-next-like-this) + ("C-c C-<" #'mc/mark-all-like-this-dwim)) +;; ** Projects +(defun my/project-save-project-files (arg) + (interactive "P") + (let* ((project-buffers (project-buffers (project-current))) + (pred (lambda () (memq (current-buffer) project-buffers)))) + (funcall-interactively #'save-some-buffers arg pred))) + +(use-package project + :ensure nil + :init + (setq project-switch-use-entire-map nil + project-vc-extra-root-markers '(".project")) + :general + (:keymaps 'project-prefix-map + "C-s" #'my/project-save-project-files)) + +(use-package project-mode-line-tag + :config (project-mode-line-tag-mode)) + +(use-package project-treemacs + :after (treemacs project) + :config + (project-treemacs-mode)) + +;; ** TODO Activities +;; Cleanup/actually use? +(use-package activities + :init + (activities-mode) + (activities-tabs-mode) + (setq edebug-inhibit-emacs-lisp-mode-bindings t + activities-bookmark-store t) + (set-face-attribute 'activities-tabs nil + :foreground (doom-color 'dark-cyan)) + + ;; Figure out keybindings, should not be prefixed with super- + :general + (:prefix "s-a" + ;; C- means changing/modifying activities + "C-n" #'activities-new + "C-a" #'activities-resume + "C-s" #'activities-suspend + "C-k" #'activities-kill + "C-r" #'activities-rename + ;; Bare means modify/list + "RET" #'activities-switch + "b" #'activities-switch-buffer + "g" #'activities-revert + "l" #'activities-list)) + +;; ** Direnv / Envrc +(use-package envrc + :config (envrc-global-mode)) + +;; ** Wakatime +(use-package wakatime-mode + :init + (global-wakatime-mode 1)) + +;; ** Highlighting +;; *** diff-hl +(use-package diff-hl + :init + (global-diff-hl-mode 1) + :config + (diff-hl-flydiff-mode 1) + (diff-hl-dired-mode 1)) + +;; *** hl-todo +(use-package hl-todo + :hook (prog-mode . hl-todo-mode)) + +;; ** Indent-bars +(use-package indent-bars + :init + (setopt indent-bars-no-descend-lists t + indent-bars-treesit-support t + indent-bars-treesit-ignore-blank-lines-types '("module") + indent-bars-treesit-wrap '((python argument_list parameters + list list_comprehension + dictionary dictionary_comprehension + parenthesized_expression subscript)) + indent-bars-treesit-scope '((python function_definition class_definition for_statement + if_statement with_statement while_statement))) + :hook + ((python-base-mode yaml-mode yaml-ts-mode) . indent-bars-mode) + ) +;; ** Dashboard +(defun my/delay-dashboard () + (message "Updating Dashboard with Agenda view") + (setopt dashboard-items '((recents . 5) + (projects . 5) + (agenda . 5) + (bookmarks . 5) + (registers . 5))) + (dashboard-insert-startupify-lists t)) + +(use-package dashboard + :init + (setopt dashboard-projects-backend 'project-el + initial-buffer-choice (lambda () (get-buffer-create "*dashboard*")) + dashboard-center-content t + dashboard-vertically-center-content t + dashboard-icon-type 'all-the-icons + dashboard-display-icons-p t + dashboard-set-heading-icons t + dashboard-set-file-icons t + dashboard-items '((recents . 5) + (projects . 5) + (bookmarks . 5) + (registers . 5))) + :config + (dashboard-setup-startup-hook) + (run-with-idle-timer 5 nil #'my/delay-dashboard)) + +;; * Buffer/Window Management +;; ** Ibuffer / Buffer list +(use-package ibuffer + :ensure nil + :general + ([remap list-buffers] #'ibuffer)) + +(defun my/ibuffer-project-run () + (setq ibuffer-filter-groups + (ibuffer-project-generate-filter-groups)) + (unless (eq ibuffer-sorting-mode 'project-file-relative) + (ibuffer-do-sort-by-project-file-relative))) + +(use-package ibuffer-project + :hook (ibuffer . my/ibuffer-project-run)) + +(use-package ibuffer-sidebar + :commands (ibuffer-sidebar-toggle-sidebar) + :hook (ibuffer-sidebar-mode . my/ibuffer-project-run)) + +;; ** Popups +;; *** Popper +(use-package popper + :defer nil + :init + ;; Window Definitions + (setq popper-group-function #'popper-group-by-project + popper-reference-buffers + '("\\*eldoc for.*\\*$" + "\\*Messages\\*" + "\\*Warnings\\*" + help-mode + flymake-diagnostics-buffer-mode + compilation-mode)) + ;; Must come before enabling + :config + (popper-mode +1) + (popper-echo-mode +1) + :general + ("C-`" #'popper-toggle) + ("M-`" #'popper-cycle) + ("C-M-`" #'popper-toggle-type)) + +;; ** Window Management +;; *** Ace-Window +(defun my/ace-window-dispatch-always (prefix) + "Run `ace-window' with `aw-dispatch-always' set to `t'" + (interactive "p") + (let ((aw-dispatch-always t)) + (ace-window prefix))) + +(use-package ace-window + :config + (setopt aw-scope 'frame) + (ace-window-display-mode 1) + (ace-window-posframe-mode 1) + (set-face-attribute 'aw-leading-char-face nil + :foreground (doom-color 'red) + :background (doom-color 'base7) + :height 6.0) + :general + ([remap other-window] #'ace-window) + ("C-x O" #'my/ace-window-dispatch-always)) + +;; ** Treemacs +(defun my/treemacs-setup-title () + (let* ((bg (face-attribute 'default :background)) + (bg2 (doom-lighten bg 0.2)) + (fg (face-attribute 'default :foreground))) + (face-remap-add-relative + 'header-line + :background bg :foreground fg + :box `(:line-width ,(/ (line-pixel-height) 4) :color ,bg2)))) + +(use-package treemacs + :init + (setq treemacs-map (make-sparse-keymap "Treemacs")) + :config + (treemacs-fringe-indicator-mode 'only-when-focused) + + + :hook (treemacs-mode . my/treemacs-setup-title) + :commands (treemacs-select-window) + :general + ("M-0" #'treemacs-select-window) + (:prefix "C-c" + "t" treemacs-map) + (:keymaps 'treemacs-map + "1" #'delete-other-window + "t" #'treemacs + "B" #'treemacs-bookmark + "C-t" #'treemacs-find-file + "M-t" #'treemacs-find-tag)) + +;; *** Treemacs Icons +(use-package treemacs-all-the-icons + :after (treemacs all-the-icons)) +;; * Completion +;; ** General +(use-package completion-preview + :ensure nil + :defer nil + :init + (setq completion-preview-minimum-symbol-length 2) + :hook + ((prog-mode text-mode comint-mode) . completion-preview-mode) + :general + (:keymaps 'completion-preview-active-mode-map + "M-n" #'completion-preview-next-candidate + "M-p" #'completion-preview-previous-candidate + "M-i" #'completion-preview-insert)) + +;; ** Minibuffer +;; *** Vertico +(elpaca (vertico :files(:defaults "extensions/*")) + (use-package vertico) + (use-package vertico-directory + :after vertico + :ensure nil + :general + (:keymaps 'vertico-map + "\r" #'vertico-directory-enter + "\d" #'vertico-directory-delete-char + "M-\d" #'vertico-directory-delete-word)) + (vertico-mode)) + +;; *** Orderless +(use-package orderless + :init + (setq completion-styles '(orderless))) + +;; *** Marginalia +(use-package marginalia + :defer nil + :config (marginalia-mode 1) + :general + (minibuffer-mode-map "s-a" #'marginalia-cycle)) + +;; ** Consult +(use-package consult + :config + (setq consult-narrow-key "<" + consult-preview-key '(:debounce 0.2 any)) + :general + ([remap switch-to-buffer] #'consult-buffer) + ([remap project-switch-to-buffer] #'consult-project-buffer) + ([remap switch-to-buffer-other-window] #'consult-buffer-other-window) + ([remap switch-to-buffer-other-frame] #'consult-buffer-other-frame) + ([remap repeat-complex-command] #'consult-complex-command) + ([remap goto-line] #'consult-goto-line) + ([remap apropos-command] #'consult-apropos) + ([remap yank-pop] #'consult-yank-pop) + ([remap pop-global-mark] #'consult-global-mark) + ("C-c k" #'consult-kmacro) + ("C-S-s" #'consult-isearch-history) + ([remap project-find-regexp] #'consult-ripgrep) + (:keymaps 'goto-map + "M-g" #'consult-line + "M-G" #'consult-line-multi + "o" #'consult-outline + "i" #'consult-imenu-multi + "M-i" #'consult-imenu) + (:keymaps 'isearch-mode-map + "M-i" #'consult-line + "M-I" #'consult-line-multi)) + +;; *** Consult-Flymake +(use-package consult-flymake + :ensure nil + :after (consult flymake) + :general + (:keymaps 'goto-map + "e" #' consult-flymake)) + +;; *** Consult-Eglot +(use-package consult-eglot + :after (consult eglot) + :general + (:keymaps 'eglot-mode-map + [remap xref-find-apropos] #'consult-eglot-symbols) + (:keymaps 'search-map :predicate '(memq 'eglot--managed-mode local-minor-modes) + "S" #'consult-eglot-symbols)) + +;; ** Embark +(use-package embark + :general ("s-e" #'embark-act)) + +;; *** Embark-Consult +(use-package embark-consult + :after (consult embark)) + +;; ** Corfu +(elpaca (corfu :files (:defaults "extensions/*")) + (use-package corfu + :ensure nil + :general + ("M-" #'complete-symbol) + ("M-/" #'completion-at-point) + :init + (setopt corfu-auto nil + corfu-auto-delay 0.1 + corfu-preselect-first t + corfu-preview-current t + corfu-cycle t + corfu-quit-at-boundary nil + corfu-quit-no-match t + corfu-scroll-margin 2)) + (global-corfu-mode 1)) + +;; *** Cape +(defun my/abbrev-completion-completers () + (add-to-list 'completion-at-point-functions #'cape-abbrev)) + +(use-package cape + :commands cape-abbrev) + +;; *** kind-icon +(use-package kind-icon + :after corfu + :custom + (kind-icon-default-face 'corfu-default) ; to compute blended backgrounds correctly + :config + (add-to-list 'corfu-margin-formatters #'kind-icon-margin-formatter)) + +;; ** Tabby +(defun my/tabby-disable-overlay () + "Disable overlays from Tabby after canceling or dismissing" + (member real-last-command '(keyboard-quit tabby-dismiss))) + +(use-package tabby + :ensure (tabby :host github :repo "alan-w-255/tabby.el" + :branch "master" + :files (:defaults "node_scripts") + :main "tabby.el") + :init + (setopt tabby-disable-predicates '(my/tabby-disable-overlay) + tabby-disable-display-predicates '(my/tabby-disable-overlay)) + :hook (prog-mode . tabby-mode) + :general + (:keymaps 'tabby-mode-map + "C-l" #'tabby-accept-completion-by-line + "C-" #'tabby-accept-completion-by-word + "C-j" #'tabby-accept-completion)) + +;; ** Snippets +;; *** Tempel +(use-package tempel + :ensure t + :init + (defun tempel-setup-capf () + ;; Add the Tempel Capf to `completion-at-point-functions'. + ;; `tempel-expand' only triggers on exact matches. Alternatively use + ;; `tempel-complete' if you want to see all matches, but then you + ;; should also configure `tempel-trigger-prefix', such that Tempel + ;; does not trigger too often when you don't expect it. NOTE: We add + ;; `tempel-expand' *before* the main programming mode Capf, such + ;; that it will be tried first. + (setq-local completion-at-point-functions + (cons #'tempel-expand + completion-at-point-functions))) + + ;; Optionally make the Tempel templates available to Abbrev, + ;; either locally or globally. `expand-abbrev' is bound to C-x '. + ;; (add-hook 'prog-mode-hook #'tempel-abbrev-mode) + (global-tempel-abbrev-mode) + :general + ("M-*" #'tempel-insert + "M-+" #'tempel-complete) + :hook ((conf-mode prog-mode text-mode) . tempel-setup-capf)) + +;; * Shell +;; ** Eshell +;; ** Eat +(use-package eat + :general + (:keymaps 'project-prefix-map + "E" #'eat-project)) + +;; * Text Editing +;; ** Formatting +;; *** apheleia +(use-package apheleia + :config + (apheleia-global-mode 1)) + +;; *** ws-butler +(use-package ws-butler + :config (ws-butler-global-mode 1)) + +;; ** Spell Check +;; *** jinx +(use-package jinx + :hook (emacs-startup . global-jinx-mode) + :general + ([remap ispell-word] #'jinx-correct + "C-M-$" #'jinx-languages)) + +;; *** flymake-vale +(elpaca (flymake-vale :repo "https://github.com/tpeacock19/flymake-vale" + :refs nil + :files (:defaults)) + (use-package flymake-vale + :ensure nil + :config + (add-to-list 'flymake-vale-modes 'python-ts-mode) + (add-to-list 'flymake-vale-modes 'python-mode) + :hook ((find-file . flymake-vale-maybe-load) + (eglot-managed-mode . flymake-vale-maybe-load)))) + +;; ** Modes +;; *** Outline +(use-package outline + :ensure nil + :defer t + :diminish outline-minor-mode) +;; **** Outline-Indent +(use-package outline-indent + :defer t + :hook + ((python-base-mode yaml-mode yaml-ts-mode) . outline-indent-minor-mode)) + +;; **** Outshine +(use-package outshine + :commands outshine-mode + :diminish "Outl") + +;; **** Outorg +(use-package outorg + :after org + :commands (outorg-edit-as-org)) + +;; *** Markdown +(use-package markdown-mode + :hook ((markdown-mode . auto-fill-mode) + (markdown-mode . flymake-mode))) + +;; *** Org-Mode +;; **** Variables +(defgroup config-org nil "" + :group 'config-init) + +(defcustom user-org-dir "~/org" "Default directory for org files" + :type 'directory + :group 'config-dirs) + +(defcustom user-roam-dir (expand-file-name "roam" user-org-dir) + "Directory for OrgRoam" + :type 'directory + :group 'config-dirs) + +(defcustom user-roam-daily-dir (expand-file-name "daily" user-roam-dir) + "Directory for Daily files in OrgRoam" + :type 'directory + :group 'config-dirs) + +(defcustom default-org-agenda-files `(,user-org-dir ,user-roam-dir ,user-roam-daily-dir) + "" + :type 'list + :group 'config-org) + +(defconst org-roam-states-not-todo '("LOG" "FUP" "TBR" "RDN")) + +;; **** Functions +(defun org-daily-agenda (arg) + "Allow direct binding to daily agenda (a)" + (interactive "P") + (org-agenda arg "a")) + +(defun my/org-statistics-update (n-done n-not-done) + "Switch to appropriate DONE (Completed) or TODO (Starting) + state when statistics are updated." + (let ((org-inhibit-logging 'note)) ; turn off logging + (org-todo (if (= n-not-done 0) 'done 1)))) + +(defun my/org-checkbox-statistics-update () + (let ((todo-state (org-get-todo-state)) + (cookie-re "\\[[0-9/%]+\\]") + (org-inhibit-logging 'note) + beg end value done) + (unless (not todo-state) + (save-excursion + (org-back-to-heading t) + (setq beg (point)) + (end-of-line) + (setq end (point)) + (goto-char beg) + (when (re-search-forward cookie-re end t) + (goto-char (match-beginning 0)) + (setq value + (substring (org-element-statistics-cookie-interpreter + (org-element-statistics-cookie-parser) t) + 1 -1)) + (message "Value" value ) + (cond + ((equal value "100%") + (setq done 'done)) + ((string-match "%" value) + (setq done 1)) + ((apply #'equal (split-string value "/")) + (setq done 'done)) + (t + (setq done 1))) + (org-todo done)))))) + +(defun my/setup-ql-views () + "Add my org-ql views to the defaults rather than overwriting" + (mapc (lambda (view) + (add-to-list 'org-ql-views view)) + org-ql--my-views)) + +(defun my/org-agenda-files-track-init () + "(Re)initialize dynamic agenda files. + +This can take a long time, so it is recommended to run this only +on installation and when first tasks are added to many files via +methods the save hook cannot detect, like file synchronization." + (interactive) + (require 'org-agenda-files-track-ql) + (setq org-agenda-files default-org-agenda-files) + (org-agenda-files-track-ql-cleanup-files 'full) + (message "Initialized org agenda files")) + +(defun my/org-roam-capture-daily (date) + (interactive "P") + (message "%S" date) + (cond + ((equal '(16) date) + (org-roam-dailies-goto-date)) + ((equal '(4) date) + (org-roam-dailies-goto-yesterday 1)) + ((eq '- date) + (org-roam-dailies-goto-tomorrow 1)) + (date + (org-roam-dailies-goto-yesterday date)) + ((org-roam-dailies-goto-today)))) + +(defun my/org-roam-node-annotate (node) + (let* ((id (org-roam-node-id node)) + (title (org-roam-node-title node)) + (aliases (org-roam-node-aliases node)) + (tags (org-roam-node-tags node)) + (blinks (length (org-roam-db-query + [:select (funcall count source) + :from links + :where (= dest $s1) + :and (= type "id")] + id))) + (flinks (length (org-roam-db-query + [:select dest + :from links + :where (= source $s1) + :and (= type "id") + :group-by dest] + id))) + (file (org-roam-node-file node)) + (dir (file-name-nondirectory (directory-file-name + (file-name-directory file))))) + (concat (propertize " " 'display `(space :align-to center)) + title " " (format "%S" aliases) " " + (format "B:%s" blinks) + " " + (format "F:%s" flinks) + " " + (s-truncate 8 (format "%s" dir) "...")))) + +;; **** Org-Mode +(use-package org + :hook + ((org-mode . auto-fill-mode) + (org-after-todo-statistics . my/org-statistics-update) + (org-checkbox-statistics . my/org-checkbox-statistics-update) + (org-mode . my/abbrev-completion-completers)) + :config + (push 'org-self-insert-command completion-preview-commands) + (add-to-list 'org-modules 'org-habit) + (setq org-agenda-custom-commands + `(("r" "Roam" + ((org-ql-block (quote ,org-ql-query--roam) + ((org-agenda-max-entries 5))))) + ("H" "Habit status" + ((org-ql-block (quote ,org-ql-query--habit-stats)))) + ("h" "Habits" + ((org-ql-block (quote ,org-ql-query--habits)))) + ("t" "Todo test" + ((org-ql-block (quote ,org-ql-query--todos)))) + ("A" "Test blocks" + ((org-ql-block (quote ,org-ql-query--todos) + (;; (org-agenda-max-entries 5) + (org-ql-block-header "TODOs"))) + (org-ql-block (quote ,org-ql-query--roam) + (;; (org-agenda-max-entries 5) + (org-ql-block-header "Roam Review"))) + ;; (org-ql-block (quote ,org-ql-query--habits) + ;; ((org-ql-block-header "Habits"))) + (org-ql-block (quote ,org-ql-query--habit-stats) + ((org-ql-block-header "Habits"))) + (org-ql-block (quote ,org-ql-query--reading-list) + ((org-ql-block-header "Reading List"))))))) + + (setq + ;; Overall Org + org-startup-indented t + org-agenda-compact-blocks t + org-log-into-drawer t + org-enforce-todo-dependencies t + org-enforce-todo-checkbox-dependencies t + org-list-allow-alphabetical t + org-agenda-include-diary nil + org-agenda-span 'day + + ;; ID Links + org-id-link-to-org-use-id 'create-if-interactive + org-id-link-consider-parent-id t + + ;; Clocks and timestamps + org-clock-into-drawer t + org-clock-rounding-minutes 15 + + ;; Habits + org-habit-show-habits-only-for-today nil + + ;; Capturing + org-outline-path-complete-in-steps nil + org-refile-use-outline-path t + org-refile-targets '((nil :maxlevel . 3) + ("~/jlptech/internal/timetracking.org" :tag . "#work") + (org-agenda-files :maxlevel . 3)) + org-capture-templates--email + `((:group "Mail Task" + :template "* TODO %:subject - %:fromname\n\n%a\n\n%i" + :contexts ((:in-mode "mu4e-view-mode") + (:in-mode "mu4e-headers-mode")) + :children (("Innovacare" + :keys "i" + :file "~/jlptech/internal/timetracking.org" + :type entry + :olp ("Innovacare" "Tasks") + :prepend t)))) + org-capture-templates--health + `(("Health" + :keys "H" + :file "~/org/roam/20240610085623-health.org" + :type table-line + :immediate-finish t + :children (("Weight" + :keys "w" + :olp ("Weight") + :template "| %^u | %^{Weight} |" + ) + ("Exercise" + :keys "e" + :olp ("Exercise") + :template "| %^u | %^{Type} | %^{Amount}")))) + org-capture-templates + '(("P" "3d Printing related") + ("PQ" "Qidi X Max 3") + ("PQs" "New Spool" + entry + (file+olp "~/org/printing.org" "Qidi XMax 3" "Filaments") + "* %^{Name} +#+name: %\\1 +| | Print | Weight| Cost | +|-+-------+-------|-| +| | | | | +|-+-------+-------|-| +|#| Total | | | +|^| | tot | | +|$| start=%^{Weight} | | | +|$| color=%^{Color} | | | +|$| type=%^{Type} | | | +|$| cost=%^{Cost} | | | +#+TBLFM: $tot=vsum(@I..@II)::@2$4..@3$4=($cost/$start)*$-1;%.2f +" + :prepare-finalize (lambda () (org-store-link 'nil 't)) + :after-finalize (lambda () (org-capture 'nil "PQS")) + :immediate-finish t) + ("PQS" "Summary Spool" + table-line + (file+olp "~/org/printing.org" "Qidi XMax 3" "Filament Amount") + "| %(substring-no-properties (cadar org-stored-links)) | | | | | |" + :immediate-finish t) + ("p" "Org protocol" + entry + (file+headline "~/org/notes.org" "Inbox") + "* %^{Title}\n\nSource: %u\n\n[[%:link][%:description]]\n\n%i")) + org-capture-templates-contexts + '(("i" ((in-mode . "mu4e-view-mode") + (in-mode . "mu4e-headers-mode")))) + org-capture-templates (doct-add-to org-capture-templates + org-capture-templates--email) + org-capture-templates (doct-add-to org-capture-templates + org-capture-templates--health) + ;; Publishing + org-publish-project-alist + `(("vitae" + :base-directory ,(expand-file-name "vitae" user-repo-dir) + :publishing-directory ,(expand-file-name "vitae/export" user-repo-dir) + :publishing-function org-latex-publish-to-pdf)) + + ;; Tags + org-use-fast-tag-selection t + org-tag-persistent-alist + '( + ;; Org Roam Tags + (:startgroup) + ("Context" . "Context") + (:grouptags) + ("#work" . ?W) + ("#homelab" . ?H) + ("#FTC". ?F) + ("#aurelius" . ?A) + ("#config" . ?C) + ("#dev" . ?D) + (:endgroup) + (:startgroup) + ("ConfigType" . "Config Type") + (:grouptags) + ("%theme" . ?t) + ("%workflow" . ?w) + ("%binding" . ?b) + (:endgroup) + ) + ;; Todo Configs + org-todo-keywords + '((sequence "TODO(t)" "PROG(p)" "PEND(n@)" "|" "DONE(d@)") + (sequence "TODO(t)" "PROG(p)" "BLOCK(b)" "|" "DONE(d@)") + (type "RISK(r@)" "ISSUE(i@)" "|" "AVRT(a@)") + (sequence "|" "CANC(c@)") + (sequence "PRCH(P)" "|" "BGHT(B)") + ;; Org Roam Sequences + (sequence "LOG(l)" "FUP(f)" "|" "LOGD(L@)") + (sequence "TBR(T)" "RDN(R)" "|" "READ(D@)") + (sequence "BLOG(g)" "|" "BLGD(G@)")) + org-modern-todo-faces + `(("TODO" . (:foreground ,(doom-color 'base1) :background ,(doom-color 'green) + :weight bold)) + ("PROG" . (:foreground ,(doom-color 'base1) :background ,(doom-color 'dark-cyan) + :weight bold)) + ("PEND" . (:foreground ,(doom-color 'magenta) + :weight bold + :inverse-video t)) + ("BLOCK" . (:foreground ,(doom-color 'orange) :inverse-video t)) + ("RISK" . (:foreground ,(doom-color 'base2) :background ,(doom-color 'red) + :weight bold)) + ("ISSUE" . (:foreground ,(doom-color 'base8) :background ,(doom-color 'red) + :weight bold)) + ("AVRT" . (:foreground ,(doom-color 'cyan) + :weight bold + :inverse-video t)) + ("DONE" . (:foreground ,(doom-color 'base5) :background ,(doom-color 'base0) + :weight bold)) + ("CANC" . (:foreground ,(doom-color 'orange) :background ,(doom-color 'base0) + :weight bold)) + ;; Org Roam faces + ("LOG" . (:foreground ,(doom-color 'violet) :background ,(doom-color 'dark-blue) + :weight bold)) + ("FUP" . (:foreground ,(doom-color 'magenta) :background ,(doom-color 'dark-blue) + :weight bold)) + ("TBR" . (:foreground ,(doom-color 'green) :background ,(doom-color 'base4) + :weight :bold)) + ("RDN" . (:foreground ,(doom-color 'yellow) :background ,(doom-color 'base4) + :weight bold)) + ("READ" . (:foreground ,(doom-color 'dark-cyan) :background ,(doom-color 'base0) + :weight bold)) + ;; Blogging faces + ("IDEA" . (:foreground ,(doom-color 'base1) :background ,(doom-color 'green) + :weight bold)) + ("WRITE" . (:foreground ,(doom-color 'base1) :background ,(doom-color 'dark-cyan) + :weight bold)) + ("TITLE" . (:foreground ,(doom-color 'magenta) + :weight bold + :inverse-video t)) + ("POSTED" . (:foreground ,(doom-color 'base5) :background ,(doom-color 'base0) + :weight bold)) + ) + org-todo-keyword-faces org-modern-todo-faces + ) + :general + ("" #'org-daily-agenda) + ("S-" #'org-agenda) + ("" #'org-capture)) + +;; ***** doct +(use-package doct + :defer t) + +;; ***** org-ql +;; Does not need to wait for org because all I do in the use-package form is set +;; up the queries. +(use-package org-ql + :init + ;; Setup org-ql filtering variables so views and agendas can use them + (setq + ;; Filters + org-ql-todo--roam-log '(todo "LOG" "FUP") + org-ql-todo--reading-list '(todo "TBR" "RDN") + org-ql-todo--excl-testing '(not (category "testing")) + + ;; Queries + org-ql-query--roam + `(and ,org-ql-todo--roam-log + (tags "review")) + org-ql-query--habit-stats + '(and (habit) + ;; (not (scheduled :to today)) + ) + org-ql-query--habits + '(and (habit) + (scheduled :to today)) + org-ql-query--reading-list + `(and ,org-ql-todo--reading-list + ,org-ql-todo--excl-testing) + org-ql-query--todos + `(and (todo) + (not ,org-ql-todo--roam-log) + ,org-ql-todo--excl-testing + (not (habit)) + (not ,org-ql-todo--reading-list)) + ;; Views + org-ql--my-views `(("Roam: Review" + :buffers-files org-agenda-files + :query ,org-ql-query--roam + :sort (date priority) + :title "Roam Review" + :super-groups ((:take (5 (:tag "review"))))) + ("Habit: status" + :buffers-files org-agenda-files + :query ,org-ql-query--habit-stats + :sort (date priority) + :title "Habit status" + :super-groups ((:name "Overdue" :scheduled past) + (:name "Current" :scheduled today) + (:name "Up-to-date" :scheduled future))) + ("Reading List" + :buffers-files org-agenda-files + :query ,org-ql-query--reading-list + :title "Reading List" + :super-groups org-super-agenda-groups))) + :defer t) + +;; ***** org-agenda-files-track-ql +(use-package org-agenda-files-track-ql + :defer nil + :after (org-ql org-roam) + :init + ;; Setup org agenda files to account for sync changes + (if (file-directory-p "~/jlptech/internal") + (add-to-list 'default-org-agenda-files "~/jlptech/internal")) + (org-agenda-files-track-ql-mode t)) + +;; ***** Org-Modern +(use-package org-modern + :after org + :defer t + :config + (setopt org-modern-hide-stars 'leading + org-modern-table nil + org-auto-align-tags nil + org-tags-column 0) + (set-face-attribute 'org-modern-date-active nil + :foreground (doom-color 'yellow) + :background (doom-color 'base2)) + (set-face-attribute 'org-modern-date-inactive nil + :foreground (doom-color 'orange) + :background (doom-color 'base2)) + (set-face-attribute 'org-modern-time-active nil + :foreground (doom-color 'yellow) + :background (doom-color 'base2) + :inverse-video t) + (set-face-attribute 'org-modern-time-inactive nil + :foreground (doom-color 'orange) + :background (doom-color 'base2) + :inverse-video t) + :hook (org-mode . org-modern-mode)) + +;; ***** Org-Modern-Indent +(use-package org-modern-indent + :ensure (org-modern-indent :host github :repo "jdtsmith/org-modern-indent") + :hook (org-mode . org-modern-indent-mode)) + +;; ***** org-appear +(use-package org-appear + :init + (setopt org-hide-emphasis-markers t + org-pretty-entities t + org-hidden-keywords '() + org-appear-autoemphasis t + org-appear-autolinks t + org-appear-autosubmarkers t + org-appear-autoentities t + org-appear-autokeywords t + org-appear-delay 0.5 + org-appear-trigger 'always) + :hook (org-mode . org-appear-mode)) +;; ***** Org-Super-Agenda +(use-package org-super-agenda + :after (org) + :defer t + :init + (add-to-list 'warning-suppress-types '(org-element)) + :config + (org-super-agenda-mode +1) + (setq org-super-agenda-groups + `((:name "" + :and (:todo ("FUP") + :tag "review")) + (:name "Logs" + :and (:todo "LOG" + :tag "review")) + (:name "" + :todo "RDN") + (:name "To read" + :todo "TBR") + (:name "Overdue Habits" + :and (:habit t + :scheduled past)) + (:name "Due Habits" + :and (:habit t + :scheduled today)) + (:name "Up-to-date Habits" + :and (:habit t + :scheduled future)) + + ;; Keep this last so timegrid doesn't steal + (:time-grid t + :order -1)))) + +;; **** Extra functionality +;; ***** verb +(use-package verb + :after org + :defer t + :general + (:keymaps 'org-mode-map + "C-c C-r" verb-command-map)) + +;; ***** DISABLED Calendar +(use-package khalel + :after org + :defer t + :disabled t + :autoload khalel-import-events + :init + (setq khalel-capture-key "e" + khalel-import-org-file (expand-file-name "khal.org" org-directory) + khalel-import-start-date "-5d" + khalel-import-end-date "+30d" + khalel-import-org-file-confirm-overwrite nil) + (run-at-time 0 300 #'khalel-import-events) + :config + (khalel-add-capture-template)) + +;; **** Exporters +;; ***** ox-hugo +(use-package ox-hugo + :after ox + :ensure (:host github :repo "jleechpe/ox-hugo")) + +;; **** Org-Roam +(use-package org-roam + :ensure t + :defer t + :after org + :init + (setq org-roam-org-mode-map (make-sparse-keymap "Org-Roam")) + + (setq org-roam-v2-ack t + org-roam-directory user-roam-dir + org-roam-db-location (expand-file-name "db/org-roam.db" user-org-dir) + org-roam-node-annotation-function #'my/org-roam-node-annotate + org-roam-completion-everywhere t + org-roam-dailies-capture-templates + '(("d" "default" entry "* %c" + :target + (file+head+olp "%<%Y-%m-%d>.org" + "#+title: %<%Y-%m-%d> +#+filetags: :log:review:\n\n* Log" + ("Log")) + :empty-lines 3))) + + :config + (org-roam-db-autosync-mode 1) + + :general + ("C-" #'my/org-roam-agenda-all-files + "C-" #'my/org-roam-capture-daily) + (:keymaps 'org-mode-map + "C-c r" org-roam-org-mode-map) + (:keymaps 'org-roam-org-mode-map + "t" #'org-roam-buffer-toggle + "d" #'org-roam-buffer-display-dedicated)) + +;; ***** org-roam-ui +(use-package org-roam-ui + :config + (setopt org-roam-ui-sync-theme t + org-roam-ui-follow t + org-roam-ui-update-on-save t + org-roam-ui-open-on-start nil) + :general + (:keymaps 'org-roam-org-mode-map + "u" #'org-roam-ui-open)) + +;; ***** consult-org-roam +(use-package consult-org-roam + :after (org-roam) + :init + (setopt consult-org-roam-grep-func #'consult-ripgrep + consult-org-roam-buffer-narrow-key ?r + consult-org-roam-buffer-after-buffers t) + :config + (consult-org-roam-mode 1) + :general + (:keymaps 'org-roam-org-mode-map + "b" #'consult-org-roam-backlinks + "B" #'consult-org-roam-backlinks-recursive + "f" #'consult-org-roam-forward-links + "g" #'consult-org-roam-search ; mirror `C-x p g' from project + )) + +;; ***** consult-notes +(use-package consult-notes + :defer t) + +;; * Programming +;; ** Caddy +(use-package caddyfile-mode + :defer t) + +;; ** Dockerfiles +(defun my/ts-fontification (n custom) + "" + (let* ((original (nth n treesit-font-lock-feature-list))) + (mapc (lambda (x) (add-to-list 'original x 't)) custom) + (setf (nth n treesit-font-lock-feature-list) original) + (treesit-font-lock-recompute-features))) + +(defun my/dockerfile-ts-fontification () + (my/ts-fontification 3 '(definition))) + +(use-package dockerfile-ts-mode + :ensure nil + :config + (setq my/dockerfile-ts-mode--font-lock-settings + (treesit-font-lock-rules + :language 'dockerfile + :feature 'definition + '((arg_instruction (unquoted_string) @font-lock-variable-name-face) + (env_pair name: (unquoted_string) @font-lock-variable-name-face) + (env_pair value: (unquoted_string) @font-lock-constant-face) + (workdir_instruction (path) @font-lock-constant-face) + (label_pair key: (unquoted_string) @font-lock-variable-name-face) + (label_pair value: (unquoted_string) @font-lock-constant-face)) + :language 'dockerfile + :feature 'string + '((json_string) @font-lock-string-face)) + dockerfile-ts-mode--font-lock-settings + (append dockerfile-ts-mode--font-lock-settings + my/dockerfile-ts-mode--font-lock-settings)) + + :hook ((dockerfile-ts-mode . my/dockerfile-ts-fontification))) + +;; ** DHall +(use-package dhall-mode + :defer t) + +;; ** DotNet +;; *** C# +(use-package csharp-mode + :ensure nil + :init + (add-to-list 'major-mode-remap-alist + '(csharp-mode . csharp-ts-mode)) + :defer t) + +;; *** F# +(use-package fsharp-mode + :defer t) + +;; ** Javascript +;; I keep setting this manually +(setopt js-indent-level 2) + +(use-package json-ts-mode + :ensure nil + :defer nil ; Do not defer since json-mode is from a different file + :init + (add-to-list 'major-mode-remap-alist + '(json-mode . json-ts-mode))) + +;; ** Just +(use-package just-mode + :defer t) + +(use-package justl + :general + (:keymaps 'project-prefix-map + "j" #'justl)) + +;; ** Lisps +;; *** Emacs-Lisp +(defun ignore-scratch () + (unless (string= (buffer-name) "*scratch*") + (outshine-mode 1))) + +(use-package emacs-lisp + :ensure nil + :hook (emacs-lisp-mode . ignore-scratch)) + +;; *** Sly +(use-package sly + :commands (sly sly-connect) + :config + (setq inferior-lisp-program "sbcl")) + +;; ** Lua +(use-package lua-mode + :defer t) + +;; ** Powershell +(use-package powershell + :defer t) + +;; ** Puppet +(use-package puppet-mode + :defer t) + +;; ** Python +(defun my/python-apheleia-formatters () + "Update apheleia mode formatters for python" + (setf (alist-get 'python-base-mode apheleia-mode-alist) + '(ruff-isort ruff)) + (setf (alist-get 'python-mode apheleia-mode-alist) + '(ruff-isort ruff)) + (setf (alist-get 'python-ts-mode apheleia-mode-alist) + '(ruff-isort ruff))) + +(defun my/python-debugpy-adapters () + "Add extra debugpy adapters" + (add-to-list + 'dape-configs + '(debugpy-remote + modes (python-mode python-ts-mode) + host "localhost" + port (lambda () + (let ((port (if (bound-and-true-p debugpy-port) + debugpy-port + 5678))) + (read-number "Port: " port))) + :request "attach" + :type "python" + :justMyCode t + :showReturnValue t + :pathMappings [( + :localRoot (lambda () + (expand-file-name + (read-directory-name "Local Source directory: " + (funcall dape-cwd-fn)))) + :remoteRoot (lambda () + (read-string "Remote source directory: " + ".")))]))) + +(use-package python + :ensure nil + :init + (add-to-list 'major-mode-remap-alist + '(python-mode . python-ts-mode)) + (setq eglot-python-backend + `((,(executable-find "pyright-langserver") "--stdio") "ruff-lsp")) + :config + (setq python-indent-guess-indent-offset-verbose nil + python-flymake-command '("flake8" "--max-line-length=88" "-")) + (cond + ((executable-find "ipython") + (progn + (setq python-shell-buffer-name "IPython" + python-shell-interpreter "ipython" + python-shell-interpreter-args "-i --simple-prompt"))) + ((executable-find "python3") + (setq python-shell-interpreter "python3")) + ((executable-find "python2") + (setq python-shell-interpreter "python2")) + (t + (setq python-shell-interpreter "python"))) + ;; Match fill column to black settings + :hook + ((python-base-mode . (lambda () (set-fill-column 88)))) + (apheleia-mode . my/python-apheleia-formatters) + (dape-breakpoint-global-mode . my/python-debugpy-adapters)) + +;; *** Inferior python +(use-package inferior-python-mode + :ensure nil + :hook (inferior-python-mode . hide-mode-line-mode)) + +;; *** pytest +(use-package python-pytest + :ensure t + :general + (:keymaps 'python-base-mode-map + "C-c t" #'python-pytest-dispatch)) + +;; *** flymake-ruff +(defun update-ruff-codes () + (interactive) + (let* ((codes (car (remove flymake-ruff--curr-codes + flymake-ruff--codes))) + (args `("check" "--quiet" + "--preview" ; enables beta checks + ,@(flatten-list (mapcar (lambda (code) (list "--select" code)) + codes)) ;codes to select + "--output-format" "concise" + "--exit-zero" "-"))) + (setq flymake-ruff--curr-codes codes + flymake-ruff-program-args args))) + +(use-package flymake-ruff + :ensure t + :hook (eglot-managed-mode . flymake-ruff-load) + :config + (setq flymake-ruff--codes '(("ALL")("E" "W" "F")) + flymake-ruff--curr-codes '("ALL")) + (update-ruff-codes)) + +;; ** Salt +(use-package salt-mode + :defer t) +;; ** Shells +;; *** Fish +(use-package fish-mode + :defer t) + +;; ** systemd +(use-package systemd + :defer t) + +;; ** sxhkdrc-mode +(use-package sxhkdrc-mode + :defer t) + +;; ** Terraform +(use-package terraform-mode + :mode "\\.tf\\'" + :init + (add-to-list 'eglot-server-programs + '(terraform-mode "terraform-ls" "serve")) + :after eglot) + +;; * LSP +;; ** Eglot +(defun eglot-next-backend () + "Switch between chosen backends for MAJOR-MODE. + +Use variable `eglot-MODE-backend' (MODE just first word to allow +for TS/non-ts) to define alternatives to cycle between. Cycling +values become buffer local since they overwrite +`eglot-server-programs' to leverage eglot lifecycle. +" + (interactive) + (let* ((name (car (split-string (symbol-name major-mode) "-"))) + (var (intern (format "eglot-%s-backend" name)))) + (message "%s" var) + (if (boundp var) + (let* ((val (symbol-value var)) + (contact (eglot--guess-contact)) + (item (nth 3 contact)) + (e (-elem-index item val)) + (l (-last-item val)) + (list-l (if (listp l) + l + (list l))) + (new-eglot (if (-same-items? item list-l) + (nth 0 val) + (nth (+ 1 e) val)))) + (setq-local eglot-server-programs + (list (cons major-mode (if (listp new-eglot) + new-eglot + (list new-eglot))))))) + (eglot-shutdown (eglot-current-server)) + (eglot-ensure))) + +(use-package eglot + :ensure t + :hook + (((dockerfile-ts-mode + terraform-mode + python-base-mode) . eglot-ensure))) + +;; **** eglot-tempel +(use-package eglot-tempel + :defer t + :after (eglot) + :init + (eglot-tempel-mode 1)) + +;; **** eldoc-box +(use-package eldoc-box + :hook ((eglot-managed-mode . eldoc-box-hover-mode))) + +;; **** dape +(use-package dape + :commands (dape-breakpoint-global-mode) + :hook + ((kill-emacs . dape-breakpoint-save) + (after-init . dape-breakpoint-load)) + :init + (dape-breakpoint-global-mode 1)) +;; * Version Control +;; ** Git +;; *** Magit +(use-package magit + :commands magit-status + :general + ("" #'magit-status) + :hook + ((magit-pre-refresh . diff-hl-magit-pre-refresh) + (magit-post-refresh . diff-hl-magit-post-refresh))) + +;; **** Extras +(use-package magit-extras + :ensure nil + :defer t + :after magit) + +;; **** treemacs-magit +(use-package treemacs-magit + :defer t + :after (treemacs magit)) + +;; **** Forge +(use-package forge + :defer t) + +;; ** Fossil +(use-package vc-fossil + :defer t) +;; * Email +(require 'config-mail nil 'noerror) diff --git a/emacs/.config/emacs/pre-early-init.el b/emacs/.config/emacs/pre-early-init.el new file mode 100644 index 0000000..88992e7 --- /dev/null +++ b/emacs/.config/emacs/pre-early-init.el @@ -0,0 +1,23 @@ +;;; pre-early-init.el --- DESCRIPTION -*- no-byte-compile: t; lexical-binding: t; -*- + +;; * Pre-Early-Init +;; ** Measure Startup time +(defun display-startup-time () + "Display the startup time and number of garbage collections." + (message "Emacs init loaded in %.2f seconds (Full emacs-startup: %.2fs) with %d garbage collections." + (float-time (time-subtract after-init-time before-init-time)) + (time-to-seconds (time-since before-init-time)) + gcs-done)) + +(add-hook 'emacs-startup-hook #'display-startup-time 100) + +;; ** Re-enable desired ui features +(setq minimal-emacs-ui-features '(menu-bar)) + +;; ** Move ELN Cache into a subdir +(when (and (fboundp 'startup-redirect-eln-cache) + (fboundp 'native-comp-available-p) + (native-comp-available-p)) + (startup-redirect-eln-cache + (convert-standard-filename + (expand-file-name "var/eln-cache/" user-emacs-directory)))) diff --git a/emacs/.config/emacs/pre-init.el b/emacs/.config/emacs/pre-init.el new file mode 100644 index 0000000..a40de37 --- /dev/null +++ b/emacs/.config/emacs/pre-init.el @@ -0,0 +1,45 @@ +;;; pre-init.el --- DESCRIPTION -*- no-byte-compile: t; lexical-binding: t; -*- +;;; post-early-init.el --- DESCRIPTION -*- no-byte-compile: t; lexical-binding: t; -*- +(setq elpaca-hide-initial-build t) +(defvar elpaca-installer-version 0.11) +(defvar elpaca-directory (expand-file-name "elpaca/" user-emacs-directory)) +(defvar elpaca-builds-directory (expand-file-name "builds/" elpaca-directory)) +(defvar elpaca-repos-directory (expand-file-name "repos/" elpaca-directory)) +(defvar elpaca-order '(elpaca :repo "https://github.com/progfolio/elpaca.git" + :ref nil :depth 1 :inherit ignore + :files (:defaults "elpaca-test.el" (:exclude "extensions")) + :build (:not elpaca--activate-package))) +(let* ((repo (expand-file-name "elpaca/" elpaca-repos-directory)) + (build (expand-file-name "elpaca/" elpaca-builds-directory)) + (order (cdr elpaca-order)) + (default-directory repo)) + (add-to-list 'load-path (if (file-exists-p build) build repo)) + (unless (file-exists-p repo) + (make-directory repo t) + (when (<= emacs-major-version 28) (require 'subr-x)) + (condition-case-unless-debug err + (if-let* ((buffer (pop-to-buffer-same-window "*elpaca-bootstrap*")) + ((zerop (apply #'call-process `("git" nil ,buffer t "clone" + ,@(when-let* ((depth (plist-get order :depth))) + (list (format "--depth=%d" depth) "--no-single-branch")) + ,(plist-get order :repo) ,repo)))) + ((zerop (call-process "git" nil buffer t "checkout" + (or (plist-get order :ref) "--")))) + (emacs (concat invocation-directory invocation-name)) + ((zerop (call-process emacs nil buffer nil "-Q" "-L" "." "--batch" + "--eval" "(byte-recompile-directory \".\" 0 'force)"))) + ((require 'elpaca)) + ((elpaca-generate-autoloads "elpaca" repo))) + (progn (message "%s" (buffer-string)) (kill-buffer buffer)) + (error "%s" (with-current-buffer buffer (buffer-string)))) + ((error) (warn "%s" err) (delete-directory repo 'recursive)))) + (unless (require 'elpaca-autoloads nil t) + (require 'elpaca) + (elpaca-generate-autoloads "elpaca" repo) + (let ((load-source-file-function nil)) (load "./elpaca-autoloads")))) +(add-hook 'after-init-hook #'elpaca-process-queues) +(elpaca `(,@elpaca-order)) + +(elpaca elpaca-use-package + (elpaca-use-package-mode +1)) +(setopt package-install-upgrade-built-in t) diff --git a/emacs/.config/emacs/secure-config/config-mail.el b/emacs/.config/emacs/secure-config/config-mail.el new file mode 100644 index 0000000..43313d8 --- /dev/null +++ b/emacs/.config/emacs/secure-config/config-mail.el @@ -0,0 +1,245 @@ +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAyNENmYm1XalpPNlpNTXgr +UXpDRHhnWE9XRUw2Z2M0ZytlaDdCaFE5WkYwCjkvN09BUk5RNUNoNVdoMUJSSERp +MnlnblhqWXRIcXBWeVhnaCtCS2NwOGcKLS0tIDZ2MEdRcFI4cHBKQisrcWYzcFBn +M3lPcjVmemtjVlZmblN0SldWaHF1M2MKEiz+xE+nEgMnw0qTi2ImejpSG8ekgv/c +W0wKcs6pRF4efX8kJCSjXbxv91DqC8qnqiNRiMfKNoUHXZ1Zj3njUvMdz7e/15xd +1AUDcK4AeVNplisVCNp7ABjD+tiYw8A7J1lw8udSrV/EbomdF9mYJhX1+yOGg5x4 +0IJMd3Qke1iQNQ1TVaNoF7jj48y/xBmOCOEFoA/odevfEYDx/8uQ3q1o8u5nZ44O +JOoHqqgXyO/n4V1qLmd6EIpQvnLG+U73pvNZNMKk0sRkgsqbFclq78Nm5kDrS1f6 +J1XuNjhA9wB5mUCaatClGSToPll0Y770Vgr7kHd9x3u2UTZasaE07gfm89DTo19L +4rtqO8mASQKKWJb5Ut3eGx7KIoBwwOnRYjMBhu4WS5tTdgcFpwa+U7Xzibqu3IR0 +EQp0G34qsny3yoSafhgm0u7gABEcnVRMffsRKYAxTrZtvkCdmvG0NQbS2djVAkPt +uOYN6KQwJcRuoZqRB8j5JXqpPmsk5zHmwxujOHnScVwAILWmc7xmS7pxr5TNeI2L +HbbDLqqhgGy4YQK+F1E3DK5I5OuczRcERttI2kzAwBSnvB7cM1ezzkWKrz1O68f4 +uHt08gx1ls3vR8b31c5A3/ExzcLV/9YSVT+6SncRdD6HnS7I2H1zjq/M2Mel+E+R +U1ZjrwacsAJHnklvHiJ9nyM1Gu3xayMLPifEBCbNmdyYbc1qdNpUu4T5zn3XNhs3 +OIztNEpnH3G5TVrNK01aweMmMZqn7ikyrKhIy4fgyHg/YGYMYf4mAJyVij+JuHGD +0HlWCcn7EGBbbkkvGrkObPC5pvlnycuhHnReD6/ffdxY3R+0j7sPGVmhfuOf3wec +PwZTzX2ppMByez0gEwXoPb+o8FUWOai/u2QNmwytEvDf8pIqsfSqbsZwqm0xNrPb +sK6eK2bxEuMtFwMZBwZV6CuwY1/J1UYwXyvCV7ylmzC+ssF9hgrKMUl8LaCqtNzb +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 +-----END AGE ENCRYPTED FILE-----