GradleScriptInputsWatcher: move long running operation out of EDT

^KT-36502
This commit is contained in:
Natalia Selezneva
2020-02-11 15:08:05 +03:00
parent 2d15914d20
commit dc77df1083
5 changed files with 115 additions and 43 deletions

View File

@@ -0,0 +1,33 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.scripting.gradle
import com.intellij.openapi.externalSystem.service.project.autoimport.AsyncFileChangeListenerBase
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.openapi.vfs.VirtualFileManager
import com.intellij.openapi.vfs.newvfs.events.VFileEvent
fun addVfsListener(watcher: GradleScriptInputsWatcher) {
VirtualFileManager.getInstance().addAsyncFileListener(
object : AsyncFileChangeListenerBase() {
override fun isRelevant(path: String): Boolean {
val files = getAffectedGradleProjectFiles(watcher.project)
return isInAffectedGradleProjectFiles(files, path)
}
override fun updateFile(file: VirtualFile, event: VFileEvent) {
watcher.fileChanged(event.path, file.timeStamp)
}
// do nothing
override fun prepareFileDeletion(file: VirtualFile) {}
override fun apply() {}
override fun reset() {}
},
watcher.project,
)
}

View File

@@ -0,0 +1,31 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.scripting.gradle
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.vfs.VirtualFileManager
import com.intellij.openapi.vfs.newvfs.BulkFileListener
import com.intellij.openapi.vfs.newvfs.events.VFileEvent
fun addVfsListener(watcher: GradleScriptInputsWatcher) {
watcher.project.messageBus.connect().subscribe(
VirtualFileManager.VFS_CHANGES,
object : BulkFileListener {
override fun after(events: List<VFileEvent>) {
ApplicationManager.getApplication().executeOnPooledThread {
if (watcher.project.isDisposed) return@executeOnPooledThread
val files = getAffectedGradleProjectFiles(watcher.project)
for (event in events) {
val file = event.file ?: return@executeOnPooledThread
if (isInAffectedGradleProjectFiles(files, event.path)) {
watcher.fileChanged(event.path, file.timeStamp)
}
}
}
}
}
)
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.scripting.gradle
import com.intellij.openapi.externalSystem.autoimport.AsyncFileChangeListenerBase
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.openapi.vfs.VirtualFileManager
import com.intellij.openapi.vfs.newvfs.events.VFileEvent
fun addVfsListener(watcher: GradleScriptInputsWatcher) {
VirtualFileManager.getInstance().addAsyncFileListener(
object : AsyncFileChangeListenerBase() {
override fun isRelevant(path: String): Boolean {
val files = getAffectedGradleProjectFiles(watcher.project)
return isInAffectedGradleProjectFiles(files, path)
}
override fun updateFile(file: VirtualFile, event: VFileEvent) {
watcher.fileChanged(event.path, file.timeStamp)
}
// do nothing
override fun apply() {}
override fun init() {}
},
watcher.project,
)
}

View File

@@ -7,15 +7,8 @@ package org.jetbrains.kotlin.idea.scripting.gradle
import com.intellij.openapi.components.*
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.io.FileUtil
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.PathUtil
import org.jetbrains.annotations.TestOnly
import org.junit.Assert.assertEquals
import org.junit.Test
@State(
name = "KotlinBuildScriptsModificationInfo",
@@ -25,43 +18,22 @@ class GradleScriptInputsWatcher(val project: Project) : PersistentStateComponent
private var storage = Storage()
fun startWatching() {
project.messageBus.connect().subscribe(
VirtualFileManager.VFS_CHANGES,
object : BulkFileListener {
override fun after(events: List<VFileEvent>) {
if (project.isDisposed) return
val files = getAffectedGradleProjectFiles(project)
for (event in events) {
val file = event.file ?: return
if (isInAffectedGradleProjectFiles(files, event.path)) {
storage.fileChanged(file, file.timeStamp)
}
}
}
})
addVfsListener(this)
}
fun areRelatedFilesUpToDate(file: VirtualFile, timeStamp: Long): Boolean {
return storage.lastModifiedTimeStampExcept(file) < timeStamp
return storage.lastModifiedTimeStampExcept(file.path) < timeStamp
}
class Storage {
private val lastModifiedFiles = LastModifiedFiles()
fun lastModifiedTimeStampExcept(file: VirtualFile): Long {
val fileId = getFileId(file)
return lastModifiedFiles.lastModifiedTimeStampExcept(fileId)
fun lastModifiedTimeStampExcept(filePath: String): Long {
return lastModifiedFiles.lastModifiedTimeStampExcept(filePath)
}
fun fileChanged(file: VirtualFile, ts: Long) {
val fileId = getFileId(file)
lastModifiedFiles.fileChanged(ts, fileId)
}
private fun getFileId(file: VirtualFile): String {
val canonized = PathUtil.getCanonicalPath(file.path)
return FileUtil.toSystemIndependentName(canonized)
fun fileChanged(filePath: String, ts: Long) {
lastModifiedFiles.fileChanged(ts, filePath)
}
}
@@ -73,13 +45,12 @@ class GradleScriptInputsWatcher(val project: Project) : PersistentStateComponent
this.storage = state
}
fun fileChanged(filePath: String, ts: Long) {
storage.fileChanged(filePath, ts)
}
@TestOnly
fun clearAndRefillState() {
loadState(project.service<GradleScriptInputsWatcher>().state)
}
@TestOnly
fun fileChanged(file: VirtualFile, ts: Long) {
storage.fileChanged(file, ts)
}
}

View File

@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.idea.scripting.gradle
import com.intellij.openapi.components.service
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.PsiFile
import org.jetbrains.kotlin.idea.core.script.ScriptConfigurationManager
import org.jetbrains.kotlin.idea.script.AbstractScriptConfigurationLoadingTest
@@ -194,8 +195,8 @@ open class GradleScriptInputsWatcherTest : AbstractScriptConfigurationLoadingTes
changeSettingsKtsOutsideSections()
val ts = System.currentTimeMillis()
project.service<GradleScriptInputsWatcher>().fileChanged(testFiles.buildKts.virtualFile, ts)
project.service<GradleScriptInputsWatcher>().fileChanged(testFiles.settings.virtualFile, ts)
markFileChanged(testFiles.buildKts.virtualFile, ts)
markFileChanged(testFiles.settings.virtualFile, ts)
assertConfigurationUpdateWasDone(testFiles.settings)
assertConfigurationUpdateWasDone(testFiles.buildKts)
@@ -206,8 +207,8 @@ open class GradleScriptInputsWatcherTest : AbstractScriptConfigurationLoadingTes
assertAndLoadInitialConfiguration(testFiles.settings)
val ts = System.currentTimeMillis()
project.service<GradleScriptInputsWatcher>().fileChanged(testFiles.buildKts.virtualFile, ts)
project.service<GradleScriptInputsWatcher>().fileChanged(testFiles.settings.virtualFile, ts)
markFileChanged(testFiles.buildKts.virtualFile, ts)
markFileChanged(testFiles.settings.virtualFile, ts)
changePropertiesFile()
@@ -215,6 +216,10 @@ open class GradleScriptInputsWatcherTest : AbstractScriptConfigurationLoadingTes
assertConfigurationUpdateWasDone(testFiles.buildKts)
}
private fun markFileChanged(virtualFile: VirtualFile, ts: Long) {
project.service<GradleScriptInputsWatcher>().fileChanged(virtualFile.path, ts)
}
fun testLoadedConfigurationWhenExternalFileChanged() {
assertAndLoadInitialConfiguration(testFiles.buildKts)