import { useRef, useState } from "react"; import clsx from "clsx"; import { BarButton, Icon, IconButton, Menu, type MenuItem } from "@/ui"; import { executeAction } from "@/platform/actions"; import { useContextKeys } from "@/platform/context"; import { useTx } from "@/platform/i18n"; import { shortcutLabel } from "@/platform/keymap"; import { runExecutors, useRunExecutors } from "@/workbench/contributions"; import { configIcon } from "./configs"; import { CURRENT_FILE, rerunSession, sessionsOf, startRun, stopSession, useRun } from "./store"; import styles from "./Run.module.css"; /** Title bar: configuration chooser, Run / Debug, and Rerun / Stop while running. */ export function RunWidget() { const tx = useTx(); const configs = useRun((s) => s.configs); const selectedId = useRun((s) => s.selectedId); const sessions = useRun((s) => s.sessions); const chooserOpen = useRun((s) => s.chooserOpen); const selected = configs.find((c) => c.id === selectedId); const currentFile = selectedId === CURRENT_FILE; const running = sessions.filter((s) => s.status === "running" || s.status === "starting"); // A compound is running while any of its configurations is; Stop stops them all. const compoundRunning = selected?.kind === "compound" ? sessionsOf(selected, running) : []; const runningSelected = compoundRunning[0] ?? running.find((s) => s.config.id === selectedId) ?? null; const target = runningSelected ?? running[running.length - 1] ?? null; // Debug sessions belong to the debugger feature; like IDEA the widget offers their Rerun / Stop. const debugging = useContextKeys((c) => !!c.debugSessionActive); const canRerunDebug = useContextKeys((c) => !!c.debugCanRerun); const debugPaused = useContextKeys((c) => !!c.debugSessionPaused); const active = !!runningSelected || debugging; const moreRef = useRef(null); const [moreOpen, setMoreOpen] = useState(false); const executors = useRunExecutors((s) => s.executors); // IDEA: the selected configuration's other executors (Run with Coverage, Profile) come first. const executorItems: MenuItem[] = selected ? runExecutors(executors) .filter((e) => e.supports(selected)) .map((e) => ({ kind: "action", action: e.action, label: e.label(selected.name), icon: e.icon })) : []; const moreItems: MenuItem[] = [ ...executorItems, ...(executorItems.length ? [{ kind: "separator" } as const] : []), { kind: "action", action: "run.runContext" }, { kind: "action", action: "run.debugContext" }, { kind: "separator" }, { kind: "action", action: "run.editConfigs" }, { kind: "action", action: "run.reloadConfigs" }, ]; return (
void executeAction("run.chooseConfig", "select")} > {selected && ( // IDEA: the configuration's type icon, with a status dot on its top-right corner while active. {active && ( )} )} {selected?.name ?? tx(currentFile ? "Current File" : "Select Configuration")} {target ? ( <> (compoundRunning.length && selected ? void startRun(selected) : rerunSession(target.id))} /> 1 ? `Stop (${running.length} running)` : `Stop '${target.config.name}'` } shortcut={shortcutLabel("run.stop")} onClick={() => compoundRunning.length ? compoundRunning.forEach((s) => void stopSession(s.id)) : running.length > 1 ? void executeAction("run.stop") : void stopSession(target.id) } /> ) : debugging ? ( // IDEA while debugging: Resume (when paused), Rerun Debug and Stop as filled buttons. <> {debugPaused && ( void executeAction("debug.resume")} /> )} void executeAction("debug.rerun")} /> void executeAction("debug.stop")} /> ) : ( <> void executeAction("run.run")} /> void executeAction("run.debug")} /> )} setMoreOpen((v) => !v)} /> setMoreOpen(false)} />
); }