Code Insight: "Generate toString" action

#KT-10309 Fixed
This commit is contained in:
Alexey Sedunov
2016-01-11 20:49:09 +03:00
parent 77b637e238
commit 618f9f62f6
37 changed files with 889 additions and 66 deletions
@@ -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<String>) {
testClass<AbstractCodeInsightActionTest>() {
model("codeInsight/generate/secondaryConstructors")
}
testClass<AbstractGenerateToStringActionTest>() {
model("codeInsight/generate/toString")
}
}
testGroup("idea/tests", "compiler/testData") {
+3
View File
@@ -176,6 +176,9 @@
<action id="KotlinGenerateEqualsAndHashCode"
class="org.jetbrains.kotlin.idea.actions.generate.KotlinGenerateEqualsAndHashcodeAction"
text="equals() and hashCode()" />
<action id="KotlinGenerateToString"
class="org.jetbrains.kotlin.idea.actions.generate.KotlinGenerateToStringAction"
text="toString()" />
<add-to-group group-id="GenerateGroup" anchor="first"/>
</group>
@@ -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<KotlinGenerateEqualsAndHashcodeAction.Info>() {
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<Kot
override fun isValidForClass(targetClass: KtClassOrObject): Boolean {
return targetClass is KtClass && targetClass !is KtEnumEntry && !targetClass.isAnnotation()
&& !targetClass.hasModifier(KtTokens.DATA_KEYWORD)
&& targetClass.getPropertiesToUse().isNotEmpty()
}
private fun KtClassOrObject.getPropertiesToUse(): List<KtNamedDeclaration> {
return ArrayList<KtNamedDeclaration>().apply {
getPrimaryConstructorParameters().filterTo(this) { it.hasValOrVar() }
declarations.filterIsInstance<KtProperty>().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<Kot
var needEquals = equalsDescriptor == null
var needHashCode = hashCodeDescriptor == null
if (!needEquals && !needHashCode) {
if (!confirmRewrite(klass, equalsDescriptor!!, hashCodeDescriptor!!)) return null
if (!confirmMemberRewrite(klass, equalsDescriptor!!, hashCodeDescriptor!!)) return null
runWriteAction {
try {
@@ -152,7 +100,7 @@ class KotlinGenerateEqualsAndHashcodeAction : KotlinGenerateMemberActionBase<Kot
}
}
val properties = klass.getPropertiesToUse()
val properties = getPropertiesToUseInGeneratedMember(klass)
if (properties.isEmpty() || ApplicationManager.getApplication().isUnitTestMode) {
val descriptors = properties.map { context[BindingContext.DECLARATION_TO_DESCRIPTOR, it] as VariableDescriptor }
@@ -170,12 +118,6 @@ class KotlinGenerateEqualsAndHashcodeAction : KotlinGenerateMemberActionBase<Kot
}
}
private fun generateFunctionSkeleton(descriptor: FunctionDescriptor, project: Project): KtNamedFunction {
return OverrideMemberChooserObject
.create(project, descriptor, descriptor, OverrideMemberChooserObject.BodyType.EMPTY)
.generateMember(project) as KtNamedFunction
}
private fun generateEquals(project: Project, info: Info): KtNamedFunction? {
with(info) {
if (!needEquals) return null
@@ -230,6 +172,7 @@ class KotlinGenerateEqualsAndHashcodeAction : KotlinGenerateMemberActionBase<Kot
return equalsFun
}
}
private fun generateHashCode(project: Project, info: Info): KtNamedFunction? {
fun VariableDescriptor.genVariableHashCode(parenthesesNeeded: Boolean): String {
val ref = name.asString().quoteIfNeeded()
@@ -0,0 +1,192 @@
/*
* 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.ide.util.MemberChooser
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.DialogWrapper
import com.intellij.openapi.util.Key
import com.intellij.util.IncorrectOperationException
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
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.util.DescriptorMemberChooserObject
import org.jetbrains.kotlin.idea.quickfix.insertMembersAfter
import org.jetbrains.kotlin.idea.refactoring.quoteIfNeeded
import org.jetbrains.kotlin.idea.util.application.runWriteAction
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny
import org.jetbrains.kotlin.resolve.source.getPsi
import org.jetbrains.kotlin.utils.addToStdlib.lastIsInstanceOrNull
private fun ClassDescriptor.findDeclaredToString(checkSupers: Boolean): FunctionDescriptor? {
return findDeclaredFunction("toString", checkSupers) { it.valueParameters.isEmpty() && it.typeParameters.isEmpty() }
}
class KotlinGenerateToStringAction : KotlinGenerateMemberActionBase<KotlinGenerateToStringAction.Info>() {
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<VariableDescriptor>,
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<DescriptorMemberChooserObject>(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<KtDeclaration> {
val targetClass = info.classDescriptor.source.getPsi() as KtClass
val prototype = generateToString(project, info) ?: return emptyList()
val anchor = with(targetClass.declarations) { lastIsInstanceOrNull<KtNamedFunction>() ?: lastOrNull() }
return insertMembersAfter(editor, targetClass, listOf(prototype), anchor)
}
}
@@ -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();
}
}
@@ -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<KtNamedDeclaration> {
return ArrayList<KtNamedDeclaration>().apply {
classOrObject.getPrimaryConstructorParameters().filterTo(this) { it.hasValOrVar() }
classOrObject.declarations.filterIsInstance<KtProperty>().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
}
@@ -0,0 +1,2 @@
// NOT_APPLICABLE
annotation class A<caret>()
@@ -0,0 +1,2 @@
// NOT_APPLICABLE
data class A<caret>(val n: Int)
@@ -0,0 +1,6 @@
// NOT_APPLICABLE
interface A {<caret>
fun foo() {
}
}
@@ -0,0 +1,6 @@
// NOT_APPLICABLE
object A {<caret>
fun foo() {
}
}
@@ -0,0 +1,8 @@
// GENERATOR: MULTIPLE_TEMPLATES
class A(val n: IntArray, val s: Array<String>) {<caret>
val f: Float = 1.0f
fun foo() {
}
}
@@ -0,0 +1,18 @@
import java.util.Arrays
// GENERATOR: MULTIPLE_TEMPLATES
class A(val n: IntArray, val s: Array<String>) {
val f: Float = 1.0f
fun foo() {
}
override fun toString(): String{
return "A(" +
"n=${Arrays.toString(n)}," +
"s=${Arrays.toString(s)}," +
"f=$f" +
")"
}
}
@@ -0,0 +1,8 @@
// GENERATOR: MULTIPLE_TEMPLATES
class A(val n: Int, val s: String) {<caret>
val f: Float = 1.0f
fun foo() {
}
}
@@ -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" +
")"
}
}
@@ -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() {<caret>
val f: Float = 1.0f
fun foo() {
}
}
@@ -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()}"
}
}
@@ -0,0 +1,6 @@
// GENERATOR: MULTIPLE_TEMPLATES
class A {<caret>
fun foo() {
}
}
@@ -0,0 +1,10 @@
// GENERATOR: MULTIPLE_TEMPLATES
class A {
fun foo() {
}
<caret>override fun toString(): String{
return "A()"
}
}
@@ -0,0 +1,6 @@
// GENERATOR: MULTIPLE_TEMPLATES
class A(val n: Int) {<caret>
fun foo() {
}
}
@@ -0,0 +1,12 @@
// GENERATOR: MULTIPLE_TEMPLATES
class A(val n: Int) {
fun foo() {
}
override fun toString(): String{
return "A(" +
"n=$n" +
")"
}
}
@@ -0,0 +1,11 @@
// GENERATOR: MULTIPLE_TEMPLATES
// GENERATE_SUPER_CALL
open class X {
override fun toString() = super.toString()
}
class A : X() {<caret>
fun foo() {
}
}
@@ -0,0 +1,16 @@
// GENERATOR: MULTIPLE_TEMPLATES
// GENERATE_SUPER_CALL
open class X {
override fun toString() = super.toString()
}
class A : X() {
fun foo() {
}
<caret>override fun toString(): String{
return "A()" +
" ${super.toString()}"
}
}
@@ -0,0 +1,8 @@
// GENERATOR: SINGLE_TEMPLATE
class A(val n: IntArray, val s: Array<String>) {<caret>
val f: Float = 1.0f
fun foo() {
}
}
@@ -0,0 +1,14 @@
import java.util.Arrays
// GENERATOR: SINGLE_TEMPLATE
class A(val n: IntArray, val s: Array<String>) {
val f: Float = 1.0f
fun foo() {
}
override fun toString(): String{
return "A(n=${Arrays.toString(n)}, s=${Arrays.toString(s)}, f=$f)"
}
}
@@ -0,0 +1,8 @@
// GENERATOR: SINGLE_TEMPLATE
class A(val n: Int, val s: String) {<caret>
val f: Float = 1.0f
fun foo() {
}
}
@@ -0,0 +1,12 @@
// GENERATOR: SINGLE_TEMPLATE
class A(val n: Int, val s: String) {
val f: Float = 1.0f
fun foo() {
}
<caret>override fun toString(): String{
return "A(n=$n, s='$s', f=$f)"
}
}
@@ -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() {<caret>
val f: Float = 1.0f
fun foo() {
}
}
@@ -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() {
}
<caret>override fun toString(): String{
return "A(n=$n, s='$s', f=$f) ${super.toString()}"
}
}
@@ -0,0 +1,6 @@
// GENERATOR: SINGLE_TEMPLATE
class A {<caret>
fun foo() {
}
}
@@ -0,0 +1,10 @@
// GENERATOR: SINGLE_TEMPLATE
class A {
fun foo() {
}
<caret>override fun toString(): String{
return "A()"
}
}
@@ -0,0 +1,6 @@
// GENERATOR: SINGLE_TEMPLATE
class A(val n: Int) {<caret>
fun foo() {
}
}
@@ -0,0 +1,10 @@
// GENERATOR: SINGLE_TEMPLATE
class A(val n: Int) {
fun foo() {
}
<caret>override fun toString(): String{
return "A(n=$n)"
}
}
@@ -0,0 +1,11 @@
// GENERATOR: SINGLE_TEMPLATE
// GENERATE_SUPER_CALL
open class X {
override fun toString() = super.toString()
}
class A : X() {<caret>
fun foo() {
}
}
@@ -0,0 +1,15 @@
// GENERATOR: SINGLE_TEMPLATE
// GENERATE_SUPER_CALL
open class X {
override fun toString() = super.toString()
}
class A : X() {
fun foo() {
}
<caret>override fun toString(): String{
return "A() ${super.toString()}"
}
}
@@ -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)) {
@@ -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<KtClass>()
try {
with(KotlinGenerateToStringAction) {
klass?.adjuster = { it.copy(generateSuperCall = generateSuperCall, generator = generator ?: it.generator) }
}
return super.testAction(action, forced)
} finally {
with(KotlinGenerateToStringAction) { klass?.adjuster = null }
}
}
}
@@ -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);
}
}
}