Extract Function/Introduce Property: Support extraction of string template fragments
This commit is contained in:
@@ -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 }
|
||||
|
||||
|
||||
+1
-1
@@ -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
|
||||
}
|
||||
|
||||
|
||||
+31
-27
@@ -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
|
||||
|
||||
+3
-2
@@ -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)
|
||||
|
||||
+16
-12
@@ -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
|
||||
|
||||
+23
-16
@@ -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 })
|
||||
|
||||
+2
-2
@@ -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!!
|
||||
}
|
||||
}
|
||||
+9
-29
@@ -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!!
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
fun foo(param: Int): String {
|
||||
return "<selection>abc${pa</selection>ram}def"
|
||||
}
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
Cannot perform refactoring without an expression
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
fun foo(param: Int): String {
|
||||
return "<selection>abc$pa</selection>ram"
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
Cannot perform refactoring without an expression
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
fun foo(a: Int): String {
|
||||
return "<selection>abc$a\</selection>ndef"
|
||||
}
|
||||
+1
@@ -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"
|
||||
}
|
||||
+9
@@ -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
|
||||
+8
@@ -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"
|
||||
}
|
||||
+10
@@ -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"
|
||||
}
|
||||
+9
@@ -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"
|
||||
}
|
||||
+10
@@ -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"
|
||||
+8
@@ -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"
|
||||
}
|
||||
+10
@@ -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
|
||||
+8
@@ -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"
|
||||
}
|
||||
+10
@@ -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
|
||||
+8
@@ -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>"
|
||||
}
|
||||
Vendored
+10
@@ -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"
|
||||
+8
@@ -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"
|
||||
}
|
||||
Vendored
+10
@@ -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"
|
||||
+8
@@ -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"
|
||||
}
|
||||
Vendored
+10
@@ -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"
|
||||
+12
@@ -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"""
|
||||
}
|
||||
Vendored
+13
@@ -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"""
|
||||
+7
@@ -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"
|
||||
}
|
||||
+9
@@ -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"
|
||||
+7
@@ -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"
|
||||
}
|
||||
+9
@@ -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"
|
||||
+7
@@ -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>"
|
||||
}
|
||||
+9
@@ -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"
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
// EXTRACTION_TARGET: property with initializer
|
||||
val prop = 1
|
||||
|
||||
fun foo(): String {
|
||||
return "<selection>abc${pr</selection>op}def"
|
||||
}
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
Cannot perform refactoring without an expression
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
// EXTRACTION_TARGET: property with initializer
|
||||
val prop = 1
|
||||
|
||||
fun foo(): String {
|
||||
return "<selection>abc$pr</selection>op"
|
||||
}
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
Cannot perform refactoring without an expression
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
// EXTRACTION_TARGET: property with initializer
|
||||
val a = 1
|
||||
|
||||
fun foo(): String {
|
||||
return "<selection>abc$a\</selection>ndef"
|
||||
}
|
||||
+1
@@ -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"
|
||||
}
|
||||
+9
@@ -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"
|
||||
}
|
||||
+8
@@ -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"
|
||||
}
|
||||
+10
@@ -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"
|
||||
}
|
||||
+9
@@ -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"
|
||||
}
|
||||
+11
@@ -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"
|
||||
}
|
||||
+9
@@ -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"
|
||||
}
|
||||
Vendored
+11
@@ -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"
|
||||
}
|
||||
+9
@@ -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"
|
||||
}
|
||||
Vendored
+11
@@ -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"
|
||||
}
|
||||
+8
@@ -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>"
|
||||
}
|
||||
Vendored
+10
@@ -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"
|
||||
}
|
||||
Vendored
+8
@@ -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"
|
||||
}
|
||||
Vendored
+10
@@ -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"
|
||||
}
|
||||
+8
@@ -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"
|
||||
}
|
||||
Vendored
+10
@@ -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"
|
||||
}
|
||||
+12
@@ -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"""
|
||||
}
|
||||
Vendored
+13
@@ -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"""
|
||||
}
|
||||
+9
@@ -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"
|
||||
}
|
||||
+11
@@ -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"
|
||||
}
|
||||
+9
@@ -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"
|
||||
}
|
||||
+11
@@ -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"
|
||||
}
|
||||
+9
@@ -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>"
|
||||
}
|
||||
+11
@@ -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"
|
||||
}
|
||||
+210
@@ -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")
|
||||
|
||||
Reference in New Issue
Block a user