diff --git a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/ScriptDependenciesUpdater.kt b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/ScriptDependenciesUpdater.kt index c63ab722fab..aa1dfcfc610 100644 --- a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/ScriptDependenciesUpdater.kt +++ b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/ScriptDependenciesUpdater.kt @@ -18,14 +18,16 @@ package org.jetbrains.kotlin.idea.core.script import com.intellij.openapi.application.Application import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.editor.EditorFactory +import com.intellij.openapi.editor.event.DocumentEvent +import com.intellij.openapi.editor.event.DocumentListener +import com.intellij.openapi.fileEditor.FileDocumentManager +import com.intellij.openapi.fileEditor.FileEditorManager +import com.intellij.openapi.fileEditor.FileEditorManagerListener import com.intellij.openapi.project.Project -import com.intellij.openapi.project.isProjectOrWorkspaceFile -import com.intellij.openapi.roots.ProjectRootManager import com.intellij.openapi.util.Key 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.VFileEvent +import com.intellij.util.Alarm import org.jetbrains.annotations.TestOnly import org.jetbrains.kotlin.idea.core.script.dependencies.FromFileAttributeScriptDependenciesLoader import org.jetbrains.kotlin.idea.core.script.dependencies.ScriptDependenciesLoader @@ -41,9 +43,11 @@ class ScriptDependenciesUpdater( private val scriptDefinitionProvider: ScriptDefinitionProvider ) { private val modifiedScripts = mutableSetOf() + private val scriptsQueue = Alarm(Alarm.ThreadToUse.SWING_THREAD, project) + private val scriptChangesListenerDelay = 1400 init { - listenToVfsChanges() + listenForChangesInScripts() } fun getCurrentDependencies(file: VirtualFile): ScriptDependencies { @@ -75,32 +79,39 @@ class ScriptDependenciesUpdater( } } - private fun listenToVfsChanges() { - project.messageBus.connect().subscribe(VirtualFileManager.VFS_CHANGES, object : BulkFileListener.Adapter() { - val projectFileIndex = ProjectRootManager.getInstance(project).fileIndex - val application = ApplicationManager.getApplication() - - override fun after(events: List) { - if (application.isUnitTestMode && application.isScriptDependenciesUpdaterDisabled == true) { - return - } - - val modifiedScripts = events.mapNotNull { - // The check is partly taken from the BuildManager.java - it.file?.takeIf { - // the isUnitTestMode check fixes ScriptConfigurationHighlighting & Navigation tests, since they are not trigger proper update mechanims - // TODO: find out the reason, then consider to fix tests and remove this check - (application.isUnitTestMode || - scriptDefinitionProvider.isScript(it.name) && projectFileIndex.isInContent(it)) && !isProjectOrWorkspaceFile(it) - } - } - requestUpdate(modifiedScripts) - - if (KotlinScriptingSettings.getInstance(project).isAutoReloadEnabled) { - reloadModifiedScripts() - } + private fun listenForChangesInScripts() { + project.messageBus.connect().subscribe(FileEditorManagerListener.FILE_EDITOR_MANAGER, object : FileEditorManagerListener { + override fun fileOpened(source: FileEditorManager, file: VirtualFile) { + val scriptDef = scriptDefinitionProvider.findScriptDefinition(file) ?: return + ScriptDependenciesLoader.updateDependencies(file, scriptDef, project, shouldNotifyRootsChanged = true) } }) + + EditorFactory.getInstance().eventMulticaster.addDocumentListener(object : DocumentListener { + override fun documentChanged(event: DocumentEvent) { + if (project.isDisposed) return + + if (ApplicationManager.getApplication().isUnitTestMode && ApplicationManager.getApplication().isScriptDependenciesUpdaterDisabled == true) return + + val document = event.document + val file = FileDocumentManager.getInstance().getFile(document)?.takeIf { it.isInLocalFileSystem } ?: return + + scriptsQueue.cancelAllRequests() + + scriptsQueue.addRequest( + { + FileDocumentManager.getInstance().saveDocument(document) + requestUpdate(listOf(file)) + + if (KotlinScriptingSettings.getInstance(project).isAutoReloadEnabled) { + reloadModifiedScripts() + } + }, + scriptChangesListenerDelay, + true + ) + } + }, project.messageBus.connect()) } } diff --git a/idea/tests/org/jetbrains/kotlin/idea/script/AbstractScriptConfigurationTest.kt b/idea/tests/org/jetbrains/kotlin/idea/script/AbstractScriptConfigurationTest.kt index 65d66963b5e..17f2957050f 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/script/AbstractScriptConfigurationTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/script/AbstractScriptConfigurationTest.kt @@ -17,6 +17,7 @@ package org.jetbrains.kotlin.idea.script import com.intellij.codeInsight.highlighting.HighlightUsagesHandler +import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.extensions.Extensions import com.intellij.openapi.module.JavaModuleType import com.intellij.openapi.module.Module @@ -34,6 +35,7 @@ import org.jetbrains.kotlin.idea.completion.test.KotlinCompletionTestCase import org.jetbrains.kotlin.idea.core.script.ScriptDefinitionContributor import org.jetbrains.kotlin.idea.core.script.ScriptDefinitionsManager import org.jetbrains.kotlin.idea.core.script.ScriptDependenciesManager.Companion.updateScriptDependenciesSynchronously +import org.jetbrains.kotlin.idea.core.script.isScriptDependenciesUpdaterDisabled import org.jetbrains.kotlin.idea.navigation.GotoCheck import org.jetbrains.kotlin.idea.util.application.runWriteAction import org.jetbrains.kotlin.test.InTextDirectivesUtils @@ -61,6 +63,16 @@ abstract class AbstractScriptConfigurationHighlightingTest : AbstractScriptConfi InTextDirectivesUtils.isDirectiveDefined(file.text, "// CHECK_WARNINGS"), InTextDirectivesUtils.isDirectiveDefined(file.text, "// CHECK_INFOS")) } + + override fun setUp() { + super.setUp() + ApplicationManager.getApplication().isScriptDependenciesUpdaterDisabled = true + } + + override fun tearDown() { + ApplicationManager.getApplication().isScriptDependenciesUpdaterDisabled = false + super.tearDown() + } } abstract class AbstractScriptConfigurationNavigationTest : AbstractScriptConfigurationTest() {