diff --git a/idea/src/org/jetbrains/jet/plugin/caches/JetFromJavaDescriptorHelper.java b/idea/src/org/jetbrains/jet/plugin/caches/JetFromJavaDescriptorHelper.java index da429d65dcf..0049b28ff59 100644 --- a/idea/src/org/jetbrains/jet/plugin/caches/JetFromJavaDescriptorHelper.java +++ b/idea/src/org/jetbrains/jet/plugin/caches/JetFromJavaDescriptorHelper.java @@ -17,6 +17,7 @@ package org.jetbrains.jet.plugin.caches; import com.google.common.base.Predicate; +import com.google.common.collect.Sets; import com.intellij.openapi.project.Project; import com.intellij.psi.*; import com.intellij.psi.impl.java.stubs.index.JavaAnnotationIndex; @@ -24,10 +25,13 @@ import com.intellij.psi.impl.java.stubs.index.JavaMethodNameIndex; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.search.PsiShortNamesCache; import com.intellij.psi.util.PsiTreeUtil; +import jet.runtime.typeinfo.JetClass; 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.JvmStdlibNames; +import org.jetbrains.jet.lang.resolve.java.kt.JetClassAnnotation; import org.jetbrains.jet.lang.resolve.java.kt.JetValueParameterAnnotation; import org.jetbrains.jet.lang.resolve.name.FqName; import org.jetbrains.jet.lang.resolve.name.Name; @@ -74,6 +78,25 @@ class JetFromJavaDescriptorHelper { return result; } + + static Collection getCompiledClassesForTopLevelObjects(Project project, GlobalSearchScope scope) { + Set jetObjectClasses = Sets.newHashSet(); + + Collection annotations = JavaAnnotationIndex.getInstance().get(JetClass.class.getSimpleName(), project, scope); + for (PsiAnnotation annotation : annotations) { + PsiModifierList modifierList = (PsiModifierList)annotation.getParent(); + final PsiElement owner = modifierList.getParent(); + if (owner instanceof PsiClass) { + PsiClass psiClass = (PsiClass) owner; + JetClassAnnotation jetAnnotation = JetClassAnnotation.get(psiClass); + if (psiClass.getContainingClass() == null && jetAnnotation.kind() == JvmStdlibNames.FLAG_CLASS_KIND_OBJECT) { + jetObjectClasses.add(psiClass); + } + } + } + + return jetObjectClasses; + } static Collection getTopExtensionFunctionNames(Project project, GlobalSearchScope scope) { diff --git a/idea/src/org/jetbrains/jet/plugin/caches/JetShortNamesCache.java b/idea/src/org/jetbrains/jet/plugin/caches/JetShortNamesCache.java index d277565853e..bc6bdbec48f 100644 --- a/idea/src/org/jetbrains/jet/plugin/caches/JetShortNamesCache.java +++ b/idea/src/org/jetbrains/jet/plugin/caches/JetShortNamesCache.java @@ -16,6 +16,8 @@ package org.jetbrains.jet.plugin.caches; +import com.google.common.base.Function; +import com.google.common.collect.Collections2; import com.google.common.collect.Sets; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Condition; @@ -27,6 +29,7 @@ import com.intellij.util.Processor; 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.asJava.JavaElementFinder; import org.jetbrains.jet.lang.descriptors.*; import org.jetbrains.jet.lang.psi.*; @@ -41,10 +44,7 @@ import org.jetbrains.jet.lang.resolve.name.Name; import org.jetbrains.jet.lang.resolve.scopes.JetScope; import org.jetbrains.jet.lang.types.JetType; import org.jetbrains.jet.lang.types.expressions.ExpressionTypingUtils; -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 org.jetbrains.jet.plugin.stubindex.*; import java.util.*; @@ -123,7 +123,56 @@ public class JetShortNamesCache extends PsiShortNamesCache { return functionNames; } - // TODO: Make it work for properties + @NotNull + public Collection getAllTopLevelObjectNames() { + Set topObjectNames = new HashSet(); + topObjectNames.addAll(JetTopLevelShortObjectNameIndex.getInstance().getAllKeys(project)); + + Collection classObjects = JetFromJavaDescriptorHelper.getCompiledClassesForTopLevelObjects(project, GlobalSearchScope.allScope(project)); + topObjectNames.addAll(Collections2.transform(classObjects, new Function() { + @Override + public String apply(@Nullable PsiClass aClass) { + assert aClass != null; + return aClass.getName(); + } + })); + + return topObjectNames; + } + + @NotNull + public Collection getTopLevelObjectsByName( + @NotNull String name, + @NotNull JetSimpleNameExpression expression, + @NotNull ResolveSession resolveSession, + @NotNull GlobalSearchScope scope + ) { + BindingContext context = ResolveSessionUtils.resolveToExpression(resolveSession, expression); + JetScope jetScope = context.get(BindingContext.RESOLUTION_SCOPE, expression); + + if (jetScope == null) { + return Collections.emptyList(); + } + + Set result = Sets.newHashSet(); + + Collection topObjects = JetTopLevelShortObjectNameIndex.getInstance().get(name, project, scope); + for (JetObjectDeclaration objectDeclaration : topObjects) { + result.add(resolveSession.getClassDescriptor(objectDeclaration)); + } + + for (PsiClass psiClass : JetFromJavaDescriptorHelper + .getCompiledClassesForTopLevelObjects(project, GlobalSearchScope.allScope(project))) { + String qualifiedName = psiClass.getQualifiedName(); + if (qualifiedName != null) { + FqName fqName = new FqName(qualifiedName); + result.addAll(ResolveSessionUtils.getClassDescriptorsByFqName(resolveSession, fqName)); + } + } + + return result; + } + @NotNull public Collection getTopLevelFunctionDescriptorsByName( @NotNull String name, diff --git a/idea/src/org/jetbrains/jet/plugin/completion/JetCompletionContributor.java b/idea/src/org/jetbrains/jet/plugin/completion/JetCompletionContributor.java index 72de55e85ab..7e6508e6d81 100644 --- a/idea/src/org/jetbrains/jet/plugin/completion/JetCompletionContributor.java +++ b/idea/src/org/jetbrains/jet/plugin/completion/JetCompletionContributor.java @@ -190,6 +190,7 @@ public class JetCompletionContributor extends CompletionContributor { if (shouldRunTopLevelCompletion()) { JetTypesCompletionHelper.addJetTypes(parameters, jetResult); addJetTopLevelFunctions(); + addJetTopLevelObjects(); } if (shouldRunExtensionsCompletion()) { @@ -214,9 +215,17 @@ public class JetCompletionContributor extends CompletionContributor { } public static boolean isPartOfTypeDeclaration(@NotNull DeclarationDescriptor descriptor) { - return (descriptor instanceof ClassDescriptor) || - (descriptor instanceof NamespaceDescriptor) || - (descriptor instanceof TypeParameterDescriptor); + if (descriptor instanceof NamespaceDescriptor || descriptor instanceof TypeParameterDescriptor) { + return true; + } + + if (descriptor instanceof ClassDescriptor) { + ClassDescriptor classDescriptor = (ClassDescriptor) descriptor; + ClassKind kind = classDescriptor.getKind(); + return !(kind == ClassKind.OBJECT || kind == ClassKind.CLASS_OBJECT); + } + + return false; } private void addJetTopLevelFunctions() { @@ -236,6 +245,18 @@ public class JetCompletionContributor extends CompletionContributor { } } + private void addJetTopLevelObjects() { + JetShortNamesCache namesCache = JetCacheManager.getInstance(getPosition().getProject()).getNamesCache(); + GlobalSearchScope scope = GlobalSearchScope.allScope(getPosition().getProject()); + Collection objectNames = namesCache.getAllTopLevelObjectNames(); + + for (String name : objectNames) { + if (jetResult.getResult().getPrefixMatcher().prefixMatches(name)) { + jetResult.addAllElements(namesCache.getTopLevelObjectsByName(name, jetReference.getExpression(), getResolveSession(), scope)); + } + } + } + private boolean shouldRunOnlyTypeCompletion() { // Check that completion in the type annotation context and if there's a qualified // expression we are at first of it diff --git a/idea/src/org/jetbrains/jet/plugin/completion/JetTypesCompletionHelper.java b/idea/src/org/jetbrains/jet/plugin/completion/JetTypesCompletionHelper.java index 3536c057a3b..3bb9b814645 100644 --- a/idea/src/org/jetbrains/jet/plugin/completion/JetTypesCompletionHelper.java +++ b/idea/src/org/jetbrains/jet/plugin/completion/JetTypesCompletionHelper.java @@ -91,12 +91,13 @@ public class JetTypesCompletionHelper { } if (DecompiledDataFactory.isCompiledFromKotlin(aClass)) { - // TODO: Filter classes for compiled kotlin objects - String qualifiedName = aClass.getQualifiedName(); - if (qualifiedName != null) { - FqName fqName = new FqName(qualifiedName); - jetCompletionResult.addAllElements( - ResolveSessionUtils.getClassDescriptorsByFqName(jetCompletionResult.getResolveSession(), fqName)); + if (!DecompiledDataFactory.isKotlinObject(aClass)) { + String qualifiedName = aClass.getQualifiedName(); + if (qualifiedName != null) { + FqName fqName = new FqName(qualifiedName); + jetCompletionResult.addAllElements( + ResolveSessionUtils.getClassDescriptorsByFqName(jetCompletionResult.getResolveSession(), fqName)); + } } return true; diff --git a/idea/src/org/jetbrains/jet/plugin/libraries/DecompiledDataFactory.java b/idea/src/org/jetbrains/jet/plugin/libraries/DecompiledDataFactory.java index fb9cd4b2809..3aa0be651ac 100644 --- a/idea/src/org/jetbrains/jet/plugin/libraries/DecompiledDataFactory.java +++ b/idea/src/org/jetbrains/jet/plugin/libraries/DecompiledDataFactory.java @@ -35,6 +35,8 @@ import org.jetbrains.jet.lang.resolve.BindingContextUtils; import org.jetbrains.jet.lang.resolve.java.DescriptorSearchRule; import org.jetbrains.jet.lang.resolve.java.JavaDescriptorResolver; import org.jetbrains.jet.lang.resolve.java.JvmAbi; +import org.jetbrains.jet.lang.resolve.java.JvmStdlibNames; +import org.jetbrains.jet.lang.resolve.java.kt.JetClassAnnotation; import org.jetbrains.jet.lang.resolve.name.FqName; import org.jetbrains.jet.resolve.DescriptorRenderer; @@ -214,4 +216,8 @@ public class DecompiledDataFactory { public static boolean isCompiledFromKotlin(@NotNull PsiClass psiClass) { return isKotlinClass(psiClass) || isKotlinNamespaceClass(psiClass); } + + public static boolean isKotlinObject(PsiClass aClass) { + return JetClassAnnotation.get(aClass).kind() == JvmStdlibNames.FLAG_CLASS_KIND_OBJECT; + } } diff --git a/idea/src/org/jetbrains/jet/plugin/quickfix/ImportInsertHelper.java b/idea/src/org/jetbrains/jet/plugin/quickfix/ImportInsertHelper.java index d5d26ab060c..b67e90469fc 100644 --- a/idea/src/org/jetbrains/jet/plugin/quickfix/ImportInsertHelper.java +++ b/idea/src/org/jetbrains/jet/plugin/quickfix/ImportInsertHelper.java @@ -24,10 +24,7 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.DefaultModuleConfiguration; import org.jetbrains.jet.lang.descriptors.ClassDescriptor; import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; -import org.jetbrains.jet.lang.psi.JetFile; -import org.jetbrains.jet.lang.psi.JetImportDirective; -import org.jetbrains.jet.lang.psi.JetPsiFactory; -import org.jetbrains.jet.lang.psi.JetPsiUtil; +import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.resolve.BindingContext; import org.jetbrains.jet.lang.resolve.BindingContextUtils; import org.jetbrains.jet.lang.resolve.DescriptorUtils; @@ -101,6 +98,12 @@ public class ImportInsertHelper { } } + if (!same) { + if (target instanceof JetObjectDeclarationName) { + same = file.getManager().areElementsEquivalent(target.getParent(), targetElement); + } + } + if (!same) { Document document = PsiDocumentManager.getInstance(file.getProject()).getDocument(file); TextRange refRange = reference.getElement().getTextRange(); diff --git a/idea/testData/completion/basic/NoObjectInTypePosition.kt b/idea/testData/completion/basic/NoObjectInTypePosition.kt new file mode 100644 index 00000000000..a2ad7955995 --- /dev/null +++ b/idea/testData/completion/basic/NoObjectInTypePosition.kt @@ -0,0 +1,8 @@ +object NamedObject + +fun test() { + val a : Named +} + +// TIMES: 2 +// ABSENT: NamedObject diff --git a/idea/testData/completion/basic/multifile/NotImportedObject-1.kt b/idea/testData/completion/basic/multifile/NotImportedObject-1.kt new file mode 100644 index 00000000000..73c9c2bb7f8 --- /dev/null +++ b/idea/testData/completion/basic/multifile/NotImportedObject-1.kt @@ -0,0 +1,9 @@ +package first + +fun testFun() { + NamedObject +} + +// EXIST: NamedObjectTopLevel1, NamedObjectTopLevel2 +// ABSENT: NamedObjectInClassObject +// ABSENT: NamedObjectInFun diff --git a/idea/testData/completion/basic/multifile/NotImportedObject-2.kt b/idea/testData/completion/basic/multifile/NotImportedObject-2.kt new file mode 100644 index 00000000000..3d105c7c9df --- /dev/null +++ b/idea/testData/completion/basic/multifile/NotImportedObject-2.kt @@ -0,0 +1,14 @@ +package test + +object NamedObjectTopLevel1 +object NamedObjectTopLevel2 + +fun some() { + object NamedObjectInFun +} + +class Some { + class object { + NamedObjectInClassObject + } +} diff --git a/idea/testData/completion/handlers/multifile/ImportAlreadyImportedObject-1.kt b/idea/testData/completion/handlers/multifile/ImportAlreadyImportedObject-1.kt new file mode 100644 index 00000000000..bfad0cfbd8f --- /dev/null +++ b/idea/testData/completion/handlers/multifile/ImportAlreadyImportedObject-1.kt @@ -0,0 +1,7 @@ +package some + +import test.SomeTestObject + +fun test() { + SomeTestObject +} \ No newline at end of file diff --git a/idea/testData/completion/handlers/multifile/ImportAlreadyImportedObject-2.kt b/idea/testData/completion/handlers/multifile/ImportAlreadyImportedObject-2.kt new file mode 100644 index 00000000000..cf34d5704eb --- /dev/null +++ b/idea/testData/completion/handlers/multifile/ImportAlreadyImportedObject-2.kt @@ -0,0 +1,3 @@ +package test + +object SomeTestObject \ No newline at end of file diff --git a/idea/testData/completion/handlers/multifile/ImportAlreadyImportedObject.kt.after b/idea/testData/completion/handlers/multifile/ImportAlreadyImportedObject.kt.after new file mode 100644 index 00000000000..bfad0cfbd8f --- /dev/null +++ b/idea/testData/completion/handlers/multifile/ImportAlreadyImportedObject.kt.after @@ -0,0 +1,7 @@ +package some + +import test.SomeTestObject + +fun test() { + SomeTestObject +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/jet/completion/JetBasicCompletionTest.java b/idea/tests/org/jetbrains/jet/completion/JetBasicCompletionTest.java index 62884e5e00e..3d6192f2791 100644 --- a/idea/tests/org/jetbrains/jet/completion/JetBasicCompletionTest.java +++ b/idea/tests/org/jetbrains/jet/completion/JetBasicCompletionTest.java @@ -213,6 +213,10 @@ public class JetBasicCompletionTest extends JetCompletionTestBase { doTest(); } + public void testNoObjectInTypePosition() { + doTest(); + } + public void testNoTopLevelCompletionInQualifiedUserTypes() { doTest(); } diff --git a/idea/tests/org/jetbrains/jet/completion/JetMultifileBasicCompletionTest.java b/idea/tests/org/jetbrains/jet/completion/JetMultifileBasicCompletionTest.java index a8b73ce4688..e08dc606d33 100644 --- a/idea/tests/org/jetbrains/jet/completion/JetMultifileBasicCompletionTest.java +++ b/idea/tests/org/jetbrains/jet/completion/JetMultifileBasicCompletionTest.java @@ -56,6 +56,10 @@ public class JetMultifileBasicCompletionTest extends JetCompletionMultiTestBase doFileTest(2); } + public void testNotImportedObject() throws Exception { + doFileTest(); + } + @Override protected String getTestDataPath() { return PluginTestCaseBase.getTestDataPathBase() + "/completion/basic/multifile/"; diff --git a/idea/tests/org/jetbrains/jet/completion/handlers/CompletionMultifileHandlerTest.java b/idea/tests/org/jetbrains/jet/completion/handlers/CompletionMultifileHandlerTest.java index 6d2da612606..55dc73e747b 100644 --- a/idea/tests/org/jetbrains/jet/completion/handlers/CompletionMultifileHandlerTest.java +++ b/idea/tests/org/jetbrains/jet/completion/handlers/CompletionMultifileHandlerTest.java @@ -30,6 +30,10 @@ public class CompletionMultifileHandlerTest extends CompletionTestCase { doTest(); } + public void testImportAlreadyImportedObject() throws Exception { + doTest(); + } + public void testJetClassCompletionImport() throws Exception { doTest(); }