From 23b6ec98d28a3d789b9c15997e08281bf2e9b550 Mon Sep 17 00:00:00 2001 From: Aleksey Pivovarov Date: Thu, 13 Nov 2025 15:27:01 +0100 Subject: [PATCH] [platform] IJPL-161632 introduce WellKnownCommands GitOrigin-RevId: a254a6fd6e8d5a2d683e43996b0a4e1744cbae04 --- .../bootstrap/src/com/intellij/idea/Main.kt | 2 +- .../src/com/intellij/idea/AppMode.java | 88 ++----------- .../com/intellij/idea/WellKnownCommands.kt | 117 ++++++++++++++++++ .../platform/ide/bootstrap/SplashManager.kt | 3 +- 4 files changed, 128 insertions(+), 82 deletions(-) create mode 100644 platform/core-api/src/com/intellij/idea/WellKnownCommands.kt diff --git a/platform/bootstrap/src/com/intellij/idea/Main.kt b/platform/bootstrap/src/com/intellij/idea/Main.kt index c9b49d87f401..2bbf956dc2a5 100644 --- a/platform/bootstrap/src/com/intellij/idea/Main.kt +++ b/platform/bootstrap/src/com/intellij/idea/Main.kt @@ -198,7 +198,7 @@ private fun initRemoteDev(args: List) { error("JBR version 17.0.6b796 or later is required to run a remote-dev server with lux") } - val isSplitMode = args.firstOrNull() == AppMode.SPLIT_MODE_COMMAND + val isSplitMode = args.firstOrNull() == WellKnownCommands.SPLIT_MODE // avoid an icon jumping in dock for the backend process if (OS.CURRENT == OS.macOS) { diff --git a/platform/core-api/src/com/intellij/idea/AppMode.java b/platform/core-api/src/com/intellij/idea/AppMode.java index 816a7012ea55..2a32b0cbf731 100644 --- a/platform/core-api/src/com/intellij/idea/AppMode.java +++ b/platform/core-api/src/com/intellij/idea/AppMode.java @@ -9,7 +9,6 @@ import org.jetbrains.annotations.TestOnly; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.Arrays; import java.util.List; @ApiStatus.Internal @@ -17,12 +16,6 @@ public final class AppMode { public static final String DISABLE_NON_BUNDLED_PLUGINS = "disableNonBundledPlugins"; public static final String DONT_REOPEN_PROJECTS = "dontReopenProjects"; public static final String FORCE_PLUGIN_UPDATES = "idea.force.plugin.updates"; - public static final String CWM_HOST_COMMAND = "cwmHost"; - /** @see com.jetbrains.rdserver.unattendedHost.HostWithClientSplitModeStarter */ - public static final String SPLIT_MODE_COMMAND = "splitMode"; - public static final String CWM_HOST_NO_LOBBY_COMMAND = "cwmHostNoLobby"; - public static final String REMOTE_DEV_HOST_COMMAND = "remoteDevHost"; - public static final String REMOTE_DEV_MODE_COMMAND = "serverMode"; public static final String HELP_OPTION = "--help"; public static final String VERSION_OPTION = "--version"; @@ -85,23 +78,21 @@ public final class AppMode { } public static void setFlags(@NotNull List args) { - isHeadless = isHeadless(args); - isCommandLine = isHeadless || (!args.isEmpty() && isGuiCommand(args.get(0))); + WellKnownCommand knownCommand = WellKnownCommands.getCommandFor(args); + + isHeadless = Boolean.getBoolean(AWT_HEADLESS) || + knownCommand != null && knownCommand.isHeadless(); + isCommandLine = isHeadless || + knownCommand != null && knownCommand.isCommandLine(); if (isHeadless) { System.setProperty(AWT_HEADLESS, Boolean.TRUE.toString()); } - if (!args.isEmpty()) { - isRemoteDevHost = CWM_HOST_COMMAND.equals(args.get(0)) || - CWM_HOST_NO_LOBBY_COMMAND.equals(args.get(0)) || - REMOTE_DEV_HOST_COMMAND.equals(args.get(0)) || - REMOTE_DEV_MODE_COMMAND.equals(args.get(0)) || - SPLIT_MODE_COMMAND.equals(args.get(0)); - } + isRemoteDevHost = knownCommand != null && knownCommand.isRemoteDevHost(); isLightEdit = Boolean.parseBoolean(System.getProperty("idea.force.light.edit.mode")) || - (!isCommandLine && !isRemoteDevHost && !isKnownNonLightEditCommand(args) && isFileAfterOptions(args)); + (knownCommand == null && !isHeadless && isFileAfterOptions(args)); for (String arg : args) { if (DISABLE_NON_BUNDLED_PLUGINS.equalsIgnoreCase(arg)) { @@ -113,18 +104,6 @@ public final class AppMode { } } - /** - * Checks whether a known command is present in the args which shouldn't be run in 'light edit' mode. - * This is a temporary workaround for IJPL-161632. - */ - private static boolean isKnownNonLightEditCommand(@NotNull List args) { - return !args.isEmpty() && "thinClient".equals(args.get(0)); - } - - private static boolean isGuiCommand(String arg) { - return "diff".equals(arg) || "merge".equals(arg); - } - private static boolean isFileAfterOptions(@NotNull List args) { for (String arg : args) { // If not an option @@ -151,57 +130,6 @@ public final class AppMode { isLightEdit = false; } - private static boolean isHeadless(List args) { - if (Boolean.getBoolean(AWT_HEADLESS)) { - return true; - } - - if (args.isEmpty()) { - return false; - } - - String firstArg = args.get(0); - - List headlessCommands = Arrays.asList( - "ant", - "appcodeClangModulesDiff", - "appcodeClangModulesPrinter", - "buildAppcodeCache", - "buildEventsScheme", - "cherryPickAnalyzer", - "cwmHostStatus", - "dataSources", - "dump-launch-parameters", - "dump-shared-index", - "dumpActions", - "duplocate", - "exit", - "format", - "inspections", - "inspectopedia-generator", - "installFrontendPlugins", - "installGatewayProtocolHandler", - "installPlugins", - "intentions", - "invalidateCaches", - "keymap", - "listBundledPlugins", - "openUrlOnClient", - "project-with-shared-caches", - "qodanaExcludedPlugins", - "rdserver-headless", - "registerBackendLocationForGateway", - "remoteDevShowHelp", - "remoteDevStatus", - "thinClient-headless", - "traverseUI", - "uninstallGatewayProtocolHandler", - "update", - "warmup" - ); - return headlessCommands.contains(firstArg) || firstArg.length() < 20 && firstArg.endsWith("inspect"); - } - public static @Nullable String getDevIdeaProjectDir() { return System.getProperty("idea.dev.project.root"); } diff --git a/platform/core-api/src/com/intellij/idea/WellKnownCommands.kt b/platform/core-api/src/com/intellij/idea/WellKnownCommands.kt new file mode 100644 index 000000000000..219acaa00721 --- /dev/null +++ b/platform/core-api/src/com/intellij/idea/WellKnownCommands.kt @@ -0,0 +1,117 @@ +// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.idea + +import org.jetbrains.annotations.ApiStatus + +@ApiStatus.Internal +object WellKnownCommands { + const val SERVER_MODE: String = "serverMode" + const val SPLIT_MODE: String = "splitMode" + + private val allCommands: Map = listOf( + WellKnownCommand("ant", CommandType.HEADLESS), + WellKnownCommand("appcodeClangModulesDiff", CommandType.HEADLESS), + WellKnownCommand("appcodeClangModulesPrinter", CommandType.HEADLESS), + WellKnownCommand("buildAppcodeCache", CommandType.HEADLESS), + WellKnownCommand("buildEventsScheme", CommandType.HEADLESS), + WellKnownCommand("cherryPickAnalyzer", CommandType.HEADLESS), + WellKnownCommand("createCourse", CommandType.HEADLESS), + WellKnownCommand("cwmHost", CommandType.REMOTE_DEV_HOST), + WellKnownCommand("cwmHostNoLobby", CommandType.REMOTE_DEV_HOST), + WellKnownCommand("cwmHostStatus", CommandType.HEADLESS), + WellKnownCommand("dataSources", CommandType.HEADLESS), + WellKnownCommand("diff", CommandType.GUI_COMMAND), + WellKnownCommand("dump-launch-parameters", CommandType.HEADLESS), + WellKnownCommand("dump-shared-index", CommandType.HEADLESS), + WellKnownCommand("dumpActions", CommandType.HEADLESS), + WellKnownCommand("duplocate", CommandType.HEADLESS), + WellKnownCommand("exit", CommandType.HEADLESS), + WellKnownCommand("format", CommandType.HEADLESS), + WellKnownCommand("full-line", CommandType.HEADLESS), + WellKnownCommand("inspect", CommandType.HEADLESS), + WellKnownCommand("inspections", CommandType.HEADLESS), + WellKnownCommand("inspectopedia-generator", CommandType.HEADLESS), + WellKnownCommand("installCoursePlugins", CommandType.HEADLESS), + WellKnownCommand("installFrontendPlugins", CommandType.HEADLESS), + WellKnownCommand("installGatewayProtocolHandler", CommandType.HEADLESS), + WellKnownCommand("installPlugins", CommandType.HEADLESS), + WellKnownCommand("intentions", CommandType.HEADLESS), + WellKnownCommand("invalidateCaches", CommandType.HEADLESS), + WellKnownCommand("keymap", CommandType.HEADLESS), + WellKnownCommand("listBundledPlugins", CommandType.HEADLESS), + WellKnownCommand("matterhorn", CommandType.HEADLESS), + WellKnownCommand("merge", CommandType.GUI_COMMAND), + WellKnownCommand("ml-evaluate", CommandType.HEADLESS), + WellKnownCommand("ml-process", CommandType.HEADLESS), + WellKnownCommand("openUrlOnClient", CommandType.HEADLESS), + WellKnownCommand("project-with-shared-caches", CommandType.HEADLESS), + WellKnownCommand("qodanaExcludedPlugins", CommandType.HEADLESS), + WellKnownCommand("rdserver-headless", CommandType.HEADLESS), + WellKnownCommand("registerBackendLocationForGateway", CommandType.HEADLESS), + WellKnownCommand("remoteDevHost", CommandType.REMOTE_DEV_HOST), + WellKnownCommand("remoteDevShowHelp", CommandType.HEADLESS), + WellKnownCommand("remoteDevStatus", CommandType.HEADLESS), + WellKnownCommand("serverMode", CommandType.REMOTE_DEV_HOST), + WellKnownCommand("splitMode", CommandType.REMOTE_DEV_HOST), + WellKnownCommand("thinClient", CommandType.GUI), + WellKnownCommand("thinClient-headless", CommandType.HEADLESS), + WellKnownCommand("traverseUI", CommandType.HEADLESS), + WellKnownCommand("uninstallGatewayProtocolHandler", CommandType.HEADLESS), + WellKnownCommand("update", CommandType.HEADLESS), + WellKnownCommand("warmup", CommandType.HEADLESS), + ).associateBy { it.command } + + @JvmStatic + fun getCommandFor(args: List): WellKnownCommand? { + val commandName = args.firstOrNull() ?: return null + + val wellKnownCommand = allCommands[commandName] + if (wellKnownCommand != null) return wellKnownCommand + + if (commandName.length < 20 && commandName.endsWith("inspect")) { + return WellKnownCommand(commandName, commandType = CommandType.HEADLESS) + } + + return wellKnownCommand + } +} + +/** + * An unknown command is treated by IDE as `WellKnownCommand(name, CommandType.GUI)` + */ +@ApiStatus.Internal +class WellKnownCommand internal constructor( + val command: String, + val commandType: CommandType, +) { + /** + * Whether a command may show Swing UI. + * + * [AppMode.isHeadless] and [com.intellij.openapi.application.Application.isHeadlessEnvironment] and [java.awt.GraphicsEnvironment.isHeadless] + */ + val isHeadless: Boolean = commandType == CommandType.HEADLESS + + /** + * [AppMode.isCommandLine] and [com.intellij.openapi.application.Application.isCommandLine] + */ + val isCommandLine: Boolean = isHeadless || commandType == CommandType.GUI_COMMAND + + /** + * Whether the command will start IDE in Remote Development host mode. + * + * [AppMode.isRemoteDevHost] + */ + val isRemoteDevHost: Boolean = commandType == CommandType.REMOTE_DEV_HOST +} + +@ApiStatus.Internal +enum class CommandType { + GUI, + GUI_COMMAND, + HEADLESS, + + /** + * RemDev host that employs [com.intellij.platform.impl.toolkit.IdeToolkit] + */ + REMOTE_DEV_HOST +} diff --git a/platform/platform-impl/src/com/intellij/platform/ide/bootstrap/SplashManager.kt b/platform/platform-impl/src/com/intellij/platform/ide/bootstrap/SplashManager.kt index bf8b95ccde10..06a7627620e3 100644 --- a/platform/platform-impl/src/com/intellij/platform/ide/bootstrap/SplashManager.kt +++ b/platform/platform-impl/src/com/intellij/platform/ide/bootstrap/SplashManager.kt @@ -7,6 +7,7 @@ import com.intellij.diagnostic.LoadingState import com.intellij.diagnostic.StartUpMeasurer import com.intellij.ide.impl.ProjectUtil.getRootFrameForWindow import com.intellij.idea.AppMode +import com.intellij.idea.WellKnownCommands import com.intellij.openapi.application.ApplicationInfo import com.intellij.openapi.application.ApplicationNamesInfo import com.intellij.openapi.application.PathManager @@ -74,7 +75,7 @@ fun scheduleShowSplashIfNeeded( } } -private fun isRealRemoteDevHost(args: List): Boolean = AppMode.isRemoteDevHost() && args.firstOrNull() != AppMode.SPLIT_MODE_COMMAND +private fun isRealRemoteDevHost(args: List): Boolean = AppMode.isRemoteDevHost() && args.firstOrNull() != WellKnownCommands.SPLIT_MODE private fun showSplashIfNeeded(scope: CoroutineScope, initUiScale: Job, appInfoDeferred: Deferred) { val oldJob = splashJob.get()