172: Remove 172 specific from psiModificationUtils.kt and KtPsiFactory.kt
This commit is contained in:
@@ -1,848 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,457 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.*
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user