Skip to content
This repository was archived by the owner on Nov 18, 2022. It is now read-only.

Commit 1e2e04c

Browse files
committed
reintroduce multi project setup
* enable multiple projects with multiple rls running * change the URI key back to string for workspaces (as it's unreliable) * add selectors such that only the correct rls is used for a given project * take great care to ensure the old setup works normally * add a configuration and turn it off by default.
1 parent 6f65f3b commit 1e2e04c

File tree

3 files changed

+97
-51
lines changed

3 files changed

+97
-51
lines changed

package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,11 @@
219219
"default": true,
220220
"description": "If one root workspace folder is nested in another root folder, look for the Rust config in the outermost root."
221221
},
222+
"rust-client.enableMultiProjectSetup": {
223+
"type": "boolean",
224+
"default": false,
225+
"description": "Allow multiple projects in the same folder, along with remove the constraint that the cargo.toml must be located at the root. (Experimental: might not work for certain setups)"
226+
},
222227
"rust.sysroot": {
223228
"type": [
224229
"string",

src/configuration.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,10 @@ export class RLSConfiguration {
118118
return this.configuration.get<string>('rust-client.rlsPath');
119119
}
120120

121+
public get multiProjectEnabled(): boolean {
122+
return this.configuration.get<boolean>('rust-client.enableMultiProjectSetup', false);
123+
}
124+
121125
// Added ignoreChannel for readChannel function. Otherwise we end in an infinite loop.
122126
public rustupConfig(ignoreChannel: boolean = false): RustupConfig {
123127
return {

src/extension.ts

Lines changed: 88 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -69,23 +69,37 @@ function didOpenTextDocument(
6969
if (!folder) {
7070
return;
7171
}
72+
7273
if (
74+
workspace
75+
.getConfiguration()
76+
.get<boolean>('rust-client.enableMultiProjectSetup', false)
77+
) {
78+
folder = getCargoTomlWorkspace(folder, document.uri.fsPath);
79+
} else if (
7380
workspace
7481
.getConfiguration()
7582
.get<boolean>('rust-client.nestedMultiRootConfigInOutermost', true)
7683
) {
7784
folder = getOuterMostWorkspaceFolder(folder);
7885
}
79-
// folder = getCargoTomlWorkspace(folder, document.uri.fsPath);
86+
8087
if (!folder) {
8188
stopSpinner(`RLS: Cargo.toml missing`);
8289
return;
8390
}
8491

85-
if (!workspaces.has(folder.uri)) {
92+
const folderPath = folder.uri.toString();
93+
94+
if (!workspaces.has(folderPath)) {
95+
8696
const workspace = new ClientWorkspace(folder);
87-
workspaces.set(folder.uri, workspace);
97+
activeWorkspace = workspace;
98+
workspaces.set(folderPath, workspace);
8899
workspace.start(context);
100+
} else {
101+
const ws = workspaces.get(folderPath);
102+
activeWorkspace = typeof ws === "undefined" ? null : ws;
89103
}
90104
}
91105

@@ -110,37 +124,37 @@ function sortedWorkspaceFolders(): string[] {
110124
return _sortedWorkspaceFolders || [];
111125
}
112126

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+
}
144158

145159
function getOuterMostWorkspaceFolder(folder: WorkspaceFolder): WorkspaceFolder {
146160
const sorted = sortedWorkspaceFolders();
@@ -166,13 +180,13 @@ function didChangeWorkspaceFolders(
166180
// if not, and it is a Rust project (i.e., has a Cargo.toml), then create a new client.
167181
for (let folder of e.added) {
168182
folder = getOuterMostWorkspaceFolder(folder);
169-
if (workspaces.has(folder.uri)) {
183+
if (workspaces.has(folder.uri.toString())) {
170184
continue;
171185
}
172186
for (const f of fs.readdirSync(folder.uri.fsPath)) {
173187
if (f === 'Cargo.toml') {
174188
const workspace = new ClientWorkspace(folder);
175-
workspaces.set(folder.uri, workspace);
189+
workspaces.set(folder.uri.toString(), workspace);
176190
workspace.start(context);
177191
break;
178192
}
@@ -181,15 +195,18 @@ function didChangeWorkspaceFolders(
181195

182196
// If a workspace is removed which is a Rust workspace, kill the client.
183197
for (const folder of e.removed) {
184-
const ws = workspaces.get(folder.uri);
198+
const ws = workspaces.get(folder.uri.toString());
185199
if (ws) {
186-
workspaces.delete(folder.uri);
200+
workspaces.delete(folder.uri.toString());
187201
ws.stop();
188202
}
189203
}
190204
}
191205

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;
193210

194211
// We run one RLS and one corresponding language client per workspace folder
195212
// (VSCode workspace, not Cargo workspace). This class contains all the per-client
@@ -209,21 +226,28 @@ class ClientWorkspace {
209226
}
210227

211228
public async start(context: ExtensionContext) {
212-
warnOnMissingCargoToml();
229+
if (!this.config.multiProjectEnabled) {
230+
warnOnMissingCargoToml();
231+
}
232+
213233

214234
startSpinner('RLS', 'Starting');
215235

216236
const serverOptions: ServerOptions = async () => {
217237
await this.autoUpdate();
218238
return this.makeRlsProcess();
219239
};
240+
241+
const pattern = this.config.multiProjectEnabled ? `${this.folder.uri.path}/**` : undefined;
242+
const collectionName = this.config.multiProjectEnabled ? `rust ${this.folder.uri.toString()}` : 'rust';
220243
const clientOptions: LanguageClientOptions = {
221244
// Register the server for Rust files
245+
222246
documentSelector: [
223-
{ language: 'rust', scheme: 'file' },
224-
{ language: 'rust', scheme: 'untitled' },
247+
{ language: 'rust', scheme: 'file', pattern },
248+
{ language: 'rust', scheme: 'untitled', pattern },
225249
],
226-
diagnosticCollectionName: 'rust',
250+
diagnosticCollectionName: collectionName,
227251
synchronize: { configurationSection: 'rust' },
228252
// Controls when to focus the channel rather than when to reveal it in the drop-down list
229253
revealOutputChannelOn: this.config.revealOutputChannelOn,
@@ -259,13 +283,15 @@ class ClientWorkspace {
259283
clientOptions,
260284
);
261285

286+
const selector = this.config.multiProjectEnabled ? { language: 'rust', scheme: 'file', pattern } : { language: 'rust' };
287+
262288
this.setupProgressCounter();
263-
this.registerCommands(context);
289+
this.registerCommands(context, this.config.multiProjectEnabled);
264290
this.disposables.push(activateTaskProvider(this.folder));
265291
this.disposables.push(this.lc.start());
266292
this.disposables.push(
267293
languages.registerSignatureHelpProvider(
268-
{ language: 'rust' },
294+
selector,
269295
new SignatureHelpProvider(this.lc),
270296
'(',
271297
',',
@@ -279,30 +305,41 @@ class ClientWorkspace {
279305
}
280306

281307
this.disposables.forEach(d => d.dispose());
308+
commandsUnregistered = true;
282309
}
283310

284-
private registerCommands(context: ExtensionContext) {
311+
private registerCommands(context: ExtensionContext, multiProjectEnabled: boolean) {
285312
if (!this.lc) {
286313
return;
287314
}
315+
if (multiProjectEnabled && !commandsUnregistered) {
316+
return;
317+
}
288318

319+
commandsUnregistered = false;
289320
const rustupUpdateDisposable = commands.registerCommand(
290321
'rls.update',
291322
() => {
292-
return rustupUpdate(this.config.rustupConfig());
323+
const ws = multiProjectEnabled && activeWorkspace ? activeWorkspace : this;
324+
return rustupUpdate(ws.config.rustupConfig());
293325
},
294326
);
295327
this.disposables.push(rustupUpdateDisposable);
296328

297329
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+
300334
});
301335
this.disposables.push(restartServer);
302336

303337
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+
306343
),
307344
);
308345
}

0 commit comments

Comments
 (0)