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