mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-01-05 01:50:56 +07:00
IDEA-318843 storage tracker per project
GitOrigin-RevId: 8d045ad6b7fd94a8dedf3ddd84dca6a0b589bee1
This commit is contained in:
committed by
intellij-monorepo-bot
parent
3e3c90d169
commit
3f3e985ff0
@@ -80,12 +80,12 @@ class ProjectInspectionManagerTest {
|
||||
file.delete()
|
||||
|
||||
refreshProjectConfigDir(project)
|
||||
StoreReloadManager.getInstance().reloadChangedStorageFiles()
|
||||
StoreReloadManager.getInstance(project).reloadChangedStorageFiles()
|
||||
assertThat(projectInspectionProfileManager.state).isEmpty()
|
||||
|
||||
file.write(doNotUseProjectProfileData)
|
||||
refreshProjectConfigDir(project)
|
||||
StoreReloadManager.getInstance().reloadChangedStorageFiles()
|
||||
StoreReloadManager.getInstance(project).reloadChangedStorageFiles()
|
||||
assertThat(projectInspectionProfileManager.state).isEqualTo(doNotUseProjectProfileState)
|
||||
}
|
||||
}
|
||||
@@ -149,7 +149,7 @@ class ProjectInspectionManagerTest {
|
||||
</component>""".trimIndent())
|
||||
|
||||
refreshProjectConfigDir(project)
|
||||
StoreReloadManager.getInstance().reloadChangedStorageFiles()
|
||||
StoreReloadManager.getInstance(project).reloadChangedStorageFiles()
|
||||
assertThat(projectInspectionProfileManager.currentProfile.getToolDefaultState("Convert2Diamond", project).level).isEqualTo(
|
||||
HighlightDisplayLevel.ERROR)
|
||||
}
|
||||
@@ -202,7 +202,7 @@ class ProjectInspectionManagerTest {
|
||||
</component>""")
|
||||
|
||||
refreshProjectConfigDir(project)
|
||||
StoreReloadManager.getInstance().reloadChangedStorageFiles()
|
||||
StoreReloadManager.getInstance(project).reloadChangedStorageFiles()
|
||||
|
||||
assertThat(profileManager.currentProfile.isProjectLevel).isTrue()
|
||||
assertThat(profileManager.currentProfile.name).isEqualTo("Project Default")
|
||||
@@ -212,7 +212,7 @@ class ProjectInspectionManagerTest {
|
||||
writeDefaultProfile(profileDir)
|
||||
|
||||
refreshProjectConfigDir(project)
|
||||
StoreReloadManager.getInstance().reloadChangedStorageFiles()
|
||||
StoreReloadManager.getInstance(project).reloadChangedStorageFiles()
|
||||
|
||||
assertThat(profileManager.currentProfile.name).isEqualTo("Project Default")
|
||||
|
||||
|
||||
@@ -4,17 +4,17 @@
|
||||
package com.intellij.configurationStore
|
||||
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.application.Application
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.components.*
|
||||
import com.intellij.openapi.components.StateStorageChooserEx.Resolution
|
||||
import com.intellij.openapi.roots.ProjectModelElement
|
||||
import com.intellij.openapi.util.Disposer
|
||||
import com.intellij.openapi.util.io.FileUtilRt
|
||||
import com.intellij.openapi.vfs.AsyncFileListener
|
||||
import com.intellij.openapi.vfs.newvfs.events.VFileEvent
|
||||
import com.intellij.util.ReflectionUtil
|
||||
import com.intellij.util.SmartList
|
||||
import com.intellij.util.ThreeState
|
||||
import com.intellij.util.io.systemIndependentPath
|
||||
import org.jdom.Element
|
||||
import org.jetbrains.annotations.ApiStatus.Internal
|
||||
import org.jetbrains.annotations.NonNls
|
||||
@@ -24,6 +24,7 @@ import java.util.*
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock
|
||||
import kotlin.concurrent.read
|
||||
import kotlin.concurrent.write
|
||||
import kotlin.io.path.invariantSeparatorsPathString
|
||||
|
||||
/**
|
||||
* If componentManager not specified, storage will not add file tracker
|
||||
@@ -31,8 +32,9 @@ import kotlin.concurrent.write
|
||||
@Internal
|
||||
open class StateStorageManagerImpl(@NonNls private val rootTagName: String,
|
||||
final override val macroSubstitutor: PathMacroSubstitutor? = null,
|
||||
override val componentManager: ComponentManager?,
|
||||
private val virtualFileTracker: StorageVirtualFileTracker? = createDefaultVirtualTracker(componentManager)) : StateStorageManager {
|
||||
final override val componentManager: ComponentManager?) : StateStorageManager {
|
||||
private val virtualFileTracker = createDefaultVirtualTracker(componentManager)
|
||||
|
||||
@Volatile
|
||||
protected var macros: List<Macro> = Collections.emptyList()
|
||||
|
||||
@@ -55,37 +57,16 @@ open class StateStorageManagerImpl(@NonNls private val rootTagName: String,
|
||||
}
|
||||
|
||||
// access under storageLock
|
||||
@Suppress("LeakingThis")
|
||||
private var isUseVfsListener = when (componentManager) {
|
||||
null, is Application -> ThreeState.NO
|
||||
null -> ThreeState.NO
|
||||
else -> ThreeState.UNSURE // unsure because depends on stream provider state
|
||||
}
|
||||
|
||||
open fun getFileBasedStorageConfiguration(fileSpec: String) = defaultFileBasedStorageConfiguration
|
||||
open fun getFileBasedStorageConfiguration(fileSpec: String): FileBasedStorageConfiguration = defaultFileBasedStorageConfiguration
|
||||
|
||||
protected open val isUseXmlProlog: Boolean
|
||||
get() = true
|
||||
|
||||
companion object {
|
||||
private fun createDefaultVirtualTracker(componentManager: ComponentManager?): StorageVirtualFileTracker? {
|
||||
return when (componentManager) {
|
||||
null -> null
|
||||
is Application -> StorageVirtualFileTracker(componentManager.messageBus)
|
||||
else -> {
|
||||
val tracker = (ApplicationManager.getApplication().stateStore.storageManager as? StateStorageManagerImpl)?.virtualFileTracker
|
||||
?: return null
|
||||
Disposer.register(componentManager, Disposable {
|
||||
tracker.remove { it.storageManager.componentManager === componentManager }
|
||||
})
|
||||
tracker
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
fun getVirtualFileTracker() = virtualFileTracker
|
||||
|
||||
/**
|
||||
* Returns an old map.
|
||||
*/
|
||||
@@ -171,8 +152,8 @@ open class StateStorageManagerImpl(@NonNls private val rootTagName: String,
|
||||
val storage = getCachedFileStorages(listOf(spec)).firstOrNull() ?: return
|
||||
if (storage is StorageVirtualFileTracker.TrackedStorage) {
|
||||
virtualFileTracker?.let { tracker ->
|
||||
tracker.remove(storage.file.systemIndependentPath)
|
||||
tracker.put(newPath.systemIndependentPath, storage)
|
||||
tracker.remove(storage.file.invariantSeparatorsPathString)
|
||||
tracker.put(newPath.invariantSeparatorsPathString, storage)
|
||||
}
|
||||
}
|
||||
storage.setFile(null, newPath)
|
||||
@@ -199,13 +180,12 @@ open class StateStorageManagerImpl(@NonNls private val rootTagName: String,
|
||||
}
|
||||
}
|
||||
|
||||
// overridden in upsource
|
||||
protected open fun createStateStorage(storageClass: Class<out StateStorage>,
|
||||
collapsedPath: String,
|
||||
roamingType: RoamingType,
|
||||
@Suppress("DEPRECATION", "removal") stateSplitter: Class<out StateSplitter>,
|
||||
usePathMacroManager: Boolean,
|
||||
exclusive: Boolean = false): StateStorage {
|
||||
private fun createStateStorage(storageClass: Class<out StateStorage>,
|
||||
collapsedPath: String,
|
||||
roamingType: RoamingType,
|
||||
@Suppress("DEPRECATION", "removal") stateSplitter: Class<out StateSplitter>,
|
||||
usePathMacroManager: Boolean,
|
||||
exclusive: Boolean = false): StateStorage {
|
||||
if (storageClass != StateStorage::class.java) {
|
||||
val constructor = storageClass.constructors.first { it.parameterCount <= 3 }
|
||||
constructor.isAccessible = true
|
||||
@@ -227,7 +207,7 @@ open class StateStorageManagerImpl(@NonNls private val rootTagName: String,
|
||||
if (stateSplitter != StateSplitter::class.java && stateSplitter != StateSplitterEx::class.java) {
|
||||
val storage = createDirectoryBasedStorage(filePath, collapsedPath, ReflectionUtil.newInstance(stateSplitter))
|
||||
if (storage is StorageVirtualFileTracker.TrackedStorage) {
|
||||
virtualFileTracker?.put(filePath.systemIndependentPath, storage)
|
||||
virtualFileTracker?.put(filePath.invariantSeparatorsPathString, storage)
|
||||
}
|
||||
return storage
|
||||
}
|
||||
@@ -243,12 +223,11 @@ open class StateStorageManagerImpl(@NonNls private val rootTagName: String,
|
||||
usePathMacroManager = usePathMacroManager,
|
||||
rootTagName = if (exclusive) null else rootTagName)
|
||||
if (isUseVfsListener == ThreeState.YES && storage is StorageVirtualFileTracker.TrackedStorage) {
|
||||
virtualFileTracker?.put(filePath.systemIndependentPath, storage)
|
||||
virtualFileTracker?.put(filePath.invariantSeparatorsPathString, storage)
|
||||
}
|
||||
return storage
|
||||
}
|
||||
|
||||
// open for upsource
|
||||
protected open fun createFileBasedStorage(path: Path,
|
||||
collapsedPath: String,
|
||||
roamingType: RoamingType,
|
||||
@@ -353,9 +332,9 @@ open class StateStorageManagerImpl(@NonNls private val rootTagName: String,
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun clearVirtualFileTracker(virtualFileTracker: StorageVirtualFileTracker) {
|
||||
internal open fun clearVirtualFileTracker(virtualFileTracker: StorageVirtualFileTracker) {
|
||||
for (collapsedPath in storages.keys) {
|
||||
virtualFileTracker.remove(expandMacro(collapsedPath).systemIndependentPath)
|
||||
virtualFileTracker.remove(expandMacro(collapsedPath).invariantSeparatorsPathString)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -365,7 +344,7 @@ open class StateStorageManagerImpl(@NonNls private val rootTagName: String,
|
||||
return value
|
||||
}
|
||||
|
||||
if (collapsedPath.length > (key.length + 2) && collapsedPath[key.length] == '/' && collapsedPath.startsWith(key)) {
|
||||
if (collapsedPath.length > (key.length + 2) && collapsedPath.get(key.length) == '/' && collapsedPath.startsWith(key)) {
|
||||
return value.resolve(collapsedPath.substring(key.length + 1))
|
||||
}
|
||||
}
|
||||
@@ -376,7 +355,7 @@ open class StateStorageManagerImpl(@NonNls private val rootTagName: String,
|
||||
fun collapseMacro(path: String): String {
|
||||
for ((key, value) in macros) {
|
||||
@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
|
||||
val result = (path as java.lang.String).replace(value.systemIndependentPath, key)
|
||||
val result = (path as java.lang.String).replace(value.invariantSeparatorsPathString, key)
|
||||
if (result !== path) {
|
||||
return result
|
||||
}
|
||||
@@ -399,7 +378,10 @@ internal val Storage.path: String
|
||||
get() = value.ifEmpty { file }
|
||||
|
||||
internal fun getEffectiveRoamingType(roamingType: RoamingType, collapsedPath: String): RoamingType {
|
||||
if (roamingType != RoamingType.DISABLED && (collapsedPath == StoragePathMacros.WORKSPACE_FILE || collapsedPath == StoragePathMacros.NON_ROAMABLE_FILE || isSpecialStorage(collapsedPath))) {
|
||||
if (roamingType != RoamingType.DISABLED &&
|
||||
(collapsedPath == StoragePathMacros.WORKSPACE_FILE ||
|
||||
collapsedPath == StoragePathMacros.NON_ROAMABLE_FILE ||
|
||||
isSpecialStorage(collapsedPath))) {
|
||||
return RoamingType.DISABLED
|
||||
}
|
||||
else {
|
||||
@@ -408,4 +390,38 @@ internal fun getEffectiveRoamingType(roamingType: RoamingType, collapsedPath: St
|
||||
}
|
||||
|
||||
@Internal
|
||||
data class Macro(@JvmField val key: String, @JvmField var value: Path)
|
||||
data class Macro(@JvmField val key: String, @JvmField var value: Path)
|
||||
|
||||
@TestOnly
|
||||
fun checkStorageIsNotTracked(module: ComponentManager) {
|
||||
service<StorageVirtualFileTrackerHolder>().tracker.remove {
|
||||
if (it.storageManager.componentManager === module) {
|
||||
throw AssertionError("Storage manager is not disposed, module $module, storage $it")
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@Service(Service.Level.APP)
|
||||
private class StorageVirtualFileTrackerHolder {
|
||||
@JvmField
|
||||
val tracker = StorageVirtualFileTracker()
|
||||
}
|
||||
|
||||
private class MyAsyncVfsListener : AsyncFileListener {
|
||||
override fun prepareChange(events: List<VFileEvent>): AsyncFileListener.ChangeApplier? {
|
||||
return service<StorageVirtualFileTrackerHolder>().tracker.prepare(events)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createDefaultVirtualTracker(componentManager: ComponentManager?): StorageVirtualFileTracker? {
|
||||
if (componentManager == null) {
|
||||
return null
|
||||
}
|
||||
|
||||
val tracker = service<StorageVirtualFileTrackerHolder>().tracker
|
||||
Disposer.register(componentManager, Disposable {
|
||||
tracker.remove { it.storageManager.componentManager === componentManager }
|
||||
})
|
||||
return tracker
|
||||
}
|
||||
|
||||
@@ -3,28 +3,26 @@
|
||||
|
||||
package com.intellij.configurationStore
|
||||
|
||||
import com.intellij.openapi.components.ComponentManager
|
||||
import com.intellij.openapi.components.StateStorage
|
||||
import com.intellij.openapi.components.impl.stores.FileStorageCoreUtil
|
||||
import com.intellij.openapi.components.impl.stores.IComponentStore
|
||||
import com.intellij.openapi.components.stateStore
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.vfs.AsyncFileListener
|
||||
import com.intellij.openapi.vfs.VfsUtil
|
||||
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.*
|
||||
import com.intellij.util.messages.MessageBus
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import java.nio.file.Paths
|
||||
import java.nio.file.Path
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import java.util.concurrent.ConcurrentMap
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
|
||||
@ApiStatus.Internal
|
||||
class StorageVirtualFileTracker(private val messageBus: MessageBus) {
|
||||
private val filePathToStorage: ConcurrentMap<String, TrackedStorage> = ConcurrentHashMap()
|
||||
internal class StorageVirtualFileTracker {
|
||||
private val filePathToStorage = ConcurrentHashMap<String, TrackedStorage>()
|
||||
@Volatile
|
||||
private var hasDirectoryBasedStorages = false
|
||||
|
||||
private val vfsListenerAdded = AtomicBoolean()
|
||||
@Volatile
|
||||
private var vfsListenerIsActive = false
|
||||
|
||||
interface TrackedStorage : StateStorage {
|
||||
val storageManager: StateStorageManagerImpl
|
||||
@@ -36,9 +34,7 @@ class StorageVirtualFileTracker(private val messageBus: MessageBus) {
|
||||
hasDirectoryBasedStorages = true
|
||||
}
|
||||
|
||||
if (vfsListenerAdded.compareAndSet(false, true)) {
|
||||
addVfsChangesListener()
|
||||
}
|
||||
vfsListenerIsActive = true
|
||||
}
|
||||
|
||||
fun remove(path: String) {
|
||||
@@ -49,79 +45,92 @@ class StorageVirtualFileTracker(private val messageBus: MessageBus) {
|
||||
filePathToStorage.values.removeIf(filter)
|
||||
}
|
||||
|
||||
private fun addVfsChangesListener() {
|
||||
messageBus.connect().subscribe(VirtualFileManager.VFS_CHANGES, object : BulkFileListener {
|
||||
override fun after(events: List<VFileEvent>) {
|
||||
var storageEvents: LinkedHashMap<ComponentManager, LinkedHashSet<StateStorage>>? = null
|
||||
eventLoop@ for (event in events) {
|
||||
var storage: StateStorage?
|
||||
if (event is VFilePropertyChangeEvent && VirtualFile.PROP_NAME == event.propertyName) {
|
||||
val oldPath = event.oldPath
|
||||
storage = filePathToStorage.remove(oldPath)
|
||||
if (storage != null) {
|
||||
filePathToStorage.put(event.path, storage)
|
||||
if (storage is FileBasedStorage) {
|
||||
storage.setFile(null, Paths.get(event.path))
|
||||
}
|
||||
// we don't support DirectoryBasedStorage renaming
|
||||
fun prepare(events: List<VFileEvent>): AsyncFileListener.ChangeApplier? {
|
||||
if (!vfsListenerIsActive) {
|
||||
return null
|
||||
}
|
||||
|
||||
// StoragePathMacros.MODULE_FILE -> old path, we must update value
|
||||
(storage.storageManager as? RenameableStateStorageManager)?.pathRenamed(Paths.get(event.path), event)
|
||||
}
|
||||
var storageEvents: LinkedHashMap<IComponentStore, LinkedHashSet<StateStorage>>? = null
|
||||
eventLoop@ for (event in events) {
|
||||
var storage: StateStorage?
|
||||
if (event is VFilePropertyChangeEvent && VirtualFile.PROP_NAME == event.propertyName) {
|
||||
val oldPath = event.oldPath
|
||||
storage = filePathToStorage.remove(oldPath)
|
||||
if (storage != null) {
|
||||
filePathToStorage.put(event.path, storage)
|
||||
if (storage is FileBasedStorage) {
|
||||
storage.setFile(null, Path.of(event.path))
|
||||
}
|
||||
else {
|
||||
val path = event.path
|
||||
storage = filePathToStorage.get(path)
|
||||
// We don't care about parent directory create (because it doesn't affect anything)
|
||||
// and move (because it is not a supported case),
|
||||
// but we should detect deletion - but again, it is not a supported case.
|
||||
// So, we don't check if some of the registered storages located inside changed directory.
|
||||
// we don't support DirectoryBasedStorage renaming
|
||||
|
||||
// but if we have DirectoryBasedStorage, we check - if file located inside it
|
||||
if (storage == null && hasDirectoryBasedStorages && path.endsWith(FileStorageCoreUtil.DEFAULT_EXT, ignoreCase = true)) {
|
||||
storage = filePathToStorage.get(VfsUtil.getParentDir(path))
|
||||
}
|
||||
}
|
||||
|
||||
if (storage == null) {
|
||||
continue
|
||||
}
|
||||
|
||||
when (event) {
|
||||
is VFileMoveEvent -> {
|
||||
if (storage is FileBasedStorage) {
|
||||
storage.setFile(null, Paths.get(event.path))
|
||||
}
|
||||
}
|
||||
is VFileCreateEvent -> {
|
||||
if (storage is FileBasedStorage && event.requestor !is SaveSession) {
|
||||
storage.setFile(event.file, null)
|
||||
}
|
||||
}
|
||||
is VFileDeleteEvent -> {
|
||||
if (storage is FileBasedStorage) {
|
||||
storage.setFile(null, null)
|
||||
}
|
||||
else {
|
||||
(storage as DirectoryBasedStorage).setVirtualDir(null)
|
||||
}
|
||||
}
|
||||
is VFileCopyEvent -> continue@eventLoop
|
||||
}
|
||||
|
||||
if (isFireStorageFileChangedEvent(event)) {
|
||||
val componentManager = storage.storageManager.componentManager!!
|
||||
if (storageEvents == null) {
|
||||
storageEvents = LinkedHashMap()
|
||||
}
|
||||
storageEvents.computeIfAbsent(componentManager) { LinkedHashSet() }.add(storage)
|
||||
}
|
||||
}
|
||||
|
||||
if (storageEvents != null) {
|
||||
StoreReloadManager.getInstance().storageFilesChanged(storageEvents)
|
||||
// StoragePathMacros.MODULE_FILE -> old path, we must update value
|
||||
(storage.storageManager as? RenameableStateStorageManager)?.pathRenamed(Path.of(event.path), event)
|
||||
}
|
||||
}
|
||||
})
|
||||
else {
|
||||
val path = event.path
|
||||
storage = filePathToStorage.get(path)
|
||||
// We don't care about parent directory create (because it doesn't affect anything)
|
||||
// and move (because it is not a supported case),
|
||||
// but we should detect deletion - but again, it is not a supported case.
|
||||
// So, we don't check if some of the registered storages located inside changed directory.
|
||||
|
||||
// but if we have DirectoryBasedStorage, we check - if file located inside it
|
||||
if (storage == null && hasDirectoryBasedStorages && path.endsWith(FileStorageCoreUtil.DEFAULT_EXT, ignoreCase = true)) {
|
||||
storage = filePathToStorage.get(VfsUtil.getParentDir(path))
|
||||
}
|
||||
}
|
||||
|
||||
if (storage == null) {
|
||||
continue
|
||||
}
|
||||
|
||||
when (event) {
|
||||
is VFileMoveEvent -> {
|
||||
if (storage is FileBasedStorage) {
|
||||
storage.setFile(null, Path.of(event.path))
|
||||
}
|
||||
}
|
||||
is VFileCreateEvent -> {
|
||||
if (storage is FileBasedStorage && event.requestor !is SaveSession) {
|
||||
storage.setFile(event.file, null)
|
||||
}
|
||||
}
|
||||
is VFileDeleteEvent -> {
|
||||
if (storage is FileBasedStorage) {
|
||||
storage.setFile(null, null)
|
||||
}
|
||||
else {
|
||||
(storage as DirectoryBasedStorage).setVirtualDir(null)
|
||||
}
|
||||
}
|
||||
is VFileCopyEvent -> continue@eventLoop
|
||||
}
|
||||
|
||||
if (isFireStorageFileChangedEvent(event)) {
|
||||
val componentManager = storage.storageManager.componentManager!!
|
||||
if (storageEvents == null) {
|
||||
storageEvents = LinkedHashMap()
|
||||
}
|
||||
storageEvents.computeIfAbsent(componentManager.stateStore) { LinkedHashSet() }.add(storage)
|
||||
}
|
||||
}
|
||||
|
||||
if (storageEvents == null) {
|
||||
return null
|
||||
}
|
||||
|
||||
return object : AsyncFileListener.ChangeApplier {
|
||||
override fun afterVfsChange() {
|
||||
for ((store, storages) in storageEvents) {
|
||||
val project: Project = when (val componentManager = store.storageManager.componentManager) {
|
||||
is Project -> componentManager
|
||||
is Module -> componentManager.project
|
||||
else -> continue
|
||||
}
|
||||
StoreReloadManager.getInstance(project).storageFilesChanged(store, storages)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,25 +4,23 @@ package com.intellij.configurationStore
|
||||
import com.intellij.configurationStore.schemeManager.SchemeChangeApplicator
|
||||
import com.intellij.configurationStore.schemeManager.SchemeChangeEvent
|
||||
import com.intellij.ide.impl.OpenProjectTask
|
||||
import com.intellij.openapi.application.*
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.application.ApplicationNamesInfo
|
||||
import com.intellij.openapi.application.EDT
|
||||
import com.intellij.openapi.application.ModalityState
|
||||
import com.intellij.openapi.application.ex.ApplicationManagerEx
|
||||
import com.intellij.openapi.components.ComponentManager
|
||||
import com.intellij.openapi.components.StateStorage
|
||||
import com.intellij.openapi.components.impl.stores.IComponentStore
|
||||
import com.intellij.openapi.components.impl.stores.IProjectStore
|
||||
import com.intellij.openapi.components.stateStore
|
||||
import com.intellij.openapi.diagnostic.debug
|
||||
import com.intellij.openapi.diagnostic.getOrLogException
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.options.Scheme
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.project.ProjectManager
|
||||
import com.intellij.openapi.project.ProjectReloadState
|
||||
import com.intellij.openapi.project.ex.ProjectManagerEx
|
||||
import com.intellij.openapi.ui.Messages
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.openapi.util.Ref
|
||||
import com.intellij.openapi.util.UserDataHolderEx
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.openapi.vfs.VirtualFileManagerListener
|
||||
import com.intellij.ui.AppUIUtil
|
||||
@@ -35,35 +33,43 @@ import kotlinx.coroutines.channels.BufferOverflow
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.debounce
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import java.nio.file.Paths
|
||||
import java.nio.file.Path
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import java.util.concurrent.atomic.AtomicReference
|
||||
import kotlin.time.Duration.Companion.milliseconds
|
||||
|
||||
private val CHANGED_FILES_KEY = Key<MutableMap<ComponentStoreImpl, MutableSet<StateStorage>>>("CHANGED_FILES_KEY")
|
||||
private val CHANGED_SCHEMES_KEY = Key<MutableMap<SchemeChangeApplicator<*,*>, MutableSet<SchemeChangeEvent<*,*>>>>("CHANGED_SCHEMES_KEY")
|
||||
|
||||
@ApiStatus.Internal
|
||||
internal class StoreReloadManagerImpl(cs: CoroutineScope) : StoreReloadManager {
|
||||
internal class StoreReloadManagerImpl(coroutineScope: CoroutineScope, private val project: Project) : StoreReloadManager {
|
||||
private val reloadBlockCount = AtomicInteger()
|
||||
private val blockStackTrace = AtomicReference<Throwable?>()
|
||||
private val changedApplicationFiles = LinkedHashSet<StateStorage>()
|
||||
private val changedStorages = LinkedHashMap<ComponentStoreImpl, MutableSet<StateStorage>>()
|
||||
private val changedSchemes = LinkedHashMap<SchemeChangeApplicator<*,*>, MutableSet<SchemeChangeEvent<*,*>>>()
|
||||
|
||||
private val changedFilesRequests = MutableSharedFlow<Unit>(replay=1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
|
||||
|
||||
init {
|
||||
@OptIn(FlowPreview::class)
|
||||
cs.launch(CoroutineName("configuration store reload request flow processing")) {
|
||||
coroutineScope.launch(CoroutineName("configuration store reload request flow processing")) {
|
||||
changedFilesRequests
|
||||
.debounce(300.milliseconds)
|
||||
.collect {
|
||||
doReload()
|
||||
}
|
||||
}
|
||||
|
||||
project.messageBus.simpleConnect().subscribe(VirtualFileManagerListener.TOPIC, object : VirtualFileManagerListener {
|
||||
override fun beforeRefreshStart(asynchronous: Boolean) {
|
||||
blockReloadingProjectOnExternalChanges()
|
||||
}
|
||||
|
||||
override fun afterRefreshFinish(asynchronous: Boolean) {
|
||||
unblockReloadingProjectOnExternalChanges()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private suspend fun doReload() {
|
||||
if (isReloadBlocked() || !tryToReloadApplication()) {
|
||||
if (isReloadBlocked()) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -87,10 +93,8 @@ internal class StoreReloadManagerImpl(cs: CoroutineScope) : StoreReloadManager {
|
||||
|
||||
@RequiresEdt
|
||||
private suspend fun applyProjectChanges(project: Project, projectsToReload: LinkedHashSet<Project>) {
|
||||
val changedSchemes = CHANGED_SCHEMES_KEY.getAndClear(project as UserDataHolderEx)
|
||||
val changedStorages = CHANGED_FILES_KEY.getAndClear(project as UserDataHolderEx)
|
||||
if ((changedSchemes.isNullOrEmpty()) && (changedStorages.isNullOrEmpty())
|
||||
&& !mayHaveAdditionalConfigurations(project)) {
|
||||
if (changedSchemes.isEmpty() && changedStorages.isEmpty()
|
||||
&& !JpsProjectModelSynchronizer.getInstance(project).needToReloadProjectEntities()) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -98,26 +102,22 @@ internal class StoreReloadManagerImpl(cs: CoroutineScope) : StoreReloadManager {
|
||||
publisher.onBatchUpdateStarted()
|
||||
try {
|
||||
// reload schemes first because project file can refer to scheme (e.g. inspection profile)
|
||||
if (changedSchemes != null) {
|
||||
for ((tracker, files) in changedSchemes) {
|
||||
runCatching {
|
||||
SlowOperations.knownIssue("IDEA-307617, EA-680581").use {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
(tracker as SchemeChangeApplicator<Scheme, Scheme>).reload(files as Set<SchemeChangeEvent<Scheme, Scheme>>)
|
||||
}
|
||||
}.getOrLogException(LOG)
|
||||
}
|
||||
for ((tracker, files) in changedSchemes) {
|
||||
runCatching {
|
||||
SlowOperations.knownIssue("IDEA-307617, EA-680581").use {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
(tracker as SchemeChangeApplicator<Scheme, Scheme>).reload(files as Set<SchemeChangeEvent<Scheme, Scheme>>)
|
||||
}
|
||||
}.getOrLogException(LOG)
|
||||
}
|
||||
|
||||
if (changedStorages != null) {
|
||||
for ((store, storages) in changedStorages) {
|
||||
if ((store.storageManager as? StateStorageManagerImpl)?.componentManager?.isDisposed == true) {
|
||||
continue
|
||||
}
|
||||
for ((store, storages) in changedStorages) {
|
||||
if ((store.storageManager as? StateStorageManagerImpl)?.componentManager?.isDisposed == true) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (reloadStore(storages, store) == ReloadComponentStoreStatus.RESTART_AGREED) {
|
||||
projectsToReload.add(project)
|
||||
}
|
||||
if (reloadStore(storages, store) == ReloadComponentStoreStatus.RESTART_AGREED) {
|
||||
projectsToReload.add(project)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,33 +128,18 @@ internal class StoreReloadManagerImpl(cs: CoroutineScope) : StoreReloadManager {
|
||||
}
|
||||
}
|
||||
|
||||
private fun mayHaveAdditionalConfigurations(project: Project): Boolean {
|
||||
return JpsProjectModelSynchronizer.getInstance(project).needToReloadProjectEntities()
|
||||
}
|
||||
|
||||
internal class MyVirtualFileManagerListener : VirtualFileManagerListener {
|
||||
private val manager = StoreReloadManager.getInstance()
|
||||
|
||||
override fun beforeRefreshStart(asynchronous: Boolean) {
|
||||
manager.blockReloadingProjectOnExternalChanges()
|
||||
}
|
||||
|
||||
override fun afterRefreshFinish(asynchronous: Boolean) {
|
||||
manager.unblockReloadingProjectOnExternalChanges()
|
||||
}
|
||||
}
|
||||
|
||||
override fun isReloadBlocked(): Boolean {
|
||||
val count = reloadBlockCount.get()
|
||||
LOG.debug { "[RELOAD] myReloadBlockCount = $count" }
|
||||
return count > 0
|
||||
}
|
||||
|
||||
override fun saveChangedProjectFile(file: VirtualFile, project: Project) {
|
||||
val storageManager = (project.stateStore as ComponentStoreImpl).storageManager as? StateStorageManagerImpl ?: return
|
||||
override fun saveChangedProjectFile(file: VirtualFile) {
|
||||
val store = project.stateStore as ComponentStoreImpl
|
||||
val storageManager = store.storageManager as? StateStorageManagerImpl ?: return
|
||||
storageManager.getCachedFileStorages(listOf(storageManager.collapseMacro(file.path))).firstOrNull()?.let {
|
||||
// if empty, so, storage is not yet loaded, so, we don't have to reload
|
||||
storageFilesChanged(mapOf(project to listOf(it)))
|
||||
storageFilesChanged(store, listOf(it))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,58 +172,37 @@ internal class StoreReloadManagerImpl(cs: CoroutineScope) : StoreReloadManager {
|
||||
doReload()
|
||||
}
|
||||
|
||||
override fun reloadProject(project: Project) {
|
||||
CHANGED_FILES_KEY.set(project, null)
|
||||
override fun reloadProject() {
|
||||
changedStorages.clear()
|
||||
doReloadProject(project)
|
||||
}
|
||||
|
||||
override fun storageFilesChanged(componentManagerToStorages: Map<ComponentManager, Collection<StateStorage>>) {
|
||||
if (componentManagerToStorages.isEmpty()) {
|
||||
return
|
||||
}
|
||||
|
||||
override fun storageFilesChanged(store: IComponentStore, storages: Collection<StateStorage>) {
|
||||
if (LOG.isDebugEnabled) {
|
||||
LOG.debug("[RELOAD] registering to reload: ${componentManagerToStorages.map { "${it.key}: ${it.value.joinToString()}" }.joinToString("\n")}", Exception())
|
||||
LOG.debug("[RELOAD] registering to reload: ${storages.joinToString("\n")}", Exception())
|
||||
}
|
||||
|
||||
for ((componentManager, storages) in componentManagerToStorages) {
|
||||
val project: Project? = when (componentManager) {
|
||||
is Project -> componentManager
|
||||
is Module -> componentManager.project
|
||||
else -> null
|
||||
}
|
||||
synchronized(changedStorages) {
|
||||
changedStorages.computeIfAbsent(store as ComponentStoreImpl) { LinkedHashSet() }.addAll(storages)
|
||||
}
|
||||
|
||||
if (project == null) {
|
||||
val changes = changedApplicationFiles
|
||||
synchronized(changes) {
|
||||
changes.addAll(storages)
|
||||
}
|
||||
}
|
||||
else {
|
||||
val changes = CHANGED_FILES_KEY.get(project) ?: (project as UserDataHolderEx).putUserDataIfAbsent(CHANGED_FILES_KEY, linkedMapOf())
|
||||
synchronized(changes) {
|
||||
changes.computeIfAbsent(componentManager.stateStore as ComponentStoreImpl) { LinkedHashSet() }.addAll(storages)
|
||||
}
|
||||
}
|
||||
|
||||
for (storage in storages) {
|
||||
if (storage is StateStorageBase<*>) {
|
||||
storage.disableSaving()
|
||||
}
|
||||
for (storage in storages) {
|
||||
if (storage is StateStorageBase<*>) {
|
||||
storage.disableSaving()
|
||||
}
|
||||
}
|
||||
|
||||
scheduleProcessingChangedFiles()
|
||||
}
|
||||
|
||||
internal fun <T : Scheme, M:T>registerChangedSchemes(events: List<SchemeChangeEvent<T,M>>, schemeFileTracker: SchemeChangeApplicator<T,M>, project: Project) {
|
||||
internal fun <T : Scheme, M : T> registerChangedSchemes(events: List<SchemeChangeEvent<T, M>>,
|
||||
schemeFileTracker: SchemeChangeApplicator<T, M>) {
|
||||
if (LOG.isDebugEnabled) {
|
||||
LOG.debug("[RELOAD] Registering schemes to reload: $events", Exception())
|
||||
}
|
||||
|
||||
val changes = CHANGED_SCHEMES_KEY.get(project) ?: (project as UserDataHolderEx).putUserDataIfAbsent(CHANGED_SCHEMES_KEY, linkedMapOf())
|
||||
synchronized(changes) {
|
||||
changes.computeIfAbsent(schemeFileTracker) { LinkedHashSet() }.addAll(events)
|
||||
synchronized(changedSchemes) {
|
||||
changedSchemes.computeIfAbsent(schemeFileTracker) { LinkedHashSet() }.addAll(events)
|
||||
}
|
||||
|
||||
scheduleProcessingChangedFiles()
|
||||
@@ -249,21 +213,6 @@ internal class StoreReloadManagerImpl(cs: CoroutineScope) : StoreReloadManager {
|
||||
check(changedFilesRequests.tryEmit(Unit))
|
||||
}
|
||||
}
|
||||
|
||||
private fun tryToReloadApplication(): Boolean {
|
||||
if (ApplicationManager.getApplication().isDisposed) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (changedApplicationFiles.isEmpty()) {
|
||||
return true
|
||||
}
|
||||
|
||||
val changes = LinkedHashSet(changedApplicationFiles)
|
||||
changedApplicationFiles.clear()
|
||||
|
||||
return reloadAppStore(changes)
|
||||
}
|
||||
}
|
||||
|
||||
fun reloadAppStore(changes: Set<StateStorage>): Boolean {
|
||||
@@ -374,31 +323,17 @@ internal enum class ReloadComponentStoreStatus {
|
||||
SUCCESS
|
||||
}
|
||||
|
||||
private fun <T : Any> Key<T>.getAndClear(holder: UserDataHolderEx): T? {
|
||||
val value = holder.getUserData(this) ?: return null
|
||||
holder.replace(this, value, null)
|
||||
return value
|
||||
}
|
||||
|
||||
private fun doReloadProject(project: Project) {
|
||||
val projectRef = Ref.create(project)
|
||||
ProjectReloadState.getInstance(project).onBeforeAutomaticProjectReload()
|
||||
AppUIExecutor.onWriteThread(ModalityState.NON_MODAL).later().submit {
|
||||
LOG.debug("Reloading project.")
|
||||
val project1 = projectRef.get()
|
||||
// Let it go
|
||||
projectRef.set(null)
|
||||
ApplicationManager.getApplication().invokeLater({
|
||||
LOG.debug("Reloading project")
|
||||
|
||||
if (project1.isDisposed) {
|
||||
return@submit
|
||||
// must compute here, before dispose of the project
|
||||
val presentableUrl = project.presentableUrl!!
|
||||
if (!ProjectManager.getInstance().closeAndDispose(project)) {
|
||||
return@invokeLater
|
||||
}
|
||||
|
||||
// must compute here, before project dispose
|
||||
val presentableUrl = project1.presentableUrl!!
|
||||
if (!ProjectManager.getInstance().closeAndDispose(project1)) {
|
||||
return@submit
|
||||
}
|
||||
|
||||
ProjectManagerEx.getInstanceEx().openProject(Paths.get(presentableUrl), OpenProjectTask())
|
||||
}
|
||||
ProjectManagerEx.getInstanceEx().openProject(Path.of(presentableUrl), OpenProjectTask())
|
||||
}, ModalityState.NON_MODAL, project.disposed)
|
||||
}
|
||||
@@ -64,7 +64,7 @@ internal class SchemeFileTracker<T: Scheme, M:T>(private val schemeManager: Sche
|
||||
}
|
||||
|
||||
if (list.isNotEmpty()) {
|
||||
(StoreReloadManager.getInstance() as StoreReloadManagerImpl).registerChangedSchemes(list, applicator, project)
|
||||
(StoreReloadManager.getInstance(project) as StoreReloadManagerImpl).registerChangedSchemes(list, applicator)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.intellij.ProjectTopics
|
||||
import com.intellij.ide.highlighter.ModuleFileType
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.application.EDT
|
||||
import com.intellij.openapi.application.writeAction
|
||||
import com.intellij.openapi.components.StateStorageOperation
|
||||
import com.intellij.openapi.components.StoragePathMacros
|
||||
import com.intellij.openapi.components.stateStore
|
||||
@@ -76,12 +77,7 @@ internal class ModuleStoreRenameTest {
|
||||
|
||||
// should be invoked after project tearDown
|
||||
override fun after() {
|
||||
(ApplicationManager.getApplication().stateStore.storageManager as StateStorageManagerImpl).getVirtualFileTracker()!!.remove {
|
||||
if (it.storageManager.componentManager == module) {
|
||||
throw AssertionError("Storage manager is not disposed, module $module, storage $it")
|
||||
}
|
||||
false
|
||||
}
|
||||
checkStorageIsNotTracked(module)
|
||||
}
|
||||
},
|
||||
DisposeModulesRule(projectRule)
|
||||
@@ -99,10 +95,8 @@ internal class ModuleStoreRenameTest {
|
||||
val oldName = module.name
|
||||
val newName = "foo"
|
||||
|
||||
withContext(Dispatchers.EDT) {
|
||||
ApplicationManager.getApplication().runWriteAction {
|
||||
projectRule.project.modifyModules { renameModule(module, newName) }
|
||||
}
|
||||
writeAction {
|
||||
projectRule.project.modifyModules { renameModule(module, newName) }
|
||||
}
|
||||
assertModuleFileRenamed(newName, oldFile)
|
||||
assertThat(oldModuleNames).containsOnly(oldName)
|
||||
@@ -178,20 +172,17 @@ internal class ModuleStoreRenameTest {
|
||||
val storage = module.storage
|
||||
val parentVirtualDir = storage.getVirtualFile(StateStorageOperation.WRITE)!!.parent
|
||||
val src = VfsTestUtil.createDir(parentVirtualDir, "foo")
|
||||
withContext(Dispatchers.EDT) {
|
||||
ApplicationManager.getApplication().runWriteAction {
|
||||
PsiTestUtil.addSourceContentToRoots(module, src, false)
|
||||
}
|
||||
writeAction {
|
||||
PsiTestUtil.addSourceContentToRoots(module, src, false)
|
||||
}
|
||||
|
||||
saveProjectState()
|
||||
|
||||
val rootManager = module.rootManager as ModuleRootManagerEx
|
||||
val stateModificationCount = rootManager.modificationCountForTests
|
||||
|
||||
withContext(Dispatchers.EDT) {
|
||||
ApplicationManager.getApplication().runWriteAction {
|
||||
src.rename(null, "bar.dot")
|
||||
}
|
||||
writeAction {
|
||||
src.rename(null, "bar.dot")
|
||||
}
|
||||
|
||||
assertThat(stateModificationCount).isLessThan(rootManager.modificationCountForTests)
|
||||
|
||||
@@ -4,7 +4,7 @@ package com.intellij.configurationStore
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.application.EDT
|
||||
import com.intellij.openapi.application.ex.PathManagerEx
|
||||
import com.intellij.openapi.application.runWriteAction
|
||||
import com.intellij.openapi.application.writeAction
|
||||
import com.intellij.openapi.components.StoragePathMacros
|
||||
import com.intellij.openapi.components.stateStore
|
||||
import com.intellij.openapi.module.Module
|
||||
@@ -157,7 +157,7 @@ class ModuleStoreTest {
|
||||
removeContentRoot(m2)
|
||||
}
|
||||
|
||||
StoreReloadManager.getInstance().reloadChangedStorageFiles()
|
||||
StoreReloadManager.getInstance(projectRule.project).reloadChangedStorageFiles()
|
||||
|
||||
assertChangesApplied(m1)
|
||||
assertChangesApplied(m2)
|
||||
@@ -196,11 +196,11 @@ class ModuleStoreTest {
|
||||
val moduleName = "tmp-module-${Ksuid.generate()}"
|
||||
val contentRoot = tempDirManager.createVirtualDir()
|
||||
|
||||
withContext(Dispatchers.EDT) {
|
||||
val module = runWriteAction {
|
||||
ModuleManager.getInstance(project).newNonPersistentModule(moduleName, ModuleTypeId.JAVA_MODULE)
|
||||
}
|
||||
|
||||
val module = writeAction {
|
||||
ModuleManager.getInstance(project).newNonPersistentModule(moduleName, ModuleTypeId.JAVA_MODULE)
|
||||
}
|
||||
withContext(Dispatchers.EDT) {
|
||||
SoftAssertions.assertSoftly {
|
||||
it.assertThat(module.moduleFilePath).isEmpty()
|
||||
it.assertThat(module.isLoaded).isTrue
|
||||
@@ -239,7 +239,7 @@ class ModuleStoreTest {
|
||||
}
|
||||
}
|
||||
|
||||
inline suspend fun <T> Module.useAndDispose(task: Module.() -> T): T {
|
||||
suspend inline fun <T> Module.useAndDispose(task: Module.() -> T): T {
|
||||
try {
|
||||
return task()
|
||||
}
|
||||
@@ -252,9 +252,7 @@ inline suspend fun <T> Module.useAndDispose(task: Module.() -> T): T {
|
||||
|
||||
suspend fun ProjectRule.loadModule(file: VirtualFile): Module {
|
||||
val project = project
|
||||
return withContext(Dispatchers.EDT) {
|
||||
runWriteAction { ModuleManager.getInstance(project).loadModule(file.toNioPath()) }
|
||||
}
|
||||
return writeAction { ModuleManager.getInstance(project).loadModule(file.toNioPath()) }
|
||||
}
|
||||
|
||||
val Module.contentRootUrls: Array<String>
|
||||
@@ -262,9 +260,7 @@ val Module.contentRootUrls: Array<String>
|
||||
|
||||
internal suspend fun ProjectRule.createModule(path: Path): Module {
|
||||
val project = project
|
||||
return withContext(Dispatchers.EDT) {
|
||||
runWriteAction {
|
||||
ModuleManager.getInstance(project).newModule(path, ModuleTypeId.JAVA_MODULE)
|
||||
}
|
||||
return writeAction {
|
||||
ModuleManager.getInstance(project).newModule(path, ModuleTypeId.JAVA_MODULE)
|
||||
}
|
||||
}
|
||||
@@ -74,7 +74,7 @@ internal class ProjectStoreTest {
|
||||
file.write(file.readText().replace("""<option name="AAvalue" value="foo" />""", """<option name="AAvalue" value="newValue" />"""))
|
||||
|
||||
refreshProjectConfigDir(project)
|
||||
StoreReloadManager.getInstance().reloadChangedStorageFiles()
|
||||
StoreReloadManager.getInstance(project).reloadChangedStorageFiles()
|
||||
|
||||
assertThat(testComponent.state).isEqualTo(TestState("newValue"))
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ public abstract class VirtualFileManager implements ModificationTracker {
|
||||
* Refreshes the cached file systems information from the physical file systems asynchronously.
|
||||
* Launches specified action when refresh is finished.
|
||||
* <p>
|
||||
* @param postAction - action which will be executed in write-action after refresh session finished.
|
||||
* @param postAction - action which will be executed in write-action after the refresh session finished.
|
||||
* @return refresh session ID.
|
||||
*/
|
||||
public abstract long asyncRefresh(@Nullable Runnable postAction);
|
||||
@@ -189,12 +189,6 @@ public abstract class VirtualFileManager implements ModificationTracker {
|
||||
return URLUtil.extractPath(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #addVirtualFileManagerListener(VirtualFileManagerListener, Disposable)}
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract void addVirtualFileManagerListener(@NotNull VirtualFileManagerListener listener);
|
||||
|
||||
public abstract void addVirtualFileManagerListener(@NotNull VirtualFileManagerListener listener, @NotNull Disposable parentDisposable);
|
||||
|
||||
/**
|
||||
@@ -210,7 +204,7 @@ public abstract class VirtualFileManager implements ModificationTracker {
|
||||
|
||||
/**
|
||||
* @return a number that's incremented every time something changes in the VFS, i.e. file hierarchy, names, flags, attributes, contents.
|
||||
* This only counts modifications done in current IDE session.
|
||||
* This only counts modifications done in the current IDE session.
|
||||
* @see #getStructureModificationCount()
|
||||
*/
|
||||
@Override
|
||||
@@ -218,7 +212,7 @@ public abstract class VirtualFileManager implements ModificationTracker {
|
||||
|
||||
/**
|
||||
* @return a number that's incremented every time something changes in the VFS structure, i.e. file hierarchy or names.
|
||||
* This only counts modifications done in current IDE session.
|
||||
* This only counts modifications done in the current IDE session.
|
||||
* @see #getModificationCount()
|
||||
*/
|
||||
public abstract long getStructureModificationCount();
|
||||
|
||||
@@ -1,14 +1,20 @@
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.openapi.vfs;
|
||||
|
||||
import com.intellij.util.messages.Topic;
|
||||
|
||||
import java.util.EventListener;
|
||||
|
||||
/**
|
||||
* Message bus cannot be used because before / after events are not supported - order of events maybe changed by message bus.
|
||||
*
|
||||
* <p>
|
||||
* Use extension point: {@code <extensionPoint name="virtualFileManagerListener" interface="com.example.Foo"/>}
|
||||
*/
|
||||
public interface VirtualFileManagerListener extends EventListener {
|
||||
@Topic.AppLevel
|
||||
Topic<VirtualFileManagerListener> TOPIC =
|
||||
new Topic<>(VirtualFileManagerListener.class, Topic.BroadcastDirection.TO_DIRECT_CHILDREN, true);
|
||||
|
||||
default void beforeRefreshStart(boolean asynchronous) {
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
package com.intellij.openapi.vfs.impl;
|
||||
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.application.*;
|
||||
import com.intellij.openapi.application.Application;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.application.ModalityState;
|
||||
import com.intellij.openapi.application.ex.ApplicationManagerEx;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.extensions.ExtensionPoint;
|
||||
@@ -39,7 +41,7 @@ public class VirtualFileManagerImpl extends VirtualFileManagerEx implements Disp
|
||||
protected static final Logger LOG = Logger.getInstance(VirtualFileManagerImpl.class);
|
||||
|
||||
// do not use extension point name to avoid map lookup on each event publishing
|
||||
private static final ExtensionPointImpl<VirtualFileManagerListener> MANAGER_LISTENER_EP =
|
||||
private static final ExtensionPointImpl<@NotNull VirtualFileManagerListener> MANAGER_LISTENER_EP =
|
||||
((ExtensionsAreaImpl)ApplicationManager.getApplication().getExtensionArea()).getExtensionPoint("com.intellij.virtualFileManagerListener");
|
||||
|
||||
private final List<? extends VirtualFileSystem> myPreCreatedFileSystems;
|
||||
@@ -172,14 +174,9 @@ public class VirtualFileManagerImpl extends VirtualFileManagerEx implements Disp
|
||||
myVirtualFileListenerMulticaster.removeListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addVirtualFileManagerListener(@NotNull VirtualFileManagerListener listener) {
|
||||
myVirtualFileManagerListeners.add(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addVirtualFileManagerListener(@NotNull VirtualFileManagerListener listener, @NotNull Disposable parentDisposable) {
|
||||
addVirtualFileManagerListener(listener);
|
||||
myVirtualFileManagerListeners.add(listener);
|
||||
Disposer.register(parentDisposable, () -> removeVirtualFileManagerListener(listener));
|
||||
}
|
||||
|
||||
@@ -202,16 +199,16 @@ public class VirtualFileManagerImpl extends VirtualFileManagerEx implements Disp
|
||||
@Override
|
||||
public void notifyPropertyChanged(@NotNull VirtualFile virtualFile, @VirtualFile.PropName @NotNull String property, Object oldValue, Object newValue) {
|
||||
Application app = ApplicationManager.getApplication();
|
||||
AppUIExecutor.onWriteThread(ModalityState.NON_MODAL).later().expireWith(app).submit(() -> {
|
||||
ApplicationManager.getApplication().invokeLater(() -> {
|
||||
if (virtualFile.isValid()) {
|
||||
WriteAction.run(() -> {
|
||||
ApplicationManager.getApplication().runWriteAction(() -> {
|
||||
List<VFileEvent> events = Collections.singletonList(new VFilePropertyChangeEvent(this, virtualFile, property, oldValue, newValue, false));
|
||||
BulkFileListener listener = app.getMessageBus().syncPublisher(VirtualFileManager.VFS_CHANGES);
|
||||
listener.before(events);
|
||||
listener.after(events);
|
||||
});
|
||||
}
|
||||
});
|
||||
}, ModalityState.NON_MODAL);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -165,7 +165,7 @@ public final class MergeUtil {
|
||||
|
||||
public static void reportProjectFileChangeIfNeeded(@Nullable Project project, @Nullable VirtualFile file) {
|
||||
if (project != null && file != null && isProjectFile(file)) {
|
||||
StoreReloadManager.getInstance().saveChangedProjectFile(file, project);
|
||||
StoreReloadManager.Companion.getInstance(project).saveChangedProjectFile(file);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,10 @@ import com.intellij.openapi.module.EmptyModuleType
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.module.ModuleManager
|
||||
import com.intellij.openapi.module.ModuleTypeId
|
||||
import com.intellij.openapi.project.*
|
||||
import com.intellij.openapi.project.ExternalStorageConfigurationManager
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.project.doNotEnableExternalStorageByDefaultInTests
|
||||
import com.intellij.openapi.project.getProjectDataPathRoot
|
||||
import com.intellij.openapi.roots.*
|
||||
import com.intellij.openapi.roots.libraries.LibraryTablesRegistrar
|
||||
import com.intellij.openapi.util.Disposer
|
||||
@@ -74,6 +77,7 @@ class ExternalSystemStorageTest {
|
||||
val appRule = ApplicationRule()
|
||||
|
||||
}
|
||||
|
||||
@JvmField
|
||||
@Rule
|
||||
val disposableRule = DisposableRule()
|
||||
@@ -84,7 +88,8 @@ class ExternalSystemStorageTest {
|
||||
|
||||
@Test
|
||||
fun `save single mavenized module`() = saveProjectInExternalStorageAndCheckResult("singleModule") { project, projectDir ->
|
||||
val module = ModuleManager.getInstance(project).newModule(projectDir.resolve("test.iml").systemIndependentPath, ModuleTypeId.JAVA_MODULE)
|
||||
val module = ModuleManager.getInstance(project).newModule(projectDir.resolve("test.iml").systemIndependentPath,
|
||||
ModuleTypeId.JAVA_MODULE)
|
||||
ModuleRootModificationUtil.addContentRoot(module, projectDir.systemIndependentPath)
|
||||
ExternalSystemModulePropertyManager.getInstance(module).setMavenized(true)
|
||||
}
|
||||
@@ -100,7 +105,8 @@ class ExternalSystemStorageTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `save single module from external system`() = saveProjectInExternalStorageAndCheckResult("singleModuleFromExternalSystem") { project, projectDir ->
|
||||
fun `save single module from external system`() = saveProjectInExternalStorageAndCheckResult(
|
||||
"singleModuleFromExternalSystem") { project, projectDir ->
|
||||
val module = ModuleManager.getInstance(project).newModule(projectDir.resolve("test.iml").systemIndependentPath,
|
||||
ModuleTypeId.JAVA_MODULE)
|
||||
ModuleRootModificationUtil.addContentRoot(module, projectDir.systemIndependentPath)
|
||||
@@ -111,33 +117,31 @@ class ExternalSystemStorageTest {
|
||||
fun `applying external system options twice`() {
|
||||
createProjectAndUseInLoadComponentStateMode(tempDirManager, directoryBased = true, useDefaultProjectSettings = false) { project ->
|
||||
runBlocking {
|
||||
withContext(Dispatchers.EDT) {
|
||||
runWriteAction {
|
||||
val projectDir = project.stateStore.directoryStorePath!!.parent
|
||||
val module = ModuleManager.getInstance(project).newModule(projectDir.resolve("test.iml").systemIndependentPath,
|
||||
ModuleTypeId.JAVA_MODULE)
|
||||
ModuleRootModificationUtil.addContentRoot(module, projectDir.systemIndependentPath)
|
||||
writeAction {
|
||||
val projectDir = project.stateStore.directoryStorePath!!.parent
|
||||
val module = ModuleManager.getInstance(project).newModule(projectDir.resolve("test.iml").systemIndependentPath,
|
||||
ModuleTypeId.JAVA_MODULE)
|
||||
ModuleRootModificationUtil.addContentRoot(module, projectDir.systemIndependentPath)
|
||||
|
||||
|
||||
val propertyManager = ExternalSystemModulePropertyManager.getInstance(module)
|
||||
val propertyManager = ExternalSystemModulePropertyManager.getInstance(module)
|
||||
|
||||
val systemId = ProjectSystemId("GRADLE")
|
||||
val moduleData = ModuleData("test", systemId, "", "", "", projectDir.systemIndependentPath).also {
|
||||
it.group = "group"
|
||||
it.version = "42.0"
|
||||
}
|
||||
val projectData = ProjectData(systemId, "", "", projectDir.systemIndependentPath)
|
||||
|
||||
|
||||
val modelsProvider = IdeModifiableModelsProviderImpl(project)
|
||||
|
||||
propertyManager.setExternalOptions(systemId, moduleData, projectData)
|
||||
propertyManager.setExternalOptions(systemId, moduleData, projectData)
|
||||
|
||||
val externalOptionsFromBuilder = modelsProvider.actualStorageBuilder
|
||||
.entities(ModuleEntity::class.java).singleOrNull()?.exModuleOptions
|
||||
assertEquals("GRADLE", externalOptionsFromBuilder?.externalSystem)
|
||||
val systemId = ProjectSystemId("GRADLE")
|
||||
val moduleData = ModuleData("test", systemId, "", "", "", projectDir.systemIndependentPath).also {
|
||||
it.group = "group"
|
||||
it.version = "42.0"
|
||||
}
|
||||
val projectData = ProjectData(systemId, "", "", projectDir.systemIndependentPath)
|
||||
|
||||
|
||||
val modelsProvider = IdeModifiableModelsProviderImpl(project)
|
||||
|
||||
propertyManager.setExternalOptions(systemId, moduleData, projectData)
|
||||
propertyManager.setExternalOptions(systemId, moduleData, projectData)
|
||||
|
||||
val externalOptionsFromBuilder = modelsProvider.actualStorageBuilder
|
||||
.entities(ModuleEntity::class.java).singleOrNull()?.exModuleOptions
|
||||
assertEquals("GRADLE", externalOptionsFromBuilder?.externalSystem)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -155,7 +159,8 @@ class ExternalSystemStorageTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `save single module from external system in internal storage`() = saveProjectInInternalStorageAndCheckResult("singleModuleFromExternalSystemInInternalStorage") { project, projectDir ->
|
||||
fun `save single module from external system in internal storage`() = saveProjectInInternalStorageAndCheckResult(
|
||||
"singleModuleFromExternalSystemInInternalStorage") { project, projectDir ->
|
||||
val module = ModuleManager.getInstance(project).newModule(projectDir.resolve("test.iml").systemIndependentPath,
|
||||
ModuleTypeId.JAVA_MODULE)
|
||||
ModuleRootModificationUtil.addContentRoot(module, projectDir.systemIndependentPath)
|
||||
@@ -163,7 +168,8 @@ class ExternalSystemStorageTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `load single module from external system in internal storage`() = loadProjectAndCheckResults("singleModuleFromExternalSystemInInternalStorage") { project ->
|
||||
fun `load single module from external system in internal storage`() = loadProjectAndCheckResults(
|
||||
"singleModuleFromExternalSystemInInternalStorage") { project ->
|
||||
val module = ModuleManager.getInstance(project).modules.single()
|
||||
assertThat(module.name).isEqualTo("test")
|
||||
assertThat(module.moduleTypeName).isEqualTo(ModuleTypeId.JAVA_MODULE)
|
||||
@@ -196,7 +202,8 @@ class ExternalSystemStorageTest {
|
||||
|
||||
|
||||
@Test
|
||||
fun `save imported module in internal storage`() = saveProjectInInternalStorageAndCheckResult("singleModuleInInternalStorage") { project, projectDir ->
|
||||
fun `save imported module in internal storage`() = saveProjectInInternalStorageAndCheckResult(
|
||||
"singleModuleInInternalStorage") { project, projectDir ->
|
||||
val module = ModuleManager.getInstance(project).newModule(projectDir.resolve("test.iml").systemIndependentPath,
|
||||
ModuleTypeId.JAVA_MODULE)
|
||||
ModuleRootModificationUtil.addContentRoot(module, projectDir.systemIndependentPath)
|
||||
@@ -215,9 +222,11 @@ class ExternalSystemStorageTest {
|
||||
|
||||
@Test
|
||||
fun `save mixed modules`() = saveProjectInExternalStorageAndCheckResult("mixedModules") { project, projectDir ->
|
||||
val regular = ModuleManager.getInstance(project).newModule(projectDir.resolve("regular.iml").systemIndependentPath, ModuleTypeId.JAVA_MODULE)
|
||||
val regular = ModuleManager.getInstance(project).newModule(projectDir.resolve("regular.iml").systemIndependentPath,
|
||||
ModuleTypeId.JAVA_MODULE)
|
||||
ModuleRootModificationUtil.addContentRoot(regular, projectDir.resolve("regular").systemIndependentPath)
|
||||
val imported = ModuleManager.getInstance(project).newModule(projectDir.resolve("imported.iml").systemIndependentPath, ModuleTypeId.JAVA_MODULE)
|
||||
val imported = ModuleManager.getInstance(project).newModule(projectDir.resolve("imported.iml").systemIndependentPath,
|
||||
ModuleTypeId.JAVA_MODULE)
|
||||
ModuleRootModificationUtil.addContentRoot(imported, projectDir.resolve("imported").systemIndependentPath)
|
||||
ExternalSystemModulePropertyManager.getInstance(imported).setMavenized(true)
|
||||
ExternalSystemModulePropertyManager.getInstance(imported).setLinkedProjectPath("${project.basePath}/imported")
|
||||
@@ -251,7 +260,8 @@ class ExternalSystemStorageTest {
|
||||
assertThat(regular.moduleTypeName).isEqualTo(ModuleTypeId.JAVA_MODULE)
|
||||
assertThat(imported.moduleFilePath).isEqualTo("${project.basePath}/imported.iml")
|
||||
assertThat(regular.moduleFilePath).isEqualTo("${project.basePath}/regular.iml")
|
||||
assertThat(ModuleRootManager.getInstance(imported).contentRootUrls.single()).isEqualTo(VfsUtil.pathToUrl("${project.basePath}/imported"))
|
||||
assertThat(ModuleRootManager.getInstance(imported).contentRootUrls.single()).isEqualTo(
|
||||
VfsUtil.pathToUrl("${project.basePath}/imported"))
|
||||
assertThat(ModuleRootManager.getInstance(regular).contentRootUrls.single()).isEqualTo(VfsUtil.pathToUrl("${project.basePath}/regular"))
|
||||
val externalModuleProperty = ExternalSystemModulePropertyManager.getInstance(imported)
|
||||
assertThat(externalModuleProperty.isMavenized()).isTrue()
|
||||
@@ -260,8 +270,10 @@ class ExternalSystemStorageTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `save regular facet in imported module`() = saveProjectInExternalStorageAndCheckResult("regularFacetInImportedModule") { project, projectDir ->
|
||||
val module = ModuleManager.getInstance(project).newModule(projectDir.resolve("test.iml").systemIndependentPath, ModuleTypeId.JAVA_MODULE)
|
||||
fun `save regular facet in imported module`() = saveProjectInExternalStorageAndCheckResult(
|
||||
"regularFacetInImportedModule") { project, projectDir ->
|
||||
val module = ModuleManager.getInstance(project).newModule(projectDir.resolve("test.iml").systemIndependentPath,
|
||||
ModuleTypeId.JAVA_MODULE)
|
||||
ModuleRootModificationUtil.addContentRoot(module, projectDir.systemIndependentPath)
|
||||
FacetManager.getInstance(module).addFacet(MockFacetType.getInstance(), "regular", null)
|
||||
ExternalSystemModulePropertyManager.getInstance(module).setMavenized(true)
|
||||
@@ -291,18 +303,21 @@ class ExternalSystemStorageTest {
|
||||
loadProjectAndCheckResults("singleModuleInInternalAndExternalStorages") { project ->
|
||||
val modules = ModuleManager.getInstance(project).modules
|
||||
assertThat(modules).hasSize(1)
|
||||
val testModule= modules[0]
|
||||
val testModule = modules[0]
|
||||
assertThat(testModule.name).isEqualTo("test")
|
||||
assertThat(testModule.moduleTypeName).isEqualTo(ModuleTypeId.JAVA_MODULE)
|
||||
assertThat(testModule.moduleFilePath).isEqualTo("${project.basePath}/test.iml")
|
||||
assertThat(ModuleRootManager.getInstance(testModule).contentRootUrls.single()).isEqualTo(VfsUtil.pathToUrl("${project.basePath}/test"))
|
||||
assertThat(ModuleRootManager.getInstance(testModule).contentRootUrls.single()).isEqualTo(
|
||||
VfsUtil.pathToUrl("${project.basePath}/test"))
|
||||
val externalModuleProperty = ExternalSystemModulePropertyManager.getInstance(testModule)
|
||||
assertThat(externalModuleProperty.isMavenized()).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `save imported facet in imported module`() = saveProjectInExternalStorageAndCheckResult("importedFacetInImportedModule") { project, projectDir ->
|
||||
val imported = ModuleManager.getInstance(project).newModule(projectDir.resolve("imported.iml").systemIndependentPath, ModuleTypeId.JAVA_MODULE)
|
||||
fun `save imported facet in imported module`() = saveProjectInExternalStorageAndCheckResult(
|
||||
"importedFacetInImportedModule") { project, projectDir ->
|
||||
val imported = ModuleManager.getInstance(project).newModule(projectDir.resolve("imported.iml").systemIndependentPath,
|
||||
ModuleTypeId.JAVA_MODULE)
|
||||
val facetRoot = VfsUtilCore.pathToUrl(projectDir.resolve("facet").systemIndependentPath)
|
||||
addFacet(imported, SerializationConstants.MAVEN_EXTERNAL_SOURCE_ID, "imported", listOf(facetRoot))
|
||||
ExternalSystemModulePropertyManager.getInstance(imported).setMavenized(true)
|
||||
@@ -521,7 +536,8 @@ class ExternalSystemStorageTest {
|
||||
|
||||
@Test
|
||||
fun `test facet and libraries saved in internal store after IDE reload`() {
|
||||
loadModifySaveAndCheck("singleModuleFacetAndLibFromExternalSystemInInternalStorage", "singleModuleFacetAndLibFromExternalSystem") { project ->
|
||||
loadModifySaveAndCheck("singleModuleFacetAndLibFromExternalSystemInInternalStorage",
|
||||
"singleModuleFacetAndLibFromExternalSystem") { project ->
|
||||
ExternalProjectsManagerImpl.getInstance(project).setStoreExternally(true)
|
||||
}
|
||||
}
|
||||
@@ -564,7 +580,7 @@ class ExternalSystemStorageTest {
|
||||
WriteAction.runAndWait<RuntimeException> {
|
||||
VfsUtil.markDirtyAndRefresh(false, false, false, miscFile)
|
||||
}
|
||||
runBlocking { StoreReloadManager.getInstance().reloadChangedStorageFiles() }
|
||||
runBlocking { StoreReloadManager.getInstance(project).reloadChangedStorageFiles() }
|
||||
ApplicationManager.getApplication().invokeAndWait {
|
||||
PlatformTestUtil.dispatchAllInvocationEventsInIdeEventQueue()
|
||||
}
|
||||
@@ -585,8 +601,8 @@ class ExternalSystemStorageTest {
|
||||
WriteAction.runAndWait<RuntimeException> {
|
||||
VfsUtil.markDirtyAndRefresh(false, false, false, miscFile)
|
||||
}
|
||||
runBlocking { StoreReloadManager.getInstance().reloadChangedStorageFiles() }
|
||||
ApplicationManager.getApplication().invokeAndWait{
|
||||
runBlocking { StoreReloadManager.getInstance(project).reloadChangedStorageFiles() }
|
||||
ApplicationManager.getApplication().invokeAndWait {
|
||||
PlatformTestUtil.dispatchAllInvocationEventsInIdeEventQueue()
|
||||
}
|
||||
}
|
||||
@@ -611,10 +627,10 @@ class ExternalSystemStorageTest {
|
||||
|
||||
@Test
|
||||
fun `incorrect modules setup same iml`() {
|
||||
loadProjectAndCheckResults("incorrectModulesSetupSameIml") { project ->
|
||||
val modules = ModuleManager.getInstance(project).modules
|
||||
assertEquals(1, modules.size)
|
||||
}
|
||||
loadProjectAndCheckResults("incorrectModulesSetupSameIml") { project ->
|
||||
val modules = ModuleManager.getInstance(project).modules
|
||||
assertEquals(1, modules.size)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -727,7 +743,7 @@ class ExternalSystemStorageTest {
|
||||
|
||||
@Test
|
||||
fun `test property for module`() {
|
||||
loadModifySaveAndCheck("twoModules", "moduleWithTestProperties") {project ->
|
||||
loadModifySaveAndCheck("twoModules", "moduleWithTestProperties") { project ->
|
||||
val mainModuleName = "foo"
|
||||
val testModuleName = "foo.test"
|
||||
val moduleManager = ModuleManager.getInstance(project)
|
||||
@@ -852,7 +868,8 @@ class ExternalSystemStorageTest {
|
||||
|
||||
val expectedCacheDir = expectedDir.resolve("cache")
|
||||
if (Files.exists(expectedCacheDir)) {
|
||||
cacheDir.toFile().assertMatches(directoryContentOf(expectedCacheDir, originalExpectedDir.resolve("cache")), FileTextMatcher.ignoreBlankLines())
|
||||
cacheDir.toFile().assertMatches(directoryContentOf(expectedCacheDir, originalExpectedDir.resolve("cache")),
|
||||
FileTextMatcher.ignoreBlankLines())
|
||||
}
|
||||
else {
|
||||
assertTrue("$cacheDir doesn't exist", !Files.exists(cacheDir) || isFolderWithoutFiles(cacheDir.toFile()))
|
||||
@@ -909,7 +926,7 @@ class ExternalSystemStorageTest {
|
||||
}
|
||||
|
||||
|
||||
private val MOCK_EXTERNAL_SOURCE = object: ProjectModelExternalSource {
|
||||
private val MOCK_EXTERNAL_SOURCE = object : ProjectModelExternalSource {
|
||||
override fun getDisplayName() = "mock"
|
||||
|
||||
override fun getId() = "mock"
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.configurationStore
|
||||
|
||||
import com.intellij.openapi.components.ComponentManager
|
||||
import com.intellij.openapi.components.StateStorage
|
||||
import com.intellij.openapi.components.impl.stores.IComponentStore
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
@@ -10,11 +10,10 @@ import org.jetbrains.annotations.ApiStatus
|
||||
|
||||
interface StoreReloadManager {
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun getInstance() = service<StoreReloadManager>()
|
||||
fun getInstance(project: Project) = project.service<StoreReloadManager>()
|
||||
}
|
||||
|
||||
fun reloadProject(project: Project)
|
||||
fun reloadProject()
|
||||
|
||||
fun blockReloadingProjectOnExternalChanges()
|
||||
|
||||
@@ -26,9 +25,9 @@ interface StoreReloadManager {
|
||||
@ApiStatus.Internal
|
||||
fun scheduleProcessingChangedFiles()
|
||||
|
||||
fun saveChangedProjectFile(file: VirtualFile, project: Project)
|
||||
fun saveChangedProjectFile(file: VirtualFile)
|
||||
|
||||
suspend fun reloadChangedStorageFiles()
|
||||
|
||||
fun storageFilesChanged(componentManagerToStorages: Map<ComponentManager, Collection<StateStorage>>)
|
||||
fun storageFilesChanged(store: IComponentStore, storages: Collection<StateStorage>)
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
@file:Suppress("ReplacePutWithAssignment")
|
||||
|
||||
package com.intellij.configurationStore
|
||||
|
||||
import com.intellij.ide.IdeBundle
|
||||
@@ -112,7 +114,7 @@ private fun checkUnknownMacros(project: Project,
|
||||
else if (Messages.showYesNoDialog(project, IdeBundle.message("dialog.message.component.could.not.be.reloaded"),
|
||||
IdeBundle.message("dialog.title.configuration.changed"),
|
||||
Messages.getQuestionIcon()) == Messages.YES) {
|
||||
StoreReloadManager.getInstance().reloadProject(project)
|
||||
StoreReloadManager.getInstance(project).reloadProject()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -212,38 +212,41 @@ fun getPerOsSettingsStorageFolderName(): String {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param forceSavingAllSettings Whether to force save non-roamable component configuration.
|
||||
*/
|
||||
@CalledInAny
|
||||
suspend fun saveProjectsAndApp(forceSavingAllSettings: Boolean, onlyProject: Project? = null) {
|
||||
val storeReloadManager = StoreReloadManager.getInstance()
|
||||
private suspend inline fun reloadChangedStorageFilesAndRun(project: Project, task: () -> Unit) {
|
||||
val storeReloadManager = StoreReloadManager.getInstance(project)
|
||||
storeReloadManager.reloadChangedStorageFiles()
|
||||
storeReloadManager.blockReloadingProjectOnExternalChanges()
|
||||
try {
|
||||
val start = System.currentTimeMillis()
|
||||
saveSettings(ApplicationManager.getApplication(), forceSavingAllSettings)
|
||||
if (onlyProject == null) {
|
||||
saveAllProjects(forceSavingAllSettings)
|
||||
}
|
||||
else {
|
||||
saveSettings(onlyProject, forceSavingAllSettings = true)
|
||||
}
|
||||
|
||||
val duration = System.currentTimeMillis() - start
|
||||
if (duration > 1000 || LOG.isDebugEnabled) {
|
||||
LOG.info("saveProjectsAndApp took $duration ms")
|
||||
}
|
||||
task()
|
||||
}
|
||||
finally {
|
||||
storeReloadManager.unblockReloadingProjectOnExternalChanges()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param forceSavingAllSettings Whether to force save non-roamable component configuration.
|
||||
*/
|
||||
@CalledInAny
|
||||
private suspend fun saveAllProjects(forceSavingAllSettings: Boolean) {
|
||||
processOpenedProjects { project ->
|
||||
saveSettings(project, forceSavingAllSettings)
|
||||
suspend fun saveProjectsAndApp(forceSavingAllSettings: Boolean, onlyProject: Project? = null) {
|
||||
val start = System.currentTimeMillis()
|
||||
saveSettings(ApplicationManager.getApplication(), forceSavingAllSettings)
|
||||
if (onlyProject == null) {
|
||||
processOpenedProjects { project ->
|
||||
reloadChangedStorageFilesAndRun(project) {
|
||||
saveSettings(project, forceSavingAllSettings)
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
reloadChangedStorageFilesAndRun(onlyProject) {
|
||||
saveSettings(onlyProject, forceSavingAllSettings = true)
|
||||
}
|
||||
}
|
||||
|
||||
val duration = System.currentTimeMillis() - start
|
||||
if (duration > 1000 || LOG.isDebugEnabled) {
|
||||
LOG.info("saveProjectsAndApp took $duration ms")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@ import org.jetbrains.annotations.NotNull;
|
||||
import java.util.Objects;
|
||||
|
||||
final class ReloadProjectAction extends AnAction implements DumbAware {
|
||||
|
||||
@Override
|
||||
public @NotNull ActionUpdateThread getActionUpdateThread() {
|
||||
return ActionUpdateThread.BGT;
|
||||
@@ -26,6 +25,6 @@ final class ReloadProjectAction extends AnAction implements DumbAware {
|
||||
@Override
|
||||
public void actionPerformed(@NotNull AnActionEvent e) {
|
||||
Project project = Objects.requireNonNull(e.getProject());
|
||||
StoreReloadManager.getInstance().reloadProject(project);
|
||||
StoreReloadManager.Companion.getInstance(project).reloadProject();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,6 +63,7 @@ import com.intellij.openapi.wm.impl.welcomeScreen.WelcomeFrame
|
||||
import com.intellij.platform.PlatformProjectOpenProcessor
|
||||
import com.intellij.platform.PlatformProjectOpenProcessor.Companion.isLoadedFromCacheButHasNoModules
|
||||
import com.intellij.platform.attachToProjectAsync
|
||||
import com.intellij.platform.jps.model.impl.diagnostic.JpsMetrics
|
||||
import com.intellij.projectImport.ProjectAttachProcessor
|
||||
import com.intellij.serviceContainer.ComponentManagerImpl
|
||||
import com.intellij.ui.IdeUICustomization
|
||||
@@ -77,7 +78,6 @@ import kotlinx.coroutines.*
|
||||
import org.jetbrains.annotations.ApiStatus.Internal
|
||||
import org.jetbrains.annotations.TestOnly
|
||||
import org.jetbrains.annotations.VisibleForTesting
|
||||
import com.intellij.platform.jps.model.impl.diagnostic.JpsMetrics
|
||||
import java.io.IOException
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.InvalidPathException
|
||||
@@ -268,7 +268,7 @@ open class ProjectManagerImpl : ProjectManagerEx(), Disposable {
|
||||
override fun findOpenProjectByHash(locationHash: String?): Project? = openProjectByHash.get(locationHash)
|
||||
|
||||
override fun reloadProject(project: Project) {
|
||||
StoreReloadManager.getInstance().reloadProject(project)
|
||||
StoreReloadManager.getInstance(project).reloadProject()
|
||||
}
|
||||
|
||||
override fun closeProject(project: Project): Boolean {
|
||||
@@ -637,7 +637,7 @@ open class ProjectManagerImpl : ProjectManagerEx(), Disposable {
|
||||
throw CancellationException("project is already opened")
|
||||
}
|
||||
|
||||
// Project is loaded and is initialized, project services and components can be accessed.
|
||||
// The project is loaded and is initialized, project services and components can be accessed.
|
||||
// But start-up and post start-up activities are not yet executed.
|
||||
if (!initFrameEarly) {
|
||||
rawProjectDeferred?.complete(project)
|
||||
@@ -1106,7 +1106,7 @@ private fun toCanonicalName(filePath: String): Path {
|
||||
catch (ignore: InvalidPathException) {
|
||||
}
|
||||
catch (e: IOException) {
|
||||
// OK. File does not yet exist, so its canonical path will be equal to its original path.
|
||||
// the file does not yet exist, so its canonical path will be equal to its original path
|
||||
}
|
||||
return file
|
||||
}
|
||||
@@ -1327,7 +1327,7 @@ private suspend fun checkTrustedState(projectStoreBaseDir: Path): Boolean {
|
||||
// this project is in recent projects => it was opened on this computer before
|
||||
// => most probably we already asked about its trusted state before
|
||||
// the only exception is: the project stayed in the UNKNOWN state in the previous version because it didn't utilize any dangerous features
|
||||
// in this case we will ask since no UNKNOWN state is allowed, but on a later stage, when we'll be able to look into the project-wide storage
|
||||
// in this case, we will ask since no UNKNOWN state is allowed, but on a later stage, when we'll be able to look into the project-wide storage
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@@ -7,13 +7,12 @@ import com.intellij.openapi.components.Storage;
|
||||
import com.intellij.openapi.components.StoragePathMacros;
|
||||
import com.intellij.openapi.project.ProjectReloadState;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@State(
|
||||
name = "ProjectReloadState",
|
||||
storages = @Storage(StoragePathMacros.WORKSPACE_FILE)
|
||||
)
|
||||
class ProjectReloadStateImpl extends ProjectReloadState implements PersistentStateComponent<ProjectReloadStateImpl> {
|
||||
final class ProjectReloadStateImpl extends ProjectReloadState implements PersistentStateComponent<ProjectReloadStateImpl> {
|
||||
public static final int UNKNOWN = 0;
|
||||
public static final int BEFORE_RELOAD = 1;
|
||||
public static final int AFTER_RELOAD = 2;
|
||||
@@ -30,9 +29,8 @@ class ProjectReloadStateImpl extends ProjectReloadState implements PersistentSta
|
||||
STATE = BEFORE_RELOAD;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public ProjectReloadStateImpl getState() {
|
||||
public @NotNull ProjectReloadStateImpl getState() {
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ package com.intellij.openapi.vfs.newvfs;
|
||||
|
||||
import com.intellij.openapi.application.Application;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.application.ReadAction;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.extensions.ExtensionPointName;
|
||||
import com.intellij.openapi.progress.ProcessCanceledException;
|
||||
@@ -43,7 +42,7 @@ public final class AsyncEventSupport {
|
||||
|
||||
public static void startListening() {
|
||||
Application app = ApplicationManager.getApplication();
|
||||
Disposer.register(app, () -> ensureAllEventsProcessed());
|
||||
Disposer.register(app, AsyncEventSupport::ensureAllEventsProcessed);
|
||||
|
||||
app.getMessageBus().connect().subscribe(VirtualFileManager.VFS_CHANGES, new BulkFileListener() {
|
||||
@Override
|
||||
@@ -87,7 +86,7 @@ public final class AsyncEventSupport {
|
||||
long startNs = System.nanoTime();
|
||||
boolean canceled = false;
|
||||
try {
|
||||
ReadAction.run(() -> ContainerUtil.addIfNotNull(appliers, listener.prepareChange(events)));
|
||||
ApplicationManager.getApplication().runReadAction(() -> ContainerUtil.addIfNotNull(appliers, listener.prepareChange(events)));
|
||||
}
|
||||
catch (ProcessCanceledException e) {
|
||||
canceled = true;
|
||||
|
||||
@@ -10,7 +10,7 @@ import com.intellij.ide.highlighter.ProjectFileType
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.application.EDT
|
||||
import com.intellij.openapi.application.runWriteAction
|
||||
import com.intellij.openapi.application.writeAction
|
||||
import com.intellij.openapi.components.Service
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.openapi.diagnostic.debug
|
||||
@@ -35,10 +35,16 @@ import com.intellij.project.stateStore
|
||||
import com.intellij.util.PlatformUtils.isIntelliJ
|
||||
import com.intellij.util.PlatformUtils.isRider
|
||||
import com.intellij.workspaceModel.ide.*
|
||||
import com.intellij.workspaceModel.ide.impl.*
|
||||
import com.intellij.workspaceModel.ide.impl.GlobalWorkspaceModel
|
||||
import com.intellij.workspaceModel.ide.impl.WorkspaceModelImpl
|
||||
import com.intellij.workspaceModel.ide.impl.WorkspaceModelInitialTestContent
|
||||
import com.intellij.workspaceModel.ide.impl.jps.serialization.JpsProjectEntitiesLoader.createProjectSerializers
|
||||
import com.intellij.workspaceModel.ide.impl.jpsMetrics
|
||||
import com.intellij.workspaceModel.ide.legacyBridge.GlobalLibraryTableBridge
|
||||
import com.intellij.workspaceModel.storage.*
|
||||
import com.intellij.workspaceModel.storage.DummyParentEntitySource
|
||||
import com.intellij.workspaceModel.storage.EntitySource
|
||||
import com.intellij.workspaceModel.storage.MutableEntityStorage
|
||||
import com.intellij.workspaceModel.storage.VersionedStorageChange
|
||||
import com.intellij.workspaceModel.storage.bridgeEntities.ModuleEntity
|
||||
import com.intellij.workspaceModel.storage.url.VirtualFileUrlManager
|
||||
import io.opentelemetry.api.metrics.Meter
|
||||
@@ -65,7 +71,7 @@ class JpsProjectModelSynchronizer(private val project: Project) : Disposable {
|
||||
private val applyLoadedStorageTimeMs: AtomicLong = AtomicLong()
|
||||
private val saveChangedProjectEntitiesTimeMs: AtomicLong = AtomicLong()
|
||||
|
||||
private fun setupOpenTelemetryReporting(meter: Meter): Unit {
|
||||
private fun setupOpenTelemetryReporting(meter: Meter) {
|
||||
val jpsLoadProjectToEmptyStorageTimeGauge = meter.gaugeBuilder("jps.load.project.to.empty.storage.ms")
|
||||
.ofLongs().setDescription("Total time spent in method").buildObserver()
|
||||
|
||||
@@ -104,8 +110,9 @@ class JpsProjectModelSynchronizer(private val project: Project) : Disposable {
|
||||
private var childActivity: Activity? = null
|
||||
|
||||
fun needToReloadProjectEntities(): Boolean {
|
||||
if (StoreReloadManager.getInstance().isReloadBlocked()) return false
|
||||
if (serializers.get() == null) return false
|
||||
if (StoreReloadManager.getInstance(project).isReloadBlocked() || serializers.get() == null) {
|
||||
return false
|
||||
}
|
||||
|
||||
synchronized(incomingChanges) {
|
||||
return incomingChanges.isNotEmpty()
|
||||
@@ -115,7 +122,7 @@ class JpsProjectModelSynchronizer(private val project: Project) : Disposable {
|
||||
suspend fun reloadProjectEntities() {
|
||||
val start = System.currentTimeMillis()
|
||||
|
||||
if (StoreReloadManager.getInstance().isReloadBlocked()) {
|
||||
if (StoreReloadManager.getInstance(project).isReloadBlocked()) {
|
||||
LOG.debug("Skip reloading because it's blocked")
|
||||
return
|
||||
}
|
||||
@@ -147,35 +154,33 @@ class JpsProjectModelSynchronizer(private val project: Project) : Disposable {
|
||||
&& !reloadingResult.unloadedEntityBuilder.hasChanges()
|
||||
&& !reloadingResult.orphanageBuilder.hasChanges()) return
|
||||
|
||||
withContext(Dispatchers.EDT) {
|
||||
runWriteAction {
|
||||
val affectedEntityFilter: (EntitySource) -> Boolean = {
|
||||
it in reloadingResult.affectedSources
|
||||
|| (it is JpsImportedEntitySource && !it.storedExternally && it.internalFile in reloadingResult.affectedSources)
|
||||
|| it is DummyParentEntitySource
|
||||
}
|
||||
val description = "Reload entities after changes in JPS configuration files"
|
||||
writeAction {
|
||||
val affectedEntityFilter: (EntitySource) -> Boolean = {
|
||||
it in reloadingResult.affectedSources
|
||||
|| (it is JpsImportedEntitySource && !it.storedExternally && it.internalFile in reloadingResult.affectedSources)
|
||||
|| it is DummyParentEntitySource
|
||||
}
|
||||
val description = "Reload entities after changes in JPS configuration files"
|
||||
|
||||
// Update builder of unloaded entities
|
||||
if (reloadingResult.unloadedEntityBuilder.hasChanges()) {
|
||||
WorkspaceModel.getInstance(project).updateUnloadedEntities(description) { builder ->
|
||||
builder.replaceBySource(affectedEntityFilter, reloadingResult.unloadedEntityBuilder.toSnapshot())
|
||||
}
|
||||
// Update builder of unloaded entities
|
||||
if (reloadingResult.unloadedEntityBuilder.hasChanges()) {
|
||||
WorkspaceModel.getInstance(project).updateUnloadedEntities(description) { builder ->
|
||||
builder.replaceBySource(affectedEntityFilter, reloadingResult.unloadedEntityBuilder.toSnapshot())
|
||||
}
|
||||
}
|
||||
|
||||
val unloadedBuilder = MutableEntityStorage.from(WorkspaceModel.getInstance(project).currentSnapshotOfUnloadedEntities)
|
||||
WorkspaceModel.getInstance(project).updateProjectModel(description) { updater ->
|
||||
val storage = reloadingResult.builder.toSnapshot()
|
||||
updater.replaceBySource(affectedEntityFilter, storage)
|
||||
runAutomaticModuleUnloader(updater, unloadedBuilder)
|
||||
}
|
||||
addUnloadedModuleEntities(unloadedBuilder)
|
||||
sourcesToSave.removeAll(reloadingResult.affectedSources)
|
||||
val unloadedBuilder = MutableEntityStorage.from(WorkspaceModel.getInstance(project).currentSnapshotOfUnloadedEntities)
|
||||
WorkspaceModel.getInstance(project).updateProjectModel(description) { updater ->
|
||||
val storage = reloadingResult.builder.toSnapshot()
|
||||
updater.replaceBySource(affectedEntityFilter, storage)
|
||||
runAutomaticModuleUnloader(updater, unloadedBuilder)
|
||||
}
|
||||
addUnloadedModuleEntities(unloadedBuilder)
|
||||
sourcesToSave.removeAll(reloadingResult.affectedSources)
|
||||
|
||||
// Update orphanage storage
|
||||
if (reloadingResult.orphanageBuilder.hasChanges()) {
|
||||
EntitiesOrphanage.getInstance(project).update { it.addDiff(reloadingResult.orphanageBuilder) }
|
||||
}
|
||||
// Update orphanage storage
|
||||
if (reloadingResult.orphanageBuilder.hasChanges()) {
|
||||
EntitiesOrphanage.getInstance(project).update { it.addDiff(reloadingResult.orphanageBuilder) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -243,7 +248,7 @@ class JpsProjectModelSynchronizer(private val project: Project) : Disposable {
|
||||
val change = JpsConfigurationFilesChange(addedUrls, removedUrls, changedUrls)
|
||||
incomingChanges.add(change)
|
||||
|
||||
StoreReloadManager.getInstance().scheduleProcessingChangedFiles()
|
||||
StoreReloadManager.getInstance(project).scheduleProcessingChangedFiles()
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -421,7 +426,7 @@ class JpsProjectModelSynchronizer(private val project: Project) : Disposable {
|
||||
val unloadedModuleSources = WorkspaceModel.getInstance(project).currentSnapshotOfUnloadedEntities.entities(
|
||||
ModuleEntity::class.java).map { it.entitySource }
|
||||
synchronized(sourcesToSave) {
|
||||
//to trigger save for modules.xml
|
||||
// to trigger save for modules.xml
|
||||
sourcesToSave.addAll(moduleSources)
|
||||
sourcesToSave.addAll(unloadedModuleSources)
|
||||
}
|
||||
|
||||
@@ -175,6 +175,7 @@
|
||||
|
||||
<applicationService serviceInterface="com.intellij.openapi.components.impl.stores.IComponentStore"
|
||||
serviceImplementation="com.intellij.configurationStore.ApplicationStoreImpl"/>
|
||||
<vfs.asyncListener implementation="com.intellij.configurationStore.MyAsyncVfsListener"/>
|
||||
|
||||
<applicationService serviceInterface="com.intellij.openapi.components.PathMacroManager"
|
||||
serviceImplementation="com.intellij.configurationStore.ApplicationPathMacroManager"/>
|
||||
@@ -1219,9 +1220,8 @@
|
||||
<registryKey key="run.anything.context.recent.directory.number" defaultValue="5"
|
||||
description="Defines storing Run Anything context combobox directories number."/>
|
||||
|
||||
<applicationService serviceInterface="com.intellij.configurationStore.StoreReloadManager"
|
||||
serviceImplementation="com.intellij.configurationStore.StoreReloadManagerImpl"/>
|
||||
<virtualFileManagerListener implementation="com.intellij.configurationStore.StoreReloadManagerImpl$MyVirtualFileManagerListener"/>
|
||||
<projectService serviceInterface="com.intellij.configurationStore.StoreReloadManager"
|
||||
serviceImplementation="com.intellij.configurationStore.StoreReloadManagerImpl"/>
|
||||
|
||||
<applicationService serviceInterface="com.intellij.ide.lightEdit.LightEditService"
|
||||
serviceImplementation="com.intellij.ide.lightEdit.LightEditServiceImpl"/>
|
||||
|
||||
@@ -51,7 +51,7 @@ public class NonProjectFileAccessTest extends HeavyFileEditorManagerTestCase {
|
||||
EditorNotifications notifications = new EditorNotificationsImpl(project, project.getCoroutineScope());
|
||||
ServiceContainerUtil.replaceService(project, EditorNotifications.class, notifications, getTestRootDisposable());
|
||||
NonProjectFileWritingAccessProvider.enableChecksInTests(project);
|
||||
StoreReloadManager.getInstance().blockReloadingProjectOnExternalChanges();
|
||||
StoreReloadManager.Companion.getInstance(project).blockReloadingProjectOnExternalChanges();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -75,7 +75,7 @@ public class NonProjectFileAccessTest extends HeavyFileEditorManagerTestCase {
|
||||
}
|
||||
finally {
|
||||
super.tearDown();
|
||||
StoreReloadManager.getInstance().unblockReloadingProjectOnExternalChanges(); // unblock only after project is disposed;
|
||||
StoreReloadManager.Companion.getInstance(getProject()).unblockReloadingProjectOnExternalChanges(); // unblock only after project is disposed;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.configurationStore
|
||||
|
||||
import java.io.IOException
|
||||
|
||||
interface SaveSession : StorageManagerFileWriteRequestor {
|
||||
@Throws(IOException::class)
|
||||
fun save()
|
||||
}
|
||||
|
||||
interface SaveSessionProducer : StorageManagerFileWriteRequestor {
|
||||
@Throws(IOException::class)
|
||||
fun setState(component: Any?, componentName: String, state: Any?)
|
||||
|
||||
/**
|
||||
@@ -19,6 +15,6 @@ interface SaveSessionProducer : StorageManagerFileWriteRequestor {
|
||||
}
|
||||
|
||||
/**
|
||||
* A marker interface for [FileUndoProvider] to not process this file change event.
|
||||
* A marker interface for to not process this file change event.
|
||||
*/
|
||||
interface StorageManagerFileWriteRequestor
|
||||
@@ -48,8 +48,5 @@ interface StorageCreator {
|
||||
// better to reduce message bus usage
|
||||
fun isFireStorageFileChangedEvent(event: VFileEvent): Boolean {
|
||||
// ignore VFilePropertyChangeEvent because doesn't affect content
|
||||
return when (event) {
|
||||
is VFilePropertyChangeEvent -> false
|
||||
else -> event.requestor !is StorageManagerFileWriteRequestor
|
||||
}
|
||||
return event !is VFilePropertyChangeEvent && event.requestor !is StorageManagerFileWriteRequestor
|
||||
}
|
||||
@@ -14,7 +14,7 @@ import java.util.function.Consumer
|
||||
// todo rewrite PlatformTestUtil to kotlin
|
||||
internal fun saveProject(project: Project, forceSavingAllSettings: Boolean = false) {
|
||||
runUnderModalProgressIfIsEdt {
|
||||
StoreReloadManager.getInstance().reloadChangedStorageFiles()
|
||||
StoreReloadManager.getInstance(project).reloadChangedStorageFiles()
|
||||
project.stateStore.save(forceSavingAllSettings = forceSavingAllSettings)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,5 +17,5 @@ suspend fun copyFilesAndReloadProject(project: Project, fromDir: Path) {
|
||||
|
||||
FileUtil.copyDir(fromDir.toFile(), base.toFile())
|
||||
VfsUtil.markDirtyAndRefresh(false, true, true, projectDir)
|
||||
StoreReloadManager.getInstance().reloadChangedStorageFiles()
|
||||
StoreReloadManager.getInstance(project).reloadChangedStorageFiles()
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
package com.intellij.util.io
|
||||
|
||||
import com.intellij.openapi.util.io.NioFiles
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
@@ -14,6 +13,7 @@ import java.nio.file.attribute.BasicFileAttributes
|
||||
import java.nio.file.attribute.FileTime
|
||||
import java.util.*
|
||||
import kotlin.io.path.exists
|
||||
import kotlin.io.path.invariantSeparatorsPathString
|
||||
|
||||
@Suppress("DeprecatedCallableAddReplaceWith") // ReplaceWith does not work
|
||||
@Deprecated(message = "Use kotlin.io.path.exists", level = DeprecationLevel.ERROR)
|
||||
@@ -103,7 +103,7 @@ fun Path.deleteChildrenStartingWith(prefix: String) {
|
||||
fun Path.lastModified(): FileTime = Files.getLastModifiedTime(this)
|
||||
|
||||
val Path.systemIndependentPath: String
|
||||
get() = toString().replace(File.separatorChar, '/')
|
||||
get() = invariantSeparatorsPathString
|
||||
|
||||
@Throws(IOException::class)
|
||||
fun Path.readBytes(): ByteArray = Files.readAllBytes(this)
|
||||
|
||||
@@ -58,19 +58,21 @@ public class VcsFreezingProcess {
|
||||
LOG.debug("finished.");
|
||||
}
|
||||
|
||||
private static void saveAndBlockInAwt() {
|
||||
private void saveAndBlockInAwt() {
|
||||
ApplicationManager.getApplication().invokeAndWait(() -> {
|
||||
StoreReloadManager.getInstance().blockReloadingProjectOnExternalChanges();
|
||||
StoreReloadManager.Companion.getInstance(myProject).blockReloadingProjectOnExternalChanges();
|
||||
FileDocumentManager.getInstance().saveAllDocuments();
|
||||
|
||||
SaveAndSyncHandler saveAndSyncHandler = SaveAndSyncHandler.getInstance();
|
||||
saveAndSyncHandler.blockSaveOnFrameDeactivation();
|
||||
saveAndSyncHandler.blockSyncOnFrameActivation();
|
||||
});
|
||||
}
|
||||
|
||||
private static void unblockInAwt() {
|
||||
private void unblockInAwt() {
|
||||
ApplicationManager.getApplication().invokeAndWait(() -> {
|
||||
StoreReloadManager.getInstance().unblockReloadingProjectOnExternalChanges();
|
||||
StoreReloadManager.Companion.getInstance(myProject).unblockReloadingProjectOnExternalChanges();
|
||||
|
||||
SaveAndSyncHandler saveAndSyncHandler = SaveAndSyncHandler.getInstance();
|
||||
saveAndSyncHandler.unblockSaveOnFrameDeactivation();
|
||||
saveAndSyncHandler.unblockSyncOnFrameActivation();
|
||||
|
||||
@@ -60,14 +60,12 @@ import javax.swing.table.AbstractTableModel
|
||||
import javax.swing.tree.DefaultMutableTreeNode
|
||||
import javax.swing.tree.TreeNode
|
||||
|
||||
|
||||
open class MultipleFileMergeDialog(
|
||||
private val project: Project?,
|
||||
files: List<VirtualFile>,
|
||||
private val mergeProvider: MergeProvider,
|
||||
private val mergeDialogCustomizer: MergeDialogCustomizer
|
||||
) : DialogWrapper(project) {
|
||||
|
||||
private var unresolvedFiles = files.toMutableList()
|
||||
private val mergeSession = (mergeProvider as? MergeProvider2)?.createMergeSession(files)
|
||||
val processedFiles: MutableList<VirtualFile> = mutableListOf()
|
||||
@@ -95,7 +93,7 @@ open class MultipleFileMergeDialog(
|
||||
}
|
||||
|
||||
init {
|
||||
StoreReloadManager.getInstance().blockReloadingProjectOnExternalChanges()
|
||||
project?.let { StoreReloadManager.getInstance(project).blockReloadingProjectOnExternalChanges() }
|
||||
title = mergeDialogCustomizer.getMultipleFileDialogTitle()
|
||||
virtualFileRenderer.font = UIUtil.getListFont()
|
||||
|
||||
@@ -169,7 +167,7 @@ open class MultipleFileMergeDialog(
|
||||
showMergeDialog()
|
||||
}
|
||||
}
|
||||
mergeAction.putValue(DEFAULT_ACTION, java.lang.Boolean.TRUE)
|
||||
mergeAction.putValue(DEFAULT_ACTION, true)
|
||||
mergeButton = createJButtonForAction(mergeAction)
|
||||
cell(mergeButton)
|
||||
.align(AlignX.FILL)
|
||||
@@ -261,7 +259,7 @@ open class MultipleFileMergeDialog(
|
||||
}
|
||||
|
||||
override fun dispose() {
|
||||
StoreReloadManager.getInstance().unblockReloadingProjectOnExternalChanges()
|
||||
project?.let { StoreReloadManager.getInstance(project).unblockReloadingProjectOnExternalChanges() }
|
||||
super.dispose()
|
||||
}
|
||||
|
||||
|
||||
@@ -339,7 +339,9 @@ public abstract class AbstractCommonUpdateAction extends AbstractVcsAction {
|
||||
}
|
||||
|
||||
private void runImpl() {
|
||||
StoreReloadManager.getInstance().blockReloadingProjectOnExternalChanges();
|
||||
if (myProject != null) {
|
||||
StoreReloadManager.Companion.getInstance(myProject).blockReloadingProjectOnExternalChanges();
|
||||
}
|
||||
myProjectLevelVcsManager.startBackgroundVcsOperation();
|
||||
|
||||
myBefore = LocalHistory.getInstance().putSystemLabel(myProject, VcsBundle.message("update.label.before.update"));
|
||||
@@ -497,7 +499,7 @@ public abstract class AbstractCommonUpdateAction extends AbstractVcsAction {
|
||||
|
||||
private void onSuccessImpl(final boolean wasCanceled) {
|
||||
if (!myProject.isOpen() || myProject.isDisposed()) {
|
||||
StoreReloadManager.getInstance().unblockReloadingProjectOnExternalChanges();
|
||||
StoreReloadManager.Companion.getInstance(myProject).unblockReloadingProjectOnExternalChanges();
|
||||
LocalHistory.getInstance().putSystemLabel(myProject, VcsBundle.message("local.history.update.from.vcs")); // TODO check why this label is needed
|
||||
return;
|
||||
}
|
||||
@@ -538,7 +540,7 @@ public abstract class AbstractCommonUpdateAction extends AbstractVcsAction {
|
||||
final boolean updateSuccess = !someSessionWasCancelled && myGroupedExceptions.isEmpty();
|
||||
|
||||
if (myProject.isDisposed()) {
|
||||
StoreReloadManager.getInstance().unblockReloadingProjectOnExternalChanges();
|
||||
StoreReloadManager.Companion.getInstance(myProject).unblockReloadingProjectOnExternalChanges();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -589,7 +591,7 @@ public abstract class AbstractCommonUpdateAction extends AbstractVcsAction {
|
||||
}
|
||||
|
||||
|
||||
StoreReloadManager.getInstance().unblockReloadingProjectOnExternalChanges();
|
||||
StoreReloadManager.Companion.getInstance(myProject).unblockReloadingProjectOnExternalChanges();
|
||||
|
||||
if (continueChainFinal && updateSuccess) {
|
||||
if (!noMerged) {
|
||||
|
||||
@@ -6,6 +6,7 @@ import com.intellij.configurationStore.*
|
||||
import com.intellij.openapi.components.BaseState
|
||||
import com.intellij.openapi.components.PersistentStateComponent
|
||||
import com.intellij.openapi.components.StateStorage
|
||||
import com.intellij.openapi.components.stateStore
|
||||
import com.intellij.openapi.diagnostic.logger
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.util.ReflectionUtil
|
||||
@@ -22,7 +23,7 @@ private class MyProjectStore(project: Project) : ProjectWithModulesStoreImpl(pro
|
||||
|
||||
fun configurationFileChanged() {
|
||||
if (storages.isNotEmpty()) {
|
||||
StoreReloadManager.getInstance().storageFilesChanged(mapOf(project to storages.values.toList()))
|
||||
StoreReloadManager.getInstance(project).storageFilesChanged(project.stateStore, storages.values.toList())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -72,9 +72,9 @@ private fun deleteRepository(icsManager: IcsManager) {
|
||||
runBlockingModalWithRawProgressReporter(ModalTaskOwner.guess(), IcsBundle.message("progress.syncing.before.deleting.repository")) {
|
||||
val repositoryManager = icsManager.repositoryManager
|
||||
|
||||
// attempt to fetch, merge and push to ensure that latest changes in the deleted user repository will be not lost
|
||||
// Attempt to fetch, merge and push to ensure that the latest changes in the deleted user repository will be not lost
|
||||
// yes, - delete repository doesn't mean "AAA, delete it, delete". It means just that user doesn't need it at this moment.
|
||||
// It is user responsibility later to delete git repository or do whatever user want. Our responsibility is to not loose user changes.
|
||||
// It is user responsibility later to delete git repository or do whatever user wants. Our responsibility is to not lose user changes.
|
||||
if (!repositoryManager.canCommit()) {
|
||||
LOG.info("Commit on repository delete skipped: repository is not committable")
|
||||
return@runBlockingModalWithRawProgressReporter
|
||||
@@ -83,7 +83,7 @@ private fun deleteRepository(icsManager: IcsManager) {
|
||||
catchAndLog(asWarning = true) {
|
||||
val updater = repositoryManager.fetch()
|
||||
ensureActive()
|
||||
// ignore result, we don't need to apply it
|
||||
// ignore a result, we don't need to apply it
|
||||
updater.merge()
|
||||
ensureActive()
|
||||
if (!updater.definitelySkipPush) {
|
||||
|
||||
@@ -95,7 +95,9 @@ public class SvnIntegrateChangesTask extends Task.Backgroundable {
|
||||
myHandler.setProgressIndicator(ProgressManager.getInstance().getProgressIndicator());
|
||||
myResolveWorker = new ResolveWorker(myInfo.isUnderProjectRoot(), myProject);
|
||||
|
||||
StoreReloadManager.getInstance().blockReloadingProjectOnExternalChanges();
|
||||
if (myProject != null) {
|
||||
StoreReloadManager.Companion.getInstance(myProject).blockReloadingProjectOnExternalChanges();
|
||||
}
|
||||
myProjectLevelVcsManager.startBackgroundVcsOperation();
|
||||
|
||||
try {
|
||||
@@ -158,7 +160,9 @@ public class SvnIntegrateChangesTask extends Task.Backgroundable {
|
||||
afterExecution(wasCancelled);
|
||||
}
|
||||
finally {
|
||||
StoreReloadManager.getInstance().unblockReloadingProjectOnExternalChanges();
|
||||
if (myProject != null) {
|
||||
StoreReloadManager.Companion.getInstance(myProject).unblockReloadingProjectOnExternalChanges();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user