ImenuMode

Overview

Imenu produces menus for accessing locations in documents, typically in the current buffer. You can access the locations using an ordinary menu (menu bar or other) or using minibuffer completion.

A typical use of Imenu shows a menu-bar menu that is an index or table of contents for the current buffer. For a source-code buffer it is typical to index definitions of functions, variables, and so on.

You can use Imenu with any major mode and any programming language or document type. If there is no Imenu support for your context, you can add it using EmacsLisp and the constructs provided in library imenu.el.

There are two ways to generate an Imenu menu (index):

For a source-code buffer the locations to index are typically definitions of functions, variables, and so on. For a text or markup buffer (e.g. LaTeX) the locations might be section headings. You can use an EmacsLisp marker as a target destination, so that targets need not be limited to the current buffer.

See Imenu in the Emacs manual.

Imenu from a Menu

To automatically add an Imenu menu to the menu bar for a given mode, do something like this:

 (add-hook 'my-mode-hook 'imenu-add-menubar-index)

‘font-lock-mode-hook’ is run after entering a major mode. You can make use of this to add an Imenu index to the menu bar in any mode that supports Imenu:

 (defun try-to-add-imenu ()
  (condition-case nil (imenu-add-to-menubar "yourFancyName") (error nil)))
 (add-hook 'font-lock-mode-hook 'try-to-add-imenu)

Imenu from the Keyboard, with Completion

It can often be quicker to access an Imenu menu from the keyboard, using completion:

 M-x imenu

Why? Because you don’t have to read down a long list of menu items, many of which look similar. Just type a prefix of the defined name, and let completion do the work for you.

Icicles can make this even handier, by letting you complete any part of a defined name (or its definition), not just a prefix – see ImenuBrowser, below. Just type enough of a name to identify it uniquely, and voila!

