Fixed incompatibility with 191 and 192
Fixed #KT-35918
This commit is contained in:
@@ -0,0 +1,337 @@
|
||||
/*
|
||||
* 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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,337 @@
|
||||
/*
|
||||
* 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
@@ -0,0 +1,337 @@
|
||||
/*
|
||||
* 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
|
||||
}
|
||||
}
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.modules;
|
||||
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.roots.ModuleRootManager;
|
||||
import com.intellij.openapi.roots.ProjectFileIndex;
|
||||
import com.intellij.openapi.vfs.JarFileSystem;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.PsiJavaFile;
|
||||
import com.intellij.psi.PsiJavaModule;
|
||||
import com.intellij.psi.PsiManager;
|
||||
import com.intellij.psi.impl.light.LightJavaModule;
|
||||
import kotlin.collections.ArraysKt;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.idea.core.FileIndexUtilsKt;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.jar.Attributes;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.jar.Manifest;
|
||||
|
||||
import static com.intellij.psi.PsiJavaModule.MODULE_INFO_FILE;
|
||||
|
||||
// Copied from com.intellij.codeInsight.daemon.impl.analysis.ModuleHighlightUtil
|
||||
public class ModuleHighlightUtil2 {
|
||||
private static final Attributes.Name MULTI_RELEASE = new Attributes.Name("Multi-Release");
|
||||
|
||||
@Nullable
|
||||
static PsiJavaModule getModuleDescriptor(@NotNull VirtualFile file, @NotNull Project project) {
|
||||
ProjectFileIndex index = ProjectFileIndex.SERVICE.getInstance(project);
|
||||
if (index.isInLibrary(file)) {
|
||||
VirtualFile root;
|
||||
if ((root = index.getClassRootForFile(file)) != null) {
|
||||
VirtualFile descriptorFile = root.findChild(PsiJavaModule.MODULE_INFO_CLS_FILE);
|
||||
if (descriptorFile == null) {
|
||||
VirtualFile alt = root.findFileByRelativePath("META-INF/versions/9/" + PsiJavaModule.MODULE_INFO_CLS_FILE);
|
||||
if (alt != null && isMultiReleaseJar(root)) {
|
||||
descriptorFile = alt;
|
||||
}
|
||||
}
|
||||
if (descriptorFile != null) {
|
||||
PsiFile psiFile = PsiManager.getInstance(project).findFile(descriptorFile);
|
||||
if (psiFile instanceof PsiJavaFile) {
|
||||
return ((PsiJavaFile) psiFile).getModuleDeclaration();
|
||||
}
|
||||
}
|
||||
else if (root.getFileSystem() instanceof JarFileSystem && "jar".equalsIgnoreCase(root.getExtension())) {
|
||||
return LightJavaModule.getModule(PsiManager.getInstance(project), root);
|
||||
}
|
||||
}
|
||||
else if ((root = index.getSourceRootForFile(file)) != null) {
|
||||
VirtualFile descriptorFile = root.findChild(MODULE_INFO_FILE);
|
||||
if (descriptorFile != null) {
|
||||
PsiFile psiFile = PsiManager.getInstance(project).findFile(descriptorFile);
|
||||
if (psiFile instanceof PsiJavaFile) {
|
||||
return ((PsiJavaFile) psiFile).getModuleDeclaration();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
Module module = index.getModuleForFile(file);
|
||||
if (module != null) {
|
||||
boolean isTest = FileIndexUtilsKt.isInTestSourceContentKotlinAware(index, file);
|
||||
VirtualFile modularRoot = ArraysKt.singleOrNull(ModuleRootManager.getInstance(module).getSourceRoots(isTest),
|
||||
root -> root.findChild(MODULE_INFO_FILE) != null);
|
||||
if (modularRoot != null) {
|
||||
VirtualFile moduleInfo = modularRoot.findChild(MODULE_INFO_FILE);
|
||||
assert moduleInfo != null : modularRoot;
|
||||
PsiFile psiFile = PsiManager.getInstance(project).findFile(moduleInfo);
|
||||
if (psiFile instanceof PsiJavaFile) {
|
||||
return ((PsiJavaFile) psiFile).getModuleDeclaration();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean isMultiReleaseJar(VirtualFile root) {
|
||||
if (root.getFileSystem() instanceof JarFileSystem) {
|
||||
VirtualFile manifest = root.findFileByRelativePath(JarFile.MANIFEST_NAME);
|
||||
if (manifest != null) {
|
||||
try (InputStream stream = manifest.getInputStream()) {
|
||||
return Boolean.valueOf(new Manifest(stream).getMainAttributes().getValue(MULTI_RELEASE));
|
||||
}
|
||||
catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.modules;
|
||||
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.roots.ModuleRootManager;
|
||||
import com.intellij.openapi.roots.ProjectFileIndex;
|
||||
import com.intellij.openapi.vfs.JarFileSystem;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.PsiJavaFile;
|
||||
import com.intellij.psi.PsiJavaModule;
|
||||
import com.intellij.psi.PsiManager;
|
||||
import com.intellij.psi.impl.light.LightJavaModule;
|
||||
import kotlin.collections.ArraysKt;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.idea.core.FileIndexUtilsKt;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.jar.Attributes;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.jar.Manifest;
|
||||
|
||||
import static com.intellij.psi.PsiJavaModule.MODULE_INFO_FILE;
|
||||
|
||||
// Copied from com.intellij.codeInsight.daemon.impl.analysis.ModuleHighlightUtil
|
||||
public class ModuleHighlightUtil2 {
|
||||
private static final Attributes.Name MULTI_RELEASE = new Attributes.Name("Multi-Release");
|
||||
|
||||
@Nullable
|
||||
static PsiJavaModule getModuleDescriptor(@NotNull VirtualFile file, @NotNull Project project) {
|
||||
ProjectFileIndex index = ProjectFileIndex.SERVICE.getInstance(project);
|
||||
if (index.isInLibrary(file)) {
|
||||
VirtualFile root;
|
||||
if ((root = index.getClassRootForFile(file)) != null) {
|
||||
VirtualFile descriptorFile = root.findChild(PsiJavaModule.MODULE_INFO_CLS_FILE);
|
||||
if (descriptorFile == null) {
|
||||
VirtualFile alt = root.findFileByRelativePath("META-INF/versions/9/" + PsiJavaModule.MODULE_INFO_CLS_FILE);
|
||||
if (alt != null && isMultiReleaseJar(root)) {
|
||||
descriptorFile = alt;
|
||||
}
|
||||
}
|
||||
if (descriptorFile != null) {
|
||||
PsiFile psiFile = PsiManager.getInstance(project).findFile(descriptorFile);
|
||||
if (psiFile instanceof PsiJavaFile) {
|
||||
return ((PsiJavaFile) psiFile).getModuleDeclaration();
|
||||
}
|
||||
}
|
||||
else if (root.getFileSystem() instanceof JarFileSystem && "jar".equalsIgnoreCase(root.getExtension())) {
|
||||
return LightJavaModule.getModule(PsiManager.getInstance(project), root);
|
||||
}
|
||||
}
|
||||
else if ((root = index.getSourceRootForFile(file)) != null) {
|
||||
VirtualFile descriptorFile = root.findChild(MODULE_INFO_FILE);
|
||||
if (descriptorFile != null) {
|
||||
PsiFile psiFile = PsiManager.getInstance(project).findFile(descriptorFile);
|
||||
if (psiFile instanceof PsiJavaFile) {
|
||||
return ((PsiJavaFile) psiFile).getModuleDeclaration();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
Module module = index.getModuleForFile(file);
|
||||
if (module != null) {
|
||||
boolean isTest = FileIndexUtilsKt.isInTestSourceContentKotlinAware(index, file);
|
||||
VirtualFile modularRoot = ArraysKt.singleOrNull(ModuleRootManager.getInstance(module).getSourceRoots(isTest),
|
||||
root -> root.findChild(MODULE_INFO_FILE) != null);
|
||||
if (modularRoot != null) {
|
||||
VirtualFile moduleInfo = modularRoot.findChild(MODULE_INFO_FILE);
|
||||
assert moduleInfo != null : modularRoot;
|
||||
PsiFile psiFile = PsiManager.getInstance(project).findFile(moduleInfo);
|
||||
if (psiFile instanceof PsiJavaFile) {
|
||||
return ((PsiJavaFile) psiFile).getModuleDeclaration();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean isMultiReleaseJar(VirtualFile root) {
|
||||
if (root.getFileSystem() instanceof JarFileSystem) {
|
||||
VirtualFile manifest = root.findFileByRelativePath(JarFile.MANIFEST_NAME);
|
||||
if (manifest != null) {
|
||||
try (InputStream stream = manifest.getInputStream()) {
|
||||
return Boolean.valueOf(new Manifest(stream).getMainAttributes().getValue(MULTI_RELEASE));
|
||||
}
|
||||
catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.modules;
|
||||
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.roots.ModuleRootManager;
|
||||
import com.intellij.openapi.roots.ProjectFileIndex;
|
||||
import com.intellij.openapi.vfs.JarFileSystem;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.PsiJavaFile;
|
||||
import com.intellij.psi.PsiJavaModule;
|
||||
import com.intellij.psi.PsiManager;
|
||||
import com.intellij.psi.impl.light.LightJavaModule;
|
||||
import kotlin.collections.ArraysKt;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.idea.core.FileIndexUtilsKt;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.jar.Attributes;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.jar.Manifest;
|
||||
|
||||
import static com.intellij.psi.PsiJavaModule.MODULE_INFO_FILE;
|
||||
|
||||
// Copied from com.intellij.codeInsight.daemon.impl.analysis.ModuleHighlightUtil
|
||||
public class ModuleHighlightUtil2 {
|
||||
private static final Attributes.Name MULTI_RELEASE = new Attributes.Name("Multi-Release");
|
||||
|
||||
@Nullable
|
||||
static PsiJavaModule getModuleDescriptor(@NotNull VirtualFile file, @NotNull Project project) {
|
||||
ProjectFileIndex index = ProjectFileIndex.SERVICE.getInstance(project);
|
||||
if (index.isInLibrary(file)) {
|
||||
VirtualFile root;
|
||||
if ((root = index.getClassRootForFile(file)) != null) {
|
||||
VirtualFile descriptorFile = root.findChild(PsiJavaModule.MODULE_INFO_CLS_FILE);
|
||||
if (descriptorFile == null) {
|
||||
VirtualFile alt = root.findFileByRelativePath("META-INF/versions/9/" + PsiJavaModule.MODULE_INFO_CLS_FILE);
|
||||
if (alt != null && isMultiReleaseJar(root)) {
|
||||
descriptorFile = alt;
|
||||
}
|
||||
}
|
||||
if (descriptorFile != null) {
|
||||
PsiFile psiFile = PsiManager.getInstance(project).findFile(descriptorFile);
|
||||
if (psiFile instanceof PsiJavaFile) {
|
||||
return ((PsiJavaFile) psiFile).getModuleDeclaration();
|
||||
}
|
||||
}
|
||||
else if (root.getFileSystem() instanceof JarFileSystem && "jar".equalsIgnoreCase(root.getExtension())) {
|
||||
return LightJavaModule.getModule(PsiManager.getInstance(project), root);
|
||||
}
|
||||
}
|
||||
else if ((root = index.getSourceRootForFile(file)) != null) {
|
||||
VirtualFile descriptorFile = root.findChild(MODULE_INFO_FILE);
|
||||
if (descriptorFile != null) {
|
||||
PsiFile psiFile = PsiManager.getInstance(project).findFile(descriptorFile);
|
||||
if (psiFile instanceof PsiJavaFile) {
|
||||
return ((PsiJavaFile) psiFile).getModuleDeclaration();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
Module module = index.getModuleForFile(file);
|
||||
if (module != null) {
|
||||
boolean isTest = FileIndexUtilsKt.isInTestSourceContentKotlinAware(index, file);
|
||||
VirtualFile modularRoot = ArraysKt.singleOrNull(ModuleRootManager.getInstance(module).getSourceRoots(isTest),
|
||||
root -> root.findChild(MODULE_INFO_FILE) != null);
|
||||
if (modularRoot != null) {
|
||||
VirtualFile moduleInfo = modularRoot.findChild(MODULE_INFO_FILE);
|
||||
assert moduleInfo != null : modularRoot;
|
||||
PsiFile psiFile = PsiManager.getInstance(project).findFile(moduleInfo);
|
||||
if (psiFile instanceof PsiJavaFile) {
|
||||
return ((PsiJavaFile) psiFile).getModuleDeclaration();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean isMultiReleaseJar(VirtualFile root) {
|
||||
if (root.getFileSystem() instanceof JarFileSystem) {
|
||||
VirtualFile manifest = root.findFileByRelativePath(JarFile.MANIFEST_NAME);
|
||||
if (manifest != null) {
|
||||
try (InputStream stream = manifest.getInputStream()) {
|
||||
return Boolean.valueOf(new Manifest(stream).getMainAttributes().getValue(MULTI_RELEASE));
|
||||
}
|
||||
catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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.statistics
|
||||
|
||||
import com.intellij.internal.statistic.beans.UsageDescriptor
|
||||
import com.intellij.internal.statistic.eventLog.FeatureUsageData
|
||||
import com.intellij.internal.statistic.service.fus.collectors.ProjectUsagesCollector
|
||||
import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.kotlin.idea.KotlinPluginUtil
|
||||
import org.jetbrains.kotlin.idea.core.script.settings.KotlinScriptingSettings
|
||||
import org.jetbrains.kotlin.idea.project.NewInferenceForIDEAnalysisComponent
|
||||
|
||||
class IDESettingsFUSCollector : ProjectUsagesCollector() {
|
||||
|
||||
override fun getUsages(project: Project): Set<UsageDescriptor> {
|
||||
val usages = mutableSetOf<UsageDescriptor>()
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
val inferenceState = NewInferenceForIDEAnalysisComponent.isEnabledForV1(project)
|
||||
usages.add(UsageDescriptor("newInference", flagUsage(inferenceState)))
|
||||
|
||||
val scriptingAutoReloadEnabled = KotlinScriptingSettings.getInstance(project).isAutoReloadEnabled
|
||||
usages.add(UsageDescriptor("scriptingAutoReloadEnabled", flagUsage(scriptingAutoReloadEnabled)))
|
||||
|
||||
return usages
|
||||
}
|
||||
|
||||
private fun flagUsage(enabled: Boolean): FeatureUsageData {
|
||||
return FeatureUsageData()
|
||||
.addData("enabled", enabled)
|
||||
.addData("pluginVersion", KotlinPluginUtil.getPluginVersion())
|
||||
}
|
||||
|
||||
override fun getGroupId() = "kotlin.ide.settings"
|
||||
override fun getVersion(): Int = 2
|
||||
}
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* 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.statistics
|
||||
|
||||
import com.intellij.facet.ProjectFacetManager
|
||||
import com.intellij.internal.statistic.beans.UsageDescriptor
|
||||
import com.intellij.internal.statistic.eventLog.FeatureUsageData
|
||||
import com.intellij.internal.statistic.service.fus.collectors.ProjectUsagesCollector
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.kotlin.idea.KotlinPluginUtil
|
||||
import org.jetbrains.kotlin.idea.configuration.BuildSystemType
|
||||
import org.jetbrains.kotlin.idea.configuration.getBuildSystemType
|
||||
import org.jetbrains.kotlin.idea.facet.KotlinFacetType
|
||||
import org.jetbrains.kotlin.idea.project.languageVersionSettings
|
||||
import org.jetbrains.kotlin.idea.project.platform
|
||||
import org.jetbrains.kotlin.platform.isCommon
|
||||
import org.jetbrains.kotlin.platform.js.isJs
|
||||
import org.jetbrains.kotlin.platform.jvm.isJvm
|
||||
import org.jetbrains.kotlin.platform.konan.isNative
|
||||
|
||||
class ProjectConfigurationCollector : ProjectUsagesCollector() {
|
||||
|
||||
override fun getUsages(project: Project): Set<UsageDescriptor> {
|
||||
val usages = mutableSetOf<UsageDescriptor>()
|
||||
val modulesWithFacet = ProjectFacetManager.getInstance(project).getModulesWithFacet(KotlinFacetType.TYPE_ID)
|
||||
|
||||
if (modulesWithFacet.isNotEmpty()) {
|
||||
val pluginVersion = KotlinPluginUtil.getPluginVersion()
|
||||
modulesWithFacet.forEach {
|
||||
|
||||
val buildSystem = getBuildSystemType(it)
|
||||
val platform = getPlatform(it)
|
||||
val languageVersion = it.languageVersionSettings.languageVersion.versionString
|
||||
|
||||
val data = FeatureUsageData()
|
||||
.addData("pluginVersion", pluginVersion)
|
||||
.addData("system", buildSystem)
|
||||
.addData("platform", platform)
|
||||
.addData("languageVersion", languageVersion)
|
||||
val usageDescriptor = UsageDescriptor("Build", data)
|
||||
usages.add(usageDescriptor)
|
||||
}
|
||||
}
|
||||
return usages
|
||||
}
|
||||
|
||||
private fun getPlatform(it: Module): String {
|
||||
return when {
|
||||
it.platform.isJvm() -> "jvm"
|
||||
it.platform.isJs() -> "js"
|
||||
it.platform.isCommon() -> "common"
|
||||
it.platform.isNative() -> "native"
|
||||
else -> "unknown"
|
||||
}
|
||||
}
|
||||
|
||||
private fun getBuildSystemType(it: Module): String {
|
||||
val buildSystem = it.getBuildSystemType()
|
||||
return when {
|
||||
buildSystem == BuildSystemType.JPS -> "JPS"
|
||||
buildSystem.toString().toLowerCase().contains("maven") -> "Maven"
|
||||
buildSystem.toString().toLowerCase().contains("gradle") -> "Gradle"
|
||||
else -> "unknown"
|
||||
}
|
||||
}
|
||||
|
||||
override fun getGroupId() = "kotlin.project.configuration"
|
||||
override fun getVersion(): Int = 1
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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.run
|
||||
|
||||
import com.intellij.execution.Location
|
||||
import com.intellij.execution.actions.ConfigurationFromContext
|
||||
import com.intellij.execution.junit.JUnitConfigurationProducer
|
||||
import com.intellij.execution.testframework.AbstractPatternBasedConfigurationProducer
|
||||
import com.intellij.ide.plugins.PluginManager
|
||||
import com.intellij.openapi.extensions.PluginId.getId
|
||||
import com.intellij.openapi.util.component1
|
||||
import com.intellij.openapi.util.component2
|
||||
import com.intellij.psi.PsiClass
|
||||
import com.intellij.psi.PsiMethod
|
||||
|
||||
private val isJUnitEnabled by lazy { isPluginEnabled("JUnit") }
|
||||
private val isTestNgEnabled by lazy { isPluginEnabled("TestNG-J") }
|
||||
|
||||
private fun isPluginEnabled(id: String): Boolean {
|
||||
return PluginManager.isPluginInstalled(getId(id)) && id !in PluginManager.getDisabledPlugins()
|
||||
}
|
||||
|
||||
internal fun ConfigurationFromContext.isJpsJunitConfiguration(): Boolean {
|
||||
return isProducedBy(JUnitConfigurationProducer::class.java)
|
||||
|| isProducedBy(AbstractPatternBasedConfigurationProducer::class.java)
|
||||
}
|
||||
|
||||
internal fun canRunJvmTests() = isJUnitEnabled || isTestNgEnabled
|
||||
|
||||
internal fun getTestClassForJvm(location: Location<*>): PsiClass? {
|
||||
val leaf = location.psiElement ?: return null
|
||||
|
||||
if (isJUnitEnabled) {
|
||||
KotlinJUnitRunConfigurationProducer.getTestClass(leaf)?.let { return it }
|
||||
}
|
||||
if (isTestNgEnabled) {
|
||||
KotlinTestNgConfigurationProducer.getTestClassAndMethod(leaf)?.let { (testClass, testMethod) ->
|
||||
return if (testMethod == null) testClass else null
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
internal fun getTestMethodForJvm(location: Location<*>): PsiMethod? {
|
||||
val leaf = location.psiElement ?: return null
|
||||
|
||||
if (isJUnitEnabled) {
|
||||
KotlinJUnitRunConfigurationProducer.getTestMethod(leaf)?.let { return it }
|
||||
}
|
||||
if (isTestNgEnabled) {
|
||||
KotlinTestNgConfigurationProducer.getTestClassAndMethod(leaf)?.second?.let { return it }
|
||||
}
|
||||
return null
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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.run
|
||||
|
||||
import com.intellij.execution.Location
|
||||
import com.intellij.execution.actions.ConfigurationFromContext
|
||||
import com.intellij.execution.junit.JUnitConfigurationProducer
|
||||
import com.intellij.execution.testframework.AbstractPatternBasedConfigurationProducer
|
||||
import com.intellij.ide.plugins.PluginManager
|
||||
import com.intellij.openapi.extensions.PluginId.getId
|
||||
import com.intellij.openapi.util.component1
|
||||
import com.intellij.openapi.util.component2
|
||||
import com.intellij.psi.PsiClass
|
||||
import com.intellij.psi.PsiMethod
|
||||
|
||||
private val isJUnitEnabled by lazy { isPluginEnabled("JUnit") }
|
||||
private val isTestNgEnabled by lazy { isPluginEnabled("TestNG-J") }
|
||||
|
||||
private fun isPluginEnabled(id: String): Boolean {
|
||||
return PluginManager.isPluginInstalled(getId(id)) && id !in PluginManager.getDisabledPlugins()
|
||||
}
|
||||
|
||||
internal fun ConfigurationFromContext.isJpsJunitConfiguration(): Boolean {
|
||||
return isProducedBy(JUnitConfigurationProducer::class.java)
|
||||
|| isProducedBy(AbstractPatternBasedConfigurationProducer::class.java)
|
||||
}
|
||||
|
||||
internal fun canRunJvmTests() = isJUnitEnabled || isTestNgEnabled
|
||||
|
||||
internal fun getTestClassForJvm(location: Location<*>): PsiClass? {
|
||||
val leaf = location.psiElement ?: return null
|
||||
|
||||
if (isJUnitEnabled) {
|
||||
KotlinJUnitRunConfigurationProducer.getTestClass(leaf)?.let { return it }
|
||||
}
|
||||
if (isTestNgEnabled) {
|
||||
KotlinTestNgConfigurationProducer.getTestClassAndMethod(leaf)?.let { (testClass, testMethod) ->
|
||||
return if (testMethod == null) testClass else null
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
internal fun getTestMethodForJvm(location: Location<*>): PsiMethod? {
|
||||
val leaf = location.psiElement ?: return null
|
||||
|
||||
if (isJUnitEnabled) {
|
||||
KotlinJUnitRunConfigurationProducer.getTestMethod(leaf)?.let { return it }
|
||||
}
|
||||
if (isTestNgEnabled) {
|
||||
KotlinTestNgConfigurationProducer.getTestClassAndMethod(leaf)?.second?.let { return it }
|
||||
}
|
||||
return null
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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.compiler.configuration
|
||||
|
||||
import com.intellij.compiler.server.BuildProcessParametersProvider
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.registry.Registry
|
||||
import org.jetbrains.kotlin.config.IncrementalCompilation
|
||||
import org.jetbrains.kotlin.idea.PluginStartupComponent
|
||||
|
||||
class KotlinBuildProcessParametersProvider(private val project: Project) : BuildProcessParametersProvider() {
|
||||
override fun getVMArguments(): MutableList<String> {
|
||||
val compilerWorkspaceSettings = KotlinCompilerWorkspaceSettings.getInstance(project)
|
||||
|
||||
val res = arrayListOf<String>()
|
||||
if (compilerWorkspaceSettings.preciseIncrementalEnabled) {
|
||||
res.add("-D" + IncrementalCompilation.INCREMENTAL_COMPILATION_JVM_PROPERTY + "=true")
|
||||
}
|
||||
if (compilerWorkspaceSettings.incrementalCompilationForJsEnabled) {
|
||||
res.add("-D" + IncrementalCompilation.INCREMENTAL_COMPILATION_JS_PROPERTY + "=true")
|
||||
}
|
||||
if (compilerWorkspaceSettings.enableDaemon) {
|
||||
res.add("-Dkotlin.daemon.enabled")
|
||||
}
|
||||
if (Registry.`is`("kotlin.jps.instrument.bytecode", false)) {
|
||||
res.add("-Dkotlin.jps.instrument.bytecode=true")
|
||||
}
|
||||
PluginStartupComponent.getInstance().aliveFlagPath.let {
|
||||
if (!it.isBlank()) {
|
||||
// TODO: consider taking the property name from compiler/daemon/common (check whether dependency will be not too heavy)
|
||||
res.add("-Dkotlin.daemon.client.alive.path=\"$it\"")
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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.compiler.configuration
|
||||
|
||||
import com.intellij.compiler.server.BuildProcessParametersProvider
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.registry.Registry
|
||||
import org.jetbrains.kotlin.config.IncrementalCompilation
|
||||
import org.jetbrains.kotlin.idea.PluginStartupComponent
|
||||
|
||||
class KotlinBuildProcessParametersProvider(private val project: Project) : BuildProcessParametersProvider() {
|
||||
override fun getVMArguments(): MutableList<String> {
|
||||
val compilerWorkspaceSettings = KotlinCompilerWorkspaceSettings.getInstance(project)
|
||||
|
||||
val res = arrayListOf<String>()
|
||||
if (compilerWorkspaceSettings.preciseIncrementalEnabled) {
|
||||
res.add("-D" + IncrementalCompilation.INCREMENTAL_COMPILATION_JVM_PROPERTY + "=true")
|
||||
}
|
||||
if (compilerWorkspaceSettings.incrementalCompilationForJsEnabled) {
|
||||
res.add("-D" + IncrementalCompilation.INCREMENTAL_COMPILATION_JS_PROPERTY + "=true")
|
||||
}
|
||||
if (compilerWorkspaceSettings.enableDaemon) {
|
||||
res.add("-Dkotlin.daemon.enabled")
|
||||
}
|
||||
if (Registry.`is`("kotlin.jps.instrument.bytecode", false)) {
|
||||
res.add("-Dkotlin.jps.instrument.bytecode=true")
|
||||
}
|
||||
PluginStartupComponent.getInstance().aliveFlagPath.let {
|
||||
if (!it.isBlank()) {
|
||||
// TODO: consider taking the property name from compiler/daemon/common (check whether dependency will be not too heavy)
|
||||
res.add("-Dkotlin.daemon.client.alive.path=\"$it\"")
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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()
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
import com.intellij.openapi.util.IconLoader
|
||||
import com.intellij.psi.PsiModifierListOwner
|
||||
import com.intellij.psi.impl.ElementPresentationUtil
|
||||
import com.intellij.util.PlatformIcons
|
||||
import javax.swing.Icon
|
||||
|
||||
class KotlinIdeFileIconProviderService : KotlinIconProviderService() {
|
||||
override fun getFileIcon(): Icon = KOTLIN_FILE
|
||||
|
||||
override fun getLightVariableIcon(element: PsiModifierListOwner, flags: Int): Icon {
|
||||
val baseIcon = ElementPresentationUtil.createLayeredIcon(PlatformIcons.VARIABLE_ICON, element, false)
|
||||
return ElementPresentationUtil.addVisibilityIcon(element, flags, baseIcon)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val KOTLIN_FILE = IconLoader.getIcon("/org/jetbrains/kotlin/idea/icons/kotlin_file.svg")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea;
|
||||
|
||||
|
||||
public class PluginStartupActivity {
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea;
|
||||
|
||||
|
||||
public class PluginStartupActivity {
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea;
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.application.PathMacros;
|
||||
import com.intellij.openapi.components.BaseComponent;
|
||||
import com.intellij.openapi.components.ServiceManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.EditorFactory;
|
||||
import com.intellij.openapi.editor.event.DocumentEvent;
|
||||
import com.intellij.openapi.editor.event.DocumentListener;
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager;
|
||||
import com.intellij.openapi.updateSettings.impl.UpdateChecker;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.search.searches.IndexPatternSearch;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.kotlin.idea.reporter.KotlinReportSubmitter;
|
||||
import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinTodoSearcher;
|
||||
import org.jetbrains.kotlin.utils.PathUtil;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.jetbrains.kotlin.idea.TestResourceBundleKt.registerAdditionalResourceBundleInTests;
|
||||
|
||||
public class PluginStartupComponent implements BaseComponent {
|
||||
private static final Logger LOG = Logger.getInstance(PluginStartupComponent.class);
|
||||
|
||||
private static final String KOTLIN_BUNDLED = "KOTLIN_BUNDLED";
|
||||
|
||||
public static PluginStartupComponent getInstance() {
|
||||
return ApplicationManager.getApplication().getComponent(PluginStartupComponent.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String getComponentName() {
|
||||
return PluginStartupComponent.class.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initComponent() {
|
||||
if (ApplicationManager.getApplication().isUnitTestMode()) {
|
||||
registerAdditionalResourceBundleInTests();
|
||||
}
|
||||
|
||||
registerPathVariable();
|
||||
|
||||
try {
|
||||
// API added in 15.0.2
|
||||
UpdateChecker.INSTANCE.getExcludedFromUpdateCheckPlugins().add("org.jetbrains.kotlin");
|
||||
}
|
||||
catch (Throwable throwable) {
|
||||
LOG.debug("Excluding Kotlin plugin updates using old API", throwable);
|
||||
UpdateChecker.getDisabledToUpdatePlugins().add("org.jetbrains.kotlin");
|
||||
}
|
||||
EditorFactory.getInstance().getEventMulticaster().addDocumentListener(new DocumentListener() {
|
||||
@Override
|
||||
public void documentChanged(@NotNull DocumentEvent e) {
|
||||
VirtualFile virtualFile = FileDocumentManager.getInstance().getFile(e.getDocument());
|
||||
if (virtualFile != null && virtualFile.getFileType() == KotlinFileType.INSTANCE) {
|
||||
KotlinPluginUpdater.Companion.getInstance().kotlinFileEdited(virtualFile);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
ServiceManager.getService(IndexPatternSearch.class).registerExecutor(new KotlinTodoSearcher());
|
||||
|
||||
KotlinPluginCompatibilityVerifier.checkCompatibility();
|
||||
|
||||
KotlinReportSubmitter.Companion.setupReportingFromRelease();
|
||||
|
||||
//todo[Sedunov]: wait for fix in platform to avoid misunderstood from Java newbies (also ConfigureKotlinInTempDirTest)
|
||||
//KotlinSdkType.Companion.setUpIfNeeded();
|
||||
}
|
||||
|
||||
private static void registerPathVariable() {
|
||||
PathMacros macros = PathMacros.getInstance();
|
||||
macros.setMacro(KOTLIN_BUNDLED, PathUtil.getKotlinPathsForIdeaPlugin().getHomePath().getPath());
|
||||
}
|
||||
|
||||
private String aliveFlagPath;
|
||||
|
||||
public synchronized String getAliveFlagPath() {
|
||||
if (this.aliveFlagPath == null) {
|
||||
try {
|
||||
File flagFile = File.createTempFile("kotlin-idea-", "-is-running");
|
||||
flagFile.deleteOnExit();
|
||||
this.aliveFlagPath = flagFile.getAbsolutePath();
|
||||
}
|
||||
catch (IOException e) {
|
||||
this.aliveFlagPath = "";
|
||||
}
|
||||
}
|
||||
return this.aliveFlagPath;
|
||||
}
|
||||
|
||||
public synchronized void resetAliveFlag() {
|
||||
if (this.aliveFlagPath != null) {
|
||||
File flagFile = new File(this.aliveFlagPath);
|
||||
if (flagFile.exists()) {
|
||||
if (flagFile.delete()) {
|
||||
this.aliveFlagPath = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disposeComponent() {}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea;
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.application.PathMacros;
|
||||
import com.intellij.openapi.components.BaseComponent;
|
||||
import com.intellij.openapi.components.ServiceManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.EditorFactory;
|
||||
import com.intellij.openapi.editor.event.DocumentEvent;
|
||||
import com.intellij.openapi.editor.event.DocumentListener;
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager;
|
||||
import com.intellij.openapi.updateSettings.impl.UpdateChecker;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.search.searches.IndexPatternSearch;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.kotlin.idea.reporter.KotlinReportSubmitter;
|
||||
import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinTodoSearcher;
|
||||
import org.jetbrains.kotlin.utils.PathUtil;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.jetbrains.kotlin.idea.TestResourceBundleKt.registerAdditionalResourceBundleInTests;
|
||||
|
||||
public class PluginStartupComponent implements BaseComponent {
|
||||
private static final Logger LOG = Logger.getInstance(PluginStartupComponent.class);
|
||||
|
||||
private static final String KOTLIN_BUNDLED = "KOTLIN_BUNDLED";
|
||||
|
||||
public static PluginStartupComponent getInstance() {
|
||||
return ApplicationManager.getApplication().getComponent(PluginStartupComponent.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String getComponentName() {
|
||||
return PluginStartupComponent.class.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initComponent() {
|
||||
if (ApplicationManager.getApplication().isUnitTestMode()) {
|
||||
registerAdditionalResourceBundleInTests();
|
||||
}
|
||||
|
||||
registerPathVariable();
|
||||
|
||||
try {
|
||||
// API added in 15.0.2
|
||||
UpdateChecker.INSTANCE.getExcludedFromUpdateCheckPlugins().add("org.jetbrains.kotlin");
|
||||
}
|
||||
catch (Throwable throwable) {
|
||||
LOG.debug("Excluding Kotlin plugin updates using old API", throwable);
|
||||
UpdateChecker.getDisabledToUpdatePlugins().add("org.jetbrains.kotlin");
|
||||
}
|
||||
EditorFactory.getInstance().getEventMulticaster().addDocumentListener(new DocumentListener() {
|
||||
@Override
|
||||
public void documentChanged(@NotNull DocumentEvent e) {
|
||||
VirtualFile virtualFile = FileDocumentManager.getInstance().getFile(e.getDocument());
|
||||
if (virtualFile != null && virtualFile.getFileType() == KotlinFileType.INSTANCE) {
|
||||
KotlinPluginUpdater.Companion.getInstance().kotlinFileEdited(virtualFile);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
ServiceManager.getService(IndexPatternSearch.class).registerExecutor(new KotlinTodoSearcher());
|
||||
|
||||
KotlinPluginCompatibilityVerifier.checkCompatibility();
|
||||
|
||||
KotlinReportSubmitter.Companion.setupReportingFromRelease();
|
||||
|
||||
//todo[Sedunov]: wait for fix in platform to avoid misunderstood from Java newbies (also ConfigureKotlinInTempDirTest)
|
||||
//KotlinSdkType.Companion.setUpIfNeeded();
|
||||
}
|
||||
|
||||
private static void registerPathVariable() {
|
||||
PathMacros macros = PathMacros.getInstance();
|
||||
macros.setMacro(KOTLIN_BUNDLED, PathUtil.getKotlinPathsForIdeaPlugin().getHomePath().getPath());
|
||||
}
|
||||
|
||||
private String aliveFlagPath;
|
||||
|
||||
public synchronized String getAliveFlagPath() {
|
||||
if (this.aliveFlagPath == null) {
|
||||
try {
|
||||
File flagFile = File.createTempFile("kotlin-idea-", "-is-running");
|
||||
flagFile.deleteOnExit();
|
||||
this.aliveFlagPath = flagFile.getAbsolutePath();
|
||||
}
|
||||
catch (IOException e) {
|
||||
this.aliveFlagPath = "";
|
||||
}
|
||||
}
|
||||
return this.aliveFlagPath;
|
||||
}
|
||||
|
||||
public synchronized void resetAliveFlag() {
|
||||
if (this.aliveFlagPath != null) {
|
||||
File flagFile = new File(this.aliveFlagPath);
|
||||
if (flagFile.exists()) {
|
||||
if (flagFile.delete()) {
|
||||
this.aliveFlagPath = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disposeComponent() {}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea;
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
public class PluginStartupService {
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea;
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
public class PluginStartupService {
|
||||
|
||||
|
||||
}
|
||||
+751
@@ -0,0 +1,751 @@
|
||||
/*
|
||||
* Copyright 2000-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.compiler.configuration;
|
||||
|
||||
import com.intellij.icons.AllIcons;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
|
||||
import com.intellij.openapi.module.ModuleManager;
|
||||
import com.intellij.openapi.options.Configurable;
|
||||
import com.intellij.openapi.options.ConfigurationException;
|
||||
import com.intellij.openapi.options.SearchableConfigurable;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.TextComponentAccessor;
|
||||
import com.intellij.openapi.ui.TextFieldWithBrowseButton;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.ui.ListCellRendererWrapper;
|
||||
import com.intellij.ui.RawCommandLineEditor;
|
||||
import com.intellij.util.text.VersionComparatorUtil;
|
||||
import com.intellij.util.ui.ThreeStateCheckBox;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import kotlin.collections.ArraysKt;
|
||||
import kotlin.collections.CollectionsKt;
|
||||
import kotlin.jvm.functions.Function0;
|
||||
import kotlin.jvm.functions.Function1;
|
||||
import org.jetbrains.annotations.Nls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments;
|
||||
import org.jetbrains.kotlin.cli.common.arguments.K2JSCompilerArguments;
|
||||
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments;
|
||||
import org.jetbrains.kotlin.cli.common.arguments.K2JsArgumentConstants;
|
||||
import org.jetbrains.kotlin.config.*;
|
||||
import org.jetbrains.kotlin.idea.KotlinBundle;
|
||||
import org.jetbrains.kotlin.idea.PluginStartupComponent;
|
||||
import org.jetbrains.kotlin.idea.facet.DescriptionListCellRenderer;
|
||||
import org.jetbrains.kotlin.idea.facet.KotlinFacet;
|
||||
import org.jetbrains.kotlin.idea.project.NewInferenceForIDEAnalysisComponent;
|
||||
import org.jetbrains.kotlin.idea.roots.RootUtilsKt;
|
||||
import org.jetbrains.kotlin.idea.util.CidrUtil;
|
||||
import org.jetbrains.kotlin.idea.util.application.ApplicationUtilsKt;
|
||||
import org.jetbrains.kotlin.platform.IdePlatformKind;
|
||||
import org.jetbrains.kotlin.platform.PlatformUtilKt;
|
||||
import org.jetbrains.kotlin.platform.impl.JsIdePlatformUtil;
|
||||
import org.jetbrains.kotlin.platform.impl.JvmIdePlatformKind;
|
||||
import org.jetbrains.kotlin.platform.impl.JvmIdePlatformUtil;
|
||||
import org.jetbrains.kotlin.config.JvmTarget;
|
||||
import org.jetbrains.kotlin.platform.jvm.JdkPlatform;
|
||||
import org.jetbrains.kotlin.platform.TargetPlatform;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.util.*;
|
||||
|
||||
public class KotlinCompilerConfigurableTab implements SearchableConfigurable {
|
||||
private static final Map<String, String> moduleKindDescriptions = new LinkedHashMap<>();
|
||||
private static final Map<String, String> soruceMapSourceEmbeddingDescriptions = new LinkedHashMap<>();
|
||||
private static final List<LanguageFeature.State> languageFeatureStates = Arrays.asList(
|
||||
LanguageFeature.State.ENABLED, LanguageFeature.State.ENABLED_WITH_WARNING, LanguageFeature.State.ENABLED_WITH_ERROR
|
||||
);
|
||||
private static final int MAX_WARNING_SIZE = 75;
|
||||
|
||||
static {
|
||||
moduleKindDescriptions.put(K2JsArgumentConstants.MODULE_PLAIN, "Plain (put to global scope)");
|
||||
moduleKindDescriptions.put(K2JsArgumentConstants.MODULE_AMD, "AMD");
|
||||
moduleKindDescriptions.put(K2JsArgumentConstants.MODULE_COMMONJS, "CommonJS");
|
||||
moduleKindDescriptions.put(K2JsArgumentConstants.MODULE_UMD, "UMD (detect AMD or CommonJS if available, fallback to plain)");
|
||||
|
||||
soruceMapSourceEmbeddingDescriptions.put(K2JsArgumentConstants.SOURCE_MAP_SOURCE_CONTENT_NEVER, "Never");
|
||||
soruceMapSourceEmbeddingDescriptions.put(K2JsArgumentConstants.SOURCE_MAP_SOURCE_CONTENT_ALWAYS, "Always");
|
||||
soruceMapSourceEmbeddingDescriptions.put(K2JsArgumentConstants.SOURCE_MAP_SOURCE_CONTENT_INLINING,
|
||||
"When inlining a function from other module with embedded sources");
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private final KotlinCompilerWorkspaceSettings compilerWorkspaceSettings;
|
||||
private final Project project;
|
||||
private final boolean isProjectSettings;
|
||||
private CommonCompilerArguments commonCompilerArguments;
|
||||
private K2JSCompilerArguments k2jsCompilerArguments;
|
||||
private K2JVMCompilerArguments k2jvmCompilerArguments;
|
||||
private CompilerSettings compilerSettings;
|
||||
private JPanel contentPane;
|
||||
private ThreeStateCheckBox reportWarningsCheckBox;
|
||||
private RawCommandLineEditor additionalArgsOptionsField;
|
||||
private JLabel additionalArgsLabel;
|
||||
private ThreeStateCheckBox generateSourceMapsCheckBox;
|
||||
private TextFieldWithBrowseButton outputPrefixFile;
|
||||
private TextFieldWithBrowseButton outputPostfixFile;
|
||||
private JLabel labelForOutputDirectory;
|
||||
private TextFieldWithBrowseButton outputDirectory;
|
||||
private ThreeStateCheckBox copyRuntimeFilesCheckBox;
|
||||
private ThreeStateCheckBox keepAliveCheckBox;
|
||||
private JCheckBox enableIncrementalCompilationForJvmCheckBox;
|
||||
private JCheckBox enableIncrementalCompilationForJsCheckBox;
|
||||
private JComboBox moduleKindComboBox;
|
||||
private JTextField scriptTemplatesField;
|
||||
private JTextField scriptTemplatesClasspathField;
|
||||
private JLabel scriptTemplatesLabel;
|
||||
private JLabel scriptTemplatesClasspathLabel;
|
||||
private JPanel k2jvmPanel;
|
||||
private JPanel k2jsPanel;
|
||||
private JComboBox jvmVersionComboBox;
|
||||
private JComboBox<VersionView> languageVersionComboBox;
|
||||
private JComboBox coroutineSupportComboBox;
|
||||
private JComboBox<VersionView> apiVersionComboBox;
|
||||
private JPanel scriptPanel;
|
||||
private JLabel labelForOutputPrefixFile;
|
||||
private JLabel labelForOutputPostfixFile;
|
||||
private JLabel warningLabel;
|
||||
private JTextField sourceMapPrefix;
|
||||
private JLabel labelForSourceMapPrefix;
|
||||
private JComboBox sourceMapEmbedSources;
|
||||
private JPanel coroutinesPanel;
|
||||
private ThreeStateCheckBox enableNewInferenceInIDECheckBox;
|
||||
private boolean isEnabled = true;
|
||||
|
||||
public KotlinCompilerConfigurableTab(
|
||||
Project project,
|
||||
@NotNull CommonCompilerArguments commonCompilerArguments,
|
||||
@NotNull K2JSCompilerArguments k2jsCompilerArguments,
|
||||
@NotNull K2JVMCompilerArguments k2jvmCompilerArguments, CompilerSettings compilerSettings,
|
||||
@Nullable KotlinCompilerWorkspaceSettings compilerWorkspaceSettings,
|
||||
boolean isProjectSettings,
|
||||
boolean isMultiEditor
|
||||
) {
|
||||
this.project = project;
|
||||
this.commonCompilerArguments = commonCompilerArguments;
|
||||
this.k2jsCompilerArguments = k2jsCompilerArguments;
|
||||
this.compilerSettings = compilerSettings;
|
||||
this.compilerWorkspaceSettings = compilerWorkspaceSettings;
|
||||
this.k2jvmCompilerArguments = k2jvmCompilerArguments;
|
||||
this.isProjectSettings = isProjectSettings;
|
||||
|
||||
warningLabel.setIcon(AllIcons.General.WarningDialog);
|
||||
|
||||
if (isProjectSettings) {
|
||||
languageVersionComboBox.addActionListener(e -> onLanguageLevelChanged(getSelectedLanguageVersionView()));
|
||||
}
|
||||
|
||||
additionalArgsOptionsField.attachLabel(additionalArgsLabel);
|
||||
|
||||
fillLanguageAndAPIVersionList();
|
||||
fillCoroutineSupportList();
|
||||
|
||||
if (CidrUtil.isRunningInCidrIde()) {
|
||||
keepAliveCheckBox.setVisible(false);
|
||||
k2jvmPanel.setVisible(false);
|
||||
k2jsPanel.setVisible(false);
|
||||
}
|
||||
else {
|
||||
initializeNonCidrSettings(isMultiEditor);
|
||||
}
|
||||
|
||||
reportWarningsCheckBox.setThirdStateEnabled(isMultiEditor);
|
||||
enableNewInferenceInIDECheckBox.setThirdStateEnabled(isMultiEditor);
|
||||
|
||||
if (isProjectSettings) {
|
||||
List<String> modulesOverridingProjectSettings = ArraysKt.mapNotNull(
|
||||
ModuleManager.getInstance(project).getModules(),
|
||||
module -> {
|
||||
KotlinFacet facet = KotlinFacet.Companion.get(module);
|
||||
if (facet == null) return null;
|
||||
KotlinFacetSettings facetSettings = facet.getConfiguration().getSettings();
|
||||
if (facetSettings.getUseProjectSettings()) return null;
|
||||
return module.getName();
|
||||
}
|
||||
);
|
||||
CollectionsKt.sort(modulesOverridingProjectSettings);
|
||||
if (!modulesOverridingProjectSettings.isEmpty()) {
|
||||
warningLabel.setVisible(true);
|
||||
warningLabel.setText(buildOverridingModulesWarning(modulesOverridingProjectSettings));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public KotlinCompilerConfigurableTab(Project project) {
|
||||
this(project,
|
||||
(CommonCompilerArguments) KotlinCommonCompilerArgumentsHolder.Companion.getInstance(project).getSettings().unfrozen(),
|
||||
(K2JSCompilerArguments) Kotlin2JsCompilerArgumentsHolder.Companion.getInstance(project).getSettings().unfrozen(),
|
||||
(K2JVMCompilerArguments) Kotlin2JvmCompilerArgumentsHolder.Companion.getInstance(project).getSettings().unfrozen(),
|
||||
(CompilerSettings) KotlinCompilerSettings.Companion.getInstance(project).getSettings().unfrozen(),
|
||||
KotlinCompilerWorkspaceSettings.getInstance(project),
|
||||
true,
|
||||
false);
|
||||
}
|
||||
|
||||
private void initializeNonCidrSettings(boolean isMultiEditor) {
|
||||
setupFileChooser(labelForOutputPrefixFile, outputPrefixFile,
|
||||
KotlinBundle.message("kotlin.compiler.js.option.output.prefix.browse.title"),
|
||||
true);
|
||||
setupFileChooser(labelForOutputPostfixFile, outputPostfixFile,
|
||||
KotlinBundle.message("kotlin.compiler.js.option.output.postfix.browse.title"),
|
||||
true);
|
||||
setupFileChooser(labelForOutputDirectory, outputDirectory,
|
||||
"Choose Output Directory",
|
||||
false);
|
||||
|
||||
fillModuleKindList();
|
||||
fillSourceMapSourceEmbeddingList();
|
||||
fillJvmVersionList();
|
||||
|
||||
generateSourceMapsCheckBox.setThirdStateEnabled(isMultiEditor);
|
||||
generateSourceMapsCheckBox.addActionListener(event -> sourceMapPrefix.setEnabled(generateSourceMapsCheckBox.isSelected()));
|
||||
|
||||
copyRuntimeFilesCheckBox.setThirdStateEnabled(isMultiEditor);
|
||||
keepAliveCheckBox.setThirdStateEnabled(isMultiEditor);
|
||||
|
||||
if (compilerWorkspaceSettings == null) {
|
||||
keepAliveCheckBox.setVisible(false);
|
||||
k2jvmPanel.setVisible(false);
|
||||
enableIncrementalCompilationForJsCheckBox.setVisible(false);
|
||||
enableNewInferenceInIDECheckBox.setVisible(false);
|
||||
}
|
||||
|
||||
updateOutputDirEnabled();
|
||||
}
|
||||
|
||||
private static int calculateNameCountToShowInWarning(List<String> allNames) {
|
||||
int lengthSoFar = 0;
|
||||
int size = allNames.size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
lengthSoFar = (i > 0 ? lengthSoFar + 2 : 0) + allNames.get(i).length();
|
||||
if (lengthSoFar > MAX_WARNING_SIZE) return i;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static String buildOverridingModulesWarning(List<String> modulesOverridingProjectSettings) {
|
||||
int nameCountToShow = calculateNameCountToShowInWarning(modulesOverridingProjectSettings);
|
||||
int allNamesCount = modulesOverridingProjectSettings.size();
|
||||
if (nameCountToShow == 0) {
|
||||
return String.valueOf(allNamesCount) + " modules override project settings";
|
||||
}
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("<html>Following modules override project settings: ");
|
||||
CollectionsKt.joinTo(
|
||||
modulesOverridingProjectSettings.subList(0, nameCountToShow),
|
||||
builder,
|
||||
", ",
|
||||
"",
|
||||
"",
|
||||
-1,
|
||||
"",
|
||||
new Function1<String, CharSequence>() {
|
||||
@Override
|
||||
public CharSequence invoke(String s) {
|
||||
return "<strong>" + s + "</strong>";
|
||||
}
|
||||
}
|
||||
);
|
||||
if (nameCountToShow < allNamesCount) {
|
||||
builder.append(" and ").append(allNamesCount - nameCountToShow).append(" other(s)");
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static String getModuleKindDescription(@Nullable String moduleKind) {
|
||||
if (moduleKind == null) return "";
|
||||
String result = moduleKindDescriptions.get(moduleKind);
|
||||
assert result != null : "Module kind " + moduleKind + " was not added to combobox, therefore it should not be here";
|
||||
return result;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static String getSourceMapSourceEmbeddingDescription(@Nullable String sourceMapSourceEmbeddingId) {
|
||||
if (sourceMapSourceEmbeddingId == null) return "";
|
||||
String result = soruceMapSourceEmbeddingDescriptions.get(sourceMapSourceEmbeddingId);
|
||||
assert result != null : "Source map source embedding mode " + sourceMapSourceEmbeddingId +
|
||||
" was not added to combobox, therefore it should not be here";
|
||||
return result;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static String getModuleKindOrDefault(@Nullable String moduleKindId) {
|
||||
if (moduleKindId == null) {
|
||||
moduleKindId = K2JsArgumentConstants.MODULE_PLAIN;
|
||||
}
|
||||
return moduleKindId;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static String getSourceMapSourceEmbeddingOrDefault(@Nullable String sourceMapSourceEmbeddingId) {
|
||||
if (sourceMapSourceEmbeddingId == null) {
|
||||
sourceMapSourceEmbeddingId = K2JsArgumentConstants.SOURCE_MAP_SOURCE_CONTENT_INLINING;
|
||||
}
|
||||
return sourceMapSourceEmbeddingId;
|
||||
}
|
||||
|
||||
private static String getJvmVersionOrDefault(@Nullable String jvmVersion) {
|
||||
return jvmVersion != null ? jvmVersion : JvmTarget.DEFAULT.getDescription();
|
||||
}
|
||||
|
||||
private static void setupFileChooser(
|
||||
@NotNull JLabel label,
|
||||
@NotNull TextFieldWithBrowseButton fileChooser,
|
||||
@NotNull String title,
|
||||
boolean forFiles
|
||||
) {
|
||||
label.setLabelFor(fileChooser);
|
||||
|
||||
fileChooser.addBrowseFolderListener(title, null, null,
|
||||
new FileChooserDescriptor(forFiles, !forFiles, false, false, false, false),
|
||||
TextComponentAccessor.TEXT_FIELD_WHOLE_TEXT);
|
||||
}
|
||||
|
||||
private static boolean isModifiedWithNullize(@NotNull TextFieldWithBrowseButton chooser, @Nullable String currentValue) {
|
||||
return !StringUtil.equals(
|
||||
StringUtil.nullize(chooser.getText(), true),
|
||||
StringUtil.nullize(currentValue, true));
|
||||
}
|
||||
|
||||
private static boolean isModified(@NotNull TextFieldWithBrowseButton chooser, @NotNull String currentValue) {
|
||||
return !StringUtil.equals(chooser.getText(), currentValue);
|
||||
}
|
||||
|
||||
private void updateOutputDirEnabled() {
|
||||
if (isEnabled && copyRuntimeFilesCheckBox != null) {
|
||||
outputDirectory.setEnabled(copyRuntimeFilesCheckBox.isSelected());
|
||||
labelForOutputDirectory.setEnabled(copyRuntimeFilesCheckBox.isSelected());
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isLessOrEqual(LanguageVersion version, LanguageVersion upperBound) {
|
||||
return VersionComparatorUtil.compare(version.getVersionString(), upperBound.getVersionString()) <= 0;
|
||||
}
|
||||
|
||||
public void onLanguageLevelChanged(VersionView languageLevel) {
|
||||
restrictAPIVersions(languageLevel);
|
||||
coroutinesPanel.setVisible(languageLevel.getVersion().compareTo(LanguageVersion.KOTLIN_1_3) < 0);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void restrictAPIVersions(VersionView upperBoundView) {
|
||||
VersionView selectedAPIView = getSelectedAPIVersionView();
|
||||
LanguageVersion selectedAPIVersion = selectedAPIView.getVersion();
|
||||
LanguageVersion upperBound = upperBoundView.getVersion();
|
||||
List<VersionView> permittedAPIVersions = new ArrayList<>(LanguageVersion.values().length + 1);
|
||||
if (isLessOrEqual(VersionView.LatestStable.INSTANCE.getVersion(), upperBound)) {
|
||||
permittedAPIVersions.add(VersionView.LatestStable.INSTANCE);
|
||||
}
|
||||
ArraysKt.mapNotNullTo(
|
||||
LanguageVersion.values(),
|
||||
permittedAPIVersions,
|
||||
version -> isLessOrEqual(version, upperBound) && !version.isUnsupported() ? new VersionView.Specific(version) : null
|
||||
);
|
||||
apiVersionComboBox.setModel(
|
||||
new DefaultComboBoxModel(permittedAPIVersions.toArray())
|
||||
);
|
||||
apiVersionComboBox.setSelectedItem(
|
||||
VersionComparatorUtil.compare(selectedAPIVersion.getVersionString(), upperBound.getVersionString()) <= 0
|
||||
? selectedAPIView
|
||||
: upperBoundView
|
||||
);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void fillJvmVersionList() {
|
||||
for (TargetPlatform jvm : JvmIdePlatformKind.INSTANCE.getPlatforms()) {
|
||||
jvmVersionComboBox.addItem(PlatformUtilKt.subplatformOfType(jvm, JdkPlatform.class).getTargetVersion().getDescription());
|
||||
}
|
||||
}
|
||||
|
||||
private void fillLanguageAndAPIVersionList() {
|
||||
languageVersionComboBox.addItem(VersionView.LatestStable.INSTANCE);
|
||||
apiVersionComboBox.addItem(VersionView.LatestStable.INSTANCE);
|
||||
|
||||
for (LanguageVersion version : LanguageVersion.values()) {
|
||||
if (version.isUnsupported() || !version.isStable() && !ApplicationManager.getApplication().isInternal()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
VersionView.Specific specificVersion = new VersionView.Specific(version);
|
||||
languageVersionComboBox.addItem(specificVersion);
|
||||
apiVersionComboBox.addItem(specificVersion);
|
||||
}
|
||||
languageVersionComboBox.setRenderer(new DescriptionListCellRenderer());
|
||||
apiVersionComboBox.setRenderer(new DescriptionListCellRenderer());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void fillCoroutineSupportList() {
|
||||
for (LanguageFeature.State coroutineSupport : languageFeatureStates) {
|
||||
coroutineSupportComboBox.addItem(coroutineSupport);
|
||||
}
|
||||
coroutineSupportComboBox.setRenderer(new DescriptionListCellRenderer());
|
||||
}
|
||||
|
||||
public void setTargetPlatform(@Nullable IdePlatformKind<?> targetPlatform) {
|
||||
k2jsPanel.setVisible(JsIdePlatformUtil.isJavaScript(targetPlatform));
|
||||
scriptPanel.setVisible(JvmIdePlatformUtil.isJvm(targetPlatform));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void fillModuleKindList() {
|
||||
for (String moduleKind : moduleKindDescriptions.keySet()) {
|
||||
moduleKindComboBox.addItem(moduleKind);
|
||||
}
|
||||
moduleKindComboBox.setRenderer(new ListCellRendererWrapper<String>() {
|
||||
@Override
|
||||
public void customize(JList list, String value, int index, boolean selected, boolean hasFocus) {
|
||||
setText(getModuleKindDescription(value));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void fillSourceMapSourceEmbeddingList() {
|
||||
for (String moduleKind : soruceMapSourceEmbeddingDescriptions.keySet()) {
|
||||
sourceMapEmbedSources.addItem(moduleKind);
|
||||
}
|
||||
sourceMapEmbedSources.setRenderer(new ListCellRendererWrapper<String>() {
|
||||
@Override
|
||||
public void customize(JList list, String value, int index, boolean selected, boolean hasFocus) {
|
||||
setText(value != null ? getSourceMapSourceEmbeddingDescription(value) : "");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getId() {
|
||||
return "project.kotlinCompiler";
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Runnable enableSearch(String option) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public JComponent createComponent() {
|
||||
return contentPane;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isModified() {
|
||||
return isModified(reportWarningsCheckBox, !commonCompilerArguments.getSuppressWarnings()) ||
|
||||
isModified(enableNewInferenceInIDECheckBox, NewInferenceForIDEAnalysisComponent.isEnabled(project)) ||
|
||||
!getSelectedLanguageVersionView().equals(KotlinFacetSettingsKt.getLanguageVersionView(commonCompilerArguments)) ||
|
||||
!getSelectedAPIVersionView().equals(KotlinFacetSettingsKt.getApiVersionView(commonCompilerArguments)) ||
|
||||
!getSelectedCoroutineState().equals(commonCompilerArguments.getCoroutinesState()) ||
|
||||
!additionalArgsOptionsField.getText().equals(compilerSettings.getAdditionalArguments()) ||
|
||||
isModified(scriptTemplatesField, compilerSettings.getScriptTemplates()) ||
|
||||
isModified(scriptTemplatesClasspathField, compilerSettings.getScriptTemplatesClasspath()) ||
|
||||
isModified(copyRuntimeFilesCheckBox, compilerSettings.getCopyJsLibraryFiles()) ||
|
||||
isModified(outputDirectory, compilerSettings.getOutputDirectoryForJsLibraryFiles()) ||
|
||||
|
||||
(compilerWorkspaceSettings != null &&
|
||||
(isModified(enableIncrementalCompilationForJvmCheckBox, compilerWorkspaceSettings.getPreciseIncrementalEnabled()) ||
|
||||
isModified(enableIncrementalCompilationForJsCheckBox, compilerWorkspaceSettings.getIncrementalCompilationForJsEnabled()) ||
|
||||
isModified(keepAliveCheckBox, compilerWorkspaceSettings.getEnableDaemon()))) ||
|
||||
|
||||
isModified(generateSourceMapsCheckBox, k2jsCompilerArguments.getSourceMap()) ||
|
||||
isModifiedWithNullize(outputPrefixFile, k2jsCompilerArguments.getOutputPrefix()) ||
|
||||
isModifiedWithNullize(outputPostfixFile, k2jsCompilerArguments.getOutputPostfix()) ||
|
||||
!getSelectedModuleKind().equals(getModuleKindOrDefault(k2jsCompilerArguments.getModuleKind())) ||
|
||||
isModified(sourceMapPrefix, StringUtil.notNullize(k2jsCompilerArguments.getSourceMapPrefix())) ||
|
||||
!getSelectedSourceMapSourceEmbedding().equals(
|
||||
getSourceMapSourceEmbeddingOrDefault(k2jsCompilerArguments.getSourceMapEmbedSources())) ||
|
||||
!getSelectedJvmVersion().equals(getJvmVersionOrDefault(k2jvmCompilerArguments.getJvmTarget()));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private String getSelectedModuleKind() {
|
||||
return getModuleKindOrDefault((String) moduleKindComboBox.getSelectedItem());
|
||||
}
|
||||
|
||||
private String getSelectedSourceMapSourceEmbedding() {
|
||||
return getSourceMapSourceEmbeddingOrDefault((String) sourceMapEmbedSources.getSelectedItem());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private String getSelectedJvmVersion() {
|
||||
return getJvmVersionOrDefault((String) jvmVersionComboBox.getSelectedItem());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public VersionView getSelectedLanguageVersionView() {
|
||||
Object item = languageVersionComboBox.getSelectedItem();
|
||||
return item != null ? (VersionView) item : VersionView.LatestStable.INSTANCE;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private VersionView getSelectedAPIVersionView() {
|
||||
Object item = apiVersionComboBox.getSelectedItem();
|
||||
return item != null ? (VersionView) item : VersionView.LatestStable.INSTANCE;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private String getSelectedCoroutineState() {
|
||||
if (getSelectedLanguageVersionView().getVersion().compareTo(LanguageVersion.KOTLIN_1_3) >= 0) {
|
||||
return CommonCompilerArguments.DEFAULT;
|
||||
}
|
||||
|
||||
LanguageFeature.State state = (LanguageFeature.State) coroutineSupportComboBox.getSelectedItem();
|
||||
if (state == null) return CommonCompilerArguments.DEFAULT;
|
||||
switch (state) {
|
||||
case ENABLED: return CommonCompilerArguments.ENABLE;
|
||||
case ENABLED_WITH_WARNING: return CommonCompilerArguments.WARN;
|
||||
case ENABLED_WITH_ERROR: return CommonCompilerArguments.ERROR;
|
||||
default: return CommonCompilerArguments.DEFAULT;
|
||||
}
|
||||
}
|
||||
|
||||
public void applyTo(
|
||||
CommonCompilerArguments commonCompilerArguments,
|
||||
K2JVMCompilerArguments k2jvmCompilerArguments,
|
||||
K2JSCompilerArguments k2jsCompilerArguments,
|
||||
CompilerSettings compilerSettings
|
||||
) throws ConfigurationException {
|
||||
if (isProjectSettings) {
|
||||
boolean shouldInvalidateCaches =
|
||||
!getSelectedLanguageVersionView().equals(KotlinFacetSettingsKt.getLanguageVersionView(commonCompilerArguments)) ||
|
||||
!getSelectedAPIVersionView().equals(KotlinFacetSettingsKt.getApiVersionView(commonCompilerArguments)) ||
|
||||
!getSelectedCoroutineState().equals(commonCompilerArguments.getCoroutinesState()) ||
|
||||
!additionalArgsOptionsField.getText().equals(compilerSettings.getAdditionalArguments()) ||
|
||||
enableNewInferenceInIDECheckBox.isSelected() != NewInferenceForIDEAnalysisComponent.isEnabled(project);
|
||||
|
||||
if (shouldInvalidateCaches) {
|
||||
ApplicationUtilsKt.runWriteAction(
|
||||
new Function0<Object>() {
|
||||
@Override
|
||||
public Object invoke() {
|
||||
RootUtilsKt.invalidateProjectRoots(project);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
commonCompilerArguments.setSuppressWarnings(!reportWarningsCheckBox.isSelected());
|
||||
NewInferenceForIDEAnalysisComponent.setEnabled(project, enableNewInferenceInIDECheckBox.isSelected());
|
||||
KotlinFacetSettingsKt.setLanguageVersionView(commonCompilerArguments, getSelectedLanguageVersionView());
|
||||
KotlinFacetSettingsKt.setApiVersionView(commonCompilerArguments, getSelectedAPIVersionView());
|
||||
|
||||
commonCompilerArguments.setCoroutinesState(getSelectedCoroutineState());
|
||||
|
||||
compilerSettings.setAdditionalArguments(additionalArgsOptionsField.getText());
|
||||
compilerSettings.setScriptTemplates(scriptTemplatesField.getText());
|
||||
compilerSettings.setScriptTemplatesClasspath(scriptTemplatesClasspathField.getText());
|
||||
compilerSettings.setCopyJsLibraryFiles(copyRuntimeFilesCheckBox.isSelected());
|
||||
compilerSettings.setOutputDirectoryForJsLibraryFiles(outputDirectory.getText());
|
||||
|
||||
if (compilerWorkspaceSettings != null) {
|
||||
compilerWorkspaceSettings.setPreciseIncrementalEnabled(enableIncrementalCompilationForJvmCheckBox.isSelected());
|
||||
compilerWorkspaceSettings.setIncrementalCompilationForJsEnabled(enableIncrementalCompilationForJsCheckBox.isSelected());
|
||||
|
||||
boolean oldEnableDaemon = compilerWorkspaceSettings.getEnableDaemon();
|
||||
compilerWorkspaceSettings.setEnableDaemon(keepAliveCheckBox.isSelected());
|
||||
if (keepAliveCheckBox.isSelected() != oldEnableDaemon) {
|
||||
PluginStartupComponent.getInstance().resetAliveFlag();
|
||||
}
|
||||
}
|
||||
|
||||
k2jsCompilerArguments.setSourceMap(generateSourceMapsCheckBox.isSelected());
|
||||
k2jsCompilerArguments.setOutputPrefix(StringUtil.nullize(outputPrefixFile.getText(), true));
|
||||
k2jsCompilerArguments.setOutputPostfix(StringUtil.nullize(outputPostfixFile.getText(), true));
|
||||
k2jsCompilerArguments.setModuleKind(getSelectedModuleKind());
|
||||
|
||||
k2jsCompilerArguments.setSourceMapPrefix(sourceMapPrefix.getText());
|
||||
k2jsCompilerArguments.setSourceMapEmbedSources(generateSourceMapsCheckBox.isSelected() ? getSelectedSourceMapSourceEmbedding() : null);
|
||||
|
||||
k2jvmCompilerArguments.setJvmTarget(getSelectedJvmVersion());
|
||||
|
||||
if (isProjectSettings) {
|
||||
KotlinCommonCompilerArgumentsHolder.Companion.getInstance(project).setSettings(commonCompilerArguments);
|
||||
Kotlin2JvmCompilerArgumentsHolder.Companion.getInstance(project).setSettings(k2jvmCompilerArguments);
|
||||
Kotlin2JsCompilerArgumentsHolder.Companion.getInstance(project).setSettings(k2jsCompilerArguments);
|
||||
KotlinCompilerSettings.Companion.getInstance(project).setSettings(compilerSettings);
|
||||
}
|
||||
|
||||
for (ClearBuildStateExtension extension : ClearBuildStateExtension.getExtensions()) {
|
||||
extension.clearState(project);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply() throws ConfigurationException {
|
||||
applyTo(commonCompilerArguments, k2jvmCompilerArguments, k2jsCompilerArguments, compilerSettings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
reportWarningsCheckBox.setSelected(!commonCompilerArguments.getSuppressWarnings());
|
||||
enableNewInferenceInIDECheckBox.setSelected(NewInferenceForIDEAnalysisComponent.isEnabled(project));
|
||||
languageVersionComboBox.setSelectedItem(KotlinFacetSettingsKt.getLanguageVersionView(commonCompilerArguments));
|
||||
onLanguageLevelChanged(getSelectedLanguageVersionView());
|
||||
apiVersionComboBox.setSelectedItem(KotlinFacetSettingsKt.getApiVersionView(commonCompilerArguments));
|
||||
coroutineSupportComboBox.setSelectedItem(CoroutineSupport.byCompilerArguments(commonCompilerArguments));
|
||||
additionalArgsOptionsField.setText(compilerSettings.getAdditionalArguments());
|
||||
scriptTemplatesField.setText(compilerSettings.getScriptTemplates());
|
||||
scriptTemplatesClasspathField.setText(compilerSettings.getScriptTemplatesClasspath());
|
||||
copyRuntimeFilesCheckBox.setSelected(compilerSettings.getCopyJsLibraryFiles());
|
||||
outputDirectory.setText(compilerSettings.getOutputDirectoryForJsLibraryFiles());
|
||||
|
||||
if (compilerWorkspaceSettings != null) {
|
||||
enableIncrementalCompilationForJvmCheckBox.setSelected(compilerWorkspaceSettings.getPreciseIncrementalEnabled());
|
||||
enableIncrementalCompilationForJsCheckBox.setSelected(compilerWorkspaceSettings.getIncrementalCompilationForJsEnabled());
|
||||
keepAliveCheckBox.setSelected(compilerWorkspaceSettings.getEnableDaemon());
|
||||
}
|
||||
|
||||
generateSourceMapsCheckBox.setSelected(k2jsCompilerArguments.getSourceMap());
|
||||
outputPrefixFile.setText(k2jsCompilerArguments.getOutputPrefix());
|
||||
outputPostfixFile.setText(k2jsCompilerArguments.getOutputPostfix());
|
||||
|
||||
moduleKindComboBox.setSelectedItem(getModuleKindOrDefault(k2jsCompilerArguments.getModuleKind()));
|
||||
sourceMapPrefix.setText(k2jsCompilerArguments.getSourceMapPrefix());
|
||||
sourceMapPrefix.setEnabled(k2jsCompilerArguments.getSourceMap());
|
||||
sourceMapEmbedSources.setSelectedItem(getSourceMapSourceEmbeddingOrDefault(k2jsCompilerArguments.getSourceMapEmbedSources()));
|
||||
|
||||
jvmVersionComboBox.setSelectedItem(getJvmVersionOrDefault(k2jvmCompilerArguments.getJvmTarget()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disposeUIResources() {
|
||||
}
|
||||
|
||||
@Nls
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return "Kotlin Compiler";
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getHelpTopic() {
|
||||
return "reference.compiler.kotlin";
|
||||
}
|
||||
|
||||
public JPanel getContentPane() {
|
||||
return contentPane;
|
||||
}
|
||||
|
||||
public ThreeStateCheckBox getReportWarningsCheckBox() {
|
||||
return reportWarningsCheckBox;
|
||||
}
|
||||
|
||||
public ThreeStateCheckBox getEnableNewInferenceInIDECheckBox() {
|
||||
return enableNewInferenceInIDECheckBox;
|
||||
}
|
||||
|
||||
public RawCommandLineEditor getAdditionalArgsOptionsField() {
|
||||
return additionalArgsOptionsField;
|
||||
}
|
||||
|
||||
public ThreeStateCheckBox getGenerateSourceMapsCheckBox() {
|
||||
return generateSourceMapsCheckBox;
|
||||
}
|
||||
|
||||
public TextFieldWithBrowseButton getOutputPrefixFile() {
|
||||
return outputPrefixFile;
|
||||
}
|
||||
|
||||
public TextFieldWithBrowseButton getOutputPostfixFile() {
|
||||
return outputPostfixFile;
|
||||
}
|
||||
|
||||
public TextFieldWithBrowseButton getOutputDirectory() {
|
||||
return outputDirectory;
|
||||
}
|
||||
|
||||
public ThreeStateCheckBox getCopyRuntimeFilesCheckBox() {
|
||||
return copyRuntimeFilesCheckBox;
|
||||
}
|
||||
|
||||
public ThreeStateCheckBox getKeepAliveCheckBox() {
|
||||
return keepAliveCheckBox;
|
||||
}
|
||||
|
||||
public JComboBox getModuleKindComboBox() {
|
||||
return moduleKindComboBox;
|
||||
}
|
||||
|
||||
public JTextField getScriptTemplatesField() {
|
||||
return scriptTemplatesField;
|
||||
}
|
||||
|
||||
public JTextField getScriptTemplatesClasspathField() {
|
||||
return scriptTemplatesClasspathField;
|
||||
}
|
||||
|
||||
public JComboBox getLanguageVersionComboBox() {
|
||||
return languageVersionComboBox;
|
||||
}
|
||||
|
||||
public JComboBox getApiVersionComboBox() {
|
||||
return apiVersionComboBox;
|
||||
}
|
||||
|
||||
public JComboBox getCoroutineSupportComboBox() {
|
||||
return coroutineSupportComboBox;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean value) {
|
||||
isEnabled = value;
|
||||
UIUtil.setEnabled(getContentPane(), value, true);
|
||||
}
|
||||
|
||||
public CommonCompilerArguments getCommonCompilerArguments() {
|
||||
return commonCompilerArguments;
|
||||
}
|
||||
|
||||
public void setCommonCompilerArguments(CommonCompilerArguments commonCompilerArguments) {
|
||||
this.commonCompilerArguments = commonCompilerArguments;
|
||||
}
|
||||
|
||||
public K2JSCompilerArguments getK2jsCompilerArguments() {
|
||||
return k2jsCompilerArguments;
|
||||
}
|
||||
|
||||
public void setK2jsCompilerArguments(K2JSCompilerArguments k2jsCompilerArguments) {
|
||||
this.k2jsCompilerArguments = k2jsCompilerArguments;
|
||||
}
|
||||
|
||||
public K2JVMCompilerArguments getK2jvmCompilerArguments() {
|
||||
return k2jvmCompilerArguments;
|
||||
}
|
||||
|
||||
public void setK2jvmCompilerArguments(K2JVMCompilerArguments k2jvmCompilerArguments) {
|
||||
this.k2jvmCompilerArguments = k2jvmCompilerArguments;
|
||||
}
|
||||
|
||||
public CompilerSettings getCompilerSettings() {
|
||||
return compilerSettings;
|
||||
}
|
||||
|
||||
public void setCompilerSettings(CompilerSettings compilerSettings) {
|
||||
this.compilerSettings = compilerSettings;
|
||||
}
|
||||
|
||||
private void createUIComponents() {
|
||||
// Workaround: ThreeStateCheckBox doesn't send suitable notification on state change
|
||||
// TODO: replace with PropertyChangerListener after fix is available in IDEA
|
||||
copyRuntimeFilesCheckBox = new ThreeStateCheckBox() {
|
||||
@Override
|
||||
public void setState(State state) {
|
||||
super.setState(state);
|
||||
updateOutputDirEnabled();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
+751
@@ -0,0 +1,751 @@
|
||||
/*
|
||||
* Copyright 2000-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.compiler.configuration;
|
||||
|
||||
import com.intellij.icons.AllIcons;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
|
||||
import com.intellij.openapi.module.ModuleManager;
|
||||
import com.intellij.openapi.options.Configurable;
|
||||
import com.intellij.openapi.options.ConfigurationException;
|
||||
import com.intellij.openapi.options.SearchableConfigurable;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.TextComponentAccessor;
|
||||
import com.intellij.openapi.ui.TextFieldWithBrowseButton;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.ui.ListCellRendererWrapper;
|
||||
import com.intellij.ui.RawCommandLineEditor;
|
||||
import com.intellij.util.text.VersionComparatorUtil;
|
||||
import com.intellij.util.ui.ThreeStateCheckBox;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import kotlin.collections.ArraysKt;
|
||||
import kotlin.collections.CollectionsKt;
|
||||
import kotlin.jvm.functions.Function0;
|
||||
import kotlin.jvm.functions.Function1;
|
||||
import org.jetbrains.annotations.Nls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments;
|
||||
import org.jetbrains.kotlin.cli.common.arguments.K2JSCompilerArguments;
|
||||
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments;
|
||||
import org.jetbrains.kotlin.cli.common.arguments.K2JsArgumentConstants;
|
||||
import org.jetbrains.kotlin.config.*;
|
||||
import org.jetbrains.kotlin.idea.KotlinBundle;
|
||||
import org.jetbrains.kotlin.idea.PluginStartupComponent;
|
||||
import org.jetbrains.kotlin.idea.facet.DescriptionListCellRenderer;
|
||||
import org.jetbrains.kotlin.idea.facet.KotlinFacet;
|
||||
import org.jetbrains.kotlin.idea.project.NewInferenceForIDEAnalysisComponent;
|
||||
import org.jetbrains.kotlin.idea.roots.RootUtilsKt;
|
||||
import org.jetbrains.kotlin.idea.util.CidrUtil;
|
||||
import org.jetbrains.kotlin.idea.util.application.ApplicationUtilsKt;
|
||||
import org.jetbrains.kotlin.platform.IdePlatformKind;
|
||||
import org.jetbrains.kotlin.platform.PlatformUtilKt;
|
||||
import org.jetbrains.kotlin.platform.impl.JsIdePlatformUtil;
|
||||
import org.jetbrains.kotlin.platform.impl.JvmIdePlatformKind;
|
||||
import org.jetbrains.kotlin.platform.impl.JvmIdePlatformUtil;
|
||||
import org.jetbrains.kotlin.config.JvmTarget;
|
||||
import org.jetbrains.kotlin.platform.jvm.JdkPlatform;
|
||||
import org.jetbrains.kotlin.platform.TargetPlatform;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.util.*;
|
||||
|
||||
public class KotlinCompilerConfigurableTab implements SearchableConfigurable {
|
||||
private static final Map<String, String> moduleKindDescriptions = new LinkedHashMap<>();
|
||||
private static final Map<String, String> soruceMapSourceEmbeddingDescriptions = new LinkedHashMap<>();
|
||||
private static final List<LanguageFeature.State> languageFeatureStates = Arrays.asList(
|
||||
LanguageFeature.State.ENABLED, LanguageFeature.State.ENABLED_WITH_WARNING, LanguageFeature.State.ENABLED_WITH_ERROR
|
||||
);
|
||||
private static final int MAX_WARNING_SIZE = 75;
|
||||
|
||||
static {
|
||||
moduleKindDescriptions.put(K2JsArgumentConstants.MODULE_PLAIN, "Plain (put to global scope)");
|
||||
moduleKindDescriptions.put(K2JsArgumentConstants.MODULE_AMD, "AMD");
|
||||
moduleKindDescriptions.put(K2JsArgumentConstants.MODULE_COMMONJS, "CommonJS");
|
||||
moduleKindDescriptions.put(K2JsArgumentConstants.MODULE_UMD, "UMD (detect AMD or CommonJS if available, fallback to plain)");
|
||||
|
||||
soruceMapSourceEmbeddingDescriptions.put(K2JsArgumentConstants.SOURCE_MAP_SOURCE_CONTENT_NEVER, "Never");
|
||||
soruceMapSourceEmbeddingDescriptions.put(K2JsArgumentConstants.SOURCE_MAP_SOURCE_CONTENT_ALWAYS, "Always");
|
||||
soruceMapSourceEmbeddingDescriptions.put(K2JsArgumentConstants.SOURCE_MAP_SOURCE_CONTENT_INLINING,
|
||||
"When inlining a function from other module with embedded sources");
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private final KotlinCompilerWorkspaceSettings compilerWorkspaceSettings;
|
||||
private final Project project;
|
||||
private final boolean isProjectSettings;
|
||||
private CommonCompilerArguments commonCompilerArguments;
|
||||
private K2JSCompilerArguments k2jsCompilerArguments;
|
||||
private K2JVMCompilerArguments k2jvmCompilerArguments;
|
||||
private CompilerSettings compilerSettings;
|
||||
private JPanel contentPane;
|
||||
private ThreeStateCheckBox reportWarningsCheckBox;
|
||||
private RawCommandLineEditor additionalArgsOptionsField;
|
||||
private JLabel additionalArgsLabel;
|
||||
private ThreeStateCheckBox generateSourceMapsCheckBox;
|
||||
private TextFieldWithBrowseButton outputPrefixFile;
|
||||
private TextFieldWithBrowseButton outputPostfixFile;
|
||||
private JLabel labelForOutputDirectory;
|
||||
private TextFieldWithBrowseButton outputDirectory;
|
||||
private ThreeStateCheckBox copyRuntimeFilesCheckBox;
|
||||
private ThreeStateCheckBox keepAliveCheckBox;
|
||||
private JCheckBox enableIncrementalCompilationForJvmCheckBox;
|
||||
private JCheckBox enableIncrementalCompilationForJsCheckBox;
|
||||
private JComboBox moduleKindComboBox;
|
||||
private JTextField scriptTemplatesField;
|
||||
private JTextField scriptTemplatesClasspathField;
|
||||
private JLabel scriptTemplatesLabel;
|
||||
private JLabel scriptTemplatesClasspathLabel;
|
||||
private JPanel k2jvmPanel;
|
||||
private JPanel k2jsPanel;
|
||||
private JComboBox jvmVersionComboBox;
|
||||
private JComboBox<VersionView> languageVersionComboBox;
|
||||
private JComboBox coroutineSupportComboBox;
|
||||
private JComboBox<VersionView> apiVersionComboBox;
|
||||
private JPanel scriptPanel;
|
||||
private JLabel labelForOutputPrefixFile;
|
||||
private JLabel labelForOutputPostfixFile;
|
||||
private JLabel warningLabel;
|
||||
private JTextField sourceMapPrefix;
|
||||
private JLabel labelForSourceMapPrefix;
|
||||
private JComboBox sourceMapEmbedSources;
|
||||
private JPanel coroutinesPanel;
|
||||
private ThreeStateCheckBox enableNewInferenceInIDECheckBox;
|
||||
private boolean isEnabled = true;
|
||||
|
||||
public KotlinCompilerConfigurableTab(
|
||||
Project project,
|
||||
@NotNull CommonCompilerArguments commonCompilerArguments,
|
||||
@NotNull K2JSCompilerArguments k2jsCompilerArguments,
|
||||
@NotNull K2JVMCompilerArguments k2jvmCompilerArguments, CompilerSettings compilerSettings,
|
||||
@Nullable KotlinCompilerWorkspaceSettings compilerWorkspaceSettings,
|
||||
boolean isProjectSettings,
|
||||
boolean isMultiEditor
|
||||
) {
|
||||
this.project = project;
|
||||
this.commonCompilerArguments = commonCompilerArguments;
|
||||
this.k2jsCompilerArguments = k2jsCompilerArguments;
|
||||
this.compilerSettings = compilerSettings;
|
||||
this.compilerWorkspaceSettings = compilerWorkspaceSettings;
|
||||
this.k2jvmCompilerArguments = k2jvmCompilerArguments;
|
||||
this.isProjectSettings = isProjectSettings;
|
||||
|
||||
warningLabel.setIcon(AllIcons.General.WarningDialog);
|
||||
|
||||
if (isProjectSettings) {
|
||||
languageVersionComboBox.addActionListener(e -> onLanguageLevelChanged(getSelectedLanguageVersionView()));
|
||||
}
|
||||
|
||||
additionalArgsOptionsField.attachLabel(additionalArgsLabel);
|
||||
|
||||
fillLanguageAndAPIVersionList();
|
||||
fillCoroutineSupportList();
|
||||
|
||||
if (CidrUtil.isRunningInCidrIde()) {
|
||||
keepAliveCheckBox.setVisible(false);
|
||||
k2jvmPanel.setVisible(false);
|
||||
k2jsPanel.setVisible(false);
|
||||
}
|
||||
else {
|
||||
initializeNonCidrSettings(isMultiEditor);
|
||||
}
|
||||
|
||||
reportWarningsCheckBox.setThirdStateEnabled(isMultiEditor);
|
||||
enableNewInferenceInIDECheckBox.setThirdStateEnabled(isMultiEditor);
|
||||
|
||||
if (isProjectSettings) {
|
||||
List<String> modulesOverridingProjectSettings = ArraysKt.mapNotNull(
|
||||
ModuleManager.getInstance(project).getModules(),
|
||||
module -> {
|
||||
KotlinFacet facet = KotlinFacet.Companion.get(module);
|
||||
if (facet == null) return null;
|
||||
KotlinFacetSettings facetSettings = facet.getConfiguration().getSettings();
|
||||
if (facetSettings.getUseProjectSettings()) return null;
|
||||
return module.getName();
|
||||
}
|
||||
);
|
||||
CollectionsKt.sort(modulesOverridingProjectSettings);
|
||||
if (!modulesOverridingProjectSettings.isEmpty()) {
|
||||
warningLabel.setVisible(true);
|
||||
warningLabel.setText(buildOverridingModulesWarning(modulesOverridingProjectSettings));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public KotlinCompilerConfigurableTab(Project project) {
|
||||
this(project,
|
||||
(CommonCompilerArguments) KotlinCommonCompilerArgumentsHolder.Companion.getInstance(project).getSettings().unfrozen(),
|
||||
(K2JSCompilerArguments) Kotlin2JsCompilerArgumentsHolder.Companion.getInstance(project).getSettings().unfrozen(),
|
||||
(K2JVMCompilerArguments) Kotlin2JvmCompilerArgumentsHolder.Companion.getInstance(project).getSettings().unfrozen(),
|
||||
(CompilerSettings) KotlinCompilerSettings.Companion.getInstance(project).getSettings().unfrozen(),
|
||||
KotlinCompilerWorkspaceSettings.getInstance(project),
|
||||
true,
|
||||
false);
|
||||
}
|
||||
|
||||
private void initializeNonCidrSettings(boolean isMultiEditor) {
|
||||
setupFileChooser(labelForOutputPrefixFile, outputPrefixFile,
|
||||
KotlinBundle.message("kotlin.compiler.js.option.output.prefix.browse.title"),
|
||||
true);
|
||||
setupFileChooser(labelForOutputPostfixFile, outputPostfixFile,
|
||||
KotlinBundle.message("kotlin.compiler.js.option.output.postfix.browse.title"),
|
||||
true);
|
||||
setupFileChooser(labelForOutputDirectory, outputDirectory,
|
||||
"Choose Output Directory",
|
||||
false);
|
||||
|
||||
fillModuleKindList();
|
||||
fillSourceMapSourceEmbeddingList();
|
||||
fillJvmVersionList();
|
||||
|
||||
generateSourceMapsCheckBox.setThirdStateEnabled(isMultiEditor);
|
||||
generateSourceMapsCheckBox.addActionListener(event -> sourceMapPrefix.setEnabled(generateSourceMapsCheckBox.isSelected()));
|
||||
|
||||
copyRuntimeFilesCheckBox.setThirdStateEnabled(isMultiEditor);
|
||||
keepAliveCheckBox.setThirdStateEnabled(isMultiEditor);
|
||||
|
||||
if (compilerWorkspaceSettings == null) {
|
||||
keepAliveCheckBox.setVisible(false);
|
||||
k2jvmPanel.setVisible(false);
|
||||
enableIncrementalCompilationForJsCheckBox.setVisible(false);
|
||||
enableNewInferenceInIDECheckBox.setVisible(false);
|
||||
}
|
||||
|
||||
updateOutputDirEnabled();
|
||||
}
|
||||
|
||||
private static int calculateNameCountToShowInWarning(List<String> allNames) {
|
||||
int lengthSoFar = 0;
|
||||
int size = allNames.size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
lengthSoFar = (i > 0 ? lengthSoFar + 2 : 0) + allNames.get(i).length();
|
||||
if (lengthSoFar > MAX_WARNING_SIZE) return i;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static String buildOverridingModulesWarning(List<String> modulesOverridingProjectSettings) {
|
||||
int nameCountToShow = calculateNameCountToShowInWarning(modulesOverridingProjectSettings);
|
||||
int allNamesCount = modulesOverridingProjectSettings.size();
|
||||
if (nameCountToShow == 0) {
|
||||
return String.valueOf(allNamesCount) + " modules override project settings";
|
||||
}
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("<html>Following modules override project settings: ");
|
||||
CollectionsKt.joinTo(
|
||||
modulesOverridingProjectSettings.subList(0, nameCountToShow),
|
||||
builder,
|
||||
", ",
|
||||
"",
|
||||
"",
|
||||
-1,
|
||||
"",
|
||||
new Function1<String, CharSequence>() {
|
||||
@Override
|
||||
public CharSequence invoke(String s) {
|
||||
return "<strong>" + s + "</strong>";
|
||||
}
|
||||
}
|
||||
);
|
||||
if (nameCountToShow < allNamesCount) {
|
||||
builder.append(" and ").append(allNamesCount - nameCountToShow).append(" other(s)");
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static String getModuleKindDescription(@Nullable String moduleKind) {
|
||||
if (moduleKind == null) return "";
|
||||
String result = moduleKindDescriptions.get(moduleKind);
|
||||
assert result != null : "Module kind " + moduleKind + " was not added to combobox, therefore it should not be here";
|
||||
return result;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static String getSourceMapSourceEmbeddingDescription(@Nullable String sourceMapSourceEmbeddingId) {
|
||||
if (sourceMapSourceEmbeddingId == null) return "";
|
||||
String result = soruceMapSourceEmbeddingDescriptions.get(sourceMapSourceEmbeddingId);
|
||||
assert result != null : "Source map source embedding mode " + sourceMapSourceEmbeddingId +
|
||||
" was not added to combobox, therefore it should not be here";
|
||||
return result;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static String getModuleKindOrDefault(@Nullable String moduleKindId) {
|
||||
if (moduleKindId == null) {
|
||||
moduleKindId = K2JsArgumentConstants.MODULE_PLAIN;
|
||||
}
|
||||
return moduleKindId;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static String getSourceMapSourceEmbeddingOrDefault(@Nullable String sourceMapSourceEmbeddingId) {
|
||||
if (sourceMapSourceEmbeddingId == null) {
|
||||
sourceMapSourceEmbeddingId = K2JsArgumentConstants.SOURCE_MAP_SOURCE_CONTENT_INLINING;
|
||||
}
|
||||
return sourceMapSourceEmbeddingId;
|
||||
}
|
||||
|
||||
private static String getJvmVersionOrDefault(@Nullable String jvmVersion) {
|
||||
return jvmVersion != null ? jvmVersion : JvmTarget.DEFAULT.getDescription();
|
||||
}
|
||||
|
||||
private static void setupFileChooser(
|
||||
@NotNull JLabel label,
|
||||
@NotNull TextFieldWithBrowseButton fileChooser,
|
||||
@NotNull String title,
|
||||
boolean forFiles
|
||||
) {
|
||||
label.setLabelFor(fileChooser);
|
||||
|
||||
fileChooser.addBrowseFolderListener(title, null, null,
|
||||
new FileChooserDescriptor(forFiles, !forFiles, false, false, false, false),
|
||||
TextComponentAccessor.TEXT_FIELD_WHOLE_TEXT);
|
||||
}
|
||||
|
||||
private static boolean isModifiedWithNullize(@NotNull TextFieldWithBrowseButton chooser, @Nullable String currentValue) {
|
||||
return !StringUtil.equals(
|
||||
StringUtil.nullize(chooser.getText(), true),
|
||||
StringUtil.nullize(currentValue, true));
|
||||
}
|
||||
|
||||
private static boolean isModified(@NotNull TextFieldWithBrowseButton chooser, @NotNull String currentValue) {
|
||||
return !StringUtil.equals(chooser.getText(), currentValue);
|
||||
}
|
||||
|
||||
private void updateOutputDirEnabled() {
|
||||
if (isEnabled && copyRuntimeFilesCheckBox != null) {
|
||||
outputDirectory.setEnabled(copyRuntimeFilesCheckBox.isSelected());
|
||||
labelForOutputDirectory.setEnabled(copyRuntimeFilesCheckBox.isSelected());
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isLessOrEqual(LanguageVersion version, LanguageVersion upperBound) {
|
||||
return VersionComparatorUtil.compare(version.getVersionString(), upperBound.getVersionString()) <= 0;
|
||||
}
|
||||
|
||||
public void onLanguageLevelChanged(VersionView languageLevel) {
|
||||
restrictAPIVersions(languageLevel);
|
||||
coroutinesPanel.setVisible(languageLevel.getVersion().compareTo(LanguageVersion.KOTLIN_1_3) < 0);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void restrictAPIVersions(VersionView upperBoundView) {
|
||||
VersionView selectedAPIView = getSelectedAPIVersionView();
|
||||
LanguageVersion selectedAPIVersion = selectedAPIView.getVersion();
|
||||
LanguageVersion upperBound = upperBoundView.getVersion();
|
||||
List<VersionView> permittedAPIVersions = new ArrayList<>(LanguageVersion.values().length + 1);
|
||||
if (isLessOrEqual(VersionView.LatestStable.INSTANCE.getVersion(), upperBound)) {
|
||||
permittedAPIVersions.add(VersionView.LatestStable.INSTANCE);
|
||||
}
|
||||
ArraysKt.mapNotNullTo(
|
||||
LanguageVersion.values(),
|
||||
permittedAPIVersions,
|
||||
version -> isLessOrEqual(version, upperBound) && !version.isUnsupported() ? new VersionView.Specific(version) : null
|
||||
);
|
||||
apiVersionComboBox.setModel(
|
||||
new DefaultComboBoxModel(permittedAPIVersions.toArray())
|
||||
);
|
||||
apiVersionComboBox.setSelectedItem(
|
||||
VersionComparatorUtil.compare(selectedAPIVersion.getVersionString(), upperBound.getVersionString()) <= 0
|
||||
? selectedAPIView
|
||||
: upperBoundView
|
||||
);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void fillJvmVersionList() {
|
||||
for (TargetPlatform jvm : JvmIdePlatformKind.INSTANCE.getPlatforms()) {
|
||||
jvmVersionComboBox.addItem(PlatformUtilKt.subplatformOfType(jvm, JdkPlatform.class).getTargetVersion().getDescription());
|
||||
}
|
||||
}
|
||||
|
||||
private void fillLanguageAndAPIVersionList() {
|
||||
languageVersionComboBox.addItem(VersionView.LatestStable.INSTANCE);
|
||||
apiVersionComboBox.addItem(VersionView.LatestStable.INSTANCE);
|
||||
|
||||
for (LanguageVersion version : LanguageVersion.values()) {
|
||||
if (version.isUnsupported() || !version.isStable() && !ApplicationManager.getApplication().isInternal()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
VersionView.Specific specificVersion = new VersionView.Specific(version);
|
||||
languageVersionComboBox.addItem(specificVersion);
|
||||
apiVersionComboBox.addItem(specificVersion);
|
||||
}
|
||||
languageVersionComboBox.setRenderer(new DescriptionListCellRenderer());
|
||||
apiVersionComboBox.setRenderer(new DescriptionListCellRenderer());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void fillCoroutineSupportList() {
|
||||
for (LanguageFeature.State coroutineSupport : languageFeatureStates) {
|
||||
coroutineSupportComboBox.addItem(coroutineSupport);
|
||||
}
|
||||
coroutineSupportComboBox.setRenderer(new DescriptionListCellRenderer());
|
||||
}
|
||||
|
||||
public void setTargetPlatform(@Nullable IdePlatformKind<?> targetPlatform) {
|
||||
k2jsPanel.setVisible(JsIdePlatformUtil.isJavaScript(targetPlatform));
|
||||
scriptPanel.setVisible(JvmIdePlatformUtil.isJvm(targetPlatform));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void fillModuleKindList() {
|
||||
for (String moduleKind : moduleKindDescriptions.keySet()) {
|
||||
moduleKindComboBox.addItem(moduleKind);
|
||||
}
|
||||
moduleKindComboBox.setRenderer(new ListCellRendererWrapper<String>() {
|
||||
@Override
|
||||
public void customize(JList list, String value, int index, boolean selected, boolean hasFocus) {
|
||||
setText(getModuleKindDescription(value));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void fillSourceMapSourceEmbeddingList() {
|
||||
for (String moduleKind : soruceMapSourceEmbeddingDescriptions.keySet()) {
|
||||
sourceMapEmbedSources.addItem(moduleKind);
|
||||
}
|
||||
sourceMapEmbedSources.setRenderer(new ListCellRendererWrapper<String>() {
|
||||
@Override
|
||||
public void customize(JList list, String value, int index, boolean selected, boolean hasFocus) {
|
||||
setText(value != null ? getSourceMapSourceEmbeddingDescription(value) : "");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getId() {
|
||||
return "project.kotlinCompiler";
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Runnable enableSearch(String option) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public JComponent createComponent() {
|
||||
return contentPane;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isModified() {
|
||||
return isModified(reportWarningsCheckBox, !commonCompilerArguments.getSuppressWarnings()) ||
|
||||
isModified(enableNewInferenceInIDECheckBox, NewInferenceForIDEAnalysisComponent.isEnabled(project)) ||
|
||||
!getSelectedLanguageVersionView().equals(KotlinFacetSettingsKt.getLanguageVersionView(commonCompilerArguments)) ||
|
||||
!getSelectedAPIVersionView().equals(KotlinFacetSettingsKt.getApiVersionView(commonCompilerArguments)) ||
|
||||
!getSelectedCoroutineState().equals(commonCompilerArguments.getCoroutinesState()) ||
|
||||
!additionalArgsOptionsField.getText().equals(compilerSettings.getAdditionalArguments()) ||
|
||||
isModified(scriptTemplatesField, compilerSettings.getScriptTemplates()) ||
|
||||
isModified(scriptTemplatesClasspathField, compilerSettings.getScriptTemplatesClasspath()) ||
|
||||
isModified(copyRuntimeFilesCheckBox, compilerSettings.getCopyJsLibraryFiles()) ||
|
||||
isModified(outputDirectory, compilerSettings.getOutputDirectoryForJsLibraryFiles()) ||
|
||||
|
||||
(compilerWorkspaceSettings != null &&
|
||||
(isModified(enableIncrementalCompilationForJvmCheckBox, compilerWorkspaceSettings.getPreciseIncrementalEnabled()) ||
|
||||
isModified(enableIncrementalCompilationForJsCheckBox, compilerWorkspaceSettings.getIncrementalCompilationForJsEnabled()) ||
|
||||
isModified(keepAliveCheckBox, compilerWorkspaceSettings.getEnableDaemon()))) ||
|
||||
|
||||
isModified(generateSourceMapsCheckBox, k2jsCompilerArguments.getSourceMap()) ||
|
||||
isModifiedWithNullize(outputPrefixFile, k2jsCompilerArguments.getOutputPrefix()) ||
|
||||
isModifiedWithNullize(outputPostfixFile, k2jsCompilerArguments.getOutputPostfix()) ||
|
||||
!getSelectedModuleKind().equals(getModuleKindOrDefault(k2jsCompilerArguments.getModuleKind())) ||
|
||||
isModified(sourceMapPrefix, StringUtil.notNullize(k2jsCompilerArguments.getSourceMapPrefix())) ||
|
||||
!getSelectedSourceMapSourceEmbedding().equals(
|
||||
getSourceMapSourceEmbeddingOrDefault(k2jsCompilerArguments.getSourceMapEmbedSources())) ||
|
||||
!getSelectedJvmVersion().equals(getJvmVersionOrDefault(k2jvmCompilerArguments.getJvmTarget()));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private String getSelectedModuleKind() {
|
||||
return getModuleKindOrDefault((String) moduleKindComboBox.getSelectedItem());
|
||||
}
|
||||
|
||||
private String getSelectedSourceMapSourceEmbedding() {
|
||||
return getSourceMapSourceEmbeddingOrDefault((String) sourceMapEmbedSources.getSelectedItem());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private String getSelectedJvmVersion() {
|
||||
return getJvmVersionOrDefault((String) jvmVersionComboBox.getSelectedItem());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public VersionView getSelectedLanguageVersionView() {
|
||||
Object item = languageVersionComboBox.getSelectedItem();
|
||||
return item != null ? (VersionView) item : VersionView.LatestStable.INSTANCE;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private VersionView getSelectedAPIVersionView() {
|
||||
Object item = apiVersionComboBox.getSelectedItem();
|
||||
return item != null ? (VersionView) item : VersionView.LatestStable.INSTANCE;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private String getSelectedCoroutineState() {
|
||||
if (getSelectedLanguageVersionView().getVersion().compareTo(LanguageVersion.KOTLIN_1_3) >= 0) {
|
||||
return CommonCompilerArguments.DEFAULT;
|
||||
}
|
||||
|
||||
LanguageFeature.State state = (LanguageFeature.State) coroutineSupportComboBox.getSelectedItem();
|
||||
if (state == null) return CommonCompilerArguments.DEFAULT;
|
||||
switch (state) {
|
||||
case ENABLED: return CommonCompilerArguments.ENABLE;
|
||||
case ENABLED_WITH_WARNING: return CommonCompilerArguments.WARN;
|
||||
case ENABLED_WITH_ERROR: return CommonCompilerArguments.ERROR;
|
||||
default: return CommonCompilerArguments.DEFAULT;
|
||||
}
|
||||
}
|
||||
|
||||
public void applyTo(
|
||||
CommonCompilerArguments commonCompilerArguments,
|
||||
K2JVMCompilerArguments k2jvmCompilerArguments,
|
||||
K2JSCompilerArguments k2jsCompilerArguments,
|
||||
CompilerSettings compilerSettings
|
||||
) throws ConfigurationException {
|
||||
if (isProjectSettings) {
|
||||
boolean shouldInvalidateCaches =
|
||||
!getSelectedLanguageVersionView().equals(KotlinFacetSettingsKt.getLanguageVersionView(commonCompilerArguments)) ||
|
||||
!getSelectedAPIVersionView().equals(KotlinFacetSettingsKt.getApiVersionView(commonCompilerArguments)) ||
|
||||
!getSelectedCoroutineState().equals(commonCompilerArguments.getCoroutinesState()) ||
|
||||
!additionalArgsOptionsField.getText().equals(compilerSettings.getAdditionalArguments()) ||
|
||||
enableNewInferenceInIDECheckBox.isSelected() != NewInferenceForIDEAnalysisComponent.isEnabled(project);
|
||||
|
||||
if (shouldInvalidateCaches) {
|
||||
ApplicationUtilsKt.runWriteAction(
|
||||
new Function0<Object>() {
|
||||
@Override
|
||||
public Object invoke() {
|
||||
RootUtilsKt.invalidateProjectRoots(project);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
commonCompilerArguments.setSuppressWarnings(!reportWarningsCheckBox.isSelected());
|
||||
NewInferenceForIDEAnalysisComponent.setEnabled(project, enableNewInferenceInIDECheckBox.isSelected());
|
||||
KotlinFacetSettingsKt.setLanguageVersionView(commonCompilerArguments, getSelectedLanguageVersionView());
|
||||
KotlinFacetSettingsKt.setApiVersionView(commonCompilerArguments, getSelectedAPIVersionView());
|
||||
|
||||
commonCompilerArguments.setCoroutinesState(getSelectedCoroutineState());
|
||||
|
||||
compilerSettings.setAdditionalArguments(additionalArgsOptionsField.getText());
|
||||
compilerSettings.setScriptTemplates(scriptTemplatesField.getText());
|
||||
compilerSettings.setScriptTemplatesClasspath(scriptTemplatesClasspathField.getText());
|
||||
compilerSettings.setCopyJsLibraryFiles(copyRuntimeFilesCheckBox.isSelected());
|
||||
compilerSettings.setOutputDirectoryForJsLibraryFiles(outputDirectory.getText());
|
||||
|
||||
if (compilerWorkspaceSettings != null) {
|
||||
compilerWorkspaceSettings.setPreciseIncrementalEnabled(enableIncrementalCompilationForJvmCheckBox.isSelected());
|
||||
compilerWorkspaceSettings.setIncrementalCompilationForJsEnabled(enableIncrementalCompilationForJsCheckBox.isSelected());
|
||||
|
||||
boolean oldEnableDaemon = compilerWorkspaceSettings.getEnableDaemon();
|
||||
compilerWorkspaceSettings.setEnableDaemon(keepAliveCheckBox.isSelected());
|
||||
if (keepAliveCheckBox.isSelected() != oldEnableDaemon) {
|
||||
PluginStartupComponent.getInstance().resetAliveFlag();
|
||||
}
|
||||
}
|
||||
|
||||
k2jsCompilerArguments.setSourceMap(generateSourceMapsCheckBox.isSelected());
|
||||
k2jsCompilerArguments.setOutputPrefix(StringUtil.nullize(outputPrefixFile.getText(), true));
|
||||
k2jsCompilerArguments.setOutputPostfix(StringUtil.nullize(outputPostfixFile.getText(), true));
|
||||
k2jsCompilerArguments.setModuleKind(getSelectedModuleKind());
|
||||
|
||||
k2jsCompilerArguments.setSourceMapPrefix(sourceMapPrefix.getText());
|
||||
k2jsCompilerArguments.setSourceMapEmbedSources(generateSourceMapsCheckBox.isSelected() ? getSelectedSourceMapSourceEmbedding() : null);
|
||||
|
||||
k2jvmCompilerArguments.setJvmTarget(getSelectedJvmVersion());
|
||||
|
||||
if (isProjectSettings) {
|
||||
KotlinCommonCompilerArgumentsHolder.Companion.getInstance(project).setSettings(commonCompilerArguments);
|
||||
Kotlin2JvmCompilerArgumentsHolder.Companion.getInstance(project).setSettings(k2jvmCompilerArguments);
|
||||
Kotlin2JsCompilerArgumentsHolder.Companion.getInstance(project).setSettings(k2jsCompilerArguments);
|
||||
KotlinCompilerSettings.Companion.getInstance(project).setSettings(compilerSettings);
|
||||
}
|
||||
|
||||
for (ClearBuildStateExtension extension : ClearBuildStateExtension.getExtensions()) {
|
||||
extension.clearState(project);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply() throws ConfigurationException {
|
||||
applyTo(commonCompilerArguments, k2jvmCompilerArguments, k2jsCompilerArguments, compilerSettings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
reportWarningsCheckBox.setSelected(!commonCompilerArguments.getSuppressWarnings());
|
||||
enableNewInferenceInIDECheckBox.setSelected(NewInferenceForIDEAnalysisComponent.isEnabled(project));
|
||||
languageVersionComboBox.setSelectedItem(KotlinFacetSettingsKt.getLanguageVersionView(commonCompilerArguments));
|
||||
onLanguageLevelChanged(getSelectedLanguageVersionView());
|
||||
apiVersionComboBox.setSelectedItem(KotlinFacetSettingsKt.getApiVersionView(commonCompilerArguments));
|
||||
coroutineSupportComboBox.setSelectedItem(CoroutineSupport.byCompilerArguments(commonCompilerArguments));
|
||||
additionalArgsOptionsField.setText(compilerSettings.getAdditionalArguments());
|
||||
scriptTemplatesField.setText(compilerSettings.getScriptTemplates());
|
||||
scriptTemplatesClasspathField.setText(compilerSettings.getScriptTemplatesClasspath());
|
||||
copyRuntimeFilesCheckBox.setSelected(compilerSettings.getCopyJsLibraryFiles());
|
||||
outputDirectory.setText(compilerSettings.getOutputDirectoryForJsLibraryFiles());
|
||||
|
||||
if (compilerWorkspaceSettings != null) {
|
||||
enableIncrementalCompilationForJvmCheckBox.setSelected(compilerWorkspaceSettings.getPreciseIncrementalEnabled());
|
||||
enableIncrementalCompilationForJsCheckBox.setSelected(compilerWorkspaceSettings.getIncrementalCompilationForJsEnabled());
|
||||
keepAliveCheckBox.setSelected(compilerWorkspaceSettings.getEnableDaemon());
|
||||
}
|
||||
|
||||
generateSourceMapsCheckBox.setSelected(k2jsCompilerArguments.getSourceMap());
|
||||
outputPrefixFile.setText(k2jsCompilerArguments.getOutputPrefix());
|
||||
outputPostfixFile.setText(k2jsCompilerArguments.getOutputPostfix());
|
||||
|
||||
moduleKindComboBox.setSelectedItem(getModuleKindOrDefault(k2jsCompilerArguments.getModuleKind()));
|
||||
sourceMapPrefix.setText(k2jsCompilerArguments.getSourceMapPrefix());
|
||||
sourceMapPrefix.setEnabled(k2jsCompilerArguments.getSourceMap());
|
||||
sourceMapEmbedSources.setSelectedItem(getSourceMapSourceEmbeddingOrDefault(k2jsCompilerArguments.getSourceMapEmbedSources()));
|
||||
|
||||
jvmVersionComboBox.setSelectedItem(getJvmVersionOrDefault(k2jvmCompilerArguments.getJvmTarget()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disposeUIResources() {
|
||||
}
|
||||
|
||||
@Nls
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return "Kotlin Compiler";
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getHelpTopic() {
|
||||
return "reference.compiler.kotlin";
|
||||
}
|
||||
|
||||
public JPanel getContentPane() {
|
||||
return contentPane;
|
||||
}
|
||||
|
||||
public ThreeStateCheckBox getReportWarningsCheckBox() {
|
||||
return reportWarningsCheckBox;
|
||||
}
|
||||
|
||||
public ThreeStateCheckBox getEnableNewInferenceInIDECheckBox() {
|
||||
return enableNewInferenceInIDECheckBox;
|
||||
}
|
||||
|
||||
public RawCommandLineEditor getAdditionalArgsOptionsField() {
|
||||
return additionalArgsOptionsField;
|
||||
}
|
||||
|
||||
public ThreeStateCheckBox getGenerateSourceMapsCheckBox() {
|
||||
return generateSourceMapsCheckBox;
|
||||
}
|
||||
|
||||
public TextFieldWithBrowseButton getOutputPrefixFile() {
|
||||
return outputPrefixFile;
|
||||
}
|
||||
|
||||
public TextFieldWithBrowseButton getOutputPostfixFile() {
|
||||
return outputPostfixFile;
|
||||
}
|
||||
|
||||
public TextFieldWithBrowseButton getOutputDirectory() {
|
||||
return outputDirectory;
|
||||
}
|
||||
|
||||
public ThreeStateCheckBox getCopyRuntimeFilesCheckBox() {
|
||||
return copyRuntimeFilesCheckBox;
|
||||
}
|
||||
|
||||
public ThreeStateCheckBox getKeepAliveCheckBox() {
|
||||
return keepAliveCheckBox;
|
||||
}
|
||||
|
||||
public JComboBox getModuleKindComboBox() {
|
||||
return moduleKindComboBox;
|
||||
}
|
||||
|
||||
public JTextField getScriptTemplatesField() {
|
||||
return scriptTemplatesField;
|
||||
}
|
||||
|
||||
public JTextField getScriptTemplatesClasspathField() {
|
||||
return scriptTemplatesClasspathField;
|
||||
}
|
||||
|
||||
public JComboBox getLanguageVersionComboBox() {
|
||||
return languageVersionComboBox;
|
||||
}
|
||||
|
||||
public JComboBox getApiVersionComboBox() {
|
||||
return apiVersionComboBox;
|
||||
}
|
||||
|
||||
public JComboBox getCoroutineSupportComboBox() {
|
||||
return coroutineSupportComboBox;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean value) {
|
||||
isEnabled = value;
|
||||
UIUtil.setEnabled(getContentPane(), value, true);
|
||||
}
|
||||
|
||||
public CommonCompilerArguments getCommonCompilerArguments() {
|
||||
return commonCompilerArguments;
|
||||
}
|
||||
|
||||
public void setCommonCompilerArguments(CommonCompilerArguments commonCompilerArguments) {
|
||||
this.commonCompilerArguments = commonCompilerArguments;
|
||||
}
|
||||
|
||||
public K2JSCompilerArguments getK2jsCompilerArguments() {
|
||||
return k2jsCompilerArguments;
|
||||
}
|
||||
|
||||
public void setK2jsCompilerArguments(K2JSCompilerArguments k2jsCompilerArguments) {
|
||||
this.k2jsCompilerArguments = k2jsCompilerArguments;
|
||||
}
|
||||
|
||||
public K2JVMCompilerArguments getK2jvmCompilerArguments() {
|
||||
return k2jvmCompilerArguments;
|
||||
}
|
||||
|
||||
public void setK2jvmCompilerArguments(K2JVMCompilerArguments k2jvmCompilerArguments) {
|
||||
this.k2jvmCompilerArguments = k2jvmCompilerArguments;
|
||||
}
|
||||
|
||||
public CompilerSettings getCompilerSettings() {
|
||||
return compilerSettings;
|
||||
}
|
||||
|
||||
public void setCompilerSettings(CompilerSettings compilerSettings) {
|
||||
this.compilerSettings = compilerSettings;
|
||||
}
|
||||
|
||||
private void createUIComponents() {
|
||||
// Workaround: ThreeStateCheckBox doesn't send suitable notification on state change
|
||||
// TODO: replace with PropertyChangerListener after fix is available in IDEA
|
||||
copyRuntimeFilesCheckBox = new ThreeStateCheckBox() {
|
||||
@Override
|
||||
public void setState(State state) {
|
||||
super.setState(state);
|
||||
updateOutputDirEnabled();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,252 @@
|
||||
/*
|
||||
* 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.hierarchy;
|
||||
|
||||
import com.intellij.ide.hierarchy.*;
|
||||
import com.intellij.ide.hierarchy.actions.BrowseHierarchyActionBase;
|
||||
import com.intellij.ide.hierarchy.call.CallerMethodsTreeStructure;
|
||||
import com.intellij.ide.hierarchy.type.SubtypesHierarchyTreeStructure;
|
||||
import com.intellij.ide.hierarchy.type.SupertypesHierarchyTreeStructure;
|
||||
import com.intellij.ide.hierarchy.type.TypeHierarchyTreeStructure;
|
||||
import com.intellij.lang.LanguageExtension;
|
||||
import com.intellij.openapi.actionSystem.CommonDataKeys;
|
||||
import com.intellij.openapi.actionSystem.DataContext;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.fileEditor.impl.text.TextEditorPsiDataProvider;
|
||||
import com.intellij.openapi.util.Computable;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.refactoring.util.CommonRefactoringUtil.RefactoringErrorHintException;
|
||||
import com.intellij.rt.execution.junit.ComparisonDetailsExtractor;
|
||||
import com.intellij.testFramework.LightProjectDescriptor;
|
||||
import com.intellij.testFramework.MapDataContext;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.Processor;
|
||||
import junit.framework.ComparisonFailure;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.kotlin.idea.KotlinHierarchyViewTestBase;
|
||||
import org.jetbrains.kotlin.idea.hierarchy.calls.KotlinCalleeTreeStructure;
|
||||
import org.jetbrains.kotlin.idea.hierarchy.calls.KotlinCallerTreeStructure;
|
||||
import org.jetbrains.kotlin.idea.hierarchy.overrides.KotlinOverrideTreeStructure;
|
||||
import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor;
|
||||
import org.jetbrains.kotlin.psi.KtCallableDeclaration;
|
||||
import org.jetbrains.kotlin.psi.KtElement;
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/*
|
||||
Test Hierarchy view
|
||||
Format: test build hierarchy for element at caret, file with caret should be the first in the sorted list of files.
|
||||
Test accept more than one file, file extension should be .java or .kt
|
||||
*/
|
||||
public abstract class AbstractHierarchyTest extends KotlinHierarchyViewTestBase {
|
||||
|
||||
protected String folderName;
|
||||
|
||||
protected void doTypeClassHierarchyTest(@NotNull String folderName) throws Exception {
|
||||
this.folderName = folderName;
|
||||
doHierarchyTest(getTypeHierarchyStructure(), getFilesToConfigure());
|
||||
}
|
||||
|
||||
protected void doSuperClassHierarchyTest(@NotNull String folderName) throws Exception {
|
||||
this.folderName = folderName;
|
||||
doHierarchyTest(getSuperTypesHierarchyStructure(), getFilesToConfigure());
|
||||
}
|
||||
|
||||
protected void doSubClassHierarchyTest(@NotNull String folderName) throws Exception {
|
||||
this.folderName = folderName;
|
||||
doHierarchyTest(getSubTypesHierarchyStructure(), getFilesToConfigure());
|
||||
}
|
||||
|
||||
protected void doCallerHierarchyTest(@NotNull String folderName) throws Exception {
|
||||
this.folderName = folderName;
|
||||
doHierarchyTest(getCallerHierarchyStructure(), getFilesToConfigure());
|
||||
}
|
||||
|
||||
protected void doCallerJavaHierarchyTest(@NotNull String folderName) throws Exception {
|
||||
this.folderName = folderName;
|
||||
doHierarchyTest(getCallerJavaHierarchyStructure(), getFilesToConfigure());
|
||||
}
|
||||
|
||||
protected void doCalleeHierarchyTest(@NotNull String folderName) throws Exception {
|
||||
this.folderName = folderName;
|
||||
doHierarchyTest(getCalleeHierarchyStructure(), getFilesToConfigure());
|
||||
}
|
||||
|
||||
protected void doOverrideHierarchyTest(@NotNull String folderName) throws Exception {
|
||||
this.folderName = folderName;
|
||||
doHierarchyTest(getOverrideHierarchyStructure(), getFilesToConfigure());
|
||||
}
|
||||
|
||||
private Computable<HierarchyTreeStructure> getSuperTypesHierarchyStructure() {
|
||||
return new Computable<HierarchyTreeStructure>() {
|
||||
@Override
|
||||
public HierarchyTreeStructure compute() {
|
||||
return new SupertypesHierarchyTreeStructure(
|
||||
getProject(),
|
||||
(PsiClass) getElementAtCaret(LanguageTypeHierarchy.INSTANCE)
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private Computable<HierarchyTreeStructure> getSubTypesHierarchyStructure() {
|
||||
return new Computable<HierarchyTreeStructure>() {
|
||||
@Override
|
||||
public HierarchyTreeStructure compute() {
|
||||
return new SubtypesHierarchyTreeStructure(
|
||||
getProject(),
|
||||
(PsiClass) getElementAtCaret(LanguageTypeHierarchy.INSTANCE),
|
||||
HierarchyBrowserBaseEx.SCOPE_PROJECT
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private Computable<HierarchyTreeStructure> getTypeHierarchyStructure() {
|
||||
return new Computable<HierarchyTreeStructure>() {
|
||||
@Override
|
||||
public HierarchyTreeStructure compute() {
|
||||
return new TypeHierarchyTreeStructure(
|
||||
getProject(),
|
||||
(PsiClass) getElementAtCaret(LanguageTypeHierarchy.INSTANCE),
|
||||
HierarchyBrowserBaseEx.SCOPE_PROJECT
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private Computable<HierarchyTreeStructure> getCallerHierarchyStructure() {
|
||||
return new Computable<HierarchyTreeStructure>() {
|
||||
@Override
|
||||
public HierarchyTreeStructure compute() {
|
||||
return new KotlinCallerTreeStructure(
|
||||
(KtElement) getElementAtCaret(LanguageCallHierarchy.INSTANCE),
|
||||
HierarchyBrowserBaseEx.SCOPE_PROJECT
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private Computable<HierarchyTreeStructure> getCallerJavaHierarchyStructure() {
|
||||
return new Computable<HierarchyTreeStructure>() {
|
||||
@Override
|
||||
public HierarchyTreeStructure compute() {
|
||||
return new CallerMethodsTreeStructure(
|
||||
getProject(),
|
||||
(PsiMethod) getElementAtCaret(LanguageCallHierarchy.INSTANCE),
|
||||
HierarchyBrowserBaseEx.SCOPE_PROJECT
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private Computable<HierarchyTreeStructure> getCalleeHierarchyStructure() {
|
||||
return new Computable<HierarchyTreeStructure>() {
|
||||
@Override
|
||||
public HierarchyTreeStructure compute() {
|
||||
return new KotlinCalleeTreeStructure(
|
||||
(KtElement) getElementAtCaret(LanguageCallHierarchy.INSTANCE),
|
||||
HierarchyBrowserBaseEx.SCOPE_PROJECT
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private Computable<HierarchyTreeStructure> getOverrideHierarchyStructure() {
|
||||
return new Computable<HierarchyTreeStructure>() {
|
||||
@Override
|
||||
public HierarchyTreeStructure compute() {
|
||||
return new KotlinOverrideTreeStructure(
|
||||
getProject(),
|
||||
(KtCallableDeclaration) getElementAtCaret(LanguageMethodHierarchy.INSTANCE)
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private PsiElement getElementAtCaret(LanguageExtension<HierarchyProvider> extension) {
|
||||
PsiFile file = PsiDocumentManager.getInstance(getProject()).getPsiFile(getEditor().getDocument());
|
||||
HierarchyProvider provider = BrowseHierarchyActionBase.findProvider(extension, file, file, getDataContext());
|
||||
PsiElement target = provider != null ? provider.getTarget(getDataContext()) : null;
|
||||
if (target == null) throw new RefactoringErrorHintException("Cannot apply action for element at caret");
|
||||
return target;
|
||||
}
|
||||
|
||||
private DataContext getDataContext() {
|
||||
Editor editor = getEditor();
|
||||
|
||||
MapDataContext context = new MapDataContext();
|
||||
context.put(CommonDataKeys.PROJECT, getProject());
|
||||
context.put(CommonDataKeys.EDITOR, editor);
|
||||
PsiElement targetElement = (PsiElement) new TextEditorPsiDataProvider().getData(
|
||||
CommonDataKeys.PSI_ELEMENT.getName(),
|
||||
editor,
|
||||
editor.getCaretModel().getCurrentCaret()
|
||||
);
|
||||
context.put(CommonDataKeys.PSI_ELEMENT, targetElement);
|
||||
return context;
|
||||
}
|
||||
|
||||
protected String[] getFilesToConfigure() {
|
||||
final List<String> files = new ArrayList<String>(2);
|
||||
FileUtil.processFilesRecursively(new File(folderName), new Processor<File>() {
|
||||
@Override
|
||||
public boolean process(File file) {
|
||||
String fileName = file.getName();
|
||||
if (fileName.endsWith(".kt") || fileName.endsWith(".java")) {
|
||||
files.add(fileName);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
Collections.sort(files);
|
||||
return ArrayUtil.toStringArray(files);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doHierarchyTest(
|
||||
@NotNull Computable<? extends HierarchyTreeStructure> treeStructureComputable, @NotNull String... fileNames
|
||||
) throws Exception {
|
||||
try {
|
||||
super.doHierarchyTest(treeStructureComputable, fileNames);
|
||||
}
|
||||
catch (RefactoringErrorHintException e) {
|
||||
File file = new File(folderName, "messages.txt");
|
||||
if (file.exists()) {
|
||||
String expectedMessage = FileUtil.loadFile(file, true);
|
||||
assertEquals(expectedMessage, e.getLocalizedMessage());
|
||||
}
|
||||
else {
|
||||
fail("Unexpected error: " + e.getLocalizedMessage());
|
||||
}
|
||||
}
|
||||
catch (ComparisonFailure failure) {
|
||||
String actual = ComparisonDetailsExtractor.getActual(failure);
|
||||
String verificationFilePath =
|
||||
getTestDataPath() + "/" + getTestName(false) + "_verification.xml";
|
||||
KotlinTestUtils.assertEqualsToFile(new File(verificationFilePath), actual);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
protected LightProjectDescriptor getProjectDescriptor() {
|
||||
return KotlinWithJdkAndRuntimeLightProjectDescriptor.INSTANCE;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected String getTestDataPath() {
|
||||
String testRoot = super.getTestDataPath();
|
||||
String testDir = KotlinTestUtils.getTestDataFileName(this.getClass(), getName());
|
||||
return testRoot + "/" + testDir;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,252 @@
|
||||
/*
|
||||
* 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.hierarchy;
|
||||
|
||||
import com.intellij.ide.hierarchy.*;
|
||||
import com.intellij.ide.hierarchy.actions.BrowseHierarchyActionBase;
|
||||
import com.intellij.ide.hierarchy.call.CallerMethodsTreeStructure;
|
||||
import com.intellij.ide.hierarchy.type.SubtypesHierarchyTreeStructure;
|
||||
import com.intellij.ide.hierarchy.type.SupertypesHierarchyTreeStructure;
|
||||
import com.intellij.ide.hierarchy.type.TypeHierarchyTreeStructure;
|
||||
import com.intellij.lang.LanguageExtension;
|
||||
import com.intellij.openapi.actionSystem.CommonDataKeys;
|
||||
import com.intellij.openapi.actionSystem.DataContext;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.fileEditor.impl.text.TextEditorPsiDataProvider;
|
||||
import com.intellij.openapi.util.Computable;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.refactoring.util.CommonRefactoringUtil.RefactoringErrorHintException;
|
||||
import com.intellij.rt.execution.junit.ComparisonDetailsExtractor;
|
||||
import com.intellij.testFramework.LightProjectDescriptor;
|
||||
import com.intellij.testFramework.MapDataContext;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.Processor;
|
||||
import junit.framework.ComparisonFailure;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.kotlin.idea.KotlinHierarchyViewTestBase;
|
||||
import org.jetbrains.kotlin.idea.hierarchy.calls.KotlinCalleeTreeStructure;
|
||||
import org.jetbrains.kotlin.idea.hierarchy.calls.KotlinCallerTreeStructure;
|
||||
import org.jetbrains.kotlin.idea.hierarchy.overrides.KotlinOverrideTreeStructure;
|
||||
import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor;
|
||||
import org.jetbrains.kotlin.psi.KtCallableDeclaration;
|
||||
import org.jetbrains.kotlin.psi.KtElement;
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/*
|
||||
Test Hierarchy view
|
||||
Format: test build hierarchy for element at caret, file with caret should be the first in the sorted list of files.
|
||||
Test accept more than one file, file extension should be .java or .kt
|
||||
*/
|
||||
public abstract class AbstractHierarchyTest extends KotlinHierarchyViewTestBase {
|
||||
|
||||
protected String folderName;
|
||||
|
||||
protected void doTypeClassHierarchyTest(@NotNull String folderName) throws Exception {
|
||||
this.folderName = folderName;
|
||||
doHierarchyTest(getTypeHierarchyStructure(), getFilesToConfigure());
|
||||
}
|
||||
|
||||
protected void doSuperClassHierarchyTest(@NotNull String folderName) throws Exception {
|
||||
this.folderName = folderName;
|
||||
doHierarchyTest(getSuperTypesHierarchyStructure(), getFilesToConfigure());
|
||||
}
|
||||
|
||||
protected void doSubClassHierarchyTest(@NotNull String folderName) throws Exception {
|
||||
this.folderName = folderName;
|
||||
doHierarchyTest(getSubTypesHierarchyStructure(), getFilesToConfigure());
|
||||
}
|
||||
|
||||
protected void doCallerHierarchyTest(@NotNull String folderName) throws Exception {
|
||||
this.folderName = folderName;
|
||||
doHierarchyTest(getCallerHierarchyStructure(), getFilesToConfigure());
|
||||
}
|
||||
|
||||
protected void doCallerJavaHierarchyTest(@NotNull String folderName) throws Exception {
|
||||
this.folderName = folderName;
|
||||
doHierarchyTest(getCallerJavaHierarchyStructure(), getFilesToConfigure());
|
||||
}
|
||||
|
||||
protected void doCalleeHierarchyTest(@NotNull String folderName) throws Exception {
|
||||
this.folderName = folderName;
|
||||
doHierarchyTest(getCalleeHierarchyStructure(), getFilesToConfigure());
|
||||
}
|
||||
|
||||
protected void doOverrideHierarchyTest(@NotNull String folderName) throws Exception {
|
||||
this.folderName = folderName;
|
||||
doHierarchyTest(getOverrideHierarchyStructure(), getFilesToConfigure());
|
||||
}
|
||||
|
||||
private Computable<HierarchyTreeStructure> getSuperTypesHierarchyStructure() {
|
||||
return new Computable<HierarchyTreeStructure>() {
|
||||
@Override
|
||||
public HierarchyTreeStructure compute() {
|
||||
return new SupertypesHierarchyTreeStructure(
|
||||
getProject(),
|
||||
(PsiClass) getElementAtCaret(LanguageTypeHierarchy.INSTANCE)
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private Computable<HierarchyTreeStructure> getSubTypesHierarchyStructure() {
|
||||
return new Computable<HierarchyTreeStructure>() {
|
||||
@Override
|
||||
public HierarchyTreeStructure compute() {
|
||||
return new SubtypesHierarchyTreeStructure(
|
||||
getProject(),
|
||||
(PsiClass) getElementAtCaret(LanguageTypeHierarchy.INSTANCE),
|
||||
HierarchyBrowserBaseEx.SCOPE_PROJECT
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private Computable<HierarchyTreeStructure> getTypeHierarchyStructure() {
|
||||
return new Computable<HierarchyTreeStructure>() {
|
||||
@Override
|
||||
public HierarchyTreeStructure compute() {
|
||||
return new TypeHierarchyTreeStructure(
|
||||
getProject(),
|
||||
(PsiClass) getElementAtCaret(LanguageTypeHierarchy.INSTANCE),
|
||||
HierarchyBrowserBaseEx.SCOPE_PROJECT
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private Computable<HierarchyTreeStructure> getCallerHierarchyStructure() {
|
||||
return new Computable<HierarchyTreeStructure>() {
|
||||
@Override
|
||||
public HierarchyTreeStructure compute() {
|
||||
return new KotlinCallerTreeStructure(
|
||||
(KtElement) getElementAtCaret(LanguageCallHierarchy.INSTANCE),
|
||||
HierarchyBrowserBaseEx.SCOPE_PROJECT
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private Computable<HierarchyTreeStructure> getCallerJavaHierarchyStructure() {
|
||||
return new Computable<HierarchyTreeStructure>() {
|
||||
@Override
|
||||
public HierarchyTreeStructure compute() {
|
||||
return new CallerMethodsTreeStructure(
|
||||
getProject(),
|
||||
(PsiMethod) getElementAtCaret(LanguageCallHierarchy.INSTANCE),
|
||||
HierarchyBrowserBaseEx.SCOPE_PROJECT
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private Computable<HierarchyTreeStructure> getCalleeHierarchyStructure() {
|
||||
return new Computable<HierarchyTreeStructure>() {
|
||||
@Override
|
||||
public HierarchyTreeStructure compute() {
|
||||
return new KotlinCalleeTreeStructure(
|
||||
(KtElement) getElementAtCaret(LanguageCallHierarchy.INSTANCE),
|
||||
HierarchyBrowserBaseEx.SCOPE_PROJECT
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private Computable<HierarchyTreeStructure> getOverrideHierarchyStructure() {
|
||||
return new Computable<HierarchyTreeStructure>() {
|
||||
@Override
|
||||
public HierarchyTreeStructure compute() {
|
||||
return new KotlinOverrideTreeStructure(
|
||||
getProject(),
|
||||
(KtCallableDeclaration) getElementAtCaret(LanguageMethodHierarchy.INSTANCE)
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private PsiElement getElementAtCaret(LanguageExtension<HierarchyProvider> extension) {
|
||||
PsiFile file = PsiDocumentManager.getInstance(getProject()).getPsiFile(getEditor().getDocument());
|
||||
HierarchyProvider provider = BrowseHierarchyActionBase.findProvider(extension, file, file, getDataContext());
|
||||
PsiElement target = provider != null ? provider.getTarget(getDataContext()) : null;
|
||||
if (target == null) throw new RefactoringErrorHintException("Cannot apply action for element at caret");
|
||||
return target;
|
||||
}
|
||||
|
||||
private DataContext getDataContext() {
|
||||
Editor editor = getEditor();
|
||||
|
||||
MapDataContext context = new MapDataContext();
|
||||
context.put(CommonDataKeys.PROJECT, getProject());
|
||||
context.put(CommonDataKeys.EDITOR, editor);
|
||||
PsiElement targetElement = (PsiElement) new TextEditorPsiDataProvider().getData(
|
||||
CommonDataKeys.PSI_ELEMENT.getName(),
|
||||
editor,
|
||||
editor.getCaretModel().getCurrentCaret()
|
||||
);
|
||||
context.put(CommonDataKeys.PSI_ELEMENT, targetElement);
|
||||
return context;
|
||||
}
|
||||
|
||||
protected String[] getFilesToConfigure() {
|
||||
final List<String> files = new ArrayList<String>(2);
|
||||
FileUtil.processFilesRecursively(new File(folderName), new Processor<File>() {
|
||||
@Override
|
||||
public boolean process(File file) {
|
||||
String fileName = file.getName();
|
||||
if (fileName.endsWith(".kt") || fileName.endsWith(".java")) {
|
||||
files.add(fileName);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
Collections.sort(files);
|
||||
return ArrayUtil.toStringArray(files);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doHierarchyTest(
|
||||
@NotNull Computable<? extends HierarchyTreeStructure> treeStructureComputable, @NotNull String... fileNames
|
||||
) throws Exception {
|
||||
try {
|
||||
super.doHierarchyTest(treeStructureComputable, fileNames);
|
||||
}
|
||||
catch (RefactoringErrorHintException e) {
|
||||
File file = new File(folderName, "messages.txt");
|
||||
if (file.exists()) {
|
||||
String expectedMessage = FileUtil.loadFile(file, true);
|
||||
assertEquals(expectedMessage, e.getLocalizedMessage());
|
||||
}
|
||||
else {
|
||||
fail("Unexpected error: " + e.getLocalizedMessage());
|
||||
}
|
||||
}
|
||||
catch (ComparisonFailure failure) {
|
||||
String actual = ComparisonDetailsExtractor.getActual(failure);
|
||||
String verificationFilePath =
|
||||
getTestDataPath() + "/" + getTestName(false) + "_verification.xml";
|
||||
KotlinTestUtils.assertEqualsToFile(new File(verificationFilePath), actual);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
protected LightProjectDescriptor getProjectDescriptor() {
|
||||
return KotlinWithJdkAndRuntimeLightProjectDescriptor.INSTANCE;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected String getTestDataPath() {
|
||||
String testRoot = super.getTestDataPath();
|
||||
String testDir = KotlinTestUtils.getTestDataFileName(this.getClass(), getName());
|
||||
return testRoot + "/" + testDir;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user