feat(run): Stop lists the running processes with Stop All when several run (#1113)

Stop from the shortcut, menu or Find Action stopped the selected configuration's process whenever it was running, so with several processes there was no way to pick another or stop them all. Like IDEA's Stop it now stops the only stoppable process or lists them all with Stop All; the run widget's stop button keeps stopping the selected configuration and the Run tool window its active tab.
Цей коміт міститься в:
l0ng-ai authored and GitHub committed 2026-11-04 07:23:09 +08:00
1 джерело cd77c9c6fb
коміт 2bb8f3b091
3 files changed
+51 -16

No files matched your search

+32 -3
Переглянути файл
@@ -3,7 +3,8 @@ import { cleanup, fireEvent, render, screen } from "@testing-library/react";
import { executeAction } from "@/platform/actions";
import { setContext } from "@/platform/context";
import type { RunConfig } from "@/platform/ipc";
import { useDebugRunSessions, type DebugRunSession } from "@/workbench/contributions";
import { registerMenuGroup, useDebugRunSessions, type DebugRunSession } from "@/workbench/contributions";
import { useContextMenuStore } from "@/ui";
import { applicationConfig } from "./application";
import { activate } from "./index";
import { RunWidget } from "./RunWidget";
@@ -93,11 +94,39 @@ it("targets the selected debug session while npm runs, through buttons and short
await executeAction("run.run");
expect(session.rerun).toHaveBeenCalledTimes(2);
fireEvent.click(screen.getByRole("button", { name: `Stop '${app.name}'` }));
await executeAction("run.stop");
expect(session.stop).toHaveBeenCalledTimes(2);
await vi.waitFor(() => expect(session.stop).toHaveBeenCalledOnce());
expect(useRun.getState().sessions[0]?.stopped).toBeUndefined();
});
it("lists every running process with Stop All for the Stop shortcut, like IDEA", async () => {
const session = debug(app);
useRun.setState({ sessions: [run(npm, 3)] });
useDebugRunSessions.setState({ sessions: [session] });
const unregister = registerMenuGroup({
id: "test.stopDebug",
menus: ["stopProcess"],
items: () => [{ label: app.name, run: () => session.stop() }],
});
try {
render(<RunWidget />);
await executeAction("run.stop");
const items = useContextMenuStore.getState().menu!.items;
expect(items.map((i) => ("label" in i ? i.label : i.kind))).toEqual([
"Stop Process",
npm.name,
app.name,
"separator",
"Stop All",
]);
const stopAll = items.at(-1) as { run: () => void };
stopAll.run();
expect(session.stop).toHaveBeenCalledOnce();
expect(useRun.getState().sessions[0]?.stopped).toBe(true);
} finally {
unregister();
}
});
it("ignores an unrelated debug session for an idle selection", () => {
useDebugRunSessions.setState({ sessions: [debug(npm)] });
render(<RunWidget />);
+1 -1
Переглянути файл
@@ -144,7 +144,7 @@ export function RunWidget() {
className={clsx(styles.widgetButton, styles.widgetFilled, styles.widgetFilledStop)}
title={active || count === 1 ? tx`Stop '${stopName ?? ""}'` : tx`Stop (${count} running)`}
shortcut={shortcutLabel("run.stop")}
onClick={() => void executeAction("run.stop")}
onClick={() => void executeAction("run.stop", "widget")}
/>
)}
<IconButton
+18 -12
Переглянути файл
@@ -195,7 +195,12 @@ function configFromArg(arg: unknown): RunConfig | undefined {
* Stop: in the Run or Debug tool window, the selected tab's process; otherwise the selected
* configuration's session, or a chooser of every running process.
*/
function stopAction() {
/**
* Stop. In the Run tool window: its active tab. From the run widget (`arg` "widget"): the selected
* configuration's process. Elsewhere (shortcut, menu, Find Action), like IDEA's Stop: the only
* stoppable process, or a list of them with Stop All when there are several.
*/
function stopAction(arg?: unknown) {
const ctx = getContext();
if (ctx.toolWindowFocus && ctx.activeToolWindow === "debug" && getAction("debug.stop")) {
void executeAction("debug.stop");
@@ -210,16 +215,17 @@ function stopAction() {
}
}
const debug = useDebugRunSessions.getState().sessions;
const selected = selectedConfig();
const compound = compoundTargets(selected, running, debug);
if (compound.length) {
compound.forEach((s) => s.stop());
return;
}
const target = selectedTarget(useRun.getState().selectedId, running, debug);
if (target) {
target.stop();
return;
if (arg === "widget") {
const compound = compoundTargets(selectedConfig(), running, debug);
if (compound.length) {
compound.forEach((s) => s.stop());
return;
}
const target = selectedTarget(useRun.getState().selectedId, running, debug);
if (target) {
target.stop();
return;
}
}
const items = [
...running.map((s): MenuItem => ({
@@ -357,7 +363,7 @@ export function activate(): () => void {
category: "Run",
icon: "stop",
when: "projectOpen",
run: () => stopAction(),
run: ({ arg }) => stopAction(arg),
},
{
id: "run.rerun",