;;; imenu.el --- framework for mode-specific buffer indexes
-;; Copyright (C) 1994, 1995, 1996, 1997, 1998, 2002, 2003, 2004,
-;; 2005, 2006 Free Software Foundation, Inc.
+;; Copyright (C) 1994, 1995, 1996, 1997, 1998, 2001, 2002, 2003, 2004,
+;; 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
;; Author: Ake Stenhoff <etxaksf@aom.ericsson.se>
;; Lars Lindberg <lli@sypro.cap.se>
;; This file is part of GNU Emacs.
-;; GNU Emacs is free software; you can redistribute it and/or modify
+;; GNU Emacs is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation; either version 2, or (at your option)
-;; any later version.
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs; see the file COPYING. If not, write to the
-;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
-;; Boston, MA 02110-1301, USA.
+;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
:link '(custom-manual "(elisp)Imenu"))
(defcustom imenu-use-markers t
- "*Non-nil means use markers instead of integers for Imenu buffer positions.
+ "Non-nil means use markers instead of integers for Imenu buffer positions.
Setting this to nil makes Imenu work a little faster but editing the
buffer will make the generated index positions wrong.
(defcustom imenu-max-item-length 60
- "*If a number, truncate Imenu entries to that length."
+ "If a number, truncate Imenu entries to that length."
:type '(choice integer
(const :tag "Unlimited"))
:group 'imenu)
(defcustom imenu-auto-rescan nil
- "*Non-nil means Imenu should always rescan the buffers."
+ "Non-nil means Imenu should always rescan the buffers."
:type 'boolean
:group 'imenu)
(defcustom imenu-auto-rescan-maxout 60000
- "*Imenu auto-rescan is disabled in buffers larger than this size (in bytes).
+ "Imenu auto-rescan is disabled in buffers larger than this size (in bytes).
This variable is buffer-local."
:type 'integer
:group 'imenu)
:version "22.1")
(defcustom imenu-after-jump-hook nil
- "*Hooks called after jumping to a place in the buffer.
+ "Hooks called after jumping to a place in the buffer.
Useful things to use here include `reposition-window', `recenter', and
\(lambda () (recenter 0)) to show at top of screen."
;;;###autoload
(defcustom imenu-sort-function nil
- "*The function to use for sorting the index mouse-menu.
+ "The function to use for sorting the index mouse-menu.
Affects only the mouse index menu.
:group 'imenu)
(defcustom imenu-max-items 25
- "*Maximum number of elements in a mouse menu for Imenu."
+ "Maximum number of elements in a mouse menu for Imenu."
:type 'integer
:group 'imenu)
;; :group 'imenu)
(defcustom imenu-space-replacement "."
- "*The replacement string for spaces in index names.
+ "The replacement string for spaces in index names.
Used when presenting the index in a completion buffer to make the
names work as tokens."
:type '(choice string (const nil))
:group 'imenu)
(defcustom imenu-level-separator ":"
- "*The separator between index names of different levels.
+ "The separator between index names of different levels.
Used for making mouse-menu titles and for flattening nested indexes
with name concatenation."
:type 'string
;;;###autoload
(defvar imenu-create-index-function 'imenu-default-create-index-function
- "The function to use for creating a buffer index.
+ "The function to use for creating an index alist of the current buffer.
-It should be a function that takes no arguments and returns an index
-of the current buffer as an alist.
+It should be a function that takes no arguments and returns
+an index alist of the current buffer. The function is
+called within a `save-excursion'.
-Simple elements in the alist look like (INDEX-NAME . INDEX-POSITION).
-Special elements look like (INDEX-NAME INDEX-POSITION FUNCTION ARGUMENTS...).
-A nested sub-alist element looks like (INDEX-NAME SUB-ALIST).
-The function `imenu--subalist-p' tests an element and returns t
-if it is a sub-alist.
-
-This function is called within a `save-excursion'.")
+See `imenu--index-alist' for the format of the buffer index alist.")
;;;###autoload
(make-variable-buffer-local 'imenu-create-index-function)
;; The latest buffer index.
;; Buffer local.
(defvar imenu--index-alist nil
- "The buffer index computed for this buffer in Imenu.
-Simple elements in the alist look like (INDEX-NAME . INDEX-POSITION).
-Special elements look like (INDEX-NAME INDEX-POSITION FUNCTION ARGUMENTS...).
-A nested sub-alist element looks like (INDEX-NAME SUB-ALIST).")
+ "The buffer index alist computed for this buffer in Imenu.
+
+Simple elements in the alist look like (INDEX-NAME . POSITION).
+POSITION is the buffer position of the item; to go to the item
+is simply to move point to that position.
+
+Special elements look like (INDEX-NAME POSITION FUNCTION ARGUMENTS...).
+To \"go to\" a special element means applying FUNCTION
+to INDEX-NAME, POSITION, and the ARGUMENTS.
+
+A nested sub-alist element looks like (INDEX-NAME SUB-ALIST).
+The function `imenu--subalist-p' tests an element and returns t
+if it is a sub-alist.
+
+There is one simple element with negative POSITION; selecting that
+element recalculates the buffer's index alist.")
(make-variable-buffer-local 'imenu--index-alist)
(defvar imenu--last-menubar-index-alist nil
- "The latest buffer index used to update the menu bar menu.")
+ "The latest buffer index alist used to update the menu bar menu.")
(make-variable-buffer-local 'imenu--last-menubar-index-alist)
;; Split LIST into sublists of max length N.
;; Example (imenu--split '(1 2 3 4 5 6 7 8) 3)-> '((1 2 3) (4 5 6) (7 8))
+;;
+;; The returned list DOES NOT share structure with LIST.
(defun imenu--split (list n)
(let ((remain list)
(result '())
;;; Split the alist MENULIST into a nested alist, if it is long enough.
;;; In any case, add TITLE to the front of the alist.
+;;; If IMENU--RESCAN-ITEM is present in MENULIST, it is moved to the
+;;; beginning of the returned alist.
+;;;
+;;; The returned alist DOES NOT share structure with MENULIST.
(defun imenu--split-menu (menulist title)
- (let (keep-at-top tail)
+ (let ((menulist (copy-sequence menulist))
+ keep-at-top tail)
(if (memq imenu--rescan-item menulist)
- (setq keep-at-top (cons imenu--rescan-item nil)
+ (setq keep-at-top (list imenu--rescan-item)
menulist (delq imenu--rescan-item menulist)))
(setq tail menulist)
(dolist (item tail)
(push item keep-at-top)
(setq menulist (delq item menulist))))
(if imenu-sort-function
- (setq menulist (sort (copy-sequence menulist) imenu-sort-function)))
+ (setq menulist (sort menulist imenu-sort-function)))
(if (> (length menulist) imenu-max-items)
(setq menulist
(mapcar
;;; Split up each long alist that are nested within ALIST
;;; into nested alists.
+;;;
+;;; Return a split and sorted copy of ALIST. The returned alist DOES
+;;; NOT share structure with ALIST.
(defun imenu--split-submenus (alist)
(mapcar (function
(lambda (elt)
(defun imenu--make-index-alist (&optional noerror)
- "Create an index-alist for the definitions in the current buffer.
-
+ "Create an index alist for the definitions in the current buffer.
+This works by using the hook function `imenu-create-index-function'.
Report an error if the list is empty unless NOERROR is supplied and
non-nil.
-Simple elements in the alist look like (INDEX-NAME . INDEX-POSITION).
-Special elements look like (INDEX-NAME FUNCTION ARGUMENTS...).
-A nested sub-alist element looks like (INDEX-NAME SUB-ALIST).
-The function `imenu--subalist-p' tests an element and returns t
-if it is a sub-alist.
-
-There is one simple element with negative POSITION; that's intended
-as a way for the user to ask to recalculate the buffer's index alist."
+See `imenu--index-alist' for the format of the index alist."
(or (and imenu--index-alist
(or (not imenu-auto-rescan)
(and imenu-auto-rescan
(make-variable-buffer-local 'imenu-syntax-alist)
(defun imenu-default-create-index-function ()
- "*Wrapper for index searching functions.
+ "Default function to create an index alist of the current buffer.
-Moves point to end of buffer and then repeatedly calls
+The most general method is to move point to end of buffer, then repeatedly call
`imenu-prev-index-position-function' and `imenu-extract-index-name-function'.
-Their results are gathered into an index alist."
+All the results returned by the latter are gathered into an index alist.
+This method is used if those two variables are non-nil.
+
+The alternate method, which is the one most often used, is to call
+`imenu--generic-function' with `imenu-generic-expression' as argument."
;; These should really be done by setting imenu-create-index-function
;; in these major modes. But save that change for later.
(cond ((and imenu-prev-index-position-function
(t
(error "This buffer cannot use `imenu-default-create-index-function'"))))
-;; Not used and would require cl at run time
-;; (defun imenu--flatten-index-alist (index-alist &optional concat-names prefix)
-;; ;; Takes a nested INDEX-ALIST and returns a flat index alist.
-;; ;; If optional CONCAT-NAMES is non-nil, then a nested index has its
-;; ;; name and a space concatenated to the names of the children.
-;; ;; Third argument PREFIX is for internal use only.
-;; (mapcan
-;; (lambda (item)
-;; (let* ((name (car item))
-;; (pos (cdr item))
-;; (new-prefix (and concat-names
-;; (if prefix
-;; (concat prefix imenu-level-separator name)
-;; name))))
-;; (cond
-;; ((or (markerp pos) (numberp pos))
-;; (list (cons new-prefix pos)))
-;; (t
-;; (imenu--flatten-index-alist pos new-prefix)))))
-;; index-alist))
-
;;;
;;; Generic index gathering function.
;;;
;; This function can be called with quitting disabled,
;; so it needs to be careful never to loop!
(defun imenu--generic-function (patterns)
- "Return an index of the current buffer as an alist.
+ "Return an index alist of the current buffer based on PATTERNS.
PATTERNS is an alist with elements that look like this:
(MENU-TITLE REGEXP INDEX)
(MENU-TITLE REGEXP INDEX FUNCTION ARGUMENTS...)
with zero or more ARGUMENTS. The former format creates a simple
element in the index alist when it matches; the latter creates a
-special element of the form (NAME POSITION-MARKER FUNCTION
-ARGUMENTS...) with FUNCTION and ARGUMENTS copied from
-`imenu-generic-expression'.
+special element of the form (INDEX-NAME POSITION-MARKER FUNCTION
+ARGUMENTS...) with FUNCTION and ARGUMENTS copied from PATTERNS.
MENU-TITLE is a string used as the title for the submenu or nil
if the entries are not nested.
appear in the menu. See the info section on Regexps for more
information. REGEXP may also be a function, called without
arguments. It is expected to search backwards. It shall return
-true and set `match-data' iff it finds another element.
+true and set `match-data' if it finds another element.
INDEX points to the substring in REGEXP that contains the
name (of the function, variable or type) that is to appear in the
With no index alist ALIST, it calls `imenu--make-index-alist' to
create the index alist.
-If `imenu-use-popup-menu' is non-nil, then the
-completion buffer is always used, no matter if the mouse was used or
-not.
+If `imenu-use-popup-menu' is nil, then the completion buffer
+is always used, no matter if the mouse was used or not.
The returned value is of the form (INDEX-NAME . INDEX-POSITION)."
(let (index-alist
`(menu-item ,name ,(make-sparse-keymap "Imenu")))
(use-local-map newmap)
(add-hook 'menu-bar-update-hook 'imenu-update-menubar))
- (error "The mode `%s' does not support Imenu" mode-name)))
+ (error "The mode `%s' does not support Imenu"
+ (format-mode-line mode-name))))
;;;###autoload
(defun imenu-add-menubar-index ()
(defvar imenu-buffer-menubar nil)
(defvar imenu-menubar-modified-tick 0
- "The value of (buffer-modified-tick) as of last call to `imenu-update-menubar'.")
+ "The value of (buffer-chars-modified-tick) as of the last call
+to `imenu-update-menubar'.")
(make-variable-buffer-local 'imenu-menubar-modified-tick)
(defun imenu-update-menubar ()
(when (and (current-local-map)
(keymapp (lookup-key (current-local-map) [menu-bar index]))
- (not (eq (buffer-modified-tick)
- imenu-menubar-modified-tick)))
- (setq imenu-menubar-modified-tick (buffer-modified-tick))
+ (/= (buffer-chars-modified-tick) imenu-menubar-modified-tick))
+ (setq imenu-menubar-modified-tick (buffer-chars-modified-tick))
(let ((index-alist (imenu--make-index-alist t)))
;; Don't bother updating if the index-alist has not changed
;; since the last time we did it.