as42: Additional fixes for AS42 and 202 platform

This commit is contained in:
Nikolay Krasko
2020-08-31 20:15:40 +03:00
committed by Konstantin Tskhovrebov
parent 2df030f583
commit 5ba70b2cbb
3 changed files with 541 additions and 0 deletions
@@ -0,0 +1,55 @@
/*
* 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.cli.jvm.compiler;
import com.intellij.DynamicBundle;
import com.intellij.codeInsight.ContainerProvider;
import com.intellij.codeInsight.runner.JavaMainMethodProvider;
import com.intellij.core.JavaCoreApplicationEnvironment;
import com.intellij.lang.MetaLanguage;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.vfs.VirtualFileSystem;
import com.intellij.psi.FileContextProvider;
import com.intellij.psi.augment.PsiAugmentProvider;
import com.intellij.psi.compiled.ClassFileDecompilers;
import com.intellij.psi.meta.MetaDataContributor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.cli.jvm.modules.CoreJrtFileSystem;
public class KotlinCoreApplicationEnvironment extends JavaCoreApplicationEnvironment {
public static KotlinCoreApplicationEnvironment create(@NotNull Disposable parentDisposable, boolean unitTestMode) {
KotlinCoreApplicationEnvironment environment = new KotlinCoreApplicationEnvironment(parentDisposable, unitTestMode);
registerExtensionPoints();
return environment;
}
private KotlinCoreApplicationEnvironment(@NotNull Disposable parentDisposable, boolean unitTestMode) {
super(parentDisposable, unitTestMode);
}
private static void registerExtensionPoints() {
registerApplicationExtensionPoint(DynamicBundle.LanguageBundleEP.EP_NAME, DynamicBundle.LanguageBundleEP.class);
registerApplicationExtensionPoint(FileContextProvider.EP_NAME, FileContextProvider.class);
registerApplicationExtensionPoint(MetaDataContributor.EP_NAME, MetaDataContributor.class);
registerApplicationExtensionPoint(PsiAugmentProvider.EP_NAME, PsiAugmentProvider.class);
registerApplicationExtensionPoint(JavaMainMethodProvider.EP_NAME, JavaMainMethodProvider.class);
registerApplicationExtensionPoint(ContainerProvider.EP_NAME, ContainerProvider.class);
registerApplicationExtensionPoint(ClassFileDecompilers.getInstance().EP_NAME, ClassFileDecompilers.Decompiler.class);
registerApplicationExtensionPoint(MetaLanguage.EP_NAME, MetaLanguage.class);
IdeaExtensionPoints.INSTANCE.registerVersionSpecificAppExtensionPoints(Extensions.getRootArea());
}
@Nullable
@Override
protected VirtualFileSystem createJrtFileSystem() {
return new CoreJrtFileSystem();
}
}
@@ -0,0 +1,259 @@
/*
* 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.ide.actions.CreateFileFromTemplateAction
import com.intellij.ide.actions.CreateFileFromTemplateDialog
import com.intellij.ide.actions.CreateFromTemplateAction
import com.intellij.ide.fileTemplates.FileTemplate
import com.intellij.ide.fileTemplates.FileTemplateManager
import com.intellij.ide.fileTemplates.actions.AttributesDefaults
import com.intellij.ide.fileTemplates.ui.CreateFromTemplateDialog
import com.intellij.openapi.actionSystem.DataContext
import com.intellij.openapi.actionSystem.LangDataKeys
import com.intellij.openapi.actionSystem.PlatformDataKeys
import com.intellij.openapi.editor.LogicalPosition
import com.intellij.openapi.extensions.ExtensionPointName
import com.intellij.openapi.fileEditor.FileEditorManager
import com.intellij.openapi.module.Module
import com.intellij.openapi.module.ModuleUtilCore
import com.intellij.openapi.project.DumbAware
import com.intellij.openapi.project.DumbService
import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.ProjectRootManager
import com.intellij.openapi.ui.InputValidatorEx
import com.intellij.psi.PsiDirectory
import com.intellij.psi.PsiFile
import com.intellij.util.IncorrectOperationException
import org.jetbrains.annotations.TestOnly
import org.jetbrains.kotlin.idea.KotlinBundle
import org.jetbrains.kotlin.idea.KotlinFileType
import org.jetbrains.kotlin.idea.KotlinIcons
import org.jetbrains.kotlin.idea.statistics.FUSEventGroups
import org.jetbrains.kotlin.idea.statistics.KotlinFUSLogger
import org.jetbrains.kotlin.idea.util.application.runWriteAction
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.parsing.KotlinParserDefinition.Companion.STD_SCRIPT_SUFFIX
import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtNamedDeclaration
import java.util.*
class NewKotlinFileAction : CreateFileFromTemplateAction(
KotlinBundle.message("action.new.file.text"),
KotlinBundle.message("action.new.file.description"),
KotlinFileType.INSTANCE.icon
), DumbAware {
override fun postProcess(createdElement: PsiFile, templateName: String?, customProperties: Map<String, String>?) {
super.postProcess(createdElement, templateName, customProperties)
val module = ModuleUtilCore.findModuleForPsiElement(createdElement!!)
if (createdElement is KtFile) {
if (module != null) {
for (hook in NewKotlinFileHook.EP_NAME.extensions) {
hook.postProcess(createdElement, module)
}
}
val ktClass = createdElement.declarations.singleOrNull() as? KtNamedDeclaration
if (ktClass != null) {
CreateFromTemplateAction.moveCaretAfterNameIdentifier(ktClass)
} else {
val editor = FileEditorManager.getInstance(createdElement.project).selectedTextEditor ?: return
if (editor.document == createdElement.viewProvider.document) {
val lineCount = editor.document.lineCount
if (lineCount > 0) {
editor.caretModel.moveToLogicalPosition(LogicalPosition(lineCount - 1, 0))
}
}
}
}
}
override fun buildDialog(project: Project, directory: PsiDirectory, builder: CreateFileFromTemplateDialog.Builder) {
builder.setTitle(KotlinBundle.message("action.new.file.dialog.title"))
.addKind(
KotlinBundle.message("action.new.file.dialog.file.title"),
KotlinFileType.INSTANCE.icon,
"Kotlin File"
)
.addKind(
KotlinBundle.message("action.new.file.dialog.class.title"),
KotlinIcons.CLASS,
"Kotlin Class"
)
.addKind(
KotlinBundle.message("action.new.file.dialog.interface.title"),
KotlinIcons.INTERFACE,
"Kotlin Interface"
)
.addKind(
KotlinBundle.message("action.new.file.dialog.enum.title"),
KotlinIcons.ENUM,
"Kotlin Enum"
)
.addKind(
KotlinBundle.message("action.new.file.dialog.object.title"),
KotlinIcons.OBJECT,
"Kotlin Object"
)
builder.setValidator(NameValidator)
}
override fun getActionName(directory: PsiDirectory, newName: String, templateName: String): String =
KotlinBundle.message("action.new.file.text")
override fun isAvailable(dataContext: DataContext): Boolean {
if (super.isAvailable(dataContext)) {
val ideView = LangDataKeys.IDE_VIEW.getData(dataContext)!!
val project = PlatformDataKeys.PROJECT.getData(dataContext)!!
val projectFileIndex = ProjectRootManager.getInstance(project).fileIndex
return ideView.directories.any { projectFileIndex.isInSourceContent(it.virtualFile) }
}
return false
}
override fun hashCode(): Int = 0
override fun equals(other: Any?): Boolean = other is NewKotlinFileAction
override fun startInWriteAction() = false
override fun createFileFromTemplate(name: String, template: FileTemplate, dir: PsiDirectory) =
createFileFromTemplateWithStat(name, template, dir)
companion object {
private object NameValidator : InputValidatorEx {
override fun getErrorText(inputString: String): String? {
if (inputString.trim().isEmpty()) {
return KotlinBundle.message("action.new.file.error.empty.name")
}
val parts: List<String> = inputString.split(*FQNAME_SEPARATORS)
if (parts.any { it.trim().isEmpty() }) {
return KotlinBundle.message("action.new.file.error.empty.name.part")
}
return null
}
override fun checkInput(inputString: String): Boolean = true
override fun canClose(inputString: String): Boolean = getErrorText(inputString) == null
}
@get:TestOnly
val nameValidator: InputValidatorEx
get() = NameValidator
private fun findOrCreateTarget(dir: PsiDirectory, name: String, directorySeparators: CharArray): Pair<String, PsiDirectory> {
var className = removeKotlinExtensionIfPresent(name)
var targetDir = dir
for (splitChar in directorySeparators) {
if (splitChar in className) {
val names = className.trim().split(splitChar)
for (dirName in names.dropLast(1)) {
targetDir = targetDir.findSubdirectory(dirName) ?: runWriteAction {
targetDir.createSubdirectory(dirName)
}
}
className = names.last()
break
}
}
return Pair(className, targetDir)
}
private fun removeKotlinExtensionIfPresent(name: String): String = when {
name.endsWith(".$KOTLIN_WORKSHEET_EXTENSION") -> name.removeSuffix(".$KOTLIN_WORKSHEET_EXTENSION")
name.endsWith(".$STD_SCRIPT_SUFFIX") -> name.removeSuffix(".$STD_SCRIPT_SUFFIX")
name.endsWith(".${KotlinFileType.EXTENSION}") -> name.removeSuffix(".${KotlinFileType.EXTENSION}")
else -> name
}
private fun createFromTemplate(dir: PsiDirectory, className: String, template: FileTemplate): PsiFile? {
val project = dir.project
val defaultProperties = FileTemplateManager.getInstance(project).defaultProperties
val properties = Properties(defaultProperties)
val element = try {
CreateFromTemplateDialog(
project, dir, template,
AttributesDefaults(className).withFixedName(true),
properties
).create()
} catch (e: IncorrectOperationException) {
throw e
} catch (e: Exception) {
LOG.error(e)
return null
}
return element?.containingFile
}
private val FILE_SEPARATORS = charArrayOf('/', '\\')
private val FQNAME_SEPARATORS = charArrayOf('/', '\\', '.')
fun createFileFromTemplateWithStat(name: String, template: FileTemplate, dir: PsiDirectory): PsiFile? {
KotlinFUSLogger.log(FUSEventGroups.NewFileTemplate, template.name)
return createFileFromTemplate(name, template, dir)
}
fun createFileFromTemplate(name: String, template: FileTemplate, dir: PsiDirectory): PsiFile? {
val directorySeparators = when (template.name) {
"Kotlin File" -> FILE_SEPARATORS
else -> FQNAME_SEPARATORS
}
val (className, targetDir) = findOrCreateTarget(dir, name, directorySeparators)
val service = DumbService.getInstance(dir.project)
service.isAlternativeResolveEnabled = true
try {
val psiFile = createFromTemplate(targetDir, className, template)
if (psiFile is KtFile) {
val singleClass = psiFile.declarations.singleOrNull() as? KtClass
if (singleClass != null && !singleClass.isEnum() && !singleClass.isInterface() && name.contains("Abstract")) {
runWriteAction {
singleClass.addModifier(KtTokens.ABSTRACT_KEYWORD)
}
}
}
return psiFile
} finally {
service.isAlternativeResolveEnabled = false
}
}
}
}
abstract class NewKotlinFileHook {
companion object {
val EP_NAME: ExtensionPointName<NewKotlinFileHook> =
ExtensionPointName.create<NewKotlinFileHook>("org.jetbrains.kotlin.newFileHook")
}
abstract fun postProcess(createdElement: KtFile, module: Module)
}
@@ -0,0 +1,227 @@
/*
* 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.conversion.*
import com.intellij.conversion.impl.ConversionContextImpl
import com.intellij.conversion.impl.ModuleSettingsImpl
import com.intellij.openapi.roots.ExternalProjectSystemRegistry
import com.intellij.openapi.roots.OrderRootType
import com.intellij.openapi.roots.impl.ContentEntryImpl
import com.intellij.openapi.roots.impl.SourceFolderImpl
import com.intellij.openapi.roots.impl.libraries.ApplicationLibraryTable
import com.intellij.openapi.roots.impl.libraries.LibraryEx
import com.intellij.openapi.roots.impl.libraries.LibraryImpl
import com.intellij.openapi.roots.libraries.Library
import com.intellij.openapi.roots.libraries.LibraryKind
import com.intellij.openapi.roots.libraries.LibraryTablesRegistrar
import com.intellij.openapi.roots.libraries.PersistentLibraryKind
import com.intellij.openapi.vfs.JarFileSystem
import com.intellij.openapi.vfs.VirtualFile
import org.jdom.Element
import org.jetbrains.jps.model.JpsElement
import org.jetbrains.jps.model.JpsElementFactory
import org.jetbrains.jps.model.java.JavaResourceRootType
import org.jetbrains.jps.model.java.JavaSourceRootType
import org.jetbrains.jps.model.module.JpsModuleSourceRootType
import org.jetbrains.jps.model.module.JpsTypedModuleSourceRoot
import org.jetbrains.jps.model.serialization.facet.JpsFacetSerializer
import org.jetbrains.jps.model.serialization.library.JpsLibraryTableSerializer
import org.jetbrains.jps.model.serialization.module.JpsModuleRootModelSerializer.*
import org.jetbrains.kotlin.config.getFacetPlatformByConfigurationElement
import org.jetbrains.kotlin.idea.KotlinBundle
import org.jetbrains.kotlin.idea.core.util.toVirtualFile
import org.jetbrains.kotlin.idea.facet.KotlinFacetType
import org.jetbrains.kotlin.idea.framework.JavaRuntimeDetectionUtil
import org.jetbrains.kotlin.idea.framework.JsLibraryStdDetectionUtil
import org.jetbrains.kotlin.idea.framework.getLibraryJar
import org.jetbrains.kotlin.platform.CommonPlatforms
import org.jetbrains.kotlin.platform.TargetPlatform
import org.jetbrains.kotlin.platform.isCommon
import org.jetbrains.kotlin.platform.js.JsPlatforms
import org.jetbrains.kotlin.platform.jvm.JvmPlatforms
import org.jetbrains.kotlin.platform.jvm.isJvm
import org.jetbrains.kotlin.utils.PathUtil
class KotlinNonJvmSourceRootConverterProvider : ConverterProvider("kotlin-non-jvm-source-roots") {
companion object {
private val rootTypesToMigrate: List<JpsModuleSourceRootType<*>> = listOf(
JavaSourceRootType.SOURCE,
JavaSourceRootType.TEST_SOURCE,
JavaResourceRootType.RESOURCE,
JavaResourceRootType.TEST_RESOURCE
)
// TODO(dsavvinov): review how it behaves in HMPP environment
private val PLATFORM_TO_STDLIB_DETECTORS: Map<TargetPlatform, (Array<VirtualFile>) -> Boolean> = mapOf(
JvmPlatforms.unspecifiedJvmPlatform to { roots: Array<VirtualFile> -> JavaRuntimeDetectionUtil.getRuntimeJar(roots.toList()) != null },
JsPlatforms.defaultJsPlatform to { roots: Array<VirtualFile> -> JsLibraryStdDetectionUtil.getJsStdLibJar(roots.toList()) != null },
CommonPlatforms.defaultCommonPlatform to { roots: Array<VirtualFile> ->
getLibraryJar(roots, PathUtil.KOTLIN_STDLIB_COMMON_JAR_PATTERN) != null
}
)
}
sealed class LibInfo {
class ByXml(
private val element: Element,
private val conversionContext: ConversionContext,
private val moduleSettings: ModuleSettings
) : LibInfo() {
override val explicitKind: PersistentLibraryKind<*>?
get() = LibraryKind.findById(element.getAttributeValue("type")) as? PersistentLibraryKind<*>
override fun getRoots(): Array<VirtualFile> {
val contextImpl = conversionContext as? ConversionContextImpl ?: return VirtualFile.EMPTY_ARRAY
val moduleSettingsImpl = moduleSettings as? ModuleSettingsImpl ?: return VirtualFile.EMPTY_ARRAY
return contextImpl
.getClassRoots(element, moduleSettingsImpl)
.mapNotNull { it.toVirtualFile()?.let { file -> JarFileSystem.getInstance().getJarRootForLocalFile(file) } }
.toTypedArray()
}
}
class ByLibrary(private val library: Library) : LibInfo() {
override val explicitKind: PersistentLibraryKind<*>?
get() = (library as? LibraryEx)?.kind
override fun getRoots(): Array<VirtualFile> = library.getFiles(OrderRootType.CLASSES)
}
abstract val explicitKind: PersistentLibraryKind<*>?
abstract fun getRoots(): Array<VirtualFile>
val stdlibPlatform: TargetPlatform? by lazy {
val roots = getRoots()
for ((platform, detector) in PLATFORM_TO_STDLIB_DETECTORS) {
if (detector.invoke(roots)) {
return@lazy platform
}
}
return@lazy null
}
}
class ConverterImpl(private val context: ConversionContext) : ProjectConverter() {
private val projectLibrariesByName by lazy {
context.projectLibrariesSettings.projectLibraries.groupBy { it.getAttributeValue(JpsLibraryTableSerializer.NAME_ATTRIBUTE) }
}
private fun findGlobalLibrary(name: String) = ApplicationLibraryTable.getApplicationTable().getLibraryByName(name)
private fun findProjectLibrary(name: String) = projectLibrariesByName[name]?.firstOrNull()
private fun createLibInfo(orderEntryElement: Element, moduleSettings: ModuleSettings): LibInfo? {
return when (orderEntryElement.getAttributeValue("type")) {
MODULE_LIBRARY_TYPE -> {
orderEntryElement.getChild(LIBRARY_TAG)?.let { LibInfo.ByXml(it, context, moduleSettings) }
}
LIBRARY_TYPE -> {
val libraryName = orderEntryElement.getAttributeValue(NAME_ATTRIBUTE) ?: return null
when (orderEntryElement.getAttributeValue(LEVEL_ATTRIBUTE)) {
LibraryTablesRegistrar.PROJECT_LEVEL ->
findProjectLibrary(libraryName)?.let { LibInfo.ByXml(it, context, moduleSettings) }
LibraryTablesRegistrar.APPLICATION_LEVEL ->
findGlobalLibrary(libraryName)?.let { LibInfo.ByLibrary(it) }
else ->
null
}
}
else -> null
}
}
override fun createModuleFileConverter(): ConversionProcessor<ModuleSettings> {
return object : ConversionProcessor<ModuleSettings>() {
private fun ModuleSettings.detectPlatformByFacet() =
getFacetElement(KotlinFacetType.ID)
?.getChild(JpsFacetSerializer.CONFIGURATION_TAG)
?.getFacetPlatformByConfigurationElement()
private fun ModuleSettings.detectPlatformByDependencies(): TargetPlatform? {
var hasCommonStdlib = false
orderEntries
.asSequence()
.mapNotNull { createLibInfo(it, this) }
.forEach {
val stdlibPlatform = it.stdlibPlatform
if (stdlibPlatform != null) {
if (stdlibPlatform.isCommon()) {
hasCommonStdlib = true
} else {
return stdlibPlatform
}
}
}
return if (hasCommonStdlib) CommonPlatforms.defaultCommonPlatform else null
}
private fun ModuleSettings.detectPlatform(): TargetPlatform {
return detectPlatformByFacet()
?: detectPlatformByDependencies()
?: JvmPlatforms.unspecifiedJvmPlatform
}
private fun ModuleSettings.getSourceFolderElements(): List<Element> {
val rootManagerElement = getComponentElement(ModuleSettings.MODULE_ROOT_MANAGER_COMPONENT) ?: return emptyList()
return rootManagerElement
.getChildren(ContentEntryImpl.ELEMENT_NAME)
.flatMap { it.getChildren(SourceFolderImpl.ELEMENT_NAME) }
}
@Suppress("UnstableApiUsage")
private fun ModuleSettings.isExternalModule(): Boolean {
return when {
rootElement.getAttributeValue(ExternalProjectSystemRegistry.EXTERNAL_SYSTEM_ID_KEY) != null -> true
rootElement.getAttributeValue(ExternalProjectSystemRegistry.IS_MAVEN_MODULE_KEY)?.toBoolean() ?: false -> true
else -> false
}
}
override fun isConversionNeeded(settings: ModuleSettings): Boolean {
if (settings.isExternalModule()) return false
val hasMigrationRoots = settings.getSourceFolderElements().any {
loadSourceRoot(it).rootType in rootTypesToMigrate
}
if (!hasMigrationRoots) {
return false
}
val targetPlatform = settings.detectPlatform()
return (!targetPlatform.isJvm())
}
override fun process(settings: ModuleSettings) {
for (sourceFolder in settings.getSourceFolderElements()) {
val contentRoot = sourceFolder.parent as? Element ?: continue
val oldSourceRoot = loadSourceRoot(sourceFolder)
val url = sourceFolder.getAttributeValue(URL_ATTRIBUTE)
val (newRootType, data) = oldSourceRoot.getMigratedSourceRootTypeWithProperties() ?: continue
@Suppress("UNCHECKED_CAST")
val newSourceRoot = JpsElementFactory.getInstance().createModuleSourceRoot(url, newRootType, data)
as? JpsTypedModuleSourceRoot<JpsElement> ?: continue
contentRoot.removeContent(sourceFolder)
saveSourceRoot(contentRoot, url, newSourceRoot)
}
}
}
}
}
override fun getConversionDescription() =
KotlinBundle.message("roots.description.text.update.source.roots.for.non.jvm.modules.in.kotlin.project")
override fun createConverter(context: ConversionContext) = ConverterImpl(context)
}