Refactoring: Introduce ExtractionEngine class
This commit is contained in:
+6
-10
@@ -17,28 +17,22 @@
|
||||
package org.jetbrains.kotlin.idea.debugger.evaluate
|
||||
|
||||
import com.intellij.psi.PsiFile
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.AnalysisResult
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.AnalysisResult.ErrorMessage
|
||||
import org.jetbrains.kotlin.idea.codeInsight.CodeInsightUtils
|
||||
import org.jetbrains.kotlin.idea.refactoring.createTempCopy
|
||||
import org.jetbrains.kotlin.psi.codeFragmentUtil.suppressDiagnosticsInDebugMode
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.ExtractionData
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.performAnalysis
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.AnalysisResult.Status
|
||||
import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.validate
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.ExtractionOptions
|
||||
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
||||
import com.intellij.psi.PsiManager
|
||||
import com.intellij.psi.impl.PsiModificationTrackerImpl
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.idea.intentions.InsertExplicitTypeArguments
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.ExtractionGeneratorOptions
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.generateDeclaration
|
||||
import org.jetbrains.kotlin.idea.util.psi.patternMatching.toRange
|
||||
import org.jetbrains.kotlin.idea.actions.internal.KotlinInternalMode
|
||||
import org.jetbrains.kotlin.psi.psiUtil.replaced
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.*
|
||||
|
||||
fun getFunctionForExtractedFragment(
|
||||
codeFragment: JetCodeFragment,
|
||||
@@ -106,9 +100,11 @@ fun getFunctionForExtractedFragment(
|
||||
throw EvaluateExceptionUtil.createEvaluateException("Following declarations are unavailable in debug scope: ${validationResult.conflicts.keySet().map { it.getText() }.joinToString(",")}")
|
||||
}
|
||||
|
||||
return validationResult.descriptor
|
||||
.generateDeclaration(ExtractionGeneratorOptions(inTempFile = true, flexibleTypesAllowed = true))
|
||||
.declaration as JetNamedFunction
|
||||
val config = ExtractionGeneratorConfiguration(
|
||||
validationResult.descriptor,
|
||||
ExtractionGeneratorOptions(inTempFile = true, flexibleTypesAllowed = true)
|
||||
)
|
||||
return config.generateDeclaration().declaration as JetNamedFunction
|
||||
}
|
||||
|
||||
return runReadAction { generateFunction() }
|
||||
|
||||
@@ -24,7 +24,6 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.idea.refactoring.changeSignature.JetChangeSignatureHandler;
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.extractFunction.ExtractKotlinFunctionHandler;
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.extractFunction.ExtractKotlinFunctionHandlerHelper;
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.introduceVariable.KotlinIntroduceVariableHandler;
|
||||
import org.jetbrains.kotlin.idea.refactoring.safeDelete.SafeDeletePackage;
|
||||
import org.jetbrains.kotlin.psi.*;
|
||||
@@ -47,7 +46,7 @@ public class JetRefactoringSupportProvider extends RefactoringSupportProvider {
|
||||
|
||||
@NotNull
|
||||
public RefactoringActionHandler getExtractFunctionToScopeHandler() {
|
||||
return new ExtractKotlinFunctionHandler(true, ExtractKotlinFunctionHandlerHelper.DEFAULT);
|
||||
return new ExtractKotlinFunctionHandler(true, ExtractKotlinFunctionHandler.InteractiveExtractionHelper.INSTANCE$);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+20
-83
@@ -25,44 +25,29 @@ import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.kotlin.idea.refactoring.JetRefactoringBundle
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.extractFunction.ui.KotlinExtractFunctionDialog
|
||||
import org.jetbrains.kotlin.psi.JetFile
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import org.jetbrains.kotlin.idea.refactoring.checkConflictsInteractively
|
||||
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
|
||||
import org.jetbrains.kotlin.psi.JetBlockExpression
|
||||
import kotlin.test.fail
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.AnalysisResult.Status
|
||||
import com.intellij.openapi.ui.popup.JBPopupFactory
|
||||
import com.intellij.openapi.ui.MessageType
|
||||
import javax.swing.event.HyperlinkEvent
|
||||
import com.intellij.refactoring.BaseRefactoringProcessor.ConflictsInTestsException
|
||||
import com.intellij.ui.awt.RelativePoint
|
||||
import com.intellij.openapi.ui.popup.Balloon.Position
|
||||
import org.jetbrains.kotlin.psi.JetFunctionLiteral
|
||||
import org.jetbrains.kotlin.idea.util.psi.patternMatching.toRange
|
||||
import org.jetbrains.kotlin.idea.refactoring.getExtractionContainers
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.*
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.*
|
||||
|
||||
public open class ExtractKotlinFunctionHandlerHelper {
|
||||
open fun adjustExtractionData(data: ExtractionData): ExtractionData = data
|
||||
open fun adjustGeneratorOptions(options: ExtractionGeneratorOptions): ExtractionGeneratorOptions = options
|
||||
open fun adjustDescriptor(descriptor: ExtractableCodeDescriptor): ExtractableCodeDescriptor = descriptor
|
||||
|
||||
class object {
|
||||
public val DEFAULT: ExtractKotlinFunctionHandlerHelper = ExtractKotlinFunctionHandlerHelper()
|
||||
}
|
||||
}
|
||||
|
||||
public class ExtractKotlinFunctionHandler(
|
||||
public val allContainersEnabled: Boolean = false,
|
||||
private val helper: ExtractKotlinFunctionHandlerHelper = ExtractKotlinFunctionHandlerHelper.DEFAULT) : RefactoringActionHandler {
|
||||
private fun adjustElements(elements: List<PsiElement>): List<PsiElement> {
|
||||
if (elements.size() != 1) return elements
|
||||
private val helper: ExtractionEngineHelper = ExtractKotlinFunctionHandler.InteractiveExtractionHelper) : RefactoringActionHandler {
|
||||
|
||||
val e = elements.first()
|
||||
if (e is JetBlockExpression && e.getParent() is JetFunctionLiteral) return e.getStatements()
|
||||
|
||||
return elements
|
||||
object InteractiveExtractionHelper : ExtractionEngineHelper() {
|
||||
override fun configureInteractively(
|
||||
project: Project,
|
||||
editor: Editor,
|
||||
descriptorWithConflicts: ExtractableCodeDescriptorWithConflicts,
|
||||
continuation: (ExtractionGeneratorConfiguration) -> Unit
|
||||
) {
|
||||
KotlinExtractFunctionDialog(descriptorWithConflicts.descriptor.extractionData.project, descriptorWithConflicts) {
|
||||
continuation(it.getCurrentConfiguration())
|
||||
}.show()
|
||||
}
|
||||
}
|
||||
|
||||
fun doInvoke(
|
||||
@@ -71,66 +56,18 @@ public class ExtractKotlinFunctionHandler(
|
||||
elements: List<PsiElement>,
|
||||
targetSibling: PsiElement
|
||||
) {
|
||||
val project = file.getProject()
|
||||
fun adjustElements(elements: List<PsiElement>): List<PsiElement> {
|
||||
if (elements.size() != 1) return elements
|
||||
|
||||
val analysisResult = helper.adjustExtractionData(
|
||||
ExtractionData(file, adjustElements(elements).toRange(false), targetSibling)
|
||||
).performAnalysis()
|
||||
val e = elements.first()
|
||||
if (e is JetBlockExpression && e.getParent() is JetFunctionLiteral) return e.getStatements()
|
||||
|
||||
if (ApplicationManager.getApplication()!!.isUnitTestMode() && analysisResult.status != Status.SUCCESS) {
|
||||
throw ConflictsInTestsException(analysisResult.messages.map { it.renderMessage() })
|
||||
return elements
|
||||
}
|
||||
|
||||
fun doRefactor(descriptor: ExtractableCodeDescriptor, generatorOptions: ExtractionGeneratorOptions) {
|
||||
val adjustedDescriptor = helper.adjustDescriptor(descriptor)
|
||||
val adjustedGeneratorOptions = helper.adjustGeneratorOptions(generatorOptions)
|
||||
val result = project.executeWriteCommand<ExtractionResult>(EXTRACT_FUNCTION) { adjustedDescriptor.generateDeclaration(adjustedGeneratorOptions) }
|
||||
processDuplicates(result.duplicateReplacers, project, editor)
|
||||
}
|
||||
|
||||
fun validateAndRefactor() {
|
||||
val validationResult = analysisResult.descriptor!!.validate()
|
||||
project.checkConflictsInteractively(validationResult.conflicts) {
|
||||
if (ApplicationManager.getApplication()!!.isUnitTestMode()) {
|
||||
doRefactor(validationResult.descriptor, ExtractionGeneratorOptions.DEFAULT)
|
||||
}
|
||||
else {
|
||||
KotlinExtractFunctionDialog(project, validationResult) {
|
||||
doRefactor(it.getCurrentDescriptor(), it.getGeneratorOptions())
|
||||
}.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val message = analysisResult.messages.map { it.renderMessage() }.joinToString("\n")
|
||||
when (analysisResult.status) {
|
||||
Status.CRITICAL_ERROR -> {
|
||||
showErrorHint(project, editor, message, EXTRACT_FUNCTION)
|
||||
}
|
||||
|
||||
Status.NON_CRITICAL_ERROR -> {
|
||||
val anchorPoint = RelativePoint(
|
||||
editor.getContentComponent(),
|
||||
editor.visualPositionToXY(editor.getSelectionModel().getSelectionStartPosition()!!)
|
||||
)
|
||||
JBPopupFactory.getInstance()!!
|
||||
.createHtmlTextBalloonBuilder(
|
||||
"$message<br/><br/><a href=\"EXTRACT\">Proceed with extraction</a>",
|
||||
MessageType.WARNING,
|
||||
{ event ->
|
||||
if (event?.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
|
||||
validateAndRefactor()
|
||||
}
|
||||
}
|
||||
)
|
||||
.setHideOnClickOutside(true)
|
||||
.setHideOnFrameResize(false)
|
||||
.setHideOnLinkClick(true)
|
||||
.createBalloon()
|
||||
.show(anchorPoint, Position.below)
|
||||
}
|
||||
|
||||
Status.SUCCESS -> validateAndRefactor()
|
||||
val extractionData = ExtractionData(file, adjustElements(elements).toRange(false), targetSibling)
|
||||
ExtractionEngine(EXTRACT_FUNCTION, helper).run(editor, extractionData) {
|
||||
processDuplicates(it.duplicateReplacers, file.getProject(), editor)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+5
-4
@@ -110,8 +110,9 @@ public class KotlinExtractFunctionDialog extends DialogWrapper {
|
||||
|
||||
setOKActionEnabled(checkNames());
|
||||
signaturePreviewField.setText(
|
||||
ExtractionEnginePackage.getDeclarationText(currentDescriptor, getGeneratorOptions(), false,
|
||||
IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES)
|
||||
ExtractionEnginePackage.getDeclarationText(getCurrentConfiguration(),
|
||||
false,
|
||||
IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -270,8 +271,8 @@ public class KotlinExtractFunctionDialog extends DialogWrapper {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public ExtractableCodeDescriptor getCurrentDescriptor() {
|
||||
return currentDescriptor;
|
||||
public ExtractionGeneratorConfiguration getCurrentConfiguration() {
|
||||
return new ExtractionGeneratorConfiguration(currentDescriptor, getGeneratorOptions());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
+6
@@ -324,7 +324,13 @@ data class ExtractionGeneratorOptions(
|
||||
}
|
||||
}
|
||||
|
||||
data class ExtractionGeneratorConfiguration(
|
||||
val descriptor: ExtractableCodeDescriptor,
|
||||
val generatorOptions: ExtractionGeneratorOptions
|
||||
)
|
||||
|
||||
data class ExtractionResult(
|
||||
val config: ExtractionGeneratorConfiguration,
|
||||
val declaration: JetNamedDeclaration,
|
||||
val duplicateReplacers: Map<JetPsiRange, () -> Unit>,
|
||||
val nameByOffset: Map<Int, JetElement>
|
||||
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine
|
||||
|
||||
import com.intellij.psi.*
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import com.intellij.openapi.editor.*
|
||||
import org.jetbrains.kotlin.idea.util.psi.patternMatching.*
|
||||
import com.intellij.openapi.application.*
|
||||
import com.intellij.refactoring.*
|
||||
import org.jetbrains.kotlin.idea.util.application.*
|
||||
import org.jetbrains.kotlin.idea.refactoring.*
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.*
|
||||
import com.intellij.ui.awt.*
|
||||
import com.intellij.openapi.ui.popup.*
|
||||
import com.intellij.openapi.ui.*
|
||||
import javax.swing.event.*
|
||||
import com.intellij.openapi.project.*
|
||||
|
||||
public open class ExtractionEngineHelper {
|
||||
open fun adjustExtractionData(data: ExtractionData): ExtractionData = data
|
||||
|
||||
open fun configure(
|
||||
descriptor: ExtractableCodeDescriptor,
|
||||
generatorOptions: ExtractionGeneratorOptions
|
||||
): ExtractionGeneratorConfiguration {
|
||||
return ExtractionGeneratorConfiguration(descriptor, generatorOptions)
|
||||
}
|
||||
|
||||
open fun configureInteractively(
|
||||
project: Project,
|
||||
editor: Editor,
|
||||
descriptorWithConflicts: ExtractableCodeDescriptorWithConflicts,
|
||||
continuation: (ExtractionGeneratorConfiguration) -> Unit
|
||||
) {
|
||||
continuation(ExtractionGeneratorConfiguration(descriptorWithConflicts.descriptor, ExtractionGeneratorOptions.DEFAULT))
|
||||
}
|
||||
|
||||
class object {
|
||||
public val DEFAULT: ExtractionEngineHelper = ExtractionEngineHelper()
|
||||
}
|
||||
}
|
||||
|
||||
public class ExtractionEngine(
|
||||
val operationName: String,
|
||||
val helper: ExtractionEngineHelper
|
||||
) {
|
||||
fun run(editor: Editor,
|
||||
extractionData: ExtractionData,
|
||||
onFinish: (ExtractionResult) -> Unit = {}
|
||||
) {
|
||||
val project = extractionData.project
|
||||
|
||||
val analysisResult = helper.adjustExtractionData(extractionData).performAnalysis()
|
||||
|
||||
if (ApplicationManager.getApplication()!!.isUnitTestMode() && analysisResult.status != AnalysisResult.Status.SUCCESS) {
|
||||
throw BaseRefactoringProcessor.ConflictsInTestsException(analysisResult.messages.map { it.renderMessage() })
|
||||
}
|
||||
|
||||
fun doRefactor(config: ExtractionGeneratorConfiguration) {
|
||||
onFinish(project.executeWriteCommand<ExtractionResult>(operationName) { config.generateDeclaration() })
|
||||
}
|
||||
|
||||
fun validateAndRefactor() {
|
||||
val validationResult = analysisResult.descriptor!!.validate()
|
||||
project.checkConflictsInteractively(validationResult.conflicts) {
|
||||
if (ApplicationManager.getApplication()!!.isUnitTestMode()) {
|
||||
doRefactor(helper.configure(validationResult.descriptor, ExtractionGeneratorOptions.DEFAULT))
|
||||
}
|
||||
else {
|
||||
helper.configureInteractively(project, editor, validationResult, ::doRefactor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val message = analysisResult.messages.map { it.renderMessage() }.joinToString("\n")
|
||||
when (analysisResult.status) {
|
||||
AnalysisResult.Status.CRITICAL_ERROR -> {
|
||||
showErrorHint(project, editor, message, operationName)
|
||||
}
|
||||
|
||||
AnalysisResult.Status.NON_CRITICAL_ERROR -> {
|
||||
val anchorPoint = RelativePoint(
|
||||
editor.getContentComponent(),
|
||||
editor.visualPositionToXY(editor.getSelectionModel().getSelectionStartPosition()!!)
|
||||
)
|
||||
JBPopupFactory.getInstance()!!
|
||||
.createHtmlTextBalloonBuilder(
|
||||
"$message<br/><br/><a href=\"EXTRACT\">Proceed with extraction</a>",
|
||||
MessageType.WARNING,
|
||||
{ event ->
|
||||
if (event?.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
|
||||
validateAndRefactor()
|
||||
}
|
||||
}
|
||||
)
|
||||
.setHideOnClickOutside(true)
|
||||
.setHideOnFrameResize(false)
|
||||
.setHideOnLinkClick(true)
|
||||
.createBalloon()
|
||||
.show(anchorPoint, Balloon.Position.below)
|
||||
}
|
||||
|
||||
AnalysisResult.Status.SUCCESS -> validateAndRefactor()
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -841,7 +841,7 @@ private fun JetNamedDeclaration.getGeneratedBlockBody() =
|
||||
fun ExtractableCodeDescriptor.validate(): ExtractableCodeDescriptorWithConflicts {
|
||||
val conflicts = MultiMap<PsiElement, String>()
|
||||
|
||||
val result = generateDeclaration(ExtractionGeneratorOptions(inTempFile = true))
|
||||
val result = ExtractionGeneratorConfiguration(this, ExtractionGeneratorOptions(inTempFile = true)).generateDeclaration()
|
||||
|
||||
val valueParameterList = (result.declaration as? JetNamedFunction)?.getValueParameterList()
|
||||
val bindingContext = result.declaration.getGeneratedBlockBody().analyze()
|
||||
|
||||
+35
-36
@@ -46,22 +46,21 @@ import java.util.Collections
|
||||
import java.util.HashMap
|
||||
import java.util.LinkedHashMap
|
||||
|
||||
fun ExtractableCodeDescriptor.getDeclarationText(
|
||||
options: ExtractionGeneratorOptions = ExtractionGeneratorOptions.DEFAULT,
|
||||
fun ExtractionGeneratorConfiguration.getDeclarationText(
|
||||
withBody: Boolean = true,
|
||||
descriptorRenderer: DescriptorRenderer = if (options.flexibleTypesAllowed)
|
||||
descriptorRenderer: DescriptorRenderer = if (generatorOptions.flexibleTypesAllowed)
|
||||
DescriptorRenderer.FLEXIBLE_TYPES_FOR_CODE
|
||||
else IdeDescriptorRenderers.SOURCE_CODE
|
||||
): String {
|
||||
if (!canGenerateProperty() && options.extractAsProperty) {
|
||||
throw IllegalArgumentException("Can't generate property: ${extractionData.getCodeFragmentText()}")
|
||||
if (!descriptor.canGenerateProperty() && generatorOptions.extractAsProperty) {
|
||||
throw IllegalArgumentException("Can't generate property: ${descriptor.extractionData.getCodeFragmentText()}")
|
||||
}
|
||||
|
||||
val builderTarget = if (options.extractAsProperty) Target.READ_ONLY_PROPERTY else Target.FUNCTION
|
||||
val builderTarget = if (generatorOptions.extractAsProperty) Target.READ_ONLY_PROPERTY else Target.FUNCTION
|
||||
return CallableBuilder(builderTarget).let { builder ->
|
||||
builder.modifier(visibility)
|
||||
builder.modifier(descriptor.visibility)
|
||||
|
||||
builder.typeParams(typeParameters.map { it.originalDeclaration.getText()!! })
|
||||
builder.typeParams(descriptor.typeParameters.map { it.originalDeclaration.getText()!! })
|
||||
|
||||
fun JetType.typeAsString(): String {
|
||||
return if (isSpecial()) DEBUG_TYPE_REFERENCE_STRING else descriptorRenderer.renderType(this)
|
||||
@@ -79,10 +78,10 @@ fun ExtractableCodeDescriptor.getDeclarationText(
|
||||
if (isDefault() || isError()) builder.noReturnType() else builder.returnType(this.typeAsString())
|
||||
}
|
||||
|
||||
builder.typeConstraints(typeParameters.flatMap { it.originalConstraints }.map { it.getText()!! })
|
||||
builder.typeConstraints(descriptor.typeParameters.flatMap { it.originalConstraints }.map { it.getText()!! })
|
||||
|
||||
if (withBody) {
|
||||
builder.blockBody(extractionData.getCodeFragmentText())
|
||||
builder.blockBody(descriptor.extractionData.getCodeFragmentText())
|
||||
}
|
||||
|
||||
builder.asString()
|
||||
@@ -335,23 +334,23 @@ private fun makeCall(
|
||||
}
|
||||
}
|
||||
|
||||
fun ExtractableCodeDescriptor.generateDeclaration(options: ExtractionGeneratorOptions): ExtractionResult{
|
||||
val psiFactory = JetPsiFactory(extractionData.originalFile)
|
||||
fun ExtractionGeneratorConfiguration.generateDeclaration(): ExtractionResult{
|
||||
val psiFactory = JetPsiFactory(descriptor.extractionData.originalFile)
|
||||
val nameByOffset = HashMap<Int, JetElement>()
|
||||
|
||||
fun createDeclaration(): JetNamedDeclaration {
|
||||
return with(extractionData) {
|
||||
if (options.inTempFile) {
|
||||
createTemporaryDeclaration("${getDeclarationText(options)}\n")
|
||||
return with(descriptor.extractionData) {
|
||||
if (generatorOptions.inTempFile) {
|
||||
createTemporaryDeclaration("${getDeclarationText()}\n")
|
||||
}
|
||||
else {
|
||||
psiFactory.createDeclaration(getDeclarationText(options))
|
||||
psiFactory.createDeclaration(getDeclarationText())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getReturnArguments(resultExpression: JetExpression?): List<String> {
|
||||
return controlFlow.outputValues
|
||||
return descriptor.controlFlow.outputValues
|
||||
.map {
|
||||
when (it) {
|
||||
is ExpressionValue -> resultExpression?.getText()
|
||||
@@ -373,9 +372,9 @@ fun ExtractableCodeDescriptor.generateDeclaration(options: ExtractionGeneratorOp
|
||||
if (originalExpression is JetReturnExpression) originalExpression.getReturnedExpression() else originalExpression
|
||||
if (currentResultExpression == null) return
|
||||
|
||||
val newResultExpression = controlFlow.defaultOutputValue?.let {
|
||||
val newResultExpression = descriptor.controlFlow.defaultOutputValue?.let {
|
||||
val boxedExpression = originalExpression.replaced(replacingExpression).getReturnedExpression()!!
|
||||
controlFlow.outputValueBoxer.extractExpressionByValue(boxedExpression, it)
|
||||
descriptor.controlFlow.outputValueBoxer.extractExpressionByValue(boxedExpression, it)
|
||||
}
|
||||
if (newResultExpression == null) {
|
||||
throw AssertionError("Can' replace '${originalExpression.getText()}' with '${replacingExpression.getText()}'")
|
||||
@@ -398,14 +397,14 @@ fun ExtractableCodeDescriptor.generateDeclaration(options: ExtractionGeneratorOp
|
||||
* Sort by descending position so that internals of value/type arguments in calls and qualified types are replaced
|
||||
* before calls/types themselves
|
||||
*/
|
||||
for ((offsetInBody, resolveResult) in extractionData.refOffsetToDeclaration.entrySet().sortDescendingBy { it.key }) {
|
||||
for ((offsetInBody, resolveResult) in descriptor.extractionData.refOffsetToDeclaration.entrySet().sortDescendingBy { it.key }) {
|
||||
val expr = file.findElementAt(bodyOffset + offsetInBody)?.getNonStrictParentOfType<JetSimpleNameExpression>()
|
||||
assert(expr != null, "Couldn't find expression at $offsetInBody in '${body.getText()}'")
|
||||
|
||||
originalOffsetByExpr[expr!!] = offsetInBody
|
||||
|
||||
replacementMap[offsetInBody]?.let { replacement ->
|
||||
if (replacement !is ParameterReplacement || replacement.parameter != receiverParameter) {
|
||||
descriptor.replacementMap[offsetInBody]?.let { replacement ->
|
||||
if (replacement !is ParameterReplacement || replacement.parameter != descriptor.receiverParameter) {
|
||||
exprReplacementMap[expr] = replacement
|
||||
}
|
||||
}
|
||||
@@ -414,11 +413,11 @@ fun ExtractableCodeDescriptor.generateDeclaration(options: ExtractionGeneratorOp
|
||||
val replacingReturn: JetExpression?
|
||||
val expressionsToReplaceWithReturn: List<JetElement>
|
||||
|
||||
val jumpValue = controlFlow.jumpOutputValue
|
||||
val jumpValue = descriptor.controlFlow.jumpOutputValue
|
||||
if (jumpValue != null) {
|
||||
replacingReturn = psiFactory.createExpression(if (jumpValue.conditional) "return true" else "return")
|
||||
expressionsToReplaceWithReturn = jumpValue.elementsToReplace.map { jumpElement ->
|
||||
val offsetInBody = jumpElement.getTextRange()!!.getStartOffset() - extractionData.originalStartOffset!!
|
||||
val offsetInBody = jumpElement.getTextRange()!!.getStartOffset() - descriptor.extractionData.originalStartOffset!!
|
||||
val expr = file.findElementAt(bodyOffset + offsetInBody)?.getNonStrictParentOfType(jumpElement.javaClass)
|
||||
assert(expr != null, "Couldn't find expression at $offsetInBody in '${body.getText()}'")
|
||||
|
||||
@@ -444,7 +443,7 @@ fun ExtractableCodeDescriptor.generateDeclaration(options: ExtractionGeneratorOp
|
||||
|
||||
val firstExpression = body.getStatements().firstOrNull()
|
||||
if (firstExpression != null) {
|
||||
for (param in parameters) {
|
||||
for (param in descriptor.parameters) {
|
||||
param.mirrorVarName?.let { varName ->
|
||||
body.addBefore(psiFactory.createProperty(varName, null, true, param.name), firstExpression)
|
||||
body.addBefore(psiFactory.createNewLine(), firstExpression)
|
||||
@@ -452,13 +451,13 @@ fun ExtractableCodeDescriptor.generateDeclaration(options: ExtractionGeneratorOp
|
||||
}
|
||||
}
|
||||
|
||||
val defaultValue = controlFlow.defaultOutputValue
|
||||
val defaultValue = descriptor.controlFlow.defaultOutputValue
|
||||
|
||||
val lastExpression = body.getStatements().lastOrNull() as? JetExpression
|
||||
if (lastExpression is JetReturnExpression) return
|
||||
|
||||
val (defaultExpression, expressionToUnifyWith) =
|
||||
if (!options.inTempFile && defaultValue != null && controlFlow.outputValueBoxer.boxingRequired && lastExpression!!.isMultiLine()) {
|
||||
if (!generatorOptions.inTempFile && defaultValue != null && descriptor.controlFlow.outputValueBoxer.boxingRequired && lastExpression!!.isMultiLine()) {
|
||||
val varNameValidator = JetNameValidatorImpl(body, lastExpression, JetNameValidatorImpl.Target.PROPERTIES)
|
||||
val resultVal = JetNameSuggester.suggestNames(defaultValue.valueType, varNameValidator, null).first()
|
||||
val newDecl = body.addBefore(psiFactory.createDeclaration("val $resultVal = ${lastExpression!!.getText()}"), lastExpression) as JetProperty
|
||||
@@ -469,7 +468,7 @@ fun ExtractableCodeDescriptor.generateDeclaration(options: ExtractionGeneratorOp
|
||||
lastExpression to null
|
||||
}
|
||||
|
||||
val returnExpression = controlFlow.outputValueBoxer.getReturnExpression(getReturnArguments(defaultExpression), psiFactory)
|
||||
val returnExpression = descriptor.controlFlow.outputValueBoxer.getReturnExpression(getReturnArguments(defaultExpression), psiFactory)
|
||||
if (returnExpression == null) return
|
||||
|
||||
when {
|
||||
@@ -482,7 +481,7 @@ fun ExtractableCodeDescriptor.generateDeclaration(options: ExtractionGeneratorOp
|
||||
}
|
||||
|
||||
fun insertDeclaration(declaration: JetNamedDeclaration, anchor: PsiElement): JetNamedDeclaration {
|
||||
return with(extractionData) {
|
||||
return with(descriptor.extractionData) {
|
||||
val targetContainer = anchor.getParent()!!
|
||||
val emptyLines = psiFactory.createWhiteSpace("\n\n")
|
||||
if (insertBefore) {
|
||||
@@ -500,9 +499,9 @@ fun ExtractableCodeDescriptor.generateDeclaration(options: ExtractionGeneratorOp
|
||||
}
|
||||
}
|
||||
|
||||
val duplicates = if (options.inTempFile) Collections.emptyList() else findDuplicates()
|
||||
val duplicates = if (generatorOptions.inTempFile) Collections.emptyList() else descriptor.findDuplicates()
|
||||
|
||||
val anchor = with(extractionData) {
|
||||
val anchor = with(descriptor.extractionData) {
|
||||
val anchorCandidates = duplicates.mapTo(ArrayList<PsiElement>()) { it.range.elements.first() }
|
||||
anchorCandidates.add(targetSibling)
|
||||
|
||||
@@ -518,7 +517,7 @@ fun ExtractableCodeDescriptor.generateDeclaration(options: ExtractionGeneratorOp
|
||||
marginalCandidate.parents().first { it.getParent() == targetParent }
|
||||
}
|
||||
|
||||
val declaration = createDeclaration().let { if (options.inTempFile) it else insertDeclaration(it, anchor) }
|
||||
val declaration = createDeclaration().let { if (generatorOptions.inTempFile) it else insertDeclaration(it, anchor) }
|
||||
adjustDeclarationBody(declaration)
|
||||
|
||||
if (declaration is JetNamedFunction && declaration.getContainingJetFile().suppressDiagnosticsInDebugMode) {
|
||||
@@ -533,11 +532,11 @@ fun ExtractableCodeDescriptor.generateDeclaration(options: ExtractionGeneratorOp
|
||||
}
|
||||
}
|
||||
|
||||
if (options.inTempFile) return ExtractionResult(declaration, Collections.emptyMap(), nameByOffset)
|
||||
if (generatorOptions.inTempFile) return ExtractionResult(this, declaration, Collections.emptyMap(), nameByOffset)
|
||||
|
||||
makeCall(this, declaration, controlFlow, extractionData.originalRange, parameters.map { it.argumentText })
|
||||
makeCall(descriptor, declaration, descriptor.controlFlow, descriptor.extractionData.originalRange, descriptor.parameters.map { it.argumentText })
|
||||
ShortenReferences.DEFAULT.process(declaration)
|
||||
|
||||
val duplicateReplacers = duplicates.map { it.range to { makeCall(this, declaration, it.controlFlow, it.range, it.arguments) } }.toMap()
|
||||
return ExtractionResult(declaration, duplicateReplacers, nameByOffset)
|
||||
val duplicateReplacers = duplicates.map { it.range to { makeCall(descriptor, declaration, it.controlFlow, it.range, it.arguments) } }.toMap()
|
||||
return ExtractionResult(this, declaration, duplicateReplacers, nameByOffset)
|
||||
}
|
||||
|
||||
+15
-17
@@ -31,18 +31,14 @@ import org.jetbrains.kotlin.renderer.DescriptorRenderer
|
||||
import kotlin.test.assertEquals
|
||||
import org.jetbrains.kotlin.idea.JetLightCodeInsightFixtureTestCase
|
||||
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.extractFunction.ExtractKotlinFunctionHandlerHelper
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.ExtractionGeneratorOptions
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.ExtractableCodeDescriptor
|
||||
import org.jetbrains.kotlin.test.ConfigLibraryUtil
|
||||
import org.jetbrains.kotlin.idea.PluginTestCaseBase
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.ExtractionData
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.ExtractionOptions
|
||||
import org.jetbrains.kotlin.psi.JetDeclaration
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import com.intellij.psi.PsiWhiteSpace
|
||||
import org.jetbrains.kotlin.psi.JetPackageDirective
|
||||
import org.jetbrains.kotlin.utils.emptyOrSingletonList
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.*
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
public abstract class AbstractJetExtractionTest() : JetLightCodeInsightFixtureTestCase() {
|
||||
@@ -106,26 +102,28 @@ public abstract class AbstractJetExtractionTest() : JetLightCodeInsightFixtureTe
|
||||
|
||||
val editor = fixture.getEditor()
|
||||
val handler = ExtractKotlinFunctionHandler(
|
||||
helper = object : ExtractKotlinFunctionHandlerHelper() {
|
||||
helper = object : ExtractionEngineHelper() {
|
||||
override fun adjustExtractionData(data: ExtractionData): ExtractionData {
|
||||
return data.copy(options = extractionOptions)
|
||||
}
|
||||
|
||||
override fun adjustGeneratorOptions(options: ExtractionGeneratorOptions): ExtractionGeneratorOptions {
|
||||
return options.copy(extractAsProperty = extractAsProperty)
|
||||
}
|
||||
|
||||
override fun adjustDescriptor(descriptor: ExtractableCodeDescriptor): ExtractableCodeDescriptor {
|
||||
val allParameters = emptyOrSingletonList(descriptor.receiverParameter) + descriptor.parameters
|
||||
val actualDescriptors = allParameters.map { renderer.render(it.originalDescriptor) }.joinToString()
|
||||
val actualTypes = allParameters.map {
|
||||
it.getParameterTypeCandidates(extractionOptions.allowSpecialClassNames).map { renderer.renderType(it) }.joinToString(", ", "[", "]")
|
||||
}.joinToString()
|
||||
override fun configure(
|
||||
descriptor: ExtractableCodeDescriptor,
|
||||
generatorOptions: ExtractionGeneratorOptions
|
||||
): ExtractionGeneratorConfiguration {
|
||||
val allParameters = emptyOrSingletonList(descriptor.receiverParameter) + descriptor.parameters
|
||||
val actualDescriptors = allParameters.map { renderer.render(it.originalDescriptor) }.joinToString()
|
||||
val actualTypes = allParameters.map {
|
||||
it.parameterTypeCandidates.map { renderer.renderType(it) }.joinToString(", ", "[", "]")
|
||||
}.joinToString()
|
||||
|
||||
assertEquals(expectedDescriptors, actualDescriptors, "Expected descriptors mismatch.")
|
||||
assertEquals(expectedTypes, actualTypes, "Expected types mismatch.")
|
||||
|
||||
return if (descriptor.name == "") descriptor.copy(name = "__dummyTestFun__") else descriptor
|
||||
return ExtractionGeneratorConfiguration(
|
||||
if (descriptor.name == "") descriptor.copy(name = "__dummyTestFun__") else descriptor,
|
||||
generatorOptions.copy(extractAsProperty = extractAsProperty)
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user