2
2
// Licensed under the MIT License.
3
3
4
4
import { inject , injectable , named } from 'inversify' ;
5
+ import * as os from 'os' ;
5
6
import { Terminal , Uri } from 'vscode' ;
6
7
import { ICondaService , IInterpreterService , InterpreterType , PythonInterpreter } from '../../interpreter/contracts' ;
7
8
import { sendTelemetryEvent } from '../../telemetry' ;
8
9
import { EventName } from '../../telemetry/constants' ;
9
- import { ITerminalManager , IWorkspaceService } from '../application/types' ;
10
+ import { ITerminalManager } from '../application/types' ;
10
11
import '../extensions' ;
11
12
import { traceDecorators , traceError } from '../logger' ;
12
13
import { IPlatformService } from '../platform/types' ;
13
- import { IConfigurationService , Resource } from '../types' ;
14
+ import { IConfigurationService , ICurrentProcess , Resource } from '../types' ;
14
15
import { OSType } from '../utils/platform' ;
15
16
import { ITerminalActivationCommandProvider , ITerminalHelper , TerminalActivationProviders , TerminalShellType } from './types' ;
16
17
@@ -21,7 +22,7 @@ const IS_BASH = /(bash.exe$|bash$)/i;
21
22
const IS_WSL = / ( w s l .e x e $ ) / i;
22
23
const IS_ZSH = / ( z s h $ ) / i;
23
24
const IS_KSH = / ( k s h $ ) / i;
24
- const IS_COMMAND = / c m d .e x e $ / i;
25
+ const IS_COMMAND = / ( c m d .e x e $ | c m d $ ) / i;
25
26
const IS_POWERSHELL = / ( p o w e r s h e l l .e x e $ | p o w e r s h e l l $ ) / i;
26
27
const IS_POWERSHELL_CORE = / ( p w s h .e x e $ | p w s h $ ) / i;
27
28
const IS_FISH = / ( f i s h $ ) / i;
@@ -41,15 +42,15 @@ export class TerminalHelper implements ITerminalHelper {
41
42
private readonly detectableShells : Map < TerminalShellType , RegExp > ;
42
43
constructor ( @inject ( IPlatformService ) private readonly platform : IPlatformService ,
43
44
@inject ( ITerminalManager ) private readonly terminalManager : ITerminalManager ,
44
- @inject ( IWorkspaceService ) private readonly workspace : IWorkspaceService ,
45
45
@inject ( ICondaService ) private readonly condaService : ICondaService ,
46
46
@inject ( IInterpreterService ) private readonly interpreterService : IInterpreterService ,
47
47
@inject ( IConfigurationService ) private readonly configurationService : IConfigurationService ,
48
48
@inject ( ITerminalActivationCommandProvider ) @named ( TerminalActivationProviders . conda ) private readonly conda : ITerminalActivationCommandProvider ,
49
49
@inject ( ITerminalActivationCommandProvider ) @named ( TerminalActivationProviders . bashCShellFish ) private readonly bashCShellFish : ITerminalActivationCommandProvider ,
50
50
@inject ( ITerminalActivationCommandProvider ) @named ( TerminalActivationProviders . commandPromptAndPowerShell ) private readonly commandPromptAndPowerShell : ITerminalActivationCommandProvider ,
51
51
@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
53
54
) {
54
55
this . detectableShells = new Map < TerminalShellType , RegExp > ( ) ;
55
56
this . detectableShells . set ( TerminalShellType . powershell , IS_POWERSHELL ) ;
@@ -68,37 +69,44 @@ export class TerminalHelper implements ITerminalHelper {
68
69
public createTerminal ( title ?: string ) : Terminal {
69
70
return this . terminalManager . createTerminal ( { name : title } ) ;
70
71
}
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 {
72
101
return Array . from ( this . detectableShells . keys ( ) )
73
102
. 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 ) ) {
75
104
return shellToDetect ;
76
105
}
77
106
return matchedShell ;
78
107
} , TerminalShellType . other ) ;
79
108
}
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
+
102
110
public buildCommandForTerminal ( terminalShellType : TerminalShellType , command : string , args : string [ ] ) {
103
111
const isPowershell = terminalShellType === TerminalShellType . powershell || terminalShellType === TerminalShellType . powershellCore ;
104
112
const commandPrefix = isPowershell ? '& ' : '' ;
@@ -173,3 +181,30 @@ export class TerminalHelper implements ITerminalHelper {
173
181
}
174
182
}
175
183
}
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
+ }
0 commit comments