(rmail-retry-failure): Bind inhibit-read-only.
[bpt/emacs.git] / lisp / imenu.el
index 3fedade..1b85879 100644 (file)
@@ -1,11 +1,10 @@
 ;;; imenu.el --- Framework for mode-specific buffer indexes.
 
-;; Copyright (C) 1994 Free Software Foundation, Inc.
+;; Copyright (C) 1994, 1995 Free Software Foundation, Inc.
 
 ;; Author: Ake Stenhoff <etxaksf@aom.ericsson.se>
 ;;         Lars Lindberg <lli@sypro.cap.se>
 ;; Created: 8 Feb 1994
-;; Version: 1.17
 ;; Keywords: tools
 ;;
 ;; This program is free software; you can redistribute it and/or modify
 ;;;
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 (defvar imenu-use-keymap-menu nil
-  "* Set this to non-nil for using a keymap when making 
-  the mouse menu.")
+  "*Non-nil means use a keymap when making the mouse menu.")
 
 (defvar imenu-auto-rescan nil
-  "* T if we always should rescan the buffers, nil to disable
-  automatic rescan.")
+  "*Non-nil means Imenu should always rescan the buffers.")
 
 (defvar imenu-auto-rescan-maxout 60000 
   "* auto-rescan is disabled in buffers larger than this.
-  This variable is buffer-local.")
+This variable is buffer-local.")
 
 (defvar imenu-always-use-completion-buffer-p nil
   "*Set this to non-nil for displaying the index in a completion buffer.
@@ -99,7 +96,7 @@ element should come before the second.  The arguments are cons cells;
 (defvar imenu-max-items 25
   "*Maximum number of elements in an index mouse-menu.")
 
-(defvar imenu-scanning-message "Scanning buffer for index. (%3d%%)"
+(defvar imenu-scanning-message "Scanning buffer for index (%3d%%)"
   "*Progress message during the index scanning of the buffer.
 If non-nil, user gets a message during the scanning of the buffer
 
@@ -133,26 +130,22 @@ 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.
+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.
 
 For emacs-lisp-mode for example PATTERN would look like:
 
-'((nil \"^\\s-*(def\\(un\\|subst\\|macro\\|advice\\)\\s-+\\([-A-Za-z0-9+]+\\)\" 2)
-  (\"*Vars*\" \"^\\s-*(def\\(var\\|const\\)\\s-+\\([-A-Za-z0-9+]+\\)\" 2)
-  (\"*Types*\" \"^\\s-*(def\\(type\\|struct\\|class\\|ine-condition\\)\\s-+\\([-A-Za-z0-9+]+\\)\" 2))
+'((nil \"^\\\\s-*(def\\\\(un\\\\|subst\\\\|macro\\\\|advice\\\\)\\\\s-+\\\\([-A-Za-z0-9+]+\\\\)\" 2)
+  (\"*Vars*\" \"^\\\\s-*(def\\\\(var\\\\|const\\\\)\\\\s-+\\\\([-A-Za-z0-9+]+\\\\)\" 2)
+  (\"*Types*\" \"^\\\\s-*(def\\\\(type\\\\|struct\\\\|class\\\\|ine-condition\\\\)\\\\s-+\\\\([-A-Za-z0-9+]+\\\\)\" 2))
 
 The variable is buffer-local.")
 
-;;;###autoload
-(make-variable-buffer-local 'imenu-create-index-pattern)
-
-;; make sure the default is nil
-(setq-default imenu-create-index-pattern nil)
+(make-variable-buffer-local 'imenu-generic-expression)
 
 ;;;; Hooks
 
@@ -160,8 +153,8 @@ The variable is buffer-local.")
   "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 elements in the alist look
-like: (INDEX-NAME . INDEX-POSITION). You may also nest index list like
+of the current buffer as an alist.  The elements in the alist look
+like: (INDEX-NAME . INDEX-POSITION).  You may also nest index list like
 \(INDEX-NAME . INDEX-ALIST).
 
 This function is called within a `save-excursion'.
@@ -178,7 +171,7 @@ to a function that will find the next index, looking backwards in the
 file.
 
 The function should leave point at the place to be connected to the
-index and it should return nil when it doesn't find another index. ")
+index and it should return nil when it doesn't find another index.")
 (make-variable-buffer-local 'imenu-prev-index-position-function)
 
 (defvar imenu-extract-index-name-function nil
@@ -214,7 +207,7 @@ This function is called after the function pointed out by
 ;;;;
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
-;; Return the current/previous sexp and the location of the sexp (it's
+;; Return the current/previous sexp and the location of the sexp (its
 ;; beginning) without moving the point.
 (defun imenu-example--name-and-position ()
   (save-excursion
@@ -295,134 +288,6 @@ This function is called after the function pointed out by
               index-alist))
     index-alist))
 
-(defvar imenu-generic-lisp-expression
-      '(
-       (nil 
-        "^\\s-*(def\\(un\\|subst\\|macro\\|advice\\)\\s-+\\([-A-Za-z0-9+]+\\)" 2)
-       ("Variables" 
-        "^\\s-*(def\\(var\\|const\\)\\s-+\\([-A-Za-z0-9+]+\\)" 2)
-       ("Types" 
-        "^\\s-*(def\\(type\\|struct\\|class\\|ine-condition\\)\\s-+\\([-A-Za-z0-9+]+\\)" 
-        2))
-
-  "imenu generic expression for Lisp mode in the form
-(PATTERN), where PATTERN is a list containing entries of the form 
-(MENU-TITLE REGEXP INDEX). See `imenu-generic-expression'.")
-
-;;;
-;;; C++
-;;;
-;; Example of an imenu-generic-expression
-;;
-(defvar imenu-generic-c++-expression
-  (` 
-   ((nil
-     (, 
-      (concat
-       "^"                               ; beginning of line is required
-       "\\(template[ \t]*<[^>]+>[ \t]*\\)?" ; there may be a "template <...>"
-       "\\([a-zA-Z0-9_:]+[ \t]+\\)?"     ; type specs; there can be no
-       "\\([a-zA-Z0-9_:]+[ \t]+\\)?"     ; more than 3 tokens, right?
-       
-       "\\("                             ; last type spec including */&
-       "[a-zA-Z0-9_:]+"
-       "\\([ \t]*[*&]+[ \t]*\\|[ \t]+\\)"        ; either pointer/ref sign or whitespace
-       "\\)?"                            ; if there is a last type spec
-       "\\("                         ; name; take that into the imenu entry
-       "[a-zA-Z0-9_:~]+"                     ; member function, ctor or dtor...
-                                       ; (may not contain * because then 
-                                       ; "a::operator char*" would become "char*"!)
-       "\\|"
-       "\\([a-zA-Z0-9_:~]*::\\)?operator"
-       "[^a-zA-Z1-9_][^(]*"          ; ...or operator
-       " \\)"
-       "[ \t]*([^)]*)[ \t\n]*[^              ;]" ; require something other than a ; after
-                                       ; the (...) to avoid prototypes. Can't
-                                       ; catch cases with () inside the parentheses
-                                       ; surrounding the parameters
-                                       ; (like "int foo(int a=bar()) {...}"
-       
-       )) 6)    
-    ("Class" 
-     (, (concat 
-        "^"                               ; beginning of line is required
-        "\\(template[ \t]*<[^>]+>[ \t]*\\)?" ; there may be a "template <...>"
-        "class[ \t]+"
-        "\\([a-zA-Z0-9_]+\\)"                ; this is the string we want to get
-        "[ \t]*[:{]"
-        )) 2)
-;; Example of generic expression for finding prototypes, structs, unions, enums.
-;; Uncomment if You want to find these too. It will be at bit slower gathering
-;; the indexes.
-;    ("Prototypes"
-;     (, 
-;      (concat
-;       "^"                              ; beginning of line is required
-;       "\\(template[ \t]*<[^>]+>[ \t]*\\)?" ; there may be a "template <...>"
-;       "\\([a-zA-Z0-9_:]+[ \t]+\\)?"    ; type specs; there can be no
-;       "\\([a-zA-Z0-9_:]+[ \t]+\\)?"    ; more than 3 tokens, right?
-       
-;       "\\("                            ; last type spec including */&
-;       "[a-zA-Z0-9_:]+"
-;       "\\([ \t]*[*&]+[ \t]*\\|[ \t]+\\)"       ; either pointer/ref sign or whitespace
-;       "\\)?"                           ; if there is a last type spec
-;       "\\("                        ; name; take that into the imenu entry
-;       "[a-zA-Z0-9_:~]+"                    ; member function, ctor or dtor...
-;                                      ; (may not contain * because then 
-;                                      ; "a::operator char*" would become "char*"!)
-;       "\\|"
-;       "\\([a-zA-Z0-9_:~]*::\\)?operator"
-;       "[^a-zA-Z1-9_][^(]*"         ; ...or operator
-;       " \\)"
-;       "[ \t]*([^)]*)[ \t\n]*;"       ; require ';' after
-;                                      ; the (...) Can't
-;                                      ; catch cases with () inside the parentheses
-;                                      ; surrounding the parameters
-;                                      ; (like "int foo(int a=bar());"       
-;       )) 6)    
-;    ("Struct"
-;     (, (concat
-;       "^"                            ; beginning of line is required
-;       "\\(static[ \t]+\\)?"          ; there may be static or const.
-;       "\\(const[ \t]+\\)?"
-;       "struct[ \t]+"
-;       "\\([a-zA-Z0-9_]+\\)"          ; this is the string we want to get
-;       "[ \t]*[{]"
-;       )) 3)
-;    ("Enum"
-;     (, (concat
-;       "^"                            ; beginning of line is required
-;       "\\(static[ \t]+\\)?"          ; there may be static or const.
-;       "\\(const[ \t]+\\)?"
-;       "enum[ \t]+"
-;       "\\([a-zA-Z0-9_]+\\)"          ; this is the string we want to get
-;       "[ \t]*[{]"
-;       )) 3)
-;    ("Union"
-;     (, (concat
-;       "^"                            ; beginning of line is required
-;       "\\(static[ \t]+\\)?"          ; there may be static or const.
-;       "\\(const[ \t]+\\)?"
-;       "union[ \t]+"
-;       "\\([a-zA-Z0-9_]+\\)"          ; this is the string we want to get
-;       "[ \t]*[{]"
-;       )) 3)
-    ))
-  "imenu generic expression for C++ mode in the form
-(PATTERN), where PATTERN is a list containing entries of the form 
-(MENU-TITLE REGEXP INDEX). See `imenu-generic-expression'.")
-
-;;;
-;;; C
-;;;
-;;;
-(defvar imenu-generic-c-expression 
-  ;; Use the C++ expression above.
-  imenu-generic-c++-expression
-  "imenu generic expression for C mode in the form
-(PATTERN), where PATTERN is a list containing entries of the form 
-(MENU-TITLE REGEXP INDEX). See `imenu-generic-expression'.")
-
 ;; Regular expression to find C functions
 (defvar imenu-example--function-name-regexp-c
   (concat 
@@ -455,57 +320,6 @@ This function is called after the function pointed out by
     (nreverse index-alist)))
 
 
-;; 
-;; Ada
-;; 
-;; Written by Christian Egli <Christian.Egli@hcsd.hac.com>
-;;
-(defvar imenu-generic-ada-expression
-      '((nil "^\\s-*\\(procedure\\|function\\)\\s-+\\([A-Za-z0-9_]+\\)" 2)
-       ("Type Defs" "^\\s-*\\(sub\\)?type\\s-+\\([A-Za-z0-9_]+\\)" 2))
-
-  "imenu generic expression for Ada mode in the form
-(PATTERN), where PATTERN is a list containing entries of the form 
-(MENU-TITLE REGEXP INDEX). See `imenu-generic-expression'.")
-
-;;; 
-;;; TexInfo
-;;; 
-;; Written by Wolfgang Bangerth <zcg51122@rpool1.rus.uni-stuttgart.de>
-;;
-;;
-(defvar imenu-generic-texinfo-expression
-  '((nil "^@node[ \t]+\\([^,\n]*\\)" 1)
-    ("Chapters" "^@chapter[ \t]+\\(.*\\)$" 1))
-
-  "imenu generic expression for TexInfo mode in the form
-(PATTERN), where PATTERN is a list containing entries of the form 
-(MENU-TITLE REGEXP INDEX). See `imenu-generic-expression'.
-
-To overide this example, Either set 'imenu-generic-expression
-or 'imenu-create-index-function")
-
-;;; 
-;;; LaTex
-;;; 
-;; Written by Wolfgang Bangerth <zcg51122@rpool1.rus.uni-stuttgart.de>
-;;
-;;
-(defvar imenu-generic-latex-expression
-  '(
-    ("Part" "\\\\part{\\([^}]*\\)}" 1)
-    ("Chapter" "\\\\chapter{\\([^}]*\\)}" 1)
-    ("Section" "\\\\[a-zA-Z]*section{\\([^}]*\\)}" 1)
-    ;; i put numbers like 3.15 before my
-    ;; \begin{equation}'s which tell me
-    ;; the number the equation will get when
-    ;; being printed.
-    ("Equations" "%[ \t]*\\([0-9]+\\.[0-9]+\\)[,;]?[ \t]?" 1))  
-
-  "imenu generic expression for LaTex mode in the form
-(PATTERN), where PATTERN is a list containing entries of the form 
-(MENU-TITLE REGEXP INDEX). See `imenu-generic-expression'.")
-
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;;;
 ;;; Internal variables
@@ -520,27 +334,13 @@ or 'imenu-create-index-function")
 (defvar imenu--index-alist nil)
 (make-variable-buffer-local 'imenu--index-alist)
 
+;; The latest buffer index used to update the menu bar menu.
+(defvar imenu--last-menubar-index-alist nil)
+(make-variable-buffer-local 'imenu--last-menubar-index-alist)
+
 ;; History list for 'jump-to-function-in-buffer'.
-;; Buffer local.
+;; Making this buffer local caused it not to work!
 (defvar imenu--history-list nil)
-(make-variable-buffer-local 'imenu--history-list)
-
-(defvar imenu--scanning-method-alist
-  '((emacs-lisp-mode  imenu-generic-lisp-expression)
-    (lisp-mode        imenu-example--create-lisp-index)
-    (c++-mode         imenu-generic-c++-expression)
-    (c-mode           imenu-generic-c-expression)
-    (latex-mode       imenu-generic-latex-expression)
-    (texinfo-mode     imenu-generic-texinfo-expression)
-    (ada-mode         imenu-generic-ada-expression))
-
-  "Alist of major mode and imenu scanning methods.  
-
-Each item should be a list of the form: (MAJOR-MODE
-IMENU-SCANNING-METHOD) where both MAJOR-MODE and IMENU-SCANNING-METHOD
-are symbols. If IMENU-SCANNING-METHOD is a function then it is called
-to create an index. If it is a `pattern' (See `imenu-generic-expression') 
-it is passed to imenu--generic-function to create an index.")
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;;;
@@ -614,7 +414,7 @@ it is passed to imenu--generic-function to create an index.")
 ;;; ((NAME . POSITION) (NAME . POSITION) ...)
 ;;;
 
-(defun imenu--make-index-alist ()
+(defun imenu--make-index-alist (&optional noerror)
   ;; Create a list for this buffer only when needed.
   (or (and imenu--index-alist
           (or (not imenu-auto-rescan)
@@ -624,8 +424,10 @@ it is passed to imenu--generic-function to create an index.")
       (setq imenu--index-alist
            (save-excursion
              (funcall imenu-create-index-function))))
+  (or imenu--index-alist noerror
+      (error "No items suitable for an index found in this buffer"))
   (or imenu--index-alist
-      (error "No items suitable for an index found in this buffer."))
+      (setq imenu--index-alist (list nil)))
   ;; Add a rescan option to the index.
   (cons imenu--rescan-item imenu--index-alist))
 ;;;
@@ -650,7 +452,7 @@ it is passed to imenu--generic-function to create an index.")
        alist)
        t))
 
-(defun imenu--create-keymap-2 (alist counter)
+(defun imenu--create-keymap-2 (alist counter &optional commands)
   (let ((map nil))
     (mapcar
      (function
@@ -658,16 +460,21 @@ it is passed to imenu--generic-function to create an index.")
        (cond
         ((listp (cdr item))
          (append (list (incf counter) (car item) 'keymap (car item))
-                 (imenu--create-keymap-2 (cdr item) (+ counter 10))))
+                 (imenu--create-keymap-2 (cdr item) (+ counter 10) commands)))
         (t
-         (let ((end (cons '(nil) t)))
+         (let ((end (if commands (list 'lambda 'nil '(interactive)
+                                       (list 'imenu--menubar-select item))
+                      (cons '(nil) t))))
            (cons (car item)
                  (cons (car item) end))))
         )))
      alist)))
 
-(defun imenu--create-keymap-1 (title alist)
-  (append (list 'keymap title) (imenu--create-keymap-2 alist 0)))
+;; If COMMANDS is non-nil, make a real keymap
+;; with a real command used as the definition.
+;; If it is nil, make something suitable for x-popup-menu.
+(defun imenu--create-keymap-1 (title alist &optional commands)
+  (append (list 'keymap title) (imenu--create-keymap-2 alist 0 commands)))
 
 
 (defun imenu--in-alist (str alist)
@@ -711,16 +518,7 @@ Their results are gathered into an index alist."
           index-alist))
        ;; Use generic expression if possible.
        ((and imenu-generic-expression)
-        (imenu--generic-function imenu-generic-expression))
-       ;; Use supplied example functions or expressions
-       ((assq major-mode imenu--scanning-method-alist)
-        (let ((method (cadr (assq major-mode imenu--scanning-method-alist))))
-          ;; is it a function?
-          (if (fboundp method)
-              ;; ... then call it
-              (funcall method) 
-            ;; ...otherwise pass the pattern to imenu--generic-function
-            (imenu--generic-function (eval method))))) 
+        (imenu--generic-function imenu-generic-expression)) 
        (t
         (error "The mode \"%s\" does not take full advantage of imenu.el yet."
                mode-name))))      
@@ -768,27 +566,27 @@ Their results are gathered into an index alist."
   "Return an index of the current buffer as an alist.
 
 PATTERN is an alist with elements that look like this: (MENU-TITLE
-REGEXP INDEX). 
+REGEXP INDEX).
 
 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.
+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.
 
 For emacs-lisp-mode for example PATTERN would look like:
 
-'((nil \"^\\s-*(def\\(un\\|subst\\|macro\\|advice\\)\\s-+\\([-A-Za-z0-9]+\\)\" 2)
-  (\"*Vars*\" \"^\\s-*(def\\(var\\|const\\)\\s-+\\([-A-Za-z0-9]+\\)\" 2)
-  (\"*Types*\" \"^\\s-*(def\\(type\\|struct\\|class\\|ine-condition\\)\\s-+\\([-A-Za-z0-9]+\\)\" 2))'
+'((nil \"^\\\\s-*(def\\\\(un\\\\|subst\\\\|macro\\\\|advice\\\\)\\\\s-+\\\\([-A-Za-z0-9]+\\\\)\" 2)
+  (\"*Vars*\" \"^\\\\s-*(def\\\\(var\\\\|const\\\\)\\\\s-+\\\\([-A-Za-z0-9]+\\\\)\" 2)
+  (\"*Types*\" \"^\\\\s-*(def\\\\(type\\\\|struct\\\\|class\\\\|ine-condition\\\\)\\\\s-+\\\\([-A-Za-z0-9]+\\\\)\" 2))'
 
-Returns an index of the current buffer as an alist. The elements in
-the alist look like: (INDEX-NAME . INDEX-POSITION). They may also be
+Returns an index of the current buffer as an alist.  The elements in
+the alist look like: (INDEX-NAME . INDEX-POSITION).  They may also be
 nested index lists like (INDEX-NAME . INDEX-ALIST) depending on
 pattern.
 
@@ -966,7 +764,7 @@ The returned value is on the form (INDEX-NAME . INDEX-POSITION)."
     (and mouse-triggered
         (not (equal last-nonmenu-event '(menu-bar)))
         (let ((window (posn-window (event-start last-nonmenu-event))))
-          (or (framep window) (select-window window))))
+          (or (framep window) (null window) (select-window window))))
     ;; Create a list for this buffer only when needed.
     (while (eq result t)
       (setq index-alist (if alist alist (imenu--make-index-alist)))
@@ -982,39 +780,75 @@ The returned value is on the form (INDEX-NAME . INDEX-POSITION)."
 
 ;;;###autoload
 (defun imenu-add-to-menubar (name)
-  "Adds an \"imenu\" entry to the menubar for the 
-current local keymap.
-NAME is the string naming the menu to be added.
-See 'imenu' for more information."
-  (interactive "sMenu name: ")
-  (and window-system
-       (define-key (current-local-map) [menu-bar index]
-        (cons name 'imenu))))
+  "Adds an \"imenu\" entry to the menu bar for the current major mode.
+NAME is a string used to name the menu bar item.
+See `imenu' for more information."
+  (interactive "sImenu menu item name: ")
+  (define-key (current-local-map) [menu-bar index]
+    (cons name (nconc (make-sparse-keymap "Imenu") (make-sparse-keymap))))
+  (add-hook 'menu-bar-update-hook 'imenu-update-menubar))
+
+(defun imenu-update-menubar ()
+  (and (current-local-map)
+       (keymapp (lookup-key (current-local-map) [menu-bar index]))
+       (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.
+        (or (equal index-alist imenu--last-menubar-index-alist)
+            (let (menu menu1 old)
+              (setq imenu--last-menubar-index-alist index-alist)
+              (setq menu (imenu--split-menu
+                          (if imenu-sort-function
+                              (sort
+                               (let ((res nil)
+                                     (oldlist index-alist))
+                                 ;; Copy list method from the cl package `copy-list'
+                                 (while (consp oldlist) (push (pop oldlist) res))
+                                 (prog1 (nreverse res) (setcdr res oldlist)))
+                               imenu-sort-function)
+                            index-alist)
+                          (buffer-name)))
+              (setq menu1 (imenu--create-keymap-1 (car menu) 
+                                                  (if (< 1 (length (cdr menu)))
+                                                      (cdr menu)
+                                                    (cdr (car (cdr menu))))
+                                                  t))
+              (setq old (lookup-key (current-local-map) [menu-bar index]))
+              (if (keymapp old)
+                  (setcdr (nthcdr 2 old) menu1)))))))
+
+(defun imenu--menubar-select (item)
+  "Use Imenu to select the function or variable named in this menu item."
+  (interactive)
+  (imenu item))
 
 ;;;###autoload
-(defun imenu ()
+(defun imenu (index-item)
   "Jump to a place in the buffer chosen using a buffer menu or mouse menu.
 See `imenu-choose-buffer-index' for more information."
-  (interactive)
-  (let ((index-item (save-restriction 
-                     (widen)
-                     (imenu-choose-buffer-index))))
-    (and index-item
-        (progn
-          (push-mark)
-          (cond
-           ((markerp (cdr index-item))
-            (if (or ( > (marker-position (cdr index-item)) (point-min))
-                    ( < (marker-position (cdr index-item)) (point-max)))
-                ;; widen if outside narrowing
-                (widen))
-            (goto-char (marker-position (cdr index-item))))
-           (t
-            (if (or ( > (cdr index-item) (point-min))
-                    ( < (cdr index-item) (point-max)))
-                ;; widen if outside narrowing
-                (widen))
-            (goto-char (cdr index-item))))))))
+  (interactive
+   (list (save-restriction 
+          (widen)
+          (car (imenu-choose-buffer-index)))))
+  ;; Convert a string to an alist element.
+  (if (stringp index-item)
+      (setq index-item (assoc index-item (imenu--make-index-alist))))
+  (and index-item
+       (progn
+        (push-mark)
+        (cond
+         ((markerp (cdr index-item))
+          (if (or ( > (marker-position (cdr index-item)) (point-min))
+                  ( < (marker-position (cdr index-item)) (point-max)))
+              ;; widen if outside narrowing
+              (widen))
+          (goto-char (marker-position (cdr index-item))))
+         (t
+          (if (or ( > (cdr index-item) (point-min))
+                  ( < (cdr index-item) (point-max)))
+              ;; widen if outside narrowing
+              (widen))
+          (goto-char (cdr index-item)))))))
 
 (provide 'imenu)