From 2b9d34dcb4657ddc10de8a46692199b5e23831aa Mon Sep 17 00:00:00 2001 From: Alexandr Suhinin Date: Tue, 19 Apr 2022 08:58:23 +0300 Subject: [PATCH] [kotlin extract method] KTIJ-21622: replace template with dialog on second call GitOrigin-RevId: 0f6bb8e089657a0b7f69cc801870e3b2a30baa66 --- .../KotlinRefactoringSupportProvider.kt | 2 +- .../ExtractKotlinFunctionHandler.kt | 110 +++++++++++------- .../introduce/AbstractExtractionTest.kt | 6 +- 3 files changed, 72 insertions(+), 46 deletions(-) diff --git a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/refactoring/KotlinRefactoringSupportProvider.kt b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/refactoring/KotlinRefactoringSupportProvider.kt index e56ca0031198..fae6f8490911 100644 --- a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/refactoring/KotlinRefactoringSupportProvider.kt +++ b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/refactoring/KotlinRefactoringSupportProvider.kt @@ -41,7 +41,7 @@ class KotlinRefactoringSupportProvider : RefactoringSupportProvider() { fun getExtractFunctionHandler(): RefactoringActionHandler = ExtractKotlinFunctionHandler() fun getExtractFunctionToScopeHandler(): RefactoringActionHandler = - ExtractKotlinFunctionHandler(true, ExtractKotlinFunctionHandler.InteractiveExtractionHelper) + ExtractKotlinFunctionHandler(allContainersEnabled = true) override fun getChangeSignatureHandler() = KotlinChangeSignatureHandler() diff --git a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractFunction/ExtractKotlinFunctionHandler.kt b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractFunction/ExtractKotlinFunctionHandler.kt index 4cc8f6540b60..f4efebc06f9e 100644 --- a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractFunction/ExtractKotlinFunctionHandler.kt +++ b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractFunction/ExtractKotlinFunctionHandler.kt @@ -4,6 +4,7 @@ package org.jetbrains.kotlin.idea.refactoring.introduce.extractFunction import com.intellij.codeInsight.template.Template import com.intellij.codeInsight.template.TemplateEditingAdapter +import com.intellij.codeInsight.template.impl.TemplateManagerImpl import com.intellij.openapi.actionSystem.DataContext import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.ex.EditorSettingsExternalizable @@ -36,7 +37,7 @@ import org.jetbrains.kotlin.utils.addToStdlib.safeAs class ExtractKotlinFunctionHandler( private val allContainersEnabled: Boolean = false, - private val helper: ExtractionEngineHelper = InteractiveExtractionHelper + private val helper: ExtractionEngineHelper = getDefaultHelper(allContainersEnabled) ) : RefactoringActionHandler { companion object { @@ -45,6 +46,10 @@ class ExtractKotlinFunctionHandler( return EditorSettingsExternalizable.getInstance().isVariableInplaceRenameEnabled && Registry.`is`("kotlin.enable.inplace.extract.method") } + + fun getDefaultHelper(allContainersEnabled: Boolean): ExtractionEngineHelper { + return if (isInplaceRefactoringEnabled) InplaceExtractionHelper(allContainersEnabled) else InteractiveExtractionHelper + } } object InteractiveExtractionHelper : ExtractionEngineHelper(EXTRACT_FUNCTION) { @@ -54,15 +59,66 @@ class ExtractKotlinFunctionHandler( descriptorWithConflicts: ExtractableCodeDescriptorWithConflicts, onFinish: (ExtractionResult) -> Unit ) { - if (isInplaceRefactoringEnabled) { - val descriptor = descriptorWithConflicts.descriptor.copy(suggestedNames = listOf("extracted")) - val configuration = ExtractionGeneratorConfiguration(descriptor, ExtractionGeneratorOptions.DEFAULT) - doRefactor(configuration, onFinish) - } else { - KotlinExtractFunctionDialog(descriptorWithConflicts.descriptor.extractionData.project, descriptorWithConflicts) { - doRefactor(it.currentConfiguration, onFinish) - }.show() + fun afterFinish(extraction: ExtractionResult){ + processDuplicates(extraction.duplicateReplacers, project, editor) + onFinish(extraction) } + KotlinExtractFunctionDialog(descriptorWithConflicts.descriptor.extractionData.project, descriptorWithConflicts) { + doRefactor(it.currentConfiguration, ::afterFinish) + }.show() + } + } + + class InplaceExtractionHelper(private val allContainersEnabled: Boolean) : ExtractionEngineHelper(EXTRACT_FUNCTION) { + override fun configureAndRun( + project: Project, + editor: Editor, + descriptorWithConflicts: ExtractableCodeDescriptorWithConflicts, + onFinish: (ExtractionResult) -> Unit + ) { + val activeTemplateState = TemplateManagerImpl.getTemplateState(editor) + if (activeTemplateState != null) { + activeTemplateState.gotoEnd(true) + ExtractKotlinFunctionHandler(allContainersEnabled, InteractiveExtractionHelper) + .invoke(project, editor, descriptorWithConflicts.descriptor.extractionData.originalFile, null) + } + + val descriptor = descriptorWithConflicts.descriptor.copy(suggestedNames = listOf("extracted")) + val elements = descriptor.extractionData.originalElements + val file = descriptor.extractionData.originalFile + val callRange = editor.document.createRangeMarker(elements.first().textRange.startOffset, elements.last().textRange.endOffset) + .apply { isGreedyToLeft = true; isGreedyToRight = true } + val editorState = EditorState(editor) + fun afterFinish(extraction: ExtractionResult){ + val callIdentifier = findSingleCallExpression(file, callRange.range)?.calleeExpression ?: throw IllegalStateException() + val methodRange = extraction.declaration.textRange + val methodOffset = extraction.declaration.navigationElement.textRange.endOffset + val callOffset = callIdentifier.textRange.endOffset + val preview = InplaceExtractUtils.createPreview(editor, methodRange, methodOffset, callRange.range!!, callOffset) + val templateState = ExtractMethodTemplate(editor, extraction.declaration, callIdentifier).runTemplate(LinkedHashSet()) + callRange.dispose() + Disposer.register(templateState, preview) + templateState.addTemplateStateListener(object: TemplateEditingAdapter() { + override fun templateFinished(template: Template, brokenOff: Boolean) { + if (brokenOff) { + editorState.revert() + } + } + }) + InplaceExtractUtils.addTemplateFinishedListener(templateState) { + processDuplicates(extraction.duplicateReplacers, file.project, editor) + } + onFinish(extraction) + } + val configuration = ExtractionGeneratorConfiguration(descriptor, ExtractionGeneratorOptions.DEFAULT) + doRefactor(configuration, ::afterFinish) + } + + private fun findSingleCallExpression(file: KtFile, range: TextRange?): KtCallExpression? { + if (range == null) return null + val container = PsiTreeUtil.findCommonParent(file.findElementAt(range.startOffset), file.findElementAt(range.endOffset)) + val callExpressions = PsiTreeUtil.findChildrenOfType(container, KtCallExpression::class.java) + return callExpressions.singleOrNull { it.textRange in range } } } @@ -76,44 +132,10 @@ class ExtractKotlinFunctionHandler( val adjustedElements = elements.singleOrNull().safeAs()?.statements ?: elements ExtractionData(file, adjustedElements.toRange(false), targetSibling) }) { extractionData -> - val callRange = editor.document.createRangeMarker(elements.first().textRange.startOffset, elements.last().textRange.endOffset) - .apply { isGreedyToLeft = true; isGreedyToRight = true } - val editorState = EditorState(editor) - ExtractionEngine(helper).run(editor, extractionData) { extraction -> - if (isInplaceRefactoringEnabled) { - val callIdentifier = findSingleCallExpression(file, callRange.range)?.calleeExpression ?: throw IllegalStateException() - val methodRange = extraction.declaration.textRange - val methodOffset = extraction.declaration.navigationElement.textRange.endOffset - val callOffset = callIdentifier.textRange.endOffset - val preview = InplaceExtractUtils.createPreview(editor, methodRange, methodOffset, callRange.range!!, callOffset) - val templateState = ExtractMethodTemplate(editor, extraction.declaration, callIdentifier).runTemplate(LinkedHashSet()) - templateState.properties.put("ExtractMethod", true) - callRange.dispose() - Disposer.register(templateState, preview) - templateState.addTemplateStateListener(object: TemplateEditingAdapter() { - override fun templateFinished(template: Template, brokenOff: Boolean) { - if (brokenOff) { - editorState.revert() - } - } - }) - InplaceExtractUtils.addTemplateFinishedListener(templateState) { - processDuplicates(extraction.duplicateReplacers, file.project, editor) - } - } else { - processDuplicates(extraction.duplicateReplacers, file.project, editor) - } - } + ExtractionEngine(helper).run(editor, extractionData) { } } } - private fun findSingleCallExpression(file: KtFile, range: TextRange?): KtCallExpression? { - if (range == null) return null - val container = PsiTreeUtil.findCommonParent(file.findElementAt(range.startOffset), file.findElementAt(range.endOffset)) - val callExpressions = PsiTreeUtil.findChildrenOfType(container, KtCallExpression::class.java) - return callExpressions.filter { it.textRange in range }.singleOrNull() - } - fun selectElements(editor: Editor, file: KtFile, continuation: (elements: List, targetSibling: PsiElement) -> Unit) { selectElementsWithTargetSibling( EXTRACT_FUNCTION, diff --git a/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/refactoring/introduce/AbstractExtractionTest.kt b/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/refactoring/introduce/AbstractExtractionTest.kt index 9ab003ee07f1..7003eada594e 100644 --- a/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/refactoring/introduce/AbstractExtractionTest.kt +++ b/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/refactoring/introduce/AbstractExtractionTest.kt @@ -496,7 +496,11 @@ fun doExtractFunction(fixture: CodeInsightTestFixture, file: KtFile) { descriptor } - doRefactor(ExtractionGeneratorConfiguration(newDescriptor, ExtractionGeneratorOptions.DEFAULT), onFinish) + fun afterFinish(extraction: ExtractionResult){ + processDuplicates(extraction.duplicateReplacers, project, editor) + onFinish(extraction) + } + doRefactor(ExtractionGeneratorConfiguration(newDescriptor, ExtractionGeneratorOptions.DEFAULT), ::afterFinish) } } )