diff --git a/src/vs/editor/browser/standalone/simpleServices.ts b/src/vs/editor/browser/standalone/simpleServices.ts index e380ac9bd39..c063ec34f7b 100644 --- a/src/vs/editor/browser/standalone/simpleServices.ts +++ b/src/vs/editor/browser/standalone/simpleServices.ts @@ -17,7 +17,7 @@ import { KeybindingResolver } from 'vs/platform/keybinding/common/keybindingReso import { IKeybindingEvent, KeybindingSource, IKeyboardEvent } from 'vs/platform/keybinding/common/keybinding'; import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IConfirmation, IMessageService } from 'vs/platform/message/common/message'; -import { IWorkspaceContextService, Workspace, IWorkspace } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, Workspace, IWorkspace, IWorkspace2 } from 'vs/platform/workspace/common/workspace'; import * as editorCommon from 'vs/editor/common/editorCommon'; import { ICodeEditor, IDiffEditor } from 'vs/editor/browser/editorBrowser'; import { Selection } from 'vs/editor/common/core/selection'; @@ -495,8 +495,8 @@ export class SimpleWorkspaceContextService implements IWorkspaceContextService { public _serviceBrand: any; - private readonly _onDidChangeFolders: Emitter = new Emitter(); - public readonly onDidChangeFolders: Event = this._onDidChangeFolders.event; + private readonly _onDidChangeWorkspaceRoots: Emitter = new Emitter(); + public readonly onDidChangeWorkspaceRoots: Event = this._onDidChangeWorkspaceRoots.event; private readonly folders: URI[]; @@ -512,6 +512,10 @@ export class SimpleWorkspaceContextService implements IWorkspaceContextService { return this.workspace; } + public getWorkspace2(): IWorkspace2 { + return this.workspace ? { id: `${this.workspace.uid}`, roots: [this.workspace.resource] } : void 0; + } + public hasWorkspace(): boolean { return !!this.workspace; } diff --git a/src/vs/platform/workspace/common/workspace.ts b/src/vs/platform/workspace/common/workspace.ts index a1ccdac3e62..8e1b08ed143 100644 --- a/src/vs/platform/workspace/common/workspace.ts +++ b/src/vs/platform/workspace/common/workspace.ts @@ -27,6 +27,17 @@ export interface IWorkspaceContextService { */ getWorkspace(): IWorkspace; + /** + * Provides access to the workspace object the platform is running with. This may be null if the workbench was opened + * without workspace (empty); + */ + getWorkspace2(): IWorkspace2; + + /** + * An event which fires on workspace roots change. + */ + onDidChangeWorkspaceRoots: Event; + /** * Returns iff the provided resource is inside the workspace or not. */ @@ -44,11 +55,6 @@ export interface IWorkspaceContextService { */ toResource: (workspaceRelativePath: string) => URI; - /** - * TODO@Ben multiroot - */ - getFolders(): URI[]; - onDidChangeFolders: Event; } export interface IWorkspace { @@ -72,6 +78,20 @@ export interface IWorkspace { name?: string; } +export interface IWorkspace2 { + + /** + * the unique identifier of the workspace. + */ + readonly id: string; + + /** + * Mutliple roots in this workspace. First entry is master and never changes. + */ + readonly roots: URI[]; + +} + export class Workspace implements IWorkspace { constructor(private _resource: URI, private _uid?: number, private _name?: string) { @@ -105,9 +125,9 @@ export class Workspace implements IWorkspace { return null; } - public toResource(workspaceRelativePath: string): URI { + public toResource(workspaceRelativePath: string, root?: URI): URI { if (typeof workspaceRelativePath === 'string') { - return URI.file(paths.join(this._resource.fsPath, workspaceRelativePath)); + return URI.file(paths.join(root ? root.fsPath : this._resource.fsPath, workspaceRelativePath)); } return null; diff --git a/src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts b/src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts index f7cd4fb7e91..f6fc31c91ef 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts @@ -37,7 +37,7 @@ export class MainThreadWorkspace extends MainThreadWorkspaceShape { ) { super(); this._proxy = threadService.get(ExtHostContext.ExtHostWorkspace); - this._contextService.onDidChangeFolders(this._onDidChangeWorkspace, this, this._toDispose); + this._contextService.onDidChangeWorkspaceRoots(this._onDidChangeWorkspace, this, this._toDispose); } // --- workspace --- diff --git a/src/vs/workbench/services/configuration/node/configuration.ts b/src/vs/workbench/services/configuration/node/configuration.ts index 4f0baaf5ba0..f449677e865 100644 --- a/src/vs/workbench/services/configuration/node/configuration.ts +++ b/src/vs/workbench/services/configuration/node/configuration.ts @@ -17,7 +17,7 @@ import { Schemas } from "vs/base/common/network"; import { RunOnceScheduler } from 'vs/base/common/async'; import { readFile } from 'vs/base/node/pfs'; import * as extfs from 'vs/base/node/extfs'; -import { IWorkspaceContextService, Workspace, IWorkspace } from "vs/platform/workspace/common/workspace"; +import { IWorkspaceContextService, IWorkspace2, Workspace as SingleRootWorkspace, IWorkspace } from "vs/platform/workspace/common/workspace"; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { FileChangeType, FileChangesEvent, isEqual } from 'vs/platform/files/common/files'; import { ConfigModel } from 'vs/platform/configuration/common/model'; @@ -44,14 +44,29 @@ interface IWorkspaceConfiguration { type IWorkspaceFoldersConfiguration = { [rootFolder: string]: { folders: string[]; } }; +class Workspace implements IWorkspace2 { + + constructor(readonly id: string, private _roots: URI[]) { + } + + get roots(): URI[] { + return this._roots; + } + + setRoots(roots: URI[]): void { + this._roots = roots; + } + +} + export class WorkspaceConfigurationService extends Disposable implements IWorkspaceContextService, IWorkspaceConfigurationService { private static RELOAD_CONFIGURATION_DELAY = 50; public _serviceBrand: any; - private readonly _onDidChangeFolders: Emitter = this._register(new Emitter()); - public readonly onDidChangeFolders: Event = this._onDidChangeFolders.event; + private readonly _onDidChangeWorkspaceRoots: Emitter = this._register(new Emitter()); + public readonly onDidChangeWorkspaceRoots: Event = this._onDidChangeWorkspaceRoots.event; private readonly _onDidUpdateConfiguration: Emitter = this._register(new Emitter()); public readonly onDidUpdateConfiguration: Event = this._onDidUpdateConfiguration.event; @@ -65,12 +80,12 @@ export class WorkspaceConfigurationService extends Disposable implements IWorksp private workspaceFilePathToConfiguration: { [relativeWorkspacePath: string]: TPromise> }; private reloadConfigurationScheduler: RunOnceScheduler; - private folders: URI[]; + private readonly workspace: Workspace; - constructor(private environmentService: IEnvironmentService, private workspace?: Workspace, private workspaceSettingsRootFolder: string = WORKSPACE_CONFIG_FOLDER_DEFAULT_NAME) { + constructor(private environmentService: IEnvironmentService, private singleRootWorkspace?: SingleRootWorkspace, private workspaceSettingsRootFolder: string = WORKSPACE_CONFIG_FOLDER_DEFAULT_NAME) { super(); - this.folders = workspace ? [workspace.resource] : []; + this.workspace = singleRootWorkspace ? new Workspace(`${singleRootWorkspace.uid}`, [singleRootWorkspace.resource]) : null; this.workspaceFilePathToConfiguration = Object.create(null); this.cachedConfig = new ConfigModel(null); @@ -95,10 +110,11 @@ export class WorkspaceConfigurationService extends Disposable implements IWorksp } // Resovled configured folders for workspace - let configuredFolders: URI[] = [this.workspace.resource]; + let [master] = this.workspace.roots; + let configuredFolders: URI[] = [master]; const config = this.getConfiguration('workspace'); if (config) { - const workspaceConfig = config[this.workspace.resource.toString()]; + const workspaceConfig = config[master.toString()]; if (workspaceConfig) { const additionalFolders = workspaceConfig.folders .map(f => URI.parse(f)) @@ -112,20 +128,20 @@ export class WorkspaceConfigurationService extends Disposable implements IWorksp configuredFolders = distinct(configuredFolders, r => r.toString()); // Find changes - const changed = !equals(this.folders, configuredFolders, (r1, r2) => r1.toString() === r2.toString()); + const changed = !equals(this.workspace.roots, configuredFolders, (r1, r2) => r1.toString() === r2.toString()); - this.folders = configuredFolders; + this.workspace.setRoots(configuredFolders); if (notify && changed) { - this._onDidChangeFolders.fire(configuredFolders); + this._onDidChangeWorkspaceRoots.fire(configuredFolders); } } - public getFolders(): URI[] { - return this.folders; + public getWorkspace(): IWorkspace { + return this.singleRootWorkspace; } - public getWorkspace(): IWorkspace { + public getWorkspace2(): IWorkspace2 { return this.workspace; } @@ -134,15 +150,15 @@ export class WorkspaceConfigurationService extends Disposable implements IWorksp } public isInsideWorkspace(resource: URI): boolean { - return this.workspace ? this.workspace.isInsideWorkspace(resource) : false; + return this.workspace ? this.singleRootWorkspace.isInsideWorkspace(resource) : false; } public toWorkspaceRelativePath(resource: URI, toOSPath?: boolean): string { - return this.workspace ? this.workspace.toWorkspaceRelativePath(resource, toOSPath) : null; + return this.workspace ? this.singleRootWorkspace.toWorkspaceRelativePath(resource, toOSPath) : null; } public toResource(workspaceRelativePath: string): URI { - return this.workspace ? this.workspace.toResource(workspaceRelativePath) : null; + return this.workspace ? this.singleRootWorkspace.toResource(workspaceRelativePath) : null; } private onBaseConfigurationChanged(event: IConfigurationServiceEvent): void { @@ -276,7 +292,7 @@ export class WorkspaceConfigurationService extends Disposable implements IWorksp // once: when invoked for the first time we fetch json files that contribute settings if (!this.bulkFetchFromWorkspacePromise) { - this.bulkFetchFromWorkspacePromise = resolveStat(this.workspace.toResource(this.workspaceSettingsRootFolder)).then(stat => { + this.bulkFetchFromWorkspacePromise = resolveStat(this.toResource(this.workspaceSettingsRootFolder)).then(stat => { if (!stat.isDirectory) { return TPromise.as([]); } @@ -287,11 +303,11 @@ export class WorkspaceConfigurationService extends Disposable implements IWorksp return false; // only JSON files } - return this.isWorkspaceConfigurationFile(this.workspace.toWorkspaceRelativePath(stat.resource)); // only workspace config files + return this.isWorkspaceConfigurationFile(this.toWorkspaceRelativePath(stat.resource)); // only workspace config files }).map(stat => stat.resource)); }, err => [] /* never fail this call */) .then((contents: IContent[]) => { - contents.forEach(content => this.workspaceFilePathToConfiguration[this.workspace.toWorkspaceRelativePath(content.resource)] = TPromise.as(this.createConfigModel(content))); + contents.forEach(content => this.workspaceFilePathToConfiguration[this.toWorkspaceRelativePath(content.resource)] = TPromise.as(this.createConfigModel(content))); }, errors.onUnexpectedError); } @@ -317,7 +333,7 @@ export class WorkspaceConfigurationService extends Disposable implements IWorksp continue; // only JSON files or the actual settings folder } - const workspacePath = this.workspace.toWorkspaceRelativePath(resource); + const workspacePath = this.toWorkspaceRelativePath(resource); if (!workspacePath) { continue; // event is not inside workspace } @@ -353,7 +369,7 @@ export class WorkspaceConfigurationService extends Disposable implements IWorksp } private createConfigModel(content: IContent): IConfigModel { - const path = this.workspace.toWorkspaceRelativePath(content.resource); + const path = this.toWorkspaceRelativePath(content.resource); if (path === WORKSPACE_CONFIG_DEFAULT_PATH) { return new WorkspaceSettingsConfigModel(content.value, content.resource.toString()); } else { diff --git a/src/vs/workbench/test/workbenchTestServices.ts b/src/vs/workbench/test/workbenchTestServices.ts index c280cd4c979..dc3c013b404 100644 --- a/src/vs/workbench/test/workbenchTestServices.ts +++ b/src/vs/workbench/test/workbenchTestServices.ts @@ -27,7 +27,7 @@ import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { IEditorInput, IEditorOptions, Position, Direction, IEditor, IResourceInput } from 'vs/platform/editor/common/editor'; import { IUntitledEditorService, UntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; import { IMessageService, IConfirmation } from 'vs/platform/message/common/message'; -import { IWorkspace, IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IWorkspace, IWorkspaceContextService, IWorkspace2 } from 'vs/platform/workspace/common/workspace'; import { ILifecycleService, ShutdownEvent, ShutdownReason, StartupKind, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { EditorStacksModel } from 'vs/workbench/common/editor/editorStacksModel'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; @@ -66,16 +66,16 @@ export class TestContextService implements IWorkspaceContextService { private workspace: any; private options: any; - private _onDidChangeFolders: Emitter; + private _onDidChangeWorkspaceRoots: Emitter; constructor(workspace: any = TestWorkspace, options: any = null) { this.workspace = workspace; this.options = options || Object.create(null); - this._onDidChangeFolders = new Emitter(); + this._onDidChangeWorkspaceRoots = new Emitter(); } - public get onDidChangeFolders(): Event { - return this._onDidChangeFolders.event; + public get onDidChangeWorkspaceRoots(): Event { + return this._onDidChangeWorkspaceRoots.event; } public getFolders(): URI[] { @@ -90,6 +90,10 @@ export class TestContextService implements IWorkspaceContextService { return this.workspace; } + public getWorkspace2(): IWorkspace2 { + return this.workspace ? { id: `${this.workspace.uid}`, roots: [this.workspace.resource] } : void 0; + } + public setWorkspace(workspace: any): void { this.workspace = workspace; }