@@ -48,6 +48,9 @@ const messages = {
48
48
[ DiagnosticCodes . IncompletePathVarDiagnostic ] : l10n . t (
49
49
'We detected an issue with "Path" environment variable that breaks features such as IntelliSense, linting and debugging. Please edit it to make sure it contains the "SystemRoot" subdirectories.' ,
50
50
) ,
51
+ [ DiagnosticCodes . DefaultShellErrorDiagnostic ] : l10n . t (
52
+ 'We detected an issue with your default shell that breaks features such as IntelliSense, linting and debugging. Try resetting "Comspec" and "Path" environment variables to fix it.' ,
53
+ ) ,
51
54
} ;
52
55
53
56
export class InvalidPythonInterpreterDiagnostic extends BaseDiagnostic {
@@ -75,7 +78,10 @@ export class InvalidPythonInterpreterDiagnostic extends BaseDiagnostic {
75
78
76
79
export class DefaultShellDiagnostic extends BaseDiagnostic {
77
80
constructor (
78
- code : DiagnosticCodes . InvalidComspecDiagnostic | DiagnosticCodes . IncompletePathVarDiagnostic ,
81
+ code :
82
+ | DiagnosticCodes . InvalidComspecDiagnostic
83
+ | DiagnosticCodes . IncompletePathVarDiagnostic
84
+ | DiagnosticCodes . DefaultShellErrorDiagnostic ,
79
85
resource : Resource ,
80
86
scope = DiagnosticScope . Global ,
81
87
) {
@@ -170,6 +176,69 @@ export class InvalidPythonInterpreterService extends BaseDiagnosticsService
170
176
return false ;
171
177
}
172
178
179
+ private async diagnoseDefaultShell ( resource : Resource ) : Promise < IDiagnostic [ ] > {
180
+ if ( getOSType ( ) !== OSType . Windows ) {
181
+ return [ ] ;
182
+ }
183
+ const interpreterService = this . serviceContainer . get < IInterpreterService > ( IInterpreterService ) ;
184
+ const currentInterpreter = await interpreterService . getActiveInterpreter ( resource ) ;
185
+ if ( currentInterpreter ) {
186
+ return [ ] ;
187
+ }
188
+ try {
189
+ await this . shellExecPython ( ) ;
190
+ } catch ( ex ) {
191
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
192
+ if ( ( ex as any ) . errno === - 4058 ) {
193
+ // ENOENT (-4058) error is thrown by Node when the default shell is invalid.
194
+ if ( await this . isComspecInvalid ( ) ) {
195
+ traceError ( 'ComSpec is set to an invalid value' , getEnvironmentVariable ( 'ComSpec' ) ) ;
196
+ return [ new DefaultShellDiagnostic ( DiagnosticCodes . InvalidComspecDiagnostic , resource ) ] ;
197
+ }
198
+ if ( this . isPathVarIncomplete ( ) ) {
199
+ traceError ( 'PATH env var appears to be incomplete' , process . env . Path , process . env . PATH ) ;
200
+ return [ new DefaultShellDiagnostic ( DiagnosticCodes . IncompletePathVarDiagnostic , resource ) ] ;
201
+ }
202
+ return [ new DefaultShellDiagnostic ( DiagnosticCodes . DefaultShellErrorDiagnostic , resource ) ] ;
203
+ }
204
+ }
205
+ return [ ] ;
206
+ }
207
+
208
+ private async isComspecInvalid ( ) {
209
+ const comSpec = getEnvironmentVariable ( 'ComSpec' ) ?? '' ;
210
+ const fs = this . serviceContainer . get < IFileSystem > ( IFileSystem ) ;
211
+ return fs . fileExists ( comSpec ) . then ( ( exists ) => ! exists ) ;
212
+ }
213
+
214
+ // eslint-disable-next-line class-methods-use-this
215
+ private isPathVarIncomplete ( ) {
216
+ const envVars = getSearchPathEnvVarNames ( ) ;
217
+ const systemRoot = getEnvironmentVariable ( 'SystemRoot' ) ?? 'C:\\WINDOWS' ;
218
+ for ( const envVar of envVars ) {
219
+ const value = getEnvironmentVariable ( envVar ) ;
220
+ if ( value ?. includes ( systemRoot ) ) {
221
+ return false ;
222
+ }
223
+ }
224
+ return true ;
225
+ }
226
+
227
+ @cache ( - 1 , true )
228
+ // eslint-disable-next-line class-methods-use-this
229
+ private async shellExecPython ( ) {
230
+ const configurationService = this . serviceContainer . get < IConfigurationService > ( IConfigurationService ) ;
231
+ const { pythonPath } = configurationService . getSettings ( ) ;
232
+ const [ args ] = getExecutable ( ) ;
233
+ const argv = [ pythonPath , ...args ] ;
234
+ // Concat these together to make a set of quoted strings
235
+ const quoted = argv . reduce (
236
+ ( p , c ) => ( p ? `${ p } ${ c . toCommandArgumentForPythonExt ( ) } ` : `${ c . toCommandArgumentForPythonExt ( ) } ` ) ,
237
+ '' ,
238
+ ) ;
239
+ return shellExec ( quoted , { timeout : 15000 } ) ;
240
+ }
241
+
173
242
@cache ( 1000 , true ) // This is to handle throttling of multiple events.
174
243
protected async onHandle ( diagnostics : IDiagnostic [ ] ) : Promise < void > {
175
244
if ( diagnostics . length === 0 ) {
@@ -215,6 +284,17 @@ export class InvalidPythonInterpreterService extends BaseDiagnosticsService
215
284
} ,
216
285
] ;
217
286
}
287
+ if ( diagnostic . code === DiagnosticCodes . DefaultShellErrorDiagnostic ) {
288
+ return [
289
+ {
290
+ prompt : Common . seeInstructions ,
291
+ command : commandFactory . createCommand ( diagnostic , {
292
+ type : 'launch' ,
293
+ options : 'https://aka.ms/AAk744c' ,
294
+ } ) ,
295
+ } ,
296
+ ] ;
297
+ }
218
298
const prompts = [
219
299
{
220
300
prompt : Common . selectPythonInterpreter ,
@@ -235,68 +315,6 @@ export class InvalidPythonInterpreterService extends BaseDiagnosticsService
235
315
}
236
316
return prompts ;
237
317
}
238
-
239
- private async diagnoseDefaultShell ( resource : Resource ) : Promise < IDiagnostic [ ] > {
240
- if ( getOSType ( ) !== OSType . Windows ) {
241
- return [ ] ;
242
- }
243
- const interpreterService = this . serviceContainer . get < IInterpreterService > ( IInterpreterService ) ;
244
- const currentInterpreter = await interpreterService . getActiveInterpreter ( resource ) ;
245
- if ( currentInterpreter ) {
246
- return [ ] ;
247
- }
248
- try {
249
- await this . shellExecPython ( ) ;
250
- } catch ( ex ) {
251
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
252
- if ( ( ex as any ) . errno === - 4058 ) {
253
- // ENOENT (-4058) error is thrown by Node when the default shell is invalid.
254
- if ( await this . isComspecInvalid ( ) ) {
255
- traceError ( 'ComSpec is set to an invalid value' , getEnvironmentVariable ( 'ComSpec' ) ) ;
256
- return [ new DefaultShellDiagnostic ( DiagnosticCodes . InvalidComspecDiagnostic , resource ) ] ;
257
- }
258
- if ( this . isPathVarIncomplete ( ) ) {
259
- traceError ( 'PATH env var appears to be incomplete' , process . env . Path , process . env . PATH ) ;
260
- return [ new DefaultShellDiagnostic ( DiagnosticCodes . IncompletePathVarDiagnostic , resource ) ] ;
261
- }
262
- }
263
- }
264
- return [ ] ;
265
- }
266
-
267
- private async isComspecInvalid ( ) {
268
- const comSpec = getEnvironmentVariable ( 'ComSpec' ) ?? '' ;
269
- const fs = this . serviceContainer . get < IFileSystem > ( IFileSystem ) ;
270
- return fs . fileExists ( comSpec ) . then ( ( exists ) => ! exists ) ;
271
- }
272
-
273
- // eslint-disable-next-line class-methods-use-this
274
- private isPathVarIncomplete ( ) {
275
- const envVars = getSearchPathEnvVarNames ( ) ;
276
- const systemRoot = getEnvironmentVariable ( 'SystemRoot' ) ?? 'C:\\WINDOWS' ;
277
- for ( const envVar of envVars ) {
278
- const value = getEnvironmentVariable ( envVar ) ;
279
- if ( value ?. includes ( systemRoot ) ) {
280
- return false ;
281
- }
282
- }
283
- return true ;
284
- }
285
-
286
- @cache ( - 1 , true )
287
- // eslint-disable-next-line class-methods-use-this
288
- private async shellExecPython ( ) {
289
- const configurationService = this . serviceContainer . get < IConfigurationService > ( IConfigurationService ) ;
290
- const { pythonPath } = configurationService . getSettings ( ) ;
291
- const [ args ] = getExecutable ( ) ;
292
- const argv = [ pythonPath , ...args ] ;
293
- // Concat these together to make a set of quoted strings
294
- const quoted = argv . reduce (
295
- ( p , c ) => ( p ? `${ p } ${ c . toCommandArgumentForPythonExt ( ) } ` : `${ c . toCommandArgumentForPythonExt ( ) } ` ) ,
296
- '' ,
297
- ) ;
298
- return shellExec ( quoted , { timeout : 15000 } ) ;
299
- }
300
318
}
301
319
302
320
function getOnCloseHandler ( diagnostic : IDiagnostic ) : IDiagnosticMessageOnCloseHandler | undefined {
0 commit comments