[extract method] decouple settings from the extract template

GitOrigin-RevId: a628ca0ecfa302e9920c315e7adacc14471b6053
This commit is contained in:
Alexandr Suhinin
2024-08-12 09:29:26 +00:00
committed by intellij-monorepo-bot
parent 1c8daa89b7
commit 13a49ea232
5 changed files with 96 additions and 81 deletions
@@ -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()
}
}
}
}
@@ -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<PsiElement>, 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<String>){
private suspend fun runInplaceExtract(editor: Editor, extractor: DuplicatesMethodExtractor, methodNames: List<String>){
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)
}
@@ -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<PsiElement> {
val file = targetClass.containingFile
val range = rangeToReplace.textRange
@@ -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>("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()
}
}
}
}
@@ -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)
}
}
}