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