diff --git a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinSourceSetDataService.kt b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinSourceSetDataService.kt index a0d5119756d..ac5fa767a3a 100644 --- a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinSourceSetDataService.kt +++ b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinSourceSetDataService.kt @@ -30,6 +30,7 @@ import org.jetbrains.kotlin.idea.inspections.gradle.findAll import org.jetbrains.kotlin.idea.inspections.gradle.findKotlinPluginVersion import org.jetbrains.kotlin.idea.platform.IdePlatformKindTooling import org.jetbrains.kotlin.idea.roots.migrateNonJvmSourceFolders +import org.jetbrains.kotlin.idea.roots.populateNonJvmSourceRootTypes import org.jetbrains.kotlin.platform.TargetPlatform import org.jetbrains.kotlin.platform.impl.JvmIdePlatformKind import org.jetbrains.kotlin.platform.impl.NativeIdePlatformKind @@ -61,6 +62,7 @@ class KotlinSourceSetDataService : AbstractProjectDataService diff --git a/idea/src/org/jetbrains/kotlin/idea/roots/projectRootUtils.kt b/idea/src/org/jetbrains/kotlin/idea/roots/projectRootUtils.kt index 8ac32cd8b7a..221d492e0bd 100644 --- a/idea/src/org/jetbrains/kotlin/idea/roots/projectRootUtils.kt +++ b/idea/src/org/jetbrains/kotlin/idea/roots/projectRootUtils.kt @@ -5,6 +5,12 @@ package org.jetbrains.kotlin.idea.roots +import com.intellij.openapi.externalSystem.model.DataNode +import com.intellij.openapi.externalSystem.model.ProjectKeys +import com.intellij.openapi.externalSystem.model.project.ContentRootData +import com.intellij.openapi.externalSystem.model.project.ExternalSystemSourceType +import com.intellij.openapi.externalSystem.service.project.manage.SourceFolderManager +import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil import com.intellij.openapi.module.Module import com.intellij.openapi.module.ModuleManager import com.intellij.openapi.project.Project @@ -13,11 +19,14 @@ import com.intellij.openapi.roots.ModifiableRootModel import com.intellij.openapi.roots.ModuleRootManager import com.intellij.openapi.roots.ProjectRootManager import com.intellij.openapi.roots.SourceFolder +import com.intellij.openapi.util.io.FileUtil import com.intellij.openapi.vfs.NonPhysicalFileSystem import com.intellij.openapi.vfs.VirtualFile +import com.intellij.openapi.vfs.VirtualFileManager import com.intellij.psi.PsiCodeFragment import com.intellij.psi.PsiFile import com.intellij.util.containers.ContainerUtil +import com.intellij.util.io.URLUtil import org.jetbrains.jps.model.JpsElement import org.jetbrains.jps.model.ex.JpsElementBase import org.jetbrains.jps.model.java.JavaModuleSourceRootTypes @@ -28,6 +37,7 @@ import org.jetbrains.jps.model.module.JpsModuleSourceRoot import org.jetbrains.jps.model.module.JpsModuleSourceRootType import org.jetbrains.kotlin.config.* import org.jetbrains.kotlin.idea.framework.KotlinSdkType +import org.jetbrains.plugins.gradle.model.data.GradleSourceSetData import java.util.* private fun JpsModuleSourceRoot.getOrCreateProperties() = @@ -59,6 +69,29 @@ fun migrateNonJvmSourceFolders(modifiableRootModel: ModifiableRootModel) { KotlinSdkType.setUpIfNeeded() } +private val ContentRootData.SourceRoot.pathAsUrl + get() = VirtualFileManager.constructUrl(URLUtil.FILE_PROTOCOL, FileUtil.toSystemIndependentName(path)) + +fun populateNonJvmSourceRootTypes(sourceSetNode: DataNode, module: Module) { + val sourceFolderManager = SourceFolderManager.getInstance(module.project) + val contentRootDataNodes = ExternalSystemApiUtil.findAll(sourceSetNode, ProjectKeys.CONTENT_ROOT) + val contentRootDataList = contentRootDataNodes.mapNotNull { it.data } + if (contentRootDataList.isEmpty()) return + + val externalToKotlinSourceTypes = mapOf( + ExternalSystemSourceType.SOURCE to SourceKotlinRootType, + ExternalSystemSourceType.TEST to TestSourceKotlinRootType + ) + externalToKotlinSourceTypes.forEach { (externalType, kotlinType) -> + val sourcesRoots = contentRootDataList.flatMap { it.getPaths(externalType) } + sourcesRoots.forEach { + if (!FileUtil.exists(it.path)) { + sourceFolderManager.addSourceFolder(module, it.pathAsUrl, kotlinType) + } + } + } +} + fun getKotlinAwareDestinationSourceRoots(project: Project): List { return ModuleManager.getInstance(project).modules.flatMap { it.collectKotlinAwareDestinationSourceRoots() } } diff --git a/idea/src/org/jetbrains/kotlin/idea/roots/projectRootUtils.kt.192 b/idea/src/org/jetbrains/kotlin/idea/roots/projectRootUtils.kt.192 new file mode 100644 index 00000000000..00e481bf956 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/roots/projectRootUtils.kt.192 @@ -0,0 +1,121 @@ +/* + * 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.roots + +import com.intellij.openapi.externalSystem.model.DataNode +import com.intellij.openapi.module.Module +import com.intellij.openapi.module.ModuleManager +import com.intellij.openapi.project.Project +import com.intellij.openapi.project.rootManager +import com.intellij.openapi.roots.ModifiableRootModel +import com.intellij.openapi.roots.ModuleRootManager +import com.intellij.openapi.roots.ProjectRootManager +import com.intellij.openapi.roots.SourceFolder +import com.intellij.openapi.vfs.NonPhysicalFileSystem +import com.intellij.openapi.vfs.VirtualFile +import com.intellij.psi.PsiCodeFragment +import com.intellij.psi.PsiFile +import com.intellij.util.containers.ContainerUtil +import org.jetbrains.jps.model.JpsElement +import org.jetbrains.jps.model.ex.JpsElementBase +import org.jetbrains.jps.model.java.JavaModuleSourceRootTypes +import org.jetbrains.jps.model.java.JavaResourceRootType +import org.jetbrains.jps.model.java.JavaSourceRootProperties +import org.jetbrains.jps.model.java.JavaSourceRootType +import org.jetbrains.jps.model.module.JpsModuleSourceRoot +import org.jetbrains.jps.model.module.JpsModuleSourceRootType +import org.jetbrains.kotlin.config.* +import org.jetbrains.kotlin.idea.framework.KotlinSdkType +import org.jetbrains.plugins.gradle.model.data.GradleSourceSetData +import java.util.* + +private fun JpsModuleSourceRoot.getOrCreateProperties() = + getProperties(rootType)?.also { (it as? JpsElementBase<*>)?.setParent(null) } ?: rootType.createDefaultProperties() + +fun JpsModuleSourceRoot.getMigratedSourceRootTypeWithProperties(): Pair, JpsElement>? { + val currentRootType = rootType + + @Suppress("UNCHECKED_CAST") + val newSourceRootType: JpsModuleSourceRootType = when (currentRootType) { + JavaSourceRootType.SOURCE -> SourceKotlinRootType as JpsModuleSourceRootType + JavaSourceRootType.TEST_SOURCE -> TestSourceKotlinRootType + JavaResourceRootType.RESOURCE -> ResourceKotlinRootType + JavaResourceRootType.TEST_RESOURCE -> TestResourceKotlinRootType + else -> return null + } as JpsModuleSourceRootType + return newSourceRootType to getOrCreateProperties() +} + +fun migrateNonJvmSourceFolders(modifiableRootModel: ModifiableRootModel) { + for (contentEntry in modifiableRootModel.contentEntries) { + for (sourceFolder in contentEntry.sourceFolders) { + val (newSourceRootType, properties) = sourceFolder.jpsElement.getMigratedSourceRootTypeWithProperties() ?: continue + val url = sourceFolder.url + contentEntry.removeSourceFolder(sourceFolder) + contentEntry.addSourceFolder(url, newSourceRootType, properties) + } + } + KotlinSdkType.setUpIfNeeded() +} + +fun populateNonJvmSourceRootTypes(sourceSetNode: DataNode, module: Module) = Unit + +fun getKotlinAwareDestinationSourceRoots(project: Project): List { + return ModuleManager.getInstance(project).modules.flatMap { it.collectKotlinAwareDestinationSourceRoots() } +} + +private val KOTLIN_AWARE_SOURCE_ROOT_TYPES: Set> = + JavaModuleSourceRootTypes.SOURCES + ALL_KOTLIN_SOURCE_ROOT_TYPES + +private fun Module.collectKotlinAwareDestinationSourceRoots(): List { + return rootManager + .contentEntries + .asSequence() + .flatMap { it.getSourceFolders(KOTLIN_AWARE_SOURCE_ROOT_TYPES).asSequence() } + .filterNot { isForGeneratedSources(it) } + .mapNotNull { it.file } + .toList() +} + +fun isOutsideSourceRootSet(psiFile: PsiFile?, sourceRootTypes: Set>): Boolean { + if (psiFile == null || psiFile is PsiCodeFragment) return false + val file = psiFile.virtualFile ?: return false + if (file.fileSystem is NonPhysicalFileSystem) return false + val projectFileIndex = ProjectRootManager.getInstance(psiFile.project).fileIndex + return !projectFileIndex.isUnderSourceRootOfType(file, sourceRootTypes) && !projectFileIndex.isInLibrary(file) +} + +fun isOutsideKotlinAwareSourceRoot(psiFile: PsiFile?) = isOutsideSourceRootSet(psiFile, KOTLIN_AWARE_SOURCE_ROOT_TYPES) + +/** + * @return list of all java source roots in the project which can be suggested as a target directory for a class created by user + */ +fun getSuitableDestinationSourceRoots(project: Project): List { + val roots = ArrayList() + for (module in ModuleManager.getInstance(project).modules) { + collectSuitableDestinationSourceRoots(module, roots) + } + return roots +} + +fun collectSuitableDestinationSourceRoots(module: Module, result: MutableList) { + for (entry in ModuleRootManager.getInstance(module).contentEntries) { + for (sourceFolder in entry.getSourceFolders(KOTLIN_AWARE_SOURCE_ROOT_TYPES)) { + if (!isForGeneratedSources(sourceFolder)) { + ContainerUtil.addIfNotNull(result, sourceFolder.file) + } + } + } +} + +fun isForGeneratedSources(sourceFolder: SourceFolder): Boolean { + val properties = sourceFolder.jpsElement.getProperties(KOTLIN_AWARE_SOURCE_ROOT_TYPES) + val javaResourceProperties = sourceFolder.jpsElement.getProperties(JavaModuleSourceRootTypes.RESOURCES) + val kotlinResourceProperties = sourceFolder.jpsElement.getProperties(ALL_KOTLIN_RESOURCE_ROOT_TYPES) + return properties != null && properties.isForGeneratedSources + || (javaResourceProperties != null && javaResourceProperties.isForGeneratedSources) + || (kotlinResourceProperties != null && kotlinResourceProperties.isForGeneratedSources) +}