Destructure intention: use also for variable declarations #KT-7488 Fixed
This commit is contained in:
@@ -48,7 +48,9 @@ class DestructureIntention : SelfTargetingRangeIntention<KtDeclaration>(
|
||||
override fun applyTo(element: KtDeclaration, editor: Editor?) {
|
||||
val forLoop = element.parent as? KtForExpression
|
||||
val functionLiteral = element.parent?.parent as? KtFunctionLiteral
|
||||
if (forLoop == null && functionLiteral == null && (element !is KtFunctionLiteral || element.hasParameterSpecification())) {
|
||||
if (forLoop == null && functionLiteral == null &&
|
||||
(element !is KtFunctionLiteral || element.hasParameterSpecification()) &&
|
||||
(element !is KtVariableDeclaration)) {
|
||||
return
|
||||
}
|
||||
val (usagesToRemove, removeSelectorInLoopRange) = collectUsagesToRemove(element, forLoop) ?: return
|
||||
@@ -67,8 +69,9 @@ class DestructureIntention : SelfTargetingRangeIntention<KtDeclaration>(
|
||||
}
|
||||
names.add(name)
|
||||
}
|
||||
val joinedNames = names.joinToString()
|
||||
if (forLoop != null) {
|
||||
element.replace(factory.createDestructuringParameterForLoop("(${names.joinToString()})"))
|
||||
element.replace(factory.createDestructuringParameterForLoop("($joinedNames)"))
|
||||
|
||||
if (removeSelectorInLoopRange && loopRange is KtDotQualifiedExpression) {
|
||||
loopRange.replace(loopRange.receiverExpression)
|
||||
@@ -78,24 +81,35 @@ class DestructureIntention : SelfTargetingRangeIntention<KtDeclaration>(
|
||||
val lambda = element.parent as KtLambdaExpression
|
||||
SpecifyExplicitLambdaSignatureIntention().applyTo(lambda, editor)
|
||||
lambda.functionLiteral.valueParameters.singleOrNull()?.replace(
|
||||
factory.createDestructuringParameterForLambda("(${names.joinToString()})")
|
||||
factory.createDestructuringParameterForLambda("($joinedNames)")
|
||||
)
|
||||
}
|
||||
else if (functionLiteral != null) {
|
||||
element.replace(factory.createDestructuringParameterForLambda("(${names.joinToString()})"))
|
||||
element.replace(factory.createDestructuringParameterForLambda("($joinedNames)"))
|
||||
}
|
||||
else if (element is KtVariableDeclaration) {
|
||||
val modifiersText = element.modifierList?.text ?: ""
|
||||
val initializerText = element.initializer!!.text
|
||||
element.replace(factory.createDestructuringDeclaration("$modifiersText val ($joinedNames) = $initializerText"))
|
||||
}
|
||||
}
|
||||
|
||||
override fun applicabilityRange(element: KtDeclaration): TextRange? {
|
||||
val forLoopIfAny = element.parent as? KtForExpression
|
||||
val functionLiteral = element.parent?.parent as? KtFunctionLiteral
|
||||
if (forLoopIfAny == null && functionLiteral == null && (element !is KtFunctionLiteral || element.hasParameterSpecification())) {
|
||||
if (forLoopIfAny == null && functionLiteral == null &&
|
||||
(element !is KtFunctionLiteral || element.hasParameterSpecification()) &&
|
||||
(element !is KtVariableDeclaration)) {
|
||||
return null
|
||||
}
|
||||
|
||||
val usagesToRemove = collectUsagesToRemove(element, forLoopIfAny)
|
||||
if (usagesToRemove != null && usagesToRemove.first.isNotEmpty()) {
|
||||
return if (element is KtFunctionLiteral) element.lBrace.textRange else element.textRange
|
||||
return when (element) {
|
||||
is KtFunctionLiteral -> element.lBrace.textRange
|
||||
is KtVariableDeclaration -> element.nameIdentifier?.textRange ?: element.textRange
|
||||
else -> element.textRange
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
@@ -104,7 +118,7 @@ class DestructureIntention : SelfTargetingRangeIntention<KtDeclaration>(
|
||||
private fun collectUsagesToRemove(declaration: KtDeclaration, forLoop: KtForExpression?): Pair<List<UsageData>, Boolean>? {
|
||||
val context = declaration.analyzeFullyAndGetResult().bindingContext
|
||||
|
||||
val parameterDescriptor = when (declaration) {
|
||||
val variableDescriptor = when (declaration) {
|
||||
is KtParameter ->
|
||||
context.get(BindingContext.VALUE_PARAMETER, declaration)
|
||||
is KtFunctionLiteral ->
|
||||
@@ -114,11 +128,13 @@ class DestructureIntention : SelfTargetingRangeIntention<KtDeclaration>(
|
||||
else {
|
||||
context.get(BindingContext.FUNCTION, declaration)?.valueParameters?.singleOrNull()
|
||||
}
|
||||
is KtVariableDeclaration ->
|
||||
context.get(BindingContext.VARIABLE, declaration)
|
||||
else -> null
|
||||
} ?: return null
|
||||
val parameterType = parameterDescriptor.type
|
||||
if (parameterType.isMarkedNullable) return null
|
||||
val classDescriptor = parameterType.constructor.declarationDescriptor as? ClassDescriptor ?: return null
|
||||
val variableType = variableDescriptor.type
|
||||
if (variableType.isMarkedNullable) return null
|
||||
val classDescriptor = variableType.constructor.declarationDescriptor as? ClassDescriptor ?: return null
|
||||
|
||||
var otherUsages = false
|
||||
val usagesToRemove : Array<UsageData>
|
||||
@@ -149,12 +165,13 @@ class DestructureIntention : SelfTargetingRangeIntention<KtDeclaration>(
|
||||
val expressionToSearch = forLoop
|
||||
?: (declaration as? KtFunctionLiteral)
|
||||
?: (declaration.parent?.parent as? KtFunctionLiteral)
|
||||
?: (declaration as? KtVariableDeclaration)?.parent
|
||||
|
||||
if (expressionToSearch !is KtExpression) return null
|
||||
|
||||
expressionToSearch.iterateOverDataClassPropertiesUsagesWithIndex(
|
||||
context,
|
||||
(declaration as? KtParameter)?.nameAsName ?: Name.identifier("it"),
|
||||
(declaration as? KtNamedDeclaration)?.nameAsName ?: Name.identifier("it"),
|
||||
classDescriptor,
|
||||
{ index, usageData -> usagesToRemove[index] += usageData },
|
||||
{ otherUsages = true }
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
org.jetbrains.kotlin.idea.intentions.DestructureIntention
|
||||
@@ -0,0 +1,10 @@
|
||||
// IS_APPLICABLE: false
|
||||
|
||||
data class XY(val x: Int, val y: Int)
|
||||
|
||||
fun create() = XY(1, 2)
|
||||
|
||||
fun use(): Int {
|
||||
val xy = <caret>create()
|
||||
return xy.x + xy.y
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
// IS_APPLICABLE: false
|
||||
|
||||
data class XY(val x: Int, val y: Int)
|
||||
|
||||
fun create() = XY(1, 2)
|
||||
|
||||
fun use(): Int {
|
||||
var <caret>xy = create()
|
||||
xy = create()
|
||||
return xy.x + xy.y
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
// IS_APPLICABLE: false
|
||||
|
||||
data class XY(val x: Int, val y: Int)
|
||||
|
||||
fun create() = XY(1, 2)
|
||||
|
||||
fun use(): Int {
|
||||
val <caret>xy: XY
|
||||
xy = create()
|
||||
return xy.x + xy.y
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
data class XY(val x: Int, val y: Int)
|
||||
|
||||
fun create() = XY(1, 2)
|
||||
|
||||
fun use(): Int {
|
||||
val <caret>xy = create()
|
||||
return xy.x + xy.y
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
data class XY(val x: Int, val y: Int)
|
||||
|
||||
fun create() = XY(1, 2)
|
||||
|
||||
fun use(): Int {
|
||||
val (x, y) = create()
|
||||
return x + y
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
data class XY(val x: Int, val y: Int)
|
||||
|
||||
fun create() = XY(1, 2)
|
||||
|
||||
fun use(): Int {
|
||||
var <caret>xy = create()
|
||||
return xy.x + xy.y
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
data class XY(val x: Int, val y: Int)
|
||||
|
||||
fun create() = XY(1, 2)
|
||||
|
||||
fun use(): Int {
|
||||
val (x, y) = create()
|
||||
return x + y
|
||||
}
|
||||
@@ -6319,6 +6319,45 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/intentions/destructuringVariables")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class DestructuringVariables extends AbstractIntentionTest {
|
||||
public void testAllFilesPresentInDestructuringVariables() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/destructuringVariables"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("caret.kt")
|
||||
public void testCaret() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/destructuringVariables/caret.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("changingVar.kt")
|
||||
public void testChangingVar() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/destructuringVariables/changingVar.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("noInitializer.kt")
|
||||
public void testNoInitializer() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/destructuringVariables/noInitializer.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("simple.kt")
|
||||
public void testSimple() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/destructuringVariables/simple.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("var.kt")
|
||||
public void testVar() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/destructuringVariables/var.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/intentions/ifNullToElvis")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
Reference in New Issue
Block a user