diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index 27215afc091..b9e9fa7319e 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -756,6 +756,9 @@ public interface Errors { TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER, TYPE_INFERENCE_CONFLICTING_SUBSTITUTIONS, TYPE_INFERENCE_PARAMETER_CONSTRAINT_ERROR, TYPE_INFERENCE_UPPER_BOUND_VIOLATED, TYPE_INFERENCE_EXPECTED_TYPE_MISMATCH); + ImmutableSet> MUST_BE_INITIALIZED_DIAGNOSTICS = ImmutableSet.of( + MUST_BE_INITIALIZED, MUST_BE_INITIALIZED_OR_BE_ABSTRACT + ); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt b/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt index ee1492014a0..5a0d4219d34 100644 --- a/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt +++ b/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt @@ -44,6 +44,7 @@ import org.jetbrains.kotlin.idea.AbstractSmartSelectionTest import org.jetbrains.kotlin.idea.actions.AbstractGotoTestOrCodeActionTest import org.jetbrains.kotlin.idea.codeInsight.* import org.jetbrains.kotlin.idea.codeInsight.generate.AbstractGenerateActionTest +import org.jetbrains.kotlin.idea.codeInsight.generate.AbstractGenerateTestSupportMethodActionTest import org.jetbrains.kotlin.idea.codeInsight.moveUpDown.AbstractCodeMoverTest import org.jetbrains.kotlin.idea.codeInsight.surroundWith.AbstractSurroundWithTest import org.jetbrains.kotlin.idea.codeInsight.unwrap.AbstractUnwrapRemoveTest @@ -731,8 +732,12 @@ fun main(args: Array) { model("kdoc/typing") } + testClass() { + model("codeInsight/generate/testFrameworkSupport") + } + testClass() { - model("codeInsight/generate") + model("codeInsight/generate/secondaryConstructors") } } diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/quickfix/generateUtil.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/quickfix/generateUtil.kt index 4df274c1546..3da13c6070e 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/quickfix/generateUtil.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/quickfix/generateUtil.kt @@ -142,20 +142,21 @@ private fun removeAfterOffset(offset: Int, whiteSpace: PsiWhiteSpace): PsiElemen return whiteSpace } -public fun generateMembers( +public fun insertMembersAfter( editor: Editor, classOrObject: JetClassOrObject, - generators: Collection<() -> T> + members: Collection, + anchor: PsiElement? = null ): List { - generators.ifEmpty { return emptyList() } + members.ifEmpty { return emptyList() } return runWriteAction> { val body = classOrObject.getOrCreateBody() - var afterAnchor = findInsertAfterAnchor(editor, body) ?: return@runWriteAction emptyList() - val insertedMembers = generators.mapTo(SmartList()) { + var afterAnchor = anchor ?: findInsertAfterAnchor(editor, body) ?: return@runWriteAction emptyList() + val insertedMembers = members.mapTo(SmartList()) { @Suppress("UNCHECKED_CAST") - (body.addAfter(it(), afterAnchor) as T).apply { afterAnchor = this } + (body.addAfter(it, afterAnchor) as T).apply { afterAnchor = this } } ShortenReferences.DEFAULT.process(insertedMembers) @@ -166,6 +167,6 @@ public fun generateMembers( } } -public fun generateMember(editor: Editor, classOrObject: JetClassOrObject, declaration: T): T { - return generateMembers(editor, classOrObject, listOf({ declaration })).single() +public fun insertMember(editor: Editor, classOrObject: JetClassOrObject, declaration: T): T { + return insertMembersAfter(editor, classOrObject, listOf(declaration)).single() } \ No newline at end of file diff --git a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/overrideImplement/OverrideImplementMembersHandler.kt b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/overrideImplement/OverrideImplementMembersHandler.kt index 3eb9bba7102..f1655aa393b 100644 --- a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/overrideImplement/OverrideImplementMembersHandler.kt +++ b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/overrideImplement/OverrideImplementMembersHandler.kt @@ -26,7 +26,7 @@ import com.intellij.psi.PsiDocumentManager import com.intellij.psi.PsiFile import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor -import org.jetbrains.kotlin.idea.quickfix.generateMembers +import org.jetbrains.kotlin.idea.quickfix.insertMembersAfter import org.jetbrains.kotlin.psi.JetClassOrObject import org.jetbrains.kotlin.psi.JetFile import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType @@ -90,7 +90,7 @@ public abstract class OverrideImplementMembersHandler : LanguageCodeInsightActio companion object { public fun generateMembers(editor: Editor, classOrObject: JetClassOrObject, selectedElements: Collection) { val project = classOrObject.project - generateMembers(editor, classOrObject, selectedElements.map { chooser -> { chooser.generateMember(project) } }) + insertMembersAfter(editor, classOrObject, selectedElements.map { it.generateMember(project) }) } } } diff --git a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/psiModificationUtils.kt b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/psiModificationUtils.kt index 5577fad8e28..10736d22be6 100644 --- a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/psiModificationUtils.kt +++ b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/psiModificationUtils.kt @@ -21,8 +21,6 @@ import com.intellij.psi.PsiWhiteSpace import com.intellij.psi.impl.source.codeStyle.CodeEditUtil import com.intellij.psi.util.PsiTreeUtil import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor -import org.jetbrains.kotlin.descriptors.Visibilities -import org.jetbrains.kotlin.descriptors.Visibility import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor import org.jetbrains.kotlin.lexer.JetModifierKeywordToken import org.jetbrains.kotlin.lexer.JetTokens @@ -103,16 +101,21 @@ public fun JetCallExpression.moveFunctionLiteralOutsideParentheses() { } } -public fun JetBlockExpression.appendElement(element: JetElement): JetElement { +public fun JetBlockExpression.appendElement(element: JetElement, addNewLine: Boolean = false): JetElement { val rBrace = getRBrace() + val newLine = JetPsiFactory(this).createNewLine() val anchor = if (rBrace == null) { val lastChild = getLastChild() - if (lastChild !is PsiWhiteSpace) addAfter(JetPsiFactory(this).createNewLine(), lastChild)!! else lastChild + if (lastChild !is PsiWhiteSpace) addAfter(newLine, lastChild)!! else lastChild } else { rBrace.getPrevSibling()!! } - return addAfter(element, anchor)!! as JetElement + val addedElement = addAfter(element, anchor)!! as JetElement + if (addNewLine) { + addAfter(newLine, addedElement) + } + return addedElement } //TODO: git rid of this method diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index 4bdf6d2771e..8291c9b3bca 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -169,6 +169,10 @@ + + diff --git a/idea/src/org/jetbrains/kotlin/idea/actions/generate/KotlinGenerateActionBase.kt b/idea/src/org/jetbrains/kotlin/idea/actions/generate/KotlinGenerateActionBase.kt index 77e76887cbb..163e5e2d982 100644 --- a/idea/src/org/jetbrains/kotlin/idea/actions/generate/KotlinGenerateActionBase.kt +++ b/idea/src/org/jetbrains/kotlin/idea/actions/generate/KotlinGenerateActionBase.kt @@ -23,14 +23,13 @@ import com.intellij.openapi.actionSystem.DataContext import com.intellij.openapi.actionSystem.Presentation import com.intellij.openapi.editor.Editor import com.intellij.openapi.project.Project -import com.intellij.psi.PsiDocumentManager import com.intellij.psi.PsiFile import org.jetbrains.kotlin.idea.core.refactoring.canRefactor import org.jetbrains.kotlin.psi.JetClassOrObject import org.jetbrains.kotlin.psi.JetFile import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType -abstract class KotlinGenerateActionBase() : CodeInsightAction() { +abstract class KotlinGenerateActionBase() : CodeInsightAction(), CodeInsightActionHandler { override fun update( presentation: Presentation, project: Project, @@ -58,4 +57,8 @@ abstract class KotlinGenerateActionBase() : CodeInsightAction() { } protected abstract fun isValidForClass(targetClass: JetClassOrObject): Boolean + + override fun startInWriteAction() = false + + override fun getHandler() = this } \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/actions/generate/KotlinGenerateMemberActionBase.kt b/idea/src/org/jetbrains/kotlin/idea/actions/generate/KotlinGenerateMemberActionBase.kt new file mode 100644 index 00000000000..c4874deccad --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/actions/generate/KotlinGenerateMemberActionBase.kt @@ -0,0 +1,47 @@ +/* + * 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.actions.generate + +import com.intellij.codeInsight.CodeInsightUtilBase +import com.intellij.codeInsight.generation.GenerateMembersUtil +import com.intellij.codeInspection.ex.GlobalInspectionContextBase +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.fileEditor.FileDocumentManager +import com.intellij.openapi.project.Project +import com.intellij.psi.PsiFile +import org.jetbrains.kotlin.idea.util.application.executeWriteCommand +import org.jetbrains.kotlin.psi.JetClassOrObject +import org.jetbrains.kotlin.psi.JetDeclaration + +abstract class KotlinGenerateMemberActionBase : KotlinGenerateActionBase() { + protected abstract fun prepareMembersInfo(klass: JetClassOrObject, project: Project): Info? + + protected abstract fun generateMembers(editor: Editor, info: Info): List + + override fun invoke(project: Project, editor: Editor, file: PsiFile) { + if (!CodeInsightUtilBase.prepareEditorForWrite(editor)) return + if (!FileDocumentManager.getInstance().requestWriting(editor.document, project)) return + val klass = getTargetClass(editor, file) ?: return + val membersInfo = prepareMembersInfo(klass, project) ?: return + + project.executeWriteCommand(commandName, this) { + val newMembers = generateMembers(editor, membersInfo) + GlobalInspectionContextBase.cleanupElements(project, null, *newMembers.toTypedArray()) + newMembers.firstOrNull()?.let { GenerateMembersUtil.positionCaret(editor, it, false) } + } + } +} diff --git a/idea/src/org/jetbrains/kotlin/idea/actions/generate/KotlinGenerateSecondaryConstructorAction.kt b/idea/src/org/jetbrains/kotlin/idea/actions/generate/KotlinGenerateSecondaryConstructorAction.kt new file mode 100644 index 00000000000..3164829e499 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/actions/generate/KotlinGenerateSecondaryConstructorAction.kt @@ -0,0 +1,201 @@ +/* + * 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.actions.generate + +import com.intellij.codeInsight.CodeInsightBundle +import com.intellij.ide.util.MemberChooser +import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.project.Project +import com.intellij.psi.PsiElement +import com.intellij.refactoring.util.CommonRefactoringUtil +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.ConstructorDescriptor +import org.jetbrains.kotlin.descriptors.PropertyDescriptor +import org.jetbrains.kotlin.diagnostics.Errors +import org.jetbrains.kotlin.idea.caches.resolve.analyzeFully +import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde +import org.jetbrains.kotlin.idea.core.CollectingNameValidator +import org.jetbrains.kotlin.idea.core.KotlinNameSuggester +import org.jetbrains.kotlin.idea.core.appendElement +import org.jetbrains.kotlin.idea.core.isVisible +import org.jetbrains.kotlin.idea.core.util.DescriptorMemberChooserObject +import org.jetbrains.kotlin.idea.quickfix.insertMembersAfter +import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.siblings +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns +import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny +import org.jetbrains.kotlin.resolve.source.getPsi +import org.jetbrains.kotlin.types.JetType +import org.jetbrains.kotlin.types.TypeSubstitutor +import org.jetbrains.kotlin.types.Variance +import org.jetbrains.kotlin.types.checker.JetTypeChecker +import org.jetbrains.kotlin.types.substitutions.getTypeSubstitutor +import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull +import org.jetbrains.kotlin.utils.addToStdlib.lastIsInstanceOrNull +import org.jetbrains.kotlin.utils.addToStdlib.singletonOrEmptyList +import java.util.* + +class KotlinGenerateSecondaryConstructorAction : KotlinGenerateMemberActionBase() { + class Info( + val propertiesToInitialize: List, + val superConstructors: List, + val classDescriptor: ClassDescriptor + ) + + override fun isValidForClass(targetClass: JetClassOrObject): Boolean { + return targetClass is JetClass && targetClass !is JetEnumEntry && !targetClass.isInterface() && !targetClass.isAnnotation() + } + + private fun shouldPreselect(element: PsiElement) = element is JetProperty && !element.isVar + + private fun chooseSuperConstructors(klass: JetClassOrObject, classDescriptor: ClassDescriptor): List { + val project = klass.project + val superClassDescriptor = classDescriptor.getSuperClassNotAny() ?: return emptyList() + val candidates = superClassDescriptor.constructors + .filter { it.isVisible(classDescriptor) } + .map { DescriptorMemberChooserObject(DescriptorToSourceUtilsIde.getAnyDeclaration(project, it) ?: klass, it) } + if (ApplicationManager.getApplication().isUnitTestMode || candidates.size <= 1) return candidates + + return with(MemberChooser(candidates.toTypedArray(), false, true, klass.project)) { + title = CodeInsightBundle.message("generate.constructor.super.constructor.chooser.title") + setCopyJavadocVisible(false) + show() + + selectedElements ?: emptyList() + } + } + + private fun choosePropertiesToInitialize(klass: JetClassOrObject, context: BindingContext): List { + val candidates = klass.declarations + .filterIsInstance() + .filter { it.isVar || context.diagnostics.forElement(it).any { it.factory in Errors.MUST_BE_INITIALIZED_DIAGNOSTICS } } + .map { context.get(BindingContext.VARIABLE, it) as PropertyDescriptor } + .map { DescriptorMemberChooserObject(it.source.getPsi()!!, it) } + if (ApplicationManager.getApplication().isUnitTestMode || candidates.isEmpty) return candidates + + return with(MemberChooser(candidates.toTypedArray(), true, true, klass.project, false, null)) { + title = "Choose Properties to Initialize by Constructor" + setCopyJavadocVisible(false) + selectElements(candidates.filter { shouldPreselect(it.element) }.toTypedArray()) + show() + + selectedElements ?: emptyList() + } + } + + override fun prepareMembersInfo(klass: JetClassOrObject, project: Project): Info? { + val context = klass.analyzeFully() + val classDescriptor = context.get(BindingContext.CLASS, klass) ?: return null + val superConstructors = chooseSuperConstructors(klass, classDescriptor).map { it.descriptor as ConstructorDescriptor } + val propertiesToInitialize = choosePropertiesToInitialize(klass, context).map { it.descriptor as PropertyDescriptor } + return Info(propertiesToInitialize, superConstructors, classDescriptor) + } + + override fun generateMembers(editor: Editor, info: Info): List { + val targetClass = info.classDescriptor.source.getPsi() as? JetClass ?: return emptyList() + + fun Info.findAnchor(): PsiElement? { + targetClass.declarations.lastIsInstanceOrNull()?.let { return it } + val lastPropertyToInitialize = propertiesToInitialize.lastOrNull()?.source?.getPsi() + val declarationsAfter = lastPropertyToInitialize?.siblings()?.filterIsInstance() ?: targetClass.declarations.asSequence() + val firstNonProperty = declarationsAfter.firstOrNull { it !is JetProperty } ?: return null + return firstNonProperty.siblings(forward = false).firstIsInstanceOrNull() ?: targetClass.getOrCreateBody().lBrace + } + + return with(info) { + val prototypes = if (superConstructors.isNotEmpty()) { + superConstructors.map { generateConstructor(classDescriptor, propertiesToInitialize, it) }.filterNotNull() + } else { + generateConstructor(classDescriptor, propertiesToInitialize, null).singletonOrEmptyList() + } + + if (prototypes.isEmpty) { + CommonRefactoringUtil.showErrorHint(targetClass.project, editor, "Constructor already exists", commandName, null) + return emptyList() + } + + insertMembersAfter(editor, targetClass, prototypes, findAnchor()) + } + } + + private fun generateConstructor( + classDescriptor: ClassDescriptor, + propertiesToInitialize: List, + superConstructor: ConstructorDescriptor? + ): JetSecondaryConstructor? { + fun equalTypes(types1: Collection, types2: Collection): Boolean { + return types1.size == types2.size && (types1.zip(types2)).all { JetTypeChecker.DEFAULT.equalTypes(it.first, it.second) } + } + + val constructorParamTypes = propertiesToInitialize.map { it.type } + + (superConstructor?.valueParameters?.map { it.varargElementType ?: it.type } ?: emptyList()) + + if (classDescriptor.constructors.any { it.source.getPsi() is JetConstructor<*> + && equalTypes(it.valueParameters.map { it.varargElementType ?: it.type }, constructorParamTypes) }) return null + + val targetClass = classDescriptor.source.getPsi() as JetClass + val psiFactory = JetPsiFactory(targetClass) + + val validator = CollectingNameValidator() + + val constructor = psiFactory.createSecondaryConstructor("constructor()") + val parameterList = constructor.valueParameterList!! + + if (superConstructor != null) { + val substitutor = getTypeSubstitutor(superConstructor.containingDeclaration.defaultType, classDescriptor.defaultType) + ?: TypeSubstitutor.EMPTY + val delegationCallArguments = ArrayList() + for (parameter in superConstructor.valueParameters) { + val isVararg = parameter.varargElementType != null + + val paramName = KotlinNameSuggester.suggestNameByName(parameter.name.asString(), validator) + + val typeToUse = parameter.varargElementType ?: parameter.type + val paramType = IdeDescriptorRenderers.SOURCE_CODE.renderType( + substitutor.substitute(typeToUse, Variance.INVARIANT) ?: classDescriptor.builtIns.anyType + ) + + val modifiers = if (isVararg) "vararg " else "" + + parameterList.addParameter(psiFactory.createParameter("$modifiers$paramName: $paramType")) + delegationCallArguments.add(if (isVararg) "*$paramName" else paramName) + } + + val delegationCall = psiFactory.createConstructorDelegationCall(delegationCallArguments.joinToString(prefix = "super(", postfix = ")")) + constructor.replaceImplicitDelegationCallWithExplicit(false).replace(delegationCall) + } + + if (propertiesToInitialize.isNotEmpty()) { + val body = psiFactory.createEmptyBody() + for (property in propertiesToInitialize) { + val propertyName = property.name + val paramName = KotlinNameSuggester.suggestNameByName(propertyName.asString(), validator) + val paramType = IdeDescriptorRenderers.SOURCE_CODE.renderType(property.type) + + parameterList.addParameter(psiFactory.createParameter("$paramName: $paramType")) + body.appendElement(psiFactory.createExpression("this.$propertyName = $paramName"), true) + } + + constructor.add(body) + } + + return constructor + } +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/actions/generate/KotlinGenerateTestSupportActionBase.kt b/idea/src/org/jetbrains/kotlin/idea/actions/generate/KotlinGenerateTestSupportActionBase.kt index 89fe7840116..d2a92227f25 100644 --- a/idea/src/org/jetbrains/kotlin/idea/actions/generate/KotlinGenerateTestSupportActionBase.kt +++ b/idea/src/org/jetbrains/kotlin/idea/actions/generate/KotlinGenerateTestSupportActionBase.kt @@ -16,7 +16,6 @@ package org.jetbrains.kotlin.idea.actions.generate -import com.intellij.codeInsight.CodeInsightActionHandler import com.intellij.codeInsight.generation.actions.GenerateActionPopupTemplateInjector import com.intellij.codeInsight.hint.HintManager import com.intellij.ide.fileTemplates.FileTemplateManager @@ -48,7 +47,7 @@ import org.jetbrains.kotlin.idea.core.overrideImplement.OverrideMemberChooserObj import org.jetbrains.kotlin.idea.core.overrideImplement.generateUnsupportedOrSuperCall import org.jetbrains.kotlin.idea.core.refactoring.j2k import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.setupEditorSelection -import org.jetbrains.kotlin.idea.quickfix.generateMember +import org.jetbrains.kotlin.idea.quickfix.insertMember import org.jetbrains.kotlin.idea.testIntegration.findSuitableFrameworks import org.jetbrains.kotlin.idea.util.application.executeWriteCommand import org.jetbrains.kotlin.lexer.JetTokens @@ -117,72 +116,6 @@ abstract class KotlinGenerateTestSupportActionBase( } } - private inner class HandlerImpl : CodeInsightActionHandler { - override fun startInWriteAction() = false - - override fun invoke(project: Project, editor: Editor, file: PsiFile) { - val klass = findTargetClass(editor, file) ?: return - val frameworks = findSuitableFrameworks(klass) - .filter { methodKind.getFileTemplateDescriptor(it) != null && isApplicableTo(it, klass) } - chooseAndPerform(editor, frameworks) { doGenerate(editor, file, klass, it) } - } - - private fun doGenerate(editor: Editor, file: PsiFile, klass: JetClassOrObject, framework: TestFramework) { - val project = file.project - val commandName = "Generate test function" - project.executeWriteCommand(commandName) { - try { - PsiDocumentManager.getInstance(project).commitAllDocuments() - - val fileTemplateDescriptor = methodKind.getFileTemplateDescriptor(framework) - val fileTemplate = FileTemplateManager.getInstance(project).getCodeTemplate(fileTemplateDescriptor.fileName) - var templateText = fileTemplate.text.replace(BODY_VAR, "") - if (templateText.contains(NAME_VAR)) { - var name = "Name" - if (!ApplicationManager.getApplication().isUnitTestMode) { - name = Messages.showInputDialog("Choose test name: ", commandName, null, name, NAME_VALIDATOR) - ?: return@executeWriteCommand - } - - templateText = fileTemplate.text.replace(NAME_VAR, name) - } - val factory = PsiElementFactory.SERVICE.getInstance(project) - val psiMethod = factory.createMethodFromText(templateText, null) - psiMethod.throwsList.referenceElements.forEach { it.delete() } - val function = psiMethod.j2k() as? JetNamedFunction - if (function == null) { - HintManager.getInstance().showErrorHint(editor, "Couldn't convert Java template to Kotlin") - return@executeWriteCommand - } - val functionInPlace = generateMember(editor, klass, function) - - val functionDescriptor = functionInPlace.resolveToDescriptor() as FunctionDescriptor - val overriddenDescriptors = functionDescriptor.overriddenDescriptors - val bodyText = when (overriddenDescriptors.size()) { - 0 -> generateUnsupportedOrSuperCall(functionDescriptor, BodyType.EMPTY) - 1 -> generateUnsupportedOrSuperCall(overriddenDescriptors.single(), BodyType.SUPER) - else -> generateUnsupportedOrSuperCall(overriddenDescriptors.first(), BodyType.QUALIFIED_SUPER) - } - functionInPlace.bodyExpression?.delete() - functionInPlace.add(JetPsiFactory(project).createBlock(bodyText)) - - if (overriddenDescriptors.isNotEmpty()) { - functionInPlace.addModifier(JetTokens.OVERRIDE_KEYWORD) - } - - setupEditorSelection(editor, functionInPlace) - } - catch (e: IncorrectOperationException) { - HintManager.getInstance().showErrorHint(editor, "Cannot generate method: " + e.getMessage()) - } - } - } - } - - private val handler = HandlerImpl() - - override fun getHandler() = handler - override fun getTargetClass(editor: Editor, file: PsiFile): JetClassOrObject? { return findTargetClass(editor, file) } @@ -193,6 +126,64 @@ abstract class KotlinGenerateTestSupportActionBase( protected abstract fun isApplicableTo(framework: TestFramework, targetClass: JetClassOrObject): Boolean + override fun invoke(project: Project, editor: Editor, file: PsiFile) { + val klass = findTargetClass(editor, file) ?: return + val frameworks = findSuitableFrameworks(klass) + .filter { methodKind.getFileTemplateDescriptor(it) != null && isApplicableTo(it, klass) } + chooseAndPerform(editor, frameworks) { doGenerate(editor, file, klass, it) } + } + + private fun doGenerate(editor: Editor, file: PsiFile, klass: JetClassOrObject, framework: TestFramework) { + val project = file.project + val commandName = "Generate test function" + project.executeWriteCommand(commandName) { + try { + PsiDocumentManager.getInstance(project).commitAllDocuments() + + val fileTemplateDescriptor = methodKind.getFileTemplateDescriptor(framework) + val fileTemplate = FileTemplateManager.getInstance(project).getCodeTemplate(fileTemplateDescriptor.fileName) + var templateText = fileTemplate.text.replace(BODY_VAR, "") + if (templateText.contains(NAME_VAR)) { + var name = "Name" + if (!ApplicationManager.getApplication().isUnitTestMode) { + name = Messages.showInputDialog("Choose test name: ", commandName, null, name, NAME_VALIDATOR) + ?: return@executeWriteCommand + } + + templateText = fileTemplate.text.replace(NAME_VAR, name) + } + val factory = PsiElementFactory.SERVICE.getInstance(project) + val psiMethod = factory.createMethodFromText(templateText, null) + psiMethod.throwsList.referenceElements.forEach { it.delete() } + val function = psiMethod.j2k() as? JetNamedFunction + if (function == null) { + HintManager.getInstance().showErrorHint(editor, "Couldn't convert Java template to Kotlin") + return@executeWriteCommand + } + val functionInPlace = insertMember(editor, klass, function) + + val functionDescriptor = functionInPlace.resolveToDescriptor() as FunctionDescriptor + val overriddenDescriptors = functionDescriptor.overriddenDescriptors + val bodyText = when (overriddenDescriptors.size) { + 0 -> generateUnsupportedOrSuperCall(functionDescriptor, BodyType.EMPTY) + 1 -> generateUnsupportedOrSuperCall(overriddenDescriptors.single(), BodyType.SUPER) + else -> generateUnsupportedOrSuperCall(overriddenDescriptors.first(), BodyType.QUALIFIED_SUPER) + } + functionInPlace.bodyExpression?.delete() + functionInPlace.add(JetPsiFactory(project).createBlock(bodyText)) + + if (overriddenDescriptors.isNotEmpty()) { + functionInPlace.addModifier(JetTokens.OVERRIDE_KEYWORD) + } + + setupEditorSelection(editor, functionInPlace) + } + catch (e: IncorrectOperationException) { + HintManager.getInstance().showErrorHint(editor, "Cannot generate method: " + e.getMessage()) + } + } + } + override fun createEditTemplateAction(dataContext: DataContext): AnAction? { val project = CommonDataKeys.PROJECT.getData(dataContext) ?: return null val editor = CommonDataKeys.EDITOR.getData(dataContext) ?: return null diff --git a/idea/testData/codeInsight/generate/secondaryConstructors/empty.kt b/idea/testData/codeInsight/generate/secondaryConstructors/empty.kt new file mode 100644 index 00000000000..4080b682ac4 --- /dev/null +++ b/idea/testData/codeInsight/generate/secondaryConstructors/empty.kt @@ -0,0 +1,20 @@ +// ACTION_CLASS: org.jetbrains.kotlin.idea.actions.generate.KotlinGenerateSecondaryConstructorAction +// WITH_RUNTIME +class Foo { + val x = 1 + val y: Int + val z: Int get() = 3 + val u: Int by lazy { 4 } + + init { + y = 2 + } + + fun foo() { + + } + + fun bar() { + + } +} \ No newline at end of file diff --git a/idea/testData/codeInsight/generate/secondaryConstructors/empty.kt.after b/idea/testData/codeInsight/generate/secondaryConstructors/empty.kt.after new file mode 100644 index 00000000000..2bc38d5f0ef --- /dev/null +++ b/idea/testData/codeInsight/generate/secondaryConstructors/empty.kt.after @@ -0,0 +1,22 @@ +// ACTION_CLASS: org.jetbrains.kotlin.idea.actions.generate.KotlinGenerateSecondaryConstructorAction +// WITH_RUNTIME +class Foo { + val x = 1 + val y: Int + val z: Int get() = 3 + val u: Int by lazy { 4 } + + constructor() + + init { + y = 2 + } + + fun foo() { + + } + + fun bar() { + + } +} \ No newline at end of file diff --git a/idea/testData/codeInsight/generate/secondaryConstructors/emptyExists.kt b/idea/testData/codeInsight/generate/secondaryConstructors/emptyExists.kt new file mode 100644 index 00000000000..99bdec72e33 --- /dev/null +++ b/idea/testData/codeInsight/generate/secondaryConstructors/emptyExists.kt @@ -0,0 +1,24 @@ +// ACTION_CLASS: org.jetbrains.kotlin.idea.actions.generate.KotlinGenerateSecondaryConstructorAction +// WITH_RUNTIME +class Foo { + constructor() { + + } + + val x = 1 + val y: Int + val z: Int get() = 3 + val u: Int by lazy { 4 } + + init { + y = 2 + } + + fun foo() { + + } + + fun bar() { + + } +} \ No newline at end of file diff --git a/idea/testData/codeInsight/generate/secondaryConstructors/emptyExists.kt.messages b/idea/testData/codeInsight/generate/secondaryConstructors/emptyExists.kt.messages new file mode 100644 index 00000000000..2f785bfa086 --- /dev/null +++ b/idea/testData/codeInsight/generate/secondaryConstructors/emptyExists.kt.messages @@ -0,0 +1 @@ +Constructor already exists \ No newline at end of file diff --git a/idea/testData/codeInsight/generate/secondaryConstructors/javaSupers.java b/idea/testData/codeInsight/generate/secondaryConstructors/javaSupers.java new file mode 100644 index 00000000000..e1409402ec5 --- /dev/null +++ b/idea/testData/codeInsight/generate/secondaryConstructors/javaSupers.java @@ -0,0 +1,9 @@ +public class Base { + public Base(X x) { + + } + + public Base(X x, Y y) { + + } +} \ No newline at end of file diff --git a/idea/testData/codeInsight/generate/secondaryConstructors/javaSupers.kt b/idea/testData/codeInsight/generate/secondaryConstructors/javaSupers.kt new file mode 100644 index 00000000000..c4250f816bb --- /dev/null +++ b/idea/testData/codeInsight/generate/secondaryConstructors/javaSupers.kt @@ -0,0 +1,12 @@ +// ACTION_CLASS: org.jetbrains.kotlin.idea.actions.generate.KotlinGenerateSecondaryConstructorAction +class Foo : Base { + val x = 1 + + fun foo() { + + } + + fun bar() { + + } +} \ No newline at end of file diff --git a/idea/testData/codeInsight/generate/secondaryConstructors/javaSupers.kt.after b/idea/testData/codeInsight/generate/secondaryConstructors/javaSupers.kt.after new file mode 100644 index 00000000000..3b89bcb1ec2 --- /dev/null +++ b/idea/testData/codeInsight/generate/secondaryConstructors/javaSupers.kt.after @@ -0,0 +1,16 @@ +// ACTION_CLASS: org.jetbrains.kotlin.idea.actions.generate.KotlinGenerateSecondaryConstructorAction +class Foo : Base { + val x = 1 + + constructor(x: U) : super(x) + + constructor(x: U, y: Int?) : super(x, y) + + fun foo() { + + } + + fun bar() { + + } +} \ No newline at end of file diff --git a/idea/testData/codeInsight/generate/secondaryConstructors/properties.kt b/idea/testData/codeInsight/generate/secondaryConstructors/properties.kt new file mode 100644 index 00000000000..ee20c3c77a2 --- /dev/null +++ b/idea/testData/codeInsight/generate/secondaryConstructors/properties.kt @@ -0,0 +1,16 @@ +// ACTION_CLASS: org.jetbrains.kotlin.idea.actions.generate.KotlinGenerateSecondaryConstructorAction +class Foo { + val n: Int + + val x = 1 + + val m: Int + + fun foo() { + + } + + fun bar() { + + } +} \ No newline at end of file diff --git a/idea/testData/codeInsight/generate/secondaryConstructors/properties.kt.after b/idea/testData/codeInsight/generate/secondaryConstructors/properties.kt.after new file mode 100644 index 00000000000..a82534e5394 --- /dev/null +++ b/idea/testData/codeInsight/generate/secondaryConstructors/properties.kt.after @@ -0,0 +1,21 @@ +// ACTION_CLASS: org.jetbrains.kotlin.idea.actions.generate.KotlinGenerateSecondaryConstructorAction +class Foo { + val n: Int + + val x = 1 + + val m: Int + + constructor(n: Int, m: Int) { + this.n = n + this.m = m + } + + fun foo() { + + } + + fun bar() { + + } +} \ No newline at end of file diff --git a/idea/testData/codeInsight/generate/secondaryConstructors/propertiesWithSupers.kt b/idea/testData/codeInsight/generate/secondaryConstructors/propertiesWithSupers.kt new file mode 100644 index 00000000000..783e4d014ec --- /dev/null +++ b/idea/testData/codeInsight/generate/secondaryConstructors/propertiesWithSupers.kt @@ -0,0 +1,18 @@ +// ACTION_CLASS: org.jetbrains.kotlin.idea.actions.generate.KotlinGenerateSecondaryConstructorAction +open class Base(n: Int) { + constructor(a: Int, b: Int): this(a + b) +} + +class Foo : Base { + val n: Int + + val x = 1 + + fun foo() { + + } + + fun bar() { + + } +} \ No newline at end of file diff --git a/idea/testData/codeInsight/generate/secondaryConstructors/propertiesWithSupers.kt.after b/idea/testData/codeInsight/generate/secondaryConstructors/propertiesWithSupers.kt.after new file mode 100644 index 00000000000..c0a38c1e7e4 --- /dev/null +++ b/idea/testData/codeInsight/generate/secondaryConstructors/propertiesWithSupers.kt.after @@ -0,0 +1,26 @@ +// ACTION_CLASS: org.jetbrains.kotlin.idea.actions.generate.KotlinGenerateSecondaryConstructorAction +open class Base(n: Int) { + constructor(a: Int, b: Int): this(a + b) +} + +class Foo : Base { + val n: Int + + val x = 1 + + constructor(a: Int, b: Int, n: Int) : super(a, b) { + this.n = n + } + + constructor(n: Int, n1: Int) : super(n) { + this.n = n1 + } + + fun foo() { + + } + + fun bar() { + + } +} \ No newline at end of file diff --git a/idea/testData/codeInsight/generate/secondaryConstructors/supers.kt b/idea/testData/codeInsight/generate/secondaryConstructors/supers.kt new file mode 100644 index 00000000000..412aa556a64 --- /dev/null +++ b/idea/testData/codeInsight/generate/secondaryConstructors/supers.kt @@ -0,0 +1,16 @@ +// ACTION_CLASS: org.jetbrains.kotlin.idea.actions.generate.KotlinGenerateSecondaryConstructorAction +open class Base(n: Int) { + constructor(a: Int, b: Int): this(a + b) +} + +class Foo : Base { + val x = 1 + + fun foo() { + + } + + fun bar() { + + } +} \ No newline at end of file diff --git a/idea/testData/codeInsight/generate/secondaryConstructors/supers.kt.after b/idea/testData/codeInsight/generate/secondaryConstructors/supers.kt.after new file mode 100644 index 00000000000..4b2aa54ed5e --- /dev/null +++ b/idea/testData/codeInsight/generate/secondaryConstructors/supers.kt.after @@ -0,0 +1,20 @@ +// ACTION_CLASS: org.jetbrains.kotlin.idea.actions.generate.KotlinGenerateSecondaryConstructorAction +open class Base(n: Int) { + constructor(a: Int, b: Int): this(a + b) +} + +class Foo : Base { + val x = 1 + + constructor(a: Int, b: Int) : super(a, b) + + constructor(n: Int) : super(n) + + fun foo() { + + } + + fun bar() { + + } +} \ No newline at end of file diff --git a/idea/testData/codeInsight/generate/secondaryConstructors/supersAllExist.kt b/idea/testData/codeInsight/generate/secondaryConstructors/supersAllExist.kt new file mode 100644 index 00000000000..4d113c7405a --- /dev/null +++ b/idea/testData/codeInsight/generate/secondaryConstructors/supersAllExist.kt @@ -0,0 +1,20 @@ +// ACTION_CLASS: org.jetbrains.kotlin.idea.actions.generate.KotlinGenerateSecondaryConstructorAction +open class Base(n: Int) { + constructor(a: Int, b: Int): this(a + b) +} + +class Foo : Base { + val x = 1 + + constructor(t: Int, u: Int) : super(u, t) + + constructor(x: Int) : super(x) + + fun foo() { + + } + + fun bar() { + + } +} \ No newline at end of file diff --git a/idea/testData/codeInsight/generate/secondaryConstructors/supersAllExist.kt.messages b/idea/testData/codeInsight/generate/secondaryConstructors/supersAllExist.kt.messages new file mode 100644 index 00000000000..2f785bfa086 --- /dev/null +++ b/idea/testData/codeInsight/generate/secondaryConstructors/supersAllExist.kt.messages @@ -0,0 +1 @@ +Constructor already exists \ No newline at end of file diff --git a/idea/testData/codeInsight/generate/secondaryConstructors/supersPrimaryExists.kt b/idea/testData/codeInsight/generate/secondaryConstructors/supersPrimaryExists.kt new file mode 100644 index 00000000000..6921e0e9a2a --- /dev/null +++ b/idea/testData/codeInsight/generate/secondaryConstructors/supersPrimaryExists.kt @@ -0,0 +1,16 @@ +// ACTION_CLASS: org.jetbrains.kotlin.idea.actions.generate.KotlinGenerateSecondaryConstructorAction +open class Base(n: Int) { + constructor(a: Int, b: Int): this(a + b) +} + +class Foo(x: Int) : Base(x) { + val x = 1 + + fun foo() { + + } + + fun bar() { + + } +} \ No newline at end of file diff --git a/idea/testData/codeInsight/generate/secondaryConstructors/supersPrimaryExists.kt.after b/idea/testData/codeInsight/generate/secondaryConstructors/supersPrimaryExists.kt.after new file mode 100644 index 00000000000..b2cfb6d5b4e --- /dev/null +++ b/idea/testData/codeInsight/generate/secondaryConstructors/supersPrimaryExists.kt.after @@ -0,0 +1,18 @@ +// ACTION_CLASS: org.jetbrains.kotlin.idea.actions.generate.KotlinGenerateSecondaryConstructorAction +open class Base(n: Int) { + constructor(a: Int, b: Int): this(a + b) +} + +class Foo(x: Int) : Base(x) { + val x = 1 + + constructor(a: Int, b: Int) : super(a, b) + + fun foo() { + + } + + fun bar() { + + } +} \ No newline at end of file diff --git a/idea/testData/codeInsight/generate/secondaryConstructors/supersSomeExist.kt b/idea/testData/codeInsight/generate/secondaryConstructors/supersSomeExist.kt new file mode 100644 index 00000000000..8593c4f1ace --- /dev/null +++ b/idea/testData/codeInsight/generate/secondaryConstructors/supersSomeExist.kt @@ -0,0 +1,18 @@ +// ACTION_CLASS: org.jetbrains.kotlin.idea.actions.generate.KotlinGenerateSecondaryConstructorAction +open class Base(n: Int) { + constructor(a: Int, b: Int): this(a + b) +} + +class Foo : Base { + val x = 1 + + constructor(x: Int) : super(x) + + fun foo() { + + } + + fun bar() { + + } +} \ No newline at end of file diff --git a/idea/testData/codeInsight/generate/secondaryConstructors/supersSomeExist.kt.after b/idea/testData/codeInsight/generate/secondaryConstructors/supersSomeExist.kt.after new file mode 100644 index 00000000000..9b8eb6ec646 --- /dev/null +++ b/idea/testData/codeInsight/generate/secondaryConstructors/supersSomeExist.kt.after @@ -0,0 +1,20 @@ +// ACTION_CLASS: org.jetbrains.kotlin.idea.actions.generate.KotlinGenerateSecondaryConstructorAction +open class Base(n: Int) { + constructor(a: Int, b: Int): this(a + b) +} + +class Foo : Base { + val x = 1 + + constructor(x: Int) : super(x) + + constructor(a: Int, b: Int) : super(a, b) + + fun foo() { + + } + + fun bar() { + + } +} \ No newline at end of file diff --git a/idea/testData/codeInsight/generate/secondaryConstructors/supersWithGenerics.kt b/idea/testData/codeInsight/generate/secondaryConstructors/supersWithGenerics.kt new file mode 100644 index 00000000000..38359ccb2af --- /dev/null +++ b/idea/testData/codeInsight/generate/secondaryConstructors/supersWithGenerics.kt @@ -0,0 +1,16 @@ +// ACTION_CLASS: org.jetbrains.kotlin.idea.actions.generate.KotlinGenerateSecondaryConstructorAction +open class Base(n: X) { + constructor(x: X, y: Y): this(x) +} + +class Foo : Base { + val x = 1 + + fun foo() { + + } + + fun bar() { + + } +} \ No newline at end of file diff --git a/idea/testData/codeInsight/generate/secondaryConstructors/supersWithGenerics.kt.after b/idea/testData/codeInsight/generate/secondaryConstructors/supersWithGenerics.kt.after new file mode 100644 index 00000000000..f46ba578064 --- /dev/null +++ b/idea/testData/codeInsight/generate/secondaryConstructors/supersWithGenerics.kt.after @@ -0,0 +1,20 @@ +// ACTION_CLASS: org.jetbrains.kotlin.idea.actions.generate.KotlinGenerateSecondaryConstructorAction +open class Base(n: X) { + constructor(x: X, y: Y): this(x) +} + +class Foo : Base { + val x = 1 + + constructor(x: U, y: Int) : super(x, y) + + constructor(n: U) : super(n) + + fun foo() { + + } + + fun bar() { + + } +} \ No newline at end of file diff --git a/idea/testData/codeInsight/generate/secondaryConstructors/supersWithVarargs.kt b/idea/testData/codeInsight/generate/secondaryConstructors/supersWithVarargs.kt new file mode 100644 index 00000000000..d9a82aa5308 --- /dev/null +++ b/idea/testData/codeInsight/generate/secondaryConstructors/supersWithVarargs.kt @@ -0,0 +1,16 @@ +// ACTION_CLASS: org.jetbrains.kotlin.idea.actions.generate.KotlinGenerateSecondaryConstructorAction +open class Base { + constructor(a: Int, vararg b: Int) +} + +class Foo : Base { + val x = 1 + + fun foo() { + + } + + fun bar() { + + } +} \ No newline at end of file diff --git a/idea/testData/codeInsight/generate/secondaryConstructors/supersWithVarargs.kt.after b/idea/testData/codeInsight/generate/secondaryConstructors/supersWithVarargs.kt.after new file mode 100644 index 00000000000..68f9d15177e --- /dev/null +++ b/idea/testData/codeInsight/generate/secondaryConstructors/supersWithVarargs.kt.after @@ -0,0 +1,18 @@ +// ACTION_CLASS: org.jetbrains.kotlin.idea.actions.generate.KotlinGenerateSecondaryConstructorAction +open class Base { + constructor(a: Int, vararg b: Int) +} + +class Foo : Base { + val x = 1 + + constructor(a: Int, vararg b: Int) : super(a, *b) + + fun foo() { + + } + + fun bar() { + + } +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/generate/AbstractGenerateActionTest.kt b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/generate/AbstractGenerateActionTest.kt index d81d94c64fa..dd5d108cc0f 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/generate/AbstractGenerateActionTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/generate/AbstractGenerateActionTest.kt @@ -17,39 +17,37 @@ package org.jetbrains.kotlin.idea.codeInsight.generate import com.intellij.codeInsight.actions.CodeInsightAction -import com.intellij.openapi.roots.ModuleRootManager import com.intellij.openapi.util.io.FileUtil +import com.intellij.refactoring.util.CommonRefactoringUtil import com.intellij.testFramework.PlatformTestUtil import junit.framework.TestCase import org.jetbrains.kotlin.idea.test.ConfigLibraryUtil import org.jetbrains.kotlin.idea.test.JetLightCodeInsightFixtureTestCase import org.jetbrains.kotlin.idea.test.JetWithJdkAndRuntimeLightProjectDescriptor -import org.jetbrains.kotlin.idea.util.application.runWriteAction import org.jetbrains.kotlin.test.InTextDirectivesUtils +import org.jetbrains.kotlin.test.JetTestUtils import java.io.File abstract class AbstractGenerateActionTest : JetLightCodeInsightFixtureTestCase() { - private fun setUpTestSourceRoot() { - val module = myModule - val model = ModuleRootManager.getInstance(module).modifiableModel - val entry = model.contentEntries.single() - val sourceFolderFile = entry.sourceFolderFiles.single() - entry.removeSourceFolder(entry.sourceFolders.single()) - entry.addSourceFolder(sourceFolderFile, true) - runWriteAction { - model.commit() - module.project.save() - } - } - - protected fun doTest(path: String) { - setUpTestSourceRoot() - + protected open fun doTest(path: String) { val fileText = FileUtil.loadFile(File(path), true) + val conflictFile = File("$path.messages") + try { ConfigLibraryUtil.configureLibrariesByDirective(myModule, PlatformTestUtil.getCommunityPath(), fileText) + val mainFile = File(path) + val mainFileName = mainFile.name + val fileNameBase = mainFile.nameWithoutExtension + val rootDir = mainFile.parentFile + rootDir + .list { file, name -> + name.startsWith(fileNameBase) && name != mainFileName && (name.endsWith(".kt") || name.endsWith(".java")) + } + .forEach { + myFixture.configureByFile(File(rootDir, it).path.replace(File.separator, "/")) + } myFixture.configureByFile(path) val actionClassName = InTextDirectivesUtils.findStringWithPrefixes(fileText, "// ACTION_CLASS: ") @@ -60,12 +58,17 @@ abstract class AbstractGenerateActionTest : JetLightCodeInsightFixtureTestCase() val presentation = myFixture.testAction(action) TestCase.assertEquals(isApplicableExpected, presentation.isEnabled) + assert(!conflictFile.exists()) { "Conflict file $conflictFile should not exist" } + if (isApplicableExpected) { - val afterFile = File(path + ".after") + val afterFile = File("$path.after") TestCase.assertTrue(afterFile.exists()) myFixture.checkResult(FileUtil.loadFile(afterFile, true)) } } + catch (e: CommonRefactoringUtil.RefactoringErrorHintException) { + JetTestUtils.assertEqualsToFile(conflictFile, e.getMessage()!!) + } finally { ConfigLibraryUtil.unconfigureLibrariesByDirective(myModule, fileText) } diff --git a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/generate/AbstractGenerateTestSupportMethodActionTest.kt b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/generate/AbstractGenerateTestSupportMethodActionTest.kt new file mode 100644 index 00000000000..15ecdf4f22b --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/generate/AbstractGenerateTestSupportMethodActionTest.kt @@ -0,0 +1,40 @@ +/* + * 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.codeInsight.generate + +import com.intellij.openapi.roots.ModuleRootManager +import org.jetbrains.kotlin.idea.util.application.runWriteAction + +abstract class AbstractGenerateTestSupportMethodActionTest : AbstractGenerateActionTest() { + private fun setUpTestSourceRoot() { + val module = myModule + val model = ModuleRootManager.getInstance(module).modifiableModel + val entry = model.contentEntries.single() + val sourceFolderFile = entry.sourceFolderFiles.single() + entry.removeSourceFolder(entry.sourceFolders.single()) + entry.addSourceFolder(sourceFolderFile, true) + runWriteAction { + model.commit() + module.project.save() + } + } + + override fun doTest(path: String) { + setUpTestSourceRoot() + super.doTest(path) + } +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/generate/GenerateActionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/generate/GenerateActionTestGenerated.java index 05584bf8822..acfb81e2061 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/generate/GenerateActionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/generate/GenerateActionTestGenerated.java @@ -27,161 +27,77 @@ import java.util.regex.Pattern; /** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */ @SuppressWarnings("all") -@TestMetadata("idea/testData/codeInsight/generate") +@TestMetadata("idea/testData/codeInsight/generate/secondaryConstructors") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) public class GenerateActionTestGenerated extends AbstractGenerateActionTest { - public void testAllFilesPresentInGenerate() throws Exception { - JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/codeInsight/generate"), Pattern.compile("^(.+)\\.kt$"), true); + public void testAllFilesPresentInSecondaryConstructors() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/codeInsight/generate/secondaryConstructors"), Pattern.compile("^(.+)\\.kt$"), true); } - @TestMetadata("idea/testData/codeInsight/generate/testFrameworkSupport") - @TestDataPath("$PROJECT_ROOT") - @RunWith(JUnit3RunnerWithInners.class) - public static class TestFrameworkSupport extends AbstractGenerateActionTest { - public void testAllFilesPresentInTestFrameworkSupport() throws Exception { - JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/codeInsight/generate/testFrameworkSupport"), Pattern.compile("^(.+)\\.kt$"), true); - } + @TestMetadata("empty.kt") + public void testEmpty() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/codeInsight/generate/secondaryConstructors/empty.kt"); + doTest(fileName); + } - @TestMetadata("idea/testData/codeInsight/generate/testFrameworkSupport/jUnit4") - @TestDataPath("$PROJECT_ROOT") - @RunWith(JUnit3RunnerWithInners.class) - public static class JUnit4 extends AbstractGenerateActionTest { - public void testAllFilesPresentInJUnit4() throws Exception { - JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/codeInsight/generate/testFrameworkSupport/jUnit4"), Pattern.compile("^(.+)\\.kt$"), true); - } + @TestMetadata("emptyExists.kt") + public void testEmptyExists() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/codeInsight/generate/secondaryConstructors/emptyExists.kt"); + doTest(fileName); + } - @TestMetadata("dataMethod.kt") - public void testDataMethod() throws Exception { - String fileName = JetTestUtils.navigationMetadata("idea/testData/codeInsight/generate/testFrameworkSupport/jUnit4/dataMethod.kt"); - doTest(fileName); - } + @TestMetadata("javaSupers.kt") + public void testJavaSupers() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/codeInsight/generate/secondaryConstructors/javaSupers.kt"); + doTest(fileName); + } - @TestMetadata("setUp.kt") - public void testSetUp() throws Exception { - String fileName = JetTestUtils.navigationMetadata("idea/testData/codeInsight/generate/testFrameworkSupport/jUnit4/setUp.kt"); - doTest(fileName); - } + @TestMetadata("properties.kt") + public void testProperties() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/codeInsight/generate/secondaryConstructors/properties.kt"); + doTest(fileName); + } - @TestMetadata("setUpExists.kt") - public void testSetUpExists() throws Exception { - String fileName = JetTestUtils.navigationMetadata("idea/testData/codeInsight/generate/testFrameworkSupport/jUnit4/setUpExists.kt"); - doTest(fileName); - } + @TestMetadata("propertiesWithSupers.kt") + public void testPropertiesWithSupers() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/codeInsight/generate/secondaryConstructors/propertiesWithSupers.kt"); + doTest(fileName); + } - @TestMetadata("setUpOverrides.kt") - public void testSetUpOverrides() throws Exception { - String fileName = JetTestUtils.navigationMetadata("idea/testData/codeInsight/generate/testFrameworkSupport/jUnit4/setUpOverrides.kt"); - doTest(fileName); - } + @TestMetadata("supers.kt") + public void testSupers() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/codeInsight/generate/secondaryConstructors/supers.kt"); + doTest(fileName); + } - @TestMetadata("tearDown.kt") - public void testTearDown() throws Exception { - String fileName = JetTestUtils.navigationMetadata("idea/testData/codeInsight/generate/testFrameworkSupport/jUnit4/tearDown.kt"); - doTest(fileName); - } + @TestMetadata("supersAllExist.kt") + public void testSupersAllExist() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/codeInsight/generate/secondaryConstructors/supersAllExist.kt"); + doTest(fileName); + } - @TestMetadata("tearDownExists.kt") - public void testTearDownExists() throws Exception { - String fileName = JetTestUtils.navigationMetadata("idea/testData/codeInsight/generate/testFrameworkSupport/jUnit4/tearDownExists.kt"); - doTest(fileName); - } + @TestMetadata("supersPrimaryExists.kt") + public void testSupersPrimaryExists() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/codeInsight/generate/secondaryConstructors/supersPrimaryExists.kt"); + doTest(fileName); + } - @TestMetadata("testMethod.kt") - public void testTestMethod() throws Exception { - String fileName = JetTestUtils.navigationMetadata("idea/testData/codeInsight/generate/testFrameworkSupport/jUnit4/testMethod.kt"); - doTest(fileName); - } - } + @TestMetadata("supersSomeExist.kt") + public void testSupersSomeExist() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/codeInsight/generate/secondaryConstructors/supersSomeExist.kt"); + doTest(fileName); + } - @TestMetadata("idea/testData/codeInsight/generate/testFrameworkSupport/junit3") - @TestDataPath("$PROJECT_ROOT") - @RunWith(JUnit3RunnerWithInners.class) - public static class Junit3 extends AbstractGenerateActionTest { - public void testAllFilesPresentInJunit3() throws Exception { - JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/codeInsight/generate/testFrameworkSupport/junit3"), Pattern.compile("^(.+)\\.kt$"), true); - } + @TestMetadata("supersWithGenerics.kt") + public void testSupersWithGenerics() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/codeInsight/generate/secondaryConstructors/supersWithGenerics.kt"); + doTest(fileName); + } - @TestMetadata("setUp.kt") - public void testSetUp() throws Exception { - String fileName = JetTestUtils.navigationMetadata("idea/testData/codeInsight/generate/testFrameworkSupport/junit3/setUp.kt"); - doTest(fileName); - } - - @TestMetadata("setUpExists.kt") - public void testSetUpExists() throws Exception { - String fileName = JetTestUtils.navigationMetadata("idea/testData/codeInsight/generate/testFrameworkSupport/junit3/setUpExists.kt"); - doTest(fileName); - } - - @TestMetadata("tearDown.kt") - public void testTearDown() throws Exception { - String fileName = JetTestUtils.navigationMetadata("idea/testData/codeInsight/generate/testFrameworkSupport/junit3/tearDown.kt"); - doTest(fileName); - } - - @TestMetadata("tearDownExists.kt") - public void testTearDownExists() throws Exception { - String fileName = JetTestUtils.navigationMetadata("idea/testData/codeInsight/generate/testFrameworkSupport/junit3/tearDownExists.kt"); - doTest(fileName); - } - - @TestMetadata("testMethod.kt") - public void testTestMethod() throws Exception { - String fileName = JetTestUtils.navigationMetadata("idea/testData/codeInsight/generate/testFrameworkSupport/junit3/testMethod.kt"); - doTest(fileName); - } - } - - @TestMetadata("idea/testData/codeInsight/generate/testFrameworkSupport/testNG") - @TestDataPath("$PROJECT_ROOT") - @RunWith(JUnit3RunnerWithInners.class) - public static class TestNG extends AbstractGenerateActionTest { - public void testAllFilesPresentInTestNG() throws Exception { - JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/codeInsight/generate/testFrameworkSupport/testNG"), Pattern.compile("^(.+)\\.kt$"), true); - } - - @TestMetadata("dataMethod.kt") - public void testDataMethod() throws Exception { - String fileName = JetTestUtils.navigationMetadata("idea/testData/codeInsight/generate/testFrameworkSupport/testNG/dataMethod.kt"); - doTest(fileName); - } - - @TestMetadata("setUp.kt") - public void testSetUp() throws Exception { - String fileName = JetTestUtils.navigationMetadata("idea/testData/codeInsight/generate/testFrameworkSupport/testNG/setUp.kt"); - doTest(fileName); - } - - @TestMetadata("setUpExists.kt") - public void testSetUpExists() throws Exception { - String fileName = JetTestUtils.navigationMetadata("idea/testData/codeInsight/generate/testFrameworkSupport/testNG/setUpExists.kt"); - doTest(fileName); - } - - @TestMetadata("setUpOverrides.kt") - public void testSetUpOverrides() throws Exception { - String fileName = JetTestUtils.navigationMetadata("idea/testData/codeInsight/generate/testFrameworkSupport/testNG/setUpOverrides.kt"); - doTest(fileName); - } - - @TestMetadata("tearDown.kt") - public void testTearDown() throws Exception { - String fileName = JetTestUtils.navigationMetadata("idea/testData/codeInsight/generate/testFrameworkSupport/testNG/tearDown.kt"); - doTest(fileName); - } - - @TestMetadata("tearDownExists.kt") - public void testTearDownExists() throws Exception { - String fileName = JetTestUtils.navigationMetadata("idea/testData/codeInsight/generate/testFrameworkSupport/testNG/tearDownExists.kt"); - doTest(fileName); - } - - @TestMetadata("testMethod.kt") - public void testTestMethod() throws Exception { - String fileName = JetTestUtils.navigationMetadata("idea/testData/codeInsight/generate/testFrameworkSupport/testNG/testMethod.kt"); - doTest(fileName); - } - } + @TestMetadata("supersWithVarargs.kt") + public void testSupersWithVarargs() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/codeInsight/generate/secondaryConstructors/supersWithVarargs.kt"); + doTest(fileName); } } diff --git a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/generate/GenerateTestSupportMethodActionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/generate/GenerateTestSupportMethodActionTestGenerated.java new file mode 100644 index 00000000000..b7738f58787 --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/generate/GenerateTestSupportMethodActionTestGenerated.java @@ -0,0 +1,178 @@ +/* + * 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.codeInsight.generate; + +import com.intellij.testFramework.TestDataPath; +import org.jetbrains.kotlin.test.JUnit3RunnerWithInners; +import org.jetbrains.kotlin.test.JetTestUtils; +import org.jetbrains.kotlin.test.TestMetadata; +import org.junit.runner.RunWith; + +import java.io.File; +import java.util.regex.Pattern; + +/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */ +@SuppressWarnings("all") +@TestMetadata("idea/testData/codeInsight/generate/testFrameworkSupport") +@TestDataPath("$PROJECT_ROOT") +@RunWith(JUnit3RunnerWithInners.class) +public class GenerateTestSupportMethodActionTestGenerated extends AbstractGenerateTestSupportMethodActionTest { + public void testAllFilesPresentInTestFrameworkSupport() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/codeInsight/generate/testFrameworkSupport"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("idea/testData/codeInsight/generate/testFrameworkSupport/jUnit4") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class JUnit4 extends AbstractGenerateTestSupportMethodActionTest { + public void testAllFilesPresentInJUnit4() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/codeInsight/generate/testFrameworkSupport/jUnit4"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("dataMethod.kt") + public void testDataMethod() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/codeInsight/generate/testFrameworkSupport/jUnit4/dataMethod.kt"); + doTest(fileName); + } + + @TestMetadata("setUp.kt") + public void testSetUp() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/codeInsight/generate/testFrameworkSupport/jUnit4/setUp.kt"); + doTest(fileName); + } + + @TestMetadata("setUpExists.kt") + public void testSetUpExists() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/codeInsight/generate/testFrameworkSupport/jUnit4/setUpExists.kt"); + doTest(fileName); + } + + @TestMetadata("setUpOverrides.kt") + public void testSetUpOverrides() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/codeInsight/generate/testFrameworkSupport/jUnit4/setUpOverrides.kt"); + doTest(fileName); + } + + @TestMetadata("tearDown.kt") + public void testTearDown() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/codeInsight/generate/testFrameworkSupport/jUnit4/tearDown.kt"); + doTest(fileName); + } + + @TestMetadata("tearDownExists.kt") + public void testTearDownExists() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/codeInsight/generate/testFrameworkSupport/jUnit4/tearDownExists.kt"); + doTest(fileName); + } + + @TestMetadata("testMethod.kt") + public void testTestMethod() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/codeInsight/generate/testFrameworkSupport/jUnit4/testMethod.kt"); + doTest(fileName); + } + } + + @TestMetadata("idea/testData/codeInsight/generate/testFrameworkSupport/junit3") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Junit3 extends AbstractGenerateTestSupportMethodActionTest { + public void testAllFilesPresentInJunit3() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/codeInsight/generate/testFrameworkSupport/junit3"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("setUp.kt") + public void testSetUp() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/codeInsight/generate/testFrameworkSupport/junit3/setUp.kt"); + doTest(fileName); + } + + @TestMetadata("setUpExists.kt") + public void testSetUpExists() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/codeInsight/generate/testFrameworkSupport/junit3/setUpExists.kt"); + doTest(fileName); + } + + @TestMetadata("tearDown.kt") + public void testTearDown() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/codeInsight/generate/testFrameworkSupport/junit3/tearDown.kt"); + doTest(fileName); + } + + @TestMetadata("tearDownExists.kt") + public void testTearDownExists() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/codeInsight/generate/testFrameworkSupport/junit3/tearDownExists.kt"); + doTest(fileName); + } + + @TestMetadata("testMethod.kt") + public void testTestMethod() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/codeInsight/generate/testFrameworkSupport/junit3/testMethod.kt"); + doTest(fileName); + } + } + + @TestMetadata("idea/testData/codeInsight/generate/testFrameworkSupport/testNG") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class TestNG extends AbstractGenerateTestSupportMethodActionTest { + public void testAllFilesPresentInTestNG() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/codeInsight/generate/testFrameworkSupport/testNG"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("dataMethod.kt") + public void testDataMethod() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/codeInsight/generate/testFrameworkSupport/testNG/dataMethod.kt"); + doTest(fileName); + } + + @TestMetadata("setUp.kt") + public void testSetUp() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/codeInsight/generate/testFrameworkSupport/testNG/setUp.kt"); + doTest(fileName); + } + + @TestMetadata("setUpExists.kt") + public void testSetUpExists() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/codeInsight/generate/testFrameworkSupport/testNG/setUpExists.kt"); + doTest(fileName); + } + + @TestMetadata("setUpOverrides.kt") + public void testSetUpOverrides() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/codeInsight/generate/testFrameworkSupport/testNG/setUpOverrides.kt"); + doTest(fileName); + } + + @TestMetadata("tearDown.kt") + public void testTearDown() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/codeInsight/generate/testFrameworkSupport/testNG/tearDown.kt"); + doTest(fileName); + } + + @TestMetadata("tearDownExists.kt") + public void testTearDownExists() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/codeInsight/generate/testFrameworkSupport/testNG/tearDownExists.kt"); + doTest(fileName); + } + + @TestMetadata("testMethod.kt") + public void testTestMethod() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/codeInsight/generate/testFrameworkSupport/testNG/testMethod.kt"); + doTest(fileName); + } + } +}