11import assert from 'assert' ;
22import * as vscode from 'vscode' ;
33import { adaExtState } from './extension' ;
4- import { AdaMain , getAdaMains , getProjectFile } from './helpers' ;
4+ import { AdaMain , exe , getAdaMains , getEvaluatedTerminalEnv , getProjectFile } from './helpers' ;
55import { BUILD_PROJECT_TASK_NAME , getBuildTaskName } from './taskProviders' ;
6+ import path from 'path' ;
7+ import { existsSync } from 'fs' ;
68
79/**
810 * Ada Configuration for a debug session
911 */
10- interface AdaConfig extends vscode . DebugConfiguration {
12+ export interface AdaConfig extends vscode . DebugConfiguration {
1113 MIMode : string ;
1214 program : string ;
1315 cwd ?: string ;
@@ -20,8 +22,25 @@ interface AdaConfig extends vscode.DebugConfiguration {
2022 ignoreFailures : boolean ;
2123 } [ ] ;
2224 processId ?: string ;
25+ miDebuggerPath ?: string ;
2326}
2427
28+ export const adaDynamicDebugConfigProvider = {
29+ async provideDebugConfigurations (
30+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
31+ _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 ) ] ;
38+ } ) ;
39+
40+ return configs ;
41+ } ,
42+ } ;
43+
2544/**
2645 * Initialize debugging support for Ada projects.
2746 *
@@ -48,35 +67,66 @@ export function initializeDebugging(ctx: vscode.ExtensionContext) {
4867 ctx . subscriptions . push (
4968 vscode . debug . registerDebugConfigurationProvider (
5069 'ada' ,
51- {
52- async provideDebugConfigurations (
53- // eslint-disable-next-line @typescript-eslint/no-unused-vars
54- _folder : vscode . WorkspaceFolder | undefined
55- ) : Promise < vscode . DebugConfiguration [ ] > {
56- const quickpick = await createQuickPickItems ( 'Build & Debug' ) ;
57-
58- const configs : AdaConfig [ ] = quickpick . flatMap ( ( i ) => {
59- assert ( i . adaMain ) ;
60- return [ initializeConfig ( i . adaMain ) , createAttachConfig ( i . adaMain ) ] ;
61- } ) ;
62-
63- return configs ;
64- } ,
65- } ,
70+ adaDynamicDebugConfigProvider ,
6671 // The 'Dynamic' trigger type only works if the package.json lists
6772 // "onDebugDynamicConfigurations:ada" as part of the
6873 // activationEvents.
6974 vscode . DebugConfigurationProviderTriggerKind . Dynamic
7075 )
7176 ) ;
7277
73- // TODO it is also possible to register another provider with trigger kind
74- // 'Dynamic', however the role of such a provider is unclear. In practical
75- // experiments it ends up never being called. The above provider is enough
76- // to make it possible to launch debug sessions without a launch.json.
77-
7878 return provider ;
7979}
80+
81+ let cachedGdb : string | undefined | null = undefined ;
82+
83+ /**
84+ *
85+ * @returns the full path to the `gdb` executable, taking into consideration the
86+ * `PATH` variable in the `terminal.integrated.env.*` setting if set. Otherwise,
87+ * the `PATH` variable of the current process environment is considered.
88+ *
89+ * The current process environment is unlikely to change during the lifetime of
90+ * the extension, and we already prompt the User to reload the window in case
91+ * the `terminal.integrated.env.*` variables change. For this reason, we compute
92+ * the value only on the first call, and cache it for subsequent calls to return
93+ * it efficiently.
94+ */
95+ function getOrFindGdb ( ) : string | undefined {
96+ if ( cachedGdb == undefined ) {
97+ /**
98+ * If undefined yet, try to compute it.
99+ */
100+ const env = getEvaluatedTerminalEnv ( ) ;
101+ let pathVal : string ;
102+ if ( env && 'PATH' in env ) {
103+ pathVal = env . PATH ;
104+ } else if ( 'PATH' in process . env ) {
105+ pathVal = process . env . PATH ?? '' ;
106+ } else {
107+ pathVal = '' ;
108+ }
109+
110+ const gdb = pathVal
111+ . split ( path . delimiter )
112+ . map < string > ( ( v ) => path . join ( v , 'gdb' + exe ) )
113+ . find ( existsSync ) ;
114+
115+ if ( gdb ) {
116+ // Found
117+ cachedGdb = gdb ;
118+ return cachedGdb ;
119+ } else {
120+ // Not found. Assign null to cache to avoid recomputing at every call.
121+ cachedGdb = null ;
122+ }
123+ }
124+
125+ // When returning, coerce null to undefined because the distinction doesn't
126+ // matter on the caller side.
127+ return cachedGdb ?? undefined ;
128+ }
129+
80130/**
81131 * Initialize a debug configuration based on 'cppdbg' for the given executable
82132 * if specified. Otherwise the program field includes
@@ -109,6 +159,7 @@ function initializeConfig(main: AdaMain, name?: string): AdaConfig {
109159 MIMode : 'gdb' ,
110160 preLaunchTask : main ? getBuildTaskName ( main ) : BUILD_PROJECT_TASK_NAME ,
111161 setupCommands : setupCmd ,
162+ miDebuggerPath : getOrFindGdb ( ) ,
112163 } ;
113164
114165 return config ;
@@ -364,5 +415,6 @@ function createAttachConfig(adaMain: AdaMain): AdaConfig {
364415 * to trigger an unwanted rebuild, so we don't set a preLaunchTask.
365416 */
366417 // preLaunchTask: adaMain ? getBuildTaskName(adaMain) : BUILD_PROJECT_TASK_NAME,
418+ miDebuggerPath : getOrFindGdb ( ) ,
367419 } ;
368420}
0 commit comments