[jvm-lang] createChangeParametersActions made able to keep existing parameters as is (IDEA-199838)

This commit is contained in:
Nicolay Mitropolsky
2019-03-14 11:03:02 +03:00
parent 6e24f52798
commit 027a62d44c
5 changed files with 104 additions and 27 deletions
@@ -1,9 +1,41 @@
// 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 file.
package com.intellij.lang.jvm.actions;
import com.intellij.lang.jvm.JvmParameter;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
public interface ChangeParametersRequest extends ActionRequest {
List<ExpectedParameter> getExpectedParameters();
class ExistingParameterWrapper implements ExpectedParameter {
private final JvmParameter myExistingParameter;
public ExistingParameterWrapper(@NotNull JvmParameter existingParameter) {
myExistingParameter = existingParameter;
}
@NotNull
@Override
public List<ExpectedType> getExpectedTypes() {
return Collections.singletonList(new SimpleExpectedType(myExistingParameter.getType(), ExpectedType.Kind.EXACT));
}
@NotNull
@Override
public Collection<String> getSemanticNames() {
return Collections.singletonList(myExistingParameter.getName());
}
@NotNull
public JvmParameter getExistingParameter() {
return myExistingParameter;
}
}
}
@@ -21,4 +21,10 @@ public interface ExpectedParameter {
default Collection<String> getSemanticNames() {
return Collections.emptyList();
}
@NotNull
default Collection<AnnotationRequest> getExpectedAnnotations() {
return Collections.emptyList();
}
}
@@ -19,10 +19,16 @@ fun expectedParameter(types: List<ExpectedType>, names: Collection<String>): Exp
return SimpleExpectedParameter(types, names)
}
fun expectedParameter(type: JvmType, name: String, annotations: Collection<AnnotationRequest>): ExpectedParameter {
return SimpleExpectedParameter(listOf(expectedType(type, ExpectedType.Kind.SUPERTYPE)), listOf(name), annotations)
}
private class SimpleExpectedParameter(
private val types: List<ExpectedType>,
private val names: Collection<String>
private val names: Collection<String>,
private val annotations: Collection<AnnotationRequest> = emptyList()
) : ExpectedParameter {
override fun getExpectedTypes() = types
override fun getSemanticNames() = names
override fun getExpectedAnnotations() = annotations
}
@@ -3,6 +3,7 @@ package com.intellij.lang.java.actions
import com.intellij.codeInsight.daemon.QuickFixBundle
import com.intellij.lang.jvm.actions.ChangeParametersRequest
import com.intellij.lang.jvm.actions.ExpectedParameter
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.text.StringUtil
@@ -26,17 +27,46 @@ internal class ChangeMethodParameters(target: PsiMethod, override val request: C
override fun getFamilyName(): String = QuickFixBundle.message("change.method.parameters.family")
override fun invoke(project: Project, editor: Editor?, file: PsiFile?) {
target.parameterList.parameters.forEach(PsiParameter::delete)
val factory = PsiElementFactory.SERVICE.getInstance(project)
val helper = JvmPsiConversionHelper.getInstance(target.project)
for (expectedParameter in request.expectedParameters) {
val name = expectedParameter.semanticNames.first()
val psiType = helper.convertType(expectedParameter.expectedTypes.first().theType)
target.parameterList.add(factory.createParameter(name, psiType))
tailrec fun updateParameters(currentParameters: List<PsiParameter>, expectedParameters: List<ExpectedParameter>) {
val currentHead = currentParameters.firstOrNull()
val expectedHead = expectedParameters.firstOrNull()
if (expectedHead == null) {
currentParameters.forEach(PsiParameter::delete)
return
}
if (expectedHead is ChangeParametersRequest.ExistingParameterWrapper) {
if (expectedHead.existingParameter == currentHead)
return updateParameters(currentParameters.subList(1, currentParameters.size),
expectedParameters.subList(1, expectedParameters.size))
else
throw UnsupportedOperationException("processing of existing params in different order is not implemented yet")
}
val name = expectedHead.semanticNames.first()
val psiType = helper.convertType(expectedHead.expectedTypes.first().theType)
val newParameter = factory.createParameter(name, psiType)
for (annotationRequest in expectedHead.expectedAnnotations) {
addAnnotationToModifierList(newParameter.modifierList!!, annotationRequest)
}
if (currentHead == null)
target.parameterList.add(newParameter)
else
target.parameterList.addBefore(newParameter, currentHead)
updateParameters(currentParameters, expectedParameters.subList(1, expectedParameters.size))
}
updateParameters(target.parameterList.parameters.toList(), request.expectedParameters)
}
}
@@ -11,6 +11,7 @@ import com.intellij.openapi.util.text.StringUtil
import com.intellij.openapi.util.text.StringUtilRt
import com.intellij.psi.PsiElementFactory
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiModifierList
import com.intellij.psi.PsiModifierListOwner
import com.intellij.psi.codeStyle.CodeStyleManager
import com.intellij.psi.codeStyle.JavaCodeStyleManager
@@ -28,28 +29,30 @@ internal class CreateAnnotationAction(target: PsiModifierListOwner, override val
override fun invoke(project: Project, editor: Editor?, file: PsiFile?) {
val modifierList = target.modifierList ?: return
val annotation = modifierList.addAnnotation(request.qualifiedName)
val psiElementFactory = PsiElementFactory.SERVICE.getInstance(project)
attributes@ for ((name, value) in request.attributes) {
val memberValue = when (value) {
is AnnotationAttributeValueRequest.PrimitiveValue -> psiElementFactory
.createExpressionFromText(value.value.toString(), null)
is AnnotationAttributeValueRequest.StringValue -> psiElementFactory
.createExpressionFromText("\"" + StringUtil.escapeStringCharacters(value.value) + "\"", null)
else -> {
LOG.error("adding annotation members of ${value.javaClass} type is not implemented"); continue@attributes
}
}
annotation.setDeclaredAttributeValue(name.takeIf { name != "value" }, memberValue)
}
val formatter = CodeStyleManager.getInstance(project)
val codeStyleManager = JavaCodeStyleManager.getInstance(project)
codeStyleManager.shortenClassReferences(formatter.reformat(annotation))
addAnnotationToModifierList(modifierList, request)
}
}
internal fun addAnnotationToModifierList(modifierList: PsiModifierList, annotationRequest: AnnotationRequest) {
val project = modifierList.project
val annotation = modifierList.addAnnotation(annotationRequest.qualifiedName)
val psiElementFactory = PsiElementFactory.SERVICE.getInstance(project)
attributes@ for ((name, value) in annotationRequest.attributes) {
val memberValue = when (value) {
is AnnotationAttributeValueRequest.PrimitiveValue -> psiElementFactory
.createExpressionFromText(value.value.toString(), null)
is AnnotationAttributeValueRequest.StringValue -> psiElementFactory
.createExpressionFromText("\"" + StringUtil.escapeStringCharacters(value.value) + "\"", null)
else -> {
LOG.error("adding annotation members of ${value.javaClass} type is not implemented"); continue@attributes
}
}
annotation.setDeclaredAttributeValue(name.takeIf { name != "value" }, memberValue)
}
val formatter = CodeStyleManager.getInstance(project)
val codeStyleManager = JavaCodeStyleManager.getInstance(project)
codeStyleManager.shortenClassReferences(formatter.reformat(annotation))
}