Skip to content

Commit 9e70dde

Browse files
abdellah257eliericha
authored andcommitted
Generate debug configuration
1 parent da82f75 commit 9e70dde

File tree

7 files changed

+386
-20
lines changed

7 files changed

+386
-20
lines changed

integration/vscode/ada/package.json

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -508,6 +508,11 @@
508508
"command": "ada.showGprLSOutput",
509509
"title": "Ada: Show GPR Language Server Output",
510510
"when": "ADA_PROJECT_CONTEXT"
511+
},
512+
{
513+
"command": "ada.initDebugFile",
514+
"title": "Ada: Debug File",
515+
"when": "ADA_PROJECT_CONTEXT"
511516
}
512517
],
513518
"keybindings": [
@@ -581,6 +586,79 @@
581586
}
582587
]
583588
}
589+
],
590+
"debuggers": [
591+
{
592+
"type": "cppdbg",
593+
"label": "Ada",
594+
"languages": [
595+
"ada",
596+
"gpr",
597+
"GNAT Project"
598+
],
599+
"when": "ADA_PROJECT_CONTEXT",
600+
"configurationSnippets": [
601+
{
602+
"label": "Ada Debugger Launch",
603+
"description": "Launch configuration for Ada debugging.",
604+
"body": {
605+
"type": "cppdbg",
606+
"request": "launch",
607+
"name": "Ada Debugger Launch",
608+
"program": "your-executable-path",
609+
"args": [],
610+
"cwd": "${workspaceFolder}",
611+
"stopAtEntry": false,
612+
"preLaunchTask": "your-prelaunch-task"
613+
}
614+
}
615+
],
616+
"configurationAttributes": {
617+
"launch": {
618+
"required": [
619+
"program"
620+
],
621+
"properties": [
622+
{
623+
"program": {
624+
"type": "string",
625+
"description": "Path to program executable.",
626+
"default": "${command:AskForProgram}"
627+
},
628+
"cwd": {
629+
"type": "string",
630+
"description": "The working directory of the target.",
631+
"default": "${workspaceRoot}"
632+
},
633+
"stopAtEntry": {
634+
"type": "boolean",
635+
"description": "If true, the debugger should stop at the entrypoint of the target.",
636+
"default": false
637+
},
638+
"args": {
639+
"description": "Arguments passed to the program entrypoint",
640+
"items": {
641+
"type": "string"
642+
},
643+
"type": [
644+
"array",
645+
"string"
646+
],
647+
"default": []
648+
},
649+
"preLaunchTask": {
650+
"type": "string",
651+
"description": "The build task to launch before debugging",
652+
"default": "ada: Build current project"
653+
}
654+
}
655+
]
656+
}
657+
},
658+
"variables": {
659+
"AskForProgram": "ada.initDebugFile"
660+
}
661+
}
584662
]
585663
},
586664
"devDependencies": {

integration/vscode/ada/src/commands.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,13 @@ import { SymbolKind } from 'vscode';
33
import { getEnclosingSymbol } from './gnatTaskProvider';
44
import { mainLogChannel } from './extension';
55
import { ContextClients } from './clients';
6+
import { AdaDebugConfigProvider } from './debugConfigProvider';
67

7-
export function RegisterCommands(context: vscode.ExtensionContext, clients: ContextClients) {
8+
export function registerCommands(
9+
context: vscode.ExtensionContext,
10+
clients: ContextClients,
11+
debug: AdaDebugConfigProvider
12+
) {
813
context.subscriptions.push(
914
vscode.commands.registerCommand('ada.otherFile', clients.otherFileHandler)
1015
);
@@ -24,6 +29,12 @@ export function RegisterCommands(context: vscode.ExtensionContext, clients: Cont
2429
clients.gprClient.outputChannel.show()
2530
)
2631
);
32+
context.subscriptions.push(
33+
vscode.commands.registerCommand('ada.initDebugFile', async () => {
34+
const p = await debug.initDebugCmd();
35+
return p;
36+
})
37+
);
2738
}
2839
/**
2940
* Add a subprogram box above the subprogram enclosing the cursor's position, if any.
Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
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+
];

integration/vscode/ada/src/extension.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/* eslint-disable @typescript-eslint/no-unsafe-call */
12
/*----------------------------------------------------------------------------
23
-- Language Server Protocol --
34
-- --
@@ -22,12 +23,12 @@ import { LanguageClient, Middleware } from 'vscode-languageclient/node';
2223
import { ContextClients } from './clients';
2324
import { alsCommandExecutor } from './alsExecuteCommand';
2425
import { ALSClientFeatures } from './alsClientFeatures';
25-
import { RegisterCommands } from './commands';
26+
import { registerCommands } from './commands';
2627
import { initializeTestView } from './gnattest';
2728
import { getEvaluatedCustomEnv, assertSupportedEnvironments } from './helpers';
29+
import { initializeDebugging } from './debugConfigProvider';
2830

2931
const ADA_CONTEXT = 'ADA_PROJECT_CONTEXT';
30-
3132
export let contextClients: ContextClients;
3233
export let mainLogChannel: vscode.OutputChannel;
3334

@@ -71,11 +72,17 @@ export async function activate(context: vscode.ExtensionContext): Promise<void>
7172

7273
await vscode.commands.executeCommand('setContext', ADA_CONTEXT, true);
7374

74-
RegisterCommands(context, contextClients);
75-
7675
await checkSrcDirectories(contextClients.adaClient);
7776

7877
await initializeTestView(context, contextClients);
78+
79+
await Promise.all([contextClients.adaClient.onReady(), contextClients.gprClient.onReady()]);
80+
81+
const adaDebugConfigProvider = await initializeDebugging(context, contextClients);
82+
83+
registerCommands(context, contextClients, adaDebugConfigProvider);
84+
85+
mainLogChannel.appendLine('Started Ada extension');
7986
}
8087

8188
export async function deactivate() {

0 commit comments

Comments
 (0)