X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/fad2f6858075f49c4c8fd16f0535c287e3f14ac3..9805f81dda38cd541ba8043f44e720e06adf6492:/lisp/imenu.el diff --git a/lisp/imenu.el b/lisp/imenu.el index af617498b0..8363956355 100644 --- a/lisp/imenu.el +++ b/lisp/imenu.el @@ -1,7 +1,6 @@ ;;; imenu.el --- framework for mode-specific buffer indexes -;; Copyright (C) 1994, 1995, 1996, 1997, 1998, 2003, 2004 -;; Free Software Foundation, Inc. +;; Copyright (C) 1994-1998, 2001-2012 Free Software Foundation, Inc. ;; Author: Ake Stenhoff ;; Lars Lindberg @@ -11,10 +10,10 @@ ;; 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 @@ -22,9 +21,7 @@ ;; 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., 59 Temple Place - Suite 330, -;; Boston, MA 02111-1307, USA. +;; along with GNU Emacs. If not, see . ;;; Commentary: @@ -78,7 +75,7 @@ :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. @@ -89,25 +86,25 @@ This might not yet be honored by all index-building functions." (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) (defvar imenu-always-use-completion-buffer-p nil) (make-obsolete-variable 'imenu-always-use-completion-buffer-p - 'imenu-use-popup-menu "21.4") + 'imenu-use-popup-menu "22.1") (defcustom imenu-use-popup-menu (if imenu-always-use-completion-buffer-p @@ -119,17 +116,18 @@ If t, always use a popup menu, If `on-mouse' use a popup menu when `imenu' was invoked with the mouse." :type '(choice (const :tag "On Mouse" on-mouse) (const :tag "Never" nil) - (other :tag "Always" t))) + (other :tag "Always" t)) + :group 'imenu) (defcustom imenu-eager-completion-buffer (not (eq imenu-always-use-completion-buffer-p 'never)) "If non-nil, eagerly popup the completion buffer." :type 'boolean :group 'imenu - :version "21.4") + :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." @@ -138,7 +136,7 @@ Useful things to use here include `reposition-window', `recenter', and ;;;###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. @@ -157,13 +155,13 @@ element should come before the second. The arguments are cons cells; :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) ;; No longer used. KFS 2004-10-27 ;; (defcustom imenu-scanning-message "Scanning buffer for index (%3d%%)" -;; "*Progress message during the index scanning of the buffer. +;; "Progress message during the index scanning of the buffer. ;; If non-nil, user gets a message during the scanning of the buffer. ;; ;; Relevant only if the mode-specific function that creates the buffer @@ -174,14 +172,14 @@ element should come before the second. The arguments are cons cells; ;; :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 @@ -191,37 +189,15 @@ with name concatenation." (defvar imenu-generic-expression nil "The regex pattern to use for creating a buffer index. -If non-nil this pattern is passed to `imenu--generic-function' -to create a buffer index. - -The value should be an alist with elements that look like this: - (MENU-TITLE REGEXP INDEX) -or like this: - (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'. - -MENU-TITLE is a string used as the title for the submenu or nil if the -entries are not nested. - -REGEXP is a regexp that should match a construct in the buffer that is -to be displayed in the menu; i.e., function or variable definitions, -etc. It contains a substring which is the name to appear in the -menu. See the info section on Regexps for more information. - -INDEX points to the substring in REGEXP that contains the name (of the -function, variable or type) that is to appear in the menu. - -The variable `imenu-case-fold-search' determines whether or not the -regexp matches are case sensitive, and `imenu-syntax-alist' can be -used to alter the syntax table for the search. +If non-nil this pattern is passed to `imenu--generic-function' to +create a buffer index. Look there for the documentation of this +pattern's structure. For example, see the value of `fortran-imenu-generic-expression' used by `fortran-mode' with `imenu-syntax-alist' set locally to give the characters which normally have \"symbol\" syntax \"word\" syntax during matching.") +;;;###autoload(put 'imenu-generic-expression 'risky-local-variable t) ;;;###autoload (make-variable-buffer-local 'imenu-generic-expression) @@ -230,18 +206,13 @@ during matching.") ;;;###autoload (defvar imenu-create-index-function 'imenu-default-create-index-function - "The function to use for creating a buffer index. - -It should be a function that takes no arguments and returns an index -of the current buffer as an alist. + "The function to use for creating an index alist of the current buffer. -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. +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'. -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) @@ -326,9 +297,9 @@ The function in this variable is called when selecting a normal index-item.") ;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; FIXME: This is the only imenu-example-* definition that's actually used, -;; and it seems to only be used by cperl-mode.el. We should just move it to -;; cperl-mode.el and remove the rest. +;; FIXME: This was the only imenu-example-* definition actually used, +;; by cperl-mode.el. Now cperl-mode has its own copy, so these can +;; all be removed. (defun imenu-example--name-and-position () "Return the current/previous sexp and its (beginning) location. Don't move point." @@ -339,6 +310,8 @@ Don't move point." (end (progn (forward-sexp) (point)))) (cons (buffer-substring beg end) beg)))) +(make-obsolete 'imenu-example--name-and-position + "use your own function instead." "23.2") ;;; ;;; Lisp @@ -357,6 +330,7 @@ Don't move point." (end (progn (forward-sexp -1) (point)))) (buffer-substring beg end))) (error nil))))) +(make-obsolete 'imenu-example--lisp-extract-index-name "your own" "23.2") (defun imenu-example--create-lisp-index () ;; Example of a candidate for `imenu-create-index-function'. @@ -408,6 +382,7 @@ Don't move point." (push (cons "Syntax-unknown" index-unknown-alist) index-alist)) index-alist)) +(make-obsolete 'imenu-example--create-lisp-index "your own" "23.2") ;; Regular expression to find C functions (defvar imenu-example--function-name-regexp-c @@ -439,7 +414,7 @@ Don't move point." (push (imenu-example--name-and-position) index-alist)))) (imenu-progress-message prev-pos 100) (nreverse index-alist))) - +(make-obsolete 'imenu-example--create-c-index "your own" "23.2") ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; @@ -453,15 +428,28 @@ Don't move point." ;; 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.") +;;;###autoload(put 'imenu--index-alist 'risky-local-variable t) (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) @@ -500,6 +488,8 @@ A nested sub-alist element looks like (INDEX-NAME SUB-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 '()) @@ -521,10 +511,15 @@ A nested sub-alist element looks like (INDEX-NAME SUB-ALIST).") ;;; 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) @@ -544,6 +539,9 @@ A nested sub-alist element looks like (INDEX-NAME SUB-ALIST).") ;;; 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) @@ -569,19 +567,12 @@ A nested sub-alist element looks like (INDEX-NAME SUB-ALIST).") (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 @@ -604,7 +595,7 @@ as a way for the user to ask to recalculate the buffer's index alist." ;;; Find all markers in alist and makes ;;; them point nowhere. ;;; The top-level call uses nil as the argument; -;;; non-nil arguments are in recursivecalls. +;;; non-nil arguments are in recursive calls. (defvar imenu--cleanup-seen) (defun imenu--cleanup (&optional alist) @@ -679,21 +670,28 @@ and speed-up matching.") (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 imenu-extract-index-name-function) - (let ((index-alist '()) + (let ((index-alist '()) (pos (point)) prev-pos name) (goto-char (point-max)) (imenu-progress-message prev-pos 0 t) ;; Search for the function (while (funcall imenu-prev-index-position-function) + (when (= pos (point)) + (error "Infinite loop at %s:%d: imenu-prev-index-position-function does not move point" (buffer-name) pos)) + (setq pos (point)) (imenu-progress-message prev-pos nil t) (save-excursion (setq name (funcall imenu-extract-index-name-function))) @@ -709,27 +707,6 @@ Their results are gathered into an index alist." (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. ;;; @@ -746,24 +723,35 @@ for modes which use `imenu--generic-function'. If it is not set, but ;; 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) or like this: (MENU-TITLE REGEXP INDEX FUNCTION ARGUMENTS...) -with zero or more ARGUMENTS. - -MENU-TITLE is a string used as the title for the submenu or nil if the -entries are not nested. +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 (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. + +REGEXP is a regexp that should match a construct in the buffer +that is to be displayed in the menu; i.e., function or variable +definitions, etc. It contains a substring which is the name to +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' 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 +menu. -REGEXP is a regexp that should match a construct in the buffer that is -to be displayed in the menu; i.e., function or variable definitions, -etc. It contains a substring which is the name to appear in the -menu. See the info section on Regexps for more information. - -INDEX points to the substring in REGEXP that contains the name (of the -function, variable or type) that is to appear in the menu. +The variable `imenu-case-fold-search' determines whether or not the +regexp matches are case sensitive, and `imenu-syntax-alist' can be +used to alter the syntax table for the search. See `lisp-imenu-generic-expression' for an example of PATTERNS. @@ -777,7 +765,7 @@ They may also be nested index alists like: depending on PATTERNS." (let ((index-alist (list 'dummy)) - prev-pos beg + prev-pos (case-fold-search (if (or (local-variable-p 'imenu-case-fold-search) (not (local-variable-p 'font-lock-defaults))) imenu-case-fold-search @@ -807,17 +795,21 @@ depending on PATTERNS." (index (nth 2 pat)) (function (nth 3 pat)) (rest (nthcdr 4 pat)) - start) + start beg) ;; Go backwards for convenience of adding items in order. (goto-char (point-max)) - (while (and (re-search-backward regexp nil t) + (while (and (if (functionp regexp) + (funcall regexp) + (re-search-backward regexp nil t)) ;; Exit the loop if we get an empty match, ;; because it means a bad regexp was specified. (not (= (match-beginning 0) (match-end 0)))) (setq start (point)) - (goto-char (match-end index)) - (setq beg (match-beginning index)) - (goto-char beg) + ;; Record the start of the line in which the match starts. + ;; That's the official position of this definition. + (goto-char (match-beginning index)) + (beginning-of-line) + (setq beg (point)) (imenu-progress-message prev-pos nil t) ;; Add this sort of submenu only when we've found an ;; item for it, avoiding empty, duff menus. @@ -839,9 +831,8 @@ depending on PATTERNS." (unless (member item (cdr menu)) (setcdr menu (cons item (cdr menu))))) - ;; Move to the start of the entire match, - ;; to ensure we keep moving backwards - ;; as long as the match is nonempty. + ;; Go to the start of the match, to make sure we + ;; keep making progress backwards. (goto-char start)))) (set-syntax-table old-table))) (imenu-progress-message prev-pos 100 t) @@ -884,7 +875,7 @@ Return one of the entries in index-alist or nil." (if (not imenu-space-replacement) index-alist (mapcar (lambda (item) - (cons (subst-char-in-string ?\ (aref imenu-space-replacement 0) + (cons (subst-char-in-string ?\s (aref imenu-space-replacement 0) (car item)) (cdr item))) index-alist)))) @@ -935,9 +926,8 @@ select from ALIST. 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 @@ -973,14 +963,16 @@ See the command `imenu' for more information." imenu-generic-expression (not (eq imenu-create-index-function 'imenu-default-create-index-function))) - (let ((newmap (make-sparse-keymap))) - (set-keymap-parent newmap (current-local-map)) - (setq imenu--last-menubar-index-alist nil) - (define-key newmap [menu-bar index] - `(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))) + (unless (keymapp (lookup-key (current-local-map) [menu-bar index])) + (let ((newmap (make-sparse-keymap))) + (set-keymap-parent newmap (current-local-map)) + (setq imenu--last-menubar-index-alist nil) + (define-key newmap [menu-bar index] + `(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" + (format-mode-line mode-name)))) ;;;###autoload (defun imenu-add-menubar-index () @@ -993,15 +985,15 @@ A trivial interface to `imenu-add-to-menubar' suitable for use in a hook." (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. @@ -1017,6 +1009,9 @@ A trivial interface to `imenu-add-to-menubar' suitable for use in a hook." (car (cdr menu)))) 'imenu--menubar-select)) (setq old (lookup-key (current-local-map) [menu-bar index])) + ;; This should never happen, but in some odd cases, potentially, + ;; lookup-key may return a dynamically composed keymap. + (if (keymapp (cadr old)) (setq old (cadr old))) (setcdr old (cdr menu1))))))) (defun imenu--menubar-select (item) @@ -1074,5 +1069,4 @@ for more information." (provide 'imenu) -;; arch-tag: 98a2f5f5-4b91-4704-b18c-3aacf77d77a7 ;;; imenu.el ends here