@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/src/session.ts
CHANGED
|
@@ -16,16 +16,31 @@ import type {
|
|
|
16
16
|
FileOperationFilter,
|
|
17
17
|
FileSystemWatcher,
|
|
18
18
|
InitializeResult,
|
|
19
|
+
LogMessageParams,
|
|
20
|
+
LogTraceParams,
|
|
19
21
|
LspTransport,
|
|
22
|
+
MessageActionItem,
|
|
23
|
+
NotebookDocument,
|
|
24
|
+
NotebookDocumentChangeEvent,
|
|
25
|
+
PreviousResultId,
|
|
20
26
|
ProgressParams,
|
|
27
|
+
ProgressToken,
|
|
21
28
|
ProviderOptions,
|
|
22
29
|
PublishDiagnosticsParams,
|
|
23
30
|
Registration,
|
|
24
31
|
RequestOptions,
|
|
25
32
|
ServerCapabilities,
|
|
33
|
+
ShowDocumentParams,
|
|
34
|
+
ShowDocumentResult,
|
|
35
|
+
ShowMessageParams,
|
|
36
|
+
ShowMessageRequestParams,
|
|
37
|
+
TextDocumentContentResult,
|
|
26
38
|
TextDocumentSyncOptions,
|
|
39
|
+
TraceValue,
|
|
40
|
+
WorkspaceDiagnosticReport,
|
|
27
41
|
WorkspaceEdit,
|
|
28
42
|
WorkspaceFolder,
|
|
43
|
+
WorkspaceFoldersChangeEvent,
|
|
29
44
|
WorkspaceSymbol,
|
|
30
45
|
WorkspaceSymbolResult
|
|
31
46
|
} from './protocol.ts';
|
|
@@ -53,7 +68,21 @@ export interface LspSessionOptions {
|
|
|
53
68
|
resourceOperations?: readonly ResourceOperationKind[];
|
|
54
69
|
onConfiguration?: (item: ConfigurationItem) => unknown | Promise<unknown>;
|
|
55
70
|
onApplyEdit?: (params: ApplyWorkspaceEditParams) => ApplyWorkspaceEditResult | Promise<ApplyWorkspaceEditResult>;
|
|
56
|
-
|
|
71
|
+
/* Answers `window/showMessageRequest` with the action a person chose, or null when they dismissed it. */
|
|
72
|
+
onShowMessage?: (params: ShowMessageRequestParams) => MessageActionItem | null | Promise<MessageActionItem | null>;
|
|
73
|
+
/* A message the server wants a person to read, without an answer (`window/showMessage`). */
|
|
74
|
+
onMessage?: (params: ShowMessageParams) => void;
|
|
75
|
+
/* A line for a log of the server (`window/logMessage`). */
|
|
76
|
+
onLog?: (params: LogMessageParams) => void;
|
|
77
|
+
/* A trace of the server, sent while `trace` is not `off` (`$/logTrace`). */
|
|
78
|
+
onTrace?: (params: LogTraceParams) => void;
|
|
79
|
+
onTelemetry?: (data: unknown) => void;
|
|
80
|
+
/* Shows a document or a web address the server asks for; without it the client answers that it did not. */
|
|
81
|
+
onShowDocument?: (params: ShowDocumentParams) => ShowDocumentResult | Promise<ShowDocumentResult>;
|
|
82
|
+
/* The trace level sent with `initialize`; `setTrace` changes it later. `off` by default. */
|
|
83
|
+
trace?: TraceValue;
|
|
84
|
+
/* The language of the interface, such as `en` or `nl-NL`, for the server's messages. */
|
|
85
|
+
locale?: string;
|
|
57
86
|
}
|
|
58
87
|
|
|
59
88
|
/* The capability that advertises each method statically. */
|
|
@@ -91,7 +120,20 @@ const PROVIDERS: Record<string, keyof ServerCapabilities> = {
|
|
|
91
120
|
'textDocument/prepareCallHierarchy': 'callHierarchyProvider',
|
|
92
121
|
'callHierarchy/incomingCalls': 'callHierarchyProvider',
|
|
93
122
|
'callHierarchy/outgoingCalls': 'callHierarchyProvider',
|
|
94
|
-
'textDocument/diagnostic': 'diagnosticProvider'
|
|
123
|
+
'textDocument/diagnostic': 'diagnosticProvider',
|
|
124
|
+
'workspace/diagnostic': 'diagnosticProvider',
|
|
125
|
+
'textDocument/documentLink': 'documentLinkProvider',
|
|
126
|
+
'documentLink/resolve': 'documentLinkProvider',
|
|
127
|
+
'textDocument/documentColor': 'colorProvider',
|
|
128
|
+
'textDocument/colorPresentation': 'colorProvider',
|
|
129
|
+
'textDocument/linkedEditingRange': 'linkedEditingRangeProvider',
|
|
130
|
+
'textDocument/moniker': 'monikerProvider',
|
|
131
|
+
'textDocument/prepareTypeHierarchy': 'typeHierarchyProvider',
|
|
132
|
+
'typeHierarchy/supertypes': 'typeHierarchyProvider',
|
|
133
|
+
'typeHierarchy/subtypes': 'typeHierarchyProvider',
|
|
134
|
+
'textDocument/inlineValue': 'inlineValueProvider',
|
|
135
|
+
'textDocument/inlineCompletion': 'inlineCompletionProvider',
|
|
136
|
+
'textDocument/rangesFormatting': 'documentRangeFormattingProvider'
|
|
95
137
|
};
|
|
96
138
|
|
|
97
139
|
/* A method whose dynamic registration is made under the name of another. */
|
|
@@ -106,10 +148,24 @@ const REGISTRATION_METHODS: Record<string, string> = {
|
|
|
106
148
|
'workspaceSymbol/resolve': 'workspace/symbol',
|
|
107
149
|
'textDocument/semanticTokens/full': 'textDocument/semanticTokens',
|
|
108
150
|
'textDocument/semanticTokens/full/delta': 'textDocument/semanticTokens',
|
|
109
|
-
'textDocument/semanticTokens/range': 'textDocument/semanticTokens'
|
|
151
|
+
'textDocument/semanticTokens/range': 'textDocument/semanticTokens',
|
|
152
|
+
'documentLink/resolve': 'textDocument/documentLink',
|
|
153
|
+
'textDocument/colorPresentation': 'textDocument/documentColor',
|
|
154
|
+
'typeHierarchy/supertypes': 'textDocument/prepareTypeHierarchy',
|
|
155
|
+
'typeHierarchy/subtypes': 'textDocument/prepareTypeHierarchy',
|
|
156
|
+
'textDocument/rangesFormatting': 'textDocument/rangeFormatting',
|
|
157
|
+
'workspace/diagnostic': 'textDocument/diagnostic'
|
|
110
158
|
};
|
|
111
159
|
|
|
112
|
-
const REFRESH_REQUESTS = [
|
|
160
|
+
const REFRESH_REQUESTS = [
|
|
161
|
+
'workspace/semanticTokens/refresh',
|
|
162
|
+
'workspace/inlayHint/refresh',
|
|
163
|
+
'workspace/diagnostic/refresh',
|
|
164
|
+
'workspace/codeLens/refresh',
|
|
165
|
+
'workspace/inlineValue/refresh',
|
|
166
|
+
'workspace/foldingRange/refresh',
|
|
167
|
+
'workspace/textDocumentContent/refresh'
|
|
168
|
+
];
|
|
113
169
|
|
|
114
170
|
function matches(options: ProviderOptions, document?: Pick<DocumentItem, 'uri' | 'languageId'>): boolean {
|
|
115
171
|
if (options.documentSelector == null) {
|
|
@@ -152,6 +208,14 @@ function offersMethod(method: string, options: ProviderOptions): boolean {
|
|
|
152
208
|
return !!options.range;
|
|
153
209
|
}
|
|
154
210
|
|
|
211
|
+
if (method === 'textDocument/rangesFormatting') {
|
|
212
|
+
return options.rangesSupport === true;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (method === 'workspace/diagnostic') {
|
|
216
|
+
return options.workspaceDiagnostics === true;
|
|
217
|
+
}
|
|
218
|
+
|
|
155
219
|
return true;
|
|
156
220
|
}
|
|
157
221
|
|
|
@@ -169,6 +233,7 @@ export class LspSession {
|
|
|
169
233
|
private readonly diagnosticListeners = new Set<(params: PublishDiagnosticsParams) => void>();
|
|
170
234
|
private readonly capabilityListeners = new Set<() => void>();
|
|
171
235
|
private configuration: Record<string, unknown>;
|
|
236
|
+
private folders: WorkspaceFolder[] | null;
|
|
172
237
|
|
|
173
238
|
constructor(transport: LspTransport, options: LspSessionOptions = {}) {
|
|
174
239
|
this.options = options;
|
|
@@ -200,6 +265,7 @@ export class LspSession {
|
|
|
200
265
|
return Promise.all(items.map((item) => (options.onConfiguration ? options.onConfiguration(item) : this.readConfiguration(item.section))));
|
|
201
266
|
});
|
|
202
267
|
|
|
268
|
+
this.folders = options.workspaceFolders ?? (options.rootUri ? [{ uri: options.rootUri, name: 'Workspace' }] : null);
|
|
203
269
|
this.connection.onRequest('workspace/workspaceFolders', () => this.workspaceFolders);
|
|
204
270
|
|
|
205
271
|
this.connection.onRequest(
|
|
@@ -207,11 +273,12 @@ export class LspSession {
|
|
|
207
273
|
(params) => options.onApplyEdit?.(params as ApplyWorkspaceEditParams) ?? { applied: false, failureReason: 'No workspace edit handler is attached' }
|
|
208
274
|
);
|
|
209
275
|
|
|
210
|
-
this.connection.onRequest(
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
);
|
|
214
|
-
|
|
276
|
+
this.connection.onRequest('window/showMessageRequest', (params) => options.onShowMessage?.(params as ShowMessageRequestParams) ?? null);
|
|
277
|
+
this.connection.onRequest('window/showDocument', (params) => options.onShowDocument?.(params as ShowDocumentParams) ?? { success: false });
|
|
278
|
+
this.connection.onNotification('window/showMessage', (params) => options.onMessage?.(params as ShowMessageParams));
|
|
279
|
+
this.connection.onNotification('window/logMessage', (params) => options.onLog?.(params as LogMessageParams));
|
|
280
|
+
this.connection.onNotification('$/logTrace', (params) => options.onTrace?.(params as LogTraceParams));
|
|
281
|
+
this.connection.onNotification('telemetry/event', (params) => options.onTelemetry?.(params));
|
|
215
282
|
this.connection.onRequest('window/workDoneProgress/create', () => null);
|
|
216
283
|
|
|
217
284
|
this.connection.onRequest('client/registerCapability', (params) => {
|
|
@@ -257,7 +324,7 @@ export class LspSession {
|
|
|
257
324
|
}
|
|
258
325
|
|
|
259
326
|
get workspaceFolders(): WorkspaceFolder[] | null {
|
|
260
|
-
return this.
|
|
327
|
+
return this.folders === null ? null : [...this.folders];
|
|
261
328
|
}
|
|
262
329
|
|
|
263
330
|
get syncOptions(): TextDocumentSyncOptions {
|
|
@@ -284,6 +351,8 @@ export class LspSession {
|
|
|
284
351
|
rootUri: this.options.rootUri ?? null,
|
|
285
352
|
workspaceFolders: this.workspaceFolders,
|
|
286
353
|
clientInfo: this.options.clientInfo ?? { name: '@adecore/lsp' },
|
|
354
|
+
locale: this.options.locale,
|
|
355
|
+
trace: this.options.trace ?? 'off',
|
|
287
356
|
capabilities: clientCapabilities(this.options),
|
|
288
357
|
initializationOptions: this.options.initializationOptions
|
|
289
358
|
});
|
|
@@ -342,7 +411,17 @@ export class LspSession {
|
|
|
342
411
|
}
|
|
343
412
|
}
|
|
344
413
|
|
|
345
|
-
|
|
414
|
+
if (method === 'textDocument/willSaveWaitUntil' && this.syncOptions.willSaveWaitUntil) {
|
|
415
|
+
candidates.push({});
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
const content = this.serverCapabilities.workspace?.textDocumentContent;
|
|
419
|
+
|
|
420
|
+
if (method === 'workspace/textDocumentContent' && content !== undefined) {
|
|
421
|
+
candidates.push({ schemes: content.schemes });
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
const provider = PROVIDERS[method] === undefined ? undefined : this.serverCapabilities[PROVIDERS[method]];
|
|
346
425
|
|
|
347
426
|
if (provider === true) {
|
|
348
427
|
candidates.push({});
|
|
@@ -393,6 +472,103 @@ export class LspSession {
|
|
|
393
472
|
return this.request('workspaceSymbol/resolve', symbol, options);
|
|
394
473
|
}
|
|
395
474
|
|
|
475
|
+
/*
|
|
476
|
+
* The diagnostics of every file of the project, for a server that offers them. `previousResultIds` are what the
|
|
477
|
+
* client last heard per document, so the server can answer `unchanged`; `onPartialResult` streams reports while it works.
|
|
478
|
+
*/
|
|
479
|
+
workspaceDiagnostics(
|
|
480
|
+
previousResultIds: readonly PreviousResultId[] = [],
|
|
481
|
+
identifier?: string,
|
|
482
|
+
options?: RequestOptions
|
|
483
|
+
): Promise<WorkspaceDiagnosticReport> {
|
|
484
|
+
if (!this.supports('workspace/diagnostic')) {
|
|
485
|
+
return Promise.reject(new LspError('Server does not support workspace diagnostics', ErrorCodes.MethodNotFound));
|
|
486
|
+
}
|
|
487
|
+
return this.request('workspace/diagnostic', { identifier, previousResultIds: [...previousResultIds] }, options);
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
/* The text of a document of a scheme the server owns, such as a decompiled class, which is shown read-only. */
|
|
491
|
+
textDocumentContent(uri: string, options?: RequestOptions): Promise<TextDocumentContentResult> {
|
|
492
|
+
const scheme = uri.slice(0, Math.max(0, uri.indexOf(':')));
|
|
493
|
+
if (!this.providerOptions('workspace/textDocumentContent')?.schemes?.includes(scheme)) {
|
|
494
|
+
return Promise.reject(new LspError(`Server does not serve documents of ${scheme}:`, ErrorCodes.MethodNotFound));
|
|
495
|
+
}
|
|
496
|
+
return this.request('workspace/textDocumentContent', { uri }, options);
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
/* Changes the trace level the server sends `$/logTrace` at. */
|
|
500
|
+
setTrace(value: TraceValue): Promise<void> {
|
|
501
|
+
return this.notify('$/setTrace', { value });
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
/* Asks the server to stop work it reported as cancellable under `token`. */
|
|
505
|
+
cancelWorkDoneProgress(token: ProgressToken): Promise<void> {
|
|
506
|
+
return this.notify('window/workDoneProgress/cancel', { token });
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
/* Adds and removes folders of the workspace, and tells a server that asked to hear about it. */
|
|
510
|
+
async changeWorkspaceFolders(event: WorkspaceFoldersChangeEvent): Promise<void> {
|
|
511
|
+
const removed = new Set(event.removed.map((folder) => fileUriKey(folder.uri)));
|
|
512
|
+
const kept = (this.folders ?? []).filter((folder) => !removed.has(fileUriKey(folder.uri)));
|
|
513
|
+
const known = new Set(kept.map((folder) => fileUriKey(folder.uri)));
|
|
514
|
+
this.folders = [...kept, ...event.added.filter((folder) => !known.has(fileUriKey(folder.uri)))];
|
|
515
|
+
const notifications = this.serverCapabilities.workspace?.workspaceFolders?.changeNotifications;
|
|
516
|
+
const registered = [...this.registrations.values()].some((registration) => registration.method === 'workspace/didChangeWorkspaceFolders');
|
|
517
|
+
if (this.phase === 'ready' && (notifications === true || typeof notifications === 'string' || registered)) {
|
|
518
|
+
await this.notify('workspace/didChangeWorkspaceFolders', { event: { added: [...event.added], removed: [...event.removed] } });
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
/* Whether the server synchronizes notebooks of this type and scheme; the `notebook*` methods do nothing for one it does not. */
|
|
523
|
+
syncsNotebook(notebook: Pick<NotebookDocument, 'uri' | 'notebookType'>): boolean {
|
|
524
|
+
const selectors = this.serverCapabilities.notebookDocumentSync?.notebookSelector ?? [];
|
|
525
|
+
const scheme = notebook.uri.slice(0, Math.max(0, notebook.uri.indexOf(':')));
|
|
526
|
+
return selectors.some(({ notebook: filter }) => {
|
|
527
|
+
if (filter === undefined) {
|
|
528
|
+
return true;
|
|
529
|
+
}
|
|
530
|
+
if (typeof filter === 'string') {
|
|
531
|
+
return filter === '*' || filter === notebook.notebookType;
|
|
532
|
+
}
|
|
533
|
+
return (
|
|
534
|
+
(!filter.notebookType || filter.notebookType === notebook.notebookType) &&
|
|
535
|
+
(!filter.scheme || filter.scheme === scheme) &&
|
|
536
|
+
(!filter.pattern || globMatch(decodeURIComponent(new URL(notebook.uri).pathname), filter.pattern))
|
|
537
|
+
);
|
|
538
|
+
});
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
/* Opens a notebook with the documents of its cells, which do not go through `openDocument`. */
|
|
542
|
+
async openNotebook(notebook: NotebookDocument, cellTextDocuments: readonly DocumentItem[]): Promise<void> {
|
|
543
|
+
if (this.syncsNotebook(notebook)) {
|
|
544
|
+
await this.notify('notebookDocument/didOpen', {
|
|
545
|
+
notebookDocument: notebook,
|
|
546
|
+
cellTextDocuments: cellTextDocuments.map((item) => ({ ...item, version: item.version ?? 0 }))
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
async changeNotebook(notebook: Pick<NotebookDocument, 'uri' | 'notebookType' | 'version'>, change: NotebookDocumentChangeEvent): Promise<void> {
|
|
552
|
+
if (this.syncsNotebook(notebook)) {
|
|
553
|
+
await this.notify('notebookDocument/didChange', { notebookDocument: { uri: notebook.uri, version: notebook.version }, change });
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
async saveNotebook(notebook: Pick<NotebookDocument, 'uri' | 'notebookType'>): Promise<void> {
|
|
558
|
+
if (this.syncsNotebook(notebook) && this.serverCapabilities.notebookDocumentSync?.save === true) {
|
|
559
|
+
await this.notify('notebookDocument/didSave', { notebookDocument: { uri: notebook.uri } });
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
async closeNotebook(notebook: Pick<NotebookDocument, 'uri' | 'notebookType'>, cellUris: readonly string[]): Promise<void> {
|
|
564
|
+
if (this.syncsNotebook(notebook)) {
|
|
565
|
+
await this.notify('notebookDocument/didClose', {
|
|
566
|
+
notebookDocument: { uri: notebook.uri },
|
|
567
|
+
cellTextDocuments: cellUris.map((uri) => ({ uri }))
|
|
568
|
+
});
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
|
|
396
572
|
async setConfiguration(settings: Record<string, unknown>): Promise<void> {
|
|
397
573
|
this.configuration = settings;
|
|
398
574
|
await this.notify('workspace/didChangeConfiguration', { settings });
|
|
@@ -591,7 +767,13 @@ function clientCapabilities(options: LspSessionOptions): object {
|
|
|
591
767
|
const symbolKind = { valueSet: Array.from({ length: 26 }, (_, index) => index + 1) };
|
|
592
768
|
|
|
593
769
|
return {
|
|
594
|
-
general: {
|
|
770
|
+
general: {
|
|
771
|
+
positionEncodings: ['utf-16'],
|
|
772
|
+
staleRequestSupport: { cancel: true, retryOnContentModified: [] },
|
|
773
|
+
regularExpressions: { engine: 'ECMAScript', version: 'ES2020' },
|
|
774
|
+
markdown: { parser: 'remark', version: '15' }
|
|
775
|
+
},
|
|
776
|
+
notebookDocument: { synchronization: { dynamicRegistration: false, executionSummarySupport: true } },
|
|
595
777
|
workspace: {
|
|
596
778
|
applyEdit: !!options.onApplyEdit,
|
|
597
779
|
workspaceEdit: { documentChanges: true, resourceOperations: [...(options.resourceOperations ?? ['create', 'rename'])], failureHandling: 'abort' },
|
|
@@ -612,11 +794,18 @@ function clientCapabilities(options: LspSessionOptions): object {
|
|
|
612
794
|
semanticTokens: { refreshSupport: true },
|
|
613
795
|
inlayHint: { refreshSupport: true },
|
|
614
796
|
codeLens: { refreshSupport: true },
|
|
615
|
-
diagnostics: { refreshSupport: true }
|
|
797
|
+
diagnostics: { refreshSupport: true },
|
|
798
|
+
inlineValue: { refreshSupport: true },
|
|
799
|
+
foldingRange: { refreshSupport: true },
|
|
800
|
+
textDocumentContent: { dynamicRegistration: false }
|
|
801
|
+
},
|
|
802
|
+
window: {
|
|
803
|
+
workDoneProgress: true,
|
|
804
|
+
showMessage: { messageActionItem: { additionalPropertiesSupport: false } },
|
|
805
|
+
showDocument: { support: !!options.onShowDocument }
|
|
616
806
|
},
|
|
617
|
-
window: { workDoneProgress: true, showMessage: { messageActionItem: { additionalPropertiesSupport: false } } },
|
|
618
807
|
textDocument: {
|
|
619
|
-
synchronization: { dynamicRegistration: false, didSave: true },
|
|
808
|
+
synchronization: { dynamicRegistration: false, willSave: true, willSaveWaitUntil: true, didSave: true },
|
|
620
809
|
publishDiagnostics: {
|
|
621
810
|
versionSupport: true,
|
|
622
811
|
relatedInformation: true,
|
|
@@ -628,6 +817,7 @@ function clientCapabilities(options: LspSessionOptions): object {
|
|
|
628
817
|
...dynamic,
|
|
629
818
|
contextSupport: true,
|
|
630
819
|
insertTextMode: 1,
|
|
820
|
+
completionItemKind: { valueSet: Array.from({ length: 25 }, (_, index) => index + 1) },
|
|
631
821
|
...(options.completionItemDefaults?.length ? { completionList: { itemDefaults: [...options.completionItemDefaults] } } : {}),
|
|
632
822
|
completionItem: {
|
|
633
823
|
snippetSupport: options.snippetSupport ?? false,
|
|
@@ -635,6 +825,7 @@ function clientCapabilities(options: LspSessionOptions): object {
|
|
|
635
825
|
commitCharactersSupport: true,
|
|
636
826
|
documentationFormat: ['markdown', 'plaintext'],
|
|
637
827
|
deprecatedSupport: true,
|
|
828
|
+
tagSupport: { valueSet: [1] },
|
|
638
829
|
preselectSupport: true,
|
|
639
830
|
insertReplaceSupport: true,
|
|
640
831
|
labelDetailsSupport: true,
|
|
@@ -682,11 +873,23 @@ function clientCapabilities(options: LspSessionOptions): object {
|
|
|
682
873
|
},
|
|
683
874
|
resolveSupport: { properties: ['edit', 'command'] }
|
|
684
875
|
},
|
|
685
|
-
codeLens: dynamic,
|
|
876
|
+
codeLens: { ...dynamic, resolveSupport: { properties: ['command'] } },
|
|
686
877
|
formatting: dynamic,
|
|
687
|
-
rangeFormatting: dynamic,
|
|
878
|
+
rangeFormatting: { ...dynamic, rangesSupport: true },
|
|
688
879
|
onTypeFormatting: dynamic,
|
|
689
|
-
foldingRange: {
|
|
880
|
+
foldingRange: {
|
|
881
|
+
...dynamic,
|
|
882
|
+
lineFoldingOnly: false,
|
|
883
|
+
foldingRangeKind: { valueSet: ['comment', 'imports', 'region'] },
|
|
884
|
+
foldingRange: { collapsedText: false }
|
|
885
|
+
},
|
|
886
|
+
documentLink: { ...dynamic, tooltipSupport: true },
|
|
887
|
+
colorProvider: dynamic,
|
|
888
|
+
linkedEditingRange: dynamic,
|
|
889
|
+
moniker: dynamic,
|
|
890
|
+
typeHierarchy: dynamic,
|
|
891
|
+
inlineValue: dynamic,
|
|
892
|
+
inlineCompletion: dynamic,
|
|
690
893
|
selectionRange: dynamic,
|
|
691
894
|
semanticTokens: {
|
|
692
895
|
...dynamic,
|
package/src/unserved.ts
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { ErrorCodes, LspError } from './connection.ts';
|
|
2
|
+
import type { Disposable } from './protocol.ts';
|
|
3
|
+
import type { LanguageService } from './service.ts';
|
|
4
|
+
|
|
5
|
+
const REQUESTS = [
|
|
6
|
+
'completion',
|
|
7
|
+
'resolveCompletion',
|
|
8
|
+
'hover',
|
|
9
|
+
'signatureHelp',
|
|
10
|
+
'definition',
|
|
11
|
+
'declaration',
|
|
12
|
+
'typeDefinition',
|
|
13
|
+
'implementation',
|
|
14
|
+
'references',
|
|
15
|
+
'documentHighlights',
|
|
16
|
+
'documentSymbols',
|
|
17
|
+
'prepareRename',
|
|
18
|
+
'rename',
|
|
19
|
+
'codeActions',
|
|
20
|
+
'resolveCodeAction',
|
|
21
|
+
'executeCommand',
|
|
22
|
+
'formatting',
|
|
23
|
+
'rangeFormatting',
|
|
24
|
+
'semanticTokens',
|
|
25
|
+
'semanticTokensDelta',
|
|
26
|
+
'semanticTokensRange',
|
|
27
|
+
'inlayHints',
|
|
28
|
+
'resolveInlayHint',
|
|
29
|
+
'workspaceSymbols',
|
|
30
|
+
'foldingRanges',
|
|
31
|
+
'selectionRanges',
|
|
32
|
+
'codeLenses',
|
|
33
|
+
'resolveCodeLens'
|
|
34
|
+
] as const satisfies readonly (keyof LanguageService)[];
|
|
35
|
+
|
|
36
|
+
const NOTHING: Disposable = { dispose: () => undefined };
|
|
37
|
+
|
|
38
|
+
function refuse(): Promise<never> {
|
|
39
|
+
return Promise.reject(new LspError('No language server serves this document', ErrorCodes.MethodNotFound));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/*
|
|
43
|
+
* A service for documents no server serves: it takes documents and answers `supports` with false, so a
|
|
44
|
+
* feature that checks first never asks, and rejects every request with an `LspError` of code -32601.
|
|
45
|
+
*/
|
|
46
|
+
export function unservedLanguageService(): LanguageService {
|
|
47
|
+
// Every request has the same refusal whatever its parameters, which the record type cannot say.
|
|
48
|
+
const requests = Object.fromEntries(REQUESTS.map((name) => [name, refuse])) as unknown as Pick<LanguageService, (typeof REQUESTS)[number]>;
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
...requests,
|
|
52
|
+
openDocument: () => Promise.resolve(),
|
|
53
|
+
changeDocument: () => Promise.resolve(),
|
|
54
|
+
closeDocument: () => Promise.resolve(),
|
|
55
|
+
supports: () => false,
|
|
56
|
+
providerOptions: () => undefined,
|
|
57
|
+
onProvidersChanged: () => NOTHING,
|
|
58
|
+
onDiagnostics: () => NOTHING
|
|
59
|
+
};
|
|
60
|
+
}
|