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.DataContext
import com.intellij.openapi.application.TransactionGuard
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.ScrollType
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.getExtractionContainers
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.KtFile
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) {
val dialog = createDialog(klass, targetParent)
TransactionGuard.submitTransaction(dialog.disposable, Runnable { dialog.show() })
createDialog(klass, targetParent).showWithTransaction()
}
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.editor.Editor
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.RefactoringDialog
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.refactoring.introduce.extractFunction.ui.ExtractFunctionParameterTablePanel
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.validateElement
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.psi.*
import org.jetbrains.kotlin.psi.psiUtil.quoteIfNeeded
@@ -241,88 +241,82 @@ class KotlinIntroduceParameterDialog private constructor(
}
fun performRefactoring() {
invokeRefactoring(
object : BaseRefactoringProcessor(myProject) {
override fun findUsages() = UsageInfo.EMPTY_ARRAY
close(DialogWrapper.OK_EXIT_CODE)
override fun performRefactoring(usages: Array<out UsageInfo>) {
fun createLambdaForArgument(function: KtFunction): KtExpression {
val statement = (function.bodyExpression as KtBlockExpression).statements.single()
val space = if (statement.isMultiLine()) "\n" else " "
val parameters = function.valueParameters
val parametersText = if (parameters.isNotEmpty()) {
" " + parameters.map { it.name }.joinToString() + " ->"
} else ""
val text = "{$parametersText$space${statement.text}$space}"
project.executeCommand(commandName) {
fun createLambdaForArgument(function: KtFunction): KtExpression {
val statement = (function.bodyExpression as KtBlockExpression).statements.single()
val space = if (statement.isMultiLine()) "\n" else " "
val parameters = function.valueParameters
val parametersText = if (parameters.isNotEmpty()) {
" " + parameters.map { it.name }.joinToString() + " ->"
} else ""
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()
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)
}
processDuplicates(duplicateReplacers, myProject, editor)
}
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.application.ApplicationManager
import com.intellij.openapi.application.TransactionGuard
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project
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.removeTemplateEntryBracesIfPossible
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.runSynchronouslyWithProgress
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
@@ -355,12 +357,15 @@ open class KotlinIntroduceParameterHandler(
if (introducer.startInplaceIntroduceTemplate()) return
}
KotlinIntroduceParameterDialog(project,
editor,
introduceParameterDescriptor,
suggestedNames.toTypedArray(),
listOf(replacementType) + replacementType.supertypes(),
helper).show()
val dialog = KotlinIntroduceParameterDialog(
project,
editor,
introduceParameterDescriptor,
suggestedNames.toTypedArray(),
listOf(replacementType) + replacementType.supertypes(),
helper
)
dialog.showWithTransaction()
}
)
}
@@ -508,7 +513,7 @@ open class KotlinIntroduceLambdaParameterHandler(
dialog.performRefactoring()
}
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.java.JavaLanguage
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.application.TransactionGuard
import com.intellij.openapi.command.CommandAdapter
import com.intellij.openapi.command.CommandEvent
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.project.Project
import com.intellij.openapi.roots.JavaProjectRootsUtil
import com.intellij.openapi.ui.DialogWrapper
import com.intellij.openapi.ui.Messages
import com.intellij.openapi.ui.popup.*
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.util.IdeDescriptorRenderers
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.j2k.ConverterSettings
import org.jetbrains.kotlin.j2k.JavaToKotlinConverter
@@ -981,13 +981,6 @@ fun KtNamedDeclaration.isCompanionMemberOf(klass: KtClassOrObject): Boolean {
return containingObject.isCompanion() && containingObject.containingClassOrObject == klass
}
internal fun KtDeclaration.withExpectedActuals(): List<KtDeclaration> {
val expect = liftToExpected() ?: return listOf(this)
val actuals = expect.actualsForExpected()
return listOf(expect) + actuals
}
internal fun KtDeclaration.resolveToExpectedDescriptorIfPossible(): DeclarationDescriptor {
val descriptor = unsafeResolveToDescriptor()
return descriptor.liftToExpected() ?: descriptor
fun DialogWrapper.showWithTransaction() {
TransactionGuard.submitTransaction(disposable, Runnable { show() })
}
@@ -13,6 +13,7 @@ import com.intellij.ide.util.PsiElementListCellRenderer
import com.intellij.lang.injection.InjectedLanguageManager
import com.intellij.lang.java.JavaLanguage
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.application.TransactionGuard
import com.intellij.openapi.command.CommandAdapter
import com.intellij.openapi.command.CommandEvent
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.project.Project
import com.intellij.openapi.roots.JavaProjectRootsUtil
import com.intellij.openapi.ui.DialogWrapper
import com.intellij.openapi.ui.Messages
import com.intellij.openapi.ui.popup.*
import com.intellij.openapi.util.Pass
@@ -990,4 +992,8 @@ internal fun KtDeclaration.withExpectedActuals(): List<KtDeclaration> {
internal fun KtDeclaration.resolveToExpectedDescriptorIfPossible(): DeclarationDescriptor {
val descriptor = unsafeResolveToDescriptor()
return descriptor.liftToExpected() ?: descriptor
}
fun DialogWrapper.showWithTransaction() {
TransactionGuard.submitTransaction(disposable, Runnable { show() })
}