Skip to content

Commit

Permalink
first draft for pylance client move
Browse files Browse the repository at this point in the history
  • Loading branch information
heejaechang committed Mar 8, 2023
1 parent e9ff78d commit a114bcc
Show file tree
Hide file tree
Showing 6 changed files with 89 additions and 12 deletions.
4 changes: 2 additions & 2 deletions src/client/activation/node/languageClientFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { PythonEnvironment } from '../../pythonEnvironments/info';
import { FileBasedCancellationStrategy } from '../common/cancellationUtils';
import { ILanguageClientFactory } from '../types';

const languageClientName = 'Pylance';
export const PYLANCE_NAME = 'Pylance';

export class NodeLanguageClientFactory implements ILanguageClientFactory {
constructor(private readonly fs: IFileSystem, private readonly extensions: IExtensions) {}
Expand Down Expand Up @@ -50,6 +50,6 @@ export class NodeLanguageClientFactory implements ILanguageClientFactory {
},
};

return new LanguageClient(PYTHON_LANGUAGE, languageClientName, serverOptions, clientOptions);
return new LanguageClient(PYTHON_LANGUAGE, PYLANCE_NAME, serverOptions, clientOptions);
}
}
31 changes: 30 additions & 1 deletion src/client/activation/node/languageServerProxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { ILanguageClientFactory, ILanguageServerProxy } from '../types';
import { traceDecoratorError, traceDecoratorVerbose, traceError } from '../../logging';
import { IWorkspaceService } from '../../common/application/types';
import { PYLANCE_EXTENSION_ID } from '../../common/constants';
import { PylanceApi } from './pylanceApi';

