diff --git a/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt b/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt index 01c4585d414..48d301ea53c 100644 --- a/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt +++ b/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt @@ -50,6 +50,7 @@ import org.jetbrains.kotlin.idea.codeInsight.* import org.jetbrains.kotlin.idea.codeInsight.generate.AbstractCodeInsightActionTest import org.jetbrains.kotlin.idea.codeInsight.generate.AbstractGenerateHashCodeAndEqualsActionTest import org.jetbrains.kotlin.idea.codeInsight.generate.AbstractGenerateTestSupportMethodActionTest +import org.jetbrains.kotlin.idea.codeInsight.generate.AbstractGenerateToStringActionTest import org.jetbrains.kotlin.idea.codeInsight.moveUpDown.AbstractCodeMoverTest import org.jetbrains.kotlin.idea.codeInsight.surroundWith.AbstractSurroundWithTest import org.jetbrains.kotlin.idea.codeInsight.unwrap.AbstractUnwrapRemoveTest @@ -779,6 +780,10 @@ fun main(args: Array) { testClass() { model("codeInsight/generate/secondaryConstructors") } + + testClass() { + model("codeInsight/generate/toString") + } } testGroup("idea/tests", "compiler/testData") { diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index 481e08c7fb0..032558b8f6b 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -176,6 +176,9 @@ + diff --git a/idea/src/org/jetbrains/kotlin/idea/actions/generate/KotlinGenerateEqualsAndHashcodeAction.kt b/idea/src/org/jetbrains/kotlin/idea/actions/generate/KotlinGenerateEqualsAndHashcodeAction.kt index 084f9ea61b2..8b66d8df2b6 100644 --- a/idea/src/org/jetbrains/kotlin/idea/actions/generate/KotlinGenerateEqualsAndHashcodeAction.kt +++ b/idea/src/org/jetbrains/kotlin/idea/actions/generate/KotlinGenerateEqualsAndHashcodeAction.kt @@ -16,32 +16,26 @@ package org.jetbrains.kotlin.idea.actions.generate -import com.intellij.codeInsight.CodeInsightBundle import com.intellij.codeInsight.CodeInsightSettings import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.editor.Editor import com.intellij.openapi.project.Project -import com.intellij.openapi.ui.Messages import com.intellij.util.IncorrectOperationException import org.jetbrains.kotlin.builtins.KotlinBuiltIns -import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.descriptors.VariableDescriptor import org.jetbrains.kotlin.idea.caches.resolve.analyzeFully -import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor import org.jetbrains.kotlin.idea.core.CollectingNameValidator import org.jetbrains.kotlin.idea.core.KotlinNameSuggester -import org.jetbrains.kotlin.idea.core.overrideImplement.OverrideMemberChooserObject -import org.jetbrains.kotlin.idea.core.overrideImplement.generateMember import org.jetbrains.kotlin.idea.quickfix.insertMembersAfter import org.jetbrains.kotlin.idea.refactoring.quoteIfNeeded import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers import org.jetbrains.kotlin.idea.util.application.runWriteAction -import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.lexer.KtTokens -import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext -import org.jetbrains.kotlin.renderer.ParameterNameRenderingPolicy import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny @@ -51,19 +45,6 @@ import org.jetbrains.kotlin.utils.addIfNotNull import org.jetbrains.kotlin.utils.addToStdlib.lastIsInstanceOrNull import java.util.* -private tailrec fun ClassDescriptor.findDeclaredFunction ( - name: String, - checkSuperClasses: Boolean, - filter: (FunctionDescriptor) -> Boolean -): FunctionDescriptor? { - unsubstitutedMemberScope - .getContributedFunctions(Name.identifier(name), NoLookupLocation.FROM_IDE) - .firstOrNull { it.containingDeclaration == this && it.kind == CallableMemberDescriptor.Kind.DECLARATION && filter(it) } - ?.let { return it } - - return if (checkSuperClasses) getSuperClassOrAny().findDeclaredFunction(name, checkSuperClasses, filter) else null -} - fun ClassDescriptor.findDeclaredEquals(checkSupers: Boolean): FunctionDescriptor? { return findDeclaredFunction("equals", checkSupers) { it.valueParameters.singleOrNull()?.type == it.builtIns.nullableAnyType && it.typeParameters.isEmpty() @@ -77,12 +58,6 @@ fun ClassDescriptor.findDeclaredHashCode(checkSupers: Boolean): FunctionDescript class KotlinGenerateEqualsAndHashcodeAction : KotlinGenerateMemberActionBase() { companion object { private val LOG = Logger.getInstance(KotlinGenerateEqualsAndHashcodeAction::class.java) - - private val MEMBER_RENDERER = IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.withOptions { - modifiers = emptySet() - startFromName = true - parameterNameRenderingPolicy = ParameterNameRenderingPolicy.NONE - } } class Info( @@ -95,35 +70,8 @@ class KotlinGenerateEqualsAndHashcodeAction : KotlinGenerateMemberActionBase { - return ArrayList().apply { - getPrimaryConstructorParameters().filterTo(this) { it.hasValOrVar() } - declarations.filterIsInstance().filterTo(this) f@ { - val descriptor = it.resolveToDescriptor() - when (descriptor) { - is ValueParameterDescriptor -> true - is PropertyDescriptor -> descriptor.accessors.all { it.isDefault } - else -> false - } - } - } - } - - private fun confirmRewrite( - targetClass: KtClass, - equalsDescriptor: FunctionDescriptor, - hashCodeDescriptor: FunctionDescriptor - ): Boolean { - if (ApplicationManager.getApplication().isUnitTestMode) return true - val functionsText = "'${MEMBER_RENDERER.render(equalsDescriptor)}' and '${MEMBER_RENDERER.render(hashCodeDescriptor)}'" - val message = "Functions $functionsText are already defined\nfor class ${targetClass.name}. Do you want to delete them and proceed?" - return Messages.showYesNoDialog(targetClass.project, message, - CodeInsightBundle.message("generate.equals.and.hashcode.already.defined.title"), - Messages.getQuestionIcon()) == Messages.YES + && !targetClass.hasModifier(KtTokens.DATA_KEYWORD) + && getPropertiesToUseInGeneratedMember(targetClass).isNotEmpty() } override fun prepareMembersInfo(klass: KtClassOrObject, project: Project, editor: Editor?): Info? { @@ -138,7 +86,7 @@ class KotlinGenerateEqualsAndHashcodeAction : KotlinGenerateMemberActionBase() { + companion object { + private val LOG = Logger.getInstance(KotlinGenerateToStringAction::class.java) + + var KtClass.adjuster: ((Info) -> Info)? by UserDataProperty(Key.create("ADJUSTER")) + } + + data class Info(val classDescriptor: ClassDescriptor, + val variablesToUse: List, + val generateSuperCall: Boolean, + val generator: Generator) + + enum class Generator(val text: String) { + SINGLE_TEMPLATE("Single template") { + override fun generate(info: Info): String { + val className = info.classDescriptor.name.asString() + + return buildString { + append("return \"${className.quoteIfNeeded()}(") + info.variablesToUse.joinTo(this) { + val ref = it.name.asString().quoteIfNeeded() + "$ref=${renderVariableValue(it, ref)}" + } + append(")") + if (info.generateSuperCall) { + append(" \${super.toString()}") + } + append("\"") + } + } + }, + + MULTIPLE_TEMPLATES("Multiple templates with concatenation") { + override fun generate(info: Info): String { + val className = info.classDescriptor.name.asString() + + return buildString { + if (info.variablesToUse.isNotEmpty()) { + append("return \"${className.quoteIfNeeded()}(\" +\n") + val varIterator = info.variablesToUse.iterator() + while (varIterator.hasNext()) { + val it = varIterator.next() + val ref = it.name.asString().quoteIfNeeded() + append("\"$ref=${renderVariableValue(it, ref)}") + if (varIterator.hasNext()) { + append(',') + } + append("\" +\n") + } + append("\")\"") + } + else { + append("return \"$className()\"") + } + + if (info.generateSuperCall) { + append(" +\n \" \${super.toString()}\"") + } + } + } + }; + + protected fun renderVariableValue(variableDescriptor: VariableDescriptor, ref: String): String { + val type = variableDescriptor.type + val rhs = when { + KotlinBuiltIns.isArray(type) || KotlinBuiltIns.isPrimitiveArray(type) -> "\${java.util.Arrays.toString($ref)}" + KotlinBuiltIns.isString(type) -> "'$$ref'" + else -> "$$ref" + } + return rhs + } + + abstract fun generate(info: Info): String + } + + override fun isValidForClass(targetClass: KtClassOrObject): Boolean { + return targetClass is KtClass + && !targetClass.isAnnotation() + && !targetClass.isInterface() + && !targetClass.hasModifier(KtTokens.DATA_KEYWORD) + } + + override fun prepareMembersInfo(klass: KtClassOrObject, project: Project, editor: Editor?): Info? { + if (klass !is KtClass) throw AssertionError("Not a class: ${klass.getElementTextWithContext()}") + + val context = klass.analyzeFully() + val classDescriptor = context.get(BindingContext.CLASS, klass) ?: return null + + classDescriptor.findDeclaredToString(false)?.let { + if (!confirmMemberRewrite(klass, it)) return null + + runWriteAction { + try { + it.source.getPsi()?.delete() + } + catch(e: IncorrectOperationException) { + LOG.error(e) + } + } + } + + val properties = getPropertiesToUseInGeneratedMember(klass) + if (ApplicationManager.getApplication().isUnitTestMode) { + val info = Info(classDescriptor, + properties.map { context[BindingContext.DECLARATION_TO_DESCRIPTOR, it] as VariableDescriptor }, + false, + Generator.SINGLE_TEMPLATE) + return klass.adjuster?.let { it(info) } ?: info + } + + val superToString = classDescriptor.getSuperClassOrAny().findDeclaredToString(true)!! + + val memberChooserObjects = properties.map { DescriptorMemberChooserObject(it, it.resolveToDescriptor()) }.toTypedArray() + val headerPanel = ToStringMemberChooserHeaderPanel(!superToString.builtIns.isMemberOfAny(superToString)) + val chooser = MemberChooser(memberChooserObjects, true, true, project, false, headerPanel).apply { + title = "Generate toString()" + setCopyJavadocVisible(false) + selectElements(memberChooserObjects) + } + + chooser.show() + if (chooser.exitCode != DialogWrapper.OK_EXIT_CODE) return null + + return Info(classDescriptor, + chooser.selectedElements?.map { it.descriptor as VariableDescriptor } ?: emptyList(), + headerPanel.isGenerateSuperCall, + headerPanel.selectedGenerator) + } + + private fun generateToString(project: Project, info: Info): KtNamedFunction? { + val superToString = info.classDescriptor.getSuperClassOrAny().findDeclaredToString(true)!! + return generateFunctionSkeleton(superToString, project).apply { + bodyExpression!!.replace(KtPsiFactory(project).createExpression("{\n${info.generator.generate(info)}\n}")) + } + } + + override fun generateMembers(project: Project, editor: Editor?, info: Info): List { + val targetClass = info.classDescriptor.source.getPsi() as KtClass + val prototype = generateToString(project, info) ?: return emptyList() + val anchor = with(targetClass.declarations) { lastIsInstanceOrNull() ?: lastOrNull() } + return insertMembersAfter(editor, targetClass, listOf(prototype), anchor) + } +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/actions/generate/ToStringMemberChooserHeaderPanel.java b/idea/src/org/jetbrains/kotlin/idea/actions/generate/ToStringMemberChooserHeaderPanel.java new file mode 100644 index 00000000000..f7b00604c04 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/actions/generate/ToStringMemberChooserHeaderPanel.java @@ -0,0 +1,86 @@ +/* + * Copyright 2010-2016 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.openapi.ui.ComboBox; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.java.generate.template.toString.ToStringTemplatesManager; + +import javax.swing.*; +import java.awt.*; + +public class ToStringMemberChooserHeaderPanel extends JPanel { + private final JComboBox comboBox; + private final JCheckBox generateSuperCheckBox; + + public ToStringMemberChooserHeaderPanel(boolean allowSuperCall) { + super(new GridBagLayout()); + + comboBox = new ComboBox(KotlinGenerateToStringAction.Generator.values()); + comboBox.setRenderer( + new DefaultListCellRenderer() { + @NotNull + @Override + public Component getListCellRendererComponent( + JList list, + Object value, + int index, + boolean isSelected, + boolean cellHasFocus + ) { + super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); + setText(((KotlinGenerateToStringAction.Generator) value).getText()); + return this; + } + } + ); + comboBox.setSelectedItem(ToStringTemplatesManager.getInstance().getDefaultTemplate()); + + JLabel templatesLabel = new JLabel("Choose implementation: "); + templatesLabel.setDisplayedMnemonic('i'); + templatesLabel.setLabelFor(comboBox); + + GridBagConstraints constraints = new GridBagConstraints(); + constraints.anchor = GridBagConstraints.BASELINE; + constraints.gridx = 0; + add(templatesLabel, constraints); + + constraints.gridx = 1; + constraints.weightx = 1.0; + constraints.fill = GridBagConstraints.HORIZONTAL; + add(comboBox, constraints); + + if (allowSuperCall) { + generateSuperCheckBox = new JCheckBox("Generate call to super.toString()"); + generateSuperCheckBox.setMnemonic('s'); + constraints.gridx = 2; + constraints.weightx = 0.0; + add(generateSuperCheckBox, constraints); + } + else { + generateSuperCheckBox = null; + } + } + + public KotlinGenerateToStringAction.Generator getSelectedGenerator() { + return (KotlinGenerateToStringAction.Generator) comboBox.getSelectedItem(); + } + + public boolean isGenerateSuperCall() { + return generateSuperCheckBox != null && generateSuperCheckBox.isSelected(); + } +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/actions/generate/utils.kt b/idea/src/org/jetbrains/kotlin/idea/actions/generate/utils.kt new file mode 100644 index 00000000000..48efd2af7bf --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/actions/generate/utils.kt @@ -0,0 +1,82 @@ +/* + * Copyright 2010-2016 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.openapi.application.ApplicationManager +import com.intellij.openapi.project.Project +import com.intellij.openapi.ui.Messages +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor +import org.jetbrains.kotlin.idea.core.overrideImplement.OverrideMemberChooserObject +import org.jetbrains.kotlin.idea.core.overrideImplement.generateMember +import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers +import org.jetbrains.kotlin.incremental.components.NoLookupLocation +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.renderer.ParameterNameRenderingPolicy +import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny +import java.util.* + +tailrec fun ClassDescriptor.findDeclaredFunction( + name: String, + checkSuperClasses: Boolean, + filter: (FunctionDescriptor) -> Boolean +): FunctionDescriptor? { + unsubstitutedMemberScope + .getContributedFunctions(Name.identifier(name), NoLookupLocation.FROM_IDE) + .firstOrNull { it.containingDeclaration == this && it.kind == CallableMemberDescriptor.Kind.DECLARATION && filter(it) } + ?.let { return it } + + return if (checkSuperClasses) getSuperClassOrAny().findDeclaredFunction(name, checkSuperClasses, filter) else null +} + +fun getPropertiesToUseInGeneratedMember(classOrObject: KtClassOrObject): List { + return ArrayList().apply { + classOrObject.getPrimaryConstructorParameters().filterTo(this) { it.hasValOrVar() } + classOrObject.declarations.filterIsInstance().filterTo(this) { + val descriptor = it.resolveToDescriptor() + when (descriptor) { + is ValueParameterDescriptor -> true + is PropertyDescriptor -> descriptor.accessors.all { it.isDefault } + else -> false + } + } + } +} + +private val MEMBER_RENDERER = IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.withOptions { + modifiers = emptySet() + startFromName = true + parameterNameRenderingPolicy = ParameterNameRenderingPolicy.NONE +} + +fun confirmMemberRewrite(targetClass: KtClass, vararg descriptors: FunctionDescriptor): Boolean { + if (ApplicationManager.getApplication().isUnitTestMode) return true + + val functionsText = descriptors.joinToString(separator = " and ") { "'${MEMBER_RENDERER.render(it)}'" } + val message = "Functions $functionsText are already defined\nfor class ${targetClass.name}. Do you want to delete them and proceed?" + return Messages.showYesNoDialog(targetClass.project, message, + CodeInsightBundle.message("generate.equals.and.hashcode.already.defined.title"), + Messages.getQuestionIcon()) == Messages.YES +} + +fun generateFunctionSkeleton(descriptor: FunctionDescriptor, project: Project): KtNamedFunction { + return OverrideMemberChooserObject + .create(project, descriptor, descriptor, OverrideMemberChooserObject.BodyType.EMPTY) + .generateMember(project) as KtNamedFunction +} \ No newline at end of file diff --git a/idea/testData/codeInsight/generate/toString/common/annotation.kt b/idea/testData/codeInsight/generate/toString/common/annotation.kt new file mode 100644 index 00000000000..881d85168d3 --- /dev/null +++ b/idea/testData/codeInsight/generate/toString/common/annotation.kt @@ -0,0 +1,2 @@ +// NOT_APPLICABLE +annotation class A() \ No newline at end of file diff --git a/idea/testData/codeInsight/generate/toString/common/dataClass.kt b/idea/testData/codeInsight/generate/toString/common/dataClass.kt new file mode 100644 index 00000000000..9cc91f7fdc9 --- /dev/null +++ b/idea/testData/codeInsight/generate/toString/common/dataClass.kt @@ -0,0 +1,2 @@ +// NOT_APPLICABLE +data class A(val n: Int) \ No newline at end of file diff --git a/idea/testData/codeInsight/generate/toString/common/interface.kt b/idea/testData/codeInsight/generate/toString/common/interface.kt new file mode 100644 index 00000000000..f430c6033fc --- /dev/null +++ b/idea/testData/codeInsight/generate/toString/common/interface.kt @@ -0,0 +1,6 @@ +// NOT_APPLICABLE +interface A { + fun foo() { + + } +} \ No newline at end of file diff --git a/idea/testData/codeInsight/generate/toString/common/object.kt b/idea/testData/codeInsight/generate/toString/common/object.kt new file mode 100644 index 00000000000..692699c6ad3 --- /dev/null +++ b/idea/testData/codeInsight/generate/toString/common/object.kt @@ -0,0 +1,6 @@ +// NOT_APPLICABLE +object A { + fun foo() { + + } +} \ No newline at end of file diff --git a/idea/testData/codeInsight/generate/toString/multipeTemplates/arrays.kt b/idea/testData/codeInsight/generate/toString/multipeTemplates/arrays.kt new file mode 100644 index 00000000000..e18fcd2acc9 --- /dev/null +++ b/idea/testData/codeInsight/generate/toString/multipeTemplates/arrays.kt @@ -0,0 +1,8 @@ +// GENERATOR: MULTIPLE_TEMPLATES +class A(val n: IntArray, val s: Array) { + val f: Float = 1.0f + + fun foo() { + + } +} \ No newline at end of file diff --git a/idea/testData/codeInsight/generate/toString/multipeTemplates/arrays.kt.after b/idea/testData/codeInsight/generate/toString/multipeTemplates/arrays.kt.after new file mode 100644 index 00000000000..69c75ac96c7 --- /dev/null +++ b/idea/testData/codeInsight/generate/toString/multipeTemplates/arrays.kt.after @@ -0,0 +1,18 @@ +import java.util.Arrays + +// GENERATOR: MULTIPLE_TEMPLATES +class A(val n: IntArray, val s: Array) { + val f: Float = 1.0f + + fun foo() { + + } + + override fun toString(): String{ + return "A(" + + "n=${Arrays.toString(n)}," + + "s=${Arrays.toString(s)}," + + "f=$f" + + ")" + } +} \ No newline at end of file diff --git a/idea/testData/codeInsight/generate/toString/multipeTemplates/multipleVars.kt b/idea/testData/codeInsight/generate/toString/multipeTemplates/multipleVars.kt new file mode 100644 index 00000000000..906e7b1cdc6 --- /dev/null +++ b/idea/testData/codeInsight/generate/toString/multipeTemplates/multipleVars.kt @@ -0,0 +1,8 @@ +// GENERATOR: MULTIPLE_TEMPLATES +class A(val n: Int, val s: String) { + val f: Float = 1.0f + + fun foo() { + + } +} \ No newline at end of file diff --git a/idea/testData/codeInsight/generate/toString/multipeTemplates/multipleVars.kt.after b/idea/testData/codeInsight/generate/toString/multipeTemplates/multipleVars.kt.after new file mode 100644 index 00000000000..027bf2ba5f8 --- /dev/null +++ b/idea/testData/codeInsight/generate/toString/multipeTemplates/multipleVars.kt.after @@ -0,0 +1,16 @@ +// GENERATOR: MULTIPLE_TEMPLATES +class A(val n: Int, val s: String) { + val f: Float = 1.0f + + fun foo() { + + } + + override fun toString(): String{ + return "A(" + + "n=$n," + + "s='$s'," + + "f=$f" + + ")" + } +} \ No newline at end of file diff --git a/idea/testData/codeInsight/generate/toString/multipeTemplates/multipleVarsWithSuperClass.kt b/idea/testData/codeInsight/generate/toString/multipeTemplates/multipleVarsWithSuperClass.kt new file mode 100644 index 00000000000..a58bee8ec63 --- /dev/null +++ b/idea/testData/codeInsight/generate/toString/multipeTemplates/multipleVarsWithSuperClass.kt @@ -0,0 +1,13 @@ +// GENERATOR: MULTIPLE_TEMPLATES +// GENERATE_SUPER_CALL +open class X { + override fun toString() = super.toString() +} + +class A(val n: Int, val s: String) : X() { + val f: Float = 1.0f + + fun foo() { + + } +} \ No newline at end of file diff --git a/idea/testData/codeInsight/generate/toString/multipeTemplates/multipleVarsWithSuperClass.kt.after b/idea/testData/codeInsight/generate/toString/multipeTemplates/multipleVarsWithSuperClass.kt.after new file mode 100644 index 00000000000..45d2464fa5c --- /dev/null +++ b/idea/testData/codeInsight/generate/toString/multipeTemplates/multipleVarsWithSuperClass.kt.after @@ -0,0 +1,22 @@ +// GENERATOR: MULTIPLE_TEMPLATES +// GENERATE_SUPER_CALL +open class X { + override fun toString() = super.toString() +} + +class A(val n: Int, val s: String) : X() { + val f: Float = 1.0f + + fun foo() { + + } + + override fun toString(): String{ + return "A(" + + "n=$n," + + "s='$s'," + + "f=$f" + + ")" + + " ${super.toString()}" + } +} \ No newline at end of file diff --git a/idea/testData/codeInsight/generate/toString/multipeTemplates/noVars.kt b/idea/testData/codeInsight/generate/toString/multipeTemplates/noVars.kt new file mode 100644 index 00000000000..26c65aec56a --- /dev/null +++ b/idea/testData/codeInsight/generate/toString/multipeTemplates/noVars.kt @@ -0,0 +1,6 @@ +// GENERATOR: MULTIPLE_TEMPLATES +class A { + fun foo() { + + } +} \ No newline at end of file diff --git a/idea/testData/codeInsight/generate/toString/multipeTemplates/noVars.kt.after b/idea/testData/codeInsight/generate/toString/multipeTemplates/noVars.kt.after new file mode 100644 index 00000000000..05925e77b6d --- /dev/null +++ b/idea/testData/codeInsight/generate/toString/multipeTemplates/noVars.kt.after @@ -0,0 +1,10 @@ +// GENERATOR: MULTIPLE_TEMPLATES +class A { + fun foo() { + + } + + override fun toString(): String{ + return "A()" + } +} \ No newline at end of file diff --git a/idea/testData/codeInsight/generate/toString/multipeTemplates/singleVar.kt b/idea/testData/codeInsight/generate/toString/multipeTemplates/singleVar.kt new file mode 100644 index 00000000000..1f52e81f1ca --- /dev/null +++ b/idea/testData/codeInsight/generate/toString/multipeTemplates/singleVar.kt @@ -0,0 +1,6 @@ +// GENERATOR: MULTIPLE_TEMPLATES +class A(val n: Int) { + fun foo() { + + } +} \ No newline at end of file diff --git a/idea/testData/codeInsight/generate/toString/multipeTemplates/singleVar.kt.after b/idea/testData/codeInsight/generate/toString/multipeTemplates/singleVar.kt.after new file mode 100644 index 00000000000..2d427579e4a --- /dev/null +++ b/idea/testData/codeInsight/generate/toString/multipeTemplates/singleVar.kt.after @@ -0,0 +1,12 @@ +// GENERATOR: MULTIPLE_TEMPLATES +class A(val n: Int) { + fun foo() { + + } + + override fun toString(): String{ + return "A(" + + "n=$n" + + ")" + } +} \ No newline at end of file diff --git a/idea/testData/codeInsight/generate/toString/multipeTemplates/superClassNoVars.kt b/idea/testData/codeInsight/generate/toString/multipeTemplates/superClassNoVars.kt new file mode 100644 index 00000000000..435e53848e8 --- /dev/null +++ b/idea/testData/codeInsight/generate/toString/multipeTemplates/superClassNoVars.kt @@ -0,0 +1,11 @@ +// GENERATOR: MULTIPLE_TEMPLATES +// GENERATE_SUPER_CALL +open class X { + override fun toString() = super.toString() +} + +class A : X() { + fun foo() { + + } +} \ No newline at end of file diff --git a/idea/testData/codeInsight/generate/toString/multipeTemplates/superClassNoVars.kt.after b/idea/testData/codeInsight/generate/toString/multipeTemplates/superClassNoVars.kt.after new file mode 100644 index 00000000000..b62335648f2 --- /dev/null +++ b/idea/testData/codeInsight/generate/toString/multipeTemplates/superClassNoVars.kt.after @@ -0,0 +1,16 @@ +// GENERATOR: MULTIPLE_TEMPLATES +// GENERATE_SUPER_CALL +open class X { + override fun toString() = super.toString() +} + +class A : X() { + fun foo() { + + } + + override fun toString(): String{ + return "A()" + + " ${super.toString()}" + } +} \ No newline at end of file diff --git a/idea/testData/codeInsight/generate/toString/singleTemplate/arrays.kt b/idea/testData/codeInsight/generate/toString/singleTemplate/arrays.kt new file mode 100644 index 00000000000..eeed675f4b2 --- /dev/null +++ b/idea/testData/codeInsight/generate/toString/singleTemplate/arrays.kt @@ -0,0 +1,8 @@ +// GENERATOR: SINGLE_TEMPLATE +class A(val n: IntArray, val s: Array) { + val f: Float = 1.0f + + fun foo() { + + } +} \ No newline at end of file diff --git a/idea/testData/codeInsight/generate/toString/singleTemplate/arrays.kt.after b/idea/testData/codeInsight/generate/toString/singleTemplate/arrays.kt.after new file mode 100644 index 00000000000..72ee26b8ace --- /dev/null +++ b/idea/testData/codeInsight/generate/toString/singleTemplate/arrays.kt.after @@ -0,0 +1,14 @@ +import java.util.Arrays + +// GENERATOR: SINGLE_TEMPLATE +class A(val n: IntArray, val s: Array) { + val f: Float = 1.0f + + fun foo() { + + } + + override fun toString(): String{ + return "A(n=${Arrays.toString(n)}, s=${Arrays.toString(s)}, f=$f)" + } +} \ No newline at end of file diff --git a/idea/testData/codeInsight/generate/toString/singleTemplate/multipleVars.kt b/idea/testData/codeInsight/generate/toString/singleTemplate/multipleVars.kt new file mode 100644 index 00000000000..efb12eba9cb --- /dev/null +++ b/idea/testData/codeInsight/generate/toString/singleTemplate/multipleVars.kt @@ -0,0 +1,8 @@ +// GENERATOR: SINGLE_TEMPLATE +class A(val n: Int, val s: String) { + val f: Float = 1.0f + + fun foo() { + + } +} \ No newline at end of file diff --git a/idea/testData/codeInsight/generate/toString/singleTemplate/multipleVars.kt.after b/idea/testData/codeInsight/generate/toString/singleTemplate/multipleVars.kt.after new file mode 100644 index 00000000000..5d872b4d2c5 --- /dev/null +++ b/idea/testData/codeInsight/generate/toString/singleTemplate/multipleVars.kt.after @@ -0,0 +1,12 @@ +// GENERATOR: SINGLE_TEMPLATE +class A(val n: Int, val s: String) { + val f: Float = 1.0f + + fun foo() { + + } + + override fun toString(): String{ + return "A(n=$n, s='$s', f=$f)" + } +} \ No newline at end of file diff --git a/idea/testData/codeInsight/generate/toString/singleTemplate/multipleVarsWithSuperClass.kt b/idea/testData/codeInsight/generate/toString/singleTemplate/multipleVarsWithSuperClass.kt new file mode 100644 index 00000000000..14c75ee1c5c --- /dev/null +++ b/idea/testData/codeInsight/generate/toString/singleTemplate/multipleVarsWithSuperClass.kt @@ -0,0 +1,13 @@ +// GENERATOR: SINGLE_TEMPLATE +// GENERATE_SUPER_CALL +open class X { + override fun toString() = super.toString() +} + +class A(val n: Int, val s: String) : X() { + val f: Float = 1.0f + + fun foo() { + + } +} \ No newline at end of file diff --git a/idea/testData/codeInsight/generate/toString/singleTemplate/multipleVarsWithSuperClass.kt.after b/idea/testData/codeInsight/generate/toString/singleTemplate/multipleVarsWithSuperClass.kt.after new file mode 100644 index 00000000000..19812fd7ee9 --- /dev/null +++ b/idea/testData/codeInsight/generate/toString/singleTemplate/multipleVarsWithSuperClass.kt.after @@ -0,0 +1,17 @@ +// GENERATOR: SINGLE_TEMPLATE +// GENERATE_SUPER_CALL +open class X { + override fun toString() = super.toString() +} + +class A(val n: Int, val s: String) : X() { + val f: Float = 1.0f + + fun foo() { + + } + + override fun toString(): String{ + return "A(n=$n, s='$s', f=$f) ${super.toString()}" + } +} \ No newline at end of file diff --git a/idea/testData/codeInsight/generate/toString/singleTemplate/noVars.kt b/idea/testData/codeInsight/generate/toString/singleTemplate/noVars.kt new file mode 100644 index 00000000000..85226e937db --- /dev/null +++ b/idea/testData/codeInsight/generate/toString/singleTemplate/noVars.kt @@ -0,0 +1,6 @@ +// GENERATOR: SINGLE_TEMPLATE +class A { + fun foo() { + + } +} \ No newline at end of file diff --git a/idea/testData/codeInsight/generate/toString/singleTemplate/noVars.kt.after b/idea/testData/codeInsight/generate/toString/singleTemplate/noVars.kt.after new file mode 100644 index 00000000000..523206f73d6 --- /dev/null +++ b/idea/testData/codeInsight/generate/toString/singleTemplate/noVars.kt.after @@ -0,0 +1,10 @@ +// GENERATOR: SINGLE_TEMPLATE +class A { + fun foo() { + + } + + override fun toString(): String{ + return "A()" + } +} \ No newline at end of file diff --git a/idea/testData/codeInsight/generate/toString/singleTemplate/singleVar.kt b/idea/testData/codeInsight/generate/toString/singleTemplate/singleVar.kt new file mode 100644 index 00000000000..fc8d2711a88 --- /dev/null +++ b/idea/testData/codeInsight/generate/toString/singleTemplate/singleVar.kt @@ -0,0 +1,6 @@ +// GENERATOR: SINGLE_TEMPLATE +class A(val n: Int) { + fun foo() { + + } +} \ No newline at end of file diff --git a/idea/testData/codeInsight/generate/toString/singleTemplate/singleVar.kt.after b/idea/testData/codeInsight/generate/toString/singleTemplate/singleVar.kt.after new file mode 100644 index 00000000000..7b1a91d72a5 --- /dev/null +++ b/idea/testData/codeInsight/generate/toString/singleTemplate/singleVar.kt.after @@ -0,0 +1,10 @@ +// GENERATOR: SINGLE_TEMPLATE +class A(val n: Int) { + fun foo() { + + } + + override fun toString(): String{ + return "A(n=$n)" + } +} \ No newline at end of file diff --git a/idea/testData/codeInsight/generate/toString/singleTemplate/superClassNoVars.kt b/idea/testData/codeInsight/generate/toString/singleTemplate/superClassNoVars.kt new file mode 100644 index 00000000000..e18f745aa17 --- /dev/null +++ b/idea/testData/codeInsight/generate/toString/singleTemplate/superClassNoVars.kt @@ -0,0 +1,11 @@ +// GENERATOR: SINGLE_TEMPLATE +// GENERATE_SUPER_CALL +open class X { + override fun toString() = super.toString() +} + +class A : X() { + fun foo() { + + } +} \ No newline at end of file diff --git a/idea/testData/codeInsight/generate/toString/singleTemplate/superClassNoVars.kt.after b/idea/testData/codeInsight/generate/toString/singleTemplate/superClassNoVars.kt.after new file mode 100644 index 00000000000..c4529ef2f75 --- /dev/null +++ b/idea/testData/codeInsight/generate/toString/singleTemplate/superClassNoVars.kt.after @@ -0,0 +1,15 @@ +// GENERATOR: SINGLE_TEMPLATE +// GENERATE_SUPER_CALL +open class X { + override fun toString() = super.toString() +} + +class A : X() { + fun foo() { + + } + + override fun toString(): String{ + return "A() ${super.toString()}" + } +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/generate/AbstractCodeInsightActionTest.kt b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/generate/AbstractCodeInsightActionTest.kt index ad096f0248c..1c7461bb881 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/generate/AbstractCodeInsightActionTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/generate/AbstractCodeInsightActionTest.kt @@ -37,7 +37,7 @@ abstract class AbstractCodeInsightActionTest : KotlinLightCodeInsightFixtureTest return Class.forName(actionClassName).newInstance() as CodeInsightAction } - private fun testAction(action: AnAction, forced: Boolean): Presentation { + protected open fun testAction(action: AnAction, forced: Boolean): Presentation { val e = TestActionEvent(action) action.beforeActionPerformedUpdate(e) if (forced || (e.presentation.isEnabled && e.presentation.isVisible)) { diff --git a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/generate/AbstractGenerateToStringActionTest.kt b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/generate/AbstractGenerateToStringActionTest.kt new file mode 100644 index 00000000000..aac9e57b9bb --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/generate/AbstractGenerateToStringActionTest.kt @@ -0,0 +1,60 @@ +/* + * Copyright 2010-2016 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. + */ + +/* + * Copyright 2010-2016 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.actionSystem.AnAction +import com.intellij.openapi.actionSystem.Presentation +import org.jetbrains.kotlin.idea.actions.generate.KotlinGenerateToStringAction +import org.jetbrains.kotlin.idea.actions.generate.KotlinGenerateToStringAction.Generator +import org.jetbrains.kotlin.psi.KtClass +import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType +import org.jetbrains.kotlin.test.InTextDirectivesUtils + +abstract class AbstractGenerateToStringActionTest : AbstractCodeInsightActionTest() { + override fun createAction(fileText: String) = KotlinGenerateToStringAction() + + override fun testAction(action: AnAction, forced: Boolean): Presentation { + val fileText = file.text + val generator = InTextDirectivesUtils.findStringWithPrefixes(fileText, "// GENERATOR: ")?.let { Generator.valueOf(it) } + val generateSuperCall = InTextDirectivesUtils.isDirectiveDefined(fileText, "// GENERATE_SUPER_CALL") + val klass = file.findElementAt(editor.caretModel.offset)?.getStrictParentOfType() + try { + with(KotlinGenerateToStringAction) { + klass?.adjuster = { it.copy(generateSuperCall = generateSuperCall, generator = generator ?: it.generator) } + } + return super.testAction(action, forced) + } finally { + with(KotlinGenerateToStringAction) { klass?.adjuster = null } + } + } +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/generate/GenerateToStringActionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/generate/GenerateToStringActionTestGenerated.java new file mode 100644 index 00000000000..f14a572e479 --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/generate/GenerateToStringActionTestGenerated.java @@ -0,0 +1,160 @@ +/* + * 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.KotlinTestUtils; +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/toString") +@TestDataPath("$PROJECT_ROOT") +@RunWith(JUnit3RunnerWithInners.class) +public class GenerateToStringActionTestGenerated extends AbstractGenerateToStringActionTest { + public void testAllFilesPresentInToString() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/codeInsight/generate/toString"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("idea/testData/codeInsight/generate/toString/common") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Common extends AbstractGenerateToStringActionTest { + public void testAllFilesPresentInCommon() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/codeInsight/generate/toString/common"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("annotation.kt") + public void testAnnotation() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/codeInsight/generate/toString/common/annotation.kt"); + doTest(fileName); + } + + @TestMetadata("dataClass.kt") + public void testDataClass() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/codeInsight/generate/toString/common/dataClass.kt"); + doTest(fileName); + } + + @TestMetadata("interface.kt") + public void testInterface() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/codeInsight/generate/toString/common/interface.kt"); + doTest(fileName); + } + + @TestMetadata("object.kt") + public void testObject() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/codeInsight/generate/toString/common/object.kt"); + doTest(fileName); + } + } + + @TestMetadata("idea/testData/codeInsight/generate/toString/multipeTemplates") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class MultipeTemplates extends AbstractGenerateToStringActionTest { + public void testAllFilesPresentInMultipeTemplates() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/codeInsight/generate/toString/multipeTemplates"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("arrays.kt") + public void testArrays() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/codeInsight/generate/toString/multipeTemplates/arrays.kt"); + doTest(fileName); + } + + @TestMetadata("multipleVars.kt") + public void testMultipleVars() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/codeInsight/generate/toString/multipeTemplates/multipleVars.kt"); + doTest(fileName); + } + + @TestMetadata("multipleVarsWithSuperClass.kt") + public void testMultipleVarsWithSuperClass() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/codeInsight/generate/toString/multipeTemplates/multipleVarsWithSuperClass.kt"); + doTest(fileName); + } + + @TestMetadata("noVars.kt") + public void testNoVars() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/codeInsight/generate/toString/multipeTemplates/noVars.kt"); + doTest(fileName); + } + + @TestMetadata("singleVar.kt") + public void testSingleVar() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/codeInsight/generate/toString/multipeTemplates/singleVar.kt"); + doTest(fileName); + } + + @TestMetadata("superClassNoVars.kt") + public void testSuperClassNoVars() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/codeInsight/generate/toString/multipeTemplates/superClassNoVars.kt"); + doTest(fileName); + } + } + + @TestMetadata("idea/testData/codeInsight/generate/toString/singleTemplate") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class SingleTemplate extends AbstractGenerateToStringActionTest { + public void testAllFilesPresentInSingleTemplate() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/codeInsight/generate/toString/singleTemplate"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("arrays.kt") + public void testArrays() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/codeInsight/generate/toString/singleTemplate/arrays.kt"); + doTest(fileName); + } + + @TestMetadata("multipleVars.kt") + public void testMultipleVars() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/codeInsight/generate/toString/singleTemplate/multipleVars.kt"); + doTest(fileName); + } + + @TestMetadata("multipleVarsWithSuperClass.kt") + public void testMultipleVarsWithSuperClass() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/codeInsight/generate/toString/singleTemplate/multipleVarsWithSuperClass.kt"); + doTest(fileName); + } + + @TestMetadata("noVars.kt") + public void testNoVars() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/codeInsight/generate/toString/singleTemplate/noVars.kt"); + doTest(fileName); + } + + @TestMetadata("singleVar.kt") + public void testSingleVar() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/codeInsight/generate/toString/singleTemplate/singleVar.kt"); + doTest(fileName); + } + + @TestMetadata("superClassNoVars.kt") + public void testSuperClassNoVars() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/codeInsight/generate/toString/singleTemplate/superClassNoVars.kt"); + doTest(fileName); + } + } +}