You can bind Imenu to any key sequence. Here is how to bind it to ‘S-mouse-3’:

 (if (featurep 'xemacs)
     (global-set-key [(shift button3)] 'imenu) ; XEmacs
   (global-set-key [S-mouse-3] 'imenu)) ; GNU Emacs

Quickly force rescan

You can avoid auto rescanning big buffers just making it easy to manually rescan:

 (defun my-imenu-rescan ()
   (interactive)
   (imenu--menubar-select imenu--rescan-item))
 (global-set-key "\C-cI" 'my-imenu-rescan)

Grouping and Truncating Menu Items

If there are too many Imenu menu items then Imenu splits them into submenus. By default, these submenus are labeled `From: ...’ – see ImenuScreenshot, below. You can change how many menu items can be listed in each submenu by customizing option ‘imenu-max-items’.

Long menu items are truncated to ‘imenu-max-item-length’ characters. Depending on the size of your display, you might want to customize this option also.

Sorting

You can control the order of Imenu menu items by customizing option ‘imenu-sort-function’. For alphabetical order, use ‘imenu--sort-by-name’ as the value.

Sorting submenus

The following patch lets the user (final user or major mode developer) control whether the submenus are sorted or kept on top. The guiding idea is that there is two major ways to offer an imenu, according to the mode:

1) Some modes show a hierarchy of language objects. For example: python-mode will show class/method, function/nested function, etc. relationships; org-mode will show section/subsection/subsubsection/… hierarchies. In these cases keeping the submenus on top is not adequate since it creates an artificial split of the list.

2) Some modes show top level submenus with fixed categories (Functions, Classes, Variables, etc). These modes will presumably want to keep the submenus on top and sorted in the order they were given.

3) Other modes would not fit either (1) or (2). Then, there is always the possibility of turning off sorting and provide the menu structure as is.

First, add a custom variable to enable submenu sorting:

 > (defcustom imenu-sort-submenus nil
 >   "Non-nil means Imenu should sort submenus also (using imenu-sort-function)."
 >   :type 'boolean
 >   :group 'imenu)

Then, modify imenu--split-menu:

 <       (when (imenu--subalist-p item)
 ---
 >       (when (and imenu-sort-submenus
 >                  (imenu--subalist-p item))

Icicles - Imenu for Multiple Buffers, Files, or Bookmarks

Icicles command ‘icicle-imenu’ lets you browse the definitions in any set of Imenu-supported buffers, files, or bookmark destinations at the same time. Use it to navigate among Imenu entries anywhere.

This screenshot shows ‘icicle-imenu’ finding function definitions that match ‘kill’:

IciclesImenuScreenshot

There are more specific Icicles commands that browse only definitions of a specific type:

There are also Icicles commands that let you match not only the name of the defined term (function etc.) but all parts of its definition. These have the same names as the commands above, but with the suffix ‘-full’.

See Icicles - Search Commands, Overview.

Imenus - Imenu for multiple buffers and files

Another way to use imenu indexes for multiple buffers or files is imenus package. Along with the usual ‘*Rescan*’ item it is possible to use a key binding (‘M-r’ by default) to rescan the current index. Also with other additional key bindings (‘M-s’ and ‘M-o’ by default) you can quickly “jump” from imenu prompt to ‘isearch’ or ‘occur’.

Imenu+

Library imenu+.el (Imenu+) extends standard library imenu.el.

Here is a screenshot showing the Defs menu in Emacs-Lisp mode (not up-to-date: does not show toggle menu items):

DrewEmacsImenuImage

Imenu anywhere with IDO and Helm

imenu-anywhere package provides `imenu-anywhere` command that pops an IDO interface with all the imenu tags across all buffers with the same mode as the current one. This makes it similar to etag selection, but only for the open buffers. This is often more convenient as you don’t have to explicitly build the etags table.

The package is also available from melpa repository. To activate, just bind `imenu-anywhere` to a convenient key:

    (global-set-key (kbd "C-.") 'imenu-anywhere)

There is also `helm-imenu-anywhere` which is like `menu-anywhere` but uses helm interface instead of IDO.

Defining an Imenu Menu for a Mode

Here is an example of an ‘imenu-generic-expression’ for ‘mail-mode’:

  (setq mail-imenu-generic-expression
      '(("Subject"  "^Subject: *\\(.*\\)" 1)
        ("Cc"     "^C[Cc]: *\\(.*\\)" 1)
        ("To"     "^To: *\\(.*\\)" 1)
        ("From"  "^From: *\\(.*\\)" 1)))

You can hook this to ‘mail-mode’:

  (add-hook 'mail-mode-hook 
        (lambda ()
           (setq imenu-generic-expression mail-imenu-generic-expression)))
Isn’t setq-ing imenu-generic-expression to mail-imenu-generic-expression overwriting default imenu search settings for that particular mode? If you only want to expand the current imenu setup with new keywords, it seems better to add them with (add-hook ….. (add-to-list my-customization imenu-generic-expression)) – MaliRemorker

Here’s a more complex example, for PL/SQL (for SqlMode):

 (setq sql-imenu-generic-expression
       '(("Comments" "^-- \\(.+\\)" 1)
	 ("Function Definitions" "^\\s-*\\(function\\|procedure\\)[ \n\t]+\\([a-z0-9_]+\\)\
 [ \n\t]*([a-z0-9 _,\n\t]*)[ \n\t]*\\(return[ \n\t]+[a-z0-9_]+[ \n\t]+\\)?[ai]s\\b" 2)
	 ("Function Prototypes" "^\\s-*\\(function\\|procedure\\)[ \n\t]+\\([a-z0-9_]+\\)\
 [ \n\t]*([a-z0-9 _,\n\t]*)[ \n\t]*\\(return[ \n\t]+[a-z0-9_]+[ \n\t]*\\)?;" 2)
	 ("Indexes" "^\\s-*create\\s-+index\\s-+\\(\\w+\\)" 1)
	 ("Tables" "^\\s-*create\\s-+table\\s-+\\(\\w+\\)" 1)))
Hmm, sorry but his does not work here. I’m using emacs 21.1 and execute the above. imenu just points at me, telling me that “imenu--make-index-alist: No items suitable for an index found in this buffer” – MathiasDahl
Aaah! Likely there is no (setq imenu-generic-expression sql-imenu-generic-expression) statement in your major mode. Use a hook like the example later.
Note: imenu-generic-expression is automatically buffer local so make-local-variable is not really needed.
You can make your imenu expression customizable too. As I do in TalMode.
(defcustom tal-imenu-expression-alist
  '(("Sections"  "^\\?SECTION +\\(\\w+\\b\\)"          1)
    ("Pages"     "^\\?PAGE +\"\\(.+?\\)\""             1)
    ("SubProcs"  "^\\w*\\s-*subproc\\s-+\\(\\w+\\)\\b" 1)
    ("procs"     "^\\(?:\\w+\\(?:\\s-*(\\w+)\\)?\\s-+\\)?proc\\s-+\\(\\w+\\)\\b" 1)
  )
  "Alist of regular expressions for the imode index.  Each element has
the form (submenu-name regexp index).  Where submenu-name is the name of
the submenu under which items matching regexp are placed. When
submenu-name is nil the matching entries appear in the root imenu list.
Regexp index indicates which regext text group defines the text entry.
When the index is 0 the entire text that matches regexp appears."
  :type '(repeat (list (choice :tag "Submenu Name" string (const nil))
                       regexp (integer :tag "Regexp index")))
  :group 'tal
)

In these examples, the major mode supports a buffer local copy of the “real” variable, ‘imenu-generic-expression’. If your mode doesn’t do it, you will have to rely on a hook. Here is an example for a fictive foo-mode. Note that we still require a foo-mode-hook. Without a hook, things would be really messy (but not impossible).

 (add-hook 'foo-mode-hook
           (lambda ()
              (set (make-local-variable imenu-generic-expression)
                   '(("Comments" "^\\s-*#" 1)
                     ...))))

That way, setting ‘imenu-generic-expression’ will only change for the buffer the hook runs in – the foo-mode buffer.

Using Selection Buffer

I do not like completion I/F. I prefer using selection buffer. – Anonymous

(define-derived-mode imenu-selection-mode fundamental-mode "imenu"
  "Major mode for imenu selection."
  (suppress-keymap imenu-selection-mode-map)
  (define-key imenu-selection-mode-map "j" 'next-line)
  (define-key imenu-selection-mode-map "k" 'previous-line)
  (define-key imenu-selection-mode-map "l" 'imenu-selection-select)
  (define-key imenu-selection-mode-map "\C-m" 'imenu-selection-select)
  (define-key imenu-selection-mode-map "h" 'kill-this-buffer)
  )
(defvar imenu--selection-buffer " *imenu-select*")
(defvar imenu--target-buffer nil)
(defun imenu-make-selection-buffer (&optional index-alist)
  (interactive)
  (require 'which-func)
  (setq index-alist (if index-alist index-alist (imenu--make-index-alist)))
  (let ((cur (which-function)))
    (when (listp cur)
      (setq cur (car cur)))
    (setq imenu--target-buffer (current-buffer))
    (switch-to-buffer imenu--selection-buffer)
    (buffer-disable-undo)
    (erase-buffer)
    (dolist (x index-alist)
      (insert (car x) "\n"))
    (if cur (search-backward (concat cur "\n") nil t))
    (imenu-selection-mode)))

(defun imenu-selection-select ()
  (interactive)
  (let ((sel (substring (thing-at-point 'line) 0 -1)))
    (bury-buffer)
    (switch-to-buffer imenu--target-buffer)
    (imenu sel)))

Using Ido

Lisp:idomenu.el by GeorgBrandl lets you use Ido with imenu.

Alternatively, add the following command to your init file:

    (defun ido-goto-symbol (&optional symbol-list)
      "Refresh imenu and jump to a place in the buffer using Ido."
      (interactive)
      (unless (featurep 'imenu)
        (require 'imenu nil t))
      (cond
       ((not symbol-list)
        (let ((ido-mode ido-mode)
              (ido-enable-flex-matching
               (if (boundp 'ido-enable-flex-matching)
                   ido-enable-flex-matching t))
              name-and-pos symbol-names position)
          (unless ido-mode
            (ido-mode 1)
            (setq ido-enable-flex-matching t))
          (while (progn
                   (imenu--cleanup)
                   (setq imenu--index-alist nil)
                   (ido-goto-symbol (imenu--make-index-alist))
                   (setq selected-symbol
                         (ido-completing-read "Symbol? " symbol-names))
                   (string= (car imenu--rescan-item) selected-symbol)))
          (unless (and (boundp 'mark-active) mark-active)
            (push-mark nil t nil))
          (setq position (cdr (assoc selected-symbol name-and-pos)))
          (cond
           ((overlayp position)
            (goto-char (overlay-start position)))
           (t
            (goto-char position)))))
       ((listp symbol-list)
        (dolist (symbol symbol-list)
          (let (name position)
            (cond
             ((and (listp symbol) (imenu--subalist-p symbol))
              (ido-goto-symbol symbol))
             ((listp symbol)
              (setq name (car symbol))
              (setq position (cdr symbol)))
             ((stringp symbol)
              (setq name symbol)
              (setq position
                    (get-text-property 1 'org-imenu-marker symbol))))
            (unless (or (null position) (null name)
                        (string= (car imenu--rescan-item) name))
              (add-to-list 'symbol-names name)
              (add-to-list 'name-and-pos (cons name position))))))))
    (global-set-key "\C-ci" 'ido-goto-symbol) ; or any key you see fit

– shjk (updated by MattKeller to handle overlays as “positions”; updated by VegardOye to set the mark before jumping).

Using Anything

Anything is a candidate selection framework. Start with ‘M-x anything-imenu’, narrow the list by typing some patterns(multiple patterns are space-delimited string), select with up/down/pgup/pgdown/C-p/C-n/C-v/M-v, choose with enter, With ‘C-z’ contents of selected candidate are displayed without quitting anything session.


Discussion

Can I make Imenu index the whole buffer even if using FoldingMode and all folds are folded?

– mina86

Maybe this (I don’t use folding):

    (defadvice imenu (around unfold-it compile activate)
      (save-restriction
        (widen)
        ad-do-it))

Error if minibuffer-prompt-properties includes "Inviolable"

Do this:

    (setq minibuffer-prompt-properties '(point-entered minibuffer-avoid-prompt)
          imenu-always-use-completion-buffer-p)

(or customize m-p-p and set Inviolable)

  1. Go to a buffer that supports imenu
  2. Do M-x imenu
  3. Don’t hit TAB
  4. Immediately try to select a completion from the completions buffer

In Gnu Emacs 21.4.1, I get an “Args out of range” error. It’s fixed in GNU Emacs in CVS.

Now can anybody tell me why I set my minibuffer prompt to be inviolable? 😊

DenisHowe 2007-03-08

Can I make Imenu index all files in a project, e.g. with rope?


CategoryCode CategoryMenus CategoryModes CategoryProgrammerUtils CategoryNavigation