@@ -12,9 +12,8 @@ import {
12
12
import { identifyShellFromShellPath } from '../../common/terminal/shellDetectors/baseShellDetector' ;
13
13
import { TerminalShellType } from '../../common/terminal/types' ;
14
14
import { IDisposableRegistry , IPersistentStateFactory } from '../../common/types' ;
15
- import { createDeferred , sleep } from '../../common/utils/async' ;
16
- import { cache } from '../../common/utils/decorators' ;
17
- import { traceError , traceInfo , traceVerbose } from '../../logging' ;
15
+ import { sleep } from '../../common/utils/async' ;
16
+ import { traceError , traceVerbose } from '../../logging' ;
18
17
import { IShellIntegrationService } from '../types' ;
19
18
20
19
/**
@@ -29,17 +28,12 @@ const ShellIntegrationShells = [
29
28
TerminalShellType . fish ,
30
29
] ;
31
30
32
- export const isShellIntegrationWorkingKey = 'SHELL_INTEGRATION_WORKING_KEY' ;
31
+ export enum isShellIntegrationWorking {
32
+ key = 'SHELL_INTEGRATION_WORKING_KEY' ,
33
+ }
33
34
34
35
@injectable ( )
35
36
export class ShellIntegrationService implements IShellIntegrationService {
36
- /**
37
- * It seems to have a couple of issues:
38
- * * Ends up cluterring terminal history
39
- * * Does not work for hidden terminals: https://github.com/microsoft/vscode/issues/199611
40
- */
41
- private readonly USE_COMMAND_APPROACH = false ;
42
-
43
37
private isWorkingForShell = new Set < TerminalShellType > ( ) ;
44
38
45
39
private readonly didChange = new EventEmitter < void > ( ) ;
@@ -55,6 +49,12 @@ export class ShellIntegrationService implements IShellIntegrationService {
55
49
@inject ( IDisposableRegistry ) private readonly disposables : IDisposableRegistry ,
56
50
) {
57
51
try {
52
+ const activeShellType = identifyShellFromShellPath ( this . appEnvironment . shell ) ;
53
+ const key = getKeyForShell ( activeShellType ) ;
54
+ const persistedResult = this . persistentStateFactory . createGlobalPersistentState < boolean > ( key ) ;
55
+ if ( persistedResult . value ) {
56
+ this . isWorkingForShell . add ( activeShellType ) ;
57
+ }
58
58
this . appShell . onDidWriteTerminalData (
59
59
( e ) => {
60
60
if ( e . data . includes ( '\x1b]633;A\x07' ) ) {
@@ -63,6 +63,7 @@ export class ShellIntegrationService implements IShellIntegrationService {
63
63
shell = e . terminal . creationOptions . shellPath ;
64
64
}
65
65
const shellType = identifyShellFromShellPath ( shell ) ;
66
+ traceVerbose ( 'Received shell integration sequence for' , shellType ) ;
66
67
const wasWorking = this . isWorkingForShell . has ( shellType ) ;
67
68
this . isWorkingForShell . add ( shellType ) ;
68
69
if ( ! wasWorking ) {
@@ -86,6 +87,12 @@ export class ShellIntegrationService implements IShellIntegrationService {
86
87
this . isDataWriteEventWorking = false ;
87
88
traceError ( 'Unable to check if shell integration is active' , ex ) ;
88
89
}
90
+ const isEnabled = ! ! this . workspaceService
91
+ . getConfiguration ( 'terminal' )
92
+ . get < boolean > ( 'integrated.shellIntegration.enabled' ) ;
93
+ if ( ! isEnabled ) {
94
+ traceVerbose ( 'Shell integration is disabled in user settings.' ) ;
95
+ }
89
96
}
90
97
91
98
public readonly onDidChangeStatus = this . didChange . event ;
@@ -97,46 +104,48 @@ export class ShellIntegrationService implements IShellIntegrationService {
97
104
} ) ;
98
105
}
99
106
100
- @cache ( - 1 , true )
101
107
public async _isWorking ( shell : string ) : Promise < boolean > {
102
- const isEnabled = this . workspaceService
103
- . getConfiguration ( 'terminal' )
104
- . get < boolean > ( 'integrated.shellIntegration.enabled' ) ! ;
105
- if ( ! isEnabled ) {
106
- traceVerbose ( 'Shell integrated is disabled in user settings.' ) ;
107
- }
108
108
const shellType = identifyShellFromShellPath ( shell ) ;
109
- const isSupposedToWork = isEnabled && ShellIntegrationShells . includes ( shellType ) ;
109
+ const isSupposedToWork = ShellIntegrationShells . includes ( shellType ) ;
110
110
if ( ! isSupposedToWork ) {
111
111
return false ;
112
112
}
113
- if ( ! this . USE_COMMAND_APPROACH ) {
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 ) ;
128
- }
129
- const key = `${ isShellIntegrationWorkingKey } _${ shellType } ` ;
113
+ const key = getKeyForShell ( shellType ) ;
130
114
const persistedResult = this . persistentStateFactory . createGlobalPersistentState < boolean > ( key ) ;
131
115
if ( persistedResult . value !== undefined ) {
132
116
return persistedResult . value ;
133
117
}
134
- const result = await this . checkIfWorkingByRunningCommand ( shell ) ;
135
- // Persist result to storage to avoid running commands unncecessary.
136
- await persistedResult . updateValue ( result ) ;
118
+ const result = await this . useDataWriteApproach ( shellType ) ;
119
+ if ( result ) {
120
+ // Once we know that shell integration is working for a shell, persist it so we need not do this check every session.
121
+ await persistedResult . updateValue ( result ) ;
122
+ }
137
123
return result ;
138
124
}
139
125
126
+ private async useDataWriteApproach ( shellType : TerminalShellType ) {
127
+ // For now, based on problems with using the command approach, use terminal data write event.
128
+ if ( ! this . isDataWriteEventWorking ) {
129
+ // Assume shell integration is working, if data write event isn't working.
130
+ return true ;
131
+ }
132
+ if ( shellType === TerminalShellType . powershell || shellType === TerminalShellType . powershellCore ) {
133
+ // Due to upstream bug: https://github.com/microsoft/vscode/issues/204616, assume shell integration is working for now.
134
+ return true ;
135
+ }
136
+ if ( ! this . isWorkingForShell . has ( shellType ) ) {
137
+ // Maybe data write event has not been processed yet, wait a bit.
138
+ await sleep ( 1000 ) ;
139
+ }
140
+ traceVerbose (
141
+ 'Did we determine shell integration to be working for' ,
142
+ shellType ,
143
+ '?' ,
144
+ this . isWorkingForShell . has ( shellType ) ,
145
+ ) ;
146
+ return this . isWorkingForShell . has ( shellType ) ;
147
+ }
148
+
140
149
/**
141
150
* Creates a dummy terminal so that we are guaranteed a data write event for this shell type.
142
151
*/
@@ -146,39 +155,8 @@ export class ShellIntegrationService implements IShellIntegrationService {
146
155
hideFromUser : true ,
147
156
} ) ;
148
157
}
158
+ }
149
159
150
- private async checkIfWorkingByRunningCommand ( shell : string ) : Promise < boolean > {
151
- const shellType = identifyShellFromShellPath ( shell ) ;
152
- const deferred = createDeferred < void > ( ) ;
153
- const timestamp = new Date ( ) . getTime ( ) ;
154
- const name = `Python ${ timestamp } ` ;
155
- const onDidExecuteTerminalCommand = this . appShell . onDidExecuteTerminalCommand ?. bind ( this . appShell ) ;
156
- if ( ! onDidExecuteTerminalCommand ) {
157
- // Proposed API is not available, assume shell integration is working at this point.
158
- return true ;
159
- }
160
- try {
161
- const disposable = onDidExecuteTerminalCommand ( ( e ) => {
162
- if ( e . terminal . name === name ) {
163
- deferred . resolve ( ) ;
164
- }
165
- } ) ;
166
- const terminal = this . terminalManager . createTerminal ( {
167
- name,
168
- shellPath : shell ,
169
- hideFromUser : true ,
170
- } ) ;
171
- terminal . sendText ( `echo ${ shell } ` ) ;
172
- const success = await Promise . race ( [ sleep ( 3000 ) . then ( ( ) => false ) , deferred . promise . then ( ( ) => true ) ] ) ;
173
- disposable . dispose ( ) ;
174
- if ( ! success ) {
175
- traceInfo ( `Shell integration is not working for ${ shellType } ` ) ;
176
- }
177
- return success ;
178
- } catch ( ex ) {
179
- traceVerbose ( `Proposed API is not available, failed to subscribe to onDidExecuteTerminalCommand` , ex ) ;
180
- // Proposed API is not available, assume shell integration is working at this point.
181
- return true ;
182
- }
183
- }
160
+ function getKeyForShell ( shellType : TerminalShellType ) {
161
+ return `${ isShellIntegrationWorking . key } _${ shellType } ` ;
184
162
}
0 commit comments