[externalSystem] SourceFolderManagerImpl is a project service

GitOrigin-RevId: d85162ebcfdab24bfdddd671d1e94e9cd8695ad7
This commit is contained in:
Andrew Kozlov
2022-09-28 18:05:30 +02:00
committed by intellij-monorepo-bot
parent 63f819bc5b
commit 117f72d8a2
5 changed files with 91 additions and 56 deletions

View File

@@ -11,8 +11,8 @@ import org.jetbrains.jps.model.module.JpsModuleSourceRootType;
@ApiStatus.Experimental
public interface SourceFolderManager {
static SourceFolderManager getInstance(@NotNull Project project) {
return project.getComponent(SourceFolderManager.class);
static @NotNull SourceFolderManager getInstance(@NotNull Project project) {
return project.getService(SourceFolderManager.class);
}
void setSourceFolderPackagePrefix(@NotNull String url, @Nullable String packagePrefix);
@@ -22,4 +22,7 @@ public interface SourceFolderManager {
void addSourceFolder(@NotNull Module module, @NotNull String url, @NotNull JpsModuleSourceRootType<?> type);
void removeSourceFolders(@NotNull Module module);
@ApiStatus.Internal
void rescanAndUpdateSourceFolders();
}

View File

@@ -2,10 +2,4 @@
<xi:include href="ExternalSystemExtensionPoints.xml"/>
<xi:include href="ExternalSystemExtensions.xml"/>
<xi:include href="ExternalSystemActions.xml"/>
<project-components>
<component>
<interface-class>com.intellij.openapi.externalSystem.service.project.manage.SourceFolderManager</interface-class>
<implementation-class>com.intellij.openapi.externalSystem.service.project.manage.SourceFolderManagerImpl</implementation-class>
</component>
</project-components>
</idea-plugin>

View File

@@ -14,7 +14,7 @@
implementation="com.intellij.openapi.externalSystem.service.notification.ExternalSystemNotificationExtensionImpl"/>
<module.workingDirectoryProvider
implementation="com.intellij.openapi.externalSystem.service.project.ExternalSystemWorkingDirectoryProvider"/>
implementation="com.intellij.openapi.externalSystem.service.project.ExternalSystemWorkingDirectoryProvider"/>
<applicationService serviceInterface="com.intellij.openapi.externalSystem.util.environment.Environment"
serviceImplementation="com.intellij.openapi.externalSystem.util.environment.SystemEnvironment"/>
@@ -23,6 +23,9 @@
<search.optionContributor
implementation="com.intellij.openapi.externalSystem.service.settings.ExternalSystemGroupSearchableContributor"/>
<projectService serviceInterface="com.intellij.openapi.externalSystem.service.project.manage.SourceFolderManager"
serviceImplementation="com.intellij.openapi.externalSystem.service.project.manage.SourceFolderManagerImpl"/>
<!--Auto-reload-->
<projectService serviceInterface="com.intellij.openapi.externalSystem.autoimport.ExternalSystemProjectTracker"
serviceImplementation="com.intellij.openapi.externalSystem.autoimport.AutoImportProjectTracker"/>
@@ -136,6 +139,12 @@
<listener class="com.intellij.openapi.externalSystem.service.project.ExternalSystemTrustedListener"
topic="com.intellij.ide.impl.TrustStateListener"
activeInHeadlessMode="false" activeInTestMode="false"/>
<listener class="com.intellij.openapi.externalSystem.service.project.manage.SourceFolderManagerImpl$BulkFileListenerImpl"
topic="com.intellij.openapi.vfs.newvfs.BulkFileListener"
activeInHeadlessMode="true" activeInTestMode="true"/>
<listener class="com.intellij.openapi.externalSystem.service.project.manage.SourceFolderManagerImpl$ModuleListenerImpl"
topic="com.intellij.openapi.project.ModuleListener"
activeInHeadlessMode="true" activeInTestMode="true"/>
</applicationListeners>
</idea-plugin>

View File

@@ -3,21 +3,25 @@ package com.intellij.openapi.externalSystem.service.project.manage
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.diagnostic.thisLogger
import com.intellij.openapi.extensions.ExtensionNotApplicableException
import com.intellij.openapi.externalSystem.util.ExternalSystemUtil
import com.intellij.openapi.project.Project
import com.intellij.openapi.startup.ProjectPostStartupActivity
private class ReprocessContentRootDataActivity : ProjectPostStartupActivity {
override suspend fun execute(project: Project) {
init {
if (ApplicationManager.getApplication().isUnitTestMode) {
return
throw ExtensionNotApplicableException.create()
}
}
override suspend fun execute(project: Project) {
if (ExternalSystemUtil.isNewProject(project)) {
thisLogger().info("Ignored reprocess of content root data service for new projects")
return
}
val instance = SourceFolderManager.getInstance(project) as SourceFolderManagerImpl
instance.rescanAndUpdateSourceFolders()
SourceFolderManager.getInstance(project).rescanAndUpdateSourceFolders()
}
}

View File

@@ -1,7 +1,6 @@
// 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.openapi.externalSystem.service.project.manage
import com.intellij.ProjectTopics
import com.intellij.ide.projectView.actions.MarkRootActionBase
import com.intellij.openapi.Disposable
import com.intellij.openapi.application.ApplicationManager
@@ -15,6 +14,7 @@ import com.intellij.openapi.module.Module
import com.intellij.openapi.module.ModuleManager
import com.intellij.openapi.project.ModuleListener
import com.intellij.openapi.project.Project
import com.intellij.openapi.project.ProjectManager
import com.intellij.openapi.roots.ModifiableRootModel
import com.intellij.openapi.roots.ModuleRootManager
import com.intellij.openapi.roots.SourceFolder
@@ -44,8 +44,11 @@ import org.jetbrains.jps.model.java.JavaSourceRootType
import org.jetbrains.jps.model.module.JpsModuleSourceRootType
import java.util.concurrent.Future
@State(name = "sourceFolderManager", storages = [Storage(StoragePathMacros.CACHE_FILE)])
class SourceFolderManagerImpl(private val project: Project) : SourceFolderManager, Disposable, PersistentStateComponent<SourceFolderManagerState> {
@State(name = "sourceFolderManager", storages = [Storage(StoragePathMacros.CACHE_FILE)])
class SourceFolderManagerImpl(private val project: Project) : SourceFolderManager,
PersistentStateComponent<SourceFolderManagerState>,
Disposable {
private val moduleNamesToSourceFolderState: MultiMap<String, SourceFolderModelState> = MultiMap.create()
private var isDisposed = false
private val mutex = Any()
@@ -124,51 +127,69 @@ class SourceFolderManagerImpl(private val project: Project) : SourceFolderManage
val sourceFolders: MutableSet<String> = CollectionFactory.createFilePathSet()
)
init {
project.messageBus.connect().subscribe(VirtualFileManager.VFS_CHANGES, object : BulkFileListener {
override fun after(events: List<VFileEvent>) {
val sourceFoldersToChange = HashMap<Module, ArrayList<Pair<VirtualFile, SourceFolderModel>>>()
val virtualFileManager = VirtualFileManager.getInstance()
@Suppress("unused") // todo fix warning
private class BulkFileListenerImpl : BulkFileListener {
for (event in events) {
if (event !is VFileCreateEvent) {
continue
}
val allDescendantValues = synchronized(mutex) { sourceFolders.getAllDescendantValues(VfsUtilCore.pathToUrl(event.path)) }
for (sourceFolder in allDescendantValues) {
val sourceFolderFile = virtualFileManager.refreshAndFindFileByUrl(sourceFolder.url)
if (sourceFolderFile != null && sourceFolderFile.isValid) {
sourceFoldersToChange.computeIfAbsent(sourceFolder.module) { ArrayList() }.add(Pair(event.file!!, sourceFolder))
removeSourceFolder(sourceFolder.url)
}
}
}
override fun after(events: List<VFileEvent>) {
val fileCreateEvents = events.filterIsInstance<VFileCreateEvent>()
if (fileCreateEvents.isEmpty()) return
val application = ApplicationManager.getApplication()
val future = project.coroutineScope.async { updateSourceFolders(sourceFoldersToChange) }.asCompletableFuture()
if (application.isUnitTestMode) {
ApplicationManager.getApplication().assertIsDispatchThread()
operationsStates.removeIf { it.isDone }
operationsStates.add(future)
}
for (project in ProjectManager.getInstance().openProjects) {
(SourceFolderManager.getInstance(project) as SourceFolderManagerImpl).filesCreated(fileCreateEvents)
}
})
project.messageBus.connect().subscribe(ProjectTopics.MODULES, object : ModuleListener {
override fun modulesAdded(project: Project, modules: List<Module>) {
synchronized(mutex) {
for (module in modules) {
moduleNamesToSourceFolderState[module.name].forEach {
loadSourceFolderState(it, module)
}
moduleNamesToSourceFolderState.remove(module.name)
}
}
}
})
}
}
fun rescanAndUpdateSourceFolders() {
@Suppress("unused") // todo fix warning
private class ModuleListenerImpl : ModuleListener {
override fun modulesAdded(project: Project, modules: List<Module>) {
(SourceFolderManager.getInstance(project) as SourceFolderManagerImpl).modulesAdded(modules)
}
}
private fun filesCreated(fileCreateEvents: List<VFileCreateEvent>) {
val sourceFoldersToChange = mutableMapOf<Module, ArrayList<Pair<VirtualFile, SourceFolderModel>>>()
val virtualFileManager = VirtualFileManager.getInstance()
for (event in fileCreateEvents) {
val allDescendantValues = synchronized(mutex) {
sourceFolders.getAllDescendantValues(VfsUtilCore.pathToUrl(event.path))
}
for (sourceFolder in allDescendantValues) {
val sourceFolderFile = virtualFileManager.refreshAndFindFileByUrl(sourceFolder.url)
if (sourceFolderFile != null && sourceFolderFile.isValid) {
sourceFoldersToChange.computeIfAbsent(sourceFolder.module) { ArrayList() }.add(Pair(event.file!!, sourceFolder))
removeSourceFolder(sourceFolder.url)
}
}
}
val application = ApplicationManager.getApplication()
val future = project.coroutineScope.async {
updateSourceFolders(sourceFoldersToChange)
}.asCompletableFuture()
if (application.isUnitTestMode) {
ApplicationManager.getApplication().assertIsDispatchThread()
operationsStates.removeIf { it.isDone }
operationsStates.add(future)
}
}
private fun modulesAdded(modules: List<Module>) {
synchronized(mutex) {
for (module in modules) {
moduleNamesToSourceFolderState.remove(module.name)!!.forEach {
loadSourceFolderState(it, module)
}
}
}
}
override fun rescanAndUpdateSourceFolders() {
val sourceFoldersToChange = HashMap<Module, ArrayList<Pair<VirtualFile, SourceFolderModel>>>()
val virtualFileManager = VirtualFileManager.getInstance()
@@ -254,6 +275,10 @@ class SourceFolderManagerImpl(private val project: Project) : SourceFolderManage
sourceFolders = PathPrefixTreeMap()
sourceFoldersByModule = HashMap()
if (state.sourceFolders.isEmpty()) {
return
}
val moduleManager = ModuleManager.getInstance(project)
state.sourceFolders.forEach { model ->