@theia/debug 1.29.0-next.6 → 1.29.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (127) hide show
  1. package/lib/browser/breakpoint/breakpoint-marker.d.ts +1 -1
  2. package/lib/browser/breakpoint/breakpoint-marker.d.ts.map +1 -1
  3. package/lib/browser/console/debug-console-items.d.ts +1 -1
  4. package/lib/browser/console/debug-console-items.d.ts.map +1 -1
  5. package/lib/browser/console/debug-console-session.d.ts +1 -1
  6. package/lib/browser/console/debug-console-session.d.ts.map +1 -1
  7. package/lib/browser/debug-configuration-manager.d.ts +15 -7
  8. package/lib/browser/debug-configuration-manager.d.ts.map +1 -1
  9. package/lib/browser/debug-configuration-manager.js +81 -31
  10. package/lib/browser/debug-configuration-manager.js.map +1 -1
  11. package/lib/browser/debug-configuration-model.d.ts +3 -0
  12. package/lib/browser/debug-configuration-model.d.ts.map +1 -1
  13. package/lib/browser/debug-configuration-model.js +17 -10
  14. package/lib/browser/debug-configuration-model.js.map +1 -1
  15. package/lib/browser/debug-contribution.d.ts +1 -1
  16. package/lib/browser/debug-contribution.d.ts.map +1 -1
  17. package/lib/browser/debug-frontend-application-contribution.d.ts.map +1 -1
  18. package/lib/browser/debug-frontend-application-contribution.js +6 -3
  19. package/lib/browser/debug-frontend-application-contribution.js.map +1 -1
  20. package/lib/browser/debug-prefix-configuration.js +3 -3
  21. package/lib/browser/debug-prefix-configuration.js.map +1 -1
  22. package/lib/browser/debug-schema-updater.d.ts.map +1 -1
  23. package/lib/browser/debug-schema-updater.js +55 -4
  24. package/lib/browser/debug-schema-updater.js.map +1 -1
  25. package/lib/browser/debug-session-connection.d.ts +1 -1
  26. package/lib/browser/debug-session-connection.d.ts.map +1 -1
  27. package/lib/browser/debug-session-contribution.d.ts +2 -2
  28. package/lib/browser/debug-session-contribution.d.ts.map +1 -1
  29. package/lib/browser/debug-session-contribution.js.map +1 -1
  30. package/lib/browser/debug-session-manager.d.ts +10 -4
  31. package/lib/browser/debug-session-manager.d.ts.map +1 -1
  32. package/lib/browser/debug-session-manager.js +64 -7
  33. package/lib/browser/debug-session-manager.js.map +1 -1
  34. package/lib/browser/debug-session-options.d.ts +36 -6
  35. package/lib/browser/debug-session-options.d.ts.map +1 -1
  36. package/lib/browser/debug-session-options.js +53 -10
  37. package/lib/browser/debug-session-options.js.map +1 -1
  38. package/lib/browser/debug-session.d.ts +4 -4
  39. package/lib/browser/debug-session.d.ts.map +1 -1
  40. package/lib/browser/debug-session.js +7 -0
  41. package/lib/browser/debug-session.js.map +1 -1
  42. package/lib/browser/disassembly-view/disassembly-view-instruction-renderer.d.ts +1 -1
  43. package/lib/browser/disassembly-view/disassembly-view-instruction-renderer.d.ts.map +1 -1
  44. package/lib/browser/disassembly-view/disassembly-view-utilities.d.ts +1 -1
  45. package/lib/browser/disassembly-view/disassembly-view-utilities.d.ts.map +1 -1
  46. package/lib/browser/editor/debug-breakpoint-widget.d.ts +3 -1
  47. package/lib/browser/editor/debug-breakpoint-widget.d.ts.map +1 -1
  48. package/lib/browser/editor/debug-breakpoint-widget.js +5 -4
  49. package/lib/browser/editor/debug-breakpoint-widget.js.map +1 -1
  50. package/lib/browser/editor/debug-editor-model.d.ts +1 -1
  51. package/lib/browser/editor/debug-editor-model.d.ts.map +1 -1
  52. package/lib/browser/editor/debug-exception-widget.d.ts +2 -0
  53. package/lib/browser/editor/debug-exception-widget.d.ts.map +1 -1
  54. package/lib/browser/editor/debug-exception-widget.js +6 -5
  55. package/lib/browser/editor/debug-exception-widget.js.map +1 -1
  56. package/lib/browser/editor/debug-hover-widget.d.ts.map +1 -1
  57. package/lib/browser/editor/debug-hover-widget.js +35 -9
  58. package/lib/browser/editor/debug-hover-widget.js.map +1 -1
  59. package/lib/browser/model/debug-breakpoint.d.ts +1 -1
  60. package/lib/browser/model/debug-breakpoint.d.ts.map +1 -1
  61. package/lib/browser/model/debug-source-breakpoint.d.ts +1 -1
  62. package/lib/browser/model/debug-source-breakpoint.d.ts.map +1 -1
  63. package/lib/browser/model/debug-source.d.ts +1 -1
  64. package/lib/browser/model/debug-source.d.ts.map +1 -1
  65. package/lib/browser/model/debug-stack-frame.d.ts +1 -1
  66. package/lib/browser/model/debug-stack-frame.d.ts.map +1 -1
  67. package/lib/browser/model/debug-thread.d.ts +1 -1
  68. package/lib/browser/model/debug-thread.d.ts.map +1 -1
  69. package/lib/browser/view/debug-configuration-select.d.ts +4 -1
  70. package/lib/browser/view/debug-configuration-select.d.ts.map +1 -1
  71. package/lib/browser/view/debug-configuration-select.js +36 -14
  72. package/lib/browser/view/debug-configuration-select.js.map +1 -1
  73. package/lib/browser/view/debug-configuration-widget.d.ts +3 -2
  74. package/lib/browser/view/debug-configuration-widget.d.ts.map +1 -1
  75. package/lib/browser/view/debug-configuration-widget.js +5 -1
  76. package/lib/browser/view/debug-configuration-widget.js.map +1 -1
  77. package/lib/browser/view/debug-toolbar-widget.d.ts +8 -7
  78. package/lib/browser/view/debug-toolbar-widget.d.ts.map +1 -1
  79. package/lib/browser/view/debug-toolbar-widget.js +5 -1
  80. package/lib/browser/view/debug-toolbar-widget.js.map +1 -1
  81. package/lib/browser/view/debug-watch-expression.d.ts +1 -1
  82. package/lib/browser/view/debug-watch-expression.d.ts.map +1 -1
  83. package/lib/common/debug-compound.d.ts +15 -0
  84. package/lib/common/debug-compound.d.ts.map +1 -0
  85. package/lib/common/debug-compound.js +27 -0
  86. package/lib/common/debug-compound.js.map +1 -0
  87. package/lib/common/debug-configuration.d.ts +2 -2
  88. package/lib/common/debug-configuration.d.ts.map +1 -1
  89. package/lib/common/debug-configuration.js.map +1 -1
  90. package/lib/node/debug-adapter-factory.d.ts.map +1 -1
  91. package/lib/node/debug-adapter-factory.js +1 -2
  92. package/lib/node/debug-adapter-factory.js.map +1 -1
  93. package/package.json +17 -17
  94. package/src/browser/breakpoint/breakpoint-marker.ts +1 -1
  95. package/src/browser/console/debug-console-items.tsx +1 -1
  96. package/src/browser/console/debug-console-session.ts +1 -1
  97. package/src/browser/debug-configuration-manager.ts +92 -40
  98. package/src/browser/debug-configuration-model.ts +19 -10
  99. package/src/browser/debug-contribution.ts +1 -1
  100. package/src/browser/debug-frontend-application-contribution.ts +10 -3
  101. package/src/browser/debug-prefix-configuration.ts +3 -3
  102. package/src/browser/debug-schema-updater.ts +56 -5
  103. package/src/browser/debug-session-connection.ts +1 -1
  104. package/src/browser/debug-session-contribution.ts +2 -2
  105. package/src/browser/debug-session-manager.ts +79 -8
  106. package/src/browser/debug-session-options.ts +72 -15
  107. package/src/browser/debug-session.tsx +9 -3
  108. package/src/browser/disassembly-view/disassembly-view-instruction-renderer.ts +1 -1
  109. package/src/browser/disassembly-view/disassembly-view-utilities.ts +1 -1
  110. package/src/browser/disassembly-view/disassembly-view-widget.ts +1 -1
  111. package/src/browser/editor/debug-breakpoint-widget.tsx +7 -5
  112. package/src/browser/editor/debug-editor-model.ts +1 -1
  113. package/src/browser/editor/debug-exception-widget.tsx +7 -5
  114. package/src/browser/editor/debug-hover-widget.ts +43 -9
  115. package/src/browser/model/debug-breakpoint.tsx +1 -1
  116. package/src/browser/model/debug-source-breakpoint.tsx +1 -1
  117. package/src/browser/model/debug-source.ts +1 -1
  118. package/src/browser/model/debug-stack-frame.tsx +1 -1
  119. package/src/browser/model/debug-thread.tsx +1 -1
  120. package/src/browser/view/debug-configuration-select.tsx +42 -20
  121. package/src/browser/view/debug-configuration-widget.tsx +7 -2
  122. package/src/browser/view/debug-toolbar-widget.tsx +7 -2
  123. package/src/browser/view/debug-watch-expression.tsx +1 -1
  124. package/src/common/debug-adapter-session.ts +1 -1
  125. package/src/common/debug-compound.ts +32 -0
  126. package/src/common/debug-configuration.ts +2 -2
  127. package/src/node/debug-adapter-factory.ts +2 -3
