Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/contrib/debug/common/debugger.ts
5229 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import * as nls from '../../../../nls.js';
7
import { isObject } from '../../../../base/common/types.js';
8
import { IJSONSchema, IJSONSchemaMap, IJSONSchemaSnippet } from '../../../../base/common/jsonSchema.js';
9
import { IWorkspaceFolder } from '../../../../platform/workspace/common/workspace.js';
10
import { IConfig, IDebuggerContribution, IDebugAdapter, IDebugger, IDebugSession, IAdapterManager, IDebugService, debuggerDisabledMessage, IDebuggerMetadata, DebugConfigurationProviderTriggerKind } from './debug.js';
11
import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';
12
import { IConfigurationResolverService } from '../../../services/configurationResolver/common/configurationResolver.js';
13
import * as ConfigurationResolverUtils from '../../../services/configurationResolver/common/configurationResolverUtils.js';
14
import { ITextResourcePropertiesService } from '../../../../editor/common/services/textResourceConfiguration.js';
15
import { URI } from '../../../../base/common/uri.js';
16
import { Schemas } from '../../../../base/common/network.js';
17
import { isDebuggerMainContribution } from './debugUtils.js';
18
import { IExtensionDescription } from '../../../../platform/extensions/common/extensions.js';
19
import { ITelemetryEndpoint } from '../../../../platform/telemetry/common/telemetry.js';
20
import { cleanRemoteAuthority } from '../../../../platform/telemetry/common/telemetryUtils.js';
21
import { IWorkbenchEnvironmentService } from '../../../services/environment/common/environmentService.js';
22
import { ContextKeyExpr, ContextKeyExpression, IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js';
23
import { filter } from '../../../../base/common/objects.js';
24
import { IProductService } from '../../../../platform/product/common/productService.js';
25
26
export class Debugger implements IDebugger, IDebuggerMetadata {
27
28
private debuggerContribution: IDebuggerContribution;
29
private mergedExtensionDescriptions: IExtensionDescription[] = [];
30
private mainExtensionDescription: IExtensionDescription | undefined;
31
32
private debuggerWhen: ContextKeyExpression | undefined;
33
private debuggerHiddenWhen: ContextKeyExpression | undefined;
34
35
constructor(
36
private adapterManager: IAdapterManager,
37
dbgContribution: IDebuggerContribution,
38
extensionDescription: IExtensionDescription,
39
@IConfigurationService private readonly configurationService: IConfigurationService,
40
@ITextResourcePropertiesService private readonly resourcePropertiesService: ITextResourcePropertiesService,
41
@IConfigurationResolverService private readonly configurationResolverService: IConfigurationResolverService,
42
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService,
43
@IDebugService private readonly debugService: IDebugService,
44
@IContextKeyService private readonly contextKeyService: IContextKeyService,
45
@IProductService private readonly productService: IProductService,
46
) {
47
this.debuggerContribution = { type: dbgContribution.type };
48
this.merge(dbgContribution, extensionDescription);
49
50
this.debuggerWhen = typeof this.debuggerContribution.when === 'string' ? ContextKeyExpr.deserialize(this.debuggerContribution.when) : undefined;
51
this.debuggerHiddenWhen = typeof this.debuggerContribution.hiddenWhen === 'string' ? ContextKeyExpr.deserialize(this.debuggerContribution.hiddenWhen) : undefined;
52
}
53
54
merge(otherDebuggerContribution: IDebuggerContribution, extensionDescription: IExtensionDescription): void {
55
56
/**
57
* Copies all properties of source into destination. The optional parameter "overwrite" allows to control
58
* if existing non-structured properties on the destination should be overwritten or not. Defaults to true (overwrite).
59
*/
60
function mixin(destination: any, source: any, overwrite: boolean, level = 0): any {
61
62
if (!isObject(destination)) {
63
return source;
64
}
65
66
if (isObject(source)) {
67
Object.keys(source).forEach(key => {
68
if (key !== '__proto__') {
69
if (isObject(destination[key]) && isObject(source[key])) {
70
mixin(destination[key], source[key], overwrite, level + 1);
71
} else {
72
if (key in destination) {
73
if (overwrite) {
74
if (level === 0 && key === 'type') {
75
// don't merge the 'type' property
76
} else {
77
destination[key] = source[key];
78
}
79
}
80
} else {
81
destination[key] = source[key];
82
}
83
}
84
}
85
});
86
}
87
88
return destination;
89
}
90
91
// only if not already merged
92
if (this.mergedExtensionDescriptions.indexOf(extensionDescription) < 0) {
93
94
// remember all extensions that have been merged for this debugger
95
this.mergedExtensionDescriptions.push(extensionDescription);
96
97
// merge new debugger contribution into existing contributions (and don't overwrite values in built-in extensions)
98
mixin(this.debuggerContribution, otherDebuggerContribution, extensionDescription.isBuiltin);
99
100
// remember the extension that is considered the "main" debugger contribution
101
if (isDebuggerMainContribution(otherDebuggerContribution)) {
102
this.mainExtensionDescription = extensionDescription;
103
}
104
}
105
}
106
107
async startDebugging(configuration: IConfig, parentSessionId: string): Promise<boolean> {
108
const parentSession = this.debugService.getModel().getSession(parentSessionId);
109
return await this.debugService.startDebugging(undefined, configuration, { parentSession }, undefined);
110
}
111
112
async createDebugAdapter(session: IDebugSession): Promise<IDebugAdapter> {
113
await this.adapterManager.activateDebuggers('onDebugAdapterProtocolTracker', this.type);
114
const da = this.adapterManager.createDebugAdapter(session);
115
if (da) {
116
return Promise.resolve(da);
117
}
118
throw new Error(nls.localize('cannot.find.da', "Cannot find debug adapter for type '{0}'.", this.type));
119
}
120
121
async substituteVariables(folder: IWorkspaceFolder | undefined, config: IConfig): Promise<IConfig> {
122
const substitutedConfig = await this.adapterManager.substituteVariables(this.type, folder, config);
123
return await this.configurationResolverService.resolveWithInteractionReplace(folder, substitutedConfig, 'launch', this.variables, substitutedConfig.__configurationTarget);
124
}
125
126
runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, sessionId: string): Promise<number | undefined> {
127
return this.adapterManager.runInTerminal(this.type, args, sessionId);
128
}
129
130
get label(): string {
131
return this.debuggerContribution.label || this.debuggerContribution.type;
132
}
133
134
get type(): string {
135
return this.debuggerContribution.type;
136
}
137
138
get variables(): { [key: string]: string } | undefined {
139
return this.debuggerContribution.variables;
140
}
141
142
get configurationSnippets(): IJSONSchemaSnippet[] | undefined {
143
return this.debuggerContribution.configurationSnippets;
144
}
145
146
get languages(): string[] | undefined {
147
return this.debuggerContribution.languages;
148
}
149
150
get when(): ContextKeyExpression | undefined {
151
return this.debuggerWhen;
152
}
153
154
get hiddenWhen(): ContextKeyExpression | undefined {
155
return this.debuggerHiddenWhen;
156
}
157
158
get enabled() {
159
return !this.debuggerWhen || this.contextKeyService.contextMatchesRules(this.debuggerWhen);
160
}
161
162
get isHiddenFromDropdown() {
163
if (!this.debuggerHiddenWhen) {
164
return false;
165
}
166
return this.contextKeyService.contextMatchesRules(this.debuggerHiddenWhen);
167
}
168
169
get strings() {
170
return this.debuggerContribution.strings ?? this.debuggerContribution.uiMessages;
171
}
172
173
interestedInLanguage(languageId: string): boolean {
174
return !!(this.languages && this.languages.indexOf(languageId) >= 0);
175
}
176
177
hasInitialConfiguration(): boolean {
178
return !!this.debuggerContribution.initialConfigurations;
179
}
180
181
hasDynamicConfigurationProviders(): boolean {
182
return this.debugService.getConfigurationManager().hasDebugConfigurationProvider(this.type, DebugConfigurationProviderTriggerKind.Dynamic);
183
}
184
185
hasConfigurationProvider(): boolean {
186
return this.debugService.getConfigurationManager().hasDebugConfigurationProvider(this.type);
187
}
188
189
getInitialConfigurationContent(initialConfigs?: IConfig[]): Promise<string> {
190
// at this point we got some configs from the package.json and/or from registered DebugConfigurationProviders
191
let initialConfigurations = this.debuggerContribution.initialConfigurations || [];
192
if (initialConfigs) {
193
initialConfigurations = initialConfigurations.concat(initialConfigs);
194
}
195
196
const eol = this.resourcePropertiesService.getEOL(URI.from({ scheme: Schemas.untitled, path: '1' })) === '\r\n' ? '\r\n' : '\n';
197
const configs = JSON.stringify(initialConfigurations, null, '\t').split('\n').map(line => '\t' + line).join(eol).trim();
198
const comment1 = nls.localize('launch.config.comment1', "Use IntelliSense to learn about possible attributes.");
199
const comment2 = nls.localize('launch.config.comment2', "Hover to view descriptions of existing attributes.");
200
const comment3 = nls.localize('launch.config.comment3', "For more information, visit: {0}", 'https://go.microsoft.com/fwlink/?linkid=830387');
201
202
let content = [
203
'{',
204
`\t// ${comment1}`,
205
`\t// ${comment2}`,
206
`\t// ${comment3}`,
207
`\t"version": "0.2.0",`,
208
`\t"configurations": ${configs}`,
209
'}'
210
].join(eol);
211
212
// fix formatting
213
const editorConfig = this.configurationService.getValue<any>();
214
if (editorConfig.editor && editorConfig.editor.insertSpaces) {
215
content = content.replace(new RegExp('\t', 'g'), ' '.repeat(editorConfig.editor.tabSize));
216
}
217
218
return Promise.resolve(content);
219
}
220
221
getMainExtensionDescriptor(): IExtensionDescription {
222
return this.mainExtensionDescription || this.mergedExtensionDescriptions[0];
223
}
224
225
getCustomTelemetryEndpoint(): ITelemetryEndpoint | undefined {
226
const aiKey = this.debuggerContribution.aiKey;
227
if (!aiKey) {
228
return undefined;
229
}
230
231
const sendErrorTelemtry = cleanRemoteAuthority(this.environmentService.remoteAuthority, this.productService) !== 'other';
232
return {
233
id: `${this.getMainExtensionDescriptor().publisher}.${this.type}`,
234
aiKey,
235
sendErrorTelemetry: sendErrorTelemtry
236
};
237
}
238
239
getSchemaAttributes(definitions: IJSONSchemaMap): IJSONSchema[] | null {
240
241
if (!this.debuggerContribution.configurationAttributes) {
242
return null;
243
}
244
245
// fill in the default configuration attributes shared by all adapters.
246
return Object.entries(this.debuggerContribution.configurationAttributes).map(([request, attributes]) => {
247
const definitionId = `${this.type}:${request}`;
248
const platformSpecificDefinitionId = `${this.type}:${request}:platform`;
249
const defaultRequired = ['name', 'type', 'request'];
250
attributes.required = attributes.required && attributes.required.length ? defaultRequired.concat(attributes.required) : defaultRequired;
251
attributes.additionalProperties = false;
252
attributes.type = 'object';
253
if (!attributes.properties) {
254
attributes.properties = {};
255
}
256
const properties = attributes.properties;
257
properties['type'] = {
258
enum: [this.type],
259
enumDescriptions: [this.label],
260
description: nls.localize('debugType', "Type of configuration."),
261
pattern: '^(?!node2)',
262
deprecationMessage: this.debuggerContribution.deprecated || (this.enabled ? undefined : debuggerDisabledMessage(this.type)),
263
doNotSuggest: !!this.debuggerContribution.deprecated,
264
errorMessage: nls.localize('debugTypeNotRecognised', "The debug type is not recognized. Make sure that you have a corresponding debug extension installed and that it is enabled."),
265
patternErrorMessage: nls.localize('node2NotSupported', "\"node2\" is no longer supported, use \"node\" instead and set the \"protocol\" attribute to \"inspector\".")
266
};
267
properties['request'] = {
268
enum: [request],
269
description: nls.localize('debugRequest', "Request type of configuration. Can be \"launch\" or \"attach\"."),
270
};
271
for (const prop in definitions['common'].properties) {
272
properties[prop] = {
273
$ref: `#/definitions/common/properties/${prop}`
274
};
275
}
276
Object.keys(properties).forEach(name => {
277
// Use schema allOf property to get independent error reporting #21113
278
ConfigurationResolverUtils.applyDeprecatedVariableMessage(properties[name]);
279
});
280
281
definitions[definitionId] = { ...attributes };
282
definitions[platformSpecificDefinitionId] = {
283
type: 'object',
284
additionalProperties: false,
285
properties: filter(properties, key => key !== 'type' && key !== 'request' && key !== 'name')
286
};
287
288
// Don't add the OS props to the real attributes object so they don't show up in 'definitions'
289
const attributesCopy = { ...attributes };
290
attributesCopy.properties = {
291
...properties,
292
...{
293
windows: {
294
$ref: `#/definitions/${platformSpecificDefinitionId}`,
295
description: nls.localize('debugWindowsConfiguration', "Windows specific launch configuration attributes."),
296
},
297
osx: {
298
$ref: `#/definitions/${platformSpecificDefinitionId}`,
299
description: nls.localize('debugOSXConfiguration', "OS X specific launch configuration attributes."),
300
},
301
linux: {
302
$ref: `#/definitions/${platformSpecificDefinitionId}`,
303
description: nls.localize('debugLinuxConfiguration', "Linux specific launch configuration attributes."),
304
}
305
}
306
};
307
308
return attributesCopy;
309
});
310
}
311
}
312
313