Code Insight: "Generate toString" action
#KT-10309 Fixed
This commit is contained in:
@@ -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.AbstractCodeInsightActionTest
|
||||||
import org.jetbrains.kotlin.idea.codeInsight.generate.AbstractGenerateHashCodeAndEqualsActionTest
|
import org.jetbrains.kotlin.idea.codeInsight.generate.AbstractGenerateHashCodeAndEqualsActionTest
|
||||||
import org.jetbrains.kotlin.idea.codeInsight.generate.AbstractGenerateTestSupportMethodActionTest
|
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.moveUpDown.AbstractCodeMoverTest
|
||||||
import org.jetbrains.kotlin.idea.codeInsight.surroundWith.AbstractSurroundWithTest
|
import org.jetbrains.kotlin.idea.codeInsight.surroundWith.AbstractSurroundWithTest
|
||||||
import org.jetbrains.kotlin.idea.codeInsight.unwrap.AbstractUnwrapRemoveTest
|
import org.jetbrains.kotlin.idea.codeInsight.unwrap.AbstractUnwrapRemoveTest
|
||||||
@@ -779,6 +780,10 @@ fun main(args: Array<String>) {
|
|||||||
testClass<AbstractCodeInsightActionTest>() {
|
testClass<AbstractCodeInsightActionTest>() {
|
||||||
model("codeInsight/generate/secondaryConstructors")
|
model("codeInsight/generate/secondaryConstructors")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
testClass<AbstractGenerateToStringActionTest>() {
|
||||||
|
model("codeInsight/generate/toString")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
testGroup("idea/tests", "compiler/testData") {
|
testGroup("idea/tests", "compiler/testData") {
|
||||||
|
|||||||
@@ -176,6 +176,9 @@
|
|||||||
<action id="KotlinGenerateEqualsAndHashCode"
|
<action id="KotlinGenerateEqualsAndHashCode"
|
||||||
class="org.jetbrains.kotlin.idea.actions.generate.KotlinGenerateEqualsAndHashcodeAction"
|
class="org.jetbrains.kotlin.idea.actions.generate.KotlinGenerateEqualsAndHashcodeAction"
|
||||||
text="equals() and hashCode()" />
|
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"/>
|
<add-to-group group-id="GenerateGroup" anchor="first"/>
|
||||||
</group>
|
</group>
|
||||||
|
|||||||
+8
-65
@@ -16,32 +16,26 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.idea.actions.generate
|
package org.jetbrains.kotlin.idea.actions.generate
|
||||||
|
|
||||||
import com.intellij.codeInsight.CodeInsightBundle
|
|
||||||
import com.intellij.codeInsight.CodeInsightSettings
|
import com.intellij.codeInsight.CodeInsightSettings
|
||||||
import com.intellij.openapi.application.ApplicationManager
|
import com.intellij.openapi.application.ApplicationManager
|
||||||
import com.intellij.openapi.diagnostic.Logger
|
import com.intellij.openapi.diagnostic.Logger
|
||||||
import com.intellij.openapi.editor.Editor
|
import com.intellij.openapi.editor.Editor
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.openapi.ui.Messages
|
|
||||||
import com.intellij.util.IncorrectOperationException
|
import com.intellij.util.IncorrectOperationException
|
||||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
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.analyzeFully
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
|
|
||||||
import org.jetbrains.kotlin.idea.core.CollectingNameValidator
|
import org.jetbrains.kotlin.idea.core.CollectingNameValidator
|
||||||
import org.jetbrains.kotlin.idea.core.KotlinNameSuggester
|
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.quickfix.insertMembersAfter
|
||||||
import org.jetbrains.kotlin.idea.refactoring.quoteIfNeeded
|
import org.jetbrains.kotlin.idea.refactoring.quoteIfNeeded
|
||||||
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
||||||
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
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.lexer.KtTokens
|
||||||
import org.jetbrains.kotlin.name.Name
|
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext
|
import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext
|
||||||
import org.jetbrains.kotlin.renderer.ParameterNameRenderingPolicy
|
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
|
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny
|
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 org.jetbrains.kotlin.utils.addToStdlib.lastIsInstanceOrNull
|
||||||
import java.util.*
|
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? {
|
fun ClassDescriptor.findDeclaredEquals(checkSupers: Boolean): FunctionDescriptor? {
|
||||||
return findDeclaredFunction("equals", checkSupers) {
|
return findDeclaredFunction("equals", checkSupers) {
|
||||||
it.valueParameters.singleOrNull()?.type == it.builtIns.nullableAnyType && it.typeParameters.isEmpty()
|
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>() {
|
class KotlinGenerateEqualsAndHashcodeAction : KotlinGenerateMemberActionBase<KotlinGenerateEqualsAndHashcodeAction.Info>() {
|
||||||
companion object {
|
companion object {
|
||||||
private val LOG = Logger.getInstance(KotlinGenerateEqualsAndHashcodeAction::class.java)
|
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(
|
class Info(
|
||||||
@@ -95,35 +70,8 @@ class KotlinGenerateEqualsAndHashcodeAction : KotlinGenerateMemberActionBase<Kot
|
|||||||
|
|
||||||
override fun isValidForClass(targetClass: KtClassOrObject): Boolean {
|
override fun isValidForClass(targetClass: KtClassOrObject): Boolean {
|
||||||
return targetClass is KtClass && targetClass !is KtEnumEntry && !targetClass.isAnnotation()
|
return targetClass is KtClass && targetClass !is KtEnumEntry && !targetClass.isAnnotation()
|
||||||
&& !targetClass.hasModifier(KtTokens.DATA_KEYWORD)
|
&& !targetClass.hasModifier(KtTokens.DATA_KEYWORD)
|
||||||
&& targetClass.getPropertiesToUse().isNotEmpty()
|
&& getPropertiesToUseInGeneratedMember(targetClass).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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun prepareMembersInfo(klass: KtClassOrObject, project: Project, editor: Editor?): Info? {
|
override fun prepareMembersInfo(klass: KtClassOrObject, project: Project, editor: Editor?): Info? {
|
||||||
@@ -138,7 +86,7 @@ class KotlinGenerateEqualsAndHashcodeAction : KotlinGenerateMemberActionBase<Kot
|
|||||||
var needEquals = equalsDescriptor == null
|
var needEquals = equalsDescriptor == null
|
||||||
var needHashCode = hashCodeDescriptor == null
|
var needHashCode = hashCodeDescriptor == null
|
||||||
if (!needEquals && !needHashCode) {
|
if (!needEquals && !needHashCode) {
|
||||||
if (!confirmRewrite(klass, equalsDescriptor!!, hashCodeDescriptor!!)) return null
|
if (!confirmMemberRewrite(klass, equalsDescriptor!!, hashCodeDescriptor!!)) return null
|
||||||
|
|
||||||
runWriteAction {
|
runWriteAction {
|
||||||
try {
|
try {
|
||||||
@@ -152,7 +100,7 @@ class KotlinGenerateEqualsAndHashcodeAction : KotlinGenerateMemberActionBase<Kot
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val properties = klass.getPropertiesToUse()
|
val properties = getPropertiesToUseInGeneratedMember(klass)
|
||||||
|
|
||||||
if (properties.isEmpty() || ApplicationManager.getApplication().isUnitTestMode) {
|
if (properties.isEmpty() || ApplicationManager.getApplication().isUnitTestMode) {
|
||||||
val descriptors = properties.map { context[BindingContext.DECLARATION_TO_DESCRIPTOR, it] as VariableDescriptor }
|
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? {
|
private fun generateEquals(project: Project, info: Info): KtNamedFunction? {
|
||||||
with(info) {
|
with(info) {
|
||||||
if (!needEquals) return null
|
if (!needEquals) return null
|
||||||
@@ -230,6 +172,7 @@ class KotlinGenerateEqualsAndHashcodeAction : KotlinGenerateMemberActionBase<Kot
|
|||||||
return equalsFun
|
return equalsFun
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun generateHashCode(project: Project, info: Info): KtNamedFunction? {
|
private fun generateHashCode(project: Project, info: Info): KtNamedFunction? {
|
||||||
fun VariableDescriptor.genVariableHashCode(parenthesesNeeded: Boolean): String {
|
fun VariableDescriptor.genVariableHashCode(parenthesesNeeded: Boolean): String {
|
||||||
val ref = name.asString().quoteIfNeeded()
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
+86
@@ -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() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
+16
@@ -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" +
|
||||||
|
")"
|
||||||
|
}
|
||||||
|
}
|
||||||
+13
@@ -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() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
Vendored
+22
@@ -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() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
// GENERATOR: MULTIPLE_TEMPLATES
|
||||||
|
class A(val n: Int) {
|
||||||
|
fun foo() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toString(): String{
|
||||||
|
return "A(" +
|
||||||
|
"n=$n" +
|
||||||
|
")"
|
||||||
|
}
|
||||||
|
}
|
||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
// GENERATOR: MULTIPLE_TEMPLATES
|
||||||
|
// GENERATE_SUPER_CALL
|
||||||
|
open class X {
|
||||||
|
override fun toString() = super.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
class A : X() {<caret>
|
||||||
|
fun foo() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
+16
@@ -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() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
+12
@@ -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)"
|
||||||
|
}
|
||||||
|
}
|
||||||
+13
@@ -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() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
Vendored
+17
@@ -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() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
+10
@@ -0,0 +1,10 @@
|
|||||||
|
// GENERATOR: SINGLE_TEMPLATE
|
||||||
|
class A(val n: Int) {
|
||||||
|
fun foo() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
<caret>override fun toString(): String{
|
||||||
|
return "A(n=$n)"
|
||||||
|
}
|
||||||
|
}
|
||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
// GENERATOR: SINGLE_TEMPLATE
|
||||||
|
// GENERATE_SUPER_CALL
|
||||||
|
open class X {
|
||||||
|
override fun toString() = super.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
class A : X() {<caret>
|
||||||
|
fun foo() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
+15
@@ -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()}"
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-1
@@ -37,7 +37,7 @@ abstract class AbstractCodeInsightActionTest : KotlinLightCodeInsightFixtureTest
|
|||||||
return Class.forName(actionClassName).newInstance() as CodeInsightAction
|
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)
|
val e = TestActionEvent(action)
|
||||||
action.beforeActionPerformedUpdate(e)
|
action.beforeActionPerformedUpdate(e)
|
||||||
if (forced || (e.presentation.isEnabled && e.presentation.isVisible)) {
|
if (forced || (e.presentation.isEnabled && e.presentation.isVisible)) {
|
||||||
|
|||||||
+60
@@ -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 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+160
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user