@@ -16,20 +16,78 @@ import { compare } from 'vs/base/common/strings';
1616import { TernarySearchTree } from 'vs/base/common/map' ;
1717import { basenameOrAuthority , isEqual } from 'vs/base/common/resources' ;
1818import { 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
2025class 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