;; -*- lexical-binding: t; -*- ;; Nicer minibuffer completion ;; First Selectrum, which provides the core incremental minibuffer completion engine (use-package selectrum :config (selectrum-mode 1) (leader-def-key "z" #'selectrum-repeat)) ;; Then prescient, which adds the ability to sort and filter completions (use-package selectrum-prescient :after selectrum :config (selectrum-prescient-mode 1) (prescient-persist-mode 1)) ;; Marginalia adds annotations to completion candidates (use-package marginalia :demand t :config (marginalia-mode 1) ;; When using Selectrum, ensure that Selectrum is refreshed when cycling annotations. (advice-add #'marginalia-cycle :after (lambda () (when (bound-and-true-p selectrum-mode) (selectrum-exhibit)))) (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 (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)) (defvar-keymap embark-password-store-actions :doc "Keymap for actions for password-store." :parent embark-general-map "c" #'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 identifiers in LSP-mode as their own target type (with-eval-after-load 'lsp-mode (defun embark-target-lsp-identifier-at-point () (when lsp-mode (when-let ((sym (embark-target-identifier-at-point))) (cons 'lsp-identifier (cdr sym))))) (add-to-list 'embark-target-finders 'embark-target-lsp-identifier-at-point) (defun embark-lsp-execute-code-action (_target) "Ignores the target and calls lsp-execute-code-action." (call-interactively #'lsp-execute-code-action)) (defvar-keymap embark-lsp-identifier-actions :doc "Keymap for actions on LSP identifiers" :parent embark-general-map "a" #'embark-lsp-execute-code-action "h" #'lsp-describe-thing-at-point) (add-to-list 'embark-keymap-alist '(lsp-identifier . embark-lsp-identifier-actions)) (add-to-list 'embark-target-injection-hooks '(lsp-describe-thing-at-point embark--ignore-target))) (add-to-list 'embark-target-injection-hooks '(xref-find-references embark--ignore-target)) :general ((emacs normal motion insert visual) "C-." #'embark-act) ((emacs normal motion insert visual) "M-." #'embark-dwim) ("C-." #'embark-act) ("M-." #'embark-dwim) (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 :after selectrum :commands (consult-xref) :init (setq xref-show-xrefs-function #'consult-xref xref-show-definitions-function #'consult-xref) :custom (consult-project-root-function #'projectile-project-root) (consult-find-args "find . -not ( -wholename */.git/* -prune )") (consult-config '((consult-ripgrep :preview-key nil) (consult-grep :preview-key nil))) :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-flycheck :after flycheck :general (flycheck-command-map "!" #'consult-flycheck)) (use-package embark-consult :defer 1) ;; 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)