notes from /dev/null

by Charles Choi 최민수

TIL Imenu

02 Jul 2024  Charles Choi

Recently I learned that Imenu is a thing. While likely old-hat to those who already know, nevertheless this post will endeavor to 1) restate Imenu's profound usefulness, 2) argue that Imenu is terribly named, and 3) because of its terrible naming, argue that an index menu should be turned on by default in Emacs.

Imenu - what is it?

To quote the GNU reference for it:

The Imenu facility offers a way to find the major definitions in a file by name. It is also useful in text formatter major modes, where it treats each chapter, section, etc., as a definition.

For programming modes, Imenu gives you a menu interface (either mouse or keyboard-completion-driven) to the class/method/function and variable declarations in a file. It also provides the same analogous functionality for the headings in your Markdown files.

If the above hasn't sunk in, perhaps these screenshots will covey Imenu's utility. From the main menu, you can immediately go to a function declaration.

That same index menu is available via a right-mouse button click context menu.

Users who prefer a keyboard-driven UI can invoke the command M-x imenu where a completing-read interface is offered.

In short, in Emacs the common IDE feature of having your class/method/function/variable declarations in a menu is provided for by Imenu. Regrettably, this feature is not turned on by default in core Emacs.

Imenu is a terrible name

Some unvarnished opinion: I think Imenu is a terrible name. Abbreviating “i”, joining it with “menu”, and calling it a day has resulted in name so anodyne and milquetoast that it is easily forgettable. Also given the ambiguous semantics of an “i” prefix (I personally confuse it with the incremental in I-search) only adds to the high cognitive load of remembering Emacs commands and bindings. This is regrettable as an index menu is a common and arguably core feature in contemporary IDEs.

Turn on Imenu by default

Here’s the funny thing though. Most other IDEs don’t even bother to name an index menu feature as it is enabled (or rather, baked-in) in the UI by default. So I think it should be with Emacs. My guidance is to turn on the index menu in the Emacs UI for modes that support it. The benefits to this are:

  • One can always discover and access the index in the main menu.
  • Even if one prefers a keyboard-driven workflow, recalling either the command (imenu) or some user-defined binding to it can be reinforced by visually seeing "Index" in the menu bar.

Of course, the above guidance presumes that the menu bar is not configured to be hidden. Especially if you are new to Emacs, please do not disable the menu bar.

Imenu Configuration

To enable an index menu in the menu bar and context menu requires adding imenu-add-menubar-index to the hook of a mode that supports it. For example, the following configuration will enable an index menu for Markdown, Makefile, and all modes derived from the programming mode prog-mode.

(add-hook 'markdown-mode-hook #'imenu-add-menubar-index)
(add-hook 'makefile-mode-hook #'imenu-add-menubar-index)
(add-hook 'prog-mode-hook #'imenu-add-menubar-index)

Note that the above prog-mode-hook configuration presumes that the derived programming mode you use supports Imenu. As far as I know, this is true for most every popular programming language mode.

To turn on context menu mode, add context-menu-mode to the mode hook of interest. For example for prog-mode-hook:

(add-hook 'prog-mode-hook 'context-menu-mode)

By default, changes to the file will not automatically update the index menu. Set imenu-auto-rescan to t in local fashion as shown below:

(add-hook 'markdown-mode-hook (lambda () (setq-local imenu-auto-rescan t)))
(add-hook 'makefile-mode-hook (lambda () (setq-local imenu-auto-rescan t)))
(add-hook 'prog-mode-hook
      (lambda ()
        (setq-local imenu-auto-rescan t)
        (setq-local imenu-sort-function #'imenu--sort-by-name)))

The variable imenu-sort-function can be set to a function that will sort the index order. Imenu provides two sort functions: imenu--sort-by-name and imenu--sort-by-position, the latter being the default if imenu-sort-function is not set. In typical Emacs fashion, a user-defined function can be used for those motivated enough to reverse engineer the Imenu sort functions.

One might be concerned on the performance issues of auto-rescan and/or sorting a file index but in practice I've really not experienced any noticeable slowdown. I suggest turning them on, particularly auto-rescan as Imenu has contingencies to avoid rescan if the file size is too large (controlled by the variable imenu-auto-rescan-maxout) or takes too much time (controlled by the variable imenu-max-index-time).

What about Org Mode?

Amended 2024-06-02 13:30 PDT

To my knowledge, Org does not support Imenu, but offers the command org-goto which is effectively the equivalent command to imenu.

Org mode does have support for Imenu but it comes with a couple of qualifiers:

  • The depth of the headings indexed is controlled by a separate customizable variable org-imenu-depth.
  • The Org file must have a first-level heading at the start of the document.
    • Org files that only have headings that are depth 2 or greater will not be recognized by Imenu.
      • For this case, Org offers the command org-goto which is effectively the equivalent command to imenu.

Shown below is configuration code to turn on Imenu for Org.

(setq org-imenu-depth 7)
(add-hook 'org-mode-hook #'imenu-add-menubar-index)
(add-hook 'org-mode-hook (lambda () (setq-local imenu-auto-rescan t)))

Note: Thanks to Signal_Pattern_2063 on Reddit for commenting on my initial error of saying Org doesn't support Imenu. It does but with the caveats described above.

Casual Avy

Index navigation is so closely related the behavior of Casual Avy that I've added Imenu support to it as of v1.2.2.

Closing Thoughts

Imenu is a nice UI feature in Emacs that is too easy to ignore because it is not enabled by default. Leave it on always and be surprised at how useful it can be.




Feeds & TagsGet Captee for macOS

Powered by Pelican