mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Option to auto-reload changed plugins without IDE restart (for plugin developers)
GitOrigin-RevId: f15ccc3dc194fb76d966706abeef3fc0202a7e6d
This commit is contained in:
committed by
intellij-monorepo-bot
parent
b37bf2f554
commit
ec8f71f90e
@@ -0,0 +1,85 @@
|
||||
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.ide.plugins
|
||||
|
||||
import com.intellij.ide.FrameStateListener
|
||||
import com.intellij.notification.NotificationType
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.application.PathManager
|
||||
import com.intellij.openapi.extensions.PluginDescriptor
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.openapi.vfs.AsyncFileListener
|
||||
import com.intellij.openapi.vfs.LocalFileSystem
|
||||
import com.intellij.openapi.vfs.VfsUtilCore
|
||||
import com.intellij.openapi.vfs.newvfs.events.VFileContentChangeEvent
|
||||
import com.intellij.openapi.vfs.newvfs.events.VFileEvent
|
||||
|
||||
/**
|
||||
* @author yole
|
||||
*/
|
||||
private const val AUTO_RELOAD_PLUGINS_SYSTEM_PROPERTY = "idea.auto.reload.plugins"
|
||||
|
||||
class DynamicPluginVfsListener : AsyncFileListener {
|
||||
init {
|
||||
if (System.getProperty(AUTO_RELOAD_PLUGINS_SYSTEM_PROPERTY) != null) {
|
||||
val pluginsPath = PathManager.getPluginsPath()
|
||||
LocalFileSystem.getInstance().addRootToWatch(pluginsPath, true)
|
||||
val pluginsRoot = LocalFileSystem.getInstance().findFileByPath(pluginsPath)
|
||||
if (pluginsRoot != null) {
|
||||
// ensure all plugins are in VFS
|
||||
VfsUtilCore.processFilesRecursively(pluginsRoot) { true }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun prepareChange(events: MutableList<out VFileEvent>): AsyncFileListener.ChangeApplier? {
|
||||
if (System.getProperty(AUTO_RELOAD_PLUGINS_SYSTEM_PROPERTY) == null) return null
|
||||
|
||||
val pluginsToReload = hashSetOf<PluginDescriptor>()
|
||||
for (event in events) {
|
||||
if (!event.isFromRefresh) continue
|
||||
if (event is VFileContentChangeEvent) {
|
||||
findPluginByPath(event.path)?.let { pluginsToReload.add(it) }
|
||||
}
|
||||
}
|
||||
val descriptorsToReload = pluginsToReload
|
||||
.filter { it.isEnabled }
|
||||
.map { PluginEnabler.loadFullDescriptor(it) }
|
||||
.filter { DynamicPlugins.allowLoadUnloadWithoutRestart(it) }
|
||||
|
||||
if (descriptorsToReload.isEmpty()) return null
|
||||
|
||||
return object : AsyncFileListener.ChangeApplier {
|
||||
override fun afterVfsChange() {
|
||||
ApplicationManager.getApplication().invokeLater {
|
||||
val reloaded = mutableListOf<String>()
|
||||
for (pluginDescriptor in descriptorsToReload) {
|
||||
if (!DynamicPlugins.unloadPlugin(pluginDescriptor, isUpdate = true)) {
|
||||
continue
|
||||
}
|
||||
reloaded.add(pluginDescriptor.name)
|
||||
DynamicPlugins.loadPlugin(pluginDescriptor, false)
|
||||
}
|
||||
if (reloaded.isNotEmpty()) {
|
||||
DynamicPlugins.notify("${reloaded.joinToString()} reloaded successfully", NotificationType.INFORMATION)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun findPluginByPath(path: String): PluginDescriptor? {
|
||||
if (!FileUtil.isAncestor(PathManager.getPluginsPath(), path, false)) return null
|
||||
return PluginManager.getPlugins().firstOrNull {
|
||||
FileUtil.isAncestor(it.path.absolutePath, path, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class DynamicPluginsFrameStateListener : FrameStateListener {
|
||||
override fun onFrameActivated() {
|
||||
if (System.getProperty(AUTO_RELOAD_PLUGINS_SYSTEM_PROPERTY) == null) return
|
||||
|
||||
val pluginsRoot = LocalFileSystem.getInstance().findFileByPath(PathManager.getPluginsPath())
|
||||
pluginsRoot?.refresh(true, true)
|
||||
}
|
||||
}
|
||||
@@ -357,8 +357,7 @@ object DynamicPlugins {
|
||||
val snapshotDate = SimpleDateFormat("dd.MM.yyyy_HH.mm.ss").format(Date())
|
||||
val snapshotPath = "$snapshotFolder/unload-${pluginDescriptor.pluginId}-$snapshotDate.hprof"
|
||||
MemoryDumpHelper.captureMemoryDump(snapshotPath)
|
||||
GROUP.createNotification("Captured memory snapshot on plugin unload fail: $snapshotPath",
|
||||
NotificationType.WARNING).notify(null)
|
||||
notify("Captured memory snapshot on plugin unload fail: $snapshotPath", NotificationType.WARNING)
|
||||
}
|
||||
LOG.info("Plugin ${pluginDescriptor.pluginId} is not unload-safe because class loader cannot be unloaded")
|
||||
}
|
||||
@@ -367,6 +366,10 @@ object DynamicPlugins {
|
||||
}
|
||||
}
|
||||
|
||||
internal fun notify(text: String, notificationType: NotificationType) {
|
||||
GROUP.createNotification(text, notificationType).notify(null)
|
||||
}
|
||||
|
||||
private fun unloadPluginDescriptor(pluginDescriptor: IdeaPluginDescriptorImpl,
|
||||
loadedPluginDescriptor: IdeaPluginDescriptorImpl) {
|
||||
val application = ApplicationManager.getApplication() as ApplicationImpl
|
||||
|
||||
@@ -901,5 +901,9 @@
|
||||
<titleInfoProvider implementation="com.intellij.openapi.wm.impl.simpleTitleParts.ProductVersionTitleInfoProvider"/>
|
||||
<titleInfoProvider implementation="com.intellij.openapi.wm.impl.simpleTitleParts.SuperUserSuffixTitleInfoProvider"/>
|
||||
|
||||
<vfs.asyncListener implementation="com.intellij.ide.plugins.DynamicPluginVfsListener"/>
|
||||
</extensions>
|
||||
<applicationListeners>
|
||||
<listener class="com.intellij.ide.plugins.DynamicPluginsFrameStateListener" topic="com.intellij.ide.FrameStateListener"/>
|
||||
</applicationListeners>
|
||||
</idea-plugin>
|
||||
|
||||
Reference in New Issue
Block a user