diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/ConstructorInsertHandler.java b/java/java-impl/src/com/intellij/codeInsight/completion/ConstructorInsertHandler.java index c2ce494ab957..b0dc386a4a57 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/ConstructorInsertHandler.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/ConstructorInsertHandler.java @@ -192,7 +192,7 @@ public class ConstructorInsertHandler implements InsertHandler { @Override public void consume(LookupElement lookupElement) { - final Object object = lookupElement.getObject(); - if (object instanceof PsiClass) { - registerClass((PsiClass)object); + PsiClass psiClass = extractClass(lookupElement); + if (psiClass != null) { + registerClass(psiClass); } myResult.addElement(AutoCompletionPolicy.NEVER_AUTOCOMPLETE.applyPolicy(lookupElement)); } + @Nullable private static PsiClass extractClass(LookupElement lookupElement) { + final Object object = lookupElement.getObject(); + if (object instanceof PsiClass) { + return (PsiClass)object; + } + if (object instanceof PsiMethod && ((PsiMethod)object).isConstructor()) { + return ((PsiMethod)object).getContainingClass(); + } + return null; + } + public void registerClass(@NotNull PsiClass psiClass) { ContainerUtil.addIfNotNull(myAddedClasses, getClassName(psiClass)); } @@ -58,8 +70,8 @@ public class JavaCompletionSession implements Consumer { } public boolean alreadyProcessed(@NotNull LookupElement element) { - final Object object = element.getObject(); - return object instanceof PsiClass && alreadyProcessed((PsiClass)object); + final PsiClass psiClass = extractClass(element); + return psiClass != null && alreadyProcessed(psiClass); } public boolean alreadyProcessed(@NotNull PsiClass object) { diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionUtil.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionUtil.java index 927dfbf3e295..ca955ed337c0 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionUtil.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionUtil.java @@ -53,8 +53,11 @@ import com.intellij.psi.scope.util.PsiScopesUtil; import com.intellij.psi.util.*; import com.intellij.psi.util.proximity.ReferenceListWeigher; import com.intellij.ui.JBColor; -import com.intellij.util.*; +import com.intellij.util.IncorrectOperationException; +import com.intellij.util.NullableFunction; +import com.intellij.util.PairFunction; import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.containers.JBIterable; import com.siyeh.ig.psiutils.SideEffectChecker; import gnu.trove.THashSet; import org.jetbrains.annotations.NotNull; @@ -308,7 +311,7 @@ public class JavaCompletionUtil { final Set mentioned = new THashSet<>(); for (CompletionElement completionElement : processor.getResults()) { - for (LookupElement item : createLookupElements(completionElement, javaReference, processor)) { + for (LookupElement item : createLookupElements(completionElement, javaReference)) { item.putUserData(QUALIFIER_TYPE_ATTR, qualifierType); final Object o = item.getObject(); if (o instanceof PsiClass && !isSourceLevelAccessible(element, (PsiClass)o, pkgContext)) { @@ -509,7 +512,7 @@ public class JavaCompletionUtil { return false; } - static List createLookupElements(CompletionElement completionElement, PsiJavaReference reference, JavaCompletionProcessor processor) { + static Iterable createLookupElements(CompletionElement completionElement, PsiJavaReference reference) { Object completion = completionElement.getElement(); assert !(completion instanceof LookupElement); @@ -520,10 +523,15 @@ public class JavaCompletionUtil { } if (completion instanceof PsiClass) { - return JavaClassNameCompletionContributor.createClassLookupItems((PsiClass)completion, - JavaClassNameCompletionContributor.AFTER_NEW.accepts(reference), - JavaClassNameInsertHandler.JAVA_CLASS_INSERT_HANDLER, - Conditions.alwaysTrue()); + List classItems = JavaClassNameCompletionContributor.createClassLookupItems((PsiClass)completion, + JavaClassNameCompletionContributor.AFTER_NEW + .accepts(reference), + JavaClassNameInsertHandler.JAVA_CLASS_INSERT_HANDLER, + Conditions.alwaysTrue()); + if (JavaClassNameCompletionContributor.AFTER_NEW.accepts(reference)) { + return JBIterable.from(classItems).flatMap(i -> JavaConstructorCallElement.wrap(i, reference.getElement())); + } + return classItems; } } @@ -534,7 +542,13 @@ public class JavaCompletionUtil { PsiSubstitutor substitutor = completionElement.getSubstitutor(); if (substitutor == null) substitutor = PsiSubstitutor.EMPTY; if (completion instanceof PsiClass) { - return Collections.singletonList(JavaClassNameCompletionContributor.createClassLookupItem((PsiClass)completion, true).setSubstitutor(substitutor)); + JavaPsiClassReferenceElement classItem = + JavaClassNameCompletionContributor.createClassLookupItem((PsiClass)completion, true).setSubstitutor(substitutor); + if (JavaClassNameCompletionContributor.AFTER_NEW.accepts(reference)) { + return JavaConstructorCallElement.wrap(classItem, reference.getElement()); + } + + return Collections.singletonList(classItem); } if (completion instanceof PsiMethod) { JavaMethodCallElement item = new JavaMethodCallElement((PsiMethod)completion).setQualifierSubstitutor(substitutor); @@ -554,15 +568,12 @@ public class JavaCompletionUtil { final PsiClass psiClass = PsiUtil.resolveClassInType(type); if (psiClass == null || psiClass.isEnum() || psiClass.isAnnotationType()) return false; - if (!(psiClass instanceof PsiCompiledElement)) return true; + PsiMethod[] methods = psiClass.getConstructors(); + return methods.length == 0 || Arrays.stream(methods).anyMatch(JavaCompletionUtil::isConstructorCompletable); + } - final PsiMethod[] methods = psiClass.getConstructors(); - if (methods.length == 0) return true; - - for (final PsiMethod method : methods) { - if (!method.hasModifierProperty(PsiModifier.PRIVATE)) return true; - } - return false; + static boolean isConstructorCompletable(@NotNull PsiMethod constructor) { + return !(constructor instanceof PsiCompiledElement) || !constructor.hasModifierProperty(PsiModifier.PRIVATE); } public static Set getAllLookupStrings(@NotNull PsiMember member) { diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaConstructorCallElement.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaConstructorCallElement.java new file mode 100644 index 000000000000..b68a0d7da7fc --- /dev/null +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaConstructorCallElement.java @@ -0,0 +1,93 @@ +/* + * Copyright 2000-2016 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.completion; + +import com.intellij.codeInsight.lookup.LookupElement; +import com.intellij.codeInsight.lookup.LookupElementDecorator; +import com.intellij.codeInsight.lookup.LookupElementPresentation; +import com.intellij.openapi.util.Key; +import com.intellij.openapi.util.registry.Registry; +import com.intellij.psi.*; +import com.intellij.util.containers.JBIterable; +import org.jetbrains.annotations.NotNull; + +import java.util.Collections; +import java.util.List; +import java.util.function.Supplier; + +/** + * @author peter + */ +public class JavaConstructorCallElement extends JavaMethodCallElement { + private static final Key WRAPPING_CONSTRUCTOR_CALL = Key.create("WRAPPING_CONSTRUCTOR_CALL"); + @NotNull private final LookupElement myClassItem; + @NotNull private final Supplier myType; + + private JavaConstructorCallElement(@NotNull LookupElement classItem, @NotNull PsiMethod constructor, @NotNull Supplier type) { + super(constructor); + myClassItem = classItem; + myType = type; + setQualifierSubstitutor(((PsiClassType) type.get()).resolveGenerics().getSubstitutor()); + + LookupElement delegate = classItem; + while (true) { + delegate.putUserData(WRAPPING_CONSTRUCTOR_CALL, this); + if (!(delegate instanceof LookupElementDecorator)) break; + delegate = ((LookupElementDecorator)delegate).getDelegate(); + } + } + + @NotNull + @Override + public PsiType getType() { + return myType.get(); + } + + @Override + public void handleInsert(InsertionContext context) { + myClassItem.handleInsert(context); + super.handleInsert(context); + } + + @Override + public void renderElement(LookupElementPresentation presentation) { + myClassItem.renderElement(presentation); + String itemText = presentation.getItemText(); + + super.renderElement(presentation); + presentation.setItemText(itemText); + } + + static List wrap(JavaPsiClassReferenceElement classItem, PsiElement position) { + PsiClass psiClass = classItem.getObject(); + return wrap(classItem, psiClass, position, () -> JavaPsiFacade.getElementFactory(psiClass.getProject()).createType(psiClass, PsiSubstitutor.EMPTY)); + } + + static List wrap(LookupElement classItem, PsiClass psiClass, PsiElement position, Supplier type) { + if (Registry.is("java.completion.show.constructors") && JavaClassNameCompletionContributor.AFTER_NEW.accepts(position)) { + return JBIterable.of(psiClass.getConstructors()). + filter(JavaCompletionUtil::isConstructorCompletable). + map(c -> new JavaConstructorCallElement(classItem, c, type)). + toList(); + } + return Collections.singletonList(classItem); + } + + static boolean isWrapped(LookupElement element) { + return element.getUserData(WRAPPING_CONSTRUCTOR_CALL) != null; + } + +} diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaInheritorsGetter.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaInheritorsGetter.java index 29b01a0146e1..19c6a5ef2981 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaInheritorsGetter.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaInheritorsGetter.java @@ -18,10 +18,7 @@ package com.intellij.codeInsight.completion; import com.intellij.codeInsight.CodeInsightUtil; import com.intellij.codeInsight.ExpectedTypeInfo; import com.intellij.codeInsight.daemon.impl.analysis.HighlightClassUtil; -import com.intellij.codeInsight.lookup.AutoCompletionPolicy; -import com.intellij.codeInsight.lookup.LookupElement; -import com.intellij.codeInsight.lookup.LookupElementDecorator; -import com.intellij.codeInsight.lookup.PsiTypeLookupItem; +import com.intellij.codeInsight.lookup.*; import com.intellij.openapi.diagnostic.Logger; import com.intellij.pom.java.LanguageLevel; import com.intellij.psi.*; @@ -43,6 +40,7 @@ import org.jetbrains.annotations.Nullable; import java.util.Arrays; import java.util.Collection; import java.util.List; +import java.util.function.Supplier; import static com.intellij.patterns.PsiJavaPatterns.psiElement; @@ -89,7 +87,8 @@ public class JavaInheritorsGetter extends CompletionProvider { final LookupElement element = addExpectedType(type, parameters); if (element != null) { - consumer.consume(element); + Supplier itemType = () -> ObjectUtils.assertNotNull(element.as(TypedLookupItem.CLASS_CONDITION_KEY)).getType(); + JavaConstructorCallElement.wrap(element, (PsiClass)element.getObject(), parameters.getPosition(), itemType).forEach(consumer::consume); } if (arraysWelcome) { consumer.consume(createNewArrayItem(parameters.getPosition(), type.createArrayType())); diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/SuperCalls.java b/java/java-impl/src/com/intellij/codeInsight/completion/SuperCalls.java index c6423a403564..dcd34bfa1f22 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/SuperCalls.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/SuperCalls.java @@ -48,7 +48,7 @@ class SuperCalls { fakeSuper.processVariants(superProcessor); for (CompletionElement completionElement : superProcessor.getResults()) { - for (LookupElement item : JavaCompletionUtil.createLookupElements(completionElement, javaReference, superProcessor)) { + for (LookupElement item : JavaCompletionUtil.createLookupElements(completionElement, javaReference)) { set.add(withQualifiedSuper(className, item)); } } diff --git a/platform/util/resources/misc/registry.properties b/platform/util/resources/misc/registry.properties index 2e439eb5dd7c..70e69290e878 100644 --- a/platform/util/resources/misc/registry.properties +++ b/platform/util/resources/misc/registry.properties @@ -318,6 +318,9 @@ java.completion.make.outer.variables.final.description=Make variables accessed f java.completion.argument.live.template=false java.completion.argument.live.template.description=When completing a method call, start a live template with all arguments +java.completion.show.constructors=false +java.completion.show.constructors.description=Show separate constructors when completing constructor call, instead of a single class name + java.annotations.inference.nullable.method=false java.annotations.inference.nullable.method.description=Restart is required; infer @Nullable annotation for method results java.annotations.inference.nullable.method.transitivity=true