Completion for top-level functions from class and jar files.

This commit is contained in:
Nikolay Krasko
2012-03-11 15:21:37 +04:00
parent ef6461c4d2
commit fba492c492
6 changed files with 115 additions and 54 deletions
@@ -24,18 +24,8 @@ import com.google.inject.Inject;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.Configuration; import org.jetbrains.jet.lang.Configuration;
import org.jetbrains.jet.lang.descriptors.ClassDescriptor; import org.jetbrains.jet.lang.descriptors.*;
import org.jetbrains.jet.lang.descriptors.ClassKind; import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.descriptors.ClassifierDescriptor;
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
import org.jetbrains.jet.lang.descriptors.NamespaceDescriptor;
import org.jetbrains.jet.lang.descriptors.VariableDescriptor;
import org.jetbrains.jet.lang.psi.JetDotQualifiedExpression;
import org.jetbrains.jet.lang.psi.JetExpression;
import org.jetbrains.jet.lang.psi.JetFile;
import org.jetbrains.jet.lang.psi.JetImportDirective;
import org.jetbrains.jet.lang.psi.JetQualifiedExpression;
import org.jetbrains.jet.lang.psi.JetSimpleNameExpression;
import org.jetbrains.jet.lang.resolve.scopes.JetScope; import org.jetbrains.jet.lang.resolve.scopes.JetScope;
import org.jetbrains.jet.lang.resolve.scopes.WritableScope; import org.jetbrains.jet.lang.resolve.scopes.WritableScope;
import org.jetbrains.jet.lang.types.JetType; import org.jetbrains.jet.lang.types.JetType;
@@ -45,13 +35,7 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import static org.jetbrains.jet.lang.diagnostics.Errors.CANNOT_BE_IMPORTED; import static org.jetbrains.jet.lang.diagnostics.Errors.*;
import static org.jetbrains.jet.lang.diagnostics.Errors.CANNOT_IMPORT_FROM_ELEMENT;
import static org.jetbrains.jet.lang.diagnostics.Errors.NO_CLASS_OBJECT;
import static org.jetbrains.jet.lang.diagnostics.Errors.UNRESOLVED_REFERENCE;
import static org.jetbrains.jet.lang.diagnostics.Errors.UNSUPPORTED;
import static org.jetbrains.jet.lang.diagnostics.Errors.USELESS_HIDDEN_IMPORT;
import static org.jetbrains.jet.lang.diagnostics.Errors.USELESS_SIMPLE_IMPORT;
/** /**
* @author abreslav * @author abreslav
@@ -166,7 +150,10 @@ public class ImportsResolver {
} }
} }
public static Collection<? extends DeclarationDescriptor> analyseImportReference(@NotNull JetImportDirective importDirective, @NotNull JetScope scope, @NotNull BindingTrace trace) { public static Collection<? extends DeclarationDescriptor> analyseImportReference(
@NotNull JetImportDirective importDirective,
@NotNull JetScope scope, @NotNull BindingTrace trace
) {
ImportsResolver.SingleImportResolver importResolver = new ImportsResolver.SingleImportResolver(trace, false); ImportsResolver.SingleImportResolver importResolver = new ImportsResolver.SingleImportResolver(trace, false);
return importResolver.processImportReference(importDirective, scope, Importer.DO_NOTHING); return importResolver.processImportReference(importDirective, scope, Importer.DO_NOTHING);
} }
@@ -16,6 +16,7 @@
package org.jetbrains.jet.plugin.caches; package org.jetbrains.jet.plugin.caches;
import com.google.common.base.Predicate;
import com.intellij.openapi.project.Project; import com.intellij.openapi.project.Project;
import com.intellij.psi.*; import com.intellij.psi.*;
import com.intellij.psi.impl.java.stubs.index.JavaAnnotationIndex; import com.intellij.psi.impl.java.stubs.index.JavaAnnotationIndex;
@@ -24,8 +25,11 @@ import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.PsiShortNamesCache; import com.intellij.psi.search.PsiShortNamesCache;
import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiTreeUtil;
import jet.runtime.typeinfo.JetValueParameter; import jet.runtime.typeinfo.JetValueParameter;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.resolve.java.JvmAbi; import org.jetbrains.jet.lang.resolve.java.JvmAbi;
import org.jetbrains.jet.lang.resolve.java.kt.JetValueParameterAnnotation; import org.jetbrains.jet.lang.resolve.java.kt.JetValueParameterAnnotation;
import org.jetbrains.jet.util.QualifiedNamesUtil;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
@@ -33,8 +37,7 @@ import java.util.HashSet;
import java.util.Set; import java.util.Set;
/** /**
* Get jet declarations from java that could be used in completion. Unlike the real jet resolver this helper is allowed * Number of helper methods for searching jet element prototypes in java. Methods use java indices for search.
* to return partially unresolved descriptors in exchange of execution speed.
* *
* @author Nikolay Krasko * @author Nikolay Krasko
*/ */
@@ -47,6 +50,9 @@ class JetFromJavaDescriptorHelper {
* Get java equivalents for jet top level classes. * Get java equivalents for jet top level classes.
*/ */
static PsiClass[] getClassesForJetNamespaces(Project project, GlobalSearchScope scope) { static PsiClass[] getClassesForJetNamespaces(Project project, GlobalSearchScope scope) {
/* Will iterate through short name caches
Kotlin namespaces from jar a class files will be collected from java cache
Kotlin namespaces classes from sources will be collected with JetShortNamesCache.getClassesByName */
return PsiShortNamesCache.getInstance(project).getClassesByName(JvmAbi.PACKAGE_CLASS, scope); return PsiShortNamesCache.getInstance(project).getClassesByName(JvmAbi.PACKAGE_CLASS, scope);
} }
@@ -96,8 +102,51 @@ class JetFromJavaDescriptorHelper {
return extensionNames; return extensionNames;
} }
static Collection<PsiMethod> getTopExtensionFunctionByName(String name, Project project, GlobalSearchScope scope) { static Collection<PsiMethod> getTopExtensionFunctionPrototypesByName(String name, Project project, GlobalSearchScope scope) {
return filterJetJavaPrototypesByName(
name, project, scope,
new Predicate<JetValueParameterAnnotation>() {
@Override
public boolean apply(@Nullable JetValueParameterAnnotation jetValueParameterAnnotation) {
assert jetValueParameterAnnotation != null;
return jetValueParameterAnnotation.receiver();
}
});
}
static Collection<PsiMethod> getTopLevelFunctionPrototypesByName(String name, Project project, GlobalSearchScope scope) {
return filterJetJavaPrototypesByName(
name, project, scope,
new Predicate<JetValueParameterAnnotation>() {
@Override
public boolean apply(@Nullable JetValueParameterAnnotation jetValueParameterAnnotation) {
assert jetValueParameterAnnotation != null;
return !jetValueParameterAnnotation.receiver();
}
});
}
@Nullable
static String getJetTopLevelDeclarationFQN(@NotNull PsiMethod method) {
PsiClass containingClass = method.getContainingClass();
if (containingClass != null) {
String classFQN = containingClass.getQualifiedName();
if (classFQN != null) {
if (QualifiedNamesUtil.fqnToShortName(classFQN).equals(JvmAbi.PACKAGE_CLASS)) {
String classParentFQN = QualifiedNamesUtil.withoutLastSegment(classFQN);
return QualifiedNamesUtil.combine(classParentFQN, method.getName());
}
}
}
return null;
}
private static Collection<PsiMethod> filterJetJavaPrototypesByName(
String name, Project project, GlobalSearchScope scope,
Predicate<JetValueParameterAnnotation> filterPredicate) {
Set<PsiMethod> selectedMethods = new HashSet<PsiMethod>(); Set<PsiMethod> selectedMethods = new HashSet<PsiMethod>();
Collection<PsiMethod> psiMethods = JavaMethodNameIndex.getInstance().get(name, project, scope); Collection<PsiMethod> psiMethods = JavaMethodNameIndex.getInstance().get(name, project, scope);
@@ -121,7 +170,7 @@ class JetFromJavaDescriptorHelper {
continue; continue;
} }
if (new JetValueParameterAnnotation(psiAnnotation).receiver()) { if (filterPredicate.apply(new JetValueParameterAnnotation(psiAnnotation))) {
selectedMethods.add(psiMethod); selectedMethods.add(psiMethod);
} }
} }
@@ -34,12 +34,11 @@ import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.asJava.JavaElementFinder; import org.jetbrains.jet.asJava.JavaElementFinder;
import org.jetbrains.jet.lang.descriptors.CallableDescriptor; import org.jetbrains.jet.lang.descriptors.*;
import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
import org.jetbrains.jet.lang.descriptors.SimpleFunctionDescriptor;
import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.BindingContext; import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.resolve.BindingTraceContext;
import org.jetbrains.jet.lang.resolve.ImportsResolver;
import org.jetbrains.jet.lang.resolve.scopes.JetScope; import org.jetbrains.jet.lang.resolve.scopes.JetScope;
import org.jetbrains.jet.lang.types.JetType; import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.lang.types.expressions.ExpressionTypingUtils; import org.jetbrains.jet.lang.types.expressions.ExpressionTypingUtils;
@@ -157,15 +156,36 @@ public class JetShortNamesCache extends PsiShortNamesCache {
} }
@NotNull @NotNull
public Collection<SimpleFunctionDescriptor> getTopLevelFunctionDescriptorsByName(@NotNull String name, public Collection<FunctionDescriptor> getTopLevelFunctionDescriptorsByName(
@NotNull GlobalSearchScope scope) { @NotNull String name,
@NotNull JetSimpleNameExpression expression,
@NotNull GlobalSearchScope scope
) {
HashSet<FunctionDescriptor> result = new HashSet<FunctionDescriptor>();
JetFile jetFile = (JetFile) expression.getContainingFile();
BindingContext context = WholeProjectAnalyzerFacade.analyzeProjectWithCacheOnAFile(jetFile);
JetScope jetScope = context.get(BindingContext.RESOLUTION_SCOPE, expression);
if (jetScope == null) {
return result;
}
Collection<PsiMethod> topLevelFunctionPrototypes = JetFromJavaDescriptorHelper.getTopLevelFunctionPrototypesByName(name, project, scope);
for (PsiMethod method : topLevelFunctionPrototypes) {
String functionFQN = JetFromJavaDescriptorHelper.getJetTopLevelDeclarationFQN(method);
if (functionFQN != null) {
JetImportDirective importDirective = JetPsiFactory.createImportDirective(project, functionFQN);
Collection<? extends DeclarationDescriptor> declarationDescriptors = ImportsResolver.analyseImportReference(importDirective, jetScope, new BindingTraceContext());
for (DeclarationDescriptor declarationDescriptor : declarationDescriptors) {
if (declarationDescriptor instanceof FunctionDescriptor) {
result.add((FunctionDescriptor) declarationDescriptor);
}
}
}
}
Collection<JetNamedFunction> jetNamedFunctions = JetShortFunctionNameIndex.getInstance().get(name, project, scope); Collection<JetNamedFunction> jetNamedFunctions = JetShortFunctionNameIndex.getInstance().get(name, project, scope);
BindingContext context = getResolutionContext(scope);
HashSet<SimpleFunctionDescriptor> result = new HashSet<SimpleFunctionDescriptor>();
for (JetNamedFunction jetNamedFunction : jetNamedFunctions) { for (JetNamedFunction jetNamedFunction : jetNamedFunctions) {
SimpleFunctionDescriptor functionDescriptor = context.get(BindingContext.FUNCTION, jetNamedFunction); SimpleFunctionDescriptor functionDescriptor = context.get(BindingContext.FUNCTION, jetNamedFunction);
if (functionDescriptor != null) { if (functionDescriptor != null) {
@@ -204,7 +224,7 @@ public class JetShortNamesCache extends PsiShortNamesCache {
public Collection<PsiElement> getJetExtensionFunctionsByName(@NotNull String name, @NotNull GlobalSearchScope scope) { public Collection<PsiElement> getJetExtensionFunctionsByName(@NotNull String name, @NotNull GlobalSearchScope scope) {
HashSet<PsiElement> functions = new HashSet<PsiElement>(); HashSet<PsiElement> functions = new HashSet<PsiElement>();
functions.addAll(JetExtensionFunctionNameIndex.getInstance().get(name, project, scope)); functions.addAll(JetExtensionFunctionNameIndex.getInstance().get(name, project, scope));
functions.addAll(JetFromJavaDescriptorHelper.getTopExtensionFunctionByName(name, project, scope)); functions.addAll(JetFromJavaDescriptorHelper.getTopExtensionFunctionPrototypesByName(name, project, scope));
return functions; return functions;
} }
@@ -217,10 +237,6 @@ public class JetShortNamesCache extends PsiShortNamesCache {
) { ) {
Collection<DeclarationDescriptor> resultDescriptors = new ArrayList<DeclarationDescriptor>(); Collection<DeclarationDescriptor> resultDescriptors = new ArrayList<DeclarationDescriptor>();
if (!(expression.getContainingFile() instanceof JetFile)) {
return resultDescriptors;
}
JetFile jetFile = (JetFile) expression.getContainingFile(); JetFile jetFile = (JetFile) expression.getContainingFile();
BindingContext context = WholeProjectAnalyzerFacade.analyzeProjectWithCacheOnAFile(jetFile); BindingContext context = WholeProjectAnalyzerFacade.analyzeProjectWithCacheOnAFile(jetFile);
@@ -245,16 +261,9 @@ public class JetShortNamesCache extends PsiShortNamesCache {
functionFQNs.add(JetPsiUtil.getFQName((JetNamedFunction) extensionFunction)); functionFQNs.add(JetPsiUtil.getFQName((JetNamedFunction) extensionFunction));
} }
else if (extensionFunction instanceof PsiMethod) { else if (extensionFunction instanceof PsiMethod) {
PsiMethod function = (PsiMethod) extensionFunction; String functionFQN = JetFromJavaDescriptorHelper.getJetTopLevelDeclarationFQN((PsiMethod) extensionFunction);
PsiClass containingClass = function.getContainingClass(); if (functionFQN != null) {
functionFQNs.add(functionFQN);
if (containingClass != null) {
String classFQN = containingClass.getQualifiedName();
if (classFQN != null) {
String classParentFQN = QualifiedNamesUtil.withoutLastSegment(classFQN);
functionFQNs.add(QualifiedNamesUtil.combine(classParentFQN, function.getName()));
}
} }
} }
} }
@@ -279,7 +288,7 @@ public class JetShortNamesCache extends PsiShortNamesCache {
return JetShortFunctionNameIndex.getInstance().get(name, project, scope); return JetShortFunctionNameIndex.getInstance().get(name, project, scope);
} }
public Collection<String> getAllJetFunctionsNames() { public Collection<String> getAllJetOnlyTopFunctionsNames() {
return JetShortFunctionNameIndex.getInstance().getAllKeys(project); return JetShortFunctionNameIndex.getInstance().getAllKeys(project);
} }
@@ -51,6 +51,7 @@ import java.util.Set;
* @author Nikolay Krasko * @author Nikolay Krasko
*/ */
public class JetCompletionContributor extends CompletionContributor { public class JetCompletionContributor extends CompletionContributor {
public JetCompletionContributor() { public JetCompletionContributor() {
extend(CompletionType.BASIC, PlatformPatterns.psiElement(), extend(CompletionType.BASIC, PlatformPatterns.psiElement(),
new CompletionProvider<CompletionParameters>() { new CompletionProvider<CompletionParameters>() {
@@ -86,7 +87,7 @@ public class JetCompletionContributor extends CompletionContributor {
if (shouldRunTopLevelCompletion(parameters, prefix)) { if (shouldRunTopLevelCompletion(parameters, prefix)) {
addClasses(parameters, result); addClasses(parameters, result);
addJetTopLevelFunctions(result, position, positions); addJetTopLevelFunctions(jetReference.getExpression(), result, position, positions);
} }
if (shouldRunExtensionsCompletion(parameters, prefix)) { if (shouldRunExtensionsCompletion(parameters, prefix)) {
@@ -132,7 +133,7 @@ public class JetCompletionContributor extends CompletionContributor {
} }
} }
private static void addJetTopLevelFunctions(@NotNull CompletionResultSet result, @NotNull PsiElement position, private static void addJetTopLevelFunctions(JetSimpleNameExpression expression, @NotNull CompletionResultSet result, @NotNull PsiElement position,
@NotNull Set<LookupPositionObject> positions) { @NotNull Set<LookupPositionObject> positions) {
String actualPrefix = result.getPrefixMatcher().getPrefix(); String actualPrefix = result.getPrefixMatcher().getPrefix();
@@ -147,7 +148,7 @@ public class JetCompletionContributor extends CompletionContributor {
for (String name : functionNames) { for (String name : functionNames) {
if (name.contains(actualPrefix)) { if (name.contains(actualPrefix)) {
for (FunctionDescriptor function : namesCache.getTopLevelFunctionDescriptorsByName(name, scope)) { for (FunctionDescriptor function : namesCache.getTopLevelFunctionDescriptorsByName(name, expression, scope)) {
addCompletionToResult(result, DescriptorLookupConverter.createLookupElement(resolutionContext, function), positions); addCompletionToResult(result, DescriptorLookupConverter.createLookupElement(resolutionContext, function), positions);
} }
} }
@@ -0,0 +1,11 @@
package testing
fun someFun() {
measureTime<caret>
}
// Important: This test checks that completion will find top level functions from jars.
// If you going to update it make sure that methods are not auto-imported
// RUNTIME: 1
// EXIST: measureTimeNano, measureTimeMillis
@@ -105,6 +105,10 @@ public class JetBasicCompletionTest extends JetCompletionTestBase {
doTest(); doTest();
} }
public void testTopLevelFromStandardLibrary() {
doTest();
}
public void testVariableClassName() { public void testVariableClassName() {
doTest(); doTest();
} }