From f66c0337bcb237d0a97df83aab0706c3b074d0fe Mon Sep 17 00:00:00 2001 From: Helin Shiah Date: Tue, 31 Mar 2026 12:36:54 -0700 Subject: [PATCH 1/8] Add reflection utility to fix split debugger error --- src/io/flutter/run/AttachState.java | 2 +- .../flutter/run/FlutterDebugSessionUtils.java | 80 +++++++++++++++++++ src/io/flutter/run/LaunchState.java | 18 ++--- .../run/bazelTest/BazelTestRunner.java | 7 +- .../flutter/run/test/FlutterTestRunner.java | 7 +- 5 files changed, 93 insertions(+), 21 deletions(-) create mode 100644 src/io/flutter/run/FlutterDebugSessionUtils.java diff --git a/src/io/flutter/run/AttachState.java b/src/io/flutter/run/AttachState.java index fe67d8c2ae..f655fe226e 100644 --- a/src/io/flutter/run/AttachState.java +++ b/src/io/flutter/run/AttachState.java @@ -47,6 +47,6 @@ protected RunContentDescriptor launch(@NotNull ExecutionEnvironment env) throws // Cache for use in console configuration, and for updating registered extensionRPCs. FlutterApp.addToEnvironment(env, app); final ExecutionResult result = setUpConsoleAndActions(app); - return createDebugSession(env, app, result).getRunContentDescriptor(); + return createDebugSession(env, app, result); } } diff --git a/src/io/flutter/run/FlutterDebugSessionUtils.java b/src/io/flutter/run/FlutterDebugSessionUtils.java new file mode 100644 index 0000000000..9d0523e962 --- /dev/null +++ b/src/io/flutter/run/FlutterDebugSessionUtils.java @@ -0,0 +1,80 @@ +package io.flutter.run; + +import com.intellij.execution.ExecutionException; +import com.intellij.execution.runners.ExecutionEnvironment; +import com.intellij.execution.ui.RunContentDescriptor; +import com.intellij.xdebugger.XDebugProcessStarter; +import com.intellij.xdebugger.XDebugSession; +import com.intellij.xdebugger.XDebuggerManager; +import org.jetbrains.annotations.NotNull; + +import java.lang.reflect.Method; + +public class FlutterDebugSessionUtils { + + public static RunContentDescriptor startSessionAndGetDescriptor( + @NotNull XDebuggerManager manager, + @NotNull ExecutionEnvironment env, + @NotNull XDebugProcessStarter starter, + boolean muteBreakpoints) throws ExecutionException { + try { + // Try to find the newSessionBuilder method (introduced in 2025.2+) + Method newSessionBuilderMethod = XDebuggerManager.class.getMethod("newSessionBuilder", XDebugProcessStarter.class); + Object builder = newSessionBuilderMethod.invoke(manager, starter); + + // builder.environment(env) + Method environmentMethod = builder.getClass().getMethod("environment", ExecutionEnvironment.class); + builder = environmentMethod.invoke(builder, env); + + // builder.startSession() -> returns XSessionStartedResult + Method startSessionMethod = builder.getClass().getMethod("startSession"); + Object sessionResult = startSessionMethod.invoke(builder); + + if (muteBreakpoints) { + Method getSessionMethod = sessionResult.getClass().getMethod("getSession"); + XDebugSession session = (XDebugSession) getSessionMethod.invoke(sessionResult); + session.setBreakpointMuted(true); + } + + // sessionResult.getRunContentDescriptor() + Method getDescriptorMethod = sessionResult.getClass().getMethod("getRunContentDescriptor"); + return (RunContentDescriptor) getDescriptorMethod.invoke(sessionResult); + + } catch (NoSuchMethodException e) { + // Fallback to old API for 2025.1 and older + XDebugSession session = manager.startSession(env, starter); + if (muteBreakpoints) { + session.setBreakpointMuted(true); + } + return session.getRunContentDescriptor(); + } catch (Exception e) { + throw new ExecutionException("Failed to start debug session via reflection", e); + } + } + + public static XDebugSession startSession( + @NotNull XDebuggerManager manager, + @NotNull ExecutionEnvironment env, + @NotNull XDebugProcessStarter starter) throws ExecutionException { + try { + Method newSessionBuilderMethod = XDebuggerManager.class.getMethod("newSessionBuilder", XDebugProcessStarter.class); + Object builder = newSessionBuilderMethod.invoke(manager, starter); + + Method environmentMethod = builder.getClass().getMethod("environment", ExecutionEnvironment.class); + builder = environmentMethod.invoke(builder, env); + + Method startSessionMethod = builder.getClass().getMethod("startSession"); + Object sessionResult = startSessionMethod.invoke(builder); // This is XSessionStartedResult + + // Now get the session from the result + Method getSessionMethod = sessionResult.getClass().getMethod("getSession"); + return (XDebugSession) getSessionMethod.invoke(sessionResult); + + } catch (NoSuchMethodException e) { + // Fallback to old API + return manager.startSession(env, starter); + } catch (Exception e) { + throw new ExecutionException("Failed to start debug session via reflection", e); + } + } +} diff --git a/src/io/flutter/run/LaunchState.java b/src/io/flutter/run/LaunchState.java index 90a720a0bb..18c8a6581c 100644 --- a/src/io/flutter/run/LaunchState.java +++ b/src/io/flutter/run/LaunchState.java @@ -160,7 +160,7 @@ protected RunContentDescriptor launch(@NotNull ExecutionEnvironment env) throws final RunContentDescriptor descriptor; if (launchMode.supportsDebugConnection()) { ToolWindowBadgeUpdater.updateBadgedIcon(app, project); - descriptor = createDebugSession(env, app, result).getRunContentDescriptor(); + descriptor = createDebugSession(env, app, result); } else { descriptor = new RunContentBuilder(result, env).showRunContent(env.getContentToReuse()); @@ -224,28 +224,22 @@ protected void showNoDeviceConnectedMessage(Project project) { } @NotNull - protected XDebugSession createDebugSession(@NotNull final ExecutionEnvironment env, - @NotNull final FlutterApp app, - @NotNull final ExecutionResult executionResult) + protected RunContentDescriptor createDebugSession(@NotNull final ExecutionEnvironment env, + @NotNull final FlutterApp app, + @NotNull final ExecutionResult executionResult) throws ExecutionException { final DartUrlResolver resolver = DartUrlResolver.getInstance(env.getProject(), sourceLocation); final FlutterPositionMapper mapper = createPositionMapper(env, app, resolver); final XDebuggerManager manager = XDebuggerManager.getInstance(env.getProject()); - final XDebugSession session = manager.startSession(env, new XDebugProcessStarter() { + return FlutterDebugSessionUtils.startSessionAndGetDescriptor(manager, env, new XDebugProcessStarter() { @Override @NotNull public XDebugProcess start(@NotNull final XDebugSession session) { return new FlutterDebugProcess(app, env, session, executionResult, resolver, mapper); } - }); - - if (app.getMode() != RunMode.DEBUG) { - session.setBreakpointMuted(true); - } - - return session; + }, app.getMode() != RunMode.DEBUG); } @NotNull diff --git a/src/io/flutter/run/bazelTest/BazelTestRunner.java b/src/io/flutter/run/bazelTest/BazelTestRunner.java index 7ffa75c510..94bdad13ab 100644 --- a/src/io/flutter/run/bazelTest/BazelTestRunner.java +++ b/src/io/flutter/run/bazelTest/BazelTestRunner.java @@ -45,6 +45,7 @@ import io.flutter.bazel.WorkspaceCache; import io.flutter.logging.PluginLogger; import io.flutter.run.FlutterPositionMapper; +import io.flutter.run.FlutterDebugSessionUtils; import io.flutter.run.common.CommonTestConfigUtils; import io.flutter.run.test.FlutterTestRunner; import io.flutter.settings.FlutterSettings; @@ -100,15 +101,13 @@ protected RunContentDescriptor runInDebugger(@NotNull BazelTestLaunchState launc // Create the debug session. final XDebuggerManager manager = XDebuggerManager.getInstance(env.getProject()); - final XDebugSession session = manager.startSession(env, new XDebugProcessStarter() { + return FlutterDebugSessionUtils.startSessionAndGetDescriptor(manager, env, new XDebugProcessStarter() { @Override @NotNull public XDebugProcess start(@NotNull final XDebugSession session) { return new BazelTestDebugProcess(env, session, executionResult, resolver, connector, mapper); } - }); - - return session.getRunContentDescriptor(); + }, false); } /** diff --git a/src/io/flutter/run/test/FlutterTestRunner.java b/src/io/flutter/run/test/FlutterTestRunner.java index 55c8b1ee77..077b771afc 100644 --- a/src/io/flutter/run/test/FlutterTestRunner.java +++ b/src/io/flutter/run/test/FlutterTestRunner.java @@ -33,6 +33,7 @@ import com.intellij.xdebugger.XDebuggerManager; import com.jetbrains.lang.dart.util.DartUrlResolver; import io.flutter.FlutterUtils; +import io.flutter.run.FlutterDebugSessionUtils; import io.flutter.ObservatoryConnector; import io.flutter.logging.PluginLogger; import io.flutter.run.FlutterPositionMapper; @@ -210,15 +211,13 @@ protected RunContentDescriptor runInDebugger(@NotNull TestLaunchState launcher, // Create the debug session. final XDebuggerManager manager = XDebuggerManager.getInstance(env.getProject()); - final XDebugSession session = manager.startSession(env, new XDebugProcessStarter() { + return FlutterDebugSessionUtils.startSessionAndGetDescriptor(manager, env, new XDebugProcessStarter() { @Override @NotNull public XDebugProcess start(@NotNull final XDebugSession session) { return new TestDebugProcess(env, session, executionResult, resolver, connector, mapper); } - }); - - return session.getRunContentDescriptor(); + }, false); } /** From 9c5e43b3bc9be12d6b7b938c26c1d7f626e63ece Mon Sep 17 00:00:00 2001 From: Helin Shiah Date: Tue, 31 Mar 2026 14:35:20 -0700 Subject: [PATCH 2/8] Extract helper in utils --- .../flutter/run/FlutterDebugSessionUtils.java | 37 ++++++++----------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/src/io/flutter/run/FlutterDebugSessionUtils.java b/src/io/flutter/run/FlutterDebugSessionUtils.java index 9d0523e962..13e1300f6f 100644 --- a/src/io/flutter/run/FlutterDebugSessionUtils.java +++ b/src/io/flutter/run/FlutterDebugSessionUtils.java @@ -18,17 +18,7 @@ public static RunContentDescriptor startSessionAndGetDescriptor( @NotNull XDebugProcessStarter starter, boolean muteBreakpoints) throws ExecutionException { try { - // Try to find the newSessionBuilder method (introduced in 2025.2+) - Method newSessionBuilderMethod = XDebuggerManager.class.getMethod("newSessionBuilder", XDebugProcessStarter.class); - Object builder = newSessionBuilderMethod.invoke(manager, starter); - - // builder.environment(env) - Method environmentMethod = builder.getClass().getMethod("environment", ExecutionEnvironment.class); - builder = environmentMethod.invoke(builder, env); - - // builder.startSession() -> returns XSessionStartedResult - Method startSessionMethod = builder.getClass().getMethod("startSession"); - Object sessionResult = startSessionMethod.invoke(builder); + Object sessionResult = buildAndStartSession(manager, env, starter); if (muteBreakpoints) { Method getSessionMethod = sessionResult.getClass().getMethod("getSession"); @@ -36,7 +26,6 @@ public static RunContentDescriptor startSessionAndGetDescriptor( session.setBreakpointMuted(true); } - // sessionResult.getRunContentDescriptor() Method getDescriptorMethod = sessionResult.getClass().getMethod("getRunContentDescriptor"); return (RunContentDescriptor) getDescriptorMethod.invoke(sessionResult); @@ -57,16 +46,8 @@ public static XDebugSession startSession( @NotNull ExecutionEnvironment env, @NotNull XDebugProcessStarter starter) throws ExecutionException { try { - Method newSessionBuilderMethod = XDebuggerManager.class.getMethod("newSessionBuilder", XDebugProcessStarter.class); - Object builder = newSessionBuilderMethod.invoke(manager, starter); - - Method environmentMethod = builder.getClass().getMethod("environment", ExecutionEnvironment.class); - builder = environmentMethod.invoke(builder, env); + Object sessionResult = buildAndStartSession(manager, env, starter); - Method startSessionMethod = builder.getClass().getMethod("startSession"); - Object sessionResult = startSessionMethod.invoke(builder); // This is XSessionStartedResult - - // Now get the session from the result Method getSessionMethod = sessionResult.getClass().getMethod("getSession"); return (XDebugSession) getSessionMethod.invoke(sessionResult); @@ -77,4 +58,18 @@ public static XDebugSession startSession( throw new ExecutionException("Failed to start debug session via reflection", e); } } + @NotNull + private static Object buildAndStartSession( + @NotNull XDebuggerManager manager, + @NotNull ExecutionEnvironment env, + @NotNull XDebugProcessStarter starter) throws Exception { + Method newSessionBuilderMethod = XDebuggerManager.class.getMethod("newSessionBuilder", XDebugProcessStarter.class); + Object builder = newSessionBuilderMethod.invoke(manager, starter); + + Method environmentMethod = builder.getClass().getMethod("environment", ExecutionEnvironment.class); + builder = environmentMethod.invoke(builder, env); + + Method startSessionMethod = builder.getClass().getMethod("startSession"); + return startSessionMethod.invoke(builder); + } } From 1742c65b6b1dba15620c7c32086062275eda1d26 Mon Sep 17 00:00:00 2001 From: Helin Shiah Date: Tue, 31 Mar 2026 15:14:11 -0700 Subject: [PATCH 3/8] Make methods static --- .../flutter/run/FlutterDebugSessionUtils.java | 29 +++++++++++++++---- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/src/io/flutter/run/FlutterDebugSessionUtils.java b/src/io/flutter/run/FlutterDebugSessionUtils.java index 13e1300f6f..231ff55982 100644 --- a/src/io/flutter/run/FlutterDebugSessionUtils.java +++ b/src/io/flutter/run/FlutterDebugSessionUtils.java @@ -12,6 +12,27 @@ public class FlutterDebugSessionUtils { + private static final Method newSessionBuilderMethod; + private static final Method environmentMethod; + private static final Method startSessionMethod; + + static { + Method nsb = null; + Method env = null; + Method ss = null; + try { + nsb = XDebuggerManager.class.getMethod("newSessionBuilder", XDebugProcessStarter.class); + Class builderClass = nsb.getReturnType(); + env = builderClass.getMethod("environment", ExecutionEnvironment.class); + ss = builderClass.getMethod("startSession"); + } catch (NoSuchMethodException e) { + // Fallback for older platforms + } + newSessionBuilderMethod = nsb; + environmentMethod = env; + startSessionMethod = ss; + } + public static RunContentDescriptor startSessionAndGetDescriptor( @NotNull XDebuggerManager manager, @NotNull ExecutionEnvironment env, @@ -63,13 +84,11 @@ private static Object buildAndStartSession( @NotNull XDebuggerManager manager, @NotNull ExecutionEnvironment env, @NotNull XDebugProcessStarter starter) throws Exception { - Method newSessionBuilderMethod = XDebuggerManager.class.getMethod("newSessionBuilder", XDebugProcessStarter.class); + if (newSessionBuilderMethod == null) { + throw new NoSuchMethodException("newSessionBuilder is not available"); + } Object builder = newSessionBuilderMethod.invoke(manager, starter); - - Method environmentMethod = builder.getClass().getMethod("environment", ExecutionEnvironment.class); builder = environmentMethod.invoke(builder, env); - - Method startSessionMethod = builder.getClass().getMethod("startSession"); return startSessionMethod.invoke(builder); } } From 6cfea4e3c05d2587cad9b7b56f73c6e8ccb5c81f Mon Sep 17 00:00:00 2001 From: Helin Shiah Date: Tue, 31 Mar 2026 15:18:57 -0700 Subject: [PATCH 4/8] Catch invocation target exception --- .../flutter/run/FlutterDebugSessionUtils.java | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/io/flutter/run/FlutterDebugSessionUtils.java b/src/io/flutter/run/FlutterDebugSessionUtils.java index 231ff55982..3f19c141bf 100644 --- a/src/io/flutter/run/FlutterDebugSessionUtils.java +++ b/src/io/flutter/run/FlutterDebugSessionUtils.java @@ -8,6 +8,7 @@ import com.intellij.xdebugger.XDebuggerManager; import org.jetbrains.annotations.NotNull; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class FlutterDebugSessionUtils { @@ -57,8 +58,14 @@ public static RunContentDescriptor startSessionAndGetDescriptor( session.setBreakpointMuted(true); } return session.getRunContentDescriptor(); + } catch (InvocationTargetException e) { + Throwable cause = e.getCause(); + if (cause instanceof ExecutionException) { + throw (ExecutionException) cause; + } + throw new ExecutionException("Failed to start debug session via reflection", cause != null ? cause : e); } catch (Exception e) { - throw new ExecutionException("Failed to start debug session via reflection", e); + throw new ExecutionException("Failed with unexpected reflection error", e); } } @@ -75,8 +82,14 @@ public static XDebugSession startSession( } catch (NoSuchMethodException e) { // Fallback to old API return manager.startSession(env, starter); + } catch (InvocationTargetException e) { + Throwable cause = e.getCause(); + if (cause instanceof ExecutionException) { + throw (ExecutionException) cause; + } + throw new ExecutionException("Failed to start debug session via reflection", cause != null ? cause : e); } catch (Exception e) { - throw new ExecutionException("Failed to start debug session via reflection", e); + throw new ExecutionException("Failed with unexpected reflection error", e); } } @NotNull From 6d40077a0543db839a9bf44a97a7b02eafebb8f4 Mon Sep 17 00:00:00 2001 From: Helin Shiah Date: Tue, 31 Mar 2026 15:33:32 -0700 Subject: [PATCH 5/8] Remove unused method --- .../flutter/run/FlutterDebugSessionUtils.java | 45 +++---------------- 1 file changed, 7 insertions(+), 38 deletions(-) diff --git a/src/io/flutter/run/FlutterDebugSessionUtils.java b/src/io/flutter/run/FlutterDebugSessionUtils.java index 3f19c141bf..45be17efa3 100644 --- a/src/io/flutter/run/FlutterDebugSessionUtils.java +++ b/src/io/flutter/run/FlutterDebugSessionUtils.java @@ -34,13 +34,18 @@ public class FlutterDebugSessionUtils { startSessionMethod = ss; } - public static RunContentDescriptor startSessionAndGetDescriptor( + public static @NotNull RunContentDescriptor startSessionAndGetDescriptor( @NotNull XDebuggerManager manager, @NotNull ExecutionEnvironment env, @NotNull XDebugProcessStarter starter, boolean muteBreakpoints) throws ExecutionException { try { - Object sessionResult = buildAndStartSession(manager, env, starter); + if (newSessionBuilderMethod == null) { + throw new NoSuchMethodException("newSessionBuilder is not available"); + } + Object builder = newSessionBuilderMethod.invoke(manager, starter); + builder = environmentMethod.invoke(builder, env); + Object sessionResult = startSessionMethod.invoke(builder); if (muteBreakpoints) { Method getSessionMethod = sessionResult.getClass().getMethod("getSession"); @@ -68,40 +73,4 @@ public static RunContentDescriptor startSessionAndGetDescriptor( throw new ExecutionException("Failed with unexpected reflection error", e); } } - - public static XDebugSession startSession( - @NotNull XDebuggerManager manager, - @NotNull ExecutionEnvironment env, - @NotNull XDebugProcessStarter starter) throws ExecutionException { - try { - Object sessionResult = buildAndStartSession(manager, env, starter); - - Method getSessionMethod = sessionResult.getClass().getMethod("getSession"); - return (XDebugSession) getSessionMethod.invoke(sessionResult); - - } catch (NoSuchMethodException e) { - // Fallback to old API - return manager.startSession(env, starter); - } catch (InvocationTargetException e) { - Throwable cause = e.getCause(); - if (cause instanceof ExecutionException) { - throw (ExecutionException) cause; - } - throw new ExecutionException("Failed to start debug session via reflection", cause != null ? cause : e); - } catch (Exception e) { - throw new ExecutionException("Failed with unexpected reflection error", e); - } - } - @NotNull - private static Object buildAndStartSession( - @NotNull XDebuggerManager manager, - @NotNull ExecutionEnvironment env, - @NotNull XDebugProcessStarter starter) throws Exception { - if (newSessionBuilderMethod == null) { - throw new NoSuchMethodException("newSessionBuilder is not available"); - } - Object builder = newSessionBuilderMethod.invoke(manager, starter); - builder = environmentMethod.invoke(builder, env); - return startSessionMethod.invoke(builder); - } } From 0bbab346b21a7bd00a1be5e1964f0b613656f5dd Mon Sep 17 00:00:00 2001 From: Helin Shiah Date: Tue, 31 Mar 2026 15:35:13 -0700 Subject: [PATCH 6/8] Add comment with TODO --- src/io/flutter/run/FlutterDebugSessionUtils.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/io/flutter/run/FlutterDebugSessionUtils.java b/src/io/flutter/run/FlutterDebugSessionUtils.java index 45be17efa3..1d778f16b7 100644 --- a/src/io/flutter/run/FlutterDebugSessionUtils.java +++ b/src/io/flutter/run/FlutterDebugSessionUtils.java @@ -11,6 +11,8 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +// TODO(helin24): This class is using reflection to find experimental APIs that are only present in platform versions 2025.3+. We should be +// able to use the APIs directly once we are only supporting versions past 2025.3. public class FlutterDebugSessionUtils { private static final Method newSessionBuilderMethod; From 75aa8d2803b5caed0a63bf733fb70eeec241d448 Mon Sep 17 00:00:00 2001 From: Helin Shiah Date: Tue, 31 Mar 2026 15:52:23 -0700 Subject: [PATCH 7/8] Add issue # --- src/io/flutter/run/FlutterDebugSessionUtils.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/io/flutter/run/FlutterDebugSessionUtils.java b/src/io/flutter/run/FlutterDebugSessionUtils.java index 1d778f16b7..a41a1971fe 100644 --- a/src/io/flutter/run/FlutterDebugSessionUtils.java +++ b/src/io/flutter/run/FlutterDebugSessionUtils.java @@ -1,3 +1,8 @@ +/* + * Copyright 2026 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ package io.flutter.run; import com.intellij.execution.ExecutionException; @@ -13,6 +18,7 @@ // TODO(helin24): This class is using reflection to find experimental APIs that are only present in platform versions 2025.3+. We should be // able to use the APIs directly once we are only supporting versions past 2025.3. +// See https://github.com/flutter/flutter-intellij/issues/8879. public class FlutterDebugSessionUtils { private static final Method newSessionBuilderMethod; From 474365e2da23f828aabba9a7c03b39c6abcb08d8 Mon Sep 17 00:00:00 2001 From: Helin Shiah Date: Tue, 31 Mar 2026 15:53:54 -0700 Subject: [PATCH 8/8] Add note to styleguide to check for copyright headers --- .gemini/styleguide.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.gemini/styleguide.md b/.gemini/styleguide.md index caf2b51937..c97d94e9fb 100644 --- a/.gemini/styleguide.md +++ b/.gemini/styleguide.md @@ -13,6 +13,8 @@ enforce standard modern Java/Kotlin coding conventions, but strictly police the - `[NIT]`: Idiomatic improvements or minor naming suggestions. - **Focus:** Prioritize logic, performance on the UI thread, and architectural consistency. - **No Empty Praise:** Do not leave "Looks good" or "Nice change" comments. If there are no issues, leave no comments. +- **Copyright Headers:** Ensure all new files have a proper copyright header (e.g., `Copyright 2026 The Chromium Authors`). Flag any missing + headers as `[MUST-FIX]`. ## 2. IntelliJ Platform Best Practices @@ -30,8 +32,10 @@ enforce standard modern Java/Kotlin coding conventions, but strictly police the as recommended by the modern SDK. - **Backward Compatibility:** Avoid using `@ApiStatus.Internal` or `@ApiStatus.ScheduledForRemoval` APIs unless strictly necessary. - **Logging:** - - Reject any use of `System.out.println` or `System.err.println` for logging in `src/` code (integration tests may use them for milestone logging). - - Enforce the use of the IntelliJ SDK's built-in logger (`com.intellij.openapi.diagnostic.Logger`) or our own (`io.flutter.logging.PluginLogger`). + - Reject any use of `System.out.println` or `System.err.println` for logging in `src/` code (integration tests may use them for + milestone logging). + - Enforce the use of the IntelliJ SDK's built-in logger (`com.intellij.openapi.diagnostic.Logger`) or our own ( + `io.flutter.logging.PluginLogger`). - **Actions:** - Classes extending `AnAction` must be completely stateless. Flag any `AnAction` class that defines mutable instance variables (fields), as the platform instantiates a single instance of the action for the lifetime of the IDE.