ScriptConfigurationManager: rebuild cache synchronously from default loader

This commit is contained in:
Sergey Rostov
2020-05-01 15:47:37 +03:00
parent feac19f46e
commit 347f69b1fd
5 changed files with 73 additions and 33 deletions
@@ -6,6 +6,8 @@
package org.jetbrains.kotlin.idea.core.script.configuration
import com.intellij.ProjectTopics
import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.progress.ProcessCanceledException
import com.intellij.openapi.project.Project
import com.intellij.openapi.projectRoots.Sdk
import com.intellij.openapi.roots.ModuleRootEvent
@@ -111,13 +113,16 @@ class CompositeScriptConfigurationManager(val project: Project) : ScriptConfigur
* Loads script configuration if classpath roots don't contain [file] yet
*/
private fun getActualClasspathRoots(file: VirtualFile): ScriptClassRootsCache {
val classpathRoots = classpathRoots
if (classpathRoots.contains(file)) {
return classpathRoots
try {
// we should run default loader if this [file] is not cached in [classpathRoots]
// and it is not supported by any of [plugins]
// getOrLoadConfiguration will do this
// (despite that it's result becomes unused, it still may populate [classpathRoots])
getOrLoadConfiguration(file, null)
} catch (cancelled: ProcessCanceledException) {
// read actions may be cancelled if we are called by impatient reader
}
getOrLoadConfiguration(file)
return this.classpathRoots
}
@@ -223,7 +223,7 @@ class DefaultScriptingSupport(manager: CompositeScriptConfigurationManager) : De
file.removeScriptDependenciesNotificationPanel(project)
}
saveReports(file, newResult.reports)
setAppliedConfiguration(file, newResult)
setAppliedConfiguration(file, newResult, syncUpdate = true)
} else {
debug(file) {
"configuration changed, notification is shown: old = $oldConfiguration, new = $newConfiguration"
@@ -377,15 +377,21 @@ abstract class DefaultScriptingSupportBase(val manager: CompositeScriptConfigura
protected open fun setAppliedConfiguration(
file: VirtualFile,
newConfigurationSnapshot: ScriptConfigurationSnapshot?
newConfigurationSnapshot: ScriptConfigurationSnapshot?,
syncUpdate: Boolean = false
) {
manager.updater.checkInTransaction()
val newConfiguration = newConfigurationSnapshot?.configuration
debug(file) { "configuration changed = $newConfiguration" }
if (newConfiguration != null) {
manager.updater.invalidate(file)
cache.setApplied(file, newConfigurationSnapshot)
if (syncUpdate) {
manager.updater.updateSynchronously()
} else {
manager.updater.invalidate(file)
}
}
}
@@ -15,6 +15,7 @@ import com.intellij.util.containers.HashSetQueue
import org.jetbrains.kotlin.idea.core.script.debug
import org.jetbrains.kotlin.idea.core.util.KotlinIdeaCoreBundle
import java.util.*
import javax.swing.SwingUtilities
/**
* Sequentially loads script configuration in background.
@@ -227,9 +228,12 @@ internal class DefaultBackgroundExecutor(
override fun start() {
super.start()
BackgroundTaskUtil.executeOnPooledThread(project, Runnable {
run()
})
// executeOnPooledThread requires read lock, and we may fail to acquire it
SwingUtilities.invokeLater {
BackgroundTaskUtil.executeOnPooledThread(project, {
run()
})
}
}
override fun close() {
@@ -22,6 +22,8 @@ import org.jetbrains.kotlin.idea.core.script.configuration.ScriptingSupportHelpe
import org.jetbrains.kotlin.idea.core.script.debug
import org.jetbrains.kotlin.idea.util.application.runWriteAction
import java.util.concurrent.atomic.AtomicInteger
import java.util.concurrent.locks.ReentrantLock
import kotlin.concurrent.withLock
/**
* Utility for postponing indexing of new roots to the end of some bulk operation.
@@ -78,6 +80,7 @@ class ScriptClassRootsUpdater(
ensureUpdateScheduled()
}
private val syncLock = ReentrantLock()
private var scheduledUpdate: ProgressIndicator? = null
@Synchronized
@@ -88,31 +91,48 @@ class ScriptClassRootsUpdater(
}
}
fun doUpdate() {
val updates = manager.collectRootsAndCheckNew()
if (!updates.changed) return
ProgressManager.checkCanceled()
if (updates.hasNewRoots) {
notifyRootsChanged()
/**
* Used from legacy FS cache only, don't use
*/
@Synchronized
fun updateSynchronously() {
syncLock.withLock {
scheduledUpdate?.cancel()
doUpdate(false)
}
}
PsiElementFinder.EP.findExtensionOrFail(KotlinScriptDependenciesClassFinder::class.java, project)
.clearCache()
fun doUpdate(underProgressManager: Boolean = true) {
syncLock.withLock {
val updates = manager.collectRootsAndCheckNew()
ScriptDependenciesModificationTracker.getInstance(project).incModificationCount()
if (!updates.changed) return
if (updates.updatedScripts.isNotEmpty()) {
ScriptingSupportHelper.updateHighlighting(project) {
it.path in updates.updatedScripts
try {
ProgressManager.checkCanceled()
if (updates.hasNewRoots) {
notifyRootsChanged()
}
PsiElementFinder.EP.findExtensionOrFail(KotlinScriptDependenciesClassFinder::class.java, project)
.clearCache()
ScriptDependenciesModificationTracker.getInstance(project).incModificationCount()
if (updates.updatedScripts.isNotEmpty()) {
ScriptingSupportHelper.updateHighlighting(project) {
it.path in updates.updatedScripts
}
}
} catch (cancel: ProcessCanceledException) {
if (underProgressManager) throw cancel
} finally {
synchronized(this) {
scheduledUpdate = null
}
}
}
synchronized(this) {
scheduledUpdate = null
}
}
private fun notifyRootsChanged() {
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.idea.scripting.gradle
import com.intellij.openapi.components.service
import com.intellij.openapi.progress.ProcessCanceledException
import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.VirtualFile
import org.jetbrains.kotlin.idea.core.script.configuration.cache.CachedConfigurationInputs
@@ -25,10 +26,14 @@ data class GradleKotlinScriptConfigurationInputs(
val inputsTS: Long
) : CachedConfigurationInputs {
override fun isUpToDate(project: Project, file: VirtualFile, ktFile: KtFile?): Boolean {
val actualStamp = getGradleScriptInputsStamp(project, file, ktFile) ?: return false
try {
val actualStamp = getGradleScriptInputsStamp(project, file, ktFile) ?: return false
if (actualStamp.sections != this.sections) return false
if (actualStamp.sections != this.sections) return false
return project.service<GradleScriptInputsWatcher>().areRelatedFilesUpToDate(file, inputsTS)
return project.service<GradleScriptInputsWatcher>().areRelatedFilesUpToDate(file, inputsTS)
} catch (cancel: ProcessCanceledException) {
return false
}
}
}