[platform] IJPL-161632 introduce WellKnownCommands

GitOrigin-RevId: a254a6fd6e8d5a2d683e43996b0a4e1744cbae04
This commit is contained in:
Aleksey Pivovarov
2025-11-14 14:36:30 +00:00
committed by intellij-monorepo-bot
parent d636eb9793
commit 23b6ec98d2
4 changed files with 128 additions and 82 deletions
@@ -198,7 +198,7 @@ private fun initRemoteDev(args: List<String>) {
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) {
@@ -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<String> 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<String> 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<String> args) {
for (String arg : args) {
// If not an option
@@ -151,57 +130,6 @@ public final class AppMode {
isLightEdit = false;
}
private static boolean isHeadless(List<String> args) {
if (Boolean.getBoolean(AWT_HEADLESS)) {
return true;
}
if (args.isEmpty()) {
return false;
}
String firstArg = args.get(0);
List<String> 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");
}
@@ -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<String, WellKnownCommand> = 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<String>): 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
}
@@ -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<String>): Boolean = AppMode.isRemoteDevHost() && args.firstOrNull() != AppMode.SPLIT_MODE_COMMAND
private fun isRealRemoteDevHost(args: List<String>): Boolean = AppMode.isRemoteDevHost() && args.firstOrNull() != WellKnownCommands.SPLIT_MODE
private fun showSplashIfNeeded(scope: CoroutineScope, initUiScale: Job, appInfoDeferred: Deferred<ApplicationInfo>) {
val oldJob = splashJob.get()