Skip to content
This repository has been archived by the owner on Dec 8, 2020. It is now read-only.

Allow developers on Windows specify what kind of integrated terminal they use #315

Merged
merged 4 commits into from
Jul 19, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion doc/cargo_command_execution.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ These commands are available through the command palette (<kbd>Ctrl</kbd>+<kbd>S

## Execute Command On Save

The extension supports executing some of these commands after saving the active document.
The extension supports executing some of these commands after saving the active document.

The `"rust.actionOnSave"` configuration parameter specifies which command to execute.

Expand Down Expand Up @@ -87,6 +87,27 @@ The possible values are:
"rust.cargoEnv": { "RUST_BACKTRACE": 1 }
```

### Executing Cargo commands in an integrated terminal

The `"rust.executeCargoCommandInTerminal"` configuration parameter controls whether a Cargo command should be executed in an integrated terminal.

By default, the extension executes Cargo commands as child processes. It then parses the output of the command and publishes diagnostics. Executing Cargo commands in an integrated terminal is useful if you need to run a binary and enter some text.

Unfortunately, there is currently no way to parse output of an integrated terminal. This means diagnostics cannot be shown in the editor.

The configuration parameter supports the following values:

* `true` - A Cargo command should be executed in an integrated terminal.
* `false` - A Cargo command should be executed as a child process.

### Specifying what kind of integrated terminal is used

The `"rust.shell.kind.windows"` configuration parameter specifies what kind of integrated terminal is used.

The configuration parameter should be specified only if the user uses Windows with [WSL](https://msdn.microsoft.com/en-us/commandline/wsl/install_guide).

In all other cases the extension should be able to determine it itself.

### Setting An Action To Handle Starting A New Command If There Is Another Command Running

The `"rust.actionOnStartingCommandIfThereIsRunningCommand"` configuration parameter specifies what the extension should do in case of starting a new command if there is a previous command running.
Expand Down
13 changes: 0 additions & 13 deletions doc/legacy_mode/main.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,16 +47,3 @@ The output channel will not be shown under any of the following conditions:

- `"rust.executeCargoCommandInTerminal"` is set to `true`
- `"rust.actionOnSave"` is set to `"check"`

### Executing Cargo commands in an integrated terminal

The `"rust.executeCargoCommandInTerminal"` configuration parameter controls whether [a Cargo command should be executed](../cargo_command_execution.md) in an integrated terminal.

By default, the extension executes Cargo commands as child processes. It then parses the output of the command and publishes diagnostics. Executing Cargo commands in an integrated terminal is useful if you need to run a binary and enter some text.

Unfortunately, there is currently no way to parse output of an integrated terminal. This means diagnostics cannot be shown in the editor.

The configuration parameter supports the following values:

* `true` - A Cargo command should be executed in an integrated terminal.
* `false` - A Cargo command should be executed as a child process.
14 changes: 14 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,20 @@
]
}
}
},
"rust.shell.kind.windows": {
"default": "null",
"enum": [
"cmd",
"powershell",
"shell",
"wsl",
"null"
],
"type": [
"string",
"null"
]
}
}
}
Expand Down
51 changes: 21 additions & 30 deletions src/CommandLine.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,5 @@
export enum Shell {
PowerShell,
CMD,
Shell
}

/**
* Parses the specified string as a variant of the enum `Shell`.
* If it fails to match the string, it returns `Shell.Shell`
* @param shell The shell textual representation
* @return The shell matching the specified string
*/
export function parseShell(shell: string): Shell {
if (shell.includes('powershell')) {
return Shell.PowerShell;
}
if (shell.includes('cmd')) {
return Shell.CMD;
}
return Shell.Shell;
}
import { Shell } from './Shell';
import { correctPath } from './WslShellUtils';

/**
* Creates a command to set the environment variable
Expand All @@ -32,9 +13,10 @@ export function getCommandToSetEnvVar(shell: Shell, varName: string, varValue: s
switch (shell) {
case Shell.PowerShell:
return `$ENV:${varName}="${varValue}"`;
case Shell.CMD:
case Shell.Cmd:
return `set ${varName}=${varValue}`;
case Shell.Shell:
case Shell.Wsl:
return ` export ${varName}=${varValue}`;
}
}
Expand All @@ -55,7 +37,7 @@ export function escapeSpaces(s: string, shell: Shell): string {
s = s.replace(new RegExp('` ', 'g'), ' ');
// Escape
return s.replace(new RegExp(' ', 'g'), '` ');
case Shell.CMD:
case Shell.Cmd:
s = s.concat();
if (!s.startsWith('"')) {
s = '"'.concat(s);
Expand All @@ -65,6 +47,7 @@ export function escapeSpaces(s: string, shell: Shell): string {
}
return s;
case Shell.Shell:
case Shell.Wsl:
s = s.concat();
if (!s.startsWith('\'')) {
s = '\''.concat(s);
Expand All @@ -76,6 +59,16 @@ export function escapeSpaces(s: string, shell: Shell): string {
}
}

export function getCommandToChangeWorkingDirectory(
shell: Shell,
workingDirectory: string
): string {
if (shell === Shell.Wsl) {
workingDirectory = correctPath(workingDirectory);
}
return getCommandForArgs(shell, ['cd', workingDirectory]);
}

/**
* Prepares the specified arguments to be passed to the specified shell and constructs the command
* from the arguments
Expand All @@ -95,28 +88,26 @@ export function getCommandForArgs(shell: Shell, args: string[]): string {
* @return A created command which if it is passed to a terminal,
* it will execute the statements
*/
export function getCommandToExecuteStatementsOneByOneIfPreviousIsSucceed(shell: Shell, statements: string[]): string {
export function getCommandToExecuteStatementsOneByOneIfPreviousIsSucceed(
shell: Shell,
statements: string[]
): string {
if (statements.length === 0) {
return '';
}

if (process.platform === 'win32' && shell === Shell.PowerShell) {
if (shell === Shell.PowerShell) {
let command = statements[0];

for (let i = 1; i < statements.length; ++i) {
command += `; if ($?) { ${statements[i]}; }`;
}

return command;
} else {
// The string starts with space to make sh not save the command.
// This code is also executed for cmd on Windows, but leading space doesn't break anything
let command = ' ' + statements[0];

for (let i = 1; i < statements.length; ++i) {
command += ` && ${statements[i]}`;
}

return command;
}
}
25 changes: 25 additions & 0 deletions src/ConfigurationParameter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { WorkspaceConfiguration, workspace } from 'vscode';

/**
* The main goal of the class is to store the parameter's name and to expose functions to get/set
* the value of the parameter
*/
export class ConfigurationParameter {
private _parameterName: string;

public constructor(parameterName: string) {
this._parameterName = parameterName;
}

public getValue(): any {
return this.getConfiguration().get(this._parameterName);
}

public async setValue(value: any): Promise<void> {
await this.getConfiguration().update(this._parameterName, value, true);
}

private getConfiguration(): WorkspaceConfiguration {
return workspace.getConfiguration('');
}
}
8 changes: 8 additions & 0 deletions src/IShellProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { Shell } from './Shell';

/**
* The interface declares methods which should be implemented for any shell providers
*/
export interface IShellProvider {
getValue(): Promise<Shell | undefined>;
}
12 changes: 12 additions & 0 deletions src/NotWindowsShellProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { IShellProvider } from './IShellProvider';
import { Shell } from './Shell';

/**
* The main goal of the class is to provide the current value of the shell
*/
export class NotWindowsShellProvider implements IShellProvider {
public getValue(): Promise<Shell | undefined> {
// All OS expect Windows use Shell
return Promise.resolve(Shell.Shell);
}
}
51 changes: 51 additions & 0 deletions src/Shell.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/**
* A enumeration of possible shells
*/
export enum Shell {
PowerShell,
Cmd,
Shell,
Wsl
}

/**
* The list of all shell values
*/
export const VALUES = [Shell.PowerShell, Shell.Cmd, Shell.Shell, Shell.Wsl];

/**
* The list of textual forms of all shell values
*/
export const VALUE_STRINGS = VALUES.map(toString);

export function fromString(s: string): Shell | undefined {
switch (s) {
case 'powershell':
return Shell.PowerShell;
case 'cmd':
return Shell.Cmd;
case 'shell':
return Shell.Shell;
case 'wsl':
return Shell.Wsl;
default:
return undefined;
}
}

/**
* Returns the textual form of the specified shell
* @param shell The shell to convert to string
*/
export function toString(shell: Shell): string {
switch (shell) {
case Shell.PowerShell:
return 'powershell';
case Shell.Cmd:
return 'cmd';
case Shell.Shell:
return 'shell';
case Shell.Wsl:
return 'wsl';
}
}
32 changes: 32 additions & 0 deletions src/ShellProviderManager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { ILogger } from './components/logging/ILogger';
import { IShellProvider } from './IShellProvider';
import { Shell } from './Shell';
import { NotWindowsShellProvider } from './NotWindowsShellProvider';
import { WindowsShellProvider } from './WindowsShellProvider';

/**
* The main goal of the class is to provide the current value of the shell
*/
export class ShellProviderManager {
private _shellProvider: IShellProvider;

/**
* Creates a new object which can be used to get the current value of the shell
* @param logger The logger which is used to create child logger which will be used to log
* messages
*/
public constructor(logger: ILogger) {
if (process.platform === 'win32') {
this._shellProvider = new WindowsShellProvider(logger);
} else {
this._shellProvider = new NotWindowsShellProvider();
}
}

/**
* Gets the current value of the shell and returns it
*/
public async getValue(): Promise<Shell | undefined> {
return await this._shellProvider.getValue();
}
}
Loading