diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/ExtractableSubstringInfo.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/ExtractableSubstringInfo.kt index 199d1db889d..b8f6290fe58 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/ExtractableSubstringInfo.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/ExtractableSubstringInfo.kt @@ -69,6 +69,9 @@ class ExtractableSubstringInfo( val contentRange: TextRange get() = TextRange(startEntry.startOffset + prefix.length, endEntry.endOffset - suffix.length) + val relativeContentRange: TextRange + get() = contentRange.shiftRight(-template.startOffset) + val entries: Sequence get() = sequence(startEntry) { if (it != endEntry) it.nextSiblingOfSameType() else null } diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/ExtractableCodeDescriptor.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/ExtractableCodeDescriptor.kt index e67963b070d..0355d0c39c8 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/ExtractableCodeDescriptor.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/ExtractableCodeDescriptor.kt @@ -387,7 +387,7 @@ enum class ExtractionTarget(val targetName: String) { } fun checkSimpleBody(descriptor: ExtractableCodeDescriptor): Boolean { - val expression = descriptor.extractionData.getExpressions().singleOrNull() + val expression = descriptor.extractionData.expressions.singleOrNull() return expression != null && expression !is KtDeclaration && expression !is KtBlockExpression } diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/ExtractionData.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/ExtractionData.kt index 84fcc1a871b..d64072e5420 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/ExtractionData.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/ExtractionData.kt @@ -17,7 +17,6 @@ package org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine import com.intellij.openapi.project.Project -import com.intellij.openapi.util.TextRange import com.intellij.psi.PsiElement import com.intellij.psi.PsiNameIdentifierOwner import com.intellij.psi.util.PsiTreeUtil @@ -28,12 +27,13 @@ import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde import org.jetbrains.kotlin.idea.codeInsight.KotlinFileReferencesResolver import org.jetbrains.kotlin.idea.core.compareDescriptors import org.jetbrains.kotlin.idea.core.refactoring.getContextForContainingDeclarationBody +import org.jetbrains.kotlin.idea.refactoring.introduce.ExtractableSubstringInfo +import org.jetbrains.kotlin.idea.refactoring.introduce.extractableSubstringInfo +import org.jetbrains.kotlin.idea.refactoring.introduce.substringContextOrThis import org.jetbrains.kotlin.idea.util.getResolutionScope import org.jetbrains.kotlin.idea.util.psi.patternMatching.KotlinPsiRange import org.jetbrains.kotlin.psi.* -import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForSelector -import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType -import org.jetbrains.kotlin.psi.psiUtil.isInsideOf +import org.jetbrains.kotlin.psi.psiUtil.* import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils import org.jetbrains.kotlin.resolve.DescriptorUtils @@ -87,35 +87,31 @@ data class ExtractionData( ) { val project: Project = originalFile.getProject() val originalElements: List = originalRange.elements + val physicalElements = originalElements.map { it.substringContextOrThis } + + val substringInfo: ExtractableSubstringInfo? + get() = (originalElements.singleOrNull() as? KtExpression)?.extractableSubstringInfo val insertBefore: Boolean = options.extractAsProperty || targetSibling.getStrictParentOfType()?.let { it is KtDeclarationWithBody || it is KtAnonymousInitializer } ?: false - fun getExpressions(): List = originalElements.filterIsInstance() - - private fun getCodeFragmentTextRange(): TextRange? { - val originalElements = originalElements - return when (originalElements.size()) { - 0 -> null - 1 -> originalElements.first().getTextRange() - else -> { - val from = originalElements.first().getTextRange()!!.getStartOffset() - val to = originalElements.last().getTextRange()!!.getEndOffset() - TextRange(from, to) - } - } - } + val expressions = originalElements.filterIsInstance() val codeFragmentText: String by lazy { - getCodeFragmentTextRange()?.let { originalFile.getText()?.substring(it.getStartOffset(), it.getEndOffset()) } ?: "" + val originalElements = originalElements + when (originalElements.size) { + 0 -> "" + 1 -> originalElements.first().text + else -> originalFile.text.substring(originalElements.first().startOffset, originalElements.last().endOffset) + } } val originalStartOffset: Int? get() = originalElements.firstOrNull()?.let { e -> e.getTextRange()!!.getStartOffset() } - val commonParent = PsiTreeUtil.findCommonParent(originalElements) as KtElement + val commonParent = PsiTreeUtil.findCommonParent(physicalElements) as KtElement val bindingContext: BindingContext? by lazy { commonParent.getContextForContainingDeclarationBody() } @@ -126,7 +122,7 @@ data class ExtractionData( fun isExtractableIt(descriptor: DeclarationDescriptor, context: BindingContext): Boolean { if (!(descriptor is ValueParameterDescriptor && (context[BindingContext.AUTO_CREATED_IT, descriptor] ?: false))) return false val function = DescriptorToSourceUtils.descriptorToDeclaration(descriptor.getContainingDeclaration()) as? KtFunctionLiteral - return function == null || !function.isInsideOf(originalElements) + return function == null || !function.isInsideOf(physicalElements) } tailrec fun getDeclaration(descriptor: DeclarationDescriptor, context: BindingContext): PsiNameIdentifierOwner? { @@ -157,18 +153,26 @@ data class ExtractionData( } override fun visitSimpleNameExpression(ref: KtSimpleNameExpression) { - if (ref !is KtSimpleNameExpression) return if (ref.getParent() is KtValueArgumentName) return - val resolvedCall = ref.getResolvedCall(context) - val descriptor = context[BindingContext.REFERENCE_TARGET, ref] ?: return + val physicalRef = substringInfo?.let { + // If substring contains some references it must be extracted as a string template + val physicalExpression = expressions.single() as KtStringTemplateExpression + val extractedContentOffset = physicalExpression.getContentRange().startOffset + physicalExpression.startOffset + val offsetInExtracted = ref.startOffset - extractedContentOffset + val offsetInTemplate = it.relativeContentRange.startOffset + offsetInExtracted + it.template.findElementAt(offsetInTemplate)!!.getStrictParentOfType() + } ?: ref + + val resolvedCall = physicalRef.getResolvedCall(context) + val descriptor = context[BindingContext.REFERENCE_TARGET, physicalRef] ?: return val declaration = getDeclaration(descriptor, context) ?: return val offset = ref.getTextRange()!!.getStartOffset() - originalStartOffset - resultMap[offset] = ResolveResult(ref, declaration, descriptor, resolvedCall) + resultMap[offset] = ResolveResult(physicalRef, declaration, descriptor, resolvedCall) } } - getExpressions().forEach { it.accept(visitor) } + expressions.forEach { it.accept(visitor) } resultMap } @@ -230,7 +234,7 @@ data class ExtractionData( val isBadRef = !(compareDescriptors(project, originalResolveResult.descriptor, descriptor) && originalContext.diagnostics.forElement(originalResolveResult.originalRefExpr) == context.diagnostics.forElement(ref)) || smartCast != null - if (isBadRef && !originalResolveResult.declaration.isInsideOf(originalElements)) { + if (isBadRef && !originalResolveResult.declaration.isInsideOf(physicalElements)) { val originalResolvedCall = originalResolveResult.resolvedCall as? VariableAsFunctionResolvedCall val originalFunctionCall = originalResolvedCall?.functionCall val originalVariableCall = originalResolvedCall?.variableCall diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/duplicateUtil.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/duplicateUtil.kt index 4c34a282305..acc7aaea279 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/duplicateUtil.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/duplicateUtil.kt @@ -35,9 +35,10 @@ import com.intellij.find.FindManager import org.jetbrains.kotlin.idea.util.application.executeWriteCommand import com.intellij.refactoring.util.duplicates.MethodDuplicatesHandler import com.intellij.refactoring.RefactoringBundle +import org.jetbrains.kotlin.idea.refactoring.introduce.getPhysicalTextRange public fun KotlinPsiRange.highlight(project: Project, editor: Editor): RangeHighlighter? { - val textRange = getTextRange() + val textRange = getPhysicalTextRange() val highlighters = ArrayList() val attributes = EditorColorsManager.getInstance().getGlobalScheme().getAttributes(EditorColors.SEARCH_RESULT_ATTRIBUTES)!! HighlightManager.getInstance(project).addRangeHighlight( @@ -48,7 +49,7 @@ public fun KotlinPsiRange.highlight(project: Project, editor: Editor): RangeHigh public fun KotlinPsiRange.preview(project: Project, editor: Editor): RangeHighlighter? { return highlight(project, editor)?.let { - val startOffset = getTextRange().getStartOffset() + val startOffset = getPhysicalTextRange().getStartOffset() val foldedRegions = CodeFoldingManager.getInstance(project) .getFoldRegionsAtOffset(editor, startOffset) diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/extractableAnalysisUtil.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/extractableAnalysisUtil.kt index 9f0085d687e..9f8596c3b8a 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/extractableAnalysisUtil.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/extractableAnalysisUtil.kt @@ -127,11 +127,11 @@ private fun List.getVarDescriptorsAccessedAfterwards(bindingContext private fun List.getExitPoints(): List = filter { localInstruction -> localInstruction.nextInstructions.any { it !in this } } -private fun List.getResultTypeAndExpressions( +private fun ExtractionData.getResultTypeAndExpressions( + instructions: List, bindingContext: BindingContext, targetScope: LexicalScope?, - options: ExtractionOptions, - module: ModuleDescriptor + options: ExtractionOptions, module: ModuleDescriptor ): Pair> { fun instructionToExpression(instruction: Instruction, unwrapReturn: Boolean): KtExpression? { return when (instruction) { @@ -146,6 +146,10 @@ private fun List.getResultTypeAndExpressions( fun instructionToType(instruction: Instruction): KotlinType? { val expression = instructionToExpression(instruction, true) ?: return null + substringInfo?.let { + if (it.template == expression) return it.type + } + if (options.inferUnitTypeForUnusedValues && expression.isUsedAsStatement(bindingContext)) return null return bindingContext.getType(expression) @@ -154,11 +158,11 @@ private fun List.getResultTypeAndExpressions( } } - val resultTypes = mapNotNull(::instructionToType) + val resultTypes = instructions.mapNotNull(::instructionToType) val commonSupertype = if (resultTypes.isNotEmpty()) CommonSupertypes.commonSupertype(resultTypes) else module.builtIns.defaultReturnType val resultType = if (options.allowSpecialClassNames) commonSupertype else commonSupertype.approximateWithResolvableType(targetScope, false) - val expressions = mapNotNull { instructionToExpression(it, false) } + val expressions = instructions.mapNotNull { instructionToExpression(it, false) } return resultType to expressions } @@ -210,7 +214,7 @@ private fun ExtractionData.getLocalDeclarationsWithNonLocalUsages( if (instruction !in localInstructions) { instruction.getPrimaryDeclarationDescriptorIfAny(bindingContext)?.let { descriptor -> val declaration = DescriptorToSourceUtilsIde.getAnyDeclaration(project, descriptor) - if (declaration is KtNamedDeclaration && declaration.isInsideOf(originalElements)) { + if (declaration is KtNamedDeclaration && declaration.isInsideOf(physicalElements)) { declarations.add(declaration) } } @@ -279,8 +283,8 @@ private fun ExtractionData.analyzeControlFlow( val nonLocallyUsedDeclarations = getLocalDeclarationsWithNonLocalUsages(pseudocode, localInstructions, bindingContext) val (declarationsToCopy, declarationsToReport) = nonLocallyUsedDeclarations.partition { it is KtProperty && it.isLocal } - val (typeOfDefaultFlow, defaultResultExpressions) = defaultExits.getResultTypeAndExpressions(bindingContext, targetScope, options, module) - val (returnValueType, valuedReturnExpressions) = valuedReturnExits.getResultTypeAndExpressions(bindingContext, targetScope, options, module) + val (typeOfDefaultFlow, defaultResultExpressions) = getResultTypeAndExpressions(defaultExits, bindingContext, targetScope, options, module) + val (returnValueType, valuedReturnExpressions) = getResultTypeAndExpressions(valuedReturnExits, bindingContext, targetScope, options, module) val emptyControlFlow = ControlFlow(Collections.emptyList(), { OutputValueBoxer.AsTuple(it, module) }, declarationsToCopy) @@ -586,7 +590,7 @@ private fun ExtractionData.checkDeclarationsMovingOutOfScope( override fun visitSimpleNameExpression(expression: KtSimpleNameExpression) { val target = expression.mainReference.resolve() if (target is KtNamedDeclaration - && target.isInsideOf(originalElements) + && target.isInsideOf(physicalElements) && target.getStrictParentOfType() == enclosingDeclaration) { declarationsOutOfScope.add(target) } @@ -605,7 +609,7 @@ private fun ExtractionData.checkDeclarationsMovingOutOfScope( private fun ExtractionData.getLocalInstructions(pseudocode: Pseudocode): List { val instructions = ArrayList() pseudocode.traverse(TraversalOrder.FORWARD) { - if (it is JetElementInstruction && it.element.isInsideOf(originalElements)) { + if (it is JetElementInstruction && it.element.isInsideOf(physicalElements)) { instructions.add(it) } } @@ -725,7 +729,7 @@ private fun ExtractionData.suggestFunctionNames(returnType: KotlinType): List + expressions.singleOrNull()?.let { expr -> val property = expr.getStrictParentOfType() if (property?.initializer == expr) { property?.name?.let { functionNames.add(KotlinNameSuggester.suggestNameByName("get" + it.capitalize(), validator)) } @@ -806,7 +810,7 @@ fun ExtractableCodeDescriptor.validate(): ExtractableCodeDescriptorWithConflicts fun validateBody() { for ((originalOffset, resolveResult) in extractionData.refOffsetToDeclaration) { - if (resolveResult.declaration.isInsideOf(extractionData.originalElements)) continue + if (resolveResult.declaration.isInsideOf(extractionData.physicalElements)) continue val currentRefExprs = result.nameByOffset[originalOffset].mapNotNull { (it as? KtThisExpression)?.instanceReference ?: it as? KtSimpleNameExpression diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/extractorUtil.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/extractorUtil.kt index 43ae4168e66..9d99af7b1a7 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/extractorUtil.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/extractorUtil.kt @@ -26,20 +26,22 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.idea.core.* import org.jetbrains.kotlin.idea.core.refactoring.isMultiLine +import org.jetbrains.kotlin.idea.core.refactoring.removeTemplateEntryBracesIfPossible import org.jetbrains.kotlin.idea.intentions.ConvertToExpressionBodyIntention import org.jetbrains.kotlin.idea.intentions.InfixCallToOrdinaryIntention import org.jetbrains.kotlin.idea.intentions.OperatorToFunctionIntention import org.jetbrains.kotlin.idea.intentions.RemoveExplicitTypeArgumentsIntention +import org.jetbrains.kotlin.idea.refactoring.introduce.extractableSubstringInfo import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.OutputValue.* import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.OutputValueBoxer.AsTuple +import org.jetbrains.kotlin.idea.refactoring.introduce.getPhysicalTextRange +import org.jetbrains.kotlin.idea.refactoring.introduce.replaceWith +import org.jetbrains.kotlin.idea.refactoring.introduce.substringContextOrThis import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers import org.jetbrains.kotlin.idea.util.ShortenReferences -import org.jetbrains.kotlin.idea.util.psi.patternMatching.KotlinPsiRange -import org.jetbrains.kotlin.idea.util.psi.patternMatching.KotlinPsiUnifier -import org.jetbrains.kotlin.idea.util.psi.patternMatching.UnificationResult +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.psi.patternMatching.UnifierParameter import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.KtPsiFactory.CallableBuilder import org.jetbrains.kotlin.psi.codeFragmentUtil.DEBUG_TYPE_REFERENCE_STRING @@ -206,15 +208,18 @@ fun ExtractableCodeDescriptor.findDuplicates(): List { val unifier = KotlinPsiUnifier(unifierParameters, true) val scopeElement = getOccurrenceContainer() ?: return Collections.emptyList() - val originalTextRange = extractionData.originalRange.getTextRange() + val originalTextRange = extractionData.originalRange.getPhysicalTextRange() return extractionData .originalRange .match(scopeElement, unifier) .asSequence() - .filter { !(it.range.getTextRange() intersects originalTextRange) } + .filter { !(it.range.getPhysicalTextRange() intersects originalTextRange) } .mapNotNull { match -> val controlFlow = getControlFlowIfMatched(match) - controlFlow?.let { DuplicateInfo(match.range, it, unifierParameters.map { match.substitution[it]!!.text!! }) } + val range = with(match.range) { + (elements.singleOrNull() as? KtStringTemplateEntryWithExpression)?.expression?.toRange() ?: this + } + controlFlow?.let { DuplicateInfo(range, it, unifierParameters.map { match.substitution[it]!!.text!! }) } } .toList() } @@ -229,16 +234,15 @@ private fun makeCall( controlFlow: ControlFlow, rangeToReplace: KotlinPsiRange, arguments: List) { - fun insertCall(anchor: PsiElement, wrappedCall: KtExpression) { + fun insertCall(anchor: PsiElement, wrappedCall: KtExpression): KtExpression? { val firstExpression = rangeToReplace.elements.firstOrNull { it is KtExpression } as? KtExpression if (firstExpression?.isFunctionLiteralOutsideParentheses() ?: false) { val functionLiteralArgument = firstExpression?.getStrictParentOfType()!! - functionLiteralArgument.moveInsideParenthesesAndReplaceWith(wrappedCall, extractableDescriptor.originalContext) - return + return functionLiteralArgument.moveInsideParenthesesAndReplaceWith(wrappedCall, extractableDescriptor.originalContext) } if (anchor is KtOperationReferenceExpression) { - val operationExpression = anchor.parent as? KtOperationExpression ?: return + val operationExpression = anchor.parent as? KtOperationExpression ?: return null val newNameExpression = when (operationExpression) { is KtUnaryExpression -> OperatorToFunctionIntention.convert(operationExpression).second is KtBinaryExpression -> { @@ -246,11 +250,14 @@ private fun makeCall( } else -> null } - newNameExpression?.replace(wrappedCall) - return + return newNameExpression?.replaced(wrappedCall) } - anchor.replace(wrappedCall) + (anchor as? KtExpression)?.extractableSubstringInfo?.let { + return it.replaceWith(wrappedCall) + } + + return anchor.replaced(wrappedCall) } if (rangeToReplace !is KotlinPsiRange.ListRange) return @@ -393,7 +400,7 @@ private fun makeCall( if (!inlinableCall) { block.addBefore(newLine, anchorInBlock) } - insertCall(anchor, wrapCall(it, unboxingExpressions[it]!!).first() as KtExpression) + insertCall(anchor, wrapCall(it, unboxingExpressions[it]!!).first() as KtExpression)?.removeTemplateEntryBracesIfPossible() } if (anchor.isValid) { @@ -617,7 +624,7 @@ fun ExtractionGeneratorConfiguration.generateDeclaration( val anchor = with(descriptor.extractionData) { val targetParent = targetSibling.parent - val anchorCandidates = duplicates.mapTo(ArrayList()) { it.range.elements.first() } + val anchorCandidates = duplicates.mapTo(ArrayList()) { it.range.elements.first().substringContextOrThis } anchorCandidates.add(targetSibling) if (targetSibling is KtEnumEntry) { anchorCandidates.add(targetSibling.siblings().last { it is KtEnumEntry }) diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/inferParameterInfo.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/inferParameterInfo.kt index 9da5ae62fb5..45f2f0768ab 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/inferParameterInfo.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/inferParameterInfo.kt @@ -88,7 +88,7 @@ internal fun ExtractionData.inferParametersInfo( val varNameValidator = NewDeclarationNameValidator( commonParent.getNonStrictParentOfType()!!, - originalElements.firstOrNull(), + physicalElements.firstOrNull(), NewDeclarationNameValidator.Target.VARIABLES ) @@ -131,7 +131,7 @@ private fun ExtractionData.extractReceiver( val thisExpr = refInfo.refExpr.parent as? KtThisExpression if (hasThisReceiver - && DescriptorToSourceUtilsIde.getAllDeclarations(project, thisDescriptor!!).all { it.isInsideOf(originalElements) }) { + && DescriptorToSourceUtilsIde.getAllDeclarations(project, thisDescriptor!!).all { it.isInsideOf(physicalElements) }) { return } diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/introduceUtil.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/introduceUtil.kt index a40368b276e..9e462326a8f 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/introduceUtil.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/introduceUtil.kt @@ -25,6 +25,7 @@ import com.intellij.psi.PsiElement import com.intellij.psi.util.PsiTreeUtil import org.jetbrains.kotlin.idea.codeInsight.CodeInsightUtils import org.jetbrains.kotlin.idea.core.refactoring.chooseContainerElementIfNecessary +import org.jetbrains.kotlin.idea.core.refactoring.removeTemplateEntryBracesIfPossible import org.jetbrains.kotlin.idea.refactoring.KotlinRefactoringBundle import org.jetbrains.kotlin.idea.refactoring.KotlinRefactoringUtil import org.jetbrains.kotlin.idea.util.psi.patternMatching.KotlinPsiRange @@ -52,7 +53,7 @@ fun selectElementsWithTargetSibling( ?: throw AssertionError("Should have at least one parent: ${physicalElements.joinToString("\n")}") if (parent == targetContainer) { - continuation(elements, elements.first()) + continuation(elements, physicalElements.first()) return } @@ -84,7 +85,7 @@ fun selectElementsWithTargetParent( val parent = PsiTreeUtil.findCommonParent(physicalElements) ?: throw AssertionError("Should have at least one parent: ${physicalElements.joinToString("\n")}") - val containers = getContainers(elements, parent) + val containers = getContainers(physicalElements, parent) if (containers.isEmpty()) { showErrorHintByKey("cannot.refactor.no.container") return @@ -173,4 +174,26 @@ fun findExpressionOrStringFragment(file: KtFile, startOffset: Int, endOffset: In val suffix = entry2.text.substring(suffixOffset) return ExtractableSubstringInfo(entry1, entry2, prefix, suffix).createExpression() +} + +fun KotlinPsiRange.getPhysicalTextRange(): TextRange { + return (elements.singleOrNull() as? KtExpression)?.extractableSubstringInfo?.contentRange ?: getTextRange() +} + +fun ExtractableSubstringInfo.replaceWith(replacement: KtExpression): KtExpression { + return with(this) { + val psiFactory = KtPsiFactory(replacement) + val parent = startEntry.parent + + psiFactory.createStringTemplate(prefix).entries.singleOrNull()?.let { parent.addBefore(it, startEntry) } + + val refEntry = psiFactory.createBlockStringTemplateEntry(replacement) + val addedRefEntry = parent.addBefore(refEntry, startEntry) as KtStringTemplateEntryWithExpression + + psiFactory.createStringTemplate(suffix).entries.singleOrNull()?.let { parent.addAfter(it, endEntry) } + + parent.deleteChildRange(startEntry, endEntry) + + addedRefEntry.expression!! + } } \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/introduceVariable/KotlinIntroduceVariableHandler.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/introduceVariable/KotlinIntroduceVariableHandler.kt index e4cfdf65b19..1f18c594852 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/introduceVariable/KotlinIntroduceVariableHandler.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/introduceVariable/KotlinIntroduceVariableHandler.kt @@ -42,8 +42,8 @@ import org.jetbrains.kotlin.idea.core.compareDescriptors import org.jetbrains.kotlin.idea.core.moveInsideParenthesesAndReplaceWith import org.jetbrains.kotlin.idea.core.refactoring.Pass import org.jetbrains.kotlin.idea.core.refactoring.chooseContainerElementIfNecessary +import org.jetbrains.kotlin.idea.core.refactoring.removeTemplateEntryBracesIfPossible import org.jetbrains.kotlin.idea.intentions.ConvertToBlockBodyIntention -import org.jetbrains.kotlin.idea.intentions.RemoveCurlyBracesFromTemplateIntention import org.jetbrains.kotlin.idea.refactoring.KotlinRefactoringBundle import org.jetbrains.kotlin.idea.refactoring.KotlinRefactoringUtil import org.jetbrains.kotlin.idea.refactoring.introduce.* @@ -108,37 +108,17 @@ object KotlinIntroduceVariableHandler : KotlinIntroduceHandlerBase() { val replacement = psiFactory.createExpression(nameSuggestion) val substringInfo = expressionToReplace.extractableSubstringInfo - var result = if (expressionToReplace.isFunctionLiteralOutsideParentheses()) { - val functionLiteralArgument = expressionToReplace.getStrictParentOfType()!! - val newCallExpression = functionLiteralArgument.moveInsideParenthesesAndReplaceWith(replacement, bindingContext) - newCallExpression.valueArguments.last().getArgumentExpression()!! - } - else if (substringInfo != null) { - with(substringInfo) { - val parent = startEntry.parent - - psiFactory.createStringTemplate(prefix).entries.singleOrNull()?.let { parent.addBefore(it, startEntry) } - - val refEntry = psiFactory.createBlockStringTemplateEntry(replacement) - val addedRefEntry = parent.addBefore(refEntry, startEntry) as KtStringTemplateEntryWithExpression - - psiFactory.createStringTemplate(suffix).entries.singleOrNull()?.let { parent.addAfter(it, endEntry) } - - parent.deleteChildRange(startEntry, endEntry) - - addedRefEntry.expression!! + var result = when { + expressionToReplace.isFunctionLiteralOutsideParentheses() -> { + val functionLiteralArgument = expressionToReplace.getStrictParentOfType()!! + val newCallExpression = functionLiteralArgument.moveInsideParenthesesAndReplaceWith(replacement, bindingContext) + newCallExpression.valueArguments.last().getArgumentExpression()!! } - } - else { - expressionToReplace.replace(replacement) as KtExpression + substringInfo != null -> substringInfo.replaceWith(replacement) + else -> expressionToReplace.replace(replacement) as KtExpression } - val parent = result.parent - if (parent is KtBlockStringTemplateEntry) { - val intention = RemoveCurlyBracesFromTemplateIntention() - val newEntry = if (intention.isApplicableTo(parent)) intention.applyTo(parent) else parent - result = newEntry.expression!! - } + result = result.removeTemplateEntryBracesIfPossible() if (addToReferences) references.addIfNotNull(result) diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/jetRefactoringUtil.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/jetRefactoringUtil.kt index 6bf99ddb93f..6d9d7cbaeb7 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/jetRefactoringUtil.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/jetRefactoringUtil.kt @@ -68,6 +68,7 @@ import org.jetbrains.kotlin.idea.caches.resolve.getJavaMemberDescriptor import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor import org.jetbrains.kotlin.idea.core.KotlinNameSuggester import org.jetbrains.kotlin.idea.core.getPackage +import org.jetbrains.kotlin.idea.intentions.RemoveCurlyBracesFromTemplateIntention import org.jetbrains.kotlin.idea.j2k.IdeaJavaToKotlinServices import org.jetbrains.kotlin.idea.util.ProjectRootsUtil import org.jetbrains.kotlin.idea.util.string.collapseSpaces @@ -750,4 +751,13 @@ fun replaceListPsiAndKeepDelimiters( fun Pass(body: (T) -> Unit) = object: Pass() { override fun pass(t: T) = body(t) +} + +fun KtExpression.removeTemplateEntryBracesIfPossible(): KtExpression { + val parent = parent + if (parent !is KtBlockStringTemplateEntry) return this + + val intention = RemoveCurlyBracesFromTemplateIntention() + val newEntry = if (intention.isApplicableTo(parent)) intention.applyTo(parent) else parent + return newEntry.expression!! } \ No newline at end of file diff --git a/idea/testData/refactoring/extractFunction/stringTemplates/brokenEntryWithBlockExpr.kt b/idea/testData/refactoring/extractFunction/stringTemplates/brokenEntryWithBlockExpr.kt new file mode 100644 index 00000000000..8d3f78f7b2c --- /dev/null +++ b/idea/testData/refactoring/extractFunction/stringTemplates/brokenEntryWithBlockExpr.kt @@ -0,0 +1,3 @@ +fun foo(param: Int): String { + return "abc${param}def" +} \ No newline at end of file diff --git a/idea/testData/refactoring/extractFunction/stringTemplates/brokenEntryWithBlockExpr.kt.conflicts b/idea/testData/refactoring/extractFunction/stringTemplates/brokenEntryWithBlockExpr.kt.conflicts new file mode 100644 index 00000000000..21617cc617d --- /dev/null +++ b/idea/testData/refactoring/extractFunction/stringTemplates/brokenEntryWithBlockExpr.kt.conflicts @@ -0,0 +1 @@ +Cannot perform refactoring without an expression \ No newline at end of file diff --git a/idea/testData/refactoring/extractFunction/stringTemplates/brokenEntryWithExpr.kt b/idea/testData/refactoring/extractFunction/stringTemplates/brokenEntryWithExpr.kt new file mode 100644 index 00000000000..a7ef59f2d42 --- /dev/null +++ b/idea/testData/refactoring/extractFunction/stringTemplates/brokenEntryWithExpr.kt @@ -0,0 +1,3 @@ +fun foo(param: Int): String { + return "abc$param" +} \ No newline at end of file diff --git a/idea/testData/refactoring/extractFunction/stringTemplates/brokenEntryWithExpr.kt.conflicts b/idea/testData/refactoring/extractFunction/stringTemplates/brokenEntryWithExpr.kt.conflicts new file mode 100644 index 00000000000..21617cc617d --- /dev/null +++ b/idea/testData/refactoring/extractFunction/stringTemplates/brokenEntryWithExpr.kt.conflicts @@ -0,0 +1 @@ +Cannot perform refactoring without an expression \ No newline at end of file diff --git a/idea/testData/refactoring/extractFunction/stringTemplates/brokenEscapeEntry.kt b/idea/testData/refactoring/extractFunction/stringTemplates/brokenEscapeEntry.kt new file mode 100644 index 00000000000..3cc3e6f3471 --- /dev/null +++ b/idea/testData/refactoring/extractFunction/stringTemplates/brokenEscapeEntry.kt @@ -0,0 +1,3 @@ +fun foo(a: Int): String { + return "abc$a\ndef" +} \ No newline at end of file diff --git a/idea/testData/refactoring/extractFunction/stringTemplates/brokenEscapeEntry.kt.conflicts b/idea/testData/refactoring/extractFunction/stringTemplates/brokenEscapeEntry.kt.conflicts new file mode 100644 index 00000000000..21617cc617d --- /dev/null +++ b/idea/testData/refactoring/extractFunction/stringTemplates/brokenEscapeEntry.kt.conflicts @@ -0,0 +1 @@ +Cannot perform refactoring without an expression \ No newline at end of file diff --git a/idea/testData/refactoring/extractFunction/stringTemplates/extractFalse.kt b/idea/testData/refactoring/extractFunction/stringTemplates/extractFalse.kt new file mode 100644 index 00000000000..285d901493c --- /dev/null +++ b/idea/testData/refactoring/extractFunction/stringTemplates/extractFalse.kt @@ -0,0 +1,7 @@ +// SUGGESTED_NAMES: b, getX +fun foo(param: Int): String { + val x = "xyfalsez" + val y = "xyFalsez" + val z = false + return "abfalsedef" +} \ No newline at end of file diff --git a/idea/testData/refactoring/extractFunction/stringTemplates/extractFalse.kt.after b/idea/testData/refactoring/extractFunction/stringTemplates/extractFalse.kt.after new file mode 100644 index 00000000000..87a07d18e6c --- /dev/null +++ b/idea/testData/refactoring/extractFunction/stringTemplates/extractFalse.kt.after @@ -0,0 +1,9 @@ +// SUGGESTED_NAMES: b, getX +fun foo(param: Int): String { + val x = "xy${b()}z" + val y = "xyFalsez" + val z = false + return "ab${b()}def" +} + +private fun b() = false \ No newline at end of file diff --git a/idea/testData/refactoring/extractFunction/stringTemplates/extractIntegerLiteral.kt b/idea/testData/refactoring/extractFunction/stringTemplates/extractIntegerLiteral.kt new file mode 100644 index 00000000000..9662606678d --- /dev/null +++ b/idea/testData/refactoring/extractFunction/stringTemplates/extractIntegerLiteral.kt @@ -0,0 +1,8 @@ +// SUGGESTED_NAMES: i, getX +fun foo(param: Int): String { + val x = "a1234_" + val y = "-4123a" + val z = "+1243a" + val u = 123 + return "ab123def" +} \ No newline at end of file diff --git a/idea/testData/refactoring/extractFunction/stringTemplates/extractIntegerLiteral.kt.after b/idea/testData/refactoring/extractFunction/stringTemplates/extractIntegerLiteral.kt.after new file mode 100644 index 00000000000..23f7cf14068 --- /dev/null +++ b/idea/testData/refactoring/extractFunction/stringTemplates/extractIntegerLiteral.kt.after @@ -0,0 +1,10 @@ +// SUGGESTED_NAMES: i, getX +fun foo(param: Int): String { + val x = "a${i()}4_" + val y = "-4${i()}a" + val z = "+1243a" + val u = 123 + return "ab${i()}def" +} + +private fun i() = 123 \ No newline at end of file diff --git a/idea/testData/refactoring/extractFunction/stringTemplates/extractTrue.kt b/idea/testData/refactoring/extractFunction/stringTemplates/extractTrue.kt new file mode 100644 index 00000000000..0a284805964 --- /dev/null +++ b/idea/testData/refactoring/extractFunction/stringTemplates/extractTrue.kt @@ -0,0 +1,7 @@ +// SUGGESTED_NAMES: b, getX +fun foo(param: Int): String { + val x = "atrue123" + val x = "aTRUE123" + val z = true + return "abtruedef" +} \ No newline at end of file diff --git a/idea/testData/refactoring/extractFunction/stringTemplates/extractTrue.kt.after b/idea/testData/refactoring/extractFunction/stringTemplates/extractTrue.kt.after new file mode 100644 index 00000000000..a882c5311e8 --- /dev/null +++ b/idea/testData/refactoring/extractFunction/stringTemplates/extractTrue.kt.after @@ -0,0 +1,9 @@ +// SUGGESTED_NAMES: b, getX +fun foo(param: Int): String { + val x = "a${b()}123" + val x = "aTRUE123" + val z = true + return "ab${b()}def" +} + +private fun b() = true \ No newline at end of file diff --git a/idea/testData/refactoring/extractFunction/stringTemplates/fullContent.kt b/idea/testData/refactoring/extractFunction/stringTemplates/fullContent.kt new file mode 100644 index 00000000000..0dc08aed3df --- /dev/null +++ b/idea/testData/refactoring/extractFunction/stringTemplates/fullContent.kt @@ -0,0 +1,8 @@ +// PARAM_DESCRIPTOR: value-parameter val a: kotlin.Int defined in foo +// PARAM_TYPES: kotlin.Int, Number, Comparable, java.io.Serializable, Any +fun foo(a: Int): String { + val x = "abc$a" + val y = "abc${a}" + val z = "abc{$a}def" + return "abc$a" + "def" +} \ No newline at end of file diff --git a/idea/testData/refactoring/extractFunction/stringTemplates/fullContent.kt.after b/idea/testData/refactoring/extractFunction/stringTemplates/fullContent.kt.after new file mode 100644 index 00000000000..ace6940cd9d --- /dev/null +++ b/idea/testData/refactoring/extractFunction/stringTemplates/fullContent.kt.after @@ -0,0 +1,10 @@ +// PARAM_DESCRIPTOR: value-parameter val a: kotlin.Int defined in foo +// PARAM_TYPES: kotlin.Int, Number, Comparable, java.io.Serializable, Any +fun foo(a: Int): String { + val x = s(a) + val y = s(a) + val z = "abc{$a}def" + return s(a) + "def" +} + +private fun s(a: Int) = "abc$a" \ No newline at end of file diff --git a/idea/testData/refactoring/extractFunction/stringTemplates/fullEntryWithBlockExpr.kt b/idea/testData/refactoring/extractFunction/stringTemplates/fullEntryWithBlockExpr.kt new file mode 100644 index 00000000000..e667409008d --- /dev/null +++ b/idea/testData/refactoring/extractFunction/stringTemplates/fullEntryWithBlockExpr.kt @@ -0,0 +1,8 @@ +// PARAM_DESCRIPTOR: value-parameter val a: kotlin.Int defined in foo +// PARAM_TYPES: kotlin.Int +fun foo(a: Int): String { + val x = "-${a + 1}" + val y = "x${a + 1}y" + val z = "x${a - 1}y" + return "abc${a + 1}def" +} \ No newline at end of file diff --git a/idea/testData/refactoring/extractFunction/stringTemplates/fullEntryWithBlockExpr.kt.after b/idea/testData/refactoring/extractFunction/stringTemplates/fullEntryWithBlockExpr.kt.after new file mode 100644 index 00000000000..9274f2094b3 --- /dev/null +++ b/idea/testData/refactoring/extractFunction/stringTemplates/fullEntryWithBlockExpr.kt.after @@ -0,0 +1,10 @@ +// PARAM_DESCRIPTOR: value-parameter val a: kotlin.Int defined in foo +// PARAM_TYPES: kotlin.Int +fun foo(a: Int): String { + val x = "-${i(a)}" + val y = "x${i(a)}y" + val z = "x${a - 1}y" + return "abc${i(a)}def" +} + +private fun i(a: Int) = a + 1 \ No newline at end of file diff --git a/idea/testData/refactoring/extractFunction/stringTemplates/fullEntryWithSimpleName.kt b/idea/testData/refactoring/extractFunction/stringTemplates/fullEntryWithSimpleName.kt new file mode 100644 index 00000000000..06e5a1a0a53 --- /dev/null +++ b/idea/testData/refactoring/extractFunction/stringTemplates/fullEntryWithSimpleName.kt @@ -0,0 +1,8 @@ +// PARAM_DESCRIPTOR: value-parameter val a: kotlin.Int defined in foo +// PARAM_TYPES: kotlin.Int, Number, Comparable, java.io.Serializable, Any +fun foo(a: Int): String { + val x = "-$a" + val y = "x${a}y" + val z = "x$ay" + return "abc${a}def" +} \ No newline at end of file diff --git a/idea/testData/refactoring/extractFunction/stringTemplates/fullEntryWithSimpleName.kt.after b/idea/testData/refactoring/extractFunction/stringTemplates/fullEntryWithSimpleName.kt.after new file mode 100644 index 00000000000..d7802ccbe99 --- /dev/null +++ b/idea/testData/refactoring/extractFunction/stringTemplates/fullEntryWithSimpleName.kt.after @@ -0,0 +1,10 @@ +// PARAM_DESCRIPTOR: value-parameter val a: kotlin.Int defined in foo +// PARAM_TYPES: kotlin.Int, Number, Comparable, java.io.Serializable, Any +fun foo(a: Int): String { + val x = "-$a" + val y = "x${a}y" + val z = "x$ay" + return "abc${i(a)}def" +} + +private fun i(a: Int) = a \ No newline at end of file diff --git a/idea/testData/refactoring/extractFunction/stringTemplates/multipleEntriesWithPrefix.kt b/idea/testData/refactoring/extractFunction/stringTemplates/multipleEntriesWithPrefix.kt new file mode 100644 index 00000000000..465a830b084 --- /dev/null +++ b/idea/testData/refactoring/extractFunction/stringTemplates/multipleEntriesWithPrefix.kt @@ -0,0 +1,8 @@ +// SUGGESTED_NAMES: s, getX +// PARAM_DESCRIPTOR: value-parameter val a: kotlin.Int defined in foo +// PARAM_TYPES: kotlin.Int +fun foo(a: Int): String { + val x = "+cd$a:${a + 1}efg" + val y = "+cd$a${a + 1}efg" + return "abcd$a:${a + 1}ef" +} \ No newline at end of file diff --git a/idea/testData/refactoring/extractFunction/stringTemplates/multipleEntriesWithPrefix.kt.after b/idea/testData/refactoring/extractFunction/stringTemplates/multipleEntriesWithPrefix.kt.after new file mode 100644 index 00000000000..9e62e4e325e --- /dev/null +++ b/idea/testData/refactoring/extractFunction/stringTemplates/multipleEntriesWithPrefix.kt.after @@ -0,0 +1,10 @@ +// SUGGESTED_NAMES: s, getX +// PARAM_DESCRIPTOR: value-parameter val a: kotlin.Int defined in foo +// PARAM_TYPES: kotlin.Int +fun foo(a: Int): String { + val x = "+${s(a)}g" + val y = "+cd$a${a + 1}efg" + return "ab${s(a)}" +} + +private fun s(a: Int) = "cd$a:${a + 1}ef" \ No newline at end of file diff --git a/idea/testData/refactoring/extractFunction/stringTemplates/multipleEntriesWithSubstring.kt b/idea/testData/refactoring/extractFunction/stringTemplates/multipleEntriesWithSubstring.kt new file mode 100644 index 00000000000..08af97badad --- /dev/null +++ b/idea/testData/refactoring/extractFunction/stringTemplates/multipleEntriesWithSubstring.kt @@ -0,0 +1,8 @@ +// SUGGESTED_NAMES: s, getX +// PARAM_DESCRIPTOR: value-parameter val a: kotlin.Int defined in foo +// PARAM_TYPES: kotlin.Int +fun foo(a: Int): String { + val x = "_c$a:${a + 1}d_" + val y = "_$a:${a + 1}d_" + return "abc$a:${a + 1}def" +} \ No newline at end of file diff --git a/idea/testData/refactoring/extractFunction/stringTemplates/multipleEntriesWithSubstring.kt.after b/idea/testData/refactoring/extractFunction/stringTemplates/multipleEntriesWithSubstring.kt.after new file mode 100644 index 00000000000..9235c793e1d --- /dev/null +++ b/idea/testData/refactoring/extractFunction/stringTemplates/multipleEntriesWithSubstring.kt.after @@ -0,0 +1,10 @@ +// SUGGESTED_NAMES: s, getX +// PARAM_DESCRIPTOR: value-parameter val a: kotlin.Int defined in foo +// PARAM_TYPES: kotlin.Int +fun foo(a: Int): String { + val x = "_${s(a)}_" + val y = "_$a:${a + 1}d_" + return "ab${s(a)}ef" +} + +private fun s(a: Int) = "c$a:${a + 1}d" \ No newline at end of file diff --git a/idea/testData/refactoring/extractFunction/stringTemplates/multipleEntriesWithSuffix.kt b/idea/testData/refactoring/extractFunction/stringTemplates/multipleEntriesWithSuffix.kt new file mode 100644 index 00000000000..ee69551069f --- /dev/null +++ b/idea/testData/refactoring/extractFunction/stringTemplates/multipleEntriesWithSuffix.kt @@ -0,0 +1,8 @@ +// SUGGESTED_NAMES: s, getX +// PARAM_DESCRIPTOR: value-parameter val a: kotlin.Int defined in foo +// PARAM_TYPES: kotlin.Int +fun foo(a: Int): String { + val x = "_ab$a:${a + 1}cd__" + val y = "_a$a:${a + 1}cd__" + return "ab$a:${a + 1}cdef" +} \ No newline at end of file diff --git a/idea/testData/refactoring/extractFunction/stringTemplates/multipleEntriesWithSuffix.kt.after b/idea/testData/refactoring/extractFunction/stringTemplates/multipleEntriesWithSuffix.kt.after new file mode 100644 index 00000000000..e526c3119d1 --- /dev/null +++ b/idea/testData/refactoring/extractFunction/stringTemplates/multipleEntriesWithSuffix.kt.after @@ -0,0 +1,10 @@ +// SUGGESTED_NAMES: s, getX +// PARAM_DESCRIPTOR: value-parameter val a: kotlin.Int defined in foo +// PARAM_TYPES: kotlin.Int +fun foo(a: Int): String { + val x = "_${s(a)}__" + val y = "_a$a:${a + 1}cd__" + return "${s(a)}ef" +} + +private fun s(a: Int) = "ab$a:${a + 1}cd" \ No newline at end of file diff --git a/idea/testData/refactoring/extractFunction/stringTemplates/rawTemplateWithSubstring.kt b/idea/testData/refactoring/extractFunction/stringTemplates/rawTemplateWithSubstring.kt new file mode 100644 index 00000000000..b45dbc90cc2 --- /dev/null +++ b/idea/testData/refactoring/extractFunction/stringTemplates/rawTemplateWithSubstring.kt @@ -0,0 +1,12 @@ +// SUGGESTED_NAMES: s, getX +// PARAM_DESCRIPTOR: value-parameter val a: kotlin.Int defined in foo +// PARAM_TYPES: kotlin.Int +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 """abc$a + :${a + 1}def""" +} \ No newline at end of file diff --git a/idea/testData/refactoring/extractFunction/stringTemplates/rawTemplateWithSubstring.kt.after b/idea/testData/refactoring/extractFunction/stringTemplates/rawTemplateWithSubstring.kt.after new file mode 100644 index 00000000000..dc6f0920546 --- /dev/null +++ b/idea/testData/refactoring/extractFunction/stringTemplates/rawTemplateWithSubstring.kt.after @@ -0,0 +1,13 @@ +// SUGGESTED_NAMES: s, getX +// PARAM_DESCRIPTOR: value-parameter val a: kotlin.Int defined in foo +// PARAM_TYPES: kotlin.Int +fun foo(a: Int): String { + val x = """_${s(a)}_""" + val y = "_$a:${a + 1}d_" + val z = """_c$a:${a + 1}d_""" + val u = "_c$a\n:${a + 1}d_" + return """ab${s(a)}ef""" +} + +private fun s(a: Int) = """c$a + :${a + 1}d""" \ No newline at end of file diff --git a/idea/testData/refactoring/extractFunction/stringTemplates/singleEntryPrefix.kt b/idea/testData/refactoring/extractFunction/stringTemplates/singleEntryPrefix.kt new file mode 100644 index 00000000000..bb8506efffc --- /dev/null +++ b/idea/testData/refactoring/extractFunction/stringTemplates/singleEntryPrefix.kt @@ -0,0 +1,7 @@ +// SUGGESTED_NAMES: s, getX +fun foo(a: Int): String { + val x = "xabc$a" + val y = "${a}abcx" + val z = "xacb$a" + return "abcdef" +} \ No newline at end of file diff --git a/idea/testData/refactoring/extractFunction/stringTemplates/singleEntryPrefix.kt.after b/idea/testData/refactoring/extractFunction/stringTemplates/singleEntryPrefix.kt.after new file mode 100644 index 00000000000..92610d1ab6e --- /dev/null +++ b/idea/testData/refactoring/extractFunction/stringTemplates/singleEntryPrefix.kt.after @@ -0,0 +1,9 @@ +// SUGGESTED_NAMES: s, getX +fun foo(a: Int): String { + val x = "x${s()}$a" + val y = "${a}${s()}x" + val z = "xacb$a" + return "${s()}def" +} + +private fun s() = "abc" \ No newline at end of file diff --git a/idea/testData/refactoring/extractFunction/stringTemplates/singleEntrySubstring.kt b/idea/testData/refactoring/extractFunction/stringTemplates/singleEntrySubstring.kt new file mode 100644 index 00000000000..16a3a56b15c --- /dev/null +++ b/idea/testData/refactoring/extractFunction/stringTemplates/singleEntrySubstring.kt @@ -0,0 +1,7 @@ +// SUGGESTED_NAMES: s, getX +fun foo(a: Int): String { + val x = "xcd$a" + val y = "${a}cdx" + val z = "xcf$a" + return "abcdef" +} \ No newline at end of file diff --git a/idea/testData/refactoring/extractFunction/stringTemplates/singleEntrySubstring.kt.after b/idea/testData/refactoring/extractFunction/stringTemplates/singleEntrySubstring.kt.after new file mode 100644 index 00000000000..210c9d5c0f7 --- /dev/null +++ b/idea/testData/refactoring/extractFunction/stringTemplates/singleEntrySubstring.kt.after @@ -0,0 +1,9 @@ +// SUGGESTED_NAMES: s, getX +fun foo(a: Int): String { + val x = "x${s()}$a" + val y = "${a}${s()}x" + val z = "xcf$a" + return "ab${s()}ef" +} + +private fun s() = "cd" \ No newline at end of file diff --git a/idea/testData/refactoring/extractFunction/stringTemplates/singleEntrySuffix.kt b/idea/testData/refactoring/extractFunction/stringTemplates/singleEntrySuffix.kt new file mode 100644 index 00000000000..c3111de2468 --- /dev/null +++ b/idea/testData/refactoring/extractFunction/stringTemplates/singleEntrySuffix.kt @@ -0,0 +1,7 @@ +// SUGGESTED_NAMES: s, getX +fun foo(a: Int): String { + val x = "xdef$a" + val y = "${a}defx" + val z = "xddf$a" + return "abcdef" +} \ No newline at end of file diff --git a/idea/testData/refactoring/extractFunction/stringTemplates/singleEntrySuffix.kt.after b/idea/testData/refactoring/extractFunction/stringTemplates/singleEntrySuffix.kt.after new file mode 100644 index 00000000000..0f7b73b7cae --- /dev/null +++ b/idea/testData/refactoring/extractFunction/stringTemplates/singleEntrySuffix.kt.after @@ -0,0 +1,9 @@ +// SUGGESTED_NAMES: s, getX +fun foo(a: Int): String { + val x = "x${s()}$a" + val y = "${a}${s()}x" + val z = "xddf$a" + return "abc${s()}" +} + +private fun s() = "def" \ No newline at end of file diff --git a/idea/testData/refactoring/introduceProperty/stringTemplates/brokenEntryWithBlockExpr.kt b/idea/testData/refactoring/introduceProperty/stringTemplates/brokenEntryWithBlockExpr.kt new file mode 100644 index 00000000000..ba92f111c54 --- /dev/null +++ b/idea/testData/refactoring/introduceProperty/stringTemplates/brokenEntryWithBlockExpr.kt @@ -0,0 +1,6 @@ +// EXTRACTION_TARGET: property with initializer +val prop = 1 + +fun foo(): String { + return "abc${prop}def" +} \ No newline at end of file diff --git a/idea/testData/refactoring/introduceProperty/stringTemplates/brokenEntryWithBlockExpr.kt.conflicts b/idea/testData/refactoring/introduceProperty/stringTemplates/brokenEntryWithBlockExpr.kt.conflicts new file mode 100644 index 00000000000..21617cc617d --- /dev/null +++ b/idea/testData/refactoring/introduceProperty/stringTemplates/brokenEntryWithBlockExpr.kt.conflicts @@ -0,0 +1 @@ +Cannot perform refactoring without an expression \ No newline at end of file diff --git a/idea/testData/refactoring/introduceProperty/stringTemplates/brokenEntryWithExpr.kt b/idea/testData/refactoring/introduceProperty/stringTemplates/brokenEntryWithExpr.kt new file mode 100644 index 00000000000..e0f5e75e69f --- /dev/null +++ b/idea/testData/refactoring/introduceProperty/stringTemplates/brokenEntryWithExpr.kt @@ -0,0 +1,6 @@ +// EXTRACTION_TARGET: property with initializer +val prop = 1 + +fun foo(): String { + return "abc$prop" +} \ No newline at end of file diff --git a/idea/testData/refactoring/introduceProperty/stringTemplates/brokenEntryWithExpr.kt.conflicts b/idea/testData/refactoring/introduceProperty/stringTemplates/brokenEntryWithExpr.kt.conflicts new file mode 100644 index 00000000000..21617cc617d --- /dev/null +++ b/idea/testData/refactoring/introduceProperty/stringTemplates/brokenEntryWithExpr.kt.conflicts @@ -0,0 +1 @@ +Cannot perform refactoring without an expression \ No newline at end of file diff --git a/idea/testData/refactoring/introduceProperty/stringTemplates/brokenEscapeEntry.kt b/idea/testData/refactoring/introduceProperty/stringTemplates/brokenEscapeEntry.kt new file mode 100644 index 00000000000..d0010dcff5f --- /dev/null +++ b/idea/testData/refactoring/introduceProperty/stringTemplates/brokenEscapeEntry.kt @@ -0,0 +1,6 @@ +// EXTRACTION_TARGET: property with initializer +val a = 1 + +fun foo(): String { + return "abc$a\ndef" +} \ No newline at end of file diff --git a/idea/testData/refactoring/introduceProperty/stringTemplates/brokenEscapeEntry.kt.conflicts b/idea/testData/refactoring/introduceProperty/stringTemplates/brokenEscapeEntry.kt.conflicts new file mode 100644 index 00000000000..21617cc617d --- /dev/null +++ b/idea/testData/refactoring/introduceProperty/stringTemplates/brokenEscapeEntry.kt.conflicts @@ -0,0 +1 @@ +Cannot perform refactoring without an expression \ No newline at end of file diff --git a/idea/testData/refactoring/introduceProperty/stringTemplates/extractFalse.kt b/idea/testData/refactoring/introduceProperty/stringTemplates/extractFalse.kt new file mode 100644 index 00000000000..b58c35ba67c --- /dev/null +++ b/idea/testData/refactoring/introduceProperty/stringTemplates/extractFalse.kt @@ -0,0 +1,7 @@ +// EXTRACTION_TARGET: property with initializer +fun foo(): String { + val x = "xyfalsez" + val y = "xyFalsez" + val z = false + return "abfalsedef" +} \ No newline at end of file diff --git a/idea/testData/refactoring/introduceProperty/stringTemplates/extractFalse.kt.after b/idea/testData/refactoring/introduceProperty/stringTemplates/extractFalse.kt.after new file mode 100644 index 00000000000..de6f1a07452 --- /dev/null +++ b/idea/testData/refactoring/introduceProperty/stringTemplates/extractFalse.kt.after @@ -0,0 +1,9 @@ +private val b = false + +// EXTRACTION_TARGET: property with initializer +fun foo(): String { + val x = "xy${b}z" + val y = "xyFalsez" + val z = false + return "ab${b}def" +} \ No newline at end of file diff --git a/idea/testData/refactoring/introduceProperty/stringTemplates/extractIntegerLiteral.kt b/idea/testData/refactoring/introduceProperty/stringTemplates/extractIntegerLiteral.kt new file mode 100644 index 00000000000..2c3ded98146 --- /dev/null +++ b/idea/testData/refactoring/introduceProperty/stringTemplates/extractIntegerLiteral.kt @@ -0,0 +1,8 @@ +// EXTRACTION_TARGET: property with initializer +fun foo(): String { + val x = "a1234_" + val y = "-4123a" + val z = "+1243a" + val u = 123 + return "ab123def" +} \ No newline at end of file diff --git a/idea/testData/refactoring/introduceProperty/stringTemplates/extractIntegerLiteral.kt.after b/idea/testData/refactoring/introduceProperty/stringTemplates/extractIntegerLiteral.kt.after new file mode 100644 index 00000000000..fbccc451708 --- /dev/null +++ b/idea/testData/refactoring/introduceProperty/stringTemplates/extractIntegerLiteral.kt.after @@ -0,0 +1,10 @@ +private val i = 123 + +// EXTRACTION_TARGET: property with initializer +fun foo(): String { + val x = "a${i}4_" + val y = "-4${i}a" + val z = "+1243a" + val u = 123 + return "ab${i}def" +} \ No newline at end of file diff --git a/idea/testData/refactoring/introduceProperty/stringTemplates/extractTrue.kt b/idea/testData/refactoring/introduceProperty/stringTemplates/extractTrue.kt new file mode 100644 index 00000000000..811029e44b6 --- /dev/null +++ b/idea/testData/refactoring/introduceProperty/stringTemplates/extractTrue.kt @@ -0,0 +1,7 @@ +// EXTRACTION_TARGET: property with initializer +fun foo(): String { + val x = "atrue123" + val x = "aTRUE123" + val z = true + return "abtruedef" +} \ No newline at end of file diff --git a/idea/testData/refactoring/introduceProperty/stringTemplates/extractTrue.kt.after b/idea/testData/refactoring/introduceProperty/stringTemplates/extractTrue.kt.after new file mode 100644 index 00000000000..4abdb696184 --- /dev/null +++ b/idea/testData/refactoring/introduceProperty/stringTemplates/extractTrue.kt.after @@ -0,0 +1,9 @@ +private val b = true + +// EXTRACTION_TARGET: property with initializer +fun foo(): String { + val x = "a${b}123" + val x = "aTRUE123" + val z = true + return "ab${b}def" +} \ No newline at end of file diff --git a/idea/testData/refactoring/introduceProperty/stringTemplates/fullContent.kt b/idea/testData/refactoring/introduceProperty/stringTemplates/fullContent.kt new file mode 100644 index 00000000000..6c4effd3abe --- /dev/null +++ b/idea/testData/refactoring/introduceProperty/stringTemplates/fullContent.kt @@ -0,0 +1,9 @@ +// EXTRACTION_TARGET: property with initializer +val a = 1 + +fun foo(): String { + val x = "abc$a" + val y = "abc${a}" + val z = "abc{$a}def" + return "abc$a" + "def" +} \ No newline at end of file diff --git a/idea/testData/refactoring/introduceProperty/stringTemplates/fullContent.kt.after b/idea/testData/refactoring/introduceProperty/stringTemplates/fullContent.kt.after new file mode 100644 index 00000000000..8cb1c7b2d2d --- /dev/null +++ b/idea/testData/refactoring/introduceProperty/stringTemplates/fullContent.kt.after @@ -0,0 +1,11 @@ +// EXTRACTION_TARGET: property with initializer +val a = 1 + +private val s = "abc$a" + +fun foo(): String { + val x = s + val y = s + val z = "abc{$a}def" + return s + "def" +} \ No newline at end of file diff --git a/idea/testData/refactoring/introduceProperty/stringTemplates/fullEntryWithBlockExpr.kt b/idea/testData/refactoring/introduceProperty/stringTemplates/fullEntryWithBlockExpr.kt new file mode 100644 index 00000000000..cb05b7cc177 --- /dev/null +++ b/idea/testData/refactoring/introduceProperty/stringTemplates/fullEntryWithBlockExpr.kt @@ -0,0 +1,9 @@ +// EXTRACTION_TARGET: property with initializer +val a = 1 + +fun foo(): String { + val x = "-${a + 1}" + val y = "x${a + 1}y" + val z = "x${a - 1}y" + return "abc${a + 1}def" +} \ No newline at end of file diff --git a/idea/testData/refactoring/introduceProperty/stringTemplates/fullEntryWithBlockExpr.kt.after b/idea/testData/refactoring/introduceProperty/stringTemplates/fullEntryWithBlockExpr.kt.after new file mode 100644 index 00000000000..51e2a1a33a7 --- /dev/null +++ b/idea/testData/refactoring/introduceProperty/stringTemplates/fullEntryWithBlockExpr.kt.after @@ -0,0 +1,11 @@ +// EXTRACTION_TARGET: property with initializer +val a = 1 + +private val i = a + 1 + +fun foo(): String { + val x = "-$i" + val y = "x${i}y" + val z = "x${a - 1}y" + return "abc${i}def" +} \ No newline at end of file diff --git a/idea/testData/refactoring/introduceProperty/stringTemplates/fullEntryWithSimpleName.kt b/idea/testData/refactoring/introduceProperty/stringTemplates/fullEntryWithSimpleName.kt new file mode 100644 index 00000000000..abef3e4cf6a --- /dev/null +++ b/idea/testData/refactoring/introduceProperty/stringTemplates/fullEntryWithSimpleName.kt @@ -0,0 +1,9 @@ +// EXTRACTION_TARGET: property with initializer +val a = 1 + +fun foo(): String { + val x = "-$a" + val y = "x${a}y" + val z = "x$ay" + return "abc${a}def" +} \ No newline at end of file diff --git a/idea/testData/refactoring/introduceProperty/stringTemplates/fullEntryWithSimpleName.kt.after b/idea/testData/refactoring/introduceProperty/stringTemplates/fullEntryWithSimpleName.kt.after new file mode 100644 index 00000000000..123413ebf88 --- /dev/null +++ b/idea/testData/refactoring/introduceProperty/stringTemplates/fullEntryWithSimpleName.kt.after @@ -0,0 +1,11 @@ +// EXTRACTION_TARGET: property with initializer +val a = 1 + +private val i = a + +fun foo(): String { + val x = "-$i" + val y = "x${i}y" + val z = "x$ay" + return "abc${i}def" +} \ No newline at end of file diff --git a/idea/testData/refactoring/introduceProperty/stringTemplates/multipleEntriesWithPrefix.kt b/idea/testData/refactoring/introduceProperty/stringTemplates/multipleEntriesWithPrefix.kt new file mode 100644 index 00000000000..c50934eaa79 --- /dev/null +++ b/idea/testData/refactoring/introduceProperty/stringTemplates/multipleEntriesWithPrefix.kt @@ -0,0 +1,8 @@ +// EXTRACTION_TARGET: property with initializer +val a = 1 + +fun foo(): String { + val x = "+cd$a:${a + 1}efg" + val y = "+cd$a${a + 1}efg" + return "abcd$a:${a + 1}ef" +} \ No newline at end of file diff --git a/idea/testData/refactoring/introduceProperty/stringTemplates/multipleEntriesWithPrefix.kt.after b/idea/testData/refactoring/introduceProperty/stringTemplates/multipleEntriesWithPrefix.kt.after new file mode 100644 index 00000000000..96fd37312e1 --- /dev/null +++ b/idea/testData/refactoring/introduceProperty/stringTemplates/multipleEntriesWithPrefix.kt.after @@ -0,0 +1,10 @@ +// EXTRACTION_TARGET: property with initializer +val a = 1 + +private val s = "cd$a:${a + 1}ef" + +fun foo(): String { + val x = "+${s}g" + val y = "+cd$a${a + 1}efg" + return "ab$s" +} \ No newline at end of file diff --git a/idea/testData/refactoring/introduceProperty/stringTemplates/multipleEntriesWithSubstring.kt b/idea/testData/refactoring/introduceProperty/stringTemplates/multipleEntriesWithSubstring.kt new file mode 100644 index 00000000000..b568baa46f7 --- /dev/null +++ b/idea/testData/refactoring/introduceProperty/stringTemplates/multipleEntriesWithSubstring.kt @@ -0,0 +1,8 @@ +// EXTRACTION_TARGET: property with initializer +val a = 1 + +fun foo(): String { + val x = "_c$a:${a + 1}d_" + val y = "_$a:${a + 1}d_" + return "abc$a:${a + 1}def" +} \ No newline at end of file diff --git a/idea/testData/refactoring/introduceProperty/stringTemplates/multipleEntriesWithSubstring.kt.after b/idea/testData/refactoring/introduceProperty/stringTemplates/multipleEntriesWithSubstring.kt.after new file mode 100644 index 00000000000..b908e1095d5 --- /dev/null +++ b/idea/testData/refactoring/introduceProperty/stringTemplates/multipleEntriesWithSubstring.kt.after @@ -0,0 +1,10 @@ +// EXTRACTION_TARGET: property with initializer +val a = 1 + +private val s = "c$a:${a + 1}d" + +fun foo(): String { + val x = "_${s}_" + val y = "_$a:${a + 1}d_" + return "ab${s}ef" +} \ No newline at end of file diff --git a/idea/testData/refactoring/introduceProperty/stringTemplates/multipleEntriesWithSuffix.kt b/idea/testData/refactoring/introduceProperty/stringTemplates/multipleEntriesWithSuffix.kt new file mode 100644 index 00000000000..50fba655d01 --- /dev/null +++ b/idea/testData/refactoring/introduceProperty/stringTemplates/multipleEntriesWithSuffix.kt @@ -0,0 +1,8 @@ +// EXTRACTION_TARGET: property with initializer +val a = 1 + +fun foo(): String { + val x = "_ab$a:${a + 1}cd__" + val y = "_a$a:${a + 1}cd__" + return "ab$a:${a + 1}cdef" +} \ No newline at end of file diff --git a/idea/testData/refactoring/introduceProperty/stringTemplates/multipleEntriesWithSuffix.kt.after b/idea/testData/refactoring/introduceProperty/stringTemplates/multipleEntriesWithSuffix.kt.after new file mode 100644 index 00000000000..224bde8ba10 --- /dev/null +++ b/idea/testData/refactoring/introduceProperty/stringTemplates/multipleEntriesWithSuffix.kt.after @@ -0,0 +1,10 @@ +// EXTRACTION_TARGET: property with initializer +val a = 1 + +private val s = "ab$a:${a + 1}cd" + +fun foo(): String { + val x = "_${s}__" + val y = "_a$a:${a + 1}cd__" + return "${s}ef" +} \ No newline at end of file diff --git a/idea/testData/refactoring/introduceProperty/stringTemplates/rawTemplateWithSubstring.kt b/idea/testData/refactoring/introduceProperty/stringTemplates/rawTemplateWithSubstring.kt new file mode 100644 index 00000000000..e04013f6561 --- /dev/null +++ b/idea/testData/refactoring/introduceProperty/stringTemplates/rawTemplateWithSubstring.kt @@ -0,0 +1,12 @@ +// EXTRACTION_TARGET: property with initializer +val a = 1 + +fun foo(): 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 """abc$a + :${a + 1}def""" +} \ No newline at end of file diff --git a/idea/testData/refactoring/introduceProperty/stringTemplates/rawTemplateWithSubstring.kt.after b/idea/testData/refactoring/introduceProperty/stringTemplates/rawTemplateWithSubstring.kt.after new file mode 100644 index 00000000000..ed41a2d9b7c --- /dev/null +++ b/idea/testData/refactoring/introduceProperty/stringTemplates/rawTemplateWithSubstring.kt.after @@ -0,0 +1,13 @@ +// EXTRACTION_TARGET: property with initializer +val a = 1 + +private val s = """c$a + :${a + 1}d""" + +fun foo(): 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""" +} \ No newline at end of file diff --git a/idea/testData/refactoring/introduceProperty/stringTemplates/singleEntryPrefix.kt b/idea/testData/refactoring/introduceProperty/stringTemplates/singleEntryPrefix.kt new file mode 100644 index 00000000000..94f7c604f76 --- /dev/null +++ b/idea/testData/refactoring/introduceProperty/stringTemplates/singleEntryPrefix.kt @@ -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 "abcdef" +} \ No newline at end of file diff --git a/idea/testData/refactoring/introduceProperty/stringTemplates/singleEntryPrefix.kt.after b/idea/testData/refactoring/introduceProperty/stringTemplates/singleEntryPrefix.kt.after new file mode 100644 index 00000000000..b7d60bb1c41 --- /dev/null +++ b/idea/testData/refactoring/introduceProperty/stringTemplates/singleEntryPrefix.kt.after @@ -0,0 +1,11 @@ +// EXTRACTION_TARGET: property with initializer +val a = 1 + +private val s = "abc" + +fun foo(): String { + val x = "x$s$a" + val y = "${a}${s}x" + val z = "xacb$a" + return "${s}def" +} \ No newline at end of file diff --git a/idea/testData/refactoring/introduceProperty/stringTemplates/singleEntrySubstring.kt b/idea/testData/refactoring/introduceProperty/stringTemplates/singleEntrySubstring.kt new file mode 100644 index 00000000000..b95ef5df721 --- /dev/null +++ b/idea/testData/refactoring/introduceProperty/stringTemplates/singleEntrySubstring.kt @@ -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 "abcdef" +} \ No newline at end of file diff --git a/idea/testData/refactoring/introduceProperty/stringTemplates/singleEntrySubstring.kt.after b/idea/testData/refactoring/introduceProperty/stringTemplates/singleEntrySubstring.kt.after new file mode 100644 index 00000000000..7900d6081f9 --- /dev/null +++ b/idea/testData/refactoring/introduceProperty/stringTemplates/singleEntrySubstring.kt.after @@ -0,0 +1,11 @@ +// EXTRACTION_TARGET: property with initializer +val a = 1 + +private val s = "cd" + +fun foo(): String { + val x = "x$s$a" + val y = "${a}${s}x" + val z = "xcf$a" + return "ab${s}ef" +} \ No newline at end of file diff --git a/idea/testData/refactoring/introduceProperty/stringTemplates/singleEntrySuffix.kt b/idea/testData/refactoring/introduceProperty/stringTemplates/singleEntrySuffix.kt new file mode 100644 index 00000000000..06f3236c0be --- /dev/null +++ b/idea/testData/refactoring/introduceProperty/stringTemplates/singleEntrySuffix.kt @@ -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 "abcdef" +} \ No newline at end of file diff --git a/idea/testData/refactoring/introduceProperty/stringTemplates/singleEntrySuffix.kt.after b/idea/testData/refactoring/introduceProperty/stringTemplates/singleEntrySuffix.kt.after new file mode 100644 index 00000000000..ad13f30eaad --- /dev/null +++ b/idea/testData/refactoring/introduceProperty/stringTemplates/singleEntrySuffix.kt.after @@ -0,0 +1,11 @@ +// EXTRACTION_TARGET: property with initializer +val a = 1 + +private val s = "def" + +fun foo(): String { + val x = "x$s$a" + val y = "${a}${s}x" + val z = "xddf$a" + return "abc$s" +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/refactoring/introduce/ExtractionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/refactoring/introduce/ExtractionTestGenerated.java index 2f0a0c80f16..5a0da257ea1 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/refactoring/introduce/ExtractionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/refactoring/introduce/ExtractionTestGenerated.java @@ -2488,6 +2488,111 @@ public class ExtractionTestGenerated extends AbstractExtractionTest { } } + @TestMetadata("idea/testData/refactoring/extractFunction/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/extractFunction/stringTemplates"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("brokenEntryWithBlockExpr.kt") + public void testBrokenEntryWithBlockExpr() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/stringTemplates/brokenEntryWithBlockExpr.kt"); + doExtractFunctionTest(fileName); + } + + @TestMetadata("brokenEntryWithExpr.kt") + public void testBrokenEntryWithExpr() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/stringTemplates/brokenEntryWithExpr.kt"); + doExtractFunctionTest(fileName); + } + + @TestMetadata("brokenEscapeEntry.kt") + public void testBrokenEscapeEntry() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/stringTemplates/brokenEscapeEntry.kt"); + doExtractFunctionTest(fileName); + } + + @TestMetadata("extractFalse.kt") + public void testExtractFalse() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/stringTemplates/extractFalse.kt"); + doExtractFunctionTest(fileName); + } + + @TestMetadata("extractIntegerLiteral.kt") + public void testExtractIntegerLiteral() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/stringTemplates/extractIntegerLiteral.kt"); + doExtractFunctionTest(fileName); + } + + @TestMetadata("extractTrue.kt") + public void testExtractTrue() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/stringTemplates/extractTrue.kt"); + doExtractFunctionTest(fileName); + } + + @TestMetadata("fullContent.kt") + public void testFullContent() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/stringTemplates/fullContent.kt"); + doExtractFunctionTest(fileName); + } + + @TestMetadata("fullEntryWithBlockExpr.kt") + public void testFullEntryWithBlockExpr() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/stringTemplates/fullEntryWithBlockExpr.kt"); + doExtractFunctionTest(fileName); + } + + @TestMetadata("fullEntryWithSimpleName.kt") + public void testFullEntryWithSimpleName() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/stringTemplates/fullEntryWithSimpleName.kt"); + doExtractFunctionTest(fileName); + } + + @TestMetadata("multipleEntriesWithPrefix.kt") + public void testMultipleEntriesWithPrefix() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/stringTemplates/multipleEntriesWithPrefix.kt"); + doExtractFunctionTest(fileName); + } + + @TestMetadata("multipleEntriesWithSubstring.kt") + public void testMultipleEntriesWithSubstring() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/stringTemplates/multipleEntriesWithSubstring.kt"); + doExtractFunctionTest(fileName); + } + + @TestMetadata("multipleEntriesWithSuffix.kt") + public void testMultipleEntriesWithSuffix() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/stringTemplates/multipleEntriesWithSuffix.kt"); + doExtractFunctionTest(fileName); + } + + @TestMetadata("rawTemplateWithSubstring.kt") + public void testRawTemplateWithSubstring() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/stringTemplates/rawTemplateWithSubstring.kt"); + doExtractFunctionTest(fileName); + } + + @TestMetadata("singleEntryPrefix.kt") + public void testSingleEntryPrefix() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/stringTemplates/singleEntryPrefix.kt"); + doExtractFunctionTest(fileName); + } + + @TestMetadata("singleEntrySubstring.kt") + public void testSingleEntrySubstring() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/stringTemplates/singleEntrySubstring.kt"); + doExtractFunctionTest(fileName); + } + + @TestMetadata("singleEntrySuffix.kt") + public void testSingleEntrySuffix() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/stringTemplates/singleEntrySuffix.kt"); + doExtractFunctionTest(fileName); + } + } + @TestMetadata("idea/testData/refactoring/extractFunction/typeParameters") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) @@ -2787,6 +2892,111 @@ public class ExtractionTestGenerated extends AbstractExtractionTest { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceProperty/typeParameterResolvableInTargetScope.kt"); doIntroducePropertyTest(fileName); } + + @TestMetadata("idea/testData/refactoring/introduceProperty/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/introduceProperty/stringTemplates"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("brokenEntryWithBlockExpr.kt") + public void testBrokenEntryWithBlockExpr() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceProperty/stringTemplates/brokenEntryWithBlockExpr.kt"); + doIntroducePropertyTest(fileName); + } + + @TestMetadata("brokenEntryWithExpr.kt") + public void testBrokenEntryWithExpr() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceProperty/stringTemplates/brokenEntryWithExpr.kt"); + doIntroducePropertyTest(fileName); + } + + @TestMetadata("brokenEscapeEntry.kt") + public void testBrokenEscapeEntry() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceProperty/stringTemplates/brokenEscapeEntry.kt"); + doIntroducePropertyTest(fileName); + } + + @TestMetadata("extractFalse.kt") + public void testExtractFalse() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceProperty/stringTemplates/extractFalse.kt"); + doIntroducePropertyTest(fileName); + } + + @TestMetadata("extractIntegerLiteral.kt") + public void testExtractIntegerLiteral() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceProperty/stringTemplates/extractIntegerLiteral.kt"); + doIntroducePropertyTest(fileName); + } + + @TestMetadata("extractTrue.kt") + public void testExtractTrue() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceProperty/stringTemplates/extractTrue.kt"); + doIntroducePropertyTest(fileName); + } + + @TestMetadata("fullContent.kt") + public void testFullContent() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceProperty/stringTemplates/fullContent.kt"); + doIntroducePropertyTest(fileName); + } + + @TestMetadata("fullEntryWithBlockExpr.kt") + public void testFullEntryWithBlockExpr() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceProperty/stringTemplates/fullEntryWithBlockExpr.kt"); + doIntroducePropertyTest(fileName); + } + + @TestMetadata("fullEntryWithSimpleName.kt") + public void testFullEntryWithSimpleName() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceProperty/stringTemplates/fullEntryWithSimpleName.kt"); + doIntroducePropertyTest(fileName); + } + + @TestMetadata("multipleEntriesWithPrefix.kt") + public void testMultipleEntriesWithPrefix() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceProperty/stringTemplates/multipleEntriesWithPrefix.kt"); + doIntroducePropertyTest(fileName); + } + + @TestMetadata("multipleEntriesWithSubstring.kt") + public void testMultipleEntriesWithSubstring() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceProperty/stringTemplates/multipleEntriesWithSubstring.kt"); + doIntroducePropertyTest(fileName); + } + + @TestMetadata("multipleEntriesWithSuffix.kt") + public void testMultipleEntriesWithSuffix() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceProperty/stringTemplates/multipleEntriesWithSuffix.kt"); + doIntroducePropertyTest(fileName); + } + + @TestMetadata("rawTemplateWithSubstring.kt") + public void testRawTemplateWithSubstring() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceProperty/stringTemplates/rawTemplateWithSubstring.kt"); + doIntroducePropertyTest(fileName); + } + + @TestMetadata("singleEntryPrefix.kt") + public void testSingleEntryPrefix() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceProperty/stringTemplates/singleEntryPrefix.kt"); + doIntroducePropertyTest(fileName); + } + + @TestMetadata("singleEntrySubstring.kt") + public void testSingleEntrySubstring() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceProperty/stringTemplates/singleEntrySubstring.kt"); + doIntroducePropertyTest(fileName); + } + + @TestMetadata("singleEntrySuffix.kt") + public void testSingleEntrySuffix() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceProperty/stringTemplates/singleEntrySuffix.kt"); + doIntroducePropertyTest(fileName); + } + } } @TestMetadata("idea/testData/refactoring/introduceParameter")