codemirror-languageserver 1.20.0 → 1.22.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/README.md +23 -1
- package/dist/index.js +40 -29
- package/dist/jsonrpc.d.ts +4 -0
- package/dist/plugin.d.ts +4 -2
- package/{demo/dist/demo.js → docs/dist/gh-pages.js} +4670 -3427
- package/docs/dist/lsp-worker.js +653 -0
- package/docs/gh-pages.js +115 -0
- package/docs/index.html +459 -0
- package/docs/lsp-worker.js +635 -0
- package/docs/worker-transport.js +50 -0
- package/package.json +5 -5
- package/rollup.config.gh-pages.js +34 -0
- package/demo/demo.js +0 -109
- package/demo/index.html +0 -17
- package/rollup.config.demo.js +0 -30
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
This plugin connects a [CodeMirror 6](https://codemirror.net/) editor with a [Language Server](https://microsoft.github.io/language-server-protocol/) over WebSocket, providing IDE-like features in the browser.
|
|
6
6
|
|
|
7
|
-
[How It Works](https://hjr265.me/blog/codemirror-lsp/)
|
|
7
|
+
[How It Works](https://hjr265.me/blog/codemirror-lsp/) | [Live Demo](https://furqansoftware.github.io/codemirror-languageserver/)
|
|
8
8
|
|
|
9
9
|
## Features
|
|
10
10
|
|
|
@@ -67,6 +67,28 @@ This sets up all built-in features: completion, hover, diagnostics, go-to-defini
|
|
|
67
67
|
| `synchronizationMethod` | `SynchronizationMethod` | `'full'` or `'incremental'` document sync (default: `'full'`) |
|
|
68
68
|
| `client` | `LanguageServerClient` | Share a client across multiple editor instances |
|
|
69
69
|
| `onCapabilities` | `(capabilities) => void` | Called when server capabilities are available |
|
|
70
|
+
| `onError` | `(error: Error) => void` | Called when the connection encounters an error |
|
|
71
|
+
| `onClose` | `() => void` | Called when the connection is closed |
|
|
72
|
+
|
|
73
|
+
## Error Handling
|
|
74
|
+
|
|
75
|
+
The plugin does not crash on connection failures. Use `onError` and `onClose` to handle disconnections:
|
|
76
|
+
|
|
77
|
+
```js
|
|
78
|
+
languageServer({
|
|
79
|
+
serverUri: 'ws://localhost:8080',
|
|
80
|
+
rootUri: 'file:///',
|
|
81
|
+
documentUri: `file:///${filename}`,
|
|
82
|
+
languageId: 'cpp',
|
|
83
|
+
onError(error) {
|
|
84
|
+
console.error('LSP error:', error);
|
|
85
|
+
},
|
|
86
|
+
onClose() {
|
|
87
|
+
console.log('LSP connection closed');
|
|
88
|
+
// Recreate the editor extensions to reconnect
|
|
89
|
+
},
|
|
90
|
+
})
|
|
91
|
+
```
|
|
70
92
|
|
|
71
93
|
## Shared Client
|
|
72
94
|
|
package/dist/index.js
CHANGED
|
@@ -35,8 +35,10 @@ class JSONRPCClient {
|
|
|
35
35
|
this.nextId = 0;
|
|
36
36
|
this.pending = new Map();
|
|
37
37
|
this.notificationHandlers = [];
|
|
38
|
+
this.errorHandlers = [];
|
|
39
|
+
this.closeHandlers = [];
|
|
38
40
|
this.transport = transport;
|
|
39
|
-
this.ready = new Promise((resolve
|
|
41
|
+
this.ready = new Promise((resolve) => {
|
|
40
42
|
if (transport instanceof WebSocketTransport) {
|
|
41
43
|
const ws = transport.connection;
|
|
42
44
|
if (ws.readyState === WebSocket.OPEN) {
|
|
@@ -44,13 +46,27 @@ class JSONRPCClient {
|
|
|
44
46
|
}
|
|
45
47
|
else {
|
|
46
48
|
ws.addEventListener('open', () => resolve());
|
|
47
|
-
ws.addEventListener('error', () => reject(new Error('WebSocket connection failed')));
|
|
48
49
|
}
|
|
49
50
|
}
|
|
50
51
|
else {
|
|
51
52
|
resolve();
|
|
52
53
|
}
|
|
53
54
|
});
|
|
55
|
+
transport.onError((error) => {
|
|
56
|
+
for (const handler of this.errorHandlers) {
|
|
57
|
+
handler(error);
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
transport.onClose(() => {
|
|
61
|
+
for (const [, pending] of this.pending) {
|
|
62
|
+
clearTimeout(pending.timer);
|
|
63
|
+
pending.reject(new Error('Connection closed'));
|
|
64
|
+
}
|
|
65
|
+
this.pending.clear();
|
|
66
|
+
for (const handler of this.closeHandlers) {
|
|
67
|
+
handler();
|
|
68
|
+
}
|
|
69
|
+
});
|
|
54
70
|
transport.onMessage((raw) => {
|
|
55
71
|
let msg;
|
|
56
72
|
try {
|
|
@@ -107,6 +123,12 @@ class JSONRPCClient {
|
|
|
107
123
|
onNotification(handler) {
|
|
108
124
|
this.notificationHandlers.push(handler);
|
|
109
125
|
}
|
|
126
|
+
onError(handler) {
|
|
127
|
+
this.errorHandlers.push(handler);
|
|
128
|
+
}
|
|
129
|
+
onClose(handler) {
|
|
130
|
+
this.closeHandlers.push(handler);
|
|
131
|
+
}
|
|
110
132
|
close() {
|
|
111
133
|
for (const [id, pending] of this.pending) {
|
|
112
134
|
clearTimeout(pending.timer);
|
|
@@ -157,7 +179,6 @@ function changeSetToEvents(doc, changes) {
|
|
|
157
179
|
}
|
|
158
180
|
|
|
159
181
|
const timeout = 10000;
|
|
160
|
-
const changesDelay = 500;
|
|
161
182
|
const CompletionItemKindMap = Object.fromEntries(Object.entries(CompletionItemKind).map(([key, value]) => [value, key]));
|
|
162
183
|
const useLast = (values) => values.reduce((_, v) => v, '');
|
|
163
184
|
Facet.define({
|
|
@@ -192,7 +213,19 @@ class LanguageServerClient {
|
|
|
192
213
|
}
|
|
193
214
|
this.processNotification(data);
|
|
194
215
|
});
|
|
195
|
-
this.
|
|
216
|
+
this.client.onError((error) => {
|
|
217
|
+
var _a, _b;
|
|
218
|
+
(_b = (_a = this.options).onError) === null || _b === void 0 ? void 0 : _b.call(_a, error);
|
|
219
|
+
});
|
|
220
|
+
this.client.onClose(() => {
|
|
221
|
+
var _a, _b;
|
|
222
|
+
this.ready = false;
|
|
223
|
+
(_b = (_a = this.options).onClose) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
224
|
+
});
|
|
225
|
+
this.initializePromise = this.initialize().catch((error) => {
|
|
226
|
+
var _a, _b;
|
|
227
|
+
(_b = (_a = this.options).onError) === null || _b === void 0 ? void 0 : _b.call(_a, error);
|
|
228
|
+
});
|
|
196
229
|
}
|
|
197
230
|
getInitializeParams() {
|
|
198
231
|
var _a;
|
|
@@ -359,8 +392,6 @@ var SynchronizationMethod;
|
|
|
359
392
|
class LanguageServerPlugin {
|
|
360
393
|
constructor(view, { client, documentUri, languageId, allowHTMLContent, synchronizationMethod, }) {
|
|
361
394
|
this.view = view;
|
|
362
|
-
this.changesTimeout = null;
|
|
363
|
-
this.pendingChanges = null;
|
|
364
395
|
this.client = client;
|
|
365
396
|
this.documentUri = documentUri;
|
|
366
397
|
this.languageId = languageId;
|
|
@@ -377,40 +408,20 @@ class LanguageServerPlugin {
|
|
|
377
408
|
if (!docChanged) {
|
|
378
409
|
return;
|
|
379
410
|
}
|
|
380
|
-
if (this.changesTimeout) {
|
|
381
|
-
clearTimeout(this.changesTimeout);
|
|
382
|
-
}
|
|
383
411
|
switch (this.synchronizationMethod) {
|
|
384
412
|
case SynchronizationMethod.Full:
|
|
385
|
-
this.
|
|
413
|
+
this.sendChanges([
|
|
386
414
|
{
|
|
387
415
|
text: this.view.state.doc.toString(),
|
|
388
416
|
},
|
|
389
|
-
];
|
|
417
|
+
]);
|
|
390
418
|
break;
|
|
391
419
|
case SynchronizationMethod.Incremental:
|
|
392
|
-
|
|
393
|
-
this.pendingChanges = [];
|
|
394
|
-
}
|
|
395
|
-
this.pendingChanges.push(...changeSetToEvents(startState.doc, changes));
|
|
420
|
+
this.sendChanges(changeSetToEvents(startState.doc, changes));
|
|
396
421
|
break;
|
|
397
422
|
}
|
|
398
|
-
this.changesTimeout = setTimeout(() => {
|
|
399
|
-
this.changesTimeout = null;
|
|
400
|
-
if (this.pendingChanges) {
|
|
401
|
-
this.sendChanges(this.pendingChanges);
|
|
402
|
-
this.pendingChanges = null;
|
|
403
|
-
}
|
|
404
|
-
}, changesDelay);
|
|
405
423
|
}
|
|
406
424
|
destroy() {
|
|
407
|
-
if (this.changesTimeout) {
|
|
408
|
-
clearTimeout(this.changesTimeout);
|
|
409
|
-
if (this.pendingChanges) {
|
|
410
|
-
this.sendChanges(this.pendingChanges);
|
|
411
|
-
this.pendingChanges = null;
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
425
|
this.client.detachPlugin(this);
|
|
415
426
|
}
|
|
416
427
|
async initialize({ documentText }) {
|
package/dist/jsonrpc.d.ts
CHANGED
|
@@ -27,6 +27,8 @@ export declare class JSONRPCClient {
|
|
|
27
27
|
private nextId;
|
|
28
28
|
private pending;
|
|
29
29
|
private notificationHandlers;
|
|
30
|
+
private errorHandlers;
|
|
31
|
+
private closeHandlers;
|
|
30
32
|
private ready;
|
|
31
33
|
constructor(transport: Transport);
|
|
32
34
|
request(req: {
|
|
@@ -38,5 +40,7 @@ export declare class JSONRPCClient {
|
|
|
38
40
|
params: any;
|
|
39
41
|
}): Promise<void>;
|
|
40
42
|
onNotification(handler: (data: any) => void): void;
|
|
43
|
+
onError(handler: (error: Error) => void): void;
|
|
44
|
+
onClose(handler: () => void): void;
|
|
41
45
|
close(): void;
|
|
42
46
|
}
|
package/dist/plugin.d.ts
CHANGED
|
@@ -127,8 +127,6 @@ export declare class LanguageServerPlugin implements PluginValue {
|
|
|
127
127
|
private documentVersion;
|
|
128
128
|
private allowHTMLContent;
|
|
129
129
|
private synchronizationMethod;
|
|
130
|
-
private changesTimeout;
|
|
131
|
-
private pendingChanges;
|
|
132
130
|
constructor(view: EditorView, { client, documentUri, languageId, allowHTMLContent, synchronizationMethod, }: {
|
|
133
131
|
client: LanguageServerClient;
|
|
134
132
|
documentUri: string;
|
|
@@ -215,5 +213,9 @@ export interface LanguageServerClientOptions<InitializationOptions = unknown> ex
|
|
|
215
213
|
locale?: string;
|
|
216
214
|
/** Called once after initialization with the server's capabilities. */
|
|
217
215
|
onCapabilities?: (capabilities: LSP.ServerCapabilities) => void;
|
|
216
|
+
/** Called when the connection encounters an error. */
|
|
217
|
+
onError?: (error: Error) => void;
|
|
218
|
+
/** Called when the connection is closed. */
|
|
219
|
+
onClose?: () => void;
|
|
218
220
|
}
|
|
219
221
|
export {};
|