diff --git a/src/vs/base/common/labels.ts b/src/vs/base/common/labels.ts index 6192b7dcd01..86b6fad0efa 100644 --- a/src/vs/base/common/labels.ts +++ b/src/vs/base/common/labels.ts @@ -18,9 +18,9 @@ export interface ILabelProvider { } export interface IWorkspaceFolderProvider { - getWorkspaceFolder(resource: URI): URI; + getWorkspaceFolder(resource: URI): { uri: URI }; getWorkspace(): { - folders: URI[]; + folders: { uri: URI }[]; }; } @@ -43,14 +43,14 @@ export function getPathLabel(resource: URI | string, rootProvider?: IWorkspaceFo const hasMultipleRoots = rootProvider.getWorkspace().folders.length > 1; let pathLabel: string; - if (isEqual(baseResource.fsPath, resource.fsPath, !platform.isLinux /* ignorecase */)) { + if (isEqual(baseResource.uri.fsPath, resource.fsPath, !platform.isLinux /* ignorecase */)) { pathLabel = ''; // no label if pathes are identical } else { - pathLabel = normalize(ltrim(resource.fsPath.substr(baseResource.fsPath.length), nativeSep), true); + pathLabel = normalize(ltrim(resource.fsPath.substr(baseResource.uri.fsPath.length), nativeSep), true); } if (hasMultipleRoots) { - const rootName = basename(baseResource.fsPath); + const rootName = basename(baseResource.uri.fsPath); pathLabel = pathLabel ? join(rootName, pathLabel) : rootName; // always show root basename if there are multiple } diff --git a/src/vs/editor/standalone/browser/simpleServices.ts b/src/vs/editor/standalone/browser/simpleServices.ts index 3dadc377cef..d3fc4d360bc 100644 --- a/src/vs/editor/standalone/browser/simpleServices.ts +++ b/src/vs/editor/standalone/browser/simpleServices.ts @@ -18,7 +18,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, IWorkspace, WorkbenchState } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, IWorkspace, WorkbenchState, WorkspaceFolder } 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'; @@ -532,7 +532,7 @@ export class SimpleWorkspaceContextService implements IWorkspaceContextService { constructor() { const resource = URI.from({ scheme: SimpleWorkspaceContextService.SCHEME, authority: 'model', path: '/' }); - this.workspace = { id: '4064f6ec-cb38-4ad0-af64-ee6467e63c82', folders: [resource], name: resource.fsPath }; + this.workspace = { id: '4064f6ec-cb38-4ad0-af64-ee6467e63c82', folders: [{ uri: resource, raw: resource.toString(), name: '', index: 0, }], name: resource.fsPath }; } public getWorkspace(): IWorkspace { @@ -549,7 +549,7 @@ export class SimpleWorkspaceContextService implements IWorkspaceContextService { return WorkbenchState.EMPTY; } - public getWorkspaceFolder(resource: URI): URI { + public getWorkspaceFolder(resource: URI): WorkspaceFolder { return resource && resource.scheme === SimpleWorkspaceContextService.SCHEME ? this.workspace.folders[0] : void 0; } diff --git a/src/vs/platform/configuration/common/configuration.ts b/src/vs/platform/configuration/common/configuration.ts index 7b9ecd5e500..d541ed739df 100644 --- a/src/vs/platform/configuration/common/configuration.ts +++ b/src/vs/platform/configuration/common/configuration.ts @@ -208,7 +208,6 @@ export class Configuration { private _globalConfiguration: ConfigurationModel; private _workspaceConsolidatedConfiguration: ConfigurationModel; - private _legacyWorkspaceConsolidatedConfiguration: ConfigurationModel; protected _foldersConsolidatedConfigurations: StrictResourceMap>; constructor(protected _defaults: ConfigurationModel, protected _user: ConfigurationModel, protected _workspaceConfiguration: ConfigurationModel = new ConfigurationModel(), protected folders: StrictResourceMap> = new StrictResourceMap>(), protected _workspace?: Workspace) { @@ -226,7 +225,6 @@ export class Configuration { protected merge(): void { this._globalConfiguration = new ConfigurationModel().merge(this._defaults).merge(this._user); this._workspaceConsolidatedConfiguration = new ConfigurationModel().merge(this._globalConfiguration).merge(this._workspaceConfiguration); - this._legacyWorkspaceConsolidatedConfiguration = null; this._foldersConsolidatedConfigurations = new StrictResourceMap>(); for (const folder of this.folders.keys()) { this.mergeFolder(folder); @@ -255,20 +253,6 @@ export class Configuration { }; } - lookupLegacy(key: string): IConfigurationValue { - if (!this._legacyWorkspaceConsolidatedConfiguration) { - this._legacyWorkspaceConsolidatedConfiguration = this._workspace ? new ConfigurationModel().merge(this._workspaceConfiguration).merge(this.folders.get(this._workspace.folders[0])) : null; - } - const consolidateConfigurationModel = this.getConsolidateConfigurationModel({}); - return { - default: objects.clone(getConfigurationValue(this._defaults.contents, key)), - user: objects.clone(getConfigurationValue(this._user.contents, key)), - workspace: objects.clone(this._legacyWorkspaceConsolidatedConfiguration ? getConfigurationValue(this._legacyWorkspaceConsolidatedConfiguration.contents, key) : void 0), - folder: void 0, - value: objects.clone(getConfigurationValue(consolidateConfigurationModel.contents, key)) - }; - } - keys(overrides: IConfigurationOverrides = {}): IConfigurationKeys { const folderConfigurationModel = this.getFolderConfigurationModelForResource(overrides.resource); return { @@ -330,7 +314,7 @@ export class Configuration { return this._workspaceConsolidatedConfiguration; } - return this._foldersConsolidatedConfigurations.get(root) || this._workspaceConsolidatedConfiguration; + return this._foldersConsolidatedConfigurations.get(root.uri) || this._workspaceConsolidatedConfiguration; } private getFolderConfigurationModelForResource(resource: URI): ConfigurationModel { @@ -339,7 +323,7 @@ export class Configuration { } const root = this._workspace.getFolder(resource); - return root ? this.folders.get(root) : null; + return root ? this.folders.get(root.uri) : null; } public toData(): IConfigurationData { diff --git a/src/vs/platform/workspace/common/workspace.ts b/src/vs/platform/workspace/common/workspace.ts index 38f755fc658..7cc1b0405d7 100644 --- a/src/vs/platform/workspace/common/workspace.ts +++ b/src/vs/platform/workspace/common/workspace.ts @@ -5,12 +5,13 @@ 'use strict'; import URI from 'vs/base/common/uri'; +import * as paths from 'vs/base/common/paths'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { TrieMap } from 'vs/base/common/map'; import Event from 'vs/base/common/event'; +import { ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier, IStoredWorkspaceFolder } from 'vs/platform/workspaces/common/workspaces'; +import { coalesce, distinct } from 'vs/base/common/arrays'; import { isLinux } from 'vs/base/common/platform'; -import { distinct } from 'vs/base/common/arrays'; -import { ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; export const IWorkspaceContextService = createDecorator('contextService'); @@ -52,7 +53,7 @@ export interface IWorkspaceContextService { * Returns the folder for the given resource from the workspace. * Can be null if there is no workspace or the resource is not inside the workspace. */ - getWorkspaceFolder(resource: URI): URI; + getWorkspaceFolder(resource: URI): WorkspaceFolder; /** * Return `true` if the current workspace has the given identifier otherwise `false`. @@ -85,7 +86,7 @@ export interface IWorkspace { /** * Folders in the workspace. */ - readonly folders: URI[]; + readonly folders: WorkspaceFolder[]; /** * the location of the workspace configuration @@ -93,31 +94,51 @@ export interface IWorkspace { readonly configuration?: URI; } +export interface WorkspaceFolder { + + /** + * The associated URI for this workspace folder. + */ + readonly uri: URI; + + /** + * The name of this workspace folder. Defaults to + * the basename its [uri-path](#Uri.path) + */ + readonly name: string; + + /** + * The ordinal number of this workspace folder. + */ + readonly index: number; + + /** + * The raw path of this workspace folder + */ + readonly raw: string; +} + export class Workspace implements IWorkspace { - private _foldersMap: TrieMap = new TrieMap(); - private _folders: URI[]; + private _foldersMap: TrieMap = new TrieMap(); + private _folders: WorkspaceFolder[]; constructor( public readonly id: string, private _name: string, - folders: URI[], + folders: WorkspaceFolder[], private _configuration: URI = null, public readonly ctime?: number ) { this.folders = folders; } - private ensureUnique(folders: URI[]): URI[] { - return distinct(folders, folder => isLinux ? folder.fsPath : folder.fsPath.toLowerCase()); - } - - public get folders(): URI[] { + public get folders(): WorkspaceFolder[] { return this._folders; } - public set folders(folders: URI[]) { - this._folders = this.ensureUnique(folders); + public set folders(folders: WorkspaceFolder[]) { + this._folders = folders; this.updateFoldersMap(); } @@ -137,7 +158,7 @@ export class Workspace implements IWorkspace { this._configuration = configuration; } - public getFolder(resource: URI): URI { + public getFolder(resource: URI): WorkspaceFolder { if (!resource) { return null; } @@ -146,9 +167,9 @@ export class Workspace implements IWorkspace { } private updateFoldersMap(): void { - this._foldersMap = new TrieMap(); + this._foldersMap = new TrieMap(); for (const folder of this.folders) { - this._foldersMap.insert(folder.fsPath, folder); + this._foldersMap.insert(folder.uri.fsPath, folder); } } @@ -156,3 +177,35 @@ export class Workspace implements IWorkspace { return { id: this.id, folders: this.folders, name: this.name }; } } + +export function toWorkspaceFolders(configuredFolders: IStoredWorkspaceFolder[], relativeTo?: URI): WorkspaceFolder[] { + let workspaceFolders = parseWorkspaceFolders(configuredFolders, relativeTo); + return ensureUnique(coalesce(workspaceFolders)) + .map(({ uri, raw, name }, index) => ({ uri, raw, name: name || paths.basename(uri.fsPath), index })); +} + +function parseWorkspaceFolders(configuredFolders: IStoredWorkspaceFolder[], relativeTo: URI): WorkspaceFolder[] { + return configuredFolders.map(({ path, name }, index) => { + const uri = toUri(path, relativeTo); + if (!uri) { + return void 0; + } + return { uri, raw: path, index, name }; + }); +} + +function toUri(path: string, relativeTo: URI): URI { + if (path) { + if (paths.isAbsolute(path)) { + return URI.file(path); + } + if (relativeTo) { + return URI.file(paths.join(relativeTo.fsPath, path)); + } + } + return null; +} + +function ensureUnique(folders: WorkspaceFolder[]): WorkspaceFolder[] { + return distinct(folders, folder => isLinux ? folder.uri.fsPath : folder.uri.fsPath.toLowerCase()); +} diff --git a/src/vs/platform/workspace/test/common/testWorkspace.ts b/src/vs/platform/workspace/test/common/testWorkspace.ts index 97c90a65d03..6a204873e0c 100644 --- a/src/vs/platform/workspace/test/common/testWorkspace.ts +++ b/src/vs/platform/workspace/test/common/testWorkspace.ts @@ -4,15 +4,16 @@ *--------------------------------------------------------------------------------------------*/ import URI from 'vs/base/common/uri'; -import { Workspace } from 'vs/platform/workspace/common/workspace'; +import { Workspace, toWorkspaceFolders } from 'vs/platform/workspace/common/workspace'; +import { isWindows } from 'vs/base/common/platform'; -const wsUri = URI.file('C:\\testWorkspace'); +const wsUri = URI.file(isWindows ? 'C:\\testWorkspace' : '/testWorkspace'); export const TestWorkspace = testWorkspace(wsUri); export function testWorkspace(resource: URI): Workspace { return new Workspace( resource.toString(), resource.fsPath, - [resource] + toWorkspaceFolders([{ path: resource.fsPath }]) ); } \ No newline at end of file diff --git a/src/vs/platform/workspace/test/common/workspace.test.ts b/src/vs/platform/workspace/test/common/workspace.test.ts index dcfdff48e38..c5e5e5662b7 100644 --- a/src/vs/platform/workspace/test/common/workspace.test.ts +++ b/src/vs/platform/workspace/test/common/workspace.test.ts @@ -6,17 +6,193 @@ 'use strict'; import * as assert from 'assert'; -import { Workspace } from 'vs/platform/workspace/common/workspace'; +import { Workspace, WorkspaceFolder, toWorkspaceFolders } from 'vs/platform/workspace/common/workspace'; import URI from 'vs/base/common/uri'; suite('Workspace', () => { - test('Workspace ensures unique roots', () => { + test('getFolder returns the folder with given uri', () => { + const expected = aWorkspaceFolder(URI.file('/src/test')); + let testObject = new Workspace('', '', [aWorkspaceFolder(URI.file('/src/main')), expected, aWorkspaceFolder(URI.file('/src/code'))]); - // Unique - let roots = [URI.file('/some/path'), URI.file('/some/path')]; - let ws = new Workspace('id', 'name', roots, URI.file('/config')); + const actual = testObject.getFolder(expected.uri); - assert.equal(ws.folders.length, 1); + assert.equal(actual, expected); }); + + test('getFolder returns the folder if the uri is sub', () => { + const expected = aWorkspaceFolder(URI.file('/src/test')); + let testObject = new Workspace('', '', [expected, aWorkspaceFolder(URI.file('/src/main')), aWorkspaceFolder(URI.file('/src/code'))]); + + const actual = testObject.getFolder(URI.file('/src/test/a')); + + assert.equal(actual, expected); + }); + + test('getFolder returns the closest folder if the uri is sub', () => { + const expected = aWorkspaceFolder(URI.file('/src/test')); + let testObject = new Workspace('', '', [aWorkspaceFolder(URI.file('/src/code')), aWorkspaceFolder(URI.file('/src')), expected]); + + const actual = testObject.getFolder(URI.file('/src/test/a')); + + assert.equal(actual, expected); + }); + + test('getFolder returns null if the uri is not sub', () => { + let testObject = new Workspace('', '', [aWorkspaceFolder(URI.file('/src/code')), aWorkspaceFolder(URI.file('/src/test'))]); + + const actual = testObject.getFolder(URI.file('/src/main/a')); + + assert.equal(actual, undefined); + }); + + test('toWorkspaceFolders with single absolute folder', () => { + const actual = toWorkspaceFolders([{ path: '/src/test' }]); + + assert.equal(actual.length, 1); + assert.equal(actual[0].uri.fsPath, '/src/test'); + assert.equal(actual[0].raw, '/src/test'); + assert.equal(actual[0].index, 0); + assert.equal(actual[0].name, 'test'); + }); + + test('toWorkspaceFolders with single relative folder', () => { + const actual = toWorkspaceFolders([{ path: './test' }], URI.file('src')); + + assert.equal(actual.length, 1); + assert.equal(actual[0].uri.fsPath, '/src/test'); + assert.equal(actual[0].raw, './test'); + assert.equal(actual[0].index, 0); + assert.equal(actual[0].name, 'test'); + }); + + test('toWorkspaceFolders with single absolute folder with name', () => { + const actual = toWorkspaceFolders([{ path: '/src/test', name: 'hello' }]); + + assert.equal(actual.length, 1); + assert.equal(actual[0].uri.fsPath, '/src/test'); + assert.equal(actual[0].raw, '/src/test'); + assert.equal(actual[0].index, 0); + assert.equal(actual[0].name, 'hello'); + }); + + test('toWorkspaceFolders with multiple unique absolute folders', () => { + const actual = toWorkspaceFolders([{ path: '/src/test2' }, { path: '/src/test3' }, { path: '/src/test1' }]); + + assert.equal(actual.length, 3); + assert.equal(actual[0].uri.fsPath, '/src/test2'); + assert.equal(actual[0].raw, '/src/test2'); + assert.equal(actual[0].index, 0); + assert.equal(actual[0].name, 'test2'); + + assert.equal(actual[1].uri.fsPath, '/src/test3'); + assert.equal(actual[1].raw, '/src/test3'); + assert.equal(actual[1].index, 1); + assert.equal(actual[1].name, 'test3'); + + assert.equal(actual[2].uri.fsPath, '/src/test1'); + assert.equal(actual[2].raw, '/src/test1'); + assert.equal(actual[2].index, 2); + assert.equal(actual[2].name, 'test1'); + }); + + test('toWorkspaceFolders with multiple unique absolute folders with names', () => { + const actual = toWorkspaceFolders([{ path: '/src/test2' }, { path: '/src/test3', name: 'noName' }, { path: '/src/test1' }]); + + assert.equal(actual.length, 3); + assert.equal(actual[0].uri.fsPath, '/src/test2'); + assert.equal(actual[0].raw, '/src/test2'); + assert.equal(actual[0].index, 0); + assert.equal(actual[0].name, 'test2'); + + assert.equal(actual[1].uri.fsPath, '/src/test3'); + assert.equal(actual[1].raw, '/src/test3'); + assert.equal(actual[1].index, 1); + assert.equal(actual[1].name, 'noName'); + + assert.equal(actual[2].uri.fsPath, '/src/test1'); + assert.equal(actual[2].raw, '/src/test1'); + assert.equal(actual[2].index, 2); + assert.equal(actual[2].name, 'test1'); + }); + + test('toWorkspaceFolders with multiple unique absolute and relative folders', () => { + const actual = toWorkspaceFolders([{ path: '/src/test2' }, { path: '/abc/test3', name: 'noName' }, { path: './test1' }], URI.file('src')); + + assert.equal(actual.length, 3); + assert.equal(actual[0].uri.fsPath, '/src/test2'); + assert.equal(actual[0].raw, '/src/test2'); + assert.equal(actual[0].index, 0); + assert.equal(actual[0].name, 'test2'); + + assert.equal(actual[1].uri.fsPath, '/abc/test3'); + assert.equal(actual[1].raw, '/abc/test3'); + assert.equal(actual[1].index, 1); + assert.equal(actual[1].name, 'noName'); + + assert.equal(actual[2].uri.fsPath, '/src/test1'); + assert.equal(actual[2].raw, './test1'); + assert.equal(actual[2].index, 2); + assert.equal(actual[2].name, 'test1'); + }); + + test('toWorkspaceFolders with multiple absolute folders with duplicates', () => { + const actual = toWorkspaceFolders([{ path: '/src/test2' }, { path: '/src/test2', name: 'noName' }, { path: '/src/test1' }]); + + assert.equal(actual.length, 2); + assert.equal(actual[0].uri.fsPath, '/src/test2'); + assert.equal(actual[0].raw, '/src/test2'); + assert.equal(actual[0].index, 0); + assert.equal(actual[0].name, 'test2'); + + assert.equal(actual[1].uri.fsPath, '/src/test1'); + assert.equal(actual[1].raw, '/src/test1'); + assert.equal(actual[1].index, 1); + assert.equal(actual[1].name, 'test1'); + }); + + test('toWorkspaceFolders with multiple absolute and relative folders with duplicates', () => { + const actual = toWorkspaceFolders([{ path: '/src/test2' }, { path: '/src/test3', name: 'noName' }, { path: './test3' }, { path: '/abc/test1' }], URI.file('src')); + + assert.equal(actual.length, 3); + assert.equal(actual[0].uri.fsPath, '/src/test2'); + assert.equal(actual[0].raw, '/src/test2'); + assert.equal(actual[0].index, 0); + assert.equal(actual[0].name, 'test2'); + + assert.equal(actual[1].uri.fsPath, '/src/test3'); + assert.equal(actual[1].raw, '/src/test3'); + assert.equal(actual[1].index, 1); + assert.equal(actual[1].name, 'noName'); + + assert.equal(actual[2].uri.fsPath, '/abc/test1'); + assert.equal(actual[2].raw, '/abc/test1'); + assert.equal(actual[2].index, 2); + assert.equal(actual[2].name, 'test1'); + }); + + test('toWorkspaceFolders with multiple absolute and relative folders with invalid paths', () => { + const actual = toWorkspaceFolders([{ path: '/src/test2' }, { path: '', name: 'noName' }, { path: './test3' }, { path: '/abc/test1' }], URI.file('src')); + + assert.equal(actual.length, 3); + assert.equal(actual[0].uri.fsPath, '/src/test2'); + assert.equal(actual[0].raw, '/src/test2'); + assert.equal(actual[0].index, 0); + assert.equal(actual[0].name, 'test2'); + + assert.equal(actual[1].uri.fsPath, '/src/test3'); + assert.equal(actual[1].raw, './test3'); + assert.equal(actual[1].index, 1); + assert.equal(actual[1].name, 'test3'); + + assert.equal(actual[2].uri.fsPath, '/abc/test1'); + assert.equal(actual[2].raw, '/abc/test1'); + assert.equal(actual[2].index, 2); + assert.equal(actual[2].name, 'test1'); + }); + + function aWorkspaceFolder(uri: URI, index: number = 0): WorkspaceFolder { + return { uri, raw: uri.fsPath, index, name: '' }; + } + }); \ No newline at end of file diff --git a/src/vs/platform/workspaces/common/workspaces.ts b/src/vs/platform/workspaces/common/workspaces.ts index 1702cd61757..44d42a64d14 100644 --- a/src/vs/platform/workspaces/common/workspaces.ts +++ b/src/vs/platform/workspaces/common/workspaces.ts @@ -34,6 +34,7 @@ export interface IWorkspaceIdentifier { export interface IStoredWorkspaceFolder { path: string; + name?: string; } export interface IStoredWorkspace { diff --git a/src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts b/src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts index fd9eef3cbef..70675aed006 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts @@ -66,7 +66,7 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape { return undefined; } const query: ISearchQuery = { - folderQueries: workspace.folders.map(root => ({ folder: root })), + folderQueries: workspace.folders.map(folder => ({ folder: folder.uri })), type: QueryType.File, maxResults, includePattern: { [include]: true }, diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index 1f461211f6d..2a77558ba23 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -48,6 +48,7 @@ import { ITreeItem } from 'vs/workbench/common/views'; import { ThemeColor } from 'vs/platform/theme/common/themeService'; import { IDisposable } from 'vs/base/common/lifecycle'; import { SerializedError } from 'vs/base/common/errors'; +import { WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; export interface IEnvironment { isExtensionDevelopmentDebug: boolean; @@ -64,7 +65,7 @@ export interface IEnvironment { export interface IWorkspaceData { id: string; name: string; - folders: URI[]; + folders: WorkspaceFolder[]; } export interface IInitData { diff --git a/src/vs/workbench/api/node/extHostWorkspace.ts b/src/vs/workbench/api/node/extHostWorkspace.ts index 219bfac0312..43323d5c00c 100644 --- a/src/vs/workbench/api/node/extHostWorkspace.ts +++ b/src/vs/workbench/api/node/extHostWorkspace.ts @@ -8,7 +8,7 @@ import URI from 'vs/base/common/uri'; import Event, { Emitter } from 'vs/base/common/event'; import { normalize } from 'vs/base/common/paths'; import { delta } from 'vs/base/common/arrays'; -import { relative, basename } from 'path'; +import { relative } from 'path'; import { Workspace } from 'vs/platform/workspace/common/workspace'; import { IResourceEdit } from 'vs/editor/common/services/bulkEdit'; import { TPromise } from 'vs/base/common/winjs.base'; @@ -28,26 +28,22 @@ class Workspace2 extends Workspace { return data ? new Workspace2(data) : null; } - private readonly _folder: vscode.WorkspaceFolder[] = []; + private readonly _workspaceFolders: vscode.WorkspaceFolder[] = []; private readonly _structure = new TrieMap(s => s.split('/')); private constructor(data: IWorkspaceData) { super(data.id, data.name, data.folders); // setup the workspace folder data structure - this.folders.forEach((uri, index) => { - const folder = { - name: basename(uri.fsPath), - uri, - index - }; - this._folder.push(folder); - this._structure.insert(folder.uri.toString(), folder); + this.folders.forEach(({ name, uri, index }) => { + const workspaceFolder = { name, uri, index }; + this._workspaceFolders.push(workspaceFolder); + this._structure.insert(workspaceFolder.uri.toString(), workspaceFolder); }); } get workspaceFolders(): vscode.WorkspaceFolder[] { - return this._folder.slice(0); + return this._workspaceFolders.slice(0); } getWorkspaceFolder(uri: URI): vscode.WorkspaceFolder { @@ -114,7 +110,7 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape { if (folders.length === 0) { return undefined; } - return folders[0].fsPath; + return folders[0].uri.fsPath; } getRelativePath(pathOrUri: string | vscode.Uri, includeWorkspace?: boolean): string { diff --git a/src/vs/workbench/browser/actions/workspaceActions.ts b/src/vs/workbench/browser/actions/workspaceActions.ts index 525931079e0..00904c302f0 100644 --- a/src/vs/workbench/browser/actions/workspaceActions.ts +++ b/src/vs/workbench/browser/actions/workspaceActions.ts @@ -77,7 +77,7 @@ export abstract class BaseWorkspacesAction extends Action { let defaultPath: string; const workspace = this.contextService.getWorkspace(); if (workspace.folders.length > 0) { - defaultPath = dirname(workspace.folders[0].fsPath); // pick the parent of the first root by default + defaultPath = dirname(workspace.folders[0].uri.fsPath); // pick the parent of the first root by default } return this.windowService.showOpenDialog({ @@ -117,7 +117,7 @@ export class AddRootFolderAction extends BaseWorkspacesAction { return this.viewletService.openViewlet(this.viewletService.getDefaultViewletId(), true); }); } - return this.instantiationService.createInstance(NewWorkspaceAction, NewWorkspaceAction.ID, NewWorkspaceAction.LABEL, this.contextService.getWorkspace().folders).run(); + return this.instantiationService.createInstance(NewWorkspaceAction, NewWorkspaceAction.ID, NewWorkspaceAction.LABEL, this.contextService.getWorkspace().folders.map(folder => folder.uri)).run(); } } @@ -204,7 +204,7 @@ export class SaveWorkspaceAsAction extends BaseWorkspacesAction { switch (workspaceState) { case WorkbenchState.FOLDER: - const workspaceFolders = this.contextService.getWorkspace().folders.map(root => root.fsPath); + const workspaceFolders = this.contextService.getWorkspace().folders.map(root => root.uri.fsPath); return this.windowService.createAndOpenWorkspace(workspaceFolders, configPath); case WorkbenchState.WORKSPACE: @@ -221,7 +221,7 @@ export class SaveWorkspaceAsAction extends BaseWorkspacesAction { if (workspace.configuration && !this.isUntitledWorkspace(workspace.configuration.fsPath)) { defaultPath = workspace.configuration.fsPath; } else if (workspace.folders.length > 0) { - defaultPath = dirname(workspace.folders[0].fsPath); // pick the parent of the first root by default + defaultPath = dirname(workspace.folders[0].uri.fsPath); // pick the parent of the first root by default } return this.windowService.showSaveDialog({ diff --git a/src/vs/workbench/browser/labels.ts b/src/vs/workbench/browser/labels.ts index 66922084d46..c0a79c5d922 100644 --- a/src/vs/workbench/browser/labels.ts +++ b/src/vs/workbench/browser/labels.ts @@ -212,8 +212,8 @@ export class FileLabel extends ResourceLabel { public setFile(resource: uri, options?: IFileLabelOptions): void { const hidePath = (options && options.hidePath) || (resource.scheme === Schemas.untitled && !this.untitledEditorService.hasAssociatedFilePath(resource)); const rootProvider: IWorkspaceFolderProvider = (options && options.root) ? { - getWorkspaceFolder(): uri { return options.root; }, - getWorkspace(): { folders: uri[]; } { return { folders: [options.root] }; }, + getWorkspaceFolder(): { uri: uri } { return { uri: options.root }; }, + getWorkspace(): { folders: { uri: uri }[]; } { return { folders: [{ uri: options.root }] }; }, } : this.contextService; this.setLabel({ diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts index 683183e95a3..3f7d2cee441 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts @@ -31,7 +31,6 @@ import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/ import { Verbosity } from 'vs/platform/editor/common/editor'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { TITLE_BAR_ACTIVE_BACKGROUND, TITLE_BAR_ACTIVE_FOREGROUND, TITLE_BAR_INACTIVE_FOREGROUND, TITLE_BAR_INACTIVE_BACKGROUND, TITLE_BAR_BORDER } from 'vs/workbench/common/theme'; -import URI from 'vs/base/common/uri'; import { IPartService } from 'vs/workbench/services/part/common/partService'; export class TitlebarPart extends Part implements ITitleService { @@ -192,19 +191,26 @@ export class TitlebarPart extends Part implements ITitleService { const input = this.editorService.getActiveEditorInput(); const workspace = this.contextService.getWorkspace(); + let root; + if (workspace.configuration) { + root = workspace.configuration; + } else if (workspace.folders.length) { + root = workspace.folders[0].uri; + } + // Compute folder resource // Single Root Workspace: always the root single workspace in this case // Otherwise: root folder of the currently active file if any - let folder: URI = this.contextService.getWorkbenchState() === WorkbenchState.FOLDER ? workspace.folders[0] : this.contextService.getWorkspaceFolder(toResource(input, { supportSideBySide: true, filter: 'file' })); + let folder = this.contextService.getWorkbenchState() === WorkbenchState.FOLDER ? workspace.folders[0] : this.contextService.getWorkspaceFolder(toResource(input, { supportSideBySide: true, filter: 'file' })); // Variables const activeEditorShort = input ? input.getTitle(Verbosity.SHORT) : ''; const activeEditorMedium = input ? input.getTitle(Verbosity.MEDIUM) : activeEditorShort; const activeEditorLong = input ? input.getTitle(Verbosity.LONG) : activeEditorMedium; const rootName = workspace.name; - const rootPath = labels.getPathLabel(workspace.configuration || workspace.folders[0], void 0, this.environmentService) || ''; - const folderName = folder ? (paths.basename(folder.fsPath) || folder.fsPath) : ''; - const folderPath = folder ? labels.getPathLabel(folder, void 0, this.environmentService) : ''; + const rootPath = root ? labels.getPathLabel(root, void 0, this.environmentService) : ''; + const folderName = folder ? (paths.basename(folder.uri.fsPath) || folder.uri.fsPath) : ''; + const folderPath = folder ? labels.getPathLabel(folder.uri, void 0, this.environmentService) : ''; const dirty = input && input.isDirty() ? TitlebarPart.TITLE_DIRTY : ''; const appName = this.environmentService.appNameLong; const separator = TitlebarPart.TITLE_SEPARATOR; diff --git a/src/vs/workbench/common/resources.ts b/src/vs/workbench/common/resources.ts index d8ae21d82d3..ee29aa9b7a2 100644 --- a/src/vs/workbench/common/resources.ts +++ b/src/vs/workbench/common/resources.ts @@ -107,12 +107,12 @@ export class ResourceGlobMatcher { // Add excludes per workspaces that got added this.contextService.getWorkspace().folders.forEach(folder => { - const rootExcludes = this.globFn(folder); - if (!this.mapRootToExpressionConfig.has(folder.toString()) || !objects.equals(this.mapRootToExpressionConfig.get(folder.toString()), rootExcludes)) { + const rootExcludes = this.globFn(folder.uri); + if (!this.mapRootToExpressionConfig.has(folder.uri.toString()) || !objects.equals(this.mapRootToExpressionConfig.get(folder.uri.toString()), rootExcludes)) { changed = true; - this.mapRootToParsedExpression.set(folder.toString(), this.parseFn(rootExcludes)); - this.mapRootToExpressionConfig.set(folder.toString(), objects.clone(rootExcludes)); + this.mapRootToParsedExpression.set(folder.uri.toString(), this.parseFn(rootExcludes)); + this.mapRootToExpressionConfig.set(folder.uri.toString(), objects.clone(rootExcludes)); } }); @@ -145,11 +145,11 @@ export class ResourceGlobMatcher { } public matches(resource: URI): boolean { - const root = this.contextService.getWorkspaceFolder(resource); + const folder = this.contextService.getWorkspaceFolder(resource); let expressionForRoot: ParsedExpression; - if (root && this.mapRootToParsedExpression.has(root.toString())) { - expressionForRoot = this.mapRootToParsedExpression.get(root.toString()); + if (folder && this.mapRootToParsedExpression.has(folder.uri.toString())) { + expressionForRoot = this.mapRootToParsedExpression.get(folder.uri.toString()); } else { expressionForRoot = this.mapRootToParsedExpression.get(ResourceGlobMatcher.NO_ROOT); } @@ -159,8 +159,8 @@ export class ResourceGlobMatcher { // a glob pattern of "src/**" will not match on an absolute path "/folder/src/file.txt" // but can match on "src/file.txt" let resourcePathToMatch: string; - if (root) { - resourcePathToMatch = paths.normalize(paths.relative(root.fsPath, resource.fsPath)); + if (folder) { + resourcePathToMatch = paths.normalize(paths.relative(folder.uri.fsPath, resource.fsPath)); } else { resourcePathToMatch = resource.fsPath; } diff --git a/src/vs/workbench/electron-browser/main.ts b/src/vs/workbench/electron-browser/main.ts index 70cbf4e7b09..08bead07081 100644 --- a/src/vs/workbench/electron-browser/main.ts +++ b/src/vs/workbench/electron-browser/main.ts @@ -162,7 +162,7 @@ function createStorageService(workspaceService: IWorkspaceContextService, enviro // the ctime is used as secondary workspace id to clean up stale UI state if necessary case WorkbenchState.FOLDER: const workspace: Workspace = workspaceService.getWorkspace(); - workspaceId = workspace.folders[0].toString(); + workspaceId = workspace.folders[0].uri.toString(); secondaryWorkspaceId = workspace.ctime; break; diff --git a/src/vs/workbench/electron-browser/window.ts b/src/vs/workbench/electron-browser/window.ts index e3307c35999..c0802c9b261 100644 --- a/src/vs/workbench/electron-browser/window.ts +++ b/src/vs/workbench/electron-browser/window.ts @@ -294,7 +294,7 @@ export class ElectronWindow extends Themable { // Single folder or no workspace: create workspace and open else { - const workspaceFolders: URI[] = [...this.contextService.getWorkspace().folders]; + const workspaceFolders: URI[] = [...this.contextService.getWorkspace().folders.map(folder => folder.uri)]; // Fill in remaining ones from request workspaceFolders.push(...request.foldersToAdd.map(folderToAdd => URI.file(folderToAdd.filePath))); diff --git a/src/vs/workbench/node/extensionHostMain.ts b/src/vs/workbench/node/extensionHostMain.ts index 9bd3d58ff3d..ccd25d7eadb 100644 --- a/src/vs/workbench/node/extensionHostMain.ts +++ b/src/vs/workbench/node/extensionHostMain.ts @@ -177,7 +177,7 @@ export class ExtensionHostMain { } const query: ISearchQuery = { - folderQueries: this._workspace.folders.map(root => ({ folder: root })), + folderQueries: this._workspace.folders.map(folder => ({ folder: folder.uri })), type: QueryType.File, maxResults: 1, includePattern: { [p]: true } @@ -187,8 +187,8 @@ export class ExtensionHostMain { } else { // find exact path return (async resolve => { - for (const { fsPath } of this._workspace.folders) { - if (await pfs.exists(join(fsPath, p))) { + for (const { uri } of this._workspace.folders) { + if (await pfs.exists(join(uri.fsPath, p))) { return p; } } diff --git a/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.ts b/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.ts index 5aa8b1e8f0a..cf5485f50eb 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.ts @@ -353,7 +353,7 @@ export class ConfigurationManager implements IConfigurationManager { } private initLaunches(): void { - this.launches = this.contextService.getWorkspace().folders.map(folder => this.instantiationService.createInstance(Launch, this, folder)); + this.launches = this.contextService.getWorkspace().folders.map(folder => this.instantiationService.createInstance(Launch, this, folder.uri)); if (this.launches.indexOf(this._selectedLaunch) === -1) { this._selectedLaunch = undefined; } diff --git a/src/vs/workbench/parts/extensions/browser/extensionsActions.ts b/src/vs/workbench/parts/extensions/browser/extensionsActions.ts index 51d6dc2eb26..b24f51d072f 100644 --- a/src/vs/workbench/parts/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/parts/extensions/browser/extensionsActions.ts @@ -1276,7 +1276,7 @@ export class ConfigureWorkspaceRecommendedExtensionsAction extends Action { } private getOrCreateExtensionsFile(): TPromise<{ created: boolean, extensionsFileResource: URI }> { - const extensionsFileResource = URI.file(paths.join(this.contextService.getWorkspace().folders[0].fsPath, '.vscode', 'extensions.json')); // TODO@Sandeep (https://github.com/Microsoft/vscode/issues/29242) + const extensionsFileResource = URI.file(paths.join(this.contextService.getWorkspace().folders[0].uri.fsPath, '.vscode', 'extensions.json')); // TODO@Sandeep (https://github.com/Microsoft/vscode/issues/29242) return this.fileService.resolveContent(extensionsFileResource).then(content => { return { created: false, extensionsFileResource }; diff --git a/src/vs/workbench/parts/files/browser/views/explorerView.ts b/src/vs/workbench/parts/files/browser/views/explorerView.ts index 85fe8e72a8a..64600a0a28f 100644 --- a/src/vs/workbench/parts/files/browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/browser/views/explorerView.ts @@ -133,7 +133,7 @@ export class ExplorerView extends CollapsibleView { const titleSpan = $('span').appendTo(titleDiv); const setHeader = () => { const workspace = this.contextService.getWorkspace(); - const title = workspace.folders.map(folder => labels.getPathLabel(folder.fsPath, void 0, this.environmentService)).join(); + const title = workspace.folders.map(folder => labels.getPathLabel(folder.uri.fsPath, void 0, this.environmentService)).join(); titleSpan.text(this.name).title(title); }; this.toDispose.push(this.contextService.onDidChangeWorkspaceName(() => setHeader())); @@ -737,7 +737,7 @@ export class ExplorerView extends CollapsibleView { if (activeFile) { const workspaceFolder = this.contextService.getWorkspaceFolder(activeFile); if (workspaceFolder) { - const found = targetsToResolve.filter(t => t.root.resource.toString() === workspaceFolder.toString()).pop(); + const found = targetsToResolve.filter(t => t.root.resource.toString() === workspaceFolder.uri.toString()).pop(); found.options.resolveTo.push(activeFile); } } @@ -745,7 +745,7 @@ export class ExplorerView extends CollapsibleView { targetsToExpand.forEach(toExpand => { const workspaceFolder = this.contextService.getWorkspaceFolder(toExpand); if (workspaceFolder) { - const found = targetsToResolve.filter(ttr => ttr.resource.toString() === workspaceFolder.toString()).pop(); + const found = targetsToResolve.filter(ttr => ttr.resource.toString() === workspaceFolder.uri.toString()).pop(); found.options.resolveTo.push(toExpand); } }); @@ -855,7 +855,8 @@ export class ExplorerView extends CollapsibleView { // Stat needs to be resolved first and then revealed const options: IResolveFileOptions = { resolveTo: [resource] }; - const rootUri = this.contextService.getWorkspaceFolder(resource) || this.model.roots[0].resource; + const workspaceFolder = this.contextService.getWorkspaceFolder(resource); + const rootUri = workspaceFolder ? workspaceFolder.uri : this.model.roots[0].resource; return this.fileService.resolveFile(rootUri, options).then(stat => { // Convert to model diff --git a/src/vs/workbench/parts/files/browser/views/explorerViewer.ts b/src/vs/workbench/parts/files/browser/views/explorerViewer.ts index 5061faf54cf..f36ef2b0db2 100644 --- a/src/vs/workbench/parts/files/browser/views/explorerViewer.ts +++ b/src/vs/workbench/parts/files/browser/views/explorerViewer.ts @@ -572,8 +572,7 @@ export class FileSorter implements ISorter { // Do not sort roots if (statA.isRoot) { if (statB.isRoot) { - const ws = this.contextService.getWorkspace(); - return ws.folders.indexOf(statA.resource) - ws.folders.indexOf(statB.resource); + return this.contextService.getWorkspaceFolder(statA.resource).index - this.contextService.getWorkspaceFolder(statB.resource).index; } return -1; } @@ -666,10 +665,10 @@ export class FileFilter implements IFilter { public updateConfiguration(): boolean { let needsRefresh = false; this.contextService.getWorkspace().folders.forEach(folder => { - const configuration = this.configurationService.getConfiguration(undefined, { resource: folder }); + const configuration = this.configurationService.getConfiguration(undefined, { resource: folder.uri }); const excludesConfig = (configuration && configuration.files && configuration.files.exclude) || Object.create(null); - needsRefresh = needsRefresh || !objects.equals(this.hiddenExpressionPerRoot.get(folder.toString()), excludesConfig); - this.hiddenExpressionPerRoot.set(folder.toString(), objects.clone(excludesConfig)); // do not keep the config, as it gets mutated under our hoods + needsRefresh = needsRefresh || !objects.equals(this.hiddenExpressionPerRoot.get(folder.uri.toString()), excludesConfig); + this.hiddenExpressionPerRoot.set(folder.uri.toString(), objects.clone(excludesConfig)); // do not keep the config, as it gets mutated under our hoods }); return needsRefresh; @@ -843,7 +842,7 @@ export class FileDragAndDrop extends SimpleFileResourceDragAndDrop { return fromDesktop || isCopy ? DRAG_OVER_ACCEPT_BUBBLE_DOWN_COPY(true) : DRAG_OVER_ACCEPT_BUBBLE_DOWN(true); } - if (this.contextService.getWorkspace().folders.every(r => r.toString() !== target.resource.toString())) { + if (this.contextService.getWorkspace().folders.every(folder => folder.uri.toString() !== target.resource.toString())) { return fromDesktop || isCopy ? DRAG_OVER_ACCEPT_BUBBLE_UP_COPY : DRAG_OVER_ACCEPT_BUBBLE_UP; } } @@ -899,7 +898,7 @@ export class FileDragAndDrop extends SimpleFileResourceDragAndDrop { }); if (result) { - const currentFolders = this.contextService.getWorkspace().folders; + const currentFolders = this.contextService.getWorkspace().folders.map(folder => folder.uri); const newRoots = [...currentFolders, ...folders]; return this.windowService.createAndOpenWorkspace(distinct(newRoots.map(root => root.fsPath))); diff --git a/src/vs/workbench/parts/files/common/explorerModel.ts b/src/vs/workbench/parts/files/common/explorerModel.ts index cbcbad4b33d..9f3a0cf3bdc 100644 --- a/src/vs/workbench/parts/files/common/explorerModel.ts +++ b/src/vs/workbench/parts/files/common/explorerModel.ts @@ -25,7 +25,7 @@ export class Model { private _roots: FileStat[]; constructor( @IWorkspaceContextService private contextService: IWorkspaceContextService) { - const setRoots = () => this._roots = this.contextService.getWorkspace().folders.map(uri => new FileStat(uri, undefined)); + const setRoots = () => this._roots = this.contextService.getWorkspace().folders.map(folder => new FileStat(folder.uri, undefined)); this.contextService.onDidChangeWorkspaceFolders(() => setRoots()); setRoots(); } @@ -51,7 +51,7 @@ export class Model { public findClosest(resource: URI): FileStat { const folder = this.contextService.getWorkspaceFolder(resource); if (folder) { - const root = this.roots.filter(r => r.resource.toString() === folder.toString()).pop(); + const root = this.roots.filter(r => r.resource.toString() === folder.uri.toString()).pop(); if (root) { return root.find(resource); } diff --git a/src/vs/workbench/parts/output/common/outputLinkProvider.ts b/src/vs/workbench/parts/output/common/outputLinkProvider.ts index e457e1073c4..09c48178696 100644 --- a/src/vs/workbench/parts/output/common/outputLinkProvider.ts +++ b/src/vs/workbench/parts/output/common/outputLinkProvider.ts @@ -80,7 +80,7 @@ export class OutputLinkProvider { if (!this.worker) { const createData: ICreateData = { - workspaceFolders: this.contextService.getWorkspace().folders.map(folder => folder.toString()) + workspaceFolders: this.contextService.getWorkspace().folders.map(folder => folder.uri.toString()) }; this.worker = createWebWorker(this.modelService, { diff --git a/src/vs/workbench/parts/preferences/browser/preferencesActions.ts b/src/vs/workbench/parts/preferences/browser/preferencesActions.ts index aeee25d30bf..902ce34b205 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesActions.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesActions.ts @@ -107,7 +107,7 @@ export class OpenFolderSettingsAction extends Action { public run(): TPromise { const picks: IPickOpenEntry[] = this.workspaceContextService.getWorkspace().folders.map((folder, index) => { return { - label: getSettingsTargetName(ConfigurationTarget.FOLDER, folder, this.workspaceContextService), + label: getSettingsTargetName(ConfigurationTarget.FOLDER, folder.uri, this.workspaceContextService), id: `${index}` }; }); @@ -115,7 +115,7 @@ export class OpenFolderSettingsAction extends Action { return this.quickOpenService.pick(picks, { placeHolder: nls.localize('pickFolder', "Select Folder") }) .then(pick => { if (pick) { - return this.preferencesService.openFolderSettings(this.workspaceContextService.getWorkspace().folders[parseInt(pick.id)]); + return this.preferencesService.openFolderSettings(this.workspaceContextService.getWorkspace().folders[parseInt(pick.id)].uri); } return undefined; }); diff --git a/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts b/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts index 624ab8f0ef9..48e10ee11a1 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts @@ -240,7 +240,8 @@ export class PreferencesEditor extends BaseEditor { return resource; } - return this.workspaceContextService.getWorkspaceFolder(resource); + const workspaceFolder = this.workspaceContextService.getWorkspaceFolder(resource); + return workspaceFolder ? workspaceFolder.uri : null; } private onWorkspaceFoldersChanged(): void { @@ -887,7 +888,7 @@ class SettingsEditorContribution extends AbstractSettingsEditorContribution impl } for (const folder of this.workspaceContextService.getWorkspace().folders) { - const folderSettingsResource = this.preferencesService.getFolderSettingsResource(folder); + const folderSettingsResource = this.preferencesService.getFolderSettingsResource(folder.uri); if (folderSettingsResource && folderSettingsResource.fsPath === model.uri.fsPath) { return true; } diff --git a/src/vs/workbench/parts/preferences/browser/preferencesService.ts b/src/vs/workbench/parts/preferences/browser/preferencesService.ts index e820df5ea27..2bda7b84eb6 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesService.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesService.ts @@ -320,10 +320,10 @@ export class PreferencesService extends Disposable implements IPreferencesServic return null; } const workspace = this.contextService.getWorkspace(); - return workspace.configuration || this.toResource(paths.join('.vscode', 'settings.json'), workspace.folders[0]); + return workspace.configuration || this.toResource(paths.join('.vscode', 'settings.json'), workspace.folders[0].uri); case ConfigurationTarget.FOLDER: const folder = this.contextService.getWorkspaceFolder(resource); - return folder ? this.toResource(paths.join('.vscode', 'settings.json'), folder) : null; + return folder ? this.toResource(paths.join('.vscode', 'settings.json'), folder.uri) : null; } return null; } diff --git a/src/vs/workbench/parts/preferences/browser/preferencesWidgets.ts b/src/vs/workbench/parts/preferences/browser/preferencesWidgets.ts index cb4392d3c8c..7226d003436 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesWidgets.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesWidgets.ts @@ -353,10 +353,10 @@ export class SettingsTargetsWidget extends Widget { actions.push(...this.workspaceContextService.getWorkspace().folders.map((folder, index) => { return { id: 'folderSettingsTarget' + index, - label: getSettingsTargetName(ConfigurationTarget.FOLDER, folder, this.workspaceContextService), - checked: this.uri.fsPath === folder.fsPath, + label: getSettingsTargetName(ConfigurationTarget.FOLDER, folder.uri, this.workspaceContextService), + checked: this.uri.fsPath === folder.uri.fsPath, enabled: true, - run: () => this.onTargetClicked(folder) + run: () => this.onTargetClicked(folder.uri) }; })); } diff --git a/src/vs/workbench/parts/preferences/common/preferences.ts b/src/vs/workbench/parts/preferences/common/preferences.ts index ec69c7a8cb8..f272aa4b966 100644 --- a/src/vs/workbench/parts/preferences/common/preferences.ts +++ b/src/vs/workbench/parts/preferences/common/preferences.ts @@ -108,7 +108,7 @@ export function getSettingsTargetName(target: ConfigurationTarget, resource: URI return localize('workspaceSettingsTarget', "Workspace Settings"); case ConfigurationTarget.FOLDER: const folder = workspaceContextService.getWorkspaceFolder(resource); - return folder ? paths.basename(folder.fsPath) : ''; + return folder ? paths.basename(folder.uri.fsPath) : ''; } } diff --git a/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.ts b/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.ts index f5dc4573e53..0a14579bf1a 100644 --- a/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.ts +++ b/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.ts @@ -45,7 +45,7 @@ export class SettingsChangeRelauncher implements IWorkbenchContribution { ) { const workspace = this.contextService.getWorkspace(); this.foldersCount = workspace.folders.length; - this.firstFolderPath = workspace.folders.length > 0 ? workspace.folders[0].fsPath : void 0; + this.firstFolderPath = workspace.folders.length > 0 ? workspace.folders[0].uri.fsPath : void 0; this.onConfigurationChange(configurationService.getConfiguration(), false); @@ -99,7 +99,7 @@ export class SettingsChangeRelauncher implements IWorkbenchContribution { const workspace = this.contextService.getWorkspace(); const newFoldersCount = workspace.folders.length; - const newFirstFolderPath = workspace.folders.length > 0 ? workspace.folders[0].fsPath : void 0; + const newFirstFolderPath = workspace.folders.length > 0 ? workspace.folders[0].uri.fsPath : void 0; let reloadWindow = false; let reloadExtensionHost = false; diff --git a/src/vs/workbench/parts/search/browser/openFileHandler.ts b/src/vs/workbench/parts/search/browser/openFileHandler.ts index 7fbbcfdbe2c..2e8df19f0b7 100644 --- a/src/vs/workbench/parts/search/browser/openFileHandler.ts +++ b/src/vs/workbench/parts/search/browser/openFileHandler.ts @@ -164,7 +164,7 @@ export class OpenFileHandler extends QuickOpenHandler { iconClass = 'file'; // only use a generic file icon if we are forced to use an icon and have no icon theme set otherwise } - const folderResources = this.contextService.getWorkspace().folders; + const folderResources = this.contextService.getWorkspace().folders.map(folder => folder.uri); return this.searchService.search(this.queryBuilder.file(folderResources, query)).then((complete) => { const results: QuickOpenEntry[] = []; for (let i = 0; i < complete.results.length; i++) { @@ -199,7 +199,7 @@ export class OpenFileHandler extends QuickOpenHandler { useRipgrep: this.experimentService.getExperiments().ripgrepQuickSearch }; - const folderResources = this.contextService.getWorkspace().folders; + const folderResources = this.contextService.getWorkspace().folders.map(folder => folder.uri); const query = this.queryBuilder.file(folderResources, options); return query; diff --git a/src/vs/workbench/parts/search/browser/searchViewlet.ts b/src/vs/workbench/parts/search/browser/searchViewlet.ts index e550acb92fb..a0b43cd4867 100644 --- a/src/vs/workbench/parts/search/browser/searchViewlet.ts +++ b/src/vs/workbench/parts/search/browser/searchViewlet.ts @@ -874,19 +874,19 @@ export class SearchViewlet extends Viewlet { if (resource) { if (this.contextService.getWorkbenchState() === WorkbenchState.FOLDER) { // Show relative path from the root for single-root mode - folderPath = paths.relative(workspace.folders[0].fsPath, resource.fsPath); + folderPath = paths.relative(workspace.folders[0].uri.fsPath, resource.fsPath); if (folderPath && folderPath !== '.') { folderPath = './' + folderPath; } } else { const owningFolder = this.contextService.getWorkspaceFolder(resource); if (owningFolder) { - const owningRootBasename = paths.basename(owningFolder.fsPath); + const owningRootBasename = paths.basename(owningFolder.uri.fsPath); // If this root is the only one with its basename, use a relative ./ path. If there is another, use an absolute path - const isUniqueFolder = workspace.folders.filter(root => paths.basename(root.fsPath) === owningRootBasename).length === 1; + const isUniqueFolder = workspace.folders.filter(folder => paths.basename(folder.uri.fsPath) === owningRootBasename).length === 1; if (isUniqueFolder) { - folderPath = `./${owningRootBasename}/${paths.relative(owningFolder.fsPath, resource.fsPath)}`; + folderPath = `./${owningRootBasename}/${paths.relative(owningFolder.uri.fsPath, resource.fsPath)}`; } else { folderPath = resource.fsPath; } @@ -969,7 +969,7 @@ export class SearchViewlet extends Viewlet { let query: ISearchQuery; try { - query = this.queryBuilder.text(content, folderResources, options); + query = this.queryBuilder.text(content, folderResources.map(folder => folder.uri), options); } catch (err) { onQueryValidationError(err); return; diff --git a/src/vs/workbench/parts/search/common/queryBuilder.ts b/src/vs/workbench/parts/search/common/queryBuilder.ts index 49fa52d03d5..c91ced37871 100644 --- a/src/vs/workbench/parts/search/common/queryBuilder.ts +++ b/src/vs/workbench/parts/search/common/queryBuilder.ts @@ -214,19 +214,19 @@ export class QueryBuilder { if (this.workspaceContextService.getWorkbenchState() === WorkbenchState.FOLDER) { // TODO: @Sandy Try checking workspace folders length instead. return [paths.normalize( - paths.join(this.workspaceContextService.getWorkspace().folders[0].fsPath, searchPath))]; + paths.join(this.workspaceContextService.getWorkspace().folders[0].uri.fsPath, searchPath))]; } else if (searchPath === './') { return []; // ./ or ./**/foo makes sense for single-folder but not multi-folder workspaces } else { const relativeSearchPathMatch = searchPath.match(/\.[\/\\]([^\/\\]+)([\/\\].+)?/); if (relativeSearchPathMatch) { const searchPathRoot = relativeSearchPathMatch[1]; - const matchingRoots = this.workspaceContextService.getWorkspace().folders.filter(folder => paths.basename(folder.fsPath) === searchPathRoot); + const matchingRoots = this.workspaceContextService.getWorkspace().folders.filter(folder => paths.basename(folder.uri.fsPath) === searchPathRoot); if (matchingRoots.length) { return matchingRoots.map(root => { return relativeSearchPathMatch[2] ? - paths.normalize(paths.join(root.fsPath, relativeSearchPathMatch[2])) : - root.fsPath; + paths.normalize(paths.join(root.uri.fsPath, relativeSearchPathMatch[2])) : + root.uri.fsPath; }); } else { // No root folder with name diff --git a/src/vs/workbench/parts/search/test/common/queryBuilder.test.ts b/src/vs/workbench/parts/search/test/common/queryBuilder.test.ts index 8c79981d63f..059f34fb3e3 100644 --- a/src/vs/workbench/parts/search/test/common/queryBuilder.test.ts +++ b/src/vs/workbench/parts/search/test/common/queryBuilder.test.ts @@ -11,7 +11,7 @@ import * as arrays from 'vs/base/common/arrays'; import uri from 'vs/base/common/uri'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IWorkspaceContextService, Workspace } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, Workspace, toWorkspaceFolders } from 'vs/platform/workspace/common/workspace'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { QueryBuilder, ISearchPathsResult } from 'vs/workbench/parts/search/common/queryBuilder'; import { TestContextService } from 'vs/workbench/test/workbenchTestServices'; @@ -37,7 +37,7 @@ suite('QueryBuilder', () => { instantiationService.stub(IConfigurationService, mockConfigService); mockContextService = new TestContextService(); - mockWorkspace = new Workspace('workspace', 'workspace', [ROOT_1_URI]); + mockWorkspace = new Workspace('workspace', 'workspace', toWorkspaceFolders([{ path: ROOT_1_URI.fsPath }])); mockContextService.setWorkspace(mockWorkspace); instantiationService.stub(IWorkspaceContextService, mockContextService); @@ -154,7 +154,7 @@ suite('QueryBuilder', () => { const ROOT_2_URI = getUri(ROOT_2); const ROOT_3 = fixPath('/project/root3'); const ROOT_3_URI = getUri(ROOT_3); - mockWorkspace.folders = [ROOT_1_URI, ROOT_2_URI, ROOT_3_URI]; + mockWorkspace.folders = toWorkspaceFolders([{ path: ROOT_1_URI.fsPath }, { path: ROOT_2_URI.fsPath }, { path: ROOT_3_URI.fsPath }]); mockWorkspace.configuration = uri.file(fixPath('/config')); mockConfigService.setUserConfiguration('search', { @@ -473,7 +473,7 @@ suite('QueryBuilder', () => { test('relative includes w/two root folders', () => { const ROOT_2 = '/project/root2'; - mockWorkspace.folders = [ROOT_1_URI, getUri(ROOT_2)]; + mockWorkspace.folders = toWorkspaceFolders([{ path: ROOT_1_URI.fsPath }, { path: getUri(ROOT_2).fsPath }]); mockWorkspace.configuration = uri.file(fixPath('config')); [ @@ -513,7 +513,7 @@ suite('QueryBuilder', () => { test('relative includes w/multiple ambiguous root folders', () => { const ROOT_2 = '/project/rootB'; const ROOT_3 = '/otherproject/rootB'; - mockWorkspace.folders = [ROOT_1_URI, getUri(ROOT_2), getUri(ROOT_3)]; + mockWorkspace.folders = toWorkspaceFolders([{ path: ROOT_1_URI.fsPath }, { path: getUri(ROOT_2).fsPath }, { path: getUri(ROOT_3).fsPath }]); mockWorkspace.configuration = uri.file(fixPath('/config')); [ diff --git a/src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts b/src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts index 445389059f5..96f6013843c 100644 --- a/src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts +++ b/src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts @@ -1619,20 +1619,20 @@ class TaskService extends EventEmitter implements ITaskService { let schemaVersion = JsonSchemaVersion.V2_0_0; if (this.contextService.getWorkbenchState() === WorkbenchState.FOLDER) { - let workspaceFolder: WorkspaceFolder = { uri: this.contextService.getWorkspace().folders[0] }; + let workspaceFolder: WorkspaceFolder = { uri: this.contextService.getWorkspace().folders[0].uri }; workspaceFolders.push(workspaceFolder); executionEngine = this.computeExecutionEngine(workspaceFolder); schemaVersion = this.computeJsonSchemaVersion(workspaceFolder); } else if (this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE) { for (let folder of this.contextService.getWorkspace().folders) { - let workspaceFolder = { uri: folder }; + let workspaceFolder = { uri: folder.uri }; if (schemaVersion === this.computeJsonSchemaVersion(workspaceFolder)) { workspaceFolders.push(workspaceFolder); } else { this._outputChannel.append(nls.localize( 'taskService.ignoreingFolder', 'Ignoring task configurations for workspace folder {0}. Multi root folder support requires that all folders use task version 2.0.', - folder.fsPath)); + folder.uri.fsPath)); } } } diff --git a/src/vs/workbench/parts/tasks/node/processRunnerDetector.ts b/src/vs/workbench/parts/tasks/node/processRunnerDetector.ts index e0e81b063ff..91d6f4130fa 100644 --- a/src/vs/workbench/parts/tasks/node/processRunnerDetector.ts +++ b/src/vs/workbench/parts/tasks/node/processRunnerDetector.ts @@ -156,7 +156,7 @@ export class ProcessRunnerDetector { this.taskConfiguration = config; this._stderr = []; this._stdout = []; - this._cwd = this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY ? Paths.normalize(this.contextService.getWorkspace().folders[0].fsPath, true) : ''; + this._cwd = this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY ? Paths.normalize(this.contextService.getWorkspace().folders[0].uri.fsPath, true) : ''; } public get stderr(): string[] { @@ -175,7 +175,7 @@ export class ProcessRunnerDetector { let isShellCommand = !!this.taskConfiguration.isShellCommand; // TODO@Dirk adopt new configuration resolver service https://github.com/Microsoft/vscode/issues/31365 return this.runDetection( - new LineProcess(this.taskConfiguration.command, this.configurationResolverService.resolve(this.contextService.getWorkspace().folders[0], args), isShellCommand, options), + new LineProcess(this.taskConfiguration.command, this.configurationResolverService.resolve(this.contextService.getWorkspace().folders[0].uri, args), isShellCommand, options), this.taskConfiguration.command, isShellCommand, config.matcher, ProcessRunnerDetector.DefaultProblemMatchers, list); } else { if (detectSpecific) { @@ -219,10 +219,10 @@ export class ProcessRunnerDetector { // TODO@Dirk adopt new configuration resolver service https://github.com/Microsoft/vscode/issues/31365 let result = Objects.clone(options); if (result.cwd) { - result.cwd = this.configurationResolverService.resolve(this.contextService.getWorkspace().folders[0], result.cwd); + result.cwd = this.configurationResolverService.resolve(this.contextService.getWorkspace().folders[0].uri, result.cwd); } if (result.env) { - result.env = this.configurationResolverService.resolve(this.contextService.getWorkspace().folders[0], result.env); + result.env = this.configurationResolverService.resolve(this.contextService.getWorkspace().folders[0].uri, result.env); } return result; } diff --git a/src/vs/workbench/parts/tasks/node/processTaskSystem.ts b/src/vs/workbench/parts/tasks/node/processTaskSystem.ts index e0006516a31..bfb82caf634 100644 --- a/src/vs/workbench/parts/tasks/node/processTaskSystem.ts +++ b/src/vs/workbench/parts/tasks/node/processTaskSystem.ts @@ -382,7 +382,7 @@ export class ProcessTaskSystem extends EventEmitter implements ITaskSystem { private resolveVariable(value: string): string { // TODO@Dirk adopt new configuration resolver service https://github.com/Microsoft/vscode/issues/31365 - return this.configurationResolverService.resolve(this.contextService.getWorkspace().folders[0], value); + return this.configurationResolverService.resolve(this.contextService.getWorkspace().folders[0].uri, value); } public log(value: string): void { diff --git a/src/vs/workbench/services/backup/test/node/backupFileService.test.ts b/src/vs/workbench/services/backup/test/node/backupFileService.test.ts index 4fb7de8ca69..65a97a110a8 100644 --- a/src/vs/workbench/services/backup/test/node/backupFileService.test.ts +++ b/src/vs/workbench/services/backup/test/node/backupFileService.test.ts @@ -20,7 +20,7 @@ import { EnvironmentService } from 'vs/platform/environment/node/environmentServ import { parseArgs } from 'vs/platform/environment/node/argv'; import { RawTextSource } from 'vs/editor/common/model/textSource'; import { TestContextService, TestTextResourceConfigurationService } from 'vs/workbench/test/workbenchTestServices'; -import { Workspace } from 'vs/platform/workspace/common/workspace'; +import { Workspace, toWorkspaceFolders } from 'vs/platform/workspace/common/workspace'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; class TestEnvironmentService extends EnvironmentService { @@ -49,7 +49,7 @@ const untitledBackupPath = path.join(workspaceBackupPath, 'untitled', crypto.cre class TestBackupFileService extends BackupFileService { constructor(workspace: Uri, backupHome: string, workspacesJsonPath: string) { - const fileService = new FileService(new TestContextService(new Workspace(workspace.fsPath, workspace.fsPath, [workspace])), new TestTextResourceConfigurationService(), new TestConfigurationService(), { disableWatcher: true }); + const fileService = new FileService(new TestContextService(new Workspace(workspace.fsPath, workspace.fsPath, toWorkspaceFolders([{ path: workspace.fsPath }]))), new TestTextResourceConfigurationService(), new TestConfigurationService(), { disableWatcher: true }); super(workspaceBackupPath, fileService); } diff --git a/src/vs/workbench/services/configuration/node/configuration.ts b/src/vs/workbench/services/configuration/node/configuration.ts index 102b1824712..8024469952f 100644 --- a/src/vs/workbench/services/configuration/node/configuration.ts +++ b/src/vs/workbench/services/configuration/node/configuration.ts @@ -9,7 +9,7 @@ import * as paths from 'vs/base/common/paths'; import { TPromise } from 'vs/base/common/winjs.base'; import Event, { Emitter } from 'vs/base/common/event'; import { StrictResourceMap } from 'vs/base/common/map'; -import { equals, coalesce } from 'vs/base/common/arrays'; +import { equals } from 'vs/base/common/arrays'; import * as objects from 'vs/base/common/objects'; import * as errors from 'vs/base/common/errors'; import * as collections from 'vs/base/common/collections'; @@ -18,7 +18,7 @@ import { RunOnceScheduler } from 'vs/base/common/async'; import { readFile, stat } from 'vs/base/node/pfs'; import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; import * as extfs from 'vs/base/node/extfs'; -import { IWorkspaceContextService, IWorkspace, Workspace, WorkbenchState } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, IWorkspace, Workspace, WorkbenchState, WorkspaceFolder, toWorkspaceFolders } from 'vs/platform/workspace/common/workspace'; import { FileChangeType, FileChangesEvent } from 'vs/platform/files/common/files'; import { isLinux } from 'vs/base/common/platform'; import { ConfigWatcher } from 'vs/base/node/config'; @@ -33,7 +33,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { ExtensionsRegistry, ExtensionMessageCollector } from 'vs/platform/extensions/common/extensionsRegistry'; import { IConfigurationNode, IConfigurationRegistry, Extensions, editorConfigurationSchemaId, IDefaultConfigurationExtension, validateProperty, ConfigurationScope, schemaId } from 'vs/platform/configuration/common/configurationRegistry'; import { createHash } from 'crypto'; -import { getWorkspaceLabel, IWorkspacesService, IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, IStoredWorkspaceFolder, isWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; +import { getWorkspaceLabel, IWorkspacesService, IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { IWindowConfiguration } from 'vs/platform/windows/common/windows'; import { IJSONSchema } from 'vs/base/common/jsonSchema'; @@ -229,7 +229,7 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat return WorkbenchState.EMPTY; } - public getWorkspaceFolder(resource: URI): URI { + public getWorkspaceFolder(resource: URI): WorkspaceFolder { return this.workspace.getFolder(resource); } @@ -240,7 +240,7 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat public isCurrentWorkspace(workspaceIdentifier: ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier): boolean { switch (this.getWorkbenchState()) { case WorkbenchState.FOLDER: - return isSingleFolderWorkspaceIdentifier(workspaceIdentifier) && this.pathEquals(this.workspace.folders[0].fsPath, workspaceIdentifier); + return isSingleFolderWorkspaceIdentifier(workspaceIdentifier) && this.pathEquals(this.workspace.folders[0].uri.fsPath, workspaceIdentifier); case WorkbenchState.WORKSPACE: return isWorkspaceIdentifier(workspaceIdentifier) && this.workspace.id === workspaceIdentifier.id; } @@ -249,7 +249,7 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat public toResource(workspaceRelativePath: string): URI { if (this.workspace.folders.length) { - return URI.file(paths.join(this.workspace.folders[0].fsPath, workspaceRelativePath)); + return URI.file(paths.join(this.workspace.folders[0].uri.fsPath, workspaceRelativePath)); } return null; } @@ -388,7 +388,7 @@ export class WorkspaceServiceImpl extends WorkspaceService { } public getUnsupportedWorkspaceKeys(): string[] { - return this.getWorkbenchState() === WorkbenchState.FOLDER ? this._configuration.getFolderConfigurationModel(this.workspace.folders[0]).workspaceSettingsConfig.unsupportedKeys : []; + return this.getWorkbenchState() === WorkbenchState.FOLDER ? this._configuration.getFolderConfigurationModel(this.workspace.folders[0].uri).workspaceSettingsConfig.unsupportedKeys : []; } public initialize(trigger: boolean = true): TPromise { @@ -419,7 +419,7 @@ export class WorkspaceServiceImpl extends WorkspaceService { } public handleWorkspaceFileEvents(event: FileChangesEvent): void { - TPromise.join(this.workspace.folders.map(folder => this.cachedFolderConfigs.get(folder).handleWorkspaceFileEvents(event))) // handle file event for each folder + TPromise.join(this.workspace.folders.map(folder => this.cachedFolderConfigs.get(folder.uri).handleWorkspaceFileEvents(event))) // handle file event for each folder .then(folderConfigurations => folderConfigurations.map((configuration, index) => ({ configuration, folder: this.workspace.folders[index] })) .filter(folderConfiguration => !!folderConfiguration.configuration) // Filter folders which are not impacted by events @@ -470,7 +470,7 @@ export class WorkspaceServiceImpl extends WorkspaceService { return this.workspaceConfiguration.load(this.workspaceConfigPath) .then(() => { const workspaceConfigurationModel = this.workspaceConfiguration.workspaceConfigurationModel; - const workspaceFolders = this.parseWorkspaceFolders(workspaceConfigurationModel.folders); + const workspaceFolders = toWorkspaceFolders(workspaceConfigurationModel.folders, URI.file(paths.dirname(this.workspaceConfigPath.fsPath))); if (!workspaceFolders.length) { return TPromise.wrapError(new Error('Invalid workspace configuraton file ' + this.workspaceConfigPath)); } @@ -482,21 +482,6 @@ export class WorkspaceServiceImpl extends WorkspaceService { }); } - private parseWorkspaceFolders(configuredFolders: IStoredWorkspaceFolder[]): URI[] { - return coalesce(configuredFolders.map(configuredFolder => { - const path = configuredFolder.path; - if (!path) { - return void 0; - } - - if (paths.isAbsolute(path)) { - return URI.file(path); - } - - return URI.file(paths.join(paths.dirname(this.workspaceConfigPath.fsPath), path)); - })); - } - private registerWorkspaceConfigSchema(): void { const contributionRegistry = Registry.as(JSONExtensions.JSONContribution); if (!contributionRegistry.getSchemaContributions().schemas['vscode://schemas/workspaceConfig']) { @@ -544,20 +529,20 @@ export class WorkspaceServiceImpl extends WorkspaceService { const ctime = isLinux ? workspaceStat.ino : workspaceStat.birthtime.getTime(); // On Linux, birthtime is ctime, so we cannot use it! We use the ino instead! const id = createHash('md5').update(this.folderPath.fsPath).update(ctime ? String(ctime) : '').digest('hex'); const folder = URI.file(this.folderPath.fsPath); - this.workspace = new Workspace(id, paths.basename(this.folderPath.fsPath), [folder], null, ctime); + this.workspace = new Workspace(id, paths.basename(this.folderPath.fsPath), toWorkspaceFolders([{ path: folder.fsPath }]), null, ctime); return TPromise.as(null); }); } - private initCachesForFolders(folders: URI[]): void { + private initCachesForFolders(folders: WorkspaceFolder[]): void { for (const folder of folders) { - this.cachedFolderConfigs.set(folder, this._register(new FolderConfiguration(folder, this.workspaceSettingsRootFolder, this.getWorkbenchState() === WorkbenchState.WORKSPACE ? ConfigurationScope.RESOURCE : ConfigurationScope.WINDOW))); + this.cachedFolderConfigs.set(folder.uri, this._register(new FolderConfiguration(folder.uri, this.workspaceSettingsRootFolder, this.getWorkbenchState() === WorkbenchState.WORKSPACE ? ConfigurationScope.RESOURCE : ConfigurationScope.WINDOW))); this.updateFolderConfiguration(folder, new FolderConfigurationModel(new FolderSettingsModel(null), [], ConfigurationScope.RESOURCE), false); } } - protected updateConfiguration(folders: URI[] = this.workspace.folders): TPromise { - return TPromise.join([...folders.map(folder => this.cachedFolderConfigs.get(folder).loadConfiguration() + protected updateConfiguration(folders: WorkspaceFolder[] = this.workspace.folders): TPromise { + return TPromise.join([...folders.map(folder => this.cachedFolderConfigs.get(folder.uri).loadConfiguration() .then(configuration => this.updateFolderConfiguration(folder, configuration, true)))]) .then(changed => changed.reduce((result, value) => result || value, false)) .then(changed => this.updateWorkspaceConfiguration(true) || changed); @@ -565,7 +550,7 @@ export class WorkspaceServiceImpl extends WorkspaceService { private onBaseConfigurationChanged({ source, sourceConfig }: IConfigurationServiceEvent): void { if (source === ConfigurationSource.Default) { - this.workspace.folders.forEach(folder => this._configuration.getFolderConfigurationModel(folder).update()); + this.workspace.folders.forEach(folder => this._configuration.getFolderConfigurationModel(folder.uri).update()); } if (this._configuration.updateBaseConfiguration(this.baseConfigurationService.configuration())) { this._onDidUpdateConfiguration.fire({ source, sourceConfig }); @@ -573,8 +558,8 @@ export class WorkspaceServiceImpl extends WorkspaceService { } private onWorkspaceConfigurationChanged(): void { - let configuredFolders = this.parseWorkspaceFolders(this.workspaceConfiguration.workspaceConfigurationModel.folders); - const foldersChanged = !equals(this.workspace.folders, configuredFolders, (r1, r2) => r1.fsPath === r2.fsPath); + let configuredFolders = toWorkspaceFolders(this.workspaceConfiguration.workspaceConfigurationModel.folders, URI.file(paths.dirname(this.workspaceConfigPath.fsPath))); + const foldersChanged = !equals(this.workspace.folders, configuredFolders, (folder1, folder2) => folder1.uri.fsPath === folder2.uri.fsPath); if (foldersChanged) { // TODO@Sandeep be smarter here about detecting changes this.workspace.folders = configuredFolders; this.onFoldersChanged() @@ -597,7 +582,7 @@ export class WorkspaceServiceImpl extends WorkspaceService { // Remove the configurations of deleted folders for (const key of this.cachedFolderConfigs.keys()) { - if (!this.workspace.folders.filter(folder => folder.toString() === key.toString())[0]) { + if (!this.workspace.folders.filter(folder => folder.uri.toString() === key.toString())[0]) { this.cachedFolderConfigs.delete(key); if (this._configuration.deleteFolderConfiguration(key)) { configurationChangedOnRemoval = true; @@ -606,7 +591,7 @@ export class WorkspaceServiceImpl extends WorkspaceService { } // Initialize the newly added folders - const toInitialize = this.workspace.folders.filter(folder => !this.cachedFolderConfigs.has(folder)); + const toInitialize = this.workspace.folders.filter(folder => !this.cachedFolderConfigs.has(folder.uri)); if (toInitialize.length) { this.initCachesForFolders(toInitialize); return this.updateConfiguration(toInitialize) @@ -618,8 +603,8 @@ export class WorkspaceServiceImpl extends WorkspaceService { return TPromise.as(false); } - private updateFolderConfiguration(folder: URI, folderConfiguration: FolderConfigurationModel, compare: boolean): boolean { - let configurationChanged = this._configuration.updateFolderConfiguration(folder, folderConfiguration, compare); + private updateFolderConfiguration(folder: WorkspaceFolder, folderConfiguration: FolderConfigurationModel, compare: boolean): boolean { + let configurationChanged = this._configuration.updateFolderConfiguration(folder.uri, folderConfiguration, compare); if (this.getWorkbenchState() === WorkbenchState.FOLDER) { // Workspace configuration changed configurationChanged = this.updateWorkspaceConfiguration(compare) || configurationChanged; @@ -628,12 +613,12 @@ export class WorkspaceServiceImpl extends WorkspaceService { } private updateWorkspaceConfiguration(compare: boolean): boolean { - const workspaceConfiguration = this.getWorkbenchState() === WorkbenchState.WORKSPACE ? this.workspaceConfiguration.workspaceConfigurationModel.workspaceConfiguration : this._configuration.getFolderConfigurationModel(this.workspace.folders[0]); + const workspaceConfiguration = this.getWorkbenchState() === WorkbenchState.WORKSPACE ? this.workspaceConfiguration.workspaceConfigurationModel.workspaceConfiguration : this._configuration.getFolderConfigurationModel(this.workspace.folders[0].uri); return this._configuration.updateWorkspaceConfiguration(workspaceConfiguration, compare); } protected triggerConfigurationChange(): void { - this._onDidUpdateConfiguration.fire({ source: ConfigurationSource.Workspace, sourceConfig: this._configuration.getFolderConfigurationModel(this.workspace.folders[0]).contents }); + this._onDidUpdateConfiguration.fire({ source: ConfigurationSource.Workspace, sourceConfig: this._configuration.getFolderConfigurationModel(this.workspace.folders[0].uri).contents }); } } @@ -909,7 +894,7 @@ export class Configuration extends BaseConfiguration { } deleteFolderConfiguration(folder: URI): boolean { - if (this._workspace && this._workspace.folders.length > 0 && this._workspace.folders[0].fsPath === folder.fsPath) { + if (this._workspace && this._workspace.folders.length > 0 && this._workspace.folders[0].uri.fsPath === folder.fsPath) { // Do not remove workspace configuration return false; } diff --git a/src/vs/workbench/services/configuration/node/configurationEditingService.ts b/src/vs/workbench/services/configuration/node/configurationEditingService.ts index 113aeeacda5..e423e2abc49 100644 --- a/src/vs/workbench/services/configuration/node/configurationEditingService.ts +++ b/src/vs/workbench/services/configuration/node/configurationEditingService.ts @@ -333,14 +333,14 @@ export class ConfigurationEditingService implements IConfigurationEditingService const workspace = this.contextService.getWorkspace(); if (target === ConfigurationTarget.WORKSPACE) { - return workspace.configuration || this.toResource(relativePath, workspace.folders[0]); + return workspace.configuration || this.toResource(relativePath, workspace.folders[0].uri); } if (target === ConfigurationTarget.FOLDER && this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE) { if (resource) { const folder = this.contextService.getWorkspaceFolder(resource); if (folder) { - return this.toResource(relativePath, folder); + return this.toResource(relativePath, folder.uri); } } } diff --git a/src/vs/workbench/services/configuration/test/node/configuration.test.ts b/src/vs/workbench/services/configuration/test/node/configuration.test.ts index 1ee691bae5f..b71d1341874 100644 --- a/src/vs/workbench/services/configuration/test/node/configuration.test.ts +++ b/src/vs/workbench/services/configuration/test/node/configuration.test.ts @@ -21,6 +21,7 @@ import uuid = require('vs/base/common/uuid'); import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry'; import { WorkspaceServiceImpl, WorkspaceService } from 'vs/workbench/services/configuration/node/configuration'; import { FileChangeType, FileChangesEvent } from 'vs/platform/files/common/files'; +import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; class SettingsTestEnvironmentService extends EnvironmentService { @@ -31,26 +32,103 @@ class SettingsTestEnvironmentService extends EnvironmentService { get appSettingsPath(): string { return this.customAppSettingsHome; } } -suite('WorkspaceConfigurationService - Node', () => { +function createWorkspace(callback: (workspaceDir: string, globalSettingsFile: string, cleanUp: (callback: () => void) => void) => void): void { + const id = uuid.generateUuid(); + const parentDir = path.join(os.tmpdir(), 'vsctests', id); + const workspaceDir = path.join(parentDir, 'workspaceconfig', id); + const workspaceSettingsDir = path.join(workspaceDir, '.vscode'); + const globalSettingsFile = path.join(workspaceDir, 'config.json'); - function createWorkspace(callback: (workspaceDir: string, globalSettingsFile: string, cleanUp: (callback: () => void) => void) => void): void { - const id = uuid.generateUuid(); - const parentDir = path.join(os.tmpdir(), 'vsctests', id); - const workspaceDir = path.join(parentDir, 'workspaceconfig', id); - const workspaceSettingsDir = path.join(workspaceDir, '.vscode'); - const globalSettingsFile = path.join(workspaceDir, 'config.json'); + extfs.mkdirp(workspaceSettingsDir, 493, (error) => { + callback(workspaceDir, globalSettingsFile, (callback) => extfs.del(parentDir, os.tmpdir(), () => { }, callback)); + }); +} +function setUpFolder(folderName: string): TPromise<{ parentDir: string, workspaceDir: string, workspaceService: WorkspaceService }> { + const id = uuid.generateUuid(); + const parentDir = path.join(os.tmpdir(), 'vsctests', id); + const workspaceDir = path.join(parentDir, folderName); + const workspaceSettingsDir = path.join(workspaceDir, '.vscode'); + const globalSettingsFile = path.join(workspaceDir, 'config.json'); + + return new TPromise((c, e) => { extfs.mkdirp(workspaceSettingsDir, 493, (error) => { - callback(workspaceDir, globalSettingsFile, (callback) => extfs.del(parentDir, os.tmpdir(), () => { }, callback)); + if (error) { + e(error); + return null; + } + const environmentService = new SettingsTestEnvironmentService(parseArgs(process.argv), process.execPath, globalSettingsFile); + const workspaceService = new WorkspaceServiceImpl(workspaceDir, environmentService, null); + workspaceService.initialize().then(() => c({ parentDir, workspaceDir, workspaceService })); }); - } + }); +} - function createService(workspaceDir: string, globalSettingsFile: string): TPromise { - const environmentService = new SettingsTestEnvironmentService(parseArgs(process.argv), process.execPath, globalSettingsFile); - const service = new WorkspaceServiceImpl(workspaceDir, environmentService, null); +function createService(workspaceDir: string, globalSettingsFile: string): TPromise { + const environmentService = new SettingsTestEnvironmentService(parseArgs(process.argv), process.execPath, globalSettingsFile); + const service = new WorkspaceServiceImpl(workspaceDir, environmentService, null); - return service.initialize().then(() => service); - } + return service.initialize().then(() => service); +} + +suite('WorkspaceContextService - Folder', () => { + + let workspaceName = `testWorkspace${uuid.generateUuid}`, parentResource: string, workspaceResource: string, workspaceContextService: IWorkspaceContextService; + + setup(() => { + return setUpFolder(workspaceName) + .then(({ parentDir, workspaceDir, workspaceService }) => { + parentResource = parentDir; + workspaceResource = workspaceDir; + workspaceContextService = workspaceService; + }); + }); + + teardown(done => { + if (workspaceContextService) { + (workspaceContextService).dispose(); + } + if (parentResource) { + extfs.del(parentResource, os.tmpdir(), () => { }, done); + } + }); + + test('getWorkspace()', () => { + const actual = workspaceContextService.getWorkspace(); + + assert.equal(actual.folders.length, 1); + assert.equal(actual.folders[0].uri.fsPath, workspaceResource); + assert.equal(actual.folders[0].name, workspaceName); + assert.equal(actual.folders[0].index, 0); + assert.equal(actual.folders[0].raw, workspaceResource); + assert.ok(!actual.configuration); + }); + + test('getWorkbenchState()', () => { + const actual = workspaceContextService.getWorkbenchState(); + + assert.equal(actual, WorkbenchState.FOLDER); + }); + + test('getWorkspaceFolder()', () => { + const actual = workspaceContextService.getWorkspaceFolder(URI.file(path.join(workspaceResource, 'a'))); + + assert.equal(actual, workspaceContextService.getWorkspace().folders[0]); + }); + + test('isCurrentWorkspace() => true', () => { + assert.ok(workspaceContextService.isCurrentWorkspace(workspaceResource)); + }); + + test('isCurrentWorkspace() => false', () => { + assert.ok(!workspaceContextService.isCurrentWorkspace(workspaceResource + 'abc')); + }); +}); + +suite('WorkspaceContextService - Folder', () => { +}); + +suite('WorkspaceConfigurationService - Node', () => { test('defaults', (done: () => void) => { interface ITestSetting { diff --git a/src/vs/workbench/services/files/electron-browser/fileService.ts b/src/vs/workbench/services/files/electron-browser/fileService.ts index 5c63068eddf..23644861e3e 100644 --- a/src/vs/workbench/services/files/electron-browser/fileService.ts +++ b/src/vs/workbench/services/files/electron-browser/fileService.ts @@ -147,7 +147,7 @@ export class FileService implements IFileService { const encodingOverride: IEncodingOverride[] = []; encodingOverride.push({ resource: uri.file(this.environmentService.appSettingsHome), encoding: encoding.UTF8 }); this.contextService.getWorkspace().folders.forEach(folder => { - encodingOverride.push({ resource: uri.file(paths.join(folder.fsPath, '.vscode')), encoding: encoding.UTF8 }); + encodingOverride.push({ resource: uri.file(paths.join(folder.uri.fsPath, '.vscode')), encoding: encoding.UTF8 }); }); return encodingOverride; diff --git a/src/vs/workbench/services/files/node/watcher/nsfw/watcherService.ts b/src/vs/workbench/services/files/node/watcher/nsfw/watcherService.ts index 6a52fff3712..9e3744b2638 100644 --- a/src/vs/workbench/services/files/node/watcher/nsfw/watcherService.ts +++ b/src/vs/workbench/services/files/node/watcher/nsfw/watcherService.ts @@ -93,18 +93,17 @@ export class FileWatcher { return; } - const folders = this.contextService.getWorkspace().folders; - this.service.setRoots(folders.map(folder => { + this.service.setRoots(this.contextService.getWorkspace().folders.map(folder => { // Fetch the root's watcherExclude setting and return it const configuration = this.configurationService.getConfiguration(undefined, { - resource: folder + resource: folder.uri }); let ignored: string[] = []; if (configuration.files && configuration.files.watcherExclude) { ignored = Object.keys(configuration.files.watcherExclude).filter(k => !!configuration.files.watcherExclude[k]); } return { - basePath: folder.fsPath, + basePath: folder.uri.fsPath, ignored }; })); diff --git a/src/vs/workbench/services/files/node/watcher/unix/watcherService.ts b/src/vs/workbench/services/files/node/watcher/unix/watcherService.ts index a330c432f4f..7c4917dc472 100644 --- a/src/vs/workbench/services/files/node/watcher/unix/watcherService.ts +++ b/src/vs/workbench/services/files/node/watcher/unix/watcherService.ts @@ -52,7 +52,7 @@ export class FileWatcher { const service = new WatcherChannelClient(channel); // Start watching - const basePath: string = normalize(this.contextService.getWorkspace().folders[0].fsPath); + const basePath: string = normalize(this.contextService.getWorkspace().folders[0].uri.fsPath); service.watch({ basePath: basePath, ignored: this.ignored, verboseLogging: this.verboseLogging }).then(null, err => { if (!this.isDisposed && !(err instanceof Error && err.name === 'Canceled' && err.message === 'Canceled')) { return TPromise.wrapError(err); // the service lib uses the promise cancel error to indicate the process died, we do not want to bubble this up diff --git a/src/vs/workbench/services/files/node/watcher/win32/watcherService.ts b/src/vs/workbench/services/files/node/watcher/win32/watcherService.ts index e0b3b87fbee..26295cbc4a9 100644 --- a/src/vs/workbench/services/files/node/watcher/win32/watcherService.ts +++ b/src/vs/workbench/services/files/node/watcher/win32/watcherService.ts @@ -26,7 +26,7 @@ export class FileWatcher { } public startWatching(): () => void { - let basePath: string = normalize(this.contextService.getWorkspace().folders[0].fsPath); + let basePath: string = normalize(this.contextService.getWorkspace().folders[0].uri.fsPath); if (basePath && basePath.indexOf('\\\\') === 0 && endsWith(basePath, sep)) { // for some weird reason, node adds a trailing slash to UNC paths diff --git a/src/vs/workbench/services/files/test/node/fileService.test.ts b/src/vs/workbench/services/files/test/node/fileService.test.ts index c9ebdeabde9..f7644dd44ba 100644 --- a/src/vs/workbench/services/files/test/node/fileService.test.ts +++ b/src/vs/workbench/services/files/test/node/fileService.test.ts @@ -20,7 +20,7 @@ import encodingLib = require('vs/base/node/encoding'); import utils = require('vs/workbench/services/files/test/node/utils'); import { onError } from 'vs/base/test/common/utils'; import { TestContextService, TestTextResourceConfigurationService } from 'vs/workbench/test/workbenchTestServices'; -import { Workspace } from 'vs/platform/workspace/common/workspace'; +import { Workspace, WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; suite('FileService', () => { @@ -38,7 +38,7 @@ suite('FileService', () => { return onError(error, done); } - service = new FileService(new TestContextService(new Workspace(testDir, testDir, [uri.file(testDir)])), new TestTextResourceConfigurationService(), new TestConfigurationService(), { disableWatcher: true }); + service = new FileService(new TestContextService(new Workspace(testDir, testDir, [{ uri: uri.file(testDir), raw: testDir, index: 0, name: '' }])), new TestTextResourceConfigurationService(), new TestConfigurationService(), { disableWatcher: true }); done(); }); }); @@ -784,7 +784,7 @@ suite('FileService', () => { const textResourceConfigurationService = new TestTextResourceConfigurationService(configurationService); - const _service = new FileService(new TestContextService(new Workspace(_testDir, _testDir, [uri.file(_testDir)])), textResourceConfigurationService, configurationService, { + const _service = new FileService(new TestContextService(new Workspace(_testDir, _testDir, [aWorkspaceFolder(_testDir, 0)])), textResourceConfigurationService, configurationService, { encodingOverride, disableWatcher: true }); @@ -811,7 +811,7 @@ suite('FileService', () => { const _sourceDir = require.toUrl('./fixtures/service'); const resource = uri.file(path.join(testDir, 'index.html')); - const _service = new FileService(new TestContextService(new Workspace(_testDir, _testDir, [uri.file(_testDir)])), new TestTextResourceConfigurationService(), new TestConfigurationService(), { + const _service = new FileService(new TestContextService(new Workspace(_testDir, _testDir, [aWorkspaceFolder(_testDir, 0)])), new TestTextResourceConfigurationService(), new TestConfigurationService(), { disableWatcher: true }); @@ -852,4 +852,13 @@ suite('FileService', () => { }); }); }); + + function aWorkspaceFolder(raw: string, index: number, name: string = ''): WorkspaceFolder { + return { + uri: uri.file(raw), + index, + raw, + name + }; + } }); diff --git a/src/vs/workbench/services/history/browser/history.ts b/src/vs/workbench/services/history/browser/history.ts index 3b30311fc0c..da81c4635dc 100644 --- a/src/vs/workbench/services/history/browser/history.ts +++ b/src/vs/workbench/services/history/browser/history.ts @@ -780,11 +780,11 @@ export class HistoryService extends BaseHistoryService implements IHistoryServic const resourceInput = input as IResourceInput; const resourceWorkspace = this.contextService.getWorkspaceFolder(resourceInput.resource); if (resourceWorkspace) { - return resourceWorkspace; + return resourceWorkspace.uri; } } // fallback to first workspace - return this.contextService.getWorkspace().folders[0]; + return this.contextService.getWorkspace().folders[0].uri; } } diff --git a/src/vs/workbench/services/keybinding/test/node/keybindingEditing.test.ts b/src/vs/workbench/services/keybinding/test/node/keybindingEditing.test.ts index 72c0fa0e3b7..c0567214e7d 100644 --- a/src/vs/workbench/services/keybinding/test/node/keybindingEditing.test.ts +++ b/src/vs/workbench/services/keybinding/test/node/keybindingEditing.test.ts @@ -74,7 +74,7 @@ suite('Keybindings Editing', () => { instantiationService.stub(ITelemetryService, NullTelemetryService); instantiationService.stub(IModeService, ModeServiceImpl); instantiationService.stub(IModelService, instantiationService.createInstance(ModelServiceImpl)); - instantiationService.stub(IFileService, new FileService(new TestContextService(new Workspace(testDir, testDir, [uri.file(testDir)])), new TestTextResourceConfigurationService(), new TestConfigurationService(), { disableWatcher: true })); + instantiationService.stub(IFileService, new FileService(new TestContextService(new Workspace(testDir, testDir, [{ uri: uri.file(testDir), raw: testDir, index: 0, name: '' }])), new TestTextResourceConfigurationService(), new TestConfigurationService(), { disableWatcher: true })); instantiationService.stub(IUntitledEditorService, instantiationService.createInstance(UntitledEditorService)); instantiationService.stub(ITextFileService, instantiationService.createInstance(TestTextFileService)); instantiationService.stub(ITextModelService, instantiationService.createInstance(TextModelResolverService)); diff --git a/src/vs/workbench/services/telemetry/common/workspaceStats.ts b/src/vs/workbench/services/telemetry/common/workspaceStats.ts index 9975ddf8c80..55ed3e778ec 100644 --- a/src/vs/workbench/services/telemetry/common/workspaceStats.ts +++ b/src/vs/workbench/services/telemetry/common/workspaceStats.ts @@ -157,7 +157,7 @@ export class WorkspaceStats { tags['workspace.roots'] = isEmpty ? 0 : workspace.folders.length; tags['workspace.empty'] = isEmpty; - const folders = !isEmpty ? workspace.folders : this.environmentService.appQuality !== 'stable' && this.findFolders(configuration); + const folders = !isEmpty ? workspace.folders.map(folder => folder.uri) : this.environmentService.appQuality !== 'stable' && this.findFolders(configuration); if (folders && folders.length && this.fileService) { return this.fileService.resolveFiles(folders.map(resource => ({ resource }))).then(results => { const names = ([]).concat(...results.map(result => result.success ? (result.stat.children || []) : [])).map(c => c.name); @@ -324,8 +324,8 @@ export class WorkspaceStats { } public reportCloudStats(): void { - const uris = this.contextService.getWorkspace().folders; - if (uris && uris.length && this.fileService) { + const uris = this.contextService.getWorkspace().folders.map(folder => folder.uri); + if (uris.length && this.fileService) { this.reportRemoteDomains(uris); this.reportRemotes(uris); this.reportAzure(uris); diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts index d112fa773c7..579388facea 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts @@ -746,7 +746,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // Check for workspace settings file return this.contextService.getWorkspace().folders.some(folder => { - return paths.isEqualOrParent(this.resource.fsPath, path.join(folder.fsPath, '.vscode')); + return paths.isEqualOrParent(this.resource.fsPath, path.join(folder.uri.fsPath, '.vscode')); }); } diff --git a/src/vs/workbench/services/workspace/node/workspaceEditingService.ts b/src/vs/workbench/services/workspace/node/workspaceEditingService.ts index ffd1ff7c3b4..ba428d77e9a 100644 --- a/src/vs/workbench/services/workspace/node/workspaceEditingService.ts +++ b/src/vs/workbench/services/workspace/node/workspaceEditingService.ts @@ -36,7 +36,7 @@ export class WorkspaceEditingService implements IWorkspaceEditingService { return TPromise.as(void 0); // we need a workspace to begin with } - const folders = this.contextService.getWorkspace().folders; + const folders = this.contextService.getWorkspace().folders.map(folder => folder.uri); return this.doSetFolders([...folders, ...foldersToAdd]); } @@ -49,7 +49,7 @@ export class WorkspaceEditingService implements IWorkspaceEditingService { const folders = this.contextService.getWorkspace().folders; const foldersToRemoveRaw = foldersToRemove.map(folder => folder.toString()); - return this.doSetFolders(folders.filter(folder => foldersToRemoveRaw.indexOf(folder.toString()) === -1)); + return this.doSetFolders(folders.filter(folder => foldersToRemoveRaw.indexOf(folder.uri.toString()) === -1).map(folder => folder.uri)); } private isSupported(): boolean { @@ -61,7 +61,7 @@ export class WorkspaceEditingService implements IWorkspaceEditingService { private doSetFolders(newFolders: URI[]): TPromise { const workspace = this.contextService.getWorkspace(); - const currentWorkspaceFolders = this.contextService.getWorkspace().folders.map(folder => folder.fsPath); + const currentWorkspaceFolders = this.contextService.getWorkspace().folders.map(folder => folder.uri.fsPath); const newWorkspaceFolders = this.validateFolders(newFolders); // See if there are any changes diff --git a/src/vs/workbench/test/electron-browser/api/extHostConfiguration.test.ts b/src/vs/workbench/test/electron-browser/api/extHostConfiguration.test.ts index d854c4ec6f1..7ef9ad0e9c0 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostConfiguration.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostConfiguration.test.ts @@ -15,6 +15,7 @@ import { ConfigurationTarget, ConfigurationEditingErrorCode, ConfigurationEditin import { ConfigurationModel } from 'vs/platform/configuration/common/configuration'; import { TestThreadService } from './testThreadService'; import { mock } from 'vs/workbench/test/electron-browser/api/mock'; +import { WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; suite('ExtHostConfiguration', function () { @@ -131,7 +132,7 @@ suite('ExtHostConfiguration', function () { new class extends mock() { }, new ExtHostWorkspace(new TestThreadService(), { 'id': 'foo', - 'folders': [URI.file('foo')], + 'folders': [aWorkspaceFolder('foo', 0)], 'name': 'foo' }), { @@ -203,7 +204,7 @@ suite('ExtHostConfiguration', function () { new class extends mock() { }, new ExtHostWorkspace(new TestThreadService(), { 'id': 'foo', - 'folders': [firstRoot, secondRoot], + 'folders': [aWorkspaceFolder(firstRoot.path, 0), aWorkspaceFolder(secondRoot.path, 1)], 'name': 'foo' }), { @@ -402,4 +403,13 @@ suite('ExtHostConfiguration', function () { .update('', true, false) .then(() => assert.ok(false), err => { /* expecting rejection */ }); }); + + function aWorkspaceFolder(raw: string, index: number, name: string = ''): WorkspaceFolder { + return { + uri: URI.file(raw), + index, + raw, + name + }; + } }); diff --git a/src/vs/workbench/test/electron-browser/api/extHostWorkspace.test.ts b/src/vs/workbench/test/electron-browser/api/extHostWorkspace.test.ts index dcde31bf2a6..0eb21a3695f 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostWorkspace.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostWorkspace.test.ts @@ -7,9 +7,11 @@ import * as assert from 'assert'; import URI from 'vs/base/common/uri'; +import { basename } from 'path'; import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace'; import { TestThreadService } from './testThreadService'; import { normalize } from 'vs/base/common/paths'; +import { WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; suite('ExtHostWorkspace', function () { @@ -24,7 +26,7 @@ suite('ExtHostWorkspace', function () { test('asRelativePath', function () { - const ws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', folders: [URI.file('/Coding/Applications/NewsWoWBot')], name: 'Test' }); + const ws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', folders: [aWorkspaceFolder(URI.file('/Coding/Applications/NewsWoWBot'), 0)], name: 'Test' }); assertAsRelativePath(ws, '/Coding/Applications/NewsWoWBot/bernd/das/brot', 'bernd/das/brot'); assertAsRelativePath(ws, '/Apps/DartPubCache/hosted/pub.dartlang.org/convert-2.0.1/lib/src/hex.dart', @@ -38,7 +40,7 @@ suite('ExtHostWorkspace', function () { test('asRelativePath, same paths, #11402', function () { const root = '/home/aeschli/workspaces/samples/docker'; const input = '/home/aeschli/workspaces/samples/docker'; - const ws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', folders: [URI.file(root)], name: 'Test' }); + const ws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', folders: [aWorkspaceFolder(URI.file(root), 0)], name: 'Test' }); assertAsRelativePath(ws, (input), input); @@ -53,14 +55,14 @@ suite('ExtHostWorkspace', function () { }); test('asRelativePath, multiple folders', function () { - const ws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', folders: [URI.file('/Coding/One'), URI.file('/Coding/Two')], name: 'Test' }); + const ws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', folders: [aWorkspaceFolder(URI.file('/Coding/One'), 0), aWorkspaceFolder(URI.file('/Coding/Two'), 1)], name: 'Test' }); assertAsRelativePath(ws, '/Coding/One/file.txt', 'One/file.txt'); assertAsRelativePath(ws, '/Coding/Two/files/out.txt', 'Two/files/out.txt'); assertAsRelativePath(ws, '/Coding/Two2/files/out.txt', '/Coding/Two2/files/out.txt'); }); test('slightly inconsistent behaviour of asRelativePath and getWorkspaceFolder, #31553', function () { - const mrws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', folders: [URI.file('/Coding/One'), URI.file('/Coding/Two')], name: 'Test' }); + const mrws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', folders: [aWorkspaceFolder(URI.file('/Coding/One'), 0), aWorkspaceFolder(URI.file('/Coding/Two'), 1)], name: 'Test' }); assertAsRelativePath(mrws, '/Coding/One/file.txt', 'One/file.txt'); assertAsRelativePath(mrws, '/Coding/One/file.txt', 'One/file.txt', true); @@ -72,7 +74,7 @@ suite('ExtHostWorkspace', function () { assertAsRelativePath(mrws, '/Coding/Two2/files/out.txt', '/Coding/Two2/files/out.txt', true); assertAsRelativePath(mrws, '/Coding/Two2/files/out.txt', '/Coding/Two2/files/out.txt', false); - const srws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', folders: [URI.file('/Coding/One')], name: 'Test' }); + const srws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', folders: [aWorkspaceFolder(URI.file('/Coding/One'), 0)], name: 'Test' }); assertAsRelativePath(srws, '/Coding/One/file.txt', 'file.txt'); assertAsRelativePath(srws, '/Coding/One/file.txt', 'file.txt', false); assertAsRelativePath(srws, '/Coding/One/file.txt', 'One/file.txt', true); @@ -91,15 +93,15 @@ suite('ExtHostWorkspace', function () { ws = new ExtHostWorkspace(new TestThreadService(), undefined); assert.equal(ws.getPath(), undefined); - ws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', name: 'Test', folders: [URI.file('Folder'), URI.file('Another/Folder')] }); + ws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', name: 'Test', folders: [aWorkspaceFolder(URI.file('Folder'), 0), aWorkspaceFolder(URI.file('Another/Folder'), 1)] }); assert.equal(ws.getPath().replace(/\\/g, '/'), '/Folder'); - ws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', name: 'Test', folders: [URI.file('/Folder')] }); + ws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', name: 'Test', folders: [aWorkspaceFolder(URI.file('/Folder'), 0)] }); assert.equal(ws.getPath().replace(/\\/g, '/'), '/Folder'); }); test('WorkspaceFolder has name and index', function () { - const ws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', folders: [URI.file('/Coding/One'), URI.file('/Coding/Two')], name: 'Test' }); + const ws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', folders: [aWorkspaceFolder(URI.file('/Coding/One'), 0), aWorkspaceFolder(URI.file('/Coding/Two'), 1)], name: 'Test' }); const [one, two] = ws.getWorkspaceFolders(); @@ -110,7 +112,7 @@ suite('ExtHostWorkspace', function () { }); test('getContainingWorkspaceFolder', function () { - const ws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', name: 'Test', folders: [URI.file('/Coding/One'), URI.file('/Coding/Two'), URI.file('/Coding/Two/Nested')] }); + const ws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', name: 'Test', folders: [aWorkspaceFolder(URI.file('/Coding/One'), 0), aWorkspaceFolder(URI.file('/Coding/Two'), 1), aWorkspaceFolder(URI.file('/Coding/Two/Nested'), 2)] }); let folder = ws.getWorkspaceFolder(URI.file('/foo/bar')); assert.equal(folder, undefined); @@ -155,7 +157,7 @@ suite('ExtHostWorkspace', function () { assert.equal(e.added.length, 1); assert.equal(e.added[0].uri.toString(), 'foo:bar'); }); - ws.$acceptWorkspaceData({ id: 'foo', name: 'Test', folders: [URI.parse('foo:bar')] }); + ws.$acceptWorkspaceData({ id: 'foo', name: 'Test', folders: [aWorkspaceFolder(URI.parse('foo:bar'), 0)] }); sub.dispose(); sub = ws.onDidChangeWorkspace(e => { @@ -163,7 +165,7 @@ suite('ExtHostWorkspace', function () { assert.equal(e.added.length, 1); assert.equal(e.added[0].uri.toString(), 'foo:bar2'); }); - ws.$acceptWorkspaceData({ id: 'foo', name: 'Test', folders: [URI.parse('foo:bar'), URI.parse('foo:bar2')] }); + ws.$acceptWorkspaceData({ id: 'foo', name: 'Test', folders: [aWorkspaceFolder(URI.parse('foo:bar'), 0), aWorkspaceFolder(URI.parse('foo:bar2'), 1)] }); sub.dispose(); sub = ws.onDidChangeWorkspace(e => { @@ -174,7 +176,7 @@ suite('ExtHostWorkspace', function () { assert.equal(e.added.length, 1); assert.equal(e.added[0].uri.toString(), 'foo:bar3'); }); - ws.$acceptWorkspaceData({ id: 'foo', name: 'Test', folders: [URI.parse('foo:bar3')] }); + ws.$acceptWorkspaceData({ id: 'foo', name: 'Test', folders: [aWorkspaceFolder(URI.parse('foo:bar3'), 0)] }); sub.dispose(); }); @@ -192,4 +194,13 @@ suite('ExtHostWorkspace', function () { ws.$acceptWorkspaceData({ id: 'foo', name: 'Test', folders: [] }); sub.dispose(); }); + + function aWorkspaceFolder(uri: URI, index: number, name: string = ''): WorkspaceFolder { + return { + uri, + index, + raw: uri.toString(), + name: name || basename(uri.path) + }; + } }); diff --git a/src/vs/workbench/test/workbenchTestServices.ts b/src/vs/workbench/test/workbenchTestServices.ts index 80a54b01068..c846b4d4a3b 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, ITextEditorSelection } 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 { IWorkspaceContextService, IWorkspace as IWorkbenchWorkspace, WorkbenchState } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, IWorkspace as IWorkbenchWorkspace, WorkbenchState, WorkspaceFolder } 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'; @@ -90,7 +90,7 @@ export class TestContextService implements IWorkspaceContextService { return this._onDidChangeWorkspaceFolders.event; } - public getFolders(): URI[] { + public getFolders(): WorkspaceFolder[] { return this.workspace ? this.workspace.folders : []; } @@ -108,7 +108,7 @@ export class TestContextService implements IWorkspaceContextService { return this.workspace; } - public getWorkspaceFolder(resource: URI): URI { + public getWorkspaceFolder(resource: URI): WorkspaceFolder { return this.isInsideWorkspace(resource) ? this.workspace.folders[0] : null; } @@ -126,7 +126,7 @@ export class TestContextService implements IWorkspaceContextService { public isInsideWorkspace(resource: URI): boolean { if (resource && this.workspace) { - return paths.isEqualOrParent(resource.fsPath, this.workspace.folders[0].fsPath, !isLinux /* ignorecase */); + return paths.isEqualOrParent(resource.fsPath, this.workspace.folders[0].uri.fsPath, !isLinux /* ignorecase */); } return false; @@ -137,7 +137,7 @@ export class TestContextService implements IWorkspaceContextService { } public isCurrentWorkspace(workspaceIdentifier: ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier): boolean { - return isSingleFolderWorkspaceIdentifier(workspaceIdentifier) && this.pathEquals(this.workspace.folders[0].fsPath, workspaceIdentifier); + return isSingleFolderWorkspaceIdentifier(workspaceIdentifier) && this.pathEquals(this.workspace.folders[0].uri.fsPath, workspaceIdentifier); } private pathEquals(path1: string, path2: string): boolean {