Implement explicit library kind for JS libraries, don't autodetect

This commit is contained in:
Dmitry Jemerov
2017-07-11 14:52:31 +02:00
parent 1367e6f303
commit 902fe1f5ce
12 changed files with 156 additions and 149 deletions
@@ -33,7 +33,9 @@ import org.jetbrains.kotlin.serialization.deserialization.MetadataPackageFragmen
object CommonLibraryDetectionUtil {
@JvmStatic
fun getLibraryPlatform(library: Library): TargetPlatform {
if (library is LibraryEx && library.isDisposed) return JvmPlatform
library as? LibraryEx ?: return JvmPlatform
if (library.isDisposed) return JvmPlatform
if (library.kind is JSLibraryKind) return JsPlatform
for (root in library.getFiles(OrderRootType.CLASSES)) {
ProgressManager.checkCanceled()
@@ -42,17 +44,12 @@ object CommonLibraryDetectionUtil {
if (hasCommonMetadata == true) {
return TargetPlatform.Default
}
val hasJSMetadata = JarUserDataManager.hasFileWithProperty(KotlinJavaScriptLibraryDetectionUtil.HasKotlinJSMetadataInJar, root)
if (hasJSMetadata == true) {
return JsPlatform
}
var platform: TargetPlatform? = null
VfsUtilCore.processFilesRecursively(root) { file ->
when {
file.fileType == JavaClassFileType.INSTANCE -> platform = JvmPlatform
isKotlinMetadataFile(file) -> platform = TargetPlatform.Default
KotlinJavaScriptLibraryDetectionUtil.isJsFileWithMetadata(file) -> platform = JsPlatform
}
platform == null
@@ -34,12 +34,11 @@ object JsLibraryStdDetectionUtil {
private val IS_JS_LIBRARY_STD_LIB = Key.create<Boolean>("IS_JS_LIBRARY_STD_LIB")
fun hasJsStdlibJar(library: Library): Boolean {
if (library is LibraryEx && library.isDisposed) return false
if (!KotlinJavaScriptLibraryDetectionUtil.isKotlinJavaScriptLibrary(library)) return false
if (library !is LibraryEx || library.isDisposed) return false
if (library.kind !is JSLibraryKind) return false
val classes = Arrays.asList(*library.getFiles(OrderRootType.CLASSES))
return getJsLibraryStdVersion(classes) != null
return getJsStdLibJar(classes) != null
}
fun getJsLibraryStdVersion(library: Library): String? = getJsLibraryStdVersion(library.getFiles(OrderRootType.CLASSES).toList())
@@ -1,77 +0,0 @@
/*
* Copyright 2010-2016 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.framework
import com.intellij.openapi.progress.ProgressManager
import com.intellij.openapi.roots.OrderRootType
import com.intellij.openapi.roots.impl.libraries.LibraryEx
import com.intellij.openapi.roots.libraries.Library
import com.intellij.openapi.vfs.VfsUtilCore
import com.intellij.openapi.vfs.VirtualFile
import org.jetbrains.kotlin.idea.caches.JarUserDataManager
import org.jetbrains.kotlin.js.JavaScript
import org.jetbrains.kotlin.utils.KotlinJavascriptMetadataUtils
import java.util.zip.ZipException
object KotlinJavaScriptLibraryDetectionUtil {
@JvmStatic fun isKotlinJavaScriptLibrary(library: Library): Boolean {
if (library is LibraryEx && library.isDisposed) return false
return isKotlinJavaScriptLibrary(library.getFiles(OrderRootType.CLASSES).toList())
}
@JvmStatic fun isKotlinJavaScriptLibrary(classesRoots: List<VirtualFile>): Boolean {
// Prevent clashing with java runtime
if (JavaRuntimeDetectionUtil.getRuntimeJar(classesRoots) != null) return false
classesRoots.forEach { root ->
ProgressManager.checkCanceled()
val hasMetadata = HasKotlinJSMetadataInJar.hasMetadataFromCache(root)
if (hasMetadata != null) {
return hasMetadata
}
if (!VfsUtilCore.processFilesRecursively(root) { !isJsFileWithMetadata(it) }) {
return true
}
}
return false
}
fun isJsFileWithMetadata(file: VirtualFile): Boolean {
if (!file.isDirectory && JavaScript.EXTENSION == file.extension) {
val content = try {
file.contentsToByteArray(false)
}
catch (e: ZipException) {
throw RuntimeException("file:${file.path} isDirectory: ${file.isDirectory}, exists: ${file.exists()}", e)
}
return KotlinJavascriptMetadataUtils.hasMetadata(String(content))
}
return false
}
object HasKotlinJSMetadataInJar : JarUserDataManager.JarBooleanPropertyCounter(HasKotlinJSMetadataInJar::class.simpleName!!) {
override fun hasProperty(file: VirtualFile) = KotlinJavaScriptLibraryDetectionUtil.isJsFileWithMetadata(file)
fun hasMetadataFromCache(root: VirtualFile): Boolean? = JarUserDataManager.hasFileWithProperty(HasKotlinJSMetadataInJar, root)
}
}
@@ -0,0 +1,40 @@
/*
* 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.
*/
/*
* 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.framework
import com.intellij.openapi.roots.libraries.DummyLibraryProperties
import com.intellij.openapi.roots.libraries.PersistentLibraryKind
object JSLibraryKind : PersistentLibraryKind<DummyLibraryProperties>("kotlin.js") {
override fun createDefaultProperties() = DummyLibraryProperties.INSTANCE!!
}
+2 -1
View File
@@ -671,7 +671,6 @@
<projectTemplatesFactory implementation="org.jetbrains.kotlin.idea.framework.KotlinTemplatesFactory" />
<library.presentationProvider implementation="org.jetbrains.kotlin.idea.framework.JavaRuntimePresentationProvider"/>
<library.presentationProvider implementation="org.jetbrains.kotlin.idea.framework.JSLibraryStdPresentationProvider"/>
<library.javaSourceRootDetector implementation="org.jetbrains.kotlin.idea.configuration.KotlinSourceRootDetector"/>
@@ -823,6 +822,8 @@
<usageContextPanelProvider implementation="org.jetbrains.kotlin.idea.slicer.KotlinUsageContextDataInflowPanel$Provider"/>
<usageContextPanelProvider implementation="org.jetbrains.kotlin.idea.slicer.KotlinUsageContextDataOutflowPanel$Provider"/>
<library.type implementation="org.jetbrains.kotlin.idea.framework.JSLibraryType"/>
<intentionAction>
<className>org.jetbrains.kotlin.idea.intentions.FoldInitializerAndIfToElvisIntention</className>
<category>Kotlin</category>
@@ -32,7 +32,6 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.idea.caches.JarUserDataManager;
import org.jetbrains.kotlin.idea.debugger.filter.DebuggerFiltersUtilKt;
import org.jetbrains.kotlin.idea.framework.CommonLibraryDetectionUtil;
import org.jetbrains.kotlin.idea.framework.KotlinJavaScriptLibraryDetectionUtil;
import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinTodoSearcher;
import org.jetbrains.kotlin.utils.PathUtil;
@@ -62,7 +61,6 @@ public class PluginStartupComponent implements ApplicationComponent {
ThreadTrackerPatcherForTeamCityTesting.INSTANCE.patchThreadTracker();
}
JarUserDataManager.INSTANCE.register(KotlinJavaScriptLibraryDetectionUtil.HasKotlinJSMetadataInJar.INSTANCE);
JarUserDataManager.INSTANCE.register(CommonLibraryDetectionUtil.HasCommonKotlinMetadataInJar.INSTANCE);
DebuggerFiltersUtilKt.addKotlinStdlibDebugFilterIfNeeded();
@@ -18,8 +18,11 @@ package org.jetbrains.kotlin.idea.configuration
import com.intellij.openapi.module.Module
import com.intellij.openapi.projectRoots.Sdk
import com.intellij.openapi.roots.libraries.DummyLibraryProperties
import com.intellij.openapi.roots.libraries.Library
import com.intellij.openapi.roots.libraries.LibraryType
import org.jetbrains.kotlin.idea.framework.JSLibraryStdDescription
import org.jetbrains.kotlin.idea.framework.JSLibraryType
import org.jetbrains.kotlin.idea.versions.LibraryJarDescriptor
import org.jetbrains.kotlin.idea.versions.isKotlinJsRuntime
import org.jetbrains.kotlin.js.JavaScript
@@ -56,6 +59,9 @@ open class KotlinJsModuleConfigurator : KotlinWithLibraryConfigurator() {
override val libraryMatcher: (Library) -> Boolean = ::isKotlinJsRuntime
override val libraryType: LibraryType<DummyLibraryProperties>?
get() = JSLibraryType.getInstance()
companion object {
const val NAME = JavaScript.LOWER_NAME
}
@@ -22,8 +22,7 @@ import com.intellij.openapi.module.ModuleManager
import com.intellij.openapi.project.Project
import com.intellij.openapi.projectRoots.Sdk
import com.intellij.openapi.roots.*
import com.intellij.openapi.roots.libraries.Library
import com.intellij.openapi.roots.libraries.LibraryTablesRegistrar
import com.intellij.openapi.roots.libraries.*
import com.intellij.openapi.vfs.JarFileSystem
import com.intellij.openapi.vfs.LocalFileSystem
import com.intellij.openapi.vfs.VfsUtil
@@ -48,6 +47,10 @@ abstract class KotlinWithLibraryConfigurator internal constructor() : KotlinProj
protected abstract val libraryCaption: String
open val libraryType: LibraryType<DummyLibraryProperties>? = null
protected val libraryKind: PersistentLibraryKind<*>? = libraryType?.kind
override fun getStatus(module: Module): ConfigureKotlinStatus {
if (!isApplicable(module)) {
return ConfigureKotlinStatus.NON_APPLICABLE
@@ -222,7 +225,11 @@ abstract class KotlinWithLibraryConfigurator internal constructor() : KotlinProj
): Library {
val table = LibraryTablesRegistrar.getInstance().getLibraryTable(project)
val library = runWriteAction {
table.createLibrary(libraryName)
table.modifiableModel.run {
val library = createLibrary(libraryName, libraryKind)
commit()
library
}
}
collector.addMessage(library.name!! + " library was created")
@@ -16,12 +16,12 @@
package org.jetbrains.kotlin.idea.framework
import com.intellij.framework.library.LibraryVersionProperties
import com.intellij.openapi.module.Module
import com.intellij.openapi.project.Project
import com.intellij.openapi.projectRoots.Sdk
import com.intellij.openapi.roots.ModifiableRootModel
import com.intellij.openapi.roots.OrderRootType
import com.intellij.openapi.roots.libraries.DummyLibraryProperties
import com.intellij.openapi.roots.libraries.Library
import com.intellij.openapi.roots.libraries.LibraryKind
import com.intellij.openapi.roots.libraries.NewLibraryConfiguration
@@ -212,7 +212,7 @@ abstract class CustomLibraryDescriptorWithDeferredConfig
}
protected fun createConfiguration(libraryFiles: List<File>, librarySourceFiles: List<File>): NewLibraryConfiguration {
return object : NewLibraryConfiguration(libraryName, null, LibraryVersionProperties()) {
return object : NewLibraryConfiguration(libraryName, configurator.libraryType, DummyLibraryProperties.INSTANCE) {
override fun addRoots(editor: LibraryEditor) {
for (libraryFile in libraryFiles) {
editor.addRoot(VfsUtil.getUrlForLibraryRoot(libraryFile), OrderRootType.CLASSES)
@@ -17,7 +17,6 @@
package org.jetbrains.kotlin.idea.framework
import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.libraries.LibraryKind
import com.intellij.openapi.roots.libraries.NewLibraryConfiguration
import org.jetbrains.annotations.TestOnly
import org.jetbrains.kotlin.idea.configuration.KotlinJsModuleConfigurator
@@ -32,7 +31,7 @@ class JSLibraryStdDescription(project: Project?) :
LIBRARY_NAME,
DIALOG_TITLE,
LIBRARY_CAPTION,
KOTLIN_JAVASCRIPT_KIND,
JSLibraryKind,
SUITABLE_LIBRARY_KINDS) {
@TestOnly
@@ -41,12 +40,11 @@ class JSLibraryStdDescription(project: Project?) :
}
companion object {
val KOTLIN_JAVASCRIPT_KIND = LibraryKind.create("kotlin-js-stdlib")
val LIBRARY_NAME = "KotlinJavaScript"
val JAVA_SCRIPT_LIBRARY_CREATION = "JavaScript Library Creation"
val DIALOG_TITLE = "Create Kotlin JavaScript Library"
val LIBRARY_CAPTION = "Kotlin JavaScript Library"
val SUITABLE_LIBRARY_KINDS = setOf(KOTLIN_JAVASCRIPT_KIND)
val SUITABLE_LIBRARY_KINDS = setOf(JSLibraryKind)
}
}
@@ -1,50 +0,0 @@
/*
* 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.framework;
import com.intellij.framework.library.LibraryVersionProperties;
import com.intellij.openapi.roots.libraries.LibraryPresentationProvider;
import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.idea.KotlinIcons;
import javax.swing.*;
import java.util.List;
public class JSLibraryStdPresentationProvider extends LibraryPresentationProvider<LibraryVersionProperties> {
public static JSLibraryStdPresentationProvider getInstance() {
return LibraryPresentationProvider.EP_NAME.findExtension(JSLibraryStdPresentationProvider.class);
}
protected JSLibraryStdPresentationProvider() {
super(JSLibraryStdDescription.Companion.getKOTLIN_JAVASCRIPT_KIND());
}
@Nullable
@Override
public Icon getIcon(@Nullable LibraryVersionProperties properties) {
return KotlinIcons.SMALL_LOGO;
}
@Nullable
@Override
public LibraryVersionProperties detect(@NotNull List<VirtualFile> classesRoots) {
String version = JsLibraryStdDetectionUtil.INSTANCE.getJsLibraryStdVersion(classesRoots);
return version == null ? null : new LibraryVersionProperties(version);
}
}
@@ -0,0 +1,88 @@
/*
* 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.framework
import com.intellij.openapi.extensions.Extensions
import com.intellij.openapi.fileChooser.FileChooserDescriptor
import com.intellij.openapi.fileChooser.FileElement
import com.intellij.openapi.fileTypes.PlainTextFileType
import com.intellij.openapi.project.Project
import com.intellij.openapi.project.ProjectBundle
import com.intellij.openapi.roots.OrderRootType
import com.intellij.openapi.roots.libraries.DummyLibraryProperties
import com.intellij.openapi.roots.libraries.LibraryType
import com.intellij.openapi.roots.libraries.LibraryTypeService
import com.intellij.openapi.roots.libraries.NewLibraryConfiguration
import com.intellij.openapi.roots.libraries.ui.FileTypeBasedRootFilter
import com.intellij.openapi.roots.libraries.ui.LibraryEditorComponent
import com.intellij.openapi.roots.libraries.ui.RootDetector
import com.intellij.openapi.roots.ui.configuration.libraryEditor.DefaultLibraryRootsComponentDescriptor
import com.intellij.openapi.util.text.StringUtil
import com.intellij.openapi.vfs.VirtualFile
import org.jetbrains.kotlin.idea.KotlinFileType
import org.jetbrains.kotlin.idea.KotlinIcons
import javax.swing.JComponent
class JSLibraryType : LibraryType<DummyLibraryProperties>(JSLibraryKind) {
override fun createPropertiesEditor(editorComponent: LibraryEditorComponent<DummyLibraryProperties>) = null
override fun getCreateActionName() = "Kotlin/JS"
override fun createNewLibrary(parentComponent: JComponent,
contextDirectory: VirtualFile?,
project: Project): NewLibraryConfiguration? {
return LibraryTypeService.getInstance().createLibraryFromFiles(RootsComponentDescriptor,
parentComponent, contextDirectory, this,
project)
}
override fun getIcon(properties: DummyLibraryProperties?) = KotlinIcons.SMALL_LOGO
companion object {
fun getInstance() = Extensions.findExtension(EP_NAME, JSLibraryType::class.java)
}
object RootsComponentDescriptor : DefaultLibraryRootsComponentDescriptor() {
override fun createAttachFilesChooserDescriptor(libraryName: String?): FileChooserDescriptor {
val descriptor = FileChooserDescriptor(true, true, true, false, true, true).withFileFilter {
FileElement.isArchive(it) || isAcceptedForJsLibrary(it.extension)
}
descriptor.title = if (StringUtil.isEmpty(libraryName))
ProjectBundle.message("library.attach.files.action")
else
ProjectBundle.message("library.attach.files.to.library.action", libraryName!!)
descriptor.description = ProjectBundle.message("library.java.attach.files.description")
return descriptor
}
override fun getRootTypes() = arrayOf(OrderRootType.CLASSES, OrderRootType.SOURCES)
override fun getRootDetectors(): List<RootDetector> {
return arrayListOf(
JSRootFilter,
FileTypeBasedRootFilter(OrderRootType.SOURCES, false, KotlinFileType.INSTANCE, "sources")
)
}
}
object JSRootFilter : FileTypeBasedRootFilter(OrderRootType.CLASSES, false, PlainTextFileType.INSTANCE, "JS files") {
override fun isFileAccepted(virtualFile: VirtualFile) = isAcceptedForJsLibrary(virtualFile.extension)
}
}
private fun isAcceptedForJsLibrary(extension: String?) = extension == "js" || extension == "kjsm"