diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index c93aeb69e01..d237dad287e 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -232,4 +232,45 @@ declare module 'vscode' { export namespace languages { export function registerColorProvider(selector: DocumentSelector, provider: DocumentColorProvider): Disposable; } + + export namespace debug { + /** + * Register a [debug configuration provider](#DebugConfigurationProvider) for a specifc debug type. + * More than one provider can be registered for the same type. + * + * @param type The debug type for which the provider is registered. + * @param provider The [debug configuration provider](#DebugConfigurationProvider) to register. + * @return A [disposable](#Disposable) that unregisters this provider when being disposed. + */ + export function registerDebugConfigurationProvider(debugType: string, provider: DebugConfigurationProvider): Disposable; + } + + /** + * A debug configuration provider allows to add the initial debug configurations to a newly created launch.json + * and allows to resolve a launch configuration before it is used to start a new debug session. + * A debug configuration provider is registered via #workspace.registerDebugConfigurationProvider. + */ + export interface DebugConfigurationProvider { + /** + * Provides initial [debug configuration](#DebugConfiguration). If more than one debug configuration provider is + * registered for the same type, debug configurations are concatenated in arbitrary order. + * + * @param folder The workspace folder for which the configurations are used or undefined for a folderless setup. + * @param token A cancellation token. + * @return An array of [debug configurations](#DebugConfiguration). + */ + provideDebugConfigurations?(folder: WorkspaceFolder | undefined, token?: CancellationToken): ProviderResult; + + /** + * Resolves a [debug configuration](#DebugConfiguration) by filling in missing values or by adding/changing/removing attributes. + * If more than one debug configuration provider is registered for the same type, the resolveDebugConfiguration calls are chained + * in arbitrary order and the initial debug configuration is piped through the chain. + * + * @param folder The workspace folder from which the configuration originates from or undefined for a folderless setup. + * @param debugConfiguration The [debug configuration](#DebugConfiguration) to resolve. + * @param token A cancellation token. + * @return The resolved debug configuration. + */ + resolveDebugConfiguration?(folder: WorkspaceFolder | undefined, debugConfiguration: DebugConfiguration, token?: CancellationToken): ProviderResult; + } } diff --git a/src/vs/workbench/api/electron-browser/mainThreadDebugService.ts b/src/vs/workbench/api/electron-browser/mainThreadDebugService.ts index e3cdf3ea119..b22f7c6df8b 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadDebugService.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadDebugService.ts @@ -6,7 +6,7 @@ import URI from 'vs/base/common/uri'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; -import { IDebugService, IConfig } from 'vs/workbench/parts/debug/common/debug'; +import { IDebugService, IConfig, IDebugConfigurationProvider } from 'vs/workbench/parts/debug/common/debug'; import { TPromise } from 'vs/base/common/winjs.base'; import { ExtHostContext, ExtHostDebugServiceShape, MainThreadDebugServiceShape, DebugSessionUUID, MainContext, IExtHostContext } from '../node/extHost.protocol'; import { extHostNamedCustomer } from "vs/workbench/api/electron-browser/extHostCustomers"; @@ -44,6 +44,31 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape { this._toDispose = dispose(this._toDispose); } + public $registerDebugConfigurationProvider(debugType: string, hasProvide: boolean, hasResolve: boolean, handle: number): TPromise { + + const provider = { + type: debugType + }; + if (hasProvide) { + provider.provideDebugConfigurations = (folder: URI | undefined) => { + return this._proxy.$provideDebugConfigurations(handle, folder); + }; + } + if (hasResolve) { + provider.resolveDebugConfiguration = (folder: URI | undefined, debugConfiguration: any) => { + return this._proxy.$resolveDebugConfiguration(handle, folder, debugConfiguration); + }; + } + this.debugService.getConfigurationManager().registerDebugConfigurationProvider(handle, provider); + + return TPromise.as(undefined); + } + + public $unregisterDebugConfigurationProvider(handle: number): TPromise { + this.debugService.getConfigurationManager().unregisterDebugConfigurationProvider(handle); + return TPromise.as(undefined); + } + public $startDebugging(folderUri: URI | undefined, nameOrConfiguration: string | IConfig): TPromise { return this.debugService.startDebugging(folderUri, nameOrConfiguration).then(x => { return true; diff --git a/src/vs/workbench/api/node/extHost.api.impl.ts b/src/vs/workbench/api/node/extHost.api.impl.ts index cdd8abf31c4..7234a7a1cdd 100644 --- a/src/vs/workbench/api/node/extHost.api.impl.ts +++ b/src/vs/workbench/api/node/extHost.api.impl.ts @@ -80,7 +80,6 @@ export function createApiFactory( // Addressable instances const extHostHeapService = threadService.set(ExtHostContext.ExtHostHeapService, new ExtHostHeapService()); - const extHostDebugService = threadService.set(ExtHostContext.ExtHostDebugService, new ExtHostDebugService(threadService)); const extHostDocumentsAndEditors = threadService.set(ExtHostContext.ExtHostDocumentsAndEditors, new ExtHostDocumentsAndEditors(threadService)); const extHostDocuments = threadService.set(ExtHostContext.ExtHostDocuments, new ExtHostDocuments(threadService, extHostDocumentsAndEditors)); const extHostDocumentContentProviders = threadService.set(ExtHostContext.ExtHostDocumentContentProviders, new ExtHostDocumentContentProvider(threadService, extHostDocumentsAndEditors)); @@ -89,6 +88,7 @@ export function createApiFactory( const extHostCommands = threadService.set(ExtHostContext.ExtHostCommands, new ExtHostCommands(threadService, extHostHeapService)); const extHostTreeViews = threadService.set(ExtHostContext.ExtHostTreeViews, new ExtHostTreeViews(threadService.get(MainContext.MainThreadTreeViews), extHostCommands)); const extHostWorkspace = threadService.set(ExtHostContext.ExtHostWorkspace, new ExtHostWorkspace(threadService, initData.workspace)); + const extHostDebugService = threadService.set(ExtHostContext.ExtHostDebugService, new ExtHostDebugService(threadService, extHostWorkspace)); const extHostConfiguration = threadService.set(ExtHostContext.ExtHostConfiguration, new ExtHostConfiguration(threadService.get(MainContext.MainThreadConfiguration), extHostWorkspace, initData.configuration)); const extHostDiagnostics = threadService.set(ExtHostContext.ExtHostDiagnostics, new ExtHostDiagnostics(threadService)); const languageFeatures = threadService.set(ExtHostContext.ExtHostLanguageFeatures, new ExtHostLanguageFeatures(threadService, extHostDocuments, extHostCommands, extHostHeapService, extHostDiagnostics)); @@ -500,7 +500,10 @@ export function createApiFactory( }, onDidReceiveDebugSessionCustomEvent(listener, thisArg?, disposables?) { return extHostDebugService.onDidReceiveDebugSessionCustomEvent(listener, thisArg, disposables); - } + }, + registerDebugConfigurationProvider: proposedApiFunction(extension, (debugType: string, provider: vscode.DebugConfigurationProvider) => { + return extHostDebugService.registerDebugConfigurationProvider(debugType, provider); + }), }; // namespace: credentials diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index 5bd7b384da8..395ee8ae8e9 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -332,6 +332,8 @@ export interface MainThreadSCMShape extends IDisposable { export type DebugSessionUUID = string; export interface MainThreadDebugServiceShape extends IDisposable { + $registerDebugConfigurationProvider(type: string, hasProvideMethod: boolean, hasResolveMethod: boolean, handle: number): TPromise; + $unregisterDebugConfigurationProvider(handle: number): TPromise; $startDebugging(folderUri: URI | undefined, nameOrConfig: string | vscode.DebugConfiguration): TPromise; $startDebugSession(folderUri: URI | undefined, config: vscode.DebugConfiguration): TPromise; $customDebugAdapterRequest(id: DebugSessionUUID, command: string, args: any): TPromise; @@ -506,6 +508,8 @@ export interface ExtHostTaskShape { } export interface ExtHostDebugServiceShape { + $resolveDebugConfiguration(handle: number, folder: URI | undefined, debugConfiguration: any): TPromise; + $provideDebugConfigurations(handle: number, folder: URI | undefined): TPromise; $acceptDebugSessionStarted(id: DebugSessionUUID, type: string, name: string): void; $acceptDebugSessionTerminated(id: DebugSessionUUID, type: string, name: string): void; $acceptDebugSessionActiveChanged(id: DebugSessionUUID | undefined, type?: string, name?: string): void; diff --git a/src/vs/workbench/api/node/extHostDebugService.ts b/src/vs/workbench/api/node/extHostDebugService.ts index 10fab6b11d4..277fce79171 100644 --- a/src/vs/workbench/api/node/extHostDebugService.ts +++ b/src/vs/workbench/api/node/extHostDebugService.ts @@ -6,15 +6,22 @@ import { TPromise } from 'vs/base/common/winjs.base'; import Event, { Emitter } from 'vs/base/common/event'; - +import { asWinJsPromise } from 'vs/base/common/async'; import { MainContext, MainThreadDebugServiceShape, ExtHostDebugServiceShape, DebugSessionUUID, IMainContext } from 'vs/workbench/api/node/extHost.protocol'; +import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace'; import * as vscode from 'vscode'; import URI from 'vs/base/common/uri'; +import * as types from 'vs/workbench/api/node/extHostTypes'; export class ExtHostDebugService implements ExtHostDebugServiceShape { + private _workspace: ExtHostWorkspace; + + private _handleCounter: number; + private _handlers: Map; + private _debugServiceProxy: MainThreadDebugServiceShape; private _debugSessions: Map = new Map(); @@ -34,7 +41,13 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { get onDidReceiveDebugSessionCustomEvent(): Event { return this._onDidReceiveDebugSessionCustomEvent.event; } - constructor(mainContext: IMainContext) { + constructor(mainContext: IMainContext, workspace: ExtHostWorkspace) { + + this._workspace = workspace; + + this._handleCounter = 0; + this._handlers = new Map(); + this._onDidStartDebugSession = new Emitter(); this._onDidTerminateDebugSession = new Emitter(); this._onDidChangeActiveDebugSession = new Emitter(); @@ -43,6 +56,44 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { this._debugServiceProxy = mainContext.get(MainContext.MainThreadDebugService); } + public registerDebugConfigurationProvider(type: string, provider: vscode.DebugConfigurationProvider): vscode.Disposable { + if (!provider) { + return new types.Disposable(() => { }); + } + + let handle = this.nextHandle(); + this._handlers.set(handle, provider); + this._debugServiceProxy.$registerDebugConfigurationProvider(type, !!provider.provideDebugConfigurations, !!provider.resolveDebugConfiguration, handle); + + return new types.Disposable(() => { + this._handlers.delete(handle); + this._debugServiceProxy.$unregisterDebugConfigurationProvider(handle); + }); + } + + public $provideDebugConfigurations(handle: number, folderUri: URI | undefined): TPromise { + let handler = this._handlers.get(handle); + if (!handler) { + return TPromise.wrapError(new Error('no handler found')); + } + if (!handler.provideDebugConfigurations) { + return TPromise.wrapError(new Error('handler has no method provideDebugConfigurations')); + } + return asWinJsPromise(token => handler.provideDebugConfigurations(this.getFolder(folderUri), token)); + } + + + public $resolveDebugConfiguration(handle: number, folderUri: URI | undefined, debugConfiguration: vscode.DebugConfiguration): TPromise { + let handler = this._handlers.get(handle); + if (!handler) { + return TPromise.wrapError(new Error('no handler found')); + } + if (!handler.resolveDebugConfiguration) { + return TPromise.wrapError(new Error('handler has no method resolveDebugConfiguration')); + } + return asWinJsPromise(token => handler.resolveDebugConfiguration(this.getFolder(folderUri), debugConfiguration, token)); + } + public startDebugging(folder: vscode.WorkspaceFolder | undefined, nameOrConfig: string | vscode.DebugConfiguration): TPromise { return this._debugServiceProxy.$startDebugging(folder ? folder.uri : undefined, nameOrConfig); @@ -106,6 +157,21 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { }; this._onDidReceiveDebugSessionCustomEvent.fire(ee); } + + private getFolder(folderUri: URI | undefined) { + if (folderUri) { + const folders = this._workspace.getWorkspaceFolders(); + const found = folders.filter(f => f.uri.toString() === folderUri.toString()); + if (found && found.length > 0) { + return found[0]; + } + } + return undefined; + } + + private nextHandle(): number { + return this._handleCounter++; + } } export class ExtHostDebugSession implements vscode.DebugSession { diff --git a/src/vs/workbench/parts/debug/common/debug.ts b/src/vs/workbench/parts/debug/common/debug.ts index b6bd691b0cb..2b4d00ad05e 100644 --- a/src/vs/workbench/parts/debug/common/debug.ts +++ b/src/vs/workbench/parts/debug/common/debug.ts @@ -373,6 +373,12 @@ export interface IRawAdapter extends IRawEnvAdapter { linux?: IRawEnvAdapter; } +export interface IDebugConfigurationProvider { + type: string; + resolveDebugConfiguration?(folderUri: uri | undefined, debugConfiguration: any): TPromise; + provideDebugConfigurations?(folderUri: uri | undefined): TPromise; +} + export interface IConfigurationManager { /** * Returns true if breakpoints can be set for a given editor model. Depends on mode. @@ -403,6 +409,10 @@ export interface IConfigurationManager { * the active editor language and matching it against the "languages" contribution of an adapter. */ getStartSessionCommand(type?: string): TPromise<{ command: string, type: string }>; + + registerDebugConfigurationProvider(handle: number, debugConfigurationProvider: IDebugConfigurationProvider): void; + unregisterDebugConfigurationProvider(handle): void; + resolveDebugConfiguration(folderUri: uri | undefined, debugConfiguration: any): TPromise; } export interface ILaunch { diff --git a/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.ts b/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.ts index a1c7fd77537..a7927aed309 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.ts @@ -28,7 +28,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ICommandService } from 'vs/platform/commands/common/commands'; -import { IRawAdapter, ICompound, IDebugConfiguration, DEBUG_SCHEME, IConfig, IEnvConfig, IGlobalConfig, IConfigurationManager, ILaunch } from 'vs/workbench/parts/debug/common/debug'; +import { IDebugConfigurationProvider, IRawAdapter, ICompound, IDebugConfiguration, DEBUG_SCHEME, IConfig, IEnvConfig, IGlobalConfig, IConfigurationManager, ILaunch } from 'vs/workbench/parts/debug/common/debug'; import { Adapter } from 'vs/workbench/parts/debug/node/debugAdapter'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen'; @@ -220,6 +220,7 @@ export class ConfigurationManager implements IConfigurationManager { private toDispose: IDisposable[]; private _onDidSelectConfigurationName = new Emitter(); private _mru: { launch: ILaunch, name: string }[]; + private _providers: Map; constructor( @IWorkspaceContextService private contextService: IWorkspaceContextService, @@ -234,6 +235,7 @@ export class ConfigurationManager implements IConfigurationManager { @IStorageService private storageService: IStorageService, @ILifecycleService lifecycleService: ILifecycleService, ) { + this._providers = new Map(); this.adapters = []; this.toDispose = []; this.registerListeners(lifecycleService); @@ -262,6 +264,51 @@ export class ConfigurationManager implements IConfigurationManager { } } + public registerDebugConfigurationProvider(handle: number, debugConfigurationProvider: IDebugConfigurationProvider): void { + if (!debugConfigurationProvider) { + return; + } + this._providers.set(handle, debugConfigurationProvider); + } + + public unregisterDebugConfigurationProvider(handle: number): boolean { + return this._providers.delete(handle); + } + + public resolveDebugConfiguration(folderUri: uri | undefined, debugConfiguration: any): TPromise { + + // collect all candidates + const providers: IDebugConfigurationProvider[] = []; + this._providers.forEach(provider => { + if (provider.type === debugConfiguration.type && provider.resolveDebugConfiguration) { + providers.push(provider); + } + }); + + // pipe the config through the promises sequentially + return providers.reduce((promise, provider) => { + return promise.then(config => { + return provider.resolveDebugConfiguration(folderUri, config); + }); + }, TPromise.as(debugConfiguration)); + } + + public provideDebugConfigurations(folderUri: uri | undefined, type: string): TPromise { + + // collect all candidates + const configs: TPromise[] = []; + this._providers.forEach(provider => { + if (provider.type === type && provider.provideDebugConfigurations) { + configs.push(provider.provideDebugConfigurations(folderUri)); + } + }); + + // combine all configs into one array + return TPromise.join(configs).then(results => { + return [].concat.apply([], results); + }); + } + private registerListeners(lifecycleService: ILifecycleService): void { debuggersExtPoint.setHandler((extensions) => { extensions.forEach(extension => { @@ -525,18 +572,29 @@ class Launch implements ILaunch { let configFileCreated = false; return this.fileService.resolveContent(resource).then(content => content, err => { - return this.configurationManager.guessAdapter(type).then(adapter => adapter ? adapter.getInitialConfigurationContent(this.workspaceUri) : undefined) - .then(content => { - if (!content) { - return undefined; - } - configFileCreated = true; - return this.fileService.updateContent(resource, content).then(() => { - // convert string into IContent; see #32135 - return { value: content }; + // launch.json not found: create one by collecting launch configs from debugConfigProviders + + return this.configurationManager.guessAdapter(type).then(adapter => { + if (adapter) { + return this.configurationManager.provideDebugConfigurations(this.workspaceUri, adapter.type).then(initialConfigs => { + return adapter.getInitialConfigurationContent(this.workspaceUri, initialConfigs); }); + } else { + return undefined; + } + }).then(content => { + + if (!content) { + return undefined; + } + + configFileCreated = true; + return this.fileService.updateContent(resource, content).then(() => { + // convert string into IContent; see #32135 + return { value: content }; }); + }); }).then(content => { if (!content) { return undefined; diff --git a/src/vs/workbench/parts/debug/electron-browser/debugService.ts b/src/vs/workbench/parts/debug/electron-browser/debugService.ts index 456902560b3..c1be37cfe29 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugService.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugService.ts @@ -661,6 +661,8 @@ export class DebugService implements debug.IDebugService { if (noDebug && config) { config.noDebug = true; } + + // deprecated code: use DebugConfigurationProvider instead of startSessionCommand if (commandAndType && commandAndType.command) { const defaultConfig = noDebug ? { noDebug: true } : {}; return this.commandService.executeCommand(commandAndType.command, config || defaultConfig, launch ? launch.workspaceUri : undefined).then((result: StartSessionResult) => { @@ -676,9 +678,14 @@ export class DebugService implements debug.IDebugService { return undefined; }); } + // end of deprecation if (config) { - return this.createProcess(root, config); + return this.configurationManager.resolveDebugConfiguration(launch ? launch.workspaceUri : undefined, config).then(config => { + + // TODO@AW: handle the 'initialConfiguration' and 'saveConfiguration' cases from above! + return this.createProcess(root, config); + }); } if (launch && commandAndType) { return launch.openConfigFile(false, commandAndType.type); diff --git a/src/vs/workbench/parts/debug/node/debugAdapter.ts b/src/vs/workbench/parts/debug/node/debugAdapter.ts index f2ddc085124..020127371fa 100644 --- a/src/vs/workbench/parts/debug/node/debugAdapter.ts +++ b/src/vs/workbench/parts/debug/node/debugAdapter.ts @@ -13,7 +13,7 @@ import * as objects from 'vs/base/common/objects'; import * as paths from 'vs/base/common/paths'; import * as platform from 'vs/base/common/platform'; import { IJSONSchema, IJSONSchemaSnippet } from 'vs/base/common/jsonSchema'; -import { IRawAdapter, IAdapterExecutable, INTERNAL_CONSOLE_OPTIONS_SCHEMA } from 'vs/workbench/parts/debug/common/debug'; +import { IConfig, IRawAdapter, IAdapterExecutable, INTERNAL_CONSOLE_OPTIONS_SCHEMA } from 'vs/workbench/parts/debug/common/debug'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -138,8 +138,10 @@ export class Adapter { return !!this.rawAdapter.initialConfigurations; } - public getInitialConfigurationContent(folderUri: uri): TPromise { + public getInitialConfigurationContent(folderUri: uri, initialConfigs?: IConfig[]): TPromise { const editorConfig = this.configurationService.getConfiguration(); + + // deprecated code: use DebugConfigurationProvider instead of command if (typeof this.rawAdapter.initialConfigurations === 'string') { // Contributed initialConfigurations is a command that needs to be invoked // Debug adapter will dynamically provide the full launch.json @@ -152,15 +154,36 @@ export class Adapter { return content; }); } + // end of deprecation - return TPromise.as(JSON.stringify( - { - version: '0.2.0', - configurations: this.rawAdapter.initialConfigurations || [] - }, - null, - editorConfig.editor && editorConfig.editor.insertSpaces ? strings.repeat(' ', editorConfig.editor.tabSize) : '\t' - )); + // at this point we got some configs from the package.json and/or from registered DebugConfigurationProviders + let initialConfigurations = this.rawAdapter.initialConfigurations || []; + if (initialConfigs) { + initialConfigurations = initialConfigurations.concat(initialConfigs); + } + + const configs = JSON.stringify(initialConfigurations, null, '\t').split('\n').map(line => '\t' + line).join('\n').trim(); + + const comment1 = nls.localize('launch.config.comment1', "Use IntelliSense to learn about possible attributes."); + const comment2 = nls.localize('launch.config.comment2', "Hover to view descriptions of existing attributes."); + const comment3 = nls.localize('launch.config.comment3', "For more information, visit: {0}", 'https://go.microsoft.com/fwlink/?linkid=830387'); + + let content = [ + '{', + `\t// ${comment1}`, + `\t// ${comment2}`, + `\t// ${comment3}`, + `\t"version": "0.2.0",`, + `\t"configurations": ${configs}`, + '}' + ].join('\n'); + + // fix formatting + if (editorConfig.editor && editorConfig.editor.insertSpaces) { + content = content.replace(new RegExp('\t', 'g'), strings.repeat(' ', editorConfig.editor.tabSize)); + } + + return TPromise.as(content); }; public getSchemaAttributes(): IJSONSchema[] { diff --git a/src/vs/workbench/parts/debug/test/node/debugAdapter.test.ts b/src/vs/workbench/parts/debug/test/node/debugAdapter.test.ts index 0bf502a6008..e65f7aa5193 100644 --- a/src/vs/workbench/parts/debug/test/node/debugAdapter.test.ts +++ b/src/vs/workbench/parts/debug/test/node/debugAdapter.test.ts @@ -110,18 +110,23 @@ suite('Debug - Adapter', () => { }); test('initial config file content', () => { - adapter.getInitialConfigurationContent(null).then(content => { - const expected = ['{', - ' "version": "0.2.0",', - ' "configurations": [', - ' {', - ' "name": "Mock-Debug",', - ' "type": "mock",', - ' "request": "launch",', - ' "program": "readme.md"', - ' }', - ' ]', - '}'].join('\n'); + + const expected = ['{', + ' // Use IntelliSense to learn about possible attributes.', + ' // Hover to view descriptions of existing attributes.', + ' // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387', + ' "version": "0.2.0",', + ' "configurations": [', + ' {', + ' "name": "Mock-Debug",', + ' "type": "mock",', + ' "request": "launch",', + ' "program": "readme.md"', + ' }', + ' ]', + '}'].join('\n'); + + return adapter.getInitialConfigurationContent(null).then(content => { assert.equal(content, expected); }, err => assert.fail()); });