Show notification in the editor when new script dependencies available
^KT-23616 Fixed
This commit is contained in:
+12
-25
@@ -31,7 +31,6 @@ import com.intellij.util.Alarm
|
|||||||
import org.jetbrains.annotations.TestOnly
|
import org.jetbrains.annotations.TestOnly
|
||||||
import org.jetbrains.kotlin.idea.core.script.dependencies.FromFileAttributeScriptDependenciesLoader
|
import org.jetbrains.kotlin.idea.core.script.dependencies.FromFileAttributeScriptDependenciesLoader
|
||||||
import org.jetbrains.kotlin.idea.core.script.dependencies.ScriptDependenciesLoader
|
import org.jetbrains.kotlin.idea.core.script.dependencies.ScriptDependenciesLoader
|
||||||
import org.jetbrains.kotlin.idea.core.script.settings.KotlinScriptingSettings
|
|
||||||
import org.jetbrains.kotlin.psi.NotNullableUserDataProperty
|
import org.jetbrains.kotlin.psi.NotNullableUserDataProperty
|
||||||
import org.jetbrains.kotlin.script.ScriptDefinitionProvider
|
import org.jetbrains.kotlin.script.ScriptDefinitionProvider
|
||||||
import org.jetbrains.kotlin.script.findScriptDefinition
|
import org.jetbrains.kotlin.script.findScriptDefinition
|
||||||
@@ -42,7 +41,6 @@ class ScriptDependenciesUpdater(
|
|||||||
private val cache: ScriptDependenciesCache,
|
private val cache: ScriptDependenciesCache,
|
||||||
private val scriptDefinitionProvider: ScriptDefinitionProvider
|
private val scriptDefinitionProvider: ScriptDefinitionProvider
|
||||||
) {
|
) {
|
||||||
private val modifiedScripts = mutableSetOf<VirtualFile>()
|
|
||||||
private val scriptsQueue = Alarm(Alarm.ThreadToUse.SWING_THREAD, project)
|
private val scriptsQueue = Alarm(Alarm.ThreadToUse.SWING_THREAD, project)
|
||||||
private val scriptChangesListenerDelay = 1400
|
private val scriptChangesListenerDelay = 1400
|
||||||
|
|
||||||
@@ -61,24 +59,6 @@ class ScriptDependenciesUpdater(
|
|||||||
return cache[file] ?: ScriptDependencies.Empty
|
return cache[file] ?: ScriptDependencies.Empty
|
||||||
}
|
}
|
||||||
|
|
||||||
fun reloadModifiedScripts() {
|
|
||||||
for (it in modifiedScripts.filter { cache[it] != null }) {
|
|
||||||
val scriptDef = scriptDefinitionProvider.findScriptDefinition(it) ?: return
|
|
||||||
ScriptDependenciesLoader.updateDependencies(it, scriptDef, project, shouldNotifyRootsChanged = true)
|
|
||||||
}
|
|
||||||
modifiedScripts.clear()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun requestUpdate(files: Iterable<VirtualFile>) {
|
|
||||||
files.forEach { file ->
|
|
||||||
if (!file.isValid) {
|
|
||||||
cache.delete(file)
|
|
||||||
} else if (cache[file] != null) { // only update dependencies for scripts that were touched recently
|
|
||||||
modifiedScripts.add(file)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun listenForChangesInScripts() {
|
private fun listenForChangesInScripts() {
|
||||||
project.messageBus.connect().subscribe(FileEditorManagerListener.FILE_EDITOR_MANAGER, object : FileEditorManagerListener {
|
project.messageBus.connect().subscribe(FileEditorManagerListener.FILE_EDITOR_MANAGER, object : FileEditorManagerListener {
|
||||||
override fun fileOpened(source: FileEditorManager, file: VirtualFile) {
|
override fun fileOpened(source: FileEditorManager, file: VirtualFile) {
|
||||||
@@ -95,17 +75,24 @@ class ScriptDependenciesUpdater(
|
|||||||
|
|
||||||
val document = event.document
|
val document = event.document
|
||||||
val file = FileDocumentManager.getInstance().getFile(document)?.takeIf { it.isInLocalFileSystem } ?: return
|
val file = FileDocumentManager.getInstance().getFile(document)?.takeIf { it.isInLocalFileSystem } ?: return
|
||||||
|
if (!file.isValid) {
|
||||||
|
cache.delete(file)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// only update dependencies for scripts that were touched recently
|
||||||
|
if (cache[file] == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val scriptDef = scriptDefinitionProvider.findScriptDefinition(file) ?: return
|
||||||
|
|
||||||
scriptsQueue.cancelAllRequests()
|
scriptsQueue.cancelAllRequests()
|
||||||
|
|
||||||
scriptsQueue.addRequest(
|
scriptsQueue.addRequest(
|
||||||
{
|
{
|
||||||
FileDocumentManager.getInstance().saveDocument(document)
|
FileDocumentManager.getInstance().saveDocument(document)
|
||||||
requestUpdate(listOf(file))
|
ScriptDependenciesLoader.updateDependencies(file, scriptDef, project, shouldNotifyRootsChanged = true)
|
||||||
|
|
||||||
if (KotlinScriptingSettings.getInstance(project).isAutoReloadEnabled) {
|
|
||||||
reloadModifiedScripts()
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
scriptChangesListenerDelay,
|
scriptChangesListenerDelay,
|
||||||
true
|
true
|
||||||
|
|||||||
+72
@@ -0,0 +1,72 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-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.core.script
|
||||||
|
|
||||||
|
import com.intellij.openapi.fileEditor.FileEditor
|
||||||
|
import com.intellij.openapi.fileEditor.FileEditorManager
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.openapi.util.Key
|
||||||
|
import com.intellij.openapi.util.Ref
|
||||||
|
import com.intellij.openapi.vfs.VirtualFile
|
||||||
|
import com.intellij.ui.EditorNotificationPanel
|
||||||
|
import com.intellij.ui.HyperlinkLabel
|
||||||
|
import org.jetbrains.kotlin.idea.core.script.settings.KotlinScriptingSettings
|
||||||
|
import org.jetbrains.kotlin.psi.UserDataProperty
|
||||||
|
import kotlin.script.experimental.dependencies.DependenciesResolver
|
||||||
|
|
||||||
|
fun VirtualFile.removeScriptDependenciesNotificationPanel(project: Project) {
|
||||||
|
val editor = FileEditorManager.getInstance(project).getSelectedEditor(this) ?: return
|
||||||
|
editor.notificationPanel?.let { FileEditorManager.getInstance(project).removeTopComponent(editor, it) }
|
||||||
|
editor.notificationPanel = null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun VirtualFile.addScriptDependenciesNotificationPanel(
|
||||||
|
result: DependenciesResolver.ResolveResult,
|
||||||
|
project: Project,
|
||||||
|
onClick: (DependenciesResolver.ResolveResult) -> Unit
|
||||||
|
) {
|
||||||
|
val editor = FileEditorManager.getInstance(project).getSelectedEditor(this) ?: return
|
||||||
|
val existingPanel = editor.notificationPanel
|
||||||
|
if (existingPanel != null) {
|
||||||
|
if (existingPanel.result == result) return
|
||||||
|
editor.notificationPanel?.let {
|
||||||
|
FileEditorManager.getInstance(project).removeTopComponent(editor, it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val panel = NewScriptDependenciesNotificationPanel(onClick, result, project)
|
||||||
|
editor.notificationPanel = panel
|
||||||
|
FileEditorManager.getInstance(project).addTopComponent(editor, panel)
|
||||||
|
}
|
||||||
|
|
||||||
|
private var FileEditor.notificationPanel: NewScriptDependenciesNotificationPanel? by UserDataProperty<FileEditor, NewScriptDependenciesNotificationPanel>(Key.create("script.dependencies.panel"))
|
||||||
|
|
||||||
|
private class NewScriptDependenciesNotificationPanel(
|
||||||
|
onClick: (DependenciesResolver.ResolveResult) -> Unit,
|
||||||
|
val result: DependenciesResolver.ResolveResult,
|
||||||
|
project: Project
|
||||||
|
) : EditorNotificationPanel() {
|
||||||
|
|
||||||
|
init {
|
||||||
|
setText("There are new script dependencies available.")
|
||||||
|
createComponentActionLabel("Reload dependencies") {
|
||||||
|
onClick(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
createComponentActionLabel("Enable auto-reload") {
|
||||||
|
onClick(result)
|
||||||
|
KotlinScriptingSettings.getInstance(project).isAutoReloadEnabled = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun EditorNotificationPanel.createComponentActionLabel(labelText: String, callback: (HyperlinkLabel) -> Unit) {
|
||||||
|
val label: Ref<HyperlinkLabel> = Ref.create()
|
||||||
|
val action = Runnable {
|
||||||
|
callback(label.get())
|
||||||
|
}
|
||||||
|
label.set(createActionLabel(labelText, action))
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
@@ -40,6 +40,7 @@ class AsyncScriptDependenciesLoader(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun shouldUseBackgroundThread() = KotlinScriptingSettings.getInstance(project).isAutoReloadEnabled
|
override fun shouldUseBackgroundThread() = KotlinScriptingSettings.getInstance(project).isAutoReloadEnabled
|
||||||
|
override fun shouldShowNotification(): Boolean = !KotlinScriptingSettings.getInstance(project).isAutoReloadEnabled
|
||||||
|
|
||||||
private var lastRequest: ModStampedRequest? = null
|
private var lastRequest: ModStampedRequest? = null
|
||||||
|
|
||||||
|
|||||||
+1
@@ -31,4 +31,5 @@ class FromFileAttributeScriptDependenciesLoader(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun shouldUseBackgroundThread() = false
|
override fun shouldUseBackgroundThread() = false
|
||||||
|
override fun shouldShowNotification(): Boolean = false
|
||||||
}
|
}
|
||||||
+23
-7
@@ -15,9 +15,7 @@ import com.intellij.openapi.util.EmptyRunnable
|
|||||||
import com.intellij.openapi.vfs.VirtualFile
|
import com.intellij.openapi.vfs.VirtualFile
|
||||||
import com.intellij.util.containers.SLRUMap
|
import com.intellij.util.containers.SLRUMap
|
||||||
import kotlinx.coroutines.experimental.launch
|
import kotlinx.coroutines.experimental.launch
|
||||||
import org.jetbrains.kotlin.idea.core.script.ScriptDependenciesCache
|
import org.jetbrains.kotlin.idea.core.script.*
|
||||||
import org.jetbrains.kotlin.idea.core.script.ScriptDependenciesModificationTracker
|
|
||||||
import org.jetbrains.kotlin.idea.core.script.scriptDependencies
|
|
||||||
import org.jetbrains.kotlin.idea.core.util.EDT
|
import org.jetbrains.kotlin.idea.core.util.EDT
|
||||||
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
||||||
import org.jetbrains.kotlin.script.*
|
import org.jetbrains.kotlin.script.*
|
||||||
@@ -68,16 +66,36 @@ abstract class ScriptDependenciesLoader(
|
|||||||
|
|
||||||
protected abstract fun loadDependencies()
|
protected abstract fun loadDependencies()
|
||||||
protected abstract fun shouldUseBackgroundThread(): Boolean
|
protected abstract fun shouldUseBackgroundThread(): Boolean
|
||||||
|
protected abstract fun shouldShowNotification(): Boolean
|
||||||
|
|
||||||
protected val contentLoader = ScriptContentLoader(project)
|
protected val contentLoader = ScriptContentLoader(project)
|
||||||
protected val cache: ScriptDependenciesCache = ServiceManager.getService(project, ScriptDependenciesCache::class.java)
|
protected val cache: ScriptDependenciesCache = ServiceManager.getService(project, ScriptDependenciesCache::class.java)
|
||||||
|
|
||||||
protected fun processResult(result: DependenciesResolver.ResolveResult) {
|
protected fun processResult(result: DependenciesResolver.ResolveResult) {
|
||||||
saveDependencies(result)
|
ServiceManager.getService(project, ScriptReportSink::class.java)?.attachReports(file, result.reports)
|
||||||
|
|
||||||
|
val newDependencies = result.dependencies?.adjustByDefinition(scriptDef) ?: return
|
||||||
|
if (cache[file] != newDependencies) {
|
||||||
|
if (shouldShowNotification() && cache[file] != null && !ApplicationManager.getApplication().isUnitTestMode) {
|
||||||
|
file.addScriptDependenciesNotificationPanel(result, project) {
|
||||||
|
saveDependencies(result)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
saveDependencies(result)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (shouldShowNotification()) {
|
||||||
|
file.removeScriptDependenciesNotificationPanel(project)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
loaders.remove(file)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun saveDependencies(result: DependenciesResolver.ResolveResult) {
|
private fun saveDependencies(result: DependenciesResolver.ResolveResult) {
|
||||||
ServiceManager.getService(project, ScriptReportSink::class.java)?.attachReports(file, result.reports)
|
if (shouldShowNotification()) {
|
||||||
|
file.removeScriptDependenciesNotificationPanel(project)
|
||||||
|
}
|
||||||
|
|
||||||
val newDependencies = result.dependencies?.adjustByDefinition(scriptDef) ?: ScriptDependencies.Empty
|
val newDependencies = result.dependencies?.adjustByDefinition(scriptDef) ?: ScriptDependencies.Empty
|
||||||
|
|
||||||
@@ -93,8 +111,6 @@ abstract class ScriptDependenciesLoader(
|
|||||||
if (rootsChanged) {
|
if (rootsChanged) {
|
||||||
notifyRootsChanged()
|
notifyRootsChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
loaders.remove(file)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("EXPERIMENTAL_FEATURE_WARNING")
|
@Suppress("EXPERIMENTAL_FEATURE_WARNING")
|
||||||
|
|||||||
+1
@@ -22,4 +22,5 @@ class SyncScriptDependenciesLoader(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun shouldUseBackgroundThread(): Boolean = false
|
override fun shouldUseBackgroundThread(): Boolean = false
|
||||||
|
override fun shouldShowNotification(): Boolean = false
|
||||||
}
|
}
|
||||||
-2
@@ -283,8 +283,6 @@ class ReloadGradleTemplatesOnSync : ExternalSystemTaskNotificationListenerAdapte
|
|||||||
val project = id.findProject() ?: return
|
val project = id.findProject() ?: return
|
||||||
val gradleDefinitionsContributor = ScriptDefinitionContributor.find<GradleScriptDefinitionsContributor>(project)
|
val gradleDefinitionsContributor = ScriptDefinitionContributor.find<GradleScriptDefinitionsContributor>(project)
|
||||||
gradleDefinitionsContributor?.reloadIfNeccessary()
|
gradleDefinitionsContributor?.reloadIfNeccessary()
|
||||||
|
|
||||||
ServiceManager.getService(project, ScriptDependenciesUpdater::class.java).reloadModifiedScripts()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
-2
@@ -285,8 +285,6 @@ class ReloadGradleTemplatesOnSync : ExternalSystemTaskNotificationListenerAdapte
|
|||||||
val project = id.findProject() ?: return
|
val project = id.findProject() ?: return
|
||||||
val gradleDefinitionsContributor = ScriptDefinitionContributor.find<GradleScriptDefinitionsContributor>(project)
|
val gradleDefinitionsContributor = ScriptDefinitionContributor.find<GradleScriptDefinitionsContributor>(project)
|
||||||
gradleDefinitionsContributor?.reloadIfNeccessary()
|
gradleDefinitionsContributor?.reloadIfNeccessary()
|
||||||
|
|
||||||
ServiceManager.getService(project, ScriptDependenciesUpdater::class.java).reloadModifiedScripts()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user