Path: blob/main/src/vs/workbench/contrib/debug/common/debugger.ts
3296 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import * as nls from '../../../../nls.js';6import { isObject } from '../../../../base/common/types.js';7import { IJSONSchema, IJSONSchemaMap, IJSONSchemaSnippet } from '../../../../base/common/jsonSchema.js';8import { IWorkspaceFolder } from '../../../../platform/workspace/common/workspace.js';9import { IConfig, IDebuggerContribution, IDebugAdapter, IDebugger, IDebugSession, IAdapterManager, IDebugService, debuggerDisabledMessage, IDebuggerMetadata, DebugConfigurationProviderTriggerKind } from './debug.js';10import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';11import { IConfigurationResolverService } from '../../../services/configurationResolver/common/configurationResolver.js';12import * as ConfigurationResolverUtils from '../../../services/configurationResolver/common/configurationResolverUtils.js';13import { ITextResourcePropertiesService } from '../../../../editor/common/services/textResourceConfiguration.js';14import { URI } from '../../../../base/common/uri.js';15import { Schemas } from '../../../../base/common/network.js';16import { isDebuggerMainContribution } from './debugUtils.js';17import { IExtensionDescription } from '../../../../platform/extensions/common/extensions.js';18import { ITelemetryEndpoint } from '../../../../platform/telemetry/common/telemetry.js';19import { cleanRemoteAuthority } from '../../../../platform/telemetry/common/telemetryUtils.js';20import { IWorkbenchEnvironmentService } from '../../../services/environment/common/environmentService.js';21import { ContextKeyExpr, ContextKeyExpression, IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js';22import { filter } from '../../../../base/common/objects.js';2324export class Debugger implements IDebugger, IDebuggerMetadata {2526private debuggerContribution: IDebuggerContribution;27private mergedExtensionDescriptions: IExtensionDescription[] = [];28private mainExtensionDescription: IExtensionDescription | undefined;2930private debuggerWhen: ContextKeyExpression | undefined;31private debuggerHiddenWhen: ContextKeyExpression | undefined;3233constructor(34private adapterManager: IAdapterManager,35dbgContribution: IDebuggerContribution,36extensionDescription: IExtensionDescription,37@IConfigurationService private readonly configurationService: IConfigurationService,38@ITextResourcePropertiesService private readonly resourcePropertiesService: ITextResourcePropertiesService,39@IConfigurationResolverService private readonly configurationResolverService: IConfigurationResolverService,40@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService,41@IDebugService private readonly debugService: IDebugService,42@IContextKeyService private readonly contextKeyService: IContextKeyService,43) {44this.debuggerContribution = { type: dbgContribution.type };45this.merge(dbgContribution, extensionDescription);4647this.debuggerWhen = typeof this.debuggerContribution.when === 'string' ? ContextKeyExpr.deserialize(this.debuggerContribution.when) : undefined;48this.debuggerHiddenWhen = typeof this.debuggerContribution.hiddenWhen === 'string' ? ContextKeyExpr.deserialize(this.debuggerContribution.hiddenWhen) : undefined;49}5051merge(otherDebuggerContribution: IDebuggerContribution, extensionDescription: IExtensionDescription): void {5253/**54* Copies all properties of source into destination. The optional parameter "overwrite" allows to control55* if existing non-structured properties on the destination should be overwritten or not. Defaults to true (overwrite).56*/57function mixin(destination: any, source: any, overwrite: boolean, level = 0): any {5859if (!isObject(destination)) {60return source;61}6263if (isObject(source)) {64Object.keys(source).forEach(key => {65if (key !== '__proto__') {66if (isObject(destination[key]) && isObject(source[key])) {67mixin(destination[key], source[key], overwrite, level + 1);68} else {69if (key in destination) {70if (overwrite) {71if (level === 0 && key === 'type') {72// don't merge the 'type' property73} else {74destination[key] = source[key];75}76}77} else {78destination[key] = source[key];79}80}81}82});83}8485return destination;86}8788// only if not already merged89if (this.mergedExtensionDescriptions.indexOf(extensionDescription) < 0) {9091// remember all extensions that have been merged for this debugger92this.mergedExtensionDescriptions.push(extensionDescription);9394// merge new debugger contribution into existing contributions (and don't overwrite values in built-in extensions)95mixin(this.debuggerContribution, otherDebuggerContribution, extensionDescription.isBuiltin);9697// remember the extension that is considered the "main" debugger contribution98if (isDebuggerMainContribution(otherDebuggerContribution)) {99this.mainExtensionDescription = extensionDescription;100}101}102}103104async startDebugging(configuration: IConfig, parentSessionId: string): Promise<boolean> {105const parentSession = this.debugService.getModel().getSession(parentSessionId);106return await this.debugService.startDebugging(undefined, configuration, { parentSession }, undefined);107}108109async createDebugAdapter(session: IDebugSession): Promise<IDebugAdapter> {110await this.adapterManager.activateDebuggers('onDebugAdapterProtocolTracker', this.type);111const da = this.adapterManager.createDebugAdapter(session);112if (da) {113return Promise.resolve(da);114}115throw new Error(nls.localize('cannot.find.da', "Cannot find debug adapter for type '{0}'.", this.type));116}117118async substituteVariables(folder: IWorkspaceFolder | undefined, config: IConfig): Promise<IConfig> {119const substitutedConfig = await this.adapterManager.substituteVariables(this.type, folder, config);120return await this.configurationResolverService.resolveWithInteractionReplace(folder, substitutedConfig, 'launch', this.variables, substitutedConfig.__configurationTarget);121}122123runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, sessionId: string): Promise<number | undefined> {124return this.adapterManager.runInTerminal(this.type, args, sessionId);125}126127get label(): string {128return this.debuggerContribution.label || this.debuggerContribution.type;129}130131get type(): string {132return this.debuggerContribution.type;133}134135get variables(): { [key: string]: string } | undefined {136return this.debuggerContribution.variables;137}138139get configurationSnippets(): IJSONSchemaSnippet[] | undefined {140return this.debuggerContribution.configurationSnippets;141}142143get languages(): string[] | undefined {144return this.debuggerContribution.languages;145}146147get when(): ContextKeyExpression | undefined {148return this.debuggerWhen;149}150151get hiddenWhen(): ContextKeyExpression | undefined {152return this.debuggerHiddenWhen;153}154155get enabled() {156return !this.debuggerWhen || this.contextKeyService.contextMatchesRules(this.debuggerWhen);157}158159get isHiddenFromDropdown() {160if (!this.debuggerHiddenWhen) {161return false;162}163return this.contextKeyService.contextMatchesRules(this.debuggerHiddenWhen);164}165166get strings() {167return this.debuggerContribution.strings ?? (this.debuggerContribution as any).uiMessages;168}169170interestedInLanguage(languageId: string): boolean {171return !!(this.languages && this.languages.indexOf(languageId) >= 0);172}173174hasInitialConfiguration(): boolean {175return !!this.debuggerContribution.initialConfigurations;176}177178hasDynamicConfigurationProviders(): boolean {179return this.debugService.getConfigurationManager().hasDebugConfigurationProvider(this.type, DebugConfigurationProviderTriggerKind.Dynamic);180}181182hasConfigurationProvider(): boolean {183return this.debugService.getConfigurationManager().hasDebugConfigurationProvider(this.type);184}185186getInitialConfigurationContent(initialConfigs?: IConfig[]): Promise<string> {187// at this point we got some configs from the package.json and/or from registered DebugConfigurationProviders188let initialConfigurations = this.debuggerContribution.initialConfigurations || [];189if (initialConfigs) {190initialConfigurations = initialConfigurations.concat(initialConfigs);191}192193const eol = this.resourcePropertiesService.getEOL(URI.from({ scheme: Schemas.untitled, path: '1' })) === '\r\n' ? '\r\n' : '\n';194const configs = JSON.stringify(initialConfigurations, null, '\t').split('\n').map(line => '\t' + line).join(eol).trim();195const comment1 = nls.localize('launch.config.comment1', "Use IntelliSense to learn about possible attributes.");196const comment2 = nls.localize('launch.config.comment2', "Hover to view descriptions of existing attributes.");197const comment3 = nls.localize('launch.config.comment3', "For more information, visit: {0}", 'https://go.microsoft.com/fwlink/?linkid=830387');198199let content = [200'{',201`\t// ${comment1}`,202`\t// ${comment2}`,203`\t// ${comment3}`,204`\t"version": "0.2.0",`,205`\t"configurations": ${configs}`,206'}'207].join(eol);208209// fix formatting210const editorConfig = this.configurationService.getValue<any>();211if (editorConfig.editor && editorConfig.editor.insertSpaces) {212content = content.replace(new RegExp('\t', 'g'), ' '.repeat(editorConfig.editor.tabSize));213}214215return Promise.resolve(content);216}217218getMainExtensionDescriptor(): IExtensionDescription {219return this.mainExtensionDescription || this.mergedExtensionDescriptions[0];220}221222getCustomTelemetryEndpoint(): ITelemetryEndpoint | undefined {223const aiKey = this.debuggerContribution.aiKey;224if (!aiKey) {225return undefined;226}227228const sendErrorTelemtry = cleanRemoteAuthority(this.environmentService.remoteAuthority) !== 'other';229return {230id: `${this.getMainExtensionDescriptor().publisher}.${this.type}`,231aiKey,232sendErrorTelemetry: sendErrorTelemtry233};234}235236getSchemaAttributes(definitions: IJSONSchemaMap): IJSONSchema[] | null {237238if (!this.debuggerContribution.configurationAttributes) {239return null;240}241242// fill in the default configuration attributes shared by all adapters.243return Object.keys(this.debuggerContribution.configurationAttributes).map(request => {244const definitionId = `${this.type}:${request}`;245const platformSpecificDefinitionId = `${this.type}:${request}:platform`;246const attributes: IJSONSchema = this.debuggerContribution.configurationAttributes[request];247const defaultRequired = ['name', 'type', 'request'];248attributes.required = attributes.required && attributes.required.length ? defaultRequired.concat(attributes.required) : defaultRequired;249attributes.additionalProperties = false;250attributes.type = 'object';251if (!attributes.properties) {252attributes.properties = {};253}254const properties = attributes.properties;255properties['type'] = {256enum: [this.type],257enumDescriptions: [this.label],258description: nls.localize('debugType', "Type of configuration."),259pattern: '^(?!node2)',260deprecationMessage: this.debuggerContribution.deprecated || (this.enabled ? undefined : debuggerDisabledMessage(this.type)),261doNotSuggest: !!this.debuggerContribution.deprecated,262errorMessage: nls.localize('debugTypeNotRecognised', "The debug type is not recognized. Make sure that you have a corresponding debug extension installed and that it is enabled."),263patternErrorMessage: nls.localize('node2NotSupported', "\"node2\" is no longer supported, use \"node\" instead and set the \"protocol\" attribute to \"inspector\".")264};265properties['request'] = {266enum: [request],267description: nls.localize('debugRequest', "Request type of configuration. Can be \"launch\" or \"attach\"."),268};269for (const prop in definitions['common'].properties) {270properties[prop] = {271$ref: `#/definitions/common/properties/${prop}`272};273}274Object.keys(properties).forEach(name => {275// Use schema allOf property to get independent error reporting #21113276ConfigurationResolverUtils.applyDeprecatedVariableMessage(properties[name]);277});278279definitions[definitionId] = { ...attributes };280definitions[platformSpecificDefinitionId] = {281type: 'object',282additionalProperties: false,283properties: filter(properties, key => key !== 'type' && key !== 'request' && key !== 'name')284};285286// Don't add the OS props to the real attributes object so they don't show up in 'definitions'287const attributesCopy = { ...attributes };288attributesCopy.properties = {289...properties,290...{291windows: {292$ref: `#/definitions/${platformSpecificDefinitionId}`,293description: nls.localize('debugWindowsConfiguration', "Windows specific launch configuration attributes."),294},295osx: {296$ref: `#/definitions/${platformSpecificDefinitionId}`,297description: nls.localize('debugOSXConfiguration', "OS X specific launch configuration attributes."),298},299linux: {300$ref: `#/definitions/${platformSpecificDefinitionId}`,301description: nls.localize('debugLinuxConfiguration', "Linux specific launch configuration attributes."),302}303}304};305306return attributesCopy;307});308}309}310311312