Commit | Line | Data |
---|---|---|
aa8724ae | 1 | ;;; semantic/analyze.el --- Analyze semantic tags against local context |
9573e58b | 2 | |
95df8112 | 3 | ;; Copyright (C) 2000-2005, 2007-2011 Free Software Foundation, Inc. |
9573e58b CY |
4 | |
5 | ;; Author: Eric M. Ludlam <zappo@gnu.org> | |
6 | ||
7 | ;; This file is part of GNU Emacs. | |
8 | ||
9 | ;; GNU Emacs is free software: you can redistribute it and/or modify | |
10 | ;; it under the terms of the GNU General Public License as published by | |
11 | ;; the Free Software Foundation, either version 3 of the License, or | |
12 | ;; (at your option) any later version. | |
13 | ||
14 | ;; GNU Emacs is distributed in the hope that it will be useful, | |
15 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
17 | ;; GNU General Public License for more details. | |
18 | ||
19 | ;; You should have received a copy of the GNU General Public License | |
20 | ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. | |
21 | ||
22 | ;;; Commentary: | |
23 | ;; | |
24 | ;; Semantic, as a tool, provides a nice list of searchable tags. | |
25 | ;; That information can provide some very accurate answers if the current | |
26 | ;; context of a position is known. | |
27 | ;; | |
28 | ;; Semantic-ctxt provides ways of analyzing, and manipulating the | |
29 | ;; semantic context of a language in code. | |
30 | ;; | |
31 | ;; This library provides routines for finding intelligent answers to | |
32 | ;; tough problems, such as if an argument to a function has the correct | |
33 | ;; return type, or all possible tags that fit in a given local context. | |
34 | ;; | |
35 | ||
36 | ;;; Vocabulary: | |
37 | ;; | |
38 | ;; Here are some words used to describe different things in the analyzer: | |
39 | ;; | |
40 | ;; tag - A single entity | |
41 | ;; prefix - The beginning of a symbol, usually used to look up something | |
42 | ;; incomplete. | |
91abaf51 | 43 | ;; type - The name of a datatype in the language. |
9573e58b CY |
44 | ;; metatype - If a type is named in a declaration like: |
45 | ;; struct moose somevariable; | |
46 | ;; that name "moose" can be turned into a concrete type. | |
47 | ;; tag sequence - In C code, a list of dereferences, such as: | |
48 | ;; this.that.theother(); | |
49 | ;; parent - For a datatype in an OO language, another datatype | |
50 | ;; inherited from. This excludes interfaces. | |
51 | ;; scope - A list of tags that can be dereferenced that cannot | |
52 | ;; be found from the global namespace. | |
53 | ;; scopetypes - A list of tags which are datatype that contain | |
54 | ;; the scope. The scopetypes need to have the scope extracted | |
55 | ;; in a way that honors the type of inheritance. | |
56 | ;; nest/nested - When one tag is contained entirely in another. | |
57 | ;; | |
58 | ;; context - A semantic datatype representing a point in a buffer. | |
59 | ;; | |
60 | ;; constriant - If a context specifies a specific datatype is needed, | |
61 | ;; that is a constraint. | |
62 | ;; constants - Some datatypes define elements of themselves as a | |
63 | ;; constant. These need to be returned as there would be no | |
64 | ;; other possible completions. | |
b90caf50 | 65 | |
67d3ffe4 | 66 | (eval-when-compile (require 'cl)) |
9573e58b CY |
67 | (require 'semantic) |
68 | (require 'semantic/format) | |
69 | (require 'semantic/ctxt) | |
9573e58b | 70 | (require 'semantic/scope) |
b90caf50 | 71 | (require 'semantic/sort) |
9573e58b CY |
72 | (require 'semantic/analyze/fcn) |
73 | ||
b90caf50 CY |
74 | (eval-when-compile (require 'semantic/find)) |
75 | ||
00676d68 CY |
76 | (declare-function data-debug-new-buffer "data-debug") |
77 | (declare-function data-debug-insert-object-slots "eieio-datadebug") | |
78 | ||
9573e58b CY |
79 | ;;; Code: |
80 | (defvar semantic-analyze-error-stack nil | |
81 | "Collection of any errors thrown during analysis.") | |
82 | ||
83 | (defun semantic-analyze-push-error (err) | |
84 | "Push the error in ERR-DATA onto the error stack. | |
91abaf51 | 85 | Argument ERR." |
9573e58b CY |
86 | (push err semantic-analyze-error-stack)) |
87 | ||
88 | ;;; Analysis Classes | |
89 | ;; | |
90 | ;; These classes represent what a context is. Different types | |
91 | ;; of contexts provide differing amounts of information to help | |
92 | ;; provide completions. | |
93 | ;; | |
94 | (defclass semantic-analyze-context () | |
95 | ((bounds :initarg :bounds | |
96 | :type list | |
97 | :documentation "The bounds of this context. | |
98 | Usually bound to the dimension of a single symbol or command.") | |
99 | (prefix :initarg :prefix | |
100 | :type list | |
101 | :documentation "List of tags defining local text. | |
102 | This can be nil, or a list where the last element can be a string | |
97610156 | 103 | representing text that may be incomplete. Preceding elements |
9573e58b CY |
104 | must be semantic tags representing variables or functions |
105 | called in a dereference sequence.") | |
106 | (prefixclass :initarg :prefixclass | |
107 | :type list | |
108 | :documentation "Tag classes expected at this context. | |
da6062e6 | 109 | These are classes for tags, such as 'function, or 'variable.") |
9573e58b CY |
110 | (prefixtypes :initarg :prefixtypes |
111 | :type list | |
112 | :documentation "List of tags defining types for :prefix. | |
113 | This list is one shorter than :prefix. Each element is a semantic | |
114 | tag representing a type matching the semantic tag in the same | |
115 | position in PREFIX.") | |
116 | (scope :initarg :scope | |
117 | :type (or null semantic-scope-cache) | |
118 | :documentation "List of tags available in scopetype. | |
119 | See `semantic-analyze-scoped-tags' for details.") | |
120 | (buffer :initarg :buffer | |
121 | :type buffer | |
122 | :documentation "The buffer this context is derived from.") | |
123 | (errors :initarg :errors | |
124 | :documentation "Any errors thrown an caught during analysis.") | |
125 | ) | |
91abaf51 | 126 | "Base analysis data for any context.") |
9573e58b CY |
127 | |
128 | (defclass semantic-analyze-context-assignment (semantic-analyze-context) | |
129 | ((assignee :initarg :assignee | |
130 | :type list | |
131 | :documentation "A sequence of tags for an assignee. | |
132 | This is a variable into which some value is being placed. The last | |
133 | item in the list is the variable accepting the value. Earlier | |
91abaf51 | 134 | tags represent the variables being dereferenced to get to the |
9573e58b CY |
135 | assignee.")) |
136 | "Analysis class for a value in an assignment.") | |
137 | ||
138 | (defclass semantic-analyze-context-functionarg (semantic-analyze-context) | |
139 | ((function :initarg :function | |
140 | :type list | |
141 | :documentation "A sequence of tags for a function. | |
142 | This is a function being called. The cursor will be in the position | |
143 | of an argument. | |
144 | The last tag in :function is the function being called. Earlier | |
145 | tags represent the variables being dereferenced to get to the | |
146 | function.") | |
147 | (index :initarg :index | |
148 | :type integer | |
149 | :documentation "The index of the argument for this context. | |
150 | If a function takes 4 arguments, this value should be bound to | |
151 | the values 1 through 4.") | |
152 | (argument :initarg :argument | |
153 | :type list | |
154 | :documentation "A sequence of tags for the :index argument. | |
155 | The argument can accept a value of some type, and this contains the | |
156 | tag for that definition. It should be a tag, but might | |
157 | be just a string in some circumstances.") | |
158 | ) | |
159 | "Analysis class for a value as a function argument.") | |
160 | ||
161 | (defclass semantic-analyze-context-return (semantic-analyze-context) | |
162 | () ; No extra data. | |
163 | "Analysis class for return data. | |
40ba43b4 | 164 | Return data methods identify the required type by the return value |
9573e58b CY |
165 | of the parent function.") |
166 | ||
167 | ;;; METHODS | |
168 | ;; | |
169 | ;; Simple methods against the context classes. | |
170 | ;; | |
171 | (defmethod semantic-analyze-type-constraint | |
172 | ((context semantic-analyze-context) &optional desired-type) | |
173 | "Return a type constraint for completing :prefix in CONTEXT. | |
174 | Optional argument DESIRED-TYPE may be a non-type tag to analyze." | |
175 | (when (semantic-tag-p desired-type) | |
176 | ;; Convert the desired type if needed. | |
177 | (if (not (eq (semantic-tag-class desired-type) 'type)) | |
178 | (setq desired-type (semantic-tag-type desired-type))) | |
179 | ;; Protect against plain strings | |
180 | (cond ((stringp desired-type) | |
181 | (setq desired-type (list desired-type 'type))) | |
182 | ((and (stringp (car desired-type)) | |
183 | (not (semantic-tag-p desired-type))) | |
184 | (setq desired-type (list (car desired-type) 'type))) | |
185 | ((semantic-tag-p desired-type) | |
186 | ;; We have a tag of some sort. Yay! | |
187 | nil) | |
188 | (t (setq desired-type nil)) | |
189 | ) | |
190 | desired-type)) | |
191 | ||
192 | (defmethod semantic-analyze-type-constraint | |
193 | ((context semantic-analyze-context-functionarg)) | |
194 | "Return a type constraint for completing :prefix in CONTEXT." | |
195 | (call-next-method context (car (oref context argument)))) | |
196 | ||
197 | (defmethod semantic-analyze-type-constraint | |
198 | ((context semantic-analyze-context-assignment)) | |
199 | "Return a type constraint for completing :prefix in CONTEXT." | |
200 | (call-next-method context (car (reverse (oref context assignee))))) | |
201 | ||
202 | (defmethod semantic-analyze-interesting-tag | |
203 | ((context semantic-analyze-context)) | |
204 | "Return a tag from CONTEXT that would be most interesting to a user." | |
205 | (let ((prefix (reverse (oref context :prefix)))) | |
206 | ;; Go back through the prefix until we find a tag we can return. | |
207 | (while (and prefix (not (semantic-tag-p (car prefix)))) | |
208 | (setq prefix (cdr prefix))) | |
209 | ;; Return the found tag, or nil. | |
210 | (car prefix))) | |
211 | ||
212 | (defmethod semantic-analyze-interesting-tag | |
213 | ((context semantic-analyze-context-functionarg)) | |
214 | "Try the base, and if that fails, return what we are assigning into." | |
215 | (or (call-next-method) (car-safe (oref context :function)))) | |
216 | ||
217 | (defmethod semantic-analyze-interesting-tag | |
218 | ((context semantic-analyze-context-assignment)) | |
219 | "Try the base, and if that fails, return what we are assigning into." | |
220 | (or (call-next-method) (car-safe (oref context :assignee)))) | |
221 | ||
222 | ;;; ANALYSIS | |
223 | ;; | |
224 | ;; Start out with routines that will calculate useful parts of | |
225 | ;; the general analyzer function. These could be used directly | |
226 | ;; by an application that doesn't need to calculate the full | |
227 | ;; context. | |
228 | ||
229 | (define-overloadable-function semantic-analyze-find-tag-sequence (sequence &optional | |
230 | scope typereturn throwsym) | |
231 | "Attempt to find all tags in SEQUENCE. | |
232 | Optional argument LOCALVAR is the list of local variables to use when | |
233 | finding the details on the first element of SEQUENCE in case | |
234 | it is not found in the global set of tables. | |
235 | Optional argument SCOPE are additional terminals to search which are currently | |
236 | scoped. These are not local variables, but symbols available in a structure | |
91abaf51 | 237 | which doesn't need to be dereferenced. |
9573e58b CY |
238 | Optional argument TYPERETURN is a symbol in which the types of all found |
239 | will be stored. If nil, that data is thrown away. | |
240 | Optional argument THROWSYM specifies a symbol the throw on non-recoverable error.") | |
241 | ||
242 | (defun semantic-analyze-find-tag-sequence-default (sequence &optional | |
243 | scope typereturn | |
244 | throwsym) | |
245 | "Attempt to find all tags in SEQUENCE. | |
246 | SCOPE are extra tags which are in scope. | |
247 | TYPERETURN is a symbol in which to place a list of tag classes that | |
248 | are found in SEQUENCE. | |
249 | Optional argument THROWSYM specifies a symbol the throw on non-recoverable error." | |
250 | (let ((s sequence) ; copy of the sequence | |
251 | (tmp nil) ; tmp find variable | |
252 | (tag nil) ; tag return list | |
253 | (tagtype nil) ; tag types return list | |
254 | (fname nil) | |
dd9af436 | 255 | (miniscope (when scope (clone scope))) |
9573e58b CY |
256 | ) |
257 | ;; First order check. Is this wholely contained in the typecache? | |
258 | (setq tmp (semanticdb-typecache-find sequence)) | |
259 | ||
260 | (if tmp | |
261 | (progn | |
262 | ;; We are effectively done... | |
263 | (setq s nil) | |
264 | (setq tag (list tmp))) | |
265 | ||
266 | ;; For the first entry, it better be a variable, but it might | |
267 | ;; be in the local context too. | |
268 | ;; NOTE: Don't forget c++ namespace foo::bar. | |
269 | (setq tmp (or | |
270 | ;; Is this tag within our scope. Scopes can sometimes | |
271 | ;; shadow other things, so it goes first. | |
272 | (and scope (semantic-scope-find (car s) nil scope)) | |
273 | ;; Find the tag out there... somewhere, but not in scope | |
274 | (semantic-analyze-find-tag (car s)) | |
275 | )) | |
276 | ||
277 | (if (and (listp tmp) (semantic-tag-p (car tmp))) | |
278 | (setq tmp (semantic-analyze-select-best-tag tmp))) | |
279 | (if (not (semantic-tag-p tmp)) | |
280 | (if throwsym | |
281 | (throw throwsym "Cannot find definition") | |
282 | (error "Cannot find definition for \"%s\"" (car s)))) | |
283 | (setq s (cdr s)) | |
284 | (setq tag (cons tmp tag)) ; tag is nil here... | |
285 | (setq fname (semantic-tag-file-name tmp)) | |
286 | ) | |
287 | ||
288 | ;; For the middle entries | |
289 | (while s | |
c7015153 | 290 | ;; Using the tag found in TMP, let's find the tag |
9573e58b CY |
291 | ;; representing the full typeographic information of its |
292 | ;; type, and use that to determine the search context for | |
293 | ;; (car s) | |
294 | (let* ((tmptype | |
295 | ;; In some cases the found TMP is a type, | |
296 | ;; and we can use it directly. | |
297 | (cond ((semantic-tag-of-class-p tmp 'type) | |
298 | ;; update the miniscope when we need to analyze types directly. | |
dd9af436 CY |
299 | (when miniscope |
300 | (let ((rawscope | |
301 | (apply 'append | |
302 | (mapcar 'semantic-tag-type-members | |
303 | tagtype)))) | |
304 | (oset miniscope fullscope rawscope))) | |
91af3942 | 305 | ;; Now analyze the type to remove metatypes. |
9573e58b CY |
306 | (or (semantic-analyze-type tmp miniscope) |
307 | tmp)) | |
308 | (t | |
309 | (semantic-analyze-tag-type tmp scope)))) | |
310 | (typefile | |
311 | (when tmptype | |
312 | (semantic-tag-file-name tmptype))) | |
313 | (slots nil)) | |
314 | ||
315 | ;; Get the children | |
316 | (setq slots (semantic-analyze-scoped-type-parts tmptype scope)) | |
317 | ||
318 | ;; find (car s) in the list o slots | |
319 | (setq tmp (semantic-find-tags-by-name (car s) slots)) | |
320 | ||
321 | ;; If we have lots | |
322 | (if (and (listp tmp) (semantic-tag-p (car tmp))) | |
323 | (setq tmp (semantic-analyze-select-best-tag tmp))) | |
324 | ||
325 | ;; Make sure we have a tag. | |
326 | (if (not (semantic-tag-p tmp)) | |
327 | (if (cdr s) | |
328 | ;; In the middle, we need to keep seeking our types out. | |
329 | (error "Cannot find definition for \"%s\"" (car s)) | |
330 | ;; Else, it's ok to end with a non-tag | |
331 | (setq tmp (car s)))) | |
332 | ||
333 | (setq fname (or typefile fname)) | |
334 | (when (and fname (semantic-tag-p tmp) | |
335 | (not (semantic-tag-in-buffer-p tmp))) | |
336 | (semantic--tag-put-property tmp :filename fname)) | |
337 | (setq tag (cons tmp tag)) | |
338 | (setq tagtype (cons tmptype tagtype)) | |
339 | ) | |
340 | (setq s (cdr s))) | |
341 | ||
342 | (if typereturn (set typereturn (nreverse tagtype))) | |
343 | ;; Return the mess | |
344 | (nreverse tag))) | |
345 | ||
346 | (defun semantic-analyze-find-tag (name &optional tagclass scope) | |
347 | "Return the first tag found with NAME or nil if not found. | |
91abaf51 JB |
348 | Optional argument TAGCLASS specifies the class of tag to return, |
349 | such as 'function or 'variable. | |
9573e58b CY |
350 | Optional argument SCOPE specifies a scope object which has |
351 | additional tags which are in SCOPE and do not need prefixing to | |
352 | find. | |
353 | ||
dd9af436 | 354 | This is a wrapper on top of semanticdb, semanticdb typecache, |
9573e58b CY |
355 | semantic-scope, and semantic search functions. Almost all |
356 | searches use the same arguments." | |
357 | (let ((namelst (if (consp name) name ;; test if pre-split. | |
358 | (semantic-analyze-split-name name)))) | |
359 | (cond | |
360 | ;; If the splitter gives us a list, use the sequence finder | |
361 | ;; to get the list. Since this routine is expected to return | |
362 | ;; only one tag, return the LAST tag found from the sequence | |
363 | ;; which is supposedly the nested reference. | |
364 | ;; | |
365 | ;; Of note, the SEQUENCE function below calls this function | |
366 | ;; (recursively now) so the names that we get from the above | |
367 | ;; fcn better not, in turn, be splittable. | |
368 | ((listp namelst) | |
369 | ;; If we had a split, then this is likely a c++ style namespace::name sequence, | |
370 | ;; so take a short-cut through the typecache. | |
371 | (or (semanticdb-typecache-find namelst) | |
372 | ;; Ok, not there, try the usual... | |
373 | (let ((seq (semantic-analyze-find-tag-sequence | |
374 | namelst scope nil))) | |
375 | (semantic-analyze-select-best-tag seq tagclass) | |
376 | ))) | |
377 | ;; If NAME is solo, then do our searches for it here. | |
378 | ((stringp namelst) | |
379 | (let ((retlist (and scope (semantic-scope-find name tagclass scope)))) | |
380 | (if retlist | |
381 | (semantic-analyze-select-best-tag | |
382 | retlist tagclass) | |
383 | (if (eq tagclass 'type) | |
384 | (semanticdb-typecache-find name) | |
385 | ;; Search in the typecache. First entries in a sequence are | |
386 | ;; often there. | |
387 | (setq retlist (semanticdb-typecache-find name)) | |
388 | (if retlist | |
389 | retlist | |
390 | (semantic-analyze-select-best-tag | |
391 | (semanticdb-strip-find-results | |
392 | (semanticdb-find-tags-by-name name) | |
393 | 'name) | |
394 | tagclass) | |
395 | ))))) | |
396 | ))) | |
397 | ||
398 | ;;; SHORT ANALYSIS | |
399 | ;; | |
400 | ;; Create a mini-analysis of just the symbol under point. | |
401 | ;; | |
402 | (define-overloadable-function semantic-analyze-current-symbol | |
403 | (analyzehookfcn &optional position) | |
404 | "Call ANALYZEHOOKFCN after analyzing the symbol under POSITION. | |
405 | The ANALYZEHOOKFCN is called with the current symbol bounds, and the | |
406 | analyzed prefix. It should take the arguments (START END PREFIX). | |
407 | The ANALYZEHOOKFCN is only called if some sort of prefix with bounds was | |
408 | found under POSITION. | |
409 | ||
410 | The results of ANALYZEHOOKFCN is returned, or nil if there was nothing to | |
411 | call it with. | |
412 | ||
413 | For regular analysis, you should call `semantic-analyze-current-context' | |
414 | to calculate the context information. The purpose for this function is | |
415 | to provide a large number of non-cached analysis for filtering symbols." | |
416 | ;; Only do this in a Semantic enabled buffer. | |
417 | (when (not (semantic-active-p)) | |
9bf6c65c | 418 | (error "Cannot analyze buffers not supported by Semantic")) |
9573e58b CY |
419 | ;; Always refresh out tags in a safe way before doing the |
420 | ;; context. | |
421 | (semantic-refresh-tags-safe) | |
422 | ;; Do the rest of the analysis. | |
423 | (save-match-data | |
424 | (save-excursion | |
425 | (:override))) | |
426 | ) | |
427 | ||
428 | (defun semantic-analyze-current-symbol-default (analyzehookfcn position) | |
429 | "Call ANALYZEHOOKFCN on the analyzed symbol at POSITION." | |
430 | (let* ((semantic-analyze-error-stack nil) | |
431 | (LLstart (current-time)) | |
432 | (prefixandbounds (semantic-ctxt-current-symbol-and-bounds (or position (point)))) | |
433 | (prefix (car prefixandbounds)) | |
434 | (bounds (nth 2 prefixandbounds)) | |
435 | (scope (semantic-calculate-scope position)) | |
436 | (end nil) | |
437 | ) | |
438 | ;; Only do work if we have bounds (meaning a prefix to complete) | |
439 | (when bounds | |
440 | ||
441 | (if debug-on-error | |
442 | (catch 'unfindable | |
443 | ;; If debug on error is on, allow debugging in this fcn. | |
444 | (setq prefix (semantic-analyze-find-tag-sequence | |
445 | prefix scope 'prefixtypes 'unfindable))) | |
446 | ;; Debug on error is off. Capture errors and move on | |
447 | (condition-case err | |
448 | ;; NOTE: This line is duplicated in | |
449 | ;; semantic-analyzer-debug-global-symbol | |
450 | ;; You will need to update both places. | |
451 | (setq prefix (semantic-analyze-find-tag-sequence | |
452 | prefix scope 'prefixtypes)) | |
453 | (error (semantic-analyze-push-error err)))) | |
454 | ||
455 | (setq end (current-time)) | |
456 | ;;(message "Analysis took %.2f sec" (semantic-elapsed-time LLstart end)) | |
457 | ||
458 | ) | |
459 | (when prefix | |
460 | (prog1 | |
461 | (funcall analyzehookfcn (car bounds) (cdr bounds) prefix) | |
462 | ;;(setq end (current-time)) | |
463 | ;;(message "hookfcn took %.5f sec" (semantic-elapsed-time LLstart end)) | |
464 | ) | |
465 | ||
466 | ))) | |
467 | ||
468 | ;;; MAIN ANALYSIS | |
469 | ;; | |
470 | ;; Create a full-up context analysis. | |
471 | ;; | |
55b522b2 | 472 | ;;;###autoload |
9573e58b CY |
473 | (define-overloadable-function semantic-analyze-current-context (&optional position) |
474 | "Analyze the current context at optional POSITION. | |
475 | If called interactively, display interesting information about POSITION | |
476 | in a separate buffer. | |
477 | Returns an object based on symbol `semantic-analyze-context'. | |
478 | ||
0b381c7e | 479 | This function can be overridden with the symbol `analyze-context'. |
9573e58b CY |
480 | When overriding this function, your override will be called while |
481 | cursor is at POSITION. In addition, your function will not be called | |
482 | if a cached copy of the return object is found." | |
483 | (interactive "d") | |
484 | ;; Only do this in a Semantic enabled buffer. | |
485 | (when (not (semantic-active-p)) | |
9bf6c65c | 486 | (error "Cannot analyze buffers not supported by Semantic")) |
9573e58b CY |
487 | ;; Always refresh out tags in a safe way before doing the |
488 | ;; context. | |
489 | (semantic-refresh-tags-safe) | |
490 | ;; Do the rest of the analysis. | |
491 | (if (not position) (setq position (point))) | |
492 | (save-excursion | |
493 | (goto-char position) | |
494 | (let* ((answer (semantic-get-cache-data 'current-context))) | |
495 | (with-syntax-table semantic-lex-syntax-table | |
496 | (when (not answer) | |
497 | (setq answer (:override)) | |
498 | (when (and answer (oref answer bounds)) | |
499 | (with-slots (bounds) answer | |
500 | (semantic-cache-data-to-buffer (current-buffer) | |
501 | (car bounds) | |
502 | (cdr bounds) | |
503 | answer | |
504 | 'current-context | |
505 | 'exit-cache-zone))) | |
506 | ;; Check for interactivity | |
2054a44c | 507 | (when (called-interactively-p 'any) |
9573e58b CY |
508 | (if answer |
509 | (semantic-analyze-pop-to-context answer) | |
510 | (message "No Context.")) | |
511 | )) | |
512 | ||
513 | answer)))) | |
514 | ||
515 | (defun semantic-analyze-current-context-default (position) | |
516 | "Analyze the current context at POSITION. | |
517 | Returns an object based on symbol `semantic-analyze-context'." | |
518 | (let* ((semantic-analyze-error-stack nil) | |
519 | (context-return nil) | |
520 | (prefixandbounds (semantic-ctxt-current-symbol-and-bounds (or position (point)))) | |
521 | (prefix (car prefixandbounds)) | |
522 | (bounds (nth 2 prefixandbounds)) | |
523 | ;; @todo - vv too early to really know this answer! vv | |
524 | (prefixclass (semantic-ctxt-current-class-list)) | |
525 | (prefixtypes nil) | |
526 | (scope (semantic-calculate-scope position)) | |
527 | (function nil) | |
528 | (fntag nil) | |
529 | arg fntagend argtag | |
530 | assign asstag | |
531 | ) | |
532 | ||
533 | ;; Pattern for Analysis: | |
534 | ;; | |
535 | ;; Step 1: Calculate DataTypes in Scope: | |
536 | ;; | |
537 | ;; a) Calculate the scope (above) | |
538 | ;; | |
539 | ;; Step 2: Parse context | |
540 | ;; | |
541 | ;; a) Identify function being called, or variable assignment, | |
542 | ;; and find source tags for those references | |
543 | ;; b) Identify the prefix (text cursor is on) and find the source | |
544 | ;; tags for those references. | |
545 | ;; | |
546 | ;; Step 3: Assemble an object | |
547 | ;; | |
548 | ||
549 | ;; Step 2 a: | |
550 | ||
551 | (setq function (semantic-ctxt-current-function)) | |
552 | ||
553 | (when function | |
554 | ;; Calculate the argument for the function if there is one. | |
555 | (setq arg (semantic-ctxt-current-argument)) | |
556 | ||
557 | ;; Find a tag related to the function name. | |
558 | (condition-case err | |
559 | (setq fntag | |
560 | (semantic-analyze-find-tag-sequence function scope)) | |
561 | (error (semantic-analyze-push-error err))) | |
562 | ||
563 | ;; fntag can have the last entry as just a string, meaning we | |
564 | ;; could not find the core datatype. In this case, the searches | |
565 | ;; below will not work. | |
566 | (when (stringp (car (last fntag))) | |
567 | ;; Take a wild guess! | |
568 | (setcar (last fntag) (semantic-tag (car (last fntag)) 'function)) | |
569 | ) | |
570 | ||
571 | (when fntag | |
572 | (let ((fcn (semantic-find-tags-by-class 'function fntag))) | |
573 | (when (not fcn) | |
574 | (let ((ty (semantic-find-tags-by-class 'type fntag))) | |
575 | (when ty | |
576 | ;; We might have a constructor with the same name as | |
577 | ;; the found datatype. | |
578 | (setq fcn (semantic-find-tags-by-name | |
579 | (semantic-tag-name (car ty)) | |
580 | (semantic-tag-type-members (car ty)))) | |
581 | (if fcn | |
582 | (let ((lp fcn)) | |
583 | (while lp | |
584 | (when (semantic-tag-get-attribute (car lp) | |
585 | :constructor) | |
586 | (setq fcn (cons (car lp) fcn))) | |
587 | (setq lp (cdr lp)))) | |
588 | ;; Give up, go old school | |
589 | (setq fcn fntag)) | |
590 | ))) | |
591 | (setq fntagend (car (reverse fcn)) | |
592 | argtag | |
593 | (when (semantic-tag-p fntagend) | |
594 | (nth (1- arg) (semantic-tag-function-arguments fntagend))) | |
595 | fntag fcn)))) | |
596 | ||
597 | ;; Step 2 b: | |
598 | ||
599 | ;; Only do work if we have bounds (meaning a prefix to complete) | |
600 | (when bounds | |
601 | ||
602 | (if debug-on-error | |
603 | (catch 'unfindable | |
604 | ;; If debug on error is on, allow debugging in this fcn. | |
605 | (setq prefix (semantic-analyze-find-tag-sequence | |
606 | prefix scope 'prefixtypes 'unfindable))) | |
607 | ;; Debug on error is off. Capture errors and move on | |
608 | (condition-case err | |
609 | ;; NOTE: This line is duplicated in | |
610 | ;; semantic-analyzer-debug-global-symbol | |
611 | ;; You will need to update both places. | |
612 | (setq prefix (semantic-analyze-find-tag-sequence | |
613 | prefix scope 'prefixtypes)) | |
614 | (error (semantic-analyze-push-error err)))) | |
615 | ) | |
616 | ||
617 | ;; Step 3: | |
618 | ||
619 | (cond | |
620 | (fntag | |
621 | ;; If we found a tag for our function, we can go into | |
622 | ;; functional context analysis mode, meaning we have a type | |
623 | ;; for the argument. | |
624 | (setq context-return | |
625 | (semantic-analyze-context-functionarg | |
626 | "functionargument" | |
627 | :buffer (current-buffer) | |
628 | :function fntag | |
629 | :index arg | |
630 | :argument (list argtag) | |
631 | :scope scope | |
632 | :prefix prefix | |
633 | :prefixclass prefixclass | |
634 | :bounds bounds | |
635 | :prefixtypes prefixtypes | |
636 | :errors semantic-analyze-error-stack))) | |
637 | ||
638 | ;; No function, try assignment | |
639 | ((and (setq assign (semantic-ctxt-current-assignment)) | |
640 | ;; We have some sort of an assignment | |
641 | (condition-case err | |
642 | (setq asstag (semantic-analyze-find-tag-sequence | |
643 | assign scope)) | |
644 | (error (semantic-analyze-push-error err) | |
645 | nil))) | |
646 | ||
647 | (setq context-return | |
648 | (semantic-analyze-context-assignment | |
649 | "assignment" | |
650 | :buffer (current-buffer) | |
651 | :assignee asstag | |
652 | :scope scope | |
653 | :bounds bounds | |
654 | :prefix prefix | |
655 | :prefixclass prefixclass | |
656 | :prefixtypes prefixtypes | |
657 | :errors semantic-analyze-error-stack))) | |
658 | ||
659 | ;; TODO: Identify return value condition. | |
660 | ;;((setq return .... what to do?) | |
661 | ;; ...) | |
662 | ||
663 | (bounds | |
664 | ;; Nothing in particular | |
665 | (setq context-return | |
666 | (semantic-analyze-context | |
667 | "context" | |
668 | :buffer (current-buffer) | |
669 | :scope scope | |
670 | :bounds bounds | |
671 | :prefix prefix | |
672 | :prefixclass prefixclass | |
673 | :prefixtypes prefixtypes | |
674 | :errors semantic-analyze-error-stack))) | |
675 | ||
676 | (t (setq context-return nil)) | |
677 | ) | |
678 | ||
679 | ;; Return our context. | |
680 | context-return)) | |
681 | ||
682 | \f | |
a964f5e5 CY |
683 | (defun semantic-adebug-analyze (&optional ctxt) |
684 | "Perform `semantic-analyze-current-context'. | |
685 | Display the results as a debug list. | |
686 | Optional argument CTXT is the context to show." | |
687 | (interactive) | |
00676d68 | 688 | (require 'data-debug) |
a964f5e5 CY |
689 | (let ((start (current-time)) |
690 | (ctxt (or ctxt (semantic-analyze-current-context))) | |
691 | (end (current-time))) | |
692 | (if (not ctxt) | |
693 | (message "No Analyzer Results") | |
694 | (message "Analysis took %.2f seconds." | |
695 | (semantic-elapsed-time start end)) | |
696 | (semantic-analyze-pulse ctxt) | |
697 | (if ctxt | |
698 | (progn | |
699 | (data-debug-new-buffer "*Analyzer ADEBUG*") | |
700 | (data-debug-insert-object-slots ctxt "]")) | |
701 | (message "No Context to analyze here."))))) | |
702 | ||
703 | \f | |
9573e58b CY |
704 | ;;; DEBUG OUTPUT |
705 | ;; | |
706 | ;; Friendly output of a context analysis. | |
707 | ;; | |
aa8724ae CY |
708 | (declare-function pulse-momentary-highlight-region "pulse") |
709 | ||
9573e58b CY |
710 | (defmethod semantic-analyze-pulse ((context semantic-analyze-context)) |
711 | "Pulse the region that CONTEXT affects." | |
aa8724ae | 712 | (require 'pulse) |
0816d744 | 713 | (with-current-buffer (oref context :buffer) |
9573e58b CY |
714 | (let ((bounds (oref context :bounds))) |
715 | (when bounds | |
716 | (pulse-momentary-highlight-region (car bounds) (cdr bounds)))))) | |
717 | ||
718 | (defcustom semantic-analyze-summary-function 'semantic-format-tag-prototype | |
b90caf50 | 719 | "Function to use when creating items in Imenu. |
9573e58b CY |
720 | Some useful functions are found in `semantic-format-tag-functions'." |
721 | :group 'semantic | |
722 | :type semantic-format-tag-custom-list) | |
723 | ||
724 | (defun semantic-analyze-princ-sequence (sequence &optional prefix buff) | |
725 | "Send the tag SEQUENCE to standard out. | |
726 | Use PREFIX as a label. | |
727 | Use BUFF as a source of override methods." | |
728 | (while sequence | |
729 | (princ prefix) | |
730 | (cond | |
731 | ((semantic-tag-p (car sequence)) | |
732 | (princ (funcall semantic-analyze-summary-function | |
733 | (car sequence)))) | |
734 | ((stringp (car sequence)) | |
735 | (princ "\"") | |
736 | (princ (semantic--format-colorize-text (car sequence) 'variable)) | |
737 | (princ "\"")) | |
738 | (t | |
739 | (princ (format "'%S" (car sequence))))) | |
740 | (princ "\n") | |
741 | (setq sequence (cdr sequence)) | |
742 | (setq prefix (make-string (length prefix) ? )) | |
743 | )) | |
744 | ||
745 | (defmethod semantic-analyze-show ((context semantic-analyze-context)) | |
746 | "Insert CONTEXT into the current buffer in a nice way." | |
747 | (semantic-analyze-princ-sequence (oref context prefix) "Prefix: " ) | |
748 | (semantic-analyze-princ-sequence (oref context prefixclass) "Prefix Classes: ") | |
749 | (semantic-analyze-princ-sequence (oref context prefixtypes) "Prefix Types: ") | |
750 | (semantic-analyze-princ-sequence (oref context errors) "Encountered Errors: ") | |
751 | (princ "--------\n") | |
752 | ;(semantic-analyze-princ-sequence (oref context scopetypes) "Scope Types: ") | |
753 | ;(semantic-analyze-princ-sequence (oref context scope) "Scope: ") | |
754 | ;(semantic-analyze-princ-sequence (oref context localvariables) "LocalVars: ") | |
755 | (when (oref context scope) | |
756 | (semantic-analyze-show (oref context scope))) | |
757 | ) | |
758 | ||
759 | (defmethod semantic-analyze-show ((context semantic-analyze-context-assignment)) | |
760 | "Insert CONTEXT into the current buffer in a nice way." | |
761 | (semantic-analyze-princ-sequence (oref context assignee) "Assignee: ") | |
762 | (call-next-method)) | |
763 | ||
764 | (defmethod semantic-analyze-show ((context semantic-analyze-context-functionarg)) | |
765 | "Insert CONTEXT into the current buffer in a nice way." | |
766 | (semantic-analyze-princ-sequence (oref context function) "Function: ") | |
767 | (princ "Argument Index: ") | |
768 | (princ (oref context index)) | |
769 | (princ "\n") | |
770 | (semantic-analyze-princ-sequence (oref context argument) "Argument: ") | |
771 | (call-next-method)) | |
772 | ||
773 | (defun semantic-analyze-pop-to-context (context) | |
774 | "Display CONTEXT in a temporary buffer. | |
775 | CONTEXT's content is described in `semantic-analyze-current-context'." | |
776 | (semantic-analyze-pulse context) | |
777 | (with-output-to-temp-buffer "*Semantic Context Analysis*" | |
778 | (princ "Context Type: ") | |
779 | (princ (object-name context)) | |
780 | (princ "\n") | |
781 | (princ "Bounds: ") | |
782 | (princ (oref context bounds)) | |
783 | (princ "\n") | |
784 | (semantic-analyze-show context) | |
785 | ) | |
786 | (shrink-window-if-larger-than-buffer | |
787 | (get-buffer-window "*Semantic Context Analysis*")) | |
788 | ) | |
789 | ||
790 | (provide 'semantic/analyze) | |
791 | ||
55b522b2 CY |
792 | ;; Local variables: |
793 | ;; generated-autoload-file: "loaddefs.el" | |
996bc9bf | 794 | ;; generated-autoload-load-name: "semantic/analyze" |
55b522b2 CY |
795 | ;; End: |
796 | ||
aa8724ae | 797 | ;;; semantic/analyze.el ends here |