@@ -6,14 +6,13 @@ import { ExtensionContext, OutputChannel } from 'vscode';
66import { Disposable , LanguageClient , LanguageClientOptions , ServerOptions } from 'vscode-languageclient' ;
77import { IApplicationShell } from '../common/application/types' ;
88import { isTestExecution , STANDARD_OUTPUT_CHANNEL } from '../common/constants' ;
9- import '../common/extensions' ;
109import { IFileSystem , IPlatformService } from '../common/platform/types' ;
11- import { IProcessService , IPythonExecutionFactory } from '../common/process/types' ;
10+ import { IProcessService } from '../common/process/types' ;
1211import { StopWatch } from '../common/stopWatch' ;
1312import { IConfigurationService , IOutputChannel , IPythonSettings } from '../common/types' ;
14- import { IInterpreterService } from '../interpreter/contracts' ;
1513import { IServiceContainer } from '../ioc/types' ;
1614import { AnalysisEngineDownloader } from './downloader' ;
15+ import { InterpreterDataService } from './interpreterDataService' ;
1716import { PlatformData } from './platformData' ;
1817import { IExtensionActivator } from './types' ;
1918
@@ -22,12 +21,7 @@ const dotNetCommand = 'dotnet';
2221const languageClientName = 'Python Tools' ;
2322const analysisEngineFolder = 'analysis' ;
2423
25- class InterpreterData {
26- constructor ( public readonly version : string , public readonly prefix : string ) { }
27- }
28-
2924export class AnalysisExtensionActivator implements IExtensionActivator {
30- private readonly executionFactory : IPythonExecutionFactory ;
3125 private readonly configuration : IConfigurationService ;
3226 private readonly appShell : IApplicationShell ;
3327 private readonly output : OutputChannel ;
@@ -37,7 +31,6 @@ export class AnalysisExtensionActivator implements IExtensionActivator {
3731 private languageClient : LanguageClient | undefined ;
3832
3933 constructor ( private readonly services : IServiceContainer , pythonSettings : IPythonSettings ) {
40- this . executionFactory = this . services . get < IPythonExecutionFactory > ( IPythonExecutionFactory ) ;
4134 this . configuration = this . services . get < IConfigurationService > ( IConfigurationService ) ;
4235 this . appShell = this . services . get < IApplicationShell > ( IApplicationShell ) ;
4336 this . output = this . services . get < OutputChannel > ( IOutputChannel , STANDARD_OUTPUT_CHANNEL ) ;
@@ -50,7 +43,6 @@ export class AnalysisExtensionActivator implements IExtensionActivator {
5043 if ( ! clientOptions ) {
5144 return false ;
5245 }
53- this . output . appendLine ( `Options determined: ${ this . sw . elapsedTime } ms` ) ;
5446 return this . startLanguageServer ( context , clientOptions ) ;
5547 }
5648
@@ -68,16 +60,17 @@ export class AnalysisExtensionActivator implements IExtensionActivator {
6860 if ( ! await this . fs . fileExistsAsync ( mscorlib ) ) {
6961 // Depends on .NET Runtime or SDK
7062 this . languageClient = this . createSimpleLanguageClient ( context , clientOptions ) ;
71- const e = await this . tryStartLanguageClient ( context , this . languageClient ) ;
72- if ( ! e ) {
63+ try {
64+ await this . tryStartLanguageClient ( context , this . languageClient ) ;
7365 return true ;
66+ } catch ( ex ) {
67+ if ( await this . isDotNetInstalled ( ) ) {
68+ this . appShell . showErrorMessage ( `.NET Runtime appears to be installed but the language server did not start. Error ${ ex } ` ) ;
69+ return false ;
70+ }
71+ // No .NET Runtime, no mscorlib - need to download self-contained package.
72+ downloadPackage = true ;
7473 }
75- if ( await this . isDotNetInstalled ( ) ) {
76- this . appShell . showErrorMessage ( `.NET Runtime appears to be installed but the language server did not start. Error ${ e } ` ) ;
77- return false ;
78- }
79- // No .NET Runtime, no mscorlib - need to download self-contained package.
80- downloadPackage = true ;
8174 }
8275
8376 if ( downloadPackage ) {
@@ -88,15 +81,16 @@ export class AnalysisExtensionActivator implements IExtensionActivator {
8881 const serverModule = path . join ( context . extensionPath , analysisEngineFolder , this . platformData . getEngineExecutableName ( ) ) ;
8982 // Now try to start self-contained app
9083 this . languageClient = this . createSelfContainedLanguageClient ( context , serverModule , clientOptions ) ;
91- const error = await this . tryStartLanguageClient ( context , this . languageClient ) ;
92- if ( ! error ) {
84+ try {
85+ await this . tryStartLanguageClient ( context , this . languageClient ) ;
9386 return true ;
87+ } catch ( ex ) {
88+ this . appShell . showErrorMessage ( `Language server failed to start. Error ${ ex } ` ) ;
89+ return false ;
9490 }
95- this . appShell . showErrorMessage ( `Language server failed to start. Error ${ error } ` ) ;
96- return false ;
9791 }
9892
99- private async tryStartLanguageClient ( context : ExtensionContext , lc : LanguageClient ) : Promise < Error > {
93+ private async tryStartLanguageClient ( context : ExtensionContext , lc : LanguageClient ) : Promise < void > {
10094 let disposable : Disposable | undefined ;
10195 try {
10296 disposable = lc . start ( ) ;
@@ -106,7 +100,7 @@ export class AnalysisExtensionActivator implements IExtensionActivator {
106100 } catch ( ex ) {
107101 if ( disposable ) {
108102 disposable . dispose ( ) ;
109- return ex ;
103+ throw ex ;
110104 }
111105 }
112106 }
@@ -135,45 +129,37 @@ export class AnalysisExtensionActivator implements IExtensionActivator {
135129 const properties = new Map < string , any > ( ) ;
136130
137131 // Microsoft Python code analysis engine needs full path to the interpreter
138- const interpreterService = this . services . get < IInterpreterService > ( IInterpreterService ) ;
139- const interpreter = await interpreterService . getActiveInterpreter ( ) ;
132+ const interpreterDataService = new InterpreterDataService ( context , this . services ) ;
133+ const interpreterData = await interpreterDataService . getInterpreterData ( ) ;
134+ if ( ! interpreterData ) {
135+ const appShell = this . services . get < IApplicationShell > ( IApplicationShell ) ;
136+ appShell . showErrorMessage ( 'Unable to determine path to Python interpreter.' ) ;
137+ return ;
138+ }
140139
141- if ( interpreter ) {
142- // tslint:disable-next-line:no-string-literal
143- properties [ 'InterpreterPath' ] = interpreter . path ;
144- if ( interpreter . displayName ) {
145- // tslint:disable-next-line:no-string-literal
146- properties [ 'Description' ] = interpreter . displayName ;
140+ // tslint:disable-next-line:no-string-literal
141+ properties [ 'InterpreterPath' ] = interpreterData . path ;
142+ // tslint:disable-next-line:no-string-literal
143+ properties [ 'Version' ] = interpreterData . version ;
144+ // tslint:disable-next-line:no-string-literal
145+ properties [ 'PrefixPath' ] = interpreterData . prefix ;
146+ // tslint:disable-next-line:no-string-literal
147+ properties [ 'DatabasePath' ] = path . join ( context . extensionPath , analysisEngineFolder ) ;
148+
149+ let searchPaths = interpreterData . searchPaths ;
150+ const settings = this . configuration . getSettings ( ) ;
151+ if ( settings . autoComplete ) {
152+ const extraPaths = settings . autoComplete . extraPaths ;
153+ if ( extraPaths && extraPaths . length > 0 ) {
154+ searchPaths = `${ searchPaths } ;${ extraPaths . join ( ';' ) } ` ;
147155 }
148- const interpreterData = await this . getInterpreterData ( ) ;
149-
150- // tslint:disable-next-line:no-string-literal
151- properties [ 'Version' ] = interpreterData . version ;
152- // tslint:disable-next-line:no-string-literal
153- properties [ 'PrefixPath' ] = interpreterData . prefix ;
154- // tslint:disable-next-line:no-string-literal
155- properties [ 'DatabasePath' ] = path . join ( context . extensionPath , analysisEngineFolder ) ;
156+ }
157+ // tslint:disable-next-line:no-string-literal
158+ properties [ 'SearchPaths' ] = searchPaths ;
156159
157- let searchPaths = await this . getSearchPaths ( ) ;
158- const settings = this . configuration . getSettings ( ) ;
159- if ( settings . autoComplete ) {
160- const extraPaths = settings . autoComplete . extraPaths ;
161- if ( extraPaths && extraPaths . length > 0 ) {
162- searchPaths = `${ searchPaths } ;${ extraPaths . join ( ';' ) } ` ;
163- }
164- }
160+ if ( isTestExecution ( ) ) {
165161 // tslint:disable-next-line:no-string-literal
166- properties [ 'SearchPaths' ] = searchPaths ;
167-
168- if ( isTestExecution ( ) ) {
169- // tslint:disable-next-line:no-string-literal
170- properties [ 'TestEnvironment' ] = true ;
171- }
172- } else {
173- const appShell = this . services . get < IApplicationShell > ( IApplicationShell ) ;
174- const pythonPath = this . configuration . getSettings ( ) . pythonPath ;
175- appShell . showErrorMessage ( `Interpreter ${ pythonPath } does not exist.` ) ;
176- return ;
162+ properties [ 'TestEnvironment' ] = true ;
177163 }
178164
179165 const selector : string [ ] = [ PYTHON ] ;
@@ -188,80 +174,18 @@ export class AnalysisExtensionActivator implements IExtensionActivator {
188174 initializationOptions : {
189175 interpreter : {
190176 properties
191- }
177+ } ,
178+ displayOptions : {
179+ trimDocumentationLines : false ,
180+ maxDocumentationLineLength : 0 ,
181+ trimDocumentationText : false ,
182+ maxDocumentationTextLength : 0
183+ } ,
184+ asyncStartup : true
192185 }
193186 } ;
194187 }
195188
196- private async getInterpreterData ( ) : Promise < InterpreterData > {
197- // Not appropriate for multiroot workspaces.
198- // See https://github.com/Microsoft/vscode-python/issues/1149
199- const execService = await this . executionFactory . create ( ) ;
200- const result = await execService . exec ( [ '-c' , 'import sys; print(sys.version_info); print(sys.prefix)' ] , { } ) ;
201- // 2.7.14 (v2.7.14:84471935ed, Sep 16 2017, 20:19:30) <<SOMETIMES NEW LINE HERE>>
202- // [MSC v.1500 32 bit (Intel)]
203- // C:\Python27
204- if ( ! result . stdout ) {
205- throw Error ( 'Unable to determine Python interpreter version and system prefix.' ) ;
206- }
207- const output = result . stdout . splitLines ( { removeEmptyEntries : true , trim : true } ) ;
208- if ( ! output || output . length < 2 ) {
209- throw Error ( 'Unable to parse version and and system prefix from the Python interpreter output.' ) ;
210- }
211- const majorMatches = output [ 0 ] . match ( / m a j o r = ( \d * ?) , / ) ;
212- const minorMatches = output [ 0 ] . match ( / m i n o r = ( \d * ?) , / ) ;
213- if ( ! majorMatches || majorMatches . length < 2 || ! minorMatches || minorMatches . length < 2 ) {
214- throw Error ( 'Unable to parse interpreter version.' ) ;
215- }
216- const prefix = output [ output . length - 1 ] ;
217- return new InterpreterData ( `${ majorMatches [ 1 ] } .${ minorMatches [ 1 ] } ` , prefix ) ;
218- }
219-
220- private async getSearchPaths ( ) : Promise < string > {
221- // Not appropriate for multiroot workspaces.
222- // See https://github.com/Microsoft/vscode-python/issues/1149
223- const execService = await this . executionFactory . create ( ) ;
224- const result = await execService . exec ( [ '-c' , 'import sys; print(sys.path);' ] , { } ) ;
225- if ( ! result . stdout ) {
226- throw Error ( 'Unable to determine Python interpreter search paths.' ) ;
227- }
228- // tslint:disable-next-line:no-unnecessary-local-variable
229- const paths = result . stdout . split ( ',' )
230- . filter ( p => this . isValidPath ( p ) )
231- . map ( p => this . pathCleanup ( p ) ) ;
232- return paths . join ( ';' ) ;
233- }
234-
235- private pathCleanup ( s : string ) : string {
236- s = s . trim ( ) ;
237- if ( s [ 0 ] === '\'' ) {
238- s = s . substr ( 1 ) ;
239- }
240- if ( s [ s . length - 1 ] === ']' ) {
241- s = s . substr ( 0 , s . length - 1 ) ;
242- }
243- if ( s [ s . length - 1 ] === '\'' ) {
244- s = s . substr ( 0 , s . length - 1 ) ;
245- }
246- return s ;
247- }
248-
249- private isValidPath ( s : string ) : boolean {
250- return s . length > 0 && s [ 0 ] !== '[' ;
251- }
252-
253- // private async checkNetCoreRuntime(): Promise<boolean> {
254- // if (!await this.isDotNetInstalled()) {
255- // const appShell = this.services.get<IApplicationShell>(IApplicationShell);
256- // if (await appShell.showErrorMessage('Python Tools require .NET Core Runtime. Would you like to install it now?', 'Yes', 'No') === 'Yes') {
257- // appShell.openUrl('https://www.microsoft.com/net/download/core#/runtime');
258- // appShell.showWarningMessage('Please restart VS Code after .NET Runtime installation is complete.');
259- // }
260- // return false;
261- // }
262- // return true;
263- // }
264-
265189 private async isDotNetInstalled ( ) : Promise < boolean > {
266190 const ps = this . services . get < IProcessService > ( IProcessService ) ;
267191 const result = await ps . exec ( 'dotnet' , [ '--version' ] ) . catch ( ( ) => { return { stdout : '' } ; } ) ;
0 commit comments