Introduce Parameter: Support extraction of string template fragments
This commit is contained in:
+21
-15
@@ -38,14 +38,13 @@ import org.jetbrains.kotlin.idea.core.KotlinNameSuggester
|
||||
import org.jetbrains.kotlin.idea.core.NewDeclarationNameValidator
|
||||
import org.jetbrains.kotlin.idea.util.getResolutionScope
|
||||
import org.jetbrains.kotlin.idea.core.moveInsideParenthesesAndReplaceWith
|
||||
import org.jetbrains.kotlin.idea.core.refactoring.removeTemplateEntryBracesIfPossible
|
||||
import org.jetbrains.kotlin.idea.core.refactoring.runRefactoringWithPostprocessing
|
||||
import org.jetbrains.kotlin.idea.core.replaced
|
||||
import org.jetbrains.kotlin.idea.refactoring.KotlinRefactoringBundle
|
||||
import org.jetbrains.kotlin.idea.refactoring.changeSignature.*
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.KotlinIntroduceHandlerBase
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.*
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.*
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.selectElementsWithTargetParent
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.showErrorHint
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.showErrorHintByKey
|
||||
import org.jetbrains.kotlin.idea.references.mainReference
|
||||
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
||||
import org.jetbrains.kotlin.idea.util.application.executeCommand
|
||||
@@ -205,9 +204,10 @@ public open class KotlinIntroduceParameterHandler(
|
||||
val helper: KotlinIntroduceParameterHelper = KotlinIntroduceParameterHelper.Default
|
||||
): KotlinIntroduceHandlerBase() {
|
||||
open fun invoke(project: Project, editor: Editor, expression: KtExpression, targetParent: KtNamedDeclaration) {
|
||||
val context = expression.analyze()
|
||||
val physicalExpression = expression.substringContextOrThis
|
||||
val context = physicalExpression.analyze()
|
||||
|
||||
val expressionType = context.getType(expression)
|
||||
val expressionType = expression.extractableSubstringInfo?.type ?: context.getType(physicalExpression)
|
||||
if (expressionType == null) {
|
||||
showErrorHint(project, editor, "Expression has no type", INTRODUCE_PARAMETER)
|
||||
return
|
||||
@@ -246,7 +246,7 @@ public open class KotlinIntroduceParameterHandler(
|
||||
val occurrencesToReplace = expression.toRange()
|
||||
.match(body, KotlinPsiUnifier.DEFAULT)
|
||||
.filterNot {
|
||||
val textRange = it.range.getTextRange()
|
||||
val textRange = it.range.getPhysicalTextRange()
|
||||
forbiddenRanges.any { it.intersects(textRange) }
|
||||
}
|
||||
.mapNotNull {
|
||||
@@ -267,7 +267,10 @@ public open class KotlinIntroduceParameterHandler(
|
||||
val haveLambdaArgumentsToReplace = occurrencesToReplace.any {
|
||||
it.elements.any { it is KtFunctionLiteralExpression && it.parent is KtFunctionLiteralArgument }
|
||||
}
|
||||
val inplaceIsAvailable = editor.settings.isVariableInplaceRenameEnabled && !isTestMode && !haveLambdaArgumentsToReplace
|
||||
val inplaceIsAvailable = editor.settings.isVariableInplaceRenameEnabled
|
||||
&& !isTestMode
|
||||
&& !haveLambdaArgumentsToReplace
|
||||
&& expression.extractableSubstringInfo == null
|
||||
|
||||
val originalExpression = KtPsiUtil.safeDeparenthesize(expression)
|
||||
val psiFactory = KtPsiFactory(project)
|
||||
@@ -286,14 +289,17 @@ public open class KotlinIntroduceParameterHandler(
|
||||
occurrenceReplacer = {
|
||||
val expressionToReplace = it.elements.single() as KtExpression
|
||||
val replacingExpression = psiFactory.createExpression(newParameterName)
|
||||
if (expressionToReplace.isFunctionLiteralOutsideParentheses()) {
|
||||
expressionToReplace
|
||||
.getStrictParentOfType<KtFunctionLiteralArgument>()!!
|
||||
.moveInsideParenthesesAndReplaceWith(replacingExpression, context)
|
||||
}
|
||||
else {
|
||||
expressionToReplace.replace(replacingExpression)
|
||||
val substringInfo = expressionToReplace.extractableSubstringInfo
|
||||
val result = when {
|
||||
expressionToReplace.isFunctionLiteralOutsideParentheses() -> {
|
||||
expressionToReplace
|
||||
.getStrictParentOfType<KtFunctionLiteralArgument>()!!
|
||||
.moveInsideParenthesesAndReplaceWith(replacingExpression, context)
|
||||
}
|
||||
substringInfo != null -> substringInfo.replaceWith(replacingExpression)
|
||||
else -> expressionToReplace.replaced(replacingExpression)
|
||||
}
|
||||
result.removeTemplateEntryBracesIfPossible()
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
fun foo(param: Int): String {
|
||||
return "<selection>abc${pa</selection>ram}def"
|
||||
}
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
Cannot perform refactoring without an expression
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
fun foo(param: Int): String {
|
||||
return "<selection>abc$pa</selection>ram"
|
||||
}
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
Cannot perform refactoring without an expression
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
fun foo(a: Int): String {
|
||||
return "<selection>abc$a\</selection>ndef"
|
||||
}
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
Cannot perform refactoring without an expression
|
||||
@@ -0,0 +1,6 @@
|
||||
fun foo(param: Int): String {
|
||||
val x = "xyfalsez"
|
||||
val y = "xyFalsez"
|
||||
val z = false
|
||||
return "ab<selection>false</selection>def"
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
fun foo(param: Int, b: Boolean = false): String {
|
||||
val x = "xy${b}z"
|
||||
val y = "xyFalsez"
|
||||
val z = false
|
||||
return "ab${b}def"
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
fun foo(param: Int): String {
|
||||
val x = "a1234_"
|
||||
val y = "-4123a"
|
||||
val z = "+1243a"
|
||||
val u = 123
|
||||
return "ab<selection>123</selection>def"
|
||||
}
|
||||
Vendored
+7
@@ -0,0 +1,7 @@
|
||||
fun foo(param: Int, i: Int = 123): String {
|
||||
val x = "a${i}4_"
|
||||
val y = "-4${i}a"
|
||||
val z = "+1243a"
|
||||
val u = 123
|
||||
return "ab${i}def"
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
fun foo(param: Int): String {
|
||||
val x = "atrue123"
|
||||
val x = "aTRUE123"
|
||||
val z = true
|
||||
return "ab<selection>true</selection>def"
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
fun foo(param: Int, b: Boolean = true): String {
|
||||
val x = "a${b}123"
|
||||
val x = "aTRUE123"
|
||||
val z = true
|
||||
return "ab${b}def"
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
fun foo(a: Int): String {
|
||||
val x = "abc$a"
|
||||
val y = "abc${a}"
|
||||
val z = "abc{$a}def"
|
||||
return "<selection>abc$a</selection>" + "def"
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
fun foo(a: Int, s: String = "abc$a"): String {
|
||||
val x = s
|
||||
val y = s
|
||||
val z = "abc{$a}def"
|
||||
return s + "def"
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
fun foo(a: Int): String {
|
||||
val x = "-${a + 1}"
|
||||
val y = "x${a + 1}y"
|
||||
val z = "x${a - 1}y"
|
||||
return "abc<selection>${a + 1}</selection>def"
|
||||
}
|
||||
Vendored
+6
@@ -0,0 +1,6 @@
|
||||
fun foo(a: Int, i: Int = a + 1): String {
|
||||
val x = "-$i"
|
||||
val y = "x${i}y"
|
||||
val z = "x${a - 1}y"
|
||||
return "abc${i}def"
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
fun foo(a: Int): String {
|
||||
val x = "-$a"
|
||||
val y = "x${a}y"
|
||||
val z = "x$ay"
|
||||
return "abc<selection>${a}</selection>def"
|
||||
}
|
||||
Vendored
+6
@@ -0,0 +1,6 @@
|
||||
fun foo(a: Int, i: Int = a): String {
|
||||
val x = "-$i"
|
||||
val y = "x${i}y"
|
||||
val z = "x$ay"
|
||||
return "abc${i}def"
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
fun foo(a: Int): String {
|
||||
val x = "+cd$a:${a + 1}efg"
|
||||
val y = "+cd$a${a + 1}efg"
|
||||
return "ab<selection>cd$a:${a + 1}ef</selection>"
|
||||
}
|
||||
Vendored
+5
@@ -0,0 +1,5 @@
|
||||
fun foo(a: Int, s: String = "cd$a:${a + 1}ef"): String {
|
||||
val x = "+${s}g"
|
||||
val y = "+cd$a${a + 1}efg"
|
||||
return "ab$s"
|
||||
}
|
||||
Vendored
+5
@@ -0,0 +1,5 @@
|
||||
fun foo(a: Int): String {
|
||||
val x = "_c$a:${a + 1}d_"
|
||||
val y = "_$a:${a + 1}d_"
|
||||
return "ab<selection>c$a:${a + 1}d</selection>ef"
|
||||
}
|
||||
Vendored
+5
@@ -0,0 +1,5 @@
|
||||
fun foo(a: Int, s: String = "c$a:${a + 1}d"): String {
|
||||
val x = "_${s}_"
|
||||
val y = "_$a:${a + 1}d_"
|
||||
return "ab${s}ef"
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
fun foo(a: Int): String {
|
||||
val x = "_ab$a:${a + 1}cd__"
|
||||
val y = "_a$a:${a + 1}cd__"
|
||||
return "<selection>ab$a:${a + 1}cd</selection>ef"
|
||||
}
|
||||
Vendored
+5
@@ -0,0 +1,5 @@
|
||||
fun foo(a: Int, s: String = "ab$a:${a + 1}cd"): String {
|
||||
val x = "_${s}__"
|
||||
val y = "_a$a:${a + 1}cd__"
|
||||
return "${s}ef"
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
fun foo(a: Int): String {
|
||||
val x = """_c$a
|
||||
:${a + 1}d_"""
|
||||
val y = "_$a:${a + 1}d_"
|
||||
val z = """_c$a:${a + 1}d_"""
|
||||
val u = "_c$a\n:${a + 1}d_"
|
||||
return """ab<selection>c$a
|
||||
:${a + 1}d</selection>ef"""
|
||||
}
|
||||
Vendored
+8
@@ -0,0 +1,8 @@
|
||||
fun foo(a: Int, s: String = """c$a
|
||||
:${a + 1}d"""): String {
|
||||
val x = """_${s}_"""
|
||||
val y = "_$a:${a + 1}d_"
|
||||
val z = """_c$a:${a + 1}d_"""
|
||||
val u = "_c$a\n:${a + 1}d_"
|
||||
return """ab${s}ef"""
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
fun foo(a: Int): String {
|
||||
val x = "xabc$a"
|
||||
val y = "${a}abcx"
|
||||
val z = "xacb$a"
|
||||
return "<selection>abc</selection>def"
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
fun foo(a: Int, s: String = "abc"): String {
|
||||
val x = "x$s$a"
|
||||
val y = "${a}${s}x"
|
||||
val z = "xacb$a"
|
||||
return "${s}def"
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
fun foo(a: Int): String {
|
||||
val x = "xcd$a"
|
||||
val y = "${a}cdx"
|
||||
val z = "xcf$a"
|
||||
return "ab<selection>cd</selection>ef"
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
fun foo(a: Int, s: String = "cd"): String {
|
||||
val x = "x$s$a"
|
||||
val y = "${a}${s}x"
|
||||
val z = "xcf$a"
|
||||
return "ab${s}ef"
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
fun foo(a: Int): String {
|
||||
val x = "xdef$a"
|
||||
val y = "${a}defx"
|
||||
val z = "xddf$a"
|
||||
return "abc<selection>def</selection>"
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
fun foo(a: Int, s: String = "def"): String {
|
||||
val x = "x$s$a"
|
||||
val y = "${a}${s}x"
|
||||
val z = "xddf$a"
|
||||
return "abc$s"
|
||||
}
|
||||
+105
@@ -3294,6 +3294,111 @@ public class ExtractionTestGenerated extends AbstractExtractionTest {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceParameter/while.kt");
|
||||
doIntroduceSimpleParameterTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/refactoring/introduceParameter/stringTemplates")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class StringTemplates extends AbstractExtractionTest {
|
||||
public void testAllFilesPresentInStringTemplates() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/refactoring/introduceParameter/stringTemplates"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("brokenEntryWithBlockExpr.kt")
|
||||
public void testBrokenEntryWithBlockExpr() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceParameter/stringTemplates/brokenEntryWithBlockExpr.kt");
|
||||
doIntroduceSimpleParameterTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("brokenEntryWithExpr.kt")
|
||||
public void testBrokenEntryWithExpr() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceParameter/stringTemplates/brokenEntryWithExpr.kt");
|
||||
doIntroduceSimpleParameterTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("brokenEscapeEntry.kt")
|
||||
public void testBrokenEscapeEntry() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceParameter/stringTemplates/brokenEscapeEntry.kt");
|
||||
doIntroduceSimpleParameterTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("extractFalse.kt")
|
||||
public void testExtractFalse() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceParameter/stringTemplates/extractFalse.kt");
|
||||
doIntroduceSimpleParameterTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("extractIntegerLiteral.kt")
|
||||
public void testExtractIntegerLiteral() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceParameter/stringTemplates/extractIntegerLiteral.kt");
|
||||
doIntroduceSimpleParameterTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("extractTrue.kt")
|
||||
public void testExtractTrue() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceParameter/stringTemplates/extractTrue.kt");
|
||||
doIntroduceSimpleParameterTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("fullContent.kt")
|
||||
public void testFullContent() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceParameter/stringTemplates/fullContent.kt");
|
||||
doIntroduceSimpleParameterTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("fullEntryWithBlockExpr.kt")
|
||||
public void testFullEntryWithBlockExpr() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceParameter/stringTemplates/fullEntryWithBlockExpr.kt");
|
||||
doIntroduceSimpleParameterTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("fullEntryWithSimpleName.kt")
|
||||
public void testFullEntryWithSimpleName() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceParameter/stringTemplates/fullEntryWithSimpleName.kt");
|
||||
doIntroduceSimpleParameterTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("multipleEntriesWithPrefix.kt")
|
||||
public void testMultipleEntriesWithPrefix() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceParameter/stringTemplates/multipleEntriesWithPrefix.kt");
|
||||
doIntroduceSimpleParameterTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("multipleEntriesWithSubstring.kt")
|
||||
public void testMultipleEntriesWithSubstring() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceParameter/stringTemplates/multipleEntriesWithSubstring.kt");
|
||||
doIntroduceSimpleParameterTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("multipleEntriesWithSuffix.kt")
|
||||
public void testMultipleEntriesWithSuffix() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceParameter/stringTemplates/multipleEntriesWithSuffix.kt");
|
||||
doIntroduceSimpleParameterTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("rawTemplateWithSubstring.kt")
|
||||
public void testRawTemplateWithSubstring() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceParameter/stringTemplates/rawTemplateWithSubstring.kt");
|
||||
doIntroduceSimpleParameterTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("singleEntryPrefix.kt")
|
||||
public void testSingleEntryPrefix() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceParameter/stringTemplates/singleEntryPrefix.kt");
|
||||
doIntroduceSimpleParameterTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("singleEntrySubstring.kt")
|
||||
public void testSingleEntrySubstring() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceParameter/stringTemplates/singleEntrySubstring.kt");
|
||||
doIntroduceSimpleParameterTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("singleEntrySuffix.kt")
|
||||
public void testSingleEntrySuffix() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceParameter/stringTemplates/singleEntrySuffix.kt");
|
||||
doIntroduceSimpleParameterTest(fileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/refactoring/introduceLambdaParameter")
|
||||
|
||||
Reference in New Issue
Block a user