2025-07-02 16:57:20 -04:00

733 lines
22 KiB
EmacsLisp

;; performance enhancements
(setq gc-cons-threshold #x40000000)
(setq read-process-output-max (* 1024 1024 4))
;; disable package.el on startup
(setq package-enable-at-startup nil)
;; bootstrap `straight.el`
(defvar bootstrap-version)
(let ((bootstrap-file
(expand-file-name
"straight/repos/straight.el/bootstrap.el"
(or (bound-and-true-p straight-base-dir)
user-emacs-directory)))
(bootstrap-version 7))
(unless (file-exists-p bootstrap-file)
(with-current-buffer
(url-retrieve-synchronously
"https://raw.githubusercontent.com/radian-software/straight.el/develop/install.el"
'silent 'inhibit-cookies)
(goto-char (point-max))
(eval-print-last-sexp)))
(load bootstrap-file nil 'nomessage))
(straight-use-package 'use-package)
;; add MELPA
(require 'package)
(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t)
(straight-use-package 'org)
;; EMACS
(use-package emacs
:ensure nil
:custom
(column-number-mode t)
(auto-save-default nil)
(create-lockfiles nil)
(display-line-numbers-type 'relative)
(global-auto-revert-non-file-buffers t)
(history-length 25)
(inhibit-startup-message t)
(initial-scratch-message "")
(ispell-dictionary "en_US")
(make-backup-files nil)
(pixel-scroll-precision-mode t)
(pixel-scroll-precision-use-momentum nil)
(ring-bell-function 'ignore)
(split-width-threshold 300)
(switch-to-buffer-obey-display-actions t)
(tab-always-indent t)
(tab-width 4)
;; (treesit-font-lock-level 4)
(truncate-lines t)
(use-dialog-box nil)
(use-short-answers t)
;;(warning-minimum-level :emergency)
:hook
(prog-mode . display-line-numbers-mode)
:config
(defun skip-these-buffers (_window buffer _bury-or-kill)
"Function for `switch-to-prev-buffer-skip'."
(string-match "\\*[^*]+\\*" (buffer-name buffer)))
(setq switch-to-prev-buffer-skip 'skip-these-buffers)
(set-face-attribute 'default nil :family "JetBrainsMono Nerd Font" :height 120)
(setq custom-file (locate-user-emacs-file "custom-vars.el"))
(load custom-file 'noerror 'nomessage)
:init
(tool-bar-mode -1)
(menu-bar-mode -1)
(scroll-bar-mode -1)
(global-hl-line-mode 1)
(global-auto-revert-mode 1)
(indent-tabs-mode -1)
(recentf-mode 1)
(savehist-mode 1)
(save-place-mode 1)
(winner-mode 1)
(xterm-mouse-mode 1)
(file-name-shadow-mode 1))
(use-package doom-themes
:ensure t
:straight t
:custom
;; Global settings (defaults)
(doom-themes-enable-bold t) ; if nil, bold is universally disabled
(doom-themes-enable-italic t) ; if nil, italics is universally disabled
:config
(load-theme 'doom-one t)
;; Enable flashing mode-line on errors
(doom-themes-visual-bell-config)
;; Corrects (and improves) org-mode's native fontification.
(doom-themes-org-config))
(use-package eldoc
:ensure nil
:init
(global-eldoc-mode))
(use-package flymake
:ensure nil
:defer t
:hook (prog-mode . flymake-mode))
(use-package org
:ensure nil
:defer t
:custom
(org-return-follows-link t)
(org-hide-emphasis-markers t)
(org-log-into-drawer t)
(org-log-done 'time)
(org-todo-keywords
'((sequence "TODO(t)" "NEXT(n)" "HOLD(h@)" "|" "DONE(d)" "CANCELLED(c@/!)")))
(org-capture-templates
`(("i" "Inbox" entry (file "inbox.org")
,(concat "* TODO %?\n"
"/Entered on/ %U"))
("@" "Inbox [email]" entry (file "inbox.org")
,(concat "* TODO Process \"%a\" %?\n"
"/Entered on/ %U"))))
(org-refile-use-outline-path 'file)
(org-outline-path-complete-in-steps nil)
:config
(setq org-directory "~/org")
;;(setq org-agenda-files (list "everything.org"))
(add-to-list 'org-tags-exclude-from-inheritance "project")
:hook
(org-mode . org-indent-mode)
:init
(require 'org-tempo))
(use-package toc-org
:ensure t
:straight t
:hook (org-mode . toc-org-mode))
(use-package org-roam
:ensure t
:straight t
:custom
(org-roam-directory (file-truename "~/org"))
(org-roam-capture-templates
'(("d" "default" plain "%?"
:target (file+head "%<%Y%m%d%H%M%S>-${slug}.org"
"#+title: ${title}\n")
:unnarrowed t)))
:bind (("C-c n l" . org-roam-buffer-toggle)
("C-c n f" . org-roam-node-find)
("C-c n g" . org-roam-graph)
("C-c n i" . org-roam-node-insert)
("C-c n c" . org-roam-capture)
;; Dailies
("C-c n j" . org-roam-dailies-capture-today))
:config
;; If you're using a vertical completion framework, you might want a more informative completion interface
(setq org-roam-node-display-template (concat "${title:*} " (propertize "${tags:10}" 'face 'org-tag)))
(org-roam-db-autosync-mode)
;; If using org-roam-protocol
(require 'org-roam-protocol)
(defun mthomson/project-files ()
"Return a list of note files containing 'project' tag." ;
(seq-uniq
(seq-map
#'car
(org-roam-db-query
[:select [nodes:file]
:from tags
:left-join nodes
:on (= tags:node-id nodes:id)
:where (like tag (quote "%\"project\"%"))]))))
(defun mthomson/agenda-files-update (&rest _)
"Update the value of `org-agenda-files'."
(setq org-agenda-files (mthomson/project-files))
(setq org-refile-targets
'((nil :maxlevel . 3)
(org-agenda-files :maxlevel . 3))))
(advice-add 'org-agenda :before #'mthomson/agenda-files-update)
(advice-add 'org-todo-list :before #'mthomson/agenda-files-update)
(advice-add 'org-refile :before #'mthomson/agenda-files-update))
(use-package org-superstar
:ensure t
:straight t
:hook
(org-mode . org-superstar-mode))
(use-package which-key
:ensure nil
:defer t
:hook
(after-init . which-key-mode))
(use-package nerd-icons
:ensure t
:straight t
:defer t)
(use-package nerd-icons-dired
:ensure t
:straight t
:defer t
:hook
(dired-mode . nerd-icons-dired-mode))
(use-package doom-modeline
:ensure t
:config
(display-battery-mode t)
(display-time-mode t)
:init (doom-modeline-mode 1))
(use-package evil
:ensure t
:straight t
:defer t
:hook
(after-init . evil-mode)
:init
(setq evil-want-integration t)
(setq evil-want-keybinding nil)
(setq evil-want-C-u-scroll t)
(setq evil-want-C-u-delete t)
:config
(evil-set-undo-system 'undo-tree)
(evil-set-leader 'normal (kbd "SPC"))
(evil-set-leader 'visual (kbd "SPC")))
(use-package evil-collection
:defer t
:straight t
:ensure t
:custom
(evil-collection-want-find-usages-bindings t)
:hook
(evil-mode . evil-collection-init))
(use-package evil-surround
:ensure t
:straight t
:after evil-collection
:config
(global-evil-surround-mode 1))
(use-package evil-matchit
:ensure t
:straight t
:after evil-collection
:config
(global-evil-matchit-mode 1))
(use-package undo-tree
:defer t
:ensure t
:straight t
:hook
(after-init . global-undo-tree-mode)
:init
(setq undo-tree-visualizer-timestamps t
undo-tree-visualizer-diff t
undo-limit 800000
undo-strong-limit 12000000
undo-outer-limit 120000000)
:config
(setq undo-tree-history-directory-alist '(("." . "~/.config/emacs/.cache/undo"))))
(use-package general
:ensure t
:straight t
:config
(defun mthomson/lsp-describe-and-jump ()
"Show hover documentation and jump to *lsp-help* buffer."
(interactive)
(lsp-describe-thing-at-point)
(let ((help-buffer "*lsp-help*"))
(when (get-buffer help-buffer)
(switch-to-buffer-other-window help-buffer))))
(general-evil-setup t)
(general-define-key
:states 'normal
"] d" '(flymake-goto-next-error :wk "Goto next error")
"[ d" '(flymake-goto-prev-error :wk "Goto prev error")
"] c" '(diff-hl-next-hunk :wk "Goto next hunk")
"[ c" '(diff-hl-previous-hunk :wk "Goto prev hunk")
"K" '(mthomson/lsp-describe-and-jump :wk "Describe"))
(general-define-key
:states 'normal
:predicate 'lsp-mode
"g r" '(lsp-find-references :wk "Go to references")
"g d" '(lsp-find-definition :wk "Go to definition")
"g D" '(lsp-find-declaration :wk "Go to declaration")
"g I" '(lsp-find-implementation :wk "Go to implementation")
"g t" '(lsp-find-type-definition :wk "Go to type definition")
"K" '(mthomson/lsp-describe-and-jump :wk "Describe"))
(general-create-definer my-leader-def
:states '(normal insert visual motion emacs)
:keymaps 'override
:prefix "SPC"
:global-prefix "C-SPC")
(my-leader-def
"f" '(:ignore t :wk "Find")
"f c" '((lambda () (interactive) (find-file "~/.config/emacs/init.el")) :wk "Edit emacs config")
"f r" '(consult-recent-file :wk "Recent files")
"f f" '(consult-find :wk "Find Files")
"f g" '(consult-ripgrep :wk "Ripgrep search in files")
"f l" '(consult-line :wk "Find line")
"f i" '(consult-imenu :wk "Imenu buffer locations"))
(my-leader-def
"c" '(:ignore t :wk "Code")
"c a" '(lsp-execute-code-action :wk "Code actions"))
(my-leader-def
"h" '(:ignore t :wk "Help")
"h v" '(describe-variable :wk "Describe variable")
"h f" '(describe-function :wk "Describe function")
"h k" '(describe-key :wk "Describe key"))
(my-leader-def
"p" '(:ignore t :wk "Project")
"p b" '(consult-project-buffer :wk "Project buffers")
"p p" '(project-switch-project :wk "Project switch")
"p f" '(project-find-file :wk "Project find file")
"p g" '(project-find-regexp :wk "Project grep file")
"p k" '(project-kill-buffers :wk "Project kill buffers")
"p d" '(project-dired :wk "Project dired"))
(my-leader-def
"x" '(:ignore t :wk "Flymake")
"x x" '(consult-flymake :wk "Show diagnostics"))
(my-leader-def
"g" '(:ignore t :wk "Git")
"g g" '(magit-status :wk "Magit status")
"g l" '(magit-log-current :wk "Magit log"))
(my-leader-def
"u" '(undo-tree-visualize :wk "Undo-tree")))
(use-package vertico
:ensure t
:straight t
:hook
(after-init . vertico-mode))
(use-package orderless
:ensure t
:straight t
:custom
(completion-styles '(orderless partial-completion basic))
(completion-category-defaults nil)
(completion-category-overrides '((file (styles partial-completion)))))
(use-package marginalia
:ensure t
:straight t
:hook
(after-init . marginalia-mode))
(use-package consult
:ensure t
:straight t
:defer t)
(use-package embark
:ensure t
:straight t
:defer t)
(use-package embark-consult
:ensure t
:straight t
:hook
(embark-collect-mode . consult-preview-at-point-mode))
(use-package treesit-auto
:ensure t
:straight t
:after emacs
:custom
(treesit-auto-install 'prompt)
:config
(treesit-auto-add-to-auto-mode-alist 'all)
(global-treesit-auto-mode)
:init
(add-to-list 'auto-mode-alist '("\\.epub\\'" . nov-mode)))
(use-package corfu
:ensure t
:straight t
:custom
(corfu-auto t)
(corfu-cycle t)
(corfu-auto-prefix 2)
(corfu-popupinfo-mode t)
(corfu-popupinfo-delay 0.5)
(corfu-on-exact-match nil)
(completion-ignore-case t)
:init
(global-corfu-mode))
(use-package nerd-icons-corfu
:ensure t
:straight t
:after corfu
:init (add-to-list 'corfu-margin-formatters #'nerd-icons-corfu-formatter))
(use-package cape
:ensure t
:straight t
:after corfu
:bind ("C-c p" . cape-prefix-map)
:init
(add-hook 'completion-at-point-functions #'cape-dabbrev)
(add-hook 'completion-at-point-functions #'cape-dict)
(add-hook 'completion-at-point-functions #'cape-file)
(add-hook 'completion-at-point-functions #'cape-elisp-block)
(add-hook 'completion-at-point-functions #'cape-keyword))
;; (use-package company
;; :ensure t
;; :straight t
;; :defer t
;; :custom
;; (company-tooltip-align-annotations t)
;; (company-minimum-prefix-length 1)
;; (company-idle-delay 0.2)
;; :hook
;; (after-init . global-company-mode))
(use-package lsp-mode
:ensure t
:straight t
:defer t
:hook (
(bash-ts-mode . lsp)
(typescript-ts-mode . lsp)
(tsx-ts-mode . lsp)
(js-mode . lsp)
(js-ts-mode . lsp)
(c-ts-mode . lsp)
(c++-ts-mode . lsp)
(lsp-mode . lsp-enable-which-key-integration)
(lsp-completion-mode . my/lsp-mode-setup-completion))
:commands lsp
:init
(defun my/lsp-mode-setup-completion ()
(setf (alist-get 'styles (alist-get 'lsp-capf completion-category-defaults))
'(flex))) ;; Configure flex
:custom
(lsp-keymap-prefix "C-c l") ;; Set the prefix for LSP commands.
(lsp-inlay-hint-enable t) ;; Enable inlay hints.
(lsp-completion-provider :none) ;; Disable the default completion provider.
(lsp-session-file (locate-user-emacs-file ".lsp-session")) ;; Specify session file location.
(lsp-log-io nil) ;; Disable IO logging for speed.
(lsp-idle-delay 0) ;; Set the delay for LSP to 0 (debouncing).
(lsp-keep-workspace-alive nil) ;; Disable keeping the workspace alive.
;; Core settings
(lsp-enable-xref t) ;; Enable cross-references.
(lsp-auto-configure t) ;; Automatically configure LSP.
(lsp-enable-links nil) ;; Disable links.
(lsp-eldoc-enable-hover t) ;; Enable ElDoc hover.
(lsp-enable-file-watchers nil) ;; Disable file watchers.
(lsp-enable-folding nil) ;; Disable folding.
(lsp-enable-imenu t) ;; Enable Imenu support.
(lsp-enable-indentation t) ;; Enable indentation.
(lsp-enable-on-type-formatting nil) ;; Disable on-type formatting.
(lsp-enable-suggest-server-download t) ;; Enable server download suggestion.
(lsp-enable-symbol-highlighting t) ;; Enable symbol highlighting.
(lsp-enable-text-document-color nil) ;; Disable text document color.
;; Modeline settings
(lsp-modeline-code-actions-enable nil) ;; Keep modeline clean.
(lsp-modeline-diagnostics-enable nil) ;; Use `flymake' instead.
(lsp-modeline-workspace-status-enable t) ;; Display "LSP" in the modeline when enabled.
(lsp-signature-doc-lines 1) ;; Limit echo area to one line.
(lsp-eldoc-render-all t) ;; Render all ElDoc messages.
;; Completion settings
(lsp-completion-enable t) ;; Enable completion.
(lsp-completion-enable-additional-text-edit t) ;; Enable additional text edits for completions.
(lsp-enable-snippet t) ;; Disable snippets
(lsp-completion-show-kind t) ;; Show kind in completions.
;; Lens settings
(lsp-lens-enable t) ;; Enable lens support.
;; Headerline settings
(lsp-headerline-breadcrumb-enable-symbol-numbers t) ;; Enable symbol numbers in the headerline.
(lsp-headerline-arrow "") ;; Set arrow for headerline.
(lsp-headerline-breadcrumb-enable-diagnostics nil) ;; Disable diagnostics in headerline.
(lsp-headerline-breadcrumb-icons-enable nil) ;; Disable icons in breadcrumb.
;; Semantic settings
(lsp-semantic-tokens-enable nil)) ;; Disable semantic tokens.
;; lsp-booster
(defun lsp-booster--advice-json-parse (old-fn &rest args)
"Try to parse bytecode instead of json."
(or
(when (equal (following-char) ?#)
(let ((bytecode (read (current-buffer))))
(when (byte-code-function-p bytecode)
(funcall bytecode))))
(apply old-fn args)))
(advice-add (if (progn (require 'json)
(fboundp 'json-parse-buffer))
'json-parse-buffer
'json-read)
:around
#'lsp-booster--advice-json-parse)
(defun lsp-booster--advice-final-command (old-fn cmd &optional test?)
"Prepend emacs-lsp-booster command to lsp CMD."
(let ((orig-result (funcall old-fn cmd test?)))
(if (and (not test?) ;; for check lsp-server-present?
(not (file-remote-p default-directory)) ;; see lsp-resolve-final-command, it would add extra shell wrapper
lsp-use-plists
(not (functionp 'json-rpc-connection)) ;; native json-rpc
(executable-find "emacs-lsp-booster"))
(progn
(when-let ((command-from-exec-path (executable-find (car orig-result)))) ;; resolve command from exec-path (in case not found in $PATH)
(setcar orig-result command-from-exec-path))
(message "Using emacs-lsp-booster for %s!" orig-result)
(cons "emacs-lsp-booster" orig-result))
orig-result)))
(advice-add 'lsp-resolve-final-command :around #'lsp-booster--advice-final-command)
;; (use-package yasnippet
;; :ensure t
;; :straight t)
;; (use-package yasnippet-snippets
;; :ensure t
;; :straight t
;; :hook (prog-mode . yas-minor-mode))
;; Configure Tempel
(use-package tempel
:ensure t
:straight t
:bind (("M-+" . tempel-complete)
("M-*" . tempel-insert))
:init
(defun tempel-setup-capf ()
(setq-local completion-at-point-functions
(cons #'tempel-expand
completion-at-point-functions)))
(add-hook 'conf-mode-hook 'tempel-setup-capf)
(add-hook 'prog-mode-hook 'tempel-setup-capf)
(add-hook 'text-mode-hook 'tempel-setup-capf)
(add-hook 'prog-mode-hook #'tempel-abbrev-mode)
(global-tempel-abbrev-mode))
(use-package tempel-collection
:ensure t
:straight t)
(use-package exec-path-from-shell
:ensure t
:straight t
:init
(exec-path-from-shell-initialize))
;; (use-package direnv
;; :ensure t
;; :straight t
;; :config
;; (direnv-mode))
(use-package envrc
:ensure t
:straight t
:init
(setq envrc-show-summary-in-minibuffer nil)
:hook
(after-init . envrc-global-mode))
;; (use-package add-node-modules-path
;; :ensure t
;; :straight t
;; :defer t
;; :custom
;; (eval-after-load 'typescript-ts-mode
;; '(add-hook 'typescript-ts-mode-hook #'add-node-modules-path))
;; (eval-after-load 'tsx-ts-mode
;; '(add-hook 'tsx-ts-mode-hook #'add-node-modules-path))
;; (eval-after-load 'typescriptreact-mode
;; '(add-hook 'typescriptreact-mode-hook #'add-node-modules-path))
;; (eval-after-load 'js-mode
;; '(add-hook 'js-mode-hook #'add-node-modules-path)))
(use-package pulsar
:defer t
:straight t
:ensure t
:hook
(after-init . pulsar-global-mode)
:config
(setq pulsar-pulse t)
(setq pulsar-delay 0.025)
(setq pulsar-iterations 10)
(setq pulsar-face 'evil-ex-lazy-highlight)
(add-to-list 'pulsar-pulse-functions 'evil-scroll-down)
(add-to-list 'pulsar-pulse-functions 'flymake-goto-next-error)
(add-to-list 'pulsar-pulse-functions 'flymake-goto-prev-error)
(add-to-list 'pulsar-pulse-functions 'evil-yank)
(add-to-list 'pulsar-pulse-functions 'evil-yank-line)
(add-to-list 'pulsar-pulse-functions 'evil-delete)
(add-to-list 'pulsar-pulse-functions 'evil-delete-line)
(add-to-list 'pulsar-pulse-functions 'evil-jump-item)
(add-to-list 'pulsar-pulse-functions 'diff-hl-next-hunk)
(add-to-list 'pulsar-pulse-functions 'diff-hl-previous-hunk))
(use-package magit
:ensure t
:straight t
:defer t)
(use-package diff-hl
:ensure t
:straight t
:config
(global-diff-hl-mode))
;; Mu4e
(use-package mu4e
:config
;; mbsync
;;(setq mu4e-mu-binary "mu")
(setq mu4e-get-mail-command (concat (executable-find "mbsync") " -a"))
(setq mu4e--get-mail-password-regexp "^Master Password: $")
(setq mu4e-update-interval 300)
(setq mu4e-change-filenames-when-moving t)
(setq mu4e-attachment-dir "~/Downloads")
;;smtp
(setq send-mail-function 'sendmail-send-it)
(setq sendmail-program (executable-find "msmtp"))
(setq message-sendmail-envelop-from 'header)
;; contexts
(setq mu4e-contexts
`(,(make-mu4e-context
:name "personal"
:enter-func
(lambda () (mu4e-message "Opening personal"))
:leave-func
(lambda () (mu4e-message "Closing personal"))
:match-func
(lambda (msg)
(when msg
(string-match-p "^/personal" (mu4e-message-field msg :maildir))))
:vars '((user-mail-address . "michael@michaelthomson.dev" )
(user-full-name . "Michael Thomson")
(mu4e-drafts-folder . "/personal/Drafts")
(mu4e-refile-folder . "/personal/Archive")
(mu4e-sent-folder . "/personal/Sent")
(mu4e-trash-folder . "/personal/Trash"))))))
(use-package notmuch
:ensure t
:straight t
:bind (("C-c m" . notmuch-hello))
:custom
(notmuch-saved-searches
'((:name "inbox (personal)" :query "tag:inbox AND tag:personal")
(:name "unread (personal)" :query "tag:unread AND tag:personal")
(:name "sent (personal)" :query "tag:sent AND tag:personal")
(:name "all mail (personal)" :query "tag:personal")
(:name "inbox (work)" :query "tag:inbox AND tag:work")
(:name "unread (work)" :query "tag:unread AND tag:work")
(:name "sent (work)" :query "tag:sent AND tag:work")
(:name "all mail (work)" :query "tag:work")
(:name "drafts" :query "tag:draft")))
:init
(add-hook 'message-send-hook (lambda ()
(let ((from (message-fetch-field "From")))
(if (string-match-p (regexp-quote "michael@michaelthomson.dev") from)
(progn
(setq sendmail-program "msmtp")
(setq message-sendmail-extra-arguments nil))
(progn
(setq sendmail-program "gmi")
(setq message-sendmail-extra-arguments '("send" "--quiet" "-t" "-C" "~/Maildir/work")))))))
:config
(setq notmuch-fcc-dirs nil))
(use-package ol-notmuch
:ensure t
:straight t
:bind
("C-c l" . org-store-link))
;; epubs
(use-package nov
:ensure t
:straight t)
;; docker
(use-package docker
:ensure t
:straight t
:bind ("C-c d" . docker))
(use-package alert
:ensure t
:straight t
:commands (alert)
:init
(setq alert-default-style 'notifier))
(use-package vterm
:ensure t
:straight t)
(use-package nix-mode
:ensure t
:straight t
:mode "\\.nix\\'")
(provide 'init)
;;; init.el ends here