From 77150f02d92b2c69be0a99cfb5b65fcc75033aad Mon Sep 17 00:00:00 2001 From: Nikolay Krasko Date: Mon, 5 Mar 2012 18:36:12 +0400 Subject: [PATCH] KT-1373 Completion suggests constructors in type position KT-1316 Type name completion inserts fully qualified name when works in incomplete code --- .../jet/lang/types/JetStandardClasses.java | 5 ++ .../jet/lang/types/JetStandardLibrary.java | 18 ++++++- .../jet/plugin/caches/JetShortNamesCache.java | 38 ++++++++++----- .../JetClassCompletionContributor.java | 23 ++++++++- .../completion/JetCompletionContributor.java | 39 ++++++++++----- .../JetKeywordCompletionContributor.java | 2 - .../completion/basic/InTypeAnnotation.kt | 14 ++++++ .../completion/class/InExpressionNoPrefix.kt | 8 ++++ .../class/InExtendTypeAnnotation.kt | 7 +++ .../handlers/ClassCompletionImport.kt | 3 ++ .../handlers/ClassCompletionImport.kt.after | 5 ++ .../completion/handlers/ParamsFunction.kt | 2 + .../handlers/ParamsFunction.kt.after | 2 + .../completion/handlers/PropertiesSetter.kt | 2 + .../handlers/PropertiesSetter.kt.after | 2 + .../completion/JetBasicCompletionTest.java | 4 ++ .../completion/JetClassCompletionTest.java | 47 +++++++++++++++++++ .../jet/completion/JetCompletionTestBase.java | 8 ++-- .../handlers/CompletionHandlerTest.java | 21 ++++++++- 19 files changed, 218 insertions(+), 32 deletions(-) create mode 100644 idea/testData/completion/basic/InTypeAnnotation.kt create mode 100644 idea/testData/completion/class/InExpressionNoPrefix.kt create mode 100644 idea/testData/completion/class/InExtendTypeAnnotation.kt create mode 100644 idea/testData/completion/handlers/ClassCompletionImport.kt create mode 100644 idea/testData/completion/handlers/ClassCompletionImport.kt.after create mode 100644 idea/tests/org/jetbrains/jet/completion/JetClassCompletionTest.java diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/JetStandardClasses.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/JetStandardClasses.java index d763b3e5ed6..a982f425804 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/JetStandardClasses.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/JetStandardClasses.java @@ -425,4 +425,9 @@ public class JetStandardClasses { List arguments = type.getArguments(); return arguments.get(arguments.size() - 1).getType(); } + + @NotNull + public static Collection getAllStandardClasses() { + return STANDARD_CLASSES.getAllDescriptors(); + } } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/JetStandardLibrary.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/JetStandardLibrary.java index f3086e20f22..93cbee3e311 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/JetStandardLibrary.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/JetStandardLibrary.java @@ -193,6 +193,22 @@ public class JetStandardLibrary { jetArrayTypeToPrimitiveJetType.put(arrayType, type); } + public Collection getStandardTypes() { + initStdClasses(); + + Collection classDescriptors = new ArrayList(primitiveTypeToClass.values()); + classDescriptors.add(numberClass); + classDescriptors.add(stringClass); + classDescriptors.add(charSequenceClass); + classDescriptors.add(arrayClass); + classDescriptors.add(volatileClass); + classDescriptors.add(throwableClass); + classDescriptors.add(iterableClass); + classDescriptors.add(comparableClass); + + return classDescriptors; + } + @NotNull public ClassDescriptor getNumber() { initStdClasses(); @@ -258,7 +274,7 @@ public class JetStandardLibrary { } @NotNull - public ClassDescriptor getArray() { + public ClassDescriptor getArray() { initStdClasses(); return arrayClass; } diff --git a/idea/src/org/jetbrains/jet/plugin/caches/JetShortNamesCache.java b/idea/src/org/jetbrains/jet/plugin/caches/JetShortNamesCache.java index dd9f3e6fbcb..ed8c4cc5527 100644 --- a/idea/src/org/jetbrains/jet/plugin/caches/JetShortNamesCache.java +++ b/idea/src/org/jetbrains/jet/plugin/caches/JetShortNamesCache.java @@ -16,6 +16,7 @@ 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; @@ -32,9 +33,13 @@ 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.ClassDescriptor; +import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; import org.jetbrains.jet.lang.descriptors.SimpleFunctionDescriptor; import org.jetbrains.jet.lang.psi.JetNamedFunction; import org.jetbrains.jet.lang.resolve.BindingContext; +import org.jetbrains.jet.lang.types.JetStandardClasses; +import org.jetbrains.jet.lang.types.JetStandardLibrary; import org.jetbrains.jet.plugin.compiler.WholeProjectAnalyzerFacade; import org.jetbrains.jet.plugin.stubindex.JetExtensionFunctionNameIndex; import org.jetbrains.jet.plugin.stubindex.JetFullClassNameIndex; @@ -42,10 +47,7 @@ import org.jetbrains.jet.plugin.stubindex.JetShortClassNameIndex; import org.jetbrains.jet.plugin.stubindex.JetShortFunctionNameIndex; import org.jetbrains.jet.util.QualifiedNamesUtil; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Set; +import java.util.*; /** * Will provide both java elements from jet context and some special declarations special to jet. @@ -79,7 +81,6 @@ public class JetShortNamesCache extends PsiShortNamesCache { @NotNull @Override public PsiClass[] getClassesByName(@NotNull @NonNls String name, @NotNull GlobalSearchScope scope) { - // Quick check for classes from getAllClassNames() Collection classNames = JetShortClassNameIndex.getInstance().getAllKeys(project); if (!classNames.contains(name)) { @@ -101,14 +102,29 @@ public class JetShortNamesCache extends PsiShortNamesCache { } @Override - public void getAllClassNames(@NotNull HashSet dest) { - // TODO: Implement it. Is it called somewhere? + public void getAllClassNames(@NotNull HashSet destination) { + destination.addAll(Arrays.asList(getAllClassNames())); } -// public Collection getALlJetClassFQNames() { -// final BindingContext context = getResolutionContext(GlobalSearchScope.allScope(project)); -// return context.getKeys(BindingContext.FQNAME_TO_CLASS_DESCRIPTOR); -// } + /** + * Types that should be visible in completion from kotlin but should be absent in java. + * @return + */ + @NotNull + public static Collection getJetOnlyTypes() { + Collection standardTypes = JetStandardClasses.getAllStandardClasses(); + standardTypes.addAll( + Collections2.transform(JetStandardLibrary.getInstance().getStandardTypes(), + new Function() { + @Override + public DeclarationDescriptor apply(@Nullable ClassDescriptor classDescriptor) { + assert classDescriptor != null; + return classDescriptor; + } + })); + + return standardTypes; + } @NotNull public Collection getFQNamesByName(@NotNull final String name, @NotNull GlobalSearchScope scope) { diff --git a/idea/src/org/jetbrains/jet/plugin/completion/JetClassCompletionContributor.java b/idea/src/org/jetbrains/jet/plugin/completion/JetClassCompletionContributor.java index cdd22def898..016880ed147 100644 --- a/idea/src/org/jetbrains/jet/plugin/completion/JetClassCompletionContributor.java +++ b/idea/src/org/jetbrains/jet/plugin/completion/JetClassCompletionContributor.java @@ -18,16 +18,24 @@ package org.jetbrains.jet.plugin.completion; import com.intellij.codeInsight.completion.*; import com.intellij.codeInsight.lookup.LookupElement; +import com.intellij.openapi.project.Project; import com.intellij.patterns.PlatformPatterns; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiReference; +import com.intellij.psi.search.GlobalSearchScope; import com.intellij.util.Consumer; import com.intellij.util.ProcessingContext; import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; import org.jetbrains.jet.lang.psi.JetFile; +import org.jetbrains.jet.lang.resolve.BindingContext; +import org.jetbrains.jet.plugin.caches.JetCacheManager; +import org.jetbrains.jet.plugin.caches.JetShortNamesCache; import org.jetbrains.jet.plugin.completion.handlers.JetJavaClassInsertHandler; import org.jetbrains.jet.plugin.references.JetSimpleNameReference; +import java.util.Collection; + /** * @author Nikolay Krasko */ @@ -67,9 +75,22 @@ public class JetClassCompletionContributor extends CompletionContributor { @NotNull final Consumer consumer) { CompletionResultSet tempResult = result.withPrefixMatcher(CompletionUtil.findReferenceOrAlphanumericPrefix(parameters)); + + Project project = parameters.getPosition().getProject(); + + JetShortNamesCache namesCache = JetCacheManager.getInstance(project).getNamesCache(); + + // TODO: Make icon for standard types + Collection jetOnlyClasses = JetShortNamesCache.getJetOnlyTypes(); + BindingContext bindingContext = namesCache.getResolutionContext(GlobalSearchScope.allScope(project)); + + for (DeclarationDescriptor jetOnlyClass : jetOnlyClasses) { + consumer.consume(DescriptorLookupConverter.createLookupElement(bindingContext, jetOnlyClass)); + } + JavaClassNameCompletionContributor.addAllClasses( parameters, - parameters.getInvocationCount() <= 2, + false, JavaCompletionSorting.addJavaSorting(parameters, tempResult).getPrefixMatcher(), new Consumer() { @Override diff --git a/idea/src/org/jetbrains/jet/plugin/completion/JetCompletionContributor.java b/idea/src/org/jetbrains/jet/plugin/completion/JetCompletionContributor.java index e0417995695..4ae0f97eaba 100644 --- a/idea/src/org/jetbrains/jet/plugin/completion/JetCompletionContributor.java +++ b/idea/src/org/jetbrains/jet/plugin/completion/JetCompletionContributor.java @@ -62,10 +62,6 @@ public class JetCompletionContributor extends CompletionContributor { Set positions = new HashSet(); - if (result.getPrefixMatcher().getPrefix().isEmpty()) { - return; - } - PsiElement position = parameters.getPosition(); if (!(position.getContainingFile() instanceof JetFile)) { return; @@ -73,12 +69,23 @@ public class JetCompletionContributor extends CompletionContributor { JetSimpleNameReference jetReference = getJetReference(parameters); if (jetReference != null) { + + if (shouldRunTypeCompletionOnly(position, jetReference)) { + addClasses(parameters, result); + result.stopHere(); + return; + } + for (Object variant : jetReference.getVariants()) { addReferenceVariant(result, variant, positions); } + if (result.getPrefixMatcher().getPrefix().isEmpty()) { + return; + } + if (shouldRunTopLevelCompletion(parameters)) { - addClasses(parameters, result, positions); + addClasses(parameters, result); addJetTopLevelFunctions(result, position, positions); addJetExtensionFunctions(jetReference.getExpression(), result, position); } @@ -95,7 +102,6 @@ public class JetCompletionContributor extends CompletionContributor { BindingContext context = WholeProjectAnalyzerFacade.analyzeProjectWithCacheOnAFile((JetFile) position.getContainingFile()); JetExpression receiverExpression = expression.getReceiverExpression(); - if (receiverExpression != null) { JetType expressionType = context.get(BindingContext.EXPRESSION_TYPE, receiverExpression); JetScope scope = context.get(BindingContext.RESOLUTION_SCOPE, receiverExpression); @@ -185,17 +191,28 @@ public class JetCompletionContributor extends CompletionContributor { */ private static void addClasses( @NotNull CompletionParameters parameters, - @NotNull final CompletionResultSet result, - @NotNull final Set positions) { - + @NotNull final CompletionResultSet result + ) { JetClassCompletionContributor.addClasses(parameters, result, new Consumer() { @Override public void consume(@NotNull LookupElement element) { - addCompletionToResult(result, element, positions); + result.addElement(element); } }); } + private static boolean shouldRunTypeCompletionOnly(PsiElement position, JetSimpleNameReference jetReference) { + // Check that completion in the type annotation context and if there's a qualified + // expression we are at first of it + JetTypeReference typeReference = PsiTreeUtil.getParentOfType(position, JetTypeReference.class); + if (typeReference != null) { + JetSimpleNameExpression firstPartReference = PsiTreeUtil.findChildOfType(typeReference, JetSimpleNameExpression.class); + return firstPartReference == jetReference.getExpression(); + } + + return false; + } + private static boolean shouldRunTopLevelCompletion(@NotNull CompletionParameters parameters) { PsiElement element = parameters.getPosition(); @@ -260,11 +277,9 @@ public class JetCompletionContributor extends CompletionContributor { private static LookupPositionObject getLookupPosition(LookupElement element) { 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; PsiElement psiElement = ((JetLookupObject) lookupObject).getPsiElement(); if (psiElement != null) { return new LookupPositionObject(psiElement); diff --git a/idea/src/org/jetbrains/jet/plugin/completion/JetKeywordCompletionContributor.java b/idea/src/org/jetbrains/jet/plugin/completion/JetKeywordCompletionContributor.java index 17ea49e999b..bd186c66e0d 100644 --- a/idea/src/org/jetbrains/jet/plugin/completion/JetKeywordCompletionContributor.java +++ b/idea/src/org/jetbrains/jet/plugin/completion/JetKeywordCompletionContributor.java @@ -344,6 +344,4 @@ public class JetKeywordCompletionContributor extends CompletionContributor { new FilterPattern(new AndFilter(GENERAL_FILTER, placeFilter))); } } - - } diff --git a/idea/testData/completion/basic/InTypeAnnotation.kt b/idea/testData/completion/basic/InTypeAnnotation.kt new file mode 100644 index 00000000000..b4f6904c5f0 --- /dev/null +++ b/idea/testData/completion/basic/InTypeAnnotation.kt @@ -0,0 +1,14 @@ +public StringMethod() : String { + +} + +open class StringMy() { +} + +public class Test : String { + +} + +// EXIST: StringMy +// EXIST: String +// ABSENT: StringMethod \ No newline at end of file diff --git a/idea/testData/completion/class/InExpressionNoPrefix.kt b/idea/testData/completion/class/InExpressionNoPrefix.kt new file mode 100644 index 00000000000..5807fdfa5dd --- /dev/null +++ b/idea/testData/completion/class/InExpressionNoPrefix.kt @@ -0,0 +1,8 @@ +class Test { + fun test() { + + } +} + +// EXIST: Any, Nothing, Tuple0, Int, Number +// EXIST: Array, Math, Hashable, OutputStream \ No newline at end of file diff --git a/idea/testData/completion/class/InExtendTypeAnnotation.kt b/idea/testData/completion/class/InExtendTypeAnnotation.kt new file mode 100644 index 00000000000..59d807c92a7 --- /dev/null +++ b/idea/testData/completion/class/InExtendTypeAnnotation.kt @@ -0,0 +1,7 @@ +class Test : { + fun test() { + } +} + +// EXIST: Any, Nothing, Tuple0, Int, Number +// EXIST: Array, Math, Hashable, OutputStream \ No newline at end of file diff --git a/idea/testData/completion/handlers/ClassCompletionImport.kt b/idea/testData/completion/handlers/ClassCompletionImport.kt new file mode 100644 index 00000000000..68e45b92a0a --- /dev/null +++ b/idea/testData/completion/handlers/ClassCompletionImport.kt @@ -0,0 +1,3 @@ +package classCompletionImport + +fun other() : SortedS diff --git a/idea/testData/completion/handlers/ClassCompletionImport.kt.after b/idea/testData/completion/handlers/ClassCompletionImport.kt.after new file mode 100644 index 00000000000..3e4d4f1aa17 --- /dev/null +++ b/idea/testData/completion/handlers/ClassCompletionImport.kt.after @@ -0,0 +1,5 @@ +package classCompletionImport + +import java.util.SortedSet + +fun other() : SortedSet diff --git a/idea/testData/completion/handlers/ParamsFunction.kt b/idea/testData/completion/handlers/ParamsFunction.kt index c7910930150..3de5e84dac7 100644 --- a/idea/testData/completion/handlers/ParamsFunction.kt +++ b/idea/testData/completion/handlers/ParamsFunction.kt @@ -1,3 +1,5 @@ +package somefortest + fun test(a: Int) { } diff --git a/idea/testData/completion/handlers/ParamsFunction.kt.after b/idea/testData/completion/handlers/ParamsFunction.kt.after index 175892cc3a5..c4d0a598061 100644 --- a/idea/testData/completion/handlers/ParamsFunction.kt.after +++ b/idea/testData/completion/handlers/ParamsFunction.kt.after @@ -1,3 +1,5 @@ +package somefortest + fun test(a: Int) { } diff --git a/idea/testData/completion/handlers/PropertiesSetter.kt b/idea/testData/completion/handlers/PropertiesSetter.kt index 07e43cdaf16..a6e2193410e 100644 --- a/idea/testData/completion/handlers/PropertiesSetter.kt +++ b/idea/testData/completion/handlers/PropertiesSetter.kt @@ -1,3 +1,5 @@ +package propertiesSetter + class TestClass { val a : Int ge diff --git a/idea/testData/completion/handlers/PropertiesSetter.kt.after b/idea/testData/completion/handlers/PropertiesSetter.kt.after index acf5481fb09..9fa5eb2f4a0 100644 --- a/idea/testData/completion/handlers/PropertiesSetter.kt.after +++ b/idea/testData/completion/handlers/PropertiesSetter.kt.after @@ -1,3 +1,5 @@ +package propertiesSetter + class TestClass { val a : Int get() diff --git a/idea/tests/org/jetbrains/jet/completion/JetBasicCompletionTest.java b/idea/tests/org/jetbrains/jet/completion/JetBasicCompletionTest.java index 24927696af2..9defa10a27e 100644 --- a/idea/tests/org/jetbrains/jet/completion/JetBasicCompletionTest.java +++ b/idea/tests/org/jetbrains/jet/completion/JetBasicCompletionTest.java @@ -77,6 +77,10 @@ public class JetBasicCompletionTest extends JetCompletionTestBase { doTest(); } + public void testInTypeAnnotation() { + doTest(); + } + public void testJavaClassNames() { doTest(); } diff --git a/idea/tests/org/jetbrains/jet/completion/JetClassCompletionTest.java b/idea/tests/org/jetbrains/jet/completion/JetClassCompletionTest.java new file mode 100644 index 00000000000..f2b9668a9b3 --- /dev/null +++ b/idea/tests/org/jetbrains/jet/completion/JetClassCompletionTest.java @@ -0,0 +1,47 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.jet.completion; + +import com.intellij.codeInsight.completion.CompletionType; +import org.jetbrains.jet.plugin.PluginTestCaseBase; + +import java.io.File; + +/** + * @author Nikolay Krasko + */ +public class JetClassCompletionTest extends JetCompletionTestBase { + + public void testInExpressionNoPrefix() { + doTest(); + } + + public void testInExtendTypeAnnotation() { + doTest(); + } + + @Override + protected CompletionType getCompletionType(String testName, String fileText) { + return CompletionType.CLASS_NAME; + } + + @Override + protected String getTestDataPath() { + return new File(PluginTestCaseBase.getTestDataPathBase(), "/completion/class").getPath() + + File.separator; + } +} diff --git a/idea/tests/org/jetbrains/jet/completion/JetCompletionTestBase.java b/idea/tests/org/jetbrains/jet/completion/JetCompletionTestBase.java index a9218f58514..8bb8254ff6a 100644 --- a/idea/tests/org/jetbrains/jet/completion/JetCompletionTestBase.java +++ b/idea/tests/org/jetbrains/jet/completion/JetCompletionTestBase.java @@ -45,12 +45,10 @@ public abstract class JetCompletionTestBase extends LightCompletionTestCase { protected void doTest() { try { final String testName = getTestName(false); - - type = (testName.startsWith("Smart")) ? CompletionType.SMART : CompletionType.BASIC; - configureByFileNoComplete(testName + ".kt"); final String fileText = getFile().getText(); + type = getCompletionType(testName, fileText); boolean withKotlinRuntime = ExpectedCompletionUtils.getPrefixedInt(fileText, "// RUNTIME:") != null; @@ -88,6 +86,10 @@ public abstract class JetCompletionTestBase extends LightCompletionTestCase { } } + protected CompletionType getCompletionType(String testName, String fileText) { + return (testName.startsWith("Smart")) ? CompletionType.SMART : CompletionType.BASIC; + } + protected static void configureWithKotlinRuntime() { final ModuleRootManager rootManager = ModuleRootManager.getInstance(getModule()); final ModifiableRootModel rootModel = rootManager.getModifiableModel(); diff --git a/idea/tests/org/jetbrains/jet/completion/handlers/CompletionHandlerTest.java b/idea/tests/org/jetbrains/jet/completion/handlers/CompletionHandlerTest.java index fa1134582ce..918ebc66ef1 100644 --- a/idea/tests/org/jetbrains/jet/completion/handlers/CompletionHandlerTest.java +++ b/idea/tests/org/jetbrains/jet/completion/handlers/CompletionHandlerTest.java @@ -16,8 +16,11 @@ package org.jetbrains.jet.completion.handlers; +import com.intellij.codeInsight.completion.CompletionType; import com.intellij.codeInsight.completion.LightCompletionTestCase; +import com.intellij.codeInsight.lookup.LookupElementBuilder; import com.intellij.openapi.projectRoots.Sdk; +import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.plugin.PluginTestCaseBase; import java.io.File; @@ -27,6 +30,10 @@ import java.io.File; */ public class CompletionHandlerTest extends LightCompletionTestCase { + public void testClassCompletionImport() { + doTest(CompletionType.CLASS_NAME, 1, "SortedSet"); + } + public void testNoParamsFunction() { doTest(); } @@ -54,15 +61,25 @@ public class CompletionHandlerTest extends LightCompletionTestCase { } public void doTest() { + doTest(CompletionType.BASIC, 2, null); + } + + public void doTest(CompletionType type, int time, @Nullable String completeItem) { String fileName = getTestName(false); try { configureByFileNoComplete(fileName + ".kt"); - complete(2); + setType(type); + + complete(time); + + if (completeItem != null) { + selectItem(LookupElementBuilder.create(completeItem), '\t'); + } + checkResultByFile(fileName + ".kt.after"); } catch (Exception e) { throw new AssertionError(e); } - } @Override