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