Fix ExtractDeclarationFromCurrentFileIntention assertion
Refactorings should be invoked inside transaction 'exception occurs when extracting sealed class from file with the same name assertion failed when refactoring tries to use MoveKotlinTopLevelDeclarationsDialog + move refactorings + small improvement of applicability range calculation Fixed #KT-33357
This commit is contained in:
+59
-41
@@ -11,7 +11,10 @@ import com.intellij.codeInsight.navigation.NavigationUtil
|
|||||||
import com.intellij.openapi.application.ApplicationManager
|
import com.intellij.openapi.application.ApplicationManager
|
||||||
import com.intellij.openapi.editor.Editor
|
import com.intellij.openapi.editor.Editor
|
||||||
import com.intellij.openapi.fileEditor.FileEditorManager
|
import com.intellij.openapi.fileEditor.FileEditorManager
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.openapi.util.TextRange
|
import com.intellij.openapi.util.TextRange
|
||||||
|
import com.intellij.psi.PsiDirectory
|
||||||
|
import com.intellij.psi.PsiFile
|
||||||
import com.intellij.refactoring.move.MoveCallback
|
import com.intellij.refactoring.move.MoveCallback
|
||||||
import com.intellij.refactoring.util.CommonRefactoringUtil
|
import com.intellij.refactoring.util.CommonRefactoringUtil
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
@@ -22,7 +25,9 @@ import org.jetbrains.kotlin.idea.core.moveCaret
|
|||||||
import org.jetbrains.kotlin.idea.intentions.SelfTargetingRangeIntention
|
import org.jetbrains.kotlin.idea.intentions.SelfTargetingRangeIntention
|
||||||
import org.jetbrains.kotlin.idea.refactoring.createKotlinFile
|
import org.jetbrains.kotlin.idea.refactoring.createKotlinFile
|
||||||
import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.ui.MoveKotlinTopLevelDeclarationsDialog
|
import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.ui.MoveKotlinTopLevelDeclarationsDialog
|
||||||
|
import org.jetbrains.kotlin.idea.refactoring.showWithTransaction
|
||||||
import org.jetbrains.kotlin.lexer.KtTokens
|
import org.jetbrains.kotlin.lexer.KtTokens
|
||||||
|
import org.jetbrains.kotlin.name.FqName
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.endOffset
|
import org.jetbrains.kotlin.psi.psiUtil.endOffset
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.isAncestor
|
import org.jetbrains.kotlin.psi.psiUtil.isAncestor
|
||||||
@@ -47,11 +52,10 @@ class ExtractDeclarationFromCurrentFileIntention :
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun applicabilityRange(element: KtClassOrObject): TextRange? {
|
override fun applicabilityRange(element: KtClassOrObject): TextRange? {
|
||||||
if (element.name == null) return null
|
element.name ?: return null
|
||||||
if (element.parent !is KtFile) return null
|
if (element.parent !is KtFile) return null
|
||||||
if (element.hasModifier(KtTokens.PRIVATE_KEYWORD)) return null
|
if (element.hasModifier(KtTokens.PRIVATE_KEYWORD)) return null
|
||||||
if (element.containingKtFile.declarations.size == 1) return null
|
if (element.containingKtFile.run { declarations.size == 1 || containingDirectory === null }) return null
|
||||||
|
|
||||||
val extraClassesToMove = element.tryGetExtraClassesToMove() ?: return null
|
val extraClassesToMove = element.tryGetExtraClassesToMove() ?: return null
|
||||||
|
|
||||||
val startOffset = when (element) {
|
val startOffset = when (element) {
|
||||||
@@ -71,7 +75,7 @@ class ExtractDeclarationFromCurrentFileIntention :
|
|||||||
override fun startInWriteAction() = false
|
override fun startInWriteAction() = false
|
||||||
|
|
||||||
override fun applyTo(element: KtClassOrObject, editor: Editor?) {
|
override fun applyTo(element: KtClassOrObject, editor: Editor?) {
|
||||||
if (editor == null) throw IllegalArgumentException("This intention requires an editor")
|
requireNotNull(editor) { "This intention requires an editor" }
|
||||||
val file = element.containingKtFile
|
val file = element.containingKtFile
|
||||||
val project = file.project
|
val project = file.project
|
||||||
val originalOffset = editor.caretModel.offset - element.startOffset
|
val originalOffset = editor.caretModel.offset - element.startOffset
|
||||||
@@ -79,38 +83,17 @@ class ExtractDeclarationFromCurrentFileIntention :
|
|||||||
val packageName = file.packageFqName
|
val packageName = file.packageFqName
|
||||||
val targetFileName = "${element.name}.kt"
|
val targetFileName = "${element.name}.kt"
|
||||||
val targetFile = directory.findFile(targetFileName)
|
val targetFile = directory.findFile(targetFileName)
|
||||||
if (targetFile != null) {
|
|
||||||
|
if (targetFile !== null) {
|
||||||
if (ApplicationManager.getApplication().isUnitTestMode) {
|
if (ApplicationManager.getApplication().isUnitTestMode) {
|
||||||
throw CommonRefactoringUtil.RefactoringErrorHintException("File $targetFileName already exists")
|
throw CommonRefactoringUtil.RefactoringErrorHintException("File $targetFileName already exists")
|
||||||
}
|
}
|
||||||
|
|
||||||
// If automatic move is not possible, fall back to full-fledged Move Declarations refactoring
|
// If automatic move is not possible, fall back to full-fledged Move Declarations refactoring
|
||||||
ApplicationManager.getApplication().invokeLater {
|
runFullFledgedMoveRefactoring(project, element, packageName, directory, targetFile, file)
|
||||||
MoveKotlinTopLevelDeclarationsDialog(
|
|
||||||
project,
|
|
||||||
setOf(element),
|
|
||||||
packageName.asString(),
|
|
||||||
directory,
|
|
||||||
targetFile as? KtFile,
|
|
||||||
true,
|
|
||||||
true,
|
|
||||||
true,
|
|
||||||
true,
|
|
||||||
MoveCallback {
|
|
||||||
runBlocking {
|
|
||||||
withTimeoutOrNull(TIMEOUT_FOR_IMPORT_OPTIMIZING_MS) {
|
|
||||||
OptimizeImportsProcessor(
|
|
||||||
project,
|
|
||||||
file
|
|
||||||
).run()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
).show()
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
val moveTarget = KotlinMoveTargetForDeferredFile(packageName, directory, null) {
|
|
||||||
|
val moveTarget = KotlinMoveTargetForDeferredFile(packageName, directory, targetFile = null) {
|
||||||
createKotlinFile(targetFileName, directory, packageName.asString())
|
createKotlinFile(targetFileName, directory, packageName.asString())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,22 +103,57 @@ class ExtractDeclarationFromCurrentFileIntention :
|
|||||||
}
|
}
|
||||||
?: MoveSource(element)
|
?: MoveSource(element)
|
||||||
|
|
||||||
|
val moveCallBack = MoveCallback {
|
||||||
|
val newFile = directory.findFile(targetFileName) as KtFile
|
||||||
|
val newDeclaration = newFile.declarations.first()
|
||||||
|
NavigationUtil.activateFileWithPsiElement(newFile)
|
||||||
|
FileEditorManager.getInstance(project).selectedTextEditor?.moveCaret(newDeclaration.startOffset + originalOffset)
|
||||||
|
runBlocking { withTimeoutOrNull(TIMEOUT_FOR_IMPORT_OPTIMIZING_MS) { OptimizeImportsProcessor(project, file).run() } }
|
||||||
|
}
|
||||||
|
|
||||||
val descriptor = MoveDeclarationsDescriptor(
|
val descriptor = MoveDeclarationsDescriptor(
|
||||||
project = project,
|
project,
|
||||||
moveSource = moveSource,
|
moveSource,
|
||||||
moveTarget = moveTarget,
|
moveTarget,
|
||||||
delegate = MoveDeclarationsDelegate.TopLevel,
|
MoveDeclarationsDelegate.TopLevel,
|
||||||
searchInCommentsAndStrings = false,
|
searchInCommentsAndStrings = false,
|
||||||
searchInNonCode = false,
|
searchInNonCode = false,
|
||||||
moveCallback = MoveCallback {
|
moveCallback = moveCallBack
|
||||||
val newFile = directory.findFile(targetFileName) as KtFile
|
|
||||||
val newDeclaration = newFile.declarations.first()
|
|
||||||
NavigationUtil.activateFileWithPsiElement(newFile)
|
|
||||||
FileEditorManager.getInstance(project).selectedTextEditor?.moveCaret(newDeclaration.startOffset + originalOffset)
|
|
||||||
runBlocking { withTimeoutOrNull(TIMEOUT_FOR_IMPORT_OPTIMIZING_MS) { OptimizeImportsProcessor(project, file).run() } }
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
MoveKotlinDeclarationsProcessor(descriptor).run()
|
MoveKotlinDeclarationsProcessor(descriptor).run()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun runFullFledgedMoveRefactoring(
|
||||||
|
project: Project,
|
||||||
|
element: KtClassOrObject,
|
||||||
|
packageName: FqName,
|
||||||
|
directory: PsiDirectory,
|
||||||
|
targetFile: PsiFile?,
|
||||||
|
file: KtFile
|
||||||
|
) {
|
||||||
|
ApplicationManager.getApplication().invokeLater {
|
||||||
|
|
||||||
|
val callBack = MoveCallback {
|
||||||
|
runBlocking {
|
||||||
|
withTimeoutOrNull(TIMEOUT_FOR_IMPORT_OPTIMIZING_MS) {
|
||||||
|
OptimizeImportsProcessor(project, file).run()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MoveKotlinTopLevelDeclarationsDialog(
|
||||||
|
project,
|
||||||
|
setOf(element),
|
||||||
|
packageName.asString(),
|
||||||
|
directory,
|
||||||
|
targetFile as? KtFile,
|
||||||
|
/* moveToPackage = */ true,
|
||||||
|
/* searchInComments = */ true,
|
||||||
|
/* searchForTextOccurrences = */ true,
|
||||||
|
/* deleteEmptySourceFiles = */ true,
|
||||||
|
callBack
|
||||||
|
).showWithTransaction()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user