From 2bb8f3b091e11c82504925d63f4183ef84aa3881 Mon Sep 17 00:00:00 2001
From: l0ng-ai <24760907+l0ng-ai@users.noreply.github.com>
Date: Wed, 4 Nov 2026 07:23:09 +0800
Subject: [PATCH] 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.
---
src/features/run/RunWidget.test.tsx | 35 ++++++++++++++++++++++++++---
src/features/run/RunWidget.tsx | 2 +-
src/features/run/index.ts | 30 +++++++++++++++----------
3 files changed, 51 insertions(+), 16 deletions(-)
diff --git a/src/features/run/RunWidget.test.tsx b/src/features/run/RunWidget.test.tsx
index abd5fdea8..858fa3b3e 100644
--- a/src/features/run/RunWidget.test.tsx
+++ b/src/features/run/RunWidget.test.tsx
@@ -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();
+ 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();
diff --git a/src/features/run/RunWidget.tsx b/src/features/run/RunWidget.tsx
index bfb150cd8..2254b24b8 100644
--- a/src/features/run/RunWidget.tsx
+++ b/src/features/run/RunWidget.tsx
@@ -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")}
/>
)}
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",