(report-emacs-bug): Use a different address for pretest versions.
[bpt/emacs.git] / lisp / imenu.el
CommitLineData
0a688fd0
RS
1;;; imenu.el --- Framework for mode-specific buffer indexes.
2
f5632218 3;; Copyright (C) 1994, 1995, 1996 Free Software Foundation, Inc.
0a688fd0
RS
4
5;; Author: Ake Stenhoff <etxaksf@aom.ericsson.se>
6;; Lars Lindberg <lli@sypro.cap.se>
7;; Created: 8 Feb 1994
0a688fd0 8;; Keywords: tools
b578f267
EN
9
10;; This file is part of GNU Emacs.
11
12;; GNU Emacs is free software; you can redistribute it and/or modify
0a688fd0
RS
13;; it under the terms of the GNU General Public License as published by
14;; the Free Software Foundation; either version 2, or (at your option)
15;; any later version.
b578f267
EN
16
17;; GNU Emacs is distributed in the hope that it will be useful,
0a688fd0
RS
18;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20;; GNU General Public License for more details.
b578f267 21
0a688fd0 22;; You should have received a copy of the GNU General Public License
b578f267
EN
23;; along with GNU Emacs; see the file COPYING. If not, write to the
24;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
25;; Boston, MA 02111-1307, USA.
0a688fd0
RS
26
27;;; Commentary:
b578f267 28
0a688fd0
RS
29;; Purpose of this package:
30;; To present a framework for mode-specific buffer indexes.
31;; A buffer index is an alist of names and buffer positions.
32;; For instance all functions in a C-file and their positions.
33;;
34;; How it works:
35
36;; A mode-specific function is called to generate the index. It is
37;; then presented to the user, who can choose from this index.
38;;
39;; The package comes with a set of example functions for how to
40;; utilize this package.
41
2d24227e
RS
42;; There are *examples* for index gathering functions/regular
43;; expressions for C/C++ and Lisp/Emacs Lisp but it is easy to
44;; customize for other modes. A function for jumping to the chosen
45;; index position is also supplied.
0a688fd0 46
26d6bb60
RS
47;;; Thanks goes to
48;; [simon] - Simon Leinen simon@lia.di.epfl.ch
49;; [dean] - Dean Andrews ada@unison.com
5d3b0f18 50;; [alon] - Alon Albert al@mercury.co.il
7804cd27 51;; [greg] - Greg Thompson gregt@porsche.visix.COM
615b306c 52;; [wolfgang] - Wolfgang Bangerth zcg51122@rpool1.rus.uni-stuttgart.de
056ab244 53;; [kai] - Kai Grossjohann grossjoh@linus.informatik.uni-dortmund.de
af447694 54;; [david] - David M. Smith dsmith@stats.adelaide.edu.au
2d24227e
RS
55;; [christian] - Christian Egli Christian.Egli@hcsd.hac.com
56;; [karl] - Karl Fogel kfogel@floss.life.uiuc.edu
57
0a688fd0 58;;; Code
b578f267 59
0ee4f8ad 60(eval-when-compile (require 'cl))
0a688fd0
RS
61
62;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
63;;;
64;;; Customizable variables
65;;;
66;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2d24227e 67(defvar imenu-use-keymap-menu nil
6c1bf12b 68 "*Non-nil means use a keymap when making the mouse menu.")
2d24227e
RS
69
70(defvar imenu-auto-rescan nil
6c1bf12b 71 "*Non-nil means Imenu should always rescan the buffers.")
2d24227e
RS
72
73(defvar imenu-auto-rescan-maxout 60000
74 "* auto-rescan is disabled in buffers larger than this.
6c1bf12b 75This variable is buffer-local.")
0a688fd0
RS
76
77(defvar imenu-always-use-completion-buffer-p nil
78 "*Set this to non-nil for displaying the index in a completion buffer.
79
80Non-nil means always display the index in a completion buffer.
81Nil means display the index as a mouse menu when the mouse was
af447694
RS
82used to invoke `imenu'.
83`never' means never automatically display a listing of any kind.")
0a688fd0
RS
84
85(defvar imenu-sort-function nil
86 "*The function to use for sorting the index mouse-menu.
87
88Affects only the mouse index menu.
89
90Set this to nil if you don't want any sorting (faster).
91The items in the menu are then presented in the order they were found
92in the buffer.
93
0ee4f8ad 94Set it to `imenu--sort-by-name' if you want alphabetic sorting.
0a688fd0
RS
95
96The function should take two arguments and return T if the first
97element should come before the second. The arguments are cons cells;
a4e104bf 98\(NAME . POSITION). Look at `imenu--sort-by-name' for an example.")
0a688fd0
RS
99
100(defvar imenu-max-items 25
0c20ee61 101 "*Maximum number of elements in an mouse menu for Imenu.")
0a688fd0 102
6c1bf12b 103(defvar imenu-scanning-message "Scanning buffer for index (%3d%%)"
0a688fd0 104 "*Progress message during the index scanning of the buffer.
7dea4e70 105If non-nil, user gets a message during the scanning of the buffer
0a688fd0
RS
106
107Relevant only if the mode-specific function that creates the buffer
0ee4f8ad 108index use `imenu-progress-message'.")
0a688fd0
RS
109
110(defvar imenu-space-replacement "^"
111 "*The replacement string for spaces in index names.
112Used when presenting the index in a completion-buffer to make the
113names work as tokens.")
114
115(defvar imenu-level-separator ":"
116 "*The separator between index names of different levels.
117Used for making mouse-menu titles and for flattening nested indexes
118with name concatenation.")
119
2d24227e 120;;;###autoload
615b306c 121(defvar imenu-generic-expression nil
2d24227e
RS
122 "The regex pattern to use for creating a buffer index.
123
124If non-nil this pattern is passed to `imenu-create-index-with-pattern'
125to create a buffer index.
126
127It is an alist with elements that look like this: (MENU-TITLE
128REGEXP INDEX).
129
130MENU-TITLE is a string used as the title for the submenu or nil if the
131entries are not nested.
132
133REGEXP is a regexp that should match a construct in the buffer that is
6c1bf12b
RS
134to be displayed in the menu; i.e., function or variable definitions,
135etc. It contains a substring which is the name to appear in the
136menu. See the info section on Regexps for more information.
2d24227e
RS
137
138INDEX points to the substring in REGEXP that contains the name (of the
139function, variable or type) that is to appear in the menu.
615b306c 140
2d24227e
RS
141For emacs-lisp-mode for example PATTERN would look like:
142
c30c8a0c
RS
143'((nil \"^\\\\s-*(def\\\\(un\\\\|subst\\\\|macro\\\\|advice\\\\)\\\\s-+\\\\([-A-Za-z0-9+]+\\\\)\" 2)
144 (\"*Vars*\" \"^\\\\s-*(def\\\\(var\\\\|const\\\\)\\\\s-+\\\\([-A-Za-z0-9+]+\\\\)\" 2)
145 (\"*Types*\" \"^\\\\s-*(def\\\\(type\\\\|struct\\\\|class\\\\|ine-condition\\\\)\\\\s-+\\\\([-A-Za-z0-9+]+\\\\)\" 2))
2d24227e
RS
146
147The variable is buffer-local.")
148
6c1bf12b 149(make-variable-buffer-local 'imenu-generic-expression)
615b306c 150
0a688fd0
RS
151;;;; Hooks
152
153(defvar imenu-create-index-function 'imenu-default-create-index-function
154 "The function to use for creating a buffer index.
155
156It should be a function that takes no arguments and returns an index
6c1bf12b
RS
157of the current buffer as an alist. The elements in the alist look
158like: (INDEX-NAME . INDEX-POSITION). You may also nest index list like
a4e104bf 159\(INDEX-NAME . INDEX-ALIST).
0a688fd0 160
0ee4f8ad 161This function is called within a `save-excursion'.
0a688fd0
RS
162
163The variable is buffer-local.")
164(make-variable-buffer-local 'imenu-create-index-function)
165
68e01f5a 166(defvar imenu-prev-index-position-function 'beginning-of-defun
0a688fd0
RS
167 "Function for finding the next index position.
168
0ee4f8ad
RS
169If `imenu-create-index-function' is set to
170`imenu-default-create-index-function', then you must set this variable
0a688fd0
RS
171to a function that will find the next index, looking backwards in the
172file.
173
174The function should leave point at the place to be connected to the
6c1bf12b 175index and it should return nil when it doesn't find another index.")
68e01f5a 176(make-variable-buffer-local 'imenu-prev-index-position-function)
0a688fd0 177
68e01f5a 178(defvar imenu-extract-index-name-function nil
0a688fd0
RS
179 "Function for extracting the index name.
180
0a688fd0 181This function is called after the function pointed out by
68e01f5a
RS
182`imenu-prev-index-position-function'.")
183(make-variable-buffer-local 'imenu-extract-index-name-function)
0a688fd0 184
615b306c
KH
185;;;
186;;; Macro to display a progress message.
187;;; RELPOS is the relative position to display.
188;;; If RELPOS is nil, then the relative position in the buffer
189;;; is calculated.
190;;; PREVPOS is the variable in which we store the last position displayed.
191(defmacro imenu-progress-message (prevpos &optional relpos reverse)
192 (` (and
193 imenu-scanning-message
194 (let ((pos (, (if relpos
195 relpos
196 (` (imenu--relative-position (, reverse)))))))
197 (if (, (if relpos t
198 (` (> pos (+ 5 (, prevpos))))))
199 (progn
200 (message imenu-scanning-message pos)
201 (setq (, prevpos) pos)))))))
202
203
204;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
205;;;;
206;;;; Some examples of functions utilizing the framework of this
207;;;; package.
208;;;;
209;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
210
e064a4f9 211;; Return the current/previous sexp and the location of the sexp (its
615b306c
KH
212;; beginning) without moving the point.
213(defun imenu-example--name-and-position ()
214 (save-excursion
215 (forward-sexp -1)
216 (let ((beg (point))
217 (end (progn (forward-sexp) (point)))
218 (marker (make-marker)))
219 (set-marker marker beg)
220 (cons (buffer-substring beg end)
221 marker))))
222
223;;;
224;;; Lisp
225;;;
226
227(defun imenu-example--lisp-extract-index-name ()
228 ;; Example of a candidate for `imenu-extract-index-name-function'.
229 ;; This will generate a flat index of definitions in a lisp file.
230 (save-match-data
231 (and (looking-at "(def")
232 (condition-case nil
233 (progn
234 (down-list 1)
235 (forward-sexp 2)
236 (let ((beg (point))
237 (end (progn (forward-sexp -1) (point))))
238 (buffer-substring beg end)))
239 (error nil)))))
240
241(defun imenu-example--create-lisp-index ()
242 ;; Example of a candidate for `imenu-create-index-function'.
243 ;; It will generate a nested index of definitions.
244 (let ((index-alist '())
245 (index-var-alist '())
246 (index-type-alist '())
247 (index-unknown-alist '())
248 prev-pos)
249 (goto-char (point-max))
250 (imenu-progress-message prev-pos 0)
251 ;; Search for the function
252 (while (beginning-of-defun)
253 (imenu-progress-message prev-pos nil t)
254 (save-match-data
255 (and (looking-at "(def")
256 (save-excursion
257 (down-list 1)
258 (cond
259 ((looking-at "def\\(var\\|const\\)")
260 (forward-sexp 2)
261 (push (imenu-example--name-and-position)
262 index-var-alist))
263 ((looking-at "def\\(un\\|subst\\|macro\\|advice\\)")
264 (forward-sexp 2)
265 (push (imenu-example--name-and-position)
266 index-alist))
267 ((looking-at "def\\(type\\|struct\\|class\\|ine-condition\\)")
268 (forward-sexp 2)
269 (if (= (char-after (1- (point))) ?\))
270 (progn
271 (forward-sexp -1)
272 (down-list 1)
273 (forward-sexp 1)))
274 (push (imenu-example--name-and-position)
275 index-type-alist))
276 (t
277 (forward-sexp 2)
278 (push (imenu-example--name-and-position)
279 index-unknown-alist)))))))
280 (imenu-progress-message prev-pos 100)
281 (and index-var-alist
0c20ee61 282 (push (cons "Variables" index-var-alist)
615b306c
KH
283 index-alist))
284 (and index-type-alist
0c20ee61 285 (push (cons "Types" index-type-alist)
615b306c
KH
286 index-alist))
287 (and index-unknown-alist
0c20ee61 288 (push (cons "Syntax-unknown" index-unknown-alist)
615b306c
KH
289 index-alist))
290 index-alist))
291
615b306c
KH
292;; Regular expression to find C functions
293(defvar imenu-example--function-name-regexp-c
294 (concat
295 "^[a-zA-Z0-9]+[ \t]?" ; type specs; there can be no
296 "\\([a-zA-Z0-9_*]+[ \t]+\\)?" ; more than 3 tokens, right?
297 "\\([a-zA-Z0-9_*]+[ \t]+\\)?"
298 "\\([*&]+[ \t]*\\)?" ; pointer
299 "\\([a-zA-Z0-9_*]+\\)[ \t]*(" ; name
300 ))
301
302(defun imenu-example--create-c-index (&optional regexp)
303 (let ((index-alist '())
304 prev-pos char)
305 (goto-char (point-min))
306 (imenu-progress-message prev-pos 0)
307 ;; Search for the function
308 (save-match-data
309 (while (re-search-forward
310 (or regexp imenu-example--function-name-regexp-c)
311 nil t)
312 (imenu-progress-message prev-pos)
313 (backward-up-list 1)
314 (save-excursion
315 (goto-char (scan-sexps (point) 1))
316 (setq char (following-char)))
317 ;; Skip this function name if it is a prototype declaration.
318 (if (not (eq char ?\;))
319 (push (imenu-example--name-and-position) index-alist))))
320 (imenu-progress-message prev-pos 100)
321 (nreverse index-alist)))
322
2d24227e 323
0a688fd0
RS
324;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
325;;;
326;;; Internal variables
327;;;
328;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
329
330;; The item to use in the index for rescanning the buffer.
331(defconst imenu--rescan-item '("*Rescan*" . -99))
332
333;; The latest buffer index.
334;; Buffer local.
335(defvar imenu--index-alist nil)
336(make-variable-buffer-local 'imenu--index-alist)
337
0a8e8bc6
KH
338;; The latest buffer index used to update the menu bar menu.
339(defvar imenu--last-menubar-index-alist nil)
340(make-variable-buffer-local 'imenu--last-menubar-index-alist)
341
0a688fd0 342;; History list for 'jump-to-function-in-buffer'.
6c1bf12b 343;; Making this buffer local caused it not to work!
0a688fd0 344(defvar imenu--history-list nil)
0a688fd0
RS
345
346;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
347;;;
348;;; Internal support functions
349;;;
350;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
351
352;;;
353;;; Sort function
354;;; Sorts the items depending on their index name.
355;;; An item look like (NAME . POSITION).
356;;;
357(defun imenu--sort-by-name (item1 item2)
358 (string-lessp (car item1) (car item2)))
359
360(defun imenu--relative-position (&optional reverse)
361 ;; Support function to calculate relative position in buffer
362 ;; Beginning of buffer is 0 and end of buffer is 100
363 ;; If REVERSE is non-nil then the beginning is 100 and the end is 0.
364 (let ((pos (point))
365 (total (buffer-size)))
366 (and reverse (setq pos (- total pos)))
367 (if (> total 50000)
368 ;; Avoid overflow from multiplying by 100!
369 (/ (1- pos) (max (/ total 100) 1))
370 (/ (* 100 (1- pos)) (max total 1)))))
371
0a688fd0
RS
372;; Split LIST into sublists of max length N.
373;; Example (imenu--split '(1 2 3 4 5 6 7 8) 3)-> '((1 2 3) (4 5 6) (7 8))
374(defun imenu--split (list n)
375 (let ((remain list)
376 (result '())
377 (sublist '())
378 (i 0))
379 (while remain
380 (push (pop remain) sublist)
381 (incf i)
382 (and (= i n)
383 ;; We have finished a sublist
384 (progn (push (nreverse sublist) result)
385 (setq i 0)
386 (setq sublist '()))))
387 ;; There might be a sublist (if the length of LIST mod n is != 0)
388 ;; that has to be added to the result list.
389 (and sublist
390 (push (nreverse sublist) result))
391 (nreverse result)))
392
0c20ee61
RS
393;;; Split the alist MENULIST into a nested alist, if it is long enough.
394;;; In any case, add TITLE to the front of the alist.
0a688fd0 395(defun imenu--split-menu (menulist title)
0c20ee61
RS
396 (if (> (length menulist) imenu-max-items)
397 (let ((count 0))
398 (cons title
399 (mapcar
400 (function
401 (lambda (menu)
402 (cons (format "(%s-%d)" title (setq count (1+ count)))
403 menu)))
404 (imenu--split menulist imenu-max-items))))
405 (cons title menulist)))
406
407;;; Split up each long alist that are nested within ALIST
408;;; into nested alists.
409(defun imenu--split-submenus (alist)
410 (mapcar (function (lambda (elt)
411 (if (and (consp elt)
412 (stringp (car elt))
413 (listp (cdr elt)))
414 (imenu--split-menu (cdr elt) (car elt))
415 elt)))
416 alist))
0a688fd0
RS
417
418;;;
419;;; Find all items in this buffer that should be in the index.
420;;; Returns an alist on the form
421;;; ((NAME . POSITION) (NAME . POSITION) ...)
422;;;
423
0a8e8bc6 424(defun imenu--make-index-alist (&optional noerror)
0a688fd0 425 ;; Create a list for this buffer only when needed.
2d24227e
RS
426 (or (and imenu--index-alist
427 (or (not imenu-auto-rescan)
428 (and imenu-auto-rescan
429 (> (buffer-size) imenu-auto-rescan-maxout))))
0a688fd0
RS
430 ;; Get the index
431 (setq imenu--index-alist
432 (save-excursion
433 (funcall imenu-create-index-function))))
0a8e8bc6 434 (or imenu--index-alist noerror
6c1bf12b 435 (error "No items suitable for an index found in this buffer"))
0a8e8bc6
KH
436 (or imenu--index-alist
437 (setq imenu--index-alist (list nil)))
0a688fd0
RS
438 ;; Add a rescan option to the index.
439 (cons imenu--rescan-item imenu--index-alist))
5d3b0f18
RS
440;;;
441;;; Find all markers in alist and makes
442;;; them point nowhere.
443;;;
444(defun imenu--cleanup (&optional alist)
445 ;; Sets the markers in imenu--index-alist
446 ;; point nowhere.
447 ;; if alist is provided use that list.
4818d210
RS
448 (or alist
449 (setq alist imenu--index-alist))
450 (and alist
7804cd27 451 (mapcar
5d3b0f18
RS
452 (function
453 (lambda (item)
454 (cond
455 ((markerp (cdr item))
456 (set-marker (cdr item) nil))
4818d210 457 ((consp (cdr item))
5d3b0f18 458 (imenu--cleanup (cdr item))))))
4818d210 459 alist)
615b306c
KH
460 t))
461
0a8e8bc6 462(defun imenu--create-keymap-2 (alist counter &optional commands)
2d24227e
RS
463 (let ((map nil))
464 (mapcar
465 (function
466 (lambda (item)
467 (cond
468 ((listp (cdr item))
fc0ac20d
KH
469 (append (list (setq counter (1+ counter))
470 (car item) 'keymap (car item))
0a8e8bc6 471 (imenu--create-keymap-2 (cdr item) (+ counter 10) commands)))
2d24227e 472 (t
fc0ac20d
KH
473 (let ((end (if commands `(lambda () (interactive)
474 (imenu--menubar-select ',item))
0a8e8bc6 475 (cons '(nil) t))))
2d24227e
RS
476 (cons (car item)
477 (cons (car item) end))))
478 )))
479 alist)))
480
0a8e8bc6
KH
481;; If COMMANDS is non-nil, make a real keymap
482;; with a real command used as the definition.
483;; If it is nil, make something suitable for x-popup-menu.
484(defun imenu--create-keymap-1 (title alist &optional commands)
485 (append (list 'keymap title) (imenu--create-keymap-2 alist 0 commands)))
2d24227e
RS
486
487
488(defun imenu--in-alist (str alist)
489 "Check whether the string STR is contained in multi-level ALIST."
490 (let (elt head tail res)
491 (setq res nil)
492 (while alist
493 (setq elt (car alist)
494 tail (cdr elt)
495 alist (cdr alist)
496 head (car elt))
497 (if (string= str head)
498 (setq alist nil res elt)
499 (if (and (listp tail)
500 (setq res (imenu--in-alist str tail)))
501 (setq alist nil))))
502 res))
503
0a688fd0
RS
504(defun imenu-default-create-index-function ()
505 "*Wrapper for index searching functions.
506
507Moves point to end of buffer and then repeatedly calls
68e01f5a 508`imenu-prev-index-position-function' and `imenu-extract-index-name-function'.
0a688fd0 509Their results are gathered into an index alist."
3e062f78
RS
510 ;; These should really be done by setting imenu-create-index-function
511 ;; in these major modes. But save that change for later.
615b306c
KH
512 (cond ((and (fboundp imenu-prev-index-position-function)
513 (fboundp imenu-extract-index-name-function))
3e062f78 514 (let ((index-alist '())
615b306c 515 prev-pos name)
3e062f78 516 (goto-char (point-max))
7dea4e70 517 (imenu-progress-message prev-pos 0 t)
3e062f78
RS
518 ;; Search for the function
519 (while (funcall imenu-prev-index-position-function)
7dea4e70 520 (imenu-progress-message prev-pos nil t)
3e062f78
RS
521 (save-excursion
522 (setq name (funcall imenu-extract-index-name-function)))
523 (and (stringp name)
524 (push (cons name (point)) index-alist)))
7dea4e70 525 (imenu-progress-message prev-pos 100 t)
615b306c
KH
526 index-alist))
527 ;; Use generic expression if possible.
528 ((and imenu-generic-expression)
e064a4f9 529 (imenu--generic-function imenu-generic-expression))
615b306c
KH
530 (t
531 (error "The mode \"%s\" does not take full advantage of imenu.el yet."
532 mode-name))))
0a688fd0
RS
533
534(defun imenu--replace-spaces (name replacement)
535 ;; Replace all spaces in NAME with REPLACEMENT.
536 ;; That second argument should be a string.
537 (mapconcat
538 (function
539 (lambda (ch)
540 (if (char-equal ch ?\ )
541 replacement
542 (char-to-string ch))))
543 name
544 ""))
545
546(defun imenu--flatten-index-alist (index-alist &optional concat-names prefix)
547 ;; Takes a nested INDEX-ALIST and returns a flat index alist.
548 ;; If optional CONCAT-NAMES is non-nil, then a nested index has its
549 ;; name and a space concatenated to the names of the children.
550 ;; Third argument PREFIX is for internal use only.
551 (mapcan
552 (function
553 (lambda (item)
554 (let* ((name (car item))
555 (pos (cdr item))
556 (new-prefix (and concat-names
557 (if prefix
558 (concat prefix imenu-level-separator name)
559 name))))
560 (cond
5d3b0f18 561 ((or (markerp pos) (numberp pos))
0a688fd0
RS
562 (list (cons new-prefix pos)))
563 (t
564 (imenu--flatten-index-alist pos new-prefix))))))
565 index-alist))
566
615b306c
KH
567;;;
568;;; Generic index gathering function.
569;;;
2d24227e
RS
570
571(defun imenu--generic-function (patterns)
572;; Built on some ideas that Erik Naggum <erik@naggum.no> once posted
573;; to comp.emacs
574 "Return an index of the current buffer as an alist.
575
576PATTERN is an alist with elements that look like this: (MENU-TITLE
6c1bf12b 577REGEXP INDEX).
2d24227e
RS
578
579MENU-TITLE is a string used as the title for the submenu or nil if the
580entries are not nested.
581
582REGEXP is a regexp that should match a construct in the buffer that is
6c1bf12b
RS
583to be displayed in the menu; i.e., function or variable definitions,
584etc. It contains a substring which is the name to appear in the
585menu. See the info section on Regexps for more information.
2d24227e
RS
586
587INDEX points to the substring in REGEXP that contains the name (of the
588function, variable or type) that is to appear in the menu.
589
590For emacs-lisp-mode for example PATTERN would look like:
591
c30c8a0c
RS
592'((nil \"^\\\\s-*(def\\\\(un\\\\|subst\\\\|macro\\\\|advice\\\\)\\\\s-+\\\\([-A-Za-z0-9]+\\\\)\" 2)
593 (\"*Vars*\" \"^\\\\s-*(def\\\\(var\\\\|const\\\\)\\\\s-+\\\\([-A-Za-z0-9]+\\\\)\" 2)
594 (\"*Types*\" \"^\\\\s-*(def\\\\(type\\\\|struct\\\\|class\\\\|ine-condition\\\\)\\\\s-+\\\\([-A-Za-z0-9]+\\\\)\" 2))'
2d24227e 595
6c1bf12b
RS
596Returns an index of the current buffer as an alist. The elements in
597the alist look like: (INDEX-NAME . INDEX-POSITION). They may also be
2d24227e
RS
598nested index lists like (INDEX-NAME . INDEX-ALIST) depending on
599pattern.
600
601\(imenu--generic-function PATTERN\)."
602
603 (let ((index-alist (list 'dummy))
604 (found nil)
605 (global-regexp
606 (concat "\\("
607 (mapconcat
608 (function (lambda (pattern) (identity (cadr pattern))))
609 patterns "\\)\\|\\(")
610 "\\)"))
611 prev-pos)
612
615b306c
KH
613 (goto-char (point-max))
614 (imenu-progress-message prev-pos 0 t)
2d24227e
RS
615 (save-match-data
616 (while (re-search-backward global-regexp nil t)
617 (imenu-progress-message prev-pos nil t)
618 (setq found nil)
619 (save-excursion
620 (goto-char (match-beginning 0))
621 (mapcar
622 (function
623 (lambda (pat)
624 (let ((menu-title (car pat))
625 (regexp (cadr pat))
626 (index (caddr pat)))
627 (if (and (not found) ; Only allow one entry;
628 (looking-at regexp))
629 (let ((beg (match-beginning index))
630 (end (match-end index)))
631 (setq found t)
632 (push
e1de3014 633 (cons (buffer-substring-no-properties beg end) beg)
2d24227e 634 (cdr
0c20ee61 635 (or (assoc menu-title index-alist)
2d24227e 636 (car (push
0c20ee61 637 (cons menu-title '())
2d24227e 638 index-alist))))))))))
0c20ee61
RS
639 patterns))))
640 (imenu-progress-message prev-pos 100 t)
641 (let ((main-element (assq nil index-alist)))
642 (nconc (delq main-element (delq 'dummy index-alist)) main-element))))
615b306c 643
0a688fd0
RS
644;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
645;;;
646;;; The main functions for this package!
647;;;
648;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
649
650(defun imenu--completion-buffer (index-alist &optional prompt)
651 "Let the user select from INDEX-ALIST in a completion buffer with PROMPT.
652
653Returns t for rescan and otherwise a position number."
654 ;; Create a list for this buffer only when needed.
655 (let (name choice
615b306c
KH
656 (prepared-index-alist
657 (mapcar
658 (function
659 (lambda (item)
660 (cons (imenu--replace-spaces (car item) imenu-space-replacement)
661 (cdr item))))
662 index-alist)))
af447694
RS
663 (if (eq imenu-always-use-completion-buffer-p 'never)
664 (setq name (completing-read (or prompt "Index item: ")
665 prepared-index-alist
666 nil t nil 'imenu--history-list))
667 (save-window-excursion
668 ;; Display the completion buffer
669 (with-output-to-temp-buffer "*Completions*"
670 (display-completion-list
671 (all-completions "" prepared-index-alist )))
672 (let ((minibuffer-setup-hook
673 (function (lambda ()
674 (let ((buffer (current-buffer)))
675 (save-excursion
676 (set-buffer "*Completions*")
677 (setq completion-reference-buffer buffer)))))))
678 ;; Make a completion question
679 (setq name (completing-read (or prompt "Index item: ")
680 prepared-index-alist
681 nil t nil 'imenu--history-list)))))
3e062f78
RS
682 (cond ((not (stringp name))
683 nil)
684 ((string= name (car imenu--rescan-item))
685 t)
686 (t
687 (setq choice (assoc name prepared-index-alist))
688 (if (listp (cdr choice))
689 (imenu--completion-buffer (cdr choice) prompt)
690 choice)))))
68e01f5a 691
0a688fd0
RS
692(defun imenu--mouse-menu (index-alist event &optional title)
693 "Let the user select from a buffer index from a mouse menu.
694
695INDEX-ALIST is the buffer index and EVENT is a mouse event.
696
697Returns t for rescan and otherwise a position number."
0c20ee61 698 (setq index-alist (imenu--split-submenus index-alist))
0a688fd0
RS
699 (let* ((menu (imenu--split-menu
700 (if imenu-sort-function
0ee4f8ad
RS
701 (sort
702 (let ((res nil)
703 (oldlist index-alist))
704 ;; Copy list method from the cl package `copy-list'
705 (while (consp oldlist) (push (pop oldlist) res))
706 (prog1 (nreverse res) (setcdr res oldlist)))
707 imenu-sort-function)
0a688fd0
RS
708 index-alist)
709 (or title (buffer-name))))
710 position)
2d24227e
RS
711 (and imenu-use-keymap-menu
712 (setq menu (imenu--create-keymap-1 (car menu)
713 (if (< 1 (length (cdr menu)))
714 (cdr menu)
715 (cdr (cadr menu))))))
cd7ac8e8
RS
716
717 (or imenu-use-keymap-menu
718 (setq menu (list "Imenu" (delq nil menu))))
0a688fd0 719 (setq position (x-popup-menu event menu))
2d24227e
RS
720 (if imenu-use-keymap-menu
721 (progn
722 (cond
723 ((and (listp position)
724 (numberp (car position))
725 (stringp (nth (1- (length position)) position)))
726 (setq position (nth (1- (length position)) position)))
727 ((and (stringp (car position))
728 (null (cdr position)))
729 (setq position (car position))))))
0a688fd0
RS
730 (cond
731 ((eq position nil)
732 position)
26d6bb60 733 ((listp position)
0a688fd0
RS
734 (imenu--mouse-menu position event
735 (if title
736 (concat title imenu-level-separator
737 (car (rassq position index-alist)))
738 (car (rassq position index-alist)))))
2d24227e
RS
739 ((stringp position)
740 (or (string= position (car imenu--rescan-item))
741 (imenu--in-alist position index-alist)))
742 ((or (= position (cdr imenu--rescan-item))
743 (and (stringp position)
744 (string= position (car imenu--rescan-item))))
0a688fd0
RS
745 t)
746 (t
747 (rassq position index-alist)))))
748
26d6bb60 749(defun imenu-choose-buffer-index (&optional prompt alist)
0a688fd0
RS
750 "Let the user select from a buffer index and return the chosen index.
751
752If the user originally activated this function with the mouse, a mouse
0a688fd0
RS
753menu is used. Otherwise a completion buffer is used and the user is
754prompted with PROMPT.
755
26d6bb60
RS
756If you call this function with index alist ALIST, then it lets the user
757select from ALIST.
758
0ee4f8ad 759With no index alist ALIST, it calls `imenu--make-index-alist' to
26d6bb60
RS
760create the index alist.
761
0ee4f8ad 762If `imenu-always-use-completion-buffer-p' is non-nil, then the
0a688fd0
RS
763completion buffer is always used, no matter if the mouse was used or
764not.
765
766The returned value is on the form (INDEX-NAME . INDEX-POSITION)."
767 (let (index-alist
7dea4e70 768 (mouse-triggered (listp last-nonmenu-event))
0a688fd0
RS
769 (result t) )
770 ;; If selected by mouse, see to that the window where the mouse is
771 ;; really is selected.
772 (and mouse-triggered
4cde72b4 773 (not (equal last-nonmenu-event '(menu-bar)))
7dea4e70 774 (let ((window (posn-window (event-start last-nonmenu-event))))
4a840d8b 775 (or (framep window) (null window) (select-window window))))
0a688fd0
RS
776 ;; Create a list for this buffer only when needed.
777 (while (eq result t)
26d6bb60 778 (setq index-alist (if alist alist (imenu--make-index-alist)))
0a688fd0
RS
779 (setq result
780 (if (and mouse-triggered
781 (not imenu-always-use-completion-buffer-p))
7dea4e70 782 (imenu--mouse-menu index-alist last-nonmenu-event)
0a688fd0
RS
783 (imenu--completion-buffer index-alist prompt)))
784 (and (eq result t)
5d3b0f18 785 (imenu--cleanup)
0a688fd0
RS
786 (setq imenu--index-alist nil)))
787 result))
788
2d24227e 789;;;###autoload
5d3b0f18 790(defun imenu-add-to-menubar (name)
d1757026 791 "Adds an `imenu' entry to the menu bar for the current buffer.
0a8e8bc6 792NAME is a string used to name the menu bar item.
d1757026 793See the command `imenu' for more information."
0a8e8bc6 794 (interactive "sImenu menu item name: ")
d1757026
RS
795 (let ((newmap (make-sparse-keymap))
796 (menu-bar (lookup-key (current-local-map) [menu-bar])))
797 (define-key newmap [menu-bar]
798 (append (make-sparse-keymap) menu-bar))
799 (define-key newmap [menu-bar index]
800 (cons name (nconc (make-sparse-keymap "Imenu")
801 (make-sparse-keymap))))
802 (use-local-map (append newmap (current-local-map))))
0a8e8bc6
KH
803 (add-hook 'menu-bar-update-hook 'imenu-update-menubar))
804
6d7a4832
KH
805(defvar imenu-buffer-menubar nil)
806
0a8e8bc6
KH
807(defun imenu-update-menubar ()
808 (and (current-local-map)
809 (keymapp (lookup-key (current-local-map) [menu-bar index]))
810 (let ((index-alist (imenu--make-index-alist t)))
811 ;; Don't bother updating if the index-alist has not changed
812 ;; since the last time we did it.
813 (or (equal index-alist imenu--last-menubar-index-alist)
814 (let (menu menu1 old)
815 (setq imenu--last-menubar-index-alist index-alist)
0c20ee61 816 (setq index-alist (imenu--split-submenus index-alist))
0a8e8bc6
KH
817 (setq menu (imenu--split-menu
818 (if imenu-sort-function
819 (sort
820 (let ((res nil)
821 (oldlist index-alist))
822 ;; Copy list method from the cl package `copy-list'
823 (while (consp oldlist) (push (pop oldlist) res))
824 (prog1 (nreverse res) (setcdr res oldlist)))
825 imenu-sort-function)
826 index-alist)
827 (buffer-name)))
828 (setq menu1 (imenu--create-keymap-1 (car menu)
829 (if (< 1 (length (cdr menu)))
830 (cdr menu)
831 (cdr (car (cdr menu))))
832 t))
833 (setq old (lookup-key (current-local-map) [menu-bar index]))
834 (if (keymapp old)
835 (setcdr (nthcdr 2 old) menu1)))))))
836
837(defun imenu--menubar-select (item)
838 "Use Imenu to select the function or variable named in this menu item."
e63679b8
RS
839 (if (equal item '("*Rescan*" . -99))
840 (progn
841 (imenu--cleanup)
842 (setq imenu--index-alist nil)
843 (imenu-update-menubar))
844 (imenu item)))
5d3b0f18 845
68e01f5a 846;;;###autoload
6c1bf12b 847(defun imenu (index-item)
68e01f5a 848 "Jump to a place in the buffer chosen using a buffer menu or mouse menu.
0ee4f8ad 849See `imenu-choose-buffer-index' for more information."
6c1bf12b
RS
850 (interactive
851 (list (save-restriction
852 (widen)
0a8e8bc6
KH
853 (car (imenu-choose-buffer-index)))))
854 ;; Convert a string to an alist element.
855 (if (stringp index-item)
856 (setq index-item (assoc index-item (imenu--make-index-alist))))
6c1bf12b
RS
857 (and index-item
858 (progn
859 (push-mark)
860 (cond
861 ((markerp (cdr index-item))
862 (if (or ( > (marker-position (cdr index-item)) (point-min))
863 ( < (marker-position (cdr index-item)) (point-max)))
864 ;; widen if outside narrowing
865 (widen))
866 (goto-char (marker-position (cdr index-item))))
867 (t
868 (if (or ( > (cdr index-item) (point-min))
869 ( < (cdr index-item) (point-max)))
870 ;; widen if outside narrowing
871 (widen))
872 (goto-char (cdr index-item)))))))
5d3b0f18 873
0a688fd0
RS
874(provide 'imenu)
875
876;;; imenu.el ends here