From 1480889205d49cf7221a36608a8896b452967cea Mon Sep 17 00:00:00 2001 From: Ashcon Partovi Date: Thu, 24 Aug 2023 22:53:34 -0700 Subject: Improved support for `debug-adapter-protocol` (#4186) * Improve support for \`debug-adapter-protocol\` * More improvements, fix formatting in debug console * Fix attaching * Prepare for source maps * Start of source map support, breakpoints work * Source map support * add some package.jsons * wip * Update package.json * More fixes * Make source maps safer if exception occurs * Check bun version if it fails * Fix console.log formatting * Fix source maps partly * More source map fixes * Prepare for extension * watch mode with dap * Improve preview code * Prepare for extension 2 --------- Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> --- packages/bun-vscode/src/features/debug.ts | 153 ++++++++++++++++++++++++++++++ 1 file changed, 153 insertions(+) create mode 100644 packages/bun-vscode/src/features/debug.ts (limited to 'packages/bun-vscode/src/features/debug.ts') diff --git a/packages/bun-vscode/src/features/debug.ts b/packages/bun-vscode/src/features/debug.ts new file mode 100644 index 000000000..3b841ea66 --- /dev/null +++ b/packages/bun-vscode/src/features/debug.ts @@ -0,0 +1,153 @@ +import * as vscode from "vscode"; +import type { CancellationToken, DebugConfiguration, ProviderResult, WorkspaceFolder } from "vscode"; +import type { DAP } from "../../../bun-debug-adapter-protocol"; +import { DebugAdapter } from "../../../bun-debug-adapter-protocol"; +import { DebugSession } from "@vscode/debugadapter"; + +const debugConfiguration: vscode.DebugConfiguration = { + type: "bun", + request: "launch", + name: "Debug Bun", + program: "${file}", + watch: true, +}; + +const runConfiguration: vscode.DebugConfiguration = { + type: "bun", + request: "launch", + name: "Run Bun", + program: "${file}", + watch: true, +}; + +const attachConfiguration: vscode.DebugConfiguration = { + type: "bun", + request: "attach", + name: "Attach to Bun", + url: "ws://localhost:6499/", +}; + +const debugConfigurations: vscode.DebugConfiguration[] = [debugConfiguration, attachConfiguration]; + +export default function (context: vscode.ExtensionContext, factory?: vscode.DebugAdapterDescriptorFactory) { + context.subscriptions.push( + vscode.commands.registerCommand("extension.bun.runFile", (resource: vscode.Uri) => { + let targetResource = resource; + if (!targetResource && vscode.window.activeTextEditor) { + targetResource = vscode.window.activeTextEditor.document.uri; + } + if (targetResource) { + vscode.debug.startDebugging(undefined, runConfiguration, { + noDebug: true, + }); + } + }), + vscode.commands.registerCommand("extension.bun.debugFile", (resource: vscode.Uri) => { + let targetResource = resource; + if (!targetResource && vscode.window.activeTextEditor) { + targetResource = vscode.window.activeTextEditor.document.uri; + } + if (targetResource) { + vscode.debug.startDebugging(undefined, { + ...debugConfiguration, + program: targetResource.fsPath, + }); + } + }), + ); + + const provider = new BunConfigurationProvider(); + context.subscriptions.push(vscode.debug.registerDebugConfigurationProvider("bun", provider)); + + context.subscriptions.push( + vscode.debug.registerDebugConfigurationProvider( + "bun", + { + provideDebugConfigurations(folder: WorkspaceFolder | undefined): ProviderResult { + return debugConfigurations; + }, + }, + vscode.DebugConfigurationProviderTriggerKind.Dynamic, + ), + ); + + if (!factory) { + factory = new InlineDebugAdapterFactory(); + } + context.subscriptions.push(vscode.debug.registerDebugAdapterDescriptorFactory("bun", factory)); + if ("dispose" in factory && typeof factory.dispose === "function") { + // @ts-ignore + context.subscriptions.push(factory); + } +} + +class BunConfigurationProvider implements vscode.DebugConfigurationProvider { + resolveDebugConfiguration( + folder: WorkspaceFolder | undefined, + config: DebugConfiguration, + token?: CancellationToken, + ): ProviderResult { + if (!config.type && !config.request && !config.name) { + const editor = vscode.window.activeTextEditor; + if (editor && isJavaScript(editor.document.languageId)) { + Object.assign(config, debugConfiguration); + } + } + return config; + } +} + +class InlineDebugAdapterFactory implements vscode.DebugAdapterDescriptorFactory { + createDebugAdapterDescriptor(_session: vscode.DebugSession): ProviderResult { + const adapter = new VSCodeAdapter(_session); + return new vscode.DebugAdapterInlineImplementation(adapter); + } +} + +function isJavaScript(languageId: string): boolean { + return ( + languageId === "javascript" || + languageId === "javascriptreact" || + languageId === "typescript" || + languageId === "typescriptreact" + ); +} + +export class VSCodeAdapter extends DebugSession { + #adapter: DebugAdapter; + #dap: vscode.OutputChannel; + + constructor(session: vscode.DebugSession) { + super(); + this.#dap = vscode.window.createOutputChannel("Debug Adapter Protocol"); + this.#adapter = new DebugAdapter({ + sendToAdapter: this.sendMessage.bind(this), + }); + } + + sendMessage(message: DAP.Request | DAP.Response | DAP.Event): void { + console.log("[dap] -->", message); + this.#dap.appendLine("--> " + JSON.stringify(message)); + + const { type } = message; + if (type === "response") { + this.sendResponse(message); + } else if (type === "event") { + this.sendEvent(message); + } else { + throw new Error(`Not supported: ${type}`); + } + } + + handleMessage(message: DAP.Event | DAP.Request | DAP.Response): void { + console.log("[dap] <--", message); + this.#dap.appendLine("<-- " + JSON.stringify(message)); + + this.#adapter.accept(message); + } + + dispose() { + this.#adapter.close(); + this.#dap.dispose(); + } +} -- cgit v1.2.3