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:
@@ -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
-18
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
-5
@@ -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
|
||||
|
||||
+1
-1
@@ -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)
|
||||
|
||||
|
||||
+127
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
-4
@@ -1,4 +0,0 @@
|
||||
fun foo(i<caret>) { }
|
||||
|
||||
// INVOCATION_COUNT: 0
|
||||
// NUMBER: 0
|
||||
-4
@@ -1,4 +0,0 @@
|
||||
fun foo(@inlineOptions i<caret>) { }
|
||||
|
||||
// INVOCATION_COUNT: 0
|
||||
// NUMBER: 0
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
import kotlin.properties.*
|
||||
|
||||
fun f(readOnlyProp<caret>)
|
||||
|
||||
// EXIST: { lookupString: "readOnlyProperty", itemText: "readOnlyProperty: ReadOnlyProperty", tailText: "<R, T> (kotlin.properties)" }
|
||||
// NUMBER: 1
|
||||
+6
@@ -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
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
fun f(read<caret>)
|
||||
|
||||
// EXIST: { lookupString: "readOnlyProperty", itemText: "readOnlyProperty: ReadOnlyProperty", tailText: "<R, T> (kotlin.properties)" }
|
||||
+3
@@ -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
-1
@@ -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
-1
@@ -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>}
|
||||
|
||||
+8
@@ -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
|
||||
Vendored
+8
@@ -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: ','
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
class FooBar
|
||||
|
||||
fun f(bar: FooBar, <caret>)
|
||||
|
||||
// ELEMENT: bar
|
||||
// CHAR: ','
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
fun f(file<caret>)
|
||||
|
||||
// ELEMENT_TEXT: "file: File"
|
||||
+5
@@ -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
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
class FooBar
|
||||
|
||||
fun f(bar: FooBar<caret>)
|
||||
|
||||
// ELEMENT: bar
|
||||
+39
-12
@@ -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)
|
||||
|
||||
+45
-12
@@ -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");
|
||||
|
||||
+13
-3
@@ -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")
|
||||
|
||||
+33
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user