22// Licensed under the MIT License. 
33
44import  {  injectable ,  inject  }  from  'inversify' ; 
5- import  {  IApplicationShell ,  ITerminalManager ,  IWorkspaceService  }  from  '../../common/application/types' ; 
5+ import  {  EventEmitter  }  from  'vscode' ; 
6+ import  { 
7+     IApplicationEnvironment , 
8+     IApplicationShell , 
9+     ITerminalManager , 
10+     IWorkspaceService , 
11+ }  from  '../../common/application/types' ; 
612import  {  identifyShellFromShellPath  }  from  '../../common/terminal/shellDetectors/baseShellDetector' ; 
713import  {  TerminalShellType  }  from  '../../common/terminal/types' ; 
8- import  {  IPersistentStateFactory  }  from  '../../common/types' ; 
14+ import  {  IDisposableRegistry ,   IPersistentStateFactory  }  from  '../../common/types' ; 
915import  {  createDeferred ,  sleep  }  from  '../../common/utils/async' ; 
1016import  {  cache  }  from  '../../common/utils/decorators' ; 
1117import  {  traceError ,  traceInfo ,  traceVerbose  }  from  '../../logging' ; 
@@ -34,12 +40,55 @@ export class ShellIntegrationService implements IShellIntegrationService {
3440     */ 
3541    private  readonly  USE_COMMAND_APPROACH  =  false ; 
3642
43+     private  isWorkingForShell  =  new  Set < TerminalShellType > ( ) ; 
44+ 
45+     private  readonly  didChange  =  new  EventEmitter < void > ( ) ; 
46+ 
47+     private  isDataWriteEventWorking  =  true ; 
48+ 
3749    constructor ( 
3850        @inject ( ITerminalManager )  private  readonly  terminalManager : ITerminalManager , 
3951        @inject ( IApplicationShell )  private  readonly  appShell : IApplicationShell , 
4052        @inject ( IWorkspaceService )  private  readonly  workspaceService : IWorkspaceService , 
4153        @inject ( IPersistentStateFactory )  private  readonly  persistentStateFactory : IPersistentStateFactory , 
42-     )  { } 
54+         @inject ( IApplicationEnvironment )  private  readonly  appEnvironment : IApplicationEnvironment , 
55+         @inject ( IDisposableRegistry )  private  readonly  disposables : IDisposableRegistry , 
56+     )  { 
57+         try  { 
58+             this . appShell . onDidWriteTerminalData ( 
59+                 ( e )  =>  { 
60+                     if  ( e . data . includes ( '\x1b]633;A\x07' ) )  { 
61+                         let  {  shell }  =  this . appEnvironment ; 
62+                         if  ( 'shellPath'  in  e . terminal . creationOptions  &&  e . terminal . creationOptions . shellPath )  { 
63+                             shell  =  e . terminal . creationOptions . shellPath ; 
64+                         } 
65+                         const  shellType  =  identifyShellFromShellPath ( shell ) ; 
66+                         const  wasWorking  =  this . isWorkingForShell . has ( shellType ) ; 
67+                         this . isWorkingForShell . add ( shellType ) ; 
68+                         if  ( ! wasWorking )  { 
69+                             // If it wasn't working previously, status has changed. 
70+                             this . didChange . fire ( ) ; 
71+                         } 
72+                     } 
73+                 } , 
74+                 this , 
75+                 this . disposables , 
76+             ) ; 
77+             this . appEnvironment . onDidChangeShell ( 
78+                 async  ( shell : string )  =>  { 
79+                     this . createDummyHiddenTerminal ( shell ) ; 
80+                 } , 
81+                 this , 
82+                 this . disposables , 
83+             ) ; 
84+             this . createDummyHiddenTerminal ( this . appEnvironment . shell ) ; 
85+         }  catch  ( ex )  { 
86+             this . isDataWriteEventWorking  =  false ; 
87+             traceError ( 'Unable to check if shell integration is active' ,  ex ) ; 
88+         } 
89+     } 
90+ 
91+     public  readonly  onDidChangeStatus  =  this . didChange . event ; 
4392
4493    public  async  isWorking ( shell : string ) : Promise < boolean >  { 
4594        return  this . _isWorking ( shell ) . catch ( ( ex )  =>  { 
@@ -62,8 +111,20 @@ export class ShellIntegrationService implements IShellIntegrationService {
62111            return  false ; 
63112        } 
64113        if  ( ! this . USE_COMMAND_APPROACH )  { 
65-             // For now, based on problems with using the command approach, assume it always works. 
66-             return  true ; 
114+             // For now, based on problems with using the command approach, use terminal data write event. 
115+             if  ( ! this . isDataWriteEventWorking )  { 
116+                 // Assume shell integration is working, if data write event isn't working. 
117+                 return  true ; 
118+             } 
119+             if  ( shellType  ===  TerminalShellType . powershell  ||  shellType  ===  TerminalShellType . powershellCore )  { 
120+                 // Due to upstream bug: https://github.com/microsoft/vscode/issues/204616, assume shell integration is working for now. 
121+                 return  true ; 
122+             } 
123+             if  ( ! this . isWorkingForShell . has ( shellType ) )  { 
124+                 // Maybe data write event has not been processed yet, wait a bit. 
125+                 await  sleep ( 1000 ) ; 
126+             } 
127+             return  this . isWorkingForShell . has ( shellType ) ; 
67128        } 
68129        const  key  =  `${ isShellIntegrationWorkingKey } ${ shellType }  ; 
69130        const  persistedResult  =  this . persistentStateFactory . createGlobalPersistentState < boolean > ( key ) ; 
@@ -76,6 +137,16 @@ export class ShellIntegrationService implements IShellIntegrationService {
76137        return  result ; 
77138    } 
78139
140+     /** 
141+      * Creates a dummy terminal so that we are guaranteed a data write event for this shell type. 
142+      */ 
143+     private  createDummyHiddenTerminal ( shell : string )  { 
144+         this . terminalManager . createTerminal ( { 
145+             shellPath : shell , 
146+             hideFromUser : true , 
147+         } ) ; 
148+     } 
149+ 
79150    private  async  checkIfWorkingByRunningCommand ( shell : string ) : Promise < boolean >  { 
80151        const  shellType  =  identifyShellFromShellPath ( shell ) ; 
81152        const  deferred  =  createDeferred < void > ( ) ; 
0 commit comments