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