fix(java): show debugger startup progress and support cancellation (#546)

Java debug preparation ran before a session existed, so the toolbar and console stayed idle until jdtls had finished, and background console output waited for an animation frame.

- Create the Debug session as soon as the action starts, with progress in its console and a Stop that cancels preparation.
- Share launch preparation between Run and Debug, and move JDK and Lombok detection off the async runtime.
- Flush background console output on a timer as well as on animation frames, so a hidden window still receives it.
- Log how long each launch phase takes.

Refs #530
This commit is contained in:
l0ng-ai authored and GitHub committed 2026-10-31 15:02:39 +08:00
1 parent a2405c083a
commit 7610fc3f80
13 files changed
+374 -154

No files matched your search

+10 -11
View File
@@ -11,7 +11,6 @@ import {
IpcError,
nextRequestId,
type LaunchConfig,
type RequestId,
type RunConfig,
} from "@/platform/ipc";
import {
@@ -19,8 +18,8 @@ import {
isJvmApplication,
joinJavaArgs,
parseJavaArgs,
resolveJavaCommand,
resolveJavaModule,
prepareJavaLaunch,
type JavaPreparation,
} from "@/platform/javaLaunch";
import { notifications } from "@/workbench/contributions";
@@ -84,19 +83,20 @@ async function debugServer(module: string, signal?: AbortSignal): Promise<number
export async function javaLaunchConfig(
rc: RunConfig,
root: string,
preparation?: { request: RequestId; signal: AbortSignal },
preparation?: JavaPreparation,
): Promise<LaunchConfig> {
const cwd = rc.cwd ? join(root, rc.cwd) : root;
const parsed = parseJavaArgs(rc.args);
if (!parsed.mainClass) throw new Error(`No main class in '${rc.name}'`);
preparation?.signal.throwIfAborted();
const module = await resolveJavaModule(rc);
preparation?.signal.throwIfAborted();
const info = await call(
commands.javaResolveLaunch(module, parsed.classpath === "test", true, preparation?.request ?? nextRequestId()),
const { module, info, resolved } = await prepareJavaLaunch(
rc,
preparation ?? {
request: nextRequestId(),
signal: new AbortController().signal,
},
);
preparation?.signal.throwIfAborted();
if (info.errorCount > 0)
if (info.errorCount > 0 && !preparation?.report)
notifications().notify({
group: "run",
level: "warning",
@@ -104,7 +104,6 @@ export async function javaLaunchConfig(
title: `'${rc.name}' has compile errors; debugging the last compiled classes`,
body: formatCompileErrors(info).trimEnd(),
});
const resolved = resolveJavaCommand(rc, info);
const kotlin = rc.debugAdapter === KOTLIN_ADAPTER || (await moduleHasKotlinSources(join(root, module)));
preparation?.signal.throwIfAborted();
const classPaths = parsed.explicitClasspath