Skip to content

Commit

Permalink
Merge pull request #29 from t-kameyama/issue_26
Browse files Browse the repository at this point in the history
Fixes #26 #27 #28
  • Loading branch information
suusan2go authored Nov 28, 2019
2 parents 4f8d1e5 + a914265 commit 1765ed2
Show file tree
Hide file tree
Showing 2 changed files with 131 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,20 @@ package com.github.suusan2go.kotlinfillclass.intentions
import com.intellij.openapi.editor.Editor
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.core.ShortenReferences
import org.jetbrains.kotlin.idea.imports.importableFqName
import org.jetbrains.kotlin.idea.intentions.SelfTargetingIntention
import org.jetbrains.kotlin.idea.intentions.callExpression
import org.jetbrains.kotlin.load.java.descriptors.JavaCallableMemberDescriptor
import org.jetbrains.kotlin.psi.KtCallExpression
import org.jetbrains.kotlin.psi.KtPsiFactory
import org.jetbrains.kotlin.psi.KtQualifiedExpression
import org.jetbrains.kotlin.psi.KtValueArgument
import org.jetbrains.kotlin.psi.KtValueArgumentList
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
Expand All @@ -34,6 +41,7 @@ class FillClassIntention : SelfTargetingIntention<KtValueArgumentList>(KtValueAr
val calleeExpression = getStrictParentOfType<KtCallExpression>()?.calleeExpression ?: return null
val context = calleeExpression.analyze(BodyResolveMode.PARTIAL)
val descriptor = calleeExpression.getReferenceTargets(context).firstOrNull() as? FunctionDescriptor
if (descriptor is JavaCallableMemberDescriptor) return null
return descriptor.takeIf { it is ClassConstructorDescriptor || it is SimpleFunctionDescriptor }
}

Expand All @@ -44,7 +52,11 @@ class FillClassIntention : SelfTargetingIntention<KtValueArgumentList>(KtValueAr
parameters.forEachIndexed { index, parameter ->
if (arguments.size > index && !arguments[index].isNamed()) return@forEachIndexed
if (parameter.name.identifier in argumentNames) return@forEachIndexed
addArgument(createDefaultValueArgument(parameter, factory))
val added = addArgument(createDefaultValueArgument(parameter, factory))
val argumentExpression = added.getArgumentExpression()
if (argumentExpression is KtQualifiedExpression) {
ShortenReferences.DEFAULT.process(argumentExpression)
}
}
}

Expand All @@ -65,19 +77,27 @@ class FillClassIntention : SelfTargetingIntention<KtValueArgumentList>(KtValueAr
type.isMarkedNullable -> "null"
else -> null
}
return if (defaultValue != null) {
factory.createArgument(factory.createExpression(defaultValue), parameter.name)
} else {
val valueParameters = (type.constructor.declarationDescriptor as? LazyClassDescriptor)
?.constructors?.firstOrNull { it is ClassConstructorDescriptor }?.valueParameters
val callExpression = if (valueParameters != null) {
(factory.createExpression("$type()") as? KtCallExpression)?.also {
it.valueArgumentList?.fillArguments(valueParameters)
}
} else {
null
if (defaultValue != null) {
return factory.createArgument(factory.createExpression(defaultValue), parameter.name)
}

val descriptor = type.constructor.declarationDescriptor as? LazyClassDescriptor
val modality = descriptor?.modality
if (descriptor?.kind == ClassKind.ENUM_CLASS || modality == Modality.ABSTRACT || modality == Modality.SEALED) {
return factory.createArgument(null, parameter.name)
}

val fqName = descriptor?.importableFqName?.asString()
val valueParameters =
descriptor?.constructors?.firstOrNull { it is ClassConstructorDescriptor }?.valueParameters
val argumentExpression = if (fqName != null && valueParameters != null) {
(factory.createExpression("$fqName()")).also {
val callExpression = it as? KtCallExpression ?: (it as? KtQualifiedExpression)?.callExpression
callExpression?.valueArgumentList?.fillArguments(valueParameters)
}
factory.createArgument(callExpression, parameter.name)
} else {
null
}
return factory.createArgument(argumentExpression, parameter.name)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,96 @@ class FillClassIntentionTest : BasePlatformTestCase() {
""")
}

fun `test don't add default value for enum,abstract,sealed`() {
doAvailableTest("""
enum class A(val a: String) {
Foo("foo"), Bar("bar"), Baz("baz");
}
sealed class B(val b: String)
abstract class C(val c: String)
class Test(a: A, b: B, c: C)
fun test() {
Test(<caret>)
}
""", """
enum class A(val a: String) {
Foo("foo"), Bar("bar"), Baz("baz");
}
sealed class B(val b: String)
abstract class C(val c: String)
class Test(a: A, b: B, c: C)
fun test() {
Test(a =, b =, c =)
}
""")
}

fun `test add import directives`() {
val dependency = """
package com.example
class A
class B(a: A)
"""
doAvailableTest("""
import com.example.B
val b = B(<caret>)
""", """
import com.example.A
import com.example.B
val b = B(a = A())
""", dependencies = listOf(dependency))
}

fun `test call java constructor`() {
val javaDependency = """
public class Java {
public Java(String str) {
}
}
"""
doUnavailableTest("""
fun test() {
Java(<caret>)
}
""", javaDependencies = listOf(javaDependency))
}

fun `test call java method`() {
val javaDependency = """
public class Java {
public Java(String str) {
}
public void foo(Java java) {
}
}
"""
doUnavailableTest("""
fun test() {
Java("").foo(<caret>)
}
""", javaDependencies = listOf(javaDependency))
}

private val intention = FillClassIntention()

private fun doAvailableTest(before: String, after: String, intentionText: String = "Fill class constructor") {
private fun doAvailableTest(
before: String,
after: String,
intentionText: String = "Fill class constructor",
dependencies: List<String> = emptyList(),
javaDependencies: List<String> = emptyList()
) {
checkCaret(before)
dependencies.forEachIndexed { index, dependency ->
myFixture.configureByText("dependency$index.kt", dependency.trimIndent())
}
javaDependencies.forEachIndexed { index, dependency ->
myFixture.configureByText("dependency$index.java", dependency.trimIndent())
}
myFixture.configureByText(KotlinFileType.INSTANCE, before.trimIndent())
myFixture.launchAction(intention)
check(intentionText == intention.text) {
Expand All @@ -84,8 +170,18 @@ class FillClassIntentionTest : BasePlatformTestCase() {
myFixture.checkResult(after.trimIndent())
}

private fun doUnavailableTest(before: String) {
private fun doUnavailableTest(
before: String,
dependencies: List<String> = emptyList(),
javaDependencies: List<String> = emptyList()
) {
checkCaret(before)
dependencies.forEachIndexed { index, dependency ->
myFixture.configureByText("dependency$index.kt", dependency.trimIndent())
}
javaDependencies.forEachIndexed { index, dependency ->
myFixture.configureByText("dependency$index.java", dependency.trimIndent())
}
myFixture.configureByText(KotlinFileType.INSTANCE, before.trimIndent())
check(intention.familyName !in myFixture.availableIntentions.mapNotNull { it.familyName }) {
"Intention should not be available"
Expand Down

0 comments on commit 1765ed2

Please sign in to comment.