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

Commit 1fe6b45

Browse files
author
=
committed
switch exec -> execFile; ShellExecution -> ProcessExecution to easily handle whitespace in paths
1 parent fcb2d73 commit 1fe6b45

File tree

7 files changed

+114
-57
lines changed

7 files changed

+114
-57
lines changed

README.md

+30-37
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,18 @@
11
# Rust support for Visual Studio Code
22

33
[![](https://vsmarketplacebadge.apphb.com/version/rust-lang.rust.svg)](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust)
4-
[![Build Status](https://travis-ci.com/rust-lang/rls-vscode.svg?branch=master)](https://travis-ci.com/rust-lang/rls-vscode)
4+
[![Build Status](https://travis-ci.com/AndriesK/rls-vscode.svg?branch=master)](https://travis-ci.com/AndriesK/rls-vscode)
55

66
Adds language support for Rust to Visual Studio Code. Supports:
77

8-
* code completion
9-
* jump to definition, peek definition, find all references, symbol search
10-
* types and documentation on hover
11-
* code formatting
12-
* refactoring (rename, deglob)
13-
* error squiggles and apply suggestions from errors
14-
* snippets
15-
* build tasks
16-
8+
- code completion
9+
- jump to definition, peek definition, find all references, symbol search
10+
- types and documentation on hover
11+
- code formatting
12+
- refactoring (rename, deglob)
13+
- error squiggles and apply suggestions from errors
14+
- snippets
15+
- build tasks
1716

1817
Rust support is powered by the [Rust Language Server](https://github.com/rust-lang/rls)
1918
(RLS). If you don't have it installed, the extension will install it for you.
@@ -33,20 +32,18 @@ advice.
3332
Contributing code, tests, documentation, and bug reports is appreciated! For
3433
more details on building and debugging, etc., see [contributing.md](contributing.md).
3534

36-
3735
## Quick start
3836

39-
* Install [rustup](https://www.rustup.rs/) (Rust toolchain manager).
40-
* Install this extension from [the VSCode Marketplace](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust)
37+
- Install [rustup](https://www.rustup.rs/) (Rust toolchain manager).
38+
- Install this extension from [the VSCode Marketplace](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust)
4139
(or by entering `ext install rust-lang.rust` at the command palette <kbd>Ctrl</kbd>+<kbd>P</kbd>).
42-
* (Skip this step if you already have Rust projects that you'd like to work on.)
40+
- (Skip this step if you already have Rust projects that you'd like to work on.)
4341
Create a new Rust project by following [these instructions](https://doc.rust-lang.org/book/ch01-03-hello-cargo.html).
44-
* Open a Rust project (`File > Add Folder to Workspace...`). Open the folder for the whole
42+
- Open a Rust project (`File > Add Folder to Workspace...`). Open the folder for the whole
4543
project (i.e., the folder containing 'Cargo.toml'), not the 'src' folder.
46-
* You'll be prompted to install the RLS. Once installed, the RLS should start
44+
- You'll be prompted to install the RLS. Once installed, the RLS should start
4745
building your project.
4846

49-
5047
## Configuration
5148

5249
This extension provides options in VSCode's configuration settings. These
@@ -57,11 +54,11 @@ have Intellisense help.
5754

5855
Some highlights:
5956

60-
* `rust.show_warnings` - set to false to silence warnings in the editor.
61-
* `rust.all_targets` - build and index code for all targets (i.e., integration tests, examples, and benches)
62-
* `rust.cfg_test` - build and index test code (i.e., code with `#[cfg(test)]`/`#[test]`)
57+
- `rust.show_warnings` - set to false to silence warnings in the editor.
58+
- `rust.all_targets` - build and index code for all targets (i.e., integration tests, examples, and benches)
59+
- `rust.cfg_test` - build and index test code (i.e., code with `#[cfg(test)]`/`#[test]`)
6360

64-
* `rust-client.channel` - specifies from which toolchain the RLS should be spawned
61+
- `rust-client.channel` - specifies from which toolchain the RLS should be spawned
6562

6663
## Features
6764

@@ -72,16 +69,16 @@ includes snippet names as options when you type; select one by pressing 'enter'.
7269
You can move to the next 'hole' in the template by pressing 'tab'. We provide
7370
the following snippets:
7471

75-
* `for` - a for loop
76-
* `unimplemented`
77-
* `unreachable`
78-
* `print(ln)`
79-
* `assert(_eq)`
80-
* `macro_rules` - declare a macro
81-
* `if let Option` - an `if let` statement for executing code only in the `Some`
72+
- `for` - a for loop
73+
- `unimplemented`
74+
- `unreachable`
75+
- `print(ln)`
76+
- `assert(_eq)`
77+
- `macro_rules` - declare a macro
78+
- `if let Option` - an `if let` statement for executing code only in the `Some`
8279
case.
83-
* `spawn` - spawn a thread
84-
* `extern crate` - insert an `extern crate` statement
80+
- `spawn` - spawn a thread
81+
- `extern crate` - insert an `extern crate` statement
8582

8683
This extension is deliberately conservative about snippets and doesn't include
8784
too many. If you want more, check out
@@ -97,22 +94,19 @@ The plugin writes these into `tasks.json`. The plugin will not overwrite
9794
existing tasks, so you can customise these tasks. To refresh back to the
9895
defaults, delete `tasks.json` and restart VSCode.
9996

100-
10197
## Format on save
10298

10399
To enable formatting on save, you need to set the `editor.formatOnSave` setting
104100
to `true`. Find it under `File > Preferences > Settings`.
105101

106-
107102
## Requirements
108103

109-
* [Rustup](https://www.rustup.rs/),
110-
* A Rust toolchain (the extension will configure this for you, with
104+
- [Rustup](https://www.rustup.rs/),
105+
- A Rust toolchain (the extension will configure this for you, with
111106
permission),
112-
* `rls`, `rust-src`, and `rust-analysis` components (the
107+
- `rls`, `rust-src`, and `rust-analysis` components (the
113108
extension will install these for you, with permission).
114109

115-
116110
## Implementation
117111

118112
This extension almost exclusively uses the RLS for its feature support (syntax
@@ -121,4 +115,3 @@ the Rust compiler (`rustc`) to get data about Rust programs. It uses Cargo to
121115
manage building. Both Cargo and `rustc` are run in-process by the RLS. Formatting
122116
and code completion are provided by `rustfmt` and Racer, again both of these are
123117
run in-process by the RLS.
124-

src/configuration.ts

+9
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,10 @@ export class RLSConfiguration {
7979
return this.configuration.get('rust-client.rustupPath', 'rustup');
8080
}
8181

82+
public get rustupPathQuotes(): string {
83+
return '"' + this.rustupPath + '"';
84+
}
85+
8286
public get useWSL(): boolean {
8387
return this.configuration.get<boolean>('rust-client.useWSL', false);
8488
}
@@ -118,6 +122,10 @@ export class RLSConfiguration {
118122
return this.configuration.get<string>('rust-client.rlsPath');
119123
}
120124

125+
public get rlsPathQuotes(): string | undefined {
126+
return this.rlsPath ? '"' + this.rlsPath + '"' : undefined;
127+
}
128+
121129
public get multiProjectEnabled(): boolean {
122130
return this.configuration.get<boolean>(
123131
'rust-client.enableMultiProjectSetup',
@@ -130,6 +138,7 @@ export class RLSConfiguration {
130138
return {
131139
channel: ignoreChannel ? '' : this.channel,
132140
path: this.rustupPath,
141+
pathQuotes: this.rustupPathQuotes,
133142
useWSL: this.useWSL,
134143
};
135144
}

src/extension.ts

+6-4
Original file line numberDiff line numberDiff line change
@@ -362,8 +362,9 @@ class ClientWorkspace {
362362
const rustcPrintSysroot = () =>
363363
this.config.rustupDisabled
364364
? wslWrapper.exec('rustc --print sysroot', { env })
365-
: wslWrapper.exec(
366-
`${this.config.rustupPath} run ${this.config.channel} rustc --print sysroot`,
365+
: wslWrapper.execFile(
366+
this.config.rustupPath,
367+
['run', this.config.channel, 'rustc', '--print', 'sysroot'],
367368
{ env },
368369
);
369370

@@ -416,7 +417,7 @@ class ClientWorkspace {
416417

417418
private async makeRlsProcess(): Promise<child_process.ChildProcess> {
418419
// Run "rls" from the PATH unless there's an override.
419-
const rlsPath = this.config.rlsPath || 'rls';
420+
const rlsPath = this.config.rlsPathQuotes || 'rls';
420421

421422
// We don't need to set [DY]LD_LIBRARY_PATH if we're using rustup,
422423
// as rustup will set it for us when it chooses a toolchain.
@@ -451,8 +452,9 @@ class ClientWorkspace {
451452
}
452453

453454
const env = await makeRlsEnv();
455+
454456
childProcess = withWsl(config.useWSL).spawn(
455-
config.path,
457+
config.pathQuotes,
456458
['run', config.channel, rlsPath],
457459
{ env, cwd, shell: true },
458460
);

src/rustup.ts

+55-15
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,17 @@ function isInstalledRegex(componentName: string): RegExp {
1717
export interface RustupConfig {
1818
channel: string;
1919
path: string;
20+
pathQuotes: string;
2021
useWSL: boolean;
2122
}
2223

2324
export async function rustupUpdate(config: RustupConfig) {
2425
startSpinner('RLS', 'Updating…');
2526

2627
try {
27-
const { stdout } = await withWsl(config.useWSL).exec(
28-
`${config.path} update`,
29-
);
28+
const { stdout } = await withWsl(config.useWSL).execFile(config.path, [
29+
'update',
30+
]);
3031

3132
// This test is imperfect because if the user has multiple toolchains installed, they
3233
// might have one updated and one unchanged. But I don't want to go too far down the
@@ -65,13 +66,38 @@ export async function ensureToolchain(config: RustupConfig) {
6566
* Checks for required RLS components and prompts the user to install if it's
6667
* not already.
6768
*/
69+
6870
export async function checkForRls(config: RustupConfig) {
6971
if (await hasRlsComponents(config)) {
7072
return;
7173
}
7274

75+
// Format an easier to understand component install prompt
76+
const componentsNeededUserOutput = REQUIRED_COMPONENTS.map((component, i) => {
77+
if (
78+
REQUIRED_COMPONENTS.length > 1 &&
79+
i === REQUIRED_COMPONENTS.length - 1
80+
) {
81+
return ' and `' + component + '`';
82+
} else if (
83+
i !== 0 &&
84+
i !== REQUIRED_COMPONENTS.length - 1 &&
85+
REQUIRED_COMPONENTS.length > 2
86+
) {
87+
return ', `' + component + '`';
88+
} else {
89+
return '`' + component + '`';
90+
}
91+
});
7392
const clicked = await Promise.resolve(
74-
window.showInformationMessage('RLS not installed. Install?', 'Yes'),
93+
window.showInformationMessage(
94+
`
95+
Rustup
96+
${
97+
componentsNeededUserOutput.length > 1 ? 'components' : 'component'
98+
} ${componentsNeededUserOutput.join('')} not installed. Install?`,
99+
'Yes',
100+
),
75101
);
76102
if (clicked) {
77103
await installRlsComponents(config);
@@ -83,9 +109,10 @@ export async function checkForRls(config: RustupConfig) {
83109

84110
async function hasToolchain(config: RustupConfig): Promise<boolean> {
85111
try {
86-
const { stdout } = await withWsl(config.useWSL).exec(
87-
`${config.path} toolchain list`,
88-
);
112+
const { stdout } = await withWsl(config.useWSL).execFile(config.path, [
113+
'toolchain',
114+
'list',
115+
]);
89116
return stdout.includes(config.channel);
90117
} catch (e) {
91118
console.log(e);
@@ -129,7 +156,7 @@ async function tryToInstallToolchain(config: RustupConfig) {
129156
*/
130157
async function listComponents(config: RustupConfig): Promise<string[]> {
131158
return withWsl(config.useWSL)
132-
.exec(`${config.path} component list --toolchain ${config.channel}`)
159+
.execFile(config.path, ['component', 'list', '--toolchain', config.channel])
133160
.then(({ stdout }) =>
134161
stdout
135162
.toString()
@@ -142,9 +169,20 @@ async function hasRlsComponents(config: RustupConfig): Promise<boolean> {
142169
try {
143170
const components = await listComponents(config);
144171

145-
return REQUIRED_COMPONENTS.map(isInstalledRegex).every(isInstalledRegex =>
146-
components.some(c => isInstalledRegex.test(c)),
147-
);
172+
// Splice the components that are installed from the REQUIRED_COMPONENTS array
173+
for (let i = REQUIRED_COMPONENTS.length; i >= 0; --i) {
174+
const installedRegex = isInstalledRegex(REQUIRED_COMPONENTS[i]);
175+
components.forEach(component => {
176+
if (installedRegex.test(component)) {
177+
REQUIRED_COMPONENTS.splice(i, 1);
178+
}
179+
});
180+
}
181+
if (REQUIRED_COMPONENTS.length === 0) {
182+
return true;
183+
} else {
184+
return false;
185+
}
148186
} catch (e) {
149187
console.log(e);
150188
window.showErrorMessage(`Can't detect RLS components: ${e.message}`);
@@ -153,7 +191,7 @@ async function hasRlsComponents(config: RustupConfig): Promise<boolean> {
153191
}
154192
}
155193

156-
async function installRlsComponents(config: RustupConfig) {
194+
export async function installRlsComponents(config: RustupConfig) {
157195
startSpinner('RLS', 'Installing components…');
158196

159197
for (const component of REQUIRED_COMPONENTS) {
@@ -226,7 +264,9 @@ export function parseActiveToolchain(rustupOutput: string): string {
226264
export async function getVersion(config: RustupConfig): Promise<string> {
227265
const versionRegex = /rustup ([0-9]+\.[0-9]+\.[0-9]+)/;
228266

229-
const output = await withWsl(config.useWSL).exec(`${config.path} --version`);
267+
const output = await withWsl(config.useWSL).execFile(config.path, [
268+
'--version',
269+
]);
230270
const versionMatch = output.stdout.toString().match(versionRegex);
231271
if (versionMatch && versionMatch.length >= 2) {
232272
return versionMatch[1];
@@ -257,7 +297,7 @@ export function getActiveChannel(wsPath: string, config: RustupConfig): string {
257297
try {
258298
// `rustup show active-toolchain` is available since rustup 1.12.0
259299
activeChannel = withWsl(config.useWSL)
260-
.execSync(`${config.path} show active-toolchain`, { cwd: wsPath })
300+
.execSync(`${config.pathQuotes} show active-toolchain`, { cwd: wsPath })
261301
.toString()
262302
.trim();
263303
// Since rustup 1.17.0 if the active toolchain is the default, we're told
@@ -268,7 +308,7 @@ export function getActiveChannel(wsPath: string, config: RustupConfig): string {
268308
} catch (e) {
269309
// Possibly an old rustup version, so try rustup show
270310
const showOutput = withWsl(config.useWSL)
271-
.execSync(`${config.path} show`, { cwd: wsPath })
311+
.execSync(`${config.pathQuotes} show`, { cwd: wsPath })
272312
.toString();
273313
activeChannel = parseActiveToolchain(showOutput);
274314
}

src/tasks.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import * as crypto from 'crypto';
22
import {
33
Disposable,
4+
ProcessExecution,
45
ShellExecution,
56
Task,
67
TaskGroup,
@@ -127,14 +128,17 @@ export async function runTaskCommand(
127128
displayName: string,
128129
folder?: WorkspaceFolder,
129130
) {
131+
if (!command) {
132+
return;
133+
}
130134
const uniqueId = crypto.randomBytes(20).toString();
131135

132136
const task = new Task(
133137
{ label: uniqueId, type: 'shell' },
134138
folder ? folder : workspace.workspaceFolders![0],
135139
displayName,
136140
TASK_SOURCE,
137-
new ShellExecution(`${command} ${args.join(' ')}`, {
141+
new ProcessExecution(command, args, {
138142
cwd: cwd || (folder && folder.uri.fsPath),
139143
env,
140144
}),

src/utils/child_process.ts

+4
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@ import * as util from 'util';
44
import { modifyParametersForWSL } from './wslpath';
55

66
const execAsync = util.promisify(child_process.exec);
7+
const execFile = util.promisify(child_process.execFile);
78

89
export interface SpawnFunctions {
910
exec: typeof execAsync;
11+
execFile: typeof execFile;
1012
execSync: typeof child_process.execSync;
1113
spawn: typeof child_process.spawn;
1214
modifyArgs: (
@@ -19,12 +21,14 @@ export function withWsl(withWsl: boolean): SpawnFunctions {
1921
return withWsl
2022
? {
2123
exec: withWslModifiedParameters(execAsync),
24+
execFile: withWslModifiedParameters(execFile),
2225
execSync: withWslModifiedParameters(child_process.execSync),
2326
spawn: withWslModifiedParameters(child_process.spawn),
2427
modifyArgs: modifyParametersForWSL,
2528
}
2629
: {
2730
exec: execAsync,
31+
execFile,
2832
execSync: child_process.execSync,
2933
spawn: child_process.spawn,
3034
modifyArgs: (command: string, args: string[]) => ({ command, args }),

0 commit comments

Comments
 (0)