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