Introduce Parameter: Fix exceptions caused by write actions in dialogs

#KT-24992 Fixed
This commit is contained in:
Alexey Sedunov
2018-06-19 14:20:25 +03:00
parent ed597e2da5
commit ba2f28720f
5 changed files with 98 additions and 101 deletions
@@ -18,7 +18,6 @@ package org.jetbrains.kotlin.idea.refactoring.introduce.extractClass
import com.intellij.openapi.actionSystem.CommonDataKeys import com.intellij.openapi.actionSystem.CommonDataKeys
import com.intellij.openapi.actionSystem.DataContext import com.intellij.openapi.actionSystem.DataContext
import com.intellij.openapi.application.TransactionGuard
import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.ScrollType import com.intellij.openapi.editor.ScrollType
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
@@ -35,6 +34,7 @@ import org.jetbrains.kotlin.idea.refactoring.SeparateFileWrapper
import org.jetbrains.kotlin.idea.refactoring.chooseContainerElementIfNecessary import org.jetbrains.kotlin.idea.refactoring.chooseContainerElementIfNecessary
import org.jetbrains.kotlin.idea.refactoring.getExtractionContainers import org.jetbrains.kotlin.idea.refactoring.getExtractionContainers
import org.jetbrains.kotlin.idea.refactoring.introduce.extractClass.ui.KotlinExtractSuperDialogBase import org.jetbrains.kotlin.idea.refactoring.introduce.extractClass.ui.KotlinExtractSuperDialogBase
import org.jetbrains.kotlin.idea.refactoring.showWithTransaction
import org.jetbrains.kotlin.psi.KtClassOrObject import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
@@ -79,8 +79,7 @@ abstract class KotlinExtractSuperHandlerBase(private val isExtractInterface: Boo
} }
private fun doInvoke(klass: KtClassOrObject, targetParent: PsiElement) { private fun doInvoke(klass: KtClassOrObject, targetParent: PsiElement) {
val dialog = createDialog(klass, targetParent) createDialog(klass, targetParent).showWithTransaction()
TransactionGuard.submitTransaction(dialog.disposable, Runnable { dialog.show() })
} }
private fun selectElements(klass: KtClassOrObject, editor: Editor?) { private fun selectElements(klass: KtClassOrObject, editor: Editor?) {
@@ -20,12 +20,10 @@ import com.intellij.openapi.command.impl.FinishMarkAction
import com.intellij.openapi.command.impl.StartMarkAction import com.intellij.openapi.command.impl.StartMarkAction
import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.refactoring.BaseRefactoringProcessor import com.intellij.openapi.ui.DialogWrapper
import com.intellij.refactoring.ui.NameSuggestionsField import com.intellij.refactoring.ui.NameSuggestionsField
import com.intellij.refactoring.ui.RefactoringDialog import com.intellij.refactoring.ui.RefactoringDialog
import com.intellij.ui.NonFocusableCheckBox import com.intellij.ui.NonFocusableCheckBox
import com.intellij.usageView.BaseUsageViewDescriptor
import com.intellij.usageView.UsageInfo
import org.jetbrains.kotlin.idea.KotlinFileType import org.jetbrains.kotlin.idea.KotlinFileType
import org.jetbrains.kotlin.idea.refactoring.introduce.extractFunction.ui.ExtractFunctionParameterTablePanel import org.jetbrains.kotlin.idea.refactoring.introduce.extractFunction.ui.ExtractFunctionParameterTablePanel
import org.jetbrains.kotlin.idea.refactoring.introduce.extractFunction.ui.KotlinExtractFunctionDialog import org.jetbrains.kotlin.idea.refactoring.introduce.extractFunction.ui.KotlinExtractFunctionDialog
@@ -34,6 +32,8 @@ import org.jetbrains.kotlin.idea.refactoring.isMultiLine
import org.jetbrains.kotlin.idea.refactoring.runRefactoringWithPostprocessing import org.jetbrains.kotlin.idea.refactoring.runRefactoringWithPostprocessing
import org.jetbrains.kotlin.idea.refactoring.validateElement import org.jetbrains.kotlin.idea.refactoring.validateElement
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
import org.jetbrains.kotlin.idea.util.application.executeCommand
import org.jetbrains.kotlin.idea.util.application.runWriteAction
import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.quoteIfNeeded import org.jetbrains.kotlin.psi.psiUtil.quoteIfNeeded
@@ -241,88 +241,82 @@ class KotlinIntroduceParameterDialog private constructor(
} }
fun performRefactoring() { fun performRefactoring() {
invokeRefactoring( close(DialogWrapper.OK_EXIT_CODE)
object : BaseRefactoringProcessor(myProject) {
override fun findUsages() = UsageInfo.EMPTY_ARRAY
override fun performRefactoring(usages: Array<out UsageInfo>) { project.executeCommand(commandName) {
fun createLambdaForArgument(function: KtFunction): KtExpression { fun createLambdaForArgument(function: KtFunction): KtExpression {
val statement = (function.bodyExpression as KtBlockExpression).statements.single() val statement = (function.bodyExpression as KtBlockExpression).statements.single()
val space = if (statement.isMultiLine()) "\n" else " " val space = if (statement.isMultiLine()) "\n" else " "
val parameters = function.valueParameters val parameters = function.valueParameters
val parametersText = if (parameters.isNotEmpty()) { val parametersText = if (parameters.isNotEmpty()) {
" " + parameters.map { it.name }.joinToString() + " ->" " " + parameters.map { it.name }.joinToString() + " ->"
} else "" } else ""
val text = "{$parametersText$space${statement.text}$space}" val text = "{$parametersText$space${statement.text}$space}"
return KtPsiFactory(myProject).createExpression(text) return KtPsiFactory(myProject).createExpression(text)
}
val chosenName = nameField.enteredName.quoteIfNeeded()
var chosenType = typeField.enteredName
var newArgumentValue = descriptor.newArgumentValue
var newReplacer = descriptor.occurrenceReplacer
val startMarkAction = StartMarkAction.start(editor, myProject, this@KotlinIntroduceParameterDialog.commandName)
lambdaExtractionDescriptor?.let { oldDescriptor ->
val newDescriptor = KotlinExtractFunctionDialog.createNewDescriptor(
oldDescriptor,
chosenName,
null,
parameterTablePanel?.selectedReceiverInfo,
parameterTablePanel?.selectedParameterInfos ?: listOf(),
null
)
val options = ExtractionGeneratorOptions.DEFAULT.copy(
target = ExtractionTarget.FAKE_LAMBDALIKE_FUNCTION,
allowExpressionBody = false
)
runWriteAction {
with (ExtractionGeneratorConfiguration(newDescriptor, options).generateDeclaration()) {
val function = declaration as KtFunction
val receiverType = function.receiverTypeReference?.text
val parameterTypes = function
.valueParameters.joinToString { it.typeReference!!.text }
val returnType = function.typeReference?.text ?: "Unit"
chosenType = (receiverType?.let { "$it." } ?: "") + "($parameterTypes) -> $returnType"
if (KtTokens.SUSPEND_KEYWORD in newDescriptor.modifiers) {
chosenType = "${KtTokens.SUSPEND_KEYWORD} $chosenType"
} }
newArgumentValue = createLambdaForArgument(function)
newReplacer = { }
val chosenName = nameField.enteredName.quoteIfNeeded() processDuplicates(duplicateReplacers, myProject, editor)
var chosenType = typeField.enteredName
var newArgumentValue = descriptor.newArgumentValue
var newReplacer = descriptor.occurrenceReplacer
val startMarkAction = StartMarkAction.start(editor, myProject, this@KotlinIntroduceParameterDialog.commandName)
lambdaExtractionDescriptor?.let { oldDescriptor ->
val newDescriptor = KotlinExtractFunctionDialog.createNewDescriptor(
oldDescriptor,
chosenName,
null,
parameterTablePanel?.selectedReceiverInfo,
parameterTablePanel?.selectedParameterInfos ?: listOf(),
null
)
val options = ExtractionGeneratorOptions.DEFAULT.copy(
target = ExtractionTarget.FAKE_LAMBDALIKE_FUNCTION,
allowExpressionBody = false
)
with (ExtractionGeneratorConfiguration(newDescriptor, options).generateDeclaration()) {
val function = declaration as KtFunction
val receiverType = function.receiverTypeReference?.text
val parameterTypes = function
.valueParameters.joinToString { it.typeReference!!.text }
val returnType = function.typeReference?.text ?: "Unit"
chosenType = (receiverType?.let { "$it." } ?: "") + "($parameterTypes) -> $returnType"
if (KtTokens.SUSPEND_KEYWORD in newDescriptor.modifiers) {
chosenType = "${KtTokens.SUSPEND_KEYWORD} $chosenType"
}
newArgumentValue = createLambdaForArgument(function)
newReplacer = { }
processDuplicates(duplicateReplacers, myProject, editor)
}
}
val descriptorToRefactor = descriptor.copy(
newParameterName = chosenName,
newParameterTypeText = chosenType,
argumentValue = newArgumentValue,
withDefaultValue = defaultValueCheckBox!!.isSelected,
occurrencesToReplace = with(descriptor) {
if (replaceAllCheckBox?.isSelected ?: true) {
occurrencesToReplace
}
else {
Collections.singletonList(originalOccurrence)
}
},
parametersToRemove = removeParamsCheckBoxes.filter { it.key.isEnabled && it.key.isSelected }.map { it.value },
occurrenceReplacer = newReplacer
)
val introduceParameter = { helper.configure(descriptorToRefactor).performRefactoring() }
introduceParameter.runRefactoringWithPostprocessing(myProject, INTRODUCE_PARAMETER_REFACTORING_ID) {
FinishMarkAction.finish(myProject, editor, startMarkAction)
}
} }
override fun createUsageViewDescriptor(usages: Array<out UsageInfo>) = BaseUsageViewDescriptor()
override fun getCommandName(): String = this@KotlinIntroduceParameterDialog.commandName
} }
) }
val descriptorToRefactor = descriptor.copy(
newParameterName = chosenName,
newParameterTypeText = chosenType,
argumentValue = newArgumentValue,
withDefaultValue = defaultValueCheckBox!!.isSelected,
occurrencesToReplace = with(descriptor) {
if (replaceAllCheckBox?.isSelected ?: true) {
occurrencesToReplace
}
else {
Collections.singletonList(originalOccurrence)
}
},
parametersToRemove = removeParamsCheckBoxes.filter { it.key.isEnabled && it.key.isSelected }.map { it.value },
occurrenceReplacer = newReplacer
)
val introduceParameter = { helper.configure(descriptorToRefactor).performRefactoring() }
introduceParameter.runRefactoringWithPostprocessing(myProject, INTRODUCE_PARAMETER_REFACTORING_ID) {
FinishMarkAction.finish(myProject, editor, startMarkAction)
}
}
} }
} }
@@ -18,6 +18,7 @@ package org.jetbrains.kotlin.idea.refactoring.introduce.introduceParameter
import com.intellij.openapi.actionSystem.DataContext import com.intellij.openapi.actionSystem.DataContext
import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.application.TransactionGuard
import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.psi.PsiDocumentManager import com.intellij.psi.PsiDocumentManager
@@ -45,6 +46,7 @@ import org.jetbrains.kotlin.idea.refactoring.introduce.*
import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.* import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.*
import org.jetbrains.kotlin.idea.refactoring.removeTemplateEntryBracesIfPossible import org.jetbrains.kotlin.idea.refactoring.removeTemplateEntryBracesIfPossible
import org.jetbrains.kotlin.idea.refactoring.runRefactoringWithPostprocessing import org.jetbrains.kotlin.idea.refactoring.runRefactoringWithPostprocessing
import org.jetbrains.kotlin.idea.refactoring.showWithTransaction
import org.jetbrains.kotlin.idea.references.mainReference import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.idea.runSynchronouslyWithProgress import org.jetbrains.kotlin.idea.runSynchronouslyWithProgress
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
@@ -355,12 +357,15 @@ open class KotlinIntroduceParameterHandler(
if (introducer.startInplaceIntroduceTemplate()) return if (introducer.startInplaceIntroduceTemplate()) return
} }
KotlinIntroduceParameterDialog(project, val dialog = KotlinIntroduceParameterDialog(
editor, project,
introduceParameterDescriptor, editor,
suggestedNames.toTypedArray(), introduceParameterDescriptor,
listOf(replacementType) + replacementType.supertypes(), suggestedNames.toTypedArray(),
helper).show() listOf(replacementType) + replacementType.supertypes(),
helper
)
dialog.showWithTransaction()
} }
) )
} }
@@ -508,7 +513,7 @@ open class KotlinIntroduceLambdaParameterHandler(
dialog.performRefactoring() dialog.performRefactoring()
} }
else { else {
dialog.show() dialog.showWithTransaction()
} }
} }
} }
@@ -13,6 +13,7 @@ import com.intellij.ide.util.PsiElementListCellRenderer
import com.intellij.lang.injection.InjectedLanguageManager import com.intellij.lang.injection.InjectedLanguageManager
import com.intellij.lang.java.JavaLanguage import com.intellij.lang.java.JavaLanguage
import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.application.TransactionGuard
import com.intellij.openapi.command.CommandAdapter import com.intellij.openapi.command.CommandAdapter
import com.intellij.openapi.command.CommandEvent import com.intellij.openapi.command.CommandEvent
import com.intellij.openapi.command.CommandProcessor import com.intellij.openapi.command.CommandProcessor
@@ -25,6 +26,7 @@ import com.intellij.openapi.editor.markup.TextAttributes
import com.intellij.openapi.options.ConfigurationException import com.intellij.openapi.options.ConfigurationException
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.JavaProjectRootsUtil import com.intellij.openapi.roots.JavaProjectRootsUtil
import com.intellij.openapi.ui.DialogWrapper
import com.intellij.openapi.ui.Messages import com.intellij.openapi.ui.Messages
import com.intellij.openapi.ui.popup.* import com.intellij.openapi.ui.popup.*
import com.intellij.openapi.util.Pass import com.intellij.openapi.util.Pass
@@ -74,8 +76,6 @@ import org.jetbrains.kotlin.idea.refactoring.changeSignature.toValVar
import org.jetbrains.kotlin.idea.refactoring.memberInfo.KtPsiClassWrapper import org.jetbrains.kotlin.idea.refactoring.memberInfo.KtPsiClassWrapper
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
import org.jetbrains.kotlin.idea.util.actualsForExpected
import org.jetbrains.kotlin.idea.util.liftToExpected
import org.jetbrains.kotlin.idea.util.string.collapseSpaces import org.jetbrains.kotlin.idea.util.string.collapseSpaces
import org.jetbrains.kotlin.j2k.ConverterSettings import org.jetbrains.kotlin.j2k.ConverterSettings
import org.jetbrains.kotlin.j2k.JavaToKotlinConverter import org.jetbrains.kotlin.j2k.JavaToKotlinConverter
@@ -981,13 +981,6 @@ fun KtNamedDeclaration.isCompanionMemberOf(klass: KtClassOrObject): Boolean {
return containingObject.isCompanion() && containingObject.containingClassOrObject == klass return containingObject.isCompanion() && containingObject.containingClassOrObject == klass
} }
internal fun KtDeclaration.withExpectedActuals(): List<KtDeclaration> { fun DialogWrapper.showWithTransaction() {
val expect = liftToExpected() ?: return listOf(this) TransactionGuard.submitTransaction(disposable, Runnable { show() })
val actuals = expect.actualsForExpected()
return listOf(expect) + actuals
}
internal fun KtDeclaration.resolveToExpectedDescriptorIfPossible(): DeclarationDescriptor {
val descriptor = unsafeResolveToDescriptor()
return descriptor.liftToExpected() ?: descriptor
} }
@@ -13,6 +13,7 @@ import com.intellij.ide.util.PsiElementListCellRenderer
import com.intellij.lang.injection.InjectedLanguageManager import com.intellij.lang.injection.InjectedLanguageManager
import com.intellij.lang.java.JavaLanguage import com.intellij.lang.java.JavaLanguage
import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.application.TransactionGuard
import com.intellij.openapi.command.CommandAdapter import com.intellij.openapi.command.CommandAdapter
import com.intellij.openapi.command.CommandEvent import com.intellij.openapi.command.CommandEvent
import com.intellij.openapi.command.CommandProcessor import com.intellij.openapi.command.CommandProcessor
@@ -25,6 +26,7 @@ import com.intellij.openapi.editor.markup.TextAttributes
import com.intellij.openapi.options.ConfigurationException import com.intellij.openapi.options.ConfigurationException
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.JavaProjectRootsUtil import com.intellij.openapi.roots.JavaProjectRootsUtil
import com.intellij.openapi.ui.DialogWrapper
import com.intellij.openapi.ui.Messages import com.intellij.openapi.ui.Messages
import com.intellij.openapi.ui.popup.* import com.intellij.openapi.ui.popup.*
import com.intellij.openapi.util.Pass import com.intellij.openapi.util.Pass
@@ -990,4 +992,8 @@ internal fun KtDeclaration.withExpectedActuals(): List<KtDeclaration> {
internal fun KtDeclaration.resolveToExpectedDescriptorIfPossible(): DeclarationDescriptor { internal fun KtDeclaration.resolveToExpectedDescriptorIfPossible(): DeclarationDescriptor {
val descriptor = unsafeResolveToDescriptor() val descriptor = unsafeResolveToDescriptor()
return descriptor.liftToExpected() ?: descriptor return descriptor.liftToExpected() ?: descriptor
}
fun DialogWrapper.showWithTransaction() {
TransactionGuard.submitTransaction(disposable, Runnable { show() })
} }