import { useMemo, useState } from "react"; import type { RunTargetValid } from "@/platform/remoteSettings"; import { useWorkflowSettings } from "@/platform/workflowSettings"; import { EmptyState, IconButton, SplitGroup, SplitHandle, SplitPanel, Tabs, type TabItem, T } from "@/ui"; import { executeAction } from "@/platform/actions"; import { formatBinding, hostBinding, shortcutLabel } from "@/platform/keymap"; import { ConsoleModel } from "./console"; import { ConsoleView } from "./ConsoleView"; import { TerminalConsole } from "./TerminalConsole"; import { runDirectory } from "./navigate"; import { remoteLabel } from "./remote"; import { acceptsInput, clearConsole, closeSessionTab, closeStdin, failedTests, rerunFailed, rerunSession, resizeTerminal, runSelectedTests, runtime, sendStdin, setPendingInput, stopSession, useRun, writeTerminal, type RunSession, } from "./store"; import { TestTreeView, formatDuration } from "./TestTreeView"; import { StackTracePanel, closeStackTrace, useStackTraces, type StackTraceTab } from "./stackTrace"; import { showApplicationUpdate, useApplicationUpdates } from "./applicationUpdate"; import { isUpdatable } from "./updatePolicies"; import styles from "./Run.module.css"; import { tx } from "@/platform/i18n"; function failed(s: RunSession): boolean { return s.status === "failed" || (s.status === "finished" && s.exitCode !== 0 && !s.stopped); } 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"; } export function SessionPanel({ session, onRerun, onStop, }: { session: RunSession; onRerun?: (() => void) | null; onStop?: () => void; }) { const updating = useApplicationUpdates((s) => s.ids.has(session.id)); const version = useRun((s) => s.versions[session.id] ?? 0); const rt = runtime(session.id); const [follow, setFollow] = useState(true); // Settings › Console › Use soft wraps is the default; the toolbar toggle overrides it for this tab. const defaultWrap = useWorkflowSettings((s) => s.console.softWrap); const [wrapOverride, setWrapOverride] = useState(null); const softWrap = wrapOverride ?? defaultWrap; const [selectedTest, setSelectedTest] = useState(null); const [findOpen, setFindOpen] = useState(false); const running = session.status === "running" || session.status === "starting"; const cwd = runDirectory(session.config.cwd); // Selecting a test with captured output shows just that output (like IDEA). const testModel = useMemo(() => { const node = selectedTest ? rt?.tests?.get(selectedTest) : undefined; if (!node || node.output.length === 0) return null; const m = new ConsoleModel(); m.append({ kind: "stdout", text: node.output.join("\n") + "\n" }); return m; }, [selectedTest, rt, version]); // eslint-disable-line react-hooks/exhaustive-deps if (!rt) return null; const toggleWrap = () => setWrapOverride(!softWrap); const consoleColumn = (
{rt.console.dropped > 0 && !testModel && (
{tx`${rt.console.dropped.toLocaleString()} earlier lines were discarded`}
)} {testModel && (
)} {rt.terminal && !testModel ? ( writeTerminal(session.id, data)} onResize={(cols, rows) => resizeTerminal(session.id, cols, rows)} aria-label={tx`${session.config.name} terminal`} /> ) : ( clearConsole(session.id)} input={ !testModel && acceptsInput(session) ? { pending: rt.input, onChange: (text) => setPendingInput(session.id, text), onSubmit: (line) => void sendStdin(session.id, line), onEof: () => void closeStdin(session.id), } : undefined } aria-label={tx`${session.config.name} output`} /> )}
); return (
rerunSession(session.id))} /> {rt.tests && ( rerunFailed(session.id)} /> )} void stopSession(session.id))} /> {isUpdatable(session.config) && ( showApplicationUpdate(session.id)} /> )}
{!rt.terminal && ( <> setFindOpen((v) => !v)} /> setFollow((v) => !v)} /> )} clearConsole(session.id)} />
void executeAction("run.editConfigs", session.config.id)} />
{rt.tests ? ( void runSelectedTests(session.id, ids, debug)} /> {consoleColumn} ) : ( consoleColumn )}
); } function sessionItems(sessions: readonly RunSession[], targets: readonly RunTargetValid[]): TabItem[] { return sessions.map((s) => ({ id: s.id, label: remoteLabel(s.config, targets), text: remoteLabel(s.config, targets), icon: sessionIcon(s), color: failed(s) ? "var(--danger)" : undefined, closable: true, tooltip: s.status === "finished" ? tx`Finished in ${formatDuration((s.endedAt ?? s.startedAt) - s.startedAt)}, exit code ${s.exitCode ?? "?"}` : undefined, })); } /** The selected tab: a run session or an analyzed stack trace (IDEA shows both in Run). */ function currentTab( sessions: readonly RunSession[], traces: readonly StackTraceTab[], active: string | null, ): { session: RunSession } | { trace: StackTraceTab } | null { const session = sessions.find((s) => s.id === active); if (session) return { session }; const trace = traces.find((t) => t.id === active); if (trace) return { trace }; if (sessions.length) return { session: sessions[sessions.length - 1]! }; return traces.length ? { trace: traces[traces.length - 1]! } : null; } /** Session (and stack trace) tabs in the tool window header. */ export function RunTabs() { const sessions = useRun((s) => s.sessions); const traces = useStackTraces((s) => s.tabs); const runTargets = useWorkflowSettings((s) => s.runTargets); const active = useRun((s) => s.activeSession); const current = currentTab(sessions, traces, active); if (!current) return null; const items: TabItem[] = [ ...sessionItems(sessions, runTargets), ...traces.map((t) => ({ id: t.id, label: t.title, text: t.title, closable: true })), ]; return ( useRun.setState({ activeSession: id })} onClose={(id) => (traces.some((t) => t.id === id) ? closeStackTrace(id) : void closeSessionTab(id))} panelIdPrefix="openidea-run" /> ); } export function RunView() { const sessions = useRun((s) => s.sessions); const traces = useStackTraces((s) => s.tabs); const active = useRun((s) => s.activeSession); const current = currentTab(sessions, traces, active); if (!current) { // IDEA's empty Run tool window: the three ways to run something. const chooser = shortcutLabel("run.chooseConfig"); return (
To run your code, do one of the following:
  • Click the Run icon in the editor gutter
  • {'Select "Run…" in the editor context menu'}
  • Launch a{" "} {chooser && ({chooser})}
); } const id = "session" in current ? current.session.id : current.trace.id; return (
{"session" in current ? ( ) : ( )}
); }