IDEA-352824 Support Multiple Projects for Maven and Gradle

general refactoring:
dropping "new workspace" import parameter
running import sequentially after project opening (fix for maven import concurrency issues)
setting first imported project jdk

GitOrigin-RevId: 82a8e6b86eff3fdf98dbfdc94693cedfad0000c6
This commit is contained in:
Dmitry Avdeev
2024-05-23 20:45:03 +02:00
committed by intellij-monorepo-bot
parent 675ab4a296
commit 3048214973
8 changed files with 39 additions and 53 deletions

View File

@@ -688,7 +688,7 @@ com.intellij.ide.workspace.ImportedProjectSettings
- a:canImportFromFile(com.intellij.openapi.vfs.VirtualFile):Z
- a:getSubprojectIcon():javax.swing.Icon
- a:getSubprojects(com.intellij.openapi.project.Project):java.util.List
- a:importFromProject(com.intellij.openapi.project.Project,Z):com.intellij.ide.workspace.ImportedProjectSettings
- a:importFromProject(com.intellij.openapi.project.Project):com.intellij.ide.workspace.ImportedProjectSettings
- a:removeSubprojects(java.util.List):V
- suppressGenericImportFor(com.intellij.openapi.module.Module):Z
*f:com.intellij.ide.workspace.SubprojectHandler$Companion
@@ -696,7 +696,7 @@ com.intellij.ide.workspace.ImportedProjectSettings
- f:getEP_NAME():com.intellij.openapi.extensions.ExtensionPointName
com.intellij.ide.workspace.WorkspaceSettingsImporter
- sf:Companion:com.intellij.ide.workspace.WorkspaceSettingsImporter$Companion
- a:importFromProject(com.intellij.openapi.project.Project,Z):com.intellij.ide.workspace.ImportedProjectSettings
- a:importFromProject(com.intellij.openapi.project.Project):com.intellij.ide.workspace.ImportedProjectSettings
f:com.intellij.ide.workspace.WorkspaceSettingsImporter$Companion
- f:getEP_NAME():com.intellij.openapi.extensions.ExtensionPointName
f:com.intellij.navigation.NavigationItemFileStatus

View File

