Skip to content

Commit

Permalink
May point release with fixes for shell detection (#5925)
Browse files Browse the repository at this point in the history
* Fixes
* Rename news file
* New point release
  • Loading branch information
DonJayamanne authored Jun 5, 2019
1 parent 9b0e94c commit d47a991
Show file tree
Hide file tree
Showing 12 changed files with 693 additions and 205 deletions.
10 changes: 9 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
# Changelog

## 2019.5.2 (4 June 2019)
## 2019.5.3 (5 June 2019)

### Fixes

1. Fixes to detection of the shell.
([#5916](https://github.com/microsoft/vscode-python/issues/5916))


## 2019.5.18426 (4 June 2019)

### Fixes

Expand Down
234 changes: 189 additions & 45 deletions package-lock.json

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "python",
"displayName": "Python",
"description": "Linting, Debugging (multi-threaded, remote), Intellisense, code formatting, refactoring, unit tests, snippets, and more.",
"version": "2019.5.2",
"version": "2019.5.3",
"languageServerVersion": "0.2.82",
"publisher": "ms-python",
"author": {
Expand Down Expand Up @@ -2252,7 +2252,7 @@
"@types/request": "^2.47.0",
"@types/semver": "^5.5.0",
"@types/shortid": "^0.0.29",
"@types/sinon": "^4.3.0",
"@types/sinon": "^4.3.3",
"@types/stack-trace": "0.0.29",
"@types/strip-json-comments": "0.0.30",
"@types/temp": "^0.8.32",
Expand Down Expand Up @@ -2326,6 +2326,7 @@
"rewiremock": "^3.13.0",
"sass-loader": "^7.1.0",
"shortid": "^2.2.8",
"sinon": "^7.3.2",
"style-loader": "^0.23.1",
"styled-jsx": "^3.1.0",
"svg-inline-loader": "^0.8.0",
Expand Down
3 changes: 3 additions & 0 deletions src/client/common/platform/platformService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ export class PlatformService implements IPlatformService {
public get isLinux(): boolean {
return this.osType === OSType.Linux;
}
public get osRelease(): string {
return os.release();
}
public get is64bit(): boolean {
// tslint:disable-next-line:no-require-imports
const arch = require('arch');
Expand Down
1 change: 1 addition & 0 deletions src/client/common/platform/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export interface IRegistry {
export const IPlatformService = Symbol('IPlatformService');
export interface IPlatformService {
readonly osType: OSType;
osRelease: string;
readonly pathVariableName: 'Path' | 'PATH';
readonly virtualEnvBinName: 'bin' | 'Scripts';

Expand Down
114 changes: 12 additions & 102 deletions src/client/common/terminal/helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,44 +2,22 @@
// Licensed under the MIT License.

import { inject, injectable, named } from 'inversify';
import * as os from 'os';
import { Terminal, Uri } from 'vscode';
import { ICondaService, IInterpreterService, InterpreterType, PythonInterpreter } from '../../interpreter/contracts';
import { sendTelemetryEvent } from '../../telemetry';
import { EventName } from '../../telemetry/constants';
import { ITerminalManager } from '../application/types';
import { ITerminalManager, IWorkspaceService } from '../application/types';
import '../extensions';
import { traceDecorators, traceError } from '../logger';
import { IPlatformService } from '../platform/types';
import { IConfigurationService, ICurrentProcess, Resource } from '../types';
import { OSType } from '../utils/platform';
import { ShellDetector } from './shellDetector';
import { ITerminalActivationCommandProvider, ITerminalHelper, TerminalActivationProviders, TerminalShellType } from './types';

// Types of shells can be found here:
// 1. https://wiki.ubuntu.com/ChangingShells
const IS_GITBASH = /(gitbash.exe$)/i;
const IS_BASH = /(bash.exe$|bash$)/i;
const IS_WSL = /(wsl.exe$)/i;
const IS_ZSH = /(zsh$)/i;
const IS_KSH = /(ksh$)/i;
const IS_COMMAND = /(cmd.exe$|cmd$)/i;
const IS_POWERSHELL = /(powershell.exe$|powershell$)/i;
const IS_POWERSHELL_CORE = /(pwsh.exe$|pwsh$)/i;
const IS_FISH = /(fish$)/i;
const IS_CSHELL = /(csh$)/i;
const IS_TCSHELL = /(tcsh$)/i;
const IS_XONSH = /(xonsh$)/i;

const defaultOSShells = {
[OSType.Linux]: TerminalShellType.bash,
[OSType.OSX]: TerminalShellType.bash,
[OSType.Windows]: TerminalShellType.commandPrompt,
[OSType.Unknown]: undefined
};

@injectable()
export class TerminalHelper implements ITerminalHelper {
private readonly detectableShells: Map<TerminalShellType, RegExp>;
private readonly shellDetector: ShellDetector;
constructor(@inject(IPlatformService) private readonly platform: IPlatformService,
@inject(ITerminalManager) private readonly terminalManager: ITerminalManager,
@inject(ICondaService) private readonly condaService: ICondaService,
Expand All @@ -50,61 +28,17 @@ export class TerminalHelper implements ITerminalHelper {
@inject(ITerminalActivationCommandProvider) @named(TerminalActivationProviders.commandPromptAndPowerShell) private readonly commandPromptAndPowerShell: ITerminalActivationCommandProvider,
@inject(ITerminalActivationCommandProvider) @named(TerminalActivationProviders.pyenv) private readonly pyenv: ITerminalActivationCommandProvider,
@inject(ITerminalActivationCommandProvider) @named(TerminalActivationProviders.pipenv) private readonly pipenv: ITerminalActivationCommandProvider,
@inject(IConfigurationService) private readonly currentProcess: ICurrentProcess
@inject(ICurrentProcess) private readonly currentProcess: ICurrentProcess,
@inject(IWorkspaceService) private readonly workspace: IWorkspaceService
) {
this.detectableShells = new Map<TerminalShellType, RegExp>();
this.detectableShells.set(TerminalShellType.powershell, IS_POWERSHELL);
this.detectableShells.set(TerminalShellType.gitbash, IS_GITBASH);
this.detectableShells.set(TerminalShellType.bash, IS_BASH);
this.detectableShells.set(TerminalShellType.wsl, IS_WSL);
this.detectableShells.set(TerminalShellType.zsh, IS_ZSH);
this.detectableShells.set(TerminalShellType.ksh, IS_KSH);
this.detectableShells.set(TerminalShellType.commandPrompt, IS_COMMAND);
this.detectableShells.set(TerminalShellType.fish, IS_FISH);
this.detectableShells.set(TerminalShellType.tcshell, IS_TCSHELL);
this.detectableShells.set(TerminalShellType.cshell, IS_CSHELL);
this.detectableShells.set(TerminalShellType.powershellCore, IS_POWERSHELL_CORE);
this.detectableShells.set(TerminalShellType.xonsh, IS_XONSH);
this.shellDetector = new ShellDetector(this.platform, this.currentProcess, this.workspace);

}
public createTerminal(title?: string): Terminal {
return this.terminalManager.createTerminal({ name: title });
}
public identifyTerminalShell(terminal?: Terminal): TerminalShellType {
let shell = TerminalShellType.other;
let usingDefaultShell = false;
const terminalProvided = !!terminal;
// Determine shell based on the name of the terminal.
// See solution here https://github.com/microsoft/vscode/issues/74233#issuecomment-497527337
if (terminal) {
shell = this.identifyTerminalShellByName(terminal.name);
}

// If still unable to identify, then use fall back to determine path to the default shell.
if (shell === TerminalShellType.other) {
const shellPath = getDefaultShell(this.platform.osType, this.currentProcess);
shell = Array.from(this.detectableShells.keys())
.reduce((matchedShell, shellToDetect) => {
if (matchedShell === TerminalShellType.other && this.detectableShells.get(shellToDetect)!.test(shellPath)) {
return shellToDetect;
}
return matchedShell;
}, TerminalShellType.other);

// We have restored to using the default shell.
usingDefaultShell = shell !== TerminalShellType.other;
}
const properties = { failed: shell === TerminalShellType.other, usingDefaultShell, terminalProvided };
sendTelemetryEvent(EventName.TERMINAL_SHELL_IDENTIFICATION, undefined, properties);
return shell;
}
public identifyTerminalShellByName(name: string): TerminalShellType {
return Array.from(this.detectableShells.keys())
.reduce((matchedShell, shellToDetect) => {
if (matchedShell === TerminalShellType.other && this.detectableShells.get(shellToDetect)!.test(name)) {
return shellToDetect;
}
return matchedShell;
}, TerminalShellType.other);
return this.shellDetector.identifyTerminalShell(terminal);
}

public buildCommandForTerminal(terminalShellType: TerminalShellType, command: string, args: string[]) {
Expand All @@ -119,7 +53,10 @@ export class TerminalHelper implements ITerminalHelper {
return promise;
}
public async getEnvironmentActivationShellCommands(resource: Resource, interpreter?: PythonInterpreter): Promise<string[] | undefined> {
const shell = defaultOSShells[this.platform.osType];
if (this.platform.osType === OSType.Unknown){
return;
}
const shell = this.shellDetector.identifyTerminalShell();
if (!shell) {
return;
}
Expand Down Expand Up @@ -181,30 +118,3 @@ export class TerminalHelper implements ITerminalHelper {
}
}
}

/*
The following code is based on VS Code from https://github.com/microsoft/vscode/blob/5c65d9bfa4c56538150d7f3066318e0db2c6151f/src/vs/workbench/contrib/terminal/node/terminal.ts#L12-L55
This is only a fall back to identify the default shell used by VSC.
On Windows, determine the default shell.
On others, default to bash.
*/
function getDefaultShell(osType: OSType, currentProcess: ICurrentProcess): string {
if (osType === OSType.Windows) {
return getTerminalDefaultShellWindows(osType, currentProcess);
}
return '/bin/bash';
}
let _TERMINAL_DEFAULT_SHELL_WINDOWS: string | null = null;
function getTerminalDefaultShellWindows(osType: OSType, currentProcess: ICurrentProcess): string {
if (!_TERMINAL_DEFAULT_SHELL_WINDOWS) {
const isAtLeastWindows10 = osType === OSType.Windows && parseFloat(os.release()) >= 10;
const is32ProcessOn64Windows = process.env.hasOwnProperty('PROCESSOR_ARCHITEW6432');
const powerShellPath = `${process.env.windir}\\${is32ProcessOn64Windows ? 'Sysnative' : 'System32'}\\WindowsPowerShell\\v1.0\\powershell.exe`;
_TERMINAL_DEFAULT_SHELL_WINDOWS = isAtLeastWindows10 ? powerShellPath : getWindowsShell(currentProcess);
}
return _TERMINAL_DEFAULT_SHELL_WINDOWS;
}

function getWindowsShell(currentProcess: ICurrentProcess): string {
return currentProcess.env.comspec || 'cmd.exe';
}
Loading

0 comments on commit d47a991

Please sign in to comment.