11import assert from 'assert' ;
2+ import { existsSync } from 'fs' ;
3+ import path from 'path' ;
24import * as vscode from 'vscode' ;
35import { 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' ;
514import { 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 ';
817
918/**
1019 * Ada Configuration for a debug session
@@ -29,14 +38,26 @@ export const adaDynamicDebugConfigProvider = {
2938 async provideDebugConfigurations (
3039 // eslint-disable-next-line @typescript-eslint/no-unused-vars
3140 _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 ) ] ;
3845 } ) ;
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+ }
4061 return configs ;
4162 } ,
4263} ;
@@ -47,9 +68,16 @@ export const adaDynamicDebugConfigProvider = {
4768 * @param ctx - the Ada extension context
4869 * @returns the debug configuration provider
4970 */
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+ } {
5179 // Instantiate a DebugConfigProvider for Ada and register it.
52- const provider = new AdaDebugConfigProvider ( ) ;
80+ const providerInitial = new AdaInitialDebugConfigProvider ( ) ;
5381
5482 // This provider is registered for the 'ada' debugger type. It means that
5583 // it is triggered either when a configuration with type 'ada' is launched,
@@ -62,8 +90,14 @@ export function initializeDebugging(ctx: vscode.ExtensionContext) {
6290 // is no longer called since it is not registered for the type 'cppdbg'.
6391 // Moreover, it is somewhat discouraged to register it for the type
6492 // '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+ */
67101 ctx . subscriptions . push (
68102 vscode . debug . registerDebugConfigurationProvider (
69103 'ada' ,
@@ -75,7 +109,7 @@ export function initializeDebugging(ctx: vscode.ExtensionContext) {
75109 )
76110 ) ;
77111
78- return provider ;
112+ return { providerInitial : providerInitial , providerDynamic : adaDynamicDebugConfigProvider } ;
79113}
80114
81115let cachedGdb : string | undefined | null = undefined ;
@@ -92,7 +126,7 @@ let cachedGdb: string | undefined | null = undefined;
92126 * the value only on the first call, and cache it for subsequent calls to return
93127 * it efficiently.
94128 */
95- function getOrFindGdb ( ) : string | undefined {
129+ export function getOrFindGdb ( ) : string | undefined {
96130 if ( cachedGdb == undefined ) {
97131 /**
98132 * If undefined yet, try to compute it.
@@ -141,11 +175,11 @@ function getOrFindGdb(): string | undefined {
141175 * 'program' parameter.
142176 * @returns an AdaConfig
143177 */
144- function initializeConfig ( main : AdaMain , name ?: string ) : AdaConfig {
178+ export function initializeConfig ( main : AdaMain , name ?: string ) : AdaConfig {
145179 // TODO it would be nice if this and the package.json configuration snippet
146180 // were the same.
147181 const config : AdaConfig = {
148- type : 'cppdbg' ,
182+ type : ADA_DEBUG_BACKEND_TYPE ,
149183 name : name ?? ( main ? `Ada: Debug main - ${ main . mainRelPath ( ) } ` : 'Ada: Debugger Launch' ) ,
150184 request : 'launch' ,
151185 targetArchitecture : process . arch ,
@@ -165,9 +199,12 @@ function initializeConfig(main: AdaMain, name?: string): AdaConfig {
165199 return config ;
166200}
167201
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 {
171208 async provideDebugConfigurations (
172209 folder : vscode . WorkspaceFolder | undefined ,
173210 _token ?: vscode . CancellationToken | undefined
@@ -182,39 +219,32 @@ export class AdaDebugConfigProvider implements vscode.DebugConfigurationProvider
182219 }
183220
184221 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+ ) ;
201234
202235 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+ ) ;
210244 } 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 ) ;
218248 } else {
219249 return Promise . reject ( 'Cancelled' ) ;
220250 }
@@ -280,32 +310,43 @@ const setupCmd = [
280310 } ,
281311] ;
282312
283- type QuickPickAdaMain = {
284- label : string ;
285- description : string ;
286- adaMain ?: AdaMain ;
287- } ;
313+ interface QuickPickAdaMain extends vscode . QuickPickItem {
314+ conf : AdaConfig ;
315+ }
288316
289317/**
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.
290321 *
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
295325 */
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 ,
308336 } ) ) ;
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 } ;
309350}
310351
311352/**
@@ -372,7 +413,11 @@ export async function getOrAskForProgram(mains?: AdaMain[]): Promise<AdaMain | u
372413
373414 // There is no current file or it matches no known Main of the project, so
374415 // 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+ } ) ) ;
376421 const selectedProgram = await vscode . window . showQuickPick ( quickpick , {
377422 placeHolder : 'Select a main file to debug' ,
378423 } ) ;
@@ -404,7 +449,7 @@ async function getAdaMainForSourceFile(
404449function createAttachConfig ( adaMain : AdaMain ) : AdaConfig {
405450 return {
406451 name : `Ada: Attach debugger to running process - ${ adaMain . mainRelPath ( ) } ` ,
407- type : 'cppdbg' ,
452+ type : ADA_DEBUG_BACKEND_TYPE ,
408453 request : 'attach' ,
409454 program : `\${workspaceFolder}/${ adaMain . execRelPath ( ) } ` ,
410455 processId : '${command:pickProcess}' ,
0 commit comments