Skip to content

Commit

Permalink
Merge pull request #170652 from microsoft/connor4312/pr-143054
Browse files Browse the repository at this point in the history
DebugConfigurationProviders for the result type of another DebugConfigurationProvider
  • Loading branch information
connor4312 authored Jan 6, 2023
2 parents 727d2d3 + 1bcbb11 commit add43fa
Show file tree
Hide file tree
Showing 3 changed files with 152 additions and 22 deletions.
46 changes: 24 additions & 22 deletions src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cance
import { Emitter, Event } from 'vs/base/common/event';
import * as json from 'vs/base/common/json';
import { IJSONSchema } from 'vs/base/common/jsonSchema';
import { DisposableStore, dispose, IDisposable } from 'vs/base/common/lifecycle';
import { DisposableStore, IDisposable, dispose } from 'vs/base/common/lifecycle';
import * as objects from 'vs/base/common/objects';
import * as resources from 'vs/base/common/resources';
import { withUndefinedAsNull } from 'vs/base/common/types';
Expand All @@ -19,17 +19,16 @@ import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configur
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IFileService } from 'vs/platform/files/common/files';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { Extensions as JSONExtensions, IJSONContributionRegistry } from 'vs/platform/jsonschemas/common/jsonContributionRegistry';
import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry';
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
import { Registry } from 'vs/platform/registry/common/platform';
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
import { ThemeIcon } from 'vs/platform/theme/common/themeService';
import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity';
import { IWorkspaceContextService, IWorkspaceFolder, IWorkspaceFoldersChangeEvent, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { IEditorPane } from 'vs/workbench/common/editor';
import { AdapterManager } from 'vs/workbench/contrib/debug/browser/debugAdapterManager';
import { debugConfigure } from 'vs/workbench/contrib/debug/browser/debugIcons';
import { CONTEXT_DEBUG_CONFIGURATION_TYPE, DebugConfigurationProviderTriggerKind, ICompound, IConfig, IConfigPresentation, IConfigurationManager, IDebugConfigurationProvider, IGlobalConfig, ILaunch } from 'vs/workbench/contrib/debug/common/debug';
import { CONTEXT_DEBUG_CONFIGURATION_TYPE, DebugConfigurationProviderTriggerKind, IAdapterManager, ICompound, IConfig, IConfigPresentation, IConfigurationManager, IDebugConfigurationProvider, IGlobalConfig, ILaunch } from 'vs/workbench/contrib/debug/common/debug';
import { launchSchema } from 'vs/workbench/contrib/debug/common/debugSchemas';
import { getVisibleAndSorted } from 'vs/workbench/contrib/debug/common/debugUtils';
import { launchSchemaId } from 'vs/workbench/services/configuration/common/configuration';
Expand Down Expand Up @@ -63,7 +62,7 @@ export class ConfigurationManager implements IConfigurationManager {
private debugConfigurationTypeContext: IContextKey<string>;

constructor(
private readonly adapterManager: AdapterManager,
private readonly adapterManager: IAdapterManager,
@IWorkspaceContextService private readonly contextService: IWorkspaceContextService,
@IConfigurationService private readonly configurationService: IConfigurationService,
@IQuickInputService private readonly quickInputService: IQuickInputService,
Expand Down Expand Up @@ -120,22 +119,25 @@ export class ConfigurationManager implements IConfigurationManager {
}

async resolveConfigurationByProviders(folderUri: uri | undefined, type: string | undefined, config: IConfig, token: CancellationToken): Promise<IConfig | null | undefined> {
await this.adapterManager.activateDebuggers('onDebugResolve', type);
// pipe the config through the promises sequentially. Append at the end the '*' types
const providers = this.configProviders.filter(p => p.type === type && p.resolveDebugConfiguration)
.concat(this.configProviders.filter(p => p.type === '*' && p.resolveDebugConfiguration));
const resolveDebugConfigurationForType = async (type: string | undefined, config: IConfig | null | undefined) => {
if (type !== '*') {
await this.adapterManager.activateDebuggers('onDebugResolve', type);
}

let result: IConfig | null | undefined = config;
await sequence(providers.map(provider => async () => {
// If any provider returned undefined or null make sure to respect that and do not pass the result to more resolver
if (result) {
result = await provider.resolveDebugConfiguration!(folderUri, result, token);
for (const p of this.configProviders) {
if (p.type === type && p.resolveDebugConfiguration && config) {
config = await p.resolveDebugConfiguration(folderUri, config, token);
}
}
}));

// The resolver can change the type, ensure activation happens, #135090
if (result?.type && result.type !== config.type) {
await this.adapterManager.activateDebuggers('onDebugResolve', result.type);
return config;
};

let result: IConfig | null | undefined = config;
for (let seen = new Set(); result && !seen.has(result.type);) {
seen.add(result?.type);
result = await resolveDebugConfigurationForType(result.type, result);
result = await resolveDebugConfigurationForType('*', result);
}

return result;
Expand Down Expand Up @@ -477,7 +479,7 @@ abstract class AbstractLaunch implements ILaunch {

constructor(
protected configurationManager: ConfigurationManager,
private readonly adapterManager: AdapterManager
private readonly adapterManager: IAdapterManager
) { }

getCompound(name: string): ICompound | undefined {
Expand Down Expand Up @@ -550,7 +552,7 @@ class Launch extends AbstractLaunch implements ILaunch {

constructor(
configurationManager: ConfigurationManager,
adapterManager: AdapterManager,
adapterManager: IAdapterManager,
public workspace: IWorkspaceFolder,
@IFileService private readonly fileService: IFileService,
@ITextFileService private readonly textFileService: ITextFileService,
Expand Down Expand Up @@ -633,7 +635,7 @@ class Launch extends AbstractLaunch implements ILaunch {
class WorkspaceLaunch extends AbstractLaunch implements ILaunch {
constructor(
configurationManager: ConfigurationManager,
adapterManager: AdapterManager,
adapterManager: IAdapterManager,
@IEditorService private readonly editorService: IEditorService,
@IConfigurationService private readonly configurationService: IConfigurationService,
@IWorkspaceContextService private readonly contextService: IWorkspaceContextService
Expand Down Expand Up @@ -685,7 +687,7 @@ class UserLaunch extends AbstractLaunch implements ILaunch {

constructor(
configurationManager: ConfigurationManager,
adapterManager: AdapterManager,
adapterManager: IAdapterManager,
@IConfigurationService private readonly configurationService: IConfigurationService,
@IPreferencesService private readonly preferencesService: IPreferencesService
) {
Expand Down
5 changes: 5 additions & 0 deletions src/vs/workbench/contrib/debug/common/debug.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ export interface IDebugger {
runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, sessionId: string): Promise<number | undefined>;
startDebugging(args: IConfig, parentSessionId: string): Promise<boolean>;
getCustomTelemetryEndpoint(): ITelemetryEndpoint | undefined;
getInitialConfigurationContent(initialConfigs?: IConfig[]): Promise<string>;
}

export interface IDebuggerMetadata {
Expand Down Expand Up @@ -897,6 +898,10 @@ export interface IAdapterManager {

substituteVariables(debugType: string, folder: IWorkspaceFolder | undefined, config: IConfig): Promise<IConfig>;
runInTerminal(debugType: string, args: DebugProtocol.RunInTerminalRequestArguments, sessionId: string): Promise<number | undefined>;
getEnabledDebugger(type: string): (IDebugger & IDebuggerMetadata) | undefined;
guessDebugger(gettingConfigurations: boolean): Promise<(IDebugger & IDebuggerMetadata) | undefined>;

get onDidDebuggersExtPointRead(): Event<void>;
}

export interface ILaunch {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import * as assert from 'assert';
import { CancellationToken } from 'vs/base/common/cancellation';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { Event } from 'vs/base/common/event';
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
import { ContextKeyService } from 'vs/platform/contextkey/browser/contextKeyService';
import { FileService } from 'vs/platform/files/common/fileService';
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
import { NullLogService } from 'vs/platform/log/common/log';
import { UriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentityService';
import { ConfigurationManager } from 'vs/workbench/contrib/debug/browser/debugConfigurationManager';
import { DebugConfigurationProviderTriggerKind, IAdapterManager, IConfig, IDebugAdapterExecutable, IDebugSession } from 'vs/workbench/contrib/debug/common/debug';
import { TestHistoryService, TestQuickInputService } from 'vs/workbench/test/browser/workbenchTestServices';
import { TestContextService, TestExtensionService, TestStorageService } from 'vs/workbench/test/common/workbenchTestServices';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences';
import { URI } from 'vs/base/common/uri';

suite('debugConfigurationManager', () => {
const configurationProviderType = 'custom-type';
let _debugConfigurationManager: ConfigurationManager;
const disposables = new DisposableStore();

const adapterManager = <IAdapterManager>{
getDebugAdapterDescriptor(session: IDebugSession, config: IConfig): Promise<IDebugAdapterExecutable | undefined> {
return Promise.resolve(undefined);
},

activateDebuggers(activationEvent: string, debugType?: string): Promise<void> {
return Promise.resolve();
},

get onDidDebuggersExtPointRead(): Event<void> {
return Event.None;
}
};

const preferencesService = <IPreferencesService>{
userSettingsResource: URI.file('/tmp/settings.json')
};

const configurationService = new TestConfigurationService();
setup(() => {
const fileService = disposables.add(new FileService(new NullLogService()));
_debugConfigurationManager = new ConfigurationManager(
adapterManager,
new TestContextService(),
configurationService,
new TestQuickInputService(),
new TestInstantiationService(new ServiceCollection([IPreferencesService, preferencesService])),
new TestStorageService(),
new TestExtensionService(),
new TestHistoryService(),
new UriIdentityService(fileService),
new ContextKeyService(configurationService));
});

test('resolves configuration based on type', async () => {
disposables.add(_debugConfigurationManager.registerDebugConfigurationProvider({
type: configurationProviderType,
resolveDebugConfiguration: (folderUri, config, token) => {
assert.strictEqual(config.type, configurationProviderType);
return Promise.resolve({
...config,
configurationResolved: true
});
},
triggerKind: DebugConfigurationProviderTriggerKind.Initial
}));

const initialConfig: IConfig = {
type: configurationProviderType,
request: 'launch',
name: 'configName',
};

const resultConfig = await _debugConfigurationManager.resolveConfigurationByProviders(undefined, configurationProviderType, initialConfig, CancellationToken.None);
assert.strictEqual((resultConfig as any).configurationResolved, true, 'Configuration should be updated by test provider');
});

test('resolves configuration from second provider if type changes', async () => {
const secondProviderType = 'second-provider';
disposables.add(_debugConfigurationManager.registerDebugConfigurationProvider({
type: configurationProviderType,
resolveDebugConfiguration: (folderUri, config, token) => {
assert.strictEqual(config.type, configurationProviderType);
return Promise.resolve({
...config,
type: secondProviderType
});
},
triggerKind: DebugConfigurationProviderTriggerKind.Initial
}));
disposables.add(_debugConfigurationManager.registerDebugConfigurationProvider({
type: secondProviderType,
resolveDebugConfiguration: (folderUri, config, token) => {
assert.strictEqual(config.type, secondProviderType);
return Promise.resolve({
...config,
configurationResolved: true
});
},
triggerKind: DebugConfigurationProviderTriggerKind.Initial
}));

const initialConfig: IConfig = {
type: configurationProviderType,
request: 'launch',
name: 'configName',
};

const resultConfig = await _debugConfigurationManager.resolveConfigurationByProviders(undefined, configurationProviderType, initialConfig, CancellationToken.None);
assert.strictEqual(resultConfig!.type, secondProviderType);
assert.strictEqual((resultConfig as any).configurationResolved, true, 'Configuration should be updated by test provider');
});

teardown(() => disposables.clear());
});

0 comments on commit add43fa

Please sign in to comment.