Intentions: Implement "Introduce local variable" intention
#KT-11768 Fixed
This commit is contained in:
@@ -11,6 +11,10 @@
|
||||
|
||||
### IDE
|
||||
|
||||
New features:
|
||||
|
||||
- [KT-11768](https://youtrack.jetbrains.com/issue/KT-11768) Implement "Introduce local variable" intention
|
||||
|
||||
Issues fixed:
|
||||
|
||||
- [KT-11145](https://youtrack.jetbrains.com/issue/KT-11145) Use progress indicator when searching usages in Introduce Parameter
|
||||
|
||||
+3
-4
@@ -32,9 +32,8 @@ import com.intellij.psi.PsiElementVisitor
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.util.SmartList
|
||||
import org.jetbrains.kotlin.idea.intentions.SelfTargetingRangeIntention
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
|
||||
abstract class IntentionBasedInspection<TElement : KtElement>(
|
||||
abstract class IntentionBasedInspection<TElement : PsiElement>(
|
||||
val intentions: List<IntentionBasedInspection.IntentionData<TElement>>,
|
||||
protected val problemText: String?,
|
||||
protected val elementType: Class<TElement>
|
||||
@@ -43,7 +42,7 @@ abstract class IntentionBasedInspection<TElement : KtElement>(
|
||||
constructor(intention: SelfTargetingRangeIntention<TElement>, additionalChecker: (TElement) -> Boolean = { true })
|
||||
: this(listOf(IntentionData(intention, additionalChecker)), null, intention.elementType)
|
||||
|
||||
data class IntentionData<TElement : KtElement>(
|
||||
data class IntentionData<TElement : PsiElement>(
|
||||
val intention: SelfTargetingRangeIntention<TElement>,
|
||||
val additionalChecker: (TElement) -> Boolean = { true }
|
||||
)
|
||||
@@ -88,7 +87,7 @@ abstract class IntentionBasedInspection<TElement : KtElement>(
|
||||
get() = ProblemHighlightType.GENERIC_ERROR_OR_WARNING
|
||||
|
||||
/* we implement IntentionAction to provide isAvailable which will be used to hide outdated items and make sure we never call 'invoke' for such item */
|
||||
private class IntentionBasedQuickFix<TElement : KtElement>(
|
||||
private class IntentionBasedQuickFix<TElement : PsiElement>(
|
||||
private val intention: SelfTargetingRangeIntention<TElement>,
|
||||
private val text: String,
|
||||
private val additionalChecker: (TElement) -> Boolean,
|
||||
|
||||
+3
-3
@@ -34,7 +34,7 @@ import org.jetbrains.kotlin.psi.psiUtil.containsInside
|
||||
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
|
||||
import java.util.*
|
||||
|
||||
abstract class SelfTargetingIntention<TElement : KtElement>(
|
||||
abstract class SelfTargetingIntention<TElement : PsiElement>(
|
||||
val elementType: Class<TElement>,
|
||||
private var text: String,
|
||||
private val familyName: String = text
|
||||
@@ -111,7 +111,7 @@ abstract class SelfTargetingIntention<TElement : KtElement>(
|
||||
companion object {
|
||||
private val intentionBasedInspections = HashMap<Class<out SelfTargetingIntention<*>>, IntentionBasedInspection<*>?>()
|
||||
|
||||
fun <TElement : KtElement> findInspection(intentionClass: Class<out SelfTargetingIntention<TElement>>): IntentionBasedInspection<TElement>? {
|
||||
fun <TElement : PsiElement> findInspection(intentionClass: Class<out SelfTargetingIntention<TElement>>): IntentionBasedInspection<TElement>? {
|
||||
if (intentionBasedInspections.containsKey(intentionClass)) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return intentionBasedInspections[intentionClass] as IntentionBasedInspection<TElement>?
|
||||
@@ -131,7 +131,7 @@ abstract class SelfTargetingIntention<TElement : KtElement>(
|
||||
}
|
||||
}
|
||||
|
||||
abstract class SelfTargetingRangeIntention<TElement : KtElement>(
|
||||
abstract class SelfTargetingRangeIntention<TElement : PsiElement>(
|
||||
elementType: Class<TElement>,
|
||||
text: String,
|
||||
familyName: String = text
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
val i = Integer.parseInt("1")
|
||||
@@ -0,0 +1 @@
|
||||
<spot>Integer.parseInt("1")</spot>
|
||||
@@ -0,0 +1,5 @@
|
||||
<html>
|
||||
<body>
|
||||
This intention puts a result of the expression into a variable.
|
||||
</body>
|
||||
</html>
|
||||
@@ -1196,6 +1196,11 @@
|
||||
<category>Kotlin</category>
|
||||
</intentionAction>
|
||||
|
||||
<intentionAction>
|
||||
<className>org.jetbrains.kotlin.idea.intentions.IntroduceVariableIntention</className>
|
||||
<category>Kotlin</category>
|
||||
</intentionAction>
|
||||
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.intentions.ObjectLiteralToLambdaInspection"
|
||||
displayName="Object literal can be converted to lambda"
|
||||
groupName="Kotlin"
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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.intentions
|
||||
|
||||
import com.intellij.codeInsight.CodeInsightBundle
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiWhiteSpace
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.introduceVariable.KotlinIntroduceVariableHandler
|
||||
import org.jetbrains.kotlin.psi.KtBlockExpression
|
||||
import org.jetbrains.kotlin.psi.KtDeclarationWithBody
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
|
||||
import org.jetbrains.kotlin.types.typeUtil.isNothing
|
||||
import org.jetbrains.kotlin.types.typeUtil.isUnit
|
||||
|
||||
class IntroduceVariableIntention : SelfTargetingRangeIntention<PsiElement>(
|
||||
PsiElement::class.java, CodeInsightBundle.message("intention.introduce.variable.text")
|
||||
) {
|
||||
private fun getExpressionToProcess(element: PsiElement): KtExpression? {
|
||||
val startElement = PsiTreeUtil.skipSiblingsBackward(element, PsiWhiteSpace::class.java) ?: element
|
||||
return startElement.parentsWithSelf
|
||||
.filterIsInstance<KtExpression>()
|
||||
.takeWhile { it !is KtDeclarationWithBody }
|
||||
.firstOrNull {
|
||||
val parent = it.parent
|
||||
parent is KtBlockExpression || parent is KtDeclarationWithBody && !parent.hasBlockBody() && parent.bodyExpression == it
|
||||
}
|
||||
}
|
||||
|
||||
override fun applicabilityRange(element: PsiElement): TextRange? {
|
||||
val expression = getExpressionToProcess(element) ?: return null
|
||||
val type = expression.analyze().getType(expression)
|
||||
if (type == null || type.isUnit() || type.isNothing()) return null
|
||||
return element.textRange
|
||||
}
|
||||
|
||||
override fun applyTo(element: PsiElement, editor: Editor?) {
|
||||
val expression = getExpressionToProcess(element) ?: return
|
||||
KotlinIntroduceVariableHandler.doRefactoring(element.project, editor, expression, null, null)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
org.jetbrains.kotlin.idea.intentions.IntroduceVariableIntention
|
||||
@@ -0,0 +1,5 @@
|
||||
fun foo(a: Int, b: Int) = a + b
|
||||
|
||||
fun foo() {
|
||||
foo(1 + 2, 3 * 4) <caret>//
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
fun foo(a: Int, b: Int) = a + b
|
||||
|
||||
fun foo() {
|
||||
val foo = foo(1 + 2, 3 * 4)<caret> //
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
fun foo(a: Int, b: Int) = a + b
|
||||
|
||||
fun foo() {
|
||||
foo(1 + 2, 3 * 4)<caret>
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
fun foo(a: Int, b: Int) = a + b
|
||||
|
||||
fun foo() {
|
||||
val foo = foo(1 + 2, 3 * 4)<caret>
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
fun foo(a: Int, b: Int) = a + b
|
||||
|
||||
fun foo() {
|
||||
<caret>foo(1 + 2, 3 * 4)
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
fun foo(a: Int, b: Int) = a + b
|
||||
|
||||
fun foo() {
|
||||
val foo = foo(1 + 2, 3 * 4)<caret>
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
fun foo(a: Int, b: Int) = a + b
|
||||
|
||||
fun foo() {
|
||||
foo(1 <caret>+ 2, 3 * 4)
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
fun foo(a: Int, b: Int) = a + b
|
||||
|
||||
fun foo() {
|
||||
val foo = foo(1 + 2, 3 * 4)<caret>
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// IS_APPLICABLE: false
|
||||
fun foo(a: Int, b: Int) = a + b
|
||||
|
||||
fun foo() {
|
||||
<caret> foo(1 + 2, 3 * 4)
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// IS_APPLICABLE: false
|
||||
fun foo(a: Int, b: Int) = a + b
|
||||
|
||||
fun foo() {
|
||||
val foo = foo(1 + 2, 3 * 4)<caret>
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
// IS_APPLICABLE: false
|
||||
fun foo(): Int {
|
||||
return 1 + 2<caret>
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// IS_APPLICABLE: false
|
||||
fun foo(a: Int, b: Int) {}
|
||||
|
||||
fun foo() {
|
||||
foo(1 + 2, 3 * 4)<caret>
|
||||
}
|
||||
+1
@@ -1,4 +1,5 @@
|
||||
// "Create class 'A'" "false"
|
||||
// ACTION: Rename reference
|
||||
// ACTION: Introduce local variable
|
||||
// ERROR: Unresolved reference: A
|
||||
fun foo() = J.<caret>A.B
|
||||
|
||||
Vendored
+1
@@ -2,5 +2,6 @@
|
||||
// ERROR: Too many arguments for public constructor G() defined in G
|
||||
// ACTION: Convert to block body
|
||||
// ACTION: Create function 'G'
|
||||
// ACTION: Introduce local variable
|
||||
|
||||
fun test() = G(<caret>1)
|
||||
+1
@@ -1,5 +1,6 @@
|
||||
// "Change parameter 'z' type of function 'foo' to '(Int) -> String'" "false"
|
||||
// ACTION: To raw string literal
|
||||
// ACTION: Introduce local variable
|
||||
|
||||
fun foo(y: Int = 0, z: (Int) -> String = {""}) {
|
||||
foo {
|
||||
|
||||
@@ -6103,6 +6103,63 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/intentions/introduceVariable")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class IntroduceVariable extends AbstractIntentionTest {
|
||||
@TestMetadata("afterExpression.kt")
|
||||
public void testAfterExpression() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/introduceVariable/afterExpression.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInIntroduceVariable() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/introduceVariable"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("atExpressionEnd.kt")
|
||||
public void testAtExpressionEnd() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/introduceVariable/atExpressionEnd.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("atExpressionStart.kt")
|
||||
public void testAtExpressionStart() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/introduceVariable/atExpressionStart.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("atNestedExpression.kt")
|
||||
public void testAtNestedExpression() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/introduceVariable/atNestedExpression.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("beforeExpression.kt")
|
||||
public void testBeforeExpression() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/introduceVariable/beforeExpression.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("noType.kt")
|
||||
public void testNoType() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/introduceVariable/noType.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("nothingType.kt")
|
||||
public void testNothingType() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/introduceVariable/nothingType.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("unitType.kt")
|
||||
public void testUnitType() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/introduceVariable/unitType.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/intentions/invertIfCondition")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
Reference in New Issue
Block a user