:BADGE: [[https://melpa.org/#/consult-dir][file:https://melpa.org/packages/consult-dir-badge.svg]] :END:
[[file:media/consult-dir.png]]
Consult-dir allows you to easily insert directory paths into the minibuffer prompt in Emacs.
When using the minibuffer, you can switch - with completion and filtering provided by your completion setup - to any directory you've visited recently, or to a project, a bookmarked directory or even a remote host via tramp. The minibuffer prompt will be replaced with the directory you choose.
Why would you want to do this?
To avoid "navigating" long distances when picking a file or directory in any Emacs command that requires one. Here I use it to select a distant directory when copying a file with dired:
https://user-images.githubusercontent.com/8607532/128619960-a89b578c-b627-417b-b045-06bcbb178b71.mp4
Think of it like the shell tools [[https://github.com/wting/autojump][autojump]], [[https://github.com/clvv/fasd][fasd]] or z but for Emacs. See the demos section below for many more examples. =consult-dir= works with all Emacs commands that require you to specify file paths, and with [[https://github.com/oantolin/embark][Embark actions]] on files.
The directory candidates are collected from user bookmarks, Projectile project roots (if available), project.el project roots (if available) and recentf file locations. The =default-directory= variable is not changed in the process.
Contents :TOC_3:
[[#installation][Installation]]
[[#usage][Usage]]
[[#demos][Demos]]
[[#configuration][Configuration]]
[[#alternatives][Alternatives]]
[[#acknowledgements][Acknowledgements]]
Installation =consult-dir= is on MELPA.
** With =use-package=
(use-package consult-dir :ensure t :bind (("C-x C-d" . consult-dir) :map minibuffer-local-completion-map ("C-x C-d" . consult-dir) ("C-x C-j" . consult-dir-jump-file)))
Replace =minibuffer-local-completion-map= above with
** General method After adding MELPA to your package archives,
(add-to-list 'package-archives '("melpa" . "http://melpa.org/packages/"))
you can install it with =M-x package-install consult-dir= and bind =consult-dir= as convenient:
(define-key global-map (kbd "C-x C-d") #'consult-dir) (define-key minibuffer-local-completion-map (kbd "C-x C-d") #'consult-dir)
If you want to use the file-jump functionality, you can bind =consult-dir-jump-file= in the =minibuffer-local-completion-map=.
(define-key minibuffer-local-completion-map (kbd "C-x C-j") #'consult-dir-jump-file)
Replace =minibuffer-local-completion-map= above with
=vertico-map= if you use Vertico,
=selectrum-minibuffer-map= if you use Selectrum.
Usage Call =consult-dir= when in the minibuffer to choose a directory with completion and insert it into the minibuffer prompt, shadowing or replacing the directory path showing currently. The file name part of the text is retained. This lets the user switch to distant directories very quickly when finding files, for instance.
Call =consult-dir= from a regular buffer to choose a directory with completion and then interactively find a file in that directory. The command run with this directory is configurable via =consult-dir-default-command= and defaults to =find-file=.
Call =consult-dir-jump-file= from the minibuffer to asynchronously find a file anywhere under the directory that is currently in the prompt. This can be used with =consult-dir= to quickly switch directories and find files at an arbitrary depth under them. =consult-dir-jump-file= uses =consult-find= under the hood.
https://user-images.githubusercontent.com/8607532/127817864-57ca9ec5-af67-4ee8-9410-4758c8450c4b.mp4
In this demo I call =consult-grep= with a prefix argument. This requires me to specify a directory to grep inside of, so I use =consult-dir= to specify that directory:
https://user-images.githubusercontent.com/8607532/127790057-1fa9f81b-2c3f-412d-be36-925773451c71.mp4
Here I use =consult-dir= to jump to one of my project directories when attaching a file to an email:
https://user-images.githubusercontent.com/8607532/127790065-39ddc117-5e1a-4580-a009-bc0cebd71ad8.mp4
In this example I combine =consult-dir= with Embark. I use =consult-dir= to specify a directory, then Embark to spawn an eshell there. I then use =consult-dir= again when tab-completing inside eshell to specify a distant directory to copy files from. Finally I use =consult-dir= with Embark to jump to a bookmark in a window-split:
https://user-images.githubusercontent.com/8607532/127790071-4f98d212-c127-48e1-84cd-01701cd63d64.mp4
(In these demos I am using Vertico as my completion system.)
However, only bookmarked directories and Project.el projects are displayed by default. if you use Projectile or want finer control over the directories that are offered as candidates to jump to, read on.
** Directory sources *** Bookmarks Enabled by default. To disable, customize =consult-dir-sources=.
*** Recent directories To enable, turn on recentf-mode. (=M-x recentf-mode=). Note that if you don't already use recentf-mode, the recentf directory cache will start out empty and build up over time as you use Emacs.
*** Project directories (Project.el) Enabled by default. To disable, customize =consult-dir-project-list-function= or
(setq consult-dir-project-list-function nil)
*** Project directories (Projectile) To enable, customize =consult-dir-project-list-function= or
(setq consult-dir-project-list-function #'consult-dir-projectile-dirs)
*** Remote hosts Also included are a number of sources for interacting with remote hosts via tramp, principally:
By default consult-dir does not display known SSH hosts as a separate directory source. If you wish to enable it, customize =consult-dir-sources= or use the following:
(add-to-list 'consult-dir-sources 'consult-dir--source-tramp-ssh t)
*** Docker hosts
It's also possible to define a source to switch to containers using consult-dir. This approach will work for both =podman= and =docker= containers, so adjust as desired.
If you're using Emacs 29+, you can also wrap =tramp-container--completion-function= instead.
(defcustom consult-dir--tramp-container-executable "docker" "Default executable to use for querying container hosts." :group 'consult-dir :type 'string)
(defcustom consult-dir--tramp-container-args nil "Optional list of arguments to pass when querying container hosts." :group 'consult-dir :type '(repeat string))
(defun consult-dir--tramp-container-hosts () "Get a list of hosts from a container host." (cl-loop for line in (cdr (ignore-errors (apply #'process-lines consult-dir--tramp-container-executable (append consult-dir--tramp-container-args (list "ps"))))) for cand = (split-string line "[[:space:]]+" t) collect (let ((user (unless (string-empty-p (car cand)) (concat (car cand) "@"))) (hostname (car (last cand)))) (format "/docker:%s%s:/" user hostname))))
(defvar consult-dir--source-tramp-docker
(:name "Docker" :narrow ?d :category file :face consult-file :history file-name-history :items ,#'consult-dir--tramp-docker-hosts) "Docker candiadate source for
consult-dir'.")
;; Adding to the list of consult-dir sources (add-to-list 'consult-dir-sources 'consult-dir--source-tramp-docker t)
Then amend =consult-dir-sources= as in the above snippet to include the source you defined.
*** Writing your own directory source If none of the above include directories you want to jump to, you can write your own source. As a template, here is a source that adds paths provided by the shell tool [[https://github.com/clvv/fasd][Fasd]] to consult-dir:
;; A function that returns a list of directories (defun consult-dir--fasd-dirs () "Return list of fasd dirs." (split-string (shell-command-to-string "fasd -ld") "\n" t))
;; A consult source that calls this function
(defvar consult-dir--source-fasd
(:name "Fasd dirs" :narrow ?f :category file :face consult-file :history file-name-history :enabled ,(lambda () (executable-find "fasd")) :items ,#'consult-dir--fasd-dirs) "Fasd directory source for
consult-dir'.")
;; Adding to the list of consult-dir sources (add-to-list 'consult-dir-sources 'consult-dir--source-fasd t)
For additional directory sources, check out the [[https://github.com/karthink/consult-dir/wiki#additional-directory-sources][wiki]].
** Default =consult-dir= action When called from a regular buffer (/i.e/ not the minibuffer), =consult-dir= defaults to calling =find-file= after you choose a directory. To set it to open the directory in dired instead or to run a custom command, customize =consult-dir-default-command=.
** File name shadowing By default, choosing a directory using =consult-dir= when in the minibuffer results in the text already in the prompt being "shadowed" or made inactive, but you can still delete the new text to recover it. You can make the new text replace the old instead by setting =consult-dir-shadow-filenames= to =nil=.
=consult-buffer= (part of Consult) already allows you to switch to bookmarks and recentf files, so this might be sufficient for you if you need to visit a proximal set of files quickly. =consult-dir= is different in that it is composable with all Emacs commands that require you to specify a directory and thus works in more contexts.
Projectile and the built-in project.el have extensive support for listing and quickly switching projects and running actions on them. =consult-dir= is more of a one-stop shop ("just get me there") for switching directories as it includes recent directories and bookmarks in the mix, allows jumping to files with =consult-dir-jump-file=, and supports running arbitrary actions on directories using Embark. Of course, it also allows for fast directory selection when using any Emacs command that requires specifying a directory.