consult-dir: insert paths into minibuffer prompts in Emacs
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:
demo-dired.mp4
Think of it like the shell tools autojump, 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 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
Installation
consult-dir
is on MELPA.
use-package
With (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
vertico-map
if you use Vertico,selectrum-minibuffer-map
if you use Selectrum.
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.
Demos
Here I show the different directory sources using Consult’s narrowing feature, then use consult-dir
to easily jump around the filesystem and open files. I also use consult-dir-jump-file
to quickly drill down a directory when I don’t find the file I’m looking for at the top level:
demo-1.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:
demo-2.mp4
Here I use consult-dir
to jump to one of my project directories when attaching a file to an email:
demo-3.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:
demo-embark-1.mp4
(In these demos I am using Vertico as my completion system.)
Configuration
consult-dir
should work out of the box with no configuration needed beyond binding it to a key.
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:
consult-dir--source-tramp-local
for a set list of local/custom hosts (seeconsult-dir-tramp-local-hosts
to customize)consult-dir--source-tramp-ssh
for a list of parsed hosts from your~/.ssh/config
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 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 wiki.
consult-dir
action
Default 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
.
Alternatives
consult-dir
is akin to shell tools like autojump or fasd but for all file/directory contexts in Emacs.
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.
Acknowledgements
- Daniel Mendler for writing Consult and help with the code
- Omar Antolin Camarena for many suggestions on the design of consult-dir
- u/harizvi for the code to include Fasd directories.
- Ellis Kenyő for the TRAMP directory sources.