;;; syd-completion.el -*- lexical-binding: t; -*- (use-package emacs :custom ;; Allow the opening of new minibuffers from inside existing minibuffers. ((enable-recursive-minibuffers t) ;; Hide commands in M-x which do not work in the current mode. (read-extended-command-predicate #'command-completion-default-include-p)) :config ;; Disable blinking cursor. Aesthetically, I personally don't fancy it; ;; technically, it doesn't play well with `evil-terminal-cursor-changer'. (blink-cursor-mode -1)) ;; Consult adds various search and navigation tools using Emacs' completing-read ;; function; i.e., in our case, Vertico. (use-package consult :defer t :config (advice-add #'consult-recent-file :before #'syd-enable-recentf-mode-a)) (use-package embark-consult :after (embark consult)) ;;;###autoload (defun syd-vertico-embark-preview () "Previews candidate in vertico buffer, unless it's a consult command" (interactive) (unless (bound-and-true-p consult--preview-function) (if (fboundp 'embark-dwim) (save-selected-window (let (embark-quit-after-action) (embark-dwim))) (user-error "Embark not installed, aborting...")))) ;;;###autoload (defun syd-enable-recentf-mode-a () "Used as :before advice for `consult-recent-file' to ensure recentf mode is enabled." ;; REVIEW: Shall we take any extra precautions to only enable `recentf-mode' ;; if it was going to be enabled anyway? (recentf-mode 1)) ;; Vertico is a simple completion engine that replaces Emacs' built-in ;; completion engine, achieving Just Works™ compatibility. This is in contrast ;; to e.g. Helm and Ivy, which spawn ecosystems orthogonal to Emacs, and ;; diametrically-opposed to each other. (use-package vertico :hook (on-first-input . vertico-mode) :general (:keymaps 'vertico-map "DEL" #'vertico-directory-delete-char "C-SPC" #'syd-vertico-embark-preview "C-j" #'vertico-next "C-k" #'vertico-previous "C-M-j" #'vertico-next-group "C-M-k" #'vertico-previous-group) (:keymaps 'vertico-map :states 'normal "j" #'vertico-next "k" #'vertico-previous "RET" #'vertico-exit) :custom ((vertico-resize nil) (vertico-count 17) (vertico-cycle t)) :config (setq-default completion-in-region-function #'consult-completion-in-region) ;; Cleans up path when moving directories with shadowed paths syntax, e.g. ;; cleans ~/foo/bar/// to /, and ~/foo/bar/~/ to ~/. (add-hook 'rfn-eshadow-update-overlay-hook #'vertico-directory-tidy)) (defun syd-vertico-orderless-dispatch (pattern _index _total) "Like `orderless-affix-dispatch', but allows affixes to be escaped. Shamelessly stolen from Doom. }:3" (let ((len (length pattern)) (alist orderless-affix-dispatch-alist)) (when (> len 0) (cond ;; Ignore single dispatcher character ((and (= len 1) (alist-get (aref pattern 0) alist)) #'ignore) ;; Prefix ((when-let ((style (alist-get (aref pattern 0) alist)) ((not (char-equal (aref pattern (max (1- len) 1)) ?\\)))) (cons style (substring pattern 1)))) ;; Suffix ((when-let ((style (alist-get (aref pattern (1- len)) alist)) ((not (char-equal (aref pattern (max 0 (- len 2))) ?\\)))) (cons style (substring pattern 0 -1)))))))) ;; Orderless provides a completion style that divides the pattern into ;; space-separated components, and matches candidates that match all of the ;; components in any order. Each component can match in any one of several ;; ways: literally, as a regexp, as an initialism, in the flex style, or as ;; multiple word prefixes. By default, regexp and literal matches are enabled. (use-package orderless :after vertico :custom ((completion-styles '(orderless basic)) (completion-category-overrides '((file (styles ;basic-remote orderless partial-completion)))) (orderless-style-dispatchers '(syd-vertico-orderless-dispatch)) ;; TODO: See Doom's `+vertico-orderless-dispatch'. (orderless-affix-dispatch-alist '((?! . orderless-without-literal) (?& . orderless-annotation) ;; %1 -> {1, ₁, ꘡, ⒈, ...} (?% . char-fold-to-regexp) ;; ,wcb -> {with-current-buffer, widget-convert-button, ...} (?, . orderless-initialism) (?= . orderless-literal) (?^ . orderless-literal-prefix) (?~ . orderless-flex))))) ;;;###autoload (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 (plist-get (car targets) :type) '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 (lambda (binding) (not (string-suffix-p "-argument" (cdr binding)))))))) ;; TODO: Mark the Embark export buffer as a popup. (use-package embark :after vertico :defer t :custom ((which-key-use-C-h-commands nil) (prefix-help-command #'embark-prefix-help-command) ;; Embark uses their own custom interface that is essentially ;; equivalent to which-key; just use which-key. }:) (embark-indicators '(embark-which-key-indicator))) :general (:keymaps 'minibuffer-local-map "C-;" `("Actions" . ,#'embark-act)) (:keymaps 'syd-leader-map "a" `("Actions" . ,#'embark-act))) (provide 'syd-completion)