@@ -69,23 +69,37 @@ function didOpenTextDocument(
69
69
if ( ! folder ) {
70
70
return ;
71
71
}
72
+
72
73
if (
74
+ workspace
75
+ . getConfiguration ( )
76
+ . get < boolean > ( 'rust-client.enableMultiProjectSetup' , false )
77
+ ) {
78
+ folder = getCargoTomlWorkspace ( folder , document . uri . fsPath ) ;
79
+ } else if (
73
80
workspace
74
81
. getConfiguration ( )
75
82
. get < boolean > ( 'rust-client.nestedMultiRootConfigInOutermost' , true )
76
83
) {
77
84
folder = getOuterMostWorkspaceFolder ( folder ) ;
78
85
}
79
- // folder = getCargoTomlWorkspace(folder, document.uri.fsPath);
86
+
80
87
if ( ! folder ) {
81
88
stopSpinner ( `RLS: Cargo.toml missing` ) ;
82
89
return ;
83
90
}
84
91
85
- if ( ! workspaces . has ( folder . uri ) ) {
92
+ const folderPath = folder . uri . toString ( ) ;
93
+
94
+ if ( ! workspaces . has ( folderPath ) ) {
95
+
86
96
const workspace = new ClientWorkspace ( folder ) ;
87
- workspaces . set ( folder . uri , workspace ) ;
97
+ activeWorkspace = workspace ;
98
+ workspaces . set ( folderPath , workspace ) ;
88
99
workspace . start ( context ) ;
100
+ } else {
101
+ const ws = workspaces . get ( folderPath ) ;
102
+ activeWorkspace = typeof ws === "undefined" ? null : ws ;
89
103
}
90
104
}
91
105
@@ -110,37 +124,37 @@ function sortedWorkspaceFolders(): string[] {
110
124
return _sortedWorkspaceFolders || [ ] ;
111
125
}
112
126
113
- // function getCargoTomlWorkspace(cur_workspace: WorkspaceFolder, file_path: string): WorkspaceFolder {
114
- // if (!cur_workspace) {
115
- // return cur_workspace;
116
- // }
117
-
118
- // const workspace_root = path.parse(cur_workspace.uri.fsPath).dir;
119
- // const root_manifest = path.join(workspace_root, 'Cargo.toml');
120
- // if (fs.existsSync(root_manifest)) {
121
- // return cur_workspace;
122
- // }
123
-
124
- // let current = file_path;
125
-
126
- // while (true) {
127
- // const old = current;
128
- // current = path.dirname(current);
129
- // if (old == current) {
130
- // break;
131
- // }
132
- // if (workspace_root == path.parse(current).dir) {
133
- // break;
134
- // }
135
-
136
- // const cargo_path = path.join(current, 'Cargo.toml');
137
- // if (fs.existsSync(cargo_path)) {
138
- // return { ...cur_workspace, uri: Uri.parse(current) };
139
- // }
140
- // }
141
-
142
- // return cur_workspace;
143
- // }
127
+ function getCargoTomlWorkspace ( cur_workspace : WorkspaceFolder , file_path : string ) : WorkspaceFolder {
128
+ if ( ! cur_workspace ) {
129
+ return cur_workspace ;
130
+ }
131
+
132
+ const workspace_root = path . parse ( cur_workspace . uri . fsPath ) . dir ;
133
+ const root_manifest = path . join ( workspace_root , 'Cargo.toml' ) ;
134
+ if ( fs . existsSync ( root_manifest ) ) {
135
+ return cur_workspace ;
136
+ }
137
+
138
+ let current = file_path ;
139
+
140
+ while ( true ) {
141
+ const old = current ;
142
+ current = path . dirname ( current ) ;
143
+ if ( old == current ) {
144
+ break ;
145
+ }
146
+ if ( workspace_root == path . parse ( current ) . dir ) {
147
+ break ;
148
+ }
149
+
150
+ const cargo_path = path . join ( current , 'Cargo.toml' ) ;
151
+ if ( fs . existsSync ( cargo_path ) ) {
152
+ return { ...cur_workspace , uri : Uri . parse ( current ) } ;
153
+ }
154
+ }
155
+
156
+ return cur_workspace ;
157
+ }
144
158
145
159
function getOuterMostWorkspaceFolder ( folder : WorkspaceFolder ) : WorkspaceFolder {
146
160
const sorted = sortedWorkspaceFolders ( ) ;
@@ -166,13 +180,13 @@ function didChangeWorkspaceFolders(
166
180
// if not, and it is a Rust project (i.e., has a Cargo.toml), then create a new client.
167
181
for ( let folder of e . added ) {
168
182
folder = getOuterMostWorkspaceFolder ( folder ) ;
169
- if ( workspaces . has ( folder . uri ) ) {
183
+ if ( workspaces . has ( folder . uri . toString ( ) ) ) {
170
184
continue ;
171
185
}
172
186
for ( const f of fs . readdirSync ( folder . uri . fsPath ) ) {
173
187
if ( f === 'Cargo.toml' ) {
174
188
const workspace = new ClientWorkspace ( folder ) ;
175
- workspaces . set ( folder . uri , workspace ) ;
189
+ workspaces . set ( folder . uri . toString ( ) , workspace ) ;
176
190
workspace . start ( context ) ;
177
191
break ;
178
192
}
@@ -181,15 +195,18 @@ function didChangeWorkspaceFolders(
181
195
182
196
// If a workspace is removed which is a Rust workspace, kill the client.
183
197
for ( const folder of e . removed ) {
184
- const ws = workspaces . get ( folder . uri ) ;
198
+ const ws = workspaces . get ( folder . uri . toString ( ) ) ;
185
199
if ( ws ) {
186
- workspaces . delete ( folder . uri ) ;
200
+ workspaces . delete ( folder . uri . toString ( ) ) ;
187
201
ws . stop ( ) ;
188
202
}
189
203
}
190
204
}
191
205
192
- const workspaces : Map < Uri , ClientWorkspace > = new Map ( ) ;
206
+ // Don't use URI as it's unreliable the same path might not become the same URI.
207
+ const workspaces : Map < string , ClientWorkspace > = new Map ( ) ;
208
+ let activeWorkspace : ClientWorkspace | null ;
209
+ let commandsUnregistered : boolean = true ;
193
210
194
211
// We run one RLS and one corresponding language client per workspace folder
195
212
// (VSCode workspace, not Cargo workspace). This class contains all the per-client
@@ -209,21 +226,28 @@ class ClientWorkspace {
209
226
}
210
227
211
228
public async start ( context : ExtensionContext ) {
212
- warnOnMissingCargoToml ( ) ;
229
+ if ( ! this . config . multiProjectEnabled ) {
230
+ warnOnMissingCargoToml ( ) ;
231
+ }
232
+
213
233
214
234
startSpinner ( 'RLS' , 'Starting' ) ;
215
235
216
236
const serverOptions : ServerOptions = async ( ) => {
217
237
await this . autoUpdate ( ) ;
218
238
return this . makeRlsProcess ( ) ;
219
239
} ;
240
+
241
+ const pattern = this . config . multiProjectEnabled ? `${ this . folder . uri . path } /**` : undefined ;
242
+ const collectionName = this . config . multiProjectEnabled ? `rust ${ this . folder . uri . toString ( ) } ` : 'rust' ;
220
243
const clientOptions : LanguageClientOptions = {
221
244
// Register the server for Rust files
245
+
222
246
documentSelector : [
223
- { language : 'rust' , scheme : 'file' } ,
224
- { language : 'rust' , scheme : 'untitled' } ,
247
+ { language : 'rust' , scheme : 'file' , pattern } ,
248
+ { language : 'rust' , scheme : 'untitled' , pattern } ,
225
249
] ,
226
- diagnosticCollectionName : 'rust' ,
250
+ diagnosticCollectionName : collectionName ,
227
251
synchronize : { configurationSection : 'rust' } ,
228
252
// Controls when to focus the channel rather than when to reveal it in the drop-down list
229
253
revealOutputChannelOn : this . config . revealOutputChannelOn ,
@@ -259,13 +283,15 @@ class ClientWorkspace {
259
283
clientOptions ,
260
284
) ;
261
285
286
+ const selector = this . config . multiProjectEnabled ? { language : 'rust' , scheme : 'file' , pattern } : { language : 'rust' } ;
287
+
262
288
this . setupProgressCounter ( ) ;
263
- this . registerCommands ( context ) ;
289
+ this . registerCommands ( context , this . config . multiProjectEnabled ) ;
264
290
this . disposables . push ( activateTaskProvider ( this . folder ) ) ;
265
291
this . disposables . push ( this . lc . start ( ) ) ;
266
292
this . disposables . push (
267
293
languages . registerSignatureHelpProvider (
268
- { language : 'rust' } ,
294
+ selector ,
269
295
new SignatureHelpProvider ( this . lc ) ,
270
296
'(' ,
271
297
',' ,
@@ -279,30 +305,41 @@ class ClientWorkspace {
279
305
}
280
306
281
307
this . disposables . forEach ( d => d . dispose ( ) ) ;
308
+ commandsUnregistered = true ;
282
309
}
283
310
284
- private registerCommands ( context : ExtensionContext ) {
311
+ private registerCommands ( context : ExtensionContext , multiProjectEnabled : boolean ) {
285
312
if ( ! this . lc ) {
286
313
return ;
287
314
}
315
+ if ( multiProjectEnabled && ! commandsUnregistered ) {
316
+ return ;
317
+ }
288
318
319
+ commandsUnregistered = false ;
289
320
const rustupUpdateDisposable = commands . registerCommand (
290
321
'rls.update' ,
291
322
( ) => {
292
- return rustupUpdate ( this . config . rustupConfig ( ) ) ;
323
+ const ws = multiProjectEnabled && activeWorkspace ? activeWorkspace : this ;
324
+ return rustupUpdate ( ws . config . rustupConfig ( ) ) ;
293
325
} ,
294
326
) ;
295
327
this . disposables . push ( rustupUpdateDisposable ) ;
296
328
297
329
const restartServer = commands . registerCommand ( 'rls.restart' , async ( ) => {
298
- await this . stop ( ) ;
299
- return this . start ( context ) ;
330
+ const ws = multiProjectEnabled && activeWorkspace ? activeWorkspace : this ;
331
+ await ws . stop ( ) ;
332
+ return ws . start ( context ) ;
333
+
300
334
} ) ;
301
335
this . disposables . push ( restartServer ) ;
302
336
303
337
this . disposables . push (
304
- commands . registerCommand ( 'rls.run' , ( cmd : Execution ) =>
305
- runRlsCommand ( this . folder , cmd ) ,
338
+ commands . registerCommand ( 'rls.run' , ( cmd : Execution ) => {
339
+ const ws = multiProjectEnabled && activeWorkspace ? activeWorkspace : this ;
340
+ runRlsCommand ( ws . folder , cmd )
341
+ } ,
342
+
306
343
) ,
307
344
) ;
308
345
}
0 commit comments