--- imenu-20.6.el Tue May 23 01:30:27 2000 +++ imenu-20.6+ydi.el Tue May 23 23:38:27 2000 @@ -33,7 +33,7 @@ ;; For instance all functions in a C-file and their positions. ;; ;; It is documented in the Emacs Lisp manual. -;; + ;; How it works: ;; A mode-specific function is called to generate the index. It is @@ -47,6 +47,71 @@ ;; customize for other modes. A function for jumping to the chosen ;; index position is also supplied. +;; How to setup for a new mode: + +;; There are several options. I'll try to present a simple decision +;; algorithm, that will help people add support for imenu without having +;; to read the whole imenu.el code. +;; +;; * Basic rules: +;; +;; IF you have regexps to find all entries +;; THEN +;; make sure `imenu-create-index-function' is still set to its +;; default of `imenu-default-create-index-function' +;; make sure that `imenu-prev-index-position-function' and +;; `imenu-extract-index-name-function' are not set +;; maybe set `imenu-syntax-alist' to simplify the next step +;; set `imenu-generic-expression' to your regexp +;; ELSE IF you can write a function which can iteratively find +;; entries by starting from the end of the file +;; THEN +;; set `imenu-prev-index-position-function' and +;; `imenu-extract-index-name-function' (see their respective +;; docstrings for more details) +;; ELSE +;; you'll need to set `imenu-create-index-function' to your own function +;; ENDIF +;; +;; +;; * Additional rules for specific needs: +;; +;; IF you can build parts of your index using regexps +;; THEN +;; you can write a `imenu-create-index-function' function for the +;; remaining entries, and refernce it from `imenu-generic-expression' +;; ENDIF +;; +;; IF you want your index to be contain entries that require another +;; behaviour than jumping to a position (eg. opening an included file) +;; THEN +;; use "special elements", either in `imenu-generic-expression', +;; or generated by a `imenu-create-index-function' func. +;; ENDIF +;; +;; IF you want to index a hierarchical structure +;; THEN +;; Have a look at `hier-imenu.el' for more building blocks. You can +;; get it from . +;; ENDIF + +;; Changes by Yann Dirson : +;; May 2000: +;; - Implemented function-generated parts of menus in `imenu-generic-expression' +;; (well, in real, ported from code I wrote in 1996-97) +;; - Implemented a number of predicate funcs to be more accurate than old +;; `imenu--subalist-p', at the cost of being more heavy-weight, but hey, now +;; it seems to work +;; - Fixed `imenu--create-menu-2', `imenu--sort-by-position', +;; `imenu--truncate-items' not to fail on "special elements" +;; - Take in account the elements kept at top of index ("rescan", sub-lists) +;; when deciding whether to split because of too many items +;; - Documented "special elements" +;; - "How to setup for a new mode" documentation +;; - Fixed and enhanced many docstrings +;; 1996-97: +;; - Implemented imenu-use-markers and imenu-max-item-length + ;;; History: ;; Thanks go to ;; [simon] - Simon Leinen simon@lia.di.epfl.ch @@ -176,11 +241,15 @@ The value should be an alist with elements that look like this: (MENU-TITLE REGEXP INDEX) or like this: + (MENU-TITLE FUNCTION) +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 +the index alist when it matches; the second format creates a submenu (or part +of the menu) by calling FUNCTION, which is expected to be a function suitable +for use as `imenu-create-index-function'; the latter creates a special element of the form (NAME FUNCTION POSITION-MARKER ARGUMENTS...) -with FUNCTION and ARGUMENTS beiong copied from `imenu-generic-expression'. +with FUNCTION and ARGUMENTS being 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. @@ -193,6 +262,9 @@ INDEX points to the substring in REGEXP that contains the name (of the function, variable or type) that is to appear in the menu. +See the docstring for `imenu--index-alist' for the definition of the +structure of the index alists. + The variable is buffer-local. The variable `imenu-case-fold-search' determines whether or not the @@ -216,11 +288,8 @@ It should be a function that takes no arguments and returns an index of the current buffer as an alist. -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. +See the docstring for `imenu--index-alist' for the definition of the +structure of the index alists. This function is called within a `save-excursion'. @@ -264,9 +333,39 @@ (make-variable-buffer-local 'imenu-default-goto-function) +(defun imenu--simple-item-p (item) + (and (consp item) + (stringp (car item)) + (integer-or-marker-p (cdr item)))) + +(defun imenu--special-item-p (item) + (and (consp item) + (stringp (car item)) + (consp (cdr item)) + (integer-or-marker-p (nth 1 item)) + (consp (cddr item)) + ;; 3 elt may be either a named or an anonymous function + (or (symbolp (nth 2 item)) + (and (cons (nth 2 item)) + (eq (car (nth 2 item) 'lambda)))))) + +(defun imenu--item-p (item) + (or (imenu--simple-item-p item) + (imenu--special-item-p item) + (imenu--subalist-p item))) + +;; is `alist' a valid imenu alist ? +(defun imenu--alist-p (alist) + (and (consp alist) + (imenu--item-p (car alist)) + (or (null (cdr alist)) + (imenu--alist-p (cdr alist))))) + (defun imenu--subalist-p (item) - (and (consp (cdr item)) (listp (cadr item)) - (not (eq (car (cadr item)) 'lambda)))) + (and (consp item) + (stringp (car item)) + (imenu--alist-p (cdr item)))) + ;; Macro to display a progress message. ;; RELPOS is the relative position to display. @@ -335,31 +434,31 @@ ;; Search for the function (while (beginning-of-defun) (imenu-progress-message prev-pos nil t) - (save-match-data - (and (looking-at "(def") - (save-excursion + (save-match-data + (and (looking-at "(def") + (save-excursion (down-list 1) - (cond + (cond ((looking-at "def\\(var\\|const\\)") - (forward-sexp 2) - (push (imenu-example--name-and-position) - index-var-alist)) + (forward-sexp 2) + (push (imenu-example--name-and-position) + index-var-alist)) ((looking-at "def\\(un\\|subst\\|macro\\|advice\\)") - (forward-sexp 2) - (push (imenu-example--name-and-position) - index-alist)) + (forward-sexp 2) + (push (imenu-example--name-and-position) + index-alist)) ((looking-at "def\\(type\\|struct\\|class\\|ine-condition\\)") - (forward-sexp 2) + (forward-sexp 2) (if (= (char-after (1- (point))) ?\)) - (progn + (progn (forward-sexp -1) - (down-list 1) + (down-list 1) (forward-sexp 1))) - (push (imenu-example--name-and-position) - index-type-alist)) - (t - (forward-sexp 2) - (push (imenu-example--name-and-position) + (push (imenu-example--name-and-position) + index-type-alist)) + (t + (forward-sexp 2) + (push (imenu-example--name-and-position) index-unknown-alist))))))) (imenu-progress-message prev-pos 100) (and index-var-alist @@ -422,6 +521,21 @@ Special elements look like (INDEX-NAME INDEX-POSITION FUNCTION ARGUMENTS...). A nested sub-alist element looks like (INDEX-NAME SUB-ALIST). +A simple element with negative POSITION can be added; that's intended +as a way for the user to ask to recalculate the buffer's index alist. +`imenu--rescan-item' is a definition for such an item. + +The FUNCTION for a special element allows to customize the way to jump +to the indexed position. A simple element is just like a special +element with a FUNCTION of `imenu-default-goto-function'. + +FUNCTION is called by `imenu' with the INDEX-NAME as first argument, +the INDEX-POSITION as second argument, and the specified ARGUMENTS as +remaining args. + +The function `imenu--subalist-p' tests an element and returns t +if it is a sub-alist. + This variable is local in all buffers, once set.") (make-variable-buffer-local 'imenu--index-alist) @@ -450,7 +564,13 @@ (string-lessp (car item1) (car item2))) (defun imenu--sort-by-position (item1 item2) - (< (cdr item1) (cdr item2))) + ;; we must take care of the 2 different formats used by + ;; simple and special elements + (let ((p1 (cdr item1)) + (p2 (cdr item2))) + (if (listp p1) (setq p1 (car p1))) + (if (listp p2) (setq p2 (car p2))) + (< p1 p2))) (defun imenu--relative-position (&optional reverse) ;; Support function to calculate relative position in buffer @@ -508,7 +628,8 @@ (if res ; in case, e.g. no functions defined (prog1 (nreverse res) (setcdr res oldlist)))) imenu-sort-function))) - (if (> (length menulist) imenu-max-items) + (if (> (+ (length menulist) (length keep-at-top)) + imenu-max-items) (let ((count 0)) (setq menulist (mapcar @@ -524,9 +645,7 @@ (defun imenu--split-submenus (alist) (mapcar (function (lambda (elt) - (if (and (consp elt) - (stringp (car elt)) - (listp (cdr elt))) + (if (imenu--subalist-p elt) (imenu--split-menu (cdr elt) (car elt)) elt))) alist)) @@ -536,7 +655,7 @@ (mapcar (function (lambda (item) (cond - ((consp (cdr item)) + ((imenu--subalist-p item) (imenu--truncate-items (cdr item))) (t ;; truncate if necessary @@ -546,20 +665,15 @@ menulist)) +;; This function basically wraps `imenu-create-index-function' (defun imenu--make-index-alist (&optional noerror) "Create an index-alist for the definitions in the current buffer. 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 the docstring for `imenu--index-alist' for the definition of the +structure of the index alists." (or (and imenu--index-alist (or (not imenu-auto-rescan) (and imenu-auto-rescan @@ -748,21 +862,8 @@ (defun imenu--generic-function (patterns) "Return an index of the current buffer as an alist. -PATTERNS is an alist with elements that look like this: - (MENU-TITLE 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. - -INDEX points to the substring in REGEXP that contains the name (of the -function, variable or type) that is to appear in the menu. - -See `lisp-imenu-generic-expression' for an example of PATTERNS. +See the docstring for `imenu-generic-expression' for a definition of +PATTERNS. 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 @@ -796,45 +897,57 @@ (function (lambda (pat) (let ((menu-title (car pat)) - (regexp (nth 1 pat)) + (regexp-or-func (nth 1 pat)) (index (nth 2 pat)) (function (nth 3 pat)) (rest (nthcdr 4 pat))) - ;; Go backwards for convenience of adding items in order. - (goto-char (point-max)) - (while (re-search-backward regexp nil t) - (imenu-progress-message prev-pos nil t) - (setq beg (match-beginning index)) - ;; Add this sort of submenu only when we've found an - ;; item for it, avoiding empty, duff menus. - (unless (assoc menu-title index-alist) - (push (list menu-title) index-alist)) - (if imenu-use-markers - (setq beg (set-marker (make-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))))))))) + (if (and (symbolp regexp-or-func) (fboundp regexp-or-func)) + ;; handle submenus filled with another function-call + (let ((func regexp-or-func)) + (if (null menu-title) + ;; if title is nil put it on top-level, for consistency + (nconc index-alist (funcall func)) + ;; else put it in a submenu + (push + (cons menu-title (funcall func)) + index-alist))) + (let ((regexp regexp-or-func)) + ;; Go backwards for convenience of adding items in order. + (goto-char (point-max)) + (while (re-search-backward regexp nil t) + (imenu-progress-message prev-pos nil t) + (setq beg (match-beginning index)) + ;; Add this sort of submenu only when we've found an + ;; item for it, avoiding empty, duff menus. + (unless (assoc menu-title index-alist) + (push (list menu-title) index-alist)) + (if imenu-use-markers + (setq beg (set-marker (make-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))))))))))) patterns) + (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. (let ((tail index-alist)) (while tail - (if (listp (car tail)) + (if (imenu--subalist-p (car tail)) (setcdr (car tail) - (sort (cdr (car tail)) 'imenu--sort-by-position))) + (sort (cdar tail) 'imenu--sort-by-position))) (setq tail (cdr tail)))) (let ((main-element (assq nil index-alist))) (nconc (delq main-element (delq 'dummy index-alist)) @@ -1065,13 +1178,14 @@ (and index-item (progn (push-mark) - (let* ((is-special-item (listp (cdr index-item))) + (let* ((is-special-item (imenu--special-item-p index-item)) (function (if is-special-item - (nth 2 index-item) imenu-default-goto-function)) - (position (if is-special-item - (cadr index-item) (cdr index-item))) - (rest (if is-special-item (cddr index-item)))) + (nth 2 index-item) + imenu-default-goto-function)) + (position (if is-special-item + (cadr index-item) (cdr index-item))) + (rest (if is-special-item (cddr index-item)))) (apply function (car index-item) position rest))))) (provide 'imenu)