Create Function From Usage: Refactor the function builder
This commit is contained in:
+4
-4
@@ -22,17 +22,17 @@ object CreateComponentFunctionActionFactory : JetSingleIntentionActionFactory()
|
|||||||
val ownerType = if (multiDeclaration == null) {
|
val ownerType = if (multiDeclaration == null) {
|
||||||
val forExpr = QuickFixUtil.getParentElementOfType(diagnostic, javaClass<JetForExpression>())!!
|
val forExpr = QuickFixUtil.getParentElementOfType(diagnostic, javaClass<JetForExpression>())!!
|
||||||
multiDeclaration = forExpr.getMultiParameter()!!
|
multiDeclaration = forExpr.getMultiParameter()!!
|
||||||
TypeOrExpressionThereof(diagnosticWithParameters.getB(), Variance.IN_VARIANCE)
|
TypeInfo(diagnosticWithParameters.getB(), Variance.IN_VARIANCE)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
val rhs = multiDeclaration!!.getInitializer() ?: return null
|
val rhs = multiDeclaration!!.getInitializer() ?: return null
|
||||||
TypeOrExpressionThereof(rhs, Variance.IN_VARIANCE)
|
TypeInfo(rhs, Variance.IN_VARIANCE)
|
||||||
}
|
}
|
||||||
val entries = multiDeclaration!!.getEntries()
|
val entries = multiDeclaration!!.getEntries()
|
||||||
|
|
||||||
val entry = entries[componentNumber]
|
val entry = entries[componentNumber]
|
||||||
val returnType = TypeOrExpressionThereof(entry, Variance.OUT_VARIANCE)
|
val returnType = TypeInfo(entry, Variance.OUT_VARIANCE)
|
||||||
|
|
||||||
return CreateFunctionFromUsageFix(multiDeclaration!!, ownerType, name.getIdentifier(), returnType)
|
return CreateFunctionFromUsageFix(multiDeclaration!!, FunctionInfo(name.getIdentifier(), ownerType, returnType))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+30
-651
@@ -1,333 +1,34 @@
|
|||||||
/*
|
|
||||||
* Copyright 2010-2014 JetBrains s.r.o.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.jetbrains.jet.plugin.quickfix.createFromUsage.createFunction
|
package org.jetbrains.jet.plugin.quickfix.createFromUsage.createFunction
|
||||||
|
|
||||||
import com.intellij.codeInsight.lookup.LookupElement
|
|
||||||
import com.intellij.codeInsight.lookup.LookupElementBuilder
|
|
||||||
import com.intellij.codeInsight.navigation.NavigationUtil
|
|
||||||
import com.intellij.codeInsight.template.*
|
|
||||||
import com.intellij.codeInsight.template.impl.TemplateImpl
|
|
||||||
import com.intellij.codeInsight.template.impl.Variable
|
|
||||||
import com.intellij.ide.fileTemplates.FileTemplate
|
|
||||||
import com.intellij.ide.fileTemplates.FileTemplateManager
|
|
||||||
import com.intellij.openapi.application.ApplicationManager
|
|
||||||
import com.intellij.openapi.command.CommandProcessor
|
|
||||||
import com.intellij.openapi.editor.Editor
|
|
||||||
import com.intellij.openapi.fileEditor.FileEditorManager
|
|
||||||
import com.intellij.openapi.progress.ProcessCanceledException
|
|
||||||
import com.intellij.openapi.project.Project
|
|
||||||
import com.intellij.openapi.ui.popup.PopupChooserBuilder
|
|
||||||
import com.intellij.openapi.util.TextRange
|
|
||||||
import com.intellij.psi.PsiDocumentManager
|
|
||||||
import com.intellij.psi.PsiElement
|
import com.intellij.psi.PsiElement
|
||||||
import com.intellij.psi.util.PsiTreeUtil
|
|
||||||
import com.intellij.refactoring.psi.SearchUtils
|
|
||||||
import com.intellij.ui.components.JBList
|
|
||||||
import com.intellij.util.ArrayUtil
|
|
||||||
import com.intellij.util.IncorrectOperationException
|
|
||||||
import org.jetbrains.jet.lang.descriptors.*
|
|
||||||
import org.jetbrains.jet.lang.psi.*
|
|
||||||
import org.jetbrains.jet.lang.resolve.BindingContext
|
|
||||||
import org.jetbrains.jet.lang.resolve.DescriptorUtils
|
|
||||||
import org.jetbrains.jet.lang.resolve.name.Name
|
|
||||||
import org.jetbrains.jet.lang.resolve.scopes.JetScope
|
|
||||||
import org.jetbrains.jet.lang.types.*
|
|
||||||
import org.jetbrains.jet.lang.types.checker.JetTypeChecker
|
|
||||||
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns
|
|
||||||
import org.jetbrains.jet.plugin.JetBundle
|
|
||||||
import org.jetbrains.jet.plugin.codeInsight.DescriptorToDeclarationUtil
|
|
||||||
import org.jetbrains.jet.plugin.codeInsight.ShortenReferences
|
|
||||||
import org.jetbrains.jet.plugin.refactoring.JetNameSuggester
|
|
||||||
import org.jetbrains.jet.plugin.refactoring.JetNameValidator
|
|
||||||
import org.jetbrains.jet.plugin.references.JetSimpleNameReference
|
|
||||||
import javax.swing.*
|
|
||||||
import kotlin.properties.Delegates
|
|
||||||
import java.util.LinkedHashSet
|
|
||||||
import java.util.Collections
|
|
||||||
import java.util.HashSet
|
|
||||||
import java.util.HashMap
|
|
||||||
import java.util.ArrayList
|
|
||||||
import java.util.Properties
|
|
||||||
import org.jetbrains.jet.plugin.caches.resolve.getAnalysisResults
|
|
||||||
import org.jetbrains.jet.lang.resolve.DescriptorToSourceUtils
|
|
||||||
import org.jetbrains.jet.plugin.quickfix.QuickFixUtil
|
|
||||||
import org.jetbrains.jet.plugin.quickfix.createFromUsage.CreateFromUsageFixBase
|
import org.jetbrains.jet.plugin.quickfix.createFromUsage.CreateFromUsageFixBase
|
||||||
import org.jetbrains.jet.plugin.refactoring.EmptyValidator
|
|
||||||
import org.jetbrains.jet.plugin.refactoring.CollectingValidator
|
|
||||||
import com.intellij.ide.util.PsiElementListCellRenderer
|
import com.intellij.ide.util.PsiElementListCellRenderer
|
||||||
|
import org.jetbrains.jet.lang.psi.JetClass
|
||||||
import org.jetbrains.jet.plugin.presentation.JetClassPresenter
|
import org.jetbrains.jet.plugin.presentation.JetClassPresenter
|
||||||
|
import javax.swing.JList
|
||||||
import java.awt.Component
|
import java.awt.Component
|
||||||
|
import org.jetbrains.jet.lang.psi.JetFile
|
||||||
|
import com.intellij.openapi.editor.Editor
|
||||||
|
import org.jetbrains.jet.plugin.JetBundle
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.openapi.application.ApplicationManager
|
||||||
|
import com.intellij.ui.components.JBList
|
||||||
|
import javax.swing.ListSelectionModel
|
||||||
|
import com.intellij.openapi.ui.popup.PopupChooserBuilder
|
||||||
|
import com.intellij.openapi.command.CommandProcessor
|
||||||
|
import org.jetbrains.jet.lang.resolve.DescriptorUtils
|
||||||
|
import org.jetbrains.jet.plugin.codeInsight.DescriptorToDeclarationUtil
|
||||||
|
|
||||||
private val TYPE_PARAMETER_LIST_VARIABLE_NAME = "typeParameterList"
|
public class CreateFunctionFromUsageFix(element: PsiElement, val functionInfo: FunctionInfo) : CreateFromUsageFixBase(element) {
|
||||||
private val TEMPLATE_FROM_USAGE_FUNCTION_BODY = "New Kotlin Function Body.kt"
|
/**
|
||||||
private val ATTRIBUTE_FUNCTION_NAME = "FUNCTION_NAME"
|
* Represents an element in the class selection list.
|
||||||
|
*/
|
||||||
/**
|
private class ClassCandidate(val typeCandidate: TypeCandidate, file: JetFile) {
|
||||||
* Represents a single choice for a type (e.g. parameter type or return type).
|
val jetClass: JetClass = DescriptorToDeclarationUtil.getDeclaration(
|
||||||
*/
|
file, DescriptorUtils.getClassDescriptorForType(typeCandidate.theType)
|
||||||
private class TypeCandidate(public val theType: JetType, scope: JetScope? = null) {
|
) as JetClass
|
||||||
public val typeParameters: Array<TypeParameterDescriptor>
|
|
||||||
public var renderedType: String? = null
|
|
||||||
private set
|
|
||||||
public var typeParameterNames: Array<String>? = null
|
|
||||||
private set
|
|
||||||
|
|
||||||
public fun render(typeParameterNameMap: Map<TypeParameterDescriptor, String>) {
|
|
||||||
renderedType = theType.renderShort(typeParameterNameMap);
|
|
||||||
typeParameterNames = typeParameters.map { typeParameterNameMap[it]!! }.copyToArray()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
|
||||||
val typeParametersInType = theType.getTypeParameters()
|
|
||||||
if (scope == null) {
|
|
||||||
typeParameters = typeParametersInType.copyToArray()
|
|
||||||
renderedType = theType.renderShort(Collections.emptyMap());
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
typeParameters = getTypeParameterNamesNotInScope(typeParametersInType, scope).copyToArray();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents an element in the class selection list.
|
|
||||||
*/
|
|
||||||
private class ClassCandidate(public val typeCandidate: TypeCandidate, file: JetFile) {
|
|
||||||
public val jetClass: JetClass = DescriptorToDeclarationUtil.getDeclaration(
|
|
||||||
file, DescriptorUtils.getClassDescriptorForType(typeCandidate.theType)
|
|
||||||
) as JetClass
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents a concrete type or a set of types yet to be inferred from an expression.
|
|
||||||
*/
|
|
||||||
private fun TypeOrExpressionThereof(expressionOfType: JetExpression, variance: Variance): TypeOrExpressionThereof =
|
|
||||||
TypeOrExpressionThereof(variance, expressionOfType, null)
|
|
||||||
private fun TypeOrExpressionThereof(theType: JetType, variance: Variance): TypeOrExpressionThereof =
|
|
||||||
TypeOrExpressionThereof(variance, null, theType)
|
|
||||||
|
|
||||||
private class TypeOrExpressionThereof(private val variance: Variance,
|
|
||||||
public val expressionOfType: JetExpression?,
|
|
||||||
public val theType: JetType?) {
|
|
||||||
public var typeCandidates: List<TypeCandidate>? = null
|
|
||||||
private set
|
|
||||||
|
|
||||||
public val possibleNamesFromExpression: Array<String> by Delegates.lazy {
|
|
||||||
if (isType()) {
|
|
||||||
ArrayUtil.EMPTY_STRING_ARRAY
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
JetNameSuggester.suggestNamesForExpression(expressionOfType!!, EmptyValidator)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public fun isType(): Boolean = (theType != null)
|
|
||||||
|
|
||||||
private fun getPossibleTypes(context: BindingContext): List<JetType> {
|
|
||||||
val types = ArrayList<JetType>()
|
|
||||||
if (theType != null) {
|
|
||||||
types.add(theType)
|
|
||||||
if (variance == Variance.IN_VARIANCE) {
|
|
||||||
types.addAll(TypeUtils.getAllSupertypes(theType))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
for (theType in expressionOfType!!.guessTypes(context)) {
|
|
||||||
types.add(theType)
|
|
||||||
if (variance == Variance.IN_VARIANCE) {
|
|
||||||
types.addAll(TypeUtils.getAllSupertypes(theType))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return types
|
|
||||||
}
|
|
||||||
|
|
||||||
public fun computeTypeCandidates(context: BindingContext) {
|
|
||||||
typeCandidates = getPossibleTypes(context).map { TypeCandidate(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
public fun computeTypeCandidates(context: BindingContext, substitutions: Array<JetTypeSubstitution>, scope: JetScope) {
|
|
||||||
val types = getPossibleTypes(context).reverse()
|
|
||||||
|
|
||||||
val newTypes = LinkedHashSet<JetType>(types)
|
|
||||||
for (substitution in substitutions) {
|
|
||||||
// each substitution can be applied or not, so we offer all options
|
|
||||||
val toAdd = newTypes.map { theType -> theType.substitute(substitution, variance) }
|
|
||||||
// substitution.byType are type arguments, but they cannot already occur in the type before substitution
|
|
||||||
val toRemove = newTypes.filter { theType -> substitution.byType in theType }
|
|
||||||
|
|
||||||
newTypes.addAll(toAdd)
|
|
||||||
newTypes.removeAll(toRemove)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newTypes.empty) {
|
|
||||||
newTypes.add(KotlinBuiltIns.getInstance().getAnyType())
|
|
||||||
}
|
|
||||||
|
|
||||||
typeCandidates = newTypes.map { TypeCandidate(it, scope) }.reverse()
|
|
||||||
}
|
|
||||||
|
|
||||||
public fun renderTypeCandidates(typeParameterNameMap: Map<TypeParameterDescriptor, String>) {
|
|
||||||
typeCandidates!!.forEach { it.render(typeParameterNameMap) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encapsulates information about a function parameter that is going to be created.
|
|
||||||
*/
|
|
||||||
private class Parameter(
|
|
||||||
public val theType: TypeOrExpressionThereof,
|
|
||||||
public val preferredName: String? = null
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Special <code>Expression</code> for parameter names based on its type.
|
|
||||||
*/
|
|
||||||
private class ParameterNameExpression(
|
|
||||||
private val names: Array<String>,
|
|
||||||
private val parameterTypeToNamesMap: Map<String, Array<String>>) : Expression() {
|
|
||||||
{
|
|
||||||
assert(names all { it.isNotEmpty() })
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun calculateResult(context: ExpressionContext?): Result? {
|
|
||||||
val lookupItems = calculateLookupItems(context)!!
|
|
||||||
return TextResult(if (lookupItems.isEmpty()) "" else lookupItems.first().getLookupString())
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun calculateQuickResult(context: ExpressionContext?) = calculateResult(context)
|
|
||||||
|
|
||||||
override fun calculateLookupItems(context: ExpressionContext?): Array<LookupElement>? {
|
|
||||||
context!!
|
|
||||||
val names = LinkedHashSet<String>(this.names.toList())
|
|
||||||
|
|
||||||
// find the parameter list
|
|
||||||
val project = context.getProject()!!
|
|
||||||
val offset = context.getStartOffset()
|
|
||||||
PsiDocumentManager.getInstance(project).commitAllDocuments()
|
|
||||||
val editor = context.getEditor()!!
|
|
||||||
val file = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument()) as JetFile
|
|
||||||
val elementAt = file.findElementAt(offset)
|
|
||||||
val func = PsiTreeUtil.getParentOfType(elementAt, javaClass<JetFunction>()) ?: return array<LookupElement>()
|
|
||||||
val parameterList = func.getValueParameterList()!!
|
|
||||||
|
|
||||||
// add names based on selected type
|
|
||||||
val parameter = PsiTreeUtil.getParentOfType(elementAt, javaClass<JetParameter>())
|
|
||||||
if (parameter != null) {
|
|
||||||
val parameterTypeRef = parameter.getTypeReference()
|
|
||||||
if (parameterTypeRef != null) {
|
|
||||||
val suggestedNamesBasedOnType = parameterTypeToNamesMap[parameterTypeRef.getText()]
|
|
||||||
if (suggestedNamesBasedOnType != null) {
|
|
||||||
names.addAll(suggestedNamesBasedOnType)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// remember other parameter names for later use
|
|
||||||
val parameterNames = parameterList.getParameters().stream().map { jetParameter ->
|
|
||||||
if (jetParameter == parameter) null else jetParameter.getName()
|
|
||||||
}.filterNotNullTo(HashSet<String>())
|
|
||||||
|
|
||||||
// add fallback parameter name
|
|
||||||
if (names.isEmpty()) {
|
|
||||||
names.add("arg")
|
|
||||||
}
|
|
||||||
|
|
||||||
// ensure there are no conflicts
|
|
||||||
val validator = CollectingValidator(parameterNames)
|
|
||||||
return names.map { LookupElementBuilder.create(validator.validateName(it)) }.copyToArray()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An <code>Expression</code> for type references.
|
|
||||||
*/
|
|
||||||
private class TypeExpression(public val theType: TypeOrExpressionThereof) : Expression() {
|
|
||||||
private val cachedLookupElements: Array<LookupElement> = theType.typeCandidates!!.map {
|
|
||||||
LookupElementBuilder.create(it, it.renderedType!!)
|
|
||||||
}.copyToArray()
|
|
||||||
|
|
||||||
override fun calculateResult(context: ExpressionContext?): Result {
|
|
||||||
val lookupItems = calculateLookupItems(context)
|
|
||||||
return TextResult(if (lookupItems.size == 0) "" else lookupItems[0].getLookupString())
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun calculateQuickResult(context: ExpressionContext?) = calculateResult(context)
|
|
||||||
|
|
||||||
override fun calculateLookupItems(context: ExpressionContext?) = cachedLookupElements
|
|
||||||
|
|
||||||
public fun getTypeFromSelection(selection: String): JetType? {
|
|
||||||
return theType.typeCandidates!!.firstOrNull { it.renderedType == selection }?.theType
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A sort-of dummy <code>Expression</code> for parameter lists, to allow us to update the parameter list as the user makes selections.
|
|
||||||
*/
|
|
||||||
private class TypeParameterListExpression(private val typeParameterNamesFromReceiverType: Array<String>,
|
|
||||||
private val parameterTypeToTypeParameterNamesMap: Map<String, Array<String>>) : Expression() {
|
|
||||||
|
|
||||||
override fun calculateResult(context: ExpressionContext?): Result {
|
|
||||||
context!!
|
|
||||||
val project = context.getProject()!!
|
|
||||||
val offset = context.getStartOffset()
|
|
||||||
|
|
||||||
PsiDocumentManager.getInstance(project).commitAllDocuments()
|
|
||||||
val editor = context.getEditor()!!
|
|
||||||
val file = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument()) as JetFile
|
|
||||||
val elementAt = file.findElementAt(offset)
|
|
||||||
val func = PsiTreeUtil.getParentOfType(elementAt, javaClass<JetFunction>()) ?: return TextResult("")
|
|
||||||
val parameters = func.getValueParameters()
|
|
||||||
|
|
||||||
val typeParameterNames = LinkedHashSet<String>()
|
|
||||||
typeParameterNames.addAll(typeParameterNamesFromReceiverType)
|
|
||||||
for (parameter in parameters) {
|
|
||||||
val parameterTypeRef = parameter.getTypeReference()
|
|
||||||
if (parameterTypeRef != null) {
|
|
||||||
val typeParameterNamesFromParameter = parameterTypeToTypeParameterNamesMap[parameterTypeRef.getText()]
|
|
||||||
if (typeParameterNamesFromParameter != null) {
|
|
||||||
typeParameterNames.addAll(typeParameterNamesFromParameter)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val returnTypeRef = func.getReturnTypeRef()
|
|
||||||
if (returnTypeRef != null) {
|
|
||||||
val typeParameterNamesFromReturnType = parameterTypeToTypeParameterNamesMap[returnTypeRef.getText()]
|
|
||||||
if (typeParameterNamesFromReturnType != null) {
|
|
||||||
typeParameterNames.addAll(typeParameterNamesFromReturnType)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return TextResult(if (typeParameterNames.empty) "" else typeParameterNames.makeString(", ", " <", ">"))
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun calculateQuickResult(context: ExpressionContext?): Result = calculateResult(context)
|
|
||||||
|
|
||||||
// do not offer the user any choices
|
|
||||||
override fun calculateLookupItems(context: ExpressionContext?) = array<LookupElement>()
|
|
||||||
}
|
|
||||||
|
|
||||||
public class CreateFunctionFromUsageFix internal (
|
|
||||||
element: PsiElement,
|
|
||||||
private val ownerType: TypeOrExpressionThereof,
|
|
||||||
private val functionName: String,
|
|
||||||
private val returnType: TypeOrExpressionThereof,
|
|
||||||
private val parameters: List<Parameter> = Collections.emptyList()
|
|
||||||
) : CreateFromUsageFixBase(element) {
|
|
||||||
private class ClassCandidateListCellRenderer : PsiElementListCellRenderer<JetClass>() {
|
private class ClassCandidateListCellRenderer : PsiElementListCellRenderer<JetClass>() {
|
||||||
private val presenter = JetClassPresenter()
|
private val presenter = JetClassPresenter()
|
||||||
|
|
||||||
@@ -345,42 +46,23 @@ public class CreateFunctionFromUsageFix internal (
|
|||||||
super.getListCellRendererComponent(list, (value as ClassCandidate).jetClass, index, isSelected, cellHasFocus)
|
super.getListCellRendererComponent(list, (value as ClassCandidate).jetClass, index, isSelected, cellHasFocus)
|
||||||
}
|
}
|
||||||
|
|
||||||
private var isUnit: Boolean = false
|
|
||||||
private var isExtension: Boolean = false
|
|
||||||
private var currentFile: JetFile by Delegates.notNull()
|
|
||||||
private var containingFile: JetFile by Delegates.notNull()
|
|
||||||
private var currentFileEditor: Editor by Delegates.notNull()
|
|
||||||
private var containingFileEditor: Editor by Delegates.notNull()
|
|
||||||
private var currentFileContext: BindingContext by Delegates.notNull()
|
|
||||||
private var currentFileModule: ModuleDescriptor by Delegates.notNull()
|
|
||||||
private var ownerClass: JetClass by Delegates.notNull()
|
|
||||||
private var ownerClassDescriptor: ClassDescriptor by Delegates.notNull()
|
|
||||||
private var selectedReceiverType: TypeCandidate by Delegates.notNull()
|
|
||||||
private var typeParameterNameMap: Map<TypeParameterDescriptor, String> by Delegates.notNull()
|
|
||||||
|
|
||||||
override fun getText(): String {
|
override fun getText(): String {
|
||||||
return JetBundle.message("create.function.from.usage", functionName)
|
return JetBundle.message("create.function.from.usage", functionInfo.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun invoke(project: Project, editor: Editor?, file: JetFile?) {
|
override fun invoke(project: Project, editor: Editor?, file: JetFile?) {
|
||||||
currentFile = file!!
|
val functionBuilder = FunctionBuilderConfiguration(functionInfo, file!!, editor!!).createBuilder()
|
||||||
currentFileEditor = editor!!
|
|
||||||
|
|
||||||
val exhaust = currentFile.getAnalysisResults()
|
val ownerTypeCandidates = functionBuilder.computeTypeCandidates(functionInfo.receiverTypeInfo)
|
||||||
currentFileContext = exhaust.getBindingContext()
|
|
||||||
currentFileModule = exhaust.getModuleDescriptor()
|
|
||||||
|
|
||||||
ownerType.computeTypeCandidates(currentFileContext)
|
|
||||||
val ownerTypeCandidates = ownerType.typeCandidates!!
|
|
||||||
assert(!ownerTypeCandidates.empty)
|
assert(!ownerTypeCandidates.empty)
|
||||||
|
|
||||||
if (ownerTypeCandidates.size == 1 || ApplicationManager.getApplication()!!.isUnitTestMode()) {
|
if (ownerTypeCandidates.size == 1 || ApplicationManager.getApplication()!!.isUnitTestMode()) {
|
||||||
selectedReceiverType = ownerTypeCandidates.first!!
|
functionBuilder.receiverTypeCandidate = ownerTypeCandidates.first!!
|
||||||
addFunctionToSelectedOwner()
|
functionBuilder.build()
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// class selection
|
// class selection
|
||||||
val list = JBList(ownerTypeCandidates.map { ClassCandidate(it, currentFile) })
|
val list = JBList(ownerTypeCandidates.map { ClassCandidate(it, file) })
|
||||||
val renderer = ClassCandidateListCellRenderer()
|
val renderer = ClassCandidateListCellRenderer()
|
||||||
list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION)
|
list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION)
|
||||||
list.setCellRenderer(renderer)
|
list.setCellRenderer(renderer)
|
||||||
@@ -391,315 +73,12 @@ public class CreateFunctionFromUsageFix internal (
|
|||||||
.setItemChoosenCallback {
|
.setItemChoosenCallback {
|
||||||
val selectedCandidate = list.getSelectedValue() as ClassCandidate?
|
val selectedCandidate = list.getSelectedValue() as ClassCandidate?
|
||||||
if (selectedCandidate != null) {
|
if (selectedCandidate != null) {
|
||||||
selectedReceiverType = selectedCandidate.typeCandidate
|
functionBuilder.receiverTypeCandidate = selectedCandidate.typeCandidate
|
||||||
CommandProcessor.getInstance().executeCommand(project, { addFunctionToSelectedOwner() }, getText(), null)
|
CommandProcessor.getInstance().executeCommand(project, { functionBuilder.build() }, getText(), null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.createPopup()
|
.createPopup()
|
||||||
.showInBestPositionFor(currentFileEditor)
|
.showInBestPositionFor(editor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun addFunctionToSelectedOwner() {
|
|
||||||
// gather relevant information
|
|
||||||
ownerClassDescriptor = DescriptorUtils.getClassDescriptorForType(selectedReceiverType.theType)
|
|
||||||
val receiverType = ownerClassDescriptor.getDefaultType()
|
|
||||||
val classDeclaration = DescriptorToSourceUtils.classDescriptorToDeclaration(ownerClassDescriptor)
|
|
||||||
if (classDeclaration is JetClass) {
|
|
||||||
ownerClass = classDeclaration
|
|
||||||
isExtension = !ownerClass.isWritable()
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
isExtension = true
|
|
||||||
}
|
|
||||||
isUnit = returnType.isType() && KotlinBuiltIns.getInstance().isUnit(returnType.theType!!)
|
|
||||||
|
|
||||||
val scope = if (isExtension) {
|
|
||||||
currentFileModule.getPackage(currentFile.getPackageFqName())!!.getMemberScope()
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
(ownerClassDescriptor as ClassDescriptorWithResolutionScopes).getScopeForMemberDeclarationResolution()
|
|
||||||
}
|
|
||||||
|
|
||||||
// figure out type substitutions for type parameters
|
|
||||||
val classTypeParameters = receiverType.getArguments()
|
|
||||||
val ownerTypeArguments = selectedReceiverType.theType.getArguments()
|
|
||||||
assert(ownerTypeArguments.size == classTypeParameters.size)
|
|
||||||
val substitutions = ownerTypeArguments.zip(classTypeParameters).map {
|
|
||||||
JetTypeSubstitution(it.first.getType(), it.second.getType())
|
|
||||||
}.copyToArray()
|
|
||||||
parameters.forEach { parameter ->
|
|
||||||
parameter.theType.computeTypeCandidates(currentFileContext, substitutions, scope)
|
|
||||||
}
|
|
||||||
if (!isUnit) {
|
|
||||||
returnType.computeTypeCandidates(currentFileContext, substitutions, scope)
|
|
||||||
}
|
|
||||||
|
|
||||||
// now that we have done substitutions, we can throw it away
|
|
||||||
selectedReceiverType = TypeCandidate(receiverType, scope)
|
|
||||||
|
|
||||||
// figure out type parameter renames to avoid conflicts
|
|
||||||
typeParameterNameMap = getTypeParameterRenames(scope)
|
|
||||||
parameters.forEach { parameter ->
|
|
||||||
parameter.theType.renderTypeCandidates(typeParameterNameMap)
|
|
||||||
}
|
|
||||||
if (!isUnit) {
|
|
||||||
returnType.renderTypeCandidates(typeParameterNameMap)
|
|
||||||
}
|
|
||||||
selectedReceiverType.render(typeParameterNameMap)
|
|
||||||
|
|
||||||
ApplicationManager.getApplication()!!.runWriteAction {
|
|
||||||
val func = createFunctionSkeleton()
|
|
||||||
buildAndRunTemplate(func)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun createFunctionSkeleton(): JetNamedFunction {
|
|
||||||
val parametersString = parameters.indices.map { i -> "p$i: Any" }.makeString(", ")
|
|
||||||
val returnTypeString = if (isUnit) "" else ": Any"
|
|
||||||
val psiFactory = JetPsiFactory(currentFile)
|
|
||||||
if (isExtension) {
|
|
||||||
// create as extension function
|
|
||||||
val ownerTypeString = selectedReceiverType.renderedType!!
|
|
||||||
val func = psiFactory.createFunction("fun $ownerTypeString.$functionName($parametersString)$returnTypeString { }")
|
|
||||||
containingFile = currentFile
|
|
||||||
containingFileEditor = currentFileEditor
|
|
||||||
return currentFile.add(func) as JetNamedFunction
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// create as regular function
|
|
||||||
val func = psiFactory.createFunction("fun $functionName($parametersString)$returnTypeString { }")
|
|
||||||
containingFile = ownerClass.getContainingJetFile()
|
|
||||||
|
|
||||||
NavigationUtil.activateFileWithPsiElement(containingFile)
|
|
||||||
containingFileEditor = FileEditorManager.getInstance(currentFile.getProject())!!.getSelectedTextEditor()!!
|
|
||||||
|
|
||||||
var classBody = ownerClass.getBody()
|
|
||||||
if (classBody == null) {
|
|
||||||
classBody = ownerClass.add(psiFactory.createEmptyClassBody()) as JetClassBody
|
|
||||||
ownerClass.addBefore(psiFactory.createWhiteSpace(), classBody)
|
|
||||||
}
|
|
||||||
val rBrace = classBody!!.getRBrace()
|
|
||||||
//TODO: Assert rbrace not null? It can be if the class isn't closed.
|
|
||||||
return classBody!!.addBefore(func, rBrace) as JetNamedFunction
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun buildAndRunTemplate(func: JetNamedFunction) {
|
|
||||||
val project = func.getProject()
|
|
||||||
val parameterList = func.getValueParameterList()!!
|
|
||||||
|
|
||||||
// build templates
|
|
||||||
PsiDocumentManager.getInstance(project).commitAllDocuments()
|
|
||||||
PsiDocumentManager.getInstance(project).doPostponedOperationsAndUnblockDocument(containingFileEditor.getDocument())
|
|
||||||
|
|
||||||
val caretModel = containingFileEditor.getCaretModel()
|
|
||||||
caretModel.moveToOffset(containingFile.getNode()!!.getStartOffset())
|
|
||||||
|
|
||||||
val builder = TemplateBuilderImpl(containingFile)
|
|
||||||
val returnTypeExpression = if (isUnit) null else setupReturnTypeTemplate(builder, func)
|
|
||||||
val parameterTypeExpressions = setupParameterTypeTemplates(builder, parameterList)
|
|
||||||
|
|
||||||
// add a segment for the parameter list
|
|
||||||
// Note: because TemplateBuilderImpl does not have a replaceElement overload that takes in both a TextRange and alwaysStopAt, we
|
|
||||||
// need to create the segment first and then hack the Expression into the template later. We use this template to update the type
|
|
||||||
// parameter list as the user makes selections in the parameter types, and we need alwaysStopAt to be false so the user can't tab to
|
|
||||||
// it.
|
|
||||||
val expression = setupTypeParameterListTemplate(builder, func)
|
|
||||||
|
|
||||||
// the template built by TemplateBuilderImpl is ordered by element position, but we want types to be first, so hack it
|
|
||||||
val templateImpl = builder.buildInlineTemplate() as TemplateImpl
|
|
||||||
val variables = templateImpl.getVariables()!!
|
|
||||||
for (i in 0..(parameters.size - 1)) {
|
|
||||||
Collections.swap(variables, i * 2, i * 2 + 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// fix up the template to include the expression for the type parameter list
|
|
||||||
variables.add(Variable(TYPE_PARAMETER_LIST_VARIABLE_NAME, expression, expression, false, true))
|
|
||||||
|
|
||||||
// TODO: Disabled shortening names because it causes some tests fail. Refactor code to use automatic reference shortening
|
|
||||||
templateImpl.setToShortenLongNames(false)
|
|
||||||
|
|
||||||
// run the template
|
|
||||||
TemplateManager.getInstance(project).startTemplate(containingFileEditor, templateImpl, object : TemplateEditingAdapter() {
|
|
||||||
override fun templateFinished(template: Template?, brokenOff: Boolean) {
|
|
||||||
// file templates
|
|
||||||
val offset = templateImpl.getSegmentOffset(0)
|
|
||||||
val newFunc = PsiTreeUtil.findElementOfClassAtOffset(containingFile, offset, javaClass<JetNamedFunction>(), false)!!
|
|
||||||
val typeRefsToShorten = ArrayList<JetTypeReference>()
|
|
||||||
|
|
||||||
ApplicationManager.getApplication()!!.runWriteAction {
|
|
||||||
// file templates
|
|
||||||
setupFunctionBody(newFunc)
|
|
||||||
|
|
||||||
// change short type names to fully qualified ones (to be shortened below)
|
|
||||||
setupTypeReferencesForShortening(newFunc, typeRefsToShorten, parameterTypeExpressions, returnTypeExpression)
|
|
||||||
ShortenReferences.process(typeRefsToShorten)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getTypeParameterRenames(scope: JetScope): Map<TypeParameterDescriptor, String> {
|
|
||||||
val allTypeParametersNotInScope = LinkedHashSet<TypeParameterDescriptor>()
|
|
||||||
|
|
||||||
allTypeParametersNotInScope.addAll(selectedReceiverType.typeParameters.toList())
|
|
||||||
|
|
||||||
parameters.stream()
|
|
||||||
.flatMap { it.theType.typeCandidates!!.stream() }
|
|
||||||
.flatMap { it.typeParameters.stream() }
|
|
||||||
.toCollection(allTypeParametersNotInScope)
|
|
||||||
|
|
||||||
if (!isUnit) {
|
|
||||||
returnType.typeCandidates!!.stream().flatMapTo(allTypeParametersNotInScope) { it.typeParameters.stream() }
|
|
||||||
}
|
|
||||||
|
|
||||||
val validator = CollectingValidator { scope.getClassifier(Name.identifier(it)) == null }
|
|
||||||
val typeParameterNames = allTypeParametersNotInScope.map { validator.validateName(it.getName().asString()) }
|
|
||||||
|
|
||||||
val typeParameterNameMap = HashMap<TypeParameterDescriptor, String>()
|
|
||||||
for ((key, value) in allTypeParametersNotInScope.zip(typeParameterNames)) {
|
|
||||||
typeParameterNameMap[key] = value
|
|
||||||
}
|
|
||||||
|
|
||||||
return typeParameterNameMap
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setupTypeReferencesForShortening(
|
|
||||||
func: JetNamedFunction,
|
|
||||||
typeRefsToShorten: MutableList<JetTypeReference>, parameterTypeExpressions: List<TypeExpression>,
|
|
||||||
returnTypeExpression: TypeExpression?) {
|
|
||||||
if (isExtension) {
|
|
||||||
val receiverTypeRef = JetPsiFactory(func).createType(selectedReceiverType.theType.renderLong(typeParameterNameMap))
|
|
||||||
replaceWithLongerName(receiverTypeRef, selectedReceiverType.theType)
|
|
||||||
|
|
||||||
val funcReceiverTypeRef = func.getReceiverTypeRef()
|
|
||||||
if (funcReceiverTypeRef != null) {
|
|
||||||
typeRefsToShorten.add(funcReceiverTypeRef)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isUnit) {
|
|
||||||
returnTypeExpression!!
|
|
||||||
val returnTypeRef = func.getReturnTypeRef()
|
|
||||||
if (returnTypeRef != null) {
|
|
||||||
val returnType = returnTypeExpression.getTypeFromSelection(returnTypeRef.getText() ?: throw AssertionError("Expression for function return type shouldn't be empty: function = ${func.getText()}"))
|
|
||||||
if (returnType != null) {
|
|
||||||
// user selected a given type
|
|
||||||
replaceWithLongerName(returnTypeRef, returnType)
|
|
||||||
typeRefsToShorten.add(func.getReturnTypeRef()!!)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val valueParameters = func.getValueParameters()
|
|
||||||
val parameterIndicesToShorten = ArrayList<Int>()
|
|
||||||
assert(valueParameters.size == parameterTypeExpressions.size)
|
|
||||||
for ((i, parameter) in valueParameters.stream().withIndices()) {
|
|
||||||
val parameterTypeRef = parameter.getTypeReference()
|
|
||||||
if (parameterTypeRef != null) {
|
|
||||||
val parameterType = parameterTypeExpressions[i].getTypeFromSelection(parameterTypeRef.getText() ?: throw AssertionError("Expression for function parameter type shouldn't be empty: function = ${func.getText()}"))
|
|
||||||
if (parameterType != null) {
|
|
||||||
replaceWithLongerName(parameterTypeRef, parameterType)
|
|
||||||
parameterIndicesToShorten.add(i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val expandedValueParameters = func.getValueParameters()
|
|
||||||
parameterIndicesToShorten.stream().map { expandedValueParameters[it].getTypeReference() }.filterNotNullTo(typeRefsToShorten)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private fun setupFunctionBody(func: JetNamedFunction) {
|
|
||||||
val fileTemplate = FileTemplateManager.getInstance()!!.getCodeTemplate(TEMPLATE_FROM_USAGE_FUNCTION_BODY)
|
|
||||||
val properties = Properties()
|
|
||||||
properties.setProperty(FileTemplate.ATTRIBUTE_RETURN_TYPE, if (isUnit) "Unit" else func.getReturnTypeRef()!!.getText())
|
|
||||||
properties.setProperty(FileTemplate.ATTRIBUTE_CLASS_NAME, DescriptorUtils.getFqName(ownerClassDescriptor).asString())
|
|
||||||
properties.setProperty(FileTemplate.ATTRIBUTE_SIMPLE_CLASS_NAME, ownerClassDescriptor.getName().asString())
|
|
||||||
properties.setProperty(ATTRIBUTE_FUNCTION_NAME, functionName)
|
|
||||||
|
|
||||||
val bodyText = try {
|
|
||||||
fileTemplate!!.getText(properties)
|
|
||||||
}
|
|
||||||
catch (e: ProcessCanceledException) {
|
|
||||||
throw e
|
|
||||||
}
|
|
||||||
catch (e: Exception) {
|
|
||||||
// TODO: This is dangerous.
|
|
||||||
// Is there any way to avoid catching all exceptions?
|
|
||||||
throw IncorrectOperationException("Failed to parse file template", e)
|
|
||||||
}
|
|
||||||
|
|
||||||
val newBodyExpression = JetPsiFactory(func).createFunctionBody(bodyText)
|
|
||||||
func.getBodyExpression()!!.replace(newBodyExpression)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setupReturnTypeTemplate(builder: TemplateBuilder, func: JetNamedFunction): TypeExpression {
|
|
||||||
val returnTypeRef = func.getReturnTypeRef()!!
|
|
||||||
val returnTypeExpression = TypeExpression(returnType)
|
|
||||||
builder.replaceElement(returnTypeRef, returnTypeExpression)
|
|
||||||
return returnTypeExpression
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setupTypeParameterListTemplate(builder: TemplateBuilderImpl, func: JetNamedFunction): TypeParameterListExpression {
|
|
||||||
val typeParameterMap = HashMap<String, Array<String>>()
|
|
||||||
val receiverTypeParameterNames = selectedReceiverType.typeParameterNames
|
|
||||||
|
|
||||||
parameters.stream().flatMap { it.theType.typeCandidates!!.stream() }.forEach {
|
|
||||||
typeParameterMap[it.renderedType!!] = it.typeParameterNames!!
|
|
||||||
}
|
|
||||||
|
|
||||||
if (func.getReturnTypeRef() != null) {
|
|
||||||
returnType.typeCandidates!!.forEach {
|
|
||||||
typeParameterMap[it.renderedType!!] = it.typeParameterNames!!
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// ((3, 3) is after "fun")
|
|
||||||
builder.replaceElement(func, TextRange.create(3, 3), TYPE_PARAMETER_LIST_VARIABLE_NAME, null, false)
|
|
||||||
return TypeParameterListExpression(receiverTypeParameterNames!!, typeParameterMap)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setupParameterTypeTemplates(builder: TemplateBuilder, parameterList: JetParameterList): List<TypeExpression> {
|
|
||||||
val jetParameters = parameterList.getParameters()
|
|
||||||
assert(jetParameters.size == parameters.size)
|
|
||||||
|
|
||||||
val typeParameters = ArrayList<TypeExpression>()
|
|
||||||
for ((parameter, jetParameter) in parameters.zip(jetParameters)) {
|
|
||||||
val parameterTypeExpression = TypeExpression(parameter.theType)
|
|
||||||
val parameterTypeRef = jetParameter.getTypeReference()!!
|
|
||||||
builder.replaceElement(parameterTypeRef, parameterTypeExpression)
|
|
||||||
|
|
||||||
// add parameter name to the template
|
|
||||||
val possibleNamesFromExpression = parameter.theType.possibleNamesFromExpression
|
|
||||||
val preferredName = parameter.preferredName
|
|
||||||
val possibleNames = if (preferredName != null) {
|
|
||||||
array(preferredName, *possibleNamesFromExpression)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
possibleNamesFromExpression
|
|
||||||
}
|
|
||||||
|
|
||||||
// figure out suggested names for each type option
|
|
||||||
val parameterTypeToNamesMap = HashMap<String, Array<String>>()
|
|
||||||
parameter.theType.typeCandidates!!.forEach { typeCandidate ->
|
|
||||||
val suggestedNames = JetNameSuggester.suggestNamesForType(typeCandidate.theType, EmptyValidator)
|
|
||||||
parameterTypeToNamesMap[typeCandidate.renderedType!!] = suggestedNames
|
|
||||||
}
|
|
||||||
|
|
||||||
// add expression to builder
|
|
||||||
val parameterNameExpression = ParameterNameExpression(possibleNames, parameterTypeToNamesMap)
|
|
||||||
val parameterNameIdentifier = jetParameter.getNameIdentifier()!!
|
|
||||||
builder.replaceElement(parameterNameIdentifier, parameterNameExpression)
|
|
||||||
|
|
||||||
typeParameters.add(parameterTypeExpression)
|
|
||||||
}
|
|
||||||
return typeParameters
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun replaceWithLongerName(typeRef: JetTypeReference, theType: JetType) {
|
|
||||||
val fullyQualifiedReceiverTypeRef = JetPsiFactory(typeRef).createType(theType.renderLong(typeParameterNameMap))
|
|
||||||
typeRef.replace(fullyQualifiedReceiverTypeRef)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+4
-4
@@ -11,13 +11,13 @@ object CreateGetFunctionActionFactory : JetSingleIntentionActionFactory() {
|
|||||||
override fun createAction(diagnostic: Diagnostic): IntentionAction? {
|
override fun createAction(diagnostic: Diagnostic): IntentionAction? {
|
||||||
val accessExpr = QuickFixUtil.getParentElementOfType(diagnostic, javaClass<JetArrayAccessExpression>()) ?: return null
|
val accessExpr = QuickFixUtil.getParentElementOfType(diagnostic, javaClass<JetArrayAccessExpression>()) ?: return null
|
||||||
val arrayExpr = accessExpr.getArrayExpression() ?: return null
|
val arrayExpr = accessExpr.getArrayExpression() ?: return null
|
||||||
val arrayType = TypeOrExpressionThereof(arrayExpr, Variance.IN_VARIANCE)
|
val arrayType = TypeInfo(arrayExpr, Variance.IN_VARIANCE)
|
||||||
|
|
||||||
val parameters = accessExpr.getIndexExpressions().map {
|
val parameters = accessExpr.getIndexExpressions().map {
|
||||||
Parameter(TypeOrExpressionThereof(it, Variance.IN_VARIANCE))
|
ParameterInfo(TypeInfo(it, Variance.IN_VARIANCE))
|
||||||
}
|
}
|
||||||
|
|
||||||
val returnType = TypeOrExpressionThereof(accessExpr, Variance.OUT_VARIANCE)
|
val returnType = TypeInfo(accessExpr, Variance.OUT_VARIANCE)
|
||||||
return CreateFunctionFromUsageFix(accessExpr, arrayType, "get", returnType, parameters)
|
return CreateFunctionFromUsageFix(accessExpr, FunctionInfo("get", arrayType, returnType, parameters))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+3
-3
@@ -13,10 +13,10 @@ import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns
|
|||||||
object CreateHasNextFunctionActionFactory : JetSingleIntentionActionFactory() {
|
object CreateHasNextFunctionActionFactory : JetSingleIntentionActionFactory() {
|
||||||
override fun createAction(diagnostic: Diagnostic): IntentionAction? {
|
override fun createAction(diagnostic: Diagnostic): IntentionAction? {
|
||||||
val diagnosticWithParameters = DiagnosticFactory.cast(diagnostic, Errors.HAS_NEXT_MISSING, Errors.HAS_NEXT_FUNCTION_NONE_APPLICABLE)
|
val diagnosticWithParameters = DiagnosticFactory.cast(diagnostic, Errors.HAS_NEXT_MISSING, Errors.HAS_NEXT_FUNCTION_NONE_APPLICABLE)
|
||||||
val ownerType = TypeOrExpressionThereof(diagnosticWithParameters.getA(), Variance.IN_VARIANCE)
|
val ownerType = TypeInfo(diagnosticWithParameters.getA(), Variance.IN_VARIANCE)
|
||||||
|
|
||||||
val forExpr = QuickFixUtil.getParentElementOfType(diagnostic, javaClass<JetForExpression>()) ?: return null
|
val forExpr = QuickFixUtil.getParentElementOfType(diagnostic, javaClass<JetForExpression>()) ?: return null
|
||||||
val returnType = TypeOrExpressionThereof(KotlinBuiltIns.getInstance().getBooleanType(), Variance.OUT_VARIANCE)
|
val returnType = TypeInfo(KotlinBuiltIns.getInstance().getBooleanType(), Variance.OUT_VARIANCE)
|
||||||
return CreateFunctionFromUsageFix(forExpr, ownerType, "hasNext", returnType)
|
return CreateFunctionFromUsageFix(forExpr, FunctionInfo("hasNext", ownerType, returnType))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+3
-3
@@ -20,7 +20,7 @@ object CreateIteratorFunctionActionFactory : JetSingleIntentionActionFactory() {
|
|||||||
val forExpr = QuickFixUtil.getParentElementOfType(diagnostic, javaClass<JetForExpression>()) ?: return null
|
val forExpr = QuickFixUtil.getParentElementOfType(diagnostic, javaClass<JetForExpression>()) ?: return null
|
||||||
val iterableExpr = forExpr.getLoopRange() ?: return null
|
val iterableExpr = forExpr.getLoopRange() ?: return null
|
||||||
val variableExpr: JetExpression = ((forExpr.getLoopParameter() ?: forExpr.getMultiParameter()) ?: return null) as JetExpression
|
val variableExpr: JetExpression = ((forExpr.getLoopParameter() ?: forExpr.getMultiParameter()) ?: return null) as JetExpression
|
||||||
val iterableType = TypeOrExpressionThereof(iterableExpr, Variance.IN_VARIANCE)
|
val iterableType = TypeInfo(iterableExpr, Variance.IN_VARIANCE)
|
||||||
val returnJetType = KotlinBuiltIns.getInstance().getIterator().getDefaultType()
|
val returnJetType = KotlinBuiltIns.getInstance().getIterator().getDefaultType()
|
||||||
|
|
||||||
val context = file.getBindingContext()
|
val context = file.getBindingContext()
|
||||||
@@ -30,7 +30,7 @@ object CreateIteratorFunctionActionFactory : JetSingleIntentionActionFactory() {
|
|||||||
val returnJetTypeParameterType = TypeProjectionImpl(returnJetTypeParameterTypes[0])
|
val returnJetTypeParameterType = TypeProjectionImpl(returnJetTypeParameterTypes[0])
|
||||||
val returnJetTypeArguments = Collections.singletonList(returnJetTypeParameterType)
|
val returnJetTypeArguments = Collections.singletonList(returnJetTypeParameterType)
|
||||||
val newReturnJetType = JetTypeImpl(returnJetType.getAnnotations(), returnJetType.getConstructor(), returnJetType.isNullable(), returnJetTypeArguments, returnJetType.getMemberScope())
|
val newReturnJetType = JetTypeImpl(returnJetType.getAnnotations(), returnJetType.getConstructor(), returnJetType.isNullable(), returnJetTypeArguments, returnJetType.getMemberScope())
|
||||||
val returnType = TypeOrExpressionThereof(newReturnJetType, Variance.OUT_VARIANCE)
|
val returnType = TypeInfo(newReturnJetType, Variance.OUT_VARIANCE)
|
||||||
return CreateFunctionFromUsageFix(forExpr, iterableType, "iterator", returnType)
|
return CreateFunctionFromUsageFix(forExpr, FunctionInfo("iterator", iterableType, returnType))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-3
@@ -13,11 +13,11 @@ import org.jetbrains.jet.lang.psi.JetForExpression
|
|||||||
object CreateNextFunctionActionFactory : JetSingleIntentionActionFactory() {
|
object CreateNextFunctionActionFactory : JetSingleIntentionActionFactory() {
|
||||||
override fun createAction(diagnostic: Diagnostic): IntentionAction? {
|
override fun createAction(diagnostic: Diagnostic): IntentionAction? {
|
||||||
val diagnosticWithParameters = DiagnosticFactory.cast(diagnostic, Errors.NEXT_MISSING, Errors.NEXT_NONE_APPLICABLE)
|
val diagnosticWithParameters = DiagnosticFactory.cast(diagnostic, Errors.NEXT_MISSING, Errors.NEXT_NONE_APPLICABLE)
|
||||||
val ownerType = TypeOrExpressionThereof(diagnosticWithParameters.getA(), Variance.IN_VARIANCE)
|
val ownerType = TypeInfo(diagnosticWithParameters.getA(), Variance.IN_VARIANCE)
|
||||||
|
|
||||||
val forExpr = QuickFixUtil.getParentElementOfType(diagnostic, javaClass<JetForExpression>()) ?: return null
|
val forExpr = QuickFixUtil.getParentElementOfType(diagnostic, javaClass<JetForExpression>()) ?: return null
|
||||||
val variableExpr: JetExpression = ((forExpr.getLoopParameter() ?: forExpr.getMultiParameter()) ?: return null) as JetExpression
|
val variableExpr: JetExpression = ((forExpr.getLoopParameter() ?: forExpr.getMultiParameter()) ?: return null) as JetExpression
|
||||||
val returnType = TypeOrExpressionThereof(variableExpr, Variance.OUT_VARIANCE)
|
val returnType = TypeInfo(variableExpr, Variance.OUT_VARIANCE)
|
||||||
return CreateFunctionFromUsageFix(forExpr, ownerType, "next", returnType)
|
return CreateFunctionFromUsageFix(forExpr, FunctionInfo("next", ownerType, returnType))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+7
-7
@@ -14,18 +14,18 @@ object CreateSetFunctionActionFactory : JetSingleIntentionActionFactory() {
|
|||||||
override fun createAction(diagnostic: Diagnostic): IntentionAction? {
|
override fun createAction(diagnostic: Diagnostic): IntentionAction? {
|
||||||
val accessExpr = QuickFixUtil.getParentElementOfType(diagnostic, javaClass<JetArrayAccessExpression>()) ?: return null
|
val accessExpr = QuickFixUtil.getParentElementOfType(diagnostic, javaClass<JetArrayAccessExpression>()) ?: return null
|
||||||
val arrayExpr = accessExpr.getArrayExpression() ?: return null
|
val arrayExpr = accessExpr.getArrayExpression() ?: return null
|
||||||
val arrayType = TypeOrExpressionThereof(arrayExpr, Variance.IN_VARIANCE)
|
val arrayType = TypeInfo(arrayExpr, Variance.IN_VARIANCE)
|
||||||
|
|
||||||
val parameters = accessExpr.getIndexExpressions().mapTo(ArrayList<Parameter>()) {
|
val parameters = accessExpr.getIndexExpressions().mapTo(ArrayList<ParameterInfo>()) {
|
||||||
Parameter(TypeOrExpressionThereof(it, Variance.IN_VARIANCE))
|
ParameterInfo(TypeInfo(it, Variance.IN_VARIANCE))
|
||||||
}
|
}
|
||||||
|
|
||||||
val assignmentExpr = QuickFixUtil.getParentElementOfType(diagnostic, javaClass<JetBinaryExpression>()) ?: return null
|
val assignmentExpr = QuickFixUtil.getParentElementOfType(diagnostic, javaClass<JetBinaryExpression>()) ?: return null
|
||||||
val rhs = assignmentExpr.getRight() ?: return null
|
val rhs = assignmentExpr.getRight() ?: return null
|
||||||
val valType = TypeOrExpressionThereof(rhs, Variance.IN_VARIANCE)
|
val valType = TypeInfo(rhs, Variance.IN_VARIANCE)
|
||||||
parameters.add(Parameter(valType, "value"))
|
parameters.add(ParameterInfo(valType, "value"))
|
||||||
|
|
||||||
val returnType = TypeOrExpressionThereof(KotlinBuiltIns.getInstance().getUnitType(), Variance.OUT_VARIANCE)
|
val returnType = TypeInfo(KotlinBuiltIns.getInstance().getUnitType(), Variance.OUT_VARIANCE)
|
||||||
return CreateFunctionFromUsageFix(accessExpr, arrayType, "set", returnType, parameters)
|
return CreateFunctionFromUsageFix(accessExpr, FunctionInfo("set", arrayType, returnType, parameters))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+63
@@ -0,0 +1,63 @@
|
|||||||
|
package org.jetbrains.jet.plugin.quickfix.createFromUsage.createFunction
|
||||||
|
|
||||||
|
import java.util.Collections
|
||||||
|
import org.jetbrains.jet.lang.psi.JetExpression
|
||||||
|
import org.jetbrains.jet.lang.types.Variance
|
||||||
|
import org.jetbrains.jet.lang.types.JetType
|
||||||
|
import kotlin.properties.Delegates
|
||||||
|
import com.intellij.util.ArrayUtil
|
||||||
|
import org.jetbrains.jet.plugin.refactoring.JetNameSuggester
|
||||||
|
import org.jetbrains.jet.plugin.refactoring.EmptyValidator
|
||||||
|
import org.jetbrains.jet.lang.resolve.BindingContext
|
||||||
|
import org.jetbrains.jet.plugin.util.supertypes
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a concrete type or a set of types yet to be inferred from an expression.
|
||||||
|
*/
|
||||||
|
abstract class TypeInfo(val variance: Variance) {
|
||||||
|
class ByExpression(val expression: JetExpression, variance: Variance): TypeInfo(variance) {
|
||||||
|
override val possibleNamesFromExpression: Array<String> by Delegates.lazy {
|
||||||
|
JetNameSuggester.suggestNamesForExpression(expression, EmptyValidator)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getPossibleTypes(context: BindingContext): List<JetType> =
|
||||||
|
expression.guessTypes(context).flatMap { it.getPossibleSupertypes(variance) }
|
||||||
|
}
|
||||||
|
|
||||||
|
class ByType(val theType: JetType, variance: Variance): TypeInfo(variance) {
|
||||||
|
override val possibleNamesFromExpression: Array<String> =
|
||||||
|
ArrayUtil.EMPTY_STRING_ARRAY
|
||||||
|
|
||||||
|
override fun getPossibleTypes(context: BindingContext): List<JetType> =
|
||||||
|
theType.getPossibleSupertypes(variance)
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract val possibleNamesFromExpression: Array<String>
|
||||||
|
abstract fun getPossibleTypes(context: BindingContext): List<JetType>
|
||||||
|
|
||||||
|
protected fun JetType.getPossibleSupertypes(variance: Variance): List<JetType> {
|
||||||
|
val single = Collections.singletonList(this)
|
||||||
|
return when (variance) {
|
||||||
|
Variance.IN_VARIANCE -> single + supertypes()
|
||||||
|
else -> single
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun TypeInfo(expressionOfType: JetExpression, variance: Variance): TypeInfo = TypeInfo.ByExpression(expressionOfType, variance)
|
||||||
|
fun TypeInfo(theType: JetType, variance: Variance): TypeInfo = TypeInfo.ByType(theType, variance)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encapsulates information about a function parameter that is going to be created.
|
||||||
|
*/
|
||||||
|
class ParameterInfo(
|
||||||
|
val typeInfo: TypeInfo,
|
||||||
|
val preferredName: String? = null
|
||||||
|
)
|
||||||
|
|
||||||
|
class FunctionInfo (
|
||||||
|
val name: String,
|
||||||
|
val receiverTypeInfo: TypeInfo,
|
||||||
|
val returnTypeInfo: TypeInfo,
|
||||||
|
val parameterInfos: List<ParameterInfo> = Collections.emptyList()
|
||||||
|
)
|
||||||
+466
@@ -0,0 +1,466 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2014 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.jet.plugin.quickfix.createFromUsage.createFunction
|
||||||
|
|
||||||
|
import com.intellij.codeInsight.navigation.NavigationUtil
|
||||||
|
import com.intellij.codeInsight.template.*
|
||||||
|
import com.intellij.codeInsight.template.impl.TemplateImpl
|
||||||
|
import com.intellij.codeInsight.template.impl.Variable
|
||||||
|
import com.intellij.ide.fileTemplates.FileTemplate
|
||||||
|
import com.intellij.ide.fileTemplates.FileTemplateManager
|
||||||
|
import com.intellij.openapi.application.ApplicationManager
|
||||||
|
import com.intellij.openapi.editor.Editor
|
||||||
|
import com.intellij.openapi.fileEditor.FileEditorManager
|
||||||
|
import com.intellij.openapi.progress.ProcessCanceledException
|
||||||
|
import com.intellij.openapi.util.TextRange
|
||||||
|
import com.intellij.psi.PsiDocumentManager
|
||||||
|
import com.intellij.psi.util.PsiTreeUtil
|
||||||
|
import com.intellij.util.IncorrectOperationException
|
||||||
|
import org.jetbrains.jet.lang.descriptors.*
|
||||||
|
import org.jetbrains.jet.lang.psi.*
|
||||||
|
import org.jetbrains.jet.lang.resolve.BindingContext
|
||||||
|
import org.jetbrains.jet.lang.resolve.DescriptorUtils
|
||||||
|
import org.jetbrains.jet.lang.resolve.name.Name
|
||||||
|
import org.jetbrains.jet.lang.resolve.scopes.JetScope
|
||||||
|
import org.jetbrains.jet.lang.types.*
|
||||||
|
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns
|
||||||
|
import org.jetbrains.jet.plugin.codeInsight.ShortenReferences
|
||||||
|
import org.jetbrains.jet.plugin.refactoring.JetNameSuggester
|
||||||
|
import kotlin.properties.Delegates
|
||||||
|
import java.util.LinkedHashSet
|
||||||
|
import java.util.Collections
|
||||||
|
import java.util.HashMap
|
||||||
|
import java.util.ArrayList
|
||||||
|
import java.util.Properties
|
||||||
|
import org.jetbrains.jet.plugin.caches.resolve.getAnalysisResults
|
||||||
|
import org.jetbrains.jet.lang.resolve.DescriptorToSourceUtils
|
||||||
|
import org.jetbrains.jet.plugin.refactoring.EmptyValidator
|
||||||
|
import org.jetbrains.jet.plugin.refactoring.CollectingValidator
|
||||||
|
import org.jetbrains.jet.plugin.util.isUnit
|
||||||
|
import org.jetbrains.jet.plugin.refactoring.runWriteAction
|
||||||
|
|
||||||
|
private val TYPE_PARAMETER_LIST_VARIABLE_NAME = "typeParameterList"
|
||||||
|
private val TEMPLATE_FROM_USAGE_FUNCTION_BODY = "New Kotlin Function Body.kt"
|
||||||
|
private val ATTRIBUTE_FUNCTION_NAME = "FUNCTION_NAME"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a single choice for a type (e.g. parameter type or return type).
|
||||||
|
*/
|
||||||
|
class TypeCandidate(val theType: JetType, scope: JetScope? = null) {
|
||||||
|
public val typeParameters: Array<TypeParameterDescriptor>
|
||||||
|
var renderedType: String? = null
|
||||||
|
private set
|
||||||
|
var typeParameterNames: Array<String>? = null
|
||||||
|
private set
|
||||||
|
|
||||||
|
fun render(typeParameterNameMap: Map<TypeParameterDescriptor, String>) {
|
||||||
|
renderedType = theType.renderShort(typeParameterNameMap);
|
||||||
|
typeParameterNames = typeParameters.map { typeParameterNameMap[it]!! }.copyToArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
val typeParametersInType = theType.getTypeParameters()
|
||||||
|
if (scope == null) {
|
||||||
|
typeParameters = typeParametersInType.copyToArray()
|
||||||
|
renderedType = theType.renderShort(Collections.emptyMap());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
typeParameters = getTypeParameterNamesNotInScope(typeParametersInType, scope).copyToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class FunctionBuilderConfiguration(
|
||||||
|
val functionInfo: FunctionInfo,
|
||||||
|
val currentFile: JetFile,
|
||||||
|
val currentEditor: Editor
|
||||||
|
)
|
||||||
|
|
||||||
|
class FunctionBuilder(val config: FunctionBuilderConfiguration) {
|
||||||
|
private var finished: Boolean = false
|
||||||
|
|
||||||
|
val currentFileContext: BindingContext
|
||||||
|
val currentFileModule: ModuleDescriptor
|
||||||
|
|
||||||
|
public var receiverTypeCandidate: TypeCandidate by Delegates.notNull()
|
||||||
|
|
||||||
|
private val typeCandidates = HashMap<TypeInfo, List<TypeCandidate>>();
|
||||||
|
|
||||||
|
{
|
||||||
|
val exhaust = config.currentFile.getAnalysisResults()
|
||||||
|
currentFileContext = exhaust.getBindingContext()
|
||||||
|
currentFileModule = exhaust.getModuleDescriptor()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun computeTypeCandidates(typeInfo: TypeInfo): List<TypeCandidate> =
|
||||||
|
typeCandidates.getOrPut(typeInfo) { typeInfo.getPossibleTypes(currentFileContext).map { TypeCandidate(it) } }
|
||||||
|
|
||||||
|
fun computeTypeCandidates(
|
||||||
|
typeInfo: TypeInfo,
|
||||||
|
substitutions: Array<JetTypeSubstitution>,
|
||||||
|
scope: JetScope): List<TypeCandidate> {
|
||||||
|
return typeCandidates.getOrPut(typeInfo) {
|
||||||
|
val types = typeInfo.getPossibleTypes(currentFileContext).reverse()
|
||||||
|
|
||||||
|
val newTypes = LinkedHashSet<JetType>(types)
|
||||||
|
for (substitution in substitutions) {
|
||||||
|
// each substitution can be applied or not, so we offer all options
|
||||||
|
val toAdd = newTypes.map { theType -> theType.substitute(substitution, typeInfo.variance) }
|
||||||
|
// substitution.byType are type arguments, but they cannot already occur in the type before substitution
|
||||||
|
val toRemove = newTypes.filter { theType -> substitution.byType in theType }
|
||||||
|
|
||||||
|
newTypes.addAll(toAdd)
|
||||||
|
newTypes.removeAll(toRemove)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newTypes.empty) {
|
||||||
|
newTypes.add(KotlinBuiltIns.getInstance().getAnyType())
|
||||||
|
}
|
||||||
|
|
||||||
|
newTypes.map { TypeCandidate(it, scope) }.reverse()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private inner class Context {
|
||||||
|
val isUnit: Boolean
|
||||||
|
val isExtension: Boolean
|
||||||
|
val containingFile: JetFile
|
||||||
|
val containingFileEditor: Editor
|
||||||
|
val receiverClass: JetClass?
|
||||||
|
val receiverClassDescriptor: ClassDescriptor
|
||||||
|
val typeParameterNameMap: Map<TypeParameterDescriptor, String>
|
||||||
|
|
||||||
|
{
|
||||||
|
// gather relevant information
|
||||||
|
|
||||||
|
receiverClassDescriptor = DescriptorUtils.getClassDescriptorForType(receiverTypeCandidate.theType)
|
||||||
|
val receiverType = receiverClassDescriptor.getDefaultType()
|
||||||
|
val classDeclaration = DescriptorToSourceUtils.classDescriptorToDeclaration(receiverClassDescriptor)
|
||||||
|
if (classDeclaration is JetClass) {
|
||||||
|
receiverClass = classDeclaration
|
||||||
|
isExtension = !classDeclaration.isWritable()
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
receiverClass = null
|
||||||
|
isExtension = true
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isExtension) {
|
||||||
|
containingFile = config.currentFile
|
||||||
|
containingFileEditor = config.currentEditor
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
containingFile = receiverClass!!.getContainingJetFile()
|
||||||
|
NavigationUtil.activateFileWithPsiElement(containingFile)
|
||||||
|
containingFileEditor = FileEditorManager.getInstance(config.currentFile.getProject())!!.getSelectedTextEditor()!!
|
||||||
|
}
|
||||||
|
|
||||||
|
isUnit = config.functionInfo.returnTypeInfo.let { it is TypeInfo.ByType && it.theType.isUnit() }
|
||||||
|
|
||||||
|
val scope = if (isExtension) {
|
||||||
|
currentFileModule.getPackage(config.currentFile.getPackageFqName())!!.getMemberScope()
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
(receiverClassDescriptor as ClassDescriptorWithResolutionScopes).getScopeForMemberDeclarationResolution()
|
||||||
|
}
|
||||||
|
|
||||||
|
// figure out type substitutions for type parameters
|
||||||
|
val classTypeParameters = receiverType.getArguments()
|
||||||
|
val ownerTypeArguments = receiverTypeCandidate.theType.getArguments()
|
||||||
|
assert(ownerTypeArguments.size == classTypeParameters.size)
|
||||||
|
val substitutions = ownerTypeArguments.zip(classTypeParameters).map {
|
||||||
|
JetTypeSubstitution(it.first.getType(), it.second.getType())
|
||||||
|
}.copyToArray()
|
||||||
|
config.functionInfo.parameterInfos.forEach { parameter -> computeTypeCandidates(parameter.typeInfo, substitutions, scope) }
|
||||||
|
if (!isUnit) {
|
||||||
|
computeTypeCandidates(config.functionInfo.returnTypeInfo, substitutions, scope)
|
||||||
|
}
|
||||||
|
|
||||||
|
// now that we have done substitutions, we can throw it away
|
||||||
|
receiverTypeCandidate = TypeCandidate(receiverType, scope)
|
||||||
|
|
||||||
|
// figure out type parameter renames to avoid conflicts
|
||||||
|
typeParameterNameMap = getTypeParameterRenames(scope)
|
||||||
|
config.functionInfo.parameterInfos.forEach { renderTypeCandidates(it.typeInfo, typeParameterNameMap) }
|
||||||
|
if (!isUnit) {
|
||||||
|
renderTypeCandidates(config.functionInfo.returnTypeInfo, typeParameterNameMap)
|
||||||
|
}
|
||||||
|
receiverTypeCandidate.render(typeParameterNameMap)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun renderTypeCandidates(
|
||||||
|
typeInfo: TypeInfo,
|
||||||
|
typeParameterNameMap: Map<TypeParameterDescriptor, String>
|
||||||
|
) {
|
||||||
|
typeCandidates[typeInfo]?.forEach { it.render(typeParameterNameMap) }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createFunctionSkeleton(): JetNamedFunction {
|
||||||
|
with (config) {
|
||||||
|
val parametersString = functionInfo.parameterInfos.indices.map { i -> "p$i: Any" }.joinToString(", ")
|
||||||
|
val returnTypeString = if (isUnit) "" else ": Any"
|
||||||
|
val psiFactory = JetPsiFactory(currentFile)
|
||||||
|
if (isExtension) {
|
||||||
|
// create as extension function
|
||||||
|
val ownerTypeString = receiverTypeCandidate.renderedType!!
|
||||||
|
val func = psiFactory.createFunction(
|
||||||
|
"fun $ownerTypeString.${functionInfo.name}($parametersString)$returnTypeString { }"
|
||||||
|
)
|
||||||
|
return currentFile.add(func) as JetNamedFunction
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
receiverClass!!
|
||||||
|
|
||||||
|
// create as regular function
|
||||||
|
val func = psiFactory.createFunction("fun ${functionInfo.name}($parametersString)$returnTypeString { }")
|
||||||
|
var classBody = receiverClass.getBody()
|
||||||
|
if (classBody == null) {
|
||||||
|
classBody = receiverClass.add(psiFactory.createEmptyClassBody()) as JetClassBody
|
||||||
|
receiverClass.addBefore(psiFactory.createWhiteSpace(), classBody)
|
||||||
|
}
|
||||||
|
val rBrace = classBody!!.getRBrace()
|
||||||
|
//TODO: Assert rbrace not null? It can be if the class isn't closed.
|
||||||
|
return classBody!!.addBefore(func, rBrace) as JetNamedFunction
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getTypeParameterRenames(scope: JetScope): Map<TypeParameterDescriptor, String> {
|
||||||
|
val allTypeParametersNotInScope = LinkedHashSet<TypeParameterDescriptor>()
|
||||||
|
|
||||||
|
allTypeParametersNotInScope.addAll(receiverTypeCandidate.typeParameters.toList())
|
||||||
|
|
||||||
|
config.functionInfo.parameterInfos.stream()
|
||||||
|
.flatMap { typeCandidates[it.typeInfo]!!.stream() }
|
||||||
|
.flatMap { it.typeParameters.stream() }
|
||||||
|
.toCollection(allTypeParametersNotInScope)
|
||||||
|
|
||||||
|
if (!isUnit) {
|
||||||
|
computeTypeCandidates(config.functionInfo.returnTypeInfo).stream().flatMapTo(allTypeParametersNotInScope) { it.typeParameters.stream() }
|
||||||
|
}
|
||||||
|
|
||||||
|
val validator = CollectingValidator { scope.getClassifier(Name.identifier(it)) == null }
|
||||||
|
val typeParameterNames = allTypeParametersNotInScope.map { validator.validateName(it.getName().asString()) }
|
||||||
|
|
||||||
|
return allTypeParametersNotInScope.zip(typeParameterNames).toMap()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupTypeReferencesForShortening(
|
||||||
|
func: JetNamedFunction,
|
||||||
|
typeRefsToShorten: MutableList<JetTypeReference>, parameterTypeExpressions: List<TypeExpression>,
|
||||||
|
returnTypeExpression: TypeExpression?) {
|
||||||
|
if (isExtension) {
|
||||||
|
val receiverTypeRef = JetPsiFactory(func).createType(receiverTypeCandidate.theType.renderLong(typeParameterNameMap))
|
||||||
|
replaceWithLongerName(receiverTypeRef, receiverTypeCandidate.theType)
|
||||||
|
|
||||||
|
val funcReceiverTypeRef = func.getReceiverTypeRef()
|
||||||
|
if (funcReceiverTypeRef != null) {
|
||||||
|
typeRefsToShorten.add(funcReceiverTypeRef)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isUnit) {
|
||||||
|
returnTypeExpression!!
|
||||||
|
val returnTypeRef = func.getReturnTypeRef()
|
||||||
|
if (returnTypeRef != null) {
|
||||||
|
val returnType = returnTypeExpression.getTypeFromSelection(returnTypeRef.getText() ?: throw AssertionError("Expression for function return type shouldn't be empty: function = ${func.getText()}"))
|
||||||
|
if (returnType != null) {
|
||||||
|
// user selected a given type
|
||||||
|
replaceWithLongerName(returnTypeRef, returnType)
|
||||||
|
typeRefsToShorten.add(func.getReturnTypeRef()!!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val valueParameters = func.getValueParameters()
|
||||||
|
val parameterIndicesToShorten = ArrayList<Int>()
|
||||||
|
assert(valueParameters.size == parameterTypeExpressions.size)
|
||||||
|
for ((i, parameter) in valueParameters.stream().withIndices()) {
|
||||||
|
val parameterTypeRef = parameter.getTypeReference()
|
||||||
|
if (parameterTypeRef != null) {
|
||||||
|
val parameterType = parameterTypeExpressions[i].getTypeFromSelection(parameterTypeRef.getText() ?: throw AssertionError("Expression for function parameter type shouldn't be empty: function = ${func.getText()}"))
|
||||||
|
if (parameterType != null) {
|
||||||
|
replaceWithLongerName(parameterTypeRef, parameterType)
|
||||||
|
parameterIndicesToShorten.add(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val expandedValueParameters = func.getValueParameters()
|
||||||
|
parameterIndicesToShorten.stream().map { expandedValueParameters[it].getTypeReference() }.filterNotNullTo(typeRefsToShorten)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupFunctionBody(func: JetNamedFunction) {
|
||||||
|
val fileTemplate = FileTemplateManager.getInstance()!!.getCodeTemplate(TEMPLATE_FROM_USAGE_FUNCTION_BODY)
|
||||||
|
val properties = Properties()
|
||||||
|
properties.setProperty(FileTemplate.ATTRIBUTE_RETURN_TYPE, if (isUnit) "Unit" else func.getReturnTypeRef()!!.getText())
|
||||||
|
properties.setProperty(FileTemplate.ATTRIBUTE_CLASS_NAME, DescriptorUtils.getFqName(receiverClassDescriptor).asString())
|
||||||
|
properties.setProperty(FileTemplate.ATTRIBUTE_SIMPLE_CLASS_NAME, receiverClassDescriptor.getName().asString())
|
||||||
|
properties.setProperty(ATTRIBUTE_FUNCTION_NAME, config.functionInfo.name)
|
||||||
|
|
||||||
|
val bodyText = try {
|
||||||
|
fileTemplate!!.getText(properties)
|
||||||
|
}
|
||||||
|
catch (e: ProcessCanceledException) {
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
catch (e: Exception) {
|
||||||
|
// TODO: This is dangerous.
|
||||||
|
// Is there any way to avoid catching all exceptions?
|
||||||
|
throw IncorrectOperationException("Failed to parse file template", e)
|
||||||
|
}
|
||||||
|
|
||||||
|
val newBodyExpression = JetPsiFactory(func).createFunctionBody(bodyText)
|
||||||
|
func.getBodyExpression()!!.replace(newBodyExpression)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupReturnTypeTemplate(builder: TemplateBuilder, func: JetNamedFunction): TypeExpression {
|
||||||
|
val returnTypeRef = func.getReturnTypeRef()!!
|
||||||
|
val returnTypeExpression = TypeExpression(typeCandidates[config.functionInfo.returnTypeInfo]!!)
|
||||||
|
builder.replaceElement(returnTypeRef, returnTypeExpression)
|
||||||
|
return returnTypeExpression
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupTypeParameterListTemplate(builder: TemplateBuilderImpl, func: JetNamedFunction): TypeParameterListExpression {
|
||||||
|
val typeParameterMap = HashMap<String, Array<String>>()
|
||||||
|
val receiverTypeParameterNames = receiverTypeCandidate.typeParameterNames
|
||||||
|
|
||||||
|
config.functionInfo.parameterInfos.stream().flatMap { typeCandidates[it.typeInfo]!!.stream() }.forEach {
|
||||||
|
typeParameterMap[it.renderedType!!] = it.typeParameterNames!!
|
||||||
|
}
|
||||||
|
|
||||||
|
if (func.getReturnTypeRef() != null) {
|
||||||
|
typeCandidates[config.functionInfo.returnTypeInfo]!!.forEach {
|
||||||
|
typeParameterMap[it.renderedType!!] = it.typeParameterNames!!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ((3, 3) is after "fun")
|
||||||
|
builder.replaceElement(func, TextRange.create(3, 3), TYPE_PARAMETER_LIST_VARIABLE_NAME, null, false)
|
||||||
|
return TypeParameterListExpression(receiverTypeParameterNames!!, typeParameterMap)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupParameterTypeTemplates(builder: TemplateBuilder, parameterList: JetParameterList): List<TypeExpression> {
|
||||||
|
val jetParameters = parameterList.getParameters()
|
||||||
|
assert(jetParameters.size == config.functionInfo.parameterInfos.size)
|
||||||
|
|
||||||
|
val typeParameters = ArrayList<TypeExpression>()
|
||||||
|
for ((parameter, jetParameter) in config.functionInfo.parameterInfos.zip(jetParameters)) {
|
||||||
|
val parameterTypeExpression = TypeExpression(typeCandidates[parameter.typeInfo]!!)
|
||||||
|
val parameterTypeRef = jetParameter.getTypeReference()!!
|
||||||
|
builder.replaceElement(parameterTypeRef, parameterTypeExpression)
|
||||||
|
|
||||||
|
// add parameter name to the template
|
||||||
|
val possibleNamesFromExpression = parameter.typeInfo.possibleNamesFromExpression
|
||||||
|
val preferredName = parameter.preferredName
|
||||||
|
val possibleNames = if (preferredName != null) {
|
||||||
|
array(preferredName, *possibleNamesFromExpression)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
possibleNamesFromExpression
|
||||||
|
}
|
||||||
|
|
||||||
|
// figure out suggested names for each type option
|
||||||
|
val parameterTypeToNamesMap = HashMap<String, Array<String>>()
|
||||||
|
typeCandidates[parameter.typeInfo]!!.forEach { typeCandidate ->
|
||||||
|
val suggestedNames = JetNameSuggester.suggestNamesForType(typeCandidate.theType, EmptyValidator)
|
||||||
|
parameterTypeToNamesMap[typeCandidate.renderedType!!] = suggestedNames
|
||||||
|
}
|
||||||
|
|
||||||
|
// add expression to builder
|
||||||
|
val parameterNameExpression = ParameterNameExpression(possibleNames, parameterTypeToNamesMap)
|
||||||
|
val parameterNameIdentifier = jetParameter.getNameIdentifier()!!
|
||||||
|
builder.replaceElement(parameterNameIdentifier, parameterNameExpression)
|
||||||
|
|
||||||
|
typeParameters.add(parameterTypeExpression)
|
||||||
|
}
|
||||||
|
return typeParameters
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun replaceWithLongerName(typeRef: JetTypeReference, theType: JetType) {
|
||||||
|
val fullyQualifiedReceiverTypeRef = JetPsiFactory(typeRef).createType(theType.renderLong(typeParameterNameMap))
|
||||||
|
typeRef.replace(fullyQualifiedReceiverTypeRef)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun buildAndRunTemplate() {
|
||||||
|
val func = createFunctionSkeleton()
|
||||||
|
val project = func.getProject()
|
||||||
|
val parameterList = func.getValueParameterList()!!
|
||||||
|
|
||||||
|
// build templates
|
||||||
|
PsiDocumentManager.getInstance(project).commitAllDocuments()
|
||||||
|
PsiDocumentManager.getInstance(project).doPostponedOperationsAndUnblockDocument(containingFileEditor.getDocument())
|
||||||
|
|
||||||
|
val caretModel = containingFileEditor.getCaretModel()
|
||||||
|
caretModel.moveToOffset(containingFile.getNode().getStartOffset())
|
||||||
|
|
||||||
|
val builder = TemplateBuilderImpl(containingFile)
|
||||||
|
val returnTypeExpression = if (isUnit) null else setupReturnTypeTemplate(builder, func)
|
||||||
|
val parameterTypeExpressions = setupParameterTypeTemplates(builder, parameterList)
|
||||||
|
|
||||||
|
// add a segment for the parameter list
|
||||||
|
// Note: because TemplateBuilderImpl does not have a replaceElement overload that takes in both a TextRange and alwaysStopAt, we
|
||||||
|
// need to create the segment first and then hack the Expression into the template later. We use this template to update the type
|
||||||
|
// parameter list as the user makes selections in the parameter types, and we need alwaysStopAt to be false so the user can't tab to
|
||||||
|
// it.
|
||||||
|
val expression = setupTypeParameterListTemplate(builder, func)
|
||||||
|
|
||||||
|
// the template built by TemplateBuilderImpl is ordered by element position, but we want types to be first, so hack it
|
||||||
|
val templateImpl = builder.buildInlineTemplate() as TemplateImpl
|
||||||
|
val variables = templateImpl.getVariables()!!
|
||||||
|
for (i in 0..(config.functionInfo.parameterInfos.size - 1)) {
|
||||||
|
Collections.swap(variables, i * 2, i * 2 + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// fix up the template to include the expression for the type parameter list
|
||||||
|
variables.add(Variable(TYPE_PARAMETER_LIST_VARIABLE_NAME, expression, expression, false, true))
|
||||||
|
|
||||||
|
// TODO: Disabled shortening names because it causes some tests fail. Refactor code to use automatic reference shortening
|
||||||
|
templateImpl.setToShortenLongNames(false)
|
||||||
|
|
||||||
|
// run the template
|
||||||
|
TemplateManager.getInstance(project).startTemplate(containingFileEditor, templateImpl, object : TemplateEditingAdapter() {
|
||||||
|
override fun templateFinished(template: Template?, brokenOff: Boolean) {
|
||||||
|
// file templates
|
||||||
|
val offset = templateImpl.getSegmentOffset(0)
|
||||||
|
val newFunc = PsiTreeUtil.findElementOfClassAtOffset(containingFile, offset, javaClass<JetNamedFunction>(), false)!!
|
||||||
|
val typeRefsToShorten = ArrayList<JetTypeReference>()
|
||||||
|
|
||||||
|
ApplicationManager.getApplication()!!.runWriteAction {
|
||||||
|
// file templates
|
||||||
|
setupFunctionBody(newFunc)
|
||||||
|
|
||||||
|
// change short type names to fully qualified ones (to be shortened below)
|
||||||
|
setupTypeReferencesForShortening(newFunc, typeRefsToShorten, parameterTypeExpressions, returnTypeExpression)
|
||||||
|
ShortenReferences.process(typeRefsToShorten)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun build() {
|
||||||
|
if (finished) throw IllegalStateException("Current builder has already finished")
|
||||||
|
|
||||||
|
val context = Context()
|
||||||
|
runWriteAction { context.buildAndRunTemplate() }
|
||||||
|
|
||||||
|
finished = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun FunctionBuilderConfiguration.createBuilder(): FunctionBuilder = FunctionBuilder(this)
|
||||||
+142
@@ -0,0 +1,142 @@
|
|||||||
|
package org.jetbrains.jet.plugin.quickfix.createFromUsage.createFunction
|
||||||
|
|
||||||
|
import com.intellij.codeInsight.template.Expression
|
||||||
|
import com.intellij.codeInsight.template.ExpressionContext
|
||||||
|
import com.intellij.codeInsight.template.Result
|
||||||
|
import com.intellij.codeInsight.template.TextResult
|
||||||
|
import com.intellij.codeInsight.lookup.LookupElement
|
||||||
|
import java.util.LinkedHashSet
|
||||||
|
import com.intellij.psi.PsiDocumentManager
|
||||||
|
import org.jetbrains.jet.lang.psi.JetFile
|
||||||
|
import com.intellij.psi.util.PsiTreeUtil
|
||||||
|
import org.jetbrains.jet.lang.psi.JetFunction
|
||||||
|
import org.jetbrains.jet.lang.psi.JetParameter
|
||||||
|
import java.util.HashSet
|
||||||
|
import org.jetbrains.jet.plugin.refactoring.CollectingValidator
|
||||||
|
import com.intellij.codeInsight.lookup.LookupElementBuilder
|
||||||
|
import org.jetbrains.jet.lang.types.JetType
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Special <code>Expression</code> for parameter names based on its type.
|
||||||
|
*/
|
||||||
|
private class ParameterNameExpression(
|
||||||
|
private val names: Array<String>,
|
||||||
|
private val parameterTypeToNamesMap: Map<String, Array<String>>) : Expression() {
|
||||||
|
{
|
||||||
|
assert(names all { it.isNotEmpty() })
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun calculateResult(context: ExpressionContext?): Result? {
|
||||||
|
val lookupItems = calculateLookupItems(context)!!
|
||||||
|
return TextResult(if (lookupItems.isEmpty()) "" else lookupItems.first().getLookupString())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun calculateQuickResult(context: ExpressionContext?) = calculateResult(context)
|
||||||
|
|
||||||
|
override fun calculateLookupItems(context: ExpressionContext?): Array<LookupElement>? {
|
||||||
|
context!!
|
||||||
|
val names = LinkedHashSet<String>(this.names.toList())
|
||||||
|
|
||||||
|
// find the parameter list
|
||||||
|
val project = context.getProject()!!
|
||||||
|
val offset = context.getStartOffset()
|
||||||
|
PsiDocumentManager.getInstance(project).commitAllDocuments()
|
||||||
|
val editor = context.getEditor()!!
|
||||||
|
val file = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument()) as JetFile
|
||||||
|
val elementAt = file.findElementAt(offset)
|
||||||
|
val func = PsiTreeUtil.getParentOfType(elementAt, javaClass<JetFunction>()) ?: return array<LookupElement>()
|
||||||
|
val parameterList = func.getValueParameterList()!!
|
||||||
|
|
||||||
|
// add names based on selected type
|
||||||
|
val parameter = PsiTreeUtil.getParentOfType(elementAt, javaClass<JetParameter>())
|
||||||
|
if (parameter != null) {
|
||||||
|
val parameterTypeRef = parameter.getTypeReference()
|
||||||
|
if (parameterTypeRef != null) {
|
||||||
|
val suggestedNamesBasedOnType = parameterTypeToNamesMap[parameterTypeRef.getText()]
|
||||||
|
if (suggestedNamesBasedOnType != null) {
|
||||||
|
names.addAll(suggestedNamesBasedOnType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// remember other parameter names for later use
|
||||||
|
val parameterNames = parameterList.getParameters().stream().map { jetParameter ->
|
||||||
|
if (jetParameter == parameter) null else jetParameter.getName()
|
||||||
|
}.filterNotNullTo(HashSet<String>())
|
||||||
|
|
||||||
|
// add fallback parameter name
|
||||||
|
if (names.isEmpty()) {
|
||||||
|
names.add("arg")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensure there are no conflicts
|
||||||
|
val validator = CollectingValidator(parameterNames)
|
||||||
|
return names.map { LookupElementBuilder.create(validator.validateName(it)) }.copyToArray()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An <code>Expression</code> for type references.
|
||||||
|
*/
|
||||||
|
private class TypeExpression(public val typeCandidates: List<TypeCandidate>) : Expression() {
|
||||||
|
private val cachedLookupElements: Array<LookupElement> =
|
||||||
|
typeCandidates.map { LookupElementBuilder.create(it, it.renderedType!!) }.copyToArray()
|
||||||
|
|
||||||
|
override fun calculateResult(context: ExpressionContext?): Result {
|
||||||
|
val lookupItems = calculateLookupItems(context)
|
||||||
|
return TextResult(if (lookupItems.size == 0) "" else lookupItems[0].getLookupString())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun calculateQuickResult(context: ExpressionContext?) = calculateResult(context)
|
||||||
|
|
||||||
|
override fun calculateLookupItems(context: ExpressionContext?) = cachedLookupElements
|
||||||
|
|
||||||
|
public fun getTypeFromSelection(selection: String): JetType? =
|
||||||
|
typeCandidates.firstOrNull { it.renderedType == selection }?.theType
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A sort-of dummy <code>Expression</code> for parameter lists, to allow us to update the parameter list as the user makes selections.
|
||||||
|
*/
|
||||||
|
private class TypeParameterListExpression(private val typeParameterNamesFromReceiverType: Array<String>,
|
||||||
|
private val parameterTypeToTypeParameterNamesMap: Map<String, Array<String>>) : Expression() {
|
||||||
|
|
||||||
|
override fun calculateResult(context: ExpressionContext?): Result {
|
||||||
|
context!!
|
||||||
|
val project = context.getProject()!!
|
||||||
|
val offset = context.getStartOffset()
|
||||||
|
|
||||||
|
PsiDocumentManager.getInstance(project).commitAllDocuments()
|
||||||
|
val editor = context.getEditor()!!
|
||||||
|
val file = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument()) as JetFile
|
||||||
|
val elementAt = file.findElementAt(offset)
|
||||||
|
val func = PsiTreeUtil.getParentOfType(elementAt, javaClass<JetFunction>()) ?: return TextResult("")
|
||||||
|
val parameters = func.getValueParameters()
|
||||||
|
|
||||||
|
val typeParameterNames = LinkedHashSet<String>()
|
||||||
|
typeParameterNames.addAll(typeParameterNamesFromReceiverType)
|
||||||
|
for (parameter in parameters) {
|
||||||
|
val parameterTypeRef = parameter.getTypeReference()
|
||||||
|
if (parameterTypeRef != null) {
|
||||||
|
val typeParameterNamesFromParameter = parameterTypeToTypeParameterNamesMap[parameterTypeRef.getText()]
|
||||||
|
if (typeParameterNamesFromParameter != null) {
|
||||||
|
typeParameterNames.addAll(typeParameterNamesFromParameter)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val returnTypeRef = func.getReturnTypeRef()
|
||||||
|
if (returnTypeRef != null) {
|
||||||
|
val typeParameterNamesFromReturnType = parameterTypeToTypeParameterNamesMap[returnTypeRef.getText()]
|
||||||
|
if (typeParameterNamesFromReturnType != null) {
|
||||||
|
typeParameterNames.addAll(typeParameterNamesFromReturnType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return TextResult(if (typeParameterNames.empty) "" else typeParameterNames.joinToString(", ", " <", ">"))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun calculateQuickResult(context: ExpressionContext?): Result = calculateResult(context)
|
||||||
|
|
||||||
|
// do not offer the user any choices
|
||||||
|
override fun calculateLookupItems(context: ExpressionContext?) = array<LookupElement>()
|
||||||
|
}
|
||||||
@@ -18,6 +18,11 @@ package org.jetbrains.jet.plugin.util
|
|||||||
|
|
||||||
import org.jetbrains.jet.lang.types.JetType
|
import org.jetbrains.jet.lang.types.JetType
|
||||||
import org.jetbrains.jet.lang.types.TypeUtils
|
import org.jetbrains.jet.lang.types.TypeUtils
|
||||||
|
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns
|
||||||
|
|
||||||
fun JetType.makeNullable() = TypeUtils.makeNullable(this)
|
fun JetType.makeNullable() = TypeUtils.makeNullable(this)
|
||||||
fun JetType.makeNotNullable() = TypeUtils.makeNotNullable(this)
|
fun JetType.makeNotNullable() = TypeUtils.makeNotNullable(this)
|
||||||
|
|
||||||
|
fun JetType.supertypes(): Set<JetType> = TypeUtils.getAllSupertypes(this)
|
||||||
|
|
||||||
|
fun JetType.isUnit(): Boolean = KotlinBuiltIns.getInstance().isUnit(this)
|
||||||
Reference in New Issue
Block a user