Skip to content

Commit

Permalink
[workspace] add spliceRoots
Browse files Browse the repository at this point in the history
Signed-off-by: Anton Kosyakov <[email protected]>
  • Loading branch information
akosyakov committed Mar 18, 2019
1 parent a2f831c commit ac21820
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 69 deletions.
91 changes: 54 additions & 37 deletions packages/workspace/src/browser/workspace-service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import { createMockPreferenceProxy } from '@theia/core/lib/browser/preferences/t
import * as jsoncparser from 'jsonc-parser';
import * as sinon from 'sinon';
import * as chai from 'chai';
import * as assert from 'assert';
import URI from '@theia/core/lib/common/uri';
const expect = chai.expect;

Expand Down Expand Up @@ -483,43 +484,6 @@ describe('WorkspaceService', () => {
await wsService.addRoot(new URI(folderB.uri));
expect(spyWriteFile.calledWith(workspaceFileStat, { folders: [{ path: folderA.uri }, { path: folderB.uri }] })).to.be.true;
});

[true, false].forEach(existTemporaryWorkspaceFile => {
it('should write workspace data into a temporary file when theia currently uses a folder as the workspace ' +
`and the temporary file ${existTemporaryWorkspaceFile ? 'exists' : 'does not exist'}`, async () => {
const stubSave = sinon.stub(wsService, 'save').callsFake(() => { });
const stubWriteWorkspaceFile = sinon.stub(wsService, <any>'writeWorkspaceFile').callsFake(() => { });
toRestore.push(...[stubSave, stubWriteWorkspaceFile]);
wsService['_workspace'] = folderA;
wsService['_roots'] = [folderA];
const homeStat = <FileStat>{
uri: 'file:///home/user',
lastModification: 0,
isDirectory: true
};
const untitledStat = <FileStat>{
uri: 'file:///home/user/.theia/Untitled.theia-workspace',
lastModification: 0,
isDirectory: true
};
(<sinon.SinonStub>mockFilesystem.getCurrentUserHome).resolves(homeStat);
const stubGetFileStat = <sinon.SinonStub>mockFilesystem.getFileStat;
stubGetFileStat.onCall(0).resolves(folderB);
(<sinon.SinonStub>mockFilesystem.exists).resolves(existTemporaryWorkspaceFile);
const stubCreateFile = <sinon.SinonStub>mockFilesystem.createFile;
stubCreateFile.resolves(untitledStat);
if (existTemporaryWorkspaceFile) {
stubGetFileStat.onCall(1).resolves(untitledStat);
}
wsService['_workspace'] = folderA;
wsService['_roots'] = [folderA];

await wsService.addRoot(new URI(folderB.uri));
expect(stubCreateFile.calledWith(untitledStat.uri)).to.eq(!existTemporaryWorkspaceFile);
expect(stubSave.calledWith(untitledStat)).to.be.true;
expect(stubWriteWorkspaceFile.called).to.be.true;
});
});
});

describe('save() function', () => {
Expand Down Expand Up @@ -725,6 +689,59 @@ describe('WorkspaceService', () => {
});
});

describe('spliceRoots', () => {
const workspace = <FileStat>{ uri: 'file:///workspace.theia-workspace', isDirectory: false };
const fooDir = <FileStat>{ uri: 'file:///foo', isDirectory: true };
const workspaceService: WorkspaceService = new WorkspaceService();
workspaceService['getUntitledWorkspace'] = async () => new URI('file:///untitled.theia-workspace');
workspaceService['save'] = async () => { };
workspaceService['getWorkspaceDataFromFile'] = async () => ({ folders: [] });
workspaceService['writeWorkspaceFile'] = async (_, data) => {
workspaceService['_roots'] = data.folders.map(({ path }) => <FileStat>{ uri: path });
return undefined;
};
const assertRemoved = (removed: URI[], ...roots: string[]) =>
assert.deepEqual(removed.map(uri => uri.toString()), roots);
const assertRoots = (...roots: string[]) =>
assert.deepEqual(workspaceService['_roots'].map(root => root.uri), roots);

beforeEach(() => {
workspaceService['_workspace'] = workspace;
workspaceService['_roots'] = [fooDir];
});

it('skip', async () => {
assertRemoved(await workspaceService.spliceRoots(0, 0));
assertRoots('file:///foo');
});

it('add', async () => {
assertRemoved(await workspaceService.spliceRoots(1, 0, new URI('file:///bar')));
assertRoots('file:///foo', 'file:///bar');
});

it('add dups', async () => {
assertRemoved(await workspaceService.spliceRoots(1, 0, new URI('file:///bar'), new URI('file:///baz'), new URI('file:///bar')));
assertRoots('file:///foo', 'file:///bar', 'file:///baz');
});

it('remove', async () => {
assertRemoved(await workspaceService.spliceRoots(0, 1), 'file:///foo');
assertRoots();
});

it('update', async () => {
assertRemoved(await workspaceService.spliceRoots(0, 1, new URI('file:///bar')), 'file:///foo');
assertRoots('file:///bar');
});

it('add untitled', async () => {
workspaceService['_workspace'] = fooDir;
assertRemoved(await workspaceService.spliceRoots(1, 0, new URI('file:///bar')));
assertRoots('file:///foo', 'file:///bar');
});
});

