дзеркало
https://github.com/l0ng-ai/openidea.git
синхронізовано 2026-11-08 19:02:17 +02:00
feat(run): session tab icons and a Debug button in the run panel (#1283)
A finished run session's tab keeps its configuration's own icon instead of the green run arrow (a failed exit still shows the error icon), and the run panel's toolbar gets a Debug button next to Rerun that starts the same configuration under the debugger (in Services, a debug session reruns its debugger launch). Co-authored-by: qtvbwfn <[email protected]>
Цей коміт міститься в:
1 джерело
78a3768ec1
коміт
19c2d40528
4 files changed
+115
-2
No files matched your search
@@ -0,0 +1,82 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { RunConfig } from "@/platform/ipc";
|
||||
import { debugSession, resetRuns, useRun, type RunSession } from "./store";
|
||||
import { sessionIcon } from "./RunView";
|
||||
|
||||
const actions = vi.hoisted(() => ({ executeAction: vi.fn() }));
|
||||
vi.mock("@/platform/actions", async (orig) => ({ ...(await orig<object>()), executeAction: actions.executeAction }));
|
||||
|
||||
const config = (extra: Partial<RunConfig> = {}): RunConfig => ({
|
||||
id: "c",
|
||||
name: "c",
|
||||
kind: "spring-boot",
|
||||
command: "mvn",
|
||||
args: [],
|
||||
cwd: null,
|
||||
env: {},
|
||||
detected: false,
|
||||
isTest: false,
|
||||
debugAdapter: null,
|
||||
...extra,
|
||||
});
|
||||
|
||||
const session = (extra: Partial<RunSession> = {}): RunSession => ({
|
||||
id: "s1",
|
||||
config: config(),
|
||||
runId: null,
|
||||
status: "finished",
|
||||
exitCode: 0,
|
||||
startedAt: 0,
|
||||
endedAt: 1,
|
||||
stdinClosed: false,
|
||||
...extra,
|
||||
});
|
||||
|
||||
describe("sessionIcon", () => {
|
||||
it("shows the execution arrow while running, the check for tests", () => {
|
||||
expect(sessionIcon(session({ status: "running" }))).toBe("run");
|
||||
expect(sessionIcon(session({ status: "starting" }))).toBe("run");
|
||||
expect(sessionIcon(session({ status: "starting", config: config({ isTest: true }) }))).toBe("check");
|
||||
});
|
||||
|
||||
it("shows the red error icon for a failed exit", () => {
|
||||
expect(sessionIcon(session({ status: "failed" }))).toBe("error");
|
||||
expect(sessionIcon(session({ exitCode: 1 }))).toBe("error");
|
||||
});
|
||||
|
||||
it("shows the configuration's own icon once the process ended (IDEA's third state)", () => {
|
||||
expect(sessionIcon(session())).toBe("spring-boot");
|
||||
expect(sessionIcon(session({ config: config({ kind: "program" }) }))).toBe("file-binary");
|
||||
expect(sessionIcon(session({ config: config({ kind: "npm" }) }))).toBe("file-npm");
|
||||
});
|
||||
|
||||
it("never shows the error icon for a session the user stopped", () => {
|
||||
expect(sessionIcon(session({ exitCode: 1, stopped: true }))).toBe("spring-boot");
|
||||
});
|
||||
});
|
||||
|
||||
describe("debugSession", () => {
|
||||
beforeEach(() => {
|
||||
resetRuns();
|
||||
actions.executeAction.mockClear();
|
||||
});
|
||||
|
||||
it("launches the session's configuration through run.debugConfig", () => {
|
||||
const c = config();
|
||||
useRun.setState({ sessions: [session({ config: c })] });
|
||||
debugSession("s1");
|
||||
expect(actions.executeAction).toHaveBeenCalledWith("run.debugConfig", c);
|
||||
});
|
||||
|
||||
it("prefers the original configuration of a derived run", () => {
|
||||
const base = config({ id: "base" });
|
||||
useRun.setState({ sessions: [session({ config: config({ id: "derived" }), baseConfig: base })] });
|
||||
debugSession("s1");
|
||||
expect(actions.executeAction).toHaveBeenCalledWith("run.debugConfig", base);
|
||||
});
|
||||
|
||||
it("ignores unknown session ids", () => {
|
||||
debugSession("nope");
|
||||
expect(actions.executeAction).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
clearConsole,
|
||||
closeSessionTab,
|
||||
closeStdin,
|
||||
debugSession,
|
||||
failedTests,
|
||||
rerunFailed,
|
||||
rerunSession,
|
||||
@@ -31,6 +32,7 @@ import { TestTreeView, formatDuration } from "./TestTreeView";
|
||||
import { StackTracePanel, closeStackTrace, useStackTraces, type StackTraceTab } from "./stackTrace";
|
||||
import { showApplicationUpdate, useApplicationUpdates } from "./applicationUpdate";
|
||||
import { isUpdatable } from "./updatePolicies";
|
||||
import { configIcon } from "./configs";
|
||||
import styles from "./Run.module.css";
|
||||
import { tx } from "@/platform/i18n";
|
||||
|
||||
@@ -38,19 +40,28 @@ function failed(s: RunSession): boolean {
|
||||
return s.status === "failed" || (s.status === "finished" && s.exitCode !== 0 && !s.stopped);
|
||||
}
|
||||
|
||||
function sessionIcon(s: RunSession): string {
|
||||
/**
|
||||
* Tab icon, IDEA's three states: the green execution arrow while the process is alive,
|
||||
* the red error badge for a failed exit, and once the process ended the run
|
||||
* configuration's own icon (Services keeps the same model, see services.tsx).
|
||||
*/
|
||||
export function sessionIcon(s: RunSession): string {
|
||||
if (s.status === "running" || s.status === "starting") return s.config.isTest ? "check" : "run";
|
||||
if (failed(s)) return "error";
|
||||
return s.config.isTest ? "check" : "run";
|
||||
// Ended (exit or stop): the configuration's own icon — the green arrow stays "still running".
|
||||
return configIcon(s.config);
|
||||
}
|
||||
|
||||
export function SessionPanel({
|
||||
session,
|
||||
onRerun,
|
||||
onDebug,
|
||||
onStop,
|
||||
}: {
|
||||
session: RunSession;
|
||||
onRerun?: (() => void) | null;
|
||||
/** Override the Debug button (Services: rerun the attached debugger's launch); null disables it. */
|
||||
onDebug?: (() => void) | null;
|
||||
onStop?: () => void;
|
||||
}) {
|
||||
const updating = useApplicationUpdates((s) => s.ids.has(session.id));
|
||||
@@ -136,6 +147,15 @@ export function SessionPanel({
|
||||
disabled={onRerun === null}
|
||||
onClick={onRerun ?? (() => rerunSession(session.id))}
|
||||
/>
|
||||
{/* Switch the launch mode: the same configuration started under the debugger. */}
|
||||
<IconButton
|
||||
icon="debug"
|
||||
iconSize={14}
|
||||
title="Debug"
|
||||
shortcut={shortcutLabel("run.debug")}
|
||||
disabled={onDebug === null}
|
||||
onClick={onDebug ?? (() => void debugSession(session.id))}
|
||||
/>
|
||||
{rt.tests && (
|
||||
<IconButton
|
||||
icon="rerun"
|
||||
|
||||
@@ -387,6 +387,7 @@ function RunDetail({ item }: { item: ServiceTreeItem }) {
|
||||
key={execution.run!.id}
|
||||
session={execution.run!}
|
||||
onRerun={d ? d.rerun : undefined}
|
||||
onDebug={d ? d.rerun : undefined}
|
||||
onStop={d ? () => void stopServiceSessions([execution]) : undefined}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -1063,6 +1063,16 @@ export function rerunSession(sessionId: string) {
|
||||
if (session) void startRun(session.config, sessionId, session.baseConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* "Debug": relaunches the session's configuration in debug mode — the same
|
||||
* `run.debugConfig` entry the Services view and the editor context menu use
|
||||
* (`baseConfig` keeps the original configuration for derived launches).
|
||||
*/
|
||||
export function debugSession(sessionId: string) {
|
||||
const session = getSession(sessionId);
|
||||
if (session) void executeAction("run.debugConfig", session.baseConfig ?? session.config);
|
||||
}
|
||||
|
||||
/** Paths of the failed tests of a session's last run. */
|
||||
export function failedTests(sessionId: string): string[][] {
|
||||
const tests = runtimes.get(sessionId)?.tests;
|
||||
|
||||
Reference in new issue
Заблокувати користувача