diff --git a/community-resources/intellij.idea.community.customization.iml b/community-resources/intellij.idea.community.customization.iml
index a0a5da7aebcc..a73828cc90ad 100644
--- a/community-resources/intellij.idea.community.customization.iml
+++ b/community-resources/intellij.idea.community.customization.iml
@@ -26,6 +26,5 @@
-
\ No newline at end of file
diff --git a/platform/core-impl/src/com/intellij/openapi/application/ex/ApplicationManagerEx.java b/platform/core-impl/src/com/intellij/openapi/application/ex/ApplicationManagerEx.java
index 0c93ba2a0247..0c8878eaa9b5 100644
--- a/platform/core-impl/src/com/intellij/openapi/application/ex/ApplicationManagerEx.java
+++ b/platform/core-impl/src/com/intellij/openapi/application/ex/ApplicationManagerEx.java
@@ -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;
- }
-
-
}
diff --git a/platform/platform-impl/src/com/intellij/openapi/application/ConfigImportHelper.java b/platform/platform-impl/src/com/intellij/openapi/application/ConfigImportHelper.java
index 3ba62691b732..d23e8d55e55b 100644
--- a/platform/platform-impl/src/com/intellij/openapi/application/ConfigImportHelper.java
+++ b/platform/platform-impl/src/com/intellij/openapi/application/ConfigImportHelper.java
@@ -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;
diff --git a/platform/platform-impl/src/com/intellij/openapi/application/IdeStartupWizard.kt b/platform/platform-impl/src/com/intellij/openapi/application/IdeStartupWizard.kt
deleted file mode 100644
index 6ae02239262f..000000000000
--- a/platform/platform-impl/src/com/intellij/openapi/application/IdeStartupWizard.kt
+++ /dev/null
@@ -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()
-
-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()
-}
diff --git a/platform/platform-impl/src/com/intellij/platform/ide/bootstrap/ApplicationLoader.kt b/platform/platform-impl/src/com/intellij/platform/ide/bootstrap/ApplicationLoader.kt
index 65f65a9cb7d1..1778fca17761 100644
--- a/platform/platform-impl/src/com/intellij/platform/ide/bootstrap/ApplicationLoader.kt
+++ b/platform/platform-impl/src/com/intellij/platform/ide/bootstrap/ApplicationLoader.kt
@@ -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()
diff --git a/platform/platform-impl/src/com/intellij/platform/ide/bootstrap/IdeStartupWizard.kt b/platform/platform-impl/src/com/intellij/platform/ide/bootstrap/IdeStartupWizard.kt
new file mode 100644
index 000000000000..a745045b0073
--- /dev/null
+++ b/platform/platform-impl/src/com/intellij/platform/ide/bootstrap/IdeStartupWizard.kt
@@ -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()
+
+ log.info("Entering startup wizard workflow.")
+
+ span("app manager initial state waiting") {
+ isInitialStart.join()
+ }
+
+ val point = app.extensionArea
+ .getExtensionPoint("com.intellij.ideStartupWizard") as ExtensionPointImpl
+ 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(app)?.run()
+ }
+ break
+ }
+ catch (e: Throwable) {
+ log.error(PluginException(e, pluginDescriptor.pluginId))
+ }
+ }
+ point.reset()
+}
+
+@Internal
+interface IdeStartupWizard {
+ suspend fun run()
+}
diff --git a/platform/platform-impl/src/com/intellij/platform/ide/bootstrap/main.kt b/platform/platform-impl/src/com/intellij/platform/ide/bootstrap/main.kt
index 9587b751f181..c3812164cda9 100644
--- a/platform/platform-impl/src/com/intellij/platform/ide/bootstrap/main.kt
+++ b/platform/platform-impl/src/com/intellij/platform/ide/bootstrap/main.kt
@@ -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,
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 = 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()
+ 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,
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,
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,
}
}
+@Volatile
+@JvmField
+internal var isInitialStart: CompletableDeferred? = null
+
private fun CoroutineScope.scheduleEnableCoroutineDumpAndJstack() {
if (!System.getProperty("idea.enable.coroutine.dump", "true").toBoolean()) {
return
diff --git a/platform/platform-resources/src/META-INF/PlatformExtensionPoints.xml b/platform/platform-resources/src/META-INF/PlatformExtensionPoints.xml
index baf8b5e72711..b66f1b861b00 100644
--- a/platform/platform-resources/src/META-INF/PlatformExtensionPoints.xml
+++ b/platform/platform-resources/src/META-INF/PlatformExtensionPoints.xml
@@ -141,6 +141,7 @@
+
diff --git a/plugins/ide-startup/importSettings/intellij.ide.startup.importSettings.iml b/plugins/ide-startup/importSettings/intellij.ide.startup.importSettings.iml
index 5b71b579933a..b58eb2c688c4 100644
--- a/plugins/ide-startup/importSettings/intellij.ide.startup.importSettings.iml
+++ b/plugins/ide-startup/importSettings/intellij.ide.startup.importSettings.iml
@@ -3,12 +3,12 @@
+
-
\ No newline at end of file
diff --git a/plugins/ide-startup/importSettings/resources/intellij.ide.startup.importSettings.xml b/plugins/ide-startup/importSettings/resources/intellij.ide.startup.importSettings.xml
new file mode 100644
index 000000000000..00cc4c20e46b
--- /dev/null
+++ b/plugins/ide-startup/importSettings/resources/intellij.ide.startup.importSettings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/plugins/ide-startup/importSettings/src/intellij/ide/startup/importSettings/IdeStartupWizardImpl.kt b/plugins/ide-startup/importSettings/src/com/intellij/ide/startup/importSettings/IdeStartupWizardImpl.kt
similarity index 55%
rename from plugins/ide-startup/importSettings/src/intellij/ide/startup/importSettings/IdeStartupWizardImpl.kt
rename to plugins/ide-startup/importSettings/src/com/intellij/ide/startup/importSettings/IdeStartupWizardImpl.kt
index e63d0f7ed51d..3d30762e0790 100644
--- a/plugins/ide-startup/importSettings/src/intellij/ide/startup/importSettings/IdeStartupWizardImpl.kt
+++ b/plugins/ide-startup/importSettings/src/com/intellij/ide/startup/importSettings/IdeStartupWizardImpl.kt
@@ -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() {
diff --git a/plugins/ide-startup/importSettings/vscode/src/readme.md b/plugins/ide-startup/importSettings/vscode/src/readme.md
new file mode 100644
index 000000000000..e69de29bb2d1