@adecore/lsp 0.42.0 → 0.44.0
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/dist/connection.d.ts +1 -0
- package/dist/connection.js +29 -1
- package/dist/document.d.ts +15 -1
- package/dist/document.js +60 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -0
- package/dist/protocol.d.ts +234 -1
- package/dist/service.d.ts +25 -1
- package/dist/session.d.ts +20 -12
- package/dist/session.js +169 -12
- package/dist/unserved.d.ts +2 -0
- package/dist/unserved.js +53 -0
- package/package.json +1 -1
- package/src/connection.ts +30 -3
- package/src/document.ts +86 -1
- package/src/index.ts +2 -1
- package/src/protocol.ts +225 -1
- package/src/service.ts +55 -0
- package/src/session.ts +221 -18
- package/src/unserved.ts +60 -0
package/dist/session.js
CHANGED
|
@@ -38,7 +38,20 @@ const PROVIDERS = {
|
|
|
38
38
|
'textDocument/prepareCallHierarchy': 'callHierarchyProvider',
|
|
39
39
|
'callHierarchy/incomingCalls': 'callHierarchyProvider',
|
|
40
40
|
'callHierarchy/outgoingCalls': 'callHierarchyProvider',
|
|
41
|
-
'textDocument/diagnostic': 'diagnosticProvider'
|
|
41
|
+
'textDocument/diagnostic': 'diagnosticProvider',
|
|
42
|
+
'workspace/diagnostic': 'diagnosticProvider',
|
|
43
|
+
'textDocument/documentLink': 'documentLinkProvider',
|
|
44
|
+
'documentLink/resolve': 'documentLinkProvider',
|
|
45
|
+
'textDocument/documentColor': 'colorProvider',
|
|
46
|
+
'textDocument/colorPresentation': 'colorProvider',
|
|
47
|
+
'textDocument/linkedEditingRange': 'linkedEditingRangeProvider',
|
|
48
|
+
'textDocument/moniker': 'monikerProvider',
|
|
49
|
+
'textDocument/prepareTypeHierarchy': 'typeHierarchyProvider',
|
|
50
|
+
'typeHierarchy/supertypes': 'typeHierarchyProvider',
|
|
51
|
+
'typeHierarchy/subtypes': 'typeHierarchyProvider',
|
|
52
|
+
'textDocument/inlineValue': 'inlineValueProvider',
|
|
53
|
+
'textDocument/inlineCompletion': 'inlineCompletionProvider',
|
|
54
|
+
'textDocument/rangesFormatting': 'documentRangeFormattingProvider'
|
|
42
55
|
};
|
|
43
56
|
/* A method whose dynamic registration is made under the name of another. */
|
|
44
57
|
const REGISTRATION_METHODS = {
|
|
@@ -52,9 +65,23 @@ const REGISTRATION_METHODS = {
|
|
|
52
65
|
'workspaceSymbol/resolve': 'workspace/symbol',
|
|
53
66
|
'textDocument/semanticTokens/full': 'textDocument/semanticTokens',
|
|
54
67
|
'textDocument/semanticTokens/full/delta': 'textDocument/semanticTokens',
|
|
55
|
-
'textDocument/semanticTokens/range': 'textDocument/semanticTokens'
|
|
68
|
+
'textDocument/semanticTokens/range': 'textDocument/semanticTokens',
|
|
69
|
+
'documentLink/resolve': 'textDocument/documentLink',
|
|
70
|
+
'textDocument/colorPresentation': 'textDocument/documentColor',
|
|
71
|
+
'typeHierarchy/supertypes': 'textDocument/prepareTypeHierarchy',
|
|
72
|
+
'typeHierarchy/subtypes': 'textDocument/prepareTypeHierarchy',
|
|
73
|
+
'textDocument/rangesFormatting': 'textDocument/rangeFormatting',
|
|
74
|
+
'workspace/diagnostic': 'textDocument/diagnostic'
|
|
56
75
|
};
|
|
57
|
-
const REFRESH_REQUESTS = [
|
|
76
|
+
const REFRESH_REQUESTS = [
|
|
77
|
+
'workspace/semanticTokens/refresh',
|
|
78
|
+
'workspace/inlayHint/refresh',
|
|
79
|
+
'workspace/diagnostic/refresh',
|
|
80
|
+
'workspace/codeLens/refresh',
|
|
81
|
+
'workspace/inlineValue/refresh',
|
|
82
|
+
'workspace/foldingRange/refresh',
|
|
83
|
+
'workspace/textDocumentContent/refresh'
|
|
84
|
+
];
|
|
58
85
|
function matches(options, document) {
|
|
59
86
|
if (options.documentSelector == null) {
|
|
60
87
|
return true;
|
|
@@ -84,6 +111,12 @@ function offersMethod(method, options) {
|
|
|
84
111
|
if (method === 'textDocument/semanticTokens/range') {
|
|
85
112
|
return !!options.range;
|
|
86
113
|
}
|
|
114
|
+
if (method === 'textDocument/rangesFormatting') {
|
|
115
|
+
return options.rangesSupport === true;
|
|
116
|
+
}
|
|
117
|
+
if (method === 'workspace/diagnostic') {
|
|
118
|
+
return options.workspaceDiagnostics === true;
|
|
119
|
+
}
|
|
87
120
|
return true;
|
|
88
121
|
}
|
|
89
122
|
/* One conversation with one language server: the handshake, what it can do, and the documents open in it. */
|
|
@@ -100,6 +133,7 @@ export class LspSession {
|
|
|
100
133
|
diagnosticListeners = new Set();
|
|
101
134
|
capabilityListeners = new Set();
|
|
102
135
|
configuration;
|
|
136
|
+
folders;
|
|
103
137
|
constructor(transport, options = {}) {
|
|
104
138
|
this.options = options;
|
|
105
139
|
this.connection = new JsonRpcConnection(transport, options.timeoutMs);
|
|
@@ -122,9 +156,15 @@ export class LspSession {
|
|
|
122
156
|
const { items } = params;
|
|
123
157
|
return Promise.all(items.map((item) => (options.onConfiguration ? options.onConfiguration(item) : this.readConfiguration(item.section))));
|
|
124
158
|
});
|
|
159
|
+
this.folders = options.workspaceFolders ?? (options.rootUri ? [{ uri: options.rootUri, name: 'Workspace' }] : null);
|
|
125
160
|
this.connection.onRequest('workspace/workspaceFolders', () => this.workspaceFolders);
|
|
126
161
|
this.connection.onRequest('workspace/applyEdit', (params) => options.onApplyEdit?.(params) ?? { applied: false, failureReason: 'No workspace edit handler is attached' });
|
|
127
162
|
this.connection.onRequest('window/showMessageRequest', (params) => options.onShowMessage?.(params) ?? null);
|
|
163
|
+
this.connection.onRequest('window/showDocument', (params) => options.onShowDocument?.(params) ?? { success: false });
|
|
164
|
+
this.connection.onNotification('window/showMessage', (params) => options.onMessage?.(params));
|
|
165
|
+
this.connection.onNotification('window/logMessage', (params) => options.onLog?.(params));
|
|
166
|
+
this.connection.onNotification('$/logTrace', (params) => options.onTrace?.(params));
|
|
167
|
+
this.connection.onNotification('telemetry/event', (params) => options.onTelemetry?.(params));
|
|
128
168
|
this.connection.onRequest('window/workDoneProgress/create', () => null);
|
|
129
169
|
this.connection.onRequest('client/registerCapability', (params) => {
|
|
130
170
|
const { registrations } = params;
|
|
@@ -157,7 +197,7 @@ export class LspSession {
|
|
|
157
197
|
return this.serverCapabilities;
|
|
158
198
|
}
|
|
159
199
|
get workspaceFolders() {
|
|
160
|
-
return this.
|
|
200
|
+
return this.folders === null ? null : [...this.folders];
|
|
161
201
|
}
|
|
162
202
|
get syncOptions() {
|
|
163
203
|
const sync = this.serverCapabilities.textDocumentSync;
|
|
@@ -178,6 +218,8 @@ export class LspSession {
|
|
|
178
218
|
rootUri: this.options.rootUri ?? null,
|
|
179
219
|
workspaceFolders: this.workspaceFolders,
|
|
180
220
|
clientInfo: this.options.clientInfo ?? { name: '@adecore/lsp' },
|
|
221
|
+
locale: this.options.locale,
|
|
222
|
+
trace: this.options.trace ?? 'off',
|
|
181
223
|
capabilities: clientCapabilities(this.options),
|
|
182
224
|
initializationOptions: this.options.initializationOptions
|
|
183
225
|
});
|
|
@@ -222,7 +264,14 @@ export class LspSession {
|
|
|
222
264
|
candidates.push(registration.registerOptions ?? {});
|
|
223
265
|
}
|
|
224
266
|
}
|
|
225
|
-
|
|
267
|
+
if (method === 'textDocument/willSaveWaitUntil' && this.syncOptions.willSaveWaitUntil) {
|
|
268
|
+
candidates.push({});
|
|
269
|
+
}
|
|
270
|
+
const content = this.serverCapabilities.workspace?.textDocumentContent;
|
|
271
|
+
if (method === 'workspace/textDocumentContent' && content !== undefined) {
|
|
272
|
+
candidates.push({ schemes: content.schemes });
|
|
273
|
+
}
|
|
274
|
+
const provider = PROVIDERS[method] === undefined ? undefined : this.serverCapabilities[PROVIDERS[method]];
|
|
226
275
|
if (provider === true) {
|
|
227
276
|
candidates.push({});
|
|
228
277
|
}
|
|
@@ -260,6 +309,87 @@ export class LspSession {
|
|
|
260
309
|
}
|
|
261
310
|
return this.request('workspaceSymbol/resolve', symbol, options);
|
|
262
311
|
}
|
|
312
|
+
/*
|
|
313
|
+
* The diagnostics of every file of the project, for a server that offers them. `previousResultIds` are what the
|
|
314
|
+
* client last heard per document, so the server can answer `unchanged`; `onPartialResult` streams reports while it works.
|
|
315
|
+
*/
|
|
316
|
+
workspaceDiagnostics(previousResultIds = [], identifier, options) {
|
|
317
|
+
if (!this.supports('workspace/diagnostic')) {
|
|
318
|
+
return Promise.reject(new LspError('Server does not support workspace diagnostics', ErrorCodes.MethodNotFound));
|
|
319
|
+
}
|
|
320
|
+
return this.request('workspace/diagnostic', { identifier, previousResultIds: [...previousResultIds] }, options);
|
|
321
|
+
}
|
|
322
|
+
/* The text of a document of a scheme the server owns, such as a decompiled class, which is shown read-only. */
|
|
323
|
+
textDocumentContent(uri, options) {
|
|
324
|
+
const scheme = uri.slice(0, Math.max(0, uri.indexOf(':')));
|
|
325
|
+
if (!this.providerOptions('workspace/textDocumentContent')?.schemes?.includes(scheme)) {
|
|
326
|
+
return Promise.reject(new LspError(`Server does not serve documents of ${scheme}:`, ErrorCodes.MethodNotFound));
|
|
327
|
+
}
|
|
328
|
+
return this.request('workspace/textDocumentContent', { uri }, options);
|
|
329
|
+
}
|
|
330
|
+
/* Changes the trace level the server sends `$/logTrace` at. */
|
|
331
|
+
setTrace(value) {
|
|
332
|
+
return this.notify('$/setTrace', { value });
|
|
333
|
+
}
|
|
334
|
+
/* Asks the server to stop work it reported as cancellable under `token`. */
|
|
335
|
+
cancelWorkDoneProgress(token) {
|
|
336
|
+
return this.notify('window/workDoneProgress/cancel', { token });
|
|
337
|
+
}
|
|
338
|
+
/* Adds and removes folders of the workspace, and tells a server that asked to hear about it. */
|
|
339
|
+
async changeWorkspaceFolders(event) {
|
|
340
|
+
const removed = new Set(event.removed.map((folder) => fileUriKey(folder.uri)));
|
|
341
|
+
const kept = (this.folders ?? []).filter((folder) => !removed.has(fileUriKey(folder.uri)));
|
|
342
|
+
const known = new Set(kept.map((folder) => fileUriKey(folder.uri)));
|
|
343
|
+
this.folders = [...kept, ...event.added.filter((folder) => !known.has(fileUriKey(folder.uri)))];
|
|
344
|
+
const notifications = this.serverCapabilities.workspace?.workspaceFolders?.changeNotifications;
|
|
345
|
+
const registered = [...this.registrations.values()].some((registration) => registration.method === 'workspace/didChangeWorkspaceFolders');
|
|
346
|
+
if (this.phase === 'ready' && (notifications === true || typeof notifications === 'string' || registered)) {
|
|
347
|
+
await this.notify('workspace/didChangeWorkspaceFolders', { event: { added: [...event.added], removed: [...event.removed] } });
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
/* Whether the server synchronizes notebooks of this type and scheme; the `notebook*` methods do nothing for one it does not. */
|
|
351
|
+
syncsNotebook(notebook) {
|
|
352
|
+
const selectors = this.serverCapabilities.notebookDocumentSync?.notebookSelector ?? [];
|
|
353
|
+
const scheme = notebook.uri.slice(0, Math.max(0, notebook.uri.indexOf(':')));
|
|
354
|
+
return selectors.some(({ notebook: filter }) => {
|
|
355
|
+
if (filter === undefined) {
|
|
356
|
+
return true;
|
|
357
|
+
}
|
|
358
|
+
if (typeof filter === 'string') {
|
|
359
|
+
return filter === '*' || filter === notebook.notebookType;
|
|
360
|
+
}
|
|
361
|
+
return ((!filter.notebookType || filter.notebookType === notebook.notebookType) &&
|
|
362
|
+
(!filter.scheme || filter.scheme === scheme) &&
|
|
363
|
+
(!filter.pattern || globMatch(decodeURIComponent(new URL(notebook.uri).pathname), filter.pattern)));
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
/* Opens a notebook with the documents of its cells, which do not go through `openDocument`. */
|
|
367
|
+
async openNotebook(notebook, cellTextDocuments) {
|
|
368
|
+
if (this.syncsNotebook(notebook)) {
|
|
369
|
+
await this.notify('notebookDocument/didOpen', {
|
|
370
|
+
notebookDocument: notebook,
|
|
371
|
+
cellTextDocuments: cellTextDocuments.map((item) => ({ ...item, version: item.version ?? 0 }))
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
async changeNotebook(notebook, change) {
|
|
376
|
+
if (this.syncsNotebook(notebook)) {
|
|
377
|
+
await this.notify('notebookDocument/didChange', { notebookDocument: { uri: notebook.uri, version: notebook.version }, change });
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
async saveNotebook(notebook) {
|
|
381
|
+
if (this.syncsNotebook(notebook) && this.serverCapabilities.notebookDocumentSync?.save === true) {
|
|
382
|
+
await this.notify('notebookDocument/didSave', { notebookDocument: { uri: notebook.uri } });
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
async closeNotebook(notebook, cellUris) {
|
|
386
|
+
if (this.syncsNotebook(notebook)) {
|
|
387
|
+
await this.notify('notebookDocument/didClose', {
|
|
388
|
+
notebookDocument: { uri: notebook.uri },
|
|
389
|
+
cellTextDocuments: cellUris.map((uri) => ({ uri }))
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
}
|
|
263
393
|
async setConfiguration(settings) {
|
|
264
394
|
this.configuration = settings;
|
|
265
395
|
await this.notify('workspace/didChangeConfiguration', { settings });
|
|
@@ -418,7 +548,13 @@ function clientCapabilities(options) {
|
|
|
418
548
|
const dynamic = { dynamicRegistration: true };
|
|
419
549
|
const symbolKind = { valueSet: Array.from({ length: 26 }, (_, index) => index + 1) };
|
|
420
550
|
return {
|
|
421
|
-
general: {
|
|
551
|
+
general: {
|
|
552
|
+
positionEncodings: ['utf-16'],
|
|
553
|
+
staleRequestSupport: { cancel: true, retryOnContentModified: [] },
|
|
554
|
+
regularExpressions: { engine: 'ECMAScript', version: 'ES2020' },
|
|
555
|
+
markdown: { parser: 'remark', version: '15' }
|
|
556
|
+
},
|
|
557
|
+
notebookDocument: { synchronization: { dynamicRegistration: false, executionSummarySupport: true } },
|
|
422
558
|
workspace: {
|
|
423
559
|
applyEdit: !!options.onApplyEdit,
|
|
424
560
|
workspaceEdit: { documentChanges: true, resourceOperations: [...(options.resourceOperations ?? ['create', 'rename'])], failureHandling: 'abort' },
|
|
@@ -439,11 +575,18 @@ function clientCapabilities(options) {
|
|
|
439
575
|
semanticTokens: { refreshSupport: true },
|
|
440
576
|
inlayHint: { refreshSupport: true },
|
|
441
577
|
codeLens: { refreshSupport: true },
|
|
442
|
-
diagnostics: { refreshSupport: true }
|
|
578
|
+
diagnostics: { refreshSupport: true },
|
|
579
|
+
inlineValue: { refreshSupport: true },
|
|
580
|
+
foldingRange: { refreshSupport: true },
|
|
581
|
+
textDocumentContent: { dynamicRegistration: false }
|
|
582
|
+
},
|
|
583
|
+
window: {
|
|
584
|
+
workDoneProgress: true,
|
|
585
|
+
showMessage: { messageActionItem: { additionalPropertiesSupport: false } },
|
|
586
|
+
showDocument: { support: !!options.onShowDocument }
|
|
443
587
|
},
|
|
444
|
-
window: { workDoneProgress: true, showMessage: { messageActionItem: { additionalPropertiesSupport: false } } },
|
|
445
588
|
textDocument: {
|
|
446
|
-
synchronization: { dynamicRegistration: false, didSave: true },
|
|
589
|
+
synchronization: { dynamicRegistration: false, willSave: true, willSaveWaitUntil: true, didSave: true },
|
|
447
590
|
publishDiagnostics: {
|
|
448
591
|
versionSupport: true,
|
|
449
592
|
relatedInformation: true,
|
|
@@ -455,6 +598,7 @@ function clientCapabilities(options) {
|
|
|
455
598
|
...dynamic,
|
|
456
599
|
contextSupport: true,
|
|
457
600
|
insertTextMode: 1,
|
|
601
|
+
completionItemKind: { valueSet: Array.from({ length: 25 }, (_, index) => index + 1) },
|
|
458
602
|
...(options.completionItemDefaults?.length ? { completionList: { itemDefaults: [...options.completionItemDefaults] } } : {}),
|
|
459
603
|
completionItem: {
|
|
460
604
|
snippetSupport: options.snippetSupport ?? false,
|
|
@@ -462,6 +606,7 @@ function clientCapabilities(options) {
|
|
|
462
606
|
commitCharactersSupport: true,
|
|
463
607
|
documentationFormat: ['markdown', 'plaintext'],
|
|
464
608
|
deprecatedSupport: true,
|
|
609
|
+
tagSupport: { valueSet: [1] },
|
|
465
610
|
preselectSupport: true,
|
|
466
611
|
insertReplaceSupport: true,
|
|
467
612
|
labelDetailsSupport: true,
|
|
@@ -509,11 +654,23 @@ function clientCapabilities(options) {
|
|
|
509
654
|
},
|
|
510
655
|
resolveSupport: { properties: ['edit', 'command'] }
|
|
511
656
|
},
|
|
512
|
-
codeLens: dynamic,
|
|
657
|
+
codeLens: { ...dynamic, resolveSupport: { properties: ['command'] } },
|
|
513
658
|
formatting: dynamic,
|
|
514
|
-
rangeFormatting: dynamic,
|
|
659
|
+
rangeFormatting: { ...dynamic, rangesSupport: true },
|
|
515
660
|
onTypeFormatting: dynamic,
|
|
516
|
-
foldingRange: {
|
|
661
|
+
foldingRange: {
|
|
662
|
+
...dynamic,
|
|
663
|
+
lineFoldingOnly: false,
|
|
664
|
+
foldingRangeKind: { valueSet: ['comment', 'imports', 'region'] },
|
|
665
|
+
foldingRange: { collapsedText: false }
|
|
666
|
+
},
|
|
667
|
+
documentLink: { ...dynamic, tooltipSupport: true },
|
|
668
|
+
colorProvider: dynamic,
|
|
669
|
+
linkedEditingRange: dynamic,
|
|
670
|
+
moniker: dynamic,
|
|
671
|
+
typeHierarchy: dynamic,
|
|
672
|
+
inlineValue: dynamic,
|
|
673
|
+
inlineCompletion: dynamic,
|
|
517
674
|
selectionRange: dynamic,
|
|
518
675
|
semanticTokens: {
|
|
519
676
|
...dynamic,
|
package/dist/unserved.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { ErrorCodes, LspError } from './connection.js';
|
|
2
|
+
const REQUESTS = [
|
|
3
|
+
'completion',
|
|
4
|
+
'resolveCompletion',
|
|
5
|
+
'hover',
|
|
6
|
+
'signatureHelp',
|
|
7
|
+
'definition',
|
|
8
|
+
'declaration',
|
|
9
|
+
'typeDefinition',
|
|
10
|
+
'implementation',
|
|
11
|
+
'references',
|
|
12
|
+
'documentHighlights',
|
|
13
|
+
'documentSymbols',
|
|
14
|
+
'prepareRename',
|
|
15
|
+
'rename',
|
|
16
|
+
'codeActions',
|
|
17
|
+
'resolveCodeAction',
|
|
18
|
+
'executeCommand',
|
|
19
|
+
'formatting',
|
|
20
|
+
'rangeFormatting',
|
|
21
|
+
'semanticTokens',
|
|
22
|
+
'semanticTokensDelta',
|
|
23
|
+
'semanticTokensRange',
|
|
24
|
+
'inlayHints',
|
|
25
|
+
'resolveInlayHint',
|
|
26
|
+
'workspaceSymbols',
|
|
27
|
+
'foldingRanges',
|
|
28
|
+
'selectionRanges',
|
|
29
|
+
'codeLenses',
|
|
30
|
+
'resolveCodeLens'
|
|
31
|
+
];
|
|
32
|
+
const NOTHING = { dispose: () => undefined };
|
|
33
|
+
function refuse() {
|
|
34
|
+
return Promise.reject(new LspError('No language server serves this document', ErrorCodes.MethodNotFound));
|
|
35
|
+
}
|
|
36
|
+
/*
|
|
37
|
+
* A service for documents no server serves: it takes documents and answers `supports` with false, so a
|
|
38
|
+
* feature that checks first never asks, and rejects every request with an `LspError` of code -32601.
|
|
39
|
+
*/
|
|
40
|
+
export function unservedLanguageService() {
|
|
41
|
+
// Every request has the same refusal whatever its parameters, which the record type cannot say.
|
|
42
|
+
const requests = Object.fromEntries(REQUESTS.map((name) => [name, refuse]));
|
|
43
|
+
return {
|
|
44
|
+
...requests,
|
|
45
|
+
openDocument: () => Promise.resolve(),
|
|
46
|
+
changeDocument: () => Promise.resolve(),
|
|
47
|
+
closeDocument: () => Promise.resolve(),
|
|
48
|
+
supports: () => false,
|
|
49
|
+
providerOptions: () => undefined,
|
|
50
|
+
onProvidersChanged: () => NOTHING,
|
|
51
|
+
onDiagnostics: () => NOTHING
|
|
52
|
+
};
|
|
53
|
+
}
|
package/package.json
CHANGED
package/src/connection.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { asError } from './as-error.ts';
|
|
2
|
-
import type { Disposable, LspTransport, RequestId, RequestOptions, RpcMessage, RpcRequest } from './protocol.ts';
|
|
2
|
+
import type { Disposable, LspTransport, RequestId, RequestOptions, RpcMessage, RpcRequest, WorkDoneProgress } from './protocol.ts';
|
|
3
3
|
|
|
4
4
|
/* The JSON-RPC and LSP codes this package answers with. */
|
|
5
5
|
export const ErrorCodes = {
|
|
@@ -89,7 +89,7 @@ export class JsonRpcConnection {
|
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
const id = ++this.nextId;
|
|
92
|
-
|
|
92
|
+
const progress = this.progressOf(id, params, options);
|
|
93
93
|
return new Promise<T>((resolve, reject) => {
|
|
94
94
|
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
95
95
|
|
|
@@ -110,6 +110,7 @@ export class JsonRpcConnection {
|
|
|
110
110
|
cleanup: () => {
|
|
111
111
|
clearTimeout(timer);
|
|
112
112
|
options.signal?.removeEventListener('abort', abort);
|
|
113
|
+
progress.listening?.dispose();
|
|
113
114
|
}
|
|
114
115
|
});
|
|
115
116
|
|
|
@@ -120,8 +121,34 @@ export class JsonRpcConnection {
|
|
|
120
121
|
timer = setTimeout(() => cancel(new LspError(`LSP request timed out: ${method}`, ErrorCodes.RequestCancelled)), timeout);
|
|
121
122
|
}
|
|
122
123
|
|
|
123
|
-
void this.send({ jsonrpc: '2.0', id, method, params }).catch((error) => this.settle(id, undefined, error));
|
|
124
|
+
void this.send({ jsonrpc: '2.0', id, method, params: progress.params }).catch((error) => this.settle(id, undefined, error));
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/* Adds the tokens of partial results and work-done progress to the params of a request, and hears `$/progress` for them. */
|
|
129
|
+
private progressOf(id: RequestId, params: unknown, options: RequestOptions): { params: unknown; listening?: Disposable } {
|
|
130
|
+
const { onPartialResult, onWorkDoneProgress } = options;
|
|
131
|
+
if ((onPartialResult === undefined && onWorkDoneProgress === undefined) || params === null || typeof params !== 'object' || Array.isArray(params)) {
|
|
132
|
+
return { params };
|
|
133
|
+
}
|
|
134
|
+
const partialResultToken = onPartialResult === undefined ? undefined : `adecore-partial-${id}`;
|
|
135
|
+
const workDoneToken = onWorkDoneProgress === undefined ? undefined : `adecore-work-${id}`;
|
|
136
|
+
const listening = this.onNotification('$/progress', (value) => {
|
|
137
|
+
const { token, value: report } = value as { token: unknown; value: unknown };
|
|
138
|
+
if (token === partialResultToken) {
|
|
139
|
+
onPartialResult?.(report);
|
|
140
|
+
} else if (token === workDoneToken) {
|
|
141
|
+
onWorkDoneProgress?.(report as WorkDoneProgress);
|
|
142
|
+
}
|
|
124
143
|
});
|
|
144
|
+
return {
|
|
145
|
+
params: {
|
|
146
|
+
...params,
|
|
147
|
+
...(partialResultToken === undefined ? {} : { partialResultToken }),
|
|
148
|
+
...(workDoneToken === undefined ? {} : { workDoneToken })
|
|
149
|
+
},
|
|
150
|
+
listening
|
|
151
|
+
};
|
|
125
152
|
}
|
|
126
153
|
|
|
127
154
|
notify(method: string, params?: unknown): Promise<void> {
|
package/src/document.ts
CHANGED
|
@@ -9,6 +9,9 @@ import type {
|
|
|
9
9
|
CodeAction,
|
|
10
10
|
CodeActionContext,
|
|
11
11
|
CodeLens,
|
|
12
|
+
Color,
|
|
13
|
+
ColorInformation,
|
|
14
|
+
ColorPresentation,
|
|
12
15
|
Command,
|
|
13
16
|
CompletionContext,
|
|
14
17
|
CompletionItem,
|
|
@@ -17,13 +20,20 @@ import type {
|
|
|
17
20
|
DocumentDiagnosticReport,
|
|
18
21
|
DocumentHighlight,
|
|
19
22
|
DocumentItem,
|
|
23
|
+
DocumentLink,
|
|
20
24
|
DocumentRequestOptions,
|
|
21
25
|
DocumentSymbolResult,
|
|
22
26
|
FoldingRange,
|
|
23
27
|
FormattingOptions,
|
|
24
28
|
Hover,
|
|
25
29
|
InlayHint,
|
|
30
|
+
InlineCompletionContext,
|
|
31
|
+
InlineCompletionResult,
|
|
32
|
+
InlineValue,
|
|
33
|
+
InlineValueContext,
|
|
34
|
+
LinkedEditingRanges,
|
|
26
35
|
Location,
|
|
36
|
+
Moniker,
|
|
27
37
|
NavigationResult,
|
|
28
38
|
Position,
|
|
29
39
|
PrepareRenameResult,
|
|
@@ -33,7 +43,9 @@ import type {
|
|
|
33
43
|
SemanticTokensDelta,
|
|
34
44
|
SignatureHelp,
|
|
35
45
|
SignatureHelpContext,
|
|
46
|
+
TextDocumentSaveReason,
|
|
36
47
|
TextEdit,
|
|
48
|
+
TypeHierarchyItem,
|
|
37
49
|
WorkspaceEdit
|
|
38
50
|
} from './protocol.ts';
|
|
39
51
|
|
|
@@ -286,6 +298,72 @@ export class LspDocument {
|
|
|
286
298
|
return this.feature('textDocument/diagnostic', { previousResultId, identifier }, options);
|
|
287
299
|
}
|
|
288
300
|
|
|
301
|
+
documentLinks(options?: DocumentRequestOptions): Promise<DocumentLink[] | null> {
|
|
302
|
+
return this.feature('textDocument/documentLink', {}, options);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
resolveDocumentLink(link: DocumentLink, options?: DocumentRequestOptions): Promise<DocumentLink> {
|
|
306
|
+
return this.resolve('documentLink/resolve', link, options);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
documentColors(options?: DocumentRequestOptions): Promise<ColorInformation[] | null> {
|
|
310
|
+
return this.feature('textDocument/documentColor', {}, options);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
colorPresentations(color: Color, range: Range, options?: DocumentRequestOptions): Promise<ColorPresentation[] | null> {
|
|
314
|
+
return this.feature('textDocument/colorPresentation', { color, range }, options);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
linkedEditingRanges(position: Position, options?: DocumentRequestOptions): Promise<LinkedEditingRanges | null> {
|
|
318
|
+
return this.feature('textDocument/linkedEditingRange', { position }, options);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
monikers(position: Position, options?: DocumentRequestOptions): Promise<Moniker[] | null> {
|
|
322
|
+
return this.feature('textDocument/moniker', { position }, options);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
prepareTypeHierarchy(position: Position, options?: DocumentRequestOptions): Promise<TypeHierarchyItem[] | null> {
|
|
326
|
+
return this.feature('textDocument/prepareTypeHierarchy', { position }, options);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/* Like the calls of a call hierarchy, expansions of the tree run beside each other unless `cancelPrevious` says otherwise. */
|
|
330
|
+
supertypes(item: TypeHierarchyItem, options?: DocumentRequestOptions): Promise<TypeHierarchyItem[] | null> {
|
|
331
|
+
return this.perform('typeHierarchy/supertypes', { item }, { cancelPrevious: false, ...options });
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
subtypes(item: TypeHierarchyItem, options?: DocumentRequestOptions): Promise<TypeHierarchyItem[] | null> {
|
|
335
|
+
return this.perform('typeHierarchy/subtypes', { item }, { cancelPrevious: false, ...options });
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
inlineValues(range: Range, context: InlineValueContext, options?: DocumentRequestOptions): Promise<InlineValue[] | null> {
|
|
339
|
+
return this.feature('textDocument/inlineValue', { range, context }, options);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
inlineCompletions(position: Position, context: InlineCompletionContext, options?: DocumentRequestOptions): Promise<InlineCompletionResult> {
|
|
343
|
+
return this.feature('textDocument/inlineCompletion', { position, context }, options);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
rangesFormatting(ranges: readonly Range[], formatting: FormattingOptions, options?: DocumentRequestOptions): Promise<TextEdit[] | null> {
|
|
347
|
+
return this.feature('textDocument/rangesFormatting', { ranges: [...ranges], options: formatting }, options);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/* Tells the server a save is coming, when it asked to hear it. */
|
|
351
|
+
async willSave(reason: TextDocumentSaveReason = 1): Promise<void> {
|
|
352
|
+
this.assertOpen();
|
|
353
|
+
if (this.session.syncOptions.willSave) {
|
|
354
|
+
await this.session.notify('textDocument/willSave', { textDocument: { uri: this.uri }, reason });
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/* The edits the server wants in the text before it is saved, or null when it did not ask to be told. Apply them, then save. */
|
|
359
|
+
async willSaveWaitUntil(reason: TextDocumentSaveReason = 1, options?: DocumentRequestOptions): Promise<TextEdit[] | null> {
|
|
360
|
+
this.assertOpen();
|
|
361
|
+
if (!this.session.syncOptions.willSaveWaitUntil) {
|
|
362
|
+
return null;
|
|
363
|
+
}
|
|
364
|
+
return this.perform('textDocument/willSaveWaitUntil', { textDocument: { uri: this.uri }, reason }, options);
|
|
365
|
+
}
|
|
366
|
+
|
|
289
367
|
/* Any feature by its LSP method, for a caller that gets the method from elsewhere. `params` leave out `textDocument`; a resolve method takes the item. */
|
|
290
368
|
request<T = unknown>(method: string, params: object, options?: DocumentRequestOptions): Promise<T> {
|
|
291
369
|
return method.endsWith('/resolve') ? (this.resolve(method, params, options) as Promise<T>) : this.feature<T>(method, params, options);
|
|
@@ -367,7 +445,14 @@ export class LspDocument {
|
|
|
367
445
|
this.sendFullText?.();
|
|
368
446
|
await this.synced;
|
|
369
447
|
this.assertCurrent(version);
|
|
370
|
-
const
|
|
448
|
+
const { onPartialResult, onWorkDoneProgress } = options;
|
|
449
|
+
const result = await this.session.request<T>(method, params, {
|
|
450
|
+
signal: controller.signal,
|
|
451
|
+
timeoutMs: options.timeoutMs,
|
|
452
|
+
// A part for a text that moved on is as stale as the answer would be.
|
|
453
|
+
onPartialResult: onPartialResult && ((value) => this.version === version && !this.closed && onPartialResult(value)),
|
|
454
|
+
onWorkDoneProgress
|
|
455
|
+
});
|
|
371
456
|
this.assertCurrent(version);
|
|
372
457
|
this.remember(result, version);
|
|
373
458
|
|
package/src/index.ts
CHANGED
|
@@ -18,7 +18,8 @@ export { type CreatedFile, type DeletedFile, type RenamedFile } from './file-ope
|
|
|
18
18
|
export { watchesFile, type FileChangeType } from './watched-files.ts';
|
|
19
19
|
export { LspSession, type LspSessionOptions, type SessionState } from './session.ts';
|
|
20
20
|
export { TextMirror } from './text-mirror.ts';
|
|
21
|
-
export type { DiagnosticsReport, LanguageDocument, LanguageRequestOptions, LanguageService } from './service.ts';
|
|
21
|
+
export type { DiagnosticsReport, LanguageDocument, LanguageRequestOptions, LanguageService, ServerMessage } from './service.ts';
|
|
22
|
+
export { unservedLanguageService } from './unserved.ts';
|
|
22
23
|
export { connectWebSocket, createStreamTransport, type ByteStream, type WebSocketTransportOptions } from './transport.ts';
|
|
23
24
|
export { fileUriToPath, pathToFileUri } from './uris.ts';
|
|
24
25
|
export { bridgeVueTypeScript, vueServerOrder, type VueServer } from './vue.ts';
|