;;; imenu+.el --- Extensions to `imenu.el'. ;; ;; Filename: imenu+.el ;; Description: Extensions to `imenu.el'. ;; Author: Drew Adams ;; Maintainer: Drew Adams ;; Copyright (C) 1999-2013, Drew Adams, all rights reserved. ;; Created: Thu Aug 26 16:05:01 1999 ;; Version: 21.0 ;; Last-Updated: Fri Dec 28 10:04:26 2012 (-0800) ;; By: dradams ;; Update #: 886 ;; URL: http://www.emacswiki.org/imenu+.el ;; Doc URL: http://emacswiki.org/ImenuMode ;; Keywords: tools, menus ;; Compatibility: GNU Emacs: 20.x, 21.x, 22.x, 23.x, 24.x ;; ;; Features that might be required by this library: ;; ;; `imenu'. ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;;; Commentary: ;; ;; Extensions to `imenu.el'. ;; ;; User options defined here: ;; ;; `imenup-sort-ignores-case'. ;; ;; Commands defined here: ;; ;; `imenup-add-defs-to-menubar', ;; `imenup-toggle-case-sensitive-sorting', `imenup-toggle-sort', ;; ;; Non-interactive functions defined here: ;; ;; `imenup--sort-submenu', `imenup-invisible-p'. ;; ;; Internal variables defined here: ;; ;; `imenup-emacs-face-defn-regexp', ;; `imenup-emacs-key-defn-regexp-1', ;; `imenup-emacs-key-defn-regexp-2', ;; `imenup-emacs-lisp-generic-expression', ;; `imenup-emacs-option-defn-regexp', `imenup-last-sort-function', ;; `imenup-lisp-fn-defn-regexp', `imenup-lisp-macro-defn-regexp', ;; `imenup-lisp-other-defn-regexp', `imenup-lisp-var-defn-regexp', ;; ;; ;; ***** NOTE: The following functions and macro defined in `imenu.el' ;; have been REDEFINED HERE: ;; ;; `imenu--generic-function', `imenu--make-index-alist', ;; `imenu--mouse-menu', `imenu--sort-by-name', ;; `imenu--split-submenus', `imenu-progress-message', ;; `imenu-update-menubar' (Emacs <22). ;; ;; ;; ***** NOTE: The following variable defined in `imenu.el' has ;; been REDEFINED HERE: ;; ;; `imenu-sort-function'. ;; ;; ***** NOTE: The following variable defined in `lisp-mode.el' has ;; been REDEFINED HERE: ;; ;; `lisp-imenu-generic-expression'. ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;;; Change Log: ;; ;; 2012/10/24 dadams ;; imenup-toggle-case-sensitive-sorting: Improve message in case not currently sorting by name. ;; 2012/10/23 dadams ;; Added defgroup. Added: imenup-toggle-case-sensitive-sorting. ;; Initialize imenup-last-sort-function to imenu--sort-by-name, not nil. ;; Added redefinition of imenu--sort-by-name. ;; Added redefinition of imenu--split-submenus: Fixes Emacs bug #12717. ;; imenu--make-index-alist: ;; Added toggle commands to menu. Removed eval-and-compile - use now for Emacs 20 too. ;; Updated for Emacs 24 (use user-error, not error). ;; Renamed all Imenu+ stuff to use prefix imenup-. ;; imenup-toggle-sort: Simplified, and arg is optional now. ;; imenup--sort-submenu: Use imenu--subalist-p to correctly test a submenu. ;; Require cl.el at compile time for all versions (use case macro now). ;; Removed defaliases defining toggle-imenu*. ;; 2012/03/15 dadams ;; imenu-update-menubar: Applied Emacs 24 bug fix to handle a dynamically composed keymap. ;; Require cl.el for Emacs 20 when byte-compile. ;; 2012/01/01 dadams ;; imenu-update-menubar: buffer-modified-tick -> buffer-chars-modified-tick. (Sync w/ vanilla.) ;; 2011/11/24 dadams ;; Added: imenup-invisible-p. ;; imenu--generic-function: Use imenup-invisible-p, not just get-text-property (so overlays too). ;; 2011/11/23 dadams ;; Make menu ignore invisible text and respect ignore-comments-flag. Added (redefinition of): ;; imenu--make-index-alist, imenu--generic-function, imenu-progress-message. ;; 2001/08/26 dadams ;; imenu--sort-submenu: Copy MENU-ITEMS so sort doesn't modify it. Thx to Michael Heerdegen. ;; 2011/05/27 dadams ;; imenu-lisp-var-defn-regexp: ;; Corrected to allow \n after var name (\n is comment-end syntax, not whitespace, in Lisp). ;; 2011/05/08 dadams ;; imenu-lisp-var-defn-regexp: Try not to create entries for vacuous defvars, e.g., (defvar foo). ;; 2011/03/18 dadams ;; imenu-emacs-key-defn-regexp-[1|2]: Handle (kbd "..."). ;; emacs-lisp-imenu-generic-expression: Increased index from 4 to 5, to fit change for kbd. ;; 2011/01/04 dadams ;; Removed autoload cookies from defvar, non-interactive fns. Added for command. ;; 2007/01/16 dadams ;; imenu-lisp-fn-defn-regexp: Updated for icicle-define-add-to-alist-command. ;; 2005/12/09 dadams ;; imenu-lisp-fn-defn-regexp: Updated to include icicle-define*. ;; Use regexp-opt for Emacs 20 version too. ;; Moved Emacs 22 macro stuff to imenu-lisp-macro-defn-regexp. ;; (emacs-)lisp-imenu-generic-expression: ;; Updated Emacs 20 index to accomodate parens for icicle-define*. ;; Added: imenu-emacs-(face|option)-defn-regexp, ;; Removed: imenu-lisp-struct-defn-regexp. ;; Renamed: imenu-lisp-type-defn-regexp to imenu-lisp-other-defn-regexp. ;; 2005/05/17 dadams ;; Updated to work with Emacs 22.x. ;; 2004/11/21 dadams ;; imenu-lisp-type-defn-regexp, imenu-lisp-fn-defn-regexp, ;; imenu-lisp-var-defn-regexp: Got rid of purecopy & eval-when-compile. ;; 2004/11/20 dadams ;; Refined to deal with Emacs 21 < 21.3.50 (soon to be 22.x) ;; 2004/10/12 dadams ;; Updated for Emacs 21. ;; 2001/01/05 dadams ;; Unquoted mapcar lambda args. ;; 2000/11/01 dadams ;; Put imenu-add-defs-to-menubar inside condition-case, in (*-)lisp-mode-hooks ;; 1999/08/30 dadams ;; 1. imenu-emacs-key-defn-regexp-2: Added define-key-after. ;; 2. Updated emacs-lisp-imenu-generic-expression (Keys in Maps). ;; 1999/08/27 dadams ;; 1. Corrected: imenu-lisp-fn-defn-regexp, imenu-lisp-macro-defn-regexp, ;; imenu-lisp-var-defn-regexp, imenu--sort-submenu, ;; imenu-emacs-key-defn-regexp-2. ;; 2. Added: imenu--sort-submenu, imenu-update-menubar, imenu--mouse-menu. ;; Redefinition of originals: imenu-update-menubar, imenu--mouse-menu. ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; This program 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. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program; see the file COPYING. If not, write to ;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth ;; Floor, Boston, MA 02110-1301, USA. ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;;; Code: (eval-when-compile (require 'cl)) ;; case (require 'imenu) ;; Quiet the byte-compiler (defvar imenu-menubar-modified-tick) ;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;###autoload (defgroup Imenu-Plus nil "Various enhancements to Imenu." :prefix "imenup-" :group 'imenu :link `(url-link :tag "Send Bug Report" ,(concat "mailto:" "drew.adams" "@" "oracle" ".com?subject=\ imenu+.el bug: \ &body=Describe bug here, starting with `emacs -q'. \ Don't forget to mention your Emacs and library versions.")) :link '(url-link :tag "Other Libraries by Drew" "http://www.emacswiki.org/cgi-bin/wiki/DrewsElispLibraries") :link '(url-link :tag "Download" "http://www.emacswiki.org/cgi-bin/wiki/imenu+.el") :link '(url-link :tag "Description" "http://www.emacswiki.org/ImenuMode#ImenuPlus") :link '(emacs-commentary-link :tag "Commentary" "imenu+")) ;;;###autoload (defcustom imenup-sort-ignores-case nil "*Non-nil means that `imenu--sort-by-name' sorts case-insensitively." :type 'boolean :group 'Imenu-Plus) ;; Apologies, but this is the easiest way to override the default value of nil. ;; If you need to override this, comment it out or set it after loading this file. (defconst imenu-sort-function 'imenu--sort-by-name) (defvar imenup-last-sort-function `imenu--sort-by-name "The last non-nil value for `imenu-sort-function' during this session.") ;; For key definitions, we need to handle: strings and vectors, but also (kbd STRING). ;; We just match optional `(kbd ' followed by a string or a vector'. (defvar imenup-emacs-key-defn-regexp-1 "(\\s-*\\(\\(global\\|local\\)-\\(un\\)?\ set-key\\|undefine-keys-bound-to\\)\\s-*\\((kbd\\s-*\\)?\\(\"[^\"]+\"\\|[[][^]]+[]]\\)" "*Regexp that recognizes Emacs key definitions. See also `imenup-emacs-key-defn-regexp-2'.") (defvar imenup-emacs-key-defn-regexp-2 "(\\s-*\\(define-key\\(-after\\)?\\s-+\ \\|substitute-key-definition\\s-+'\\)\\(\\S-+\\)\\s-*'?\\((kbd\\s-*\\)?\\(\"[^\"]+\"\\|[[][^]]+[]]\\)" "*Regexp that recognizes Emacs key definitions. See also `imenup-emacs-key-defn-regexp-1'.") (defvar imenup-lisp-other-defn-regexp (if (>= emacs-major-version 22) (concat "^\\s-*(" (regexp-opt '("defgroup" "deftheme" "deftype" "defstruct" "defclass" "define-condition" "define-widget" "defpackage") t) "\\s-+'?\\(\\sw\\(\\sw\\|\\s_\\)+\\)") "(\\s-*def\\(type\\|class\\|ine-condition\\)\\s-+'?\\([^ \t()]+\\)") "*Regexp that recognizes other Lisp definitions.") (defvar imenup-lisp-fn-defn-regexp (if (>= emacs-major-version 22) (concat "^\\s-*(" (regexp-opt '("defun" "defun*" "defsubst" "defadvice" "define-skeleton" "define-minor-mode" "define-global-minor-mode" "define-globalized-minor-mode" "define-derived-mode" "define-generic-mode" "defsetf" "define-setf-expander" "define-method-combination" "defgeneric" "defmethod" "icicle-define-command" "icicle-define-file-command") t) "\\s-+\\(\\sw\\(\\sw\\|\\s_\\)+\\)") (concat "^\\s-*(" (regexp-opt '("defun" "defun*" "defsubst" "defadvice" "define-skeleton" "define-derived-mode" "defsetf" "icicle-define-add-to-alist-command" "icicle-define-command" "icicle-define-file-command") t) "\\s-+\\(\\sw\\(\\sw\\|\\s_\\)+\\)")) "*Regexp that recognizes Lisp function definitions.") (defvar imenup-lisp-macro-defn-regexp "(\\s-*\\(defmacro\\|define-compiler-macro\\|define-modify-macro\\)\\s-+\\([^ \t()]+\\)" "*Regexp that recognizes Lisp macro definitions.") (defvar imenup-emacs-face-defn-regexp "(\\s-*\\(defface\\)\\s-+\\([^ \t()]+\\)" "*Regexp for Emacs face definitions (defface).") (defvar imenup-emacs-option-defn-regexp "(\\s-*\\(defcustom\\)\\s-+\\([^ \t()]+\\)" "*Regexp for Emacs user option definitions (defcustom).") (defvar imenup-lisp-var-defn-regexp (if (>= emacs-major-version 22) (concat "^\\s-*(" (regexp-opt '("defvar" "defconst" "defconstant" "defcustom" "defparameter" "define-symbol-macro") t) "\\s-+\\(\\sw\\(\\sw\\|\\s_\\)+\\)" ;; Because \n has char syntax `>', not whitespace. See Emacs bug #8638. "\\(\\s-\\|[\n]\\)+" "[^) \t\n]") "(\\s-*def\\(var\\|const\\)\\s-+\\([^ \t()]+\\)") "*Regexp that recognizes global Lisp variable definitions.") ;; REPLACE ORIGINAL in `lisp-mode.el'. ;; ;; Add `Functions', `Macros', `Structures'. ;; (defconst lisp-imenu-generic-expression (list (list "Other" imenup-lisp-other-defn-regexp 2) (list "Macros" imenup-lisp-macro-defn-regexp 2) (list "Functions" imenup-lisp-fn-defn-regexp (if (string-match "\\(?:\\)" "") 2 6)) (list "Variables" imenup-lisp-var-defn-regexp 2) ) "*Imenu generic expression for Lisp mode. See `imenu-generic-expression'.") (defvar imenup-emacs-lisp-generic-expression (list (list "Other" imenup-lisp-other-defn-regexp 2) (list "Keys in Maps" imenup-emacs-key-defn-regexp-2 5) (list "Keys" imenup-emacs-key-defn-regexp-1 5) (list "Macros" imenup-lisp-macro-defn-regexp 2) (list "Functions" imenup-lisp-fn-defn-regexp (if (string-match "\\(?:\\)" "") 2 6)) (list "Variables" imenup-lisp-var-defn-regexp 2) (list "User Options" imenup-emacs-option-defn-regexp 2) (list "Faces" imenup-emacs-face-defn-regexp 2) ) "*Imenu generic expression for Emacs Lisp mode. See `imenu-generic-expression'.") (add-hook 'lisp-mode-hook (lambda () (setq imenu-generic-expression lisp-imenu-generic-expression) (condition-case nil (imenup-add-defs-to-menubar) (error nil)))) (add-hook 'emacs-lisp-mode-hook (lambda () (setq imenu-generic-expression imenup-emacs-lisp-generic-expression) (condition-case nil (imenup-add-defs-to-menubar) (error nil)))) ;;;###autoload (defun imenup-toggle-sort (&optional arg) "Toggle imenu between sorting menus and not. With a prefix ARG, turn on if ARG is non-negative, off if negative. See also command `imenup-toggle-case-sensitive-sorting'." (interactive "P") (if arg (setq imenu-sort-function (and (wholenump (prefix-numeric-value arg)) imenup-last-sort-function)) (if imenu-sort-function (setq imenup-last-sort-function imenu-sort-function ; Save it. imenu-sort-function nil) ; Don't sort. (setq imenu-sort-function imenup-last-sort-function))) (imenu--menubar-select imenu--rescan-item) (case imenu-sort-function (imenu--sort-by-name (message "Menu items now being sorted by name")) ((nil) (message "Menu items are in buffer order (NOT SORTED)")) (otherwise (message "Menu items now being sorted with `%s'" imenu-sort-function)))) ;;;###autoload (defun imenup-toggle-case-sensitive-sorting () "Toggle option `imenup-sort-ignores-case'. This affects menu sorting using `imenu--sort-by-name'." (interactive) (setq imenup-sort-ignores-case (not imenup-sort-ignores-case)) (imenu--menubar-select imenu--rescan-item) (if (eq 'imenu--sort-by-name imenu-sort-function) (message "Sorting menu items by name %s" (if imenup-sort-ignores-case "now ignores case" "is now case SENSITIVE")) (message "NOT sorting by name now, but will %s if you do" (if imenup-sort-ignores-case "IGNORE case" "RESPECT case")))) ;; REPLACE ORIGINAL in `imenu.el'. ;; ;; Respect `imenup-sort-ignores-case' ;; ;;;###autoload (defun imenu--sort-by-name (item1 item2) "Return non-nil if ITEM1 comes before ITEM2 alphabetically. The arguments are menu items, which have form (NAME . POSITION). Their NAMEs are compared. Comparison is case-sensitive if `imenup-sort-ignores-case' is non-nil. You can toggle that option using `\\[imenup-toggle-sort]'." (let ((name1 (car item1)) (name2 (car item2))) (when imenup-sort-ignores-case (setq name1 (upcase name1) name2 (upcase name2))) (string-lessp name1 name2))) ;;;###autoload (defun imenup-add-defs-to-menubar () "Add \"Defs\" imenu entry to menu bar for current local keymap. See `imenu' for more information." (interactive) (imenu-add-to-menubar "Defs")) (defun imenup--sort-submenu (submenu predicate) "Create an imenu SUBMENU, sorting with PREDICATE." (let ((menu-name (car submenu)) (menu-items (cdr submenu))) (cons menu-name (if (imenu--subalist-p submenu) ;; Must copy, because MENU-ITEMS can be part of `imenu--index-alist'. (sort (copy-sequence menu-items) predicate) menu-items)))) ;; REPLACE ORIGINAL in `imenu.el'. ;; ;; Correctly handle special menu items (distinguish from submenus). Fixes Emacs bug #12717. ;; (defun imenu--split-submenus (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." (mapcar (lambda (elt) (if (imenu--subalist-p elt) (imenu--split-menu (cdr elt) (car elt)) elt)) alist)) ;; REPLACE ORIGINAL in `imenu.el'. ;; ;; Sort each submenu before splitting submenus, in addition to sorting among submenus. ;; (defun imenu-update-menubar () "Update the Imenu menu. Use as `menu-bar-update-hook'." (when (and (current-local-map) (keymapp (lookup-key (current-local-map) [menu-bar index])) (or (not (boundp 'imenu-menubar-modified-tick)) (/= (buffer-chars-modified-tick) imenu-menubar-modified-tick))) ; Emacs 22+ (when (boundp 'imenu-menubar-modified-tick) ; Emacs 22+ (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. (unless (equal index-alist imenu--last-menubar-index-alist) (let (menu menu1 old) (setq imenu--last-menubar-index-alist index-alist index-alist (imenu--split-submenus (if imenu-sort-function (mapcar (lambda (sm) (imenup--sort-submenu sm imenu-sort-function)) index-alist) index-alist)) menu (imenu--split-menu index-alist (buffer-name))) (if (>= emacs-major-version 22) (setq menu1 (imenu--create-keymap (car menu) (cdr (if (< 1 (length (cdr menu))) menu (car (cdr menu)))) 'imenu--menubar-select)) (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])) ;; Next line was added in vanilla Emacs 24, with the comment. ;; This should never happen, but in some odd cases, potentially, ;; lookup-key may return a dynamically composed keymap. (when (keymapp (cadr old)) (setq old (cadr old))) (setcdr old (cdr menu1))))))) (eval-and-compile (when (< emacs-major-version 22) ;; REPLACE ORIGINAL in `imenu.el'. ;; ;; Use Emacs 22+ definition, which is vacuous. Otherwise, if byte-compile in Emacs < 22 and use ;; the byte-compiled file in Emacs 22+, then get runtime error: ;; `Error in menu-bar-update-hook: (void-variable imenu-scanning-message)'. ;; (defmacro imenu-progress-message (prevpos &optional relpos reverse) ))) ;; REPLACE ORIGINAL in `imenu.el'. ;; ;; 1. Respect `ignore-comments-flag', if defined: use `with-comments-hidden'. ;; 2. Add Imenu+ toggle commands to menu. ;; (defun imenu--make-index-alist (&optional noerror) "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. If `ignore-comments-flag' is defined and non-nil, then respect it, ignoring hidden comments. 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 (> (buffer-size) imenu-auto-rescan-maxout)))) ;; Get the index; truncate if necessary (progn (setq imenu--index-alist (save-excursion (save-restriction (widen) (if (and (> emacs-major-version 20) (require 'hide-comnt nil t)) (let ((search-invisible nil)) (with-comments-hidden (point-min) (point-max) (funcall imenu-create-index-function))) (funcall imenu-create-index-function))))) (imenu--truncate-items imenu--index-alist))) (or imenu--index-alist noerror (if (fboundp 'user-error) (user-error "No items suitable for an index found in this buffer") (error "No items suitable for an index found in this buffer"))) (or imenu--index-alist (setq imenu--index-alist (list nil))) (cons imenu--rescan-item ; `*Rescan*'. (cons '("Toggle Case-Sensitive Name-Sort" IGNORE (lambda (&rest _ignore) (imenup-toggle-case-sensitive-sorting))) (cons '("Toggle Sorting" IGNORE (lambda (&rest _ignore) (imenup-toggle-sort))) imenu--index-alist)))) ;; Same as `thgcmd-invisible-p' in `thing-cmds.el', and `icicle-invisible-p' in `icicles-cmd2.el'. (defun imenup-invisible-p (position) "Return non-nil if the character at POSITION is invisible." (if (fboundp 'invisible-p) ; Emacs 22+ (invisible-p position) (let ((prop (get-char-property position 'invisible))) ; Overlay or text property. (if (eq buffer-invisibility-spec t) prop (or (memq prop buffer-invisibility-spec) (assq prop buffer-invisibility-spec)))))) ;; REPLACE ORIGINAL in `imenu.el'. ;; ;; Ignore invisible definitions. ;; (defun imenu--generic-function (patterns) "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) 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 (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. 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. Returns an index of the current buffer as an alist. The elements in the alist look like: (INDEX-NAME . INDEX-POSITION) or like: (INDEX-NAME INDEX-POSITION FUNCTION ARGUMENTS...) They may also be nested index alists like: (INDEX-NAME . INDEX-ALIST) depending on PATTERNS." (let ((index-alist (list 'dummy)) (case-fold-search (if (or (local-variable-p 'imenu-case-fold-search) (not (local-variable-p 'font-lock-defaults))) imenu-case-fold-search (nth 2 font-lock-defaults))) (old-table (syntax-table)) (table (copy-syntax-table (syntax-table))) (slist imenu-syntax-alist) prev-pos) (dolist (syn slist) ; Modify the syntax table used while matching regexps. (if (numberp (car syn)) ; The char(s) to modify may be a single char or a string. (modify-syntax-entry (car syn) (cdr syn) table) (mapc (lambda (c) (modify-syntax-entry c (cdr syn) table)) (car syn)))) (goto-char (point-max)) (imenu-progress-message prev-pos 0 t) (unwind-protect ; for syntax table (save-match-data (set-syntax-table table) (dolist (pat patterns) ; Map over the elements of `imenu-generic-expression'. (let ((menu-title (car pat)) (regexp (nth 1 pat)) (index (nth 2 pat)) (function (nth 3 pat)) (rest (nthcdr 4 pat)) start beg) (goto-char (point-max)) ; Go backwards for convenience of adding items in order. (while (and (if (functionp regexp) (funcall regexp) (and (re-search-backward regexp nil t) ;; Do not count invisible definitions. (let ((invis (imenup-invisible-p (point)))) (or (not invis) (progn (while (and invis (not (bobp))) (setq invis (not (re-search-backward regexp nil 'MOVE)))) (not invis)))))) ;; Exit loop if empty match -it means a bad regexp was specified. (not (= (match-beginning 0) (match-end 0)))) (setq start (point)) ;; 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 find an item for it, to avoid empty menus. (unless (assoc menu-title index-alist) (push (list menu-title) index-alist)) (when imenu-use-markers (setq beg (copy-marker beg))) (let ((item (if function (nconc (list (match-string-no-properties index) beg function) rest) (cons (match-string-no-properties index) beg))) ;; This is the desired submenu, starting with its title (or nil). (menu (assoc menu-title index-alist))) ;; Insert the item unless it is already present. (unless (member item (cdr menu)) (setcdr menu (cons item (cdr menu))))) ;; 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) ;; Sort each submenu by position. ;; This is in case one submenu gets items from two different regexps. (dolist (item index-alist) (when (listp item) (setcdr item (sort (cdr item) 'imenu--sort-by-position)))) (let ((main-element (assq nil index-alist))) (nconc (delq main-element (delq 'dummy index-alist)) (cdr main-element))))) ;; REPLACE ORIGINAL in `imenu.el'. ;; ;; Sort each submenu before splitting submenus, instead of sorting among submenus after. ;; (defun imenu--mouse-menu (index-alist event &optional title) "Let the user select from a buffer index from a mouse menu. INDEX-ALIST is the buffer index. EVENT is a mouse event. TITLE is the menu title. Returns t for rescan, or else an element or subelement of INDEX-ALIST." (setq index-alist (imenu--split-submenus (if imenu-sort-function (mapcar (lambda (sm) (imenup--sort-submenu sm imenu-sort-function)) index-alist) index-alist))) (if (>= emacs-major-version 22) (let* ((menu (imenu--split-menu index-alist (or title (buffer-name)))) (map (imenu--create-keymap (car menu) (cdr (if (< 1 (length (cdr menu))) menu (car (cdr menu))))))) (popup-menu map event)) (let ((menu (imenu--split-menu index-alist (or title (buffer-name)))) position) (setq menu (imenu--create-keymap-1 (car menu) (if (< 1 (length (cdr menu))) (cdr menu) (cdr (cadr menu)))) position (x-popup-menu event menu)) (cond ((eq position nil) position) ;; If one call to x-popup-menu handled the nested menus, find the result by looking down ;; the menus here. ((and (listp position) (numberp (car position)) (stringp (nth (1- (length position)) position))) (let ((final menu)) (while position (setq final (assq (car position) final) position (cdr position))) (or (string= (car final) (car imenu--rescan-item)) (nthcdr 3 final)))) ;; If x-popup-menu went just one level and found a leaf item, return the INDEX-ALIST ;; element for that. ((and (consp position) (stringp (car position)) (null (cdr position))) (or (string= (car position) (car imenu--rescan-item)) (assq (car position) index-alist))) ;; If x-popup-menu went just one level and found a non-leaf item (a submenu), ;; recurse to handle the rest. ((listp position) (imenu--mouse-menu position event (if title (concat title imenu-level-separator (car (rassq position index-alist))) (car (rassq position index-alist))))))))) ;;;;;;;;;;;;;;;;;; (provide 'imenu+) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; imenu+.el ends here