172: Revert "Quick Fixes: Support cross-language "Create from Usage" with Kotlin target"
This reverts commit 908bf71
This commit is contained in:
committed by
Nikolay Krasko
parent
747f397f40
commit
8d3f71a04f
@@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2015 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.resolve.annotations
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
|
||||||
|
import org.jetbrains.kotlin.name.FqName
|
||||||
|
import org.jetbrains.kotlin.name.Name
|
||||||
|
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||||
|
import org.jetbrains.kotlin.resolve.constants.ConstantValue
|
||||||
|
import org.jetbrains.kotlin.resolve.constants.ErrorValue
|
||||||
|
|
||||||
|
private val JVM_STATIC_ANNOTATION_FQ_NAME = FqName("kotlin.jvm.JvmStatic")
|
||||||
|
|
||||||
|
val JVM_FIELD_ANNOTATION_FQ_NAME = FqName("kotlin.jvm.JvmField")
|
||||||
|
|
||||||
|
val JVM_DEFAULT_FQ_NAME = FqName("kotlin.jvm.JvmDefault")
|
||||||
|
|
||||||
|
fun CallableMemberDescriptor.hasJvmDefaultAnnotation() =
|
||||||
|
DescriptorUtils.getDirectMember(this).annotations.hasAnnotation(JVM_DEFAULT_FQ_NAME)
|
||||||
|
|
||||||
|
|
||||||
|
fun DeclarationDescriptor.hasJvmStaticAnnotation(): Boolean {
|
||||||
|
return annotations.findAnnotation(JVM_STATIC_ANNOTATION_FQ_NAME) != null
|
||||||
|
}
|
||||||
|
|
||||||
|
private val JVM_SYNTHETIC_ANNOTATION_FQ_NAME = FqName("kotlin.jvm.JvmSynthetic")
|
||||||
|
|
||||||
|
fun DeclarationDescriptor.hasJvmSyntheticAnnotation() = findJvmSyntheticAnnotation() != null
|
||||||
|
|
||||||
|
fun DeclarationDescriptor.findJvmSyntheticAnnotation() =
|
||||||
|
DescriptorUtils.getAnnotationByFqName(annotations, JVM_SYNTHETIC_ANNOTATION_FQ_NAME)
|
||||||
|
|
||||||
|
private val STRICTFP_ANNOTATION_FQ_NAME = FqName("kotlin.jvm.Strictfp")
|
||||||
|
|
||||||
|
fun DeclarationDescriptor.findStrictfpAnnotation() =
|
||||||
|
DescriptorUtils.getAnnotationByFqName(annotations, STRICTFP_ANNOTATION_FQ_NAME)
|
||||||
|
|
||||||
|
fun AnnotationDescriptor.argumentValue(parameterName: String): ConstantValue<*>? {
|
||||||
|
return allValueArguments[Name.identifier(parameterName)].takeUnless { it is ErrorValue }
|
||||||
|
}
|
||||||
@@ -0,0 +1,848 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||||
|
* that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.psi
|
||||||
|
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.openapi.util.Key
|
||||||
|
import com.intellij.psi.PsiComment
|
||||||
|
import com.intellij.psi.PsiElement
|
||||||
|
import com.intellij.psi.PsiFileFactory
|
||||||
|
import com.intellij.psi.util.PsiTreeUtil
|
||||||
|
import com.intellij.util.LocalTimeCounter
|
||||||
|
import org.jetbrains.kotlin.idea.KotlinFileType
|
||||||
|
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
|
||||||
|
import org.jetbrains.kotlin.lexer.KtTokens
|
||||||
|
import org.jetbrains.kotlin.name.FqName
|
||||||
|
import org.jetbrains.kotlin.name.Name
|
||||||
|
import org.jetbrains.kotlin.resolve.ImportPath
|
||||||
|
|
||||||
|
@JvmOverloads
|
||||||
|
fun KtPsiFactory(project: Project?, markGenerated: Boolean = true): KtPsiFactory = KtPsiFactory(project!!, markGenerated)
|
||||||
|
|
||||||
|
@JvmOverloads
|
||||||
|
fun KtPsiFactory(elementForProject: PsiElement, markGenerated: Boolean = true): KtPsiFactory =
|
||||||
|
KtPsiFactory(elementForProject.project, markGenerated)
|
||||||
|
|
||||||
|
private val DO_NOT_ANALYZE_NOTIFICATION = "This file was created by KtPsiFactory and should not be analyzed\n" +
|
||||||
|
"Use createAnalyzableFile to create file that can be analyzed\n"
|
||||||
|
|
||||||
|
var KtFile.doNotAnalyze: String? by UserDataProperty(Key.create("DO_NOT_ANALYZE"))
|
||||||
|
var KtFile.analysisContext: PsiElement? by UserDataProperty(Key.create("ANALYSIS_CONTEXT"))
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param markGenerated This needs to be set to true if the `KtPsiFactory` is going to be used for creating elements that are going
|
||||||
|
* to be inserted in the user source code (this ensures that the elements will be formatted correctly). In other cases, `markGenerated`
|
||||||
|
* should be false, which saves time and memory.
|
||||||
|
*/
|
||||||
|
class KtPsiFactory @JvmOverloads constructor(private val project: Project, val markGenerated: Boolean = true) {
|
||||||
|
|
||||||
|
fun createValKeyword(): PsiElement {
|
||||||
|
val property = createProperty("val x = 1")
|
||||||
|
return property.valOrVarKeyword
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createVarKeyword(): PsiElement {
|
||||||
|
val property = createProperty("var x = 1")
|
||||||
|
return property.valOrVarKeyword
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun doCreateExpression(text: String): KtExpression? {
|
||||||
|
//NOTE: '\n' below is important - some strange code indenting problems appear without it
|
||||||
|
return createProperty("val x =\n$text").initializer
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createExpression(text: String): KtExpression {
|
||||||
|
val expression = doCreateExpression(text) ?: error("Failed to create expression from text: '$text'")
|
||||||
|
assert(expression.text == text) {
|
||||||
|
"Failed to create expression from text: '$text', resulting expression's text was: '${expression.text}'"
|
||||||
|
}
|
||||||
|
return expression
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createExpressionIfPossible(text: String): KtExpression? {
|
||||||
|
val expression = doCreateExpression(text) ?: return null
|
||||||
|
return if (expression.text == text) expression else null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createThisExpression() =
|
||||||
|
(createExpression("this.x") as KtQualifiedExpression).receiverExpression as KtThisExpression
|
||||||
|
|
||||||
|
fun createThisExpression(qualifier: String) =
|
||||||
|
(createExpression("this@$qualifier.x") as KtQualifiedExpression).receiverExpression as KtThisExpression
|
||||||
|
|
||||||
|
fun createCallArguments(text: String): KtValueArgumentList {
|
||||||
|
val property = createProperty("val x = foo $text")
|
||||||
|
return (property.initializer as KtCallExpression).valueArgumentList!!
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createTypeArguments(text: String): KtTypeArgumentList {
|
||||||
|
val property = createProperty("val x = foo$text()")
|
||||||
|
return (property.initializer as KtCallExpression).typeArgumentList!!
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createTypeArgument(text: String) = createTypeArguments("<$text>").arguments.first()
|
||||||
|
|
||||||
|
fun createType(type: String): KtTypeReference {
|
||||||
|
val typeReference = createTypeIfPossible(type)
|
||||||
|
if (typeReference == null || typeReference.text != type) {
|
||||||
|
throw IllegalArgumentException("Incorrect type: $type")
|
||||||
|
}
|
||||||
|
return typeReference
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createType(typeElement: KtTypeElement) = createType("X").apply { this.typeElement!!.replace(typeElement) }
|
||||||
|
|
||||||
|
fun createTypeIfPossible(type: String): KtTypeReference? {
|
||||||
|
val typeReference = createProperty("val x : $type").typeReference
|
||||||
|
return if (typeReference?.text == type) typeReference else null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createFunctionTypeReceiver(typeReference: KtTypeReference): KtFunctionTypeReceiver {
|
||||||
|
return (createType("A.() -> B").typeElement as KtFunctionType).receiver!!.apply { this.typeReference.replace(typeReference) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createFunctionTypeParameter(typeReference: KtTypeReference): KtParameter {
|
||||||
|
return (createType("(A) -> B").typeElement as KtFunctionType).parameters.first()
|
||||||
|
.apply { this.typeReference!!.replace(typeReference) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createTypeAlias(name: String, typeParameters: List<String>, typeElement: KtTypeElement): KtTypeAlias {
|
||||||
|
return createTypeAlias(name, typeParameters, "X").apply { getTypeReference()!!.replace(createType(typeElement)) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createTypeAlias(name: String, typeParameters: List<String>, body: String): KtTypeAlias {
|
||||||
|
val typeParametersText = if (typeParameters.isNotEmpty()) typeParameters.joinToString(prefix = "<", postfix = ">") else ""
|
||||||
|
return createDeclaration("typealias $name$typeParametersText = $body")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createStar(): PsiElement {
|
||||||
|
return createType("List<*>").findElementAt(5)!!
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createComma(): PsiElement {
|
||||||
|
return createType("T<X, Y>").findElementAt(3)!!
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createDot(): PsiElement {
|
||||||
|
return createType("T.(X)").findElementAt(1)!!
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createColon(): PsiElement {
|
||||||
|
return createProperty("val x: Int").findElementAt(5)!!
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createEQ(): PsiElement {
|
||||||
|
return createFunction("fun foo() = foo").equalsToken!!
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createSemicolon(): PsiElement {
|
||||||
|
return createProperty("val x: Int;").findElementAt(10)!!
|
||||||
|
}
|
||||||
|
|
||||||
|
//the pair contains the first and the last elements of a range
|
||||||
|
fun createWhitespaceAndArrow(): Pair<PsiElement, PsiElement> {
|
||||||
|
val functionType = createType("() -> Int").typeElement as KtFunctionType
|
||||||
|
return Pair(functionType.findElementAt(2)!!, functionType.findElementAt(3)!!)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createWhiteSpace(): PsiElement {
|
||||||
|
return createWhiteSpace(" ")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createWhiteSpace(text: String): PsiElement {
|
||||||
|
return createProperty("val${text}x: Int").findElementAt(3)!!
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove when all Java usages are rewritten to Kotlin
|
||||||
|
fun createNewLine(): PsiElement {
|
||||||
|
return createWhiteSpace("\n ")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createNewLine(lineBreaks: Int): PsiElement {
|
||||||
|
return createWhiteSpace("\n".repeat(lineBreaks))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createClass(text: String): KtClass {
|
||||||
|
return createDeclaration(text)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createObject(text: String): KtObjectDeclaration {
|
||||||
|
return createDeclaration(text)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createCompanionObject(): KtObjectDeclaration {
|
||||||
|
return createCompanionObject("companion object {\n}")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createCompanionObject(text: String): KtObjectDeclaration {
|
||||||
|
return createClass("class A {\n $text\n}").companionObjects.first()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createFileAnnotation(annotationText: String): KtAnnotationEntry {
|
||||||
|
return createFileAnnotationListWithAnnotation(annotationText).annotationEntries.first()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createFileAnnotationListWithAnnotation(annotationText: String): KtFileAnnotationList {
|
||||||
|
return createFile("@file:$annotationText").fileAnnotationList!!
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createFile(text: String): KtFile {
|
||||||
|
return createFile("dummy.kt", text)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun doCreateFile(fileName: String, text: String): KtFile {
|
||||||
|
return PsiFileFactory.getInstance(project).createFileFromText(
|
||||||
|
fileName,
|
||||||
|
KotlinFileType.INSTANCE,
|
||||||
|
text,
|
||||||
|
LocalTimeCounter.currentTime(),
|
||||||
|
false,
|
||||||
|
markGenerated
|
||||||
|
) as KtFile
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createFile(fileName: String, text: String): KtFile {
|
||||||
|
val file = doCreateFile(fileName, text)
|
||||||
|
|
||||||
|
file.doNotAnalyze = DO_NOT_ANALYZE_NOTIFICATION
|
||||||
|
|
||||||
|
return file
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createAnalyzableFile(fileName: String, text: String, contextToAnalyzeIn: PsiElement): KtFile {
|
||||||
|
val file = doCreateFile(fileName, text)
|
||||||
|
file.analysisContext = contextToAnalyzeIn
|
||||||
|
return file
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createFileWithLightClassSupport(fileName: String, text: String, contextToAnalyzeIn: PsiElement): KtFile {
|
||||||
|
val file = createPhysicalFile(fileName, text)
|
||||||
|
file.analysisContext = contextToAnalyzeIn
|
||||||
|
return file
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createPhysicalFile(fileName: String, text: String): KtFile {
|
||||||
|
return PsiFileFactory.getInstance(project).createFileFromText(
|
||||||
|
fileName,
|
||||||
|
KotlinFileType.INSTANCE,
|
||||||
|
text,
|
||||||
|
LocalTimeCounter.currentTime(),
|
||||||
|
true
|
||||||
|
) as KtFile
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createProperty(modifiers: String?, name: String, type: String?, isVar: Boolean, initializer: String?): KtProperty {
|
||||||
|
val text = modifiers.let { "$it " } +
|
||||||
|
(if (isVar) " var " else " val ") + name +
|
||||||
|
(if (type != null) ":" + type else "") + (if (initializer == null) "" else " = " + initializer)
|
||||||
|
return createProperty(text)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createProperty(name: String, type: String?, isVar: Boolean, initializer: String?): KtProperty {
|
||||||
|
return createProperty(null, name, type, isVar, initializer)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createProperty(name: String, type: String?, isVar: Boolean): KtProperty {
|
||||||
|
return createProperty(name, type, isVar, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createProperty(text: String): KtProperty {
|
||||||
|
return createDeclaration(text)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createPropertyGetter(expression: KtExpression): KtPropertyAccessor {
|
||||||
|
val property = createProperty("val x get() = 1")
|
||||||
|
val getter = property.getter!!
|
||||||
|
val bodyExpression = getter.bodyExpression!!
|
||||||
|
|
||||||
|
bodyExpression.replace(expression)
|
||||||
|
return getter
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createPropertySetter(expression: KtExpression): KtPropertyAccessor {
|
||||||
|
val property = if (expression is KtBlockExpression)
|
||||||
|
createProperty("val x get() = 1\nset(value) {\n field = value\n }")
|
||||||
|
else
|
||||||
|
createProperty("val x get() = 1\nset(value) = TODO()")
|
||||||
|
val setter = property.setter!!
|
||||||
|
val bodyExpression = setter.bodyExpression!!
|
||||||
|
|
||||||
|
bodyExpression.replace(expression)
|
||||||
|
return setter
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createDestructuringDeclaration(text: String): KtDestructuringDeclaration {
|
||||||
|
return (createFunction("fun foo() {$text}").bodyExpression as KtBlockExpression).statements.first() as KtDestructuringDeclaration
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createDestructuringParameter(text: String): KtParameter {
|
||||||
|
val dummyFun = createFunction("fun foo() = { $text -> }")
|
||||||
|
return (dummyFun.bodyExpression as KtLambdaExpression).functionLiteral.valueParameters.first()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <TDeclaration : KtDeclaration> createDeclaration(text: String): TDeclaration {
|
||||||
|
val file = createFile(text)
|
||||||
|
val declarations = file.declarations
|
||||||
|
assert(declarations.size == 1) { "${declarations.size} declarations in $text" }
|
||||||
|
return declarations.first() as TDeclaration
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createNameIdentifier(name: String) = createNameIdentifierIfPossible(name)!!
|
||||||
|
|
||||||
|
fun createNameIdentifierIfPossible(name: String) = createProperty(name, null, false).nameIdentifier
|
||||||
|
|
||||||
|
fun createSimpleName(name: String): KtSimpleNameExpression {
|
||||||
|
return createProperty(name, null, false, name).initializer as KtSimpleNameExpression
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createOperationName(name: String): KtSimpleNameExpression {
|
||||||
|
return (createExpression("0 $name 0") as KtBinaryExpression).operationReference
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createIdentifier(name: String): PsiElement {
|
||||||
|
return createSimpleName(name).getIdentifier()!!
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createFunction(funDecl: String): KtNamedFunction {
|
||||||
|
return createDeclaration(funDecl)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createCallableReferenceExpression(text: String) = createExpression(text) as? KtCallableReferenceExpression
|
||||||
|
|
||||||
|
fun createSecondaryConstructor(decl: String): KtSecondaryConstructor {
|
||||||
|
return createClass("class Foo {\n $decl \n}").secondaryConstructors.first()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createModifierList(modifier: KtModifierKeywordToken): KtModifierList {
|
||||||
|
return createModifierList(modifier.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createModifierList(text: String): KtModifierList {
|
||||||
|
return createProperty(text + " val x").modifierList!!
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createModifier(modifier: KtModifierKeywordToken): PsiElement {
|
||||||
|
return createModifierList(modifier.value).getModifier(modifier)!!
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createAnnotationEntry(text: String): KtAnnotationEntry {
|
||||||
|
val modifierList = createProperty(text + " val x").modifierList
|
||||||
|
return modifierList!!.annotationEntries.first()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createEmptyBody(): KtBlockExpression {
|
||||||
|
return createFunction("fun foo() {}").bodyExpression as KtBlockExpression
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createAnonymousInitializer(): KtAnonymousInitializer {
|
||||||
|
return createClass("class A { init {} }").getAnonymousInitializers().first()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createEmptyClassBody(): KtClassBody {
|
||||||
|
return createClass("class A(){}").getBody()!!
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createParameter(text: String): KtParameter {
|
||||||
|
return createClass("class A($text)").primaryConstructorParameters.first()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createParameterList(text: String): KtParameterList {
|
||||||
|
return createFunction("fun foo$text{}").valueParameterList!!
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createTypeParameterList(text: String) = createClass("class Foo$text").typeParameterList!!
|
||||||
|
|
||||||
|
fun createTypeParameter(text: String) = createTypeParameterList("<$text>").parameters.first()!!
|
||||||
|
|
||||||
|
fun createLambdaParameterListIfAny(text: String) =
|
||||||
|
createLambdaExpression(text, "0").functionLiteral.valueParameterList
|
||||||
|
|
||||||
|
fun createLambdaParameterList(text: String) = createLambdaParameterListIfAny(text)!!
|
||||||
|
|
||||||
|
fun createLambdaExpression(parameters: String, body: String): KtLambdaExpression =
|
||||||
|
(if (parameters.isNotEmpty()) createExpression("{ $parameters -> $body }")
|
||||||
|
else createExpression("{ $body }")) as KtLambdaExpression
|
||||||
|
|
||||||
|
|
||||||
|
fun createEnumEntry(text: String): KtEnumEntry {
|
||||||
|
return createDeclaration<KtClass>("enum class E {$text}").declarations[0] as KtEnumEntry
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createEnumEntryInitializerList(): KtInitializerList {
|
||||||
|
return createEnumEntry("Entry()").initializerList!!
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createWhenEntry(entryText: String): KtWhenEntry {
|
||||||
|
val function = createFunction("fun foo() { when(12) { $entryText } }")
|
||||||
|
val whenEntry = PsiTreeUtil.findChildOfType(function, KtWhenEntry::class.java)
|
||||||
|
|
||||||
|
assert(whenEntry != null) { "Couldn't generate when entry" }
|
||||||
|
assert(entryText == whenEntry!!.text) { "Generate when entry text differs from the given text" }
|
||||||
|
|
||||||
|
return whenEntry
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createWhenCondition(conditionText: String): KtWhenCondition {
|
||||||
|
val whenEntry = createWhenEntry("$conditionText -> {}")
|
||||||
|
return whenEntry.conditions[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createBlockStringTemplateEntry(expression: KtExpression): KtStringTemplateEntryWithExpression {
|
||||||
|
// We don't want reformatting here as it can potentially change something in raw strings
|
||||||
|
val stringTemplateExpression = createExpressionByPattern("\"$\${$0}\"", expression, reformat = false) as KtStringTemplateExpression
|
||||||
|
return stringTemplateExpression.entries[0] as KtStringTemplateEntryWithExpression
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createSimpleNameStringTemplateEntry(name: String): KtSimpleNameStringTemplateEntry {
|
||||||
|
val stringTemplateExpression = createExpression("\"\$$name\"") as KtStringTemplateExpression
|
||||||
|
return stringTemplateExpression.entries[0] as KtSimpleNameStringTemplateEntry
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createStringTemplate(content: String) = createExpression("\"$content\"") as KtStringTemplateExpression
|
||||||
|
|
||||||
|
fun createPackageDirective(fqName: FqName): KtPackageDirective {
|
||||||
|
return createFile("package ${fqName.asString()}").packageDirective!!
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createPackageDirectiveIfNeeded(fqName: FqName): KtPackageDirective? {
|
||||||
|
return if (fqName.isRoot) null else createPackageDirective(fqName)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createImportDirective(importPath: ImportPath): KtImportDirective {
|
||||||
|
if (importPath.fqName.isRoot) {
|
||||||
|
throw IllegalArgumentException("import path must not be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
val file = createFile(buildString { appendImport(importPath) })
|
||||||
|
return file.importDirectives.first()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun StringBuilder.appendImport(importPath: ImportPath) {
|
||||||
|
if (importPath.fqName.isRoot) {
|
||||||
|
throw IllegalArgumentException("import path must not be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
append("import ")
|
||||||
|
append(importPath.pathStr)
|
||||||
|
|
||||||
|
val alias = importPath.alias
|
||||||
|
if (alias != null) {
|
||||||
|
append(" as ").append(alias.asString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createImportDirectives(paths: Collection<ImportPath>): List<KtImportDirective> {
|
||||||
|
val fileContent = buildString {
|
||||||
|
for (path in paths) {
|
||||||
|
appendImport(path)
|
||||||
|
append('\n')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val file = createFile(fileContent)
|
||||||
|
return file.importDirectives
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createPrimaryConstructor(text: String = ""): KtPrimaryConstructor {
|
||||||
|
return createClass(if (text.isNotEmpty()) "class A $text" else "class A()").primaryConstructor!!
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createPrimaryConstructorWithModifiers(modifiers: String?): KtPrimaryConstructor {
|
||||||
|
return modifiers?.let { createPrimaryConstructor("$it constructor()") } ?: createPrimaryConstructor()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createConstructorKeyword(): PsiElement =
|
||||||
|
createClass("class A constructor()").primaryConstructor!!.getConstructorKeyword()!!
|
||||||
|
|
||||||
|
fun createLabeledExpression(labelName: String): KtLabeledExpression = createExpression("$labelName@ 1") as KtLabeledExpression
|
||||||
|
|
||||||
|
fun createTypeCodeFragment(text: String, context: PsiElement?): KtTypeCodeFragment {
|
||||||
|
return KtTypeCodeFragment(project, "fragment.kt", text, context)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createExpressionCodeFragment(text: String, context: PsiElement?): KtExpressionCodeFragment {
|
||||||
|
return KtExpressionCodeFragment(project, "fragment.kt", text, null, context)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createBlockCodeFragment(text: String, context: PsiElement?): KtBlockCodeFragment {
|
||||||
|
return KtBlockCodeFragment(project, "fragment.kt", text, null, context)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createIf(condition: KtExpression, thenExpr: KtExpression, elseExpr: KtExpression? = null): KtIfExpression {
|
||||||
|
return (if (elseExpr != null)
|
||||||
|
createExpressionByPattern("if ($0) $1 else $2", condition, thenExpr, elseExpr) as KtIfExpression
|
||||||
|
else
|
||||||
|
createExpressionByPattern("if ($0) $1", condition, thenExpr)) as KtIfExpression
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createArgument(expression: KtExpression?, name: Name? = null, isSpread: Boolean = false): KtValueArgument {
|
||||||
|
val argumentList = buildByPattern({ pattern, args -> createByPattern(pattern, *args) { createCallArguments(it) } }) {
|
||||||
|
appendFixedText("(")
|
||||||
|
|
||||||
|
if (name != null) {
|
||||||
|
appendName(name)
|
||||||
|
appendFixedText("=")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isSpread) {
|
||||||
|
appendFixedText("*")
|
||||||
|
}
|
||||||
|
|
||||||
|
appendExpression(expression)
|
||||||
|
|
||||||
|
appendFixedText(")")
|
||||||
|
}
|
||||||
|
return argumentList.arguments.single()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createArgument(text: String) = createCallArguments("($text)").arguments.first()!!
|
||||||
|
|
||||||
|
fun createSuperTypeCallEntry(text: String): KtSuperTypeCallEntry {
|
||||||
|
return createClass("class A: $text").superTypeListEntries.first() as KtSuperTypeCallEntry
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createSuperTypeEntry(text: String): KtSuperTypeEntry {
|
||||||
|
return createClass("class A: $text").superTypeListEntries.first() as KtSuperTypeEntry
|
||||||
|
}
|
||||||
|
|
||||||
|
fun creareDelegatedSuperTypeEntry(text: String): KtConstructorDelegationCall {
|
||||||
|
val colonOrEmpty = if (text.isEmpty()) "" else ": "
|
||||||
|
return createClass("class A { constructor()$colonOrEmpty$text {}").secondaryConstructors.first().getDelegationCall()
|
||||||
|
}
|
||||||
|
|
||||||
|
class ClassHeaderBuilder {
|
||||||
|
|
||||||
|
enum class State {
|
||||||
|
MODIFIERS,
|
||||||
|
NAME,
|
||||||
|
TYPE_PARAMETERS,
|
||||||
|
BASE_CLASS,
|
||||||
|
TYPE_CONSTRAINTS,
|
||||||
|
DONE
|
||||||
|
}
|
||||||
|
|
||||||
|
private val sb = StringBuilder()
|
||||||
|
private var state = State.MODIFIERS
|
||||||
|
|
||||||
|
fun modifier(modifier: String): ClassHeaderBuilder {
|
||||||
|
assert(state == State.MODIFIERS)
|
||||||
|
|
||||||
|
sb.append(modifier)
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun placeKeyword() {
|
||||||
|
assert(state == State.MODIFIERS)
|
||||||
|
|
||||||
|
if (sb.isNotEmpty()) {
|
||||||
|
sb.append(" ")
|
||||||
|
}
|
||||||
|
sb.append("class ")
|
||||||
|
|
||||||
|
state = State.NAME
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun name(name: String): ClassHeaderBuilder {
|
||||||
|
placeKeyword()
|
||||||
|
|
||||||
|
sb.append(name)
|
||||||
|
state = State.TYPE_PARAMETERS
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun appendInAngleBrackets(values: Collection<String>) {
|
||||||
|
if (values.isNotEmpty()) {
|
||||||
|
sb.append(values.joinToString(", ", "<", ">"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun typeParameters(values: Collection<String>): ClassHeaderBuilder {
|
||||||
|
assert(state == State.TYPE_PARAMETERS)
|
||||||
|
|
||||||
|
appendInAngleBrackets(values)
|
||||||
|
state = State.BASE_CLASS
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun baseClass(name: String, typeArguments: Collection<String>, isInterface: Boolean): ClassHeaderBuilder {
|
||||||
|
assert(state == State.BASE_CLASS)
|
||||||
|
|
||||||
|
sb.append(" : $name")
|
||||||
|
appendInAngleBrackets(typeArguments)
|
||||||
|
if (!isInterface) {
|
||||||
|
sb.append("()")
|
||||||
|
}
|
||||||
|
|
||||||
|
state = State.TYPE_CONSTRAINTS
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun typeConstraints(values: Collection<String>): ClassHeaderBuilder {
|
||||||
|
assert(state == State.TYPE_CONSTRAINTS)
|
||||||
|
|
||||||
|
if (!values.isEmpty()) {
|
||||||
|
sb.append(values.joinToString(", ", " where ", "", -1, ""))
|
||||||
|
}
|
||||||
|
state = State.DONE
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun transform(f: StringBuilder.() -> Unit) = sb.f()
|
||||||
|
|
||||||
|
fun asString(): String {
|
||||||
|
if (state != State.DONE) {
|
||||||
|
state = State.DONE
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CallableBuilder(private val target: Target) {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val CONSTRUCTOR_NAME = KtTokens.CONSTRUCTOR_KEYWORD.value
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class Target {
|
||||||
|
FUNCTION,
|
||||||
|
CONSTRUCTOR,
|
||||||
|
READ_ONLY_PROPERTY
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class State {
|
||||||
|
MODIFIERS,
|
||||||
|
NAME,
|
||||||
|
RECEIVER,
|
||||||
|
FIRST_PARAM,
|
||||||
|
REST_PARAMS,
|
||||||
|
TYPE_CONSTRAINTS,
|
||||||
|
BODY,
|
||||||
|
DONE
|
||||||
|
}
|
||||||
|
|
||||||
|
private val sb = StringBuilder()
|
||||||
|
private var state = State.MODIFIERS
|
||||||
|
|
||||||
|
private fun closeParams() {
|
||||||
|
if (target == Target.FUNCTION || target == Target.CONSTRUCTOR) {
|
||||||
|
assert(state == State.FIRST_PARAM || state == State.REST_PARAMS)
|
||||||
|
sb.append(")")
|
||||||
|
}
|
||||||
|
|
||||||
|
state = State.TYPE_CONSTRAINTS
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun placeKeyword() {
|
||||||
|
assert(state == State.MODIFIERS)
|
||||||
|
|
||||||
|
if (sb.isNotEmpty() && !sb.endsWith(" ")) {
|
||||||
|
sb.append(" ")
|
||||||
|
}
|
||||||
|
val keyword = when (target) {
|
||||||
|
Target.FUNCTION -> "fun"
|
||||||
|
Target.CONSTRUCTOR -> ""
|
||||||
|
Target.READ_ONLY_PROPERTY -> "val"
|
||||||
|
}
|
||||||
|
sb.append("$keyword ")
|
||||||
|
|
||||||
|
state = State.RECEIVER
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun bodyPrefix(breakLine: Boolean = true) = when (target) {
|
||||||
|
Target.FUNCTION, Target.CONSTRUCTOR -> ""
|
||||||
|
Target.READ_ONLY_PROPERTY -> (if (breakLine) "\n" else " ") + "get()"
|
||||||
|
}
|
||||||
|
|
||||||
|
fun modifier(modifier: String): CallableBuilder {
|
||||||
|
assert(state == State.MODIFIERS)
|
||||||
|
|
||||||
|
sb.append(modifier)
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun typeParams(values: Collection<String> = emptyList()): CallableBuilder {
|
||||||
|
placeKeyword()
|
||||||
|
if (!values.isEmpty()) {
|
||||||
|
sb.append(values.joinToString(", ", "<", "> ", -1, ""))
|
||||||
|
}
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun receiver(receiverType: String): CallableBuilder {
|
||||||
|
assert(state == State.RECEIVER)
|
||||||
|
|
||||||
|
sb.append(receiverType).append(".")
|
||||||
|
state = State.NAME
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun name(name: String = CONSTRUCTOR_NAME): CallableBuilder {
|
||||||
|
assert(state == State.NAME || state == State.RECEIVER)
|
||||||
|
assert(name != CONSTRUCTOR_NAME || target == Target.CONSTRUCTOR)
|
||||||
|
|
||||||
|
sb.append(name)
|
||||||
|
state = when (target) {
|
||||||
|
Target.FUNCTION, Target.CONSTRUCTOR -> {
|
||||||
|
sb.append("(")
|
||||||
|
State.FIRST_PARAM
|
||||||
|
}
|
||||||
|
else ->
|
||||||
|
State.TYPE_CONSTRAINTS
|
||||||
|
}
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun param(name: String, type: String, defaultValue: String? = null): CallableBuilder {
|
||||||
|
assert(target == Target.FUNCTION || target == Target.CONSTRUCTOR)
|
||||||
|
assert(state == State.FIRST_PARAM || state == State.REST_PARAMS)
|
||||||
|
|
||||||
|
if (state == State.REST_PARAMS) {
|
||||||
|
sb.append(", ")
|
||||||
|
}
|
||||||
|
sb.append(name).append(": ").append(type)
|
||||||
|
if (defaultValue != null) {
|
||||||
|
sb.append(" = ").append(defaultValue)
|
||||||
|
}
|
||||||
|
if (state == State.FIRST_PARAM) {
|
||||||
|
state = State.REST_PARAMS
|
||||||
|
}
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun returnType(type: String): CallableBuilder {
|
||||||
|
closeParams()
|
||||||
|
sb.append(": ").append(type)
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun noReturnType(): CallableBuilder {
|
||||||
|
closeParams()
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun typeConstraints(values: Collection<String>): CallableBuilder {
|
||||||
|
assert(state == State.TYPE_CONSTRAINTS && target != Target.CONSTRUCTOR)
|
||||||
|
|
||||||
|
if (!values.isEmpty()) {
|
||||||
|
sb.append(values.joinToString(", ", " where ", "", -1, ""))
|
||||||
|
}
|
||||||
|
state = State.BODY
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun superDelegation(argumentList: String): CallableBuilder {
|
||||||
|
assert(state == State.TYPE_CONSTRAINTS && target == Target.CONSTRUCTOR)
|
||||||
|
|
||||||
|
sb.append(": super").append(argumentList)
|
||||||
|
state = State.BODY
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun blockBody(body: String): CallableBuilder {
|
||||||
|
assert(state == State.BODY || state == State.TYPE_CONSTRAINTS)
|
||||||
|
|
||||||
|
sb.append(bodyPrefix()).append(" {\n").append(body).append("\n}")
|
||||||
|
state = State.DONE
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getterExpression(expression: String, breakLine: Boolean = true): CallableBuilder {
|
||||||
|
assert(target == Target.READ_ONLY_PROPERTY)
|
||||||
|
assert(state == State.BODY || state == State.TYPE_CONSTRAINTS)
|
||||||
|
|
||||||
|
sb.append(bodyPrefix(breakLine)).append(" = ").append(expression)
|
||||||
|
state = State.DONE
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun initializer(body: String): CallableBuilder {
|
||||||
|
assert(target == Target.READ_ONLY_PROPERTY && (state == State.BODY || state == State.TYPE_CONSTRAINTS))
|
||||||
|
|
||||||
|
sb.append(" = ").append(body)
|
||||||
|
state = State.DONE
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun lazyBody(body: String): CallableBuilder {
|
||||||
|
assert(target == Target.READ_ONLY_PROPERTY && (state == State.BODY || state == State.TYPE_CONSTRAINTS))
|
||||||
|
|
||||||
|
sb.append(" by kotlin.lazy {\n").append(body).append("\n}")
|
||||||
|
state = State.DONE
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun transform(f: StringBuilder.() -> Unit) = sb.f()
|
||||||
|
|
||||||
|
fun asString(): String {
|
||||||
|
if (state != State.DONE) {
|
||||||
|
state = State.DONE
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createBlock(bodyText: String): KtBlockExpression {
|
||||||
|
return createFunction("fun foo() {\n$bodyText\n}").bodyExpression as KtBlockExpression
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createSingleStatementBlock(statement: KtExpression, prevComment: String? = null, nextComment: String? = null): KtBlockExpression {
|
||||||
|
val prev = if (prevComment == null) "" else " $prevComment "
|
||||||
|
val next = if (nextComment == null) "" else " $nextComment "
|
||||||
|
return createDeclarationByPattern<KtNamedFunction>("fun foo() {\n$prev$0$next\n}", statement).bodyExpression as KtBlockExpression
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createComment(text: String): PsiComment {
|
||||||
|
val file = createFile(text)
|
||||||
|
val comments = file.children.filterIsInstance<PsiComment>()
|
||||||
|
val comment = comments.single()
|
||||||
|
assert(comment.text == text)
|
||||||
|
return comment
|
||||||
|
}
|
||||||
|
|
||||||
|
// special hack used in ControlStructureTypingVisitor
|
||||||
|
// TODO: get rid of it
|
||||||
|
fun wrapInABlockWrapper(expression: KtExpression): KtBlockExpression {
|
||||||
|
if (expression is KtBlockExpression) {
|
||||||
|
return expression
|
||||||
|
}
|
||||||
|
val function = createFunction("fun f() { ${expression.text} }")
|
||||||
|
val block = function.bodyExpression as KtBlockExpression
|
||||||
|
return BlockWrapper(block, expression)
|
||||||
|
}
|
||||||
|
|
||||||
|
private class BlockWrapper(fakeBlockExpression: KtBlockExpression, private val expression: KtExpression) :
|
||||||
|
KtBlockExpression(fakeBlockExpression.node), KtPsiUtil.KtExpressionWrapper {
|
||||||
|
override fun getStatements(): List<KtExpression> {
|
||||||
|
return listOf(expression)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getBaseExpression(): KtExpression {
|
||||||
|
return expression
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,460 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2015 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.core
|
||||||
|
|
||||||
|
import com.intellij.psi.PsiElement
|
||||||
|
import com.intellij.psi.PsiWhiteSpace
|
||||||
|
import com.intellij.psi.impl.source.codeStyle.CodeEditUtil
|
||||||
|
import com.intellij.psi.tree.IElementType
|
||||||
|
import com.intellij.psi.util.PsiTreeUtil
|
||||||
|
import org.jetbrains.kotlin.builtins.isFunctionType
|
||||||
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
|
import org.jetbrains.kotlin.extensions.DeclarationAttributeAltererExtension
|
||||||
|
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||||
|
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
|
||||||
|
import org.jetbrains.kotlin.idea.references.mainReference
|
||||||
|
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
||||||
|
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
|
||||||
|
import org.jetbrains.kotlin.lexer.KtTokens
|
||||||
|
import org.jetbrains.kotlin.name.Name
|
||||||
|
import org.jetbrains.kotlin.psi.*
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.getLambdaArgumentName
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.hasBody
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.visibilityModifierType
|
||||||
|
import org.jetbrains.kotlin.psi.typeRefHelpers.setReceiverTypeReference
|
||||||
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
|
import org.jetbrains.kotlin.resolve.OverridingUtil
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.callUtil.getValueArgumentsInParentheses
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.model.ArgumentMatch
|
||||||
|
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||||
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
|
import org.jetbrains.kotlin.types.isError
|
||||||
|
import org.jetbrains.kotlin.utils.KotlinExceptionWithAttachments
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
inline fun <reified T : PsiElement> PsiElement.replaced(newElement: T): T {
|
||||||
|
val result = replace(newElement)
|
||||||
|
return if (result is T)
|
||||||
|
result
|
||||||
|
else
|
||||||
|
(result as KtParenthesizedExpression).expression as T
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
fun <T : PsiElement> T.copied(): T = copy() as T
|
||||||
|
|
||||||
|
fun KtLambdaArgument.moveInsideParentheses(bindingContext: BindingContext): KtCallExpression {
|
||||||
|
val ktExpression = this.getArgumentExpression()
|
||||||
|
?: throw KotlinExceptionWithAttachments("no argument expression for $this")
|
||||||
|
.withAttachment("lambdaExpression", this.text)
|
||||||
|
return moveInsideParenthesesAndReplaceWith(ktExpression, bindingContext)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun KtLambdaArgument.moveInsideParenthesesAndReplaceWith(
|
||||||
|
replacement: KtExpression,
|
||||||
|
bindingContext: BindingContext
|
||||||
|
): KtCallExpression = moveInsideParenthesesAndReplaceWith(replacement, getLambdaArgumentName(bindingContext))
|
||||||
|
|
||||||
|
|
||||||
|
fun KtLambdaArgument.getLambdaArgumentName(bindingContext: BindingContext): Name? {
|
||||||
|
val callExpression = parent as KtCallExpression
|
||||||
|
val resolvedCall = callExpression.getResolvedCall(bindingContext)
|
||||||
|
return (resolvedCall?.getArgumentMapping(this) as? ArgumentMatch)?.valueParameter?.name
|
||||||
|
}
|
||||||
|
|
||||||
|
fun KtLambdaArgument.moveInsideParenthesesAndReplaceWith(
|
||||||
|
replacement: KtExpression,
|
||||||
|
functionLiteralArgumentName: Name?
|
||||||
|
): KtCallExpression {
|
||||||
|
val oldCallExpression = parent as KtCallExpression
|
||||||
|
val newCallExpression = oldCallExpression.copy() as KtCallExpression
|
||||||
|
|
||||||
|
val psiFactory = KtPsiFactory(project)
|
||||||
|
|
||||||
|
val argument = if (shouldLambdaParameterBeNamed(newCallExpression.getValueArgumentsInParentheses(), oldCallExpression)) {
|
||||||
|
psiFactory.createArgument(replacement, functionLiteralArgumentName)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
psiFactory.createArgument(replacement)
|
||||||
|
}
|
||||||
|
|
||||||
|
val functionLiteralArgument = newCallExpression.lambdaArguments.firstOrNull()!!
|
||||||
|
val valueArgumentList = newCallExpression.valueArgumentList ?: psiFactory.createCallArguments("()")
|
||||||
|
|
||||||
|
valueArgumentList.addArgument(argument)
|
||||||
|
|
||||||
|
(functionLiteralArgument.prevSibling as? PsiWhiteSpace)?.delete()
|
||||||
|
if (newCallExpression.valueArgumentList != null) {
|
||||||
|
functionLiteralArgument.delete()
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
functionLiteralArgument.replace(valueArgumentList)
|
||||||
|
}
|
||||||
|
return oldCallExpression.replace(newCallExpression) as KtCallExpression
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun shouldLambdaParameterBeNamed(args: List<ValueArgument>, callExpr: KtCallExpression): Boolean {
|
||||||
|
if (args.any { it.isNamed() }) return true
|
||||||
|
val calee = (callExpr.calleeExpression?.mainReference?.resolve() as? KtFunction) ?: return true
|
||||||
|
return if (calee.valueParameters.any { it.isVarArg }) true else calee.valueParameters.size - 1 > args.size
|
||||||
|
}
|
||||||
|
|
||||||
|
fun KtCallExpression.getLastLambdaExpression(): KtLambdaExpression? {
|
||||||
|
if (lambdaArguments.isNotEmpty()) return null
|
||||||
|
return valueArguments.lastOrNull()?.getArgumentExpression()?.unpackFunctionLiteral()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun KtCallExpression.canMoveLambdaOutsideParentheses(): Boolean {
|
||||||
|
if (getLastLambdaExpression() == null) return false
|
||||||
|
|
||||||
|
val callee = calleeExpression
|
||||||
|
if (callee is KtNameReferenceExpression) {
|
||||||
|
val bindingContext = analyze(BodyResolveMode.PARTIAL)
|
||||||
|
val targets = bindingContext[BindingContext.REFERENCE_TARGET, callee]?.let { listOf(it) }
|
||||||
|
?: bindingContext[BindingContext.AMBIGUOUS_REFERENCE_TARGET, callee]
|
||||||
|
?: listOf()
|
||||||
|
val candidates = targets.filterIsInstance<FunctionDescriptor>()
|
||||||
|
// if there are functions among candidates but none of them have last function parameter then not show the intention
|
||||||
|
if (candidates.isNotEmpty() && candidates.none {
|
||||||
|
val lastParameter = it.valueParameters.lastOrNull()
|
||||||
|
lastParameter != null && lastParameter.type.isFunctionType
|
||||||
|
}) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun KtCallExpression.moveFunctionLiteralOutsideParentheses() {
|
||||||
|
assert(lambdaArguments.isEmpty())
|
||||||
|
val argumentList = valueArgumentList!!
|
||||||
|
val argument = argumentList.arguments.last()
|
||||||
|
val expression = argument.getArgumentExpression()!!
|
||||||
|
assert(expression.unpackFunctionLiteral() != null)
|
||||||
|
|
||||||
|
val dummyCall = KtPsiFactory(this).createExpressionByPattern("foo()$0:'{}'", expression) as KtCallExpression
|
||||||
|
val functionLiteralArgument = dummyCall.lambdaArguments.single()
|
||||||
|
this.add(functionLiteralArgument)
|
||||||
|
/* we should not remove empty parenthesis when callee is a call too - it won't parse */
|
||||||
|
if (argumentList.arguments.size == 1 && calleeExpression !is KtCallExpression) {
|
||||||
|
argumentList.delete()
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
argumentList.removeArgument(argument)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun KtBlockExpression.appendElement(element: KtElement, addNewLine: Boolean = false): KtElement {
|
||||||
|
val rBrace = rBrace
|
||||||
|
val newLine = KtPsiFactory(this).createNewLine()
|
||||||
|
val anchor = if (rBrace == null) {
|
||||||
|
val lastChild = lastChild
|
||||||
|
if (lastChild !is PsiWhiteSpace) addAfter(newLine, lastChild)!! else lastChild
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
rBrace.prevSibling!!
|
||||||
|
}
|
||||||
|
val addedElement = addAfter(element, anchor)!! as KtElement
|
||||||
|
if (addNewLine) {
|
||||||
|
addAfter(newLine, addedElement)
|
||||||
|
}
|
||||||
|
return addedElement
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: git rid of this method
|
||||||
|
fun PsiElement.deleteElementAndCleanParent() {
|
||||||
|
val parent = parent
|
||||||
|
|
||||||
|
deleteElementWithDelimiters(this)
|
||||||
|
deleteChildlessElement(parent, this::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete element if it doesn't contain children of a given type
|
||||||
|
private fun <T : PsiElement> deleteChildlessElement(element: PsiElement, childClass: Class<T>) {
|
||||||
|
if (PsiTreeUtil.getChildrenOfType<T>(element, childClass) == null) {
|
||||||
|
element.delete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete given element and all the elements separating it from the neighboring elements of the same class
|
||||||
|
private fun deleteElementWithDelimiters(element: PsiElement) {
|
||||||
|
val paramBefore = PsiTreeUtil.getPrevSiblingOfType<PsiElement>(element, element.javaClass)
|
||||||
|
|
||||||
|
val from: PsiElement
|
||||||
|
val to: PsiElement
|
||||||
|
if (paramBefore != null) {
|
||||||
|
from = paramBefore.nextSibling
|
||||||
|
to = element
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
val paramAfter = PsiTreeUtil.getNextSiblingOfType<PsiElement>(element, element.javaClass)
|
||||||
|
|
||||||
|
from = element
|
||||||
|
to = if (paramAfter != null) paramAfter.prevSibling else element
|
||||||
|
}
|
||||||
|
|
||||||
|
val parent = element.parent
|
||||||
|
|
||||||
|
parent.deleteChildRange(from, to)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun PsiElement.deleteSingle() {
|
||||||
|
CodeEditUtil.removeChild(parent?.node ?: return, node ?: return)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun KtClass.getOrCreateCompanionObject(): KtObjectDeclaration {
|
||||||
|
companionObjects.firstOrNull()?.let { return it }
|
||||||
|
return addDeclaration(KtPsiFactory(this).createCompanionObject())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun KtDeclaration.toDescriptor(): DeclarationDescriptor? {
|
||||||
|
val bindingContext = analyze()
|
||||||
|
// TODO: temporary code
|
||||||
|
if (this is KtPrimaryConstructor) {
|
||||||
|
return this.getContainingClassOrObject().resolveToDescriptorIfAny()?.unsubstitutedPrimaryConstructor
|
||||||
|
}
|
||||||
|
|
||||||
|
val descriptor = bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, this]
|
||||||
|
if (descriptor is ValueParameterDescriptor) {
|
||||||
|
return bindingContext[BindingContext.VALUE_PARAMETER_AS_PROPERTY, descriptor]
|
||||||
|
}
|
||||||
|
return descriptor
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: code style option whether to insert redundant 'public' keyword or not
|
||||||
|
fun KtModifierListOwner.setVisibility(visibilityModifier: KtModifierKeywordToken) {
|
||||||
|
if (this is KtDeclaration) {
|
||||||
|
val defaultVisibilityKeyword = implicitVisibility()
|
||||||
|
|
||||||
|
if (visibilityModifier == defaultVisibilityKeyword) {
|
||||||
|
this.visibilityModifierType()?.let { removeModifier(it) }
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addModifier(visibilityModifier)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun KtDeclaration.implicitVisibility(): KtModifierKeywordToken? =
|
||||||
|
when {
|
||||||
|
this is KtConstructor<*> -> {
|
||||||
|
val klass = getContainingClassOrObject()
|
||||||
|
if (klass is KtClass && (klass.isEnum() || klass.isSealed())) KtTokens.PRIVATE_KEYWORD
|
||||||
|
else KtTokens.DEFAULT_VISIBILITY_KEYWORD
|
||||||
|
}
|
||||||
|
hasModifier(KtTokens.OVERRIDE_KEYWORD) -> {
|
||||||
|
(resolveToDescriptorIfAny() as? CallableMemberDescriptor)
|
||||||
|
?.overriddenDescriptors
|
||||||
|
?.let { OverridingUtil.findMaxVisibility(it) }
|
||||||
|
?.toKeywordToken()
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
KtTokens.DEFAULT_VISIBILITY_KEYWORD
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun KtModifierListOwner.canBePrivate() = modifierList?.hasModifier(KtTokens.ABSTRACT_KEYWORD) != true
|
||||||
|
|
||||||
|
fun KtModifierListOwner.canBeProtected(): Boolean {
|
||||||
|
val parent = when (this) {
|
||||||
|
is KtPropertyAccessor -> this.property.parent
|
||||||
|
else -> this.parent
|
||||||
|
}
|
||||||
|
return when (parent) {
|
||||||
|
is KtClassBody -> parent.parent is KtClass
|
||||||
|
is KtParameterList -> parent.parent is KtPrimaryConstructor
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun KtClass.isInheritable(): Boolean {
|
||||||
|
return when (getModalityFromDescriptor()) {
|
||||||
|
KtTokens.ABSTRACT_KEYWORD, KtTokens.OPEN_KEYWORD, KtTokens.SEALED_KEYWORD -> true
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun KtDeclaration.isOverridable(): Boolean {
|
||||||
|
val parent = parent
|
||||||
|
if (!(parent is KtClassBody || parent is KtParameterList)) return false
|
||||||
|
|
||||||
|
val klass = if (parent.parent is KtPrimaryConstructor)
|
||||||
|
parent.parent.parent as? KtClass
|
||||||
|
else
|
||||||
|
parent.parent as? KtClass
|
||||||
|
|
||||||
|
if (klass == null || (!klass.isInheritable() && !klass.isEnum())) return false
|
||||||
|
|
||||||
|
if (this.hasModifier(KtTokens.PRIVATE_KEYWORD)) {
|
||||||
|
// 'private' is incompatible with 'open'
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return when (getModalityFromDescriptor()) {
|
||||||
|
KtTokens.ABSTRACT_KEYWORD, KtTokens.OPEN_KEYWORD -> true
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun KtDeclaration.getModalityFromDescriptor(): KtModifierKeywordToken? {
|
||||||
|
val descriptor = this.resolveToDescriptorIfAny()
|
||||||
|
if (descriptor is MemberDescriptor) {
|
||||||
|
return mapModality(descriptor.modality)
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun KtDeclaration.implicitModality(): KtModifierKeywordToken {
|
||||||
|
var predictedModality = predictImplicitModality()
|
||||||
|
val bindingContext = analyze(BodyResolveMode.PARTIAL)
|
||||||
|
val descriptor = bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, this] ?: return predictedModality
|
||||||
|
val containingDescriptor = descriptor.containingDeclaration ?: return predictedModality
|
||||||
|
|
||||||
|
val extensions = DeclarationAttributeAltererExtension.getInstances(this.project)
|
||||||
|
for (extension in extensions) {
|
||||||
|
val newModality = extension.refineDeclarationModality(
|
||||||
|
this,
|
||||||
|
descriptor as? ClassDescriptor,
|
||||||
|
containingDescriptor,
|
||||||
|
mapModalityToken(predictedModality),
|
||||||
|
bindingContext,
|
||||||
|
isImplicitModality = true)
|
||||||
|
|
||||||
|
if (newModality != null) {
|
||||||
|
predictedModality = mapModality(newModality)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return predictedModality
|
||||||
|
}
|
||||||
|
|
||||||
|
fun mapModality(accurateModality: Modality): KtModifierKeywordToken = when (accurateModality) {
|
||||||
|
Modality.FINAL -> KtTokens.FINAL_KEYWORD
|
||||||
|
Modality.SEALED -> KtTokens.SEALED_KEYWORD
|
||||||
|
Modality.OPEN -> KtTokens.OPEN_KEYWORD
|
||||||
|
Modality.ABSTRACT -> KtTokens.ABSTRACT_KEYWORD
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun mapModalityToken(modalityToken: IElementType): Modality = when (modalityToken) {
|
||||||
|
KtTokens.FINAL_KEYWORD -> Modality.FINAL
|
||||||
|
KtTokens.SEALED_KEYWORD -> Modality.SEALED
|
||||||
|
KtTokens.OPEN_KEYWORD -> Modality.OPEN
|
||||||
|
KtTokens.ABSTRACT_KEYWORD -> Modality.ABSTRACT
|
||||||
|
else -> error("Unexpected modality keyword $modalityToken")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun KtDeclaration.predictImplicitModality(): KtModifierKeywordToken {
|
||||||
|
if (this is KtClassOrObject) {
|
||||||
|
if (this is KtClass && this.isInterface()) return KtTokens.ABSTRACT_KEYWORD
|
||||||
|
return KtTokens.FINAL_KEYWORD
|
||||||
|
}
|
||||||
|
val klass = containingClassOrObject ?: return KtTokens.FINAL_KEYWORD
|
||||||
|
if (hasModifier(KtTokens.OVERRIDE_KEYWORD)) {
|
||||||
|
if (klass.hasModifier(KtTokens.ABSTRACT_KEYWORD) ||
|
||||||
|
klass.hasModifier(KtTokens.OPEN_KEYWORD) ||
|
||||||
|
klass.hasModifier(KtTokens.SEALED_KEYWORD)) {
|
||||||
|
return KtTokens.OPEN_KEYWORD
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (klass is KtClass && klass.isInterface() && !hasModifier(KtTokens.PRIVATE_KEYWORD)) {
|
||||||
|
return if (hasBody()) KtTokens.OPEN_KEYWORD else KtTokens.ABSTRACT_KEYWORD
|
||||||
|
}
|
||||||
|
return KtTokens.FINAL_KEYWORD
|
||||||
|
}
|
||||||
|
|
||||||
|
fun KtSecondaryConstructor.getOrCreateBody(): KtBlockExpression {
|
||||||
|
bodyExpression?.let { return it }
|
||||||
|
|
||||||
|
val delegationCall = getDelegationCall()
|
||||||
|
val anchor = if (delegationCall.isImplicit) valueParameterList else delegationCall
|
||||||
|
val newBody = KtPsiFactory(this).createEmptyBody()
|
||||||
|
return addAfter(newBody, anchor) as KtBlockExpression
|
||||||
|
}
|
||||||
|
|
||||||
|
fun KtParameter.dropDefaultValue() {
|
||||||
|
val from = equalsToken ?: return
|
||||||
|
val to = defaultValue ?: from
|
||||||
|
deleteChildRange(from, to)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun dropEnclosingParenthesesIfPossible(expression: KtExpression): KtExpression {
|
||||||
|
val parent = expression.parent as? KtParenthesizedExpression ?: return expression
|
||||||
|
if (!KtPsiUtil.areParenthesesUseless(parent)) return expression
|
||||||
|
return parent.replaced(expression)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun KtTypeParameterListOwner.addTypeParameter(typeParameter: KtTypeParameter): KtTypeParameter? {
|
||||||
|
typeParameterList?.let { return it.addParameter(typeParameter) }
|
||||||
|
|
||||||
|
val list = KtPsiFactory(this).createTypeParameterList("<X>")
|
||||||
|
list.parameters[0].replace(typeParameter)
|
||||||
|
val leftAnchor = when (this) {
|
||||||
|
is KtClass -> nameIdentifier ?: getClassOrInterfaceKeyword()
|
||||||
|
is KtNamedFunction -> funKeyword
|
||||||
|
is KtProperty -> valOrVarKeyword
|
||||||
|
else -> null
|
||||||
|
} ?: return null
|
||||||
|
return (addAfter(list, leftAnchor) as KtTypeParameterList).parameters.first()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun KtNamedFunction.getOrCreateValueParameterList(): KtParameterList {
|
||||||
|
valueParameterList?.let { return it }
|
||||||
|
val parameterList = KtPsiFactory(this).createParameterList("()")
|
||||||
|
val anchor = nameIdentifier ?: funKeyword!!
|
||||||
|
return addAfter(parameterList, anchor) as KtParameterList
|
||||||
|
}
|
||||||
|
|
||||||
|
fun KtCallableDeclaration.setType(type: KotlinType, shortenReferences: Boolean = true) {
|
||||||
|
if (type.isError) return
|
||||||
|
setType(IdeDescriptorRenderers.SOURCE_CODE.renderType(type), shortenReferences)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun KtCallableDeclaration.setType(typeString: String, shortenReferences: Boolean = true) {
|
||||||
|
val typeReference = KtPsiFactory(project).createType(typeString)
|
||||||
|
setTypeReference(typeReference)
|
||||||
|
if (shortenReferences) {
|
||||||
|
ShortenReferences.DEFAULT.process(getTypeReference()!!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun KtCallableDeclaration.setReceiverType(type: KotlinType) {
|
||||||
|
if (type.isError) return
|
||||||
|
val typeReference = KtPsiFactory(project).createType(IdeDescriptorRenderers.SOURCE_CODE.renderType(type))
|
||||||
|
setReceiverTypeReference(typeReference)
|
||||||
|
ShortenReferences.DEFAULT.process(receiverTypeReference!!)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun KtParameter.setDefaultValue(newDefaultValue: KtExpression): PsiElement? {
|
||||||
|
defaultValue?.let { return it.replaced(newDefaultValue) }
|
||||||
|
|
||||||
|
val psiFactory = KtPsiFactory(this)
|
||||||
|
val eq = equalsToken ?: add(psiFactory.createEQ())
|
||||||
|
return addAfter(newDefaultValue, eq) as KtExpression
|
||||||
|
}
|
||||||
|
|
||||||
|
fun KtBlockStringTemplateEntry.canDropBraces() =
|
||||||
|
expression is KtNameReferenceExpression && canPlaceAfterSimpleNameEntry(nextSibling)
|
||||||
|
|
||||||
|
fun KtBlockStringTemplateEntry.dropBraces(): KtSimpleNameStringTemplateEntry {
|
||||||
|
val name = (expression as KtNameReferenceExpression).getReferencedName()
|
||||||
|
val newEntry = KtPsiFactory(this).createSimpleNameStringTemplateEntry(name)
|
||||||
|
return replaced(newEntry)
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2015 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.quickfix
|
||||||
|
|
||||||
|
import com.intellij.codeInsight.FileModificationService
|
||||||
|
import com.intellij.codeInsight.intention.IntentionAction
|
||||||
|
import com.intellij.openapi.application.ApplicationManager
|
||||||
|
import com.intellij.openapi.editor.Editor
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.psi.PsiElement
|
||||||
|
import com.intellij.psi.PsiFile
|
||||||
|
import org.jetbrains.kotlin.psi.CREATEBYPATTERN_MAY_NOT_REFORMAT
|
||||||
|
import org.jetbrains.kotlin.psi.KtCodeFragment
|
||||||
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.createSmartPointer
|
||||||
|
|
||||||
|
abstract class KotlinQuickFixAction<out T : PsiElement>(element: T) : IntentionAction {
|
||||||
|
private val elementPointer = element.createSmartPointer()
|
||||||
|
|
||||||
|
protected val element: T?
|
||||||
|
get() = elementPointer.element
|
||||||
|
|
||||||
|
final override fun isAvailable(project: Project, editor: Editor?, file: PsiFile): Boolean {
|
||||||
|
if (ApplicationManager.getApplication().isUnitTestMode) {
|
||||||
|
CREATEBYPATTERN_MAY_NOT_REFORMAT = true
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
val element = element ?: return false
|
||||||
|
return element.isValid &&
|
||||||
|
!element.project.isDisposed &&
|
||||||
|
(file.manager.isInProject(file) || file is KtCodeFragment) &&
|
||||||
|
(file is KtFile) &&
|
||||||
|
isAvailable(project, editor, file)
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
CREATEBYPATTERN_MAY_NOT_REFORMAT = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected open fun isAvailable(project: Project, editor: Editor?, file: KtFile): Boolean {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
final override fun invoke(project: Project, editor: Editor?, file: PsiFile) {
|
||||||
|
val element = element ?: return
|
||||||
|
if (file is KtFile && FileModificationService.getInstance().prepareFileForWrite(element.containingFile)) {
|
||||||
|
invoke(project, editor, file)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract fun invoke(project: Project, editor: Editor?, file: KtFile)
|
||||||
|
|
||||||
|
override fun startInWriteAction() = true
|
||||||
|
}
|
||||||
+46
-65
@@ -464,34 +464,16 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
|
|||||||
|
|
||||||
val psiFactory = KtPsiFactory(currentFile)
|
val psiFactory = KtPsiFactory(currentFile)
|
||||||
|
|
||||||
val modifiers = buildString {
|
val modifiers =
|
||||||
val modifierList = callableInfo.modifierList?.copied() ?: psiFactory.createEmptyModifierList()
|
if (callableInfo.isAbstract) {
|
||||||
val visibilityKeyword = modifierList.visibilityModifierType()
|
if (containingElement is KtClass && containingElement.isInterface()) "" else "abstract "
|
||||||
if (visibilityKeyword == null) {
|
}
|
||||||
val defaultVisibility =
|
else if (containingElement is KtClassOrObject
|
||||||
if (callableInfo.isAbstract) ""
|
&& !(containingElement is KtClass && containingElement.isInterface())
|
||||||
else if (containingElement is KtClassOrObject
|
&& containingElement.isAncestor(config.originalElement)
|
||||||
&& !(containingElement is KtClass && containingElement.isInterface())
|
&& callableInfo.kind != CallableKind.CONSTRUCTOR) "private "
|
||||||
&& containingElement.isAncestor(config.originalElement)
|
else if (isExtension) "private "
|
||||||
&& callableInfo.kind != CallableKind.CONSTRUCTOR) "private "
|
else ""
|
||||||
else if (isExtension) "private "
|
|
||||||
else ""
|
|
||||||
append(defaultVisibility)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Get rid of isAbstract
|
|
||||||
if (callableInfo.isAbstract
|
|
||||||
&& containingElement is KtClass
|
|
||||||
&& !containingElement.isInterface()
|
|
||||||
&& !modifierList.hasModifier(KtTokens.ABSTRACT_KEYWORD)) {
|
|
||||||
modifierList.appendModifier(KtTokens.ABSTRACT_KEYWORD)
|
|
||||||
}
|
|
||||||
|
|
||||||
val text = modifierList.normalize().text
|
|
||||||
if (text.isNotEmpty()) {
|
|
||||||
append("$text ")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val isExpectClassMember by lazy {
|
val isExpectClassMember by lazy {
|
||||||
containingElement is KtClassOrObject && containingElement.resolveToDescriptorIfAny()?.isExpect ?: false
|
containingElement is KtClassOrObject && containingElement.resolveToDescriptorIfAny()?.isExpect ?: false
|
||||||
@@ -500,7 +482,7 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
|
|||||||
val declaration: KtNamedDeclaration = when (callableInfo.kind) {
|
val declaration: KtNamedDeclaration = when (callableInfo.kind) {
|
||||||
CallableKind.FUNCTION, CallableKind.CONSTRUCTOR -> {
|
CallableKind.FUNCTION, CallableKind.CONSTRUCTOR -> {
|
||||||
val body = when {
|
val body = when {
|
||||||
callableInfo is ConstructorInfo -> if (callableInfo.withBody) "{\n\n}" else ""
|
callableInfo.kind == CallableKind.CONSTRUCTOR -> ""
|
||||||
callableInfo.isAbstract -> ""
|
callableInfo.isAbstract -> ""
|
||||||
containingElement is KtClass && containingElement.hasModifier(KtTokens.EXTERNAL_KEYWORD) -> ""
|
containingElement is KtClass && containingElement.hasModifier(KtTokens.EXTERNAL_KEYWORD) -> ""
|
||||||
containingElement is KtObjectDeclaration && containingElement.hasModifier(KtTokens.EXTERNAL_KEYWORD) -> ""
|
containingElement is KtObjectDeclaration && containingElement.hasModifier(KtTokens.EXTERNAL_KEYWORD) -> ""
|
||||||
@@ -508,7 +490,7 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
|
|||||||
&& containingElement.parent.parent is KtClass
|
&& containingElement.parent.parent is KtClass
|
||||||
&& (containingElement.parent.parent as KtClass).hasModifier(KtTokens.EXTERNAL_KEYWORD) -> ""
|
&& (containingElement.parent.parent as KtClass).hasModifier(KtTokens.EXTERNAL_KEYWORD) -> ""
|
||||||
isExpectClassMember -> ""
|
isExpectClassMember -> ""
|
||||||
else -> "{\n\n}"
|
else -> "{}"
|
||||||
|
|
||||||
}
|
}
|
||||||
@Suppress("USELESS_CAST") // KT-10755
|
@Suppress("USELESS_CAST") // KT-10755
|
||||||
@@ -574,11 +556,7 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
|
|||||||
return assignmentToReplace.replace(declaration) as KtCallableDeclaration
|
return assignmentToReplace.replace(declaration) as KtCallableDeclaration
|
||||||
}
|
}
|
||||||
|
|
||||||
val container = if (containingElement is KtClass && callableInfo.isForCompanion) {
|
val declarationInPlace = placeDeclarationInContainer(declaration, containingElement, config.originalElement, jetFileToEdit)
|
||||||
containingElement.getOrCreateCompanionObject()
|
|
||||||
}
|
|
||||||
else containingElement
|
|
||||||
val declarationInPlace = placeDeclarationInContainer(declaration, container, config.originalElement, jetFileToEdit)
|
|
||||||
|
|
||||||
if (declarationInPlace is KtSecondaryConstructor) {
|
if (declarationInPlace is KtSecondaryConstructor) {
|
||||||
val containingClass = declarationInPlace.containingClassOrObject!!
|
val containingClass = declarationInPlace.containingClassOrObject!!
|
||||||
@@ -615,11 +593,14 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun setupTypeReferencesForShortening(declaration: KtNamedDeclaration,
|
private fun setupTypeReferencesForShortening(declaration: KtNamedDeclaration,
|
||||||
parameterTypeExpressions: List<TypeExpression>) {
|
parameterTypeExpressions: List<TypeExpression>): List<KtElement> {
|
||||||
|
val typeRefsToShorten = ArrayList<KtElement>()
|
||||||
|
|
||||||
if (config.isExtension) {
|
if (config.isExtension) {
|
||||||
val receiverTypeText = receiverTypeCandidate!!.theType.renderLong(typeParameterNameMap).first()
|
val receiverTypeText = receiverTypeCandidate!!.theType.renderLong(typeParameterNameMap).first()
|
||||||
val replacingTypeRef = KtPsiFactory(declaration).createType(receiverTypeText)
|
val replacingTypeRef = KtPsiFactory(declaration).createType(receiverTypeText)
|
||||||
(declaration as KtCallableDeclaration).setReceiverTypeReference(replacingTypeRef)!!
|
val newTypeRef = (declaration as KtCallableDeclaration).setReceiverTypeReference(replacingTypeRef)!!
|
||||||
|
typeRefsToShorten.add(newTypeRef)
|
||||||
}
|
}
|
||||||
|
|
||||||
val returnTypeRefs = declaration.getReturnTypeReferences()
|
val returnTypeRefs = declaration.getReturnTypeReferences()
|
||||||
@@ -630,6 +611,7 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
|
|||||||
if (returnType != null) {
|
if (returnType != null) {
|
||||||
// user selected a given type
|
// user selected a given type
|
||||||
replaceWithLongerName(returnTypeRefs, returnType)
|
replaceWithLongerName(returnTypeRefs, returnType)
|
||||||
|
typeRefsToShorten.addAll(declaration.getReturnTypeReferences())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -648,6 +630,11 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val expandedValueParameters = declaration.getValueParameters()
|
||||||
|
parameterIndicesToShorten.mapNotNullTo(typeRefsToShorten) { expandedValueParameters[it].typeReference }
|
||||||
|
|
||||||
|
return typeRefsToShorten
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun postprocessDeclaration(declaration: KtNamedDeclaration) {
|
private fun postprocessDeclaration(declaration: KtNamedDeclaration) {
|
||||||
@@ -661,12 +648,15 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun setupDeclarationBody(func: KtDeclarationWithBody) {
|
private fun setupDeclarationBody(func: KtDeclarationWithBody) {
|
||||||
if (func !is KtNamedFunction && func !is KtPropertyAccessor) return
|
|
||||||
if (skipReturnType && callableInfo is FunctionInfo && callableInfo.preferEmptyBody) return
|
|
||||||
val oldBody = func.bodyExpression ?: return
|
val oldBody = func.bodyExpression ?: return
|
||||||
|
val templateKind = when (func) {
|
||||||
|
is KtSecondaryConstructor -> TemplateKind.SECONDARY_CONSTRUCTOR
|
||||||
|
is KtNamedFunction, is KtPropertyAccessor -> TemplateKind.FUNCTION
|
||||||
|
else -> throw AssertionError("Unexpected declaration: " + func.getElementTextWithContext())
|
||||||
|
}
|
||||||
val bodyText = getFunctionBodyTextFromTemplate(
|
val bodyText = getFunctionBodyTextFromTemplate(
|
||||||
func.project,
|
func.project,
|
||||||
TemplateKind.FUNCTION,
|
templateKind,
|
||||||
if (callableInfo.name.isNotEmpty()) callableInfo.name else null,
|
if (callableInfo.name.isNotEmpty()) callableInfo.name else null,
|
||||||
if (skipReturnType) "Unit" else (func as? KtFunction)?.typeReference?.text ?: "",
|
if (skipReturnType) "Unit" else (func as? KtFunction)?.typeReference?.text ?: "",
|
||||||
receiverClassDescriptor?.importableFqName ?: receiverClassDescriptor?.name?.let { FqName.topLevel(it) }
|
receiverClassDescriptor?.importableFqName ?: receiverClassDescriptor?.name?.let { FqName.topLevel(it) }
|
||||||
@@ -773,7 +763,13 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
|
|||||||
|
|
||||||
// add parameter name to the template
|
// add parameter name to the template
|
||||||
val possibleNamesFromExpression = parameter.typeInfo.getPossibleNamesFromExpression(currentFileContext)
|
val possibleNamesFromExpression = parameter.typeInfo.getPossibleNamesFromExpression(currentFileContext)
|
||||||
val possibleNames = arrayOf(*parameter.nameSuggestions.toTypedArray(), *possibleNamesFromExpression)
|
val preferredName = parameter.preferredName
|
||||||
|
val possibleNames = if (preferredName != null) {
|
||||||
|
arrayOf(preferredName, *possibleNamesFromExpression)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
possibleNamesFromExpression
|
||||||
|
}
|
||||||
|
|
||||||
// figure out suggested names for each type option
|
// figure out suggested names for each type option
|
||||||
val parameterTypeToNamesMap = HashMap<String, Array<String>>()
|
val parameterTypeToNamesMap = HashMap<String, Array<String>>()
|
||||||
@@ -967,14 +963,6 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
|
|||||||
|
|
||||||
if (newDeclaration is KtProperty) {
|
if (newDeclaration is KtProperty) {
|
||||||
newDeclaration.getter?.let { setupDeclarationBody(it) }
|
newDeclaration.getter?.let { setupDeclarationBody(it) }
|
||||||
|
|
||||||
if (newDeclaration.getter == null
|
|
||||||
&& newDeclaration.initializer == null
|
|
||||||
&& callableInfo is PropertyInfo
|
|
||||||
&& callableInfo.withInitializer
|
|
||||||
&& !callableInfo.isLateinitPreferred) {
|
|
||||||
newDeclaration.initializer = KtPsiFactory(newDeclaration).createExpression("TODO(\"initialize me\")")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val callElement = config.originalElement as? KtCallElement
|
val callElement = config.originalElement as? KtCallElement
|
||||||
@@ -985,9 +973,9 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
|
|||||||
CodeStyleManager.getInstance(project).reformat(newDeclaration)
|
CodeStyleManager.getInstance(project).reformat(newDeclaration)
|
||||||
|
|
||||||
// change short type names to fully qualified ones (to be shortened below)
|
// change short type names to fully qualified ones (to be shortened below)
|
||||||
setupTypeReferencesForShortening(newDeclaration, parameterTypeExpressions)
|
val typeRefsToShorten = setupTypeReferencesForShortening(newDeclaration, parameterTypeExpressions)
|
||||||
if (!transformToJavaMemberIfApplicable(newDeclaration)) {
|
if (!transformToJavaMemberIfApplicable(newDeclaration)) {
|
||||||
elementsToShorten.add(newDeclaration)
|
elementsToShorten.addAll(typeRefsToShorten)
|
||||||
setupEditor(newDeclaration)
|
setupEditor(newDeclaration)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1114,24 +1102,17 @@ internal fun <D : KtNamedDeclaration> placeDeclarationInContainer(
|
|||||||
}
|
}
|
||||||
|
|
||||||
container is KtClassOrObject -> {
|
container is KtClassOrObject -> {
|
||||||
var sibling: PsiElement? = container.declarations.lastOrNull { it::class == declaration::class }
|
insertMember(null, container, declaration, container.declarations.lastOrNull())
|
||||||
if (sibling == null && declaration is KtProperty) {
|
|
||||||
sibling = container.getBody()?.lBrace
|
|
||||||
}
|
|
||||||
|
|
||||||
insertMember(null, container, declaration, sibling)
|
|
||||||
}
|
}
|
||||||
else -> throw AssertionError("Invalid containing element: ${container.text}")
|
else -> throw AssertionError("Invalid containing element: ${container.text}")
|
||||||
}
|
}
|
||||||
|
|
||||||
if (declaration !is KtPrimaryConstructor) {
|
val parent = declarationInPlace.parent
|
||||||
val parent = declarationInPlace.parent
|
calcNecessaryEmptyLines(declarationInPlace, false).let {
|
||||||
calcNecessaryEmptyLines(declarationInPlace, false).let {
|
if (it > 0) parent.addBefore(psiFactory.createNewLine(it), declarationInPlace)
|
||||||
if (it > 0) parent.addBefore(psiFactory.createNewLine(it), declarationInPlace)
|
}
|
||||||
}
|
calcNecessaryEmptyLines(declarationInPlace, true).let {
|
||||||
calcNecessaryEmptyLines(declarationInPlace, true).let {
|
if (it > 0) parent.addAfter(psiFactory.createNewLine(it), declarationInPlace)
|
||||||
if (it > 0) parent.addAfter(psiFactory.createNewLine(it), declarationInPlace)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return declarationInPlace
|
return declarationInPlace
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-44
@@ -70,10 +70,6 @@ abstract class TypeInfo(val variance: Variance) {
|
|||||||
(builder.placement as CallablePlacement.WithReceiver).receiverTypeCandidate.theType.getPossibleSupertypes(variance, builder)
|
(builder.placement as CallablePlacement.WithReceiver).receiverTypeCandidate.theType.getPossibleSupertypes(variance, builder)
|
||||||
}
|
}
|
||||||
|
|
||||||
class ByExplicitCandidateTypes(val types: List<KotlinType>) : TypeInfo(Variance.INVARIANT) {
|
|
||||||
override fun getPossibleTypes(builder: CallableBuilder) = types
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class DelegatingTypeInfo(val delegate: TypeInfo): TypeInfo(delegate.variance) {
|
abstract class DelegatingTypeInfo(val delegate: TypeInfo): TypeInfo(delegate.variance) {
|
||||||
override val substitutionsAllowed: Boolean = delegate.substitutionsAllowed
|
override val substitutionsAllowed: Boolean = delegate.substitutionsAllowed
|
||||||
override fun getPossibleNamesFromExpression(bindingContext: BindingContext) = delegate.getPossibleNamesFromExpression(bindingContext)
|
override fun getPossibleNamesFromExpression(bindingContext: BindingContext) = delegate.getPossibleNamesFromExpression(bindingContext)
|
||||||
@@ -159,10 +155,8 @@ fun TypeInfo.ofThis() = TypeInfo.OfThis(this)
|
|||||||
*/
|
*/
|
||||||
class ParameterInfo(
|
class ParameterInfo(
|
||||||
val typeInfo: TypeInfo,
|
val typeInfo: TypeInfo,
|
||||||
val nameSuggestions: List<String>
|
val preferredName: String? = null
|
||||||
) {
|
)
|
||||||
constructor(typeInfo: TypeInfo, preferredName: String? = null): this(typeInfo, listOfNotNull(preferredName))
|
|
||||||
}
|
|
||||||
|
|
||||||
enum class CallableKind {
|
enum class CallableKind {
|
||||||
FUNCTION,
|
FUNCTION,
|
||||||
@@ -177,9 +171,7 @@ abstract class CallableInfo (
|
|||||||
val returnTypeInfo: TypeInfo,
|
val returnTypeInfo: TypeInfo,
|
||||||
val possibleContainers: List<KtElement>,
|
val possibleContainers: List<KtElement>,
|
||||||
val typeParameterInfos: List<TypeInfo>,
|
val typeParameterInfos: List<TypeInfo>,
|
||||||
val isAbstract: Boolean = false,
|
val isAbstract: Boolean = false
|
||||||
val isForCompanion: Boolean = false,
|
|
||||||
val modifierList: KtModifierList? = null
|
|
||||||
) {
|
) {
|
||||||
abstract val kind: CallableKind
|
abstract val kind: CallableKind
|
||||||
abstract val parameterInfos: List<ParameterInfo>
|
abstract val parameterInfos: List<ParameterInfo>
|
||||||
@@ -197,11 +189,8 @@ class FunctionInfo(name: String,
|
|||||||
typeParameterInfos: List<TypeInfo> = Collections.emptyList(),
|
typeParameterInfos: List<TypeInfo> = Collections.emptyList(),
|
||||||
val isOperator: Boolean = false,
|
val isOperator: Boolean = false,
|
||||||
val isInfix: Boolean = false,
|
val isInfix: Boolean = false,
|
||||||
isAbstract: Boolean = false,
|
isAbstract: Boolean = false
|
||||||
isForCompanion: Boolean = false,
|
) : CallableInfo(name, receiverTypeInfo, returnTypeInfo, possibleContainers, typeParameterInfos, isAbstract) {
|
||||||
modifierList: KtModifierList? = null,
|
|
||||||
val preferEmptyBody: Boolean = false
|
|
||||||
) : CallableInfo(name, receiverTypeInfo, returnTypeInfo, possibleContainers, typeParameterInfos, isAbstract, isForCompanion, modifierList) {
|
|
||||||
override val kind: CallableKind get() = CallableKind.FUNCTION
|
override val kind: CallableKind get() = CallableKind.FUNCTION
|
||||||
|
|
||||||
override fun copy(receiverTypeInfo: TypeInfo, possibleContainers: List<KtElement>, isAbstract: Boolean) = FunctionInfo(
|
override fun copy(receiverTypeInfo: TypeInfo, possibleContainers: List<KtElement>, isAbstract: Boolean) = FunctionInfo(
|
||||||
@@ -217,12 +206,8 @@ class FunctionInfo(name: String,
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
class ClassWithPrimaryConstructorInfo(
|
class ClassWithPrimaryConstructorInfo(val classInfo: ClassInfo, expectedTypeInfo: TypeInfo): CallableInfo(
|
||||||
val classInfo: ClassInfo,
|
classInfo.name, TypeInfo.Empty, expectedTypeInfo.forceNotNull(), Collections.emptyList(), classInfo.typeArguments, false
|
||||||
expectedTypeInfo: TypeInfo,
|
|
||||||
modifierList: KtModifierList? = null
|
|
||||||
): CallableInfo(
|
|
||||||
classInfo.name, TypeInfo.Empty, expectedTypeInfo.forceNotNull(), Collections.emptyList(), classInfo.typeArguments, false, modifierList = modifierList
|
|
||||||
) {
|
) {
|
||||||
override val kind: CallableKind get() = CallableKind.CLASS_WITH_PRIMARY_CONSTRUCTOR
|
override val kind: CallableKind get() = CallableKind.CLASS_WITH_PRIMARY_CONSTRUCTOR
|
||||||
override val parameterInfos: List<ParameterInfo> get() = classInfo.parameterInfos
|
override val parameterInfos: List<ParameterInfo> get() = classInfo.parameterInfos
|
||||||
@@ -233,10 +218,8 @@ class ClassWithPrimaryConstructorInfo(
|
|||||||
class ConstructorInfo(
|
class ConstructorInfo(
|
||||||
override val parameterInfos: List<ParameterInfo>,
|
override val parameterInfos: List<ParameterInfo>,
|
||||||
val targetClass: PsiElement,
|
val targetClass: PsiElement,
|
||||||
val isPrimary: Boolean = false,
|
val isPrimary: Boolean = false
|
||||||
modifierList: KtModifierList? = null,
|
): CallableInfo("", TypeInfo.Empty, TypeInfo.Empty, Collections.emptyList(), Collections.emptyList(), false) {
|
||||||
val withBody: Boolean = false
|
|
||||||
): CallableInfo("", TypeInfo.Empty, TypeInfo.Empty, Collections.emptyList(), Collections.emptyList(), false, modifierList = modifierList) {
|
|
||||||
override val kind: CallableKind get() = CallableKind.CONSTRUCTOR
|
override val kind: CallableKind get() = CallableKind.CONSTRUCTOR
|
||||||
|
|
||||||
override fun copy(receiverTypeInfo: TypeInfo, possibleContainers: List<KtElement>, isAbstract: Boolean) = throw UnsupportedOperationException()
|
override fun copy(receiverTypeInfo: TypeInfo, possibleContainers: List<KtElement>, isAbstract: Boolean) = throw UnsupportedOperationException()
|
||||||
@@ -249,23 +232,12 @@ class PropertyInfo(name: String,
|
|||||||
possibleContainers: List<KtElement> = Collections.emptyList(),
|
possibleContainers: List<KtElement> = Collections.emptyList(),
|
||||||
typeParameterInfos: List<TypeInfo> = Collections.emptyList(),
|
typeParameterInfos: List<TypeInfo> = Collections.emptyList(),
|
||||||
isAbstract: Boolean = false,
|
isAbstract: Boolean = false,
|
||||||
val isLateinitPreferred: Boolean = false,
|
val isLateinitPreferred: Boolean = false
|
||||||
isForCompanion: Boolean = false,
|
) : CallableInfo(name, receiverTypeInfo, returnTypeInfo, possibleContainers, typeParameterInfos, isAbstract) {
|
||||||
modifierList: KtModifierList? = null,
|
|
||||||
val withInitializer: Boolean = false
|
|
||||||
) : CallableInfo(name, receiverTypeInfo, returnTypeInfo, possibleContainers, typeParameterInfos, isAbstract, isForCompanion, modifierList) {
|
|
||||||
override val kind: CallableKind get() = CallableKind.PROPERTY
|
override val kind: CallableKind get() = CallableKind.PROPERTY
|
||||||
override val parameterInfos: List<ParameterInfo> get() = Collections.emptyList()
|
override val parameterInfos: List<ParameterInfo> get() = Collections.emptyList()
|
||||||
|
|
||||||
override fun copy(receiverTypeInfo: TypeInfo, possibleContainers: List<KtElement>, isAbstract: Boolean) =
|
override fun copy(receiverTypeInfo: TypeInfo, possibleContainers: List<KtElement>, isAbstract: Boolean) = PropertyInfo(
|
||||||
copyProperty(receiverTypeInfo, possibleContainers, isAbstract)
|
|
||||||
|
|
||||||
fun copyProperty(
|
|
||||||
receiverTypeInfo: TypeInfo = this.receiverTypeInfo,
|
|
||||||
possibleContainers: List<KtElement> = this.possibleContainers,
|
|
||||||
isAbstract: Boolean = this.isAbstract,
|
|
||||||
isLateinitPreferred: Boolean = this.isLateinitPreferred
|
|
||||||
) = PropertyInfo(
|
|
||||||
name,
|
name,
|
||||||
receiverTypeInfo,
|
receiverTypeInfo,
|
||||||
returnTypeInfo,
|
returnTypeInfo,
|
||||||
@@ -273,9 +245,6 @@ class PropertyInfo(name: String,
|
|||||||
possibleContainers,
|
possibleContainers,
|
||||||
typeParameterInfos,
|
typeParameterInfos,
|
||||||
isAbstract,
|
isAbstract,
|
||||||
isLateinitPreferred,
|
isLateinitPreferred
|
||||||
isForCompanion,
|
|
||||||
modifierList,
|
|
||||||
withInitializer
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+62
@@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2015 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder
|
||||||
|
|
||||||
|
import com.intellij.openapi.editor.Editor
|
||||||
|
import com.intellij.openapi.editor.ScrollType
|
||||||
|
import com.intellij.psi.PsiElement
|
||||||
|
import com.intellij.psi.PsiWhiteSpace
|
||||||
|
import org.jetbrains.kotlin.psi.*
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.endOffset
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.siblings
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.startOffset
|
||||||
|
|
||||||
|
fun setupEditorSelection(editor: Editor, declaration: KtNamedDeclaration) {
|
||||||
|
val caretModel = editor.caretModel
|
||||||
|
val selectionModel = editor.selectionModel
|
||||||
|
|
||||||
|
if (declaration is KtSecondaryConstructor) {
|
||||||
|
caretModel.moveToOffset(declaration.getConstructorKeyword().endOffset)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
caretModel.moveToOffset(declaration.nameIdentifier!!.endOffset)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun positionBetween(left: PsiElement, right: PsiElement) {
|
||||||
|
val from = left.siblings(withItself = false, forward = true).firstOrNull { it !is PsiWhiteSpace } ?: return
|
||||||
|
val to = right.siblings(withItself = false, forward = false).firstOrNull { it !is PsiWhiteSpace } ?: return
|
||||||
|
val startOffset = from.startOffset
|
||||||
|
val endOffset = to.endOffset
|
||||||
|
caretModel.moveToOffset(endOffset)
|
||||||
|
selectionModel.setSelection(startOffset, endOffset)
|
||||||
|
}
|
||||||
|
|
||||||
|
when (declaration) {
|
||||||
|
is KtNamedFunction, is KtSecondaryConstructor -> {
|
||||||
|
((declaration as KtFunction).bodyExpression as? KtBlockExpression)?.let {
|
||||||
|
positionBetween(it.lBrace!!, it.rBrace!!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is KtClassOrObject -> {
|
||||||
|
caretModel.moveToOffset(declaration.startOffset)
|
||||||
|
}
|
||||||
|
is KtProperty -> {
|
||||||
|
caretModel.moveToOffset(declaration.endOffset)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
editor.scrollingModel.scrollToCaret(ScrollType.CENTER)
|
||||||
|
}
|
||||||
+216
@@ -0,0 +1,216 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2015 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.quickfix.createFromUsage.createCallable
|
||||||
|
|
||||||
|
import com.intellij.codeInsight.intention.LowPriorityAction
|
||||||
|
import com.intellij.openapi.editor.Editor
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.psi.PsiClass
|
||||||
|
import com.intellij.psi.PsiElement
|
||||||
|
import org.jetbrains.kotlin.builtins.functions.FunctionClassDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor
|
||||||
|
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
|
||||||
|
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.CreateFromUsageFixBase
|
||||||
|
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.*
|
||||||
|
import org.jetbrains.kotlin.idea.refactoring.canRefactor
|
||||||
|
import org.jetbrains.kotlin.idea.refactoring.chooseContainerElementIfNecessary
|
||||||
|
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
||||||
|
import org.jetbrains.kotlin.idea.util.application.executeCommand
|
||||||
|
import org.jetbrains.kotlin.idea.util.isAbstract
|
||||||
|
import org.jetbrains.kotlin.psi.*
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
class CreateCallableFromUsageFix<E : KtElement>(
|
||||||
|
originalExpression: E,
|
||||||
|
callableInfos: List<CallableInfo>
|
||||||
|
) : CreateCallableFromUsageFixBase<E>(originalExpression, callableInfos, false)
|
||||||
|
|
||||||
|
class CreateExtensionCallableFromUsageFix<E : KtElement>(
|
||||||
|
originalExpression: E,
|
||||||
|
callableInfos: List<CallableInfo>
|
||||||
|
) : CreateCallableFromUsageFixBase<E>(originalExpression, callableInfos, true), LowPriorityAction
|
||||||
|
|
||||||
|
abstract class CreateCallableFromUsageFixBase<E : KtElement>(
|
||||||
|
originalExpression: E,
|
||||||
|
private val callableInfos: List<CallableInfo>,
|
||||||
|
val isExtension: Boolean
|
||||||
|
) : CreateFromUsageFixBase<E>(originalExpression) {
|
||||||
|
init {
|
||||||
|
assert (callableInfos.isNotEmpty()) { "No CallableInfos: ${originalExpression.getElementTextWithContext()}" }
|
||||||
|
if (callableInfos.size > 1) {
|
||||||
|
val receiverSet = callableInfos.mapTo(HashSet<TypeInfo>()) { it.receiverTypeInfo }
|
||||||
|
if (receiverSet.size > 1) throw AssertionError("All functions must have common receiver: $receiverSet")
|
||||||
|
|
||||||
|
val possibleContainerSet = callableInfos.mapTo(HashSet<List<KtElement>>()) { it.possibleContainers }
|
||||||
|
if (possibleContainerSet.size > 1) throw AssertionError("All functions must have common containers: $possibleContainerSet")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getDeclaration(descriptor: ClassifierDescriptor, project: Project): PsiElement? {
|
||||||
|
if (descriptor is FunctionClassDescriptor) {
|
||||||
|
val psiFactory = KtPsiFactory(project)
|
||||||
|
val syntheticClass = psiFactory.createClass(IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.render(descriptor))
|
||||||
|
return psiFactory.createAnalyzableFile("${descriptor.name.asString()}.kt", "", element!!).add(syntheticClass)
|
||||||
|
}
|
||||||
|
return DescriptorToSourceUtilsIde.getAnyDeclaration(project, descriptor)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getDeclarationIfApplicable(project: Project, candidate: TypeCandidate): PsiElement? {
|
||||||
|
val descriptor = candidate.theType.constructor.declarationDescriptor ?: return null
|
||||||
|
val declaration = getDeclaration(descriptor, project) ?: return null
|
||||||
|
if (declaration !is KtClassOrObject && declaration !is KtTypeParameter && declaration !is PsiClass) return null
|
||||||
|
return if (isExtension || declaration.canRefactor()) declaration else null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getText(): String {
|
||||||
|
val element = element ?: return ""
|
||||||
|
val receiverTypeInfo = callableInfos.first().receiverTypeInfo
|
||||||
|
val renderedCallables = callableInfos.map {
|
||||||
|
buildString {
|
||||||
|
if (it.isAbstract) {
|
||||||
|
append("abstract ")
|
||||||
|
}
|
||||||
|
|
||||||
|
val kind = when (it.kind) {
|
||||||
|
CallableKind.FUNCTION -> "function"
|
||||||
|
CallableKind.PROPERTY -> "property"
|
||||||
|
CallableKind.CONSTRUCTOR -> "secondary constructor"
|
||||||
|
else -> throw AssertionError("Unexpected callable info: $it")
|
||||||
|
}
|
||||||
|
append(kind)
|
||||||
|
|
||||||
|
if (it.name.isNotEmpty()) {
|
||||||
|
append(" '")
|
||||||
|
|
||||||
|
val receiverType = if (!receiverTypeInfo.isOfThis) {
|
||||||
|
CallableBuilderConfiguration(callableInfos, element, isExtension = isExtension)
|
||||||
|
.createBuilder()
|
||||||
|
.computeTypeCandidates(receiverTypeInfo)
|
||||||
|
.firstOrNull()
|
||||||
|
?.theType
|
||||||
|
}
|
||||||
|
else null
|
||||||
|
|
||||||
|
if (receiverType != null) {
|
||||||
|
if (isExtension) {
|
||||||
|
val receiverTypeText = IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.renderType(receiverType)
|
||||||
|
val isFunctionType = receiverType.constructor.declarationDescriptor is FunctionClassDescriptor
|
||||||
|
append(if (isFunctionType) "($receiverTypeText)" else receiverTypeText).append('.')
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
receiverType.constructor.declarationDescriptor?.let {
|
||||||
|
append(IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.renderClassifierName(it)).append('.')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
append("${it.name}'")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return StringBuilder().apply {
|
||||||
|
append("Create ")
|
||||||
|
|
||||||
|
val receiverInfo = receiverTypeInfo
|
||||||
|
if (!callableInfos.any { it.isAbstract }) {
|
||||||
|
if (isExtension) {
|
||||||
|
append("extension ")
|
||||||
|
}
|
||||||
|
else if (receiverInfo !is TypeInfo.Empty) {
|
||||||
|
append("member ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
renderedCallables.joinTo(this)
|
||||||
|
}.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isAvailable(project: Project, editor: Editor?, file: KtFile): Boolean {
|
||||||
|
val element = element ?: return false
|
||||||
|
|
||||||
|
val receiverInfo = callableInfos.first().receiverTypeInfo
|
||||||
|
|
||||||
|
if (receiverInfo is TypeInfo.Empty) {
|
||||||
|
if (callableInfos.any { it is PropertyInfo && it.possibleContainers.isEmpty() }) return false
|
||||||
|
return !isExtension
|
||||||
|
}
|
||||||
|
// TODO: Remove after companion object extensions are supported
|
||||||
|
if (isExtension && receiverInfo.staticContextRequired) return false
|
||||||
|
|
||||||
|
val callableBuilder = CallableBuilderConfiguration(callableInfos, element, isExtension = isExtension).createBuilder()
|
||||||
|
val receiverTypeCandidates = callableBuilder.computeTypeCandidates(callableInfos.first().receiverTypeInfo)
|
||||||
|
val propertyInfo = callableInfos.firstOrNull { it is PropertyInfo } as PropertyInfo?
|
||||||
|
val isFunction = callableInfos.any { it.kind == CallableKind.FUNCTION }
|
||||||
|
return receiverTypeCandidates.any {
|
||||||
|
val declaration = getDeclarationIfApplicable(project, it)
|
||||||
|
val insertToJavaInterface = declaration is PsiClass && declaration.isInterface
|
||||||
|
when {
|
||||||
|
!isExtension && propertyInfo != null && insertToJavaInterface && (!receiverInfo.staticContextRequired || propertyInfo.writable) ->
|
||||||
|
false
|
||||||
|
isFunction && insertToJavaInterface && receiverInfo.staticContextRequired ->
|
||||||
|
false
|
||||||
|
!isExtension && declaration is KtTypeParameter -> false
|
||||||
|
propertyInfo != null && !propertyInfo.isAbstract && declaration is KtClass && declaration.isInterface() -> false
|
||||||
|
else ->
|
||||||
|
declaration != null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun invoke(project: Project, editor: Editor?, file: KtFile) {
|
||||||
|
val element = element ?: return
|
||||||
|
val callableInfo = callableInfos.first()
|
||||||
|
|
||||||
|
val callableBuilder =
|
||||||
|
CallableBuilderConfiguration(callableInfos, element as KtElement, file, editor!!, isExtension).createBuilder()
|
||||||
|
|
||||||
|
fun runBuilder(placement: CallablePlacement) {
|
||||||
|
callableBuilder.placement = placement
|
||||||
|
project.executeCommand(text) { callableBuilder.build() }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (callableInfo is ConstructorInfo) {
|
||||||
|
runBuilder(CallablePlacement.NoReceiver(callableInfo.targetClass))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val popupTitle = "Choose target class or interface"
|
||||||
|
val receiverTypeCandidates = callableBuilder.computeTypeCandidates(callableInfo.receiverTypeInfo).let {
|
||||||
|
if (callableInfo.isAbstract) it.filter { it.theType.isAbstract() } else it
|
||||||
|
}
|
||||||
|
if (receiverTypeCandidates.isNotEmpty()) {
|
||||||
|
val containers = receiverTypeCandidates
|
||||||
|
.mapNotNull { candidate -> getDeclarationIfApplicable(project, candidate)?.let { candidate to it } }
|
||||||
|
|
||||||
|
chooseContainerElementIfNecessary(containers, editor, popupTitle, false, { it.second }) {
|
||||||
|
runBuilder(CallablePlacement.WithReceiver(it.first))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
assert(callableInfo.receiverTypeInfo is TypeInfo.Empty) {
|
||||||
|
"No receiver type candidates: ${element.text} in ${file.text}"
|
||||||
|
}
|
||||||
|
|
||||||
|
chooseContainerElementIfNecessary(callableInfo.possibleContainers, editor, popupTitle, true, { it }) {
|
||||||
|
val container = if (it is KtClassBody) it.parent as KtClassOrObject else it
|
||||||
|
runBuilder(CallablePlacement.NoReceiver(container))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+78
@@ -0,0 +1,78 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2015 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.quickfix.createFromUsage.createCallable
|
||||||
|
|
||||||
|
import com.intellij.psi.SmartPsiElementPointer
|
||||||
|
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||||
|
import org.jetbrains.kotlin.idea.quickfix.IntentionActionPriority
|
||||||
|
import org.jetbrains.kotlin.idea.quickfix.KotlinIntentionActionFactoryWithDelegate
|
||||||
|
import org.jetbrains.kotlin.idea.quickfix.QuickFixWithDelegateFactory
|
||||||
|
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.CreateFromUsageFixBase
|
||||||
|
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.CallableInfo
|
||||||
|
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.PropertyInfo
|
||||||
|
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createVariable.CreateParameterFromUsageFix
|
||||||
|
import org.jetbrains.kotlin.psi.KtElement
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
abstract class CreateCallableMemberFromUsageFactory<E : KtElement>(
|
||||||
|
private val extensionsSupported: Boolean = true
|
||||||
|
) : KotlinIntentionActionFactoryWithDelegate<E, List<CallableInfo>>() {
|
||||||
|
private fun newCallableQuickFix(
|
||||||
|
originalElementPointer: SmartPsiElementPointer<E>,
|
||||||
|
priority: IntentionActionPriority,
|
||||||
|
quickFixDataFactory: () -> List<CallableInfo>?,
|
||||||
|
quickFixFactory: (E, List<CallableInfo>) -> CreateFromUsageFixBase<E>?
|
||||||
|
): QuickFixWithDelegateFactory {
|
||||||
|
return QuickFixWithDelegateFactory(priority) {
|
||||||
|
val data = quickFixDataFactory().orEmpty()
|
||||||
|
val originalElement = originalElementPointer.element
|
||||||
|
if (data.isNotEmpty() && originalElement != null) quickFixFactory(originalElement, data) else null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected open fun createCallableInfo(element: E, diagnostic: Diagnostic): CallableInfo? = null
|
||||||
|
|
||||||
|
override fun extractFixData(element: E, diagnostic: Diagnostic): List<CallableInfo>
|
||||||
|
= listOfNotNull(createCallableInfo(element, diagnostic))
|
||||||
|
|
||||||
|
override fun createFixes(
|
||||||
|
originalElementPointer: SmartPsiElementPointer<E>,
|
||||||
|
diagnostic: Diagnostic,
|
||||||
|
quickFixDataFactory: () -> List<CallableInfo>?
|
||||||
|
): List<QuickFixWithDelegateFactory> {
|
||||||
|
val fixes = ArrayList<QuickFixWithDelegateFactory>(3)
|
||||||
|
|
||||||
|
newCallableQuickFix(originalElementPointer, IntentionActionPriority.NORMAL, quickFixDataFactory) { element, data ->
|
||||||
|
CreateCallableFromUsageFix(element, data)
|
||||||
|
}.let { fixes.add(it) }
|
||||||
|
|
||||||
|
newCallableQuickFix(originalElementPointer, IntentionActionPriority.NORMAL, quickFixDataFactory) f@ { element, data ->
|
||||||
|
(data.singleOrNull() as? PropertyInfo)?.let {
|
||||||
|
CreateParameterFromUsageFix.createFixForPrimaryConstructorPropertyParameter(element, it)
|
||||||
|
}
|
||||||
|
}.let { fixes.add(it) }
|
||||||
|
|
||||||
|
if (extensionsSupported) {
|
||||||
|
newCallableQuickFix(originalElementPointer, IntentionActionPriority.LOW, quickFixDataFactory) { element, data ->
|
||||||
|
if (data.any { it.isAbstract }) return@newCallableQuickFix null
|
||||||
|
CreateExtensionCallableFromUsageFix(element, data)
|
||||||
|
}.let { fixes.add(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
return fixes
|
||||||
|
}
|
||||||
|
}
|
||||||
-390
@@ -1,390 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2010-2017 JetBrains s.r.o.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.jetbrains.kotlin.idea.quickfix.crossLanguage
|
|
||||||
|
|
||||||
import com.intellij.codeInsight.intention.IntentionAction
|
|
||||||
import com.intellij.codeInsight.intention.QuickFixFactory
|
|
||||||
import com.intellij.lang.jvm.*
|
|
||||||
import com.intellij.lang.jvm.actions.*
|
|
||||||
import com.intellij.openapi.project.Project
|
|
||||||
import com.intellij.psi.*
|
|
||||||
import com.intellij.psi.impl.source.tree.java.PsiReferenceExpressionImpl
|
|
||||||
import org.jetbrains.kotlin.asJava.classes.KtLightClassForFacade
|
|
||||||
import org.jetbrains.kotlin.asJava.classes.KtLightClassForSourceDeclaration
|
|
||||||
import org.jetbrains.kotlin.asJava.toLightMethods
|
|
||||||
import org.jetbrains.kotlin.asJava.unwrapped
|
|
||||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
|
||||||
import org.jetbrains.kotlin.descriptors.Modality
|
|
||||||
import org.jetbrains.kotlin.descriptors.SourceElement
|
|
||||||
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
|
||||||
import org.jetbrains.kotlin.descriptors.impl.ClassDescriptorImpl
|
|
||||||
import org.jetbrains.kotlin.descriptors.impl.MutablePackageFragmentDescriptor
|
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
|
|
||||||
import org.jetbrains.kotlin.idea.core.appendModifier
|
|
||||||
import org.jetbrains.kotlin.idea.quickfix.AddModifierFix
|
|
||||||
import org.jetbrains.kotlin.idea.quickfix.RemoveModifierFix
|
|
||||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.*
|
|
||||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createCallable.CreateCallableFromUsageFix
|
|
||||||
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
|
|
||||||
import org.jetbrains.kotlin.idea.util.approximateFlexibleTypes
|
|
||||||
import org.jetbrains.kotlin.lexer.KtTokens
|
|
||||||
import org.jetbrains.kotlin.load.java.components.TypeUsage
|
|
||||||
import org.jetbrains.kotlin.load.java.lazy.JavaResolverComponents
|
|
||||||
import org.jetbrains.kotlin.load.java.lazy.LazyJavaResolverContext
|
|
||||||
import org.jetbrains.kotlin.load.java.lazy.TypeParameterResolver
|
|
||||||
import org.jetbrains.kotlin.load.java.lazy.child
|
|
||||||
import org.jetbrains.kotlin.load.java.lazy.descriptors.LazyJavaTypeParameterDescriptor
|
|
||||||
import org.jetbrains.kotlin.load.java.lazy.types.JavaTypeAttributes
|
|
||||||
import org.jetbrains.kotlin.load.java.lazy.types.JavaTypeResolver
|
|
||||||
import org.jetbrains.kotlin.load.java.structure.JavaTypeParameter
|
|
||||||
import org.jetbrains.kotlin.load.java.structure.impl.JavaTypeImpl
|
|
||||||
import org.jetbrains.kotlin.load.java.structure.impl.JavaTypeParameterImpl
|
|
||||||
import org.jetbrains.kotlin.name.FqName
|
|
||||||
import org.jetbrains.kotlin.name.Name
|
|
||||||
import org.jetbrains.kotlin.psi.*
|
|
||||||
import org.jetbrains.kotlin.resolve.annotations.JVM_FIELD_ANNOTATION_FQ_NAME
|
|
||||||
import org.jetbrains.kotlin.resolve.annotations.JVM_STATIC_ANNOTATION_FQ_NAME
|
|
||||||
import org.jetbrains.kotlin.types.KotlinType
|
|
||||||
import org.jetbrains.kotlin.types.Variance
|
|
||||||
import org.jetbrains.kotlin.types.typeUtil.supertypes
|
|
||||||
|
|
||||||
class KotlinElementActionsFactory : JvmElementActionsFactory() {
|
|
||||||
companion object {
|
|
||||||
val javaPsiModifiersMapping = mapOf(
|
|
||||||
JvmModifier.PRIVATE to KtTokens.PRIVATE_KEYWORD,
|
|
||||||
JvmModifier.PUBLIC to KtTokens.PUBLIC_KEYWORD,
|
|
||||||
JvmModifier.PROTECTED to KtTokens.PUBLIC_KEYWORD,
|
|
||||||
JvmModifier.ABSTRACT to KtTokens.ABSTRACT_KEYWORD
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private class FakeExpressionFromParameter(private val psiParam: PsiParameter) : PsiReferenceExpressionImpl() {
|
|
||||||
override fun getText(): String = psiParam.name!!
|
|
||||||
override fun getProject(): Project = psiParam.project
|
|
||||||
override fun getParent(): PsiElement = psiParam.parent
|
|
||||||
override fun getType(): PsiType? = psiParam.type
|
|
||||||
override fun isValid(): Boolean = true
|
|
||||||
override fun getContainingFile(): PsiFile = psiParam.containingFile
|
|
||||||
override fun getReferenceName(): String? = psiParam.name
|
|
||||||
override fun resolve(): PsiElement? = psiParam
|
|
||||||
}
|
|
||||||
|
|
||||||
private class ModifierBuilder(
|
|
||||||
private val targetContainer: KtElement,
|
|
||||||
private val allowJvmStatic: Boolean = true
|
|
||||||
) {
|
|
||||||
private val psiFactory = KtPsiFactory(targetContainer.project)
|
|
||||||
|
|
||||||
val modifierList = psiFactory.createEmptyModifierList()
|
|
||||||
|
|
||||||
private fun JvmModifier.transformAndAppend(): Boolean {
|
|
||||||
javaPsiModifiersMapping[this]?.let {
|
|
||||||
modifierList.appendModifier(it)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
when (this) {
|
|
||||||
JvmModifier.STATIC -> {
|
|
||||||
if (allowJvmStatic && targetContainer is KtClassOrObject) {
|
|
||||||
addAnnotation(JVM_STATIC_ANNOTATION_FQ_NAME)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
JvmModifier.ABSTRACT -> modifierList.appendModifier(KtTokens.ABSTRACT_KEYWORD)
|
|
||||||
JvmModifier.FINAL -> modifierList.appendModifier(KtTokens.FINAL_KEYWORD)
|
|
||||||
else -> return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
var isValid = true
|
|
||||||
private set
|
|
||||||
|
|
||||||
fun addJvmModifier(modifier: JvmModifier) {
|
|
||||||
isValid = isValid && modifier.transformAndAppend()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun addJvmModifiers(modifiers: Iterable<JvmModifier>) {
|
|
||||||
modifiers.forEach { addJvmModifier(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
fun addAnnotation(fqName: FqName) {
|
|
||||||
if (!isValid) return
|
|
||||||
modifierList.add(psiFactory.createAnnotationEntry("@${fqName.asString()}"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class CreatePropertyFix(
|
|
||||||
private val targetClass: JvmClass,
|
|
||||||
contextElement: KtElement,
|
|
||||||
propertyInfo: PropertyInfo
|
|
||||||
) : CreateCallableFromUsageFix<KtElement>(contextElement, listOf(propertyInfo)) {
|
|
||||||
override fun getFamilyName() = "Add property"
|
|
||||||
override fun getText(): String {
|
|
||||||
val info = callableInfos.first() as PropertyInfo
|
|
||||||
return buildString {
|
|
||||||
append("Add '")
|
|
||||||
if (info.isLateinitPreferred) {
|
|
||||||
append("lateinit ")
|
|
||||||
}
|
|
||||||
append(if (info.writable) "var" else "val")
|
|
||||||
append("' property '${info.name}' to '${targetClass.name}'")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun JvmClass.toKtClassOrFile(): KtElement? {
|
|
||||||
val psi = sourceElement
|
|
||||||
return when (psi) {
|
|
||||||
is KtClassOrObject -> psi
|
|
||||||
is KtLightClassForSourceDeclaration -> psi.kotlinOrigin
|
|
||||||
is KtLightClassForFacade -> psi.files.firstOrNull()
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private inline fun <reified T : KtElement> JvmElement.toKtElement() = sourceElement?.unwrapped as? T
|
|
||||||
|
|
||||||
private fun fakeParametersExpressions(parameters: List<JvmParameter>, project: Project): Array<PsiExpression>? =
|
|
||||||
when {
|
|
||||||
parameters.isEmpty() -> emptyArray()
|
|
||||||
else -> JavaPsiFacade
|
|
||||||
.getElementFactory(project)
|
|
||||||
.createParameterList(
|
|
||||||
parameters.map { it.name }.toTypedArray(),
|
|
||||||
parameters.map { it.type as? PsiType ?: return null }.toTypedArray()
|
|
||||||
)
|
|
||||||
.parameters
|
|
||||||
.map(::FakeExpressionFromParameter)
|
|
||||||
.toTypedArray()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun PsiType.collectTypeParameters(): List<PsiTypeParameter> {
|
|
||||||
val results = ArrayList<PsiTypeParameter>()
|
|
||||||
accept(
|
|
||||||
object : PsiTypeVisitor<Unit>() {
|
|
||||||
override fun visitArrayType(arrayType: PsiArrayType) {
|
|
||||||
arrayType.componentType.accept(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visitClassType(classType: PsiClassType) {
|
|
||||||
(classType.resolve() as? PsiTypeParameter)?.let { results += it }
|
|
||||||
classType.parameters.forEach { it.accept(this) }
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visitWildcardType(wildcardType: PsiWildcardType) {
|
|
||||||
wildcardType.bound?.accept(this)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
return results
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun PsiType.resolveToKotlinType(resolutionFacade: ResolutionFacade): KotlinType? {
|
|
||||||
val typeParameters = collectTypeParameters()
|
|
||||||
val components = resolutionFacade.getFrontendService(JavaResolverComponents::class.java)
|
|
||||||
val rootContext = LazyJavaResolverContext(components, TypeParameterResolver.EMPTY) { null }
|
|
||||||
val dummyPackageDescriptor = MutablePackageFragmentDescriptor(resolutionFacade.moduleDescriptor, FqName("dummy"))
|
|
||||||
val dummyClassDescriptor = ClassDescriptorImpl(
|
|
||||||
dummyPackageDescriptor,
|
|
||||||
Name.identifier("Dummy"),
|
|
||||||
Modality.FINAL,
|
|
||||||
ClassKind.CLASS,
|
|
||||||
emptyList(),
|
|
||||||
SourceElement.NO_SOURCE,
|
|
||||||
false
|
|
||||||
)
|
|
||||||
val typeParameterResolver = object : TypeParameterResolver {
|
|
||||||
override fun resolveTypeParameter(javaTypeParameter: JavaTypeParameter): TypeParameterDescriptor? {
|
|
||||||
val psiTypeParameter = (javaTypeParameter as JavaTypeParameterImpl).psi
|
|
||||||
val index = typeParameters.indexOf(psiTypeParameter)
|
|
||||||
if (index < 0) return null
|
|
||||||
return LazyJavaTypeParameterDescriptor(rootContext.child(this), javaTypeParameter, index, dummyClassDescriptor)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val typeResolver = JavaTypeResolver(rootContext, typeParameterResolver)
|
|
||||||
val attributes = JavaTypeAttributes(TypeUsage.COMMON)
|
|
||||||
return typeResolver.transformJavaType(JavaTypeImpl.create(this), attributes).approximateFlexibleTypes(preferNotNull = true)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun ExpectedTypes.toKotlinTypeInfo(resolutionFacade: ResolutionFacade): TypeInfo {
|
|
||||||
val candidateTypes = flatMapTo(LinkedHashSet<KotlinType>()) {
|
|
||||||
val ktType = (it.theType as? PsiType)?.resolveToKotlinType(resolutionFacade) ?: return@flatMapTo emptyList()
|
|
||||||
when (it.theKind) {
|
|
||||||
ExpectedType.Kind.EXACT, ExpectedType.Kind.SUBTYPE -> listOf(ktType)
|
|
||||||
ExpectedType.Kind.SUPERTYPE -> listOf(ktType) + ktType.supertypes()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (candidateTypes.isEmpty()) {
|
|
||||||
val nullableAnyType = resolutionFacade.moduleDescriptor.builtIns.nullableAnyType
|
|
||||||
return TypeInfo(nullableAnyType, Variance.INVARIANT)
|
|
||||||
}
|
|
||||||
return TypeInfo.ByExplicitCandidateTypes(candidateTypes.toList())
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun createChangeModifierActions(target: JvmModifiersOwner, request: MemberRequest.Modifier): List<IntentionAction> {
|
|
||||||
val kModifierOwner = target.toKtElement<KtModifierListOwner>() ?: return emptyList()
|
|
||||||
|
|
||||||
val modifier = request.modifier
|
|
||||||
val shouldPresent = request.shouldPresent
|
|
||||||
val (kToken, shouldPresentMapped) = if (JvmModifier.FINAL == modifier)
|
|
||||||
KtTokens.OPEN_KEYWORD to !shouldPresent
|
|
||||||
else
|
|
||||||
javaPsiModifiersMapping[modifier] to shouldPresent
|
|
||||||
if (kToken == null) return emptyList()
|
|
||||||
|
|
||||||
val action = if (shouldPresentMapped)
|
|
||||||
AddModifierFix.createIfApplicable(kModifierOwner, kToken)
|
|
||||||
else
|
|
||||||
RemoveModifierFix(kModifierOwner, kToken, false)
|
|
||||||
return listOfNotNull(action)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun createAddConstructorActions(targetClass: JvmClass, request: MemberRequest.Constructor): List<IntentionAction> {
|
|
||||||
val targetKtClass = targetClass.toKtClassOrFile() as? KtClass ?: return emptyList()
|
|
||||||
|
|
||||||
if (request.typeParameters.isNotEmpty()) return emptyList()
|
|
||||||
|
|
||||||
val modifierBuilder = ModifierBuilder(targetKtClass).apply { addJvmModifiers(request.modifiers) }
|
|
||||||
if (!modifierBuilder.isValid) return emptyList()
|
|
||||||
|
|
||||||
val resolutionFacade = targetKtClass.getResolutionFacade()
|
|
||||||
val nullableAnyType = resolutionFacade.moduleDescriptor.builtIns.nullableAnyType
|
|
||||||
val parameterInfos = request.parameters.mapIndexed { index, param ->
|
|
||||||
val ktType = (param.type as? PsiType)?.resolveToKotlinType(resolutionFacade) ?: nullableAnyType
|
|
||||||
val name = param.name ?: "arg${index + 1}"
|
|
||||||
ParameterInfo(TypeInfo(ktType, Variance.IN_VARIANCE), listOf(name))
|
|
||||||
}
|
|
||||||
val needPrimary = !targetKtClass.hasExplicitPrimaryConstructor()
|
|
||||||
val constructorInfo = ConstructorInfo(
|
|
||||||
parameterInfos,
|
|
||||||
targetKtClass,
|
|
||||||
isPrimary = needPrimary,
|
|
||||||
modifierList = modifierBuilder.modifierList,
|
|
||||||
withBody = true
|
|
||||||
)
|
|
||||||
val addConstructorAction = object : CreateCallableFromUsageFix<KtElement>(targetKtClass, listOf(constructorInfo)) {
|
|
||||||
override fun getFamilyName() = "Add method"
|
|
||||||
override fun getText() = "Add ${if (needPrimary) "primary" else "secondary"} constructor to '${targetClass.name}'"
|
|
||||||
}
|
|
||||||
|
|
||||||
val changePrimaryConstructorAction = run {
|
|
||||||
val primaryConstructor = targetKtClass.primaryConstructor ?: return@run null
|
|
||||||
val lightMethod = primaryConstructor.toLightMethods().firstOrNull() ?: return@run null
|
|
||||||
val project = targetKtClass.project
|
|
||||||
val fakeParametersExpressions = fakeParametersExpressions(request.parameters, project) ?: return@run null
|
|
||||||
QuickFixFactory.getInstance()
|
|
||||||
.createChangeMethodSignatureFromUsageFix(
|
|
||||||
lightMethod,
|
|
||||||
fakeParametersExpressions,
|
|
||||||
PsiSubstitutor.EMPTY,
|
|
||||||
targetKtClass,
|
|
||||||
false,
|
|
||||||
2
|
|
||||||
).takeIf { it.isAvailable(project, null, targetKtClass.containingFile) }
|
|
||||||
}
|
|
||||||
|
|
||||||
return listOfNotNull(changePrimaryConstructorAction, addConstructorAction)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun createAddPropertyActions(targetClass: JvmClass, request: MemberRequest.Property): List<IntentionAction> {
|
|
||||||
val targetContainer = targetClass.toKtClassOrFile() ?: return emptyList()
|
|
||||||
|
|
||||||
val modifierBuilder = ModifierBuilder(targetContainer).apply { addJvmModifier(request.visibilityModifier) }
|
|
||||||
if (!modifierBuilder.isValid) return emptyList()
|
|
||||||
|
|
||||||
val resolutionFacade = targetContainer.getResolutionFacade()
|
|
||||||
val nullableAnyType = resolutionFacade.moduleDescriptor.builtIns.nullableAnyType
|
|
||||||
val ktType = (request.propertyType as? PsiType)?.resolveToKotlinType(resolutionFacade) ?: nullableAnyType
|
|
||||||
val propertyInfo = PropertyInfo(
|
|
||||||
request.propertyName,
|
|
||||||
TypeInfo.Empty,
|
|
||||||
TypeInfo(ktType, Variance.INVARIANT),
|
|
||||||
request.setterRequired,
|
|
||||||
listOf(targetContainer),
|
|
||||||
modifierList = modifierBuilder.modifierList,
|
|
||||||
withInitializer = true
|
|
||||||
)
|
|
||||||
val propertyInfos = if (request.setterRequired) {
|
|
||||||
listOf(propertyInfo, propertyInfo.copyProperty(isLateinitPreferred = true))
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
listOf(propertyInfo)
|
|
||||||
}
|
|
||||||
return propertyInfos.map { CreatePropertyFix(targetClass, targetContainer, it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun createAddFieldActions(targetClass: JvmClass, request: CreateFieldRequest): List<IntentionAction> {
|
|
||||||
val targetContainer = targetClass.toKtClassOrFile() ?: return emptyList()
|
|
||||||
|
|
||||||
val modifierBuilder = ModifierBuilder(targetContainer, allowJvmStatic = false).apply {
|
|
||||||
addJvmModifiers(request.modifiers)
|
|
||||||
addAnnotation(JVM_FIELD_ANNOTATION_FQ_NAME)
|
|
||||||
}
|
|
||||||
if (!modifierBuilder.isValid) return emptyList()
|
|
||||||
|
|
||||||
val resolutionFacade = targetContainer.getResolutionFacade()
|
|
||||||
val typeInfo = request.fieldType.toKotlinTypeInfo(resolutionFacade)
|
|
||||||
val writable = JvmModifier.FINAL !in request.modifiers
|
|
||||||
val propertyInfo = PropertyInfo(
|
|
||||||
request.fieldName,
|
|
||||||
TypeInfo.Empty,
|
|
||||||
typeInfo,
|
|
||||||
writable,
|
|
||||||
listOf(targetContainer),
|
|
||||||
isForCompanion = JvmModifier.STATIC in request.modifiers,
|
|
||||||
modifierList = modifierBuilder.modifierList,
|
|
||||||
withInitializer = true
|
|
||||||
)
|
|
||||||
val propertyInfos = if (writable) {
|
|
||||||
listOf(propertyInfo, propertyInfo.copyProperty(isLateinitPreferred = true))
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
listOf(propertyInfo)
|
|
||||||
}
|
|
||||||
return propertyInfos.map { CreatePropertyFix(targetClass, targetContainer, it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun createAddMethodActions(targetClass: JvmClass, request: CreateMethodRequest): List<IntentionAction> {
|
|
||||||
val targetContainer = targetClass.toKtClassOrFile() ?: return emptyList()
|
|
||||||
|
|
||||||
val modifierBuilder = ModifierBuilder(targetContainer).apply { addJvmModifiers(request.modifiers) }
|
|
||||||
if (!modifierBuilder.isValid) return emptyList()
|
|
||||||
|
|
||||||
val resolutionFacade = targetContainer.getResolutionFacade()
|
|
||||||
val returnTypeInfo = request.returnType.toKotlinTypeInfo(resolutionFacade)
|
|
||||||
val parameterInfos = request.parameters.map { (suggestedNames, expectedTypes) ->
|
|
||||||
ParameterInfo(expectedTypes.toKotlinTypeInfo(resolutionFacade), suggestedNames.names.toList())
|
|
||||||
}
|
|
||||||
val functionInfo = FunctionInfo(
|
|
||||||
request.methodName,
|
|
||||||
TypeInfo.Empty,
|
|
||||||
returnTypeInfo,
|
|
||||||
listOf(targetContainer),
|
|
||||||
parameterInfos,
|
|
||||||
isAbstract = JvmModifier.ABSTRACT in request.modifiers,
|
|
||||||
isForCompanion = JvmModifier.STATIC in request.modifiers,
|
|
||||||
modifierList = modifierBuilder.modifierList,
|
|
||||||
preferEmptyBody = true
|
|
||||||
)
|
|
||||||
val action = object : CreateCallableFromUsageFix<KtElement>(targetContainer, listOf(functionInfo)) {
|
|
||||||
override fun getFamilyName() = "Add method"
|
|
||||||
override fun getText() = "Add method '${request.methodName}' to '${targetClass.name}'"
|
|
||||||
}
|
|
||||||
return listOf(action)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Vendored
Vendored
Vendored
Vendored
Vendored
Vendored
Vendored
Vendored
+2061
File diff suppressed because it is too large
Load Diff
@@ -54,8 +54,6 @@ open class KotlinUClass private constructor(
|
|||||||
|
|
||||||
override val psi = unwrap<UClass, PsiClass>(psi)
|
override val psi = unwrap<UClass, PsiClass>(psi)
|
||||||
|
|
||||||
override fun getSourceElement() = sourcePsi ?: this
|
|
||||||
|
|
||||||
override fun getOriginalElement(): PsiElement? = super.getOriginalElement()
|
override fun getOriginalElement(): PsiElement? = super.getOriginalElement()
|
||||||
|
|
||||||
override fun getNameIdentifier(): PsiIdentifier? = UastLightIdentifier(psi, ktClass)
|
override fun getNameIdentifier(): PsiIdentifier? = UastLightIdentifier(psi, ktClass)
|
||||||
|
|||||||
Reference in New Issue
Block a user