Skip to content

Commit ec63f68

Browse files
committed
Allow having Cargo.toml in a subdirectory with rust-client.cargoSubdir
Fixes rust-lang#222. This patch is quite hacky and may reveal my lack of knowledge on the RLS or LSP architectures. My idea was to give the user the opportunity using the variable `rust-client.cargoSubdir` to select a subdirectory as the root for the Cargo.toml file. To do that, I hacked the vscode's workspaceFoldervariable; my hack won't work with multi-workspaces. Alternate solutions: - (not so good idea) add a new setting to the initialize JSON-RPC (similar to the existing `omitInitBuild` in the rls server) that would set a different path that the one given by vscode's workspaceFolder. - (better) we add support for multi-root workspaces in the RLS server; (mentionned in <rust-lang/rls#608>) but this does not really help when we want to open a general project where one of the subfolders is a rust project. - we tell people that in order to get RLS working in a subdirectory, they must use the 'Multi-crate projects' feature or the 'rlsCommandOverride' feature (see README at <https://github.com/mehcode/atom-ide-rust>).
1 parent d214d0b commit ec63f68

File tree

3 files changed

+53
-11
lines changed

3 files changed

+53
-11
lines changed

package.json

+7-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@
4747
"postinstall": "node ./node_modules/vscode/bin/install"
4848
},
4949
"dependencies": {
50-
"vscode-languageclient": "^4.1.4"
50+
"vscode-languageclient": "^4.1.4",
51+
"path": "^0.12.0"
5152
},
5253
"devDependencies": {
5354
"@types/mocha": "^2.2.43",
@@ -191,6 +192,11 @@
191192
"default": "rls-preview",
192193
"description": "Name of the RLS rustup component."
193194
},
195+
"rust-client.cargoSubdir": {
196+
"type": "string",
197+
"default": "",
198+
"description": "Relative path to Cargo.toml. By default, it is workspace root."
199+
},
194200
"rust.sysroot": {
195201
"type": [
196202
"string",

src/configuration.ts

+2
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ export class RLSConfiguration {
4040
* If specified, RLS will be spawned by executing a file at the given path.
4141
*/
4242
public readonly rlsPath: string | null;
43+
public readonly cargoSubdir: string | null;
4344

4445
public static loadFromWorkspace(): RLSConfiguration {
4546
const configuration = workspace.getConfiguration();
@@ -66,6 +67,7 @@ export class RLSConfiguration {
6667
if (!this.rlsPath) {
6768
this.rlsPath = rlsPath;
6869
}
70+
this.cargoSubdir = configuration.get('rust-client.cargoSubdir', null);
6971
}
7072

7173
private static readRevealOutputChannelOn(configuration: WorkspaceConfiguration) {

src/extension.ts

+44-10
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,10 @@ import { activateTaskProvider, deactivateTaskProvider } from './tasks';
1717

1818
import * as child_process from 'child_process';
1919
import * as fs from 'fs';
20+
import * as path from 'path';
2021

2122
import { commands, ExtensionContext, IndentAction, languages, TextEditor,
22-
TextEditorEdit, window, workspace } from 'vscode';
23+
TextEditorEdit, window, workspace, Uri } from 'vscode';
2324
import { LanguageClient, LanguageClientOptions, Location, NotificationType,
2425
ServerOptions } from 'vscode-languageclient';
2526
import { execFile, ExecChildProcessResult } from './utils/child_process';
@@ -82,14 +83,19 @@ async function makeRlsProcess(): Promise<child_process.ChildProcess> {
8283
const rls_path = CONFIGURATION.rlsPath;
8384

8485
let childProcessPromise: Promise<child_process.ChildProcess>;
86+
const cargoSubdir = workspace.getConfiguration().get('rust-client.cargoSubdir');
87+
const cargo_toml_path = workspace.rootPath + ( cargoSubdir ? '/'+cargoSubdir : '');
8588
if (rls_path) {
8689
const env = await makeRlsEnv(true);
87-
console.info('running ' + rls_path);
88-
childProcessPromise = Promise.resolve(child_process.spawn(rls_path, [], { env }));
90+
console.info('running ' + rls_path + ' using CWD "'+cargo_toml_path+'"');
91+
childProcessPromise = Promise.resolve(child_process.spawn(rls_path, [], { cwd: cargo_toml_path, env }));
8992
} else {
9093
const env = await makeRlsEnv();
91-
console.info('running with rustup');
92-
childProcessPromise = runRlsViaRustup(env);
94+
console.info('running with rustup using CWD "'+cargo_toml_path+'"');
95+
const env_2 = {...env, cwd: cargo_toml_path};
96+
console.info(env_2);
97+
console.info(cargo_toml_path);
98+
childProcessPromise = runRlsViaRustup(env_2);
9399
}
94100
try {
95101
const childProcess = await childProcessPromise;
@@ -134,7 +140,7 @@ export async function activate(context: ExtensionContext) {
134140
}
135141

136142
async function startLanguageClient(context: ExtensionContext) {
137-
if (workspace.rootPath === undefined) {
143+
if (workspace.rootPath === undefined || !workspace.workspaceFolders) {
138144
window.showWarningMessage('Startup error: the RLS can only operate on a folder, not a single file');
139145
return;
140146
}
@@ -154,6 +160,8 @@ async function startLanguageClient(context: ExtensionContext) {
154160
await autoUpdate();
155161
return makeRlsProcess();
156162
};
163+
const cargoSubdir = workspace.getConfiguration().get('rust-client.cargoSubdir');
164+
const workspaceFolder = workspace.workspaceFolders[0];
157165
const clientOptions: LanguageClientOptions = {
158166
// Register the server for Rust files
159167
documentSelector: [
@@ -164,6 +172,8 @@ async function startLanguageClient(context: ExtensionContext) {
164172
// Controls when to focus the channel rather than when to reveal it in the drop-down list
165173
revealOutputChannelOn: CONFIGURATION.revealOutputChannelOn,
166174
initializationOptions: { omitInitBuild: true },
175+
workspaceFolder: {...workspaceFolder,
176+
uri: Uri.parse('file://' + workspace.rootPath + (cargoSubdir ? '/'+cargoSubdir : ''))}
167177
};
168178

169179
// Create the language client and start the client.
@@ -186,10 +196,34 @@ export function deactivate(): Promise<void> {
186196
}
187197

188198
async function warnOnMissingCargoToml() {
189-
const files = await workspace.findFiles('Cargo.toml');
190-
191-
if (files.length < 1) {
192-
window.showWarningMessage('A Cargo.toml file must be at the root of the workspace in order to support all features');
199+
const setting_section = 'rust-client.cargoSubdir';
200+
const root_toml_files = await workspace.findFiles('Cargo.toml');
201+
202+
if (root_toml_files.length < 1) {
203+
const subdir_toml_files = await workspace.findFiles('**/Cargo.toml');
204+
const setting_value = workspace.getConfiguration().get(setting_section);
205+
if (subdir_toml_files.length < 1) {
206+
window.showWarningMessage(`The Cargo.toml file has not been found at
207+
the root of the workspace or anywhere else. In order to support all
208+
RLS features, some Cargo.toml must be present.`);
209+
210+
} else if (! setting_value) {
211+
const subdir = path.dirname(workspace.asRelativePath(subdir_toml_files[0]));
212+
const item_save_to_settings = `Save to '.vscode/settings.json'`;
213+
window.showWarningMessage(`A Cargo.toml has been found in the sudirectory '${subdir}/'.
214+
Although RLS expects Cargo.toml to be at the root of the workspace,
215+
we can set this path using the '${setting_section}' setting in your
216+
'.vscode/settings.json'`, ...[item_save_to_settings])
217+
.then(v => {
218+
switch (v) {
219+
case item_save_to_settings:
220+
workspace.getConfiguration().update(setting_section, subdir);
221+
break;
222+
default:
223+
break;
224+
}
225+
});
226+
}
193227
}
194228
}
195229

0 commit comments

Comments
 (0)