// eslint-disable-next-line @typescript-eslint/no-namespace
namespace InExperiment {
Expand Down Expand Up @@ -56,6 +57,8 @@ export class NodeLanguageServerProxy implements ILanguageServerProxy {

private lsVersion: string | undefined;

private pylanceApi: PylanceApi | undefined;

constructor(
private readonly factory: ILanguageClientFactory,
private readonly experimentService: IExperimentService,
Expand Down Expand Up @@ -89,9 +92,16 @@ export class NodeLanguageServerProxy implements ILanguageServerProxy {
interpreter: PythonEnvironment | undefined,
options: LanguageClientOptions,
): Promise<void> {
const extension = this.extensions.getExtension(PYLANCE_EXTENSION_ID);
const extension = await this.getPylanceExtension();
this.lsVersion = extension?.packageJSON.version || '0';

const api = extension?.exports as PylanceApi | undefined;
if (api && api.client && api.client.isEnabled()) {
this.pylanceApi = api;
await api.client.start();
return;
}

this.cancellationStrategy = new FileBasedCancellationStrategy();
options.connectionOptions = { cancellationStrategy: this.cancellationStrategy };

Expand All @@ -111,6 +121,12 @@ export class NodeLanguageServerProxy implements ILanguageServerProxy {

@traceDecoratorVerbose('Disposing language server')
public async stop(): Promise<void> {
if (this.pylanceApi) {
const api = this.pylanceApi;
this.pylanceApi = undefined;
await api.client!.stop();
}

while (this.disposables.length > 0) {
const d = this.disposables.shift()!;
d.dispose();
Expand Down Expand Up @@ -203,4 +219,17 @@ export class NodeLanguageServerProxy implements ILanguageServerProxy {
})),
);
}

private async getPylanceExtension() {
const extension = this.extensions.getExtension(PYLANCE_EXTENSION_ID);
if (!extension) {
return undefined;
}

if (!extension.isActive) {
await extension.activate();
}

return extension;
}
}
10 changes: 10 additions & 0 deletions src/client/activation/node/pylanceApi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

export interface PylanceApi {
client?: {
isEnabled(): boolean;
start(): Promise<void>;
stop(): Promise<void>;
};
}
12 changes: 11 additions & 1 deletion src/client/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@

import { noop } from 'lodash';
import { Uri, Event } from 'vscode';
import { BaseLanguageClient } from 'vscode-languageclient';
import { LanguageClient } from 'vscode-languageclient/node';
import { PYLANCE_NAME } from './activation/node/languageClientFactory';
import { IExtensionApi } from './apiTypes';
import { isTestExecution } from './common/constants';
import { isTestExecution, PYTHON_LANGUAGE } from './common/constants';
import { IConfigurationService, Resource } from './common/types';
import { IEnvironmentVariablesProvider } from './common/variables/types';
import { getDebugpyLauncherArgs, getDebugpyPackagePath } from './debugger/extension/adapter/remoteLaunchers';
Expand All @@ -33,6 +36,9 @@ export function buildApi(
pylance: {
getPythonPathVar: (resource?: Uri) => Promise<string | undefined>;
readonly onDidEnvironmentVariablesChange: Event<Uri | undefined>;
createClient(...args: any[]): BaseLanguageClient;
start(client: BaseLanguageClient): Promise<void>;
stop(client: BaseLanguageClient): Promise<void>;
};
} = {
// 'ready' will propagate the exception, but we must log it here first.
Expand Down Expand Up @@ -83,6 +89,10 @@ export function buildApi(
return envs.PYTHONPATH;
},
onDidEnvironmentVariablesChange: envService.onDidEnvironmentVariablesChange,
createClient: (...args: any[]): BaseLanguageClient =>
new LanguageClient(PYTHON_LANGUAGE, PYLANCE_NAME, args[0], args[1]),
start: (client: BaseLanguageClient): Promise<void> => client.start(),
stop: (client: BaseLanguageClient): Promise<void> => client.stop(),
},
};

Expand Down
38 changes: 32 additions & 6 deletions src/client/browser/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@ import { LanguageServerType } from '../activation/types';
import { AppinsightsKey, PYLANCE_EXTENSION_ID } from '../common/constants';
import { EventName } from '../telemetry/constants';
import { createStatusItem } from './intellisenseStatus';
import { PylanceApi } from '../activation/node/pylanceApi';

interface BrowserConfig {
distUrl: string; // URL to Pylance's dist folder.
}

let languageClient: LanguageClient | undefined;
let pylanceApi: PylanceApi | undefined;

export async function activate(context: vscode.ExtensionContext): Promise<void> {
const pylanceExtension = vscode.extensions.getExtension(PYLANCE_EXTENSION_ID);
Expand All @@ -34,17 +36,35 @@ export async function activate(context: vscode.ExtensionContext): Promise<void>
}

export async function deactivate(): Promise<void> {
const client = languageClient;
languageClient = undefined;
if (pylanceApi) {
const api = pylanceApi;
pylanceApi = undefined;
await api.client!.stop();
}

if (languageClient) {
const client = languageClient;
languageClient = undefined;

await client?.stop();
await client?.dispose();
await client.stop();
await client.dispose();
}
}

async function runPylance(
context: vscode.ExtensionContext,
pylanceExtension: vscode.Extension<unknown>,
): Promise<void> {
context.subscriptions.push(createStatusItem());

pylanceExtension = await getActivatedExtension(pylanceExtension);
const api = pylanceExtension.exports as PylanceApi;
if (api.client && api.client.isEnabled()) {
pylanceApi = api;
await api.client.start();
return;
}

const { extensionUri, packageJSON } = pylanceExtension;
const distUrl = vscode.Uri.joinPath(extensionUri, 'dist');

Expand Down Expand Up @@ -111,8 +131,6 @@ async function runPylance(
);

await client.start();

context.subscriptions.push(createStatusItem());
} catch (e) {
console.log(e);
}
Expand Down Expand Up @@ -196,3 +214,11 @@ function sendTelemetryEventBrowser(
reporter.sendTelemetryEvent(eventNameSent, customProperties, measures);
}
}

async function getActivatedExtension<T>(extension: vscode.Extension<T>): Promise<vscode.Extension<T>> {
if (!extension.isActive) {
await extension.activate();
}

return extension;
}
6 changes: 4 additions & 2 deletions src/client/languageServer/watcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -262,11 +262,11 @@ export class LanguageServerWatcher implements IExtensionActivationService, ILang
return lsManager;
}

private async refreshLanguageServer(resource?: Resource): Promise<void> {
private async refreshLanguageServer(resource?: Resource, forced?: boolean): Promise<void> {
const lsResource = this.getWorkspaceUri(resource);
const languageServerType = this.configurationService.getSettings(lsResource).languageServer;

if (languageServerType !== this.languageServerType) {
if (languageServerType !== this.languageServerType || forced) {
await this.stopLanguageServer(resource);
await this.startLanguageServer(languageServerType, lsResource);
}
Expand All @@ -283,6 +283,8 @@ export class LanguageServerWatcher implements IExtensionActivationService, ILang
workspacesUris.forEach(async (resource) => {
if (event.affectsConfiguration(`python.languageServer`, resource)) {
await this.refreshLanguageServer(resource);
} else if (event.affectsConfiguration(`python.analysis.pylanceLspClientEnabled`, resource)) {
await this.refreshLanguageServer(resource, /* forced */ true);
}
});
}
Expand Down

0 comments on commit a114bcc

Please sign in to comment.