@jupyterlab/lsp 4.0.0-alpha.19 → 4.0.0-alpha.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,77 @@
1
+ /*
2
+ * Copyright (c) Jupyter Development Team.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+
6
+ // Disclaimer/acknowledgement: Fragments are based on https://github.com/wylieconlon/lsp-editor-adapter,
7
+ // which is copyright of wylieconlon and contributors and ISC licenced.
8
+ // ISC licence is, quote, "functionally equivalent to the simplified BSD and MIT licenses,
9
+ // but without language deemed unnecessary following the Berne Convention." (Wikipedia).
10
+ // Introduced modifications are BSD licenced, copyright JupyterLab development team.
11
+
12
+ import {
13
+ Registration,
14
+ ServerCapabilities,
15
+ Unregistration
16
+ } from 'vscode-languageserver-protocol';
17
+
18
+ interface IFlexibleServerCapabilities extends ServerCapabilities {
19
+ [key: string]: any;
20
+ }
21
+
22
+ /**
23
+ * Register the capabilities with the server capabilities provider
24
+ *
25
+ * @param serverCapabilities - server capabilities provider.
26
+ * @param registration - capabilities to be registered.
27
+ * @return - the new server capabilities provider
28
+ */
29
+ function registerServerCapability(
30
+ serverCapabilities: ServerCapabilities,
31
+ registration: Registration
32
+ ): ServerCapabilities | null {
33
+ const serverCapabilitiesCopy = JSON.parse(
34
+ JSON.stringify(serverCapabilities)
35
+ ) as IFlexibleServerCapabilities;
36
+ const { method, registerOptions } = registration;
37
+ const providerName = method.substring(13) + 'Provider';
38
+
39
+ if (providerName) {
40
+ if (!registerOptions) {
41
+ serverCapabilitiesCopy[providerName] = true;
42
+ } else {
43
+ serverCapabilitiesCopy[providerName] = JSON.parse(
44
+ JSON.stringify(registerOptions)
45
+ );
46
+ }
47
+ } else {
48
+ console.warn('Could not register server capability.', registration);
49
+ return null;
50
+ }
51
+
52
+ return serverCapabilitiesCopy;
53
+ }
54
+
55
+ /**
56
+ * Unregister the capabilities with the server capabilities provider
57
+ *
58
+ * @param serverCapabilities - server capabilities provider.
59
+ * @param registration - capabilities to be unregistered.
60
+ * @return - the new server capabilities provider
61
+ */
62
+ function unregisterServerCapability(
63
+ serverCapabilities: ServerCapabilities,
64
+ unregistration: Unregistration
65
+ ): ServerCapabilities {
66
+ const serverCapabilitiesCopy = JSON.parse(
67
+ JSON.stringify(serverCapabilities)
68
+ ) as IFlexibleServerCapabilities;
69
+ const { method } = unregistration;
70
+ const providerName = method.substring(13) + 'Provider';
71
+
72
+ delete serverCapabilitiesCopy[providerName];
73
+
74
+ return serverCapabilitiesCopy;
75
+ }
76
+
77
+ export { registerServerCapability, unregisterServerCapability };
@@ -0,0 +1,102 @@
1
+ /*
2
+ * Copyright (c) Jupyter Development Team.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+
6
+ // Disclaimer/acknowledgement: Fragments are based on https://github.com/wylieconlon/lsp-editor-adapter,
7
+ // which is copyright of wylieconlon and contributors and ISC licenced.
8
+ // ISC licence is, quote, "functionally equivalent to the simplified BSD and MIT licenses,
9
+ // but without language deemed unnecessary following the Berne Convention." (Wikipedia).
10
+ // Introduced modifications are BSD licenced, copyright JupyterLab development team.
11
+
12
+ import type * as lsp from 'vscode-languageserver-protocol';
13
+
14
+ export interface IDocumentInfo {
15
+ /**
16
+ * URI of the virtual document.
17
+ */
18
+ uri: string;
19
+
20
+ /**
21
+ * Version of the virtual document.
22
+ */
23
+ version: number;
24
+
25
+ /**
26
+ * Text content of the document.
27
+ */
28
+ text: string;
29
+
30
+ /**
31
+ * Language id of the document.
32
+ */
33
+ languageId: string;
34
+ }
35
+
36
+ export interface ILspConnection {
37
+ /**
38
+ * Is the language server is connected?
39
+ */
40
+ isConnected: boolean;
41
+ /**
42
+ * Is the language server is initialized?
43
+ */
44
+ isInitialized: boolean;
45
+
46
+ /**
47
+ * Is the language server is connected and initialized?
48
+ */
49
+ isReady: boolean;
50
+
51
+ /**
52
+ * Initialize a connection over a web socket that speaks the LSP protocol
53
+ */
54
+ connect(socket: WebSocket): void;
55
+
56
+ /**
57
+ * Close the connection
58
+ */
59
+ close(): void;
60
+
61
+ // This should support every method from https://microsoft.github.io/language-server-protocol/specification
62
+ /**
63
+ * The initialize request tells the server which options the client supports
64
+ */
65
+ sendInitialize(): void;
66
+ /**
67
+ * Inform the server that the document was opened
68
+ */
69
+ sendOpen(documentInfo: IDocumentInfo): void;
70
+
71
+ /**
72
+ * Sends the full text of the document to the server
73
+ */
74
+ sendChange(documentInfo: IDocumentInfo): void;
75
+
76
+ /**
77
+ * Send save notification to the server.
78
+ */
79
+ sendSaved(documentInfo: IDocumentInfo): void;
80
+
81
+ /**
82
+ * Send configuration change to the server.
83
+ */
84
+ sendConfigurationChange(settings: lsp.DidChangeConfigurationParams): void;
85
+ }
86
+
87
+ export interface ILspOptions {
88
+ /**
89
+ * LSP handler endpoint.
90
+ */
91
+ serverUri: string;
92
+
93
+ /**
94
+ * Language Id.
95
+ */
96
+ languageId: string;
97
+
98
+ /**
99
+ * The root URI set by server.
100
+ */
101
+ rootUri: string;
102
+ }
@@ -0,0 +1,319 @@
1
+ /*
2
+ * Copyright (c) Jupyter Development Team.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+
6
+ // Disclaimer/acknowledgement: Fragments are based on https://github.com/wylieconlon/lsp-editor-adapter,
7
+ // which is copyright of wylieconlon and contributors and ISC licenced.
8
+ // ISC licence is, quote, "functionally equivalent to the simplified BSD and MIT licenses,
9
+ // but without language deemed unnecessary following the Berne Convention." (Wikipedia).
10
+ // Introduced modifications are BSD licenced, copyright JupyterLab development team.
11
+
12
+ import { ConsoleLogger, listen, MessageConnection } from 'vscode-ws-jsonrpc';
13
+
14
+ import { ISignal, Signal } from '@lumino/signaling';
15
+
16
+ import {
17
+ registerServerCapability,
18
+ unregisterServerCapability
19
+ } from './server-capability-registration';
20
+ import { IDocumentInfo, ILspConnection, ILspOptions } from './types';
21
+
22
+ import type * as protocol from 'vscode-languageserver-protocol';
23
+
24
+ export class LspWsConnection implements ILspConnection {
25
+ constructor(options: ILspOptions) {
26
+ this._rootUri = options.rootUri;
27
+ }
28
+
29
+ /**
30
+ * Is the language server is connected?
31
+ */
32
+ get isConnected(): boolean {
33
+ return this._isConnected;
34
+ }
35
+
36
+ /**
37
+ * Is the language server is initialized?
38
+ */
39
+ get isInitialized(): boolean {
40
+ return this._isInitialized;
41
+ }
42
+
43
+ /**
44
+ * Is the language server is connected and initialized?
45
+ */
46
+ get isReady() {
47
+ return this._isConnected && this._isInitialized;
48
+ }
49
+
50
+ /**
51
+ * A signal emitted when the connection is disposed.
52
+ */
53
+ get disposed(): ISignal<this, void> {
54
+ return this._disposed;
55
+ }
56
+
57
+ /**
58
+ * Check if the connection is disposed
59
+ */
60
+ get isDisposed(): boolean {
61
+ return this._isDisposed;
62
+ }
63
+
64
+ /**
65
+ * Initialize a connection over a web socket that speaks the LSP protocol
66
+ */
67
+ connect(socket: WebSocket): void {
68
+ this.socket = socket;
69
+
70
+ listen({
71
+ webSocket: this.socket,
72
+ logger: new ConsoleLogger(),
73
+ onConnection: (connection: MessageConnection) => {
74
+ connection.listen();
75
+ this._isConnected = true;
76
+
77
+ this.connection = connection;
78
+ this.sendInitialize();
79
+
80
+ const registerCapabilityDisposable = this.connection.onRequest(
81
+ 'client/registerCapability',
82
+ (params: protocol.RegistrationParams) => {
83
+ params.registrations.forEach(
84
+ (capabilityRegistration: protocol.Registration) => {
85
+ try {
86
+ this.serverCapabilities = registerServerCapability(
87
+ this.serverCapabilities,
88
+ capabilityRegistration
89
+ )!;
90
+ } catch (err) {
91
+ console.error(err);
92
+ }
93
+ }
94
+ );
95
+ }
96
+ );
97
+ this._disposables.push(registerCapabilityDisposable);
98
+
99
+ const unregisterCapabilityDisposable = this.connection.onRequest(
100
+ 'client/unregisterCapability',
101
+ (params: protocol.UnregistrationParams) => {
102
+ params.unregisterations.forEach(
103
+ (capabilityUnregistration: protocol.Unregistration) => {
104
+ this.serverCapabilities = unregisterServerCapability(
105
+ this.serverCapabilities,
106
+ capabilityUnregistration
107
+ );
108
+ }
109
+ );
110
+ }
111
+ );
112
+ this._disposables.push(unregisterCapabilityDisposable);
113
+
114
+ const disposable = this.connection.onClose(() => {
115
+ this._isConnected = false;
116
+ });
117
+ this._disposables.push(disposable);
118
+ }
119
+ });
120
+ }
121
+
122
+ /**
123
+ * Close the connection
124
+ */
125
+ close() {
126
+ if (this.connection) {
127
+ this.connection.dispose();
128
+ }
129
+ this.openedUris.clear();
130
+ this.socket.close();
131
+ }
132
+
133
+ /**
134
+ * The initialize request telling the server which options the client supports
135
+ */
136
+ sendInitialize() {
137
+ if (!this._isConnected) {
138
+ return;
139
+ }
140
+
141
+ this.openedUris.clear();
142
+
143
+ const message: protocol.InitializeParams = this.initializeParams();
144
+
145
+ this.connection
146
+ .sendRequest<protocol.InitializeResult>('initialize', message)
147
+ .then(
148
+ params => {
149
+ this.onServerInitialized(params);
150
+ },
151
+ e => {
152
+ console.warn('LSP websocket connection initialization failure', e);
153
+ }
154
+ );
155
+ }
156
+
157
+ /**
158
+ * Inform the server that the document was opened
159
+ */
160
+ sendOpen(documentInfo: IDocumentInfo) {
161
+ const textDocumentMessage: protocol.DidOpenTextDocumentParams = {
162
+ textDocument: {
163
+ uri: documentInfo.uri,
164
+ languageId: documentInfo.languageId,
165
+ text: documentInfo.text,
166
+ version: documentInfo.version
167
+ } as protocol.TextDocumentItem
168
+ };
169
+ this.connection
170
+ .sendNotification('textDocument/didOpen', textDocumentMessage)
171
+ .catch(console.error);
172
+ this.openedUris.set(documentInfo.uri, true);
173
+ this.sendChange(documentInfo);
174
+ }
175
+
176
+ /**
177
+ * Sends the full text of the document to the server
178
+ */
179
+ sendChange(documentInfo: IDocumentInfo) {
180
+ if (!this.isReady) {
181
+ return;
182
+ }
183
+ if (!this.openedUris.get(documentInfo.uri)) {
184
+ this.sendOpen(documentInfo);
185
+ return;
186
+ }
187
+ const textDocumentChange: protocol.DidChangeTextDocumentParams = {
188
+ textDocument: {
189
+ uri: documentInfo.uri,
190
+ version: documentInfo.version
191
+ } as protocol.VersionedTextDocumentIdentifier,
192
+ contentChanges: [{ text: documentInfo.text }]
193
+ };
194
+ this.connection
195
+ .sendNotification('textDocument/didChange', textDocumentChange)
196
+ .catch(console.error);
197
+ documentInfo.version++;
198
+ }
199
+
200
+ /**
201
+ * Send save notification to the server.
202
+ */
203
+ sendSaved(documentInfo: IDocumentInfo) {
204
+ if (!this.isReady) {
205
+ return;
206
+ }
207
+
208
+ const textDocumentChange: protocol.DidSaveTextDocumentParams = {
209
+ textDocument: {
210
+ uri: documentInfo.uri,
211
+ version: documentInfo.version
212
+ } as protocol.VersionedTextDocumentIdentifier,
213
+ text: documentInfo.text
214
+ };
215
+ this.connection
216
+ .sendNotification('textDocument/didSave', textDocumentChange)
217
+ .catch(console.error);
218
+ }
219
+
220
+ /**
221
+ * Send configuration change to the server.
222
+ */
223
+ sendConfigurationChange(settings: protocol.DidChangeConfigurationParams) {
224
+ if (!this.isReady) {
225
+ return;
226
+ }
227
+
228
+ this.connection
229
+ .sendNotification('workspace/didChangeConfiguration', settings)
230
+ .catch(console.error);
231
+ }
232
+
233
+ /**
234
+ * Dispose the connection.
235
+ */
236
+ dispose(): void {
237
+ if (this._isDisposed) {
238
+ return;
239
+ }
240
+ this._isDisposed = true;
241
+ this._disposables.forEach(disposable => {
242
+ disposable.dispose();
243
+ });
244
+ this._disposed.emit();
245
+ Signal.clearData(this);
246
+ }
247
+
248
+ /**
249
+ * The internal websocket connection to the LSP handler
250
+ */
251
+ protected socket: WebSocket;
252
+
253
+ /**
254
+ * The json-rpc wrapper over the internal websocket connection.
255
+ */
256
+ protected connection: MessageConnection;
257
+
258
+ /**
259
+ * Map to track opened virtual documents..
260
+ */
261
+ protected openedUris = new Map<string, boolean>();
262
+
263
+ /**
264
+ * Server capabilities provider.
265
+ */
266
+ protected serverCapabilities: protocol.ServerCapabilities;
267
+
268
+ /**
269
+ * The connection is connected?
270
+ */
271
+ protected _isConnected = false;
272
+
273
+ /**
274
+ * The connection is initialized?
275
+ */
276
+ protected _isInitialized = false;
277
+
278
+ /**
279
+ * Array of LSP callback disposables, it is used to
280
+ * clear the callbacks when the connection is disposed.
281
+ */
282
+ protected _disposables: Array<protocol.Disposable> = [];
283
+
284
+ /**
285
+ * Callback called when the server is initialized.
286
+ */
287
+ protected onServerInitialized(params: protocol.InitializeResult): void {
288
+ this._isInitialized = true;
289
+ this.serverCapabilities = params.capabilities;
290
+ this.connection.sendNotification('initialized', {}).catch(console.error);
291
+ this.connection
292
+ .sendNotification('workspace/didChangeConfiguration', {
293
+ settings: {}
294
+ })
295
+ .catch(console.error);
296
+ }
297
+
298
+ /**
299
+ * Initialization parameters to be sent to the language server.
300
+ * Subclasses should override this when adding more features.
301
+ */
302
+ protected initializeParams(): protocol.InitializeParams {
303
+ return {
304
+ capabilities: {} as protocol.ClientCapabilities,
305
+ processId: null,
306
+ rootUri: this._rootUri,
307
+ workspaceFolders: null
308
+ };
309
+ }
310
+
311
+ /**
312
+ * URI of the LSP handler enpoint.
313
+ */
314
+ private _rootUri: string;
315
+
316
+ private _disposed = new Signal<this, void>(this);
317
+
318
+ private _isDisposed = false;
319
+ }