22// Licensed under the MIT License.
33
44import { inject , injectable , named } from 'inversify' ;
5- import * as os from 'os' ;
65import { Terminal , Uri } from 'vscode' ;
76import { ICondaService , IInterpreterService , InterpreterType , PythonInterpreter } from '../../interpreter/contracts' ;
87import { sendTelemetryEvent } from '../../telemetry' ;
98import { EventName } from '../../telemetry/constants' ;
10- import { ITerminalManager } from '../application/types' ;
9+ import { ITerminalManager , IWorkspaceService } from '../application/types' ;
1110import '../extensions' ;
1211import { traceDecorators , traceError } from '../logger' ;
1312import { IPlatformService } from '../platform/types' ;
1413import { IConfigurationService , ICurrentProcess , Resource } from '../types' ;
1514import { OSType } from '../utils/platform' ;
15+ import { ShellDetector } from './shellDetector' ;
1616import { ITerminalActivationCommandProvider , ITerminalHelper , TerminalActivationProviders , TerminalShellType } from './types' ;
1717
18- // Types of shells can be found here:
19- // 1. https://wiki.ubuntu.com/ChangingShells
20- const IS_GITBASH = / ( g i t b a s h .e x e $ ) / i;
21- const IS_BASH = / ( b a s h .e x e $ | b a s h $ ) / i;
22- const IS_WSL = / ( w s l .e x e $ ) / i;
23- const IS_ZSH = / ( z s h $ ) / i;
24- const IS_KSH = / ( k s h $ ) / i;
25- const IS_COMMAND = / ( c m d .e x e $ | c m d $ ) / i;
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;
27- const IS_POWERSHELL_CORE = / ( p w s h .e x e $ | p w s h $ ) / i;
28- const IS_FISH = / ( f i s h $ ) / i;
29- const IS_CSHELL = / ( c s h $ ) / i;
30- const IS_TCSHELL = / ( t c s h $ ) / i;
31- const IS_XONSH = / ( x o n s h $ ) / i;
32-
33- const defaultOSShells = {
34- [ OSType . Linux ] : TerminalShellType . bash ,
35- [ OSType . OSX ] : TerminalShellType . bash ,
36- [ OSType . Windows ] : TerminalShellType . commandPrompt ,
37- [ OSType . Unknown ] : undefined
38- } ;
39-
4018@injectable ( )
4119export class TerminalHelper implements ITerminalHelper {
42- private readonly detectableShells : Map < TerminalShellType , RegExp > ;
20+ private readonly shellDetector : ShellDetector ;
4321 constructor ( @inject ( IPlatformService ) private readonly platform : IPlatformService ,
4422 @inject ( ITerminalManager ) private readonly terminalManager : ITerminalManager ,
4523 @inject ( ICondaService ) private readonly condaService : ICondaService ,
@@ -50,61 +28,17 @@ export class TerminalHelper implements ITerminalHelper {
5028 @inject ( ITerminalActivationCommandProvider ) @named ( TerminalActivationProviders . commandPromptAndPowerShell ) private readonly commandPromptAndPowerShell : ITerminalActivationCommandProvider ,
5129 @inject ( ITerminalActivationCommandProvider ) @named ( TerminalActivationProviders . pyenv ) private readonly pyenv : ITerminalActivationCommandProvider ,
5230 @inject ( ITerminalActivationCommandProvider ) @named ( TerminalActivationProviders . pipenv ) private readonly pipenv : ITerminalActivationCommandProvider ,
53- @inject ( IConfigurationService ) private readonly currentProcess : ICurrentProcess
31+ @inject ( ICurrentProcess ) private readonly currentProcess : ICurrentProcess ,
32+ @inject ( IWorkspaceService ) private readonly workspace : IWorkspaceService
5433 ) {
55- this . detectableShells = new Map < TerminalShellType , RegExp > ( ) ;
56- this . detectableShells . set ( TerminalShellType . powershell , IS_POWERSHELL ) ;
57- this . detectableShells . set ( TerminalShellType . gitbash , IS_GITBASH ) ;
58- this . detectableShells . set ( TerminalShellType . bash , IS_BASH ) ;
59- this . detectableShells . set ( TerminalShellType . wsl , IS_WSL ) ;
60- this . detectableShells . set ( TerminalShellType . zsh , IS_ZSH ) ;
61- this . detectableShells . set ( TerminalShellType . ksh , IS_KSH ) ;
62- this . detectableShells . set ( TerminalShellType . commandPrompt , IS_COMMAND ) ;
63- this . detectableShells . set ( TerminalShellType . fish , IS_FISH ) ;
64- this . detectableShells . set ( TerminalShellType . tcshell , IS_TCSHELL ) ;
65- this . detectableShells . set ( TerminalShellType . cshell , IS_CSHELL ) ;
66- this . detectableShells . set ( TerminalShellType . powershellCore , IS_POWERSHELL_CORE ) ;
67- this . detectableShells . set ( TerminalShellType . xonsh , IS_XONSH ) ;
34+ this . shellDetector = new ShellDetector ( this . platform , this . currentProcess , this . workspace ) ;
35+
6836 }
6937 public createTerminal ( title ?: string ) : Terminal {
7038 return this . terminalManager . createTerminal ( { name : title } ) ;
7139 }
7240 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 {
101- return Array . from ( this . detectableShells . keys ( ) )
102- . reduce ( ( matchedShell , shellToDetect ) => {
103- if ( matchedShell === TerminalShellType . other && this . detectableShells . get ( shellToDetect ) ! . test ( name ) ) {
104- return shellToDetect ;
105- }
106- return matchedShell ;
107- } , TerminalShellType . other ) ;
41+ return this . shellDetector . identifyTerminalShell ( terminal ) ;
10842 }
10943
11044 public buildCommandForTerminal ( terminalShellType : TerminalShellType , command : string , args : string [ ] ) {
@@ -119,7 +53,10 @@ export class TerminalHelper implements ITerminalHelper {
11953 return promise ;
12054 }
12155 public async getEnvironmentActivationShellCommands ( resource : Resource , interpreter ?: PythonInterpreter ) : Promise < string [ ] | undefined > {
122- const shell = defaultOSShells [ this . platform . osType ] ;
56+ if ( this . platform . osType === OSType . Unknown ) {
57+ return ;
58+ }
59+ const shell = this . shellDetector . identifyTerminalShell ( ) ;
12360 if ( ! shell ) {
12461 return ;
12562 }
@@ -181,30 +118,3 @@ export class TerminalHelper implements ITerminalHelper {
181118 }
182119 }
183120}
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