Convert to block body intention action
This commit is contained in:
@@ -73,7 +73,12 @@ public class JetPropertyAccessor extends JetDeclarationImpl
|
||||
|
||||
@Override
|
||||
public boolean hasBlockBody() {
|
||||
return findChildByType(JetTokens.EQ) == null;
|
||||
return getEqualsToken() == null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public PsiElement getEqualsToken() {
|
||||
return findChildByType(JetTokens.EQ);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -104,6 +109,6 @@ public class JetPropertyAccessor extends JetDeclarationImpl
|
||||
@Nullable
|
||||
@Override
|
||||
public JetExpression getInitializer() {
|
||||
return PsiTreeUtil.getNextSiblingOfType(findChildByType(JetTokens.EQ), JetExpression.class);
|
||||
return PsiTreeUtil.getNextSiblingOfType(getEqualsToken(), JetExpression.class);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -274,6 +274,10 @@ fun main(args: Array<String>) {
|
||||
model("intentions/convertToExpressionBody", pattern = "^before(\\w+)\\.kt$")
|
||||
}
|
||||
|
||||
testClass(javaClass<AbstractIntentionTest>(), "ConvertToBlockBodyTestGenerated") {
|
||||
model("intentions/convertToBlockBody", pattern = "^before(\\w+)\\.kt$")
|
||||
}
|
||||
|
||||
testClass(javaClass<AbstractJSBasicCompletionTest>()) {
|
||||
model("completion/basic/common")
|
||||
model("completion/basic/js")
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
fun foo()<spot>: String {
|
||||
return "abc"
|
||||
}</spot>
|
||||
@@ -0,0 +1 @@
|
||||
fun foo() <spot>= "abc"</spot>
|
||||
@@ -0,0 +1,5 @@
|
||||
<html>
|
||||
<body>
|
||||
This intention converts expression body (single expression after '=' sign) to block body with return statement.
|
||||
</body>
|
||||
</html>
|
||||
@@ -381,6 +381,11 @@
|
||||
<category>Kotlin</category>
|
||||
</intentionAction>
|
||||
|
||||
<intentionAction>
|
||||
<className>org.jetbrains.jet.plugin.intentions.ConvertToBlockBodyAction</className>
|
||||
<category>Kotlin</category>
|
||||
</intentionAction>
|
||||
|
||||
<intentionAction>
|
||||
<className>org.jetbrains.jet.plugin.ktSignature.KotlinSignatureAnnotationIntention</className>
|
||||
<category>Kotlin</category>
|
||||
|
||||
@@ -55,6 +55,8 @@ specify.type.explicitly.add.action.name=Specify type explicitly
|
||||
specify.type.explicitly.remove.action.name=Remove explicitly specified type
|
||||
convert.to.expression.body.action.family.name=Convert to Expression Body
|
||||
convert.to.expression.body.action.name=Convert to expression body
|
||||
convert.to.block.body.action.family.name=Convert to Block Body
|
||||
convert.to.block.body.action.name=Convert to block body
|
||||
rename.parameter.to.match.overridden.method=Rename parameter to match overridden method
|
||||
rename.family=Rename
|
||||
rename.kotlin.package.class.error="Can't rename kotlin package class"
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
package org.jetbrains.jet.plugin.intentions
|
||||
|
||||
import com.intellij.codeInsight.intention.PsiElementBaseIntentionAction
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import org.jetbrains.jet.lang.psi.JetBlockExpression
|
||||
import org.jetbrains.jet.plugin.JetBundle
|
||||
import org.jetbrains.jet.lang.psi.*
|
||||
import org.jetbrains.jet.plugin.project.AnalyzerFacadeWithCache
|
||||
import org.jetbrains.jet.lang.resolve.BindingContext
|
||||
import org.jetbrains.jet.lang.types.TypeUtils
|
||||
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns
|
||||
import org.jetbrains.jet.lexer.JetTokens
|
||||
import org.jetbrains.jet.lang.types.JetType
|
||||
|
||||
public class ConvertToBlockBodyAction : PsiElementBaseIntentionAction() {
|
||||
override fun getFamilyName(): String = JetBundle.message("convert.to.block.body.action.family.name")
|
||||
|
||||
override fun isAvailable(project: Project, editor: Editor, element: PsiElement): Boolean {
|
||||
setText(JetBundle.message("convert.to.block.body.action.name"))
|
||||
return findDeclaration(element) != null
|
||||
}
|
||||
|
||||
override fun invoke(project: Project, editor: Editor, element: PsiElement) {
|
||||
val declaration = findDeclaration(element)!!
|
||||
val body = declaration.getBodyExpression()!!
|
||||
|
||||
fun generateBody(returnsValue: Boolean): JetExpression {
|
||||
val bodyType = expressionType(body)
|
||||
val needReturn = returnsValue &&
|
||||
(bodyType == null || (!KotlinBuiltIns.getInstance().isUnit(bodyType) && !KotlinBuiltIns.getInstance().isNothing(bodyType)))
|
||||
|
||||
val oldBodyText = body.getText()!!
|
||||
val newBodyText = if (needReturn) "return ${oldBodyText}" else oldBodyText
|
||||
return JetPsiFactory.createFunctionBody(project, newBodyText)
|
||||
}
|
||||
|
||||
if (declaration is JetNamedFunction) {
|
||||
val returnType = functionReturnType(declaration)!!
|
||||
if (!declaration.hasDeclaredReturnType() && !KotlinBuiltIns.getInstance().isUnit(returnType)) {
|
||||
specifyTypeExplicitly(declaration, returnType)
|
||||
}
|
||||
|
||||
val newBody = generateBody(!KotlinBuiltIns.getInstance().isUnit(returnType) && !KotlinBuiltIns.getInstance().isNothing(returnType))
|
||||
|
||||
declaration.getEqualsToken()!!.delete()
|
||||
body.replace(newBody)
|
||||
}
|
||||
else if (declaration is JetPropertyAccessor) {
|
||||
val newBody = generateBody(declaration.isGetter())
|
||||
declaration.getEqualsToken()!!.delete()
|
||||
body.replace(newBody)
|
||||
}
|
||||
else {
|
||||
throw RuntimeException("Unknown declaration type: $declaration")
|
||||
}
|
||||
}
|
||||
|
||||
private fun findDeclaration(element: PsiElement): JetDeclarationWithBody? {
|
||||
val declaration = PsiTreeUtil.getParentOfType(element, javaClass<JetDeclarationWithBody>())
|
||||
if (declaration == null || declaration is JetFunctionLiteral || declaration.hasBlockBody()) return null
|
||||
val body = declaration.getBodyExpression()
|
||||
if (body == null) return null
|
||||
|
||||
return when (declaration) {
|
||||
is JetNamedFunction -> {
|
||||
val returnType = functionReturnType(declaration)
|
||||
if (returnType == null) return null
|
||||
if (!declaration.hasDeclaredReturnType() && returnType.isError()) return null // do not convert when type is implicit and unknown
|
||||
declaration
|
||||
}
|
||||
|
||||
is JetPropertyAccessor -> declaration
|
||||
|
||||
else -> throw RuntimeException("Unknown declaration type: $declaration")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,7 @@ public class ConvertToExpressionBodyAction : PsiElementBaseIntentionAction() {
|
||||
if (!declaration.hasDeclaredReturnType() && declaration is JetNamedFunction) {
|
||||
val valueType = expressionType(value)
|
||||
if (valueType == null || !KotlinBuiltIns.getInstance().isUnit(valueType)) {
|
||||
specifyUnitTypeExplicitly(declaration)
|
||||
specifyTypeExplicitly(declaration, "Unit")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,20 +75,6 @@ public class ConvertToExpressionBodyAction : PsiElementBaseIntentionAction() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun expressionType(expression: JetExpression): JetType? {
|
||||
val resolveSession = AnalyzerFacadeWithCache.getLazyResolveSessionForFile(expression.getContainingFile() as JetFile)
|
||||
val bindingContext = resolveSession.resolveToElement(expression)
|
||||
return bindingContext.get(BindingContext.EXPRESSION_TYPE, expression)
|
||||
}
|
||||
|
||||
private fun specifyUnitTypeExplicitly(declaration: JetNamedFunction) {
|
||||
val project = declaration.getProject()
|
||||
val typeReference = JetPsiFactory.createType(project, "Unit")
|
||||
val anchor = declaration.getValueParameterList() ?: return/*incomplete declaration*/
|
||||
declaration.addAfter(typeReference, anchor)
|
||||
declaration.addAfter(JetPsiFactory.createColon(project), anchor)
|
||||
}
|
||||
|
||||
private fun containsReturn(element: PsiElement): Boolean {
|
||||
if (element is JetReturnExpression) return true
|
||||
//TODO: would be better to have some interface of declaration where return can be used
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
package org.jetbrains.jet.plugin.intentions
|
||||
|
||||
import org.jetbrains.jet.lang.psi.*
|
||||
import org.jetbrains.jet.lang.types.JetType
|
||||
import org.jetbrains.jet.plugin.project.AnalyzerFacadeWithCache
|
||||
import org.jetbrains.jet.lang.resolve.BindingContext
|
||||
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.jet.renderer.DescriptorRenderer
|
||||
import org.jetbrains.jet.plugin.codeInsight.ShortenReferences
|
||||
|
||||
fun specifyTypeExplicitly(declaration: JetNamedFunction, typeText: String) {
|
||||
specifyTypeExplicitly(declaration, JetPsiFactory.createType(declaration.getProject(), typeText))
|
||||
}
|
||||
|
||||
fun specifyTypeExplicitly(declaration: JetNamedFunction, `type`: JetType) {
|
||||
if (`type`.isError()) return
|
||||
val typeReference = JetPsiFactory.createType(declaration.getProject(), DescriptorRenderer.SOURCE_CODE.renderType(`type`))
|
||||
specifyTypeExplicitly(declaration, typeReference)
|
||||
ShortenReferences.process(declaration.getReturnTypeRef()!!)
|
||||
}
|
||||
|
||||
fun specifyTypeExplicitly(declaration: JetNamedFunction, typeReference: JetTypeReference) {
|
||||
val project = declaration.getProject()
|
||||
val anchor = declaration.getValueParameterList() ?: return/*incomplete declaration*/
|
||||
declaration.addAfter(typeReference, anchor)
|
||||
declaration.addAfter(JetPsiFactory.createColon(project), anchor)
|
||||
}
|
||||
|
||||
fun expressionType(expression: JetExpression): JetType? {
|
||||
val resolveSession = AnalyzerFacadeWithCache.getLazyResolveSessionForFile(expression.getContainingFile() as JetFile)
|
||||
val bindingContext = resolveSession.resolveToElement(expression)
|
||||
return bindingContext.get(BindingContext.EXPRESSION_TYPE, expression)
|
||||
}
|
||||
|
||||
fun functionReturnType(function: JetNamedFunction): JetType? {
|
||||
val resolveSession = AnalyzerFacadeWithCache.getLazyResolveSessionForFile(function.getContainingFile() as JetFile)
|
||||
val bindingContext = resolveSession.resolveToElement(function)
|
||||
val descriptor = bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, function)
|
||||
if (descriptor == null) return null
|
||||
return (descriptor as FunctionDescriptor).getReturnType()
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
// "Convert to block body" "true"
|
||||
fun foo(): String {
|
||||
return "abc"
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
// "Convert to block body" "true"
|
||||
// ERROR: Unresolved reference: bar
|
||||
fun <caret>foo(): String {
|
||||
return bar()
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
// "Convert to block body" "true"
|
||||
// ERROR: Unresolved reference: XXX
|
||||
// ERROR: Unresolved reference: bar
|
||||
fun <caret>foo(): XXX {
|
||||
return bar()
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// "Convert to block body" "true"
|
||||
fun foo(): Unit {
|
||||
bar()
|
||||
}
|
||||
|
||||
fun bar() { }
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
// "Convert to block body" "true"
|
||||
// ERROR: Unresolved reference: bar
|
||||
fun <caret>foo(): Unit {
|
||||
bar()
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
// "Convert to block body" "true"
|
||||
fun <caret>foo(): String {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
// "Convert to block body" "true"
|
||||
val foo: String
|
||||
get() {
|
||||
return "abc"
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
// "Convert to block body" "true"
|
||||
val foo: String
|
||||
get() {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// "Convert to block body" "true"
|
||||
import java.net.URI
|
||||
|
||||
fun foo(): URI {
|
||||
return java.io.File("x").toURI()
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// "Convert to block body" "true"
|
||||
fun foo() {
|
||||
bar()
|
||||
}
|
||||
|
||||
fun bar() { }
|
||||
@@ -0,0 +1,4 @@
|
||||
// "Convert to block body" "true"
|
||||
fun foo(): Nothing {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
// "Convert to block body" "true"
|
||||
var foo: String
|
||||
get() = "abc"
|
||||
set(value) {
|
||||
doSet(value)
|
||||
}
|
||||
|
||||
fun doSet(value: String){}
|
||||
@@ -0,0 +1,2 @@
|
||||
// "Convert to block body" "true"
|
||||
fun <caret>foo(): String = "abc"
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
// "Convert to block body" "true"
|
||||
// ERROR: Unresolved reference: bar
|
||||
fun <caret>foo(): String = bar()
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
// "Convert to block body" "true"
|
||||
// ERROR: Unresolved reference: XXX
|
||||
// ERROR: Unresolved reference: bar
|
||||
fun <caret>foo(): XXX = bar()
|
||||
@@ -0,0 +1,4 @@
|
||||
// "Convert to block body" "true"
|
||||
fun <caret>foo(): Unit = bar()
|
||||
|
||||
fun bar() { }
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
// "Convert to block body" "true"
|
||||
// ERROR: Unresolved reference: bar
|
||||
fun <caret>foo(): Unit = bar()
|
||||
@@ -0,0 +1,2 @@
|
||||
// "Convert to block body" "true"
|
||||
fun <caret>foo(): String = throw UnsupportedOperationException()
|
||||
@@ -0,0 +1,3 @@
|
||||
// "Convert to block body" "true"
|
||||
val foo: String
|
||||
<caret>get() = "abc"
|
||||
@@ -0,0 +1,3 @@
|
||||
// "Convert to block body" "true"
|
||||
val foo: String
|
||||
<caret>get() = throw UnsupportedOperationException()
|
||||
@@ -0,0 +1,2 @@
|
||||
// "Convert to block body" "true"
|
||||
fun <caret>foo() = java.io.File("x").toURI()
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
// "Convert to block body" "false"
|
||||
// ERROR: Unresolved reference: bar
|
||||
fun <caret>foo() = bar()
|
||||
@@ -0,0 +1,4 @@
|
||||
// "Convert to block body" "true"
|
||||
fun <caret>foo() = bar()
|
||||
|
||||
fun bar() { }
|
||||
@@ -0,0 +1,2 @@
|
||||
// "Convert to block body" "true"
|
||||
fun <caret>foo(): Nothing = throw UnsupportedOperationException()
|
||||
@@ -0,0 +1,6 @@
|
||||
// "Convert to block body" "true"
|
||||
var foo: String
|
||||
get() = "abc"
|
||||
<caret>set(value) = doSet(value)
|
||||
|
||||
fun doSet(value: String){}
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright 2010-2014 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.jet.plugin.intentions;
|
||||
|
||||
import junit.framework.Assert;
|
||||
import junit.framework.Test;
|
||||
import junit.framework.TestSuite;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.regex.Pattern;
|
||||
import org.jetbrains.jet.JetTestUtils;
|
||||
import org.jetbrains.jet.test.InnerTestClasses;
|
||||
import org.jetbrains.jet.test.TestMetadata;
|
||||
|
||||
import org.jetbrains.jet.plugin.intentions.AbstractIntentionTest;
|
||||
|
||||
/** This class is generated by {@link org.jetbrains.jet.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
|
||||
@SuppressWarnings("all")
|
||||
@TestMetadata("idea/testData/intentions/convertToBlockBody")
|
||||
public class ConvertToBlockBodyTestGenerated extends AbstractIntentionTest {
|
||||
public void testAllFilesPresentInConvertToBlockBody() throws Exception {
|
||||
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("idea/testData/intentions/convertToBlockBody"), Pattern.compile("^before(\\w+)\\.kt$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("beforeExplicitlyNonUnitFun.kt")
|
||||
public void testExplicitlyNonUnitFun() throws Exception {
|
||||
doTest("idea/testData/intentions/convertToBlockBody/beforeExplicitlyNonUnitFun.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("beforeExplicitlyTypedFunWithUnresolvedExpression.kt")
|
||||
public void testExplicitlyTypedFunWithUnresolvedExpression() throws Exception {
|
||||
doTest("idea/testData/intentions/convertToBlockBody/beforeExplicitlyTypedFunWithUnresolvedExpression.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("beforeExplicitlyTypedFunWithUnresolvedType.kt")
|
||||
public void testExplicitlyTypedFunWithUnresolvedType() throws Exception {
|
||||
doTest("idea/testData/intentions/convertToBlockBody/beforeExplicitlyTypedFunWithUnresolvedType.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("beforeExplicitlyUnitFun.kt")
|
||||
public void testExplicitlyUnitFun() throws Exception {
|
||||
doTest("idea/testData/intentions/convertToBlockBody/beforeExplicitlyUnitFun.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("beforeExplicitlyUnitFunWithUnresolvedExpression.kt")
|
||||
public void testExplicitlyUnitFunWithUnresolvedExpression() throws Exception {
|
||||
doTest("idea/testData/intentions/convertToBlockBody/beforeExplicitlyUnitFunWithUnresolvedExpression.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("beforeFunWithThrow.kt")
|
||||
public void testFunWithThrow() throws Exception {
|
||||
doTest("idea/testData/intentions/convertToBlockBody/beforeFunWithThrow.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("beforeGetter.kt")
|
||||
public void testGetter() throws Exception {
|
||||
doTest("idea/testData/intentions/convertToBlockBody/beforeGetter.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("beforeGetterWithThrow.kt")
|
||||
public void testGetterWithThrow() throws Exception {
|
||||
doTest("idea/testData/intentions/convertToBlockBody/beforeGetterWithThrow.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("beforeImplicitlyNonUnitFun.kt")
|
||||
public void testImplicitlyNonUnitFun() throws Exception {
|
||||
doTest("idea/testData/intentions/convertToBlockBody/beforeImplicitlyNonUnitFun.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("beforeImplicitlyTypedFunWithUnresolvedType.kt")
|
||||
public void testImplicitlyTypedFunWithUnresolvedType() throws Exception {
|
||||
doTest("idea/testData/intentions/convertToBlockBody/beforeImplicitlyTypedFunWithUnresolvedType.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("beforeImplicitlyUnitFun.kt")
|
||||
public void testImplicitlyUnitFun() throws Exception {
|
||||
doTest("idea/testData/intentions/convertToBlockBody/beforeImplicitlyUnitFun.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("beforeNothingFun.kt")
|
||||
public void testNothingFun() throws Exception {
|
||||
doTest("idea/testData/intentions/convertToBlockBody/beforeNothingFun.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("beforeSetter.kt")
|
||||
public void testSetter() throws Exception {
|
||||
doTest("idea/testData/intentions/convertToBlockBody/beforeSetter.kt");
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user