IJPL-214795 [java-completion] Some reference completion (partial classes, variables, fields, packages)

GitOrigin-RevId: 2f8f143e509eb686bc542ededd61aabe559214cf
This commit is contained in:
Tagir Valeev
2025-12-15 10:16:32 +00:00
committed by intellij-monorepo-bot
parent b19d370b89
commit 2c9ab44f07
17 changed files with 844 additions and 72 deletions
@@ -1195,6 +1195,7 @@
<modcompletion.completionItemProvider language="JAVA" implementationClass="com.intellij.codeInsight.completion.modcompletion.LabelReferenceItemProvider"/>
<modcompletion.completionItemProvider language="JAVA" implementationClass="com.intellij.codeInsight.completion.modcompletion.AnnotationAttributeItemProvider"/>
<modcompletion.completionItemProvider language="JAVA" implementationClass="com.intellij.codeInsight.completion.modcompletion.GenerateMemberItemProvider"/>
<modcompletion.completionItemProvider language="JAVA" implementationClass="com.intellij.codeInsight.completion.modcompletion.ReferenceItemProvider"/>
<completion.skip implementation="com.intellij.codeInsight.completion.AbstractExpectedTypeSkipper" id="skipAbstract"/>
<completion.skip implementation="com.intellij.codeInsight.completion.DeprecatedSkipper" id="skipDeprecated"/>
@@ -13,6 +13,7 @@ import com.intellij.psi.util.PsiUtil;
import com.intellij.util.JavaPsiConstructorUtil;
import com.intellij.util.containers.ContainerUtil;
import com.siyeh.ig.psiutils.VariableAccessUtils;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -20,16 +21,17 @@ import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
class CheckInitialized implements ElementFilter {
@ApiStatus.Internal
public class CheckInitialized implements ElementFilter {
private final Set<PsiField> myNonInitializedFields;
private final boolean myInsideConstructorCall;
CheckInitialized(@NotNull PsiElement position) {
public CheckInitialized(@NotNull PsiElement position) {
myNonInitializedFields = getNonInitializedFields(position);
myInsideConstructorCall = isInsideConstructorCall(position);
}
static boolean isInsideConstructorCall(@NotNull PsiElement position) {
public static boolean isInsideConstructorCall(@NotNull PsiElement position) {
return JavaPsiConstructorUtil.isConstructorCall(PsiTreeUtil.getParentOfType(position, PsiMethodCallExpression.class)) &&
!JavaKeywordCompletion.AFTER_DOT.accepts(position);
}
@@ -14,6 +14,7 @@ import com.intellij.codeInsight.guess.GuessManager;
import com.intellij.codeInsight.lookup.*;
import com.intellij.java.codeserver.core.JavaPsiModuleUtil;
import com.intellij.lang.java.JavaLanguage;
import com.intellij.modcompletion.ModCompletionItemProvider;
import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
@@ -85,7 +86,7 @@ public final class JavaCompletionUtil {
public static final Key<Boolean> SUPER_METHOD_PARAMETERS = Key.create("SUPER_METHOD_PARAMETERS");
public static @Nullable Set<PsiType> getExpectedTypes(@NotNull CompletionParameters parameters) {
public static @Nullable Set<PsiType> getExpectedTypes(@NotNull BaseCompletionParameters parameters) {
PsiExpression expr = PsiTreeUtil.getContextOfType(parameters.getPosition(), PsiExpression.class, true);
if (expr != null) {
Set<PsiType> set = new HashSet<>();
@@ -328,48 +329,45 @@ public final class JavaCompletionUtil {
return Collections.emptyList();
}
private static boolean shouldCast(@NotNull LookupElement item,
@NotNull PsiTypeLookupItem castTypeItem,
@Nullable PsiType plainQualifier,
@NotNull JavaCompletionProcessor processor,
@NotNull Set<? extends PsiType> expectedTypes) {
PsiType castType = castTypeItem.getType();
public static boolean shouldCast(@Nullable PsiType plainQualifier,
@NotNull JavaCompletionProcessor processor,
@NotNull Set<? extends PsiType> expectedTypes,
PsiType castType,
@NotNull PsiMember member) {
if (plainQualifier != null) {
Object o = item.getObject();
if (o instanceof PsiMethod) {
if (plainQualifier instanceof PsiClassType && castType instanceof PsiClassType) {
PsiMethod method = (PsiMethod)o;
PsiClassType.ClassResolveResult plainResult = ((PsiClassType)plainQualifier).resolveGenerics();
PsiClass plainClass = plainResult.getElement();
HierarchicalMethodSignature signature = method.getHierarchicalMethodSignature();
PsiMethod plainMethod = plainClass == null ? null :
StreamEx.ofTree(signature, s -> StreamEx.of(s.getSuperSignatures()))
.map(sig -> MethodSignatureUtil.findMethodBySignature(plainClass, sig, true))
.filter(Objects::nonNull)
.findFirst().orElse(null);
if (plainMethod != null) {
PsiClassType.ClassResolveResult castResult = ((PsiClassType)castType).resolveGenerics();
PsiClass castClass = castResult.getElement();
if (member instanceof PsiMethod method &&
plainQualifier instanceof PsiClassType classQualifier &&
castType instanceof PsiClassType classCastType) {
PsiClassType.ClassResolveResult plainResult = classQualifier.resolveGenerics();
PsiClass plainClass = plainResult.getElement();
HierarchicalMethodSignature signature = method.getHierarchicalMethodSignature();
PsiMethod plainMethod = plainClass == null ? null :
StreamEx.ofTree(signature, s -> StreamEx.of(s.getSuperSignatures()))
.map(sig -> MethodSignatureUtil.findMethodBySignature(plainClass, sig, true))
.filter(Objects::nonNull)
.findFirst().orElse(null);
if (plainMethod != null) {
PsiClassType.ClassResolveResult castResult = classCastType.resolveGenerics();
PsiClass castClass = castResult.getElement();
if (castClass == null || !castClass.isInheritor(plainClass, true)) {
return false;
}
if (!processor.isAccessible(plainMethod)) {
return true;
}
PsiSubstitutor castSub = TypeConversionUtil.getSuperClassSubstitutor(plainClass, (PsiClassType)castType);
PsiType typeAfterCast = toRaw(castSub.substitute(method.getReturnType()));
PsiType typeDeclared = toRaw(plainResult.getSubstitutor().substitute(plainMethod.getReturnType()));
return typeAfterCast != null && typeDeclared != null &&
!typeAfterCast.equals(typeDeclared) &&
ContainerUtil.exists(expectedTypes, et -> et.isAssignableFrom(typeAfterCast) && !et.isAssignableFrom(typeDeclared));
if (castClass == null || !castClass.isInheritor(plainClass, true)) {
return false;
}
if (!processor.isAccessible(plainMethod)) {
return true;
}
PsiSubstitutor castSub = TypeConversionUtil.getSuperClassSubstitutor(plainClass, classCastType);
PsiType typeAfterCast = toRaw(castSub.substitute(method.getReturnType()));
PsiType typeDeclared = toRaw(plainResult.getSubstitutor().substitute(plainMethod.getReturnType()));
return typeAfterCast != null && typeDeclared != null &&
!typeAfterCast.equals(typeDeclared) &&
ContainerUtil.exists(expectedTypes, et -> et.isAssignableFrom(typeAfterCast) && !et.isAssignableFrom(typeDeclared));
}
}
return containsMember(castType, o, true) && !containsMember(plainQualifier, o, true);
return containsMember(castType, member, true) && !containsMember(plainQualifier, member, true);
}
return false;
}
@@ -420,7 +418,12 @@ public final class JavaCompletionUtil {
@Nullable PsiType plainQualifier,
JavaCompletionProcessor processor,
Set<? extends PsiType> expectedTypes) {
return ContainerUtil.find(castTypeItems, c -> shouldCast(item, c, plainQualifier, processor, expectedTypes));
PsiMember member = ObjectUtils.tryCast(item.getObject(), PsiMember.class);
if (member == null) return null;
return ContainerUtil.find(castTypeItems, c -> {
PsiType castType = c.getType();
return shouldCast(plainQualifier, processor, expectedTypes, castType, member);
});
}
private static @Nullable PsiType toRaw(@Nullable PsiType type) {
@@ -536,7 +539,7 @@ public final class JavaCompletionUtil {
return Collections.singletonList(JavaLookupElementBuilder.forMethod((PsiMethod)completion, PsiSubstitutor.EMPTY));
}
if (completion instanceof PsiClass) {
if (completion instanceof PsiClass && !ModCompletionItemProvider.modCommandCompletionEnabled()) {
List<JavaPsiClassReferenceElement> classItems = JavaClassNameCompletionContributor.createClassLookupItems(
CompletionUtil.getOriginalOrSelf((PsiClass)completion),
JavaClassNameCompletionContributor.AFTER_NEW.accepts(reference),
@@ -548,7 +551,7 @@ public final class JavaCompletionUtil {
PsiSubstitutor substitutor = completionElement.getSubstitutor();
if (substitutor == null) substitutor = PsiSubstitutor.EMPTY;
if (completion instanceof PsiClass) {
if (completion instanceof PsiClass && !ModCompletionItemProvider.modCommandCompletionEnabled()) {
JavaPsiClassReferenceElement classItem =
JavaClassNameCompletionContributor.createClassLookupItem((PsiClass)completion, true).setSubstitutor(substitutor);
return JavaConstructorCallElement.wrap(classItem, reference.getElement());
@@ -563,14 +566,14 @@ public final class JavaCompletionUtil {
item.setForcedQualifier(completionElement.getQualifierText());
return Collections.singletonList(item);
}
if (completion instanceof PsiVariable) {
if (completion instanceof PsiVariable && !ModCompletionItemProvider.modCommandCompletionEnabled()) {
if (completion instanceof PsiEnumConstant enumConstant &&
PsiTreeUtil.isAncestor(enumConstant.getArgumentList(), reference.getElement(), true)) {
return Collections.emptyList();
}
return Collections.singletonList(new VariableLookupItem((PsiVariable)completion).setSubstitutor(substitutor).qualifyIfNeeded(reference, null));
}
if (completion instanceof PsiPackage) {
if (completion instanceof PsiPackage && !ModCompletionItemProvider.modCommandCompletionEnabled()) {
return Collections.singletonList(new PackageLookupItem((PsiPackage)completion, reference.getElement()));
}
@@ -762,9 +765,13 @@ public final class JavaCompletionUtil {
}
}
static boolean inSomePackage(@NotNull PsiElement context) {
/**
* @param context element from Java file
* @return true if the element is located not in the unnamed package
*/
public static boolean inSomePackage(@NotNull PsiElement context) {
PsiFile contextFile = context.getContainingFile();
return contextFile instanceof PsiClassOwner && StringUtil.isNotEmpty(((PsiClassOwner)contextFile).getPackageName());
return contextFile instanceof PsiClassOwner owner && StringUtil.isNotEmpty(owner.getPackageName());
}
static boolean isSourceLevelAccessible(@NotNull PsiElement context,
@@ -773,7 +780,7 @@ public final class JavaCompletionUtil {
return isSourceLevelAccessible(context, psiClass, pkgContext, psiClass.getContainingClass());
}
private static boolean isSourceLevelAccessible(PsiElement context,
public static boolean isSourceLevelAccessible(PsiElement context,
@NotNull PsiClass psiClass,
boolean pkgContext,
@Nullable PsiClass qualifierClass) {
@@ -211,7 +211,12 @@ public class JavaPsiClassReferenceElement extends LookupItem<Object> implements
return CodeStyle.getLanguageSettings(element.getContainingFile(), JavaLanguage.INSTANCE).SPACE_AFTER_COMMA;
}
static boolean isInaccessibleConstructorSuggestion(@NotNull PsiElement position, @Nullable PsiClass cls) {
/**
* @param position position to test
* @param cls class where constructor is located
* @return true if there's no accessible constructor of a given class in a given position.
*/
public static boolean isInaccessibleConstructorSuggestion(@NotNull PsiElement position, @Nullable PsiClass cls) {
if (cls == null || cls.hasModifierProperty(PsiModifier.ABSTRACT)) return false;
PsiMethod[] constructors = cls.getConstructors();
if (constructors.length > 0) {
@@ -134,6 +134,10 @@ public final class JavaSmartCompletionContributor {
}
public static ExpectedTypeInfo @NotNull [] getExpectedTypes(final CompletionParameters parameters) {
return getExpectedTypes((BaseCompletionParameters)parameters);
}
public static ExpectedTypeInfo @NotNull [] getExpectedTypes(final BaseCompletionParameters parameters) {
return getExpectedTypes(parameters.getPosition(), parameters.getCompletionType() == CompletionType.SMART);
}
@@ -2,6 +2,7 @@
package com.intellij.codeInsight.completion;
import com.intellij.codeInsight.lookup.LookupElementPresentation;
import com.intellij.java.JavaBundle;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiFormatUtil;
@@ -87,7 +88,7 @@ public final class MemberLookupHelper {
presentation.appendTailText(params, false);
if (myShouldImport && !constructor && StringUtil.isNotEmpty(className)) {
presentation.appendTailText(" in " + className + location, true);
presentation.appendTailText(JavaBundle.message("member.in.class", className) + location, true);
} else {
presentation.appendTailText(location, true);
}
@@ -25,7 +25,7 @@ import static com.intellij.patterns.PsiJavaPatterns.psiElement;
public final class ReferenceExpressionCompletionContributor {
private static final Logger LOG = Logger.getInstance(ReferenceExpressionCompletionContributor.class);
static @NotNull ElementFilter getReferenceFilter(PsiElement element, boolean allowRecursion) {
public static @NotNull ElementFilter getReferenceFilter(PsiElement element, boolean allowRecursion) {
//throw foo
if (psiElement().withParent(psiElement(PsiReferenceExpression.class).withParent(PsiThrowStatement.class)).accepts(element)) {
return TrueFilter.INSTANCE;
@@ -3,11 +3,13 @@ package com.intellij.codeInsight.completion.modcompletion;
import com.intellij.application.options.CodeStyle;
import com.intellij.codeInsight.completion.AllClassesGetter;
import com.intellij.codeInsight.completion.JavaCompletionUtil;
import com.intellij.lang.java.JavaLanguage;
import com.intellij.modcommand.ActionContext;
import com.intellij.modcommand.ModPsiUpdater;
import com.intellij.modcompletion.ModCompletionItemPresentation;
import com.intellij.modcompletion.PsiUpdateCompletionItem;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Iconable;
import com.intellij.openapi.util.NlsSafe;
@@ -15,7 +17,12 @@ import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.util.text.MarkupText;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.JavaCodeStyleSettings;
import com.intellij.psi.impl.source.codeStyle.ImportHelper;
import com.intellij.psi.javadoc.PsiDocComment;
import com.intellij.psi.javadoc.PsiDocTag;
import com.intellij.psi.util.PsiFormatUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtilCore;
import com.siyeh.ig.psiutils.JavaDeprecationUtils;
import org.jetbrains.annotations.NotNullByDefault;
@@ -57,9 +64,57 @@ public final class ClassReferenceCompletionItem extends PsiUpdateCompletionItem<
@Override
public void update(ActionContext actionContext, InsertionContext insertionContext, ModPsiUpdater updater) {
if (!contextObject().isValid()) return;
if (processInImport(updater)) return;
if (processJavaDoc(actionContext.offset(), updater)) return;
AllClassesGetter.tryShorten(updater.getPsiFile(), updater, contextObject());
}
private boolean processJavaDoc(int startOffset, ModPsiUpdater updater) {
PsiFile file = updater.getPsiFile();
int offset = updater.getCaretOffset();
PsiElement position = file.findElementAt(offset - 1);
PsiJavaCodeReferenceElement ref = position != null && position.getParent() instanceof PsiJavaCodeReferenceElement codeRef ?
codeRef : null;
String qname = myQualifiedName;
if (qname != null && PsiTreeUtil.getParentOfType(position, PsiDocComment.class, false) != null &&
(ref == null || !ref.isQualified()) &&
shouldInsertFqnInJavadoc(file)) {
updater.getDocument().replaceString(startOffset, offset, qname);
return true;
}
return ref != null && PsiTreeUtil.getParentOfType(position, PsiDocTag.class) != null && ref.isReferenceTo(contextObject());
}
private boolean shouldInsertFqnInJavadoc(PsiFile file) {
return switch (JavaCodeStyleSettings.getInstance(file).CLASS_NAMES_IN_JAVADOC) {
case JavaCodeStyleSettings.FULLY_QUALIFY_NAMES_ALWAYS -> true;
case JavaCodeStyleSettings.FULLY_QUALIFY_NAMES_IF_NOT_IMPORTED -> file instanceof PsiJavaFile javaFile && myQualifiedName != null &&
!ImportHelper.isAlreadyImported(javaFile, myQualifiedName);
default -> false;
};
}
private boolean processInImport(ModPsiUpdater updater) {
int offset = updater.getCaretOffset();
PsiFile file = updater.getPsiFile();
PsiImportStatementBase importStatement = PsiTreeUtil.findElementOfClassAtOffset(file, offset - 1, PsiImportStatementBase.class, false);
if (importStatement == null) return false;
PsiJavaCodeReferenceElement ref = PsiTreeUtil.findElementOfClassAtOffset(file, offset - 1, PsiJavaCodeReferenceElement.class, false);
Document document = updater.getDocument();
if (myQualifiedName != null && (ref == null || !myQualifiedName.equals(ref.getCanonicalText()))) {
int start = JavaCompletionUtil.findQualifiedNameStart(offset, document);
document.replaceString(start, offset, myQualifiedName);
}
if (importStatement instanceof PsiImportStaticStatement) {
//context.setAddCompletionChar(false);
document.insertString(offset, ".");
updater.moveCaretTo(offset + 1);
}
return true;
}
@Override
public ModCompletionItemPresentation presentation() {
String name = getName();
@@ -1,16 +1,409 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.completion.modcompletion;
import com.intellij.application.options.CodeStyle;
import com.intellij.codeInsight.ExpectedTypeInfo;
import com.intellij.codeInsight.JavaTailTypes;
import com.intellij.codeInsight.ModNavigatorTailType;
import com.intellij.codeInsight.completion.*;
import com.intellij.codeInsight.completion.scope.CompletionElement;
import com.intellij.codeInsight.completion.scope.JavaCompletionProcessor;
import com.intellij.codeInsight.guess.GuessManager;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.java.syntax.parser.JavaKeywords;
import com.intellij.lang.Language;
import com.intellij.modcommand.ActionContext;
import com.intellij.modcommand.ModCommand;
import com.intellij.modcommand.ModCommandExecutor;
import com.intellij.modcompletion.ModCompletionItem;
import com.intellij.modcompletion.ModCompletionItemPresentation;
import com.intellij.modcompletion.ModCompletionItemProvider;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.Iconable;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.text.MarkupText;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.patterns.ElementPattern;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.CommonCodeStyleSettings;
import com.intellij.psi.filters.AndFilter;
import com.intellij.psi.filters.ElementExtractorFilter;
import com.intellij.psi.filters.ElementFilter;
import com.intellij.psi.filters.TrueFilter;
import com.intellij.psi.util.*;
import com.intellij.ui.IconManager;
import com.intellij.ui.PlatformIcons;
import com.intellij.util.ObjectUtils;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.JBIterable;
import org.jetbrains.annotations.NotNullByDefault;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.Unmodifiable;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.function.Consumer;
import static com.intellij.patterns.PlatformPatterns.psiElement;
import static com.intellij.patterns.PsiJavaPatterns.psiMethod;
import static com.intellij.patterns.PsiJavaPatterns.virtualFile;
@NotNullByDefault
final class ReferenceItemProvider implements ModCompletionItemProvider {
private static final ElementPattern<PsiElement> TOP_LEVEL_VAR_IN_MODULE = psiElement().withSuperParent(3, PsiJavaFile.class)
.inVirtualFile(virtualFile().withName("module-info.java"));
private static final ElementPattern<PsiElement> INSIDE_TYPECAST_EXPRESSION = psiElement().withParent(
psiElement(PsiReferenceExpression.class).afterLeaf(
psiElement().withText(")").withParent(PsiTypeCastExpression.class)));
private static final ElementPattern<PsiElement> INSIDE_CONSTRUCTOR = psiElement().inside(psiMethod().constructor(true));
private static final ElementPattern<PsiElement> AFTER_NEW = psiElement().afterLeaf(psiElement().withText(JavaKeywords.NEW));
@Override
public void provideItems(CompletionContext context, Consumer<ModCompletionItem> sink) {
PsiElement position = context.getPosition();
if (!(position.getParent() instanceof PsiJavaCodeReferenceElement ref)) return;
if (TOP_LEVEL_VAR_IN_MODULE.accepts(position) ||
JavaMemberNameCompletionContributor.INSIDE_TYPE_PARAMS_PATTERN.accepts(position)) {
return;
}
Set<ExpectedTypeInfo> expectedTypes = ContainerUtil.newHashSet(
JavaSmartCompletionContributor.getExpectedTypes(position, context.isSmart()));
ElementFilter filter = JavaCompletionContributor.getReferenceFilter(position);
if (filter == null) return;
if (context.invocationCount() <= 1 && JavaClassNameCompletionContributor.AFTER_NEW.accepts(position)) {
filter = new AndFilter(filter, new ElementFilter() {
@Override
public boolean isAcceptable(Object element, @Nullable PsiElement context) {
return !JavaPsiClassReferenceElement.isInaccessibleConstructorSuggestion(position, ObjectUtils.tryCast(element, PsiClass.class));
}
@Override
public boolean isClassAcceptable(Class<?> hintClass) {
return true;
}
});
}
boolean smart = context.isSmart();
if (smart) {
if (INSIDE_TYPECAST_EXPRESSION.accepts(position) || inCastContext(context)) {
return;
}
ElementFilter smartRestriction = ReferenceExpressionCompletionContributor.getReferenceFilter(position, false);
if (smartRestriction != TrueFilter.INSTANCE) {
filter = new AndFilter(filter, smartRestriction);
}
}
boolean inSwitchLabel = JavaCompletionContributor.IN_SWITCH_LABEL.accepts(position);
ModNavigatorTailType forcedTail = getTailType(smart, inSwitchLabel, position);
if (INSIDE_CONSTRUCTOR.accepts(position) &&
(context.invocationCount() <= 1 || CheckInitialized.isInsideConstructorCall(position))) {
filter = new AndFilter(filter, new CheckInitialized(position));
}
PsiFile originalFile = context.getOriginalFile();
boolean first = context.invocationCount() <= 1;
boolean instantiableOnly = AFTER_NEW.accepts(position);
JavaCompletionProcessor.Options options =
JavaCompletionProcessor.Options.DEFAULT_OPTIONS
.withCheckAccess(first)
.withFilterStaticAfterInstance(first)
.withInstantiableOnly(instantiableOnly)
.withShowInstanceInStaticContext(!first && !smart);
processJavaReference(position, ref, new ElementExtractorFilter(filter),
options, context, element -> {
if (forcedTail != null && element instanceof CommonCompletionItem commonItem) {
element = commonItem.withTail(forcedTail);
}
if (inSwitchLabel && !smart && element instanceof CommonCompletionItem commonItem) {
element = commonItem.adjustIndent();
}
//if (originalFile instanceof PsiJavaCodeReferenceCodeFragment fragment &&
// !fragment.isClassesAccepted() && item != null) {
// item.setTailType(TailTypes.noneType());
//}
//if (item instanceof JavaMethodCallElement call) {
// PsiMethod method = call.getObject();
// if (method.getTypeParameters().length > 0) {
// PsiType returned = TypeConversionUtil.erasure(method.getReturnType());
// ExpectedTypeInfo matchingExpectation = returned == null ? null : ContainerUtil.find(
// expectedTypes, info -> info.getDefaultType().isAssignableFrom(returned) ||
// AssignableFromFilter.isAcceptable(method, position, info.getDefaultType(), call.getSubstitutor()));
// if (matchingExpectation != null) {
// call.setInferenceSubstitutorFromExpectedType(position, matchingExpectation.getDefaultType());
// }
// }
//}
sink.accept(element);
CommonCompletionItem firstArrayElement = accessFirstElement(element);
if (firstArrayElement != null) {
sink.accept(firstArrayElement);
}
});
//if (context.invocationCount() > 0) {
// items.addAll(getInnerScopeVariables(parameters, position));
//}
}
private static @Nullable CommonCompletionItem accessFirstElement(ModCompletionItem item) {
if (item.contextObject() instanceof PsiLocalVariable variable) {
final PsiType type = variable.getType();
final PsiExpression expression = variable.getInitializer();
if (type instanceof PsiArrayType arrayType && expression instanceof PsiNewExpression newExpression) {
final PsiExpression[] dimensions = newExpression.getArrayDimensions();
if (dimensions.length == 1 && "1".equals(dimensions[0].getText()) && newExpression.getArrayInitializer() == null) {
final String text = variable.getName() + "[0]";
return new CommonCompletionItem(text)
.withPresentation(new ModCompletionItemPresentation(MarkupText.plainText(text))
.withMainIcon(variable.getIcon(Iconable.ICON_FLAG_VISIBILITY))
.withDetailText(JavaModCompletionUtils.typeMarkup(arrayType.getComponentType())));
}
}
}
return null;
}
private static boolean inCastContext(CompletionContext context) {
PsiElement position = context.getPosition();
PsiElement parent = getParenthesisOwner(position);
if (parent instanceof PsiTypeCastExpression) return true;
if (parent instanceof PsiParenthesizedExpression) {
return context.getOffset() == position.getTextRange().getStartOffset();
}
return false;
}
private static @Nullable PsiElement getParenthesisOwner(PsiElement position) {
PsiElement lParen = PsiTreeUtil.prevVisibleLeaf(position);
return lParen == null || !lParen.textMatches("(") ? null : lParen.getParent();
}
private static @Nullable ModNavigatorTailType getTailType(boolean smart, boolean inSwitchLabel, PsiElement position) {
if (!smart && inSwitchLabel) {
if (position instanceof PsiClass) {
return ModNavigatorTailType.insertSpaceType();
}
return JavaTailTypes.forSwitchLabel(Objects.requireNonNull(PsiTreeUtil.getParentOfType(position, PsiSwitchBlock.class)));
}
if (!smart && shouldInsertSemicolon(position)) {
return ModNavigatorTailType.semicolonType();
}
return null;
}
private static boolean shouldInsertSemicolon(PsiElement position) {
return position.getParent() instanceof PsiMethodReferenceExpression &&
JavaFrontendCompletionUtil.insertSemicolon(position.getParent().getParent());
}
private static List<PsiType> getQualifierCastTypes(PsiJavaReference javaReference, int invocationCount) {
if (javaReference instanceof PsiReferenceExpression refExpr) {
PsiExpression qualifier = refExpr.getQualifierExpression();
if (qualifier != null) {
Project project = qualifier.getProject();
return GuessManager.getInstance(project).getControlFlowExpressionTypeConjuncts(qualifier, invocationCount > 1);
}
}
return Collections.emptyList();
}
private static void processJavaReference(PsiElement element,
PsiJavaCodeReferenceElement javaReference,
ElementFilter elementFilter,
JavaCompletionProcessor.Options options,
CompletionContext parameters,
Consumer<ModCompletionItem> sink) {
Condition<String> nameCondition = s -> true;
JavaCompletionProcessor processor = new JavaCompletionProcessor(element, elementFilter, options, nameCondition);
PsiType plainQualifier = processor.getQualifierType();
List<PsiType> runtimeQualifiers = getQualifierCastTypes(javaReference, parameters.invocationCount());
if (!runtimeQualifiers.isEmpty()) {
PsiType[] conjuncts = JBIterable.of(plainQualifier).append(runtimeQualifiers).toArray(PsiType.EMPTY_ARRAY);
PsiType composite = PsiIntersectionType.createIntersection(false, conjuncts);
PsiElement ctx = JavaCompletionUtil.createContextWithXxxVariable(element, composite);
javaReference = (PsiReferenceExpression)JavaPsiFacade.getElementFactory(parameters.getProject())
.createExpressionFromText("xxx.xxx", ctx);
processor.setQualifierType(composite);
}
javaReference.processVariants(processor);
List<PsiTypeCompletionItem> castItems = ContainerUtil.map(runtimeQualifiers, q -> new PsiTypeCompletionItem(q, q.getPresentableText()));
boolean pkgContext = JavaCompletionUtil.inSomePackage(element);
PsiClass qualifierClass = PsiUtil.resolveClassInClassTypeOnly(plainQualifier);
boolean honorExcludes = qualifierClass == null || !JavaCompletionUtil.isInExcludedPackage(qualifierClass, false);
Set<PsiType> expectedTypes = ObjectUtils.coalesce(JavaCompletionUtil.getExpectedTypes(parameters), Collections.emptySet());
//Set<PsiMember> mentioned = new HashSet<>();
//JavaCompletionUtil.JavaLookupElementHighlighter highlighter = getHighlighterForPlace(element, parameters.getOriginalFile().getVirtualFile());
for (CompletionElement completionElement : processor.getResults()) {
for (ModCompletionItem item : createLookupElements(completionElement, javaReference)) {
//item.putUserData(QUALIFIER_TYPE_ATTR, plainQualifier);
Object o = item.contextObject();
if (o instanceof PsiClass psiClass) {
PsiClass specifiedQualifierClass = javaReference.isQualified() ? qualifierClass : (psiClass).getContainingClass();
if (!JavaCompletionUtil.isSourceLevelAccessible(element, psiClass, pkgContext, specifiedQualifierClass)) {
continue;
}
}
if (o instanceof PsiMember member) {
if (honorExcludes && JavaCompletionUtil.isInExcludedPackage(member, true)) {
continue;
}
//mentioned.add(CompletionUtil.getOriginalOrSelf(member));
}
PsiTypeCompletionItem qualifierCast = null;
PsiMember member = ObjectUtils.tryCast(item.contextObject(), PsiMember.class);
if (member != null) {
qualifierCast = ContainerUtil.find(castItems, c -> {
PsiType castType = c.getType();
return JavaCompletionUtil.shouldCast(plainQualifier, processor, expectedTypes, castType, member);
});
}
// TODO: support VariableCompletionItem, etc.
if (qualifierCast != null && item instanceof CommonCompletionItem cci) item = castQualifier(cci, qualifierCast);
sink.accept(item);
//set.add(highlighter.highlightIfNeeded(qualifierCast != null ? qualifierCast.getType() : plainQualifier, item, o));
}
}
//PsiElement refQualifier = javaReference.getQualifier();
//if (refQualifier == null && PsiTreeUtil.getParentOfType(element, PsiPackageStatement.class, PsiImportStatementBase.class) == null) {
// StaticMemberProcessor memberProcessor = new JavaStaticMemberProcessor(parameters);
// memberProcessor.processMembersOfRegisteredClasses(nameCondition, (member, psiClass) -> {
// if (!mentioned.contains(member) && processor.satisfies(member, ResolveState.initial())) {
// ContainerUtil.addIfNotNull(set, memberProcessor.createLookupElement(member, psiClass, true));
// }
// });
//}
//else if (refQualifier instanceof PsiSuperExpression && ((PsiSuperExpression)refQualifier).getQualifier() == null) {
// set.addAll(SuperCalls.suggestQualifyingSuperCalls(element, javaReference, elementFilter, options, nameCondition));
//}
}
private static @Unmodifiable List<ModCompletionItem> createLookupElements(CompletionElement completionElement, PsiJavaReference reference) {
Object completion = completionElement.getElement();
assert !(completion instanceof LookupElement);
if (reference instanceof PsiJavaCodeReferenceElement ref) {
if (completion instanceof PsiMethod method &&
ref.getParent() instanceof PsiImportStaticStatement) {
String parameters = PsiFormatUtil.formatMethod(method, PsiSubstitutor.EMPTY,
PsiFormatUtilBase.SHOW_PARAMETERS,
PsiFormatUtilBase.SHOW_NAME | PsiFormatUtilBase.SHOW_TYPE);
ModCompletionItemPresentation presentation =
new ModCompletionItemPresentation(
MarkupText.plainText(method.getName()).concat(parameters, MarkupText.Kind.GRAYED))
.withMainIcon(method.getIcon(Iconable.ICON_FLAG_VISIBILITY))
.withDetailText(JavaModCompletionUtils.typeMarkup(method.getReturnType()));
return List.of(new CommonCompletionItem(method.getName())
.withObject(method)
.withPresentation(presentation));
}
//if (completion instanceof PsiClass) {
// List<JavaPsiClassReferenceElement> classItems = JavaClassNameCompletionContributor.createClassLookupItems(
// CompletionUtil.getOriginalOrSelf((PsiClass)completion),
// JavaClassNameCompletionContributor.AFTER_NEW.accepts(reference),
// JavaClassNameInsertHandler.JAVA_CLASS_INSERT_HANDLER,
// Conditions.alwaysTrue());
// return JBIterable.from(classItems).flatMap(i -> JavaConstructorCallElement.wrap(i, reference.getElement()));
//}
}
PsiSubstitutor substitutor = completionElement.getSubstitutor();
if (substitutor == null) substitutor = PsiSubstitutor.EMPTY;
if (completion instanceof PsiClass cls) {
return List.of(new ClassReferenceCompletionItem(cls).withSubstitutor(substitutor));
//return JavaConstructorCallElement.wrap(classItem, reference.getElement());
}
//if (completion instanceof PsiMethod) {
// if (reference instanceof PsiMethodReferenceExpression) {
// return Collections.singleton((LookupElement)new JavaMethodReferenceElement(
// (PsiMethod)completion, (PsiMethodReferenceExpression)reference, completionElement.getMethodRefType()));
// }
//
// JavaMethodCallElement item = new JavaMethodCallElement((PsiMethod)completion).setQualifierSubstitutor(substitutor);
// item.setForcedQualifier(completionElement.getQualifierText());
// return Collections.singletonList(item);
//}
if (completion instanceof PsiVariable var) {
if (completion instanceof PsiEnumConstant enumConstant &&
PsiTreeUtil.isAncestor(enumConstant.getArgumentList(), reference.getElement(), true)) {
return List.of();
}
return List.of(new VariableCompletionItem(var).withSubstitutor(substitutor).qualifyIfNeeded(reference, null));
}
if (completion instanceof PsiPackage pkg) {
PsiElement context = reference.getElement();
PsiFile file = context.getContainingFile();
boolean inExportsOpens = context.getParent() instanceof PsiPackageAccessibilityStatement;
boolean addDot = !inExportsOpens && (!(file instanceof PsiJavaCodeReferenceCodeFragment ref) || ref.isClassesAccepted());
return List.of(new CommonCompletionItem(StringUtil.notNullize(pkg.getName()))
.withObject(pkg)
.withTail(addDot ? ModNavigatorTailType.dotType() : ModNavigatorTailType.noneType())
.withPresentation(new ModCompletionItemPresentation(MarkupText.plainText(pkg.getName()+(addDot?".":"")))
.withMainIcon(IconManager.getInstance().getPlatformIcon(PlatformIcons.Package))));
}
//
//return Collections.singletonList(LookupItemUtil.objectToLookupItem(completion));
return List.of();
}
private static CommonCompletionItem castQualifier(CommonCompletionItem item, PsiTypeCompletionItem castTypeItem) {
return item.withAdditionalUpdater((completionStart, updater) -> {
Document document = updater.getDocument();
PsiDocumentManager.getInstance(updater.getProject()).commitDocument(document);
PsiFile file = updater.getPsiFile();
PsiJavaCodeReferenceElement ref =
PsiTreeUtil.findElementOfClassAtOffset(file, completionStart, PsiJavaCodeReferenceElement.class, false);
if (ref != null) {
PsiElement qualifier = ref.getQualifier();
if (qualifier != null) {
Language lang = PsiUtilCore.getLanguageAtOffset(file, updater.getCaretOffset());
CommonCodeStyleSettings settings = CodeStyle.getLanguageSettings(file, lang);
String parenSpace = settings.SPACE_WITHIN_PARENTHESES ? " " : "";
document.insertString(qualifier.getTextRange().getEndOffset(), parenSpace + ")");
String spaceWithin = settings.SPACE_WITHIN_CAST_PARENTHESES ? " " : "";
String prefix = "(" + parenSpace + "(" + spaceWithin;
String spaceAfter = settings.SPACE_AFTER_TYPE_CAST ? " " : "";
int exprStart = qualifier.getTextRange().getStartOffset();
document.insertString(exprStart, prefix + spaceWithin + ")" + spaceAfter);
ActionContext ctx = new ActionContext(updater.getProject(), updater.getPsiFile(),
updater.getCaretOffset(), TextRange.from(updater.getCaretOffset(), 0),
null);
ModCommand command = castTypeItem.perform(ctx, ModCompletionItem.DEFAULT_INSERTION_CONTEXT);
ModCommandExecutor.getInstance().executeForFileCopy(command, updater.getPsiFile());
}
}
});
//
// @Override
// public void renderElement(@NotNull LookupElementPresentation presentation) {
// super.renderElement(presentation);
//
// presentation.appendTailText(" on " + castTypeItem.getType().getPresentableText(), true);
// }
//};
}
}
@@ -0,0 +1,270 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.completion.modcompletion;
import com.intellij.codeInsight.completion.JavaCompletionUtil;
import com.intellij.codeInsight.completion.MemberLookupHelper;
import com.intellij.codeInsight.daemon.impl.quickfix.BringVariableIntoScopeFix;
import com.intellij.codeInsight.lookup.VariableLookupItem;
import com.intellij.java.JavaBundle;
import com.intellij.modcommand.ActionContext;
import com.intellij.modcommand.ModCommandExecutor;
import com.intellij.modcommand.ModPsiUpdater;
import com.intellij.modcompletion.ModCompletionItemPresentation;
import com.intellij.modcompletion.PsiUpdateCompletionItem;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.RangeMarker;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.NlsSafe;
import com.intellij.openapi.util.text.MarkupText;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.pom.java.JavaFeature;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.controlFlow.ControlFlowUtil;
import com.intellij.psi.impl.source.PostprocessReformattingAspect;
import com.intellij.psi.impl.source.resolve.JavaResolveUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.util.containers.ContainerUtil;
import com.siyeh.ig.psiutils.JavaDeprecationUtils;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNullByDefault;
import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
import java.util.Objects;
import java.util.Set;
@NotNullByDefault
final class VariableCompletionItem extends PsiUpdateCompletionItem<PsiVariable> {
private final @Nullable MemberLookupHelper myHelper;
private final boolean myNegatable;
@NlsSafe private final @Nullable String myTailText;
private final PsiSubstitutor mySubstitutor;
private final @Nullable PsiClass myQualifierClass;
VariableCompletionItem(PsiVariable var) {
this(var, null, null, PsiSubstitutor.EMPTY, null);
}
VariableCompletionItem(PsiField field, boolean shouldImport) {
this(field, new MemberLookupHelper(field, field.getContainingClass(), shouldImport, false), null, PsiSubstitutor.EMPTY, null);
}
/**
* @param var variable to lookup
* @param tailText specific tail text to insert (initializer text is used if not specified)
*/
VariableCompletionItem(PsiVariable var, @Nls String tailText) {
this(var, null, tailText, PsiSubstitutor.EMPTY, null);
}
private VariableCompletionItem(PsiVariable var,
@Nullable MemberLookupHelper helper,
@Nullable @Nls String tailText,
PsiSubstitutor substitutor,
@Nullable PsiClass qualifierClass) {
super(Objects.requireNonNull(var.getName()), var);
myHelper = helper;
myTailText = tailText == null ? VariableLookupItem.getInitializerText(var) : tailText;
myNegatable = TypeConversionUtil.isBooleanType(var.getType());
mySubstitutor = substitutor;
myQualifierClass = qualifierClass;
}
public VariableCompletionItem withSubstitutor(PsiSubstitutor substitutor) {
return new VariableCompletionItem(contextObject(), myHelper, myTailText, substitutor, myQualifierClass);
}
@ApiStatus.Internal
public VariableCompletionItem qualifyIfNeeded(@Nullable PsiReference position, @Nullable PsiClass origClass) {
PsiVariable var = contextObject();
if (var instanceof PsiField field && !shouldImport() && VariableLookupItem.shouldQualify(field, position)) {
PsiClass aClass = field.getContainingClass();
if (aClass != null && origClass != null &&
!JavaResolveUtil.isAccessible(aClass, aClass.getContainingClass(), aClass.getModifierList(), position.getElement(), null, null) &&
JavaResolveUtil.isAccessible(origClass, origClass.getContainingClass(), origClass.getModifierList(), position.getElement(), null,
null) &&
var.isEquivalentTo(origClass.findFieldByName(var.getName(), true))) {
aClass = origClass;
}
if (aClass != null && aClass.getName() != null) {
return new VariableCompletionItem(var, myHelper, myTailText, mySubstitutor, aClass);
}
}
return this;
}
private boolean shouldImport() {
return myHelper != null && myHelper.willBeImported();
}
@Override
public Set<@NlsSafe String> additionalLookupStrings() {
if (myQualifierClass != null) {
String infix = contextObject().hasModifierProperty(PsiModifier.STATIC) ? "." : ".this.";
String suffix = infix + contextObject().getName();
return ContainerUtil.map2Set(JavaCompletionUtil.getAllLookupStrings(myQualifierClass),
s -> s + suffix);
}
return Set.of();
}
private @Nullable String getForcedQualifier() {
if (myQualifierClass == null) return null;
String className = myQualifierClass.getName();
if (className == null) return null;
return contextObject().hasModifierProperty(PsiModifier.STATIC) ? className + "." : className + ".this.";
}
@Override
public void update(ActionContext actionContext, InsertionContext insertionContext, ModPsiUpdater updater) {
PsiVariable variable = contextObject();
Document document = updater.getDocument();
PsiFile file = updater.getPsiFile();
Project project = updater.getProject();
if (variable instanceof PsiField field) {
if (shouldImport()) {
RangeMarker toDelete = JavaCompletionUtil.insertTemporary(updater.getCaretOffset(), document, " ");
PsiDocumentManager.getInstance(project).commitDocument(document);
PsiReferenceExpression ref = PsiTreeUtil.findElementOfClassAtOffset(
file, actionContext.offset(), PsiReferenceExpression.class, false);
if (ref != null) {
PsiClass containingClass = field.getContainingClass();
if (!ref.isQualified() && containingClass != null) {
ref.bindToElementViaStaticImport(containingClass);
PostprocessReformattingAspect.getInstance(project).doPostponedFormatting();
}
}
if (toDelete != null && toDelete.isValid()) {
document.deleteString(toDelete.getStartOffset(), toDelete.getEndOffset());
}
PsiDocumentManager.getInstance(project).commitDocument(document);
}
else if (VariableLookupItem.shouldQualify(field, file.findReferenceAt(updater.getCaretOffset() - 1))) {
qualifyFieldReference(actionContext.offset(), updater, field);
}
}
PsiReferenceExpression ref = PsiTreeUtil.findElementOfClassAtOffset(file, updater.getCaretOffset() - 1, PsiReferenceExpression.class, false);
if (ref != null) {
JavaCodeStyleManager.getInstance(project).shortenClassReferences(ref);
}
ref = PsiTreeUtil.findElementOfClassAtOffset(file, updater.getCaretOffset() - 1, PsiReferenceExpression.class, false);
PsiElement target = ref == null ? null : ref.resolve();
if (PsiUtil.isJvmLocalVariable(target)) {
makeFinalIfNeeded(ref, (PsiVariable)target);
}
if (target == null && ref != null &&
JavaPsiFacade.getInstance(project).getResolveHelper()
.resolveReferencedVariable(Objects.requireNonNull(variable.getName()), ref) == null) {
BringVariableIntoScopeFix fix = BringVariableIntoScopeFix.fromReference(ref);
if (fix != null) {
if (fix.getPresentation(actionContext) != null) {
ModCommandExecutor.getInstance().executeForFileCopy(fix.perform(actionContext), file);
}
}
}
//final char completionChar = insertionContext.insertionCharacter();
//if (completionChar == '=') {
// //context.setAddCompletionChar(false);
// EqTailType.INSTANCE.processTail(context.getEditor(), context.getTailOffset());
//}
//else if (completionChar == ',' && getAttribute(LookupItem.TAIL_TYPE_ATTR) != TailTypes.unknownType()) {
// //context.setAddCompletionChar(false);
// CommaTailType.INSTANCE.processTail(context.getEditor(), context.getTailOffset());
// AutoPopupController.getInstance(context.getProject()).autoPopupParameterInfo(context.getEditor(), null);
//}
//else if (completionChar == ':' && getAttribute(LookupItem.TAIL_TYPE_ATTR) != TailTypes.unknownType() && isTernaryCondition(ref)) {
// //context.setAddCompletionChar(false);
// TailTypes.conditionalExpressionColonType().processTail(context.getEditor(), context.getTailOffset());
//}
//else if (completionChar == '.') {
// AutoPopupController.getInstance(context.getProject()).scheduleAutoPopup(context.getEditor());
//}
//else if (completionChar == '!' && myNegatable) {
// //context.setAddCompletionChar(false);
// if (ref != null) {
// FeatureUsageTracker.getInstance().triggerFeatureUsed(CodeCompletionFeatures.EXCLAMATION_FINISH);
// document.insertString(ref.getTextRange().getStartOffset(), "!");
// }
//}
//else if (completionChar == Lookup.REPLACE_SELECT_CHAR) {
// removeEmptyCallParentheses(context);
//}
}
private void qualifyFieldReference(int startOffset, ModPsiUpdater updater, PsiField field) {
Document document = updater.getDocument();
PsiDocumentManager.getInstance(updater.getProject()).commitDocument(document);
PsiFile file = updater.getPsiFile();
final PsiReference reference = file.findReferenceAt(startOffset);
if (reference instanceof PsiJavaCodeReferenceElement codeRef && codeRef.isQualified()) {
return;
}
PsiClass containingClass = myQualifierClass != null && myQualifierClass.isValid() ? myQualifierClass : field.getContainingClass();
if (containingClass != null && containingClass.getName() != null) {
document.insertString(startOffset, field.hasModifierProperty(PsiModifier.STATIC) ? "." : ".this.");
JavaCompletionUtil.insertClassReference(containingClass, file, startOffset);
PsiDocumentManager.getInstance(updater.getProject()).commitDocument(document);
}
}
public static void makeFinalIfNeeded(PsiElement place, PsiVariable variable) {
if (PsiUtil.isAvailable(JavaFeature.EFFECTIVELY_FINAL, place) || JspPsiUtil.isInJspFile(place)) {
return;
}
if (ControlFlowUtil.getScopeEnforcingEffectiveFinality(variable, place) != null &&
!ControlFlowUtil.isReassigned(variable, new HashMap<>())) {
PsiUtil.setModifierProperty(variable, PsiModifier.FINAL, true);
}
}
@Override
public ModCompletionItemPresentation presentation() {
PsiVariable variable = contextObject();
String forcedQualifier = getForcedQualifier();
boolean qualify = myHelper != null && !myHelper.willBeImported() || forcedQualifier != null;
String name = Objects.requireNonNull(variable.getName());
String locationTail = "";
if (variable instanceof PsiField field) {
PsiClass containingClass = field.getContainingClass();
if (containingClass != null) {
String className = containingClass.getName();
if (qualify) {
name = (forcedQualifier != null ? forcedQualifier : className + ".") + name;
}
String qname = containingClass.getQualifiedName();
String pkg = qname == null ? "" : StringUtil.getPackageName(qname);
locationTail = StringUtil.isNotEmpty(pkg) ? " (" + pkg + ")" : "";
if (shouldImport() && StringUtil.isNotEmpty(className)) {
locationTail = JavaBundle.message("member.in.class", className) + locationTail;
}
}
}
MarkupText main = MarkupText.plainText(name);
if (JavaDeprecationUtils.isDeprecated(variable, null)) {
main = main.highlightAll(MarkupText.Kind.STRIKEOUT);
}
if (myTailText != null) {
if (myTailText.startsWith(VariableLookupItem.EQ)) {
main = main.concat(" (" + myTailText + ")", MarkupText.Kind.EMPHASIZED);
} else {
main = main.concat(myTailText, MarkupText.Kind.GRAYED);
}
}
main = main.concat(locationTail, MarkupText.Kind.GRAYED);
ModCompletionItemPresentation presentation = new ModCompletionItemPresentation(main)
.withMainIcon(variable.getIcon(0));
return presentation.withDetailText(JavaModCompletionUtils.typeMarkup(mySubstitutor.substitute(variable.getType())));
}
}
@@ -22,6 +22,7 @@ import com.intellij.psi.impl.source.PsiFieldImpl;
import com.intellij.psi.impl.source.resolve.JavaResolveUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.util.ObjectUtils;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nls;
@@ -32,7 +33,8 @@ import java.util.HashMap;
import java.util.Objects;
public class VariableLookupItem extends LookupItem<PsiVariable> implements TypedLookupItem, StaticallyImportable {
private static final String EQ = " = ";
@ApiStatus.Internal
public static final String EQ = " = ";
private final @Nullable MemberLookupHelper myHelper;
private final String myTailText;
private final boolean myNegatable;
@@ -60,7 +62,7 @@ public class VariableLookupItem extends LookupItem<PsiVariable> implements Typed
super(var, Objects.requireNonNull(var.getName()));
myHelper = helper;
myTailText = tailText == null ? getInitializerText(var) : tailText;
myNegatable = PsiTypes.booleanType().isAssignableFrom(var.getType());
myNegatable = TypeConversionUtil.isBooleanType(var.getType());
}
@ApiStatus.Internal
@@ -92,7 +94,12 @@ public class VariableLookupItem extends LookupItem<PsiVariable> implements Typed
return this;
}
private @Nullable String getInitializerText(PsiVariable var) {
/**
* @param var variable to get its initializer text
* @return the initializer text to display in completion popup; null if not applicable
*/
@ApiStatus.Internal
public static @Nullable String getInitializerText(PsiVariable var) {
if (!var.hasModifierProperty(PsiModifier.FINAL) || !var.hasModifierProperty(PsiModifier.STATIC)) return null;
if (PlainDescriptor.hasInitializationHacks(var)) return null;
@@ -294,9 +301,15 @@ public class VariableLookupItem extends LookupItem<PsiVariable> implements Typed
return shouldQualify(field, context.getFile().findReferenceAt(context.getTailOffset() - 1));
}
private static boolean shouldQualify(@NotNull PsiField field, @Nullable PsiReference context) {
if ((context instanceof PsiReferenceExpression && !((PsiReferenceExpression)context).isQualified()) ||
(context instanceof PsiJavaCodeReferenceElement && !((PsiJavaCodeReferenceElement)context).isQualified())) {
/**
* @param field field to check
* @param context context where it's about to be used
* @return whether the field access should be qualified in a given context
*/
@ApiStatus.Internal
public static boolean shouldQualify(@NotNull PsiField field, @Nullable PsiReference context) {
if ((context instanceof PsiReferenceExpression ref && !ref.isQualified()) ||
(context instanceof PsiJavaCodeReferenceElement codeRef && !codeRef.isQualified())) {
PsiElement element = context.getElement();
if (isEnumInSwitch(field, element)) return false;
PsiVariable target = JavaPsiFacade.getInstance(element.getProject()).getResolveHelper()
@@ -1,11 +1,11 @@
package bar;
public class Outer {
private static final String SOME_CONSTANT = "someConstant";
public static void main(String[] args) {
String someString = SO<caret>;
switch(args[0]) {
case SO<caret> -> System.out.println(someString);
private static final String SOME_CONSTANT = "someConstant";
public static void main(String[] args) {
String someString = SO<caret>;
switch(args[0]) {
case SO<caret> -> System.out.println(someString);
}
}
}
}
@@ -1,11 +1,11 @@
package bar;
public class Outer {
private static final String SOME_CONSTANT = "someConstant";
public static void main(String[] args) {
String someString = SOME_CONSTANT<caret>;
switch(args[0]) {
case SOME_CONSTANT<caret> -> System.out.println(someString);
private static final String SOME_CONSTANT = "someConstant";
public static void main(String[] args) {
String someString = SOME_CONSTANT<caret>;
switch(args[0]) {
case SOME_CONSTANT<caret> -> System.out.println(someString);
}
}
}
}
@@ -354,11 +354,11 @@ public class KeywordCompletionTest extends LightCompletionTestCase {
checkResultByTestName();
}
private void configureByTestName() {
void configureByTestName() {
configureByFile(BASE_PATH + getTestName(true) + ".java");
}
private void checkResultByTestName() {
void checkResultByTestName() {
checkResultByFile(BASE_PATH + getTestName(true) + "_after.java");
}
@@ -380,6 +380,16 @@ public class KeywordCompletionTest extends LightCompletionTestCase {
Registry.get("ide.completion.modcommand").setValue(true, getTestRootDisposable());
}
@Override
public void testNoExtraArrowMultiCaret() {
configureByTestName();
// Intermittently no-variants delegator appears here adding a second completion item
// ResourceBundle (as it contains 'SO'), and now no-variants delegators
// gets no information that we have no other variants.
selectItem(myItems[0]);
checkResultByTestName();
}
@Override
public void testInstanceofNegation() {
// skip; TODO: support custom completion chars
@@ -2003,3 +2003,4 @@ todo.item=todo item
intention.rename.underscore.name=Rename {0} of type ''{1}''
intention.rename.underscore.popup.title=Select Declaration
intention.rename.underscore.family.name=Rename unnamed variable
member.in.class=\ in {0}
@@ -1,6 +1,7 @@
*a:com.intellij.codeInsight.ModNavigatorTailType
- com.intellij.codeInsight.TailType
- <init>():V
- s:dotType():com.intellij.codeInsight.ModNavigatorTailType
- s:humbleSpaceBeforeWordType():com.intellij.codeInsight.ModNavigatorTailType
- s:insertSpaceType():com.intellij.codeInsight.ModNavigatorTailType
- isApplicable(com.intellij.codeInsight.completion.InsertionContext):Z
@@ -72,6 +72,15 @@ public abstract class ModNavigatorTailType extends TailType {
return (ModNavigatorTailType)TailTypes.spaceType();
}
/**
* @return tail type that inserts a dot
*/
@Contract(pure = true)
public static @NotNull ModNavigatorTailType dotType() {
return (ModNavigatorTailType)TailTypes.dotType();
}
/**
* @return tail type that insert a space unless there's one at the caret position already, followed by a word or '@'
*/