Skip to content

Commit 40798a9

Browse files
author
Benjamin Pasero
committed
use live objects and avoid promises
1 parent 5c9b0eb commit 40798a9

5 files changed

Lines changed: 124 additions & 54 deletions

File tree

src/vs/platform/workspace/common/workspace.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -205,8 +205,8 @@ export class Workspace implements IWorkspace {
205205
export class WorkspaceFolder implements IWorkspaceFolder {
206206

207207
readonly uri: URI;
208-
readonly name: string;
209-
readonly index: number;
208+
name: string;
209+
index: number;
210210

211211
constructor(data: IWorkspaceFolderData,
212212
readonly raw?: IStoredWorkspaceFolder) {

src/vs/vscode.proposed.d.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -188,9 +188,10 @@ declare module 'vscode' {
188188
* @param deleteCount the optional number of workspace folders to remove.
189189
* @param workspaceFoldersToAdd the optional variable set of workspace folders to add in place of the deleted ones.
190190
* Each workspace is identified with a mandatory URI and an optional name.
191-
* @return A thenable that resolves when the workspace folder was removed successfully.
191+
* @return true if the operation was successfully started. Use the [onDidChangeWorkspaceFolders()](#onDidChangeWorkspaceFolders)
192+
* event to get notified when the workspace folders have been updated.
192193
*/
193-
export function updateWorkspaceFolders(start: number, deleteCount: number, ...workspaceFoldersToAdd: { uri: Uri, name?: string }[]): Thenable<boolean>;
194+
export function updateWorkspaceFolders(start: number, deleteCount: number, ...workspaceFoldersToAdd: { uri: Uri, name?: string }[]): boolean;
194195
}
195196

196197
export namespace window {

src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,13 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape {
5050

5151
// --- workspace ---
5252

53-
$updateWorkspaceFolders(extensionName: string, index: number, deleteCount: number, foldersToAdd: { uri: UriComponents, name?: string }[]): Thenable<boolean> {
53+
$updateWorkspaceFolders(extensionName: string, index: number, deleteCount: number, foldersToAdd: { uri: UriComponents, name?: string }[]): Thenable<void> {
5454
const workspaceFoldersToAdd = foldersToAdd.map(f => ({ uri: URI.revive(f.uri), name: f.name }));
5555

5656
// Indicate in status message
5757
this._statusbarService.setStatusMessage(this.getStatusMessage(extensionName, workspaceFoldersToAdd.length, deleteCount), 10 * 1000 /* 10s */);
5858

59-
return this._workspaceEditingService.updateFolders(index, deleteCount, workspaceFoldersToAdd, true).then(() => true);
59+
return this._workspaceEditingService.updateFolders(index, deleteCount, workspaceFoldersToAdd, true);
6060
}
6161

6262
private getStatusMessage(extensionName, addCount: number, removeCount: number): string {

src/vs/workbench/api/node/extHost.protocol.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -355,7 +355,7 @@ export interface MainThreadWorkspaceShape extends IDisposable {
355355
$startSearch(includePattern: string, includeFolder: string, excludePattern: string, maxResults: number, requestId: number): Thenable<UriComponents[]>;
356356
$cancelSearch(requestId: number): Thenable<boolean>;
357357
$saveAll(includeUntitled?: boolean): Thenable<boolean>;
358-
$updateWorkspaceFolders(extensionName: string, index: number, deleteCount: number, workspaceFoldersToAdd: { uri: UriComponents, name?: string }[]): Thenable<boolean>;
358+
$updateWorkspaceFolders(extensionName: string, index: number, deleteCount: number, workspaceFoldersToAdd: { uri: UriComponents, name?: string }[]): Thenable<void>;
359359
}
360360

361361
export interface IFileChangeDto {

src/vs/workbench/api/node/extHostWorkspace.ts

Lines changed: 116 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,78 @@ import { compare } from 'vs/base/common/strings';
1616
import { TernarySearchTree } from 'vs/base/common/map';
1717
import { basenameOrAuthority, isEqual } from 'vs/base/common/resources';
1818
import { isLinux } from 'vs/base/common/platform';
19+
import { onUnexpectedError } from 'vs/base/common/errors';
20+
21+
function isFolderEqual(folderA: URI, folderB: URI): boolean {
22+
return isEqual(folderA, folderB, !isLinux);
23+
}
1924

2025
class Workspace2 extends Workspace {
2126

22-
static fromData(data: IWorkspaceData) {
27+
static acceptWorkspaceData(data: IWorkspaceData, oldWorkspace?: Workspace2): { workspace: Workspace2, added: vscode.WorkspaceFolder[], removed: vscode.WorkspaceFolder[] } {
2328
if (!data) {
24-
return null;
29+
return { workspace: null, added: [], removed: [] };
30+
}
31+
32+
const { id, name, folders } = data;
33+
const newWorkspaceFolders: WorkspaceFolder[] = [];
34+
35+
// If we have an existing workspace, we try to find the folders that match our
36+
// data and update their properties. It could be that an extension stored them
37+
// for later use and we want to keep them "live" if they are still present.
38+
if (oldWorkspace) {
39+
folders.forEach((folderData, index) => {
40+
const folderUri = URI.revive(folderData.uri);
41+
const existingFolder = Workspace2._findFolder(oldWorkspace, folderUri);
42+
43+
if (existingFolder) {
44+
existingFolder.name = folderData.name;
45+
existingFolder.index = folderData.index;
46+
47+
newWorkspaceFolders.push(existingFolder);
48+
} else {
49+
newWorkspaceFolders.push(new WorkspaceFolder({ name: folderData.name, index, uri: folderUri }));
50+
}
51+
});
2552
} else {
26-
const { id, name, folders } = data;
27-
return new Workspace2(
28-
id,
29-
name,
30-
folders.map(({ uri, name, index }) => new WorkspaceFolder({ name, index, uri: URI.revive(uri) }))
31-
);
53+
newWorkspaceFolders.push(...folders.map(({ uri, name, index }) => new WorkspaceFolder({ name, index, uri: URI.revive(uri) })));
54+
}
55+
56+
const workspace = new Workspace2(id, name, newWorkspaceFolders);
57+
58+
const oldRoots = oldWorkspace ? oldWorkspace.workspaceFolders.sort(Workspace2.compareWorkspaceFolderByUri) : [];
59+
const newRoots = workspace.workspaceFolders.sort(Workspace2.compareWorkspaceFolderByUri);
60+
61+
const { added, removed } = delta(oldRoots, newRoots, Workspace2.compareWorkspaceFolderByUri);
62+
63+
return { workspace, added, removed };
64+
}
65+
66+
static compareWorkspaceFolderByUri(a: vscode.WorkspaceFolder, b: vscode.WorkspaceFolder, includeName?: boolean): number {
67+
if (isFolderEqual(a.uri, b.uri)) {
68+
return 0;
69+
}
70+
71+
return compare(a.uri.toString(), b.uri.toString());
72+
}
73+
74+
static compareWorkspaceFolderByUriAndName(a: vscode.WorkspaceFolder, b: vscode.WorkspaceFolder): number {
75+
if (isFolderEqual(a.uri, b.uri) && compare(a.name, b.name) === 0) {
76+
return 0;
77+
}
78+
79+
return compare(a.uri.toString(), b.uri.toString()) + compare(a.name, b.name);
80+
}
81+
82+
private static _findFolder(workspace: Workspace2, folderUriToFind: URI): WorkspaceFolder {
83+
for (let i = 0; i < workspace.folders.length; i++) {
84+
const folder = workspace.folders[i];
85+
if (isFolderEqual(folder.uri, folderUriToFind)) {
86+
return folder;
87+
}
3288
}
89+
90+
return undefined;
3391
}
3492

3593
private readonly _workspaceFolders: vscode.WorkspaceFolder[] = [];
@@ -65,77 +123,95 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape {
65123

66124
private readonly _onDidChangeWorkspace = new Emitter<vscode.WorkspaceFoldersChangeEvent>();
67125
private readonly _proxy: MainThreadWorkspaceShape;
68-
private _workspace: Workspace2;
126+
127+
private _confirmedWorkspace: Workspace2;
128+
private _unconfirmedWorkspace: Workspace2;
69129

70130
readonly onDidChangeWorkspace: Event<vscode.WorkspaceFoldersChangeEvent> = this._onDidChangeWorkspace.event;
71131

72132
constructor(mainContext: IMainContext, data: IWorkspaceData) {
73133
this._proxy = mainContext.getProxy(MainContext.MainThreadWorkspace);
74-
this._workspace = Workspace2.fromData(data);
134+
this._confirmedWorkspace = Workspace2.acceptWorkspaceData(data).workspace;
75135
}
76136

77137
// --- workspace ---
78138

79139
get workspace(): Workspace {
80-
return this._workspace;
140+
return this._actualWorkspace;
141+
}
142+
143+
private get _actualWorkspace(): Workspace2 {
144+
return this._unconfirmedWorkspace || this._confirmedWorkspace;
81145
}
82146

83147
getWorkspaceFolders(): vscode.WorkspaceFolder[] {
84-
if (!this._workspace) {
148+
if (!this._actualWorkspace) {
85149
return undefined;
86-
} else {
87-
return this._workspace.workspaceFolders.slice(0);
88150
}
151+
return this._actualWorkspace.workspaceFolders.slice(0);
89152
}
90153

91-
updateWorkspaceFolders(extensionName: string, index: number, deleteCount: number, ...workspaceFoldersToAdd: { uri: vscode.Uri, name?: string }[]): Thenable<boolean> {
154+
updateWorkspaceFolders(extensionName: string, index: number, deleteCount: number, ...workspaceFoldersToAdd: { uri: vscode.Uri, name?: string }[]): boolean {
92155
const validatedDistinctWorkspaceFoldersToAdd: { uri: vscode.Uri, name?: string }[] = [];
93156
if (Array.isArray(workspaceFoldersToAdd)) {
94157
workspaceFoldersToAdd.forEach(folderToAdd => {
95-
if (URI.isUri(folderToAdd.uri) && !validatedDistinctWorkspaceFoldersToAdd.some(f => isEqual(f.uri, folderToAdd.uri, !isLinux))) {
158+
if (URI.isUri(folderToAdd.uri) && !validatedDistinctWorkspaceFoldersToAdd.some(f => isFolderEqual(f.uri, folderToAdd.uri))) {
96159
validatedDistinctWorkspaceFoldersToAdd.push(folderToAdd);
97160
}
98161
});
99162
}
100163

101164
if ([index, deleteCount].some(i => typeof i !== 'number' || i < 0)) {
102-
return Promise.resolve(false); // validate numbers
165+
return false; // validate numbers
103166
}
104167

105168
if (deleteCount === 0 && validatedDistinctWorkspaceFoldersToAdd.length === 0) {
106-
return Promise.resolve(false); // nothing to delete or add
169+
return false; // nothing to delete or add
107170
}
108171

109-
const currentWorkspaceFolders: vscode.WorkspaceFolder[] = this._workspace ? this._workspace.workspaceFolders : [];
172+
const currentWorkspaceFolders: vscode.WorkspaceFolder[] = this._actualWorkspace ? this._actualWorkspace.workspaceFolders : [];
110173
if (index + deleteCount > currentWorkspaceFolders.length) {
111-
return Promise.resolve(false); // cannot delete more than we have
174+
return false; // cannot delete more than we have
112175
}
113176

114177
const newWorkspaceFolders = currentWorkspaceFolders.slice(0);
115178
newWorkspaceFolders.splice(index, deleteCount, ...validatedDistinctWorkspaceFoldersToAdd.map((f, index) => ({ uri: f.uri, name: f.name || basenameOrAuthority(f.uri), index })));
116-
const { added, removed } = delta(currentWorkspaceFolders, newWorkspaceFolders, ExtHostWorkspace._compareWorkspaceFolderByUriAndName);
179+
const oldRoots = currentWorkspaceFolders.sort(Workspace2.compareWorkspaceFolderByUri);
180+
const newRoots = newWorkspaceFolders.sort(Workspace2.compareWorkspaceFolderByUri);
181+
const { added, removed } = delta(oldRoots, newRoots, Workspace2.compareWorkspaceFolderByUriAndName);
117182
if (added.length === 0 && removed.length === 0) {
118-
return Promise.resolve(false); // nothing actually changed
183+
return false; // nothing actually changed
184+
}
185+
186+
// Trigger on main side
187+
this._proxy.$updateWorkspaceFolders(extensionName, index, deleteCount, validatedDistinctWorkspaceFoldersToAdd).then(null, onUnexpectedError);
188+
189+
// Update directly here. The workspace is unconfirmed as long as we did not get an
190+
// acknowledgement from the main side (via acceptWorkspaceData)
191+
if (this._actualWorkspace) {
192+
this._unconfirmedWorkspace = Workspace2.acceptWorkspaceData({ id: this._actualWorkspace.id, name: this._actualWorkspace.name, configuration: this._actualWorkspace.configuration, folders: newWorkspaceFolders }, this._actualWorkspace).workspace;
119193
}
120194

121-
return this._proxy.$updateWorkspaceFolders(extensionName, index, deleteCount, validatedDistinctWorkspaceFoldersToAdd);
195+
return true;
122196
}
123197

124198
getWorkspaceFolder(uri: vscode.Uri, resolveParent?: boolean): vscode.WorkspaceFolder {
125-
if (!this._workspace) {
199+
if (!this._actualWorkspace) {
126200
return undefined;
127201
}
128-
return this._workspace.getWorkspaceFolder(uri, resolveParent);
202+
return this._actualWorkspace.getWorkspaceFolder(uri, resolveParent);
129203
}
130204

131205
getPath(): string {
206+
132207
// this is legacy from the days before having
133208
// multi-root and we keep it only alive if there
134209
// is just one workspace folder.
135-
if (!this._workspace) {
210+
if (!this._actualWorkspace) {
136211
return undefined;
137212
}
138-
const { folders } = this._workspace;
213+
214+
const { folders } = this._actualWorkspace;
139215
if (folders.length === 0) {
140216
return undefined;
141217
}
@@ -165,7 +241,7 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape {
165241
}
166242

167243
if (typeof includeWorkspace === 'undefined') {
168-
includeWorkspace = this.workspace.folders.length > 1;
244+
includeWorkspace = this._actualWorkspace.folders.length > 1;
169245
}
170246

171247
let result = relative(folder.uri.fsPath, path);
@@ -177,27 +253,20 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape {
177253

178254
$acceptWorkspaceData(data: IWorkspaceData): void {
179255

180-
// keep old workspace folder, build new workspace, and
181-
// capture new workspace folders. Compute delta between
182-
// them send that as event
183-
const oldRoots = this._workspace ? this._workspace.workspaceFolders.sort(ExtHostWorkspace._compareWorkspaceFolderByUri) : [];
184-
185-
this._workspace = Workspace2.fromData(data);
186-
const newRoots = this._workspace ? this._workspace.workspaceFolders.sort(ExtHostWorkspace._compareWorkspaceFolderByUri) : [];
256+
const { workspace, added, removed } = Workspace2.acceptWorkspaceData(data, this._confirmedWorkspace /* use confirmed workspace to produce the true delta from last time */);
187257

188-
const { added, removed } = delta(oldRoots, newRoots, ExtHostWorkspace._compareWorkspaceFolderByUri);
189-
this._onDidChangeWorkspace.fire(Object.freeze({
190-
added: Object.freeze<vscode.WorkspaceFolder[]>(added),
191-
removed: Object.freeze<vscode.WorkspaceFolder[]>(removed)
192-
}));
193-
}
258+
// Update our workspace object. We have a confirmed workspace, so we drop our
259+
// unconfirmed workspace.
260+
this._confirmedWorkspace = workspace;
261+
this._unconfirmedWorkspace = undefined;
194262

195-
private static _compareWorkspaceFolderByUri(a: vscode.WorkspaceFolder, b: vscode.WorkspaceFolder, includeName?: boolean): number {
196-
return compare(a.uri.toString(), b.uri.toString());
197-
}
198-
199-
private static _compareWorkspaceFolderByUriAndName(a: vscode.WorkspaceFolder, b: vscode.WorkspaceFolder): number {
200-
return compare(a.uri.toString(), b.uri.toString()) + compare(a.name, b.name);
263+
// Events
264+
if (added.length || removed.length) {
265+
this._onDidChangeWorkspace.fire(Object.freeze({
266+
added: Object.freeze<vscode.WorkspaceFolder[]>(added),
267+
removed: Object.freeze<vscode.WorkspaceFolder[]>(removed)
268+
}));
269+
}
201270
}
202271

203272
// --- search ---

0 commit comments

Comments
 (0)