Introduce Parameter: Remove parameters which become unused after occurrence replacement
This commit is contained in:
-4
@@ -33,10 +33,6 @@ public class JetMutableMethodDescriptor(val original: JetMethodDescriptor): JetM
|
||||
parameters.add(parameter)
|
||||
}
|
||||
|
||||
public fun setParameter(index: Int, parameter: JetParameterInfo) {
|
||||
parameters[index] = parameter
|
||||
}
|
||||
|
||||
public fun removeParameter(index: Int) {
|
||||
parameters.remove(index)
|
||||
}
|
||||
|
||||
+23
-6
@@ -83,13 +83,25 @@ public class KotlinInplaceParameterIntroducer(
|
||||
private val LOG = Logger.getInstance(javaClass<KotlinInplaceParameterIntroducer>())
|
||||
}
|
||||
|
||||
object PreviewDecorator {
|
||||
protected val textAttributes: TextAttributes = with(TextAttributes()) {
|
||||
setEffectType(EffectType.ROUNDED_BOX)
|
||||
setEffectColor(JBColor.RED)
|
||||
this
|
||||
enum class PreviewDecorator {
|
||||
FOR_ADD: PreviewDecorator() {
|
||||
override val textAttributes: TextAttributes = with(TextAttributes()) {
|
||||
setEffectType(EffectType.ROUNDED_BOX)
|
||||
setEffectColor(JBColor.RED)
|
||||
this
|
||||
}
|
||||
}
|
||||
|
||||
FOR_REMOVAL: PreviewDecorator() {
|
||||
override val textAttributes: TextAttributes = with(TextAttributes()) {
|
||||
setEffectType(EffectType.STRIKEOUT)
|
||||
setEffectColor(Color.BLACK)
|
||||
this
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract val textAttributes: TextAttributes
|
||||
|
||||
fun applyToRange(range: TextRange, markupModel: MarkupModel) {
|
||||
markupModel.addRangeHighlighter(range.getStartOffset(),
|
||||
range.getEndOffset(),
|
||||
@@ -104,6 +116,7 @@ public class KotlinInplaceParameterIntroducer(
|
||||
|
||||
private fun updatePreview(currentName: String?, currentType: String?) {
|
||||
with (descriptor) {
|
||||
val rangesToRemove = ArrayList<TextRange>()
|
||||
var addedRange: TextRange? = null
|
||||
val builder = StringBuilder()
|
||||
|
||||
@@ -129,6 +142,9 @@ public class KotlinInplaceParameterIntroducer(
|
||||
if (parameter == addedParameter) {
|
||||
addedRange = range
|
||||
}
|
||||
else if (parameter in parametersToRemove) {
|
||||
rangesToRemove.add(range)
|
||||
}
|
||||
|
||||
if (i < parameters.lastIndex) {
|
||||
builder.append(", ")
|
||||
@@ -145,7 +161,8 @@ public class KotlinInplaceParameterIntroducer(
|
||||
|
||||
val markupModel = DocumentMarkupModel.forDocument(document, myProject, true)
|
||||
markupModel.removeAllHighlighters()
|
||||
addedRange?.let { PreviewDecorator.applyToRange(it, markupModel) }
|
||||
rangesToRemove.forEach { PreviewDecorator.FOR_REMOVAL.applyToRange(it, markupModel) }
|
||||
addedRange?.let { PreviewDecorator.FOR_ADD.applyToRange(it, markupModel) }
|
||||
}
|
||||
revalidate()
|
||||
}
|
||||
|
||||
+35
-11
@@ -24,6 +24,7 @@ import com.intellij.openapi.util.TextRange
|
||||
import com.intellij.psi.PsiDocumentManager
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.psi.search.LocalSearchScope
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
@@ -35,18 +36,26 @@ import org.jetbrains.kotlin.idea.refactoring.changeSignature.*
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.KotlinIntroduceHandlerBase
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.selectElementsWithTargetParent
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.showErrorHintByKey
|
||||
import org.jetbrains.kotlin.idea.search.usagesSearch.DefaultSearchHelper
|
||||
import org.jetbrains.kotlin.idea.search.usagesSearch.UsagesSearchTarget
|
||||
import org.jetbrains.kotlin.idea.search.usagesSearch.search
|
||||
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
||||
import org.jetbrains.kotlin.idea.util.application.executeCommand
|
||||
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
||||
import org.jetbrains.kotlin.idea.util.approximateWithResolvableType
|
||||
import org.jetbrains.kotlin.idea.util.psi.patternMatching.JetPsiUnifier
|
||||
import org.jetbrains.kotlin.idea.util.psi.patternMatching.toRange
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getParentOfTypeAndBranch
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getValueParameterList
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getValueParameters
|
||||
import org.jetbrains.kotlin.psi.psiUtil.isAncestor
|
||||
import org.jetbrains.kotlin.psi.psiUtil.parents
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.scopes.JetScopeUtils
|
||||
import org.jetbrains.kotlin.types.JetType
|
||||
import java.util.Collections
|
||||
import kotlin.test.fail
|
||||
|
||||
public data class IntroduceParameterDescriptor(
|
||||
@@ -54,7 +63,8 @@ public data class IntroduceParameterDescriptor(
|
||||
val callable: JetNamedDeclaration,
|
||||
val callableDescriptor: FunctionDescriptor,
|
||||
val addedParameter: JetParameter,
|
||||
val parameterType: JetType
|
||||
val parameterType: JetType,
|
||||
val parametersToRemove: List<JetParameter>
|
||||
) {
|
||||
val valVar: JetValVar
|
||||
|
||||
@@ -81,10 +91,13 @@ public data class IntroduceParameterDescriptor(
|
||||
fun IntroduceParameterDescriptor.performRefactoring() {
|
||||
runWriteAction {
|
||||
JetPsiUtil.deleteElementWithDelimiters(addedParameter)
|
||||
|
||||
|
||||
val config = object: JetChangeSignatureConfiguration {
|
||||
override fun configure(originalDescriptor: JetMethodDescriptor, bindingContext: BindingContext): JetMethodDescriptor {
|
||||
return originalDescriptor.modify {
|
||||
val parameters = callable.getValueParameters()
|
||||
parametersToRemove.map { parameters.indexOf(it) }.sortDescending().forEach { removeParameter(it) }
|
||||
|
||||
val parameterInfo = JetParameterInfo(name = addedParameter.getName()!!,
|
||||
type = parameterType,
|
||||
defaultValueForParameter = JetPsiUtil.deparenthesize(originalExpression),
|
||||
@@ -103,7 +116,7 @@ fun IntroduceParameterDescriptor.performRefactoring() {
|
||||
}
|
||||
|
||||
public class KotlinIntroduceParameterHandler: KotlinIntroduceHandlerBase() {
|
||||
public fun invoke(project: Project, editor: Editor, expression: JetExpression, targetParent: JetNamedDeclaration) {
|
||||
fun invoke(project: Project, editor: Editor, expression: JetExpression, targetParent: JetNamedDeclaration) {
|
||||
val psiFactory = JetPsiFactory(project)
|
||||
|
||||
val parameterList = targetParent.getValueParameterList()
|
||||
@@ -119,15 +132,25 @@ public class KotlinIntroduceParameterHandler: KotlinIntroduceHandlerBase() {
|
||||
val expressionType = context[BindingContext.EXPRESSION_TYPE, expression] ?: KotlinBuiltIns.getInstance().getAnyType()
|
||||
val parameterType = expressionType.approximateWithResolvableType(JetScopeUtils.getResolutionScope(targetParent, context), false)
|
||||
|
||||
val validatorContainer =
|
||||
when (targetParent) {
|
||||
is JetFunction -> targetParent.getBodyExpression()
|
||||
is JetClass -> targetParent.getBody()
|
||||
else -> null
|
||||
} ?: throw AssertionError("Body element is not found: ${JetPsiUtil.getElementTextWithContext(targetParent)}")
|
||||
val nameValidator = JetNameValidatorImpl(validatorContainer, null, JetNameValidatorImpl.Target.PROPERTIES)
|
||||
val body = when (targetParent) {
|
||||
is JetFunction -> targetParent.getBodyExpression()
|
||||
is JetClass -> targetParent.getBody()
|
||||
else -> null
|
||||
} ?: throw AssertionError("Body element is not found: ${JetPsiUtil.getElementTextWithContext(targetParent)}")
|
||||
val nameValidator = JetNameValidatorImpl(body, null, JetNameValidatorImpl.Target.PROPERTIES)
|
||||
val suggestedNames = linkedSetOf(*JetNameSuggester.suggestNames(parameterType, nameValidator, "p"))
|
||||
|
||||
val parametersToRemove = (parameterList?.getParameters() ?: Collections.emptyList()).filter {
|
||||
if (!it.hasValOrVarNode()) {
|
||||
val usages = DefaultSearchHelper<JetParameter>()
|
||||
.newRequest(UsagesSearchTarget(element = it))
|
||||
.search()
|
||||
.toList()
|
||||
usages.isNotEmpty() && usages.all { expression.isAncestor(it.getElement()) }
|
||||
}
|
||||
else false
|
||||
}
|
||||
|
||||
project.executeCommand(INTRODUCE_PARAMETER) {
|
||||
val renderedType = IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.renderType(parameterType)
|
||||
val newParameter = psiFactory.createParameter("${suggestedNames.first()}: $renderedType")
|
||||
@@ -160,7 +183,8 @@ public class KotlinIntroduceParameterHandler: KotlinIntroduceHandlerBase() {
|
||||
targetParent,
|
||||
functionDescriptor,
|
||||
addedParameter,
|
||||
parameterType)
|
||||
parameterType,
|
||||
parametersToRemove)
|
||||
if (editor.getSettings().isVariableInplaceRenameEnabled() && !ApplicationManager.getApplication().isUnitTestMode()) {
|
||||
with(PsiDocumentManager.getInstance(project)) {
|
||||
commitDocument(editor.getDocument())
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
// TARGET:
|
||||
class A(val a: Int, s: String) {
|
||||
fun foo() = (<selection>a + 1</selection>) * 2
|
||||
}
|
||||
|
||||
fun test() {
|
||||
val t = A(1, "2").a
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
// TARGET:
|
||||
class A(val a: Int, s: String, val i: Int = a + 1) {
|
||||
fun foo() = (i) * 2
|
||||
}
|
||||
|
||||
fun test() {
|
||||
val t = A(1, "2").a
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
// TARGET:
|
||||
class A(val a: Int, s: String) {
|
||||
fun foo() = (<selection>a + 1</selection>) * 2
|
||||
}
|
||||
|
||||
fun test() {
|
||||
A(1, "2")
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
// TARGET:
|
||||
class A(val a: Int, s: String, val i: Int = a + 1) {
|
||||
fun foo() = (i) * 2
|
||||
}
|
||||
|
||||
fun test() {
|
||||
A(1, "2")
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// TARGET:
|
||||
class A(val a: Int, s: String) {
|
||||
val x = a + 2
|
||||
|
||||
fun foo() = (<selection>a + 1</selection>) * 2
|
||||
}
|
||||
|
||||
fun test() {
|
||||
A(1, "2")
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// TARGET:
|
||||
class A(val a: Int, s: String, val i: Int = a + 1) {
|
||||
val x = a + 2
|
||||
|
||||
fun foo() = (i) * 2
|
||||
}
|
||||
|
||||
fun test() {
|
||||
A(1, "2")
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
fun foo(a: Int, s: String, b: Int): Int {
|
||||
return (<selection>a + b</selection>) * 2
|
||||
}
|
||||
|
||||
fun test() {
|
||||
foo(1, "2", 3)
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
fun foo(s: String, i: Int = a + b): Int {
|
||||
return (i) * 2
|
||||
}
|
||||
|
||||
fun test() {
|
||||
foo("2")
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
fun foo(a: Int, s: String): Int {
|
||||
return (<selection>a + 1</selection>) * 2
|
||||
}
|
||||
|
||||
fun test() {
|
||||
foo(1, "2")
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
fun foo(s: String, i: Int = a + 1): Int {
|
||||
return (i) * 2
|
||||
}
|
||||
|
||||
fun test() {
|
||||
foo("2")
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
fun foo(a: Int, s: String): Int {
|
||||
return (<selection>a + 1</selection>) * a
|
||||
}
|
||||
|
||||
fun test() {
|
||||
foo(1, "2")
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
fun foo(a: Int, s: String, i: Int = a + 1): Int {
|
||||
return (i) * a
|
||||
}
|
||||
|
||||
fun test() {
|
||||
foo(1, "2")
|
||||
}
|
||||
+36
@@ -2215,12 +2215,48 @@ public class JetExtractionTestGenerated extends AbstractJetExtractionTest {
|
||||
doIntroduceParameterTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("classParameterUsedOutside.kt")
|
||||
public void testClassParameterUsedOutside() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/introduceParameter/classParameterUsedOutside.kt");
|
||||
doIntroduceParameterTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("classUnusedParameter.kt")
|
||||
public void testClassUnusedParameter() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/introduceParameter/classUnusedParameter.kt");
|
||||
doIntroduceParameterTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("classUsedParameter.kt")
|
||||
public void testClassUsedParameter() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/introduceParameter/classUsedParameter.kt");
|
||||
doIntroduceParameterTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("defaultValueInParens.kt")
|
||||
public void testDefaultValueInParens() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/introduceParameter/defaultValueInParens.kt");
|
||||
doIntroduceParameterTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("functionMultipleUnusedParameters.kt")
|
||||
public void testFunctionMultipleUnusedParameters() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/introduceParameter/functionMultipleUnusedParameters.kt");
|
||||
doIntroduceParameterTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("functionUnusedParameter.kt")
|
||||
public void testFunctionUnusedParameter() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/introduceParameter/functionUnusedParameter.kt");
|
||||
doIntroduceParameterTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("functionUsedParameter.kt")
|
||||
public void testFunctionUsedParameter() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/introduceParameter/functionUsedParameter.kt");
|
||||
doIntroduceParameterTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("functionWithApproximatedType.kt")
|
||||
public void testFunctionWithApproximatedType() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/introduceParameter/functionWithApproximatedType.kt");
|
||||
|
||||
Reference in New Issue
Block a user