diff --git a/package-lock.json b/package-lock.json index 227d7207..4952e70f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "bazel-stack-vscode", - "version": "1.1.0", + "version": "1.1.1", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -3653,6 +3653,20 @@ "resolved": "https://registry.npmjs.org/vscode-common/-/vscode-common-1.50.0.tgz", "integrity": "sha512-LLya2PcXD1PNipniNKKZapRdxJBu2lc9PUANwxS9th7y92d6JDq/yY4LtK2gi4s7yD4JuaatUX3JUVk2LcjgjA==" }, + "vscode-debugadapter": { + "version": "1.47.0", + "resolved": "https://registry.npmjs.org/vscode-debugadapter/-/vscode-debugadapter-1.47.0.tgz", + "integrity": "sha512-ppO1M9/mwsxE1B1lNNhZ9z1IfNNaRfDmR+yrdRQBW6Tz4VmNhRJ5hyUREJGk+OyW26RU4shO6tXpZa9CzUYiQw==", + "requires": { + "mkdirp": "^0.5.5", + "vscode-debugprotocol": "1.47.0" + } + }, + "vscode-debugprotocol": { + "version": "1.47.0", + "resolved": "https://registry.npmjs.org/vscode-debugprotocol/-/vscode-debugprotocol-1.47.0.tgz", + "integrity": "sha512-ii7oCz3Wfr/SGtFr5AYop5dJm0dUmpg0hq2lTzTBdaht8nSheYMMjPntxULBR+2TUxXLcCKFZkF2UEJQduYsIQ==" + }, "vscode-extension-telemetry": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/vscode-extension-telemetry/-/vscode-extension-telemetry-0.1.6.tgz", diff --git a/package.json b/package.json index 9f56db4c..1366adc1 100644 --- a/package.json +++ b/package.json @@ -19,10 +19,12 @@ }, "homepage": "https://github.com/stackb/bazel-stack-vscode#readme", "categories": [ + "Debuggers", "Programming Languages", "Snippets" ], "activationEvents": [ + "onDebug", "onUri", "onCommand:bsv.openExtensionSetting", "onCommand:workbench.view.extension.bazel-explorer", @@ -36,6 +38,39 @@ "vscode": "^1.45.0" }, "contributes": { + "breakpoints": [ + { + "language": "bazel" + } + ], + "debuggers": [ + { + "type": "starlark", + "label": "Starlark Debug", + "configurationAttributes": { + "attach": { + "required": [ + "stopOnEntry" + ], + "properties": { + "stopOnEntry": { + "type": "boolean", + "description": "Automatically stop after launch.", + "default": true + } + } + } + }, + "initialConfigurations": [ + { + "type": "attach", + "request": "attach", + "name": "Attach to Starlark Debug Server (Bazel)", + "stopOnEntry": true + } + ] + } + ], "configuration": { "type": "object", "title": "Bzl", @@ -190,7 +225,7 @@ "default": [ "lsp", "serve", - "--log_level=debug" + "--log_level=info" ] }, "bsv.bzl.lsp.enableCodelenses": { @@ -617,6 +652,7 @@ "strip-ansi": "^6.0.0", "tmp": "0.2.1", "vscode-common": "1.50.0", + "vscode-debugadapter": "1.47.0", "vscode-extension-telemetry": "^0.1.6", "vscode-languageclient": "7.0.0" }, @@ -662,4 +698,4 @@ "tabWidth": 2, "arrowParens": "avoid" } -} +} \ No newline at end of file diff --git a/src/bezel/bepRunner.ts b/src/bezel/bepRunner.ts index 214efa5c..01304cd7 100644 --- a/src/bezel/bepRunner.ts +++ b/src/bezel/bepRunner.ts @@ -113,6 +113,7 @@ export class BEPRunner implements vscode.Disposable, vscode.Pseudoterminal { this.writeEmitter.fire( `bazel ${request.arg?.join(' ')}\r\n (to cancel, kill the terminal).\r\n\n` ); + // use a timeout here? await this.terminalIsOpen!.wait(); const clearExecution = () => { @@ -204,6 +205,7 @@ export class BEPRunner implements vscode.Disposable, vscode.Pseudoterminal { async close(): Promise { if (this.currentExecution) { this.currentExecution.cancellation?.cancel(); + this.currentExecution = undefined; } this.terminal?.dispose(); this.terminal = undefined; diff --git a/src/bezel/debugger.ts b/src/bezel/debugger.ts index 25edc025..407c526b 100644 --- a/src/bezel/debugger.ts +++ b/src/bezel/debugger.ts @@ -7,12 +7,11 @@ import { } from './configuration'; import { CommandName } from './constants'; import { Settings } from './settings'; -import { LaunchableComponent, LaunchArgs, Status } from './status'; +import { LaunchableComponent, LaunchArgs } from './status'; export class StarlarkDebugger extends LaunchableComponent - implements vscode.Disposable -{ + implements vscode.Disposable, vscode.DebugAdapterDescriptorFactory, vscode.DebugConfigurationProvider { constructor( public readonly settings: StarlarkDebuggerSettings, private readonly bazelSettings: Settings, @@ -20,13 +19,16 @@ export class StarlarkDebugger private readonly workspaceFolder: string ) { super('SDB', settings, CommandName.LaunchDebugCLI, 'debug-cli'); + + this.disposables.push(vscode.debug.registerDebugAdapterDescriptorFactory('starlark', this)); + this.disposables.push(vscode.debug.registerDebugConfigurationProvider('starlark', this)); } async shouldLaunch(e: Error): Promise { return false; } - async launchInternal(): Promise {} + async launchInternal(): Promise { } async invoke(command: string, label: string): Promise { const bazel = await this.bazelSettings.get(); @@ -53,6 +55,70 @@ export class StarlarkDebugger noHideOnReady: true, }; } + + /** + * Massage a debug configuration just before a debug session is being launched, + * e.g. add all missing attributes to the debug configuration. + */ + resolveDebugConfiguration(folder: vscode.WorkspaceFolder | undefined, config: vscode.DebugConfiguration, token?: vscode.CancellationToken): vscode.ProviderResult { + + // if launch.json is missing or empty + if (!config.type && !config.request && !config.name) { + const editor = vscode.window.activeTextEditor; + if (editor && editor.document.languageId === 'bazel') { + config.type = 'starlark'; + config.name = 'Attach to Starlark Debug Server'; + config.request = 'launch'; + config.stopOnEntry = true; + } + } + + // if (!config.program) { + // return vscode.window.showInformationMessage("Cannot find a program to debug").then(_ => { + // return undefined; // abort launch + // }); + // } + + return config; + } + + + /** + * This hook is directly called after 'resolveDebugConfiguration' but with all variables substituted. + * It can be used to resolve or verify a [debug configuration](#DebugConfiguration) by filling in missing values or by adding/changing/removing attributes. + * If more than one debug configuration provider is registered for the same type, the 'resolveDebugConfigurationWithSubstitutedVariables' calls are chained + * in arbitrary order and the initial debug configuration is piped through the chain. + * Returning the value 'undefined' prevents the debug session from starting. + * Returning the value 'null' prevents the debug session from starting and opens the underlying debug configuration instead. + * + * @param folder The workspace folder from which the configuration originates from or `undefined` for a folderless setup. + * @param debugConfiguration The [debug configuration](#DebugConfiguration) to resolve. + * @param token A cancellation token. + * @return The resolved debug configuration or undefined or null. + */ + resolveDebugConfigurationWithSubstitutedVariables?(folder: vscode.WorkspaceFolder | undefined, debugConfiguration: vscode.DebugConfiguration, token?: vscode.CancellationToken): vscode.ProviderResult { + console.log('resolved', debugConfiguration); + return debugConfiguration; + } + + async createDebugAdapterDescriptor(session: vscode.DebugSession, executable: vscode.DebugAdapterExecutable | undefined): Promise> { + // param "executable" contains the executable optionally specified in the package.json (if any) + const bzl = await this.bzlSettings.get(); + const cfg = await this.settings.get(); + + // use the executable specified in the package.json if it exists or determine it based on some other information (e.g. the session) + if (!executable) { + const args = ["dap", "--address=localhost:3737"]; + const options = { + cwd: this.workspaceFolder, + env: { "VAR": "some value" } + }; + executable = new vscode.DebugAdapterExecutable(bzl.executable, args, options); + } + + // make VS Code launch the DA executable + return executable; + } } function debugInfoMessage(): string { @@ -64,3 +130,11 @@ function debugInfoMessage(): string { 'Are you sure you want to continue?' ); } + + +// Debugger plan: +// +// 1. Move server.go into other package. +// 2. Add a client field to the struct (and a constructor) +// 3. Make Serve an method. +// 4. Implement Attach. \ No newline at end of file