Add quickfix for missing 'constructor' inside primary ctor

This commit is contained in:
Denis Zharkov
2015-05-12 18:04:27 +03:00
parent a2578d0f46
commit a9c1c3edb6
13 changed files with 133 additions and 1 deletions
@@ -17,6 +17,7 @@
package org.jetbrains.kotlin.psi;
import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.lexer.JetModifierKeywordToken;
@@ -85,6 +86,11 @@ public class JetPrimaryConstructor extends JetDeclarationStub<KotlinPlaceHolderS
public boolean hasConstructorKeyword() {
if (getStub() != null) return true;
return findChildByType(JetTokens.CONSTRUCTOR_KEYWORD) != null;
return getConstructorKeyword() != null;
}
@Nullable
public PsiElement getConstructorKeyword() {
return findChildByType(JetTokens.CONSTRUCTOR_KEYWORD);
}
}
@@ -324,6 +324,9 @@ public class JetPsiFactory(private val project: Project) {
return createClass("class A()").getPrimaryConstructor()!!
}
public fun createConstructorKeyword(): PsiElement =
createClass("class A constructor()").getPrimaryConstructor()!!.getConstructorKeyword()!!
public fun createClassLabel(labelName: String): JetSimpleNameExpression {
return (createExpression("this@" + labelName) as JetThisExpression).getTargetLabel()!!
}
@@ -0,0 +1,51 @@
/*
* 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.quickfix
import com.intellij.codeInsight.intention.IntentionAction
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.idea.quickfix.quickfixUtil.createIntentionFactory
import org.jetbrains.kotlin.idea.quickfix.quickfixUtil.createIntentionForFirstParentOfType
import org.jetbrains.kotlin.psi.JetFile
import org.jetbrains.kotlin.psi.JetPrimaryConstructor
import org.jetbrains.kotlin.idea.quickfix.quickfixUtil.addConstructorKeyword
public class MissingConstructorKeywordFix(element: JetPrimaryConstructor) : JetIntentionAction<JetPrimaryConstructor>(element) {
override fun getFamilyName(): String = getText()
override fun getText(): String = "Add 'constructor' keyword"
override fun invoke(project: Project, editor: Editor?, file: JetFile?) {
element.addConstructorKeyword()
}
companion object : JetSingleIntentionActionFactory() {
override fun createAction(diagnostic: Diagnostic): IntentionAction? =
diagnostic.createIntentionForFirstParentOfType(::MissingConstructorKeywordFix)
public fun createWholeProjectFixFactory(): JetSingleIntentionActionFactory = createIntentionFactory {
JetWholeProjectForEachElementOfTypeFix.createByPredicate<JetPrimaryConstructor>(
predicate = { it.getModifierList() != null && !it.hasConstructorKeyword() },
taskProcessor = { it.addConstructorKeyword() },
modalTitle = "Adding missing 'constructor' keyword",
name = "Add missing 'constructor' keyword in whole project",
familyName = "Add missing 'constructor' keyword in whole project"
)
}
}
}
@@ -334,5 +334,8 @@ public class QuickFixRegistrar {
QuickFixes.factories.put(ENUM_ENTRY_USES_DEPRECATED_OR_NO_DELIMITER, DeprecatedEnumEntryDelimiterSyntaxFix.Companion);
QuickFixes.factories.put(ENUM_ENTRY_USES_DEPRECATED_OR_NO_DELIMITER, DeprecatedEnumEntryDelimiterSyntaxFix.Companion.createWholeProjectFixFactory());
QuickFixes.factories.put(MISSING_CONSTRUCTOR_KEYWORD, MissingConstructorKeywordFix.Companion);
QuickFixes.factories.put(MISSING_CONSTRUCTOR_KEYWORD, MissingConstructorKeywordFix.Companion.createWholeProjectFixFactory());
}
}
@@ -21,6 +21,8 @@ import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.idea.quickfix.JetIntentionAction
import org.jetbrains.kotlin.idea.quickfix.JetSingleIntentionActionFactory
import org.jetbrains.kotlin.psi.JetPrimaryConstructor
import org.jetbrains.kotlin.psi.JetPsiFactory
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
inline fun <reified T : PsiElement> Diagnostic.createIntentionForFirstParentOfType(
@@ -33,3 +35,8 @@ fun createIntentionFactory(
) = object : JetSingleIntentionActionFactory() {
override fun createAction(diagnostic: Diagnostic) = factory(diagnostic)
}
public fun JetPrimaryConstructor.addConstructorKeyword(): PsiElement? {
val keyword = JetPsiFactory(this).createConstructorKeyword()
return addAfter(keyword, getModifierList() ?: return null)
}
@@ -0,0 +1,3 @@
// "Add 'constructor' keyword" "true"
class A private<caret>()
@@ -0,0 +1,3 @@
// "Add 'constructor' keyword" "true"
class A private constructor()
@@ -0,0 +1 @@
class Q private constructor()
@@ -0,0 +1,12 @@
// "Add missing 'constructor' keyword in whole project" "true"
annotation class Ann(val x: Int = 1)
class A @Ann(1)private constructor(x: Int) {
inner class B() // do not insert here
inner class C protected constructor() {
fun foo() {
@data class Local private constructor()
}
}
}
@@ -0,0 +1,12 @@
// "Add missing 'constructor' keyword in whole project" "true"
annotation class Ann(val x: Int = 1)
class A @Ann(1)<caret>private (x: Int) {
inner class B() // do not insert here
inner class C protected () {
fun foo() {
@data class Local private()
}
}
}
@@ -903,6 +903,21 @@ public class QuickFixMultiFileTestGenerated extends AbstractQuickFixMultiFileTes
}
}
@TestMetadata("idea/testData/quickfix/migration/missingConstructorKeyword")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class MissingConstructorKeyword extends AbstractQuickFixMultiFileTest {
public void testAllFilesPresentInMissingConstructorKeyword() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/migration/missingConstructorKeyword"), Pattern.compile("^(\\w+)\\.before\\.Main\\.kt$"), true);
}
@TestMetadata("manyFilesMuitliple.before.Main.kt")
public void testManyFilesMuitliple() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/migration/missingConstructorKeyword/manyFilesMuitliple.before.Main.kt");
doTestWithExtraFile(fileName);
}
}
@TestMetadata("idea/testData/quickfix/migration/replaceJavaClassAsAnnotationParameter")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -3280,6 +3280,21 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
}
}
@TestMetadata("idea/testData/quickfix/migration/missingConstructorKeyword")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class MissingConstructorKeyword extends AbstractQuickFixTest {
public void testAllFilesPresentInMissingConstructorKeyword() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/migration/missingConstructorKeyword"), Pattern.compile("^(\\w+)\\.kt$"), true);
}
@TestMetadata("basic.kt")
public void testBasic() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/migration/missingConstructorKeyword/basic.kt");
doTest(fileName);
}
}
@TestMetadata("idea/testData/quickfix/migration/replaceJavaClassAsAnnotationParameter")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)