refs.el 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  1. ;;; semantic/analyze/refs.el --- Analysis of the references between tags.
  2. ;; Copyright (C) 2008-2012 Free Software Foundation, Inc.
  3. ;; Author: Eric M. Ludlam <zappo@gnu.org>
  4. ;; This file is part of GNU Emacs.
  5. ;; GNU Emacs is free software: you can redistribute it and/or modify
  6. ;; it under the terms of the GNU General Public License as published by
  7. ;; the Free Software Foundation, either version 3 of the License, or
  8. ;; (at your option) any later version.
  9. ;; GNU Emacs is distributed in the hope that it will be useful,
  10. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. ;; GNU General Public License for more details.
  13. ;; You should have received a copy of the GNU General Public License
  14. ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
  15. ;;; Commentary:
  16. ;;
  17. ;; Analyze the references between tags.
  18. ;;
  19. ;; The original purpose of these analysis is to provide a way to jump
  20. ;; between a prototype and implementation.
  21. ;;
  22. ;; Finding all prototype/impl matches is hard because you have to search
  23. ;; through the entire set of allowed databases to capture all possible
  24. ;; refs. The core analysis class stores basic starting point, and then
  25. ;; entire raw search data, which is expensive to calculate.
  26. ;;
  27. ;; Once the raw data is available, queries for impl, prototype, or
  28. ;; perhaps other things become cheap.
  29. (require 'semantic)
  30. (require 'semantic/analyze)
  31. (require 'semantic/db-find)
  32. (eval-when-compile (require 'semantic/find))
  33. (declare-function data-debug-new-buffer "data-debug")
  34. (declare-function data-debug-insert-object-slots "eieio-datadebug")
  35. (declare-function semantic-momentary-highlight-tag "semantic/decorate")
  36. ;;; Code:
  37. (defclass semantic-analyze-references ()
  38. ((tag :initarg :tag
  39. :type semantic-tag
  40. :documentation
  41. "The starting TAG we are providing references analysis for.")
  42. (tagdb :initarg :tagdb
  43. :documentation
  44. "The database that tag can be found in.")
  45. (scope :initarg :scope
  46. :documentation "A Scope object.")
  47. (rawsearchdata :initarg :rawsearchdata
  48. :documentation
  49. "The raw search data for TAG's name across all databases.")
  50. ;; Note: Should I cache queried data here? I expect that searching
  51. ;; through rawsearchdata will be super-fast, so why bother?
  52. )
  53. "Class containing data from a semantic analysis.")
  54. (define-overloadable-function semantic-analyze-tag-references (tag &optional db)
  55. "Analyze the references for TAG.
  56. Returns a class with information about TAG.
  57. Optional argument DB is a database. It will be used to help
  58. locate TAG.
  59. Use `semantic-analyze-current-tag' to debug this fcn.")
  60. (defun semantic-analyze-tag-references-default (tag &optional db)
  61. "Analyze the references for TAG.
  62. Returns a class with information about TAG.
  63. Optional argument DB is a database. It will be used to help
  64. locate TAG.
  65. Use `semantic-analyze-current-tag' to debug this fcn."
  66. (when (not (semantic-tag-p tag)) (signal 'wrong-type-argument (list 'semantic-tag-p tag)))
  67. (let ((allhits nil)
  68. (scope nil)
  69. )
  70. (save-excursion
  71. (semantic-go-to-tag tag db)
  72. (setq scope (semantic-calculate-scope))
  73. (setq allhits (semantic--analyze-refs-full-lookup tag scope))
  74. (semantic-analyze-references (semantic-tag-name tag)
  75. :tag tag
  76. :tagdb db
  77. :scope scope
  78. :rawsearchdata allhits)
  79. )))
  80. ;;; METHODS
  81. ;;
  82. ;; These accessor methods will calculate the useful bits from the context, and cache values
  83. ;; into the context.
  84. (defmethod semantic-analyze-refs-impl ((refs semantic-analyze-references) &optional in-buffer)
  85. "Return the implementations derived in the reference analyzer REFS.
  86. Optional argument IN-BUFFER indicates that the returned tag should be in an active buffer."
  87. (let ((allhits (oref refs rawsearchdata))
  88. (tag (oref refs :tag))
  89. (impl nil)
  90. )
  91. (semanticdb-find-result-mapc
  92. (lambda (T DB)
  93. "Examine T in the database DB, and sont it."
  94. (let* ((ans (semanticdb-normalize-one-tag DB T))
  95. (aT (cdr ans))
  96. (aDB (car ans))
  97. )
  98. (when (and (not (semantic-tag-prototype-p aT))
  99. (semantic-tag-similar-p tag aT :prototype-flag :parent))
  100. (when in-buffer (save-excursion (semantic-go-to-tag aT aDB)))
  101. (push aT impl))))
  102. allhits)
  103. impl))
  104. (defmethod semantic-analyze-refs-proto ((refs semantic-analyze-references) &optional in-buffer)
  105. "Return the prototypes derived in the reference analyzer REFS.
  106. Optional argument IN-BUFFER indicates that the returned tag should be in an active buffer."
  107. (let ((allhits (oref refs rawsearchdata))
  108. (tag (oref refs :tag))
  109. (proto nil))
  110. (semanticdb-find-result-mapc
  111. (lambda (T DB)
  112. "Examine T in the database DB, and sort it."
  113. (let* ((ans (semanticdb-normalize-one-tag DB T))
  114. (aT (cdr ans))
  115. (aDB (car ans))
  116. )
  117. (when (and (semantic-tag-prototype-p aT)
  118. (semantic-tag-similar-p tag aT :prototype-flag :parent))
  119. (when in-buffer (save-excursion (semantic-go-to-tag aT aDB)))
  120. (push aT proto))))
  121. allhits)
  122. proto))
  123. ;;; LOOKUP
  124. ;;
  125. (defun semantic--analyze-refs-full-lookup (tag scope)
  126. "Perform a full lookup for all occurrences of TAG in the current project.
  127. TAG should be the tag currently under point.
  128. SCOPE is the scope the cursor is in. From this a list of parents is
  129. derived. If SCOPE does not have parents, then only a simple lookup is done."
  130. (if (not (oref scope parents))
  131. ;; If this tag has some named parent, but is not
  132. (semantic--analyze-refs-full-lookup-simple tag)
  133. ;; We have some sort of lineage we need to consider when we do
  134. ;; our side lookup of tags.
  135. (semantic--analyze-refs-full-lookup-with-parents tag scope)
  136. ))
  137. (defun semantic--analyze-refs-find-child-in-find-results (find-results name class)
  138. "Find in FIND-RESULT a tag NAME which is a child of a tag in FIND-RESULTS.
  139. CLASS is the class of the tag that ought to be returned."
  140. (let ((ans nil)
  141. (subans nil))
  142. ;; Loop over each segment of the find results.
  143. (dolist (FDB find-results)
  144. (setq subans nil)
  145. ;; Loop over each tag in the find results.
  146. (dolist (T (cdr FDB))
  147. ;; For each tag, get the children.
  148. (let* ((chil (semantic-tag-type-members T))
  149. (match (semantic-find-tags-by-name name chil)))
  150. ;; Go over the matches, looking for matching tag class.
  151. (dolist (M match)
  152. (when (semantic-tag-of-class-p M class)
  153. (push M subans)))))
  154. ;; Store current matches into a new find results.
  155. (when subans
  156. (push (cons (car FDB) subans) ans))
  157. )
  158. ans))
  159. (defun semantic--analyze-refs-find-tags-with-parent (find-results parents)
  160. "Find in FIND-RESULTS all tags with PARENTS.
  161. NAME is the name of the tag needing finding.
  162. PARENTS is a list of names."
  163. (let ((ans nil) (usingnames nil))
  164. ;; Loop over the find-results passed in.
  165. (semanticdb-find-result-mapc
  166. (lambda (tag db)
  167. (let* ((p (semantic-tag-named-parent tag))
  168. (ps (when (stringp p) (semantic-analyze-split-name p))))
  169. (when (stringp ps) (setq ps (list ps)))
  170. (when ps
  171. ;; If there is a perfect match, then use it.
  172. (if (equal ps parents)
  173. (push (list db tag) ans))
  174. ;; No match, find something from our list of using names.
  175. ;; Do we need to split UN?
  176. (save-excursion
  177. (semantic-go-to-tag tag db)
  178. (setq usingnames nil)
  179. (let ((imports (semantic-ctxt-imported-packages)))
  180. ;; Derive the names from all the using statements.
  181. (mapc (lambda (T)
  182. (setq usingnames
  183. (cons (semantic-format-tag-name-from-anything T) usingnames)))
  184. imports))
  185. (dolist (UN usingnames)
  186. (when (equal (cons UN ps) parents)
  187. (push (list db tag) ans)
  188. (setq usingnames (cdr usingnames))))
  189. ))))
  190. find-results)
  191. ans))
  192. (defun semantic--analyze-refs-full-lookup-with-parents (tag scope)
  193. "Perform a lookup for all occurrences of TAG based on TAG's SCOPE.
  194. TAG should be the tag currently under point."
  195. (let* ((classmatch (semantic-tag-class tag))
  196. (plist (mapcar (lambda (T) (semantic-tag-name T)) (oref scope parents)))
  197. ;; The first item in the parent list
  198. (name (car plist))
  199. ;; Stuff from the simple list.
  200. (simple (semantic--analyze-refs-full-lookup-simple tag t))
  201. ;; Find all hits for the first parent name.
  202. (brute (semanticdb-find-tags-collector
  203. (lambda (table tags)
  204. (semanticdb-deep-find-tags-by-name-method table name tags)
  205. )
  206. nil nil t))
  207. ;; Prime the answer.
  208. (answer (semantic--analyze-refs-find-tags-with-parent simple plist))
  209. )
  210. ;; First parent is already search to initialize "brute".
  211. (setq plist (cdr plist))
  212. ;; Go through the list of parents, and try to find matches.
  213. ;; As we cycle through plist, for each level look for NAME,
  214. ;; and compare the named-parent, and also dive into the next item of
  215. ;; plist.
  216. (while (and plist brute)
  217. ;; Find direct matches
  218. (let* ((direct (semantic--analyze-refs-find-child-in-find-results
  219. brute (semantic-tag-name tag) classmatch))
  220. (pdirect (semantic--analyze-refs-find-tags-with-parent
  221. direct plist)))
  222. (setq answer (append pdirect answer)))
  223. ;; The next set of search items.
  224. (setq brute (semantic--analyze-refs-find-child-in-find-results
  225. brute (car plist) 'type))
  226. (setq plist (cdr plist)))
  227. ;; Brute now has the children from the very last match.
  228. (let* ((direct (semantic--analyze-refs-find-child-in-find-results
  229. brute (semantic-tag-name tag) classmatch))
  230. )
  231. (setq answer (append direct answer)))
  232. answer))
  233. (defun semantic--analyze-refs-full-lookup-simple (tag &optional noerror)
  234. "Perform a simple lookup for occurrences of TAG in the current project.
  235. TAG should be the tag currently under point.
  236. Optional NOERROR means don't throw errors on failure to find something.
  237. This only compares the tag name, and does not infer any matches in namespaces,
  238. or parts of some other data structure.
  239. Only works for tags in the global namespace."
  240. (let* ((name (semantic-tag-name tag))
  241. (brute (semanticdb-find-tags-collector
  242. (lambda (table tags)
  243. (semanticdb-find-tags-by-name-method table name tags)
  244. )
  245. nil ;; This may need to be the entire project??
  246. nil t))
  247. )
  248. (when (and (not brute) (not noerror))
  249. ;; An error, because tag under point ought to be found.
  250. (error "Cannot find any references to %s in wide search" name))
  251. (let* ((classmatch (semantic-tag-class tag))
  252. (RES
  253. (semanticdb-find-tags-collector
  254. (lambda (table tags)
  255. (semantic-find-tags-by-class classmatch tags)
  256. ;; @todo - Add parent check also.
  257. )
  258. brute nil)))
  259. (when (and (not RES) (not noerror))
  260. (error "Cannot find any definitions for %s in wide search"
  261. (semantic-tag-name tag)))
  262. ;; Return the matching tags and databases.
  263. RES)))
  264. ;;; USER COMMANDS
  265. ;;
  266. ;;;###autoload
  267. (defun semantic-analyze-current-tag ()
  268. "Analyze the tag under point."
  269. (interactive)
  270. (let* ((tag (semantic-current-tag))
  271. (start (current-time))
  272. (sac (semantic-analyze-tag-references tag))
  273. (end (current-time))
  274. )
  275. (message "Analysis took %.2f seconds." (semantic-elapsed-time start end))
  276. (if sac
  277. (progn
  278. (require 'eieio-datadebug)
  279. (data-debug-new-buffer "*Analyzer Reference ADEBUG*")
  280. (data-debug-insert-object-slots sac "]"))
  281. (message "No Context to analyze here."))))
  282. ;;;###autoload
  283. (defun semantic-analyze-proto-impl-toggle ()
  284. "Toggle between the implementation, and a prototype of tag under point."
  285. (interactive)
  286. (require 'semantic/decorate)
  287. (semantic-fetch-tags)
  288. (let* ((tag (semantic-current-tag))
  289. (sar (if tag
  290. (semantic-analyze-tag-references tag)
  291. (error "Point must be in a declaration")))
  292. (target (if (semantic-tag-prototype-p tag)
  293. (car (semantic-analyze-refs-impl sar t))
  294. (car (semantic-analyze-refs-proto sar t))))
  295. )
  296. (when (not target)
  297. (error "Could not find suitable %s"
  298. (if (semantic-tag-prototype-p tag) "implementation" "prototype")))
  299. (push-mark)
  300. (semantic-go-to-tag target)
  301. (switch-to-buffer (current-buffer))
  302. (semantic-momentary-highlight-tag target))
  303. )
  304. (provide 'semantic/analyze/refs)
  305. ;; Local variables:
  306. ;; generated-autoload-file: "../loaddefs.el"
  307. ;; generated-autoload-load-name: "semantic/analyze/refs"
  308. ;; End:
  309. ;;; semantic/analyze/refs.el ends here