22// Licensed under the MIT License. 
33
44import  {  inject ,  injectable ,  named  }  from  'inversify' ; 
5+ import  *  as  os  from  'os' ; 
56import  {  Terminal ,  Uri  }  from  'vscode' ; 
67import  {  ICondaService ,  IInterpreterService ,  InterpreterType ,  PythonInterpreter  }  from  '../../interpreter/contracts' ; 
78import  {  sendTelemetryEvent  }  from  '../../telemetry' ; 
89import  {  EventName  }  from  '../../telemetry/constants' ; 
9- import  {  ITerminalManager ,   IWorkspaceService  }  from  '../application/types' ; 
10+ import  {  ITerminalManager  }  from  '../application/types' ; 
1011import  '../extensions' ; 
1112import  {  traceDecorators ,  traceError  }  from  '../logger' ; 
1213import  {  IPlatformService  }  from  '../platform/types' ; 
13- import  {  IConfigurationService ,  Resource  }  from  '../types' ; 
14+ import  {  IConfigurationService ,  ICurrentProcess ,   Resource  }  from  '../types' ; 
1415import  {  OSType  }  from  '../utils/platform' ; 
1516import  {  ITerminalActivationCommandProvider ,  ITerminalHelper ,  TerminalActivationProviders ,  TerminalShellType  }  from  './types' ; 
1617
@@ -21,7 +22,7 @@ const IS_BASH = /(bash.exe$|bash$)/i;
2122const  IS_WSL  =  / ( w s l .e x e $ ) / i; 
2223const  IS_ZSH  =  / ( z s h $ ) / i; 
2324const  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; 
2526const  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; 
2627const  IS_POWERSHELL_CORE  =  / ( p w s h .e x e $ | p w s h $ ) / i; 
2728const  IS_FISH  =  / ( f i s h $ ) / 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' }  ; 
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