diff --git a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/configuration/CompositeScriptConfigurationManager.kt b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/configuration/CompositeScriptConfigurationManager.kt index 1f879f067ba..dc675da879e 100644 --- a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/configuration/CompositeScriptConfigurationManager.kt +++ b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/configuration/CompositeScriptConfigurationManager.kt @@ -96,7 +96,7 @@ class CompositeScriptConfigurationManager(val project: Project) : ScriptConfigur default.updateScriptDefinitionsReferences() if (classpathRoots.customDefinitionsUsed) { - updater.ensureUpdateScheduled() + updater.invalidateAndCommit() } } diff --git a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/uсache/ScriptClassRootsUpdater.kt b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/uсache/ScriptClassRootsUpdater.kt index 63026b6a752..7c4470857cc 100644 --- a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/uсache/ScriptClassRootsUpdater.kt +++ b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/uсache/ScriptClassRootsUpdater.kt @@ -34,12 +34,22 @@ import java.util.concurrent.locks.ReentrantLock import kotlin.concurrent.withLock /** - * Utility for postponing indexing of new roots to the end of some bulk operation. + * Holder for [ScriptClassRootsCache]. + * + * Updates of [classpathRoots] performed asynchronously using the copy-on-write strategy. + * [gatherRoots] called when updating is required. Cache will built from the scratch. + * + * Updates can be coalesced by using `update { invalidate() }` transaction. + * As an alternative you can just call [invalidateAndCommit]. + * + * After update roots changed event will be triggered if there are new root. + * This will start indexing. + * Also analysis cache will be cleared and changed opened script files will be reanalyzed. */ class ScriptClassRootsUpdater( val project: Project, val manager: CompositeScriptConfigurationManager, - private val buildNewRoots: (ScriptClassRootsBuilder) -> Unit + private val gatherRoots: (ScriptClassRootsBuilder) -> Unit ) { private var lastSeen: ScriptClassRootsCache? = null private var invalidated: Boolean = false @@ -48,10 +58,11 @@ class ScriptClassRootsUpdater( private fun recreateRootsCache(): ScriptClassRootsCache { val builder = ScriptClassRootsBuilder(project) - buildNewRoots(builder) + gatherRoots(builder) return builder.build() } + @Volatile var classpathRoots: ScriptClassRootsCache = recreateRootsCache() private set @@ -77,6 +88,10 @@ class ScriptClassRootsUpdater( } } + fun invalidateAndCommit() { + update { invalidate() } + } + fun checkInTransaction() { check(concurrentUpdates.get() > 0) } @@ -119,7 +134,7 @@ class ScriptClassRootsUpdater( private var scheduledUpdate: ProgressIndicator? = null @Synchronized - fun ensureUpdateScheduled() { + private fun ensureUpdateScheduled() { scheduledUpdate?.cancel() runReadAction { if (project.isDisposed && !Disposer.isDisposing(project)) { @@ -136,12 +151,14 @@ class ScriptClassRootsUpdater( doUpdate(false) } - fun checkInvalidSdks(remove: Sdk? = null) { + internal fun checkInvalidSdks(remove: Sdk? = null) { // sdks should be updated synchronously to avoid disposed roots usage syncLock.withLock { val current = classpathRoots val actualSdks = current.sdks.rebuild(remove = remove) if (actualSdks != current.sdks) { + // don't call invalidateAndCommit as it may be synchronous + // let's update sdks immediately and schedule cache rebuilding classpathRoots = current.withUpdatedSdks(actualSdks) ensureUpdateScheduled() } diff --git a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/roots/GradleBuildRootsManager.kt b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/roots/GradleBuildRootsManager.kt index 74a48674b8c..b6ae1cd11f5 100644 --- a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/roots/GradleBuildRootsManager.kt +++ b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/roots/GradleBuildRootsManager.kt @@ -342,7 +342,7 @@ class GradleBuildRootsManager(val project: Project) : ScriptingSupport() { val old = roots.add(newRoot) if (old is GradleBuildRoot.Imported) removeData(old.pathPrefix) if (old is GradleBuildRoot.Imported || newRoot is GradleBuildRoot.Imported) { - updater.ensureUpdateScheduled() + updater.invalidateAndCommit() } updateNotifications(newRoot.pathPrefix) @@ -352,7 +352,7 @@ class GradleBuildRootsManager(val project: Project) : ScriptingSupport() { val removed = roots.remove(rootPath) if (removed is GradleBuildRoot.Imported) { removeData(rootPath) - updater.ensureUpdateScheduled() + updater.invalidateAndCommit() } updateNotifications(rootPath)