[extract method] cleanup: use range marker to track extracted range

GitOrigin-RevId: cf23863e723b305a0024015ab060bb70071f614b
This commit is contained in:
Alexandr Suhinin
2024-08-07 21:40:44 +03:00
committed by intellij-monorepo-bot
parent 9d6675e877
commit c89c0bb036
7 changed files with 49 additions and 38 deletions

View File

@@ -6,7 +6,6 @@ import com.intellij.openapi.util.TextRange
import com.intellij.psi.*
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.refactoring.IntroduceVariableUtil
import com.intellij.refactoring.util.RefactoringUtil
import com.intellij.util.CommonJavaRefactoringUtil
class ExtractSelector {

View File

@@ -2,7 +2,6 @@
package com.intellij.refactoring.extractMethod.newImpl
import com.intellij.openapi.util.Key
import com.intellij.openapi.util.TextRange
import com.intellij.psi.*
import com.intellij.psi.util.PsiTreeUtil
@@ -34,7 +33,6 @@ class JavaDuplicatesFinder(pattern: List<PsiElement>, private val parametrizedEx
private fun getElementInPhysicalFile(element: PsiElement): PsiElement? = element.getCopyableUserData(ELEMENT_IN_PHYSICAL_FILE)
fun textRangeOf(range: List<PsiElement>) = TextRange(range.first().textRange.startOffset, range.last().textRange.endOffset)
}
private val pattern: List<PsiElement> = pattern.filterNot(::isNoise)

View File

@@ -80,6 +80,10 @@ class MethodExtractor {
return
}
readAction {
sendRefactoringStartedEvent(elements.toTypedArray())
}
val prepareStart = System.currentTimeMillis()
val descriptorsForAllTargetPlaces = prepareDescriptorsForAllTargetPlaces(file.project, editor, elements)
if (descriptorsForAllTargetPlaces.isEmpty()) return
@@ -121,7 +125,7 @@ class MethodExtractor {
try {
readAction { findAllOptionsToExtract(elements) }
} catch (exception: ExtractException) {
InplaceExtractUtils.showExtractErrorHint(editor, exception)
InplaceExtractUtils.showExtractErrorHint(editor, exception.message.orEmpty(), exception.problems)
emptyList()
}
}

View File

@@ -12,6 +12,7 @@ import com.intellij.openapi.application.constrainedReadAction
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.ScrollType
import com.intellij.openapi.util.TextRange
import com.intellij.platform.ide.progress.runWithModalProgressBlocking
@@ -29,7 +30,8 @@ import com.intellij.refactoring.extractMethod.newImpl.*
import com.intellij.refactoring.extractMethod.newImpl.ExtractMethodHelper.inputParameterOf
import com.intellij.refactoring.extractMethod.newImpl.ExtractMethodHelper.replacePsiRange
import com.intellij.refactoring.extractMethod.newImpl.ExtractMethodHelper.replaceWithMethod
import com.intellij.refactoring.extractMethod.newImpl.JavaDuplicatesFinder.Companion.textRangeOf
import com.intellij.refactoring.extractMethod.newImpl.inplace.InplaceExtractUtils.createGreedyRangeMarker
import com.intellij.refactoring.extractMethod.newImpl.inplace.InplaceExtractUtils.textRangeOf
import com.intellij.refactoring.extractMethod.newImpl.structures.ExtractOptions
import com.intellij.refactoring.extractMethod.newImpl.structures.InputParameter
import com.intellij.refactoring.introduceField.ElementToWorkOn
@@ -39,7 +41,14 @@ import com.siyeh.ig.psiutils.SideEffectChecker.mayHaveSideEffects
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
class DuplicatesMethodExtractor(val extractOptions: ExtractOptions, val targetClass: PsiClass, val elements: List<PsiElement>) {
class DuplicatesMethodExtractor(val extractOptions: ExtractOptions, val targetClass: PsiClass, val rangeToReplace: RangeMarker) {
internal fun getElements(): List<PsiElement> {
val file = targetClass.containingFile
val range = rangeToReplace.textRange
require(rangeToReplace.isValid)
return ExtractSelector().suggestElementsToExtract(file, range)
}
companion object {
private val isSilentMode = ApplicationManager.getApplication().isUnitTestMode
@@ -48,6 +57,8 @@ class DuplicatesMethodExtractor(val extractOptions: ExtractOptions, val targetCl
fun create(targetClass: PsiClass, elements: List<PsiElement>, methodName: String, makeStatic: Boolean): DuplicatesMethodExtractor {
val file = targetClass.containingFile
val document = file.viewProvider.document ?: throw IllegalStateException()
val rangeToReplace = createGreedyRangeMarker(document, textRangeOf(elements.first(), elements.last()))
JavaDuplicatesFinder.linkCopiedClassMembersWithOrigin(file)
val copiedFile = file.copy() as PsiFile
val copiedClass = PsiTreeUtil.findSameElementInCopy(targetClass, copiedFile)
@@ -56,19 +67,18 @@ class DuplicatesMethodExtractor(val extractOptions: ExtractOptions, val targetCl
val range = virtualExpressionRange ?: TextRange(elements.first().textRange.startOffset, elements.last().textRange.endOffset)
val copiedElements = ExtractSelector().suggestElementsToExtract(copiedFile, range)
val extractOptions = findExtractOptions(copiedClass, copiedElements, methodName, makeStatic)
return DuplicatesMethodExtractor(extractOptions, targetClass, elements)
return DuplicatesMethodExtractor(extractOptions, targetClass, rangeToReplace)
}
}
private var callsToReplace: List<SmartPsiElementPointer<PsiElement>>? = null
suspend fun extract(): ExtractedElements {
val file = readAction { targetClass.containingFile }
val project = file.project
val elements = readAction { getElements() }
val preparedElements = readAction { MethodExtractor().prepareRefactoringElements(extractOptions) }
val (callsPointer, methodPointer) = writeCommandAction(project, ExtractMethodHandler.getRefactoringName()) {
val (calls, method) = replaceWithMethod(targetClass, this@DuplicatesMethodExtractor.elements, preparedElements)
val (calls, method) = replaceWithMethod(targetClass, elements, preparedElements)
val methodPointer = SmartPointerManager.createPointer(method)
val callsPointer = calls.map(SmartPointerManager::createPointer)
Pair(callsPointer, methodPointer)
@@ -80,14 +90,13 @@ class DuplicatesMethodExtractor(val extractOptions: ExtractOptions, val targetCl
callsPointer.map { it.element ?: throw IllegalStateException() }
}
this.callsToReplace = callsPointer
return ExtractedElements(replacedCalls, replacedMethod)
}
suspend fun replaceDuplicates(editor: Editor, method: PsiMethod, beforeDuplicateReplaced: (candidate: List<PsiElement>) -> Unit = {}) {
val project = readAction { editor.project } ?: return
val calls = readAction { callsToReplace?.map { it.element!! } }?: return
val calls = readAction { getElements() }
if (calls.isEmpty()) return
val defaultExtraction = ExtractedElements(calls, method)
val prepareTimeStart = System.currentTimeMillis()
@@ -262,7 +271,7 @@ class DuplicatesMethodExtractor(val extractOptions: ExtractOptions, val targetCl
val initialPosition = editor.caretModel.logicalPosition
val confirmedDuplicates = mutableListOf<Duplicate>()
duplicates.forEach { duplicate ->
val highlighters = DuplicatesImpl.previewMatch(project, editor, textRangeOf(duplicate.candidate))
val highlighters = DuplicatesImpl.previewMatch(project, editor, textRangeOf(duplicate.candidate.first(), duplicate.candidate.last()))
try {
val prompt = ReplacePromptDialog(false, JavaRefactoringBundle.message("process.duplicates.title"), project)
prompt.show()
@@ -343,8 +352,7 @@ fun DuplicatesMethodExtractor.extractInDialog() {
if (!passFieldsAsParameters) {
JavaRefactoringSettings.getInstance().EXTRACT_STATIC_METHOD = dialogOptions.isStatic
}
val mappedExtractor = DuplicatesMethodExtractor(dialogOptions, targetClass, elements)
MethodExtractor.sendRefactoringStartedEvent(elements.toTypedArray())
val mappedExtractor = DuplicatesMethodExtractor(dialogOptions, targetClass, rangeToReplace)
//todo avoid blocking, suspend should be propagated further
val (_, method) = runWithModalProgressBlocking(extractOptions.project, ExtractMethodHandler.getRefactoringName()) {
mappedExtractor.extract()

View File

@@ -16,6 +16,7 @@ import com.intellij.internal.statistic.eventLog.events.FusInputEvent
import com.intellij.java.refactoring.JavaRefactoringBundle
import com.intellij.openapi.Disposable
import com.intellij.openapi.actionSystem.IdeActions
import com.intellij.openapi.application.EDT
import com.intellij.openapi.diff.DiffColors
import com.intellij.openapi.editor.*
import com.intellij.openapi.editor.colors.EditorColors
@@ -38,10 +39,11 @@ import com.intellij.psi.*
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.psi.util.PsiUtil
import com.intellij.refactoring.RefactoringBundle
import com.intellij.refactoring.extractMethod.newImpl.ExtractException
import com.intellij.refactoring.rename.inplace.TemplateInlayUtil
import com.intellij.ui.GotItTooltip
import com.intellij.util.SmartList
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.jetbrains.annotations.Nls
import java.awt.Point
import java.awt.event.KeyEvent
@@ -65,14 +67,18 @@ object InplaceExtractUtils {
return true
}
fun showExtractErrorHint(editor: Editor, error: @Nls String) {
suspend fun showExtractErrorHint(editor: Editor, error: @Nls String) {
val message: @Nls String = JavaRefactoringBundle.message("extract.method.error.prefix") + " " + error
IdeUiService.getInstance().showErrorHint(editor, message)
withContext(Dispatchers.EDT) {
IdeUiService.getInstance().showErrorHint(editor, message)
}
}
fun showExtractErrorHint(editor: Editor, exception: ExtractException){
showExtractErrorHint(editor, exception.message.orEmpty())
highlightErrors(editor, exception.problems)
suspend fun showExtractErrorHint(editor: Editor, error: @Nls String, highlightedRanges: List<TextRange>){
showExtractErrorHint(editor, error)
withContext(Dispatchers.EDT) {
highlightErrors(editor, highlightedRanges)
}
}
private fun highlightErrors(editor: Editor, ranges: List<TextRange>) {
@@ -285,13 +291,17 @@ object InplaceExtractUtils {
return PsiTreeUtil.findElementOfClassAtOffset(file, offset, T::class.java, false)
}
fun createGreedyRangeMarker(document: Document, range: TextRange): RangeMarker {
internal fun createGreedyRangeMarker(document: Document, range: TextRange): RangeMarker {
return document.createRangeMarker(range).apply {
isGreedyToLeft = true
isGreedyToRight = true
}
}
internal fun textRangeOf(first: PsiElement, last: PsiElement): TextRange {
return TextRange(first.textRange.startOffset, last.textRange.endOffset)
}
private fun IntRange.trim(maxLength: Int) = first until first + minOf(maxLength, last - first + 1)
fun getLinesFromTextRange(document: Document, range: TextRange, maxLength: Int): IntRange {

View File

@@ -30,7 +30,6 @@ import com.intellij.refactoring.extractMethod.newImpl.CodeFragmentAnalyzer
import com.intellij.refactoring.extractMethod.newImpl.ExtractMethodHelper
import com.intellij.refactoring.extractMethod.newImpl.ExtractMethodPipeline
import com.intellij.refactoring.extractMethod.newImpl.ExtractMethodService
import com.intellij.refactoring.extractMethod.newImpl.ExtractSelector
import com.intellij.refactoring.extractMethod.newImpl.MethodExtractor
import com.intellij.refactoring.extractMethod.newImpl.inplace.InplaceExtractUtils.addInlaySettingsElement
import com.intellij.refactoring.extractMethod.newImpl.inplace.InplaceExtractUtils.checkReferenceIdentifier
@@ -78,20 +77,17 @@ internal class InplaceMethodExtractor(private val editor: Editor,
private val project = file.project
private fun createExtractor(): DuplicatesMethodExtractor {
val elements = ExtractSelector().suggestElementsToExtract(defaultExtractor.targetClass.containingFile, range)
var options = defaultExtractor.extractOptions
if (popupProvider.makeStatic == true) {
val analyzer = CodeFragmentAnalyzer(options.elements)
options = ExtractMethodPipeline.withForcedStatic(analyzer, options) ?: throw IllegalStateException()
}
return DuplicatesMethodExtractor(options, defaultExtractor.targetClass, elements)
val rangeToReplace = createGreedyRangeMarker(editor.document, range)
return DuplicatesMethodExtractor(options, defaultExtractor.targetClass, rangeToReplace)
}
suspend fun extractAndRunTemplate(suggestedNames: List<String>) {
try {
readAction {
MethodExtractor.sendRefactoringStartedEvent(extractor.elements.toTypedArray())
}
ExtractMethodHelper.mergeWriteCommands(editor, disposable, ExtractMethodHandler.getRefactoringName())
val (callElements, method) = extractor.extract()
@@ -208,9 +204,9 @@ internal class InplaceMethodExtractor(private val editor: Editor,
val methodName = if (methodRange != null) editor.document.getText(methodRange) else ""
InplaceExtractMethodCollector.openExtractDialog.log(project, isLinkUsed)
TemplateManagerImpl.getTemplateState(editor)?.gotoEnd(true)
val elements = ExtractSelector().suggestElementsToExtract(extractor.targetClass.containingFile, range)
val extractOptions = extractor.extractOptions.copy(methodName = methodName)
val extractor = DuplicatesMethodExtractor(extractOptions, extractor.targetClass, elements)
val rangeToReplace = createGreedyRangeMarker(editor.document, range)
val extractor = DuplicatesMethodExtractor(extractOptions, extractor.targetClass, rangeToReplace)
extractor.extractInDialog()
}

View File

@@ -4,7 +4,6 @@ package com.intellij.refactoring.extractMethod.newImpl.parameterObject
import com.intellij.codeInsight.hint.EditorCodePreview
import com.intellij.codeInsight.hint.HintManager
import com.intellij.java.refactoring.JavaRefactoringBundle
import com.intellij.openapi.application.EDT
import com.intellij.openapi.application.invokeLater
import com.intellij.openapi.application.readAction
import com.intellij.openapi.command.writeCommandAction
@@ -16,16 +15,14 @@ import com.intellij.psi.*
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.psi.util.PsiUtil
import com.intellij.refactoring.extractMethod.ExtractMethodHandler
import com.intellij.refactoring.extractMethod.newImpl.ExtractException
import com.intellij.refactoring.extractMethod.newImpl.ExtractMethodHelper
import com.intellij.refactoring.extractMethod.newImpl.MethodExtractor
import com.intellij.refactoring.extractMethod.newImpl.inplace.EditorState
import com.intellij.refactoring.extractMethod.newImpl.inplace.ExtractMethodTemplateBuilder
import com.intellij.refactoring.extractMethod.newImpl.inplace.InplaceExtractUtils
import com.intellij.refactoring.extractMethod.newImpl.inplace.InplaceExtractUtils.createGreedyRangeMarker
import com.intellij.refactoring.extractMethod.newImpl.inplace.InplaceExtractUtils.showExtractErrorHint
import com.intellij.refactoring.extractMethod.newImpl.inplace.TemplateField
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
private data class IntroduceObjectResult(
val introducedClass: PsiClass,
@@ -44,9 +41,8 @@ internal object ResultObjectExtractor {
val affectedReferences = readAction { ParameterObjectUtils.findAffectedReferences(variables, scope) }
if (affectedReferences == null) {
withContext(Dispatchers.EDT) {
InplaceExtractUtils.showExtractErrorHint(editor, ExtractException(JavaRefactoringBundle.message("extract.method.error.many.outputs"), variables))
}
showExtractErrorHint(editor, JavaRefactoringBundle.message("extract.method.error.many.outputs"), variables.map { it.textRange })
return
}
val shouldInsertRecord = readAction { PsiUtil.isAvailable(JavaFeature.RECORDS, variables.first()) }