From e5c39df519c967bff4d9b8cf3f562f6ff57555eb Mon Sep 17 00:00:00 2001 From: Nikolay Krasko Date: Wed, 7 Dec 2011 12:19:50 +0400 Subject: [PATCH] KT-658 Show types in IDEA auto assist for kotlin --- .../resolve/java/JavaDescriptorResolver.java | 2 +- .../lang/resolve/java/JavaPackageScope.java | 47 +++++++++++++-- .../resolve/scopes/WriteThroughScope.java | 4 ++ .../jetbrains/jet/plugin/JetIconProvider.java | 18 +++--- .../compiler/WholeProjectAnalyzerFacade.java | 18 ++++-- idea/testData/completion/basic/FromImports.kt | 9 +++ idea/testData/completion/basic/JavaPackage.kt | 6 ++ .../completion/basic/VariableClassName.kt | 4 +- .../completion/keywords/InTypeScope.kt | 2 +- idea/testData/completion/keywords/TopScope.kt | 10 ++-- .../completion/JetBasicCompletionTest.java | 2 +- .../jet/completion/JetCompletionTestBase.java | 60 +++++++++---------- .../completion/KeywordsCompletionTest.java | 8 ++- 13 files changed, 126 insertions(+), 64 deletions(-) create mode 100644 idea/testData/completion/basic/FromImports.kt create mode 100644 idea/testData/completion/basic/JavaPackage.kt diff --git a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/JavaDescriptorResolver.java b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/JavaDescriptorResolver.java index 4ce838b3069..56b9f16b534 100644 --- a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/JavaDescriptorResolver.java +++ b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/JavaDescriptorResolver.java @@ -267,7 +267,7 @@ public class JavaDescriptorResolver { return javaFacade.findClass(qualifiedName, javaSearchScope); } - private PsiPackage findPackage(String qualifiedName) { + /*package*/ PsiPackage findPackage(String qualifiedName) { return javaFacade.findPackage(qualifiedName); } diff --git a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/JavaPackageScope.java b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/JavaPackageScope.java index 776e5219dfc..2763af7e1f4 100644 --- a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/JavaPackageScope.java +++ b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/JavaPackageScope.java @@ -1,10 +1,17 @@ package org.jetbrains.jet.lang.resolve.java; +import com.google.common.collect.Sets; import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiModifier; +import com.intellij.psi.PsiPackage; import org.jetbrains.annotations.NotNull; -import org.jetbrains.jet.lang.descriptors.*; +import org.jetbrains.jet.lang.descriptors.ClassifierDescriptor; +import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; +import org.jetbrains.jet.lang.descriptors.FunctionDescriptor; +import org.jetbrains.jet.lang.descriptors.NamespaceDescriptor; import org.jetbrains.jet.lang.resolve.scopes.JetScopeImpl; +import java.util.Collection; import java.util.Collections; import java.util.Set; @@ -14,11 +21,13 @@ import java.util.Set; public class JavaPackageScope extends JetScopeImpl { private final JavaSemanticServices semanticServices; private final DeclarationDescriptor containingDescriptor; - private final String packagePrefix; + private final String packageFQN; + + private Collection allDescriptors; public JavaPackageScope(@NotNull String packageFQN, DeclarationDescriptor containingDescriptor, JavaSemanticServices semanticServices) { this.semanticServices = semanticServices; - this.packagePrefix = packageFQN.isEmpty() ? "" : packageFQN + "."; + this.packageFQN = packageFQN; this.containingDescriptor = containingDescriptor; } @@ -36,7 +45,7 @@ public class JavaPackageScope extends JetScopeImpl { @Override public Set getFunctions(@NotNull String name) { // TODO: what is GlobalSearchScope - PsiClass psiClass = semanticServices.getDescriptorResolver().javaFacade.findClass(packagePrefix + "namespace"); + PsiClass psiClass = semanticServices.getDescriptorResolver().javaFacade.findClass(getQualifiedName("namespace")); if (psiClass == null) { return Collections.emptySet(); } @@ -54,7 +63,35 @@ public class JavaPackageScope extends JetScopeImpl { return containingDescriptor; } + @NotNull + @Override + public Collection getAllDescriptors() { + if (allDescriptors == null) { + allDescriptors = Sets.newHashSet(); + + final PsiPackage javaPackage = semanticServices.getDescriptorResolver().findPackage(packageFQN); + + if (javaPackage != null) { + final JavaDescriptorResolver descriptorResolver = semanticServices.getDescriptorResolver(); + + for (PsiPackage psiSubPackage : javaPackage.getSubPackages()) { + allDescriptors.add(descriptorResolver.resolveNamespace(psiSubPackage.getQualifiedName())); + } + + for (PsiClass psiClass : javaPackage.getClasses()) { + if (psiClass.hasModifierProperty(PsiModifier.PUBLIC)) { + allDescriptors.add(descriptorResolver.resolveClass(psiClass)); + } + } + } + } + + return allDescriptors; + } + private String getQualifiedName(String name) { - return packagePrefix + name; + return (packageFQN.isEmpty() ? "" : packageFQN + ".") + name; } + + } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/scopes/WriteThroughScope.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/scopes/WriteThroughScope.java index 5f123dc5a44..7113b9ceaaa 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/scopes/WriteThroughScope.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/scopes/WriteThroughScope.java @@ -199,6 +199,10 @@ public class WriteThroughScope extends WritableScopeWithImports { allDescriptors = Lists.newArrayList(); allDescriptors.addAll(writableWorker.getAllDescriptors()); allDescriptors.addAll(getWorkerScope().getAllDescriptors()); + + for (JetScope imported : getImports()) { + allDescriptors.addAll(imported.getAllDescriptors()); + } } return allDescriptors; } diff --git a/idea/src/org/jetbrains/jet/plugin/JetIconProvider.java b/idea/src/org/jetbrains/jet/plugin/JetIconProvider.java index 3d10d912353..c1a8df88bec 100644 --- a/idea/src/org/jetbrains/jet/plugin/JetIconProvider.java +++ b/idea/src/org/jetbrains/jet/plugin/JetIconProvider.java @@ -4,7 +4,7 @@ import com.intellij.ide.IconProvider; import com.intellij.openapi.util.Iconable; import com.intellij.psi.PsiElement; import com.intellij.psi.util.PsiTreeUtil; -import com.intellij.util.Icons; +import com.intellij.util.PlatformIcons; import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lexer.JetTokens; @@ -15,21 +15,21 @@ import javax.swing.*; * @author yole */ public class JetIconProvider extends IconProvider { - public static final Icon ICON_FOR_OBJECT = Icons.ANONYMOUS_CLASS_ICON; + public static final Icon ICON_FOR_OBJECT = PlatformIcons.ANONYMOUS_CLASS_ICON; @Override public Icon getIcon(@NotNull PsiElement psiElement, int flags) { if (psiElement instanceof JetNamespace) { - return (flags & Iconable.ICON_FLAG_OPEN) != 0 ? Icons.PACKAGE_OPEN_ICON : Icons.PACKAGE_ICON; + return (flags & Iconable.ICON_FLAG_OPEN) != 0 ? PlatformIcons.PACKAGE_OPEN_ICON : PlatformIcons.PACKAGE_ICON; } if (psiElement instanceof JetNamedFunction) { return PsiTreeUtil.getParentOfType(psiElement, JetNamedDeclaration.class) instanceof JetClass - ? Icons.METHOD_ICON - : Icons.FUNCTION_ICON; + ? PlatformIcons.METHOD_ICON + : PlatformIcons.FUNCTION_ICON; } if (psiElement instanceof JetClass) { JetClass jetClass = (JetClass) psiElement; - Icon icon = jetClass.hasModifier(JetTokens.ENUM_KEYWORD) ? Icons.ENUM_ICON : Icons.CLASS_ICON; + Icon icon = jetClass.hasModifier(JetTokens.ENUM_KEYWORD) ? PlatformIcons.ENUM_ICON : PlatformIcons.CLASS_ICON; if (jetClass instanceof JetEnumEntry) { JetEnumEntry enumEntry = (JetEnumEntry) jetClass; if (enumEntry.getPrimaryConstructorParameterList() == null) { @@ -42,13 +42,13 @@ public class JetIconProvider extends IconProvider { if (((JetParameter) psiElement).getValOrVarNode() != null) { JetParameterList parameterList = PsiTreeUtil.getParentOfType(psiElement, JetParameterList.class); if (parameterList != null && parameterList.getParent() instanceof JetClass) { - return Icons.PROPERTY_ICON; + return PlatformIcons.PROPERTY_ICON; } } - return Icons.PARAMETER_ICON; + return PlatformIcons.PARAMETER_ICON; } if (psiElement instanceof JetProperty) { - return Icons.PROPERTY_ICON; + return PlatformIcons.PROPERTY_ICON; } return null; } diff --git a/idea/src/org/jetbrains/jet/plugin/compiler/WholeProjectAnalyzerFacade.java b/idea/src/org/jetbrains/jet/plugin/compiler/WholeProjectAnalyzerFacade.java index 01d853c9d7d..ec99fa7d3df 100644 --- a/idea/src/org/jetbrains/jet/plugin/compiler/WholeProjectAnalyzerFacade.java +++ b/idea/src/org/jetbrains/jet/plugin/compiler/WholeProjectAnalyzerFacade.java @@ -24,13 +24,23 @@ import java.util.Set; /** * @author abreslav */ -public class WholeProjectAnalyzerFacade { - public static final Function> WHOLE_PROJECT_DECLARATION_PROVIDER = new Function>() { +public final class WholeProjectAnalyzerFacade { + + /** Forbid creating */ + private WholeProjectAnalyzerFacade() {} + + /** + * Will collect all root-namespaces in all kotlin files in the project. + */ + public static final Function> WHOLE_PROJECT_DECLARATION_PROVIDER = + new Function>() { + @Override public Collection fun(final JetFile rootFile) { final Project project = rootFile.getProject(); final Set namespaces = Sets.newLinkedHashSet(); - ProjectRootManager rootManager = ProjectRootManager.getInstance(project); + final ProjectRootManager rootManager = ProjectRootManager.getInstance(project); + if (rootManager != null && !ApplicationManager.getApplication().isUnitTestMode()) { VirtualFile[] contentRoots = rootManager.getContentRoots(); @@ -55,9 +65,9 @@ public class WholeProjectAnalyzerFacade { } }; + @NotNull public static BindingContext analyzeProjectWithCacheOnAFile(@NotNull JetFile file) { return AnalyzerFacade.analyzeFileWithCache(file, WHOLE_PROJECT_DECLARATION_PROVIDER); } - } diff --git a/idea/testData/completion/basic/FromImports.kt b/idea/testData/completion/basic/FromImports.kt new file mode 100644 index 00000000000..893b30bbadb --- /dev/null +++ b/idea/testData/completion/basic/FromImports.kt @@ -0,0 +1,9 @@ +namespace Tests + +import java.util.* + +fun hello() { + val a = So +} + +// EXIST: SortedSet, SortedMap diff --git a/idea/testData/completion/basic/JavaPackage.kt b/idea/testData/completion/basic/JavaPackage.kt new file mode 100644 index 00000000000..59e1767100b --- /dev/null +++ b/idea/testData/completion/basic/JavaPackage.kt @@ -0,0 +1,6 @@ +namespace Tests + +class A : java. + +// EXIST: lang, util, io +// ABSENT: fun, val, var, namespace diff --git a/idea/testData/completion/basic/VariableClassName.kt b/idea/testData/completion/basic/VariableClassName.kt index 0296a23af4c..38c1068b400 100644 --- a/idea/testData/completion/basic/VariableClassName.kt +++ b/idea/testData/completion/basic/VariableClassName.kt @@ -1,10 +1,10 @@ open class MyClass() { } -class A() : My { +class A() { public fun test() { val a : MyC } } -// EXIST: MyClass \ No newline at end of file +// EXIST: MyClass diff --git a/idea/testData/completion/keywords/InTypeScope.kt b/idea/testData/completion/keywords/InTypeScope.kt index 31f2249d499..c8a490df14c 100644 --- a/idea/testData/completion/keywords/InTypeScope.kt +++ b/idea/testData/completion/keywords/InTypeScope.kt @@ -3,4 +3,4 @@ fun foo() { } // TODO: Move all keywords to absent -// EXPECT: fun, val, var, namespace +// EXIST: fun, val, var, namespace diff --git a/idea/testData/completion/keywords/TopScope.kt b/idea/testData/completion/keywords/TopScope.kt index a151d11e196..308a7710384 100644 --- a/idea/testData/completion/keywords/TopScope.kt +++ b/idea/testData/completion/keywords/TopScope.kt @@ -1,8 +1,8 @@ -// EXPECT: namespace, as, type, class, this, super, val, var, fun, for, null, true -// EXPECT: false, is, in, throw, return, break, continue, object, if, try, else, while -// EXPECT: do, when, trait, This -// EXPECT: import, where, by, get, set, abstract, enum, open, annotation, override, private -// EXPECT: public, internal, protected, catch, out, vararg, inline, finally, final, ref +// EXIST: namespace, as, type, class, this, super, val, var, fun, for, null, true +// EXIST: false, is, in, throw, return, break, continue, object, if, try, else, while +// EXIST: do, when, trait, This +// EXIST: import, where, by, get, set, abstract, enum, open, annotation, override, private +// EXIST: public, internal, protected, catch, out, vararg, inline, finally, final, ref // ABSENT: ?in, new, extends, implements \ 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 14bc6d16912..995363c1be8 100644 --- a/idea/tests/org/jetbrains/jet/completion/JetBasicCompletionTest.java +++ b/idea/tests/org/jetbrains/jet/completion/JetBasicCompletionTest.java @@ -23,7 +23,7 @@ public class JetBasicCompletionTest extends JetCompletionTestBase { setName("testCompletionExecute"); } - public void testCompletionExecute() { + public void testCompletionExecute() throws Exception { doTest(); } diff --git a/idea/tests/org/jetbrains/jet/completion/JetCompletionTestBase.java b/idea/tests/org/jetbrains/jet/completion/JetCompletionTestBase.java index c0ff4263210..39372cd71c2 100644 --- a/idea/tests/org/jetbrains/jet/completion/JetCompletionTestBase.java +++ b/idea/tests/org/jetbrains/jet/completion/JetCompletionTestBase.java @@ -2,70 +2,64 @@ package org.jetbrains.jet.completion; import com.intellij.codeInsight.completion.CodeCompletionHandlerBase; import com.intellij.codeInsight.completion.CompletionType; +import com.intellij.codeInsight.completion.LightCompletionTestCase; import com.intellij.codeInsight.lookup.LookupElement; -import com.intellij.codeInsight.lookup.LookupEx; import com.intellij.codeInsight.lookup.LookupManager; -import com.intellij.testFramework.LightCodeInsightTestCase; +import com.intellij.codeInsight.lookup.impl.LookupImpl; +import com.intellij.openapi.projectRoots.Sdk; import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.plugin.PluginTestCaseBase; import java.io.BufferedReader; import java.io.IOException; import java.io.StringReader; import java.util.ArrayList; -import java.util.HashSet; import java.util.List; /** * @author Nikolay.Krasko */ -public abstract class JetCompletionTestBase extends LightCodeInsightTestCase { +public abstract class JetCompletionTestBase extends LightCompletionTestCase { - protected void doTest() { + private CompletionType type; + + protected void doTest() throws Exception { final String testName = getTestName(false); - configureByFile(testName + ".kt"); - CompletionType completionType = (testName.startsWith("Smart")) ? CompletionType.SMART : CompletionType.BASIC; - new CodeCompletionHandlerBase(completionType, false, false, true).invokeCompletion(getProject(), getEditor()); + type = (testName.startsWith("Smart")) ? CompletionType.SMART : CompletionType.BASIC; - LookupEx lookup = LookupManager.getActiveLookup(getEditor()); - assert lookup != null; + configureByFile(testName + ".kt"); - HashSet items = new HashSet(resolveLookups(lookup.getItems())); + assertContainsItems(itemsShouldExist(getFile().getText())); + assertNotContainItems(itemsShouldAbsent(getFile().getText())); + } - List shouldExist = itemsShouldExist(getFile().getText()); - for (String shouldExistItem : shouldExist) { - assertTrue(String.format("Should contain proposal '%s'.", shouldExistItem), - items.contains(shouldExistItem)); - } - - List shouldAbsent = itemsShouldAbsent(getFile().getText()); - for (String shouldAbsentItem : shouldAbsent) { - assertTrue(String.format("Shouldn't contain proposal '%s'.", shouldAbsentItem), - !items.contains(shouldAbsentItem)); - } + @Override + protected Sdk getProjectJDK() { + return PluginTestCaseBase.jdkFromIdeaHome(); } - - private static List resolveLookups(List items) { - ArrayList result = new ArrayList(items.size()); - for (LookupElement item : items) { - result.add(item.getLookupString()); - } - return result; + @Override + protected void complete(final int time) { + new CodeCompletionHandlerBase(type, false, false, true).invokeCompletion(getProject(), getEditor(), time, false); + + LookupImpl lookup = (LookupImpl) LookupManager.getActiveLookup(myEditor); + myItems = lookup == null ? null : lookup.getItems().toArray(LookupElement.EMPTY_ARRAY); + myPrefix = lookup == null ? null : lookup.itemPattern(lookup.getItems().get(0)); } @NotNull - private static List itemsShouldExist(String fileText) { + private static String[] itemsShouldExist(String fileText) { return findListWithPrefix("// EXIST:", fileText); } @NotNull - private static List itemsShouldAbsent(String fileText) { + private static String[] itemsShouldAbsent(String fileText) { return findListWithPrefix("// ABSENT:", fileText); } @NotNull - private static List findListWithPrefix(String prefix, String fileText) { + private static String[] findListWithPrefix(String prefix, String fileText) { ArrayList result = new ArrayList(); for (String line : findLinesWithPrefixRemoved(prefix, fileText)) { @@ -76,7 +70,7 @@ public abstract class JetCompletionTestBase extends LightCodeInsightTestCase { } } - return result; + return result.toArray(new String[result.size()]); } @NotNull diff --git a/idea/tests/org/jetbrains/jet/completion/KeywordsCompletionTest.java b/idea/tests/org/jetbrains/jet/completion/KeywordsCompletionTest.java index 1df0d5ef2cf..4a0e14d9ed4 100644 --- a/idea/tests/org/jetbrains/jet/completion/KeywordsCompletionTest.java +++ b/idea/tests/org/jetbrains/jet/completion/KeywordsCompletionTest.java @@ -1,5 +1,6 @@ package org.jetbrains.jet.completion; +import junit.framework.Test; import junit.framework.TestSuite; import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.JetTestCaseBuilder; @@ -25,7 +26,7 @@ public class KeywordsCompletionTest extends JetCompletionTestBase { setName("testCompletionExecute"); } - public void testCompletionExecute() { + public void testCompletionExecute() throws Exception { doTest(); } @@ -49,13 +50,14 @@ public class KeywordsCompletionTest extends JetCompletionTestBase { PluginTestCaseBase.getTestDataPathBase(), "/completion/keywords/", false, JetTestCaseBuilder.emptyFilter, new JetTestCaseBuilder.NamedTestFactory() { + @NotNull @Override - public junit.framework.Test createTest(@NotNull String dataPath, @NotNull String name, @NotNull File file) { + public Test createTest(@NotNull String dataPath, @NotNull String name, @NotNull File file) { return new KeywordsCompletionTest(dataPath, name); } }, suite); return suite; } -} \ No newline at end of file +} -- GitLab