java class reference: search subtypes only on smart completion, work as usual (and fast) on basic (IDEA-92934)

This commit is contained in:
peter
2012-10-16 16:15:58 +02:00
parent c13e9f5e3c
commit 30cdd2b4bc
11 changed files with 116 additions and 71 deletions
@@ -15,11 +15,15 @@
*/
package com.intellij.codeInsight.completion;
import com.intellij.openapi.actionSystem.IdeActions;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiPackage;
import com.intellij.psi.PsiReference;
import com.intellij.psi.impl.source.resolve.reference.impl.PsiMultiReference;
import com.intellij.psi.impl.source.resolve.reference.impl.providers.JavaClassReference;
import com.intellij.psi.impl.source.resolve.reference.impl.providers.JavaClassReferenceSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -31,24 +35,33 @@ public class JavaClassReferenceCompletionContributor extends CompletionContribut
public void duringCompletion(@NotNull CompletionInitializationContext context) {
JavaClassReference reference = findJavaClassReference(context.getFile(), context.getStartOffset());
if (reference != null && reference.getExtendClassNames() != null) {
final PsiReference[] references = reference.getJavaClassReferenceSet().getReferences();
final PsiReference last = references[references.length - 1];
context.setReplacementOffset(last.getRangeInElement().getEndOffset() + last.getElement().getTextRange().getStartOffset());
JavaClassReferenceSet set = reference.getJavaClassReferenceSet();
context.setReplacementOffset(set.getRangeInElement().getEndOffset() + set.getElement().getTextRange().getStartOffset());
}
}
@Override
public void fillCompletionVariants(CompletionParameters parameters, CompletionResultSet result) {
if (parameters.getCompletionType() == CompletionType.SMART) {
return;
}
PsiElement position = parameters.getPosition();
JavaClassReference reference = findJavaClassReference(position.getContainingFile(), parameters.getOffset());
if (reference == null) {
return;
}
String[] extendClassNames = reference.getExtendClassNames();
PsiElement context = reference.getCompletionContext();
if (extendClassNames != null && context instanceof PsiPackage) {
if (parameters.getCompletionType() == CompletionType.SMART) {
JavaClassReferenceSet set = reference.getJavaClassReferenceSet();
int setStart = set.getRangeInElement().getStartOffset() + set.getElement().getTextRange().getStartOffset();
String fullPrefix = parameters.getPosition().getContainingFile().getText().substring(setStart, parameters.getOffset());
reference.processSubclassVariants((PsiPackage)context, extendClassNames, result.withPrefixMatcher(fullPrefix));
return;
}
result.addLookupAdvertisement("Press " + getActionShortcut(IdeActions.ACTION_SMART_TYPE_COMPLETION) + " to see inheritors of " +
StringUtil.join(extendClassNames, ", "));
}
if (parameters.getCompletionType() == CompletionType.CLASS_NAME) {
JavaClassNameCompletionContributor.addAllClasses(parameters, result);
}
@@ -15,6 +15,7 @@
*/
package com.intellij.psi.impl.source.resolve.reference.impl.providers;
import com.intellij.codeInsight.completion.CompletionResultSet;
import com.intellij.codeInsight.completion.JavaLookupElementBuilder;
import com.intellij.codeInsight.completion.scope.JavaCompletionProcessor;
import com.intellij.codeInsight.daemon.QuickFixProvider;
@@ -23,6 +24,7 @@ import com.intellij.codeInsight.daemon.impl.quickfix.OrderEntryFix;
import com.intellij.codeInsight.daemon.impl.quickfix.QuickFixAction;
import com.intellij.codeInsight.daemon.impl.quickfix.QuickFixActionRegistrarImpl;
import com.intellij.codeInsight.daemon.quickFix.CreateClassOrPackageFix;
import com.intellij.codeInsight.lookup.LookupElementBuilder;
import com.intellij.codeInspection.LocalQuickFix;
import com.intellij.codeInspection.LocalQuickFixProvider;
import com.intellij.lang.java.JavaLanguage;
@@ -55,7 +57,10 @@ import com.intellij.util.text.CharArrayUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
/**
* @author peter
@@ -225,15 +230,8 @@ public class JavaClassReference extends GenericReference implements PsiJavaRefer
@Override
@NotNull
public Object[] getVariants() {
PsiElement context = getContext();
if (context == null) {
context = JavaPsiFacade.getInstance(getElement().getProject()).findPackage("");
}
PsiElement context = getCompletionContext();
if (context instanceof PsiPackage) {
final String[] extendClasses = getExtendClassNames();
if (extendClasses != null) {
return getSubclassVariants((PsiPackage)context, extendClasses);
}
return processPackage((PsiPackage)context);
}
if (context instanceof PsiClass) {
@@ -257,6 +255,12 @@ public class JavaClassReference extends GenericReference implements PsiJavaRefer
return ArrayUtil.EMPTY_OBJECT_ARRAY;
}
@NotNull
public PsiElement getCompletionContext() {
PsiElement context = getContext();
return context == null ? JavaPsiFacade.getInstance(getElement().getProject()).findPackage("") : context;
}
public String[] getExtendClassNames() {
return JavaClassReferenceProvider.EXTEND_CLASS_NAMES.getValue(getOptions());
}
@@ -508,8 +512,7 @@ public class JavaClassReference extends GenericReference implements PsiJavaRefer
}
@NotNull
private Object[] getSubclassVariants(@NotNull PsiPackage context, @NotNull String[] extendClasses) {
HashSet<Object> lookups = new HashSet<Object>();
public void processSubclassVariants(@NotNull PsiPackage context, @NotNull String[] extendClasses, CompletionResultSet result) {
GlobalSearchScope packageScope = PackageScope.packageScope(context, true);
GlobalSearchScope scope = myJavaClassReferenceSet.getProvider().getScope(getElement().getProject());
if (scope != null) {
@@ -529,35 +532,22 @@ public class JavaClassReference extends GenericReference implements PsiJavaRefer
// add itself
if (packageScope.contains(extendClass.getContainingFile().getVirtualFile())) {
if (isClassAccepted(extendClass, classKind, instantiatable, concrete, notInterface, notEnum)) {
ContainerUtil.addIfNotNull(createSubclassLookupValue(context, extendClass), lookups);
result.addElement(createSubclassLookupValue(extendClass, extendClassName));
}
}
for (final PsiClass clazz : ClassInheritorsSearch.search(extendClass, packageScope, true)) {
if (isClassAccepted(clazz, classKind, instantiatable, concrete, notInterface, notEnum)) {
ContainerUtil.addIfNotNull(createSubclassLookupValue(context, clazz), lookups);
String qname = clazz.getQualifiedName();
if (qname != null && isClassAccepted(clazz, classKind, instantiatable, concrete, notInterface, notEnum)) {
result.addElement(createSubclassLookupValue(clazz, qname));
}
}
}
}
return lookups.toArray();
}
@Nullable
private static Object createSubclassLookupValue(@NotNull final PsiPackage context, @NotNull final PsiClass clazz) {
final String qname = clazz.getQualifiedName();
if (qname == null) return null;
String name = qname;
final String pack = context.getQualifiedName();
if (pack.length() > 0) {
if (name.startsWith(pack)) {
name = name.substring(pack.length() + 1);
}
else {
return null;
}
}
return JavaLookupElementBuilder.forClass(clazz, name, true).withLookupString(qname).withLookupString(clazz.getName());
@NotNull
private static LookupElementBuilder createSubclassLookupValue(@NotNull final PsiClass clazz, @NotNull String qname) {
return JavaLookupElementBuilder.forClass(clazz, qname, true).withPresentableText(StringUtil.getShortName(qname));
}
@Override
@@ -59,6 +59,11 @@ public class JavaClassReferenceSet {
return myProvider;
}
public TextRange getRangeInElement() {
PsiReference[] references = getReferences();
return new TextRange(references[0].getRangeInElement().getStartOffset(), references[references.length - 1].getRangeInElement().getEndOffset());
}
private void reparse(String str, PsiElement element, final boolean isStaticImport, JavaClassReferenceSet context) {
myElement = element;
myContext = context;
@@ -69,4 +69,6 @@ public interface Lookup {
PrefixMatcher itemMatcher(@NotNull LookupElement item);
boolean isSelectionTouched();
List<String> getAdvertisements();
}
@@ -138,4 +138,7 @@ public class Advertiser {
return myComponent;
}
public List<String> getAdvertisements() {
return myTexts;
}
}
@@ -1158,6 +1158,11 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable
return mySelectionTouched;
}
@Override
public List<String> getAdvertisements() {
return myAdComponent.getAdvertisements();
}
@Override
public void hide(){
hideLookup(true);
@@ -1290,7 +1295,7 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable
}
public void addAdvertisement(@NotNull final String text) {
ApplicationManager.getApplication().invokeLater(new Runnable() {
Runnable runnable = new Runnable() {
@Override
public void run() {
if (!myDisposed) {
@@ -1301,7 +1306,12 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable
}
}
}
}, myModalityState);
};
if (ApplicationManager.getApplication().isDispatchThread()) {
runnable.run();
} else {
ApplicationManager.getApplication().invokeLater(runnable, myModalityState);
}
}
public boolean isLookupDisposed() {
@@ -19,6 +19,7 @@ package com.intellij.testFramework.fixtures;
import com.intellij.codeInsight.completion.CompletionType;
import com.intellij.codeInsight.daemon.impl.HighlightInfo;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.codeInsight.lookup.Lookup;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInspection.InspectionProfileEntry;
import com.intellij.codeInspection.InspectionToolProvider;
@@ -361,6 +362,8 @@ public interface CodeInsightTestFixture extends IdeaProjectTestFixture {
void finishLookup();
Lookup getLookup();
@NotNull
PsiElement getElementAtCaret();
@@ -1879,7 +1879,7 @@ public class CodeInsightTestFixtureImpl extends BaseFixture implements CodeInsig
}
}
private LookupImpl getLookup() {
public LookupImpl getLookup() {
return (LookupImpl)LookupManager.getActiveLookup(myEditor);
}
@@ -0,0 +1,5 @@
<idea-plugin>
<actions>
<action class="foo.<caret>"/>
</actions>
</idea-plugin>
@@ -0,0 +1,5 @@
<idea-plugin>
<actions>
<action class="foo.<caret>"/>
</actions>
</idea-plugin>
@@ -13,28 +13,22 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.idea.devkit.codeInsight;
import com.intellij.codeInsight.TargetElementUtilBase;
import com.intellij.codeInspection.LocalInspectionTool;
import com.intellij.openapi.application.PluginPathManager;
import com.intellij.openapi.application.Result;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.psi.ElementDescriptionUtil;
import com.intellij.psi.PsiElement;
import com.intellij.testFramework.PsiTestUtil;
import com.intellij.testFramework.fixtures.IdeaTestFixtureFactory;
import com.intellij.testFramework.fixtures.JavaCodeInsightFixtureTestCase;
import com.intellij.testFramework.fixtures.TempDirTestFixture;
import com.intellij.usageView.UsageViewNodeTextLocation;
import com.intellij.usageView.UsageViewTypeLocation;
import com.intellij.util.xml.DeprecatedClassUsageInspection;
import org.jetbrains.idea.devkit.inspections.*;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
package org.jetbrains.idea.devkit.codeInsight
import com.intellij.codeInsight.TargetElementUtilBase
import com.intellij.codeInsight.completion.CompletionType
import com.intellij.codeInspection.LocalInspectionTool
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.application.PluginPathManager
import com.intellij.psi.ElementDescriptionUtil
import com.intellij.psi.PsiElement
import com.intellij.testFramework.PsiTestUtil
import com.intellij.testFramework.fixtures.IdeaTestFixtureFactory
import com.intellij.testFramework.fixtures.JavaCodeInsightFixtureTestCase
import com.intellij.testFramework.fixtures.TempDirTestFixture
import com.intellij.usageView.UsageViewNodeTextLocation
import com.intellij.usageView.UsageViewTypeLocation
import com.intellij.util.xml.DeprecatedClassUsageInspection
import org.jetbrains.idea.devkit.inspections.*
/**
* @author peter
*/
@@ -124,14 +118,8 @@ public class PluginXmlFunctionalTest extends JavaCodeInsightFixtureTestCase {
}
private void addPluginXml(final String root, final String text) throws IOException {
myTempDirFixture.createFile(root +
"/META-INF/plugin.xml", text);
new WriteCommandAction(getProject()) {
@Override
protected void run(Result result) throws Throwable {
PsiTestUtil.addSourceContentToRoots(myModule, myTempDirFixture.getFile(root));
}
}.execute();
myTempDirFixture.createFile(root + "/META-INF/plugin.xml", text);
ApplicationManager.application.runWriteAction { PsiTestUtil.addSourceContentToRoots(myModule, myTempDirFixture.getFile(root)) }
}
public void testNoWordCompletionInClassPlaces() throws Throwable {
@@ -152,6 +140,27 @@ public class PluginXmlFunctionalTest extends JavaCodeInsightFixtureTestCase {
myFixture.checkResultByFile(getTestName(false) + "_after.xml");
}
public void testShowPackagesInActionClass() {
myFixture.addClass("package com.intellij.openapi.actionSystem; public class AnAction { }");
myFixture.addClass("package foo.bar; public class BarAction extends com.intellij.openapi.actionSystem.AnAction { }");
myFixture.addClass("package foo.goo; public class GooAction extends com.intellij.openapi.actionSystem.AnAction { }");
myFixture.configureByFile(getTestName(false) + ".xml");
myFixture.completeBasic();
assert myFixture.lookupElementStrings == ['bar', 'goo']
assert myFixture.lookup.advertisements.find { it.contains('to see inheritors of com.intellij.openapi.actionSystem.AnAction') }
}
public void testShowAnActionInheritorsOnSmartCompletion() {
myFixture.addClass("package com.intellij.openapi.actionSystem; public class AnAction { }");
myFixture.addClass("package foo.bar; public class BarAction extends com.intellij.openapi.actionSystem.AnAction { }");
myFixture.addClass("package foo.goo; public class GooAction extends com.intellij.openapi.actionSystem.AnAction { }");
myFixture.addClass("package another.goo; public class AnotherAction extends com.intellij.openapi.actionSystem.AnAction { }");
myFixture.configureByFile(getTestName(false) + ".xml");
myFixture.complete(CompletionType.SMART);
assert myFixture.lookupElementStrings == ['foo.bar.BarAction', 'foo.goo.GooAction']
assert !myFixture.lookup.advertisements.find { it.contains('to see inheritors of com.intellij.openapi.actionSystem.AnAction') }
}
public void testDeprecatedExtensionAttribute() {
myFixture.enableInspections(DeprecatedClassUsageInspection.class);
myFixture.testHighlighting("deprecatedExtensionAttribute.xml", "MyExtBean.java");