Extract Function/Introduce Property: Support extraction of string template fragments

This commit is contained in:
Alexey Sedunov
2015-11-18 15:11:10 +03:00
parent f4b4b023a3
commit 741489f4a5
75 changed files with 846 additions and 91 deletions
@@ -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<KtStringTemplateEntry>
get() = sequence(startEntry) { if (it != endEntry) it.nextSiblingOfSameType() else null }
@@ -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
}
@@ -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<PsiElement> = originalRange.elements
val physicalElements = originalElements.map { it.substringContextOrThis }
val substringInfo: ExtractableSubstringInfo?
get() = (originalElements.singleOrNull() as? KtExpression)?.extractableSubstringInfo
val insertBefore: Boolean = options.extractAsProperty
|| targetSibling.getStrictParentOfType<KtDeclaration>()?.let {
it is KtDeclarationWithBody || it is KtAnonymousInitializer
} ?: false
fun getExpressions(): List<KtExpression> = originalElements.filterIsInstance<KtExpression>()
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<KtExpression>()
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<KtSimpleNameExpression>()
} ?: 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
@@ -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<RangeHighlighter>()
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)
@@ -127,11 +127,11 @@ private fun List<Instruction>.getVarDescriptorsAccessedAfterwards(bindingContext
private fun List<Instruction>.getExitPoints(): List<Instruction> =
filter { localInstruction -> localInstruction.nextInstructions.any { it !in this } }
private fun List<Instruction>.getResultTypeAndExpressions(
private fun ExtractionData.getResultTypeAndExpressions(
instructions: List<Instruction>,
bindingContext: BindingContext,
targetScope: LexicalScope?,
options: ExtractionOptions,
module: ModuleDescriptor
options: ExtractionOptions, module: ModuleDescriptor
): Pair<KotlinType, List<KtExpression>> {
fun instructionToExpression(instruction: Instruction, unwrapReturn: Boolean): KtExpression? {
return when (instruction) {
@@ -146,6 +146,10 @@ private fun List<Instruction>.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<Instruction>.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<KtDeclaration>() == enclosingDeclaration) {
declarationsOutOfScope.add(target)
}
@@ -605,7 +609,7 @@ private fun ExtractionData.checkDeclarationsMovingOutOfScope(
private fun ExtractionData.getLocalInstructions(pseudocode: Pseudocode): List<Instruction> {
val instructions = ArrayList<Instruction>()
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<St
functionNames.addAll(KotlinNameSuggester.suggestNamesByType(returnType, validator))
}
getExpressions().singleOrNull()?.let { expr ->
expressions.singleOrNull()?.let { expr ->
val property = expr.getStrictParentOfType<KtProperty>()
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
@@ -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<DuplicateInfo> {
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<String>) {
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<KtFunctionLiteralArgument>()!!
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<PsiElement>()) { it.range.elements.first() }
val anchorCandidates = duplicates.mapTo(ArrayList<PsiElement>()) { it.range.elements.first().substringContextOrThis }
anchorCandidates.add(targetSibling)
if (targetSibling is KtEnumEntry) {
anchorCandidates.add(targetSibling.siblings().last { it is KtEnumEntry })
@@ -88,7 +88,7 @@ internal fun ExtractionData.inferParametersInfo(
val varNameValidator = NewDeclarationNameValidator(
commonParent.getNonStrictParentOfType<KtExpression>()!!,
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
}
@@ -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!!
}
}
@@ -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<KtFunctionLiteralArgument>()!!
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<KtFunctionLiteralArgument>()!!
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)
@@ -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 <ListType : KtElement> replaceListPsiAndKeepDelimiters(
fun <T> Pass(body: (T) -> Unit) = object: Pass<T>() {
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!!
}
@@ -0,0 +1,3 @@
fun foo(param: Int): String {
return "<selection>abc${pa</selection>ram}def"
}
@@ -0,0 +1 @@
Cannot perform refactoring without an expression
@@ -0,0 +1,3 @@
fun foo(param: Int): String {
return "<selection>abc$pa</selection>ram"
}
@@ -0,0 +1 @@
Cannot perform refactoring without an expression
@@ -0,0 +1,3 @@
fun foo(a: Int): String {
return "<selection>abc$a\</selection>ndef"
}
@@ -0,0 +1 @@
Cannot perform refactoring without an expression
@@ -0,0 +1,7 @@
// SUGGESTED_NAMES: b, getX
fun foo(param: Int): String {
val x = "xyfalsez"
val y = "xyFalsez"
val z = false
return "ab<selection>false</selection>def"
}
@@ -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
@@ -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 "ab<selection>123</selection>def"
}
@@ -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
@@ -0,0 +1,7 @@
// SUGGESTED_NAMES: b, getX
fun foo(param: Int): String {
val x = "atrue123"
val x = "aTRUE123"
val z = true
return "ab<selection>true</selection>def"
}
@@ -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
@@ -0,0 +1,8 @@
// PARAM_DESCRIPTOR: value-parameter val a: kotlin.Int defined in foo
// PARAM_TYPES: kotlin.Int, Number, Comparable<Int>, java.io.Serializable, Any
fun foo(a: Int): String {
val x = "abc$a"
val y = "abc${a}"
val z = "abc{$a}def"
return "<selection>abc$a</selection>" + "def"
}
@@ -0,0 +1,10 @@
// PARAM_DESCRIPTOR: value-parameter val a: kotlin.Int defined in foo
// PARAM_TYPES: kotlin.Int, Number, Comparable<Int>, 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"
@@ -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<selection>${a + 1}</selection>def"
}
@@ -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
@@ -0,0 +1,8 @@
// PARAM_DESCRIPTOR: value-parameter val a: kotlin.Int defined in foo
// PARAM_TYPES: kotlin.Int, Number, Comparable<Int>, java.io.Serializable, Any
fun foo(a: Int): String {
val x = "-$a"
val y = "x${a}y"
val z = "x$ay"
return "abc<selection>${a}</selection>def"
}
@@ -0,0 +1,10 @@
// PARAM_DESCRIPTOR: value-parameter val a: kotlin.Int defined in foo
// PARAM_TYPES: kotlin.Int, Number, Comparable<Int>, 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
@@ -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 "ab<selection>cd$a:${a + 1}ef</selection>"
}
@@ -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"
@@ -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 "ab<selection>c$a:${a + 1}d</selection>ef"
}
@@ -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"
@@ -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 "<selection>ab$a:${a + 1}cd</selection>ef"
}
@@ -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"
@@ -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 """ab<selection>c$a
:${a + 1}d</selection>ef"""
}
@@ -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"""
@@ -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 "<selection>abc</selection>def"
}
@@ -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"
@@ -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 "ab<selection>cd</selection>ef"
}
@@ -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"
@@ -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 "abc<selection>def</selection>"
}
@@ -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"
@@ -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,7 @@
// EXTRACTION_TARGET: property with initializer
fun foo(): String {
val x = "xyfalsez"
val y = "xyFalsez"
val z = false
return "ab<selection>false</selection>def"
}
@@ -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"
}
@@ -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 "ab<selection>123</selection>def"
}
@@ -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"
}
@@ -0,0 +1,7 @@
// EXTRACTION_TARGET: property with initializer
fun foo(): String {
val x = "atrue123"
val x = "aTRUE123"
val z = true
return "ab<selection>true</selection>def"
}
@@ -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"
}
@@ -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 "<selection>abc$a</selection>" + "def"
}
@@ -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"
}
@@ -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<selection>${a + 1}</selection>def"
}
@@ -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"
}
@@ -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<selection>${a}</selection>def"
}
@@ -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"
}
@@ -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 "ab<selection>cd$a:${a + 1}ef</selection>"
}
@@ -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"
}
@@ -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 "ab<selection>c$a:${a + 1}d</selection>ef"
}
@@ -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"
}
@@ -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 "<selection>ab$a:${a + 1}cd</selection>ef"
}
@@ -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"
}
@@ -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 """ab<selection>c$a
:${a + 1}d</selection>ef"""
}
@@ -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"""
}
@@ -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 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 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 val s = "def"
fun foo(): String {
val x = "x$s$a"
val y = "${a}${s}x"
val z = "xddf$a"
return "abc$s"
}
@@ -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")