summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexey Sedunov <alexey.sedunov@jetbrains.com>2018-07-27 16:30:39 +0300
committerAlexey Sedunov <alexey.sedunov@jetbrains.com>2018-08-03 15:28:14 +0300
commit375c785694cf0fc5cc4c63a0911d1651931a1d0e (patch)
tree0d3c8052239a4bd337cea1fa8d67e008b29d4c40
parent4c9eb5ef1c3859b986ebb72e6b526cb16a96d676 (diff)
downloadkotlin-375c785694cf0fc5cc4c63a0911d1651931a1d0e.tar.gz
Misc: Add bunch files to fix compatibility with AS3.3C4
-rw-r--r--idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinGradleSourceSetDataService.kt.as33c4279
-rw-r--r--idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinWithGradleConfigurator.kt.as33c4403
-rw-r--r--idea/src/org/jetbrains/kotlin/idea/actions/JavaToKotlinAction.kt.as33c4231
3 files changed, 913 insertions, 0 deletions
diff --git a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinGradleSourceSetDataService.kt.as33c4 b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinGradleSourceSetDataService.kt.as33c4
new file mode 100644
index 00000000000..e9c91492b50
--- /dev/null
+++ b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinGradleSourceSetDataService.kt.as33c4
@@ -0,0 +1,279 @@
+/*
+ * 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.configuration
+
+import com.intellij.openapi.diagnostic.Logger
+import com.intellij.openapi.externalSystem.model.DataNode
+import com.intellij.openapi.externalSystem.model.ProjectKeys
+import com.intellij.openapi.externalSystem.model.project.LibraryData
+import com.intellij.openapi.externalSystem.model.project.ModuleData
+import com.intellij.openapi.externalSystem.model.project.ProjectData
+import com.intellij.openapi.externalSystem.service.project.IdeModifiableModelsProvider
+import com.intellij.openapi.externalSystem.service.project.manage.AbstractProjectDataService
+import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil
+import com.intellij.openapi.module.Module
+import com.intellij.openapi.module.isQualifiedModuleNamesEnabled
+import com.intellij.openapi.project.Project
+import com.intellij.openapi.roots.OrderRootType
+import com.intellij.openapi.roots.impl.libraries.LibraryEx
+import com.intellij.openapi.roots.impl.libraries.LibraryImpl
+import com.intellij.openapi.roots.libraries.PersistentLibraryKind
+import com.intellij.openapi.util.Key
+import com.intellij.util.PathUtil
+import org.jetbrains.kotlin.cli.common.arguments.K2JSCompilerArguments
+import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
+import org.jetbrains.kotlin.cli.common.arguments.parseCommandLineArguments
+import org.jetbrains.kotlin.config.CoroutineSupport
+import org.jetbrains.kotlin.config.JvmTarget
+import org.jetbrains.kotlin.config.LanguageFeature
+import org.jetbrains.kotlin.config.TargetPlatformKind
+import org.jetbrains.kotlin.extensions.ProjectExtensionDescriptor
+import org.jetbrains.kotlin.gradle.ArgsInfo
+import org.jetbrains.kotlin.gradle.CompilerArgumentsBySourceSet
+import org.jetbrains.kotlin.idea.facet.*
+import org.jetbrains.kotlin.idea.framework.CommonLibraryKind
+import org.jetbrains.kotlin.idea.framework.JSLibraryKind
+import org.jetbrains.kotlin.idea.framework.detectLibraryKind
+import org.jetbrains.kotlin.idea.inspections.gradle.findAll
+import org.jetbrains.kotlin.idea.inspections.gradle.findKotlinPluginVersion
+import org.jetbrains.kotlin.idea.inspections.gradle.getResolvedVersionByModuleData
+import org.jetbrains.kotlin.psi.UserDataProperty
+import org.jetbrains.plugins.gradle.model.data.BuildScriptClasspathData
+import org.jetbrains.plugins.gradle.model.data.GradleSourceSetData
+import java.io.File
+import java.util.*
+
+var Module.compilerArgumentsBySourceSet
+ by UserDataProperty(Key.create<CompilerArgumentsBySourceSet>("CURRENT_COMPILER_ARGUMENTS"))
+
+var Module.sourceSetName
+ by UserDataProperty(Key.create<String>("SOURCE_SET_NAME"))
+
+interface GradleProjectImportHandler {
+ companion object : ProjectExtensionDescriptor<GradleProjectImportHandler>(
+ "org.jetbrains.kotlin.gradleProjectImportHandler",
+ GradleProjectImportHandler::class.java
+ )
+
+ fun importBySourceSet(facet: KotlinFacet, sourceSetNode: DataNode<GradleSourceSetData>)
+ fun importByModule(facet: KotlinFacet, moduleNode: DataNode<ModuleData>)
+}
+
+class KotlinGradleSourceSetDataService : AbstractProjectDataService<GradleSourceSetData, Void>() {
+ override fun getTargetDataKey() = GradleSourceSetData.KEY
+
+ override fun postProcess(
+ toImport: Collection<DataNode<GradleSourceSetData>>,
+ projectData: ProjectData?,
+ project: Project,
+ modelsProvider: IdeModifiableModelsProvider
+ ) {
+ for (sourceSetNode in toImport) {
+ val sourceSetData = sourceSetNode.data
+ val ideModule = modelsProvider.findIdeModule(sourceSetData) ?: continue
+
+ val moduleNode = ExternalSystemApiUtil.findParent(sourceSetNode, ProjectKeys.MODULE) ?: continue
+ val sourceSetName = sourceSetNode.data.id.let { it.substring(it.lastIndexOf(':') + 1) }
+ val kotlinFacet = configureFacetByGradleModule(moduleNode, sourceSetName, ideModule, modelsProvider) ?: continue
+ GradleProjectImportHandler.getInstances(project).forEach { it.importBySourceSet(kotlinFacet, sourceSetNode) }
+ }
+ }
+}
+
+class KotlinGradleProjectDataService : AbstractProjectDataService<ModuleData, Void>() {
+ override fun getTargetDataKey() = ProjectKeys.MODULE
+
+ override fun postProcess(
+ toImport: MutableCollection<DataNode<ModuleData>>,
+ projectData: ProjectData?,
+ project: Project,
+ modelsProvider: IdeModifiableModelsProvider
+ ) {
+ for (moduleNode in toImport) {
+ // If source sets are present, configure facets in the their modules
+ if (ExternalSystemApiUtil.getChildren(moduleNode, GradleSourceSetData.KEY).isNotEmpty()) continue
+
+ val moduleData = moduleNode.data
+ val ideModule = modelsProvider.findIdeModule(moduleData) ?: continue
+ val kotlinFacet = configureFacetByGradleModule(moduleNode, null, ideModule, modelsProvider) ?: continue
+ GradleProjectImportHandler.getInstances(project).forEach { it.importByModule(kotlinFacet, moduleNode) }
+ }
+ }
+}
+
+class KotlinGradleLibraryDataService : AbstractProjectDataService<LibraryData, Void>() {
+ override fun getTargetDataKey() = ProjectKeys.LIBRARY
+
+ override fun postProcess(
+ toImport: MutableCollection<DataNode<LibraryData>>,
+ projectData: ProjectData?,
+ project: Project,
+ modelsProvider: IdeModifiableModelsProvider
+ ) {
+ if (toImport.isEmpty()) return
+ val projectDataNode = toImport.first().parent!!
+ @Suppress("UNCHECKED_CAST")
+ val moduleDataNodes = projectDataNode.children.filter { it.data is ModuleData } as List<DataNode<ModuleData>>
+ val anyNonJvmModules = moduleDataNodes.any { detectPlatformByPlugin(it)?.takeIf { it !is TargetPlatformKind.Jvm } != null }
+ for (libraryDataNode in toImport) {
+ val ideLibrary = modelsProvider.findIdeLibrary(libraryDataNode.data) ?: continue
+
+ val modifiableModel = modelsProvider.getModifiableLibraryModel(ideLibrary) as LibraryEx.ModifiableModelEx
+ if (anyNonJvmModules || ideLibrary.name?.looksAsNonJvmLibraryName() == true) {
+ detectLibraryKind(modifiableModel.getFiles(OrderRootType.CLASSES))?.let { modifiableModel.kind = it }
+ } else if (ideLibrary is LibraryImpl && (ideLibrary.kind === JSLibraryKind || ideLibrary.kind === CommonLibraryKind)) {
+ resetLibraryKind(modifiableModel)
+ }
+ }
+ }
+
+ private fun String.looksAsNonJvmLibraryName() = nonJvmSuffixes.any { it in this }
+
+ private fun resetLibraryKind(modifiableModel: LibraryEx.ModifiableModelEx) {
+ try {
+ val cls = LibraryImpl::class.java
+ // Don't use name-based lookup because field names are scrambled in IDEA Ultimate
+ for (field in cls.declaredFields) {
+ if (field.type == PersistentLibraryKind::class.java) {
+ field.isAccessible = true
+ field.set(modifiableModel, null)
+ return
+ }
+ }
+ LOG.info("Could not find field of type PersistentLibraryKind in LibraryImpl.class")
+ } catch (e: Exception) {
+ LOG.info("Failed to reset library kind", e)
+ }
+ }
+
+ companion object {
+ val LOG = Logger.getInstance(KotlinGradleLibraryDataService::class.java)
+
+ val nonJvmSuffixes = listOf("-common", "-js", "-native", "-kjsm")
+ }
+}
+
+fun detectPlatformByPlugin(moduleNode: DataNode<ModuleData>): TargetPlatformKind<*>? {
+ return when (moduleNode.platformPluginId) {
+ "kotlin-platform-jvm" -> TargetPlatformKind.Jvm[JvmTarget.JVM_1_6]
+ "kotlin-platform-js" -> TargetPlatformKind.JavaScript
+ "kotlin-platform-common" -> TargetPlatformKind.Common
+ else -> null
+ }
+}
+
+private fun detectPlatformByLibrary(moduleNode: DataNode<ModuleData>): TargetPlatformKind<*>? {
+ val detectedPlatforms =
+ mavenLibraryIdToPlatform.entries
+ .filter { moduleNode.getResolvedVersionByModuleData(KOTLIN_GROUP_ID, listOf(it.key)) != null }
+ .map { it.value }.distinct()
+ return detectedPlatforms.singleOrNull() ?: detectedPlatforms.firstOrNull { it != TargetPlatformKind.Common }
+}
+
+fun configureFacetByGradleModule(
+ moduleNode: DataNode<ModuleData>,
+ sourceSetName: String?,
+ ideModule: Module,
+ modelsProvider: IdeModifiableModelsProvider
+): KotlinFacet? {
+ if (!moduleNode.isResolved) return null
+
+ if (!moduleNode.hasKotlinPlugin) {
+ val facetModel = modelsProvider.getModifiableFacetModel(ideModule)
+ val facet = facetModel.getFacetByType(KotlinFacetType.TYPE_ID)
+ if (facet != null) {
+ facetModel.removeFacet(facet)
+ }
+ return null
+ }
+
+ val compilerVersion = moduleNode.findAll(BuildScriptClasspathData.KEY).firstOrNull()?.data?.let(::findKotlinPluginVersion)
+ ?: return null
+ val platformKind = detectPlatformByPlugin(moduleNode) ?: detectPlatformByLibrary(moduleNode)
+
+ val coroutinesProperty = CoroutineSupport.byCompilerArgument(
+ moduleNode.coroutines ?: findKotlinCoroutinesProperty(ideModule.project)
+ )
+
+ val kotlinFacet = ideModule.getOrCreateFacet(modelsProvider, false)
+ kotlinFacet.configureFacet(compilerVersion, coroutinesProperty, platformKind, modelsProvider)
+
+ ideModule.compilerArgumentsBySourceSet = moduleNode.compilerArgumentsBySourceSet
+ ideModule.sourceSetName = sourceSetName
+
+ val argsInfo = moduleNode.compilerArgumentsBySourceSet?.get(sourceSetName ?: "main")
+ if (argsInfo != null) {
+ configureFacetByCompilerArguments(kotlinFacet, argsInfo, modelsProvider)
+ }
+
+ with(kotlinFacet.configuration.settings) {
+ val sourceSetNameForImplementedModules = if (ideModule.getBuildSystemType() == AndroidGradle) null else sourceSetName
+ implementedModuleNames = getImplementedModuleNames(moduleNode, sourceSetNameForImplementedModules, ideModule.project)
+
+ productionOutputPath = getExplicitOutputPath(moduleNode, platformKind, "main")
+ testOutputPath = getExplicitOutputPath(moduleNode, platformKind, "test")
+ }
+
+ kotlinFacet.noVersionAutoAdvance()
+
+ return kotlinFacet
+}
+
+fun configureFacetByCompilerArguments(kotlinFacet: KotlinFacet, argsInfo: ArgsInfo, modelsProvider: IdeModifiableModelsProvider?) {
+ val currentCompilerArguments = argsInfo.currentArguments
+ val defaultCompilerArguments = argsInfo.defaultArguments
+ val dependencyClasspath = argsInfo.dependencyClasspath.map { PathUtil.toSystemIndependentName(it) }
+ if (modelsProvider != null && currentCompilerArguments.isNotEmpty()) {
+ parseCompilerArgumentsToFacet(currentCompilerArguments, defaultCompilerArguments, kotlinFacet, modelsProvider)
+ }
+ adjustClasspath(kotlinFacet, dependencyClasspath)
+}
+
+private fun getImplementedModuleNames(moduleNode: DataNode<ModuleData>, sourceSetName: String?, project: Project): List<String> {
+ val baseModuleNames = moduleNode.implementedModuleNames
+ if (baseModuleNames.isEmpty() || sourceSetName == null) return baseModuleNames
+ val delimiter = if(isQualifiedModuleNamesEnabled(project)) "." else "_"
+ return baseModuleNames.map { "$it$delimiter$sourceSetName" }
+}
+
+private fun getExplicitOutputPath(moduleNode: DataNode<ModuleData>, platformKind: TargetPlatformKind<*>?, sourceSet: String): String? {
+ if (platformKind !== TargetPlatformKind.JavaScript) return null
+ val k2jsArgumentList = moduleNode.compilerArgumentsBySourceSet?.get(sourceSet)?.currentArguments ?: return null
+ return K2JSCompilerArguments().apply { parseCommandLineArguments(k2jsArgumentList, this) }.outputFile
+}
+
+private fun adjustClasspath(kotlinFacet: KotlinFacet, dependencyClasspath: List<String>) {
+ if (dependencyClasspath.isEmpty()) return
+ val arguments = kotlinFacet.configuration.settings.compilerArguments as? K2JVMCompilerArguments ?: return
+ val fullClasspath = arguments.classpath?.split(File.pathSeparator) ?: emptyList()
+ if (fullClasspath.isEmpty()) return
+ val newClasspath = fullClasspath - dependencyClasspath
+ arguments.classpath = if (newClasspath.isNotEmpty()) newClasspath.joinToString(File.pathSeparator) else null
+}
+
+private val gradlePropertyFiles = listOf("local.properties", "gradle.properties")
+
+private fun findKotlinCoroutinesProperty(project: Project): String {
+ for (propertyFileName in gradlePropertyFiles) {
+ val propertyFile = project.baseDir.findChild(propertyFileName) ?: continue
+ val properties = Properties()
+ properties.load(propertyFile.inputStream)
+ properties.getProperty("kotlin.coroutines")?.let { return it }
+ }
+
+ return CoroutineSupport.getCompilerArgument(LanguageFeature.Coroutines.defaultState)
+}
diff --git a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinWithGradleConfigurator.kt.as33c4 b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinWithGradleConfigurator.kt.as33c4
new file mode 100644
index 00000000000..f7f671811ef
--- /dev/null
+++ b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinWithGradleConfigurator.kt.as33c4
@@ -0,0 +1,403 @@
+/*
+ * Copyright 2000-2018 JetBrains s.r.o. 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.configuration
+
+import com.intellij.codeInsight.CodeInsightUtilCore
+import com.intellij.codeInsight.daemon.impl.quickfix.OrderEntryFix
+import com.intellij.ide.actions.OpenFileAction
+import com.intellij.openapi.extensions.Extensions
+import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil
+import com.intellij.openapi.fileEditor.OpenFileDescriptor
+import com.intellij.openapi.module.Module
+import com.intellij.openapi.module.ModuleUtil
+import com.intellij.openapi.project.Project
+import com.intellij.openapi.projectRoots.Sdk
+import com.intellij.openapi.roots.DependencyScope
+import com.intellij.openapi.roots.ExternalLibraryDescriptor
+import com.intellij.openapi.roots.ModuleRootManager
+import com.intellij.openapi.ui.Messages
+import com.intellij.openapi.vfs.VfsUtil
+import com.intellij.openapi.vfs.WritingAccessProvider
+import com.intellij.psi.PsiElement
+import com.intellij.psi.PsiFile
+import com.intellij.psi.PsiManager
+import com.intellij.util.PathUtil
+import org.jetbrains.kotlin.config.ApiVersion
+import org.jetbrains.kotlin.config.CoroutineSupport
+import org.jetbrains.kotlin.config.LanguageFeature
+import org.jetbrains.kotlin.idea.facet.getRuntimeLibraryVersion
+import org.jetbrains.kotlin.idea.framework.ui.ConfigureDialogWithModulesAndVersion
+import org.jetbrains.kotlin.idea.quickfix.ChangeCoroutineSupportFix
+import org.jetbrains.kotlin.idea.util.application.executeCommand
+import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
+import org.jetbrains.kotlin.idea.util.application.runReadAction
+import org.jetbrains.kotlin.idea.versions.LibraryJarDescriptor
+import org.jetbrains.kotlin.idea.versions.getStdlibArtifactId
+import org.jetbrains.kotlin.psi.KtFile
+import org.jetbrains.plugins.gradle.util.GradleConstants
+import org.jetbrains.plugins.groovy.lang.psi.GroovyFile
+import java.io.File
+import java.util.*
+
+abstract class KotlinWithGradleConfigurator : KotlinProjectConfigurator {
+
+ override fun getStatus(moduleSourceRootGroup: ModuleSourceRootGroup): ConfigureKotlinStatus {
+ val module = moduleSourceRootGroup.baseModule
+ if (!isApplicable(module)) {
+ return ConfigureKotlinStatus.NON_APPLICABLE
+ }
+
+ if (moduleSourceRootGroup.sourceRootModules.all(::hasAnyKotlinRuntimeInScope)) {
+ return ConfigureKotlinStatus.CONFIGURED
+ }
+
+ val buildFiles = runReadAction {
+ listOf(
+ module.getBuildScriptPsiFile(),
+ module.project.getTopLevelBuildScriptPsiFile()
+ ).filterNotNull()
+ }
+
+ if (buildFiles.isEmpty()) {
+ return ConfigureKotlinStatus.NON_APPLICABLE
+ }
+
+ if (buildFiles.none { it.isConfiguredByAnyGradleConfigurator() }) {
+ return ConfigureKotlinStatus.CAN_BE_CONFIGURED
+ }
+
+ return ConfigureKotlinStatus.BROKEN
+ }
+
+ private fun PsiFile.isConfiguredByAnyGradleConfigurator(): Boolean {
+ return Extensions.getExtensions(KotlinProjectConfigurator.EP_NAME)
+ .filterIsInstance<KotlinWithGradleConfigurator>()
+ .any { it.isFileConfigured(this) }
+ }
+
+ protected open fun isApplicable(module: Module): Boolean =
+ module.getBuildSystemType() == Gradle
+
+ protected open fun getMinimumSupportedVersion() = "1.0.0"
+
+ protected fun PsiFile.isKtDsl() = this is KtFile
+
+ private fun isFileConfigured(buildScript: PsiFile): Boolean = with(getManipulator(buildScript)) {
+ isConfiguredWithOldSyntax(kotlinPluginName) || isConfigured(getKotlinPluginExpression(buildScript.isKtDsl()))
+ }
+
+ @JvmSuppressWildcards
+ override fun configure(project: Project, excludeModules: Collection<Module>) {
+ val dialog = ConfigureDialogWithModulesAndVersion(project, this, excludeModules, getMinimumSupportedVersion())
+
+ dialog.show()
+ if (!dialog.isOK) return
+
+ val collector = configureSilently(project, dialog.modulesToConfigure, dialog.kotlinVersion)
+ collector.showNotification()
+ }
+
+ fun configureSilently(project: Project, modules: List<Module>, version: String): NotificationMessageCollector {
+ return project.executeCommand("Configure Kotlin") {
+ val collector = createConfigureKotlinNotificationCollector(project)
+ val changedFiles = configureWithVersion(project, modules, version, collector)
+
+ for (file in changedFiles) {
+ OpenFileAction.openFile(file.virtualFile, project)
+ }
+ collector
+ }
+ }
+
+ fun configureWithVersion(
+ project: Project,
+ modulesToConfigure: List<Module>,
+ kotlinVersion: String,
+ collector: NotificationMessageCollector
+ ): HashSet<PsiFile> {
+ val filesToOpen = HashSet<PsiFile>()
+ val buildScript = project.getTopLevelBuildScriptPsiFile()
+ if (buildScript != null && canConfigureFile(buildScript)) {
+ val isModified = configureBuildScript(buildScript, true, kotlinVersion, collector)
+ if (isModified) {
+ filesToOpen.add(buildScript)
+ }
+ }
+
+ for (module in modulesToConfigure) {
+ val file = module.getBuildScriptPsiFile()
+ if (file != null && canConfigureFile(file)) {
+ configureModule(module, file, false, kotlinVersion, collector, filesToOpen)
+ } else {
+ showErrorMessage(project, "Cannot find build.gradle file for module " + module.name)
+ }
+ }
+ return filesToOpen
+ }
+
+ open fun configureModule(
+ module: Module,
+ file: PsiFile,
+ isTopLevelProjectFile: Boolean,
+ version: String,
+ collector: NotificationMessageCollector,
+ filesToOpen: MutableCollection<PsiFile>
+ ) {
+ val isModified = configureBuildScript(file, isTopLevelProjectFile, version, collector)
+ if (isModified) {
+ filesToOpen.add(file)
+ }
+ }
+
+ protected fun configureModuleBuildScript(file: PsiFile, version: String): Boolean {
+ val sdk = ModuleUtil.findModuleForPsiElement(file)?.let { ModuleRootManager.getInstance(it).sdk }
+ val jvmTarget = getJvmTarget(sdk, version)
+ return getManipulator(file).configureModuleBuildScript(
+ kotlinPluginName,
+ getKotlinPluginExpression(file.isKtDsl()),
+ getStdlibArtifactName(sdk, version),
+ version,
+ jvmTarget
+ )
+ }
+
+ protected open fun getStdlibArtifactName(sdk: Sdk?, version: String) = getStdlibArtifactId(sdk, version)
+
+ protected open fun getJvmTarget(sdk: Sdk?, version: String): String? = null
+
+ protected abstract val kotlinPluginName: String
+ protected abstract fun getKotlinPluginExpression(forKotlinDsl: Boolean): String
+
+ protected open fun addElementsToFile(
+ file: PsiFile,
+ isTopLevelProjectFile: Boolean,
+ version: String
+ ): Boolean {
+ if (!isTopLevelProjectFile) {
+ var wasModified = getManipulator(file).configureProjectBuildScript(kotlinPluginName, version)
+ wasModified = wasModified or configureModuleBuildScript(file, version)
+ return wasModified
+ }
+ return false
+ }
+
+ private fun configureBuildScript(
+ file: PsiFile,
+ isTopLevelProjectFile: Boolean,
+ version: String,
+ collector: NotificationMessageCollector
+ ): Boolean {
+ val isModified = file.project.executeWriteCommand("Configure ${file.name}", null) {
+ val isModified = addElementsToFile(file, isTopLevelProjectFile, version)
+
+ CodeInsightUtilCore.forcePsiPostprocessAndRestoreElement(file)
+ isModified
+ }
+
+ val virtualFile = file.virtualFile
+ if (virtualFile != null && isModified) {
+ collector.addMessage(virtualFile.path + " was modified")
+ }
+ return isModified
+ }
+
+ override fun updateLanguageVersion(
+ module: Module,
+ languageVersion: String?,
+ apiVersion: String?,
+ requiredStdlibVersion: ApiVersion,
+ forTests: Boolean
+ ) {
+ val runtimeUpdateRequired = getRuntimeLibraryVersion(module)?.let { ApiVersion.parse(it) }?.let { runtimeVersion ->
+ runtimeVersion < requiredStdlibVersion
+ } ?: false
+
+ if (runtimeUpdateRequired) {
+ Messages.showErrorDialog(
+ module.project,
+ "This language feature requires version $requiredStdlibVersion or later of the Kotlin runtime library. " +
+ "Please update the version in your build script.",
+ "Update Language Version"
+ )
+ return
+ }
+
+ val element = changeLanguageVersion(module, languageVersion, apiVersion, forTests)
+
+ element?.let {
+ OpenFileDescriptor(module.project, it.containingFile.virtualFile, it.textRange.startOffset).navigate(true)
+ }
+ }
+
+ override fun changeCoroutineConfiguration(module: Module, state: LanguageFeature.State) {
+ val runtimeUpdateRequired = state != LanguageFeature.State.DISABLED &&
+ (getRuntimeLibraryVersion(module)?.startsWith("1.0") ?: false)
+
+ if (runtimeUpdateRequired) {
+ Messages.showErrorDialog(
+ module.project,
+ "Coroutines support requires version 1.1 or later of the Kotlin runtime library. " +
+ "Please update the version in your build script.",
+ ChangeCoroutineSupportFix.getFixText(state)
+ )
+ return
+ }
+
+ val element = changeCoroutineConfiguration(module, CoroutineSupport.getCompilerArgument(state))
+ if (element != null) {
+ OpenFileDescriptor(module.project, element.containingFile.virtualFile, element.textRange.startOffset).navigate(true)
+ }
+ }
+
+ override fun addLibraryDependency(
+ module: Module,
+ element: PsiElement,
+ library: ExternalLibraryDescriptor,
+ libraryJarDescriptors: List<LibraryJarDescriptor>
+ ) {
+ val scope = OrderEntryFix.suggestScopeByLocation(module, element)
+ KotlinWithGradleConfigurator.addKotlinLibraryToModule(module, scope, library)
+ }
+
+ companion object {
+ fun getManipulator(file: PsiFile, preferNewSyntax: Boolean = true): GradleBuildScriptManipulator<*> = when (file) {
+ is KtFile -> KotlinBuildScriptManipulator(file, preferNewSyntax)
+ is GroovyFile -> GroovyBuildScriptManipulator(file, preferNewSyntax)
+ else -> error("Unknown build script file type!")
+ }
+
+ val GROUP_ID = "org.jetbrains.kotlin"
+ val GRADLE_PLUGIN_ID = "kotlin-gradle-plugin"
+
+ val CLASSPATH = "classpath \"$GROUP_ID:$GRADLE_PLUGIN_ID:\$kotlin_version\""
+
+ private val KOTLIN_BUILD_SCRIPT_NAME = "build.gradle.kts"
+ private val KOTLIN_SETTINGS_SCRIPT_NAME = "settings.gradle.kts"
+
+ fun getGroovyDependencySnippet(artifactName: String, scope: String, withVersion: Boolean) =
+ "$scope \"org.jetbrains.kotlin:$artifactName${if (withVersion) ":\$kotlin_version" else ""}\""
+
+ fun getGroovyApplyPluginDirective(pluginName: String) = "apply plugin: '$pluginName'"
+
+ fun addKotlinLibraryToModule(module: Module, scope: DependencyScope, libraryDescriptor: ExternalLibraryDescriptor) {
+ val buildScript = module.getBuildScriptPsiFile() ?: return
+ if (!canConfigureFile(buildScript)) {
+ return
+ }
+
+ getManipulator(buildScript)
+ .addKotlinLibraryToModuleBuildScript(scope, libraryDescriptor, module.getBuildSystemType() == AndroidGradle)
+
+ buildScript.virtualFile?.let {
+ createConfigureKotlinNotificationCollector(buildScript.project)
+ .addMessage(it.path + " was modified")
+ .showNotification()
+ }
+ }
+
+ fun changeCoroutineConfiguration(module: Module, coroutineOption: String): PsiElement? = changeBuildGradle(module) {
+ getManipulator(it).changeCoroutineConfiguration(coroutineOption)
+ }
+
+ fun changeLanguageVersion(module: Module, languageVersion: String?, apiVersion: String?, forTests: Boolean) =
+ changeBuildGradle(module) { buildScriptFile ->
+ val manipulator = getManipulator(buildScriptFile)
+ var result: PsiElement? = null
+ if (languageVersion != null) {
+ result = manipulator.changeLanguageVersion(languageVersion, forTests)
+ }
+
+ if (apiVersion != null) {
+ result = manipulator.changeApiVersion(apiVersion, forTests)
+ }
+
+ result
+ }
+
+ private fun changeBuildGradle(module: Module, body: (PsiFile) -> PsiElement?): PsiElement? {
+ val buildScriptFile = module.getBuildScriptPsiFile()
+ if (buildScriptFile != null && canConfigureFile(buildScriptFile)) {
+ return buildScriptFile.project.executeWriteCommand("Change build.gradle configuration", null) {
+ body(buildScriptFile)
+ }
+ }
+ return null
+ }
+
+ fun getKotlinStdlibVersion(module: Module): String? {
+ return module.getBuildScriptPsiFile()?.let {
+ getManipulator(it).getKotlinStdlibVersion()
+ }
+ }
+
+ private fun canConfigureFile(file: PsiFile): Boolean = WritingAccessProvider.isPotentiallyWritable(file.virtualFile, null)
+
+ private fun Module.getBuildScriptPsiFile() =
+ getBuildScriptFile(GradleConstants.DEFAULT_SCRIPT_NAME, KOTLIN_BUILD_SCRIPT_NAME)?.getPsiFile(project)
+
+ fun Module.getBuildScriptSettingsPsiFile() =
+ getBuildScriptSettingsFile(GradleConstants.SETTINGS_FILE_NAME, KOTLIN_SETTINGS_SCRIPT_NAME)?.getPsiFile(project)
+
+ private fun Project.getTopLevelBuildScriptPsiFile() = basePath?.let {
+ findBuildGradleFile(it, GradleConstants.DEFAULT_SCRIPT_NAME, KOTLIN_BUILD_SCRIPT_NAME)?.getPsiFile(this)
+ }
+
+ fun Module.getTopLevelBuildScriptSettingsPsiFile() =
+ ExternalSystemApiUtil.getExternalRootProjectPath(this)?.let { externalProjectPath ->
+ findBuildGradleFile(externalProjectPath, GradleConstants.SETTINGS_FILE_NAME, KOTLIN_SETTINGS_SCRIPT_NAME)?.getPsiFile(project)
+ }
+
+ private fun Module.getBuildScriptFile(vararg fileNames: String): File? {
+ val moduleDir = File(moduleFilePath).parent
+ findBuildGradleFile(moduleDir, *fileNames)?.let {
+ return it
+ }
+
+ ModuleRootManager.getInstance(this).contentRoots.forEach { root ->
+ findBuildGradleFile(root.path, *fileNames)?.let {
+ return it
+ }
+ }
+
+ ExternalSystemApiUtil.getExternalProjectPath(this)?.let { externalProjectPath ->
+ findBuildGradleFile(externalProjectPath, *fileNames)?.let {
+ return it
+ }
+ }
+
+ return null
+ }
+
+ private fun Module.getBuildScriptSettingsFile(vararg fileNames: String): File? {
+ ExternalSystemApiUtil.getExternalProjectPath(this)?.let { externalProjectPath ->
+ return generateSequence(externalProjectPath) {
+ PathUtil.getParentPath(it).let { if (it.isBlank()) null else it }
+ }.mapNotNull {
+ findBuildGradleFile(it, *fileNames)
+ }.firstOrNull()
+ }
+
+ return null
+ }
+
+ private fun findBuildGradleFile(path: String, vararg fileNames: String): File? =
+ fileNames.asSequence().map { File(path + "/" + it) }.firstOrNull { it.exists() }
+
+ private fun File.getPsiFile(project: Project) = VfsUtil.findFileByIoFile(this, true)?.let {
+ PsiManager.getInstance(project).findFile(it)
+ }
+
+ private fun showErrorMessage(project: Project, message: String?) {
+ Messages.showErrorDialog(
+ project,
+ "<html>Couldn't configure kotlin-gradle plugin automatically.<br/>" +
+ (if (message != null) message + "<br/>" else "") +
+ "<br/>See manual installation instructions <a href=\"https://kotlinlang.org/docs/reference/using-gradle.html\">here</a>.</html>",
+ "Configure Kotlin-Gradle Plugin"
+ )
+ }
+ }
+}
diff --git a/idea/src/org/jetbrains/kotlin/idea/actions/JavaToKotlinAction.kt.as33c4 b/idea/src/org/jetbrains/kotlin/idea/actions/JavaToKotlinAction.kt.as33c4
new file mode 100644
index 00000000000..dc0c28f012f
--- /dev/null
+++ b/idea/src/org/jetbrains/kotlin/idea/actions/JavaToKotlinAction.kt.as33c4
@@ -0,0 +1,231 @@
+/*
+ * 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.actions
+
+import com.intellij.codeInsight.navigation.NavigationUtil
+import com.intellij.ide.highlighter.JavaFileType
+import com.intellij.ide.scratch.ScratchFileService
+import com.intellij.ide.scratch.ScratchRootType
+import com.intellij.openapi.actionSystem.AnAction
+import com.intellij.openapi.actionSystem.AnActionEvent
+import com.intellij.openapi.actionSystem.CommonDataKeys
+import com.intellij.openapi.command.CommandProcessor
+import com.intellij.openapi.fileEditor.FileDocumentManager
+import com.intellij.openapi.fileEditor.FileEditorManager
+import com.intellij.openapi.progress.ProgressManager
+import com.intellij.openapi.project.Project
+import com.intellij.openapi.ui.Messages
+import com.intellij.openapi.ui.ex.MessagesEx
+import com.intellij.openapi.vfs.VfsUtilCore
+import com.intellij.openapi.vfs.VirtualFile
+import com.intellij.openapi.vfs.VirtualFileVisitor
+import com.intellij.psi.PsiDocumentManager
+import com.intellij.psi.PsiErrorElement
+import com.intellij.psi.PsiJavaFile
+import com.intellij.psi.PsiManager
+import com.intellij.psi.search.FileTypeIndex
+import com.intellij.psi.search.GlobalSearchScopesCore
+import com.intellij.psi.util.PsiTreeUtil
+import org.jetbrains.kotlin.idea.KotlinFileType
+import org.jetbrains.kotlin.idea.j2k.IdeaJavaToKotlinServices
+import org.jetbrains.kotlin.idea.j2k.J2kPostProcessor
+import org.jetbrains.kotlin.idea.refactoring.toPsiFile
+import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
+import org.jetbrains.kotlin.idea.util.application.progressIndicatorNullable
+import org.jetbrains.kotlin.idea.util.application.runReadAction
+import org.jetbrains.kotlin.j2k.ConverterSettings
+import org.jetbrains.kotlin.j2k.JavaToKotlinConverter
+import org.jetbrains.kotlin.psi.KtFile
+import java.io.File
+import java.io.IOException
+import java.util.*
+
+class JavaToKotlinAction : AnAction() {
+ companion object {
+ private fun uniqueKotlinFileName(javaFile: VirtualFile): String {
+ val ioFile = File(javaFile.path.replace('/', File.separatorChar))
+
+ var i = 0
+ while (true) {
+ val fileName = javaFile.nameWithoutExtension + (if (i > 0) i else "") + ".kt"
+ if (!ioFile.resolveSibling(fileName).exists()) return fileName
+ i++
+ }
+ }
+
+ val title = "Convert Java to Kotlin"
+
+ private fun saveResults(javaFiles: List<PsiJavaFile>, convertedTexts: List<String>): List<VirtualFile> {
+ val result = ArrayList<VirtualFile>()
+ for ((psiFile, text) in javaFiles.zip(convertedTexts)) {
+ try {
+ val document = PsiDocumentManager.getInstance(psiFile.project).getDocument(psiFile)
+ if (document == null) {
+ MessagesEx.error(psiFile.project, "Failed to save conversion result: couldn't find document for " + psiFile.name).showLater()
+ continue
+ }
+ document.replaceString(0, document.textLength, text)
+ FileDocumentManager.getInstance().saveDocument(document)
+
+ val virtualFile = psiFile.virtualFile
+ if (ScratchRootType.getInstance().containsFile(virtualFile)) {
+ val mapping = ScratchFileService.getInstance().scratchesMapping
+ mapping.setMapping(virtualFile, KotlinFileType.INSTANCE.language)
+ }
+ else {
+ val fileName = uniqueKotlinFileName(virtualFile)
+ virtualFile.rename(this, fileName)
+ }
+ }
+ catch (e: IOException) {
+ MessagesEx.error(psiFile.project, e.message ?: "").showLater()
+ }
+ }
+ return result
+ }
+
+ fun convertFiles(javaFiles: List<PsiJavaFile>, project: Project,
+ enableExternalCodeProcessing: Boolean = true,
+ askExternalCodeProcessing: Boolean = true): List<KtFile> {
+ var converterResult: JavaToKotlinConverter.FilesResult? = null
+ fun convert() {
+ val converter = JavaToKotlinConverter(project, ConverterSettings.defaultSettings, IdeaJavaToKotlinServices)
+ converterResult = converter.filesToKotlin(
+ javaFiles, J2kPostProcessor(formatCode = true),
+ ProgressManager.getInstance().progressIndicatorNullable!!
+ )
+ }
+
+
+ if (!ProgressManager.getInstance().runProcessWithProgressSynchronously(
+ ::convert,
+ title,
+ true,
+ project)) return emptyList()
+
+
+ var externalCodeUpdate: (() -> Unit)? = null
+
+ if (enableExternalCodeProcessing && converterResult!!.externalCodeProcessing != null) {
+ val question = "Some code in the rest of your project may require corrections after performing this conversion. Do you want to find such code and correct it too?"
+ if (!askExternalCodeProcessing || (Messages.showOkCancelDialog(project, question, title, Messages.getQuestionIcon()) == Messages.OK)) {
+ ProgressManager.getInstance().runProcessWithProgressSynchronously(
+ {
+ runReadAction {
+ externalCodeUpdate = converterResult!!.externalCodeProcessing!!.prepareWriteOperation(
+ ProgressManager.getInstance().progressIndicatorNullable!!
+ )
+ }
+ },
+ title,
+ true,
+ project)
+ }
+ }
+
+ return project.executeWriteCommand("Convert files from Java to Kotlin", null) {
+ CommandProcessor.getInstance().markCurrentCommandAsGlobal(project)
+
+ val newFiles = saveResults(javaFiles, converterResult!!.results)
+
+ externalCodeUpdate?.invoke()
+
+ PsiDocumentManager.getInstance(project).commitAllDocuments()
+
+ newFiles.singleOrNull()?.let {
+ FileEditorManager.getInstance(project).openFile(it, true)
+ }
+
+ newFiles.map { it.toPsiFile(project) as KtFile }
+ }
+ }
+ }
+
+ override fun actionPerformed(e: AnActionEvent) {
+ val javaFiles = selectedJavaFiles(e).filter { it.isWritable }.toList()
+ val project = CommonDataKeys.PROJECT.getData(e.dataContext)!!
+
+ val firstSyntaxError = javaFiles.asSequence().map { PsiTreeUtil.findChildOfType(it, PsiErrorElement::class.java) }.firstOrNull()
+
+ if (firstSyntaxError != null) {
+ val count = javaFiles.filter { PsiTreeUtil.hasErrorElements(it) }.count()
+ val question = firstSyntaxError.containingFile.name +
+ (if (count > 1) " and ${count - 1} more Java files" else " file") +
+ " contain syntax errors, the conversion result may be incorrect"
+
+ val okText = "Investigate Errors"
+ val cancelText = "Proceed with Conversion"
+ if (Messages.showOkCancelDialog(
+ project,
+ question,
+ title,
+ okText,
+ cancelText,
+ Messages.getWarningIcon()
+ ) == Messages.OK) {
+ NavigationUtil.activateFileWithPsiElement(firstSyntaxError.navigationElement)
+ return
+ }
+ }
+
+ convertFiles(javaFiles, project)
+ }
+
+ override fun update(e: AnActionEvent) {
+ val virtualFiles = e.getData(CommonDataKeys.VIRTUAL_FILE_ARRAY) ?: return
+ val project = e.project ?: return
+
+ e.presentation.isEnabled = isAnyJavaFileSelected(project, virtualFiles)
+ }
+
+ private fun isAnyJavaFileSelected(project: Project, files: Array<VirtualFile>): Boolean {
+
+ val filesScope = GlobalSearchScopesCore.directoriesScope(project, true, *files)
+ val potentialJavaFiles = FileTypeIndex.getFiles(JavaFileType.INSTANCE, filesScope)
+
+ if (potentialJavaFiles.isEmpty()) return false
+
+ val manager = PsiManager.getInstance(project)
+ return potentialJavaFiles.any { manager.findFile(it) is PsiJavaFile && it.isWritable }
+ }
+
+ private fun selectedJavaFiles(e: AnActionEvent): Sequence<PsiJavaFile> {
+ val virtualFiles = e.getData(CommonDataKeys.VIRTUAL_FILE_ARRAY) ?: return sequenceOf()
+ val project = e.project ?: return sequenceOf()
+ return allJavaFiles(virtualFiles, project)
+ }
+
+ private fun allJavaFiles(filesOrDirs: Array<VirtualFile>, project: Project): Sequence<PsiJavaFile> {
+ val manager = PsiManager.getInstance(project)
+ return allFiles(filesOrDirs)
+ .asSequence()
+ .mapNotNull { manager.findFile(it) as? PsiJavaFile }
+ }
+
+ private fun allFiles(filesOrDirs: Array<VirtualFile>): Collection<VirtualFile> {
+ val result = ArrayList<VirtualFile>()
+ for (file in filesOrDirs) {
+ VfsUtilCore.visitChildrenRecursively(file, object : VirtualFileVisitor<Unit>() {
+ override fun visitFile(file: VirtualFile): Boolean {
+ result.add(file)
+ return true
+ }
+ })
+ }
+ return result
+ }
+}