Skip to content

Commit 9b0e94c

Browse files
authored
Point release with changes to identification of shell (#5898)
1 parent 4b2d7ae commit 9b0e94c

17 files changed

+108
-106
lines changed

.vscode/launch.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,4 +210,4 @@
210210
]
211211
}
212212
]
213-
}
213+
}

CHANGELOG.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
# Changelog
22

3-
## 2019.5.1 (30 May 2019)
3+
## 2019.5.2 (4 June 2019)
4+
5+
### Fixes
6+
7+
1. Changes to identificaction of `shell` for the activation of environments in the terminal.
8+
([#5743](https://github.com/microsoft/vscode-python/issues/5743))
9+
10+
11+
## 2019.5.17517 (30 May 2019)
412

513
### Fixes
614

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "python",
33
"displayName": "Python",
44
"description": "Linting, Debugging (multi-threaded, remote), Intellisense, code formatting, refactoring, unit tests, snippets, and more.",
5-
"version": "2019.5.1",
5+
"version": "2019.5.2",
66
"languageServerVersion": "0.2.82",
77
"publisher": "ms-python",
88
"author": {

src/client/common/terminal/activator/base.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,7 @@ export class BaseTerminalActivator implements ITerminalActivator {
1616
}
1717
const deferred = createDeferred<boolean>();
1818
this.activatedTerminals.set(terminal, deferred.promise);
19-
const shellPath = this.helper.getTerminalShellPath();
20-
const terminalShellType = !shellPath || shellPath.length === 0 ? TerminalShellType.other : this.helper.identifyTerminalShell(shellPath);
19+
const terminalShellType = this.helper.identifyTerminalShell(terminal);
2120

2221
const activationCommamnds = await this.helper.getEnvironmentActivationCommands(terminalShellType, resource);
2322
let activated = false;

src/client/common/terminal/activator/powershellFailedHandler.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,11 @@ export class PowershellTerminalActivationFailedHandler implements ITerminalActiv
1717
@inject(IPlatformService) private readonly platformService: IPlatformService,
1818
@inject(IDiagnosticsService) @named(PowerShellActivationHackDiagnosticsServiceId) private readonly diagnosticService: IDiagnosticsService) {
1919
}
20-
public async handleActivation(_terminal: Terminal, resource: Resource, _preserveFocus: boolean, activated: boolean) {
20+
public async handleActivation(terminal: Terminal, resource: Resource, _preserveFocus: boolean, activated: boolean) {
2121
if (activated || !this.platformService.isWindows) {
2222
return;
2323
}
24-
const shell = this.helper.identifyTerminalShell(this.helper.getTerminalShellPath());
24+
const shell = this.helper.identifyTerminalShell(terminal);
2525
if (shell !== TerminalShellType.powershell && shell !== TerminalShellType.powershellCore) {
2626
return;
2727
}

src/client/common/terminal/helper.ts

Lines changed: 64 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,16 @@
22
// Licensed under the MIT License.
33

44
import { inject, injectable, named } from 'inversify';
5+
import * as os from 'os';
56
import { Terminal, Uri } from 'vscode';
67
import { ICondaService, IInterpreterService, InterpreterType, PythonInterpreter } from '../../interpreter/contracts';
78
import { sendTelemetryEvent } from '../../telemetry';
89
import { EventName } from '../../telemetry/constants';
9-
import { ITerminalManager, IWorkspaceService } from '../application/types';
10+
import { ITerminalManager } from '../application/types';
1011
import '../extensions';
1112
import { traceDecorators, traceError } from '../logger';
1213
import { IPlatformService } from '../platform/types';
13-
import { IConfigurationService, Resource } from '../types';
14+
import { IConfigurationService, ICurrentProcess, Resource } from '../types';
1415
import { OSType } from '../utils/platform';
1516
import { ITerminalActivationCommandProvider, ITerminalHelper, TerminalActivationProviders, TerminalShellType } from './types';
1617

@@ -21,7 +22,7 @@ const IS_BASH = /(bash.exe$|bash$)/i;
2122
const IS_WSL = /(wsl.exe$)/i;
2223
const IS_ZSH = /(zsh$)/i;
2324
const IS_KSH = /(ksh$)/i;
24-
const IS_COMMAND = /cmd.exe$/i;
25+
const IS_COMMAND = /(cmd.exe$|cmd$)/i;
2526
const IS_POWERSHELL = /(powershell.exe$|powershell$)/i;
2627
const IS_POWERSHELL_CORE = /(pwsh.exe$|pwsh$)/i;
2728
const IS_FISH = /(fish$)/i;
@@ -41,15 +42,15 @@ export class TerminalHelper implements ITerminalHelper {
4142
private readonly detectableShells: Map<TerminalShellType, RegExp>;
4243
constructor(@inject(IPlatformService) private readonly platform: IPlatformService,
4344
@inject(ITerminalManager) private readonly terminalManager: ITerminalManager,
44-
@inject(IWorkspaceService) private readonly workspace: IWorkspaceService,
4545
@inject(ICondaService) private readonly condaService: ICondaService,
4646
@inject(IInterpreterService) private readonly interpreterService: IInterpreterService,
4747
@inject(IConfigurationService) private readonly configurationService: IConfigurationService,
4848
@inject(ITerminalActivationCommandProvider) @named(TerminalActivationProviders.conda) private readonly conda: ITerminalActivationCommandProvider,
4949
@inject(ITerminalActivationCommandProvider) @named(TerminalActivationProviders.bashCShellFish) private readonly bashCShellFish: ITerminalActivationCommandProvider,
5050
@inject(ITerminalActivationCommandProvider) @named(TerminalActivationProviders.commandPromptAndPowerShell) private readonly commandPromptAndPowerShell: ITerminalActivationCommandProvider,
5151
@inject(ITerminalActivationCommandProvider) @named(TerminalActivationProviders.pyenv) private readonly pyenv: ITerminalActivationCommandProvider,
52-
@inject(ITerminalActivationCommandProvider) @named(TerminalActivationProviders.pipenv) private readonly pipenv: ITerminalActivationCommandProvider
52+
@inject(ITerminalActivationCommandProvider) @named(TerminalActivationProviders.pipenv) private readonly pipenv: ITerminalActivationCommandProvider,
53+
@inject(IConfigurationService) private readonly currentProcess: ICurrentProcess
5354
) {
5455
this.detectableShells = new Map<TerminalShellType, RegExp>();
5556
this.detectableShells.set(TerminalShellType.powershell, IS_POWERSHELL);
@@ -68,37 +69,44 @@ export class TerminalHelper implements ITerminalHelper {
6869
public createTerminal(title?: string): Terminal {
6970
return this.terminalManager.createTerminal({ name: title });
7071
}
71-
public identifyTerminalShell(shellPath: string): TerminalShellType {
72+
public identifyTerminalShell(terminal?: Terminal): TerminalShellType {
73+
let shell = TerminalShellType.other;
74+
let usingDefaultShell = false;
75+
const terminalProvided = !!terminal;
76+
// Determine shell based on the name of the terminal.
77+
// See solution here https://github.com/microsoft/vscode/issues/74233#issuecomment-497527337
78+
if (terminal) {
79+
shell = this.identifyTerminalShellByName(terminal.name);
80+
}
81+
82+
// If still unable to identify, then use fall back to determine path to the default shell.
83+
if (shell === TerminalShellType.other) {
84+
const shellPath = getDefaultShell(this.platform.osType, this.currentProcess);
85+
shell = Array.from(this.detectableShells.keys())
86+
.reduce((matchedShell, shellToDetect) => {
87+
if (matchedShell === TerminalShellType.other && this.detectableShells.get(shellToDetect)!.test(shellPath)) {
88+
return shellToDetect;
89+
}
90+
return matchedShell;
91+
}, TerminalShellType.other);
92+
93+
// We have restored to using the default shell.
94+
usingDefaultShell = shell !== TerminalShellType.other;
95+
}
96+
const properties = { failed: shell === TerminalShellType.other, usingDefaultShell, terminalProvided };
97+
sendTelemetryEvent(EventName.TERMINAL_SHELL_IDENTIFICATION, undefined, properties);
98+
return shell;
99+
}
100+
public identifyTerminalShellByName(name: string): TerminalShellType {
72101
return Array.from(this.detectableShells.keys())
73102
.reduce((matchedShell, shellToDetect) => {
74-
if (matchedShell === TerminalShellType.other && this.detectableShells.get(shellToDetect)!.test(shellPath)) {
103+
if (matchedShell === TerminalShellType.other && this.detectableShells.get(shellToDetect)!.test(name)) {
75104
return shellToDetect;
76105
}
77106
return matchedShell;
78107
}, TerminalShellType.other);
79108
}
80-
public getTerminalShellPath(): string {
81-
const shellConfig = this.workspace.getConfiguration('terminal.integrated.shell');
82-
let osSection = '';
83-
switch (this.platform.osType) {
84-
case OSType.Windows: {
85-
osSection = 'windows';
86-
break;
87-
}
88-
case OSType.OSX: {
89-
osSection = 'osx';
90-
break;
91-
}
92-
case OSType.Linux: {
93-
osSection = 'linux';
94-
break;
95-
}
96-
default: {
97-
return '';
98-
}
99-
}
100-
return shellConfig.get<string>(osSection)!;
101-
}
109+
102110
public buildCommandForTerminal(terminalShellType: TerminalShellType, command: string, args: string[]) {
103111
const isPowershell = terminalShellType === TerminalShellType.powershell || terminalShellType === TerminalShellType.powershellCore;
104112
const commandPrefix = isPowershell ? '& ' : '';
@@ -173,3 +181,30 @@ export class TerminalHelper implements ITerminalHelper {
173181
}
174182
}
175183
}
184+
185+
/*
186+
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
187+
This is only a fall back to identify the default shell used by VSC.
188+
On Windows, determine the default shell.
189+
On others, default to bash.
190+
*/
191+
function getDefaultShell(osType: OSType, currentProcess: ICurrentProcess): string {
192+
if (osType === OSType.Windows) {
193+
return getTerminalDefaultShellWindows(osType, currentProcess);
194+
}
195+
return '/bin/bash';
196+
}
197+
let _TERMINAL_DEFAULT_SHELL_WINDOWS: string | null = null;
198+
function getTerminalDefaultShellWindows(osType: OSType, currentProcess: ICurrentProcess): string {
199+
if (!_TERMINAL_DEFAULT_SHELL_WINDOWS) {
200+
const isAtLeastWindows10 = osType === OSType.Windows && parseFloat(os.release()) >= 10;
201+
const is32ProcessOn64Windows = process.env.hasOwnProperty('PROCESSOR_ARCHITEW6432');
202+
const powerShellPath = `${process.env.windir}\\${is32ProcessOn64Windows ? 'Sysnative' : 'System32'}\\WindowsPowerShell\\v1.0\\powershell.exe`;
203+
_TERMINAL_DEFAULT_SHELL_WINDOWS = isAtLeastWindows10 ? powerShellPath : getWindowsShell(currentProcess);
204+
}
205+
return _TERMINAL_DEFAULT_SHELL_WINDOWS;
206+
}
207+
208+
function getWindowsShell(currentProcess: ICurrentProcess): string {
209+
return currentProcess.env.comspec || 'cmd.exe';
210+
}

src/client/common/terminal/service.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,7 @@ export class TerminalService implements ITerminalService, Disposable {
5858
if (this.terminal) {
5959
return;
6060
}
61-
const shellPath = this.terminalHelper.getTerminalShellPath();
62-
this.terminalShellType = !shellPath || shellPath.length === 0 ? TerminalShellType.other : this.terminalHelper.identifyTerminalShell(shellPath);
61+
this.terminalShellType = this.terminalHelper.identifyTerminalShell(this.terminal);
6362
this.terminal = this.terminalManager.createTerminal({ name: this.title });
6463

6564
// Sometimes the terminal takes some time to start up before it can start accepting input.

src/client/common/terminal/types.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,7 @@ export const ITerminalHelper = Symbol('ITerminalHelper');
5454

5555
export interface ITerminalHelper {
5656
createTerminal(title?: string): Terminal;
57-
identifyTerminalShell(shellPath: string): TerminalShellType;
58-
getTerminalShellPath(): string;
57+
identifyTerminalShell(terminal?: Terminal): TerminalShellType;
5958
buildCommandForTerminal(terminalShellType: TerminalShellType, command: string, args: string[]): string;
6059
getEnvironmentActivationCommands(terminalShellType: TerminalShellType, resource?: Uri): Promise<string[] | undefined>;
6160
getEnvironmentActivationShellCommands(resource: Resource, interpreter?: PythonInterpreter): Promise<string[] | undefined>;

src/client/extension.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -354,7 +354,7 @@ async function getActivationTelemetryProps(serviceContainer: IServiceContainer):
354354
// be able to partially populate as much as possible instead
355355
// (through granular try-catch statements).
356356
const terminalHelper = serviceContainer.get<ITerminalHelper>(ITerminalHelper);
357-
const terminalShellType = terminalHelper.identifyTerminalShell(terminalHelper.getTerminalShellPath());
357+
const terminalShellType = terminalHelper.identifyTerminalShell();
358358
const condaLocator = serviceContainer.get<ICondaService>(ICondaService);
359359
const interpreterService = serviceContainer.get<IInterpreterService>(IInterpreterService);
360360
const workspaceService = serviceContainer.get<IWorkspaceService>(IWorkspaceService);

0 commit comments

Comments
 (0)