fix for EA-68992 (JS) IPBE: UninitializedMessageException.asInvalidProtocolBufferException.
This commit is contained in:
@@ -31,21 +31,33 @@ import com.intellij.openapi.roots.impl.OrderEntryUtil
|
|||||||
import com.intellij.openapi.roots.libraries.Library
|
import com.intellij.openapi.roots.libraries.Library
|
||||||
import com.intellij.openapi.roots.libraries.LibraryTable
|
import com.intellij.openapi.roots.libraries.LibraryTable
|
||||||
import com.intellij.openapi.util.Disposer
|
import com.intellij.openapi.util.Disposer
|
||||||
|
import com.intellij.openapi.vfs.VfsUtil
|
||||||
|
import com.intellij.openapi.vfs.VirtualFile
|
||||||
|
import com.intellij.openapi.vfs.VirtualFileManager
|
||||||
|
import com.intellij.openapi.vfs.newvfs.BulkFileListener
|
||||||
|
import com.intellij.openapi.vfs.newvfs.events.VFileContentChangeEvent
|
||||||
|
import com.intellij.openapi.vfs.newvfs.events.VFileEvent
|
||||||
|
import com.intellij.util.FileContentUtilCore
|
||||||
import com.intellij.util.PathUtil
|
import com.intellij.util.PathUtil
|
||||||
import org.jetbrains.annotations.TestOnly
|
import org.jetbrains.annotations.TestOnly
|
||||||
import org.jetbrains.kotlin.idea.framework.KotlinJavaScriptLibraryDetectionUtil
|
import org.jetbrains.kotlin.idea.framework.KotlinJavaScriptLibraryDetectionUtil
|
||||||
import org.jetbrains.kotlin.idea.project.ProjectStructureUtil.isJsKotlinModule
|
import org.jetbrains.kotlin.idea.project.ProjectStructureUtil.isJsKotlinModule
|
||||||
import org.jetbrains.kotlin.utils.KotlinJavascriptMetadataUtils
|
import org.jetbrains.kotlin.utils.KotlinJavascriptMetadataUtils
|
||||||
|
import java.io.File
|
||||||
|
import java.util.*
|
||||||
import java.util.concurrent.atomic.AtomicBoolean
|
import java.util.concurrent.atomic.AtomicBoolean
|
||||||
|
|
||||||
public class KotlinJavaScriptLibraryManager private constructor(private var myProject: Project?) : ProjectComponent, ModuleRootListener {
|
public class KotlinJavaScriptLibraryManager private constructor(private var myProject: Project?) : ProjectComponent, ModuleRootListener, BulkFileListener {
|
||||||
private val myMuted = AtomicBoolean(false)
|
private val myMuted = AtomicBoolean(false)
|
||||||
|
|
||||||
|
private val libraryFileStampMap: MutableMap<String, Long> = Collections.synchronizedMap(hashMapOf())
|
||||||
|
|
||||||
override fun getComponentName(): String = "KotlinJavascriptLibraryManager"
|
override fun getComponentName(): String = "KotlinJavascriptLibraryManager"
|
||||||
|
|
||||||
override fun projectOpened() {
|
override fun projectOpened() {
|
||||||
val project = myProject!!
|
val project = myProject!!
|
||||||
project.messageBus.connect(project).subscribe(ProjectTopics.PROJECT_ROOTS, this)
|
project.messageBus.connect(project).subscribe(ProjectTopics.PROJECT_ROOTS, this)
|
||||||
|
project.messageBus.connect(project).subscribe(VirtualFileManager.VFS_CHANGES, this)
|
||||||
DumbService.getInstance(project).smartInvokeLater() { updateProjectLibrary() }
|
DumbService.getInstance(project).smartInvokeLater() { updateProjectLibrary() }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,6 +71,22 @@ public class KotlinJavaScriptLibraryManager private constructor(private var myPr
|
|||||||
myProject = null
|
myProject = null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun after(events: List<VFileEvent>) {
|
||||||
|
if (myMuted.get()) return
|
||||||
|
|
||||||
|
val changedFiles = events
|
||||||
|
.filter { it !is MyVFileContentChangeEvent && it is VFileContentChangeEvent }
|
||||||
|
.map { it.file }
|
||||||
|
.filterNotNull()
|
||||||
|
|
||||||
|
val files = update(changedFiles, addToMapIfAbsent = false)
|
||||||
|
val application = ApplicationManager.getApplication()
|
||||||
|
refreshFiles(files, application.isUnitTestMode)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun before(events: List<VFileEvent>) {
|
||||||
|
}
|
||||||
|
|
||||||
override fun beforeRootsChange(event: ModuleRootEvent) {
|
override fun beforeRootsChange(event: ModuleRootEvent) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,7 +94,7 @@ public class KotlinJavaScriptLibraryManager private constructor(private var myPr
|
|||||||
if (myMuted.get()) return
|
if (myMuted.get()) return
|
||||||
|
|
||||||
ApplicationManager.getApplication().invokeLater(Runnable {
|
ApplicationManager.getApplication().invokeLater(Runnable {
|
||||||
DumbService.getInstance(myProject!!).runWhenSmart() { updateProjectLibrary() }
|
DumbService.getInstance(myProject!!).runWhenSmart() { updateProjectLibrary() }
|
||||||
}, ModalityState.NON_MODAL, myProject!!.disposed)
|
}, ModalityState.NON_MODAL, myProject!!.disposed)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,38 +120,42 @@ public class KotlinJavaScriptLibraryManager private constructor(private var myPr
|
|||||||
|
|
||||||
val clsRootUrls: MutableList<String> = arrayListOf()
|
val clsRootUrls: MutableList<String> = arrayListOf()
|
||||||
val srcRootUrls: MutableList<String> = arrayListOf()
|
val srcRootUrls: MutableList<String> = arrayListOf()
|
||||||
|
val rootFiles: MutableList<VirtualFile> = arrayListOf()
|
||||||
|
|
||||||
ModuleRootManager.getInstance(module).orderEntries().librariesOnly().forEachLibrary() { library ->
|
ModuleRootManager.getInstance(module).orderEntries().librariesOnly().forEachLibrary() { library ->
|
||||||
if (KotlinJavaScriptLibraryDetectionUtil.isKotlinJavaScriptLibrary(library)) {
|
if (KotlinJavaScriptLibraryDetectionUtil.isKotlinJavaScriptLibrary(library)) {
|
||||||
var addSources = false
|
var addSources = false
|
||||||
for (clsRootFile in library.getFiles(OrderRootType.CLASSES)) {
|
for (clsRootFile in library.getFiles(OrderRootType.CLASSES)) {
|
||||||
val path = PathUtil.getLocalPath(clsRootFile)
|
val path = PathUtil.getLocalPath(clsRootFile)
|
||||||
assert(path != null) { "expected not-null path for ${clsRootFile.name}" }
|
assert(path != null) { "expected not-null path for ${clsRootFile.name}" }
|
||||||
|
|
||||||
val metadataList = KotlinJavascriptMetadataUtils.loadMetadata(path!!)
|
val metadataList = KotlinJavascriptMetadataUtils.loadMetadata(path!!)
|
||||||
if (metadataList.filter { !it.isAbiVersionCompatible }.isNotEmpty()) continue
|
if (metadataList.filter { !it.isAbiVersionCompatible }.isNotEmpty()) continue
|
||||||
|
|
||||||
val classRoot = KotlinJavaScriptMetaFileSystem.getInstance().refreshAndFindFileByPath("$path!/")
|
VfsUtil.findFileByIoFile(File(path), true)?.let { rootFiles.add(it) }
|
||||||
classRoot?.let {
|
val classRoot = KotlinJavaScriptMetaFileSystem.getInstance().refreshAndFindFileByPath("$path!/")
|
||||||
clsRootUrls.add(it.url)
|
classRoot?.let {
|
||||||
addSources = true
|
clsRootUrls.add(it.url)
|
||||||
}
|
addSources = true
|
||||||
}
|
|
||||||
if (addSources) {
|
|
||||||
srcRootUrls.addAll(library.getFiles(OrderRootType.SOURCES).map { it.url })
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
true
|
if (addSources) {
|
||||||
|
srcRootUrls.addAll(library.getFiles(OrderRootType.SOURCES).map { it.url })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
val changesToApply = ChangesToApply(clsRootUrls, srcRootUrls)
|
val filesToRefresh = update(rootFiles, addToMapIfAbsent = true)
|
||||||
|
|
||||||
|
val changesToApply = ChangesToApply(clsRootUrls, srcRootUrls, filesToRefresh)
|
||||||
|
|
||||||
resetLibraries(module, changesToApply, LIBRARY_NAME, synchronously)
|
resetLibraries(module, changesToApply, LIBRARY_NAME, synchronously)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun resetLibraries(module: Module, changesToApply: ChangesToApply, libraryName: String, synchronously: Boolean) =
|
private fun resetLibraries(module: Module, changesToApply: ChangesToApply, libraryName: String, synchronously: Boolean) =
|
||||||
applyChange(module, changesToApply, synchronously, libraryName)
|
applyChange(module, changesToApply, synchronously, libraryName)
|
||||||
|
|
||||||
private fun applyChange(module: Module, changesToApply: ChangesToApply, synchronously: Boolean, libraryName: String) {
|
private fun applyChange(module: Module, changesToApply: ChangesToApply, synchronously: Boolean, libraryName: String) {
|
||||||
if (synchronously) {
|
if (synchronously) {
|
||||||
@@ -148,10 +180,52 @@ public class KotlinJavaScriptLibraryManager private constructor(private var myPr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun update(changedFiles: List<VirtualFile>, addToMapIfAbsent: Boolean): List<VirtualFile> {
|
||||||
|
if (changedFiles.isEmpty()) return emptyList()
|
||||||
|
|
||||||
|
val files = arrayListOf<VirtualFile>()
|
||||||
|
synchronized(libraryFileStampMap) {
|
||||||
|
changedFiles.forEach { file ->
|
||||||
|
PathUtil.getLocalPath(file)?.let { path ->
|
||||||
|
val timeStamp = libraryFileStampMap.get(path)
|
||||||
|
if (addToMapIfAbsent && timeStamp != file.timeStamp ||
|
||||||
|
!addToMapIfAbsent && timeStamp != null) {
|
||||||
|
libraryFileStampMap[path] = file.timeStamp
|
||||||
|
files.add(file)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return files
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun refreshFiles(files: List<VirtualFile>, synchronously: Boolean) {
|
||||||
|
val events = files.filter { !it.isDirectory && it.isValid }.map { MyVFileContentChangeEvent(it) }
|
||||||
|
if (events.isEmpty()) return
|
||||||
|
|
||||||
|
val commitInWriteAction = Runnable {
|
||||||
|
ApplicationManager.getApplication().runWriteAction() {
|
||||||
|
val publisher = ApplicationManager.getApplication().messageBus.syncPublisher(VirtualFileManager.VFS_CHANGES)
|
||||||
|
publisher.before(events)
|
||||||
|
publisher.after(events)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (synchronously) {
|
||||||
|
commitInWriteAction.run()
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ApplicationManager.getApplication().invokeLater(commitInWriteAction, myProject!!.disposed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
private fun applyChangeImpl(module: Module, changesToApply: ChangesToApply, libraryName: String) {
|
private fun applyChangeImpl(module: Module, changesToApply: ChangesToApply, libraryName: String) {
|
||||||
if (module.isDisposed) return
|
if (module.isDisposed) return
|
||||||
|
|
||||||
|
refreshFiles(changesToApply.filesToRefresh, synchronously = true)
|
||||||
|
|
||||||
val model = ModuleRootManager.getInstance(module).modifiableModel
|
val model = ModuleRootManager.getInstance(module).modifiableModel
|
||||||
val libraryTableModel = model.moduleLibraryTable.modifiableModel
|
val libraryTableModel = model.moduleLibraryTable.modifiableModel
|
||||||
|
|
||||||
@@ -212,7 +286,19 @@ public class KotlinJavaScriptLibraryManager private constructor(private var myPr
|
|||||||
private fun findLibraryByName(module: Module, libraryName: String) =
|
private fun findLibraryByName(module: Module, libraryName: String) =
|
||||||
OrderEntryUtil.findLibraryOrderEntry(ModuleRootManager.getInstance(module), libraryName)?.library
|
OrderEntryUtil.findLibraryOrderEntry(ModuleRootManager.getInstance(module), libraryName)?.library
|
||||||
|
|
||||||
private class ChangesToApply(val clsUrlsToAdd: List<String> = listOf(), val srcUrlsToAdd: List<String> = listOf())
|
private class ChangesToApply(val clsUrlsToAdd: List<String> = listOf(), val srcUrlsToAdd: List<String> = listOf(), val filesToRefresh: List<VirtualFile> = listOf())
|
||||||
|
|
||||||
|
private class MyVFileContentChangeEvent(
|
||||||
|
file: VirtualFile
|
||||||
|
) : VFileContentChangeEvent(
|
||||||
|
FileContentUtilCore.FORCE_RELOAD_REQUESTOR,
|
||||||
|
file,
|
||||||
|
file.modificationStamp,
|
||||||
|
-1, /* oldModificationStamp */
|
||||||
|
false /* isFromRefresh */
|
||||||
|
) {
|
||||||
|
override fun getPath(): String = PathUtil.getLocalPath(file.path + KotlinJavaScriptMetaFileSystem.ARCHIVE_SUFFIX)
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
@@ -220,6 +306,6 @@ public class KotlinJavaScriptLibraryManager private constructor(private var myPr
|
|||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
public fun getInstance(project: Project): KotlinJavaScriptLibraryManager =
|
public fun getInstance(project: Project): KotlinJavaScriptLibraryManager =
|
||||||
project.getComponent(KotlinJavaScriptLibraryManager::class.java)!!
|
project.getComponent(KotlinJavaScriptLibraryManager::class.java)!!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,12 +26,12 @@ import org.jetbrains.kotlin.utils.KotlinJavascriptMetadataUtils
|
|||||||
|
|
||||||
public class KotlinJavaScriptMetaFileSystem : ArchiveFileSystem() {
|
public class KotlinJavaScriptMetaFileSystem : ArchiveFileSystem() {
|
||||||
companion object {
|
companion object {
|
||||||
|
val ARCHIVE_SUFFIX = ".kjsm_archive"
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
public fun getInstance(): KotlinJavaScriptMetaFileSystem = VirtualFileManager.getInstance().getFileSystem(KotlinJavascriptMetadataUtils.VFS_PROTOCOL) as KotlinJavaScriptMetaFileSystem
|
public fun getInstance(): KotlinJavaScriptMetaFileSystem = VirtualFileManager.getInstance().getFileSystem(KotlinJavascriptMetadataUtils.VFS_PROTOCOL) as KotlinJavaScriptMetaFileSystem
|
||||||
}
|
}
|
||||||
|
|
||||||
private val ARCHIVE_SUFFIX = ".kjsm_archive"
|
|
||||||
|
|
||||||
override fun getProtocol(): String = KotlinJavascriptMetadataUtils.VFS_PROTOCOL
|
override fun getProtocol(): String = KotlinJavascriptMetadataUtils.VFS_PROTOCOL
|
||||||
|
|
||||||
override fun extractRootPath(path: String): String {
|
override fun extractRootPath(path: String): String {
|
||||||
@@ -41,15 +41,15 @@ public class KotlinJavaScriptMetaFileSystem : ArchiveFileSystem() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun getHandler(entryFile: VirtualFile): KotlinJavaScriptHandler {
|
override fun getHandler(entryFile: VirtualFile): KotlinJavaScriptHandler {
|
||||||
val pathToRoot = extractLocalPath(this.extractRootPath(entryFile.path))
|
val pathToRoot = extractLocalPath(this.extractRootPath(entryFile.path)).removeSuffix()
|
||||||
return VfsImplUtil.getHandler<KotlinJavaScriptHandler>(this, "$pathToRoot$ARCHIVE_SUFFIX") {
|
return VfsImplUtil.getHandler<KotlinJavaScriptHandler>(this, "$pathToRoot$ARCHIVE_SUFFIX") {
|
||||||
KotlinJavaScriptHandler(it.substringBeforeLast(ARCHIVE_SUFFIX))
|
KotlinJavaScriptHandler(it.removeSuffix())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun extractLocalPath(rootPath: String): String = StringUtil.trimEnd(rootPath, JarFileSystem.JAR_SEPARATOR)
|
override fun extractLocalPath(rootPath: String): String = StringUtil.trimEnd(rootPath, JarFileSystem.JAR_SEPARATOR)
|
||||||
|
|
||||||
override fun composeRootPath(localPath: String): String = localPath + JarFileSystem.JAR_SEPARATOR
|
override fun composeRootPath(localPath: String): String = localPath.removeSuffix() + JarFileSystem.JAR_SEPARATOR
|
||||||
|
|
||||||
override fun findFileByPath(path: String): VirtualFile? = VfsImplUtil.findFileByPath(this, path)
|
override fun findFileByPath(path: String): VirtualFile? = VfsImplUtil.findFileByPath(this, path)
|
||||||
|
|
||||||
@@ -58,4 +58,6 @@ public class KotlinJavaScriptMetaFileSystem : ArchiveFileSystem() {
|
|||||||
override fun refreshAndFindFileByPath(path: String): VirtualFile? = VfsImplUtil.refreshAndFindFileByPath(this, path)
|
override fun refreshAndFindFileByPath(path: String): VirtualFile? = VfsImplUtil.refreshAndFindFileByPath(this, path)
|
||||||
|
|
||||||
override fun refresh(asynchronous: Boolean) = VfsImplUtil.refresh(this, asynchronous)
|
override fun refresh(asynchronous: Boolean) = VfsImplUtil.refresh(this, asynchronous)
|
||||||
|
|
||||||
|
private fun String.removeSuffix() = substringBeforeLast(ARCHIVE_SUFFIX)
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user