Commit | Line | Data |
---|---|---|
0a688fd0 RS |
1 | ;;; imenu.el --- Framework for mode-specific buffer indexes. |
2 | ||
3 | ;; Copyright (C) 1994 Free Software Foundation, Inc. | |
4 | ||
5 | ;; Author: Ake Stenhoff <etxaksf@aom.ericsson.se> | |
6 | ;; Lars Lindberg <lli@sypro.cap.se> | |
7 | ;; Created: 8 Feb 1994 | |
5d3b0f18 | 8 | ;; Version: 1.11 |
0a688fd0 RS |
9 | ;; Keywords: tools |
10 | ;; | |
11 | ;; This program is free software; you can redistribute it and/or modify | |
12 | ;; it under the terms of the GNU General Public License as published by | |
13 | ;; the Free Software Foundation; either version 2, or (at your option) | |
14 | ;; any later version. | |
15 | ;; | |
16 | ;; This program is distributed in the hope that it will be useful, | |
17 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | |
18 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
19 | ;; GNU General Public License for more details. | |
20 | ;; | |
21 | ;; You should have received a copy of the GNU General Public License | |
22 | ;; along with this program; if not, write to the Free Software | |
23 | ;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
24 | ||
25 | ;;; Commentary: | |
26 | ;; | |
27 | ;; Purpose of this package: | |
28 | ;; To present a framework for mode-specific buffer indexes. | |
29 | ;; A buffer index is an alist of names and buffer positions. | |
30 | ;; For instance all functions in a C-file and their positions. | |
31 | ;; | |
32 | ;; How it works: | |
33 | ||
34 | ;; A mode-specific function is called to generate the index. It is | |
35 | ;; then presented to the user, who can choose from this index. | |
36 | ;; | |
37 | ;; The package comes with a set of example functions for how to | |
38 | ;; utilize this package. | |
39 | ||
40 | ;; There are *examples* for index gathering functions for C/C++ and | |
41 | ;; Lisp/Emacs Lisp but it is easy to customize for other modes. A | |
42 | ;; function for jumping to the chosen index position is also | |
43 | ;; supplied. | |
0a688fd0 RS |
44 | |
45 | ;;; Change Log: | |
5d3b0f18 RS |
46 | ;; v1.11 Jul 26 1994 Ake Stenhoff |
47 | ;; Fixed bugs in 'imenu-add-to-menubar'. | |
48 | ;; v1.10 Jul 21 1994 Ake Stenhoff | |
49 | ;; Added support for markers. | |
50 | ;; Changed the examples to use | |
51 | ;; markers. | |
52 | ;; Thanks [alon]. | |
53 | ;; v1.9 Jun 14 1994 Ake Stenhoff | |
54 | ;; Added 'imenu-add-to-menubar'. | |
0ee4f8ad RS |
55 | ;; v1.7 Apr 12 1994 Ake Stenhoff |
56 | ;; Changed doc strings refering to symbols. | |
57 | ;; Require 'cl' when compiling only. | |
58 | ;; Only uses 'cl' macros. | |
26d6bb60 RS |
59 | ;; v1.6 Feb 28 1994 Ake Stenhoff |
60 | ;; Added alist as an optional argument to | |
61 | ;; 'imenu-choose-buffer-index'. | |
62 | ;; Thanks [dean]. | |
63 | ;; v1.5 Feb 25 1994 Ake Stenhoff | |
64 | ;; Added code to parse DEFSTRUCT, DEFCLASS, DEFTYPE, | |
65 | ;; DEFINE-CONDITION in the lisp example function. | |
66 | ;; Thanks [simon]. | |
0a688fd0 RS |
67 | ;; v1.4 Feb 18 1994 Ake Stenhoff |
68 | ;; Added 'imenu-create-submenu-name' for creating a submenu name. | |
69 | ;; This is for getting a general look of submenu names. | |
70 | ;; Added variable 'imenu-submenu-name-format' used by | |
71 | ;; 'imenu-create-submenu-name'. | |
72 | ;; v1.3 Feb 17 1994 Lars Lindberg | |
73 | ;; Added 'imenu--flatten-index-alist' for flatten nexted index | |
74 | ;; alists. | |
75 | ;; New examples for lisp mode that utilizes the features better. | |
76 | ;; Added the variable 'imenu-space-replacement'. | |
77 | ;; The completion-buffer version of the index menu now replaces | |
78 | ;; spaces in the index-names to make tokens of them. | |
79 | ;; v1.2 Feb 14 1994 Ake Stenhoff & Lars Lindberg | |
80 | ;; Now handles nested index lists. | |
81 | ;; v1.1 Feb 9 1994 Ake Stenhoff & Lars Lindberg | |
82 | ;; Better comments (?). | |
83 | ;; v1.0 Feb 8 1994 Ake Stenhoff & Lars Lindberg | |
84 | ;; Based on func-menu.el 3.5. | |
85 | ||
26d6bb60 RS |
86 | ;;; Thanks goes to |
87 | ;; [simon] - Simon Leinen simon@lia.di.epfl.ch | |
88 | ;; [dean] - Dean Andrews ada@unison.com | |
5d3b0f18 | 89 | ;; [alon] - Alon Albert al@mercury.co.il |
26d6bb60 | 90 | |
0a688fd0 | 91 | ;;; Code |
0ee4f8ad | 92 | (eval-when-compile (require 'cl)) |
0a688fd0 RS |
93 | |
94 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
95 | ;;; | |
96 | ;;; Customizable variables | |
97 | ;;; | |
98 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
99 | ||
100 | (defvar imenu-always-use-completion-buffer-p nil | |
101 | "*Set this to non-nil for displaying the index in a completion buffer. | |
102 | ||
103 | Non-nil means always display the index in a completion buffer. | |
104 | Nil means display the index as a mouse menu when the mouse was | |
68e01f5a | 105 | used to invoke `imenu'.") |
0a688fd0 RS |
106 | |
107 | (defvar imenu-sort-function nil | |
108 | "*The function to use for sorting the index mouse-menu. | |
109 | ||
110 | Affects only the mouse index menu. | |
111 | ||
112 | Set this to nil if you don't want any sorting (faster). | |
113 | The items in the menu are then presented in the order they were found | |
114 | in the buffer. | |
115 | ||
0ee4f8ad | 116 | Set it to `imenu--sort-by-name' if you want alphabetic sorting. |
0a688fd0 RS |
117 | |
118 | The function should take two arguments and return T if the first | |
119 | element should come before the second. The arguments are cons cells; | |
a4e104bf | 120 | \(NAME . POSITION). Look at `imenu--sort-by-name' for an example.") |
0a688fd0 RS |
121 | |
122 | (defvar imenu-max-items 25 | |
123 | "*Maximum number of elements in an index mouse-menu.") | |
124 | ||
125 | (defvar imenu-scanning-message "Scanning buffer for index. (%3d%%)" | |
126 | "*Progress message during the index scanning of the buffer. | |
7dea4e70 | 127 | If non-nil, user gets a message during the scanning of the buffer |
0a688fd0 RS |
128 | |
129 | Relevant only if the mode-specific function that creates the buffer | |
0ee4f8ad | 130 | index use `imenu-progress-message'.") |
0a688fd0 RS |
131 | |
132 | (defvar imenu-space-replacement "^" | |
133 | "*The replacement string for spaces in index names. | |
134 | Used when presenting the index in a completion-buffer to make the | |
135 | names work as tokens.") | |
136 | ||
137 | (defvar imenu-level-separator ":" | |
138 | "*The separator between index names of different levels. | |
139 | Used for making mouse-menu titles and for flattening nested indexes | |
140 | with name concatenation.") | |
141 | ||
142 | (defvar imenu-submenu-name-format "%s..." | |
143 | "*The format for making a submenu name.") | |
144 | ||
145 | ;;;; Hooks | |
146 | ||
147 | (defvar imenu-create-index-function 'imenu-default-create-index-function | |
148 | "The function to use for creating a buffer index. | |
149 | ||
150 | It should be a function that takes no arguments and returns an index | |
151 | of the current buffer as an alist. The elements in the alist look | |
152 | like: (INDEX-NAME . INDEX-POSITION). You may also nest index list like | |
a4e104bf | 153 | \(INDEX-NAME . INDEX-ALIST). |
0a688fd0 | 154 | |
0ee4f8ad | 155 | This function is called within a `save-excursion'. |
0a688fd0 RS |
156 | |
157 | The variable is buffer-local.") | |
158 | (make-variable-buffer-local 'imenu-create-index-function) | |
159 | ||
68e01f5a | 160 | (defvar imenu-prev-index-position-function 'beginning-of-defun |
0a688fd0 RS |
161 | "Function for finding the next index position. |
162 | ||
0ee4f8ad RS |
163 | If `imenu-create-index-function' is set to |
164 | `imenu-default-create-index-function', then you must set this variable | |
0a688fd0 RS |
165 | to a function that will find the next index, looking backwards in the |
166 | file. | |
167 | ||
168 | The function should leave point at the place to be connected to the | |
169 | index and it should return nil when it doesn't find another index. ") | |
68e01f5a | 170 | (make-variable-buffer-local 'imenu-prev-index-position-function) |
0a688fd0 | 171 | |
68e01f5a | 172 | (defvar imenu-extract-index-name-function nil |
0a688fd0 RS |
173 | "Function for extracting the index name. |
174 | ||
0a688fd0 | 175 | This function is called after the function pointed out by |
68e01f5a RS |
176 | `imenu-prev-index-position-function'.") |
177 | (make-variable-buffer-local 'imenu-extract-index-name-function) | |
0a688fd0 RS |
178 | |
179 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
180 | ;;; | |
181 | ;;; Internal variables | |
182 | ;;; | |
183 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
184 | ||
185 | ;; The item to use in the index for rescanning the buffer. | |
186 | (defconst imenu--rescan-item '("*Rescan*" . -99)) | |
187 | ||
188 | ;; The latest buffer index. | |
189 | ;; Buffer local. | |
190 | (defvar imenu--index-alist nil) | |
191 | (make-variable-buffer-local 'imenu--index-alist) | |
192 | ||
193 | ;; History list for 'jump-to-function-in-buffer'. | |
194 | ;; Buffer local. | |
195 | (defvar imenu--history-list nil) | |
196 | (make-variable-buffer-local 'imenu--history-list) | |
197 | ||
198 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
199 | ;;; | |
200 | ;;; Internal support functions | |
201 | ;;; | |
202 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
203 | ||
204 | ;;; | |
205 | ;;; Sort function | |
206 | ;;; Sorts the items depending on their index name. | |
207 | ;;; An item look like (NAME . POSITION). | |
208 | ;;; | |
209 | (defun imenu--sort-by-name (item1 item2) | |
210 | (string-lessp (car item1) (car item2))) | |
211 | ||
212 | (defun imenu--relative-position (&optional reverse) | |
213 | ;; Support function to calculate relative position in buffer | |
214 | ;; Beginning of buffer is 0 and end of buffer is 100 | |
215 | ;; If REVERSE is non-nil then the beginning is 100 and the end is 0. | |
216 | (let ((pos (point)) | |
217 | (total (buffer-size))) | |
218 | (and reverse (setq pos (- total pos))) | |
219 | (if (> total 50000) | |
220 | ;; Avoid overflow from multiplying by 100! | |
221 | (/ (1- pos) (max (/ total 100) 1)) | |
222 | (/ (* 100 (1- pos)) (max total 1))))) | |
223 | ||
224 | ;;; | |
7dea4e70 | 225 | ;;; Macro to display a progress message. |
0a688fd0 RS |
226 | ;;; RELPOS is the relative position to display. |
227 | ;;; If RELPOS is nil, then the relative position in the buffer | |
228 | ;;; is calculated. | |
7dea4e70 RS |
229 | ;;; PREVPOS is the variable in which we store the last position displayed. |
230 | (defmacro imenu-progress-message (prevpos &optional relpos reverse) | |
0a688fd0 RS |
231 | (` (and |
232 | imenu-scanning-message | |
7dea4e70 | 233 | (let ((pos (, (if relpos |
0a688fd0 | 234 | relpos |
7dea4e70 RS |
235 | (` (imenu--relative-position (, reverse))))))) |
236 | (if (, (if relpos t | |
237 | (` (> pos (+ 5 (, prevpos)))))) | |
238 | (progn | |
239 | (message imenu-scanning-message pos) | |
240 | (setq (, prevpos) pos))))))) | |
0a688fd0 RS |
241 | |
242 | ;;; | |
243 | ;;; Function for suporting general looking submenu names. | |
0ee4f8ad | 244 | ;;; Uses `imenu-submenu-name-format' for creating the name. |
0a688fd0 RS |
245 | ;;; NAME is the base of the new submenu name. |
246 | ;;; | |
247 | (defun imenu-create-submenu-name (name) | |
248 | (format imenu-submenu-name-format name)) | |
249 | ||
250 | ;; Split LIST into sublists of max length N. | |
251 | ;; Example (imenu--split '(1 2 3 4 5 6 7 8) 3)-> '((1 2 3) (4 5 6) (7 8)) | |
252 | (defun imenu--split (list n) | |
253 | (let ((remain list) | |
254 | (result '()) | |
255 | (sublist '()) | |
256 | (i 0)) | |
257 | (while remain | |
258 | (push (pop remain) sublist) | |
259 | (incf i) | |
260 | (and (= i n) | |
261 | ;; We have finished a sublist | |
262 | (progn (push (nreverse sublist) result) | |
263 | (setq i 0) | |
264 | (setq sublist '())))) | |
265 | ;; There might be a sublist (if the length of LIST mod n is != 0) | |
266 | ;; that has to be added to the result list. | |
267 | (and sublist | |
268 | (push (nreverse sublist) result)) | |
269 | (nreverse result))) | |
270 | ||
271 | ;;; | |
272 | ;;; Split a menu in to several menus. | |
273 | ;;; | |
274 | (defun imenu--split-menu (menulist title) | |
275 | (cons "Function menus" | |
276 | (mapcar | |
277 | (function | |
278 | (lambda (menu) | |
279 | (cons (format "(%s)" title) menu))) | |
280 | (imenu--split menulist imenu-max-items)))) | |
281 | ||
282 | ;;; | |
283 | ;;; Find all items in this buffer that should be in the index. | |
284 | ;;; Returns an alist on the form | |
285 | ;;; ((NAME . POSITION) (NAME . POSITION) ...) | |
286 | ;;; | |
287 | ||
288 | (defun imenu--make-index-alist () | |
289 | ;; Create a list for this buffer only when needed. | |
290 | (or imenu--index-alist | |
291 | ;; Get the index | |
292 | (setq imenu--index-alist | |
293 | (save-excursion | |
294 | (funcall imenu-create-index-function)))) | |
295 | (or imenu--index-alist | |
296 | (error "No items suitable for an index found in this buffer.")) | |
297 | ;; Add a rescan option to the index. | |
298 | (cons imenu--rescan-item imenu--index-alist)) | |
5d3b0f18 RS |
299 | ;;; |
300 | ;;; Find all markers in alist and makes | |
301 | ;;; them point nowhere. | |
302 | ;;; | |
303 | (defun imenu--cleanup (&optional alist) | |
304 | ;; Sets the markers in imenu--index-alist | |
305 | ;; point nowhere. | |
306 | ;; if alist is provided use that list. | |
307 | (and imenu--index-alist | |
308 | (mapc | |
309 | (function | |
310 | (lambda (item) | |
311 | (cond | |
312 | ((markerp (cdr item)) | |
313 | (set-marker (cdr item) nil)) | |
314 | ((listp (cdr item)) | |
315 | (imenu--cleanup (cdr item)))))) | |
316 | (if alist alist imenu--index-alist)) | |
317 | t)) | |
0a688fd0 RS |
318 | (defun imenu-default-create-index-function () |
319 | "*Wrapper for index searching functions. | |
320 | ||
321 | Moves point to end of buffer and then repeatedly calls | |
68e01f5a | 322 | `imenu-prev-index-position-function' and `imenu-extract-index-name-function'. |
0a688fd0 | 323 | Their results are gathered into an index alist." |
3e062f78 RS |
324 | ;; These should really be done by setting imenu-create-index-function |
325 | ;; in these major modes. But save that change for later. | |
326 | (cond ((eq major-mode 'emacs-lisp-mode) | |
327 | (imenu-example--create-lisp-index)) | |
328 | ((eq major-mode 'lisp-mode) | |
329 | (imenu-example--create-lisp-index)) | |
330 | ((eq major-mode 'c++-mode) | |
331 | (imenu-example--create-c++-index)) | |
332 | ((eq major-mode 'c-mode) | |
333 | (imenu-example--create-c-index)) | |
334 | (t | |
335 | (or (and (fboundp imenu-prev-index-position-function) | |
336 | (fboundp imenu-extract-index-name-function)) | |
337 | (error "The mode \"%s\" does not take full advantage of imenu.el yet." | |
338 | mode-name)) | |
339 | (let ((index-alist '()) | |
7dea4e70 | 340 | name prev-pos) |
3e062f78 | 341 | (goto-char (point-max)) |
7dea4e70 | 342 | (imenu-progress-message prev-pos 0 t) |
3e062f78 RS |
343 | ;; Search for the function |
344 | (while (funcall imenu-prev-index-position-function) | |
7dea4e70 | 345 | (imenu-progress-message prev-pos nil t) |
3e062f78 RS |
346 | (save-excursion |
347 | (setq name (funcall imenu-extract-index-name-function))) | |
348 | (and (stringp name) | |
349 | (push (cons name (point)) index-alist))) | |
7dea4e70 | 350 | (imenu-progress-message prev-pos 100 t) |
3e062f78 | 351 | index-alist)))) |
0a688fd0 RS |
352 | |
353 | (defun imenu--replace-spaces (name replacement) | |
354 | ;; Replace all spaces in NAME with REPLACEMENT. | |
355 | ;; That second argument should be a string. | |
356 | (mapconcat | |
357 | (function | |
358 | (lambda (ch) | |
359 | (if (char-equal ch ?\ ) | |
360 | replacement | |
361 | (char-to-string ch)))) | |
362 | name | |
363 | "")) | |
364 | ||
365 | (defun imenu--flatten-index-alist (index-alist &optional concat-names prefix) | |
366 | ;; Takes a nested INDEX-ALIST and returns a flat index alist. | |
367 | ;; If optional CONCAT-NAMES is non-nil, then a nested index has its | |
368 | ;; name and a space concatenated to the names of the children. | |
369 | ;; Third argument PREFIX is for internal use only. | |
370 | (mapcan | |
371 | (function | |
372 | (lambda (item) | |
373 | (let* ((name (car item)) | |
374 | (pos (cdr item)) | |
375 | (new-prefix (and concat-names | |
376 | (if prefix | |
377 | (concat prefix imenu-level-separator name) | |
378 | name)))) | |
379 | (cond | |
5d3b0f18 | 380 | ((or (markerp pos) (numberp pos)) |
0a688fd0 RS |
381 | (list (cons new-prefix pos))) |
382 | (t | |
383 | (imenu--flatten-index-alist pos new-prefix)))))) | |
384 | index-alist)) | |
385 | ||
386 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
387 | ;;; | |
388 | ;;; The main functions for this package! | |
389 | ;;; | |
390 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
391 | ||
392 | (defun imenu--completion-buffer (index-alist &optional prompt) | |
393 | "Let the user select from INDEX-ALIST in a completion buffer with PROMPT. | |
394 | ||
395 | Returns t for rescan and otherwise a position number." | |
396 | ;; Create a list for this buffer only when needed. | |
397 | (let (name choice | |
398 | (prepared-index-alist | |
399 | (mapcar | |
400 | (function | |
401 | (lambda (item) | |
402 | (cons (imenu--replace-spaces (car item) imenu-space-replacement) | |
403 | (cdr item)))) | |
404 | index-alist))) | |
405 | (save-window-excursion | |
406 | ;; Display the completion buffer | |
407 | (with-output-to-temp-buffer "*Completions*" | |
408 | (display-completion-list | |
409 | (all-completions "" prepared-index-alist ))) | |
3e062f78 RS |
410 | (let ((minibuffer-setup-hook |
411 | (function (lambda () | |
412 | (let ((buffer (current-buffer))) | |
413 | (save-excursion | |
414 | (set-buffer "*Completions*") | |
415 | (setq completion-reference-buffer buffer))))))) | |
416 | ;; Make a completion question | |
417 | (setq name (completing-read (or prompt "Index item: ") | |
0a688fd0 | 418 | prepared-index-alist |
3e062f78 RS |
419 | nil t nil 'imenu--history-list)))) |
420 | (cond ((not (stringp name)) | |
421 | nil) | |
422 | ((string= name (car imenu--rescan-item)) | |
423 | t) | |
424 | (t | |
425 | (setq choice (assoc name prepared-index-alist)) | |
426 | (if (listp (cdr choice)) | |
427 | (imenu--completion-buffer (cdr choice) prompt) | |
428 | choice))))) | |
68e01f5a | 429 | |
0a688fd0 RS |
430 | (defun imenu--mouse-menu (index-alist event &optional title) |
431 | "Let the user select from a buffer index from a mouse menu. | |
432 | ||
433 | INDEX-ALIST is the buffer index and EVENT is a mouse event. | |
434 | ||
435 | Returns t for rescan and otherwise a position number." | |
436 | (let* ((menu (imenu--split-menu | |
437 | (if imenu-sort-function | |
0ee4f8ad RS |
438 | (sort |
439 | (let ((res nil) | |
440 | (oldlist index-alist)) | |
441 | ;; Copy list method from the cl package `copy-list' | |
442 | (while (consp oldlist) (push (pop oldlist) res)) | |
443 | (prog1 (nreverse res) (setcdr res oldlist))) | |
444 | imenu-sort-function) | |
0a688fd0 RS |
445 | index-alist) |
446 | (or title (buffer-name)))) | |
447 | position) | |
448 | (setq position (x-popup-menu event menu)) | |
449 | (cond | |
450 | ((eq position nil) | |
451 | position) | |
26d6bb60 | 452 | ((listp position) |
0a688fd0 RS |
453 | (imenu--mouse-menu position event |
454 | (if title | |
455 | (concat title imenu-level-separator | |
456 | (car (rassq position index-alist))) | |
457 | (car (rassq position index-alist))))) | |
458 | ((= position (cdr imenu--rescan-item)) | |
459 | t) | |
460 | (t | |
461 | (rassq position index-alist))))) | |
462 | ||
26d6bb60 | 463 | (defun imenu-choose-buffer-index (&optional prompt alist) |
0a688fd0 RS |
464 | "Let the user select from a buffer index and return the chosen index. |
465 | ||
466 | If the user originally activated this function with the mouse, a mouse | |
0a688fd0 RS |
467 | menu is used. Otherwise a completion buffer is used and the user is |
468 | prompted with PROMPT. | |
469 | ||
26d6bb60 RS |
470 | If you call this function with index alist ALIST, then it lets the user |
471 | select from ALIST. | |
472 | ||
0ee4f8ad | 473 | With no index alist ALIST, it calls `imenu--make-index-alist' to |
26d6bb60 RS |
474 | create the index alist. |
475 | ||
0ee4f8ad | 476 | If `imenu-always-use-completion-buffer-p' is non-nil, then the |
0a688fd0 RS |
477 | completion buffer is always used, no matter if the mouse was used or |
478 | not. | |
479 | ||
480 | The returned value is on the form (INDEX-NAME . INDEX-POSITION)." | |
481 | (let (index-alist | |
7dea4e70 | 482 | (mouse-triggered (listp last-nonmenu-event)) |
0a688fd0 RS |
483 | (result t) ) |
484 | ;; If selected by mouse, see to that the window where the mouse is | |
485 | ;; really is selected. | |
486 | (and mouse-triggered | |
7dea4e70 | 487 | (let ((window (posn-window (event-start last-nonmenu-event)))) |
0a688fd0 RS |
488 | (or (framep window) (select-window window)))) |
489 | ;; Create a list for this buffer only when needed. | |
490 | (while (eq result t) | |
26d6bb60 | 491 | (setq index-alist (if alist alist (imenu--make-index-alist))) |
0a688fd0 RS |
492 | (setq result |
493 | (if (and mouse-triggered | |
494 | (not imenu-always-use-completion-buffer-p)) | |
7dea4e70 | 495 | (imenu--mouse-menu index-alist last-nonmenu-event) |
0a688fd0 RS |
496 | (imenu--completion-buffer index-alist prompt))) |
497 | (and (eq result t) | |
5d3b0f18 | 498 | (imenu--cleanup) |
0a688fd0 RS |
499 | (setq imenu--index-alist nil))) |
500 | result)) | |
501 | ||
5d3b0f18 RS |
502 | (defun imenu-add-to-menubar (name) |
503 | "Adds an \"imenu\" entry to the menubar for the | |
504 | current local keymap. | |
505 | NAME is the string naming the menu to be added. | |
506 | See 'imenu' for more information." | |
507 | (interactive "sMenu name: ") | |
508 | (and window-system | |
509 | (define-key (current-local-map) [menu-bar index] | |
510 | (cons name 'imenu)))) | |
511 | ||
68e01f5a RS |
512 | ;;;###autoload |
513 | (defun imenu () | |
514 | "Jump to a place in the buffer chosen using a buffer menu or mouse menu. | |
0ee4f8ad | 515 | See `imenu-choose-buffer-index' for more information." |
0a688fd0 RS |
516 | (interactive) |
517 | (let ((index-item (imenu-choose-buffer-index))) | |
518 | (and index-item | |
519 | (progn | |
520 | (push-mark) | |
5d3b0f18 RS |
521 | (cond |
522 | ((markerp (cdr index-item)) | |
523 | (goto-char (marker-position (cdr index-item)))) | |
524 | (t | |
525 | (goto-char (cdr index-item)))))))) | |
526 | ||
0a688fd0 RS |
527 | |
528 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
529 | ;;;; | |
530 | ;;;; Some examples of functions utilizing the framework of this | |
531 | ;;;; package. | |
532 | ;;;; | |
533 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
534 | ||
31faef47 | 535 | ;; Return the current/previous sexp and the location of the sexp (its |
0a688fd0 RS |
536 | ;; beginning) without moving the point. |
537 | (defun imenu-example--name-and-position () | |
538 | (save-excursion | |
539 | (forward-sexp -1) | |
540 | (let ((beg (point)) | |
5d3b0f18 RS |
541 | (end (progn (forward-sexp) (point))) |
542 | (marker (make-marker))) | |
543 | (set-marker marker beg) | |
0a688fd0 | 544 | (cons (buffer-substring beg end) |
5d3b0f18 | 545 | marker)))) |
0a688fd0 RS |
546 | |
547 | ;;; | |
548 | ;;; Lisp | |
549 | ;;; | |
550 | ||
551 | (defun imenu-example--lisp-extract-index-name () | |
0ee4f8ad | 552 | ;; Example of a candidate for `imenu-extract-index-name-function'. |
0a688fd0 RS |
553 | ;; This will generate a flat index of definitions in a lisp file. |
554 | (save-match-data | |
555 | (and (looking-at "(def") | |
556 | (condition-case nil | |
557 | (progn | |
558 | (down-list 1) | |
559 | (forward-sexp 2) | |
560 | (let ((beg (point)) | |
561 | (end (progn (forward-sexp -1) (point)))) | |
562 | (buffer-substring beg end))) | |
563 | (error nil))))) | |
564 | ||
565 | (defun imenu-example--create-lisp-index () | |
0ee4f8ad | 566 | ;; Example of a candidate for `imenu-create-index-function'. |
0a688fd0 RS |
567 | ;; It will generate a nested index of definitions. |
568 | (let ((index-alist '()) | |
569 | (index-var-alist '()) | |
26d6bb60 | 570 | (index-type-alist '()) |
7dea4e70 RS |
571 | (index-unknown-alist '()) |
572 | prev-pos) | |
0a688fd0 | 573 | (goto-char (point-max)) |
7dea4e70 | 574 | (imenu-progress-message prev-pos 0) |
0a688fd0 RS |
575 | ;; Search for the function |
576 | (while (beginning-of-defun) | |
7dea4e70 | 577 | (imenu-progress-message prev-pos nil t) |
0a688fd0 RS |
578 | (save-match-data |
579 | (and (looking-at "(def") | |
580 | (save-excursion | |
581 | (down-list 1) | |
582 | (cond | |
583 | ((looking-at "def\\(var\\|const\\)") | |
584 | (forward-sexp 2) | |
585 | (push (imenu-example--name-and-position) | |
586 | index-var-alist)) | |
587 | ((looking-at "def\\(un\\|subst\\|macro\\|advice\\)") | |
588 | (forward-sexp 2) | |
589 | (push (imenu-example--name-and-position) | |
590 | index-alist)) | |
26d6bb60 RS |
591 | ((looking-at "def\\(type\\|struct\\|class\\|ine-condition\\)") |
592 | (forward-sexp 2) | |
593 | (if (= (char-after (1- (point))) ?\)) | |
594 | (progn | |
595 | (forward-sexp -1) | |
596 | (down-list 1) | |
597 | (forward-sexp 1))) | |
598 | (push (imenu-example--name-and-position) | |
599 | index-type-alist)) | |
0a688fd0 RS |
600 | (t |
601 | (forward-sexp 2) | |
602 | (push (imenu-example--name-and-position) | |
603 | index-unknown-alist))))))) | |
7dea4e70 | 604 | (imenu-progress-message prev-pos 100) |
0a688fd0 RS |
605 | (and index-var-alist |
606 | (push (cons (imenu-create-submenu-name "Variables") index-var-alist) | |
607 | index-alist)) | |
26d6bb60 RS |
608 | (and index-type-alist |
609 | (push (cons (imenu-create-submenu-name "Types") index-type-alist) | |
610 | index-alist)) | |
0a688fd0 RS |
611 | (and index-unknown-alist |
612 | (push (cons (imenu-create-submenu-name "Syntax-unknown") index-unknown-alist) | |
613 | index-alist)) | |
614 | index-alist)) | |
615 | ||
616 | ||
617 | ;;; | |
618 | ;;; C | |
619 | ;;; | |
620 | ;; Regular expression to find C functions | |
621 | (defvar imenu-example--function-name-regexp-c | |
622 | (concat | |
623 | "^[a-zA-Z0-9]+[ \t]?" ; type specs; there can be no | |
624 | "\\([a-zA-Z0-9_*]+[ \t]+\\)?" ; more than 3 tokens, right? | |
625 | "\\([a-zA-Z0-9_*]+[ \t]+\\)?" | |
626 | "\\([*&]+[ \t]*\\)?" ; pointer | |
627 | "\\([a-zA-Z0-9_*]+\\)[ \t]*(" ; name | |
628 | )) | |
629 | ||
0a688fd0 RS |
630 | (defun imenu-example--create-c-index (&optional regexp) |
631 | (let ((index-alist '()) | |
7dea4e70 | 632 | prev-pos char) |
0a688fd0 | 633 | (goto-char (point-min)) |
7dea4e70 | 634 | (imenu-progress-message prev-pos 0) |
0a688fd0 RS |
635 | ;; Search for the function |
636 | (save-match-data | |
637 | (while (re-search-forward | |
638 | (or regexp imenu-example--function-name-regexp-c) | |
639 | nil t) | |
7dea4e70 | 640 | (imenu-progress-message prev-pos) |
0a688fd0 RS |
641 | (backward-up-list 1) |
642 | (save-excursion | |
643 | (goto-char (scan-sexps (point) 1)) | |
644 | (setq char (following-char))) | |
645 | ;; Skip this function name if it is a prototype declaration. | |
646 | (if (not (eq char ?\;)) | |
647 | (push (imenu-example--name-and-position) index-alist)))) | |
7dea4e70 | 648 | (imenu-progress-message prev-pos 100) |
0a688fd0 RS |
649 | (nreverse index-alist))) |
650 | ||
651 | ;;; | |
652 | ;;; C++ | |
653 | ;;; | |
654 | ;; Regular expression to find C++ functions | |
655 | (defvar imenu-example--function-name-regexp-c++ | |
656 | (concat | |
657 | "^[a-zA-Z0-9:]+[ \t]?" ; type specs; there can be no | |
658 | "\\([a-zA-Z0-9_:~*]+[ \t]+\\)?" ; more than 3 tokens, right? | |
659 | "\\([a-zA-Z0-9_:~*]+[ \t]+\\)?" | |
660 | "\\([*&]+[ \t]*\\)?" ; pointer | |
661 | "\\([a-zA-Z0-9_:*]+\\)[ \t]*(" ; name | |
662 | )) | |
663 | (defun imenu-example--create-c++-index () | |
664 | (imenu-example--create-c-index imenu-example--function-name-regexp-c++)) | |
665 | ||
0a688fd0 RS |
666 | (provide 'imenu) |
667 | ||
668 | ;;; imenu.el ends here |