@platformos/codemirror-language-client 0.0.2

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.
Files changed (127) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/README.md +91 -0
  3. package/dist/esm/CodeMirrorLanguageClient.d.ts +60 -0
  4. package/dist/esm/CodeMirrorLanguageClient.js +80 -0
  5. package/dist/esm/CodeMirrorLanguageClient.js.map +1 -0
  6. package/dist/esm/LanguageClient.d.ts +112 -0
  7. package/dist/esm/LanguageClient.js +187 -0
  8. package/dist/esm/LanguageClient.js.map +1 -0
  9. package/dist/esm/extensions/client.d.ts +6 -0
  10. package/dist/esm/extensions/client.js +12 -0
  11. package/dist/esm/extensions/client.js.map +1 -0
  12. package/dist/esm/extensions/complete.d.ts +17 -0
  13. package/dist/esm/extensions/complete.js +202 -0
  14. package/dist/esm/extensions/complete.js.map +1 -0
  15. package/dist/esm/extensions/complete.spec.d.ts +1 -0
  16. package/dist/esm/extensions/complete.spec.js +189 -0
  17. package/dist/esm/extensions/complete.spec.js.map +1 -0
  18. package/dist/esm/extensions/documentHighlights.d.ts +14 -0
  19. package/dist/esm/extensions/documentHighlights.js +78 -0
  20. package/dist/esm/extensions/documentHighlights.js.map +1 -0
  21. package/dist/esm/extensions/documentHighlights.spec.d.ts +1 -0
  22. package/dist/esm/extensions/documentHighlights.spec.js +99 -0
  23. package/dist/esm/extensions/documentHighlights.spec.js.map +1 -0
  24. package/dist/esm/extensions/hover.d.ts +16 -0
  25. package/dist/esm/extensions/hover.js +49 -0
  26. package/dist/esm/extensions/hover.js.map +1 -0
  27. package/dist/esm/extensions/hover.spec.d.ts +1 -0
  28. package/dist/esm/extensions/hover.spec.js +59 -0
  29. package/dist/esm/extensions/hover.spec.js.map +1 -0
  30. package/dist/esm/extensions/index.d.ts +6 -0
  31. package/dist/esm/extensions/index.js +7 -0
  32. package/dist/esm/extensions/index.js.map +1 -0
  33. package/dist/esm/extensions/lspLinter.d.ts +23 -0
  34. package/dist/esm/extensions/lspLinter.js +104 -0
  35. package/dist/esm/extensions/lspLinter.js.map +1 -0
  36. package/dist/esm/extensions/lspLinter.spec.d.ts +1 -0
  37. package/dist/esm/extensions/lspLinter.spec.js +141 -0
  38. package/dist/esm/extensions/lspLinter.spec.js.map +1 -0
  39. package/dist/esm/extensions/snippet.d.ts +19 -0
  40. package/dist/esm/extensions/snippet.js +25 -0
  41. package/dist/esm/extensions/snippet.js.map +1 -0
  42. package/dist/esm/extensions/snippet.spec.d.ts +1 -0
  43. package/dist/esm/extensions/snippet.spec.js +23 -0
  44. package/dist/esm/extensions/snippet.spec.js.map +1 -0
  45. package/dist/esm/extensions/textDocumentSync.d.ts +4 -0
  46. package/dist/esm/extensions/textDocumentSync.js +109 -0
  47. package/dist/esm/extensions/textDocumentSync.js.map +1 -0
  48. package/dist/esm/extensions/textDocumentSync.spec.d.ts +1 -0
  49. package/dist/esm/extensions/textDocumentSync.spec.js +163 -0
  50. package/dist/esm/extensions/textDocumentSync.spec.js.map +1 -0
  51. package/dist/esm/index.d.ts +2 -0
  52. package/dist/esm/index.js +3 -0
  53. package/dist/esm/index.js.map +1 -0
  54. package/dist/esm/test/MockClient.d.ts +18 -0
  55. package/dist/esm/test/MockClient.js +63 -0
  56. package/dist/esm/test/MockClient.js.map +1 -0
  57. package/dist/esm/tsconfig.tsbuildInfo +1 -0
  58. package/dist/esm/utils/simpleStateField.d.ts +3 -0
  59. package/dist/esm/utils/simpleStateField.js +17 -0
  60. package/dist/esm/utils/simpleStateField.js.map +1 -0
  61. package/dist/umd/CodeMirrorLanguageClient.d.ts +60 -0
  62. package/dist/umd/CodeMirrorLanguageClient.js +94 -0
  63. package/dist/umd/CodeMirrorLanguageClient.js.map +1 -0
  64. package/dist/umd/LanguageClient.d.ts +112 -0
  65. package/dist/umd/LanguageClient.js +202 -0
  66. package/dist/umd/LanguageClient.js.map +1 -0
  67. package/dist/umd/extensions/client.d.ts +6 -0
  68. package/dist/umd/extensions/client.js +25 -0
  69. package/dist/umd/extensions/client.js.map +1 -0
  70. package/dist/umd/extensions/complete.d.ts +17 -0
  71. package/dist/umd/extensions/complete.js +217 -0
  72. package/dist/umd/extensions/complete.js.map +1 -0
  73. package/dist/umd/extensions/documentHighlights.d.ts +14 -0
  74. package/dist/umd/extensions/documentHighlights.js +93 -0
  75. package/dist/umd/extensions/documentHighlights.js.map +1 -0
  76. package/dist/umd/extensions/hover.d.ts +16 -0
  77. package/dist/umd/extensions/hover.js +64 -0
  78. package/dist/umd/extensions/hover.js.map +1 -0
  79. package/dist/umd/extensions/index.d.ts +6 -0
  80. package/dist/umd/extensions/index.js +36 -0
  81. package/dist/umd/extensions/index.js.map +1 -0
  82. package/dist/umd/extensions/lspLinter.d.ts +23 -0
  83. package/dist/umd/extensions/lspLinter.js +119 -0
  84. package/dist/umd/extensions/lspLinter.js.map +1 -0
  85. package/dist/umd/extensions/snippet.d.ts +19 -0
  86. package/dist/umd/extensions/snippet.js +38 -0
  87. package/dist/umd/extensions/snippet.js.map +1 -0
  88. package/dist/umd/extensions/textDocumentSync.d.ts +4 -0
  89. package/dist/umd/extensions/textDocumentSync.js +122 -0
  90. package/dist/umd/extensions/textDocumentSync.js.map +1 -0
  91. package/dist/umd/index.d.ts +2 -0
  92. package/dist/umd/index.js +29 -0
  93. package/dist/umd/index.js.map +1 -0
  94. package/dist/umd/test/MockClient.d.ts +18 -0
  95. package/dist/umd/test/MockClient.js +77 -0
  96. package/dist/umd/test/MockClient.js.map +1 -0
  97. package/dist/umd/tsconfig.tsbuildInfo +1 -0
  98. package/dist/umd/utils/simpleStateField.d.ts +3 -0
  99. package/dist/umd/utils/simpleStateField.js +30 -0
  100. package/dist/umd/utils/simpleStateField.js.map +1 -0
  101. package/package.json +67 -0
  102. package/playground/src/index.html +10 -0
  103. package/playground/src/language-server-worker.ts +82 -0
  104. package/playground/src/playground.ts +251 -0
  105. package/playground/tsconfig.json +26 -0
  106. package/playground/webpack.config.js +85 -0
  107. package/src/CodeMirrorLanguageClient.ts +179 -0
  108. package/src/LanguageClient.ts +329 -0
  109. package/src/extensions/client.ts +17 -0
  110. package/src/extensions/complete.spec.ts +200 -0
  111. package/src/extensions/complete.ts +274 -0
  112. package/src/extensions/documentHighlights.spec.ts +111 -0
  113. package/src/extensions/documentHighlights.ts +91 -0
  114. package/src/extensions/hover.spec.ts +68 -0
  115. package/src/extensions/hover.ts +66 -0
  116. package/src/extensions/index.ts +19 -0
  117. package/src/extensions/lspLinter.spec.ts +156 -0
  118. package/src/extensions/lspLinter.ts +154 -0
  119. package/src/extensions/snippet.spec.ts +31 -0
  120. package/src/extensions/snippet.ts +42 -0
  121. package/src/extensions/textDocumentSync.spec.ts +188 -0
  122. package/src/extensions/textDocumentSync.ts +138 -0
  123. package/src/index.ts +2 -0
  124. package/src/test/MockClient.ts +96 -0
  125. package/src/utils/simpleStateField.ts +22 -0
  126. package/tsconfig.json +18 -0
  127. package/tsconfig.umd.json +9 -0
