So I'm looking for some insight into how to customize my ibuffer window. I don't know much lisp but have a decent amount of emacs experience.

I tried solving it based on some google searches:
and a few more links. I've tried various combinations of the tools they describe, but nothing does exactly what I want, which is roughly a combination of sorting by pathname, and filter groups. However, instead of predefined 'saved-filter-groups', I want to have filter groups based on the directory structure.

Ideally my ibuffer would display directory names as filter groups, and then sort the files alphabetically within each group. Also if possible, I would like the named directory filter groups to only display up to 3 parent directory levels

So if the buffers I have open are:


Then my ibuffer window would look like:

[ .../Foo/Bar/a ]
[ .../Foo/Bar/b ]

While I'd love a complete solution, any insight or resources you could point me towards would be nice too.

3 Answers 3


Not really part of the question, but here's a sorter that sorts by directory.

  (define-ibuffer-sorter pathname
    "Sort by pathname"
    (:description "path")
    (cl-flet ((get-pathname
               (with-current-buffer (car data)
                 (or buffer-file-name
                    (if (eq major-mode 'dired-mode)
                        (expand-file-name dired-directory))
                    ;; so that all non pathnames are at the end
      (string< (get-pathname a) (get-pathname b))))

  (define-key ibuffer-mode-map
    (kbd "s p") 'ibuffer-do-sort-by-pathname)

This defines a directory filter:

  (defun get-all-buffer-directories ()
    "Return a list of all directories that have at least one
       file being visited."
    (let (l)
      (dolist (e (sort (mapcar 'file-name-directory
                               (remove-if-not 'identity
                                              (mapcar 'buffer-file-name
        (unless (string= (car l) e)
          (setq l (cons e l))))

  (define-ibuffer-filter dirname
      "Toggle current view to buffers with in a directory DIRNAME."
    (:description "directory name"
                   (completing-read "Filter by directory: "
                                    t nil nil nil nil)))
    (string= qualifier
             (and (buffer-file-name buf)
                (file-name-directory (buffer-file-name buf)))))

This uses the filter to create filter groups by directory:

  (defun ibuffer-set-filter-groups-by-directory ()
    "Set the current filter groups to filter by directory."
    (setq ibuffer-filter-groups
          (mapcar (lambda (dir)
                    (cons (format "%s" dir) `((dirname . ,dir))))
    (ibuffer-update nil t))

  (define-key ibuffer-mode-map
    (kbd "/ D") 'ibuffer-set-filter-groups-by-directory)
  (define-key ibuffer-mode-map
    (kbd "/ d") 'ibuffer-filter-by-dirname)

You could modify the format expression to truncate the filter group names.

  • I tested this, and it does exactly what OP wants. It even adds some more goodies (directory filtering + sorting) to ibuffer. Perfect answer! Mar 27, 2021 at 0:19
  • 1
    Most of this is built into ibuffer now, but not ibuffer-set-filter-groups-by-directory.
    – jpkotta
    Mar 29, 2021 at 1:44
  • which version of emacs - because also sorting by pathname was not available for me running on 26.1 - ibuffer seems super cool, but probably the only way to learn about it is to read the code Mar 30, 2021 at 1:06
  • 1
    The latest version, 27. And yeah it's not documented that well. Once you understand things like marks, sorters, filters, and filter groups, the C-h f ibuffer-mode doc is OK. There are some nice third party packages like ibuffer-projectile and ibuffer-tramp that add more filter groups.
    – jpkotta
    Mar 30, 2021 at 23:11

Here's a solution offered by puercopop that I've been using, which works pretty well. I've modified it slightly in the following rehash (reproduced here to sidestep possible link rot):

;; create a "default" placeholder to build the filter groups
(setf ibuffer-saved-filter-groups (quote (("default"))))

;; replace the PROJECTS directory with whatever you like
(cl-dolist (dir (directory-files "~/projects/"))
  (when (not (member dir '("." "..")))
    (setf (car ibuffer-saved-filter-groups)
          (append (car ibuffer-saved-filter-groups)
                     (filename . ,(expand-file-name
                                   dir "~/projects/"))))))))

Here are some hints: First you need to write a filter, which will match members of one of the desired groups, i.e. by directory-name.

(define-ibuffer-filter directory-name
    "Filter by directory-name `equal'ity."

Then you need to write some function, which will setup ibuffer-filter-groups like this:

(setq ibuffer-filter-groups
      (("abbrev-dir1" (directory-name . "dir1"))
       ("abbrev-dir2" (directory-name . "dir2"))

, where dir1, dir2, etc. are the directories occuring in the ibuffer buffer (see e.g. ibuffer-map-lines). Finally use ibuffer-update to display the groups.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.