1
Fork 0
mirror of git://git.sv.gnu.org/emacs.git synced 2025-12-10 08:10:21 -08:00
emacs/lisp/cedet/semantic/analyze/refs.el
Paul Eggert 3155cb1585 Merge from origin/emacs-26
11bd8aa24b Fix flymake's loading of subr-x
b500e06f4d Fix Bug#28896
d815de017b Skip a Flymake test for old gcc versions
fd3d8610b2 Make :align-to account for display-line-numbers
831eafc8ae Augment Flymake API for third-party extensions
ddd547fada Improve treatment of Fortran's "class default"
234b1e3864 Flymake backends must check proc obsoleteness in source bu...
3ea6a4d4ba Skip an rsync test in tramp-tests.el
25f83fa7c5 ; Indentation fixes
4d578d432d On Windows default a frame's border width to zero (Bug#28873)
6f1dea5c74 Spelling fixes
b8433b0954 Use pop-to-buffer-same-window instead of switch-to-buffer
2f7163fb72 Fix the MSDOS build.
2551d28fe8 Fix line number display after 'widen'
dc8812829b Remove resizable attribute on macOS undecorated frames (bu...
b970a4a52a Fix handling of `border-width' in `frameset--restore-frame...
445e92658f Mention how to send CC to > 1 address in a bug report
8ca6fa585a Improve format-time-string doc
2e1b3522b8 Improve documentation of 'line-number-display-width'
5b6e59cfdb Implement vc-default-dir-extra-headers for vc-rcs
22adeca42a In NEWS give advice on use of `switch-to-buffer' (Bug#28645)
2c3e6f1ddc Dont update primary selection with winner-undo
b38724ab67 Work around ImageMagick bug 825
20cc68e871 Document rectangle-preview option more (Bug#27974)
a0b7b301dd Do not reject https://gnu.org in commit messages
fb4200a875 Fix Edebug spec for cl-defun (bug#24255)
db68cefe72 Fix errors in kmacro.el post-command-hook
c63b344c3d Fix range-error in image-dired.el
081d2187c4 Fix 'line-number-display-width' in hscrolled windows
16e85456e7 Fix error in tramp-smb-handle-insert-directory
613db8d35c Don't reject PBM header whitespace unnecessarily
3205b12a78 Fix regression in display of PPM images
1ca9ae7069 Require subr-x when compiling nnimap.el
de60992053 Fix ert-test finding by symbol (Bug#28849)
51615a8082 Don't remember old debugger window (Bug#17882)
5980de3727 Disable python native completion on w32 (Bug#28580)
616b4c5956 Let select-frame-by-name choose any frame when called from...
8eb3c01dbd * lisp/dired-aux.el (dired-create-directory): Doc fix.
325dfdae13 Avoid compilation warnings in optimized builds
f79382819c ; * src/composite.c (Fclear_composition_cache): Fix last c...
f95cd5cd70 Improve customization of arabic-shaper-ZWNJ-handling.
a7f154688d Improve customization type of 'mouse-drag-and-drop-region'
864734d112 ; Prefer https: to http: in GNU URLs
3c78960a47 Encourage https: in commit messages
def9715282 ; Cleanup of etc/NEWS
4e59ecc646 Fix wording in Elisp manual's child frames section (Bug#28...
eda9f5018c Another fix for C mode fontification of w32 source files
05aadd8990 Fix fontification of ALIGN_STACK functions
aa0c38f358 Make sure thread stack is properly aligned on MS-Windows
d7038020aa Do not under-align pseudovectors
ff33053012 Fix indentation bug in multi-line CSS selectors
8968be822e ; * etc/NEWS: Grammar and spelling fixes
716b84034d gnutls_mac_get_nonce_size has been added in gnutls 3.3
55e313f7be ; * CONTRIBUTE: More suggestions for using US English.
622c675648 * CONTRIBUTE: Suggest American English.

# Conflicts:
#	etc/NEWS
#	msdos/sed2v2.inp
2017-10-20 22:44:13 -07:00

361 lines
12 KiB
EmacsLisp

;;; semantic/analyze/refs.el --- Analysis of the references between tags.
;; Copyright (C) 2008-2017 Free Software Foundation, Inc.
;; Author: Eric M. Ludlam <zappo@gnu.org>
;; 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 <https://www.gnu.org/licenses/>.
;;; Commentary:
;;
;; Analyze the references between tags.
;;
;; The original purpose of these analysis is to provide a way to jump
;; between a prototype and implementation.
;;
;; Finding all prototype/impl matches is hard because you have to search
;; through the entire set of allowed databases to capture all possible
;; refs. The core analysis class stores basic starting point, and then
;; entire raw search data, which is expensive to calculate.
;;
;; Once the raw data is available, queries for impl, prototype, or
;; perhaps other things become cheap.
(require 'semantic)
(require 'semantic/analyze)
(require 'semantic/db-find)
(eval-when-compile (require 'semantic/find))
(declare-function data-debug-new-buffer "data-debug")
(declare-function data-debug-insert-object-slots "eieio-datadebug")
(declare-function semantic-momentary-highlight-tag "semantic/decorate")
;;; Code:
(defclass semantic-analyze-references ()
((tag :initarg :tag
:type semantic-tag
:documentation
"The starting TAG we are providing references analysis for.")
(tagdb :initarg :tagdb
:documentation
"The database that tag can be found in.")
(scope :initarg :scope
:documentation "A Scope object.")
(rawsearchdata :initarg :rawsearchdata
:documentation
"The raw search data for TAG's name across all databases.")
;; Note: Should I cache queried data here? I expect that searching
;; through rawsearchdata will be super-fast, so why bother?
)
"Class containing data from a semantic analysis.")
(define-overloadable-function semantic-analyze-tag-references (tag &optional db)
"Analyze the references for TAG.
Returns a class with information about TAG.
Optional argument DB is a database. It will be used to help
locate TAG.
Use `semantic-analyze-current-tag' to debug this fcn.")
(defun semantic-analyze-tag-references-default (tag &optional db)
"Analyze the references for TAG.
Returns a class with information about TAG.
Optional argument DB is a database. It will be used to help
locate TAG.
Use `semantic-analyze-current-tag' to debug this fcn."
(when (not (semantic-tag-p tag)) (signal 'wrong-type-argument (list 'semantic-tag-p tag)))
(let ((allhits nil)
(scope nil)
)
(save-excursion
(semantic-go-to-tag tag db)
(setq scope (semantic-calculate-scope))
(setq allhits (semantic--analyze-refs-full-lookup tag scope t))
(semantic-analyze-references (semantic-tag-name tag)
:tag tag
:tagdb db
:scope scope
:rawsearchdata allhits)
)))
;;; METHODS
;;
;; These accessor methods will calculate the useful bits from the context, and cache values
;; into the context.
(cl-defmethod semantic-analyze-refs-impl ((refs semantic-analyze-references) &optional in-buffer)
"Return the implementations derived in the reference analyzer REFS.
Optional argument IN-BUFFER indicates that the returned tag should be in an active buffer."
(let ((allhits (oref refs rawsearchdata))
(tag (oref refs :tag))
(impl nil)
)
(semanticdb-find-result-mapc
(lambda (T DB)
"Examine T in the database DB, and sort it."
(let* ((ans (semanticdb-normalize-one-tag DB T))
(aT (cdr ans))
(aDB (car ans))
)
(when (and (not (semantic-tag-prototype-p aT))
(semantic-tag-similar-p tag aT
:prototype-flag
:parent
:typemodifiers
:default-value))
(when in-buffer (save-excursion (semantic-go-to-tag aT aDB)))
(push aT impl))))
allhits)
impl))
(cl-defmethod semantic-analyze-refs-proto ((refs semantic-analyze-references) &optional in-buffer)
"Return the prototypes derived in the reference analyzer REFS.
Optional argument IN-BUFFER indicates that the returned tag should be in an active buffer."
(let ((allhits (oref refs rawsearchdata))
(tag (oref refs :tag))
(proto nil))
(semanticdb-find-result-mapc
(lambda (T DB)
"Examine T in the database DB, and sort it."
(let* ((ans (semanticdb-normalize-one-tag DB T))
(aT (cdr ans))
(aDB (car ans))
)
(when (and (semantic-tag-prototype-p aT)
(semantic-tag-similar-p tag aT
:prototype-flag
:parent
:typemodifiers
:default-value))
(when in-buffer (save-excursion (semantic-go-to-tag aT aDB)))
(push aT proto))))
allhits)
proto))
;;; LOOKUP
;;
(defun semantic--analyze-refs-full-lookup (tag scope &optional noerror)
"Perform a full lookup for all occurrences of TAG in the current project.
TAG should be the tag currently under point.
SCOPE is the scope the cursor is in. From this a list of parents is
derived. If SCOPE does not have parents, then only a simple lookup is done.
Optional argument NOERROR means don't error if the lookup fails."
(if (not (oref scope parents))
;; If this tag has some named parent, but is not
(semantic--analyze-refs-full-lookup-simple tag noerror)
;; We have some sort of lineage we need to consider when we do
;; our side lookup of tags.
(semantic--analyze-refs-full-lookup-with-parents tag scope)
))
(defun semantic--analyze-refs-find-child-in-find-results (find-results name class)
"Find in FIND-RESULT a tag NAME which is a child of a tag in FIND-RESULTS.
CLASS is the class of the tag that ought to be returned."
(let ((ans nil)
(subans nil))
;; Loop over each segment of the find results.
(dolist (FDB find-results)
(setq subans nil)
;; Loop over each tag in the find results.
(dolist (T (cdr FDB))
;; For each tag, get the children.
(let* ((chil (semantic-tag-type-members T))
(match (semantic-find-tags-by-name name chil)))
;; Go over the matches, looking for matching tag class.
(dolist (M match)
(when (semantic-tag-of-class-p M class)
(push M subans)))))
;; Store current matches into a new find results.
(when subans
(push (cons (car FDB) subans) ans))
)
ans))
(defun semantic--analyze-refs-find-tags-with-parent (find-results parents)
"Find in FIND-RESULTS all tags with PARENTS.
NAME is the name of the tag needing finding.
PARENTS is a list of names."
(let ((ans nil) (usingnames nil))
;; Loop over the find-results passed in.
(semanticdb-find-result-mapc
(lambda (tag db)
(let* ((p (semantic-tag-named-parent tag))
(ps (when (stringp p) (semantic-analyze-split-name p))))
(when (stringp ps) (setq ps (list ps)))
(when ps
;; If there is a perfect match, then use it.
(if (equal ps parents)
(push (list db tag) ans))
;; No match, find something from our list of using names.
;; Do we need to split UN?
(save-excursion
(semantic-go-to-tag tag db)
(setq usingnames nil)
(let ((imports (semantic-ctxt-imported-packages)))
;; Derive the names from all the using statements.
(mapc (lambda (T)
(setq usingnames
(cons (semantic-format-tag-name-from-anything T) usingnames)))
imports))
(dolist (UN usingnames)
(when (equal (cons UN ps) parents)
(push (list db tag) ans)
(setq usingnames (cdr usingnames))))
))))
find-results)
ans))
(defun semantic--analyze-refs-full-lookup-with-parents (tag scope)
"Perform a lookup for all occurrences of TAG based on TAG's SCOPE.
TAG should be the tag currently under point."
(let* ((classmatch (semantic-tag-class tag))
(plist (mapcar (lambda (T) (semantic-tag-name T)) (oref scope parents)))
;; The first item in the parent list
(name (car plist))
;; Stuff from the simple list.
(simple (semantic--analyze-refs-full-lookup-simple tag t))
;; Find all hits for the first parent name.
(brute (semanticdb-find-tags-collector
(lambda (table tags)
(semanticdb-deep-find-tags-by-name-method table name tags)
)
nil nil t))
;; Prime the answer.
(answer (semantic--analyze-refs-find-tags-with-parent simple plist))
)
;; First parent is already search to initialize "brute".
(setq plist (cdr plist))
;; Go through the list of parents, and try to find matches.
;; As we cycle through plist, for each level look for NAME,
;; and compare the named-parent, and also dive into the next item of
;; plist.
(while (and plist brute)
;; Find direct matches
(let* ((direct (semantic--analyze-refs-find-child-in-find-results
brute (semantic-tag-name tag) classmatch))
(pdirect (semantic--analyze-refs-find-tags-with-parent
direct plist)))
(setq answer (append pdirect answer)))
;; The next set of search items.
(setq brute (semantic--analyze-refs-find-child-in-find-results
brute (car plist) 'type))
(setq plist (cdr plist)))
;; Brute now has the children from the very last match.
(let* ((direct (semantic--analyze-refs-find-child-in-find-results
brute (semantic-tag-name tag) classmatch))
)
(setq answer (append direct answer)))
answer))
(defun semantic--analyze-refs-full-lookup-simple (tag &optional noerror)
"Perform a simple lookup for occurrences of TAG in the current project.
TAG should be the tag currently under point.
Optional NOERROR means don't throw errors on failure to find something.
This only compares the tag name, and does not infer any matches in namespaces,
or parts of some other data structure.
Only works for tags in the global namespace."
(let* ((name (semantic-tag-name tag))
(brute (semanticdb-find-tags-collector
(lambda (table tags)
(semanticdb-find-tags-by-name-method table name tags)
)
nil ;; This may need to be the entire project??
nil t))
)
(when (and (not brute) (not noerror))
;; An error, because tag under point ought to be found.
(error "Cannot find any references to %s in wide search" name))
(let* ((classmatch (semantic-tag-class tag))
(RES
(semanticdb-find-tags-collector
(lambda (table tags)
(semantic-find-tags-by-class classmatch tags)
;; @todo - Add parent check also.
)
brute nil)))
(when (and (not RES) (not noerror))
(error "Cannot find any definitions for %s in wide search"
(semantic-tag-name tag)))
;; Return the matching tags and databases.
RES)))
;;; USER COMMANDS
;;
;;;###autoload
(defun semantic-analyze-current-tag ()
"Analyze the tag under point."
(interactive)
(let* ((tag (semantic-current-tag))
(start (current-time))
(sac (semantic-analyze-tag-references tag))
)
(message "Analysis took %.2f seconds." (semantic-elapsed-time start nil))
(if sac
(progn
(require 'eieio-datadebug)
(data-debug-new-buffer "*Analyzer Reference ADEBUG*")
(data-debug-insert-object-slots sac "]"))
(message "No Context to analyze here."))))
;;;###autoload
(defun semantic-analyze-proto-impl-toggle ()
"Toggle between the implementation, and a prototype of tag under point."
(interactive)
(require 'semantic/decorate)
(semantic-fetch-tags)
(let* ((tag (semantic-current-tag))
(sar (if tag
(semantic-analyze-tag-references tag)
(error "Point must be in a declaration")))
(target (if (semantic-tag-prototype-p tag)
(car (semantic-analyze-refs-impl sar t))
(car (semantic-analyze-refs-proto sar t))))
)
(when (not target)
(error "Could not find suitable %s"
(if (semantic-tag-prototype-p tag) "implementation" "prototype")))
(push-mark)
(semantic-go-to-tag target)
(pop-to-buffer-same-window (current-buffer))
(semantic-momentary-highlight-tag target))
)
(provide 'semantic/analyze/refs)
;; Local variables:
;; generated-autoload-file: "../loaddefs.el"
;; generated-autoload-load-name: "semantic/analyze/refs"
;; End:
;;; semantic/analyze/refs.el ends here