diff --git a/src/client/activation/analysis.ts b/src/client/activation/analysis.ts index b628159409e6..522722b39a69 100644 --- a/src/client/activation/analysis.ts +++ b/src/client/activation/analysis.ts @@ -4,11 +4,9 @@ import { inject, injectable } from 'inversify'; import * as path from 'path'; import { ExtensionContext, OutputChannel } from 'vscode'; -import { Message } from 'vscode-jsonrpc'; -import { CloseAction, Disposable, ErrorAction, ErrorHandler, LanguageClient, LanguageClientOptions, ServerOptions } from 'vscode-languageclient'; +import { Disposable, LanguageClient, LanguageClientOptions, ServerOptions } from 'vscode-languageclient'; import { IApplicationShell } from '../common/application/types'; import { isTestExecution, STANDARD_OUTPUT_CHANNEL } from '../common/constants'; -import { createDeferred, Deferred } from '../common/helpers'; import { IFileSystem, IPlatformService } from '../common/platform/types'; import { StopWatch } from '../common/stopWatch'; import { IConfigurationService, IExtensionContext, IOutputChannel } from '../common/types'; @@ -18,8 +16,7 @@ import { IServiceContainer } from '../ioc/types'; import { PYTHON_ANALYSIS_ENGINE_DOWNLOADED, PYTHON_ANALYSIS_ENGINE_ENABLED, - PYTHON_ANALYSIS_ENGINE_ERROR, - PYTHON_ANALYSIS_ENGINE_STARTUP + PYTHON_ANALYSIS_ENGINE_ERROR } from '../telemetry/constants'; import { getTelemetryReporter } from '../telemetry/telemetry'; import { AnalysisEngineDownloader } from './downloader'; @@ -32,18 +29,6 @@ const dotNetCommand = 'dotnet'; const languageClientName = 'Python Tools'; const analysisEngineFolder = 'analysis'; -class LanguageServerStartupErrorHandler implements ErrorHandler { - constructor(private readonly deferred: Deferred) { } - public error(error: Error, message: Message, count: number): ErrorAction { - this.deferred.reject(error); - return ErrorAction.Continue; - } - public closed(): CloseAction { - this.deferred.reject(); - return CloseAction.Restart; - } -} - @injectable() export class AnalysisExtensionActivator implements IExtensionActivator { private readonly configuration: IConfigurationService; @@ -102,8 +87,6 @@ export class AnalysisExtensionActivator implements IExtensionActivator { private async startLanguageServer(context: ExtensionContext, clientOptions: LanguageClientOptions): Promise { // Determine if we are running MSIL/Universal via dotnet or self-contained app. - const mscorlib = path.join(context.extensionPath, analysisEngineFolder, 'mscorlib.dll'); - const downloader = new AnalysisEngineDownloader(this.services, analysisEngineFolder); const reporter = getTelemetryReporter(); reporter.sendTelemetryEvent(PYTHON_ANALYSIS_ENGINE_ENABLED); @@ -112,20 +95,21 @@ export class AnalysisExtensionActivator implements IExtensionActivator { if (!settings.downloadCodeAnalysis) { // Depends on .NET Runtime or SDK. Typically development-only case. this.languageClient = this.createSimpleLanguageClient(context, clientOptions); - await this.tryStartLanguageClient(context, this.languageClient); + await this.startLanguageClient(context); return true; } + const mscorlib = path.join(context.extensionPath, analysisEngineFolder, 'mscorlib.dll'); if (!await this.fs.fileExists(mscorlib)) { + const downloader = new AnalysisEngineDownloader(this.services, analysisEngineFolder); await downloader.downloadAnalysisEngine(context); reporter.sendTelemetryEvent(PYTHON_ANALYSIS_ENGINE_DOWNLOADED); } const serverModule = path.join(context.extensionPath, analysisEngineFolder, this.platformData.getEngineExecutableName()); - // Now try to start self-contained app this.languageClient = this.createSelfContainedLanguageClient(context, serverModule, clientOptions); try { - await this.tryStartLanguageClient(context, this.languageClient); + await this.startLanguageClient(context); return true; } catch (ex) { this.appShell.showErrorMessage(`Language server failed to start. Error ${ex}`); @@ -134,31 +118,10 @@ export class AnalysisExtensionActivator implements IExtensionActivator { } } - private async tryStartLanguageClient(context: ExtensionContext, lc: LanguageClient): Promise { - let disposable: Disposable | undefined; - const deferred = createDeferred(); - try { - const sw = new StopWatch(); - lc.clientOptions.errorHandler = new LanguageServerStartupErrorHandler(deferred); - - disposable = lc.start(); - lc.onReady() - .then(() => deferred.resolve()) - .catch((reason) => { - deferred.reject(reason); - }); - await deferred.promise; - - this.output.appendLine(`Language server ready: ${this.sw.elapsedTime} ms`); - context.subscriptions.push(disposable); - - const reporter = getTelemetryReporter(); - reporter.sendTelemetryEvent(PYTHON_ANALYSIS_ENGINE_STARTUP, {}, { startup_time: sw.elapsedTime }); - } catch (ex) { - if (disposable) { - disposable.dispose(); - } - throw ex; + private async startLanguageClient(context: ExtensionContext): Promise { + context.subscriptions.push(this.languageClient!.start()); + if (isTestExecution()) { + await this.languageClient!.onReady(); } } diff --git a/src/test/definitions/hover.ptvs.test.ts b/src/test/definitions/hover.ptvs.test.ts index 089245836090..cb297e0e9374 100644 --- a/src/test/definitions/hover.ptvs.test.ts +++ b/src/test/definitions/hover.ptvs.test.ts @@ -53,7 +53,7 @@ suite('Hover Definition (Analysis Engine)', () => { const expected = [ 'obj.method1:', '```python', - 'method method1 of one.Class1 objects', + 'method method1 of pythonFiles.autocomp.one.Class1 objects', '```', 'This is method1' ]; @@ -70,7 +70,7 @@ suite('Hover Definition (Analysis Engine)', () => { const expected = [ 'two.ct().fun:', '```python', - 'method fun of two.ct objects', + 'method fun of pythonFiles.autocomp.two.ct objects', '```', 'This is fun' ]; @@ -86,7 +86,7 @@ suite('Hover Definition (Analysis Engine)', () => { const actual = normalizeMarkedString(def[0].contents[0]).splitLines(); const expected = [ '```python', - 'four.Foo.bar() -> bool', + 'pythonFiles.autocomp.four.Foo.bar() -> bool', 'declared in Foo', '```', '说明 - keep this line, it works', @@ -105,7 +105,7 @@ suite('Hover Definition (Analysis Engine)', () => { const actual = normalizeMarkedString(def[0].contents[0]).splitLines(); const expected = [ '```python', - 'four.showMessage()', + 'pythonFiles.autocomp.four.showMessage()', '```', 'Кюм ут жэмпэр пошжим льаборэж, коммюны янтэрэсщэт нам ед, декта игнота ныморэ жят эи.', 'Шэа декам экшырки эи, эи зыд эррэм докэндё, векж факэтэ пэрчыквюэрёж ку.' @@ -138,7 +138,7 @@ suite('Hover Definition (Analysis Engine)', () => { const actual = normalizeMarkedString(def[0].contents[0]).splitLines(); const expected = [ '```python', - 'class misc.Random(_random.Random)', + 'class pythonFiles.autocomp.misc.Random(_random.Random)', '```', 'Random number generator base class used by bound module functions.', 'Used to instantiate instances of Random to get generators that don\'t', @@ -162,7 +162,7 @@ suite('Hover Definition (Analysis Engine)', () => { const expected = [ 'rnd2.randint:', '```python', - 'method randint of misc.Random objects -> int', + 'method randint of pythonFiles.autocomp.misc.Random objects -> int', '```', 'Return random integer in range [a, b], including both end points.' ]; @@ -195,7 +195,7 @@ suite('Hover Definition (Analysis Engine)', () => { const actual = normalizeMarkedString(def[0].contents[0]).splitLines(); const expected = [ '```python', - 'class misc.Thread(_Verbose)', + 'class pythonFiles.autocomp.misc.Thread(_Verbose)', '```', 'A class that represents a thread of control.', 'This class can be safely subclassed in a limited fashion.'