From c90d283ff52c20ec492de8188f40fc9e06fc5b99 Mon Sep 17 00:00:00 2001 From: Alexey Sedunov Date: Wed, 23 Dec 2015 15:30:37 +0300 Subject: [PATCH] Quick Fixes: Implement 'Add val/var to primary constructor parameter' quick-fix for data classes #KT-4038 Fixed --- .../after.kt.template | 0 .../before.kt.template | 0 .../description.html | 0 idea/src/META-INF/plugin.xml | 2 +- .../AddValVarToConstructorParameterAction.kt | 90 +++++++++++++++++++ ...ddValVarToConstructorParameterIntention.kt | 56 ------------ .../kotlin/idea/quickfix/QuickFixRegistrar.kt | 3 + .../intentions/addValOrVar/.intention | 2 +- .../intentions/addValOrVar/dataClass.kt | 5 ++ idea/testData/quickfix/addValVar/addVal.kt | 4 + .../quickfix/addValVar/addVal.kt.after | 4 + .../intentions/IntentionTestGenerated.java | 6 ++ .../idea/quickfix/QuickFixTestGenerated.java | 15 ++++ 13 files changed, 129 insertions(+), 58 deletions(-) rename idea/resources/intentionDescriptions/{AddValVarToConstructorParameterIntention => AddValVarToConstructorParameterActionIntention}/after.kt.template (100%) rename idea/resources/intentionDescriptions/{AddValVarToConstructorParameterIntention => AddValVarToConstructorParameterActionIntention}/before.kt.template (100%) rename idea/resources/intentionDescriptions/{AddValVarToConstructorParameterIntention => AddValVarToConstructorParameterActionIntention}/description.html (100%) create mode 100644 idea/src/org/jetbrains/kotlin/idea/intentions/AddValVarToConstructorParameterAction.kt delete mode 100644 idea/src/org/jetbrains/kotlin/idea/intentions/AddValVarToConstructorParameterIntention.kt create mode 100644 idea/testData/intentions/addValOrVar/dataClass.kt create mode 100644 idea/testData/quickfix/addValVar/addVal.kt create mode 100644 idea/testData/quickfix/addValVar/addVal.kt.after diff --git a/idea/resources/intentionDescriptions/AddValVarToConstructorParameterIntention/after.kt.template b/idea/resources/intentionDescriptions/AddValVarToConstructorParameterActionIntention/after.kt.template similarity index 100% rename from idea/resources/intentionDescriptions/AddValVarToConstructorParameterIntention/after.kt.template rename to idea/resources/intentionDescriptions/AddValVarToConstructorParameterActionIntention/after.kt.template diff --git a/idea/resources/intentionDescriptions/AddValVarToConstructorParameterIntention/before.kt.template b/idea/resources/intentionDescriptions/AddValVarToConstructorParameterActionIntention/before.kt.template similarity index 100% rename from idea/resources/intentionDescriptions/AddValVarToConstructorParameterIntention/before.kt.template rename to idea/resources/intentionDescriptions/AddValVarToConstructorParameterActionIntention/before.kt.template diff --git a/idea/resources/intentionDescriptions/AddValVarToConstructorParameterIntention/description.html b/idea/resources/intentionDescriptions/AddValVarToConstructorParameterActionIntention/description.html similarity index 100% rename from idea/resources/intentionDescriptions/AddValVarToConstructorParameterIntention/description.html rename to idea/resources/intentionDescriptions/AddValVarToConstructorParameterActionIntention/description.html diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index 581fbf9950e..c51c0591547 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -1116,7 +1116,7 @@ - org.jetbrains.kotlin.idea.intentions.AddValVarToConstructorParameterIntention + org.jetbrains.kotlin.idea.intentions.AddValVarToConstructorParameterAction$Intention Kotlin diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/AddValVarToConstructorParameterAction.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/AddValVarToConstructorParameterAction.kt new file mode 100644 index 00000000000..a722894bf56 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/AddValVarToConstructorParameterAction.kt @@ -0,0 +1,90 @@ +/* + * Copyright 2010-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.idea.intentions + +import com.intellij.codeInsight.template.TemplateBuilderImpl +import com.intellij.codeInsight.template.TemplateManager +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.project.Project +import com.intellij.openapi.util.TextRange +import com.intellij.psi.PsiDocumentManager +import org.jetbrains.kotlin.diagnostics.Diagnostic +import org.jetbrains.kotlin.diagnostics.Errors +import org.jetbrains.kotlin.idea.quickfix.KotlinQuickFixAction +import org.jetbrains.kotlin.idea.quickfix.KotlinSingleIntentionActionFactory +import org.jetbrains.kotlin.idea.refactoring.ValVarExpression +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.createSmartPointer +import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType + +interface AddValVarToConstructorParameterAction { + companion object { + val actionFamily = "Add val/var to primary constructor parameter" + } + + fun getActionText(element: KtParameter) = "Add val/var to parameter '${element.name ?: ""}'" + + fun canInvoke(element: KtParameter): Boolean { + return element.valOrVarKeyword == null && (element.parent as? KtParameterList)?.parent is KtPrimaryConstructor + } + + fun invoke(element: KtParameter, editor: Editor?) { + val project = element.project + + element.addAfter(KtPsiFactory(project).createValKeyword(), null) + + if (editor == null) return + + val parameter = element.createSmartPointer().let { + PsiDocumentManager.getInstance(project).commitAllDocuments() + PsiDocumentManager.getInstance(project).doPostponedOperationsAndUnblockDocument(editor.document) + it.element + } ?: return + + TemplateBuilderImpl(parameter) + .apply { replaceElement(parameter.valOrVarKeyword!!, ValVarExpression) } + .buildInlineTemplate() + .let { TemplateManager.getInstance(project).startTemplate(editor, it) } + } + + class Intention : + SelfTargetingRangeIntention(KtParameter::class.java, actionFamily), + AddValVarToConstructorParameterAction { + override fun applicabilityRange(element: KtParameter): TextRange? { + if (!canInvoke(element)) return null + if (element.getStrictParentOfType()?.isData() ?: false) return null + text = getActionText(element) + return element.nameIdentifier?.textRange + } + + override fun applyTo(element: KtParameter, editor: Editor) = invoke(element, editor) + } + + class QuickFix(parameter: KtParameter) : + KotlinQuickFixAction(parameter), + AddValVarToConstructorParameterAction { + override fun getText() = getActionText(element) + + override fun getFamilyName() = actionFamily + + override fun invoke(project: Project, editor: Editor?, file: KtFile) = invoke(element, editor) + } + + object QuickFixFactory : KotlinSingleIntentionActionFactory() { + override fun createAction(diagnostic: Diagnostic) = QuickFix(Errors.DATA_CLASS_NOT_PROPERTY_PARAMETER.cast(diagnostic).psiElement) + } +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/AddValVarToConstructorParameterIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/AddValVarToConstructorParameterIntention.kt deleted file mode 100644 index a781a47a5bb..00000000000 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/AddValVarToConstructorParameterIntention.kt +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2010-2015 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.jetbrains.kotlin.idea.intentions - -import com.intellij.codeInsight.template.TemplateBuilderImpl -import com.intellij.codeInsight.template.TemplateManager -import com.intellij.openapi.editor.Editor -import com.intellij.openapi.util.TextRange -import com.intellij.psi.PsiDocumentManager -import org.jetbrains.kotlin.idea.refactoring.ValVarExpression -import org.jetbrains.kotlin.psi.KtParameter -import org.jetbrains.kotlin.psi.KtParameterList -import org.jetbrains.kotlin.psi.KtPrimaryConstructor -import org.jetbrains.kotlin.psi.KtPsiFactory -import org.jetbrains.kotlin.psi.psiUtil.createSmartPointer - -class AddValVarToConstructorParameterIntention : SelfTargetingRangeIntention(KtParameter::class.java, - "Add val/var to primary constructor parameter") { - override fun applicabilityRange(element: KtParameter): TextRange? { - if (element.valOrVarKeyword != null) return null - if ((element.parent as? KtParameterList)?.parent !is KtPrimaryConstructor) return null - text = "Add val/var to parameter '${element.name ?: ""}'" - return element.nameIdentifier?.textRange - } - - override fun applyTo(element: KtParameter, editor: Editor) { - val project = element.project - - element.addAfter(KtPsiFactory(project).createValKeyword(), null) - - val parameter = element.createSmartPointer().let { - PsiDocumentManager.getInstance(project).commitAllDocuments() - PsiDocumentManager.getInstance(project).doPostponedOperationsAndUnblockDocument(editor.document) - it.element - } ?: return - - TemplateBuilderImpl(parameter) - .apply { replaceElement(parameter.valOrVarKeyword!!, ValVarExpression) } - .buildInlineTemplate() - .let { TemplateManager.getInstance(project).startTemplate(editor, it) } - } -} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt index 710f2c58ab0..269be530783 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt @@ -24,6 +24,7 @@ import org.jetbrains.kotlin.idea.inspections.AddModifierFixFactory import org.jetbrains.kotlin.idea.inspections.AddReflectionQuickFix import org.jetbrains.kotlin.idea.inspections.AddTestLibQuickFix import org.jetbrains.kotlin.idea.inspections.InfixCallFix +import org.jetbrains.kotlin.idea.intentions.AddValVarToConstructorParameterAction import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createCallable.* import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createClass.CreateClassFromCallWithConstructorCalleeActionFactory import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createClass.CreateClassFromConstructorCallActionFactory @@ -360,5 +361,7 @@ public class QuickFixRegistrar : QuickFixContributor { COMMA_IN_WHEN_CONDITION_WITHOUT_ARGUMENT.registerFactory(CommaInWhenConditionWithoutArgumentFix) UNSUPPORTED.registerFactory(UnsupportedAsyncFix) + + DATA_CLASS_NOT_PROPERTY_PARAMETER.registerFactory(AddValVarToConstructorParameterAction.QuickFixFactory) } } diff --git a/idea/testData/intentions/addValOrVar/.intention b/idea/testData/intentions/addValOrVar/.intention index 6c3841003d3..865ed8dd1de 100644 --- a/idea/testData/intentions/addValOrVar/.intention +++ b/idea/testData/intentions/addValOrVar/.intention @@ -1 +1 @@ -org.jetbrains.kotlin.idea.intentions.AddValVarToConstructorParameterIntention \ No newline at end of file +org.jetbrains.kotlin.idea.intentions.AddValVarToConstructorParameterAction$Intention \ No newline at end of file diff --git a/idea/testData/intentions/addValOrVar/dataClass.kt b/idea/testData/intentions/addValOrVar/dataClass.kt new file mode 100644 index 00000000000..362b808e20d --- /dev/null +++ b/idea/testData/intentions/addValOrVar/dataClass.kt @@ -0,0 +1,5 @@ +// IS_APPLICABLE: false +// ERROR: Data class primary constructor must have only property (val / var) parameters +data class Foo(x: Int, val y: Int) { + +} \ No newline at end of file diff --git a/idea/testData/quickfix/addValVar/addVal.kt b/idea/testData/quickfix/addValVar/addVal.kt new file mode 100644 index 00000000000..095e8765a0a --- /dev/null +++ b/idea/testData/quickfix/addValVar/addVal.kt @@ -0,0 +1,4 @@ +// "Add val/var to parameter 'x'" "true" +data class Foo(x: Int, val y: Int) { + +} \ No newline at end of file diff --git a/idea/testData/quickfix/addValVar/addVal.kt.after b/idea/testData/quickfix/addValVar/addVal.kt.after new file mode 100644 index 00000000000..86dbeb67e08 --- /dev/null +++ b/idea/testData/quickfix/addValVar/addVal.kt.after @@ -0,0 +1,4 @@ +// "Add val/var to parameter 'x'" "true" +data class Foo(val x: Int, val y: Int) { + +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java index 693d390a789..ab9c40aee82 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java @@ -376,6 +376,12 @@ public class IntentionTestGenerated extends AbstractIntentionTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/addValOrVar"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), true); } + @TestMetadata("dataClass.kt") + public void testDataClass() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/addValOrVar/dataClass.kt"); + doTest(fileName); + } + @TestMetadata("funParameter.kt") public void testFunParameter() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/addValOrVar/funParameter.kt"); diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java index 4259a378e49..bd34c1f57f7 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java @@ -509,6 +509,21 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest { } } + @TestMetadata("idea/testData/quickfix/addValVar") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class AddValVar extends AbstractQuickFixTest { + @TestMetadata("addVal.kt") + public void testAddVal() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/addValVar/addVal.kt"); + doTest(fileName); + } + + public void testAllFilesPresentInAddValVar() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/addValVar"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), true); + } + } + @TestMetadata("idea/testData/quickfix/asyncUnsupported") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)