;; -*- lexical-binding: t; -*- ;; Nicer minibuffer completion ;; First Vertico, which provides the core incremental minibuffer completion engine (use-package vertico :straight (:files (:defaults "extensions/vertico-repeat.el" "extensions/vertico-directory.el")) :init (vertico-mode)) (use-package vertico-repeat :straight (:type built-in) :after vertico :config (add-hook 'minibuffer-setup-hook #'vertico-repeat-save) :general (leader-map "r" #'vertico-repeat)) (use-package vertico-directory :straight (:type built-in) :after vertico :general (vertico-map "RET" #'vertico-directory-enter "DEL" #'vertico-directory-delete-char "M-DEL" #'vertico-directory-delete-word) ;; Tidy shadowed file names :hook (rfn-eshadow-update-overlay . vertico-directory-tidy)) ;; Save history in order to persist sort order across emacs sessions (use-package savehist :straight (:type built-in) :config (savehist-mode 1) :custom (savehist-additional-variables '(projectile-project-command-history evil-jumps-history vertico-repeat-history))) ;; Then orderless, which adds the ability to filter completions in an intuitive way (use-package orderless :ensure t :custom (completion-styles '(orderless basic)) (completion-category-overrides '((file (styles basic partial-completion))))) ;; Marginalia adds annotations to completion candidates (use-package marginalia :demand t :config (marginalia-mode 1) (add-to-list 'marginalia-prompt-categories '("Find file:" . project-file)) (add-to-list 'marginalia-prompt-categories '("Find dir:" . project-file)) (add-to-list 'marginalia-prompt-categories '("Switch to project" . file)) (add-to-list 'marginalia-prompt-categories '("recipe\\|package" . straight)) (add-to-list 'marginalia-prompt-categories '("Password entry" . password-store)) :general (minibuffer-local-map "M-A" #'marginalia-cycle) :custom (marginalia-annotators '(marginalia-annotators-heavy marginalia-annotators-light nil))) ;; Embark adds context actions to completion candidates (and other things!) (use-package embark :config ;; Put xref targets at the top of the list in programming modes (defun embark-prod-mode-hook () (setq-local embark-target-finders (remove #'embark-target-identifier-at-point embark-target-finders)) (add-to-list 'embark-target-finders #'embark-target-identifier-at-point)) (add-hook 'prog-mode-hook #'embark-prod-mode-hook) (defun embark-which-key-indicator () "An embark indicator that displays keymaps using which-key. The which-key help message will show the type and value of the current target followed by an ellipsis if there are further targets." (lambda (&optional keymap targets prefix) (if (null keymap) (which-key--hide-popup-ignore-command) (which-key--show-keymap (if (eq (caar targets) 'embark-become) "Become" (format "Act on %s '%s'%s" (plist-get (car targets) :type) (embark--truncate-target (plist-get (car targets) :target)) (if (cdr targets) "…" ""))) (if prefix (pcase (lookup-key keymap prefix 'accept-default) ((and (pred keymapp) km) km) (_ (key-binding prefix 'accept-default))) keymap) nil nil t)))) (defvar-keymap embark-straight-map :doc "Keymap for actions for straight.el" :parent embark-general-map "u" #'straight-visit-package-website "r" #'straight-get-recipe "i" #'straight-use-package "c" #'straight-check-package "F" #'straight-pull-package "f" #'straight-fetch-package "p" #'straight-push-package "n" #'straight-normalize-package "m" #'straight-merge-package) (add-to-list 'embark-keymap-alist '(straight . embark-straight-map)) (with-eval-after-load 'pass (defun embark-target-finder-pass () "Identify password-store entries at point." (when-let ((entry (and (eq major-mode 'pass-mode) (pass-closest-entry)))) `(password-store . ,entry))) (add-to-list 'embark-target-finders #'embark-target-finder-pass) (defvar-keymap embark-password-store-actions :doc "Keymap for actions for password-store." :parent embark-general-map "c" #'password-store-copy "RET" #'password-store-copy "f" #'password-store-copy-field "i" #'password-store-insert "I" #'password-store-generate "r" #'password-store-rename "e" #'password-store-edit "k" #'password-store-remove "U" #'password-store-url) (add-to-list 'embark-keymap-alist '(password-store . embark-password-store-actions))) (add-to-list 'embark-target-injection-hooks '(xref-find-references embark--ignore-target)) (defun embark-target-finder-forge () "Identify Forge commits/issues/PRs at point." (when-let ((target (and (fboundp 'forge--browse-target) (forge--browse-target)))) `(forge ,(or (and (stringp target) target) (ignore-errors (forge-get-url target)) (s-chomp (thing-at-point 'line t))) ,(line-beginning-position) . ,(line-end-position)))) (defun embark-forge-magit-setup () (make-local-variable 'embark-target-finders) (add-to-list 'embark-target-finders #'embark-target-finder-forge)) (add-hook 'magit-mode-hook #'embark-forge-magit-setup) (defvar-keymap embark-forge-actions :doc "Keymap for actions for forge." :parent embark-general-map "RET" #'forge-browse "y" #'forge-copy-url-at-point-as-kill) (add-to-list 'embark-keymap-alist '(forge . embark-forge-actions)) :general ((emacs normal motion insert visual) "C-." #'embark-act) ((emacs normal motion insert visual) "M-." #'embark-dwim) ("C-." #'embark-act) ("M-." #'embark-dwim) (embark-general-map "C-k" #'browse-url-or-search "/" #'consult-line) (embark-file-map "s" #'sudo-edit-find-file "l" #'vlf "g" #'magit-file-dispatch) ('normal embark-collect-mode-map "TAB" #'forward-button "?" #'describe-mode "A" #'embark-collect-direct-action-minor-mode "S" #'tabulated-list-sort "a" #'embark-act "b" #'backward-button "e" #'embark-export "f" #'forward-button "gr" #'revert-buffer "n" #'next-line "p" #'previous-line "q" #'quit-window "s" #'isearch-forward "v" #'embark-collect-toggle-view "z" #'embark-collect-zebra-minor-mode "{" #'tabulated-list-narrow-current-column "}" #'tabulated-list-widen-current-column "" #'backward-button) :custom (embark-prompter 'embark-keymap-prompter) (embark-indicators '(embark-which-key-indicator embark-highlight-indicator embark-isearch-highlight-indicator))) ;; Consult adds a bunch of completing-read based utilities (use-package consult :init (setq xref-show-xrefs-function #'consult-xref xref-show-definitions-function #'consult-xref) :custom (consult-project-function #'projectile-project-root) (consult-find-args "find . -not ( -wholename */.git/* -prune )") (consult-narrow-key "<") :general ([remap switch-to-buffer] #'consult-buffer) ([remap imenu] #'consult-imenu) ([remap projectile-ripgrep] #'consult-ripgrep) ([remap projectile-grep] #'consult-grep) ("C-c p" #'consult-yank-from-kill-ring)) (use-package consult-projectile :after (consult projectile) :commands (consult-projectile) :general (projectile-command-map "RET" #'consult-projectile)) (use-package consult-eglot :after (consult eglot) :general (eglot-prefix-map "s" #'consult-eglot-symbols)) (use-package consult-org-roam :after (consult org-roam) :config (consult-org-roam-mode 1) :custom (consult-org-roam-grep-func #'consult-ripgrep) (consult-org-roam-buffer-after-buffers t) :general (org-roam-commands-map "b" #'consult-org-roam-backlinks "B" #'consult-org-roam-backlinks-recursive "l" #'consult-org-roam-forward-links)) (use-package embark-consult :general (leader-map "/" embark-consult-search-map)) ;; Make grep-like embark collect buffers editable (use-package wgrep :general (grep-mode-map "C-x C-q" #'wgrep-change-to-wgrep-mode)) (provide 'init-completion)