Initial implementation of KT-6427 Completion to use Java name suggestion to complete function parameters

(+ filtered out synthetic Kotlin classes from completion)
This commit is contained in:
Valentin Kipyatkov
2015-06-17 17:55:51 +03:00
parent e0f1bde20a
commit 8210d3091f
31 changed files with 408 additions and 81 deletions
@@ -95,3 +95,13 @@ public fun <T : Any> constant(calculator: () -> T): T {
}
private val constantMap = ConcurrentHashMap<Function0<*>, Any>()
public fun String.indexOfOrNull(char: Char, startIndex: Int = 0, ignoreCase: Boolean = false): Int? {
val index = indexOf(char, startIndex, ignoreCase)
return if (index >= 0) index else null
}
public fun String.lastIndexOfOrNull(char: Char, startIndex: Int = 0, ignoreCase: Boolean = false): Int? {
val index = lastIndexOf(char, startIndex, ignoreCase)
return if (index >= 0) index else null
}
@@ -19,8 +19,10 @@ package org.jetbrains.kotlin.idea.completion
import com.intellij.codeInsight.completion.AllClassesGetter
import com.intellij.codeInsight.completion.CompletionParameters
import com.intellij.codeInsight.completion.PrefixMatcher
import com.intellij.psi.PsiClass
import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.asJava.KotlinLightClass
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
@@ -31,34 +33,28 @@ import org.jetbrains.kotlin.platform.JavaToKotlinClassMap
import org.jetbrains.kotlin.psi.JetFile
import org.jetbrains.kotlin.resolve.BindingContext
class AllClassesCompletion(val parameters: CompletionParameters,
val lookupElementFactory: LookupElementFactory,
val resolutionFacade: ResolutionFacade,
val bindingContext: BindingContext,
val moduleDescriptor: ModuleDescriptor,
val scope: GlobalSearchScope,
val prefixMatcher: PrefixMatcher,
val kindFilter: (ClassKind) -> Boolean,
val visibilityFilter: (DeclarationDescriptor) -> Boolean) {
fun collect(result: LookupElementsCollector) {
class AllClassesCompletion(private val parameters: CompletionParameters,
private val kotlinIndicesHelper: KotlinIndicesHelper,
private val prefixMatcher: PrefixMatcher,
private val kindFilter: (ClassKind) -> Boolean) {
fun collect(classDescriptorCollector: (ClassDescriptor) -> Unit, javaClassCollector: (PsiClass) -> Unit) {
//TODO: this is a temporary hack until we have built-ins in indices
val builtIns = JavaToKotlinClassMap.INSTANCE.allKotlinClasses()
val filteredBuiltIns = builtIns.filter { kindFilter(it.getKind()) && prefixMatcher.prefixMatches(it.getName().asString()) }
result.addDescriptorElements(filteredBuiltIns, suppressAutoInsertion = true)
filteredBuiltIns.forEach { classDescriptorCollector(it) }
val project = parameters.getOriginalFile().getProject()
val helper = KotlinIndicesHelper(project, resolutionFacade, bindingContext, scope, moduleDescriptor, visibilityFilter)
result.addDescriptorElements(helper.getClassDescriptors({ prefixMatcher.prefixMatches(it) }, kindFilter),
suppressAutoInsertion = true)
kotlinIndicesHelper.getClassDescriptors({ prefixMatcher.prefixMatches(it) }, kindFilter).forEach { classDescriptorCollector(it) }
if (!ProjectStructureUtil.isJsKotlinModule(parameters.getOriginalFile() as JetFile)) {
addAdaptedJavaCompletion(result)
addAdaptedJavaCompletion(javaClassCollector)
}
}
private fun addAdaptedJavaCompletion(collector: LookupElementsCollector) {
private fun addAdaptedJavaCompletion(collector: (PsiClass) -> Unit) {
AllClassesGetter.processJavaClasses(parameters, prefixMatcher, true, { psiClass ->
if (psiClass!! !is KotlinLightClass) { // Kotlin class should have already been added as kotlin element before
if (psiClass.isSyntheticKotlinClass()) return@processJavaClasses // filter out synthetic classes produced by Kotlin compiler
val kind = when {
psiClass.isAnnotationType() -> ClassKind.ANNOTATION_CLASS
psiClass.isInterface() -> ClassKind.INTERFACE
@@ -66,9 +62,14 @@ class AllClassesCompletion(val parameters: CompletionParameters,
else -> ClassKind.CLASS
}
if (kindFilter(kind)) {
collector.addElementWithAutoInsertionSuppressed(lookupElementFactory.createLookupElementForJavaClass(psiClass))
collector(psiClass)
}
}
})
}
private fun PsiClass.isSyntheticKotlinClass(): Boolean {
if (!getName().contains('$')) return false // optimization to not analyze annotations of all classes
return getModifierList()?.findAnnotation(javaClass<kotlin.jvm.internal.KotlinSyntheticClass>().getName()) != null
}
}
@@ -128,7 +128,7 @@ abstract class CompletionSessionBase(protected val configuration: CompletionSess
protected val prefixMatcher: PrefixMatcher = this.resultSet.getPrefixMatcher()
protected val referenceVariantsHelper: ReferenceVariantsHelper = ReferenceVariantsHelper(bindingContext, moduleDescriptor, project) { isVisibleDescriptor(it) }
protected val referenceVariantsHelper: ReferenceVariantsHelper = ReferenceVariantsHelper(bindingContext, moduleDescriptor, project, { isVisibleDescriptor(it) })
protected val receiversData: ReferenceVariantsHelper.ReceiversData? = reference?.let { referenceVariantsHelper.getReferenceVariantsReceivers(it.expression) }
@@ -168,7 +168,7 @@ abstract class CompletionSessionBase(protected val configuration: CompletionSess
}
protected val indicesHelper: KotlinIndicesHelper
get() = KotlinIndicesHelper(project, resolutionFacade, bindingContext, searchScope, moduleDescriptor) { isVisibleDescriptor(it) }
get() = KotlinIndicesHelper(project, resolutionFacade, searchScope, moduleDescriptor, { isVisibleDescriptor(it) })
protected fun isVisibleDescriptor(descriptor: DeclarationDescriptor): Boolean {
if (descriptor is DeclarationDescriptorWithVisibility && inDescriptor != null) {
@@ -241,7 +241,7 @@ abstract class CompletionSessionBase(protected val configuration: CompletionSess
}
protected fun getTopLevelExtensions(): Collection<CallableDescriptor> {
val descriptors = indicesHelper.getCallableTopLevelExtensions({ prefixMatcher.prefixMatches(it) }, reference!!.expression)
val descriptors = indicesHelper.getCallableTopLevelExtensions({ prefixMatcher.prefixMatches(it) }, reference!!.expression, bindingContext)
return filterShadowedNonImported(descriptors, reference)
}
@@ -250,10 +250,11 @@ abstract class CompletionSessionBase(protected val configuration: CompletionSess
}
protected fun addAllClasses(kindFilter: (ClassKind) -> Boolean) {
AllClassesCompletion(
parameters, lookupElementFactory, resolutionFacade, bindingContext, moduleDescriptor,
searchScope, prefixMatcher, kindFilter, { isVisibleDescriptor(it) }
).collect(collector)
AllClassesCompletion(parameters, indicesHelper, prefixMatcher, kindFilter)
.collect(
{ descriptor -> collector.addDescriptorElements(descriptor, suppressAutoInsertion = true) },
{ javaClass -> collector.addElementWithAutoInsertionSuppressed(lookupElementFactory.createLookupElementForJavaClass(javaClass)) }
)
}
}
@@ -287,6 +288,11 @@ class BasicCompletionSession(configuration: CompletionSessionConfiguration,
null
}
private val parameterNameAndTypeCompletion = if (completionKind == CompletionKind.ANNOTATION_TYPES_OR_PARAMETER_NAME)
ParameterNameAndTypeCompletion(collector, lookupElementFactory, prefixMatcher)
else
null
private fun calcCompletionKind(): CompletionKind {
if (NamedArgumentCompletion.isOnlyNamedArgumentExpected(position)) {
return CompletionKind.NAMED_ARGUMENTS_ONLY
@@ -327,6 +333,8 @@ class BasicCompletionSession(configuration: CompletionSessionConfiguration,
if (completionKind != CompletionKind.NAMED_ARGUMENTS_ONLY) {
collector.addDescriptorElements(referenceVariants, suppressAutoInsertion = false)
parameterNameAndTypeCompletion?.addFromImports(reference!!.expression, bindingContext, { isVisibleDescriptor(it) })
val keywordsPrefix = prefix.substringBefore('@') // if there is '@' in the prefix - use shorter prefix to not loose 'this' etc
KeywordCompletion.complete(expression ?: parameters.getPosition(), keywordsPrefix) { lookupElement ->
val keyword = lookupElement.getLookupString()
@@ -405,6 +413,8 @@ class BasicCompletionSession(configuration: CompletionSessionConfiguration,
collector.addDescriptorElements(getTopLevelCallables(), suppressAutoInsertion = true)
}
}
parameterNameAndTypeCompletion?.addAll(parameters, indicesHelper)
}
}
@@ -225,11 +225,6 @@ public class KotlinCompletionContributor : CompletionContributor() {
if (parameters.getCompletionType() == CompletionType.BASIC) {
val session = BasicCompletionSession(configuration, parameters, result)
if (session.completionKind == BasicCompletionSession.CompletionKind.ANNOTATION_TYPES_OR_PARAMETER_NAME && parameters.isAutoPopup()) {
result.stopHere()
return
}
val somethingAdded = session.complete()
if (!somethingAdded && parameters.getInvocationCount() < 2) {
// Rerun completion if nothing was found
@@ -69,7 +69,7 @@ class LookupElementsCollector(
}
}
private fun addDescriptorElements(descriptor: DeclarationDescriptor, suppressAutoInsertion: Boolean, withReceiverCast: Boolean) {
public fun addDescriptorElements(descriptor: DeclarationDescriptor, suppressAutoInsertion: Boolean, withReceiverCast: Boolean = false) {
run {
var lookupElement = lookupElementFactory.createLookupElement(descriptor, true)
@@ -0,0 +1,127 @@
/*
* Copyright 2010-2015 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.kotlin.idea.completion
import com.intellij.codeInsight.completion.CompletionParameters
import com.intellij.codeInsight.completion.InsertionContext
import com.intellij.codeInsight.completion.PrefixMatcher
import com.intellij.codeInsight.lookup.LookupElement
import com.intellij.codeInsight.lookup.LookupElementDecorator
import com.intellij.codeInsight.lookup.LookupElementPresentation
import com.intellij.psi.PsiClass
import com.intellij.psi.codeStyle.CodeStyleSettingsManager
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.idea.core.KotlinIndicesHelper
import org.jetbrains.kotlin.idea.core.formatter.JetCodeStyleSettings
import org.jetbrains.kotlin.idea.core.refactoring.EmptyValidator
import org.jetbrains.kotlin.idea.core.refactoring.JetNameSuggester
import org.jetbrains.kotlin.psi.JetSimpleNameExpression
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered
class ParameterNameAndTypeCompletion(
private val collector: LookupElementsCollector,
private val lookupElementFactory: LookupElementFactory,
private val prefixMatcher: PrefixMatcher
) {
private val modifiedPrefixMatcher = prefixMatcher.cloneWithPrefix(prefixMatcher.getPrefix().capitalize())
public fun addFromImports(nameExpression: JetSimpleNameExpression, bindingContext: BindingContext, visibilityFilter: (DeclarationDescriptor) -> Boolean) {
if (prefixMatcher.getPrefix().isEmpty()) return
val resolutionScope = bindingContext[BindingContext.RESOLUTION_SCOPE, nameExpression] ?: return
val classifiers = resolutionScope.getDescriptorsFiltered(DescriptorKindFilter.NON_SINGLETON_CLASSIFIERS, modifiedPrefixMatcher.asNameFilter())
for (classifier in classifiers) {
if (visibilityFilter(classifier)) {
addSuggestionsForClassifier(classifier)
}
}
}
public fun addAll(parameters: CompletionParameters, indicesHelper: KotlinIndicesHelper) {
if (prefixMatcher.getPrefix().isEmpty()) return
AllClassesCompletion(parameters, indicesHelper, modifiedPrefixMatcher, { !it.isSingleton() })
.collect({ addSuggestionsForClassifier(it) }, { addSuggestionsForJavaClass(it) })
}
private fun addSuggestionsForClassifier(classifier: DeclarationDescriptor) {
addSuggestions(classifier.getName().asString()) { name -> NameAndDescriptorType(name, classifier as ClassifierDescriptor) }
}
private fun addSuggestionsForJavaClass(psiClass: PsiClass) {
addSuggestions(psiClass.getName()) { name -> NameAndJavaType(name, psiClass) }
}
private inline fun addSuggestions(className: String, nameAndTypeFactory: (String) -> NameAndType) {
val parameterNames = JetNameSuggester.getCamelNames(className, EmptyValidator)
for (parameterName in parameterNames) {
if (prefixMatcher.prefixMatches(parameterName)) {
val nameAndType = nameAndTypeFactory(parameterName)
collector.addElement(MyLookupElement(nameAndType, lookupElementFactory))
}
}
}
private interface NameAndType {
val parameterName: String
fun createTypeLookupElement(lookupElementFactory: LookupElementFactory): LookupElement
}
private data class NameAndDescriptorType(override val parameterName: String, val type: ClassifierDescriptor) : NameAndType {
override fun createTypeLookupElement(lookupElementFactory: LookupElementFactory)
= lookupElementFactory.createLookupElement(type, false)
}
private data class NameAndJavaType(override val parameterName: String, val type: PsiClass) : NameAndType {
override fun createTypeLookupElement(lookupElementFactory: LookupElementFactory)
= lookupElementFactory.createLookupElementForJavaClass(type)
}
private class MyLookupElement(
val nameAndType: NameAndType,
factory: LookupElementFactory
) : LookupElementDecorator<LookupElement>(nameAndType.createTypeLookupElement(factory)) {
override fun getObject() = nameAndType
override fun equals(other: Any?)
= other is MyLookupElement && nameAndType.parameterName == other.getObject().parameterName && getDelegate() == other.getDelegate()
override fun hashCode() = nameAndType.parameterName.hashCode()
override fun getLookupString() = nameAndType.parameterName
override fun getAllLookupStrings() = setOf(nameAndType.parameterName)
override fun renderElement(presentation: LookupElementPresentation) {
super.renderElement(presentation)
presentation.setItemText(nameAndType.parameterName + ": " + presentation.getItemText())
}
override fun handleInsert(context: InsertionContext) {
super.handleInsert(context)
val settings = CodeStyleSettingsManager.getInstance(context.getProject()).getCurrentSettings().getCustomSettings(javaClass<JetCodeStyleSettings>())
val spaceBefore = if (settings.SPACE_BEFORE_TYPE_COLON) " " else ""
val spaceAfter = if (settings.SPACE_AFTER_TYPE_COLON) " " else ""
val text = nameAndType.parameterName + spaceBefore + ":" + spaceAfter
context.getDocument().insertString(context.getStartOffset(), text)
}
}
}
@@ -1,4 +0,0 @@
fun foo(i<caret>) { }
// INVOCATION_COUNT: 0
// NUMBER: 0
@@ -1,4 +0,0 @@
fun foo(@inlineOptions i<caret>) { }
// INVOCATION_COUNT: 0
// NUMBER: 0
@@ -0,0 +1,6 @@
import kotlin.properties.*
fun f(readOnlyProp<caret>)
// EXIST: { lookupString: "readOnlyProperty", itemText: "readOnlyProperty: ReadOnlyProperty", tailText: "<R, T> (kotlin.properties)" }
// NUMBER: 1
@@ -0,0 +1,6 @@
import java.io.*
fun f(printSt<caret>)
// EXIST_JAVA_ONLY: { lookupString: "printStream", itemText: "printStream: PrintStream", tailText: " (java.io)" }
// NUMBER_JAVA: 1
@@ -0,0 +1,3 @@
fun f(read<caret>)
// EXIST: { lookupString: "readOnlyProperty", itemText: "readOnlyProperty: ReadOnlyProperty", tailText: "<R, T> (kotlin.properties)" }
@@ -0,0 +1,3 @@
fun f(file<caret>)
// EXIST_JAVA_ONLY: { lookupString: "file", itemText: "file: File", tailText: " (java.io)" }
@@ -0,0 +1,11 @@
package pack
class FooBar
class Boo
fun f(b<caret>)
// EXIST: { lookupString: "bar", itemText: "bar: FooBar", tailText: " (pack)" }
// EXIST: { lookupString: "fooBar", itemText: "fooBar: FooBar", tailText: " (pack)" }
// EXIST: { lookupString: "boo", itemText: "boo: Boo", tailText: " (pack)" }
@@ -0,0 +1,8 @@
import kotlin.properties.*
val x: ReadOnlyPr<caret>
// INVOCATION_COUNT: 2
// EXIST: "ReadOnlyProperty"
// ABSENT: "ReadOnlyProperty$$TImpl"
// NOTHING_ELSE
@@ -1,4 +1,4 @@
// INSERT_WHITESPACES_IN_SIMPLE_ONE_LINE_METHOD: false
// CODE_STYLE_SETTING: INSERT_WHITESPACES_IN_SIMPLE_ONE_LINE_METHOD = false
fun main(args: Array<String>) {
args.fil<caret>
@@ -1,4 +1,4 @@
// INSERT_WHITESPACES_IN_SIMPLE_ONE_LINE_METHOD: false
// CODE_STYLE_SETTING: INSERT_WHITESPACES_IN_SIMPLE_ONE_LINE_METHOD = false
fun main(args: Array<String>) {
args.filter {<caret>}
@@ -0,0 +1,8 @@
// CODE_STYLE_SETTING: SPACE_BEFORE_TYPE_COLON = true
// CODE_STYLE_SETTING: SPACE_AFTER_TYPE_COLON = false
class FooBar
fun f(b<caret>)
// ELEMENT: bar
@@ -0,0 +1,8 @@
// CODE_STYLE_SETTING: SPACE_BEFORE_TYPE_COLON = true
// CODE_STYLE_SETTING: SPACE_AFTER_TYPE_COLON = false
class FooBar
fun f(bar :FooBar<caret>)
// ELEMENT: bar
@@ -0,0 +1,6 @@
class FooBar
fun f(b<caret>)
// ELEMENT: bar
// CHAR: ','
@@ -0,0 +1,6 @@
class FooBar
fun f(bar: FooBar, <caret>)
// ELEMENT: bar
// CHAR: ','
@@ -0,0 +1,3 @@
fun f(file<caret>)
// ELEMENT_TEXT: "file: File"
@@ -0,0 +1,5 @@
import java.io.File
fun f(file: File<caret>)
// ELEMENT_TEXT: "file: File"
@@ -0,0 +1,5 @@
class FooBar
fun f(b<caret>)
// ELEMENT: bar
@@ -0,0 +1,5 @@
class FooBar
fun f(bar: FooBar<caret>)
// ELEMENT: bar
@@ -1053,18 +1053,6 @@ public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTes
doTest(fileName);
}
@TestMetadata("NoParameterAnnotationAutoPopup1.kt")
public void testNoParameterAnnotationAutoPopup1() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/annotations/NoParameterAnnotationAutoPopup1.kt");
doTest(fileName);
}
@TestMetadata("NoParameterAnnotationAutoPopup2.kt")
public void testNoParameterAnnotationAutoPopup2() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/annotations/NoParameterAnnotationAutoPopup2.kt");
doTest(fileName);
}
@TestMetadata("ParameterAnnotation1.kt")
public void testParameterAnnotation1() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/annotations/ParameterAnnotation1.kt");
@@ -1402,6 +1390,45 @@ public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTes
}
}
@TestMetadata("idea/idea-completion/testData/basic/common/parameterNameAndType")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class ParameterNameAndType extends AbstractJSBasicCompletionTest {
public void testAllFilesPresentInParameterNameAndType() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/idea-completion/testData/basic/common/parameterNameAndType"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("NoDuplication.kt")
public void testNoDuplication() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/parameterNameAndType/NoDuplication.kt");
doTest(fileName);
}
@TestMetadata("NoDuplicationJava.kt")
public void testNoDuplicationJava() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/parameterNameAndType/NoDuplicationJava.kt");
doTest(fileName);
}
@TestMetadata("NotImported.kt")
public void testNotImported() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/parameterNameAndType/NotImported.kt");
doTest(fileName);
}
@TestMetadata("NotImportedJava.kt")
public void testNotImportedJava() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/parameterNameAndType/NotImportedJava.kt");
doTest(fileName);
}
@TestMetadata("Simple.kt")
public void testSimple() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/parameterNameAndType/Simple.kt");
doTest(fileName);
}
}
@TestMetadata("idea/idea-completion/testData/basic/common/shadowing")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -1053,18 +1053,6 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT
doTest(fileName);
}
@TestMetadata("NoParameterAnnotationAutoPopup1.kt")
public void testNoParameterAnnotationAutoPopup1() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/annotations/NoParameterAnnotationAutoPopup1.kt");
doTest(fileName);
}
@TestMetadata("NoParameterAnnotationAutoPopup2.kt")
public void testNoParameterAnnotationAutoPopup2() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/annotations/NoParameterAnnotationAutoPopup2.kt");
doTest(fileName);
}
@TestMetadata("ParameterAnnotation1.kt")
public void testParameterAnnotation1() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/annotations/ParameterAnnotation1.kt");
@@ -1402,6 +1390,45 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT
}
}
@TestMetadata("idea/idea-completion/testData/basic/common/parameterNameAndType")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class ParameterNameAndType extends AbstractJvmBasicCompletionTest {
public void testAllFilesPresentInParameterNameAndType() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/idea-completion/testData/basic/common/parameterNameAndType"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("NoDuplication.kt")
public void testNoDuplication() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/parameterNameAndType/NoDuplication.kt");
doTest(fileName);
}
@TestMetadata("NoDuplicationJava.kt")
public void testNoDuplicationJava() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/parameterNameAndType/NoDuplicationJava.kt");
doTest(fileName);
}
@TestMetadata("NotImported.kt")
public void testNotImported() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/parameterNameAndType/NotImported.kt");
doTest(fileName);
}
@TestMetadata("NotImportedJava.kt")
public void testNotImportedJava() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/parameterNameAndType/NotImportedJava.kt");
doTest(fileName);
}
@TestMetadata("Simple.kt")
public void testSimple() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/parameterNameAndType/Simple.kt");
doTest(fileName);
}
}
@TestMetadata("idea/idea-completion/testData/basic/common/shadowing")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -1696,6 +1723,12 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT
doTest(fileName);
}
@TestMetadata("NoSyntheticClasses.kt")
public void testNoSyntheticClasses() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/java/NoSyntheticClasses.kt");
doTest(fileName);
}
@TestMetadata("PackageDirective.kt")
public void testPackageDirective() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/java/PackageDirective.kt");
@@ -22,6 +22,7 @@ import com.intellij.psi.codeStyle.CodeStyleSettingsManager
import org.jetbrains.kotlin.idea.core.formatter.JetCodeStyleSettings
import org.jetbrains.kotlin.idea.test.JetWithJdkAndRuntimeLightProjectDescriptor
import org.jetbrains.kotlin.test.InTextDirectivesUtils
import org.jetbrains.kotlin.utils.addToStdlib.indexOfOrNull
import java.io.File
public abstract class AbstractCompletionHandlerTest(private val defaultCompletionType: CompletionType) : CompletionHandlerTestBase() {
@@ -31,7 +32,7 @@ public abstract class AbstractCompletionHandlerTest(private val defaultCompletio
private val TAIL_TEXT_PREFIX = "TAIL_TEXT:"
private val COMPLETION_CHAR_PREFIX = "CHAR:"
private val COMPLETION_TYPE_PREFIX = "COMPLETION_TYPE:"
private val INSERT_WHITESPACES_IN_SIMPLE_ONE_LINE_METHOD = "INSERT_WHITESPACES_IN_SIMPLE_ONE_LINE_METHOD:"
private val CODE_STYLE_SETTING_PREFIX = "CODE_STYLE_SETTING:"
protected open fun doTest(testPath: String) {
setUpFixture(testPath)
@@ -61,8 +62,17 @@ public abstract class AbstractCompletionHandlerTest(private val defaultCompletio
else -> error("Unknown completion type: $completionTypeString")
}
InTextDirectivesUtils.getPrefixedBoolean(fileText, INSERT_WHITESPACES_IN_SIMPLE_ONE_LINE_METHOD)?.let {
JetCodeStyleSettings.getInstance(getProject()).INSERT_WHITESPACES_IN_SIMPLE_ONE_LINE_METHOD = it
val codeStyleSettings = JetCodeStyleSettings.getInstance(getProject())
for (line in InTextDirectivesUtils.findLinesWithPrefixesRemoved(fileText, CODE_STYLE_SETTING_PREFIX)) {
val index = line.indexOfOrNull('=') ?: error("Invalid code style setting '$line': '=' expected")
val settingName = line.substring(0, index).trim()
val settingValue = line.substring(index + 1).trim()
val field = codeStyleSettings.javaClass.getDeclaredField(settingName)
when (field.getType().getName()) {
"boolean" -> field.setBoolean(codeStyleSettings, settingValue.toBoolean())
"int" -> field.setInt(codeStyleSettings, settingValue.toInt())
else -> error("Unsupported setting type: ${field.getType()}")
}
}
doTestWithTextLoaded(completionType, invocationCount, lookupString, itemText, tailText, completionChar, testPath + ".after")
@@ -239,6 +239,39 @@ public class BasicCompletionHandlerTestGenerated extends AbstractBasicCompletion
}
}
@TestMetadata("idea/idea-completion/testData/handlers/basic/parameterNameAndType")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class ParameterNameAndType extends AbstractBasicCompletionHandlerTest {
public void testAllFilesPresentInParameterNameAndType() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/idea-completion/testData/handlers/basic/parameterNameAndType"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("CodeStyleSettings.kt")
public void testCodeStyleSettings() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/basic/parameterNameAndType/CodeStyleSettings.kt");
doTest(fileName);
}
@TestMetadata("Comma.kt")
public void testComma() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/basic/parameterNameAndType/Comma.kt");
doTest(fileName);
}
@TestMetadata("InsertImport.kt")
public void testInsertImport() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/basic/parameterNameAndType/InsertImport.kt");
doTest(fileName);
}
@TestMetadata("Simple.kt")
public void testSimple() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/basic/parameterNameAndType/Simple.kt");
doTest(fileName);
}
}
@TestMetadata("idea/idea-completion/testData/handlers/basic/stringTemplate")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -46,7 +46,6 @@ import java.util.LinkedHashSet
public class KotlinIndicesHelper(
private val project: Project,
private val resolutionFacade: ResolutionFacade,
private val bindingContext: BindingContext,
private val scope: GlobalSearchScope,
private val moduleDescriptor: ModuleDescriptor,
private val visibilityFilter: (DeclarationDescriptor) -> Boolean
@@ -73,36 +72,36 @@ public class KotlinIndicesHelper(
}
public fun getTopLevelCallables(nameFilter: (String) -> Boolean): Collection<CallableDescriptor> {
return (JetTopLevelFunctionFqnNameIndex.getInstance().getAllKeys(project).sequence() +
JetTopLevelPropertyFqnNameIndex.getInstance().getAllKeys(project).sequence())
return (JetTopLevelFunctionFqnNameIndex.getInstance().getAllKeys(project).asSequence() +
JetTopLevelPropertyFqnNameIndex.getInstance().getAllKeys(project).asSequence())
.map { FqName(it) }
.filter { nameFilter(it.shortName().asString()) }
.toSet()
.flatMap { findTopLevelCallables(it).filter(visibilityFilter) }
}
public fun getCallableTopLevelExtensions(nameFilter: (String) -> Boolean, expression: JetSimpleNameExpression): Collection<CallableDescriptor> {
val receiverValues = receiverValues(expression)
public fun getCallableTopLevelExtensions(nameFilter: (String) -> Boolean, expression: JetSimpleNameExpression, bindingContext: BindingContext): Collection<CallableDescriptor> {
val receiverValues = receiverValues(expression, bindingContext)
if (receiverValues.isEmpty()) return emptyList()
val dataFlowInfo = bindingContext.getDataFlowInfo(expression)
val receiverTypeNames = possibleReceiverTypeNames(receiverValues.map { it.first }, dataFlowInfo)
val receiverTypeNames = possibleReceiverTypeNames(receiverValues.map { it.first }, dataFlowInfo, bindingContext)
val index = JetTopLevelExtensionsByReceiverTypeIndex.INSTANCE
val declarations = index.getAllKeys(project)
.sequence()
.asSequence()
.filter {
JetTopLevelExtensionsByReceiverTypeIndex.receiverTypeNameFromKey(it) in receiverTypeNames
&& nameFilter(JetTopLevelExtensionsByReceiverTypeIndex.callableNameFromKey(it))
}
.flatMap { index.get(it, project, scope).sequence() }
.flatMap { index.get(it, project, scope).asSequence() }
return findSuitableExtensions(declarations, receiverValues, dataFlowInfo, bindingContext)
}
private fun possibleReceiverTypeNames(receiverValues: Collection<ReceiverValue>, dataFlowInfo: DataFlowInfo): Set<String> {
private fun possibleReceiverTypeNames(receiverValues: Collection<ReceiverValue>, dataFlowInfo: DataFlowInfo, bindingContext: BindingContext): Set<String> {
val result = HashSet<String>()
for (receiverValue in receiverValues) {
for (type in SmartCastUtils.getSmartCastVariants(receiverValue, bindingContext, moduleDescriptor, dataFlowInfo)) {
@@ -118,7 +117,7 @@ public class KotlinIndicesHelper(
constructor.getSupertypes().forEach { addTypeNames(it) }
}
private fun receiverValues(expression: JetSimpleNameExpression): Collection<Pair<ReceiverValue, CallType>> {
private fun receiverValues(expression: JetSimpleNameExpression, bindingContext: BindingContext): Collection<Pair<ReceiverValue, CallType>> {
val receiverPair = ReferenceVariantsHelper.getExplicitReceiverData(expression)
if (receiverPair != null) {
val (receiverExpression, callType) = receiverPair
@@ -173,7 +172,7 @@ public class KotlinIndicesHelper(
}
public fun getClassDescriptors(nameFilter: (String) -> Boolean, kindFilter: (ClassKind) -> Boolean): Collection<ClassDescriptor> {
return JetFullClassNameIndex.getInstance().getAllKeys(project).sequence()
return JetFullClassNameIndex.getInstance().getAllKeys(project).asSequence()
.map { FqName(it) }
.filter { nameFilter(it.shortName().asString()) }
.toList()
@@ -37,6 +37,7 @@ import org.jetbrains.kotlin.types.TypeUtils;
import org.jetbrains.kotlin.types.checker.JetTypeChecker;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -198,6 +199,12 @@ public class JetNameSuggester {
private static final String[] ACCESSOR_PREFIXES = { "get", "is", "set" };
public static List<String> getCamelNames(String name, JetNameValidator validator) {
ArrayList<String> result = new ArrayList<String>();
addCamelNames(result, name, validator);
return result;
}
private static void addCamelNames(ArrayList<String> result, String name, JetNameValidator validator) {
if (name == "") return;
String s = deleteNonLetterFromString(name);
@@ -138,7 +138,7 @@ public class AutoImportFix(element: JetSimpleNameExpression) : JetHintAction<Jet
val result = ArrayList<DeclarationDescriptor>()
val moduleDescriptor = resolutionFacade.findModuleDescriptor(element)
val indicesHelper = KotlinIndicesHelper(file.getProject(), resolutionFacade, bindingContext, searchScope, moduleDescriptor, ::isVisible)
val indicesHelper = KotlinIndicesHelper(file.getProject(), resolutionFacade, searchScope, moduleDescriptor, ::isVisible)
if (!element.isImportDirectiveExpression() && !JetPsiUtil.isSelectorInQualified(element)) {
if (ProjectStructureUtil.isJsKotlinModule(file)) {
@@ -150,7 +150,7 @@ public class AutoImportFix(element: JetSimpleNameExpression) : JetHintAction<Jet
result.addAll(indicesHelper.getTopLevelCallablesByName(referenceName))
}
result.addAll(indicesHelper.getCallableTopLevelExtensions({ it == referenceName }, element))
result.addAll(indicesHelper.getCallableTopLevelExtensions({ it == referenceName }, element, bindingContext))
return result
}