BREAKING CHANGE: This commit changes the behaviour of the TAB and RET keys in a consult-completing read multiple session, in order to make them more intuitive. The behaviour is now: - TAB: (unchanged) always select or deselect the current candidate, and if the candidate is selected, move the index to the next one (this allows for pressing TAB repeatedly to select multiple subsequent candidates). - RET: If no candidates have been selected, select the current candidate and exit the completion session. If some have been selected, disregard the current candidate and exit. - S-TAB: (new) like TAB, but the keeps the input.
276 lines
12 KiB
EmacsLisp
276 lines
12 KiB
EmacsLisp
;;; completion/vertico/autoload/vertico.el -*- lexical-binding: t; -*-
|
|
|
|
;; To prevent "Defining as dynamic an already lexical var" from +vertico/embark-preview
|
|
;;;###autoload
|
|
(defvar embark-quit-after-action)
|
|
|
|
;;;###autoload
|
|
(cl-defun +vertico-file-search (&key query in all-files (recursive t) prompt args)
|
|
"Conduct a file search using ripgrep.
|
|
|
|
:query STRING
|
|
Determines the initial input to search for.
|
|
:in PATH
|
|
Sets what directory to base the search out of. Defaults to the current project's root.
|
|
:recursive BOOL
|
|
Whether or not to search files recursively from the base directory."
|
|
(declare (indent defun))
|
|
(unless (executable-find "rg")
|
|
(user-error "Couldn't find ripgrep in your PATH"))
|
|
(require 'consult)
|
|
(setq deactivate-mark t)
|
|
(let* ((project-root (or (doom-project-root) default-directory))
|
|
(directory (or in project-root))
|
|
(consult-ripgrep-args
|
|
(concat "rg "
|
|
(if all-files "-uu ")
|
|
(unless recursive "--maxdepth 1 ")
|
|
"--null --line-buffered --color=never --max-columns=1000 "
|
|
"--path-separator / --smart-case --no-heading --line-number "
|
|
"--hidden -g !.git "
|
|
(mapconcat #'shell-quote-argument args " ")
|
|
" ."))
|
|
(prompt (if (stringp prompt) (string-trim prompt) "Search"))
|
|
(query (or query
|
|
(when (doom-region-active-p)
|
|
(regexp-quote (doom-thing-at-point-or-region)))))
|
|
(consult-async-split-style consult-async-split-style)
|
|
(consult-async-split-styles-alist consult-async-split-styles-alist))
|
|
;; Change the split style if the initial query contains the separator.
|
|
(when query
|
|
(cl-destructuring-bind (&key type separator initial)
|
|
(consult--async-split-style)
|
|
(pcase type
|
|
(`separator
|
|
(replace-regexp-in-string (regexp-quote (char-to-string separator))
|
|
(concat "\\" (char-to-string separator))
|
|
query t t))
|
|
(`perl
|
|
(when (string-match-p initial query)
|
|
(setf (alist-get 'perlalt consult-async-split-styles-alist)
|
|
`(:initial ,(or (cl-loop for char in (list "%" "@" "!" "&" "/" ";")
|
|
unless (string-match-p char query)
|
|
return char)
|
|
"%")
|
|
:type perl)
|
|
consult-async-split-style 'perlalt))))))
|
|
(consult--grep prompt #'consult--ripgrep-builder directory query)))
|
|
|
|
;;;###autoload
|
|
(defun +vertico/project-search (&optional arg initial-query directory)
|
|
"Peforms a live project search from the project root using ripgrep.
|
|
If ARG (universal argument), include all files, even hidden or compressed ones,
|
|
in the search."
|
|
(interactive "P")
|
|
(+vertico-file-search :query initial-query :in directory :all-files arg))
|
|
|
|
;;;###autoload
|
|
(defun +vertico/project-search-from-cwd (&optional arg initial-query)
|
|
"Performs a live project search from the current directory.
|
|
If ARG (universal argument), include all files, even hidden or compressed ones."
|
|
(interactive "P")
|
|
(+vertico/project-search arg initial-query default-directory))
|
|
|
|
;;;###autoload
|
|
(defun +vertico/search-symbol-at-point ()
|
|
(interactive)
|
|
(consult-line (thing-at-point 'symbol)))
|
|
|
|
;;;###autoload
|
|
(defun +vertico-embark-target-package-fn ()
|
|
"Targets Doom's package! statements and returns the package name"
|
|
(when (or (derived-mode-p 'emacs-lisp-mode) (derived-mode-p 'org-mode))
|
|
(save-excursion
|
|
(when (and (search-backward "(" nil t)
|
|
(looking-at "(\\s-*package!\\s-*\\(\\(\\sw\\|\\s_\\)+\\)\\s-*"))
|
|
(let ((pkg (match-string 1)))
|
|
(set-text-properties 0 (length pkg) nil pkg)
|
|
`(package . ,pkg))))))
|
|
|
|
;;;###autoload
|
|
(defun +vertico/embark-export-write ()
|
|
"Export the current vertico results to a writable buffer if possible.
|
|
|
|
Supports exporting consult-grep to wgrep, file to wdeired, and consult-location to occur-edit"
|
|
(interactive)
|
|
(require 'embark)
|
|
(require 'wgrep)
|
|
(pcase-let ((`(,type . ,candidates)
|
|
(run-hook-with-args-until-success 'embark-candidate-collectors)))
|
|
(pcase type
|
|
('consult-grep (let ((embark-after-export-hook #'wgrep-change-to-wgrep-mode))
|
|
(embark-export)))
|
|
('file (let ((embark-after-export-hook #'wdired-change-to-wdired-mode))
|
|
(embark-export)))
|
|
('consult-location (let ((embark-after-export-hook #'occur-edit-mode))
|
|
(embark-export)))
|
|
(x (user-error "embark category %S doesn't support writable export" x)))))
|
|
|
|
;;;###autoload
|
|
(defun +vertico/embark-preview ()
|
|
"Previews candidate in vertico buffer, unless it's a consult command"
|
|
(interactive)
|
|
(unless (bound-and-true-p consult--preview-function)
|
|
(save-selected-window
|
|
(let ((embark-quit-after-action nil))
|
|
(embark-dwim)))))
|
|
|
|
(defvar +vertico/find-file-in--history nil)
|
|
;;;###autoload
|
|
(defun +vertico/find-file-in (&optional dir initial)
|
|
"Jump to file under DIR (recursive).
|
|
If INITIAL is non-nil, use as initial input."
|
|
(interactive)
|
|
(require 'consult)
|
|
(let* ((default-directory (or dir default-directory))
|
|
(prompt-dir (consult--directory-prompt "Find" default-directory))
|
|
(cmd (split-string-and-unquote +vertico-consult-fd-args " ")))
|
|
(find-file
|
|
(consult--read
|
|
(split-string (cdr (apply #'doom-call-process cmd)) "\n" t)
|
|
:prompt default-directory
|
|
:sort nil
|
|
:initial (if initial (shell-quote-argument initial))
|
|
:add-history (thing-at-point 'filename)
|
|
:category 'file
|
|
:history '(:input +vertico/find-file-in--history)))))
|
|
|
|
;;;###autoload
|
|
(defun +vertico/jump-list (jump)
|
|
"Go to an entry in evil's (or better-jumper's) jumplist."
|
|
(interactive
|
|
(let (buffers)
|
|
(require 'consult)
|
|
(unwind-protect
|
|
(list
|
|
(consult--read
|
|
;; REVIEW Refactor me
|
|
(nreverse
|
|
(delete-dups
|
|
(delq
|
|
nil (mapcar
|
|
(lambda (mark)
|
|
(when mark
|
|
(cl-destructuring-bind (path pt _id) mark
|
|
(let* ((visiting (find-buffer-visiting path))
|
|
(buf (or visiting (find-file-noselect path t)))
|
|
(dir default-directory))
|
|
(unless visiting
|
|
(push buf buffers))
|
|
(with-current-buffer buf
|
|
(goto-char pt)
|
|
(font-lock-fontify-region
|
|
(line-beginning-position) (line-end-position))
|
|
(format "%s:%d: %s"
|
|
(car (cl-sort (list (abbreviate-file-name (buffer-file-name buf))
|
|
(file-relative-name (buffer-file-name buf) dir))
|
|
#'< :key #'length))
|
|
(line-number-at-pos)
|
|
(string-trim-right (or (thing-at-point 'line) ""))))))))
|
|
(cddr (better-jumper-jump-list-struct-ring
|
|
(better-jumper-get-jumps (better-jumper--get-current-context))))))))
|
|
:prompt "jumplist: "
|
|
:sort nil
|
|
:require-match t
|
|
:category 'jump-list))
|
|
(mapc #'kill-buffer buffers))))
|
|
(if (not (string-match "^\\([^:]+\\):\\([0-9]+\\): " jump))
|
|
(user-error "No match")
|
|
(let ((file (match-string-no-properties 1 jump))
|
|
(line (match-string-no-properties 2 jump)))
|
|
(find-file file)
|
|
(goto-char (point-min))
|
|
(forward-line (string-to-number line)))))
|
|
|
|
;;;###autoload
|
|
(defun +vertico-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))))))))
|
|
|
|
;;;###autoload
|
|
(defun +vertico/crm-select ()
|
|
"Toggle selection of current candidate in `consult-completing-read-multiple'.
|
|
If the candidate has been selected, move the index up by one, to allow for quick
|
|
selection of multiple subsequent candidates."
|
|
(interactive)
|
|
(let* ((selected-p (get-text-property 0 'consult--crm-selected (vertico--candidate)))
|
|
(goto-idx (+ vertico--index (if selected-p 0 1))))
|
|
(run-at-time 0 nil (cmd! (vertico--goto goto-idx) (vertico--exhibit))))
|
|
(vertico-exit))
|
|
|
|
;;;###autoload
|
|
(defun +vertico/crm-select-keep-input ()
|
|
"Like `+vertico/crm-select', but keeps the current minibuffer input."
|
|
(interactive)
|
|
(let* ((input (substring-no-properties (car vertico--input)))
|
|
(selected-p (get-text-property 0 'consult--crm-selected (vertico--candidate)))
|
|
(goto-idx (+ vertico--index (if selected-p 0 1))))
|
|
(run-at-time 0 nil (cmd! (insert input) (vertico--exhibit) (vertico--goto goto-idx) (vertico--exhibit))))
|
|
(vertico-exit))
|
|
|
|
;;;###autoload
|
|
(defun +vertico/crm-exit ()
|
|
"Exit `consult-completing-read-multiple' session in a dwim way.
|
|
If there are no selected candidates, select the current candidate and exit.
|
|
If there are selected candidates, disregard the current candidate and exit."
|
|
(interactive)
|
|
(if (consult--crm-selected)
|
|
(progn
|
|
(when (minibuffer-contents)
|
|
(delete-minibuffer-contents)
|
|
(vertico--exhibit))
|
|
(vertico--goto -1)
|
|
(vertico-exit))
|
|
(run-at-time 0 nil #'vertico-exit)
|
|
(vertico-exit)))
|
|
|
|
;;;###autoload
|
|
(defun +vertico--consult--fd-builder (input)
|
|
(pcase-let* ((cmd (split-string-and-unquote +vertico-consult-fd-args))
|
|
(`(,arg . ,opts) (consult--command-split input))
|
|
(`(,re . ,hl) (funcall consult--regexp-compiler
|
|
arg 'extended)))
|
|
(when re
|
|
(list :command (append cmd
|
|
(list (consult--join-regexps re 'extended))
|
|
opts)
|
|
:highlight hl))))
|
|
|
|
(autoload #'consult--directory-prompt "consult")
|
|
;;;###autoload
|
|
(defun +vertico/consult-fd (&optional dir initial)
|
|
(interactive "P")
|
|
(if doom-projectile-fd-binary
|
|
(let* ((prompt-dir (consult--directory-prompt "Fd" dir))
|
|
(default-directory (cdr prompt-dir)))
|
|
(find-file (consult--find (car prompt-dir) #'+vertico--consult--fd-builder initial)))
|
|
(consult-find dir initial)))
|
|
|
|
;;;###autoload
|
|
(defun +vertico-basic-remote-try-completion (string table pred point)
|
|
(and (vertico--remote-p string)
|
|
(completion-basic-try-completion string table pred point)))
|
|
|
|
;;;###autoload
|
|
(defun +vertico-basic-remote-all-completions (string table pred point)
|
|
(and (vertico--remote-p string)
|
|
(completion-basic-all-completions string table pred point)))
|