Add intentions to convert lazy <--> ordinary property
#KT-23501 Fixed
This commit is contained in:
committed by
Mikhail Glukhikh
parent
a2205cfc98
commit
fbd992f8c7
@@ -278,6 +278,14 @@ class KtPsiFactory @JvmOverloads constructor(private val project: Project, val m
|
||||
bodyExpression.replace(expression)
|
||||
return setter
|
||||
}
|
||||
|
||||
fun createPropertyDelegate(expression: KtExpression): KtPropertyDelegate {
|
||||
val property = createProperty("val x by lazy { 1 }")
|
||||
val delegate = property.delegate!!
|
||||
val delegateExpression = delegate.expression!!
|
||||
delegateExpression.replace(expression)
|
||||
return delegate
|
||||
}
|
||||
|
||||
fun createDestructuringDeclaration(text: String): KtDestructuringDeclaration {
|
||||
return createFunction("fun foo() {$text}").bodyBlockExpression!!.statements.first() as KtDestructuringDeclaration
|
||||
|
||||
@@ -1663,6 +1663,16 @@
|
||||
<category>Kotlin</category>
|
||||
</intentionAction>
|
||||
|
||||
<intentionAction>
|
||||
<className>org.jetbrains.kotlin.idea.intentions.ConvertOrdinaryPropertyToLazyIntention</className>
|
||||
<category>Kotlin</category>
|
||||
</intentionAction>
|
||||
|
||||
<intentionAction>
|
||||
<className>org.jetbrains.kotlin.idea.intentions.ConvertLazyPropertyToOrdinaryIntention</className>
|
||||
<category>Kotlin</category>
|
||||
</intentionAction>
|
||||
|
||||
<lang.inspectionSuppressor language="kotlin" implementationClass="org.jetbrains.kotlin.idea.inspections.KotlinInspectionSuppressor"/>
|
||||
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.intentions.ObjectLiteralToLambdaInspection"
|
||||
|
||||
+1
@@ -0,0 +1 @@
|
||||
val x: String = "Hello"
|
||||
+1
@@ -0,0 +1 @@
|
||||
val x: String by lazy { "Hello" }
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
<html>
|
||||
<body>
|
||||
This intention converts a lazy property / variable to an ordinary property / variable.
|
||||
</body>
|
||||
</html>
|
||||
+1
@@ -0,0 +1 @@
|
||||
val x: String by lazy { "Hello" }
|
||||
+1
@@ -0,0 +1 @@
|
||||
val x: String = "Hello"
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
<html>
|
||||
<body>
|
||||
This intention converts a ordinary property / variable to a lazy property / variable.
|
||||
|
||||
</body>
|
||||
</html>
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.intentions
|
||||
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.psi.PsiComment
|
||||
import org.jetbrains.kotlin.idea.inspections.collections.isCalling
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.allChildren
|
||||
|
||||
class ConvertLazyPropertyToOrdinaryIntention : SelfTargetingIntention<KtProperty>(
|
||||
KtProperty::class.java, "Convert to ordinary property"
|
||||
) {
|
||||
override fun isApplicableTo(element: KtProperty, caretOffset: Int): Boolean {
|
||||
val delegateExpression = element.delegate?.expression as? KtCallExpression ?: return false
|
||||
val lambdaBody = delegateExpression.functionLiteral()?.bodyExpression ?: return false
|
||||
if (lambdaBody.statements.isEmpty()) return false
|
||||
return delegateExpression.isCalling(FqName("kotlin.lazy"))
|
||||
}
|
||||
|
||||
override fun applyTo(element: KtProperty, editor: Editor?) {
|
||||
val delegate = element.delegate ?: return
|
||||
val delegateExpression = delegate.expression as? KtCallExpression ?: return
|
||||
val functionLiteral = delegateExpression.functionLiteral() ?: return
|
||||
element.initializer = functionLiteral.singleStatement() ?: KtPsiFactory(element).createExpression("run ${functionLiteral.text}")
|
||||
delegate.delete()
|
||||
}
|
||||
|
||||
private fun KtCallExpression.functionLiteral(): KtFunctionLiteral? {
|
||||
return lambdaArguments.singleOrNull()?.getLambdaExpression()?.functionLiteral
|
||||
}
|
||||
|
||||
private fun KtFunctionLiteral.singleStatement(): KtExpression? {
|
||||
val body = this.bodyExpression ?: return null
|
||||
if (body.allChildren.any { it is PsiComment }) return null
|
||||
return body.statements.singleOrNull()
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.intentions
|
||||
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import org.jetbrains.kotlin.idea.inspections.collections.isCalling
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.KtCallExpression
|
||||
import org.jetbrains.kotlin.psi.KtProperty
|
||||
import org.jetbrains.kotlin.psi.KtPsiFactory
|
||||
import org.jetbrains.kotlin.psi.createExpressionByPattern
|
||||
|
||||
class ConvertOrdinaryPropertyToLazyIntention : SelfTargetingIntention<KtProperty>(
|
||||
KtProperty::class.java, "Convert to lazy property"
|
||||
) {
|
||||
override fun isApplicableTo(element: KtProperty, caretOffset: Int): Boolean {
|
||||
return !element.isVar && element.initializer != null && element.getter == null && !element.isLocal
|
||||
}
|
||||
|
||||
override fun applyTo(element: KtProperty, editor: Editor?) {
|
||||
val initializer = element.initializer ?: return
|
||||
val psiFactory = KtPsiFactory(element)
|
||||
val newExpression = if (initializer is KtCallExpression && initializer.isCalling(FqName("kotlin.run"))) {
|
||||
initializer.calleeExpression?.replace(psiFactory.createExpression("lazy"))
|
||||
initializer
|
||||
} else {
|
||||
psiFactory.createExpressionByPattern("lazy { $0 }", initializer)
|
||||
}
|
||||
element.addAfter(psiFactory.createPropertyDelegate(newExpression), initializer)
|
||||
element.initializer = null
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
org.jetbrains.kotlin.idea.intentions.ConvertLazyPropertyToOrdinaryIntention
|
||||
@@ -0,0 +1,5 @@
|
||||
// WITH_RUNTIME
|
||||
<caret>val x by lazy {
|
||||
// comment
|
||||
1
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
// WITH_RUNTIME
|
||||
val x = run {
|
||||
// comment
|
||||
1
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
// WITH_RUNTIME
|
||||
<caret>val x by lazy {
|
||||
foo()
|
||||
1
|
||||
}
|
||||
|
||||
fun foo() {}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
// WITH_RUNTIME
|
||||
val x = run {
|
||||
foo()
|
||||
1
|
||||
}
|
||||
|
||||
fun foo() {}
|
||||
@@ -0,0 +1,3 @@
|
||||
// IS_APPLICABLE: false
|
||||
// WITH_RUNTIME
|
||||
<caret>val x by lazy { }
|
||||
@@ -0,0 +1,2 @@
|
||||
// WITH_RUNTIME
|
||||
<caret>val x: Int by lazy { 1 }
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
// WITH_RUNTIME
|
||||
val x: Int = 1
|
||||
@@ -0,0 +1,4 @@
|
||||
// WITH_RUNTIME
|
||||
val x by lazy {
|
||||
1
|
||||
}<caret>
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
// WITH_RUNTIME
|
||||
val x = 1
|
||||
@@ -0,0 +1,3 @@
|
||||
// DISABLE-ERRORS
|
||||
// WITH_RUNTIME
|
||||
<caret>var x: Int by lazy { 1 }
|
||||
@@ -0,0 +1,3 @@
|
||||
// DISABLE-ERRORS
|
||||
// WITH_RUNTIME
|
||||
var x: Int = 1
|
||||
@@ -0,0 +1 @@
|
||||
org.jetbrains.kotlin.idea.intentions.ConvertOrdinaryPropertyToLazyIntention
|
||||
@@ -0,0 +1,2 @@
|
||||
// WITH_RUNTIME
|
||||
<caret>val x: Int = 1
|
||||
@@ -0,0 +1,2 @@
|
||||
// WITH_RUNTIME
|
||||
val x: Int by lazy { 1 }
|
||||
@@ -0,0 +1,4 @@
|
||||
// IS_APPLICABLE: false
|
||||
class Foo {
|
||||
lateinit <caret>var x: String
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
// WITH_RUNTIME
|
||||
val x = run {
|
||||
foo()
|
||||
3
|
||||
}<caret>
|
||||
|
||||
fun foo() {}
|
||||
@@ -0,0 +1,7 @@
|
||||
// WITH_RUNTIME
|
||||
val x by lazy {
|
||||
foo()
|
||||
3
|
||||
}
|
||||
|
||||
fun foo() {}
|
||||
@@ -0,0 +1,2 @@
|
||||
// IS_APPLICABLE: false
|
||||
<caret>var x: Int = 1
|
||||
+1
@@ -1,5 +1,6 @@
|
||||
// "Add annotation target" "false"
|
||||
// WITH_RUNTIME
|
||||
// ACTION: Convert to ordinary property
|
||||
// ACTION: Introduce import alias
|
||||
// ACTION: Make internal
|
||||
// ACTION: Make private
|
||||
|
||||
+1
@@ -1,5 +1,6 @@
|
||||
// "Add annotation target" "false"
|
||||
// WITH_RUNTIME
|
||||
// ACTION: Convert to ordinary property
|
||||
// ACTION: Introduce import alias
|
||||
// ACTION: Make internal
|
||||
// ACTION: Make private
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// "Add non-null asserted (!!) call" "false"
|
||||
// ACTION: Convert to lazy property
|
||||
// ACTION: Add 'const' modifier
|
||||
// ACTION: Add 'toString()' call
|
||||
// ACTION: Change type of 'x' to 'String?'
|
||||
|
||||
+1
@@ -1,4 +1,5 @@
|
||||
// "class org.jetbrains.kotlin.idea.quickfix.ImportFix" "false"
|
||||
// ACTION: Convert to lazy property
|
||||
// ACTION: Convert property initializer to getter
|
||||
// ACTION: Create class 'SomeTest'
|
||||
// ACTION: Rename reference
|
||||
|
||||
Vendored
+1
@@ -1,6 +1,7 @@
|
||||
// "class org.jetbrains.kotlin.idea.quickfix.ImportFix" "false"
|
||||
// ERROR: Unresolved reference: SomeTest
|
||||
// ERROR: Expression expected, but a package name found
|
||||
// ACTION: Convert to lazy property
|
||||
// ACTION: Convert property initializer to getter
|
||||
// ACTION: Create class 'SomeTest'
|
||||
// ACTION: Replace safe access expression with 'if' expression
|
||||
|
||||
+1
@@ -1,4 +1,5 @@
|
||||
// "class org.jetbrains.kotlin.idea.quickfix.ImportFix" "false"
|
||||
// ACTION: Convert to lazy property
|
||||
// ACTION: Convert property initializer to getter
|
||||
// ACTION: Create class 'SomeClass'
|
||||
// ACTION: Create function 'SomeClass'
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// "class org.jetbrains.kotlin.idea.quickfix.ImportFix" "false"
|
||||
// ACTION: Convert to lazy property
|
||||
// ACTION: Convert property initializer to getter
|
||||
// ACTION: Create class 'ExcludedClass'
|
||||
// ACTION: Create function 'ExcludedClass'
|
||||
|
||||
+1
@@ -1,4 +1,5 @@
|
||||
// "class org.jetbrains.kotlin.idea.quickfix.ImportFix" "false"
|
||||
// ACTION: Convert to lazy property
|
||||
// ACTION: Convert property initializer to getter
|
||||
// ACTION: Create function 'someFunction'
|
||||
// ACTION: Rename reference
|
||||
|
||||
+1
@@ -1,4 +1,5 @@
|
||||
// "Create local variable 'foo'" "false"
|
||||
// ACTION: Convert to lazy property
|
||||
// ACTION: Convert property initializer to getter
|
||||
// ACTION: Create parameter 'foo'
|
||||
// ACTION: Create property 'foo'
|
||||
|
||||
+1
@@ -1,4 +1,5 @@
|
||||
// "Create local variable 'foo'" "false"
|
||||
// ACTION: Convert to lazy property
|
||||
// ACTION: Convert property initializer to getter
|
||||
// ACTION: Create property 'foo'
|
||||
// ACTION: Rename reference
|
||||
|
||||
+1
@@ -1,4 +1,5 @@
|
||||
// "Create parameter 'foo'" "false"
|
||||
// ACTION: Convert to lazy property
|
||||
// ACTION: Convert property initializer to getter
|
||||
// ACTION: Create property 'foo'
|
||||
// ACTION: Rename reference
|
||||
|
||||
Vendored
+1
@@ -1,4 +1,5 @@
|
||||
// "Create parameter 'foo'" "false"
|
||||
// ACTION: Convert to lazy property
|
||||
// ACTION: Convert property initializer to getter
|
||||
// ACTION: Create property 'foo'
|
||||
// ACTION: Rename reference
|
||||
|
||||
Vendored
+1
@@ -1,4 +1,5 @@
|
||||
// "Create parameter 'foo'" "false"
|
||||
// ACTION: Convert to lazy property
|
||||
// ACTION: Convert property initializer to getter
|
||||
// ACTION: Create property 'foo'
|
||||
// ACTION: Rename reference
|
||||
|
||||
+1
@@ -1,4 +1,5 @@
|
||||
// "Make bar internal" "false"
|
||||
// ACTION: Convert to lazy property
|
||||
// ACTION: Convert property initializer to getter
|
||||
// ACTION: Add names to call arguments
|
||||
// ACTION: Move to constructor
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// "Make 'MyClass' open" "false"
|
||||
// ACTION: Convert to lazy property
|
||||
// ACTION: Make 'y' not open
|
||||
// ACTION: Add 'const' modifier
|
||||
// ACTION: Make internal
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// "Make bar suspend" "false"
|
||||
// ACTION: Convert to lazy property
|
||||
// ACTION: Convert property initializer to getter
|
||||
// ACTION: Introduce import alias
|
||||
// ERROR: Suspend function 'foo' should be called only from a coroutine or another suspend function
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// "Replace '@JvmField' with 'const'" "false"
|
||||
// WITH_RUNTIME
|
||||
// ERROR: JvmField has no effect on a private property
|
||||
// ACTION: Convert to lazy property
|
||||
// ACTION: Make internal
|
||||
// ACTION: Make protected
|
||||
// ACTION: Make public
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// "Replace '@JvmField' with 'const'" "false"
|
||||
// WITH_RUNTIME
|
||||
// ERROR: JvmField has no effect on a private property
|
||||
// ACTION: Convert to lazy property
|
||||
// ACTION: Make internal
|
||||
// ACTION: Make public
|
||||
// ACTION: Remove explicit type specification
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// "Replace '@JvmField' with 'const'" "false"
|
||||
// WITH_RUNTIME
|
||||
// ERROR: JvmField has no effect on a private property
|
||||
// ACTION: Convert to lazy property
|
||||
// ACTION: Make internal
|
||||
// ACTION: Make public
|
||||
// ACTION: Remove explicit type specification
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// "Replace '@JvmField' with 'const'" "false"
|
||||
// WITH_RUNTIME
|
||||
// ERROR: JvmField has no effect on a private property
|
||||
// ACTION: Convert to lazy property
|
||||
// ACTION: Add 'const' modifier
|
||||
// ACTION: Make internal
|
||||
// ACTION: Make public
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// "Typo: Change to..." "false"
|
||||
// TOOL: com.intellij.spellchecker.inspections.SpellCheckingInspection
|
||||
// ACTION: Convert to lazy property
|
||||
// ACTION: Add 'const' modifier
|
||||
// ACTION: Convert property initializer to getter
|
||||
// ACTION: To raw string literal
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// "Cast expression '1' to 'UInt'" "false"
|
||||
// WITH_RUNTIME
|
||||
// ERROR: Conversion of signed constants to unsigned ones is prohibited
|
||||
// ACTION: Convert to lazy property
|
||||
// ACTION: Change parameter 'u' type of function 'takeUInt' to 'Int'
|
||||
// ACTION: Convert property initializer to getter
|
||||
// ACTION: Change to '1u'
|
||||
|
||||
+1
@@ -1,4 +1,5 @@
|
||||
// "Change to '1'" "false"
|
||||
// ACTION: Convert to lazy property
|
||||
// ACTION: Add 'const' modifier
|
||||
// ACTION: Change type of 'a' to 'Double'
|
||||
// ACTION: Convert expression to 'Int'
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// "Change to '10000000000000000000L'" "false"
|
||||
// ACTION: Convert to lazy property
|
||||
// ACTION: Add 'const' modifier
|
||||
// ACTION: Change type of 'a' to 'Double'
|
||||
// ACTION: Convert expression to 'Long'
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// "Change to '65000'" "false"
|
||||
// ACTION: Convert to lazy property
|
||||
// ACTION: Add 'const' modifier
|
||||
// ACTION: Change type of 'a' to 'Double'
|
||||
// ACTION: Convert expression to 'Short'
|
||||
|
||||
@@ -5049,6 +5049,49 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/intentions/convertLazyPropertyToOrdinary")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class ConvertLazyPropertyToOrdinary extends AbstractIntentionTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInConvertLazyPropertyToOrdinary() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/convertLazyPropertyToOrdinary"), Pattern.compile("^([\\w\\-_]+)\\.(kt|kts)$"), TargetBackend.ANY, true);
|
||||
}
|
||||
|
||||
@TestMetadata("comment.kt")
|
||||
public void testComment() throws Exception {
|
||||
runTest("idea/testData/intentions/convertLazyPropertyToOrdinary/comment.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("multiStatement.kt")
|
||||
public void testMultiStatement() throws Exception {
|
||||
runTest("idea/testData/intentions/convertLazyPropertyToOrdinary/multiStatement.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("noStatement.kt")
|
||||
public void testNoStatement() throws Exception {
|
||||
runTest("idea/testData/intentions/convertLazyPropertyToOrdinary/noStatement.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("singleStatement.kt")
|
||||
public void testSingleStatement() throws Exception {
|
||||
runTest("idea/testData/intentions/convertLazyPropertyToOrdinary/singleStatement.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("singleStatement2.kt")
|
||||
public void testSingleStatement2() throws Exception {
|
||||
runTest("idea/testData/intentions/convertLazyPropertyToOrdinary/singleStatement2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("var.kt")
|
||||
public void testVar() throws Exception {
|
||||
runTest("idea/testData/intentions/convertLazyPropertyToOrdinary/var.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/intentions/convertLineCommentToBlockComment")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
@@ -5248,6 +5291,39 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/intentions/convertOrdinaryPropertyToLazy")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class ConvertOrdinaryPropertyToLazy extends AbstractIntentionTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInConvertOrdinaryPropertyToLazy() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/convertOrdinaryPropertyToLazy"), Pattern.compile("^([\\w\\-_]+)\\.(kt|kts)$"), TargetBackend.ANY, true);
|
||||
}
|
||||
|
||||
@TestMetadata("basic.kt")
|
||||
public void testBasic() throws Exception {
|
||||
runTest("idea/testData/intentions/convertOrdinaryPropertyToLazy/basic.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("noInitializer.kt")
|
||||
public void testNoInitializer() throws Exception {
|
||||
runTest("idea/testData/intentions/convertOrdinaryPropertyToLazy/noInitializer.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("run.kt")
|
||||
public void testRun() throws Exception {
|
||||
runTest("idea/testData/intentions/convertOrdinaryPropertyToLazy/run.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("var.kt")
|
||||
public void testVar() throws Exception {
|
||||
runTest("idea/testData/intentions/convertOrdinaryPropertyToLazy/var.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/intentions/convertParameterToReceiver")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
Reference in New Issue
Block a user