KT-1051 Java interoperability completion (class and subpackages completion)

This commit is contained in:
Nikolay Krasko
2012-02-02 14:28:32 +04:00
parent fd3ae3923a
commit 38817014d5
9 changed files with 262 additions and 116 deletions
@@ -80,7 +80,9 @@ public class JavaPackageScope extends JavaClassOrPackageScope {
final JavaDescriptorResolver descriptorResolver = semanticServices.getDescriptorResolver();
for (PsiPackage psiSubPackage : javaPackage.getSubPackages()) {
allDescriptors.add(descriptorResolver.resolveNamespace(psiSubPackage.getQualifiedName()));
if (semanticServices.getKotlinNamespaceDescriptor(psiSubPackage.getQualifiedName()) == null) {
allDescriptors.add(descriptorResolver.resolveNamespace(psiSubPackage.getQualifiedName()));
}
}
for (PsiClass psiClass : javaPackage.getClasses()) {
@@ -11,6 +11,7 @@ import com.intellij.util.io.StringRef;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.psi.JetClass;
import org.jetbrains.jet.lang.psi.JetPsiUtil;
import org.jetbrains.jet.lang.psi.stubs.PsiJetClassStub;
import org.jetbrains.jet.lang.psi.stubs.impl.PsiJetClassStubImpl;
@@ -43,7 +44,7 @@ public class JetClassElementType extends JetStubElementType<PsiJetClassStub, Jet
@Override
public PsiJetClassStub createStub(@NotNull JetClass psi, StubElement parentStub) {
return new PsiJetClassStubImpl(JetStubElementTypes.CLASS, parentStub, psi.getName(), psi.getName());
return new PsiJetClassStubImpl(JetStubElementTypes.CLASS, parentStub, JetPsiUtil.getFQName(psi), psi.getName());
}
@Override
@@ -17,7 +17,7 @@ import java.io.IOException;
* @author Nikolay Krasko
*/
public class JetFileElementType extends IStubFileElementType<PsiJetFileStub> {
public static final int STUB_VERSION = 1;
public static final int STUB_VERSION = 2;
public JetFileElementType() {
super("jet.FILE", JetLanguage.INSTANCE);
@@ -146,7 +146,7 @@ public class JavaElementFinder extends PsiElementFinder {
final List<JetFile> psiFiles = collectProjectJetFiles(project, GlobalSearchScope.allScope(project));
for (JetFile psiFile : psiFiles) {
if (qualifiedName.equals(JetPsiUtil.getFQName(psiFile))) {
if (JetPsiUtil.getFQName(psiFile).startsWith(qualifiedName)) {
return new PsiPackageImpl(psiFile.getManager(), qualifiedName);
}
}
@@ -154,6 +154,23 @@ public class JavaElementFinder extends PsiElementFinder {
return null;
}
@NotNull
@Override
public PsiPackage[] getSubPackages(@NotNull PsiPackage psiPackage, @NotNull GlobalSearchScope scope) {
final List<JetFile> psiFiles = collectProjectJetFiles(project, GlobalSearchScope.allScope(project));
Set<PsiPackage> answer = new HashSet<PsiPackage>();
for (JetFile psiFile : psiFiles) {
String jetRootNamespace = JetPsiUtil.getFQName(psiFile);
if (isInPackage(psiPackage.getQualifiedName(), jetRootNamespace)) {
answer.add(new JetLightPackage(psiFile.getManager(), jetRootNamespace));
}
}
return answer.toArray(new PsiPackage[answer.size()]);
}
@NotNull
@Override
public PsiClass[] getClasses(@NotNull PsiPackage psiPackage, @NotNull GlobalSearchScope scope) {
@@ -174,6 +191,24 @@ public class JavaElementFinder extends PsiElementFinder {
return answer.toArray(new PsiClass[answer.size()]);
}
private static boolean isInPackage(String packageName, String fqn) {
return fqn.startsWith(packageName + ".");
}
private static String subPackageName(String packageName, String packageFQN) {
if (!isInPackage(packageName, packageFQN)) {
return null;
}
int nextDotIndex = packageFQN.indexOf('.', (packageName + ".").length());
if (nextDotIndex != -1) {
return packageFQN.substring((packageName + ".").length(), nextDotIndex);
}
return packageFQN.substring((packageName + ".").length());
}
private synchronized void invalidateJetFilesCache() {
jetFiles.clear();
}
@@ -0,0 +1,18 @@
package org.jetbrains.jet.asJava;
import com.intellij.psi.PsiManager;
import com.intellij.psi.impl.file.PsiPackageImpl;
/**
* @author Nikolay Krasko
*/
public class JetLightPackage extends PsiPackageImpl {
public JetLightPackage(PsiManager manager, String qualifiedName) {
super(manager, qualifiedName);
}
@Override
public boolean isValid() {
return true;
}
}
@@ -0,0 +1,56 @@
package org.jetbrains.jet.plugin.caches;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiModifier;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.PsiShortNamesCache;
import com.intellij.xml.impl.schema.TypeDescriptor;
import org.jetbrains.jet.lang.resolve.java.JvmAbi;
import java.util.ArrayList;
import java.util.Collection;
/**
* Get jet declarations from java that could be used in completion. Unlike the real jet resolver this helper is allowed
* to return partially unresolved descriptors in exchange of execution speed.
*
* @author Nikolay Krasko
*/
class JetFromJavaDescriptorHelper {
private JetFromJavaDescriptorHelper() {
}
/**
* Get java equivalents for jet top level classes.
*/
static PsiClass[] getClassesForJetNamespaces(Project project, GlobalSearchScope scope) {
return PsiShortNamesCache.getInstance(project).getClassesByName(JvmAbi.PACKAGE_CLASS, scope);
}
/**
* 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>();
for (PsiClass jetNamespaceClass : getClassesForJetNamespaces(project, scope)) {
for (PsiMethod psiMethod : jetNamespaceClass.getMethods()) {
if (psiMethod.getModifierList().hasModifierProperty(PsiModifier.STATIC)) {
result.add(psiMethod.getName());
}
}
}
return result;
}
static Collection<String> getTopExtensionFunctionNames(TypeDescriptor typeDescriptor, Project project,
GlobalSearchScope scope) {
return getPossiblePackageDeclarationsNames(project, scope);
}
}
@@ -1,11 +1,9 @@
package org.jetbrains.jet.plugin.caches;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiField;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.search.GlobalSearchScope;
@@ -14,12 +12,14 @@ import com.intellij.util.containers.HashSet;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
import org.jetbrains.jet.lang.psi.JetClass;
import org.jetbrains.jet.asJava.JavaElementFinder;
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
import org.jetbrains.jet.lang.psi.JetNamedFunction;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.plugin.compiler.WholeProjectAnalyzerFacade;
import org.jetbrains.jet.plugin.stubindex.JetExtensionFunctionNameIndex;
import org.jetbrains.jet.plugin.stubindex.JetFullClassNameIndex;
import org.jetbrains.jet.plugin.stubindex.JetShortClassNameIndex;
import org.jetbrains.jet.plugin.stubindex.JetShortFunctionNameIndex;
import java.util.ArrayList;
@@ -27,78 +27,113 @@ import java.util.Collection;
import java.util.List;
/**
* Will provide both java elements from jet context and some special declarations special to jet.
* All those declaration are planned to be used in completion.
*
* @author Nikolay Krasko
*/
public class JetShortNamesCache extends PsiShortNamesCache {
private final Project project;
private final JavaElementFinder javaElementFinder;
public JetShortNamesCache(Project project) {
this.project = project;
this.javaElementFinder = new JavaElementFinder(project);
}
@NotNull
@Override
public PsiClass[] getClassesByName(@NotNull @NonNls String name, @NotNull GlobalSearchScope scope) {
return new PsiClass[0];
}
/**
* Return jet class names form jet project sources which should be visible from java.
*/
@NotNull
@Override
public String[] getAllClassNames() {
return new String[0];
final Collection<String> classNames = JetShortClassNameIndex.getInstance().getAllKeys(project);
return classNames.toArray(new String[classNames.size()]);
// final Collection<String> fqNames = getALlJetClassFQNames();
//
// return Collections2.transform(fqNames, new Function<String, String>() {
// @Override
// public String apply(@Nullable String fqName) {
// assert fqName != null;
// return fqnToShortName(fqName);
// }
// }).toArray(new String[fqNames.size()]);
}
/**
* Return fake java names form jet project sources which should be visible from java.
*/
@NotNull
@Override
public PsiClass[] getClassesByName(@NotNull @NonNls final String name, @NotNull GlobalSearchScope scope) {
// Quick check for classes from getAllClassNames()
final Collection<String> classNames = JetShortClassNameIndex.getInstance().getAllKeys(project);
if (!classNames.contains(name)) {
return new PsiClass[0];
}
List<PsiClass> result = new ArrayList<PsiClass>();
for (String fqName : JetFullClassNameIndex.getInstance().getAllKeys(project)) {
final PsiClass psiClass = javaElementFinder.findClass(fqName, scope);
if (psiClass != null) {
result.add(psiClass);
}
}
return result.toArray(new PsiClass[result.size()]);
}
@Override
public void getAllClassNames(@NotNull HashSet<String> dest) {
// TODO: Implement it. Is it called somewhere?
}
public List<String> getAllJetClassNames() {
final BindingContext context = WholeProjectAnalyzerFacade.analyzeProjectWithCache(project, GlobalSearchScope.allScope(project));
final Collection<String> fqNames = context.getKeys(BindingContext.FQNAME_TO_CLASS_DESCRIPTOR);
public static String fqnToShortName(String fqName) {
int lastDotIndex = fqName.lastIndexOf('.');
final ArrayList<String> classNames = new ArrayList<String>();
for (String fqName : fqNames) {
int lastDotIndex = fqName.lastIndexOf('.');
if (lastDotIndex != -1) {
classNames.add(fqName.substring(lastDotIndex + 1, fqName.length()));
}
else {
classNames.add(fqName);
}
if (lastDotIndex != -1) {
return fqName.substring(lastDotIndex + 1, fqName.length());
}
return classNames;
return fqName;
}
public Collection<ClassDescriptor> getClassDescriptors() {
public Collection<String> getALlJetClassFQNames() {
final BindingContext context = WholeProjectAnalyzerFacade.analyzeProjectWithCache(project, GlobalSearchScope.allScope(project));
final Collection<String> fqNames = context.getKeys(BindingContext.FQNAME_TO_CLASS_DESCRIPTOR);
return Collections2.transform(fqNames, new Function<String, ClassDescriptor>() {
@Override
public ClassDescriptor apply(String fqName) {
return context.get(BindingContext.FQNAME_TO_CLASS_DESCRIPTOR, fqName);
}
});
return context.getKeys(BindingContext.FQNAME_TO_CLASS_DESCRIPTOR);
}
// public Collection<ClassDescriptor> getClassDescriptors() {
// final BindingContext context = WholeProjectAnalyzerFacade.analyzeProjectWithCache(project, GlobalSearchScope.allScope(project));
//
// final Collection<String> fqNames = context.getKeys(BindingContext.FQNAME_TO_CLASS_DESCRIPTOR);
// return Collections2.transform(fqNames, new Function<String, ClassDescriptor>() {
// @Override
// public ClassDescriptor apply(String fqName) {
// return context.get(BindingContext.FQNAME_TO_CLASS_DESCRIPTOR, fqName);
// }
// });
// }
public List<JetClass> getJetClassesByName(@NotNull String name, @NotNull GlobalSearchScope scope) {
final BindingContext context = WholeProjectAnalyzerFacade.analyzeProjectWithCache(project, scope);
final ArrayList<JetClass> classesResult = new ArrayList<JetClass>();
for (String fqn : getFQNamesByName(name, scope)) {
final ClassDescriptor descriptor = context.get(BindingContext.FQNAME_TO_CLASS_DESCRIPTOR, fqn);
final PsiElement declaration = context.get(BindingContext.DESCRIPTOR_TO_DECLARATION, descriptor);
if (declaration instanceof JetClass) {
classesResult.add((JetClass) declaration);
}
}
return classesResult;
}
// public List<JetClass> getJetClassesByName(@NotNull String name, @NotNull GlobalSearchScope scope) {
// final BindingContext context = WholeProjectAnalyzerFacade.analyzeProjectWithCache(project, scope);
// final ArrayList<JetClass> classesResult = new ArrayList<JetClass>();
//
// for (String fqn : getFQNamesByName(name, scope)) {
// final ClassDescriptor descriptor = context.get(BindingContext.FQNAME_TO_CLASS_DESCRIPTOR, fqn);
// final PsiElement declaration = context.get(BindingContext.DESCRIPTOR_TO_DECLARATION, descriptor);
//
// if (declaration instanceof JetClass) {
// classesResult.add((JetClass) declaration);
// }
// }
//
// return classesResult;
// }
@NotNull
public Collection<String> getFQNamesByName(@NotNull final String name, @NotNull GlobalSearchScope scope) {
@@ -111,44 +146,30 @@ public class JetShortNamesCache extends PsiShortNamesCache {
});
}
public Collection<JetNamedFunction> getAllExtensionFunctionsByName(@NotNull String name, @NotNull GlobalSearchScope scope) {
return JetExtensionFunctionNameIndex.getInstance().get(name, project, scope);
}
@NotNull
public Collection<String> getAllTopLevelFunctionNames() {
return JetShortFunctionNameIndex.getInstance().getAllKeys(project);
final ArrayList<String> functionNames = new ArrayList<String>();
functionNames.addAll(JetShortFunctionNameIndex.getInstance().getAllKeys(project));
functionNames.addAll(JetFromJavaDescriptorHelper.getPossiblePackageDeclarationsNames(project, GlobalSearchScope.allScope(project)));
return functionNames;
}
public Collection<FunctionDescriptor> getTopLevelFunctionDescriptorsByName(final @NotNull String name, @NotNull GlobalSearchScope scope) {
return new ArrayList<FunctionDescriptor>();
}
public Collection<JetNamedFunction> getTopLevelFunctionsByName(final @NotNull String name, @NotNull GlobalSearchScope scope) {
return JetShortFunctionNameIndex.getInstance().get(name, project, scope);
}
// private Collection<FunctionDescriptor> getTopLeveFunctions(@NotNull GlobalSearchScope scope) {
// final BindingContext context = WholeProjectAnalyzerFacade.analyzeProjectWithCache(project, scope);
// final Collection<String> keys = context.getKeys(BindingContext.FQNAME_TO_NAMESPACE_DESCRIPTOR);
//
// final List<FunctionDescriptor> result = new ArrayList<FunctionDescriptor>();
//
// for (String namespaceKey : keys) {
// final NamespaceDescriptor namespace = context.get(BindingContext.FQNAME_TO_NAMESPACE_DESCRIPTOR, namespaceKey);
// if (namespace != null) {
// final JetScope memberScope = namespace.getNamespaceType().getMemberScope();
// final Collection<DeclarationDescriptor> allDescriptors = memberScope.getAllDescriptors();
//
// for (DeclarationDescriptor descriptor : allDescriptors) {
// if (descriptor instanceof FunctionDescriptor) {
// FunctionDescriptor functionDescriptor = (FunctionDescriptor) descriptor;
// if (functionDescriptor.getReceiverParameter() == ReceiverDescriptor.NO_RECEIVER) {
// result.add(functionDescriptor);
// }
// }
// }
// }
// }
//
// return result;
// }
@NotNull
public Collection<String> getAllJetExtensionFunctionsNames(@NotNull GlobalSearchScope scope) {
return JetFromJavaDescriptorHelper.getTopExtensionFunctionNames(null, project, scope);
}
public Collection<JetNamedFunction> getAllJetExtensionFunctionsByName(@NotNull String name, @NotNull GlobalSearchScope scope) {
return JetExtensionFunctionNameIndex.getInstance().get(name, project, scope);
}
@NotNull
@Override
@@ -13,8 +13,11 @@ 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.ClassDescriptor;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
import org.jetbrains.jet.lang.psi.JetFile;
import org.jetbrains.jet.lang.psi.JetQualifiedExpression;
import org.jetbrains.jet.lang.psi.JetSimpleNameExpression;
import org.jetbrains.jet.lang.psi.JetUserType;
import org.jetbrains.jet.lexer.JetTokens;
import org.jetbrains.jet.plugin.caches.JetCacheManager;
import org.jetbrains.jet.plugin.caches.JetShortNamesCache;
@@ -57,9 +60,13 @@ public class JetCompletionContributor extends CompletionContributor {
}
}
// final JetShortNamesCache namesCache = JetCacheManager.getInstance(position.getProject()).getNamesCache();
// final GlobalSearchScope scope = GlobalSearchScope.allScope(parameters.getPosition().getProject());
// final Collection<String> functionNames = namesCache.getAllJetExtensionFunctionsNames(scope);
if (shouldRunClassNameCompletion(parameters)) {
addJavaClasses(parameters, result);
addJetClasses(result, position);
// addJetClasses(result, position);
}
if (shouldRunFunctionNameCompletion(parameters)) {
@@ -92,38 +99,42 @@ public class JetCompletionContributor extends CompletionContributor {
for (String name : functionNames) {
if (name.contains(actualPrefix)) {
for (JetNamedFunction function : namesCache.getTopLevelFunctionsByName(name, scope)) {
String functionName = function.getName();
String qualifiedName = function.getQualifiedName();
assert functionName != null;
final LookupElementBuilder lookup = LookupElementBuilder.create(
new LookupPositionObject(function), functionName);
if (qualifiedName != null) {
lookup.setTailText(qualifiedName);
}
addCompletionToResult(result, lookup);
for (FunctionDescriptor function : namesCache.getTopLevelFunctionDescriptorsByName(name, scope)) {
addCompletionToResult(result, DescriptorLookupConverter.createLookupElement(null, function, null));
}
// for (JetNamedFunction function : namesCache.getTopLevelFunctionsByName(name, scope)) {
// String functionName = function.getName();
// String qualifiedName = function.getQualifiedName();
// assert functionName != null;
//
// final LookupElementBuilder lookup = LookupElementBuilder.create(
// new LookupPositionObject(function), functionName);
//
// if (qualifiedName != null) {
// lookup.setTailText(qualifiedName);
// }
//
// addCompletionToResult(result, lookup);
// }
}
}
}
}
private void addJetClasses(@NotNull CompletionResultSet result, @NotNull PsiElement position) {
String actualPrefix = getActualCompletionPrefix(position);
if (actualPrefix != null) {
final Collection<ClassDescriptor> classDescriptors =
JetCacheManager.getInstance(position.getProject()).getNamesCache().getClassDescriptors();
for (ClassDescriptor descriptor : classDescriptors) {
if (descriptor.getName().startsWith(actualPrefix)) {
addCompletionToResult(result, DescriptorLookupConverter.createLookupElement(null, descriptor, null));
}
}
}
}
// private void addJetClasses(@NotNull CompletionResultSet result, @NotNull PsiElement position) {
// String actualPrefix = getActualCompletionPrefix(position);
// if (actualPrefix != null) {
// final Collection<ClassDescriptor> classDescriptors =
// JetCacheManager.getInstance(position.getProject()).getNamesCache().getClassDescriptors();
//
// for (ClassDescriptor descriptor : classDescriptors) {
// if (descriptor.getName().startsWith(actualPrefix)) {
// addCompletionToResult(result, DescriptorLookupConverter.createLookupElement(null, descriptor, null));
// }
// }
// }
// }
@Nullable
private static String getActualCompletionPrefix(@NotNull PsiElement position) {
@@ -203,9 +214,6 @@ public class JetCompletionContributor extends CompletionContributor {
}
}
}
// if (reference == null && parent instanceof CompositeElement) {
// reference = ((CompositeElement) parent).getPsi().getReference();
// }
}
return null;
@@ -16,6 +16,11 @@ public class StubIndexServiceImpl implements StubIndexService {
if (name != null) {
sink.occurrence(JetIndexKeys.SHORT_NAME_KEY, name);
}
String fqn = stub.getQualifiedName();
if (fqn != null) {
sink.occurrence(JetIndexKeys.FQN_KEY, fqn);
}
}
@Override