diff --git a/src/vs/code/electron-main/windows.ts b/src/vs/code/electron-main/windows.ts index 9c2ff750b69..9540bd6a526 100644 --- a/src/vs/code/electron-main/windows.ts +++ b/src/vs/code/electron-main/windows.ts @@ -34,7 +34,7 @@ import { normalizeNFC } from 'vs/base/common/normalization'; import { URI } from 'vs/base/common/uri'; import { Queue, timeout } from 'vs/base/common/async'; import { exists } from 'vs/base/node/pfs'; -import { getComparisonKey, isEqual, normalizePath, basename as resourcesBasename } from 'vs/base/common/resources'; +import { getComparisonKey, isEqual, normalizePath, basename as resourcesBasename, fsPath } from 'vs/base/common/resources'; import { endsWith } from 'vs/base/common/strings'; import { getRemoteAuthority } from 'vs/platform/remote/common/remoteHosts'; @@ -102,6 +102,10 @@ interface IFileInputs { remoteAuthority?: string; } +enum URIType { + FILE, FOLDER, WORKSPACE +} + interface IPathToOpen extends IPath { // the workspace for a Code instance to open @@ -438,7 +442,7 @@ export class WindowsManager implements IWindowsMainService { // Make sure to pass focus to the most relevant of the windows if we open multiple if (usedWindows.length > 1) { - let focusLastActive = this.windowsState.lastActiveWindow && !openConfig.forceEmpty && !hasArgs(openConfig.cli._) && !hasArgs(openConfig.cli['file-uri']) && !hasArgs(openConfig.cli['folder-uri']) && !(openConfig.urisToOpen && openConfig.urisToOpen.length); + let focusLastActive = this.windowsState.lastActiveWindow && !openConfig.forceEmpty && !hasArgs(openConfig.cli._) && !hasArgs(openConfig.cli['file-uri']) && !hasArgs(openConfig.cli['folder-uri']) && !hasArgs(openConfig.cli['workspace-uri']) && !(openConfig.urisToOpen && openConfig.urisToOpen.length); let focusLastOpened = true; let focusLastWindow = true; @@ -552,10 +556,9 @@ export class WindowsManager implements IWindowsMainService { let bestWindowOrFolder = findBestWindowOrFolderForFile({ windows, newWindow: openFilesInNewWindow, - reuseWindow: openConfig.forceReuseWindow, context: openConfig.context, fileUri: fileToCheck && fileToCheck.fileUri, - workspaceResolver: workspace => this.workspacesMainService.resolveWorkspaceSync(workspace.configPath) + workspaceResolver: workspace => workspace.configPath.scheme === Schemas.file && this.workspacesMainService.resolveWorkspaceSync(fsPath(workspace.configPath)) }); // We found a window to open the files in @@ -801,7 +804,7 @@ export class WindowsManager implements IWindowsMainService { } // Extract paths: from CLI - else if (hasArgs(openConfig.cli._) || hasArgs(openConfig.cli['folder-uri']) || hasArgs(openConfig.cli['file-uri'])) { + else if (hasArgs(openConfig.cli._) || hasArgs(openConfig.cli['folder-uri']) || hasArgs(openConfig.cli['file-uri']) || hasArgs(openConfig.cli['workspace-uri'])) { windowsToOpen = this.doExtractPathsFromCLI(openConfig.cli); isCommandLineOrAPICall = true; } @@ -838,7 +841,7 @@ export class WindowsManager implements IWindowsMainService { continue; } - const path = this.parseUri(pathToOpen, openConfig.forceOpenWorkspaceAsFile, parseOptions); + const path = this.parseUri(pathToOpen, openConfig.forceOpenWorkspaceAsFile ? URIType.FILE : URIType.FOLDER, parseOptions); if (path) { pathsToOpen.push(path); } else { @@ -874,7 +877,7 @@ export class WindowsManager implements IWindowsMainService { // folder uris const folderUris = asArray(cli['folder-uri']); for (let folderUri of folderUris) { - const path = this.parseUri(this.argToUri(folderUri), false, parseOptions); + const path = this.parseUri(this.argToUri(folderUri), URIType.FOLDER, parseOptions); if (path) { pathsToOpen.push(path); } @@ -883,12 +886,21 @@ export class WindowsManager implements IWindowsMainService { // file uris const fileUris = asArray(cli['file-uri']); for (let fileUri of fileUris) { - const path = this.parseUri(this.argToUri(fileUri), true, parseOptions); + const path = this.parseUri(this.argToUri(fileUri), URIType.FILE, parseOptions); if (path) { pathsToOpen.push(path); } } + const workspaceUris = asArray(cli['workspace-uri']); + for (let workspaceUri of workspaceUris) { + const path = this.parseUri(this.argToUri(workspaceUri), URIType.WORKSPACE, parseOptions); + if (path) { + pathsToOpen.push(path); + } + } + + // folder or file paths const cliArgs = asArray(cli._); for (let cliArg of cliArgs) { @@ -932,12 +944,12 @@ export class WindowsManager implements IWindowsMainService { const windowsToOpen: IPathToOpen[] = []; for (const openedWindow of openedWindows) { if (openedWindow.workspace) { // Workspaces - const pathToOpen = this.parsePath(openedWindow.workspace.configPath, { remoteAuthority: openedWindow.remoteAuthority }); + const pathToOpen = this.parseUri(openedWindow.workspace.configPath, URIType.WORKSPACE, { remoteAuthority: openedWindow.remoteAuthority }); if (pathToOpen && pathToOpen.workspace) { windowsToOpen.push(pathToOpen); } } else if (openedWindow.folderUri) { // Folders - const pathToOpen = this.parseUri(openedWindow.folderUri, false, { remoteAuthority: openedWindow.remoteAuthority }); + const pathToOpen = this.parseUri(openedWindow.folderUri, URIType.FOLDER, { remoteAuthority: openedWindow.remoteAuthority }); if (pathToOpen && pathToOpen.folderUri) { windowsToOpen.push(pathToOpen); } @@ -987,7 +999,7 @@ export class WindowsManager implements IWindowsMainService { return null; } - private parseUri(uri: URI, isFile: boolean, options?: IPathParseOptions): IPathToOpen { + private parseUri(uri: URI, type: URIType, options?: IPathParseOptions): IPathToOpen { if (!uri || !uri.scheme) { return null; } @@ -1004,7 +1016,7 @@ export class WindowsManager implements IWindowsMainService { if (uriPath.length > 2 && endsWith(uriPath, '/')) { uri = uri.with({ path: uriPath.substr(0, uriPath.length - 1) }); } - if (isFile) { + if (type === URIType.FILE) { if (options && options.gotoLineMode) { const parsedPath = parseLineAndColumnAware(uri.path); return { @@ -1018,6 +1030,11 @@ export class WindowsManager implements IWindowsMainService { fileUri: uri, remoteAuthority }; + } else if (type === URIType.WORKSPACE) { + return { + workspace: this.workspacesMainService.getWorkspaceIdentifier(uri), + remoteAuthority + }; } return { folderUri: uri, @@ -1141,6 +1158,7 @@ export class WindowsManager implements IWindowsMainService { } let folderUris = asArray(openConfig.cli['folder-uri']); let fileUris = asArray(openConfig.cli['file-uri']); + let workspaceUris = asArray(openConfig.cli['workspace-uri']); let cliArgs = openConfig.cli._; // Fill in previously opened workspace unless an explicit path is provided and we are not unit testing @@ -1155,7 +1173,11 @@ export class WindowsManager implements IWindowsMainService { folderUris = [workspaceToOpen.toString()]; } } else { - cliArgs = [workspaceToOpen.configPath]; + if (workspaceToOpen.configPath.scheme === Schemas.file) { + cliArgs = [fsPath(workspaceToOpen.configPath)]; + } else { + workspaceUris = [workspaceToOpen.configPath.toString()]; + } } } } @@ -1173,12 +1195,17 @@ export class WindowsManager implements IWindowsMainService { fileUris = []; } + if (workspaceUris.length && workspaceUris.some(uri => !!findWindowOnWorkspaceOrFolderUri(WindowsManager.WINDOWS, this.argToUri(uri)))) { + workspaceUris = []; + } + openConfig.cli._ = cliArgs; openConfig.cli['folder-uri'] = folderUris; openConfig.cli['file-uri'] = fileUris; + openConfig.cli['workspace-uri'] = workspaceUris; // Open it - this.open({ context: openConfig.context, cli: openConfig.cli, forceNewWindow: true, forceEmpty: !cliArgs.length && !folderUris.length && !fileUris.length, userEnv: openConfig.userEnv }); + this.open({ context: openConfig.context, cli: openConfig.cli, forceNewWindow: true, forceEmpty: !cliArgs.length && !folderUris.length && !fileUris.length && !workspaceUris.length, userEnv: openConfig.userEnv }); } private openInBrowserWindow(options: IOpenBrowserWindowOptions): ICodeWindow { @@ -1980,7 +2007,7 @@ class WorkspacesManager { return Promise.resolve(true); } - if (window.openedWorkspace && isEqual(URI.file(window.openedWorkspace.configPath), path)) { + if (window.openedWorkspace && isEqual(window.openedWorkspace.configPath, path)) { return Promise.resolve(false); // window is already opened on a workspace with that path } @@ -2116,7 +2143,7 @@ class WorkspacesManager { return workspace.scheme === Schemas.file ? dirname(workspace.fsPath) : undefined; } - const resolvedWorkspace = this.workspacesMainService.resolveWorkspaceSync(workspace.configPath); + const resolvedWorkspace = workspace.configPath.scheme === Schemas.file && this.workspacesMainService.resolveWorkspaceSync(workspace.configPath.fsPath); if (resolvedWorkspace && resolvedWorkspace.folders.length > 0) { for (const folder of resolvedWorkspace.folders) { if (folder.uri.scheme === Schemas.file) { diff --git a/src/vs/code/node/windowsFinder.ts b/src/vs/code/node/windowsFinder.ts index 607e68dd163..3650ca43c65 100644 --- a/src/vs/code/node/windowsFinder.ts +++ b/src/vs/code/node/windowsFinder.ts @@ -21,7 +21,6 @@ export interface ISimpleWindow { export interface IBestWindowOrFolderOptions { windows: W[]; newWindow: boolean; - reuseWindow: boolean; context: OpenContext; fileUri?: URI; userHome?: string; @@ -29,7 +28,7 @@ export interface IBestWindowOrFolderOptions { workspaceResolver: (workspace: IWorkspaceIdentifier) => IResolvedWorkspace | null; } -export function findBestWindowOrFolderForFile({ windows, newWindow, reuseWindow, context, fileUri, workspaceResolver }: IBestWindowOrFolderOptions): W | null { +export function findBestWindowOrFolderForFile({ windows, newWindow, context, fileUri, workspaceResolver }: IBestWindowOrFolderOptions): W | null { if (!newWindow && fileUri && (context === OpenContext.DESKTOP || context === OpenContext.CLI || context === OpenContext.DOCK)) { const windowOnFilePath = findWindowOnFilePath(windows, fileUri, workspaceResolver); if (windowOnFilePath) { @@ -42,11 +41,21 @@ export function findBestWindowOrFolderForFile({ windows function findWindowOnFilePath(windows: W[], fileUri: URI, workspaceResolver: (workspace: IWorkspaceIdentifier) => IResolvedWorkspace | null): W | null { // First check for windows with workspaces that have a parent folder of the provided path opened - const workspaceWindows = windows.filter(window => !!window.openedWorkspace); - for (const window of workspaceWindows) { - const resolvedWorkspace = workspaceResolver(window.openedWorkspace!); - if (resolvedWorkspace && resolvedWorkspace.folders.some(folder => isEqualOrParent(fileUri, folder.uri))) { - return window; + for (const window of windows) { + const workspace = window.openedWorkspace; + if (workspace) { + const resolvedWorkspace = workspaceResolver(workspace); + if (resolvedWorkspace) { + // workspace cpuld be resolved: It's in the local file system + if (resolvedWorkspace.folders.some(folder => isEqualOrParent(fileUri, folder.uri))) { + return window; + } + } else { + // use the config path instead + if (isEqualOrParent(fileUri, workspace.configPath)) { + return window; + } + } } } @@ -102,7 +111,7 @@ export function findWindowOnWorkspaceOrFolderUri(window } for (const window of windows) { // check for workspace config path - if (window.openedWorkspace && isEqual(URI.file(window.openedWorkspace.configPath), uri, !platform.isLinux /* ignorecase */)) { + if (window.openedWorkspace && isEqual(window.openedWorkspace.configPath, uri)) { return window; } diff --git a/src/vs/code/test/node/windowsFinder.test.ts b/src/vs/code/test/node/windowsFinder.test.ts index 270a7dc04c9..79d476c53d0 100644 --- a/src/vs/code/test/node/windowsFinder.test.ts +++ b/src/vs/code/test/node/windowsFinder.test.ts @@ -15,14 +15,13 @@ const fixturesFolder = getPathFromAmdModule(require, './fixtures'); const testWorkspace: IWorkspaceIdentifier = { id: Date.now().toString(), - configPath: path.join(fixturesFolder, 'workspaces.json') + configPath: URI.file(path.join(fixturesFolder, 'workspaces.json')) }; function options(custom?: Partial>): IBestWindowOrFolderOptions { return { windows: [], newWindow: false, - reuseWindow: false, context: OpenContext.CLI, codeSettingsFolder: '_vscode', workspaceResolver: workspace => { return workspace === testWorkspace ? { id: testWorkspace.id, configPath: workspace.configPath, folders: toWorkspaceFolders([{ path: path.join(fixturesFolder, 'vscode_workspace_1_folder') }, { path: path.join(fixturesFolder, 'vscode_workspace_2_folder') }]) } : null!; }, @@ -52,7 +51,6 @@ suite('WindowsFinder', () => { })), null); assert.equal(findBestWindowOrFolderForFile(options({ fileUri: URI.file(path.join(fixturesFolder, 'vscode_folder', 'file.txt')), - reuseWindow: true })), null); assert.equal(findBestWindowOrFolderForFile(options({ fileUri: URI.file(path.join(fixturesFolder, 'vscode_folder', 'file.txt')), @@ -85,7 +83,6 @@ suite('WindowsFinder', () => { assert.equal(findBestWindowOrFolderForFile(options({ windows: [lastActiveWindow, noVscodeFolderWindow], fileUri: URI.file(path.join(fixturesFolder, 'vscode_folder', 'file.txt')), - reuseWindow: true })), lastActiveWindow); assert.equal(findBestWindowOrFolderForFile(options({ windows, diff --git a/src/vs/platform/backup/electron-main/backupMainService.ts b/src/vs/platform/backup/electron-main/backupMainService.ts index 98a462fde33..4b258d2d1a2 100644 --- a/src/vs/platform/backup/electron-main/backupMainService.ts +++ b/src/vs/platform/backup/electron-main/backupMainService.ts @@ -253,7 +253,7 @@ export class BackupMainService implements IBackupMainService { // If the workspace has no backups, ignore it if (hasBackups) { - if (await exists(workspace.configPath)) { + if (workspace.configPath.scheme !== Schemas.file || await exists(workspace.configPath.fsPath)) { result.push(workspace); } else { // If the workspace has backups, but the target workspace is missing, convert backups to empty ones diff --git a/src/vs/platform/backup/test/electron-main/backupMainService.test.ts b/src/vs/platform/backup/test/electron-main/backupMainService.test.ts index 35808ca924e..e1c1d159fb2 100644 --- a/src/vs/platform/backup/test/electron-main/backupMainService.test.ts +++ b/src/vs/platform/backup/test/electron-main/backupMainService.test.ts @@ -9,7 +9,7 @@ import * as fs from 'fs'; import * as os from 'os'; import * as path from 'path'; import * as pfs from 'vs/base/node/pfs'; -import { URI as Uri } from 'vs/base/common/uri'; +import { URI as Uri, URI } from 'vs/base/common/uri'; import { EnvironmentService } from 'vs/platform/environment/node/environmentService'; import { parseArgs } from 'vs/platform/environment/node/argv'; import { BackupMainService } from 'vs/platform/backup/electron-main/backupMainService'; @@ -60,7 +60,7 @@ suite('BackupMainService', () => { function toWorkspace(path: string): IWorkspaceIdentifier { return { id: createHash('md5').update(sanitizePath(path)).digest('hex'), - configPath: path + configPath: URI.file(path) }; } @@ -73,8 +73,8 @@ suite('BackupMainService', () => { } async function ensureWorkspaceExists(workspace: IWorkspaceIdentifier): Promise { - if (!fs.existsSync(workspace.configPath)) { - await pfs.writeFile(workspace.configPath, 'Hello'); + if (!fs.existsSync(workspace.configPath.fsPath)) { + await pfs.writeFile(workspace.configPath.fsPath, 'Hello'); } const backupFolder = service.toBackupPath(workspace.id); await createBackupFolder(backupFolder); diff --git a/src/vs/platform/environment/common/environment.ts b/src/vs/platform/environment/common/environment.ts index 23e84ca2f83..e13c736429f 100644 --- a/src/vs/platform/environment/common/environment.ts +++ b/src/vs/platform/environment/common/environment.ts @@ -11,6 +11,7 @@ export interface ParsedArgs { _: string[]; 'folder-uri'?: string | string[]; 'file-uri'?: string | string[]; + 'workspace-uri'?: string | string[]; _urls?: string[]; help?: boolean; version?: boolean; diff --git a/src/vs/platform/environment/node/argv.ts b/src/vs/platform/environment/node/argv.ts index d7e62cbfd82..7b0a9c431b8 100644 --- a/src/vs/platform/environment/node/argv.ts +++ b/src/vs/platform/environment/node/argv.ts @@ -40,6 +40,7 @@ export const options: Option[] = [ { id: 'help', type: 'boolean', cat: 'o', alias: 'h', description: localize('help', "Print usage.") }, { id: 'folder-uri', type: 'string', cat: 'o', args: 'uri', description: localize('folderUri', "Opens a window with given folder uri(s)") }, { id: 'file-uri', type: 'string', cat: 'o', args: 'uri', description: localize('fileUri', "Opens a window with given file uri(s)") }, + { id: 'workspace-uri', type: 'string', cat: 'o', args: 'uri', description: localize('workspaceUri', "Opens a window with given workspace file") }, { id: 'extensions-dir', type: 'string', cat: 'e', args: 'dir', description: localize('extensionHomePath', "Set the root path for extensions.") }, { id: 'list-extensions', type: 'boolean', cat: 'e', description: localize('listExtensions', "List the installed extensions.") }, diff --git a/src/vs/platform/history/electron-main/historyMainService.ts b/src/vs/platform/history/electron-main/historyMainService.ts index 1b0d20dc036..a4ff2c0242b 100644 --- a/src/vs/platform/history/electron-main/historyMainService.ts +++ b/src/vs/platform/history/electron-main/historyMainService.ts @@ -16,7 +16,7 @@ import { IWorkspaceIdentifier, IWorkspacesMainService, ISingleFolderWorkspaceIde import { IHistoryMainService, IRecentlyOpened } from 'vs/platform/history/common/history'; import { isEqual } from 'vs/base/common/paths'; import { RunOnceScheduler } from 'vs/base/common/async'; -import { getComparisonKey, isEqual as areResourcesEqual, dirname } from 'vs/base/common/resources'; +import { getComparisonKey, isEqual as areResourcesEqual, dirname, fsPath } from 'vs/base/common/resources'; import { URI, UriComponents } from 'vs/base/common/uri'; import { Schemas } from 'vs/base/common/network'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; @@ -112,7 +112,7 @@ export class HistoryMainService implements IHistoryMainService { // Remove workspace let index = arrays.firstIndex(mru.workspaces, workspace => { if (isWorkspaceIdentifier(pathToRemove)) { - return isWorkspaceIdentifier(workspace) && isEqual(pathToRemove.configPath, workspace.configPath, !isLinux /* ignorecase */); + return isWorkspaceIdentifier(workspace) && areResourcesEqual(pathToRemove.configPath, workspace.configPath, !isLinux /* ignorecase */); } if (isSingleFolderWorkspaceIdentifier(pathToRemove)) { return isSingleFolderWorkspaceIdentifier(workspace) && areResourcesEqual(pathToRemove, workspace); @@ -122,7 +122,7 @@ export class HistoryMainService implements IHistoryMainService { return workspace.scheme === Schemas.file && isEqual(pathToRemove, workspace.fsPath, !isLinux /* ignorecase */); } if (isWorkspaceIdentifier(workspace)) { - return isEqual(pathToRemove, workspace.configPath, !isLinux /* ignorecase */); + return workspace.configPath.scheme === Schemas.file && isEqual(pathToRemove, workspace.configPath.fsPath, !isLinux /* ignorecase */); } } return false; @@ -178,12 +178,14 @@ export class HistoryMainService implements IHistoryMainService { const workspace = mru.workspaces[i]; if (isSingleFolderWorkspaceIdentifier(workspace)) { if (workspace.scheme === Schemas.file) { - app.addRecentDocument(workspace.fsPath); + app.addRecentDocument(fsPath(workspace)); entries++; } } else { - app.addRecentDocument(workspace.configPath); - entries++; + if (workspace.configPath.scheme === Schemas.file) { + app.addRecentDocument(fsPath(workspace.configPath)); + entries++; + } } } @@ -192,7 +194,7 @@ export class HistoryMainService implements IHistoryMainService { for (let i = 0; i < mru.files.length && entries < HistoryMainService.MAX_MACOS_DOCK_RECENT_FILES; i++) { const file = mru.files[i]; if (file.scheme === Schemas.file) { - app.addRecentDocument(file.fsPath); + app.addRecentDocument(fsPath(file)); entries++; } } @@ -345,14 +347,9 @@ export class HistoryMainService implements IHistoryMainService { for (let item of app.getJumpListSettings().removedItems) { const args = item.args; if (args) { - const match = /^--folder-uri\s+"([^"]+)"$/.exec(args); + const match = /^--(folder|workspace)-uri\s+"([^"]+)"$/.exec(args); if (match) { - if (args[0] === '-') { - toRemove.push(URI.parse(match[1])); - } else { - let configPath = match[1]; - toRemove.push({ id: this.workspacesMainService.getWorkspaceId(configPath), configPath }); - } + toRemove.push(URI.parse(match[2])); } } } @@ -372,7 +369,7 @@ export class HistoryMainService implements IHistoryMainService { args = `--folder-uri "${workspace.toString()}"`; } else { description = nls.localize('codeWorkspace', "Code Workspace"); - args = `"${workspace.configPath}"`; + args = `--workspace-uri "${workspace.configPath.toString()}"`; } return { type: 'task', diff --git a/src/vs/platform/label/common/label.ts b/src/vs/platform/label/common/label.ts index 6f018648f98..4bd34446d4f 100644 --- a/src/vs/platform/label/common/label.ts +++ b/src/vs/platform/label/common/label.ts @@ -8,12 +8,9 @@ import { IDisposable } from 'vs/base/common/lifecycle'; import { Event } from 'vs/base/common/event'; import { IWorkspace } from 'vs/platform/workspace/common/workspace'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { basename as resourceBasename } from 'vs/base/common/resources'; -import { isLinux } from 'vs/base/common/platform'; import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, WORKSPACE_EXTENSION } from 'vs/platform/workspaces/common/workspaces'; import { localize } from 'vs/nls'; -import { isParent } from 'vs/platform/files/common/files'; -import { basename } from 'vs/base/common/paths'; +import { isEqualOrParent, basename } from 'vs/base/common/resources'; export interface ILabelService { _serviceBrand: any; @@ -48,10 +45,10 @@ const LABEL_SERVICE_ID = 'label'; export function getSimpleWorkspaceLabel(workspace: IWorkspaceIdentifier | URI, workspaceHome: string): string { if (isSingleFolderWorkspaceIdentifier(workspace)) { - return resourceBasename(workspace); + return basename(workspace); } // Workspace: Untitled - if (isParent(workspace.configPath, workspaceHome, !isLinux /* ignore case */)) { + if (isEqualOrParent(workspace.configPath, URI.file(workspaceHome))) { return localize('untitledWorkspace', "Untitled (Workspace)"); } diff --git a/src/vs/platform/launch/electron-main/launchService.ts b/src/vs/platform/launch/electron-main/launchService.ts index ba14b195335..aa77f122c84 100644 --- a/src/vs/platform/launch/electron-main/launchService.ts +++ b/src/vs/platform/launch/electron-main/launchService.ts @@ -19,6 +19,7 @@ import { BrowserWindow } from 'electron'; import { Event } from 'vs/base/common/event'; import { hasArgs } from 'vs/platform/environment/node/argv'; import { coalesce } from 'vs/base/common/arrays'; +import { Schemas } from 'vs/base/common/network'; export const ID = 'launchService'; export const ILaunchService = createDecorator(ID); @@ -270,12 +271,16 @@ export class LaunchService implements ILaunchService { if (window.openedFolderUri) { folderURIs.push(window.openedFolderUri); } else if (window.openedWorkspace) { - const resolvedWorkspace = this.workspacesMainService.resolveWorkspaceSync(window.openedWorkspace.configPath); + // workspace folders can only be shown for local workspaces + const workspaceConfigPath = window.openedWorkspace.configPath; + const resolvedWorkspace = workspaceConfigPath.scheme === Schemas.file && this.workspacesMainService.resolveWorkspaceSync(workspaceConfigPath.fsPath); if (resolvedWorkspace) { const rootFolders = resolvedWorkspace.folders; rootFolders.forEach(root => { folderURIs.push(root.uri); }); + } else { + //TODO: can we add the workspace file here? } } diff --git a/src/vs/platform/workspaces/common/workspaces.ts b/src/vs/platform/workspaces/common/workspaces.ts index ec81bacefb9..a5b8c5a57b8 100644 --- a/src/vs/platform/workspaces/common/workspaces.ts +++ b/src/vs/platform/workspaces/common/workspaces.ts @@ -23,7 +23,7 @@ export type ISingleFolderWorkspaceIdentifier = URI; export interface IWorkspaceIdentifier { id: string; - configPath: string; + configPath: URI; } export function isStoredWorkspaceFolder(thing: any): thing is IStoredWorkspaceFolder { @@ -91,8 +91,6 @@ export interface IWorkspacesMainService extends IWorkspacesService { getUntitledWorkspacesSync(): IWorkspaceIdentifier[]; - getWorkspaceId(workspacePath: string): string; - getWorkspaceIdentifier(workspacePath: URI): IWorkspaceIdentifier; } @@ -115,7 +113,7 @@ export function isWorkspaceIdentifier(obj: any): obj is IWorkspaceIdentifier { export function toWorkspaceIdentifier(workspace: IWorkspace): IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | undefined { if (workspace.configuration) { return { - configPath: workspace.configuration.fsPath, + configPath: workspace.configuration, id: workspace.id }; } diff --git a/src/vs/platform/workspaces/electron-main/workspacesMainService.ts b/src/vs/platform/workspaces/electron-main/workspacesMainService.ts index 015156751a9..a9a6242f1d3 100644 --- a/src/vs/platform/workspaces/electron-main/workspacesMainService.ts +++ b/src/vs/platform/workspaces/electron-main/workspacesMainService.ts @@ -14,7 +14,6 @@ import { delSync, readdirSync, writeFileAndFlushSync } from 'vs/base/node/extfs' import { Event, Emitter } from 'vs/base/common/event'; import { ILogService } from 'vs/platform/log/common/log'; import { isEqual } from 'vs/base/common/paths'; -import { coalesce } from 'vs/base/common/arrays'; import { createHash } from 'crypto'; import * as json from 'vs/base/common/json'; import { massageFolderPathForWorkspace, rewriteWorkspaceFileForNewLocation } from 'vs/platform/workspaces/node/workspaces'; @@ -107,7 +106,7 @@ export class WorkspacesMainService extends Disposable implements IWorkspacesMain const { workspace, configParent, storedWorkspace } = this.newUntitledWorkspace(folders); return mkdirp(configParent).then(() => { - return writeFile(workspace.configPath, JSON.stringify(storedWorkspace, null, '\t')).then(() => workspace); + return writeFile(workspace.configPath.fsPath, JSON.stringify(storedWorkspace, null, '\t')).then(() => workspace); }); } @@ -120,7 +119,7 @@ export class WorkspacesMainService extends Disposable implements IWorkspacesMain mkdirSync(configParent); - writeFileAndFlushSync(workspace.configPath, JSON.stringify(storedWorkspace, null, '\t')); + writeFileAndFlushSync(workspace.configPath.fsPath, JSON.stringify(storedWorkspace, null, '\t')); return workspace; } @@ -154,16 +153,14 @@ export class WorkspacesMainService extends Disposable implements IWorkspacesMain }; return { - workspace: { - id: this.getWorkspaceId(untitledWorkspaceConfigPath), - configPath: untitledWorkspaceConfigPath - }, + workspace: this.getWorkspaceIdentifier(URI.file(untitledWorkspaceConfigPath)), configParent: untitledWorkspaceConfigFolder, storedWorkspace }; } - getWorkspaceId(workspaceConfigPath: string): string { + getWorkspaceId(configPath: URI): string { + let workspaceConfigPath = configPath.scheme === Schemas.file ? fsPath(configPath) : configPath.toString(); if (!isLinux) { workspaceConfigPath = workspaceConfigPath.toLowerCase(); // sanitize for platform file system } @@ -171,36 +168,34 @@ export class WorkspacesMainService extends Disposable implements IWorkspacesMain return createHash('md5').update(workspaceConfigPath).digest('hex'); } - getWorkspaceIdentifier(workspacePath: URI): IWorkspaceIdentifier { - if (workspacePath.scheme === Schemas.file) { - const configPath = fsPath(workspacePath); - return { - configPath, - id: this.getWorkspaceId(configPath) - }; - } - throw new Error('Not yet supported'); - /*return { - configPath: workspacePath - id: this.getWorkspaceId(workspacePath.toString()); - };*/ + getWorkspaceIdentifier(configPath: URI): IWorkspaceIdentifier { + return { + configPath, + id: this.getWorkspaceId(configPath) + }; } isUntitledWorkspace(workspace: IWorkspaceIdentifier): boolean { - return this.isInsideWorkspacesHome(workspace.configPath); + return workspace.configPath.scheme === Schemas.file && this.isInsideWorkspacesHome(fsPath(workspace.configPath)); } saveWorkspaceAs(workspace: IWorkspaceIdentifier, targetConfigPath: string): Promise { + if (workspace.configPath.scheme !== Schemas.file) { + throw new Error('Only local workspaces can be saved with this API. Use WorkspaceEditingService.saveWorkspaceAs on the renderer instead.'); + } + + const configPath = fsPath(workspace.configPath); + // Return early if target is same as source - if (isEqual(workspace.configPath, targetConfigPath, !isLinux)) { + if (isEqual(configPath, targetConfigPath, !isLinux)) { return Promise.resolve(workspace); } // Read the contents of the workspace file and resolve it - return readFile(workspace.configPath).then(raw => { + return readFile(configPath).then(raw => { const targetConfigPathURI = URI.file(targetConfigPath); - const newRawWorkspaceContents = rewriteWorkspaceFileForNewLocation(raw.toString(), URI.file(workspace.configPath), targetConfigPathURI); + const newRawWorkspaceContents = rewriteWorkspaceFileForNewLocation(raw.toString(), workspace.configPath, targetConfigPathURI); return writeFile(targetConfigPath, newRawWorkspaceContents).then(() => { return this.getWorkspaceIdentifier(targetConfigPathURI); @@ -214,20 +209,20 @@ export class WorkspacesMainService extends Disposable implements IWorkspacesMain } // Delete from disk - this.doDeleteUntitledWorkspaceSync(workspace.configPath); + this.doDeleteUntitledWorkspaceSync(workspace); // Event this._onUntitledWorkspaceDeleted.fire(workspace); } - private doDeleteUntitledWorkspaceSync(configPath: string): void { + private doDeleteUntitledWorkspaceSync(workspace: IWorkspaceIdentifier): void { + const configPath = fsPath(workspace.configPath); try { - // Delete Workspace delSync(dirname(configPath)); // Mark Workspace Storage to be deleted - const workspaceStoragePath = join(this.environmentService.workspaceStorageHome, this.getWorkspaceId(configPath)); + const workspaceStoragePath = join(this.environmentService.workspaceStorageHome, workspace.id); if (existsSync(workspaceStoragePath)) { writeFileSync(join(workspaceStoragePath, 'obsolete'), ''); } @@ -237,26 +232,22 @@ export class WorkspacesMainService extends Disposable implements IWorkspacesMain } getUntitledWorkspacesSync(): IWorkspaceIdentifier[] { - let untitledWorkspacePaths: string[] = []; + let untitledWorkspaces: IWorkspaceIdentifier[] = []; try { - untitledWorkspacePaths = readdirSync(this.workspacesHome).map(folder => join(this.workspacesHome, folder, UNTITLED_WORKSPACE_NAME)); + const untitledWorkspacePaths = readdirSync(this.workspacesHome).map(folder => join(this.workspacesHome, folder, UNTITLED_WORKSPACE_NAME)); + for (const untitledWorkspacePath of untitledWorkspacePaths) { + const workspace = this.getWorkspaceIdentifier(URI.file(untitledWorkspacePath)); + if (!this.resolveWorkspaceSync(untitledWorkspacePath)) { + this.doDeleteUntitledWorkspaceSync(workspace); + } else { + untitledWorkspaces.push(workspace); + } + } } catch (error) { if (error && error.code !== 'ENOENT') { this.logService.warn(`Unable to read folders in ${this.workspacesHome} (${error}).`); } } - - const untitledWorkspaces: IWorkspaceIdentifier[] = coalesce(untitledWorkspacePaths.map(untitledWorkspacePath => { - const workspace = this.resolveWorkspaceSync(untitledWorkspacePath); - if (!workspace) { - this.doDeleteUntitledWorkspaceSync(untitledWorkspacePath); - - return null; // invalid workspace - } - - return { id: workspace.id, configPath: untitledWorkspacePath }; - })); - return untitledWorkspaces; } } diff --git a/src/vs/platform/workspaces/test/electron-main/workspacesMainService.test.ts b/src/vs/platform/workspaces/test/electron-main/workspacesMainService.test.ts index 914930bb9ff..09475dc928e 100644 --- a/src/vs/platform/workspaces/test/electron-main/workspacesMainService.test.ts +++ b/src/vs/platform/workspaces/test/electron-main/workspacesMainService.test.ts @@ -77,10 +77,10 @@ suite('WorkspacesMainService', () => { test('createWorkspace (folders)', () => { return createWorkspace([process.cwd(), os.tmpdir()]).then(workspace => { assert.ok(workspace); - assert.ok(fs.existsSync(workspace.configPath)); + assert.ok(fs.existsSync(workspace.configPath.fsPath)); assert.ok(service.isUntitledWorkspace(workspace)); - const ws = JSON.parse(fs.readFileSync(workspace.configPath).toString()) as IStoredWorkspace; + const ws = JSON.parse(fs.readFileSync(workspace.configPath.fsPath).toString()) as IStoredWorkspace; assert.equal(ws.folders.length, 2); // assertPathEquals((ws.folders[0]).path, process.cwd()); assertPathEquals((ws.folders[1]).path, os.tmpdir()); @@ -93,10 +93,10 @@ suite('WorkspacesMainService', () => { test('createWorkspace (folders with name)', () => { return createWorkspace([process.cwd(), os.tmpdir()], ['currentworkingdirectory', 'tempdir']).then(workspace => { assert.ok(workspace); - assert.ok(fs.existsSync(workspace.configPath)); + assert.ok(fs.existsSync(workspace.configPath.fsPath)); assert.ok(service.isUntitledWorkspace(workspace)); - const ws = JSON.parse(fs.readFileSync(workspace.configPath).toString()) as IStoredWorkspace; + const ws = JSON.parse(fs.readFileSync(workspace.configPath.fsPath).toString()) as IStoredWorkspace; assert.equal(ws.folders.length, 2); // assertPathEquals((ws.folders[0]).path, process.cwd()); assertPathEquals((ws.folders[1]).path, os.tmpdir()); @@ -109,10 +109,10 @@ suite('WorkspacesMainService', () => { test('createUntitledWorkspace (folders as other resource URIs)', () => { return service.createUntitledWorkspace([{ uri: URI.from({ scheme: 'myScheme', path: process.cwd() }) }, { uri: URI.from({ scheme: 'myScheme', path: os.tmpdir() }) }]).then(workspace => { assert.ok(workspace); - assert.ok(fs.existsSync(workspace.configPath)); + assert.ok(fs.existsSync(workspace.configPath.fsPath)); assert.ok(service.isUntitledWorkspace(workspace)); - const ws = JSON.parse(fs.readFileSync(workspace.configPath).toString()) as IStoredWorkspace; + const ws = JSON.parse(fs.readFileSync(workspace.configPath.fsPath).toString()) as IStoredWorkspace; assert.equal(ws.folders.length, 2); assert.equal((ws.folders[0]).uri, URI.from({ scheme: 'myScheme', path: process.cwd() }).toString(true)); assert.equal((ws.folders[1]).uri, URI.from({ scheme: 'myScheme', path: os.tmpdir() }).toString(true)); @@ -125,10 +125,10 @@ suite('WorkspacesMainService', () => { test('createWorkspaceSync (folders)', () => { const workspace = createWorkspaceSync([process.cwd(), os.tmpdir()]); assert.ok(workspace); - assert.ok(fs.existsSync(workspace.configPath)); + assert.ok(fs.existsSync(workspace.configPath.fsPath)); assert.ok(service.isUntitledWorkspace(workspace)); - const ws = JSON.parse(fs.readFileSync(workspace.configPath).toString()) as IStoredWorkspace; + const ws = JSON.parse(fs.readFileSync(workspace.configPath.fsPath).toString()) as IStoredWorkspace; assert.equal(ws.folders.length, 2); assertPathEquals((ws.folders[0]).path, process.cwd()); assertPathEquals((ws.folders[1]).path, os.tmpdir()); @@ -140,10 +140,10 @@ suite('WorkspacesMainService', () => { test('createWorkspaceSync (folders with names)', () => { const workspace = createWorkspaceSync([process.cwd(), os.tmpdir()], ['currentworkingdirectory', 'tempdir']); assert.ok(workspace); - assert.ok(fs.existsSync(workspace.configPath)); + assert.ok(fs.existsSync(workspace.configPath.fsPath)); assert.ok(service.isUntitledWorkspace(workspace)); - const ws = JSON.parse(fs.readFileSync(workspace.configPath).toString()) as IStoredWorkspace; + const ws = JSON.parse(fs.readFileSync(workspace.configPath.fsPath).toString()) as IStoredWorkspace; assert.equal(ws.folders.length, 2); assertPathEquals((ws.folders[0]).path, process.cwd()); assertPathEquals((ws.folders[1]).path, os.tmpdir()); @@ -155,10 +155,10 @@ suite('WorkspacesMainService', () => { test('createUntitledWorkspaceSync (folders as other resource URIs)', () => { const workspace = service.createUntitledWorkspaceSync([{ uri: URI.from({ scheme: 'myScheme', path: process.cwd() }) }, { uri: URI.from({ scheme: 'myScheme', path: os.tmpdir() }) }]); assert.ok(workspace); - assert.ok(fs.existsSync(workspace.configPath)); + assert.ok(fs.existsSync(workspace.configPath.fsPath)); assert.ok(service.isUntitledWorkspace(workspace)); - const ws = JSON.parse(fs.readFileSync(workspace.configPath).toString()) as IStoredWorkspace; + const ws = JSON.parse(fs.readFileSync(workspace.configPath.fsPath).toString()) as IStoredWorkspace; assert.equal(ws.folders.length, 2); assert.equal((ws.folders[0]).uri, URI.from({ scheme: 'myScheme', path: process.cwd() }).toString(true)); assert.equal((ws.folders[1]).uri, URI.from({ scheme: 'myScheme', path: os.tmpdir() }).toString(true)); @@ -169,57 +169,57 @@ suite('WorkspacesMainService', () => { test('resolveWorkspaceSync', () => { return createWorkspace([process.cwd(), os.tmpdir()]).then(workspace => { - assert.ok(service.resolveWorkspaceSync(workspace.configPath)); + assert.ok(service.resolveWorkspaceSync(workspace.configPath.fsPath)); // make it a valid workspace path - const newPath = path.join(path.dirname(workspace.configPath), `workspace.${WORKSPACE_EXTENSION}`); - fs.renameSync(workspace.configPath, newPath); - workspace.configPath = newPath; + const newPath = path.join(path.dirname(workspace.configPath.fsPath), `workspace.${WORKSPACE_EXTENSION}`); + fs.renameSync(workspace.configPath.fsPath, newPath); + workspace.configPath = URI.file(newPath); - const resolved = service.resolveWorkspaceSync(workspace.configPath); + const resolved = service.resolveWorkspaceSync(workspace.configPath.fsPath); assert.equal(2, resolved!.folders.length); assert.equal(resolved!.configPath, workspace.configPath); assert.ok(resolved!.id); - fs.writeFileSync(workspace.configPath, JSON.stringify({ something: 'something' })); // invalid workspace - const resolvedInvalid = service.resolveWorkspaceSync(workspace.configPath); + fs.writeFileSync(workspace.configPath.fsPath, JSON.stringify({ something: 'something' })); // invalid workspace + const resolvedInvalid = service.resolveWorkspaceSync(workspace.configPath.fsPath); assert.ok(!resolvedInvalid); }); }); test('resolveWorkspaceSync (support relative paths)', () => { return createWorkspace([process.cwd(), os.tmpdir()]).then(workspace => { - fs.writeFileSync(workspace.configPath, JSON.stringify({ folders: [{ path: './ticino-playground/lib' }] })); + fs.writeFileSync(workspace.configPath.fsPath, JSON.stringify({ folders: [{ path: './ticino-playground/lib' }] })); - const resolved = service.resolveWorkspaceSync(workspace.configPath); - assert.equal(resolved!.folders[0].uri.fsPath, URI.file(path.join(path.dirname(workspace.configPath), 'ticino-playground', 'lib')).fsPath); + const resolved = service.resolveWorkspaceSync(workspace.configPath.fsPath); + assert.equal(resolved!.folders[0].uri.fsPath, URI.file(path.join(path.dirname(workspace.configPath.fsPath), 'ticino-playground', 'lib')).fsPath); }); }); test('resolveWorkspaceSync (support relative paths #2)', () => { return createWorkspace([process.cwd(), os.tmpdir()]).then(workspace => { - fs.writeFileSync(workspace.configPath, JSON.stringify({ folders: [{ path: './ticino-playground/lib/../other' }] })); + fs.writeFileSync(workspace.configPath.fsPath, JSON.stringify({ folders: [{ path: './ticino-playground/lib/../other' }] })); - const resolved = service.resolveWorkspaceSync(workspace.configPath); - assert.equal(resolved!.folders[0].uri.fsPath, URI.file(path.join(path.dirname(workspace.configPath), 'ticino-playground', 'other')).fsPath); + const resolved = service.resolveWorkspaceSync(workspace.configPath.fsPath); + assert.equal(resolved!.folders[0].uri.fsPath, URI.file(path.join(path.dirname(workspace.configPath.fsPath), 'ticino-playground', 'other')).fsPath); }); }); test('resolveWorkspaceSync (support relative paths #3)', () => { return createWorkspace([process.cwd(), os.tmpdir()]).then(workspace => { - fs.writeFileSync(workspace.configPath, JSON.stringify({ folders: [{ path: 'ticino-playground/lib' }] })); + fs.writeFileSync(workspace.configPath.fsPath, JSON.stringify({ folders: [{ path: 'ticino-playground/lib' }] })); - const resolved = service.resolveWorkspaceSync(workspace.configPath); - assert.equal(resolved!.folders[0].uri.fsPath, URI.file(path.join(path.dirname(workspace.configPath), 'ticino-playground', 'lib')).fsPath); + const resolved = service.resolveWorkspaceSync(workspace.configPath.fsPath); + assert.equal(resolved!.folders[0].uri.fsPath, URI.file(path.join(path.dirname(workspace.configPath.fsPath), 'ticino-playground', 'lib')).fsPath); }); }); test('resolveWorkspaceSync (support invalid JSON via fault tolerant parsing)', () => { return createWorkspace([process.cwd(), os.tmpdir()]).then(workspace => { - fs.writeFileSync(workspace.configPath, '{ "folders": [ { "path": "./ticino-playground/lib" } , ] }'); // trailing comma + fs.writeFileSync(workspace.configPath.fsPath, '{ "folders": [ { "path": "./ticino-playground/lib" } , ] }'); // trailing comma - const resolved = service.resolveWorkspaceSync(workspace.configPath); - assert.equal(resolved!.folders[0].uri.fsPath, URI.file(path.join(path.dirname(workspace.configPath), 'ticino-playground', 'lib')).fsPath); + const resolved = service.resolveWorkspaceSync(workspace.configPath.fsPath); + assert.equal(resolved!.folders[0].uri.fsPath, URI.file(path.join(path.dirname(workspace.configPath.fsPath), 'ticino-playground', 'lib')).fsPath); }); }); @@ -232,7 +232,7 @@ suite('WorkspacesMainService', () => { assert.notEqual(savedWorkspace.id, workspace.id); assert.equal(savedWorkspace.configPath, workspaceConfigPath); - const ws = JSON.parse(fs.readFileSync(savedWorkspace.configPath).toString()) as IStoredWorkspace; + const ws = JSON.parse(fs.readFileSync(savedWorkspace.configPath.fsPath).toString()) as IStoredWorkspace; assert.equal(ws.folders.length, 3); assertPathEquals((ws.folders[0]).path, process.cwd()); // absolute assertPathEquals((ws.folders[1]).path, '.'); // relative @@ -252,9 +252,9 @@ suite('WorkspacesMainService', () => { return service.saveWorkspaceAs(savedWorkspace, newWorkspaceConfigPath).then(newSavedWorkspace => { assert.ok(newSavedWorkspace.id); assert.notEqual(newSavedWorkspace.id, workspace.id); - assertPathEquals(newSavedWorkspace.configPath, newWorkspaceConfigPath); + assertPathEquals(newSavedWorkspace.configPath.fsPath, newWorkspaceConfigPath); - const ws = JSON.parse(fs.readFileSync(newSavedWorkspace.configPath).toString()) as IStoredWorkspace; + const ws = JSON.parse(fs.readFileSync(newSavedWorkspace.configPath.fsPath).toString()) as IStoredWorkspace; assert.equal(ws.folders.length, 3); assertPathEquals((ws.folders[0]).path, process.cwd()); // absolute path because outside of tmpdir assertPathEquals((ws.folders[1]).path, '.'); // relative path because inside of tmpdir @@ -273,15 +273,15 @@ suite('WorkspacesMainService', () => { const newWorkspaceConfigPath = path.join(os.tmpdir(), `mySavedWorkspace.${Date.now()}.${WORKSPACE_EXTENSION}`); return service.saveWorkspaceAs(workspace, workspaceConfigPath).then(savedWorkspace => { - const contents = fs.readFileSync(savedWorkspace.configPath).toString(); - fs.writeFileSync(savedWorkspace.configPath, `// this is a comment\n${contents}`); + const contents = fs.readFileSync(savedWorkspace.configPath.fsPath).toString(); + fs.writeFileSync(savedWorkspace.configPath.fsPath, `// this is a comment\n${contents}`); return service.saveWorkspaceAs(savedWorkspace, newWorkspaceConfigPath).then(newSavedWorkspace => { assert.ok(newSavedWorkspace.id); assert.notEqual(newSavedWorkspace.id, workspace.id); - assertPathEquals(newSavedWorkspace.configPath, newWorkspaceConfigPath); + assertPathEquals(newSavedWorkspace.configPath.fsPath, newWorkspaceConfigPath); - const savedContents = fs.readFileSync(newSavedWorkspace.configPath).toString(); + const savedContents = fs.readFileSync(newSavedWorkspace.configPath.fsPath).toString(); assert.equal(0, savedContents.indexOf('// this is a comment')); extfs.delSync(workspaceConfigPath); @@ -297,15 +297,15 @@ suite('WorkspacesMainService', () => { const newWorkspaceConfigPath = path.join(os.tmpdir(), `mySavedWorkspace.${Date.now()}.${WORKSPACE_EXTENSION}`); return service.saveWorkspaceAs(workspace, workspaceConfigPath).then(savedWorkspace => { - const contents = fs.readFileSync(savedWorkspace.configPath).toString(); - fs.writeFileSync(savedWorkspace.configPath, contents.replace(/[\\]/g, '/')); // convert backslash to slash + const contents = fs.readFileSync(savedWorkspace.configPath.fsPath).toString(); + fs.writeFileSync(savedWorkspace.configPath.fsPath, contents.replace(/[\\]/g, '/')); // convert backslash to slash return service.saveWorkspaceAs(savedWorkspace, newWorkspaceConfigPath).then(newSavedWorkspace => { assert.ok(newSavedWorkspace.id); assert.notEqual(newSavedWorkspace.id, workspace.id); - assertPathEquals(newSavedWorkspace.configPath, newWorkspaceConfigPath); + assertPathEquals(newSavedWorkspace.configPath.fsPath, newWorkspaceConfigPath); - const ws = JSON.parse(fs.readFileSync(newSavedWorkspace.configPath).toString()) as IStoredWorkspace; + const ws = JSON.parse(fs.readFileSync(newSavedWorkspace.configPath.fsPath).toString()) as IStoredWorkspace; assert.ok(ws.folders.every(f => (f).path.indexOf('\\') < 0)); extfs.delSync(workspaceConfigPath); @@ -317,11 +317,11 @@ suite('WorkspacesMainService', () => { test('deleteUntitledWorkspaceSync (untitled)', () => { return createWorkspace([process.cwd(), os.tmpdir()]).then(workspace => { - assert.ok(fs.existsSync(workspace.configPath)); + assert.ok(fs.existsSync(workspace.configPath.fsPath)); service.deleteUntitledWorkspaceSync(workspace); - assert.ok(!fs.existsSync(workspace.configPath)); + assert.ok(!fs.existsSync(workspace.configPath.fsPath)); }); }); @@ -330,11 +330,11 @@ suite('WorkspacesMainService', () => { const workspaceConfigPath = path.join(os.tmpdir(), `myworkspace.${Date.now()}.${WORKSPACE_EXTENSION}`); return service.saveWorkspaceAs(workspace, workspaceConfigPath).then(savedWorkspace => { - assert.ok(fs.existsSync(savedWorkspace.configPath)); + assert.ok(fs.existsSync(savedWorkspace.configPath.fsPath)); service.deleteUntitledWorkspaceSync(savedWorkspace); - assert.ok(fs.existsSync(savedWorkspace.configPath)); + assert.ok(fs.existsSync(savedWorkspace.configPath.fsPath)); }); }); }); @@ -344,7 +344,7 @@ suite('WorkspacesMainService', () => { assert.equal(0, untitled.length); return createWorkspace([process.cwd(), os.tmpdir()]).then(untitledOne => { - assert.ok(fs.existsSync(untitledOne.configPath)); + assert.ok(fs.existsSync(untitledOne.configPath.fsPath)); untitled = service.getUntitledWorkspacesSync(); @@ -352,12 +352,12 @@ suite('WorkspacesMainService', () => { assert.equal(untitledOne.id, untitled[0].id); return createWorkspace([os.tmpdir(), process.cwd()]).then(untitledTwo => { - assert.ok(fs.existsSync(untitledTwo.configPath)); + assert.ok(fs.existsSync(untitledTwo.configPath.fsPath)); untitled = service.getUntitledWorkspacesSync(); if (untitled.length === 1) { - assert.fail('Unexpected workspaces count, contents:\n' + fs.readFileSync(untitledTwo.configPath, 'utf8')); + assert.fail('Unexpected workspaces count, contents:\n' + fs.readFileSync(untitledTwo.configPath.fsPath, 'utf8')); } assert.equal(2, untitled.length); diff --git a/src/vs/workbench/browser/actions/workspaceActions.ts b/src/vs/workbench/browser/actions/workspaceActions.ts index 9cfdc373556..6a8b00cb170 100644 --- a/src/vs/workbench/browser/actions/workspaceActions.ts +++ b/src/vs/workbench/browser/actions/workspaceActions.ts @@ -256,7 +256,7 @@ export class DuplicateWorkspaceInNewWindowAction extends Action { return this.workspacesService.createUntitledWorkspace(folders).then(newWorkspace => { return this.workspaceEditingService.copyWorkspaceSettings(newWorkspace).then(() => { - return this.windowService.openWindow([URI.file(newWorkspace.configPath)], { forceNewWindow: true }); + return this.windowService.openWindow([newWorkspace.configPath], { forceNewWindow: true }); }); }); } diff --git a/src/vs/workbench/browser/dnd.ts b/src/vs/workbench/browser/dnd.ts index e73acff052b..5e5039eade3 100644 --- a/src/vs/workbench/browser/dnd.ts +++ b/src/vs/workbench/browser/dnd.ts @@ -295,7 +295,7 @@ export class ResourcesDropHandler { // Multiple folders: Create new workspace with folders and open else if (folders.length > 1) { - workspacesToOpen = this.workspacesService.createUntitledWorkspace(folders.map(folder => ({ uri: folder }))).then(workspace => [URI.file(workspace.configPath)]); + workspacesToOpen = this.workspacesService.createUntitledWorkspace(folders.map(folder => ({ uri: folder }))).then(workspace => [workspace.configPath]); } // Open diff --git a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts index 121bf09bb82..7d4ef576f04 100644 --- a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts +++ b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts @@ -327,7 +327,7 @@ export class MenubarControl extends Disposable { uri = workspace; } else if (isWorkspaceIdentifier(workspace)) { label = this.labelService.getWorkspaceLabel(workspace, { verbose: true }); - uri = URI.file(workspace.configPath); + uri = workspace.configPath; } else { uri = workspace; label = this.labelService.getUriLabel(uri); diff --git a/src/vs/workbench/electron-browser/actions/windowActions.ts b/src/vs/workbench/electron-browser/actions/windowActions.ts index 0cf3ec66110..d61cba9441d 100644 --- a/src/vs/workbench/electron-browser/actions/windowActions.ts +++ b/src/vs/workbench/electron-browser/actions/windowActions.ts @@ -226,7 +226,7 @@ export abstract class BaseSwitchWindow extends Action { return this.windowsService.getWindows().then(windows => { const placeHolder = nls.localize('switchWindowPlaceHolder', "Select a window to switch to"); const picks = windows.map(win => { - const resource = win.filename ? URI.file(win.filename) : win.folderUri ? win.folderUri : win.workspace ? URI.file(win.workspace.configPath) : undefined; + const resource = win.filename ? URI.file(win.filename) : win.folderUri ? win.folderUri : win.workspace ? win.workspace.configPath : undefined; const fileKind = win.filename ? FileKind.FILE : win.workspace ? FileKind.ROOT_FOLDER : win.folderUri ? FileKind.FOLDER : FileKind.FILE; return { payload: win.id, @@ -346,7 +346,7 @@ export abstract class BaseOpenRecentAction extends Action { label = labelService.getWorkspaceLabel(workspace); description = labelService.getUriLabel(dirname(resource)!); } else if (isWorkspaceIdentifier(workspace)) { - resource = URI.file(workspace.configPath); + resource = workspace.configPath; label = labelService.getWorkspaceLabel(workspace); description = labelService.getUriLabel(dirname(resource)!); } else { diff --git a/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.ts b/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.ts index 3e7bb14b1af..5a8cd0e7970 100644 --- a/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.ts +++ b/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.ts @@ -349,7 +349,7 @@ class WelcomePage { label = this.labelService.getWorkspaceLabel(workspace); } else if (isWorkspaceIdentifier(workspace)) { label = this.labelService.getWorkspaceLabel(workspace); - resource = URI.file(workspace.configPath); + resource = workspace.configPath; } else { label = getBaseLabel(workspace); resource = URI.file(workspace); diff --git a/src/vs/workbench/services/configuration/node/configurationService.ts b/src/vs/workbench/services/configuration/node/configurationService.ts index e07ca65249a..78862ead418 100644 --- a/src/vs/workbench/services/configuration/node/configurationService.ts +++ b/src/vs/workbench/services/configuration/node/configurationService.ts @@ -338,9 +338,9 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat } private createMultiFolderWorkspace(workspaceIdentifier: IWorkspaceIdentifier): Promise { - return this.workspaceConfiguration.load({ id: workspaceIdentifier.id, configPath: URI.file(workspaceIdentifier.configPath) }) + return this.workspaceConfiguration.load({ id: workspaceIdentifier.id, configPath: workspaceIdentifier.configPath }) .then(() => { - const workspaceConfigPath = URI.file(workspaceIdentifier.configPath); + const workspaceConfigPath = workspaceIdentifier.configPath; const workspaceFolders = toWorkspaceFolders(this.workspaceConfiguration.getFolders(), dirname(workspaceConfigPath)); const workspaceId = workspaceIdentifier.id; return new Workspace(workspaceId, workspaceFolders, workspaceConfigPath); diff --git a/src/vs/workbench/services/label/common/labelService.ts b/src/vs/workbench/services/label/common/labelService.ts index c4cb59dede6..d92d21679ac 100644 --- a/src/vs/workbench/services/label/common/labelService.ts +++ b/src/vs/workbench/services/label/common/labelService.ts @@ -11,13 +11,11 @@ import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry, IWo import { Registry } from 'vs/platform/registry/common/platform'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IWorkspaceContextService, IWorkspace } from 'vs/platform/workspace/common/workspace'; -import { isEqual, basenameOrAuthority, basename as resourceBasename } from 'vs/base/common/resources'; +import { isEqual, basenameOrAuthority, isEqualOrParent, basename, joinPath, dirname } from 'vs/base/common/resources'; import { isLinux, isWindows } from 'vs/base/common/platform'; import { tildify, getPathLabel } from 'vs/base/common/labels'; import { ltrim } from 'vs/base/common/strings'; import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, WORKSPACE_EXTENSION, toWorkspaceIdentifier, isWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; -import { isParent } from 'vs/platform/files/common/files'; -import { basename, dirname, join } from 'vs/base/common/paths'; import { Schemas } from 'vs/base/common/network'; import { IWindowService } from 'vs/platform/windows/common/windows'; import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts'; @@ -175,7 +173,7 @@ export class LabelService implements ILabelService { // Workspace: Single Folder if (isSingleFolderWorkspaceIdentifier(workspace)) { // Folder on disk - const label = options && options.verbose ? this.getUriLabel(workspace) : resourceBasename(workspace) || '/'; + const label = options && options.verbose ? this.getUriLabel(workspace) : basename(workspace) || '/'; if (workspace.scheme === Schemas.file) { return label; } @@ -186,7 +184,7 @@ export class LabelService implements ILabelService { } // Workspace: Untitled - if (isParent(workspace.configPath, this.environmentService.workspacesHome, !isLinux /* ignore case */)) { + if (isEqualOrParent(workspace.configPath, URI.file(this.environmentService.workspacesHome))) { return localize('untitledWorkspace', "Untitled (Workspace)"); } @@ -194,7 +192,7 @@ export class LabelService implements ILabelService { const filename = basename(workspace.configPath); const workspaceName = filename.substr(0, filename.length - WORKSPACE_EXTENSION.length - 1); if (options && options.verbose) { - return localize('workspaceNameVerbose', "{0} (Workspace)", this.getUriLabel(URI.file(join(dirname(workspace.configPath), workspaceName)))); + return localize('workspaceNameVerbose', "{0} (Workspace)", this.getUriLabel(joinPath(dirname(workspace.configPath)!, workspaceName))); } return localize('workspaceName', "{0} (Workspace)", workspaceName); diff --git a/src/vs/workbench/services/workspace/node/workspaceEditingService.ts b/src/vs/workbench/services/workspace/node/workspaceEditingService.ts index 6172c7e23fb..6e4f073e0c0 100644 --- a/src/vs/workbench/services/workspace/node/workspaceEditingService.ts +++ b/src/vs/workbench/services/workspace/node/workspaceEditingService.ts @@ -154,7 +154,7 @@ export class WorkspaceEditingService implements IWorkspaceEditingService { if (path) { await this.saveWorkspaceAs(untitledWorkspace, path); } else { - path = URI.file(untitledWorkspace.configPath); + path = untitledWorkspace.configPath; } return this.enterWorkspace(path); } @@ -177,7 +177,7 @@ export class WorkspaceEditingService implements IWorkspaceEditingService { const windows = await this.windowsService.getWindows(); // Prevent overwriting a workspace that is currently opened in another window - if (windows.some(window => window.workspace && isEqual(URI.file(window.workspace.configPath), path))) { + if (windows.some(window => window.workspace && isEqual(window.workspace.configPath, path))) { const options: MessageBoxOptions = { type: 'info', buttons: [nls.localize('ok', "OK")], @@ -192,7 +192,7 @@ export class WorkspaceEditingService implements IWorkspaceEditingService { } private async saveWorkspaceAs(workspace: IWorkspaceIdentifier, targetConfigPathURI: URI): Promise { - const configPathURI = URI.file(workspace.configPath); + const configPathURI = workspace.configPath; // Return early if target is same as source if (isEqual(configPathURI, targetConfigPathURI)) { @@ -317,6 +317,6 @@ export class WorkspaceEditingService implements IWorkspaceEditingService { } } - return this.jsonEditingService.write(URI.file(toWorkspace.configPath), { key: 'settings', value: targetWorkspaceConfiguration }, true); + return this.jsonEditingService.write(toWorkspace.configPath, { key: 'settings', value: targetWorkspaceConfiguration }, true); } }