KT-1151 Code completion for not imported extension functions - refactorings:

- Don't touch call resolver
- Check only newly imported descriptors
- Use Stepa's wrapper for getting boolean attribute from annotation
This commit is contained in:
Nikolay Krasko
2012-02-29 15:43:56 +04:00
parent a086e3985e
commit 8d1c3dc759
10 changed files with 132 additions and 278 deletions
@@ -25,10 +25,12 @@ import com.intellij.psi.search.PsiShortNamesCache;
import com.intellij.psi.util.PsiTreeUtil;
import jet.runtime.typeinfo.JetValueParameter;
import org.jetbrains.jet.lang.resolve.java.JvmAbi;
import org.jetbrains.jet.lang.resolve.java.kt.JetValueParameterAnnotation;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
/**
* Get jet declarations from java that could be used in completion. Unlike the real jet resolver this helper is allowed
@@ -52,7 +54,7 @@ class JetFromJavaDescriptorHelper {
* Get names that could have jet descriptor equivalents. It could be inaccurate and return more results than necessary.
*/
static Collection<String> getPossiblePackageDeclarationsNames(Project project, GlobalSearchScope scope) {
final ArrayList<String> result = new ArrayList<String>();
Collection<String> result = new ArrayList<String>();
for (PsiClass jetNamespaceClass : getClassesForJetNamespaces(project, scope)) {
for (PsiMethod psiMethod : jetNamespaceClass.getMethods()) {
@@ -69,7 +71,7 @@ class JetFromJavaDescriptorHelper {
// Extension function should have an parameter of type JetValueParameter with explicit receiver parameter.
HashSet<String> extensionNames = new HashSet<String>();
Set<String> extensionNames = new HashSet<String>();
Collection<PsiAnnotation> valueParametersAnnotations = JavaAnnotationIndex.getInstance().get(
JetValueParameter.class.getSimpleName(), project, scope);
@@ -81,7 +83,7 @@ class JetFromJavaDescriptorHelper {
continue;
}
if (!getAnnotationAttribute(parameterAnnotation, "receiver", false)) {
if (!new JetValueParameterAnnotation(parameterAnnotation).receiver()) {
continue;
}
@@ -96,7 +98,7 @@ class JetFromJavaDescriptorHelper {
static Collection<PsiMethod> getTopExtensionFunctionByName(String name, Project project, GlobalSearchScope scope) {
HashSet<PsiMethod> selectedMethods = new HashSet<PsiMethod>();
Set<PsiMethod> selectedMethods = new HashSet<PsiMethod>();
Collection<PsiMethod> psiMethods = JavaMethodNameIndex.getInstance().get(name, project, scope);
for (PsiMethod psiMethod : psiMethods) {
@@ -119,7 +121,7 @@ class JetFromJavaDescriptorHelper {
continue;
}
if (getAnnotationAttribute(psiAnnotation, "receiver", false)) {
if (new JetValueParameterAnnotation(psiAnnotation).receiver()) {
selectedMethods.add(psiMethod);
}
}
@@ -129,20 +131,4 @@ class JetFromJavaDescriptorHelper {
return selectedMethods;
}
private static boolean getAnnotationAttribute(PsiAnnotation annotation, String attributeName, boolean defaultValue) {
// Check that parameter is receiver
PsiAnnotationMemberValue attributeValue = annotation.findAttributeValue(attributeName);
if (!(attributeValue instanceof PsiLiteralExpression)) {
return defaultValue;
}
// Every extension function will have parameter marked with attribute where receiver == true
Object value = ((PsiLiteralExpression) attributeValue).getValue();
if (value == null) {
return defaultValue;
}
return (Boolean) value;
}
}
@@ -68,7 +68,7 @@ public class JetShortNamesCache extends PsiShortNamesCache {
@NotNull
@Override
public String[] getAllClassNames() {
final Collection<String> classNames = JetShortClassNameIndex.getInstance().getAllKeys(project);
Collection<String> classNames = JetShortClassNameIndex.getInstance().getAllKeys(project);
return ArrayUtil.toStringArray(classNames);
}
@@ -77,10 +77,10 @@ public class JetShortNamesCache extends PsiShortNamesCache {
*/
@NotNull
@Override
public PsiClass[] getClassesByName(@NotNull @NonNls final String name, @NotNull GlobalSearchScope scope) {
public PsiClass[] getClassesByName(@NotNull @NonNls String name, @NotNull GlobalSearchScope scope) {
// Quick check for classes from getAllClassNames()
final Collection<String> classNames = JetShortClassNameIndex.getInstance().getAllKeys(project);
Collection<String> classNames = JetShortClassNameIndex.getInstance().getAllKeys(project);
if (!classNames.contains(name)) {
return new PsiClass[0];
}
@@ -89,7 +89,7 @@ public class JetShortNamesCache extends PsiShortNamesCache {
for (String fqName : JetFullClassNameIndex.getInstance().getAllKeys(project)) {
if (QualifiedNamesUtil.fqnToShortName(fqName).equals(name)) {
final PsiClass psiClass = javaElementFinder.findClass(fqName, scope);
PsiClass psiClass = javaElementFinder.findClass(fqName, scope);
if (psiClass != null) {
result.add(psiClass);
}
@@ -111,7 +111,7 @@ public class JetShortNamesCache extends PsiShortNamesCache {
@NotNull
public Collection<String> getFQNamesByName(@NotNull final String name, @NotNull GlobalSearchScope scope) {
final BindingContext context = getResolutionContext(scope);
BindingContext context = getResolutionContext(scope);
return Collections2.filter(context.getKeys(BindingContext.FQNAME_TO_CLASS_DESCRIPTOR), new Predicate<String>() {
@Override
public boolean apply(@Nullable String fqName) {
@@ -128,26 +128,24 @@ public class JetShortNamesCache extends PsiShortNamesCache {
*/
@NotNull
public Collection<String> getAllTopLevelFunctionNames() {
final HashSet<String> functionNames = new HashSet<String>();
Set<String> functionNames = new HashSet<String>();
functionNames.addAll(JetShortFunctionNameIndex.getInstance().getAllKeys(project));
functionNames.addAll(JetFromJavaDescriptorHelper.getPossiblePackageDeclarationsNames(project, GlobalSearchScope.allScope(project)));
return functionNames;
}
@NotNull
public Collection<SimpleFunctionDescriptor> getTopLevelFunctionDescriptorsByName(final @NotNull String name,
final @NotNull GlobalSearchScope scope) {
public Collection<SimpleFunctionDescriptor> getTopLevelFunctionDescriptorsByName(@NotNull String name,
@NotNull GlobalSearchScope scope) {
final Collection<JetNamedFunction> jetNamedFunctions = JetShortFunctionNameIndex.getInstance().get(name, project, scope);
Collection<JetNamedFunction> jetNamedFunctions = JetShortFunctionNameIndex.getInstance().get(name, project, scope);
final BindingContext context = getResolutionContext(scope);
BindingContext context = getResolutionContext(scope);
final HashSet<SimpleFunctionDescriptor> result = new HashSet<SimpleFunctionDescriptor>();
HashSet<SimpleFunctionDescriptor> result = new HashSet<SimpleFunctionDescriptor>();
for (JetNamedFunction jetNamedFunction : jetNamedFunctions) {
final SimpleFunctionDescriptor functionDescriptor = context.get(BindingContext.FUNCTION, jetNamedFunction);
SimpleFunctionDescriptor functionDescriptor = context.get(BindingContext.FUNCTION, jetNamedFunction);
if (functionDescriptor != null) {
result.add(functionDescriptor);
}
@@ -157,11 +155,11 @@ public class JetShortNamesCache extends PsiShortNamesCache {
}
@NotNull
public BindingContext getResolutionContext(final @NotNull GlobalSearchScope scope) {
public BindingContext getResolutionContext(@NotNull GlobalSearchScope scope) {
return WholeProjectAnalyzerFacade.analyzeProjectWithCache(project, scope);
}
public Collection<JetNamedFunction> getTopLevelFunctionsByName(final @NotNull String name, @NotNull GlobalSearchScope scope) {
public Collection<JetNamedFunction> getTopLevelFunctionsByName(@NotNull String name, @NotNull GlobalSearchScope scope) {
return JetShortFunctionNameIndex.getInstance().get(name, project, scope);
}
@@ -173,7 +171,7 @@ public class JetShortNamesCache extends PsiShortNamesCache {
*/
@NotNull
public Collection<String> getAllJetExtensionFunctionsNames(@NotNull GlobalSearchScope scope) {
final Set<String> extensionFunctionNames = new HashSet<String>();
Set<String> extensionFunctionNames = new HashSet<String>();
extensionFunctionNames.addAll(JetExtensionFunctionNameIndex.getInstance().getAllKeys(project));
extensionFunctionNames.addAll(JetFromJavaDescriptorHelper.getTopExtensionFunctionNames(project, scope));
@@ -181,7 +179,7 @@ public class JetShortNamesCache extends PsiShortNamesCache {
return extensionFunctionNames;
}
public Collection<PsiElement> getJetExtensionFunctionsByName(final @NotNull String name, @NotNull GlobalSearchScope scope) {
public Collection<PsiElement> getJetExtensionFunctionsByName(@NotNull String name, @NotNull GlobalSearchScope scope) {
HashSet<PsiElement> functions = new HashSet<PsiElement>();
functions.addAll(JetExtensionFunctionNameIndex.getInstance().get(name, project, scope));
functions.addAll(JetFromJavaDescriptorHelper.getTopExtensionFunctionByName(name, project, scope));
@@ -189,29 +187,6 @@ public class JetShortNamesCache extends PsiShortNamesCache {
return functions;
}
// public Collection<NamedFunctionDescriptor> getAllJetExtensionFunctionsByName(@NotNull String name, @NotNull GlobalSearchScope scope) {
// // TODO: Add jet extension functions in jar-dependencies (those functions are missing in BindingContext and stubs)
//
// final Collection<JetNamedFunction> jetNamedFunctions = JetShortFunctionNameIndex.getInstance().get(name, project, scope);
//
// final BindingContext context = getResolutionContext(scope);
//
// final HashSet<NamedFunctionDescriptor> result = new HashSet<NamedFunctionDescriptor>();
//
// for (JetNamedFunction jetNamedFunction : jetNamedFunctions) {
// final NamedFunctionDescriptor functionDescriptor = context.get(BindingContext.FUNCTION, jetNamedFunction);
// if (functionDescriptor != null) {
// if (functionDescriptor.getContainingDeclaration() instanceof NamespaceDescriptor) {
// if (functionDescriptor.getExpectedThisObject() != ReceiverDescriptor.NO_RECEIVER) {
// result.add(functionDescriptor);
// }
// }
// }
// }
//
// return result;
// }
public Collection<JetNamedFunction> getJetFunctionsByName(@NonNls @NotNull String name, @NotNull GlobalSearchScope scope) {
return JetShortFunctionNameIndex.getInstance().get(name, project, scope);
}
@@ -31,6 +31,7 @@ import com.intellij.util.Consumer;
import com.intellij.util.ProcessingContext;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.CallableDescriptor;
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.BindingContext;
@@ -46,7 +47,7 @@ import org.jetbrains.jet.util.QualifiedNamesUtil;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* @author Nikolay Krasko
@@ -57,20 +58,20 @@ public class JetCompletionContributor extends CompletionContributor {
new CompletionProvider<CompletionParameters>() {
@Override
protected void addCompletions(@NotNull CompletionParameters parameters, ProcessingContext context,
final @NotNull CompletionResultSet result) {
@NotNull CompletionResultSet result) {
final HashSet<LookupPositionObject> positions = new HashSet<LookupPositionObject>();
Set<LookupPositionObject> positions = new HashSet<LookupPositionObject>();
if (result.getPrefixMatcher().getPrefix().isEmpty()) {
return;
}
final PsiElement position = parameters.getPosition();
PsiElement position = parameters.getPosition();
if (!(position.getContainingFile() instanceof JetFile)) {
return;
}
final JetSimpleNameReference jetReference = getJetReference(parameters);
JetSimpleNameReference jetReference = getJetReference(parameters);
if (jetReference != null) {
for (Object variant : jetReference.getVariants()) {
addReferenceVariant(result, variant, positions);
@@ -88,6 +89,7 @@ public class JetCompletionContributor extends CompletionContributor {
});
}
// TODO: Make it work for properties
private static void addJetExtensionFunctions(JetSimpleNameExpression expression, CompletionResultSet result, PsiElement position) {
BindingContext context = WholeProjectAnalyzerFacade.analyzeProjectWithCacheOnAFile((JetFile) position.getContainingFile());
@@ -95,15 +97,15 @@ public class JetCompletionContributor extends CompletionContributor {
if (receiverExpression != null) {
final JetType expressionType = context.get(BindingContext.EXPRESSION_TYPE, receiverExpression);
final JetScope scope = context.get(BindingContext.RESOLUTION_SCOPE, receiverExpression);
JetType expressionType = context.get(BindingContext.EXPRESSION_TYPE, receiverExpression);
JetScope scope = context.get(BindingContext.RESOLUTION_SCOPE, receiverExpression);
if (expressionType != null && scope != null) {
JetShortNamesCache namesCache = JetCacheManager.getInstance(position.getProject()).getNamesCache();
Collection<String> extensionFunctionsNames = namesCache.getAllJetExtensionFunctionsNames(
GlobalSearchScope.allScope(position.getProject()));
HashSet<String> functionFQNs = new HashSet<String>();
Set<String> functionFQNs = new HashSet<String>();
// Collect all possible extension function qualified names
for (String name : extensionFunctionsNames) {
@@ -114,15 +116,16 @@ public class JetCompletionContributor extends CompletionContributor {
for (PsiElement extensionFunction : extensionFunctions) {
if (extensionFunction instanceof JetNamedFunction) {
functionFQNs.add(JetPsiUtil.getFQName((JetNamedFunction) extensionFunction));
} else if (extensionFunction instanceof PsiMethod) {
final PsiMethod function = (PsiMethod) extensionFunction;
final PsiClass containingClass = function.getContainingClass();
}
else if (extensionFunction instanceof PsiMethod) {
PsiMethod function = (PsiMethod) extensionFunction;
PsiClass containingClass = function.getContainingClass();
if (containingClass != null) {
final String classFQN = containingClass.getQualifiedName();
String classFQN = containingClass.getQualifiedName();
if (classFQN != null) {
final String classParentFQN = QualifiedNamesUtil.withoutLastSegment(classFQN);
String classParentFQN = QualifiedNamesUtil.withoutLastSegment(classFQN);
functionFQNs.add(QualifiedNamesUtil.combine(classParentFQN, function.getName()));
}
}
@@ -133,17 +136,9 @@ public class JetCompletionContributor extends CompletionContributor {
// Iterate through the function with attempt to resolve found functions
for (String functionFQN : functionFQNs) {
// System.out.println(functionFQN);
List<FunctionDescriptor> functionDescriptors = ExpressionTypingUtils.canFindSuitableCall(
functionFQN, position.getProject(), receiverExpression, expressionType, scope);
// System.out.println(!functionDescriptors.isEmpty());
if (!functionDescriptors.isEmpty()) {
for (FunctionDescriptor functionDescriptor : functionDescriptors) {
result.addElement(DescriptorLookupConverter.createLookupElement(context, functionDescriptor));
}
for (CallableDescriptor functionDescriptor : ExpressionTypingUtils.canFindSuitableCall(
functionFQN, position.getProject(), receiverExpression, expressionType, scope)) {
result.addElement(DescriptorLookupConverter.createLookupElement(context, functionDescriptor));
}
}
}
@@ -153,7 +148,7 @@ public class JetCompletionContributor extends CompletionContributor {
private static void addReferenceVariant(
@NotNull CompletionResultSet result,
@NotNull Object variant,
@NotNull final HashSet<LookupPositionObject> positions) {
@NotNull Set<LookupPositionObject> positions) {
if (variant instanceof LookupElement) {
addCompletionToResult(result, (LookupElement) variant, positions);
@@ -164,15 +159,15 @@ public class JetCompletionContributor extends CompletionContributor {
}
private static void addJetTopLevelFunctions(@NotNull CompletionResultSet result, @NotNull PsiElement position,
@NotNull final HashSet<LookupPositionObject> positions) {
@NotNull Set<LookupPositionObject> positions) {
String actualPrefix = result.getPrefixMatcher().getPrefix();
final Project project = position.getProject();
Project project = position.getProject();
final JetShortNamesCache namesCache = JetCacheManager.getInstance(position.getProject()).getNamesCache();
final GlobalSearchScope scope = GlobalSearchScope.allScope(project);
final Collection<String> functionNames = namesCache.getAllTopLevelFunctionNames();
JetShortNamesCache namesCache = JetCacheManager.getInstance(position.getProject()).getNamesCache();
GlobalSearchScope scope = GlobalSearchScope.allScope(project);
Collection<String> functionNames = namesCache.getAllTopLevelFunctionNames();
BindingContext resolutionContext = namesCache.getResolutionContext(scope);
@@ -189,9 +184,9 @@ public class JetCompletionContributor extends CompletionContributor {
* Jet classes will be added as java completions for unification
*/
private static void addClasses(
@NotNull final CompletionParameters parameters,
@NotNull CompletionParameters parameters,
@NotNull final CompletionResultSet result,
@NotNull final HashSet<LookupPositionObject> positions) {
@NotNull final Set<LookupPositionObject> positions) {
JetClassCompletionContributor.addClasses(parameters, result, new Consumer<LookupElement>() {
@Override
@@ -202,7 +197,7 @@ public class JetCompletionContributor extends CompletionContributor {
}
private static boolean shouldRunTopLevelCompletion(@NotNull CompletionParameters parameters) {
final PsiElement element = parameters.getPosition();
PsiElement element = parameters.getPosition();
if (parameters.getInvocationCount() > 1) {
return true;
@@ -226,9 +221,9 @@ public class JetCompletionContributor extends CompletionContributor {
@Nullable
private static JetSimpleNameReference getJetReference(@NotNull CompletionParameters parameters) {
final PsiElement element = parameters.getPosition();
PsiElement element = parameters.getPosition();
if (element.getParent() != null) {
final PsiElement parent = element.getParent();
PsiElement parent = element.getParent();
PsiReference[] references = parent.getReferences();
if (references.length != 0) {
@@ -244,11 +239,11 @@ public class JetCompletionContributor extends CompletionContributor {
}
private static void addCompletionToResult(
@NotNull final CompletionResultSet result,
@NotNull CompletionResultSet result,
@NotNull LookupElement element,
@NotNull HashSet<LookupPositionObject> positions) {
@NotNull Set<LookupPositionObject> positions) {
final LookupPositionObject lookupPosition = getLookupPosition(element);
LookupPositionObject lookupPosition = getLookupPosition(element);
if (lookupPosition != null) {
if (!positions.contains(lookupPosition)) {
positions.add(lookupPosition);
@@ -263,14 +258,14 @@ public class JetCompletionContributor extends CompletionContributor {
}
private static LookupPositionObject getLookupPosition(LookupElement element) {
final Object lookupObject = element.getObject();
Object lookupObject = element.getObject();
if (lookupObject instanceof PsiElement) {
// PsiElement psiElement = (PsiElement) lookupObject;
return new LookupPositionObject((PsiElement) lookupObject);
}
else if (lookupObject instanceof JetLookupObject) {
// JetLookupObject jetLookupObject = (JetLookupObject) lookupObject;
final PsiElement psiElement = ((JetLookupObject) lookupObject).getPsiElement();
PsiElement psiElement = ((JetLookupObject) lookupObject).getPsiElement();
if (psiElement != null) {
return new LookupPositionObject(psiElement);
}