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
@@ -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 }
}
}