Smart completion: refactored one big class into a set of smaller ones
This commit is contained in:
@@ -39,6 +39,7 @@ import org.jetbrains.jet.lang.resolve.scopes.JetScope;
|
||||
import org.jetbrains.jet.lexer.JetTokens;
|
||||
import org.jetbrains.jet.plugin.caches.JetShortNamesCache;
|
||||
import org.jetbrains.jet.plugin.codeInsight.TipsManager;
|
||||
import org.jetbrains.jet.plugin.completion.smart.SmartCompletion;
|
||||
import org.jetbrains.jet.plugin.completion.weigher.WeigherPackage;
|
||||
import org.jetbrains.jet.plugin.project.AnalyzerFacadeWithCache;
|
||||
import org.jetbrains.jet.plugin.project.ResolveSessionForBodies;
|
||||
|
||||
@@ -1,651 +0,0 @@
|
||||
package org.jetbrains.jet.plugin.completion
|
||||
|
||||
import org.jetbrains.jet.lang.descriptors.*
|
||||
import org.jetbrains.jet.lang.resolve.*
|
||||
import org.jetbrains.jet.lang.psi.*
|
||||
import org.jetbrains.jet.lexer.JetTokens
|
||||
import org.jetbrains.jet.plugin.project.ResolveSessionForBodies
|
||||
import org.jetbrains.jet.lang.types.*
|
||||
import org.jetbrains.jet.lang.types.checker.JetTypeChecker
|
||||
import com.intellij.codeInsight.lookup.*
|
||||
import org.jetbrains.jet.renderer.DescriptorRenderer
|
||||
import com.intellij.codeInsight.completion.*
|
||||
import org.jetbrains.jet.plugin.completion.handlers.*
|
||||
import com.google.common.collect.SetMultimap
|
||||
import java.util.*
|
||||
import org.jetbrains.jet.lang.resolve.calls.autocasts.*
|
||||
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns
|
||||
import org.jetbrains.jet.lang.resolve.name.Name
|
||||
import org.jetbrains.jet.lang.resolve.java.descriptor.JavaClassDescriptor
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import org.jetbrains.jet.plugin.codeInsight.ImplementMethodsHandler
|
||||
import com.intellij.psi.PsiDocumentManager
|
||||
import org.jetbrains.jet.plugin.codeInsight.ShortenReferences
|
||||
import org.jetbrains.jet.di.InjectorForMacros
|
||||
import org.jetbrains.jet.lang.resolve.calls.context.BasicCallResolutionContext
|
||||
import org.jetbrains.jet.lang.resolve.calls.context.ContextDependency
|
||||
import org.jetbrains.jet.lang.resolve.calls.context.CheckValueArgumentsMode
|
||||
import org.jetbrains.jet.lang.resolve.calls.CompositeExtension
|
||||
import org.jetbrains.jet.lang.resolve.calls.results.OverloadResolutionResults
|
||||
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall
|
||||
import org.jetbrains.jet.lang.resolve.calls.util.CallMaker
|
||||
import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver
|
||||
import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue
|
||||
import com.intellij.lang.ASTNode
|
||||
import org.jetbrains.jet.lang.resolve.scopes.JetScope
|
||||
import org.jetbrains.jet.plugin.refactoring.JetNameSuggester
|
||||
import org.jetbrains.jet.plugin.refactoring.JetNameValidator
|
||||
|
||||
class SmartCompletion(val expression: JetSimpleNameExpression,
|
||||
val resolveSession: ResolveSessionForBodies,
|
||||
val visibilityFilter: (DeclarationDescriptor) -> Boolean) {
|
||||
|
||||
private val bindingContext = resolveSession.resolveToElement(expression)
|
||||
private val moduleDescriptor = resolveSession.getModuleDescriptor()
|
||||
private val project = expression.getProject()
|
||||
|
||||
private enum class Tail {
|
||||
COMMA
|
||||
PARENTHESIS
|
||||
}
|
||||
|
||||
private data class ExpectedTypeInfo(val `type`: JetType, val tail: Tail?)
|
||||
|
||||
public fun buildLookupElements(referenceVariants: Iterable<DeclarationDescriptor>): Collection<LookupElement>? {
|
||||
val parent = expression.getParent()
|
||||
val expressionWithType: JetExpression
|
||||
val receiver: JetExpression?
|
||||
if (parent is JetQualifiedExpression) {
|
||||
expressionWithType = parent
|
||||
receiver = parent.getReceiverExpression()
|
||||
}
|
||||
else {
|
||||
expressionWithType = expression
|
||||
receiver = null
|
||||
}
|
||||
|
||||
val allExpectedTypes = calcExpectedTypes(expressionWithType) ?: return null
|
||||
val expectedTypes = allExpectedTypes.filter { !it.`type`.isError() }
|
||||
if (expectedTypes.isEmpty()) return null
|
||||
|
||||
val result = ArrayList<LookupElement>()
|
||||
|
||||
val typesOf: (DeclarationDescriptor) -> Iterable<JetType> = typesWithAutoCasts(expressionWithType, receiver)
|
||||
|
||||
val itemsToSkip = calcItemsToSkip(expressionWithType)
|
||||
|
||||
val functionExpectedTypes = expectedTypes.filter { KotlinBuiltIns.getInstance().isExactFunctionOrExtensionFunctionType(it.`type`) }
|
||||
|
||||
for (descriptor in referenceVariants) {
|
||||
if (itemsToSkip.contains(descriptor)) continue
|
||||
|
||||
val matchedExpectedTypes = expectedTypes.filter { expectedType ->
|
||||
typesOf(descriptor).any { it.isSubtypeOf(expectedType.`type`) }
|
||||
}
|
||||
if (matchedExpectedTypes.isNotEmpty()) {
|
||||
val lookupElement = DescriptorLookupConverter.createLookupElement(resolveSession, bindingContext, descriptor)
|
||||
result.add(addTailToLookupElement(lookupElement, matchedExpectedTypes))
|
||||
}
|
||||
|
||||
if (receiver == null) {
|
||||
toFunctionReferenceLookupElement(descriptor, functionExpectedTypes)?.let { result.add(it) }
|
||||
}
|
||||
}
|
||||
|
||||
if (receiver == null) {
|
||||
result.addTypeInstantiationItems(expectedTypes)
|
||||
|
||||
result.addStaticMembers(expressionWithType, expectedTypes)
|
||||
|
||||
result.addThisItems(expressionWithType, expectedTypes)
|
||||
|
||||
result.addLambdaItems(functionExpectedTypes)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
private fun calcExpectedTypes(expressionWithType: JetExpression): Collection<ExpectedTypeInfo>? {
|
||||
val expectedTypes = calcArgumentExpectedTypes(expressionWithType)
|
||||
if (expectedTypes != null) {
|
||||
return expectedTypes
|
||||
}
|
||||
else {
|
||||
val expectedType = bindingContext[BindingContext.EXPECTED_EXPRESSION_TYPE, expressionWithType] ?: return null
|
||||
return listOf(ExpectedTypeInfo(expectedType, null))
|
||||
}
|
||||
}
|
||||
|
||||
private fun calcArgumentExpectedTypes(expressionWithType: JetExpression): Collection<ExpectedTypeInfo>? {
|
||||
val argument = expressionWithType.getParent() as? JetValueArgument ?: return null
|
||||
if (argument.isNamed()) return null //TODO - support named arguments (also do not forget to check for presence of named arguments before)
|
||||
val argumentList = argument.getParent() as JetValueArgumentList
|
||||
val argumentIndex = argumentList.getArguments().indexOf(argument)
|
||||
val callExpression = argumentList.getParent() as? JetCallExpression ?: return null
|
||||
val calleeExpression = callExpression.getCalleeExpression()
|
||||
|
||||
val parent = callExpression.getParent()
|
||||
val receiver: ReceiverValue
|
||||
val callOperationNode: ASTNode?
|
||||
if (parent is JetQualifiedExpression && callExpression == parent.getSelectorExpression()) {
|
||||
val receiverExpression = parent.getReceiverExpression()
|
||||
val expressionType = bindingContext[BindingContext.EXPRESSION_TYPE, receiverExpression] ?: return null
|
||||
receiver = ExpressionReceiver(receiverExpression, expressionType)
|
||||
callOperationNode = parent.getOperationTokenNode()
|
||||
}
|
||||
else {
|
||||
receiver = ReceiverValue.NO_RECEIVER
|
||||
callOperationNode = null
|
||||
}
|
||||
val call = CallMaker.makeCall(receiver, callOperationNode, callExpression)
|
||||
|
||||
val resolutionScope = bindingContext[BindingContext.RESOLUTION_SCOPE, calleeExpression] ?: return null //TODO: discuss it
|
||||
|
||||
val callResolutionContext = BasicCallResolutionContext.create(
|
||||
DelegatingBindingTrace(bindingContext, "Temporary trace for smart completion"),
|
||||
resolutionScope,
|
||||
call,
|
||||
bindingContext[BindingContext.EXPECTED_EXPRESSION_TYPE, callExpression] ?: TypeUtils.NO_EXPECTED_TYPE,
|
||||
bindingContext[BindingContext.EXPRESSION_DATA_FLOW_INFO, callExpression] ?: DataFlowInfo.EMPTY,
|
||||
ContextDependency.INDEPENDENT,
|
||||
CheckValueArgumentsMode.ENABLED,
|
||||
CompositeExtension(listOf()),
|
||||
false).replaceCollectAllCandidates(true)
|
||||
val callResolver = InjectorForMacros(expressionWithType.getProject(), moduleDescriptor).getCallResolver()!!
|
||||
val results: OverloadResolutionResults<FunctionDescriptor> = callResolver.resolveFunctionCall(callResolutionContext)
|
||||
|
||||
val expectedTypes = HashSet<ExpectedTypeInfo>()
|
||||
for (candidate: ResolvedCall<FunctionDescriptor> in results.getAllCandidates()!!) {
|
||||
val parameters = candidate.getResultingDescriptor().getValueParameters()
|
||||
if (parameters.size <= argumentIndex) continue
|
||||
val parameterDescriptor = parameters[argumentIndex]
|
||||
val tail = if (argumentIndex == parameters.size - 1) Tail.PARENTHESIS else Tail.COMMA
|
||||
expectedTypes.add(ExpectedTypeInfo(parameterDescriptor.getType(), tail))
|
||||
}
|
||||
return expectedTypes
|
||||
}
|
||||
|
||||
private fun calcItemsToSkip(expression: JetExpression): Collection<DeclarationDescriptor> {
|
||||
val parent = expression.getParent()
|
||||
when(parent) {
|
||||
is JetProperty -> {
|
||||
//TODO: this can be filtered out by ordinary completion
|
||||
if (expression == parent.getInitializer()) {
|
||||
return resolveSession.resolveToElement(parent)[BindingContext.DECLARATION_TO_DESCRIPTOR, parent].toList()
|
||||
}
|
||||
}
|
||||
|
||||
is JetBinaryExpression -> {
|
||||
if (parent.getRight() == expression && parent.getOperationToken() == JetTokens.EQ) {
|
||||
val left = parent.getLeft()
|
||||
if (left is JetReferenceExpression) {
|
||||
return resolveSession.resolveToElement(left)[BindingContext.REFERENCE_TARGET, left].toList()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return listOf()
|
||||
}
|
||||
|
||||
private fun toFunctionReferenceLookupElement(descriptor: DeclarationDescriptor,
|
||||
functionExpectedTypes: Collection<ExpectedTypeInfo>): LookupElement? {
|
||||
if (functionExpectedTypes.isEmpty()) return null
|
||||
|
||||
fun toLookupElement(descriptor: FunctionDescriptor): LookupElement? {
|
||||
val functionType = functionType(descriptor)
|
||||
if (functionType == null) return null
|
||||
|
||||
val matchedExpectedTypes = functionExpectedTypes.filter { functionType.isSubtypeOf(it.`type`) }
|
||||
if (matchedExpectedTypes.isEmpty()) return null
|
||||
|
||||
var lookupElement = DescriptorLookupConverter.createLookupElement(resolveSession, bindingContext, descriptor)
|
||||
val text = "::" + (if (descriptor is ConstructorDescriptor) descriptor.getContainingDeclaration().getName() else descriptor.getName())
|
||||
lookupElement = object: LookupElementDecorator<LookupElement>(lookupElement) {
|
||||
override fun getLookupString() = text
|
||||
|
||||
override fun renderElement(presentation: LookupElementPresentation) {
|
||||
super.renderElement(presentation)
|
||||
presentation.setItemText(text)
|
||||
presentation.setTypeText("")
|
||||
}
|
||||
|
||||
override fun handleInsert(context: InsertionContext) {
|
||||
}
|
||||
}
|
||||
|
||||
return addTailToLookupElement(lookupElement, matchedExpectedTypes)
|
||||
}
|
||||
|
||||
if (descriptor is SimpleFunctionDescriptor) {
|
||||
return toLookupElement(descriptor)
|
||||
}
|
||||
else if (descriptor is ClassDescriptor && descriptor.getModality() != Modality.ABSTRACT) {
|
||||
val constructors = descriptor.getConstructors().filter(visibilityFilter)
|
||||
if (constructors.size == 1) {
|
||||
//TODO: this code is to be changed if overloads to start work after ::
|
||||
return toLookupElement(constructors.single())
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private fun MutableCollection<LookupElement>.addTypeInstantiationItems(expectedTypes: Collection<ExpectedTypeInfo>) {
|
||||
val expectedTypesGrouped: Map<JetType, List<ExpectedTypeInfo>> = expectedTypes.groupBy { TypeUtils.makeNotNullable(it.`type`) }
|
||||
for ((jetType, types) in expectedTypesGrouped) {
|
||||
val tail = mergeTails(types.map { it.tail })
|
||||
addTypeInstantiationItems(jetType, tail)
|
||||
}
|
||||
}
|
||||
|
||||
private fun MutableCollection<LookupElement>.addTypeInstantiationItems(jetType: JetType, tail: Tail?) {
|
||||
if (KotlinBuiltIns.getInstance().isExactFunctionOrExtensionFunctionType(jetType)) return // do not show "object: ..." for function types
|
||||
|
||||
val classifier = jetType.getConstructor().getDeclarationDescriptor()
|
||||
if (!(classifier is ClassDescriptor)) return
|
||||
//TODO: check for constructor's visibility
|
||||
|
||||
var lookupElement = DescriptorLookupConverter.createLookupElement(resolveSession, bindingContext, classifier)
|
||||
|
||||
var lookupString = lookupElement.getLookupString()
|
||||
|
||||
val typeArgs = jetType.getArguments()
|
||||
var itemText = lookupString + DescriptorRenderer.TEXT.renderTypeArguments(typeArgs)
|
||||
|
||||
val insertHandler: InsertHandler<LookupElement>
|
||||
val typeText = DescriptorUtils.getFqName(classifier).toString() + DescriptorRenderer.SOURCE_CODE.renderTypeArguments(typeArgs)
|
||||
if (classifier.getModality() == Modality.ABSTRACT) {
|
||||
val constructorParenthesis = if (classifier.getKind() != ClassKind.TRAIT) "()" else ""
|
||||
itemText += constructorParenthesis
|
||||
itemText = "object: " + itemText + "{...}"
|
||||
lookupString = "object" //?
|
||||
insertHandler = InsertHandler<LookupElement> {(context, item) ->
|
||||
val editor = context.getEditor()
|
||||
val startOffset = context.getStartOffset()
|
||||
val text = "object: $typeText$constructorParenthesis {}"
|
||||
editor.getDocument().replaceString(startOffset, context.getTailOffset(), text)
|
||||
editor.getCaretModel().moveToOffset(startOffset + text.length - 1)
|
||||
|
||||
shortenReferences(context, startOffset, startOffset + text.length)
|
||||
|
||||
ImplementMethodsHandler().invoke(context.getProject(), editor, context.getFile(), true)
|
||||
}
|
||||
lookupElement = lookupElement.suppressAutoInsertion()
|
||||
}
|
||||
else {
|
||||
itemText += "()"
|
||||
val constructors: Collection<ConstructorDescriptor> = classifier.getConstructors()
|
||||
val caretPosition =
|
||||
if (constructors.size == 0)
|
||||
CaretPosition.AFTER_BRACKETS
|
||||
else if (constructors.size == 1)
|
||||
if (constructors.first().getValueParameters().isEmpty()) CaretPosition.AFTER_BRACKETS else CaretPosition.IN_BRACKETS
|
||||
else
|
||||
CaretPosition.IN_BRACKETS
|
||||
insertHandler = InsertHandler<LookupElement> {(context, item) ->
|
||||
val editor = context.getEditor()
|
||||
val startOffset = context.getStartOffset()
|
||||
val text = typeText + "()"
|
||||
editor.getDocument().replaceString(startOffset, context.getTailOffset(), text)
|
||||
val endOffset = startOffset + text.length
|
||||
editor.getCaretModel().moveToOffset(if (caretPosition == CaretPosition.IN_BRACKETS) endOffset - 1 else endOffset)
|
||||
|
||||
//TODO: auto-popup parameter info and other functionality from JetFunctionInsertHandler
|
||||
|
||||
shortenReferences(context, startOffset, endOffset)
|
||||
}
|
||||
}
|
||||
|
||||
lookupElement = object: LookupElementDecorator<LookupElement>(lookupElement) {
|
||||
override fun getLookupString() = lookupString
|
||||
|
||||
override fun renderElement(presentation: LookupElementPresentation) {
|
||||
getDelegate().renderElement(presentation)
|
||||
presentation.setItemText(itemText)
|
||||
}
|
||||
|
||||
override fun handleInsert(context: InsertionContext) {
|
||||
insertHandler.handleInsert(context, getDelegate())
|
||||
}
|
||||
}
|
||||
|
||||
add(addTailToLookupElement(lookupElement, tail))
|
||||
}
|
||||
|
||||
private fun MutableCollection<LookupElement>.addThisItems(context: JetExpression, expectedTypes: Collection<ExpectedTypeInfo>) {
|
||||
val scope = bindingContext[BindingContext.RESOLUTION_SCOPE, context]
|
||||
if (scope == null) return
|
||||
|
||||
val receivers: List<ReceiverParameterDescriptor> = scope.getImplicitReceiversHierarchy()
|
||||
for (i in 0..receivers.size - 1) {
|
||||
val receiver = receivers[i]
|
||||
val thisType = receiver.getType()
|
||||
val matchedExpectedTypes = expectedTypes.filter { thisType.isSubtypeOf(it.`type`) }
|
||||
if (matchedExpectedTypes.notEmpty) {
|
||||
//TODO: use this code when KT-4258 fixed
|
||||
//val expressionText = if (i == 0) "this" else "this@" + (thisQualifierName(receiver, bindingContext) ?: continue)
|
||||
val qualifier = if (i == 0) null else thisQualifierName(receiver) ?: continue
|
||||
val expressionText = if (qualifier == null) "this" else "this@" + qualifier
|
||||
val lookupElement = LookupElementBuilder.create(expressionText).withTypeText(DescriptorRenderer.TEXT.renderType(thisType))
|
||||
add(addTailToLookupElement(lookupElement, matchedExpectedTypes))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun thisQualifierName(receiver: ReceiverParameterDescriptor): String? {
|
||||
val descriptor: DeclarationDescriptor = receiver.getContainingDeclaration()
|
||||
val name: Name = descriptor.getName()
|
||||
if (!name.isSpecial()) return name.asString()
|
||||
|
||||
val psiElement = BindingContextUtils.descriptorToDeclaration(bindingContext, descriptor)
|
||||
val expression: JetExpression? = when (psiElement) {
|
||||
is JetFunctionLiteral -> psiElement.getParent() as? JetFunctionLiteralExpression
|
||||
is JetObjectDeclaration -> psiElement.getParent() as? JetObjectLiteralExpression
|
||||
else -> null
|
||||
}
|
||||
return ((((expression?.getParent() as? JetValueArgument)
|
||||
?.getParent() as? JetValueArgumentList)
|
||||
?.getParent() as? JetCallExpression)
|
||||
?.getCalleeExpression() as? JetSimpleNameExpression)
|
||||
?.getReferencedName()
|
||||
}
|
||||
|
||||
private fun MutableCollection<LookupElement>.addLambdaItems(functionExpectedTypes: Collection<ExpectedTypeInfo>) {
|
||||
val distinctTypes = functionExpectedTypes.map { it.`type` }.toSet()
|
||||
|
||||
fun createLookupElement(lookupString: String, textBeforeCaret: String, textAfterCaret: String, shortenRefs: Boolean)
|
||||
= LookupElementBuilder.create(lookupString)
|
||||
.withInsertHandler(ArtificialElementInsertHandler(textBeforeCaret, textAfterCaret, shortenRefs))
|
||||
.suppressAutoInsertion()
|
||||
|
||||
val singleType = if (distinctTypes.size == 1) distinctTypes.single() else null
|
||||
val singleSignatureLength = singleType?.let { KotlinBuiltIns.getInstance().getParameterTypeProjectionsFromFunctionType(it).size }
|
||||
val offerNoParametersLambda = singleSignatureLength == 0 || singleSignatureLength == 1
|
||||
if (offerNoParametersLambda) {
|
||||
val lookupElement = createLookupElement("{...}", "{ ", " }", shortenRefs = false)
|
||||
add(addTailToLookupElement(lookupElement, functionExpectedTypes))
|
||||
}
|
||||
|
||||
if (singleSignatureLength != 0) {
|
||||
fun functionParameterTypes(functionType: JetType)
|
||||
= KotlinBuiltIns.getInstance().getParameterTypeProjectionsFromFunctionType(functionType).map { it.getType() }
|
||||
|
||||
for (functionType in distinctTypes) {
|
||||
val parameterTypes = functionParameterTypes(functionType)
|
||||
val parametersPresentation = parameterTypes.map { DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(it) }.makeString(", ")
|
||||
|
||||
val useExplicitTypes = distinctTypes.stream().any { it != functionType && functionParameterTypes(it).size == parameterTypes.size }
|
||||
val nameValidator = JetNameValidator.getEmptyValidator(project)
|
||||
|
||||
fun parameterName(parameterType: JetType) = JetNameSuggester.suggestNames(parameterType, nameValidator, "p")[0]
|
||||
|
||||
fun parameterText(parameterType: JetType): String {
|
||||
return if (useExplicitTypes)
|
||||
parameterName(parameterType) + ": " + DescriptorRenderer.SOURCE_CODE.renderType(parameterType)
|
||||
else
|
||||
parameterName(parameterType)
|
||||
}
|
||||
|
||||
val parametersText = parameterTypes.map(::parameterText).makeString(", ")
|
||||
|
||||
val useParenthesis = parameterTypes.size != 1
|
||||
fun wrap(s: String) = if (useParenthesis) "($s)" else s
|
||||
|
||||
val lookupString = "{ ${wrap(parametersPresentation)} -> ... }"
|
||||
val lookupElement = createLookupElement(lookupString, "{ ${wrap(parametersText)} -> ", " }", shortenRefs = true)
|
||||
add(addTailToLookupElement(lookupElement, functionExpectedTypes.filter { it.`type` == functionType }))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun typesWithAutoCasts(expression: JetExpression, receiver: JetExpression?): (DeclarationDescriptor) -> Iterable<JetType> {
|
||||
val dataFlowInfo = bindingContext[BindingContext.EXPRESSION_DATA_FLOW_INFO, expression]
|
||||
val (variableToTypes: Map<VariableDescriptor, Collection<JetType>>, notNullVariables: Set<VariableDescriptor>)
|
||||
= processDataFlowInfo(dataFlowInfo, receiver)
|
||||
|
||||
fun typesOf(descriptor: DeclarationDescriptor): Iterable<JetType> {
|
||||
if (descriptor is CallableDescriptor) {
|
||||
var returnType = descriptor.getReturnType()
|
||||
if (returnType != null && KotlinBuiltIns.getInstance().isNothing(returnType!!)) {
|
||||
//TODO: maybe we should include them on the second press?
|
||||
return listOf()
|
||||
}
|
||||
if (descriptor is VariableDescriptor) {
|
||||
if (notNullVariables.contains(descriptor) && returnType != null) {
|
||||
returnType = TypeUtils.makeNotNullable(returnType!!)
|
||||
}
|
||||
|
||||
val autoCastTypes = variableToTypes[descriptor]
|
||||
if (autoCastTypes != null && !autoCastTypes.isEmpty()) {
|
||||
return autoCastTypes + returnType.toList()
|
||||
}
|
||||
}
|
||||
return returnType.toList()
|
||||
}
|
||||
else if (descriptor is ClassDescriptor && descriptor.getKind() == ClassKind.ENUM_ENTRY) {
|
||||
return listOf(descriptor.getDefaultType())
|
||||
}
|
||||
else {
|
||||
return listOf()
|
||||
}
|
||||
}
|
||||
|
||||
return ::typesOf
|
||||
}
|
||||
|
||||
private data class ProcessDataFlowInfoResult(
|
||||
val variableToTypes: Map<VariableDescriptor, Collection<JetType>> = Collections.emptyMap(),
|
||||
val notNullVariables: Set<VariableDescriptor> = Collections.emptySet()
|
||||
)
|
||||
|
||||
private fun processDataFlowInfo(dataFlowInfo: DataFlowInfo?, receiver: JetExpression?): ProcessDataFlowInfoResult {
|
||||
if (dataFlowInfo != null) {
|
||||
val dataFlowValueToVariable: (DataFlowValue) -> VariableDescriptor?
|
||||
if (receiver != null) {
|
||||
val receiverType = bindingContext[BindingContext.EXPRESSION_TYPE, receiver]
|
||||
if (receiverType != null) {
|
||||
val receiverId = DataFlowValueFactory.createDataFlowValue(receiver, receiverType, bindingContext).getId()
|
||||
dataFlowValueToVariable = {(value) ->
|
||||
val id = value.getId()
|
||||
if (id is com.intellij.openapi.util.Pair<*, *> && id.first == receiverId) id.second as? VariableDescriptor else null
|
||||
}
|
||||
}
|
||||
else {
|
||||
return ProcessDataFlowInfoResult()
|
||||
}
|
||||
}
|
||||
else {
|
||||
dataFlowValueToVariable = {(value) -> value.getId() as? VariableDescriptor }
|
||||
}
|
||||
|
||||
val variableToType = HashMap<VariableDescriptor, Collection<JetType>>()
|
||||
val typeInfo: SetMultimap<DataFlowValue, JetType> = dataFlowInfo.getCompleteTypeInfo()
|
||||
for ((dataFlowValue, types) in typeInfo.asMap().entrySet()) {
|
||||
val variable = dataFlowValueToVariable.invoke(dataFlowValue)
|
||||
if (variable != null) {
|
||||
variableToType[variable] = types
|
||||
}
|
||||
}
|
||||
|
||||
val nullabilityInfo: Map<DataFlowValue, Nullability> = dataFlowInfo.getCompleteNullabilityInfo()
|
||||
val notNullVariables = nullabilityInfo
|
||||
.filter { it.getValue() == Nullability.NOT_NULL }
|
||||
.map { dataFlowValueToVariable(it.getKey()) }
|
||||
.filterNotNullTo(HashSet<VariableDescriptor>())
|
||||
|
||||
return ProcessDataFlowInfoResult(variableToType, notNullVariables)
|
||||
}
|
||||
|
||||
return ProcessDataFlowInfoResult()
|
||||
}
|
||||
|
||||
// adds java static members, enum members and members from class object
|
||||
private fun MutableCollection<LookupElement>.addStaticMembers(context: JetExpression, expectedTypes: Collection<ExpectedTypeInfo>) {
|
||||
val scope = bindingContext[BindingContext.RESOLUTION_SCOPE, context]
|
||||
if (scope == null) return
|
||||
|
||||
val expectedTypesByClass = expectedTypes.groupBy { TypeUtils.getClassDescriptor(it.`type`) }
|
||||
for ((classDescriptor, expectedTypesForClass) in expectedTypesByClass) {
|
||||
if (classDescriptor != null && !classDescriptor.getName().isSpecial()) {
|
||||
addStaticMembers(classDescriptor, expectedTypesForClass, scope)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun MutableCollection<LookupElement>.addStaticMembers(classDescriptor: ClassDescriptor,
|
||||
expectedTypes: Collection<ExpectedTypeInfo>,
|
||||
scope: JetScope) {
|
||||
|
||||
fun processMember(descriptor: DeclarationDescriptor) {
|
||||
if (descriptor is DeclarationDescriptorWithVisibility && !Visibilities.isVisible(descriptor, scope.getContainingDeclaration())) return
|
||||
|
||||
val matchedExpectedTypes = expectedTypes.filter {
|
||||
expectedType ->
|
||||
descriptor is CallableDescriptor && descriptor.getReturnType()?.let { it.isSubtypeOf(expectedType.`type`) } ?: false
|
||||
|| descriptor is ClassDescriptor && descriptor.getKind() == ClassKind.ENUM_ENTRY
|
||||
}
|
||||
if (matchedExpectedTypes.isEmpty()) return
|
||||
|
||||
val lookupElement = createStaticMemberLookupElement(descriptor, classDescriptor)
|
||||
add(addTailToLookupElement(lookupElement, matchedExpectedTypes))
|
||||
}
|
||||
|
||||
if (classDescriptor is JavaClassDescriptor) {
|
||||
val pseudoPackage = classDescriptor.getCorrespondingPackageFragment()
|
||||
if (pseudoPackage != null) {
|
||||
pseudoPackage.getMemberScope().getAllDescriptors().forEach(::processMember)
|
||||
}
|
||||
}
|
||||
|
||||
val classObject = classDescriptor.getClassObjectDescriptor()
|
||||
if (classObject != null) {
|
||||
classObject.getDefaultType().getMemberScope().getAllDescriptors().forEach(::processMember)
|
||||
}
|
||||
|
||||
if (classDescriptor.getKind() == ClassKind.ENUM_CLASS) {
|
||||
classDescriptor.getDefaultType().getMemberScope().getAllDescriptors().forEach(::processMember)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun createStaticMemberLookupElement(memberDescriptor: DeclarationDescriptor, classDescriptor: ClassDescriptor): LookupElement {
|
||||
val lookupElement = DescriptorLookupConverter.createLookupElement(resolveSession, bindingContext, memberDescriptor)
|
||||
val qualifierPresentation = classDescriptor.getName().asString()
|
||||
val lookupString = qualifierPresentation + "." + lookupElement.getLookupString()
|
||||
val qualifierText = DescriptorUtils.getFqName(classDescriptor).asString() //TODO: escape keywords
|
||||
|
||||
val caretPosition: CaretPosition?
|
||||
if (memberDescriptor is FunctionDescriptor) {
|
||||
caretPosition = if (memberDescriptor.getValueParameters().empty) CaretPosition.AFTER_BRACKETS else CaretPosition.IN_BRACKETS
|
||||
}
|
||||
else {
|
||||
caretPosition = null
|
||||
}
|
||||
|
||||
return object: LookupElementDecorator<LookupElement>(lookupElement) {
|
||||
override fun getLookupString() = lookupString
|
||||
|
||||
override fun renderElement(presentation: LookupElementPresentation) {
|
||||
getDelegate().renderElement(presentation)
|
||||
|
||||
presentation.setItemText(qualifierPresentation + "." + presentation.getItemText())
|
||||
|
||||
val tailText = " (" + DescriptorUtils.getFqName(classDescriptor.getContainingDeclaration()) + ")"
|
||||
if (memberDescriptor is FunctionDescriptor) {
|
||||
presentation.appendTailText(tailText, true)
|
||||
}
|
||||
else {
|
||||
presentation.setTailText(tailText, true)
|
||||
}
|
||||
|
||||
if (presentation.getTypeText().isNullOrEmpty()) {
|
||||
presentation.setTypeText(DescriptorRenderer.TEXT.renderType(classDescriptor.getDefaultType()))
|
||||
}
|
||||
}
|
||||
|
||||
override fun handleInsert(context: InsertionContext) {
|
||||
val editor = context.getEditor()
|
||||
val startOffset = context.getStartOffset()
|
||||
var text = qualifierText + "." + memberDescriptor.getName().asString() //TODO: escape
|
||||
if (memberDescriptor is FunctionDescriptor) {
|
||||
text += "()"
|
||||
//TODO: auto-popup parameter info and other functionality from JetFunctionInsertHandler
|
||||
}
|
||||
|
||||
editor.getDocument().replaceString(startOffset, context.getTailOffset(), text)
|
||||
val endOffset = startOffset + text.length
|
||||
editor.getCaretModel().moveToOffset(if (caretPosition == CaretPosition.IN_BRACKETS) endOffset - 1 else endOffset)
|
||||
|
||||
shortenReferences(context, startOffset, startOffset + qualifierText.length)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class object {
|
||||
private class ArtificialElementInsertHandler(
|
||||
val textBeforeCaret: String, val textAfterCaret: String, val shortenRefs: Boolean) : InsertHandler<LookupElement>{
|
||||
override fun handleInsert(context: InsertionContext, item: LookupElement) {
|
||||
val offset = context.getEditor().getCaretModel().getOffset()
|
||||
val startOffset = offset - item.getLookupString().length
|
||||
context.getDocument().deleteString(startOffset, offset) // delete inserted lookup string
|
||||
context.getDocument().insertString(startOffset, textBeforeCaret + textAfterCaret)
|
||||
context.getEditor().getCaretModel().moveToOffset(startOffset + textBeforeCaret.length)
|
||||
|
||||
if (shortenRefs) {
|
||||
shortenReferences(context, startOffset, startOffset + textBeforeCaret.length + textAfterCaret.length)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun shortenReferences(context: InsertionContext, startOffset: Int, endOffset: Int) {
|
||||
PsiDocumentManager.getInstance(context.getProject()).commitAllDocuments();
|
||||
val file = context.getFile() as JetFile
|
||||
val element = PsiTreeUtil.findElementOfClassAtRange(file, startOffset, endOffset, javaClass<JetElement>())
|
||||
if (element != null) {
|
||||
ShortenReferences.process(element)
|
||||
}
|
||||
}
|
||||
|
||||
private fun mergeTails(tails: Collection<Tail?>): Tail? {
|
||||
if (tails.size == 1) return tails.single()
|
||||
return if (HashSet(tails).size == 1) tails.first() else null
|
||||
}
|
||||
|
||||
private fun addTailToLookupElement(lookupElement: LookupElement, tail: Tail?): LookupElement {
|
||||
return when (tail) {
|
||||
null -> lookupElement
|
||||
|
||||
Tail.COMMA -> object: LookupElementDecorator<LookupElement>(lookupElement) {
|
||||
override fun handleInsert(context: InsertionContext) {
|
||||
WithTailInsertHandler(',', true /*TODO: use code style option*/).handleInsert(context, lookupElement)
|
||||
}
|
||||
}
|
||||
|
||||
Tail.PARENTHESIS -> object: LookupElementDecorator<LookupElement>(lookupElement) {
|
||||
override fun handleInsert(context: InsertionContext) {
|
||||
WithTailInsertHandler(')', false).handleInsert(context, lookupElement)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun addTailToLookupElement(lookupElement: LookupElement, matchedExpectedTypes: Collection<ExpectedTypeInfo>): LookupElement
|
||||
= addTailToLookupElement(lookupElement, mergeTails(matchedExpectedTypes.map { it.tail }))
|
||||
|
||||
private fun LookupElement.suppressAutoInsertion() = AutoCompletionPolicy.NEVER_AUTOCOMPLETE.applyPolicy(this)
|
||||
|
||||
private fun functionType(function: FunctionDescriptor): JetType? {
|
||||
return KotlinBuiltIns.getInstance().getKFunctionType(function.getAnnotations(),
|
||||
null,
|
||||
function.getValueParameters().map { it.getType() },
|
||||
function.getReturnType() ?: return null,
|
||||
function.getReceiverParameter() != null)
|
||||
}
|
||||
|
||||
private fun JetType.isSubtypeOf(expectedType: JetType) = !isError() && JetTypeChecker.INSTANCE.isSubtypeOf(this, expectedType)
|
||||
|
||||
private fun <T : Any> T?.toList(): List<T> = if (this != null) listOf(this) else listOf()
|
||||
|
||||
private fun String?.isNullOrEmpty() = this == null || this.isEmpty()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
package org.jetbrains.jet.plugin.completion.smart
|
||||
|
||||
import org.jetbrains.jet.lang.psi.JetExpression
|
||||
import org.jetbrains.jet.lang.resolve.BindingContext
|
||||
import org.jetbrains.jet.lang.psi.JetValueArgument
|
||||
import org.jetbrains.jet.lang.psi.JetValueArgumentList
|
||||
import org.jetbrains.jet.lang.psi.JetCallExpression
|
||||
import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue
|
||||
import com.intellij.lang.ASTNode
|
||||
import org.jetbrains.jet.lang.psi.JetQualifiedExpression
|
||||
import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver
|
||||
import org.jetbrains.jet.lang.resolve.calls.util.CallMaker
|
||||
import org.jetbrains.jet.lang.resolve.calls.context.BasicCallResolutionContext
|
||||
import org.jetbrains.jet.lang.resolve.DelegatingBindingTrace
|
||||
import org.jetbrains.jet.lang.types.TypeUtils
|
||||
import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo
|
||||
import org.jetbrains.jet.lang.resolve.calls.context.ContextDependency
|
||||
import org.jetbrains.jet.lang.resolve.calls.context.CheckValueArgumentsMode
|
||||
import org.jetbrains.jet.lang.resolve.calls.CompositeExtension
|
||||
import org.jetbrains.jet.di.InjectorForMacros
|
||||
import org.jetbrains.jet.lang.resolve.calls.results.OverloadResolutionResults
|
||||
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor
|
||||
import java.util.HashSet
|
||||
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall
|
||||
import org.jetbrains.jet.lang.descriptors.ModuleDescriptor
|
||||
|
||||
class ExpectedTypes(val bindingContext: BindingContext, val moduleDescriptor: ModuleDescriptor) {
|
||||
public fun calculate(expressionWithType: JetExpression): Collection<ExpectedTypeInfo>? {
|
||||
val expectedTypes = calculateForArgument(expressionWithType)
|
||||
if (expectedTypes != null) {
|
||||
return expectedTypes
|
||||
}
|
||||
else {
|
||||
val expectedType = bindingContext[BindingContext.EXPECTED_EXPRESSION_TYPE, expressionWithType] ?: return null
|
||||
return listOf(ExpectedTypeInfo(expectedType, null))
|
||||
}
|
||||
}
|
||||
|
||||
private fun calculateForArgument(expressionWithType: JetExpression): Collection<ExpectedTypeInfo>? {
|
||||
val argument = expressionWithType.getParent() as? JetValueArgument ?: return null
|
||||
if (argument.isNamed()) return null //TODO - support named arguments (also do not forget to check for presence of named arguments before)
|
||||
val argumentList = argument.getParent() as JetValueArgumentList
|
||||
val argumentIndex = argumentList.getArguments().indexOf(argument)
|
||||
val callExpression = argumentList.getParent() as? JetCallExpression ?: return null
|
||||
val calleeExpression = callExpression.getCalleeExpression()
|
||||
|
||||
val parent = callExpression.getParent()
|
||||
val receiver: ReceiverValue
|
||||
val callOperationNode: ASTNode?
|
||||
if (parent is JetQualifiedExpression && callExpression == parent.getSelectorExpression()) {
|
||||
val receiverExpression = parent.getReceiverExpression()
|
||||
val expressionType = bindingContext[BindingContext.EXPRESSION_TYPE, receiverExpression] ?: return null
|
||||
receiver = ExpressionReceiver(receiverExpression, expressionType)
|
||||
callOperationNode = parent.getOperationTokenNode()
|
||||
}
|
||||
else {
|
||||
receiver = ReceiverValue.NO_RECEIVER
|
||||
callOperationNode = null
|
||||
}
|
||||
val call = CallMaker.makeCall(receiver, callOperationNode, callExpression)
|
||||
|
||||
val resolutionScope = bindingContext[BindingContext.RESOLUTION_SCOPE, calleeExpression] ?: return null //TODO: discuss it
|
||||
|
||||
val callResolutionContext = BasicCallResolutionContext.create(
|
||||
DelegatingBindingTrace(bindingContext, "Temporary trace for smart completion"),
|
||||
resolutionScope,
|
||||
call,
|
||||
bindingContext[BindingContext.EXPECTED_EXPRESSION_TYPE, callExpression] ?: TypeUtils.NO_EXPECTED_TYPE,
|
||||
bindingContext[BindingContext.EXPRESSION_DATA_FLOW_INFO, callExpression] ?: DataFlowInfo.EMPTY,
|
||||
ContextDependency.INDEPENDENT,
|
||||
CheckValueArgumentsMode.ENABLED,
|
||||
CompositeExtension(listOf()),
|
||||
false).replaceCollectAllCandidates(true)
|
||||
val callResolver = InjectorForMacros(expressionWithType.getProject(), moduleDescriptor).getCallResolver()!!
|
||||
val results: OverloadResolutionResults<FunctionDescriptor> = callResolver.resolveFunctionCall(callResolutionContext)
|
||||
|
||||
val expectedTypes = HashSet<ExpectedTypeInfo>()
|
||||
for (candidate: ResolvedCall<FunctionDescriptor> in results.getAllCandidates()!!) {
|
||||
val parameters = candidate.getResultingDescriptor().getValueParameters()
|
||||
if (parameters.size <= argumentIndex) continue
|
||||
val parameterDescriptor = parameters[argumentIndex]
|
||||
val tail = if (argumentIndex == parameters.size - 1) Tail.PARENTHESIS else Tail.COMMA
|
||||
expectedTypes.add(ExpectedTypeInfo(parameterDescriptor.getType(), tail))
|
||||
}
|
||||
return expectedTypes
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package org.jetbrains.jet.plugin.completion.smart
|
||||
|
||||
import com.intellij.codeInsight.lookup.LookupElement
|
||||
import com.intellij.codeInsight.lookup.LookupElementBuilder
|
||||
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns
|
||||
import org.jetbrains.jet.lang.types.JetType
|
||||
import org.jetbrains.jet.renderer.DescriptorRenderer
|
||||
import org.jetbrains.jet.plugin.refactoring.JetNameValidator
|
||||
import org.jetbrains.jet.plugin.refactoring.JetNameSuggester
|
||||
import com.intellij.openapi.project.Project
|
||||
|
||||
class LambdaItems(val project: Project) {
|
||||
public fun addToCollection(collection: MutableCollection<LookupElement>, functionExpectedTypes: Collection<ExpectedTypeInfo>) {
|
||||
val distinctTypes = functionExpectedTypes.map { it.`type` }.toSet()
|
||||
|
||||
fun createLookupElement(lookupString: String, textBeforeCaret: String, textAfterCaret: String, shortenRefs: Boolean)
|
||||
= LookupElementBuilder.create(lookupString)
|
||||
.withInsertHandler(ArtificialElementInsertHandler(textBeforeCaret, textAfterCaret, shortenRefs))
|
||||
.suppressAutoInsertion()
|
||||
|
||||
val singleType = if (distinctTypes.size == 1) distinctTypes.single() else null
|
||||
val singleSignatureLength = singleType?.let { KotlinBuiltIns.getInstance().getParameterTypeProjectionsFromFunctionType(it).size }
|
||||
val offerNoParametersLambda = singleSignatureLength == 0 || singleSignatureLength == 1
|
||||
if (offerNoParametersLambda) {
|
||||
val lookupElement = createLookupElement("{...}", "{ ", " }", shortenRefs = false)
|
||||
collection.add(addTailToLookupElement(lookupElement, functionExpectedTypes))
|
||||
}
|
||||
|
||||
if (singleSignatureLength != 0) {
|
||||
fun functionParameterTypes(functionType: JetType)
|
||||
= KotlinBuiltIns.getInstance().getParameterTypeProjectionsFromFunctionType(functionType).map { it.getType() }
|
||||
|
||||
for (functionType in distinctTypes) {
|
||||
val parameterTypes = functionParameterTypes(functionType)
|
||||
val parametersPresentation = parameterTypes.map { DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(it) }.makeString(", ")
|
||||
|
||||
val useExplicitTypes = distinctTypes.stream().any { it != functionType && functionParameterTypes(it).size == parameterTypes.size }
|
||||
val nameValidator = JetNameValidator.getEmptyValidator(project)
|
||||
|
||||
fun parameterName(parameterType: JetType) = JetNameSuggester.suggestNames(parameterType, nameValidator, "p")[0]
|
||||
|
||||
fun parameterText(parameterType: JetType): String {
|
||||
return if (useExplicitTypes)
|
||||
parameterName(parameterType) + ": " + DescriptorRenderer.SOURCE_CODE.renderType(parameterType)
|
||||
else
|
||||
parameterName(parameterType)
|
||||
}
|
||||
|
||||
val parametersText = parameterTypes.map(::parameterText).makeString(", ")
|
||||
|
||||
val useParenthesis = parameterTypes.size != 1
|
||||
fun wrap(s: String) = if (useParenthesis) "($s)" else s
|
||||
|
||||
val lookupString = "{ ${wrap(parametersPresentation)} -> ... }"
|
||||
val lookupElement = createLookupElement(lookupString, "{ ${wrap(parametersText)} -> ", " }", shortenRefs = true)
|
||||
collection.add(addTailToLookupElement(lookupElement, functionExpectedTypes.filter { it.`type` == functionType }))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
package org.jetbrains.jet.plugin.completion.smart
|
||||
|
||||
import org.jetbrains.jet.lang.descriptors.*
|
||||
import org.jetbrains.jet.lang.resolve.*
|
||||
import org.jetbrains.jet.lang.psi.*
|
||||
import org.jetbrains.jet.lexer.JetTokens
|
||||
import org.jetbrains.jet.plugin.project.ResolveSessionForBodies
|
||||
import org.jetbrains.jet.lang.types.*
|
||||
import com.intellij.codeInsight.lookup.*
|
||||
import com.intellij.codeInsight.completion.*
|
||||
import java.util.*
|
||||
import org.jetbrains.jet.plugin.completion.DescriptorLookupConverter
|
||||
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns
|
||||
|
||||
enum class Tail {
|
||||
COMMA
|
||||
PARENTHESIS
|
||||
}
|
||||
|
||||
data class ExpectedTypeInfo(val `type`: JetType, val tail: Tail?)
|
||||
|
||||
class SmartCompletion(val expression: JetSimpleNameExpression,
|
||||
val resolveSession: ResolveSessionForBodies,
|
||||
val visibilityFilter: (DeclarationDescriptor) -> Boolean) {
|
||||
|
||||
private val bindingContext = resolveSession.resolveToElement(expression)
|
||||
private val moduleDescriptor = resolveSession.getModuleDescriptor()
|
||||
private val project = expression.getProject()
|
||||
|
||||
public fun buildLookupElements(referenceVariants: Iterable<DeclarationDescriptor>): Collection<LookupElement>? {
|
||||
val parent = expression.getParent()
|
||||
val expressionWithType: JetExpression
|
||||
val receiver: JetExpression?
|
||||
if (parent is JetQualifiedExpression) {
|
||||
expressionWithType = parent
|
||||
receiver = parent.getReceiverExpression()
|
||||
}
|
||||
else {
|
||||
expressionWithType = expression
|
||||
receiver = null
|
||||
}
|
||||
|
||||
val allExpectedTypes = ExpectedTypes(bindingContext, moduleDescriptor).calculate(expressionWithType) ?: return null
|
||||
val expectedTypes = allExpectedTypes.filter { !it.`type`.isError() }
|
||||
if (expectedTypes.isEmpty()) return null
|
||||
|
||||
val result = ArrayList<LookupElement>()
|
||||
|
||||
val typesWithAutoCasts: (DeclarationDescriptor) -> Iterable<JetType> = TypesWithAutoCasts(bindingContext).calculate(expressionWithType, receiver)
|
||||
|
||||
val itemsToSkip = calcItemsToSkip(expressionWithType)
|
||||
|
||||
val functionExpectedTypes = expectedTypes.filter { KotlinBuiltIns.getInstance().isExactFunctionOrExtensionFunctionType(it.`type`) }
|
||||
|
||||
for (descriptor in referenceVariants) {
|
||||
if (itemsToSkip.contains(descriptor)) continue
|
||||
|
||||
val matchedExpectedTypes = expectedTypes.filter { expectedType ->
|
||||
typesWithAutoCasts(descriptor).any { it.isSubtypeOf(expectedType.`type`) }
|
||||
}
|
||||
if (matchedExpectedTypes.isNotEmpty()) {
|
||||
val lookupElement = DescriptorLookupConverter.createLookupElement(resolveSession, bindingContext, descriptor)
|
||||
result.add(addTailToLookupElement(lookupElement, matchedExpectedTypes))
|
||||
}
|
||||
|
||||
if (receiver == null) {
|
||||
toFunctionReferenceLookupElement(descriptor, functionExpectedTypes)?.let { result.add(it) }
|
||||
}
|
||||
}
|
||||
|
||||
if (receiver == null) {
|
||||
TypeInstantiationItems(bindingContext, resolveSession).addToCollection(result, expectedTypes)
|
||||
|
||||
StaticMembers(bindingContext, resolveSession).addToCollection(result, expectedTypes, expression)
|
||||
|
||||
ThisItems(bindingContext).addToCollection(result, expressionWithType, expectedTypes)
|
||||
|
||||
LambdaItems(project).addToCollection(result, functionExpectedTypes)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
private fun calcItemsToSkip(expression: JetExpression): Collection<DeclarationDescriptor> {
|
||||
val parent = expression.getParent()
|
||||
when(parent) {
|
||||
is JetProperty -> {
|
||||
//TODO: this can be filtered out by ordinary completion
|
||||
if (expression == parent.getInitializer()) {
|
||||
return resolveSession.resolveToElement(parent)[BindingContext.DECLARATION_TO_DESCRIPTOR, parent].toList()
|
||||
}
|
||||
}
|
||||
|
||||
is JetBinaryExpression -> {
|
||||
if (parent.getRight() == expression && parent.getOperationToken() == JetTokens.EQ) {
|
||||
val left = parent.getLeft()
|
||||
if (left is JetReferenceExpression) {
|
||||
return resolveSession.resolveToElement(left)[BindingContext.REFERENCE_TARGET, left].toList()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return listOf()
|
||||
}
|
||||
|
||||
private fun toFunctionReferenceLookupElement(descriptor: DeclarationDescriptor,
|
||||
functionExpectedTypes: Collection<ExpectedTypeInfo>): LookupElement? {
|
||||
if (functionExpectedTypes.isEmpty()) return null
|
||||
|
||||
fun toLookupElement(descriptor: FunctionDescriptor): LookupElement? {
|
||||
val functionType = functionType(descriptor)
|
||||
if (functionType == null) return null
|
||||
|
||||
val matchedExpectedTypes = functionExpectedTypes.filter { functionType.isSubtypeOf(it.`type`) }
|
||||
if (matchedExpectedTypes.isEmpty()) return null
|
||||
|
||||
var lookupElement = DescriptorLookupConverter.createLookupElement(resolveSession, bindingContext, descriptor)
|
||||
val text = "::" + (if (descriptor is ConstructorDescriptor) descriptor.getContainingDeclaration().getName() else descriptor.getName())
|
||||
lookupElement = object: LookupElementDecorator<LookupElement>(lookupElement) {
|
||||
override fun getLookupString() = text
|
||||
|
||||
override fun renderElement(presentation: LookupElementPresentation) {
|
||||
super.renderElement(presentation)
|
||||
presentation.setItemText(text)
|
||||
presentation.setTypeText("")
|
||||
}
|
||||
|
||||
override fun handleInsert(context: InsertionContext) {
|
||||
}
|
||||
}
|
||||
|
||||
return addTailToLookupElement(lookupElement, matchedExpectedTypes)
|
||||
}
|
||||
|
||||
if (descriptor is SimpleFunctionDescriptor) {
|
||||
return toLookupElement(descriptor)
|
||||
}
|
||||
else if (descriptor is ClassDescriptor && descriptor.getModality() != Modality.ABSTRACT) {
|
||||
val constructors = descriptor.getConstructors().filter(visibilityFilter)
|
||||
if (constructors.size == 1) {
|
||||
//TODO: this code is to be changed if overloads to start work after ::
|
||||
return toLookupElement(constructors.single())
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
package org.jetbrains.jet.plugin.completion.smart
|
||||
|
||||
import com.intellij.codeInsight.lookup.LookupElement
|
||||
import org.jetbrains.jet.lang.types.TypeUtils
|
||||
import org.jetbrains.jet.lang.descriptors.ClassDescriptor
|
||||
import org.jetbrains.jet.lang.resolve.scopes.JetScope
|
||||
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptorWithVisibility
|
||||
import org.jetbrains.jet.lang.descriptors.Visibilities
|
||||
import org.jetbrains.jet.lang.descriptors.CallableDescriptor
|
||||
import org.jetbrains.jet.lang.descriptors.ClassKind
|
||||
import org.jetbrains.jet.lang.resolve.java.descriptor.JavaClassDescriptor
|
||||
import org.jetbrains.jet.plugin.completion.DescriptorLookupConverter
|
||||
import org.jetbrains.jet.lang.resolve.DescriptorUtils
|
||||
import org.jetbrains.jet.plugin.completion.handlers.CaretPosition
|
||||
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor
|
||||
import com.intellij.codeInsight.lookup.LookupElementDecorator
|
||||
import com.intellij.codeInsight.lookup.LookupElementPresentation
|
||||
import org.jetbrains.jet.renderer.DescriptorRenderer
|
||||
import com.intellij.codeInsight.completion.InsertionContext
|
||||
import org.jetbrains.jet.plugin.project.ResolveSessionForBodies
|
||||
import org.jetbrains.jet.lang.resolve.BindingContext
|
||||
import org.jetbrains.jet.lang.psi.JetExpression
|
||||
|
||||
// adds java static members, enum members and members from class object
|
||||
class StaticMembers(val bindingContext: BindingContext, val resolveSession: ResolveSessionForBodies) {
|
||||
public fun addToCollection(collection: MutableCollection<LookupElement>, expectedTypes: Collection<ExpectedTypeInfo>, context: JetExpression) {
|
||||
val scope = bindingContext[BindingContext.RESOLUTION_SCOPE, context]
|
||||
if (scope == null) return
|
||||
|
||||
val expectedTypesByClass = expectedTypes.groupBy { TypeUtils.getClassDescriptor(it.`type`) }
|
||||
for ((classDescriptor, expectedTypesForClass) in expectedTypesByClass) {
|
||||
if (classDescriptor != null && !classDescriptor.getName().isSpecial()) {
|
||||
addToCollection(collection, classDescriptor, expectedTypesForClass, scope)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun addToCollection(
|
||||
collection: MutableCollection<LookupElement>,
|
||||
classDescriptor: ClassDescriptor,
|
||||
expectedTypes: Collection<ExpectedTypeInfo>,
|
||||
scope: JetScope) {
|
||||
|
||||
fun processMember(descriptor: DeclarationDescriptor) {
|
||||
if (descriptor is DeclarationDescriptorWithVisibility && !Visibilities.isVisible(descriptor, scope.getContainingDeclaration())) return
|
||||
|
||||
val matchedExpectedTypes = expectedTypes.filter {
|
||||
expectedType ->
|
||||
descriptor is CallableDescriptor && descriptor.getReturnType()?.let { it.isSubtypeOf(expectedType.`type`) } ?: false
|
||||
|| descriptor is ClassDescriptor && descriptor.getKind() == ClassKind.ENUM_ENTRY
|
||||
}
|
||||
if (matchedExpectedTypes.isEmpty()) return
|
||||
|
||||
val lookupElement = createLookupElement(descriptor, classDescriptor)
|
||||
collection.add(addTailToLookupElement(lookupElement, matchedExpectedTypes))
|
||||
}
|
||||
|
||||
if (classDescriptor is JavaClassDescriptor) {
|
||||
val pseudoPackage = classDescriptor.getCorrespondingPackageFragment()
|
||||
if (pseudoPackage != null) {
|
||||
pseudoPackage.getMemberScope().getAllDescriptors().forEach(::processMember)
|
||||
}
|
||||
}
|
||||
|
||||
val classObject = classDescriptor.getClassObjectDescriptor()
|
||||
if (classObject != null) {
|
||||
classObject.getDefaultType().getMemberScope().getAllDescriptors().forEach(::processMember)
|
||||
}
|
||||
|
||||
if (classDescriptor.getKind() == ClassKind.ENUM_CLASS) {
|
||||
classDescriptor.getDefaultType().getMemberScope().getAllDescriptors().forEach(::processMember)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createLookupElement(memberDescriptor: DeclarationDescriptor, classDescriptor: ClassDescriptor): LookupElement {
|
||||
val lookupElement = DescriptorLookupConverter.createLookupElement(resolveSession, bindingContext, memberDescriptor)
|
||||
val qualifierPresentation = classDescriptor.getName().asString()
|
||||
val lookupString = qualifierPresentation + "." + lookupElement.getLookupString()
|
||||
val qualifierText = DescriptorUtils.getFqName(classDescriptor).asString() //TODO: escape keywords
|
||||
|
||||
val caretPosition: CaretPosition?
|
||||
if (memberDescriptor is FunctionDescriptor) {
|
||||
caretPosition = if (memberDescriptor.getValueParameters().empty) CaretPosition.AFTER_BRACKETS else CaretPosition.IN_BRACKETS
|
||||
}
|
||||
else {
|
||||
caretPosition = null
|
||||
}
|
||||
|
||||
return object: LookupElementDecorator<LookupElement>(lookupElement) {
|
||||
override fun getLookupString() = lookupString
|
||||
|
||||
override fun renderElement(presentation: LookupElementPresentation) {
|
||||
getDelegate().renderElement(presentation)
|
||||
|
||||
presentation.setItemText(qualifierPresentation + "." + presentation.getItemText())
|
||||
|
||||
val tailText = " (" + DescriptorUtils.getFqName(classDescriptor.getContainingDeclaration()) + ")"
|
||||
if (memberDescriptor is FunctionDescriptor) {
|
||||
presentation.appendTailText(tailText, true)
|
||||
}
|
||||
else {
|
||||
presentation.setTailText(tailText, true)
|
||||
}
|
||||
|
||||
if (presentation.getTypeText().isNullOrEmpty()) {
|
||||
presentation.setTypeText(DescriptorRenderer.TEXT.renderType(classDescriptor.getDefaultType()))
|
||||
}
|
||||
}
|
||||
|
||||
override fun handleInsert(context: InsertionContext) {
|
||||
val editor = context.getEditor()
|
||||
val startOffset = context.getStartOffset()
|
||||
var text = qualifierText + "." + memberDescriptor.getName().asString() //TODO: escape
|
||||
if (memberDescriptor is FunctionDescriptor) {
|
||||
text += "()"
|
||||
//TODO: auto-popup parameter info and other functionality from JetFunctionInsertHandler
|
||||
}
|
||||
|
||||
editor.getDocument().replaceString(startOffset, context.getTailOffset(), text)
|
||||
val endOffset = startOffset + text.length
|
||||
editor.getCaretModel().moveToOffset(if (caretPosition == CaretPosition.IN_BRACKETS) endOffset - 1 else endOffset)
|
||||
|
||||
shortenReferences(context, startOffset, startOffset + qualifierText.length)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package org.jetbrains.jet.plugin.completion.smart
|
||||
|
||||
import com.intellij.codeInsight.lookup.LookupElement
|
||||
import org.jetbrains.jet.lang.psi.JetExpression
|
||||
import org.jetbrains.jet.lang.descriptors.ReceiverParameterDescriptor
|
||||
import com.intellij.codeInsight.lookup.LookupElementBuilder
|
||||
import org.jetbrains.jet.renderer.DescriptorRenderer
|
||||
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.jet.lang.resolve.name.Name
|
||||
import org.jetbrains.jet.lang.resolve.BindingContextUtils
|
||||
import org.jetbrains.jet.lang.psi.JetFunctionLiteral
|
||||
import org.jetbrains.jet.lang.psi.JetFunctionLiteralExpression
|
||||
import org.jetbrains.jet.lang.psi.JetObjectDeclaration
|
||||
import org.jetbrains.jet.lang.psi.JetObjectLiteralExpression
|
||||
import org.jetbrains.jet.lang.psi.JetValueArgument
|
||||
import org.jetbrains.jet.lang.psi.JetValueArgumentList
|
||||
import org.jetbrains.jet.lang.psi.JetCallExpression
|
||||
import org.jetbrains.jet.lang.psi.JetSimpleNameExpression
|
||||
import org.jetbrains.jet.lang.resolve.BindingContext
|
||||
|
||||
class ThisItems(val bindingContext: BindingContext) {
|
||||
public fun addToCollection(collection: MutableCollection<LookupElement>, context: JetExpression, expectedTypes: Collection<ExpectedTypeInfo>) {
|
||||
val scope = bindingContext[BindingContext.RESOLUTION_SCOPE, context]
|
||||
if (scope == null) return
|
||||
|
||||
val receivers: List<ReceiverParameterDescriptor> = scope.getImplicitReceiversHierarchy()
|
||||
for (i in 0..receivers.size - 1) {
|
||||
val receiver = receivers[i]
|
||||
val thisType = receiver.getType()
|
||||
val matchedExpectedTypes = expectedTypes.filter { thisType.isSubtypeOf(it.`type`) }
|
||||
if (matchedExpectedTypes.notEmpty) {
|
||||
//TODO: use this code when KT-4258 fixed
|
||||
//val expressionText = if (i == 0) "this" else "this@" + (thisQualifierName(receiver, bindingContext) ?: continue)
|
||||
val qualifier = if (i == 0) null else thisQualifierName(receiver) ?: continue
|
||||
val expressionText = if (qualifier == null) "this" else "this@" + qualifier
|
||||
val lookupElement = LookupElementBuilder.create(expressionText).withTypeText(DescriptorRenderer.TEXT.renderType(thisType))
|
||||
collection.add(addTailToLookupElement(lookupElement, matchedExpectedTypes))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun thisQualifierName(receiver: ReceiverParameterDescriptor): String? {
|
||||
val descriptor: DeclarationDescriptor = receiver.getContainingDeclaration()
|
||||
val name: Name = descriptor.getName()
|
||||
if (!name.isSpecial()) return name.asString()
|
||||
|
||||
val psiElement = BindingContextUtils.descriptorToDeclaration(bindingContext, descriptor)
|
||||
val expression: JetExpression? = when (psiElement) {
|
||||
is JetFunctionLiteral -> psiElement.getParent() as? JetFunctionLiteralExpression
|
||||
is JetObjectDeclaration -> psiElement.getParent() as? JetObjectLiteralExpression
|
||||
else -> null
|
||||
}
|
||||
return ((((expression?.getParent() as? JetValueArgument)
|
||||
?.getParent() as? JetValueArgumentList)
|
||||
?.getParent() as? JetCallExpression)
|
||||
?.getCalleeExpression() as? JetSimpleNameExpression)
|
||||
?.getReferencedName()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
package org.jetbrains.jet.plugin.completion.smart
|
||||
|
||||
import com.intellij.codeInsight.lookup.LookupElement
|
||||
import org.jetbrains.jet.lang.types.JetType
|
||||
import org.jetbrains.jet.lang.types.TypeUtils
|
||||
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns
|
||||
import org.jetbrains.jet.lang.descriptors.ClassDescriptor
|
||||
import org.jetbrains.jet.plugin.completion.DescriptorLookupConverter
|
||||
import org.jetbrains.jet.renderer.DescriptorRenderer
|
||||
import com.intellij.codeInsight.completion.InsertHandler
|
||||
import org.jetbrains.jet.lang.resolve.DescriptorUtils
|
||||
import org.jetbrains.jet.lang.descriptors.Modality
|
||||
import org.jetbrains.jet.lang.descriptors.ClassKind
|
||||
import org.jetbrains.jet.plugin.codeInsight.ImplementMethodsHandler
|
||||
import org.jetbrains.jet.lang.descriptors.ConstructorDescriptor
|
||||
import org.jetbrains.jet.plugin.completion.handlers.CaretPosition
|
||||
import com.intellij.codeInsight.lookup.LookupElementDecorator
|
||||
import com.intellij.codeInsight.lookup.LookupElementPresentation
|
||||
import com.intellij.codeInsight.completion.InsertionContext
|
||||
import org.jetbrains.jet.lang.resolve.BindingContext
|
||||
import org.jetbrains.jet.plugin.project.ResolveSessionForBodies
|
||||
|
||||
class TypeInstantiationItems(val bindingContext: BindingContext, val resolveSession: ResolveSessionForBodies) {
|
||||
public fun addToCollection(collection: MutableCollection<LookupElement>, expectedTypes: Collection<ExpectedTypeInfo>) {
|
||||
val expectedTypesGrouped: Map<JetType, List<ExpectedTypeInfo>> = expectedTypes.groupBy { TypeUtils.makeNotNullable(it.`type`) }
|
||||
for ((jetType, types) in expectedTypesGrouped) {
|
||||
val tail = mergeTails(types.map { it.tail })
|
||||
addToCollection(collection, jetType, tail)
|
||||
}
|
||||
}
|
||||
|
||||
private fun addToCollection(collection: MutableCollection<LookupElement>, jetType: JetType, tail: Tail?) {
|
||||
if (KotlinBuiltIns.getInstance().isExactFunctionOrExtensionFunctionType(jetType)) return // do not show "object: ..." for function types
|
||||
|
||||
val classifier = jetType.getConstructor().getDeclarationDescriptor()
|
||||
if (!(classifier is ClassDescriptor)) return
|
||||
//TODO: check for constructor's visibility
|
||||
|
||||
var lookupElement = DescriptorLookupConverter.createLookupElement(resolveSession, bindingContext, classifier)
|
||||
|
||||
var lookupString = lookupElement.getLookupString()
|
||||
|
||||
val typeArgs = jetType.getArguments()
|
||||
var itemText = lookupString + DescriptorRenderer.TEXT.renderTypeArguments(typeArgs)
|
||||
|
||||
val insertHandler: InsertHandler<LookupElement>
|
||||
val typeText = DescriptorUtils.getFqName(classifier).toString() + DescriptorRenderer.SOURCE_CODE.renderTypeArguments(typeArgs)
|
||||
if (classifier.getModality() == Modality.ABSTRACT) {
|
||||
val constructorParenthesis = if (classifier.getKind() != ClassKind.TRAIT) "()" else ""
|
||||
itemText += constructorParenthesis
|
||||
itemText = "object: " + itemText + "{...}"
|
||||
lookupString = "object" //?
|
||||
insertHandler = InsertHandler<LookupElement> {(context, item) ->
|
||||
val editor = context.getEditor()
|
||||
val startOffset = context.getStartOffset()
|
||||
val text = "object: $typeText$constructorParenthesis {}"
|
||||
editor.getDocument().replaceString(startOffset, context.getTailOffset(), text)
|
||||
editor.getCaretModel().moveToOffset(startOffset + text.length - 1)
|
||||
|
||||
shortenReferences(context, startOffset, startOffset + text.length)
|
||||
|
||||
ImplementMethodsHandler().invoke(context.getProject(), editor, context.getFile(), true)
|
||||
}
|
||||
lookupElement = lookupElement.suppressAutoInsertion()
|
||||
}
|
||||
else {
|
||||
itemText += "()"
|
||||
val constructors: Collection<ConstructorDescriptor> = classifier.getConstructors()
|
||||
val caretPosition =
|
||||
if (constructors.size == 0)
|
||||
CaretPosition.AFTER_BRACKETS
|
||||
else if (constructors.size == 1)
|
||||
if (constructors.first().getValueParameters().isEmpty()) CaretPosition.AFTER_BRACKETS else CaretPosition.IN_BRACKETS
|
||||
else
|
||||
CaretPosition.IN_BRACKETS
|
||||
insertHandler = InsertHandler<LookupElement> {(context, item) ->
|
||||
val editor = context.getEditor()
|
||||
val startOffset = context.getStartOffset()
|
||||
val text = typeText + "()"
|
||||
editor.getDocument().replaceString(startOffset, context.getTailOffset(), text)
|
||||
val endOffset = startOffset + text.length
|
||||
editor.getCaretModel().moveToOffset(if (caretPosition == CaretPosition.IN_BRACKETS) endOffset - 1 else endOffset)
|
||||
|
||||
//TODO: auto-popup parameter info and other functionality from JetFunctionInsertHandler
|
||||
|
||||
shortenReferences(context, startOffset, endOffset)
|
||||
}
|
||||
}
|
||||
|
||||
lookupElement = object: LookupElementDecorator<LookupElement>(lookupElement) {
|
||||
override fun getLookupString() = lookupString
|
||||
|
||||
override fun renderElement(presentation: LookupElementPresentation) {
|
||||
getDelegate().renderElement(presentation)
|
||||
presentation.setItemText(itemText)
|
||||
}
|
||||
|
||||
override fun handleInsert(context: InsertionContext) {
|
||||
insertHandler.handleInsert(context, getDelegate())
|
||||
}
|
||||
}
|
||||
|
||||
collection.add(addTailToLookupElement(lookupElement, tail))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
package org.jetbrains.jet.plugin.completion.smart
|
||||
|
||||
import org.jetbrains.jet.lang.psi.JetExpression
|
||||
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.jet.lang.types.JetType
|
||||
import org.jetbrains.jet.lang.descriptors.VariableDescriptor
|
||||
import org.jetbrains.jet.lang.descriptors.CallableDescriptor
|
||||
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns
|
||||
import org.jetbrains.jet.lang.types.TypeUtils
|
||||
import org.jetbrains.jet.lang.descriptors.ClassDescriptor
|
||||
import org.jetbrains.jet.lang.descriptors.ClassKind
|
||||
import java.util.Collections
|
||||
import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo
|
||||
import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowValue
|
||||
import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowValueFactory
|
||||
import java.util.HashMap
|
||||
import com.google.common.collect.SetMultimap
|
||||
import org.jetbrains.jet.lang.resolve.calls.autocasts.Nullability
|
||||
import java.util.HashSet
|
||||
import org.jetbrains.jet.lang.resolve.BindingContext
|
||||
|
||||
class TypesWithAutoCasts(val bindingContext: BindingContext) {
|
||||
public fun calculate(expression: JetExpression, receiver: JetExpression?): (DeclarationDescriptor) -> Iterable<JetType> {
|
||||
val dataFlowInfo = bindingContext[BindingContext.EXPRESSION_DATA_FLOW_INFO, expression]
|
||||
val (variableToTypes: Map<VariableDescriptor, Collection<JetType>>, notNullVariables: Set<VariableDescriptor>)
|
||||
= processDataFlowInfo(dataFlowInfo, receiver)
|
||||
|
||||
fun typesOf(descriptor: DeclarationDescriptor): Iterable<JetType> {
|
||||
if (descriptor is CallableDescriptor) {
|
||||
var returnType = descriptor.getReturnType()
|
||||
if (returnType != null && KotlinBuiltIns.getInstance().isNothing(returnType!!)) {
|
||||
//TODO: maybe we should include them on the second press?
|
||||
return listOf()
|
||||
}
|
||||
if (descriptor is VariableDescriptor) {
|
||||
if (notNullVariables.contains(descriptor) && returnType != null) {
|
||||
returnType = TypeUtils.makeNotNullable(returnType!!)
|
||||
}
|
||||
|
||||
val autoCastTypes = variableToTypes[descriptor]
|
||||
if (autoCastTypes != null && !autoCastTypes.isEmpty()) {
|
||||
return autoCastTypes + returnType.toList()
|
||||
}
|
||||
}
|
||||
return returnType.toList()
|
||||
}
|
||||
else if (descriptor is ClassDescriptor && descriptor.getKind() == ClassKind.ENUM_ENTRY) {
|
||||
return listOf(descriptor.getDefaultType())
|
||||
}
|
||||
else {
|
||||
return listOf()
|
||||
}
|
||||
}
|
||||
|
||||
return ::typesOf
|
||||
}
|
||||
|
||||
private data class ProcessDataFlowInfoResult(
|
||||
val variableToTypes: Map<VariableDescriptor, Collection<JetType>> = Collections.emptyMap(),
|
||||
val notNullVariables: Set<VariableDescriptor> = Collections.emptySet()
|
||||
)
|
||||
|
||||
private fun processDataFlowInfo(dataFlowInfo: DataFlowInfo?, receiver: JetExpression?): ProcessDataFlowInfoResult {
|
||||
if (dataFlowInfo != null) {
|
||||
val dataFlowValueToVariable: (DataFlowValue) -> VariableDescriptor?
|
||||
if (receiver != null) {
|
||||
val receiverType = bindingContext[BindingContext.EXPRESSION_TYPE, receiver]
|
||||
if (receiverType != null) {
|
||||
val receiverId = DataFlowValueFactory.createDataFlowValue(receiver, receiverType, bindingContext).getId()
|
||||
dataFlowValueToVariable = {(value) ->
|
||||
val id = value.getId()
|
||||
if (id is com.intellij.openapi.util.Pair<*, *> && id.first == receiverId) id.second as? VariableDescriptor else null
|
||||
}
|
||||
}
|
||||
else {
|
||||
return ProcessDataFlowInfoResult()
|
||||
}
|
||||
}
|
||||
else {
|
||||
dataFlowValueToVariable = {(value) -> value.getId() as? VariableDescriptor }
|
||||
}
|
||||
|
||||
val variableToType = HashMap<VariableDescriptor, Collection<JetType>>()
|
||||
val typeInfo: SetMultimap<DataFlowValue, JetType> = dataFlowInfo.getCompleteTypeInfo()
|
||||
for ((dataFlowValue, types) in typeInfo.asMap().entrySet()) {
|
||||
val variable = dataFlowValueToVariable.invoke(dataFlowValue)
|
||||
if (variable != null) {
|
||||
variableToType[variable] = types
|
||||
}
|
||||
}
|
||||
|
||||
val nullabilityInfo: Map<DataFlowValue, Nullability> = dataFlowInfo.getCompleteNullabilityInfo()
|
||||
val notNullVariables = nullabilityInfo
|
||||
.filter { it.getValue() == Nullability.NOT_NULL }
|
||||
.map { dataFlowValueToVariable(it.getKey()) }
|
||||
.filterNotNullTo(HashSet<VariableDescriptor>())
|
||||
|
||||
return ProcessDataFlowInfoResult(variableToType, notNullVariables)
|
||||
}
|
||||
|
||||
return ProcessDataFlowInfoResult()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package org.jetbrains.jet.plugin.completion.smart
|
||||
|
||||
import com.intellij.codeInsight.completion.InsertHandler
|
||||
import com.intellij.codeInsight.lookup.LookupElement
|
||||
import com.intellij.codeInsight.completion.InsertionContext
|
||||
import com.intellij.psi.PsiDocumentManager
|
||||
import org.jetbrains.jet.lang.psi.JetFile
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import org.jetbrains.jet.lang.psi.JetElement
|
||||
import org.jetbrains.jet.plugin.codeInsight.ShortenReferences
|
||||
import java.util.HashSet
|
||||
import com.intellij.codeInsight.lookup.LookupElementDecorator
|
||||
import org.jetbrains.jet.plugin.completion.handlers.WithTailInsertHandler
|
||||
import com.intellij.codeInsight.lookup.AutoCompletionPolicy
|
||||
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.jet.lang.types.JetType
|
||||
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns
|
||||
import org.jetbrains.jet.lang.types.checker.JetTypeChecker
|
||||
|
||||
class ArtificialElementInsertHandler(
|
||||
val textBeforeCaret: String, val textAfterCaret: String, val shortenRefs: Boolean) : InsertHandler<LookupElement>{
|
||||
override fun handleInsert(context: InsertionContext, item: LookupElement) {
|
||||
val offset = context.getEditor().getCaretModel().getOffset()
|
||||
val startOffset = offset - item.getLookupString().length
|
||||
context.getDocument().deleteString(startOffset, offset) // delete inserted lookup string
|
||||
context.getDocument().insertString(startOffset, textBeforeCaret + textAfterCaret)
|
||||
context.getEditor().getCaretModel().moveToOffset(startOffset + textBeforeCaret.length)
|
||||
|
||||
if (shortenRefs) {
|
||||
shortenReferences(context, startOffset, startOffset + textBeforeCaret.length + textAfterCaret.length)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun shortenReferences(context: InsertionContext, startOffset: Int, endOffset: Int) {
|
||||
PsiDocumentManager.getInstance(context.getProject()).commitAllDocuments();
|
||||
val file = context.getFile() as JetFile
|
||||
val element = PsiTreeUtil.findElementOfClassAtRange(file, startOffset, endOffset, javaClass<JetElement>())
|
||||
if (element != null) {
|
||||
ShortenReferences.process(element)
|
||||
}
|
||||
}
|
||||
|
||||
fun mergeTails(tails: Collection<Tail?>): Tail? {
|
||||
if (tails.size == 1) return tails.single()
|
||||
return if (HashSet(tails).size == 1) tails.first() else null
|
||||
}
|
||||
|
||||
fun addTailToLookupElement(lookupElement: LookupElement, tail: Tail?): LookupElement {
|
||||
return when (tail) {
|
||||
null -> lookupElement
|
||||
|
||||
Tail.COMMA -> object: LookupElementDecorator<LookupElement>(lookupElement) {
|
||||
override fun handleInsert(context: InsertionContext) {
|
||||
WithTailInsertHandler(',', true /*TODO: use code style option*/).handleInsert(context, lookupElement)
|
||||
}
|
||||
}
|
||||
|
||||
Tail.PARENTHESIS -> object: LookupElementDecorator<LookupElement>(lookupElement) {
|
||||
override fun handleInsert(context: InsertionContext) {
|
||||
WithTailInsertHandler(')', false).handleInsert(context, lookupElement)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun addTailToLookupElement(lookupElement: LookupElement, matchedExpectedTypes: Collection<ExpectedTypeInfo>): LookupElement
|
||||
= addTailToLookupElement(lookupElement, mergeTails(matchedExpectedTypes.map { it.tail }))
|
||||
|
||||
fun LookupElement.suppressAutoInsertion() = AutoCompletionPolicy.NEVER_AUTOCOMPLETE.applyPolicy(this)
|
||||
|
||||
fun functionType(function: FunctionDescriptor): JetType? {
|
||||
return KotlinBuiltIns.getInstance().getKFunctionType(function.getAnnotations(),
|
||||
null,
|
||||
function.getValueParameters().map { it.getType() },
|
||||
function.getReturnType() ?: return null,
|
||||
function.getReceiverParameter() != null)
|
||||
}
|
||||
|
||||
fun JetType.isSubtypeOf(expectedType: JetType) = !isError() && JetTypeChecker.INSTANCE.isSubtypeOf(this, expectedType)
|
||||
|
||||
fun <T : Any> T?.toList(): List<T> = if (this != null) listOf(this) else listOf()
|
||||
|
||||
fun String?.isNullOrEmpty() = this == null || this.isEmpty()
|
||||
Reference in New Issue
Block a user