Skip to content

Commit 3164b3e

Browse files
authored
move LSP to the latest version (#19017)
* move LSP to the latest version * address PR feedbacks * try fix vscode test mock make integration tests to use insiders * give it longer timeout * disable 2 tests * update lsp to latest * skip definition test for python 2.7
1 parent 34da7cd commit 3164b3e

15 files changed

Lines changed: 236 additions & 265 deletions

package-lock.json

Lines changed: 79 additions & 79 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
"theme": "dark"
4343
},
4444
"engines": {
45-
"vscode": "^1.65.0"
45+
"vscode": "^1.67.0-insider"
4646
},
4747
"keywords": [
4848
"python",
@@ -1828,7 +1828,7 @@
18281828
"webpack": "webpack"
18291829
},
18301830
"dependencies": {
1831-
"@vscode/jupyter-lsp-middleware": "^0.2.41",
1831+
"@vscode/jupyter-lsp-middleware": "^0.2.42",
18321832
"arch": "^2.1.0",
18331833
"diff-match-patch": "^1.0.0",
18341834
"fs-extra": "^10.0.1",
@@ -1856,10 +1856,10 @@
18561856
"vscode-debugadapter": "^1.28.0",
18571857
"vscode-debugprotocol": "^1.28.0",
18581858
"vscode-extension-telemetry": "0.4.5",
1859-
"vscode-jsonrpc": "8.0.0-next.7",
1860-
"vscode-languageclient": "8.0.0-next.13",
1861-
"vscode-languageserver": "8.0.0-next.9",
1862-
"vscode-languageserver-protocol": "3.17.0-next.15",
1859+
"vscode-jsonrpc": "8.0.0-next.8",
1860+
"vscode-languageclient": "8.0.0-next.18",
1861+
"vscode-languageserver": "8.0.0-next.14",
1862+
"vscode-languageserver-protocol": "3.17.0-next.20",
18631863
"vscode-tas-client": "^0.1.22",
18641864
"winreg": "^1.2.4",
18651865
"xml2js": "^0.4.19"
@@ -1884,7 +1884,7 @@
18841884
"@types/sinon": "^10.0.11",
18851885
"@types/tmp": "^0.0.33",
18861886
"@types/uuid": "^8.3.4",
1887-
"@types/vscode": "~1.65.0",
1887+
"@types/vscode": "~1.66.0",
18881888
"@types/winreg": "^1.2.30",
18891889
"@types/xml2js": "^0.4.2",
18901890
"@typescript-eslint/eslint-plugin": "^3.7.0",

src/client/activation/jedi/languageServerProxy.ts

Lines changed: 10 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,13 @@ import {
77
Disposable,
88
LanguageClient,
99
LanguageClientOptions,
10-
State,
1110
} from 'vscode-languageclient/node';
1211

1312
import { ChildProcess } from 'child_process';
1413
import { IInterpreterPathService, Resource } from '../../common/types';
1514
import { PythonEnvironment } from '../../pythonEnvironments/info';
1615
import { captureTelemetry } from '../../telemetry';
1716
import { EventName } from '../../telemetry/constants';
18-
import { FileBasedCancellationStrategy } from '../common/cancellationUtils';
1917
import { LanguageClientMiddleware } from '../languageClientMiddleware';
2018
import { ProgressReporting } from '../progress';
2119
import { ILanguageClientFactory, ILanguageServerProxy } from '../types';
@@ -25,7 +23,7 @@ import { traceDecoratorError, traceDecoratorVerbose, traceError } from '../../lo
2523
export class JediLanguageServerProxy implements ILanguageServerProxy {
2624
public languageClient: LanguageClient | undefined;
2725

28-
private cancellationStrategy: FileBasedCancellationStrategy | undefined;
26+
private languageServerTask: Promise<void> | undefined;
2927

3028
private readonly disposables: Disposable[] = [];
3129

@@ -54,6 +52,7 @@ export class JediLanguageServerProxy implements ILanguageServerProxy {
5452
killPid(pid);
5553
}
5654
};
55+
5756
// Do not await on this.
5857
this.languageClient.stop().then(
5958
() => killServer(),
@@ -62,12 +61,9 @@ export class JediLanguageServerProxy implements ILanguageServerProxy {
6261
killServer();
6362
},
6463
);
65-
this.languageClient = undefined;
66-
}
6764

