Skip to content

Commit d213a79

Browse files
author
Mikhail Arkhipov
authored
Language server startup time improvement (#1299)
* Undo changes * Test fixes * Increase timeout * Remove double event listening * Remove test * Revert "Remove test" This reverts commit e240c3f. * Revert "Remove double event listening" This reverts commit af573be. * Undo changes * Test fixes * .NET Core check * Better find dotnet * Fix pip test * Linting tests * Undo accidental changes * Add clone and build PTVS * Appveyor PTVS build * Fix slashes * Enable build * Try absolute path * Fix xcopy switch * Activate Analysis Engine test on Appveyor * Temporary only run new tests * Disable PEP hint tests * Test fix * Disable appveyor build and tests for PTVS for now * Remove analysis engine test from the set * Remove VS image for now * Build/sign VSXI project * Run vsce from cmd * Rename * Abs path vsce * Path * Move project * Ignore publishing project * Try csproj * Add framework * Ignore build output folder * Package before build * Try batch instead of PS * Fix path quotes * #1096 The if statement is automatically formatted incorrectly * Merge fix * Add more tests * More tests * Typo * Test * Also better handle multiline arguments * Changes lost on squash * More lost changes * Restore Jedi/PTVS setting * Update tests to new PTVS * Signature tests * Add PTVS tests task * Analysis Engine contribution * Add Mac/Linux info * Disable csproj build * Add unzip to dependencies * Minor fixes to doc * Change setting type to bool * Report progress on status bar * Simplify * CR feedback * Fix launching fx-independent code on Mac/Linux * Add title * PTVS startup time * PTVS startup time * Remove test * Revert "Remove test" This reverts commit e240c3f. * Undo changes * Test fixes * Merge master * Remove unused code * Add clone and build PTVS * Fix slashes * Disable PEP hint tests * Test fix * Remove analysis engine test from the set * Build/sign VSXI project * Run vsce from cmd * Rename * Abs path vsce * Path * Move project * Ignore publishing project * Try csproj * Add framework * Ignore build output folder * Package before build * More lost changes * Change setting type to bool * Merge issues * Merge issues * Merge issues * Check search paths only if using cache * Undo change * PR feedback * Add async startup to PTVS
1 parent 2fd7b69 commit d213a79

File tree

5 files changed

+201
-160
lines changed

5 files changed

+201
-160
lines changed

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1907,4 +1907,4 @@
19071907
"publisherDisplayName": "Microsoft",
19081908
"publisherId": "998b010b-e2af-44a5-a6cd-0b5fd3b9b6f8"
19091909
}
1910-
}
1910+
}

src/client/activation/analysis.ts

+53-129
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,13 @@ import { ExtensionContext, OutputChannel } from 'vscode';
66
import { Disposable, LanguageClient, LanguageClientOptions, ServerOptions } from 'vscode-languageclient';
77
import { IApplicationShell } from '../common/application/types';
88
import { isTestExecution, STANDARD_OUTPUT_CHANNEL } from '../common/constants';
9-
import '../common/extensions';
109
import { IFileSystem, IPlatformService } from '../common/platform/types';
11-
import { IProcessService, IPythonExecutionFactory } from '../common/process/types';
10+
import { IProcessService } from '../common/process/types';
1211
import { StopWatch } from '../common/stopWatch';
1312
import { IConfigurationService, IOutputChannel, IPythonSettings } from '../common/types';
14-
import { IInterpreterService } from '../interpreter/contracts';
1513
import { IServiceContainer } from '../ioc/types';
1614
import { AnalysisEngineDownloader } from './downloader';
15+
import { InterpreterDataService } from './interpreterDataService';
1716
import { PlatformData } from './platformData';
1817
import { IExtensionActivator } from './types';
1918

@@ -22,12 +21,7 @@ const dotNetCommand = 'dotnet';
2221
const languageClientName = 'Python Tools';
2322
const analysisEngineFolder = 'analysis';
2423

25-
class InterpreterData {
26-
constructor(public readonly version: string, public readonly prefix: string) { }
27-
}
28-
2924
export 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(/major=(\d*?),/);
212-
const minorMatches = output[0].match(/minor=(\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

Comments
 (0)