123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538 |
- ;;; semantic/imenu.el --- Use Semantic as an imenu tag generator
- ;;; Copyright (C) 2000-2005, 2007-2008, 2010-2012
- ;; Free Software Foundation, Inc.
- ;; Author: Eric M. Ludlam <zappo@gnu.org>
- ;; Maintainer: Eric Ludlam
- ;; This file is part of GNU Emacs.
- ;; GNU Emacs is free software: you can redistribute it and/or modify
- ;; it under the terms of the GNU General Public License as published by
- ;; the Free Software Foundation, either version 3 of the License, or
- ;; (at your option) any later version.
- ;; GNU Emacs is distributed in the hope that it will be useful,
- ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
- ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ;; GNU General Public License for more details.
- ;; You should have received a copy of the GNU General Public License
- ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
- ;;; Commentary:
- ;;
- ;; This support function can be used in any buffer which supports
- ;; the bovinator to create the imenu index.
- ;;
- ;; To use this in a buffer, do this in a hook.
- ;;
- ;; (add-hook 'mode-hook
- ;; (lambda ()
- ;; (setq imenu-create-index-function 'semantic-create-imenu-index)
- ;; ))
- (require 'semantic)
- (require 'semantic/format)
- (require 'semantic/db)
- (require 'semantic/db-file)
- (require 'semantic/sort)
- (require 'imenu)
- (declare-function pulse-momentary-highlight-one-line "pulse" (o &optional face))
- (declare-function semanticdb-semantic-init-hook-fcn "db-mode")
- ;; Because semantic imenu tags will hose the current imenu handling
- ;; code in speedbar, force semantic/sb in.
- (if (featurep 'speedbar)
- (require 'semantic/sb)
- (add-hook 'speedbar-load-hook (lambda () (require 'semantic/sb))))
- (defgroup semantic-imenu nil
- "Semantic interface to Imenu."
- :group 'semantic
- :group 'imenu
- )
- ;;;###autoload
- (defcustom semantic-imenu-summary-function 'semantic-format-tag-abbreviate
- "*Function to use when creating items in Imenu.
- Some useful functions are found in `semantic-format-tag-functions'."
- :group 'semantic-imenu
- :type semantic-format-tag-custom-list)
- (make-variable-buffer-local 'semantic-imenu-summary-function)
- ;;;###autoload
- (defcustom semantic-imenu-bucketize-file t
- "*Non-nil if tags in a file are to be grouped into buckets."
- :group 'semantic-imenu
- :type 'boolean)
- (make-variable-buffer-local 'semantic-imenu-bucketize-file)
- (defcustom semantic-imenu-adopt-external-members t
- "*Non-nil if types in a file should adopt externally defined members.
- C++ and CLOS can define methods that are not in the body of a class
- definition."
- :group 'semantic-imenu
- :type 'boolean)
- (defcustom semantic-imenu-buckets-to-submenu t
- "*Non-nil if buckets of tags are to be turned into submenus.
- This option is ignored if `semantic-imenu-bucketize-file' is nil."
- :group 'semantic-imenu
- :type 'boolean)
- (make-variable-buffer-local 'semantic-imenu-buckets-to-submenu)
- ;;;###autoload
- (defcustom semantic-imenu-expand-type-members t
- "*Non-nil if types should have submenus with members in them."
- :group 'semantic-imenu
- :type 'boolean)
- (make-variable-buffer-local 'semantic-imenu-expand-type-members)
- (semantic-varalias-obsolete 'semantic-imenu-expand-type-parts
- 'semantic-imenu-expand-type-members "23.2")
- (defcustom semantic-imenu-bucketize-type-members t
- "*Non-nil if members of a type should be grouped into buckets.
- A nil value means to keep them in the same order.
- Overridden to nil if `semantic-imenu-bucketize-file' is nil."
- :group 'semantic-imenu
- :type 'boolean)
- (make-variable-buffer-local 'semantic-imenu-bucketize-type-members)
- (semantic-varalias-obsolete 'semantic-imenu-bucketize-type-parts
- 'semantic-imenu-bucketize-type-members "23.2")
- (defcustom semantic-imenu-sort-bucket-function nil
- "*Function to use when sorting tags in the buckets of functions.
- See `semantic-bucketize' and the FILTER argument for more details on this function."
- :group 'semantic-imenu
- :type '(radio (const :tag "No Sorting" nil)
- (const semantic-sort-tags-by-name-increasing)
- (const semantic-sort-tags-by-name-decreasing)
- (const semantic-sort-tags-by-type-increasing)
- (const semantic-sort-tags-by-type-decreasing)
- (const semantic-sort-tags-by-name-increasing-ci)
- (const semantic-sort-tags-by-name-decreasing-ci)
- (const semantic-sort-tags-by-type-increasing-ci)
- (const semantic-sort-tags-by-type-decreasing-ci)
- (function)))
- (make-variable-buffer-local 'semantic-imenu-sort-bucket-function)
- (defcustom semantic-imenu-index-directory nil
- "*Non nil to index the entire directory for tags.
- Doesn't actually parse the entire directory, but displays tags for all files
- currently listed in the current Semantic database.
- This variable has no meaning if semanticdb is not active."
- :group 'semantic-imenu
- :type 'boolean)
- (defcustom semantic-imenu-auto-rebuild-directory-indexes nil
- "*If non-nil automatically rebuild directory index imenus.
- That is when a directory index imenu is updated, automatically rebuild
- other buffer local ones based on the same semanticdb."
- :group 'semantic-imenu
- :type 'boolean)
- (defvar semantic-imenu-directory-current-file nil
- "When building a file index, this is the file name currently being built.")
- (defvar semantic-imenu-auto-rebuild-running nil
- "Non-nil if `semantic-imenu-rebuild-directory-indexes' is running.")
- ;;;###autoload
- (defvar semantic-imenu-expandable-tag-classes '(type)
- "List of expandable tag classes.
- Tags of those classes will be given submenu with children.
- By default, a `type' has interesting children. In Texinfo, however, a
- `section' has interesting children.")
- (make-variable-buffer-local 'semantic-imenu-expandable-tag-classes)
- (semantic-varalias-obsolete 'semantic-imenu-expandable-token
- 'semantic-imenu-expandable-tag-classes "23.2")
- ;;; Code:
- (defun semantic-imenu-tag-overlay (tag)
- "Return the overlay belonging to tag.
- If TAG doesn't have an overlay, and instead as a vector of positions,
- concoct a combination of file name, and position."
- (let ((o (semantic-tag-overlay tag)))
- (if (not (semantic-overlay-p o))
- (let ((v (make-vector 3 nil)))
- (aset v 0 semantic-imenu-directory-current-file)
- (aset v 1 (aref o 0))
- (aset v 2 (aref o 1))
- v)
- o)))
- (defun semantic-imenu-goto-function (name position &optional rest)
- "Move point associated with NAME to POSITION.
- Used to override function `imenu-default-goto-function' so that
- we can continue to use overlays to maintain the current position.
- Optional argument REST is some extra stuff."
- (require 'pulse)
- (if (semantic-overlay-p position)
- (let ((os (semantic-overlay-start position))
- (ob (semantic-overlay-buffer position)))
- (if os
- (progn
- (if (not (eq ob (current-buffer)))
- (switch-to-buffer ob))
- (imenu-default-goto-function name os rest)
- (pulse-momentary-highlight-one-line (point))
- )
- ;; This should never happen, but check anyway.
- (message "Imenu is out of date, try again. (internal bug)")
- (setq imenu--index-alist nil)))
- ;; When the POSITION is actually a pair of numbers in an array, then
- ;; the file isn't loaded into the current buffer.
- (if (vectorp position)
- (let ((file (aref position 0))
- (pos (aref position 1)))
- (and file (find-file file))
- (imenu-default-goto-function name pos rest)
- (pulse-momentary-highlight-one-line (point))
- )
- ;; When the POSITION is the symbol 'file-only' it means that this
- ;; is a directory index entry and there is no tags in this
- ;; file. So just jump to the beginning of the file.
- (if (eq position 'file-only)
- (progn
- (find-file name)
- (imenu-default-goto-function name (point-min) rest)
- (pulse-momentary-highlight-one-line (point))
- )
- ;; Probably POSITION don't came from a semantic imenu. Try
- ;; the default imenu goto function.
- (condition-case nil
- (progn
- (imenu-default-goto-function name position rest)
- (pulse-momentary-highlight-one-line (point))
- )
- (error
- (message "Semantic Imenu override problem. (Internal bug)")
- (setq imenu--index-alist nil)))))
- ))
- (defun semantic-imenu-flush-fcn (&optional ignore)
- "This function is called as a hook to clear the imenu cache.
- It is cleared after any parsing.
- IGNORE arguments."
- (if (eq imenu-create-index-function 'semantic-create-imenu-index)
- (setq imenu--index-alist nil
- imenu-menubar-modified-tick 0))
- (remove-hook 'semantic-after-toplevel-cache-change-hook
- 'semantic-imenu-flush-fcn t)
- (remove-hook 'semantic-after-partial-cache-change-hook
- 'semantic-imenu-flush-fcn t)
- )
- ;;;###autoload
- (defun semantic-create-imenu-index (&optional stream)
- "Create an imenu index for any buffer which supports Semantic.
- Uses the output of the Semantic parser to create the index.
- Optional argument STREAM is an optional stream of tags used to create menus."
- (setq imenu-default-goto-function 'semantic-imenu-goto-function)
- (prog1
- (if (and semantic-imenu-index-directory
- (featurep 'semantic/db)
- (semanticdb-minor-mode-p))
- (semantic-create-imenu-directory-index
- (or stream (semantic-fetch-tags-fast)))
- (semantic-create-imenu-index-1
- (or stream (semantic-fetch-tags-fast)) nil))
- (semantic-make-local-hook 'semantic-after-toplevel-cache-change-hook)
- (add-hook 'semantic-after-toplevel-cache-change-hook
- 'semantic-imenu-flush-fcn nil t)
- (semantic-make-local-hook 'semantic-after-partial-cache-change-hook)
- (add-hook 'semantic-after-partial-cache-change-hook
- 'semantic-imenu-flush-fcn nil t)))
- (defun semantic-create-imenu-directory-index (&optional stream)
- "Create an imenu tag index based on all files active in semanticdb.
- Optional argument STREAM is the stream of tags for the current buffer."
- (if (not semanticdb-current-database)
- (semantic-create-imenu-index-1 stream nil)
- ;; We have a database, list all files, with the current file on top.
- (let ((index (list
- (cons (oref semanticdb-current-table file)
- (or (semantic-create-imenu-index-1 stream nil)
- ;; No tags in this file
- 'file-only))))
- (tables (semanticdb-get-database-tables semanticdb-current-database)))
- (while tables
- (let ((semantic-imenu-directory-current-file
- (oref (car tables) file))
- tags)
- (when (and (not (eq (car tables) semanticdb-current-table))
- (semanticdb-live-p (car tables))
- (semanticdb-equivalent-mode (car tables))
- )
- (setq tags (oref (car tables) tags)
- index (cons (cons semantic-imenu-directory-current-file
- (or (and tags
- ;; don't pass nil stream because
- ;; it will use the current
- ;; buffer
- (semantic-create-imenu-index-1
- (oref (car tables) tags)
- nil))
- ;; no tags in the file
- 'file-only))
- index)))
- (setq tables (cdr tables))))
- ;; If enabled automatically rebuild other imenu directory
- ;; indexes based on the same Semantic database
- (or (not semantic-imenu-auto-rebuild-directory-indexes)
- ;; If auto rebuild already in progress does nothing
- semantic-imenu-auto-rebuild-running
- (unwind-protect
- (progn
- (setq semantic-imenu-auto-rebuild-running t)
- (semantic-imenu-rebuild-directory-indexes
- semanticdb-current-database))
- (setq semantic-imenu-auto-rebuild-running nil)))
- (nreverse index))))
- (defun semantic-create-imenu-index-1 (stream &optional parent)
- "Create an imenu index for any buffer which supports Semantic.
- Uses the output of the Semantic parser to create the index.
- STREAM is a stream of tags used to create menus.
- Optional argument PARENT is a tag parent of STREAM."
- (let ((tags stream)
- (semantic-imenu-adopt-external-members
- semantic-imenu-adopt-external-members))
- ;; If we should regroup, do so.
- (if semantic-imenu-adopt-external-members
- (setq tags (semantic-adopt-external-members tags)
- ;; Don't allow recursion here.
- semantic-imenu-adopt-external-members nil))
- ;; Test for bucketing vs not.
- (if semantic-imenu-bucketize-file
- (let ((buckets (semantic-bucketize
- tags parent
- semantic-imenu-sort-bucket-function))
- item name
- index)
- (cond
- ((null buckets)
- nil)
- ((or (cdr-safe buckets) ;; if buckets has more than one item in it.
- (not semantic-imenu-buckets-to-submenu)) ;; to force separators between buckets
- (while buckets
- (setq name (car (car buckets))
- item (cdr (car buckets)))
- (if semantic-imenu-buckets-to-submenu
- (progn
- ;; Make submenus
- (if item
- (setq index
- (cons (cons name
- (semantic-create-imenu-subindex item))
- index))))
- ;; Glom everything together with "---" between
- (if item
- (setq index
- (append index
- ;; do not create a menu separator in the parent menu
- ;; when creating a sub-menu
- (if (memq (semantic-tag-class (car item))
- semantic-imenu-expandable-tag-classes)
- (semantic-create-imenu-subindex item)
- (cons
- '("---")
- (semantic-create-imenu-subindex item)))))
- ))
- (setq buckets (cdr buckets)))
- (if semantic-imenu-buckets-to-submenu
- (nreverse index)
- index))
- (t
- (setq name (car (car buckets))
- item (cdr (car buckets)))
- (semantic-create-imenu-subindex item))))
- ;; Else, group everything together
- (semantic-create-imenu-subindex tags))))
- (defun semantic-create-imenu-subindex (tags)
- "From TAGS, create an imenu index of interesting things."
- (let ((notypecheck (not semantic-imenu-expand-type-members))
- children index tag parts)
- (while tags
- (setq tag (car tags)
- children (semantic-tag-components-with-overlays tag))
- (if (and (not notypecheck)
- (memq (semantic-tag-class tag)
- semantic-imenu-expandable-tag-classes)
- children
- )
- ;; to keep an homogeneous menu organization, type menu items
- ;; always have a sub-menu with at least the *definition*
- ;; item (even if the tag has no type components)
- (progn
- (setq parts children)
- ;; There is options which create the submenu
- ;; * Type has an overlay, but children do.
- ;; The type doesn't have to have it's own overlay,
- ;; but a type with no overlay and no children should be
- ;; invalid.
- (setq index
- (cons
- (cons
- (funcall semantic-imenu-summary-function tag)
- ;; Add a menu for getting at the type definitions
- (if (and parts
- ;; Note to self: enable menu items for
- ;; sub parts even if they are not proper
- ;; tags.
- (semantic-tag-p (car parts)))
- (let ((submenu
- (if (and semantic-imenu-bucketize-type-members
- semantic-imenu-bucketize-file)
- (semantic-create-imenu-index-1 parts tag)
- (semantic-create-imenu-subindex parts))))
- ;; Only add a *definition* if we have a position
- ;; in that type tag.
- (if (semantic-tag-with-position-p tag)
- (cons
- (cons "*definition*"
- (semantic-imenu-tag-overlay tag))
- submenu)
- submenu))
- ;; There were no parts, or something like that, so
- ;; instead just put the definition here.
- (if (semantic-tag-with-position-p tag)
- (semantic-imenu-tag-overlay tag)
- nil)
- ))
- index)))
- (if (semantic-tag-with-position-p tag)
- (setq index (cons
- (cons
- (funcall semantic-imenu-summary-function tag)
- (semantic-imenu-tag-overlay tag))
- index))))
- (setq tags (cdr tags)))
- ;; `imenu--split-submenus' sort submenus according to
- ;; `imenu-sort-function' setting and split them up if they are
- ;; longer than `imenu-max-items'.
- (imenu--split-submenus (nreverse index))))
- ;;; directory imenu rebuilding.
- ;;
- (defun semantic-imenu-rebuild-directory-indexes (db)
- "Rebuild directory index imenus based on Semantic database DB."
- (let ((l (buffer-list))
- b)
- (while l
- (setq b (car l)
- l (cdr l))
- (if (and (not (eq b (current-buffer)))
- (buffer-live-p b))
- (with-current-buffer b
- ;; If there is a buffer local Semantic index directory
- ;; imenu
- (when (and (eq imenu-create-index-function
- 'semantic-create-imenu-index)
- semanticdb-current-database
- (eq semanticdb-current-database db))
- ;; Rebuild the imenu
- (imenu--cleanup)
- (setq imenu--index-alist nil)
- (funcall
- (if (fboundp 'imenu-menu-filter)
- ;; XEmacs imenu
- 'imenu-menu-filter
- ;; Emacs imenu
- 'imenu-update-menubar))))))))
- (defun semantic-imenu-semanticdb-hook ()
- "Function to be called from `semanticdb-mode-hook'.
- Clears all imenu menus that may be depending on the database."
- (require 'semantic/db-mode)
- (semantic-map-buffers
- #'(lambda ()
- ;; Set up semanticdb environment if enabled.
- (if (semanticdb-minor-mode-p)
- (semanticdb-semantic-init-hook-fcn))
- ;; Clear imenu cache to redraw the imenu.
- (semantic-imenu-flush-fcn))))
- (add-hook 'semanticdb-mode-hook 'semantic-imenu-semanticdb-hook)
- ;;; Interactive Utilities
- ;;
- (defun semantic-imenu-toggle-bucketize-file ()
- "Toggle the ability of imenu to bucketize the current file."
- (interactive)
- (setq semantic-imenu-bucketize-file (not semantic-imenu-bucketize-file))
- ;; Force a rescan
- (setq imenu--index-alist nil))
- (defun semantic-imenu-toggle-buckets-to-submenu ()
- "Toggle the ability of imenu to turn buckets into submenus."
- (interactive)
- (setq semantic-imenu-buckets-to-submenu (not semantic-imenu-buckets-to-submenu))
- ;; Force a rescan
- (setq imenu--index-alist nil))
- (defun semantic-imenu-toggle-bucketize-type-parts ()
- "Toggle the ability of imenu to bucketize the current file."
- (interactive)
- (setq semantic-imenu-bucketize-type-members (not semantic-imenu-bucketize-type-members))
- ;; Force a rescan
- (setq imenu--index-alist nil))
- ;;; Which function support
- ;;
- ;; The which-function library will display the current function in the
- ;; mode line. It tries do do this through imenu. With a semantic parsed
- ;; buffer, there is a much more efficient way of doing this.
- ;; Advise `which-function' so that we optionally use semantic tags
- ;; instead, and get better stuff.
- (require 'advice)
- (defvar semantic-which-function 'semantic-default-which-function
- "Function to convert semantic tags into `which-function' text.")
- (defcustom semantic-which-function-use-color nil
- "*Use color when displaying the current function with `which-function'."
- :group 'semantic-imenu
- :type 'boolean)
- (defun semantic-default-which-function (taglist)
- "Convert TAGLIST into a string usable by `which-function'.
- Returns the first tag name in the list, unless it is a type,
- in which case it concatenates them together."
- (cond ((eq (length taglist) 1)
- (semantic-format-tag-abbreviate
- (car taglist) nil semantic-which-function-use-color))
- ((memq (semantic-tag-class (car taglist))
- semantic-imenu-expandable-tag-classes)
- (concat (semantic-format-tag-name
- (car taglist) nil semantic-which-function-use-color)
- (car semantic-type-relation-separator-character)
- ;; recurse until we no longer have a type
- ;; or any tags left.
- (semantic-default-which-function (cdr taglist))))
- (t (semantic-format-tag-abbreviate
- (car taglist) nil semantic-which-function-use-color))))
- ;; (defadvice which-function (around semantic-which activate)
- ;; "Choose the function to display via semantic if it is currently active."
- ;; (if (and (featurep 'semantic) semantic--buffer-cache)
- ;; (let ((ol (semantic-find-tag-by-overlay)))
- ;; (setq ad-return-value (funcall semantic-which-function ol)))
- ;; ad-do-it))
- (provide 'semantic/imenu)
- ;; Local variables:
- ;; generated-autoload-file: "loaddefs.el"
- ;; generated-autoload-load-name: "semantic/imenu"
- ;; End:
- ;;; semantic/imenu.el ends here
|