1
1
import assert from 'assert' ;
2
+ import { existsSync } from 'fs' ;
3
+ import path from 'path' ;
2
4
import * as vscode from 'vscode' ;
3
5
import { adaExtState } from './extension' ;
4
- import { AdaMain , exe , getAdaMains , getEvaluatedTerminalEnv , getProjectFile } from './helpers' ;
6
+ import {
7
+ AdaMain ,
8
+ exe ,
9
+ getAdaMains ,
10
+ getEvaluatedTerminalEnv ,
11
+ getProjectFile ,
12
+ getProjectFileRelPath ,
13
+ } from './helpers' ;
5
14
import { BUILD_PROJECT_TASK_NAME , getBuildTaskName } from './taskProviders' ;
6
- import path from 'path' ;
7
- import { existsSync } from 'fs ';
15
+
16
+ export const ADA_DEBUG_BACKEND_TYPE = 'cppdbg ';
8
17
9
18
/**
10
19
* Ada Configuration for a debug session
@@ -29,14 +38,26 @@ export const adaDynamicDebugConfigProvider = {
29
38
async provideDebugConfigurations (
30
39
// eslint-disable-next-line @typescript-eslint/no-unused-vars
31
40
_folder ?: vscode . WorkspaceFolder
32
- ) : Promise < vscode . DebugConfiguration [ ] > {
33
- const quickpick = await createQuickPickItems ( 'Build & Debug' ) ;
34
-
35
- const configs : AdaConfig [ ] = quickpick . flatMap ( ( i ) => {
36
- assert ( i . adaMain ) ;
37
- return [ initializeConfig ( i . adaMain ) , createAttachConfig ( i . adaMain ) ] ;
41
+ ) : Promise < AdaConfig [ ] > {
42
+ const mains = await getAdaMains ( ) ;
43
+ const configs : AdaConfig [ ] = mains . flatMap ( ( m ) => {
44
+ return [ initializeConfig ( m ) , createAttachConfig ( m ) ] ;
38
45
} ) ;
39
-
46
+ if ( configs . length == 0 ) {
47
+ /**
48
+ * No configs were computed which means that there are no mains.
49
+ */
50
+ const msg =
51
+ `The project ${ await getProjectFileRelPath ( ) } does ` +
52
+ `not have a Main attribute. Using debug configurations with no ` +
53
+ `main program is not supported. Please provide a Main declaration in the ` +
54
+ `project and reload the Visual Studio Code window to ` +
55
+ `use debug configurations.` ;
56
+ void vscode . window . showWarningMessage ( msg , {
57
+ modal : true ,
58
+ } ) ;
59
+ return Promise . reject ( msg ) ;
60
+ }
40
61
return configs ;
41
62
} ,
42
63
} ;
@@ -47,9 +68,16 @@ export const adaDynamicDebugConfigProvider = {
47
68
* @param ctx - the Ada extension context
48
69
* @returns the debug configuration provider
49
70
*/
50
- export function initializeDebugging ( ctx : vscode . ExtensionContext ) {
71
+ export function initializeDebugging ( ctx : vscode . ExtensionContext ) : {
72
+ providerInitial : AdaInitialDebugConfigProvider ;
73
+ providerDynamic : {
74
+ provideDebugConfigurations (
75
+ _folder ?: vscode . WorkspaceFolder | undefined
76
+ ) : Promise < vscode . DebugConfiguration [ ] > ;
77
+ } ;
78
+ } {
51
79
// Instantiate a DebugConfigProvider for Ada and register it.
52
- const provider = new AdaDebugConfigProvider ( ) ;
80
+ const providerInitial = new AdaInitialDebugConfigProvider ( ) ;
53
81
54
82
// This provider is registered for the 'ada' debugger type. It means that
55
83
// it is triggered either when a configuration with type 'ada' is launched,
@@ -62,8 +90,14 @@ export function initializeDebugging(ctx: vscode.ExtensionContext) {
62
90
// is no longer called since it is not registered for the type 'cppdbg'.
63
91
// Moreover, it is somewhat discouraged to register it for the type
64
92
// 'cppdbg' since that type is provided by another extension.
65
- ctx . subscriptions . push ( vscode . debug . registerDebugConfigurationProvider ( 'ada' , provider ) ) ;
66
-
93
+ ctx . subscriptions . push ( vscode . debug . registerDebugConfigurationProvider ( 'ada' , providerInitial ) ) ;
94
+
95
+ /**
96
+ * This provider is registered for the 'Dynamic' trigger kind. It is called
97
+ * to provide a choice of launch configurations that are unrelated to the
98
+ * launch.json file. Such configurations are stored in memory and can be
99
+ * relaunched by the User without being saved in the launch.json file.
100
+ */
67
101
ctx . subscriptions . push (
68
102
vscode . debug . registerDebugConfigurationProvider (
69
103
'ada' ,
@@ -75,7 +109,7 @@ export function initializeDebugging(ctx: vscode.ExtensionContext) {
75
109
)
76
110
) ;
77
111
78
- return provider ;
112
+ return { providerInitial : providerInitial , providerDynamic : adaDynamicDebugConfigProvider } ;
79
113
}
80
114
81
115
let cachedGdb : string | undefined | null = undefined ;
@@ -92,7 +126,7 @@ let cachedGdb: string | undefined | null = undefined;
92
126
* the value only on the first call, and cache it for subsequent calls to return
93
127
* it efficiently.
94
128
*/
95
- function getOrFindGdb ( ) : string | undefined {
129
+ export function getOrFindGdb ( ) : string | undefined {
96
130
if ( cachedGdb == undefined ) {
97
131
/**
98
132
* If undefined yet, try to compute it.
@@ -141,11 +175,11 @@ function getOrFindGdb(): string | undefined {
141
175
* 'program' parameter.
142
176
* @returns an AdaConfig
143
177
*/
144
- function initializeConfig ( main : AdaMain , name ?: string ) : AdaConfig {
178
+ export function initializeConfig ( main : AdaMain , name ?: string ) : AdaConfig {
145
179
// TODO it would be nice if this and the package.json configuration snippet
146
180
// were the same.
147
181
const config : AdaConfig = {
148
- type : 'cppdbg' ,
182
+ type : ADA_DEBUG_BACKEND_TYPE ,
149
183
name : name ?? ( main ? `Ada: Debug main - ${ main . mainRelPath ( ) } ` : 'Ada: Debugger Launch' ) ,
150
184
request : 'launch' ,
151
185
targetArchitecture : process . arch ,
@@ -165,9 +199,12 @@ function initializeConfig(main: AdaMain, name?: string): AdaConfig {
165
199
return config ;
166
200
}
167
201
168
- export class AdaDebugConfigProvider implements vscode . DebugConfigurationProvider {
169
- public static adaConfigType = 'cppdbg' ;
170
-
202
+ /**
203
+ * A provider of debug configurations for Ada that is called when no launch.json
204
+ * exists. It offers a number of debug configurations based on the mains defined
205
+ * in the project, and an option to create all available configurations.
206
+ */
207
+ export class AdaInitialDebugConfigProvider implements vscode . DebugConfigurationProvider {
171
208
async provideDebugConfigurations (
172
209
folder : vscode . WorkspaceFolder | undefined ,
173
210
_token ?: vscode . CancellationToken | undefined
@@ -182,39 +219,32 @@ export class AdaDebugConfigProvider implements vscode.DebugConfigurationProvider
182
219
}
183
220
184
221
if ( folder != undefined ) {
185
- // Offer a list of known Mains from the project
186
- const itemDescription = 'Generate the associated launch configuration' ;
187
- const quickpick = await createQuickPickItems ( itemDescription ) ;
188
-
189
- const generateAll : QuickPickAdaMain = {
190
- label : 'All of the above' ,
191
- description : 'Generate launch configurations for each Main file of the project' ,
192
- adaMain : undefined ,
193
- } ;
194
- if ( quickpick . length > 1 ) {
195
- quickpick . push ( generateAll ) ;
196
- }
197
-
198
- const selectedProgram = await vscode . window . showQuickPick ( quickpick , {
199
- placeHolder : 'Select a main file to create a launch configuration' ,
200
- } ) ;
222
+ // Offer a list of choices to the User based on the same choices as
223
+ // the dynamic debug config provider + a choice to create all
224
+ // configs.
225
+ const { quickpick, generateAll } = await createQuickPicksInitialLaunch ( ) ;
226
+
227
+ const selectedProgram = await vscode . window . showQuickPick (
228
+ quickpick ,
229
+ {
230
+ placeHolder : 'Select a launch configuration to create in the launch.json file' ,
231
+ } ,
232
+ _token
233
+ ) ;
201
234
202
235
if ( selectedProgram == generateAll ) {
203
- for ( let i = 0 ; i < quickpick . length ; i ++ ) {
204
- const item = quickpick [ i ] ;
205
- if ( item != generateAll ) {
206
- assert ( item . adaMain ) ;
207
- configs . push ( initializeConfig ( item . adaMain ) ) ;
208
- }
209
- }
236
+ configs . push (
237
+ ...quickpick
238
+ . filter ( ( item ) => 'conf' in item )
239
+ . map ( ( item ) => {
240
+ assert ( 'conf' in item ) ;
241
+ return item . conf ;
242
+ } )
243
+ ) ;
210
244
} else if ( selectedProgram ) {
211
- assert ( selectedProgram . adaMain ) ;
212
-
213
- // The cppdbg debug configuration exepects the executable to be
214
- // a full path rather than a path relative to the specified
215
- // cwd. That is why we include ${workspaceFolder}.
216
- const configuration = initializeConfig ( selectedProgram . adaMain ) ;
217
- configs . push ( configuration ) ;
245
+ assert ( 'conf' in selectedProgram ) ;
246
+ assert ( selectedProgram . conf ) ;
247
+ configs . push ( selectedProgram . conf ) ;
218
248
} else {
219
249
return Promise . reject ( 'Cancelled' ) ;
220
250
}
@@ -280,32 +310,43 @@ const setupCmd = [
280
310
} ,
281
311
] ;
282
312
283
- type QuickPickAdaMain = {
284
- label : string ;
285
- description : string ;
286
- adaMain ?: AdaMain ;
287
- } ;
313
+ interface QuickPickAdaMain extends vscode . QuickPickItem {
314
+ conf : AdaConfig ;
315
+ }
288
316
289
317
/**
318
+ * This function is used to create a quick picker in the scenario where no
319
+ * launch.json exists and the User triggers the action to create a launch.json
320
+ * from scratch.
290
321
*
291
- * @param itemDescription - description to use for each item
292
- * @param mains - optional list of AdaMains if known on the caller site,
293
- * otherwise it will be computed by the call
294
- * @returns a list of objects to use with a QuickPicker, one per Main declared in the project.
322
+ * @returns an array of quick pick items representing the Ada debug
323
+ * configurations, including one item representing the action to create all
324
+ * debug configurations
295
325
*/
296
- async function createQuickPickItems (
297
- itemDescription : string ,
298
- mains ?: AdaMain [ ]
299
- ) : Promise < QuickPickAdaMain [ ] > {
300
- mains = mains ?? ( await getAdaMains ( ) ) ;
301
-
302
- await assertProjectHasMains ( ) ;
303
-
304
- return mains . map ( ( main ) => ( {
305
- label : vscode . workspace . asRelativePath ( main . mainFullPath ) ,
306
- description : itemDescription ,
307
- adaMain : main ,
326
+ export async function createQuickPicksInitialLaunch ( ) : Promise < {
327
+ quickpick : ( QuickPickAdaMain | vscode . QuickPickItem ) [ ] ;
328
+ generateAll : vscode . QuickPickItem ;
329
+ } > {
330
+ // Offer the same list of debug configurations as the dynamic provider + an
331
+ // option to generate all configurations
332
+ const configs = await adaDynamicDebugConfigProvider . provideDebugConfigurations ( ) ;
333
+ const quickpick : ( QuickPickAdaMain | vscode . QuickPickItem ) [ ] = configs . map ( ( conf ) => ( {
334
+ label : conf . name ,
335
+ conf : conf ,
308
336
} ) ) ;
337
+ const generateAll : vscode . QuickPickItem = {
338
+ label : 'All of the above' ,
339
+ description : 'Create all of the above configurations in the launch.json file' ,
340
+ } ;
341
+ if ( quickpick . length > 1 ) {
342
+ quickpick . push ( {
343
+ label : '' ,
344
+ kind : vscode . QuickPickItemKind . Separator ,
345
+ } ) ;
346
+ // Add the generateAll option only if there are multiple choices
347
+ quickpick . push ( generateAll ) ;
348
+ }
349
+ return { quickpick, generateAll } ;
309
350
}
310
351
311
352
/**
@@ -372,7 +413,11 @@ export async function getOrAskForProgram(mains?: AdaMain[]): Promise<AdaMain | u
372
413
373
414
// There is no current file or it matches no known Main of the project, so
374
415
// we offer all Mains in a QuickPicker for the user to choose from.
375
- const quickpick = await createQuickPickItems ( 'Select for debugging' , mains ) ;
416
+ const quickpick = mains . map ( ( m ) => ( {
417
+ label : m . mainRelPath ( ) ,
418
+ description : m . execRelPath ( ) ,
419
+ adaMain : m ,
420
+ } ) ) ;
376
421
const selectedProgram = await vscode . window . showQuickPick ( quickpick , {
377
422
placeHolder : 'Select a main file to debug' ,
378
423
} ) ;
@@ -404,7 +449,7 @@ async function getAdaMainForSourceFile(
404
449
function createAttachConfig ( adaMain : AdaMain ) : AdaConfig {
405
450
return {
406
451
name : `Ada: Attach debugger to running process - ${ adaMain . mainRelPath ( ) } ` ,
407
- type : 'cppdbg' ,
452
+ type : ADA_DEBUG_BACKEND_TYPE ,
408
453
request : 'attach' ,
409
454
program : `\${workspaceFolder}/${ adaMain . execRelPath ( ) } ` ,
410
455
processId : '${command:pickProcess}' ,
0 commit comments