diff --git a/java/java-impl/src/com/intellij/codeInsight/TargetElementUtil.java b/java/java-impl/src/com/intellij/codeInsight/TargetElementUtil.java index c9931b23cbd7..d4bf334a961e 100644 --- a/java/java-impl/src/com/intellij/codeInsight/TargetElementUtil.java +++ b/java/java-impl/src/com/intellij/codeInsight/TargetElementUtil.java @@ -34,52 +34,52 @@ import org.jetbrains.annotations.Nullable; import java.util.*; -public class TargetElementUtil extends TargetElementUtilBase { +public class TargetElementUtil extends TargetElementEvaluatorEx2 implements TargetElementUtilExtender{ public static final int NEW_AS_CONSTRUCTOR = 0x04; public static final int THIS_ACCEPTED = 0x10; public static final int SUPER_ACCEPTED = 0x20; @Override - public int getAllAccepted() { - return super.getAllAccepted() | NEW_AS_CONSTRUCTOR | THIS_ACCEPTED | SUPER_ACCEPTED; + public int getAdditionalAccepted() { + return NEW_AS_CONSTRUCTOR | THIS_ACCEPTED | SUPER_ACCEPTED; } @Override - public int getDefinitionSearchFlags() { - return super.getDefinitionSearchFlags() | THIS_ACCEPTED | SUPER_ACCEPTED; + public int getAdditionalDefinitionSearchFlags() { + return THIS_ACCEPTED | SUPER_ACCEPTED; } @Override - public int getReferenceSearchFlags() { - return super.getReferenceSearchFlags() | NEW_AS_CONSTRUCTOR; + public int getAdditionalReferenceSearchFlags() { + return NEW_AS_CONSTRUCTOR; } @Nullable @Override - public PsiElement findTargetElement(@NotNull final Editor editor, final int flags, final int offset) { - final PsiElement element = super.findTargetElement(editor, flags, offset); - if (element instanceof PsiKeyword) { - if (element.getParent() instanceof PsiThisExpression) { + public PsiElement adjustTargetElement(Editor editor, int offset, int flags, @NotNull PsiElement targetElement) { + if (targetElement instanceof PsiKeyword) { + if (targetElement.getParent() instanceof PsiThisExpression) { if ((flags & THIS_ACCEPTED) == 0) return null; - PsiType type = ((PsiThisExpression)element.getParent()).getType(); + PsiType type = ((PsiThisExpression)targetElement.getParent()).getType(); if (!(type instanceof PsiClassType)) return null; return ((PsiClassType)type).resolve(); } - if (element.getParent() instanceof PsiSuperExpression) { + if (targetElement.getParent() instanceof PsiSuperExpression) { if ((flags & SUPER_ACCEPTED) == 0) return null; - PsiType type = ((PsiSuperExpression)element.getParent()).getType(); + PsiType type = ((PsiSuperExpression)targetElement.getParent()).getType(); if (!(type instanceof PsiClassType)) return null; return ((PsiClassType)type).resolve(); } } - return element; + return super.adjustTargetElement(editor, offset, flags, targetElement); } @Override - protected boolean isAcceptableReferencedElement(final PsiElement element, final PsiElement referenceOrReferencedElement) { - return super.isAcceptableReferencedElement(element, referenceOrReferencedElement) && - !isEnumConstantReference(element, referenceOrReferencedElement); + @NotNull + public Answer isAcceptableReferencedElement(@NotNull final PsiElement element, final PsiElement referenceOrReferencedElement) { + if (isEnumConstantReference(element, referenceOrReferencedElement)) return Answer.NO; + return super.isAcceptableReferencedElement(element, referenceOrReferencedElement); } private static boolean isEnumConstantReference(final PsiElement element, final PsiElement referenceOrReferencedElement) { @@ -89,10 +89,19 @@ public class TargetElementUtil extends TargetElementUtilBase { ((PsiMethod)referenceOrReferencedElement).isConstructor(); } - @Override @Nullable - protected PsiElement getReferenceOrReferencedElement(PsiFile file, Editor editor, int flags, int offset) { - PsiElement refElement = super.getReferenceOrReferencedElement(file, editor, flags, offset); + @Override + public PsiElement getElementByReference(@NotNull PsiReference ref, int flags) { + return null; + } + + @Nullable + @Override + public PsiElement adjustReferenceOrReferencedElement(PsiFile file, + Editor editor, + int offset, + int flags, + @Nullable PsiElement refElement) { PsiReference ref = null; if (refElement == null) { ref = TargetElementUtilBase.findReference(editor, offset); @@ -144,45 +153,37 @@ public class TargetElementUtil extends TargetElementUtilBase { } } } - return refElement; + return super.adjustReferenceOrReferencedElement(file, editor, offset, flags, refElement); } @Nullable @Override - protected PsiElement getNamedElement(final PsiElement element) { + public PsiElement getNamedElement(@NotNull final PsiElement element) { PsiElement parent = element.getParent(); if (element instanceof PsiIdentifier) { - if (parent instanceof PsiClass && element.equals(((PsiClass)parent).getNameIdentifier())) { - return parent; - } - else if (parent instanceof PsiVariable && element.equals(((PsiVariable)parent).getNameIdentifier())) { - return parent; - } - else if (parent instanceof PsiMethod && element.equals(((PsiMethod)parent).getNameIdentifier())) { - return parent; - } - else if (parent instanceof PsiLabeledStatement && element.equals(((PsiLabeledStatement)parent).getLabelIdentifier())) { - return parent; - } - } - //TODO: Code below this comment is very similar to parent code. We probably need to use "super()" instead, to prevent copy/paste in inheritors - else if ((parent = PsiTreeUtil.getParentOfType(element, PsiNamedElement.class, false)) != null) { - // A bit hacky depends on navigation offset correctly overridden - if (parent.getTextOffset() == element.getTextRange().getStartOffset() && !(parent instanceof XmlAttribute) - && !(parent instanceof PsiFile && InjectedLanguageManager.getInstance(parent.getProject()).isInjectedFragment((PsiFile)parent))) { + if (parent instanceof PsiClass && element.equals(((PsiClass)parent).getNameIdentifier()) + || parent instanceof PsiVariable && element.equals(((PsiVariable)parent).getNameIdentifier()) + || parent instanceof PsiMethod && element.equals(((PsiMethod)parent).getNameIdentifier()) + || parent instanceof PsiLabeledStatement && element.equals(((PsiLabeledStatement)parent).getLabelIdentifier())) { return parent; } } return null; } + public boolean isAcceptableNamedParent(@NotNull PsiElement parent) { + return !(parent instanceof XmlAttribute) + && !(parent instanceof PsiFile && InjectedLanguageManager.getInstance(parent.getProject()).isInjectedFragment((PsiFile)parent)); + } + @Nullable public static PsiReferenceExpression findReferenceExpression(Editor editor) { - final PsiReference ref = findReference(editor); + final PsiReference ref = TargetElementUtilBase.findReference(editor); return ref instanceof PsiReferenceExpression ? (PsiReferenceExpression)ref : null; } + @Nullable @Override public PsiElement adjustReference(@NotNull final PsiReference ref) { final PsiElement parent = ref.getElement().getParent(); @@ -192,7 +193,7 @@ public class TargetElementUtil extends TargetElementUtilBase { @Nullable @Override - public PsiElement adjustElement(final Editor editor, final int flags, final PsiElement element, final PsiElement contextElement) { + public PsiElement adjustElement(Editor editor, int flags, @Nullable PsiElement element, @Nullable PsiElement contextElement) { if (element != null) { if (element instanceof PsiAnonymousClass) { return ((PsiAnonymousClass)element).getBaseClassType().resolve(); @@ -212,9 +213,9 @@ public class TargetElementUtil extends TargetElementUtilBase { return null; } - @NotNull @Override - public Collection getTargetCandidates(final PsiReference reference) { + @Nullable + public Collection getTargetCandidates(@NotNull PsiReference reference) { PsiElement parent = reference.getElement().getParent(); if (parent instanceof PsiMethodCallExpression || parent instanceof PsiNewExpression && ((PsiNewExpression)parent).getArrayDimensions().length == 0 && @@ -251,12 +252,13 @@ public class TargetElementUtil extends TargetElementUtilBase { } @Override - public PsiElement getGotoDeclarationTarget(final PsiElement element, final PsiElement navElement) { + @Nullable + public PsiElement getGotoDeclarationTarget(@NotNull final PsiElement element, @Nullable final PsiElement navElement) { if (navElement == element && element instanceof PsiCompiledElement && element instanceof PsiMethod) { PsiMethod method = (PsiMethod)element; if (method.isConstructor() && method.getParameterList().getParametersCount() == 0) { PsiClass aClass = method.getContainingClass(); - PsiElement navClass = aClass.getNavigationElement(); + PsiElement navClass = aClass == null ? null : aClass.getNavigationElement(); if (aClass != navClass) return navClass; } } @@ -272,7 +274,7 @@ public class TargetElementUtil extends TargetElementUtilBase { } @Override - public boolean acceptImplementationForReference(final PsiReference reference, final PsiElement element) { + public boolean acceptImplementationForReference(@Nullable PsiReference reference, @NotNull PsiElement element) { if (reference instanceof PsiReferenceExpression && element instanceof PsiMember) { return getMemberClass(reference, element) != null; } @@ -317,8 +319,10 @@ public class TargetElementUtil extends TargetElementUtilBase { }); } + @Override - public SearchScope getSearchScope(Editor editor, PsiElement element) { + @Nullable + public SearchScope getSearchScope(Editor editor, @NotNull PsiElement element) { final PsiReferenceExpression referenceExpression = editor != null ? findReferenceExpression(editor) : null; if (referenceExpression != null && element instanceof PsiMethod) { final PsiClass[] memberClass = getMemberClass(referenceExpression, element); diff --git a/java/java-impl/src/com/intellij/codeInspection/concurrencyAnnotations/JCiPOrderEntryFix.java b/java/java-impl/src/com/intellij/codeInspection/concurrencyAnnotations/JCiPOrderEntryFix.java index 2cfc9171e873..49f9bd8c252b 100644 --- a/java/java-impl/src/com/intellij/codeInspection/concurrencyAnnotations/JCiPOrderEntryFix.java +++ b/java/java-impl/src/com/intellij/codeInspection/concurrencyAnnotations/JCiPOrderEntryFix.java @@ -20,7 +20,7 @@ */ package com.intellij.codeInspection.concurrencyAnnotations; -import com.intellij.codeInsight.TargetElementUtil; +import com.intellij.codeInsight.TargetElementUtilBase; import com.intellij.codeInsight.daemon.impl.quickfix.OrderEntryFix; import com.intellij.codeInsight.intention.IntentionAction; import com.intellij.openapi.diagnostic.Logger; @@ -57,7 +57,7 @@ public class JCiPOrderEntryFix implements IntentionAction { public boolean isAvailable(@NotNull final Project project, final Editor editor, final PsiFile file) { if (!(file instanceof PsiJavaFile)) return false; - final PsiReference reference = TargetElementUtil.findReference(editor); + final PsiReference reference = TargetElementUtilBase.findReference(editor); if (!(reference instanceof PsiJavaCodeReferenceElement)) return false; if (reference.resolve() != null) return false; @NonNls final String referenceName = ((PsiJavaCodeReferenceElement)reference).getReferenceName(); @@ -74,7 +74,7 @@ public class JCiPOrderEntryFix implements IntentionAction { @Override public void invoke(@NotNull final Project project, final Editor editor, final PsiFile file) throws IncorrectOperationException { - final PsiJavaCodeReferenceElement reference = (PsiJavaCodeReferenceElement)TargetElementUtil.findReference(editor); + final PsiJavaCodeReferenceElement reference = (PsiJavaCodeReferenceElement)TargetElementUtilBase.findReference(editor); LOG.assertTrue(reference != null); String jarPath = PathUtil.getJarPathForClass(GuardedBy.class); final VirtualFile virtualFile = file.getVirtualFile(); diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/completion/JavaAutoPopupTest.groovy b/java/java-tests/testSrc/com/intellij/codeInsight/completion/JavaAutoPopupTest.groovy index 863ad0c9b5f5..6b12753d6aa3 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/completion/JavaAutoPopupTest.groovy +++ b/java/java-tests/testSrc/com/intellij/codeInsight/completion/JavaAutoPopupTest.groovy @@ -16,7 +16,7 @@ package com.intellij.codeInsight.completion import com.intellij.codeInsight.CodeInsightSettings -import com.intellij.codeInsight.TargetElementUtil +import com.intellij.codeInsight.TargetElementUtilBase import com.intellij.codeInsight.completion.impl.CompletionServiceImpl import com.intellij.codeInsight.editorActions.CompletionAutoPopupHandler import com.intellij.codeInsight.lookup.Lookup @@ -1107,17 +1107,17 @@ class Foo { type('x') assertContains 'x__foo', 'x__goo' edt { - assert foo == TargetElementUtil.instance.findTargetElement(myFixture.editor, TargetElementUtil.LOOKUP_ITEM_ACCEPTED) + assert foo == TargetElementUtilBase.instance.findTargetElement(myFixture.editor, TargetElementUtilBase.LOOKUP_ITEM_ACCEPTED) myFixture.performEditorAction(IdeActions.ACTION_EDITOR_MOVE_CARET_DOWN) - assert goo == TargetElementUtil.instance.findTargetElement(myFixture.editor, TargetElementUtil.LOOKUP_ITEM_ACCEPTED) + assert goo == TargetElementUtilBase.instance.findTargetElement(myFixture.editor, TargetElementUtilBase.LOOKUP_ITEM_ACCEPTED) } type('_') myFixture.assertPreferredCompletionItems 1, 'x__foo', 'x__goo' edt { - assert goo == TargetElementUtil.instance.findTargetElement(myFixture.editor, TargetElementUtil.LOOKUP_ITEM_ACCEPTED) + assert goo == TargetElementUtilBase.instance.findTargetElement(myFixture.editor, TargetElementUtilBase.LOOKUP_ITEM_ACCEPTED) myFixture.performEditorAction(IdeActions.ACTION_EDITOR_MOVE_CARET_UP) - assert foo == TargetElementUtil.instance.findTargetElement(myFixture.editor, TargetElementUtil.LOOKUP_ITEM_ACCEPTED) + assert foo == TargetElementUtilBase.instance.findTargetElement(myFixture.editor, TargetElementUtilBase.LOOKUP_ITEM_ACCEPTED) } } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/TargetElementEvaluatorEx2.java b/platform/lang-impl/src/com/intellij/codeInsight/TargetElementEvaluatorEx2.java index 541a5c1a510f..ace586b6b954 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/TargetElementEvaluatorEx2.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/TargetElementEvaluatorEx2.java @@ -15,13 +15,77 @@ */ package com.intellij.codeInsight; +import com.intellij.openapi.editor.Editor; import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiReference; +import com.intellij.psi.search.SearchScope; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -public abstract class TargetElementEvaluatorEx2 implements TargetElementEvaluatorEx { +import java.util.Collection; + +public abstract class TargetElementEvaluatorEx2 implements TargetElementEvaluator { @Nullable - public abstract PsiElement getNamedElement(@NotNull PsiElement element); + public PsiElement getNamedElement(@NotNull PsiElement element) { + return null; + } - public abstract boolean isAcceptableReferencedElement(@Nullable PsiElement element, @Nullable PsiElement referenceOrReferencedElement); + public boolean isAcceptableNamedParent(@NotNull PsiElement parent) { + return true; + } + + @Nullable + public PsiElement adjustElement(Editor editor, int flags, @Nullable PsiElement element, @Nullable PsiElement contextElement) { + return element; + } + + @Nullable + public PsiElement adjustTargetElement(Editor editor, int offset, int flags, @NotNull PsiElement targetElement) { + return targetElement; + } + + @Nullable + public PsiElement adjustReferenceOrReferencedElement(PsiFile file, + Editor editor, + int offset, + int flags, + @Nullable PsiElement refElement) { + return refElement; + } + + @Nullable + public PsiElement adjustReference(@NotNull PsiReference ref) { + return null; + } + + @Nullable + public Collection getTargetCandidates(@NotNull PsiReference reference) { + return null; + } + + @Nullable + public PsiElement getGotoDeclarationTarget(@NotNull final PsiElement element, @Nullable final PsiElement navElement) { + return null; + } + + @NotNull + public Answer isAcceptableReferencedElement(@NotNull PsiElement element, @Nullable PsiElement referenceOrReferencedElement) { + return Answer.DEFAULT; + } + + public boolean includeSelfInGotoImplementation(@NotNull final PsiElement element) { + return true; + } + + public boolean acceptImplementationForReference(@Nullable PsiReference reference, @NotNull PsiElement element) { + return true; + } + + @Nullable + public SearchScope getSearchScope(Editor editor, @NotNull PsiElement element) { + return null; + } + + public enum Answer { YES, NO, DEFAULT;} } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/TargetElementUtilBase.java b/platform/lang-impl/src/com/intellij/codeInsight/TargetElementUtilBase.java index 7eb959506722..5b2384c0d042 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/TargetElementUtilBase.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/TargetElementUtilBase.java @@ -35,8 +35,8 @@ import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.actions.EditorActionUtil; import com.intellij.openapi.editor.ex.util.EditorUtil; +import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.TextRange; import com.intellij.pom.Navigatable; import com.intellij.pom.PomDeclarationSearcher; @@ -67,21 +67,33 @@ public class TargetElementUtilBase { } public int getAllAccepted() { - return REFERENCED_ELEMENT_ACCEPTED | ELEMENT_NAME_ACCEPTED | LOOKUP_ITEM_ACCEPTED; + int result = REFERENCED_ELEMENT_ACCEPTED | ELEMENT_NAME_ACCEPTED | LOOKUP_ITEM_ACCEPTED; + for (TargetElementUtilExtender each : Extensions.getExtensions(TargetElementUtilExtender.EP_NAME)) { + result |= each.getAdditionalAccepted(); + } + return result; } /** * Accepts THIS or SUPER but not NEW_AS_CONSTRUCTOR. */ public int getDefinitionSearchFlags() { - return getAllAccepted(); + int result = getAllAccepted(); + for (TargetElementUtilExtender each : Extensions.getExtensions(TargetElementUtilExtender.EP_NAME)) { + result |= each.getAdditionalDefinitionSearchFlags(); + } + return result; } /** * Accepts NEW_AS_CONSTRUCTOR but not THIS or SUPER. */ public int getReferenceSearchFlags() { - return getAllAccepted(); + int result = getAllAccepted(); + for (TargetElementUtilExtender each : Extensions.getExtensions(TargetElementUtilExtender.EP_NAME)) { + result |= each.getAdditionalReferenceSearchFlags(); + } + return result; } @Nullable @@ -140,30 +152,12 @@ public class TargetElementUtilBase { private static boolean isIdentifierPart(@Nullable PsiFile file, CharSequence text, int offset) { if (file != null) { - for (TargetElementEvaluatorEx evaluator : getInstance().getElementEvaluatorsEx(file.getLanguage())) { - if (evaluator instanceof TargetElementEvaluatorEx && ((TargetElementEvaluatorEx)evaluator).isIdentifierPart(file, text, offset)) { - return true; - } - } + TargetElementEvaluatorEx evaluator = getInstance().getElementEvaluatorsEx(file.getLanguage()); + if (evaluator != null && evaluator.isIdentifierPart(file, text, offset)) return true; } return Character.isJavaIdentifierPart(text.charAt(offset)); } - @Nullable - public static PsiElement findTargetElement(Editor editor, int flags) { - ApplicationManager.getApplication().assertIsDispatchThread(); - - final PsiElement result = getInstance().findTargetElement(editor, flags, editor.getCaretModel().getOffset()); - if (result != null) { - return result; - } - final Integer offset = editor.getUserData(EditorActionUtil.EXPECTED_CARET_OFFSET); - if (offset != null) { - return getInstance().findTargetElement(editor, flags, offset); - } - return result; - } - public static boolean inVirtualSpace(@NotNull Editor editor, int offset) { if (offset == editor.getCaretModel().getOffset()) { return EditorUtil.inVirtualSpace(editor, editor.getCaretModel().getLogicalPosition()); @@ -172,8 +166,32 @@ public class TargetElementUtilBase { return false; } + @Nullable + public static PsiElement findTargetElement(Editor editor, int flags) { + ApplicationManager.getApplication().assertIsDispatchThread(); + + final PsiElement result = getInstance().findTargetElement(editor, flags, editor.getCaretModel().getOffset()); + if (result != null) return result; + + final Integer offset = editor.getUserData(EditorActionUtil.EXPECTED_CARET_OFFSET); + if (offset != null) { + return getInstance().findTargetElement(editor, flags, offset); + } + return null; + } + @Nullable public PsiElement findTargetElement(@NotNull Editor editor, int flags, int offset) { + PsiElement result = doFindTargetElement(editor, flags, offset); + TargetElementEvaluatorEx2 evaluator = result != null ? getElementEvaluatorsEx2(result.getLanguage()) : null; + if (evaluator != null) { + result = evaluator.adjustTargetElement(editor, offset, flags, result); + } + return result; + } + + @Nullable + private PsiElement doFindTargetElement(@NotNull Editor editor, int flags, int offset) { Project project = editor.getProject(); if (project == null) return null; @@ -229,25 +247,33 @@ public class TargetElementUtilBase { } protected boolean isAcceptableReferencedElement(@Nullable PsiElement element, @Nullable PsiElement referenceOrReferencedElement) { - if (referenceOrReferencedElement != null && referenceOrReferencedElement.isValid()) return true; + if (referenceOrReferencedElement == null || !referenceOrReferencedElement.isValid()) return false; - if (element != null) { - for (TargetElementEvaluatorEx2 evaluator : getElementEvaluatorsEx2(element.getLanguage())) { - if (evaluator.isAcceptableReferencedElement(element, referenceOrReferencedElement)) return true; - } + TargetElementEvaluatorEx2 evaluator = element != null ? getElementEvaluatorsEx2(element.getLanguage()) : null; + if (evaluator != null) { + TargetElementEvaluatorEx2.Answer answer = evaluator.isAcceptableReferencedElement(element, referenceOrReferencedElement); + if (answer == TargetElementEvaluatorEx2.Answer.YES) return true; + if (answer == TargetElementEvaluatorEx2.Answer.NO) return false; } - return false; + return true; } @Nullable - public PsiElement adjustElement(final Editor editor, final int flags, final PsiElement element, final PsiElement contextElement) { + public PsiElement adjustElement(final Editor editor, final int flags, @Nullable PsiElement element, @Nullable PsiElement contextElement) { + PsiElement langElement = element == null ? contextElement : element; + TargetElementEvaluatorEx2 evaluator = langElement != null ? getElementEvaluatorsEx2(langElement.getLanguage()) : null; + if (evaluator != null) { + element = evaluator.adjustElement(editor, flags, element, contextElement); + } return element; } @Nullable - public PsiElement adjustReference(@NotNull PsiReference ref){ - return null; + public PsiElement adjustReference(@NotNull PsiReference ref) { + PsiElement element = ref.getElement(); + TargetElementEvaluatorEx2 evaluator = element != null ? getElementEvaluatorsEx2(element.getLanguage()) : null; + return evaluator != null ? evaluator.adjustReference(ref) : null; } @Nullable @@ -291,20 +317,22 @@ public class TargetElementUtilBase { @Nullable - protected PsiElement getNamedElement(@Nullable final PsiElement element) { + private PsiElement getNamedElement(@Nullable final PsiElement element) { if (element == null) return null; - for (TargetElementEvaluatorEx2 each : getElementEvaluatorsEx2(element.getLanguage())) { - PsiElement result = each.getNamedElement(element); + TargetElementEvaluatorEx2 evaluator = getElementEvaluatorsEx2(element.getLanguage()); + if (evaluator != null) { + PsiElement result = evaluator.getNamedElement(element); if (result != null) return result; } PsiElement parent; if ((parent = PsiTreeUtil.getParentOfType(element, PsiNamedElement.class, false)) != null) { // A bit hacky depends on navigation offset correctly overridden - assert element != null : "notnull parent?"; if (parent.getTextOffset() == element.getTextRange().getStartOffset()) { - return parent; + if (evaluator == null || evaluator.isAcceptableNamedParent(parent)) { + return parent; + } } } @@ -312,13 +340,25 @@ public class TargetElementUtilBase { } @Nullable - protected PsiElement getReferenceOrReferencedElement(PsiFile file, Editor editor, int flags, int offset) { + private PsiElement getReferenceOrReferencedElement(PsiFile file, Editor editor, int flags, int offset) { + PsiElement result = doGetReferenceOrReferencedElement(file, editor, flags, offset); + PsiElement languageElement = file.findElementAt(offset); + Language language = languageElement != null ? languageElement.getLanguage() : file.getLanguage(); + TargetElementEvaluatorEx2 evaluator = getElementEvaluatorsEx2(language); + if (evaluator != null) { + result = evaluator.adjustReferenceOrReferencedElement(file, editor, offset, flags, result); + } + return result; + } + + @Nullable + private PsiElement doGetReferenceOrReferencedElement(PsiFile file, Editor editor, int flags, int offset) { PsiReference ref = findReference(editor, offset); if (ref == null) return null; final Language language = ref.getElement().getLanguage(); - final List evaluators = targetElementEvaluator.allForLanguage(language); - for (TargetElementEvaluator evaluator : evaluators) { + TargetElementEvaluator evaluator = targetElementEvaluator.forLanguage(language); + if (evaluator != null) { final PsiElement element = evaluator.getElementByReference(ref, flags); if (element != null) return element; } @@ -337,7 +377,14 @@ public class TargetElementUtilBase { } @NotNull - public Collection getTargetCandidates(PsiReference reference) { + public Collection getTargetCandidates(@NotNull PsiReference reference) { + PsiElement refElement = reference.getElement(); + TargetElementEvaluatorEx2 evaluator = refElement != null ? getElementEvaluatorsEx2(refElement.getLanguage()) : null; + if (evaluator != null) { + Collection candidates = evaluator.getTargetCandidates(reference); + if (candidates != null) return candidates; + } + if (reference instanceof PsiPolyVariantReference) { final ResolveResult[] results = ((PsiPolyVariantReference)reference).multiResolve(false); List navigatableResults = new ArrayList(results.length); @@ -359,40 +406,40 @@ public class TargetElementUtilBase { } public PsiElement getGotoDeclarationTarget(final PsiElement element, final PsiElement navElement) { + TargetElementEvaluatorEx2 evaluator = element != null ? getElementEvaluatorsEx2(element.getLanguage()) : null; + if (evaluator != null) { + PsiElement result = evaluator.getGotoDeclarationTarget(element, navElement); + if (result != null) return result; + } return navElement; } public boolean includeSelfInGotoImplementation(@NotNull final PsiElement element) { - final TargetElementEvaluator elementEvaluator = targetElementEvaluator.forLanguage(element.getLanguage()); - return elementEvaluator == null || elementEvaluator.includeSelfInGotoImplementation(element); + TargetElementEvaluator evaluator = targetElementEvaluator.forLanguage(element.getLanguage()); + return evaluator == null || evaluator.includeSelfInGotoImplementation(element); + } + + public boolean acceptImplementationForReference(@Nullable PsiReference reference, @Nullable PsiElement element) { + TargetElementEvaluatorEx2 evaluator = element != null ? getElementEvaluatorsEx2(element.getLanguage()) : null; + return evaluator == null || evaluator.acceptImplementationForReference(reference, element); + } + + @NotNull + public SearchScope getSearchScope(Editor editor, @NotNull PsiElement element) { + TargetElementEvaluatorEx2 evaluator = getElementEvaluatorsEx2(element.getLanguage()); + SearchScope result = evaluator != null ? evaluator.getSearchScope(editor, element) : null; + return result != null ? result : PsiSearchHelper.SERVICE.getInstance(element.getProject()).getUseScope(element); } protected final LanguageExtension targetElementEvaluator = new LanguageExtension("com.intellij.targetElementEvaluator"); - - private Iterable getElementEvaluatorsEx(@NotNull Language language) { - //noinspection unchecked - return ContainerUtil.iterate(targetElementEvaluator.allForLanguage(language), new Condition() { - @Override - public boolean value(Object evaluator) { - return evaluator instanceof TargetElementEvaluatorEx; - } - }); + @Nullable + private TargetElementEvaluatorEx getElementEvaluatorsEx(@NotNull Language language) { + TargetElementEvaluator result = targetElementEvaluator.forLanguage(language); + return result instanceof TargetElementEvaluatorEx ? (TargetElementEvaluatorEx)result : null; } - private Iterable getElementEvaluatorsEx2(@NotNull Language language) { - //noinspection unchecked - return ContainerUtil.iterate(targetElementEvaluator.allForLanguage(language), new Condition() { - @Override - public boolean value(Object evaluator) { - return evaluator instanceof TargetElementEvaluatorEx2; - } - }); - } - - public boolean acceptImplementationForReference(PsiReference reference, PsiElement element) { - return true; - } - - public SearchScope getSearchScope(Editor editor, PsiElement element) { - return PsiSearchHelper.SERVICE.getInstance(element.getProject()).getUseScope(element); + @Nullable + private TargetElementEvaluatorEx2 getElementEvaluatorsEx2(@NotNull Language language) { + TargetElementEvaluator result = targetElementEvaluator.forLanguage(language); + return result instanceof TargetElementEvaluatorEx2 ? (TargetElementEvaluatorEx2)result : null; } } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/TargetElementUtilExtender.java b/platform/lang-impl/src/com/intellij/codeInsight/TargetElementUtilExtender.java new file mode 100644 index 000000000000..b378c106df1a --- /dev/null +++ b/platform/lang-impl/src/com/intellij/codeInsight/TargetElementUtilExtender.java @@ -0,0 +1,28 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.codeInsight; + +import com.intellij.openapi.extensions.ExtensionPointName; + +public interface TargetElementUtilExtender { + ExtensionPointName EP_NAME = ExtensionPointName.create("com.intellij.targetElementUtilExtender"); + + int getAdditionalAccepted(); + + int getAdditionalDefinitionSearchFlags(); + + int getAdditionalReferenceSearchFlags(); +} diff --git a/platform/platform-resources/src/META-INF/LangExtensionPoints.xml b/platform/platform-resources/src/META-INF/LangExtensionPoints.xml index f1bea2c94fcf..6c5e79b54576 100644 --- a/platform/platform-resources/src/META-INF/LangExtensionPoints.xml +++ b/platform/platform-resources/src/META-INF/LangExtensionPoints.xml @@ -720,6 +720,7 @@ + diff --git a/plugins/groovy/groovy-psi/groovy-psi.iml b/plugins/groovy/groovy-psi/groovy-psi.iml index f1f297e58bc2..84c5382cc7a7 100644 --- a/plugins/groovy/groovy-psi/groovy-psi.iml +++ b/plugins/groovy/groovy-psi/groovy-psi.iml @@ -15,6 +15,7 @@ + diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/GroovyTargetElementEvaluator.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/GroovyTargetElementEvaluator.java index 0511752500cb..f819e882c842 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/GroovyTargetElementEvaluator.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/GroovyTargetElementEvaluator.java @@ -16,7 +16,7 @@ package org.jetbrains.plugins.groovy.lang.psi.impl; -import com.intellij.codeInsight.TargetElementEvaluator; +import com.intellij.codeInsight.TargetElementUtil; import com.intellij.openapi.util.Key; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiMethod; @@ -35,14 +35,10 @@ import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GrRenameableLightEle /** * @author Maxim.Medvedev */ -public class GroovyTargetElementEvaluator implements TargetElementEvaluator { +public class GroovyTargetElementEvaluator extends TargetElementUtil { public static final Key NAVIGATION_ELEMENT_IS_NOT_TARGET = Key.create("GroovyTargetElementEvaluator.DONT_FOLLOW_NAVIGATION_ELEMENT"); - @Override - public boolean includeSelfInGotoImplementation(@NotNull PsiElement element) { - return true; - } @Override public PsiElement getElementByReference(@NotNull PsiReference ref, int flags) { diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/references/MavenTargetUtil.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/references/MavenTargetUtil.java index 18ecd76db4b6..4462538683ba 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/references/MavenTargetUtil.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/references/MavenTargetUtil.java @@ -15,7 +15,6 @@ */ package org.jetbrains.idea.maven.dom.references; -import com.intellij.codeInsight.TargetElementUtil; import com.intellij.codeInsight.TargetElementUtilBase; import com.intellij.openapi.editor.Editor; import com.intellij.psi.PsiElement; @@ -34,7 +33,7 @@ public class MavenTargetUtil { public static PsiElement getFindTarget(Editor editor, PsiFile file) { if (editor == null || file == null) return null; - PsiElement target = TargetElementUtil.findTargetElement(editor, TargetElementUtilBase.REFERENCED_ELEMENT_ACCEPTED); + PsiElement target = TargetElementUtilBase.findTargetElement(editor, TargetElementUtilBase.REFERENCED_ELEMENT_ACCEPTED); if (target instanceof MavenPsiElementWrapper) { return ((MavenPsiElementWrapper)target).getWrappee(); } diff --git a/plugins/maven/src/test/java/org/jetbrains/idea/maven/dom/MavenDomTestCase.java b/plugins/maven/src/test/java/org/jetbrains/idea/maven/dom/MavenDomTestCase.java index ec73f528aefe..f4b689d9f066 100644 --- a/plugins/maven/src/test/java/org/jetbrains/idea/maven/dom/MavenDomTestCase.java +++ b/plugins/maven/src/test/java/org/jetbrains/idea/maven/dom/MavenDomTestCase.java @@ -16,7 +16,6 @@ package org.jetbrains.idea.maven.dom; import com.intellij.codeInsight.CodeInsightSettings; -import com.intellij.codeInsight.TargetElementUtil; import com.intellij.codeInsight.TargetElementUtilBase; import com.intellij.codeInsight.documentation.DocumentationManager; import com.intellij.codeInsight.highlighting.HighlightUsagesHandler; @@ -353,7 +352,7 @@ public abstract class MavenDomTestCase extends MavenImportingTestCase { MapDataContext context = new MapDataContext(); context.put(CommonDataKeys.EDITOR, getEditor(f)); context.put(CommonDataKeys.PSI_FILE, getTestPsiFile(f)); - context.put(CommonDataKeys.PSI_ELEMENT, TargetElementUtil.findTargetElement(getEditor(f), + context.put(CommonDataKeys.PSI_ELEMENT, TargetElementUtilBase.findTargetElement(getEditor(f), TargetElementUtilBase.REFERENCED_ELEMENT_ACCEPTED | TargetElementUtilBase.ELEMENT_NAME_ACCEPTED)); return context; diff --git a/resources/src/META-INF/IdeaPlugin.xml b/resources/src/META-INF/IdeaPlugin.xml index c1053917bc37..d505255c77e1 100644 --- a/resources/src/META-INF/IdeaPlugin.xml +++ b/resources/src/META-INF/IdeaPlugin.xml @@ -1363,8 +1363,12 @@ + + serviceImplementation="com.intellij.codeInsight.TargetElementUtilBase"/> + + +