mirror of
git://git.sv.gnu.org/emacs.git
synced 2025-12-10 08:10:21 -08:00
11bd8aa24bFix flymake's loading of subr-xb500e06f4dFix Bug#28896d815de017bSkip a Flymake test for old gcc versionsfd3d8610b2Make :align-to account for display-line-numbers831eafc8aeAugment Flymake API for third-party extensionsddd547fadaImprove treatment of Fortran's "class default"234b1e3864Flymake backends must check proc obsoleteness in source bu...3ea6a4d4baSkip an rsync test in tramp-tests.el25f83fa7c5; Indentation fixes4d578d432dOn Windows default a frame's border width to zero (Bug#28873)6f1dea5c74Spelling fixesb8433b0954Use pop-to-buffer-same-window instead of switch-to-buffer2f7163fb72Fix the MSDOS build.2551d28fe8Fix line number display after 'widen'dc8812829bRemove resizable attribute on macOS undecorated frames (bu...b970a4a52aFix handling of `border-width' in `frameset--restore-frame...445e92658fMention how to send CC to > 1 address in a bug report8ca6fa585aImprove format-time-string doc2e1b3522b8Improve documentation of 'line-number-display-width'5b6e59cfdbImplement vc-default-dir-extra-headers for vc-rcs22adeca42aIn NEWS give advice on use of `switch-to-buffer' (Bug#28645)2c3e6f1ddcDont update primary selection with winner-undob38724ab67Work around ImageMagick bug 82520cc68e871Document rectangle-preview option more (Bug#27974)a0b7b301ddDo not reject https://gnu.org in commit messagesfb4200a875Fix Edebug spec for cl-defun (bug#24255)db68cefe72Fix errors in kmacro.el post-command-hookc63b344c3dFix range-error in image-dired.el081d2187c4Fix 'line-number-display-width' in hscrolled windows16e85456e7Fix error in tramp-smb-handle-insert-directory613db8d35cDon't reject PBM header whitespace unnecessarily3205b12a78Fix regression in display of PPM images1ca9ae7069Require subr-x when compiling nnimap.elde60992053Fix ert-test finding by symbol (Bug#28849)51615a8082Don't remember old debugger window (Bug#17882)5980de3727Disable python native completion on w32 (Bug#28580)616b4c5956Let select-frame-by-name choose any frame when called from...8eb3c01dbd* lisp/dired-aux.el (dired-create-directory): Doc fix.325dfdae13Avoid compilation warnings in optimized buildsf79382819c; * src/composite.c (Fclear_composition_cache): Fix last c...f95cd5cd70Improve customization of arabic-shaper-ZWNJ-handling.a7f154688dImprove customization type of 'mouse-drag-and-drop-region'864734d112; Prefer https: to http: in GNU URLs3c78960a47Encourage https: in commit messagesdef9715282; Cleanup of etc/NEWS4e59ecc646Fix wording in Elisp manual's child frames section (Bug#28...eda9f5018cAnother fix for C mode fontification of w32 source files05aadd8990Fix fontification of ALIGN_STACK functionsaa0c38f358Make sure thread stack is properly aligned on MS-Windowsd7038020aaDo not under-align pseudovectorsff33053012Fix indentation bug in multi-line CSS selectors8968be822e; * etc/NEWS: Grammar and spelling fixes716b84034dgnutls_mac_get_nonce_size has been added in gnutls 3.355e313f7be; * CONTRIBUTE: More suggestions for using US English.622c675648* CONTRIBUTE: Suggest American English. # Conflicts: # etc/NEWS # msdos/sed2v2.inp
361 lines
12 KiB
EmacsLisp
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
|