Fix J2K to apply post convert intentions on EDT only

#KT-16754 fixed
This commit is contained in:
Simon Ogorodnik
2017-03-29 22:23:00 +03:00
parent 6cbeffe4b2
commit 8bd23d71a7
6 changed files with 69 additions and 39 deletions
@@ -58,10 +58,6 @@ abstract class SelfTargetingIntention<TElement : PsiElement>(
abstract fun applyTo(element: TElement, editor: Editor?) abstract fun applyTo(element: TElement, editor: Editor?)
protected fun <R> runInWriteActionOrHere(inWriteAction: Boolean = true, action: () -> R): R =
if (inWriteAction) runWriteAction(action)
else run(action)
private fun getTarget(editor: Editor, file: PsiFile): TElement? { private fun getTarget(editor: Editor, file: PsiFile): TElement? {
val offset = editor.caretModel.offset val offset = editor.caretModel.offset
val leaf1 = file.findElementAt(offset) val leaf1 = file.findElementAt(offset)
@@ -21,12 +21,15 @@ import com.intellij.codeInsight.editorActions.TextBlockTransferableData
import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.RangeMarker import com.intellij.openapi.editor.RangeMarker
import com.intellij.openapi.progress.ProgressManager
import com.intellij.openapi.project.DumbService import com.intellij.openapi.project.DumbService
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Ref import com.intellij.openapi.util.Ref
import com.intellij.openapi.util.TextRange import com.intellij.openapi.util.TextRange
import com.intellij.openapi.util.ThrowableComputable
import com.intellij.psi.* import com.intellij.psi.*
import org.jetbrains.annotations.TestOnly import org.jetbrains.annotations.TestOnly
import org.jetbrains.kotlin.idea.actions.JavaToKotlinAction
import org.jetbrains.kotlin.idea.caches.resolve.resolveImportReference import org.jetbrains.kotlin.idea.caches.resolve.resolveImportReference
import org.jetbrains.kotlin.idea.codeInsight.KotlinCopyPasteReferenceProcessor import org.jetbrains.kotlin.idea.codeInsight.KotlinCopyPasteReferenceProcessor
import org.jetbrains.kotlin.idea.codeInsight.KotlinReferenceData import org.jetbrains.kotlin.idea.codeInsight.KotlinReferenceData
@@ -34,6 +37,7 @@ import org.jetbrains.kotlin.idea.editor.KotlinEditorOptions
import org.jetbrains.kotlin.idea.j2k.IdeaJavaToKotlinServices import org.jetbrains.kotlin.idea.j2k.IdeaJavaToKotlinServices
import org.jetbrains.kotlin.idea.j2k.J2kPostProcessor import org.jetbrains.kotlin.idea.j2k.J2kPostProcessor
import org.jetbrains.kotlin.idea.util.ImportInsertHelper import org.jetbrains.kotlin.idea.util.ImportInsertHelper
import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.idea.util.application.runWriteAction import org.jetbrains.kotlin.idea.util.application.runWriteAction
import org.jetbrains.kotlin.j2k.AfterConversionPass import org.jetbrains.kotlin.j2k.AfterConversionPass
import org.jetbrains.kotlin.j2k.ConverterSettings import org.jetbrains.kotlin.j2k.ConverterSettings
@@ -212,7 +216,17 @@ internal fun ElementAndTextList.convertCodeToKotlin(project: Project): Conversio
) )
val inputElements = this.toList().filterIsInstance<PsiElement>() val inputElements = this.toList().filterIsInstance<PsiElement>()
val results = converter.elementsToKotlin(inputElements).results val results =
ProgressManager.getInstance().runProcessWithProgressSynchronously(
ThrowableComputable<JavaToKotlinConverter.Result, Exception> {
runReadAction { converter.elementsToKotlin(inputElements) }
},
JavaToKotlinAction.title,
false,
project
).results
val importsToAdd = LinkedHashSet<FqName>() val importsToAdd = LinkedHashSet<FqName>()
var resultIndex = 0 var resultIndex = 0
@@ -41,12 +41,10 @@ class IfThenToElvisInspection : IntentionBasedInspection<KtIfExpression>(
{ it -> it.isUsedAsExpression(it.analyze(BodyResolveMode.PARTIAL)) } { it -> it.isUsedAsExpression(it.analyze(BodyResolveMode.PARTIAL)) }
) )
class IfThenToElvisIntention(private val fromJ2K: Boolean) : SelfTargetingOffsetIndependentIntention<KtIfExpression>( class IfThenToElvisIntention : SelfTargetingOffsetIndependentIntention<KtIfExpression>(
KtIfExpression::class.java, KtIfExpression::class.java,
"Replace 'if' expression with elvis expression" "Replace 'if' expression with elvis expression"
) { ) {
@Suppress("unused")
constructor(): this(fromJ2K = false)
private fun KtExpression.clausesReplaceableByElvis(firstClause: KtExpression, secondClause: KtExpression, context: BindingContext) = private fun KtExpression.clausesReplaceableByElvis(firstClause: KtExpression, secondClause: KtExpression, context: BindingContext) =
!firstClause.isNullOrBlockExpression() && !firstClause.isNullOrBlockExpression() &&
@@ -147,7 +145,7 @@ class IfThenToElvisIntention(private val fromJ2K: Boolean) : SelfTargetingOffset
it.typeReference!!) it.typeReference!!)
} }
val checkedExpression = condition.checkedExpression()!! val checkedExpression = condition.checkedExpression()!!
val elvis = runInWriteActionOrHere(inWriteAction = !fromJ2K) { val elvis = runWriteAction {
val replacedLeft = if (left.evaluatesTo(checkedExpression)) { val replacedLeft = if (left.evaluatesTo(checkedExpression)) {
if (condition is KtIsExpression) newReceiver!! else left if (condition is KtIsExpression) newReceiver!! else left
} }
@@ -21,16 +21,15 @@ import org.jetbrains.kotlin.idea.core.replaced
import org.jetbrains.kotlin.idea.inspections.IntentionBasedInspection import org.jetbrains.kotlin.idea.inspections.IntentionBasedInspection
import org.jetbrains.kotlin.idea.intentions.SelfTargetingOffsetIndependentIntention import org.jetbrains.kotlin.idea.intentions.SelfTargetingOffsetIndependentIntention
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.* import org.jetbrains.kotlin.idea.intentions.branchedTransformations.*
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.*
class IfThenToSafeAccessInspection : IntentionBasedInspection<KtIfExpression>(IfThenToSafeAccessIntention::class) class IfThenToSafeAccessInspection : IntentionBasedInspection<KtIfExpression>(IfThenToSafeAccessIntention::class)
class IfThenToSafeAccessIntention(private val fromJ2K: Boolean) : SelfTargetingOffsetIndependentIntention<KtIfExpression>( class IfThenToSafeAccessIntention : SelfTargetingOffsetIndependentIntention<KtIfExpression>(
KtIfExpression::class.java, "Replace 'if' expression with safe access expression" KtIfExpression::class.java, "Replace 'if' expression with safe access expression"
) { ) {
@Suppress("unused")
constructor(): this(fromJ2K = false)
override fun isApplicableTo(element: KtIfExpression): Boolean { override fun isApplicableTo(element: KtIfExpression): Boolean {
val condition = element.condition as? KtBinaryExpression ?: return false val condition = element.condition as? KtBinaryExpression ?: return false
@@ -70,9 +69,7 @@ class IfThenToSafeAccessIntention(private val fromJ2K: Boolean) : SelfTargetingO
} }
val newExpr = KtPsiFactory(element).createExpressionByPattern("$0?.$1", receiverExpression, selectorExpression) as KtSafeQualifiedExpression val newExpr = KtPsiFactory(element).createExpressionByPattern("$0?.$1", receiverExpression, selectorExpression) as KtSafeQualifiedExpression
val safeAccessExpr = runInWriteActionOrHere(inWriteAction = !fromJ2K) { val safeAccessExpr = runWriteAction { element.replaced(newExpr) }
element.replaced(newExpr)
}
if (editor != null) { if (editor != null) {
safeAccessExpr.inlineReceiverIfApplicableWithPrompt(editor) safeAccessExpr.inlineReceiverIfApplicableWithPrompt(editor)
@@ -33,6 +33,7 @@ import org.jetbrains.kotlin.idea.intentions.conventionNameCalls.ReplaceGetOrSetI
import org.jetbrains.kotlin.idea.quickfix.RemoveModifierFix import org.jetbrains.kotlin.idea.quickfix.RemoveModifierFix
import org.jetbrains.kotlin.idea.quickfix.RemoveUselessCastFix import org.jetbrains.kotlin.idea.quickfix.RemoveUselessCastFix
import org.jetbrains.kotlin.idea.references.mainReference import org.jetbrains.kotlin.idea.references.mainReference
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.visibilityModifierType import org.jetbrains.kotlin.psi.psiUtil.visibilityModifierType
@@ -69,8 +70,8 @@ object J2KPostProcessingRegistrar {
_processings.add(RemoveRedundantCastToNullableProcessing()) _processings.add(RemoveRedundantCastToNullableProcessing())
registerIntentionBasedProcessing(ConvertToExpressionBodyIntention(convertEmptyToUnit = false)) { it is KtPropertyAccessor } registerIntentionBasedProcessing(ConvertToExpressionBodyIntention(convertEmptyToUnit = false)) { it is KtPropertyAccessor }
registerIntentionBasedProcessing(IfThenToSafeAccessIntention(fromJ2K = true)) registerIntentionBasedProcessing(IfThenToSafeAccessIntention())
registerIntentionBasedProcessing(IfThenToElvisIntention(fromJ2K = true)) registerIntentionBasedProcessing(IfThenToElvisIntention())
registerIntentionBasedProcessing(FoldInitializerAndIfToElvisIntention()) registerIntentionBasedProcessing(FoldInitializerAndIfToElvisIntention())
registerIntentionBasedProcessing(SimplifyNegatedBinaryExpressionIntention()) registerIntentionBasedProcessing(SimplifyNegatedBinaryExpressionIntention())
registerIntentionBasedProcessing(ReplaceGetOrSetIntention(), additionalChecker = ReplaceGetOrSetInspection.additionalChecker) registerIntentionBasedProcessing(ReplaceGetOrSetIntention(), additionalChecker = ReplaceGetOrSetInspection.additionalChecker)
@@ -136,7 +137,12 @@ object J2KPostProcessingRegistrar {
if (!additionalChecker(tElement)) return null if (!additionalChecker(tElement)) return null
return { return {
if (intention.applicabilityRange(tElement) != null) { // check availability of the intention again because something could change if (intention.applicabilityRange(tElement) != null) { // check availability of the intention again because something could change
intention.applyTo(element, null) val apply = { intention.applyTo(element, null) }
if (intention.startInWriteAction())
runWriteAction(apply)
else
apply()
} }
} }
} }
@@ -16,15 +16,21 @@
package org.jetbrains.kotlin.idea.j2k package org.jetbrains.kotlin.idea.j2k
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.editor.RangeMarker import com.intellij.openapi.editor.RangeMarker
import com.intellij.openapi.util.TextRange import com.intellij.openapi.util.TextRange
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import com.intellij.psi.PsiRecursiveElementVisitor import com.intellij.psi.PsiRecursiveElementVisitor
import com.intellij.psi.codeStyle.CodeStyleManager import com.intellij.psi.codeStyle.CodeStyleManager
import kotlinx.coroutines.experimental.run
import kotlinx.coroutines.experimental.runBlocking
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.caches.resolve.resolveImportReference import org.jetbrains.kotlin.idea.caches.resolve.resolveImportReference
import org.jetbrains.kotlin.idea.conversion.copy.range import org.jetbrains.kotlin.idea.conversion.copy.range
import org.jetbrains.kotlin.idea.core.util.EDT
import org.jetbrains.kotlin.idea.util.ImportInsertHelper import org.jetbrains.kotlin.idea.util.ImportInsertHelper
import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.idea.util.application.runWriteAction
import org.jetbrains.kotlin.j2k.PostProcessor import org.jetbrains.kotlin.j2k.PostProcessor
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.psi.KtElement
@@ -35,8 +41,12 @@ import java.util.*
class J2kPostProcessor(private val formatCode: Boolean) : PostProcessor { class J2kPostProcessor(private val formatCode: Boolean) : PostProcessor {
override fun insertImport(file: KtFile, fqName: FqName) { override fun insertImport(file: KtFile, fqName: FqName) {
val descriptors = file.resolveImportReference(fqName) ApplicationManager.getApplication().invokeAndWait {
descriptors.firstOrNull()?.let { ImportInsertHelper.getInstance(file.project).importDescriptor(file, it) } runWriteAction {
val descriptors = file.resolveImportReference(fqName)
descriptors.firstOrNull()?.let { ImportInsertHelper.getInstance(file.project).importDescriptor(file, it) }
}
}
} }
private enum class RangeFilterResult { private enum class RangeFilterResult {
@@ -45,39 +55,48 @@ class J2kPostProcessor(private val formatCode: Boolean) : PostProcessor {
PROCESS PROCESS
} }
override fun doAdditionalProcessing(file: KtFile, rangeMarker: RangeMarker?) { override fun doAdditionalProcessing(file: KtFile, rangeMarker: RangeMarker?) = runBlocking {
var elementToActions = collectAvailableActions(file, rangeMarker) do {
while (elementToActions.isNotEmpty()) {
var modificationStamp: Long? = file.modificationStamp var modificationStamp: Long? = file.modificationStamp
val elementToActions = runReadAction {
collectAvailableActions(file, rangeMarker)
}
for ((element, action, _) in elementToActions) { run(EDT) {
if (element.isValid) { for ((element, action, _) in elementToActions) {
action() if (element.isValid) {
} action()
else { }
modificationStamp = null else {
modificationStamp = null
}
} }
} }
if (modificationStamp == file.modificationStamp) break if (modificationStamp == file.modificationStamp) break
elementToActions = collectAvailableActions(file, rangeMarker)
} }
while (elementToActions.isNotEmpty())
if (formatCode) { if (formatCode) {
val codeStyleManager = CodeStyleManager.getInstance(file.project) run(EDT) {
if (rangeMarker != null) { runWriteAction {
if (rangeMarker.isValid) { val codeStyleManager = CodeStyleManager.getInstance(file.project)
codeStyleManager.reformatRange(file, rangeMarker.startOffset, rangeMarker.endOffset) if (rangeMarker != null) {
if (rangeMarker.isValid) {
codeStyleManager.reformatRange(file, rangeMarker.startOffset, rangeMarker.endOffset)
}
}
else {
codeStyleManager.reformat(file)
}
Unit
} }
} }
else {
codeStyleManager.reformat(file)
}
} }
} }
private data class ActionData(val element: KtElement, val action: () -> Unit, val priority: Int) private data class ActionData(val element: KtElement, val action: () -> Unit, val priority: Int)
private fun collectAvailableActions(file: KtFile, rangeMarker: RangeMarker?): List<ActionData> { private fun collectAvailableActions(file: KtFile, rangeMarker: RangeMarker?): List<ActionData> {