Add intention to specify all types explicitly in destructuring assignment

#KT-16260 Fixed
This commit is contained in:
Toshiaki Kameyama
2017-08-17 09:45:36 +09:00
committed by Dmitry Jemerov
parent 3529d61a7d
commit 6b2c22aff1
21 changed files with 197 additions and 4 deletions
@@ -0,0 +1,3 @@
fun main(args: Array<String>) {
val (i<spot>: Int</spot>, s<spot>: String</spot>) = Pair(1, "s")
}
@@ -0,0 +1,3 @@
fun main(args: Array<String>) {
val <spot>(i, s)</spot> = Pair(1, "s")
}
@@ -0,0 +1,5 @@
<html>
<body>
Specifies all types explicitly in a destructuring declaration.
</body>
</html>
+5
View File
@@ -799,6 +799,11 @@
<category>Kotlin</category>
</intentionAction>
<intentionAction>
<className>org.jetbrains.kotlin.idea.intentions.SpecifyTypeExplicitlyInDestructuringAssignmentIntention</className>
<category>Kotlin</category>
</intentionAction>
<intentionAction>
<className>org.jetbrains.kotlin.idea.intentions.RemoveExplicitTypeIntention</className>
<category>Kotlin</category>
@@ -0,0 +1,43 @@
/*
* 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/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.intentions
import com.intellij.codeInsight.intention.LowPriorityAction
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.util.TextRange
import org.jetbrains.kotlin.idea.core.setType
import org.jetbrains.kotlin.psi.KtCodeFragment
import org.jetbrains.kotlin.psi.KtDestructuringDeclaration
import org.jetbrains.kotlin.psi.KtParameterList
import org.jetbrains.kotlin.psi.psiUtil.endOffset
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
import org.jetbrains.kotlin.psi.psiUtil.startOffset
import org.jetbrains.kotlin.types.isError
class SpecifyTypeExplicitlyInDestructuringAssignmentIntention : SelfTargetingRangeIntention<KtDestructuringDeclaration>(
KtDestructuringDeclaration::class.java, "Specify all types explicitly in destructuring declaration"
), LowPriorityAction {
override fun applicabilityRange(element: KtDestructuringDeclaration): TextRange? {
if (element.containingFile is KtCodeFragment) return null
val entries = element.noTypeReferenceEntries()
if (entries.isEmpty()) return null
if (entries.any { SpecifyTypeExplicitlyIntention.getTypeForDeclaration(it).isError }) return null
return TextRange(element.startOffset, element.initializer?.let { it.startOffset - 1 } ?: element.endOffset)
}
override fun applyTo(element: KtDestructuringDeclaration, editor: Editor?) {
val entries = element.noTypeReferenceEntries()
if (editor != null && element.getParentOfType<KtParameterList>(false) == null)
SpecifyTypeExplicitlyIntention.addTypeAnnotationWithTemplate(editor, entries.iterator())
else
entries.forEach {
it.setType(SpecifyTypeExplicitlyIntention.getTypeForDeclaration(it))
}
}
}
private fun KtDestructuringDeclaration.noTypeReferenceEntries() = entries.filter { it.typeReference == null }
@@ -166,18 +166,32 @@ class SpecifyTypeExplicitlyIntention :
}
}
fun createTypeReferencePostprocessor(declaration: KtCallableDeclaration): TemplateEditingAdapter {
@JvmOverloads
fun createTypeReferencePostprocessor(declaration: KtCallableDeclaration,
iterator: Iterator<KtCallableDeclaration>? = null,
editor: Editor? = null): TemplateEditingAdapter {
return object : TemplateEditingAdapter() {
override fun templateFinished(template: Template?, brokenOff: Boolean) {
val typeRef = declaration.typeReference
if (typeRef != null && typeRef.isValid) {
runWriteAction { ShortenReferences.DEFAULT.process(typeRef) }
runWriteAction {
ShortenReferences.DEFAULT.process(typeRef)
if (iterator != null && editor != null) addTypeAnnotationWithTemplate(editor, iterator)
}
}
}
}
}
private fun addTypeAnnotationWithTemplate(editor: Editor, declaration: KtCallableDeclaration, exprType: KotlinType) {
fun addTypeAnnotationWithTemplate(editor: Editor, iterator: Iterator<KtCallableDeclaration>?) {
if (iterator == null || !iterator.hasNext()) return
val declaration = iterator.next()
val exprType = getTypeForDeclaration(declaration)
addTypeAnnotationWithTemplate(editor, declaration, exprType, iterator)
}
private fun addTypeAnnotationWithTemplate(editor: Editor, declaration: KtCallableDeclaration, exprType: KotlinType,
iterator: Iterator<KtCallableDeclaration>? = null) {
assert(!exprType.isError) { "Unexpected error type, should have been checked before: " + declaration.getElementTextWithContext() + ", type = " + exprType }
val project = declaration.project
@@ -194,7 +208,10 @@ class SpecifyTypeExplicitlyIntention :
editor.caretModel.moveToOffset(newTypeRef.node.startOffset)
TemplateManager.getInstance(project).startTemplate(editor, builder.buildInlineTemplate(), createTypeReferencePostprocessor(declaration))
TemplateManager.getInstance(project).startTemplate(
editor,
builder.buildInlineTemplate(),
createTypeReferencePostprocessor(declaration, iterator, editor))
}
}
}
@@ -0,0 +1 @@
org.jetbrains.kotlin.idea.intentions.SpecifyTypeExplicitlyInDestructuringAssignmentIntention
@@ -0,0 +1,6 @@
// WITH_RUNTIME
fun test() {
val map = mapOf(1 to "two")
for (<caret>(key, value) in map) {
}
}
@@ -0,0 +1,6 @@
// WITH_RUNTIME
fun test() {
val map = mapOf(1 to "two")
for ((key: Int, value: String<caret>) in map) {
}
}
@@ -0,0 +1,6 @@
// WITH_RUNTIME
fun test() {
val list = emptyList<Pair<Int, String>>()
list.forEach { (i, s)<caret> ->
}
}
@@ -0,0 +1,6 @@
// WITH_RUNTIME
fun test() {
val list = emptyList<Pair<Int, String>>()
list.forEach { (i: Int, s: String)<caret> ->
}
}
@@ -0,0 +1,6 @@
// WITH_RUNTIME
fun test() {
val list = emptyList<Pair<Int, String>>()
list.forEach { (i, s)<caret>: Pair<Int, String> ->
}
}
@@ -0,0 +1,6 @@
// WITH_RUNTIME
fun test() {
val list = emptyList<Pair<Int, String>>()
list.forEach { (i: Int, s: String)<caret>: Pair<Int, String> ->
}
}
@@ -0,0 +1,5 @@
// IS_APPLICABLE: false
// WITH_RUNTIME
fun test() {
val (i: Int, s: String)<caret> = Pair(1, "s")
}
@@ -0,0 +1,4 @@
// WITH_RUNTIME
fun test() {
val (i, s)<caret> = Pair(1, "s")
}
@@ -0,0 +1,4 @@
// WITH_RUNTIME
fun test() {
val (i: Int, s: String<caret>) = Pair(1, "s")
}
@@ -0,0 +1,4 @@
// WITH_RUNTIME
fun test() {
val (i: Int, s) <caret>= Pair(1, "s")
}
@@ -0,0 +1,4 @@
// WITH_RUNTIME
fun test() {
val (i: Int, s: String<caret>) = Pair(1, "s")
}
@@ -0,0 +1,4 @@
// WITH_RUNTIME
fun test() {
val (_, s) =<caret> Pair(1, "s")
}
@@ -0,0 +1,4 @@
// WITH_RUNTIME
fun test() {
val (_: Int, s: String<caret>) = Pair(1, "s")
}
@@ -14724,6 +14724,57 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
}
}
@TestMetadata("idea/testData/intentions/specifyTypeExplicitlyInDestructuringAssignment")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class SpecifyTypeExplicitlyInDestructuringAssignment extends AbstractIntentionTest {
public void testAllFilesPresentInSpecifyTypeExplicitlyInDestructuringAssignment() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/specifyTypeExplicitlyInDestructuringAssignment"), Pattern.compile("^([\\w\\-_]+)\\.(kt|kts)$"), TargetBackend.ANY, true);
}
@TestMetadata("in.kt")
public void testIn() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/specifyTypeExplicitlyInDestructuringAssignment/in.kt");
doTest(fileName);
}
@TestMetadata("lambda.kt")
public void testLambda() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/specifyTypeExplicitlyInDestructuringAssignment/lambda.kt");
doTest(fileName);
}
@TestMetadata("lambdaHasSignature.kt")
public void testLambdaHasSignature() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/specifyTypeExplicitlyInDestructuringAssignment/lambdaHasSignature.kt");
doTest(fileName);
}
@TestMetadata("variableHasAllTypes.kt")
public void testVariableHasAllTypes() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/specifyTypeExplicitlyInDestructuringAssignment/variableHasAllTypes.kt");
doTest(fileName);
}
@TestMetadata("variableHasNoTypes.kt")
public void testVariableHasNoTypes() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/specifyTypeExplicitlyInDestructuringAssignment/variableHasNoTypes.kt");
doTest(fileName);
}
@TestMetadata("variableHasTypes.kt")
public void testVariableHasTypes() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/specifyTypeExplicitlyInDestructuringAssignment/variableHasTypes.kt");
doTest(fileName);
}
@TestMetadata("variableHasUnderscore.kt")
public void testVariableHasUnderscore() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/specifyTypeExplicitlyInDestructuringAssignment/variableHasUnderscore.kt");
doTest(fileName);
}
}
@TestMetadata("idea/testData/intentions/splitIf")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)