]> code.delx.au - gnu-emacs/blobdiff - lisp/imenu.el
* doc/emacs/dired.texi (Shell Commands in Dired): Fix typo.
[gnu-emacs] / lisp / imenu.el
index af617498b078d6a7703d57affffd535531c1a360..8363956355b863396470b39803309730f5e61946 100644 (file)
@@ -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 <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
@@ -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 <http://www.gnu.org/licenses/>.
 
 ;;; 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