Vscode merge (#4582)

* Merge from vscode 37cb23d3dd4f9433d56d4ba5ea3203580719a0bd

* fix issues with merges

* bump node version in azpipe

* replace license headers

* remove duplicate launch task

* fix build errors

* fix build errors

* fix tslint issues

* working through package and linux build issues

* more work

* wip

* fix packaged builds

* working through linux build errors

* wip

* wip

* wip

* fix mac and linux file limits

* iterate linux pipeline

* disable editor typing

* revert series to parallel

* remove optimize vscode from linux

* fix linting issues

* revert testing change

* add work round for new node

* readd packaging for extensions

* fix issue with angular not resolving decorator dependencies
This commit is contained in:
Anthony Dresser
2019-03-19 17:44:35 -07:00
committed by GitHub
parent 833d197412
commit 87765e8673
1879 changed files with 54505 additions and 38058 deletions

View File

@@ -7,7 +7,15 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'
import { localize } from 'vs/nls';
import { Event } from 'vs/base/common/event';
import { IWorkspaceFolder, IWorkspace } from 'vs/platform/workspace/common/workspace';
import { URI } from 'vs/base/common/uri';
import { URI, UriComponents } from 'vs/base/common/uri';
import { isWindows, isLinux, isMacintosh } from 'vs/base/common/platform';
import { extname } from 'vs/base/common/path';
import { dirname, resolvePath, isEqualAuthority, isEqualOrParent, relativePath } from 'vs/base/common/resources';
import * as jsonEdit from 'vs/base/common/jsonEdit';
import * as json from 'vs/base/common/json';
import { Schemas } from 'vs/base/common/network';
import { normalizeDriveLetter } from 'vs/base/common/labels';
import { toSlashes } from 'vs/base/common/extpath';
export const IWorkspacesMainService = createDecorator<IWorkspacesMainService>('workspacesMainService');
export const IWorkspacesService = createDecorator<IWorkspacesService>('workspacesService');
@@ -23,7 +31,11 @@ export type ISingleFolderWorkspaceIdentifier = URI;
export interface IWorkspaceIdentifier {
id: string;
configPath: string;
configPath: URI;
}
export function reviveWorkspaceIdentifier(workspace: { id: string, configPath: UriComponents; }): IWorkspaceIdentifier {
return { id: workspace.id, configPath: URI.revive(workspace.configPath) };
}
export function isStoredWorkspaceFolder(thing: any): thing is IStoredWorkspaceFolder {
@@ -58,6 +70,7 @@ export type IStoredWorkspaceFolder = IRawFileWorkspaceFolder | IRawUriWorkspaceF
export interface IResolvedWorkspace extends IWorkspaceIdentifier {
folders: IWorkspaceFolder[];
remoteAuthority?: string;
}
export interface IStoredWorkspace {
@@ -74,32 +87,35 @@ export interface IWorkspaceFolderCreationData {
name?: string;
}
export interface IUntitledWorkspaceInfo {
workspace: IWorkspaceIdentifier;
remoteAuthority?: string;
}
export interface IWorkspacesMainService extends IWorkspacesService {
_serviceBrand: any;
onUntitledWorkspaceDeleted: Event<IWorkspaceIdentifier>;
saveWorkspaceAs(workspace: IWorkspaceIdentifier, target: string): Promise<IWorkspaceIdentifier>;
createUntitledWorkspaceSync(folders?: IWorkspaceFolderCreationData[]): IWorkspaceIdentifier;
resolveWorkspaceSync(path: string): IResolvedWorkspace | null;
resolveLocalWorkspaceSync(path: URI): IResolvedWorkspace | null;
isUntitledWorkspace(workspace: IWorkspaceIdentifier): boolean;
deleteUntitledWorkspaceSync(workspace: IWorkspaceIdentifier): void;
getUntitledWorkspacesSync(): IWorkspaceIdentifier[];
getWorkspaceId(workspacePath: string): string;
getWorkspaceIdentifier(workspacePath: URI): IWorkspaceIdentifier;
getUntitledWorkspacesSync(): IUntitledWorkspaceInfo[];
}
export interface IWorkspacesService {
_serviceBrand: any;
createUntitledWorkspace(folders?: IWorkspaceFolderCreationData[]): Promise<IWorkspaceIdentifier>;
createUntitledWorkspace(folders?: IWorkspaceFolderCreationData[], remoteAuthority?: string): Promise<IWorkspaceIdentifier>;
deleteUntitledWorkspace(workspace: IWorkspaceIdentifier): Promise<void>;
getWorkspaceIdentifier(workspacePath: URI): Promise<IWorkspaceIdentifier>;
}
export function isSingleFolderWorkspaceIdentifier(obj: any): obj is ISingleFolderWorkspaceIdentifier {
@@ -109,13 +125,13 @@ export function isSingleFolderWorkspaceIdentifier(obj: any): obj is ISingleFolde
export function isWorkspaceIdentifier(obj: any): obj is IWorkspaceIdentifier {
const workspaceIdentifier = obj as IWorkspaceIdentifier;
return workspaceIdentifier && typeof workspaceIdentifier.id === 'string' && typeof workspaceIdentifier.configPath === 'string';
return workspaceIdentifier && typeof workspaceIdentifier.id === 'string' && workspaceIdentifier.configPath instanceof URI;
}
export function toWorkspaceIdentifier(workspace: IWorkspace): IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | undefined {
if (workspace.configuration) {
return {
configPath: workspace.configuration.fsPath,
configPath: workspace.configuration,
id: workspace.id
};
}
@@ -136,3 +152,120 @@ export type IWorkspaceInitializationPayload = IMultiFolderWorkspaceInitializatio
export function isSingleFolderWorkspaceInitializationPayload(obj: any): obj is ISingleFolderWorkspaceInitializationPayload {
return isSingleFolderWorkspaceIdentifier((obj.folder as ISingleFolderWorkspaceIdentifier));
}
const WORKSPACE_SUFFIX = '.' + WORKSPACE_EXTENSION;
export function hasWorkspaceFileExtension(path: string) {
return extname(path) === WORKSPACE_SUFFIX;
}
const SLASH = '/';
/**
* Given a folder URI and the workspace config folder, computes the IStoredWorkspaceFolder using
* a relative or absolute path or a uri.
* Undefined is returned if the folderURI and the targetConfigFolderURI don't have the same schema or authority
*
* @param folderURI a workspace folder
* @param folderName a workspace name
* @param targetConfigFolderURI the folder where the workspace is living in
* @param useSlashForPath if set, use forward slashes for file paths on windows
*/
export function getStoredWorkspaceFolder(folderURI: URI, folderName: string | undefined, targetConfigFolderURI: URI, useSlashForPath = !isWindows): IStoredWorkspaceFolder {
if (folderURI.scheme !== targetConfigFolderURI.scheme) {
return { name: folderName, uri: folderURI.toString(true) };
}
let folderPath: string | undefined;
if (isEqualOrParent(folderURI, targetConfigFolderURI)) {
// use relative path
folderPath = relativePath(targetConfigFolderURI, folderURI) || '.'; // always uses forward slashes
if (isWindows && folderURI.scheme === Schemas.file && !useSlashForPath) {
// Windows gets special treatment:
// - use backslahes unless slash is used by other existing folders
folderPath = folderPath.replace(/\//g, '\\');
}
} else {
// use absolute path
if (folderURI.scheme === Schemas.file) {
folderPath = folderURI.fsPath;
if (isWindows) {
// Windows gets special treatment:
// - normalize all paths to get nice casing of drive letters
// - use backslahes unless slash is used by other existing folders
folderPath = normalizeDriveLetter(folderPath);
if (useSlashForPath) {
folderPath = toSlashes(folderPath);
}
}
} else {
if (!isEqualAuthority(folderURI.authority, targetConfigFolderURI.authority)) {
return { name: folderName, uri: folderURI.toString(true) };
}
folderPath = folderURI.path;
}
}
return { name: folderName, path: folderPath };
}
/**
* Rewrites the content of a workspace file to be saved at a new location.
* Throws an exception if file is not a valid workspace file
*/
export function rewriteWorkspaceFileForNewLocation(rawWorkspaceContents: string, configPathURI: URI, targetConfigPathURI: URI) {
let storedWorkspace = doParseStoredWorkspace(configPathURI, rawWorkspaceContents);
const sourceConfigFolder = dirname(configPathURI);
const targetConfigFolder = dirname(targetConfigPathURI);
const rewrittenFolders: IStoredWorkspaceFolder[] = [];
const slashForPath = useSlashForPath(storedWorkspace.folders);
// Rewrite absolute paths to relative paths if the target workspace folder
// is a parent of the location of the workspace file itself. Otherwise keep
// using absolute paths.
for (const folder of storedWorkspace.folders) {
let folderURI = isRawFileWorkspaceFolder(folder) ? resolvePath(sourceConfigFolder, folder.path) : URI.parse(folder.uri);
rewrittenFolders.push(getStoredWorkspaceFolder(folderURI, folder.name, targetConfigFolder, slashForPath));
}
// Preserve as much of the existing workspace as possible by using jsonEdit
// and only changing the folders portion.
let newRawWorkspaceContents = rawWorkspaceContents;
const edits = jsonEdit.setProperty(rawWorkspaceContents, ['folders'], rewrittenFolders, { insertSpaces: false, tabSize: 4, eol: (isLinux || isMacintosh) ? '\n' : '\r\n' });
edits.forEach(edit => {
newRawWorkspaceContents = jsonEdit.applyEdit(rawWorkspaceContents, edit);
});
return newRawWorkspaceContents;
}
function doParseStoredWorkspace(path: URI, contents: string): IStoredWorkspace {
// Parse workspace file
let storedWorkspace: IStoredWorkspace = json.parse(contents); // use fault tolerant parser
// Filter out folders which do not have a path or uri set
if (Array.isArray(storedWorkspace.folders)) {
storedWorkspace.folders = storedWorkspace.folders.filter(folder => isStoredWorkspaceFolder(folder));
}
// Validate
if (!Array.isArray(storedWorkspace.folders)) {
throw new Error(`${path} looks like an invalid workspace file.`);
}
return storedWorkspace;
}
export function useSlashForPath(storedFolders: IStoredWorkspaceFolder[]): boolean {
if (isWindows) {
for (const folder of storedFolders) {
if (isRawFileWorkspaceFolder(folder) && folder.path.indexOf(SLASH) >= 0) {
return true;
}
}
return false;
}
return true;
}

View File

@@ -0,0 +1,33 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IChannel } from 'vs/base/parts/ipc/common/ipc';
import { IWorkspacesService, IWorkspaceIdentifier, IWorkspaceFolderCreationData, reviveWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
import { IMainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService';
import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation';
import { URI } from 'vs/base/common/uri';
export class WorkspacesService implements IWorkspacesService {
_serviceBrand: ServiceIdentifier<any>;
private channel: IChannel;
constructor(@IMainProcessService mainProcessService: IMainProcessService) {
this.channel = mainProcessService.getChannel('workspaces');
}
createUntitledWorkspace(folders?: IWorkspaceFolderCreationData[], remoteAuthority?: string): Promise<IWorkspaceIdentifier> {
return this.channel.call('createUntitledWorkspace', [folders, remoteAuthority]).then(reviveWorkspaceIdentifier);
}
deleteUntitledWorkspace(workspaceIdentifier: IWorkspaceIdentifier): Promise<void> {
return this.channel.call('deleteUntitledWorkspace', workspaceIdentifier);
}
getWorkspaceIdentifier(configPath: URI): Promise<IWorkspaceIdentifier> {
return this.channel.call('getWorkspaceIdentifier', configPath);
}
}

View File

@@ -3,36 +3,33 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IWorkspacesMainService, IWorkspaceIdentifier, WORKSPACE_EXTENSION, UNTITLED_WORKSPACE_NAME, IResolvedWorkspace, IStoredWorkspaceFolder, isStoredWorkspaceFolder, IWorkspaceFolderCreationData } from 'vs/platform/workspaces/common/workspaces';
import { isParent } from 'vs/platform/files/common/files';
import { IWorkspacesMainService, IWorkspaceIdentifier, hasWorkspaceFileExtension, UNTITLED_WORKSPACE_NAME, IResolvedWorkspace, IStoredWorkspaceFolder, isStoredWorkspaceFolder, IWorkspaceFolderCreationData, IUntitledWorkspaceInfo, getStoredWorkspaceFolder } from 'vs/platform/workspaces/common/workspaces';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { join, dirname, extname } from 'path';
import { mkdirp, writeFile, readFile } from 'vs/base/node/pfs';
import { join, dirname } from 'vs/base/common/path';
import { mkdirp, writeFile } from 'vs/base/node/pfs';
import { readFileSync, existsSync, mkdirSync, writeFileSync } from 'fs';
import { isLinux } from 'vs/base/common/platform';
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';
import { toWorkspaceFolders } from 'vs/platform/workspace/common/workspace';
import { URI } from 'vs/base/common/uri';
import { Schemas } from 'vs/base/common/network';
import { Disposable } from 'vs/base/common/lifecycle';
import { fsPath, dirname as resourcesDirname } from 'vs/base/common/resources';
import { originalFSPath, dirname as resourcesDirname, isEqualOrParent, joinPath } from 'vs/base/common/resources';
export interface IStoredWorkspace {
folders: IStoredWorkspaceFolder[];
remoteAuthority?: string;
}
export class WorkspacesMainService extends Disposable implements IWorkspacesMainService {
_serviceBrand: any;
private workspacesHome: string;
private readonly untitledWorkspacesHome: URI; // local URI that contains all untitled workspaces
private readonly _onUntitledWorkspaceDeleted = this._register(new Emitter<IWorkspaceIdentifier>());
get onUntitledWorkspaceDeleted(): Event<IWorkspaceIdentifier> { return this._onUntitledWorkspaceDeleted.event; }
@@ -43,36 +40,40 @@ export class WorkspacesMainService extends Disposable implements IWorkspacesMain
) {
super();
this.workspacesHome = environmentService.workspacesHome;
this.untitledWorkspacesHome = environmentService.untitledWorkspacesHome;
}
resolveWorkspaceSync(path: string): IResolvedWorkspace | null {
if (!this.isWorkspacePath(path)) {
resolveLocalWorkspaceSync(uri: URI): IResolvedWorkspace | null {
if (!this.isWorkspacePath(uri)) {
return null; // does not look like a valid workspace config file
}
if (uri.scheme !== Schemas.file) {
return null;
}
let contents: string;
try {
contents = readFileSync(path, 'utf8');
contents = readFileSync(uri.fsPath, 'utf8');
} catch (error) {
return null; // invalid workspace
}
return this.doResolveWorkspace(URI.file(path), contents);
return this.doResolveWorkspace(uri, contents);
}
private isWorkspacePath(path: string): boolean {
return this.isInsideWorkspacesHome(path) || extname(path) === `.${WORKSPACE_EXTENSION}`;
private isWorkspacePath(uri: URI): boolean {
return this.isInsideWorkspacesHome(uri) || hasWorkspaceFileExtension(uri.path);
}
private doResolveWorkspace(path: URI, contents: string): IResolvedWorkspace | null {
try {
const workspace = this.doParseStoredWorkspace(path, contents);
const workspaceIdentifier = this.getWorkspaceIdentifier(path);
const workspaceIdentifier = getWorkspaceIdentifier(path);
return {
id: workspaceIdentifier.id,
configPath: workspaceIdentifier.configPath,
folders: toWorkspaceFolders(workspace.folders, resourcesDirname(path)!)
folders: toWorkspaceFolders(workspace.folders, resourcesDirname(path)),
remoteAuthority: workspace.remoteAuthority
};
} catch (error) {
this.logService.warn(error.toString());
@@ -99,135 +100,87 @@ export class WorkspacesMainService extends Disposable implements IWorkspacesMain
return storedWorkspace;
}
private isInsideWorkspacesHome(path: string): boolean {
return isParent(path, this.environmentService.workspacesHome, !isLinux /* ignore case */);
private isInsideWorkspacesHome(path: URI): boolean {
return isEqualOrParent(path, this.environmentService.untitledWorkspacesHome);
}
createUntitledWorkspace(folders?: IWorkspaceFolderCreationData[]): Promise<IWorkspaceIdentifier> {
const { workspace, configParent, storedWorkspace } = this.newUntitledWorkspace(folders);
createUntitledWorkspace(folders?: IWorkspaceFolderCreationData[], remoteAuthority?: string): Promise<IWorkspaceIdentifier> {
const { workspace, storedWorkspace } = this.newUntitledWorkspace(folders, remoteAuthority);
const configPath = workspace.configPath.fsPath;
return mkdirp(configParent).then(() => {
return writeFile(workspace.configPath, JSON.stringify(storedWorkspace, null, '\t')).then(() => workspace);
return mkdirp(dirname(configPath)).then(() => {
return writeFile(configPath, JSON.stringify(storedWorkspace, null, '\t')).then(() => workspace);
});
}
createUntitledWorkspaceSync(folders?: IWorkspaceFolderCreationData[]): IWorkspaceIdentifier {
const { workspace, configParent, storedWorkspace } = this.newUntitledWorkspace(folders);
createUntitledWorkspaceSync(folders?: IWorkspaceFolderCreationData[], remoteAuthority?: string): IWorkspaceIdentifier {
const { workspace, storedWorkspace } = this.newUntitledWorkspace(folders, remoteAuthority);
const configPath = workspace.configPath.fsPath;
if (!existsSync(this.workspacesHome)) {
mkdirSync(this.workspacesHome);
const configPathDir = dirname(configPath);
if (!existsSync(configPathDir)) {
const configPathDirDir = dirname(configPathDir);
if (!existsSync(configPathDirDir)) {
mkdirSync(configPathDirDir);
}
mkdirSync(configPathDir);
}
mkdirSync(configParent);
writeFileAndFlushSync(workspace.configPath, JSON.stringify(storedWorkspace, null, '\t'));
writeFileAndFlushSync(configPath, JSON.stringify(storedWorkspace, null, '\t'));
return workspace;
}
private newUntitledWorkspace(folders: IWorkspaceFolderCreationData[] = []): { workspace: IWorkspaceIdentifier, configParent: string, storedWorkspace: IStoredWorkspace } {
private newUntitledWorkspace(folders: IWorkspaceFolderCreationData[] = [], remoteAuthority?: string): { workspace: IWorkspaceIdentifier, storedWorkspace: IStoredWorkspace } {
const randomId = (Date.now() + Math.round(Math.random() * 1000)).toString();
const untitledWorkspaceConfigFolder = join(this.workspacesHome, randomId);
const untitledWorkspaceConfigPath = join(untitledWorkspaceConfigFolder, UNTITLED_WORKSPACE_NAME);
const untitledWorkspaceConfigFolder = joinPath(this.untitledWorkspacesHome, randomId);
const untitledWorkspaceConfigPath = joinPath(untitledWorkspaceConfigFolder, UNTITLED_WORKSPACE_NAME);
const storedWorkspace: IStoredWorkspace = {
folders: folders.map(folder => {
const folderResource = folder.uri;
let storedWorkspace: IStoredWorkspaceFolder;
const storedWorkspaceFolder: IStoredWorkspaceFolder[] = [];
// File URI
if (folderResource.scheme === Schemas.file) {
storedWorkspace = { path: massageFolderPathForWorkspace(fsPath(folderResource), URI.file(untitledWorkspaceConfigFolder), []) };
}
// Any URI
else {
storedWorkspace = { uri: folderResource.toString(true) };
}
if (folder.name) {
storedWorkspace.name = folder.name;
}
return storedWorkspace;
})
};
for (const folder of folders) {
storedWorkspaceFolder.push(getStoredWorkspaceFolder(folder.uri, folder.name, untitledWorkspaceConfigFolder));
}
return {
workspace: {
id: this.getWorkspaceId(untitledWorkspaceConfigPath),
configPath: untitledWorkspaceConfigPath
},
configParent: untitledWorkspaceConfigFolder,
storedWorkspace
workspace: getWorkspaceIdentifier(untitledWorkspaceConfigPath),
storedWorkspace: { folders: storedWorkspaceFolder, remoteAuthority }
};
}
getWorkspaceId(workspaceConfigPath: string): string {
if (!isLinux) {
workspaceConfigPath = workspaceConfigPath.toLowerCase(); // sanitize for platform file system
}
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): Promise<IWorkspaceIdentifier> {
return Promise.resolve(getWorkspaceIdentifier(configPath));
}
isUntitledWorkspace(workspace: IWorkspaceIdentifier): boolean {
return this.isInsideWorkspacesHome(workspace.configPath);
}
saveWorkspaceAs(workspace: IWorkspaceIdentifier, targetConfigPath: string): Promise<IWorkspaceIdentifier> {
// Return early if target is same as source
if (isEqual(workspace.configPath, targetConfigPath, !isLinux)) {
return Promise.resolve(workspace);
}
// Read the contents of the workspace file and resolve it
return readFile(workspace.configPath).then(raw => {
const targetConfigPathURI = URI.file(targetConfigPath);
const newRawWorkspaceContents = rewriteWorkspaceFileForNewLocation(raw.toString(), URI.file(workspace.configPath), targetConfigPathURI);
return writeFile(targetConfigPath, newRawWorkspaceContents).then(() => {
return this.getWorkspaceIdentifier(targetConfigPathURI);
});
});
}
deleteUntitledWorkspaceSync(workspace: IWorkspaceIdentifier): void {
if (!this.isUntitledWorkspace(workspace)) {
return; // only supported for untitled workspaces
}
// Delete from disk
this.doDeleteUntitledWorkspaceSync(workspace.configPath);
this.doDeleteUntitledWorkspaceSync(workspace);
// Event
this._onUntitledWorkspaceDeleted.fire(workspace);
}
private doDeleteUntitledWorkspaceSync(configPath: string): void {
try {
deleteUntitledWorkspace(workspace: IWorkspaceIdentifier): Promise<void> {
this.deleteUntitledWorkspaceSync(workspace);
return Promise.resolve();
}
private doDeleteUntitledWorkspaceSync(workspace: IWorkspaceIdentifier): void {
const configPath = originalFSPath(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'), '');
}
@@ -236,27 +189,40 @@ export class WorkspacesMainService extends Disposable implements IWorkspacesMain
}
}
getUntitledWorkspacesSync(): IWorkspaceIdentifier[] {
let untitledWorkspacePaths: string[] = [];
getUntitledWorkspacesSync(): IUntitledWorkspaceInfo[] {
let untitledWorkspaces: IUntitledWorkspaceInfo[] = [];
try {
untitledWorkspacePaths = readdirSync(this.workspacesHome).map(folder => join(this.workspacesHome, folder, UNTITLED_WORKSPACE_NAME));
const untitledWorkspacePaths = readdirSync(this.untitledWorkspacesHome.fsPath).map(folder => joinPath(this.untitledWorkspacesHome, folder, UNTITLED_WORKSPACE_NAME));
for (const untitledWorkspacePath of untitledWorkspacePaths) {
const workspace = getWorkspaceIdentifier(untitledWorkspacePath);
const resolvedWorkspace = this.resolveLocalWorkspaceSync(untitledWorkspacePath);
if (!resolvedWorkspace) {
this.doDeleteUntitledWorkspaceSync(workspace);
} else {
untitledWorkspaces.push({ workspace, remoteAuthority: resolvedWorkspace.remoteAuthority });
}
}
} catch (error) {
if (error && error.code !== 'ENOENT') {
this.logService.warn(`Unable to read folders in ${this.workspacesHome} (${error}).`);
this.logService.warn(`Unable to read folders in ${this.untitledWorkspacesHome} (${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;
}
}
function getWorkspaceId(configPath: URI): string {
let workspaceConfigPath = configPath.scheme === Schemas.file ? originalFSPath(configPath) : configPath.toString();
if (!isLinux) {
workspaceConfigPath = workspaceConfigPath.toLowerCase(); // sanitize for platform file system
}
return createHash('md5').update(workspaceConfigPath).digest('hex');
}
export function getWorkspaceIdentifier(configPath: URI): IWorkspaceIdentifier {
return {
configPath,
id: getWorkspaceId(configPath)
};
}

View File

@@ -1,127 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IStoredWorkspaceFolder, isRawFileWorkspaceFolder, IStoredWorkspace, isStoredWorkspaceFolder } from 'vs/platform/workspaces/common/workspaces';
import { isWindows, isLinux, isMacintosh } from 'vs/base/common/platform';
import { isAbsolute, relative, posix, resolve } from 'path';
import { normalize, isEqualOrParent } from 'vs/base/common/paths';
import { normalizeDriveLetter } from 'vs/base/common/labels';
import { URI } from 'vs/base/common/uri';
import { fsPath, dirname } from 'vs/base/common/resources';
import { Schemas } from 'vs/base/common/network';
import * as jsonEdit from 'vs/base/common/jsonEdit';
import * as json from 'vs/base/common/json';
const SLASH = '/';
/**
* Given the absolute path to a folder, massage it in a way that it fits
* into an existing set of workspace folders of a workspace.
*
* @param absoluteFolderPath the absolute path of a workspace folder
* @param targetConfigFolder the folder where the workspace is living in
* @param existingFolders a set of existing folders of the workspace
*/
export function massageFolderPathForWorkspace(absoluteFolderPath: string, targetConfigFolderURI: URI, existingFolders: IStoredWorkspaceFolder[]): string {
if (targetConfigFolderURI.scheme === Schemas.file) {
const targetFolderPath = fsPath(targetConfigFolderURI);
// Convert path to relative path if the target config folder
// is a parent of the path.
if (isEqualOrParent(absoluteFolderPath, targetFolderPath, !isLinux)) {
absoluteFolderPath = relative(targetFolderPath, absoluteFolderPath) || '.';
}
// Windows gets special treatment:
// - normalize all paths to get nice casing of drive letters
// - convert to slashes if we want to use slashes for paths
if (isWindows) {
if (isAbsolute(absoluteFolderPath)) {
if (shouldUseSlashForPath(existingFolders)) {
absoluteFolderPath = normalize(absoluteFolderPath, false /* do not use OS path separator */);
}
absoluteFolderPath = normalizeDriveLetter(absoluteFolderPath);
} else if (shouldUseSlashForPath(existingFolders)) {
absoluteFolderPath = absoluteFolderPath.replace(/[\\]/g, SLASH);
}
}
} else {
if (isEqualOrParent(absoluteFolderPath, targetConfigFolderURI.path)) {
absoluteFolderPath = posix.relative(absoluteFolderPath, targetConfigFolderURI.path) || '.';
}
}
return absoluteFolderPath;
}
/**
* Rewrites the content of a workspace file to be saved at a new location.
* Throws an exception if file is not a valid workspace file
*/
export function rewriteWorkspaceFileForNewLocation(rawWorkspaceContents: string, configPathURI: URI, targetConfigPathURI: URI) {
let storedWorkspace = doParseStoredWorkspace(configPathURI, rawWorkspaceContents);
const sourceConfigFolder = dirname(configPathURI)!;
const targetConfigFolder = dirname(targetConfigPathURI)!;
// Rewrite absolute paths to relative paths if the target workspace folder
// is a parent of the location of the workspace file itself. Otherwise keep
// using absolute paths.
for (const folder of storedWorkspace.folders) {
if (isRawFileWorkspaceFolder(folder)) {
if (sourceConfigFolder.scheme === Schemas.file) {
if (!isAbsolute(folder.path)) {
folder.path = resolve(fsPath(sourceConfigFolder), folder.path); // relative paths get resolved against the workspace location
}
folder.path = massageFolderPathForWorkspace(folder.path, targetConfigFolder, storedWorkspace.folders);
}
}
}
// Preserve as much of the existing workspace as possible by using jsonEdit
// and only changing the folders portion.
let newRawWorkspaceContents = rawWorkspaceContents;
const edits = jsonEdit.setProperty(rawWorkspaceContents, ['folders'], storedWorkspace.folders, { insertSpaces: false, tabSize: 4, eol: (isLinux || isMacintosh) ? '\n' : '\r\n' });
edits.forEach(edit => {
newRawWorkspaceContents = jsonEdit.applyEdit(rawWorkspaceContents, edit);
});
return newRawWorkspaceContents;
}
function doParseStoredWorkspace(path: URI, contents: string): IStoredWorkspace {
// Parse workspace file
let storedWorkspace: IStoredWorkspace = json.parse(contents); // use fault tolerant parser
// Filter out folders which do not have a path or uri set
if (Array.isArray(storedWorkspace.folders)) {
storedWorkspace.folders = storedWorkspace.folders.filter(folder => isStoredWorkspaceFolder(folder));
}
// Validate
if (!Array.isArray(storedWorkspace.folders)) {
throw new Error(`${path} looks like an invalid workspace file.`);
}
return storedWorkspace;
}
function shouldUseSlashForPath(storedFolders: IStoredWorkspaceFolder[]): boolean {
// Determine which path separator to use:
// - macOS/Linux: slash
// - Windows: use slash if already used in that file
let useSlashesForPath = !isWindows;
if (isWindows) {
storedFolders.forEach(folder => {
if (isRawFileWorkspaceFolder(folder) && !useSlashesForPath && folder.path.indexOf(SLASH) >= 0) {
useSlashesForPath = true;
}
});
}
return useSlashesForPath;
}

View File

@@ -3,8 +3,8 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IChannel, IServerChannel } from 'vs/base/parts/ipc/node/ipc';
import { IWorkspacesService, IWorkspaceIdentifier, IWorkspaceFolderCreationData, IWorkspacesMainService } from 'vs/platform/workspaces/common/workspaces';
import { IServerChannel } from 'vs/base/parts/ipc/common/ipc';
import { IWorkspaceIdentifier, IWorkspaceFolderCreationData, IWorkspacesMainService } from 'vs/platform/workspaces/common/workspaces';
import { URI } from 'vs/base/common/uri';
import { Event } from 'vs/base/common/event';
@@ -19,7 +19,8 @@ export class WorkspacesChannel implements IServerChannel {
call(_, command: string, arg?: any): Promise<any> {
switch (command) {
case 'createUntitledWorkspace': {
const rawFolders: IWorkspaceFolderCreationData[] = arg;
const rawFolders: IWorkspaceFolderCreationData[] = arg[0];
const remoteAuthority: string = arg[1];
let folders: IWorkspaceFolderCreationData[] | undefined = undefined;
if (Array.isArray(rawFolders)) {
folders = rawFolders.map(rawFolder => {
@@ -30,21 +31,17 @@ export class WorkspacesChannel implements IServerChannel {
});
}
return this.service.createUntitledWorkspace(folders);
return this.service.createUntitledWorkspace(folders, remoteAuthority);
}
case 'deleteUntitledWorkspace': {
const w: IWorkspaceIdentifier = arg;
return this.service.deleteUntitledWorkspace({ id: w.id, configPath: URI.revive(w.configPath) });
}
case 'getWorkspaceIdentifier': {
return this.service.getWorkspaceIdentifier(URI.revive(arg));
}
}
throw new Error(`Call not found: ${command}`);
}
}
export class WorkspacesChannelClient implements IWorkspacesService {
_serviceBrand: any;
constructor(private channel: IChannel) { }
createUntitledWorkspace(folders?: IWorkspaceFolderCreationData[]): Promise<IWorkspaceIdentifier> {
return this.channel.call('createUntitledWorkspace', folders);
}
}

View File

@@ -6,26 +6,25 @@
import * as assert from 'assert';
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
import * as extfs from 'vs/base/node/extfs';
import * as path from 'vs/base/common/path';
import * as pfs from 'vs/base/node/pfs';
import { EnvironmentService } from 'vs/platform/environment/node/environmentService';
import { parseArgs } from 'vs/platform/environment/node/argv';
import { WorkspacesMainService, IStoredWorkspace } from 'vs/platform/workspaces/electron-main/workspacesMainService';
import { WORKSPACE_EXTENSION, IWorkspaceIdentifier, IRawFileWorkspaceFolder, IWorkspaceFolderCreationData, IRawUriWorkspaceFolder } from 'vs/platform/workspaces/common/workspaces';
import { WORKSPACE_EXTENSION, IWorkspaceIdentifier, IRawFileWorkspaceFolder, IWorkspaceFolderCreationData, IRawUriWorkspaceFolder, rewriteWorkspaceFileForNewLocation } from 'vs/platform/workspaces/common/workspaces';
import { NullLogService } from 'vs/platform/log/common/log';
import { URI } from 'vs/base/common/uri';
import { getRandomTestPath } from 'vs/workbench/test/workbenchTestServices';
import { getRandomTestPath } from 'vs/base/test/node/testUtils';
import { isWindows } from 'vs/base/common/platform';
import { normalizeDriveLetter } from 'vs/base/common/labels';
suite('WorkspacesMainService', () => {
const parentDir = getRandomTestPath(os.tmpdir(), 'vsctests', 'workspacesservice');
const workspacesHome = path.join(parentDir, 'Workspaces');
const untitledWorkspacesHomePath = path.join(parentDir, 'Workspaces');
class TestEnvironmentService extends EnvironmentService {
get workspacesHome(): string {
return workspacesHome;
get untitledWorkspacesHome(): URI {
return URI.file(untitledWorkspacesHomePath);
}
}
@@ -56,13 +55,13 @@ suite('WorkspacesMainService', () => {
service = new TestWorkspacesMainService(environmentService, logService);
// Delete any existing backups completely and then re-create it.
return pfs.del(workspacesHome, os.tmpdir()).then(() => {
return pfs.mkdirp(workspacesHome);
return pfs.del(untitledWorkspacesHomePath, os.tmpdir()).then(() => {
return pfs.mkdirp(untitledWorkspacesHomePath);
});
});
teardown(() => {
return pfs.del(workspacesHome, os.tmpdir());
return pfs.del(untitledWorkspacesHomePath, os.tmpdir());
});
function assertPathEquals(p1: string, p2): void {
@@ -74,13 +73,17 @@ suite('WorkspacesMainService', () => {
assert.equal(p1, p2);
}
function assertEqualURI(u1: URI, u2: URI): void {
assert.equal(u1.toString(), u2.toString());
}
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((<IRawFileWorkspaceFolder>ws.folders[0]).path, process.cwd());
assertPathEquals((<IRawFileWorkspaceFolder>ws.folders[1]).path, os.tmpdir());
@@ -93,10 +96,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((<IRawFileWorkspaceFolder>ws.folders[0]).path, process.cwd());
assertPathEquals((<IRawFileWorkspaceFolder>ws.folders[1]).path, os.tmpdir());
@@ -107,28 +110,33 @@ 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 => {
const folder1URI = URI.parse('myscheme://server/work/p/f1');
const folder2URI = URI.parse('myscheme://server/work/o/f3');
return service.createUntitledWorkspace([{ uri: folder1URI }, { uri: folder2URI }], 'server').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((<IRawUriWorkspaceFolder>ws.folders[0]).uri, URI.from({ scheme: 'myScheme', path: process.cwd() }).toString(true));
assert.equal((<IRawUriWorkspaceFolder>ws.folders[1]).uri, URI.from({ scheme: 'myScheme', path: os.tmpdir() }).toString(true));
assert.equal((<IRawUriWorkspaceFolder>ws.folders[0]).uri, folder1URI.toString(true));
assert.equal((<IRawUriWorkspaceFolder>ws.folders[1]).uri, folder2URI.toString(true));
assert.ok(!(<IRawFileWorkspaceFolder>ws.folders[0]).name);
assert.ok(!(<IRawFileWorkspaceFolder>ws.folders[1]).name);
assert.equal(ws.remoteAuthority, 'server');
});
});
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((<IRawFileWorkspaceFolder>ws.folders[0]).path, process.cwd());
assertPathEquals((<IRawFileWorkspaceFolder>ws.folders[1]).path, os.tmpdir());
@@ -140,10 +148,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((<IRawFileWorkspaceFolder>ws.folders[0]).path, process.cwd());
assertPathEquals((<IRawFileWorkspaceFolder>ws.folders[1]).path, os.tmpdir());
@@ -153,15 +161,18 @@ 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() }) }]);
const folder1URI = URI.parse('myscheme://server/work/p/f1');
const folder2URI = URI.parse('myscheme://server/work/o/f3');
const workspace = service.createUntitledWorkspaceSync([{ uri: folder1URI }, { uri: folder2URI }]);
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((<IRawUriWorkspaceFolder>ws.folders[0]).uri, URI.from({ scheme: 'myScheme', path: process.cwd() }).toString(true));
assert.equal((<IRawUriWorkspaceFolder>ws.folders[1]).uri, URI.from({ scheme: 'myScheme', path: os.tmpdir() }).toString(true));
assert.equal((<IRawUriWorkspaceFolder>ws.folders[0]).uri, folder1URI.toString(true));
assert.equal((<IRawUriWorkspaceFolder>ws.folders[1]).uri, folder2URI.toString(true));
assert.ok(!(<IRawFileWorkspaceFolder>ws.folders[0]).name);
assert.ok(!(<IRawFileWorkspaceFolder>ws.folders[1]).name);
@@ -169,195 +180,202 @@ suite('WorkspacesMainService', () => {
test('resolveWorkspaceSync', () => {
return createWorkspace([process.cwd(), os.tmpdir()]).then(workspace => {
assert.ok(service.resolveWorkspaceSync(workspace.configPath));
assert.ok(service.resolveLocalWorkspaceSync(workspace.configPath));
// 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.resolveLocalWorkspaceSync(workspace.configPath);
assert.equal(2, resolved!.folders.length);
assert.equal(resolved!.configPath, workspace.configPath);
assertEqualURI(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.resolveLocalWorkspaceSync(workspace.configPath);
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.resolveLocalWorkspaceSync(workspace.configPath);
assertEqualURI(resolved!.folders[0].uri, URI.file(path.join(path.dirname(workspace.configPath.fsPath), 'ticino-playground', 'lib')));
});
});
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.resolveLocalWorkspaceSync(workspace.configPath);
assertEqualURI(resolved!.folders[0].uri, URI.file(path.join(path.dirname(workspace.configPath.fsPath), 'ticino-playground', 'other')));
});
});
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.resolveLocalWorkspaceSync(workspace.configPath);
assertEqualURI(resolved!.folders[0].uri, URI.file(path.join(path.dirname(workspace.configPath.fsPath), 'ticino-playground', 'lib')));
});
});
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.resolveLocalWorkspaceSync(workspace.configPath);
assertEqualURI(resolved!.folders[0].uri, URI.file(path.join(path.dirname(workspace.configPath.fsPath), 'ticino-playground', 'lib')));
});
});
test('saveWorkspace (untitled)', () => {
return createWorkspace([process.cwd(), os.tmpdir(), path.join(os.tmpdir(), 'somefolder')]).then(workspace => {
const workspaceConfigPath = path.join(os.tmpdir(), `myworkspace.${Date.now()}.${WORKSPACE_EXTENSION}`);
test('rewriteWorkspaceFileForNewLocation', () => {
const folder1 = process.cwd(); // absolute path because outside of tmpDir
const tmpDir = os.tmpdir();
const tmpInsideDir = path.join(os.tmpdir(), 'inside');
return service.saveWorkspaceAs(workspace, workspaceConfigPath).then(savedWorkspace => {
assert.ok(savedWorkspace.id);
assert.notEqual(savedWorkspace.id, workspace.id);
assert.equal(savedWorkspace.configPath, workspaceConfigPath);
return createWorkspace([folder1, tmpInsideDir, path.join(tmpInsideDir, 'somefolder')]).then(workspace => {
const origContent = fs.readFileSync(workspace.configPath.fsPath).toString();
const ws = JSON.parse(fs.readFileSync(savedWorkspace.configPath).toString()) as IStoredWorkspace;
assert.equal(ws.folders.length, 3);
assertPathEquals((<IRawFileWorkspaceFolder>ws.folders[0]).path, process.cwd()); // absolute
assertPathEquals((<IRawFileWorkspaceFolder>ws.folders[1]).path, '.'); // relative
assertPathEquals((<IRawFileWorkspaceFolder>ws.folders[2]).path, path.relative(path.dirname(workspaceConfigPath), path.join(os.tmpdir(), 'somefolder'))); // relative
let origConfigPath = workspace.configPath;
let workspaceConfigPath = URI.file(path.join(tmpDir, 'inside', 'myworkspace1.code-workspace'));
let newContent = rewriteWorkspaceFileForNewLocation(origContent, origConfigPath, workspaceConfigPath);
extfs.delSync(workspaceConfigPath);
});
let ws = JSON.parse(newContent) as IStoredWorkspace;
assert.equal(ws.folders.length, 3);
assertPathEquals((<IRawFileWorkspaceFolder>ws.folders[0]).path, folder1); // absolute path because outside of tmpdir
assertPathEquals((<IRawFileWorkspaceFolder>ws.folders[1]).path, '.');
assertPathEquals((<IRawFileWorkspaceFolder>ws.folders[2]).path, 'somefolder');
origConfigPath = workspaceConfigPath;
workspaceConfigPath = URI.file(path.join(tmpDir, 'myworkspace2.code-workspace'));
newContent = rewriteWorkspaceFileForNewLocation(newContent, origConfigPath, workspaceConfigPath);
ws = JSON.parse(newContent) as IStoredWorkspace;
assert.equal(ws.folders.length, 3);
assertPathEquals((<IRawFileWorkspaceFolder>ws.folders[0]).path, folder1);
assertPathEquals((<IRawFileWorkspaceFolder>ws.folders[1]).path, 'inside');
assertPathEquals((<IRawFileWorkspaceFolder>ws.folders[2]).path, isWindows ? 'inside\\somefolder' : 'inside/somefolder');
origConfigPath = workspaceConfigPath;
workspaceConfigPath = URI.file(path.join(tmpDir, 'other', 'myworkspace2.code-workspace'));
newContent = rewriteWorkspaceFileForNewLocation(newContent, origConfigPath, workspaceConfigPath);
ws = JSON.parse(newContent) as IStoredWorkspace;
assert.equal(ws.folders.length, 3);
assertPathEquals((<IRawFileWorkspaceFolder>ws.folders[0]).path, folder1);
assertPathEquals((<IRawFileWorkspaceFolder>ws.folders[1]).path, tmpInsideDir);
assertPathEquals((<IRawFileWorkspaceFolder>ws.folders[2]).path, path.join(tmpInsideDir, 'somefolder'));
origConfigPath = workspaceConfigPath;
workspaceConfigPath = URI.parse('foo://foo/bar/myworkspace2.code-workspace');
newContent = rewriteWorkspaceFileForNewLocation(newContent, origConfigPath, workspaceConfigPath);
ws = JSON.parse(newContent) as IStoredWorkspace;
assert.equal(ws.folders.length, 3);
assert.equal((<IRawUriWorkspaceFolder>ws.folders[0]).uri, URI.file(folder1).toString(true));
assert.equal((<IRawUriWorkspaceFolder>ws.folders[1]).uri, URI.file(tmpInsideDir).toString(true));
assert.equal((<IRawUriWorkspaceFolder>ws.folders[2]).uri, URI.file(path.join(tmpInsideDir, 'somefolder')).toString(true));
service.deleteUntitledWorkspaceSync(workspace);
});
});
test('saveWorkspace (saved workspace)', () => {
test('rewriteWorkspaceFileForNewLocation (preserves comments)', () => {
return createWorkspace([process.cwd(), os.tmpdir(), path.join(os.tmpdir(), 'somefolder')]).then(workspace => {
const workspaceConfigPath = path.join(os.tmpdir(), `myworkspace.${Date.now()}.${WORKSPACE_EXTENSION}`);
const newWorkspaceConfigPath = path.join(os.tmpdir(), `mySavedWorkspace.${Date.now()}.${WORKSPACE_EXTENSION}`);
const workspaceConfigPath = URI.file(path.join(os.tmpdir(), `myworkspace.${Date.now()}.${WORKSPACE_EXTENSION}`));
return service.saveWorkspaceAs(workspace, workspaceConfigPath).then(savedWorkspace => {
return service.saveWorkspaceAs(savedWorkspace, newWorkspaceConfigPath).then(newSavedWorkspace => {
assert.ok(newSavedWorkspace.id);
assert.notEqual(newSavedWorkspace.id, workspace.id);
assertPathEquals(newSavedWorkspace.configPath, newWorkspaceConfigPath);
let origContent = fs.readFileSync(workspace.configPath.fsPath).toString();
origContent = `// this is a comment\n${origContent}`;
const ws = JSON.parse(fs.readFileSync(newSavedWorkspace.configPath).toString()) as IStoredWorkspace;
assert.equal(ws.folders.length, 3);
assertPathEquals((<IRawFileWorkspaceFolder>ws.folders[0]).path, process.cwd()); // absolute path because outside of tmpdir
assertPathEquals((<IRawFileWorkspaceFolder>ws.folders[1]).path, '.'); // relative path because inside of tmpdir
assertPathEquals((<IRawFileWorkspaceFolder>ws.folders[2]).path, path.relative(path.dirname(workspaceConfigPath), path.join(os.tmpdir(), 'somefolder'))); // relative
let newContent = rewriteWorkspaceFileForNewLocation(origContent, workspace.configPath, workspaceConfigPath);
extfs.delSync(workspaceConfigPath);
extfs.delSync(newWorkspaceConfigPath);
});
});
assert.equal(0, newContent.indexOf('// this is a comment'));
service.deleteUntitledWorkspaceSync(workspace);
});
});
test('saveWorkspace (saved workspace, preserves comments)', () => {
test('rewriteWorkspaceFileForNewLocation (preserves forward slashes)', () => {
return createWorkspace([process.cwd(), os.tmpdir(), path.join(os.tmpdir(), 'somefolder')]).then(workspace => {
const workspaceConfigPath = path.join(os.tmpdir(), `myworkspace.${Date.now()}.${WORKSPACE_EXTENSION}`);
const newWorkspaceConfigPath = path.join(os.tmpdir(), `mySavedWorkspace.${Date.now()}.${WORKSPACE_EXTENSION}`);
const workspaceConfigPath = URI.file(path.join(os.tmpdir(), `myworkspace.${Date.now()}.${WORKSPACE_EXTENSION}`));
let origContent = fs.readFileSync(workspace.configPath.fsPath).toString();
origContent = origContent.replace(/[\\]/g, '/'); // convert backslash to slash
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 newContent = rewriteWorkspaceFileForNewLocation(origContent, workspace.configPath, workspaceConfigPath);
return service.saveWorkspaceAs(savedWorkspace, newWorkspaceConfigPath).then(newSavedWorkspace => {
assert.ok(newSavedWorkspace.id);
assert.notEqual(newSavedWorkspace.id, workspace.id);
assertPathEquals(newSavedWorkspace.configPath, newWorkspaceConfigPath);
const ws = JSON.parse(newContent) as IStoredWorkspace;
assert.ok(ws.folders.every(f => (<IRawFileWorkspaceFolder>f).path.indexOf('\\') < 0));
const savedContents = fs.readFileSync(newSavedWorkspace.configPath).toString();
assert.equal(0, savedContents.indexOf('// this is a comment'));
extfs.delSync(workspaceConfigPath);
extfs.delSync(newWorkspaceConfigPath);
});
});
service.deleteUntitledWorkspaceSync(workspace);
});
});
test('saveWorkspace (saved workspace, preserves forward slashes)', () => {
return createWorkspace([process.cwd(), os.tmpdir(), path.join(os.tmpdir(), 'somefolder')]).then(workspace => {
const workspaceConfigPath = path.join(os.tmpdir(), `myworkspace.${Date.now()}.${WORKSPACE_EXTENSION}`);
const newWorkspaceConfigPath = path.join(os.tmpdir(), `mySavedWorkspace.${Date.now()}.${WORKSPACE_EXTENSION}`);
test('rewriteWorkspaceFileForNewLocation (unc paths)', () => {
if (!isWindows) {
return Promise.resolve();
}
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 workspaceLocation = path.join(os.tmpdir(), 'wsloc');
const folder1Location = 'x:\\foo';
const folder2Location = '\\\\server\\share2\\some\\path';
const folder3Location = path.join(os.tmpdir(), 'wsloc', 'inner', 'more');
return service.saveWorkspaceAs(savedWorkspace, newWorkspaceConfigPath).then(newSavedWorkspace => {
assert.ok(newSavedWorkspace.id);
assert.notEqual(newSavedWorkspace.id, workspace.id);
assertPathEquals(newSavedWorkspace.configPath, newWorkspaceConfigPath);
return createWorkspace([folder1Location, folder2Location, folder3Location]).then(workspace => {
const workspaceConfigPath = URI.file(path.join(workspaceLocation, `myworkspace.${Date.now()}.${WORKSPACE_EXTENSION}`));
let origContent = fs.readFileSync(workspace.configPath.fsPath).toString();
const ws = JSON.parse(fs.readFileSync(newSavedWorkspace.configPath).toString()) as IStoredWorkspace;
assert.ok(ws.folders.every(f => (<IRawFileWorkspaceFolder>f).path.indexOf('\\') < 0));
const newContent = rewriteWorkspaceFileForNewLocation(origContent, workspace.configPath, workspaceConfigPath);
extfs.delSync(workspaceConfigPath);
extfs.delSync(newWorkspaceConfigPath);
});
});
const ws = JSON.parse(newContent) as IStoredWorkspace;
assertPathEquals((<IRawFileWorkspaceFolder>ws.folders[0]).path, folder1Location);
assertPathEquals((<IRawFileWorkspaceFolder>ws.folders[1]).path, folder2Location);
assertPathEquals((<IRawFileWorkspaceFolder>ws.folders[2]).path, 'inner\\more');
service.deleteUntitledWorkspaceSync(workspace);
});
});
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));
});
});
test('deleteUntitledWorkspaceSync (saved)', () => {
return createWorkspace([process.cwd(), os.tmpdir()]).then(workspace => {
const workspaceConfigPath = path.join(os.tmpdir(), `myworkspace.${Date.now()}.${WORKSPACE_EXTENSION}`);
return service.saveWorkspaceAs(workspace, workspaceConfigPath).then(savedWorkspace => {
assert.ok(fs.existsSync(savedWorkspace.configPath));
service.deleteUntitledWorkspaceSync(savedWorkspace);
assert.ok(fs.existsSync(savedWorkspace.configPath));
});
service.deleteUntitledWorkspaceSync(workspace);
});
});
test('getUntitledWorkspaceSync', () => {
let untitled = service.getUntitledWorkspacesSync();
assert.equal(0, untitled.length);
assert.equal(untitled.length, 0);
return createWorkspace([process.cwd(), os.tmpdir()]).then(untitledOne => {
assert.ok(fs.existsSync(untitledOne.configPath));
assert.ok(fs.existsSync(untitledOne.configPath.fsPath));
untitled = service.getUntitledWorkspacesSync();
assert.equal(1, untitled.length);
assert.equal(untitledOne.id, untitled[0].id);
assert.equal(untitledOne.id, untitled[0].workspace.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);