From a8e12ff7c42784d3b9d2cecf3916f72c8dafec34 Mon Sep 17 00:00:00 2001 From: ntbang0901 <121003585+ntbang0901@users.noreply.github.com> Date: Mon, 2 Nov 2026 13:57:15 +0700 Subject: [PATCH] feat(run): reuse the configuration of a main class run from its context (#783) Running or debugging a main class from the editor, gutter or Project view now selects an existing saved Application or Spring Boot configuration of the same main class and module, keeping its VM options, environment and profiles, instead of creating a temporary one. Co-authored-by: ntbang0901 <121003585+ntbang0901@users.noreply.github.com> --- src/features/run/java.test.ts | 19 +++++++++++++++++++ src/features/run/store.ts | 12 +++++++++++- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/features/run/java.test.ts b/src/features/run/java.test.ts index 586f3f8c..161e1017 100644 --- a/src/features/run/java.test.ts +++ b/src/features/run/java.test.ts @@ -395,6 +395,25 @@ describe("temporary configurations", () => { expect(useRun.getState().temporary).toEqual([]); }); + it("reuse a configuration of the same main class and module, whatever its id", () => { + const own = (module: string) => + cfg({ + id: `user:${module}`, + kind: "spring-boot", + name: `Mine ${module}`, + args: ["-Xmx1g", "-cp", "${classpath}", "a.Main"], + module, + detected: false, + }); + useRun.setState({ configs: [own("other"), own("app")] }); + expect(adoptTemporary(applicationConfig({ className: "a.Main" }, "app/src/main/java/a/Main.java")).name).toBe( + "Mine app", + ); + expect(useRun.getState().temporary).toEqual([]); + const fresh = adoptTemporary(applicationConfig({ className: "a.Other" }, "app/src/main/java/a/Other.java")); + expect(useRun.getState().temporary).toEqual([fresh.id]); + }); + it("offer Save in the chooser while selected", async () => { const app = adoptTemporary(applicationConfig({ className: "a.Main" }, "src/main/java/a/Main.java")); const { configs, temporary } = useRun.getState(); diff --git a/src/features/run/store.ts b/src/features/run/store.ts index 38a3c1b3..3d7f3552 100644 --- a/src/features/run/store.ts +++ b/src/features/run/store.ts @@ -256,6 +256,13 @@ function persistTemporary() { ); } +/** IDEA's `isConfigurationFromContext` for an Application: the same main class of the same module. */ +function sameApplication(a: RunConfig, b: RunConfig): boolean { + if (!isJvmApplication(a) || !isJvmApplication(b)) return false; + const main = parseJavaArgs(a.args).mainClass; + return main !== null && main === parseJavaArgs(b.args).mainClass && (a.module ?? a.cwd) === (b.module ?? b.cwd); +} + /** * Selects the configuration for a gutter / context run: a saved or detected * configuration with the same id is reused, otherwise `config` becomes a @@ -263,7 +270,10 @@ function persistTemporary() { */ export function adoptTemporary(config: RunConfig): RunConfig { const state = useRun.getState(); - const existing = state.configs.find((c) => c.id === config.id); + // IDEA's `findExistingConfiguration`: a configuration the user keeps for this context wins. + const existing = + state.configs.find((c) => c.id === config.id) ?? + state.configs.find((c) => !state.temporary.includes(c.id) && sameApplication(c, config)); if (existing && !state.temporary.includes(config.id)) { selectConfig(existing.id); return existing;