|
| 1 | +import * as vscode from 'vscode'; |
| 2 | +import * as path from 'path'; |
| 3 | +import { ContextClients } from './clients'; |
| 4 | +import { getExecutables, getMains } from './helpers'; |
| 5 | + |
| 6 | +/** |
| 7 | + * Ada Configuration for a debug session |
| 8 | + */ |
| 9 | +interface AdaConfig extends vscode.DebugConfiguration { |
| 10 | + MIMode: string; |
| 11 | + program: string; |
| 12 | + cwd: string; |
| 13 | + targetArchitecture: string; |
| 14 | + stopAtEntry: boolean; |
| 15 | + args: string[]; |
| 16 | + setupCommands: { |
| 17 | + description: string; |
| 18 | + text: string; |
| 19 | + ignoreFailures: boolean; |
| 20 | + }[]; |
| 21 | +} |
| 22 | + |
| 23 | +/** |
| 24 | + * Initialize debugging on an ada project by creating a default configuration in launch.json |
| 25 | + * @param ctx - the ada extension context |
| 26 | + * @param clients - the language clients |
| 27 | + * @returns the debug configuration provider |
| 28 | + */ |
| 29 | +export async function initializeDebugging(ctx: vscode.ExtensionContext, clients: ContextClients) { |
| 30 | + const provider = new AdaDebugConfigProvider(clients); |
| 31 | + ctx.subscriptions.push( |
| 32 | + vscode.debug.registerDebugConfigurationProvider( |
| 33 | + AdaDebugConfigProvider.adaConfigType, |
| 34 | + provider |
| 35 | + ) |
| 36 | + ); |
| 37 | + |
| 38 | + const workspaceConfig = vscode.workspace.getConfiguration(); |
| 39 | + const configurations: vscode.DebugConfiguration[] = |
| 40 | + (workspaceConfig.get('launch.configurations') as vscode.DebugConfiguration[]) || []; |
| 41 | + |
| 42 | + let status = false; |
| 43 | + for (const config of configurations) { |
| 44 | + if (config.name == 'Debug Ada') { |
| 45 | + status = true; |
| 46 | + break; |
| 47 | + } |
| 48 | + } |
| 49 | + |
| 50 | + if (!status) { |
| 51 | + const initialDebugConfiguration = initializeConfig(undefined, '${command:AskForProgram}'); |
| 52 | + |
| 53 | + configurations.push(initialDebugConfiguration); |
| 54 | + |
| 55 | + await workspaceConfig.update( |
| 56 | + 'launch.configurations', |
| 57 | + configurations, |
| 58 | + vscode.ConfigurationTarget.Workspace |
| 59 | + ); |
| 60 | + } |
| 61 | + return provider; |
| 62 | +} |
| 63 | +/** |
| 64 | + * Initialize the GDB debug configuration for an executable, |
| 65 | + * either program or command must be specified |
| 66 | + * @param program - the executable to debug (optional) |
| 67 | + * @param command - the command to collect the program to debug (optional) |
| 68 | + * @returns an AdaConfig |
| 69 | + */ |
| 70 | +function initializeConfig(program?: string, command?: string): AdaConfig { |
| 71 | + // Get the executable name from the relative path |
| 72 | + const config: AdaConfig = { |
| 73 | + type: 'cppdbg', |
| 74 | + name: 'Ada Config', |
| 75 | + request: 'launch', |
| 76 | + targetArchitecture: process.arch, |
| 77 | + cwd: '${workspaceFolder}', |
| 78 | + program: 'Ada executable to debug', |
| 79 | + stopAtEntry: false, |
| 80 | + externalConsole: false, |
| 81 | + args: [], |
| 82 | + MIMode: 'gdb', |
| 83 | + // preLaunchTask: 'gpr: Build Executable for File ' + main_name, |
| 84 | + preLaunchTask: 'ada: Build current project', |
| 85 | + setupCommands: setupCmd, |
| 86 | + }; |
| 87 | + if (command) { |
| 88 | + config.name = 'Debug Ada'; |
| 89 | + config.program = command; |
| 90 | + } else if (program) { |
| 91 | + const name = path.basename(program); |
| 92 | + config.name = 'Debug executable ' + name; |
| 93 | + config.program = program; |
| 94 | + } |
| 95 | + return config; |
| 96 | +} |
| 97 | + |
| 98 | +export class AdaDebugConfigProvider implements vscode.DebugConfigurationProvider { |
| 99 | + private readonly clients: ContextClients; |
| 100 | + public static adaConfigType = 'cppdbg'; |
| 101 | + |
| 102 | + constructor(clients: ContextClients) { |
| 103 | + this.clients = clients; |
| 104 | + } |
| 105 | + // Provider |
| 106 | + async provideDebugConfigurations( |
| 107 | + folder: vscode.WorkspaceFolder | undefined, |
| 108 | + _token?: vscode.CancellationToken | undefined |
| 109 | + ): Promise<vscode.DebugConfiguration[]> { |
| 110 | + // provide a non-existent debug/launch configuration |
| 111 | + const config: vscode.DebugConfiguration[] = []; |
| 112 | + if (_token?.isCancellationRequested) { |
| 113 | + return []; |
| 114 | + } |
| 115 | + if (folder != undefined) { |
| 116 | + const execs = await getExecutables(this.clients.adaClient); |
| 117 | + // Show the option for the user to choose the executable |
| 118 | + const quickpick = execs.map((e) => ({ |
| 119 | + label: vscode.workspace.asRelativePath(e), |
| 120 | + description: 'Generate the associated configuration', |
| 121 | + })); |
| 122 | + const selectedProgram = await vscode.window.showQuickPick(quickpick, { |
| 123 | + placeHolder: 'Select a program to debug', |
| 124 | + }); |
| 125 | + if (selectedProgram) { |
| 126 | + const configuration = initializeConfig(selectedProgram.label); |
| 127 | + config.push(configuration); |
| 128 | + } |
| 129 | + return config; |
| 130 | + } else { |
| 131 | + return config; |
| 132 | + } |
| 133 | + } |
| 134 | + |
| 135 | + async resolveDebugConfiguration( |
| 136 | + _folder: vscode.WorkspaceFolder | undefined, |
| 137 | + debugConfiguration: vscode.DebugConfiguration, |
| 138 | + _token?: vscode.CancellationToken | undefined |
| 139 | + ): Promise<vscode.DebugConfiguration | undefined> { |
| 140 | + // resolve a incompleted debug/launch configuration |
| 141 | + if (_token?.isCancellationRequested) { |
| 142 | + return undefined; |
| 143 | + } |
| 144 | + if (debugConfiguration.request == 'launch') { |
| 145 | + return debugConfiguration; |
| 146 | + } |
| 147 | + const file = vscode.window.activeTextEditor?.document.uri.path; |
| 148 | + if (file != undefined) { |
| 149 | + const mains = await getMains(this.clients.adaClient); |
| 150 | + const execs = await getExecutables(this.clients.adaClient); |
| 151 | + for (let i = 0; i < mains.length; i++) { |
| 152 | + if (file == mains[i]) { |
| 153 | + const config = initializeConfig(execs[i]); |
| 154 | + return config; |
| 155 | + } |
| 156 | + } |
| 157 | + const quickpick = mains.map((e) => ({ |
| 158 | + label: vscode.workspace.asRelativePath(e), |
| 159 | + description: 'Run & Debug', |
| 160 | + main: e, |
| 161 | + })); |
| 162 | + const selectedProgram = await vscode.window.showQuickPick(quickpick, { |
| 163 | + placeHolder: 'Select a main file', |
| 164 | + }); |
| 165 | + if (selectedProgram) { |
| 166 | + const index = mains.indexOf(selectedProgram.main); |
| 167 | + const configuration = initializeConfig(execs[index]); |
| 168 | + return configuration; |
| 169 | + } |
| 170 | + } |
| 171 | + return undefined; |
| 172 | + } |
| 173 | + |
| 174 | + /** |
| 175 | + * Resolves the program path fron the 'Debug Ada' default configuration |
| 176 | + * @returns the executable path to debug |
| 177 | + */ |
| 178 | + async initDebugCmd(): Promise<string | undefined> { |
| 179 | + const file = vscode.window.activeTextEditor?.document.uri.path; |
| 180 | + const mains = await getMains(this.clients.adaClient); |
| 181 | + const execs = await getExecutables(this.clients.adaClient); |
| 182 | + |
| 183 | + if (mains.length == 1) return execs[0]; |
| 184 | + if (file != undefined) { |
| 185 | + for (let i = 0; i < mains.length; i++) { |
| 186 | + if (file == mains[i]) { |
| 187 | + return execs[i]; |
| 188 | + } |
| 189 | + } |
| 190 | + } |
| 191 | + const quickpick = mains.map((e) => ({ |
| 192 | + label: vscode.workspace.asRelativePath(e), |
| 193 | + description: 'Run & Debug', |
| 194 | + main: e, |
| 195 | + })); |
| 196 | + const selectedProgram = await vscode.window.showQuickPick(quickpick, { |
| 197 | + placeHolder: 'Select a main file', |
| 198 | + }); |
| 199 | + if (selectedProgram) { |
| 200 | + const index = mains.indexOf(selectedProgram.main); |
| 201 | + return execs[index]; |
| 202 | + } |
| 203 | + return undefined; |
| 204 | + } |
| 205 | +} |
| 206 | + |
| 207 | +/** |
| 208 | + * GDB default setup options |
| 209 | + */ |
| 210 | +const setupCmd = [ |
| 211 | + { |
| 212 | + description: 'Catch all Ada exceptions', |
| 213 | + text: 'catch exception', |
| 214 | + ignoreFailures: true, |
| 215 | + }, |
| 216 | + { |
| 217 | + description: 'Enable pretty-printing for gdb', |
| 218 | + text: '-enable-pretty-printing', |
| 219 | + ignoreFailures: true, |
| 220 | + }, |
| 221 | + { |
| 222 | + description: 'Set Disassembly Flavor to Intel', |
| 223 | + text: '-gdb-set disassembly-flavor intel', |
| 224 | + ignoreFailures: true, |
| 225 | + }, |
| 226 | +]; |
0 commit comments