1
- import * as vscode from 'vscode' ;
2
1
import * as path from 'path' ;
2
+ import * as vscode from 'vscode' ;
3
3
import { ContextClients } from './clients' ;
4
4
import { getExecutables , getMains } from './helpers' ;
5
5
@@ -26,72 +26,62 @@ interface AdaConfig extends vscode.DebugConfiguration {
26
26
* @param clients - the language clients
27
27
* @returns the debug configuration provider
28
28
*/
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.
30
31
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
32
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 ) ) ;
52
45
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.
54
50
55
- await workspaceConfig . update (
56
- 'launch.configurations' ,
57
- configurations ,
58
- vscode . ConfigurationTarget . Workspace
59
- ) ;
60
- }
61
51
return provider ;
62
52
}
63
53
/**
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
+ *
66
58
* @param program - the executable to debug (optional)
67
- * @param command - the command to collect the program to debug (optional)
68
59
* @returns an AdaConfig
69
60
*/
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.
72
64
const config : AdaConfig = {
73
65
type : 'cppdbg' ,
74
- name : 'Ada Config ' ,
66
+ name : 'Ada: Debugger Launch ' ,
75
67
request : 'launch' ,
76
68
targetArchitecture : process . arch ,
77
69
cwd : '${workspaceFolder}' ,
78
- program : 'Ada executable to debug ' ,
70
+ program : '${workspaceFolder}/${command:ada.askForProgram} ' ,
79
71
stopAtEntry : false ,
80
72
externalConsole : false ,
81
73
args : [ ] ,
82
74
MIMode : 'gdb' ,
83
- // preLaunchTask: 'gpr: Build Executable for File ' + main_name,
84
75
preLaunchTask : 'ada: Build current project' ,
85
76
setupCommands : setupCmd ,
86
77
} ;
87
- if ( command ) {
88
- config . name = 'Debug Ada' ;
89
- config . program = command ;
90
- } else if ( program ) {
78
+
79
+ if ( program ) {
91
80
const name = path . basename ( program ) ;
92
- config . name = 'Debug executable ' + name ;
81
+ config . name = 'Ada: Debug executable - ' + name ;
93
82
config . program = program ;
94
83
}
84
+
95
85
return config ;
96
86
}
97
87
@@ -102,19 +92,23 @@ export class AdaDebugConfigProvider implements vscode.DebugConfigurationProvider
102
92
constructor ( clients : ContextClients ) {
103
93
this . clients = clients ;
104
94
}
105
- // Provider
95
+
106
96
async provideDebugConfigurations (
107
97
folder : vscode . WorkspaceFolder | undefined ,
108
98
_token ?: vscode . CancellationToken | undefined
109
99
) : 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
+
112
105
if ( _token ?. isCancellationRequested ) {
113
- return [ ] ;
106
+ return Promise . reject ( 'Cancelled' ) ;
114
107
}
108
+
115
109
if ( folder != undefined ) {
110
+ // Offer a list of known Mains from the project
116
111
const execs = await getExecutables ( this . clients . adaClient ) ;
117
- // Show the option for the user to choose the executable
118
112
const quickpick = execs . map ( ( e ) => ( {
119
113
label : vscode . workspace . asRelativePath ( e ) ,
120
114
description : 'Generate the associated configuration' ,
@@ -123,27 +117,48 @@ export class AdaDebugConfigProvider implements vscode.DebugConfigurationProvider
123
117
placeHolder : 'Select a program to debug' ,
124
118
} ) ;
125
119
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' ) ;
128
129
}
129
- return config ;
130
- } else {
131
- return config ;
132
130
}
131
+
132
+ return configs ;
133
133
}
134
134
135
135
async resolveDebugConfiguration (
136
136
_folder : vscode . WorkspaceFolder | undefined ,
137
137
debugConfiguration : vscode . DebugConfiguration ,
138
138
_token ?: vscode . CancellationToken | undefined
139
139
) : 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
+
141
144
if ( _token ?. isCancellationRequested ) {
142
145
return undefined ;
143
146
}
147
+
144
148
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.
145
156
return debugConfiguration ;
146
157
}
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.
147
162
const file = vscode . window . activeTextEditor ?. document . uri . path ;
148
163
if ( file != undefined ) {
149
164
const mains = await getMains ( this . clients . adaClient ) ;
@@ -154,33 +169,44 @@ export class AdaDebugConfigProvider implements vscode.DebugConfigurationProvider
154
169
return config ;
155
170
}
156
171
}
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
172
}
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
+
171
192
return undefined ;
172
193
}
173
194
174
195
/**
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
177
202
*/
178
- async initDebugCmd ( ) : Promise < string | undefined > {
203
+ async askForProgram ( ) : Promise < string | undefined > {
179
204
const file = vscode . window . activeTextEditor ?. document . uri . path ;
180
205
const mains = await getMains ( this . clients . adaClient ) ;
181
206
const execs = await getExecutables ( this . clients . adaClient ) ;
182
207
183
- if ( mains . length == 1 ) return execs [ 0 ] ;
208
+ if ( execs . length == 1 ) return vscode . workspace . asRelativePath ( execs [ 0 ] ) ;
209
+
184
210
if ( file != undefined ) {
185
211
for ( let i = 0 ; i < mains . length ; i ++ ) {
186
212
if ( file == mains [ i ] ) {
@@ -198,8 +224,9 @@ export class AdaDebugConfigProvider implements vscode.DebugConfigurationProvider
198
224
} ) ;
199
225
if ( selectedProgram ) {
200
226
const index = mains . indexOf ( selectedProgram . main ) ;
201
- return execs [ index ] ;
227
+ return vscode . workspace . asRelativePath ( execs [ index ] ) ;
202
228
}
229
+
203
230
return undefined ;
204
231
}
205
232
}
0 commit comments