diff --git a/java/java-impl-refactorings/src/com/intellij/refactoring/extractMethod/newImpl/ExtractMethodHelper.kt b/java/java-impl-refactorings/src/com/intellij/refactoring/extractMethod/newImpl/ExtractMethodHelper.kt index 85350d2cabb1..0907cc40cfdc 100644 --- a/java/java-impl-refactorings/src/com/intellij/refactoring/extractMethod/newImpl/ExtractMethodHelper.kt +++ b/java/java-impl-refactorings/src/com/intellij/refactoring/extractMethod/newImpl/ExtractMethodHelper.kt @@ -5,11 +5,14 @@ import com.intellij.codeInsight.Nullability import com.intellij.codeInsight.NullableNotNullManager import com.intellij.codeInsight.PsiEquivalenceUtil import com.intellij.codeInsight.intention.AddAnnotationPsiFix +import com.intellij.codeInsight.template.impl.TemplateState import com.intellij.openapi.Disposable +import com.intellij.openapi.application.EDT import com.intellij.openapi.command.WriteCommandAction import com.intellij.openapi.command.impl.FinishMarkAction import com.intellij.openapi.command.impl.StartMarkAction import com.intellij.openapi.editor.Editor +import com.intellij.openapi.editor.impl.EditorImpl import com.intellij.openapi.util.Conditions import com.intellij.openapi.util.Disposer import com.intellij.openapi.util.NlsContexts.Command @@ -30,6 +33,9 @@ import com.intellij.refactoring.extractMethod.newImpl.structures.ExtractOptions import com.intellij.refactoring.extractMethod.newImpl.structures.InputParameter import com.intellij.refactoring.introduceField.ElementToWorkOn import com.intellij.util.CommonJavaRefactoringUtil +import com.intellij.util.concurrency.annotations.RequiresWriteLock +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext object ExtractMethodHelper { @@ -303,4 +309,31 @@ object ExtractMethodHelper { } private data class PsiRange(val parent: PsiElement, val firstChild: PsiElement, val lastChild: PsiElement) + + @RequiresWriteLock + fun renameTemplate(templateState: TemplateState?, name: String) { + if (templateState == null) return + val range = templateState.currentVariableRange ?: return + templateState.editor.document.replaceString(range.startOffset, range.endOffset, name) + templateState.update() + } + + internal suspend fun runWithDumbEditor(editor: Editor, action: suspend () -> Unit) { + val editorImpl = editor as? EditorImpl + if (editorImpl == null) { + action.invoke() + return + } + withContext(Dispatchers.EDT) { + editorImpl.startDumb() + } + try { + action.invoke() + } + finally { + withContext(Dispatchers.EDT) { + editor.stopDumbLater() + } + } + } } \ No newline at end of file diff --git a/java/java-impl-refactorings/src/com/intellij/refactoring/extractMethod/newImpl/MethodExtractor.kt b/java/java-impl-refactorings/src/com/intellij/refactoring/extractMethod/newImpl/MethodExtractor.kt index 8620ce5d8d27..91d4ee8a83ac 100644 --- a/java/java-impl-refactorings/src/com/intellij/refactoring/extractMethod/newImpl/MethodExtractor.kt +++ b/java/java-impl-refactorings/src/com/intellij/refactoring/extractMethod/newImpl/MethodExtractor.kt @@ -2,12 +2,15 @@ package com.intellij.refactoring.extractMethod.newImpl import com.intellij.codeInsight.Nullability +import com.intellij.codeInsight.template.impl.TemplateManagerImpl +import com.intellij.codeInsight.template.impl.TemplateState import com.intellij.ide.util.PropertiesComponent import com.intellij.java.refactoring.JavaRefactoringBundle import com.intellij.openapi.application.EDT import com.intellij.openapi.application.WriteAction import com.intellij.openapi.application.readAction import com.intellij.openapi.command.CommandProcessor +import com.intellij.openapi.command.writeCommandAction import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.ex.EditorSettingsExternalizable @@ -22,11 +25,14 @@ import com.intellij.refactoring.RefactoringBundle import com.intellij.refactoring.extractMethod.ExtractMethodDialog import com.intellij.refactoring.extractMethod.ExtractMethodHandler import com.intellij.refactoring.extractMethod.newImpl.ExtractMethodHelper.guessMethodName +import com.intellij.refactoring.extractMethod.newImpl.ExtractMethodHelper.renameTemplate import com.intellij.refactoring.extractMethod.newImpl.ExtractMethodHelper.replaceWithMethod +import com.intellij.refactoring.extractMethod.newImpl.ExtractMethodHelper.runWithDumbEditor import com.intellij.refactoring.extractMethod.newImpl.ExtractMethodPipeline.findAllOptionsToExtract import com.intellij.refactoring.extractMethod.newImpl.ExtractMethodPipeline.selectOptionWithTargetClass import com.intellij.refactoring.extractMethod.newImpl.ExtractMethodPipeline.withFilteredAnnotations import com.intellij.refactoring.extractMethod.newImpl.inplace.* +import com.intellij.refactoring.extractMethod.newImpl.inplace.InplaceExtractMethodCollector import com.intellij.refactoring.extractMethod.newImpl.inplace.InplaceExtractUtils.createGreedyRangeMarker import com.intellij.refactoring.extractMethod.newImpl.parameterObject.ResultObjectExtractor import com.intellij.refactoring.extractMethod.newImpl.structures.ExtractOptions @@ -46,14 +52,51 @@ data class ExtractedElements(val callElements: List, val method: Psi class MethodExtractor { + internal fun restartInDialog(templateState: TemplateState, extractor: DuplicatesMethodExtractor, isLinkUsed: Boolean = false) { + val editor = templateState.editor + val project = templateState.project + val methodRange = templateState.currentVariableRange + val methodName = if (methodRange != null) editor.document.getText(methodRange) else "" + InplaceExtractMethodCollector.openExtractDialog.log(project, isLinkUsed) + TemplateManagerImpl.getTemplateState(editor)?.gotoEnd(true) + val extractOptions = extractor.extractOptions.copy(methodName = methodName) + val rangeToReplace = createGreedyRangeMarker(editor.document, extractor.rangeToReplaceOriginal) + val extractor = DuplicatesMethodExtractor(extractOptions, extractor.targetClass, rangeToReplace) + extractor.extractInDialog() + } + + internal suspend fun restartInplace(templateState: TemplateState, defaultExtractor: DuplicatesMethodExtractor, popup: ExtractMethodPopupProvider) { + val startTime = System.currentTimeMillis() + + val editor = templateState.editor + val project = templateState.project + val methodRange = templateState.currentVariableRange + val methodName = if (methodRange != null) editor.document.getText(methodRange) else "" + + runWithDumbEditor(editor) { + withContext(Dispatchers.EDT) { + TemplateManagerImpl.getTemplateState(editor)?.gotoEnd(true) + } + val inplaceExtractor = readAction { InplaceMethodExtractor(editor, popup, defaultExtractor) } + inplaceExtractor.extractAndRunTemplate(emptyList()) + val newTemplateState = TemplateManagerImpl.getTemplateState(editor) + writeCommandAction(project, ExtractMethodHandler.getRefactoringName()) { + renameTemplate(newTemplateState, methodName) + } + } + val endTime = System.currentTimeMillis() + InplaceExtractMethodCollector.previewUpdated.log(endTime - startTime) + } + fun doExtract(file: PsiFile, range: TextRange) { if (!CommonRefactoringUtil.checkReadOnlyStatus(file.project, file)) return val coroutineScope = ExtractMethodService.getInstance(file.project).scope val editor = PsiEditorUtil.findEditor(file) ?: return val activeExtractor = InplaceMethodExtractor.getActiveExtractor(editor) - if (activeExtractor != null) { - activeExtractor.restartInDialog() + val template = TemplateManagerImpl.getTemplateState(editor) + if (activeExtractor != null && template != null) { + restartInDialog(template, activeExtractor.extractor, false) return } @@ -107,7 +150,7 @@ class MethodExtractor { } if (EditorSettingsExternalizable.getInstance().isVariableInplaceRenameEnabled) { val templateStart = System.currentTimeMillis() - runInplaceExtract(editor, range, extractor, guessedNames) + runInplaceExtract(editor, extractor, guessedNames) val prepareTemplateTime = System.currentTimeMillis() - templateStart reportPerformanceStatistics(preparePlacesTime, prepareTemplateTime, descriptorsForAllTargetPlaces.size) } @@ -139,10 +182,10 @@ class MethodExtractor { } } - private suspend fun runInplaceExtract(editor: Editor, range: TextRange, extractor: DuplicatesMethodExtractor, methodNames: List){ + private suspend fun runInplaceExtract(editor: Editor, extractor: DuplicatesMethodExtractor, methodNames: List){ val popupSettings = readAction { createInplaceSettingsPopup(extractor.extractOptions) } val suggestedNames = methodNames.takeIf { it.size > 1 }.orEmpty() - val inplaceExtractor = readAction { InplaceMethodExtractor(editor, range, popupSettings, extractor) } + val inplaceExtractor = readAction { InplaceMethodExtractor(editor, popupSettings, extractor) } inplaceExtractor.extractAndRunTemplate(suggestedNames) } diff --git a/java/java-impl-refactorings/src/com/intellij/refactoring/extractMethod/newImpl/inplace/DuplicatesMethodExtractor.kt b/java/java-impl-refactorings/src/com/intellij/refactoring/extractMethod/newImpl/inplace/DuplicatesMethodExtractor.kt index 9ea298a565c9..720aa60c9a4b 100644 --- a/java/java-impl-refactorings/src/com/intellij/refactoring/extractMethod/newImpl/inplace/DuplicatesMethodExtractor.kt +++ b/java/java-impl-refactorings/src/com/intellij/refactoring/extractMethod/newImpl/inplace/DuplicatesMethodExtractor.kt @@ -43,6 +43,8 @@ import kotlinx.coroutines.withContext class DuplicatesMethodExtractor(val extractOptions: ExtractOptions, val targetClass: PsiClass, val rangeToReplace: RangeMarker) { + val rangeToReplaceOriginal = rangeToReplace.textRange + internal fun getElements(): List { val file = targetClass.containingFile val range = rangeToReplace.textRange diff --git a/java/java-impl-refactorings/src/com/intellij/refactoring/extractMethod/newImpl/inplace/InplaceMethodExtractor.kt b/java/java-impl-refactorings/src/com/intellij/refactoring/extractMethod/newImpl/inplace/InplaceMethodExtractor.kt index c4ec2275ab29..33032d36a90d 100644 --- a/java/java-impl-refactorings/src/com/intellij/refactoring/extractMethod/newImpl/inplace/InplaceMethodExtractor.kt +++ b/java/java-impl-refactorings/src/com/intellij/refactoring/extractMethod/newImpl/inplace/InplaceMethodExtractor.kt @@ -6,19 +6,16 @@ import com.intellij.codeInsight.template.impl.TemplateState import com.intellij.ide.util.PropertiesComponent import com.intellij.openapi.application.EDT import com.intellij.openapi.application.readAction -import com.intellij.openapi.command.writeCommandAction import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.RangeMarker import com.intellij.openapi.editor.asTextRange import com.intellij.openapi.editor.event.CaretEvent import com.intellij.openapi.editor.event.CaretListener import com.intellij.openapi.editor.ex.util.EditorUtil -import com.intellij.openapi.editor.impl.EditorImpl import com.intellij.openapi.util.Disposer import com.intellij.openapi.util.Key import com.intellij.openapi.util.TextRange import com.intellij.platform.ide.progress.runWithModalProgressBlocking -import com.intellij.psi.PsiDocumentManager import com.intellij.psi.PsiFile import com.intellij.psi.PsiMethod import com.intellij.psi.PsiMethodCallExpression @@ -45,10 +42,11 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -internal class InplaceMethodExtractor(private val editor: Editor, - private val range: TextRange, - private val popupProvider: ExtractMethodPopupProvider, - private val defaultExtractor: DuplicatesMethodExtractor) { +internal class InplaceMethodExtractor( + private val editor: Editor, + private val popupProvider: ExtractMethodPopupProvider, + private val defaultExtractor: DuplicatesMethodExtractor +) { companion object { private val INPLACE_METHOD_EXTRACTOR = Key("InplaceMethodExtractor") @@ -62,7 +60,7 @@ internal class InplaceMethodExtractor(private val editor: Editor, } } - private val extractor: DuplicatesMethodExtractor = createExtractor() + internal val extractor: DuplicatesMethodExtractor = createExtractor() private val file: PsiFile = defaultExtractor.targetClass.containingFile @@ -82,7 +80,7 @@ internal class InplaceMethodExtractor(private val editor: Editor, val analyzer = CodeFragmentAnalyzer(options.elements) options = ExtractMethodPipeline.withForcedStatic(analyzer, options) ?: throw IllegalStateException() } - val rangeToReplace = createGreedyRangeMarker(editor.document, range) + val rangeToReplace = createGreedyRangeMarker(editor.document, defaultExtractor.rangeToReplaceOriginal) return DuplicatesMethodExtractor(options, defaultExtractor.targetClass, rangeToReplace) } @@ -180,75 +178,15 @@ internal class InplaceMethodExtractor(private val editor: Editor, JavaRefactoringSettings.getInstance().EXTRACT_STATIC_METHOD = makeStatic } ExtractMethodService.getInstance(project).scope.launch { - restartInplace() + MethodExtractor().restartInplace(templateState, defaultExtractor, popupProvider) } } - popupProvider.setShowDialogAction { actionEvent -> restartInDialog(actionEvent == null) } + popupProvider.setShowDialogAction { actionEvent -> + MethodExtractor().restartInDialog(templateState, extractor, actionEvent == null) + } addInlaySettingsElement(templateState, popupProvider)?.also { inlay -> Disposer.register(disposable, inlay) } } - private fun setMethodName(methodName: String) { - val callRange = callIdentifierRange ?: return - val methodRange = methodIdentifierRange ?: return - if (callRange.isValid && callRange.isValid) { - editor.document.replaceString(callRange.startOffset, callRange.endOffset, methodName) - editor.document.replaceString(methodRange.startOffset, methodRange.endOffset, methodName) - PsiDocumentManager.getInstance(project).commitDocument(editor.document) - } - } - - fun restartInDialog(isLinkUsed: Boolean = false) { - val methodRange = callIdentifierRange?.asTextRange - val methodName = if (methodRange != null) editor.document.getText(methodRange) else "" - InplaceExtractMethodCollector.openExtractDialog.log(project, isLinkUsed) - TemplateManagerImpl.getTemplateState(editor)?.gotoEnd(true) - val extractOptions = extractor.extractOptions.copy(methodName = methodName) - val rangeToReplace = createGreedyRangeMarker(editor.document, range) - val extractor = DuplicatesMethodExtractor(extractOptions, extractor.targetClass, rangeToReplace) - extractor.extractInDialog() - } - - private suspend fun restartInplace() { - val startTime = System.currentTimeMillis() - val identifierRange = readAction { callIdentifierRange?.asTextRange } - val methodName = if (identifierRange != null) readAction { editor.document.getText(identifierRange) } else null - - runWithDumbEditor(editor) { - withContext(Dispatchers.EDT) { - TemplateManagerImpl.getTemplateState(editor)?.gotoEnd(true) - } - val inplaceExtractor = readAction { InplaceMethodExtractor(editor, range, popupProvider, defaultExtractor) } - inplaceExtractor.extractAndRunTemplate(emptyList()) - if (methodName != null) { - writeCommandAction(project, ExtractMethodHandler.getRefactoringName()) { - inplaceExtractor.setMethodName(methodName) - } - } - } - - val endTime = System.currentTimeMillis() - InplaceExtractMethodCollector.previewUpdated.log(endTime - startTime) - } - - private suspend fun runWithDumbEditor(editor: Editor, action: suspend () -> Unit) { - val editorImpl = editor as? EditorImpl - if (editorImpl == null) { - action.invoke() - return - } - withContext(Dispatchers.EDT) { - editorImpl.startDumb() - } - try { - action.invoke() - } - finally { - withContext(Dispatchers.EDT) { - editor.stopDumbLater() - } - } - } - } \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/java/refactoring/ExtractMethodAndDuplicatesInplaceTest.kt b/java/java-tests/testSrc/com/intellij/java/refactoring/ExtractMethodAndDuplicatesInplaceTest.kt index 7adc28b0ac60..7ac74935be94 100644 --- a/java/java-tests/testSrc/com/intellij/java/refactoring/ExtractMethodAndDuplicatesInplaceTest.kt +++ b/java/java-tests/testSrc/com/intellij/java/refactoring/ExtractMethodAndDuplicatesInplaceTest.kt @@ -18,6 +18,7 @@ import com.intellij.openapi.util.TextRange import com.intellij.pom.java.LanguageLevel import com.intellij.refactoring.JavaRefactoringSettings import com.intellij.refactoring.RefactoringBundle +import com.intellij.refactoring.extractMethod.newImpl.ExtractMethodHelper import com.intellij.refactoring.extractMethod.newImpl.ExtractMethodService import com.intellij.refactoring.extractMethod.newImpl.MethodExtractor import com.intellij.refactoring.extractMethod.newImpl.inplace.DuplicatesMethodExtractor @@ -50,9 +51,7 @@ class ExtractMethodAndDuplicatesInplaceTest: LightJavaCodeInsightTestCase() { fun renameTemplate(templateState: TemplateState?, name: String) { if (templateState == null) return WriteCommandAction.runWriteCommandAction(templateState.project) { - val range = templateState.currentVariableRange!! - templateState.editor.document.replaceString(range.startOffset, range.endOffset, name) - templateState.update() + ExtractMethodHelper.renameTemplate(templateState, name) } } }