99 lines · typescript
1import * as vscode from "vscode";2 3/**4 * A button with a particular label that can perform an action when clicked.5 *6 * Used to add buttons to {@link showErrorMessage showErrorMessage()}.7 */8export interface NotificationButton<T, Result> {9 readonly label: T;10 action(): Promise<Result>;11}12 13/**14 * Represents a button that, when clicked, will open a particular VS Code setting.15 */16export class OpenSettingsButton17 implements NotificationButton<"Open Settings", undefined>18{19 readonly label = "Open Settings";20 21 constructor(private readonly settingId?: string) {}22 23 async action(): Promise<undefined> {24 await vscode.commands.executeCommand(25 "workbench.action.openSettings",26 this.settingId ?? "@ext:llvm-vs-code-extensions.lldb-dap ",27 );28 }29}30 31/**32 * Represents a button that, when clicked, will return `null`.33 *34 * Used by a {@link vscode.DebugConfigurationProvider} to indicate that VS Code should35 * cancel a debug session and open its launch configuration.36 *37 * **IMPORTANT**: this button will do nothing if the callee isn't a38 * {@link vscode.DebugConfigurationProvider}.39 */40export class ConfigureButton41 implements NotificationButton<"Configure", null | undefined>42{43 readonly label = "Configure";44 45 async action(): Promise<null | undefined> {46 return null; // Opens the launch.json if returned from a DebugConfigurationProvider47 }48}49 50/** Gets the Result type from a {@link NotificationButton} or string value. */51type ResultOf<T> = T extends string52 ? T53 : T extends NotificationButton<any, infer Result>54 ? Result55 : never;56 57/**58 * Shows an error message to the user with an optional array of buttons.59 *60 * This can be used with common buttons such as {@link OpenSettingsButton} or plain61 * strings as would normally be accepted by {@link vscode.window.showErrorMessage}.62 *63 * @param message The error message to display to the user64 * @param options Configures the behaviour of the message.65 * @param buttons An array of {@link NotificationButton buttons} or strings that the user can click on66 * @returns `undefined` or the result of a button's action67 */68export async function showErrorMessage<69 T extends string | NotificationButton<any, any>,70>(71 message: string,72 options: vscode.MessageOptions = {},73 ...buttons: T[]74): Promise<ResultOf<T> | undefined> {75 const userSelection = await vscode.window.showErrorMessage(76 message,77 options,78 ...buttons.map((button) => {79 if (typeof button === "string") {80 return button;81 }82 return button.label;83 }),84 );85 86 for (const button of buttons) {87 if (typeof button === "string") {88 if (userSelection === button) {89 // Type assertion is required to let TypeScript know that "button" isn't just any old string.90 return button as ResultOf<T>;91 }92 } else if (userSelection === button.label) {93 return await button.action();94 }95 }96 97 return undefined;98}99