(fortran-current-defun): Use save-excursion.
[bpt/emacs.git] / lisp / imenu.el
CommitLineData
55535639 1;;; imenu.el --- framework for mode-specific buffer indexes
0a688fd0 2
fe2908be 3;; Copyright (C) 1994, 1995, 1996, 1997, 1998 Free Software Foundation, Inc.
0a688fd0
RS
4
5;; Author: Ake Stenhoff <etxaksf@aom.ericsson.se>
6;; Lars Lindberg <lli@sypro.cap.se>
e4874521 7;; Maintainer: FSF
0a688fd0 8;; Created: 8 Feb 1994
f5f727f8 9;; Keywords: tools convenience
b578f267
EN
10
11;; This file is part of GNU Emacs.
12
13;; GNU Emacs is free software; you can redistribute it and/or modify
0a688fd0
RS
14;; it under the terms of the GNU General Public License as published by
15;; the Free Software Foundation; either version 2, or (at your option)
16;; any later version.
b578f267
EN
17
18;; GNU Emacs is distributed in the hope that it will be useful,
0a688fd0
RS
19;; but WITHOUT ANY WARRANTY; without even the implied warranty of
20;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21;; GNU General Public License for more details.
b578f267 22
0a688fd0 23;; You should have received a copy of the GNU General Public License
b578f267
EN
24;; along with GNU Emacs; see the file COPYING. If not, write to the
25;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
26;; Boston, MA 02111-1307, USA.
0a688fd0
RS
27
28;;; Commentary:
b578f267 29
0a688fd0
RS
30;; Purpose of this package:
31;; To present a framework for mode-specific buffer indexes.
32;; A buffer index is an alist of names and buffer positions.
33;; For instance all functions in a C-file and their positions.
34;;
fe2908be
RS
35;; It is documented in the Emacs Lisp manual.
36;;
0a688fd0
RS
37;; How it works:
38
39;; A mode-specific function is called to generate the index. It is
40;; then presented to the user, who can choose from this index.
41;;
42;; The package comes with a set of example functions for how to
43;; utilize this package.
44
2d24227e
RS
45;; There are *examples* for index gathering functions/regular
46;; expressions for C/C++ and Lisp/Emacs Lisp but it is easy to
47;; customize for other modes. A function for jumping to the chosen
48;; index position is also supplied.
0a688fd0 49
fe2908be
RS
50;;; History:
51;; Thanks go to
26d6bb60
RS
52;; [simon] - Simon Leinen simon@lia.di.epfl.ch
53;; [dean] - Dean Andrews ada@unison.com
fe2908be 54;; [alon] - Alon Albert al@mercury.co.il
7804cd27 55;; [greg] - Greg Thompson gregt@porsche.visix.COM
615b306c 56;; [wolfgang] - Wolfgang Bangerth zcg51122@rpool1.rus.uni-stuttgart.de
056ab244 57;; [kai] - Kai Grossjohann grossjoh@linus.informatik.uni-dortmund.de
af447694 58;; [david] - David M. Smith dsmith@stats.adelaide.edu.au
2d24227e
RS
59;; [christian] - Christian Egli Christian.Egli@hcsd.hac.com
60;; [karl] - Karl Fogel kfogel@floss.life.uiuc.edu
61
55535639 62;;; Code:
b578f267 63
0ee4f8ad 64(eval-when-compile (require 'cl))
0a688fd0
RS
65
66;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
67;;;
68;;; Customizable variables
69;;;
70;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2d24227e 71
94114394
RS
72(defgroup imenu nil
73 "Mode-specific buffer indexes."
74 :group 'matching
fe2908be 75 :group 'frames
f5f727f8 76 :group 'convenience
fe2908be 77 :link '(custom-manual "(elisp)Imenu"))
94114394
RS
78
79(defcustom imenu-use-markers t
e7c8378c 80 "*Non-nil means use markers instead of integers for Imenu buffer positions.
fe2908be
RS
81
82Setting this to nil makes Imenu work a little faster but editing the
83buffer will make the generated index positions wrong.
e7c8378c 84
94114394
RS
85This might not yet be honored by all index-building functions."
86 :type 'boolean
87 :group 'imenu)
88
e7c8378c 89
94114394
RS
90(defcustom imenu-max-item-length 60
91 "*If a number, truncate Imenu entries to that length."
fe2908be
RS
92 :type '(choice integer
93 (const :tag "Unlimited"))
94114394 94 :group 'imenu)
e7c8378c 95
9df23821 96(defcustom imenu-auto-rescan nil
94114394
RS
97 "*Non-nil means Imenu should always rescan the buffers."
98 :type 'boolean
99 :group 'imenu)
2d24227e 100
fe2908be
RS
101(defcustom imenu-auto-rescan-maxout 60000
102 "*Imenu auto-rescan is disabled in buffers larger than this size (in bytes).
94114394
RS
103This variable is buffer-local."
104 :type 'integer
105 :group 'imenu)
0a688fd0 106
5988bd27
SM
107(defvar imenu-always-use-completion-buffer-p nil)
108(make-obsolete-variable 'imenu-always-use-completion-buffer-p
109 'imenu-use-popup-menu "21.4")
110
111(defcustom imenu-use-popup-menu
112 (if imenu-always-use-completion-buffer-p
113 (not (eq imenu-always-use-completion-buffer-p 'never))
114 'on-mouse)
115 "Use a popup menu rather than a minibuffer prompt.
116If nil, always use a minibuffer prompt.
117If t, always use a popup menu,
118If `on-mouse' use a popup menu when `imenu' was invoked with the mouse."
119 :type '(choice (const :tag "On Mouse" on-mouse)
120 (const :tag "Never" nil)
121 (other :tag "Always" t)))
122
123(defcustom imenu-eager-completion-buffer
124 (not (eq imenu-always-use-completion-buffer-p 'never))
125 "If non-nil, eagerly popup the completion buffer."
126 :type 'boolean)
0a688fd0 127
020e8fdf
PR
128(defcustom imenu-after-jump-hook nil
129 "*Hooks called after jumping to a place in the buffer.
130
131Useful things to use here include `reposition-window', `recenter', and
132\(lambda () (recenter 0)) to show at top of screen."
133 :type 'hook
134 :group 'imenu)
135
31f2a064 136;;;###autoload
94114394 137(defcustom imenu-sort-function nil
0a688fd0
RS
138 "*The function to use for sorting the index mouse-menu.
139
140Affects only the mouse index menu.
141
142Set this to nil if you don't want any sorting (faster).
143The items in the menu are then presented in the order they were found
144in the buffer.
145
0ee4f8ad 146Set it to `imenu--sort-by-name' if you want alphabetic sorting.
0a688fd0 147
c01ee596 148The function should take two arguments and return t if the first
0a688fd0 149element should come before the second. The arguments are cons cells;
94114394 150\(NAME . POSITION). Look at `imenu--sort-by-name' for an example."
fe2908be 151 :type '(choice (const :tag "No sorting" nil)
df90db13 152 (const :tag "Sort by name" imenu--sort-by-name)
fe2908be 153 (function :tag "Another function"))
94114394 154 :group 'imenu)
0a688fd0 155
94114394
RS
156(defcustom imenu-max-items 25
157 "*Maximum number of elements in a mouse menu for Imenu."
158 :type 'integer
159 :group 'imenu)
0a688fd0 160
94114394 161(defcustom imenu-scanning-message "Scanning buffer for index (%3d%%)"
0a688fd0 162 "*Progress message during the index scanning of the buffer.
2892dc82 163If non-nil, user gets a message during the scanning of the buffer.
0a688fd0
RS
164
165Relevant only if the mode-specific function that creates the buffer
fe2908be
RS
166index use `imenu-progress-message', and not useful if that is fast, in
167which case you might as well set this to nil."
168 :type '(choice string
169 (const :tag "None" nil))
94114394 170 :group 'imenu)
0a688fd0 171
a742f6cc 172(defcustom imenu-space-replacement "."
0a688fd0 173 "*The replacement string for spaces in index names.
a742f6cc 174Used when presenting the index in a completion buffer to make the
94114394 175names work as tokens."
5988bd27 176 :type '(choice string (const nil))
94114394 177 :group 'imenu)
0a688fd0 178
94114394 179(defcustom imenu-level-separator ":"
0a688fd0
RS
180 "*The separator between index names of different levels.
181Used for making mouse-menu titles and for flattening nested indexes
94114394
RS
182with name concatenation."
183 :type 'string
184 :group 'imenu)
0a688fd0 185
2d24227e 186;;;###autoload
615b306c 187(defvar imenu-generic-expression nil
2d24227e
RS
188 "The regex pattern to use for creating a buffer index.
189
01e980fb 190If non-nil this pattern is passed to `imenu--generic-function'
2d24227e
RS
191to create a buffer index.
192
215b077e
RS
193The value should be an alist with elements that look like this:
194 (MENU-TITLE REGEXP INDEX)
195or like this:
196 (MENU-TITLE REGEXP INDEX FUNCTION ARGUMENTS...)
197with zero or more ARGUMENTS. The former format creates a simple element in
198the index alist when it matches; the latter creates a special element
70223ca4
SM
199of the form (NAME POSITION-MARKER FUNCTION ARGUMENTS...)
200with FUNCTION and ARGUMENTS copied from `imenu-generic-expression'.
2d24227e
RS
201
202MENU-TITLE is a string used as the title for the submenu or nil if the
203entries are not nested.
204
205REGEXP is a regexp that should match a construct in the buffer that is
6c1bf12b
RS
206to be displayed in the menu; i.e., function or variable definitions,
207etc. It contains a substring which is the name to appear in the
208menu. See the info section on Regexps for more information.
2d24227e
RS
209
210INDEX points to the substring in REGEXP that contains the name (of the
211function, variable or type) that is to appear in the menu.
615b306c 212
73f48953
DL
213The variable is buffer-local.
214
215The variable `imenu-case-fold-search' determines whether or not the
1447c4b1 216regexp matches are case sensitive, and `imenu-syntax-alist' can be
fe2908be
RS
217used to alter the syntax table for the search.
218
0cdb3baa 219For example, see the value of `fortran-imenu-generic-expression' used by
1447c4b1
DL
220`fortran-mode' with `imenu-syntax-alist' set locally to give the
221characters which normally have \"symbol\" syntax \"word\" syntax
222during matching.")
2d24227e 223
af5eb153 224;;;###autoload
6c1bf12b 225(make-variable-buffer-local 'imenu-generic-expression)
615b306c 226
0a688fd0
RS
227;;;; Hooks
228
31f2a064 229;;;###autoload
0a688fd0
RS
230(defvar imenu-create-index-function 'imenu-default-create-index-function
231 "The function to use for creating a buffer index.
232
233It should be a function that takes no arguments and returns an index
215b077e
RS
234of the current buffer as an alist.
235
236Simple elements in the alist look like (INDEX-NAME . INDEX-POSITION).
35c8b898 237Special elements look like (INDEX-NAME INDEX-POSITION FUNCTION ARGUMENTS...).
215b077e
RS
238A nested sub-alist element looks like (INDEX-NAME SUB-ALIST).
239The function `imenu--subalist-p' tests an element and returns t
fe2908be 240if it is a sub-alist.
0a688fd0 241
0ee4f8ad 242This function is called within a `save-excursion'.
0a688fd0
RS
243
244The variable is buffer-local.")
31f2a064 245;;;###autoload
0a688fd0
RS
246(make-variable-buffer-local 'imenu-create-index-function)
247
31f2a064 248;;;###autoload
68e01f5a 249(defvar imenu-prev-index-position-function 'beginning-of-defun
0a688fd0
RS
250 "Function for finding the next index position.
251
0ee4f8ad
RS
252If `imenu-create-index-function' is set to
253`imenu-default-create-index-function', then you must set this variable
0a688fd0
RS
254to a function that will find the next index, looking backwards in the
255file.
256
257The function should leave point at the place to be connected to the
35c8b898
RS
258index and it should return nil when it doesn't find another index.
259
260This variable is local in all buffers.")
31f2a064 261;;;###autoload
68e01f5a 262(make-variable-buffer-local 'imenu-prev-index-position-function)
0a688fd0 263
31f2a064 264;;;###autoload
68e01f5a 265(defvar imenu-extract-index-name-function nil
fe2908be 266 "Function for extracting the index item name, given a position.
35c8b898
RS
267
268This function is called after `imenu-prev-index-position-function'
269finds a position for an index item, with point at that position.
270It should return the name for that index item.
271
272This variable is local in all buffers.")
31f2a064 273;;;###autoload
68e01f5a 274(make-variable-buffer-local 'imenu-extract-index-name-function)
0a688fd0 275
020e8fdf
PR
276;;;###autoload
277(defvar imenu-name-lookup-function nil
278 "Function to compare string with index item.
279
280This function will be called with two strings, and should return
281non-nil if they match.
282
283If nil, comparison is done with `string='.
284Set this to some other function for more advanced comparisons,
285such as \"begins with\" or \"name matches and number of
286arguments match\".
287
288This variable is local in all buffers.")
289;;;###autoload
290(make-variable-buffer-local 'imenu-name-lookup-function)
291
31f2a064 292;;;###autoload
37954a9a
RS
293(defvar imenu-default-goto-function 'imenu-default-goto-function
294 "The default function called when selecting an Imenu item.
295The function in this variable is called when selecting a normal index-item.")
31f2a064 296;;;###autoload
37954a9a
RS
297(make-variable-buffer-local 'imenu-default-goto-function)
298
299
215b077e
RS
300(defun imenu--subalist-p (item)
301 (and (consp (cdr item)) (listp (cadr item))
fe2908be 302 (not (eq (car (cadr item)) 'lambda))))
215b077e 303
fe2908be
RS
304;; Macro to display a progress message.
305;; RELPOS is the relative position to display.
306;; If RELPOS is nil, then the relative position in the buffer
307;; is calculated.
308;; PREVPOS is the variable in which we store the last position displayed.
615b306c 309(defmacro imenu-progress-message (prevpos &optional relpos reverse)
fe2908be
RS
310 `(and
311 imenu-scanning-message
312 (let ((pos ,(if relpos
313 relpos
314 `(imenu--relative-position ,reverse))))
315 (if ,(if relpos t
316 `(> pos (+ 5 ,prevpos)))
317 (progn
318 (message imenu-scanning-message pos)
319 (setq ,prevpos pos))))))
615b306c
KH
320
321
322;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
323;;;;
324;;;; Some examples of functions utilizing the framework of this
325;;;; package.
326;;;;
327;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
328
e064a4f9 329;; Return the current/previous sexp and the location of the sexp (its
615b306c
KH
330;; beginning) without moving the point.
331(defun imenu-example--name-and-position ()
332 (save-excursion
333 (forward-sexp -1)
e7c8378c
RS
334 ;; [ydi] modified for imenu-use-markers
335 (let ((beg (if imenu-use-markers (point-marker) (point)))
336 (end (progn (forward-sexp) (point))))
615b306c 337 (cons (buffer-substring beg end)
e7c8378c 338 beg))))
615b306c
KH
339
340;;;
341;;; Lisp
fe2908be 342;;;
615b306c
KH
343
344(defun imenu-example--lisp-extract-index-name ()
345 ;; Example of a candidate for `imenu-extract-index-name-function'.
346 ;; This will generate a flat index of definitions in a lisp file.
347 (save-match-data
348 (and (looking-at "(def")
349 (condition-case nil
350 (progn
351 (down-list 1)
352 (forward-sexp 2)
353 (let ((beg (point))
354 (end (progn (forward-sexp -1) (point))))
355 (buffer-substring beg end)))
356 (error nil)))))
357
358(defun imenu-example--create-lisp-index ()
359 ;; Example of a candidate for `imenu-create-index-function'.
360 ;; It will generate a nested index of definitions.
361 (let ((index-alist '())
362 (index-var-alist '())
363 (index-type-alist '())
364 (index-unknown-alist '())
365 prev-pos)
366 (goto-char (point-max))
367 (imenu-progress-message prev-pos 0)
368 ;; Search for the function
369 (while (beginning-of-defun)
370 (imenu-progress-message prev-pos nil t)
fe2908be
RS
371 (save-match-data
372 (and (looking-at "(def")
373 (save-excursion
615b306c 374 (down-list 1)
fe2908be 375 (cond
615b306c 376 ((looking-at "def\\(var\\|const\\)")
fe2908be
RS
377 (forward-sexp 2)
378 (push (imenu-example--name-and-position)
379 index-var-alist))
615b306c 380 ((looking-at "def\\(un\\|subst\\|macro\\|advice\\)")
fe2908be
RS
381 (forward-sexp 2)
382 (push (imenu-example--name-and-position)
383 index-alist))
615b306c 384 ((looking-at "def\\(type\\|struct\\|class\\|ine-condition\\)")
fe2908be 385 (forward-sexp 2)
615b306c 386 (if (= (char-after (1- (point))) ?\))
fe2908be 387 (progn
615b306c 388 (forward-sexp -1)
fe2908be 389 (down-list 1)
615b306c 390 (forward-sexp 1)))
fe2908be
RS
391 (push (imenu-example--name-and-position)
392 index-type-alist))
393 (t
394 (forward-sexp 2)
395 (push (imenu-example--name-and-position)
615b306c
KH
396 index-unknown-alist)))))))
397 (imenu-progress-message prev-pos 100)
398 (and index-var-alist
0c20ee61 399 (push (cons "Variables" index-var-alist)
615b306c
KH
400 index-alist))
401 (and index-type-alist
0c20ee61 402 (push (cons "Types" index-type-alist)
615b306c
KH
403 index-alist))
404 (and index-unknown-alist
0c20ee61 405 (push (cons "Syntax-unknown" index-unknown-alist)
615b306c
KH
406 index-alist))
407 index-alist))
408
615b306c
KH
409;; Regular expression to find C functions
410(defvar imenu-example--function-name-regexp-c
fe2908be 411 (concat
615b306c
KH
412 "^[a-zA-Z0-9]+[ \t]?" ; type specs; there can be no
413 "\\([a-zA-Z0-9_*]+[ \t]+\\)?" ; more than 3 tokens, right?
414 "\\([a-zA-Z0-9_*]+[ \t]+\\)?"
415 "\\([*&]+[ \t]*\\)?" ; pointer
416 "\\([a-zA-Z0-9_*]+\\)[ \t]*(" ; name
417 ))
418
419(defun imenu-example--create-c-index (&optional regexp)
420 (let ((index-alist '())
421 prev-pos char)
422 (goto-char (point-min))
423 (imenu-progress-message prev-pos 0)
424 ;; Search for the function
425 (save-match-data
426 (while (re-search-forward
427 (or regexp imenu-example--function-name-regexp-c)
428 nil t)
429 (imenu-progress-message prev-pos)
430 (backward-up-list 1)
431 (save-excursion
432 (goto-char (scan-sexps (point) 1))
433 (setq char (following-char)))
434 ;; Skip this function name if it is a prototype declaration.
435 (if (not (eq char ?\;))
436 (push (imenu-example--name-and-position) index-alist))))
437 (imenu-progress-message prev-pos 100)
438 (nreverse index-alist)))
439
2d24227e 440
0a688fd0
RS
441;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
442;;;
443;;; Internal variables
444;;;
445;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
446
447;; The item to use in the index for rescanning the buffer.
448(defconst imenu--rescan-item '("*Rescan*" . -99))
449
450;; The latest buffer index.
451;; Buffer local.
35c8b898
RS
452(defvar imenu--index-alist nil
453 "The buffer index computed for this buffer in Imenu.
454Simple elements in the alist look like (INDEX-NAME . INDEX-POSITION).
455Special elements look like (INDEX-NAME INDEX-POSITION FUNCTION ARGUMENTS...).
456A nested sub-alist element looks like (INDEX-NAME SUB-ALIST).
457
458This variable is local in all buffers, once set.")
459
0a688fd0
RS
460(make-variable-buffer-local 'imenu--index-alist)
461
0cff96e7
RS
462(defvar imenu--last-menubar-index-alist nil
463 "The latest buffer index used to update the menu bar menu.")
464
0a8e8bc6
KH
465(make-variable-buffer-local 'imenu--last-menubar-index-alist)
466
0a688fd0 467;; History list for 'jump-to-function-in-buffer'.
6c1bf12b 468;; Making this buffer local caused it not to work!
0a688fd0 469(defvar imenu--history-list nil)
0a688fd0
RS
470
471;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
472;;;
473;;; Internal support functions
474;;;
475;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
476
477;;;
478;;; Sort function
479;;; Sorts the items depending on their index name.
c01ee596 480;;; An item looks like (NAME . POSITION).
0a688fd0
RS
481;;;
482(defun imenu--sort-by-name (item1 item2)
483 (string-lessp (car item1) (car item2)))
484
c01ee596
KH
485(defun imenu--sort-by-position (item1 item2)
486 (< (cdr item1) (cdr item2)))
487
0a688fd0
RS
488(defun imenu--relative-position (&optional reverse)
489 ;; Support function to calculate relative position in buffer
490 ;; Beginning of buffer is 0 and end of buffer is 100
491 ;; If REVERSE is non-nil then the beginning is 100 and the end is 0.
492 (let ((pos (point))
493 (total (buffer-size)))
494 (and reverse (setq pos (- total pos)))
495 (if (> total 50000)
496 ;; Avoid overflow from multiplying by 100!
497 (/ (1- pos) (max (/ total 100) 1))
498 (/ (* 100 (1- pos)) (max total 1)))))
499
0a688fd0
RS
500;; Split LIST into sublists of max length N.
501;; Example (imenu--split '(1 2 3 4 5 6 7 8) 3)-> '((1 2 3) (4 5 6) (7 8))
502(defun imenu--split (list n)
503 (let ((remain list)
504 (result '())
505 (sublist '())
506 (i 0))
507 (while remain
508 (push (pop remain) sublist)
509 (incf i)
510 (and (= i n)
511 ;; We have finished a sublist
512 (progn (push (nreverse sublist) result)
513 (setq i 0)
514 (setq sublist '()))))
515 ;; There might be a sublist (if the length of LIST mod n is != 0)
516 ;; that has to be added to the result list.
517 (and sublist
518 (push (nreverse sublist) result))
519 (nreverse result)))
520
0c20ee61
RS
521;;; Split the alist MENULIST into a nested alist, if it is long enough.
522;;; In any case, add TITLE to the front of the alist.
0a688fd0 523(defun imenu--split-menu (menulist title)
7ebea144
RS
524 (let (keep-at-top tail)
525 (if (memq imenu--rescan-item menulist)
526 (setq keep-at-top (cons imenu--rescan-item nil)
527 menulist (delq imenu--rescan-item menulist)))
528 (setq tail menulist)
0cdb3baa
SM
529 (dolist (item tail)
530 (if (imenu--subalist-p item)
531 (setq keep-at-top (cons item keep-at-top)
532 menulist (delq item menulist))))
7ebea144
RS
533 (if imenu-sort-function
534 (setq menulist
535 (sort
0cdb3baa 536 (copy-sequence menulist)
7ebea144
RS
537 imenu-sort-function)))
538 (if (> (length menulist) imenu-max-items)
539 (let ((count 0))
540 (setq menulist
541 (mapcar
542 (function
543 (lambda (menu)
544 (cons (format "From: %s" (caar menu)) menu)))
545 (imenu--split menulist imenu-max-items)))))
546 (cons title
547 (nconc (nreverse keep-at-top) menulist))))
0c20ee61
RS
548
549;;; Split up each long alist that are nested within ALIST
550;;; into nested alists.
551(defun imenu--split-submenus (alist)
fe2908be
RS
552 (mapcar (function
553 (lambda (elt)
554 (if (and (consp elt)
555 (stringp (car elt))
556 (listp (cdr elt)))
557 (imenu--split-menu (cdr elt) (car elt))
558 elt)))
0c20ee61 559 alist))
0a688fd0 560
e7c8378c
RS
561;;; Truncate all strings in MENULIST to imenu-max-item-length
562(defun imenu--truncate-items (menulist)
117be359
DL
563 (mapcar (function
564 (lambda (item)
565 (cond
566 ((consp (cdr item))
567 (imenu--truncate-items (cdr item)))
568 (t
569 ;; truncate if necessary
570 (if (and (numberp imenu-max-item-length)
571 (> (length (car item)) imenu-max-item-length))
572 (setcar item (substring (car item) 0
573 imenu-max-item-length)))))))
574 menulist))
e7c8378c
RS
575
576
0a8e8bc6 577(defun imenu--make-index-alist (&optional noerror)
7e563e04
RS
578 "Create an index-alist for the definitions in the current buffer.
579
fe2908be
RS
580Report an error if the list is empty unless NOERROR is supplied and
581non-nil.
582
7e563e04
RS
583Simple elements in the alist look like (INDEX-NAME . INDEX-POSITION).
584Special elements look like (INDEX-NAME FUNCTION ARGUMENTS...).
585A nested sub-alist element looks like (INDEX-NAME SUB-ALIST).
586The function `imenu--subalist-p' tests an element and returns t
fe2908be 587if it is a sub-alist.
7e563e04
RS
588
589There is one simple element with negative POSITION; that's intended
590as a way for the user to ask to recalculate the buffer's index alist."
2d24227e
RS
591 (or (and imenu--index-alist
592 (or (not imenu-auto-rescan)
593 (and imenu-auto-rescan
594 (> (buffer-size) imenu-auto-rescan-maxout))))
e7c8378c
RS
595 ;; Get the index; truncate if necessary
596 (progn
597 (setq imenu--index-alist
598 (save-excursion
599 (save-restriction
600 (widen)
601 (funcall imenu-create-index-function))))
602 (imenu--truncate-items imenu--index-alist)))
0a8e8bc6 603 (or imenu--index-alist noerror
6c1bf12b 604 (error "No items suitable for an index found in this buffer"))
0a8e8bc6
KH
605 (or imenu--index-alist
606 (setq imenu--index-alist (list nil)))
0a688fd0
RS
607 ;; Add a rescan option to the index.
608 (cons imenu--rescan-item imenu--index-alist))
79e098ca 609
5d3b0f18
RS
610;;; Find all markers in alist and makes
611;;; them point nowhere.
79e098ca
RS
612;;; The top-level call uses nil as the argument;
613;;; non-nil arguments are in recursivecalls.
614(defvar imenu--cleanup-seen)
615
5d3b0f18 616(defun imenu--cleanup (&optional alist)
fe2908be 617 ;; If alist is provided use that list.
79e098ca
RS
618 ;; If not, empty the table of lists already seen
619 ;; and use imenu--index-alist.
620 (if alist
621 (setq imenu--cleanup-seen (cons alist imenu--cleanup-seen))
622 (setq alist imenu--index-alist imenu--cleanup-seen (list alist)))
623
4818d210 624 (and alist
e26b2a28
DL
625 (mapc
626 (lambda (item)
627 (cond
628 ((markerp (cdr item))
629 (set-marker (cdr item) nil))
630 ;; Don't process one alist twice.
631 ((memq (cdr item) imenu--cleanup-seen))
632 ((imenu--subalist-p item)
633 (imenu--cleanup (cdr item)))))
4818d210 634 alist)
615b306c
KH
635 t))
636
0cdb3baa
SM
637(defun imenu--create-keymap-1 (title alist)
638 (let ((counter 0))
639 (list* 'keymap title
640 (mapcar
641 (lambda (item)
642 (list* (car item) (car item)
643 (cond
644 ((imenu--subalist-p item)
645 (imenu--create-keymap-1 (car item) (cdr item)))
646 (t
647 `(lambda () (interactive)
648 (imenu--menubar-select ',item))))))
649 alist))))
2d24227e 650
2d24227e
RS
651(defun imenu--in-alist (str alist)
652 "Check whether the string STR is contained in multi-level ALIST."
653 (let (elt head tail res)
654 (setq res nil)
655 (while alist
fe2908be 656 (setq elt (car alist)
2d24227e 657 tail (cdr elt)
fe2908be
RS
658 alist (cdr alist)
659 head (car elt))
8396299d
RS
660 ;; A nested ALIST element looks like
661 ;; (INDEX-NAME (INDEX-NAME . INDEX-POSITION) ...)
662 ;; while a bottom-level element looks like
663 ;; (INDEX-NAME . INDEX-POSITION)
664 ;; We are only interested in the bottom-level elements, so we need to
665 ;; recurse if TAIL is a list.
666 (cond ((listp tail)
667 (if (setq res (imenu--in-alist str tail))
668 (setq alist nil)))
020e8fdf
PR
669 ((if imenu-name-lookup-function
670 (funcall imenu-name-lookup-function str head)
671 (string= str head))
8396299d 672 (setq alist nil res elt))))
2d24227e
RS
673 res))
674
fea79780 675(defvar imenu-syntax-alist nil
0cdb3baa 676 "Alist of syntax table modifiers to use while in `imenu--generic-function'.
fea79780
DL
677
678The car of the assocs may be either a character or a string and the
679cdr is a syntax description appropriate fo `modify-syntax-entry'. For
680a string, all the characters in the string get the specified syntax.
681
682This is typically used to give word syntax to characters which
90806abc 683normally have symbol syntax to simplify `imenu-expression'
fea79780 684and speed-up matching.")
005913e4 685;;;###autoload
fea79780
DL
686(make-variable-buffer-local 'imenu-syntax-alist)
687
0a688fd0
RS
688(defun imenu-default-create-index-function ()
689 "*Wrapper for index searching functions.
690
691Moves point to end of buffer and then repeatedly calls
68e01f5a 692`imenu-prev-index-position-function' and `imenu-extract-index-name-function'.
0a688fd0 693Their results are gathered into an index alist."
3e062f78
RS
694 ;; These should really be done by setting imenu-create-index-function
695 ;; in these major modes. But save that change for later.
e536ef56
KH
696 (cond ((and imenu-prev-index-position-function
697 imenu-extract-index-name-function)
3e062f78 698 (let ((index-alist '())
615b306c 699 prev-pos name)
3e062f78 700 (goto-char (point-max))
7dea4e70 701 (imenu-progress-message prev-pos 0 t)
3e062f78 702 ;; Search for the function
fe2908be 703 (while (funcall imenu-prev-index-position-function)
7dea4e70 704 (imenu-progress-message prev-pos nil t)
3e062f78
RS
705 (save-excursion
706 (setq name (funcall imenu-extract-index-name-function)))
707 (and (stringp name)
e7c8378c
RS
708 ;; [ydi] updated for imenu-use-markers
709 (push (cons name (if imenu-use-markers (point-marker) (point)))
710 index-alist)))
7dea4e70 711 (imenu-progress-message prev-pos 100 t)
615b306c
KH
712 index-alist))
713 ;; Use generic expression if possible.
714 ((and imenu-generic-expression)
fe2908be 715 (imenu--generic-function imenu-generic-expression))
615b306c 716 (t
e7c8378c 717 (error "This buffer cannot use `imenu-default-create-index-function'"))))
0a688fd0 718
fe2908be
RS
719;; Not used and would require cl at run time
720;;; (defun imenu--flatten-index-alist (index-alist &optional concat-names prefix)
721;;; ;; Takes a nested INDEX-ALIST and returns a flat index alist.
722;;; ;; If optional CONCAT-NAMES is non-nil, then a nested index has its
723;;; ;; name and a space concatenated to the names of the children.
724;;; ;; Third argument PREFIX is for internal use only.
725;;; (mapcan
726;;; (lambda (item)
727;;; (let* ((name (car item))
728;;; (pos (cdr item))
729;;; (new-prefix (and concat-names
730;;; (if prefix
731;;; (concat prefix imenu-level-separator name)
732;;; name))))
733;;; (cond
734;;; ((or (markerp pos) (numberp pos))
735;;; (list (cons new-prefix pos)))
736;;; (t
737;;; (imenu--flatten-index-alist pos new-prefix)))))
738;;; index-alist))
0a688fd0 739
615b306c
KH
740;;;
741;;; Generic index gathering function.
742;;;
2d24227e 743
73f48953
DL
744(defvar imenu-case-fold-search t
745 "Defines whether `imenu--generic-function' should fold case when matching.
746
0cdb3baa 747This variable should be set (only) by initialization code
73f48953
DL
748for modes which use `imenu--generic-function'. If it is not set, that
749function will use the current value of `case-fold-search' to match
750patterns.")
fe2908be 751;;;###autoload
73f48953
DL
752(make-variable-buffer-local 'imenu-case-fold-search)
753
fe2908be
RS
754;; Originally "Built on some ideas that Erik Naggum <erik@naggum.no>
755;; once posted to comp.emacs" but since substantially re-written.
2d24227e 756(defun imenu--generic-function (patterns)
2d24227e
RS
757 "Return an index of the current buffer as an alist.
758
fe2908be
RS
759PATTERNS is an alist with elements that look like this:
760 (MENU-TITLE REGEXP INDEX).
2d24227e
RS
761
762MENU-TITLE is a string used as the title for the submenu or nil if the
763entries are not nested.
764
765REGEXP is a regexp that should match a construct in the buffer that is
6c1bf12b
RS
766to be displayed in the menu; i.e., function or variable definitions,
767etc. It contains a substring which is the name to appear in the
768menu. See the info section on Regexps for more information.
2d24227e
RS
769
770INDEX points to the substring in REGEXP that contains the name (of the
771function, variable or type) that is to appear in the menu.
772
fe2908be 773See `lisp-imenu-generic-expression' for an example of PATTERNS.
2d24227e 774
6c1bf12b
RS
775Returns an index of the current buffer as an alist. The elements in
776the alist look like: (INDEX-NAME . INDEX-POSITION). They may also be
2d24227e 777nested index lists like (INDEX-NAME . INDEX-ALIST) depending on
fe2908be 778PATTERNS."
2d24227e
RS
779
780 (let ((index-alist (list 'dummy))
fe2908be 781 prev-pos beg
fea79780
DL
782 (case-fold-search imenu-case-fold-search)
783 (old-table (syntax-table))
784 (table (copy-syntax-table (syntax-table)))
785 (slist imenu-syntax-alist))
786 ;; Modify the syntax table used while matching regexps.
0cdb3baa 787 (dolist (syn slist)
fe2908be 788 ;; The character(s) to modify may be a single char or a string.
0cdb3baa
SM
789 (if (numberp (car syn))
790 (modify-syntax-entry (car syn) (cdr syn) table)
5b89a8c9
GM
791 (mapc (lambda (c)
792 (modify-syntax-entry c (cdr syn) table))
793 (car syn))))
615b306c
KH
794 (goto-char (point-max))
795 (imenu-progress-message prev-pos 0 t)
fe2908be
RS
796 (unwind-protect ; for syntax table
797 (save-match-data
798 (set-syntax-table table)
799 ;; map over the elements of imenu-generic-expression
800 ;; (typically functions, variables ...)
0cdb3baa
SM
801 (dolist (pat patterns)
802 (let ((menu-title (car pat))
803 (regexp (nth 1 pat))
804 (index (nth 2 pat))
805 (function (nth 3 pat))
806 (rest (nthcdr 4 pat)))
807 ;; Go backwards for convenience of adding items in order.
808 (goto-char (point-max))
809 (while (re-search-backward regexp nil t)
810 (imenu-progress-message prev-pos nil t)
811 (setq beg (match-beginning index))
812 ;; Add this sort of submenu only when we've found an
813 ;; item for it, avoiding empty, duff menus.
814 (unless (assoc menu-title index-alist)
815 (push (list menu-title) index-alist))
816 (if imenu-use-markers
817 (setq beg (copy-marker beg)))
818 (let ((item
819 (if function
820 (nconc (list (match-string-no-properties index)
821 beg function)
822 rest)
823 (cons (match-string-no-properties index)
824 beg)))
825 ;; This is the desired submenu,
826 ;; starting with its title (or nil).
827 (menu (assoc menu-title index-alist)))
828 ;; Insert the item unless it is already present.
829 (unless (member item (cdr menu))
830 (setcdr menu
831 (cons item (cdr menu))))))))
fe2908be 832 (set-syntax-table old-table)))
0c20ee61 833 (imenu-progress-message prev-pos 100 t)
c01ee596
KH
834 ;; Sort each submenu by position.
835 ;; This is in case one submenu gets items from two different regexps.
0cdb3baa
SM
836 (dolist (item index-alist)
837 (when (listp item)
838 (setcdr item (sort (cdr item) 'imenu--sort-by-position))))
0c20ee61 839 (let ((main-element (assq nil index-alist)))
7ebea144
RS
840 (nconc (delq main-element (delq 'dummy index-alist))
841 (cdr main-element)))))
615b306c 842
0a688fd0
RS
843;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
844;;;
845;;; The main functions for this package!
846;;;
847;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
848
0cdb3baa
SM
849;; See also info-lookup-find-item
850(defun imenu-find-default (guess completions)
851 "Fuzzily find an item based on GUESS inside the alist COMPLETIONS."
852 (catch 'found
853 (let ((case-fold-search t))
854 (if (assoc guess completions) guess
855 (dolist (re (list (concat "\\`" (regexp-quote guess) "\\'")
856 (concat "\\`" (regexp-quote guess))
857 (concat (regexp-quote guess) "\\'")
858 (regexp-quote guess)))
859 (dolist (x completions)
860 (if (string-match re (car x)) (throw 'found (car x)))))))))
861
0a688fd0
RS
862(defun imenu--completion-buffer (index-alist &optional prompt)
863 "Let the user select from INDEX-ALIST in a completion buffer with PROMPT.
864
865Returns t for rescan and otherwise a position number."
866 ;; Create a list for this buffer only when needed.
fe2908be
RS
867 (let ((name (thing-at-point 'symbol))
868 choice
869 (prepared-index-alist
5988bd27
SM
870 (if (not imenu-space-replacement) index-alist
871 (mapcar
872 (lambda (item)
873 (cons (subst-char-in-string ?\ (aref imenu-space-replacement 0)
874 (car item))
875 (cdr item)))
876 index-alist))))
0cdb3baa
SM
877 (when (stringp name)
878 (setq name (or (imenu-find-default name prepared-index-alist) name)))
fe2908be
RS
879 (cond (prompt)
880 ((and name (imenu--in-alist name prepared-index-alist))
881 (setq prompt (format "Index item (default %s): " name)))
882 (t (setq prompt "Index item: ")))
5988bd27
SM
883 (let ((minibuffer-setup-hook minibuffer-setup-hook))
884 ;; Display the completion buffer.
885 (if (not imenu-eager-completion-buffer)
886 (add-hook 'minibuffer-setup-hook 'minibuffer-completion-help))
887 (setq name (completing-read prompt
888 prepared-index-alist
889 nil t nil 'imenu--history-list name)))
0cdb3baa
SM
890 (cond ((not (stringp name)) nil)
891 ((string= name (car imenu--rescan-item)) t)
3e062f78
RS
892 (t
893 (setq choice (assoc name prepared-index-alist))
215b077e 894 (if (imenu--subalist-p choice)
3e062f78
RS
895 (imenu--completion-buffer (cdr choice) prompt)
896 choice)))))
68e01f5a 897
0a688fd0
RS
898(defun imenu--mouse-menu (index-alist event &optional title)
899 "Let the user select from a buffer index from a mouse menu.
900
901INDEX-ALIST is the buffer index and EVENT is a mouse event.
902
32c1a22e 903Returns t for rescan and otherwise an element or subelement of INDEX-ALIST."
0c20ee61 904 (setq index-alist (imenu--split-submenus index-alist))
0cdb3baa
SM
905 (let* ((menu (imenu--split-menu index-alist (or title (buffer-name))))
906 (map (imenu--create-keymap-1 (car menu)
907 (if (< 1 (length (cdr menu)))
908 (cdr menu)
909 (cdr (car (cdr menu)))))))
910 (popup-menu map event)))
0a688fd0 911
26d6bb60 912(defun imenu-choose-buffer-index (&optional prompt alist)
0a688fd0
RS
913 "Let the user select from a buffer index and return the chosen index.
914
915If the user originally activated this function with the mouse, a mouse
0a688fd0
RS
916menu is used. Otherwise a completion buffer is used and the user is
917prompted with PROMPT.
918
26d6bb60
RS
919If you call this function with index alist ALIST, then it lets the user
920select from ALIST.
921
0ee4f8ad 922With no index alist ALIST, it calls `imenu--make-index-alist' to
26d6bb60
RS
923create the index alist.
924
5988bd27 925If `imenu-use-popup-menu' is non-nil, then the
0a688fd0
RS
926completion buffer is always used, no matter if the mouse was used or
927not.
928
7e563e04 929The returned value is of the form (INDEX-NAME . INDEX-POSITION)."
0a688fd0 930 (let (index-alist
7dea4e70 931 (mouse-triggered (listp last-nonmenu-event))
0cdb3baa 932 (result t))
0a688fd0
RS
933 ;; If selected by mouse, see to that the window where the mouse is
934 ;; really is selected.
935 (and mouse-triggered
4cde72b4 936 (not (equal last-nonmenu-event '(menu-bar)))
7dea4e70 937 (let ((window (posn-window (event-start last-nonmenu-event))))
4a840d8b 938 (or (framep window) (null window) (select-window window))))
0a688fd0
RS
939 ;; Create a list for this buffer only when needed.
940 (while (eq result t)
26d6bb60 941 (setq index-alist (if alist alist (imenu--make-index-alist)))
0a688fd0 942 (setq result
5988bd27
SM
943 (if (and imenu-use-popup-menu
944 (or (eq imenu-use-popup-menu t) mouse-triggered))
7dea4e70 945 (imenu--mouse-menu index-alist last-nonmenu-event)
0a688fd0
RS
946 (imenu--completion-buffer index-alist prompt)))
947 (and (eq result t)
5d3b0f18 948 (imenu--cleanup)
0a688fd0
RS
949 (setq imenu--index-alist nil)))
950 result))
951
2d24227e 952;;;###autoload
5d3b0f18 953(defun imenu-add-to-menubar (name)
fe2908be 954 "Add an `imenu' entry to the menu bar for the current buffer.
0a8e8bc6 955NAME is a string used to name the menu bar item.
d1757026 956See the command `imenu' for more information."
0a8e8bc6 957 (interactive "sImenu menu item name: ")
e536ef56
KH
958 (if (or (and imenu-prev-index-position-function
959 imenu-extract-index-name-function)
960 imenu-generic-expression
961 (not (eq imenu-create-index-function
962 'imenu-default-create-index-function)))
e26b2a28
DL
963 (let ((newmap (make-sparse-keymap)))
964 (set-keymap-parent newmap (current-local-map))
0cff96e7 965 (setq imenu--last-menubar-index-alist nil)
f1d7969d 966 (define-key newmap [menu-bar index]
fbfb705c 967 `(menu-item ,name ,(make-sparse-keymap "Imenu")))
e26b2a28 968 (use-local-map newmap)
e536ef56 969 (add-hook 'menu-bar-update-hook 'imenu-update-menubar))
37954a9a 970 (error "The mode `%s' does not support Imenu" mode-name)))
0a8e8bc6 971
fe2908be
RS
972;;;###autoload
973(defun imenu-add-menubar-index ()
974 "Add an Imenu \"Index\" entry on the menu bar for the current buffer.
975
976A trivial interface to `imenu-add-to-menubar' suitable for use in a hook."
977 (interactive)
978 (imenu-add-to-menubar "Index"))
979
6d7a4832
KH
980(defvar imenu-buffer-menubar nil)
981
9fb980fc 982(defvar imenu-menubar-modified-tick 0
a3841d3b
RS
983 "The value of (buffer-modified-tick) as of last call to `imenu-update-menubar'.
984This value becomes local in every buffer when it is set.")
9fb980fc 985(make-variable-buffer-local 'imenu-menubar-modified-tick)
a3841d3b 986
0a8e8bc6 987(defun imenu-update-menubar ()
9fb980fc
RS
988 (when (and (current-local-map)
989 (keymapp (lookup-key (current-local-map) [menu-bar index]))
990 (not (eq (buffer-modified-tick)
991 imenu-menubar-modified-tick)))
992 (setq imenu-menubar-modified-tick (buffer-modified-tick))
993 (let ((index-alist (imenu--make-index-alist t)))
994 ;; Don't bother updating if the index-alist has not changed
995 ;; since the last time we did it.
996 (unless (equal index-alist imenu--last-menubar-index-alist)
997 (let (menu menu1 old)
998 (setq imenu--last-menubar-index-alist index-alist)
999 (setq index-alist (imenu--split-submenus index-alist))
1000 (setq menu (imenu--split-menu index-alist
1001 (buffer-name)))
1002 (setq menu1 (imenu--create-keymap-1 (car menu)
1003 (if (< 1 (length (cdr menu)))
1004 (cdr menu)
1005 (cdr (car (cdr menu))))))
1006 (setq old (lookup-key (current-local-map) [menu-bar index]))
1007 (setcdr old (cdr menu1)))))))
0a8e8bc6
KH
1008
1009(defun imenu--menubar-select (item)
0cdb3baa 1010 "Use Imenu to select the function or variable named in this menu ITEM."
37954a9a 1011 (if (equal item imenu--rescan-item)
e63679b8
RS
1012 (progn
1013 (imenu--cleanup)
1014 (setq imenu--index-alist nil)
0cdb3baa
SM
1015 (imenu-update-menubar)
1016 t)
1017 (imenu item)
1018 nil))
5d3b0f18 1019
37954a9a 1020(defun imenu-default-goto-function (name position &optional rest)
fe2908be
RS
1021 "Move the point to the given position.
1022
1023NAME is ignored. POSITION is where to move. REST is also ignored.
1024The ignored args just make this function have the same interface as a
1025function placed in a special index-item."
e7c8378c
RS
1026 (if (or (< position (point-min))
1027 (> position (point-max)))
37954a9a
RS
1028 ;; widen if outside narrowing
1029 (widen))
e7c8378c 1030 (goto-char position))
37954a9a 1031
68e01f5a 1032;;;###autoload
6c1bf12b 1033(defun imenu (index-item)
68e01f5a 1034 "Jump to a place in the buffer chosen using a buffer menu or mouse menu.
fe2908be
RS
1035INDEX-ITEM specifies the position. See `imenu-choose-buffer-index'
1036for more information."
01e980fb 1037 (interactive (list (imenu-choose-buffer-index)))
0a8e8bc6
KH
1038 ;; Convert a string to an alist element.
1039 (if (stringp index-item)
1040 (setq index-item (assoc index-item (imenu--make-index-alist))))
0cdb3baa
SM
1041 (when index-item
1042 (push-mark)
1043 (let* ((is-special-item (listp (cdr index-item)))
1044 (function
1045 (if is-special-item
1046 (nth 2 index-item) imenu-default-goto-function))
1047 (position (if is-special-item
1048 (cadr index-item) (cdr index-item)))
1049 (rest (if is-special-item (cddr index-item))))
1050 (apply function (car index-item) position rest))
1051 (run-hooks 'imenu-after-jump-hook)))
5d3b0f18 1052
f1ed9461
DL
1053(dolist (mess
1054 '("^No items suitable for an index found in this buffer$"
1055 "^This buffer cannot use `imenu-default-create-index-function'$"
1056 "^The mode `.*' does not support Imenu$"))
1057 (add-to-list 'debug-ignored-errors mess))
1058
0a688fd0
RS
1059(provide 'imenu)
1060
1061;;; imenu.el ends here