From 91c5875fdc7cdd10bd5d8e073d08fca5cc2f5ab5 Mon Sep 17 00:00:00 2001 From: Tim McCarthy Date: Tue, 24 Sep 2024 23:41:07 -0700 Subject: [PATCH] Move config into single init.el with outli-mode headings --- thoom-emacs/init.el | 866 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 843 insertions(+), 23 deletions(-) diff --git a/thoom-emacs/init.el b/thoom-emacs/init.el index 08bad24..296dfb8 100644 --- a/thoom-emacs/init.el +++ b/thoom-emacs/init.el @@ -1,29 +1,849 @@ -(defvar thoom/dir (file-name-directory user-init-file) - "The root dir of the Thoom Emacs distribution.") -(defvar thoom/modules-dir (expand-file-name "modules" thoom/dir) - "This directory houses all of the built-in Prelude modules.") -(add-to-list 'load-path thoom/modules-dir) - +;;; Preamble ;; Suppress native compilation warnings (setq native-comp-async-report-warnings-errors nil) ;; Set a location for the custom file so it doesn't pollute this file. (setq custom-file (locate-user-emacs-file "custom-vars.el")) -;; Elpaca package manager -(require 'thoom-elpaca) -;; OS-specific tweaks -(require 'thoom-os) -;; Editing and keybind tweaks -(require 'thoom-editing) -;; Vertico, Embark, Corfu, etc -(require 'thoom-completion) -;; Emacs appearance -(require 'thoom-theme) -(require 'thoom-tweaks) -(require 'thoom-org) -(require 'thoom-lisp) -(require 'thoom-nix) -(require 'thoom-git) -(require 'thoom-prog) -(require 'thoom-eshell) +;;; Elpaca package manager + +(defvar elpaca-installer-version 0.7) +(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 + :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) + (load "./elpaca-autoloads"))) +(add-hook 'after-init-hook #'elpaca-process-queues) +(elpaca `(,@elpaca-order)) + +(elpaca elpaca-use-package + (elpaca-use-package-mode)) +(elpaca-wait) + +;; Magit depends on newer versions of seq/transient than Emacs bundles, but Elpaca +;; can't or won't update them on its own. +(defun +elpaca-unload-seq (e) + (and (featurep 'seq) (unload-feature 'seq t)) + (elpaca--continue-build e)) + +(defun +elpaca-seq-build-steps () + (append (butlast (if (file-exists-p (expand-file-name "seq" elpaca-builds-directory)) + elpaca--pre-built-steps elpaca-build-steps)) + (list '+elpaca-unload-seq 'elpaca--activate-package))) + +(use-package seq :ensure `(seq :build ,(+elpaca-seq-build-steps))) +(use-package transient :ensure t) +(elpaca-wait) + +;;; OS-specific tweaks +(defconst ON-LINUX (eq system-type 'gnu/linux)) +(defconst ON-MAC (eq system-type 'darwin)) +(defconst ON-BSD (or ON-MAC (eq system-type 'berkeley-unix))) +(defconst ON-WINDOWS (memq system-type '(cygwin windows-nt ms-dos))) + +;; Emacs on macOS doesn't naturally find shell variables like PATH, +;; so to help it out we run a shell and load some variables from the env command. +(use-package exec-path-from-shell + :ensure t + :if (and ON-MAC (memq window-system '(mac ns))) + :config + (dolist (var '("NIX_SSL_CERT_FILE" + "NIX_PATH" + "NIX_PROFILES")) + (add-to-list 'exec-path-from-shell-variables var)) + (exec-path-from-shell-initialize)) + +(elpaca-wait) + +;;; Keybindings +(use-package emacs + :bind (("C-c o b" . reveal-in-file-browser) + ("C-h C-i" . info-apropos) + ("C-x )" . kmacro-end-or-call-macro) + ;; Eval related mappings + ("C-x e" . nil) + ("C-x e b" . eval-buffer) + ("C-x e d" . eval-defun) + ("C-x e r" . eval-region) + ("C-x e e" . eval-expression) + ;; Window-management + ("C-o" . nil) + ("C-o C-o" . other-window) + ("C-o o" . other-window) + ("C-o b" . consult-buffer-other-window-no-focus) + ("C-o B" . consult-buffer-other-window) + ("C-o /" . split-window-right) + ("C-o -" . split-window-below) + ("C-o C-k" . delete-window) + ("C-o 1" . delete-other-windows) + ("C-o w" . windmove-up) + ("C-o s" . windmove-down) + ("C-o a" . windmove-left) + ("C-o d" . windmove-right) + ("C-o W" . windmove-swap-states-up) + ("C-o S" . windmove-swap-states-down) + ("C-o A" . windmove-swap-states-left) + ("C-o D" . windmove-swap-states-right) + ;; TODO C-wasd for resizing + ;; TODO C-/ for undo window state change + ("C-o z" . thoom/zoom-window) + ;; TODO other-window scrolling + ("C-o n" . next-buffer) + ("C-o p" . previous-buffer) + :repeat-map windmove-repeat-map + ("w" . windmove-up) + ("a" . windmove-left) + ("s" . windmove-down) + ("d" . windmove-right) + ("W" . windmove-swap-states-up) + ("S" . windmove-swap-states-down) + ("A" . windmove-swap-states-left) + ("D" . windmove-swap-states-right) + :repeat-map next-buffer-repeat-map + ("n" . next-buffer) + ("p" . previous-buffer) + :repeat-map kmacro-repeat-map + (")" . kmacro-end-or-call-macro))) +;;;; which-key +(use-package which-key + :ensure t + :init + (which-key-mode) + (which-key-setup-side-window-bottom)) +;;;; Mac modifier keys +;; Use right-option as regular option on Mac +(setq ns-alternate-modifier 'meta) +(setq ns-right-alternate-modifier 'none) +(setq ns-right-command-modifier 'meta) +;;; Editing +;;;; MWIM +(use-package mwim + :ensure t + :bind (([remap move-beginning-of-line] . mwim-beginning) + ([remap move-end-of-line] . mwim-end) + :map visual-line-mode-map + ("C-a" . mwim-beginning) + ("C-e" . mwim-end)) + :init + (defun mwim-visual-line-end () + (mwim-point-at + (end-of-visual-line))) + (defun mwim-visual-line-beginning () + (mwim-point-at + (beginning-of-visual-line))) + :custom + (mwim-beginning-position-functions + '(mwim-visual-line-beginning + mwim-line-beginning + mwim-code-beginning + mwim-comment-beginning)) + (mwim-end-position-functions + '(mwim-visual-line-end + mwim-block-end + mwim-code-end + mwim-line-end))) + +;;;; Isearch +;; Allow movement during isearch +;; C/M-v move to next/previous match not currently visible +;; M- move to first/last match in buffer +(setq isearch-allow-motion t) +(setq isearch-wrap-pause nil) + +;;;; Avy +(use-package avy + :ensure t + :bind + (("C-j" . avy-goto-char-timer) + ([remap goto-line] . avy-goto-line) + :map isearch-mode-map + ("C-j" . avy-isearch) + :map lisp-interaction-mode-map + ("C-j" . nil)) + + :demand t + :config + (defun avy-action-embark (pt) + (unwind-protect + (save-excursion + (goto-char pt) + (embark-act)) + (select-window + (cdr (ring-ref avy-ring 0)))) + t) + + (setf (alist-get ?\; avy-dispatch-alist) 'avy-action-embark) + + (setq avy-timeout-seconds 0.25) + ;; Always show candidates even when there's just one, to give an + ;; opportunity to select an avy action + (setq avy-single-candidate-jump nil)) + +(use-package avy-zap + :ensure t + :bind (("C-z" . avy-zap-up-to-char-dwim) + ("M-z" . avy-zap-to-char-dwim))) + +;;;; Expand-region +(use-package expand-region + :ensure t + :bind (("C-=" . er/expand-region))) + +;;;; Dot-mode +(use-package dot-mode + :ensure t + :config + (global-dot-mode t)) + +;;;; Vundo +(use-package vundo + :ensure t + :bind ("M-/" . vundo)) + +;;;; Outli +(use-package outli + :ensure (:host github :repo "jdtsmith/outli") + :bind (:map outli-mode-map + ("C-c C-p" . (lambda () (interactive) (outline-back-to-heading)))) + :hook ((prog-mode text-mode) . outli-mode)) + +;;; Completion +;;;; Vertico +(use-package vertico + :ensure t + :init + (vertico-mode)) + +(use-package vertico-directory + :after vertico + :bind (:map vertico-map + ("C-h" . vertico-directory-delete-word) + ("C-l" . vertico-directory-enter))) + +;;;; Marginalia +(use-package marginalia + :ensure t + ;; Either bind `marginalia-cycle' globally or only in the minibuffer + :bind (:map minibuffer-local-map + ("M-A" . marginalia-cycle)) + + :init + (marginalia-mode)) + +;;;; Orderless +(use-package orderless + :ensure t + :init + (setq completion-ignore-case t) + (setq completion-styles '(orderless basic) + completion-category-defaults nil + completion-category-overrides '((file (styles partial-completion))))) + +;;;; Consult +(use-package consult + :ensure t + ;; Replace bindings. Lazily loaded due by `use-package'. + :bind (("C-x b" . consult-buffer) + ("C-x C-b" . consult-buffer) + ("C-x p g" . consult-ripgrep) + ("M-i" . consult-imenu)) + + ;; Enable automatic preview at point in the *Completions* buffer. This is + ;; relevant when you use the default completion UI. + ;; :hook (completion-list-mode . consult-preview-at-point-mode) + + :init + ;; Optionally configure the register formatting. This improves the register + ;; preview for `consult-register', `consult-register-load', + ;; `consult-register-store' and the Emacs built-ins. + (setq register-preview-delay 0.5 + register-preview-function #'consult-register-format) + + ;; Optionally tweak the register preview window. + ;; This adds thin lines, sorting and hides the mode line of the window. + (advice-add #'register-preview :override #'consult-register-window) + + ;; Use Consult to select xref locations with preview + (setq xref-show-xrefs-function #'consult-xref + xref-show-definitions-function #'consult-xref) + + (defun consult-buffer-other-window-no-focus () + (interactive) + (with-selected-window (next-window) + (consult-buffer))) + :config + ;; For some commands and buffer sources it is useful to configure the + ;; :preview-key on a per-command basis using the `consult-customize' macro. + (consult-customize + consult-theme + :preview-key '(:debounce 0.5 any) + consult-ripgrep consult-git-grep consult-grep + consult-bookmark consult-recent-file consult-xref + consult--source-bookmark consult--source-recent-file + consult--source-project-recent-file)) + +(use-package consult-dir + :ensure t + :bind (("C-x C-d" . consult-dir) + :map vertico-map + ("C-x C-d" . consult-dir) + ("C-x C-j" . consult-dir-jump-file))) + +;;;; Embark +(use-package embark + :ensure t + :bind + (("C-;" . embark-act) ;; pick some comfortable binding + ("C-:" . embark-dwim) ;; good alternative: M-. + ("C-h B" . embark-bindings)) ;; alternative for `describe-bindings' + + :init + ;; Optionally replace the key help with a completing-read interface + (setq prefix-help-command #'embark-prefix-help-command) + + :config + ;; Hide the mode line of the Embark live/completions buffers + (add-to-list 'display-buffer-alist + '("\\`\\*Embark Collect \\(Live\\|Completions\\)\\*" + nil + (window-parameters (mode-line-format . none))))) + +;; Consult users will also want the embark-consult package. +(use-package embark-consult + :ensure t + :after (embark consult) + :demand t ; only necessary if you have the hook below + ;; if you want to have consult previews as you move around an + ;; auto-updating embark collect buffer + :hook + (embark-collect-mode . consult-preview-at-point-mode)) + +;;;; completion-at-point +(use-package corfu + :ensure t + :bind + (("C-'" . completion-at-point)) + :init + (global-corfu-mode)) + +(use-package cape + :ensure t + ;; Bind prefix keymap providing all Cape commands under a mnemonic key. + :bind ("C-c p" . cape-prefix-map) ;; Alternative keys: M-p, M-+, ... + :custom + (cape-dabbrev-check-other-buffers nil) + (cape-file-directory-must-exist nil) + :init + (add-hook 'completion-at-point-functions #'cape-dabbrev) + (add-hook 'completion-at-point-functions #'cape-file) + (add-hook 'completion-at-point-functions #'cape-elisp-block)) + +;;; Appearance +;;;; Theme +(use-package doom-themes + :ensure t + :config + (setq doom-themes-enable-bold t + doom-themes-enable-italic t) + + (load-theme 'doom-monokai-pro t) + ;; Lighten comments slightly for better contrast + (custom-theme-set-faces + 'doom-monokai-pro + '(font-lock-comment-face + ((t (:foreground "#9A989A"))))) + (enable-theme 'doom-monokai-pro) + + ;; Enable flashing mode-line on errors + (doom-themes-visual-bell-config) + ;; Corrects (and improves) org-mode's native fontification. + (doom-themes-org-config)) + +;;;; Window size +(modify-all-frames-parameters '((alpha 99 99) + (top . 50) + (left . 100) + (width . 120) + (height . 60))) + +;;;; Fonts +(setq thoom-font-candidates + '("FiraCode Nerd Font Mono" "Fira Code" "Menlo" "Deja Vu Sans")) +(defvar thoom-font + (seq-find #'x-list-fonts thoom-font-candidates) + "The default font to use.") +(defvar thoom-font-size + (if ON-MAC + 14 + 12) + "The default font size to use.") +(modify-all-frames-parameters + `((font . ,(concat thoom-font "-" (number-to-string thoom-font-size))))) + +;;;; Hide clutter +(setq inhibit-startup-message t + use-dialog-box nil) +(tool-bar-mode -1) +(scroll-bar-mode -1) + +;; On a Mac, the menu bar doesn't take up screen real-estate, so leave it on +(unless ON-MAC + (menu-bar-mode -1)) + +;;; Window management +;;;; Popper +(use-package popper + :ensure t + :bind (("M-o" . popper-toggle) + ("C-o t" . popper-toggle-type) + ("C-o ." . popper-cycle)) + :init + (setq popper-reference-buffers + '("\\*Messages\\*" + "Output\\*$" + "\\*Async Shell Command\\*" + "\\*Backtrace\\*" + "\\*Compile-Log\\*" + help-mode + compilation-mode)) + (setq popper-group-function #'popper-group-by-project) + (popper-mode +1) + (popper-echo-mode +1)) + +;;;; Zooming +(defvar thoom/zoom-saved-windows nil + "Variable to store the current window configuration for thoom/zoom.") + +(defun thoom/zoom-window () + "Delete other windows, or restore the saved window configuration if available." + (interactive) + (if (and thoom/zoom-saved-windows + (not (window-configuration-p thoom/zoom-saved-windows))) + ;; Clean up if the saved config isn't valid anymore. + (setq thoom/zoom-saved-windows nil)) + + (if thoom/zoom-saved-windows + (progn + (set-window-configuration thoom/zoom-saved-windows) + (setq thoom/zoom-saved-windows nil)) + (setq thoom/zoom-saved-windows (current-window-configuration)) + (delete-other-windows))) + +;;; Dired +(use-package dired + :bind (:map dired-mode-map + ("C-o" . nil) + ("h" . dired-up-directory))) + +(use-package dirvish + :ensure t + :init + (dirvish-override-dired-mode)) + +;;; Misc tweaks +;; When scrolling by page and hitting top/bottom, move cursor to top/bottom of buffer +(setq-default scroll-error-top-bottom t) + +;; Revert buffers when the underlying file has changed +(global-auto-revert-mode 1) + +;; Remember recent files +(recentf-mode 1) +(setq recentf-max-menu-items 20) +(setq recentf-max-saved-items 1000) + +;; Save what you enter into minibuffer prompts +(setq history-length 25) +(savehist-mode 1) + +;; Visually mark the line the cursor is on +(global-hl-line-mode 1) + +;; Enable repeat mode. Keymaps are defined through use-package's :repeat-map directive. +(repeat-mode 1) + +;; Answer questions with y/n instead of yes/no +(defalias 'yes-or-no-p 'y-or-n-p) + +;; when opening a help window, switch focus to it unless a help window was already open +(setq-default help-window-select t) + +;; tabs are for monsters +(setq-default indent-tabs-mode nil) +(setq-default tab-width 4) +(setq-default sentence-end-double-space nil) +;; automatically cleanup whitespace on save +(defun my-whitespace-cleanup () + "Run `whitespace-cleanup' except for TSV files." + (unless (or (derived-mode-p 'text-mode) ; Adjust this check if necessary + (string-match "\\.tsv\\'" buffer-file-name)) + (whitespace-cleanup))) + +(add-hook 'before-save-hook 'my-whitespace-cleanup) + +;; clean up backup file spam +(setq backup-directory-alist `(("." . "~/.saves") + ("/etc/\\.*" . nil)) + delete-old-versions t + kept-new-versions 4 + kept-old-versions 2 + version-control t) + +;;;; Project +(use-package project + :config + ;; Make project.el recognize directories with a .project file as project roots + (add-to-list 'project-vc-extra-root-markers ".project")) + + +;;;; hl-todo +(use-package hl-todo + :ensure t + :init + (global-hl-todo-mode)) + +;;;; View-mode +;; Read-only buffers activate view mode, toggle read only with C-x C-q +(use-package view + :init + (setq view-read-only t) + :bind (:map view-mode-map + ("v" . View-scroll-half-page-forward) + ("V" . View-scroll-half-page-backward))) + +;;;; Direnv +(use-package direnv + :ensure t + :config + (setq direnv-always-show-summary nil) + (direnv-mode)) + +;;;; Explain-pause +(use-package explain-pause-mode + :ensure (:host github :repo "lastquestion/explain-pause-mode")) + +;;;; Reveal-in-finder +(defun first-executable (candidates) + (seq-find #'executable-find candidates)) + +;; Reveal in finder/nautilus/whatever +(defun reveal-in-file-browser () + (interactive) + (call-process + (first-executable '("xdg-open" "open")) + nil nil nil ".")) + +;;; Org-mode +(use-package org + :bind (("C-c o ," . thoom/org-clear-all) + ("C-c o s" . org-screenshot) + ("C-c SPC" . org-table-blank-field) + ;; Unbind to make room for avy and mwim + :map org-mode-map + ("C-j" . nil) + ("C-a" . mwim-beginning) + ("C-e" . mwim-end)) + :hook ((org-mode . visual-line-mode)) + :custom + (org-special-ctrl-a/e 'reversed) + (org-hide-emphasis-markers t) + (org-use-speed-commands t) + (org-use-fast-todo-selection 'expert) + (org-todo-keywords '((sequence "TODO(t)" "BLOCKED(b)" "|" "DONE(d)"))) + (org-image-actual-width '(800))) + +(use-package org-bullets + :ensure t + :config + (add-hook 'org-mode-hook (lambda () (org-bullets-mode 1)))) + +;; Eagerly load org-mode +(with-temp-buffer (org-mode)) + +(defun thoom/org-clear-all () + (interactive) + (goto-char 0) + (org-map-entries + (lambda () + (org-todo ""))) + ;;(flush-lines "CLOSED") + (message "Entries cleared.")) + +;;;; Screenshots for games +;; Steps to set up: +;; 1. Install ShareX, add ShareX folder to PATH +;; 2. Create hotkey named GameNotes with settings: +;; Capture preconfigured window +;; Capture -> Pre configured window title: +;; Override after capture tasks to "Save image to file" +;; Override screenshots folder to org-screenshot-import-path +;; Override Upload settings -> File naming -> Name pattern +;; "GameNotes_%t_%y-%mo-%d-%h-%mi-%s.%ms" +;; 3. Put the following at the top of the org file: +;; #+STARTUP: inlineimages +;; #+ATTR_HTML: :width 500px +(defvar org-screenshot-import-path "/mnt/c/Users/thoom/Seafile/Games/Screenshots/") +(defvar org-screenshot-export-path org-screenshot-import-path) +(defvar org-screenshot-exec-path "ShareX.exe") +(defvar org-screenshot-exec-args "-s -workflow GameNotes") + +(defun org-screenshot () + (interactive) + (let* ((heading-name (org-get-heading t t t t)) + ;; Strip out the font information from the heading name + (_ (set-text-properties 0 (length heading-name) nil heading-name)) + (_ (shell-command (concat org-screenshot-exec-path " " org-screenshot-exec-args))) + ;; shell-command returns immediately, so wait until the screenshot is likely to have been taken + (_ (sit-for 0.35)) + (in-file (car (nreverse (directory-files org-screenshot-import-path 'full "GameNotes")))) + (out-dir-name (file-name-sans-extension (file-name-nondirectory buffer-file-name))) + (out-dir (file-name-as-directory (concat (file-name-as-directory org-screenshot-export-path) out-dir-name))) + (out-file (concat out-dir heading-name "-" (org-id-uuid) ".png"))) + (if (stringp in-file) + (save-excursion + (unless (file-exists-p out-dir) + (make-directory out-dir)) + (rename-file in-file out-file) + (message (format "Saved %S to %S" in-file out-file)) + (org-next-visible-heading 1) + (org-open-line 1) + (org-insert-link nil out-file)) + (progn + (message "Failed to find saved screenshot."))))) + + +;;; (Ma)git +(use-package magit + :after seq + :ensure t + :custom (magit-save-repository-buffers 'dontask) + :bind (("C-x g" . magit-status) + :map magit-mode-map + (":" . execute-extended-command) + ("x" . magit-discard) + :map magit-diff-section-map + ("C-j" . nil) + :map magit-file-section-map + ;; Unbind to stop overriding avy + ("C-j" . nil))) + +;; Disable vc-mode +(setq vc-handled-backends nil) + +;;; Programming languages +;;;; Treesitter +(use-package treesit-auto + :ensure t + :custom + (treesit-auto-install 'prompt) + :config + (treesit-auto-add-to-auto-mode-alist 'all) + (global-treesit-auto-mode)) + +;;;; Markdown +(use-package markdown-mode + :ensure t + :mode ("README\\.md\\'" . gfm-mode) + :hook ((markdown-mode . visual-line-mode))) + +;; TODO - replace with https://github.com/sshaw/copy-as-format if necessary +(defun copy-source-for-reddit () + (interactive) + (let ((contents (buffer-substring (point) (mark)))) + (with-temp-buffer + (insert contents) + (mark-whole-buffer) + (indent-rigidly (point) (mark) 4 t) + (mark-whole-buffer) + (kill-ring-save 0 0 t)))) + +(keymap-global-set "C-c o r" #'copy-source-for-reddit) + +;;;; YAML +(use-package yaml-mode + :ensure t) + +;;;; Rust +(use-package rust-mode + :ensure t) + +;;;; Docker +(use-package dockerfile-mode + :ensure t) + +;;;; LSP +(use-package lsp-mode + :ensure t + :commands lsp + :custom + (lsp-completion-provider :none) + :init + (setq lsp-keymap-prefix "C-c l") + ;; from https://github.com/minad/corfu/wiki + (defun my/lsp-mode-setup-completion () + (setf (alist-get 'styles (alist-get 'lsp-capf completion-category-defaults)) + '(orderless))) + :hook ((rust-ts-mode . lsp-deferred) + (lsp-mode . lsp-enable-which-key-integration) + (lsp-completion-mode . my/lsp-mode-setup-completion))) + +(use-package lsp-ui + :ensure t + :commands lsp-ui-mode) + +;;;; Nix +(use-package nix-mode + :ensure t + :mode "\\.nix\\'") +;;;; Lisp +(if (string-match "x86_64-apple" system-configuration) + ;; The latest version of parinfer-rust-mode is incompatible with x86 Macs, so fall back on an earlier commit + (use-package parinfer-rust-mode + :ensure (:host + github + :repo + "justinbarclay/parinfer-rust-mode" + :ref + "8df117a3b54d9e01266a3905b132a1d082944702") + :hook emacs-lisp-mode) + (use-package parinfer-rust-mode + :ensure t + :hook emacs-lisp-mode)) +;;; Eshell +;;;; Eshell +(use-package eshell + :config + (setq eshell-destroy-buffer-when-process-dies t + eshell-visual-commands '() + eshell-banner-message "") + + ;; From https://karthinks.com/software/jumping-directories-in-eshell/ + (defun eshell/j (&optional regexp) + "Navigate to a previously visited directory in eshell, or to +any directory proferred by `consult-dir'." + (let ((eshell-dirs (delete-dups + (mapcar 'abbreviate-file-name + (ring-elements eshell-last-dir-ring))))) + (cond + ((and (not regexp) (featurep 'consult-dir)) + (let* ((consult-dir--source-eshell `(:name "Eshell" + :narrow ?e + :category file + :face consult-file + :items ,eshell-dirs)) + (consult-dir-sources (cons consult-dir--source-eshell + consult-dir-sources))) + (eshell/cd (substring-no-properties + (consult-dir--pick "Switch directory: "))))) + (t (eshell/cd (if regexp (eshell-find-previous-directory regexp) + (completing-read "cd: " eshell-dirs)))))))) + +(use-package eshell + :bind (:map eshell-hist-mode-map + ("M-r" . consult-history)) + :after em-hist) + +(use-package pcre2el + :ensure t + :config + (defmacro prx (&rest rx-sexp) + "Convert rx-compatible regular expressions to PCRE." + `(rxt-elisp-to-pcre (rx ,@rx-sexp)))) + +(use-package eshell-toggle + :ensure t + :custom + (eshell-toggle-size-fraction 2) + (eshell-toggle-window-side 'below) + :bind (("M-`" . eshell-toggle))) + +(use-package eshell-p10k + :ensure (:host github :repo "elken/eshell-p10k") + :config + (eshell-p10k-def-segment time + "" + (format-time-string "%H:%M" (current-time)) + 'eshell-p10k-distro-face) + + (defun num-exitcode-string () + (if (= eshell-last-command-status 0) + (number-to-string eshell-p10k--prompt-num-index) + (format "%d (%d)" eshell-p10k--prompt-num-index eshell-last-command-status))) + + (defun num-exitcode-face () + (if (= eshell-last-command-status 0) + 'eshell-p10k-git-clean-face + 'eshell-p10k-git-dirty-face)) + + (eshell-p10k-def-segment num-exitcode + "" + (num-exitcode-string) + (num-exitcode-face)) + + (defun eshell-p10k-prompt-function () + "Prompt defining function." + (eshell-p10k-def-prompt '(num-exitcode time dir git))) + (setq eshell-prompt-function #'eshell-p10k-prompt-function + eshell-prompt-regexp eshell-p10k-prompt-string)) + +(use-package eshell-syntax-highlighting + :after eshell-mode + :ensure t + :config + (eshell-syntax-highlighting-global-mode +1)) + +;;;; Eat +(use-package eat + :ensure t + :custom + (eat-term-name "xterm-256color") + :init + (eval-after-load 'eshell #'eat-eshell-mode) + (eval-after-load 'eshell #'eat-eshell-visual-command-mode) + :config + (defun advise-eat-keymap (map) + (define-key map [?\C-o] nil) + (define-key map (vector meta-prefix-char ?`) nil) + (define-key map [S-v] #'eat-yank)) + + (define-advice eat-eshell-semi-char-mode + (:after (&rest r)) + (advise-eat-keymap eat-eshell-semi-char-mode-map)) + + (define-advice eat-semi-char-mode + (:after (&rest r)) + (advise-eat-keymap eat-semi-char-mode-map)) + + :hook (eat-mode . (lambda () + (setq display-line-numbers nil) + (hl-line-mode -1))))