192: KotlinElementActionsFactory: changing parameters could add annotations
This commit is contained in:
+6
@@ -5,6 +5,7 @@
|
|||||||
package org.jetbrains.kotlin.idea.quickfix.crossLanguage
|
package org.jetbrains.kotlin.idea.quickfix.crossLanguage
|
||||||
|
|
||||||
import com.intellij.codeInsight.daemon.QuickFixBundle
|
import com.intellij.codeInsight.daemon.QuickFixBundle
|
||||||
|
import com.intellij.lang.jvm.actions.AnnotationRequest
|
||||||
import com.intellij.lang.jvm.actions.ChangeParametersRequest
|
import com.intellij.lang.jvm.actions.ChangeParametersRequest
|
||||||
import com.intellij.lang.jvm.actions.ExpectedParameter
|
import com.intellij.lang.jvm.actions.ExpectedParameter
|
||||||
import com.intellij.openapi.diagnostic.Logger
|
import com.intellij.openapi.diagnostic.Logger
|
||||||
@@ -65,6 +66,7 @@ internal class ChangeMethodParameters(
|
|||||||
data class Add(
|
data class Add(
|
||||||
val name: String,
|
val name: String,
|
||||||
val ktType: KotlinType,
|
val ktType: KotlinType,
|
||||||
|
val expectedAnnotations: Collection<AnnotationRequest>,
|
||||||
val beforeAnchor: KtParameter?
|
val beforeAnchor: KtParameter?
|
||||||
) : ParameterModification()
|
) : ParameterModification()
|
||||||
}
|
}
|
||||||
@@ -115,6 +117,7 @@ internal class ChangeMethodParameters(
|
|||||||
collected + ParameterModification.Add(
|
collected + ParameterModification.Add(
|
||||||
expectedHead.semanticNames.firstOrNull() ?: "param$index",
|
expectedHead.semanticNames.firstOrNull() ?: "param$index",
|
||||||
kotlinType,
|
kotlinType,
|
||||||
|
expectedHead.expectedAnnotations,
|
||||||
currentParameters.firstOrNull { anchor ->
|
currentParameters.firstOrNull { anchor ->
|
||||||
expectedParameters.any {
|
expectedParameters.any {
|
||||||
it is ChangeParametersRequest.ExistingParameterWrapper && it.existingKtParameter == anchor
|
it is ChangeParametersRequest.ExistingParameterWrapper && it.existingKtParameter == anchor
|
||||||
@@ -144,6 +147,9 @@ internal class ChangeMethodParameters(
|
|||||||
when (action) {
|
when (action) {
|
||||||
is ParameterModification.Add -> {
|
is ParameterModification.Add -> {
|
||||||
val parameter = parametersGenerated.getValue(action)
|
val parameter = parametersGenerated.getValue(action)
|
||||||
|
for (expectedAnnotation in action.expectedAnnotations) {
|
||||||
|
addAnnotationEntry(parameter, expectedAnnotation, null)
|
||||||
|
}
|
||||||
val anchor = action.beforeAnchor
|
val anchor = action.beforeAnchor
|
||||||
if (anchor != null) {
|
if (anchor != null) {
|
||||||
target.valueParameterList!!.addParameterBefore(parameter, anchor)
|
target.valueParameterList!!.addParameterBefore(parameter, anchor)
|
||||||
|
|||||||
+51
-41
@@ -437,57 +437,67 @@ class KotlinElementActionsFactory : JvmElementActionsFactory() {
|
|||||||
|
|
||||||
override fun invoke(project: Project, editor: Editor?, file: PsiFile?) {
|
override fun invoke(project: Project, editor: Editor?, file: PsiFile?) {
|
||||||
val target = pointer.element ?: return
|
val target = pointer.element ?: return
|
||||||
val annotationClass = JavaPsiFacade.getInstance(project).findClass(request.qualifiedName, target.resolveScope)
|
val entry = addAnnotationEntry(target, request, annotationTarget)
|
||||||
|
|
||||||
val kotlinAnnotation = annotationClass?.language == KotlinLanguage.INSTANCE
|
|
||||||
|
|
||||||
val annotationUseSiteTargetPrefix = run prefixEvaluation@{
|
|
||||||
if (annotationTarget == null) return@prefixEvaluation ""
|
|
||||||
|
|
||||||
val moduleDescriptor = (target as? KtDeclaration)?.resolveToDescriptorIfAny()?.module ?: return@prefixEvaluation ""
|
|
||||||
val annotationClassDescriptor = moduleDescriptor.resolveClassByFqName(
|
|
||||||
FqName(request.qualifiedName), NoLookupLocation.FROM_IDE
|
|
||||||
) ?: return@prefixEvaluation ""
|
|
||||||
|
|
||||||
val applicableTargetSet =
|
|
||||||
AnnotationChecker.applicableTargetSet(annotationClassDescriptor) ?: KotlinTarget.DEFAULT_TARGET_SET
|
|
||||||
|
|
||||||
if (KotlinTarget.PROPERTY !in applicableTargetSet) return@prefixEvaluation ""
|
|
||||||
|
|
||||||
"${annotationTarget.renderName}:"
|
|
||||||
}
|
|
||||||
|
|
||||||
val entry = target.addAnnotationEntry(
|
|
||||||
KtPsiFactory(target)
|
|
||||||
.createAnnotationEntry(
|
|
||||||
"@$annotationUseSiteTargetPrefix${request.qualifiedName}${
|
|
||||||
request.attributes.mapIndexed { i, p ->
|
|
||||||
if (!kotlinAnnotation && i == 0 && p.name == "value")
|
|
||||||
renderAttributeValue(p.value).toString()
|
|
||||||
else
|
|
||||||
"${p.name} = ${renderAttributeValue(p.value)}"
|
|
||||||
}.joinToString(", ", "(", ")")
|
|
||||||
}"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
ShortenReferences.DEFAULT.process(entry)
|
ShortenReferences.DEFAULT.process(entry)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun renderAttributeValue(annotationAttributeRequest: AnnotationAttributeValueRequest) =
|
|
||||||
when (annotationAttributeRequest) {
|
|
||||||
is AnnotationAttributeValueRequest.PrimitiveValue -> annotationAttributeRequest.value
|
|
||||||
is AnnotationAttributeValueRequest.StringValue -> "\"" + annotationAttributeRequest.value + "\""
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun createChangeParametersActions(target: JvmMethod, request: ChangeParametersRequest): List<IntentionAction> {
|
override fun createChangeParametersActions(target: JvmMethod, request: ChangeParametersRequest): List<IntentionAction> {
|
||||||
val ktNamedFunction = (target as? KtLightElement<*, *>)?.kotlinOrigin as? KtNamedFunction ?: return emptyList()
|
val ktNamedFunction = (target as? KtLightElement<*, *>)?.kotlinOrigin as? KtNamedFunction ?: return emptyList()
|
||||||
return listOfNotNull(ChangeMethodParameters.create(ktNamedFunction,request ))
|
return listOfNotNull(ChangeMethodParameters.create(ktNamedFunction, request))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal fun addAnnotationEntry(
|
||||||
|
target: KtModifierListOwner,
|
||||||
|
request: AnnotationRequest,
|
||||||
|
annotationTarget: AnnotationUseSiteTarget?
|
||||||
|
): KtAnnotationEntry {
|
||||||
|
val annotationClass = JavaPsiFacade.getInstance(target.project).findClass(request.qualifiedName, target.resolveScope)
|
||||||
|
|
||||||
|
val kotlinAnnotation = annotationClass?.language == KotlinLanguage.INSTANCE
|
||||||
|
|
||||||
|
val annotationUseSiteTargetPrefix = run prefixEvaluation@{
|
||||||
|
if (annotationTarget == null) return@prefixEvaluation ""
|
||||||
|
|
||||||
|
val moduleDescriptor = (target as? KtDeclaration)?.resolveToDescriptorIfAny()?.module ?: return@prefixEvaluation ""
|
||||||
|
val annotationClassDescriptor = moduleDescriptor.resolveClassByFqName(
|
||||||
|
FqName(request.qualifiedName), NoLookupLocation.FROM_IDE
|
||||||
|
) ?: return@prefixEvaluation ""
|
||||||
|
|
||||||
|
val applicableTargetSet =
|
||||||
|
AnnotationChecker.applicableTargetSet(annotationClassDescriptor) ?: KotlinTarget.DEFAULT_TARGET_SET
|
||||||
|
|
||||||
|
if (KotlinTarget.PROPERTY !in applicableTargetSet) return@prefixEvaluation ""
|
||||||
|
|
||||||
|
"${annotationTarget.renderName}:"
|
||||||
|
}
|
||||||
|
|
||||||
|
// could be generated via descriptor when KT-30478 is fixed
|
||||||
|
val entry = target.addAnnotationEntry(
|
||||||
|
KtPsiFactory(target)
|
||||||
|
.createAnnotationEntry(
|
||||||
|
"@$annotationUseSiteTargetPrefix${request.qualifiedName}${
|
||||||
|
request.attributes.mapIndexed { i, p ->
|
||||||
|
if (!kotlinAnnotation && i == 0 && p.name == "value")
|
||||||
|
renderAttributeValue(p.value).toString()
|
||||||
|
else
|
||||||
|
"${p.name} = ${renderAttributeValue(p.value)}"
|
||||||
|
}.joinToString(", ", "(", ")")
|
||||||
|
}"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return entry
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun renderAttributeValue(annotationAttributeRequest: AnnotationAttributeValueRequest) =
|
||||||
|
when (annotationAttributeRequest) {
|
||||||
|
is AnnotationAttributeValueRequest.PrimitiveValue -> annotationAttributeRequest.value
|
||||||
|
is AnnotationAttributeValueRequest.StringValue -> "\"" + annotationAttributeRequest.value + "\""
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun PsiType.collectTypeParameters(): List<PsiTypeParameter> {
|
private fun PsiType.collectTypeParameters(): List<PsiTypeParameter> {
|
||||||
val results = ArrayList<PsiTypeParameter>()
|
val results = ArrayList<PsiTypeParameter>()
|
||||||
accept(
|
accept(
|
||||||
|
|||||||
+9
-6
@@ -67,7 +67,8 @@ class CommonIntentionActionsParametersTest : LightPlatformCodeInsightFixtureTest
|
|||||||
|
|
||||||
runParametersTransformation("Change method parameters to '(a: Int, file: File)'") { currentParameters ->
|
runParametersTransformation("Change method parameters to '(a: Int, file: File)'") { currentParameters ->
|
||||||
currentParameters + expectedParameter(
|
currentParameters + expectedParameter(
|
||||||
PsiType.getTypeByName("java.io.File", project, myFixture.file.resolveScope), "file"
|
PsiType.getTypeByName("java.io.File", project, myFixture.file.resolveScope), "file",
|
||||||
|
listOf(annotationRequest("Anno", intAttribute("i", 8)))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,7 +77,7 @@ class CommonIntentionActionsParametersTest : LightPlatformCodeInsightFixtureTest
|
|||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
class Foo {
|
class Foo {
|
||||||
fun bar(@Anno(3) a: Int, file: File) {}
|
fun bar(@Anno(3) a: Int, @Anno(i = 8) file: File) {}
|
||||||
}
|
}
|
||||||
""".trimIndent(), true
|
""".trimIndent(), true
|
||||||
)
|
)
|
||||||
@@ -96,7 +97,8 @@ class CommonIntentionActionsParametersTest : LightPlatformCodeInsightFixtureTest
|
|||||||
runParametersTransformation("Change method parameters to '(file: File, a: Int)'") { currentParameters ->
|
runParametersTransformation("Change method parameters to '(file: File, a: Int)'") { currentParameters ->
|
||||||
listOf(
|
listOf(
|
||||||
expectedParameter(
|
expectedParameter(
|
||||||
PsiType.getTypeByName("java.io.File", project, myFixture.file.resolveScope), "file"
|
PsiType.getTypeByName("java.io.File", project, myFixture.file.resolveScope), "file",
|
||||||
|
listOf(annotationRequest("Anno", intAttribute("i", 8)))
|
||||||
)
|
)
|
||||||
) + currentParameters
|
) + currentParameters
|
||||||
}
|
}
|
||||||
@@ -106,7 +108,7 @@ class CommonIntentionActionsParametersTest : LightPlatformCodeInsightFixtureTest
|
|||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
class Foo {
|
class Foo {
|
||||||
fun bar(file: File, @Anno(3) a: Int) {}
|
fun bar(@Anno(i = 8) file: File, @Anno(3) a: Int) {}
|
||||||
}
|
}
|
||||||
""".trimIndent(), true
|
""".trimIndent(), true
|
||||||
)
|
)
|
||||||
@@ -125,7 +127,8 @@ class CommonIntentionActionsParametersTest : LightPlatformCodeInsightFixtureTest
|
|||||||
runParametersTransformation("Change method parameters to '(a: Int, file: File, c: Int)'") { currentParameters ->
|
runParametersTransformation("Change method parameters to '(a: Int, file: File, c: Int)'") { currentParameters ->
|
||||||
ArrayList<ExpectedParameter>(currentParameters).apply {
|
ArrayList<ExpectedParameter>(currentParameters).apply {
|
||||||
this[1] = expectedParameter(
|
this[1] = expectedParameter(
|
||||||
PsiType.getTypeByName("java.io.File", project, myFixture.file.resolveScope), "file"
|
PsiType.getTypeByName("java.io.File", project, myFixture.file.resolveScope), "file",
|
||||||
|
listOf(annotationRequest("Anno", intAttribute("i", 8)))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -135,7 +138,7 @@ class CommonIntentionActionsParametersTest : LightPlatformCodeInsightFixtureTest
|
|||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
class Foo {
|
class Foo {
|
||||||
fun bar(@Anno(1) a: Int, file: File, @Anno(3) c: Int) {}
|
fun bar(@Anno(1) a: Int, @Anno(i = 8) file: File, @Anno(3) c: Int) {}
|
||||||
}
|
}
|
||||||
""".trimIndent(), true
|
""".trimIndent(), true
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user