@@ -18,7 +18,7 @@ interface SubprojectHandler {
fun getSubprojects(project: Project): List<Subproject>
fun canImportFromFile(file: VirtualFile): Boolean
fun removeSubprojects(subprojects: List<Subproject>)
fun importFromProject(project: Project, newWorkspace: Boolean): ImportedProjectSettings?
fun importFromProject(project: Project): ImportedProjectSettings?
fun suppressGenericImportFor(module: Module): Boolean = false
@@ -30,7 +30,7 @@ interface WorkspaceSettingsImporter {
val EP_NAME: ExtensionPointName<WorkspaceSettingsImporter> = ExtensionPointName.create("com.intellij.workspace.settingsImporter")
}
fun importFromProject(project: Project, newWorkspace: Boolean): ImportedProjectSettings?
fun importFromProject(project: Project): ImportedProjectSettings?
}
interface ImportedProjectSettings {

View File

@@ -5,6 +5,7 @@ import com.intellij.ide.impl.OpenProjectTask
import com.intellij.ide.impl.TrustedPaths
import com.intellij.openapi.actionSystem.ActionUpdateThread
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.application.EDT
import com.intellij.openapi.components.ComponentManagerEx
import com.intellij.openapi.progress.blockingContext
@@ -13,9 +14,13 @@ import com.intellij.openapi.project.Project
import com.intellij.openapi.project.ProjectManager
import com.intellij.openapi.project.ex.ProjectManagerEx
import com.intellij.openapi.project.impl.ProjectManagerImpl
import com.intellij.openapi.startup.StartupManager
import com.intellij.util.concurrency.annotations.RequiresEdt
import com.intellij.util.containers.addIfNotNull
import kotlinx.coroutines.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.cancelAndJoin
import kotlinx.coroutines.job
import kotlinx.coroutines.withContext
import java.nio.file.Files
import java.nio.file.Path
@@ -38,33 +43,30 @@ internal open class CreateWorkspaceAction: BaseWorkspaceAction(false) {
}
@RequiresEdt
internal fun createWorkspace(project: Project): Boolean {
internal fun createWorkspace(project: Project) {
val subprojects = SubprojectHandler.getAllSubprojects(project).associateBy { it.projectPath }
val dialog = NewWorkspaceDialog(project, subprojects.values, true)
if (!dialog.showAndGet()) return false
if (!dialog.showAndGet()) return
val settings = importSettingsFromProject(project, true)
getCoroutineScope(project).launch {
createAndOpenWorkspaceProject(project, dialog.projectPath, dialog.projectName) { workspace ->
for (importedSetting in settings) {
importedSetting.applyTo(workspace)
}
dialog.projectPaths.forEach { linkToWorkspace(workspace, it) }
ApplicationManager.getApplication().executeOnPooledThread {
val workspace = createAndOpenWorkspaceProject(project, dialog.projectPath, dialog.projectName)
?: return@executeOnPooledThread
StartupManager.getInstance(workspace).runAfterOpened {
addToWorkspace(workspace, dialog.projectPaths)
}
}
return true
}
private fun importSettingsFromProject(project: Project, newWorkspace: Boolean): List<ImportedProjectSettings> {
private fun importSettingsFromProject(project: Project): List<ImportedProjectSettings> {
val settings = mutableListOf<ImportedProjectSettings>()
val handlers = SubprojectHandler.EP_NAME.extensionList
for (handler in handlers) {
settings.addIfNotNull(handler.importFromProject(project, newWorkspace))
settings.addIfNotNull(handler.importFromProject(project))
}
val importers = WorkspaceSettingsImporter.EP_NAME.extensionList
for (importer in importers) {
settings.addIfNotNull(importer.importFromProject(project, newWorkspace))
settings.addIfNotNull(importer.importFromProject(project))
}
return settings
}
@@ -73,7 +75,7 @@ internal suspend fun linkToWorkspace(workspace: Project, projectPath: String) {
val projectManagerImpl = blockingContext { ProjectManager.getInstance() as ProjectManagerImpl }
val referentProject = blockingContext { projectManagerImpl.loadProject(Path.of(projectPath), false, false) }
try {
val settings = importSettingsFromProject(referentProject, false)
val settings = importSettingsFromProject(referentProject)
for (importedSettings in settings) {
importedSettings.applyTo(workspace)
}
@@ -88,8 +90,7 @@ internal suspend fun linkToWorkspace(workspace: Project, projectPath: String) {
private fun createAndOpenWorkspaceProject(project: Project,
workspacePath: Path,
projectName: String?,
initTask: suspend (workspace: Project) -> Unit) {
projectName: String): Project? {
val options = OpenProjectTask {
projectToClose = project
this.projectName = projectName
@@ -99,11 +100,10 @@ private fun createAndOpenWorkspaceProject(project: Project,
isRefreshVfsNeeded = true
beforeOpen = { workspace ->
setWorkspace(workspace)
initTask(workspace)
true
}
}
Files.createDirectories(workspacePath)
TrustedPaths.getInstance().setProjectPathTrusted(workspacePath, true)
ProjectManagerEx.getInstanceEx().openProject(workspacePath, options)
return ProjectManagerEx.getInstanceEx().openProject(workspacePath, options)
}

View File

@@ -7,13 +7,8 @@ import com.intellij.openapi.projectRoots.Sdk
import com.intellij.openapi.roots.ProjectRootManager
internal class DefaultWorkspaceSettingsImporter : WorkspaceSettingsImporter {
override fun importFromProject(project: Project, newWorkspace: Boolean): ImportedProjectSettings? {
if (newWorkspace) {
return DefaultImportedProjectSettings(project)
}
else {
return null
}
override fun importFromProject(project: Project): ImportedProjectSettings {
return DefaultImportedProjectSettings(project)
}
}
@@ -30,7 +25,7 @@ private class DefaultImportedProjectSettings(project: Project) : ImportedProject
}
override suspend fun applyTo(workspace: Project) {
if (projectSdk != null) {
if (projectSdk != null && ProjectRootManager.getInstance(workspace).projectSdk == null) {
writeAction {
ProjectRootManager.getInstance(workspace).projectSdk = projectSdk
}

View File

@@ -11,15 +11,13 @@ import kotlin.io.path.pathString
internal class WorkspaceAttachProcessor : ProjectAttachProcessor() {
override fun attachToProject(project: Project, projectDir: Path, callback: ProjectOpenedCallback?): Boolean {
if (project.isWorkspace) {
getCoroutineScope(project).launch {
linkToWorkspace(project, projectDir.pathString)
}
return true
if (!project.isWorkspace) {
return false
}
else {
return createWorkspace(project)
getCoroutineScope(project).launch {
linkToWorkspace(project, projectDir.pathString)
}
return true
}
override fun isEnabled(project: Project?, path: Path?): Boolean =

View File

@@ -19,10 +19,10 @@ internal fun getCoroutineScope(workspace: Project) = workspace.service<MyCorouti
internal fun getHandlers(file: VirtualFile): List<SubprojectHandler> =
SubprojectHandler.EP_NAME.extensionList.filter { it.canImportFromFile(file) }
internal fun addToWorkspace(project: Project, projectPaths: List<String>) {
getCoroutineScope(project).launch {
internal fun addToWorkspace(workspace: Project, projectPaths: List<String>) {
getCoroutineScope(workspace).launch {
projectPaths.forEach { s ->
linkToWorkspace(project, s)
linkToWorkspace(workspace, s)
}
}
}

View File

@@ -26,7 +26,7 @@ internal class GradleSubprojectHandler : ExternalSubprojectHandler(GradleConstan
return canOpenGradleProject(file)
}
override fun importFromProject(project: Project, newWorkspace: Boolean): ImportedProjectSettings = GradleImportedProjectSettings(project)
override fun importFromProject(project: Project): ImportedProjectSettings = GradleImportedProjectSettings(project)
override fun suppressGenericImportFor(module: Module): Boolean {
return ExternalSystemModulePropertyManager.getInstance(module).getExternalSystemId() == GradleConstants.SYSTEM_ID.id

View File

@@ -7,13 +7,10 @@ import com.intellij.ide.workspace.SubprojectHandler
import com.intellij.openapi.module.Module
import com.intellij.openapi.project.Project
import com.intellij.openapi.project.guessProjectDir
import com.intellij.openapi.startup.StartupManager
import com.intellij.openapi.vfs.VirtualFile
import icons.MavenIcons
import kotlinx.coroutines.launch
import org.jetbrains.idea.maven.project.MavenProject
import org.jetbrains.idea.maven.project.MavenProjectsManager
import org.jetbrains.idea.maven.utils.MavenCoroutineScopeProvider
import org.jetbrains.idea.maven.utils.MavenUtil
import org.jetbrains.idea.maven.wizards.MavenOpenProjectProvider
import javax.swing.Icon
@@ -34,7 +31,7 @@ internal class MavenSubprojectHandler : SubprojectHandler {
MavenProjectsManager.getInstance(workspace).removeManagedFiles(files, null, null)
}
override fun importFromProject(project: Project, newWorkspace: Boolean): ImportedProjectSettings {
override fun importFromProject(project: Project): ImportedProjectSettings {
// FIXME: does not work for new project: AbstractMavenModuleBuilder creates project in 'MavenUtil.runWhenInitialized' callback
return MavenImportedProjectSettings(project)
}
@@ -48,16 +45,12 @@ internal class MavenSubprojectHandler : SubprojectHandler {
}
private class MavenImportedProjectSettings(project: Project) : ImportedProjectSettings {
val projectDir = project.guessProjectDir()
val projectDir = requireNotNull(project.guessProjectDir())
override suspend fun applyTo(workspace: Project) {
val openProjectProvider = MavenOpenProjectProvider()
if (openProjectProvider.canOpenProject(projectDir!!)) {
StartupManager.getInstance(workspace).runAfterOpened {
MavenCoroutineScopeProvider.getCoroutineScope(workspace).launch {
openProjectProvider.forceLinkToExistingProjectAsync(projectDir, workspace)
}
}
if (openProjectProvider.canOpenProject(projectDir)) {
openProjectProvider.forceLinkToExistingProjectAsync(projectDir, workspace)
}
}
}