Skip to content

Commit

Permalink
Merge pull request microsoft#41818 from Microsoft/isidorn/multiRootLa…
Browse files Browse the repository at this point in the history
…unch

support Compound debug configurations across workspace folders
  • Loading branch information
isidorn authored Jan 23, 2018
2 parents 58c705b + 32fcea6 commit fb20841
Show file tree
Hide file tree
Showing 10 changed files with 133 additions and 52 deletions.
6 changes: 3 additions & 3 deletions src/vs/workbench/parts/debug/browser/debugActionItems.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ export class StartDebugActionItem implements IActionItem {
if (name === manager.selectedName && launch === manager.selectedLaunch) {
this.selected = this.options.length;
}
const label = launches.length > 1 ? `${name} (${launch.workspace.name})` : name;
const label = launches.length > 1 ? `${name} (${launch.name})` : name;
this.options.push({ label, handler: () => { manager.selectConfiguration(launch, name); return true; } });
}));

Expand All @@ -169,10 +169,10 @@ export class StartDebugActionItem implements IActionItem {

const disabledIdx = this.options.length - 1;
launches.forEach(l => {
const label = launches.length > 1 ? nls.localize("addConfigTo", "Add Config ({0})...", l.workspace.name) : nls.localize('addConfiguration', "Add Configuration...");
const label = launches.length > 1 ? nls.localize("addConfigTo", "Add Config ({0})...", l.name) : nls.localize('addConfiguration', "Add Configuration...");
this.options.push({
label, handler: () => {
this.commandService.executeCommand('debug.addConfiguration', l.workspace.uri.toString()).done(undefined, errors.onUnexpectedError);
this.commandService.executeCommand('debug.addConfiguration', l.uri.toString()).done(undefined, errors.onUnexpectedError);
return false;
}
});
Expand Down
2 changes: 1 addition & 1 deletion src/vs/workbench/parts/debug/browser/debugActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ export class StartAction extends AbstractDebugAction {
if (contextService && contextService.getWorkbenchState() === WorkbenchState.EMPTY && processes.length > 0) {
return false;
}
if (processes.some(p => p.getName(false) === configName && (!launch || p.session.root.uri.toString() === launch.workspace.uri.toString()))) {
if (processes.some(p => p.getName(false) === configName && (!launch || p.session.root.uri.toString() === launch.uri.toString()))) {
return false;
}
const compound = launch && launch.getCompound(configName);
Expand Down
8 changes: 4 additions & 4 deletions src/vs/workbench/parts/debug/browser/debugQuickOpen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class AddConfigEntry extends Model.QuickOpenEntry {
}

public getDescription(): string {
return this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE ? this.launch.workspace.name : '';
return this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE ? this.launch.name : '';
}

public getAriaLabel(): string {
Expand All @@ -40,7 +40,7 @@ class AddConfigEntry extends Model.QuickOpenEntry {
if (mode === QuickOpen.Mode.PREVIEW) {
return false;
}
this.commandService.executeCommand('debug.addConfiguration', this.launch.workspace.uri.toString()).done(undefined, errors.onUnexpectedError);
this.commandService.executeCommand('debug.addConfiguration', this.launch.uri.toString()).done(undefined, errors.onUnexpectedError);

return true;
}
Expand All @@ -57,7 +57,7 @@ class StartDebugEntry extends Model.QuickOpenEntry {
}

public getDescription(): string {
return this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE ? this.launch.workspace.name : '';
return this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE ? this.launch.name : '';
}

public getAriaLabel(): string {
Expand Down Expand Up @@ -110,7 +110,7 @@ export class DebugQuickOpenHandler extends Quickopen.QuickOpenHandler {
});
}
launches.forEach((l, index) => {
const label = launches.length > 1 ? nls.localize("addConfigTo", "Add Config ({0})...", l.workspace.name) : nls.localize('addConfiguration', "Add Configuration...");
const label = launches.length > 1 ? nls.localize("addConfigTo", "Add Config ({0})...", l.name) : nls.localize('addConfiguration', "Add Configuration...");
const entry = new AddConfigEntry(label, l, this.commandService, this.contextService, Filters.matchesContiguousSubString(input, label));
if (index === 0) {
configurations.push(new QuickOpenEntryGroup(entry, undefined, true));
Expand Down
2 changes: 1 addition & 1 deletion src/vs/workbench/parts/debug/browser/debugStatus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ export class DebugStatus extends Themable implements IStatusbarItem {
if (manager.selectedName) {
const name = manager.selectedName;
this.statusBarItem.style.display = 'block';
this.label.textContent = manager.getLaunches().length > 1 ? `${name} (${manager.selectedLaunch.workspace.name})` : name;
this.label.textContent = manager.getLaunches().length > 1 ? `${name} (${manager.selectedLaunch.name})` : name;
} else {
this.statusBarItem.style.display = 'none';
}
Expand Down
7 changes: 6 additions & 1 deletion src/vs/workbench/parts/debug/common/debug.ts
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,11 @@ export interface ILaunch {
*/
uri: uri;

/**
* Name of the launch.
*/
name: string;

workspace: IWorkspaceFolder;

/**
Expand Down Expand Up @@ -466,7 +471,7 @@ export interface ILaunch {
/**
* Opens the launch.json file. Creates if it does not exist.
*/
openConfigFile(sideBySide: boolean, type?: string): TPromise<{ editor: IEditor; configFileCreated: boolean; }>;
openConfigFile(sideBySide: boolean, type?: string): TPromise<IEditor>;
}

// Debug service interfaces
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,11 +169,11 @@ export function registerCommands(): void {
accessor.get(IMessageService).show(severity.Info, nls.localize('noFolderDebugConfig', "Please first open a folder in order to do advanced debug configuration."));
return TPromise.as(null);
}
const launch = manager.getLaunches().filter(l => l.workspace.uri.toString() === workspaceUri).pop() || manager.selectedLaunch;
const launch = manager.getLaunches().filter(l => l.uri.toString() === workspaceUri).pop() || manager.selectedLaunch;

return launch.openConfigFile(false).done(result => {
if (result.editor && !result.configFileCreated) {
const codeEditor = <ICodeEditor>result.editor.getControl();
return launch.openConfigFile(false).done(editor => {
if (editor) {
const codeEditor = <ICodeEditor>editor.getControl();
if (codeEditor) {
return codeEditor.getContribution<IDebugEditorContribution>(EDITOR_CONTRIBUTION_ID).addLaunchConfiguration();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import Event, { Emitter } from 'vs/base/common/event';
import { TPromise } from 'vs/base/common/winjs.base';
import * as strings from 'vs/base/common/strings';
import { first } from 'vs/base/common/arrays';
import severity from 'vs/base/common/severity';
import { isLinux, isMacintosh, isWindows } from 'vs/base/common/platform';
import * as objects from 'vs/base/common/objects';
import uri from 'vs/base/common/uri';
Expand All @@ -24,7 +25,7 @@ import { IExtensionService } from 'vs/platform/extensions/common/extensions';
import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IFileService } from 'vs/platform/files/common/files';
import { IWorkspaceContextService, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { IWorkspaceContextService, IWorkspaceFolder, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { IDebugConfigurationProvider, IRawAdapter, ICompound, IDebugConfiguration, IConfig, IEnvConfig, IGlobalConfig, IConfigurationManager, ILaunch } from 'vs/workbench/parts/debug/common/debug';
Expand All @@ -33,6 +34,8 @@ import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/edi
import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen';
import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver';
import { isCodeEditor } from 'vs/editor/browser/editorBrowser';
import { launchSchemaId } from 'vs/workbench/services/configuration/common/configuration';
import { IMessageService } from 'vs/platform/message/common/message';

// debuggers extension point
export const debuggersExtPoint = extensionsRegistry.ExtensionsRegistry.registerExtensionPoint<IRawAdapter[]>('debuggers', [], {
Expand Down Expand Up @@ -147,14 +150,12 @@ const breakpointsExtPoint = extensionsRegistry.ExtensionsRegistry.registerExtens
});

// debug general schema

export const schemaId = 'vscode://schemas/launch';
const defaultCompound: ICompound = { name: 'Compound', configurations: [] };
const schema: IJSONSchema = {
id: schemaId,
id: launchSchemaId,
type: 'object',
title: nls.localize('app.launch.json.title', "Launch"),
required: ['version', 'configurations'],
required: [],
default: { version: '0.2.0', configurations: [], compounds: [] },
properties: {
version: {
Expand Down Expand Up @@ -201,7 +202,7 @@ const schema: IJSONSchema = {
};

const jsonRegistry = <IJSONContributionRegistry>Registry.as(JSONExtensions.JSONContribution);
jsonRegistry.registerSchema(schemaId, schema);
jsonRegistry.registerSchema(launchSchemaId, schema);
const DEBUG_SELECTED_CONFIG_NAME_KEY = 'debug.selectedconfigname';
const DEBUG_SELECTED_ROOT = 'debug.selectedroot';

Expand Down Expand Up @@ -232,7 +233,7 @@ export class ConfigurationManager implements IConfigurationManager {
this.registerListeners(lifecycleService);
this.initLaunches();
const previousSelectedRoot = this.storageService.get(DEBUG_SELECTED_ROOT, StorageScope.WORKSPACE);
const filtered = this.launches.filter(l => l.workspace.uri.toString() === previousSelectedRoot);
const filtered = this.launches.filter(l => l.uri.toString() === previousSelectedRoot);
this.selectConfiguration(filtered.length ? filtered[0] : undefined, this.storageService.get(DEBUG_SELECTED_CONFIG_NAME_KEY, StorageScope.WORKSPACE));
}

Expand Down Expand Up @@ -335,6 +336,10 @@ export class ConfigurationManager implements IConfigurationManager {

private initLaunches(): void {
this.launches = this.contextService.getWorkspace().folders.map(folder => this.instantiationService.createInstance(Launch, this, folder));
if (this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE) {
this.launches.push(this.instantiationService.createInstance(WorkspaceLaunch, this));
}

if (this.launches.indexOf(this._selectedLaunch) === -1) {
this._selectedLaunch = undefined;
}
Expand All @@ -356,6 +361,14 @@ export class ConfigurationManager implements IConfigurationManager {
return this._onDidSelectConfigurationName.event;
}

public getWorkspaceLaunch(): ILaunch {
if (this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE) {
return this.launches[this.launches.length - 1];
}

return undefined;
}

public selectConfiguration(launch?: ILaunch, name?: string, debugStarted?: boolean): void {
const previousLaunch = this._selectedLaunch;
const previousName = this._selectedName;
Expand Down Expand Up @@ -438,7 +451,7 @@ export class ConfigurationManager implements IConfigurationManager {
private store(): void {
this.storageService.store(DEBUG_SELECTED_CONFIG_NAME_KEY, this.selectedName, StorageScope.WORKSPACE);
if (this._selectedLaunch) {
this.storageService.store(DEBUG_SELECTED_ROOT, this._selectedLaunch.workspace.uri.toString(), StorageScope.WORKSPACE);
this.storageService.store(DEBUG_SELECTED_ROOT, this._selectedLaunch.uri.toString(), StorageScope.WORKSPACE);
}
}

Expand All @@ -453,15 +466,28 @@ class Launch implements ILaunch {
private configurationManager: ConfigurationManager,
public workspace: IWorkspaceFolder,
@IFileService private fileService: IFileService,
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
@IConfigurationService private configurationService: IConfigurationService,
@IConfigurationResolverService private configurationResolverService: IConfigurationResolverService
@IWorkbenchEditorService protected editorService: IWorkbenchEditorService,
@IConfigurationService protected configurationService: IConfigurationService,
@IConfigurationResolverService private configurationResolverService: IConfigurationResolverService,
@IMessageService private messageService: IMessageService
) {
// noop
}

public get uri(): uri {
return this.workspace.uri.with({ path: paths.join(this.workspace.uri.path, '/.vscode/launch.json') });
}

public get name(): string {
return this.workspace.name;
}

protected getConfig(): IGlobalConfig {
return this.configurationService.getValue<IGlobalConfig>('launch', { resource: this.workspace.uri });
}

public getCompound(name: string): ICompound {
const config = this.configurationService.getValue<IGlobalConfig>('launch', { resource: this.workspace.uri });
const config = this.getConfig();
if (!config || !config.compounds) {
return null;
}
Expand All @@ -470,7 +496,7 @@ class Launch implements ILaunch {
}

public getConfigurationNames(): string[] {
const config = this.configurationService.getValue<IGlobalConfig>('launch', { resource: this.workspace.uri });
const config = this.getConfig();
if (!config || !config.configurations || !Array.isArray(config.configurations)) {
return [];
} else {
Expand All @@ -487,7 +513,7 @@ class Launch implements ILaunch {
}

public getConfiguration(name: string): IConfig {
const config = objects.deepClone(this.configurationService.getValue<IGlobalConfig>('launch', { resource: this.workspace.uri }));
const config = this.getConfig();
if (!config || !config.configurations) {
return null;
}
Expand Down Expand Up @@ -518,15 +544,11 @@ class Launch implements ILaunch {
return this.configurationResolverService.resolveInteractiveVariables(result, adapter ? adapter.variables : null);
}

public get uri(): uri {
return this.workspace.uri.with({ path: paths.join(this.workspace.uri.path, '/.vscode/launch.json') });
}

public openConfigFile(sideBySide: boolean, type?: string): TPromise<{ editor: IEditor; configFileCreated: boolean; }> {
public openConfigFile(sideBySide: boolean, type?: string): TPromise<IEditor> {
const resource = this.uri;
let configFileCreated = false;

return this.fileService.resolveContent(resource).then(content => content, err => {
return this.fileService.resolveContent(resource).then(content => content.value, err => {

// launch.json not found: create one by collecting launch configs from debugConfigProviders

Expand All @@ -547,17 +569,17 @@ class Launch implements ILaunch {
configFileCreated = true;
return this.fileService.updateContent(resource, content).then(() => {
// convert string into IContent; see #32135
return { value: content };
return content;
});
});
}).then(content => {
if (!content) {
return { editor: undefined, configFileCreated };
return undefined;
}
const index = content.value.indexOf(`"${this.configurationManager.selectedName}"`);
const index = content.indexOf(`"${this.configurationManager.selectedName}"`);
let startLineNumber = 1;
for (let i = 0; i < index; i++) {
if (content.value.charAt(i) === '\n') {
if (content.charAt(i) === '\n') {
startLineNumber++;
}
}
Expand All @@ -571,9 +593,46 @@ class Launch implements ILaunch {
pinned: configFileCreated, // pin only if config file is created #8727
revealIfVisible: true
},
}, sideBySide).then(editor => ({ editor, configFileCreated }));
}, sideBySide).then(editor => {
if (configFileCreated) {
this.messageService.show(severity.Info, nls.localize('NewLaunchConfig', "Please set up the launch configuration file for your application."));
}

return editor;
});
}, (error) => {
throw new Error(nls.localize('DebugConfig.failed', "Unable to create 'launch.json' file inside the '.vscode' folder ({0}).", error));
});
}
}

class WorkspaceLaunch extends Launch implements ILaunch {

constructor(
configurationManager: ConfigurationManager,
@IFileService fileService: IFileService,
@IWorkbenchEditorService editorService: IWorkbenchEditorService,
@IConfigurationService configurationService: IConfigurationService,
@IConfigurationResolverService configurationResolverService: IConfigurationResolverService,
@IWorkspaceContextService private workspaceContextService: IWorkspaceContextService,
@IMessageService messageService: IMessageService
) {
super(configurationManager, undefined, fileService, editorService, configurationService, configurationResolverService, messageService);
}

get uri(): uri {
return this.workspaceContextService.getWorkspace().configuration;
}

get name(): string {
return nls.localize('workspace', "workspace");
}

protected getConfig(): IGlobalConfig {
return this.configurationService.inspect<IGlobalConfig>('launch').workspace;
}

openConfigFile(sideBySide: boolean, type?: string): TPromise<IEditor> {
return this.editorService.openEditor({ resource: this.workspaceContextService.getWorkspace().configuration });
}
}
Loading

0 comments on commit fb20841

Please sign in to comment.