182: KotlinElementActionsFactory.createAddAnnotationActions implementation (KT-22876)
This commit is contained in:
committed by
Nikolay Krasko
parent
d5f26f2d8d
commit
41c30f06bb
+457
@@ -0,0 +1,457 @@
|
|||||||
|
/*
|
||||||
|
* 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.daemon.QuickFixBundle
|
||||||
|
import com.intellij.codeInsight.intention.IntentionAction
|
||||||
|
import com.intellij.codeInsight.intention.QuickFixFactory
|
||||||
|
import com.intellij.lang.jvm.JvmClass
|
||||||
|
import com.intellij.lang.jvm.JvmElement
|
||||||
|
import com.intellij.lang.jvm.JvmModifier
|
||||||
|
import com.intellij.lang.jvm.JvmModifiersOwner
|
||||||
|
import com.intellij.lang.jvm.actions.*
|
||||||
|
import com.intellij.openapi.editor.Editor
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.openapi.util.text.StringUtilRt
|
||||||
|
import com.intellij.psi.*
|
||||||
|
import com.intellij.psi.codeStyle.SuggestedNameInfo
|
||||||
|
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.elements.KtLightElement
|
||||||
|
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.KotlinLanguage
|
||||||
|
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
|
||||||
|
import org.jetbrains.kotlin.idea.core.ShortenReferences
|
||||||
|
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.psi.psiUtil.createSmartPointer
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.visibilityModifierType
|
||||||
|
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<Pair<SuggestedNameInfo, List<ExpectedType>>>, project: Project): Array<PsiExpression>? =
|
||||||
|
when {
|
||||||
|
parameters.isEmpty() -> emptyArray()
|
||||||
|
else -> JavaPsiFacade
|
||||||
|
.getElementFactory(project)
|
||||||
|
.createParameterList(
|
||||||
|
parameters.map { it.first.names.firstOrNull() }.toTypedArray(),
|
||||||
|
parameters.map { JvmPsiConversionHelper.getInstance(project).asPsiType(it) ?: 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
|
||||||
|
//TODO: make similar to `createAddMethodActions`
|
||||||
|
val (kToken, shouldPresentMapped) = when {
|
||||||
|
modifier == JvmModifier.FINAL -> KtTokens.OPEN_KEYWORD to !shouldPresent
|
||||||
|
modifier == JvmModifier.PUBLIC && shouldPresent ->
|
||||||
|
kModifierOwner.visibilityModifierType()
|
||||||
|
?.takeIf { it != KtTokens.DEFAULT_VISIBILITY_KEYWORD }
|
||||||
|
?.let { it to false } ?: return emptyList()
|
||||||
|
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: CreateConstructorRequest): List<IntentionAction> {
|
||||||
|
val targetKtClass = targetClass.toKtClassOrFile() as? KtClass ?: 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 helper = JvmPsiConversionHelper.getInstance(targetKtClass.project)
|
||||||
|
val parameters = request.parameters as List<Pair<SuggestedNameInfo, List<ExpectedType>>>
|
||||||
|
val parameterInfos = parameters.mapIndexed { index, param: Pair<SuggestedNameInfo, List<ExpectedType>> ->
|
||||||
|
val ktType = helper.asPsiType(param)?.resolveToKotlinType(resolutionFacade) ?: nullableAnyType
|
||||||
|
val name = param.first.names.firstOrNull() ?: "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(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 parameters = request.parameters as List<Pair<SuggestedNameInfo, List<ExpectedType>>>
|
||||||
|
val parameterInfos = parameters.map { (suggestedNames, expectedTypes) ->
|
||||||
|
ParameterInfo(expectedTypes.toKotlinTypeInfo(resolutionFacade), suggestedNames.names.toList())
|
||||||
|
}
|
||||||
|
val functionInfo = FunctionInfo(
|
||||||
|
request.methodName,
|
||||||
|
TypeInfo.Empty,
|
||||||
|
returnTypeInfo,
|
||||||
|
listOf(targetContainer),
|
||||||
|
parameterInfos,
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun createAddAnnotationActions(target: JvmModifiersOwner, request: AnnotationRequest): List<IntentionAction> {
|
||||||
|
val declaration = (target as? KtLightElement<*, *>)?.kotlinOrigin as? KtModifierListOwner ?: return emptyList()
|
||||||
|
if (declaration.language != KotlinLanguage.INSTANCE) return emptyList()
|
||||||
|
return listOf(CreateAnnotationAction(declaration, request))
|
||||||
|
}
|
||||||
|
|
||||||
|
private class CreateAnnotationAction(
|
||||||
|
target: KtModifierListOwner,
|
||||||
|
val request: AnnotationRequest
|
||||||
|
) : IntentionAction {
|
||||||
|
|
||||||
|
private val pointer = target.createSmartPointer()
|
||||||
|
|
||||||
|
override fun startInWriteAction(): Boolean = true
|
||||||
|
|
||||||
|
override fun getText(): String =
|
||||||
|
QuickFixBundle.message("create.annotation.text", StringUtilRt.getShortName(request.qualifiedName))
|
||||||
|
|
||||||
|
override fun getFamilyName(): String = QuickFixBundle.message("create.annotation.family")
|
||||||
|
|
||||||
|
override fun isAvailable(project: Project, editor: Editor?, file: PsiFile?): Boolean = pointer.element != null
|
||||||
|
|
||||||
|
|
||||||
|
override fun invoke(project: Project, editor: Editor?, file: PsiFile?) {
|
||||||
|
val target = pointer.element ?: return
|
||||||
|
val entry = target.addAnnotationEntry(
|
||||||
|
KtPsiFactory(target)
|
||||||
|
.createAnnotationEntry(
|
||||||
|
"@${request.qualifiedName}${
|
||||||
|
request.attributes.joinToString(", ", "(", ")") { p ->
|
||||||
|
"${p.name} = ${renderAttributeValue(p.value)}"
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
ShortenReferences.DEFAULT.process(entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun renderAttributeValue(annotationAttributeRequest: AnnotationAttributeValueRequest) =
|
||||||
|
when (annotationAttributeRequest) {
|
||||||
|
is AnnotationAttributeValueRequest.PrimitiveValue -> annotationAttributeRequest.value
|
||||||
|
is AnnotationAttributeValueRequest.StringValue -> "\"" + annotationAttributeRequest.value + "\""
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun JvmPsiConversionHelper.asPsiType(param: Pair<SuggestedNameInfo, List<ExpectedType>>): PsiType? =
|
||||||
|
param.second.firstOrNull()?.theType?.let { convertType(it) }
|
||||||
@@ -0,0 +1,450 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
|
||||||
|
import com.intellij.codeInsight.intention.IntentionAction
|
||||||
|
import com.intellij.lang.jvm.JvmElement
|
||||||
|
import com.intellij.lang.jvm.JvmModifier
|
||||||
|
import com.intellij.lang.jvm.actions.*
|
||||||
|
import com.intellij.lang.jvm.types.JvmSubstitutor
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.openapi.util.Pair.pair
|
||||||
|
import com.intellij.psi.*
|
||||||
|
import com.intellij.psi.codeStyle.SuggestedNameInfo
|
||||||
|
import com.intellij.testFramework.fixtures.CodeInsightTestFixture
|
||||||
|
import com.intellij.testFramework.fixtures.LightPlatformCodeInsightFixtureTestCase
|
||||||
|
import org.jetbrains.kotlin.asJava.toLightElements
|
||||||
|
import org.jetbrains.kotlin.idea.search.allScope
|
||||||
|
import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor
|
||||||
|
import org.jetbrains.kotlin.psi.KtModifierListOwner
|
||||||
|
import org.jetbrains.uast.UParameter
|
||||||
|
import org.jetbrains.uast.UastContext
|
||||||
|
import org.jetbrains.uast.toUElement
|
||||||
|
import org.junit.Assert
|
||||||
|
|
||||||
|
class CommonIntentionActionsTest : LightPlatformCodeInsightFixtureTestCase() {
|
||||||
|
private class SimpleMethodRequest(
|
||||||
|
project: Project,
|
||||||
|
private val methodName: String,
|
||||||
|
private val modifiers: Collection<JvmModifier> = emptyList(),
|
||||||
|
private val returnType: ExpectedTypes = emptyList(),
|
||||||
|
private val annotations: Collection<AnnotationRequest> = emptyList(),
|
||||||
|
private val parameters: List<Pair<SuggestedNameInfo, List<ExpectedType>>> = emptyList(),
|
||||||
|
private val targetSubstitutor: JvmSubstitutor = PsiJvmSubstitutor(project, PsiSubstitutor.EMPTY)
|
||||||
|
) : CreateMethodRequest {
|
||||||
|
override fun getTargetSubstitutor(): JvmSubstitutor = targetSubstitutor
|
||||||
|
|
||||||
|
override fun getModifiers() = modifiers
|
||||||
|
|
||||||
|
override fun getMethodName() = methodName
|
||||||
|
|
||||||
|
override fun getAnnotations() = annotations
|
||||||
|
|
||||||
|
override fun getParameters() = parameters
|
||||||
|
|
||||||
|
override fun getReturnType() = returnType
|
||||||
|
|
||||||
|
override fun isValid(): Boolean = true
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private class NameInfo(vararg names: String) : SuggestedNameInfo(names)
|
||||||
|
|
||||||
|
override fun getProjectDescriptor() = KotlinWithJdkAndRuntimeLightProjectDescriptor.INSTANCE_FULL_JDK
|
||||||
|
|
||||||
|
fun testMakeNotFinal() {
|
||||||
|
myFixture.configureByText("foo.kt", """
|
||||||
|
class Foo {
|
||||||
|
fun bar<caret>(){}
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
|
||||||
|
myFixture.launchAction(
|
||||||
|
createModifierActions(
|
||||||
|
myFixture.atCaret(), MemberRequest.Modifier(JvmModifier.FINAL, false)
|
||||||
|
).findWithText("Make 'bar' open")
|
||||||
|
)
|
||||||
|
myFixture.checkResult("""
|
||||||
|
class Foo {
|
||||||
|
open fun bar(){}
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testMakePrivate() {
|
||||||
|
myFixture.configureByText("foo.kt", """
|
||||||
|
class Foo<caret> {
|
||||||
|
fun bar(){}
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
|
||||||
|
myFixture.launchAction(
|
||||||
|
createModifierActions(
|
||||||
|
myFixture.atCaret(), MemberRequest.Modifier(JvmModifier.PRIVATE, true)
|
||||||
|
).findWithText("Make 'Foo' private")
|
||||||
|
)
|
||||||
|
myFixture.checkResult("""
|
||||||
|
private class Foo {
|
||||||
|
fun bar(){}
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testMakeNotPrivate() {
|
||||||
|
myFixture.configureByText("foo.kt", """
|
||||||
|
private class Foo<caret> {
|
||||||
|
fun bar(){}
|
||||||
|
}
|
||||||
|
""".trim())
|
||||||
|
|
||||||
|
myFixture.launchAction(
|
||||||
|
createModifierActions(
|
||||||
|
myFixture.atCaret(), MemberRequest.Modifier(JvmModifier.PRIVATE, false)
|
||||||
|
).findWithText("Remove 'private' modifier")
|
||||||
|
)
|
||||||
|
myFixture.checkResult("""
|
||||||
|
class Foo {
|
||||||
|
fun bar(){}
|
||||||
|
}
|
||||||
|
""".trim(), true)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testMakePrivatePublic() {
|
||||||
|
myFixture.configureByText(
|
||||||
|
"foo.kt", """class Foo {
|
||||||
|
| private fun <caret>bar(){}
|
||||||
|
|}""".trim().trimMargin()
|
||||||
|
)
|
||||||
|
|
||||||
|
myFixture.launchAction(
|
||||||
|
createModifierActions(
|
||||||
|
myFixture.atCaret(), MemberRequest.Modifier(JvmModifier.PUBLIC, true)
|
||||||
|
).findWithText("Remove 'private' modifier")
|
||||||
|
)
|
||||||
|
myFixture.checkResult(
|
||||||
|
"""class Foo {
|
||||||
|
| fun <caret>bar(){}
|
||||||
|
|}""".trim().trimMargin(), true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testMakeProtectedPublic() {
|
||||||
|
myFixture.configureByText(
|
||||||
|
"foo.kt", """open class Foo {
|
||||||
|
| protected fun <caret>bar(){}
|
||||||
|
|}""".trim().trimMargin()
|
||||||
|
)
|
||||||
|
|
||||||
|
myFixture.launchAction(
|
||||||
|
createModifierActions(
|
||||||
|
myFixture.atCaret(), MemberRequest.Modifier(JvmModifier.PUBLIC, true)
|
||||||
|
).findWithText("Remove 'protected' modifier")
|
||||||
|
)
|
||||||
|
myFixture.checkResult(
|
||||||
|
"""open class Foo {
|
||||||
|
| fun <caret>bar(){}
|
||||||
|
|}""".trim().trimMargin(), true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testMakeInternalPublic() {
|
||||||
|
myFixture.configureByText(
|
||||||
|
"foo.kt", """class Foo {
|
||||||
|
| internal fun <caret>bar(){}
|
||||||
|
|}""".trim().trimMargin()
|
||||||
|
)
|
||||||
|
|
||||||
|
myFixture.launchAction(
|
||||||
|
createModifierActions(
|
||||||
|
myFixture.atCaret(), MemberRequest.Modifier(JvmModifier.PUBLIC, true)
|
||||||
|
).findWithText("Remove 'internal' modifier")
|
||||||
|
)
|
||||||
|
myFixture.checkResult(
|
||||||
|
"""class Foo {
|
||||||
|
| fun <caret>bar(){}
|
||||||
|
|}""".trim().trimMargin(), true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testAddAnnotation() {
|
||||||
|
myFixture.configureByText(
|
||||||
|
"foo.kt", """class Foo {
|
||||||
|
| fun <caret>bar(){}
|
||||||
|
|}""".trim().trimMargin()
|
||||||
|
)
|
||||||
|
|
||||||
|
myFixture.launchAction(
|
||||||
|
createAddAnnotationActions(
|
||||||
|
myFixture.findElementByText("bar", KtModifierListOwner::class.java).toLightElements().single() as PsiMethod,
|
||||||
|
annotationRequest("kotlin.jvm.JvmName", stringAttribute("name", "foo"))
|
||||||
|
).single()
|
||||||
|
)
|
||||||
|
myFixture.checkResult(
|
||||||
|
"""class Foo {
|
||||||
|
| @JvmName(name = "foo")
|
||||||
|
| fun <caret>bar(){}
|
||||||
|
|}""".trim().trimMargin(), true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testDontMakePublicPublic() {
|
||||||
|
myFixture.configureByText(
|
||||||
|
"foo.kt", """class Foo {
|
||||||
|
| fun <caret>bar(){}
|
||||||
|
|}""".trim().trimMargin()
|
||||||
|
)
|
||||||
|
|
||||||
|
assertEmpty(createModifierActions(myFixture.atCaret(), MemberRequest.Modifier(JvmModifier.PUBLIC, true)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testDontMakeFunInObjectsOpen() {
|
||||||
|
myFixture.configureByText("foo.kt", """
|
||||||
|
object Foo {
|
||||||
|
fun bar<caret>(){}
|
||||||
|
}
|
||||||
|
""".trim())
|
||||||
|
assertEmpty(createModifierActions(myFixture.atCaret(), MemberRequest.Modifier(JvmModifier.FINAL, false)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testAddVoidVoidMethod() {
|
||||||
|
myFixture.configureByText("foo.kt", """
|
||||||
|
|class Foo<caret> {
|
||||||
|
| fun bar() {}
|
||||||
|
|}
|
||||||
|
""".trim().trimMargin())
|
||||||
|
|
||||||
|
myFixture.launchAction(
|
||||||
|
createMethodActions(
|
||||||
|
myFixture.atCaret(),
|
||||||
|
methodRequest(project, "baz", JvmModifier.PRIVATE, PsiType.VOID)
|
||||||
|
).findWithText("Add method 'baz' to 'Foo'")
|
||||||
|
)
|
||||||
|
myFixture.checkResult("""
|
||||||
|
|class Foo {
|
||||||
|
| fun bar() {}
|
||||||
|
| private fun baz() {
|
||||||
|
|
|
||||||
|
| }
|
||||||
|
|}
|
||||||
|
""".trim().trimMargin(), true)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testAddIntIntMethod() {
|
||||||
|
myFixture.configureByText("foo.kt", """
|
||||||
|
|class Foo<caret> {
|
||||||
|
| fun bar() {}
|
||||||
|
|}
|
||||||
|
""".trim().trimMargin())
|
||||||
|
|
||||||
|
myFixture.launchAction(
|
||||||
|
createMethodActions(
|
||||||
|
myFixture.atCaret(),
|
||||||
|
SimpleMethodRequest(project,
|
||||||
|
methodName = "baz",
|
||||||
|
modifiers = listOf(JvmModifier.PUBLIC),
|
||||||
|
returnType = expectedTypes(PsiType.INT),
|
||||||
|
parameters = expectedParams(PsiType.INT))
|
||||||
|
).findWithText("Add method 'baz' to 'Foo'")
|
||||||
|
)
|
||||||
|
myFixture.checkResult("""
|
||||||
|
|class Foo {
|
||||||
|
| fun bar() {}
|
||||||
|
| fun baz(param0: Int): Int {
|
||||||
|
| TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||||
|
| }
|
||||||
|
|}
|
||||||
|
""".trim().trimMargin(), true)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testAddIntPrimaryConstructor() {
|
||||||
|
myFixture.configureByText("foo.kt", """
|
||||||
|
|class Foo<caret> {
|
||||||
|
|}
|
||||||
|
""".trim().trimMargin())
|
||||||
|
|
||||||
|
myFixture.launchAction(
|
||||||
|
createConstructorActions(
|
||||||
|
myFixture.atCaret(), constructorRequest(project, listOf(pair("param0", PsiType.INT as PsiType)))
|
||||||
|
).findWithText("Add primary constructor to 'Foo'")
|
||||||
|
)
|
||||||
|
myFixture.checkResult("""
|
||||||
|
|class Foo(param0: Int) {
|
||||||
|
|}
|
||||||
|
""".trim().trimMargin(), true)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testAddIntSecondaryConstructor() {
|
||||||
|
myFixture.configureByText("foo.kt", """
|
||||||
|
|class <caret>Foo() {
|
||||||
|
|}
|
||||||
|
""".trim().trimMargin())
|
||||||
|
|
||||||
|
myFixture.launchAction(
|
||||||
|
createConstructorActions(
|
||||||
|
myFixture.atCaret(),
|
||||||
|
constructorRequest(project, listOf(pair("param0", PsiType.INT as PsiType)))
|
||||||
|
).findWithText("Add secondary constructor to 'Foo'")
|
||||||
|
)
|
||||||
|
myFixture.checkResult("""
|
||||||
|
|class Foo() {
|
||||||
|
| constructor(param0: Int) {
|
||||||
|
|
|
||||||
|
| }
|
||||||
|
|}
|
||||||
|
""".trim().trimMargin(), true)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testChangePrimaryConstructorInt() {
|
||||||
|
myFixture.configureByText("foo.kt", """
|
||||||
|
|class <caret>Foo() {
|
||||||
|
|}
|
||||||
|
""".trim().trimMargin())
|
||||||
|
|
||||||
|
myFixture.launchAction(
|
||||||
|
createConstructorActions(
|
||||||
|
myFixture.atCaret(),
|
||||||
|
constructorRequest(project, listOf(pair("param0", PsiType.INT as PsiType)))
|
||||||
|
).findWithText("Add 'int' as 1st parameter to method 'Foo'")
|
||||||
|
)
|
||||||
|
myFixture.checkResult("""
|
||||||
|
|class Foo(param0: Int) {
|
||||||
|
|}
|
||||||
|
""".trim().trimMargin(), true)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testRemoveConstructorParameters() {
|
||||||
|
myFixture.configureByText("foo.kt", """
|
||||||
|
|class <caret>Foo(i: Int) {
|
||||||
|
|}
|
||||||
|
""".trim().trimMargin())
|
||||||
|
|
||||||
|
myFixture.launchAction(
|
||||||
|
createConstructorActions(
|
||||||
|
myFixture.atCaret(),
|
||||||
|
constructorRequest(project, listOf(pair("param0", PsiType.INT as PsiType)))
|
||||||
|
).findWithText("Remove 1st parameter from method 'Foo'")
|
||||||
|
)
|
||||||
|
myFixture.checkResult("""
|
||||||
|
|class Foo() {
|
||||||
|
|}
|
||||||
|
""".trim().trimMargin(), true)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testAddStringVarProperty() {
|
||||||
|
myFixture.configureByText("foo.kt", """
|
||||||
|
|class Foo<caret> {
|
||||||
|
| fun bar() {}
|
||||||
|
|}
|
||||||
|
""".trim().trimMargin())
|
||||||
|
|
||||||
|
myFixture.launchAction(
|
||||||
|
createPropertyActions(
|
||||||
|
myFixture.atCaret(),
|
||||||
|
MemberRequest.Property(
|
||||||
|
propertyName = "baz",
|
||||||
|
visibilityModifier = JvmModifier.PUBLIC,
|
||||||
|
propertyType = PsiType.getTypeByName("java.lang.String", project, project.allScope()),
|
||||||
|
getterRequired = true,
|
||||||
|
setterRequired = true
|
||||||
|
)
|
||||||
|
).findWithText("Add 'var' property 'baz' to 'Foo'")
|
||||||
|
)
|
||||||
|
myFixture.checkResult("""
|
||||||
|
|class Foo {
|
||||||
|
| var baz: String = TODO("initialize me")
|
||||||
|
|
|
||||||
|
| fun bar() {}
|
||||||
|
|}
|
||||||
|
""".trim().trimMargin(), true)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testAddLateInitStringVarProperty() {
|
||||||
|
myFixture.configureByText("foo.kt", """
|
||||||
|
|class Foo<caret> {
|
||||||
|
| fun bar() {}
|
||||||
|
|}
|
||||||
|
""".trim().trimMargin())
|
||||||
|
|
||||||
|
myFixture.launchAction(
|
||||||
|
createPropertyActions(
|
||||||
|
myFixture.atCaret(),
|
||||||
|
MemberRequest.Property(
|
||||||
|
propertyName = "baz",
|
||||||
|
visibilityModifier = JvmModifier.PUBLIC,
|
||||||
|
propertyType = PsiType.getTypeByName("java.lang.String", project, project.allScope()),
|
||||||
|
getterRequired = true,
|
||||||
|
setterRequired = true
|
||||||
|
)
|
||||||
|
).findWithText("Add 'lateinit var' property 'baz' to 'Foo'")
|
||||||
|
)
|
||||||
|
myFixture.checkResult("""
|
||||||
|
|class Foo {
|
||||||
|
| lateinit var baz: String
|
||||||
|
|
|
||||||
|
| fun bar() {}
|
||||||
|
|}
|
||||||
|
""".trim().trimMargin(), true)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testAddStringValProperty() {
|
||||||
|
myFixture.configureByText("foo.kt", """
|
||||||
|
|class Foo<caret> {
|
||||||
|
| fun bar() {}
|
||||||
|
|}
|
||||||
|
""".trim().trimMargin())
|
||||||
|
|
||||||
|
myFixture.launchAction(
|
||||||
|
createPropertyActions(
|
||||||
|
myFixture.atCaret(),
|
||||||
|
MemberRequest.Property(
|
||||||
|
propertyName = "baz",
|
||||||
|
visibilityModifier = JvmModifier.PUBLIC,
|
||||||
|
propertyType = PsiType.getTypeByName("java.lang.String", project, project.allScope()),
|
||||||
|
getterRequired = true,
|
||||||
|
setterRequired = false
|
||||||
|
)
|
||||||
|
).findWithText("Add 'val' property 'baz' to 'Foo'")
|
||||||
|
)
|
||||||
|
myFixture.checkResult("""
|
||||||
|
|class Foo {
|
||||||
|
| val baz: String = TODO("initialize me")
|
||||||
|
|
|
||||||
|
| fun bar() {}
|
||||||
|
|}
|
||||||
|
""".trim().trimMargin(), true)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun makeParams(vararg psyTypes: PsiType): List<UParameter> {
|
||||||
|
val uastContext = UastContext(myFixture.project)
|
||||||
|
val factory = JavaPsiFacade.getElementFactory(myFixture.project)
|
||||||
|
val parameters = psyTypes.mapIndexed { index, psiType -> factory.createParameter("param$index", psiType) }
|
||||||
|
return parameters.map { uastContext.convertElement(it, null, UParameter::class.java) as UParameter }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun expectedTypes(vararg psiTypes: PsiType) = psiTypes.map { expectedType(it) }
|
||||||
|
|
||||||
|
private fun expectedParams(vararg psyTypes: PsiType) =
|
||||||
|
psyTypes.mapIndexed { index, psiType -> NameInfo("param$index") to expectedTypes(psiType) }
|
||||||
|
|
||||||
|
private inline fun <reified T : JvmElement> CodeInsightTestFixture.atCaret() = elementAtCaret.toUElement() as T
|
||||||
|
|
||||||
|
@Suppress("CAST_NEVER_SUCCEEDS")
|
||||||
|
private fun List<IntentionAction>.findWithText(text: String): IntentionAction =
|
||||||
|
this.firstOrNull { it.text == text } ?:
|
||||||
|
Assert.fail("intention with text '$text' was not found, only ${this.joinToString { "\"${it.text}\"" }} available") as Nothing
|
||||||
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user