68-
if (this.cancellationStrategy) {
69-
this.cancellationStrategy.dispose();
70-
this.cancellationStrategy = undefined;
65+
this.languageClient = undefined;
66+
this.languageServerTask = undefined;
7167
}
7268

7369
while (this.disposables.length > 0) {
@@ -91,31 +87,19 @@ export class JediLanguageServerProxy implements ILanguageServerProxy {
9187
interpreter: PythonEnvironment | undefined,
9288
options: LanguageClientOptions,
9389
): Promise<void> {
94-
if (this.languageClient) {
95-
return this.serverReady();
90+
if (this.languageServerTask) {
91+
await this.languageServerTask;
92+
return;
9693
}
9794

9895
this.lsVersion =
9996
(options.middleware ? (<LanguageClientMiddleware>options.middleware).serverVersion : undefined) ?? '0.19.3';
10097

101-
this.cancellationStrategy = new FileBasedCancellationStrategy();
102-
options.connectionOptions = { cancellationStrategy: this.cancellationStrategy };
103-
10498
this.languageClient = await this.factory.createLanguageClient(resource, interpreter, options);
99+
this.registerHandlers();
105100

106-
this.languageClient.onDidChangeState((e) => {
107-
// The client's on* methods must be called after the client has started, but if called too
108-
// late the server may have already sent a message (which leads to failures). Register
109-
// these on the state change to running to ensure they are ready soon enough.
110-
if (e.newState === State.Running) {
111-
this.registerHandlers();
112-
}
113-
});
114-
115-
this.disposables.push(this.languageClient.start());
116-
await this.serverReady();
117-
118-
return Promise.resolve();
101+
this.languageServerTask = this.languageClient.start();
102+
await this.languageServerTask;
119103
}
120104

121105
// eslint-disable-next-line class-methods-use-this
@@ -130,12 +114,6 @@ export class JediLanguageServerProxy implements ILanguageServerProxy {
130114
undefined,
131115
JediLanguageServerProxy.versionTelemetryProps,
132116
)
133-
protected async serverReady(): Promise<void> {
134-
if (this.languageClient) {
135-
await this.languageClient.onReady();
136-
}
137-
}
138-
139117
private registerHandlers() {
140118
if (this.disposed) {
141119
// Check if it got disposed in the interim.

src/client/activation/node/languageServerProxy.ts

Lines changed: 25 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,9 @@ import {
77
Disposable,
88
LanguageClient,
99
LanguageClientOptions,
10-
State,
1110
} from 'vscode-languageclient/node';
1211

1312
import { IExperimentService, IExtensions, IInterpreterPathService, Resource } from '../../common/types';
14-
import { createDeferred, Deferred, sleep } from '../../common/utils/async';
1513
import { noop } from '../../common/utils/misc';
1614
import { IEnvironmentVariablesProvider } from '../../common/variables/types';
1715
import { PythonEnvironment } from '../../pythonEnvironments/info';
@@ -53,7 +51,7 @@ namespace GetExperimentValue {
5351
export class NodeLanguageServerProxy implements ILanguageServerProxy {
5452
public languageClient: LanguageClient | undefined;
5553

56-
private startupCompleted: Deferred<void>;
54+
private languageServerTask: Promise<void> | undefined;
5755

5856
private cancellationStrategy: FileBasedCancellationStrategy | undefined;
5957

@@ -70,9 +68,7 @@ export class NodeLanguageServerProxy implements ILanguageServerProxy {
7068
private readonly environmentService: IEnvironmentVariablesProvider,
7169
private readonly workspace: IWorkspaceService,
7270
private readonly extensions: IExtensions,
73-
) {
74-
this.startupCompleted = createDeferred<void>();
75-
}
71+
) {}
7672

7773
private static versionTelemetryProps(instance: NodeLanguageServerProxy) {
7874
return {
@@ -85,7 +81,9 @@ export class NodeLanguageServerProxy implements ILanguageServerProxy {
8581
if (this.languageClient) {
8682
// Do not await on this.
8783
this.languageClient.stop().then(noop, (ex) => traceError('Stopping language client failed', ex));
84+
8885
this.languageClient = undefined;
86+
this.languageServerTask = undefined;
8987
}
9088
if (this.cancellationStrategy) {
9189
this.cancellationStrategy.dispose();
@@ -95,10 +93,6 @@ export class NodeLanguageServerProxy implements ILanguageServerProxy {
9593
const d = this.disposables.shift()!;
9694
d.dispose();
9795
}
98-
if (this.startupCompleted.completed) {
99-
this.startupCompleted.reject(new Error('Disposed language server'));
100-
this.startupCompleted = createDeferred<void>();
101-
}
10296
this.disposed = true;
10397
}
10498

@@ -115,41 +109,28 @@ export class NodeLanguageServerProxy implements ILanguageServerProxy {
115109
interpreter: PythonEnvironment | undefined,
116110
options: LanguageClientOptions,
117111
): Promise<void> {
118-
if (!this.languageClient) {
119-
const extension = this.extensions.getExtension(PYLANCE_EXTENSION_ID);
120-
this.lsVersion = extension?.packageJSON.version || '0';
121-
122-
this.cancellationStrategy = new FileBasedCancellationStrategy();
123-
options.connectionOptions = { cancellationStrategy: this.cancellationStrategy };
124-
125-
this.languageClient = await this.factory.createLanguageClient(resource, interpreter, options);
126-
127-
this.languageClient.onDidChangeState((e) => {
128-
// The client's on* methods must be called after the client has started, but if called too
129-
// late the server may have already sent a message (which leads to failures). Register
130-
// these on the state change to running to ensure they are ready soon enough.
131-
if (e.newState === State.Running) {
132-
this.registerHandlers(resource);
133-
}
134-
});
135-
136-
this.disposables.push(
137-
this.workspace.onDidGrantWorkspaceTrust(() => {
138-
this.languageClient!.onReady().then(() => {
139-
this.languageClient!.sendNotification('python/workspaceTrusted', { isTrusted: true });
140-
});
141-
}),
142-
);
143-
144-
this.disposables.push(this.languageClient.start());
145-
await this.serverReady();
146-
147-
if (this.disposed) {
148-
// Check if it got disposed in the interim.
149-
}
150-
} else {
151-
await this.startupCompleted.promise;
112+
if (this.languageServerTask) {
113+
await this.languageServerTask;
114+
return;
152115
}
116+
117+
const extension = this.extensions.getExtension(PYLANCE_EXTENSION_ID);
118+
this.lsVersion = extension?.packageJSON.version || '0';
119+
120+
this.cancellationStrategy = new FileBasedCancellationStrategy();
121+
options.connectionOptions = { cancellationStrategy: this.cancellationStrategy };
122+
123+
this.languageClient = await this.factory.createLanguageClient(resource, interpreter, options);
124+
this.registerHandlers(resource);
125+
126+
this.disposables.push(
127+
this.workspace.onDidGrantWorkspaceTrust(() => {
128+
this.languageClient!.sendNotification('python/workspaceTrusted', { isTrusted: true });
129+
}),
130+
);
131+
132+
this.languageServerTask = this.languageClient.start();
133+
await this.languageServerTask;
153134
}
154135

155136
// eslint-disable-next-line class-methods-use-this
@@ -164,16 +145,6 @@ export class NodeLanguageServerProxy implements ILanguageServerProxy {
164145
undefined,
165146
NodeLanguageServerProxy.versionTelemetryProps,
166147
)
167-
protected async serverReady(): Promise<void> {
168-
while (this.languageClient && !this.languageClient.initializeResult) {
169-
await sleep(100);
170-
}
171-
if (this.languageClient) {
172-
await this.languageClient.onReady();
173-
}
174-
this.startupCompleted.resolve();
175-
}
176-
177148
private registerHandlers(_resource: Resource) {
178149
if (this.disposed) {
179150
// Check if it got disposed in the interim.

src/client/browser/extension.ts

Lines changed: 40 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
import * as vscode from 'vscode';
55
import TelemetryReporter from 'vscode-extension-telemetry';
6-
import { LanguageClientOptions, State } from 'vscode-languageclient';
6+
import { LanguageClientOptions } from 'vscode-languageclient';
77
import { LanguageClient } from 'vscode-languageclient/browser';
88
import { LanguageClientMiddlewareBase } from '../activation/languageClientMiddlewareBase';
99
import { LanguageServerType } from '../activation/types';
@@ -16,24 +16,33 @@ interface BrowserConfig {
1616
distUrl: string; // URL to Pylance's dist folder.
1717
}
1818

19+
let languageClient: LanguageClient | undefined;
20+
1921
export async function activate(context: vscode.ExtensionContext): Promise<void> {
2022
// Run in a promise and return early so that VS Code can go activate Pylance.
2123
await loadLocalizedStringsForBrowser();
2224
const pylanceExtension = vscode.extensions.getExtension(PYLANCE_EXTENSION_ID);
2325
if (pylanceExtension) {
24-
runPylance(context, pylanceExtension);
26+
await runPylance(context, pylanceExtension);
2527
return;
2628
}
2729

28-
const changeDisposable = vscode.extensions.onDidChange(() => {
30+
const changeDisposable = vscode.extensions.onDidChange(async () => {
2931
const newPylanceExtension = vscode.extensions.getExtension(PYLANCE_EXTENSION_ID);
3032
if (newPylanceExtension) {
3133
changeDisposable.dispose();
32-
runPylance(context, newPylanceExtension);
34+
await runPylance(context, newPylanceExtension);
3335
}
3436
});
3537
}
3638

39+
export function deactivate(): Promise<void> | undefined {
40+
const client = languageClient;
41+
languageClient = undefined;
42+
43+
return client?.stop();
44+
}
45+
3746
async function runPylance(
3847
context: vscode.ExtensionContext,
3948
pylanceExtension: vscode.Extension<unknown>,
@@ -76,48 +85,37 @@ async function runPylance(
7685
middleware,
7786
};
7887

79-
const languageClient = new LanguageClient('python', 'Python Language Server', clientOptions, worker);
88+
const client = new LanguageClient('python', 'Python Language Server', clientOptions, worker);
89+
languageClient = client;
8090

81-
languageClient.onDidChangeState((e): void => {
82-
// The client's on* methods must be called after the client has started, but if called too
83-
// late the server may have already sent a message (which leads to failures). Register
84-
// these on the state change to running to ensure they are ready soon enough.
85-
if (e.newState !== State.Running) {
86-
return;
87-
}
91+
context.subscriptions.push(
92+
vscode.commands.registerCommand('python.viewLanguageServerOutput', () => client.outputChannel.show()),
93+
);
8894

89-
context.subscriptions.push(
90-
vscode.commands.registerCommand('python.viewLanguageServerOutput', () =>
91-
languageClient.outputChannel.show(),
92-
),
93-
);
94-
95-
languageClient.onTelemetry(
96-
(telemetryEvent: {
97-
EventName: EventName;
98-
Properties: { method: string };
99-
Measurements: number | Record<string, number> | undefined;
100-
Exception: Error | undefined;
101-
}) => {
102-
const eventName = telemetryEvent.EventName || EventName.LANGUAGE_SERVER_TELEMETRY;
103-
const formattedProperties = {
104-
...telemetryEvent.Properties,
105-
// Replace all slashes in the method name so it doesn't get scrubbed by vscode-extension-telemetry.
106-
method: telemetryEvent.Properties.method?.replace(/\//g, '.'),
107-
};
108-
sendTelemetryEventBrowser(
109-
eventName,
110-
telemetryEvent.Measurements,
111-
formattedProperties,
112-
telemetryEvent.Exception,
113-
);
114-
},
115-
);
116-
});
95+
client.onTelemetry(
96+
(telemetryEvent: {
97+
EventName: EventName;
98+
Properties: { method: string };
99+
Measurements: number | Record<string, number> | undefined;
100+
Exception: Error | undefined;
101+
}) => {
102+
const eventName = telemetryEvent.EventName || EventName.LANGUAGE_SERVER_TELEMETRY;
103+
const formattedProperties = {
104+
...telemetryEvent.Properties,
105+
// Replace all slashes in the method name so it doesn't get scrubbed by vscode-extension-telemetry.
106+
method: telemetryEvent.Properties.method?.replace(/\//g, '.'),
107+
};
108+
sendTelemetryEventBrowser(
109+
eventName,
110+
telemetryEvent.Measurements,
111+
formattedProperties,
112+
telemetryEvent.Exception,
113+
);
114+
},
115+
);
117116

118-
const disposable = languageClient.start();
117+
await client.start();
119118

120-
context.subscriptions.push(disposable);
121119
context.subscriptions.push(createStatusItem());
122120
} catch (e) {
123121
console.log(e);

0 commit comments

Comments
 (0)