дзеркало
https://github.com/l0ng-ai/openidea.git
синхронізовано 2026-11-09 19:25:54 +02:00
feat(app): ask before closing with running Run/Debug processes (#1445)
Closing a window, closing or switching a project, or quitting while Run/Debug sessions are still running now asks before terminating them (Terminate / Cancel / Don't ask again, sharing run.confirmTerminate with the Run tab prompt). Run publishes its live sessions through a RunningProcessesProvider contribution, so the close guard collects them without importing the run feature; the prompt comes before the unsaved-changes step so cancelling never discards edits. Closes #1433 Co-authored-by: ppxb <[email protected]>
Цей коміт міститься в:
1 джерело
3e88fa1f39
коміт
43af99d3ec
7 files changed
+223
-7
No files matched your search
@@ -1459,6 +1459,42 @@ export const useDebugRunSessions = create<{
|
||||
Console: ComponentType<{ sessionId: number }> | null;
|
||||
}>(() => ({ sessions: [], serviceSessions: [], Console: null }));
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Processes a closing window or project would terminate. Features that own
|
||||
// child processes (Run/Debug sessions) publish them here, so the close guard can
|
||||
// ask first (IDEA's "Terminate processes?").
|
||||
|
||||
export interface RunningProcess {
|
||||
/** Configuration or session name shown in the prompt. */
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface RunningProcessesProvider {
|
||||
id: string;
|
||||
/** Processes this feature is running in the window right now. */
|
||||
running(): RunningProcess[] | Promise<RunningProcess[]>;
|
||||
}
|
||||
|
||||
const runningProcessesProviders = new Map<string, RunningProcessesProvider>();
|
||||
|
||||
export function registerRunningProcessesProvider(p: RunningProcessesProvider): () => void {
|
||||
runningProcessesProviders.set(p.id, p);
|
||||
return () => {
|
||||
if (runningProcessesProviders.get(p.id) === p) runningProcessesProviders.delete(p.id);
|
||||
};
|
||||
}
|
||||
|
||||
/** Live processes of every provider, one entry per name. */
|
||||
export async function runningProcesses(): Promise<RunningProcess[]> {
|
||||
const all = (await Promise.all([...runningProcessesProviders.values()].map((p) => p.running()))).flat();
|
||||
const seen = new Set<string>();
|
||||
return all.filter((p) => {
|
||||
if (!p.name || seen.has(p.name)) return false;
|
||||
seen.add(p.name);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// "Get from Version Control" sources besides a plain URL (IDEA: GitHub, GitLab…),
|
||||
// listed in the Clone Repository dialog's sidebar.
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import { expect, it, vi } from "vitest";
|
||||
import { registerRunningProcessesProvider, runningProcesses } from "./contributions";
|
||||
|
||||
it("collects live processes from every provider, one entry per name", async () => {
|
||||
const first = vi.fn(async () => [{ name: "app" }, { name: "worker" }]);
|
||||
const second = vi.fn(() => [{ name: "app" }, { name: "docs" }]);
|
||||
const disposers = [
|
||||
registerRunningProcessesProvider({ id: "test.first", running: first }),
|
||||
registerRunningProcessesProvider({ id: "test.second", running: second }),
|
||||
];
|
||||
try {
|
||||
expect(await runningProcesses()).toEqual([{ name: "app" }, { name: "worker" }, { name: "docs" }]);
|
||||
expect(first).toHaveBeenCalledOnce();
|
||||
expect(second).toHaveBeenCalledOnce();
|
||||
} finally {
|
||||
disposers.forEach((dispose) => dispose());
|
||||
}
|
||||
expect(await runningProcesses()).toEqual([]);
|
||||
});
|
||||
|
||||
it("keeps a replacement provider registered when its predecessor is disposed", async () => {
|
||||
const disposeOld = registerRunningProcessesProvider({ id: "test.replaced", running: async () => [] });
|
||||
const disposeNew = registerRunningProcessesProvider({
|
||||
id: "test.replaced",
|
||||
running: async () => [{ name: "app" }],
|
||||
});
|
||||
try {
|
||||
disposeOld();
|
||||
expect(await runningProcesses()).toEqual([{ name: "app" }]);
|
||||
} finally {
|
||||
disposeOld();
|
||||
disposeNew();
|
||||
}
|
||||
expect(await runningProcesses()).toEqual([]);
|
||||
});
|
||||
Reference in new issue
Заблокувати користувача