Avoid using quite new ScratchFileService API
Relates to #KT-35918
This commit is contained in:
@@ -81,11 +81,10 @@ fun getScriptRelatedModuleInfo(project: Project, virtualFile: VirtualFile): Modu
|
|||||||
return moduleRelatedModuleInfo
|
return moduleRelatedModuleInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
return if (ScratchFileService.findRootType(virtualFile) != ScratchRootType.getInstance()) null
|
return if (ScratchFileService.getInstance().getRootType(virtualFile) is ScratchRootType) {
|
||||||
else {
|
|
||||||
val scratchModule = virtualFile.scriptRelatedModuleName?.let { ModuleManager.getInstance(project).findModuleByName(it) }
|
val scratchModule = virtualFile.scriptRelatedModuleName?.let { ModuleManager.getInstance(project).findModuleByName(it) }
|
||||||
scratchModule?.testSourceInfo() ?: scratchModule?.productionSourceInfo()
|
scratchModule?.testSourceInfo() ?: scratchModule?.productionSourceInfo()
|
||||||
}
|
} else null
|
||||||
}
|
}
|
||||||
|
|
||||||
private typealias VirtualFileProcessor<T> = (Project, VirtualFile, Boolean) -> T
|
private typealias VirtualFileProcessor<T> = (Project, VirtualFile, Boolean) -> T
|
||||||
|
|||||||
@@ -1,337 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2010-2019 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.caches.project
|
|
||||||
|
|
||||||
import com.intellij.ide.scratch.ScratchFileService
|
|
||||||
import com.intellij.openapi.module.ModuleManager
|
|
||||||
import com.intellij.openapi.project.Project
|
|
||||||
import com.intellij.openapi.roots.*
|
|
||||||
import com.intellij.openapi.util.Key
|
|
||||||
import com.intellij.openapi.vfs.VirtualFile
|
|
||||||
import com.intellij.psi.PsiElement
|
|
||||||
import com.intellij.psi.PsiFile
|
|
||||||
import org.jetbrains.kotlin.analyzer.ModuleInfo
|
|
||||||
import org.jetbrains.kotlin.asJava.classes.FakeLightClassForFileOfPackage
|
|
||||||
import org.jetbrains.kotlin.asJava.classes.KtLightClassForFacade
|
|
||||||
import org.jetbrains.kotlin.asJava.elements.KtLightElement
|
|
||||||
import org.jetbrains.kotlin.caches.project.cacheInvalidatingOnRootModifications
|
|
||||||
import org.jetbrains.kotlin.idea.caches.lightClasses.KtLightClassForDecompiledDeclaration
|
|
||||||
import org.jetbrains.kotlin.idea.core.isInTestSourceContentKotlinAware
|
|
||||||
import org.jetbrains.kotlin.idea.core.script.ScriptConfigurationManager
|
|
||||||
import org.jetbrains.kotlin.idea.core.script.scriptRelatedModuleName
|
|
||||||
import org.jetbrains.kotlin.idea.highlighter.OutsidersPsiFileSupportUtils
|
|
||||||
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
|
|
||||||
import org.jetbrains.kotlin.idea.util.isInSourceContentWithoutInjected
|
|
||||||
import org.jetbrains.kotlin.idea.util.isKotlinBinary
|
|
||||||
import org.jetbrains.kotlin.psi.*
|
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
|
|
||||||
import org.jetbrains.kotlin.scripting.definitions.findScriptDefinition
|
|
||||||
import org.jetbrains.kotlin.scripting.definitions.runReadAction
|
|
||||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
|
||||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
|
||||||
import org.jetbrains.kotlin.utils.sure
|
|
||||||
import org.jetbrains.kotlin.utils.yieldIfNotNull
|
|
||||||
|
|
||||||
var PsiFile.forcedModuleInfo: ModuleInfo? by UserDataProperty(Key.create("FORCED_MODULE_INFO"))
|
|
||||||
|
|
||||||
fun PsiElement.getModuleInfo(): IdeaModuleInfo = this.collectInfos(ModuleInfoCollector.NotNullTakeFirst)
|
|
||||||
|
|
||||||
fun PsiElement.getNullableModuleInfo(): IdeaModuleInfo? = this.collectInfos(ModuleInfoCollector.NullableTakeFirst)
|
|
||||||
|
|
||||||
fun PsiElement.getModuleInfos(): Sequence<IdeaModuleInfo> = this.collectInfos(ModuleInfoCollector.ToSequence)
|
|
||||||
|
|
||||||
private fun ModuleInfo.doFindSdk(): SdkInfo? = dependencies().lazyClosure { it.dependencies() }.firstIsInstanceOrNull<SdkInfo>()
|
|
||||||
|
|
||||||
fun ModuleInfo.findSdkAcrossDependencies(): SdkInfo? {
|
|
||||||
return when (this) {
|
|
||||||
// It is important to check for cases of test/production explicitly, because we're using lambda's class
|
|
||||||
// of 'cacheInvalidatingOnRootModifications' as key for cache, and it should be different for Production/Source
|
|
||||||
is ModuleProductionSourceInfo -> module.cacheInvalidatingOnRootModifications { doFindSdk() }
|
|
||||||
is ModuleTestSourceInfo -> module.cacheInvalidatingOnRootModifications { doFindSdk() }
|
|
||||||
else -> doFindSdk()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getModuleInfoByVirtualFile(project: Project, virtualFile: VirtualFile): IdeaModuleInfo? =
|
|
||||||
collectInfosByVirtualFile(
|
|
||||||
project, virtualFile,
|
|
||||||
treatAsLibrarySource = false,
|
|
||||||
onOccurrence = { return@getModuleInfoByVirtualFile it }
|
|
||||||
)
|
|
||||||
|
|
||||||
fun getBinaryLibrariesModuleInfos(project: Project, virtualFile: VirtualFile) =
|
|
||||||
collectModuleInfosByType<BinaryModuleInfo>(
|
|
||||||
project,
|
|
||||||
virtualFile
|
|
||||||
)
|
|
||||||
|
|
||||||
fun getLibrarySourcesModuleInfos(project: Project, virtualFile: VirtualFile) =
|
|
||||||
collectModuleInfosByType<LibrarySourceInfo>(
|
|
||||||
project,
|
|
||||||
virtualFile
|
|
||||||
)
|
|
||||||
|
|
||||||
fun getScriptRelatedModuleInfo(project: Project, virtualFile: VirtualFile): ModuleSourceInfo? {
|
|
||||||
val moduleRelatedModuleInfo = getModuleRelatedModuleInfo(project, virtualFile)
|
|
||||||
if (moduleRelatedModuleInfo != null) {
|
|
||||||
return moduleRelatedModuleInfo
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ScratchFileService.isInScratchRoot(virtualFile)) {
|
|
||||||
val scratchModule = virtualFile.scriptRelatedModuleName?.let { ModuleManager.getInstance(project).findModuleByName(it) }
|
|
||||||
return scratchModule?.testSourceInfo() ?: scratchModule?.productionSourceInfo()
|
|
||||||
}
|
|
||||||
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
private typealias VirtualFileProcessor<T> = (Project, VirtualFile, Boolean) -> T
|
|
||||||
|
|
||||||
private sealed class ModuleInfoCollector<out T>(
|
|
||||||
val onResult: (IdeaModuleInfo?) -> T,
|
|
||||||
val onFailure: (String) -> T,
|
|
||||||
val virtualFileProcessor: VirtualFileProcessor<T>
|
|
||||||
) {
|
|
||||||
object NotNullTakeFirst : ModuleInfoCollector<IdeaModuleInfo>(
|
|
||||||
onResult = { it ?: NotUnderContentRootModuleInfo },
|
|
||||||
onFailure = { reason ->
|
|
||||||
LOG.error("Could not find correct module information.\nReason: $reason")
|
|
||||||
NotUnderContentRootModuleInfo
|
|
||||||
},
|
|
||||||
virtualFileProcessor = processor@{ project, virtualFile, isLibrarySource ->
|
|
||||||
collectInfosByVirtualFile(
|
|
||||||
project,
|
|
||||||
virtualFile,
|
|
||||||
isLibrarySource
|
|
||||||
) {
|
|
||||||
return@processor it ?: NotUnderContentRootModuleInfo
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
object NullableTakeFirst : ModuleInfoCollector<IdeaModuleInfo?>(
|
|
||||||
onResult = { it },
|
|
||||||
onFailure = { reason ->
|
|
||||||
LOG.warn("Could not find correct module information.\nReason: $reason")
|
|
||||||
null
|
|
||||||
},
|
|
||||||
virtualFileProcessor = processor@{ project, virtualFile, isLibrarySource ->
|
|
||||||
collectInfosByVirtualFile(
|
|
||||||
project,
|
|
||||||
virtualFile,
|
|
||||||
isLibrarySource
|
|
||||||
) { return@processor it }
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
object ToSequence : ModuleInfoCollector<Sequence<IdeaModuleInfo>>(
|
|
||||||
onResult = { result -> result?.let { sequenceOf(it) } ?: emptySequence() },
|
|
||||||
onFailure = { reason ->
|
|
||||||
LOG.warn("Could not find correct module information.\nReason: $reason")
|
|
||||||
emptySequence()
|
|
||||||
},
|
|
||||||
virtualFileProcessor = { project, virtualFile, isLibrarySource ->
|
|
||||||
sequence {
|
|
||||||
collectInfosByVirtualFile(
|
|
||||||
project,
|
|
||||||
virtualFile,
|
|
||||||
isLibrarySource
|
|
||||||
) { yieldIfNotNull(it) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun <T> PsiElement.collectInfos(c: ModuleInfoCollector<T>): T {
|
|
||||||
(containingFile?.forcedModuleInfo as? IdeaModuleInfo)?.let {
|
|
||||||
return c.onResult(it)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this is KtLightElement<*, *>) {
|
|
||||||
return this.processLightElement(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
collectModuleInfoByUserData(this).firstOrNull()?.let {
|
|
||||||
return c.onResult(it)
|
|
||||||
}
|
|
||||||
|
|
||||||
val containingFile =
|
|
||||||
containingFile ?: return c.onFailure("Analyzing element of type ${this::class.java} with no containing file\nText:\n$text")
|
|
||||||
|
|
||||||
val containingKtFile = (this as? KtElement)?.containingFile as? KtFile
|
|
||||||
containingKtFile?.analysisContext?.let {
|
|
||||||
return it.collectInfos(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
containingKtFile?.doNotAnalyze?.let {
|
|
||||||
return c.onFailure("Should not analyze element: $text in file ${containingKtFile.name}\n$it")
|
|
||||||
}
|
|
||||||
|
|
||||||
val explicitModuleInfo = containingKtFile?.forcedModuleInfo ?: (containingKtFile?.originalFile as? KtFile)?.forcedModuleInfo
|
|
||||||
if (explicitModuleInfo is IdeaModuleInfo) {
|
|
||||||
return c.onResult(explicitModuleInfo)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (containingKtFile is KtCodeFragment) {
|
|
||||||
val context = containingKtFile.getContext()
|
|
||||||
?: return c.onFailure("Analyzing code fragment of type ${containingKtFile::class.java} with no context element\nText:\n${containingKtFile.getText()}")
|
|
||||||
return context.collectInfos(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
val virtualFile = containingFile.originalFile.virtualFile
|
|
||||||
?: return c.onFailure("Analyzing element of type ${this::class.java} in non-physical file $containingFile of type ${containingFile::class.java}\nText:\n$text")
|
|
||||||
|
|
||||||
val isScript = runReadAction { containingKtFile?.isScript() == true }
|
|
||||||
if (isScript) {
|
|
||||||
getModuleRelatedModuleInfo(project, virtualFile)?.let {
|
|
||||||
return c.onResult(it)
|
|
||||||
}
|
|
||||||
val script = runReadAction { containingKtFile?.script }
|
|
||||||
script?.let {
|
|
||||||
containingKtFile?.findScriptDefinition()?.let {
|
|
||||||
return c.onResult(ScriptModuleInfo(project, virtualFile, it))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.virtualFileProcessor(
|
|
||||||
project,
|
|
||||||
virtualFile,
|
|
||||||
(containingFile as? KtFile)?.isCompiled ?: false
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun <T> KtLightElement<*, *>.processLightElement(c: ModuleInfoCollector<T>): T {
|
|
||||||
val decompiledClass = this.getParentOfType<KtLightClassForDecompiledDeclaration>(strict = false)
|
|
||||||
if (decompiledClass != null) {
|
|
||||||
return c.virtualFileProcessor(
|
|
||||||
project,
|
|
||||||
containingFile.virtualFile.sure { "Decompiled class should be build from physical file" },
|
|
||||||
false
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
val element = kotlinOrigin ?: when (this) {
|
|
||||||
is FakeLightClassForFileOfPackage -> this.getContainingFile()!!
|
|
||||||
is KtLightClassForFacade -> this.files.first()
|
|
||||||
else -> return c.onFailure("Light element without origin is referenced by resolve:\n$this\n${this.clsDelegate.text}")
|
|
||||||
}
|
|
||||||
|
|
||||||
return element.collectInfos(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
private inline fun <T> collectInfosByVirtualFile(
|
|
||||||
project: Project,
|
|
||||||
virtualFile: VirtualFile,
|
|
||||||
treatAsLibrarySource: Boolean,
|
|
||||||
onOccurrence: (IdeaModuleInfo?) -> T
|
|
||||||
): T {
|
|
||||||
collectModuleInfoByUserData(project, virtualFile).map(onOccurrence)
|
|
||||||
|
|
||||||
val moduleRelatedModuleInfo = getModuleRelatedModuleInfo(project, virtualFile)
|
|
||||||
if (moduleRelatedModuleInfo != null) {
|
|
||||||
onOccurrence(moduleRelatedModuleInfo)
|
|
||||||
}
|
|
||||||
|
|
||||||
val projectFileIndex = ProjectFileIndex.SERVICE.getInstance(project)
|
|
||||||
projectFileIndex.getOrderEntriesForFile(virtualFile).forEach {
|
|
||||||
it.toIdeaModuleInfo(project, virtualFile, treatAsLibrarySource).map(onOccurrence)
|
|
||||||
}
|
|
||||||
|
|
||||||
val isBinary = virtualFile.fileType.isKotlinBinary()
|
|
||||||
val scriptConfigurationManager = ScriptConfigurationManager.getInstance(project)
|
|
||||||
if (isBinary && virtualFile in scriptConfigurationManager.getAllScriptsDependenciesClassFilesScope()) {
|
|
||||||
if (treatAsLibrarySource) {
|
|
||||||
onOccurrence(ScriptDependenciesSourceInfo.ForProject(project))
|
|
||||||
} else {
|
|
||||||
onOccurrence(ScriptDependenciesInfo.ForProject(project))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!isBinary && virtualFile in scriptConfigurationManager.getAllScriptDependenciesSourcesScope()) {
|
|
||||||
onOccurrence(ScriptDependenciesSourceInfo.ForProject(project))
|
|
||||||
}
|
|
||||||
|
|
||||||
return onOccurrence(null)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getModuleRelatedModuleInfo(project: Project, virtualFile: VirtualFile): ModuleSourceInfo? {
|
|
||||||
val projectFileIndex = ProjectFileIndex.SERVICE.getInstance(project)
|
|
||||||
|
|
||||||
val module = projectFileIndex.getModuleForFile(virtualFile)
|
|
||||||
if (module != null && !module.isDisposed) {
|
|
||||||
val moduleFileIndex = ModuleRootManager.getInstance(module).fileIndex
|
|
||||||
if (moduleFileIndex.isInTestSourceContentKotlinAware(virtualFile)) {
|
|
||||||
return module.testSourceInfo()
|
|
||||||
} else if (moduleFileIndex.isInSourceContentWithoutInjected(virtualFile)) {
|
|
||||||
return module.productionSourceInfo()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val fileOrigin = OutsidersPsiFileSupportUtils.getOutsiderFileOrigin(project, virtualFile)
|
|
||||||
if (fileOrigin != null) {
|
|
||||||
return getModuleRelatedModuleInfo(project, fileOrigin)
|
|
||||||
}
|
|
||||||
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
private inline fun <reified T : IdeaModuleInfo> collectModuleInfosByType(project: Project, virtualFile: VirtualFile): Collection<T> {
|
|
||||||
val result = linkedSetOf<T>()
|
|
||||||
collectInfosByVirtualFile(project, virtualFile, treatAsLibrarySource = false) {
|
|
||||||
result.addIfNotNull(it as? T)
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun OrderEntry.toIdeaModuleInfo(
|
|
||||||
project: Project,
|
|
||||||
virtualFile: VirtualFile,
|
|
||||||
treatAsLibrarySource: Boolean = false
|
|
||||||
): List<IdeaModuleInfo> {
|
|
||||||
if (this is ModuleOrderEntry) return emptyList()
|
|
||||||
if (!isValid) return emptyList()
|
|
||||||
|
|
||||||
when (this) {
|
|
||||||
is LibraryOrderEntry -> {
|
|
||||||
val library = library ?: return emptyList()
|
|
||||||
if (ProjectRootsUtil.isLibraryClassFile(project, virtualFile) && !treatAsLibrarySource) {
|
|
||||||
return createLibraryInfo(project, library)
|
|
||||||
} else if (ProjectRootsUtil.isLibraryFile(project, virtualFile) || treatAsLibrarySource) {
|
|
||||||
return createLibraryInfo(project, library).map { it.sourcesModuleInfo }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is JdkOrderEntry -> {
|
|
||||||
return listOf(SdkInfo(project, jdk ?: return emptyList()))
|
|
||||||
}
|
|
||||||
else -> return emptyList()
|
|
||||||
}
|
|
||||||
return emptyList()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see [org.jetbrains.kotlin.types.typeUtil.closure].
|
|
||||||
*/
|
|
||||||
fun <T> Collection<T>.lazyClosure(f: (T) -> Collection<T>): Sequence<T> = sequence {
|
|
||||||
if (size == 0) return@sequence
|
|
||||||
var sizeBeforeIteration = 0
|
|
||||||
|
|
||||||
yieldAll(this@lazyClosure)
|
|
||||||
var yieldedCount = size
|
|
||||||
var elementsToCheck = this@lazyClosure
|
|
||||||
|
|
||||||
while (yieldedCount > sizeBeforeIteration) {
|
|
||||||
val toAdd = hashSetOf<T>()
|
|
||||||
elementsToCheck.forEach {
|
|
||||||
val neighbours = f(it)
|
|
||||||
yieldAll(neighbours)
|
|
||||||
yieldedCount += neighbours.size
|
|
||||||
toAdd.addAll(neighbours)
|
|
||||||
}
|
|
||||||
elementsToCheck = toAdd
|
|
||||||
sizeBeforeIteration = yieldedCount
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,337 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2010-2019 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.caches.project
|
|
||||||
|
|
||||||
import com.intellij.ide.scratch.ScratchFileService
|
|
||||||
import com.intellij.openapi.module.ModuleManager
|
|
||||||
import com.intellij.openapi.project.Project
|
|
||||||
import com.intellij.openapi.roots.*
|
|
||||||
import com.intellij.openapi.util.Key
|
|
||||||
import com.intellij.openapi.vfs.VirtualFile
|
|
||||||
import com.intellij.psi.PsiElement
|
|
||||||
import com.intellij.psi.PsiFile
|
|
||||||
import org.jetbrains.kotlin.analyzer.ModuleInfo
|
|
||||||
import org.jetbrains.kotlin.asJava.classes.FakeLightClassForFileOfPackage
|
|
||||||
import org.jetbrains.kotlin.asJava.classes.KtLightClassForFacade
|
|
||||||
import org.jetbrains.kotlin.asJava.elements.KtLightElement
|
|
||||||
import org.jetbrains.kotlin.caches.project.cacheInvalidatingOnRootModifications
|
|
||||||
import org.jetbrains.kotlin.idea.caches.lightClasses.KtLightClassForDecompiledDeclaration
|
|
||||||
import org.jetbrains.kotlin.idea.core.isInTestSourceContentKotlinAware
|
|
||||||
import org.jetbrains.kotlin.idea.core.script.ScriptConfigurationManager
|
|
||||||
import org.jetbrains.kotlin.idea.core.script.scriptRelatedModuleName
|
|
||||||
import org.jetbrains.kotlin.idea.highlighter.OutsidersPsiFileSupportUtils
|
|
||||||
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
|
|
||||||
import org.jetbrains.kotlin.idea.util.isInSourceContentWithoutInjected
|
|
||||||
import org.jetbrains.kotlin.idea.util.isKotlinBinary
|
|
||||||
import org.jetbrains.kotlin.psi.*
|
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
|
|
||||||
import org.jetbrains.kotlin.scripting.definitions.findScriptDefinition
|
|
||||||
import org.jetbrains.kotlin.scripting.definitions.runReadAction
|
|
||||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
|
||||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
|
||||||
import org.jetbrains.kotlin.utils.sure
|
|
||||||
import org.jetbrains.kotlin.utils.yieldIfNotNull
|
|
||||||
|
|
||||||
var PsiFile.forcedModuleInfo: ModuleInfo? by UserDataProperty(Key.create("FORCED_MODULE_INFO"))
|
|
||||||
|
|
||||||
fun PsiElement.getModuleInfo(): IdeaModuleInfo = this.collectInfos(ModuleInfoCollector.NotNullTakeFirst)
|
|
||||||
|
|
||||||
fun PsiElement.getNullableModuleInfo(): IdeaModuleInfo? = this.collectInfos(ModuleInfoCollector.NullableTakeFirst)
|
|
||||||
|
|
||||||
fun PsiElement.getModuleInfos(): Sequence<IdeaModuleInfo> = this.collectInfos(ModuleInfoCollector.ToSequence)
|
|
||||||
|
|
||||||
private fun ModuleInfo.doFindSdk(): SdkInfo? = dependencies().lazyClosure { it.dependencies() }.firstIsInstanceOrNull<SdkInfo>()
|
|
||||||
|
|
||||||
fun ModuleInfo.findSdkAcrossDependencies(): SdkInfo? {
|
|
||||||
return when (this) {
|
|
||||||
// It is important to check for cases of test/production explicitly, because we're using lambda's class
|
|
||||||
// of 'cacheInvalidatingOnRootModifications' as key for cache, and it should be different for Production/Source
|
|
||||||
is ModuleProductionSourceInfo -> module.cacheInvalidatingOnRootModifications { doFindSdk() }
|
|
||||||
is ModuleTestSourceInfo -> module.cacheInvalidatingOnRootModifications { doFindSdk() }
|
|
||||||
else -> doFindSdk()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getModuleInfoByVirtualFile(project: Project, virtualFile: VirtualFile): IdeaModuleInfo? =
|
|
||||||
collectInfosByVirtualFile(
|
|
||||||
project, virtualFile,
|
|
||||||
treatAsLibrarySource = false,
|
|
||||||
onOccurrence = { return@getModuleInfoByVirtualFile it }
|
|
||||||
)
|
|
||||||
|
|
||||||
fun getBinaryLibrariesModuleInfos(project: Project, virtualFile: VirtualFile) =
|
|
||||||
collectModuleInfosByType<BinaryModuleInfo>(
|
|
||||||
project,
|
|
||||||
virtualFile
|
|
||||||
)
|
|
||||||
|
|
||||||
fun getLibrarySourcesModuleInfos(project: Project, virtualFile: VirtualFile) =
|
|
||||||
collectModuleInfosByType<LibrarySourceInfo>(
|
|
||||||
project,
|
|
||||||
virtualFile
|
|
||||||
)
|
|
||||||
|
|
||||||
fun getScriptRelatedModuleInfo(project: Project, virtualFile: VirtualFile): ModuleSourceInfo? {
|
|
||||||
val moduleRelatedModuleInfo = getModuleRelatedModuleInfo(project, virtualFile)
|
|
||||||
if (moduleRelatedModuleInfo != null) {
|
|
||||||
return moduleRelatedModuleInfo
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ScratchFileService.isInScratchRoot(virtualFile)) {
|
|
||||||
val scratchModule = virtualFile.scriptRelatedModuleName?.let { ModuleManager.getInstance(project).findModuleByName(it) }
|
|
||||||
return scratchModule?.testSourceInfo() ?: scratchModule?.productionSourceInfo()
|
|
||||||
}
|
|
||||||
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
private typealias VirtualFileProcessor<T> = (Project, VirtualFile, Boolean) -> T
|
|
||||||
|
|
||||||
private sealed class ModuleInfoCollector<out T>(
|
|
||||||
val onResult: (IdeaModuleInfo?) -> T,
|
|
||||||
val onFailure: (String) -> T,
|
|
||||||
val virtualFileProcessor: VirtualFileProcessor<T>
|
|
||||||
) {
|
|
||||||
object NotNullTakeFirst : ModuleInfoCollector<IdeaModuleInfo>(
|
|
||||||
onResult = { it ?: NotUnderContentRootModuleInfo },
|
|
||||||
onFailure = { reason ->
|
|
||||||
LOG.error("Could not find correct module information.\nReason: $reason")
|
|
||||||
NotUnderContentRootModuleInfo
|
|
||||||
},
|
|
||||||
virtualFileProcessor = processor@{ project, virtualFile, isLibrarySource ->
|
|
||||||
collectInfosByVirtualFile(
|
|
||||||
project,
|
|
||||||
virtualFile,
|
|
||||||
isLibrarySource
|
|
||||||
) {
|
|
||||||
return@processor it ?: NotUnderContentRootModuleInfo
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
object NullableTakeFirst : ModuleInfoCollector<IdeaModuleInfo?>(
|
|
||||||
onResult = { it },
|
|
||||||
onFailure = { reason ->
|
|
||||||
LOG.warn("Could not find correct module information.\nReason: $reason")
|
|
||||||
null
|
|
||||||
},
|
|
||||||
virtualFileProcessor = processor@{ project, virtualFile, isLibrarySource ->
|
|
||||||
collectInfosByVirtualFile(
|
|
||||||
project,
|
|
||||||
virtualFile,
|
|
||||||
isLibrarySource
|
|
||||||
) { return@processor it }
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
object ToSequence : ModuleInfoCollector<Sequence<IdeaModuleInfo>>(
|
|
||||||
onResult = { result -> result?.let { sequenceOf(it) } ?: emptySequence() },
|
|
||||||
onFailure = { reason ->
|
|
||||||
LOG.warn("Could not find correct module information.\nReason: $reason")
|
|
||||||
emptySequence()
|
|
||||||
},
|
|
||||||
virtualFileProcessor = { project, virtualFile, isLibrarySource ->
|
|
||||||
sequence {
|
|
||||||
collectInfosByVirtualFile(
|
|
||||||
project,
|
|
||||||
virtualFile,
|
|
||||||
isLibrarySource
|
|
||||||
) { yieldIfNotNull(it) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun <T> PsiElement.collectInfos(c: ModuleInfoCollector<T>): T {
|
|
||||||
(containingFile?.forcedModuleInfo as? IdeaModuleInfo)?.let {
|
|
||||||
return c.onResult(it)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this is KtLightElement<*, *>) {
|
|
||||||
return this.processLightElement(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
collectModuleInfoByUserData(this).firstOrNull()?.let {
|
|
||||||
return c.onResult(it)
|
|
||||||
}
|
|
||||||
|
|
||||||
val containingFile =
|
|
||||||
containingFile ?: return c.onFailure("Analyzing element of type ${this::class.java} with no containing file\nText:\n$text")
|
|
||||||
|
|
||||||
val containingKtFile = (this as? KtElement)?.containingFile as? KtFile
|
|
||||||
containingKtFile?.analysisContext?.let {
|
|
||||||
return it.collectInfos(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
containingKtFile?.doNotAnalyze?.let {
|
|
||||||
return c.onFailure("Should not analyze element: $text in file ${containingKtFile.name}\n$it")
|
|
||||||
}
|
|
||||||
|
|
||||||
val explicitModuleInfo = containingKtFile?.forcedModuleInfo ?: (containingKtFile?.originalFile as? KtFile)?.forcedModuleInfo
|
|
||||||
if (explicitModuleInfo is IdeaModuleInfo) {
|
|
||||||
return c.onResult(explicitModuleInfo)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (containingKtFile is KtCodeFragment) {
|
|
||||||
val context = containingKtFile.getContext()
|
|
||||||
?: return c.onFailure("Analyzing code fragment of type ${containingKtFile::class.java} with no context element\nText:\n${containingKtFile.getText()}")
|
|
||||||
return context.collectInfos(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
val virtualFile = containingFile.originalFile.virtualFile
|
|
||||||
?: return c.onFailure("Analyzing element of type ${this::class.java} in non-physical file $containingFile of type ${containingFile::class.java}\nText:\n$text")
|
|
||||||
|
|
||||||
val isScript = runReadAction { containingKtFile?.isScript() == true }
|
|
||||||
if (isScript) {
|
|
||||||
getModuleRelatedModuleInfo(project, virtualFile)?.let {
|
|
||||||
return c.onResult(it)
|
|
||||||
}
|
|
||||||
val script = runReadAction { containingKtFile?.script }
|
|
||||||
script?.let {
|
|
||||||
containingKtFile?.findScriptDefinition()?.let {
|
|
||||||
return c.onResult(ScriptModuleInfo(project, virtualFile, it))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.virtualFileProcessor(
|
|
||||||
project,
|
|
||||||
virtualFile,
|
|
||||||
(containingFile as? KtFile)?.isCompiled ?: false
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun <T> KtLightElement<*, *>.processLightElement(c: ModuleInfoCollector<T>): T {
|
|
||||||
val decompiledClass = this.getParentOfType<KtLightClassForDecompiledDeclaration>(strict = false)
|
|
||||||
if (decompiledClass != null) {
|
|
||||||
return c.virtualFileProcessor(
|
|
||||||
project,
|
|
||||||
containingFile.virtualFile.sure { "Decompiled class should be build from physical file" },
|
|
||||||
false
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
val element = kotlinOrigin ?: when (this) {
|
|
||||||
is FakeLightClassForFileOfPackage -> this.getContainingFile()!!
|
|
||||||
is KtLightClassForFacade -> this.files.first()
|
|
||||||
else -> return c.onFailure("Light element without origin is referenced by resolve:\n$this\n${this.clsDelegate.text}")
|
|
||||||
}
|
|
||||||
|
|
||||||
return element.collectInfos(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
private inline fun <T> collectInfosByVirtualFile(
|
|
||||||
project: Project,
|
|
||||||
virtualFile: VirtualFile,
|
|
||||||
treatAsLibrarySource: Boolean,
|
|
||||||
onOccurrence: (IdeaModuleInfo?) -> T
|
|
||||||
): T {
|
|
||||||
collectModuleInfoByUserData(project, virtualFile).map(onOccurrence)
|
|
||||||
|
|
||||||
val moduleRelatedModuleInfo = getModuleRelatedModuleInfo(project, virtualFile)
|
|
||||||
if (moduleRelatedModuleInfo != null) {
|
|
||||||
onOccurrence(moduleRelatedModuleInfo)
|
|
||||||
}
|
|
||||||
|
|
||||||
val projectFileIndex = ProjectFileIndex.SERVICE.getInstance(project)
|
|
||||||
projectFileIndex.getOrderEntriesForFile(virtualFile).forEach {
|
|
||||||
it.toIdeaModuleInfo(project, virtualFile, treatAsLibrarySource).map(onOccurrence)
|
|
||||||
}
|
|
||||||
|
|
||||||
val isBinary = virtualFile.fileType.isKotlinBinary()
|
|
||||||
val scriptConfigurationManager = ScriptConfigurationManager.getInstance(project)
|
|
||||||
if (isBinary && virtualFile in scriptConfigurationManager.getAllScriptsDependenciesClassFilesScope()) {
|
|
||||||
if (treatAsLibrarySource) {
|
|
||||||
onOccurrence(ScriptDependenciesSourceInfo.ForProject(project))
|
|
||||||
} else {
|
|
||||||
onOccurrence(ScriptDependenciesInfo.ForProject(project))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!isBinary && virtualFile in scriptConfigurationManager.getAllScriptDependenciesSourcesScope()) {
|
|
||||||
onOccurrence(ScriptDependenciesSourceInfo.ForProject(project))
|
|
||||||
}
|
|
||||||
|
|
||||||
return onOccurrence(null)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getModuleRelatedModuleInfo(project: Project, virtualFile: VirtualFile): ModuleSourceInfo? {
|
|
||||||
val projectFileIndex = ProjectFileIndex.SERVICE.getInstance(project)
|
|
||||||
|
|
||||||
val module = projectFileIndex.getModuleForFile(virtualFile)
|
|
||||||
if (module != null && !module.isDisposed) {
|
|
||||||
val moduleFileIndex = ModuleRootManager.getInstance(module).fileIndex
|
|
||||||
if (moduleFileIndex.isInTestSourceContentKotlinAware(virtualFile)) {
|
|
||||||
return module.testSourceInfo()
|
|
||||||
} else if (moduleFileIndex.isInSourceContentWithoutInjected(virtualFile)) {
|
|
||||||
return module.productionSourceInfo()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val fileOrigin = OutsidersPsiFileSupportUtils.getOutsiderFileOrigin(project, virtualFile)
|
|
||||||
if (fileOrigin != null) {
|
|
||||||
return getModuleRelatedModuleInfo(project, fileOrigin)
|
|
||||||
}
|
|
||||||
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
private inline fun <reified T : IdeaModuleInfo> collectModuleInfosByType(project: Project, virtualFile: VirtualFile): Collection<T> {
|
|
||||||
val result = linkedSetOf<T>()
|
|
||||||
collectInfosByVirtualFile(project, virtualFile, treatAsLibrarySource = false) {
|
|
||||||
result.addIfNotNull(it as? T)
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun OrderEntry.toIdeaModuleInfo(
|
|
||||||
project: Project,
|
|
||||||
virtualFile: VirtualFile,
|
|
||||||
treatAsLibrarySource: Boolean = false
|
|
||||||
): List<IdeaModuleInfo> {
|
|
||||||
if (this is ModuleOrderEntry) return emptyList()
|
|
||||||
if (!isValid) return emptyList()
|
|
||||||
|
|
||||||
when (this) {
|
|
||||||
is LibraryOrderEntry -> {
|
|
||||||
val library = library ?: return emptyList()
|
|
||||||
if (ProjectRootsUtil.isLibraryClassFile(project, virtualFile) && !treatAsLibrarySource) {
|
|
||||||
return createLibraryInfo(project, library)
|
|
||||||
} else if (ProjectRootsUtil.isLibraryFile(project, virtualFile) || treatAsLibrarySource) {
|
|
||||||
return createLibraryInfo(project, library).map { it.sourcesModuleInfo }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is JdkOrderEntry -> {
|
|
||||||
return listOf(SdkInfo(project, jdk ?: return emptyList()))
|
|
||||||
}
|
|
||||||
else -> return emptyList()
|
|
||||||
}
|
|
||||||
return emptyList()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see [org.jetbrains.kotlin.types.typeUtil.closure].
|
|
||||||
*/
|
|
||||||
fun <T> Collection<T>.lazyClosure(f: (T) -> Collection<T>): Sequence<T> = sequence {
|
|
||||||
if (size == 0) return@sequence
|
|
||||||
var sizeBeforeIteration = 0
|
|
||||||
|
|
||||||
yieldAll(this@lazyClosure)
|
|
||||||
var yieldedCount = size
|
|
||||||
var elementsToCheck = this@lazyClosure
|
|
||||||
|
|
||||||
while (yieldedCount > sizeBeforeIteration) {
|
|
||||||
val toAdd = hashSetOf<T>()
|
|
||||||
elementsToCheck.forEach {
|
|
||||||
val neighbours = f(it)
|
|
||||||
yieldAll(neighbours)
|
|
||||||
yieldedCount += neighbours.size
|
|
||||||
toAdd.addAll(neighbours)
|
|
||||||
}
|
|
||||||
elementsToCheck = toAdd
|
|
||||||
sizeBeforeIteration = yieldedCount
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-337
@@ -1,337 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2010-2019 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.caches.project
|
|
||||||
|
|
||||||
import com.intellij.ide.scratch.ScratchFileService
|
|
||||||
import com.intellij.openapi.module.ModuleManager
|
|
||||||
import com.intellij.openapi.project.Project
|
|
||||||
import com.intellij.openapi.roots.*
|
|
||||||
import com.intellij.openapi.util.Key
|
|
||||||
import com.intellij.openapi.vfs.VirtualFile
|
|
||||||
import com.intellij.psi.PsiElement
|
|
||||||
import com.intellij.psi.PsiFile
|
|
||||||
import org.jetbrains.kotlin.analyzer.ModuleInfo
|
|
||||||
import org.jetbrains.kotlin.asJava.classes.FakeLightClassForFileOfPackage
|
|
||||||
import org.jetbrains.kotlin.asJava.classes.KtLightClassForFacade
|
|
||||||
import org.jetbrains.kotlin.asJava.elements.KtLightElement
|
|
||||||
import org.jetbrains.kotlin.caches.project.cacheInvalidatingOnRootModifications
|
|
||||||
import org.jetbrains.kotlin.idea.caches.lightClasses.KtLightClassForDecompiledDeclaration
|
|
||||||
import org.jetbrains.kotlin.idea.core.isInTestSourceContentKotlinAware
|
|
||||||
import org.jetbrains.kotlin.idea.core.script.ScriptConfigurationManager
|
|
||||||
import org.jetbrains.kotlin.idea.core.script.scriptRelatedModuleName
|
|
||||||
import org.jetbrains.kotlin.idea.highlighter.OutsidersPsiFileSupportUtils
|
|
||||||
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
|
|
||||||
import org.jetbrains.kotlin.idea.util.isInSourceContentWithoutInjected
|
|
||||||
import org.jetbrains.kotlin.idea.util.isKotlinBinary
|
|
||||||
import org.jetbrains.kotlin.psi.*
|
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
|
|
||||||
import org.jetbrains.kotlin.scripting.definitions.findScriptDefinition
|
|
||||||
import org.jetbrains.kotlin.scripting.definitions.runReadAction
|
|
||||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
|
||||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
|
||||||
import org.jetbrains.kotlin.utils.sure
|
|
||||||
import org.jetbrains.kotlin.utils.yieldIfNotNull
|
|
||||||
|
|
||||||
var PsiFile.forcedModuleInfo: ModuleInfo? by UserDataProperty(Key.create("FORCED_MODULE_INFO"))
|
|
||||||
|
|
||||||
fun PsiElement.getModuleInfo(): IdeaModuleInfo = this.collectInfos(ModuleInfoCollector.NotNullTakeFirst)
|
|
||||||
|
|
||||||
fun PsiElement.getNullableModuleInfo(): IdeaModuleInfo? = this.collectInfos(ModuleInfoCollector.NullableTakeFirst)
|
|
||||||
|
|
||||||
fun PsiElement.getModuleInfos(): Sequence<IdeaModuleInfo> = this.collectInfos(ModuleInfoCollector.ToSequence)
|
|
||||||
|
|
||||||
private fun ModuleInfo.doFindSdk(): SdkInfo? = dependencies().lazyClosure { it.dependencies() }.firstIsInstanceOrNull<SdkInfo>()
|
|
||||||
|
|
||||||
fun ModuleInfo.findSdkAcrossDependencies(): SdkInfo? {
|
|
||||||
return when (this) {
|
|
||||||
// It is important to check for cases of test/production explicitly, because we're using lambda's class
|
|
||||||
// of 'cacheInvalidatingOnRootModifications' as key for cache, and it should be different for Production/Source
|
|
||||||
is ModuleProductionSourceInfo -> module.cacheInvalidatingOnRootModifications { doFindSdk() }
|
|
||||||
is ModuleTestSourceInfo -> module.cacheInvalidatingOnRootModifications { doFindSdk() }
|
|
||||||
else -> doFindSdk()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getModuleInfoByVirtualFile(project: Project, virtualFile: VirtualFile): IdeaModuleInfo? =
|
|
||||||
collectInfosByVirtualFile(
|
|
||||||
project, virtualFile,
|
|
||||||
treatAsLibrarySource = false,
|
|
||||||
onOccurrence = { return@getModuleInfoByVirtualFile it }
|
|
||||||
)
|
|
||||||
|
|
||||||
fun getBinaryLibrariesModuleInfos(project: Project, virtualFile: VirtualFile) =
|
|
||||||
collectModuleInfosByType<BinaryModuleInfo>(
|
|
||||||
project,
|
|
||||||
virtualFile
|
|
||||||
)
|
|
||||||
|
|
||||||
fun getLibrarySourcesModuleInfos(project: Project, virtualFile: VirtualFile) =
|
|
||||||
collectModuleInfosByType<LibrarySourceInfo>(
|
|
||||||
project,
|
|
||||||
virtualFile
|
|
||||||
)
|
|
||||||
|
|
||||||
fun getScriptRelatedModuleInfo(project: Project, virtualFile: VirtualFile): ModuleSourceInfo? {
|
|
||||||
val moduleRelatedModuleInfo = getModuleRelatedModuleInfo(project, virtualFile)
|
|
||||||
if (moduleRelatedModuleInfo != null) {
|
|
||||||
return moduleRelatedModuleInfo
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ScratchFileService.isInScratchRoot(virtualFile)) {
|
|
||||||
val scratchModule = virtualFile.scriptRelatedModuleName?.let { ModuleManager.getInstance(project).findModuleByName(it) }
|
|
||||||
return scratchModule?.testSourceInfo() ?: scratchModule?.productionSourceInfo()
|
|
||||||
}
|
|
||||||
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
private typealias VirtualFileProcessor<T> = (Project, VirtualFile, Boolean) -> T
|
|
||||||
|
|
||||||
private sealed class ModuleInfoCollector<out T>(
|
|
||||||
val onResult: (IdeaModuleInfo?) -> T,
|
|
||||||
val onFailure: (String) -> T,
|
|
||||||
val virtualFileProcessor: VirtualFileProcessor<T>
|
|
||||||
) {
|
|
||||||
object NotNullTakeFirst : ModuleInfoCollector<IdeaModuleInfo>(
|
|
||||||
onResult = { it ?: NotUnderContentRootModuleInfo },
|
|
||||||
onFailure = { reason ->
|
|
||||||
LOG.error("Could not find correct module information.\nReason: $reason")
|
|
||||||
NotUnderContentRootModuleInfo
|
|
||||||
},
|
|
||||||
virtualFileProcessor = processor@{ project, virtualFile, isLibrarySource ->
|
|
||||||
collectInfosByVirtualFile(
|
|
||||||
project,
|
|
||||||
virtualFile,
|
|
||||||
isLibrarySource
|
|
||||||
) {
|
|
||||||
return@processor it ?: NotUnderContentRootModuleInfo
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
object NullableTakeFirst : ModuleInfoCollector<IdeaModuleInfo?>(
|
|
||||||
onResult = { it },
|
|
||||||
onFailure = { reason ->
|
|
||||||
LOG.warn("Could not find correct module information.\nReason: $reason")
|
|
||||||
null
|
|
||||||
},
|
|
||||||
virtualFileProcessor = processor@{ project, virtualFile, isLibrarySource ->
|
|
||||||
collectInfosByVirtualFile(
|
|
||||||
project,
|
|
||||||
virtualFile,
|
|
||||||
isLibrarySource
|
|
||||||
) { return@processor it }
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
object ToSequence : ModuleInfoCollector<Sequence<IdeaModuleInfo>>(
|
|
||||||
onResult = { result -> result?.let { sequenceOf(it) } ?: emptySequence() },
|
|
||||||
onFailure = { reason ->
|
|
||||||
LOG.warn("Could not find correct module information.\nReason: $reason")
|
|
||||||
emptySequence()
|
|
||||||
},
|
|
||||||
virtualFileProcessor = { project, virtualFile, isLibrarySource ->
|
|
||||||
sequence {
|
|
||||||
collectInfosByVirtualFile(
|
|
||||||
project,
|
|
||||||
virtualFile,
|
|
||||||
isLibrarySource
|
|
||||||
) { yieldIfNotNull(it) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun <T> PsiElement.collectInfos(c: ModuleInfoCollector<T>): T {
|
|
||||||
(containingFile?.forcedModuleInfo as? IdeaModuleInfo)?.let {
|
|
||||||
return c.onResult(it)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this is KtLightElement<*, *>) {
|
|
||||||
return this.processLightElement(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
collectModuleInfoByUserData(this).firstOrNull()?.let {
|
|
||||||
return c.onResult(it)
|
|
||||||
}
|
|
||||||
|
|
||||||
val containingFile =
|
|
||||||
containingFile ?: return c.onFailure("Analyzing element of type ${this::class.java} with no containing file\nText:\n$text")
|
|
||||||
|
|
||||||
val containingKtFile = (this as? KtElement)?.containingFile as? KtFile
|
|
||||||
containingKtFile?.analysisContext?.let {
|
|
||||||
return it.collectInfos(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
containingKtFile?.doNotAnalyze?.let {
|
|
||||||
return c.onFailure("Should not analyze element: $text in file ${containingKtFile.name}\n$it")
|
|
||||||
}
|
|
||||||
|
|
||||||
val explicitModuleInfo = containingKtFile?.forcedModuleInfo ?: (containingKtFile?.originalFile as? KtFile)?.forcedModuleInfo
|
|
||||||
if (explicitModuleInfo is IdeaModuleInfo) {
|
|
||||||
return c.onResult(explicitModuleInfo)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (containingKtFile is KtCodeFragment) {
|
|
||||||
val context = containingKtFile.getContext()
|
|
||||||
?: return c.onFailure("Analyzing code fragment of type ${containingKtFile::class.java} with no context element\nText:\n${containingKtFile.getText()}")
|
|
||||||
return context.collectInfos(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
val virtualFile = containingFile.originalFile.virtualFile
|
|
||||||
?: return c.onFailure("Analyzing element of type ${this::class.java} in non-physical file $containingFile of type ${containingFile::class.java}\nText:\n$text")
|
|
||||||
|
|
||||||
val isScript = runReadAction { containingKtFile?.isScript() == true }
|
|
||||||
if (isScript) {
|
|
||||||
getModuleRelatedModuleInfo(project, virtualFile)?.let {
|
|
||||||
return c.onResult(it)
|
|
||||||
}
|
|
||||||
val script = runReadAction { containingKtFile?.script }
|
|
||||||
script?.let {
|
|
||||||
containingKtFile?.findScriptDefinition()?.let {
|
|
||||||
return c.onResult(ScriptModuleInfo(project, virtualFile, it))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.virtualFileProcessor(
|
|
||||||
project,
|
|
||||||
virtualFile,
|
|
||||||
(containingFile as? KtFile)?.isCompiled ?: false
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun <T> KtLightElement<*, *>.processLightElement(c: ModuleInfoCollector<T>): T {
|
|
||||||
val decompiledClass = this.getParentOfType<KtLightClassForDecompiledDeclaration>(strict = false)
|
|
||||||
if (decompiledClass != null) {
|
|
||||||
return c.virtualFileProcessor(
|
|
||||||
project,
|
|
||||||
containingFile.virtualFile.sure { "Decompiled class should be build from physical file" },
|
|
||||||
false
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
val element = kotlinOrigin ?: when (this) {
|
|
||||||
is FakeLightClassForFileOfPackage -> this.getContainingFile()!!
|
|
||||||
is KtLightClassForFacade -> this.files.first()
|
|
||||||
else -> return c.onFailure("Light element without origin is referenced by resolve:\n$this\n${this.clsDelegate.text}")
|
|
||||||
}
|
|
||||||
|
|
||||||
return element.collectInfos(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
private inline fun <T> collectInfosByVirtualFile(
|
|
||||||
project: Project,
|
|
||||||
virtualFile: VirtualFile,
|
|
||||||
treatAsLibrarySource: Boolean,
|
|
||||||
onOccurrence: (IdeaModuleInfo?) -> T
|
|
||||||
): T {
|
|
||||||
collectModuleInfoByUserData(project, virtualFile).map(onOccurrence)
|
|
||||||
|
|
||||||
val moduleRelatedModuleInfo = getModuleRelatedModuleInfo(project, virtualFile)
|
|
||||||
if (moduleRelatedModuleInfo != null) {
|
|
||||||
onOccurrence(moduleRelatedModuleInfo)
|
|
||||||
}
|
|
||||||
|
|
||||||
val projectFileIndex = ProjectFileIndex.SERVICE.getInstance(project)
|
|
||||||
projectFileIndex.getOrderEntriesForFile(virtualFile).forEach {
|
|
||||||
it.toIdeaModuleInfo(project, virtualFile, treatAsLibrarySource).map(onOccurrence)
|
|
||||||
}
|
|
||||||
|
|
||||||
val isBinary = virtualFile.fileType.isKotlinBinary()
|
|
||||||
val scriptConfigurationManager = ScriptConfigurationManager.getInstance(project)
|
|
||||||
if (isBinary && virtualFile in scriptConfigurationManager.getAllScriptsDependenciesClassFilesScope()) {
|
|
||||||
if (treatAsLibrarySource) {
|
|
||||||
onOccurrence(ScriptDependenciesSourceInfo.ForProject(project))
|
|
||||||
} else {
|
|
||||||
onOccurrence(ScriptDependenciesInfo.ForProject(project))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!isBinary && virtualFile in scriptConfigurationManager.getAllScriptDependenciesSourcesScope()) {
|
|
||||||
onOccurrence(ScriptDependenciesSourceInfo.ForProject(project))
|
|
||||||
}
|
|
||||||
|
|
||||||
return onOccurrence(null)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getModuleRelatedModuleInfo(project: Project, virtualFile: VirtualFile): ModuleSourceInfo? {
|
|
||||||
val projectFileIndex = ProjectFileIndex.SERVICE.getInstance(project)
|
|
||||||
|
|
||||||
val module = projectFileIndex.getModuleForFile(virtualFile)
|
|
||||||
if (module != null && !module.isDisposed) {
|
|
||||||
val moduleFileIndex = ModuleRootManager.getInstance(module).fileIndex
|
|
||||||
if (moduleFileIndex.isInTestSourceContentKotlinAware(virtualFile)) {
|
|
||||||
return module.testSourceInfo()
|
|
||||||
} else if (moduleFileIndex.isInSourceContentWithoutInjected(virtualFile)) {
|
|
||||||
return module.productionSourceInfo()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val fileOrigin = OutsidersPsiFileSupportUtils.getOutsiderFileOrigin(project, virtualFile)
|
|
||||||
if (fileOrigin != null) {
|
|
||||||
return getModuleRelatedModuleInfo(project, fileOrigin)
|
|
||||||
}
|
|
||||||
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
private inline fun <reified T : IdeaModuleInfo> collectModuleInfosByType(project: Project, virtualFile: VirtualFile): Collection<T> {
|
|
||||||
val result = linkedSetOf<T>()
|
|
||||||
collectInfosByVirtualFile(project, virtualFile, treatAsLibrarySource = false) {
|
|
||||||
result.addIfNotNull(it as? T)
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun OrderEntry.toIdeaModuleInfo(
|
|
||||||
project: Project,
|
|
||||||
virtualFile: VirtualFile,
|
|
||||||
treatAsLibrarySource: Boolean = false
|
|
||||||
): List<IdeaModuleInfo> {
|
|
||||||
if (this is ModuleOrderEntry) return emptyList()
|
|
||||||
if (!isValid) return emptyList()
|
|
||||||
|
|
||||||
when (this) {
|
|
||||||
is LibraryOrderEntry -> {
|
|
||||||
val library = library ?: return emptyList()
|
|
||||||
if (ProjectRootsUtil.isLibraryClassFile(project, virtualFile) && !treatAsLibrarySource) {
|
|
||||||
return createLibraryInfo(project, library)
|
|
||||||
} else if (ProjectRootsUtil.isLibraryFile(project, virtualFile) || treatAsLibrarySource) {
|
|
||||||
return createLibraryInfo(project, library).map { it.sourcesModuleInfo }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is JdkOrderEntry -> {
|
|
||||||
return listOf(SdkInfo(project, jdk ?: return emptyList()))
|
|
||||||
}
|
|
||||||
else -> return emptyList()
|
|
||||||
}
|
|
||||||
return emptyList()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see [org.jetbrains.kotlin.types.typeUtil.closure].
|
|
||||||
*/
|
|
||||||
fun <T> Collection<T>.lazyClosure(f: (T) -> Collection<T>): Sequence<T> = sequence {
|
|
||||||
if (size == 0) return@sequence
|
|
||||||
var sizeBeforeIteration = 0
|
|
||||||
|
|
||||||
yieldAll(this@lazyClosure)
|
|
||||||
var yieldedCount = size
|
|
||||||
var elementsToCheck = this@lazyClosure
|
|
||||||
|
|
||||||
while (yieldedCount > sizeBeforeIteration) {
|
|
||||||
val toAdd = hashSetOf<T>()
|
|
||||||
elementsToCheck.forEach {
|
|
||||||
val neighbours = f(it)
|
|
||||||
yieldAll(neighbours)
|
|
||||||
yieldedCount += neighbours.size
|
|
||||||
toAdd.addAll(neighbours)
|
|
||||||
}
|
|
||||||
elementsToCheck = toAdd
|
|
||||||
sizeBeforeIteration = yieldedCount
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+2
-6
@@ -5,8 +5,6 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.idea.scratch
|
package org.jetbrains.kotlin.idea.scratch
|
||||||
|
|
||||||
import com.intellij.ide.scratch.ScratchFileService
|
|
||||||
import com.intellij.ide.scratch.ScratchRootType
|
|
||||||
import com.intellij.openapi.module.Module
|
import com.intellij.openapi.module.Module
|
||||||
import com.intellij.openapi.module.ModuleManager
|
import com.intellij.openapi.module.ModuleManager
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
@@ -20,7 +18,7 @@ import org.jetbrains.kotlin.utils.addIfNotNull
|
|||||||
class ScratchAdditionalIdeaDependenciesProvider : ScriptAdditionalIdeaDependenciesProvider() {
|
class ScratchAdditionalIdeaDependenciesProvider : ScriptAdditionalIdeaDependenciesProvider() {
|
||||||
|
|
||||||
override fun getRelatedModules(file: VirtualFile, project: Project): List<Module> {
|
override fun getRelatedModules(file: VirtualFile, project: Project): List<Module> {
|
||||||
if (!isInScratchRoot(file)) return emptyList()
|
if (!file.isKotlinScratch) return emptyList()
|
||||||
|
|
||||||
val scratchModule = file.scriptRelatedModuleName?.let {
|
val scratchModule = file.scriptRelatedModuleName?.let {
|
||||||
ModuleManager.getInstance(project).findModuleByName(it)
|
ModuleManager.getInstance(project).findModuleByName(it)
|
||||||
@@ -38,7 +36,7 @@ class ScratchAdditionalIdeaDependenciesProvider : ScriptAdditionalIdeaDependenci
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun getRelatedLibraries(file: VirtualFile, project: Project): List<Library> {
|
override fun getRelatedLibraries(file: VirtualFile, project: Project): List<Library> {
|
||||||
if (!isInScratchRoot(file)) return emptyList()
|
if (!file.isKotlinScratch) return emptyList()
|
||||||
|
|
||||||
val result = linkedSetOf<Library>()
|
val result = linkedSetOf<Library>()
|
||||||
getRelatedModules(file, project).forEach {
|
getRelatedModules(file, project).forEach {
|
||||||
@@ -52,8 +50,6 @@ class ScratchAdditionalIdeaDependenciesProvider : ScriptAdditionalIdeaDependenci
|
|||||||
return result.toList()
|
return result.toList()
|
||||||
}
|
}
|
||||||
|
|
||||||
private inline fun isInScratchRoot(file: VirtualFile) = ScratchFileService.findRootType(file) == ScratchRootType.getInstance()
|
|
||||||
|
|
||||||
private fun moduleDependencyEnumerator(it: Module): OrderEnumerator {
|
private fun moduleDependencyEnumerator(it: Module): OrderEnumerator {
|
||||||
return ModuleRootManager.getInstance(it).orderEntries()
|
return ModuleRootManager.getInstance(it).orderEntries()
|
||||||
.compileOnly().withoutSdk().recursively().exportedOnly()
|
.compileOnly().withoutSdk().recursively().exportedOnly()
|
||||||
|
|||||||
-58
@@ -1,58 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2010-2018 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.scratch
|
|
||||||
|
|
||||||
import com.intellij.ide.scratch.ScratchFileService
|
|
||||||
import com.intellij.openapi.module.Module
|
|
||||||
import com.intellij.openapi.module.ModuleManager
|
|
||||||
import com.intellij.openapi.project.Project
|
|
||||||
import com.intellij.openapi.roots.*
|
|
||||||
import com.intellij.openapi.roots.libraries.Library
|
|
||||||
import com.intellij.openapi.vfs.VirtualFile
|
|
||||||
import org.jetbrains.kotlin.idea.core.script.dependencies.ScriptAdditionalIdeaDependenciesProvider
|
|
||||||
import org.jetbrains.kotlin.idea.core.script.scriptRelatedModuleName
|
|
||||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
|
||||||
|
|
||||||
class ScratchAdditionalIdeaDependenciesProvider : ScriptAdditionalIdeaDependenciesProvider() {
|
|
||||||
|
|
||||||
override fun getRelatedModules(file: VirtualFile, project: Project): List<Module> {
|
|
||||||
if (!ScratchFileService.isInScratchRoot(file)) return emptyList()
|
|
||||||
|
|
||||||
val scratchModule = file.scriptRelatedModuleName?.let {
|
|
||||||
ModuleManager.getInstance(project).findModuleByName(it)
|
|
||||||
} ?: return emptyList()
|
|
||||||
|
|
||||||
val modules = linkedSetOf(scratchModule)
|
|
||||||
moduleDependencyEnumerator(scratchModule).withoutLibraries().forEach { orderEntry ->
|
|
||||||
when (orderEntry) {
|
|
||||||
is ModuleSourceOrderEntry -> modules.add(orderEntry.getOwnerModule())
|
|
||||||
is ModuleOrderEntry -> modules.addIfNotNull(orderEntry.module)
|
|
||||||
}
|
|
||||||
true
|
|
||||||
}
|
|
||||||
return modules.toList()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getRelatedLibraries(file: VirtualFile, project: Project): List<Library> {
|
|
||||||
if (!ScratchFileService.isInScratchRoot(file)) return emptyList()
|
|
||||||
|
|
||||||
val result = linkedSetOf<Library>()
|
|
||||||
getRelatedModules(file, project).forEach {
|
|
||||||
moduleDependencyEnumerator(it).withoutDepModules().forEach { orderEntry ->
|
|
||||||
if (orderEntry is LibraryOrderEntry) {
|
|
||||||
result.addIfNotNull(orderEntry.library)
|
|
||||||
}
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result.toList()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun moduleDependencyEnumerator(it: Module): OrderEnumerator {
|
|
||||||
return ModuleRootManager.getInstance(it).orderEntries()
|
|
||||||
.compileOnly().withoutSdk().recursively().exportedOnly()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-58
@@ -1,58 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2010-2018 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.scratch
|
|
||||||
|
|
||||||
import com.intellij.ide.scratch.ScratchFileService
|
|
||||||
import com.intellij.openapi.module.Module
|
|
||||||
import com.intellij.openapi.module.ModuleManager
|
|
||||||
import com.intellij.openapi.project.Project
|
|
||||||
import com.intellij.openapi.roots.*
|
|
||||||
import com.intellij.openapi.roots.libraries.Library
|
|
||||||
import com.intellij.openapi.vfs.VirtualFile
|
|
||||||
import org.jetbrains.kotlin.idea.core.script.dependencies.ScriptAdditionalIdeaDependenciesProvider
|
|
||||||
import org.jetbrains.kotlin.idea.core.script.scriptRelatedModuleName
|
|
||||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
|
||||||
|
|
||||||
class ScratchAdditionalIdeaDependenciesProvider : ScriptAdditionalIdeaDependenciesProvider() {
|
|
||||||
|
|
||||||
override fun getRelatedModules(file: VirtualFile, project: Project): List<Module> {
|
|
||||||
if (!ScratchFileService.isInScratchRoot(file)) return emptyList()
|
|
||||||
|
|
||||||
val scratchModule = file.scriptRelatedModuleName?.let {
|
|
||||||
ModuleManager.getInstance(project).findModuleByName(it)
|
|
||||||
} ?: return emptyList()
|
|
||||||
|
|
||||||
val modules = linkedSetOf(scratchModule)
|
|
||||||
moduleDependencyEnumerator(scratchModule).withoutLibraries().forEach { orderEntry ->
|
|
||||||
when (orderEntry) {
|
|
||||||
is ModuleSourceOrderEntry -> modules.add(orderEntry.getOwnerModule())
|
|
||||||
is ModuleOrderEntry -> modules.addIfNotNull(orderEntry.module)
|
|
||||||
}
|
|
||||||
true
|
|
||||||
}
|
|
||||||
return modules.toList()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getRelatedLibraries(file: VirtualFile, project: Project): List<Library> {
|
|
||||||
if (!ScratchFileService.isInScratchRoot(file)) return emptyList()
|
|
||||||
|
|
||||||
val result = linkedSetOf<Library>()
|
|
||||||
getRelatedModules(file, project).forEach {
|
|
||||||
moduleDependencyEnumerator(it).withoutDepModules().forEach { orderEntry ->
|
|
||||||
if (orderEntry is LibraryOrderEntry) {
|
|
||||||
result.addIfNotNull(orderEntry.library)
|
|
||||||
}
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result.toList()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun moduleDependencyEnumerator(it: Module): OrderEnumerator {
|
|
||||||
return ModuleRootManager.getInstance(it).orderEntries()
|
|
||||||
.compileOnly().withoutSdk().recursively().exportedOnly()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-58
@@ -1,58 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2010-2018 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.scratch
|
|
||||||
|
|
||||||
import com.intellij.ide.scratch.ScratchFileService
|
|
||||||
import com.intellij.openapi.module.Module
|
|
||||||
import com.intellij.openapi.module.ModuleManager
|
|
||||||
import com.intellij.openapi.project.Project
|
|
||||||
import com.intellij.openapi.roots.*
|
|
||||||
import com.intellij.openapi.roots.libraries.Library
|
|
||||||
import com.intellij.openapi.vfs.VirtualFile
|
|
||||||
import org.jetbrains.kotlin.idea.core.script.dependencies.ScriptAdditionalIdeaDependenciesProvider
|
|
||||||
import org.jetbrains.kotlin.idea.core.script.scriptRelatedModuleName
|
|
||||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
|
||||||
|
|
||||||
class ScratchAdditionalIdeaDependenciesProvider : ScriptAdditionalIdeaDependenciesProvider() {
|
|
||||||
|
|
||||||
override fun getRelatedModules(file: VirtualFile, project: Project): List<Module> {
|
|
||||||
if (!ScratchFileService.isInScratchRoot(file)) return emptyList()
|
|
||||||
|
|
||||||
val scratchModule = file.scriptRelatedModuleName?.let {
|
|
||||||
ModuleManager.getInstance(project).findModuleByName(it)
|
|
||||||
} ?: return emptyList()
|
|
||||||
|
|
||||||
val modules = linkedSetOf(scratchModule)
|
|
||||||
moduleDependencyEnumerator(scratchModule).withoutLibraries().forEach { orderEntry ->
|
|
||||||
when (orderEntry) {
|
|
||||||
is ModuleSourceOrderEntry -> modules.add(orderEntry.getOwnerModule())
|
|
||||||
is ModuleOrderEntry -> modules.addIfNotNull(orderEntry.module)
|
|
||||||
}
|
|
||||||
true
|
|
||||||
}
|
|
||||||
return modules.toList()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getRelatedLibraries(file: VirtualFile, project: Project): List<Library> {
|
|
||||||
if (!ScratchFileService.isInScratchRoot(file)) return emptyList()
|
|
||||||
|
|
||||||
val result = linkedSetOf<Library>()
|
|
||||||
getRelatedModules(file, project).forEach {
|
|
||||||
moduleDependencyEnumerator(it).withoutDepModules().forEach { orderEntry ->
|
|
||||||
if (orderEntry is LibraryOrderEntry) {
|
|
||||||
result.addIfNotNull(orderEntry.library)
|
|
||||||
}
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result.toList()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun moduleDependencyEnumerator(it: Module): OrderEnumerator {
|
|
||||||
return ModuleRootManager.getInstance(it).orderEntries()
|
|
||||||
.compileOnly().withoutSdk().recursively().exportedOnly()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user