Home GitHub Patreon
Discussions RSS Twitter

Support for imenu in dired

Date Change
2018-09-22 Use imenu--generic-function as part of custom index-making functions to capture indices generated by external packages

imenu is a very simple package that builds index of interesting positions in the current buffer and presents them as a menu. You pick the item and the point moves there. There is a built-in interface and also one in sallet, helm or counsel.

Unfortunatelly dired doesn't come with support for it, so here I add some definitions to generate the index of all the inserted directories.

The most common way to add items to the index is by modifying imenu-generic-expression which is a list of lists of the form (GROUP-NAME REGEX MATCH-GROUP). Then imenu searches for the REGEX and adds the corresponding MATCH-GROUP and its match position to the index. This is done by imenu-default-create-index-function which is the default value of imenu-create-index-function.

Another more generic way is to write your own functions imenu-prev-index-position-function and imenu-extract-index-name-function which find the position and the name of the item. If both of these are set imenu-default-create-index-function uses those instead of the regexp list.

I have a hybrid approach here. I use the regexp mechanism because that is what most external packages use and I want to be able to install those additions and use them seamlessly (for example dired-hacks and its filter groups). But I also want to add some other items to the index, so I set my own imenu-create-index-function and add some more items "manually".

In particular, I like to add all the parents of the current directory which can then be opened via dired-open's dired-open-subdir.

(defun my-dired-imenu-create-parents-index ()
  "Create index of all parent positions of current dired."
  (save-excursion
    (dired-prev-subdir 0)
    (let (parents
          (beg (save-excursion
                 (beginning-of-line)
                 (1- (search-forward "/")))))
      (while (search-backward "/" beg t)
        (push
         (cons
          (buffer-substring-no-properties beg (point))
          (1- (point)))
         parents))
      (cons "Parents" (cdr parents)))))

(defun my-dired-imenu-create-index ()
  "Create `imenu' index for dired."
  (let (subdirs-alist
        (parents-alist (my-dired-imenu-create-parents-index)))
    (let* ((imenu-generic-expression '(("Subdir" "^  \\(.*?\\):$" 1)))
           (alist (car (imenu-default-create-index-function)))
           (uniquified (f-uniquify-alist (-map 'car (cdr alist)))))
      (setq subdirs-alist
            (cons
             (car alist)
             (--remove
              (= 0 (length (car it)))
              (--map (cons (cdr (assoc (car it) uniquified)) (cdr it))
                     (cdr alist))))))
    (let ((alist (imenu-default-create-index-function)))
      (-cons* subdirs-alist
              parents-alist
              alist))))

(defun my-dired-imenu-init ()
  "Initialize `imenu' variables in current buffer."
  (setq-local imenu-create-index-function
              'my-dired-imenu-create-index))

To use this just add my-dired-imenu-init to dired-mode-hook.

(add-hook 'dired-mode-hook 'my-dired-imenu-init)

The code depends on f and dash.


Published at: 2017-05-01 14:27 Last updated at: 2023-02-08 15:59
Found a typo? Edit on GitHub!