Fixed incompatibility with 191 and 192

Fixed #KT-35918
This commit is contained in:
Vladimir Dolzhenko
2020-02-06 17:19:55 +01:00
parent 5605eb5a31
commit a17027e330
28 changed files with 11300 additions and 0 deletions
@@ -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
}
}
@@ -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,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;
}
}
@@ -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;
}
}
@@ -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;
}
}
@@ -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
}
@@ -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
}
@@ -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
}
}
@@ -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
}
}
@@ -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()
}
}
@@ -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()
}
}
@@ -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 {
}
@@ -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,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;
}
}