@@ -28,6 +28,10 @@ import { EventName } from '../../../telemetry/constants';
2828import { IExtensionSingleActivationService } from '../../../activation/types' ;
2929import { cache } from '../../../common/utils/decorators' ;
3030import { noop } from '../../../common/utils/misc' ;
31+ import { IPythonExecutionFactory } from '../../../common/process/types' ;
32+ import { getOSType , OSType } from '../../../common/utils/platform' ;
33+ import { IFileSystem } from '../../../common/platform/types' ;
34+ import { traceError } from '../../../logging' ;
3135
3236const messages = {
3337 [ DiagnosticCodes . NoPythonInterpretersDiagnostic ] : l10n . t (
@@ -36,6 +40,9 @@ const messages = {
3640 [ DiagnosticCodes . InvalidPythonInterpreterDiagnostic ] : l10n . t (
3741 'An Invalid Python interpreter is selected{0}, please try changing it to enable features such as IntelliSense, linting, and debugging. See output for more details regarding why the interpreter is invalid.' ,
3842 ) ,
43+ [ DiagnosticCodes . InvalidComspecDiagnostic ] : l10n . t (
44+ "The environment variable 'Comspec' seems to be set to an invalid value. Please correct it to carry valid path to Command Prompt to enable features such as IntelliSense, linting, and debugging. See instructions which might help." ,
45+ ) ,
3946} ;
4047
4148export class InvalidPythonInterpreterDiagnostic extends BaseDiagnostic {
@@ -61,6 +68,12 @@ export class InvalidPythonInterpreterDiagnostic extends BaseDiagnostic {
6168 }
6269}
6370
71+ export class DefaultShellDiagnostic extends BaseDiagnostic {
72+ constructor ( code : DiagnosticCodes . InvalidComspecDiagnostic , resource : Resource , scope = DiagnosticScope . Global ) {
73+ super ( code , messages [ code ] , DiagnosticSeverity . Error , scope , resource , undefined , 'always' ) ;
74+ }
75+ }
76+
6477export const InvalidPythonInterpreterServiceId = 'InvalidPythonInterpreterServiceId' ;
6578
6679@injectable ( )
@@ -73,7 +86,11 @@ export class InvalidPythonInterpreterService extends BaseDiagnosticsService
7386 @inject ( IDisposableRegistry ) disposableRegistry : IDisposableRegistry ,
7487 ) {
7588 super (
76- [ DiagnosticCodes . NoPythonInterpretersDiagnostic , DiagnosticCodes . InvalidPythonInterpreterDiagnostic ] ,
89+ [
90+ DiagnosticCodes . NoPythonInterpretersDiagnostic ,
91+ DiagnosticCodes . InvalidPythonInterpreterDiagnostic ,
92+ DiagnosticCodes . InvalidComspecDiagnostic ,
93+ ] ,
7794 serviceContainer ,
7895 disposableRegistry ,
7996 false ,
@@ -103,6 +120,13 @@ export class InvalidPythonInterpreterService extends BaseDiagnosticsService
103120 public async _manualDiagnose ( resource : Resource ) : Promise < IDiagnostic [ ] > {
104121 const workspaceService = this . serviceContainer . get < IWorkspaceService > ( IWorkspaceService ) ;
105122 const interpreterService = this . serviceContainer . get < IInterpreterService > ( IInterpreterService ) ;
123+ const currentInterpreter = await interpreterService . getActiveInterpreter ( resource ) ;
124+ if ( ! currentInterpreter ) {
125+ const diagnostics = await this . diagnoseDefaultShell ( resource ) ;
126+ if ( diagnostics . length ) {
127+ return diagnostics ;
128+ }
129+ }
106130 const hasInterpreters = await interpreterService . hasInterpreters ( ) ;
107131 const interpreterPathService = this . serviceContainer . get < IInterpreterPathService > ( IInterpreterPathService ) ;
108132 const isInterpreterSetToDefault = interpreterPathService . get ( resource ) === 'python' ;
@@ -118,7 +142,6 @@ export class InvalidPythonInterpreterService extends BaseDiagnosticsService
118142 ] ;
119143 }
120144
121- const currentInterpreter = await interpreterService . getActiveInterpreter ( resource ) ;
122145 if ( ! currentInterpreter ) {
123146 return [
124147 new InvalidPythonInterpreterDiagnostic (
@@ -163,6 +186,17 @@ export class InvalidPythonInterpreterService extends BaseDiagnosticsService
163186
164187 private getCommandPrompts ( diagnostic : IDiagnostic ) : { prompt : string ; command ?: IDiagnosticCommand } [ ] {
165188 const commandFactory = this . serviceContainer . get < IDiagnosticsCommandFactory > ( IDiagnosticsCommandFactory ) ;
189+ if ( diagnostic . code === DiagnosticCodes . InvalidComspecDiagnostic ) {
190+ return [
191+ {
192+ prompt : Common . instructions ,
193+ command : commandFactory . createCommand ( diagnostic , {
194+ type : 'launch' ,
195+ options : 'https://aka.ms/AAk3djo' ,
196+ } ) ,
197+ } ,
198+ ] ;
199+ }
166200 const prompts = [
167201 {
168202 prompt : Common . selectPythonInterpreter ,
@@ -183,6 +217,32 @@ export class InvalidPythonInterpreterService extends BaseDiagnosticsService
183217 }
184218 return prompts ;
185219 }
220+
221+ private async diagnoseDefaultShell ( resource : Resource ) : Promise < IDiagnostic [ ] > {
222+ if ( getOSType ( ) !== OSType . Windows ) {
223+ return [ ] ;
224+ }
225+ const executionFactory = this . serviceContainer . get < IPythonExecutionFactory > ( IPythonExecutionFactory ) ;
226+ const executionService = await executionFactory . create ( { resource } ) ;
227+ try {
228+ await executionService . getExecutablePath ( { throwOnError : true } ) ;
229+ } catch ( ex ) {
230+ if ( ( ex as Error ) . message ?. includes ( '4058' ) ) {
231+ // ENOENT (-4058) error is thrown by Node when the default shell is invalid.
232+ if ( await this . isComspecInvalid ( ) ) {
233+ traceError ( 'ComSpec is set to an invalid value' , process . env . ComSpec ) ;
234+ return [ new DefaultShellDiagnostic ( DiagnosticCodes . InvalidComspecDiagnostic , resource ) ] ;
235+ }
236+ }
237+ }
238+ return [ ] ;
239+ }
240+
241+ private async isComspecInvalid ( ) {
242+ const comSpec = process . env . ComSpec ?? '' ;
243+ const fs = this . serviceContainer . get < IFileSystem > ( IFileSystem ) ;
244+ return fs . fileExists ( comSpec ) ;
245+ }
186246}
187247
188248function getOnCloseHandler ( diagnostic : IDiagnostic ) : IDiagnosticMessageOnCloseHandler | undefined {
0 commit comments