2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
21 package com
.intellij
.codeInsight
;
23 import com
.intellij
.codeInsight
.daemon
.DaemonCodeAnalyzer
;
24 import com
.intellij
.codeInsight
.lookup
.Lookup
;
25 import com
.intellij
.codeInsight
.lookup
.LookupElement
;
26 import com
.intellij
.codeInsight
.lookup
.LookupManager
;
27 import com
.intellij
.codeInsight
.lookup
.LookupValueWithPsiElement
;
28 import com
.intellij
.ide
.util
.EditSourceUtil
;
29 import com
.intellij
.lang
.Language
;
30 import com
.intellij
.lang
.LanguageExtension
;
31 import com
.intellij
.navigation
.NavigationItem
;
32 import com
.intellij
.openapi
.application
.ApplicationManager
;
33 import com
.intellij
.openapi
.components
.ServiceManager
;
34 import com
.intellij
.openapi
.editor
.Document
;
35 import com
.intellij
.openapi
.editor
.Editor
;
36 import com
.intellij
.openapi
.editor
.LogicalPosition
;
37 import com
.intellij
.openapi
.project
.Project
;
38 import com
.intellij
.openapi
.util
.TextRange
;
39 import com
.intellij
.pom
.Navigatable
;
40 import com
.intellij
.pom
.PomDeclarationSearcher
;
41 import com
.intellij
.pom
.PomTarget
;
42 import com
.intellij
.pom
.PsiDeclaredTarget
;
43 import com
.intellij
.pom
.references
.PomService
;
44 import com
.intellij
.psi
.*;
45 import com
.intellij
.psi
.util
.PsiTreeUtil
;
46 import com
.intellij
.util
.Consumer
;
47 import com
.intellij
.util
.containers
.CollectionFactory
;
48 import org
.jetbrains
.annotations
.NotNull
;
49 import org
.jetbrains
.annotations
.Nullable
;
51 import java
.util
.ArrayList
;
52 import java
.util
.Collection
;
53 import java
.util
.Collections
;
54 import java
.util
.List
;
56 public class TargetElementUtilBase
{
57 public static final int REFERENCED_ELEMENT_ACCEPTED
= 0x01;
58 public static final int ELEMENT_NAME_ACCEPTED
= 0x02;
59 public static final int LOOKUP_ITEM_ACCEPTED
= 0x08;
61 public static TargetElementUtilBase
getInstance() {
62 return ServiceManager
.getService(TargetElementUtilBase
.class);
65 public int getAllAccepted() {
66 return REFERENCED_ELEMENT_ACCEPTED
| ELEMENT_NAME_ACCEPTED
| LOOKUP_ITEM_ACCEPTED
;
70 * Accepts THIS or SUPER but not NEW_AS_CONSTRUCTOR.
72 public int getDefinitionSearchFlags() {
73 return getAllAccepted();
77 * Accepts NEW_AS_CONSTRUCTOR but not THIS or SUPER.
79 public int getReferenceSearchFlags() {
80 return getAllAccepted();
84 public static PsiReference
findReference(Editor editor
) {
85 return findReference(editor
, editor
.getCaretModel().getOffset());
89 public static PsiReference
findReference(Editor editor
, int offset
) {
90 Project project
= editor
.getProject();
91 if (project
== null) return null;
93 Document document
= editor
.getDocument();
94 PsiFile file
= PsiDocumentManager
.getInstance(project
).getPsiFile(document
);
95 if (file
== null) return null;
96 if (ApplicationManager
.getApplication().isDispatchThread()) {
97 PsiDocumentManager
.getInstance(project
).commitAllDocuments();
100 offset
= adjustOffset(document
, offset
);
102 if (file
instanceof PsiCompiledElement
) {
103 return ((PsiCompiledElement
)file
).getMirror().findReferenceAt(offset
);
106 return file
.findReferenceAt(offset
);
109 public static int adjustOffset(Document document
, final int offset
) {
110 CharSequence text
= document
.getCharsSequence();
111 int correctedOffset
= offset
;
112 int textLength
= document
.getTextLength();
113 if (offset
>= textLength
) {
114 correctedOffset
= textLength
- 1;
116 else if (!Character
.isJavaIdentifierPart(text
.charAt(offset
))) {
119 if (correctedOffset
< 0 || !Character
.isJavaIdentifierPart(text
.charAt(correctedOffset
))) return offset
;
120 return correctedOffset
;
124 public static PsiElement
findTargetElement(Editor editor
, int flags
) {
125 ApplicationManager
.getApplication().assertIsDispatchThread();
127 return getInstance().findTargetElement(editor
, flags
, editor
.getCaretModel().getOffset());
130 public static boolean inVirtualSpace(Editor editor
, int offset
) {
131 if (offset
== editor
.getCaretModel().getOffset()) {
132 return inVirtualSpace(editor
, editor
.getCaretModel().getLogicalPosition());
138 public static boolean inVirtualSpace(Editor editor
, LogicalPosition logicalPosition
) {
139 return !editor
.offsetToLogicalPosition(editor
.logicalPositionToOffset(logicalPosition
)).equals(logicalPosition
);
143 public PsiElement
findTargetElement(Editor editor
, int flags
, int offset
) {
144 Project project
= editor
.getProject();
145 if (project
== null) return null;
147 Lookup activeLookup
= LookupManager
.getInstance(project
).getActiveLookup();
148 if (activeLookup
!= null && (flags
& LOOKUP_ITEM_ACCEPTED
) != 0) {
149 final PsiElement lookupItem
= getLookupItem(activeLookup
);
150 return lookupItem
!= null && lookupItem
.isValid() ? lookupItem
: null;
153 Document document
= editor
.getDocument();
154 if (ApplicationManager
.getApplication().isDispatchThread()) {
155 PsiDocumentManager
.getInstance(project
).commitAllDocuments();
157 PsiFile file
= PsiDocumentManager
.getInstance(project
).getPsiFile(document
);
158 if (file
== null) return null;
160 offset
= adjustOffset(document
, offset
);
162 PsiElement element
= file
.findElementAt(offset
);
163 if ((flags
& REFERENCED_ELEMENT_ACCEPTED
) != 0) {
164 final PsiElement referenceOrReferencedElement
= getReferenceOrReferencedElement(file
, editor
, flags
, offset
);
165 //if (referenceOrReferencedElement == null) {
166 // return getReferenceOrReferencedElement(file, editor, flags, offset);
168 if (isAcceptableReferencedElement(element
, referenceOrReferencedElement
)) {
169 return referenceOrReferencedElement
;
173 if (element
== null) return null;
175 if ((flags
& ELEMENT_NAME_ACCEPTED
) != 0) {
176 if (element
instanceof PsiNamedElement
) return element
;
177 return getNamedElement(element
, offset
- element
.getTextRange().getStartOffset());
182 protected boolean isAcceptableReferencedElement(final PsiElement element
, final PsiElement referenceOrReferencedElement
) {
183 return referenceOrReferencedElement
!= null &&
184 referenceOrReferencedElement
.isValid();
188 public PsiElement
adjustElement(final Editor editor
, final int flags
, final PsiElement element
, final PsiElement contextElement
) {
193 public PsiElement
adjustReference(@NotNull PsiReference ref
){
198 public PsiElement
getNamedElement(@Nullable final PsiElement element
, final int offsetInElement
) {
199 if (element
== null) return null;
201 final List
<PomTarget
> targets
= CollectionFactory
.arrayList();
202 final Consumer
<PomTarget
> consumer
= new Consumer
<PomTarget
>() {
203 public void consume(PomTarget target
) {
204 if (target
instanceof PsiDeclaredTarget
) {
205 final PsiDeclaredTarget declaredTarget
= (PsiDeclaredTarget
)target
;
206 final PsiElement navigationElement
= declaredTarget
.getNavigationElement();
207 final TextRange range
= declaredTarget
.getNameIdentifierRange();
208 if (range
!= null && !range
.shiftRight(navigationElement
.getTextRange().getStartOffset())
209 .contains(element
.getTextRange().getStartOffset() + offsetInElement
)) {
217 PsiElement parent
= element
;
219 int offset
= offsetInElement
;
220 while (parent
!= null) {
221 for (PomDeclarationSearcher searcher
: PomDeclarationSearcher
.EP_NAME
.getExtensions()) {
222 searcher
.findDeclarationsAt(parent
, offset
, consumer
);
223 if (!targets
.isEmpty()) {
224 final PomTarget target
= targets
.get(0);
225 return target
== null ?
null : PomService
.convertToPsi(element
.getProject(), target
);
228 offset
+= parent
.getStartOffsetInParent();
229 parent
= parent
.getParent();
232 return getNamedElement(element
);
237 protected PsiElement
getNamedElement(@Nullable final PsiElement element
) {
239 if ((parent
= PsiTreeUtil
.getParentOfType(element
, PsiNamedElement
.class, false)) != null) {
240 // A bit hacky depends on navigation offset correctly overridden
241 assert element
!= null : "notnull parent?";
242 if (parent
.getTextOffset() == element
.getTextRange().getStartOffset()) {
250 private static PsiElement
getLookupItem(Lookup activeLookup
) {
251 LookupElement item
= activeLookup
.getCurrentItem();
252 if (item
== null) return null;
253 Object o
= item
.getObject();
255 if (o
instanceof PsiElement
) {
256 PsiElement element
= (PsiElement
)o
;
257 if (!(element
instanceof PsiDirectoryContainer
)) {
258 if (!isValidElement(element
)) return null;
262 else if (o
instanceof LookupValueWithPsiElement
) {
263 final PsiElement element
= ((LookupValueWithPsiElement
)o
).getElement();
264 if (element
!= null && isValidElement(element
)) return element
;
269 private static boolean isValidElement(@NotNull PsiElement element
) {
270 if (!element
.isValid()) return false;
271 PsiFile file
= element
.getContainingFile();
272 if (file
== null) return false;
273 return file
.getOriginalFile().getVirtualFile() != null;
277 protected PsiElement
getReferenceOrReferencedElement(PsiFile file
, Editor editor
, int flags
, int offset
) {
278 PsiReference ref
= findReference(editor
, offset
);
279 if (ref
== null) return null;
281 final Language language
= ref
.getElement().getLanguage();
282 final TargetElementEvaluator evaluator
= targetElementEvaluator
.forLanguage(language
);
283 if (evaluator
!= null) {
284 final PsiElement element
= evaluator
.getElementByReference(ref
, flags
);
285 if (element
!= null) return element
;
288 PsiManager manager
= file
.getManager();
289 PsiElement refElement
= ref
.resolve();
290 if (refElement
== null) {
291 if (ApplicationManager
.getApplication().isDispatchThread()) {
292 DaemonCodeAnalyzer
.getInstance(manager
.getProject()).updateVisibleHighlighters(editor
);
301 public Collection
<PsiElement
> getTargetCandidates(PsiReference reference
) {
302 if (reference
instanceof PsiPolyVariantReference
) {
303 final ResolveResult
[] results
= ((PsiPolyVariantReference
)reference
).multiResolve(false);
304 final ArrayList
<PsiElement
> navigatableResults
= new ArrayList
<PsiElement
>(results
.length
);
306 for(ResolveResult r
:results
) {
307 PsiElement element
= r
.getElement();
308 if (EditSourceUtil
.canNavigate(element
) || element
instanceof Navigatable
&& ((Navigatable
)element
).canNavigateToSource()) {
309 navigatableResults
.add(element
);
313 return navigatableResults
;
315 PsiElement resolved
= reference
.resolve();
316 if (resolved
instanceof NavigationItem
) {
317 return Collections
.singleton(resolved
);
319 return Collections
.emptyList();
322 public PsiElement
getGotoDeclarationTarget(final PsiElement element
, final PsiElement navElement
) {
326 public boolean includeSelfInGotoImplementation(final PsiElement element
) {
327 final TargetElementEvaluator elementEvaluator
= element
!= null ? targetElementEvaluator
.forLanguage(element
.getLanguage()):null;
328 return elementEvaluator
== null || elementEvaluator
.includeSelfInGotoImplementation(element
);
331 protected final LanguageExtension
<TargetElementEvaluator
> targetElementEvaluator
= new LanguageExtension
<TargetElementEvaluator
>("com.intellij.targetElementEvaluator");