@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.
- package/lib/connection.d.ts +1 -1
- package/lib/lsp.d.ts +7 -7
- package/lib/plugin.d.ts +4 -4
- package/lib/schema.d.ts +14 -14
- package/lib/tokens.d.ts +10 -10
- package/lib/virtual/document.d.ts +3 -3
- package/lib/virtual/document.js +2 -1
- package/lib/virtual/document.js.map +1 -1
- package/package.json +14 -13
- package/src/adapters/adapter.ts +686 -0
- package/src/adapters/statusmessage.ts +94 -0
- package/src/connection.ts +576 -0
- package/src/connection_manager.ts +684 -0
- package/src/extractors/index.ts +6 -0
- package/src/extractors/manager.ts +84 -0
- package/src/extractors/text_extractor.ts +94 -0
- package/src/extractors/types.ts +78 -0
- package/src/feature.ts +58 -0
- package/src/index.ts +17 -0
- package/src/lsp.ts +160 -0
- package/src/manager.ts +336 -0
- package/src/plugin.ts +62 -0
- package/src/positioning.ts +126 -0
- package/src/schema.ts +238 -0
- package/src/tokens.ts +872 -0
- package/src/utils.ts +106 -0
- package/src/virtual/document.ts +1285 -0
- package/src/ws-connection/server-capability-registration.ts +77 -0
- package/src/ws-connection/types.ts +102 -0
- package/src/ws-connection/ws-connection.ts +319 -0
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
// Copyright (c) Jupyter Development Team.
|
|
2
|
+
// Distributed under the terms of the Modified BSD License.
|
|
3
|
+
|
|
4
|
+
import { IDisposable } from '@lumino/disposable';
|
|
5
|
+
import { ISignal, Signal } from '@lumino/signaling';
|
|
6
|
+
|
|
7
|
+
export class StatusMessage implements IDisposable {
|
|
8
|
+
constructor() {
|
|
9
|
+
this._message = '';
|
|
10
|
+
this._timer = null;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Signal emitted on status changed event.
|
|
15
|
+
*/
|
|
16
|
+
get changed(): ISignal<StatusMessage, void> {
|
|
17
|
+
return this._changed;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Test whether the object is disposed.
|
|
22
|
+
*/
|
|
23
|
+
get isDisposed(): boolean {
|
|
24
|
+
return this._isDisposed;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Dispose the object.
|
|
29
|
+
*/
|
|
30
|
+
dispose(): void {
|
|
31
|
+
if (this.isDisposed) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
this._isDisposed = true;
|
|
35
|
+
if (this._timer) {
|
|
36
|
+
window.clearTimeout(this._timer);
|
|
37
|
+
}
|
|
38
|
+
Signal.clearData(this);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* The text message to be shown on the statusbar.
|
|
43
|
+
*/
|
|
44
|
+
get message(): string {
|
|
45
|
+
return this._message;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Set the text message and (optionally) the timeout to remove it.
|
|
50
|
+
* @param message
|
|
51
|
+
* @param timeout - number of ms to until the message is cleaned;
|
|
52
|
+
* -1 if the message should stay up indefinitely;
|
|
53
|
+
* defaults to 3000ms (3 seconds)
|
|
54
|
+
*/
|
|
55
|
+
set(message: string, timeout: number = 1000 * 3): void {
|
|
56
|
+
this._expireTimer();
|
|
57
|
+
this._message = message;
|
|
58
|
+
this._changed.emit();
|
|
59
|
+
if (timeout !== -1) {
|
|
60
|
+
this._timer = window.setTimeout(this.clear.bind(this), timeout);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Clear the status message.
|
|
66
|
+
*/
|
|
67
|
+
clear(): void {
|
|
68
|
+
this._message = '';
|
|
69
|
+
this._changed.emit();
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Timeout reference used to clear the previous `setTimeout` call.
|
|
73
|
+
*/
|
|
74
|
+
private _timer: number | null;
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Clear the previous `setTimeout` call.
|
|
78
|
+
*/
|
|
79
|
+
private _expireTimer(): void {
|
|
80
|
+
if (this._timer !== null) {
|
|
81
|
+
window.clearTimeout(this._timer);
|
|
82
|
+
this._timer = null;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* The text message to be shown on the statusbar
|
|
88
|
+
*/
|
|
89
|
+
private _message: string;
|
|
90
|
+
|
|
91
|
+
private _changed = new Signal<StatusMessage, void>(this);
|
|
92
|
+
|
|
93
|
+
private _isDisposed = false;
|
|
94
|
+
}
|
|
@@ -0,0 +1,576 @@
|
|
|
1
|
+
// Copyright (c) Jupyter Development Team.
|
|
2
|
+
// Distributed under the terms of the Modified BSD License.
|
|
3
|
+
|
|
4
|
+
import { ISignal, Signal } from '@lumino/signaling';
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
ClientNotifications,
|
|
8
|
+
ClientRequests,
|
|
9
|
+
IClientRequestHandler,
|
|
10
|
+
IClientRequestParams,
|
|
11
|
+
IClientResult,
|
|
12
|
+
IDocumentInfo,
|
|
13
|
+
ILSPConnection,
|
|
14
|
+
ILSPOptions,
|
|
15
|
+
IServerRequestHandler,
|
|
16
|
+
IServerRequestParams,
|
|
17
|
+
IServerResult,
|
|
18
|
+
Method,
|
|
19
|
+
ServerNotifications,
|
|
20
|
+
ServerRequests
|
|
21
|
+
} from './tokens';
|
|
22
|
+
import { untilReady } from './utils';
|
|
23
|
+
import {
|
|
24
|
+
registerServerCapability,
|
|
25
|
+
unregisterServerCapability
|
|
26
|
+
} from './ws-connection/server-capability-registration';
|
|
27
|
+
import { LspWsConnection } from './ws-connection/ws-connection';
|
|
28
|
+
|
|
29
|
+
import type * as lsp from 'vscode-languageserver-protocol';
|
|
30
|
+
|
|
31
|
+
import type { MessageConnection } from 'vscode-ws-jsonrpc';
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Helper class to handle client request
|
|
35
|
+
*/
|
|
36
|
+
class ClientRequestHandler<
|
|
37
|
+
T extends keyof IClientRequestParams = keyof IClientRequestParams
|
|
38
|
+
> implements IClientRequestHandler
|
|
39
|
+
{
|
|
40
|
+
constructor(
|
|
41
|
+
protected connection: MessageConnection,
|
|
42
|
+
protected method: T,
|
|
43
|
+
protected emitter: LSPConnection
|
|
44
|
+
) {}
|
|
45
|
+
request(params: IClientRequestParams[T]): Promise<IClientResult[T]> {
|
|
46
|
+
// TODO check if is ready?
|
|
47
|
+
this.emitter.log(MessageKind.clientRequested, {
|
|
48
|
+
method: this.method,
|
|
49
|
+
message: params
|
|
50
|
+
});
|
|
51
|
+
return this.connection
|
|
52
|
+
.sendRequest(this.method, params)
|
|
53
|
+
.then((result: IClientResult[T]) => {
|
|
54
|
+
this.emitter.log(MessageKind.resultForClient, {
|
|
55
|
+
method: this.method,
|
|
56
|
+
message: params
|
|
57
|
+
});
|
|
58
|
+
return result;
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Helper class to handle server responses
|
|
65
|
+
*/
|
|
66
|
+
class ServerRequestHandler<
|
|
67
|
+
T extends keyof IServerRequestParams = keyof IServerRequestParams
|
|
68
|
+
> implements IServerRequestHandler
|
|
69
|
+
{
|
|
70
|
+
constructor(
|
|
71
|
+
protected connection: MessageConnection,
|
|
72
|
+
protected method: T,
|
|
73
|
+
protected emitter: LSPConnection
|
|
74
|
+
) {
|
|
75
|
+
// on request accepts "thenable"
|
|
76
|
+
this.connection.onRequest(method, this._handle.bind(this));
|
|
77
|
+
this._handler = null;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
setHandler(
|
|
81
|
+
handler: (
|
|
82
|
+
params: IServerRequestParams[T],
|
|
83
|
+
connection?: LSPConnection
|
|
84
|
+
) => Promise<IServerResult[T]>
|
|
85
|
+
) {
|
|
86
|
+
this._handler = handler;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
clearHandler() {
|
|
90
|
+
this._handler = null;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
private _handler:
|
|
94
|
+
| ((
|
|
95
|
+
params: IServerRequestParams[T],
|
|
96
|
+
connection?: LSPConnection
|
|
97
|
+
) => Promise<IServerResult[T]>)
|
|
98
|
+
| null;
|
|
99
|
+
|
|
100
|
+
private _handle(
|
|
101
|
+
request: IServerRequestParams[T]
|
|
102
|
+
): Promise<IServerResult[T] | undefined> {
|
|
103
|
+
this.emitter.log(MessageKind.serverRequested, {
|
|
104
|
+
method: this.method,
|
|
105
|
+
message: request
|
|
106
|
+
});
|
|
107
|
+
if (!this._handler) {
|
|
108
|
+
return new Promise(() => undefined);
|
|
109
|
+
}
|
|
110
|
+
return this._handler(request, this.emitter).then(result => {
|
|
111
|
+
this.emitter.log(MessageKind.responseForServer, {
|
|
112
|
+
method: this.method,
|
|
113
|
+
message: result
|
|
114
|
+
});
|
|
115
|
+
return result;
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export const Provider: { [key: string]: keyof lsp.ServerCapabilities } = {
|
|
121
|
+
TEXT_DOCUMENT_SYNC: 'textDocumentSync',
|
|
122
|
+
COMPLETION: 'completionProvider',
|
|
123
|
+
HOVER: 'hoverProvider',
|
|
124
|
+
SIGNATURE_HELP: 'signatureHelpProvider',
|
|
125
|
+
DECLARATION: 'declarationProvider',
|
|
126
|
+
DEFINITION: 'definitionProvider',
|
|
127
|
+
TYPE_DEFINITION: 'typeDefinitionProvider',
|
|
128
|
+
IMPLEMENTATION: 'implementationProvider',
|
|
129
|
+
REFERENCES: 'referencesProvider',
|
|
130
|
+
DOCUMENT_HIGHLIGHT: 'documentHighlightProvider',
|
|
131
|
+
DOCUMENT_SYMBOL: 'documentSymbolProvider',
|
|
132
|
+
CODE_ACTION: 'codeActionProvider',
|
|
133
|
+
CODE_LENS: 'codeLensProvider',
|
|
134
|
+
DOCUMENT_LINK: 'documentLinkProvider',
|
|
135
|
+
COLOR: 'colorProvider',
|
|
136
|
+
DOCUMENT_FORMATTING: 'documentFormattingProvider',
|
|
137
|
+
DOCUMENT_RANGE_FORMATTING: 'documentRangeFormattingProvider',
|
|
138
|
+
DOCUMENT_ON_TYPE_FORMATTING: 'documentOnTypeFormattingProvider',
|
|
139
|
+
RENAME: 'renameProvider',
|
|
140
|
+
FOLDING_RANGE: 'foldingRangeProvider',
|
|
141
|
+
EXECUTE_COMMAND: 'executeCommandProvider',
|
|
142
|
+
SELECTION_RANGE: 'selectionRangeProvider',
|
|
143
|
+
WORKSPACE_SYMBOL: 'workspaceSymbolProvider',
|
|
144
|
+
WORKSPACE: 'workspace'
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
type AnyMethodType =
|
|
148
|
+
| typeof Method.ServerNotification
|
|
149
|
+
| typeof Method.ClientNotification
|
|
150
|
+
| typeof Method.ClientRequest
|
|
151
|
+
| typeof Method.ServerRequest;
|
|
152
|
+
type AnyMethod =
|
|
153
|
+
| Method.ServerNotification
|
|
154
|
+
| Method.ClientNotification
|
|
155
|
+
| Method.ClientRequest
|
|
156
|
+
| Method.ServerRequest;
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Create a map between the request method and its handler
|
|
160
|
+
*/
|
|
161
|
+
function createMethodMap<T, H, U extends keyof T = keyof T>(
|
|
162
|
+
methods: AnyMethodType,
|
|
163
|
+
handlerFactory: (method: U) => H
|
|
164
|
+
): T {
|
|
165
|
+
const result: { [key in U]?: H } = {};
|
|
166
|
+
for (let method of Object.values(methods)) {
|
|
167
|
+
result[method as U] = handlerFactory(method as U);
|
|
168
|
+
}
|
|
169
|
+
return result as T;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
enum MessageKind {
|
|
173
|
+
clientNotifiedServer,
|
|
174
|
+
serverNotifiedClient,
|
|
175
|
+
serverRequested,
|
|
176
|
+
clientRequested,
|
|
177
|
+
resultForClient,
|
|
178
|
+
responseForServer
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
interface IMessageLog<T extends AnyMethod = AnyMethod> {
|
|
182
|
+
method: T;
|
|
183
|
+
message: any;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export class LSPConnection extends LspWsConnection implements ILSPConnection {
|
|
187
|
+
constructor(options: ILSPOptions) {
|
|
188
|
+
super(options);
|
|
189
|
+
this._options = options;
|
|
190
|
+
this.logAllCommunication = false;
|
|
191
|
+
this.serverIdentifier = options.serverIdentifier;
|
|
192
|
+
this.serverLanguage = options.languageId;
|
|
193
|
+
this.documentsToOpen = [];
|
|
194
|
+
this.clientNotifications =
|
|
195
|
+
this.constructNotificationHandlers<ClientNotifications>(
|
|
196
|
+
Method.ClientNotification
|
|
197
|
+
);
|
|
198
|
+
this.serverNotifications =
|
|
199
|
+
this.constructNotificationHandlers<ServerNotifications>(
|
|
200
|
+
Method.ServerNotification
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Identifier of the language server
|
|
206
|
+
*/
|
|
207
|
+
readonly serverIdentifier?: string;
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Language of the language server
|
|
211
|
+
*/
|
|
212
|
+
readonly serverLanguage?: string;
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Notifications comes from the client.
|
|
216
|
+
*/
|
|
217
|
+
readonly clientNotifications: ClientNotifications;
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Notifications comes from the server.
|
|
221
|
+
*/
|
|
222
|
+
readonly serverNotifications: ServerNotifications;
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Requests comes from the client.
|
|
226
|
+
*/
|
|
227
|
+
clientRequests: ClientRequests;
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Responses comes from the server.
|
|
231
|
+
*/
|
|
232
|
+
serverRequests: ServerRequests;
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Should log all communication?
|
|
236
|
+
*/
|
|
237
|
+
logAllCommunication: boolean;
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Signal emitted when the connection is closed.
|
|
241
|
+
*/
|
|
242
|
+
get closeSignal(): ISignal<ILSPConnection, boolean> {
|
|
243
|
+
return this._closeSignal;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Signal emitted when the connection receives an error
|
|
248
|
+
* message..
|
|
249
|
+
*/
|
|
250
|
+
get errorSignal(): ISignal<ILSPConnection, any> {
|
|
251
|
+
return this._errorSignal;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Signal emitted when the connection is initialized.
|
|
256
|
+
*/
|
|
257
|
+
get serverInitialized(): ISignal<
|
|
258
|
+
ILSPConnection,
|
|
259
|
+
lsp.ServerCapabilities<any>
|
|
260
|
+
> {
|
|
261
|
+
return this._serverInitialized;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Dispose the connection.
|
|
266
|
+
*/
|
|
267
|
+
dispose(): void {
|
|
268
|
+
if (this.isDisposed) {
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
Object.values(this.serverRequests).forEach(request =>
|
|
272
|
+
request.clearHandler()
|
|
273
|
+
);
|
|
274
|
+
this.close();
|
|
275
|
+
super.dispose();
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Helper to print the logs to logger, for now we are using
|
|
280
|
+
* directly the browser's console.
|
|
281
|
+
*/
|
|
282
|
+
log(kind: MessageKind, message: IMessageLog): void {
|
|
283
|
+
if (this.logAllCommunication) {
|
|
284
|
+
console.log(kind, message);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Send the open request to the backend when the server is
|
|
290
|
+
* ready.
|
|
291
|
+
*/
|
|
292
|
+
sendOpenWhenReady(documentInfo: IDocumentInfo): void {
|
|
293
|
+
if (this.isReady) {
|
|
294
|
+
this.sendOpen(documentInfo);
|
|
295
|
+
} else {
|
|
296
|
+
this.documentsToOpen.push(documentInfo);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Send the document changes to the server.
|
|
302
|
+
*/
|
|
303
|
+
sendSelectiveChange(
|
|
304
|
+
changeEvent: lsp.TextDocumentContentChangeEvent,
|
|
305
|
+
documentInfo: IDocumentInfo
|
|
306
|
+
): void {
|
|
307
|
+
this._sendChange([changeEvent], documentInfo);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Send all changes to the server.
|
|
312
|
+
*/
|
|
313
|
+
sendFullTextChange(text: string, documentInfo: IDocumentInfo): void {
|
|
314
|
+
this._sendChange([{ text }], documentInfo);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Check if a provider is available in the registered capabilities.
|
|
319
|
+
*/
|
|
320
|
+
provides(provider: keyof lsp.ServerCapabilities): boolean {
|
|
321
|
+
return !!(this.serverCapabilities && this.serverCapabilities[provider]);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Close the connection to the server.
|
|
326
|
+
*/
|
|
327
|
+
close(): void {
|
|
328
|
+
try {
|
|
329
|
+
this._closingManually = true;
|
|
330
|
+
super.close();
|
|
331
|
+
} catch (e) {
|
|
332
|
+
this._closingManually = false;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* initialize a connection over a web socket that speaks the LSP
|
|
338
|
+
*/
|
|
339
|
+
connect(socket: WebSocket): void {
|
|
340
|
+
super.connect(socket);
|
|
341
|
+
untilReady(() => {
|
|
342
|
+
return this.isConnected;
|
|
343
|
+
}, -1)
|
|
344
|
+
.then(() => {
|
|
345
|
+
const disposable = this.connection.onClose(() => {
|
|
346
|
+
this._isConnected = false;
|
|
347
|
+
this._closeSignal.emit(this._closingManually);
|
|
348
|
+
});
|
|
349
|
+
this._disposables.push(disposable);
|
|
350
|
+
})
|
|
351
|
+
.catch(() => {
|
|
352
|
+
console.error('Could not connect onClose signal');
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Get send request to the server to get completion results
|
|
358
|
+
* from a completion item
|
|
359
|
+
*/
|
|
360
|
+
async getCompletionResolve(
|
|
361
|
+
completionItem: lsp.CompletionItem
|
|
362
|
+
): Promise<lsp.CompletionItem | undefined> {
|
|
363
|
+
if (!this.isReady) {
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
return this.connection.sendRequest<lsp.CompletionItem>(
|
|
367
|
+
'completionItem/resolve',
|
|
368
|
+
completionItem
|
|
369
|
+
);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* List of documents waiting to be opened once the connection
|
|
374
|
+
* is ready.
|
|
375
|
+
*/
|
|
376
|
+
protected documentsToOpen: IDocumentInfo[];
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* Generate the notification handlers
|
|
380
|
+
*/
|
|
381
|
+
protected constructNotificationHandlers<
|
|
382
|
+
T extends ServerNotifications | ClientNotifications
|
|
383
|
+
>(
|
|
384
|
+
methods: typeof Method.ServerNotification | typeof Method.ClientNotification
|
|
385
|
+
): T {
|
|
386
|
+
const factory = () => new Signal<any, any>(this);
|
|
387
|
+
return createMethodMap<T, Signal<any, any>>(methods, factory);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* Generate the client request handler
|
|
392
|
+
*/
|
|
393
|
+
protected constructClientRequestHandler<
|
|
394
|
+
T extends ClientRequests,
|
|
395
|
+
U extends keyof T = keyof T
|
|
396
|
+
>(methods: typeof Method.ClientRequest): T {
|
|
397
|
+
return createMethodMap<T, IClientRequestHandler>(
|
|
398
|
+
methods,
|
|
399
|
+
method =>
|
|
400
|
+
new ClientRequestHandler(this.connection, method as U as any, this)
|
|
401
|
+
);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* Generate the server response handler
|
|
406
|
+
*/
|
|
407
|
+
protected constructServerRequestHandler<
|
|
408
|
+
T extends ServerRequests,
|
|
409
|
+
U extends keyof T = keyof T
|
|
410
|
+
>(methods: typeof Method.ServerRequest): T {
|
|
411
|
+
return createMethodMap<T, IServerRequestHandler>(
|
|
412
|
+
methods,
|
|
413
|
+
method =>
|
|
414
|
+
new ServerRequestHandler(this.connection, method as U as any, this)
|
|
415
|
+
);
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* Initialization parameters to be sent to the language server.
|
|
420
|
+
* Subclasses can overload this when adding more features.
|
|
421
|
+
*/
|
|
422
|
+
protected initializeParams(): lsp.InitializeParams {
|
|
423
|
+
return {
|
|
424
|
+
...super.initializeParams(),
|
|
425
|
+
capabilities: this._options.capabilities,
|
|
426
|
+
initializationOptions: null,
|
|
427
|
+
processId: null,
|
|
428
|
+
workspaceFolders: null
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* Callback called when the server is initialized.
|
|
434
|
+
*/
|
|
435
|
+
protected onServerInitialized(params: lsp.InitializeResult): void {
|
|
436
|
+
this.afterInitialized();
|
|
437
|
+
super.onServerInitialized(params);
|
|
438
|
+
while (this.documentsToOpen.length) {
|
|
439
|
+
this.sendOpen(this.documentsToOpen.pop()!);
|
|
440
|
+
}
|
|
441
|
+
this._serverInitialized.emit(this.serverCapabilities);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
/**
|
|
445
|
+
* Once the server is initialized, this method generates the
|
|
446
|
+
* client and server handlers
|
|
447
|
+
*/
|
|
448
|
+
protected afterInitialized(): void {
|
|
449
|
+
const disposable = this.connection.onError(e => this._errorSignal.emit(e));
|
|
450
|
+
this._disposables.push(disposable);
|
|
451
|
+
for (const method of Object.values(
|
|
452
|
+
Method.ServerNotification
|
|
453
|
+
) as (keyof ServerNotifications)[]) {
|
|
454
|
+
const signal = this.serverNotifications[method] as Signal<any, any>;
|
|
455
|
+
const disposable = this.connection.onNotification(method, params => {
|
|
456
|
+
this.log(MessageKind.serverNotifiedClient, {
|
|
457
|
+
method,
|
|
458
|
+
message: params
|
|
459
|
+
});
|
|
460
|
+
signal.emit(params);
|
|
461
|
+
});
|
|
462
|
+
this._disposables.push(disposable);
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
for (const method of Object.values(
|
|
466
|
+
Method.ClientNotification
|
|
467
|
+
) as (keyof ClientNotifications)[]) {
|
|
468
|
+
const signal = this.clientNotifications[method] as Signal<any, any>;
|
|
469
|
+
signal.connect((emitter, params) => {
|
|
470
|
+
this.log(MessageKind.clientNotifiedServer, {
|
|
471
|
+
method,
|
|
472
|
+
message: params
|
|
473
|
+
});
|
|
474
|
+
this.connection.sendNotification(method, params).catch(console.error);
|
|
475
|
+
});
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
this.clientRequests = this.constructClientRequestHandler<ClientRequests>(
|
|
479
|
+
Method.ClientRequest
|
|
480
|
+
);
|
|
481
|
+
this.serverRequests = this.constructServerRequestHandler<ServerRequests>(
|
|
482
|
+
Method.ServerRequest
|
|
483
|
+
);
|
|
484
|
+
|
|
485
|
+
this.serverRequests['client/registerCapability'].setHandler(
|
|
486
|
+
async (params: lsp.RegistrationParams) => {
|
|
487
|
+
params.registrations.forEach(
|
|
488
|
+
(capabilityRegistration: lsp.Registration) => {
|
|
489
|
+
try {
|
|
490
|
+
const updatedCapabilities = registerServerCapability(
|
|
491
|
+
this.serverCapabilities,
|
|
492
|
+
capabilityRegistration
|
|
493
|
+
);
|
|
494
|
+
if (updatedCapabilities === null) {
|
|
495
|
+
console.error(
|
|
496
|
+
`Failed to register server capability: ${capabilityRegistration}`
|
|
497
|
+
);
|
|
498
|
+
return;
|
|
499
|
+
}
|
|
500
|
+
this.serverCapabilities = updatedCapabilities;
|
|
501
|
+
} catch (err) {
|
|
502
|
+
console.error(err);
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
);
|
|
506
|
+
}
|
|
507
|
+
);
|
|
508
|
+
|
|
509
|
+
this.serverRequests['client/unregisterCapability'].setHandler(
|
|
510
|
+
async (params: lsp.UnregistrationParams) => {
|
|
511
|
+
params.unregisterations.forEach(
|
|
512
|
+
(capabilityUnregistration: lsp.Unregistration) => {
|
|
513
|
+
this.serverCapabilities = unregisterServerCapability(
|
|
514
|
+
this.serverCapabilities,
|
|
515
|
+
capabilityUnregistration
|
|
516
|
+
);
|
|
517
|
+
}
|
|
518
|
+
);
|
|
519
|
+
}
|
|
520
|
+
);
|
|
521
|
+
|
|
522
|
+
this.serverRequests['workspace/configuration'].setHandler(async params => {
|
|
523
|
+
return params.items.map(item => {
|
|
524
|
+
// LSP: "If the client can’t provide a configuration setting for a given scope
|
|
525
|
+
// then `null` needs to be present in the returned array."
|
|
526
|
+
|
|
527
|
+
// for now we do not support configuration, but yaml server does not respect
|
|
528
|
+
// client capability so we have a handler just for that
|
|
529
|
+
return null;
|
|
530
|
+
});
|
|
531
|
+
});
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
/**
|
|
535
|
+
* Is the connection is closed manually?
|
|
536
|
+
*/
|
|
537
|
+
private _closingManually = false;
|
|
538
|
+
|
|
539
|
+
private _options: ILSPOptions;
|
|
540
|
+
|
|
541
|
+
private _closeSignal: Signal<ILSPConnection, boolean> = new Signal(this);
|
|
542
|
+
private _errorSignal: Signal<ILSPConnection, any> = new Signal(this);
|
|
543
|
+
private _serverInitialized: Signal<
|
|
544
|
+
ILSPConnection,
|
|
545
|
+
lsp.ServerCapabilities<any>
|
|
546
|
+
> = new Signal(this);
|
|
547
|
+
|
|
548
|
+
/**
|
|
549
|
+
* Send the document changed data to the server.
|
|
550
|
+
*/
|
|
551
|
+
private _sendChange(
|
|
552
|
+
changeEvents: lsp.TextDocumentContentChangeEvent[],
|
|
553
|
+
documentInfo: IDocumentInfo
|
|
554
|
+
) {
|
|
555
|
+
if (!this.isReady) {
|
|
556
|
+
return;
|
|
557
|
+
}
|
|
558
|
+
if (documentInfo.uri.length === 0) {
|
|
559
|
+
return;
|
|
560
|
+
}
|
|
561
|
+
if (!this.openedUris.get(documentInfo.uri)) {
|
|
562
|
+
this.sendOpen(documentInfo);
|
|
563
|
+
}
|
|
564
|
+
const textDocumentChange: lsp.DidChangeTextDocumentParams = {
|
|
565
|
+
textDocument: {
|
|
566
|
+
uri: documentInfo.uri,
|
|
567
|
+
version: documentInfo.version
|
|
568
|
+
} as lsp.VersionedTextDocumentIdentifier,
|
|
569
|
+
contentChanges: changeEvents
|
|
570
|
+
};
|
|
571
|
+
this.connection
|
|
572
|
+
.sendNotification('textDocument/didChange', textDocumentChange)
|
|
573
|
+
.catch(console.error);
|
|
574
|
+
documentInfo.version++;
|
|
575
|
+
}
|
|
576
|
+
}
|