@@ -0,0 +1,179 @@
1
+ import { Extension } from '@codemirror/state';
2
+ import { ClientCapabilities, MarkupKind } from 'vscode-languageserver-protocol';
3
+
4
+ import { Dependencies, LanguageClient } from './LanguageClient';
5
+ import {
6
+ AutocompleteOptions,
7
+ DiagnosticRenderer,
8
+ HoverOptions,
9
+ HoverRenderer,
10
+ InfoRenderer,
11
+ LinterOptions,
12
+ clientFacet,
13
+ diagnosticRendererFacet,
14
+ fileUriFacet,
15
+ infoRendererFacet,
16
+ lspComplete,
17
+ lspDocumentHighlights,
18
+ lspHover,
19
+ lspLinter,
20
+ serverCapabilitiesFacet,
21
+ textDocumentSync,
22
+ } from './extensions';
23
+ import { hoverRendererFacet } from './extensions/hover';
24
+
25
+ /**
26
+ * The client capabilities are how we tell the language server what
27
+ * features we support so that they have the opportunity to skip doing
28
+ * things that the client does not support. Everything is false by default,
29
+ * but we migth need to change that and this is where we'll do it.
30
+ */
31
+ const clientCapabilities: ClientCapabilities = {
32
+ textDocument: {
33
+ documentHighlight: {
34
+ dynamicRegistration: false,
35
+ },
36
+ completion: {
37
+ // We send the completion context to the server
38
+ contextSupport: true,
39
+ completionItem: {
40
+ snippetSupport: true,
41
+ insertReplaceSupport: true,
42
+ documentationFormat: [MarkupKind.PlainText, MarkupKind.Markdown],
43
+ commitCharactersSupport: false,
44
+ },
45
+ },
46
+ },
47
+ };
48
+
49
+ const defaultLogger = console.log.bind(console);
50
+
51
+ export { Dependencies };
52
+
53
+ export interface FeatureFlags {
54
+ shouldComplete: boolean;
55
+ shouldLint: boolean;
56
+ shouldHover: boolean;
57
+ }
58
+
59
+ export type ClientDependencies = Partial<Dependencies>;
60
+
61
+ export interface CodeMirrorDependencies {
62
+ /**
63
+ * The infoRenderer is a function that returns a DOM node that contains the documentation
64
+ * for a completion item. Presumably does markdown conversions to DOM nodes.
65
+ *
66
+ * A function that takes a completion object and returns a DOM node.
67
+ */
68
+ infoRenderer?: InfoRenderer;
69
+
70
+ /**
71
+ * Say you wanted to change the settings of the `autocomplete` extension,
72
+ * you'd do it with that.
73
+ */
74
+ autocompleteOptions?: AutocompleteOptions;
75
+
76
+ /**
77
+ * Say you wanted to change the settings of the `linter` extension,
78
+ * you'd do it with that.
79
+ */
80
+ linterOptions?: LinterOptions;
81
+
82
+ /**
83
+ * The diagnosticRenderer is a function that returns a DOM node that
84
+ * contains the content of a diagnostic. It overrides the default
85
+ * rendering logic for diagnostics.
86
+ */
87
+ diagnosticRenderer?: DiagnosticRenderer;
88
+
89
+ /**
90
+ * The hoverRenderer is a function that returns a DOM node that contains the documentation
91
+ * for the item under the cursor. The documentation is provided by the Language Server.
92
+ */
93
+ hoverRenderer?: HoverRenderer;
94
+
95
+ /**
96
+ * Say you wanted to change the settings of the `hoverTooltip` extension,
97
+ * you'd do it with that.
98
+ */
99
+ hoverOptions?: HoverOptions;
100
+ }
101
+
102
+ // There is one LanguageClient
103
+ // There is one LanguageServer
104
+ // There are many CodeMirror instances
105
+ export class CodeMirrorLanguageClient {
106
+ readonly client: LanguageClient;
107
+ private readonly infoRenderer: InfoRenderer | undefined;
108
+ private readonly autocompleteExtension: Extension;
109
+ private readonly diagnosticRenderer: DiagnosticRenderer | undefined;
110
+ private readonly linterExtension: Extension;
111
+ private readonly hoverRenderer: HoverRenderer | undefined;
112
+ private readonly hoverExtension: Extension;
113
+ private readonly documentHighlightsExtension: Extension;
114
+
115
+ constructor(
116
+ private readonly worker: Worker,
117
+ { log = defaultLogger, initializationOptions }: ClientDependencies = {},
118
+ {
119
+ infoRenderer,
120
+ autocompleteOptions,
121
+ diagnosticRenderer,
122
+ linterOptions,
123
+ hoverRenderer,
124
+ hoverOptions,
125
+ }: CodeMirrorDependencies = {},
126
+ ) {
127
+ this.client = new LanguageClient(worker, {
128
+ clientCapabilities,
129
+ initializationOptions,
130
+ log,
131
+ });
132
+ this.worker = worker;
133
+ this.infoRenderer = infoRenderer;
134
+ this.autocompleteExtension = lspComplete(autocompleteOptions);
135
+
136
+ this.diagnosticRenderer = diagnosticRenderer;
137
+ this.linterExtension = lspLinter(linterOptions);
138
+
139
+ this.hoverRenderer = hoverRenderer;
140
+ this.hoverExtension = lspHover(hoverOptions);
141
+
142
+ this.documentHighlightsExtension = lspDocumentHighlights();
143
+ }
144
+
145
+ public async start() {
146
+ await this.client.start();
147
+ }
148
+
149
+ public async stop() {
150
+ try {
151
+ await this.client.stop();
152
+ } finally {
153
+ this.worker.terminate();
154
+ }
155
+ }
156
+
157
+ public extension(
158
+ fileUri: string,
159
+ { shouldLint, shouldComplete, shouldHover }: FeatureFlags = {
160
+ shouldLint: true,
161
+ shouldComplete: true,
162
+ shouldHover: true,
163
+ },
164
+ ): Extension[] {
165
+ return [
166
+ clientFacet.of(this.client),
167
+ serverCapabilitiesFacet.of(this.client.serverCapabilities),
168
+ fileUriFacet.of(fileUri),
169
+ textDocumentSync,
170
+ infoRendererFacet.of(this.infoRenderer),
171
+ diagnosticRendererFacet.of(this.diagnosticRenderer),
172
+ hoverRendererFacet.of(this.hoverRenderer),
173
+ this.documentHighlightsExtension,
174
+ ]
175
+ .concat(shouldLint ? this.linterExtension : [])
176
+ .concat(shouldComplete ? this.autocompleteExtension : [])
177
+ .concat(shouldHover ? this.hoverExtension : []);
178
+ }
179
+ }
@@ -0,0 +1,329 @@
1
+ /* eslint-disable lines-between-class-members */
2
+ import {
3
+ CancellationToken,
4
+ CancellationTokenSource,
5
+ Disposable,
6
+ ExitNotification,
7
+ InitializeRequest,
8
+ InitializedNotification,
9
+ Message,
10
+ NotificationHandler,
11
+ NotificationMessage,
12
+ ProtocolNotificationType,
13
+ ProtocolRequestType,
14
+ ProtocolRequestType0,
15
+ RequestHandler,
16
+ RequestMessage,
17
+ ResponseError,
18
+ ResponseMessage,
19
+ ShutdownRequest,
20
+ MessageSignature,
21
+ ProtocolNotificationType0,
22
+ ServerCapabilities,
23
+ ClientCapabilities,
24
+ } from 'vscode-languageserver-protocol';
25
+
26
+ export interface PromiseCompletion {
27
+ resolve(value: unknown): void;
28
+ reject(error: unknown): void;
29
+ }
30
+
31
+ export interface Dependencies {
32
+ clientCapabilities: ClientCapabilities;
33
+ initializationOptions?: any;
34
+ log(...args: any[]): void;
35
+ }
36
+
37
+ const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
38
+
39
+ // Useful for mocking/stubbing the client in our tests. aka our "public" API.
40
+ export interface AbstractLanguageClient {
41
+ clientCapabilities: ClientCapabilities;
42
+ serverCapabilities: ServerCapabilities | null;
43
+ serverInfo: any;
44
+ onRequest: LanguageClient['onRequest'];
45
+ onNotification: LanguageClient['onNotification'];
46
+ sendRequest: LanguageClient['sendRequest'];
47
+ sendNotification: LanguageClient['sendNotification'];
48
+ }
49
+
50
+ export class LanguageClient extends EventTarget implements AbstractLanguageClient {
51
+ public readonly clientCapabilities: ClientCapabilities;
52
+ public readonly initializationOptions: any;
53
+ public serverCapabilities: ServerCapabilities | null;
54
+ public serverInfo: any;
55
+
56
+ private requestId: number;
57
+ private requests: Map<string, PromiseCompletion>;
58
+ private dispose: () => void;
59
+ private disposables: Disposable[];
60
+ private log: Dependencies['log'];
61
+
62
+ constructor(public readonly worker: Worker, dependencies: Dependencies) {
63
+ super();
64
+ this.requests = new Map();
65
+ this.requestId = 0;
66
+ this.dispose = () => {};
67
+ this.disposables = [];
68
+ this.clientCapabilities = dependencies.clientCapabilities;
69
+ this.initializationOptions = dependencies.initializationOptions;
70
+ this.log = dependencies.log;
71
+ this.serverCapabilities = null;
72
+ this.serverInfo = null;
73
+ }
74
+
75
+ /**
76
+ * Lifecycle method to start the server
77
+ */
78
+ async start() {
79
+ /**
80
+ * Here we setup the web worker event handler.
81
+ * We route the message sent via the worker's postMessage to our event handler.
82
+ */
83
+ const handler = (ev: MessageEvent<Message>) => this.handleMessage(ev.data);
84
+ this.worker.addEventListener('message', handler);
85
+ this.dispose = () => {
86
+ this.worker.removeEventListener('message', handler);
87
+ };
88
+
89
+ /**
90
+ * We send the `initialize` request and obtain the capabilities supported by
91
+ * the language server.
92
+ *
93
+ * https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#initialize
94
+ */
95
+ const response = await this.sendRequest(InitializeRequest.type, {
96
+ capabilities: this.clientCapabilities,
97
+ initializationOptions: this.initializationOptions,
98
+ processId: 0,
99
+ rootUri: 'browser:///',
100
+ });
101
+
102
+ /**
103
+ * We unpack the response from the server and remember the capabilities.
104
+ * Those will be useful.
105
+ */
106
+ this.serverCapabilities = response.capabilities;
107
+ this.serverInfo = response.serverInfo;
108
+
109
+ /**
110
+ * Once we're ready, we send the `initialized` notification to tell the
111
+ * server that we're ready to roll!
112
+ *
113
+ * https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#initialized
114
+ */
115
+ this.sendNotification(InitializedNotification.type, {});
116
+ }
117
+
118
+ /**
119
+ * Lifecycle method to stop the server
120
+ */
121
+ async stop() {
122
+ await Promise.race([this.sendRequest(ShutdownRequest.type), sleep(1000)]);
123
+ this.disposables.forEach((disposable) => disposable.dispose());
124
+ this.disposables = [];
125
+ this.dispose();
126
+ this.sendNotification(ExitNotification.type);
127
+ }
128
+
129
+ /**
130
+ * For when you want to handle requests sent from the language server.
131
+ *
132
+ * @param {MessageSignature} type this is an export from vscode-languageserver-protocol
133
+ * @param {RequestHandler} handler a type-inferred event handler whose return value matches the MessageSignature
134
+ *
135
+ * @example
136
+ * import {ApplyWorkspaceEditRequest, ApplyWorkspaceEditResponse} from 'vscode-languageserver-protocol';
137
+ * client.onRequest(ApplyWorkspaceEditRequest.type, (params, cancellationToken) => {
138
+ * // `params` and `cancellationToken` are typed inferred
139
+ * // The return value must match the MessageSignature (ApplyWorkspaceEditResponse)
140
+ * return result as ApplyWorkspaceEditResponse;
141
+ * })
142
+ */
143
+ onRequest<P, R, PR, E, RO>(
144
+ type: ProtocolRequestType<P, R, PR, E, RO>,
145
+ handler: RequestHandler<P, R, E>,
146
+ ): Disposable;
147
+ onRequest<P>(type: string | MessageSignature, handler: RequestHandler<P, any, any>): Disposable {
148
+ const method = typeof type === 'string' ? type : type.method;
149
+ const callback = async (event: Event) => {
150
+ const { params, id } = (event as CustomEvent<RequestMessage>).detail;
151
+ const cancellationToken: CancellationToken = new CancellationTokenSource().token;
152
+ try {
153
+ const response = await handler(params as any as P, cancellationToken);
154
+ if (response && typeof response === 'object' && 'code' in response) {
155
+ this.sendResponse(id!, undefined, response as any as ResponseError);
156
+ } else {
157
+ this.sendResponse(id!, response, undefined);
158
+ }
159
+ } catch (error) {
160
+ this.sendResponse(
161
+ id!,
162
+ undefined,
163
+ new ResponseError(1, error instanceof Error ? error.message : (error as string)),
164
+ );
165
+ }
166
+ };
167
+
168
+ this.addEventListener(method, callback);
169
+ return this.disposable(() => this.removeEventListener(method, callback));
170
+ }
171
+
172
+ /**
173
+ * For when you want to handle notifications sent from the language server.
174
+ *
175
+ * @param {MessageSignature} type this is an export from vscode-languageserver-protocol
176
+ * @param {NotificationHandler} handler this is a type-inferred event handler
177
+ *
178
+ * @example
179
+ * import {PublishDiagnosticsNotification} from 'vscode-languageserver-protocol';
180
+ * client.onNotification(PublishDiagnosticsNotification.type, (params) => {
181
+ * // the type of `params` is inferred from the first argument
182
+ * });
183
+ */
184
+ onNotification<P, RO>(
185
+ type: ProtocolNotificationType<P, RO>,
186
+ handler: NotificationHandler<P>,
187
+ ): Disposable;
188
+ onNotification<P>(type: string | MessageSignature, handler: NotificationHandler<P>): Disposable {
189
+ const method = typeof type === 'string' ? type : type.method;
190
+ const callback = (event: Event) => {
191
+ const { params } = (event as CustomEvent<NotificationMessage>).detail;
192
+ handler(params as any as P);
193
+ };
194
+
195
+ this.addEventListener(method, callback);
196
+ return this.disposable(() => this.removeEventListener(method, callback));
197
+ }
198
+
199
+ /**
200
+ * Send a request to the language server and await a response.
201
+ *
202
+ * @param {MessageSignature} type this is an export from vscode-languageserver-protocol
203
+ * @param [params] its type is inferred from the first argument
204
+ * @returns {any} response, its type is inferred from the first argument
205
+ *
206
+ * @example
207
+ *
208
+ * import {CompletionRequest} from 'vscode-languageserver-protocol';
209
+ * const completions = await client.sendRequest(
210
+ * CompletionRequest.type,
211
+ * params
212
+ * );
213
+ */
214
+ public sendRequest<R, PR, E, RO>(type: ProtocolRequestType0<R, PR, E, RO>): Promise<R>;
215
+ public sendRequest<P, R, PR, E, RO>(
216
+ type: ProtocolRequestType<P, R, PR, E, RO>,
217
+ params?: P,
218
+ ): Promise<R>;
219
+ public async sendRequest<R>(type: string | MessageSignature, params?: any): Promise<R> {
220
+ this.requestId += 1;
221
+ const requestId = this.requestId;
222
+ const method = typeof type === 'string' ? type : type.method;
223
+ const request: RequestMessage = {
224
+ jsonrpc: '2.0',
225
+ id: requestId,
226
+ method,
227
+ };
228
+ if (params) request.params = params;
229
+
230
+ this.log(`Client->Server ${request.method} request [${request.id}]`, request);
231
+ this.sendMessage(request);
232
+
233
+ return new Promise((resolve, reject) => {
234
+ this.requests.set(requestId.toString(), { resolve, reject });
235
+ });
236
+ }
237
+
238
+ /**
239
+ * Send a notification to the language server. Notifications are fire and forget.
240
+ *
241
+ * @param {MessageSignature} type this is an export from vscode-languageserver-protocol
242
+ * @param [params] its type is inferred from the first argument
243
+ *
244
+ * @example
245
+ *
246
+ * import {DidChangeTextDocumentNotification} from 'vscode-languageserver-protocol';
247
+ * client.sendNotification(
248
+ * DidChangeTextDocumentNotification.type,
249
+ * params
250
+ * );
251
+ */
252
+ public sendNotification<RO>(type: ProtocolNotificationType0<RO>): void;
253
+ public sendNotification<P, RO>(type: ProtocolNotificationType<P, RO>, params?: P): void;
254
+ public sendNotification(type: string | MessageSignature, params?: any): void {
255
+ const method = typeof type === 'string' ? type : type.method;
256
+ const notification: NotificationMessage = {
257
+ jsonrpc: '2.0',
258
+ method,
259
+ };
260
+ if (params) notification.params = params;
261
+ this.log(`Client->Server ${method} notification`, notification);
262
+ this.sendMessage(notification);
263
+ }
264
+
265
+ private sendResponse(id: number | string, result: any, error?: ResponseError) {
266
+ const response: ResponseMessage = { jsonrpc: '2.0', id };
267
+ if (result !== undefined) response.result = result;
268
+ if (error !== undefined) response.error = error.toJson();
269
+ this.log(`Client->Server response [${id}]`, response);
270
+ this.sendMessage(response);
271
+ }
272
+
273
+ private sendMessage(message: Message) {
274
+ this.worker.postMessage(message);
275
+ }
276
+
277
+ /**
278
+ * Map messages to their correct destition.
279
+ * - Responses -> resolve or reject the corresponding Request
280
+ * - Notification -> emit(message.method, message)
281
+ * - Request -> emit(message.method, message)
282
+ */
283
+ private handleMessage(message: any) {
284
+ if (isResponse(message)) {
285
+ this.log(`Server->Client response [${message.id}]`, message);
286
+ const id = message.id!.toString();
287
+ if ('result' in message) {
288
+ this.requests.get(id)!.resolve(message.result!);
289
+ } else {
290
+ this.requests.get(id)!.reject(message.error);
291
+ }
292
+ this.requests.delete(id);
293
+ } else if (isRequest(message)) {
294
+ this.log(`Server->Client ${message.method} request [${message.id}]`, message);
295
+ this.dispatchEvent(new CustomEvent<RequestMessage>(message.method, { detail: message }));
296
+ } else if (isNotification(message)) {
297
+ this.log(`Server->Client ${message.method} notification`, message);
298
+ this.dispatchEvent(
299
+ new CustomEvent(message.method, {
300
+ detail: message,
301
+ }),
302
+ );
303
+ } else {
304
+ this.log('what?', message);
305
+ }
306
+ }
307
+
308
+ private disposable(dispose: () => void) {
309
+ const disp = disposable(dispose);
310
+ this.disposables.push(disp);
311
+ return disp;
312
+ }
313
+ }
314
+
315
+ export function disposable(dispose: () => void): Disposable {
316
+ return { dispose };
317
+ }
318
+
319
+ function isResponse(message: any): message is ResponseMessage {
320
+ return 'id' in message && ('error' in message || 'result' in message);
321
+ }
322
+
323
+ function isRequest(message: any): message is RequestMessage {
324
+ return 'id' in message && 'method' in message;
325
+ }
326
+
327
+ function isNotification(message: any): message is NotificationMessage {
328
+ return !('id' in message) && 'method' in message;
329
+ }
@@ -0,0 +1,17 @@
1
+ import { Facet } from '@codemirror/state';
2
+
3
+ import { AbstractLanguageClient } from '../LanguageClient';
4
+ import { ServerCapabilities } from 'vscode-languageserver-protocol';
5
+
6
+ export const clientFacet = Facet.define<AbstractLanguageClient, AbstractLanguageClient>({
7
+ combine: (values) => values[0],
8
+ static: true,
9
+ });
10
+
11
+ export const fileUriFacet = Facet.define<string, string>({
12
+ combine: (values) => values[0],
13
+ });
14
+
15
+ export const serverCapabilitiesFacet = Facet.define<ServerCapabilities | null, ServerCapabilities>({
16
+ combine: (values) => values[0] ?? {},
17
+ });