Print test timings unconditionally
[emacs.git] / lisp / cedet / semantic / imenu.el
blob62bcfac38f9c3f267a2231245368c73543a33c3a
1 ;;; semantic/imenu.el --- Use Semantic as an imenu tag generator
3 ;; Copyright (C) 2000-2005, 2007-2008, 2010-2018 Free Software
4 ;; Foundation, Inc.
6 ;; Author: Eric M. Ludlam <zappo@gnu.org>
7 ;; Maintainer: Eric Ludlam
9 ;; This file is part of GNU Emacs.
11 ;; GNU Emacs 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 3 of the License, or
14 ;; (at your option) any later version.
16 ;; GNU Emacs 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.
21 ;; You should have received a copy of the GNU General Public License
22 ;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
24 ;;; Commentary:
26 ;; This support function can be used in any buffer which supports
27 ;; the bovinator to create the imenu index.
29 ;; To use this in a buffer, do this in a hook.
31 ;; (add-hook 'mode-hook
32 ;; (lambda ()
33 ;; (setq imenu-create-index-function 'semantic-create-imenu-index)
34 ;; ))
36 (require 'semantic)
37 (require 'semantic/format)
38 (require 'semantic/db)
39 (require 'semantic/db-file)
40 (require 'semantic/sort)
41 (require 'imenu)
43 (declare-function pulse-momentary-highlight-one-line "pulse" (o &optional face))
44 (declare-function semanticdb-semantic-init-hook-fcn "db-mode")
46 ;; Because semantic imenu tags will hose the current imenu handling
47 ;; code in speedbar, force semantic/sb in.
48 (if (featurep 'speedbar)
49 (require 'semantic/sb)
50 (add-hook 'speedbar-load-hook (lambda () (require 'semantic/sb))))
52 (defgroup semantic-imenu nil
53 "Semantic interface to Imenu."
54 :group 'semantic
55 :group 'imenu
58 ;;;###autoload
59 (defcustom semantic-imenu-summary-function 'semantic-format-tag-abbreviate
60 "Function to use when creating items in Imenu.
61 Some useful functions are found in `semantic-format-tag-functions'."
62 :group 'semantic-imenu
63 :type semantic-format-tag-custom-list)
64 (make-variable-buffer-local 'semantic-imenu-summary-function)
66 ;;;###autoload
67 (defcustom semantic-imenu-bucketize-file t
68 "Non-nil if tags in a file are to be grouped into buckets."
69 :group 'semantic-imenu
70 :type 'boolean)
71 (make-variable-buffer-local 'semantic-imenu-bucketize-file)
73 (defcustom semantic-imenu-adopt-external-members t
74 "Non-nil if types in a file should adopt externally defined members.
75 C++ and CLOS can define methods that are not in the body of a class
76 definition."
77 :group 'semantic-imenu
78 :type 'boolean)
80 (defcustom semantic-imenu-buckets-to-submenu t
81 "Non-nil if buckets of tags are to be turned into submenus.
82 This option is ignored if `semantic-imenu-bucketize-file' is nil."
83 :group 'semantic-imenu
84 :type 'boolean)
85 (make-variable-buffer-local 'semantic-imenu-buckets-to-submenu)
87 ;;;###autoload
88 (defcustom semantic-imenu-expand-type-members t
89 "Non-nil if types should have submenus with members in them."
90 :group 'semantic-imenu
91 :type 'boolean)
92 (make-variable-buffer-local 'semantic-imenu-expand-type-members)
93 (semantic-varalias-obsolete 'semantic-imenu-expand-type-parts
94 'semantic-imenu-expand-type-members "23.2")
96 (defcustom semantic-imenu-bucketize-type-members t
97 "Non-nil if members of a type should be grouped into buckets.
98 A nil value means to keep them in the same order.
99 Overridden to nil if `semantic-imenu-bucketize-file' is nil."
100 :group 'semantic-imenu
101 :type 'boolean)
102 (make-variable-buffer-local 'semantic-imenu-bucketize-type-members)
103 (semantic-varalias-obsolete 'semantic-imenu-bucketize-type-parts
104 'semantic-imenu-bucketize-type-members "23.2")
106 (defcustom semantic-imenu-sort-bucket-function nil
107 "Function to use when sorting tags in the buckets of functions.
108 See `semantic-bucketize' and the FILTER argument for more details on this function."
109 :group 'semantic-imenu
110 :type '(radio (const :tag "No Sorting" nil)
111 (const semantic-sort-tags-by-name-increasing)
112 (const semantic-sort-tags-by-name-decreasing)
113 (const semantic-sort-tags-by-type-increasing)
114 (const semantic-sort-tags-by-type-decreasing)
115 (const semantic-sort-tags-by-name-increasing-ci)
116 (const semantic-sort-tags-by-name-decreasing-ci)
117 (const semantic-sort-tags-by-type-increasing-ci)
118 (const semantic-sort-tags-by-type-decreasing-ci)
119 (function)))
120 (make-variable-buffer-local 'semantic-imenu-sort-bucket-function)
122 (defcustom semantic-imenu-index-directory nil
123 "Non nil to index the entire directory for tags.
124 Doesn't actually parse the entire directory, but displays tags for all files
125 currently listed in the current Semantic database.
126 This variable has no meaning if semanticdb is not active."
127 :group 'semantic-imenu
128 :type 'boolean)
130 (defcustom semantic-imenu-auto-rebuild-directory-indexes nil
131 "If non-nil automatically rebuild directory index imenus.
132 That is when a directory index imenu is updated, automatically rebuild
133 other buffer local ones based on the same semanticdb."
134 :group 'semantic-imenu
135 :type 'boolean)
137 (defvar semantic-imenu-directory-current-file nil
138 "When building a file index, this is the file name currently being built.")
140 (defvar semantic-imenu-auto-rebuild-running nil
141 "Non-nil if `semantic-imenu-rebuild-directory-indexes' is running.")
143 ;;;###autoload
144 (defvar semantic-imenu-expandable-tag-classes '(type)
145 "List of expandable tag classes.
146 Tags of those classes will be given submenu with children.
147 By default, a `type' has interesting children. In Texinfo, however, a
148 `section' has interesting children.")
149 (make-variable-buffer-local 'semantic-imenu-expandable-tag-classes)
150 (semantic-varalias-obsolete 'semantic-imenu-expandable-token
151 'semantic-imenu-expandable-tag-classes "23.2")
153 ;;; Code:
154 (defun semantic-imenu-tag-overlay (tag)
155 "Return the overlay belonging to tag.
156 If TAG doesn't have an overlay, and instead as a vector of positions,
157 concoct a combination of file name, and position."
158 (let ((o (semantic-tag-overlay tag)))
159 (if (not (semantic-overlay-p o))
160 (let ((v (make-vector 3 nil)))
161 (aset v 0 semantic-imenu-directory-current-file)
162 (aset v 1 (aref o 0))
163 (aset v 2 (aref o 1))
165 o)))
168 (defun semantic-imenu-goto-function (name position &optional rest)
169 "Move point associated with NAME to POSITION.
170 Used to override function `imenu-default-goto-function' so that
171 we can continue to use overlays to maintain the current position.
172 Optional argument REST is some extra stuff."
173 (require 'pulse)
174 (if (semantic-overlay-p position)
175 (let ((os (semantic-overlay-start position))
176 (ob (semantic-overlay-buffer position)))
177 (if os
178 (progn
179 (if (not (eq ob (current-buffer)))
180 (switch-to-buffer ob))
181 (imenu-default-goto-function name os rest)
182 (pulse-momentary-highlight-one-line (point))
184 ;; This should never happen, but check anyway.
185 (message "Imenu is out of date, try again. (internal bug)")
186 (setq imenu--index-alist nil)))
187 ;; When the POSITION is actually a pair of numbers in an array, then
188 ;; the file isn't loaded into the current buffer.
189 (if (vectorp position)
190 (let ((file (aref position 0))
191 (pos (aref position 1)))
192 (and file (find-file file))
193 (imenu-default-goto-function name pos rest)
194 (pulse-momentary-highlight-one-line (point))
196 ;; When the POSITION is the symbol 'file-only' it means that this
197 ;; is a directory index entry and there is no tags in this
198 ;; file. So just jump to the beginning of the file.
199 (if (eq position 'file-only)
200 (progn
201 (find-file name)
202 (imenu-default-goto-function name (point-min) rest)
203 (pulse-momentary-highlight-one-line (point))
205 ;; Probably POSITION don't came from a semantic imenu. Try
206 ;; the default imenu goto function.
207 (condition-case nil
208 (progn
209 (imenu-default-goto-function name position rest)
210 (pulse-momentary-highlight-one-line (point))
212 (error
213 (message "Semantic Imenu override problem. (Internal bug)")
214 (setq imenu--index-alist nil)))))
217 (defun semantic-imenu-flush-fcn (&optional ignore)
218 "This function is called as a hook to clear the imenu cache.
219 It is cleared after any parsing.
220 IGNORE arguments."
221 (if (eq imenu-create-index-function 'semantic-create-imenu-index)
222 (setq imenu--index-alist nil
223 imenu-menubar-modified-tick 0))
224 (remove-hook 'semantic-after-toplevel-cache-change-hook
225 'semantic-imenu-flush-fcn t)
226 (remove-hook 'semantic-after-partial-cache-change-hook
227 'semantic-imenu-flush-fcn t)
230 ;;;###autoload
231 (defun semantic-create-imenu-index (&optional stream)
232 "Create an imenu index for any buffer which supports Semantic.
233 Uses the output of the Semantic parser to create the index.
234 Optional argument STREAM is an optional stream of tags used to create menus."
235 (setq imenu-default-goto-function 'semantic-imenu-goto-function)
236 (prog1
237 (if (and semantic-imenu-index-directory
238 (featurep 'semantic/db)
239 (semanticdb-minor-mode-p))
240 (semantic-create-imenu-directory-index
241 (or stream (semantic-fetch-tags-fast)))
242 (semantic-create-imenu-index-1
243 (or stream (semantic-fetch-tags-fast)) nil))
244 (semantic-make-local-hook 'semantic-after-toplevel-cache-change-hook)
245 (add-hook 'semantic-after-toplevel-cache-change-hook
246 'semantic-imenu-flush-fcn nil t)
247 (semantic-make-local-hook 'semantic-after-partial-cache-change-hook)
248 (add-hook 'semantic-after-partial-cache-change-hook
249 'semantic-imenu-flush-fcn nil t)))
251 (defun semantic-create-imenu-directory-index (&optional stream)
252 "Create an imenu tag index based on all files active in semanticdb.
253 Optional argument STREAM is the stream of tags for the current buffer."
254 (if (not semanticdb-current-database)
255 (semantic-create-imenu-index-1 stream nil)
256 ;; We have a database, list all files, with the current file on top.
257 (let ((index (list
258 (cons (oref semanticdb-current-table file)
259 (or (semantic-create-imenu-index-1 stream nil)
260 ;; No tags in this file
261 'file-only))))
262 (tables (semanticdb-get-database-tables semanticdb-current-database)))
263 (while tables
264 (let ((semantic-imenu-directory-current-file
265 (oref (car tables) file))
266 tags)
267 (when (and (not (eq (car tables) semanticdb-current-table))
268 (semanticdb-live-p (car tables))
269 (semanticdb-equivalent-mode (car tables))
271 (setq tags (oref (car tables) tags)
272 index (cons (cons semantic-imenu-directory-current-file
273 (or (and tags
274 ;; don't pass nil stream because
275 ;; it will use the current
276 ;; buffer
277 (semantic-create-imenu-index-1
278 (oref (car tables) tags)
279 nil))
280 ;; no tags in the file
281 'file-only))
282 index)))
283 (setq tables (cdr tables))))
285 ;; If enabled automatically rebuild other imenu directory
286 ;; indexes based on the same Semantic database
287 (or (not semantic-imenu-auto-rebuild-directory-indexes)
288 ;; If auto rebuild already in progress does nothing
289 semantic-imenu-auto-rebuild-running
290 (unwind-protect
291 (progn
292 (setq semantic-imenu-auto-rebuild-running t)
293 (semantic-imenu-rebuild-directory-indexes
294 semanticdb-current-database))
295 (setq semantic-imenu-auto-rebuild-running nil)))
297 (nreverse index))))
299 (defun semantic-create-imenu-index-1 (stream &optional parent)
300 "Create an imenu index for any buffer which supports Semantic.
301 Uses the output of the Semantic parser to create the index.
302 STREAM is a stream of tags used to create menus.
303 Optional argument PARENT is a tag parent of STREAM."
304 (let ((tags stream)
305 (semantic-imenu-adopt-external-members
306 semantic-imenu-adopt-external-members))
307 ;; If we should regroup, do so.
308 (if semantic-imenu-adopt-external-members
309 (setq tags (semantic-adopt-external-members tags)
310 ;; Don't allow recursion here.
311 semantic-imenu-adopt-external-members nil))
312 ;; Test for bucketing vs not.
313 (if semantic-imenu-bucketize-file
314 (let ((buckets (semantic-bucketize
315 tags parent
316 semantic-imenu-sort-bucket-function))
317 item name
318 index)
319 (cond
320 ((null buckets)
321 nil)
322 ((or (cdr-safe buckets) ;; if buckets has more than one item in it.
323 (not semantic-imenu-buckets-to-submenu)) ;; to force separators between buckets
324 (while buckets
325 (setq name (car (car buckets))
326 item (cdr (car buckets)))
327 (if semantic-imenu-buckets-to-submenu
328 (progn
329 ;; Make submenus
330 (if item
331 (setq index
332 (cons (cons name
333 (semantic-create-imenu-subindex item))
334 index))))
335 ;; Glom everything together with "---" between
336 (if item
337 (setq index
338 (append index
339 ;; do not create a menu separator in the parent menu
340 ;; when creating a sub-menu
341 (if (memq (semantic-tag-class (car item))
342 semantic-imenu-expandable-tag-classes)
343 (semantic-create-imenu-subindex item)
344 (cons
345 '("---")
346 (semantic-create-imenu-subindex item)))))
348 (setq buckets (cdr buckets)))
349 (if semantic-imenu-buckets-to-submenu
350 (nreverse index)
351 index))
353 (setq name (car (car buckets))
354 item (cdr (car buckets)))
355 (semantic-create-imenu-subindex item))))
356 ;; Else, group everything together
357 (semantic-create-imenu-subindex tags))))
359 (defun semantic-create-imenu-subindex (tags)
360 "From TAGS, create an imenu index of interesting things."
361 (let ((notypecheck (not semantic-imenu-expand-type-members))
362 children index tag parts)
363 (while tags
364 (setq tag (car tags)
365 children (semantic-tag-components-with-overlays tag))
366 (if (and (not notypecheck)
367 (memq (semantic-tag-class tag)
368 semantic-imenu-expandable-tag-classes)
369 children
371 ;; to keep an homogeneous menu organization, type menu items
372 ;; always have a sub-menu with at least the *definition*
373 ;; item (even if the tag has no type components)
374 (progn
375 (setq parts children)
376 ;; There is options which create the submenu
377 ;; * Type has an overlay, but children do.
378 ;; The type doesn't have to have it's own overlay,
379 ;; but a type with no overlay and no children should be
380 ;; invalid.
381 (setq index
382 (cons
383 (cons
384 (funcall semantic-imenu-summary-function tag)
385 ;; Add a menu for getting at the type definitions
386 (if (and parts
387 ;; Note to self: enable menu items for
388 ;; sub parts even if they are not proper
389 ;; tags.
390 (semantic-tag-p (car parts)))
391 (let ((submenu
392 (if (and semantic-imenu-bucketize-type-members
393 semantic-imenu-bucketize-file)
394 (semantic-create-imenu-index-1 parts tag)
395 (semantic-create-imenu-subindex parts))))
396 ;; Only add a *definition* if we have a position
397 ;; in that type tag.
398 (if (semantic-tag-with-position-p tag)
399 (cons
400 (cons "*definition*"
401 (semantic-imenu-tag-overlay tag))
402 submenu)
403 submenu))
404 ;; There were no parts, or something like that, so
405 ;; instead just put the definition here.
406 (if (semantic-tag-with-position-p tag)
407 (semantic-imenu-tag-overlay tag)
408 nil)
410 index)))
411 (if (semantic-tag-with-position-p tag)
412 (setq index (cons
413 (cons
414 (funcall semantic-imenu-summary-function tag)
415 (semantic-imenu-tag-overlay tag))
416 index))))
417 (setq tags (cdr tags)))
418 ;; `imenu--split-submenus' sort submenus according to
419 ;; `imenu-sort-function' setting and split them up if they are
420 ;; longer than `imenu-max-items'.
421 (imenu--split-submenus (nreverse index))))
423 ;;; directory imenu rebuilding.
425 (defun semantic-imenu-rebuild-directory-indexes (db)
426 "Rebuild directory index imenus based on Semantic database DB."
427 (let ((l (buffer-list))
429 (while l
430 (setq b (car l)
431 l (cdr l))
432 (if (and (not (eq b (current-buffer)))
433 (buffer-live-p b))
434 (with-current-buffer b
435 ;; If there is a buffer local Semantic index directory
436 ;; imenu
437 (when (and (eq imenu-create-index-function
438 'semantic-create-imenu-index)
439 semanticdb-current-database
440 (eq semanticdb-current-database db))
441 ;; Rebuild the imenu
442 (imenu--cleanup)
443 (setq imenu--index-alist nil)
444 (funcall
445 (if (fboundp 'imenu-menu-filter)
446 ;; XEmacs imenu
447 'imenu-menu-filter
448 ;; Emacs imenu
449 'imenu-update-menubar))))))))
451 (defun semantic-imenu-semanticdb-hook ()
452 "Function to be called from `semanticdb-mode-hook'.
453 Clears all imenu menus that may be depending on the database."
454 (require 'semantic/db-mode)
455 (semantic-map-buffers
456 #'(lambda ()
457 ;; Set up semanticdb environment if enabled.
458 (if (semanticdb-minor-mode-p)
459 (semanticdb-semantic-init-hook-fcn))
460 ;; Clear imenu cache to redraw the imenu.
461 (semantic-imenu-flush-fcn))))
463 (add-hook 'semanticdb-mode-hook 'semantic-imenu-semanticdb-hook)
465 ;;; Interactive Utilities
467 (defun semantic-imenu-toggle-bucketize-file ()
468 "Toggle the ability of imenu to bucketize the current file."
469 (interactive)
470 (setq semantic-imenu-bucketize-file (not semantic-imenu-bucketize-file))
471 ;; Force a rescan
472 (setq imenu--index-alist nil))
474 (defun semantic-imenu-toggle-buckets-to-submenu ()
475 "Toggle the ability of imenu to turn buckets into submenus."
476 (interactive)
477 (setq semantic-imenu-buckets-to-submenu (not semantic-imenu-buckets-to-submenu))
478 ;; Force a rescan
479 (setq imenu--index-alist nil))
481 (defun semantic-imenu-toggle-bucketize-type-parts ()
482 "Toggle the ability of imenu to bucketize the current file."
483 (interactive)
484 (setq semantic-imenu-bucketize-type-members (not semantic-imenu-bucketize-type-members))
485 ;; Force a rescan
486 (setq imenu--index-alist nil))
488 ;;; Which function support
490 ;; The which-function library will display the current function in the
491 ;; mode line. It tries to do this through imenu. With a semantic parsed
492 ;; buffer, there is a much more efficient way of doing this.
493 ;; Advise `which-function' so that we optionally use semantic tags
494 ;; instead, and get better stuff.
495 (require 'advice)
497 (defvar semantic-which-function 'semantic-default-which-function
498 "Function to convert semantic tags into `which-function' text.")
500 (defcustom semantic-which-function-use-color nil
501 "Use color when displaying the current function with `which-function'."
502 :group 'semantic-imenu
503 :type 'boolean)
505 (defun semantic-default-which-function (taglist)
506 "Convert TAGLIST into a string usable by `which-function'.
507 Returns the first tag name in the list, unless it is a type,
508 in which case it concatenates them together."
509 (cond ((eq (length taglist) 1)
510 (semantic-format-tag-abbreviate
511 (car taglist) nil semantic-which-function-use-color))
512 ((memq (semantic-tag-class (car taglist))
513 semantic-imenu-expandable-tag-classes)
514 (concat (semantic-format-tag-name
515 (car taglist) nil semantic-which-function-use-color)
516 (car semantic-type-relation-separator-character)
517 ;; recurse until we no longer have a type
518 ;; or any tags left.
519 (semantic-default-which-function (cdr taglist))))
520 (t (semantic-format-tag-abbreviate
521 (car taglist) nil semantic-which-function-use-color))))
523 ;; (defadvice which-function (around semantic-which activate)
524 ;; "Choose the function to display via semantic if it is currently active."
525 ;; (if (and (featurep 'semantic) semantic--buffer-cache)
526 ;; (let ((ol (semantic-find-tag-by-overlay)))
527 ;; (setq ad-return-value (funcall semantic-which-function ol)))
528 ;; ad-do-it))
530 (provide 'semantic/imenu)
532 ;; Local variables:
533 ;; generated-autoload-file: "loaddefs.el"
534 ;; generated-autoload-load-name: "semantic/imenu"
535 ;; End:
537 ;;; semantic/imenu.el ends here