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?) {
|
override fun applyTo(element: KtDeclaration, editor: Editor?) {
|
||||||
val forLoop = element.parent as? KtForExpression
|
val forLoop = element.parent as? KtForExpression
|
||||||
val functionLiteral = element.parent?.parent as? KtFunctionLiteral
|
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
|
return
|
||||||
}
|
}
|
||||||
val (usagesToRemove, removeSelectorInLoopRange) = collectUsagesToRemove(element, forLoop) ?: return
|
val (usagesToRemove, removeSelectorInLoopRange) = collectUsagesToRemove(element, forLoop) ?: return
|
||||||
@@ -67,8 +69,9 @@ class DestructureIntention : SelfTargetingRangeIntention<KtDeclaration>(
|
|||||||
}
|
}
|
||||||
names.add(name)
|
names.add(name)
|
||||||
}
|
}
|
||||||
|
val joinedNames = names.joinToString()
|
||||||
if (forLoop != null) {
|
if (forLoop != null) {
|
||||||
element.replace(factory.createDestructuringParameterForLoop("(${names.joinToString()})"))
|
element.replace(factory.createDestructuringParameterForLoop("($joinedNames)"))
|
||||||
|
|
||||||
if (removeSelectorInLoopRange && loopRange is KtDotQualifiedExpression) {
|
if (removeSelectorInLoopRange && loopRange is KtDotQualifiedExpression) {
|
||||||
loopRange.replace(loopRange.receiverExpression)
|
loopRange.replace(loopRange.receiverExpression)
|
||||||
@@ -78,24 +81,35 @@ class DestructureIntention : SelfTargetingRangeIntention<KtDeclaration>(
|
|||||||
val lambda = element.parent as KtLambdaExpression
|
val lambda = element.parent as KtLambdaExpression
|
||||||
SpecifyExplicitLambdaSignatureIntention().applyTo(lambda, editor)
|
SpecifyExplicitLambdaSignatureIntention().applyTo(lambda, editor)
|
||||||
lambda.functionLiteral.valueParameters.singleOrNull()?.replace(
|
lambda.functionLiteral.valueParameters.singleOrNull()?.replace(
|
||||||
factory.createDestructuringParameterForLambda("(${names.joinToString()})")
|
factory.createDestructuringParameterForLambda("($joinedNames)")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
else if (functionLiteral != null) {
|
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? {
|
override fun applicabilityRange(element: KtDeclaration): TextRange? {
|
||||||
val forLoopIfAny = element.parent as? KtForExpression
|
val forLoopIfAny = element.parent as? KtForExpression
|
||||||
val functionLiteral = element.parent?.parent as? KtFunctionLiteral
|
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
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
val usagesToRemove = collectUsagesToRemove(element, forLoopIfAny)
|
val usagesToRemove = collectUsagesToRemove(element, forLoopIfAny)
|
||||||
if (usagesToRemove != null && usagesToRemove.first.isNotEmpty()) {
|
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
|
return null
|
||||||
}
|
}
|
||||||
@@ -104,7 +118,7 @@ class DestructureIntention : SelfTargetingRangeIntention<KtDeclaration>(
|
|||||||
private fun collectUsagesToRemove(declaration: KtDeclaration, forLoop: KtForExpression?): Pair<List<UsageData>, Boolean>? {
|
private fun collectUsagesToRemove(declaration: KtDeclaration, forLoop: KtForExpression?): Pair<List<UsageData>, Boolean>? {
|
||||||
val context = declaration.analyzeFullyAndGetResult().bindingContext
|
val context = declaration.analyzeFullyAndGetResult().bindingContext
|
||||||
|
|
||||||
val parameterDescriptor = when (declaration) {
|
val variableDescriptor = when (declaration) {
|
||||||
is KtParameter ->
|
is KtParameter ->
|
||||||
context.get(BindingContext.VALUE_PARAMETER, declaration)
|
context.get(BindingContext.VALUE_PARAMETER, declaration)
|
||||||
is KtFunctionLiteral ->
|
is KtFunctionLiteral ->
|
||||||
@@ -114,11 +128,13 @@ class DestructureIntention : SelfTargetingRangeIntention<KtDeclaration>(
|
|||||||
else {
|
else {
|
||||||
context.get(BindingContext.FUNCTION, declaration)?.valueParameters?.singleOrNull()
|
context.get(BindingContext.FUNCTION, declaration)?.valueParameters?.singleOrNull()
|
||||||
}
|
}
|
||||||
|
is KtVariableDeclaration ->
|
||||||
|
context.get(BindingContext.VARIABLE, declaration)
|
||||||
else -> null
|
else -> null
|
||||||
} ?: return null
|
} ?: return null
|
||||||
val parameterType = parameterDescriptor.type
|
val variableType = variableDescriptor.type
|
||||||
if (parameterType.isMarkedNullable) return null
|
if (variableType.isMarkedNullable) return null
|
||||||
val classDescriptor = parameterType.constructor.declarationDescriptor as? ClassDescriptor ?: return null
|
val classDescriptor = variableType.constructor.declarationDescriptor as? ClassDescriptor ?: return null
|
||||||
|
|
||||||
var otherUsages = false
|
var otherUsages = false
|
||||||
val usagesToRemove : Array<UsageData>
|
val usagesToRemove : Array<UsageData>
|
||||||
@@ -149,12 +165,13 @@ class DestructureIntention : SelfTargetingRangeIntention<KtDeclaration>(
|
|||||||
val expressionToSearch = forLoop
|
val expressionToSearch = forLoop
|
||||||
?: (declaration as? KtFunctionLiteral)
|
?: (declaration as? KtFunctionLiteral)
|
||||||
?: (declaration.parent?.parent as? KtFunctionLiteral)
|
?: (declaration.parent?.parent as? KtFunctionLiteral)
|
||||||
|
?: (declaration as? KtVariableDeclaration)?.parent
|
||||||
|
|
||||||
if (expressionToSearch !is KtExpression) return null
|
if (expressionToSearch !is KtExpression) return null
|
||||||
|
|
||||||
expressionToSearch.iterateOverDataClassPropertiesUsagesWithIndex(
|
expressionToSearch.iterateOverDataClassPropertiesUsagesWithIndex(
|
||||||
context,
|
context,
|
||||||
(declaration as? KtParameter)?.nameAsName ?: Name.identifier("it"),
|
(declaration as? KtNamedDeclaration)?.nameAsName ?: Name.identifier("it"),
|
||||||
classDescriptor,
|
classDescriptor,
|
||||||
{ index, usageData -> usagesToRemove[index] += usageData },
|
{ index, usageData -> usagesToRemove[index] += usageData },
|
||||||
{ otherUsages = true }
|
{ 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")
|
@TestMetadata("idea/testData/intentions/ifNullToElvis")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
|||||||
Reference in New Issue
Block a user