Tree-style directory views in dired with dired-subtree

By default, Emacs’ file browser/manager dired usually presents you with a flat list of files in a given directory. Entering a subdirectory then opens a new buffer with the listing of the subdirectory. Sometimes you might want to be able to see the contents of the subdirectory and the current directory in the same view. Many GUI file browsers visualise this with a tree structure with nodes that can be expanded or collapsed. In Emacs there is a built-in function dired-insert-subdir that inserts a listing of the subdirectory under the cursor, at the bottom of the current buffer instead of in a new buffer, but I’ve never found that very helpful.

The dired-subtree package (part of the magnificent dired hacks) improves on this by allowing you to expand subdirectories in place, like a tree structure. To install the package, use the following code:

(use-package dired-subtree
  (bind-keys :map dired-mode-map
             ("i" . dired-subtree-insert)
             (";" . dired-subtree-remove)))

This sets up the keybinds so that in dired, hitting i on a subdirectory expands it in place with an indented listing. You can expand sub-subdirectories in the same way, and so on. Hitting ; inside an expanded subdirectory collapses it.

Happily, some of my other favourite tools from dired hacks like dynamically narrowing the directory listing or copying and pasting files work as you would want in these expanded subdirectories.


Case-Insensitive Sorting in Dired on OS X

I like my dired directory listings to be sorted by name regardless of case. This was a bit fiddly to get working in OS X, but I found a solution using the built-in ls-lisp with a few extra options, rather than the system ls to generate the dired listing.

Here are the required settings:

;; using ls-lisp with these settings gives case-insensitve
;; sorting on OS X
(require 'ls-lisp)
(setq dired-listing-switches "-alhG")
(setq ls-lisp-use-insert-directory-program nil)
(setq ls-lisp-ignore-case t)
(setq ls-lisp-use-string-collate nil)
;; customise the appearance of the listing
(setq ls-lisp-verbosity '(links uid))
(setq ls-lisp-format-time-list '("%b %e %H:%M" "%b %e  %Y"))
(setq ls-lisp-use-localized-time-format t)

One downside of this is that it breaks dired-quick-sort, but I can live with that.

Speedy sorting in dired with dired-quick-sort

The package dired-quick-sort gives you a pop-up menu with lots of useful options to sort your dired view by name, time, size and extension, and optionally group all of the directories together at the top of the listing. This can be a bit fiddly and dired-quick-sort makes it really easy.

Install the package with

(use-package dired-quick-sort
  :ensure t

and then hit S in a dired buffer to bring up the sorting menu. Your sorting choice is then remembered for new dired buffers.

Batch-edit file permissions in dired

I’ve written before about using the editing features in dired (AKA wdired) to do neat things like redirect symlinks. I recently discovered that by adding the following option to your emacs config file:

;; allow editing file permissions
(setq wdired-allow-to-change-permissions t)

then you can also edit the file permissions directly in the dired buffer.

Here’s a quick animated example where I want to set group write permissions for all the pdf files in a particular directory. I use several tools to make this really easy:

  1. I used dired-narrow to filter the view down to just the pdf files
  2. I use C-x C-q to make the dired buffer editable
  3. I move to the group write permission spot on the first line and then use multiple cursors to add a cursor for each line
  4. I hit w to set the write permission, RET to quit multiple cursors, and C-c C-c to make the change permanent


Use Emacs as a two-paned FTP client

I’ve written before about using sunrise commander for two-paned dired allowing an intuitive way to copy files between directories. You can also use sunrise to connect to an ftp server. For example in sunrise commander I hit j to go to a new directory and enter /username@ftp.mysite.com: which prompts me for my password and then connects to the top level directory of my site. Note that the syntax is important – in this case I don’t need to specify I am using an ftp connection since the host name starts with ftp, but the trailing colon is needed to specify the top level directory. The full syntax is described in the manual.

Now you can have the local directory in the left pane and the remote directory in the right pane, and copy files back and forth. Of course the same approach can be used to connect to an ftp server in normal dired, but I find the two-paned view of sunrise commander particularly intuitive here.

Quickly preview images and other files with peep-dired

The package peep-dired lets you quickly scan through images (or any other documents) with previews in dired. In the example below, I invoke peep-dired (I bind it to P) and scan through a set of images in a dired listing. When I am finished I hit P again to quit peep-dired.


Here is my code to install and configure peep-dired with use-package

;;preview files in dired
(use-package peep-dired
  :ensure t
  :defer t ; don't access `dired-mode-map' until `peep-dired' is loaded
  :bind (:map dired-mode-map
              ("P" . peep-dired)))

Dynamically filter directory listing with dired-narrow

If you install the package dired-narrow (one of several neat packages described on the dired-hacks github page), then you can type filter strings to dynamically filter down a dired listing to match the filter.

For example, in the animation below I have a dired buffer containing lots of files. I invoke dired-narrow by hitting / and then type “png” to narrow to filenames matching “png”. I then hit RET to complete the filtering. At that point I can operate on the filtered dired list as I would for any dired buffer. I can even narrow further by simply invoking dired-narrow again and typing a new filter (I use “mirror” in the example below). Once I have finished with the filter, I hit g to revert back to the full directory listing.


As an aside, I have started using use-package to install and configure packages (see the update on this page). Here is the code I use to install and set up dired-narrow.

;;narrow dired to match filter
(use-package dired-narrow
  :ensure t
  :bind (:map dired-mode-map
              ("/" . dired-narrow)))

Open a recent directory in dired: revisited

I wrote recently about how to use the command diredp-dired-recent-dirs from dired+ to open a recently used directory in dired. It turns out I had misunderstood what that command was for – it is intended to create a dired buffer containing a list of recently used directories, which is not exactly what I wanted.

Here is a function to give you a list of recent directories, using ivy (part of swiper) to narrow it dynamically, and then open the selected one in dired.

;; open recent directory, requires ivy (part of swiper)
;; borrows from http://stackoverflow.com/questions/23328037/in-emacs-how-to-maintain-a-list-of-recent-directories
(defun bjm/ivy-dired-recent-dirs ()
  "Present a list of recently used directories and open the selected one in dired"
  (let ((recent-dirs
          (mapcar (lambda (file)
                    (if (file-directory-p file) file (file-name-directory file)))

    (let ((dir (ivy-read "Directory: "
                         :re-builder #'ivy--regex
                         :sort nil
                         :initial-input nil)))
      (dired dir))))

(global-set-key (kbd "C-x C-d") 'bjm/ivy-dired-recent-dirs)

Open recent directories in dired

I find myself doing more and more of my file system tasks in emacs with dired. The package dired+ adds some extra features to dired, one of which I discovered today.

The command diredp-dired-recent-dirs, bound to C-x R by default, presents you with a list of recent directories that you can then choose to open in dired. I have found to be a really quick way to find the directory I want to open, especially when combined with ivy-mode (or one of the other completion packages).

Dired+ can be installed from the normal package repositories.


This post is superseded by this one.