1- import * as vscode from 'vscode' ;
21import * as path from 'path' ;
2+ import * as vscode from 'vscode' ;
33import { ContextClients } from './clients' ;
44import { getExecutables , getMains } from './helpers' ;
55
@@ -26,72 +26,62 @@ interface AdaConfig extends vscode.DebugConfiguration {
2626 * @param clients - the language clients
2727 * @returns the debug configuration provider
2828 */
29- export async function initializeDebugging ( ctx : vscode . ExtensionContext , clients : ContextClients ) {
29+ export function initializeDebugging ( ctx : vscode . ExtensionContext , clients : ContextClients ) {
30+ // Instantiate a DebugConfigProvider for Ada and register it.
3031 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- }
4932
50- if ( ! status ) {
51- const initialDebugConfiguration = initializeConfig ( undefined , '${command:AskForProgram}' ) ;
33+ // This provider is registered for the 'ada' debugger type. It means that
34+ // it is triggered either when a configuration with type 'ada' is launched,
35+ // or when the applicable context of the 'ada' debugger type is enabled
36+ // (see package.json/debuggers).
37+ //
38+ // However concretely we never define debug configurations of the type
39+ // 'ada'. All the provided configurations use the type 'cppdbg'. This means
40+ // that once a 'cppdbg' is declared in the launch.json file, this provider
41+ // is no longer called since it is not registered for the type 'cppdbg'.
42+ // Moreover, it is somewhat discouraged to register it for the type
43+ // 'cppdbg' since that type is provided by another extension.
44+ ctx . subscriptions . push ( vscode . debug . registerDebugConfigurationProvider ( 'ada' , provider ) ) ;
5245
53- configurations . push ( initialDebugConfiguration ) ;
46+ // TODO it is also possible to register another provider with trigger kind
47+ // 'Dynamic', however the role of such a provider is unclear. In practical
48+ // experiments it ends up never being called. The above provider is enough
49+ // to make it possible to launch debug sessions without a launch.json.
5450
55- await workspaceConfig . update (
56- 'launch.configurations' ,
57- configurations ,
58- vscode . ConfigurationTarget . Workspace
59- ) ;
60- }
6151 return provider ;
6252}
6353/**
64- * Initialize the GDB debug configuration for an executable,
65- * either program or command must be specified
54+ * Initialize a debug configuration based on 'cppdbg' for the given executable
55+ * if specified. Otherwise the program field includes
56+ * ${command:ada.askForProgram} to prompt the User for an executable to debug.
57+ *
6658 * @param program - the executable to debug (optional)
67- * @param command - the command to collect the program to debug (optional)
6859 * @returns an AdaConfig
6960 */
70- function initializeConfig ( program ?: string , command ?: string ) : AdaConfig {
71- // Get the executable name from the relative path
61+ function initializeConfig ( program ?: string ) : AdaConfig {
62+ // TODO it would be nice if this and the package.json configuration snippet
63+ // were the same.
7264 const config : AdaConfig = {
7365 type : 'cppdbg' ,
74- name : 'Ada Config ' ,
66+ name : 'Ada: Debugger Launch ' ,
7567 request : 'launch' ,
7668 targetArchitecture : process . arch ,
7769 cwd : '${workspaceFolder}' ,
78- program : 'Ada executable to debug ' ,
70+ program : '${workspaceFolder}/${command:ada.askForProgram} ' ,
7971 stopAtEntry : false ,
8072 externalConsole : false ,
8173 args : [ ] ,
8274 MIMode : 'gdb' ,
83- // preLaunchTask: 'gpr: Build Executable for File ' + main_name,
8475 preLaunchTask : 'ada: Build current project' ,
8576 setupCommands : setupCmd ,
8677 } ;
87- if ( command ) {
88- config . name = 'Debug Ada' ;
89- config . program = command ;
90- } else if ( program ) {
78+
79+ if ( program ) {
9180 const name = path . basename ( program ) ;
92- config . name = 'Debug executable ' + name ;
81+ config . name = 'Ada: Debug executable - ' + name ;
9382 config . program = program ;
9483 }
84+
9585 return config ;
9686}
9787
@@ -102,19 +92,23 @@ export class AdaDebugConfigProvider implements vscode.DebugConfigurationProvider
10292 constructor ( clients : ContextClients ) {
10393 this . clients = clients ;
10494 }
105- // Provider
95+
10696 async provideDebugConfigurations (
10797 folder : vscode . WorkspaceFolder | undefined ,
10898 _token ?: vscode . CancellationToken | undefined
10999 ) : Promise < vscode . DebugConfiguration [ ] > {
110- // provide a non-existent debug/launch configuration
111- const config : vscode . DebugConfiguration [ ] = [ ] ;
100+ // This method is called when no launch.json exists. The provider
101+ // should return a set of configurations to initialize the launch.json
102+ // file with.
103+ const configs : vscode . DebugConfiguration [ ] = [ ] ;
104+
112105 if ( _token ?. isCancellationRequested ) {
113- return [ ] ;
106+ return Promise . reject ( 'Cancelled' ) ;
114107 }
108+
115109 if ( folder != undefined ) {
110+ // Offer a list of known Mains from the project
116111 const execs = await getExecutables ( this . clients . adaClient ) ;
117- // Show the option for the user to choose the executable
118112 const quickpick = execs . map ( ( e ) => ( {
119113 label : vscode . workspace . asRelativePath ( e ) ,
120114 description : 'Generate the associated configuration' ,
@@ -123,27 +117,48 @@ export class AdaDebugConfigProvider implements vscode.DebugConfigurationProvider
123117 placeHolder : 'Select a program to debug' ,
124118 } ) ;
125119 if ( selectedProgram ) {
126- const configuration = initializeConfig ( selectedProgram . label ) ;
127- config . push ( configuration ) ;
120+ // The cppdbg debug configuration exepects the executable to be
121+ // a full path rather than a path relative to the specified
122+ // cwd. That is why we include ${workspaceFolder}.
123+ const configuration = initializeConfig (
124+ `\${workspaceFolder}/${ selectedProgram . label } `
125+ ) ;
126+ configs . push ( configuration ) ;
127+ } else {
128+ return Promise . reject ( 'Cancelled' ) ;
128129 }
129- return config ;
130- } else {
131- return config ;
132130 }
131+
132+ return configs ;
133133 }
134134
135135 async resolveDebugConfiguration (
136136 _folder : vscode . WorkspaceFolder | undefined ,
137137 debugConfiguration : vscode . DebugConfiguration ,
138138 _token ?: vscode . CancellationToken | undefined
139139 ) : Promise < vscode . DebugConfiguration | undefined > {
140- // resolve a incompleted debug/launch configuration
140+ // This method is called when a debug session is being started. The
141+ // debug configuration either comes from the launch.json file, or is
142+ // empty when no launch.json exists.
143+
141144 if ( _token ?. isCancellationRequested ) {
142145 return undefined ;
143146 }
147+
144148 if ( debugConfiguration . request == 'launch' ) {
149+ // When the given debug configuration has its fields set, it means
150+ // that the debug configuration is coming from a launch.json file
151+ // and we don't want to alter it. Concretely this never occurs
152+ // because we register this provider for the debugger type 'ada'
153+ // which we never create in launch.json files. Instead we always
154+ // create 'cppdbg' configurations which never go through this
155+ // provider.
145156 return debugConfiguration ;
146157 }
158+
159+ // We are operating without a launch.json. So we try to determine the
160+ // program to debug dynamically. If the current editor matches one of
161+ // the Mains of the project, then debug the corresponding executable.
147162 const file = vscode . window . activeTextEditor ?. document . uri . path ;
148163 if ( file != undefined ) {
149164 const mains = await getMains ( this . clients . adaClient ) ;
@@ -154,33 +169,44 @@ export class AdaDebugConfigProvider implements vscode.DebugConfigurationProvider
154169 return config ;
155170 }
156171 }
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- }
170172 }
173+
174+ // There is no current file or it matches no known Main of the project,
175+ // so we offer all Main in a QuickPicker for the user to choose from.
176+ const quickpick = execs . map ( ( e ) => ( {
177+ label : vscode . workspace . asRelativePath ( e ) ,
178+ description : 'Run & Debug' ,
179+ fullPath : e ,
180+ } ) ) ;
181+ const selectedProgram = await vscode . window . showQuickPick ( quickpick , {
182+ placeHolder : 'Select an executable to debug' ,
183+ } ) ;
184+ if ( selectedProgram ) {
185+ // This is an in-memory configuration that will not be stored. It's
186+ // okay to use the full path directly instead of using
187+ // ${workspaceFolder}.
188+ const configuration = initializeConfig ( selectedProgram . fullPath ) ;
189+ return configuration ;
190+ }
191+
171192 return undefined ;
172193 }
173194
174195 /**
175- * Resolves the program path fron the 'Debug Ada' default configuration
176- * @returns the executable path to debug
196+ * Consults the project for a list of Mains. If only one is defined, it is
197+ * returned immediately. If multiple ones are defines, a QuickPicker is
198+ * given to the User to choose and executable to debug or to specify in a
199+ * debug configuration.
200+ *
201+ * @returns the path of the executable to debug relative to the workspace
177202 */
178- async initDebugCmd ( ) : Promise < string | undefined > {
203+ async askForProgram ( ) : Promise < string | undefined > {
179204 const file = vscode . window . activeTextEditor ?. document . uri . path ;
180205 const mains = await getMains ( this . clients . adaClient ) ;
181206 const execs = await getExecutables ( this . clients . adaClient ) ;
182207
183- if ( mains . length == 1 ) return execs [ 0 ] ;
208+ if ( execs . length == 1 ) return vscode . workspace . asRelativePath ( execs [ 0 ] ) ;
209+
184210 if ( file != undefined ) {
185211 for ( let i = 0 ; i < mains . length ; i ++ ) {
186212 if ( file == mains [ i ] ) {
@@ -198,8 +224,9 @@ export class AdaDebugConfigProvider implements vscode.DebugConfigurationProvider
198224 } ) ;
199225 if ( selectedProgram ) {
200226 const index = mains . indexOf ( selectedProgram . main ) ;
201- return execs [ index ] ;
227+ return vscode . workspace . asRelativePath ( execs [ index ] ) ;
202228 }
229+
203230 return undefined ;
204231 }
205232}
0 commit comments