idea: cleanup code

This commit is contained in:
Dmitry Gridin
2019-12-13 22:11:25 +07:00
parent e77d8657f4
commit 8dbbd64beb
897 changed files with 13161 additions and 18514 deletions
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2016 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.
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.completion
@@ -34,19 +23,20 @@ import org.jetbrains.kotlin.idea.refactoring.fqName.getKotlinFqName
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.platform.jvm.isJvm
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
import org.jetbrains.kotlin.resolve.scopes.MemberScope
import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered
class AllClassesCompletion(private val parameters: CompletionParameters,
private val kotlinIndicesHelper: KotlinIndicesHelper,
private val prefixMatcher: PrefixMatcher,
private val resolutionFacade: ResolutionFacade,
private val kindFilter: (ClassKind) -> Boolean,
private val includeTypeAliases: Boolean,
private val includeJavaClassesNotToBeUsed: Boolean
class AllClassesCompletion(
private val parameters: CompletionParameters,
private val kotlinIndicesHelper: KotlinIndicesHelper,
private val prefixMatcher: PrefixMatcher,
private val resolutionFacade: ResolutionFacade,
private val kindFilter: (ClassKind) -> Boolean,
private val includeTypeAliases: Boolean,
private val includeJavaClassesNotToBeUsed: Boolean
) {
fun collect(classifierDescriptorCollector: (ClassifierDescriptorWithTypeParameters) -> Unit, javaClassCollector: (PsiClass) -> Unit) {
@@ -60,14 +50,11 @@ class AllClassesCompletion(private val parameters: CompletionParameters,
}
}
kotlinIndicesHelper
.getKotlinClasses({ prefixMatcher.prefixMatches(it) }, kindFilter = kindFilter)
.forEach { classifierDescriptorCollector(it) }
kotlinIndicesHelper.getKotlinClasses({ prefixMatcher.prefixMatches(it) }, kindFilter = kindFilter)
.forEach { classifierDescriptorCollector(it) }
if (includeTypeAliases) {
kotlinIndicesHelper
.getTopLevelTypeAliases(prefixMatcher.asStringNameFilter())
.forEach { classifierDescriptorCollector(it) }
kotlinIndicesHelper.getTopLevelTypeAliases(prefixMatcher.asStringNameFilter()).forEach { classifierDescriptorCollector(it) }
}
if (TargetPlatformDetector.getPlatform(parameters.originalFile as KtFile).isJvm()) {
@@ -1,17 +1,6 @@
/*
* 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.
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.completion
@@ -55,12 +44,12 @@ import org.jetbrains.kotlin.load.java.sam.SamConstructorDescriptor
import org.jetbrains.kotlin.load.java.sam.SamConstructorDescriptorKindExclude
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.platform.jvm.isJvm
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.*
import org.jetbrains.kotlin.renderer.render
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.descriptorUtil.*
import org.jetbrains.kotlin.platform.jvm.isJvm
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindExclude
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
@@ -206,7 +195,9 @@ class BasicCompletionSession(
// no auto-popup on typing after "val", "var" and "fun" because it's likely the name of the declaration which is being typed by user
if (parameters.invocationCount == 0) {
val suppressOtherCompletion = when (declaration) {
is KtNamedFunction, is KtProperty -> prefixMatcher.prefix.let { it.isEmpty() || it[0].isLowerCase() /* function name usually starts with lower case letter */ }
is KtNamedFunction, is KtProperty -> prefixMatcher.prefix.let {
it.isEmpty() || it[0].isLowerCase() /* function name usually starts with lower case letter */
}
else -> true
}
if (suppressOtherCompletion) return
@@ -263,7 +254,9 @@ class BasicCompletionSession(
}
// getting root packages from scope is very slow so we do this in alternative way
if (callTypeAndReceiver.receiver == null && callTypeAndReceiver.callType.descriptorKindFilter.kindMask.and(DescriptorKindFilter.PACKAGES_MASK) != 0) {
if (callTypeAndReceiver.receiver == null &&
callTypeAndReceiver.callType.descriptorKindFilter.kindMask.and(DescriptorKindFilter.PACKAGES_MASK) != 0
) {
//TODO: move this code somewhere else?
val packageNames = PackageIndexUtil.getSubPackageFqNames(FqName.ROOT, searchScope, project, prefixMatcher.asNameFilter())
.toMutableSet()
@@ -471,7 +464,7 @@ class BasicCompletionSession(
if (file != null) {
val receiverInFile =
file.findElementAt(receiver.startOffset)?.getParentOfType<KtSimpleNameExpression>(false)
?: return
?: return
receiverInFile.mainReference.bindToFqName(fqNameToImport, FORCED_SHORTENING)
}
}
@@ -570,7 +563,7 @@ class BasicCompletionSession(
if (keyword in keywordsToSkip) return@complete
when (keyword) {
// if "this" is parsed correctly in the current context - insert it and all this@xxx items
// if "this" is parsed correctly in the current context - insert it and all this@xxx items
"this" -> {
if (expression != null) {
collector.addElements(
@@ -586,7 +579,7 @@ class BasicCompletionSession(
}
}
// if "return" is parsed correctly in the current context - insert it and all return@xxx items
// if "return" is parsed correctly in the current context - insert it and all return@xxx items
"return" -> {
if (expression != null) {
collector.addElements(returnExpressionItems(bindingContext, expression))
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2016 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.
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.completion
@@ -31,7 +20,6 @@ import org.jetbrains.kotlin.idea.completion.handlers.KotlinFunctionInsertHandler
import org.jetbrains.kotlin.idea.core.completion.DeclarationLookupObject
import org.jetbrains.kotlin.idea.core.completion.PackageLookupObject
import org.jetbrains.kotlin.idea.highlighter.dsl.DslHighlighterExtension
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor
import org.jetbrains.kotlin.name.FqName
@@ -46,8 +34,8 @@ import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
import java.awt.Font
class BasicLookupElementFactory(
private val project: Project,
val insertHandlerProvider: InsertHandlerProvider
private val project: Project,
val insertHandlerProvider: InsertHandlerProvider
) {
companion object {
// we skip parameter names in functional types in most of cases for shortness
@@ -58,10 +46,10 @@ class BasicLookupElementFactory(
}
fun createLookupElement(
descriptor: DeclarationDescriptor,
qualifyNestedClasses: Boolean = false,
includeClassTypeArguments: Boolean = true,
parametersAndTypeGrayed: Boolean = false
descriptor: DeclarationDescriptor,
qualifyNestedClasses: Boolean = false,
includeClassTypeArguments: Boolean = true,
parametersAndTypeGrayed: Boolean = false
): LookupElement {
val _descriptor = if (descriptor is CallableMemberDescriptor)
DescriptorUtils.unwrapFakeOverride(descriptor)
@@ -70,14 +58,18 @@ class BasicLookupElementFactory(
return createLookupElementUnwrappedDescriptor(_descriptor, qualifyNestedClasses, includeClassTypeArguments, parametersAndTypeGrayed)
}
fun createLookupElementForJavaClass(psiClass: PsiClass, qualifyNestedClasses: Boolean = false, includeClassTypeArguments: Boolean = true): LookupElement {
fun createLookupElementForJavaClass(
psiClass: PsiClass,
qualifyNestedClasses: Boolean = false,
includeClassTypeArguments: Boolean = true
): LookupElement {
val lookupObject = object : DeclarationLookupObjectImpl(null) {
override val psiElement: PsiElement?
get() = psiClass
override fun getIcon(flags: Int) = psiClass.getIcon(flags)
}
var element = LookupElementBuilder.create(lookupObject, psiClass.name!!)
.withInsertHandler(KotlinClassifierInsertHandler)
var element = LookupElementBuilder.create(lookupObject, psiClass.name!!).withInsertHandler(KotlinClassifierInsertHandler)
val typeParams = psiClass.typeParameters
if (includeClassTypeArguments && typeParams.isNotEmpty()) {
@@ -123,10 +115,10 @@ class BasicLookupElementFactory(
}
private fun createLookupElementUnwrappedDescriptor(
descriptor: DeclarationDescriptor,
qualifyNestedClasses: Boolean,
includeClassTypeArguments: Boolean,
parametersAndTypeGrayed: Boolean
descriptor: DeclarationDescriptor,
qualifyNestedClasses: Boolean,
includeClassTypeArguments: Boolean,
parametersAndTypeGrayed: Boolean
): LookupElement {
if (descriptor is JavaClassDescriptor) {
val declaration = DescriptorToSourceUtilsIde.getAnyDeclaration(project, descriptor)
@@ -168,8 +160,10 @@ class BasicLookupElementFactory(
else -> {
lookupObject = object : DeclarationLookupObjectImpl(descriptor) {
override val psiElement by lazy {
DescriptorToSourceUtils.getSourceFromDescriptor(descriptor)
?: DescriptorToSourceUtilsIde.getAnyDeclaration(project, descriptor)
DescriptorToSourceUtils.getSourceFromDescriptor(descriptor) ?: DescriptorToSourceUtilsIde.getAnyDeclaration(
project,
descriptor
)
}
override fun getIcon(flags: Int) = KotlinDescriptorIconProvider.getIcon(descriptor, psiElement, flags)
@@ -186,14 +180,20 @@ class BasicLookupElementFactory(
when (descriptor) {
is FunctionDescriptor -> {
val returnType = descriptor.returnType
element = element.withTypeText(if (returnType != null) SHORT_NAMES_RENDERER.renderType(returnType) else "", parametersAndTypeGrayed)
element = element.withTypeText(
if (returnType != null) SHORT_NAMES_RENDERER.renderType(returnType) else "",
parametersAndTypeGrayed
)
val insertsLambda = (insertHandler as? KotlinFunctionInsertHandler.Normal)?.lambdaInfo != null
if (insertsLambda) {
element = element.appendTailText(" {...} ", parametersAndTypeGrayed)
}
element = element.appendTailText(SHORT_NAMES_RENDERER.renderFunctionParameters(descriptor), parametersAndTypeGrayed || insertsLambda)
element = element.appendTailText(
SHORT_NAMES_RENDERER.renderFunctionParameters(descriptor),
parametersAndTypeGrayed || insertsLambda
)
}
is VariableDescriptor -> {
@@ -210,8 +210,7 @@ class BasicLookupElementFactory(
if (descriptor.isArtificialImportAliasedDescriptor) {
container = descriptor.original // we show original descriptor instead of container for import aliased descriptors
}
else if (qualifyNestedClasses) {
} else if (qualifyNestedClasses) {
element = element.withPresentableText(SHORT_NAMES_RENDERER.renderClassifierName(descriptor))
while (container is ClassDescriptor) {
@@ -287,19 +286,17 @@ class BasicLookupElementFactory(
val extensionReceiver = descriptor.original.extensionReceiverParameter
if (extensionReceiver != null) {
when {
descriptor is SamAdapterExtensionFunctionDescriptor -> {
when (descriptor) {
is SamAdapterExtensionFunctionDescriptor -> {
// no need to show them as extensions
return
}
descriptor is SyntheticJavaPropertyDescriptor -> {
is SyntheticJavaPropertyDescriptor -> {
var from = descriptor.getMethod.name.asString() + "()"
descriptor.setMethod?.let { from += "/" + it.name.asString() + "()" }
appendTailText(" (from $from)")
return
}
else -> {
val receiverPresentation = SHORT_NAMES_RENDERER.renderType(extensionReceiver.type)
appendTailText(" for $receiverPresentation")
@@ -321,8 +318,7 @@ class BasicLookupElementFactory(
}
descriptor.isExtension -> {
val container = descriptor.containingDeclaration
val containerPresentation = when (container) {
val containerPresentation = when (val container = descriptor.containingDeclaration) {
is ClassDescriptor -> DescriptorUtils.getFqNameFromTopLevelClass(container).toString()
is PackageFragmentDescriptor -> container.fqName.toString()
else -> return null
@@ -332,21 +328,19 @@ class BasicLookupElementFactory(
else -> {
val container = descriptor.containingDeclaration as? PackageFragmentDescriptor
// we show container only for global functions and properties
?: return null
// we show container only for global functions and properties
?: return null
//TODO: it would be probably better to show it also for static declarations which are not from the current class (imported)
return "(${container.fqName})"
}
}
}
private fun LookupElement.withIconFromLookupObject(): LookupElement {
// add icon in renderElement only to pass presentation.isReal()
return object : LookupElementDecorator<LookupElement>(this) {
override fun renderElement(presentation: LookupElementPresentation) {
super.renderElement(presentation)
presentation.icon = DefaultLookupItemRenderer.getRawIcon(this@withIconFromLookupObject, presentation.isReal)
}
// add icon in renderElement only to pass presentation.isReal()
private fun LookupElement.withIconFromLookupObject(): LookupElement = object : LookupElementDecorator<LookupElement>(this) {
override fun renderElement(presentation: LookupElementPresentation) {
super.renderElement(presentation)
presentation.icon = DefaultLookupItemRenderer.getRawIcon(this@withIconFromLookupObject, presentation.isReal)
}
}
}
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2016 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.
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.completion
@@ -56,21 +45,21 @@ class CompletionBindingContextProvider(project: Project) {
internal var TEST_LOG: StringBuilder? = null
companion object {
fun getInstance(project: Project): CompletionBindingContextProvider
= project.getComponent(CompletionBindingContextProvider::class.java)
fun getInstance(project: Project): CompletionBindingContextProvider =
project.getComponent(CompletionBindingContextProvider::class.java)
var ENABLED = true
}
private class CompletionData(
val block: KtBlockExpression,
val prevStatement: KtExpression?,
val psiElementsBeforeAndAfter: List<PsiElementData>,
val bindingContext: BindingContext,
val moduleDescriptor: ModuleDescriptor,
val statementResolutionScope: LexicalScope,
val statementDataFlowInfo: DataFlowInfo,
val debugText: String
val block: KtBlockExpression,
val prevStatement: KtExpression?,
val psiElementsBeforeAndAfter: List<PsiElementData>,
val bindingContext: BindingContext,
val moduleDescriptor: ModuleDescriptor,
val statementResolutionScope: LexicalScope,
val statementDataFlowInfo: DataFlowInfo,
val debugText: String
)
private data class PsiElementData(val element: PsiElement, val level: Int)
@@ -80,7 +69,9 @@ class CompletionBindingContextProvider(project: Project) {
var data: CompletionData?
get() = reference?.get()
set(value) { reference = value?.let { SoftReference(it) } }
set(value) {
reference = value?.let { SoftReference(it) }
}
}
private var prevCompletionDataCache: CachedValue<DataHolder> = CachedValuesManager.getManager(project).createCachedValue(
@@ -94,13 +85,10 @@ class CompletionBindingContextProvider(project: Project) {
)
fun getBindingContext(position: PsiElement, resolutionFacade: ResolutionFacade): BindingContext {
return if (ENABLED) {
_getBindingContext(position, resolutionFacade)
}
else {
resolutionFacade.analyze(position.parentsWithSelf.firstIsInstance<KtElement>(), BodyResolveMode.PARTIAL_FOR_COMPLETION)
}
fun getBindingContext(position: PsiElement, resolutionFacade: ResolutionFacade): BindingContext = if (ENABLED) {
_getBindingContext(position, resolutionFacade)
} else {
resolutionFacade.analyze(position.parentsWithSelf.firstIsInstance<KtElement>(), BodyResolveMode.PARTIAL_FOR_COMPLETION)
}
private fun _getBindingContext(position: PsiElement, resolutionFacade: ResolutionFacade): BindingContext {
@@ -130,23 +118,33 @@ class CompletionBindingContextProvider(project: Project) {
LOG.debug("Reusing data from completion of \"${prevCompletionData.debugText}\"")
//TODO: expected type?
val statementContext = inStatement.analyzeInContext(scope = prevCompletionData.statementResolutionScope,
contextExpression = block,
dataFlowInfo = prevCompletionData.statementDataFlowInfo,
isStatement = true)
val statementContext = inStatement.analyzeInContext(
scope = prevCompletionData.statementResolutionScope,
contextExpression = block,
dataFlowInfo = prevCompletionData.statementDataFlowInfo,
isStatement = true
)
// we do not update prevCompletionDataCache because the same data should work
return CompositeBindingContext.create(listOf(statementContext, prevCompletionData.bindingContext))
}
}
val bindingContext = resolutionFacade.analyze(position.parentsWithSelf.firstIsInstance<KtElement>(), BodyResolveMode.PARTIAL_FOR_COMPLETION)
val bindingContext =
resolutionFacade.analyze(position.parentsWithSelf.firstIsInstance<KtElement>(), BodyResolveMode.PARTIAL_FOR_COMPLETION)
prevCompletionDataCache.value.data = if (block != null && modificationScope != null) {
val resolutionScope = inStatement.getResolutionScope(bindingContext, resolutionFacade)
val dataFlowInfo = bindingContext.getDataFlowInfoBefore(inStatement)
CompletionData(block, prevStatement, psiElementsBeforeAndAfter!!, bindingContext, resolutionFacade.moduleDescriptor, resolutionScope, dataFlowInfo,
debugText = position.text)
}
else {
CompletionData(
block,
prevStatement,
psiElementsBeforeAndAfter!!,
bindingContext,
resolutionFacade.moduleDescriptor,
resolutionScope,
dataFlowInfo,
debugText = position.text
)
} else {
null
}
@@ -1,17 +1,6 @@
/*
* 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.
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.completion
@@ -34,8 +23,8 @@ interface ContextVariablesProvider {
}
class RealContextVariablesProvider(
private val referenceVariantsHelper: ReferenceVariantsHelper,
private val contextElement: PsiElement
private val referenceVariantsHelper: ReferenceVariantsHelper,
private val contextElement: PsiElement
) : ContextVariablesProvider {
val allFunctionTypeVariables by lazy {
@@ -43,9 +32,13 @@ class RealContextVariablesProvider(
}
private fun collectVariables(): Collection<VariableDescriptor> {
val descriptorFilter = DescriptorKindFilter.VARIABLES exclude DescriptorKindExclude.Extensions // we exclude extensions by performance reasons
return referenceVariantsHelper.getReferenceVariants(contextElement, CallTypeAndReceiver.DEFAULT, descriptorFilter, nameFilter = { true })
.map { it as VariableDescriptor }
val descriptorFilter =
DescriptorKindFilter.VARIABLES exclude DescriptorKindExclude.Extensions // we exclude extensions by performance reasons
return referenceVariantsHelper.getReferenceVariants(
contextElement,
CallTypeAndReceiver.DEFAULT,
descriptorFilter,
nameFilter = { true }).map { it as VariableDescriptor }
}
override fun functionTypeVariables(requiredType: FuzzyType): Collection<Pair<VariableDescriptor, TypeSubstitutor>> {
@@ -1,17 +1,6 @@
/*
* 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.
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.completion
@@ -32,8 +21,8 @@ import org.jetbrains.kotlin.util.descriptorsEqualWithSubstitution
* Position will be used for sorting
*/
abstract class DeclarationLookupObjectImpl(
final override val descriptor: DeclarationDescriptor?
): DeclarationLookupObject {
final override val descriptor: DeclarationDescriptor?
) : DeclarationLookupObject {
override val name: Name?
get() = descriptor?.name ?: (psiElement as? PsiNamedElement)?.name?.let { Name.identifier(it) }
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Copyright 2000-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
@@ -37,8 +37,9 @@ class DslMembersCompletion(
val extensionDescriptors = indicesHelper.getCallableTopLevelExtensions(
nameFilter = { prefixMatcher.prefixMatches(it) },
declarationFilter = {
(it as KtModifierListOwner).modifierList?.collectAnnotationEntriesFromStubOrPsi()?.any { it.shortName in receiverMarkersShortNames }
?: false
(it as KtModifierListOwner).modifierList?.collectAnnotationEntriesFromStubOrPsi()
?.any { it.shortName in receiverMarkersShortNames }
?: false
},
callTypeAndReceiver = callTypeAndReceiver,
receiverTypes = listOf(nearestReceiver.type)
@@ -1,17 +1,6 @@
/*
* 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.
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.completion
@@ -32,9 +21,9 @@ import org.jetbrains.kotlin.util.OperatorNameConventions
import java.util.*
class ExtensionFunctionTypeValueCompletion(
private val receiverTypes: Collection<ReceiverType>,
private val callType: CallType<*>,
private val lookupElementFactory: LookupElementFactory
private val receiverTypes: Collection<ReceiverType>,
private val callType: CallType<*>,
private val lookupElementFactory: LookupElementFactory
) {
data class Result(val invokeDescriptor: FunctionDescriptor, val factory: AbstractLookupElementFactory)
@@ -51,7 +40,10 @@ class ExtensionFunctionTypeValueCompletion(
for (invoke in createSynthesizedInvokes(invokes)) {
for (substituted in invoke.substituteExtensionIfCallable(receiverTypes.map { it.type }, callType)) {
val factory = object : AbstractLookupElementFactory {
override fun createStandardLookupElementsForDescriptor(descriptor: DeclarationDescriptor, useReceiverTypes: Boolean): Collection<LookupElement> {
override fun createStandardLookupElementsForDescriptor(
descriptor: DeclarationDescriptor,
useReceiverTypes: Boolean
): Collection<LookupElement> {
if (!useReceiverTypes) return emptyList()
descriptor as FunctionDescriptor // should be descriptor for "invoke"
@@ -82,11 +74,12 @@ class ExtensionFunctionTypeValueCompletion(
}
override fun createLookupElement(
descriptor: DeclarationDescriptor,
useReceiverTypes: Boolean,
qualifyNestedClasses: Boolean,
includeClassTypeArguments: Boolean,
parametersAndTypeGrayed: Boolean): LookupElement? = null
descriptor: DeclarationDescriptor,
useReceiverTypes: Boolean,
qualifyNestedClasses: Boolean,
includeClassTypeArguments: Boolean,
parametersAndTypeGrayed: Boolean
): LookupElement? = null
}
results.add(Result(substituted, factory))
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2016 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.
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.completion
@@ -31,8 +20,8 @@ import org.jetbrains.kotlin.psi.psiUtil.startOffset
import java.util.*
class FromUnresolvedNamesCompletion(
private val collector: LookupElementsCollector,
private val prefixMatcher: PrefixMatcher
private val collector: LookupElementsCollector,
private val prefixMatcher: PrefixMatcher
) {
fun addNameSuggestions(scope: KtElement, afterOffset: Int?, sampleDescriptor: DeclarationDescriptor?) {
val names = HashSet<String>()
@@ -50,7 +39,10 @@ class FromUnresolvedNamesCompletion(
val canBeUsage = when (sampleDescriptor) {
is FunctionDescriptor -> isCall // cannot use simply function name without arguments
is VariableDescriptor -> true // variable can as well be used with arguments when it has invoke()
is ClassDescriptor -> if (isCall) sampleDescriptor.kind == ClassKind.CLASS else sampleDescriptor.kind.isSingleton
is ClassDescriptor -> if (isCall)
sampleDescriptor.kind == ClassKind.CLASS
else
sampleDescriptor.kind.isSingleton
else -> false // what else it can be?
}
if (!canBeUsage) return@forEachDescendantOfType
@@ -69,9 +61,8 @@ class FromUnresolvedNamesCompletion(
}
for (name in names.sorted()) {
val lookupElement = LookupElementBuilder.create(name)
.suppressAutoInsertion()
.assignPriority(ItemPriority.FROM_UNRESOLVED_NAME_SUGGESTION)
val lookupElement =
LookupElementBuilder.create(name).suppressAutoInsertion().assignPriority(ItemPriority.FROM_UNRESOLVED_NAME_SUGGESTION)
lookupElement.putUserData(KotlinCompletionCharFilter.SUPPRESS_ITEM_SELECTION_BY_CHARS_ON_TYPING, Unit)
collector.addElement(lookupElement)
}
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2016 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.
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.completion
@@ -32,8 +21,8 @@ import org.jetbrains.kotlin.types.KotlinType
import java.util.*
class InsertHandlerProvider(
private val callType: CallType<*>,
expectedInfosCalculator: () -> Collection<ExpectedInfo>
private val callType: CallType<*>,
expectedInfosCalculator: () -> Collection<ExpectedInfo>
) {
private val expectedInfos by lazy(LazyThreadSafetyMode.NONE) { expectedInfosCalculator() }
@@ -55,8 +44,8 @@ class InsertHandlerProvider(
if (getValueParametersCountFromFunctionType(parameterType) <= 1 && !parameter.hasDefaultValue()) {
// otherwise additional item with lambda template is to be added
return KotlinFunctionInsertHandler.Normal(
callType, needTypeArguments, inputValueArguments = false,
lambdaInfo = GenerateLambdaInfo(parameterType, false)
callType, needTypeArguments, inputValueArguments = false,
lambdaInfo = GenerateLambdaInfo(parameterType, false)
)
}
}
@@ -126,7 +115,10 @@ class InsertHandlerProvider(
if (returnType != null) {
addPotentiallyInferred(returnType)
if (allTypeParametersPotentiallyInferred() && expectedInfos.any { it.fuzzyType?.checkIsSuperTypeOf(originalFunction.fuzzyReturnType()!!) != null }) {
if (allTypeParametersPotentiallyInferred() && expectedInfos.any {
it.fuzzyType?.checkIsSuperTypeOf(originalFunction.fuzzyReturnType()!!) != null
}
) {
return false
}
}
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2016 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.
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.completion
@@ -40,22 +29,23 @@ import org.jetbrains.kotlin.types.typeUtil.isBooleanOrNullableBoolean
object KeywordValues {
interface Consumer {
fun consume(
lookupString: String,
expectedInfoMatcher: (ExpectedInfo) -> ExpectedInfoMatch,
priority: SmartCompletionItemPriority,
factory: () -> LookupElement)
lookupString: String,
expectedInfoMatcher: (ExpectedInfo) -> ExpectedInfoMatch,
priority: SmartCompletionItemPriority,
factory: () -> LookupElement
)
}
fun process(
consumer: Consumer,
callTypeAndReceiver: CallTypeAndReceiver<*, *>,
bindingContext: BindingContext,
resolutionFacade: ResolutionFacade,
moduleDescriptor: ModuleDescriptor,
isJvmModule: Boolean
consumer: Consumer,
callTypeAndReceiver: CallTypeAndReceiver<*, *>,
bindingContext: BindingContext,
resolutionFacade: ResolutionFacade,
moduleDescriptor: ModuleDescriptor,
isJvmModule: Boolean
) {
if (callTypeAndReceiver is CallTypeAndReceiver.DEFAULT) {
val booleanInfoMatcher = matcher@ { info: ExpectedInfo ->
val booleanInfoMatcher = matcher@{ info: ExpectedInfo ->
// no sense in true or false as if-condition or when entry for when with no subject
val additionalData = info.additionalData
val skipTrueFalse = when (additionalData) {
@@ -72,10 +62,10 @@ object KeywordValues {
else
ExpectedInfoMatch.noMatch
}
consumer.consume("true", booleanInfoMatcher, SmartCompletionItemPriority.TRUE) {
consumer.consume("true", booleanInfoMatcher, SmartCompletionItemPriority.TRUE) {
LookupElementBuilder.create(KeywordLookupObject(), "true").bold()
}
consumer.consume("false", booleanInfoMatcher, SmartCompletionItemPriority.FALSE) {
consumer.consume("false", booleanInfoMatcher, SmartCompletionItemPriority.FALSE) {
LookupElementBuilder.create(KeywordLookupObject(), "false").bold()
}
@@ -97,7 +87,8 @@ object KeywordValues {
val qualifierType = bindingContext.get(BindingContext.DOUBLE_COLON_LHS, callTypeAndReceiver.receiver!!)?.type
if (qualifierType != null) {
val kClassDescriptor = resolutionFacade.getFrontendService(ReflectionTypes::class.java).kClass
val classLiteralType = KotlinTypeFactory.simpleNotNullType(Annotations.EMPTY, kClassDescriptor, listOf(TypeProjectionImpl(qualifierType)))
val classLiteralType =
KotlinTypeFactory.simpleNotNullType(Annotations.EMPTY, kClassDescriptor, listOf(TypeProjectionImpl(qualifierType)))
val kClassTypes = listOf(classLiteralType.toFuzzyType(emptyList()))
val kClassMatcher = { info: ExpectedInfo -> kClassTypes.matchExpectedInfo(info) }
consumer.consume("class", kClassMatcher, SmartCompletionItemPriority.CLASS_LITERAL) {
@@ -106,17 +97,21 @@ object KeywordValues {
if (isJvmModule) {
val javaLangClassDescriptor = resolutionFacade.resolveImportReference(moduleDescriptor, FqName("java.lang.Class"))
.singleOrNull() as? ClassDescriptor
.singleOrNull() as? ClassDescriptor
if (javaLangClassDescriptor != null) {
val javaLangClassType = KotlinTypeFactory.simpleNotNullType(Annotations.EMPTY, javaLangClassDescriptor, listOf(TypeProjectionImpl(qualifierType)))
val javaLangClassType = KotlinTypeFactory.simpleNotNullType(
Annotations.EMPTY,
javaLangClassDescriptor,
listOf(TypeProjectionImpl(qualifierType))
)
val javaClassTypes = listOf(javaLangClassType.toFuzzyType(emptyList()))
val javaClassMatcher = { info: ExpectedInfo -> javaClassTypes.matchExpectedInfo(info) }
consumer.consume("class", javaClassMatcher, SmartCompletionItemPriority.CLASS_LITERAL) {
LookupElementBuilder.create(KeywordLookupObject(), "class.java")
.withPresentableText("class")
.withTailText(".java")
.bold()
.withPresentableText("class")
.withTailText(".java")
.bold()
}
}
}
@@ -1,17 +1,6 @@
/*
* 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.
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.completion
@@ -26,13 +15,14 @@ class KotlinCompletionCharFilter() : CharFilter() {
companion object {
val ACCEPT_OPENING_BRACE: Key<Unit> = Key("KotlinCompletionCharFilter.ACCEPT_OPENING_BRACE")
val SUPPRESS_ITEM_SELECTION_BY_CHARS_ON_TYPING: Key<Unit> = Key("KotlinCompletionCharFilter.SUPPRESS_ITEM_SELECTION_BY_CHARS_ON_TYPING")
val SUPPRESS_ITEM_SELECTION_BY_CHARS_ON_TYPING: Key<Unit> =
Key("KotlinCompletionCharFilter.SUPPRESS_ITEM_SELECTION_BY_CHARS_ON_TYPING")
val HIDE_LOOKUP_ON_COLON: Key<Unit> = Key("KotlinCompletionCharFilter.HIDE_LOOKUP_ON_COLON")
val JUST_TYPING_PREFIX: Key<String> = Key("KotlinCompletionCharFilter.JUST_TYPING_PREFIX")
}
override fun acceptChar(c : Char, prefixLength : Int, lookup : Lookup) : Result? {
override fun acceptChar(c: Char, prefixLength: Int, lookup: Lookup): Result? {
if (lookup.psiFile !is KtFile) return null
if (!lookup.isCompletion) return null
val isAutopopup = CompletionService.getCompletionService().currentCompletion?.isAutopopupCompletion ?: return null
@@ -1,17 +1,6 @@
/*
* 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.
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.completion
@@ -50,11 +39,18 @@ import kotlin.math.max
var KtFile.doNotComplete: Boolean? by UserDataProperty(Key.create("DO_NOT_COMPLETE"))
class KotlinCompletionContributor : CompletionContributor() {
private val AFTER_NUMBER_LITERAL = psiElement().afterLeafSkipping(psiElement().withText(""), psiElement().withElementType(elementType().oneOf(KtTokens.FLOAT_LITERAL, KtTokens.INTEGER_LITERAL)))
private val AFTER_INTEGER_LITERAL_AND_DOT = psiElement().afterLeafSkipping(psiElement().withText("."), psiElement().withElementType(elementType().oneOf(KtTokens.INTEGER_LITERAL)))
private val AFTER_NUMBER_LITERAL = psiElement().afterLeafSkipping(
psiElement().withText(""),
psiElement().withElementType(elementType().oneOf(KtTokens.FLOAT_LITERAL, KtTokens.INTEGER_LITERAL))
)
private val AFTER_INTEGER_LITERAL_AND_DOT = psiElement().afterLeafSkipping(
psiElement().withText("."),
psiElement().withElementType(elementType().oneOf(KtTokens.INTEGER_LITERAL))
)
companion object {
val DEFAULT_DUMMY_IDENTIFIER: String = CompletionUtilCore.DUMMY_IDENTIFIER_TRIMMED + "$" // add '$' to ignore context after the caret
val DEFAULT_DUMMY_IDENTIFIER: String =
CompletionUtilCore.DUMMY_IDENTIFIER_TRIMMED + "$" // add '$' to ignore context after the caret
}
init {
@@ -102,15 +98,16 @@ class KotlinCompletionContributor : CompletionContributor() {
isInSimpleStringTemplate(tokenBefore) -> CompletionUtilCore.DUMMY_IDENTIFIER_TRIMMED
else -> specialLambdaSignatureDummyIdentifier(tokenBefore)
?: specialExtensionReceiverDummyIdentifier(tokenBefore)
?: specialInTypeArgsDummyIdentifier(tokenBefore)
?: specialInArgumentListDummyIdentifier(tokenBefore)
?: DEFAULT_DUMMY_IDENTIFIER
?: specialExtensionReceiverDummyIdentifier(tokenBefore)
?: specialInTypeArgsDummyIdentifier(tokenBefore)
?: specialInArgumentListDummyIdentifier(tokenBefore)
?: DEFAULT_DUMMY_IDENTIFIER
}
val tokenAt = psiFile.findElementAt(max(0, offset))
if (tokenAt != null) {
if (context.completionType == CompletionType.SMART && !isAtEndOfLine(offset, context.editor.document) /* do not use parent expression if we are at the end of line - it's probably parsed incorrectly */) {
/* do not use parent expression if we are at the end of line - it's probably parsed incorrectly */
if (context.completionType == CompletionType.SMART && !isAtEndOfLine(offset, context.editor.document)) {
var parent = tokenAt.parent
if (parent is KtExpression && parent !is KtBlockExpression) {
// search expression to be replaced - go up while we are the first child of parent expression
@@ -130,8 +127,10 @@ class KotlinCompletionContributor : CompletionContributor() {
val argumentList = (expression.parent as? KtValueArgument)?.parent as? KtValueArgumentList
if (argumentList != null) {
context.offsetMap.addOffset(SmartCompletion.MULTIPLE_ARGUMENTS_REPLACEMENT_OFFSET,
argumentList.rightParenthesis?.textRange?.startOffset ?: argumentList.endOffset)
context.offsetMap.addOffset(
SmartCompletion.MULTIPLE_ARGUMENTS_REPLACEMENT_OFFSET,
argumentList.rightParenthesis?.textRange?.startOffset ?: argumentList.endOffset
)
}
}
}
@@ -193,12 +192,16 @@ class KotlinCompletionContributor : CompletionContributor() {
}
private val declarationKeywords = TokenSet.create(KtTokens.FUN_KEYWORD, KtTokens.VAL_KEYWORD, KtTokens.VAR_KEYWORD)
private val declarationTokens = TokenSet.orSet(TokenSet.create(KtTokens.IDENTIFIER, KtTokens.LT, KtTokens.GT,
KtTokens.COMMA, KtTokens.DOT, KtTokens.QUEST, KtTokens.COLON,
KtTokens.IN_KEYWORD, KtTokens.OUT_KEYWORD,
KtTokens.LPAR, KtTokens.RPAR, KtTokens.ARROW,
TokenType.ERROR_ELEMENT),
KtTokens.WHITE_SPACE_OR_COMMENT_BIT_SET)
private val declarationTokens = TokenSet.orSet(
TokenSet.create(
KtTokens.IDENTIFIER, KtTokens.LT, KtTokens.GT,
KtTokens.COMMA, KtTokens.DOT, KtTokens.QUEST, KtTokens.COLON,
KtTokens.IN_KEYWORD, KtTokens.OUT_KEYWORD,
KtTokens.LPAR, KtTokens.RPAR, KtTokens.ARROW,
TokenType.ERROR_ELEMENT
),
KtTokens.WHITE_SPACE_OR_COMMENT_BIT_SET
)
private fun specialExtensionReceiverDummyIdentifier(tokenBefore: PsiElement?): String? {
var token = tokenBefore ?: return null
@@ -223,8 +226,8 @@ class KotlinCompletionContributor : CompletionContributor() {
val file = KtPsiFactory(tokenBefore.project).createFile(text)
val declaration = file.declarations.singleOrNull() ?: return null
if (declaration.textLength != text.length) return null
val containsErrorElement = !PsiTreeUtil.processElements(file, PsiElementProcessor<PsiElement>{ it !is PsiErrorElement })
return if (containsErrorElement) null else tail + "$"
val containsErrorElement = !PsiTreeUtil.processElements(file, PsiElementProcessor<PsiElement> { it !is PsiErrorElement })
return if (containsErrorElement) null else "$tail$"
}
if (tokenType !in declarationTokens) return null
if (tokenType == KtTokens.LT) ltCount++
@@ -266,10 +269,10 @@ class KotlinCompletionContributor : CompletionContributor() {
}
private fun doComplete(
parameters: CompletionParameters,
toFromOriginalFileMapper: ToFromOriginalFileMapper,
result: CompletionResultSet,
lookupElementPostProcessor: ((LookupElement) -> LookupElement)? = null
parameters: CompletionParameters,
toFromOriginalFileMapper: ToFromOriginalFileMapper,
result: CompletionResultSet,
lookupElementPostProcessor: ((LookupElement) -> LookupElement)? = null
) {
val name = parameters.originalFile.virtualFile?.name ?: "default.kts"
completionStatsData = completionStatsData?.copy(
@@ -328,20 +331,19 @@ class KotlinCompletionContributor : CompletionContributor() {
if (!somethingAdded && parameters.invocationCount < 2) {
// Rerun completion if nothing was found
val newConfiguration = CompletionSessionConfiguration(
useBetterPrefixMatcherForNonImportedClasses = false,
nonAccessibleDeclarations = false,
javaGettersAndSetters = true,
javaClassesNotToBeUsed = false,
staticMembers = parameters.invocationCount > 0,
dataClassComponentFunctions = true
useBetterPrefixMatcherForNonImportedClasses = false,
nonAccessibleDeclarations = false,
javaGettersAndSetters = true,
javaClassesNotToBeUsed = false,
staticMembers = parameters.invocationCount > 0,
dataClassComponentFunctions = true
)
val newSession = BasicCompletionSession(newConfiguration, parameters, toFromOriginalFileMapper, result)
addPostProcessor(newSession)
newSession.complete()
}
}
else {
} else {
val session = SmartCompletionSession(configuration, parameters, toFromOriginalFileMapper, result)
addPostProcessor(session)
session.complete()
@@ -421,8 +423,7 @@ class KotlinCompletionContributor : CompletionContributor() {
val targets = nameRef.getReferenceTargets(bindingContext)
return if (targets.isNotEmpty() && targets.all { it is FunctionDescriptor || it is ClassDescriptor && it.kind == ClassKind.CLASS }) {
CompletionUtilCore.DUMMY_IDENTIFIER_TRIMMED + ">".repeat(balance) + "$"
}
else {
} else {
null
}
}
@@ -432,16 +433,19 @@ class KotlinCompletionContributor : CompletionContributor() {
val pair = unclosedTypeArgListNameAndBalance(nameToken)
return if (pair == null) {
Pair(nameToken, 1)
}
else {
} else {
Pair(pair.first, pair.second + 1)
}
}
private val callTypeArgsTokens = TokenSet.orSet(TokenSet.create(KtTokens.IDENTIFIER, KtTokens.LT, KtTokens.GT,
KtTokens.COMMA, KtTokens.DOT, KtTokens.QUEST, KtTokens.COLON,
KtTokens.LPAR, KtTokens.RPAR, KtTokens.ARROW),
KtTokens.WHITE_SPACE_OR_COMMENT_BIT_SET)
private val callTypeArgsTokens = TokenSet.orSet(
TokenSet.create(
KtTokens.IDENTIFIER, KtTokens.LT, KtTokens.GT,
KtTokens.COMMA, KtTokens.DOT, KtTokens.QUEST, KtTokens.COLON,
KtTokens.LPAR, KtTokens.RPAR, KtTokens.ARROW
),
KtTokens.WHITE_SPACE_OR_COMMENT_BIT_SET
)
// if the leaf could be located inside type argument list of a call (if parsed properly)
// then it returns the call name reference this type argument list would belong to
@@ -479,7 +483,7 @@ class KotlinCompletionContributor : CompletionContributor() {
private fun isInUnclosedSuperQualifier(tokenBefore: PsiElement?): Boolean {
if (tokenBefore == null) return false
val tokensToSkip = TokenSet.orSet(TokenSet.create(KtTokens.IDENTIFIER, KtTokens.DOT ), KtTokens.WHITE_SPACE_OR_COMMENT_BIT_SET)
val tokensToSkip = TokenSet.orSet(TokenSet.create(KtTokens.IDENTIFIER, KtTokens.DOT), KtTokens.WHITE_SPACE_OR_COMMENT_BIT_SET)
val tokens = generateSequence(tokenBefore) { it.prevLeaf() }
val ltToken = tokens.firstOrNull { it.node.elementType !in tokensToSkip } ?: return false
if (ltToken.node.elementType != KtTokens.LT) return false
@@ -497,6 +501,6 @@ abstract class KotlinCompletionExtension {
companion object {
val EP_NAME: ExtensionPointName<KotlinCompletionExtension> =
ExtensionPointName.create<KotlinCompletionExtension>("org.jetbrains.kotlin.completionExtension")
ExtensionPointName.create<KotlinCompletionExtension>("org.jetbrains.kotlin.completionExtension")
}
}
@@ -1,17 +1,6 @@
/*
* 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.
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.completion
@@ -43,8 +32,8 @@ class KotlinExcludeFromCompletionLookupActionProvider : LookupActionProvider {
}
private class ExcludeFromCompletionAction(
private val project: Project,
private val exclude: String
private val project: Project,
private val exclude: String
) : LookupElementAction(null, "Exclude '$exclude' from completion") {
override fun performLookupAction(): Result {
AddImportAction.excludeFromImport(project, exclude)
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2017 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.
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.completion
@@ -46,11 +35,11 @@ import org.jetbrains.kotlin.types.TypeProjection
object LambdaSignatureTemplates {
fun insertTemplate(
context: InsertionContext,
placeholderRange: TextRange,
lambdaType: KotlinType,
explicitParameterTypes: Boolean,
signatureOnly: Boolean
context: InsertionContext,
placeholderRange: TextRange,
lambdaType: KotlinType,
explicitParameterTypes: Boolean,
signatureOnly: Boolean
) {
// we start template later to not interfere with insertion of tail type
val commandProcessor = CommandProcessor.getInstance()
@@ -77,8 +66,7 @@ object LambdaSignatureTemplates {
val template = buildTemplate(lambdaType, signatureOnly, explicitParameterTypes, context.project)
TemplateManager.getInstance(context.project).startTemplate(context.editor, template)
}
}
finally {
} finally {
rangeMarker.dispose()
}
}
@@ -114,16 +102,18 @@ object LambdaSignatureTemplates {
fun explicitParameterTypesRequired(file: KtFile, placeholderRange: TextRange, lambdaType: KotlinType): Boolean {
PsiDocumentManager.getInstance(file.project).commitAllDocuments()
val expression = PsiTreeUtil.findElementOfClassAtRange(file, placeholderRange.startOffset, placeholderRange.endOffset, KtExpression::class.java)
?: return false
val expression =
PsiTreeUtil.findElementOfClassAtRange(file, placeholderRange.startOffset, placeholderRange.endOffset, KtExpression::class.java)
?: return false
val resolutionFacade = file.getResolutionFacade()
val bindingContext = resolutionFacade.analyze(expression, BodyResolveMode.PARTIAL)
val expectedInfos = ExpectedInfos(bindingContext, resolutionFacade, indicesHelper = null, useHeuristicSignatures = false).calculate(expression)
val expectedInfos =
ExpectedInfos(bindingContext, resolutionFacade, indicesHelper = null, useHeuristicSignatures = false).calculate(expression)
val functionTypes = expectedInfos
.mapNotNull { it.fuzzyType?.type }
.filter(KotlinType::isFunctionOrSuspendFunctionType)
.toSet()
.mapNotNull { it.fuzzyType?.type }
.filter(KotlinType::isFunctionOrSuspendFunctionType)
.toSet()
return explicitParameterTypesRequired(functionTypes, lambdaType)
}
@@ -138,10 +128,10 @@ object LambdaSignatureTemplates {
}
private fun buildTemplate(
lambdaType: KotlinType,
signatureOnly: Boolean,
explicitParameterTypes: Boolean,
project: Project
lambdaType: KotlinType,
signatureOnly: Boolean,
explicitParameterTypes: Boolean,
project: Project
): Template {
val parameterTypes = functionParameterTypes(lambdaType)
@@ -161,14 +151,13 @@ object LambdaSignatureTemplates {
}
//TODO: check for names in scope
val parameterName = parameterType.extractParameterNameFromFunctionTypeArgument()?.render()
val nameExpression = if (parameterName != null) {
val nameExpression = if (parameterName != null) {
object : Expression() {
override fun calculateResult(context: ExpressionContext?) = TextResult(parameterName)
override fun calculateQuickResult(context: ExpressionContext?): Result? = TextResult(parameterName)
override fun calculateLookupItems(context: ExpressionContext?) = emptyArray<LookupElement>()
}
}
else {
} else {
val count = (noNameParameterCount[parameterType] ?: 0) + 1
noNameParameterCount[parameterType] = count
val suffix = if (count == 1) null else "$count"
@@ -176,8 +165,8 @@ object LambdaSignatureTemplates {
object : Expression() {
override fun calculateResult(context: ExpressionContext?) = TextResult(nameSuggestions[0])
override fun calculateQuickResult(context: ExpressionContext?): Result? = null
override fun calculateLookupItems(context: ExpressionContext?)
= nameSuggestions.map { LookupElementBuilder.create(it) }.toTypedArray()
override fun calculateLookupItems(context: ExpressionContext?) =
nameSuggestions.map { LookupElementBuilder.create(it) }.toTypedArray()
}
}
template.addVariable(nameExpression, true)
@@ -201,7 +190,7 @@ object LambdaSignatureTemplates {
val suggestions = KotlinNameSuggester.suggestNamesByType(parameterType, { true }, "p")
return if (suffix != null) suggestions.map { "$it$suffix" } else suggestions
}
private fun nameSuggestion(parameterType: KotlinType) = nameSuggestions(parameterType)[0]
private fun functionParameterTypes(functionType: KotlinType): List<KotlinType> {
@@ -1,17 +1,6 @@
/*
* 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.
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.completion
@@ -24,11 +13,9 @@ import com.intellij.psi.tree.IElementType
class LeafElementFilter(private val elementType: IElementType) : ElementFilter {
override fun isAcceptable(element: Any?, context: PsiElement?)
= element is LeafPsiElement && element.elementType == elementType
override fun isAcceptable(element: Any?, context: PsiElement?) = element is LeafPsiElement && element.elementType == elementType
override fun isClassAcceptable(hintClass: Class<*>)
= LEAF_CLASS_FILTER.isClassAcceptable(hintClass)
override fun isClassAcceptable(hintClass: Class<*>) = LEAF_CLASS_FILTER.isClassAcceptable(hintClass)
companion object {
private val LEAF_CLASS_FILTER = ClassFilter(LeafPsiElement::class.java)
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2016 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.
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.completion
@@ -55,22 +44,22 @@ interface AbstractLookupElementFactory {
fun createStandardLookupElementsForDescriptor(descriptor: DeclarationDescriptor, useReceiverTypes: Boolean): Collection<LookupElement>
fun createLookupElement(
descriptor: DeclarationDescriptor,
useReceiverTypes: Boolean,
qualifyNestedClasses: Boolean = false,
includeClassTypeArguments: Boolean = true,
parametersAndTypeGrayed: Boolean = false
descriptor: DeclarationDescriptor,
useReceiverTypes: Boolean,
qualifyNestedClasses: Boolean = false,
includeClassTypeArguments: Boolean = true,
parametersAndTypeGrayed: Boolean = false
): LookupElement?
}
data /* we need copy() */
class LookupElementFactory(
val basicFactory: BasicLookupElementFactory,
private val receiverTypes: Collection<ReceiverType>?,
private val callType: CallType<*>,
private val inDescriptor: DeclarationDescriptor,
private val contextVariablesProvider: ContextVariablesProvider,
private val standardLookupElementsPostProcessor: (LookupElement) -> LookupElement = { it }
val basicFactory: BasicLookupElementFactory,
private val receiverTypes: Collection<ReceiverType>?,
private val callType: CallType<*>,
private val inDescriptor: DeclarationDescriptor,
private val contextVariablesProvider: ContextVariablesProvider,
private val standardLookupElementsPostProcessor: (LookupElement) -> LookupElement = { it }
) : AbstractLookupElementFactory {
companion object {
fun hasSingleFunctionTypeParameter(descriptor: FunctionDescriptor): Boolean {
@@ -84,18 +73,18 @@ class LookupElementFactory(
val insertHandlerProvider = basicFactory.insertHandlerProvider
private val superFunctions: Set<FunctionDescriptor> by lazy {
inDescriptor.parentsWithSelf
.takeWhile { it !is ClassDescriptor }
.filterIsInstance<FunctionDescriptor>()
.toList()
.flatMap { it.findOriginalTopMostOverriddenDescriptors() }
.toSet()
inDescriptor.parentsWithSelf.takeWhile { it !is ClassDescriptor }.filterIsInstance<FunctionDescriptor>().toList()
.flatMap { it.findOriginalTopMostOverriddenDescriptors() }.toSet()
}
override fun createStandardLookupElementsForDescriptor(descriptor: DeclarationDescriptor, useReceiverTypes: Boolean): Collection<LookupElement> {
override fun createStandardLookupElementsForDescriptor(
descriptor: DeclarationDescriptor,
useReceiverTypes: Boolean
): Collection<LookupElement> {
val result = SmartList<LookupElement>()
val isNormalCall = callType == CallType.DEFAULT || callType == CallType.DOT || callType == CallType.SAFE || callType == CallType.SUPER_MEMBERS
val isNormalCall =
callType == CallType.DEFAULT || callType == CallType.DOT || callType == CallType.SAFE || callType == CallType.SUPER_MEMBERS
result.add(createLookupElement(descriptor, useReceiverTypes, parametersAndTypeGrayed = !isNormalCall && callType != CallType.INFIX))
@@ -103,8 +92,7 @@ class LookupElementFactory(
if (descriptor is FunctionDescriptor && isNormalCall) {
if (callType != CallType.SUPER_MEMBERS) {
result.addSpecialFunctionCallElements(descriptor, useReceiverTypes)
}
else if (useReceiverTypes) {
} else if (useReceiverTypes) {
result.addIfNotNull(createSuperFunctionCallWithArguments(descriptor))
}
}
@@ -149,7 +137,14 @@ class LookupElementFactory(
val insertHandler = insertHandlerProvider.insertHandler(descriptor) as KotlinFunctionInsertHandler.Normal
if (insertHandler.lambdaInfo == null) {
val functionParameterCount = getValueParametersCountFromFunctionType(parameterType)
add(createFunctionCallElementWithLambda(descriptor, parameterType, useReceiverTypes, explicitLambdaParameters = functionParameterCount > 1))
add(
createFunctionCallElementWithLambda(
descriptor,
parameterType,
useReceiverTypes,
explicitLambdaParameters = functionParameterCount > 1
)
)
}
if (isSingleParameter) {
@@ -165,10 +160,10 @@ class LookupElementFactory(
}
private fun createFunctionCallElementWithLambda(
descriptor: FunctionDescriptor,
parameterType: KotlinType,
useReceiverTypes: Boolean,
explicitLambdaParameters: Boolean
descriptor: FunctionDescriptor,
parameterType: KotlinType,
useReceiverTypes: Boolean,
explicitLambdaParameters: Boolean
): LookupElement {
var lookupElement = createLookupElement(descriptor, useReceiverTypes)
val inputTypeArguments = (insertHandlerProvider.insertHandler(descriptor) as KotlinFunctionInsertHandler.Normal).inputTypeArguments
@@ -182,14 +177,20 @@ class LookupElementFactory(
var parametersRenderer = BasicLookupElementFactory.SHORT_NAMES_RENDERER
if (descriptor.valueParameters.size > 1) {
parametersRenderer = parametersRenderer.withOptions {
valueParametersHandler = object: DescriptorRenderer.ValueParametersHandler by this.valueParametersHandler {
override fun appendBeforeValueParameter(parameter: ValueParameterDescriptor, parameterIndex: Int, parameterCount: Int, builder: StringBuilder) {
valueParametersHandler = object : DescriptorRenderer.ValueParametersHandler by this.valueParametersHandler {
override fun appendBeforeValueParameter(
parameter: ValueParameterDescriptor,
parameterIndex: Int,
parameterCount: Int,
builder: StringBuilder
) {
builder.append("..., ")
}
}
}
}
val parametersPresentation = parametersRenderer.renderValueParameters(listOf(descriptor.valueParameters.last()), descriptor.hasSynthesizedParameterNames())
val parametersPresentation =
parametersRenderer.renderValueParameters(listOf(descriptor.valueParameters.last()), descriptor.hasSynthesizedParameterNames())
lookupElement = object : LookupElementDecorator<LookupElement>(lookupElement) {
override fun renderElement(presentation: LookupElementPresentation) {
@@ -202,7 +203,8 @@ class LookupElementFactory(
}
override fun handleInsert(context: InsertionContext) {
KotlinFunctionInsertHandler.Normal(callType, inputTypeArguments, inputValueArguments = false, lambdaInfo = lambdaInfo).handleInsert(context, this)
KotlinFunctionInsertHandler.Normal(callType, inputTypeArguments, inputValueArguments = false, lambdaInfo = lambdaInfo)
.handleInsert(context, this)
}
}
@@ -223,7 +225,11 @@ class LookupElementFactory(
return lookupElement
}
private fun createFunctionCallElementWithArguments(descriptor: FunctionDescriptor, argumentText: String, useReceiverTypes: Boolean): LookupElement {
private fun createFunctionCallElementWithArguments(
descriptor: FunctionDescriptor,
argumentText: String,
useReceiverTypes: Boolean
): LookupElement {
val lookupElement = createLookupElement(descriptor, useReceiverTypes)
val needTypeArguments = (insertHandlerProvider.insertHandler(descriptor) as KotlinFunctionInsertHandler.Normal).inputTypeArguments
@@ -231,13 +237,15 @@ class LookupElementFactory(
}
private inner class FunctionCallWithArgumentsLookupElement(
originalLookupElement: LookupElement,
private val descriptor: FunctionDescriptor,
private val argumentText: String,
private val needTypeArguments: Boolean
originalLookupElement: LookupElement,
private val descriptor: FunctionDescriptor,
private val argumentText: String,
private val needTypeArguments: Boolean
) : LookupElementDecorator<LookupElement>(originalLookupElement) {
override fun equals(other: Any?) = other is FunctionCallWithArgumentsLookupElement && delegate == other.delegate && argumentText == other.argumentText
override fun equals(other: Any?) =
other is FunctionCallWithArgumentsLookupElement && delegate == other.delegate && argumentText == other.argumentText
override fun hashCode() = delegate.hashCode() * 17 + argumentText.hashCode()
override fun renderElement(presentation: LookupElementPresentation) {
@@ -249,17 +257,21 @@ class LookupElementFactory(
}
override fun handleInsert(context: InsertionContext) {
KotlinFunctionInsertHandler.Normal(callType, inputTypeArguments = needTypeArguments, inputValueArguments = false, argumentText = argumentText)
.handleInsert(context, this)
KotlinFunctionInsertHandler.Normal(
callType,
inputTypeArguments = needTypeArguments,
inputValueArguments = false,
argumentText = argumentText
).handleInsert(context, this)
}
}
override fun createLookupElement(
descriptor: DeclarationDescriptor,
useReceiverTypes: Boolean,
qualifyNestedClasses: Boolean,
includeClassTypeArguments: Boolean,
parametersAndTypeGrayed: Boolean
descriptor: DeclarationDescriptor,
useReceiverTypes: Boolean,
qualifyNestedClasses: Boolean,
includeClassTypeArguments: Boolean,
parametersAndTypeGrayed: Boolean
): LookupElement {
var element = basicFactory.createLookupElement(descriptor, qualifyNestedClasses, includeClassTypeArguments, parametersAndTypeGrayed)
@@ -286,8 +298,7 @@ class LookupElementFactory(
super.renderElement(presentation)
if (style == Style.BOLD) {
presentation.isItemTextBold = true
}
else {
} else {
presentation.itemTextForeground = CAST_REQUIRED_COLOR
// gray all tail fragments too:
val fragments = presentation.tailFragments
@@ -298,8 +309,7 @@ class LookupElementFactory(
}
}
}
}
else {
} else {
this
}
}
@@ -324,15 +334,11 @@ class LookupElementFactory(
if (descriptor.overriddenDescriptors.isNotEmpty()) {
// Optimization: when one of direct overridden fits, then nothing can fit better
descriptor.overriddenDescriptors
.mapNotNull { it.callableWeightBasedOnReceiver(receiverTypes, onReceiverTypeMismatch = null) }
.minBy { it.enum }
?.let { return it }
descriptor.overriddenDescriptors.mapNotNull { it.callableWeightBasedOnReceiver(receiverTypes, onReceiverTypeMismatch = null) }
.minBy { it.enum }?.let { return it }
val overridden = descriptor.overriddenTreeUniqueAsSequence(useOriginal = false)
return overridden
.map { callableWeightBasic(it, receiverTypes)!! }
.minBy { it.enum }!!
return overridden.map { callableWeightBasic(it, receiverTypes)!! }.minBy { it.enum }!!
}
return callableWeightBasic(descriptor, receiverTypes)
@@ -348,8 +354,8 @@ class LookupElementFactory(
}
private fun CallableDescriptor.callableWeightBasedOnReceiver(
receiverTypes: Collection<ReceiverType>,
onReceiverTypeMismatch: CallableWeight?
receiverTypes: Collection<ReceiverType>,
onReceiverTypeMismatch: CallableWeight?
): CallableWeight? {
val bothReceivers = listOfNotNull(extensionReceiverParameter, dispatchReceiverParameter)
@@ -357,18 +363,18 @@ class LookupElementFactory(
val receiverTypesForFirstReceiver = receiverTypes.filterNot { it.implicit }.ifEmpty { receiverTypes }
val weights = bothReceivers.zip(generateSequence(receiverTypesForFirstReceiver) { receiverTypes }.asIterable())
.map { (receiverParameter, receiverTypes) ->
callableWeightBasedOnReceiver(receiverTypes, onReceiverTypeMismatch, receiverParameter)
}
.map { (receiverParameter, receiverTypes) ->
callableWeightBasedOnReceiver(receiverTypes, onReceiverTypeMismatch, receiverParameter)
}
if (weights.any { it == onReceiverTypeMismatch }) return onReceiverTypeMismatch
return weights.firstOrNull()
}
private fun CallableDescriptor.callableWeightBasedOnReceiver(
receiverTypes: Collection<ReceiverType>,
onReceiverTypeMismatch: CallableWeight?,
receiverParameter: ReceiverParameterDescriptor
receiverTypes: Collection<ReceiverType>,
onReceiverTypeMismatch: CallableWeight?,
receiverParameter: ReceiverParameterDescriptor
): CallableWeight? {
if ((receiverParameter.value as? TransientReceiver)?.type?.isFunctionType == true) return null
@@ -403,8 +409,8 @@ class LookupElementFactory(
}
private fun CallableDescriptor.callableWeightForReceiverType(
receiverType: KotlinType,
receiverParameterType: KotlinType
receiverType: KotlinType,
receiverParameterType: KotlinType
): CallableWeightEnum? = when {
TypeUtils.equalTypes(receiverType, receiverParameterType) -> when {
isExtensionForTypeParameter() -> CallableWeightEnum.typeParameterExtension
@@ -1,17 +1,6 @@
/*
* 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.
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.completion
@@ -33,13 +22,13 @@ import java.util.*
import kotlin.math.max
class LookupElementsCollector(
private val onFlush: () -> Unit,
private val prefixMatcher: PrefixMatcher,
private val completionParameters: CompletionParameters,
resultSet: CompletionResultSet,
sorter: CompletionSorter,
private val filter: ((LookupElement) -> Boolean)?,
private val allowExpectDeclarations: Boolean
private val onFlush: () -> Unit,
private val prefixMatcher: PrefixMatcher,
private val completionParameters: CompletionParameters,
resultSet: CompletionResultSet,
sorter: CompletionSorter,
private val filter: ((LookupElement) -> Boolean)?,
private val allowExpectDeclarations: Boolean
) {
var bestMatchingDegree = Int.MIN_VALUE
@@ -47,9 +36,7 @@ class LookupElementsCollector(
private val elements = ArrayList<LookupElement>()
private val resultSet = resultSet
.withPrefixMatcher(prefixMatcher)
.withRelevanceSorter(sorter)
private val resultSet = resultSet.withPrefixMatcher(prefixMatcher).withRelevanceSorter(sorter)
private val postProcessors = ArrayList<(LookupElement) -> LookupElement>()
private val processedCallables = mutableSetOf<CallableDescriptor>()
@@ -1,17 +1,6 @@
/*
* 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.
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.completion
@@ -41,9 +30,7 @@ object NamedArgumentCompletion {
val callElement = thisArgument.getStrictParentOfType<KtCallElement>() ?: return false
return callElement.valueArguments
.takeWhile { it != thisArgument }
.any { it.isNamed() }
return callElement.valueArguments.takeWhile { it != thisArgument }.any { it.isNamed() }
}
fun complete(collector: LookupElementsCollector, expectedInfos: Collection<ExpectedInfo>, callType: CallType<*>) {
@@ -61,10 +48,10 @@ object NamedArgumentCompletion {
val typeText = types.singleOrNull()?.let { BasicLookupElementFactory.SHORT_NAMES_RENDERER.renderType(it) } ?: "..."
val nameString = name.asString()
val lookupElement = LookupElementBuilder.create("$nameString =")
.withPresentableText("$nameString =")
.withTailText(" $typeText")
.withIcon(KotlinIcons.PARAMETER)
.withInsertHandler(NamedArgumentInsertHandler(name))
.withPresentableText("$nameString =")
.withTailText(" $typeText")
.withIcon(KotlinIcons.PARAMETER)
.withInsertHandler(NamedArgumentInsertHandler(name))
lookupElement.putUserData(SmartCompletionInBasicWeigher.NAMED_ARGUMENT_KEY, Unit)
collector.addElement(lookupElement)
}
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2017 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.
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.completion
@@ -31,28 +20,26 @@ import org.jetbrains.kotlin.util.OperatorNameConventions.SET
object OperatorNameCompletion {
private val additionalOperatorPresentation = mapOf(
SET to "[...] = ...",
GET to "[...]",
CONTAINS to "in !in",
COMPARE_TO to "< > <= >=",
EQUALS to "== !=",
INVOKE to "(...)"
SET to "[...] = ...",
GET to "[...]",
CONTAINS to "in !in",
COMPARE_TO to "< > <= >=",
EQUALS to "== !=",
INVOKE to "(...)"
)
private fun buildLookupElement(opName: Name): LookupElement {
val element = LookupElementBuilder.create(opName)
val symbol =
(OperatorConventions.getOperationSymbolForName(opName) as? KtSingleValueToken)?.value ?:
additionalOperatorPresentation[opName]
(OperatorConventions.getOperationSymbolForName(opName) as? KtSingleValueToken)?.value ?: additionalOperatorPresentation[opName]
if (symbol != null) return element.withTypeText(symbol)
return element
}
fun doComplete(collector: LookupElementsCollector, descriptorNameFilter: (String) -> Boolean) {
collector.addElements(OperatorConventions.CONVENTION_NAMES
.filter { descriptorNameFilter(it.asString()) }
.map(this::buildLookupElement))
collector.addElements(OperatorConventions.CONVENTION_NAMES.filter { descriptorNameFilter(it.asString()) }
.map(this::buildLookupElement))
}
}
@@ -1,17 +1,6 @@
/*
* 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.
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.completion
@@ -53,7 +42,10 @@ object PackageDirectiveCompletion {
val packageMemberScope = resolutionFacade.moduleDescriptor.getPackage(file.packageFqName.parent()).memberScope
val variants = packageMemberScope.getContributedDescriptors(DescriptorKindFilter.PACKAGES, prefixMatcher.asNameFilter())
val lookupElementFactory = BasicLookupElementFactory(resolutionFacade.project, InsertHandlerProvider(callType = CallType.PACKAGE_DIRECTIVE, expectedInfosCalculator = { emptyList() }))
val lookupElementFactory = BasicLookupElementFactory(
resolutionFacade.project,
InsertHandlerProvider(callType = CallType.PACKAGE_DIRECTIVE, expectedInfosCalculator = { emptyList() })
)
for (variant in variants) {
val lookupElement = lookupElementFactory.createLookupElement(variant)
if (!lookupElement.lookupString.contains(DUMMY_IDENTIFIER)) {
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2016 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.
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.completion
@@ -43,22 +32,24 @@ private operator fun ReferenceVariants.plus(other: ReferenceVariants): Reference
}
class ReferenceVariantsCollector(
private val referenceVariantsHelper: ReferenceVariantsHelper,
private val indicesHelper: KotlinIndicesHelper,
private val prefixMatcher: PrefixMatcher,
private val nameExpression: KtSimpleNameExpression,
private val callTypeAndReceiver: CallTypeAndReceiver<*, *>,
private val resolutionFacade: ResolutionFacade,
private val bindingContext: BindingContext,
private val importableFqNameClassifier: ImportableFqNameClassifier,
private val configuration: CompletionSessionConfiguration,
private val runtimeReceiver: ExpressionReceiver? = null
private val referenceVariantsHelper: ReferenceVariantsHelper,
private val indicesHelper: KotlinIndicesHelper,
private val prefixMatcher: PrefixMatcher,
private val nameExpression: KtSimpleNameExpression,
private val callTypeAndReceiver: CallTypeAndReceiver<*, *>,
private val resolutionFacade: ResolutionFacade,
private val bindingContext: BindingContext,
private val importableFqNameClassifier: ImportableFqNameClassifier,
private val configuration: CompletionSessionConfiguration,
private val runtimeReceiver: ExpressionReceiver? = null
) {
private data class FilterConfiguration internal constructor(val descriptorKindFilter: DescriptorKindFilter,
val additionalPropertyNameFilter: ((String) -> Boolean)?,
val shadowedDeclarationsFilter: ShadowedDeclarationsFilter?,
val completeExtensionsFromIndices: Boolean)
private data class FilterConfiguration internal constructor(
val descriptorKindFilter: DescriptorKindFilter,
val additionalPropertyNameFilter: ((String) -> Boolean)?,
val shadowedDeclarationsFilter: ShadowedDeclarationsFilter?,
val completeExtensionsFromIndices: Boolean
)
private val prefix = prefixMatcher.prefix
private val descriptorNameFilter = prefixMatcher.asStringNameFilter()
@@ -111,8 +102,9 @@ class ReferenceVariantsCollector(
private fun configuration(descriptorKindFilter: DescriptorKindFilter): FilterConfiguration {
val completeExtensionsFromIndices = descriptorKindFilter.kindMask.and(DescriptorKindFilter.CALLABLES_MASK) != 0
&& DescriptorKindExclude.Extensions !in descriptorKindFilter.excludes
&& callTypeAndReceiver !is CallTypeAndReceiver.IMPORT_DIRECTIVE
&& DescriptorKindExclude.Extensions !in descriptorKindFilter.excludes
&& callTypeAndReceiver !is CallTypeAndReceiver.IMPORT_DIRECTIVE
@Suppress("NAME_SHADOWING")
val descriptorKindFilter = if (completeExtensionsFromIndices)
descriptorKindFilter exclude TopLevelExtensionsExclude // handled via indices
@@ -120,27 +112,34 @@ class ReferenceVariantsCollector(
descriptorKindFilter
val getOrSetPrefix = GET_SET_PREFIXES.firstOrNull { prefix.startsWith(it) }
val additionalPropertyNameFilter: ((String) -> Boolean)? = getOrSetPrefix
?.let { prefixMatcher.cloneWithPrefix(prefix.removePrefix(getOrSetPrefix).decapitalizeSmartForCompiler()).asStringNameFilter() }
val additionalPropertyNameFilter: ((String) -> Boolean)? = getOrSetPrefix?.let {
prefixMatcher.cloneWithPrefix(prefix.removePrefix(getOrSetPrefix).decapitalizeSmartForCompiler()).asStringNameFilter()
}
val shadowedDeclarationsFilter = if (runtimeReceiver != null)
ShadowedDeclarationsFilter(bindingContext, resolutionFacade, nameExpression, runtimeReceiver)
else
ShadowedDeclarationsFilter.create(bindingContext, resolutionFacade, nameExpression, callTypeAndReceiver)
return FilterConfiguration(descriptorKindFilter, additionalPropertyNameFilter, shadowedDeclarationsFilter, completeExtensionsFromIndices)
return FilterConfiguration(
descriptorKindFilter,
additionalPropertyNameFilter,
shadowedDeclarationsFilter,
completeExtensionsFromIndices
)
}
private fun doCollectBasicVariants(filterConfiguration: FilterConfiguration): ReferenceVariants {
fun getReferenceVariants(kindFilter: DescriptorKindFilter, nameFilter: (Name) -> Boolean): Collection<DeclarationDescriptor> {
return referenceVariantsHelper.getReferenceVariants(
nameExpression,
kindFilter,
nameFilter,
filterOutJavaGettersAndSetters = false,
filterOutShadowed = false,
excludeNonInitializedVariable = false,
useReceiverType = runtimeReceiver?.type)
nameExpression,
kindFilter,
nameFilter,
filterOutJavaGettersAndSetters = false,
filterOutShadowed = false,
excludeNonInitializedVariable = false,
useReceiverType = runtimeReceiver?.type
)
}
val basicNameFilter = descriptorNameFilter.toNameFilter()
@@ -150,7 +149,10 @@ class ReferenceVariantsCollector(
var basicVariants = getReferenceVariants(descriptorKindFilter, basicNameFilter)
if (additionalPropertyNameFilter != null) {
basicVariants += getReferenceVariants(descriptorKindFilter.intersect(DescriptorKindFilter.VARIABLES), additionalPropertyNameFilter.toNameFilter())
basicVariants += getReferenceVariants(
descriptorKindFilter.intersect(DescriptorKindFilter.VARIABLES),
additionalPropertyNameFilter.toNameFilter()
)
runDistinct = true
}
@@ -183,18 +185,22 @@ class ReferenceVariantsCollector(
else
indicesHelper.getCallableTopLevelExtensions(callTypeAndReceiver, nameExpression, bindingContext, nameFilter)
val (extensionsVariants, notImportedExtensions) = extensions.partition { importableFqNameClassifier.isImportableDescriptorImported(it) }
val (extensionsVariants, notImportedExtensions) = extensions.partition {
importableFqNameClassifier.isImportableDescriptorImported(
it
)
}
val notImportedDeclarationsFilter =
shadowedDeclarationsFilter?.createNonImportedDeclarationsFilter<CallableDescriptor>(importedDeclarations = basicVariants.imported + extensionsVariants)
shadowedDeclarationsFilter?.createNonImportedDeclarationsFilter<CallableDescriptor>(importedDeclarations = basicVariants.imported + extensionsVariants)
val filteredImported = filterConfiguration.filterVariants(extensionsVariants + basicVariants.imported)
val importedExtensionsVariants = filteredImported.filter { it !in basicVariants.imported }
return ReferenceVariants(
importedExtensionsVariants,
notImportedExtensions.let { variants -> notImportedDeclarationsFilter?.invoke(variants) ?: variants }
importedExtensionsVariants,
notImportedExtensions.let { variants -> notImportedDeclarationsFilter?.invoke(variants) ?: variants }
)
}
@@ -226,17 +232,15 @@ class ReferenceVariantsCollector(
if (descriptor.kind != CallableMemberDescriptor.Kind.DECLARATION) return false /* do not filter out synthetic extensions */
if (descriptor.isArtificialImportAliasedDescriptor) return false // do not exclude aliased descriptors - they cannot be completed via indices
val containingPackage = descriptor.containingDeclaration as? PackageFragmentDescriptor ?: return false
if (containingPackage.fqName.asString().startsWith("kotlinx.android.synthetic.")) return false // TODO: temporary solution for Android synthetic extensions
// TODO: temporary solution for Android synthetic extensions
if (containingPackage.fqName.asString().startsWith("kotlinx.android.synthetic.")) return false
return true
}
override val fullyExcludedDescriptorKinds: Int get() = 0
}
private fun isDataClassComponentFunction(descriptor: DeclarationDescriptor): Boolean {
return descriptor is FunctionDescriptor &&
descriptor.isOperator &&
DataClassDescriptorResolver.isComponentLike(descriptor.name) &&
descriptor.kind == CallableMemberDescriptor.Kind.SYNTHESIZED
}
private fun isDataClassComponentFunction(descriptor: DeclarationDescriptor): Boolean =
descriptor is FunctionDescriptor && descriptor.isOperator && DataClassDescriptorResolver.isComponentLike(descriptor.name) && descriptor.kind == CallableMemberDescriptor.Kind
.SYNTHESIZED
}
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2017 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.
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.completion
@@ -39,43 +28,42 @@ import org.jetbrains.kotlin.types.KotlinType
import java.util.*
class StaticMembersCompletion(
private val prefixMatcher: PrefixMatcher,
private val resolutionFacade: ResolutionFacade,
private val lookupElementFactory: LookupElementFactory,
alreadyAdded: Collection<DeclarationDescriptor>,
private val isJvmModule: Boolean
private val prefixMatcher: PrefixMatcher,
private val resolutionFacade: ResolutionFacade,
private val lookupElementFactory: LookupElementFactory,
alreadyAdded: Collection<DeclarationDescriptor>,
private val isJvmModule: Boolean
) {
private val alreadyAdded = alreadyAdded.mapTo(HashSet()) {
if (it is ImportedFromObjectCallableDescriptor<*>) it.callableFromObject else it
}
fun decoratedLookupElementFactory(itemPriority: ItemPriority): AbstractLookupElementFactory {
return object : AbstractLookupElementFactory {
override fun createStandardLookupElementsForDescriptor(descriptor: DeclarationDescriptor, useReceiverTypes: Boolean): Collection<LookupElement> {
if (!useReceiverTypes) return emptyList()
return lookupElementFactory.createLookupElement(descriptor, useReceiverTypes = false)
.decorateAsStaticMember(descriptor, classNameAsLookupString = false)
?.assignPriority(itemPriority)
?.suppressAutoInsertion()
.let(::listOfNotNull)
}
override fun createLookupElement(descriptor: DeclarationDescriptor, useReceiverTypes: Boolean,
qualifyNestedClasses: Boolean, includeClassTypeArguments: Boolean,
parametersAndTypeGrayed: Boolean) = null
fun decoratedLookupElementFactory(itemPriority: ItemPriority): AbstractLookupElementFactory = object : AbstractLookupElementFactory {
override fun createStandardLookupElementsForDescriptor(
descriptor: DeclarationDescriptor,
useReceiverTypes: Boolean
): Collection<LookupElement> {
if (!useReceiverTypes) return emptyList()
return lookupElementFactory.createLookupElement(descriptor, useReceiverTypes = false)
.decorateAsStaticMember(descriptor, classNameAsLookupString = false)
?.assignPriority(itemPriority)
?.suppressAutoInsertion()
.let(::listOfNotNull)
}
override fun createLookupElement(
descriptor: DeclarationDescriptor, useReceiverTypes: Boolean,
qualifyNestedClasses: Boolean, includeClassTypeArguments: Boolean,
parametersAndTypeGrayed: Boolean
) = null
}
fun membersFromImports(file: KtFile): Collection<DeclarationDescriptor> {
val containers = file.importDirectives
.filter { !it.isAllUnder }
.mapNotNull {
it.targetDescriptors(resolutionFacade)
.map { it.containingDeclaration }
.distinct()
.singleOrNull() as? ClassDescriptor
}
.toSet()
val containers = file.importDirectives.filter { !it.isAllUnder }.mapNotNull {
it.targetDescriptors(resolutionFacade).map { descriptor ->
descriptor.containingDeclaration
}.distinct().singleOrNull() as? ClassDescriptor
}.toSet()
val result = ArrayList<DeclarationDescriptor>()
@@ -84,8 +72,11 @@ class StaticMembersCompletion(
for (container in containers) {
val memberScope = if (container.kind == ClassKind.OBJECT) container.unsubstitutedMemberScope else container.staticScope
val members =
memberScope.getDescriptorsFiltered(kindFilter, nameFilter) +
memberScope.collectSyntheticStaticMembersAndConstructors(resolutionFacade, kindFilter, nameFilter)
memberScope.getDescriptorsFiltered(kindFilter, nameFilter) + memberScope.collectSyntheticStaticMembersAndConstructors(
resolutionFacade,
kindFilter,
nameFilter
)
members.filterTo(result) { it is CallableDescriptor && it !in alreadyAdded }
}
return result
@@ -109,7 +100,7 @@ class StaticMembersCompletion(
}
if (isJvmModule) {
indicesHelper.processJavaStaticMembers(descriptorKindFilter, nameFilter){
indicesHelper.processJavaStaticMembers(descriptorKindFilter, nameFilter) {
if (it !in alreadyAdded) {
processor(it)
}
@@ -132,9 +123,8 @@ class StaticMembersCompletion(
fun completeFromImports(file: KtFile, collector: LookupElementsCollector) {
val factory = decoratedLookupElementFactory(ItemPriority.STATIC_MEMBER_FROM_IMPORTS)
membersFromImports(file)
.flatMap { factory.createStandardLookupElementsForDescriptor(it, useReceiverTypes = true) }
.forEach { collector.addElement(it) }
membersFromImports(file).flatMap { factory.createStandardLookupElementsForDescriptor(it, useReceiverTypes = true) }
.forEach { collector.addElement(it) }
}
/**
@@ -1,17 +1,6 @@
/*
* 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.
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.completion
@@ -42,8 +31,7 @@ class KotlinCompletionStatistician : CompletionStatistician() {
if (o.descriptor != null) {
return KotlinStatisticsInfo.forDescriptor(o.descriptor!!.original, context)
}
else {
} else {
val fqName = o.importableFqName ?: return StatisticsInfo.EMPTY
return StatisticsInfo(context, fqName.asString())
}
@@ -74,13 +62,12 @@ object KotlinStatisticsInfo {
return descriptor.importableFqName?.let { StatisticsInfo("", it.asString()) } ?: StatisticsInfo.EMPTY
}
val container = descriptor.containingDeclaration
val containerFqName = when (container) {
is ClassDescriptor -> container.importableFqName?.asString()
is PackageFragmentDescriptor -> container.fqName.asString()
is ModuleDescriptor -> ""
else -> null
} ?: return StatisticsInfo.EMPTY
val containerFqName = when (val container = descriptor.containingDeclaration) {
is ClassDescriptor -> container.importableFqName?.asString()
is PackageFragmentDescriptor -> container.fqName.asString()
is ModuleDescriptor -> ""
else -> null
} ?: return StatisticsInfo.EMPTY
val signature = SIGNATURE_RENDERER.render(descriptor)
return StatisticsInfo(context, "$containerFqName###$signature")
}
@@ -1,17 +1,6 @@
/*
* 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.
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.completion
@@ -25,9 +14,9 @@ import org.jetbrains.kotlin.utils.checkWithAttachment
import kotlin.math.min
class ToFromOriginalFileMapper private constructor(
val originalFile: KtFile,
val syntheticFile: KtFile,
val completionOffset: Int
val originalFile: KtFile,
val syntheticFile: KtFile,
val completionOffset: Int
) {
companion object {
fun create(parameters: CompletionParameters): ToFromOriginalFileMapper {
@@ -60,7 +49,7 @@ class ToFromOriginalFileMapper private constructor(
syntheticLength = syntheticText.length
originalLength = originalText.length
val minLength = min(originalLength, syntheticLength)
tailLength = (0..minLength-1).firstOrNull {
tailLength = (0 until minLength).firstOrNull {
syntheticText[syntheticLength - it - 1] != originalText[originalLength - it - 1]
} ?: minLength
shift = syntheticLength - originalLength
@@ -1,17 +1,6 @@
/*
* 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.
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.completion
@@ -39,8 +28,7 @@ import org.jetbrains.kotlin.resolve.findOriginalTopMostOverriddenDescriptors
import org.jetbrains.kotlin.types.typeUtil.isNothing
object PriorityWeigher : LookupElementWeigher("kotlin.priority") {
override fun weigh(element: LookupElement, context: WeighingContext)
= element.getUserData(ITEM_PRIORITY_KEY) ?: ItemPriority.DEFAULT
override fun weigh(element: LookupElement, context: WeighingContext) = element.getUserData(ITEM_PRIORITY_KEY) ?: ItemPriority.DEFAULT
}
object PreferDslMembers : LookupElementWeigher("kotlin.preferDsl") {
@@ -71,7 +59,8 @@ class NotImportedWeigher(private val classifier: ImportableFqNameClassifier) : L
}
}
class NotImportedStaticMemberWeigher(private val classifier: ImportableFqNameClassifier) : LookupElementWeigher("kotlin.notImportedMember") {
class NotImportedStaticMemberWeigher(private val classifier: ImportableFqNameClassifier) :
LookupElementWeigher("kotlin.notImportedMember") {
override fun weigh(element: LookupElement): Comparable<*>? {
if (element.getUserData(ITEM_PRIORITY_KEY) != ItemPriority.STATIC_MEMBER) return null
val fqName = (element.`object` as DeclarationLookupObject).importableFqName ?: return null
@@ -109,8 +98,8 @@ object KotlinLookupElementProximityWeigher : CompletionWeigher() {
}
object SmartCompletionPriorityWeigher : LookupElementWeigher("kotlin.smartCompletionPriority") {
override fun weigh(element: LookupElement, context: WeighingContext)
= element.getUserData(SMART_COMPLETION_ITEM_PRIORITY_KEY) ?: SmartCompletionItemPriority.DEFAULT
override fun weigh(element: LookupElement, context: WeighingContext) =
element.getUserData(SMART_COMPLETION_ITEM_PRIORITY_KEY) ?: SmartCompletionItemPriority.DEFAULT
}
object KindWeigher : LookupElementWeigher("kotlin.kind") {
@@ -197,7 +186,7 @@ object CallableWeigher : LookupElementWeigher("kotlin.callableWeight") {
}
}
object VariableOrFunctionWeigher : LookupElementWeigher("kotlin.variableOrFunction"){
object VariableOrFunctionWeigher : LookupElementWeigher("kotlin.variableOrFunction") {
private enum class Weight {
variable,
function
@@ -216,7 +205,7 @@ object VariableOrFunctionWeigher : LookupElementWeigher("kotlin.variableOrFuncti
/**
* Decreases priority of properties when prefix starts with "get" or "set" (and the property name does not)
*/
object PreferGetSetMethodsToPropertyWeigher : LookupElementWeigher("kotlin.preferGetSetMethodsToProperty", false, true){
object PreferGetSetMethodsToPropertyWeigher : LookupElementWeigher("kotlin.preferGetSetMethodsToProperty", false, true) {
override fun weigh(element: LookupElement, context: WeighingContext): Int {
val property = (element.`object` as? DeclarationLookupObject)?.descriptor as? PropertyDescriptor ?: return 0
val prefixMatcher = context.itemMatcher(element)
@@ -247,8 +236,7 @@ object PreferMatchingItemWeigher : LookupElementWeigher("kotlin.preferMatching",
val prefix = context.itemPattern(element)
if (element.lookupString != prefix) {
return Weight.notExactMatch
}
else {
} else {
val o = element.`object`
return when (o) {
is KeywordLookupObject -> Weight.keywordExactMatch
@@ -270,10 +258,10 @@ object PreferMatchingItemWeigher : LookupElementWeigher("kotlin.preferMatching",
}
class SmartCompletionInBasicWeigher(
private val smartCompletion: SmartCompletion,
private val callTypeAndReceiver: CallTypeAndReceiver<*, *>,
private val resolutionFacade: ResolutionFacade,
private val bindingContext: BindingContext
private val smartCompletion: SmartCompletion,
private val callTypeAndReceiver: CallTypeAndReceiver<*, *>,
private val resolutionFacade: ResolutionFacade,
private val bindingContext: BindingContext
) : LookupElementWeigher("kotlin.smartInBasic", true, false) {
companion object {
@@ -286,8 +274,8 @@ class SmartCompletionInBasicWeigher(
private val PRIORITY_COUNT = SmartCompletionItemPriority.values().size
private fun itemWeight(priority: SmartCompletionItemPriority, nameSimilarity: Int)
= (nameSimilarity.toLong() shl 32) + PRIORITY_COUNT - priority.ordinal
private fun itemWeight(priority: SmartCompletionItemPriority, nameSimilarity: Int) =
(nameSimilarity.toLong() shl 32) + PRIORITY_COUNT - priority.ordinal
private val NAMED_ARGUMENT_WEIGHT = 1L
@@ -316,7 +304,12 @@ class SmartCompletionInBasicWeigher(
val (fuzzyTypes, name) = when (o) {
is DeclarationLookupObject -> {
val descriptor = o.descriptor ?: return NO_MATCH_WEIGHT
descriptor.fuzzyTypesForSmartCompletion(smartCastCalculator, callTypeAndReceiver, resolutionFacade, bindingContext) to descriptor.name
descriptor.fuzzyTypesForSmartCompletion(
smartCastCalculator,
callTypeAndReceiver,
resolutionFacade,
bindingContext
) to descriptor.name
}
is ThisItemLookupObject -> smartCastCalculator.types(o.receiverParameter).map { it.toFuzzyType(emptyList()) } to null
@@ -332,8 +325,7 @@ class SmartCompletionInBasicWeigher(
val nameSimilarity = if (name != null) {
val matchingInfos = matched.filter { it.second != ExpectedInfoMatch.noMatch }.map { it.first }
calcNameSimilarity(name.asString(), matchingInfos)
}
else {
} else {
0
}
@@ -346,10 +338,10 @@ class SmartCompletionInBasicWeigher(
class PreferContextElementsWeigher(context: DeclarationDescriptor) : LookupElementWeigher("kotlin.preferContextElements", true, false) {
private val contextElements = context.parentsWithSelf
.takeWhile { it !is PackageFragmentDescriptor }
.toList()
.flatMap { if (it is CallableDescriptor) it.findOriginalTopMostOverriddenDescriptors() else listOf(it) }
.toSet()
.takeWhile { it !is PackageFragmentDescriptor }
.toList()
.flatMap { if (it is CallableDescriptor) it.findOriginalTopMostOverriddenDescriptors() else listOf(it) }
.toSet()
private val contextElementNames = contextElements.map { it.name }.toSet()
override fun weigh(element: LookupElement): Boolean {
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2017 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.
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.completion.confidence
@@ -29,7 +18,8 @@ import org.jetbrains.kotlin.psi.psiUtil.startOffset
class EnableAutopopupInStringTemplate : CompletionConfidence() {
override fun shouldSkipAutopopup(contextElement: PsiElement, psiFile: PsiFile, offset: Int): ThreeState {
val stringTemplate = contextElement.prevLeaf()?.getParentOfType<KtSimpleNameStringTemplateEntry>(strict = false) ?: return ThreeState.UNSURE
val stringTemplate =
contextElement.prevLeaf()?.getParentOfType<KtSimpleNameStringTemplateEntry>(strict = false) ?: return ThreeState.UNSURE
// "$<caret>nameRef" stringTemplate here is "$nameRef", so offset are inside template, we should show lookup
if (offset in stringTemplate.startOffset until stringTemplate.endOffset) return ThreeState.NO
@@ -45,7 +45,8 @@ object KotlinClassifierInsertHandler : BaseDeclarationInsertHandler() {
val document = context.document
val lookupObject = item.`object` as DeclarationLookupObject
if (lookupObject.descriptor?.isArtificialImportAliasedDescriptor == true) return // never need to insert import or use qualified name for import-aliased class
// never need to insert import or use qualified name for import-aliased class
if (lookupObject.descriptor?.isArtificialImportAliasedDescriptor == true) return
val qualifiedName = qualifiedName(lookupObject)
@@ -117,7 +117,8 @@ sealed class KotlinFunctionInsertHandler(callType: CallType<*>) : KotlinCallable
val token = context.file.findElementAt(offset1)!!
if (token.node.elementType == KtTokens.LT) {
val parent = token.parent
if (parent is KtTypeArgumentList && parent.getText().indexOf('\n') < 0/* if type argument list is on multiple lines this is more likely wrong parsing*/) {
/* if type argument list is on multiple lines this is more likely wrong parsing*/
if (parent is KtTypeArgumentList && parent.getText().indexOf('\n') < 0) {
offset = parent.endOffset
insertTypeArguments = false
}
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2017 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.
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.completion.smart
@@ -30,18 +19,22 @@ import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
object ArrayLiteralsInAnnotationItems {
private fun MutableCollection<LookupElement>.addForUsage(expectedInfos: Collection<ExpectedInfo>,
position: PsiElement) {
private fun MutableCollection<LookupElement>.addForUsage(
expectedInfos: Collection<ExpectedInfo>,
position: PsiElement
) {
if (position.getParentOfType<KtAnnotationEntry>(false) != null) {
expectedInfos.asSequence()
.filter { it.fuzzyType?.type?.let { type -> KotlinBuiltIns.isArray(type) } == true }
.filterNot { it.itemOptions.starPrefix }
.mapTo(this) { createLookupElement() }
.filter { it.fuzzyType?.type?.let { type -> KotlinBuiltIns.isArray(type) } == true }
.filterNot { it.itemOptions.starPrefix }
.mapTo(this) { createLookupElement() }
}
}
private fun MutableCollection<LookupElement>.addForDefaultArguments(expectedInfos: Collection<ExpectedInfo>,
position: PsiElement) {
private fun MutableCollection<LookupElement>.addForDefaultArguments(
expectedInfos: Collection<ExpectedInfo>,
position: PsiElement
) {
// CLASS [MODIFIER_LIST, PRIMARY_CONSTRUCTOR [VALUE_PARAMETER_LIST [VALUE_PARAMETER [..., REFERENCE_EXPRESSION=position]]]]
val valueParameter = position.parent as? KtParameter ?: return
@@ -50,24 +43,20 @@ object ArrayLiteralsInAnnotationItems {
val primaryConstructor = klass.primaryConstructor ?: return
if (primaryConstructor.valueParameterList == valueParameter.parent) {
expectedInfos
.filter { it.fuzzyType?.type?.let { type -> KotlinBuiltIns.isArray(type) } == true }
.mapTo(this) { createLookupElement() }
expectedInfos.filter { it.fuzzyType?.type?.let { type -> KotlinBuiltIns.isArray(type) } == true }
.mapTo(this) { createLookupElement() }
}
}
private fun createLookupElement(): LookupElement {
return LookupElementBuilder.create("[]")
.withInsertHandler { context, _ ->
context.editor.caretModel.moveToOffset(context.tailOffset - 1)
}
.apply { putUserData(SMART_COMPLETION_ITEM_PRIORITY_KEY, SmartCompletionItemPriority.ARRAY_LITERAL_IN_ANNOTATION) }
}
private fun createLookupElement(): LookupElement = LookupElementBuilder.create("[]")
.withInsertHandler { context, _ ->
context.editor.caretModel.moveToOffset(context.tailOffset - 1)
}
.apply { putUserData(SMART_COMPLETION_ITEM_PRIORITY_KEY, SmartCompletionItemPriority.ARRAY_LITERAL_IN_ANNOTATION) }
fun collect(expectedInfos: Collection<ExpectedInfo>, position: PsiElement): Collection<LookupElement> {
return mutableListOf<LookupElement>().apply {
fun collect(expectedInfos: Collection<ExpectedInfo>, position: PsiElement): Collection<LookupElement> =
mutableListOf<LookupElement>().apply {
addForUsage(expectedInfos, position)
addForDefaultArguments(expectedInfos, position)
}
}
}
@@ -1,17 +1,6 @@
/*
* 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.
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.completion.smart
@@ -27,8 +16,8 @@ import org.jetbrains.kotlin.idea.completion.BasicLookupElementFactory
import org.jetbrains.kotlin.idea.completion.createLookupElementForType
import org.jetbrains.kotlin.idea.core.ExpectedInfo
import org.jetbrains.kotlin.idea.core.fuzzyType
import org.jetbrains.kotlin.idea.imports.importableFqName
import org.jetbrains.kotlin.idea.core.moveCaret
import org.jetbrains.kotlin.idea.imports.importableFqName
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
@@ -36,10 +25,10 @@ import java.util.*
object ClassLiteralItems {
fun addToCollection(
collection: MutableCollection<LookupElement>,
expectedInfos: Collection<ExpectedInfo>,
lookupElementFactory: BasicLookupElementFactory,
isJvmModule: Boolean
collection: MutableCollection<LookupElement>,
expectedInfos: Collection<ExpectedInfo>,
lookupElementFactory: BasicLookupElementFactory,
isJvmModule: Boolean
) {
val typeAndSuffixToExpectedInfos = LinkedHashMap<Pair<KotlinType, String>, MutableList<ExpectedInfo>>()
@@ -64,8 +53,7 @@ object ClassLiteralItems {
val (type, suffix) = pair
val typeToUse = if (KotlinBuiltIns.isArray(type)) {
type.makeNotNullable()
}
else {
} else {
val classifier = (type.constructor.declarationDescriptor as? ClassDescriptor) ?: continue
classifier.defaultType
}
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2016 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.
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.completion.smart
@@ -39,46 +28,49 @@ object LambdaItems {
val functionExpectedInfos = expectedInfos.filter { it.fuzzyType?.type?.isFunctionOrSuspendFunctionType == true }
if (functionExpectedInfos.isEmpty()) return
val functionTypes = functionExpectedInfos
.mapNotNull { it.fuzzyType?.type }
.toSet()
val functionTypes = functionExpectedInfos.mapNotNull { it.fuzzyType?.type }.toSet()
val singleType = if (functionTypes.size == 1) functionTypes.single() else null
val singleSignatureLength = singleType?.let(::getValueParametersCountFromFunctionType)
val offerNoParametersLambda = singleSignatureLength == 0 || singleSignatureLength == 1
if (offerNoParametersLambda) {
val lookupElement = LookupElementBuilder.create(LambdaSignatureTemplates.DEFAULT_LAMBDA_PRESENTATION)
.withInsertHandler(ArtificialElementInsertHandler("{ ", " }", false))
.suppressAutoInsertion()
.assignSmartCompletionPriority(SmartCompletionItemPriority.LAMBDA_NO_PARAMS)
.addTailAndNameSimilarity(functionExpectedInfos)
.withInsertHandler(ArtificialElementInsertHandler("{ ", " }", false))
.suppressAutoInsertion()
.assignSmartCompletionPriority(SmartCompletionItemPriority.LAMBDA_NO_PARAMS)
.addTailAndNameSimilarity(functionExpectedInfos)
collection.add(lookupElement)
}
if (singleSignatureLength != 0) {
for (functionType in functionTypes) {
if (LambdaSignatureTemplates.explicitParameterTypesRequired(functionTypes, functionType)) {
collection.add(createLookupElement(
collection.add(
createLookupElement(
functionType,
functionExpectedInfos,
LambdaSignatureTemplates.SignaturePresentation.NAMES_OR_TYPES,
explicitParameterTypes = true
))
)
)
}
else {
collection.add(createLookupElement(
} else {
collection.add(
createLookupElement(
functionType,
functionExpectedInfos,
LambdaSignatureTemplates.SignaturePresentation.NAMES_AND_TYPES,
explicitParameterTypes = true
))
collection.add(createLookupElement(
)
)
collection.add(
createLookupElement(
functionType,
functionExpectedInfos,
LambdaSignatureTemplates.SignaturePresentation.NAMES,
explicitParameterTypes = false
))
)
)
}
}
}
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2017 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.
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.completion.smart
@@ -35,10 +24,10 @@ import org.jetbrains.kotlin.types.KotlinType
object LambdaSignatureItems {
fun addToCollection(
collection: MutableCollection<LookupElement>,
position: KtExpression,
bindingContext: BindingContext,
resolutionFacade: ResolutionFacade
collection: MutableCollection<LookupElement>,
position: KtExpression,
bindingContext: BindingContext,
resolutionFacade: ResolutionFacade
) {
val block = position.parent as? KtBlockExpression ?: return
if (position != block.statements.first()) return
@@ -47,18 +36,35 @@ object LambdaSignatureItems {
val literalExpression = functionLiteral.parent as KtLambdaExpression
val expectedFunctionTypes = ExpectedInfos(bindingContext, resolutionFacade, null).calculate(literalExpression)
.mapNotNull { it.fuzzyType?.type }
.filter { it.isFunctionOrSuspendFunctionType }
.toSet()
.mapNotNull { it.fuzzyType?.type }
.filter { it.isFunctionOrSuspendFunctionType }
.toSet()
for (functionType in expectedFunctionTypes) {
if (functionType.getValueParameterTypesFromFunctionType().isEmpty()) continue
if (LambdaSignatureTemplates.explicitParameterTypesRequired(expectedFunctionTypes, functionType)) {
collection.add(createLookupElement(functionType, LambdaSignatureTemplates.SignaturePresentation.NAMES_OR_TYPES, explicitParameterTypes = true))
}
else {
collection.add(createLookupElement(functionType, LambdaSignatureTemplates.SignaturePresentation.NAMES, explicitParameterTypes = false))
collection.add(createLookupElement(functionType, LambdaSignatureTemplates.SignaturePresentation.NAMES_AND_TYPES, explicitParameterTypes = true))
collection.add(
createLookupElement(
functionType,
LambdaSignatureTemplates.SignaturePresentation.NAMES_OR_TYPES,
explicitParameterTypes = true
)
)
} else {
collection.add(
createLookupElement(
functionType,
LambdaSignatureTemplates.SignaturePresentation.NAMES,
explicitParameterTypes = false
)
)
collection.add(
createLookupElement(
functionType,
LambdaSignatureTemplates.SignaturePresentation.NAMES_AND_TYPES,
explicitParameterTypes = true
)
)
}
}
}
@@ -1,17 +1,6 @@
/*
* 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.
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.completion.smart
@@ -45,14 +34,16 @@ import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
import java.util.*
class MultipleArgumentsItemProvider(
private val bindingContext: BindingContext,
private val smartCastCalculator: SmartCastCalculator,
private val resolutionFacade: ResolutionFacade
private val bindingContext: BindingContext,
private val smartCastCalculator: SmartCastCalculator,
private val resolutionFacade: ResolutionFacade
) {
fun addToCollection(collection: MutableCollection<LookupElement>,
expectedInfos: Collection<ExpectedInfo>,
context: KtExpression) {
fun addToCollection(
collection: MutableCollection<LookupElement>,
expectedInfos: Collection<ExpectedInfo>,
context: KtExpression
) {
val resolutionScope = context.getResolutionScope(bindingContext, resolutionFacade)
val added = HashSet<String>()
@@ -69,7 +60,8 @@ class MultipleArgumentsItemProvider(
for ((i, parameter) in parameters.withIndex()) {
variables.add(variableInScope(parameter, resolutionScope) ?: break)
if (i > 0 && parameters.asSequence().drop(i + 1).all { it.hasDefaultValue() }) { // this is the last parameter or all others have default values
// this is the last parameter or all others have default values
if (i > 0 && parameters.asSequence().drop(i + 1).all { it.hasDefaultValue() }) {
val lookupElement = createParametersLookupElement(variables, tail)
if (added.add(lookupElement.lookupString)) { // check that we don't already have item with the same text
collection.add(lookupElement)
@@ -88,27 +80,26 @@ class MultipleArgumentsItemProvider(
compoundIcon.setIcon(lastIcon, 0, 2 * firstIcon.iconWidth / 5, 0)
compoundIcon.setIcon(firstIcon, 1, 0, 0)
return LookupElementBuilder
.create(variables.joinToString(", ") { it.name.render() }) //TODO: use code formatting settings
.withInsertHandler { context, _ ->
if (context.completionChar == Lookup.REPLACE_SELECT_CHAR) {
val offset = context.offsetMap.tryGetOffset(SmartCompletion.MULTIPLE_ARGUMENTS_REPLACEMENT_OFFSET)
if (offset != null) {
context.document.deleteString(context.tailOffset, offset)
}
return LookupElementBuilder.create(variables.joinToString(", ") { it.name.render() }) //TODO: use code formatting settings
.withInsertHandler { context, _ ->
if (context.completionChar == Lookup.REPLACE_SELECT_CHAR) {
val offset = context.offsetMap.tryGetOffset(SmartCompletion.MULTIPLE_ARGUMENTS_REPLACEMENT_OFFSET)
if (offset != null) {
context.document.deleteString(context.tailOffset, offset)
}
}
.withIcon(compoundIcon)
.addTail(tail)
.assignSmartCompletionPriority(SmartCompletionItemPriority.MULTIPLE_ARGUMENTS_ITEM)
}
.withIcon(compoundIcon)
.addTail(tail)
.assignSmartCompletionPriority(SmartCompletionItemPriority.MULTIPLE_ARGUMENTS_ITEM)
}
private fun variableInScope(parameter: ValueParameterDescriptor, scope: LexicalScope): VariableDescriptor? {
val name = parameter.name
//TODO: there can be more than one property with such name in scope and we should be able to select one (but we need API for this)
val variable = scope.findVariable(name, NoLookupLocation.FROM_IDE) { !it.isExtension }
?: scope.getVariableFromImplicitReceivers(name) ?: return null
?: scope.getVariableFromImplicitReceivers(name) ?: return null
return if (smartCastCalculator.types(variable).any { KotlinTypeChecker.DEFAULT.isSubtypeOf(it, parameter.type) })
variable
else
@@ -1,17 +1,6 @@
/*
* 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.
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.completion.smart
@@ -27,16 +16,11 @@ import kotlin.math.min
val NAME_SIMILARITY_KEY = Key<Int>("NAME_SIMILARITY_KEY")
object NameSimilarityWeigher : LookupElementWeigher("kotlin.nameSimilarity") {
override fun weigh(element: LookupElement, context: WeighingContext)
= -(element.getUserData(NAME_SIMILARITY_KEY) ?: 0)
override fun weigh(element: LookupElement, context: WeighingContext) = -(element.getUserData(NAME_SIMILARITY_KEY) ?: 0)
}
fun calcNameSimilarity(name: String, expectedInfos: Collection<ExpectedInfo>): Int {
return expectedInfos
.mapNotNull { it.expectedName }
.map { calcNameSimilarity(name, it) }
.max() ?: 0
}
fun calcNameSimilarity(name: String, expectedInfos: Collection<ExpectedInfo>): Int =
expectedInfos.mapNotNull { it.expectedName }.map { calcNameSimilarity(name, it) }.max() ?: 0
private fun calcNameSimilarity(name: String, expectedName: String): Int {
val words1 = NameUtil.nameToWordsLowerCase(name)
@@ -51,8 +35,8 @@ private fun calcNameSimilarity(name: String, expectedName: String): Int {
// count number of words matched at the end (but ignore number words - they are less important)
val minWords = min(nonNumberWords1.size, nonNumberWords2.size)
val matchedTailLength = (0..minWords-1).firstOrNull {
i -> nonNumberWords1[nonNumberWords1.size - i - 1] != nonNumberWords2[nonNumberWords2.size - i - 1]
val matchedTailLength = (0 until minWords).firstOrNull { i ->
nonNumberWords1[nonNumberWords1.size - i - 1] != nonNumberWords2[nonNumberWords2.size - i - 1]
} ?: minWords
return matchedWords.size * 1000 + matchedTailLength
@@ -1,17 +1,6 @@
/*
* 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.
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.completion.smart
@@ -54,18 +43,18 @@ interface InheritanceItemsSearcher {
}
class SmartCompletion(
private val expression: KtExpression,
private val resolutionFacade: ResolutionFacade,
private val bindingContext: BindingContext,
private val moduleDescriptor: ModuleDescriptor,
private val visibilityFilter: (DeclarationDescriptor) -> Boolean,
private val indicesHelper: KotlinIndicesHelper,
private val prefixMatcher: PrefixMatcher,
private val inheritorSearchScope: GlobalSearchScope,
private val toFromOriginalFileMapper: ToFromOriginalFileMapper,
private val callTypeAndReceiver: CallTypeAndReceiver<*, *>,
private val isJvmModule: Boolean,
private val forBasicCompletion: Boolean = false
private val expression: KtExpression,
private val resolutionFacade: ResolutionFacade,
private val bindingContext: BindingContext,
private val moduleDescriptor: ModuleDescriptor,
private val visibilityFilter: (DeclarationDescriptor) -> Boolean,
private val indicesHelper: KotlinIndicesHelper,
private val prefixMatcher: PrefixMatcher,
private val inheritorSearchScope: GlobalSearchScope,
private val toFromOriginalFileMapper: ToFromOriginalFileMapper,
private val callTypeAndReceiver: CallTypeAndReceiver<*, *>,
private val isJvmModule: Boolean,
private val forBasicCompletion: Boolean = false
) {
private val expressionWithType = when (callTypeAndReceiver) {
is CallTypeAndReceiver.DEFAULT ->
@@ -87,13 +76,19 @@ class SmartCompletion(
private val callableTypeExpectedInfo = expectedInfos.filterCallableExpected()
val smartCastCalculator: SmartCastCalculator by lazy(LazyThreadSafetyMode.NONE) {
SmartCastCalculator(bindingContext, resolutionFacade.moduleDescriptor, expression, callTypeAndReceiver.receiver as? KtExpression, resolutionFacade)
SmartCastCalculator(
bindingContext,
resolutionFacade.moduleDescriptor,
expression,
callTypeAndReceiver.receiver as? KtExpression,
resolutionFacade
)
}
val descriptorFilter: ((DeclarationDescriptor, AbstractLookupElementFactory) -> Collection<LookupElement>)? =
{ descriptor: DeclarationDescriptor, factory: AbstractLookupElementFactory ->
filterDescriptor(descriptor, factory).map { postProcess(it) }
}.takeIf { expectedInfos.isNotEmpty() }
{ descriptor: DeclarationDescriptor, factory: AbstractLookupElementFactory ->
filterDescriptor(descriptor, factory).map { postProcess(it) }
}.takeIf { expectedInfos.isNotEmpty() }
fun additionalItems(lookupElementFactory: LookupElementFactory): Pair<Collection<LookupElement>, InheritanceItemsSearcher?> {
val (items, inheritanceSearcher) = additionalItemsNoPostProcess(lookupElementFactory)
@@ -142,12 +137,11 @@ class SmartCompletion(
val subjectType = bindingContext.getType(subject) ?: return@lazy emptySet()
val classDescriptor = TypeUtils.getClassDescriptor(subjectType)
if (classDescriptor != null && DescriptorUtils.isEnumClass(classDescriptor)) {
val conditions = whenExpression.entries
.flatMap { it.conditions.toList() }
.filterIsInstance<KtWhenConditionWithExpression>()
val conditions =
whenExpression.entries.flatMap { it.conditions.toList() }.filterIsInstance<KtWhenConditionWithExpression>()
for (condition in conditions) {
val selectorExpr = (condition.expression as? KtDotQualifiedExpression)
?.selectorExpression as? KtReferenceExpression ?: continue
val selectorExpr =
(condition.expression as? KtDotQualifiedExpression)?.selectorExpression as? KtReferenceExpression ?: continue
val target = bindingContext[BindingContext.REFERENCE_TARGET, selectorExpr] as? ClassDescriptor ?: continue
if (DescriptorUtils.isEnumEntry(target)) {
descriptorsToSkip.add(target)
@@ -161,7 +155,10 @@ class SmartCompletion(
return@lazy emptySet()
}
private fun filterDescriptor(descriptor: DeclarationDescriptor, lookupElementFactory: AbstractLookupElementFactory): Collection<LookupElement> {
private fun filterDescriptor(
descriptor: DeclarationDescriptor,
lookupElementFactory: AbstractLookupElementFactory
): Collection<LookupElement> {
ProgressManager.checkCanceled()
if (descriptor in descriptorsToSkip) return emptyList()
@@ -169,7 +166,12 @@ class SmartCompletion(
val types = descriptor.fuzzyTypesForSmartCompletion(smartCastCalculator, callTypeAndReceiver, resolutionFacade, bindingContext)
val infoMatcher = { expectedInfo: ExpectedInfo -> types.matchExpectedInfo(expectedInfo) }
result.addLookupElements(descriptor, expectedInfos, infoMatcher, noNameSimilarityForReturnItself = callTypeAndReceiver is CallTypeAndReceiver.DEFAULT) { declarationDescriptor ->
result.addLookupElements(
descriptor,
expectedInfos,
infoMatcher,
noNameSimilarityForReturnItself = callTypeAndReceiver is CallTypeAndReceiver.DEFAULT
) { declarationDescriptor ->
lookupElementFactory.createStandardLookupElementsForDescriptor(declarationDescriptor, useReceiverTypes = true)
}
@@ -192,7 +194,12 @@ class SmartCompletion(
if (!forBasicCompletion) { // basic completion adds keyword values on its own
val keywordValueConsumer = object : KeywordValues.Consumer {
override fun consume(lookupString: String, expectedInfoMatcher: (ExpectedInfo) -> ExpectedInfoMatch, priority: SmartCompletionItemPriority, factory: () -> LookupElement) {
override fun consume(
lookupString: String,
expectedInfoMatcher: (ExpectedInfo) -> ExpectedInfoMatch,
priority: SmartCompletionItemPriority,
factory: () -> LookupElement
) {
items.addLookupElements(null, expectedInfos, expectedInfoMatcher) {
val lookupElement = factory()
lookupElement.putUserData(SMART_COMPLETION_ITEM_PRIORITY_KEY, priority)
@@ -200,7 +207,14 @@ class SmartCompletion(
}
}
}
KeywordValues.process(keywordValueConsumer, callTypeAndReceiver, bindingContext, resolutionFacade, moduleDescriptor, isJvmModule)
KeywordValues.process(
keywordValueConsumer,
callTypeAndReceiver,
bindingContext,
resolutionFacade,
moduleDescriptor,
isJvmModule
)
}
if (expectedInfos.isNotEmpty()) {
@@ -211,12 +225,24 @@ class SmartCompletion(
}
if (callTypeAndReceiver is CallTypeAndReceiver.DEFAULT) {
TypeInstantiationItems(resolutionFacade, bindingContext, visibilityFilter, toFromOriginalFileMapper, inheritorSearchScope, lookupElementFactory, forBasicCompletion, indicesHelper)
.addTo(items, inheritanceSearchers, expectedInfos)
TypeInstantiationItems(
resolutionFacade,
bindingContext,
visibilityFilter,
toFromOriginalFileMapper,
inheritorSearchScope,
lookupElementFactory,
forBasicCompletion,
indicesHelper
).addTo(items, inheritanceSearchers, expectedInfos)
if (expression is KtSimpleNameExpression) {
StaticMembers(bindingContext, lookupElementFactory, resolutionFacade, moduleDescriptor)
.addToCollection(items, expectedInfos, expression, descriptorsToSkip)
StaticMembers(bindingContext, lookupElementFactory, resolutionFacade, moduleDescriptor).addToCollection(
items,
expectedInfos,
expression,
descriptorsToSkip
)
}
ClassLiteralItems.addToCollection(items, expectedInfos, lookupElementFactory.basicFactory, isJvmModule)
@@ -235,7 +261,7 @@ class SmartCompletion(
val entries = whenExpression.entries
if (whenExpression.elseExpression == null && entry == entries.last() && entries.size != 1) {
val lookupElement = LookupElementBuilder.create("else").bold().withTailText(" ->")
items.add(object: LookupElementDecorator<LookupElement>(lookupElement) {
items.add(object : LookupElementDecorator<LookupElement>(lookupElement) {
override fun handleInsert(context: InsertionContext) {
WithTailInsertHandler("->", spaceBefore = true, spaceAfter = true).handleInsert(context, delegate)
}
@@ -244,8 +270,11 @@ class SmartCompletion(
}
}
MultipleArgumentsItemProvider(bindingContext, smartCastCalculator, resolutionFacade)
.addToCollection(items, expectedInfos, expression)
MultipleArgumentsItemProvider(bindingContext, smartCastCalculator, resolutionFacade).addToCollection(
items,
expectedInfos,
expression
)
}
}
@@ -277,13 +306,16 @@ class SmartCompletion(
super.handleInsert(context)
}
}
}
else {
} else {
item
}
}
private fun MutableCollection<LookupElement>.addThisItems(place: KtExpression, expectedInfos: Collection<ExpectedInfo>, smartCastCalculator: SmartCastCalculator) {
private fun MutableCollection<LookupElement>.addThisItems(
place: KtExpression,
expectedInfos: Collection<ExpectedInfo>,
smartCastCalculator: SmartCastCalculator
) {
if (shouldCompleteThisItems(prefixMatcher)) {
val items = thisExpressionItems(bindingContext, place, prefixMatcher.prefix, resolutionFacade)
for (item in items) {
@@ -330,8 +362,10 @@ class SmartCompletion(
private fun createNamedArgumentWithValueLookupElement(name: Name, value: String, priority: SmartCompletionItemPriority): LookupElement {
val lookupElement = LookupElementBuilder.create("${name.asString()} = $value")
.withIcon(KotlinIcons.PARAMETER)
.withInsertHandler({ context, _ -> context.document.replaceString(context.startOffset, context.tailOffset, "${name.render()} = $value") })
.withIcon(KotlinIcons.PARAMETER)
.withInsertHandler { context, _ ->
context.document.replaceString(context.startOffset, context.tailOffset, "${name.render()} = $value")
}
lookupElement.putUserData(SmartCompletionInBasicWeigher.NAMED_ARGUMENT_KEY, Unit)
lookupElement.assignSmartCompletionPriority(priority)
return lookupElement
@@ -354,8 +388,8 @@ class SmartCompletion(
// if expected types are too general, try to use expected type from outer calls
var count = 0
while (true) {
val infos = ExpectedInfos(bindingContext, resolutionFacade, indicesHelper, useOuterCallsExpectedTypeCount = count)
.calculate(expression)
val infos =
ExpectedInfos(bindingContext, resolutionFacade, indicesHelper, useOuterCallsExpectedTypeCount = count).calculate(expression)
if (count == 2 /* use two outer calls maximum */ || infos.none { it.fuzzyType?.isAlmostEverything() ?: false }) {
return if (forBasicCompletion)
infos.map { it.copy(tail = null) }
@@ -376,7 +410,10 @@ class SmartCompletion(
return null
}
private fun MutableCollection<LookupElement>.addCallableReferenceLookupElements(descriptor: DeclarationDescriptor, lookupElementFactory: AbstractLookupElementFactory) {
private fun MutableCollection<LookupElement>.addCallableReferenceLookupElements(
descriptor: DeclarationDescriptor,
lookupElementFactory: AbstractLookupElementFactory
) {
if (callableTypeExpectedInfo.isEmpty()) return
fun toLookupElement(descriptor: CallableDescriptor): LookupElement? {
@@ -385,9 +422,10 @@ class SmartCompletion(
val matchedExpectedInfos = callableTypeExpectedInfo.filter { it.matchingSubstitutor(callableReferenceType) != null }
if (matchedExpectedInfos.isEmpty()) return null
var lookupElement = lookupElementFactory.createLookupElement(descriptor, useReceiverTypes = false, parametersAndTypeGrayed = true)
?: return null
lookupElement = object: LookupElementDecorator<LookupElement>(lookupElement) {
var lookupElement =
lookupElementFactory.createLookupElement(descriptor, useReceiverTypes = false, parametersAndTypeGrayed = true)
?: return null
lookupElement = object : LookupElementDecorator<LookupElement>(lookupElement) {
override fun getLookupString() = "::" + delegate.lookupString
override fun getAllLookupStrings() = setOf(lookupString)
@@ -400,9 +438,8 @@ class SmartCompletion(
}
}
return lookupElement
.assignSmartCompletionPriority(SmartCompletionItemPriority.CALLABLE_REFERENCE)
.addTailAndNameSimilarity(matchedExpectedInfos)
return lookupElement.assignSmartCompletionPriority(SmartCompletionItemPriority.CALLABLE_REFERENCE)
.addTailAndNameSimilarity(matchedExpectedInfos)
}
when (descriptor) {
@@ -415,19 +452,15 @@ class SmartCompletion(
is ClassDescriptor -> {
if (descriptor.modality != Modality.ABSTRACT && !descriptor.isInner) {
descriptor.constructors
.filter(visibilityFilter)
.mapNotNullTo(this, ::toLookupElement)
descriptor.constructors.filter(visibilityFilter).mapNotNullTo(this, ::toLookupElement)
}
}
}
}
private fun buildForAsTypePosition(lookupElementFactory: BasicLookupElementFactory): Collection<LookupElement>? {
val binaryExpression = ((expression.parent as? KtUserType)
?.parent as? KtTypeReference)
?.parent as? KtBinaryExpressionWithTypeRHS
?: return null
val binaryExpression =
((expression.parent as? KtUserType)?.parent as? KtTypeReference)?.parent as? KtBinaryExpressionWithTypeRHS ?: return null
val elementType = binaryExpression.operationReference.getReferencedNameElementType()
if (elementType != KtTokens.AS_KEYWORD && elementType != KtTokens.AS_SAFE) return null
val expectedInfos = calcExpectedInfos(binaryExpression)
@@ -1,17 +1,6 @@
/*
* 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.
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.completion.smart
@@ -39,10 +28,10 @@ import org.jetbrains.kotlin.resolve.calls.util.DelegatingCall
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
class SmartCompletionSession(
configuration: CompletionSessionConfiguration,
parameters: CompletionParameters,
toFromOriginalFileMapper: ToFromOriginalFileMapper,
resultSet: CompletionResultSet
configuration: CompletionSessionConfiguration,
parameters: CompletionParameters,
toFromOriginalFileMapper: ToFromOriginalFileMapper,
resultSet: CompletionResultSet
) : CompletionSession(configuration, parameters, toFromOriginalFileMapper, resultSet) {
override val descriptorKindFilter: DescriptorKindFilter by lazy {
@@ -62,9 +51,11 @@ class SmartCompletionSession(
private val smartCompletion by lazy(LazyThreadSafetyMode.NONE) {
expression?.let {
SmartCompletion(it, resolutionFacade, bindingContext, moduleDescriptor, isVisibleFilter, indicesHelper(false),
prefixMatcher, searchScope, toFromOriginalFileMapper,
callTypeAndReceiver, isJvmModule)
SmartCompletion(
it, resolutionFacade, bindingContext, moduleDescriptor, isVisibleFilter, indicesHelper(false),
prefixMatcher, searchScope, toFromOriginalFileMapper,
callTypeAndReceiver, isJvmModule
)
}
}
@@ -92,7 +83,8 @@ class SmartCompletionSession(
val contextVariableTypesForReferenceVariants = filter?.let {
withCollectRequiredContextVariableTypes { lookupElementFactory ->
if (referenceVariantsCollector != null) {
val (imported, notImported) = referenceVariantsCollector.collectReferenceVariants(descriptorKindFilter).excludeNonInitializedVariable()
val (imported, notImported) = referenceVariantsCollector.collectReferenceVariants(descriptorKindFilter)
.excludeNonInitializedVariable()
imported.forEach { collector.addElements(filter(it, lookupElementFactory)) }
notImported.forEach { collector.addElements(filter(it, lookupElementFactory), notImported = true) }
referenceVariantsCollector.collectingFinished()
@@ -105,8 +97,11 @@ class SmartCompletionSession(
val contextVariablesProvider = RealContextVariablesProvider(referenceVariantsHelper, position)
withContextVariablesProvider(contextVariablesProvider) { lookupElementFactory ->
if (filter != null && receiverTypes != null) {
val results = ExtensionFunctionTypeValueCompletion(receiverTypes, callTypeAndReceiver.callType, lookupElementFactory)
.processVariables(contextVariablesProvider)
val results = ExtensionFunctionTypeValueCompletion(
receiverTypes,
callTypeAndReceiver.callType,
lookupElementFactory
).processVariables(contextVariablesProvider)
for ((invokeDescriptor, factory) in results) {
collector.addElements(filter(invokeDescriptor, factory))
}
@@ -117,7 +112,9 @@ class SmartCompletionSession(
collector.addElements(additionalItems)
}
if (filter != null && contextVariableTypesForReferenceVariants!!.any { contextVariablesProvider.functionTypeVariables(it).isNotEmpty() }) {
if (filter != null &&
contextVariableTypesForReferenceVariants!!.any { contextVariablesProvider.functionTypeVariables(it).isNotEmpty() }
) {
val (imported, notImported) = referenceVariantsWithSingleFunctionTypeParameter()!!
imported.forEach { collector.addElements(filter(it, lookupElementFactory)) }
notImported.forEach { collector.addElements(filter(it, lookupElementFactory), notImported = true) }
@@ -129,13 +126,12 @@ class SmartCompletionSession(
val staticMembersCompletion: StaticMembersCompletion?
if (callTypeAndReceiver is CallTypeAndReceiver.DEFAULT) {
val alreadyCollected = referenceVariantsCollector!!.allCollected.imported
staticMembersCompletion = StaticMembersCompletion(prefixMatcher, resolutionFacade, lookupElementFactory, alreadyCollected, isJvmModule)
staticMembersCompletion =
StaticMembersCompletion(prefixMatcher, resolutionFacade, lookupElementFactory, alreadyCollected, isJvmModule)
val decoratedFactory = staticMembersCompletion.decoratedLookupElementFactory(ItemPriority.STATIC_MEMBER_FROM_IMPORTS)
staticMembersCompletion.membersFromImports(file)
.flatMap { filter(it, decoratedFactory) }
.forEach { collector.addElement(it) }
}
else {
staticMembersCompletion.membersFromImports(file).flatMap { filter(it, decoratedFactory) }
.forEach { collector.addElement(it) }
} else {
staticMembersCompletion = null
}
@@ -152,7 +148,12 @@ class SmartCompletionSession(
val variants = variantsAndFactory.first
@Suppress("NAME_SHADOWING") val lookupElementFactory = variantsAndFactory.second
variants.imported.forEach { collector.addElements(filter(it, lookupElementFactory).map { it.withReceiverCast() }) }
variants.notImportedExtensions.forEach { collector.addElements(filter(it, lookupElementFactory).map { it.withReceiverCast() }, notImported = true) }
variants.notImportedExtensions.forEach {
collector.addElements(
filter(it, lookupElementFactory).map { element -> element.withReceiverCast() },
notImported = true
)
}
flushToResultSet()
}
}
@@ -160,8 +161,8 @@ class SmartCompletionSession(
if (staticMembersCompletion != null && configuration.staticMembers) {
val decoratedFactory = staticMembersCompletion.decoratedLookupElementFactory(ItemPriority.STATIC_MEMBER)
staticMembersCompletion.processMembersFromIndices(indicesHelper(false)) {
filter(it, decoratedFactory).forEach {
collector.addElement(it)
filter(it, decoratedFactory).forEach { element ->
collector.addElement(element)
flushToResultSet()
}
}
@@ -199,23 +200,24 @@ class SmartCompletionSession(
override fun getValueArgumentList() = throw UnsupportedOperationException()
}
val expectedInfos = ExpectedInfos(bindingContext, resolutionFacade, indicesHelper(false))
.calculateForArgument(dummyCall, dummyArgument)
val expectedInfos =
ExpectedInfos(bindingContext, resolutionFacade, indicesHelper(false)).calculateForArgument(dummyCall, dummyArgument)
collector.addElements(LambdaItems.collect(expectedInfos))
}
}
}
override fun createSorter(): CompletionSorter {
return super.createSorter()
.weighBefore(KindWeigher.toString(), NameSimilarityWeigher, SmartCompletionPriorityWeigher, CallableReferenceWeigher(callTypeAndReceiver.callType))
}
override fun createSorter(): CompletionSorter = super.createSorter().weighBefore(
KindWeigher.toString(),
NameSimilarityWeigher,
SmartCompletionPriorityWeigher,
CallableReferenceWeigher(callTypeAndReceiver.callType)
)
override fun createLookupElementFactory(contextVariablesProvider: ContextVariablesProvider): LookupElementFactory {
return super.createLookupElementFactory(contextVariablesProvider).copy(
standardLookupElementsPostProcessor = { wrapStandardLookupElement(it) }
override fun createLookupElementFactory(contextVariablesProvider: ContextVariablesProvider): LookupElementFactory =
super.createLookupElementFactory(contextVariablesProvider).copy(
standardLookupElementsPostProcessor = { wrapStandardLookupElement(it) }
)
}
private fun wrapStandardLookupElement(lookupElement: LookupElement): LookupElement {
val descriptor = (lookupElement.`object` as DeclarationLookupObject).descriptor
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2017 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.
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.completion.smart
@@ -58,19 +47,19 @@ import org.jetbrains.kotlin.utils.addIfNotNull
import java.util.*
class TypeInstantiationItems(
val resolutionFacade: ResolutionFacade,
val bindingContext: BindingContext,
val visibilityFilter: (DeclarationDescriptor) -> Boolean,
val toFromOriginalFileMapper: ToFromOriginalFileMapper,
val inheritorSearchScope: GlobalSearchScope,
val lookupElementFactory: LookupElementFactory,
val forOrdinaryCompletion: Boolean,
val indicesHelper: KotlinIndicesHelper
val resolutionFacade: ResolutionFacade,
val bindingContext: BindingContext,
val visibilityFilter: (DeclarationDescriptor) -> Boolean,
val toFromOriginalFileMapper: ToFromOriginalFileMapper,
val inheritorSearchScope: GlobalSearchScope,
val lookupElementFactory: LookupElementFactory,
val forOrdinaryCompletion: Boolean,
val indicesHelper: KotlinIndicesHelper
) {
fun addTo(
items: MutableCollection<LookupElement>,
inheritanceSearchers: MutableCollection<InheritanceItemsSearcher>,
expectedInfos: Collection<ExpectedInfo>
items: MutableCollection<LookupElement>,
inheritanceSearchers: MutableCollection<InheritanceItemsSearcher>,
expectedInfos: Collection<ExpectedInfo>
) {
val expectedInfosGrouped = LinkedHashMap<FuzzyType, MutableList<ExpectedInfo>>()
for (expectedInfo in expectedInfos) {
@@ -86,10 +75,10 @@ class TypeInstantiationItems(
}
private fun addTo(
items: MutableCollection<LookupElement>,
inheritanceSearchers: MutableCollection<InheritanceItemsSearcher>,
fuzzyType: FuzzyType,
tail: Tail?
items: MutableCollection<LookupElement>,
inheritanceSearchers: MutableCollection<InheritanceItemsSearcher>,
fuzzyType: FuzzyType,
tail: Tail?
) {
if (fuzzyType.type.isFunctionType) return // do not show "object: ..." for function types
@@ -115,7 +104,8 @@ class TypeInstantiationItems(
val javaClassId = JavaToKotlinClassMap.mapKotlinToJava(DescriptorUtils.getFqName(classifier))
if (javaClassId != null) {
val javaAnalog = resolutionFacade.moduleDescriptor.resolveTopLevelClass(javaClassId.asSingleFqName(), NoLookupLocation.FROM_IDE)
val javaAnalog =
resolutionFacade.moduleDescriptor.resolveTopLevelClass(javaClassId.asSingleFqName(), NoLookupLocation.FROM_IDE)
if (javaAnalog != null) {
inheritanceSearchers.addInheritorSearcher(javaAnalog, classDescriptor, typeArgs, fuzzyType.freeParameters, tail)
}
@@ -124,7 +114,11 @@ class TypeInstantiationItems(
}
private fun MutableCollection<InheritanceItemsSearcher>.addInheritorSearcher(
descriptor: ClassDescriptor, kotlinClassDescriptor: ClassDescriptor, typeArgs: List<TypeProjection>, freeParameters: Collection<TypeParameterDescriptor>, tail: Tail?
descriptor: ClassDescriptor,
kotlinClassDescriptor: ClassDescriptor,
typeArgs: List<TypeProjection>,
freeParameters: Collection<TypeParameterDescriptor>,
tail: Tail?
) {
val _declaration = DescriptorToSourceUtilsIde.getAnyDeclaration(resolutionFacade.project, descriptor) ?: return
val declaration = if (_declaration is KtDeclaration)
@@ -176,11 +170,11 @@ class TypeInstantiationItems(
// drop "in" and "out" from type arguments - they cannot be used in constructor call
val typeArgsToUse = typeArgs.map { TypeProjectionImpl(Variance.INVARIANT, it.type) }
val allTypeArgsKnown = fuzzyType.freeParameters.isEmpty() || typeArgs.none { it.type.areTypeParametersUsedInside(fuzzyType.freeParameters) }
val allTypeArgsKnown =
fuzzyType.freeParameters.isEmpty() || typeArgs.none { it.type.areTypeParametersUsedInside(fuzzyType.freeParameters) }
itemText += if (allTypeArgsKnown) {
IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_NO_ANNOTATIONS.renderTypeArguments(typeArgsToUse)
}
else {
} else {
"<...>"
}
@@ -210,8 +204,7 @@ class TypeInstantiationItems(
shortenReferences(context, startOffset, startOffset + text.length)
ImplementMembersHandler().invoke(context.project, context.editor, context.file, true)
}
else {
} else {
context.editor.caretModel.moveToOffset(startOffset + text1.length + 1) // put caret into "<>"
shortenReferences(context, startOffset, startOffset + text.length)
@@ -219,8 +212,7 @@ class TypeInstantiationItems(
}
lookupElement = lookupElement.suppressAutoInsertion()
lookupElement = lookupElement.assignSmartCompletionPriority(SmartCompletionItemPriority.ANONYMOUS_OBJECT)
}
else {
} else {
//TODO: when constructor has one parameter of lambda type with more than one parameter, generate special additional item
signatureText = when (visibleConstructors.size) {
0 -> "()"
@@ -228,7 +220,8 @@ class TypeInstantiationItems(
1 -> {
val constructor = visibleConstructors.single()
val substitutor = TypeSubstitutor.create(fuzzyType.presentationType())
val substitutedConstructor = constructor.substitute(substitutor) ?: constructor // render original signature if failed to substitute
val substitutedConstructor = constructor.substitute(substitutor)
?: constructor // render original signature if failed to substitute
BasicLookupElementFactory.SHORT_NAMES_RENDERER.renderFunctionParameters(substitutedConstructor)
}
@@ -236,24 +229,33 @@ class TypeInstantiationItems(
}
val baseInsertHandler = when (visibleConstructors.size) {
0 -> KotlinFunctionInsertHandler.Normal(CallType.DEFAULT, inputTypeArguments = false, inputValueArguments = false, argumentsOnly = true)
0 -> KotlinFunctionInsertHandler.Normal(
CallType.DEFAULT,
inputTypeArguments = false,
inputValueArguments = false,
argumentsOnly = true
)
1 -> (lookupElementFactory.insertHandlerProvider.insertHandler(visibleConstructors.single()) as KotlinFunctionInsertHandler.Normal)
.copy(argumentsOnly = true)
1 -> (lookupElementFactory.insertHandlerProvider
.insertHandler(visibleConstructors.single()) as KotlinFunctionInsertHandler.Normal).copy(argumentsOnly = true)
else -> KotlinFunctionInsertHandler.Normal(CallType.DEFAULT, inputTypeArguments = false, inputValueArguments = true, argumentsOnly = true)
else -> KotlinFunctionInsertHandler.Normal(
CallType.DEFAULT,
inputTypeArguments = false,
inputValueArguments = true,
argumentsOnly = true
)
}
insertHandler = object : InsertHandler<LookupElement> {
override fun handleInsert(context: InsertionContext, item: LookupElement) {
context.document.replaceString(context.startOffset, context.tailOffset, typeText)
context.tailOffset = context.startOffset + typeText.length
insertHandler = InsertHandler { context, item ->
context.document.replaceString(context.startOffset, context.tailOffset, typeText)
context.tailOffset = context.startOffset + typeText.length
baseInsertHandler.handleInsert(context, item)
baseInsertHandler.handleInsert(context, item)
shortenReferences(context, context.startOffset, context.tailOffset)
}
shortenReferences(context, context.startOffset, context.tailOffset)
}
if (baseInsertHandler.inputValueArguments) {
lookupElement = lookupElement.keepOldArgumentListOnTab()
}
@@ -304,34 +306,39 @@ class TypeInstantiationItems(
return FuzzyType(this, freeParameters).freeParameters.isNotEmpty()
}
private fun addSamConstructorItem(collection: MutableCollection<LookupElement>,
classifier: ClassifierDescriptorWithTypeParameters,
classDescriptor: ClassDescriptor?,
tail: Tail?) {
private fun addSamConstructorItem(
collection: MutableCollection<LookupElement>,
classifier: ClassifierDescriptorWithTypeParameters,
classDescriptor: ClassDescriptor?,
tail: Tail?
) {
if (classDescriptor?.kind == ClassKind.INTERFACE) {
val samConstructor = run {
val container = classifier.containingDeclaration
val scope = when (container) {
val scope = when (val container = classifier.containingDeclaration) {
is PackageFragmentDescriptor -> container.getMemberScope()
is ClassDescriptor -> container.unsubstitutedMemberScope
else -> return
}
scope.collectSyntheticStaticMembersAndConstructors(resolutionFacade, DescriptorKindFilter.FUNCTIONS, { classifier.name == it })
.filterIsInstance<SamConstructorDescriptor>()
.singleOrNull() ?: return
scope.collectSyntheticStaticMembersAndConstructors(
resolutionFacade,
DescriptorKindFilter.FUNCTIONS
) { classifier.name == it }
.filterIsInstance<SamConstructorDescriptor>()
.singleOrNull() ?: return
}
lookupElementFactory
.createStandardLookupElementsForDescriptor(samConstructor, useReceiverTypes = false)
.mapTo(collection) { it.assignSmartCompletionPriority(SmartCompletionItemPriority.INSTANTIATION).addTail(tail) }
.createStandardLookupElementsForDescriptor(samConstructor, useReceiverTypes = false)
.mapTo(collection) { it.assignSmartCompletionPriority(SmartCompletionItemPriority.INSTANTIATION).addTail(tail) }
}
}
private inner class InheritanceSearcher(
private val psiClass: PsiClass,
classDescriptor: ClassDescriptor,
typeArgs: List<TypeProjection>,
private val freeParameters: Collection<TypeParameterDescriptor>,
private val tail: Tail?) : InheritanceItemsSearcher {
private val psiClass: PsiClass,
classDescriptor: ClassDescriptor,
typeArgs: List<TypeProjection>,
private val freeParameters: Collection<TypeParameterDescriptor>,
private val tail: Tail?
) : InheritanceItemsSearcher {
private val baseHasTypeArgs = classDescriptor.declaredTypeParameters.isNotEmpty()
private val expectedType = KotlinTypeFactory.simpleNotNullType(Annotations.EMPTY, classDescriptor, typeArgs)
@@ -341,9 +348,8 @@ class TypeInstantiationItems(
val parameters = ClassInheritorsSearch.SearchParameters(psiClass, inheritorSearchScope, true, true, false, nameFilter)
for (inheritor in ClassInheritorsSearch.search(parameters)) {
val descriptor = inheritor.resolveToDescriptor(
resolutionFacade,
{ toFromOriginalFileMapper.toSyntheticFile(it) }
) ?: continue
resolutionFacade
) { toFromOriginalFileMapper.toSyntheticFile(it) } ?: continue
if (!visibilityFilter(descriptor)) continue
var inheritorFuzzyType = descriptor.defaultType.toFuzzyType(descriptor.typeConstructor.parameters)
@@ -1,17 +1,6 @@
/*
* 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.
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.completion.smart
@@ -23,7 +12,6 @@ import com.intellij.codeInsight.lookup.LookupElementDecorator
import com.intellij.codeInsight.lookup.LookupElementPresentation
import com.intellij.openapi.util.Key
import org.jetbrains.kotlin.builtins.ReflectionTypes
import org.jetbrains.kotlin.builtins.isFunctionType
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor
import org.jetbrains.kotlin.idea.completion.handlers.WithExpressionPrefixInsertHandler
@@ -46,10 +34,10 @@ import org.jetbrains.kotlin.util.descriptorsEqualWithSubstitution
import java.util.*
class ArtificialElementInsertHandler(
private val textBeforeCaret: String,
private val textAfterCaret: String,
private val shortenRefs: Boolean
) : InsertHandler<LookupElement>{
private val textBeforeCaret: String,
private val textAfterCaret: String,
private val shortenRefs: Boolean
) : InsertHandler<LookupElement> {
override fun handleInsert(context: InsertionContext, item: LookupElement) {
val offset = context.editor.caretModel.offset
val startOffset = offset - item.lookupString.length
@@ -67,38 +55,36 @@ fun mergeTails(tails: Collection<Tail?>): Tail? {
return tails.singleOrNull() ?: tails.toSet().singleOrNull()
}
fun LookupElement.addTail(tail: Tail?): LookupElement {
return when (tail) {
null -> this
fun LookupElement.addTail(tail: Tail?): LookupElement = when (tail) {
null -> this
Tail.COMMA -> object: LookupElementDecorator<LookupElement>(this) {
override fun handleInsert(context: InsertionContext) {
WithTailInsertHandler.COMMA.handleInsert(context, delegate)
}
Tail.COMMA -> object : LookupElementDecorator<LookupElement>(this) {
override fun handleInsert(context: InsertionContext) {
WithTailInsertHandler.COMMA.handleInsert(context, delegate)
}
}
Tail.RPARENTH -> object: LookupElementDecorator<LookupElement>(this) {
override fun handleInsert(context: InsertionContext) {
WithTailInsertHandler.RPARENTH.handleInsert(context, delegate)
}
Tail.RPARENTH -> object : LookupElementDecorator<LookupElement>(this) {
override fun handleInsert(context: InsertionContext) {
WithTailInsertHandler.RPARENTH.handleInsert(context, delegate)
}
}
Tail.RBRACKET -> object: LookupElementDecorator<LookupElement>(this) {
override fun handleInsert(context: InsertionContext) {
WithTailInsertHandler.RBRACKET.handleInsert(context, delegate)
}
Tail.RBRACKET -> object : LookupElementDecorator<LookupElement>(this) {
override fun handleInsert(context: InsertionContext) {
WithTailInsertHandler.RBRACKET.handleInsert(context, delegate)
}
}
Tail.ELSE -> object: LookupElementDecorator<LookupElement>(this) {
override fun handleInsert(context: InsertionContext) {
WithTailInsertHandler.ELSE.handleInsert(context, delegate)
}
Tail.ELSE -> object : LookupElementDecorator<LookupElement>(this) {
override fun handleInsert(context: InsertionContext) {
WithTailInsertHandler.ELSE.handleInsert(context, delegate)
}
}
Tail.RBRACE -> object: LookupElementDecorator<LookupElement>(this) {
override fun handleInsert(context: InsertionContext) {
WithTailInsertHandler.RBRACE.handleInsert(context, delegate)
}
Tail.RBRACE -> object : LookupElementDecorator<LookupElement>(this) {
override fun handleInsert(context: InsertionContext) {
WithTailInsertHandler.RBRACE.handleInsert(context, delegate)
}
}
}
@@ -121,8 +107,8 @@ fun LookupElement.withOptions(options: ItemOptions): LookupElement {
}
fun LookupElement.addTailAndNameSimilarity(
matchedExpectedInfos: Collection<ExpectedInfo>,
nameSimilarityExpectedInfos: Collection<ExpectedInfo> = matchedExpectedInfos
matchedExpectedInfos: Collection<ExpectedInfo>,
nameSimilarityExpectedInfos: Collection<ExpectedInfo> = matchedExpectedInfos
): LookupElement {
val lookupElement = addTail(mergeTails(matchedExpectedInfos.map { it.tail }))
val similarity = calcNameSimilarity(lookupElement.lookupString, nameSimilarityExpectedInfos)
@@ -134,8 +120,8 @@ fun LookupElement.addTailAndNameSimilarity(
class ExpectedInfoMatch
private constructor(
val substitutor: TypeSubstitutor?,
val makeNotNullable: Boolean
val substitutor: TypeSubstitutor?,
val makeNotNullable: Boolean
) {
fun isMatch() = substitutor != null && !makeNotNullable
@@ -165,18 +151,20 @@ fun Collection<FuzzyType>.matchExpectedInfo(expectedInfo: ExpectedInfo): Expecte
fun FuzzyType.matchExpectedInfo(expectedInfo: ExpectedInfo) = listOf(this).matchExpectedInfo(expectedInfo)
fun<TDescriptor: DeclarationDescriptor?> MutableCollection<LookupElement>.addLookupElements(
descriptor: TDescriptor,
expectedInfos: Collection<ExpectedInfo>,
infoMatcher: (ExpectedInfo) -> ExpectedInfoMatch,
noNameSimilarityForReturnItself: Boolean = false,
lookupElementFactory: (TDescriptor) -> Collection<LookupElement>
fun <TDescriptor : DeclarationDescriptor?> MutableCollection<LookupElement>.addLookupElements(
descriptor: TDescriptor,
expectedInfos: Collection<ExpectedInfo>,
infoMatcher: (ExpectedInfo) -> ExpectedInfoMatch,
noNameSimilarityForReturnItself: Boolean = false,
lookupElementFactory: (TDescriptor) -> Collection<LookupElement>
) {
class ItemData(val descriptor: TDescriptor, val itemOptions: ItemOptions) {
@Suppress("UNCHECKED_CAST")
override fun equals(other: Any?)
= descriptorsEqualWithSubstitution(this.descriptor, (other as? ItemData)?.descriptor) && itemOptions ==
(other as? ItemData)?.itemOptions
override fun equals(other: Any?) = descriptorsEqualWithSubstitution(
this.descriptor,
(other as? ItemData)?.descriptor
) && itemOptions == (other as? ItemData)?.itemOptions
override fun hashCode() = if (this.descriptor != null) this.descriptor.original.hashCode() else 0
}
@@ -194,18 +182,16 @@ fun<TDescriptor: DeclarationDescriptor?> MutableCollection<LookupElement>.addLoo
}
}
if (!matchedInfos.isEmpty()) {
if (matchedInfos.isNotEmpty()) {
for ((itemData, infos) in matchedInfos) {
val lookupElements = itemData.createLookupElements()
val nameSimilarityInfos = if (noNameSimilarityForReturnItself && descriptor is CallableDescriptor) {
infos.filter { (it.additionalData as? ReturnValueAdditionalData)?.callable != descriptor } // do not calculate name similarity with function itself in its return
}
else
} else
infos
lookupElements.mapTo(this) { it.addTailAndNameSimilarity(infos, nameSimilarityInfos) }
}
}
else {
} else {
for ((itemData, infos) in makeNullableInfos) {
addLookupElementsForNullable({ itemData.createLookupElements() }, infos)
}
@@ -220,7 +206,10 @@ private fun <T : DeclarationDescriptor?> T.substituteFixed(substitutor: TypeSubs
return this.substitute(substitutor) as T
}
private fun MutableCollection<LookupElement>.addLookupElementsForNullable(factory: () -> Collection<LookupElement>, matchedInfos: Collection<ExpectedInfo>) {
private fun MutableCollection<LookupElement>.addLookupElementsForNullable(
factory: () -> Collection<LookupElement>,
matchedInfos: Collection<ExpectedInfo>
) {
fun LookupElement.postProcess(): LookupElement {
var element = this
element = element.suppressAutoInsertion()
@@ -230,11 +219,12 @@ private fun MutableCollection<LookupElement>.addLookupElementsForNullable(factor
}
factory().mapTo(this) {
object: LookupElementDecorator<LookupElement>(it) {
object : LookupElementDecorator<LookupElement>(it) {
override fun renderElement(presentation: LookupElementPresentation) {
super.renderElement(presentation)
presentation.itemText = "!! " + presentation.itemText
}
override fun handleInsert(context: InsertionContext) {
WithTailInsertHandler("!!", spaceBefore = false, spaceAfter = false).handleInsert(context, delegate)
}
@@ -242,11 +232,12 @@ private fun MutableCollection<LookupElement>.addLookupElementsForNullable(factor
}
factory().mapTo(this) {
object: LookupElementDecorator<LookupElement>(it) {
object : LookupElementDecorator<LookupElement>(it) {
override fun renderElement(presentation: LookupElementPresentation) {
super.renderElement(presentation)
presentation.itemText = "?: " + presentation.itemText
}
override fun handleInsert(context: InsertionContext) {
WithTailInsertHandler("?:", spaceBefore = true, spaceAfter = true).handleInsert(context, delegate) //TODO: code style
}
@@ -258,10 +249,10 @@ fun CallableDescriptor.callableReferenceType(resolutionFacade: ResolutionFacade,
if (!CallType.CALLABLE_REFERENCE.descriptorKindFilter.accepts(this)) return null // not supported by callable references
return DoubleColonExpressionResolver.createKCallableTypeForReference(
this,
lhs,
resolutionFacade.getFrontendService(ReflectionTypes::class.java),
resolutionFacade.moduleDescriptor
this,
lhs,
resolutionFacade.getFrontendService(ReflectionTypes::class.java),
resolutionFacade.moduleDescriptor
)?.toFuzzyType(emptyList())
}
@@ -302,10 +293,10 @@ fun LookupElement.assignSmartCompletionPriority(priority: SmartCompletionItemPri
var LookupElement.isProbableKeyword: Boolean by NotNullableUserDataProperty(Key.create("PROBABLE_KEYWORD_KEY"), false)
fun DeclarationDescriptor.fuzzyTypesForSmartCompletion(
smartCastCalculator: SmartCastCalculator,
callTypeAndReceiver: CallTypeAndReceiver<*, *>,
resolutionFacade: ResolutionFacade,
bindingContext: BindingContext
smartCastCalculator: SmartCastCalculator,
callTypeAndReceiver: CallTypeAndReceiver<*, *>,
resolutionFacade: ResolutionFacade,
bindingContext: BindingContext
): Collection<FuzzyType> {
if (callTypeAndReceiver is CallTypeAndReceiver.CALLABLE_REFERENCE) {
val lhs = callTypeAndReceiver.receiver?.let { bindingContext[BindingContext.DOUBLE_COLON_LHS, it] }
@@ -319,24 +310,22 @@ fun DeclarationDescriptor.fuzzyTypesForSmartCompletion(
if (returnType.type.isNothing() ||
returnType.type.isNullableNothing() ||
returnType.type.isDynamic() ||
returnType.isAlmostEverything()) {
returnType.isAlmostEverything()
) {
return emptyList()
}
return if (this is VariableDescriptor) { //TODO: generic properties!
smartCastCalculator.types(this).map { it.toFuzzyType(emptyList()) }
}
else {
} else {
listOf(returnType)
}
}
else if (this is ClassDescriptor && kind.isSingleton) {
} else if (this is ClassDescriptor && kind.isSingleton) {
return listOf(defaultType.toFuzzyType(emptyList()))
}
else {
} else {
return emptyList()
}
}
fun Collection<ExpectedInfo>.filterCallableExpected()
= filter { it.fuzzyType != null && ReflectionTypes.isCallableType(it.fuzzyType!!.type) }
fun Collection<ExpectedInfo>.filterCallableExpected() =
filter { it.fuzzyType != null && ReflectionTypes.isCallableType(it.fuzzyType!!.type) }
@@ -13,6 +13,7 @@ abstract class AbstractCompiledKotlinInJavaCompletionTest : KotlinFixtureComplet
override fun getPlatform() = JvmPlatforms.unspecifiedJvmPlatform
override fun getProjectDescriptor() =
SdkAndMockLibraryProjectDescriptor(COMPLETION_TEST_DATA_BASE_PATH + "/injava/mockLib", false)
SdkAndMockLibraryProjectDescriptor("$COMPLETION_TEST_DATA_BASE_PATH/injava/mockLib", false)
override fun defaultCompletionType() = CompletionType.BASIC
}
@@ -40,10 +40,12 @@ abstract class AbstractCompletionIncrementalResolveTest : KotlinLightCodeInsight
assertTrue("\"$CHANGE_MARKER\" is missing in file \"$testPath\"", changeMarkerOffset >= 0)
val textToType = InTextDirectivesUtils.findArrayWithPrefixes(document.text, TYPE_DIRECTIVE_PREFIX).singleOrNull()
?.let { StringUtil.unquoteString(it) }
?.let { StringUtil.unquoteString(it) }
val backspaceCount = InTextDirectivesUtils.getPrefixedInt(document.text, BACKSPACES_DIRECTIVE_PREFIX)
assertTrue("At least one of \"$TYPE_DIRECTIVE_PREFIX\" and \"$BACKSPACES_DIRECTIVE_PREFIX\" should be defined",
textToType != null || backspaceCount != null)
assertTrue(
"At least one of \"$TYPE_DIRECTIVE_PREFIX\" and \"$BACKSPACES_DIRECTIVE_PREFIX\" should be defined",
textToType != null || backspaceCount != null
)
val beforeMarker = document.createRangeMarker(beforeMarkerOffset, beforeMarkerOffset + BEFORE_MARKER.length)
val changeMarker = document.createRangeMarker(changeMarkerOffset, changeMarkerOffset + CHANGE_MARKER.length)
@@ -76,15 +78,16 @@ abstract class AbstractCompletionIncrementalResolveTest : KotlinLightCodeInsight
if (caretMarker != null) {
editor.caretModel.moveToOffset(caretMarker.startOffset)
}
else {
} else {
editor.caretModel.moveToOffset(changeMarker.endOffset)
}
testCompletion(FileUtil.loadFile(file, true),
JvmPlatforms.unspecifiedJvmPlatform,
{ completionType, count -> myFixture.complete(completionType, count) },
additionalValidDirectives = listOf(TYPE_DIRECTIVE_PREFIX, BACKSPACES_DIRECTIVE_PREFIX))
testCompletion(
FileUtil.loadFile(file, true),
JvmPlatforms.unspecifiedJvmPlatform,
{ completionType, count -> myFixture.complete(completionType, count) },
additionalValidDirectives = listOf(TYPE_DIRECTIVE_PREFIX, BACKSPACES_DIRECTIVE_PREFIX)
)
KotlinTestUtils.assertEqualsToFile(File(file.parent, file.nameWithoutExtension + ".log"), testLog.toString())
} finally {
@@ -36,7 +36,10 @@ fun testCompletion(
val itemsNumber = ExpectedCompletionUtils.getExpectedNumber(fileText, platform)
val nothingElse = ExpectedCompletionUtils.isNothingElseExpected(fileText)
Assert.assertTrue("Should be some assertions about completion", expected.size != 0 || unexpected.size != 0 || itemsNumber != null || nothingElse)
Assert.assertTrue(
"Should be some assertions about completion",
expected.size != 0 || unexpected.size != 0 || itemsNumber != null || nothingElse
)
ExpectedCompletionUtils.assertContainsRenderedItems(expected, items, ExpectedCompletionUtils.isWithOrder(fileText), nothingElse)
ExpectedCompletionUtils.assertNotContainsRenderedItems(unexpected, items)
@@ -57,8 +60,7 @@ private fun testWithAutoCompleteSetting(fileText: String, doTest: () -> Unit) {
settings.AUTOCOMPLETE_ON_CODE_COMPLETION = autoComplete
settings.AUTOCOMPLETE_ON_SMART_TYPE_COMPLETION = autoComplete
doTest()
}
finally {
} finally {
settings.AUTOCOMPLETE_ON_CODE_COMPLETION = oldValue1
settings.AUTOCOMPLETE_ON_SMART_TYPE_COMPLETION = oldValue2
}
@@ -63,8 +63,7 @@ object ExpectedCompletionUtils {
}
}
fun matches(expectedProposal: CompletionProposal): Boolean
= expectedProposal.map.entries.none { it.value != map[it.key] }
fun matches(expectedProposal: CompletionProposal): Boolean = expectedProposal.map.entries.none { it.value != map[it.key] }
override fun toString(): String {
val jsonObject = JsonObject()
@@ -75,13 +74,13 @@ object ExpectedCompletionUtils {
}
companion object {
val LOOKUP_STRING: String = "lookupString"
val ALL_LOOKUP_STRINGS: String = "allLookupStrings"
val PRESENTATION_ITEM_TEXT: String = "itemText"
val PRESENTATION_TYPE_TEXT: String = "typeText"
val PRESENTATION_TAIL_TEXT: String = "tailText"
val PRESENTATION_TEXT_ATTRIBUTES: String = "attributes"
val MODULE_NAME: String = "module"
const val LOOKUP_STRING: String = "lookupString"
const val ALL_LOOKUP_STRINGS: String = "allLookupStrings"
const val PRESENTATION_ITEM_TEXT: String = "itemText"
const val PRESENTATION_TYPE_TEXT: String = "typeText"
const val PRESENTATION_TAIL_TEXT: String = "tailText"
const val PRESENTATION_TEXT_ATTRIBUTES: String = "attributes"
const val MODULE_NAME: String = "module"
val validKeys: Set<String> = setOf(
LOOKUP_STRING, ALL_LOOKUP_STRINGS, PRESENTATION_ITEM_TEXT, PRESENTATION_TYPE_TEXT,
PRESENTATION_TAIL_TEXT, PRESENTATION_TEXT_ATTRIBUTES, MODULE_NAME
@@ -89,69 +88,67 @@ object ExpectedCompletionUtils {
}
}
private val UNSUPPORTED_PLATFORM_MESSAGE = "Only ${JvmPlatforms.unspecifiedJvmPlatform} and ${JsPlatforms.defaultJsPlatform} platforms are supported"
private val UNSUPPORTED_PLATFORM_MESSAGE =
"Only ${JvmPlatforms.unspecifiedJvmPlatform} and ${JsPlatforms.defaultJsPlatform} platforms are supported"
private val EXIST_LINE_PREFIX = "EXIST:"
private const val EXIST_LINE_PREFIX = "EXIST:"
private val ABSENT_LINE_PREFIX = "ABSENT:"
private val ABSENT_JS_LINE_PREFIX = "ABSENT_JS:"
private val ABSENT_JAVA_LINE_PREFIX = "ABSENT_JAVA:"
private const val ABSENT_LINE_PREFIX = "ABSENT:"
private const val ABSENT_JS_LINE_PREFIX = "ABSENT_JS:"
private const val ABSENT_JAVA_LINE_PREFIX = "ABSENT_JAVA:"
private val EXIST_JAVA_ONLY_LINE_PREFIX = "EXIST_JAVA_ONLY:"
private val EXIST_JS_ONLY_LINE_PREFIX = "EXIST_JS_ONLY:"
private const val EXIST_JAVA_ONLY_LINE_PREFIX = "EXIST_JAVA_ONLY:"
private const val EXIST_JS_ONLY_LINE_PREFIX = "EXIST_JS_ONLY:"
private val NUMBER_LINE_PREFIX = "NUMBER:"
private val NUMBER_JS_LINE_PREFIX = "NUMBER_JS:"
private val NUMBER_JAVA_LINE_PREFIX = "NUMBER_JAVA:"
private const val NUMBER_LINE_PREFIX = "NUMBER:"
private const val NUMBER_JS_LINE_PREFIX = "NUMBER_JS:"
private const val NUMBER_JAVA_LINE_PREFIX = "NUMBER_JAVA:"
private val NOTHING_ELSE_PREFIX = "NOTHING_ELSE"
private val RUN_HIGHLIGHTING_BEFORE_PREFIX = "RUN_HIGHLIGHTING_BEFORE"
private const val NOTHING_ELSE_PREFIX = "NOTHING_ELSE"
private const val RUN_HIGHLIGHTING_BEFORE_PREFIX = "RUN_HIGHLIGHTING_BEFORE"
private val INVOCATION_COUNT_PREFIX = "INVOCATION_COUNT:"
private val WITH_ORDER_PREFIX = "WITH_ORDER"
private val AUTOCOMPLETE_SETTING_PREFIX = "AUTOCOMPLETE_SETTING:"
private const val INVOCATION_COUNT_PREFIX = "INVOCATION_COUNT:"
private const val WITH_ORDER_PREFIX = "WITH_ORDER"
private const val AUTOCOMPLETE_SETTING_PREFIX = "AUTOCOMPLETE_SETTING:"
val RUNTIME_TYPE: String = "RUNTIME_TYPE:"
const val RUNTIME_TYPE: String = "RUNTIME_TYPE:"
private val COMPLETION_TYPE_PREFIX = "COMPLETION_TYPE:"
private const val COMPLETION_TYPE_PREFIX = "COMPLETION_TYPE:"
val KNOWN_PREFIXES: List<String> = ImmutableList.of(
"LANGUAGE_VERSION:",
EXIST_LINE_PREFIX,
ABSENT_LINE_PREFIX,
ABSENT_JS_LINE_PREFIX,
ABSENT_JAVA_LINE_PREFIX,
EXIST_JAVA_ONLY_LINE_PREFIX,
EXIST_JS_ONLY_LINE_PREFIX,
NUMBER_LINE_PREFIX,
NUMBER_JS_LINE_PREFIX,
NUMBER_JAVA_LINE_PREFIX,
INVOCATION_COUNT_PREFIX,
WITH_ORDER_PREFIX,
AUTOCOMPLETE_SETTING_PREFIX,
NOTHING_ELSE_PREFIX,
RUN_HIGHLIGHTING_BEFORE_PREFIX,
RUNTIME_TYPE,
COMPLETION_TYPE_PREFIX,
LightClassComputationControl.LIGHT_CLASS_DIRECTIVE,
AstAccessControl.ALLOW_AST_ACCESS_DIRECTIVE)
"LANGUAGE_VERSION:",
EXIST_LINE_PREFIX,
ABSENT_LINE_PREFIX,
ABSENT_JS_LINE_PREFIX,
ABSENT_JAVA_LINE_PREFIX,
EXIST_JAVA_ONLY_LINE_PREFIX,
EXIST_JS_ONLY_LINE_PREFIX,
NUMBER_LINE_PREFIX,
NUMBER_JS_LINE_PREFIX,
NUMBER_JAVA_LINE_PREFIX,
INVOCATION_COUNT_PREFIX,
WITH_ORDER_PREFIX,
AUTOCOMPLETE_SETTING_PREFIX,
NOTHING_ELSE_PREFIX,
RUN_HIGHLIGHTING_BEFORE_PREFIX,
RUNTIME_TYPE,
COMPLETION_TYPE_PREFIX,
LightClassComputationControl.LIGHT_CLASS_DIRECTIVE,
AstAccessControl.ALLOW_AST_ACCESS_DIRECTIVE
)
fun itemsShouldExist(fileText: String, platform: TargetPlatform?): Array<CompletionProposal> {
return when {
platform.isJvm() -> processProposalAssertions(fileText, EXIST_LINE_PREFIX, EXIST_JAVA_ONLY_LINE_PREFIX)
platform.isJs() -> processProposalAssertions(fileText, EXIST_LINE_PREFIX, EXIST_JS_ONLY_LINE_PREFIX)
platform == null -> processProposalAssertions(fileText, EXIST_LINE_PREFIX)
else -> throw IllegalArgumentException(UNSUPPORTED_PLATFORM_MESSAGE)
}
fun itemsShouldExist(fileText: String, platform: TargetPlatform?): Array<CompletionProposal> = when {
platform.isJvm() -> processProposalAssertions(fileText, EXIST_LINE_PREFIX, EXIST_JAVA_ONLY_LINE_PREFIX)
platform.isJs() -> processProposalAssertions(fileText, EXIST_LINE_PREFIX, EXIST_JS_ONLY_LINE_PREFIX)
platform == null -> processProposalAssertions(fileText, EXIST_LINE_PREFIX)
else -> throw IllegalArgumentException(UNSUPPORTED_PLATFORM_MESSAGE)
}
fun itemsShouldAbsent(fileText: String, platform: TargetPlatform?): Array<CompletionProposal> {
return when {
platform.isJvm() -> processProposalAssertions(fileText, ABSENT_LINE_PREFIX, ABSENT_JAVA_LINE_PREFIX, EXIST_JS_ONLY_LINE_PREFIX)
platform.isJs() -> processProposalAssertions(fileText, ABSENT_LINE_PREFIX, ABSENT_JS_LINE_PREFIX, EXIST_JAVA_ONLY_LINE_PREFIX)
platform == null -> processProposalAssertions(fileText, ABSENT_LINE_PREFIX)
else -> throw IllegalArgumentException(UNSUPPORTED_PLATFORM_MESSAGE)
}
fun itemsShouldAbsent(fileText: String, platform: TargetPlatform?): Array<CompletionProposal> = when {
platform.isJvm() -> processProposalAssertions(fileText, ABSENT_LINE_PREFIX, ABSENT_JAVA_LINE_PREFIX, EXIST_JS_ONLY_LINE_PREFIX)
platform.isJs() -> processProposalAssertions(fileText, ABSENT_LINE_PREFIX, ABSENT_JS_LINE_PREFIX, EXIST_JAVA_ONLY_LINE_PREFIX)
platform == null -> processProposalAssertions(fileText, ABSENT_LINE_PREFIX)
else -> throw IllegalArgumentException(UNSUPPORTED_PLATFORM_MESSAGE)
}
fun processProposalAssertions(fileText: String, vararg prefixes: String): Array<CompletionProposal> {
@@ -161,16 +158,13 @@ object ExpectedCompletionUtils {
val parser = JsonParser()
val json: JsonElement? = try {
parser.parse(proposalStr)
}
catch(t: Throwable) {
} catch (t: Throwable) {
throw RuntimeException("Error parsing '$proposalStr'", t)
}
proposals.add(CompletionProposal(json as JsonObject))
}
else if (proposalStr.startsWith("\"") && proposalStr.endsWith("\"")) {
} else if (proposalStr.startsWith("\"") && proposalStr.endsWith("\"")) {
proposals.add(CompletionProposal(proposalStr.substring(1, proposalStr.length - 1)))
}
else {
} else {
for (item in proposalStr.split(",")) {
proposals.add(CompletionProposal(item.trim()))
}
@@ -180,50 +174,44 @@ object ExpectedCompletionUtils {
return ArrayUtil.toObjectArray(proposals, CompletionProposal::class.java)
}
fun getExpectedNumber(fileText: String, platform: TargetPlatform?): Int? {
return when {
platform == null -> InTextDirectivesUtils.getPrefixedInt(fileText, NUMBER_LINE_PREFIX)
platform.isJvm() -> getPlatformExpectedNumber(fileText, NUMBER_JAVA_LINE_PREFIX)
platform.isJs() -> getPlatformExpectedNumber(fileText, NUMBER_JS_LINE_PREFIX)
else -> throw IllegalArgumentException(UNSUPPORTED_PLATFORM_MESSAGE)
}
fun getExpectedNumber(fileText: String, platform: TargetPlatform?): Int? = when {
platform == null -> InTextDirectivesUtils.getPrefixedInt(fileText, NUMBER_LINE_PREFIX)
platform.isJvm() -> getPlatformExpectedNumber(fileText, NUMBER_JAVA_LINE_PREFIX)
platform.isJs() -> getPlatformExpectedNumber(fileText, NUMBER_JS_LINE_PREFIX)
else -> throw IllegalArgumentException(UNSUPPORTED_PLATFORM_MESSAGE)
}
fun isNothingElseExpected(fileText: String): Boolean {
return InTextDirectivesUtils.findLinesWithPrefixesRemoved(fileText, NOTHING_ELSE_PREFIX).isNotEmpty()
}
fun isNothingElseExpected(fileText: String): Boolean =
InTextDirectivesUtils.findLinesWithPrefixesRemoved(fileText, NOTHING_ELSE_PREFIX).isNotEmpty()
fun shouldRunHighlightingBeforeCompletion(fileText: String): Boolean {
return InTextDirectivesUtils.findLinesWithPrefixesRemoved(fileText, RUN_HIGHLIGHTING_BEFORE_PREFIX).isNotEmpty()
}
fun shouldRunHighlightingBeforeCompletion(fileText: String): Boolean =
InTextDirectivesUtils.findLinesWithPrefixesRemoved(fileText, RUN_HIGHLIGHTING_BEFORE_PREFIX).isNotEmpty()
fun getInvocationCount(fileText: String): Int? {
return InTextDirectivesUtils.getPrefixedInt(fileText, INVOCATION_COUNT_PREFIX)
}
fun getInvocationCount(fileText: String): Int? = InTextDirectivesUtils.getPrefixedInt(fileText, INVOCATION_COUNT_PREFIX)
fun getCompletionType(fileText: String): CompletionType? {
val completionTypeString = InTextDirectivesUtils.findStringWithPrefixes(fileText, COMPLETION_TYPE_PREFIX)
return when (completionTypeString) {
fun getCompletionType(fileText: String): CompletionType? =
when (val completionTypeString = InTextDirectivesUtils.findStringWithPrefixes(fileText, COMPLETION_TYPE_PREFIX)) {
"BASIC" -> CompletionType.BASIC
"SMART" -> CompletionType.SMART
null -> null
else -> error("Unknown completion type: $completionTypeString")
}
}
fun getAutocompleteSetting(fileText: String): Boolean? {
return InTextDirectivesUtils.getPrefixedBoolean(fileText, AUTOCOMPLETE_SETTING_PREFIX)
}
fun getAutocompleteSetting(fileText: String): Boolean? = InTextDirectivesUtils.getPrefixedBoolean(fileText, AUTOCOMPLETE_SETTING_PREFIX)
fun isWithOrder(fileText: String): Boolean {
return !InTextDirectivesUtils.findLinesWithPrefixesRemoved(fileText, WITH_ORDER_PREFIX).isEmpty()
}
fun isWithOrder(fileText: String): Boolean =
InTextDirectivesUtils.findLinesWithPrefixesRemoved(fileText, WITH_ORDER_PREFIX).isNotEmpty()
fun assertDirectivesValid(fileText: String, additionalValidDirectives: Collection<String> = emptyList()) {
InTextDirectivesUtils.assertHasUnknownPrefixes(fileText, KNOWN_PREFIXES + additionalValidDirectives)
}
fun assertContainsRenderedItems(expected: Array<CompletionProposal>, items: Array<LookupElement>, checkOrder: Boolean, nothingElse: Boolean) {
fun assertContainsRenderedItems(
expected: Array<CompletionProposal>,
items: Array<LookupElement>,
checkOrder: Boolean,
nothingElse: Boolean
) {
val itemsInformation = getItemsInformation(items)
val allItemsString = listToString(itemsInformation)
@@ -235,13 +223,15 @@ object ExpectedCompletionUtils {
var isFound = false
for (index in itemsInformation.indices) {
val proposal = itemsInformation.get(index)
val proposal = itemsInformation[index]
if (proposal.matches(expectedProposal)) {
isFound = true
Assert.assertTrue("Invalid order of existent elements in $allItemsString",
!checkOrder || index > indexOfPrevious)
Assert.assertTrue(
"Invalid order of existent elements in $allItemsString",
!checkOrder || index > indexOfPrevious
)
indexOfPrevious = index
leftItems?.remove(proposal)
@@ -253,14 +243,13 @@ object ExpectedCompletionUtils {
if (!isFound) {
if (allItemsString.isEmpty()) {
Assert.fail("Completion is empty but $expectedProposal is expected")
}
else {
} else {
Assert.fail("Expected $expectedProposal not found in:\n$allItemsString")
}
}
}
if (leftItems != null && !leftItems.isEmpty()) {
if (leftItems != null && leftItems.isNotEmpty()) {
Assert.fail("No items not mentioned in EXIST directives expected but some found:\n" + listToString(leftItems))
}
}
@@ -268,8 +257,10 @@ object ExpectedCompletionUtils {
private fun getPlatformExpectedNumber(fileText: String, platformNumberPrefix: String): Int? {
val prefixedInt = InTextDirectivesUtils.getPrefixedInt(fileText, platformNumberPrefix)
if (prefixedInt != null) {
Assert.assertNull("There shouldn't be $NUMBER_LINE_PREFIX and $platformNumberPrefix prefixes set in same time",
InTextDirectivesUtils.getPrefixedInt(fileText, NUMBER_LINE_PREFIX))
Assert.assertNull(
"There shouldn't be $NUMBER_LINE_PREFIX and $platformNumberPrefix prefixes set in same time",
InTextDirectivesUtils.getPrefixedInt(fileText, NUMBER_LINE_PREFIX)
)
return prefixedInt
}
@@ -282,8 +273,10 @@ object ExpectedCompletionUtils {
for (unexpectedProposal in unexpected) {
for (proposal in itemsInformation) {
Assert.assertFalse("Unexpected '$unexpectedProposal' presented in $allItemsString",
proposal.matches(unexpectedProposal))
Assert.assertFalse(
"Unexpected '$unexpectedProposal' presented in $allItemsString",
proposal.matches(unexpectedProposal)
)
}
}
}
@@ -296,21 +289,21 @@ object ExpectedCompletionUtils {
item.renderElement(presentation)
val map = HashMap<String, String?>()
map.put(CompletionProposal.LOOKUP_STRING, item.lookupString)
map[CompletionProposal.LOOKUP_STRING] = item.lookupString
map.put(CompletionProposal.ALL_LOOKUP_STRINGS, item.allLookupStrings.sorted().joinToString())
map[CompletionProposal.ALL_LOOKUP_STRINGS] = item.allLookupStrings.sorted().joinToString()
if (presentation.itemText != null) {
map.put(CompletionProposal.PRESENTATION_ITEM_TEXT, presentation.itemText)
map.put(CompletionProposal.PRESENTATION_TEXT_ATTRIBUTES, textAttributes(presentation))
map[CompletionProposal.PRESENTATION_ITEM_TEXT] = presentation.itemText
map[CompletionProposal.PRESENTATION_TEXT_ATTRIBUTES] = textAttributes(presentation)
}
if (presentation.typeText != null) {
map.put(CompletionProposal.PRESENTATION_TYPE_TEXT, presentation.typeText)
map[CompletionProposal.PRESENTATION_TYPE_TEXT] = presentation.typeText
}
if (presentation.tailText != null) {
map.put(CompletionProposal.PRESENTATION_TAIL_TEXT, presentation.tailText)
map[CompletionProposal.PRESENTATION_TAIL_TEXT] = presentation.tailText
}
item.moduleName?.let {
map.put(CompletionProposal.MODULE_NAME, it)
@@ -20,7 +20,7 @@ import java.util.Collections.synchronizedList
object LightClassComputationControl {
fun testWithControl(project: Project, testText: String, testBody: () -> Unit) {
val expectedLightClassFqNames = InTextDirectivesUtils.findLinesWithPrefixesRemoved(
testText, "// $LIGHT_CLASS_DIRECTIVE"
testText, "// $LIGHT_CLASS_DIRECTIVE"
).map { it.trim() }
val actualFqNames = synchronizedList(ArrayList<String>())
@@ -37,14 +37,14 @@ object LightClassComputationControl {
if (expectedLightClassFqNames.toSortedSet() != synchronized(actualFqNames) { actualFqNames.toSortedSet() }) {
Assert.fail(
"Expected to compute: ${expectedLightClassFqNames.prettyToString()}\n" +
"Actually computed: ${actualFqNames.prettyToString()}\n" +
"Use $LIGHT_CLASS_DIRECTIVE to specify expected light class computations"
"Expected to compute: ${expectedLightClassFqNames.prettyToString()}\n" +
"Actually computed: ${actualFqNames.prettyToString()}\n" +
"Use $LIGHT_CLASS_DIRECTIVE to specify expected light class computations"
)
}
}
val LIGHT_CLASS_DIRECTIVE = "LIGHT_CLASS:"
const val LIGHT_CLASS_DIRECTIVE = "LIGHT_CLASS:"
private fun List<String>.prettyToString() = if (isEmpty()) "<empty>" else joinToString()
}
@@ -56,8 +56,7 @@ inline fun <reified T : Any, R> ComponentManager.withServiceRegistered(instance:
picoContainer.unregisterComponent(key)
picoContainer.registerComponentInstance(key, instance)
return body()
}
finally {
} finally {
picoContainer.unregisterComponent(key)
}
}
@@ -14,8 +14,8 @@ import java.io.File
@Deprecated("All tests from here to be moved to the generated test")
@RunWith(JUnit3WithIdeaConfigurationRunner::class)
class BasicCompletionHandlerTest : CompletionHandlerTestBase(){
private fun checkResult(){
class BasicCompletionHandlerTest : CompletionHandlerTestBase() {
private fun checkResult() {
fixture.checkResultByFile(getTestName(false) + ".kt.after")
}
@@ -144,7 +144,11 @@ class BasicCompletionHandlerTest : CompletionHandlerTestBase(){
fun testInfixCall() = doTest(1, "to", null, null, '\n')
fun testInfixCallOnSpace() = doTest(1, "to", null, null, ' ')
fun testImportedEnumMember() { doTest(1, "AAA", null, null, '\n') }
fun testImportedEnumMember() {
doTest(1, "AAA", null, null, '\n')
}
fun testInnerClass() { doTest(1, "Inner", null, null, '\n') }
fun testInnerClass() {
doTest(1, "Inner", null, null, '\n')
}
}
@@ -82,7 +82,7 @@ abstract class CompletionHandlerTestBase : KotlinLightCodeInsightFixtureTestCase
return items.firstOrNull()
}
var foundElement : LookupElement? = null
var foundElement: LookupElement? = null
val presentation = LookupElementPresentation()
for (lookupElement in items) {
val lookupOk = if (lookupString != null) lookupElement.lookupString == lookupString else true
@@ -93,8 +93,7 @@ abstract class CompletionHandlerTestBase : KotlinLightCodeInsightFixtureTestCase
val textOk = if (itemText != null) {
val itemItemText = presentation.itemText
itemItemText != null && itemItemText == itemText
}
else {
} else {
true
}
@@ -102,14 +101,20 @@ abstract class CompletionHandlerTestBase : KotlinLightCodeInsightFixtureTestCase
val tailOk = if (tailText != null) {
val itemTailText = presentation.tailText
itemTailText != null && itemTailText == tailText
}
else {
} else {
true
}
if (tailOk) {
if (foundElement != null) {
val dump = ExpectedCompletionUtils.listToString(ExpectedCompletionUtils.getItemsInformation(arrayOf(foundElement, lookupElement)))
val dump = ExpectedCompletionUtils.listToString(
ExpectedCompletionUtils.getItemsInformation(
arrayOf(
foundElement,
lookupElement
)
)
)
fail("Several elements satisfy to completion restrictions:\n$dump")
}
@@ -134,8 +139,7 @@ abstract class CompletionHandlerTestBase : KotlinLightCodeInsightFixtureTestCase
lookup.focusDegree = LookupImpl.FocusDegree.FOCUSED
if (LookupEvent.isSpecialCompletionChar(completionChar)) {
lookup.finishLookup(completionChar)
}
else {
} else {
fixture.type(completionChar)
}
}
@@ -16,15 +16,25 @@ import java.io.File
@RunWith(JUnit3WithIdeaConfigurationRunner::class)
class SmartCompletionMultifileHandlerTest : KotlinCompletionTestCase() {
fun testImportExtensionFunction() { doTest() }
fun testImportExtensionFunction() {
doTest()
}
fun testImportExtensionProperty() { doTest() }
fun testImportExtensionProperty() {
doTest()
}
fun testAnonymousObjectGenericJava() { doTest() }
fun testAnonymousObjectGenericJava() {
doTest()
}
fun testImportAnonymousObject() { doTest() }
fun testImportAnonymousObject() {
doTest()
}
fun testNestedSamAdapter() { doTest(lookupString = "Nested") }
fun testNestedSamAdapter() {
doTest(lookupString = "Nested")
}
override fun setUp() {
setType(CompletionType.SMART)
@@ -34,7 +44,7 @@ class SmartCompletionMultifileHandlerTest : KotlinCompletionTestCase() {
private fun doTest(lookupString: String? = null, itemText: String? = null) {
val fileName = getTestName(false)
val fileNames = listOf(fileName + "-1.kt", fileName + "-2.kt", fileName + ".java")
val fileNames = listOf("$fileName-1.kt", "$fileName-2.kt", "$fileName.java")
configureByFiles(null, *fileNames.filter { File(testDataPath + it).exists() }.toTypedArray())
@@ -58,7 +68,7 @@ class SmartCompletionMultifileHandlerTest : KotlinCompletionTestCase() {
}
}
checkResultByFile(fileName + ".kt.after")
checkResultByFile("$fileName.kt.after")
}
override fun getTestDataPath() = File(COMPLETION_TEST_DATA_BASE_PATH, "/handlers/multifile/smart/").path + File.separator
@@ -15,12 +15,11 @@ fun CodeInsightTestFixture.configureWithExtraFile(path: String, vararg extraName
val noExtensionPath = FileUtil.getNameWithoutExtension(fileName)
val extensions = arrayOf("kt", "java", "properties")
val extraPaths: List<String> = extraNameParts
.flatMap { extensions.map { ext -> "$noExtensionPath$it.$ext" } }
.mapNotNull { File(testDataPath, it).takeIf { it.exists() }?.name }
val extraPaths: List<String> = extraNameParts.flatMap { extensions.map { ext -> "$noExtensionPath$it.$ext" } }
.mapNotNull { File(testDataPath, it).takeIf { file -> file.exists() }?.name }
configureByFiles(*(listOf(fileName) + extraPaths).toTypedArray())
}
@Suppress("unused") // Used in kotlin-ultimate
inline fun <reified T: Any> Any?.assertInstanceOf() = UsefulTestCase.assertInstanceOf(this, T::class.java)
inline fun <reified T : Any> Any?.assertInstanceOf() = UsefulTestCase.assertInstanceOf(this, T::class.java)
@@ -15,9 +15,10 @@ import org.jetbrains.kotlin.idea.test.rollbackCompilerOptions
import org.jetbrains.kotlin.test.InTextDirectivesUtils
import org.junit.Assert
abstract class AbstractCompletionWeigherTest(val completionType: CompletionType, val relativeTestDataPath: String) : KotlinLightCodeInsightFixtureTestCase() {
abstract class AbstractCompletionWeigherTest(val completionType: CompletionType, val relativeTestDataPath: String) :
KotlinLightCodeInsightFixtureTestCase() {
fun doTest(path: String) {
val pathPrefix = RELATIVE_COMPLETION_TEST_DATA_BASE_PATH + "/" + relativeTestDataPath
val pathPrefix = "$RELATIVE_COMPLETION_TEST_DATA_BASE_PATH/$relativeTestDataPath"
assert(path.startsWith(pathPrefix))
val relativePath = path.removePrefix(pathPrefix)
@@ -28,7 +29,7 @@ abstract class AbstractCompletionWeigherTest(val completionType: CompletionType,
val configured = configureCompilerOptions(text, project, module)
val items = InTextDirectivesUtils.findArrayWithPrefixes(text, "// ORDER:")
Assert.assertTrue("""Some items should be defined with "// ORDER:" directive""", !items.isEmpty())
Assert.assertTrue("""Some items should be defined with "// ORDER:" directive""", items.isNotEmpty())
try {
myFixture.complete(completionType, InTextDirectivesUtils.getPrefixedInt(text, "// INVOCATION_COUNT:") ?: 1)