diff --git a/compiler/tests/org/jetbrains/jet/InTextDirectivesUtils.java b/compiler/tests/org/jetbrains/jet/InTextDirectivesUtils.java index 4d218f552f0..94c9f1b5554 100644 --- a/compiler/tests/org/jetbrains/jet/InTextDirectivesUtils.java +++ b/compiler/tests/org/jetbrains/jet/InTextDirectivesUtils.java @@ -36,9 +36,21 @@ public final class InTextDirectivesUtils { @Nullable public static Integer getPrefixedInt(String fileText, String prefix) { - String[] numberStrings = findArrayWithPrefixes(fileText, prefix); - if (numberStrings.length > 0) { - return Integer.parseInt(numberStrings[0]); + String[] strings = findArrayWithPrefixes(fileText, prefix); + if (strings.length > 0) { + assert strings.length == 1; + return Integer.parseInt(strings[0]); + } + + return null; + } + + @Nullable + public static Boolean getPrefixedBoolean(String fileText, String prefix) { + String[] strings = findArrayWithPrefixes(fileText, prefix); + if (strings.length > 0) { + assert strings.length == 1; + return Boolean.parseBoolean(strings[0]); } return null; diff --git a/idea/src/org/jetbrains/jet/plugin/completion/CompletionSession.kt b/idea/src/org/jetbrains/jet/plugin/completion/CompletionSession.kt index 5b8a2456e80..71efbdc0bc5 100644 --- a/idea/src/org/jetbrains/jet/plugin/completion/CompletionSession.kt +++ b/idea/src/org/jetbrains/jet/plugin/completion/CompletionSession.kt @@ -60,7 +60,6 @@ abstract class CompletionSessionBase(protected val configuration: CompletionSess protected val prefixMatcher: PrefixMatcher = this.resultSet.getPrefixMatcher() protected val collector: LookupElementsCollector = LookupElementsCollector(prefixMatcher, resolveSession, { isVisibleDescriptor(it) }) - protected var anythingAdded: Boolean = false protected val project: Project = position.getProject() protected val indicesHelper: KotlinIndicesHelper = KotlinIndicesHelper(project) @@ -77,16 +76,13 @@ abstract class CompletionSessionBase(protected val configuration: CompletionSess } protected fun flushToResultSet() { - if (!collector.isEmpty) { - anythingAdded = true - } collector.flushToResultSet(resultSet) } public fun complete(): Boolean { doComplete() flushToResultSet() - return anythingAdded + return !collector.isResultEmpty } protected abstract fun doComplete() @@ -157,11 +153,11 @@ class BasicCompletionSession(configuration: CompletionSessionConfiguration, private fun addNonImported() { if (shouldRunTopLevelCompletion()) { TypesCompletion(parameters, resolveSession, prefixMatcher).addAllTypes(collector) - collector.addDescriptorElements(getKotlinTopLevelDeclarations()) + collector.addDescriptorElements(getKotlinTopLevelDeclarations(), suppressAutoInsertion = true) } if (shouldRunExtensionsCompletion()) { - collector.addDescriptorElements(getKotlinExtensions()) + collector.addDescriptorElements(getKotlinExtensions(), suppressAutoInsertion = true) } } @@ -196,7 +192,7 @@ class BasicCompletionSession(configuration: CompletionSessionConfiguration, private fun addReferenceVariants(filterCondition: (DeclarationDescriptor) -> Boolean = { true }) { val descriptors = TipsManager.getReferenceVariants(jetReference!!.expression, bindingContext!!) - collector.addDescriptorElements(descriptors.filter { filterCondition(it) }) + collector.addDescriptorElements(descriptors.filter { filterCondition(it) }, suppressAutoInsertion = false) } } diff --git a/idea/src/org/jetbrains/jet/plugin/completion/LookupElementsCollector.kt b/idea/src/org/jetbrains/jet/plugin/completion/LookupElementsCollector.kt index a31c81e14e1..4f801940a34 100644 --- a/idea/src/org/jetbrains/jet/plugin/completion/LookupElementsCollector.kt +++ b/idea/src/org/jetbrains/jet/plugin/completion/LookupElementsCollector.kt @@ -35,23 +35,35 @@ class LookupElementsCollector(private val prefixMatcher: PrefixMatcher, private val elements = ArrayList() public fun flushToResultSet(resultSet: CompletionResultSet) { - resultSet.addAllElements(elements) - elements.clear() - } - - public val isEmpty: Boolean - get() = elements.isEmpty() - - public fun addDescriptorElements(descriptors: Iterable) { - for (descriptor in descriptors) { - addDescriptorElements(descriptor) + if (!elements.isEmpty()) { + resultSet.addAllElements(elements) + elements.clear() + isResultEmpty = false } } - public fun addDescriptorElements(descriptor: DeclarationDescriptor) { + public var isResultEmpty: Boolean = true + private set + + public fun addDescriptorElements(descriptors: Iterable, + suppressAutoInsertion: Boolean // auto-insertion suppression is used for elements that require adding an import + ) { + for (descriptor in descriptors) { + addDescriptorElements(descriptor, suppressAutoInsertion) + } + } + + public fun addDescriptorElements(descriptor: DeclarationDescriptor, suppressAutoInsertion: Boolean) { if (!descriptorFilter(descriptor)) return - addElement(DescriptorLookupConverter.createLookupElement(resolveSession, descriptor)) + run { + var lookupElement = DescriptorLookupConverter.createLookupElement(resolveSession, descriptor) + if (suppressAutoInsertion && + elements.isEmpty() && isResultEmpty /* without these checks we would get duplicated items */) { + lookupElement = lookupElement.suppressAutoInsertion() + } + addElement(lookupElement) + } // add special item for function with one argument of function type with more than one parameter if (descriptor is FunctionDescriptor) { diff --git a/idea/src/org/jetbrains/jet/plugin/completion/TypesCompletion.kt b/idea/src/org/jetbrains/jet/plugin/completion/TypesCompletion.kt index df866a2819a..ceefd195a0a 100644 --- a/idea/src/org/jetbrains/jet/plugin/completion/TypesCompletion.kt +++ b/idea/src/org/jetbrains/jet/plugin/completion/TypesCompletion.kt @@ -37,11 +37,13 @@ import org.jetbrains.jet.plugin.search.searchScopeForSourceElementDependencies class TypesCompletion(val parameters: CompletionParameters, val resolveSession: ResolveSessionForBodies, val prefixMatcher: PrefixMatcher) { fun addAllTypes(result: LookupElementsCollector) { - result.addDescriptorElements(KotlinBuiltIns.getInstance().getNonPhysicalClasses()) + result.addDescriptorElements(KotlinBuiltIns.getInstance().getNonPhysicalClasses().filter { prefixMatcher.prefixMatches(it.getName().asString()) }, + suppressAutoInsertion = true) val project = parameters.getOriginalFile().getProject() val searchScope = searchScopeForSourceElementDependencies(parameters.getOriginalFile()) ?: return - result.addDescriptorElements(KotlinIndicesHelper(project).getClassDescriptors({ prefixMatcher.prefixMatches(it) }, resolveSession, searchScope)) + result.addDescriptorElements(KotlinIndicesHelper(project).getClassDescriptors({ prefixMatcher.prefixMatches(it) }, resolveSession, searchScope), + suppressAutoInsertion = true) if (!ProjectStructureUtil.isJsKotlinModule(parameters.getOriginalFile() as JetFile)) { addAdaptedJavaCompletion(result) @@ -63,7 +65,7 @@ class TypesCompletion(val parameters: CompletionParameters, val resolveSession: override fun handleInsert(context: InsertionContext) { JetJavaClassInsertHandler.handleInsert(context, lookupElement) } - }) + }.suppressAutoInsertion()) } } }) @@ -79,7 +81,8 @@ class TypesCompletion(val parameters: CompletionParameters, val resolveSession: if (JetFromJavaDescriptorHelper.getCompiledClassKind(aClass) != ClassKind.CLASS_OBJECT) { val qualifiedName = aClass.getQualifiedName() if (qualifiedName != null) { - result.addDescriptorElements(ResolveSessionUtils.getClassDescriptorsByFqName(resolveSession.getModuleDescriptor(), FqName(qualifiedName))) + result.addDescriptorElements(ResolveSessionUtils.getClassDescriptorsByFqName(resolveSession.getModuleDescriptor(), FqName(qualifiedName)), + suppressAutoInsertion = true) } } diff --git a/idea/testData/completion/basic/common/NoAutoInsertionOfNotImported.kt b/idea/testData/completion/basic/common/NoAutoInsertionOfNotImported.kt new file mode 100644 index 00000000000..490288b8683 --- /dev/null +++ b/idea/testData/completion/basic/common/NoAutoInsertionOfNotImported.kt @@ -0,0 +1,7 @@ +fun foo() { + PrintS +} + +// AUTOCOMPLETE_SETTING: true +// EXIST_JAVA_ONLY: PrintStream +// NUMBER_JAVA: 1 diff --git a/idea/testData/completion/basic/multifile/NoAutoInsertionOfNotImported/NoAutoInsertionOfNotImported.dependency.kt b/idea/testData/completion/basic/multifile/NoAutoInsertionOfNotImported/NoAutoInsertionOfNotImported.dependency.kt new file mode 100644 index 00000000000..4155be14028 --- /dev/null +++ b/idea/testData/completion/basic/multifile/NoAutoInsertionOfNotImported/NoAutoInsertionOfNotImported.dependency.kt @@ -0,0 +1,5 @@ +package second + +public class NotImportedClass + +// ALLOW_AST_ACCESS \ No newline at end of file diff --git a/idea/testData/completion/basic/multifile/NoAutoInsertionOfNotImported/NoAutoInsertionOfNotImported.kt b/idea/testData/completion/basic/multifile/NoAutoInsertionOfNotImported/NoAutoInsertionOfNotImported.kt new file mode 100644 index 00000000000..0243d233d2d --- /dev/null +++ b/idea/testData/completion/basic/multifile/NoAutoInsertionOfNotImported/NoAutoInsertionOfNotImported.kt @@ -0,0 +1,7 @@ +package first + +class A : NotImported + +// AUTOCOMPLETE_SETTING: true +// EXIST: NotImportedClass +// NUMBER: 1 diff --git a/idea/tests/org/jetbrains/jet/completion/CompletionTestUtil.kt b/idea/tests/org/jetbrains/jet/completion/CompletionTestUtil.kt index 65b82fc73a9..d34aaa71208 100644 --- a/idea/tests/org/jetbrains/jet/completion/CompletionTestUtil.kt +++ b/idea/tests/org/jetbrains/jet/completion/CompletionTestUtil.kt @@ -20,23 +20,47 @@ import com.intellij.codeInsight.lookup.LookupElement import org.jetbrains.jet.plugin.project.TargetPlatform import org.junit.Assert import org.jetbrains.jet.completion.ExpectedCompletionUtils +import com.intellij.codeInsight.CodeInsightSettings fun testCompletion(fileText: String, platform: TargetPlatform?, complete: (Int) -> Array?, defaultInvocationCount: Int = 0) { - val invocationCount = ExpectedCompletionUtils.getInvocationCount(fileText) ?: defaultInvocationCount - val items = complete(invocationCount) ?: array() + testWithAutoCompleteSetting(fileText) { + val invocationCount = ExpectedCompletionUtils.getInvocationCount(fileText) ?: defaultInvocationCount + val items = complete(invocationCount) ?: array() - ExpectedCompletionUtils.assertDirectivesValid(fileText) + ExpectedCompletionUtils.assertDirectivesValid(fileText) - val expected = ExpectedCompletionUtils.itemsShouldExist(fileText, platform) - val unexpected = ExpectedCompletionUtils.itemsShouldAbsent(fileText, platform) - val itemsNumber = ExpectedCompletionUtils.getExpectedNumber(fileText, platform) + val expected = ExpectedCompletionUtils.itemsShouldExist(fileText, platform) + val unexpected = ExpectedCompletionUtils.itemsShouldAbsent(fileText, platform) + val itemsNumber = ExpectedCompletionUtils.getExpectedNumber(fileText, platform) - Assert.assertTrue("Should be some assertions about completion", expected.size != 0 || unexpected.size != 0 || itemsNumber != null) - ExpectedCompletionUtils.assertContainsRenderedItems(expected, items, ExpectedCompletionUtils.isWithOrder(fileText)) - ExpectedCompletionUtils.assertNotContainsRenderedItems(unexpected, items) + Assert.assertTrue("Should be some assertions about completion", expected.size != 0 || unexpected.size != 0 || itemsNumber != null) + ExpectedCompletionUtils.assertContainsRenderedItems(expected, items, ExpectedCompletionUtils.isWithOrder(fileText)) + ExpectedCompletionUtils.assertNotContainsRenderedItems(unexpected, items) - if (itemsNumber != null) { - val expectedItems = ExpectedCompletionUtils.listToString(ExpectedCompletionUtils.getItemsInformation(items)) - Assert.assertEquals("Invalid number of completion items: ${expectedItems}", itemsNumber, items.size) + if (itemsNumber != null) { + val expectedItems = ExpectedCompletionUtils.listToString(ExpectedCompletionUtils.getItemsInformation(items)) + Assert.assertEquals("Invalid number of completion items: ${expectedItems}", itemsNumber, items.size) + } + } +} + +private fun testWithAutoCompleteSetting(fileText: String, doTest: () -> Unit) { + val autoComplete = ExpectedCompletionUtils.getAutocompleteSetting(fileText) + if (autoComplete == null) { + doTest() + return + } + + val settings = CodeInsightSettings.getInstance() + val oldValue1 = settings.AUTOCOMPLETE_ON_CODE_COMPLETION + val oldValue2 = settings.AUTOCOMPLETE_ON_SMART_TYPE_COMPLETION + try { + settings.AUTOCOMPLETE_ON_CODE_COMPLETION = autoComplete + settings.AUTOCOMPLETE_ON_SMART_TYPE_COMPLETION = autoComplete + doTest() + } + finally { + settings.AUTOCOMPLETE_ON_CODE_COMPLETION = oldValue1 + settings.AUTOCOMPLETE_ON_SMART_TYPE_COMPLETION = oldValue2 } } \ No newline at end of file diff --git a/idea/tests/org/jetbrains/jet/completion/ExpectedCompletionUtils.java b/idea/tests/org/jetbrains/jet/completion/ExpectedCompletionUtils.java index 0327db08d82..f50f7593006 100644 --- a/idea/tests/org/jetbrains/jet/completion/ExpectedCompletionUtils.java +++ b/idea/tests/org/jetbrains/jet/completion/ExpectedCompletionUtils.java @@ -115,6 +115,7 @@ public class ExpectedCompletionUtils { private static final String INVOCATION_COUNT_PREFIX = "INVOCATION_COUNT:"; private static final String WITH_ORDER_PREFIX = "WITH_ORDER:"; + private static final String AUTOCOMPLETE_SETTING_PREFIX = "AUTOCOMPLETE_SETTING:"; public static final List KNOWN_PREFIXES = ImmutableList.of( EXIST_LINE_PREFIX, @@ -128,6 +129,7 @@ public class ExpectedCompletionUtils { NUMBER_JAVA_LINE_PREFIX, INVOCATION_COUNT_PREFIX, WITH_ORDER_PREFIX, + AUTOCOMPLETE_SETTING_PREFIX, AstAccessControl.INSTANCE$.getALLOW_AST_ACCESS_DIRECTIVE()); @NotNull @@ -204,6 +206,11 @@ public class ExpectedCompletionUtils { return InTextDirectivesUtils.getPrefixedInt(fileText, INVOCATION_COUNT_PREFIX); } + @Nullable + public static Boolean getAutocompleteSetting(String fileText) { + return InTextDirectivesUtils.getPrefixedBoolean(fileText, AUTOCOMPLETE_SETTING_PREFIX); + } + public static boolean isWithOrder(String fileText) { return InTextDirectivesUtils.getPrefixedInt(fileText, WITH_ORDER_PREFIX) != null; } diff --git a/idea/tests/org/jetbrains/jet/completion/JSBasicCompletionTestGenerated.java b/idea/tests/org/jetbrains/jet/completion/JSBasicCompletionTestGenerated.java index 9baf9d3cfc5..ca65b0b1af2 100644 --- a/idea/tests/org/jetbrains/jet/completion/JSBasicCompletionTestGenerated.java +++ b/idea/tests/org/jetbrains/jet/completion/JSBasicCompletionTestGenerated.java @@ -420,6 +420,12 @@ public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTes doTest(fileName); } + @TestMetadata("NoAutoInsertionOfNotImported.kt") + public void testNoAutoInsertionOfNotImported() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/common/NoAutoInsertionOfNotImported.kt"); + doTest(fileName); + } + @TestMetadata("NoAutoPopupAfterNumberLiteral.kt") public void testNoAutoPopupAfterNumberLiteral() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/common/NoAutoPopupAfterNumberLiteral.kt"); diff --git a/idea/tests/org/jetbrains/jet/completion/JvmBasicCompletionTestGenerated.java b/idea/tests/org/jetbrains/jet/completion/JvmBasicCompletionTestGenerated.java index 2da1a783633..a0a4a44d3ca 100644 --- a/idea/tests/org/jetbrains/jet/completion/JvmBasicCompletionTestGenerated.java +++ b/idea/tests/org/jetbrains/jet/completion/JvmBasicCompletionTestGenerated.java @@ -420,6 +420,12 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT doTest(fileName); } + @TestMetadata("NoAutoInsertionOfNotImported.kt") + public void testNoAutoInsertionOfNotImported() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/common/NoAutoInsertionOfNotImported.kt"); + doTest(fileName); + } + @TestMetadata("NoAutoPopupAfterNumberLiteral.kt") public void testNoAutoPopupAfterNumberLiteral() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/common/NoAutoPopupAfterNumberLiteral.kt"); diff --git a/idea/tests/org/jetbrains/jet/completion/MultiFileJvmBasicCompletionTestGenerated.java b/idea/tests/org/jetbrains/jet/completion/MultiFileJvmBasicCompletionTestGenerated.java index 1ab8ed5c817..4b8302aad3b 100644 --- a/idea/tests/org/jetbrains/jet/completion/MultiFileJvmBasicCompletionTestGenerated.java +++ b/idea/tests/org/jetbrains/jet/completion/MultiFileJvmBasicCompletionTestGenerated.java @@ -112,6 +112,12 @@ public class MultiFileJvmBasicCompletionTestGenerated extends AbstractMultiFileJ doTest(fileName); } + @TestMetadata("NoAutoInsertionOfNotImported") + public void testNoAutoInsertionOfNotImported() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/multifile/NoAutoInsertionOfNotImported/"); + doTest(fileName); + } + @TestMetadata("NotImportedExtensionForImplicitReceiver") public void testNotImportedExtensionForImplicitReceiver() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/multifile/NotImportedExtensionForImplicitReceiver/"); diff --git a/idea/tests/org/jetbrains/jet/completion/handlers/CompletionMultifileHandlerTest.java b/idea/tests/org/jetbrains/jet/completion/handlers/CompletionMultifileHandlerTest.java index 2a6e019c052..1ad3ed97aba 100644 --- a/idea/tests/org/jetbrains/jet/completion/handlers/CompletionMultifileHandlerTest.java +++ b/idea/tests/org/jetbrains/jet/completion/handlers/CompletionMultifileHandlerTest.java @@ -67,6 +67,10 @@ public class CompletionMultifileHandlerTest extends KotlinCompletionTestCase { configureByFiles(null, fileName + "-1.kt", fileName + "-2.kt"); complete(2); + if (myItems != null) { + assertTrue("Multiple items in completion", myItems.length == 1); + selectItem(myItems[0]); + } checkResultByFile(fileName + ".kt.after"); } diff --git a/idea/tests/org/jetbrains/jet/plugin/quickfix/QuickFixMultiFileTestGenerated.java b/idea/tests/org/jetbrains/jet/plugin/quickfix/QuickFixMultiFileTestGenerated.java index 4968413fb0e..f76a0841f30 100644 --- a/idea/tests/org/jetbrains/jet/plugin/quickfix/QuickFixMultiFileTestGenerated.java +++ b/idea/tests/org/jetbrains/jet/plugin/quickfix/QuickFixMultiFileTestGenerated.java @@ -17,10 +17,13 @@ package org.jetbrains.jet.plugin.quickfix; import com.intellij.testFramework.TestDataPath; +import junit.framework.Test; +import junit.framework.TestSuite; +import org.junit.runner.RunWith; import org.jetbrains.jet.JetTestUtils; import org.jetbrains.jet.test.InnerTestClasses; import org.jetbrains.jet.test.TestMetadata; -import org.junit.runner.RunWith; +import org.jetbrains.jet.JUnit3RunnerWithInners; import java.io.File; import java.util.regex.Pattern;