@@ -30,7 +30,7 @@ import { LabelProvider, PreferenceScope, PreferenceService, QuickPickValue, Stor
30
30
  import { QuickPickService } from '@theia/core/lib/common/quick-pick-service';
31
31
  import { WorkspaceService } from '@theia/workspace/lib/browser/workspace-service';
32
32
  import { DebugConfigurationModel } from './debug-configuration-model';
33
- import { DebugSessionOptions } from './debug-session-options';
33
+ import { DebugSessionOptions, DynamicDebugConfigurationSessionOptions } from './debug-session-options';
34
34
  import { DebugService } from '../common/debug-service';
35
35
  import { ContextKey, ContextKeyService } from '@theia/core/lib/browser/context-key-service';
36
36
  import { DebugConfiguration } from '../common/debug-common';
@@ -41,6 +41,7 @@ import * as monaco from '@theia/monaco-editor-core';
41
41
  import { ICommandService } from '@theia/monaco-editor-core/esm/vs/platform/commands/common/commands';
42
42
  import { StandaloneServices } from '@theia/monaco-editor-core/esm/vs/editor/standalone/browser/standaloneServices';
43
43
  import { nls } from '@theia/core';
44
+ import { DebugCompound } from '../common/debug-compound';
44
45
 
45
46
  export interface WillProvideDebugConfiguration extends WaitUntilEvent {
46
47
  }
@@ -94,7 +95,7 @@ export class DebugConfigurationManager {
94
95
 
95
96
  protected initialized: Promise<void>;
96
97
 
97
- protected recentDynamicOptionsTracker: DebugSessionOptions[] = [];
98
+ protected recentDynamicOptionsTracker: DynamicDebugConfigurationSessionOptions[] = [];
98
99
 
99
100
  @postConstruct()
100
101
  protected async init(): Promise<void> {
@@ -141,10 +142,10 @@ export class DebugConfigurationManager {
141
142
  protected *getAll(): IterableIterator<DebugSessionOptions> {
142
143
  for (const model of this.models.values()) {
143
144
  for (const configuration of model.configurations) {
144
- yield {
145
- configuration,
146
- workspaceFolderUri: model.workspaceFolderUri
147
- };
145
+ yield this.configurationToOptions(configuration, model.workspaceFolderUri);
146
+ }
147
+ for (const compound of model.compounds) {
148
+ yield this.compoundToOptions(compound, model.workspaceFolderUri);
148
149
  }
149
150
  }
150
151
  }
@@ -159,7 +160,7 @@ export class DebugConfigurationManager {
159
160
  }
160
161
  protected *doGetSupported(debugTypes: Set<string>): IterableIterator<DebugSessionOptions> {
161
162
  for (const options of this.getAll()) {
162
- if (debugTypes.has(options.configuration.type)) {
163
+ if (options.configuration && debugTypes.has(options.configuration.type)) {
163
164
  yield options;
164
165
  }
165
166
  }
@@ -171,8 +172,7 @@ export class DebugConfigurationManager {
171
172
  }
172
173
 
173
174
  async getSelectedConfiguration(): Promise<DebugSessionOptions | undefined> {
174
- // providerType applies to dynamic configurations only
175
- if (!this._currentOptions?.providerType) {
175
+ if (!DebugSessionOptions.isDynamic(this._currentOptions)) {
176
176
  return this._currentOptions;
177
177
  }
178
178
 
@@ -188,7 +188,7 @@ export class DebugConfigurationManager {
188
188
  throw new Error(message);
189
189
  }
190
190
 
191
- return { configuration, providerType };
191
+ return { name, configuration, providerType };
192
192
  }
193
193
 
194
194
  set current(option: DebugSessionOptions | undefined) {
@@ -197,7 +197,7 @@ export class DebugConfigurationManager {
197
197
  }
198
198
 
199
199
  protected updateRecentlyUsedDynamicConfigurationOptions(option: DebugSessionOptions | undefined): void {
200
- if (option?.providerType) { // if it's a dynamic configuration option
200
+ if (DebugSessionOptions.isDynamic(option)) {
201
201
  // Removing an item already present in the list
202
202
  const index = this.recentDynamicOptionsTracker.findIndex(item => this.dynamicOptionsMatch(item, option));
203
203
  if (index > -1) {
@@ -212,32 +212,33 @@ export class DebugConfigurationManager {
212
212
  }
213
213
  }
214
214
 
215
- protected dynamicOptionsMatch(one: DebugSessionOptions, other: DebugSessionOptions): boolean {
215
+ protected dynamicOptionsMatch(one: DynamicDebugConfigurationSessionOptions, other: DynamicDebugConfigurationSessionOptions): boolean {
216
216
  return one.providerType !== undefined
217
- && one.configuration.name === other.configuration.name
218
- && one.providerType === other.providerType;
217
+ && one.configuration.name === other.configuration.name
218
+ && one.providerType === other.providerType;
219
219
  }
220
220
 
221
- get recentDynamicOptions(): readonly DebugSessionOptions[] {
221
+ get recentDynamicOptions(): readonly DynamicDebugConfigurationSessionOptions[] {
222
222
  return this.recentDynamicOptionsTracker;
223
223
  }
224
224
 
225
225
  protected updateCurrent(options: DebugSessionOptions | undefined = this._currentOptions): void {
226
- this._currentOptions = options && this.find(options.configuration, options.workspaceFolderUri, options.providerType);
226
+ if (DebugSessionOptions.isCompound(options)) {
227
+ this._currentOptions = options && this.find(options.compound, options.workspaceFolderUri);
228
+ } else {
229
+ this._currentOptions = options && this.find(options.configuration, options.workspaceFolderUri, options.providerType);
230
+ }
227
231
 
228
232
  if (!this._currentOptions) {
229
233
  const model = this.getModel();
230
234
  if (model) {
231
235
  const configuration = model.configurations[0];
232
236
  if (configuration) {
233
- this._currentOptions = {
234
- configuration,
235
- workspaceFolderUri: model.workspaceFolderUri
236
- };
237
+ this._currentOptions = this.configurationToOptions(configuration, model.workspaceFolderUri);
237
238
  }
238
239
  }
239
240
  }
240
- this.debugConfigurationTypeKey.set(this.current && this.current.configuration.type);
241
+ this.debugConfigurationTypeKey.set(this.current && this.current.configuration?.type);
241
242
  this.onDidChangeEmitter.fire(undefined);
242
243
  }
243
244
 
@@ -248,24 +249,57 @@ export class DebugConfigurationManager {
248
249
  /**
249
250
  * Find / Resolve DebugSessionOptions from a given target debug configuration
250
251
  */
251
- find(targetConfiguration: DebugConfiguration, workspaceFolderUri?: string, providerType?: string): DebugSessionOptions | undefined;
252
- find(nameOrTargetConfiguration: string | DebugConfiguration, workspaceFolderUri?: string, providerType?: string): DebugSessionOptions | undefined {
253
- // providerType is only applicable to dynamic debug configurations
254
- if (typeof nameOrTargetConfiguration === 'object' && providerType) {
255
- return {
256
- configuration: nameOrTargetConfiguration,
257
- providerType
258
- };
259
- }
260
- const name = typeof nameOrTargetConfiguration === 'string' ? nameOrTargetConfiguration : nameOrTargetConfiguration.name;
252
+ find(compound: DebugCompound, workspaceFolderUri?: string): DebugSessionOptions | undefined;
253
+ find(configuration: DebugConfiguration, workspaceFolderUri?: string, providerType?: string): DebugSessionOptions | undefined;
254
+ find(name: string, workspaceFolderUri?: string, providerType?: string): DebugSessionOptions | undefined;
255
+ find(nameOrConfigurationOrCompound: string | DebugConfiguration | DebugCompound, workspaceFolderUri?: string, providerType?: string): DebugSessionOptions | undefined {
256
+ if (DebugConfiguration.is(nameOrConfigurationOrCompound) && providerType) {
257
+ // providerType is only applicable to dynamic debug configurations and may only be created if we have a configuration given
258
+ return this.configurationToOptions(nameOrConfigurationOrCompound, workspaceFolderUri, providerType);
259
+ }
260
+ const name = typeof nameOrConfigurationOrCompound === 'string' ? nameOrConfigurationOrCompound : nameOrConfigurationOrCompound.name;
261
+ const configuration = this.findConfiguration(name, workspaceFolderUri);
262
+ if (configuration) {
263
+ return this.configurationToOptions(configuration, workspaceFolderUri);
264
+ }
265
+ const compound = this.findCompound(name, workspaceFolderUri);
266
+ if (compound) {
267
+ return this.compoundToOptions(compound, workspaceFolderUri);
268
+ }
269
+ }
270
+
271
+ findConfigurations(name: string, workspaceFolderUri?: string): DebugConfiguration[] {
272
+ const matches = [];
261
273
  for (const model of this.models.values()) {
262
274
  if (model.workspaceFolderUri === workspaceFolderUri) {
263
275
  for (const configuration of model.configurations) {
264
276
  if (configuration.name === name) {
265
- return {
266
- configuration,
267
- workspaceFolderUri
268
- };
277
+ matches.push(configuration);
278
+ }
279
+ }
280
+ }
281
+ }
282
+ return matches;
283
+ }
284
+
285
+ findConfiguration(name: string, workspaceFolderUri?: string): DebugConfiguration | undefined {
286
+ for (const model of this.models.values()) {
287
+ if (model.workspaceFolderUri === workspaceFolderUri) {
288
+ for (const configuration of model.configurations) {
289
+ if (configuration.name === name) {
290
+ return configuration;
291
+ }
292
+ }
293
+ }
294
+ }
295
+ }
296
+
297
+ findCompound(name: string, workspaceFolderUri?: string): DebugCompound | undefined {
298
+ for (const model of this.models.values()) {
299
+ if (model.workspaceFolderUri === workspaceFolderUri) {
300
+ for (const compound of model.compounds) {
301
+ if (compound.name === name) {
302
+ return compound;
269
303
  }
270
304
  }
271
305
  }
@@ -279,6 +313,14 @@ export class DebugConfigurationManager {
279
313
  }
280
314
  }
281
315
 
316
+ protected configurationToOptions(configuration: DebugConfiguration, workspaceFolderUri?: string, providerType?: string): DebugSessionOptions {
317
+ return { name: configuration.name, configuration, providerType, workspaceFolderUri };
318
+ }
319
+
320
+ protected compoundToOptions(compound: DebugCompound, workspaceFolderUri?: string): DebugSessionOptions {
321
+ return { name: compound.name, compound, workspaceFolderUri };
322
+ }
323
+
282
324
  async addConfiguration(): Promise<void> {
283
325
  let rootUri: URI | undefined = undefined;
284
326
  if (this.workspaceService.saved && this.workspaceService.tryGetRoots().length > 1) {
@@ -345,7 +387,8 @@ export class DebugConfigurationManager {
345
387
  });
346
388
  }
347
389
  const root = await this.quickPickService.show(items, {
348
- placeholder: nls.localize('theia/debug/addConfigurationPlaceholder', 'Select workspace root to add configuration to')
390
+ placeholder: nls.localize('theia/debug/addConfigurationPlaceholder', 'Select workspace root to add configuration to'),
391
+ ignoreFocusOut: true
349
392
  });
350
393
  return root?.value;
351
394
  }
@@ -469,17 +512,26 @@ export class DebugConfigurationManager {
469
512
 
470
513
  // Between versions v1.26 and v1.27, the expected format of the data changed so that old stored data
471
514
  // may not contain the configuration key.
472
- if (data.current && 'configuration' in data.current) {
515
+ if (DebugSessionOptions.isConfiguration(data.current)) {
516
+ // ensure options name is reflected from old configurations data
517
+ data.current.name = data.current.name ?? data.current.configuration?.name;
473
518
  this.current = this.find(data.current.configuration, data.current.workspaceFolderUri, data.current.providerType);
519
+ } else if (DebugSessionOptions.isCompound(data.current)) {
520
+ this.current = this.find(data.current.name, data.current.workspaceFolderUri);
474
521
  }
475
522
  }
476
523
 
477
- protected resolveRecentDynamicOptionsFromData(options?: DebugSessionOptions[]): void {
524
+ protected resolveRecentDynamicOptionsFromData(options?: DynamicDebugConfigurationSessionOptions[]): void {
478
525
  if (!options || this.recentDynamicOptionsTracker.length !== 0) {
479
526
  return;
480
527
  }
481
528
 
482
- this.recentDynamicOptionsTracker = options;
529
+ // ensure options name is reflected from old configurations data
530
+ const dynamicOptions = options.map(option => {
531
+ option.name = option.name ?? option.configuration.name;
532
+ return option;
533
+ }).filter(DebugSessionOptions.isDynamic);
534
+ this.recentDynamicOptionsTracker = dynamicOptions;
483
535
  }
484
536
 
485
537
  save(): void {
@@ -502,6 +554,6 @@ export class DebugConfigurationManager {
502
554
  export namespace DebugConfigurationManager {
503
555
  export interface Data {
504
556
  current?: DebugSessionOptions,
505
- recentDynamicOptions?: DebugSessionOptions[]
557
+ recentDynamicOptions?: DynamicDebugConfigurationSessionOptions[]
506
558
  }
507
559
  }
@@ -19,6 +19,7 @@ import { Emitter, Event } from '@theia/core/lib/common/event';
19
19
  import { Disposable, DisposableCollection } from '@theia/core/lib/common/disposable';
20
20
  import { DebugConfiguration } from '../common/debug-common';
21
21
  import { PreferenceService } from '@theia/core/lib/browser/preferences/preference-service';
22
+ import { DebugCompound } from '../common/debug-compound';
22
23
 
23
24
  export class DebugConfigurationModel implements Disposable {
24
25
 
@@ -58,6 +59,10 @@ export class DebugConfigurationModel implements Disposable {
58
59
  return this.json.configurations;
59
60
  }
60
61
 
62
+ get compounds(): DebugCompound[] {
63
+ return this.json.compounds;
64
+ }
65
+
61
66
  async reconcile(): Promise<void> {
62
67
  this.json = this.parseConfigurations();
63
68
  this.onDidChangeEmitter.fire(undefined);
@@ -66,19 +71,22 @@ export class DebugConfigurationModel implements Disposable {
66
71
  const configurations: DebugConfiguration[] = [];
67
72
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
68
73
  const { configUri, value } = this.preferences.resolve<any>('launch', undefined, this.workspaceFolderUri);
69
- if (value && typeof value === 'object' && 'configurations' in value) {
70
- if (Array.isArray(value.configurations)) {
71
- for (const configuration of value.configurations) {
72
- if (DebugConfiguration.is(configuration)) {
73
- configurations.push(configuration);
74
- }
74
+ if (value && typeof value === 'object' && Array.isArray(value.configurations)) {
75
+ for (const configuration of value.configurations) {
76
+ if (DebugConfiguration.is(configuration)) {
77
+ configurations.push(configuration);
78
+ }
79
+ }
80
+ }
81
+ const compounds: DebugCompound[] = [];
82
+ if (value && typeof value === 'object' && Array.isArray(value.compounds)) {
83
+ for (const compound of value.compounds) {
84
+ if (DebugCompound.is(compound)) {
85
+ compounds.push(compound);
75
86
  }
76
87
  }
77
88
  }
78
- return {
79
- uri: configUri,
80
- configurations
81
- };
89
+ return { uri: configUri, configurations, compounds };
82
90
  }
83
91
 
84
92
  }
@@ -86,5 +94,6 @@ export namespace DebugConfigurationModel {
86
94
  export interface JsonContent {
87
95
  uri?: URI
88
96
  configurations: DebugConfiguration[]
97
+ compounds: DebugCompound[]
89
98
  }
90
99
  }
@@ -14,7 +14,7 @@
14
14
  // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15
15
  // *****************************************************************************
16
16
 
17
- import { DebugProtocol } from 'vscode-debugprotocol';
17
+ import { DebugProtocol } from '@vscode/debugprotocol';
18
18
  import { DebugSessionConnection } from './debug-session-connection';
19
19
 
20
20
  export const DebugContribution = Symbol('DebugContribution');
@@ -1122,8 +1122,9 @@ export class DebugFrontendApplicationContribution extends AbstractViewContributi
1122
1122
  await this.configurations.addConfiguration();
1123
1123
  return;
1124
1124
  }
1125
- if (current) {
1126
- if (noDebug !== undefined) {
1125
+
1126
+ if (noDebug !== undefined) {
1127
+ if (current.configuration) {
1127
1128
  current = {
1128
1129
  ...current,
1129
1130
  configuration: {
@@ -1131,9 +1132,15 @@ export class DebugFrontendApplicationContribution extends AbstractViewContributi
1131
1132
  noDebug
1132
1133
  }
1133
1134
  };
1135
+ } else {
1136
+ current = {
1137
+ ...current,
1138
+ noDebug
1139
+ };
1134
1140
  }
1135
- await this.manager.start(current);
1136
1141
  }
1142
+
1143
+ await this.manager.start(current);
1137
1144
  }
1138
1145
 
1139
1146
  get threads(): DebugThreadsWidget | undefined {
@@ -113,7 +113,7 @@ export class DebugPrefixConfiguration implements CommandContribution, CommandHan
113
113
 
114
114
  for (const config of configurations) {
115
115
  items.push({
116
- label: config.configuration.name,
116
+ label: config.name,
117
117
  description: this.workspaceService.isMultiRootWorkspaceOpened
118
118
  ? this.labelProvider.getName(new URI(config.workspaceFolderUri))
119
119
  : '',
@@ -134,7 +134,7 @@ export class DebugPrefixConfiguration implements CommandContribution, CommandHan
134
134
  for (const configuration of dynamicConfigurations) {
135
135
  items.push({
136
136
  label: configuration.name,
137
- execute: () => this.runConfiguration({ configuration, providerType })
137
+ execute: () => this.runConfiguration({ name: configuration.name, configuration, providerType })
138
138
  });
139
139
  }
140
140
  }
@@ -170,7 +170,7 @@ export class DebugPrefixConfiguration implements CommandContribution, CommandHan
170
170
  */
171
171
  protected updateStatusBar(): void {
172
172
  const text: string = this.debugConfigurationManager.current
173
- ? this.debugConfigurationManager.current.configuration.name
173
+ ? this.debugConfigurationManager.current.name
174
174
  : '';
175
175
  const icon = '$(codicon-debug-alt-small)';
176
176
  this.statusBar.setElement(this.statusBarId, {
@@ -16,13 +16,14 @@
16
16
 
17
17
  import { injectable, inject, postConstruct } from '@theia/core/shared/inversify';
18
18
  import { JsonSchemaRegisterContext, JsonSchemaContribution } from '@theia/core/lib/browser/json-schema-store';
19
- import { InMemoryResources, deepClone } from '@theia/core/lib/common';
19
+ import { InMemoryResources, deepClone, nls } from '@theia/core/lib/common';
20
20
  import { IJSONSchema } from '@theia/core/lib/common/json-schema';
21
21
  import URI from '@theia/core/lib/common/uri';
22
22
  import { DebugService } from '../common/debug-service';
23
23
  import { debugPreferencesSchema } from './debug-preferences';
24
24
  import { inputsSchema } from '@theia/variable-resolver/lib/browser/variable-input-schema';
25
25
  import { WorkspaceService } from '@theia/workspace/lib/browser';
26
+ import { defaultCompound } from '../common/debug-compound';
26
27
 
27
28
  @injectable()
28
29
  export class DebugSchemaUpdater implements JsonSchemaContribution {
@@ -73,24 +74,74 @@ export const launchSchemaId = 'vscode://schemas/launch';
73
74
  const launchSchema: IJSONSchema = {
74
75
  $id: launchSchemaId,
75
76
  type: 'object',
76
- title: 'Launch',
77
+ title: nls.localizeByDefault('Launch'),
77
78
  required: [],
78
- default: { version: '0.2.0', configurations: [] },
79
+ default: { version: '0.2.0', configurations: [], compounds: [] },
79
80
  properties: {
80
81
  version: {
81
82
  type: 'string',
82
- description: 'Version of this file format.',
83
+ description: nls.localizeByDefault('Version of this file format.'),
83
84
  default: '0.2.0'
84
85
  },
85
86
  configurations: {
86
87
  type: 'array',
87
- description: 'List of configurations. Add new configurations or edit existing ones by using IntelliSense.',
88
+ description: nls.localizeByDefault('List of configurations. Add new configurations or edit existing ones by using IntelliSense.'),
88
89
  items: {
89
90
  defaultSnippets: [],
90
91
  'type': 'object',
91
92
  oneOf: []
92
93
  }
93
94
  },
95
+ compounds: {
96
+ type: 'array',
97
+ description: nls.localizeByDefault('List of compounds. Each compound references multiple configurations which will get launched together.'),
98
+ items: {
99
+ type: 'object',
100
+ required: ['name', 'configurations'],
101
+ properties: {
102
+ name: {
103
+ type: 'string',
104
+ description: nls.localizeByDefault('Name of compound. Appears in the launch configuration drop down menu.')
105
+ },
106
+ configurations: {
107
+ type: 'array',
108
+ default: [],
109
+ items: {
110
+ oneOf: [{
111
+ type: 'string',
112
+ description: nls.localizeByDefault('Please use unique configuration names.')
113
+ }, {
114
+ type: 'object',
115
+ required: ['name'],
116
+ properties: {
117
+ name: {
118
+ enum: [],
119
+ description: nls.localizeByDefault('Name of compound. Appears in the launch configuration drop down menu.')
120
+ },
121
+ folder: {
122
+ enum: [],
123
+ description: nls.localizeByDefault('Name of folder in which the compound is located.')
124
+ }
125
+ }
126
+ }]
127
+ },
128
+ description: nls.localizeByDefault('Names of configurations that will be started as part of this compound.')
129
+ },
130
+ stopAll: {
131
+ type: 'boolean',
132
+ default: false,
133
+ description: nls.localizeByDefault('Controls whether manually terminating one session will stop all of the compound sessions.')
134
+ },
135
+ preLaunchTask: {
136
+ type: 'string',
137
+ default: '',
138
+ description: nls.localizeByDefault('Task to run before any of the compound configurations start.')
139
+ }
140
+ },
141
+ default: defaultCompound
142
+ },
143
+ default: [defaultCompound]
144
+ },
94
145
  inputs: inputsSchema.definitions!.inputs
95
146
  },
96
147
  allowComments: true,
@@ -16,7 +16,7 @@
16
16
 
17
17
  /* eslint-disable @typescript-eslint/no-explicit-any */
18
18
 
19
- import { DebugProtocol } from 'vscode-debugprotocol';
19
+ import { DebugProtocol } from '@vscode/debugprotocol';
20
20
  import { Deferred } from '@theia/core/lib/common/promise-util';
21
21
  import { Event, Emitter, DisposableCollection, Disposable, MaybePromise } from '@theia/core';
22
22
  import { OutputChannel } from '@theia/output/lib/browser/output-channel';
@@ -22,7 +22,7 @@ import { TerminalService } from '@theia/terminal/lib/browser/base/terminal-servi
22
22
  import { WebSocketConnectionProvider } from '@theia/core/lib/browser/messaging/ws-connection-provider';
23
23
  import { DebugSession } from './debug-session';
24
24
  import { BreakpointManager } from './breakpoint/breakpoint-manager';
25
- import { DebugSessionOptions } from './debug-session-options';
25
+ import { DebugConfigurationSessionOptions, DebugSessionOptions } from './debug-session-options';
26
26
  import { OutputChannelManager, OutputChannel } from '@theia/output/lib/browser/output-channel';
27
27
  import { DebugPreferences } from './debug-preferences';
28
28
  import { DebugSessionConnection } from './debug-session-connection';
@@ -118,7 +118,7 @@ export class DefaultDebugSessionFactory implements DebugSessionFactory {
118
118
  @inject(WorkspaceService)
119
119
  protected readonly workspaceService: WorkspaceService;
120
120
 
121
- get(sessionId: string, options: DebugSessionOptions, parentSession?: DebugSession): DebugSession {
121
+ get(sessionId: string, options: DebugConfigurationSessionOptions, parentSession?: DebugSession): DebugSession {
122
122
  const connection = new DebugSessionConnection(
123
123
  sessionId,
124
124
  () => new Promise<DebugChannel>(resolve =>
@@ -14,7 +14,7 @@
14
14
  // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15
15
  // *****************************************************************************
16
16
 
17
- import { DisposableCollection, Emitter, Event, MessageService, ProgressService, WaitUntilEvent } from '@theia/core';
17
+ import { DisposableCollection, Emitter, Event, MessageService, nls, ProgressService, WaitUntilEvent } from '@theia/core';
18
18
  import { LabelProvider, ApplicationShell } from '@theia/core/lib/browser';
19
19
  import { ContextKey, ContextKeyService } from '@theia/core/lib/browser/context-key-service';
20
20
  import URI from '@theia/core/lib/common/uri';
@@ -29,7 +29,7 @@ import { BreakpointManager } from './breakpoint/breakpoint-manager';
29
29
  import { DebugConfigurationManager } from './debug-configuration-manager';
30
30
  import { DebugSession, DebugState } from './debug-session';
31
31
  import { DebugSessionContributionRegistry, DebugSessionFactory } from './debug-session-contribution';
32
- import { DebugSessionOptions, InternalDebugSessionOptions } from './debug-session-options';
32
+ import { DebugCompoundRoot, DebugCompoundSessionOptions, DebugConfigurationSessionOptions, DebugSessionOptions, InternalDebugSessionOptions } from './debug-session-options';
33
33
  import { DebugStackFrame } from './model/debug-stack-frame';
34
34
  import { DebugThread } from './model/debug-thread';
35
35
  import { TaskIdentifier } from '@theia/task/lib/common';
@@ -191,7 +191,19 @@ export class DebugSessionManager {
191
191
  }
192
192
  }
193
193
 
194
- async start(options: DebugSessionOptions): Promise<DebugSession | undefined> {
194
+ async start(options: DebugCompoundSessionOptions): Promise<boolean | undefined>;
195
+ async start(options: DebugConfigurationSessionOptions): Promise<DebugSession | undefined>;
196
+ async start(options: DebugSessionOptions): Promise<DebugSession | boolean | undefined>;
197
+ async start(name: string): Promise<DebugSession | boolean | undefined>;
198
+ async start(optionsOrName: DebugSessionOptions | string): Promise<DebugSession | boolean | undefined> {
199
+ if (typeof optionsOrName === 'string') {
200
+ const options = this.debugConfigurationManager.find(optionsOrName);
201
+ return !!options && this.start(options);
202
+ }
203
+ return optionsOrName.configuration ? this.startConfiguration(optionsOrName) : this.startCompound(optionsOrName);
204
+ }
205
+
206
+ protected async startConfiguration(options: DebugConfigurationSessionOptions): Promise<DebugSession | undefined> {
195
207
  return this.progressService.withProgress('Start...', 'debug', async () => {
196
208
  try {
197
209
  if (!await this.saveAll()) {
@@ -200,7 +212,7 @@ export class DebugSessionManager {
200
212
  await this.fireWillStartDebugSession();
201
213
  const resolved = await this.resolveConfiguration(options);
202
214
 
203
- if (!resolved) {
215
+ if (!resolved || !resolved.configuration) {
204
216
  // As per vscode API: https://code.visualstudio.com/api/references/vscode-api#DebugConfigurationProvider
205
217
  // "Returning the value 'undefined' prevents the debug session from starting.
206
218
  // Returning the value 'null' prevents the debug session from starting and opens the
@@ -236,13 +248,70 @@ export class DebugSessionManager {
236
248
  });
237
249
  }
238
250
 
251
+ protected async startCompound(options: DebugCompoundSessionOptions): Promise<boolean | undefined> {
252
+ let configurations: DebugConfigurationSessionOptions[] = [];
253
+ try {
254
+ configurations = this.getCompoundConfigurations(options);
255
+ } catch (error) {
256
+ this.messageService.error(error.message);
257
+ return;
258
+ }
259
+
260
+ if (options.compound.preLaunchTask) {
261
+ const taskRun = await this.runTask(options.workspaceFolderUri, options.compound.preLaunchTask, true);
262
+ if (!taskRun) {
263
+ return undefined;
264
+ }
265
+ }
266
+
267
+ // Compound launch is a success only if each configuration launched successfully
268
+ const values = await Promise.all(configurations.map(configuration => this.startConfiguration(configuration)));
269
+ const result = values.every(success => !!success);
270
+ return result;
271
+ }
272
+
273
+ protected getCompoundConfigurations(options: DebugCompoundSessionOptions): DebugConfigurationSessionOptions[] {
274
+ const compound = options.compound;
275
+ if (!compound.configurations) {
276
+ throw new Error(nls.localizeByDefault('Compound must have "configurations" attribute set in order to start multiple configurations.'));
277
+ }
278
+
279
+ const compoundRoot = compound.stopAll ? new DebugCompoundRoot() : undefined;
280
+ const configurations: DebugConfigurationSessionOptions[] = [];
281
+ for (const configData of compound.configurations) {
282
+ const name = typeof configData === 'string' ? configData : configData.name;
283
+ if (name === compound.name) {
284
+ throw new Error(nls.localize('theia/debug/compound-cycle', "Launch configuration '{0}' contains a cycle with itself", name));
285
+ }
286
+
287
+ const workspaceFolderUri = typeof configData === 'string' ? options.workspaceFolderUri : configData.folder;
288
+ const matchingOptions = [...this.debugConfigurationManager.all]
289
+ .filter(option => option.name === name && !!option.configuration && option.workspaceFolderUri === workspaceFolderUri);
290
+ if (matchingOptions.length === 1) {
291
+ const match = matchingOptions[0];
292
+ if (DebugSessionOptions.isConfiguration(match)) {
293
+ configurations.push({ ...match, compoundRoot, configuration: { ...match.configuration, noDebug: options.noDebug } });
294
+ } else {
295
+ throw new Error(nls.localizeByDefault("Could not find launch configuration '{0}' in the workspace.", name));
296
+ }
297
+ } else {
298
+ throw new Error(matchingOptions.length === 0
299
+ ? workspaceFolderUri
300
+ ? nls.localizeByDefault("Can not find folder with name '{0}' for configuration '{1}' in compound '{2}'.", workspaceFolderUri, name, compound.name)
301
+ : nls.localizeByDefault("Could not find launch configuration '{0}' in the workspace.", name)
302
+ : nls.localizeByDefault("There are multiple launch configurations '{0}' in the workspace. Use folder name to qualify the configuration.", name));
303
+ }
304
+ }
305
+ return configurations;
306
+ }
307
+
239
308
  protected async fireWillStartDebugSession(): Promise<void> {
240
309
  await WaitUntilEvent.fire(this.onWillStartDebugSessionEmitter, {});
241
310
  }
242
311
 
243
312
  protected configurationIds = new Map<string, number>();
244
313
  protected async resolveConfiguration(
245
- options: Readonly<DebugSessionOptions>
314
+ options: Readonly<DebugConfigurationSessionOptions>
246
315
  ): Promise<InternalDebugSessionOptions | undefined | null> {
247
316
  if (InternalDebugSessionOptions.is(options)) {
248
317
  return options;
@@ -275,10 +344,12 @@ export class DebugSessionManager {
275
344
  const key = configuration.name + workspaceFolderUri;
276
345
  const id = this.configurationIds.has(key) ? this.configurationIds.get(key)! + 1 : 0;
277
346
  this.configurationIds.set(key, id);
347
+
278
348
  return {
279
349
  id,
280
- configuration,
281
- workspaceFolderUri
350
+ ...options,
351
+ name: configuration.name,
352
+ configuration
282
353
  };
283
354
  }
284
355
 
@@ -301,7 +372,7 @@ export class DebugSessionManager {
301
372
  return this.debug.resolveDebugConfigurationWithSubstitutedVariables(configuration, workspaceFolderUri);
302
373
  }
303
374
 
304
- protected async doStart(sessionId: string, options: DebugSessionOptions): Promise<DebugSession> {
375
+ protected async doStart(sessionId: string, options: DebugConfigurationSessionOptions): Promise<DebugSession> {
305
376
  const parentSession = options.configuration.parentSession && this._sessions.get(options.configuration.parentSession.id);
306
377
  const contrib = this.sessionContributionRegistry.get(options.configuration.type);
307
378
  const sessionFactory = contrib ? contrib.debugSessionFactory() : this.debugSessionFactory;