describe('getWorkspaceRootUri() function', () => {
it('should return undefined if no uri is passed into the function', () => {
expect(wsService.getWorkspaceRootUri(undefined)).to.be.undefined;
Expand Down
68 changes: 36 additions & 32 deletions packages/workspace/src/browser/workspace-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -301,27 +301,7 @@ export class WorkspaceService implements FrontendApplicationContribution {
* @param uri URI of the root folder being added
*/
async addRoot(uri: URI): Promise<void> {
await this.roots;

if (!this.opened) {
throw new Error('Folder cannot be added as there is no active workspace or opened folder.');
}
const valid = await this.toValidRoot(uri);
if (!valid) {
throw new Error(`Invalid workspace root URI. Expected an existing directory location. URI: ${uri.toString()}.`);
}

if (this._workspace && !this._roots.find(r => r.uri === valid.uri)) {
if (this._workspace.isDirectory) { // save the workspace data in a temporary file
const tempFile = await this.getTemporaryWorkspaceFile();
if (tempFile) {
await this.save(tempFile);
}
}
const workspaceData = await this.getWorkspaceDataFromFile();
this._workspace = await this.writeWorkspaceFile(this._workspace,
WorkspaceData.buildWorkspaceData([...this._roots, valid], workspaceData ? workspaceData.settings : undefined));
}
await this.spliceRoots(this._roots.length, 0, uri);
}

/**
Expand All @@ -342,6 +322,41 @@ export class WorkspaceService implements FrontendApplicationContribution {
}
}

async spliceRoots(start: number, deleteCount?: number, ...rootsToAdd: URI[]): Promise<URI[]> {
if (!this._workspace) {
throw new Error('There is not active workspace');
}
const dedup = new Set<string>();
const roots = this._roots.map(root => (dedup.add(root.uri), root.uri));
const toAdd: string[] = [];
for (const root of rootsToAdd) {
const uri = root.toString();
if (!dedup.has(uri)) {
dedup.add(uri);
toAdd.push(uri);
}
}
const toRemove = roots.splice(start, deleteCount || 0, ...toAdd);
if (!toRemove.length && !toAdd.length) {
return [];
}
if (this._workspace.isDirectory) {
const utitledWorkspace = await this.getUntitledWorkspace();
if (utitledWorkspace) {
await this.save(utitledWorkspace);
}
}
const currentData = await this.getWorkspaceDataFromFile();
const newData = WorkspaceData.buildWorkspaceData(roots, currentData && currentData.settings);
await this.writeWorkspaceFile(this._workspace, newData);
return toRemove.map(root => new URI(root));
}

protected async getUntitledWorkspace(): Promise<URI | undefined> {
const home = await this.fileSystem.getCurrentUserHome();
return home && getTemporaryWorkspaceFileUri(new URI(home.uri));
}

private async writeWorkspaceFile(workspaceFile: FileStat | undefined, workspaceData: WorkspaceData): Promise<FileStat | undefined> {
if (workspaceFile) {
const data = JSON.stringify(WorkspaceData.transformToRelative(workspaceData, workspaceFile));
Expand All @@ -352,17 +367,6 @@ export class WorkspaceService implements FrontendApplicationContribution {
}
}

private async getTemporaryWorkspaceFile(): Promise<FileStat | undefined> {
const home = await this.fileSystem.getCurrentUserHome();
if (home) {
const tempWorkspaceUri = getTemporaryWorkspaceFileUri(new URI(home.uri));
if (!await this.fileSystem.exists(tempWorkspaceUri.toString())) {
return this.fileSystem.createFile(tempWorkspaceUri.toString());
}
return this.toFileStat(tempWorkspaceUri);
}
}

/**
* Clears current workspace root.
*/
Expand Down

0 comments on commit ac21820

Please sign in to comment.