mirror of
https://gitflic.ru/project/openide/openide.git
synced 2025-12-15 11:53:49 +07:00
IDEA-331839 do not use CountDownLatch, introduce ideStartupWizard EP
GitOrigin-RevId: f554674027a55a36256b456add65f5820a9bab80
This commit is contained in:
committed by
intellij-monorepo-bot
parent
2ab5336e83
commit
0d943b69fa
@@ -26,6 +26,5 @@
|
||||
</orderEntry>
|
||||
<orderEntry type="module" module-name="intellij.platform.ide.impl" />
|
||||
<orderEntry type="module" module-name="intellij.idea.customization.base" />
|
||||
<orderEntry type="module" module-name="intellij.ide.startup.importSettings" />
|
||||
</component>
|
||||
</module>
|
||||
@@ -3,16 +3,12 @@ package com.intellij.openapi.application.ex;
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.util.indexing.impl.IndexDebugProperties;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.TestOnly;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
public final class ApplicationManagerEx extends ApplicationManager {
|
||||
public static final String IS_INTERNAL_PROPERTY = "idea.is.internal";
|
||||
|
||||
private static volatile boolean inStressTest;
|
||||
private static volatile CountDownLatch isInitialStart;
|
||||
|
||||
public static ApplicationEx getApplicationEx() {
|
||||
return (ApplicationEx)ourApplication;
|
||||
@@ -31,19 +27,4 @@ public final class ApplicationManagerEx extends ApplicationManager {
|
||||
inStressTest = value;
|
||||
IndexDebugProperties.IS_IN_STRESS_TESTS = value;
|
||||
}
|
||||
|
||||
public static void setInitialStart() {
|
||||
isInitialStart = new CountDownLatch(1);
|
||||
}
|
||||
|
||||
public static boolean isInitialStart() {
|
||||
return getInitialStartState() != null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static CountDownLatch getInitialStartState() {
|
||||
return isInitialStart;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -146,7 +146,7 @@ public final class ConfigImportHelper {
|
||||
log.error("Couldn't backup current config or delete current config directory", e);
|
||||
}
|
||||
}
|
||||
else if (!IdeStartupWizardKt.isStartupWizardEnabled()) {
|
||||
else if (!Boolean.getBoolean("intellij.startup.wizard")) {
|
||||
if (shouldAskForConfig()) {
|
||||
oldConfigDirAndOldIdePath = showDialogAndGetOldConfigPath(guessedOldConfigDirs.getPaths());
|
||||
importScenarioStatistics = SHOW_DIALOG_REQUESTED_BY_PROPERTY;
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.openapi.application
|
||||
|
||||
import com.intellij.openapi.application.ex.ApplicationManagerEx
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.diagnostic.logger
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.runInterruptible
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
private const val INTELLIJ_STARTUP_WIZARD_CLASS_PROPERTY = "intellij.startup-wizard.class"
|
||||
|
||||
private val LOG: Logger
|
||||
get() = logger<IdeStartupWizard>()
|
||||
|
||||
internal val isStartupWizardEnabled: Boolean =
|
||||
!System.getProperty(INTELLIJ_STARTUP_WIZARD_CLASS_PROPERTY).isNullOrBlank()
|
||||
|
||||
internal suspend fun runStartupWizard() {
|
||||
if (!isStartupWizardEnabled) return
|
||||
if (!ConfigImportHelper.isNewUser()) return
|
||||
|
||||
LOG.info("Entering startup wizard workflow.")
|
||||
|
||||
waitForAppManagerInitialState()
|
||||
|
||||
val className = System.getProperty(INTELLIJ_STARTUP_WIZARD_CLASS_PROPERTY)
|
||||
LOG.info("Passing execution control to $className.")
|
||||
val wizardClass = Class.forName(className)
|
||||
val instance = wizardClass.getDeclaredConstructor().newInstance() as IdeStartupWizard
|
||||
instance.run()
|
||||
}
|
||||
|
||||
private suspend fun waitForAppManagerInitialState() {
|
||||
val latch = ApplicationManagerEx.getInitialStartState()
|
||||
if (latch == null) error("Cannot get initial startup state")
|
||||
LOG.info("Waiting for app manager initial state.")
|
||||
withContext(Dispatchers.IO) {
|
||||
runInterruptible {
|
||||
latch.await()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface IdeStartupWizard {
|
||||
suspend fun run()
|
||||
}
|
||||
@@ -17,7 +17,10 @@ import com.intellij.ide.ui.IconMapLoader
|
||||
import com.intellij.ide.ui.LafManager
|
||||
import com.intellij.ide.ui.UISettings
|
||||
import com.intellij.ide.ui.laf.LafManagerImpl
|
||||
import com.intellij.idea.*
|
||||
import com.intellij.idea.AppExitCodes
|
||||
import com.intellij.idea.AppMode
|
||||
import com.intellij.idea.IdeStarter
|
||||
import com.intellij.idea.StartupErrorReporter
|
||||
import com.intellij.internal.statistic.collectors.fus.actions.persistence.ActionsEventLogGroup
|
||||
import com.intellij.openapi.application.*
|
||||
import com.intellij.openapi.application.ex.ApplicationEx
|
||||
@@ -160,6 +163,7 @@ internal suspend fun loadApp(app: ApplicationImpl,
|
||||
|
||||
appRegisteredJob.join()
|
||||
initConfigurationStoreJob.join()
|
||||
|
||||
val appInitializedListenerJob = launch {
|
||||
val appInitializedListeners = appInitListeners.await()
|
||||
span("app initialized callback") {
|
||||
@@ -167,6 +171,7 @@ internal suspend fun loadApp(app: ApplicationImpl,
|
||||
callAppInitialized(listeners = appInitializedListeners, asyncScope = app.coroutineScope)
|
||||
}
|
||||
}
|
||||
|
||||
asyncScope.launch {
|
||||
launch(CoroutineName("checkThirdPartyPluginsAllowed")) {
|
||||
checkThirdPartyPluginsAllowed()
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.platform.ide.bootstrap
|
||||
|
||||
import com.intellij.diagnostic.PluginException
|
||||
import com.intellij.openapi.application.Application
|
||||
import com.intellij.openapi.diagnostic.logger
|
||||
import com.intellij.openapi.extensions.impl.ExtensionPointImpl
|
||||
import com.intellij.platform.diagnostic.telemetry.impl.span
|
||||
import kotlinx.coroutines.Job
|
||||
import org.jetbrains.annotations.ApiStatus.Internal
|
||||
|
||||
internal suspend fun runStartupWizard(isInitialStart: Job, app: Application) {
|
||||
val log = logger<IdeStartupWizard>()
|
||||
|
||||
log.info("Entering startup wizard workflow.")
|
||||
|
||||
span("app manager initial state waiting") {
|
||||
isInitialStart.join()
|
||||
}
|
||||
|
||||
val point = app.extensionArea
|
||||
.getExtensionPoint<IdeStartupWizard>("com.intellij.ideStartupWizard") as ExtensionPointImpl<IdeStartupWizard>
|
||||
for (adapter in point.sortedAdapters) {
|
||||
val pluginDescriptor = adapter.pluginDescriptor
|
||||
if (!pluginDescriptor.isBundled) {
|
||||
log.error(PluginException("ideStartupWizard extension can be implemented only by a bundled plugin", pluginDescriptor.pluginId))
|
||||
continue
|
||||
}
|
||||
|
||||
try {
|
||||
log.info("Passing execution control to $adapter.")
|
||||
span("${adapter.assignableToClassName}.run") {
|
||||
adapter.createInstance<IdeStartupWizard>(app)?.run()
|
||||
}
|
||||
break
|
||||
}
|
||||
catch (e: Throwable) {
|
||||
log.error(PluginException(e, pluginDescriptor.pluginId))
|
||||
}
|
||||
}
|
||||
point.reset()
|
||||
}
|
||||
|
||||
@Internal
|
||||
interface IdeStartupWizard {
|
||||
suspend fun run()
|
||||
}
|
||||
@@ -22,7 +22,6 @@ import com.intellij.openapi.application.impl.*
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.diagnostic.getOrLogException
|
||||
import com.intellij.openapi.diagnostic.logger
|
||||
import com.intellij.openapi.diagnostic.runAndLogException
|
||||
import com.intellij.openapi.util.Disposer
|
||||
import com.intellij.openapi.util.IconLoader
|
||||
import com.intellij.openapi.util.ShutDownTracker
|
||||
@@ -64,6 +63,7 @@ import java.util.function.BiFunction
|
||||
import java.util.logging.ConsoleHandler
|
||||
import java.util.logging.Level
|
||||
import javax.swing.*
|
||||
import kotlin.concurrent.Volatile
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
internal const val IDE_STARTED: String = "------------------------------------------------------ IDE STARTED ------------------------------------------------------"
|
||||
@@ -187,34 +187,41 @@ fun CoroutineScope.startApplication(args: List<String>,
|
||||
|
||||
val euaDocumentDeferred = async { loadEuaDocument(appInfoDeferred) }
|
||||
|
||||
val configImportDeferred = async {
|
||||
if (!isHeadless && configImportNeededDeferred.await()) {
|
||||
initLafJob.join()
|
||||
val log = logDeferred.await()
|
||||
importConfig(
|
||||
args = args,
|
||||
targetDirectoryToImportConfig = targetDirectoryToImportConfig ?: PathManager.getConfigDir(),
|
||||
log = log,
|
||||
appStarter = appStarterDeferred.await(),
|
||||
euaDocumentDeferred = euaDocumentDeferred,
|
||||
)
|
||||
val configImportDeferred: Deferred<Job?> = async {
|
||||
if (isHeadless || !configImportNeededDeferred.await()) {
|
||||
return@async null
|
||||
}
|
||||
|
||||
if (ConfigImportHelper.isNewUser() && System.getProperty("ide.experimental.ui") == null) {
|
||||
initLafJob.join()
|
||||
val log = logDeferred.await()
|
||||
importConfig(
|
||||
args = args,
|
||||
targetDirectoryToImportConfig = targetDirectoryToImportConfig ?: PathManager.getConfigDir(),
|
||||
log = log,
|
||||
appStarter = appStarterDeferred.await(),
|
||||
euaDocumentDeferred = euaDocumentDeferred,
|
||||
)
|
||||
|
||||
if (ConfigImportHelper.isNewUser()) {
|
||||
if (System.getProperty("ide.experimental.ui") == null) {
|
||||
runCatching {
|
||||
EarlyAccessRegistryManager.setAndFlush(mapOf("ide.experimental.ui" to "true"))
|
||||
}.getOrLogException(log)
|
||||
}
|
||||
|
||||
if (isStartupWizardEnabled && ConfigImportHelper.isNewUser()) {
|
||||
log.info("Will enter initial app wizard flow.")
|
||||
ApplicationManagerEx.setInitialStart()
|
||||
}
|
||||
log.info("Will enter initial app wizard flow.")
|
||||
val result = CompletableDeferred<Boolean>()
|
||||
isInitialStart = result
|
||||
result
|
||||
}
|
||||
else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
val pluginSetDeferred = async {
|
||||
// plugins cannot be loaded when a config import is needed, because plugins may be added after importing
|
||||
configImportDeferred.await()
|
||||
configImportDeferred.join()
|
||||
|
||||
PluginManagerCore.scheduleDescriptorLoading(coroutineScope = this@startApplication,
|
||||
zipFilePoolDeferred = zipFilePoolDeferred,
|
||||
@@ -245,7 +252,7 @@ fun CoroutineScope.startApplication(args: List<String>,
|
||||
Class.forName(OpenTelemetrySdkBuilder::class.java.name, true, classLoader)
|
||||
}
|
||||
|
||||
val appLoaded = async {
|
||||
val appLoaded = launch {
|
||||
checkSystemDirJob.join()
|
||||
|
||||
val classBeforeAppProperty = System.getProperty(IDEA_CLASS_BEFORE_APPLICATION_PROPERTY)
|
||||
@@ -262,30 +269,26 @@ fun CoroutineScope.startApplication(args: List<String>,
|
||||
val starter = loadApp(app = app,
|
||||
initAwtToolkitAndEventQueueJob = initEventQueueJob,
|
||||
pluginSetDeferred = pluginSetDeferred,
|
||||
appInfoDeferred = appInfoDeferred,
|
||||
appInfoDeferred = appInfoDeferred,
|
||||
euaDocumentDeferred = euaDocumentDeferred,
|
||||
asyncScope = this@startApplication,
|
||||
initLafJob = initLafJob,
|
||||
logDeferred = logDeferred,
|
||||
appRegisteredJob = appRegisteredJob,
|
||||
args = args.filterNot { CommandLineArgs.isKnownArgument(it) })
|
||||
if (isStartupWizardEnabled) {
|
||||
// Initial startup wizard relies on config import being performed before this async ends.
|
||||
configImportDeferred.await()
|
||||
}
|
||||
else {
|
||||
// In case of startup wizard, this will be executed at a stage separated from app loading.
|
||||
executeApplicationStarter(starter, args)
|
||||
}
|
||||
return@async starter
|
||||
}
|
||||
|
||||
if (isStartupWizardEnabled) {
|
||||
launch {
|
||||
val starter = appLoaded.await()
|
||||
val log = logDeferred.await()
|
||||
log.runAndLogException { runStartupWizard() }
|
||||
executeApplicationStarter(starter, args)
|
||||
// out of appLoaded scope
|
||||
this@startApplication.launch {
|
||||
val isInitialStart = configImportDeferred.await()
|
||||
// appLoaded not only provides starter, but also loads app, that's why it is here
|
||||
if (isInitialStart != null) {
|
||||
val log = logDeferred.await()
|
||||
runCatching {
|
||||
span("startup wizard run") {
|
||||
runStartupWizard(isInitialStart = isInitialStart, app = ApplicationManager.getApplication())
|
||||
}
|
||||
}.getOrLogException(log)
|
||||
}
|
||||
executeApplicationStarter(starter = starter, args = args)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -311,6 +314,10 @@ fun CoroutineScope.startApplication(args: List<String>,
|
||||
}
|
||||
}
|
||||
|
||||
@Volatile
|
||||
@JvmField
|
||||
internal var isInitialStart: CompletableDeferred<Boolean>? = null
|
||||
|
||||
private fun CoroutineScope.scheduleEnableCoroutineDumpAndJstack() {
|
||||
if (!System.getProperty("idea.enable.coroutine.dump", "true").toBoolean()) {
|
||||
return
|
||||
|
||||
@@ -141,6 +141,7 @@
|
||||
<extensionPoint name="projectTemplate"
|
||||
beanClass="com.intellij.platform.ProjectTemplateEP" dynamic="true"/>
|
||||
|
||||
<extensionPoint name="ideStartupWizard" interface="com.intellij.platform.ide.bootstrap.IdeStartupWizard" dynamic="false"/>
|
||||
<extensionPoint name="ApplicationLoadListener" interface="com.intellij.ide.ApplicationLoadListener" dynamic="false"/>
|
||||
<extensionPoint name="ideEventQueueDispatcher" interface="com.intellij.ide.IdeEventQueue$EventDispatcher" dynamic="true"/>
|
||||
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/resources" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="library" name="kotlin-stdlib" level="project" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="module" module-name="intellij.platform.ide.impl" />
|
||||
<orderEntry type="module" module-name="intellij.platform.ide.impl" />
|
||||
</component>
|
||||
</module>
|
||||
@@ -0,0 +1,6 @@
|
||||
<idea-plugin package="com.intellij.ide.startup.importSettings">
|
||||
<extensions defaultExtensionNs="com.intellij">
|
||||
<!--suppress PluginXmlDynamicPlugin -->
|
||||
<ideStartupWizard implementation="com.intellij.ide.startup.importSettings.IdeStartupWizardImpl"/>
|
||||
</extensions>
|
||||
</idea-plugin>
|
||||
@@ -1,14 +1,19 @@
|
||||
package intellij.ide.startup.importSettings
|
||||
package com.intellij.ide.startup.importSettings
|
||||
|
||||
import com.intellij.openapi.application.EDT
|
||||
import com.intellij.openapi.application.IdeStartupWizard
|
||||
import com.intellij.openapi.extensions.ExtensionNotApplicableException
|
||||
import com.intellij.platform.ide.bootstrap.IdeStartupWizard
|
||||
import com.intellij.ui.components.dialog
|
||||
import com.intellij.ui.dsl.builder.panel
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
@Suppress("unused") // instantiated by reflection
|
||||
class IdeStartupWizardImpl : IdeStartupWizard {
|
||||
private class IdeStartupWizardImpl : IdeStartupWizard {
|
||||
init {
|
||||
if (!System.getProperty("intellij.startup.wizard", "false").toBoolean()) {
|
||||
throw ExtensionNotApplicableException.create()
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("HardCodedStringLiteral") // temporary
|
||||
override suspend fun run() {
|
||||
Reference in New Issue
Block a user