mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-03-22 15:19:59 +07:00
IJPL-162718 wizard: non-blocking project creation
GitOrigin-RevId: 84810b3acdc12c703a255a86355d2d57c8a47913
This commit is contained in:
committed by
intellij-monorepo-bot
parent
86b2d9ecc9
commit
17b5c9688b
@@ -12,13 +12,19 @@ import com.intellij.openapi.actionSystem.ActionUpdateThread
|
||||
import com.intellij.openapi.actionSystem.AnAction
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent
|
||||
import com.intellij.openapi.application.EDT
|
||||
import com.intellij.openapi.progress.currentThreadCoroutineScope
|
||||
import com.intellij.openapi.components.Service
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.openapi.project.DumbAware
|
||||
import com.intellij.openapi.roots.ui.configuration.ModulesProvider
|
||||
import com.intellij.openapi.wm.impl.welcomeScreen.NewWelcomeScreen
|
||||
import com.intellij.ui.ExperimentalUI
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
@Service()
|
||||
private class NewProjectActionCoroutineScopeHolder(@JvmField val coroutineScope: CoroutineScope)
|
||||
|
||||
open class NewProjectAction : AnAction(), DumbAware, NewProjectOrModuleAction {
|
||||
init {
|
||||
@@ -42,8 +48,10 @@ open class NewProjectAction : AnAction(), DumbAware, NewProjectOrModuleAction {
|
||||
}
|
||||
|
||||
override fun actionPerformed(e: AnActionEvent) {
|
||||
currentThreadCoroutineScope().launch(Dispatchers.EDT) {
|
||||
val wizard = NewProjectWizard(null, ModulesProvider.EMPTY_MODULES_PROVIDER, null)
|
||||
service<NewProjectActionCoroutineScopeHolder>().coroutineScope.launch {
|
||||
val wizard = withContext(Dispatchers.EDT) {
|
||||
NewProjectWizard(null, ModulesProvider.EMPTY_MODULES_PROVIDER, null)
|
||||
}
|
||||
createNewProjectAsync(wizard)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.ide.impl
|
||||
|
||||
import com.intellij.configurationStore.runInAutoSaveDisabledMode
|
||||
import com.intellij.configurationStore.saveSettings
|
||||
import com.intellij.ide.JavaUiBundle
|
||||
import com.intellij.ide.SaveAndSyncHandler
|
||||
import com.intellij.ide.impl.NewProjectUtil.createFromWizard
|
||||
import com.intellij.ide.impl.NewProjectUtil.setCompilerOutputPath
|
||||
import com.intellij.ide.impl.OpenProjectTask.Companion.build
|
||||
import com.intellij.ide.impl.ProjectUtil.focusProjectWindow
|
||||
import com.intellij.ide.impl.ProjectUtil.isSameProject
|
||||
import com.intellij.ide.impl.ProjectUtil.updateLastProjectLocation
|
||||
import com.intellij.ide.projectWizard.NewProjectWizardCollector
|
||||
import com.intellij.ide.util.newProjectWizard.AbstractProjectWizard
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.application.EDT
|
||||
import com.intellij.openapi.application.ModalityState
|
||||
import com.intellij.openapi.application.backgroundWriteAction
|
||||
import com.intellij.openapi.command.CommandProcessor
|
||||
import com.intellij.openapi.components.StorageScheme
|
||||
import com.intellij.openapi.components.serviceAsync
|
||||
@@ -38,6 +38,10 @@ import com.intellij.projectImport.ProjectOpenedCallback
|
||||
import com.intellij.ui.AppUIUtil
|
||||
import com.intellij.ui.IdeUICustomization
|
||||
import com.intellij.util.TimeoutUtil
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.jetbrains.annotations.ApiStatus.Internal
|
||||
import org.jetbrains.annotations.VisibleForTesting
|
||||
import java.io.IOException
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
@@ -46,20 +50,29 @@ import java.util.concurrent.CancellationException
|
||||
private val LOG = logger<NewProjectUtil>()
|
||||
|
||||
@Suppress("DuplicatedCode")
|
||||
internal suspend fun createNewProjectAsync(wizard: AbstractProjectWizard) {
|
||||
@Internal
|
||||
suspend fun createNewProjectAsync(wizard: AbstractProjectWizard) {
|
||||
// warm-up components
|
||||
serviceAsync<ProjectManager>().defaultProject
|
||||
|
||||
val context = wizard.wizardContext
|
||||
val time = System.nanoTime()
|
||||
NewProjectWizardCollector.logOpen(context)
|
||||
if (wizard.showAndGet()) {
|
||||
createFromWizard(wizard)
|
||||
NewProjectWizardCollector.logFinish(context, true, TimeoutUtil.getDurationMillis(time))
|
||||
}
|
||||
else {
|
||||
if (!withContext(Dispatchers.EDT) { wizard.showAndGet() }) {
|
||||
NewProjectWizardCollector.logFinish(context, false, TimeoutUtil.getDurationMillis(time))
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
val projectFile = Path.of(wizard.newProjectFilePath)
|
||||
val newProject = createProjectFromWizardImpl(wizard = wizard, projectFile = projectFile, projectToClose = null)
|
||||
NewProjectWizardCollector.logProjectCreated(newProject, wizard.wizardContext)
|
||||
}
|
||||
catch (e: IOException) {
|
||||
AppUIUtil.invokeOnEdt { Messages.showErrorDialog(e.message, JavaUiBundle.message("dialog.title.project.initialization.failed")) }
|
||||
return
|
||||
}
|
||||
NewProjectWizardCollector.logFinish(context, true, TimeoutUtil.getDurationMillis(time))
|
||||
}
|
||||
|
||||
object NewProjectUtil {
|
||||
@@ -67,11 +80,13 @@ object NewProjectUtil {
|
||||
@Deprecated("Use {@link #createNewProject(AbstractProjectWizard)}, projectToClose param is not used.",
|
||||
ReplaceWith("createNewProject(wizard)", "com.intellij.ide.impl.NewProjectUtil.createNewProject"))
|
||||
fun createNewProject(@Suppress("unused") projectToClose: Project?, wizard: AbstractProjectWizard) {
|
||||
@Suppress("DEPRECATION")
|
||||
createNewProject(wizard)
|
||||
}
|
||||
|
||||
@Suppress("DuplicatedCode")
|
||||
@JvmStatic
|
||||
@Deprecated("Use {@link #createNewProjectAsync(AbstractProjectWizard, Project)}")
|
||||
fun createNewProject(wizard: AbstractProjectWizard) {
|
||||
// warm-up components
|
||||
ProjectManager.getInstance().defaultProject
|
||||
@@ -91,7 +106,13 @@ object NewProjectUtil {
|
||||
@JvmStatic
|
||||
fun createFromWizard(wizard: AbstractProjectWizard, projectToClose: Project? = null): Project? {
|
||||
try {
|
||||
val newProject = doCreate(wizard, projectToClose)
|
||||
val projectFile = Path.of(wizard.newProjectFilePath)
|
||||
val newProject = runWithModalProgressBlocking(
|
||||
owner = ModalTaskOwner.guess(),
|
||||
title = IdeUICustomization.getInstance().projectMessage("progress.title.project.loading.name", projectFile.fileName.toString()),
|
||||
) {
|
||||
createProjectFromWizardImpl(wizard = wizard, projectFile = projectFile, projectToClose = projectToClose)
|
||||
}
|
||||
NewProjectWizardCollector.logProjectCreated(newProject, wizard.wizardContext)
|
||||
return newProject
|
||||
}
|
||||
@@ -125,14 +146,12 @@ object NewProjectUtil {
|
||||
}
|
||||
}
|
||||
|
||||
private fun doCreate(wizard: AbstractProjectWizard, projectToClose: Project?): Project? {
|
||||
val projectFilePath = wizard.newProjectFilePath
|
||||
val projectManager = ProjectManagerEx.getInstanceEx()
|
||||
|
||||
val projectFile = Path.of(projectFilePath)
|
||||
|
||||
for (p in ProjectUtilCore.getOpenProjects()) {
|
||||
if (isSameProject(projectFile, p)) {
|
||||
@VisibleForTesting
|
||||
@Internal
|
||||
suspend fun createProjectFromWizardImpl(wizard: AbstractProjectWizard, projectFile: Path, projectToClose: Project?): Project? {
|
||||
val projectManager = (serviceAsync<ProjectManager>() as ProjectManagerEx)
|
||||
for (p in projectManager.getOpenProjects()) {
|
||||
if (ProjectUtil.isSameProject(projectFile, p)) {
|
||||
focusProjectWindow(project = p, stealFocusIfAppInactive = false)
|
||||
return null
|
||||
}
|
||||
@@ -142,7 +161,7 @@ private fun doCreate(wizard: AbstractProjectWizard, projectToClose: Project?): P
|
||||
LOG.debug { "builder $projectBuilder" }
|
||||
try {
|
||||
val projectDir = if (wizard.storageScheme == StorageScheme.DEFAULT) {
|
||||
projectFile.parent ?: throw IOException("Cannot create project in '$projectFilePath': no parent file exists")
|
||||
projectFile.parent ?: throw IOException("Cannot create project in '$projectFile': no parent file exists")
|
||||
}
|
||||
else {
|
||||
projectFile
|
||||
@@ -151,11 +170,11 @@ private fun doCreate(wizard: AbstractProjectWizard, projectToClose: Project?): P
|
||||
val newProject: Project? = if (projectBuilder == null || !projectBuilder.isUpdate) {
|
||||
val name = wizard.projectName
|
||||
if (projectBuilder == null) {
|
||||
projectManager.newProject(projectFile, build().asNewProject().withProjectName(name))
|
||||
projectManager.newProject(projectFile, OpenProjectTask.build().asNewProject().withProjectName(name))
|
||||
}
|
||||
else {
|
||||
try {
|
||||
projectBuilder.createProject(name, projectFilePath)
|
||||
projectBuilder.createProject(name, projectFile.toString())
|
||||
}
|
||||
catch (e: CancellationException) {
|
||||
throw e
|
||||
@@ -175,33 +194,50 @@ private fun doCreate(wizard: AbstractProjectWizard, projectToClose: Project?): P
|
||||
}
|
||||
|
||||
val compileOutput = wizard.newCompileOutput
|
||||
setCompilerOutputPath(newProject, compileOutput)
|
||||
val javaCompilerExtension = CompilerProjectExtension.getInstance(newProject)
|
||||
if (javaCompilerExtension != null) {
|
||||
val canonicalPath = try {
|
||||
FileUtil.resolveShortWindowsName(compileOutput)
|
||||
}
|
||||
catch (_: IOException) {
|
||||
compileOutput
|
||||
}
|
||||
|
||||
@Suppress("UsagesOfObsoleteApi")
|
||||
backgroundWriteAction {
|
||||
javaCompilerExtension.compilerOutputUrl = VfsUtilCore.pathToUrl(canonicalPath)
|
||||
}
|
||||
}
|
||||
|
||||
if (projectBuilder != null) {
|
||||
// validate can require a project on disk
|
||||
if (!ApplicationManager.getApplication().isUnitTestMode) {
|
||||
newProject.save()
|
||||
runInAutoSaveDisabledMode {
|
||||
saveSettings(componentManager = newProject, forceSavingAllSettings = true)
|
||||
}
|
||||
}
|
||||
if (!projectBuilder.validate(projectToClose, newProject)) {
|
||||
return projectToClose
|
||||
}
|
||||
projectBuilder.commit(newProject, null, ModulesProvider.EMPTY_MODULES_PROVIDER)
|
||||
|
||||
withContext(Dispatchers.EDT) {
|
||||
projectBuilder.commit(newProject, null, ModulesProvider.EMPTY_MODULES_PROVIDER)
|
||||
}
|
||||
}
|
||||
|
||||
val jdk = wizard.newProjectJdk
|
||||
if (jdk != null) {
|
||||
CommandProcessor.getInstance().executeCommand(newProject, {
|
||||
ApplicationManager.getApplication().runWriteAction {
|
||||
JavaSdkUtil.applyJdkToProject(newProject, jdk)
|
||||
}
|
||||
}, null, null)
|
||||
@Suppress("UsagesOfObsoleteApi")
|
||||
backgroundWriteAction {
|
||||
JavaSdkUtil.applyJdkToProject(newProject, jdk)
|
||||
}
|
||||
}
|
||||
|
||||
if (!ApplicationManager.getApplication().isUnitTestMode) {
|
||||
val needToOpenProjectStructure = projectBuilder == null || projectBuilder.isOpenProjectSettingsAfter
|
||||
StartupManager.getInstance(newProject).runAfterOpened {
|
||||
// ensure the dialog is shown after all startup activities are done
|
||||
ApplicationManager.getApplication().invokeLater(
|
||||
{
|
||||
ApplicationManager.getApplication().invokeLater({
|
||||
if (needToOpenProjectStructure) {
|
||||
ModulesConfigurator.showDialog(newProject, null, null)
|
||||
}
|
||||
@@ -227,13 +263,9 @@ private fun doCreate(wizard: AbstractProjectWizard, projectToClose: Project?): P
|
||||
}
|
||||
}
|
||||
}
|
||||
runWithModalProgressBlocking(
|
||||
owner = ModalTaskOwner.guess(),
|
||||
title = IdeUICustomization.getInstance().projectMessage("progress.title.project.loading.name", options.projectName),
|
||||
) {
|
||||
serviceAsync<TrustedPaths>().setProjectPathTrusted(projectDir, true)
|
||||
(serviceAsync<ProjectManager>() as ProjectManagerEx).openProjectAsync(projectStoreBaseDir = projectDir, options = options)
|
||||
}
|
||||
|
||||
serviceAsync<TrustedPaths>().setProjectPathTrusted(projectDir, true)
|
||||
(serviceAsync<ProjectManager>() as ProjectManagerEx).openProjectAsync(projectStoreBaseDir = projectDir, options = options)
|
||||
}
|
||||
if (!ApplicationManager.getApplication().isUnitTestMode) {
|
||||
SaveAndSyncHandler.getInstance().scheduleProjectSave(newProject)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.plugins.gradle.setup
|
||||
|
||||
import com.intellij.ide.impl.NewProjectUtil
|
||||
import com.intellij.ide.impl.createProjectFromWizardImpl
|
||||
import com.intellij.ide.projectWizard.NewProjectWizard
|
||||
import com.intellij.ide.projectWizard.NewProjectWizardConstants.BuildSystem.GRADLE
|
||||
import com.intellij.ide.projectWizard.NewProjectWizardConstants.Language.JAVA
|
||||
@@ -16,6 +16,7 @@ import com.intellij.ide.wizard.NewProjectWizardStep.Companion.GENERATE_ONBOARDIN
|
||||
import com.intellij.ide.wizard.NewProjectWizardStep.Companion.GIT_PROPERTY_NAME
|
||||
import com.intellij.ide.wizard.NewProjectWizardStep.Companion.GROUP_ID_PROPERTY_NAME
|
||||
import com.intellij.ide.wizard.Step
|
||||
import com.intellij.openapi.application.EDT
|
||||
import com.intellij.openapi.application.invokeAndWaitIfNeeded
|
||||
import com.intellij.openapi.externalSystem.model.project.ProjectData
|
||||
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil
|
||||
@@ -31,6 +32,8 @@ import com.intellij.testFramework.PlatformTestUtil
|
||||
import com.intellij.testFramework.utils.vfs.getDirectory
|
||||
import com.intellij.testFramework.withProjectAsync
|
||||
import com.intellij.ui.UIBundle
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.jetbrains.plugins.gradle.frameworkSupport.buildscript.GradleBuildScriptBuilder
|
||||
import org.jetbrains.plugins.gradle.service.project.wizard.GradleJavaNewProjectWizardData.Companion.javaGradleData
|
||||
import org.jetbrains.plugins.gradle.service.project.wizard.GradleNewProjectWizardStep
|
||||
@@ -41,6 +44,7 @@ import org.jetbrains.plugins.gradle.testFramework.util.withBuildFile
|
||||
import org.jetbrains.plugins.gradle.util.GradleConstants
|
||||
import org.junit.jupiter.api.Assertions
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import java.nio.file.Path
|
||||
|
||||
abstract class GradleCreateProjectTestCase : GradleTestCase() {
|
||||
|
||||
@@ -95,15 +99,13 @@ abstract class GradleCreateProjectTestCase : GradleTestCase() {
|
||||
numProjectSyncs: Int = 1,
|
||||
configure: NewProjectWizardStep.() -> Unit,
|
||||
): Project {
|
||||
val wizard = createAndConfigureWizard(group, null, configure)
|
||||
val wizard = createAndConfigureWizard(group = group, project = null, configure = configure)
|
||||
return awaitOpenProjectConfiguration(numProjectSyncs) {
|
||||
blockingContext {
|
||||
invokeAndWaitIfNeeded {
|
||||
val project = NewProjectUtil.createFromWizard(wizard, null)!!
|
||||
PlatformTestUtil.dispatchAllEventsInIdeEventQueue()
|
||||
project
|
||||
}
|
||||
val project = createProjectFromWizardImpl(wizard = wizard, projectFile = Path.of(wizard.newProjectFilePath), projectToClose = null)
|
||||
withContext(Dispatchers.EDT) {
|
||||
PlatformTestUtil.dispatchAllEventsInIdeEventQueue()
|
||||
}
|
||||
project!!
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user