dotfiles/emacs/.config/emacs/post-init.el

1765 lines
52 KiB
EmacsLisp

;;; 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-<return>"))
(cua-selection-mode 1))
;; ** Stop minimizing Emacs on C-z
;; TWM can manage my frame
(global-unset-key (kbd "C-z"))
;; ** Bookmarks
(use-package bookmark
:ensure nil
:init
(setopt bookmark-save-flag 1))
;; ** 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-<tab>" #'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-<tab>" #'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
org-hide-leading-stars t
;; 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 'green) :weight bold :inverse-video t))
("WRITE" . (:foreground ,(doom-color 'dark-cyan) :weight bold :inverse-video t))
("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
("<f5>" #'org-daily-agenda)
("S-<f5>" #'org-agenda)
("<f6>" #'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-<f5>" #'my/org-roam-agenda-all-files
"C-<f6>" #'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
("<f12>" #'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)
;; * Custom file
;; Finally load custom file
(load custom-file 'no-error)