mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
java completion: qualify calls to outer class methods when needed (IDEA-144816, IDEA-151237)
This commit is contained in:
@@ -289,13 +289,8 @@ public class JavaCompletionUtil {
|
||||
private static Set<LookupElement> processJavaQualifiedReference(PsiElement element, PsiJavaReference javaReference, ElementFilter elementFilter,
|
||||
JavaCompletionProcessor.Options options,
|
||||
final PrefixMatcher matcher, CompletionParameters parameters) {
|
||||
final Set<LookupElement> set = new LinkedHashSet<LookupElement>();
|
||||
final Condition<String> nameCondition = new Condition<String>() {
|
||||
@Override
|
||||
public boolean value(String s) {
|
||||
return matcher.prefixMatches(s);
|
||||
}
|
||||
};
|
||||
final Set<LookupElement> set = new LinkedHashSet<>();
|
||||
final Condition<String> nameCondition = matcher::prefixMatches;
|
||||
|
||||
PsiMethodCallExpression call = PsiTreeUtil.getParentOfType(element, PsiMethodCallExpression.class);
|
||||
boolean checkInitialized = parameters.getInvocationCount() <= 1 && call != null && PsiKeyword.SUPER.equals(call.getMethodExpression().getText());
|
||||
@@ -322,9 +317,9 @@ public class JavaCompletionUtil {
|
||||
PsiClass qualifierClass = PsiUtil.resolveClassInClassTypeOnly(qualifierType);
|
||||
final boolean honorExcludes = qualifierClass == null || !isInExcludedPackage(qualifierClass, false);
|
||||
|
||||
final Set<PsiMember> mentioned = new THashSet<PsiMember>();
|
||||
final Set<PsiMember> mentioned = new THashSet<>();
|
||||
for (CompletionElement completionElement : processor.getResults()) {
|
||||
for (LookupElement item : createLookupElements(completionElement, javaReference)) {
|
||||
for (LookupElement item : createLookupElements(completionElement, javaReference, processor)) {
|
||||
item.putUserData(QUALIFIER_TYPE_ATTR, qualifierType);
|
||||
final Object o = item.getObject();
|
||||
if (o instanceof PsiClass && !isSourceLevelAccessible(element, (PsiClass)o, pkgContext)) {
|
||||
@@ -528,7 +523,7 @@ public class JavaCompletionUtil {
|
||||
return false;
|
||||
}
|
||||
|
||||
static List<? extends LookupElement> createLookupElements(CompletionElement completionElement, PsiJavaReference reference) {
|
||||
static List<? extends LookupElement> createLookupElements(CompletionElement completionElement, PsiJavaReference reference, JavaCompletionProcessor processor) {
|
||||
Object completion = completionElement.getElement();
|
||||
assert !(completion instanceof LookupElement);
|
||||
|
||||
@@ -556,7 +551,16 @@ public class JavaCompletionUtil {
|
||||
return Collections.singletonList(JavaClassNameCompletionContributor.createClassLookupItem((PsiClass)completion, true).setSubstitutor(substitutor));
|
||||
}
|
||||
if (completion instanceof PsiMethod) {
|
||||
return Collections.singletonList(new JavaMethodCallElement((PsiMethod)completion).setQualifierSubstitutor(substitutor));
|
||||
PsiMethod method = (PsiMethod)completion;
|
||||
JavaMethodCallElement item = new JavaMethodCallElement(method).setQualifierSubstitutor(substitutor);
|
||||
if (processor.shouldQualifyMethodCall(method)) {
|
||||
PsiClass containingClass = method.getContainingClass();
|
||||
String className = containingClass == null ? null : containingClass.getName();
|
||||
if (className != null) {
|
||||
item.setForcedQualifier(className + (method.hasModifierProperty(PsiModifier.STATIC) ? "." : ".this."));
|
||||
}
|
||||
}
|
||||
return Collections.singletonList(item);
|
||||
}
|
||||
if (completion instanceof PsiVariable) {
|
||||
return Collections.singletonList(new VariableLookupItem((PsiVariable)completion).setSubstitutor(substitutor));
|
||||
|
||||
@@ -40,6 +40,7 @@ public class JavaMethodCallElement extends LookupItem<PsiMethod> implements Type
|
||||
private PsiSubstitutor myQualifierSubstitutor = PsiSubstitutor.EMPTY;
|
||||
private PsiSubstitutor myInferenceSubstitutor = PsiSubstitutor.EMPTY;
|
||||
private boolean myMayNeedExplicitTypeParameters;
|
||||
private String myForcedQualifier = "";
|
||||
|
||||
public JavaMethodCallElement(@NotNull PsiMethod method) {
|
||||
this(method, method.getName());
|
||||
@@ -67,6 +68,11 @@ public class JavaMethodCallElement extends LookupItem<PsiMethod> implements Type
|
||||
}
|
||||
}
|
||||
|
||||
void setForcedQualifier(@NotNull String forcedQualifier) {
|
||||
myForcedQualifier = forcedQualifier;
|
||||
setLookupString(forcedQualifier + getLookupString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public PsiType getType() {
|
||||
return getSubstitutor().substitute(getInferenceSubstitutor().substitute(getObject().getReturnType()));
|
||||
@@ -255,6 +261,9 @@ public class JavaMethodCallElement extends LookupItem<PsiMethod> implements Type
|
||||
|
||||
MemberLookupHelper helper = myHelper != null ? myHelper : new MemberLookupHelper(myMethod, myContainingClass, false, false);
|
||||
helper.renderElement(presentation, myHelper != null, myHelper != null && !myHelper.willBeImported(), getSubstitutor());
|
||||
if (!myForcedQualifier.isEmpty()) {
|
||||
presentation.setItemText(myForcedQualifier + presentation.getItemText());
|
||||
}
|
||||
|
||||
if (shouldInsertTypeParameters()) {
|
||||
String typeParamsText = getTypeParamsText(true);
|
||||
|
||||
@@ -48,7 +48,7 @@ class SuperCalls {
|
||||
fakeSuper.processVariants(superProcessor);
|
||||
|
||||
for (CompletionElement completionElement : superProcessor.getResults()) {
|
||||
for (LookupElement item : JavaCompletionUtil.createLookupElements(completionElement, javaReference)) {
|
||||
for (LookupElement item : JavaCompletionUtil.createLookupElements(completionElement, javaReference, superProcessor)) {
|
||||
set.add(withQualifiedSuper(className, item));
|
||||
}
|
||||
}
|
||||
|
||||
+22
-9
@@ -56,9 +56,12 @@ public class JavaCompletionProcessor extends BaseScopeProcessor implements Eleme
|
||||
private final boolean myInJavaDoc;
|
||||
private boolean myStatic = false;
|
||||
private PsiElement myDeclarationHolder = null;
|
||||
private final Map<CompletionElement, CompletionElement> myResults = new LinkedHashMap<CompletionElement, CompletionElement>();
|
||||
private final Map<CompletionElement, CompletionElement> myResults = new LinkedHashMap<>();
|
||||
private final Set<CompletionElement> mySecondRateResults = ContainerUtil.newIdentityTroveSet();
|
||||
private final Set<String> myShadowedNames = ContainerUtil.newHashSet();
|
||||
private final Set<String> myCurrentScopeMethodNames = ContainerUtil.newHashSet();
|
||||
private final Set<String> myFinishedScopesMethodNames = ContainerUtil.newHashSet();
|
||||
private final Set<PsiMethod> myMethodsToQualify = ContainerUtil.newHashSet();
|
||||
private final PsiElement myElement;
|
||||
private final PsiElement myScope;
|
||||
private final ElementFilter myFilter;
|
||||
@@ -68,7 +71,7 @@ public class JavaCompletionProcessor extends BaseScopeProcessor implements Eleme
|
||||
private PsiClass myQualifierClass = null;
|
||||
private final Condition<String> myMatcher;
|
||||
private final Options myOptions;
|
||||
private final Set<PsiField> myNonInitializedFields = new HashSet<PsiField>();
|
||||
private final Set<PsiField> myNonInitializedFields = new HashSet<>();
|
||||
private final boolean myAllowStaticWithInstanceQualifier;
|
||||
|
||||
public JavaCompletionProcessor(@NotNull PsiElement element, ElementFilter filter, Options options, @NotNull Condition<String> nameCondition) {
|
||||
@@ -135,6 +138,7 @@ public class JavaCompletionProcessor extends BaseScopeProcessor implements Eleme
|
||||
|
||||
public static Set<PsiField> getNonInitializedFields(PsiElement element) {
|
||||
final PsiStatement statement = PsiTreeUtil.getParentOfType(element, PsiStatement.class);
|
||||
//noinspection SSBasedInspection
|
||||
final PsiMethod method = PsiTreeUtil.getParentOfType(element, PsiMethod.class, true, PsiClass.class);
|
||||
if (statement == null || method == null || !method.isConstructor()) {
|
||||
return Collections.emptySet();
|
||||
@@ -152,7 +156,7 @@ public class JavaCompletionProcessor extends BaseScopeProcessor implements Eleme
|
||||
parent = next;
|
||||
}
|
||||
|
||||
final Set<PsiField> fields = new HashSet<PsiField>();
|
||||
final Set<PsiField> fields = new HashSet<>();
|
||||
final PsiClass containingClass = method.getContainingClass();
|
||||
assert containingClass != null;
|
||||
for (PsiField field : containingClass.getFields()) {
|
||||
@@ -195,6 +199,8 @@ public class JavaCompletionProcessor extends BaseScopeProcessor implements Eleme
|
||||
}
|
||||
if(event == JavaScopeProcessorEvent.CHANGE_LEVEL){
|
||||
myMembersFlag = true;
|
||||
myFinishedScopesMethodNames.addAll(myCurrentScopeMethodNames);
|
||||
myCurrentScopeMethodNames.clear();
|
||||
}
|
||||
if (event == JavaScopeProcessorEvent.SET_CURRENT_FILE_CONTEXT) {
|
||||
myDeclarationHolder = (PsiElement)associated;
|
||||
@@ -251,11 +257,23 @@ public class JavaCompletionProcessor extends BaseScopeProcessor implements Eleme
|
||||
if (sp == StaticProblem.staticAfterInstance) {
|
||||
mySecondRateResults.add(completion);
|
||||
}
|
||||
|
||||
if (element instanceof PsiMethod) {
|
||||
String name = ((PsiMethod)element).getName();
|
||||
myCurrentScopeMethodNames.add(name);
|
||||
if (myFinishedScopesMethodNames.contains(name)) {
|
||||
myMethodsToQualify.add((PsiMethod)element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean shouldQualifyMethodCall(@NotNull PsiMethod method) {
|
||||
return myMethodsToQualify.contains(method);
|
||||
}
|
||||
|
||||
private boolean isQualifiedContext() {
|
||||
final PsiElement elementParent = myElement.getParent();
|
||||
return elementParent instanceof PsiQualifiedReference && ((PsiQualifiedReference)elementParent).getQualifier() != null;
|
||||
@@ -331,12 +349,7 @@ public class JavaCompletionProcessor extends BaseScopeProcessor implements Eleme
|
||||
if (mySecondRateResults.size() == myResults.size()) {
|
||||
return mySecondRateResults;
|
||||
}
|
||||
return ContainerUtil.filter(myResults.values(), new Condition<CompletionElement>() {
|
||||
@Override
|
||||
public boolean value(CompletionElement element) {
|
||||
return !mySecondRateResults.contains(element);
|
||||
}
|
||||
});
|
||||
return ContainerUtil.filter(myResults.values(), element -> !mySecondRateResults.contains(element));
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
class A {
|
||||
void foo(int x){}
|
||||
class Inner {
|
||||
void foo(){}
|
||||
|
||||
void test() {
|
||||
fo<caret>
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
class A {
|
||||
void foo(int x){}
|
||||
class Inner {
|
||||
void foo(){}
|
||||
|
||||
void test() {
|
||||
A.this.foo(<caret>);
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
class A {
|
||||
static void foo(int x){}
|
||||
class Inner {
|
||||
void foo(){}
|
||||
|
||||
void test() {
|
||||
fo<caret>
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
class A {
|
||||
static void foo(int x){}
|
||||
class Inner {
|
||||
void foo(){}
|
||||
|
||||
void test() {
|
||||
A.foo(<caret>);
|
||||
}
|
||||
}
|
||||
}
|
||||
+16
@@ -482,6 +482,22 @@ public class NormalCompletionTest extends LightFixtureCompletionTestCase {
|
||||
public void testMethodCallAfterFinally() { doTest() }
|
||||
public void testPrivateInAnonymous() throws Throwable { doTest() }
|
||||
|
||||
public void testStaticMethodFromOuterClass() {
|
||||
configure()
|
||||
assertStringItems 'foo', 'A.foo', 'for'
|
||||
assert LookupElementPresentation.renderElement(myItems[1]).itemText == 'A.foo'
|
||||
selectItem(myItems[1])
|
||||
checkResult()
|
||||
}
|
||||
|
||||
public void testInstanceMethodFromOuterClass() {
|
||||
configure()
|
||||
assertStringItems 'foo', 'A.this.foo', 'for'
|
||||
assert LookupElementPresentation.renderElement(myItems[1]).itemText == 'A.this.foo'
|
||||
selectItem(myItems[1])
|
||||
checkResult()
|
||||
}
|
||||
|
||||
public void testMethodParenthesesSpaces() throws Throwable {
|
||||
codeStyleSettings.SPACE_BEFORE_METHOD_CALL_PARENTHESES = true
|
||||
codeStyleSettings.SPACE_WITHIN_METHOD_CALL_PARENTHESES = true
|
||||
|
||||
Reference in New Issue
Block a user