@@ -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 = nearestCargoTomlWorkspace ( 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,41 @@ 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 nearestCargoTomlWorkspace (
128
+ curWorkspace : WorkspaceFolder ,
129
+ filePath : string ,
130
+ ) : WorkspaceFolder {
131
+ if ( ! curWorkspace ) {
132
+ return curWorkspace ;
133
+ }
134
+
135
+ const workspaceRoot = path . parse ( curWorkspace . uri . fsPath ) . dir ;
136
+ const rootManifest = path . join ( workspaceRoot , 'Cargo.toml' ) ;
137
+ if ( fs . existsSync ( rootManifest ) ) {
138
+ return curWorkspace ;
139
+ }
140
+
141
+ let current = filePath ;
142
+
143
+ while ( true ) {
144
+ const old = current ;
145
+ current = path . dirname ( current ) ;
146
+ if ( old === current ) {
147
+ break ;
148
+ }
149
+ if ( workspaceRoot === path . parse ( current ) . dir ) {
150
+ break ;
151
+ }
152
+
153
+ const cargoPath = path . join ( current , 'Cargo.toml' ) ;
154
+ const uri = Uri . file ( current ) ;
155
+ if ( fs . existsSync ( cargoPath ) ) {
156
+ return { ...curWorkspace , uri } ;
157
+ }
158
+ }
159
+
160
+ return curWorkspace ;
161
+ }
144
162
145
163
function getOuterMostWorkspaceFolder ( folder : WorkspaceFolder ) : WorkspaceFolder {
146
164
const sorted = sortedWorkspaceFolders ( ) ;
@@ -166,13 +184,13 @@ function didChangeWorkspaceFolders(
166
184
// if not, and it is a Rust project (i.e., has a Cargo.toml), then create a new client.
167
185
for ( let folder of e . added ) {
168
186
folder = getOuterMostWorkspaceFolder ( folder ) ;
169
- if ( workspaces . has ( folder . uri ) ) {
187
+ if ( workspaces . has ( folder . uri . toString ( ) ) ) {
170
188
continue ;
171
189
}
172
190
for ( const f of fs . readdirSync ( folder . uri . fsPath ) ) {
173
191
if ( f === 'Cargo.toml' ) {
174
192
const workspace = new ClientWorkspace ( folder ) ;
175
- workspaces . set ( folder . uri , workspace ) ;
193
+ workspaces . set ( folder . uri . toString ( ) , workspace ) ;
176
194
workspace . start ( context ) ;
177
195
break ;
178
196
}
@@ -181,15 +199,18 @@ function didChangeWorkspaceFolders(
181
199
182
200
// If a workspace is removed which is a Rust workspace, kill the client.
183
201
for ( const folder of e . removed ) {
184
- const ws = workspaces . get ( folder . uri ) ;
202
+ const ws = workspaces . get ( folder . uri . toString ( ) ) ;
185
203
if ( ws ) {
186
- workspaces . delete ( folder . uri ) ;
204
+ workspaces . delete ( folder . uri . toString ( ) ) ;
187
205
ws . stop ( ) ;
188
206
}
189
207
}
190
208
}
191
209
192
- const workspaces : Map < Uri , ClientWorkspace > = new Map ( ) ;
210
+ // Don't use URI as it's unreliable the same path might not become the same URI.
211
+ const workspaces : Map < string , ClientWorkspace > = new Map ( ) ;
212
+ let activeWorkspace : ClientWorkspace | null ;
213
+ let commandsUnregistered : boolean = true ;
193
214
194
215
// We run one RLS and one corresponding language client per workspace folder
195
216
// (VSCode workspace, not Cargo workspace). This class contains all the per-client
@@ -209,21 +230,28 @@ class ClientWorkspace {
209
230
}
210
231
211
232
public async start ( context : ExtensionContext ) {
212
- warnOnMissingCargoToml ( ) ;
233
+ if ( ! this . config . multiProjectEnabled ) {
234
+ warnOnMissingCargoToml ( ) ;
235
+ }
236
+
213
237
214
238
startSpinner ( 'RLS' , 'Starting' ) ;
215
239
216
240
const serverOptions : ServerOptions = async ( ) => {
217
241
await this . autoUpdate ( ) ;
218
242
return this . makeRlsProcess ( ) ;
219
243
} ;
244
+
245
+ const pattern = this . config . multiProjectEnabled ? `${ this . folder . uri . path } /**` : undefined ;
246
+ const collectionName = this . config . multiProjectEnabled ? `rust ${ this . folder . uri . toString ( ) } ` : 'rust' ;
220
247
const clientOptions : LanguageClientOptions = {
221
248
// Register the server for Rust files
249
+
222
250
documentSelector : [
223
- { language : 'rust' , scheme : 'file' } ,
224
- { language : 'rust' , scheme : 'untitled' } ,
251
+ { language : 'rust' , scheme : 'file' , pattern } ,
252
+ { language : 'rust' , scheme : 'untitled' , pattern } ,
225
253
] ,
226
- diagnosticCollectionName : 'rust' ,
254
+ diagnosticCollectionName : collectionName ,
227
255
synchronize : { configurationSection : 'rust' } ,
228
256
// Controls when to focus the channel rather than when to reveal it in the drop-down list
229
257
revealOutputChannelOn : this . config . revealOutputChannelOn ,
@@ -259,13 +287,15 @@ class ClientWorkspace {
259
287
clientOptions ,
260
288
) ;
261
289
290
+ const selector = this . config . multiProjectEnabled ? { language : 'rust' , scheme : 'file' , pattern } : { language : 'rust' } ;
291
+
262
292
this . setupProgressCounter ( ) ;
263
- this . registerCommands ( context ) ;
293
+ this . registerCommands ( context , this . config . multiProjectEnabled ) ;
264
294
this . disposables . push ( activateTaskProvider ( this . folder ) ) ;
265
295
this . disposables . push ( this . lc . start ( ) ) ;
266
296
this . disposables . push (
267
297
languages . registerSignatureHelpProvider (
268
- { language : 'rust' } ,
298
+ selector ,
269
299
new SignatureHelpProvider ( this . lc ) ,
270
300
'(' ,
271
301
',' ,
@@ -279,30 +309,41 @@ class ClientWorkspace {
279
309
}
280
310
281
311
this . disposables . forEach ( d => d . dispose ( ) ) ;
312
+ commandsUnregistered = true ;
282
313
}
283
314
284
- private registerCommands ( context : ExtensionContext ) {
315
+ private registerCommands ( context : ExtensionContext , multiProjectEnabled : boolean ) {
285
316
if ( ! this . lc ) {
286
317
return ;
287
318
}
319
+ if ( multiProjectEnabled && ! commandsUnregistered ) {
320
+ return ;
321
+ }
288
322
323
+ commandsUnregistered = false ;
289
324
const rustupUpdateDisposable = commands . registerCommand (
290
325
'rls.update' ,
291
326
( ) => {
292
- return rustupUpdate ( this . config . rustupConfig ( ) ) ;
327
+ const ws = multiProjectEnabled && activeWorkspace ? activeWorkspace : this ;
328
+ return rustupUpdate ( ws . config . rustupConfig ( ) ) ;
293
329
} ,
294
330
) ;
295
331
this . disposables . push ( rustupUpdateDisposable ) ;
296
332
297
333
const restartServer = commands . registerCommand ( 'rls.restart' , async ( ) => {
298
- await this . stop ( ) ;
299
- return this . start ( context ) ;
334
+ const ws = multiProjectEnabled && activeWorkspace ? activeWorkspace : this ;
335
+ await ws . stop ( ) ;
336
+ return ws . start ( context ) ;
337
+
300
338
} ) ;
301
339
this . disposables . push ( restartServer ) ;
302
340
303
341
this . disposables . push (
304
- commands . registerCommand ( 'rls.run' , ( cmd : Execution ) =>
305
- runRlsCommand ( this . folder , cmd ) ,
342
+ commands . registerCommand ( 'rls.run' , ( cmd : Execution ) => {
343
+ const ws = multiProjectEnabled && activeWorkspace ? activeWorkspace : this ;
344
+ runRlsCommand ( ws . folder , cmd )
345
+ } ,
346
+
306
347
) ,
307
348
) ;
308
349
}
0 commit comments