New J2K : move resolve resolve out of EDT in shorten references processing

relates to #KT-31812
This commit is contained in:
Ilya Kirillov
2020-02-13 16:51:40 +03:00
parent dc57d307f3
commit d825428718
8 changed files with 126 additions and 59 deletions
@@ -0,0 +1,26 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.util
import com.intellij.openapi.application.ApplicationManager
enum class ActionRunningMode {
RUN_IN_CURRENT_THREAD,
RUN_IN_EDT
}
inline fun <T> ActionRunningMode.runAction(crossinline action: () -> T): T = when (this) {
ActionRunningMode.RUN_IN_CURRENT_THREAD -> {
action()
}
ActionRunningMode.RUN_IN_EDT -> {
var result: T? = null
ApplicationManager.getApplication().invokeAndWait {
result = ApplicationManager.getApplication().runWriteAction<T> { action() }
}
result!!
}
}
@@ -25,6 +25,7 @@ abstract class ImportInsertHelper {
abstract fun importDescriptor(
file: KtFile,
descriptor: DeclarationDescriptor,
actionRunningMode: ActionRunningMode = ActionRunningMode.RUN_IN_CURRENT_THREAD,
forceAllUnderImport: Boolean = false
): ImportDescriptorResult
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.idea.core
import com.intellij.openapi.application.runReadAction
import com.intellij.openapi.util.TextRange
import com.intellij.psi.PsiDocumentManager
import com.intellij.psi.PsiElement
@@ -19,7 +20,6 @@ import org.jetbrains.kotlin.idea.imports.canBeReferencedViaImport
import org.jetbrains.kotlin.idea.imports.getImportableTargets
import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.idea.util.*
import org.jetbrains.kotlin.idea.util.application.runWriteAction
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.*
@@ -95,19 +95,31 @@ class ShortenReferences(val options: (KtElement) -> Options = { Options.DEFAULT
}
@JvmOverloads
fun process(element: KtElement, elementFilter: (PsiElement) -> FilterResult = { FilterResult.PROCESS }): KtElement {
return process(listOf(element), elementFilter).single()
fun process(
element: KtElement,
elementFilter: (PsiElement) -> FilterResult = { FilterResult.PROCESS },
actionRunningMode: ActionRunningMode = ActionRunningMode.RUN_IN_CURRENT_THREAD
): KtElement {
return process(listOf(element), elementFilter, actionRunningMode).single()
}
@JvmOverloads
fun process(file: KtFile, startOffset: Int, endOffset: Int, additionalFilter: (PsiElement) -> FilterResult = { FilterResult.PROCESS }) {
val documentManager = PsiDocumentManager.getInstance(file.project)
val document = file.viewProvider.document!!
check(documentManager.isCommitted(document)) { "Document should be committed to shorten references in range" }
val rangeMarker = document.createRangeMarker(startOffset, endOffset)
rangeMarker.isGreedyToLeft = true
rangeMarker.isGreedyToRight = true
fun process(
file: KtFile,
startOffset: Int,
endOffset: Int,
additionalFilter: (PsiElement) -> FilterResult = { FilterResult.PROCESS },
actionRunningMode: ActionRunningMode = ActionRunningMode.RUN_IN_CURRENT_THREAD
) {
val rangeMarker = runReadAction {
val documentManager = PsiDocumentManager.getInstance(file.project)
val document = file.viewProvider.document!!
check(documentManager.isCommitted(document)) { "Document should be committed to shorten references in range" }
document.createRangeMarker(startOffset, endOffset).apply {
isGreedyToLeft = true
isGreedyToRight = true
}
}
val rangeFilter = { element: PsiElement ->
if (rangeMarker.isValid) {
@@ -136,11 +148,12 @@ class ShortenReferences(val options: (KtElement) -> Options = { Options.DEFAULT
}
}
try {
process(listOf(file)) { element ->
val filter = { element: PsiElement ->
minOf(rangeFilter(element), additionalFilter(element))
}
process(listOf(file), filter, actionRunningMode)
} finally {
rangeMarker.dispose()
runReadAction { rangeMarker.dispose() }
}
}
@@ -153,16 +166,18 @@ class ShortenReferences(val options: (KtElement) -> Options = { Options.DEFAULT
@JvmOverloads
fun process(
elements: Iterable<KtElement>,
elementFilter: (PsiElement) -> FilterResult = { FilterResult.PROCESS }
elementFilter: (PsiElement) -> FilterResult = { FilterResult.PROCESS },
actionRunningMode: ActionRunningMode = ActionRunningMode.RUN_IN_CURRENT_THREAD
): Collection<KtElement> {
return elements.groupBy(KtElement::getContainingKtFile)
.flatMap { shortenReferencesInFile(it.key, it.value, elementFilter) }
return runReadAction { elements.groupBy(KtElement::getContainingKtFile) }
.flatMap { shortenReferencesInFile(it.key, it.value, elementFilter, actionRunningMode) }
}
private fun shortenReferencesInFile(
file: KtFile,
elements: List<KtElement>,
elementFilter: (PsiElement) -> FilterResult
elementFilter: (PsiElement) -> FilterResult,
actionRunningMode: ActionRunningMode = ActionRunningMode.RUN_IN_CURRENT_THREAD
): Collection<KtElement> {
//TODO: that's not correct since we have options!
val elementsToUse = dropNestedElements(elements)
@@ -182,39 +197,45 @@ class ShortenReferences(val options: (KtElement) -> Options = { Options.DEFAULT
while (true) {
// Processors order is important here so that enclosing elements are not shortened before their children are, e.g.
// test.foo(this@A) -> foo(this)
val processors: List<ShorteningProcessor<*>> = listOf(
ShortenTypesProcessor(file, elementFilter, failedToImportDescriptors),
ShortenThisExpressionsProcessor(file, elementFilter, failedToImportDescriptors),
ShortenQualifiedExpressionsProcessor(file, elementFilter, failedToImportDescriptors),
RemoveExplicitCompanionObjectReferenceProcessor(file, companionElementFilter, failedToImportDescriptors)
)
val processors: List<ShorteningProcessor<*>> = runReadAction {
listOf(
ShortenTypesProcessor(file, elementFilter, failedToImportDescriptors),
ShortenThisExpressionsProcessor(file, elementFilter, failedToImportDescriptors),
ShortenQualifiedExpressionsProcessor(file, elementFilter, failedToImportDescriptors),
RemoveExplicitCompanionObjectReferenceProcessor(file, companionElementFilter, failedToImportDescriptors)
)
}
// step 1: collect qualified elements to analyze (no resolve at this step)
val visitors = processors.map { it.collectElementsVisitor }
for (visitor in visitors) {
for (element in elementsToUse) {
visitor.options = options(element)
element.accept(visitor)
runReadAction {
for (visitor in visitors) {
for (element in elementsToUse) {
visitor.options = options(element)
element.accept(visitor)
}
}
}
// step 2: analyze collected elements with resolve and decide which can be shortened now and which need descriptors to be imported before shortening
val allElementsToAnalyze = visitors.flatMap { visitor -> visitor.getElementsToAnalyze().map { it.element } }.toSet()
val bindingContext = allowResolveInDispatchThread {
file.getResolutionFacade().analyze(allElementsToAnalyze, BodyResolveMode.PARTIAL_WITH_CFA)
}
processors.forEach { it.analyzeCollectedElements(bindingContext) }
// step 2: analyze collected elements with resolve and decide which can be shortened now and which need descriptors to be imported before shortening
val allElementsToAnalyze = visitors.flatMap { visitor -> visitor.getElementsToAnalyze().map { it.element } }.toSet()
val bindingContext = allowResolveInDispatchThread {
file.getResolutionFacade().analyze(allElementsToAnalyze, BodyResolveMode.PARTIAL_WITH_CFA)
}
processors.forEach { it.analyzeCollectedElements(bindingContext) }
}
// step 3: shorten elements that can be shortened right now
runWriteAction {
actionRunningMode.runAction {
processors.forEach { it.shortenElements(elementSetToUpdate = elementsToUse, options = options) }
}
// step 4: try to import descriptors needed to shorten other elements
val descriptorsToImport = processors.flatMap { it.getDescriptorsToImport() }.toSet()
var anyChange = false
runWriteAction {
// step 4: try to import descriptors needed to shorten other elements
val descriptorsToImport = runReadAction {
processors.flatMap { it.getDescriptorsToImport() }.toSet()
}
actionRunningMode.runAction {
for (descriptor in descriptorsToImport) {
assert(descriptor !in failedToImportDescriptors)
@@ -226,7 +247,10 @@ class ShortenReferences(val options: (KtElement) -> Options = { Options.DEFAULT
failedToImportDescriptors.add(descriptor)
}
}
if (!anyChange) processors.forEach { it.removeRootPrefixes() }
if (!anyChange) {
processors.forEach { it.removeRootPrefixes() }
}
}
if (!anyChange) break
@@ -235,9 +259,9 @@ class ShortenReferences(val options: (KtElement) -> Options = { Options.DEFAULT
return elementsToUse
}
private fun dropNestedElements(elements: List<KtElement>): LinkedHashSet<KtElement> {
private fun dropNestedElements(elements: List<KtElement>): LinkedHashSet<KtElement> = runReadAction {
val elementSet = elements.toSet()
return elementSet.filterTo(LinkedHashSet(elementSet.size)) { element ->
elementSet.filterTo(LinkedHashSet(elementSet.size)) { element ->
element.parents.none { it in elementSet }
}
}
@@ -441,7 +441,7 @@ class CodeInliner<TCallElement : KtElement>(
}
val newElements = elements.map {
ShortenReferences { ShortenReferences.Options(removeThis = true) }.process(it, shortenFilter)
ShortenReferences { ShortenReferences.Options(removeThis = true) }.process(it, elementFilter = shortenFilter)
}
newElements.forEach { element ->
@@ -238,12 +238,14 @@ class ConvertScopeFunctionToParameter(counterpartName: String) : ConvertScopeFun
}
override fun postprocessLambda(lambda: KtLambdaArgument) {
ShortenReferences { ShortenReferences.Options(removeThisLabels = true) }.process(lambda) { element ->
val filter = { element: PsiElement ->
if (element is KtThisExpression && element.getLabelName() != null)
ShortenReferences.FilterResult.PROCESS
else
ShortenReferences.FilterResult.GO_INSIDE
}
ShortenReferences{ ShortenReferences.Options(removeThisLabels = true) }.process(lambda, filter)
}
private fun needUniqueNameForParameter(
@@ -342,7 +344,7 @@ class ConvertScopeFunctionToReceiver(counterpartName: String) : ConvertScopeFunc
}
override fun postprocessLambda(lambda: KtLambdaArgument) {
ShortenReferences { ShortenReferences.Options(removeThis = true, removeThisLabels = true) }.process(lambda) { element ->
val filter = { element: PsiElement ->
if (element is KtThisExpression && element.getLabelName() != null)
ShortenReferences.FilterResult.PROCESS
else if (element is KtQualifiedExpression && element.receiverExpression is KtThisExpression)
@@ -350,6 +352,7 @@ class ConvertScopeFunctionToReceiver(counterpartName: String) : ConvertScopeFunc
else
ShortenReferences.FilterResult.GO_INSIDE
}
ShortenReferences { ShortenReferences.Options(removeThis = true, removeThisLabels = true) }.process(lambda, filter)
}
}
@@ -256,7 +256,8 @@ private class ObsoleteExtensionFunctionUsage(
.map { it.resolveToDescriptorIfAny() }
.find { it != null && it.importableFqName?.asString() == fqName } ?: return
ImportInsertHelper.getInstance(element.project).importDescriptor(element.containingKtFile, importFun, false)
ImportInsertHelper.getInstance(element.project)
.importDescriptor(element.containingKtFile, importFun, forceAllUnderImport = false)
}
}
}
@@ -82,8 +82,13 @@ class ImportInsertHelperImpl(private val project: Project) : ImportInsertHelper(
}
}
override fun importDescriptor(file: KtFile, descriptor: DeclarationDescriptor, forceAllUnderImport: Boolean): ImportDescriptorResult {
val importer = Importer(file)
override fun importDescriptor(
file: KtFile,
descriptor: DeclarationDescriptor,
actionRunningMode: ActionRunningMode,
forceAllUnderImport: Boolean
): ImportDescriptorResult {
val importer = Importer(file, actionRunningMode)
return if (forceAllUnderImport) {
importer.importDescriptorWithStarImport(descriptor)
} else {
@@ -92,7 +97,8 @@ class ImportInsertHelperImpl(private val project: Project) : ImportInsertHelper(
}
private inner class Importer(
private val file: KtFile
private val file: KtFile,
private val actionRunningMode: ActionRunningMode
) {
private val resolutionFacade = file.getResolutionFacade()
@@ -260,10 +266,9 @@ class ImportInsertHelperImpl(private val project: Project) : ImportInsertHelper(
val addedImport = addImport(parentFqName, true)
if (!isAlreadyImported(targetDescriptor, resolutionFacade.getFileResolutionScope(file), targetFqName)) {
addedImport.delete()
actionRunningMode.runAction { addedImport.delete() }
return ImportDescriptorResult.FAIL
}
dropRedundantExplicitImports(parentFqName)
val conflicts = futureCheckMap
@@ -326,7 +331,7 @@ class ImportInsertHelperImpl(private val project: Project) : ImportInsertHelper(
if (targets.any { it is PackageViewDescriptor }) continue // do not drop import of package
val classDescriptor = targets.filterIsInstance<ClassDescriptor>().firstOrNull()
importsToCheck.addIfNotNull(classDescriptor?.importableFqName)
import.delete()
actionRunningMode.runAction { import.delete() }
}
if (importsToCheck.isNotEmpty()) {
@@ -380,7 +385,9 @@ class ImportInsertHelperImpl(private val project: Project) : ImportInsertHelper(
private fun KtReferenceExpression.resolveTargets(): Collection<DeclarationDescriptor> =
this.getImportableTargets(resolutionFacade.analyze(this, BodyResolveMode.PARTIAL))
private fun addImport(fqName: FqName, allUnder: Boolean): KtImportDirective = addImport(project, file, fqName, allUnder)
private fun addImport(fqName: FqName, allUnder: Boolean): KtImportDirective = actionRunningMode.runAction {
addImport(project, file, fqName, allUnder)
}
}
companion object {
@@ -5,15 +5,14 @@
package org.jetbrains.kotlin.nj2k.postProcessing.processings
import com.intellij.openapi.command.CommandProcessor
import com.intellij.openapi.editor.RangeMarker
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.idea.core.ShortenReferences
import org.jetbrains.kotlin.idea.util.ActionRunningMode
import org.jetbrains.kotlin.nj2k.JKImportStorage
import org.jetbrains.kotlin.nj2k.NewJ2kConverterContext
import org.jetbrains.kotlin.nj2k.postProcessing.FileBasedPostProcessing
import org.jetbrains.kotlin.nj2k.postProcessing.GeneralPostProcessing
import org.jetbrains.kotlin.nj2k.postProcessing.PostProcessingOptions
import org.jetbrains.kotlin.nj2k.postProcessing.runUndoTransparentActionInEdt
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtQualifiedExpression
@@ -29,13 +28,19 @@ class ShortenReferenceProcessing : FileBasedPostProcessing() {
}
override fun runProcessing(file: KtFile, allFiles: List<KtFile>, rangeMarker: RangeMarker?, converterContext: NewJ2kConverterContext) {
runUndoTransparentActionInEdt(inWriteAction = false) {
CommandProcessor.getInstance().runUndoTransparentAction {
if (rangeMarker != null) {
if (rangeMarker.isValid) {
ShortenReferences.DEFAULT.process(file, rangeMarker.startOffset, rangeMarker.endOffset, filter)
ShortenReferences.DEFAULT.process(
file,
rangeMarker.startOffset,
rangeMarker.endOffset,
filter,
actionRunningMode = ActionRunningMode.RUN_IN_EDT
)
}
} else {
ShortenReferences.DEFAULT.process(file, filter)
ShortenReferences.DEFAULT.process(file, filter, actionRunningMode = ActionRunningMode.RUN_IN_EDT)
}
}
}