Mark not applied configuration as up-to-date, simplify concurrency handling
This commit is contained in:
committed by
Natalia Selezneva
parent
0422abdb7b
commit
9105e38041
+1
-7
@@ -34,8 +34,7 @@ fun VirtualFile.removeScriptDependenciesNotificationPanel(project: Project) {
|
|||||||
fun VirtualFile.addScriptDependenciesNotificationPanel(
|
fun VirtualFile.addScriptDependenciesNotificationPanel(
|
||||||
compilationConfigurationResult: ScriptCompilationConfigurationWrapper,
|
compilationConfigurationResult: ScriptCompilationConfigurationWrapper,
|
||||||
project: Project,
|
project: Project,
|
||||||
onClick: () -> Unit,
|
onClick: () -> Unit
|
||||||
onHide: () -> Unit
|
|
||||||
) {
|
) {
|
||||||
withSelectedEditor(project) { manager ->
|
withSelectedEditor(project) { manager ->
|
||||||
val existingPanel = notificationPanel
|
val existingPanel = notificationPanel
|
||||||
@@ -50,11 +49,6 @@ fun VirtualFile.addScriptDependenciesNotificationPanel(
|
|||||||
|
|
||||||
val panel = NewScriptDependenciesNotificationPanel(onClick, compilationConfigurationResult, project)
|
val panel = NewScriptDependenciesNotificationPanel(onClick, compilationConfigurationResult, project)
|
||||||
notificationPanel = panel
|
notificationPanel = panel
|
||||||
panel.addComponentListener(object: ComponentAdapter() {
|
|
||||||
override fun componentHidden(e: ComponentEvent) {
|
|
||||||
onHide()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
manager.addTopComponent(this, panel)
|
manager.addTopComponent(this, panel)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+7
@@ -193,6 +193,13 @@ internal abstract class AbstractScriptConfigurationManager(
|
|||||||
updateHighlighting(listOf(file))
|
updateHighlighting(listOf(file))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected fun markUpToDate(
|
||||||
|
file: VirtualFile,
|
||||||
|
inputs: CachedConfigurationInputs
|
||||||
|
) {
|
||||||
|
cache.markUpToDate(file, inputs)
|
||||||
|
}
|
||||||
|
|
||||||
private fun hasNotCachedRoots(configuration: ScriptCompilationConfigurationWrapper): Boolean {
|
private fun hasNotCachedRoots(configuration: ScriptCompilationConfigurationWrapper): Boolean {
|
||||||
return classpathRoots.hasNotCachedRoots(configuration)
|
return classpathRoots.hasNotCachedRoots(configuration)
|
||||||
}
|
}
|
||||||
|
|||||||
+32
-72
@@ -15,25 +15,26 @@ import com.intellij.ui.EditorNotifications
|
|||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.GlobalScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.jetbrains.kotlin.idea.core.script.*
|
import org.jetbrains.kotlin.idea.core.script.*
|
||||||
import org.jetbrains.kotlin.idea.core.script.configuration.cache.*
|
|
||||||
import org.jetbrains.kotlin.idea.core.script.configuration.cache.ScriptConfigurationFileAttributeCache
|
import org.jetbrains.kotlin.idea.core.script.configuration.cache.ScriptConfigurationFileAttributeCache
|
||||||
|
import org.jetbrains.kotlin.idea.core.script.configuration.cache.ScriptConfigurationMemoryCache
|
||||||
|
import org.jetbrains.kotlin.idea.core.script.configuration.cache.ScriptConfigurationSnapshot
|
||||||
import org.jetbrains.kotlin.idea.core.script.configuration.listener.DefaultScriptChangeListener
|
import org.jetbrains.kotlin.idea.core.script.configuration.listener.DefaultScriptChangeListener
|
||||||
import org.jetbrains.kotlin.idea.core.script.configuration.listener.ScriptChangeListener
|
import org.jetbrains.kotlin.idea.core.script.configuration.listener.ScriptChangeListener
|
||||||
import org.jetbrains.kotlin.idea.core.script.configuration.loader.*
|
import org.jetbrains.kotlin.idea.core.script.configuration.listener.ScriptChangesNotifier
|
||||||
|
import org.jetbrains.kotlin.idea.core.script.configuration.listener.ScriptConfigurationUpdater
|
||||||
import org.jetbrains.kotlin.idea.core.script.configuration.loader.DefaultScriptConfigurationLoader
|
import org.jetbrains.kotlin.idea.core.script.configuration.loader.DefaultScriptConfigurationLoader
|
||||||
|
import org.jetbrains.kotlin.idea.core.script.configuration.loader.ScriptConfigurationLoader
|
||||||
|
import org.jetbrains.kotlin.idea.core.script.configuration.loader.ScriptConfigurationLoadingContext
|
||||||
import org.jetbrains.kotlin.idea.core.script.configuration.loader.ScriptOutsiderFileConfigurationLoader
|
import org.jetbrains.kotlin.idea.core.script.configuration.loader.ScriptOutsiderFileConfigurationLoader
|
||||||
import org.jetbrains.kotlin.idea.core.script.configuration.utils.BackgroundExecutor
|
import org.jetbrains.kotlin.idea.core.script.configuration.utils.BackgroundExecutor
|
||||||
import org.jetbrains.kotlin.idea.core.script.configuration.listener.ScriptChangesNotifier
|
|
||||||
import org.jetbrains.kotlin.idea.core.script.settings.KotlinScriptingSettings
|
import org.jetbrains.kotlin.idea.core.script.settings.KotlinScriptingSettings
|
||||||
import org.jetbrains.kotlin.idea.core.util.EDT
|
import org.jetbrains.kotlin.idea.core.util.EDT
|
||||||
import org.jetbrains.kotlin.psi.KtFile
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
import org.jetbrains.kotlin.scripting.definitions.findScriptDefinition
|
import org.jetbrains.kotlin.scripting.definitions.findScriptDefinition
|
||||||
import org.jetbrains.kotlin.scripting.resolve.ScriptReportSink
|
import org.jetbrains.kotlin.scripting.resolve.ScriptReportSink
|
||||||
import java.util.*
|
|
||||||
import java.util.concurrent.locks.ReentrantLock
|
import java.util.concurrent.locks.ReentrantLock
|
||||||
import kotlin.concurrent.withLock
|
import kotlin.concurrent.withLock
|
||||||
import kotlin.script.experimental.api.ScriptDiagnostic
|
import kotlin.script.experimental.api.ScriptDiagnostic
|
||||||
import org.jetbrains.kotlin.idea.core.script.configuration.listener.ScriptConfigurationUpdater
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Standard implementation of scripts configuration loading and caching
|
* Standard implementation of scripts configuration loading and caching
|
||||||
@@ -111,24 +112,9 @@ internal class DefaultScriptConfigurationManager(project: Project) :
|
|||||||
|
|
||||||
private val notifier = ScriptChangesNotifier(project, updater, listeners)
|
private val notifier = ScriptChangesNotifier(project, updater, listeners)
|
||||||
|
|
||||||
/**
|
|
||||||
* Loaded but not applied result.
|
|
||||||
* Weakness required since it is hard to track editor and notification hiding.
|
|
||||||
*/
|
|
||||||
private val notApplied = WeakHashMap<VirtualFile, LoadedScriptConfiguration>()
|
|
||||||
private val saveLock = ReentrantLock()
|
private val saveLock = ReentrantLock()
|
||||||
|
|
||||||
override fun createCache(): ScriptConfigurationCache {
|
override fun createCache() = ScriptConfigurationMemoryCache(project)
|
||||||
return object : ScriptConfigurationMemoryCache(project) {
|
|
||||||
override fun markOutOfDate(file: VirtualFile) {
|
|
||||||
super.markOutOfDate(file)
|
|
||||||
|
|
||||||
synchronized(notApplied) {
|
|
||||||
notApplied.remove(file)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Will be called on [cache] miss to initiate loading of [file]'s script configuration.
|
* Will be called on [cache] miss to initiate loading of [file]'s script configuration.
|
||||||
@@ -146,17 +132,17 @@ internal class DefaultScriptConfigurationManager(project: Project) :
|
|||||||
* - invalid, waiting for apply. `cache[file]?.upToDate == false && file !in backgroundExecutor` and has notification panel?
|
* - invalid, waiting for apply. `cache[file]?.upToDate == false && file !in backgroundExecutor` and has notification panel?
|
||||||
*
|
*
|
||||||
* Async:
|
* Async:
|
||||||
* - up-to-date:
|
* - up-to-date: [reloadOutOfDateConfiguration] will not be called.
|
||||||
* [reloadOutOfDateConfiguration] will not be called.
|
|
||||||
* - `unknown` and `invalid, in queue`:
|
* - `unknown` and `invalid, in queue`:
|
||||||
* Concurrent async loading will be guarded by `backgroundExecutor.ensureScheduled`
|
* Concurrent async loading will be guarded by `backgroundExecutor.ensureScheduled`
|
||||||
* (only one task per file will be scheduled at same time)
|
* (only one task per file will be scheduled at same time)
|
||||||
* - `invalid`:
|
* - `invalid, loading`
|
||||||
* Loading should be rescheduled, since the work already started for old input.
|
* Loading should be removed from `backgroundExecutor`, and will be rescheduled on change
|
||||||
* This will work, because file will be removed from backgroundExecutor.
|
* and file will be up-to-date checked again. This will happen after current loading,
|
||||||
* - `loading`: Scheduled loading for unchanged file will be noop thanks to isUpToDate check
|
* because only `backgroundExecutor` execute tasks in one thread.
|
||||||
* - `not applied`: Scheduled loading for unchanged file with loaded but not applied
|
* - `invalid, waiting for apply`:
|
||||||
* configuration will be also noop thanks check in [notApplied] map.
|
* Loading will not be queued, since we are marking file is up to date with
|
||||||
|
* not yet applied configuration.
|
||||||
*
|
*
|
||||||
* Sync:
|
* Sync:
|
||||||
* - up-to-date:
|
* - up-to-date:
|
||||||
@@ -192,23 +178,9 @@ internal class DefaultScriptConfigurationManager(project: Project) :
|
|||||||
// don't start loading if nothing was changed
|
// don't start loading if nothing was changed
|
||||||
// (in case we checking for up-to-date and loading concurrently)
|
// (in case we checking for up-to-date and loading concurrently)
|
||||||
val cached = getCachedConfiguration(virtualFile)
|
val cached = getCachedConfiguration(virtualFile)
|
||||||
if (cached?.inputs?.isUpToDate(project, virtualFile) != true) {
|
if (cached == null || !cached.inputs.isUpToDate(project, virtualFile)) {
|
||||||
val prevNotApplied = synchronized(notApplied) { notApplied[virtualFile] }
|
val actualIsFirstLoad = cached == null
|
||||||
if (prevNotApplied?.inputs?.isUpToDate(project, virtualFile) == true) {
|
async.first { it.loadDependencies(actualIsFirstLoad, virtualFile, scriptDefinition, loadingContext) }
|
||||||
// reuse loaded but not applied result
|
|
||||||
// (in case we checking for up-to-date and waiting notification answer concurrently)
|
|
||||||
loadingContext.suggestNewConfiguration(
|
|
||||||
virtualFile,
|
|
||||||
prevNotApplied
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
synchronized(notApplied) {
|
|
||||||
notApplied.remove(virtualFile)
|
|
||||||
}
|
|
||||||
|
|
||||||
val actualIsFirstLoad = cached == null
|
|
||||||
async.first { it.loadDependencies(actualIsFirstLoad, virtualFile, scriptDefinition, loadingContext) }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -220,7 +192,7 @@ internal class DefaultScriptConfigurationManager(project: Project) :
|
|||||||
* Start indexing for new class/source roots.
|
* Start indexing for new class/source roots.
|
||||||
* Re-highlight opened scripts with changed configuration.
|
* Re-highlight opened scripts with changed configuration.
|
||||||
*/
|
*/
|
||||||
override fun saveCompilationConfigurationAfterImport(files: List<Pair<VirtualFile, LoadedScriptConfiguration>>) {
|
override fun saveCompilationConfigurationAfterImport(files: List<Pair<VirtualFile, ScriptConfigurationSnapshot>>) {
|
||||||
rootsIndexer.transaction {
|
rootsIndexer.transaction {
|
||||||
for ((file, result) in files) {
|
for ((file, result) in files) {
|
||||||
loadingContext.saveNewConfiguration(file, result)
|
loadingContext.saveNewConfiguration(file, result)
|
||||||
@@ -229,33 +201,35 @@ internal class DefaultScriptConfigurationManager(project: Project) :
|
|||||||
}
|
}
|
||||||
|
|
||||||
private val loadingContext = object : ScriptConfigurationLoadingContext {
|
private val loadingContext = object : ScriptConfigurationLoadingContext {
|
||||||
override fun getCachedConfiguration(file: VirtualFile): CachedConfigurationSnapshot? =
|
override fun getCachedConfiguration(file: VirtualFile): ScriptConfigurationSnapshot? =
|
||||||
this@DefaultScriptConfigurationManager.getCachedConfiguration(file)
|
this@DefaultScriptConfigurationManager.getCachedConfiguration(file)
|
||||||
|
|
||||||
override fun suggestNewConfiguration(file: VirtualFile, newResult: LoadedScriptConfiguration) {
|
override fun suggestNewConfiguration(file: VirtualFile, newResult: ScriptConfigurationSnapshot) {
|
||||||
suggestOrSaveConfiguration(file, newResult, false)
|
suggestOrSaveConfiguration(file, newResult, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun saveNewConfiguration(file: VirtualFile, newResult: LoadedScriptConfiguration) {
|
override fun saveNewConfiguration(file: VirtualFile, newResult: ScriptConfigurationSnapshot) {
|
||||||
suggestOrSaveConfiguration(file, newResult, true)
|
suggestOrSaveConfiguration(file, newResult, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun suggestOrSaveConfiguration(
|
private fun suggestOrSaveConfiguration(
|
||||||
file: VirtualFile,
|
file: VirtualFile,
|
||||||
newResult: LoadedScriptConfiguration,
|
newResult: ScriptConfigurationSnapshot,
|
||||||
skipNotification: Boolean
|
skipNotification: Boolean
|
||||||
) {
|
) {
|
||||||
saveLock.withLock {
|
saveLock.withLock {
|
||||||
debug(file) { "configuration received = $newResult" }
|
debug(file) { "configuration received = $newResult" }
|
||||||
|
|
||||||
saveReports(file, newResult.reports)
|
markUpToDate(file, newResult.inputs)
|
||||||
|
|
||||||
val newConfiguration = newResult.configuration
|
val newConfiguration = newResult.configuration
|
||||||
if (newConfiguration != null) {
|
if (newConfiguration == null) {
|
||||||
val newConfigurationSnapshot = CachedConfigurationSnapshot(newResult.inputs, newConfiguration)
|
saveReports(file, newResult.reports)
|
||||||
|
} else {
|
||||||
val oldConfiguration = getCachedConfiguration(file)?.configuration
|
val oldConfiguration = getCachedConfiguration(file)?.configuration
|
||||||
if (oldConfiguration == newConfiguration) {
|
if (oldConfiguration == newConfiguration) {
|
||||||
|
saveReports(file, newResult.reports)
|
||||||
file.removeScriptDependenciesNotificationPanel(project)
|
file.removeScriptDependenciesNotificationPanel(project)
|
||||||
} else {
|
} else {
|
||||||
val autoReload = skipNotification
|
val autoReload = skipNotification
|
||||||
@@ -267,25 +241,19 @@ internal class DefaultScriptConfigurationManager(project: Project) :
|
|||||||
if (oldConfiguration != null) {
|
if (oldConfiguration != null) {
|
||||||
file.removeScriptDependenciesNotificationPanel(project)
|
file.removeScriptDependenciesNotificationPanel(project)
|
||||||
}
|
}
|
||||||
saveChangedConfiguration(file, newConfigurationSnapshot)
|
saveReports(file, newResult.reports)
|
||||||
|
saveChangedConfiguration(file, newResult)
|
||||||
} else {
|
} else {
|
||||||
debug(file) {
|
debug(file) {
|
||||||
"configuration changed, notification is shown: old = $oldConfiguration, new = $newConfiguration"
|
"configuration changed, notification is shown: old = $oldConfiguration, new = $newConfiguration"
|
||||||
}
|
}
|
||||||
synchronized(notApplied) {
|
|
||||||
notApplied[file] = newResult
|
|
||||||
}
|
|
||||||
file.addScriptDependenciesNotificationPanel(
|
file.addScriptDependenciesNotificationPanel(
|
||||||
newConfiguration, project,
|
newConfiguration, project,
|
||||||
onClick = {
|
onClick = {
|
||||||
|
saveReports(file, newResult.reports)
|
||||||
file.removeScriptDependenciesNotificationPanel(project)
|
file.removeScriptDependenciesNotificationPanel(project)
|
||||||
rootsIndexer.transaction {
|
rootsIndexer.transaction {
|
||||||
saveChangedConfiguration(file, newConfigurationSnapshot)
|
saveChangedConfiguration(file, newResult)
|
||||||
}
|
|
||||||
},
|
|
||||||
onHide = {
|
|
||||||
synchronized(notApplied) {
|
|
||||||
notApplied.remove(file)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -295,14 +263,6 @@ internal class DefaultScriptConfigurationManager(project: Project) :
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun saveChangedConfiguration(file: VirtualFile, newConfigurationSnapshot: CachedConfigurationSnapshot?) {
|
|
||||||
super.saveChangedConfiguration(file, newConfigurationSnapshot)
|
|
||||||
|
|
||||||
synchronized(notApplied) {
|
|
||||||
notApplied.remove(file)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun saveReports(
|
private fun saveReports(
|
||||||
file: VirtualFile,
|
file: VirtualFile,
|
||||||
newReports: List<ScriptDiagnostic>
|
newReports: List<ScriptDiagnostic>
|
||||||
|
|||||||
+2
-1
@@ -40,7 +40,8 @@ interface ScriptConfigurationCache {
|
|||||||
operator fun get(file: VirtualFile): ScriptConfigurationSnapshot?
|
operator fun get(file: VirtualFile): ScriptConfigurationSnapshot?
|
||||||
operator fun set(file: VirtualFile, configurationSnapshot: ScriptConfigurationSnapshot)
|
operator fun set(file: VirtualFile, configurationSnapshot: ScriptConfigurationSnapshot)
|
||||||
|
|
||||||
fun markOutOfDate(file: VirtualFile)
|
fun markUpToDate(file: VirtualFile, inputs: CachedConfigurationInputs)
|
||||||
|
fun markOutOfDate(file: VirtualFile) = markUpToDate(file, CachedConfigurationInputs.OutOfDate)
|
||||||
|
|
||||||
fun all(): Collection<Pair<VirtualFile, ScriptCompilationConfigurationWrapper>>
|
fun all(): Collection<Pair<VirtualFile, ScriptCompilationConfigurationWrapper>>
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user