.gradle.kts: fix freezes by avoiding fs walking

^KT-36502 Fixed
This commit is contained in:
Natalia Selezneva
2020-03-02 09:56:38 +03:00
parent 01c4e66edb
commit c0cd010b7d
5 changed files with 44 additions and 44 deletions
@@ -9,16 +9,12 @@ import com.intellij.openapi.externalSystem.service.project.autoimport.AsyncFileC
import com.intellij.openapi.vfs.VirtualFile import com.intellij.openapi.vfs.VirtualFile
import com.intellij.openapi.vfs.VirtualFileManager import com.intellij.openapi.vfs.VirtualFileManager
import com.intellij.openapi.vfs.newvfs.events.VFileEvent import com.intellij.openapi.vfs.newvfs.events.VFileEvent
import java.util.*
fun addVfsListener(watcher: GradleScriptInputsWatcher) { fun addVfsListener(watcher: GradleScriptInputsWatcher) {
VirtualFileManager.getInstance().addAsyncFileListener( VirtualFileManager.getInstance().addAsyncFileListener(
object : AsyncFileChangeListenerBase() { object : AsyncFileChangeListenerBase() {
private var files: Set<String> = Collections.emptySet()
override fun isRelevant(path: String): Boolean { override fun isRelevant(path: String): Boolean {
return isInAffectedGradleProjectFiles(files, path) return isInAffectedGradleProjectFiles(watcher.project, path)
} }
override fun updateFile(file: VirtualFile, event: VFileEvent) { override fun updateFile(file: VirtualFile, event: VFileEvent) {
@@ -28,11 +24,9 @@ fun addVfsListener(watcher: GradleScriptInputsWatcher) {
// do nothing // do nothing
override fun prepareFileDeletion(file: VirtualFile) {} override fun prepareFileDeletion(file: VirtualFile) {}
override fun apply() {} override fun apply() {}
override fun reset() { override fun reset() {}
files = getAffectedGradleProjectFiles(watcher.project)
}
}, },
watcher.project, watcher.project
) )
} }
@@ -5,26 +5,28 @@
package org.jetbrains.kotlin.idea.scripting.gradle package org.jetbrains.kotlin.idea.scripting.gradle
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.vfs.VirtualFileManager import com.intellij.openapi.vfs.VirtualFileManager
import com.intellij.openapi.vfs.newvfs.BulkFileListener import com.intellij.openapi.vfs.newvfs.BulkFileListener
import com.intellij.openapi.vfs.newvfs.events.VFileEvent import com.intellij.openapi.vfs.newvfs.events.VFileEvent
import com.intellij.util.concurrency.SequentialTaskExecutor
fun addVfsListener(watcher: GradleScriptInputsWatcher) { fun addVfsListener(watcher: GradleScriptInputsWatcher) {
watcher.project.messageBus.connect().subscribe( watcher.project.messageBus.connect().subscribe(
VirtualFileManager.VFS_CHANGES, VirtualFileManager.VFS_CHANGES,
object : BulkFileListener { object : BulkFileListener {
private val executor =
SequentialTaskExecutor.createSequentialApplicationPoolExecutor("GradleScriptInputsWatcherVfsChangesExecutor")
override fun after(events: List<VFileEvent>) { override fun after(events: List<VFileEvent>) {
ApplicationManager.getApplication().executeOnPooledThread { executor.submit(Runnable {
if (watcher.project.isDisposed) return@executeOnPooledThread if (watcher.project.isDisposed) return@Runnable
val files = getAffectedGradleProjectFiles(watcher.project)
for (event in events) { for (event in events) {
val file = event.file ?: return@executeOnPooledThread val file = event.file ?: continue
if (isInAffectedGradleProjectFiles(files, event.path)) { if (isInAffectedGradleProjectFiles(watcher.project, event.path)) {
watcher.fileChanged(event.path, file.timeStamp) watcher.fileChanged(event.path, file.timeStamp)
} }
} }
} })
} }
} }
) )
@@ -9,16 +9,12 @@ import com.intellij.openapi.externalSystem.autoimport.AsyncFileChangeListenerBas
import com.intellij.openapi.vfs.VirtualFile import com.intellij.openapi.vfs.VirtualFile
import com.intellij.openapi.vfs.VirtualFileManager import com.intellij.openapi.vfs.VirtualFileManager
import com.intellij.openapi.vfs.newvfs.events.VFileEvent import com.intellij.openapi.vfs.newvfs.events.VFileEvent
import java.util.*
fun addVfsListener(watcher: GradleScriptInputsWatcher) { fun addVfsListener(watcher: GradleScriptInputsWatcher) {
VirtualFileManager.getInstance().addAsyncFileListener( VirtualFileManager.getInstance().addAsyncFileListener(
object : AsyncFileChangeListenerBase() { object : AsyncFileChangeListenerBase() {
private var files: Set<String> = Collections.emptySet();
override fun isRelevant(path: String): Boolean { override fun isRelevant(path: String): Boolean {
return isInAffectedGradleProjectFiles(files, path) return isInAffectedGradleProjectFiles(watcher.project, path)
} }
override fun updateFile(file: VirtualFile, event: VFileEvent) { override fun updateFile(file: VirtualFile, event: VFileEvent) {
@@ -27,11 +23,9 @@ fun addVfsListener(watcher: GradleScriptInputsWatcher) {
// do nothing // do nothing
override fun apply() {} override fun apply() {}
override fun init() { override fun init() {}
files = getAffectedGradleProjectFiles(watcher.project)
}
}, },
watcher.project, watcher.project
) )
} }
@@ -26,6 +26,7 @@ import org.jetbrains.kotlin.idea.core.script.ScriptConfigurationManager
import org.jetbrains.kotlin.idea.core.script.configuration.cache.CachedConfigurationInputs import org.jetbrains.kotlin.idea.core.script.configuration.cache.CachedConfigurationInputs
import org.jetbrains.kotlin.idea.core.script.configuration.cache.ScriptConfigurationSnapshot import org.jetbrains.kotlin.idea.core.script.configuration.cache.ScriptConfigurationSnapshot
import org.jetbrains.kotlin.idea.scripting.gradle.getGradleScriptInputsStamp import org.jetbrains.kotlin.idea.scripting.gradle.getGradleScriptInputsStamp
import org.jetbrains.kotlin.idea.scripting.gradle.saveGradleProjectRootsAfterImport
import org.jetbrains.kotlin.scripting.definitions.findScriptDefinition import org.jetbrains.kotlin.scripting.definitions.findScriptDefinition
import org.jetbrains.kotlin.scripting.resolve.ScriptCompilationConfigurationWrapper import org.jetbrains.kotlin.scripting.resolve.ScriptCompilationConfigurationWrapper
import org.jetbrains.kotlin.scripting.resolve.VirtualFileScriptSource import org.jetbrains.kotlin.scripting.resolve.VirtualFileScriptSource
@@ -55,7 +56,11 @@ class KotlinDslScriptModelDataService : AbstractProjectDataService<ProjectData,
gradleKotlinBuildScripts.clear() gradleKotlinBuildScripts.clear()
val gradleSettings = ExternalSystemApiUtil.getSettings(project, GradleConstants.SYSTEM_ID) val gradleSettings = ExternalSystemApiUtil.getSettings(project, GradleConstants.SYSTEM_ID)
val projectSettings = gradleSettings.getLinkedProjectSettings(projectData?.linkedExternalProjectPath ?: return) ?: return val projectRoot = projectData?.linkedExternalProjectPath ?: return
val projectSettings = gradleSettings.getLinkedProjectSettings(projectRoot) ?: return
saveGradleProjectRootsAfterImport(projectSettings.modules.takeIf { it.isNotEmpty() } ?: setOf(projectRoot))
val gradleExeSettings = ExternalSystemApiUtil.getExecutionSettings<GradleExecutionSettings>( val gradleExeSettings = ExternalSystemApiUtil.getExecutionSettings<GradleExecutionSettings>(
project, project,
projectSettings.externalProjectPath, projectSettings.externalProjectPath,
@@ -12,14 +12,12 @@ import com.intellij.openapi.util.registry.Registry
import com.intellij.openapi.vfs.VirtualFile import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.* import com.intellij.psi.*
import com.intellij.psi.impl.source.tree.LeafPsiElement import com.intellij.psi.impl.source.tree.LeafPsiElement
import com.intellij.util.io.systemIndependentPath
import org.gradle.util.GradleVersion import org.gradle.util.GradleVersion
import org.jetbrains.kotlin.idea.util.application.runReadAction import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.psi.KtCallExpression import org.jetbrains.kotlin.psi.KtCallExpression
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtScriptInitializer import org.jetbrains.kotlin.psi.KtScriptInitializer
import org.jetbrains.kotlin.psi.psiUtil.getChildrenOfType import org.jetbrains.kotlin.psi.psiUtil.getChildrenOfType
import org.jetbrains.plugins.gradle.GradleManager
import org.jetbrains.plugins.gradle.settings.GradleProjectSettings import org.jetbrains.plugins.gradle.settings.GradleProjectSettings
import org.jetbrains.plugins.gradle.util.GradleConstants import org.jetbrains.plugins.gradle.util.GradleConstants
@@ -28,34 +26,41 @@ private val sections = arrayListOf("buildscript", "plugins", "initscript", "plug
fun isGradleKotlinScript(virtualFile: VirtualFile) = virtualFile.name.endsWith(".gradle.kts") fun isGradleKotlinScript(virtualFile: VirtualFile) = virtualFile.name.endsWith(".gradle.kts")
fun isInAffectedGradleProjectFiles(project: Project, filePath: String): Boolean { fun isInAffectedGradleProjectFiles(project: Project, filePath: String): Boolean {
// fast path if (filePath.endsWith("/gradle.properties")) return true
if (!filePath.contains("gradle")) return false if (filePath.endsWith("/gradle-wrapper.properties")) return true
val affectedFiles = getAffectedGradleProjectFiles(project) if (filePath.endsWith(".gradle") || filePath.endsWith(".gradle.kts")) {
return isInAffectedGradleProjectFiles(affectedFiles, filePath) if (ApplicationManager.getApplication().isUnitTestModeWithoutAffectedGradleProjectFilesCheck) {
} return true
}
fun isInAffectedGradleProjectFiles(files: Set<String>, filePath: String): Boolean { return filePath.substringBeforeLast("/") in getGradleProjectsRoots(project)
if (ApplicationManager.getApplication().isUnitTestModeWithoutAffectedGradleProjectFilesCheck) {
return true
} }
return filePath in files return false
} }
fun getAffectedGradleProjectFiles(project: Project): Set<String> { private var cachedGradleProjectsRoots: Set<String>? = null
private fun getGradleProjectsRoots(project: Project): Set<String> {
if (cachedGradleProjectsRoots == null) {
cachedGradleProjectsRoots = computeGradleProjectRoots(project)
}
return cachedGradleProjectsRoots ?: emptySet()
}
fun saveGradleProjectRootsAfterImport(roots: Set<String>) {
cachedGradleProjectsRoots = roots
}
private fun computeGradleProjectRoots(project: Project): Set<String> {
val gradleSettings = ExternalSystemApiUtil.getSettings(project, GradleConstants.SYSTEM_ID) val gradleSettings = ExternalSystemApiUtil.getSettings(project, GradleConstants.SYSTEM_ID)
if (gradleSettings.getLinkedProjectsSettings().isEmpty()) return setOf() if (gradleSettings.getLinkedProjectsSettings().isEmpty()) return setOf()
val projectSettings = gradleSettings.getLinkedProjectsSettings().filterIsInstance<GradleProjectSettings>().firstOrNull() val projectSettings = gradleSettings.getLinkedProjectsSettings().filterIsInstance<GradleProjectSettings>().firstOrNull()
?: return setOf() ?: return setOf()
return ExternalSystemApiUtil.getAllManagers() return projectSettings.modules.takeIf { it.isNotEmpty() } ?: setOf(projectSettings.externalProjectPath)
.filterIsInstance<GradleManager>()
.firstOrNull()
?.getAffectedExternalProjectFiles(projectSettings.externalProjectPath, project)
?.mapTo(mutableSetOf()) { it.toPath().systemIndependentPath }
?: setOf()
} }
fun getGradleScriptInputsStamp( fun getGradleScriptInputsStamp(