Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ The debugger will automatically attach. See [No-Config Debug Documentation](bund

No-Config Debug is enabled by default. To disable the terminal integration and the AI `debug_java_application` tool, set `"java.debug.settings.enableNoConfigDebug": false`, reload VS Code, and recreate existing terminals. Standard Java launch/attach debugging, including F5 and Run/Debug CodeLens, remains available.

No-Config Debug prepares its terminal integration in the background without delaying core Run/Debug registration. The AI launch tool waits for it to be ready (up to 60 seconds, cancellable). A terminal opened before preparation finishes may need to be recreated to receive the environment contributions.

## AI-Assisted Debugging

When using GitHub Copilot Chat, you can now ask AI to help you debug Java applications! The extension provides a Language Model Tool that enables natural language debugging:
Expand Down
2 changes: 2 additions & 0 deletions bundled/agents/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,8 @@ Make sure the Java project is properly loaded. Check that:

The `debug_java_application` tool requires `java.debug.settings.enableNoConfigDebug` (enabled by default). If you disable this setting, reload VS Code and recreate existing terminals. The launch tool then returns an explanatory message without running `debugjava`; tools that inspect or control existing debug sessions remain available.

The launch tool also waits for No-Config Debug initialization to finish before building, creating a terminal, or stopping an existing session. This wait is cancellable and limited to 60 seconds. A timeout does not stop background initialization; you can retry later. Initialization failures are reported without attempting to launch.

Ensure:
- Your project compiles successfully
- No other debug session is running
Expand Down
14 changes: 12 additions & 2 deletions bundled/scripts/noConfigScripts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,23 @@ This feature enables configuration-less debugging for Java applications, similar

## How It Works

When you open a terminal in VS Code with this extension installed, the following environment variables are automatically set:
Once No-Config Debug initialization finishes, newly opened VS Code terminals receive the following environment contributions:

- `VSCODE_JDWP_ADAPTER_ENDPOINTS`: Path to a communication file for port exchange
- `PATH`: Includes the `debugjava` command wrapper

Note: `JAVA_TOOL_OPTIONS` is NOT set globally to avoid affecting other Java tools (javac, maven, gradle). Instead, it's set only when you run the `debugjava` command.

### Startup readiness

The extension registers core Java Run/Debug support first, then starts No-Config Debug initialization in the background. Ordinary launch/attach registration and extension activation do not wait for endpoint storage, Java executable discovery, or wrapper permission preparation.

The AI `debug_java_application` tool waits for the shared initialization task before inspecting the launch input, building, creating a terminal, or stopping an existing debug session. Each wait is cancellable and limited to 60 seconds. Cancelling or timing out one invocation does not cancel initialization or another invocation's wait; a later invocation can retry. Initialization failure or extension disposal returns an explanatory result rather than attempting a launch.

This is not lazy terminal setup: preparation still starts during activation. However, activation completing does not guarantee that `debugjava` is ready. Terminals opened before preparation finishes may lack the environment contributions and must be recreated afterward. Existing terminals are not automatically closed or repaired.

On disposal, listeners are released immediately even if initialization is still pending. Already-started filesystem operations or Java extension activation are not forcibly cancelled, but their late completion cannot publish environment updates or register new listeners.

## Disabling No-Config Debug

No-Config Debug is enabled by default. To opt out for all workspaces or just the current workspace, add this to the corresponding VS Code settings:
Expand Down Expand Up @@ -104,7 +114,7 @@ If you see "Address already in use", another Java debug session is running. Term

1. Ensure you're running with `debugjava` command (not plain `java`)
2. Check that the `debugjava` command is available: `which debugjava` (Unix) or `Get-Command debugjava` (PowerShell)
3. Verify the terminal was opened AFTER the extension activated
3. Verify the terminal was opened after No-Config Debug initialization finished; recreate an early terminal if its environment is missing the contributions
4. Check the Debug Console for error messages

### Node.js Not Found
Expand Down
23 changes: 10 additions & 13 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { ENABLE_NO_CONFIG_DEBUG, HCR_EVENT, JAVA_LANGID, TELEMETRY_EVENT, USER_N
import { NotificationBar } from "./customWidget";
import { initializeCodeLensProvider, startDebugging } from "./debugCodeLensProvider";
import { initExpService } from "./experimentationService";
import { registerNoConfigDebug } from "./noConfigDebugInit";
import { NoConfigDebugRegistration, registerNoConfigDebug } from "./noConfigDebugInit";
import { handleHotCodeReplaceCustomEvent, initializeHotCodeReplace, NO_BUTTON, YES_BUTTON } from "./hotCodeReplace";
import { JavaDebugAdapterDescriptorFactory } from "./javaDebugAdapterDescriptorFactory";
import { JavaInlineValuesProvider } from "./JavaInlineValueProvider";
Expand All @@ -39,20 +39,18 @@ export async function activate(context: vscode.ExtensionContext): Promise<any> {

// Capture once so terminal integration and the AI launch tool both require a reload to change.
const noConfigDebugEnabled = vscode.workspace.getConfiguration().get<boolean>(ENABLE_NO_CONFIG_DEBUG, true);
const noConfigDisposable = await registerNoConfigDebug(
const api = await instrumentOperation("activation", initializeExtension)(context);
const noConfigDebug = registerNoConfigDebug(
context.environmentVariableCollection,
context.extensionPath,
context.storageUri,
noConfigDebugEnabled,
);
if (noConfigDisposable) {
context.subscriptions.push(noConfigDisposable);
}
context.subscriptions.push(noConfigDebug);

// Register Language Model Tools after Java Language Server is ready
registerLanguageModelToolsWhenReady(context, noConfigDebugEnabled);
registerLanguageModelTools(context, noConfigDebug);

return instrumentOperation("activation", initializeExtension)(context);
return api;
}

function initializeExtension(_operationId: string, context: vscode.ExtensionContext): any {
Expand Down Expand Up @@ -113,11 +111,10 @@ export async function deactivate() {
const delay = promisify(setTimeout);

/**
* Register Language Model Tools after Java Language Server is ready.
* The debug tools depend on JDT.LS for compilation, classpath resolution,
* and executing debug server commands.
* Register tools when the Java extension is installed. The launch tool waits
* for No-Config Debug readiness at invocation, not during core activation.
*/
async function registerLanguageModelToolsWhenReady(context: vscode.ExtensionContext, noConfigDebugEnabled: boolean): Promise<void> {
function registerLanguageModelTools(context: vscode.ExtensionContext, noConfigDebug: NoConfigDebugRegistration): void {
// Check if Language Model API is available
if (!vscode.lm || typeof vscode.lm.registerTool !== 'function') {
return;
Expand All @@ -129,7 +126,7 @@ async function registerLanguageModelToolsWhenReady(context: vscode.ExtensionCont
}

// Register Language Model Tools for AI-assisted debugging
registerLanguageModelTool(context, noConfigDebugEnabled);
registerLanguageModelTool(context, noConfigDebug);
const debugToolsDisposables = registerDebugSessionTools(context);
context.subscriptions.push(...debugToolsDisposables);

Expand Down
31 changes: 26 additions & 5 deletions src/languageModelTool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import * as fs from 'fs';
import * as path from 'path';
import * as vscode from 'vscode';
import { ENABLE_NO_CONFIG_DEBUG } from "./constants";
import { NoConfigDebugRegistration } from "./noConfigDebugInit";
import {
beginDebugSessionInvocation,
classifyBreakpoint,
Expand Down Expand Up @@ -115,7 +116,7 @@ interface LanguageModelTool<T = any> {
*/
export function registerLanguageModelTool(
context: Pick<vscode.ExtensionContext, "subscriptions">,
noConfigDebugEnabled: boolean = true,
noConfigDebug: Pick<NoConfigDebugRegistration, "waitUntilReady">,
): vscode.Disposable | undefined {
// Check if the Language Model API is available
const lmApi = (vscode as any).lm;
Expand All @@ -126,12 +127,32 @@ export function registerLanguageModelTool(

const tool: LanguageModelTool<DebugJavaApplicationInput> = {
async invoke(options: { input: DebugJavaApplicationInput }, token: vscode.CancellationToken): Promise<any> {
if (!noConfigDebugEnabled) {
const readiness = await noConfigDebug.waitUntilReady(token);
if (readiness.status !== "ready") {
let message: string;
switch (readiness.status) {
case "disabled":
message = `Java No-Config Debug is disabled by ${ENABLE_NO_CONFIG_DEBUG}. `
+ "To use this tool, enable that setting, reload VS Code, and recreate existing terminals.";
break;
case "failed":
message = `${readiness.message} This tool cannot launch until initialization succeeds. `
+ "Resolve the initialization problem and reload VS Code before retrying.";
break;
case "cancelled":
message = "Operation cancelled by user while waiting for Java No-Config Debug initialization.";
break;
case "timeout":
message = "Timed out waiting for Java No-Config Debug initialization. "
+ "Initialization is still running; you can retry this tool later.";
break;
case "disposed":
message = "Java No-Config Debug has been disposed. Reload VS Code before retrying this tool.";
break;
}
return new vscode.LanguageModelToolResult([
new vscode.LanguageModelTextPart(
`Java No-Config Debug is disabled by ${ENABLE_NO_CONFIG_DEBUG}. `
+ "To use this tool, enable that setting, reload VS Code, and recreate existing terminals. "
+ "Standard Java launch/attach debugging remains available.",
`${message} Standard Java launch/attach debugging remains available.`,
),
]);
}
Expand Down
Loading