[kotlin] Add "Introduce Constant" refactoring

#KTIJ-4841 Fixed

GitOrigin-RevId: d81aba8e9c2d4efca10779cac30389db415a1cf0
This commit is contained in:
XiangRongLin
2021-09-16 14:03:31 +00:00
committed by intellij-monorepo-bot
parent 8c40d37800
commit 0928661ce5
96 changed files with 1070 additions and 4 deletions
@@ -840,6 +840,7 @@ private fun assembleWorkspace(): TWorkspace = workspace {
model("refactoring/introduceJavaParameter", pattern = JAVA, testMethodName = "doIntroduceJavaParameterTest")
model("refactoring/introduceTypeParameter", pattern = KT_OR_KTS, testMethodName = "doIntroduceTypeParameterTest")
model("refactoring/introduceTypeAlias", pattern = KT_OR_KTS, testMethodName = "doIntroduceTypeAliasTest")
model("refactoring/introduceConstant", pattern = KT_OR_KTS, testMethodName = "doIntroduceConstantTest")
model("refactoring/extractSuperclass", pattern = KT_OR_KTS_WITHOUT_DOTS, testMethodName = "doExtractSuperclassTest")
model("refactoring/extractInterface", pattern = KT_OR_KTS_WITHOUT_DOTS, testMethodName = "doExtractInterfaceTest")
}
@@ -769,6 +769,8 @@ error.text.can.t.copy.class.to.the.containing.file=Can't copy class to the conta
error.text.can.t.generate.0.1=Can''t generate {0}: {1}
error.text.can.t.introduce.lambda.parameter.for.this.expression=Can't introduce lambda parameter for this expression
error.text.can.t.introduce.property.for.this.expression=Can't introduce property for this expression
error.text.can.t.introduce.constant.for.this.expression.because.not.constant=Can't introduce constant for non constant expression
error.text.can.t.introduce.constant.for.this.expression=Can't introduce constant for this expression
error.text.different.name.expected=Different name expected
error.text.expression.has.no.type=Expression has no type
error.text.extraction.from.expect.class.is.not.yet.supported=Extraction from expect class is not yet supported
@@ -799,6 +801,7 @@ introduce.property=Introduce Property
introduce.type.parameter.to.declaration=Introduce type parameter to declaration
introduce.type.parameter=Introduce Type Parameter
introduce.variable=Introduce Variable
introduce.constant=Introduce Constant
label.text.default.receiver.value=&Default receiver value:\u0020
label.text.destination.directory=&Destination directory:
label.text.file.name=File &name:
@@ -11,6 +11,7 @@ import org.jetbrains.kotlin.idea.refactoring.changeSignature.KotlinChangeSignatu
import org.jetbrains.kotlin.idea.refactoring.introduce.extractClass.KotlinExtractInterfaceHandler
import org.jetbrains.kotlin.idea.refactoring.introduce.extractClass.KotlinExtractSuperclassHandler
import org.jetbrains.kotlin.idea.refactoring.introduce.extractFunction.ExtractKotlinFunctionHandler
import org.jetbrains.kotlin.idea.refactoring.introduce.introduceConstant.KotlinIntroduceConstantHandler
import org.jetbrains.kotlin.idea.refactoring.introduce.introduceParameter.KotlinIntroduceLambdaParameterHandler
import org.jetbrains.kotlin.idea.refactoring.introduce.introduceParameter.KotlinIntroduceParameterHandler
import org.jetbrains.kotlin.idea.refactoring.introduce.introduceProperty.KotlinIntroducePropertyHandler
@@ -31,6 +32,8 @@ class KotlinRefactoringSupportProvider : RefactoringSupportProvider() {
override fun getIntroduceFunctionalParameterHandler() = KotlinIntroduceLambdaParameterHandler()
override fun getIntroduceConstantHandler() = KotlinIntroduceConstantHandler()
fun getIntroducePropertyHandler(): RefactoringActionHandler = KotlinIntroducePropertyHandler()
fun getIntroduceTypeParameterHandler(): RefactoringActionHandler = KotlinIntroduceTypeParameterHandler
@@ -462,7 +462,8 @@ data class ExtractionGeneratorOptions(
val target: ExtractionTarget = ExtractionTarget.FUNCTION,
val dummyName: String? = null,
val allowExpressionBody: Boolean = true,
val delayInitialOccurrenceReplacement: Boolean = false
val delayInitialOccurrenceReplacement: Boolean = false,
val isConst: Boolean = false
) {
companion object {
@JvmField
@@ -13,8 +13,8 @@ import com.intellij.refactoring.BaseRefactoringProcessor
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.builtins.isFunctionType
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.idea.KotlinBundle
import org.jetbrains.kotlin.idea.core.*
import org.jetbrains.kotlin.idea.core.util.isMultiLine
@@ -34,7 +34,6 @@ import org.jetbrains.kotlin.idea.util.getResolutionScope
import org.jetbrains.kotlin.idea.util.psi.patternMatching.*
import org.jetbrains.kotlin.idea.util.psi.patternMatching.UnificationResult.StronglyMatched
import org.jetbrains.kotlin.idea.util.psi.patternMatching.UnificationResult.WeaklyMatched
import org.jetbrains.kotlin.idea.util.reformatted
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.*
@@ -75,7 +74,8 @@ private fun buildSignature(config: ExtractionGeneratorConfiguration, renderer: D
config.descriptor.annotations.joinToString(separator = "\n", postfix = "\n") { renderer.renderAnnotation(it) }
}
val extraModifiers = config.descriptor.modifiers.map { it.value } +
listOfNotNull(if (shouldBeInline) KtTokens.INLINE_KEYWORD.value else null)
listOfNotNull(if (shouldBeInline) KtTokens.INLINE_KEYWORD.value else null) +
listOfNotNull(if (config.generatorOptions.isConst) KtTokens.CONST_KEYWORD.value else null)
val modifiers = if (visibility.isNotEmpty()) listOf(visibility) + extraModifiers else extraModifiers
modifier(annotations + modifiers.joinToString(separator = " "))
@@ -0,0 +1,14 @@
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.kotlin.idea.refactoring.introduce.introduceConstant
import com.intellij.lang.refactoring.RefactoringSupportProvider
import com.intellij.refactoring.RefactoringActionHandler
import org.jetbrains.kotlin.idea.refactoring.KotlinRefactoringSupportProvider
import org.jetbrains.kotlin.idea.refactoring.introduce.AbstractIntroduceAction
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
class IntroduceConstantAction : AbstractIntroduceAction() {
override fun getRefactoringHandler(provider: RefactoringSupportProvider): RefactoringActionHandler? =
provider.safeAs<KotlinRefactoringSupportProvider>()?.introduceConstantHandler
}
@@ -0,0 +1,174 @@
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.kotlin.idea.refactoring.introduce.introduceConstant
import com.intellij.openapi.actionSystem.DataContext
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiDocumentManager
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.codeStyle.NameUtil
import com.intellij.refactoring.RefactoringActionHandler
import org.jetbrains.annotations.Nls
import org.jetbrains.kotlin.idea.KotlinBundle
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.core.util.CodeInsightUtils
import org.jetbrains.kotlin.idea.refactoring.getExtractionContainers
import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.*
import org.jetbrains.kotlin.idea.refactoring.introduce.introduceProperty.KotlinInplacePropertyIntroducer
import org.jetbrains.kotlin.idea.refactoring.introduce.selectElementsWithTargetSibling
import org.jetbrains.kotlin.idea.refactoring.introduce.showErrorHint
import org.jetbrains.kotlin.idea.refactoring.introduce.showErrorHintByKey
import org.jetbrains.kotlin.idea.refactoring.introduce.validateExpressionElements
import org.jetbrains.kotlin.idea.util.application.isUnitTestMode
import org.jetbrains.kotlin.idea.util.psi.patternMatching.toRange
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.plainContent
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
class KotlinIntroduceConstantHandler(
val helper: ExtractionEngineHelper = InteractiveExtractionHelper
) : RefactoringActionHandler {
object InteractiveExtractionHelper : ExtractionEngineHelper(INTRODUCE_CONSTANT) {
private fun getExtractionTarget(descriptor: ExtractableCodeDescriptor) =
propertyTargets.firstOrNull { it.isAvailable(descriptor) }
override fun validate(descriptor: ExtractableCodeDescriptor) =
descriptor.validate(getExtractionTarget(descriptor) ?: ExtractionTarget.FUNCTION)
override fun configureAndRun(
project: Project,
editor: Editor,
descriptorWithConflicts: ExtractableCodeDescriptorWithConflicts,
onFinish: (ExtractionResult) -> Unit
) {
val descriptor = descriptorWithConflicts.descriptor
val target = getExtractionTarget(descriptor)
if (target != null) {
val options = ExtractionGeneratorOptions(target = target, delayInitialOccurrenceReplacement = true, isConst = true)
doRefactor(ExtractionGeneratorConfiguration(descriptor, options), onFinish)
} else {
showErrorHint(
project,
editor,
KotlinBundle.message("error.text.can.t.introduce.constant.for.this.expression"),
INTRODUCE_CONSTANT
)
}
}
}
fun doInvoke(project: Project, editor: Editor, file: KtFile, elements: List<PsiElement>, target: PsiElement) {
val adjustedElements = (elements.singleOrNull() as? KtBlockExpression)?.statements ?: elements
when {
adjustedElements.isEmpty() -> {
showErrorHintByKey(
project, editor, "cannot.refactor.no.expression",
INTRODUCE_CONSTANT
)
}
else -> {
val options = ExtractionOptions(extractAsProperty = true)
val extractionData = ExtractionData(file, adjustedElements.toRange(), target, null, options)
ExtractionEngine(helper).run(editor, extractionData) {
val property = it.declaration as KtProperty
val descriptor = it.config.descriptor
editor.caretModel.moveToOffset(property.textOffset)
editor.selectionModel.removeSelection()
if (editor.settings.isVariableInplaceRenameEnabled && !isUnitTestMode()) {
with(PsiDocumentManager.getInstance(project)) {
commitDocument(editor.document)
doPostponedOperationsAndUnblockDocument(editor.document)
}
val introducer = KotlinInplacePropertyIntroducer(
property = property,
editor = editor,
project = project,
title = INTRODUCE_CONSTANT,
doNotChangeVar = false,
exprType = descriptor.returnType,
extractionResult = it,
availableTargets = listOf(ExtractionTarget.PROPERTY_WITH_GETTER)
)
introducer.performInplaceRefactoring(LinkedHashSet(getNameSuggestions(property) + descriptor.suggestedNames))
} else {
processDuplicatesSilently(it.duplicateReplacers, project)
}
}
}
}
}
private fun getNameSuggestions(property: KtProperty): List<String> {
val initializerValue = property.initializer.safeAs<KtStringTemplateExpression>()?.plainContent
val identifierValue = property.identifyingElement?.text
return listOfNotNull(initializerValue, identifierValue).map { NameUtil.capitalizeAndUnderscore(it) }
}
override fun invoke(project: Project, editor: Editor, file: PsiFile, dataContext: DataContext?) {
if (file !is KtFile) return
selectElements(editor, file) { elements, targets -> doInvoke(project, editor, file, elements, targets) }
}
fun selectElements(
editor: Editor,
file: KtFile,
continuation: (elements: List<PsiElement>, targets: PsiElement) -> Unit
) {
selectElementsWithTargetSibling(
INTRODUCE_CONSTANT,
editor,
file,
KotlinBundle.message("title.select.target.code.block"),
listOf(CodeInsightUtils.ElementKind.EXPRESSION),
::validateElements,
{ _, sibling ->
sibling.getExtractionContainers(strict = true, includeAll = true)
.filter { (it is KtFile && !it.isScript()) }
},
continuation
)
}
private fun validateElements(elements: List<PsiElement>): String? {
val errorMessage = validateExpressionElements(elements)
return when {
errorMessage != null -> errorMessage
elements.any {
// unchecked cast always succeeds because only expressions are selected in selectElements
(it as KtExpression).isNotConst()
} -> KotlinBundle.message(
"error.text.can.t.introduce.constant.for.this.expression.because.not.constant"
)
else -> null
}
}
private fun KtExpression.isNotConst(): Boolean {
when (this) {
// Handle these two expressions separately because in case of selecting part of a string
// a temp file will be created in which the analysis fails
is KtConstantExpression -> return false
is KtStringTemplateExpression -> return this.hasInterpolation()
else -> {
val constInfo = ConstantExpressionEvaluator.getConstant(this, analyze(BodyResolveMode.PARTIAL))
return constInfo == null || constInfo.usesNonConstValAsConstant || constInfo.usesVariableAsConstant
}
}
}
override fun invoke(project: Project, elements: Array<out PsiElement>, dataContext: DataContext?) {
throw AssertionError("$INTRODUCE_CONSTANT can only be invoked from editor")
}
}
val INTRODUCE_CONSTANT: String
@Nls
get() = KotlinBundle.message("introduce.constant")
@@ -37,6 +37,8 @@ import org.jetbrains.kotlin.idea.refactoring.introduce.extractClass.ExtractSuper
import org.jetbrains.kotlin.idea.refactoring.introduce.extractFunction.EXTRACT_FUNCTION
import org.jetbrains.kotlin.idea.refactoring.introduce.extractFunction.ExtractKotlinFunctionHandler
import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.*
import org.jetbrains.kotlin.idea.refactoring.introduce.introduceConstant.INTRODUCE_CONSTANT
import org.jetbrains.kotlin.idea.refactoring.introduce.introduceConstant.KotlinIntroduceConstantHandler
import org.jetbrains.kotlin.idea.refactoring.introduce.introduceParameter.*
import org.jetbrains.kotlin.idea.refactoring.introduce.introduceProperty.INTRODUCE_PROPERTY
import org.jetbrains.kotlin.idea.refactoring.introduce.introduceProperty.KotlinIntroducePropertyHandler
@@ -281,6 +283,39 @@ abstract class AbstractExtractionTest : KotlinLightCodeInsightFixtureTestCase()
}
}
protected fun doIntroduceConstantTest(unused: String) {
doTest { file ->
file as KtFile
val extractionTarget = propertyTargets.single {
it.targetName == InTextDirectivesUtils.findStringWithPrefixes(file.getText(), "// EXTRACTION_TARGET: ")
}
val helper = object : ExtractionEngineHelper(INTRODUCE_CONSTANT) {
override fun configureAndRun(
project: Project,
editor: Editor,
descriptorWithConflicts: ExtractableCodeDescriptorWithConflicts,
onFinish: (ExtractionResult) -> Unit
) {
doRefactor(
ExtractionGeneratorConfiguration(
descriptorWithConflicts.descriptor,
ExtractionGeneratorOptions(target = extractionTarget, delayInitialOccurrenceReplacement = true, isConst = true)
),
onFinish
)
}
}
val handler = KotlinIntroduceConstantHandler(helper)
val editor = fixture.editor
handler.selectElements(editor, file) { elements, target ->
handler.doInvoke(project, editor, file, elements, target)
}
}
}
protected fun doExtractSuperTest(unused: String, isInterface: Boolean) {
doTest(true) { file ->
file as KtFile
@@ -3696,6 +3696,253 @@ public abstract class ExtractionTestGenerated extends AbstractExtractionTest {
}
}
@RunWith(JUnit3RunnerWithInners.class)
@TestMetadata("testData/refactoring/introduceConstant")
public static class IntroduceConstant extends AbstractExtractionTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doIntroduceConstantTest, this, testDataFilePath);
}
@TestMetadata("extractInteger.kt")
public void testExtractInteger() throws Exception {
runTest("testData/refactoring/introduceConstant/extractInteger.kt");
}
@TestMetadata("extractMultipleExpressions.kt")
public void testExtractMultipleExpressions() throws Exception {
runTest("testData/refactoring/introduceConstant/extractMultipleExpressions.kt");
}
@TestMetadata("extractPartNonConstVal.kt")
public void testExtractPartNonConstVal() throws Exception {
runTest("testData/refactoring/introduceConstant/extractPartNonConstVal.kt");
}
@TestMetadata("extractWithNameClash.kt")
public void testExtractWithNameClash() throws Exception {
runTest("testData/refactoring/introduceConstant/extractWithNameClash.kt");
}
@RunWith(JUnit3RunnerWithInners.class)
@TestMetadata("testData/refactoring/introduceConstant/binaryExpression")
public static class BinaryExpression extends AbstractExtractionTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doIntroduceConstantTest, this, testDataFilePath);
}
@TestMetadata("integerCompareTo.kt")
public void testIntegerCompareTo() throws Exception {
runTest("testData/refactoring/introduceConstant/binaryExpression/integerCompareTo.kt");
}
@TestMetadata("integerDec.kt")
public void testIntegerDec() throws Exception {
runTest("testData/refactoring/introduceConstant/binaryExpression/integerDec.kt");
}
@TestMetadata("integerDiv.kt")
public void testIntegerDiv() throws Exception {
runTest("testData/refactoring/introduceConstant/binaryExpression/integerDiv.kt");
}
@TestMetadata("integerEquals.kt")
public void testIntegerEquals() throws Exception {
runTest("testData/refactoring/introduceConstant/binaryExpression/integerEquals.kt");
}
@TestMetadata("integerInc.kt")
public void testIntegerInc() throws Exception {
runTest("testData/refactoring/introduceConstant/binaryExpression/integerInc.kt");
}
@TestMetadata("integerMinus.kt")
public void testIntegerMinus() throws Exception {
runTest("testData/refactoring/introduceConstant/binaryExpression/integerMinus.kt");
}
@TestMetadata("integerPlus.kt")
public void testIntegerPlus() throws Exception {
runTest("testData/refactoring/introduceConstant/binaryExpression/integerPlus.kt");
}
@TestMetadata("integerPlusPartNotConst.kt")
public void testIntegerPlusPartNotConst() throws Exception {
runTest("testData/refactoring/introduceConstant/binaryExpression/integerPlusPartNotConst.kt");
}
@TestMetadata("integerRem.kt")
public void testIntegerRem() throws Exception {
runTest("testData/refactoring/introduceConstant/binaryExpression/integerRem.kt");
}
@TestMetadata("integerTimes.kt")
public void testIntegerTimes() throws Exception {
runTest("testData/refactoring/introduceConstant/binaryExpression/integerTimes.kt");
}
@TestMetadata("integerUnaryMinus.kt")
public void testIntegerUnaryMinus() throws Exception {
runTest("testData/refactoring/introduceConstant/binaryExpression/integerUnaryMinus.kt");
}
@TestMetadata("integerUnaryPlus.kt")
public void testIntegerUnaryPlus() throws Exception {
runTest("testData/refactoring/introduceConstant/binaryExpression/integerUnaryPlus.kt");
}
@TestMetadata("paranthesis.kt")
public void testParanthesis() throws Exception {
runTest("testData/refactoring/introduceConstant/binaryExpression/paranthesis.kt");
}
@TestMetadata("paranthesisBroken.kt")
public void testParanthesisBroken() throws Exception {
runTest("testData/refactoring/introduceConstant/binaryExpression/paranthesisBroken.kt");
}
@TestMetadata("stringCompareTo.kt")
public void testStringCompareTo() throws Exception {
runTest("testData/refactoring/introduceConstant/binaryExpression/stringCompareTo.kt");
}
@TestMetadata("stringCustomOperator.kt")
public void testStringCustomOperator() throws Exception {
runTest("testData/refactoring/introduceConstant/binaryExpression/stringCustomOperator.kt");
}
@TestMetadata("stringEquals.kt")
public void testStringEquals() throws Exception {
runTest("testData/refactoring/introduceConstant/binaryExpression/stringEquals.kt");
}
@TestMetadata("stringPlus.kt")
public void testStringPlus() throws Exception {
runTest("testData/refactoring/introduceConstant/binaryExpression/stringPlus.kt");
}
@TestMetadata("stringPlusPartNotConst.kt")
public void testStringPlusPartNotConst() throws Exception {
runTest("testData/refactoring/introduceConstant/binaryExpression/stringPlusPartNotConst.kt");
}
}
@RunWith(JUnit3RunnerWithInners.class)
@TestMetadata("testData/refactoring/introduceConstant/dotQualifiedExpression")
public static class DotQualifiedExpression extends AbstractExtractionTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doIntroduceConstantTest, this, testDataFilePath);
}
@TestMetadata("and.kt")
public void testAnd() throws Exception {
runTest("testData/refactoring/introduceConstant/dotQualifiedExpression/and.kt");
}
@TestMetadata("inv.kt")
public void testInv() throws Exception {
runTest("testData/refactoring/introduceConstant/dotQualifiedExpression/inv.kt");
}
@TestMetadata("or.kt")
public void testOr() throws Exception {
runTest("testData/refactoring/introduceConstant/dotQualifiedExpression/or.kt");
}
@TestMetadata("plus.kt")
public void testPlus() throws Exception {
runTest("testData/refactoring/introduceConstant/dotQualifiedExpression/plus.kt");
}
@TestMetadata("shl.kt")
public void testShl() throws Exception {
runTest("testData/refactoring/introduceConstant/dotQualifiedExpression/shl.kt");
}
@TestMetadata("shr.kt")
public void testShr() throws Exception {
runTest("testData/refactoring/introduceConstant/dotQualifiedExpression/shr.kt");
}
@TestMetadata("toByte.kt")
public void testToByte() throws Exception {
runTest("testData/refactoring/introduceConstant/dotQualifiedExpression/toByte.kt");
}
@TestMetadata("toFloat.kt")
public void testToFloat() throws Exception {
runTest("testData/refactoring/introduceConstant/dotQualifiedExpression/toFloat.kt");
}
@TestMetadata("ushr.kt")
public void testUshr() throws Exception {
runTest("testData/refactoring/introduceConstant/dotQualifiedExpression/ushr.kt");
}
@TestMetadata("xor.kt")
public void testXor() throws Exception {
runTest("testData/refactoring/introduceConstant/dotQualifiedExpression/xor.kt");
}
}
@RunWith(JUnit3RunnerWithInners.class)
@TestMetadata("testData/refactoring/introduceConstant/stringTemplates")
public static class StringTemplates extends AbstractExtractionTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doIntroduceConstantTest, this, testDataFilePath);
}
@TestMetadata("brokenEntryWithBlockExpr.kt")
public void testBrokenEntryWithBlockExpr() throws Exception {
runTest("testData/refactoring/introduceConstant/stringTemplates/brokenEntryWithBlockExpr.kt");
}
@TestMetadata("brokenEntryWithExpr.kt")
public void testBrokenEntryWithExpr() throws Exception {
runTest("testData/refactoring/introduceConstant/stringTemplates/brokenEntryWithExpr.kt");
}
@TestMetadata("brokenEscapeEntry.kt")
public void testBrokenEscapeEntry() throws Exception {
runTest("testData/refactoring/introduceConstant/stringTemplates/brokenEscapeEntry.kt");
}
@TestMetadata("extractFalse.kt")
public void testExtractFalse() throws Exception {
runTest("testData/refactoring/introduceConstant/stringTemplates/extractFalse.kt");
}
@TestMetadata("extractIntegerLiteral.kt")
public void testExtractIntegerLiteral() throws Exception {
runTest("testData/refactoring/introduceConstant/stringTemplates/extractIntegerLiteral.kt");
}
@TestMetadata("extractTrue.kt")
public void testExtractTrue() throws Exception {
runTest("testData/refactoring/introduceConstant/stringTemplates/extractTrue.kt");
}
@TestMetadata("partTemplate.kt")
public void testPartTemplate() throws Exception {
runTest("testData/refactoring/introduceConstant/stringTemplates/partTemplate.kt");
}
@TestMetadata("singleEntryPrefix.kt")
public void testSingleEntryPrefix() throws Exception {
runTest("testData/refactoring/introduceConstant/stringTemplates/singleEntryPrefix.kt");
}
@TestMetadata("singleEntrySubstring.kt")
public void testSingleEntrySubstring() throws Exception {
runTest("testData/refactoring/introduceConstant/stringTemplates/singleEntrySubstring.kt");
}
@TestMetadata("singleEntrySuffix.kt")
public void testSingleEntrySuffix() throws Exception {
runTest("testData/refactoring/introduceConstant/stringTemplates/singleEntrySuffix.kt");
}
}
}
@RunWith(JUnit3RunnerWithInners.class)
@TestMetadata("testData/refactoring/extractSuperclass")
public static class ExtractSuperclass extends AbstractExtractionTest {
@@ -0,0 +1,6 @@
// EXTRACTION_TARGET: property with initializer
val a = 1
fun foo(): Boolean {
return <selection>1>1</selection>
}
@@ -0,0 +1,8 @@
// EXTRACTION_TARGET: property with initializer
val a = 1
private const val b = 1 > 1
fun foo(): Boolean {
return b
}
@@ -0,0 +1,6 @@
// EXTRACTION_TARGET: property with initializer
val a = 1
fun foo(): Int {
return <selection>1--</selection>
}
@@ -0,0 +1 @@
Can't introduce constant for non constant expression
@@ -0,0 +1,6 @@
// EXTRACTION_TARGET: property with initializer
val a = 1
fun foo(): Int {
return <selection>1/1</selection>
}
@@ -0,0 +1,8 @@
// EXTRACTION_TARGET: property with initializer
val a = 1
private const val i = 1 / 1
fun foo(): Int {
return i
}
@@ -0,0 +1,6 @@
// EXTRACTION_TARGET: property with initializer
val a = 1
fun foo(): Boolean {
return <selection>1 == 1</selection>
}
@@ -0,0 +1,8 @@
// EXTRACTION_TARGET: property with initializer
val a = 1
private const val b = 1 == 1
fun foo(): Boolean {
return b
}
@@ -0,0 +1,6 @@
// EXTRACTION_TARGET: property with initializer
val a = 1
fun foo(): Int {
return <selection>1++</selection>
}
@@ -0,0 +1 @@
Can't introduce constant for non constant expression
@@ -0,0 +1,6 @@
// EXTRACTION_TARGET: property with initializer
val a = 1
fun foo(): Int {
return <selection>1-1</selection>
}
@@ -0,0 +1,8 @@
// EXTRACTION_TARGET: property with initializer
val a = 1
private const val i = 1 - 1
fun foo(): Int {
return i
}
@@ -0,0 +1,7 @@
// EXTRACTION_TARGET: property with initializer
val a = 1
fun foo(): Int {
val x = "1"+"1"
return <selection>1+1</selection>
}
@@ -0,0 +1,9 @@
// EXTRACTION_TARGET: property with initializer
val a = 1
private const val i = 1 + 1
fun foo(): Int {
val x = "1"+"1"
return i
}
@@ -0,0 +1,10 @@
// EXTRACTION_TARGET: property with initializer
val a = 1
fun foo(): Int {
return <selection>1+getNumber()</selection>
}
fun getNumber(): Int {
return 1
}
@@ -0,0 +1 @@
Can't introduce constant for non constant expression
@@ -0,0 +1,6 @@
// EXTRACTION_TARGET: property with initializer
val a = 1
fun foo(): Int {
return <selection>1%1</selection>
}
@@ -0,0 +1,8 @@
// EXTRACTION_TARGET: property with initializer
val a = 1
private const val i = 1 % 1
fun foo(): Int {
return i
}
@@ -0,0 +1,6 @@
// EXTRACTION_TARGET: property with initializer
val a = 1
fun foo(): Int {
return <selection>1*1</selection>
}
@@ -0,0 +1,8 @@
// EXTRACTION_TARGET: property with initializer
val a = 1
private const val i = 1 * 1
fun foo(): Int {
return i
}
@@ -0,0 +1,6 @@
// EXTRACTION_TARGET: property with initializer
val a = 1
fun foo(): Int {
return <selection>-1</selection>
}
@@ -0,0 +1,8 @@
// EXTRACTION_TARGET: property with initializer
val a = 1
private const val i = -1
fun foo(): Int {
return i
}
@@ -0,0 +1,6 @@
// EXTRACTION_TARGET: property with initializer
val a = 1
fun foo(): Int {
return <selection>+1</selection>
}
@@ -0,0 +1,8 @@
// EXTRACTION_TARGET: property with initializer
val a = 1
private const val i = +1
fun foo(): Int {
return i
}
@@ -0,0 +1,6 @@
// EXTRACTION_TARGET: property with initializer
val a = 1
fun foo(): Int {
return <selection>(1+1)*2</selection>
}
@@ -0,0 +1,8 @@
// EXTRACTION_TARGET: property with initializer
val a = 1
private const val i = (1 + 1) * 2
fun foo(): Int {
return i
}
@@ -0,0 +1,6 @@
// EXTRACTION_TARGET: property with initializer
val a = 1
fun foo(): Int {
return (1+<selection>1)*2</selection>
}
@@ -0,0 +1 @@
Cannot perform refactoring without an expression
@@ -0,0 +1,6 @@
// EXTRACTION_TARGET: property with initializer
val a = 1
fun foo(): String {
return <selection>"1">"1"</selection>
}
@@ -0,0 +1,8 @@
// EXTRACTION_TARGET: property with initializer
val a = 1
private const val b = "1" > "1"
fun foo(): String {
return b
}
@@ -0,0 +1,7 @@
// EXTRACTION_TARGET: property with initializer
fun foo(): String {
return <selection>"1"-1</selection>
}
operator fun String.minus(i: Int): String = this.substring(0, length - i)
@@ -0,0 +1 @@
Can't introduce constant for non constant expression
@@ -0,0 +1,6 @@
// EXTRACTION_TARGET: property with initializer
val a = 1
fun foo(): String {
return <selection>"1" == "1"</selection>
}
@@ -0,0 +1,8 @@
// EXTRACTION_TARGET: property with initializer
val a = 1
private const val b = "1" == "1"
fun foo(): String {
return b
}
@@ -0,0 +1,7 @@
// EXTRACTION_TARGET: property with initializer
val a = 1
fun foo(): String {
val x = 1 + 1
return <selection>"1"+"1"</selection>
}
@@ -0,0 +1,9 @@
// EXTRACTION_TARGET: property with initializer
val a = 1
private const val s = "1" + "1"
fun foo(): String {
val x = 1 + 1
return s
}
@@ -0,0 +1,10 @@
// EXTRACTION_TARGET: property with initializer
val a = 1
fun foo(): String {
return <selection>"1"+getString()</selection>
}
fun getString(): String {
return "1"
}
@@ -0,0 +1 @@
Can't introduce constant for non constant expression
@@ -0,0 +1,6 @@
// EXTRACTION_TARGET: property with initializer
val a = 1
fun foo(): Int {
return <selection>1.and(1)</selection>
}
@@ -0,0 +1,8 @@
// EXTRACTION_TARGET: property with initializer
val a = 1
private const val i = 1.and(1)
fun foo(): Int {
return i
}
@@ -0,0 +1,6 @@
// EXTRACTION_TARGET: property with initializer
val a = 1
fun foo(): Int {
return <selection>1.inv(1)</selection>
}
@@ -0,0 +1,8 @@
// EXTRACTION_TARGET: property with initializer
val a = 1
private const val i = 1.inv(1)
fun foo(): Int {
return i
}
@@ -0,0 +1,6 @@
// EXTRACTION_TARGET: property with initializer
val a = 1
fun foo(): Int {
return <selection>1.or(1)</selection>
}
@@ -0,0 +1,8 @@
// EXTRACTION_TARGET: property with initializer
val a = 1
private const val i = 1.or(1)
fun foo(): Int {
return i
}
@@ -0,0 +1,6 @@
// EXTRACTION_TARGET: property with initializer
val a = 1
fun foo(): Int {
return <selection>1.plus(1)</selection>
}
@@ -0,0 +1,8 @@
// EXTRACTION_TARGET: property with initializer
val a = 1
private const val i = 1.plus(1)
fun foo(): Int {
return i
}
@@ -0,0 +1,6 @@
// EXTRACTION_TARGET: property with initializer
val a = 1
fun foo(): Int {
return <selection>1.shl(1)</selection>
}
@@ -0,0 +1,8 @@
// EXTRACTION_TARGET: property with initializer
val a = 1
private const val i = 1.shl(1)
fun foo(): Int {
return i
}
@@ -0,0 +1,6 @@
// EXTRACTION_TARGET: property with initializer
val a = 1
fun foo(): Int {
return <selection>1.shr(1)</selection>
}
@@ -0,0 +1,8 @@
// EXTRACTION_TARGET: property with initializer
val a = 1
private const val i = 1.shr(1)
fun foo(): Int {
return i
}
@@ -0,0 +1,7 @@
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
// EXTRACTION_TARGET: property with initializer
val a = 1
fun foo(): Byte {
return <selection>1.toByte()</selection>
}
@@ -0,0 +1,9 @@
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
// EXTRACTION_TARGET: property with initializer
val a = 1
private const val byte = 1.toByte()
fun foo(): Byte {
return byte
}
@@ -0,0 +1,6 @@
// EXTRACTION_TARGET: property with initializer
val a = 1
fun foo(): Int {
return <selection>1.toFloat()</selection>
}
@@ -0,0 +1,8 @@
// EXTRACTION_TARGET: property with initializer
val a = 1
private const val fl = 1.toFloat()
fun foo(): Int {
return fl
}
@@ -0,0 +1,6 @@
// EXTRACTION_TARGET: property with initializer
val a = 1
fun foo(): Int {
return <selection>1.ushr(1)</selection>
}
@@ -0,0 +1,8 @@
// EXTRACTION_TARGET: property with initializer
val a = 1
private const val i = 1.ushr(1)
fun foo(): Int {
return i
}
@@ -0,0 +1,6 @@
// EXTRACTION_TARGET: property with initializer
val a = 1
fun foo(): Int {
return <selection>1.xor(1)</selection>
}
@@ -0,0 +1,8 @@
// EXTRACTION_TARGET: property with initializer
val a = 1
private const val i = 1.xor(1)
fun foo(): Int {
return i
}
@@ -0,0 +1,9 @@
// EXTRACTION_TARGET: property with initializer
val a = 1
fun foo(): Int {
val x = 2 + 3
val y = 3 + 2 + 22
val z = "2"
return <selection>2</selection>
}
@@ -0,0 +1,11 @@
// EXTRACTION_TARGET: property with initializer
val a = 1
private const val i = 2
fun foo(): Int {
val x = i + 3
val y = 3 + i + 22
val z = "2"
return i
}
@@ -0,0 +1,10 @@
// EXTRACTION_TARGET: property with initializer
class A {
fun foo(): Int {
<selection>
val a = 1 + 2
val b = a * 2 </selection>
val c = b - 1
}
}
@@ -0,0 +1 @@
Can't introduce constant for non constant expression
@@ -0,0 +1,7 @@
// EXTRACTION_TARGET: property with initializer
val n: Int = 1
fun foo(): Int {
return <selection>n+1</selection>
}
@@ -0,0 +1 @@
Can't introduce constant for non constant expression
@@ -0,0 +1,7 @@
// EXTRACTION_TARGET: property with initializer
val i = 1
fun foo(): Int {
return <selection>2</selection>
}
@@ -0,0 +1,8 @@
// EXTRACTION_TARGET: property with initializer
val i = 1
private const val i1 = 2
fun foo(): Int {
return i1
}
@@ -0,0 +1,6 @@
// EXTRACTION_TARGET: property with initializer
val prop = 1
fun foo(): String {
return "<selection>abc${pr < / selection > op}def"
}
@@ -0,0 +1 @@
Cannot perform refactoring without an expression
@@ -0,0 +1,6 @@
// EXTRACTION_TARGET: property with initializer
val prop = 1
fun foo(): String {
return "<selection>abc$pr</selection>op"
}
@@ -0,0 +1 @@
Cannot perform refactoring without an expression
@@ -0,0 +1,6 @@
// EXTRACTION_TARGET: property with initializer
val a = 1
fun foo(): String {
return "<selection>abc$a\</selection>ndef"
}
@@ -0,0 +1 @@
Cannot perform refactoring without an expression
@@ -0,0 +1,9 @@
// EXTRACTION_TARGET: property with initializer
val a = 1
fun foo(): String {
val x = "xyfalsez"
val y = "xyFalsez"
val z = false
return "ab<selection>false</selection>def"
}
@@ -0,0 +1,11 @@
// EXTRACTION_TARGET: property with initializer
val a = 1
private const val b = false
fun foo(): String {
val x = "xy${b}z"
val y = "xyFalsez"
val z = false
return "ab${b}def"
}
@@ -0,0 +1,10 @@
// EXTRACTION_TARGET: property with initializer
val a = 1
fun foo(): String {
val x = "a1234_"
val y = "-4123a"
val z = "+1243a"
val u = 123
return "ab<selection>123</selection>def"
}
@@ -0,0 +1,12 @@
// EXTRACTION_TARGET: property with initializer
val a = 1
private const val i = 123
fun foo(): String {
val x = "a${i}4_"
val y = "-4${i}a"
val z = "+1243a"
val u = 123
return "ab${i}def"
}
@@ -0,0 +1,9 @@
// EXTRACTION_TARGET: property with initializer
val a = 1
fun foo(): String {
val x = "atrue123"
val x = "aTRUE123"
val z = true
return "ab<selection>true</selection>def"
}
@@ -0,0 +1,11 @@
// EXTRACTION_TARGET: property with initializer
val a = 1
private const val b = true
fun foo(): String {
val x = "a${b}123"
val x = "aTRUE123"
val z = true
return "ab${b}def"
}
@@ -0,0 +1,6 @@
// EXTRACTION_TARGET: property with initializer
val a = 1
fun foo(): String {
val x = "<selection>a$a</selection>"
}
@@ -0,0 +1 @@
Can't introduce constant for non constant expression
@@ -0,0 +1,9 @@
// EXTRACTION_TARGET: property with initializer
val a = 1
fun foo(): String {
val x = "xabc$a"
val y = "${a}abcx"
val z = "xacb$a"
return "<selection>abc</selection>def"
}
@@ -0,0 +1,11 @@
// EXTRACTION_TARGET: property with initializer
val a = 1
private const val s = "abc"
fun foo(): String {
val x = "x$s$a"
val y = "${a}${s}x"
val z = "xacb$a"
return "${s}def"
}
@@ -0,0 +1,9 @@
// EXTRACTION_TARGET: property with initializer
val a = 1
fun foo(): String {
val x = "xcd$a"
val y = "${a}cdx"
val z = "xcf$a"
return "ab<selection>cd</selection>ef"
}
@@ -0,0 +1,11 @@
// EXTRACTION_TARGET: property with initializer
val a = 1
private const val s = "cd"
fun foo(): String {
val x = "x$s$a"
val y = "${a}${s}x"
val z = "xcf$a"
return "ab${s}ef"
}
@@ -0,0 +1,9 @@
// EXTRACTION_TARGET: property with initializer
val a = 1
fun foo(): String {
val x = "xdef$a"
val y = "${a}defx"
val z = "xddf$a"
return "abc<selection>def</selection>"
}
@@ -0,0 +1,11 @@
// EXTRACTION_TARGET: property with initializer
val a = 1
private const val s = "def"
fun foo(): String {
val x = "x$s$a"
val y = "${a}${s}x"
val z = "xddf$a"
return "abc$s"
}
@@ -201,6 +201,11 @@
<add-to-group group-id="IntroduceActionsGroup" anchor="after" relative-to-action="IntroduceField"/>
</action>
<action id="IntroduceConstantKt" class="org.jetbrains.kotlin.idea.refactoring.introduce.introduceConstant.IntroduceConstantAction"
use-shortcut-of="IntroduceConstant">
<add-to-group group-id="IntroduceActionsGroup" anchor="after" relative-to-action="IntroduceProperty"/>
</action>
<action id="ExtractFunctionToScope" class="org.jetbrains.kotlin.idea.refactoring.introduce.extractFunction.ExtractFunctionToScopeAction">
<keyboard-shortcut keymap="$default" first-keystroke="control alt shift M"/>
<add-to-group group-id="IntroduceActionsGroup" anchor="after" relative-to-action="ExtractFunction"/>