@@ -6,14 +6,13 @@ import { ExtensionContext, OutputChannel } from 'vscode';
6
6
import { Disposable , LanguageClient , LanguageClientOptions , ServerOptions } from 'vscode-languageclient' ;
7
7
import { IApplicationShell } from '../common/application/types' ;
8
8
import { isTestExecution , STANDARD_OUTPUT_CHANNEL } from '../common/constants' ;
9
- import '../common/extensions' ;
10
9
import { IFileSystem , IPlatformService } from '../common/platform/types' ;
11
- import { IProcessService , IPythonExecutionFactory } from '../common/process/types' ;
10
+ import { IProcessService } from '../common/process/types' ;
12
11
import { StopWatch } from '../common/stopWatch' ;
13
12
import { IConfigurationService , IOutputChannel , IPythonSettings } from '../common/types' ;
14
- import { IInterpreterService } from '../interpreter/contracts' ;
15
13
import { IServiceContainer } from '../ioc/types' ;
16
14
import { AnalysisEngineDownloader } from './downloader' ;
15
+ import { InterpreterDataService } from './interpreterDataService' ;
17
16
import { PlatformData } from './platformData' ;
18
17
import { IExtensionActivator } from './types' ;
19
18
@@ -22,12 +21,7 @@ const dotNetCommand = 'dotnet';
22
21
const languageClientName = 'Python Tools' ;
23
22
const analysisEngineFolder = 'analysis' ;
24
23
25
- class InterpreterData {
26
- constructor ( public readonly version : string , public readonly prefix : string ) { }
27
- }
28
-
29
24
export class AnalysisExtensionActivator implements IExtensionActivator {
30
- private readonly executionFactory : IPythonExecutionFactory ;
31
25
private readonly configuration : IConfigurationService ;
32
26
private readonly appShell : IApplicationShell ;
33
27
private readonly output : OutputChannel ;
@@ -37,7 +31,6 @@ export class AnalysisExtensionActivator implements IExtensionActivator {
37
31
private languageClient : LanguageClient | undefined ;
38
32
39
33
constructor ( private readonly services : IServiceContainer , pythonSettings : IPythonSettings ) {
40
- this . executionFactory = this . services . get < IPythonExecutionFactory > ( IPythonExecutionFactory ) ;
41
34
this . configuration = this . services . get < IConfigurationService > ( IConfigurationService ) ;
42
35
this . appShell = this . services . get < IApplicationShell > ( IApplicationShell ) ;
43
36
this . output = this . services . get < OutputChannel > ( IOutputChannel , STANDARD_OUTPUT_CHANNEL ) ;
@@ -50,7 +43,6 @@ export class AnalysisExtensionActivator implements IExtensionActivator {
50
43
if ( ! clientOptions ) {
51
44
return false ;
52
45
}
53
- this . output . appendLine ( `Options determined: ${ this . sw . elapsedTime } ms` ) ;
54
46
return this . startLanguageServer ( context , clientOptions ) ;
55
47
}
56
48
@@ -68,16 +60,17 @@ export class AnalysisExtensionActivator implements IExtensionActivator {
68
60
if ( ! await this . fs . fileExistsAsync ( mscorlib ) ) {
69
61
// Depends on .NET Runtime or SDK
70
62
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 ) ;
73
65
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 ;
74
73
}
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 ;
81
74
}
82
75
83
76
if ( downloadPackage ) {
@@ -88,15 +81,16 @@ export class AnalysisExtensionActivator implements IExtensionActivator {
88
81
const serverModule = path . join ( context . extensionPath , analysisEngineFolder , this . platformData . getEngineExecutableName ( ) ) ;
89
82
// Now try to start self-contained app
90
83
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 ) ;
93
86
return true ;
87
+ } catch ( ex ) {
88
+ this . appShell . showErrorMessage ( `Language server failed to start. Error ${ ex } ` ) ;
89
+ return false ;
94
90
}
95
- this . appShell . showErrorMessage ( `Language server failed to start. Error ${ error } ` ) ;
96
- return false ;
97
91
}
98
92
99
- private async tryStartLanguageClient ( context : ExtensionContext , lc : LanguageClient ) : Promise < Error > {
93
+ private async tryStartLanguageClient ( context : ExtensionContext , lc : LanguageClient ) : Promise < void > {
100
94
let disposable : Disposable | undefined ;
101
95
try {
102
96
disposable = lc . start ( ) ;
@@ -106,7 +100,7 @@ export class AnalysisExtensionActivator implements IExtensionActivator {
106
100
} catch ( ex ) {
107
101
if ( disposable ) {
108
102
disposable . dispose ( ) ;
109
- return ex ;
103
+ throw ex ;
110
104
}
111
105
}
112
106
}
@@ -135,45 +129,37 @@ export class AnalysisExtensionActivator implements IExtensionActivator {
135
129
const properties = new Map < string , any > ( ) ;
136
130
137
131
// 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
+ }
140
139
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 ( ';' ) } ` ;
147
155
}
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 ;
156
159
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 ( ) ) {
165
161
// 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 ;
177
163
}
178
164
179
165
const selector : string [ ] = [ PYTHON ] ;
@@ -188,80 +174,18 @@ export class AnalysisExtensionActivator implements IExtensionActivator {
188
174
initializationOptions : {
189
175
interpreter : {
190
176
properties
191
- }
177
+ } ,
178
+ displayOptions : {
179
+ trimDocumentationLines : false ,
180
+ maxDocumentationLineLength : 0 ,
181
+ trimDocumentationText : false ,
182
+ maxDocumentationTextLength : 0
183
+ } ,
184
+ asyncStartup : true
192
185
}
193
186
} ;
194
187
}
195
188
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
-
265
189
private async isDotNetInstalled ( ) : Promise < boolean > {
266
190
const ps = this . services . get < IProcessService > ( IProcessService ) ;
267
191
const result = await ps . exec ( 'dotnet' , [ '--version' ] ) . catch ( ( ) => { return { stdout : '' } ; } ) ;
0 commit comments