2
2
// Licensed under the MIT License.
3
3
4
4
import { 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' ;
6
12
import { identifyShellFromShellPath } from '../../common/terminal/shellDetectors/baseShellDetector' ;
7
13
import { TerminalShellType } from '../../common/terminal/types' ;
8
- import { IPersistentStateFactory } from '../../common/types' ;
14
+ import { IDisposableRegistry , IPersistentStateFactory } from '../../common/types' ;
9
15
import { createDeferred , sleep } from '../../common/utils/async' ;
10
16
import { cache } from '../../common/utils/decorators' ;
11
17
import { traceError , traceInfo , traceVerbose } from '../../logging' ;
@@ -34,12 +40,55 @@ export class ShellIntegrationService implements IShellIntegrationService {
34
40
*/
35
41
private readonly USE_COMMAND_APPROACH = false ;
36
42
43
+ private isWorkingForShell = new Set < TerminalShellType > ( ) ;
44
+
45
+ private readonly didChange = new EventEmitter < void > ( ) ;
46
+
47
+ private isDataWriteEventWorking = true ;
48
+
37
49
constructor (
38
50
@inject ( ITerminalManager ) private readonly terminalManager : ITerminalManager ,
39
51
@inject ( IApplicationShell ) private readonly appShell : IApplicationShell ,
40
52
@inject ( IWorkspaceService ) private readonly workspaceService : IWorkspaceService ,
41
53
@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 ;
43
92
44
93
public async isWorking ( shell : string ) : Promise < boolean > {
45
94
return this . _isWorking ( shell ) . catch ( ( ex ) => {
@@ -62,8 +111,20 @@ export class ShellIntegrationService implements IShellIntegrationService {
62
111
return false ;
63
112
}
64
113
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 ) ;
67
128
}
68
129
const key = `${ isShellIntegrationWorkingKey } _${ shellType } ` ;
69
130
const persistedResult = this . persistentStateFactory . createGlobalPersistentState < boolean > ( key ) ;
@@ -76,6 +137,16 @@ export class ShellIntegrationService implements IShellIntegrationService {
76
137
return result ;
77
138
}
78
139
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
+
79
150
private async checkIfWorkingByRunningCommand ( shell : string ) : Promise < boolean > {
80
151
const shellType = identifyShellFromShellPath ( shell ) ;
81
152
const deferred = createDeferred < void > ( ) ;
0 commit comments