blob: 8d92139c02a0048dc37b7d5268b611a0d5e09b6b [file] [log] [blame] [edit]
import * as vscode from "vscode";
import * as child_process from "child_process";
import * as util from "util";
import { LLDBDapServer } from "./lldb-dap-server";
import { createDebugAdapterExecutable } from "./debug-adapter-factory";
import { ConfigureButton, showErrorMessage } from "./ui/show-error-message";
import { ErrorWithNotification } from "./ui/error-with-notification";
const exec = util.promisify(child_process.execFile);
/**
* Determines whether or not the given lldb-dap executable supports executing
* in server mode.
*
* @param exe the path to the lldb-dap executable
* @returns a boolean indicating whether or not lldb-dap supports server mode
*/
async function isServerModeSupported(exe: string): Promise<boolean> {
const { stdout } = await exec(exe, ["--help"]);
return /--connection/.test(stdout);
}
export class LLDBDapConfigurationProvider
implements vscode.DebugConfigurationProvider
{
constructor(private readonly server: LLDBDapServer) {}
async resolveDebugConfigurationWithSubstitutedVariables(
folder: vscode.WorkspaceFolder | undefined,
debugConfiguration: vscode.DebugConfiguration,
_token?: vscode.CancellationToken,
): Promise<vscode.DebugConfiguration | null | undefined> {
try {
if (
"debugAdapterHostname" in debugConfiguration &&
!("debugAdapterPort" in debugConfiguration)
) {
throw new ErrorWithNotification(
"A debugAdapterPort must be provided when debugAdapterHostname is set. Please update your launch configuration.",
new ConfigureButton(),
);
}
// Check if we're going to launch a debug session or use an existing process
if ("debugAdapterPort" in debugConfiguration) {
if (
"debugAdapterExecutable" in debugConfiguration ||
"debugAdapterArgs" in debugConfiguration
) {
throw new ErrorWithNotification(
"The debugAdapterPort property is incompatible with debugAdapterExecutable and debugAdapterArgs. Please update your launch configuration.",
new ConfigureButton(),
);
}
} else {
// Always try to create the debug adapter executable as this will show the user errors
// if there are any.
const executable = await createDebugAdapterExecutable(
folder,
debugConfiguration,
);
if (!executable) {
return undefined;
}
// Server mode needs to be handled here since DebugAdapterDescriptorFactory
// will show an unhelpful error if it returns undefined. We'd rather show a
// nicer error message here and allow stopping the debug session gracefully.
const config = vscode.workspace.getConfiguration("lldb-dap", folder);
if (
config.get<boolean>("serverMode", false) &&
(await isServerModeSupported(executable.command))
) {
const serverInfo = await this.server.start(
executable.command,
executable.args,
executable.options,
);
if (!serverInfo) {
return undefined;
}
// Use a debug adapter host and port combination rather than an executable
// and list of arguments.
delete debugConfiguration.debugAdapterExecutable;
delete debugConfiguration.debugAdapterArgs;
debugConfiguration.debugAdapterHostname = serverInfo.host;
debugConfiguration.debugAdapterPort = serverInfo.port;
}
}
return debugConfiguration;
} catch (error) {
// Show a better error message to the user if possible
if (!(error instanceof ErrorWithNotification)) {
throw error;
}
return await error.showNotification({
modal: true,
showConfigureButton: true,
});
}
}
}